1
1
import { Checkbox as CheckboxPrimitive } from '@base-ui-components/react/checkbox' ;
2
- import { CheckIcon } from 'lucide-react' ;
2
+ import { cva } from 'class-variance-authority' ;
3
+ import { CheckIcon , MinusIcon } from 'lucide-react' ;
3
4
import React from 'react' ;
4
5
5
6
import { cn } from '@/lib/tailwind/utils' ;
6
7
8
+ const labelVariants = cva ( 'flex items-center gap-2.5 text-primary' , {
9
+ variants : {
10
+ size : {
11
+ default : 'text-base' ,
12
+ sm : 'gap-2 text-sm' ,
13
+ lg : 'gap-3 text-lg' ,
14
+ } ,
15
+ } ,
16
+ defaultVariants : {
17
+ size : 'default' ,
18
+ } ,
19
+ } ) ;
20
+
21
+ const checkboxVariants = cva (
22
+ 'flex flex-none cursor-pointer items-center justify-center rounded-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-muted-foreground disabled:opacity-20 data-checked:bg-primary data-checked:text-primary-foreground data-indeterminate:border data-indeterminate:border-primary/20 data-unchecked:border data-unchecked:border-primary/20' ,
23
+ {
24
+ variants : {
25
+ size : {
26
+ default : 'size-5 [&_svg]:size-3.5 [&_svg]:stroke-3' ,
27
+ sm : 'size-4 rounded-[5px] [&_svg]:size-2.5 [&_svg]:stroke-4' ,
28
+ lg : 'size-6 [&_svg]:size-4 [&_svg]:stroke-3' ,
29
+ } ,
30
+ } ,
31
+ defaultVariants : {
32
+ size : 'default' ,
33
+ } ,
34
+ }
35
+ ) ;
36
+
7
37
export type CheckboxProps = Omit < CheckboxPrimitive . Root . Props , 'type' > & {
8
38
/**
9
39
* By default, the checkbox is wrapped in a `<label>`. Set to `false` if you do not want it.
10
40
*/
11
41
noLabel ?: boolean ;
12
42
labelProps ?: React . ComponentProps < 'label' > ;
43
+ size ?: 'default' | 'sm' | 'lg' ;
13
44
} ;
14
45
15
46
export function Checkbox ( {
16
47
children,
17
48
className,
18
49
noLabel,
19
50
labelProps,
51
+ size,
20
52
...props
21
53
} : CheckboxProps ) {
22
54
const Comp = noLabel ? React . Fragment : 'label' ;
@@ -25,33 +57,27 @@ export function Checkbox({
25
57
? { }
26
58
: {
27
59
...labelProps ,
28
- className : cn (
29
- 'flex items-center gap-2 text-base text-primary' ,
30
- labelProps ?. className
31
- ) ,
60
+ className : cn ( labelVariants ( { size } ) , labelProps ?. className ) ,
32
61
} ;
33
62
34
63
return (
35
64
< Comp { ...compProps } >
36
65
< CheckboxPrimitive . Root
37
- className = { cn (
38
- 'flex size-5 cursor-pointer items-center justify-center rounded-sm outline-none' ,
39
- 'focus-visible:ring-[3px] focus-visible:ring-ring/50' ,
40
- 'data-checked:bg-primary data-unchecked:border data-unchecked:border-primary/50' ,
41
- 'disabled:cursor-not-allowed disabled:bg-muted-foreground disabled:opacity-20' ,
42
- className
43
- ) }
66
+ className = { cn ( checkboxVariants ( { size } ) , className ) }
44
67
{ ...props }
45
68
>
46
69
< CheckboxPrimitive . Indicator
47
70
keepMounted = { true }
48
71
className = { cn (
49
72
'flex transition-transform duration-150 ease-in-out' ,
50
- 'data-checked:scale-100 data-checked:rotate-0 data-unchecked:invisible data-unchecked:scale-50 data-unchecked:rotate-45'
73
+ 'data-checked:scale-100 data-unchecked:invisible data-unchecked:scale-75'
74
+ ) }
75
+ render = { ( props , state ) => (
76
+ < span { ...props } >
77
+ { state . indeterminate ? < MinusIcon /> : < CheckIcon /> }
78
+ </ span >
51
79
) }
52
- >
53
- < CheckIcon className = "size-3.5 stroke-3 text-primary-foreground" />
54
- </ CheckboxPrimitive . Indicator >
80
+ />
55
81
</ CheckboxPrimitive . Root >
56
82
{ children }
57
83
</ Comp >
0 commit comments