{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "native-select",
  "title": "Native Select",
  "description": "A native single-selection input built on Expo UI Picker. Always compose with Content; optionally add Trigger and Input for a form-styled field or custom trigger. Content opens a wheel bottom sheet on iOS and ExposedDropdownMenuBox on Android.",
  "dependencies": [
    "@expo/ui",
    "react-native-reanimated"
  ],
  "registryDependencies": [
    "@tetra-ui/action-input",
    "@tetra-ui/bottom-sheet",
    "@tetra-ui/icons",
    "@tetra-ui/input",
    "@tetra-ui/slot"
  ],
  "files": [
    {
      "path": "ui/native-select/index.ts",
      "content": "export * from \"./native-select\";\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/native-select/native-select.tsx",
      "content": "import {\n  type PickerAppearance,\n  type PickerItemValue,\n  Picker as PickerPrimitive,\n} from \"@expo/ui\";\nimport {\n  Children,\n  createContext,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useState,\n} from \"react\";\nimport {\n  type GestureResponderEvent,\n  Platform,\n  Pressable,\n  type PressableProps,\n  View,\n} from \"react-native\";\nimport Animated, {\n  Easing,\n  interpolate,\n  useAnimatedStyle,\n  useSharedValue,\n  withTiming,\n} from \"react-native-reanimated\";\nimport { cn } from \"@/lib/utils\";\nimport { ActionInput } from \"../action-input\";\nimport {\n  BottomSheet,\n  BottomSheetBody,\n  BottomSheetContent,\n  BottomSheetFooter,\n  BottomSheetHeader,\n  BottomSheetTitle,\n} from \"../bottom-sheet\";\nimport { ChevronDownIcon } from \"../icons\";\nimport {\n  InputAddon,\n  type InputAddonChild,\n  type InputAddonChildren,\n  InputAddonIcon,\n  useInputAddons,\n} from \"../input\";\nimport { Slot } from \"../slot\";\nimport {\n  NativeSelectAndroidHost,\n  NativeSelectContentMenu,\n  NativeSelectTriggerAnchor,\n} from \"./native-select-input\";\nimport { NativeSelectPicker } from \"./native-select-picker\";\n\n// Constants\nconst ANIMATION_DURATION = 280;\nconst ANIMATION_EASING = Easing.out(Easing.cubic);\nconst NATIVE_SELECT_INPUT_NAME = \"NativeSelectInput\";\nconst NATIVE_SELECT_TRIGGER_NAME = \"NativeSelectTrigger\";\nconst NATIVE_SELECT_CONTENT_NAME = \"NativeSelectContent\";\nconst NATIVE_SELECT_SHEET_FOOTER_NAME = \"NativeSelectSheetFooter\";\nconst WHEEL_PICKER_HEIGHT = 216;\nconst DEFAULT_PLACEHOLDER = \"Select...\";\n\ntype NativeSelectVariant = PickerAppearance;\n\n// Types\ntype NativeSelectItemData<T extends PickerItemValue> = {\n  label: string;\n  value: T;\n};\n\ntype NativeSelectContextProps<T extends PickerItemValue> = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  value?: T;\n  onValueChange: (value: T) => void;\n  selectedValue?: T;\n  setSelectedValue: (value: T) => void;\n  onConfirm: (value?: T) => void;\n  onCancel: () => void;\n  disabled?: boolean;\n  items: NativeSelectItemData<T>[];\n  itemElements: React.ReactElement[];\n  setItemElements: (elements: React.ReactElement[]) => void;\n  placeholder: string;\n  setPlaceholder: (placeholder: string) => void;\n  variant: NativeSelectVariant;\n  setVariant: (variant: NativeSelectVariant) => void;\n  hasTrigger: boolean;\n  hasInput: boolean;\n  className?: string;\n  testID?: string;\n};\n\ntype NativeSelectProps<T extends PickerItemValue> = {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  value?: T;\n  onValueChange?: (value: T) => void;\n  disabled?: boolean;\n  variant?: NativeSelectVariant;\n  className?: string;\n  testID?: string;\n  children: React.ReactNode;\n};\n\ntype NativeSelectInputProps = Partial<\n  React.ComponentProps<typeof ActionInput>\n> & {\n  variant?: NativeSelectVariant;\n  placeholder?: string;\n  children?: React.ReactNode;\n};\n\ntype NativeSelectTriggerProps = PressableProps & {\n  asChild?: boolean;\n};\n\ntype NativeSelectContentProps = {\n  children?: React.ReactNode;\n};\n\ntype NativeSelectSheetConfirmProps = PressableProps & {\n  asChild?: boolean;\n};\n\n// Context\nconst NativeSelectContext =\n  createContext<NativeSelectContextProps<PickerItemValue> | null>(null);\n\nconst useNativeSelect = () => {\n  const context = useContext(NativeSelectContext);\n  if (!context) {\n    throw new Error(\"useNativeSelect must be used within a NativeSelect\");\n  }\n  return context;\n};\n\n// Helpers\nconst getDisplayName = (type: React.ReactElement[\"type\"]) => {\n  if (typeof type === \"string\" || !(\"displayName\" in type)) {\n    return;\n  }\n  return type.displayName;\n};\n\nconst extractNativeSelectItems = <T extends PickerItemValue>(\n  children: React.ReactNode\n): NativeSelectItemData<T>[] => {\n  const items: NativeSelectItemData<T>[] = [];\n\n  for (const child of Children.toArray(children)) {\n    if (!isValidElement(child) || child.type !== NativeSelectItem) {\n      continue;\n    }\n\n    const { label, value } = child.props as NativeSelectItemData<T>;\n    items.push({ label, value });\n  }\n\n  return items;\n};\n\nconst splitNativeSelectContentChildren = (children: React.ReactNode) => {\n  const itemElements: React.ReactElement[] = [];\n  let sheetFooter: React.ReactElement | undefined;\n\n  for (const child of Children.toArray(children)) {\n    if (!isValidElement(child)) {\n      continue;\n    }\n\n    if (child.type === NativeSelectItem) {\n      itemElements.push(child);\n      continue;\n    }\n\n    if (\n      child.type === NativeSelectSheetFooter ||\n      getDisplayName(child.type) === NATIVE_SELECT_SHEET_FOOTER_NAME\n    ) {\n      sheetFooter = child;\n    }\n  }\n\n  return { itemElements, sheetFooter };\n};\n\nconst getNativeSelectFormFlags = (children: React.ReactNode) => {\n  let hasTrigger = false;\n  let hasInput = false;\n  let inputVariant: NativeSelectVariant | undefined;\n\n  const visit = (node: React.ReactNode) => {\n    for (const child of Children.toArray(node)) {\n      if (!isValidElement(child)) {\n        continue;\n      }\n\n      const name = getDisplayName(child.type);\n      if (name === NATIVE_SELECT_TRIGGER_NAME) {\n        hasTrigger = true;\n      }\n      if (name === NATIVE_SELECT_INPUT_NAME) {\n        hasInput = true;\n        const props = child.props as { variant?: NativeSelectVariant };\n        inputVariant = props.variant ?? \"wheel\";\n      }\n\n      if (\n        child.props &&\n        typeof child.props === \"object\" &&\n        \"children\" in child.props\n      ) {\n        visit((child.props as { children?: React.ReactNode }).children);\n      }\n    }\n  };\n\n  visit(children);\n\n  return { hasInput, hasTrigger, inputVariant };\n};\n\n// Components\nexport const NativeSelectItem = PickerPrimitive.Item;\n\n/**\n * Native single-selection input built on Expo UI Picker.\n * Always compose with NativeSelectContent. Optionally add Trigger and Input.\n */\nexport const NativeSelect = <T extends PickerItemValue>({\n  open: openProp,\n  onOpenChange: onOpenChangeProp,\n  value: valueProp,\n  onValueChange: onValueChangeProp,\n  disabled,\n  variant: variantProp = \"menu\",\n  className,\n  testID,\n  children,\n}: NativeSelectProps<T>) => {\n  const [internalOpen, setInternalOpen] = useState(openProp ?? false);\n  const [internalValue, setInternalValue] = useState<T>();\n  const [selectedValue, setSelectedValue] = useState<T>();\n  const [itemElements, setItemElements] = useState<React.ReactElement[]>([]);\n  const [placeholder, setPlaceholder] = useState(DEFAULT_PLACEHOLDER);\n  // Seeded from root or Input; Input can still override. Do not re-sync from\n  // root props or an Input override will be overwritten.\n  const { hasTrigger, hasInput, inputVariant } = useMemo(\n    () => getNativeSelectFormFlags(children),\n    [children]\n  );\n  const hasFormUi = hasTrigger || hasInput;\n  const [variant, setVariant] = useState<NativeSelectVariant>(\n    () => inputVariant ?? (hasFormUi ? \"wheel\" : variantProp)\n  );\n\n  const items = useMemo(\n    () => extractNativeSelectItems<T>(itemElements),\n    [itemElements]\n  );\n\n  const isOpenControlled = openProp !== undefined;\n  const open = isOpenControlled ? openProp : internalOpen;\n\n  const isValueControlled = valueProp !== undefined;\n  const value = isValueControlled ? valueProp : internalValue;\n\n  useEffect(() => {\n    if (value !== undefined) {\n      setSelectedValue(value);\n    }\n  }, [value]);\n\n  const onOpenChange = useCallback(\n    (nextOpen: boolean) => {\n      if (!isOpenControlled) {\n        setInternalOpen(nextOpen);\n      }\n      onOpenChangeProp?.(nextOpen);\n    },\n    [isOpenControlled, onOpenChangeProp]\n  );\n\n  const onValueChange = useCallback(\n    (nextValue: T) => {\n      if (!isValueControlled) {\n        setInternalValue(nextValue);\n      }\n      onValueChangeProp?.(nextValue);\n    },\n    [isValueControlled, onValueChangeProp]\n  );\n\n  const onConfirm = useCallback(\n    (nextValue?: T) => {\n      const finalValue = nextValue ?? selectedValue;\n\n      if (typeof finalValue !== \"undefined\") {\n        onValueChange(finalValue);\n      }\n\n      onOpenChange(false);\n    },\n    [onOpenChange, onValueChange, selectedValue]\n  );\n\n  const onCancel = useCallback(() => {\n    if (value !== undefined) {\n      setSelectedValue(value);\n    }\n    onOpenChange(false);\n  }, [onOpenChange, value]);\n\n  const ctx = useMemo(\n    () => ({\n      className,\n      disabled,\n      hasInput,\n      hasTrigger,\n      itemElements,\n      items,\n      onCancel,\n      onConfirm,\n      onOpenChange,\n      onValueChange,\n      open,\n      placeholder,\n      selectedValue,\n      setItemElements,\n      setPlaceholder,\n      setSelectedValue,\n      setVariant,\n      testID,\n      value,\n      variant,\n    }),\n    [\n      className,\n      disabled,\n      hasInput,\n      hasTrigger,\n      itemElements,\n      items,\n      onCancel,\n      onConfirm,\n      onOpenChange,\n      onValueChange,\n      open,\n      placeholder,\n      selectedValue,\n      testID,\n      value,\n      variant,\n    ]\n  );\n\n  return (\n    <NativeSelectContext.Provider\n      value={ctx as NativeSelectContextProps<T | PickerItemValue>}\n    >\n      {Platform.OS === \"android\" && hasFormUi ? (\n        <NativeSelectAndroidHost\n          disabled={disabled}\n          onOpenChange={onOpenChange}\n          open={open}\n        >\n          {children}\n        </NativeSelectAndroidHost>\n      ) : (\n        children\n      )}\n    </NativeSelectContext.Provider>\n  );\n};\n\nexport const NativeSelectTrigger = ({\n  asChild,\n  onPress: onPressProp,\n  ...props\n}: NativeSelectTriggerProps) => {\n  const {\n    disabled,\n    open,\n    onOpenChange,\n    value,\n    items,\n    selectedValue,\n    setSelectedValue,\n  } = useNativeSelect();\n\n  const handlePress = useCallback(\n    (event: GestureResponderEvent) => {\n      onPressProp?.(event);\n\n      if (disabled) {\n        return;\n      }\n\n      const committedValue = value ?? items.at(0)?.value;\n      const seedValue = selectedValue ?? committedValue;\n      if (seedValue !== undefined) {\n        setSelectedValue(seedValue);\n      }\n\n      if (Platform.OS === \"android\") {\n        onOpenChange(!open);\n        return;\n      }\n\n      onOpenChange(true);\n    },\n    [\n      disabled,\n      items,\n      onOpenChange,\n      onPressProp,\n      open,\n      selectedValue,\n      setSelectedValue,\n      value,\n    ]\n  );\n\n  const Comp = asChild ? Slot.Pressable : Pressable;\n\n  return (\n    <NativeSelectTriggerAnchor disabled={disabled}>\n      <Comp {...props} disabled={disabled} onPress={handlePress} />\n    </NativeSelectTriggerAnchor>\n  );\n};\n\nNativeSelectTrigger.displayName = NATIVE_SELECT_TRIGGER_NAME;\n\n/**\n * Form-styled native select input.\n * - Default / wheel: display-only ActionInput (open via NativeSelectTrigger)\n * - iOS `menu`: non-pressable input shell; only the native menu picker is interactive\n */\nexport const NativeSelectInput = ({\n  variant = \"wheel\",\n  placeholder = DEFAULT_PLACEHOLDER,\n  className,\n  testID,\n  children,\n  ...props\n}: NativeSelectInputProps) => {\n  const {\n    open,\n    value,\n    onValueChange,\n    items,\n    itemElements,\n    disabled,\n    setPlaceholder,\n    setVariant,\n  } = useNativeSelect();\n\n  const addonElements = useMemo(() => {\n    const addons: InputAddonChild[] = [];\n    for (const child of Children.toArray(children)) {\n      if (isValidElement(child) && child.type === InputAddon) {\n        addons.push(child as InputAddonChild);\n      }\n    }\n    return addons;\n  }, [children]);\n\n  const { startAddons, endAddons, pressableClassName } = useInputAddons(\n    addonElements as InputAddonChildren\n  );\n\n  useLayoutEffect(() => {\n    setPlaceholder(placeholder);\n  }, [placeholder, setPlaceholder]);\n\n  useLayoutEffect(() => {\n    setVariant(variant);\n  }, [variant, setVariant]);\n\n  const committedValue = value ?? items.at(0)?.value;\n\n  const valueLabel = useMemo(() => {\n    if (committedValue === undefined) {\n      return;\n    }\n    return items.find((item) => item.value === committedValue)?.label;\n  }, [items, committedValue]);\n\n  const openSharedValue = useSharedValue(open ? 1 : 0);\n\n  useEffect(() => {\n    openSharedValue.value = withTiming(open ? 1 : 0, {\n      duration: ANIMATION_DURATION,\n      easing: ANIMATION_EASING,\n    });\n  }, [open, openSharedValue]);\n\n  const animatedStyle = useAnimatedStyle(() => {\n    const rotate = interpolate(openSharedValue.value, [0, 1], [0, 180]);\n    return {\n      transform: [{ rotate: `${rotate}deg` }],\n    };\n  });\n\n  if (Platform.OS === \"ios\" && variant === \"menu\") {\n    if (committedValue === undefined) {\n      return null;\n    }\n\n    return (\n      <View\n        className={cn(\n          \"flex min-h-12 w-full flex-row items-center gap-2 rounded-lg border border-input bg-background py-2 pr-0 pl-3\",\n          disabled && \"opacity-50\",\n          pressableClassName,\n          className\n        )}\n        pointerEvents=\"box-none\"\n      >\n        {startAddons}\n\n        <View className=\"min-w-0 grow\" />\n\n        <InputAddon align=\"inline-end\">\n          <NativeSelectPicker\n            appearance=\"menu\"\n            enabled={!disabled}\n            onValueChange={onValueChange}\n            selectedValue={committedValue}\n            testID={testID}\n          >\n            {itemElements}\n          </NativeSelectPicker>\n        </InputAddon>\n\n        {endAddons}\n      </View>\n    );\n  }\n\n  const sheetInputAddons = [\n    ...startAddons,\n    <InputAddon align=\"inline-end\" key=\"native-select-chevron\">\n      <Animated.View style={animatedStyle}>\n        <InputAddonIcon>\n          <ChevronDownIcon />\n        </InputAddonIcon>\n      </Animated.View>\n    </InputAddon>,\n    ...endAddons,\n  ] as InputAddonChildren;\n\n  return (\n    <ActionInput\n      {...props}\n      className={cn(pressableClassName, className)}\n      disabled={disabled}\n      focused={open}\n      placeholder={placeholder}\n      testID={testID}\n      value={valueLabel}\n    >\n      {sheetInputAddons}\n    </ActionInput>\n  );\n};\n\nNativeSelectInput.displayName = NATIVE_SELECT_INPUT_NAME;\n\n/**\n * Presentation surface for the native select. Always required.\n * - Content-only: inline Expo Picker (variant from root)\n * - iOS wheel + form UI: bottom sheet with wheel picker\n * - iOS menu + Input: registers items only (picker lives in NativeSelectInput)\n * - Android + form UI: ExposedDropdownMenu items\n */\nexport const NativeSelectContent = ({ children }: NativeSelectContentProps) => {\n  const {\n    open,\n    onOpenChange,\n    value,\n    onValueChange,\n    selectedValue,\n    setSelectedValue,\n    onCancel,\n    disabled,\n    variant,\n    placeholder,\n    setItemElements,\n    hasTrigger,\n    hasInput,\n    className,\n    testID,\n  } = useNativeSelect();\n\n  const hasFormUi = hasTrigger || hasInput;\n\n  const { itemElements, sheetFooter } = useMemo(\n    () => splitNativeSelectContentChildren(children),\n    [children]\n  );\n  const items = useMemo(\n    () => extractNativeSelectItems(itemElements),\n    [itemElements]\n  );\n\n  useLayoutEffect(() => {\n    setItemElements(itemElements);\n  }, [itemElements, setItemElements]);\n\n  const requiresConfirm =\n    Platform.OS === \"ios\" && variant === \"wheel\" && Boolean(sheetFooter);\n  const committedValue = value ?? items.at(0)?.value;\n  const draftValue = selectedValue ?? committedValue;\n  const pickerValue = requiresConfirm ? draftValue : committedValue;\n\n  const handlePickerValueChange = useCallback(\n    (nextValue: PickerItemValue) => {\n      if (requiresConfirm) {\n        setSelectedValue(nextValue);\n        return;\n      }\n      onValueChange(nextValue);\n    },\n    [onValueChange, requiresConfirm, setSelectedValue]\n  );\n\n  if (!hasFormUi) {\n    if (committedValue === undefined) {\n      return null;\n    }\n\n    return (\n      <NativeSelectPicker\n        appearance={variant}\n        className={className}\n        enabled={!disabled}\n        onValueChange={onValueChange}\n        selectedValue={committedValue}\n        testID={testID}\n      >\n        {itemElements}\n      </NativeSelectPicker>\n    );\n  }\n\n  // Menu picker is embedded in Input; Content only registers items.\n  if (Platform.OS === \"ios\" && variant === \"menu\" && hasInput) {\n    return null;\n  }\n\n  if (Platform.OS === \"android\") {\n    return (\n      <NativeSelectContentMenu\n        disabled={disabled}\n        items={items}\n        onOpenChange={onOpenChange}\n        onValueChange={onValueChange}\n        open={open}\n        selectedValue={committedValue}\n      />\n    );\n  }\n\n  if (pickerValue === undefined) {\n    return null;\n  }\n\n  return (\n    <BottomSheet\n      onOpenChange={requiresConfirm ? onCancel : onOpenChange}\n      open={open}\n    >\n      <BottomSheetContent>\n        <BottomSheetHeader>\n          <BottomSheetTitle>{placeholder}</BottomSheetTitle>\n        </BottomSheetHeader>\n        <BottomSheetBody className={sheetFooter ? undefined : \"pb-4\"}>\n          <NativeSelectPicker\n            appearance=\"wheel\"\n            enabled={!disabled}\n            matchContents={false}\n            onValueChange={handlePickerValueChange}\n            selectedValue={pickerValue}\n            style={{ height: WHEEL_PICKER_HEIGHT, width: \"100%\" }}\n            testID={testID}\n          >\n            {itemElements}\n          </NativeSelectPicker>\n        </BottomSheetBody>\n        {sheetFooter}\n      </BottomSheetContent>\n    </BottomSheet>\n  );\n};\n\nNativeSelectContent.displayName = NATIVE_SELECT_CONTENT_NAME;\n\nexport const NativeSelectSheetFooter = (\n  props: React.ComponentProps<typeof BottomSheetFooter>\n) => {\n  return <BottomSheetFooter {...props} />;\n};\n\nNativeSelectSheetFooter.displayName = NATIVE_SELECT_SHEET_FOOTER_NAME;\n\nexport const NativeSelectSheetConfirm = ({\n  asChild,\n  onPress: onPressProp,\n  ...props\n}: NativeSelectSheetConfirmProps) => {\n  const { onConfirm } = useNativeSelect();\n\n  const onPress = useCallback(\n    (event: GestureResponderEvent) => {\n      onPressProp?.(event);\n      onConfirm();\n    },\n    [onConfirm, onPressProp]\n  );\n\n  const Comp = asChild ? Slot.Pressable : Pressable;\n\n  return <Comp {...props} onPress={onPress} />;\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/native-select/native-select-input.tsx",
      "content": "import type { PickerItemValue } from \"@expo/ui\";\n\ntype NativeSelectAndroidHostProps = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  disabled?: boolean;\n  children: React.ReactNode;\n};\n\ntype NativeSelectTriggerAnchorProps = {\n  disabled?: boolean;\n  children: React.ReactElement;\n};\n\ntype NativeSelectContentMenuProps = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  disabled?: boolean;\n  selectedValue?: PickerItemValue;\n  onValueChange: (value: PickerItemValue) => void;\n  items: { label: string; value: PickerItemValue }[];\n};\n\n/** Default stub — Metro resolves the `.android` file on Android. */\nexport const NativeSelectAndroidHost = ({\n  children,\n}: NativeSelectAndroidHostProps) => {\n  return <>{children}</>;\n};\n\n/** Default stub — Metro resolves the `.android` file on Android. */\nexport const NativeSelectTriggerAnchor = ({\n  children,\n}: NativeSelectTriggerAnchorProps) => {\n  return <>{children}</>;\n};\n\n/** Default stub — Metro resolves the `.android` file on Android. */\nexport const NativeSelectContentMenu = (\n  _props: NativeSelectContentMenuProps\n) => {\n  return null;\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/native-select/native-select-input.android.tsx",
      "content": "import type { PickerItemValue } from \"@expo/ui\";\nimport {\n  DropdownMenuItem,\n  ExposedDropdownMenu,\n  ExposedDropdownMenuBox,\n  Host,\n  RNHostView,\n  Text as TextPrimitive,\n} from \"@expo/ui/jetpack-compose\";\nimport { clip, menuAnchor, Shapes } from \"@expo/ui/jetpack-compose/modifiers\";\nimport { useCSSVariable } from \"uniwind\";\n\ntype NativeSelectAndroidHostProps = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  disabled?: boolean;\n  children: React.ReactNode;\n};\n\n/**\n * Wraps Trigger + Content so ExposedDropdownMenuBox can own both the anchor and menu.\n *\n * @see https://docs.expo.dev/versions/latest/sdk/ui/jetpack-compose/exposeddropdownmenubox/\n */\nexport const NativeSelectAndroidHost = ({\n  open,\n  onOpenChange,\n  disabled,\n  children,\n}: NativeSelectAndroidHostProps) => {\n  return (\n    <Host matchContents style={{ alignSelf: \"stretch\", width: \"100%\" }}>\n      <ExposedDropdownMenuBox\n        expanded={open}\n        onExpandedChange={disabled ? undefined : onOpenChange}\n      >\n        {children}\n      </ExposedDropdownMenuBox>\n    </Host>\n  );\n};\n\ntype NativeSelectTriggerAnchorProps = {\n  disabled?: boolean;\n  children: React.ReactElement;\n};\n\n/** Anchors the Android ExposedDropdownMenu to the Trigger child. */\nexport const NativeSelectTriggerAnchor = ({\n  disabled,\n  children,\n}: NativeSelectTriggerAnchorProps) => {\n  return (\n    <RNHostView\n      matchContents\n      modifiers={[\n        menuAnchor(\"primaryNotEditable\", !disabled),\n        clip(Shapes.RoundedCorner(8)),\n      ]}\n      style={{ alignSelf: \"stretch\", width: \"100%\" }}\n    >\n      {children}\n    </RNHostView>\n  );\n};\n\ntype NativeSelectContentMenuProps = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  disabled?: boolean;\n  selectedValue?: PickerItemValue;\n  onValueChange: (value: PickerItemValue) => void;\n  items: { label: string; value: PickerItemValue }[];\n};\n\n/** Renders the Android ExposedDropdownMenu item list. */\nexport const NativeSelectContentMenu = ({\n  open,\n  onOpenChange,\n  disabled,\n  selectedValue,\n  onValueChange,\n  items,\n}: NativeSelectContentMenuProps) => {\n  const foregroundColor = useCSSVariable(\"--color-foreground\") as string;\n  const popoverColor = useCSSVariable(\"--color-popover\") as string;\n\n  return (\n    <ExposedDropdownMenu\n      containerColor={popoverColor}\n      expanded={open}\n      onDismissRequest={() => onOpenChange(false)}\n    >\n      {items.map((item) => {\n        const isSelected = item.value === selectedValue;\n\n        return (\n          <DropdownMenuItem\n            enabled={!disabled}\n            key={String(item.value)}\n            onClick={\n              disabled\n                ? undefined\n                : () => {\n                    onValueChange(item.value);\n                    onOpenChange(false);\n                  }\n            }\n          >\n            <DropdownMenuItem.Text>\n              <TextPrimitive color={foregroundColor}>\n                {item.label}\n              </TextPrimitive>\n            </DropdownMenuItem.Text>\n            {isSelected ? (\n              <DropdownMenuItem.TrailingIcon>\n                <TextPrimitive color={foregroundColor}>✓</TextPrimitive>\n              </DropdownMenuItem.TrailingIcon>\n            ) : null}\n          </DropdownMenuItem>\n        );\n      })}\n    </ExposedDropdownMenu>\n  );\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/native-select/native-select-picker.tsx",
      "content": "import {\n  Host as HostPrimitive,\n  type PickerAppearance,\n  type PickerItemValue,\n  Picker as PickerPrimitive,\n} from \"@expo/ui\";\nimport { withUniwind } from \"uniwind\";\nimport { cn } from \"@/lib/utils\";\n\nconst StyledHost = withUniwind(HostPrimitive);\n\nexport type NativeSelectPickerProps<T extends PickerItemValue> = {\n  appearance?: PickerAppearance;\n  selectedValue: T;\n  onValueChange: (value: T) => void;\n  enabled?: boolean;\n  className?: string;\n  style?: React.ComponentProps<typeof HostPrimitive>[\"style\"];\n  testID?: string;\n  matchContents?: boolean;\n  children: React.ReactNode;\n};\n\nexport const NativeSelectPicker = <T extends PickerItemValue>({\n  appearance = \"menu\",\n  selectedValue,\n  onValueChange,\n  enabled = true,\n  className,\n  style,\n  testID,\n  matchContents = true,\n  children,\n}: NativeSelectPickerProps<T>) => {\n  return (\n    <StyledHost\n      className={cn(className)}\n      matchContents={matchContents}\n      style={style}\n    >\n      <PickerPrimitive\n        appearance={appearance}\n        enabled={enabled}\n        onValueChange={onValueChange}\n        selectedValue={selectedValue}\n        testID={testID}\n      >\n        {children}\n      </PickerPrimitive>\n    </StyledHost>\n  );\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/native-select/native-select-picker.ios.tsx",
      "content": "import {\n  Host as HostPrimitive,\n  type PickerAppearance,\n  type PickerItemValue,\n} from \"@expo/ui\";\nimport { Picker as SwiftUIPicker, Text } from \"@expo/ui/swift-ui\";\nimport {\n  disabled as disabledModifier,\n  type ModifierConfig,\n  pickerStyle,\n  tag,\n  tint,\n} from \"@expo/ui/swift-ui/modifiers\";\nimport { Children, isValidElement } from \"react\";\nimport { useCSSVariable, withUniwind } from \"uniwind\";\nimport { cn } from \"@/lib/utils\";\n\nconst StyledHost = withUniwind(HostPrimitive);\n\nexport type NativeSelectPickerProps<T extends PickerItemValue> = {\n  appearance?: PickerAppearance;\n  selectedValue: T;\n  onValueChange: (value: T) => void;\n  enabled?: boolean;\n  className?: string;\n  style?: React.ComponentProps<typeof HostPrimitive>[\"style\"];\n  testID?: string;\n  matchContents?: boolean;\n  children: React.ReactNode;\n};\n\ntype PickerItemProps<T extends PickerItemValue> = {\n  label: string;\n  value: T;\n};\n\nconst extractItems = <T extends PickerItemValue>(children: React.ReactNode) => {\n  const items: PickerItemProps<T>[] = [];\n\n  for (const child of Children.toArray(children)) {\n    if (!isValidElement(child)) {\n      continue;\n    }\n\n    const { label, value } = child.props as PickerItemProps<T>;\n    if (typeof label !== \"string\" || value === undefined) {\n      continue;\n    }\n\n    items.push({ label, value });\n  }\n\n  return items;\n};\n\nexport const NativeSelectPicker = <T extends PickerItemValue>({\n  appearance = \"menu\",\n  selectedValue,\n  onValueChange,\n  enabled = true,\n  className,\n  style,\n  testID,\n  matchContents = true,\n  children,\n}: NativeSelectPickerProps<T>) => {\n  const primaryColor = useCSSVariable(\"--color-primary\") as string;\n  const items = extractItems<T>(children);\n\n  const modifiers: ModifierConfig[] = [\n    pickerStyle(appearance === \"wheel\" ? \"wheel\" : \"menu\"),\n    tint(primaryColor),\n  ];\n\n  if (!enabled) {\n    modifiers.push(disabledModifier(true));\n  }\n\n  return (\n    <StyledHost\n      className={cn(className)}\n      matchContents={matchContents}\n      style={style}\n    >\n      <SwiftUIPicker\n        modifiers={modifiers}\n        onSelectionChange={(value) => onValueChange(value as T)}\n        selection={selectedValue}\n        testID={testID}\n      >\n        {items.map((item) => (\n          <Text key={String(item.value)} modifiers={[tag(item.value)]}>\n            {item.label}\n          </Text>\n        ))}\n      </SwiftUIPicker>\n    </StyledHost>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
