@@ -17,35 +17,70 @@ export interface CheckboxGroupProps
1717 extends Omit < RACCheckboxGroupProps , 'children' > {
1818 children ?: ReactNode ;
1919 orientation ?: 'vertical' | 'horizontal' ;
20+ variant ?: 'checkbox' | 'card' | 'button' ;
21+ labelPlacement ?: 'start' | 'end' ;
2022}
2123
24+ type CheckBoxGroupVariant = Pick <
25+ CheckboxGroupProps ,
26+ 'orientation' | 'variant' | 'labelPlacement'
27+ > ;
28+
29+ const CheckboxVariantContext = React . createContext < CheckBoxGroupVariant | null > (
30+ null ,
31+ ) ;
32+
33+ const useCheckboxGroupVariantContext = ( ) => {
34+ const {
35+ labelPlacement = 'end' ,
36+ orientation = 'vertical' ,
37+ variant = 'checkbox' ,
38+ } = React . useContext ( CheckboxVariantContext ) ?? { } ;
39+
40+ return {
41+ labelPlacement,
42+ orientation,
43+ variant,
44+ } ;
45+ } ;
46+
2247export function CheckboxGroup ( {
2348 orientation = 'vertical' ,
49+ variant = 'checkbox' ,
50+ labelPlacement = 'end' ,
2451 ...props
2552} : CheckboxGroupProps ) {
2653 return (
27- < RACCheckboxGroup
28- { ...props }
29- data-orientation = { orientation }
30- className = { composeRenderProps ( props . className , ( className ) => {
31- return twMerge ( groupBox , className ) ;
32- } ) }
33- />
54+ < CheckboxVariantContext . Provider
55+ value = { { variant, orientation, labelPlacement } }
56+ >
57+ < RACCheckboxGroup
58+ { ...props }
59+ className = { composeRenderProps ( props . className , ( className ) => {
60+ return twMerge ( groupBox , className ) ;
61+ } ) }
62+ />
63+ </ CheckboxVariantContext . Provider >
3464 ) ;
3565}
3666
3767export function Checkboxes ( {
3868 className,
3969 ...props
4070} : React . JSX . IntrinsicElements [ 'div' ] ) {
71+ const { orientation } = useCheckboxGroupVariantContext ( ) ;
4172 return (
4273 < div
4374 data-ui = "box"
4475 className = { twMerge (
4576 'flex flex-col' ,
46- 'group-data-[orientation=horizontal]:flex-row' ,
47- 'group-data-[orientation=horizontal]:flex-wrap' ,
77+ orientation === 'horizontal' && [
78+ 'flex-row flex-wrap' ,
79+ 'gap-x-4 gap-y-2' ,
80+ 'has([data-ui=description]):not([class*=gap-y])]:gap-y-4' ,
81+ ] ,
4882 'has-data-[ui=description]:[&_label]:font-medium' ,
83+
4984 className ,
5085 ) }
5186 { ...props }
@@ -75,11 +110,9 @@ export function CheckboxField({
75110 ) ;
76111}
77112
78- type LabelPlacement = 'start' | 'end' ;
79-
80113interface CheckboxProps extends RACCheckboxProps {
81- labelPlacement ?: LabelPlacement ;
82114 render ?: never ;
115+ labelPlacement ?: CheckboxGroupProps [ 'labelPlacement' ] ;
83116}
84117
85118export interface CustomRenderCheckboxProps
@@ -92,6 +125,7 @@ export interface CustomRenderCheckboxProps
92125
93126export function Checkbox ( props : CheckboxProps | CustomRenderCheckboxProps ) {
94127 const descriptionContext = React . useContext ( DescriptionContext ) ;
128+ const { labelPlacement, orientation } = useCheckboxGroupVariantContext ( ) ;
95129
96130 if ( props . render ) {
97131 const { render, ...restProps } = props ;
@@ -119,7 +153,8 @@ export function Checkbox(props: CheckboxProps | CustomRenderCheckboxProps) {
119153 ) ;
120154 }
121155
122- const { labelPlacement = 'end' , ...restProps } = props ;
156+ const { labelPlacement : itemLabelPlacement , ...restProps } = props ;
157+ const placement = itemLabelPlacement ?? labelPlacement ;
123158
124159 return (
125160 < RACCheckbox
@@ -130,8 +165,9 @@ export function Checkbox(props: CheckboxProps | CustomRenderCheckboxProps) {
130165 props . className ,
131166 ( className , renderProps ) => {
132167 return twMerge (
133- 'group flex items-center text-base/6 group-data-[orientation=horizontal]:text-nowrap sm:text-sm/6' ,
134- labelPlacement === 'start' && 'flex-row-reverse justify-between' ,
168+ 'group flex items-center text-base/6 sm:text-sm/6' ,
169+ orientation === 'horizontal' && [ 'text-nowrap' ] ,
170+ placement === 'start' && 'flex-row-reverse justify-between' ,
135171 renderProps . isDisabled && 'opacity-50' ,
136172 className ,
137173 ) ;
@@ -142,9 +178,9 @@ export function Checkbox(props: CheckboxProps | CustomRenderCheckboxProps) {
142178 return (
143179 < >
144180 < CheckToggle
145- { ... renderProps }
181+ renderProps = { renderProps }
146182 className = { twMerge (
147- labelPlacement === 'end' ? 'me-3' : 'ms-3' ,
183+ placement === 'end' ? 'me-3' : 'ms-3' ,
148184 'size-4.5 sm:size-4' ,
149185 ) }
150186 />
@@ -159,35 +195,28 @@ export function Checkbox(props: CheckboxProps | CustomRenderCheckboxProps) {
159195 ) ;
160196}
161197
162- type BoxProps = Partial < CheckboxRenderProps > &
163- Omit < React . JSX . IntrinsicElements [ 'div' ] , 'children' > ;
198+ type BoxProps = {
199+ renderProps ?: Partial < CheckboxRenderProps > ;
200+ } & Omit < React . JSX . IntrinsicElements [ 'div' ] , 'children' > ;
164201
165- export function CheckToggle ( {
166- isReadOnly,
167- isHovered,
168- isSelected,
169- isIndeterminate,
170- isInvalid,
171- isFocusVisible,
172- className,
173- ...props
174- } : BoxProps ) {
202+ export function CheckToggle ( { renderProps, className, ...props } : BoxProps ) {
175203 return (
176204 < div
177205 { ...props }
178206 data-check-indicator
179207 className = { twMerge ( [
180208 'size-4' ,
181209 'flex shrink-0 items-center justify-center rounded-sm shadow ring ring-zinc-950/15 dark:ring-white/20' ,
182- isReadOnly
210+ renderProps ?. isReadOnly
183211 ? 'opacity-50'
184- : isHovered && 'ring-zinc-950/25 dark:ring-white/25' ,
185- isSelected || isIndeterminate
212+ : renderProps ?. isHovered && 'ring-zinc-950/25 dark:ring-white/25' ,
213+ renderProps ?. isSelected || renderProps ?. isIndeterminate
186214 ? 'ring-accent bg-accent shadow-[inset_0_1px_0_0_rgba(255,255,255,0.1)] dark:ring-0'
187215 : 'dark:bg-white/5 dark:[--contract:1.05]' ,
188216
189- isInvalid && 'ring-red-600 dark:ring-red-600' ,
190- isFocusVisible && 'outline-ring outline-2 outline-offset-3' ,
217+ renderProps ?. isInvalid && 'ring-red-600 dark:ring-red-600' ,
218+ renderProps ?. isFocusVisible &&
219+ 'outline-ring outline-2 outline-offset-3' ,
191220
192221 // when used in menu item as the selected indicator
193222 'in-[&[data-ui=content][data-hovered=true]]:ring-zinc-950/25' ,
@@ -204,15 +233,15 @@ export function CheckToggle({
204233 < CheckIcon
205234 className = { twMerge (
206235 'hidden size-4 text-[lch(from_var(--accent)_calc((49.44_-_l)_*_infinity)_0_0)]' ,
207- isSelected && ! isIndeterminate && 'inline' ,
236+ renderProps ?. isSelected && ! renderProps . isIndeterminate && 'inline' ,
208237 'in-[&[data-ui=content][data-selected=true]]:inline' ,
209238 ) }
210239 />
211240
212241 < MinusIcon
213242 className = { twMerge (
214243 'hidden size-4 text-[lch(from_var(--accent)_calc((49.44_-_l)_*_infinity)_0_0)]' ,
215- isIndeterminate && 'inline' ,
244+ renderProps ?. isIndeterminate && 'inline' ,
216245 ) }
217246 />
218247 </ div >
0 commit comments