-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCheckbox.tsx
More file actions
121 lines (110 loc) · 3.41 KB
/
Checkbox.tsx
File metadata and controls
121 lines (110 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { Checkbox as BaseCheckbox } from "@base-ui-components/react/checkbox";
import { forwardRef, type ReactNode } from "react";
import { LuCheck, LuMinus } from "react-icons/lu";
import { twMerge } from "tailwind-merge";
export type CheckboxSize = "sm" | "md" | "lg";
export interface CheckboxProps extends Omit<BaseCheckbox.Root.Props, "className" | "render"> {
size?: CheckboxSize;
label?: ReactNode;
description?: string;
className?: string;
}
const sizeClasses: Record<CheckboxSize, string> = {
sm: "h-4 w-4",
md: "h-5 w-5",
lg: "h-6 w-6",
};
const iconSizeClasses: Record<CheckboxSize, string> = {
sm: "h-3 w-3",
md: "h-3.5 w-3.5",
lg: "h-4 w-4",
};
const labelSizeClasses: Record<CheckboxSize, string> = {
sm: "text-sm",
md: "text-sm",
lg: "text-base",
};
function CheckboxIcon({ size }: { size: CheckboxSize }) {
return (
<>
<LuCheck
className={twMerge(iconSizeClasses[size], "hidden group-data-[checked]:block")}
strokeWidth={3}
/>
<LuMinus
className={twMerge(iconSizeClasses[size], "hidden group-data-[indeterminate]:block")}
strokeWidth={3}
/>
</>
);
}
export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
({ size = "md", label, description, className, disabled, ...props }, ref) => {
const checkbox = (
<BaseCheckbox.Root
ref={ref}
disabled={disabled}
className={twMerge(
"group inline-flex shrink-0 cursor-pointer items-center justify-center rounded border transition-colors",
sizeClasses[size],
"border-surface-600 bg-surface-800",
"hover:border-surface-500 hover:bg-surface-700",
"focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-900 focus-visible:outline-none",
"data-[checked]:border-brand-600 data-[checked]:bg-brand-600",
"data-[checked]:hover:border-brand-500 data-[checked]:hover:bg-brand-500",
"data-[indeterminate]:border-brand-600 data-[indeterminate]:bg-brand-600",
"disabled:cursor-not-allowed disabled:opacity-50",
!label && className,
)}
{...props}
>
<BaseCheckbox.Indicator className="flex items-center justify-center text-white">
<CheckboxIcon size={size} />
</BaseCheckbox.Indicator>
</BaseCheckbox.Root>
);
if (!label) {
return checkbox;
}
return (
<label
className={twMerge(
"inline-flex cursor-pointer items-start gap-3",
disabled && "cursor-not-allowed opacity-50",
className,
)}
>
{checkbox}
<div className="flex flex-col">
<span className={twMerge("text-surface-100", labelSizeClasses[size])}>{label}</span>
{description && <span className="mt-0.5 text-xs text-surface-400">{description}</span>}
</div>
</label>
);
},
);
Checkbox.displayName = "Checkbox";
// Checkbox Group
export interface CheckboxGroupProps {
children: ReactNode;
className?: string;
orientation?: "horizontal" | "vertical";
}
export function CheckboxGroup({
children,
className,
orientation = "vertical",
}: CheckboxGroupProps) {
return (
<div
role="group"
className={twMerge(
"flex",
orientation === "vertical" ? "flex-col gap-3" : "flex-row flex-wrap gap-4",
className,
)}
>
{children}
</div>
);
}