{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "gummy-command",
  "type": "registry:ui",
  "title": "Gummy Command",
  "description": "Filterable command surface with labelled combobox input, grouped options, keyboard focus, activation, disabled state, and shortcuts.",
  "registryDependencies": [
    "https://gummyui.dev/r/gummy-primitives-styles.json"
  ],
  "files": [
    {
      "path": "components/ui/GummyCommand.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nfunction joinClassNames(...values: Array<string | false | null | undefined>) {\n  return values.filter(Boolean).join(\" \");\n}\n\ntype CommandContextValue = {\n  search: string;\n  setSearch: (value: string) => void;\n  listId: string;\n  label: string;\n};\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null);\n\nfunction useCommand() {\n  const value = React.useContext(CommandContext);\n  if (!value) throw new Error(\"GummyCommand parts must be used inside GummyCommand.\");\n  return value;\n}\n\nexport const GummyCommand = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & { label?: string }\n>(function GummyCommand({ label = \"Command menu\", className, onKeyDown, ...props }, ref) {\n  const [search, setSearch] = React.useState(\"\");\n  const listId = React.useId().replace(/:/g, \"\");\n  return (\n    <CommandContext.Provider value={{ search, setSearch, listId, label }}>\n      <div\n        {...props}\n        ref={ref}\n        className={joinClassNames(\"gummy-command\", className)}\n        aria-label={label}\n        onKeyDown={(event) => {\n          onKeyDown?.(event);\n          if (event.defaultPrevented || ![\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(event.key)) return;\n          const items = Array.from(event.currentTarget.querySelectorAll<HTMLElement>(\"[data-command-item]:not([hidden]):not([aria-disabled='true'])\"));\n          if (!items.length) return;\n          event.preventDefault();\n          const current = items.indexOf(document.activeElement as HTMLElement);\n          const next = event.key === \"Home\"\n            ? 0\n            : event.key === \"End\"\n              ? items.length - 1\n              : event.key === \"ArrowDown\"\n                ? (current + 1 + items.length) % items.length\n                : (current - 1 + items.length) % items.length;\n          items[next]?.focus();\n        }}\n      />\n    </CommandContext.Provider>\n  );\n});\n\nGummyCommand.displayName = \"GummyCommand\";\n\nexport const GummyCommandInput = React.forwardRef<\n  HTMLInputElement,\n  React.InputHTMLAttributes<HTMLInputElement>\n>(function GummyCommandInput({ className, value, onChange, ...props }, ref) {\n  const command = useCommand();\n  return (\n    <input\n      {...props}\n      ref={ref}\n      className={joinClassNames(\"gummy-command__input\", className)}\n      role=\"combobox\"\n      aria-controls={command.listId}\n      aria-expanded=\"true\"\n      autoComplete=\"off\"\n      value={value ?? command.search}\n      onChange={(event) => {\n        if (value === undefined) command.setSearch(event.currentTarget.value);\n        onChange?.(event);\n      }}\n    />\n  );\n});\n\nGummyCommandInput.displayName = \"GummyCommandInput\";\n\nexport const GummyCommandList = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(function GummyCommandList({ className, ...props }, ref) {\n  const { listId, label } = useCommand();\n  return <div {...props} ref={ref} id={listId} role=\"listbox\" aria-label={props[\"aria-label\"] ?? `${label} results`} className={joinClassNames(\"gummy-command__list\", className)} />;\n});\n\nGummyCommandList.displayName = \"GummyCommandList\";\n\nexport const GummyCommandGroup = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & { label: string }\n>(function GummyCommandGroup({ label, className, children, ...props }, ref) {\n  const id = React.useId().replace(/:/g, \"\");\n  return (\n    <div {...props} ref={ref} role=\"group\" aria-labelledby={id} className={joinClassNames(\"gummy-command__group\", className)}>\n      <div id={id} className=\"gummy-command__group-label\">{label}</div>\n      {children}\n    </div>\n  );\n});\n\nGummyCommandGroup.displayName = \"GummyCommandGroup\";\n\nexport type GummyCommandItemProps = Omit<React.HTMLAttributes<HTMLDivElement>, \"onSelect\"> & {\n  value: string;\n  keywords?: string[];\n  disabled?: boolean;\n  onSelect?: (value: string) => void;\n};\n\nexport const GummyCommandItem = React.forwardRef<HTMLDivElement, GummyCommandItemProps>(\n  function GummyCommandItem(\n    { value, keywords = [], disabled = false, onSelect, className, children, ...props },\n    ref,\n  ) {\n    const { search } = useCommand();\n    const haystack = `${value} ${keywords.join(\" \")}`.toLocaleLowerCase();\n    const hidden = Boolean(search && !haystack.includes(search.toLocaleLowerCase().trim()));\n    return (\n      <div\n        {...props}\n        ref={ref}\n        role=\"option\"\n        aria-selected=\"false\"\n        tabIndex={hidden || disabled ? -1 : 0}\n        aria-disabled={disabled || undefined}\n        className={joinClassNames(\"gummy-command__item\", className)}\n        data-command-item=\"\"\n        hidden={hidden}\n        onClick={(event) => {\n          props.onClick?.(event);\n          if (!event.defaultPrevented && !disabled) onSelect?.(value);\n        }}\n        onKeyDown={(event) => {\n          props.onKeyDown?.(event);\n          if (!event.defaultPrevented && !disabled && (event.key === \"Enter\" || event.key === \" \")) {\n            event.preventDefault();\n            onSelect?.(value);\n          }\n        }}\n      >\n        {children}\n      </div>\n    );\n  },\n);\n\nGummyCommandItem.displayName = \"GummyCommandItem\";\n\nexport function GummyCommandEmpty({\n  when,\n  children = \"No results found.\",\n}: {\n  when: boolean;\n  children?: React.ReactNode;\n}) {\n  return when ? <div className=\"gummy-command__empty\">{children}</div> : null;\n}\n\nexport function GummyCommandSeparator() {\n  return <div className=\"gummy-command__separator\" role=\"separator\" />;\n}\n\nexport const GummyCommandShortcut = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(\n  function GummyCommandShortcut({ className, ...props }, ref) {\n    return <span {...props} ref={ref} className={joinClassNames(\"gummy-command__shortcut\", className)} aria-hidden=\"true\" />;\n  },\n);\n\nGummyCommandShortcut.displayName = \"GummyCommandShortcut\";\n"
    }
  ]
}
