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,61 @@ 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
+ className = { clsx ( className , {
189
+ [ classes . ControlVerticalLayout ] : enabled ,
190
+ } ) }
193
191
>
194
192
{ slots . label }
195
193
{ React . isValidElement ( InputComponent ) &&
@@ -215,13 +213,96 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
215
213
< ValidationAnimationContainer show > { slots . validation } </ ValidationAnimationContainer >
216
214
) : null }
217
215
{ slots . caption }
218
- </ Box >
216
+ </ StyledVerticalLayout >
219
217
) }
220
218
</ FormControlContextProvider >
221
219
)
222
220
} ,
223
221
)
224
222
223
+ const StyledHorizontalLayout = toggleStyledComponent (
224
+ cssModulesFlag ,
225
+ 'div' ,
226
+ styled . div `
227
+ display: flex;
228
+
229
+ &:where([data-has-leading-visual]) {
230
+ align-items: center;
231
+ }
232
+
233
+ ${ sx }
234
+ ` ,
235
+ )
236
+
237
+ const StyledChoiceInputs = toggleStyledComponent (
238
+ cssModulesFlag ,
239
+ 'div' ,
240
+ styled . div `
241
+ > input {
242
+ margin-left: 0;
243
+ margin-right: 0;
244
+ }
245
+ ` ,
246
+ )
247
+
248
+ const StyledLabelContainer = toggleStyledComponent (
249
+ cssModulesFlag ,
250
+ 'div' ,
251
+ styled . div `
252
+ > * {
253
+ padding-left: var(--stack-gap-condensed);
254
+ }
255
+
256
+ > label {
257
+ font-weight: var(--base-text-weight-normal);
258
+ }
259
+ ` ,
260
+ )
261
+
262
+ const StyledVerticalLayout = toggleStyledComponent (
263
+ cssModulesFlag ,
264
+ 'div' ,
265
+ styled . div `
266
+ display: flex;
267
+ flex-direction: column;
268
+ align-items: flex-start;
269
+
270
+ & > *:not(label) + * {
271
+ margin-top: var(--base-size-4);
272
+ }
273
+
274
+ &:where([data-has-label]) > * + * {
275
+ margin-top: var(--base-size-4);
276
+ }
277
+
278
+ ${ sx }
279
+ ` ,
280
+ )
281
+
282
+ const StyledLeadingVisual = toggleStyledComponent (
283
+ cssModulesFlag ,
284
+ 'div' ,
285
+ styled . div `
286
+ color: var(--fgColor-default);
287
+ margin-left: var(--base-size-8);
288
+
289
+ &:where([data-disabled]) {
290
+ color: var(--fgColor-muted);
291
+ }
292
+
293
+ > * {
294
+ fill: currentColor;
295
+ min-width: var(--text-body-size-large);
296
+ min-height: var(--text-body-size-large);
297
+ }
298
+
299
+ > *:where([data-has-caption]) {
300
+ min-width: var(--base-size-24);
301
+ min-height: var(--base-size-24);
302
+ }
303
+ ` ,
304
+ )
305
+
225
306
export default Object . assign ( FormControl , {
226
307
Caption : FormControlCaption ,
227
308
Label : FormControlLabel ,
0 commit comments