diff --git a/src/renderer/features/search/components/collapsible-command-group.tsx b/src/renderer/features/search/components/collapsible-command-group.tsx index e74bb4a99..ed1be16d3 100644 --- a/src/renderer/features/search/components/collapsible-command-group.tsx +++ b/src/renderer/features/search/components/collapsible-command-group.tsx @@ -8,19 +8,30 @@ import { Paper } from '/@/shared/components/paper/paper'; interface CollapsibleCommandGroupProps { children: ReactNode; defaultExpanded?: boolean; + expanded?: boolean; heading: string; + onToggle?: () => void; } export function CollapsibleCommandGroup({ children, defaultExpanded = true, + expanded: controlledExpanded, heading, + onToggle, }: CollapsibleCommandGroupProps) { - const [expanded, setExpanded] = useState(defaultExpanded); + const [internalExpanded, setInternalExpanded] = useState(defaultExpanded); + + const isControlled = controlledExpanded !== undefined && onToggle !== undefined; + const expanded = isControlled ? controlledExpanded : internalExpanded; const toggle = useCallback(() => { - setExpanded((prev) => !prev); - }, []); + if (isControlled) { + onToggle?.(); + } else { + setInternalExpanded((prev) => !prev); + } + }, [isControlled, onToggle]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { diff --git a/src/renderer/features/search/components/command-palette.tsx b/src/renderer/features/search/components/command-palette.tsx index a2faaf77d..d192dfc4b 100644 --- a/src/renderer/features/search/components/command-palette.tsx +++ b/src/renderer/features/search/components/command-palette.tsx @@ -12,7 +12,7 @@ import { HomeCommands } from '/@/renderer/features/search/components/home-comman import { LibraryCommandItem } from '/@/renderer/features/search/components/library-command-item'; import { ServerCommands } from '/@/renderer/features/search/components/server-commands'; import { AppRoute } from '/@/renderer/router/routes'; -import { useCurrentServer } from '/@/renderer/store'; +import { useAppStore, useCurrentServer } from '/@/renderer/store'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Breadcrumb } from '/@/shared/components/breadcrumb/breadcrumb'; import { Button } from '/@/shared/components/button/button'; @@ -31,9 +31,21 @@ interface CommandPaletteProps { modalProps: (typeof useDisclosure)['arguments']; } +const SEARCH_SECTION_IDS = { + albums: 'albums', + artists: 'artists', + tracks: 'tracks', +} as const; + export const CommandPalette = ({ modalProps }: CommandPaletteProps) => { const navigate = useNavigate(); const server = useCurrentServer(); + const searchSectionsExpanded = useAppStore( + (state) => state.commandPaletteSearchSectionsExpanded, + ); + const setSearchSectionExpanded = useAppStore( + (state) => state.actions.setCommandPaletteSearchSectionExpanded, + ); const [value, setValue] = useState(''); const [query, setQuery] = useState(''); const [debouncedQuery] = useDebouncedValue(query, 400); @@ -148,7 +160,16 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => { {t('common.noResultsFromQuery', { postProcess: 'sentenceCase' })} {showAlbumGroup && ( - + + setSearchSectionExpanded( + SEARCH_SECTION_IDS.albums, + !(searchSectionsExpanded[SEARCH_SECTION_IDS.albums] ?? true), + ) + } + > {data?.albums?.map((album) => ( { )} {showArtistGroup && ( - + + setSearchSectionExpanded( + SEARCH_SECTION_IDS.artists, + !(searchSectionsExpanded[SEARCH_SECTION_IDS.artists] ?? true), + ) + } + > {data?.albumArtists.map((artist) => ( { )} {showTrackGroup && ( - + + setSearchSectionExpanded( + SEARCH_SECTION_IDS.tracks, + !(searchSectionsExpanded[SEARCH_SECTION_IDS.tracks] ?? true), + ) + } + > {data?.songs.map((song) => ( ) => void; setArtistIdsMode: (mode: 'and' | 'or') => void; setArtistSelectMode: (mode: 'multi' | 'single') => void; + setCommandPaletteSearchSectionExpanded: (sectionId: string, expanded: boolean) => void; setGenreIdsMode: (mode: 'and' | 'or') => void; setGenreSelectMode: (mode: 'multi' | 'single') => void; setGlobalExpanded: (value: GlobalExpandedState | null) => void; @@ -45,6 +46,7 @@ export interface AppState { artistIdsMode: 'and' | 'or'; artistSelectMode: 'multi' | 'single'; commandPalette: CommandPaletteProps; + commandPaletteSearchSectionsExpanded: Record; genreIdsMode: 'and' | 'or'; genreSelectMode: 'multi' | 'single'; globalExpanded: GlobalExpandedState | null; @@ -134,6 +136,11 @@ export const useAppStore = createWithEqualityFn()( state.artistSelectMode = mode; }); }, + setCommandPaletteSearchSectionExpanded: (sectionId, expanded) => { + set((state) => { + state.commandPaletteSearchSectionsExpanded[sectionId] = expanded; + }); + }, setGenreIdsMode: (mode) => { set((state) => { state.genreIdsMode = mode; @@ -206,6 +213,7 @@ export const useAppStore = createWithEqualityFn()( }); }, }, + commandPaletteSearchSectionsExpanded: {}, genreIdsMode: 'and', genreSelectMode: 'multi', globalExpanded: null,