Spaces:
Build error
Build error
'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<typeof CommandAnatomy> | |
>({}); | |
export type CommandProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive | |
> & | |
ComponentAnatomy<typeof CommandAnatomy>; | |
export const Command = React.forwardRef<HTMLDivElement, CommandProps>( | |
(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, | |
}} | |
> | |
<CommandPrimitive | |
ref={ref} | |
className={cn(CommandAnatomy.root(), className)} | |
loop={loop} | |
{...rest} | |
/> | |
</__CommandAnatomyContext.Provider> | |
); | |
} | |
); | |
Command.displayName = CommandPrimitive.displayName; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandInput | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandInputProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive.Input | |
> & | |
Pick< | |
ComponentAnatomy<typeof CommandAnatomy>, | |
'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 ( | |
<div | |
className={cn( | |
CommandAnatomy.inputContainer(), | |
_inputContainerClass, | |
inputContainerClass | |
)} | |
cmdk-input-wrapper="" | |
> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
width="24" | |
height="24" | |
viewBox="0 0 24 24" | |
fill="none" | |
stroke="currentColor" | |
strokeWidth="2" | |
strokeLinecap="round" | |
strokeLinejoin="round" | |
className={cn( | |
CommandAnatomy.inputIcon(), | |
_inputIconClass, | |
inputIconClass | |
)} | |
> | |
<circle cx="11" cy="11" r="8" /> | |
<path d="m21 21-4.3-4.3" /> | |
</svg> | |
<CommandPrimitive.Input | |
ref={ref} | |
className={cn( | |
InputAnatomy.root({ | |
intent: 'unstyled', | |
size: 'sm', | |
isDisabled: rest.disabled, | |
}), | |
className | |
)} | |
{...rest} | |
/> | |
</div> | |
); | |
}); | |
CommandInput.displayName = 'CommandInput'; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandList | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandListProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive.List | |
>; | |
export const CommandList = React.forwardRef<HTMLDivElement, CommandListProps>( | |
(props, ref) => { | |
const { className, ...rest } = props; | |
const { listClass } = React.useContext(__CommandAnatomyContext); | |
return ( | |
<CommandPrimitive.List | |
ref={ref} | |
className={cn(CommandAnatomy.list(), listClass, className)} | |
{...rest} | |
/> | |
); | |
} | |
); | |
CommandList.displayName = 'CommandList'; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandEmpty | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandEmptyProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive.Empty | |
>; | |
export const CommandEmpty = React.forwardRef<HTMLDivElement, CommandEmptyProps>( | |
(props, ref) => { | |
const { className, ...rest } = props; | |
const { emptyClass } = React.useContext(__CommandAnatomyContext); | |
return ( | |
<CommandPrimitive.Empty | |
ref={ref} | |
className={cn(CommandAnatomy.empty(), emptyClass, className)} | |
{...rest} | |
/> | |
); | |
} | |
); | |
CommandEmpty.displayName = 'CommandEmpty'; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandGroup | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandGroupProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive.Group | |
>; | |
export const CommandGroup = React.forwardRef<HTMLDivElement, CommandGroupProps>( | |
(props, ref) => { | |
const { className, ...rest } = props; | |
const { groupClass } = React.useContext(__CommandAnatomyContext); | |
return ( | |
<CommandPrimitive.Group | |
ref={ref} | |
className={cn(CommandAnatomy.group(), groupClass, className)} | |
{...rest} | |
/> | |
); | |
} | |
); | |
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 ( | |
<CommandPrimitive.Separator | |
ref={ref} | |
className={cn(CommandAnatomy.separator(), separatorClass, className)} | |
{...rest} | |
/> | |
); | |
}); | |
CommandSeparator.displayName = 'CommandSeparator'; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandItem | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandItemProps = React.ComponentPropsWithoutRef< | |
typeof CommandPrimitive.Item | |
> & | |
Pick<ComponentAnatomy<typeof CommandAnatomy>, 'itemIconContainerClass'> & { | |
leftIcon?: React.ReactNode; | |
}; | |
export const CommandItem = React.forwardRef<HTMLDivElement, CommandItemProps>( | |
(props, ref) => { | |
const { className, itemIconContainerClass, leftIcon, children, ...rest } = | |
props; | |
const { itemClass, itemIconContainerClass: _itemIconContainerClass } = | |
React.useContext(__CommandAnatomyContext); | |
const itemRef = React.useRef<HTMLDivElement | null>(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 ( | |
<CommandPrimitive.Item | |
ref={setRefs} | |
className={cn(CommandAnatomy.item(), itemClass, className)} | |
{...rest} | |
data-cmdkvalue={rest.id} | |
> | |
{leftIcon && ( | |
<span | |
className={cn( | |
CommandAnatomy.itemIconContainer(), | |
_itemIconContainerClass, | |
itemIconContainerClass | |
)} | |
> | |
{leftIcon} | |
</span> | |
)} | |
{children} | |
</CommandPrimitive.Item> | |
); | |
} | |
); | |
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 ( | |
<span | |
ref={ref} | |
className={cn(CommandAnatomy.shortcut(), shortcutClass, className)} | |
{...rest} | |
/> | |
); | |
}); | |
CommandShortcut.displayName = 'CommandShortcut'; | |
/* ------------------------------------------------------------------------------------------------- | |
* CommandDialog | |
* -----------------------------------------------------------------------------------------------*/ | |
export type CommandDialogProps = ModalProps & | |
ComponentAnatomy<typeof CommandDialogAnatomy> & { | |
commandProps: React.ComponentPropsWithoutRef<typeof CommandPrimitive>; | |
}; | |
export const CommandDialog = (props: CommandDialogProps) => { | |
const { children, commandClass, contentClass, commandProps, ...rest } = props; | |
return ( | |
<Modal | |
{...rest} | |
contentClass={cn(CommandDialogAnatomy.content(), contentClass)} | |
> | |
<Command | |
shouldFilter={false} | |
className={cn(CommandDialogAnatomy.command(), commandClass)} | |
{...commandProps} | |
> | |
{children} | |
</Command> | |
</Modal> | |
); | |
}; | |
CommandDialog.displayName = 'CommandDialog'; | |