{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "select-editor",
  "title": "Select Editor",
  "description": "An editor to select tags.",
  "dependencies": [
    "fzf@0.5.2",
    "@platejs/tag",
    "@udecode/cmdk"
  ],
  "registryDependencies": [
    "https://platejs.org/r/editor.json",
    "command",
    "popover",
    "https://platejs.org/r/tag-node.json"
  ],
  "files": [
    {
      "path": "src/registry/ui/select-editor.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nimport { isEqualTags } from '@platejs/tag';\nimport {\n  MultiSelectPlugin,\n  TagPlugin,\n  useSelectableItems,\n  useSelectEditorCombobox,\n} from '@platejs/tag/react';\nimport { Command as CommandPrimitive, useCommandActions } from '@udecode/cmdk';\nimport { Fzf } from 'fzf';\nimport { PlusIcon } from 'lucide-react';\nimport { isHotkey, KEYS } from 'platejs';\nimport {\n  Plate,\n  useEditorContainerRef,\n  useEditorRef,\n  usePlateEditor,\n} from 'platejs/react';\n\nimport {\n  Popover,\n  PopoverAnchor,\n  PopoverContent,\n} from '@/components/ui/popover';\nimport { cn } from '@/lib/utils';\n\nimport { Editor, EditorContainer } from './editor';\nimport { TagElement } from './tag-node';\n\nexport type SelectItem = {\n  value: string;\n  isNew?: boolean;\n};\n\ntype SelectEditorContextValue = {\n  items: SelectItem[];\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  defaultValue?: SelectItem[];\n  value?: SelectItem[];\n  onValueChange?: (items: SelectItem[]) => void;\n};\n\nconst SelectEditorContext = React.createContext<\n  SelectEditorContextValue | undefined\n>(undefined);\n\nconst useSelectEditorContext = () => {\n  const context = React.useContext(SelectEditorContext);\n\n  if (!context) {\n    throw new Error('useSelectEditor must be used within SelectEditor');\n  }\n\n  return context;\n};\n\nexport function SelectEditor({\n  children,\n  defaultValue,\n  items = [],\n  value,\n  onValueChange,\n}: {\n  children: React.ReactNode;\n  defaultValue?: SelectItem[];\n  items?: SelectItem[];\n  value?: SelectItem[];\n  onValueChange?: (items: SelectItem[]) => void;\n}) {\n  const [open, setOpen] = React.useState(false);\n  const [internalValue] = React.useState(defaultValue);\n\n  return (\n    <SelectEditorContext.Provider\n      value={{\n        items,\n        open,\n        setOpen,\n        value: value ?? internalValue,\n        onValueChange,\n      }}\n    >\n      <Command\n        className=\"overflow-visible bg-transparent has-data-readonly:w-fit\"\n        shouldFilter={false}\n        loop\n      >\n        {children}\n      </Command>\n    </SelectEditorContext.Provider>\n  );\n}\n\nexport function SelectEditorContent({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  const { value } = useSelectEditorContext();\n  const { setSearch } = useCommandActions();\n\n  const editor = usePlateEditor(\n    {\n      plugins: [MultiSelectPlugin.withComponent(TagElement)],\n      value: createEditorValue(value),\n    },\n    []\n  );\n\n  React.useEffect(() => {\n    if (!isEqualTags(editor, value)) {\n      editor.tf.replaceNodes(createEditorValue(value), {\n        at: [],\n        children: true,\n      });\n    }\n  }, [editor, value]);\n\n  return (\n    <Plate\n      onValueChange={({ editor }) => {\n        setSearch(editor.api.string([]));\n      }}\n      editor={editor}\n    >\n      <EditorContainer variant=\"select\">{children}</EditorContainer>\n    </Plate>\n  );\n}\n\nexport const SelectEditorInput = ({\n  ref,\n  ...props\n}: React.ComponentPropsWithoutRef<typeof Editor> & {\n  ref?: React.RefObject<HTMLDivElement | null>;\n}) => {\n  const editor = useEditorRef();\n  const { setOpen } = useSelectEditorContext();\n  const { selectCurrentItem, selectFirstItem } = useCommandActions();\n\n  return (\n    <Editor\n      ref={ref}\n      variant=\"select\"\n      onBlur={() => setOpen(false)}\n      onFocusCapture={() => {\n        setOpen(true);\n        selectFirstItem();\n      }}\n      onKeyDown={(e) => {\n        if (isHotkey('enter', e)) {\n          e.preventDefault();\n          selectCurrentItem();\n          editor.tf.removeNodes({ at: [], empty: false, text: true });\n        }\n        if (isHotkey('escape', e) || isHotkey('mod+enter', e)) {\n          e.preventDefault();\n          e.currentTarget.blur();\n        }\n      }}\n      autoFocusOnEditable\n      {...props}\n    />\n  );\n};\n\nexport function SelectEditorCombobox() {\n  const editor = useEditorRef();\n  const containerRef = useEditorContainerRef();\n  const { items, open, onValueChange } = useSelectEditorContext();\n  const selectableItems = useSelectableItems({\n    filter: fzfFilter,\n    items,\n  });\n  const { selectFirstItem } = useCommandActions();\n\n  useSelectEditorCombobox({ open, selectFirstItem, onValueChange });\n\n  if (!open || selectableItems.length === 0) return null;\n\n  return (\n    <Popover open={open}>\n      <PopoverAnchor virtualRef={containerRef as any} />\n      <PopoverContent\n        className=\"p-0 data-[state=open]:animate-none\"\n        style={{\n          // eslint-disable-next-line react-hooks/refs -- Reading ref for dynamic width calculation\n          width: (containerRef.current?.offsetWidth ?? 0) + 8,\n        }}\n        onCloseAutoFocus={(e) => e.preventDefault()}\n        onOpenAutoFocus={(e) => e.preventDefault()}\n        align=\"start\"\n        alignOffset={-4}\n        sideOffset={8}\n      >\n        <CommandList>\n          <CommandGroup>\n            {selectableItems.map((item) => (\n              <CommandItem\n                key={item.value}\n                className=\"cursor-pointer gap-2\"\n                onMouseDown={(e) => e.preventDefault()}\n                onSelect={() => {\n                  editor.getTransforms(TagPlugin).insert.tag(item);\n                }}\n              >\n                {item.isNew ? (\n                  <div className=\"flex items-center gap-1\">\n                    <PlusIcon className=\"size-4 text-foreground\" />\n                    Create new label:\n                    <span className=\"text-gray-600\">\"{item.value}\"</span>\n                  </div>\n                ) : (\n                  item.value\n                )}\n              </CommandItem>\n            ))}\n          </CommandGroup>\n        </CommandList>\n      </PopoverContent>\n    </Popover>\n  );\n}\n\nconst createEditorValue = (value?: SelectItem[]) => [\n  {\n    children: [\n      { text: '' },\n      ...(value?.flatMap((item) => [\n        {\n          children: [{ text: '' }],\n          type: KEYS.tag,\n          ...item,\n        },\n        {\n          text: '',\n        },\n      ]) ?? []),\n    ],\n    type: KEYS.p,\n  },\n];\n\nconst fzfFilter = (value: string, search: string): boolean => {\n  if (!search) return true;\n\n  const fzf = new Fzf([value], {\n    casing: 'case-insensitive',\n    selector: (v: string) => v,\n  });\n\n  return fzf.find(search).length > 0;\n};\n\n/**\n * You could replace this with import from '@/components/ui/command' + replace\n * 'cmdk' import with '@udecode/cmdk'\n */\nfunction Command({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive>) {\n  return (\n    <CommandPrimitive\n      className={cn(\n        'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',\n        className\n      )}\n      data-slot=\"command\"\n      {...props}\n    />\n  );\n}\n\nfunction CommandList({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.List>) {\n  return (\n    <CommandPrimitive.List\n      className={cn(\n        'max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden',\n        className\n      )}\n      data-slot=\"command-list\"\n      {...props}\n    />\n  );\n}\n\nfunction CommandGroup({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n  return (\n    <CommandPrimitive.Group\n      className={cn(\n        'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:text-xs',\n        className\n      )}\n      data-slot=\"command-group\"\n      {...props}\n    />\n  );\n}\n\nfunction CommandItem({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Item>) {\n  return (\n    <CommandPrimitive.Item\n      className={cn(\n        \"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n        className\n      )}\n      data-slot=\"command-item\"\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "docs": [
      {
        "route": "/docs/multi-select"
      }
    ],
    "examples": [
      "select-editor-demo"
    ],
    "label": "New"
  },
  "type": "registry:ui"
}