@exxatdesignux/ui 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/package.json +72 -0
  2. package/src/components/ui/avatar.tsx +384 -0
  3. package/src/components/ui/badge.tsx +49 -0
  4. package/src/components/ui/banner.tsx +364 -0
  5. package/src/components/ui/breadcrumb.tsx +120 -0
  6. package/src/components/ui/button.tsx +66 -0
  7. package/src/components/ui/calendar.tsx +220 -0
  8. package/src/components/ui/card.tsx +136 -0
  9. package/src/components/ui/chart.tsx +378 -0
  10. package/src/components/ui/checkbox.tsx +160 -0
  11. package/src/components/ui/coach-mark.tsx +361 -0
  12. package/src/components/ui/collapsible.tsx +33 -0
  13. package/src/components/ui/command.tsx +232 -0
  14. package/src/components/ui/date-picker-field.tsx +186 -0
  15. package/src/components/ui/dialog.tsx +171 -0
  16. package/src/components/ui/drag-handle-grip.tsx +10 -0
  17. package/src/components/ui/drawer.tsx +134 -0
  18. package/src/components/ui/dropdown-menu.tsx +422 -0
  19. package/src/components/ui/field.tsx +238 -0
  20. package/src/components/ui/form.tsx +137 -0
  21. package/src/components/ui/input-group.tsx +156 -0
  22. package/src/components/ui/input-mask.tsx +135 -0
  23. package/src/components/ui/input.tsx +22 -0
  24. package/src/components/ui/kbd.tsx +55 -0
  25. package/src/components/ui/label.tsx +25 -0
  26. package/src/components/ui/payment-card-fields.tsx +65 -0
  27. package/src/components/ui/popover.tsx +46 -0
  28. package/src/components/ui/radio-group.tsx +217 -0
  29. package/src/components/ui/select.tsx +191 -0
  30. package/src/components/ui/selection-tile-grid.tsx +246 -0
  31. package/src/components/ui/separator.tsx +28 -0
  32. package/src/components/ui/sheet.tsx +147 -0
  33. package/src/components/ui/sidebar.tsx +716 -0
  34. package/src/components/ui/skeleton.tsx +13 -0
  35. package/src/components/ui/sonner.tsx +39 -0
  36. package/src/components/ui/status-badge.tsx +109 -0
  37. package/src/components/ui/table.tsx +117 -0
  38. package/src/components/ui/tabs.tsx +90 -0
  39. package/src/components/ui/textarea.tsx +18 -0
  40. package/src/components/ui/tip.tsx +21 -0
  41. package/src/components/ui/toggle-group.tsx +89 -0
  42. package/src/components/ui/toggle-switch.tsx +31 -0
  43. package/src/components/ui/toggle.tsx +48 -0
  44. package/src/components/ui/tooltip.tsx +59 -0
  45. package/src/components/ui/view-segmented-control.tsx +160 -0
  46. package/src/globals.css +1795 -0
  47. package/src/hooks/.gitkeep +0 -0
  48. package/src/hooks/use-app-theme.ts +172 -0
  49. package/src/hooks/use-coach-mark.ts +342 -0
  50. package/src/hooks/use-mobile.ts +31 -0
  51. package/src/hooks/use-mod-key-label.ts +29 -0
  52. package/src/index.ts +55 -0
  53. package/src/lib/compose-refs.ts +15 -0
  54. package/src/lib/date-filter.ts +67 -0
  55. package/src/lib/utils.ts +6 -0
  56. package/src/theme/apply-windows-contrast-theme.ts +29 -0
  57. package/src/theme/windows-contrast-theme.json +147 -0
  58. package/src/theme.css +1130 -0
  59. package/src/types/react-payment-inputs.d.ts +20 -0
