mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
perf(ui): split out gallery settings popover components
This was taking over 15ms (!) to render each time a setting changed, wtf
This commit is contained in:
parent
2b744480d6
commit
1468f4d37e
@ -1,172 +0,0 @@
|
||||
import type { ComboboxOption, FormLabelProps } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
Checkbox,
|
||||
Combobox,
|
||||
CompositeSlider,
|
||||
Divider,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormControlGroup,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Popover,
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Switch,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import type { SingleValue } from 'chakra-react-select';
|
||||
import {
|
||||
alwaysShowImageSizeBadgeChanged,
|
||||
autoAssignBoardOnClickChanged,
|
||||
orderDirChanged,
|
||||
setGalleryImageMinimumWidth,
|
||||
shouldAutoSwitchChanged,
|
||||
shouldShowArchivedBoardsChanged,
|
||||
starredFirstChanged,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiSettings4Fill } from 'react-icons/ri';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import BoardAutoAddSelect from './Boards/BoardAutoAddSelect';
|
||||
|
||||
const formLabelProps: FormLabelProps = {
|
||||
flexGrow: 1,
|
||||
};
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const galleryImageMinimumWidth = useAppSelector((s) => s.gallery.galleryImageMinimumWidth);
|
||||
const shouldAutoSwitch = useAppSelector((s) => s.gallery.shouldAutoSwitch);
|
||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||
const alwaysShowImageSizeBadge = useAppSelector((s) => s.gallery.alwaysShowImageSizeBadge);
|
||||
const shouldShowArchivedBoards = useAppSelector((s) => s.gallery.shouldShowArchivedBoards);
|
||||
const orderDir = useAppSelector((s) => s.gallery.orderDir);
|
||||
const starredFirst = useAppSelector((s) => s.gallery.starredFirst);
|
||||
|
||||
const handleChangeGalleryImageMinimumWidth = useCallback(
|
||||
(v: number) => {
|
||||
dispatch(setGalleryImageMinimumWidth(v));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChangeAutoSwitch = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldAutoSwitchChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChangeAutoAssignBoardOnClick = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(autoAssignBoardOnClickChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChangeAlwaysShowImageSizeBadgeChanged = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(alwaysShowImageSizeBadgeChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChangeShouldShowArchivedBoardsChanged = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldShowArchivedBoardsChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const onChangeStarredFirst = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(starredFirstChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const orderDirOptions = useMemo<ComboboxOption[]>(
|
||||
() => [
|
||||
{ value: 'DESC', label: t('gallery.newestFirst') },
|
||||
{ value: 'ASC', label: t('gallery.oldestFirst') },
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const onChangeOrderDir = useCallback(
|
||||
(v: SingleValue<ComboboxOption>) => {
|
||||
assert(v?.value === 'ASC' || v?.value === 'DESC');
|
||||
dispatch(orderDirChanged(v.value));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const orderDirValue = useMemo(() => {
|
||||
return orderDirOptions.find((opt) => opt.value === orderDir);
|
||||
}, [orderDir, orderDirOptions]);
|
||||
|
||||
return (
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
<IconButton aria-label={t('gallery.gallerySettings')} size="sm" icon={<RiSettings4Fill />} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverBody>
|
||||
<Flex direction="column" gap={2}>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.galleryImageSize')}</FormLabel>
|
||||
<CompositeSlider
|
||||
value={galleryImageMinimumWidth}
|
||||
onChange={handleChangeGalleryImageMinimumWidth}
|
||||
min={45}
|
||||
max={256}
|
||||
defaultValue={90}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControlGroup formLabelProps={formLabelProps}>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.autoSwitchNewImages')}</FormLabel>
|
||||
<Checkbox isChecked={shouldAutoSwitch} onChange={handleChangeAutoSwitch} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.autoAssignBoardOnClick')}</FormLabel>
|
||||
<Checkbox isChecked={autoAssignBoardOnClick} onChange={handleChangeAutoAssignBoardOnClick} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.alwaysShowImageSizeBadge')}</FormLabel>
|
||||
<Checkbox isChecked={alwaysShowImageSizeBadge} onChange={handleChangeAlwaysShowImageSizeBadgeChanged} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.showArchivedBoards')}</FormLabel>
|
||||
<Checkbox isChecked={shouldShowArchivedBoards} onChange={handleChangeShouldShowArchivedBoardsChanged} />
|
||||
</FormControl>
|
||||
</FormControlGroup>
|
||||
<BoardAutoAddSelect />
|
||||
<Divider />
|
||||
<FormControl w="full">
|
||||
<FormLabel flexGrow={1} m={0}>
|
||||
{t('gallery.showStarredImagesFirst')}
|
||||
</FormLabel>
|
||||
<Switch size="sm" isChecked={starredFirst} onChange={onChangeStarredFirst} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1} m={0}>
|
||||
{t('gallery.sortDirection')}
|
||||
</FormLabel>
|
||||
<Combobox
|
||||
isSearchable={false}
|
||||
value={orderDirValue}
|
||||
options={orderDirOptions}
|
||||
onChange={onChangeOrderDir}
|
||||
/>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,26 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { alwaysShowImageSizeBadgeChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const alwaysShowImageSizeBadge = useAppSelector((s) => s.gallery.alwaysShowImageSizeBadge);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(alwaysShowImageSizeBadgeChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1}>{t('gallery.alwaysShowImageSizeBadge')}</FormLabel>
|
||||
<Checkbox isChecked={alwaysShowImageSizeBadge} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,26 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { autoAssignBoardOnClickChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(autoAssignBoardOnClickChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1}>{t('gallery.autoAssignBoardOnClick')}</FormLabel>
|
||||
<Checkbox isChecked={autoAssignBoardOnClick} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,28 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { shouldAutoSwitchChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const shouldAutoSwitch = useAppSelector((s) => s.gallery.shouldAutoSwitch);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldAutoSwitchChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1}>{t('gallery.autoSwitchNewImages')}</FormLabel>
|
||||
<Checkbox isChecked={shouldAutoSwitch} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,41 @@
|
||||
import { Divider, Flex, IconButton, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
||||
import BoardAutoAddSelect from 'features/gallery/components/Boards/BoardAutoAddSelect';
|
||||
import AlwaysShowImageSizeCheckbox from 'features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox';
|
||||
import AutoAssignBoardCheckbox from 'features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox';
|
||||
import AutoSwitchCheckbox from 'features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox';
|
||||
import ImageMinimumWidthSlider from 'features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider';
|
||||
import ShowArchivedBoardsCheckbox from 'features/gallery/components/GallerySettingsPopover/ShowArchivedBoardsCheckbox';
|
||||
import ShowStarredFirstCheckbox from 'features/gallery/components/GallerySettingsPopover/ShowStarredFirstCheckbox';
|
||||
import SortDirectionCombobox from 'features/gallery/components/GallerySettingsPopover/SortDirectionCombobox';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiSettings4Fill } from 'react-icons/ri';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
<IconButton aria-label={t('gallery.gallerySettings')} size="sm" icon={<RiSettings4Fill />} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverBody>
|
||||
<Flex direction="column" gap={2}>
|
||||
<ImageMinimumWidthSlider />
|
||||
<AutoSwitchCheckbox />
|
||||
<AutoAssignBoardCheckbox />
|
||||
<AlwaysShowImageSizeCheckbox />
|
||||
<ShowArchivedBoardsCheckbox />
|
||||
<BoardAutoAddSelect />
|
||||
<Divider />
|
||||
<ShowStarredFirstCheckbox />
|
||||
<SortDirectionCombobox />
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,26 @@
|
||||
import { CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { setGalleryImageMinimumWidth } from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const galleryImageMinimumWidth = useAppSelector((s) => s.gallery.galleryImageMinimumWidth);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
dispatch(setGalleryImageMinimumWidth(v));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.galleryImageSize')}</FormLabel>
|
||||
<CompositeSlider value={galleryImageMinimumWidth} onChange={onChange} min={45} max={256} defaultValue={90} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,28 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { shouldShowArchivedBoardsChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const shouldShowArchivedBoards = useAppSelector((s) => s.gallery.shouldShowArchivedBoards);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldShowArchivedBoardsChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1}>{t('gallery.showArchivedBoards')}</FormLabel>
|
||||
<Checkbox isChecked={shouldShowArchivedBoards} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,30 @@
|
||||
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { starredFirstChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const starredFirst = useAppSelector((s) => s.gallery.starredFirst);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(starredFirstChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl w="full">
|
||||
<FormLabel flexGrow={1} m={0}>
|
||||
{t('gallery.showStarredImagesFirst')}
|
||||
</FormLabel>
|
||||
<Switch size="sm" isChecked={starredFirst} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -0,0 +1,45 @@
|
||||
import type { ComboboxOption } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import type { SingleValue } from 'chakra-react-select';
|
||||
import { orderDirChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const GallerySettingsPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const orderDir = useAppSelector((s) => s.gallery.orderDir);
|
||||
|
||||
const options = useMemo<ComboboxOption[]>(
|
||||
() => [
|
||||
{ value: 'DESC', label: t('gallery.newestFirst') },
|
||||
{ value: 'ASC', label: t('gallery.oldestFirst') },
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: SingleValue<ComboboxOption>) => {
|
||||
assert(v?.value === 'ASC' || v?.value === 'DESC');
|
||||
dispatch(orderDirChanged(v.value));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const value = useMemo(() => {
|
||||
return options.find((opt) => opt.value === orderDir);
|
||||
}, [orderDir, options]);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel flexGrow={1} m={0}>
|
||||
{t('gallery.sortDirection')}
|
||||
</FormLabel>
|
||||
<Combobox isSearchable={false} value={value} options={options} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GallerySettingsPopover);
|
@ -10,7 +10,7 @@ import { RiServerLine } from 'react-icons/ri';
|
||||
|
||||
import BoardsList from './Boards/BoardsList/BoardsList';
|
||||
import GalleryBoardName from './GalleryBoardName';
|
||||
import GallerySettingsPopover from './GallerySettingsPopover';
|
||||
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
|
||||
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
|
||||
import { GalleryPagination } from './ImageGrid/GalleryPagination';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user