Refine cmdk menu design

This commit is contained in:
Sam Becker 2025-05-31 21:45:35 -05:00
parent bcb6da9e86
commit af8ae638a1
2 changed files with 47 additions and 35 deletions

View File

@ -78,6 +78,7 @@ import { labelForFilm } from '@/film';
import IconFavs from '@/components/icons/IconFavs'; import IconFavs from '@/components/icons/IconFavs';
import IconHidden from '@/components/icons/IconHidden'; import IconHidden from '@/components/icons/IconHidden';
import { useAppText } from '@/i18n/state/client'; import { useAppText } from '@/i18n/state/client';
import LoaderButton from '@/components/primitives/LoaderButton';
const DIALOG_TITLE = 'Global Command-K Menu'; const DIALOG_TITLE = 'Global Command-K Menu';
const DIALOG_DESCRIPTION = 'For searching photos, views, and settings'; const DIALOG_DESCRIPTION = 'For searching photos, views, and settings';
@ -199,7 +200,6 @@ export default function CommandKClient({
const [queryLiveRaw, setQueryLive] = useState(''); const [queryLiveRaw, setQueryLive] = useState('');
const [queryDebouncedRaw] = const [queryDebouncedRaw] =
useDebounce(queryLiveRaw, 500, { trailing: true }); useDebounce(queryLiveRaw, 500, { trailing: true });
const isPlaceholderVisible = queryLiveRaw === '';
// Parameterized query values // Parameterized query values
const queryLive = useMemo(() => const queryLive = useMemo(() =>
@ -586,38 +586,47 @@ export default function CommandKClient({
<DialogDescription>{DIALOG_DESCRIPTION}</DialogDescription> <DialogDescription>{DIALOG_DESCRIPTION}</DialogDescription>
</VisuallyHidden.Root> </VisuallyHidden.Root>
<div className={clsx( <div className={clsx(
'px-3 md:px-3.5', 'flex items-center justify-center gap-2',
'pt-3 md:pt-3.5', 'py-1 px-4.5',
'rounded-none bg-transparent',
'border-b border-b-gray-400/25 dark:border-b-gray-800',
)}> )}>
<div className="relative"> <Command.Input
<Command.Input ref={refInput}
ref={refInput} onChangeCapture={(e) => {
onChangeCapture={(e) => { setQueryLive(e.currentTarget.value);
setQueryLive(e.currentTarget.value); updateMask();
updateMask(); }}
}} className={clsx(
className={clsx( 'grow p-0',
'w-full min-w-0!', 'focus:ring-0',
'focus:ring-0', 'border-transparent focus:border-transparent',
isPlaceholderVisible || isLoading && 'pr-10!', 'bg-transparent rounded-none',
'border-medium', 'placeholder:text-gray-400/80',
'focus:border-gray-200 dark:focus:border-gray-800', 'dark:placeholder:text-gray-700',
'placeholder:text-gray-400/80', 'focus:outline-hidden',
'dark:placeholder:text-gray-700', isPending && 'opacity-20',
'focus:outline-hidden', )}
isPending && 'opacity-20', placeholder={appText.cmdk.placeholder}
)} disabled={isPending}
placeholder={appText.cmdk.placeholder} />
disabled={isPending} {isLoading && !isPending
/> ? <span className="mr-1 translate-y-[2px]">
{isLoading && !isPending && <Spinner size={16} />
<span className={clsx( </span>
'absolute top-[9px] right-0 w-10', : <span className="max-sm:hidden">
'flex items-center justify-center translate-y-[2px]', <LoaderButton
)}> className={clsx(
<Spinner size={16} /> 'h-auto! px-1.5 py-1 -mr-1.5',
</span>} 'border-medium shadow-none',
</div> 'text-[12px]',
'text-gray-400/90 dark:text-gray-700',
)}
onClick={() => setIsOpen?.(false)}
>
ESC
</LoaderButton>
</span>}
</div> </div>
<Command.List <Command.List
ref={refScroll} ref={refScroll}
@ -690,9 +699,7 @@ export default function CommandKClient({
if (path !== pathname) { if (path !== pathname) {
setKeyWaiting(key); setKeyWaiting(key);
shouldCloseAfterWaiting.current = true; shouldCloseAfterWaiting.current = true;
startTransition(() => { startTransition(() => router.push(path));
router.push(path, { scroll: true });
});
} else { } else {
setIsOpen?.(false); setIsOpen?.(false);
} }

View File

@ -15,6 +15,11 @@
} }
} }
html {
/* Necessary for nextjs scroll restoration */
min-height: 100%;
}
@theme { @theme {
--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;