@@ -0,0 +1,378 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RechartsPrimitive from "recharts"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ // Format: { THEME_NAME: CSS_SELECTOR }
9
+ const THEMES = { light: "", dark: ".dark" } as const
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode
14
+ icon?: React.ComponentType
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ )
19
+ }
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig
23
+ }
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null)
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext)
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />")
32
+ }
33
+
34
+ return context
35
+ }
36
+
37
+ function ChartContainer({
38
+ id,
39
+ className,
40
+ children,
41
+ config,
42
+ ...props
43
+ }: React.ComponentProps<"div"> & {
44
+ config: ChartConfig
45
+ children: React.ComponentProps<
46
+ typeof RechartsPrimitive.ResponsiveContainer
47
+ >["children"]
48
+ }) {
49
+ const uniqueId = React.useId()
50
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
51
+
52
+ return (
53
+ <ChartContext.Provider value={{ config }}>
54
+ <div
55
+ data-slot="chart"
56
+ data-chart={chartId}
57
+ className={cn(
58
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_line]:stroke-border/50 [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_line]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden",
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ <ChartStyle id={chartId} config={config} />
64
+ <RechartsPrimitive.ResponsiveContainer>
65
+ {children}
66
+ </RechartsPrimitive.ResponsiveContainer>
67
+ </div>
68
+ </ChartContext.Provider>
69
+ )
70
+ }
71
+
72
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
73
+ const colorConfig = Object.entries(config).filter(
74
+ ([, config]) => config.theme || config.color
75
+ )
76
+
77
+ if (!colorConfig.length) {
78
+ return null
79
+ }
80
+
81
+ return (
82
+ <style
83
+ dangerouslySetInnerHTML={{
84
+ __html: Object.entries(THEMES)
85
+ .map(
86
+ ([theme, prefix]) => `
87
+ ${prefix} [data-chart=${id}] {
88
+ ${colorConfig
89
+ .map(([key, itemConfig]) => {
90
+ const color =
91
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
92
+ itemConfig.color
93
+ return color ? ` --color-${key}: ${color};` : null
94
+ })
95
+ .join("\n")}
96
+ }
97
+ `
98
+ )
99
+ .join("\n"),
100
+ }}
101
+ />
102
+ )
103
+ }
104
+
105
+ const ChartTooltip = RechartsPrimitive.Tooltip
106
+
107
+ /**
108
+ * Returns props for `<ChartTooltip />` keyboard parity.
109
+ *
110
+ * Usage — pass `key` explicitly (React requires it outside the spread):
111
+ * ```tsx
112
+ * const sync = chartTooltipKeyboardSyncProps(activeIndex)
113
+ * <ChartTooltip key={sync.key} {...sync.props} />
114
+ * ```
115
+ */
116
+ function chartTooltipKeyboardSyncProps(keyboardActiveIndex: number | null): {
117
+ key: string
118
+ props: { defaultIndex?: number }
119
+ } {
120
+ const hasKbd =
121
+ typeof keyboardActiveIndex === "number" && keyboardActiveIndex >= 0
122
+ return {
123
+ key: hasKbd ? `kbd-${keyboardActiveIndex}` : "kbd-off",
124
+ props: hasKbd ? { defaultIndex: keyboardActiveIndex } : {},
125
+ }
126
+ }
127
+
128
+ function ChartTooltipContent({
129
+ active,
130
+ payload,
131
+ className,
132
+ indicator = "dot",
133
+ hideLabel = false,
134
+ hideIndicator = false,
135
+ label,
136
+ labelFormatter,
137
+ labelClassName,
138
+ formatter,
139
+ color,
140
+ nameKey,
141
+ labelKey,
142
+ }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
143
+ React.ComponentProps<"div"> & {
144
+ hideLabel?: boolean
145
+ hideIndicator?: boolean
146
+ indicator?: "line" | "dot" | "dashed"
147
+ nameKey?: string
148
+ labelKey?: string
149
+ }) {
150
+ const { config } = useChart()
151
+
152
+ const tooltipLabel = React.useMemo(() => {
153
+ if (hideLabel || !payload?.length) {
154
+ return null
155
+ }
156
+
157
+ const [item] = payload
158
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`
159
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
160
+ const value =
161
+ !labelKey && typeof label === "string"
162
+ ? config[label as keyof typeof config]?.label || label
163
+ : itemConfig?.label
164
+
165
+ if (labelFormatter) {
166
+ return (
167
+ <div className={cn("font-medium", labelClassName)}>
168
+ {labelFormatter(value, payload)}
169
+ </div>
170
+ )
171
+ }
172
+
173
+ if (!value) {
174
+ return null
175
+ }
176
+
177
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
178
+ }, [
179
+ label,
180
+ labelFormatter,
181
+ payload,
182
+ hideLabel,
183
+ labelClassName,
184
+ config,
185
+ labelKey,
186
+ ])
187
+
188
+ if (!active || !payload?.length) {
189
+ return null
190
+ }
191
+
192
+ const nestLabel = payload.length === 1 && indicator !== "dot"
193
+
194
+ return (
195
+ <div
196
+ className={cn(
197
+ "grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
198
+ className
199
+ )}
200
+ >
201
+ {!nestLabel ? tooltipLabel : null}
202
+ <div className="grid gap-1.5">
203
+ {payload
204
+ .filter((item) => item.type !== "none")
205
+ .map((item, index) => {
206
+ const key = `${nameKey || item.name || item.dataKey || "value"}`
207
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
208
+ const indicatorColor = color || item.payload.fill || item.color
209
+
210
+ return (
211
+ <div
212
+ key={item.dataKey}
213
+ className={cn(
214
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
215
+ indicator === "dot" && "items-center"
216
+ )}
217
+ >
218
+ {formatter && item?.value !== undefined && item.name ? (
219
+ formatter(item.value, item.name, item, index, item.payload)
220
+ ) : (
221
+ <>
222
+ {itemConfig?.icon ? (
223
+ <itemConfig.icon />
224
+ ) : (
225
+ !hideIndicator && (
226
+ <div
227
+ className={cn(
228
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
229
+ {
230
+ "h-2.5 w-2.5": indicator === "dot",
231
+ "w-1": indicator === "line",
232
+ "w-0 border-[1.5px] border-dashed bg-transparent":
233
+ indicator === "dashed",
234
+ "my-0.5": nestLabel && indicator === "dashed",
235
+ }
236
+ )}
237
+ style={
238
+ {
239
+ "--color-bg": indicatorColor,
240
+ "--color-border": indicatorColor,
241
+ } as React.CSSProperties
242
+ }
243
+ />
244
+ )
245
+ )}
246
+ <div
247
+ className={cn(
248
+ "flex flex-1 justify-between leading-none",
249
+ nestLabel ? "items-end" : "items-center"
250
+ )}
251
+ >
252
+ <div className="grid gap-1.5">
253
+ {nestLabel ? tooltipLabel : null}
254
+ <span className="text-muted-foreground">
255
+ {itemConfig?.label || item.name}
256
+ </span>
257
+ </div>
258
+ {item.value && (
259
+ <span className="font-mono font-medium text-foreground tabular-nums">
260
+ {item.value.toLocaleString()}
261
+ </span>
262
+ )}
263
+ </div>
264
+ </>
265
+ )}
266
+ </div>
267
+ )
268
+ })}
269
+ </div>
270
+ </div>
271
+ )
272
+ }
273
+
274
+ const ChartLegend = RechartsPrimitive.Legend
275
+
276
+ function ChartLegendContent({
277
+ className,
278
+ hideIcon = false,
279
+ payload,
280
+ verticalAlign = "bottom",
281
+ nameKey,
282
+ }: React.ComponentProps<"div"> &
283
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
284
+ hideIcon?: boolean
285
+ nameKey?: string
286
+ }) {
287
+ const { config } = useChart()
288
+
289
+ if (!payload?.length) {
290
+ return null
291
+ }
292
+
293
+ return (
294
+ <div
295
+ className={cn(
296
+ "flex items-center justify-center gap-4",
297
+ verticalAlign === "top" ? "pb-3" : "pt-3",
298
+ className
299
+ )}
300
+ >
301
+ {payload
302
+ .filter((item) => item.type !== "none")
303
+ .map((item) => {
304
+ const key = `${nameKey || item.dataKey || "value"}`
305
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
306
+
307
+ return (
308
+ <div
309
+ key={item.value}
310
+ className={cn(
311
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
312
+ )}
313
+ >
314
+ {itemConfig?.icon && !hideIcon ? (
315
+ <itemConfig.icon />
316
+ ) : (
317
+ <div
318
+ className="h-2 w-2 shrink-0 rounded-[2px]"
319
+ style={{
320
+ backgroundColor: item.color,
321
+ }}
322
+ />
323
+ )}
324
+ {itemConfig?.label}
325
+ </div>
326
+ )
327
+ })}
328
+ </div>
329
+ )
330
+ }
331
+
332
+ function getPayloadConfigFromPayload(
333
+ config: ChartConfig,
334
+ payload: unknown,
335
+ key: string
336
+ ) {
337
+ if (typeof payload !== "object" || payload === null) {
338
+ return undefined
339
+ }
340
+
341
+ const payloadPayload =
342
+ "payload" in payload &&
343
+ typeof payload.payload === "object" &&
344
+ payload.payload !== null
345
+ ? payload.payload
346
+ : undefined
347
+
348
+ let configLabelKey: string = key
349
+
350
+ if (
351
+ key in payload &&
352
+ typeof payload[key as keyof typeof payload] === "string"
353
+ ) {
354
+ configLabelKey = payload[key as keyof typeof payload] as string
355
+ } else if (
356
+ payloadPayload &&
357
+ key in payloadPayload &&
358
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
359
+ ) {
360
+ configLabelKey = payloadPayload[
361
+ key as keyof typeof payloadPayload
362
+ ] as string
363
+ }
364
+
365
+ return configLabelKey in config
366
+ ? config[configLabelKey]
367
+ : config[key as keyof typeof config]
368
+ }
369
+
370
+ export {
371
+ ChartContainer,
372
+ ChartTooltip,
373
+ chartTooltipKeyboardSyncProps,
374
+ ChartTooltipContent,
375
+ ChartLegend,
376
+ ChartLegendContent,
377
+ ChartStyle,
378
+ }
@@ -0,0 +1,160 @@
1
+ "use client"
2
+
3
+ /**
4
+ * Checkbox — Radix control + Exxat styling.
5
+ *
6
+ * Aligned with Shadcn Studio checkbox guidance (see shadcnstudio.com/docs/components/checkbox):
7
+ * • Tri-state: `checked={true | false | "indeterminate"}` (parent “select some” rows)
8
+ * • Visual variants: default, outline, secondary, success, destructive, warning, muted
9
+ * • Sizes: sm, default, lg
10
+ * • Motion: none | pop | glow | pop-glow — uses `motion-safe` / `motion-reduce` (WCAG 2.3.3)
11
+ * • Forms: `aria-invalid` from Field, `disabled`, extended hit slop (`after:`)
12
+ * • Pair with `CheckboxLabel` + `htmlFor` / `id` for 44px-friendly targets
13
+ */
14
+
15
+ import * as React from "react"
16
+ import { cva, type VariantProps } from "class-variance-authority"
17
+ import { Checkbox as CheckboxPrimitive } from "radix-ui"
18
+
19
+ import { cn } from "../../lib/utils"
20
+ import { Label } from "./label"
21
+
22
+ const checkboxVariants = cva(
23
+ [
24
+ "peer relative box-border flex shrink-0 items-center justify-center self-center rounded-[4px] border border-input",
25
+ "outline-none transition-[color,box-shadow,transform,background-color,border-color] duration-150",
26
+ "motion-reduce:transition-none",
27
+ "group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2",
28
+ "focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50",
29
+ "disabled:cursor-not-allowed disabled:opacity-50",
30
+ "aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary",
31
+ "dark:bg-input/15 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
32
+ ].join(" "),
33
+ {
34
+ variants: {
35
+ variant: {
36
+ default: [
37
+ "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary",
38
+ "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=indeterminate]:bg-primary dark:data-[state=indeterminate]:text-primary-foreground",
39
+ ].join(" "),
40
+ outline: [
41
+ "bg-background",
42
+ "data-[state=checked]:border-primary data-[state=checked]:bg-background data-[state=checked]:text-primary data-[state=checked]:ring-2 data-[state=checked]:ring-primary/25",
43
+ "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-background data-[state=indeterminate]:text-primary data-[state=indeterminate]:ring-2 data-[state=indeterminate]:ring-primary/25",
44
+ ].join(" "),
45
+ secondary: [
46
+ "data-[state=checked]:border-secondary data-[state=checked]:bg-secondary data-[state=checked]:text-secondary-foreground",
47
+ "data-[state=indeterminate]:border-secondary data-[state=indeterminate]:bg-secondary data-[state=indeterminate]:text-secondary-foreground",
48
+ ].join(" "),
49
+ success: [
50
+ "data-[state=checked]:border-chart-2 data-[state=checked]:bg-chart-2 data-[state=checked]:text-primary-foreground",
51
+ "data-[state=indeterminate]:border-chart-2 data-[state=indeterminate]:bg-chart-2 data-[state=indeterminate]:text-primary-foreground",
52
+ ].join(" "),
53
+ destructive: [
54
+ "data-[state=checked]:border-destructive data-[state=checked]:bg-destructive data-[state=checked]:text-destructive-foreground",
55
+ "data-[state=indeterminate]:border-destructive data-[state=indeterminate]:bg-destructive data-[state=indeterminate]:text-destructive-foreground",
56
+ ].join(" "),
57
+ warning: [
58
+ "data-[state=checked]:border-amber-500 data-[state=checked]:bg-amber-500 data-[state=checked]:text-amber-950",
59
+ "data-[state=indeterminate]:border-amber-500 data-[state=indeterminate]:bg-amber-500 data-[state=indeterminate]:text-amber-950",
60
+ ].join(" "),
61
+ muted: [
62
+ "data-[state=checked]:border-muted-foreground/50 data-[state=checked]:bg-muted data-[state=checked]:text-foreground",
63
+ "data-[state=indeterminate]:border-muted-foreground/50 data-[state=indeterminate]:bg-muted data-[state=indeterminate]:text-foreground",
64
+ ].join(" "),
65
+ },
66
+ size: {
67
+ sm: "size-3.5 max-h-3.5 max-w-3.5 min-h-3.5 min-w-3.5 rounded-[3px] [&_[data-slot=checkbox-indicator]_i]:text-[10px]",
68
+ default: "size-4 max-h-4 max-w-4 min-h-4 min-w-4 [&_[data-slot=checkbox-indicator]_i]:text-xs",
69
+ lg: "size-5 max-h-5 max-w-5 min-h-5 min-w-5 rounded-[5px] [&_[data-slot=checkbox-indicator]_i]:text-sm",
70
+ },
71
+ motion: {
72
+ none: "",
73
+ pop: [
74
+ "motion-safe:active:scale-95",
75
+ "data-[state=checked]:motion-safe:scale-[1.04] data-[state=indeterminate]:motion-safe:scale-[1.04]",
76
+ ].join(" "),
77
+ glow: [
78
+ "data-[state=checked]:shadow-[0_0_0_3px] data-[state=checked]:shadow-primary/35",
79
+ "data-[state=indeterminate]:shadow-[0_0_0_3px] data-[state=indeterminate]:shadow-primary/35",
80
+ ].join(" "),
81
+ "pop-glow": [
82
+ "motion-safe:active:scale-95",
83
+ "data-[state=checked]:motion-safe:scale-[1.04] data-[state=indeterminate]:motion-safe:scale-[1.04]",
84
+ "data-[state=checked]:shadow-[0_0_0_3px] data-[state=checked]:shadow-primary/35",
85
+ "data-[state=indeterminate]:shadow-[0_0_0_3px] data-[state=indeterminate]:shadow-primary/35",
86
+ ].join(" "),
87
+ },
88
+ },
89
+ defaultVariants: {
90
+ variant: "default",
91
+ size: "default",
92
+ motion: "none",
93
+ },
94
+ }
95
+ )
96
+
97
+ const checkboxIndicatorVariants = cva("grid place-content-center text-current", {
98
+ variants: {
99
+ motion: {
100
+ none: "transition-none",
101
+ pop: "motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:duration-150",
102
+ glow: "",
103
+ "pop-glow":
104
+ "motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:duration-150",
105
+ },
106
+ },
107
+ defaultVariants: {
108
+ motion: "none",
109
+ },
110
+ })
111
+
112
+ export type CheckboxProps = React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> &
113
+ VariantProps<typeof checkboxVariants>
114
+
115
+ const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
116
+ function Checkbox({ className, variant, size, motion, checked, ...props }, ref) {
117
+ const m = motion ?? "none"
118
+ return (
119
+ <CheckboxPrimitive.Root
120
+ ref={ref}
121
+ data-slot="checkbox"
122
+ data-variant={variant ?? "default"}
123
+ data-motion={m}
124
+ checked={checked}
125
+ className={cn(checkboxVariants({ variant, size, motion: m }), className)}
126
+ {...props}
127
+ >
128
+ <CheckboxPrimitive.Indicator
129
+ data-slot="checkbox-indicator"
130
+ className={checkboxIndicatorVariants({ motion: m })}
131
+ >
132
+ {checked === "indeterminate" ? (
133
+ <i className="fa-solid fa-minus text-current" aria-hidden="true" />
134
+ ) : (
135
+ <i className="fa-solid fa-check text-current" aria-hidden="true" />
136
+ )}
137
+ </CheckboxPrimitive.Indicator>
138
+ </CheckboxPrimitive.Root>
139
+ )
140
+ }
141
+ )
142
+
143
+ export type CheckboxLabelProps = React.ComponentPropsWithRef<typeof Label>
144
+
145
+ /** Label tuned for `Checkbox` siblings: larger tap target, respects `peer-disabled`. */
146
+ function CheckboxLabel({ className, ...props }: CheckboxLabelProps) {
147
+ return (
148
+ <Label
149
+ data-slot="checkbox-label"
150
+ className={cn(
151
+ "inline-flex min-h-11 cursor-pointer select-none items-center gap-2 py-1 -my-1 text-sm font-medium leading-snug",
152
+ "peer-disabled:cursor-not-allowed peer-disabled:opacity-60",
153
+ className
154
+ )}
155
+ {...props}
156
+ />
157
+ )
158
+ }
159
+
160
+ export { Checkbox, CheckboxLabel, checkboxVariants }