mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): remember open/closed state of accordions/expanders
This commit is contained in:
parent
504bdac14a
commit
e7e7793896
@ -1,23 +1,25 @@
|
||||
import { useDisclosure } from '@invoke-ai/ui';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { expanderToggled } from 'features/settingsAccordions/store/actions';
|
||||
import { useCallback } from 'react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
expanderStateChanged,
|
||||
selectUiSlice,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
type UseExpanderToggleArg = {
|
||||
defaultIsOpen: boolean;
|
||||
id?: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const useExpanderToggle = (arg: UseExpanderToggleArg) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isOpen, onToggle: _onToggle } = useDisclosure({
|
||||
defaultIsOpen: arg.defaultIsOpen,
|
||||
});
|
||||
const selectIsOpen = useMemo(
|
||||
() => createSelector(selectUiSlice, (ui) => ui.expanders[arg.id] ?? arg.defaultIsOpen),
|
||||
[arg]
|
||||
);
|
||||
const isOpen = useAppSelector(selectIsOpen);
|
||||
const onToggle = useCallback(() => {
|
||||
if (arg.id) {
|
||||
dispatch(expanderToggled({ id: arg.id, isOpen }));
|
||||
}
|
||||
_onToggle();
|
||||
}, [_onToggle, dispatch, arg.id, isOpen]);
|
||||
dispatch(expanderStateChanged({ id: arg.id, isOpen: !isOpen }));
|
||||
}, [dispatch, arg.id, isOpen]);
|
||||
return { isOpen, onToggle };
|
||||
};
|
||||
|
@ -1,25 +1,31 @@
|
||||
import { useDisclosure } from '@invoke-ai/ui';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { standaloneAccordionToggled } from 'features/settingsAccordions/store/actions';
|
||||
import { useCallback } from 'react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
accordionStateChanged,
|
||||
selectUiSlice,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
type UseStandaloneAccordionToggleArg = {
|
||||
defaultIsOpen: boolean;
|
||||
id?: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const useStandaloneAccordionToggle = (
|
||||
arg: UseStandaloneAccordionToggleArg
|
||||
) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isOpen, onToggle: _onToggle } = useDisclosure({
|
||||
defaultIsOpen: arg.defaultIsOpen,
|
||||
});
|
||||
const selectIsOpen = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
selectUiSlice,
|
||||
(ui) => ui.accordions[arg.id] ?? arg.defaultIsOpen
|
||||
),
|
||||
[arg]
|
||||
);
|
||||
const isOpen = useAppSelector(selectIsOpen);
|
||||
const onToggle = useCallback(() => {
|
||||
if (arg.id) {
|
||||
dispatch(standaloneAccordionToggled({ id: arg.id, isOpen }));
|
||||
}
|
||||
_onToggle();
|
||||
}, [_onToggle, arg.id, dispatch, isOpen]);
|
||||
dispatch(accordionStateChanged({ id: arg.id, isOpen: !isOpen }));
|
||||
}, [arg.id, dispatch, isOpen]);
|
||||
return { isOpen, onToggle };
|
||||
};
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
export const expanderToggled = createAction<{ id: string; isOpen: boolean }>(
|
||||
'parameters/expanderToggled'
|
||||
);
|
||||
|
||||
export const standaloneAccordionToggled = createAction<{
|
||||
id: string;
|
||||
isOpen: boolean;
|
||||
}>('parameters/standaloneAccordionToggled');
|
@ -13,6 +13,8 @@ export const initialUIState: UIState = {
|
||||
shouldHidePreview: false,
|
||||
shouldShowProgressInViewer: true,
|
||||
panels: {},
|
||||
accordions: {},
|
||||
expanders: {},
|
||||
};
|
||||
|
||||
export const uiSlice = createSlice({
|
||||
@ -37,6 +39,20 @@ export const uiSlice = createSlice({
|
||||
) => {
|
||||
state.panels[action.payload.name] = action.payload.value;
|
||||
},
|
||||
accordionStateChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; isOpen: boolean }>
|
||||
) => {
|
||||
const { id, isOpen } = action.payload;
|
||||
state.accordions[id] = isOpen;
|
||||
},
|
||||
expanderStateChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; isOpen: boolean }>
|
||||
) => {
|
||||
const { id, isOpen } = action.payload;
|
||||
state.expanders[id] = isOpen;
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(initialImageChanged, (state) => {
|
||||
@ -51,6 +67,8 @@ export const {
|
||||
setShouldHidePreview,
|
||||
setShouldShowProgressInViewer,
|
||||
panelsChanged,
|
||||
accordionStateChanged,
|
||||
expanderStateChanged,
|
||||
} = uiSlice.actions;
|
||||
|
||||
export default uiSlice.reducer;
|
||||
|
@ -1,10 +1,36 @@
|
||||
import type { InvokeTabName } from './tabMap';
|
||||
|
||||
export interface UIState {
|
||||
/**
|
||||
* Slice schema version.
|
||||
*/
|
||||
_version: 1;
|
||||
/**
|
||||
* The currently active tab.
|
||||
*/
|
||||
activeTab: InvokeTabName;
|
||||
/**
|
||||
* Whether or not to show image details, e.g. metadata, workflow, etc.
|
||||
*/
|
||||
shouldShowImageDetails: boolean;
|
||||
/**
|
||||
* Whether or not to hide the preview.
|
||||
*/
|
||||
shouldHidePreview: boolean;
|
||||
/**
|
||||
* Whether or not to show progress in the viewer.
|
||||
*/
|
||||
shouldShowProgressInViewer: boolean;
|
||||
/**
|
||||
* The react-resizable-panels state. The shape is managed by react-resizable-panels.
|
||||
*/
|
||||
panels: Record<string, string>;
|
||||
/**
|
||||
* The state of accordions. The key is the id of the accordion, and the value is a boolean representing the open state.
|
||||
*/
|
||||
accordions: Record<string, boolean>;
|
||||
/**
|
||||
* The state of expanders. The key is the id of the expander, and the value is a boolean representing the open state.
|
||||
*/
|
||||
expanders: Record<string, boolean>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user