{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"native-select","type":"registry:ui","title":"Native Select","description":"A styled native <select> — fully accessible and mobile-friendly.","dependencies":["lucide-react"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/field.json"],"files":[{"path":"registry/dashral/native-select.tsx","type":"registry:file","target":"components/dashral/native-select.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { cn } from \"./lib/utils\"\nimport { FieldWrapper, splitFieldProps, useFieldControl, type FieldClassNames, type FieldOwnProps } from \"./field\"\n\ntype NativeSelectOption = string | { value: string; label?: string; disabled?: boolean }\ntype NativeSelectOptionGroup = { label: string; options: NativeSelectOption[] }\n\ntype NativeSelectProps = Omit<React.ComponentProps<\"select\">, \"children\"> &\n  FieldOwnProps & {\n    /** The choices: strings, { value, label, disabled }, or groups of them ({ label, options }). */\n    options: (NativeSelectOption | NativeSelectOptionGroup)[]\n    /** Shown while nothing is picked, as a first option that can't be picked again. */\n    placeholder?: string\n    classNames?: FieldClassNames & { select?: string; icon?: string }\n  }\n\n/** A styled native <select>: fully accessible and the phone's own picker on mobile. */\nfunction NativeSelect(props: NativeSelectProps) {\n  const [field, control] = splitFieldProps(props)\n  return (\n    <FieldWrapper {...field}>\n      <NativeSelectControl {...control} />\n    </FieldWrapper>\n  )\n}\n\nfunction renderOption(option: NativeSelectOption) {\n  const { value, label = value, disabled } = typeof option === \"string\" ? { value: option } : option\n  return (\n    <option key={value} value={value} disabled={disabled} className=\"text-foreground\">\n      {label}\n    </option>\n  )\n}\n\nfunction NativeSelectControl({ options, placeholder, className, classNames, ...rest }: Omit<NativeSelectProps, keyof FieldOwnProps>) {\n  const props = useFieldControl(rest)\n  // A placeholder only shows while the value is empty, so start there unless a value was given.\n  const defaultValue = placeholder && props.value === undefined && props.defaultValue === undefined ? \"\" : props.defaultValue\n\n  return (\n    <div className=\"relative\">\n      <select\n        data-slot=\"native-select\"\n        className={cn(\n          \"flex h-9 w-full appearance-none rounded-md border border-input bg-transparent py-1 pe-9 ps-3 text-base shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/15 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/15 md:text-sm dark:[&_option]:bg-popover\",\n          placeholder && \"has-[option[value='']:checked]:text-muted-foreground\",\n          className,\n          classNames?.select\n        )}\n        {...props}\n        defaultValue={defaultValue}\n      >\n        {placeholder && (\n          <option value=\"\" disabled>\n            {placeholder}\n          </option>\n        )}\n        {options.map((option) =>\n          typeof option === \"object\" && \"options\" in option ? (\n            <optgroup key={option.label} label={option.label}>\n              {option.options.map(renderOption)}\n            </optgroup>\n          ) : (\n            renderOption(option)\n          )\n        )}\n      </select>\n      <ChevronDownIcon\n        aria-hidden\n        className={cn(\"pointer-events-none absolute top-1/2 end-3 size-4 -translate-y-1/2 text-muted-foreground\", classNames?.icon)}\n      />\n    </div>\n  )\n}\n\nexport { NativeSelect, type NativeSelectProps, type NativeSelectOption, type NativeSelectOptionGroup }\n"}]}