1
1
import { useLabelContext } from '@radix-ui/react-label'
2
2
import { 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'
4
11
import type { CSSProps , VariantProps } from '../../stitches.config'
5
12
import { styled } from '../../stitches.config'
6
13
import { ChevronDown } from '../Icons'
7
14
import { inputStyles } from '../Input/Input'
8
15
import { Label } from '../Label'
16
+ import { useId } from '@radix-ui/react-id'
9
17
import {
10
18
Menu ,
11
19
MenuContent ,
@@ -20,6 +28,7 @@ const DEFAULT_TAG = 'input'
20
28
const StyledSelect = styled ( DEFAULT_TAG , inputStyles , {
21
29
cursor : 'pointer' ,
22
30
textAlign : 'left' ,
31
+ pointerEvents : 'none' ,
23
32
} )
24
33
export const SelectItem = MenuRadioItem
25
34
@@ -54,6 +63,8 @@ type SelectProps = CSSProps &
54
63
defaultValue ?: string
55
64
/** Supply a starting placeholder value for uncontrolled instance */
56
65
placeholder ?: string
66
+ /** Supply a header for the select menu */
67
+ header ?: string
57
68
/** Called on Select change with new value */
58
69
onValueChange ?: ( value : string ) => void
59
70
} & ComponentProps < typeof DEFAULT_TAG >
@@ -71,12 +82,13 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
71
82
(
72
83
{
73
84
label,
74
- id,
85
+ id : idProp ,
75
86
value,
76
87
onValueChange,
77
88
defaultValue,
78
89
children,
79
90
placeholder,
91
+ header,
80
92
disabled,
81
93
...props
82
94
} ,
@@ -87,8 +99,25 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
87
99
defaultProp : defaultValue || placeholder ,
88
100
onChange : onValueChange ,
89
101
} )
102
+
103
+ const id = useId ( idProp )
90
104
const labelId = useLabelContext ( )
91
105
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
+
92
121
return (
93
122
< >
94
123
{ label && (
@@ -105,15 +134,16 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
105
134
disabled = { disabled }
106
135
{ ...props }
107
136
ref = { ref }
108
- value = { internalValue }
137
+ placeholder = { placeholder }
138
+ value = { internalValue && valueToText [ internalValue ] }
109
139
// Just there to suppress warning
110
140
onChange = { ( ) => null }
111
141
/>
112
142
< ChevronDown />
113
143
</ Root >
114
144
</ MenuTrigger >
115
145
< MenuContent align = "start" sideOffset = { 4 } alignOffset = { 4 } >
116
- { placeholder && < MenuItem disabled > { placeholder } </ MenuItem > }
146
+ { header && < MenuItem disabled > { header } </ MenuItem > }
117
147
< MenuRadioGroup value = { internalValue } onValueChange = { setValue } >
118
148
{ children }
119
149
</ MenuRadioGroup >
0 commit comments