diff --git a/components/dashboard/package.json b/components/dashboard/package.json index 7f235dbbe17bb6..d063106f658707 100644 --- a/components/dashboard/package.json +++ b/components/dashboard/package.json @@ -4,8 +4,8 @@ "version": "0.0.0", "private": true, "dependencies": { - "@connectrpc/connect-web": "1.1.2", "@connectrpc/connect": "1.1.2", + "@connectrpc/connect-web": "1.1.2", "@gitpod/gitpod-protocol": "0.1.5", "@gitpod/public-api": "0.1.5", "@radix-ui/react-popover": "^1.0.7", @@ -26,6 +26,7 @@ "idb-keyval": "^6.2.0", "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8", + "lucide-react": "^0.287.0", "monaco-editor": "^0.25.2", "pretty-bytes": "^6.1.0", "process": "^0.11.10", diff --git a/components/dashboard/src/components/RepositoryFinder.tsx b/components/dashboard/src/components/RepositoryFinder.tsx index 5754857f1ad790..4b1901b2b0e3e2 100644 --- a/components/dashboard/src/components/RepositoryFinder.tsx +++ b/components/dashboard/src/components/RepositoryFinder.tsx @@ -5,7 +5,7 @@ */ import { FC, useCallback, useMemo, useState } from "react"; -import { DropDown2, DropDown2Element, DropDown2SelectedElement } from "./DropDown2"; +import { Combobox, ComboboxElement, ComboboxSelectedItem } from "./podkit/combobox/Combobox"; import RepositorySVG from "../icons/Repository.svg"; import { ReactComponent as RepositoryIcon } from "../icons/RepositoryWithColor.svg"; import { SuggestedRepository } from "@gitpod/gitpod-protocol"; @@ -92,14 +92,14 @@ export default function RepositoryFinder({ id: repo.projectId || repo.url, element: , isSelectable: true, - } as DropDown2Element; + } as ComboboxElement; }); }, [repos], ); return ( - - - + ); } diff --git a/components/dashboard/src/components/SelectIDEComponent.tsx b/components/dashboard/src/components/SelectIDEComponent.tsx index 72434a974634a2..06c7afcff38a70 100644 --- a/components/dashboard/src/components/SelectIDEComponent.tsx +++ b/components/dashboard/src/components/SelectIDEComponent.tsx @@ -6,7 +6,7 @@ import { IDEOption, IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol"; import { FC, useCallback, useEffect, useMemo } from "react"; -import { DropDown2, DropDown2Element, DropDown2SelectedElement } from "./DropDown2"; +import { Combobox, ComboboxElement, ComboboxSelectedItem } from "./podkit/combobox/Combobox"; import Editor from "../icons/Editor.svg"; import { useIDEOptions } from "../data/ide-options/ide-options-query"; @@ -58,7 +58,7 @@ export default function SelectIDEComponent({ if (!options) { return []; } - const result: DropDown2Element[] = []; + const result: ComboboxElement[] = []; for (const ide of options.filter((ide) => `${ide.label}${ide.title}${ide.notes}${ide.id}`.toLowerCase().includes(search.toLowerCase()), )) { @@ -98,7 +98,7 @@ export default function SelectIDEComponent({ } }, [ide, ideOptions, setError]); return ( - - + ); } @@ -141,7 +141,7 @@ const IdeOptionElementSelected: FC = ({ option, useLatest } return ( - { + const getElements = useCallback((): ComboboxElement[] => { return (workspaceClasses || [])?.map((c) => ({ id: c.id, element: , @@ -61,7 +61,7 @@ export default function SelectWorkspaceClassComponent({ return workspaceClasses.find((ws) => ws.id === (selectedWorkspaceClass || defaultClassId)); }, [selectedWorkspaceClass, workspaceClasses]); return ( - - + ); } @@ -89,7 +89,7 @@ const WorkspaceClassDropDownElementSelected: FC DropDown2Element[]; + getElements: (searchString: string) => ComboboxElement[]; disabled?: boolean; loading?: boolean; searchPlaceholder?: string; @@ -29,7 +28,7 @@ export interface DropDown2Props { onSearchChange?: (searchString: string) => void; } -export const DropDown2: FunctionComponent = ({ +export const Combobox: FunctionComponent = ({ initialValue = "", disabled = false, loading = false, @@ -188,8 +187,14 @@ export const DropDown2: FunctionComponent = ({ > {children}
-
- +
+
@@ -213,8 +218,8 @@ export const DropDown2: FunctionComponent = ({ onChange={handleInputChange} /> {showInputLoadingIndicator && ( -
- +
+
)}
@@ -229,7 +234,7 @@ export const DropDown2: FunctionComponent = ({ {!showResultsLoadingIndicator && filteredOptions.length > 0 ? ( filteredOptions.map((element) => { return ( - = ({ ); }; -type DropDown2SelectedElementProps = { +type ComboboxSelectedItemProps = { // Either a string of the icon source or an element icon: ReactNode; loading?: boolean; @@ -259,7 +264,7 @@ type DropDown2SelectedElementProps = { htmlTitle?: string; }; -export const DropDown2SelectedElement: FC = ({ +export const ComboboxSelectedItem: FC = ({ icon, loading = false, title, @@ -297,14 +302,14 @@ export const DropDown2SelectedElement: FC = ({ ); }; -type Dropdown2ElementProps = { - element: DropDown2Element; +type ComboboxItemProps = { + element: ComboboxElement; isActive: boolean; onSelected: (id: string) => void; onFocused: (id: string) => void; }; -export const Dropdown2Element: FC = ({ element, isActive, onSelected, onFocused }) => { +export const ComboboxItem: FC = ({ element, isActive, onSelected, onFocused }) => { let selectionClasses = `dark:bg-gray-800 cursor-pointer`; if (isActive) { selectionClasses = `bg-gray-200 dark:bg-gray-700 cursor-pointer focus:outline-none focus:ring-0`; diff --git a/components/dashboard/src/hooks/use-debounce.ts b/components/dashboard/src/hooks/use-debounce.ts index 71a13d1cffcf53..d4c7ccbbc7fcb2 100644 --- a/components/dashboard/src/hooks/use-debounce.ts +++ b/components/dashboard/src/hooks/use-debounce.ts @@ -20,7 +20,8 @@ export const useDebounce = (value: T, delay = 500, options?: DebounceOptions) return debounce(setDebouncedValue, delay, { leading: options?.leading || false, trailing: options?.trailing || true, - maxWait: options?.maxWait ?? undefined, + // ensures debounced value is updated at least every 1s + maxWait: options?.maxWait ?? 1000, }); }, [delay, options?.leading, options?.maxWait, options?.trailing]); diff --git a/components/dashboard/tailwind.config.js b/components/dashboard/tailwind.config.js index 185ef305f5b2cb..9b1a139fd816fa 100644 --- a/components/dashboard/tailwind.config.js +++ b/components/dashboard/tailwind.config.js @@ -65,6 +65,8 @@ module.exports = { animation: { "toast-in-right": "toast-in-right 0.3s ease-in-out", "fade-in": "fade-in 3s linear", + "fade-in-fast": "fade-in .3s ease-in-out", + "spin-slow": "spin 2s linear infinite", }, transitionProperty: { width: "width", diff --git a/yarn.lock b/yarn.lock index dd2a5fb1126782..b833ea79658f77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10374,6 +10374,11 @@ lru_map@^0.3.3: resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= +lucide-react@^0.287.0: + version "0.287.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.287.0.tgz#efa49872a91fa97b7ef650c4b40396b6880d0088" + integrity sha512-auxP2bTGiMoELzX+6ItTeNzLmhGd/O+PHBsrXV2YwPXYCxarIFJhiMOSzFT9a1GWeYPSZtnWdLr79IVXr/5JqQ== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz"