1010 * governing permissions and limitations under the License.
1111 */
1212
13+ import { baseColor , focusRing , space , style } from '../style' with { type : 'macro' } ;
14+ import { CenterBaseline } from './CenterBaseline' ;
1315import {
14- Checkbox as AriaCheckbox ,
15- CheckboxProps as AriaCheckboxProps ,
16+ CheckboxButton ,
17+ CheckboxField ,
18+ CheckboxFieldProps ,
1619 CheckboxRenderProps
1720} from 'react-aria-components/Checkbox' ;
18- import { baseColor , focusRing , space , style } from '../style' with { type : 'macro' } ;
19- import { CenterBaseline } from './CenterBaseline' ;
2021import { CheckboxGroupStateContext } from 'react-aria-components/CheckboxGroup' ;
2122import CheckmarkIcon from '../ui-icons/Checkmark' ;
2223import { ContextValue , useSlottedContext } from 'react-aria-components/slots' ;
2324import { controlBorderRadius , controlFont , controlSize , getAllowedOverrides , StyleProps } from './style-utils' with { type : 'macro' } ;
2425import { createContext , forwardRef , ReactNode , useContext , useRef } from 'react' ;
2526import DashIcon from '../ui-icons/Dash' ;
26- import { FocusableRef , FocusableRefValue , GlobalDOMAttributes } from '@react-types/shared' ;
27+ import { FocusableRef , FocusableRefValue , GlobalDOMAttributes , HelpTextProps } from '@react-types/shared' ;
2728import { FormContext , useFormProps } from './Form' ;
29+ import { HelpText } from './Field' ;
2830import { pressScale } from './pressScale' ;
2931import { useFocusableRef } from './useDOMRef' ;
3032import { useSpectrumContextProps } from './useSpectrumContextProps' ;
@@ -42,20 +44,50 @@ interface CheckboxStyleProps {
4244
4345interface RenderProps extends CheckboxRenderProps , CheckboxStyleProps { }
4446
45- export interface CheckboxProps extends Omit < AriaCheckboxProps , 'className' | 'style' | 'render' | 'children' | 'onHover' | 'onHoverStart' | 'onHoverEnd' | 'onHoverChange' | 'onClick' | keyof GlobalDOMAttributes > , StyleProps , CheckboxStyleProps {
47+ export interface CheckboxProps extends Omit < CheckboxFieldProps , 'className' | 'style' | 'render' | 'children' | 'onHover' | 'onHoverStart' | 'onHoverEnd' | 'onHoverChange' | 'onClick' | keyof GlobalDOMAttributes > , HelpTextProps , StyleProps , CheckboxStyleProps {
4648 /** The label for the element. */
4749 children ?: ReactNode
4850}
4951
50- export const CheckboxContext = createContext < ContextValue < Partial < CheckboxProps > , FocusableRefValue < HTMLLabelElement > > > ( null ) ;
52+ export const CheckboxContext = createContext < ContextValue < Partial < CheckboxProps > , FocusableRefValue < HTMLInputElement , HTMLDivElement > > > ( null ) ;
53+
54+ const field = style ( {
55+ display : 'grid' ,
56+ gridTemplateColumns : [ 'max-content' , '1fr' ] ,
57+ columnGap : 'text-to-control' ,
58+ alignContent : 'start' ,
59+ width : {
60+ default : 'fit' ,
61+ isInCheckboxGroup : 'auto'
62+ } ,
63+ font : controlFont ( ) ,
64+ '--field-height' : {
65+ type : 'height' ,
66+ value : controlSize ( )
67+ } ,
68+ rowGap : {
69+ default : 'calc(var(--field-height) - 1lh)' ,
70+ isInCheckboxGroup : {
71+ size : {
72+ S : space ( 1 ) ,
73+ M : space ( 1 ) ,
74+ L : 2 ,
75+ XL : 2
76+ }
77+ }
78+ } ,
79+ gridColumnStart : {
80+ isInForm : 'field'
81+ }
82+ } , getAllowedOverrides ( ) ) ;
5183
5284const wrapper = style ( {
53- display : 'flex' ,
85+ display : 'grid' ,
86+ gridTemplateColumns : 'subgrid' ,
87+ gridColumnStart : 1 ,
88+ gridColumnEnd : - 1 ,
5489 position : 'relative' ,
55- columnGap : 'text-to-control' ,
5690 alignItems : 'baseline' ,
57- width : 'fit' ,
58- font : controlFont ( ) ,
5991 transition : 'colors' ,
6092 color : {
6193 default : baseColor ( 'neutral' ) ,
@@ -64,11 +96,8 @@ const wrapper = style({
6496 forcedColors : 'GrayText'
6597 }
6698 } ,
67- gridColumnStart : {
68- isInForm : 'field'
69- } ,
7099 disableTapHighlight : true
71- } , getAllowedOverrides ( ) ) ;
100+ } ) ;
72101
73102export const box = style < RenderProps > ( {
74103 ...focusRing ( ) ,
@@ -126,7 +155,7 @@ export const iconStyles = style({
126155 }
127156} ) ;
128157
129- const iconSize = {
158+ const smallerSize = {
130159 S : 'XS' ,
131160 M : 'S' ,
132161 L : 'M' ,
@@ -137,7 +166,7 @@ const iconSize = {
137166 * Checkboxes allow users to select multiple items from a list of individual items,
138167 * or to mark one individual item as selected.
139168 */
140- export const Checkbox = forwardRef ( function Checkbox ( { children, ...props } : CheckboxProps , ref : FocusableRef < HTMLLabelElement > ) {
169+ export const Checkbox = forwardRef ( function Checkbox ( { children, ...props } : CheckboxProps , ref : FocusableRef < HTMLInputElement , HTMLDivElement > ) {
141170 [ props , ref ] = useSpectrumContextProps ( props , ref , CheckboxContext ) ;
142171 let boxRef = useRef ( null ) ;
143172 let inputRef = useRef < HTMLInputElement | null > ( null ) ;
@@ -148,47 +177,66 @@ export const Checkbox = forwardRef(function Checkbox({children, ...props}: Check
148177 let ctx = useSlottedContext ( CheckboxContext , props . slot ) ;
149178
150179 return (
151- < AriaCheckbox
180+ < CheckboxField
152181 { ...props }
153182 ref = { domRef }
154183 inputRef = { inputRef }
155184 style = { props . UNSAFE_style }
156- className = { renderProps => ( props . UNSAFE_className || '' ) + wrapper ( { ...renderProps , isInForm, size : props . size || 'M' } , props . styles ) } >
157- { renderProps => {
158- let checkbox = (
159- < div
160- ref = { boxRef }
161- style = { pressScale ( boxRef ) ( renderProps ) }
162- className = { box ( {
163- ...renderProps ,
164- isSelected : renderProps . isSelected || renderProps . isIndeterminate ,
165- size : props . size || 'M' ,
166- isEmphasized : isInCheckboxGroup ? ctx ?. isEmphasized : props . isEmphasized
167- } ) } >
168- { renderProps . isIndeterminate &&
169- < DashIcon size = { iconSize [ props . size || 'M' ] } className = { iconStyles } />
170- }
171- { renderProps . isSelected && ! renderProps . isIndeterminate &&
172- < CheckmarkIcon size = { iconSize [ props . size || 'M' ] } className = { iconStyles } />
173- }
174- </ div >
175- ) ;
185+ className = { ( props . UNSAFE_className || '' ) + field ( { size : props . size || 'M' , isInCheckboxGroup} , props . styles ) } >
186+ { ( { isDisabled, isInvalid} ) => ( < >
187+ < CheckboxButton className = { renderProps => wrapper ( { ...renderProps , isInForm, size : props . size || 'M' } ) } >
188+ { renderProps => {
189+ let checkbox = (
190+ < div
191+ ref = { boxRef }
192+ style = { pressScale ( boxRef ) ( renderProps ) }
193+ className = { box ( {
194+ ...renderProps ,
195+ isSelected : renderProps . isSelected || renderProps . isIndeterminate ,
196+ size : props . size || 'M' ,
197+ isEmphasized : isInCheckboxGroup ? ctx ?. isEmphasized : props . isEmphasized
198+ } ) } >
199+ { renderProps . isIndeterminate &&
200+ < DashIcon size = { smallerSize [ props . size || 'M' ] } className = { iconStyles } />
201+ }
202+ { renderProps . isSelected && ! renderProps . isIndeterminate &&
203+ < CheckmarkIcon size = { smallerSize [ props . size || 'M' ] } className = { iconStyles } />
204+ }
205+ </ div >
206+ ) ;
176207
177- // Only render checkbox without center baseline if no label.
178- // This avoids expanding the checkbox height to the font's line height.
179- if ( ! children ) {
180- return checkbox ;
181- }
208+ // Only render checkbox without center baseline if no label.
209+ // This avoids expanding the checkbox height to the font's line height.
210+ if ( ! children ) {
211+ return checkbox ;
212+ }
182213
183- return (
184- < >
185- < CenterBaseline >
186- { checkbox }
187- </ CenterBaseline >
188- { children }
189- </ >
190- ) ;
191- } }
192- </ AriaCheckbox >
214+ return (
215+ < >
216+ < CenterBaseline >
217+ { checkbox }
218+ </ CenterBaseline >
219+ < span className = { style ( { gridColumnStart : 2 } ) } > { children } </ span >
220+ </ >
221+ ) ;
222+ } }
223+ </ CheckboxButton >
224+ < HelpText
225+ size = { isInCheckboxGroup ? smallerSize [ props . size || 'M' ] : props . size || 'M' }
226+ styles = { style ( {
227+ gridColumnStart : {
228+ default : 1 ,
229+ isInCheckboxGroup : 2
230+ } ,
231+ paddingTop : 0
232+ } ) ( { isInCheckboxGroup} ) }
233+ isDisabled = { isDisabled }
234+ isInvalid = { isInCheckboxGroup ? false : isInvalid }
235+ description = { props . description }
236+ showErrorIcon >
237+ { props . errorMessage }
238+ </ HelpText >
239+ </ > ) }
240+ </ CheckboxField >
193241 ) ;
194242} ) ;
0 commit comments