From 79d65125c23ad991e3e10d928e6618979780e10a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 14 Jul 2023 23:00:38 +1000 Subject: [PATCH] feat(ui): extract mantine component styles to hook, add less opinionated mantine components IAIMantineSelect and IAIMantineMultiSelect have a bit of extra logic that prevents simple select functionality from working as expected. - extract the styles into hooks - rename those two components to IAIMantineSearchableSelect and IAIMantineSearchableMultiSelect - Create IAIMantineSelect (which is just a dropdown) and use it in model manager and a few other places When we only have a few options to present and searching is not efficient, we should use this instead. --- .../app/components/ThemeLocaleProvider.tsx | 3 +- .../src/common/components/IAIMantineInput.tsx | 17 +- .../components/IAIMantineMultiSelect.tsx | 113 +----------- .../components/IAIMantineSearchableSelect.tsx | 78 +++++++++ .../common/components/IAIMantineSelect.tsx | 159 +---------------- .../IAICanvasToolbar/IAICanvasToolbar.tsx | 4 +- .../parameters/ParamControlNetModel.tsx | 6 +- .../ParamControlNetProcessorSelect.tsx | 14 +- .../components/ParamEmbeddingPopover.tsx | 4 +- .../Boards/UpdateImageBoardModal.tsx | 6 +- .../lora/components/ParamLoraSelect.tsx | 4 +- .../features/nodes/components/AddNodeMenu.tsx | 4 +- .../fields/LoRAModelInputFieldComponent.tsx | 4 +- .../fields/ModelInputFieldComponent.tsx | 6 +- .../fields/VaeModelInputFieldComponent.tsx | 4 +- .../ParamScaleBeforeProcessing.tsx | 4 +- .../Parameters/Core/ParamScheduler.tsx | 4 +- .../FaceRestore/FaceRestoreType.tsx | 4 +- .../MainModel/ParamMainModelSelect.tsx | 6 +- .../Parameters/Upscale/UpscaleScale.tsx | 4 +- .../VAEModel/ParamVAEModelSelect.tsx | 4 +- .../subpanels/MergeModelsPanel.tsx | 7 +- .../ModelManagerPanel/ModelListItem.tsx | 6 +- .../UnifiedCanvas/UnifiedCanvasContent.tsx | 10 +- .../hooks/useMantineMultiSelectStyles.ts | 140 +++++++++++++++ .../hooks/useMantineSelectStyles.ts | 134 ++++++++++++++ .../frontend/web/src/mantine-theme/theme.ts | 164 ++++++++++++++++-- 27 files changed, 580 insertions(+), 333 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/IAIMantineSearchableSelect.tsx create mode 100644 invokeai/frontend/web/src/mantine-theme/hooks/useMantineMultiSelectStyles.ts create mode 100644 invokeai/frontend/web/src/mantine-theme/hooks/useMantineSelectStyles.ts diff --git a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx index 1e86e0ce1b..132667f8ab 100644 --- a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx +++ b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx @@ -9,7 +9,6 @@ import { theme as invokeAITheme } from 'theme/theme'; import '@fontsource-variable/inter'; import { MantineProvider } from '@mantine/core'; -import { mantineTheme } from 'mantine-theme/theme'; import 'overlayscrollbars/overlayscrollbars.css'; import 'theme/css/overlayscrollbars.css'; @@ -36,7 +35,7 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) { }, [direction]); return ( - + {children} diff --git a/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx b/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx index f7c2b91ff0..d60f6614df 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx @@ -7,8 +7,17 @@ type IAIMantineTextInputProps = TextInputProps; export default function IAIMantineTextInput(props: IAIMantineTextInputProps) { const { ...rest } = props; - const { base50, base100, base200, base800, base900, accent500, accent300 } = - useChakraThemeTokens(); + const { + base50, + base100, + base200, + base300, + base800, + base700, + base900, + accent500, + accent300, + } = useChakraThemeTokens(); const { colorMode } = useColorMode(); return ( @@ -24,6 +33,10 @@ export default function IAIMantineTextInput(props: IAIMantineTextInputProps) { borderColor: mode(accent300, accent500)(colorMode), }, }, + label: { + color: mode(base700, base300)(colorMode), + fontWeight: 'normal', + }, })} {...rest} /> diff --git a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx index dc6db707e7..dd5c602150 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx @@ -1,10 +1,9 @@ -import { Tooltip, useColorMode, useToken } from '@chakra-ui/react'; +import { Tooltip } from '@chakra-ui/react'; import { MultiSelect, MultiSelectProps } from '@mantine/core'; import { useAppDispatch } from 'app/store/storeHooks'; -import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice'; +import { useMantineMultiSelectStyles } from 'mantine-theme/hooks/useMantineMultiSelectStyles'; import { KeyboardEvent, RefObject, memo, useCallback } from 'react'; -import { mode } from 'theme/util/mode'; type IAIMultiSelectProps = MultiSelectProps & { tooltip?: string; @@ -14,25 +13,6 @@ type IAIMultiSelectProps = MultiSelectProps & { const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { const { searchable = true, tooltip, inputRef, ...rest } = props; const dispatch = useAppDispatch(); - const { - base50, - base100, - base200, - base300, - base400, - base500, - base600, - base700, - base800, - base900, - accent200, - accent300, - accent400, - accent500, - accent600, - } = useChakraThemeTokens(); - const [boxShadow] = useToken('shadows', ['dark-lg']); - const { colorMode } = useColorMode(); const handleKeyDown = useCallback( (e: KeyboardEvent) => { @@ -52,6 +32,8 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { [dispatch] ); + const styles = useMantineMultiSelectStyles(); + return ( { onKeyUp={handleKeyUp} searchable={searchable} maxDropdownHeight={300} - styles={() => ({ - label: { - color: mode(base700, base300)(colorMode), - fontWeight: 'normal', - }, - searchInput: { - ':placeholder': { - color: mode(base300, base700)(colorMode), - }, - }, - input: { - backgroundColor: mode(base50, base900)(colorMode), - borderWidth: '2px', - borderColor: mode(base200, base800)(colorMode), - color: mode(base900, base100)(colorMode), - paddingRight: 24, - fontWeight: 600, - '&:hover': { borderColor: mode(base300, base600)(colorMode) }, - '&:focus': { - borderColor: mode(accent300, accent600)(colorMode), - }, - '&:is(:focus, :hover)': { - borderColor: mode(base400, base500)(colorMode), - }, - '&:focus-within': { - borderColor: mode(accent200, accent600)(colorMode), - }, - '&[data-disabled]': { - backgroundColor: mode(base300, base700)(colorMode), - color: mode(base600, base400)(colorMode), - cursor: 'not-allowed', - }, - }, - value: { - backgroundColor: mode(base200, base800)(colorMode), - color: mode(base900, base100)(colorMode), - button: { - color: mode(base900, base100)(colorMode), - }, - '&:hover': { - backgroundColor: mode(base300, base700)(colorMode), - cursor: 'pointer', - }, - }, - dropdown: { - backgroundColor: mode(base200, base800)(colorMode), - borderColor: mode(base200, base800)(colorMode), - boxShadow, - }, - item: { - backgroundColor: mode(base200, base800)(colorMode), - color: mode(base800, base200)(colorMode), - padding: 6, - '&[data-hovered]': { - color: mode(base900, base100)(colorMode), - backgroundColor: mode(base300, base700)(colorMode), - }, - '&[data-active]': { - backgroundColor: mode(base300, base700)(colorMode), - '&:hover': { - color: mode(base900, base100)(colorMode), - backgroundColor: mode(base300, base700)(colorMode), - }, - }, - '&[data-selected]': { - backgroundColor: mode(accent400, accent600)(colorMode), - color: mode(base50, base100)(colorMode), - fontWeight: 600, - '&:hover': { - backgroundColor: mode(accent500, accent500)(colorMode), - color: mode('white', base50)(colorMode), - }, - }, - '&[data-disabled]': { - color: mode(base500, base600)(colorMode), - cursor: 'not-allowed', - }, - }, - rightSection: { - width: 24, - padding: 20, - button: { - color: mode(base900, base100)(colorMode), - }, - }, - })} + styles={styles} {...rest} /> diff --git a/invokeai/frontend/web/src/common/components/IAIMantineSearchableSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineSearchableSelect.tsx new file mode 100644 index 0000000000..edf1665bb4 --- /dev/null +++ b/invokeai/frontend/web/src/common/components/IAIMantineSearchableSelect.tsx @@ -0,0 +1,78 @@ +import { Tooltip } from '@chakra-ui/react'; +import { Select, SelectProps } from '@mantine/core'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice'; +import { useMantineSelectStyles } from 'mantine-theme/hooks/useMantineSelectStyles'; +import { KeyboardEvent, RefObject, memo, useCallback, useState } from 'react'; + +export type IAISelectDataType = { + value: string; + label: string; + tooltip?: string; +}; + +type IAISelectProps = SelectProps & { + tooltip?: string; + inputRef?: RefObject; +}; + +const IAIMantineSearchableSelect = (props: IAISelectProps) => { + const { searchable = true, tooltip, inputRef, onChange, ...rest } = props; + const dispatch = useAppDispatch(); + + const [searchValue, setSearchValue] = useState(''); + + // we want to capture shift keypressed even when an input is focused + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.shiftKey) { + dispatch(shiftKeyPressed(true)); + } + }, + [dispatch] + ); + + const handleKeyUp = useCallback( + (e: KeyboardEvent) => { + if (!e.shiftKey) { + dispatch(shiftKeyPressed(false)); + } + }, + [dispatch] + ); + + // wrap onChange to clear search value on select + const handleChange = useCallback( + (v: string | null) => { + setSearchValue(''); + + if (!onChange) { + return; + } + + onChange(v); + }, + [onChange] + ); + + const styles = useMantineSelectStyles(); + + return ( + + ({ - label: { - color: mode(base700, base300)(colorMode), - fontWeight: 'normal', - }, - input: { - backgroundColor: mode(base50, base900)(colorMode), - borderWidth: '2px', - borderColor: mode(base200, base800)(colorMode), - color: mode(base900, base100)(colorMode), - paddingRight: 24, - fontWeight: 600, - '&:hover': { borderColor: mode(base300, base600)(colorMode) }, - '&:focus': { - borderColor: mode(accent300, accent600)(colorMode), - }, - '&:is(:focus, :hover)': { - borderColor: mode(base400, base500)(colorMode), - }, - '&:focus-within': { - borderColor: mode(accent200, accent600)(colorMode), - }, - '&[data-disabled]': { - backgroundColor: mode(base300, base700)(colorMode), - color: mode(base600, base400)(colorMode), - cursor: 'not-allowed', - }, - }, - value: { - backgroundColor: mode(base100, base900)(colorMode), - color: mode(base900, base100)(colorMode), - button: { - color: mode(base900, base100)(colorMode), - }, - '&:hover': { - backgroundColor: mode(base300, base700)(colorMode), - cursor: 'pointer', - }, - }, - dropdown: { - backgroundColor: mode(base200, base800)(colorMode), - borderColor: mode(base200, base800)(colorMode), - boxShadow, - }, - item: { - backgroundColor: mode(base200, base800)(colorMode), - color: mode(base800, base200)(colorMode), - padding: 6, - '&[data-hovered]': { - color: mode(base900, base100)(colorMode), - backgroundColor: mode(base300, base700)(colorMode), - }, - '&[data-active]': { - backgroundColor: mode(base300, base700)(colorMode), - '&:hover': { - color: mode(base900, base100)(colorMode), - backgroundColor: mode(base300, base700)(colorMode), - }, - }, - '&[data-selected]': { - backgroundColor: mode(accent400, accent600)(colorMode), - color: mode(base50, base100)(colorMode), - fontWeight: 600, - '&:hover': { - backgroundColor: mode(accent500, accent500)(colorMode), - color: mode('white', base50)(colorMode), - }, - }, - '&[data-disabled]': { - color: mode(base500, base600)(colorMode), - cursor: 'not-allowed', - }, - }, - rightSection: { - width: 32, - button: { - color: mode(base900, base100)(colorMode), - }, - }, - })} - {...rest} - /> +