1
+ /*
2
+ * Copyright 2025 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
9
+ * governing permissions and limitations under the License.
10
+ */
11
+
12
+ import { Radio as AriaRadio , Checkbox as AriaCheckbox , ContextValue , RadioProps , CheckboxProps } from 'react-aria-components' ;
13
+ import { FocusableRef , FocusableRefValue , SpectrumLabelableProps , HelpTextProps } from '@react-types/shared' ;
14
+ import { Checkbox } from './Checkbox' ;
15
+ import { forwardRef , ReactNode , useContext , useRef , createContext } from 'react' ;
16
+ import { useFocusableRef } from '@react-spectrum/utils' ;
17
+ import { SelectBoxContext } from './SelectBoxGroup' ;
18
+ import { style , focusRing , baseColor } from '../style' with { type : 'macro' } ;
19
+ import { controlFont , getAllowedOverrides , StyleProps } from './style-utils' with { type : 'macro' } ;
20
+ import { useSpectrumContextProps } from './useSpectrumContextProps' ;
21
+ import React from 'react' ;
22
+
23
+ export interface SelectBoxProps extends
24
+ Omit < CheckboxProps & RadioProps , 'className' | 'style' | 'children' > , StyleProps {
25
+ /**
26
+ * The value of the SelectBox.
27
+ */
28
+ value : string ,
29
+ /**
30
+ * The label for the element.
31
+ */
32
+ children ?: ReactNode ,
33
+ /**
34
+ * Whether the SelectBox is disabled.
35
+ */
36
+ isDisabled ?: boolean
37
+ }
38
+
39
+ export const SelectBoxItemContext = createContext < ContextValue < Partial < SelectBoxProps > , FocusableRefValue < HTMLLabelElement > > > ( null ) ;
40
+
41
+ // Simple basic styling with proper dark mode support
42
+ const selectBoxStyles = style ( {
43
+ ...focusRing ( ) ,
44
+ display : 'flex' ,
45
+ flexDirection : 'column' ,
46
+ lineHeight : 'title' ,
47
+ justifyContent : 'center' ,
48
+ flexShrink : 0 ,
49
+ alignItems : 'center' ,
50
+ fontFamily : 'sans' ,
51
+ font : 'ui' ,
52
+ //vertical orientation
53
+ size : {
54
+ default : {
55
+ size : {
56
+ S : 120 ,
57
+ M : 170 ,
58
+ L : 220 ,
59
+ XL : 270
60
+ }
61
+ } ,
62
+ //WIP horizontal orientation
63
+ orientation : {
64
+ horizontal : {
65
+ size : {
66
+ S : 280 ,
67
+ M : 368 ,
68
+ L : 420 ,
69
+ XL : 480
70
+ }
71
+ }
72
+ }
73
+ } ,
74
+ minWidth : {
75
+ default : {
76
+ size : {
77
+ S : 100 ,
78
+ M : 144 ,
79
+ L : 180 ,
80
+ XL : 220
81
+ }
82
+ } ,
83
+ orientation : {
84
+ horizontal : {
85
+ size : {
86
+ S : 160 ,
87
+ M : 188 ,
88
+ L : 220 ,
89
+ XL : 250
90
+ }
91
+ }
92
+ }
93
+ } ,
94
+ maxWidth : {
95
+ default : {
96
+ size : {
97
+ S : 140 ,
98
+ M : 200 ,
99
+ L : 260 ,
100
+ XL : 320
101
+ }
102
+ } ,
103
+ orientation : {
104
+ horizontal : {
105
+ size : {
106
+ S : 360 ,
107
+ M : 420 ,
108
+ L : 480 ,
109
+ XL : 540
110
+ }
111
+ }
112
+ }
113
+ } ,
114
+ minHeight : {
115
+ default : {
116
+ size : {
117
+ S : 100 ,
118
+ M : 144 ,
119
+ L : 180 ,
120
+ XL : 220
121
+ }
122
+ } ,
123
+ orientation : {
124
+ horizontal : 80
125
+ }
126
+ } ,
127
+ maxHeight : {
128
+ default : {
129
+ size : {
130
+ S : 140 ,
131
+ M : 200 ,
132
+ L : 260 ,
133
+ XL : 320
134
+ }
135
+ } ,
136
+ orientation : {
137
+ horizontal : 240
138
+ }
139
+ } ,
140
+ padding : {
141
+ size : {
142
+ S : 16 ,
143
+ M : 24 ,
144
+ L : 32 ,
145
+ XL : 40
146
+ }
147
+ } ,
148
+ borderRadius : 'lg' ,
149
+ backgroundColor : 'layer-2' ,
150
+ boxShadow : {
151
+ default : 'emphasized' ,
152
+ isHovered : 'elevated' ,
153
+ isSelected : 'elevated' ,
154
+ forcedColors : 'none'
155
+ } ,
156
+ position : 'relative' ,
157
+ borderWidth : 2 ,
158
+ borderStyle : {
159
+ default : 'solid' ,
160
+ isSelected : 'solid'
161
+ } ,
162
+ borderColor : {
163
+ default : 'transparent' ,
164
+ isSelected : 'gray-900' ,
165
+ isFocusVisible : 'transparent'
166
+ } ,
167
+ transition : 'default'
168
+ } , getAllowedOverrides ( ) ) ;
169
+
170
+ const checkboxContainer = style ( {
171
+ position : 'absolute' ,
172
+ top : 16 ,
173
+ left : 16
174
+ } , getAllowedOverrides ( ) ) ;
175
+
176
+ /**
177
+ * SelectBox components allow users to select options from a list.
178
+ * They can behave as radio buttons (single selection) or checkboxes (multiple selection).
179
+ */
180
+ export const SelectBox = /*#__PURE__*/ forwardRef ( function SelectBox ( props : SelectBoxProps , ref : FocusableRef < HTMLLabelElement > ) {
181
+ [ props , ref ] = useSpectrumContextProps ( props , ref , SelectBoxItemContext ) ;
182
+ let { children, value, isDisabled = false , UNSAFE_className = '' , UNSAFE_style} = props ;
183
+ let inputRef = useRef < HTMLInputElement | null > ( null ) ;
184
+ let domRef = useFocusableRef ( ref , inputRef ) ;
185
+
186
+ let groupContext = useContext ( SelectBoxContext ) ;
187
+ let {
188
+ allowMultiSelect = false ,
189
+ size = 'M' ,
190
+ orientation = 'vertical'
191
+ } = groupContext || { } ;
192
+
193
+ const Selector = allowMultiSelect ? AriaCheckbox : AriaRadio ;
194
+
195
+ return (
196
+ < Selector
197
+ value = { value }
198
+ isDisabled = { isDisabled }
199
+ ref = { domRef }
200
+ inputRef = { inputRef }
201
+ style = { UNSAFE_style }
202
+ className = { renderProps => UNSAFE_className + selectBoxStyles ( { ...renderProps , size, orientation} , props . styles ) } >
203
+ { renderProps => (
204
+ < >
205
+ { ( renderProps . isSelected || renderProps . isHovered ) && (
206
+ < div className = { checkboxContainer ( { ...renderProps , size} , props . styles ) } >
207
+ < Checkbox
208
+ value = { value }
209
+ isSelected = { renderProps . isSelected }
210
+ isDisabled = { isDisabled }
211
+ size = { size }
212
+ />
213
+ </ div >
214
+ ) }
215
+ { children }
216
+ </ >
217
+ ) }
218
+ </ Selector >
219
+ ) ;
220
+ } ) ;
0 commit comments