@boxcustodia/library 2.0.0-alpha.13 → 2.0.0-alpha.15
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 -717
- package/dist/index.es.js +7059 -56179
- package/dist/theme.css +1 -1
- package/package.json +34 -26
- package/src/__doc__/Changelog.mdx +6 -6
- 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 +1245 -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 +615 -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 +236 -294
- package/src/components/button/button.test.tsx +10 -17
- package/src/components/button/button.tsx +53 -18
- package/src/components/button/components/base-button.tsx +25 -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 +140 -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 +120 -104
- package/src/components/date-picker/date-input.stories.tsx +14 -6
- package/src/components/date-picker/date-input.tsx +3 -3
- 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 +29 -15
- 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 +70 -90
- package/src/components/dropzone/dropzone.tsx +383 -105
- package/src/components/dropzone/index.ts +0 -1
- package/src/components/empty/empty.stories.tsx +164 -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 +5 -5
- 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 +275 -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 +29 -33
- 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 +153 -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 +766 -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 +372 -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 +147 -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 +322 -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 +364 -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 +69 -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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Accordion,
|
|
6
|
+
AccordionContent,
|
|
7
|
+
AccordionItem,
|
|
8
|
+
AccordionRoot,
|
|
9
|
+
AccordionTrigger,
|
|
10
|
+
} from "./accordion";
|
|
11
|
+
|
|
12
|
+
const richContent = (
|
|
13
|
+
<span>
|
|
14
|
+
Content can include <strong>rich markup</strong> like bold text, links, or
|
|
15
|
+
any React node.
|
|
16
|
+
</span>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Collapsible sections that expand/collapse to show or hide content.
|
|
21
|
+
* Use the composite `Accordion` for simple item lists, or compose primitives
|
|
22
|
+
* (`AccordionRoot`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`) for
|
|
23
|
+
* full control. Supports single or multiple open panels via `multiple`.
|
|
24
|
+
*/
|
|
25
|
+
const meta: Meta<typeof Accordion> = {
|
|
26
|
+
title: "Components/Accordion",
|
|
27
|
+
component: Accordion,
|
|
28
|
+
args: {
|
|
29
|
+
items: [
|
|
30
|
+
{
|
|
31
|
+
value: "item-1",
|
|
32
|
+
trigger: "What is this library?",
|
|
33
|
+
content:
|
|
34
|
+
"A collection of accessible, customizable UI components built on Base UI and styled with Tailwind CSS.",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: "item-2",
|
|
38
|
+
trigger: "Is it open source?",
|
|
39
|
+
content: richContent,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
value: "item-3",
|
|
43
|
+
trigger: "Can I customize the theme?",
|
|
44
|
+
content:
|
|
45
|
+
"Override any token via @theme in your CSS after importing the library stylesheet.",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
tags: ["new"],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default meta;
|
|
53
|
+
type Story = StoryObj<typeof Accordion>;
|
|
54
|
+
|
|
55
|
+
export const Default: Story = {};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `className` styles the accordion root. `classNames` exposes the `item`,
|
|
59
|
+
* `trigger`, and `content` slots for fine-grained styling.
|
|
60
|
+
*
|
|
61
|
+
* For structural changes (extra slots, custom layouts) use the primitives.
|
|
62
|
+
*/
|
|
63
|
+
export const WithClassNames: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
className: "max-w-md gap-2",
|
|
66
|
+
classNames: {
|
|
67
|
+
item: "rounded-md border border-input px-3 not-last:border-b",
|
|
68
|
+
trigger: "py-3 text-base",
|
|
69
|
+
content: "text-muted-foreground",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Allow multiple panels to be open simultaneously via `multiple`.
|
|
76
|
+
*/
|
|
77
|
+
export const Multiple: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
multiple: true,
|
|
80
|
+
defaultValue: ["item-1", "item-2"],
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Controlled accordion — external state drives which panel is open.
|
|
86
|
+
*/
|
|
87
|
+
export const Controlled: Story = {
|
|
88
|
+
render: (args) => {
|
|
89
|
+
const [value, setValue] = React.useState<string[]>(["item-1"]);
|
|
90
|
+
return <Accordion {...args} value={value} onValueChange={setValue} />;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const Disabled: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
items: [
|
|
97
|
+
{
|
|
98
|
+
value: "item-1",
|
|
99
|
+
trigger: "Available section",
|
|
100
|
+
content: "This section can be opened.",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
value: "item-2",
|
|
104
|
+
trigger: "Disabled section",
|
|
105
|
+
content: "This section is disabled.",
|
|
106
|
+
disabled: true,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build accordion manually using primitives when you need custom slots,
|
|
114
|
+
* additional markup between items, or conditional rendering.
|
|
115
|
+
*/
|
|
116
|
+
export const Primitive: Story = {
|
|
117
|
+
render: () => (
|
|
118
|
+
<AccordionRoot defaultValue={["item-1"]}>
|
|
119
|
+
<AccordionItem value="item-1">
|
|
120
|
+
<AccordionTrigger>What is this library?</AccordionTrigger>
|
|
121
|
+
<AccordionContent>
|
|
122
|
+
A collection of accessible, customizable UI components.
|
|
123
|
+
</AccordionContent>
|
|
124
|
+
</AccordionItem>
|
|
125
|
+
<AccordionItem value="item-2">
|
|
126
|
+
<AccordionTrigger>Is it open source?</AccordionTrigger>
|
|
127
|
+
<AccordionContent>{richContent}</AccordionContent>
|
|
128
|
+
</AccordionItem>
|
|
129
|
+
</AccordionRoot>
|
|
130
|
+
),
|
|
131
|
+
parameters: {
|
|
132
|
+
docs: {
|
|
133
|
+
source: {
|
|
134
|
+
code: `<AccordionRoot defaultValue={["item-1"]}>
|
|
135
|
+
<AccordionItem value="item-1">
|
|
136
|
+
<AccordionTrigger>Question</AccordionTrigger>
|
|
137
|
+
<AccordionContent>Answer</AccordionContent>
|
|
138
|
+
</AccordionItem>
|
|
139
|
+
</AccordionRoot>`,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
|
|
2
|
+
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
3
|
+
import { type ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib";
|
|
6
|
+
|
|
7
|
+
export function AccordionRoot({
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: AccordionPrimitive.Root.Props) {
|
|
11
|
+
return (
|
|
12
|
+
<AccordionPrimitive.Root
|
|
13
|
+
data-slot="accordion"
|
|
14
|
+
className={cn("flex w-full flex-col", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AccordionItem({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}: AccordionPrimitive.Item.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<AccordionPrimitive.Item
|
|
26
|
+
data-slot="accordion-item"
|
|
27
|
+
className={cn("not-last:border-b", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function AccordionTrigger({
|
|
34
|
+
className,
|
|
35
|
+
children,
|
|
36
|
+
...props
|
|
37
|
+
}: AccordionPrimitive.Trigger.Props) {
|
|
38
|
+
return (
|
|
39
|
+
<AccordionPrimitive.Header className="flex">
|
|
40
|
+
<AccordionPrimitive.Trigger
|
|
41
|
+
data-slot="accordion-trigger"
|
|
42
|
+
className={cn(
|
|
43
|
+
"group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
<ChevronDownIcon
|
|
50
|
+
data-slot="accordion-trigger-icon"
|
|
51
|
+
className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
|
|
52
|
+
/>
|
|
53
|
+
<ChevronUpIcon
|
|
54
|
+
data-slot="accordion-trigger-icon"
|
|
55
|
+
className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
|
|
56
|
+
/>
|
|
57
|
+
</AccordionPrimitive.Trigger>
|
|
58
|
+
</AccordionPrimitive.Header>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function AccordionContent({
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
...props
|
|
66
|
+
}: AccordionPrimitive.Panel.Props) {
|
|
67
|
+
return (
|
|
68
|
+
<AccordionPrimitive.Panel
|
|
69
|
+
data-slot="accordion-content"
|
|
70
|
+
className="h-(--accordion-panel-height) overflow-hidden text-sm transition-[height] duration-150 ease-out data-starting-style:h-0 data-ending-style:h-0"
|
|
71
|
+
{...props}
|
|
72
|
+
>
|
|
73
|
+
<div
|
|
74
|
+
className={cn(
|
|
75
|
+
"pb-2.5 pt-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
|
76
|
+
className,
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</div>
|
|
81
|
+
</AccordionPrimitive.Panel>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface AccordionItem {
|
|
86
|
+
value: string;
|
|
87
|
+
trigger: ReactNode;
|
|
88
|
+
content: ReactNode;
|
|
89
|
+
disabled?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface AccordionProps
|
|
93
|
+
extends Omit<AccordionPrimitive.Root.Props, "children" | "className"> {
|
|
94
|
+
items: AccordionItem[];
|
|
95
|
+
/** Styles the accordion root container. */
|
|
96
|
+
className?: string;
|
|
97
|
+
/** Styles applied to each internal slot. */
|
|
98
|
+
classNames?: {
|
|
99
|
+
/** Each collapsible item wrapper. */
|
|
100
|
+
item?: string;
|
|
101
|
+
/** Trigger button that toggles the item. */
|
|
102
|
+
trigger?: string;
|
|
103
|
+
/** Panel that contains the item content. */
|
|
104
|
+
content?: string;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function Accordion({
|
|
109
|
+
items,
|
|
110
|
+
className,
|
|
111
|
+
classNames,
|
|
112
|
+
...props
|
|
113
|
+
}: AccordionProps) {
|
|
114
|
+
return (
|
|
115
|
+
<AccordionRoot className={className} {...props}>
|
|
116
|
+
{items.map(({ value, trigger, content, disabled }) => (
|
|
117
|
+
<AccordionItem
|
|
118
|
+
key={value}
|
|
119
|
+
value={value}
|
|
120
|
+
disabled={disabled}
|
|
121
|
+
className={classNames?.item}
|
|
122
|
+
>
|
|
123
|
+
<AccordionTrigger className={classNames?.trigger}>
|
|
124
|
+
{trigger}
|
|
125
|
+
</AccordionTrigger>
|
|
126
|
+
<AccordionContent className={classNames?.content}>
|
|
127
|
+
{content}
|
|
128
|
+
</AccordionContent>
|
|
129
|
+
</AccordionItem>
|
|
130
|
+
))}
|
|
131
|
+
</AccordionRoot>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { AccordionPrimitive };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./accordion";
|
|
@@ -20,10 +20,9 @@ import {
|
|
|
20
20
|
* background tint, and icon color.
|
|
21
21
|
*
|
|
22
22
|
* Accepts `title`, `description`, `icon`, and `action` as props for the common case.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* without rebuilding the whole structure.
|
|
23
|
+
* `className` styles the alert root; `classNames` exposes the `title`, `description`,
|
|
24
|
+
* and `action` slots for fine-grained styling. For full layout control, compose the
|
|
25
|
+
* primitives directly: `AlertRoot`, `AlertTitle`, `AlertDescription`, and `AlertAction`.
|
|
27
26
|
*/
|
|
28
27
|
const meta: Meta<typeof Alert> = {
|
|
29
28
|
title: "Components/Alert",
|
|
@@ -46,7 +45,9 @@ const meta: Meta<typeof Alert> = {
|
|
|
46
45
|
},
|
|
47
46
|
title: { control: "text" },
|
|
48
47
|
description: { control: "text" },
|
|
48
|
+
classNames: { control: false },
|
|
49
49
|
},
|
|
50
|
+
tags: ["new"],
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
export default meta;
|
|
@@ -54,6 +55,25 @@ type Story = StoryObj<typeof Alert>;
|
|
|
54
55
|
|
|
55
56
|
export const Default: Story = {};
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* `className` styles the alert root. `classNames` exposes the `title`,
|
|
60
|
+
* `description`, and `action` slots for fine-grained styling without
|
|
61
|
+
* dropping to primitives.
|
|
62
|
+
*/
|
|
63
|
+
export const WithClassNames: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
variant: "info",
|
|
66
|
+
title: "Custom styled alert",
|
|
67
|
+
description: "Each slot can be styled independently.",
|
|
68
|
+
icon: <InfoIcon className="size-4" />,
|
|
69
|
+
className: "border-info/50 bg-info/10",
|
|
70
|
+
classNames: {
|
|
71
|
+
title: "text-base",
|
|
72
|
+
description: "text-foreground/80",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
57
77
|
/**
|
|
58
78
|
* Use `variant="error"` for validation failures or destructive outcomes.
|
|
59
79
|
* Pair with an icon to reinforce the severity.
|
|
@@ -79,9 +79,15 @@ interface AlertProps extends Omit<ComponentProps<typeof AlertRoot>, "title"> {
|
|
|
79
79
|
description?: ReactNode;
|
|
80
80
|
icon?: ReactNode;
|
|
81
81
|
action?: ReactNode;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
/** Styles applied to each internal slot. */
|
|
83
|
+
classNames?: {
|
|
84
|
+
/** Alert title text. */
|
|
85
|
+
title?: string;
|
|
86
|
+
/** Supporting description below the title. */
|
|
87
|
+
description?: string;
|
|
88
|
+
/** Action area on the right of the alert. */
|
|
89
|
+
action?: string;
|
|
90
|
+
};
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
export function Alert({
|
|
@@ -89,20 +95,22 @@ export function Alert({
|
|
|
89
95
|
description,
|
|
90
96
|
icon,
|
|
91
97
|
action,
|
|
92
|
-
|
|
93
|
-
descriptionProps,
|
|
94
|
-
actionProps,
|
|
98
|
+
classNames,
|
|
95
99
|
children,
|
|
96
100
|
...props
|
|
97
101
|
}: AlertProps) {
|
|
98
102
|
return (
|
|
99
103
|
<AlertRoot {...props}>
|
|
100
104
|
{icon}
|
|
101
|
-
{title && <AlertTitle {
|
|
105
|
+
{title && <AlertTitle className={classNames?.title}>{title}</AlertTitle>}
|
|
102
106
|
{description && (
|
|
103
|
-
<AlertDescription {
|
|
107
|
+
<AlertDescription className={classNames?.description}>
|
|
108
|
+
{description}
|
|
109
|
+
</AlertDescription>
|
|
110
|
+
)}
|
|
111
|
+
{action && (
|
|
112
|
+
<AlertAction className={classNames?.action}>{action}</AlertAction>
|
|
104
113
|
)}
|
|
105
|
-
{action && <AlertAction {...actionProps}>{action}</AlertAction>}
|
|
106
114
|
{children}
|
|
107
115
|
</AlertRoot>
|
|
108
116
|
);
|
|
@@ -47,6 +47,7 @@ const meta: Meta<typeof AlertDialog> = {
|
|
|
47
47
|
control: "select",
|
|
48
48
|
options: ["default", "error", "secondary", "outline", "ghost"],
|
|
49
49
|
},
|
|
50
|
+
classNames: { control: false },
|
|
50
51
|
},
|
|
51
52
|
};
|
|
52
53
|
|
|
@@ -55,6 +56,29 @@ type Story = StoryObj<typeof AlertDialog>;
|
|
|
55
56
|
|
|
56
57
|
export const Default: Story = {};
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* `className` styles the popup panel. `classNames` exposes the `backdrop`,
|
|
61
|
+
* `header`, `title`, `description`, `footer`, `closeButton`, and `actionButton`
|
|
62
|
+
* slots — use for fine-grained styling without dropping to primitives.
|
|
63
|
+
*
|
|
64
|
+
* For structural changes (custom layouts, extra slots) use the primitives directly.
|
|
65
|
+
*/
|
|
66
|
+
export const WithClassNames: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
className: "max-w-md",
|
|
69
|
+
classNames: {
|
|
70
|
+
backdrop: "bg-black/70 backdrop-blur-sm",
|
|
71
|
+
title: "text-xl text-error",
|
|
72
|
+
description: "text-base",
|
|
73
|
+
footer: "sm:justify-between",
|
|
74
|
+
actionButton: "min-w-32",
|
|
75
|
+
},
|
|
76
|
+
title: "Custom styled dialog",
|
|
77
|
+
description: "Slots can be styled without touching the primitives.",
|
|
78
|
+
actionText: "Confirm",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
58
82
|
/**
|
|
59
83
|
* Use `variant="error"` for irreversible actions like deletes.
|
|
60
84
|
* The error button signals danger — reserve it for data loss scenarios.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { render, screen } from "@testing-library/react";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { AlertDialog } from "../../components";
|
|
4
|
-
import { click } from "../../utils";
|
|
4
|
+
import { click } from "../../utils/tests";
|
|
5
5
|
|
|
6
6
|
describe("AlertDialog component", () => {
|
|
7
7
|
it("should show custom title and description", () => {
|
|
@@ -69,18 +69,26 @@ export function AlertDialogViewport({
|
|
|
69
69
|
/**
|
|
70
70
|
* Dialog content container. Includes Portal, Backdrop and Viewport internally.
|
|
71
71
|
* Pass `portalProps` to configure the portal (e.g. a custom `container`).
|
|
72
|
+
* `classNames` styles the internal `backdrop` and `viewport` slots.
|
|
72
73
|
*/
|
|
73
74
|
export function AlertDialogPopup({
|
|
74
75
|
className,
|
|
76
|
+
classNames,
|
|
75
77
|
portalProps,
|
|
76
78
|
...props
|
|
77
79
|
}: AlertDialogBase.Popup.Props & {
|
|
78
80
|
portalProps?: AlertDialogBase.Portal.Props;
|
|
81
|
+
classNames?: {
|
|
82
|
+
/** Overlay rendered behind the dialog. */
|
|
83
|
+
backdrop?: string;
|
|
84
|
+
/** Full-screen container that centers the popup. */
|
|
85
|
+
viewport?: string;
|
|
86
|
+
};
|
|
79
87
|
}) {
|
|
80
88
|
return (
|
|
81
89
|
<AlertDialogBase.Portal {...portalProps}>
|
|
82
|
-
<AlertDialogBackdrop />
|
|
83
|
-
<AlertDialogViewport>
|
|
90
|
+
<AlertDialogBackdrop className={classNames?.backdrop} />
|
|
91
|
+
<AlertDialogViewport className={classNames?.viewport}>
|
|
84
92
|
<AlertDialogBase.Popup
|
|
85
93
|
data-slot="alert-dialog-popup"
|
|
86
94
|
className={cn(
|
|
@@ -194,8 +202,25 @@ export type AlertDialogProps = Omit<AlertDialogBase.Root.Props, "children"> & {
|
|
|
194
202
|
closeText?: ReactNode;
|
|
195
203
|
/** Label for the action button. @default "Confirm" */
|
|
196
204
|
actionText?: ReactNode;
|
|
197
|
-
/**
|
|
205
|
+
/** Styles the dialog popup panel. */
|
|
198
206
|
className?: string;
|
|
207
|
+
/** Styles applied to each internal slot. */
|
|
208
|
+
classNames?: {
|
|
209
|
+
/** Overlay rendered behind the dialog. */
|
|
210
|
+
backdrop?: string;
|
|
211
|
+
/** Layout wrapper for title and description. */
|
|
212
|
+
header?: string;
|
|
213
|
+
/** Dialog heading. */
|
|
214
|
+
title?: string;
|
|
215
|
+
/** Supporting text below the title. */
|
|
216
|
+
description?: string;
|
|
217
|
+
/** Layout wrapper for action buttons. */
|
|
218
|
+
footer?: string;
|
|
219
|
+
/** Cancel button. */
|
|
220
|
+
closeButton?: string;
|
|
221
|
+
/** Action button. */
|
|
222
|
+
actionButton?: string;
|
|
223
|
+
};
|
|
199
224
|
};
|
|
200
225
|
|
|
201
226
|
/**
|
|
@@ -205,6 +230,10 @@ export type AlertDialogProps = Omit<AlertDialogBase.Root.Props, "children"> & {
|
|
|
205
230
|
* making it composable with `<Tooltip>` at the primitive level.
|
|
206
231
|
* For full structural control, use the exported primitives directly.
|
|
207
232
|
*
|
|
233
|
+
* `className` styles the popup panel. `classNames` exposes the `backdrop`,
|
|
234
|
+
* `header`, `title`, `description`, `footer`, `closeButton`, and `actionButton`
|
|
235
|
+
* slots for fine-grained tweaks without dropping to primitives.
|
|
236
|
+
*
|
|
208
237
|
* @example
|
|
209
238
|
* ```tsx
|
|
210
239
|
* <AlertDialog
|
|
@@ -228,29 +257,48 @@ export function AlertDialog({
|
|
|
228
257
|
closeText = "Cancel",
|
|
229
258
|
actionText = "Confirm",
|
|
230
259
|
className,
|
|
260
|
+
classNames,
|
|
231
261
|
...props
|
|
232
262
|
}: AlertDialogProps) {
|
|
233
263
|
return (
|
|
234
264
|
<AlertDialogRoot {...props}>
|
|
235
265
|
{trigger && <AlertDialogTrigger render={trigger} />}
|
|
236
|
-
<AlertDialogPopup
|
|
266
|
+
<AlertDialogPopup
|
|
267
|
+
className={className}
|
|
268
|
+
classNames={{ backdrop: classNames?.backdrop }}
|
|
269
|
+
>
|
|
237
270
|
{(title || description) && (
|
|
238
|
-
<AlertDialogHeader>
|
|
239
|
-
{title &&
|
|
271
|
+
<AlertDialogHeader className={classNames?.header}>
|
|
272
|
+
{title && (
|
|
273
|
+
<AlertDialogTitle className={classNames?.title}>
|
|
274
|
+
{title}
|
|
275
|
+
</AlertDialogTitle>
|
|
276
|
+
)}
|
|
240
277
|
{description && (
|
|
241
|
-
<AlertDialogDescription
|
|
278
|
+
<AlertDialogDescription className={classNames?.description}>
|
|
279
|
+
{description}
|
|
280
|
+
</AlertDialogDescription>
|
|
242
281
|
)}
|
|
243
282
|
</AlertDialogHeader>
|
|
244
283
|
)}
|
|
245
284
|
{children}
|
|
246
|
-
<AlertDialogFooter>
|
|
285
|
+
<AlertDialogFooter className={classNames?.footer}>
|
|
247
286
|
<AlertDialogClose
|
|
248
|
-
render={
|
|
287
|
+
render={
|
|
288
|
+
<Button variant="secondary" className={classNames?.closeButton}>
|
|
289
|
+
{closeText}
|
|
290
|
+
</Button>
|
|
291
|
+
}
|
|
249
292
|
onClick={onClose}
|
|
250
293
|
/>
|
|
251
294
|
<AlertDialogClose
|
|
252
295
|
render={
|
|
253
|
-
<Button
|
|
296
|
+
<Button
|
|
297
|
+
variant={variant ?? "default"}
|
|
298
|
+
className={classNames?.actionButton}
|
|
299
|
+
>
|
|
300
|
+
{actionText}
|
|
301
|
+
</Button>
|
|
254
302
|
}
|
|
255
303
|
onClick={onAction}
|
|
256
304
|
/>
|