'use client'; import { cva } from 'class-variance-authority'; import { Command as CommandPrimitive } from 'cmdk'; import * as React from 'react'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; import { InputAnatomy } from '../input'; import { Modal, ModalProps } from '../modal'; /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const CommandAnatomy = defineStyleAnatomy({ root: cva([ 'UI-Command__root', 'flex h-full w-full flex-col overflow-hidden rounded-[--radius-md] bg-[--paper] text-[--foreground]', ]), inputContainer: cva([ 'UI-Command__input', 'flex items-center px-3 py-2', 'cmdk-input-wrapper', ]), inputIcon: cva(['UI-Command__inputIcon', 'mr-2 h-5 w-5 shrink-0 opacity-50']), list: cva([ 'UI-Command__list', 'max-h-[300px] overflow-y-auto overflow-x-hidden', ]), empty: cva([ 'UI-Command__empty', 'py-6 text-center text-base text-[--muted]', ]), group: cva([ 'UI-Command__group', 'overflow-hidden p-1 text-[--foreground]', '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-sm [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-[--muted]', ]), separator: cva(['UI-Command__separator', '-mx-1 h-px bg-[--border]']), item: cva([ 'UI-Command__item', 'relative flex cursor-default select-none items-center rounded-[--radius] px-2 py-1.5 text-base outline-none', 'aria-selected:bg-[--subtle] data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50', '[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', ]), itemIconContainer: cva([ 'UI-Command__itemIconContainer', 'mr-2 text-base shrink-0 w-4', ]), shortcut: cva([ 'UI-Command__shortcut', 'ml-auto text-xs tracking-widest text-[--muted]', ]), }); export const CommandDialogAnatomy = defineStyleAnatomy({ content: cva(['UI-CommandDialog__content', 'overflow-hidden p-0']), command: cva([ 'UI-CommandDialog__command', '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-[--muted]', '[&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pb-2 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5', '[&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-2 [&_[cmdk-item]_svg]:h-4 [&_[cmdk-item]_svg]:w-5', ]), }); /* ------------------------------------------------------------------------------------------------- * Command * -----------------------------------------------------------------------------------------------*/ const __CommandAnatomyContext = React.createContext< ComponentAnatomy >({}); export type CommandProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive > & ComponentAnatomy; export const Command = React.forwardRef( (props, ref) => { const { className, inputContainerClass, inputIconClass, listClass, emptyClass, groupClass, separatorClass, itemClass, itemIconContainerClass, shortcutClass, loop = true, ...rest } = props; return ( <__CommandAnatomyContext.Provider value={{ inputContainerClass, inputIconClass, listClass, emptyClass, groupClass, separatorClass, itemClass, itemIconContainerClass, shortcutClass, }} > ); } ); Command.displayName = CommandPrimitive.displayName; /* ------------------------------------------------------------------------------------------------- * CommandInput * -----------------------------------------------------------------------------------------------*/ export type CommandInputProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.Input > & Pick< ComponentAnatomy, 'inputContainerClass' | 'inputIconClass' >; export const CommandInput = React.forwardRef< HTMLInputElement, CommandInputProps >((props, ref) => { const { className, inputContainerClass, inputIconClass, ...rest } = props; const { inputContainerClass: _inputContainerClass, inputIconClass: _inputIconClass, } = React.useContext(__CommandAnatomyContext); return (
); }); CommandInput.displayName = 'CommandInput'; /* ------------------------------------------------------------------------------------------------- * CommandList * -----------------------------------------------------------------------------------------------*/ export type CommandListProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.List >; export const CommandList = React.forwardRef( (props, ref) => { const { className, ...rest } = props; const { listClass } = React.useContext(__CommandAnatomyContext); return ( ); } ); CommandList.displayName = 'CommandList'; /* ------------------------------------------------------------------------------------------------- * CommandEmpty * -----------------------------------------------------------------------------------------------*/ export type CommandEmptyProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.Empty >; export const CommandEmpty = React.forwardRef( (props, ref) => { const { className, ...rest } = props; const { emptyClass } = React.useContext(__CommandAnatomyContext); return ( ); } ); CommandEmpty.displayName = 'CommandEmpty'; /* ------------------------------------------------------------------------------------------------- * CommandGroup * -----------------------------------------------------------------------------------------------*/ export type CommandGroupProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.Group >; export const CommandGroup = React.forwardRef( (props, ref) => { const { className, ...rest } = props; const { groupClass } = React.useContext(__CommandAnatomyContext); return ( ); } ); CommandGroup.displayName = 'CommandGroup'; /* ------------------------------------------------------------------------------------------------- * CommandSeparator * -----------------------------------------------------------------------------------------------*/ export type CommandSeparatorProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.Separator >; export const CommandSeparator = React.forwardRef< HTMLDivElement, CommandSeparatorProps >((props, ref) => { const { className, ...rest } = props; const { separatorClass } = React.useContext(__CommandAnatomyContext); return ( ); }); CommandSeparator.displayName = 'CommandSeparator'; /* ------------------------------------------------------------------------------------------------- * CommandItem * -----------------------------------------------------------------------------------------------*/ export type CommandItemProps = React.ComponentPropsWithoutRef< typeof CommandPrimitive.Item > & Pick, 'itemIconContainerClass'> & { leftIcon?: React.ReactNode; }; export const CommandItem = React.forwardRef( (props, ref) => { const { className, itemIconContainerClass, leftIcon, children, ...rest } = props; const { itemClass, itemIconContainerClass: _itemIconContainerClass } = React.useContext(__CommandAnatomyContext); const itemRef = React.useRef(null); React.useEffect(() => { const element = itemRef.current; if (!element) return; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if ( mutation.attributeName === 'aria-selected' && element.getAttribute('aria-selected') === 'true' ) { element.scrollIntoView({ block: 'nearest' }); } }); }); observer.observe(element, { attributes: true }); return () => observer.disconnect(); }, []); const setRefs = React.useCallback( (node: HTMLDivElement | null) => { itemRef.current = node; if (ref) { if (typeof ref === 'function') { ref(node); } } }, [ref] ); return ( {leftIcon && ( {leftIcon} )} {children} ); } ); CommandItem.displayName = 'CommandItem'; /* ------------------------------------------------------------------------------------------------- * CommandShortcut * -----------------------------------------------------------------------------------------------*/ export type CommandShortcutProps = React.ComponentPropsWithoutRef<'span'>; export const CommandShortcut = React.forwardRef< HTMLSpanElement, CommandShortcutProps >((props, ref) => { const { className, ...rest } = props; const { shortcutClass } = React.useContext(__CommandAnatomyContext); return ( ); }); CommandShortcut.displayName = 'CommandShortcut'; /* ------------------------------------------------------------------------------------------------- * CommandDialog * -----------------------------------------------------------------------------------------------*/ export type CommandDialogProps = ModalProps & ComponentAnatomy & { commandProps: React.ComponentPropsWithoutRef; }; export const CommandDialog = (props: CommandDialogProps) => { const { children, commandClass, contentClass, commandProps, ...rest } = props; return ( {children} ); }; CommandDialog.displayName = 'CommandDialog';