11import { useLabelContext } from '@radix-ui/react-label'
22import { useControllableState } from '@radix-ui/react-use-controllable-state'
3- import React , { ComponentProps , ElementRef , forwardRef } from 'react'
3+ import React , {
4+ ComponentProps ,
5+ ElementRef ,
6+ forwardRef ,
7+ useMemo ,
8+ Children ,
9+ isValidElement ,
10+ } from 'react'
411import type { CSSProps , VariantProps } from '../../stitches.config'
512import { styled } from '../../stitches.config'
613import { ChevronDown } from '../Icons'
714import { inputStyles } from '../Input/Input'
815import { Label } from '../Label'
16+ import { useId } from '@radix-ui/react-id'
917import {
1018 Menu ,
1119 MenuContent ,
@@ -20,6 +28,7 @@ const DEFAULT_TAG = 'input'
2028const StyledSelect = styled ( DEFAULT_TAG , inputStyles , {
2129 cursor : 'pointer' ,
2230 textAlign : 'left' ,
31+ pointerEvents : 'none' ,
2332} )
2433export const SelectItem = MenuRadioItem
2534
@@ -54,6 +63,8 @@ type SelectProps = CSSProps &
5463 defaultValue ?: string
5564 /** Supply a starting placeholder value for uncontrolled instance */
5665 placeholder ?: string
66+ /** Supply a header for the select menu */
67+ header ?: string
5768 /** Called on Select change with new value */
5869 onValueChange ?: ( value : string ) => void
5970 } & ComponentProps < typeof DEFAULT_TAG >
@@ -71,12 +82,13 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
7182 (
7283 {
7384 label,
74- id,
85+ id : idProp ,
7586 value,
7687 onValueChange,
7788 defaultValue,
7889 children,
7990 placeholder,
91+ header,
8092 disabled,
8193 ...props
8294 } ,
@@ -87,8 +99,25 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
8799 defaultProp : defaultValue || placeholder ,
88100 onChange : onValueChange ,
89101 } )
102+
103+ const id = useId ( idProp )
90104 const labelId = useLabelContext ( )
91105
106+ const valueToText = useMemo < Record < string , string > > ( ( ) => {
107+ const mapped : Record < string , string > = { }
108+ Children . forEach ( Children . toArray ( children ) , ( child ) => {
109+ if (
110+ isValidElement ( child ) &&
111+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
112+ typeof child ?. props ?. children === 'string'
113+ ) {
114+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
115+ mapped [ child . props . value ] = child . props . children
116+ }
117+ } )
118+ return mapped
119+ } , [ children ] )
120+
92121 return (
93122 < >
94123 { label && (
@@ -105,15 +134,16 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
105134 disabled = { disabled }
106135 { ...props }
107136 ref = { ref }
108- value = { internalValue }
137+ placeholder = { placeholder }
138+ value = { internalValue && valueToText [ internalValue ] }
109139 // Just there to suppress warning
110140 onChange = { ( ) => null }
111141 />
112142 < ChevronDown />
113143 </ Root >
114144 </ MenuTrigger >
115145 < MenuContent align = "start" sideOffset = { 4 } alignOffset = { 4 } >
116- { placeholder && < MenuItem disabled > { placeholder } </ MenuItem > }
146+ { header && < MenuItem disabled > { header } </ MenuItem > }
117147 < MenuRadioGroup value = { internalValue } onValueChange = { setValue } >
118148 { children }
119149 </ MenuRadioGroup >
0 commit comments