1
+ import { clsx } from 'clsx'
1
2
import React , { useContext } from 'react'
2
3
import Autocomplete from '../Autocomplete'
3
- import Box from '../Box'
4
4
import Checkbox from '../Checkbox'
5
5
import Radio from '../Radio'
6
6
import Select from '../Select/Select'
@@ -10,7 +10,6 @@ import TextInputWithTokens from '../TextInputWithTokens'
10
10
import Textarea from '../Textarea'
11
11
import { CheckboxOrRadioGroupContext } from '../internal/components/CheckboxOrRadioGroup'
12
12
import ValidationAnimationContainer from '../internal/components/ValidationAnimationContainer'
13
- import { get } from '../constants'
14
13
import { useSlots } from '../hooks/useSlots'
15
14
import type { SxProp } from '../sx'
16
15
import { useId } from '../hooks/useId'
@@ -20,6 +19,12 @@ import FormControlLeadingVisual from './FormControlLeadingVisual'
20
19
import FormControlValidation from './_FormControlValidation'
21
20
import { FormControlContextProvider } from './_FormControlContext'
22
21
import { warning } from '../utils/warning'
22
+ import styled from 'styled-components'
23
+ import sx from '../sx'
24
+ import { toggleStyledComponent } from '../internal/utils/toggleStyledComponent'
25
+ import { cssModulesFlag } from './feature-flags'
26
+ import { useFeatureFlag } from '../FeatureFlags'
27
+ import classes from './FormControl.module.css'
23
28
24
29
export type FormControlProps = {
25
30
children ?: React . ReactNode
@@ -45,6 +50,7 @@ export type FormControlProps = {
45
50
46
51
const FormControl = React . forwardRef < HTMLDivElement , FormControlProps > (
47
52
( { children, disabled : disabledProp , layout = 'vertical' , id : idProp , required, sx, className} , ref ) => {
53
+ const enabled = useFeatureFlag ( cssModulesFlag )
48
54
const [ slots , childrenWithoutSlots ] = useSlots ( children , {
49
55
caption : FormControlCaption ,
50
56
label : FormControlLabel ,
@@ -127,69 +133,62 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
127
133
} }
128
134
>
129
135
{ isChoiceInput || layout === 'horizontal' ? (
130
- < Box
136
+ < StyledHorizontalLayout
131
137
ref = { ref }
132
- display = "flex"
133
- alignItems = { slots . leadingVisual ? 'center' : undefined }
138
+ data-has-leading-visual = { slots . leadingVisual ? '' : undefined }
134
139
sx = { sx }
135
- className = { className }
140
+ className = { clsx ( className , {
141
+ [ classes . ControlHorizontalLayout ] : enabled ,
142
+ } ) }
136
143
>
137
- < Box sx = { { '> input' : { marginLeft : 0 , marginRight : 0 } } } >
138
- { React . isValidElement ( InputComponent ) &&
139
- React . cloneElement (
140
- InputComponent as React . ReactElement < {
141
- id : string
142
- disabled : boolean
143
- required : boolean
144
- [ 'aria-describedby' ] : string
145
- } > ,
146
- {
147
- id,
148
- disabled,
149
- // allow checkboxes to be required
150
- required : required && ! isRadioInput ,
151
- [ 'aria-describedby' ] : captionId as string ,
152
- } ,
153
- ) }
144
+ < StyledChoiceInputs className = { classes . ControlChoiceInputs } >
145
+ { React . isValidElement ( InputComponent )
146
+ ? React . cloneElement (
147
+ InputComponent as React . ReactElement < {
148
+ id : string
149
+ disabled : boolean
150
+ required : boolean
151
+ [ 'aria-describedby' ] : string
152
+ } > ,
153
+ {
154
+ id,
155
+ disabled,
156
+ // allow checkboxes to be required
157
+ required : required && ! isRadioInput ,
158
+ [ 'aria-describedby' ] : captionId as string ,
159
+ } ,
160
+ )
161
+ : null }
154
162
{ childrenWithoutSlots . filter (
155
163
child =>
156
164
React . isValidElement ( child ) &&
157
165
! [ Checkbox , Radio ] . some ( inputComponent => child . type === inputComponent ) ,
158
166
) }
159
- </ Box >
160
- { slots . leadingVisual && (
161
- < Box
162
- color = { disabled ? 'fg.muted' : 'fg.default' }
163
- sx = { {
164
- '> *' : {
165
- minWidth : slots . caption ? get ( 'fontSizes.4' ) : get ( 'fontSizes.2' ) ,
166
- minHeight : slots . caption ? get ( 'fontSizes.4' ) : get ( 'fontSizes.2' ) ,
167
- fill : 'currentColor' ,
168
- } ,
169
- } }
170
- ml = { 2 }
167
+ </ StyledChoiceInputs >
168
+ { slots . leadingVisual ? (
169
+ < StyledLeadingVisual
170
+ className = { clsx ( {
171
+ [ classes . LeadingVisual ] : enabled ,
172
+ } ) }
173
+ data-disabled = { disabled ? '' : undefined }
174
+ data-has-caption = { slots . caption ? '' : undefined }
171
175
>
172
176
{ slots . leadingVisual }
173
- </ Box >
174
- ) }
175
- < Box
176
- sx = { {
177
- '> *' : { paddingLeft : 'var(--stack-gap-condensed)' } ,
178
- '> label' : { fontWeight : 'var(--base-text-weight-normal)' } ,
179
- } }
180
- >
177
+ </ StyledLeadingVisual >
178
+ ) : null }
179
+ < StyledLabelContainer className = { classes . LabelContainer } >
181
180
{ slots . label }
182
181
{ slots . caption }
183
- </ Box >
184
- </ Box >
182
+ </ StyledLabelContainer >
183
+ </ StyledHorizontalLayout >
185
184
) : (
186
- < Box
185
+ < StyledVerticalLayout
187
186
ref = { ref }
188
- display = "flex"
189
- flexDirection = "column"
190
- alignItems = "flex-start"
191
- sx = { { ... ( isLabelHidden ? { '> *:not(label) + *' : { marginTop : 1 } } : { '> * + *' : { marginTop : 1 } } ) , ... sx } }
192
- className = { className }
187
+ data-has-label = { ! isLabelHidden ? '' : undefined }
188
+ sx = { sx }
189
+ className = { clsx ( className , {
190
+ [ classes . ControlVerticalLayout ] : enabled ,
191
+ } ) }
193
192
>
194
193
{ slots . label }
195
194
{ React . isValidElement ( InputComponent ) &&
@@ -215,13 +214,96 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
215
214
< ValidationAnimationContainer show > { slots . validation } </ ValidationAnimationContainer >
216
215
) : null }
217
216
{ slots . caption }
218
- </ Box >
217
+ </ StyledVerticalLayout >
219
218
) }
220
219
</ FormControlContextProvider >
221
220
)
222
221
} ,
223
222
)
224
223
224
+ const StyledHorizontalLayout = toggleStyledComponent (
225
+ cssModulesFlag ,
226
+ 'div' ,
227
+ styled . div `
228
+ display: flex;
229
+
230
+ &:where([data-has-leading-visual]) {
231
+ align-items: center;
232
+ }
233
+
234
+ ${ sx }
235
+ ` ,
236
+ )
237
+
238
+ const StyledChoiceInputs = toggleStyledComponent (
239
+ cssModulesFlag ,
240
+ 'div' ,
241
+ styled . div `
242
+ > input {
243
+ margin-left: 0;
244
+ margin-right: 0;
245
+ }
246
+ ` ,
247
+ )
248
+
249
+ const StyledLabelContainer = toggleStyledComponent (
250
+ cssModulesFlag ,
251
+ 'div' ,
252
+ styled . div `
253
+ > * {
254
+ padding-left: var(--stack-gap-condensed);
255
+ }
256
+
257
+ > label {
258
+ font-weight: var(--base-text-weight-normal);
259
+ }
260
+ ` ,
261
+ )
262
+
263
+ const StyledVerticalLayout = toggleStyledComponent (
264
+ cssModulesFlag ,
265
+ 'div' ,
266
+ styled . div `
267
+ display: flex;
268
+ flex-direction: column;
269
+ align-items: flex-start;
270
+
271
+ & > *:not(label) + * {
272
+ margin-top: var(--base-size-4);
273
+ }
274
+
275
+ &:where([data-has-label]) > * + * {
276
+ margin-top: var(--base-size-4);
277
+ }
278
+
279
+ ${ sx }
280
+ ` ,
281
+ )
282
+
283
+ const StyledLeadingVisual = toggleStyledComponent (
284
+ cssModulesFlag ,
285
+ 'div' ,
286
+ styled . div `
287
+ color: var(--fgColor-default);
288
+ margin-left: var(--base-size-8);
289
+
290
+ &:where([data-disabled]) {
291
+ color: var(--fgColor-muted);
292
+ }
293
+
294
+ > * {
295
+ fill: currentColor;
296
+ min-width: var(--text-body-size-large);
297
+ min-height: var(--text-body-size-large);
298
+ }
299
+
300
+ > *:where([data-has-caption]) {
301
+ min-width: var(--base-size-24);
302
+ min-height: var(--base-size-24);
303
+ }
304
+ ` ,
305
+ )
306
+
225
307
export default Object . assign ( FormControl , {
226
308
Caption : FormControlCaption ,
227
309
Label : FormControlLabel ,
0 commit comments