{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "select",
  "type": "registry:ui",
  "title": "Select",
  "description": "Dropdown select with option groups and a styled trigger. Radix-backed and fully accessible.",
  "dependencies": [
    "@radix-ui/react-select",
    "@saasflare/ui",
    "motion"
  ],
  "files": [
    {
      "path": "components/ui/select.tsx",
      "type": "registry:ui",
      "content": "// @toreview\n\"use client\"\n\n/**\n * @fileoverview Select — animated dropdown select with spring transitions and grouped options.\n * @module packages/ui/components/ui/select\n * @layer core\n *\n * @component\n * @example\n * import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@saasflare/ui';\n * <Select>\n *   <SelectTrigger>\n *     <SelectValue placeholder=\"Pick an option\" />\n *   </SelectTrigger>\n *   <SelectContent>\n *     <SelectItem value=\"one\">Option One</SelectItem>\n *     <SelectItem value=\"two\">Option Two</SelectItem>\n *   </SelectContent>\n * </Select>\n */\n\nimport * as React from \"react\"\nimport { m } from \"motion/react\"\nimport { CaretDownIcon, CaretUpIcon, CheckIcon } from \"@saasflare/ui\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\nimport { useSaasflareMotion, springBouncy } from \"@saasflare/ui\"\n\n/**\n * Animated dropdown select built on Radix Select. Manages value and open\n * state; compose with {@link SelectTrigger}, {@link SelectValue},\n * {@link SelectContent}, and {@link SelectItem}. Use for single-choice\n * selection from a list too long for radio buttons.\n *\n * @component\n * @layer core\n */\nfunction Select({\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n  return <SelectPrimitive.Root data-slot=\"select\" {...props} />\n}\n\n/**\n * Groups related items inside {@link SelectContent}, typically headed by a\n * {@link SelectLabel}.\n *\n * @component\n * @layer core\n */\nfunction SelectGroup({\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n  return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />\n}\n\n/**\n * Displays the selected item's text (or a `placeholder`) inside\n * {@link SelectTrigger}.\n *\n * @component\n * @layer core\n */\nfunction SelectValue({\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n  return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\n/**\n * Button that opens the dropdown — renders the current {@link SelectValue}\n * with a trailing caret icon.\n *\n * @component\n * @layer core\n */\nfunction SelectTrigger({\n  className,\n  size = \"md\",\n  children,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n  /** Control height of the trigger. `\"md\"` (h-9) or `\"sm\"` (h-8). (`\"default\"` is a deprecated alias for `\"md\"`.) */\n  size?: \"sm\" | \"md\" | \"default\"\n}) {\n  const sf = useSaasflareProps()\n  const resolvedSize = size === \"default\" ? \"md\" : size\n  return (\n    <SelectPrimitive.Trigger\n      data-slot=\"select-trigger\"\n      data-size={resolvedSize}\n      className={cn(\n        \"flex w-fit cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow,border-color] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=md]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:bg-input/30\",\n        \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <SelectPrimitive.Icon asChild>\n        <CaretDownIcon weight={sf.iconWeight} className=\"size-4 opacity-50\" />\n      </SelectPrimitive.Icon>\n    </SelectPrimitive.Trigger>\n  )\n}\n\n/**\n * Props for {@link SelectContent}. Extends {@link SaasflareComponentProps} so\n * `surface`, `radius`, `animated`, and `iconWeight` can override the\n * <SaasflareProvider> context per instance.\n */\ninterface SelectContentProps\n  extends Omit<React.ComponentProps<typeof SelectPrimitive.Content>, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {}\n\n/**\n * Portalled dropdown panel with a spring entrance animation — hosts the\n * scrollable item viewport plus the scroll-up/down affordances.\n *\n * @component\n * @layer core\n */\nfunction SelectContent({\n  className,\n  children,\n  position = \"popper\",\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: SelectContentProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const motion = useSaasflareMotion(sf.animated, springBouncy)\n\n  return (\n    <SelectPrimitive.Portal>\n      <SelectPrimitive.Content\n        {...props}\n        data-slot=\"select-content\"\n        position={position}\n        asChild\n      >\n        <m.div\n          data-surface={sf.surface}\n          data-radius={sf.radius}\n          data-animated={String(sf.animated)}\n          initial={motion.disabled ? false : { opacity: 0, scale: 0.96, y: -4 }}\n          animate={{ opacity: 1, scale: 1, y: 0 }}\n          transition={motion.transition}\n          className={cn(\n            \"relative z-50 max-h-[min(var(--radix-select-content-available-height),24rem)] min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md\",\n            position === \"popper\" &&\n              \"w-full min-w-[var(--radix-select-trigger-width)]\",\n            className\n          )}\n        >\n          <SelectScrollUpButton />\n          <SelectPrimitive.Viewport\n            className={cn(\n              \"p-1\",\n              position === \"popper\" && \"h-[var(--radix-select-trigger-height)]\"\n            )}\n          >\n            {children}\n          </SelectPrimitive.Viewport>\n          <SelectScrollDownButton />\n        </m.div>\n      </SelectPrimitive.Content>\n    </SelectPrimitive.Portal>\n  )\n}\n\n/**\n * Non-interactive heading for a {@link SelectGroup}.\n *\n * @component\n * @layer core\n */\nfunction SelectLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Label>) {\n  return (\n    <SelectPrimitive.Label\n      data-slot=\"select-label\"\n      className={cn(\"px-2 py-1.5 text-sm font-medium\", className)}\n      {...props}\n    />\n  )\n}\n\n/**\n * Selectable option inside {@link SelectContent} — shows a check indicator\n * when it is the current value.\n *\n * @component\n * @layer core\n */\nfunction SelectItem({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n  const sf = useSaasflareProps()\n  return (\n    <SelectPrimitive.Item\n      data-slot=\"select-item\"\n      className={cn(\n        \"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\n        className\n      )}\n      {...props}\n    >\n      <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n        <SelectPrimitive.ItemIndicator>\n          <CheckIcon weight={sf.iconWeight} className=\"size-4\" />\n        </SelectPrimitive.ItemIndicator>\n      </span>\n      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n    </SelectPrimitive.Item>\n  )\n}\n\n/**\n * Thin divider between groups of items.\n *\n * @component\n * @layer core\n */\nfunction SelectSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Separator>) {\n  return (\n    <SelectPrimitive.Separator\n      data-slot=\"select-separator\"\n      className={cn(\"bg-border pointer-events-none -mx-1 my-1 h-px\", className)}\n      {...props}\n    />\n  )\n}\n\n/**\n * Scroll affordance pinned to the top of the viewport when items overflow.\n * Rendered automatically by {@link SelectContent}.\n *\n * @component\n * @layer core\n */\nfunction SelectScrollUpButton({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n  const sf = useSaasflareProps()\n  return (\n    <SelectPrimitive.ScrollUpButton\n      data-slot=\"select-scroll-up-button\"\n      className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n      {...props}\n    >\n      <CaretUpIcon weight={sf.iconWeight} className=\"size-4\" />\n    </SelectPrimitive.ScrollUpButton>\n  )\n}\n\n/**\n * Scroll affordance pinned to the bottom of the viewport when items overflow.\n * Rendered automatically by {@link SelectContent}.\n *\n * @component\n * @layer core\n */\nfunction SelectScrollDownButton({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n  const sf = useSaasflareProps()\n  return (\n    <SelectPrimitive.ScrollDownButton\n      data-slot=\"select-scroll-down-button\"\n      className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n      {...props}\n    >\n      <CaretDownIcon weight={sf.iconWeight} className=\"size-4\" />\n    </SelectPrimitive.ScrollDownButton>\n  )\n}\n\nexport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectLabel,\n  SelectScrollDownButton,\n  SelectScrollUpButton,\n  SelectSeparator,\n  SelectTrigger,\n  SelectValue,\n  type SelectContentProps,\n}\n",
      "target": "components/ui/select.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/select.json"
  }
}
