1+ import { clsx } from 'clsx'
12import React , { useContext } from 'react'
23import Autocomplete from '../Autocomplete'
3- import Box from '../Box'
44import Checkbox from '../Checkbox'
55import Radio from '../Radio'
66import Select from '../Select/Select'
@@ -10,7 +10,6 @@ import TextInputWithTokens from '../TextInputWithTokens'
1010import Textarea from '../Textarea'
1111import { CheckboxOrRadioGroupContext } from '../internal/components/CheckboxOrRadioGroup'
1212import ValidationAnimationContainer from '../internal/components/ValidationAnimationContainer'
13- import { get } from '../constants'
1413import { useSlots } from '../hooks/useSlots'
1514import type { SxProp } from '../sx'
1615import { useId } from '../hooks/useId'
@@ -20,6 +19,12 @@ import FormControlLeadingVisual from './FormControlLeadingVisual'
2019import FormControlValidation from './_FormControlValidation'
2120import { FormControlContextProvider } from './_FormControlContext'
2221import { 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'
2328
2429export type FormControlProps = {
2530 children ?: React . ReactNode
@@ -45,6 +50,7 @@ export type FormControlProps = {
4550
4651const FormControl = React . forwardRef < HTMLDivElement , FormControlProps > (
4752 ( { children, disabled : disabledProp , layout = 'vertical' , id : idProp , required, sx, className} , ref ) => {
53+ const enabled = useFeatureFlag ( cssModulesFlag )
4854 const [ slots , childrenWithoutSlots ] = useSlots ( children , {
4955 caption : FormControlCaption ,
5056 label : FormControlLabel ,
@@ -127,69 +133,62 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
127133 } }
128134 >
129135 { isChoiceInput || layout === 'horizontal' ? (
130- < Box
136+ < StyledHorizontalLayout
131137 ref = { ref }
132- display = "flex"
133- alignItems = { slots . leadingVisual ? 'center' : undefined }
138+ data-has-leading-visual = { slots . leadingVisual ? '' : undefined }
134139 sx = { sx }
135- className = { className }
140+ className = { clsx ( className , {
141+ [ classes . ControlHorizontalLayout ] : enabled ,
142+ } ) }
136143 >
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 }
154162 { childrenWithoutSlots . filter (
155163 child =>
156164 React . isValidElement ( child ) &&
157165 ! [ Checkbox , Radio ] . some ( inputComponent => child . type === inputComponent ) ,
158166 ) }
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 }
171175 >
172176 { 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 } >
181180 { slots . label }
182181 { slots . caption }
183- </ Box >
184- </ Box >
182+ </ StyledLabelContainer >
183+ </ StyledHorizontalLayout >
185184 ) : (
186- < Box
185+ < StyledVerticalLayout
187186 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+ } ) }
193192 >
194193 { slots . label }
195194 { React . isValidElement ( InputComponent ) &&
@@ -215,13 +214,96 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
215214 < ValidationAnimationContainer show > { slots . validation } </ ValidationAnimationContainer >
216215 ) : null }
217216 { slots . caption }
218- </ Box >
217+ </ StyledVerticalLayout >
219218 ) }
220219 </ FormControlContextProvider >
221220 )
222221 } ,
223222)
224223
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+
225307export default Object . assign ( FormControl , {
226308 Caption : FormControlCaption ,
227309 Label : FormControlLabel ,
0 commit comments