From 0d26d214b2c9cddf33af78b96d5b2f0a3911e1be Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Fri, 28 Feb 2025 21:55:50 +1000 Subject: [PATCH 01/27] [dashboard/ProjectExecutionsCalendar] Fix missing `key` --- .../layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx index 49698ce4d08c..cf556806a165 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx @@ -230,6 +230,7 @@ function ProjectExecutionsCalendarInternal(props: ProjectExecutionsCalendarInter {getText('noProjectExecutions')} : projectExecutionsForToday.map(({ projectExecution, date }) => ( Date: Fri, 28 Feb 2025 21:58:20 +1000 Subject: [PATCH 02/27] [dashboard] Replace `gap-icon-with-text` with `gap-2` --- app/gui/src/dashboard/components/dashboard/AssetSummary.tsx | 4 ++-- .../columnHeading/AccessedByProjectsColumnHeading.tsx | 2 +- .../dashboard/columnHeading/AccessedDataColumnHeading.tsx | 2 +- .../dashboard/columnHeading/DocsColumnHeading.tsx | 2 +- .../dashboard/columnHeading/LabelsColumnHeading.tsx | 2 +- .../dashboard/columnHeading/ModifiedColumnHeading.tsx | 4 ++-- .../dashboard/columnHeading/NameColumnHeading.tsx | 2 +- .../dashboard/columnHeading/PathColumnHeading.tsx | 2 +- .../dashboard/columnHeading/SharedWithColumnHeading.tsx | 2 +- app/gui/src/dashboard/layouts/Chat.tsx | 2 +- .../layouts/Settings/ActivityLogSettingsSection.tsx | 6 +++--- .../src/dashboard/pages/authentication/RestoreAccount.tsx | 2 +- app/gui/src/dashboard/tailwind.css | 2 -- app/gui/tailwind.config.ts | 3 +-- 14 files changed, 17 insertions(+), 20 deletions(-) diff --git a/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx b/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx index 28cedc773e8e..56d0d0028a3a 100644 --- a/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx +++ b/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx @@ -33,7 +33,7 @@ export default function AssetSummary(props: AssetSummaryProps) { return (
@@ -41,7 +41,7 @@ export default function AssetSummary(props: AssetSummaryProps) {
- + {asset.title} {newName != null && ( <> diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/AccessedByProjectsColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/AccessedByProjectsColumnHeading.tsx index b1d5b81fcc42..e381ab6ee902 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/AccessedByProjectsColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/AccessedByProjectsColumnHeading.tsx @@ -16,7 +16,7 @@ export default function AccessedByProjectsColumnHeading(props: AssetColumnHeadin }) return ( -
+
) diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx index b78408285140..bb117bb2dce0 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx @@ -1,6 +1,6 @@ /** @file A heading for the "Name" column. */ import SortAscendingIcon from '#/assets/sort_ascending.svg' -import { Button, Text } from '#/components/AriaComponents' +import { Button } from '#/components/AriaComponents' import type { AssetColumnHeadingProps } from '#/components/dashboard/column' import { Column } from '#/components/dashboard/column/columnUtils' import { useEventCallback } from '#/hooks/eventCallbackHooks' @@ -33,29 +33,31 @@ export default function NameColumnHeading(props: AssetColumnHeadingProps) { return ( ) } From 673b887eb3cf969c5d06f64fd5ec8e781e44f6c9 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 18:14:04 +1000 Subject: [PATCH 06/27] Format `Button/types.ts` --- .../components/AriaComponents/Button/types.ts | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts index 08c1e131c0ec..233d8aea19b5 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts +++ b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts @@ -1,8 +1,4 @@ -/** - * @file - * - * Reusable types for the Button component - */ +/** @file Types for a `Button`. */ import type * as aria from '#/components/aria' import type { TextProps } from '#/components/AriaComponents/Text' import type { ExtractFunction } from '#/utilities/tailwindVariants' @@ -10,34 +6,24 @@ import type { ReactElement, ReactNode } from 'react' import type { Addon, IconProp, TestIdProps } from '../types' import type { BUTTON_STYLES, ButtonVariants } from './variants' -/** - * Position of a joined button - */ +/** Position of a joined button. */ export type PrivateJoinedButtonPosition = ButtonVariants['position'] -/** - * Whether the button is joined - */ +/** Whether the button is joined. */ export type PrivateJoinedButton = ButtonVariants['isJoined'] -/** - * Props for a joined button unlike other button props, - */ +/** Props for a joined button unlike other button props. */ export interface PrivateJoinedButtonProps { readonly position: PrivateJoinedButtonPosition readonly isJoined: NonNullable } -/** - * Render props for a button. - */ +/** Render props for a button. */ export interface ButtonRenderProps extends aria.ButtonRenderProps { readonly isLoading: boolean } -/** - * Render props for a link. - */ +/** Render props for a link. */ export interface LinkRenderProps extends aria.LinkRenderProps { readonly isLoading: boolean } @@ -103,16 +89,12 @@ export interface BaseButtonProps readonly addonEnd?: Addon } -/** - * A type that makes all properties of a type optional - */ +/** A new type `undefined` added to all properties of a type. */ type WithUndefined = { [K in keyof T]: T[K] | undefined } -/** - * Props that are shared between buttons in a button group. - */ +/** Props that are shared between buttons in a button group. */ export interface ButtonGroupSharedButtonProps extends WithUndefined { readonly isDisabled?: boolean | undefined readonly isLoading?: boolean | undefined From dc6ed86e0281a65f0fa1289ff87ad612ce021d28 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 18:15:25 +1000 Subject: [PATCH 07/27] [dashboard/Button] Replace `import *` with `import {}` --- .../components/AriaComponents/Button/types.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts index 233d8aea19b5..c57e0328b1ef 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts +++ b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts @@ -1,5 +1,12 @@ /** @file Types for a `Button`. */ -import type * as aria from '#/components/aria' +import type { + ButtonProps as AriaButtonProps, + ButtonRenderProps as AriaButtonRenderProps, + LinkRenderProps as AriaLinkRenderProps, + LinkProps, + Placement, + PressEvent, +} from '#/components/aria' import type { TextProps } from '#/components/AriaComponents/Text' import type { ExtractFunction } from '#/utilities/tailwindVariants' import type { ReactElement, ReactNode } from 'react' @@ -19,22 +26,22 @@ export interface PrivateJoinedButtonProps { } /** Render props for a button. */ -export interface ButtonRenderProps extends aria.ButtonRenderProps { +export interface ButtonRenderProps extends AriaButtonRenderProps { readonly isLoading: boolean } /** Render props for a link. */ -export interface LinkRenderProps extends aria.LinkRenderProps { +export interface LinkRenderProps extends AriaLinkRenderProps { readonly isLoading: boolean } /** Props for a Button. */ export type ButtonProps = | (BaseButtonProps & - Omit & + Omit & PropsWithoutHref) | (BaseButtonProps & - Omit & + Omit & PropsWithHref) /** Props for a button with an href. */ @@ -55,7 +62,7 @@ export interface BaseButtonProps readonly hideLoader?: boolean /** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */ readonly tooltip?: ReactElement | string | false | null - readonly tooltipPlacement?: aria.Placement + readonly tooltipPlacement?: Placement /** The icon to display in the button */ readonly icon?: IconProp /** When `true`, icon will be shown only when hovered. */ @@ -68,7 +75,7 @@ export interface BaseButtonProps // prettier-ignore readonly onPress?: // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents - | ((event: aria.PressEvent) => Promise | unknown) + | ((event: PressEvent) => Promise | unknown) | null | undefined readonly contentClassName?: string From 4938880969e4a11f80841bcb79645980210be804 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 18:20:45 +1000 Subject: [PATCH 08/27] [dashboard/Breadcrumbs] Format --- .../components/Breadcrumbs/Breadcrumbs.tsx | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/app/gui/src/dashboard/components/Breadcrumbs/Breadcrumbs.tsx b/app/gui/src/dashboard/components/Breadcrumbs/Breadcrumbs.tsx index 801d4aa76540..b874f13284dd 100644 --- a/app/gui/src/dashboard/components/Breadcrumbs/Breadcrumbs.tsx +++ b/app/gui/src/dashboard/components/Breadcrumbs/Breadcrumbs.tsx @@ -1,6 +1,4 @@ -/** - * @file Breadcrumbs component implementation. - */ +/** @file A breadcrumb nagivation component. */ import { useEventCallback } from '#/hooks/eventCallbackHooks' import { tv, type VariantProps } from '#/utilities/tailwindVariants' import { @@ -22,14 +20,10 @@ export const BREADCRUMBS_STYLES = tv({ slots: { separator: 'text-primary last:hidden w-2.5 h-2.5 mt-[0.5px]' }, }) -/** - * The type of the `onDrop` callback. - */ +/** The type of the `onDrop` callback. */ export type OnDrop = (key: Key, e: DropEvent) => Promise | void -/** - * Props for {@link Breadcrumbs} - */ +/** Props for {@link Breadcrumbs}. */ export interface BreadcrumbsProps extends AriaBreadcrumbsProps, VariantProps, @@ -42,9 +36,7 @@ export interface BreadcrumbsProps readonly onDrop?: OnDrop } -/** - * A breadcrumb navigation component. - */ +/** A breadcrumb navigation component. */ export function Breadcrumbs(props: BreadcrumbsProps) { const { children, @@ -84,9 +76,7 @@ export function Breadcrumbs(props: BreadcrumbsProps) { ) } -/** - * Props for {@link BreadcrumbInner} - */ +/** Props for {@link BreadcrumbInner}. */ interface BreadcrumbInnerProps extends TestIdProps, AriaBreadcrumbsProps, PropsWithChildren { readonly className?: string } @@ -107,17 +97,13 @@ function BreadcrumbInner(props: BreadcrumbInnerProps) { ) } -/** - * Props for {@link BreadcrumbSeparator} - */ +/** Props for {@link BreadcrumbSeparator}. */ interface BreadcrumbSeparatorProps { readonly icon?: IconProp readonly className?: string } -/** - * A separator between breadcrumb items. - */ +/** A separator between breadcrumb items. */ // eslint-disable-next-line no-restricted-syntax const BreadcrumbSeparator = memo(function BreadcrumbSeparator( props: BreadcrumbSeparatorProps, From 532930e6a48674cb60a1a6204c62131fe640c4ed Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 18:35:18 +1000 Subject: [PATCH 09/27] [dashboard/Drive] Infinite `staleTime` for root directory and parent directory --- app/gui/src/dashboard/hooks/backendHooks.ts | 4 --- app/gui/src/dashboard/layouts/AssetsTable.tsx | 16 +++++++-- .../layouts/Drive/fetchDirectoriesHooks.ts | 34 ------------------- .../dashboard/pages/dashboard/Dashboard.tsx | 8 ++--- .../dashboard/Drive/DriveBar/DriveBar.tsx | 7 ++-- .../Drive/DriveBar/DriveBarNavigation.tsx | 1 + 6 files changed, 20 insertions(+), 50 deletions(-) delete mode 100644 app/gui/src/dashboard/layouts/Drive/fetchDirectoriesHooks.ts diff --git a/app/gui/src/dashboard/hooks/backendHooks.ts b/app/gui/src/dashboard/hooks/backendHooks.ts index 03e742a95c49..2968f6f22c9e 100644 --- a/app/gui/src/dashboard/hooks/backendHooks.ts +++ b/app/gui/src/dashboard/hooks/backendHooks.ts @@ -332,10 +332,6 @@ export function listDirectoryQueryOptions(options: ListDirectoryQueryOptions) { recentProjects: category.type === 'recent', }, ] as const, - // Setting stale time to `Infinity` avoids attaching a ton of - // setTimeouts to the query. Improves performance. - // This is fine as refetching is handled by another query. - staleTime: Infinity, queryFn: async () => { try { return await backend.listDirectory( diff --git a/app/gui/src/dashboard/layouts/AssetsTable.tsx b/app/gui/src/dashboard/layouts/AssetsTable.tsx index ba7d8ba344b0..fc8021aec516 100644 --- a/app/gui/src/dashboard/layouts/AssetsTable.tsx +++ b/app/gui/src/dashboard/layouts/AssetsTable.tsx @@ -98,6 +98,7 @@ import { useSetVisuallySelectedKeys, type SelectedAssetInfo, } from '#/providers/DriveProvider' +import { useFeatureFlag } from '#/providers/FeatureFlagsProvider' import { useInputBindings } from '#/providers/InputBindingsProvider' import { useLocalStorage } from '#/providers/LocalStorageProvider' import { useSetModal } from '#/providers/ModalProvider' @@ -256,6 +257,10 @@ function AssetsTable(props: AssetsTableProps) { const setSelectedAssets = useSetSelectedAssets() const setVisuallySelectedKeys = useSetVisuallySelectedKeys() const setPasteData = useSetPasteData() + const enableAssetsTableBackgroundRefresh = useFeatureFlag('enableAssetsTableBackgroundRefresh') + const assetsTableBackgroundRefreshInterval = useFeatureFlag( + 'assetsTableBackgroundRefreshInterval', + ) const uploadFiles = useUploadFiles(backend, category) const updateSecretMutation = useMutation(backendMutationOptions(backend, 'updateSecret')) @@ -268,9 +273,14 @@ function AssetsTable(props: AssetsTableProps) { const { currentDirectoryId, setCurrentDirectoryId } = useDirectoryIds({ category, }) - const { data: assets = [], status: fetchStatus } = useQuery( - listDirectoryQueryOptions({ backend, parentId: currentDirectoryId, category }), - ) + const { data: assets = [], status: fetchStatus } = useQuery({ + ...listDirectoryQueryOptions({ + backend, + parentId: currentDirectoryId, + category, + }), + staleTime: enableAssetsTableBackgroundRefresh ? assetsTableBackgroundRefreshInterval : Infinity, + }) const isLoading = fetchStatus === 'pending' const { visibleItems } = useAssetsTableItems({ diff --git a/app/gui/src/dashboard/layouts/Drive/fetchDirectoriesHooks.ts b/app/gui/src/dashboard/layouts/Drive/fetchDirectoriesHooks.ts deleted file mode 100644 index 51f5a2034799..000000000000 --- a/app/gui/src/dashboard/layouts/Drive/fetchDirectoriesHooks.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** @file Hooks related to fetching directories. */ -import { useFeatureFlag } from '#/providers/FeatureFlagsProvider' -import { useIsMutating, useQuery, useQueryClient } from '@tanstack/react-query' -import type { BackendType } from 'enso-common/src/services/Backend' - -/** Periodically refetch directories for the given backend type. */ -export function useRefetchDirectories(backendType: BackendType) { - const queryClient = useQueryClient() - const enableAssetsTableBackgroundRefresh = useFeatureFlag('enableAssetsTableBackgroundRefresh') - const assetsTableBackgroundRefreshInterval = useFeatureFlag( - 'assetsTableBackgroundRefreshInterval', - ) - const isMutating = useIsMutating({ mutationKey: [backendType] }) !== 0 - - // We use a different query to refetch the directory data in the background. - // This reduces the amount of rerenders by batching them together, so they happen less often. - useQuery({ - queryKey: [backendType, 'refetchListDirectory'], - queryFn: async () => { - await queryClient.refetchQueries({ - queryKey: [backendType, 'listDirectory'], - type: 'active', - }) - return null - }, - refetchInterval: - enableAssetsTableBackgroundRefresh ? assetsTableBackgroundRefreshInterval : false, - refetchOnMount: 'always', - refetchIntervalInBackground: false, - refetchOnWindowFocus: true, - meta: { persist: false }, - enabled: !isMutating, - }) -} diff --git a/app/gui/src/dashboard/pages/dashboard/Dashboard.tsx b/app/gui/src/dashboard/pages/dashboard/Dashboard.tsx index 5f79f1660a69..7c3ad2640a64 100644 --- a/app/gui/src/dashboard/pages/dashboard/Dashboard.tsx +++ b/app/gui/src/dashboard/pages/dashboard/Dashboard.tsx @@ -37,7 +37,6 @@ import * as localBackendModule from '#/services/LocalBackend' import * as projectManager from '#/services/ProjectManager' import { useCategoriesAPI } from '#/layouts/Drive/Categories/categoriesHooks' -import { useRefetchDirectories } from '#/layouts/Drive/fetchDirectoriesHooks' import { baseName } from '#/utilities/fileInfo' import { STATIC_QUERY_OPTIONS } from '#/utilities/reactQuery' import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets' @@ -104,14 +103,11 @@ function DashboardInner(props: DashboardProps) { const initialProjectName = initialLocalProjectPath != null ? null : initialProjectNameRaw const categoriesAPI = useCategoriesAPI() - - useRefetchDirectories(backendModule.BackendType.local) - useRefetchDirectories(backendModule.BackendType.remote) - const projectsStore = useProjectsStore() - const page = usePage() + const page = usePage() const setPage = useSetPage() + const openEditor = projectHooks.useOpenEditor() const openProject = projectHooks.useOpenProject() const closeProject = projectHooks.useCloseProject() diff --git a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx index 42af11792a2e..d98457a7488d 100644 --- a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx +++ b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx @@ -37,7 +37,7 @@ export function DriveBar(props: DriveBarProps) { const { showBoundary } = useErrorBoundary() const queryClient = useQueryClient() - const rootDirectoryQuery = listDirectoryQueryOptions({ + const rootDirectoryQueryOptions = listDirectoryQueryOptions({ backend, category, parentId: rootDirectoryId, @@ -48,7 +48,8 @@ export function DriveBar(props: DriveBarProps) { error, isFetching, } = useSuspenseQuery({ - ...rootDirectoryQuery, + ...rootDirectoryQueryOptions, + staleTime: Infinity, select: (data) => data.length === 0, }) @@ -57,7 +58,7 @@ export function DriveBar(props: DriveBarProps) { showBoundary(error) // Remove the query from the cache. // This will force the query to be refetched when the user navigates again. - queryClient.removeQueries({ queryKey: rootDirectoryQuery.queryKey }) + queryClient.removeQueries({ queryKey: rootDirectoryQueryOptions.queryKey }) } // When the directory is no longer empty, we need to hide the start modal. diff --git a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx index 50f65316f528..013afbe47b8a 100644 --- a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx +++ b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx @@ -61,6 +61,7 @@ export function DriveBarNavigation() { parentId: parentDirectoryId, category, }), + staleTime: Infinity, select: (data) => { if (parentDirectoryId === currentDirectoryId) { return null From c1b73319c86aa73210f3838975e424683257819b Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 18:39:46 +1000 Subject: [PATCH 10/27] [dashboard/Drive] Remove incorrect `staleTime` from "list directories" calls and replace with `refetchInterval` when necessary --- app/gui/src/dashboard/layouts/AssetsTable.tsx | 3 ++- .../src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx | 1 - .../pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/gui/src/dashboard/layouts/AssetsTable.tsx b/app/gui/src/dashboard/layouts/AssetsTable.tsx index fc8021aec516..a904e3362b74 100644 --- a/app/gui/src/dashboard/layouts/AssetsTable.tsx +++ b/app/gui/src/dashboard/layouts/AssetsTable.tsx @@ -279,7 +279,8 @@ function AssetsTable(props: AssetsTableProps) { parentId: currentDirectoryId, category, }), - staleTime: enableAssetsTableBackgroundRefresh ? assetsTableBackgroundRefreshInterval : Infinity, + refetchInterval: + enableAssetsTableBackgroundRefresh ? assetsTableBackgroundRefreshInterval : Infinity, }) const isLoading = fetchStatus === 'pending' diff --git a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx index d98457a7488d..6e420d833ab4 100644 --- a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx +++ b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBar.tsx @@ -49,7 +49,6 @@ export function DriveBar(props: DriveBarProps) { isFetching, } = useSuspenseQuery({ ...rootDirectoryQueryOptions, - staleTime: Infinity, select: (data) => data.length === 0, }) diff --git a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx index 013afbe47b8a..50f65316f528 100644 --- a/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx +++ b/app/gui/src/dashboard/pages/dashboard/Drive/DriveBar/DriveBarNavigation.tsx @@ -61,7 +61,6 @@ export function DriveBarNavigation() { parentId: parentDirectoryId, category, }), - staleTime: Infinity, select: (data) => { if (parentDirectoryId === currentDirectoryId) { return null From 5ec659a66cffbd38e178fdee33fae36140f95bff Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 21:08:20 +1000 Subject: [PATCH 11/27] [dashboard] Replace column sort icons with `Icon`s --- .../components/AriaComponents/types.ts | 44 ++----- .../src/dashboard/components/Icon/Icon.tsx | 4 +- .../columnHeading/ModifiedColumnHeading.tsx | 10 +- .../columnHeading/NameColumnHeading.tsx | 10 +- .../Settings/ActivityLogSettingsSection.tsx | 117 +++++++----------- app/gui/src/dashboard/utilities/sorting.ts | 29 +++-- 6 files changed, 87 insertions(+), 127 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/types.ts b/app/gui/src/dashboard/components/AriaComponents/types.ts index ed8b259c5761..85ba06710c45 100644 --- a/app/gui/src/dashboard/components/AriaComponents/types.ts +++ b/app/gui/src/dashboard/components/AriaComponents/types.ts @@ -1,8 +1,7 @@ -/** @file Common types for ARIA components. */ +/** @file Common types for WAI-ARIA components. */ import type { Icon as PossibleIcon } from '@/util/iconMetadata/iconName' import type { ReactElement } from 'react' - -import type * as reactAria from 'react-aria' +export type { Placement } from 'react-aria' /** Props for adding a test id to a component */ export interface TestIdProps { @@ -11,22 +10,12 @@ export interface TestIdProps { readonly testId?: string | undefined } -/** - * A type alias for the Placement type from react-aria. - * This type represents the possible positions where an element can be placed relative to a reference element. - */ -export type Placement = reactAria.Placement - -/** - * Type for any icon - */ +/** Any icon. */ export type IconProp = | IconPropSvgUse | LegacyIconProp -/** - * A type that represents the possible return values for a legacy icon. - */ +/** The possible return values for a legacy icon. */ export type LegacyAvialableIconReturn = | LegacyIcon | ReactElement @@ -34,36 +23,27 @@ export type LegacyAvialableIconReturn = | null | undefined -/** - * A type that represents the possible return values for a legacy icon. - */ +/** The possible return values for a legacy icon. */ export type AvailableIconReturn = ReactElement | SvgUseIcon | false | null | undefined /** - * Generic type for any icon - * @deprecated Prefer defined keys over importing from `#/assets/*.svg + * Any legacy icon. + * @deprecated Prefer defined keys over importing from `#/assets/*.svg`. */ export type LegacyIconProp = | LegacyAvialableIconReturn | ((render: Render) => LegacyAvialableIconReturn) -/** - * Generic type for imported from figma icons - */ +/** Generic type for imported from figma icons. */ export type IconPropSvgUse = AvailableIconReturn | ((render: Render) => AvailableIconReturn) -/** - * @deprecated - */ +/** @deprecated */ export type LegacyIcon = Exclude & {} -/** - * Type for any icon imported from figma - */ +/** Any icon imported from Figma. */ export type SvgUseIcon = PossibleIcon -/** - * Generic type for any addon - */ + +/** Any addon. */ export type Addon = | ReactElement | string diff --git a/app/gui/src/dashboard/components/Icon/Icon.tsx b/app/gui/src/dashboard/components/Icon/Icon.tsx index fac5f8f318fd..de0a71136a88 100644 --- a/app/gui/src/dashboard/components/Icon/Icon.tsx +++ b/app/gui/src/dashboard/components/Icon/Icon.tsx @@ -41,9 +41,7 @@ export interface LegacyIconProps readonly icon?: never } -/** - * Generic type for imported from figma icons - */ +/** Generic type for icons imported from Figma. */ export interface SvgUseIconProps { readonly children?: never readonly icon: IconTypeSvgUse diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx index 8df173356e3c..11e74c03129f 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx @@ -1,12 +1,12 @@ /** @file A heading for the "Modified" column. */ -import SortAscendingIcon from '#/assets/sort_ascending.svg' import TimeIcon from '#/assets/time.svg' import { Button } from '#/components/AriaComponents' +import { Icon } from '#/components/Icon' import type { AssetColumnHeadingProps } from '#/components/dashboard/column' import { Column } from '#/components/dashboard/column/columnUtils' import { useEventCallback } from '#/hooks/eventCallbackHooks' import { useText } from '#/providers/TextProvider' -import { SortDirection, nextSortDirection } from '#/utilities/sorting' +import { SortDirection, iconIdFor, nextSortDirection } from '#/utilities/sorting' import { twJoin } from '#/utilities/tailwindMerge' /** A heading for the "Modified" column. */ @@ -61,13 +61,11 @@ export default function ModifiedColumnHeading(props: AssetColumnHeadingProps) { variant="custom" weight="bold" addonEnd={ - {isDescending } diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx index bb117bb2dce0..073ea48b2dce 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx @@ -1,11 +1,11 @@ /** @file A heading for the "Name" column. */ -import SortAscendingIcon from '#/assets/sort_ascending.svg' import { Button } from '#/components/AriaComponents' +import { Icon } from '#/components/Icon' import type { AssetColumnHeadingProps } from '#/components/dashboard/column' import { Column } from '#/components/dashboard/column/columnUtils' import { useEventCallback } from '#/hooks/eventCallbackHooks' import { useText } from '#/providers/TextProvider' -import { SortDirection, nextSortDirection } from '#/utilities/sorting' +import { SortDirection, iconIdFor, nextSortDirection } from '#/utilities/sorting' import { twJoin } from '#/utilities/tailwindMerge' /** A heading for the "Name" column. */ @@ -44,13 +44,11 @@ export default function NameColumnHeading(props: AssetColumnHeadingProps) { : getText('sortByNameDescending') } addonEnd={ - {isDescending } diff --git a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx index 9a5ae60b4c16..e6714d7b85d6 100644 --- a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx +++ b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx @@ -7,9 +7,9 @@ import * as z from 'zod' import DataUploadIcon from '#/assets/data_upload.svg' import KeyIcon from '#/assets/key.svg' import Play2Icon from '#/assets/play2.svg' -import SortAscendingIcon from '#/assets/sort_ascending.svg' import TrashIcon from '#/assets/trash.svg' import { Button, DatePicker, Dropdown, Form, Text } from '#/components/AriaComponents' +import { Icon } from '#/components/Icon' import { StatelessSpinner } from '#/components/StatelessSpinner' import FocusArea from '#/components/styled/FocusArea' import SvgMask from '#/components/SvgMask' @@ -17,14 +17,10 @@ import { useBackendQuery } from '#/hooks/backendHooks' import { useText } from '#/providers/TextProvider' import type Backend from '#/services/Backend' import { EVENT_TYPES, EventType, type Event } from '#/services/Backend' -import { nextSortDirection, SortDirection, type SortInfo } from '#/utilities/sorting' +import { iconIdFor, nextSortDirection, SortDirection, type SortInfo } from '#/utilities/sorting' import { twMerge } from '#/utilities/tailwindMerge' import { formatDateTime } from 'enso-common/src/utilities/data/dateTime' -// ================= -// === Constants === -// ================= - const EVENT_TYPE_ICON: Record = { [EventType.GetSecret]: KeyIcon, [EventType.DeleteAssets]: TrashIcon, @@ -49,10 +45,6 @@ function createActivityLogSchema() { }) } -// ================================= -// === ActivityLogSortableColumn === -// ================================= - /** Sortable columns in an activity log table. */ enum ActivityLogSortableColumn { type = 'type', @@ -60,10 +52,6 @@ enum ActivityLogSortableColumn { timestamp = 'timestamp', } -// ================================== -// === ActivityLogSettingsSection === -// ================================== - /** Props for a {@link ActivityLogSettingsSection}. */ export interface ActivityLogSettingsSectionProps { readonly backend: Backend @@ -225,6 +213,19 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec getText('stopSortingByName') : getText('sortByNameDescending') } + addonEnd={ + + } className="group flex h-table-row w-full items-center justify-start gap-2 border-0 px-name-column-x" onPress={() => { const nextDirection = @@ -241,23 +242,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('type')} - { + {getText('type')} @@ -270,6 +255,19 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec getText('stopSortingByEmail') : getText('sortByEmailDescending') } + addonEnd={ + + } className="group flex h-table-row w-full items-center justify-start gap-2 border-0 px-name-column-x" onPress={() => { const nextDirection = @@ -286,23 +284,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('email')} - { + {getText('email')} @@ -316,6 +298,19 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec getText('stopSortingByTimestamp') : getText('sortByTimestampDescending') } + addonEnd={ + + } className="group flex h-table-row w-full items-center justify-start gap-2 border-0 px-name-column-x" onPress={() => { const nextDirection = @@ -332,23 +327,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('timestamp')} - { + {getText('timestamp')} @@ -383,10 +362,6 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec ) } -// ============================= -// === ActivityLogHeaderCell === -// ============================= - /** Props for a {@link ActivityLogHeaderCell}. */ export interface ActivityLogHeaderCellProps extends Readonly { readonly className?: string @@ -408,10 +383,6 @@ function ActivityLogHeaderCell(props: ActivityLogHeaderCellProps) { ) } -// ============================ -// === ActivityLogTableCell === -// ============================ - /** Props for a {@link ActivityLogTableCell}. */ export type ActivityLogTableCellProps = Readonly diff --git a/app/gui/src/dashboard/utilities/sorting.ts b/app/gui/src/dashboard/utilities/sorting.ts index abfb309a7859..0347cf2a0818 100644 --- a/app/gui/src/dashboard/utilities/sorting.ts +++ b/app/gui/src/dashboard/utilities/sorting.ts @@ -1,8 +1,5 @@ /** @file Utilities related to sorting. */ - -// ===================== -// === SortDirection === -// ===================== +import type { SvgUseIcon } from '#/components/AriaComponents' /** Sort direction. */ export enum SortDirection { @@ -28,9 +25,27 @@ export function nextSortDirection(sortDirection: SortDirection | null) { } } -// ================ -// === SortInfo === -// ================ +/** The corresponding icon id forr a given {@link SortDirection}. */ +export function iconIdFor( + sortDirection: SortDirection | null | undefined, + sortInfoAppliesToCurrentColumn = true, +): SvgUseIcon { + if (!sortInfoAppliesToCurrentColumn) { + return 'sort' + } + switch (sortDirection) { + case null: + case undefined: { + return 'sort' + } + case SortDirection.ascending: { + return 'sort_ascending' + } + case SortDirection.descending: { + return 'sort_descending' + } + } +} /** Sort information. */ export interface SortInfo { From 65188bfa693449e2d0999a2f671c47e9eb4802dc Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 21:21:20 +1000 Subject: [PATCH 12/27] [dashboard/Button] Remove unnecessary `weight` prop from `Button` --- .../AriaComponents/Button/Button.tsx | 9 ++------- .../components/AriaComponents/Button/types.ts | 3 --- .../components/AriaComponents/Text/Text.tsx | 19 ++++++++++++++----- .../columnHeading/ModifiedColumnHeading.tsx | 3 +-- .../columnHeading/NameColumnHeading.tsx | 3 +-- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx b/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx index 834b033d7dc1..9d59313ca8c6 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx @@ -10,7 +10,7 @@ import { } from 'react' import * as aria from '#/components/aria' -import { Text, useVisualTooltip, type TextProps } from '#/components/AriaComponents/Text' +import { Text, useVisualTooltip } from '#/components/AriaComponents/Text' import { Tooltip, TooltipTrigger } from '#/components/AriaComponents/Tooltip' import { Icon as IconComponent } from '#/components/Icon' import { StatelessSpinner } from '#/components/StatelessSpinner' @@ -64,7 +64,6 @@ export const Button = memo( addonStart, addonEnd, hideLoader = false, - weight, ...ariaProps } = props @@ -228,7 +227,6 @@ export const Button = memo( addonStart={typeof addonStart === 'function' ? addonStart(render) : addonStart} /* @ts-expect-error any here is safe because we transparently pass it to the children, and ts infer the type outside correctly */ addonEnd={typeof addonEnd === 'function' ? addonEnd(render) : addonEnd} - weight={weight} > {/* @ts-expect-error any here is safe because we transparently pass it to the children, and ts infer the type outside correctly */} {typeof children === 'function' ? children(render) : children} @@ -288,8 +286,6 @@ interface ButtonContentProps { readonly icon: ReactElement | string | null | undefined readonly styles: ReturnType readonly children: ReactNode - /** The font weight of the button label. */ - readonly weight?: TextProps['weight'] readonly addonStart?: ReactElement | string | false | null | undefined readonly addonEnd?: ReactElement | string | false | null | undefined } @@ -311,7 +307,6 @@ const ButtonContent = memo(function ButtonContent(props: ButtonContentProps) { addonStart, addonEnd, hideLoader, - weight, } = props // Icon only button @@ -342,7 +337,7 @@ const ButtonContent = memo(function ButtonContent(props: ButtonContentProps) { styles={styles} hideLoader={hideLoader} /> - + {children} {hasAddon(addonEnd) &&
{addonEnd}
} diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts index c57e0328b1ef..148548eb9c11 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/types.ts +++ b/app/gui/src/dashboard/components/AriaComponents/Button/types.ts @@ -7,7 +7,6 @@ import type { Placement, PressEvent, } from '#/components/aria' -import type { TextProps } from '#/components/AriaComponents/Text' import type { ExtractFunction } from '#/utilities/tailwindVariants' import type { ReactElement, ReactNode } from 'react' import type { Addon, IconProp, TestIdProps } from '../types' @@ -86,8 +85,6 @@ export interface BaseButtonProps * When `icon`, only the icon will be replaced with the loader. */ readonly loaderPosition?: 'full' | 'icon' - /** The font weight of the button label. */ - readonly weight?: TextProps['weight'] readonly styles?: ExtractFunction | undefined readonly children?: ReactNode | ((render: Render) => ReactNode) diff --git a/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx b/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx index 851d3a8f9bce..fc3cb196f2da 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx @@ -52,13 +52,13 @@ export const TEXT_STYLE = twv.tv({ // leading should always be after the text size to make sure it is not stripped by twMerge variant: { custom: '', - body: 'text-xs leading-[20px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px] font-medium', + body: 'text-xs leading-[20px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px', // eslint-disable-next-line @typescript-eslint/naming-convention 'body-sm': - 'text-[10.5px] leading-[16px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px] font-medium', - h1: 'text-xl leading-[29px] before:h-0.5 after:h-[5px] macos:before:h-[3px] macos:after:h-[3px] font-bold', + 'text-[10.5px] leading-[16px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px]', + h1: 'text-xl leading-[29px] before:h-0.5 after:h-[5px] macos:before:h-[3px] macos:after:h-[3px]', subtitle: - 'text-[13.5px] leading-[20px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px] font-bold', + 'text-[13.5px] leading-[20px] before:h-[2px] after:h-[2px] macos:before:h-[1px] macos:after:h-[3px]', caption: 'text-[8.5px] leading-[12px] before:h-[1px] after:h-[1px] macos:before:h-[0.5px] macos:after:h-[1.5px]', overline: @@ -66,6 +66,7 @@ export const TEXT_STYLE = twv.tv({ }, weight: { custom: '', + default: '', bold: 'font-bold', semibold: 'font-semibold', extraBold: 'font-extrabold', @@ -117,7 +118,7 @@ export const TEXT_STYLE = twv.tv({ defaultVariants: { variant: 'body', font: 'default', - weight: 'medium', + weight: 'default', transform: 'none', color: 'primary', italic: false, @@ -126,6 +127,14 @@ export const TEXT_STYLE = twv.tv({ disableLineHeightCompensation: false, textSelection: 'auto', }, + compoundVariants: [ + { variant: 'body', weight: 'default', className: 'font-medium' }, + { variant: 'body-sm', weight: 'default', className: 'font-medium' }, + { variant: 'h1', weight: 'default', className: 'font-bold' }, + { variant: 'subtitle', weight: 'default', className: 'font-bold' }, + { variant: 'caption', weight: 'default', className: 'font-medium' }, + { variant: 'overline', weight: 'default', className: 'font-medium' }, + ], }) /** Text component that supports truncation and show a tooltip on hover when text is truncated */ diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx index 11e74c03129f..0627797538aa 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx @@ -59,7 +59,6 @@ export default function ModifiedColumnHeading(props: AssetColumnHeadingProps) { fullWidth size="custom" variant="custom" - weight="bold" addonEnd={ } - className="flex justify-start" + className="flex justify-start font-bold" onPress={cycleSortDirection} > {getText('modifiedColumnName')} diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx index 073ea48b2dce..ea0253d0c1aa 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx @@ -36,7 +36,6 @@ export default function NameColumnHeading(props: AssetColumnHeadingProps) { fullWidth size="custom" variant="custom" - weight="bold" aria-label={ !isSortActive ? getText('sortByName') : isDescending ? @@ -52,7 +51,7 @@ export default function NameColumnHeading(props: AssetColumnHeadingProps) { )} /> } - className="group sticky left-0 flex h-table-row justify-start bg-dashboard px-name-column-x" + className="group sticky left-0 flex h-table-row justify-start bg-dashboard px-name-column-x font-bold" onPress={cycleSortDirection} > {getText('nameColumnName')} From 81209bb2a66deee6492b87a8377fc6c8d2c9ea1f Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 21:22:28 +1000 Subject: [PATCH 13/27] [dashboard/DatePicker] Fix reset button placement --- .../components/AriaComponents/Inputs/DatePicker/DatePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx index 0cee4c7242fd..599991b81a89 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx @@ -61,7 +61,7 @@ const DATE_PICKER_STYLES = tv({ dateInput: 'flex justify-start grow order-2', dateSegment: 'rounded placeholder-shown:text-primary/30 focus:bg-primary/10 px-[0.5px]', calendarButton: 'order-1 rotate-90', - resetButton: '', + resetButton: 'order-2', calendarPopover: '', calendarDialog: 'text-primary text-xs mx-2', calendarContainer: '', From e7c04af82aef2f8423af7e226e365acab77bd44b Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 21:29:56 +1000 Subject: [PATCH 14/27] [dashboard/DatePicker] Fix foreign placeholders (visible in "Activity Log" settings page) --- .../Inputs/DatePicker/DatePicker.tsx | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx index 599991b81a89..558e794e38cf 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx @@ -80,6 +80,40 @@ const DATE_PICKER_STYLES = tv({ }, }) +/** Return the date segment using English placeholders. */ +function normalizeDateSegment(segment: DateSegmentType): DateSegmentType { + switch (segment.type) { + case 'era': { + return { ...segment, text: 'AD', placeholder: 'AD' } + } + case 'year': { + return { ...segment, text: 'yyyy', placeholder: 'yyyy' } + } + case 'month': { + return { ...segment, text: 'mm', placeholder: 'mm' } + } + case 'day': { + return { ...segment, text: 'dd', placeholder: 'dd' } + } + case 'hour': { + return { ...segment, text: 'HH', placeholder: 'HH' } + } + case 'minute': { + return { ...segment, text: 'MM', placeholder: 'MM' } + } + case 'second': { + return { ...segment, text: 'SS', placeholder: 'SS' } + } + case 'timeZoneName': { + return { ...segment, text: 'UTC+XX', placeholder: 'UTC+XX' } + } + case 'dayPeriod': + case 'literal': { + return segment + } + } +} + /** Props for a {@link DatePicker}. */ export interface DatePickerProps< Schema extends TSchema, @@ -175,7 +209,7 @@ export const DatePicker = forwardRef(function DatePicker< segments[segment.type] === false ? <> : Date: Mon, 3 Mar 2025 21:33:12 +1000 Subject: [PATCH 15/27] [dashboard] Fix margin to the left of short icons --- .../dashboard/columnHeading/ModifiedColumnHeading.tsx | 2 +- .../layouts/Settings/ActivityLogSettingsSection.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx index 0627797538aa..fe7079791b6d 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx @@ -63,7 +63,7 @@ export default function ModifiedColumnHeading(props: AssetColumnHeadingProps) { diff --git a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx index e6714d7b85d6..97a05b05f43d 100644 --- a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx +++ b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx @@ -220,7 +220,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec sortInfo?.field === ActivityLogSortableColumn.type, )} className={twMerge( - 'transition-all duration-arrow', + 'ml-1 transition-all duration-arrow', sortInfo?.field !== ActivityLogSortableColumn.type && 'opacity-0 group-hover:opacity-50', )} @@ -262,7 +262,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec sortInfo?.field === ActivityLogSortableColumn.email, )} className={twMerge( - 'transition-all duration-arrow', + 'ml-1 transition-all duration-arrow', sortInfo?.field !== ActivityLogSortableColumn.email && 'opacity-0 group-hover:opacity-50', )} @@ -305,7 +305,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec sortInfo?.field === ActivityLogSortableColumn.timestamp, )} className={twMerge( - 'transition-all duration-arrow', + 'ml-1 transition-all duration-arrow', sortInfo?.field !== ActivityLogSortableColumn.timestamp && 'opacity-0 group-hover:opacity-50', )} From e3ca1e7894fa38fed5be003eb8df210cfe462472 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 21:48:27 +1000 Subject: [PATCH 16/27] [dashboard] Allow destructive code actions for "organize imports" Prettier plugin to allow automatically removing unused imports --- .prettierrc.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.prettierrc.yaml b/.prettierrc.yaml index adf11cb2a67c..9b217182582b 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -19,7 +19,6 @@ overrides: printWidth: 100 trailingComma: "all" arrowParens: "always" - organizeImportsSkipDestructiveCodeActions: true experimentalTernaries: true tailwindConfig: ./app/gui/tailwind.config.ts From 5e59056e3f46afd5f6bee511246c298fb094409b Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:01:17 +1000 Subject: [PATCH 17/27] [dashboard] Prettier + refactor imports --- .../AriaComponents/Button/CopyButton.tsx | 6 --- .../components/AriaComponents/Separator.tsx | 26 ++++------ .../dashboard/components/ContextMenuEntry.tsx | 11 +---- .../components/Paywall/ContextMenuEntry.tsx | 32 ++++--------- .../components/Paywall/PaywallDialog.tsx | 42 +++++++---------- .../components/Paywall/PaywallScreen.tsx | 47 +++++++------------ .../components/PaywallBulletPoints.tsx | 30 ++++-------- .../src/dashboard/components/SubmitButton.tsx | 6 --- app/gui/src/dashboard/components/Twemoji.tsx | 9 ---- .../components/dashboard/AssetIcon.tsx | 2 - .../components/dashboard/AssetSummary.tsx | 6 --- .../dashboard/components/styled/Separator.tsx | 10 +--- .../AssetPanel/components/AssetVersions.tsx | 1 - app/gui/src/dashboard/layouts/Samples.tsx | 11 +---- .../DeleteUserAccountSettingsSection.tsx | 35 +++++--------- .../dashboard/layouts/Settings/Paywall.tsx | 17 +++---- app/gui/src/dashboard/layouts/StartModal.tsx | 22 +++------ app/gui/src/dashboard/layouts/WhatsNew.tsx | 14 ++---- .../modals/ConfirmDeleteUserModal.tsx | 5 +- .../src/dashboard/modals/ProjectLogsModal.tsx | 32 +++++-------- .../pages/authentication/ErrorScreen.tsx | 22 +++------ 21 files changed, 115 insertions(+), 271 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/CopyButton.tsx b/app/gui/src/dashboard/components/AriaComponents/Button/CopyButton.tsx index 5a5f57801211..17ab114fb05f 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/CopyButton.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Button/CopyButton.tsx @@ -1,6 +1,4 @@ /** @file A button that copies text to the clipboard. */ -import * as React from 'react' - import Error from '#/assets/cross.svg' import CopyIcon from '#/assets/duplicate.svg' import Done from '#/assets/tick.svg' @@ -12,10 +10,6 @@ import * as textProvider from '#/providers/TextProvider' import { Button } from './Button' import type { ButtonProps } from './types' -// ================== -// === CopyButton === -// ================== - /** Props for a {@link CopyButton}. */ export interface CopyButtonProps extends Omit, 'icon' | 'loading' | 'onPress'> { diff --git a/app/gui/src/dashboard/components/AriaComponents/Separator.tsx b/app/gui/src/dashboard/components/AriaComponents/Separator.tsx index c2df4fe69691..c2a9c394e13b 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Separator.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Separator.tsx @@ -1,23 +1,17 @@ -/** - * @file - * - * The separator component. - */ -import * as React from 'react' - -import * as aria from '#/components/aria' - -import * as twv from '#/utilities/tailwindVariants' +/** @file A visual separator. */ +import { + Separator as AriaSeparator, + type SeparatorProps as AriaSeparatorProps, +} from '#/components/aria' +import { tv, type VariantProps } from '#/utilities/tailwindVariants' /** The props for {@link Separator} component. */ -export interface SeparatorProps - extends aria.SeparatorProps, - twv.VariantProps { +export interface SeparatorProps extends AriaSeparatorProps, VariantProps { readonly className?: string | undefined } /** The styles for the {@link Separator} component. */ -export const SEPARATOR_STYLES = twv.tv({ +export const SEPARATOR_STYLES = tv({ base: 'rounded-full border-none', variants: { size: { @@ -75,7 +69,7 @@ export const SEPARATOR_STYLES = twv.tv({ ], }) -/** A separator component. */ +/** A visual separator. */ export function Separator(props: SeparatorProps) { const { orientation = 'horizontal', @@ -88,5 +82,5 @@ export function Separator(props: SeparatorProps) { const styles = variants({ orientation, variant, size, className }) - return + return } diff --git a/app/gui/src/dashboard/components/ContextMenuEntry.tsx b/app/gui/src/dashboard/components/ContextMenuEntry.tsx index 28332f7e470a..c2b7912e2751 100644 --- a/app/gui/src/dashboard/components/ContextMenuEntry.tsx +++ b/app/gui/src/dashboard/components/ContextMenuEntry.tsx @@ -1,15 +1,8 @@ /** @file An entry in a context menu. */ -import * as React from 'react' - -import type * as menuEntry from '#/components/MenuEntry' -import MenuEntry from '#/components/MenuEntry' - -// ======================== -// === ContextMenuEntry === -// ======================== +import MenuEntry, { type MenuEntryProps } from '#/components/MenuEntry' /** Props for a {@link ContextMenuEntry}. */ -export type ContextMenuEntryProps = Omit +export type ContextMenuEntryProps = Omit /** An item in a menu. */ export default function ContextMenuEntry(props: ContextMenuEntryProps) { diff --git a/app/gui/src/dashboard/components/Paywall/ContextMenuEntry.tsx b/app/gui/src/dashboard/components/Paywall/ContextMenuEntry.tsx index c8e5d4fee000..de449bdf90e9 100644 --- a/app/gui/src/dashboard/components/Paywall/ContextMenuEntry.tsx +++ b/app/gui/src/dashboard/components/Paywall/ContextMenuEntry.tsx @@ -1,34 +1,22 @@ -/** - * @file - * - * A context menu entry that opens a paywall dialog. - */ - -import * as React from 'react' - +/** @file A context menu entry that opens a paywall dialog. */ import LockIcon from '#/assets/lock.svg' - -import type * as billingHooks from '#/hooks/billing' - -import * as modalProvider from '#/providers/ModalProvider' - -import type * as contextMenuEntry from '#/components/ContextMenuEntry' +import type { ContextMenuEntryProps as ContextMenuEntryBaseProps } from '#/components/ContextMenuEntry' import ContextMenuEntryBase from '#/components/ContextMenuEntry' - +import type { PaywallFeatureName } from '#/hooks/billing' +import { useSetModal } from '#/providers/ModalProvider' import { useText } from '#/providers/TextProvider' -import * as paywallDialog from './PaywallDialog' +import { PaywallDialog } from './PaywallDialog' /** Props for {@link ContextMenuEntry}. */ -export interface ContextMenuEntryProps - extends Omit { - readonly feature: billingHooks.PaywallFeatureName +export interface ContextMenuEntryProps extends Omit { + readonly feature: PaywallFeatureName readonly isUnderPaywall: boolean } /** A context menu entry that opens a paywall dialog. */ export function ContextMenuEntry(props: ContextMenuEntryProps) { const { feature, isUnderPaywall, doAction, icon, ...rest } = props - const { setModal } = modalProvider.useSetModal() + const { setModal } = useSetModal() const { getText } = useText() return ( @@ -38,9 +26,7 @@ export function ContextMenuEntry(props: ContextMenuEntryProps) { tooltip={isUnderPaywall ? getText('upgradeToUseCloud') : null} doAction={() => { if (isUnderPaywall) { - setModal( - , - ) + setModal() } else { doAction() } diff --git a/app/gui/src/dashboard/components/Paywall/PaywallDialog.tsx b/app/gui/src/dashboard/components/Paywall/PaywallDialog.tsx index c0c344e03d21..da3b8911d867 100644 --- a/app/gui/src/dashboard/components/Paywall/PaywallDialog.tsx +++ b/app/gui/src/dashboard/components/Paywall/PaywallDialog.tsx @@ -1,45 +1,35 @@ -/** - * @file - * - * A dialog that prompts the user to upgrade to a paid plan. - */ - -import * as React from 'react' - -import * as billingHooks from '#/hooks/billing' - -import * as textProvider from '#/providers/TextProvider' - -import * as ariaComponents from '#/components/AriaComponents' - -import * as components from './components' -import * as upgradeButton from './UpgradeButton' +/** @file A dialog that prompts the user to upgrade to a paid plan. */ +import { Dialog, Text, type DialogProps } from '#/components/AriaComponents' +import { usePaywallFeatures, type PaywallFeatureName } from '#/hooks/billing' +import { useText } from '#/providers/TextProvider' +import { PaywallBulletPoints, PaywallLock } from './components' +import { UpgradeButton } from './UpgradeButton' /** Props for a {@link PaywallDialog}. */ -export interface PaywallDialogProps extends ariaComponents.DialogProps { - readonly feature: billingHooks.PaywallFeatureName +export interface PaywallDialogProps extends DialogProps { + readonly feature: PaywallFeatureName } /** A dialog that prompts the user to upgrade to a paid plan. */ export function PaywallDialog(props: PaywallDialogProps) { const { feature, type = 'modal', title, ...dialogProps } = props - const { getText } = textProvider.useText() - const { getFeature } = billingHooks.usePaywallFeatures() + const { getText } = useText() + const { getFeature } = usePaywallFeatures() const { bulletPointsTextId, label, descriptionTextId } = getFeature(feature) return ( - +
- + - {getText(descriptionTextId)} + {getText(descriptionTextId)} - + - +
-
+ ) } diff --git a/app/gui/src/dashboard/components/Paywall/PaywallScreen.tsx b/app/gui/src/dashboard/components/Paywall/PaywallScreen.tsx index 34ce0b534b4d..c793a4de45df 100644 --- a/app/gui/src/dashboard/components/Paywall/PaywallScreen.tsx +++ b/app/gui/src/dashboard/components/Paywall/PaywallScreen.tsx @@ -1,52 +1,39 @@ -/** - * @file - * - * A screen that shows a paywall. - */ - -import * as React from 'react' - -import * as tw from 'tailwind-merge' - -import * as billingHooks from '#/hooks/billing' - -import * as textProvider from '#/providers/TextProvider' - -import * as ariaComponents from '#/components/AriaComponents' - -import * as components from './components' -import * as upgradeButton from './UpgradeButton' +/** @file A screen that shows a paywall. */ +import { Text } from '#/components/AriaComponents' +import { usePaywallFeatures, type PaywallFeatureName } from '#/hooks/billing' +import { useText } from '#/providers/TextProvider' +import { twMerge } from '#/utilities/tailwindMerge' +import { PaywallBulletPoints, PaywallLock } from './components' +import { UpgradeButton } from './UpgradeButton' /** Props for a {@link PaywallScreen}. */ export interface PaywallScreenProps { - readonly feature: billingHooks.PaywallFeatureName + readonly feature: PaywallFeatureName readonly className?: string } /** A screen that shows a paywall. */ export function PaywallScreen(props: PaywallScreenProps) { const { feature, className } = props - const { getText } = textProvider.useText() + const { getText } = useText() - const { getFeature } = billingHooks.usePaywallFeatures() + const { getFeature } = usePaywallFeatures() const { bulletPointsTextId, descriptionTextId } = getFeature(feature) return ( -
- +
+ - - {getText('paywallScreenTitle')} - + {getText('paywallScreenTitle')} - + {getText(descriptionTextId)} - + - + - +
) } diff --git a/app/gui/src/dashboard/components/Paywall/components/PaywallBulletPoints.tsx b/app/gui/src/dashboard/components/Paywall/components/PaywallBulletPoints.tsx index cae54ed1100d..9315c710a051 100644 --- a/app/gui/src/dashboard/components/Paywall/components/PaywallBulletPoints.tsx +++ b/app/gui/src/dashboard/components/Paywall/components/PaywallBulletPoints.tsx @@ -1,24 +1,14 @@ -/** - * @file - * - * A component that renders a list of bullet points for a paywall. - */ -import * as React from 'react' - -import * as tw from 'tailwind-merge' - -import type * as text from 'enso-common/src/text' - +/** @file A list of bullet points for a paywall. */ import Check from '#/assets/check_mark.svg' - -import * as textProvider from '#/providers/TextProvider' - -import * as ariaComponents from '#/components/AriaComponents' +import { Text } from '#/components/AriaComponents' import SvgMask from '#/components/SvgMask' +import { useText } from '#/providers/TextProvider' +import { twMerge } from '#/utilities/tailwindMerge' +import type { TextId } from 'enso-common/src/text' /** Props for a {@link PaywallBulletPoints}. */ export interface PaywallBulletPointsProps { - readonly bulletPointsTextId: text.TextId + readonly bulletPointsTextId: TextId readonly className?: string } @@ -26,7 +16,7 @@ export interface PaywallBulletPointsProps { export function PaywallBulletPoints(props: PaywallBulletPointsProps) { const { bulletPointsTextId, className } = props - const { getText } = textProvider.useText() + const { getText } = useText() const bulletPoints = getText(bulletPointsTextId) .split(';') .map((bulletPoint) => bulletPoint.trim()) @@ -35,7 +25,7 @@ export function PaywallBulletPoints(props: PaywallBulletPointsProps) { return null } else { return ( -
    +
      {bulletPoints.map((bulletPoint) => (
    • @@ -46,9 +36,9 @@ export function PaywallBulletPoints(props: PaywallBulletPointsProps) {
- + {bulletPoint} - +
))} diff --git a/app/gui/src/dashboard/components/SubmitButton.tsx b/app/gui/src/dashboard/components/SubmitButton.tsx index 5a1ba7c5ed0f..ea2fcf14853f 100644 --- a/app/gui/src/dashboard/components/SubmitButton.tsx +++ b/app/gui/src/dashboard/components/SubmitButton.tsx @@ -1,13 +1,7 @@ /** @file A styled submit button. */ -import * as React from 'react' - import { Button } from '#/components/AriaComponents' import { submitForm } from '#/utilities/event' -// ==================== -// === SubmitButton === -// ==================== - /** Props for a {@link SubmitButton}. */ export interface SubmitButtonProps { readonly isLoading?: boolean diff --git a/app/gui/src/dashboard/components/Twemoji.tsx b/app/gui/src/dashboard/components/Twemoji.tsx index 46a952ad02c4..1fdc066150de 100644 --- a/app/gui/src/dashboard/components/Twemoji.tsx +++ b/app/gui/src/dashboard/components/Twemoji.tsx @@ -1,17 +1,8 @@ /** @file A wrapper for a twemoji image. */ -import * as React from 'react' - -// ================= -// === Constants === -// ================= /** The base of hexadecimal numbers. */ const HEXADECIMAL = 16 -// =============== -// === Twemoji === -// =============== - /** Props for a {@link Twemoji}. */ export interface TwemojiProps { readonly emoji: string diff --git a/app/gui/src/dashboard/components/dashboard/AssetIcon.tsx b/app/gui/src/dashboard/components/dashboard/AssetIcon.tsx index b2d9bec546cf..a4bdb86a4e47 100644 --- a/app/gui/src/dashboard/components/dashboard/AssetIcon.tsx +++ b/app/gui/src/dashboard/components/dashboard/AssetIcon.tsx @@ -1,6 +1,4 @@ /** @file Displays a non-interactable icon for an asset based on its type and name. */ -import * as React from 'react' - import BlankIcon from '#/assets/blank.svg' import DatalinkIcon from '#/assets/datalink.svg' import FolderIcon from '#/assets/folder.svg' diff --git a/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx b/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx index 56d0d0028a3a..1b14b0e0d228 100644 --- a/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx +++ b/app/gui/src/dashboard/components/dashboard/AssetSummary.tsx @@ -1,6 +1,4 @@ /** @file Displays a few details of an asset. */ -import * as React from 'react' - import BreadcrumbArrowIcon from '#/assets/breadcrumb_arrow.svg' import * as textProvider from '#/providers/TextProvider' @@ -13,10 +11,6 @@ import type * as backend from '#/services/Backend' import * as tailwindMerge from '#/utilities/tailwindMerge' import * as dateTime from 'enso-common/src/utilities/data/dateTime' -// ==================== -// === AssetSummary === -// ==================== - /** Props for an {@link AssetSummary}. */ export interface AssetSummaryProps { readonly asset: backend.AnyAsset diff --git a/app/gui/src/dashboard/components/styled/Separator.tsx b/app/gui/src/dashboard/components/styled/Separator.tsx index 870498c18f01..ddc9a0a5e3cf 100644 --- a/app/gui/src/dashboard/components/styled/Separator.tsx +++ b/app/gui/src/dashboard/components/styled/Separator.tsx @@ -1,11 +1,5 @@ /** @file A horizontal line dividing two sections in a menu. */ -import * as React from 'react' - -import * as aria from '#/components/aria' - -// ================= -// === Separator === -// ================= +import { Separator as AriaSeparator } from '#/components/aria' /** Props for a {@link Separator}. */ export interface SeparatorProps { @@ -18,7 +12,7 @@ export default function Separator(props: SeparatorProps) { return ( !hidden && ( - + ) ) } diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/AssetVersions.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/AssetVersions.tsx index bf50240a975d..34d2cca84788 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/AssetVersions.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/AssetVersions.tsx @@ -1,5 +1,4 @@ /** @file A list of previous versions of an asset. */ -import * as React from 'react' import { useState } from 'react' import { useMutation, useSuspenseQuery } from '@tanstack/react-query' diff --git a/app/gui/src/dashboard/layouts/Samples.tsx b/app/gui/src/dashboard/layouts/Samples.tsx index cfeedd39090f..0a9e12d8f80e 100644 --- a/app/gui/src/dashboard/layouts/Samples.tsx +++ b/app/gui/src/dashboard/layouts/Samples.tsx @@ -1,5 +1,4 @@ /** @file Renders the list of templates from which a project can be created. */ -import * as React from 'react' import ReadAndFilterImage from '#/assets/ReadAndFilter.png' import AggregatingImage from '#/assets/aggregate.png' @@ -17,10 +16,6 @@ import WeatherImage from '#/assets/weather.png' import { Button, Text } from '#/components/AriaComponents' -// ========================= -// === List of templates === -// ========================= - /** Template metadata. */ export interface Sample { readonly title: string @@ -170,10 +165,6 @@ function ProjectTile(props: InternalProjectTileProps) { ) } -// =============== -// === Samples === -// =============== - /** Props for a {@link Samples}. */ export interface SamplesProps { readonly groupName: string @@ -181,7 +172,7 @@ export interface SamplesProps { } /** A list of sample projects. */ -export default function Samples(props: SamplesProps) { +export function Samples(props: SamplesProps) { const { groupName, createProject } = props return ( diff --git a/app/gui/src/dashboard/layouts/Settings/DeleteUserAccountSettingsSection.tsx b/app/gui/src/dashboard/layouts/Settings/DeleteUserAccountSettingsSection.tsx index a32c57ab64d6..59ebd0881b2e 100644 --- a/app/gui/src/dashboard/layouts/Settings/DeleteUserAccountSettingsSection.tsx +++ b/app/gui/src/dashboard/layouts/Settings/DeleteUserAccountSettingsSection.tsx @@ -1,23 +1,14 @@ /** @file Settings tab for deleting the current user. */ -import * as React from 'react' - -import * as authProvider from '#/providers/AuthProvider' -import * as textProvider from '#/providers/TextProvider' - -import * as aria from '#/components/aria' -import * as ariaComponents from '#/components/AriaComponents' +import { Button, DialogTrigger, Text } from '#/components/AriaComponents' import FocusArea from '#/components/styled/FocusArea' - -import ConfirmDeleteUserModal from '#/modals/ConfirmDeleteUserModal' - -// ======================================== -// === DeleteUserAccountSettingsSection === -// ======================================== +import { ConfirmDeleteUserModal } from '#/modals/ConfirmDeleteUserModal' +import { useAuth } from '#/providers/AuthProvider' +import { useText } from '#/providers/TextProvider' /** Settings tab for deleting the current user. */ export default function DeleteUserAccountSettingsSection() { - const { deleteUser } = authProvider.useAuth() - const { getText } = textProvider.useText() + const { deleteUser } = useAuth() + const { getText } = useText() return ( @@ -26,21 +17,19 @@ export default function DeleteUserAccountSettingsSection() { className="flex flex-col items-start gap-2.5 rounded-2.5xl border-2 border-danger px-[1rem] pb-[0.9375rem] pt-[0.5625rem]" {...innerProps} > - - {getText('dangerZone')} - + {getText('dangerZone')}
- - + + { await deleteUser() }} /> - - {getText('deleteUserAccountWarning')} + + {getText('deleteUserAccountWarning')}
)} diff --git a/app/gui/src/dashboard/layouts/Settings/Paywall.tsx b/app/gui/src/dashboard/layouts/Settings/Paywall.tsx index 430dbebcfc67..d87f78b39eae 100644 --- a/app/gui/src/dashboard/layouts/Settings/Paywall.tsx +++ b/app/gui/src/dashboard/layouts/Settings/Paywall.tsx @@ -5,23 +5,18 @@ * The paywall is shown if the user's plan does not include the feature. * The feature is determined by the `isFeatureUnderPaywall` hook. */ - -import * as React from 'react' - -import type * as billingHooks from '#/hooks/billing' - -import * as paywallComponents from '#/components/Paywall' - -import * as twv from '#/utilities/tailwindVariants' +import { PaywallScreen } from '#/components/Paywall' +import type { PaywallFeatureName } from '#/hooks/billing' +import { tv } from '#/utilities/tailwindVariants' /** Props for a {@link SettingsPaywall}. */ export interface SettingsPaywallProps { - readonly feature: billingHooks.PaywallFeatureName + readonly feature: PaywallFeatureName readonly className?: string | undefined readonly onInteracted?: () => void } -const PAYWALL_LAYOUT_STYLES = twv.tv({ base: 'mt-1' }) +const PAYWALL_LAYOUT_STYLES = tv({ base: 'mt-1' }) /** A layout that shows a paywall for a feature. */ export default function SettingsPaywall(props: SettingsPaywallProps) { @@ -34,7 +29,7 @@ export default function SettingsPaywall(props: SettingsPaywallProps) { onPointerDown={onInteracted} onFocus={onInteracted} > - +
) } diff --git a/app/gui/src/dashboard/layouts/StartModal.tsx b/app/gui/src/dashboard/layouts/StartModal.tsx index 9068c51f1a07..dd73f58dacc2 100644 --- a/app/gui/src/dashboard/layouts/StartModal.tsx +++ b/app/gui/src/dashboard/layouts/StartModal.tsx @@ -1,16 +1,8 @@ /** @file A modal containing project templates and news. */ -import * as React from 'react' - -import * as textProvider from '#/providers/TextProvider' - -import Samples from '#/layouts/Samples' -import WhatsNew from '#/layouts/WhatsNew' - -import * as ariaComponents from '#/components/AriaComponents' - -// ================== -// === StartModal === -// ================== +import { Dialog } from '#/components/AriaComponents' +import { Samples } from '#/layouts/Samples' +import { WhatsNew } from '#/layouts/WhatsNew' +import { useText } from '#/providers/TextProvider' /** Props for a {@link StartModal}. */ export interface StartModalProps { @@ -20,10 +12,10 @@ export interface StartModalProps { /** A modal containing project templates and news. */ export default function StartModal(props: StartModalProps) { const { createProject } = props - const { getText } = textProvider.useText() + const { getText } = useText() return ( - + {(opts) => (
@@ -53,6 +45,6 @@ export default function StartModal(props: StartModalProps) { />
)} -
+ ) } diff --git a/app/gui/src/dashboard/layouts/WhatsNew.tsx b/app/gui/src/dashboard/layouts/WhatsNew.tsx index 6b608e1a4733..595c0320db5e 100644 --- a/app/gui/src/dashboard/layouts/WhatsNew.tsx +++ b/app/gui/src/dashboard/layouts/WhatsNew.tsx @@ -1,23 +1,15 @@ /** @file Community updates for the app. */ -import * as React from 'react' - import BookImage from '#/assets/book.png' import Logo from '#/assets/enso_logo_large.svg' import IntegrationsImage from '#/assets/integrations.png' import YoutubeIcon from '#/assets/youtube.svg' import { Button, Text } from '#/components/AriaComponents' - -import * as textProvider from '#/providers/TextProvider' - import SvgMask from '#/components/SvgMask' - -// ================ -// === WhatsNew === -// ================ +import { useText } from '#/providers/TextProvider' /** Community updates for the app. */ -export default function WhatsNew() { - const { getText } = textProvider.useText() +export function WhatsNew() { + const { getText } = useText() return (
diff --git a/app/gui/src/dashboard/modals/ConfirmDeleteUserModal.tsx b/app/gui/src/dashboard/modals/ConfirmDeleteUserModal.tsx index 0f2dcb8e8d8a..9b383909aa34 100644 --- a/app/gui/src/dashboard/modals/ConfirmDeleteUserModal.tsx +++ b/app/gui/src/dashboard/modals/ConfirmDeleteUserModal.tsx @@ -1,8 +1,7 @@ /** @file Modal for confirming delete of any type of asset. */ -import * as z from 'zod' - import { ButtonGroup, Dialog, Form, Text } from '#/components/AriaComponents' import { useText } from '#/providers/TextProvider' +import * as z from 'zod' /** Props for a {@link ConfirmDeleteUserModal}. */ export interface ConfirmDeleteUserModalProps { @@ -10,7 +9,7 @@ export interface ConfirmDeleteUserModalProps { } /** A modal for confirming the deletion of a user. */ -export default function ConfirmDeleteUserModal(props: ConfirmDeleteUserModalProps) { +export function ConfirmDeleteUserModal(props: ConfirmDeleteUserModalProps) { const { doDelete } = props const { getText } = useText() diff --git a/app/gui/src/dashboard/modals/ProjectLogsModal.tsx b/app/gui/src/dashboard/modals/ProjectLogsModal.tsx index 0deab138dc3b..66dace416d7b 100644 --- a/app/gui/src/dashboard/modals/ProjectLogsModal.tsx +++ b/app/gui/src/dashboard/modals/ProjectLogsModal.tsx @@ -1,45 +1,35 @@ /** @file A modal for showing logs for a project. */ -import * as React from 'react' - -import * as reactQuery from '@tanstack/react-query' - import ReloadIcon from '#/assets/reload.svg' - -import * as textProvider from '#/providers/TextProvider' - -import * as ariaComponents from '#/components/AriaComponents' - -import type * as backendModule from '#/services/Backend' +import { Button, Dialog } from '#/components/AriaComponents' +import { useText } from '#/providers/TextProvider' import type Backend from '#/services/Backend' - -// ======================== -// === ProjectLogsModal === -// ======================== +import type { ProjectSessionId } from '#/services/Backend' +import { useSuspenseQuery } from '@tanstack/react-query' /** Props for a {@link ProjectLogsModal}. */ export interface ProjectLogsModalProps { readonly backend: Backend - readonly projectSessionId: backendModule.ProjectSessionId + readonly projectSessionId: ProjectSessionId readonly projectTitle: string } /** A modal for showing logs for a project. */ export default function ProjectLogsModal(props: ProjectLogsModalProps) { - const { getText } = textProvider.useText() + const { getText } = useText() return ( - + {() => } - + ) } /** A modal for showing logs for a project. */ function ProjectLogsModalInternal(props: ProjectLogsModalProps) { const { backend, projectSessionId, projectTitle } = props - const { getText } = textProvider.useText() + const { getText } = useText() - const logsQuery = reactQuery.useSuspenseQuery({ + const logsQuery = useSuspenseQuery({ queryKey: ['projectLogs', { projectSessionId, projectTitle }], queryFn: async () => { const logs = await backend.getProjectSessionLogs(projectSessionId, projectTitle) @@ -50,7 +40,7 @@ function ProjectLogsModalInternal(props: ProjectLogsModalProps) { return (
-
- {getText('appErroredMessage')} - {getText('appErroredPrompt')} - {errorModule.getMessageOrToString(error)} + {getText('appErroredMessage')} + {getText('appErroredPrompt')} + {getMessageOrToString(error)}
) From 7ae9d02c327d668b39d8ae010b3982c446f700f6 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:07:04 +1000 Subject: [PATCH 18/27] [dashboard] Remove `` wrapper for button content --- .../dashboard/components/AriaComponents/Button/Button.tsx | 6 ++---- .../dashboard/components/AriaComponents/Button/variants.ts | 1 - .../dashboard/columnHeading/ModifiedColumnHeading.tsx | 6 +++--- .../dashboard/columnHeading/NameColumnHeading.tsx | 6 +++--- .../layouts/Settings/ActivityLogSettingsSection.tsx | 6 +++--- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx b/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx index 9d59313ca8c6..aa35ea10142e 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Button/Button.tsx @@ -10,7 +10,7 @@ import { } from 'react' import * as aria from '#/components/aria' -import { Text, useVisualTooltip } from '#/components/AriaComponents/Text' +import { useVisualTooltip } from '#/components/AriaComponents/Text' import { Tooltip, TooltipTrigger } from '#/components/AriaComponents/Tooltip' import { Icon as IconComponent } from '#/components/Icon' import { StatelessSpinner } from '#/components/StatelessSpinner' @@ -337,9 +337,7 @@ const ButtonContent = memo(function ButtonContent(props: ButtonContentProps) { styles={styles} hideLoader={hideLoader} /> - - {children} - + {children} {hasAddon(addonEnd) &&
{addonEnd}
} ) diff --git a/app/gui/src/dashboard/components/AriaComponents/Button/variants.ts b/app/gui/src/dashboard/components/AriaComponents/Button/variants.ts index 29a2d9651a32..fc1e0414ae7f 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Button/variants.ts +++ b/app/gui/src/dashboard/components/AriaComponents/Button/variants.ts @@ -204,7 +204,6 @@ export const BUTTON_STYLES = tv({ wrapper: 'relative block max-w-full', loader: 'absolute inset-0 flex items-center justify-center', content: 'flex items-center', - text: 'w-full max-w-full', icon: 'h-[1.906cap] w-[1.906cap] flex-none aspect-square flex items-center justify-center', addonStart: 'flex items-center justify-center macos:-mb-0.5', addonEnd: 'flex items-center justify-center macos:-mb-0.5', diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx index fe7079791b6d..d162fb477a08 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/ModifiedColumnHeading.tsx @@ -1,6 +1,6 @@ /** @file A heading for the "Modified" column. */ import TimeIcon from '#/assets/time.svg' -import { Button } from '#/components/AriaComponents' +import { Button, Text } from '#/components/AriaComponents' import { Icon } from '#/components/Icon' import type { AssetColumnHeadingProps } from '#/components/dashboard/column' import { Column } from '#/components/dashboard/column/columnUtils' @@ -68,10 +68,10 @@ export default function ModifiedColumnHeading(props: AssetColumnHeadingProps) { )} /> } - className="flex justify-start font-bold" + className="flex justify-start" onPress={cycleSortDirection} > - {getText('modifiedColumnName')} + {getText('modifiedColumnName')}
) diff --git a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx index ea0253d0c1aa..b89e43a50375 100644 --- a/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx +++ b/app/gui/src/dashboard/components/dashboard/columnHeading/NameColumnHeading.tsx @@ -1,5 +1,5 @@ /** @file A heading for the "Name" column. */ -import { Button } from '#/components/AriaComponents' +import { Button, Text } from '#/components/AriaComponents' import { Icon } from '#/components/Icon' import type { AssetColumnHeadingProps } from '#/components/dashboard/column' import { Column } from '#/components/dashboard/column/columnUtils' @@ -51,10 +51,10 @@ export default function NameColumnHeading(props: AssetColumnHeadingProps) { )} /> } - className="group sticky left-0 flex h-table-row justify-start bg-dashboard px-name-column-x font-bold" + className="group sticky left-0 flex h-table-row justify-start bg-dashboard px-name-column-x" onPress={cycleSortDirection} > - {getText('nameColumnName')} + {getText('nameColumnName')} ) } diff --git a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx index 97a05b05f43d..073e97c8f970 100644 --- a/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx +++ b/app/gui/src/dashboard/layouts/Settings/ActivityLogSettingsSection.tsx @@ -242,7 +242,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('type')} + {getText('type')} @@ -284,7 +284,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('email')} + {getText('email')} @@ -327,7 +327,7 @@ export default function ActivityLogSettingsSection(props: ActivityLogSettingsSec } }} > - {getText('timestamp')} + {getText('timestamp')} From cfd6a5e6ad50f4e3d97eb69a8c2f4ee6641cbad0 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:09:06 +1000 Subject: [PATCH 19/27] [dashboard/DatePicker] Fix segments always showing as placeholders --- .../components/AriaComponents/Inputs/DatePicker/DatePicker.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx index 558e794e38cf..f7d7aade74ec 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Inputs/DatePicker/DatePicker.tsx @@ -82,6 +82,9 @@ const DATE_PICKER_STYLES = tv({ /** Return the date segment using English placeholders. */ function normalizeDateSegment(segment: DateSegmentType): DateSegmentType { + if (segment.text !== segment.placeholder) { + return segment + } switch (segment.type) { case 'era': { return { ...segment, text: 'AD', placeholder: 'AD' } From c6e79bd10b066a9c266eb87751b3477001c7d1f3 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:11:11 +1000 Subject: [PATCH 20/27] [dashboard/ProjectExecution] Add truncated text and hover tooltip back --- .../layouts/AssetPanel/components/ProjectExecution.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx index 70c053b54de3..98ad86d0e951 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx @@ -236,7 +236,7 @@ export function ProjectExecution(props: ProjectExecutionProps) { tooltipPlacement="left" className={styles.info()} > - {maxDurationDescription} + {maxDurationDescription} )} )} From 8f50c31632ad4897fae58b46104d267efaaee900 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:14:40 +1000 Subject: [PATCH 21/27] [dashboard/ProjectExecutionsCalendar] Avoid fading out date numbers in this month --- .../layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx index cf556806a165..dcd04703341c 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecutionsCalendar.tsx @@ -187,7 +187,7 @@ function ProjectExecutionsCalendarInternal(props: ProjectExecutionsCalendarInter
{date.day} From ef51cb25d94453a2dc5c999c0124c0102edd829c Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 22:19:37 +1000 Subject: [PATCH 22/27] [project-view] Prettier --- app/gui/src/project-view/composables/syncLocalStorage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/gui/src/project-view/composables/syncLocalStorage.ts b/app/gui/src/project-view/composables/syncLocalStorage.ts index fe067478af9e..ec36f04ccf2a 100644 --- a/app/gui/src/project-view/composables/syncLocalStorage.ts +++ b/app/gui/src/project-view/composables/syncLocalStorage.ts @@ -1,7 +1,6 @@ import { useAbortScope } from '@/util/net' import { debouncedWatch, useLocalStorage } from '@vueuse/core' import { encoding } from 'lib0' -import { Encoder } from 'lib0/encoding.js' import { computed, getCurrentInstance, ref, watch, withCtx } from 'vue' import { xxHash128 } from 'ydoc-shared/ast/ffi' import { AbortScope } from 'ydoc-shared/util/net' From a95b150eb6e32db991324241794f00818ec931c2 Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 3 Mar 2025 23:07:04 +1000 Subject: [PATCH 23/27] [dashboard/ProjectExecution] Show date instead of "Does not repeat" in execution info --- .../layouts/AssetPanel/components/ProjectExecution.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx index 98ad86d0e951..da592a9d9d46 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx @@ -7,6 +7,7 @@ import { getDescriptionForTimeZone, getTimeZoneOffsetStringWithGMT, MONTH_3_LETTER_TEXT_IDS, + zonedDateTimeToReadableIsoString, } from 'enso-common/src/utilities/data/dateTime' import LogsIcon from '#/assets/logs.svg' @@ -107,7 +108,7 @@ export function ProjectExecution(props: ProjectExecutionProps) { ) switch (repeat.type) { case 'none': { - return getText('doesNotRepeat') + return zonedDateTimeToReadableIsoString(zonedStartDate) } case 'daily': { return `${startDateDailyRepeat} ${getText('everyDaySuffix')}` From 438c4f3120ae91304b5788611366a03d12f524fe Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Wed, 5 Mar 2025 19:23:41 +1000 Subject: [PATCH 24/27] [dashboard/IconDisplay] Add and use component --- .../IconDisplay/IconDisplay.tsx | 68 +++++++++++++++++++ .../AriaComponents/IconDisplay/index.ts | 2 + .../components/AriaComponents/index.ts | 1 + .../components/AriaComponents/types.ts | 6 +- .../components/Breadcrumbs/BreadcrumbItem.tsx | 28 ++++---- .../src/dashboard/components/Icon/Icon.tsx | 4 +- .../components/ProjectExecution.tsx | 25 +++---- 7 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx create mode 100644 app/gui/src/dashboard/components/AriaComponents/IconDisplay/index.ts diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx new file mode 100644 index 000000000000..bac1894fc03f --- /dev/null +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx @@ -0,0 +1,68 @@ +/** @file A text display with an icon. */ +import { Icon } from '#/components/Icon' +import { tv, type VariantProps } from '#/utilities/tailwindVariants' +import type { ReactNode } from 'react' +import { Text, type IconProp, type TextProps } from '..' + +const ICON_DISPLAY_STYLES = tv({ + base: 'block max-w-48 min-w-4 w-auto', + slots: { + container: 'flex items-center gap-2', + icon: '-mb-0.5', + }, + variants: { + variant: { + custom: '', + link: 'inline-block px-0 py-0 rounded-sm text-primary/50 underline border-0', + primary: 'bg-primary text-white', + accent: 'bg-accent text-white', + ghost: 'text-primary', + submit: 'bg-invite text-white opacity-80', + outline: 'border-primary/20 text-primary', + }, + }, +}) + +/** Render props for {@link IconDisplay}. */ +export interface IconDisplayRenderProps { + /** Defaults to `true`. */ + readonly isCurrent?: boolean + /** Defaults to `false`. */ + readonly isDisabled?: boolean +} + +/** Props for an {@link IconDisplay}. */ +export interface IconDisplayProps + extends Omit, + IconDisplayRenderProps, + VariantProps { + readonly icon: IconProp> + readonly children: ReactNode | ((renderProps: Required) => ReactNode) +} + +/** A text display with an icon. */ +export function IconDisplay(props: IconDisplayProps) { + const { + icon, + isCurrent = true, + isDisabled = false, + children, + variant, + variants = ICON_DISPLAY_STYLES, + ...textProps + } = props + const renderProps = { isCurrent, isDisabled } + + const styles = variants({ variant }) + + return ( + + + + {icon} + + {typeof children === 'function' ? children(renderProps) : children} + + + ) +} diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/index.ts b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/index.ts new file mode 100644 index 000000000000..ae1df0e5b0d3 --- /dev/null +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/index.ts @@ -0,0 +1,2 @@ +/** Barrel file for `IconDisplay`. */ +export * from './IconDisplay' diff --git a/app/gui/src/dashboard/components/AriaComponents/index.ts b/app/gui/src/dashboard/components/AriaComponents/index.ts index fabf723fa374..eac657711dc6 100644 --- a/app/gui/src/dashboard/components/AriaComponents/index.ts +++ b/app/gui/src/dashboard/components/AriaComponents/index.ts @@ -8,6 +8,7 @@ export * from './Checkbox' export * from './CopyBlock' export * from './Dialog' export * from './Form' +export * from './IconDisplay' export * from './Inputs' export * from './Menu' export * from './Radio' diff --git a/app/gui/src/dashboard/components/AriaComponents/types.ts b/app/gui/src/dashboard/components/AriaComponents/types.ts index 85ba06710c45..d23946b84cd6 100644 --- a/app/gui/src/dashboard/components/AriaComponents/types.ts +++ b/app/gui/src/dashboard/components/AriaComponents/types.ts @@ -16,7 +16,7 @@ export type IconProp = | LegacyIconProp /** The possible return values for a legacy icon. */ -export type LegacyAvialableIconReturn = +export type LegacyAvailableIconReturn = | LegacyIcon | ReactElement | false @@ -31,8 +31,8 @@ export type AvailableIconReturn = ReactElement | SvgUseIcon | false | null | und * @deprecated Prefer defined keys over importing from `#/assets/*.svg`. */ export type LegacyIconProp = - | LegacyAvialableIconReturn - | ((render: Render) => LegacyAvialableIconReturn) + | LegacyAvailableIconReturn + | ((render: Render) => LegacyAvailableIconReturn) /** Generic type for imported from figma icons. */ export type IconPropSvgUse = AvailableIconReturn | ((render: Render) => AvailableIconReturn) diff --git a/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx b/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx index ef153cb6493d..98727899cce9 100644 --- a/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx +++ b/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx @@ -20,8 +20,14 @@ import { } from 'react-aria' import type * as aria from 'react-aria-components' import invariant from 'tiny-invariant' -import { Button, Text, type Addon, type IconProp, type TestIdProps } from '../AriaComponents' -import { Icon as IconComponent } from '../Icon' +import { + Button, + IconDisplay, + Text, + type Addon, + type IconProp, + type TestIdProps, +} from '../AriaComponents' export const BREADCRUMB_ITEM_STYLES = tv({ base: 'flex items-center gap-2 bg-transparent transition-colors', @@ -29,7 +35,6 @@ export const BREADCRUMB_ITEM_STYLES = tv({ link: 'block max-w-48 min-w-4 w-auto', more: 'aspect-square', container: 'flex items-center gap-2', - icon: '-mb-0.5', }, variants: { isCurrent: { @@ -192,22 +197,17 @@ export function BreadcrumbItem(props: BreadcrumbItemPro const container = isCurrent ? - - - - {icon} - - {typeof children === 'function' ? children(renderProps) : children} - - + {children} + : + {maxDurationDescription} + )} - - + {timeZoneDescription} + )}
From cab3917b279145e6e8f845a92b471fc46ba5a4fd Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Wed, 5 Mar 2025 20:11:20 +1000 Subject: [PATCH 25/27] [dashboard/IconDisplay] Fix tooltips --- .../IconDisplay/IconDisplay.tsx | 37 +++++++++++++------ .../components/AriaComponents/Text/Text.tsx | 23 ++++++------ .../WithVisualTooltip/WithVisualTooltip.tsx | 5 ++- .../components/Breadcrumbs/BreadcrumbItem.tsx | 6 ++- .../components/ProjectExecution.tsx | 3 ++ 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx index bac1894fc03f..3d7b44d9630f 100644 --- a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx @@ -1,14 +1,15 @@ /** @file A text display with an icon. */ import { Icon } from '#/components/Icon' import { tv, type VariantProps } from '#/utilities/tailwindVariants' -import type { ReactNode } from 'react' -import { Text, type IconProp, type TextProps } from '..' +import { Text, WithVisualTooltip, type IconProp, type TextProps, type TooltipElementType } from '..' const ICON_DISPLAY_STYLES = tv({ - base: 'block max-w-48 min-w-4 w-auto', + base: 'flex items-center gap-2 max-w-48 min-w-4 px-1', slots: { - container: 'flex items-center gap-2', icon: '-mb-0.5', + // For some reason `min-w-0` is required for the ellipsis to appear. + container: 'flex mx-auto min-w-0', + text: 'block truncate', }, variants: { variant: { @@ -18,9 +19,12 @@ const ICON_DISPLAY_STYLES = tv({ accent: 'bg-accent text-white', ghost: 'text-primary', submit: 'bg-invite text-white opacity-80', - outline: 'border-primary/20 text-primary', + outline: 'border-0.5 rounded-full border-primary/20 text-primary', }, }, + defaultVariants: { + variant: 'custom', + }, }) /** Render props for {@link IconDisplay}. */ @@ -36,33 +40,44 @@ export interface IconDisplayProps extends Omit, IconDisplayRenderProps, VariantProps { + readonly showTooltip?: boolean readonly icon: IconProp> - readonly children: ReactNode | ((renderProps: Required) => ReactNode) + readonly children: + | TooltipElementType + | ((renderProps: Required) => TooltipElementType) } /** A text display with an icon. */ export function IconDisplay(props: IconDisplayProps) { const { + showTooltip = false, icon, isCurrent = true, isDisabled = false, children, variant, variants = ICON_DISPLAY_STYLES, + tooltip, ...textProps } = props const renderProps = { isCurrent, isDisabled } const styles = variants({ variant }) + const renderedChildren = typeof children === 'function' ? children(renderProps) : children + return ( - - +
+ {icon} - {typeof children === 'function' ? children(renderProps) : children} - - + +
+ + {renderedChildren} + +
+
) } diff --git a/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx b/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx index fc3cb196f2da..b8253f3fe021 100644 --- a/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/Text/Text.tsx @@ -6,6 +6,7 @@ import * as aria from '#/components/aria' import * as mergeRefs from '#/utilities/mergeRefs' import * as twv from '#/utilities/tailwindVariants' +import type { TooltipElementType } from '#/components/AriaComponents' import { useEventCallback } from '#/hooks/eventCallbackHooks' import { forwardRef } from '#/utilities/react' import { memo } from 'react' @@ -20,7 +21,7 @@ export interface TextProps TestIdProps { readonly elementType?: keyof HTMLElementTagNameMap readonly lineClamp?: number - readonly tooltip?: React.ReactElement | string | false | null + readonly tooltip?: TooltipElementType readonly tooltipTriggerRef?: React.RefObject readonly tooltipDisplay?: visualTooltip.VisualTooltipProps['display'] readonly tooltipPlacement?: aria.Placement @@ -86,16 +87,16 @@ export const TEXT_STYLE = twv.tv({ }, truncate: { /* eslint-disable @typescript-eslint/naming-convention */ - '1': 'block truncate ellipsis', - '2': 'line-clamp-2 ellipsis', - '3': 'line-clamp-3 ellipsis', - '4': 'line-clamp-4 ellipsis', - '5': 'line-clamp-5 ellipsis', - '6': 'line-clamp-6 ellipsis', - '7': 'line-clamp-7 ellipsis', - '8': 'line-clamp-8 ellipsis', - '9': 'line-clamp-9 ellipsis', - custom: 'line-clamp-[var(--line-clamp)] ellipsis', + '1': 'block truncate', + '2': 'line-clamp-2 text-ellipsis', + '3': 'line-clamp-3 text-ellipsis', + '4': 'line-clamp-4 text-ellipsis', + '5': 'line-clamp-5 text-ellipsis', + '6': 'line-clamp-6 text-ellipsis', + '7': 'line-clamp-7 text-ellipsis', + '8': 'line-clamp-8 text-ellipsis', + '9': 'line-clamp-9 text-ellipsis', + custom: 'line-clamp-[var(--line-clamp)] text-ellipsis', /* eslint-enable @typescript-eslint/naming-convention */ }, monospace: { true: 'font-mono' }, diff --git a/app/gui/src/dashboard/components/AriaComponents/WithVisualTooltip/WithVisualTooltip.tsx b/app/gui/src/dashboard/components/AriaComponents/WithVisualTooltip/WithVisualTooltip.tsx index 024857734aac..d1c4b159d33c 100644 --- a/app/gui/src/dashboard/components/AriaComponents/WithVisualTooltip/WithVisualTooltip.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/WithVisualTooltip/WithVisualTooltip.tsx @@ -4,10 +4,13 @@ import { useRef, type PropsWithChildren, type ReactElement } from 'react' import type { Placement } from '#/components/aria' import { useVisualTooltip } from '#/components/AriaComponents/Text' +/** Valid types of a tooltip. */ +export type TooltipElementType = ReactElement | string | false | null | undefined + /** Props for a {@link WithVisualTooltip}. */ export interface WithVisualTooltipProps extends Readonly { /** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */ - readonly tooltip?: ReactElement | string | false | null | undefined + readonly tooltip?: TooltipElementType readonly tooltipPlacement?: Placement | undefined readonly className?: string | undefined } diff --git a/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx b/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx index 98727899cce9..47867115de37 100644 --- a/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx +++ b/app/gui/src/dashboard/components/Breadcrumbs/BreadcrumbItem.tsx @@ -10,7 +10,6 @@ import { type CSSProperties, type Key, type PropsWithChildren, - type ReactNode, } from 'react' import { useBreadcrumbItem, @@ -27,6 +26,7 @@ import { type Addon, type IconProp, type TestIdProps, + type TooltipElementType, } from '../AriaComponents' export const BREADCRUMB_ITEM_STYLES = tv({ @@ -71,7 +71,9 @@ export interface BreadcrumbItemProps readonly isDisabled?: boolean readonly className?: string | ((renderProps: BreadcrumbItemRenderProps) => string) readonly style?: CSSProperties | ((renderProps: BreadcrumbItemRenderProps) => CSSProperties) - readonly children: ReactNode | ((renderProps: BreadcrumbItemRenderProps) => ReactNode) + readonly children: + | TooltipElementType + | ((renderProps: BreadcrumbItemRenderProps) => TooltipElementType) readonly isLoading?: boolean } diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx index 62fc7f57181f..070eceebf103 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx @@ -230,6 +230,7 @@ export function ProjectExecution(props: ProjectExecutionProps) { {enableAdvancedProjectExecutionOptions && ( )} Date: Wed, 5 Mar 2025 20:18:20 +1000 Subject: [PATCH 26/27] [dashboard/IconDisplay] Add stories --- .../IconDisplay/IconDisplay.stories.tsx | 30 +++++++++++++++++++ .../IconDisplay/IconDisplay.tsx | 4 +-- .../components/AriaComponents/index.ts | 5 +--- .../components/ProjectExecution.tsx | 3 -- 4 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx new file mode 100644 index 000000000000..ad945862b307 --- /dev/null +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { IconDisplay, type IconDisplayProps } from './IconDisplay' + +type Props = IconDisplayProps +type Story = StoryObj + +export default { + title: 'Components/IconDisplay', + component: IconDisplay, + render: (args) => , + tags: ['autodocs'], + args: {}, + parameters: { + layout: 'centered', + }, +} as Meta + +export const Default: Story = { + args: { + icon: 'time', + children: 'aaaaaaaa', + } satisfies Props, +} + +export const Overflowing: Story = { + args: { + icon: 'sort', + children: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + } satisfies Props, +} diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx index 3d7b44d9630f..88d0efe0ce08 100644 --- a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.tsx @@ -40,7 +40,6 @@ export interface IconDisplayProps extends Omit, IconDisplayRenderProps, VariantProps { - readonly showTooltip?: boolean readonly icon: IconProp> readonly children: | TooltipElementType @@ -50,7 +49,6 @@ export interface IconDisplayProps /** A text display with an icon. */ export function IconDisplay(props: IconDisplayProps) { const { - showTooltip = false, icon, isCurrent = true, isDisabled = false, @@ -68,7 +66,7 @@ export function IconDisplay(props: IconDisplayProps - + {icon} diff --git a/app/gui/src/dashboard/components/AriaComponents/index.ts b/app/gui/src/dashboard/components/AriaComponents/index.ts index eac657711dc6..f630f4aca3d6 100644 --- a/app/gui/src/dashboard/components/AriaComponents/index.ts +++ b/app/gui/src/dashboard/components/AriaComponents/index.ts @@ -1,7 +1,4 @@ -/** - * @file index.ts - * Index file for Aria Components - */ +/** @file Index file for Aria components. */ export * from './Alert' export * from './Button' export * from './Checkbox' diff --git a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx index 070eceebf103..62fc7f57181f 100644 --- a/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx +++ b/app/gui/src/dashboard/layouts/AssetPanel/components/ProjectExecution.tsx @@ -230,7 +230,6 @@ export function ProjectExecution(props: ProjectExecutionProps) { {enableAdvancedProjectExecutionOptions && ( )} Date: Wed, 5 Mar 2025 20:25:28 +1000 Subject: [PATCH 27/27] [dashboard/IconDisplay] Fix stories --- .../IconDisplay/IconDisplay.stories.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx index ad945862b307..5c44abdb9d65 100644 --- a/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx +++ b/app/gui/src/dashboard/components/AriaComponents/IconDisplay/IconDisplay.stories.tsx @@ -9,22 +9,19 @@ export default { component: IconDisplay, render: (args) => , tags: ['autodocs'], - args: {}, + args: { icon: 'time', children: 'aaaaaaaa' } satisfies Props, + // `text-primary` is required to make icons show up. + decorators: [(Story, context) =>
{Story(context)}
], parameters: { layout: 'centered', }, } as Meta -export const Default: Story = { - args: { - icon: 'time', - children: 'aaaaaaaa', - } satisfies Props, -} +export const Default: Story = {} export const Overflowing: Story = { args: { icon: 'sort', - children: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + children: 'very long example label that overflows the max width', } satisfies Props, }