{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "select",
  "type": "registry:ui",
  "title": "Select",
  "description": "One value from a list of named options: a raised cap showing the value and an up-down chevron, opening the menu's frosted list with the chosen row over the trigger; the chosen row carries the latched green LED.",
  "dependencies": [
    "@base-ui/react@^1.8.0"
  ],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/components/select/select.tsx",
      "type": "registry:ui",
      "target": "components/metalui/select/select.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Select as BaseSelect } from '@base-ui/react/select';\nimport { menuParts, ListGlide } from '../menu/menu';\n\n/* ─────────────────────────────────────────────────────────\n * SELECT on Base UI Select: one value from a list of named options\n *\n * Use it for a value picked from a list: an icon, a folder colour, where to move a block, a\n * preset. Two to four short options that fit side by side are a Switcher; a long list you\n * search is a combobox; an action is a Menu.\n *\n *   trigger   a raised cap like a button (it is clicked, not typed into): the value, and an\n *             up-down chevron (it opens over itself, like a Mac pop-up button)\n *   rest      the cap; the placeholder in ink3 when nothing is chosen\n *   hover     the cap lightens a little; the chevron darkens\n *   open      the cap stays pressed in; the chevron is ink\n *   focus     the focus ring (keyboard only)\n *   disabled  40 %, no pointer\n *   invalid   a thin red ring inside the cap (a required value is missing)\n *   list      the menu's frosted plate. With room, it opens with the chosen row over the\n *             trigger; without, below it. It grows from the trigger on the surface spring\n *             (scale .97 → 1, opacity), and fades out fast on close\n *   rows      the menu's rows under one highlight that glides row to row on the settle spring;\n *             pointer and keys move it; ↑ ↓, Home, End,\n *             type-ahead; ↩ or a click chooses and closes; ⎋ closes without choosing\n *   chosen    the green LED the system uses for latched, in a slot before the label\n * Two sizes: regular 32 (forms, settings rows), compact 28 (dense strips, toolbars).\n * Reduce Motion: the list fades only.\n * ───────────────────────────────────────────────────────── */\n\nexport interface SelectOption<V extends string = string> {\n  value: V;\n  label: string;\n  /** A leading glyph or swatch, shown in the row and in the trigger. */\n  lead?: React.ReactNode;\n  disabled?: boolean;\n}\n\nexport interface SelectGroup<V extends string = string> {\n  /** An engraved heading over the group. */\n  label: string;\n  options: SelectOption<V>[];\n}\n\nexport interface SelectProps<V extends string = string> {\n  /** The options, flat or in labelled groups. */\n  options: SelectOption<V>[] | SelectGroup<V>[];\n  value?: V | null;\n  defaultValue?: V;\n  onValueChange?: (value: V) => void;\n  /** Shown in ink3 while nothing is chosen. */\n  placeholder?: string;\n  size?: 'regular' | 'compact';\n  disabled?: boolean;\n  /** A required value is missing. */\n  invalid?: boolean;\n  /** Its accessible name, when no visible label names it. */\n  'aria-label'?: string;\n  /** For a form. */\n  name?: string;\n  className?: string;\n}\n\nconst PRESS = 'not-data-disabled:active:translate-y-button-travel not-data-disabled:active:duration-button-press not-data-disabled:active:ease-linear';\nconst TRIGGER = {\n  regular: `mu-select-trigger select-trigger select-regular recipe-button transition-button ${PRESS} not-data-disabled:active:recipe-button-pressed data-popup-open:recipe-button-pressed`,\n  compact: `mu-select-trigger select-trigger select-compact recipe-button-compact transition-button-compact ${PRESS} not-data-disabled:active:recipe-button-compact-pressed data-popup-open:recipe-button-compact-pressed`,\n};\nconst VALUE = 'mu-select-value select-value';\nconst CHEVRON = 'mu-select-chevron select-chevron';\nconst POSITIONER = 'mu-menu-positioner z-menu-z';\nconst POP = `${menuParts.PLATE} relative mu-select-pop select-pop`;\n// The menu's live rows under its one gliding highlight.\nconst ROW = menuParts.LIVE_ROW;\nconst HEADING = menuParts.HEADING;\nconst SEP = menuParts.SEP;\nconst SLOT = 'select-led-slot';\nconst LED = 'mu-select-led select-led';\n\nconst isGroups = <V extends string>(o: SelectOption<V>[] | SelectGroup<V>[]): o is SelectGroup<V>[] => o.length > 0 && 'options' in o[0];\n\nfunction offset() {\n  if (typeof window === 'undefined') return 6;\n  return parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--mu-r-select-pop-offset')) || 6;\n}\n\n/** The up-down chevron: this list opens over its trigger. */\nfunction Chevron() {\n  return (\n    <svg aria-hidden viewBox=\"0 0 12 12\" className={CHEVRON} fill=\"none\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d=\"M3.5 4.75 6 2.5l2.5 2.25M3.5 7.25 6 9.5l2.5-2.25\" />\n    </svg>\n  );\n}\n\nfunction Row<V extends string>({ option }: { option: SelectOption<V> }) {\n  return (\n    <BaseSelect.Item value={option.value} disabled={option.disabled} className={ROW}>\n      <span className={SLOT}><BaseSelect.ItemIndicator className={LED}>{null}</BaseSelect.ItemIndicator></span>\n      {option.lead && <span aria-hidden className={menuParts.GLYPH}>{option.lead}</span>}\n      <BaseSelect.ItemText className={menuParts.LABEL}>{option.label}</BaseSelect.ItemText>\n    </BaseSelect.Item>\n  );\n}\n\n/** One value from a list of named options: a raised cap that opens a frosted list. */\nexport function Select<V extends string = string>({\n  options, value, defaultValue, onValueChange, placeholder, size = 'regular', disabled, invalid, className, name, ...aria\n}: SelectProps<V>) {\n  const flat = isGroups(options) ? options.flatMap((g) => g.options) : options;\n  const byValue = React.useMemo(() => new Map(flat.map((o) => [o.value, o])), [flat]);\n  return (\n    <BaseSelect.Root<V>\n      value={value}\n      defaultValue={defaultValue}\n      onValueChange={(v) => { if (v != null) onValueChange?.(v as V); }}\n      disabled={disabled}\n      name={name}\n    >\n      <BaseSelect.Trigger\n        aria-label={aria['aria-label']}\n        data-invalid={invalid ? '' : undefined}\n        aria-invalid={invalid || undefined}\n        className={className ? `${TRIGGER[size]} ${className}` : TRIGGER[size]}\n      >\n        <BaseSelect.Value className={VALUE} placeholder={placeholder}>\n          {(v: V | null) => {\n            const o = v != null ? byValue.get(v) : undefined;\n            if (!o) return placeholder ?? '';\n            return <>{o.lead && <span aria-hidden className={menuParts.GLYPH}>{o.lead}</span>}{o.label}</>;\n          }}\n        </BaseSelect.Value>\n        <Chevron />\n      </BaseSelect.Trigger>\n      <BaseSelect.Portal>\n        <BaseSelect.Positioner className={POSITIONER} sideOffset={offset()} collisionPadding={8}>\n          <BaseSelect.Popup className={POP}>\n            <ListGlide />\n            {isGroups(options)\n              ? options.flatMap((g, gi) => [\n                  gi > 0 ? <BaseSelect.Separator key={`${g.label}-sep`} className={SEP} /> : null,\n                  <BaseSelect.Group key={g.label}>\n                    <BaseSelect.GroupLabel className={HEADING}>{g.label}</BaseSelect.GroupLabel>\n                    {g.options.map((o) => <Row key={o.value} option={o} />)}\n                  </BaseSelect.Group>,\n                ])\n              : options.map((o) => <Row key={o.value} option={o} />)}\n          </BaseSelect.Popup>\n        </BaseSelect.Positioner>\n      </BaseSelect.Portal>\n    </BaseSelect.Root>\n  );\n}\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/select.md. SwiftUI: MetalSelect in the MetalUI Swift package."
}
