Add base command palette component

This commit is contained in:
jeffvli 2023-05-18 02:10:34 -07:00 committed by Jeff
parent 547fe7be38
commit 822060b82c
4 changed files with 263 additions and 0 deletions

View file

@ -0,0 +1,99 @@
/* eslint-disable react/no-unknown-property */
import { useCallback, useState } from 'react';
import { useDisclosure } from '@mantine/hooks';
import styled from 'styled-components';
import { GoToCommands } from './go-to-commands';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { Modal } from '/@/renderer/components';
import { HomeCommands } from './home-commands';
interface CommandPaletteProps {
modalProps: typeof useDisclosure['arguments'];
}
const CustomModal = styled(Modal)`
& .mantine-Modal-header {
display: none;
}
`;
export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
const [value, setValue] = useState('');
const [query, setQuery] = useState('');
const [pages, setPages] = useState<CommandPalettePages[]>([CommandPalettePages.HOME]);
const activePage = pages[pages.length - 1];
const isHome = activePage === CommandPalettePages.HOME;
const popPage = useCallback(() => {
setPages((pages) => {
const x = [...pages];
x.splice(-1, 1);
return x;
});
}, []);
return (
<CustomModal
{...modalProps}
centered
handlers={{
...modalProps.handlers,
close: () => {
if (isHome) {
modalProps.handlers.close();
setQuery('');
} else {
popPage();
}
},
toggle: () => {
console.log('toggle');
if (isHome) {
modalProps.handlers.toggle();
setQuery('');
} else {
popPage();
}
},
}}
>
<Command
filter={(value, search) => {
if (value.includes(search)) return 1;
if (value === 'search') return 1;
return 0;
}}
label="Global Command Menu"
value={value}
onValueChange={setValue}
>
<Command.Input
autoFocus
placeholder="Enter your search..."
value={query}
onValueChange={setQuery}
/>
<Command.Separator />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
{activePage === CommandPalettePages.HOME && (
<HomeCommands
handleClose={modalProps.handlers.close}
pages={pages}
query={query}
setPages={setPages}
setQuery={setQuery}
/>
)}
{activePage === CommandPalettePages.GO_TO && (
<GoToCommands
handleClose={modalProps.handlers.close}
setPages={setPages}
/>
)}
</Command.List>
</Command>
</CustomModal>
);
};

View file

@ -0,0 +1,61 @@
import { Command as Cmdk } from 'cmdk';
import styled from 'styled-components';
export enum CommandPalettePages {
GO_TO = 'go to',
HOME = 'home',
}
export const Command = styled(Cmdk)`
[cmdk-root] {
font-family: var(--content-font-family);
background-color: var(--background-color);
}
input[cmdk-input] {
width: 100%;
height: 2rem;
margin-bottom: 1rem;
padding: 0 0.5rem;
color: var(--input-fg);
font-size: 1.1rem;
background: transparent;
border: none;
&::placeholder {
color: var(--input-placeholder-fg);
}
}
div[cmdk-group-heading] {
margin: 1rem 0;
font-size: 0.9rem;
opacity: 0.8;
}
div[cmdk-item] {
display: flex;
gap: 0.5rem;
align-items: center;
padding: 1rem 0.5rem;
color: var(--btn-subtle-fg);
background: var(--btn-subtle-bg);
border-radius: 5px;
svg {
width: 1.2rem;
height: 1.2rem;
}
&[data-selected] {
color: var(--btn-subtle-fg-hover);
background: rgba(255, 255, 255, 10%);
}
}
div[cmdk-separator] {
height: 1px;
margin: 0 0 0.5rem;
background: var(--generic-border-color);
}
`;

View file

@ -0,0 +1,41 @@
import { useCallback } from 'react';
import { useNavigate } from 'react-router';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { AppRoute } from '/@/renderer/router/routes';
interface GoToCommandsProps {
handleClose: () => void;
setPages: (pages: CommandPalettePages[]) => void;
}
export const GoToCommands = ({ setPages, handleClose }: GoToCommandsProps) => {
const navigate = useNavigate();
const goTo = useCallback(
(route: string) => {
navigate(route);
setPages([CommandPalettePages.HOME]);
handleClose();
},
[handleClose, navigate, setPages],
);
return (
<>
<Command.Item onSelect={() => goTo(AppRoute.HOME)}>Home</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.SEARCH)}>Search</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.SETTINGS)}>Settings</Command.Item>
<Command.Group heading="Library">
<Command.Item onSelect={() => goTo(AppRoute.LIBRARY_ALBUMS)}>Albums</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.LIBRARY_SONGS)}>Tracks</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.LIBRARY_ALBUM_ARTISTS)}>
Album artists
</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.LIBRARY_GENRES)}>Genres</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.LIBRARY_FOLDERS)}>Folders</Command.Item>
<Command.Item onSelect={() => goTo(AppRoute.PLAYLISTS)}>Playlists</Command.Item>
</Command.Group>
<Command.Separator />
</>
);
};

View file

@ -0,0 +1,62 @@
import { openModal, closeAllModals } from '@mantine/modals';
import { Dispatch, useCallback } from 'react';
import { useNavigate } from 'react-router';
import { CreatePlaylistForm } from '/@/renderer/features/playlists';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store';
import { ServerType } from '/@/renderer/types';
interface HomeCommandsProps {
handleClose: () => void;
pages: CommandPalettePages[];
query: string;
setPages: Dispatch<CommandPalettePages[]>;
setQuery: Dispatch<string>;
}
export const HomeCommands = ({
query,
setQuery,
pages,
setPages,
handleClose,
}: HomeCommandsProps) => {
const navigate = useNavigate();
const server = useCurrentServer();
const handleCreatePlaylistModal = useCallback(() => {
handleClose();
openModal({
children: <CreatePlaylistForm onCancel={() => closeAllModals()} />,
size: server?.type === ServerType?.NAVIDROME ? 'lg' : 'sm',
title: 'Create Playlist',
});
}, [handleClose, server?.type]);
const handleSearch = useCallback(() => {
navigate(AppRoute.SEARCH);
setQuery('');
handleClose();
}, [handleClose, navigate, setQuery]);
return (
<>
<Command.Group heading="Commands">
<Command.Item onSelect={handleCreatePlaylistModal}>Create playlist...</Command.Item>
<Command.Item onSelect={() => setPages([...pages, CommandPalettePages.GO_TO])}>
Go to page...
</Command.Item>
{query !== '' && (
<Command.Item
value="Search"
onSelect={handleSearch}
>
{query ? `Search for "${query}"...` : 'Search...'}
</Command.Item>
)}
</Command.Group>
</>
);
};