import React, { useState, useEffect, useRef, createContext, useContext } from 'react'; import { Dialog } from '../Dialog/Dialog'; import { Stack } from '../Stack/Stack'; import { Search } from '../Search/Search'; import { Text } from '../Text/Text'; import { Kbd } from '../Kbd/Kbd'; import { useStyles, useTheme } from '../../core'; interface CommandContextType { search: string; setSearch: (search: string) => void; onSelect: () => void; } const CommandContext = createContext(null); // Main Dialog Wrapper export const CommandDialog: React.FC<{ children: React.ReactNode; isOpen: boolean; onClose: () => void; }> = ({ children, isOpen, onClose }) => { const [search, setSearch] = useState(''); useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); onClose(); } }; document.addEventListener('keydown', down); return () => document.removeEventListener('keydown', down); }, [onClose]); return ( {children} ); }; CommandDialog.displayName = "Command.Dialog"; // Search Input export const CommandInput: React.FC = () => { const context = useContext(CommandContext); if (!context) throw new Error('CommandInput must be used within CommandDialog'); return ( context.setSearch(e.target.value)} /> ); }; CommandInput.displayName = "Command.Input"; // List Wrapper export const CommandList: React.FC<{ children: React.ReactNode }> = ({ children }) => { return ( {children} ); }; CommandList.displayName = "Command.List"; // Empty State export const CommandEmpty: React.FC = () => { const context = useContext(CommandContext); const { theme } = useTheme(); if (!context?.search) return null; // Only show if there is a search query return ( No results found. ); }; CommandEmpty.displayName = "Command.Empty"; // Group export const CommandGroup: React.FC<{ heading: string, children: React.ReactNode }> = ({ heading, children }) => { const { theme } = useTheme(); return (
{heading} {children}
); }; CommandGroup.displayName = "Command.Group"; // Item export const CommandItem: React.FC<{ onSelect: () => void; children: React.ReactNode; shortcut?: string }> = ({ children, onSelect, shortcut }) => { const context = useContext(CommandContext); const createStyle = useStyles('command-item'); const itemClass = createStyle({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0.5rem', borderRadius: '4px', cursor: 'pointer', transition: 'background-color 0.2s', '&:hover': { backgroundColor: 'rgba(255, 255, 255, 0.05)', } }); const handleSelect = () => { onSelect(); context?.onSelect(); }; return (
{children} {shortcut && {shortcut}}
); }; CommandItem.displayName = "Command.Item"; interface CompoundCommand { Dialog: typeof CommandDialog; Input: typeof CommandInput; List: typeof CommandList; Empty: typeof CommandEmpty; Group: typeof CommandGroup; Item: typeof CommandItem; } export const Command: Partial = {}; Command.Dialog = CommandDialog; Command.Input = CommandInput; Command.List = CommandList; Command.Empty = CommandEmpty; Command.Group = CommandGroup; Command.Item = CommandItem;