{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "select",
  "type": "registry:component",
  "title": "Select",
  "description": "A button that opens a list of options and shows the one chosen.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/panelui/select.tsx",
      "type": "registry:component",
      "target": "@components/panelui/select.tsx",
      "content": "'use client';\n\n/**\n * Select — a button that opens a list of options, one of which is chosen.\n *\n * ```tsx\n * <Select items={plans} defaultValue=\"team\">\n *   <Select.Trigger>\n *     <Select.Value placeholder=\"Pick a plan\" />\n *   </Select.Trigger>\n *   <Select.Content>\n *     {plans.map((plan) => (\n *       <Select.Item key={plan.value} value={plan.value} description={plan.price}>\n *         {plan.label}\n *       </Select.Item>\n *     ))}\n *   </Select.Content>\n * </Select>\n * ```\n *\n * ## The list opens over the trigger\n *\n * Opened with a pointer, the list is placed so the chosen option sits exactly\n * over the trigger, and the eye does not have to go looking for it. Where\n * there is no room for that, or `alignItemWithTrigger` is false, it opens\n * below. Opened from the keyboard, the arrow keys move through the options,\n * typing jumps to one by its first letters, and Enter chooses.\n *\n * ## Labels\n *\n * Pass `items` to the root, as `{ value, label }` pairs, and `Select.Value`\n * shows the chosen option's label rather than its value. Without `items`,\n * give `Select.Value` a function that turns a value into what to show.\n *\n * ## Three triggers\n *\n * `default` is a field, for a form. `ghost` has no box until it is hovered,\n * for a choice set into a toolbar or a line of text. `pill` is a rounded chip\n * with an edge, for a choice that sits among other chips, such as the model\n * in a prompt.\n */\nimport { Select as BaseSelect } from '@base-ui/react/select';\nimport type { ComponentProps, ReactNode } from 'react';\nimport { tv } from 'tailwind-variants';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport type { ElementType as RenderElementType } from 'react';\n\n/** Draws a part as another element, or composes it with a component: `render={<a href=\"/\" />}`. */\ntype RenderProp<Tag extends RenderElementType> = { render?: useRender.ComponentProps<Tag>['render'] };\n\nconst selectVariants = tv({\n  slots: {\n    trigger: [\n      'group/select-trigger inline-flex min-w-0 cursor-pointer select-none items-center justify-between gap-2 whitespace-nowrap text-foreground outline-none',\n      'transition-[background-color,border-color,box-shadow,color] duration-150',\n      'focus-visible:ring-[3px] focus-visible:ring-ring/30',\n      'data-disabled:cursor-not-allowed data-disabled:opacity-50',\n      '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n    ],\n    icon: 'flex text-muted-foreground transition-transform duration-200 group-data-popup-open/select-trigger:rotate-180 motion-reduce:transition-none',\n    value: 'flex min-w-0 flex-1 items-center gap-2 truncate text-start data-placeholder:text-muted-foreground/80',\n    positioner: 'z-50 outline-none',\n    popup: [\n      'min-w-(--anchor-width) origin-(--transform-origin) overflow-hidden rounded-xl border bg-popover text-popover-foreground shadow-lg outline-none',\n      'transition-[opacity,scale] duration-150 ease-out',\n      'data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0',\n      // Placed over the trigger, it only fades: growing from a point inside\n      // the list would look like it came from nowhere.\n      'data-[side=none]:data-starting-style:scale-100 data-[side=none]:data-ending-style:scale-100',\n      'motion-reduce:transition-none',\n    ],\n    list: 'max-h-(--available-height) scroll-py-1 overflow-y-auto p-1 [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin]',\n    item: [\n      'flex cursor-default items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm outline-none select-none',\n      'data-highlighted:bg-accent data-highlighted:text-accent-foreground',\n      'data-disabled:pointer-events-none data-disabled:opacity-50',\n      'in-data-[side=none]:min-w-[calc(var(--anchor-width)+0.5rem)]',\n      '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n    ],\n    itemIcon: 'flex size-4 shrink-0 items-center justify-center text-muted-foreground',\n    itemBody: 'flex min-w-0 flex-1 flex-col',\n    itemText: 'truncate',\n    itemDescription: 'truncate text-xs text-muted-foreground',\n    // The box is always there, so an option does not change width as it is chosen.\n    indicator: 'flex size-4 shrink-0 items-center justify-center text-foreground',\n    group: 'flex flex-col',\n    groupLabel: 'px-2 pb-1 pt-2 text-xs font-medium text-muted-foreground',\n    separator: 'mx-2 my-1 h-px bg-border',\n  },\n  variants: {\n    variant: {\n      default: {\n        trigger:\n          'rounded-lg border border-input bg-background shadow-xs hover:bg-accent/40 data-popup-open:border-ring dark:bg-input/30',\n      },\n      ghost: {\n        trigger:\n          'rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground data-popup-open:bg-accent data-popup-open:text-foreground',\n      },\n      pill: {\n        trigger:\n          'rounded-full border bg-background shadow-xs hover:bg-accent data-popup-open:bg-accent dark:bg-input/30',\n      },\n    },\n    size: {\n      sm: { trigger: 'h-8 px-2.5 text-[0.8125rem]' },\n      md: { trigger: 'h-9 px-3 text-sm' },\n      lg: { trigger: 'h-11 px-3.5 text-[0.9375rem]' },\n    },\n  },\n  compoundVariants: [\n    // A field is as wide as its column; a ghost or a pill is as wide as its label.\n    { variant: 'default', className: { trigger: 'w-full' } },\n    { variant: 'ghost', size: 'sm', className: { trigger: 'px-2' } },\n  ],\n  defaultVariants: { variant: 'default', size: 'md' },\n});\n\nexport type SelectProps<Value = string, Multiple extends boolean | undefined = false> = BaseSelect.Root.Props<\n  Value,\n  Multiple\n>;\n\n/** Holds the chosen value. Takes `value`/`onValueChange`, `defaultValue`, `items`, `name`, `disabled` and `multiple`. */\nfunction SelectRoot<Value = string, Multiple extends boolean | undefined = false>(props: SelectProps<Value, Multiple>) {\n  return <BaseSelect.Root {...props} />;\n}\nSelectRoot.displayName = 'Select';\n\nexport interface SelectTriggerProps extends BaseSelect.Trigger.Props {\n  /** `default` is a field, `ghost` has no box until hovered, `pill` is a rounded chip. Default `default`. */\n  variant?: 'default' | 'ghost' | 'pill';\n  /** Its height and text size. Default `md`. */\n  size?: 'sm' | 'md' | 'lg';\n  /** Replaces the chevron at the end. Pass `null` for none. */\n  icon?: ReactNode;\n  className?: string;\n}\n\nfunction Chevron({ variant }: { variant: SelectTriggerProps['variant'] }) {\n  // A field shows both directions, since its list can open either way over it.\n  // A chip shows one, and turns over while its list is open.\n  return (\n    <svg\n      aria-hidden=\"true\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    >\n      {variant === 'default' || variant === undefined ? (\n        <path d=\"m7 15 5 5 5-5M7 9l5-5 5 5\" />\n      ) : (\n        <path d=\"m6 9 6 6 6-6\" />\n      )}\n    </svg>\n  );\n}\n\n/** The button that opens the list. Put a `Select.Value` in it. */\nfunction SelectTrigger({ variant, size, icon, className, children, ...props }: SelectTriggerProps) {\n  const slots = selectVariants({ variant, size });\n  return (\n    <BaseSelect.Trigger data-slot=\"select-trigger\" className={slots.trigger({ className })} {...props}>\n      {children}\n      {icon === null ? null : (\n        <BaseSelect.Icon\n          data-slot=\"select-icon\"\n          className={slots.icon({\n            className: variant === 'default' || variant === undefined ? 'rotate-0!' : undefined,\n          })}\n        >\n          {icon ?? <Chevron variant={variant} />}\n        </BaseSelect.Icon>\n      )}\n    </BaseSelect.Trigger>\n  );\n}\nSelectTrigger.displayName = 'Select.Trigger';\n\nexport interface SelectValueProps extends BaseSelect.Value.Props {\n  className?: string;\n}\n\n/** The chosen option's label, or `placeholder` while there is none. */\nfunction SelectValue({ className, ...props }: SelectValueProps) {\n  return <BaseSelect.Value data-slot=\"select-value\" className={selectVariants().value({ className })} {...props} />;\n}\nSelectValue.displayName = 'Select.Value';\n\nexport interface SelectContentProps extends BaseSelect.Popup.Props {\n  /** Which side of the trigger it opens on, when it is not placed over it. Default `bottom`. */\n  side?: BaseSelect.Positioner.Props['side'];\n  /** How it lines up with the trigger along that side. Default `start`. */\n  align?: BaseSelect.Positioner.Props['align'];\n  /** The gap between the trigger and the list, in pixels. Default `6`. */\n  sideOffset?: number;\n  /** Whether a pointer opens it with the chosen option over the trigger. Default `true`. */\n  alignItemWithTrigger?: boolean;\n  /** Where in the document the list is put. Default `document.body`. */\n  container?: BaseSelect.Portal.Props['container'];\n  className?: string;\n}\n\n/** The list of options, opened in a layer over the page. */\nfunction SelectContent({\n  side = 'bottom',\n  align = 'start',\n  sideOffset = 6,\n  alignItemWithTrigger = true,\n  container,\n  className,\n  children,\n  ...props\n}: SelectContentProps) {\n  const slots = selectVariants();\n  return (\n    <BaseSelect.Portal container={container}>\n      <BaseSelect.Positioner\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        alignItemWithTrigger={alignItemWithTrigger}\n        className={slots.positioner()}\n      >\n        <BaseSelect.Popup data-slot=\"select-content\" className={slots.popup({ className })} {...props}>\n          <BaseSelect.List className={slots.list()}>{children}</BaseSelect.List>\n        </BaseSelect.Popup>\n      </BaseSelect.Positioner>\n    </BaseSelect.Portal>\n  );\n}\nSelectContent.displayName = 'Select.Content';\n\nexport interface SelectItemProps extends Omit<BaseSelect.Item.Props, 'children'> {\n  /** The option's label: what the trigger shows once it is chosen. */\n  children?: ReactNode;\n  /** A second, quieter line under the label. */\n  description?: ReactNode;\n  /** An icon before the label. */\n  icon?: ReactNode;\n  className?: string;\n}\n\n/** One option. A check marks the chosen one. */\nfunction SelectItem({ description, icon, className, children, ...props }: SelectItemProps) {\n  const slots = selectVariants();\n  return (\n    <BaseSelect.Item data-slot=\"select-item\" className={slots.item({ className })} {...props}>\n      {icon ? (\n        <span data-slot=\"select-item-icon\" aria-hidden=\"true\" className={slots.itemIcon()}>\n          {icon}\n        </span>\n      ) : null}\n      <span className={slots.itemBody()}>\n        <BaseSelect.ItemText className={slots.itemText()}>{children}</BaseSelect.ItemText>\n        {description ? <span className={slots.itemDescription()}>{description}</span> : null}\n      </span>\n      <span className={slots.indicator()}>\n        <BaseSelect.ItemIndicator className=\"flex\">\n          <svg\n            aria-hidden=\"true\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            strokeWidth=\"2.25\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          >\n            <path d=\"M20 6 9 17l-5-5\" />\n          </svg>\n        </BaseSelect.ItemIndicator>\n      </span>\n    </BaseSelect.Item>\n  );\n}\nSelectItem.displayName = 'Select.Item';\n\nexport interface SelectGroupProps extends BaseSelect.Group.Props {\n  className?: string;\n}\n\n/** Options that belong together, under a `Select.GroupLabel`. */\nfunction SelectGroup({ className, ...props }: SelectGroupProps) {\n  return <BaseSelect.Group data-slot=\"select-group\" className={selectVariants().group({ className })} {...props} />;\n}\nSelectGroup.displayName = 'Select.Group';\n\nexport interface SelectGroupLabelProps extends BaseSelect.GroupLabel.Props {\n  className?: string;\n}\n\n/** The heading of a group. Screen readers announce the group by it. */\nfunction SelectGroupLabel({ className, ...props }: SelectGroupLabelProps) {\n  return (\n    <BaseSelect.GroupLabel\n      data-slot=\"select-group-label\"\n      className={selectVariants().groupLabel({ className })}\n      {...props}\n    />\n  );\n}\nSelectGroupLabel.displayName = 'Select.GroupLabel';\n\nexport interface SelectSeparatorProps extends ComponentProps<'div'> {}\n\n/** A rule between groups of options. */\nfunction SelectSeparator({ className, render, ...props }: SelectSeparatorProps & RenderProp<'div'>) {\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      { role: 'separator', 'data-slot': 'select-separator', className: selectVariants().separator({ className }) },\n      props\n    ),\n  });\n}\nSelectSeparator.displayName = 'Select.Separator';\n\nexport const Select = Object.assign(SelectRoot, {\n  Trigger: SelectTrigger,\n  Value: SelectValue,\n  Content: SelectContent,\n  Item: SelectItem,\n  Group: SelectGroup,\n  GroupLabel: SelectGroupLabel,\n  Separator: SelectSeparator,\n});\n\nexport { SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectGroupLabel, SelectSeparator };\n"
    }
  ]
}
