@boxcustodia/library 2.0.0-alpha.13 → 2.0.0-alpha.14
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.
- package/dist/index.cjs.js +1 -138
- package/dist/index.d.ts +1083 -715
- package/dist/index.es.js +7077 -56175
- package/dist/theme.css +1 -1
- package/package.json +34 -26
- package/src/__doc__/Examples.tsx +1 -1
- package/src/__doc__/Intro.mdx +3 -3
- package/src/__doc__/Tabs.mdx +112 -0
- package/src/__doc__/V2.mdx +1246 -0
- package/src/components/accordion/accordion.stories.tsx +143 -0
- package/src/components/accordion/accordion.tsx +135 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/alert/alert.stories.tsx +24 -4
- package/src/components/alert/alert.tsx +17 -9
- package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
- package/src/components/alert-dialog/alert-dialog.tsx +58 -10
- package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
- package/src/components/auto-complete/auto-complete.tsx +420 -68
- package/src/components/auto-complete/index.ts +0 -1
- package/src/components/avatar/avatar.stories.tsx +162 -21
- package/src/components/avatar/avatar.tsx +79 -20
- package/src/components/button/button.stories.tsx +219 -294
- package/src/components/button/button.test.tsx +10 -17
- package/src/components/button/button.tsx +78 -19
- package/src/components/button/components/base-button.tsx +30 -53
- package/src/components/button/index.ts +0 -1
- package/src/components/calendar/calendar.stories.tsx +1 -1
- package/src/components/calendar/calendar.tsx +4 -4
- package/src/components/card/card.stories.tsx +141 -69
- package/src/components/card/card.tsx +155 -54
- package/src/components/center/center.stories.tsx +22 -39
- package/src/components/checkbox/checkbox.stories.tsx +25 -5
- package/src/components/checkbox/checkbox.tsx +76 -15
- package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
- package/src/components/checkbox-group/checkbox-group.tsx +84 -3
- package/src/components/combobox/combobox.stories.tsx +33 -23
- package/src/components/combobox/combobox.tsx +119 -103
- package/src/components/date-picker/date-input.stories.tsx +14 -6
- package/src/components/date-picker/date-input.tsx +2 -2
- package/src/components/date-picker/date-picker.model.ts +13 -4
- package/src/components/date-picker/date-picker.stories.tsx +38 -12
- package/src/components/date-picker/date-picker.tsx +28 -14
- package/src/components/dialog/dialog.stories.tsx +18 -0
- package/src/components/dialog/dialog.test.tsx +1 -1
- package/src/components/dialog/dialog.tsx +51 -20
- package/src/components/divider/divider.stories.tsx +6 -0
- package/src/components/dropzone/dropzone.stories.tsx +71 -90
- package/src/components/dropzone/dropzone.tsx +383 -105
- package/src/components/dropzone/index.ts +0 -1
- package/src/components/empty/empty.stories.tsx +165 -0
- package/src/components/empty/empty.tsx +156 -0
- package/src/components/empty/index.ts +1 -0
- package/src/components/field/field.stories.tsx +226 -3
- package/src/components/field/field.tsx +77 -42
- package/src/components/form/form.stories.tsx +320 -197
- package/src/components/form/form.tsx +3 -23
- package/src/components/index.ts +2 -6
- package/src/components/input/input.stories.tsx +5 -5
- package/src/components/input/input.tsx +4 -4
- package/src/components/kbd/kbd.stories.tsx +1 -0
- package/src/components/label/label.stories.tsx +16 -0
- package/src/components/label/label.tsx +13 -2
- package/src/components/loader/loader.stories.tsx +7 -5
- package/src/components/loader/loader.tsx +8 -3
- package/src/components/menu/menu-primitives.tsx +207 -196
- package/src/components/menu/menu.stories.tsx +276 -146
- package/src/components/menu/menu.tsx +146 -54
- package/src/components/number-input/number-input.stories.tsx +27 -4
- package/src/components/number-input/number-input.test.tsx +2 -2
- package/src/components/number-input/number-input.tsx +25 -29
- package/src/components/otp/index.ts +1 -0
- package/src/components/otp/otp.stories.tsx +209 -0
- package/src/components/otp/otp.tsx +100 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.model.ts +2 -0
- package/src/components/pagination/pagination.stories.tsx +154 -59
- package/src/components/pagination/pagination.test.tsx +122 -57
- package/src/components/pagination/pagination.tsx +575 -77
- package/src/components/password/password.stories.tsx +18 -3
- package/src/components/password/password.tsx +26 -10
- package/src/components/popover/popover.stories.tsx +26 -5
- package/src/components/popover/popover.tsx +15 -23
- package/src/components/progress/progress.stories.tsx +1 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +251 -0
- package/src/components/radio-group/radio-group.tsx +212 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
- package/src/components/select/select.stories.tsx +118 -19
- package/src/components/select/select.tsx +67 -62
- package/src/components/skeleton/skeleton.stories.tsx +1 -0
- package/src/components/stack/stack.stories.tsx +179 -89
- package/src/components/stack/stack.tsx +2 -2
- package/src/components/stepper/index.ts +1 -1
- package/src/components/stepper/stepper.stories.tsx +767 -83
- package/src/components/stepper/stepper.test.tsx +18 -18
- package/src/components/stepper/stepper.tsx +554 -0
- package/src/components/switch/switch.stories.tsx +15 -1
- package/src/components/switch/switch.tsx +17 -4
- package/src/components/table/index.ts +0 -2
- package/src/components/table/table.stories.tsx +131 -18
- package/src/components/table/table.test.tsx +1 -1
- package/src/components/table/table.tsx +183 -77
- package/src/components/tabs/tabs.stories.tsx +373 -155
- package/src/components/tabs/tabs.test.tsx +12 -12
- package/src/components/tabs/tabs.tsx +72 -149
- package/src/components/tag/index.ts +0 -1
- package/src/components/tag/tag.stories.tsx +155 -120
- package/src/components/tag/tag.tsx +47 -95
- package/src/components/textarea/textarea.stories.tsx +8 -22
- package/src/components/textarea/textarea.tsx +17 -79
- package/src/components/timeline/timeline.stories.tsx +323 -42
- package/src/components/timeline/timeline.tsx +359 -132
- package/src/components/toast/toast.stories.tsx +1 -0
- package/src/components/tooltip/tooltip.tsx +11 -9
- package/src/components/tree/index.ts +0 -1
- package/src/components/tree/tree.stories.tsx +365 -408
- package/src/components/tree/tree.test.tsx +163 -0
- package/src/components/tree/tree.tsx +212 -36
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
- package/src/hooks/usePagination/usePagination.tsx +36 -24
- package/src/styles/theme.css +1 -1
- package/src/utils/form.tsx +67 -37
- package/src/utils/index.ts +1 -1
- package/src/__doc__/Migration.mdx +0 -451
- package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
- package/src/components/background-image/background-image.stories.tsx +0 -21
- package/src/components/background-image/background-image.test.tsx +0 -29
- package/src/components/background-image/background-image.tsx +0 -23
- package/src/components/background-image/index.ts +0 -1
- package/src/components/button/button.variants.ts +0 -44
- package/src/components/button/components/loader-overlay.tsx +0 -21
- package/src/components/button/components/loading-icon.tsx +0 -47
- package/src/components/dropzone/upload-primitives.tsx +0 -310
- package/src/components/dropzone/use-dropzone.ts +0 -122
- package/src/components/empty-state/empty-state.stories.tsx +0 -56
- package/src/components/empty-state/empty-state.tsx +0 -39
- package/src/components/empty-state/index.ts +0 -1
- package/src/components/heading/heading.stories.tsx +0 -74
- package/src/components/heading/heading.tsx +0 -28
- package/src/components/heading/heading.variants.ts +0 -27
- package/src/components/heading/index.ts +0 -1
- package/src/components/kbd/kbd.variants.ts +0 -26
- package/src/components/menu/util/render-menu-item.tsx +0 -54
- package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
- package/src/components/multi-select/index.ts +0 -1
- package/src/components/multi-select/multi-select.stories.tsx +0 -294
- package/src/components/multi-select/multi-select.tsx +0 -300
- package/src/components/multi-select/multi-select.variants.ts +0 -22
- package/src/components/pagination/components/pagination-option.tsx +0 -27
- package/src/components/show/index.ts +0 -1
- package/src/components/show/show.stories.tsx +0 -197
- package/src/components/show/show.test.tsx +0 -41
- package/src/components/show/show.tsx +0 -16
- package/src/components/stepper/Stepper.tsx +0 -190
- package/src/components/stepper/context/stepper-context.tsx +0 -11
- package/src/components/table/table-primitives.tsx +0 -122
- package/src/components/table/table.model.ts +0 -20
- package/src/components/table-pagination/index.ts +0 -2
- package/src/components/table-pagination/table-pagination.model.ts +0 -2
- package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
- package/src/components/table-pagination/table-pagination.test.tsx +0 -32
- package/src/components/table-pagination/table-pagination.tsx +0 -108
- package/src/components/tabs/context/tabs-context.tsx +0 -14
- package/src/components/tag/tag.variants.ts +0 -31
- package/src/components/timeline/timeline-status.ts +0 -5
- package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
- package/src/components/tree/tree-primitives.tsx +0 -126
|
@@ -1,74 +1,175 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { cn } from "../../lib";
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export function CardRoot({
|
|
5
5
|
className,
|
|
6
|
+
size = "default",
|
|
6
7
|
...props
|
|
7
|
-
}: React.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="card"
|
|
12
|
+
data-size={size}
|
|
13
|
+
className={cn(
|
|
14
|
+
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
export
|
|
22
|
+
export function CardHeader({
|
|
19
23
|
className,
|
|
20
24
|
...props
|
|
21
|
-
}: React.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
}: React.ComponentProps<"div">) {
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
data-slot="card-header"
|
|
29
|
+
className={cn(
|
|
30
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
28
37
|
|
|
29
|
-
export
|
|
38
|
+
export function CardTitle({
|
|
30
39
|
className,
|
|
31
40
|
...props
|
|
32
|
-
}: React.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
className
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
}: React.ComponentProps<"div">) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-slot="card-title"
|
|
45
|
+
className={cn(
|
|
46
|
+
"cn-font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
|
|
47
|
+
className,
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
42
53
|
|
|
43
|
-
export
|
|
54
|
+
export function CardDescription({
|
|
44
55
|
className,
|
|
45
56
|
...props
|
|
46
|
-
}: React.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
}: React.ComponentProps<"div">) {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
data-slot="card-description"
|
|
61
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
53
66
|
|
|
54
|
-
export
|
|
67
|
+
export function CardAction({
|
|
55
68
|
className,
|
|
56
69
|
...props
|
|
57
|
-
}: React.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
}: React.ComponentProps<"div">) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
data-slot="card-action"
|
|
74
|
+
className={cn(
|
|
75
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
76
|
+
className,
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
64
82
|
|
|
65
|
-
export
|
|
83
|
+
export function CardContent({
|
|
66
84
|
className,
|
|
67
85
|
...props
|
|
68
|
-
}: React.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
}: React.ComponentProps<"div">) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
data-slot="card-content"
|
|
90
|
+
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
|
|
91
|
+
{...props}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function CardFooter({
|
|
97
|
+
className,
|
|
98
|
+
...props
|
|
99
|
+
}: React.ComponentProps<"div">) {
|
|
100
|
+
return (
|
|
101
|
+
<div
|
|
102
|
+
data-slot="card-footer"
|
|
103
|
+
className={cn(
|
|
104
|
+
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
|
|
105
|
+
className,
|
|
106
|
+
)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface CardProps
|
|
113
|
+
extends Omit<React.ComponentProps<"div">, "children" | "title"> {
|
|
114
|
+
title?: React.ReactNode;
|
|
115
|
+
description?: React.ReactNode;
|
|
116
|
+
action?: React.ReactNode;
|
|
117
|
+
footer?: React.ReactNode;
|
|
118
|
+
children?: React.ReactNode;
|
|
119
|
+
size?: "default" | "sm";
|
|
120
|
+
/** Styles applied to each internal slot. */
|
|
121
|
+
classNames?: {
|
|
122
|
+
/** Header wrapper holding title, description, and action. */
|
|
123
|
+
header?: string;
|
|
124
|
+
/** Card title text. */
|
|
125
|
+
title?: string;
|
|
126
|
+
/** Supporting description below the title. */
|
|
127
|
+
description?: string;
|
|
128
|
+
/** Action area on the right of the header. */
|
|
129
|
+
action?: string;
|
|
130
|
+
/** Body content rendered between the header and the footer. */
|
|
131
|
+
content?: string;
|
|
132
|
+
/** Footer area below the content. */
|
|
133
|
+
footer?: string;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function Card({
|
|
138
|
+
title,
|
|
139
|
+
description,
|
|
140
|
+
action,
|
|
141
|
+
footer,
|
|
142
|
+
children,
|
|
143
|
+
size,
|
|
144
|
+
className,
|
|
145
|
+
classNames,
|
|
146
|
+
...props
|
|
147
|
+
}: CardProps) {
|
|
148
|
+
const hasHeader = title || description || action;
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<CardRoot size={size} className={className} {...props}>
|
|
152
|
+
{hasHeader && (
|
|
153
|
+
<CardHeader className={classNames?.header}>
|
|
154
|
+
{title && (
|
|
155
|
+
<CardTitle className={classNames?.title}>{title}</CardTitle>
|
|
156
|
+
)}
|
|
157
|
+
{description && (
|
|
158
|
+
<CardDescription className={classNames?.description}>
|
|
159
|
+
{description}
|
|
160
|
+
</CardDescription>
|
|
161
|
+
)}
|
|
162
|
+
{action && (
|
|
163
|
+
<CardAction className={classNames?.action}>{action}</CardAction>
|
|
164
|
+
)}
|
|
165
|
+
</CardHeader>
|
|
166
|
+
)}
|
|
167
|
+
{children && (
|
|
168
|
+
<CardContent className={classNames?.content}>{children}</CardContent>
|
|
169
|
+
)}
|
|
170
|
+
{footer && (
|
|
171
|
+
<CardFooter className={classNames?.footer}>{footer}</CardFooter>
|
|
172
|
+
)}
|
|
173
|
+
</CardRoot>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Inbox, Phone } from "lucide-react";
|
|
3
|
-
import { Stack } from "../stack";
|
|
4
2
|
import { Center } from ".";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
|
-
* Layout primitive that centers its
|
|
8
|
-
*
|
|
5
|
+
* Layout primitive that centers its single child on both axes via flexbox.
|
|
6
|
+
* Defaults to `display: flex`. `inline` switches to `inline-flex` for inline contexts.
|
|
7
|
+
* `as` renders any HTML element for semantic markup.
|
|
9
8
|
*/
|
|
10
9
|
const meta: Meta<typeof Center> = {
|
|
11
10
|
title: "Components/Center",
|
|
12
11
|
component: Center,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: "centered",
|
|
14
|
+
},
|
|
13
15
|
argTypes: {
|
|
14
16
|
children: { control: false },
|
|
15
17
|
className: { control: false },
|
|
18
|
+
as: { control: false },
|
|
16
19
|
},
|
|
17
20
|
};
|
|
18
21
|
|
|
@@ -21,61 +24,41 @@ type Story = StoryObj<typeof Center>;
|
|
|
21
24
|
|
|
22
25
|
export const Default: Story = {
|
|
23
26
|
render: () => (
|
|
24
|
-
<Center className="h-
|
|
25
|
-
<
|
|
26
|
-
direction="vertical"
|
|
27
|
-
align="center"
|
|
28
|
-
gap={8}
|
|
29
|
-
className="text-muted-foreground"
|
|
30
|
-
>
|
|
31
|
-
<Inbox className="size-10 stroke-1" />
|
|
32
|
-
<span className="text-sm font-medium">No results found</span>
|
|
33
|
-
<span className="text-xs">Try adjusting your search or filters</span>
|
|
34
|
-
</Stack>
|
|
35
|
-
</Center>
|
|
36
|
-
),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const Icon: Story = {
|
|
40
|
-
render: () => (
|
|
41
|
-
<Center className="w-fit p-4 bg-primary text-white rounded-full">
|
|
42
|
-
<Phone />
|
|
27
|
+
<Center className="h-48 w-72 border border-dashed rounded-lg">
|
|
28
|
+
<span className="text-sm text-muted-foreground">Centered</span>
|
|
43
29
|
</Center>
|
|
44
30
|
),
|
|
45
31
|
};
|
|
46
32
|
|
|
47
33
|
/**
|
|
48
|
-
* `inline`
|
|
49
|
-
*
|
|
34
|
+
* `inline` swaps `flex` for `inline-flex` so the Center flows with surrounding inline content
|
|
35
|
+
* instead of starting a new block. Useful for badges, counters, and icon chips inside text.
|
|
50
36
|
*/
|
|
51
37
|
export const Inline: Story = {
|
|
52
38
|
render: () => (
|
|
53
|
-
<p className="text-sm">
|
|
54
|
-
|
|
39
|
+
<p className="text-sm max-w-sm">
|
|
40
|
+
You have{" "}
|
|
55
41
|
<Center
|
|
56
42
|
inline
|
|
57
|
-
className="w-
|
|
43
|
+
className="w-6 h-6 bg-primary text-primary-foreground rounded-full text-xs font-medium"
|
|
58
44
|
>
|
|
59
|
-
|
|
45
|
+
3
|
|
60
46
|
</Center>{" "}
|
|
61
|
-
|
|
47
|
+
unread messages in your inbox.
|
|
62
48
|
</p>
|
|
63
49
|
),
|
|
64
50
|
};
|
|
65
51
|
|
|
66
52
|
/**
|
|
67
|
-
* `as`
|
|
68
|
-
*
|
|
53
|
+
* `as` swaps the rendered element while keeping the centering behavior.
|
|
54
|
+
* Pick the tag that matches the role: `main`, `section`, `header`, `footer`.
|
|
69
55
|
*/
|
|
70
56
|
export const As: Story = {
|
|
71
57
|
render: () => (
|
|
72
|
-
<Center
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<div className="bg-primary text-primary-foreground rounded p-4">
|
|
77
|
-
Rendered as <main>
|
|
78
|
-
</div>
|
|
58
|
+
<Center as="section" className="h-48 w-72 bg-muted rounded-lg">
|
|
59
|
+
<code className="text-xs px-2 py-1 bg-background rounded">
|
|
60
|
+
<section>
|
|
61
|
+
</code>
|
|
79
62
|
</Center>
|
|
80
63
|
),
|
|
81
64
|
};
|
|
@@ -32,7 +32,7 @@ const meta: Meta<typeof Checkbox> = {
|
|
|
32
32
|
},
|
|
33
33
|
argTypes: {
|
|
34
34
|
onCheckedChange: { control: false },
|
|
35
|
-
|
|
35
|
+
classNames: { control: false },
|
|
36
36
|
tooltip: { control: false },
|
|
37
37
|
},
|
|
38
38
|
};
|
|
@@ -42,6 +42,22 @@ type Story = StoryObj<typeof Checkbox>;
|
|
|
42
42
|
|
|
43
43
|
export const Default: Story = {};
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* `className` styles the checkbox root. `classNames` exposes the
|
|
47
|
+
* `wrapper`, `indicator`, and `label` slots — the `wrapper` slot only
|
|
48
|
+
* applies when a `label` is rendered.
|
|
49
|
+
*/
|
|
50
|
+
export const WithClassNames: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
label: "Custom styled",
|
|
53
|
+
classNames: {
|
|
54
|
+
wrapper: "gap-3",
|
|
55
|
+
indicator: "data-checked:bg-emerald-500",
|
|
56
|
+
label: "text-emerald-700 font-medium",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
45
61
|
/**
|
|
46
62
|
* Pass `label` for a linked `<label>` element. `children` works as an alias —
|
|
47
63
|
* `label` takes priority when both are provided.
|
|
@@ -179,7 +195,7 @@ export const Group: Story = {
|
|
|
179
195
|
<Checkbox.Group
|
|
180
196
|
legend="Notifications"
|
|
181
197
|
defaultValue={["email"]}
|
|
182
|
-
|
|
198
|
+
onValueChange={action("onValueChange")}
|
|
183
199
|
>
|
|
184
200
|
<Checkbox.Item value="email" label="Email" />
|
|
185
201
|
<Checkbox.Item value="sms" label="SMS" />
|
|
@@ -227,7 +243,11 @@ export const GroupParent: Story = {
|
|
|
227
243
|
onCheckedChange={(checked) => setValue(checked ? ALL : [])}
|
|
228
244
|
/>
|
|
229
245
|
<div className="ml-6 border-l-2 pl-4">
|
|
230
|
-
<Checkbox.Group
|
|
246
|
+
<Checkbox.Group
|
|
247
|
+
value={value}
|
|
248
|
+
onValueChange={setValue}
|
|
249
|
+
allValues={ALL}
|
|
250
|
+
>
|
|
231
251
|
<Checkbox.Item value="email" label="Email" />
|
|
232
252
|
<Checkbox.Item value="sms" label="SMS" />
|
|
233
253
|
<Checkbox.Item value="push" label="Push notifications" />
|
|
@@ -239,7 +259,7 @@ export const GroupParent: Story = {
|
|
|
239
259
|
};
|
|
240
260
|
|
|
241
261
|
/**
|
|
242
|
-
* Use `value` + `
|
|
262
|
+
* Use `value` + `onValueChange` for a controlled group.
|
|
243
263
|
*/
|
|
244
264
|
export const GroupControlled: Story = {
|
|
245
265
|
render: () => {
|
|
@@ -248,7 +268,7 @@ export const GroupControlled: Story = {
|
|
|
248
268
|
<Checkbox.Group
|
|
249
269
|
legend="Contact channel"
|
|
250
270
|
value={value}
|
|
251
|
-
|
|
271
|
+
onValueChange={setValue}
|
|
252
272
|
>
|
|
253
273
|
<Checkbox.Item value="email" label="Email" />
|
|
254
274
|
<Checkbox.Item value="sms" label="SMS" />
|
|
@@ -92,7 +92,7 @@ export function CheckboxIndicator({
|
|
|
92
92
|
|
|
93
93
|
// ── Group context ──────────────────────────────────────────────────────────────
|
|
94
94
|
|
|
95
|
-
const CheckboxGroupContext = createContext<{
|
|
95
|
+
export const CheckboxGroupContext = createContext<{
|
|
96
96
|
controlFirst: boolean;
|
|
97
97
|
invalid: boolean;
|
|
98
98
|
}>({
|
|
@@ -106,13 +106,21 @@ interface CheckboxProps extends Omit<CheckboxPrimitive.Root.Props, "children"> {
|
|
|
106
106
|
children?: ReactNode;
|
|
107
107
|
label?: ReactNode;
|
|
108
108
|
tooltip?: ReactNode;
|
|
109
|
-
indicatorProps?: CheckboxPrimitive.Indicator.Props;
|
|
110
109
|
controlFirst?: boolean;
|
|
110
|
+
/** Styles applied to each internal slot. */
|
|
111
|
+
classNames?: {
|
|
112
|
+
/** Flex wrapper around checkbox and label (only rendered when a label is provided). */
|
|
113
|
+
wrapper?: string;
|
|
114
|
+
/** Checkmark indicator inside the checkbox. */
|
|
115
|
+
indicator?: string;
|
|
116
|
+
/** Label element next to the checkbox. */
|
|
117
|
+
label?: string;
|
|
118
|
+
};
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
function CheckboxSingle({
|
|
114
122
|
className,
|
|
115
|
-
|
|
123
|
+
classNames,
|
|
116
124
|
label,
|
|
117
125
|
children,
|
|
118
126
|
tooltip,
|
|
@@ -132,7 +140,7 @@ function CheckboxSingle({
|
|
|
132
140
|
required={required}
|
|
133
141
|
{...props}
|
|
134
142
|
>
|
|
135
|
-
<CheckboxIndicator {
|
|
143
|
+
<CheckboxIndicator className={classNames?.indicator} />
|
|
136
144
|
</CheckboxRoot>
|
|
137
145
|
);
|
|
138
146
|
|
|
@@ -142,10 +150,16 @@ function CheckboxSingle({
|
|
|
142
150
|
className={cn(
|
|
143
151
|
"flex select-none items-center gap-x-2",
|
|
144
152
|
!controlFirst && "flex-row-reverse justify-end",
|
|
153
|
+
classNames?.wrapper,
|
|
145
154
|
)}
|
|
146
155
|
>
|
|
147
156
|
{checkbox}
|
|
148
|
-
<Label
|
|
157
|
+
<Label
|
|
158
|
+
htmlFor={idToUse}
|
|
159
|
+
required={required}
|
|
160
|
+
tooltip={tooltip}
|
|
161
|
+
className={classNames?.label}
|
|
162
|
+
>
|
|
149
163
|
{labelContent}
|
|
150
164
|
</Label>
|
|
151
165
|
</div>
|
|
@@ -160,10 +174,20 @@ function CheckboxSingle({
|
|
|
160
174
|
export interface CheckboxItemProps
|
|
161
175
|
extends Omit<CheckboxPrimitive.Root.Props, "children"> {
|
|
162
176
|
label: ReactNode;
|
|
177
|
+
/** Styles applied to each internal slot. */
|
|
178
|
+
classNames?: {
|
|
179
|
+
/** Flex wrapper around checkbox and label. */
|
|
180
|
+
wrapper?: string;
|
|
181
|
+
/** Checkmark indicator inside the checkbox. */
|
|
182
|
+
indicator?: string;
|
|
183
|
+
/** Label element next to the checkbox. */
|
|
184
|
+
label?: string;
|
|
185
|
+
};
|
|
163
186
|
}
|
|
164
187
|
|
|
165
188
|
function CheckboxItem({
|
|
166
189
|
className,
|
|
190
|
+
classNames,
|
|
167
191
|
label,
|
|
168
192
|
id,
|
|
169
193
|
...props
|
|
@@ -178,12 +202,15 @@ function CheckboxItem({
|
|
|
178
202
|
className={cn(
|
|
179
203
|
"flex select-none items-center gap-x-2",
|
|
180
204
|
!controlFirst && "flex-row-reverse justify-end",
|
|
205
|
+
classNames?.wrapper,
|
|
181
206
|
)}
|
|
182
207
|
>
|
|
183
208
|
<CheckboxRoot id={idToUse} className={className} {...props}>
|
|
184
|
-
<CheckboxIndicator />
|
|
209
|
+
<CheckboxIndicator className={classNames?.indicator} />
|
|
185
210
|
</CheckboxRoot>
|
|
186
|
-
<Label htmlFor={idToUse}
|
|
211
|
+
<Label htmlFor={idToUse} className={classNames?.label}>
|
|
212
|
+
{label}
|
|
213
|
+
</Label>
|
|
187
214
|
</div>
|
|
188
215
|
</FieldBase.Root>
|
|
189
216
|
);
|
|
@@ -211,18 +238,29 @@ function CheckboxLegend({
|
|
|
211
238
|
|
|
212
239
|
// ── Composite: Checkbox.Group ──────────────────────────────────────────────────
|
|
213
240
|
|
|
214
|
-
|
|
241
|
+
interface CheckboxGroupProps {
|
|
215
242
|
legend?: ReactNode;
|
|
216
243
|
children: ReactNode;
|
|
217
244
|
error?: string;
|
|
218
245
|
description?: ReactNode;
|
|
219
246
|
defaultValue?: string[];
|
|
220
247
|
value?: string[];
|
|
221
|
-
|
|
248
|
+
onValueChange?: (value: string[]) => void;
|
|
222
249
|
allValues?: string[];
|
|
223
250
|
disabled?: boolean;
|
|
224
251
|
controlFirst?: boolean;
|
|
225
252
|
className?: string;
|
|
253
|
+
/** Styles applied to each internal slot. */
|
|
254
|
+
classNames?: {
|
|
255
|
+
/** Legend element above the items. */
|
|
256
|
+
legend?: string;
|
|
257
|
+
/** Wrapper around the rendered children (items). */
|
|
258
|
+
items?: string;
|
|
259
|
+
/** Description shown below the items when there is no error. */
|
|
260
|
+
description?: string;
|
|
261
|
+
/** Error message shown below the items. */
|
|
262
|
+
error?: string;
|
|
263
|
+
};
|
|
226
264
|
}
|
|
227
265
|
|
|
228
266
|
function CheckboxGroup({
|
|
@@ -232,27 +270,50 @@ function CheckboxGroup({
|
|
|
232
270
|
description,
|
|
233
271
|
defaultValue,
|
|
234
272
|
value,
|
|
235
|
-
|
|
273
|
+
onValueChange,
|
|
236
274
|
allValues,
|
|
237
275
|
disabled,
|
|
238
276
|
controlFirst = true,
|
|
239
277
|
className,
|
|
278
|
+
classNames,
|
|
240
279
|
}: CheckboxGroupProps): React.ReactElement {
|
|
241
280
|
return (
|
|
242
281
|
<CheckboxGroupContext.Provider value={{ controlFirst, invalid: !!error }}>
|
|
243
282
|
<CheckboxGroupPrimitive
|
|
244
283
|
defaultValue={defaultValue}
|
|
245
284
|
value={value}
|
|
246
|
-
onValueChange={
|
|
285
|
+
onValueChange={onValueChange}
|
|
247
286
|
allValues={allValues}
|
|
248
287
|
disabled={disabled}
|
|
249
288
|
>
|
|
250
289
|
<Fieldset.Root className={cn("flex flex-col gap-3", className)}>
|
|
251
|
-
{legend &&
|
|
252
|
-
|
|
253
|
-
|
|
290
|
+
{legend && (
|
|
291
|
+
<CheckboxLegend className={classNames?.legend}>
|
|
292
|
+
{legend}
|
|
293
|
+
</CheckboxLegend>
|
|
294
|
+
)}
|
|
295
|
+
<div className={cn("flex flex-col gap-2", classNames?.items)}>
|
|
296
|
+
{children}
|
|
297
|
+
</div>
|
|
298
|
+
{error && (
|
|
299
|
+
<p
|
|
300
|
+
className={cn(
|
|
301
|
+
"text-sm font-medium text-error",
|
|
302
|
+
classNames?.error,
|
|
303
|
+
)}
|
|
304
|
+
>
|
|
305
|
+
{error}
|
|
306
|
+
</p>
|
|
307
|
+
)}
|
|
254
308
|
{!error && description && (
|
|
255
|
-
<p
|
|
309
|
+
<p
|
|
310
|
+
className={cn(
|
|
311
|
+
"text-sm text-muted-foreground",
|
|
312
|
+
classNames?.description,
|
|
313
|
+
)}
|
|
314
|
+
>
|
|
315
|
+
{description}
|
|
316
|
+
</p>
|
|
256
317
|
)}
|
|
257
318
|
</Fieldset.Root>
|
|
258
319
|
</CheckboxGroupPrimitive>
|