{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"tags","type":"registry:ui","title":"tags","description":"Tags are a way to apply multiple labels to an item.","author":"Hayden Bleasel <hello@haydenbleasel.com>","dependencies":["lucide-react"],"devDependencies":[],"registryDependencies":["badge","button","command","popover"],"files":[{"type":"registry:ui","path":"index.tsx","content":"\"use client\";\n\nimport { XIcon } from \"lucide-react\";\nimport {\n  type ComponentProps,\n  createContext,\n  type MouseEventHandler,\n  type ReactNode,\n  useContext,\n  useEffect,\n  useRef,\n  useState,\n} from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from \"@/components/ui/command\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { cn } from \"@/lib/utils\";\n\ntype TagsContextType = {\n  value?: string;\n  setValue?: (value: string) => void;\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  width?: number;\n  setWidth?: (width: number) => void;\n};\n\nconst TagsContext = createContext<TagsContextType>({\n  value: undefined,\n  setValue: undefined,\n  open: false,\n  onOpenChange: () => {},\n  width: undefined,\n  setWidth: undefined,\n});\n\nconst useTagsContext = () => {\n  const context = useContext(TagsContext);\n\n  if (!context) {\n    throw new Error(\"useTagsContext must be used within a TagsProvider\");\n  }\n\n  return context;\n};\n\nexport type TagsProps = {\n  value?: string;\n  setValue?: (value: string) => void;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  children?: ReactNode;\n  className?: string;\n};\n\nexport const Tags = ({\n  value,\n  setValue,\n  open: controlledOpen,\n  onOpenChange: controlledOnOpenChange,\n  children,\n  className,\n}: TagsProps) => {\n  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n  const [width, setWidth] = useState<number>();\n  const ref = useRef<HTMLDivElement>(null);\n\n  const open = controlledOpen ?? uncontrolledOpen;\n  const onOpenChange = controlledOnOpenChange ?? setUncontrolledOpen;\n\n  useEffect(() => {\n    if (!ref.current) {\n      return;\n    }\n\n    const resizeObserver = new ResizeObserver((entries) => {\n      setWidth(entries[0].contentRect.width);\n    });\n\n    resizeObserver.observe(ref.current);\n\n    return () => {\n      resizeObserver.disconnect();\n    };\n  }, []);\n\n  return (\n    <TagsContext.Provider\n      value={{ value, setValue, open, onOpenChange, width, setWidth }}\n    >\n      <Popover onOpenChange={onOpenChange} open={open}>\n        <div className={cn(\"relative w-full\", className)} ref={ref}>\n          {children}\n        </div>\n      </Popover>\n    </TagsContext.Provider>\n  );\n};\n\nexport type TagsTriggerProps = ComponentProps<typeof Button>;\n\nexport const TagsTrigger = ({\n  className,\n  children,\n  ...props\n}: TagsTriggerProps) => (\n  <PopoverTrigger asChild>\n    <Button\n      className={cn(\"h-auto w-full justify-between p-2\", className)}\n      // biome-ignore lint/a11y/useSemanticElements: \"Required\"\n      role=\"combobox\"\n      variant=\"outline\"\n      {...props}\n    >\n      <div className=\"flex flex-wrap items-center gap-1\">\n        {children}\n        <span className=\"px-2 py-px text-muted-foreground\">\n          Select a tag...\n        </span>\n      </div>\n    </Button>\n  </PopoverTrigger>\n);\n\nexport type TagsValueProps = ComponentProps<typeof Badge>;\n\nexport const TagsValue = ({\n  className,\n  children,\n  onRemove,\n  ...props\n}: TagsValueProps & { onRemove?: () => void }) => {\n  const handleRemove: MouseEventHandler<HTMLDivElement> = (event) => {\n    event.preventDefault();\n    event.stopPropagation();\n    onRemove?.();\n  };\n\n  return (\n    <Badge className={cn(\"flex items-center gap-2\", className)} {...props}>\n      {children}\n      {onRemove && (\n        // biome-ignore lint/a11y/noStaticElementInteractions: \"This is a clickable badge\"\n        // biome-ignore lint/a11y/useKeyWithClickEvents: \"This is a clickable badge\"\n        <div\n          className=\"size-auto cursor-pointer hover:text-muted-foreground\"\n          onClick={handleRemove}\n        >\n          <XIcon size={12} />\n        </div>\n      )}\n    </Badge>\n  );\n};\n\nexport type TagsContentProps = ComponentProps<typeof PopoverContent>;\n\nexport const TagsContent = ({\n  className,\n  children,\n  ...props\n}: TagsContentProps) => {\n  const { width } = useTagsContext();\n\n  return (\n    <PopoverContent\n      className={cn(\"p-0\", className)}\n      style={{ width }}\n      {...props}\n    >\n      <Command>{children}</Command>\n    </PopoverContent>\n  );\n};\n\nexport type TagsInputProps = ComponentProps<typeof CommandInput>;\n\nexport const TagsInput = ({ className, ...props }: TagsInputProps) => (\n  <CommandInput className={cn(\"h-9\", className)} {...props} />\n);\n\nexport type TagsListProps = ComponentProps<typeof CommandList>;\n\nexport const TagsList = ({ className, ...props }: TagsListProps) => (\n  <CommandList className={cn(\"max-h-[200px]\", className)} {...props} />\n);\n\nexport type TagsEmptyProps = ComponentProps<typeof CommandEmpty>;\n\nexport const TagsEmpty = ({\n  children,\n  className,\n  ...props\n}: TagsEmptyProps) => (\n  <CommandEmpty {...props}>{children ?? \"No tags found.\"}</CommandEmpty>\n);\n\nexport type TagsGroupProps = ComponentProps<typeof CommandGroup>;\n\nexport const TagsGroup = CommandGroup;\n\nexport type TagsItemProps = ComponentProps<typeof CommandItem>;\n\nexport const TagsItem = ({ className, ...props }: TagsItemProps) => (\n  <CommandItem\n    className={cn(\"cursor-pointer items-center justify-between\", className)}\n    {...props}\n  />\n);\n","target":"components/kibo-ui/tags/index.tsx"}],"css":{}}