Skip to content

Commit e7e7793

Browse files
psychedelicioushipsterusername
authored andcommitted
feat(ui): remember open/closed state of accordions/expanders
1 parent 504bdac commit e7e7793

File tree

5 files changed

+78
-36
lines changed

5 files changed

+78
-36
lines changed
Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import { useDisclosure } from '@invoke-ai/ui';
2-
import { useAppDispatch } from 'app/store/storeHooks';
3-
import { expanderToggled } from 'features/settingsAccordions/store/actions';
4-
import { useCallback } from 'react';
1+
import { createSelector } from '@reduxjs/toolkit';
2+
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
3+
import {
4+
expanderStateChanged,
5+
selectUiSlice,
6+
} from 'features/ui/store/uiSlice';
7+
import { useCallback, useMemo } from 'react';
58

69
type UseExpanderToggleArg = {
710
defaultIsOpen: boolean;
8-
id?: string;
11+
id: string;
912
};
1013

1114
export const useExpanderToggle = (arg: UseExpanderToggleArg) => {
1215
const dispatch = useAppDispatch();
13-
const { isOpen, onToggle: _onToggle } = useDisclosure({
14-
defaultIsOpen: arg.defaultIsOpen,
15-
});
16+
const selectIsOpen = useMemo(
17+
() => createSelector(selectUiSlice, (ui) => ui.expanders[arg.id] ?? arg.defaultIsOpen),
18+
[arg]
19+
);
20+
const isOpen = useAppSelector(selectIsOpen);
1621
const onToggle = useCallback(() => {
17-
if (arg.id) {
18-
dispatch(expanderToggled({ id: arg.id, isOpen }));
19-
}
20-
_onToggle();
21-
}, [_onToggle, dispatch, arg.id, isOpen]);
22+
dispatch(expanderStateChanged({ id: arg.id, isOpen: !isOpen }));
23+
}, [dispatch, arg.id, isOpen]);
2224
return { isOpen, onToggle };
2325
};
Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
import { useDisclosure } from '@invoke-ai/ui';
2-
import { useAppDispatch } from 'app/store/storeHooks';
3-
import { standaloneAccordionToggled } from 'features/settingsAccordions/store/actions';
4-
import { useCallback } from 'react';
1+
import { createSelector } from '@reduxjs/toolkit';
2+
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
3+
import {
4+
accordionStateChanged,
5+
selectUiSlice,
6+
} from 'features/ui/store/uiSlice';
7+
import { useCallback, useMemo } from 'react';
58

69
type UseStandaloneAccordionToggleArg = {
710
defaultIsOpen: boolean;
8-
id?: string;
11+
id: string;
912
};
1013

1114
export const useStandaloneAccordionToggle = (
1215
arg: UseStandaloneAccordionToggleArg
1316
) => {
1417
const dispatch = useAppDispatch();
15-
const { isOpen, onToggle: _onToggle } = useDisclosure({
16-
defaultIsOpen: arg.defaultIsOpen,
17-
});
18+
const selectIsOpen = useMemo(
19+
() =>
20+
createSelector(
21+
selectUiSlice,
22+
(ui) => ui.accordions[arg.id] ?? arg.defaultIsOpen
23+
),
24+
[arg]
25+
);
26+
const isOpen = useAppSelector(selectIsOpen);
1827
const onToggle = useCallback(() => {
19-
if (arg.id) {
20-
dispatch(standaloneAccordionToggled({ id: arg.id, isOpen }));
21-
}
22-
_onToggle();
23-
}, [_onToggle, arg.id, dispatch, isOpen]);
28+
dispatch(accordionStateChanged({ id: arg.id, isOpen: !isOpen }));
29+
}, [arg.id, dispatch, isOpen]);
2430
return { isOpen, onToggle };
2531
};

invokeai/frontend/web/src/features/settingsAccordions/store/actions.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

invokeai/frontend/web/src/features/ui/store/uiSlice.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export const initialUIState: UIState = {
1313
shouldHidePreview: false,
1414
shouldShowProgressInViewer: true,
1515
panels: {},
16+
accordions: {},
17+
expanders: {},
1618
};
1719

1820
export const uiSlice = createSlice({
@@ -37,6 +39,20 @@ export const uiSlice = createSlice({
3739
) => {
3840
state.panels[action.payload.name] = action.payload.value;
3941
},
42+
accordionStateChanged: (
43+
state,
44+
action: PayloadAction<{ id: string; isOpen: boolean }>
45+
) => {
46+
const { id, isOpen } = action.payload;
47+
state.accordions[id] = isOpen;
48+
},
49+
expanderStateChanged: (
50+
state,
51+
action: PayloadAction<{ id: string; isOpen: boolean }>
52+
) => {
53+
const { id, isOpen } = action.payload;
54+
state.expanders[id] = isOpen;
55+
},
4056
},
4157
extraReducers(builder) {
4258
builder.addCase(initialImageChanged, (state) => {
@@ -51,6 +67,8 @@ export const {
5167
setShouldHidePreview,
5268
setShouldShowProgressInViewer,
5369
panelsChanged,
70+
accordionStateChanged,
71+
expanderStateChanged,
5472
} = uiSlice.actions;
5573

5674
export default uiSlice.reducer;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
11
import type { InvokeTabName } from './tabMap';
22

33
export interface UIState {
4+
/**
5+
* Slice schema version.
6+
*/
47
_version: 1;
8+
/**
9+
* The currently active tab.
10+
*/
511
activeTab: InvokeTabName;
12+
/**
13+
* Whether or not to show image details, e.g. metadata, workflow, etc.
14+
*/
615
shouldShowImageDetails: boolean;
16+
/**
17+
* Whether or not to hide the preview.
18+
*/
719
shouldHidePreview: boolean;
20+
/**
21+
* Whether or not to show progress in the viewer.
22+
*/
823
shouldShowProgressInViewer: boolean;
24+
/**
25+
* The react-resizable-panels state. The shape is managed by react-resizable-panels.
26+
*/
927
panels: Record<string, string>;
28+
/**
29+
* The state of accordions. The key is the id of the accordion, and the value is a boolean representing the open state.
30+
*/
31+
accordions: Record<string, boolean>;
32+
/**
33+
* The state of expanders. The key is the id of the expander, and the value is a boolean representing the open state.
34+
*/
35+
expanders: Record<string, boolean>;
1036
}

0 commit comments

Comments
 (0)