{"slug":"picker","name":"Picker","packageName":"@liquidify/react","category":"controls","maturity":"experimental","summary":"A single-select control that chooses one value from a discrete set of options.","importPath":"@liquidify/react/picker","sourcePath":"packages/@liquidify/react/src/components/picker/picker.tsx","specPath":"packages/@liquidify/react/src/components/picker/spec.md","docsPath":"../liquidify-page/content/component-source/picker.md","documentationStatus":"draft","storyIds":["controls-picker--default"],"additionalExports":["ControlProps","PickerItem","PickerPlacement","PickerVariant"],"anatomy":[{"name":"menu","description":"menu — root renders the shared Popup compound primitive"},{"name":"wheel","description":"wheel — root is wrapping a"},{"name":"segmented","description":"segmented — root is . Each"},{"name":"radioGroup","description":"radioGroup — root is carrying"}],"props":[{"name":"className","type":"string | undefined","required":false,"description":"Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.","declaredIn":"packages/@liquidify/react/src/liquid-component-props.ts"},{"name":"defaultOpen","type":"boolean | undefined","required":false,"description":"Uncontrolled seed for the open state of the menu popover panel. Ignored for `segmented` and `wheel` styles. Pass `defaultOpen={true}` in a Storybook story or test to pre-open the listbox panel for visual grading without a user interaction.","defaultValue":"`false`","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"defaultValue","type":"string | undefined","required":false,"description":"Uncontrolled seed for the internal selection (ignored once {@link PickerProps.value} is supplied).","defaultValue":"`items[0].value`","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"disabled","type":"boolean | undefined","required":false,"description":"When `true`, the whole control is dimmed, removed from the tab order, and all interaction is suppressed. Since the roots vary by style (some are `<div>`, some `<button>`), `data-disabled` provides the CSS hook and `aria-disabled` is set on the appropriate ARIA root.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"items","type":"readonly PickerItem[]","required":true,"description":"The selectable options, rendered in order. At least one item is required.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"labelProps","type":"LabelProps | undefined","required":false,"description":"Per-instance typography override for the trigger value and the option labels, forwarded to the internal {@link Text} (ADR-0028). Token-backed axes only; the HIG default is `variant=\"body\"` with `color=\"inherit\"`, so each label adopts its host's colour (accent trigger, primary/secondary option, the wheel's selected colour transition). Applies only to string/number labels — an `<Icon>` / element label passes through unwrapped.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"onChange","type":"((next: string) => void) | undefined","required":false,"description":"Fires with the next selected item's `value` whenever the selection changes. Always called, in both controlled and uncontrolled modes.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"onOpenChange","type":"((open: boolean) => void) | undefined","required":false,"description":"Fires with the next open state on every open/close path of the menu popover panel — trigger click, item selection, Escape, and outside pointer-down. Ignored for `segmented` and `wheel` styles.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"open","type":"boolean | undefined","required":false,"description":"Controlled open state of the menu popover panel (ADR-0015 presentation triad, with {@link PickerProps.defaultOpen} / {@link PickerProps.onOpenChange}). Passing it switches the open state to controlled mode: the panel follows this prop and every open/close request only notifies. Ignored for `segmented` and `wheel` styles.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"placement","type":"PickerPlacement | undefined","required":false,"description":"Which trigger corner the `menu` panel anchors to — the corner picks the open side (`top-*` below the trigger, `bottom-*` above) and the trigger edge the panel aligns with (`*-left` / `*-right`), so the menu grows away from that corner (see {@link PickerPlacement}). Ignored for `segmented` and `wheel` styles.","defaultValue":"`\"top-left\"`","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"ref","type":"Ref<HTMLDivElement> | undefined","required":false,"description":"Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`.","declaredIn":"packages/@liquidify/react/src/liquid-component-props.ts"},{"name":"value","type":"string | undefined","required":false,"description":"Controlled selected value. Passing this switches Picker to controlled mode: the rendered selection always reflects this value and the consumer owns it via {@link PickerProps.onChange}.","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"},{"name":"variant","type":"PickerVariant | undefined","required":false,"description":"The visual variant of the picker — maps to SwiftUI `pickerStyle`.","defaultValue":"`\"menu\"`","declaredIn":"packages/@liquidify/react/src/components/picker/picker.tsx"}],"swiftUIMap":[{"swiftUI":"`Picker(_ title:, selection: Binding<V>, content:)`","react":"`aria-label` / `aria-labelledby` + `value?: string` + `onChange(next: string)`","notes":"`title` becomes the accessible name (via `aria-label`/`aria-labelledby`); `selection: Binding<V>` splits into `value` (controlled) + `onChange`; single-select, string-keyed (ADR-0015).","relationship":"direct"},{"swiftUI":"`@State sel = 1` seed","react":"`defaultValue?: string` (default `items[0].value`)","notes":"Uncontrolled seed; mirrors Segmented exactly.","relationship":"direct"},{"swiftUI":"`Text(opts[i]).tag(i)`","react":"`items: PickerItem[]` where `PickerItem = { value: string; label: ReactNode; disabled?: boolean; icon?: ReactNode }`","notes":"`.tag` → `value`; `Text`/`Label` → `label`; `Label(systemImage:)` → `icon`. A string/number `label` renders through the `Text` primitive (`variant=\"body\"`, `color=\"inherit\"` so each option/trigger keeps its own colour authority — accent trigger, primary/secondary option, the wheel selected-colour transition); an element label passes through unwrapped (ADR-0028).","relationship":"direct"},{"swiftUI":"`.font(_:)` / `.foregroundStyle(_:)` on labels","react":"`labelProps` (`Pick<TextProps, …>`)","notes":"Per-instance typography override forwarded to the internal `Text` of the trigger value **and** every option label (token-backed axes only; ADR-0028). Spread last, so an explicit value wins. Applies only to string/number labels.","relationship":"direct"},{"swiftUI":"`.pickerStyle(.menu)` / `.wheel` / `.segmented` / `.radioGroup`","react":"`variant?: PickerVariant` (`\"menu\" | \"wheel\" | \"segmented\" | \"radioGroup\"`)","notes":"The whole variant axis. Default `\"menu\"`. `radioGroup` is the SwiftUI macOS/visionOS `.radioGroup` style, rendered web-side with the iOS checkmark-row idiom (resolved ADR-0014/0027) — a vertical native-radio group with a trailing flat token-filled checkmark indicator.","relationship":"direct"},{"swiftUI":"`.radioGroup` selection idiom (`Picker(selection:)` single `Binding<Hashable>`)","react":"reuses `value` / `defaultValue` / `onChange` (value-bearing **string** triad, NOT the boolean `checked` triad)","notes":"`radioGroup` maps its single-select `Binding` to the **same string `value` triad** as `menu`/`wheel`/`segmented` and to Segmented — never Toggle's `checked`. The resolved `value` maps to the checked radio row; a controlled `value` absent from `items` renders no checked row (no raw-key echo).","relationship":"direct"},{"swiftUI":"menu presentation state (no SwiftUI counterpart — UIKit/AppKit own the menu presentation)","react":"`open?: boolean` + `defaultOpen?: boolean` (default `false`) + `onOpenChange?(open: boolean)`","notes":"The `menu` panel's presentation triad (ADR-0015): `open` switches to controlled mode, `defaultOpen` seeds the uncontrolled state, `onOpenChange` fires on every open/close path (trigger click, selection, `Escape`, outside pointer-down). Delegated to the shared `Popup` primitive. Ignored by `segmented`/`wheel`.","relationship":"direct"},{"swiftUI":"`.disabled(true)`","react":"`disabled?: boolean`","notes":"Whole-control disable: `data-disabled` + `aria-disabled` + `pointer-events: none` + removed from tab order. Per-item via `PickerItem.disabled`.","relationship":"direct"},{"swiftUI":"`.controlSize(_:)`","react":"— (deferred; see Sizes)","notes":"No size axis in v1.","relationship":"direct"},{"swiftUI":"`.tint(_:)`","react":"— (deferred; see Out of scope)","notes":"No tint axis in v1; accent is always `--lq-color-accent`.","relationship":"direct"},{"swiftUI":"`.frame(height: 150)` (wheel)","react":"n/a (component-owned geometry)","notes":"iOS drum height; resolved from `--lq-picker-wheel-h`.","relationship":"direct"},{"swiftUI":"`Picker(sources:selection:…)` multi-select","react":"—","notes":"Out of scope; single-select only.","relationship":"direct"}],"tokens":["--lq-color-","--lq-color-accent","--lq-color-bg-primary","--lq-color-label-primary","--lq-color-label-quaternary-dark","--lq-color-label-quaternary-light","--lq-color-label-secondary","--lq-color-separator","--lq-glass-","--lq-hit-target-touch","--lq-icon-size-small","--lq-lens-progress","--lq-lens-stretch","--lq-lens-x","--lq-picker-indicator-alpha","--lq-picker-pad","--lq-picker-wheel-band-fill","--lq-picker-wheel-band-h","--lq-picker-wheel-band-inset","--lq-picker-wheel-band-radius","--lq-picker-wheel-h","--lq-radius-large","--lq-radius-pill","--lq-shadow-ambient-","--lq-space-"],"keyboard":[{"key":"Keyboard","action":"(accessible listbox fallback): ↑/↓ scroll by one row; Home/ End to first/last; Enter confirms."}],"variants":["menu","radioGroup","segmented","wheel"],"variantPages":[{"slug":"picker-radio-group","variant":"radioGroup","title":"Picker (radioGroup variant)","docsPath":"../liquidify-page/content/component-source/picker-radio-group.md","documentationStatus":"stable"}]}