Add collapsible sections to style preset list with state management

Co-authored-by: kent <kent@invoke.ai>
This commit is contained in:
Cursor Agent
2025-07-10 03:50:26 +00:00
parent 0d67ee6548
commit e2ee05ebf0
4 changed files with 58 additions and 10 deletions

View File

@ -55,9 +55,7 @@ export const ModelInstallQueue = memo(() => {
<Box layerStyle="first" p={3} borderRadius="base" w="full" h="full">
<ScrollableContent>
<Flex flexDir="column-reverse" gap="2" w="full">
{data?.map((model) => (
<ModelInstallQueueItem key={model.id} installJob={model} />
))}
{data?.map((model) => <ModelInstallQueueItem key={model.id} installJob={model} />)}
</Flex>
</ScrollableContent>
</Box>

View File

@ -1,8 +1,13 @@
import { Button, Collapse, Flex, Icon, Text, useDisclosure } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { Button, Collapse, Flex, Icon, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { fixTooltipCloseOnScrollStyles } from 'common/util/fixTooltipCloseOnScrollStyles';
import { selectStylePresetSearchTerm } from 'features/stylePresets/store/stylePresetSlice';
import {
collapsedSectionToggled,
selectCollapsedSections,
selectStylePresetSearchTerm,
} from 'features/stylePresets/store/stylePresetSlice';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold } from 'react-icons/pi';
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
@ -11,12 +16,37 @@ import { StylePresetListItem } from './StylePresetListItem';
export const StylePresetList = ({ title, data }: { title: string; data: StylePresetRecordWithImage[] }) => {
const { t } = useTranslation();
const { onToggle, isOpen } = useDisclosure({ defaultIsOpen: true });
const dispatch = useAppDispatch();
const searchTerm = useAppSelector(selectStylePresetSearchTerm);
const collapsedSections = useAppSelector(selectCollapsedSections);
// Determine which section this is based on the title
const getSectionKey = useCallback(
(title: string) => {
if (title === t('stylePresets.myTemplates')) {
return 'myTemplates';
}
if (title === t('stylePresets.sharedTemplates')) {
return 'sharedTemplates';
}
if (title === t('stylePresets.defaultTemplates')) {
return 'defaultTemplates';
}
return 'myTemplates'; // fallback
},
[t]
);
const sectionKey = getSectionKey(title);
const isOpen = !collapsedSections[sectionKey];
const handleToggle = useCallback(() => {
dispatch(collapsedSectionToggled(sectionKey));
}, [dispatch, sectionKey]);
return (
<Flex flexDir="column">
<Button variant="unstyled" onClick={onToggle}>
<Button variant="unstyled" onClick={handleToggle}>
<Flex gap={2} alignItems="center">
<Icon boxSize={4} as={PiCaretDownBold} transform={isOpen ? undefined : 'rotate(-90deg)'} fill="base.500" />
<Text fontSize="sm" fontWeight="semibold" userSelect="none" color="base.500">

View File

@ -13,6 +13,11 @@ const initialState: StylePresetState = {
searchTerm: '',
viewMode: false,
showPromptPreviews: false,
collapsedSections: {
myTemplates: false,
sharedTemplates: false,
defaultTemplates: false,
},
};
export const stylePresetSlice = createSlice({
@ -31,6 +36,10 @@ export const stylePresetSlice = createSlice({
showPromptPreviewsChanged: (state, action: PayloadAction<boolean>) => {
state.showPromptPreviews = action.payload;
},
collapsedSectionToggled: (state, action: PayloadAction<keyof StylePresetState['collapsedSections']>) => {
const section = action.payload;
state.collapsedSections[section] = !state.collapsedSections[section];
},
},
extraReducers(builder) {
builder.addCase(paramsReset, () => {
@ -57,8 +66,13 @@ export const stylePresetSlice = createSlice({
},
});
export const { activeStylePresetIdChanged, searchTermChanged, viewModeChanged, showPromptPreviewsChanged } =
stylePresetSlice.actions;
export const {
activeStylePresetIdChanged,
searchTermChanged,
viewModeChanged,
showPromptPreviewsChanged,
collapsedSectionToggled,
} = stylePresetSlice.actions;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateStylePresetState = (state: any): any => {
@ -85,6 +99,7 @@ export const selectStylePresetActivePresetId = createStylePresetSelector(
export const selectStylePresetViewMode = createStylePresetSelector((stylePreset) => stylePreset.viewMode);
export const selectStylePresetSearchTerm = createStylePresetSelector((stylePreset) => stylePreset.searchTerm);
export const selectShowPromptPreviews = createStylePresetSelector((stylePreset) => stylePreset.showPromptPreviews);
export const selectCollapsedSections = createStylePresetSelector((stylePreset) => stylePreset.collapsedSections);
/**
* Tracks whether or not the style preset menu is open.

View File

@ -3,4 +3,9 @@ export type StylePresetState = {
searchTerm: string;
viewMode: boolean;
showPromptPreviews: boolean;
collapsedSections: {
myTemplates: boolean;
sharedTemplates: boolean;
defaultTemplates: boolean;
};
};