CommandPalette
You provide a search function, sync or async, that returns groups; the palette handles focus, arrow keys, highlighting and live announcements. Bind it to Mod+K with useCommandPaletteShortcut.
import { useState } from 'react';
import {
Button,
CommandPalette,
KeyHint,
toast,
useCommandPaletteShortcut,
} from '@andrewbehma/glass-ui';
import {
FolderKanban,
Inbox,
Plus,
Search,
Settings,
UserPlus,
} from 'lucide-react';Playground
CommandPalette.tsx
import { useState } from 'react';import {Button,CommandPalette,KeyHint,toast,useCommandPaletteShortcut,} from '@andrewbehma/glass-ui';import {FolderKanban,Inbox,Plus,Search,Settings,UserPlus,} from 'lucide-react';function Example() {const [open, setOpen] = useState(false);useCommandPaletteShortcut(() => setOpen(true));const groups = [{id: 'go',title: 'Go to',items: [{ id: 'inbox', label: 'Inbox', icon: Inbox, hint: 'G I' },{ id: 'projects', label: 'Projects', icon: FolderKanban, hint: 'G P' },{ id: 'settings', label: 'Settings', icon: Settings },],},{id: 'create',title: 'Create',items: [{ id: 'issue', label: 'New issue', icon: Plus, keywords: ['bug', 'task'] },{ id: 'invite', label: 'Invite teammate', icon: UserPlus },],},];const search = (query) =>groups.map((g) => ({...g,items: g.items.filter((i) =>[i.label, ...(i.keywords ?? [])].join(' ').toLowerCase().includes(query.toLowerCase()),),})).filter((g) => g.items.length > 0);return (<><Button onClick={() => setOpen(true)}>Open palette <KeyHint keys="Mod+K" /></Button><CommandPaletteopen={open}onOpenChange={setOpen}search={search}placeholder="Search commands"onSelect={(item) => toast(item.label)}/></>);}