@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
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { cva, VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
import { Flame, SearchIcon } from "lucide-react";
|
|
4
|
-
import { ComponentProps } from "react";
|
|
5
|
-
import { BaseButton, Button,
|
|
4
|
+
import { type ComponentProps } from "react";
|
|
5
|
+
import { BaseButton, Button, buttonVariants } from "../../components";
|
|
6
6
|
import { cn } from "../../lib";
|
|
7
|
-
import { ThemeProvider } from "../../providers";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
9
|
+
* Primary action trigger built on the [Base UI Button](https://base-ui.com/react/components/button) primitive.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
11
|
+
* **Loading** — pass an async function to `onClick` and loading state is managed automatically.
|
|
12
|
+
* Control it externally with the `loading` prop. The spinner overlays content while preserving
|
|
13
|
+
* the button's original dimensions so layout never shifts.
|
|
14
|
+
*
|
|
15
|
+
* **Custom elements** — use the `render` prop to swap the underlying element.
|
|
16
|
+
* For links, apply `buttonVariants` directly to a plain `<a>` — Base UI always sets
|
|
17
|
+
* `role="button"` which overrides the semantic link role on `<a>` elements rendered via `render`.
|
|
18
|
+
*
|
|
19
|
+
* **Extension** — use `BaseButton` to build fully custom variants without inheriting any styles.
|
|
13
20
|
*/
|
|
14
21
|
const meta: Meta<typeof Button> = {
|
|
15
|
-
title: "
|
|
22
|
+
title: "Components/Button",
|
|
16
23
|
component: Button,
|
|
17
24
|
args: {
|
|
18
|
-
children: "Click me
|
|
25
|
+
children: "Click me",
|
|
19
26
|
},
|
|
27
|
+
parameters: { layout: "centered" },
|
|
20
28
|
};
|
|
21
29
|
|
|
22
30
|
export default meta;
|
|
@@ -24,161 +32,194 @@ type Story = StoryObj<typeof Button>;
|
|
|
24
32
|
|
|
25
33
|
export const Default: Story = {};
|
|
26
34
|
|
|
27
|
-
export const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
},
|
|
35
|
+
export const Variants: Story = {
|
|
36
|
+
render: (args) => (
|
|
37
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
38
|
+
{(
|
|
39
|
+
[
|
|
40
|
+
"default",
|
|
41
|
+
"outline",
|
|
42
|
+
"secondary",
|
|
43
|
+
"ghost",
|
|
44
|
+
"link",
|
|
45
|
+
"error",
|
|
46
|
+
"success",
|
|
47
|
+
"warning",
|
|
48
|
+
] as const
|
|
49
|
+
).map((variant) => (
|
|
50
|
+
<Button key={variant} {...args} variant={variant}>
|
|
51
|
+
{variant}
|
|
52
|
+
</Button>
|
|
53
|
+
))}
|
|
54
|
+
</div>
|
|
55
|
+
),
|
|
49
56
|
};
|
|
50
57
|
|
|
51
|
-
export const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
};
|
|
58
|
+
export const Sizes: Story = {
|
|
59
|
+
render: () => {
|
|
60
|
+
const textSizes = ["xs", "sm", "default", "lg"] as const;
|
|
61
|
+
const iconSizes = ["icon-xs", "icon-sm", "icon", "icon-lg"] as const;
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
return (
|
|
64
|
+
<div className="space-y-6">
|
|
65
|
+
<div className="flex flex-wrap items-end gap-4">
|
|
66
|
+
{textSizes.map((size) => (
|
|
67
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
68
|
+
<p className="text-xs text-muted-foreground">{size}</p>
|
|
69
|
+
<Button size={size}>Click me</Button>
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
<div className="flex flex-wrap items-end gap-4">
|
|
74
|
+
{iconSizes.map((size) => (
|
|
75
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
76
|
+
<p className="text-xs text-muted-foreground">{size}</p>
|
|
77
|
+
<Button size={size}>
|
|
78
|
+
<Flame />
|
|
79
|
+
</Button>
|
|
80
|
+
</div>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
60
85
|
},
|
|
61
86
|
};
|
|
62
87
|
|
|
63
88
|
/**
|
|
64
|
-
*
|
|
89
|
+
* Compose icons directly as children. Position is determined by JSX order —
|
|
90
|
+
* place the icon before or after the label. Use an icon-only size (`icon`,
|
|
91
|
+
* `icon-sm`, etc.) for square icon buttons.
|
|
65
92
|
*/
|
|
66
|
-
export const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
export const WithIcon: Story = {
|
|
94
|
+
render: (args) => (
|
|
95
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
96
|
+
<Button {...args}>
|
|
97
|
+
<SearchIcon />
|
|
98
|
+
Search
|
|
99
|
+
</Button>
|
|
100
|
+
<Button {...args}>
|
|
101
|
+
Search
|
|
102
|
+
<SearchIcon />
|
|
103
|
+
</Button>
|
|
104
|
+
<Button size="icon">
|
|
105
|
+
<SearchIcon />
|
|
106
|
+
</Button>
|
|
107
|
+
</div>
|
|
108
|
+
),
|
|
109
|
+
parameters: {
|
|
110
|
+
docs: {
|
|
111
|
+
source: {
|
|
112
|
+
code: `<Button>
|
|
113
|
+
<SearchIcon />
|
|
114
|
+
Search
|
|
115
|
+
</Button>
|
|
71
116
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
117
|
+
<Button>
|
|
118
|
+
Search
|
|
119
|
+
<SearchIcon />
|
|
120
|
+
</Button>
|
|
121
|
+
|
|
122
|
+
<Button size="icon">
|
|
123
|
+
<SearchIcon />
|
|
124
|
+
</Button>`,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
81
127
|
},
|
|
82
128
|
};
|
|
83
129
|
|
|
84
130
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
131
|
+
* When `onClick` returns a `Promise`, loading activates automatically and stops when
|
|
132
|
+
* the Promise settles — no external state needed. Pass `loading` directly to control it
|
|
133
|
+
* from outside (e.g., from a form submission handler or mutation hook).
|
|
134
|
+
*
|
|
135
|
+
* While loading, the button is non-interactive (`pointer-events-none`, `aria-busy`)
|
|
136
|
+
* and the spinner overlays content without changing the button's dimensions.
|
|
89
137
|
*/
|
|
90
138
|
export const Loading: Story = {
|
|
91
139
|
render: () => {
|
|
92
|
-
const sleep = (): Promise<void> =>
|
|
93
|
-
|
|
94
|
-
};
|
|
140
|
+
const sleep = (): Promise<void> =>
|
|
141
|
+
new Promise((resolve) => setTimeout(resolve, 2000));
|
|
95
142
|
|
|
96
143
|
return (
|
|
97
|
-
<div className="space-y-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<p className="text-sm text-muted-foreground mb-4">
|
|
104
|
-
En este modo, el loader se agrega al contenido existente. Si hay un
|
|
105
|
-
ícono, el loader lo reemplaza.
|
|
144
|
+
<div className="space-y-10">
|
|
145
|
+
<section className="space-y-3">
|
|
146
|
+
<h2 className="text-sm font-semibold">Auto — async onClick</h2>
|
|
147
|
+
<p className="text-sm text-muted-foreground">
|
|
148
|
+
Click any button. Loading starts when the Promise begins and stops
|
|
149
|
+
when it resolves.
|
|
106
150
|
</p>
|
|
107
|
-
<div className="flex flex-wrap items-center gap-
|
|
108
|
-
<Button onClick={sleep}>
|
|
109
|
-
<Button onClick={sleep}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<Button onClick={sleep} icon={<SearchIcon />}>
|
|
113
|
-
Ícono inicio
|
|
151
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
152
|
+
<Button onClick={sleep}>Save</Button>
|
|
153
|
+
<Button onClick={sleep}>
|
|
154
|
+
<SearchIcon />
|
|
155
|
+
Search
|
|
114
156
|
</Button>
|
|
115
|
-
<Button onClick={sleep}
|
|
116
|
-
|
|
157
|
+
<Button onClick={sleep} size="icon">
|
|
158
|
+
<SearchIcon />
|
|
117
159
|
</Button>
|
|
118
|
-
<Button onClick={sleep} icon={<SearchIcon />} size="icon" />
|
|
119
160
|
</div>
|
|
120
161
|
</section>
|
|
121
162
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<p className="text-sm text-muted-foreground mb-4">
|
|
128
|
-
En este modo, el loader reemplaza todo el contenido manteniendo el
|
|
129
|
-
ancho original del botón.
|
|
163
|
+
<section className="space-y-3">
|
|
164
|
+
<h2 className="text-sm font-semibold">Controlled</h2>
|
|
165
|
+
<p className="text-sm text-muted-foreground">
|
|
166
|
+
Pass the <code>loading</code> prop directly to control state
|
|
167
|
+
externally.
|
|
130
168
|
</p>
|
|
131
|
-
<div className="flex flex-wrap items-center gap-
|
|
132
|
-
<Button
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Ícono inicio
|
|
169
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
170
|
+
<Button loading>Save</Button>
|
|
171
|
+
<Button loading>
|
|
172
|
+
<SearchIcon />
|
|
173
|
+
Search
|
|
137
174
|
</Button>
|
|
138
|
-
<Button
|
|
139
|
-
|
|
140
|
-
loaderReplace
|
|
141
|
-
icon={<SearchIcon />}
|
|
142
|
-
iconPosition="end"
|
|
143
|
-
>
|
|
144
|
-
Ícono final
|
|
175
|
+
<Button loading size="icon">
|
|
176
|
+
<SearchIcon />
|
|
145
177
|
</Button>
|
|
146
|
-
<Button
|
|
147
|
-
onClick={sleep}
|
|
148
|
-
loaderReplace
|
|
149
|
-
icon={<SearchIcon />}
|
|
150
|
-
size="icon"
|
|
151
|
-
/>
|
|
152
178
|
</div>
|
|
153
179
|
</section>
|
|
154
180
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
181
|
+
<section className="space-y-3">
|
|
182
|
+
<h2 className="text-sm font-semibold">All sizes</h2>
|
|
183
|
+
<div className="flex flex-wrap items-end gap-4">
|
|
184
|
+
{(["xs", "sm", "default", "lg"] as const).map((size) => (
|
|
185
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
186
|
+
<p className="text-xs text-muted-foreground">{size}</p>
|
|
187
|
+
<Button loading size={size}>
|
|
188
|
+
Save
|
|
189
|
+
</Button>
|
|
190
|
+
</div>
|
|
191
|
+
))}
|
|
192
|
+
{(["icon-xs", "icon-sm", "icon", "icon-lg"] as const).map(
|
|
193
|
+
(size) => (
|
|
194
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
195
|
+
<p className="text-xs text-muted-foreground">{size}</p>
|
|
196
|
+
<Button loading size={size}>
|
|
197
|
+
<SearchIcon />
|
|
198
|
+
</Button>
|
|
199
|
+
</div>
|
|
200
|
+
),
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
</section>
|
|
204
|
+
|
|
205
|
+
<section className="space-y-3">
|
|
206
|
+
<h2 className="text-sm font-semibold">All variants</h2>
|
|
207
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
208
|
+
{(
|
|
209
|
+
[
|
|
210
|
+
"default",
|
|
211
|
+
"outline",
|
|
212
|
+
"secondary",
|
|
213
|
+
"ghost",
|
|
214
|
+
"error",
|
|
215
|
+
"success",
|
|
216
|
+
"warning",
|
|
217
|
+
] as const
|
|
218
|
+
).map((variant) => (
|
|
219
|
+
<Button key={variant} onClick={sleep} variant={variant}>
|
|
220
|
+
{variant}
|
|
221
|
+
</Button>
|
|
222
|
+
))}
|
|
182
223
|
</div>
|
|
183
224
|
</section>
|
|
184
225
|
</div>
|
|
@@ -187,83 +228,55 @@ export const Loading: Story = {
|
|
|
187
228
|
};
|
|
188
229
|
|
|
189
230
|
/**
|
|
190
|
-
*
|
|
231
|
+
* Use the `render` prop to render the button as any element while keeping all styles
|
|
232
|
+
* and behavior. For navigation links, use `buttonVariants` on a plain `<a>` tag instead —
|
|
233
|
+
* Base UI always applies `role="button"` which overrides the semantic `<a>` role.
|
|
191
234
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* El componente recibe todas las propiedades de `button`, por lo que se puede customizar los estilos, accesibilidad, referencias, eventos, etc
|
|
206
|
-
*/
|
|
207
|
-
export const Custom: Story = {
|
|
208
|
-
args: {
|
|
209
|
-
className: "bg-amber-500 hover:bg-amber-600",
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Estos son los tamanos disponibles para el `Button`.
|
|
235
|
+
* ```tsx
|
|
236
|
+
* // Render as a different element
|
|
237
|
+
* <Button render={<div />}>Custom element</Button>
|
|
238
|
+
*
|
|
239
|
+
* // Link styled as a button — correct pattern for navigation
|
|
240
|
+
* <a href="/dashboard" className={buttonVariants({ variant: "outline" })}>
|
|
241
|
+
* Go to dashboard
|
|
242
|
+
* </a>
|
|
243
|
+
* ```
|
|
215
244
|
*/
|
|
216
|
-
export const
|
|
217
|
-
render: () =>
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
);
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
export const Shapes: Story = {
|
|
236
|
-
render: () => {
|
|
237
|
-
const shapes = ["rounded", "square", "circle"] as const;
|
|
245
|
+
export const Render: Story = {
|
|
246
|
+
render: (args) => (
|
|
247
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
248
|
+
<Button {...args} render={<div />}>
|
|
249
|
+
Rendered as div
|
|
250
|
+
</Button>
|
|
251
|
+
<a href="#" className={buttonVariants({ variant: "outline" })}>
|
|
252
|
+
Link as button
|
|
253
|
+
</a>
|
|
254
|
+
</div>
|
|
255
|
+
),
|
|
256
|
+
parameters: {
|
|
257
|
+
docs: {
|
|
258
|
+
source: {
|
|
259
|
+
code: `<Button render={<div />}>Rendered as div</Button>
|
|
238
260
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
<Button shape={shape} key={shape} size="icon" icon={<Flame />} />
|
|
245
|
-
<Button shape={shape} key={shape}>
|
|
246
|
-
{shape}
|
|
247
|
-
</Button>
|
|
248
|
-
</div>
|
|
249
|
-
))}
|
|
250
|
-
</div>
|
|
251
|
-
);
|
|
261
|
+
<a href="/dashboard" className={buttonVariants({ variant: "outline" })}>
|
|
262
|
+
Link as button
|
|
263
|
+
</a>`,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
252
266
|
},
|
|
253
267
|
};
|
|
254
268
|
|
|
255
|
-
const
|
|
269
|
+
const customButtonVariants = cva(
|
|
256
270
|
[
|
|
257
|
-
"flex items-center justify-center gap-2 px-4 py-2",
|
|
258
|
-
"text-sm font-medium transition-all",
|
|
259
|
-
"
|
|
260
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
271
|
+
"inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2",
|
|
272
|
+
"text-sm font-medium transition-all outline-none select-none",
|
|
273
|
+
"focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
261
274
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
262
275
|
],
|
|
263
276
|
{
|
|
264
277
|
variants: {
|
|
265
278
|
variant: {
|
|
266
|
-
white: "bg-white text-primary
|
|
279
|
+
white: "border border-border bg-white text-primary",
|
|
267
280
|
black: "bg-black text-white",
|
|
268
281
|
},
|
|
269
282
|
},
|
|
@@ -274,123 +287,52 @@ const buttonVariants = cva(
|
|
|
274
287
|
);
|
|
275
288
|
|
|
276
289
|
type BaseButtonProps = ComponentProps<typeof BaseButton>;
|
|
277
|
-
interface
|
|
290
|
+
interface CustomButtonProps
|
|
291
|
+
extends BaseButtonProps,
|
|
292
|
+
VariantProps<typeof customButtonVariants> {}
|
|
278
293
|
|
|
279
|
-
const CustomButton = ({ variant, ...props }:
|
|
280
|
-
|
|
281
|
-
}
|
|
294
|
+
const CustomButton = ({ variant, className, ...props }: CustomButtonProps) => (
|
|
295
|
+
<BaseButton
|
|
296
|
+
className={cn(customButtonVariants({ variant }), className)}
|
|
297
|
+
{...props}
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
282
300
|
|
|
283
301
|
/**
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* A continuación, se definen las variantes de botón que puedes extender o modificar a tus necesidades.
|
|
302
|
+
* `BaseButton` is an unstyled wrapper around the Base UI primitive. Use it to build
|
|
303
|
+
* fully custom button variants without inheriting any library styles.
|
|
304
|
+
* It supports all native button props plus `loading` and the `render` prop.
|
|
289
305
|
*
|
|
290
306
|
* ```tsx
|
|
291
|
-
* import { ComponentProps } from 'react';
|
|
307
|
+
* import { type ComponentProps } from 'react';
|
|
292
308
|
* import { cva, type VariantProps } from 'class-variance-authority';
|
|
293
309
|
* import { BaseButton, cn } from '@boxcustodia/library';
|
|
294
310
|
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* 'flex items-center justify-center gap-2',
|
|
298
|
-
* 'text-sm font-medium transition-all',
|
|
299
|
-
* 'rounded-md ring-offset-background',
|
|
300
|
-
* 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
301
|
-
* 'disabled:pointer-events-none disabled:opacity-50',
|
|
302
|
-
* 'hover:brightness-105',
|
|
303
|
-
* ],
|
|
311
|
+
* const myVariants = cva(
|
|
312
|
+
* 'inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-all outline-none disabled:pointer-events-none disabled:opacity-50',
|
|
304
313
|
* {
|
|
305
314
|
* variants: {
|
|
306
315
|
* variant: {
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* success: "bg-success text-success-foreground",
|
|
310
|
-
* outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
311
|
-
* secondary: "bg-secondary text-secondary-foreground hover:brightness-100 hover:bg-secondary/80",
|
|
312
|
-
* ghost: "hover:bg-accent hover:brightness-100 hover:text-accent-foreground",
|
|
313
|
-
* link: "text-primary underline-offset-4 hover:underline",
|
|
314
|
-
* white: 'bg-white text-primary', <-- Nueva variante
|
|
315
|
-
* black: 'bg-black text-white', <-- Nueva variante
|
|
316
|
-
* },
|
|
317
|
-
* size: {
|
|
318
|
-
* default: "h-10 px-4 py-2",
|
|
319
|
-
* sm: "h-9 rounded-md px-3",
|
|
320
|
-
* lg: "h-11 rounded-md px-8",
|
|
321
|
-
* icon: "w-10 aspect-square",
|
|
316
|
+
* white: 'border border-border bg-white text-primary',
|
|
317
|
+
* black: 'bg-black text-white',
|
|
322
318
|
* },
|
|
323
319
|
* },
|
|
324
|
-
* defaultVariants: {
|
|
325
|
-
* variant: 'default',
|
|
326
|
-
* size: 'default',
|
|
327
|
-
* },
|
|
320
|
+
* defaultVariants: { variant: 'white' },
|
|
328
321
|
* },
|
|
329
322
|
* );
|
|
330
323
|
*
|
|
331
|
-
* type
|
|
332
|
-
* typer Props = BaseButtonProps, VariantProps<typeof buttonVariants>
|
|
333
|
-
*
|
|
334
|
-
* export const Button = ({ className, variant, size, ...props }: Props) => {
|
|
335
|
-
* return (
|
|
336
|
-
* <BaseButton
|
|
337
|
-
* {...props}
|
|
338
|
-
* className={cn(buttonVariants({ variant, size }), className)}
|
|
339
|
-
* />
|
|
340
|
-
* );
|
|
341
|
-
* };
|
|
324
|
+
* type Props = ComponentProps<typeof BaseButton> & VariantProps<typeof myVariants>;
|
|
342
325
|
*
|
|
326
|
+
* export const CustomButton = ({ variant, className, ...props }: Props) => (
|
|
327
|
+
* <BaseButton className={cn(myVariants({ variant }), className)} {...props} />
|
|
328
|
+
* );
|
|
343
329
|
* ```
|
|
344
|
-
*
|
|
345
330
|
*/
|
|
346
|
-
|
|
347
331
|
export const CustomVariants: Story = {
|
|
348
332
|
render: () => (
|
|
349
|
-
<div className="
|
|
350
|
-
<CustomButton variant="
|
|
351
|
-
<CustomButton variant="
|
|
333
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
334
|
+
<CustomButton variant="white">White button</CustomButton>
|
|
335
|
+
<CustomButton variant="black">Black button</CustomButton>
|
|
352
336
|
</div>
|
|
353
337
|
),
|
|
354
338
|
};
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* ThemeProvider permite customizar de manera global las propiedades del componente
|
|
358
|
-
*
|
|
359
|
-
* Ejemplo:
|
|
360
|
-
* ```tsx
|
|
361
|
-
* <ThemeProvider
|
|
362
|
-
* theme={{
|
|
363
|
-
* Button: {
|
|
364
|
-
* className: "bg-red-500",
|
|
365
|
-
* icon: <SearchIcon />,
|
|
366
|
-
* iconPosition: "end",
|
|
367
|
-
* loaderReplace: true,
|
|
368
|
-
* },
|
|
369
|
-
* }}
|
|
370
|
-
* >
|
|
371
|
-
* <Button>Default Button</Button>
|
|
372
|
-
* </ThemeProvider>
|
|
373
|
-
* ```
|
|
374
|
-
*/
|
|
375
|
-
export const Theme: Story = {
|
|
376
|
-
render: () => {
|
|
377
|
-
const sleep = (): Promise<void> => {
|
|
378
|
-
return new Promise((resolve) => setTimeout(resolve, 2000));
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
return (
|
|
382
|
-
<ThemeProvider
|
|
383
|
-
theme={{
|
|
384
|
-
Button: {
|
|
385
|
-
className: "bg-red-500",
|
|
386
|
-
icon: <SearchIcon />,
|
|
387
|
-
iconPosition: "end",
|
|
388
|
-
loaderReplace: true,
|
|
389
|
-
},
|
|
390
|
-
}}
|
|
391
|
-
>
|
|
392
|
-
<Button onClick={sleep}>Default Button</Button>
|
|
393
|
-
</ThemeProvider>
|
|
394
|
-
);
|
|
395
|
-
},
|
|
396
|
-
};
|