@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,6 +1,6 @@
|
|
|
1
1
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
2
2
|
import { CalendarIcon } from "lucide-react";
|
|
3
|
-
import { ReactNode, useRef, useState } from "react";
|
|
3
|
+
import { type ReactElement, ReactNode, useRef, useState } from "react";
|
|
4
4
|
import {
|
|
5
5
|
Calendar,
|
|
6
6
|
FieldControl,
|
|
@@ -24,11 +24,14 @@ import {
|
|
|
24
24
|
formatSingleDate,
|
|
25
25
|
} from "./date-picker.utils";
|
|
26
26
|
|
|
27
|
-
export
|
|
27
|
+
export function DatePicker(props: SingleDatePickerProps): ReactElement;
|
|
28
|
+
export function DatePicker(props: RangeDatePickerProps): ReactElement;
|
|
29
|
+
export function DatePicker(props: MultipleDatePickerProps): ReactElement;
|
|
30
|
+
export function DatePicker(props: DatePickerProps): ReactElement {
|
|
28
31
|
if (props.mode === "range") return <RangePicker {...props} />;
|
|
29
32
|
if (props.mode === "multiple") return <MultiplePicker {...props} />;
|
|
30
33
|
return <SinglePicker {...props} />;
|
|
31
|
-
}
|
|
34
|
+
}
|
|
32
35
|
|
|
33
36
|
// ─── Shared popover shell ────────────────────────────────────────────────────
|
|
34
37
|
|
|
@@ -39,6 +42,7 @@ type PickerShellProps = {
|
|
|
39
42
|
hasValue: boolean;
|
|
40
43
|
disabled?: boolean;
|
|
41
44
|
className?: string;
|
|
45
|
+
classNames?: DatePickerProps["classNames"];
|
|
42
46
|
children: ReactNode;
|
|
43
47
|
footer?: ReactNode;
|
|
44
48
|
slot: string;
|
|
@@ -53,6 +57,7 @@ const PickerShell = ({
|
|
|
53
57
|
hasValue,
|
|
54
58
|
disabled,
|
|
55
59
|
className,
|
|
60
|
+
classNames,
|
|
56
61
|
children,
|
|
57
62
|
footer,
|
|
58
63
|
slot,
|
|
@@ -70,7 +75,7 @@ const PickerShell = ({
|
|
|
70
75
|
disabled={disabled}
|
|
71
76
|
className={cn(
|
|
72
77
|
inputBaseClasses,
|
|
73
|
-
"inline-flex items-center gap-2 h-
|
|
78
|
+
"inline-flex items-center gap-2 h-8 cursor-pointer text-left",
|
|
74
79
|
"focus-visible:border-ring",
|
|
75
80
|
"aria-invalid:border-error focus-visible:aria-invalid:ring-error/20",
|
|
76
81
|
"group-data-[invalid]:border-error focus-visible:group-data-[invalid]:ring-error/20",
|
|
@@ -100,13 +105,16 @@ const PickerShell = ({
|
|
|
100
105
|
}
|
|
101
106
|
/>
|
|
102
107
|
<PopoverPopup
|
|
103
|
-
className="w-auto p-0"
|
|
108
|
+
className={cn("w-auto p-0", classNames?.popup)}
|
|
104
109
|
align="start"
|
|
105
110
|
data-slot={`${slot}-content`}
|
|
106
111
|
>
|
|
107
112
|
{children}
|
|
108
113
|
{footer && (
|
|
109
|
-
<div
|
|
114
|
+
<div
|
|
115
|
+
className={cn("p-2", classNames?.footer)}
|
|
116
|
+
data-slot={`${slot}-footer`}
|
|
117
|
+
>
|
|
110
118
|
{footer}
|
|
111
119
|
</div>
|
|
112
120
|
)}
|
|
@@ -120,19 +128,20 @@ const PickerShell = ({
|
|
|
120
128
|
const SinglePicker = ({
|
|
121
129
|
value: valueProp,
|
|
122
130
|
defaultValue,
|
|
123
|
-
|
|
131
|
+
onValueChange,
|
|
124
132
|
placeholder = "Seleccionar fecha",
|
|
125
133
|
disabled,
|
|
126
134
|
disabledDate,
|
|
127
135
|
renderFooter,
|
|
128
136
|
className,
|
|
137
|
+
classNames,
|
|
129
138
|
required,
|
|
130
139
|
}: SingleDatePickerProps) => {
|
|
131
140
|
const [open, setOpen] = useState(false);
|
|
132
141
|
|
|
133
142
|
const [value, setValue] = useControllableState<Date | null>({
|
|
134
143
|
prop: valueProp,
|
|
135
|
-
onChange: (next) =>
|
|
144
|
+
onChange: (next) => onValueChange?.(next ?? null),
|
|
136
145
|
defaultProp: defaultValue ?? null,
|
|
137
146
|
});
|
|
138
147
|
|
|
@@ -157,12 +166,13 @@ const SinglePicker = ({
|
|
|
157
166
|
hasValue={!!value}
|
|
158
167
|
disabled={disabled}
|
|
159
168
|
className={className}
|
|
169
|
+
classNames={classNames}
|
|
160
170
|
footer={renderFooter?.(footerProps)}
|
|
161
171
|
formValue={formatSingleDate(value, "")}
|
|
162
172
|
required={required}
|
|
163
173
|
>
|
|
164
174
|
<Calendar
|
|
165
|
-
className="border-none"
|
|
175
|
+
className={cn("border-none", classNames?.calendar)}
|
|
166
176
|
mode="single"
|
|
167
177
|
selected={value ?? undefined}
|
|
168
178
|
onSelect={(next) => {
|
|
@@ -183,19 +193,20 @@ const EMPTY_RANGE: DateRange = { start: null, end: null };
|
|
|
183
193
|
const RangePicker = ({
|
|
184
194
|
value: valueProp,
|
|
185
195
|
defaultValue,
|
|
186
|
-
|
|
196
|
+
onValueChange,
|
|
187
197
|
placeholder = "Seleccionar rango",
|
|
188
198
|
disabled,
|
|
189
199
|
disabledDate,
|
|
190
200
|
renderFooter,
|
|
191
201
|
className,
|
|
202
|
+
classNames,
|
|
192
203
|
required,
|
|
193
204
|
}: RangeDatePickerProps) => {
|
|
194
205
|
const [open, setOpen] = useState(false);
|
|
195
206
|
|
|
196
207
|
const [value, setValue] = useControllableState<DateRange>({
|
|
197
208
|
prop: valueProp,
|
|
198
|
-
onChange: (next) =>
|
|
209
|
+
onChange: (next) => onValueChange?.(next ?? EMPTY_RANGE),
|
|
199
210
|
defaultProp: defaultValue ?? EMPTY_RANGE,
|
|
200
211
|
});
|
|
201
212
|
|
|
@@ -220,6 +231,7 @@ const RangePicker = ({
|
|
|
220
231
|
hasValue={!!(range.start || range.end)}
|
|
221
232
|
disabled={disabled}
|
|
222
233
|
className={className}
|
|
234
|
+
classNames={classNames}
|
|
223
235
|
footer={renderFooter?.(footerProps)}
|
|
224
236
|
formValue={formatRangeDate(
|
|
225
237
|
range.start || range.end ? range : undefined,
|
|
@@ -228,7 +240,7 @@ const RangePicker = ({
|
|
|
228
240
|
required={required}
|
|
229
241
|
>
|
|
230
242
|
<Calendar
|
|
231
|
-
className="border-none"
|
|
243
|
+
className={cn("border-none", classNames?.calendar)}
|
|
232
244
|
mode="range"
|
|
233
245
|
selected={{
|
|
234
246
|
from: range.start ?? undefined,
|
|
@@ -248,19 +260,20 @@ const RangePicker = ({
|
|
|
248
260
|
const MultiplePicker = ({
|
|
249
261
|
value: valueProp,
|
|
250
262
|
defaultValue,
|
|
251
|
-
|
|
263
|
+
onValueChange,
|
|
252
264
|
placeholder = "Seleccionar fechas",
|
|
253
265
|
disabled,
|
|
254
266
|
disabledDate,
|
|
255
267
|
renderFooter,
|
|
256
268
|
className,
|
|
269
|
+
classNames,
|
|
257
270
|
required,
|
|
258
271
|
}: MultipleDatePickerProps) => {
|
|
259
272
|
const [open, setOpen] = useState(false);
|
|
260
273
|
|
|
261
274
|
const [value, setValue] = useControllableState<Date[]>({
|
|
262
275
|
prop: valueProp,
|
|
263
|
-
onChange: (next) =>
|
|
276
|
+
onChange: (next) => onValueChange?.(next ?? []),
|
|
264
277
|
defaultProp: defaultValue ?? [],
|
|
265
278
|
});
|
|
266
279
|
|
|
@@ -280,12 +293,13 @@ const MultiplePicker = ({
|
|
|
280
293
|
hasValue={(value?.length ?? 0) > 0}
|
|
281
294
|
disabled={disabled}
|
|
282
295
|
className={className}
|
|
296
|
+
classNames={classNames}
|
|
283
297
|
footer={renderFooter?.(footerProps)}
|
|
284
298
|
formValue={formatMultipleDates(value, "")}
|
|
285
299
|
required={required}
|
|
286
300
|
>
|
|
287
301
|
<Calendar
|
|
288
|
-
className="border-none"
|
|
302
|
+
className={cn("border-none", classNames?.calendar)}
|
|
289
303
|
mode="multiple"
|
|
290
304
|
selected={value ?? []}
|
|
291
305
|
onSelect={(next) => {
|
|
@@ -42,6 +42,7 @@ const meta: Meta<typeof Dialog> = {
|
|
|
42
42
|
hideClose: { control: "boolean" },
|
|
43
43
|
footer: { control: false },
|
|
44
44
|
children: { control: false },
|
|
45
|
+
classNames: { control: false },
|
|
45
46
|
},
|
|
46
47
|
};
|
|
47
48
|
|
|
@@ -50,6 +51,23 @@ type Story = StoryObj<typeof Dialog>;
|
|
|
50
51
|
|
|
51
52
|
export const Default: Story = {};
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* `className` styles the popup panel. `classNames` exposes the `backdrop`,
|
|
56
|
+
* `viewport`, `header`, `title`, `description`, `footer`, and `closeButton` slots.
|
|
57
|
+
*/
|
|
58
|
+
export const WithClassNames: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
trigger: <Button variant="outline">Open styled dialog</Button>,
|
|
61
|
+
title: "Styled dialog",
|
|
62
|
+
description: "Each internal slot can be tweaked via classNames.",
|
|
63
|
+
classNames: {
|
|
64
|
+
backdrop: "bg-primary/30",
|
|
65
|
+
title: "text-primary",
|
|
66
|
+
closeButton: "text-primary",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
53
71
|
/**
|
|
54
72
|
* The primary use case: a dialog containing a form.
|
|
55
73
|
* `children` renders between the description and the footer.
|
|
@@ -68,24 +68,31 @@ export function DialogViewport({
|
|
|
68
68
|
/**
|
|
69
69
|
* Dialog content container. Includes Portal, Backdrop and Viewport internally.
|
|
70
70
|
* Pass `portalProps` to configure the portal (e.g. a custom `container`).
|
|
71
|
-
*
|
|
71
|
+
* `classNames` styles the internal `backdrop`, `viewport` and `closeButton` slots.
|
|
72
72
|
*/
|
|
73
73
|
export function DialogPopup({
|
|
74
74
|
className,
|
|
75
|
+
classNames,
|
|
75
76
|
children,
|
|
76
77
|
hideClose,
|
|
77
|
-
closeProps,
|
|
78
78
|
portalProps,
|
|
79
79
|
...props
|
|
80
80
|
}: DialogBase.Popup.Props & {
|
|
81
81
|
hideClose?: boolean;
|
|
82
|
-
closeProps?: DialogBase.Close.Props;
|
|
83
82
|
portalProps?: DialogBase.Portal.Props;
|
|
83
|
+
classNames?: {
|
|
84
|
+
/** Overlay rendered behind the dialog. */
|
|
85
|
+
backdrop?: string;
|
|
86
|
+
/** Full-screen container that centers the popup. */
|
|
87
|
+
viewport?: string;
|
|
88
|
+
/** Close (×) button rendered inside the popup. */
|
|
89
|
+
closeButton?: string;
|
|
90
|
+
};
|
|
84
91
|
}) {
|
|
85
92
|
return (
|
|
86
93
|
<DialogBase.Portal {...portalProps}>
|
|
87
|
-
<DialogBackdrop />
|
|
88
|
-
<DialogViewport>
|
|
94
|
+
<DialogBackdrop className={classNames?.backdrop} />
|
|
95
|
+
<DialogViewport className={classNames?.viewport}>
|
|
89
96
|
<DialogBase.Popup
|
|
90
97
|
data-slot="dialog-popup"
|
|
91
98
|
className={cn(
|
|
@@ -101,9 +108,8 @@ export function DialogPopup({
|
|
|
101
108
|
{!hideClose && (
|
|
102
109
|
<DialogBase.Close
|
|
103
110
|
aria-label="Close"
|
|
104
|
-
className="absolute right-2 top-2"
|
|
111
|
+
className={cn("absolute right-2 top-2", classNames?.closeButton)}
|
|
105
112
|
render={<Button size="icon" variant="ghost" />}
|
|
106
|
-
{...closeProps}
|
|
107
113
|
>
|
|
108
114
|
<X className="size-4" />
|
|
109
115
|
</DialogBase.Close>
|
|
@@ -201,12 +207,25 @@ export type DialogProps = Omit<DialogBase.Root.Props, "children"> & {
|
|
|
201
207
|
onClose?: () => void;
|
|
202
208
|
/** Hides the close (×) button inside the popup. */
|
|
203
209
|
hideClose?: boolean;
|
|
204
|
-
/**
|
|
205
|
-
closeProps?: DialogBase.Close.Props;
|
|
206
|
-
/** Props forwarded to the portal. */
|
|
207
|
-
portalProps?: DialogBase.Portal.Props;
|
|
208
|
-
/** Additional className forwarded to the popup panel. */
|
|
210
|
+
/** Styles the dialog popup panel. */
|
|
209
211
|
className?: string;
|
|
212
|
+
/** Styles applied to each internal slot. */
|
|
213
|
+
classNames?: {
|
|
214
|
+
/** Overlay rendered behind the dialog. */
|
|
215
|
+
backdrop?: string;
|
|
216
|
+
/** Full-screen container that centers the popup. */
|
|
217
|
+
viewport?: string;
|
|
218
|
+
/** Layout wrapper for title and description. */
|
|
219
|
+
header?: string;
|
|
220
|
+
/** Dialog heading. */
|
|
221
|
+
title?: string;
|
|
222
|
+
/** Supporting text below the title. */
|
|
223
|
+
description?: string;
|
|
224
|
+
/** Layout wrapper for footer content. */
|
|
225
|
+
footer?: string;
|
|
226
|
+
/** Close (×) button rendered inside the popup. */
|
|
227
|
+
closeButton?: string;
|
|
228
|
+
};
|
|
210
229
|
};
|
|
211
230
|
|
|
212
231
|
/**
|
|
@@ -216,6 +235,10 @@ export type DialogProps = Omit<DialogBase.Root.Props, "children"> & {
|
|
|
216
235
|
* making it composable with `<Tooltip>` at the primitive level.
|
|
217
236
|
* For full structural control, use the exported primitives directly.
|
|
218
237
|
*
|
|
238
|
+
* `className` styles the popup panel. `classNames` exposes the `backdrop`,
|
|
239
|
+
* `viewport`, `header`, `title`, `description`, `footer`, and `closeButton`
|
|
240
|
+
* slots for fine-grained tweaks without dropping to primitives.
|
|
241
|
+
*
|
|
219
242
|
* @example
|
|
220
243
|
* ```tsx
|
|
221
244
|
* <Dialog
|
|
@@ -236,9 +259,8 @@ export function Dialog({
|
|
|
236
259
|
footer,
|
|
237
260
|
onClose,
|
|
238
261
|
hideClose,
|
|
239
|
-
closeProps,
|
|
240
|
-
portalProps,
|
|
241
262
|
className,
|
|
263
|
+
classNames,
|
|
242
264
|
onOpenChange,
|
|
243
265
|
...props
|
|
244
266
|
}: DialogProps) {
|
|
@@ -254,19 +276,28 @@ export function Dialog({
|
|
|
254
276
|
<DialogPopup
|
|
255
277
|
className={className}
|
|
256
278
|
hideClose={hideClose}
|
|
257
|
-
|
|
258
|
-
|
|
279
|
+
classNames={{
|
|
280
|
+
backdrop: classNames?.backdrop,
|
|
281
|
+
viewport: classNames?.viewport,
|
|
282
|
+
closeButton: classNames?.closeButton,
|
|
283
|
+
}}
|
|
259
284
|
>
|
|
260
285
|
{(title || description) && (
|
|
261
|
-
<DialogHeader>
|
|
262
|
-
{title &&
|
|
286
|
+
<DialogHeader className={classNames?.header}>
|
|
287
|
+
{title && (
|
|
288
|
+
<DialogTitle className={classNames?.title}>{title}</DialogTitle>
|
|
289
|
+
)}
|
|
263
290
|
{description && (
|
|
264
|
-
<DialogDescription
|
|
291
|
+
<DialogDescription className={classNames?.description}>
|
|
292
|
+
{description}
|
|
293
|
+
</DialogDescription>
|
|
265
294
|
)}
|
|
266
295
|
</DialogHeader>
|
|
267
296
|
)}
|
|
268
297
|
{children}
|
|
269
|
-
{footer &&
|
|
298
|
+
{footer && (
|
|
299
|
+
<DialogFooter className={classNames?.footer}>{footer}</DialogFooter>
|
|
300
|
+
)}
|
|
270
301
|
</DialogPopup>
|
|
271
302
|
</DialogRoot>
|
|
272
303
|
);
|
|
@@ -1,36 +1,60 @@
|
|
|
1
|
-
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
2
|
import { Camera, File, FileArchive, ImageIcon, Trash } from "lucide-react";
|
|
3
|
-
import React from "react";
|
|
4
3
|
import { Button } from "../../components/button";
|
|
5
|
-
import { Dropzone } from ".";
|
|
6
|
-
import { FileType, FileTypeGroups, FileTypeValue } from "./file-types";
|
|
7
4
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
Dropzone,
|
|
6
|
+
DropzoneAcceptedFiles,
|
|
7
|
+
DropzoneContent,
|
|
8
|
+
DropzoneRejectedFiles,
|
|
9
|
+
DropzoneRoot,
|
|
10
|
+
DropzoneTrigger,
|
|
11
|
+
type FileError,
|
|
12
|
+
} from ".";
|
|
13
|
+
import { FileType, FileTypeGroups, type FileTypeValue } from "./file-types";
|
|
15
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Drag-and-drop file uploader built on react-dropzone. Supports controlled and uncontrolled
|
|
17
|
+
* file state, MIME type filtering, size limits, and custom file item rendering via render props.
|
|
18
|
+
*
|
|
19
|
+
* Compose with primitives (`DropzoneRoot`, `DropzoneTrigger`, `DropzoneContent`,
|
|
20
|
+
* `DropzoneAcceptedFiles`, `DropzoneRejectedFiles`) for full layout control.
|
|
21
|
+
*/
|
|
16
22
|
const meta: Meta<typeof Dropzone> = {
|
|
17
|
-
title: "
|
|
23
|
+
title: "Components/Dropzone",
|
|
18
24
|
component: Dropzone,
|
|
19
25
|
subcomponents: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
DropzoneRoot: DropzoneRoot as React.ComponentType<unknown>,
|
|
27
|
+
DropzoneTrigger: DropzoneTrigger as React.ComponentType<unknown>,
|
|
28
|
+
DropzoneContent: DropzoneContent as React.ComponentType<unknown>,
|
|
29
|
+
DropzoneAcceptedFiles:
|
|
30
|
+
DropzoneAcceptedFiles as React.ComponentType<unknown>,
|
|
31
|
+
DropzoneRejectedFiles:
|
|
32
|
+
DropzoneRejectedFiles as React.ComponentType<unknown>,
|
|
33
|
+
},
|
|
34
|
+
args: {
|
|
35
|
+
allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
|
|
36
|
+
},
|
|
37
|
+
argTypes: {
|
|
38
|
+
classNames: { control: false },
|
|
25
39
|
},
|
|
26
40
|
};
|
|
27
41
|
|
|
28
42
|
export default meta;
|
|
29
43
|
type Story = StoryObj<typeof Dropzone>;
|
|
30
44
|
|
|
31
|
-
export const Default: Story = {
|
|
45
|
+
export const Default: Story = {};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* `className` styles the outer wrapper. `classNames` exposes the `trigger`,
|
|
49
|
+
* `icon`, `label`, and `content` slots for fine-grained tweaks.
|
|
50
|
+
*/
|
|
51
|
+
export const WithClassNames: Story = {
|
|
32
52
|
args: {
|
|
33
|
-
|
|
53
|
+
classNames: {
|
|
54
|
+
trigger: "border-primary bg-primary/5",
|
|
55
|
+
icon: "text-primary",
|
|
56
|
+
label: "text-primary",
|
|
57
|
+
},
|
|
34
58
|
},
|
|
35
59
|
};
|
|
36
60
|
|
|
@@ -59,60 +83,20 @@ export const MultipleFiles: Story = {
|
|
|
59
83
|
};
|
|
60
84
|
|
|
61
85
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* ```typescript
|
|
66
|
-
* type FileError = {
|
|
67
|
-
* file: File; // El archivo que causó el error
|
|
68
|
-
* errorMessage: string; // Mensaje de error genérico
|
|
69
|
-
* errorCode: FileErrorCode; // Código para identificar el tipo de error
|
|
70
|
-
* };
|
|
71
|
-
* ```
|
|
72
|
-
*
|
|
73
|
-
* ### Códigos de Error Disponibles
|
|
74
|
-
* - `'INVALID_EXTENSION'`: El archivo no tiene una extensión permitida
|
|
75
|
-
* - `'FILE_TOO_LARGE'`: El archivo excede el tamaño máximo permitido
|
|
76
|
-
* - `'MAX_FILES_EXCEEDED'`: Se ha alcanzado el límite máximo de archivos
|
|
77
|
-
*
|
|
78
|
-
* ### Ejemplo de Uso
|
|
79
|
-
* ```typescript
|
|
80
|
-
* onError: (fileErrors) => {
|
|
81
|
-
* fileErrors.forEach(({ file, errorCode }) => {
|
|
82
|
-
* switch (errorCode) {
|
|
83
|
-
* case 'INVALID_EXTENSION':
|
|
84
|
-
* toast.error(`${file.name} no es un tipo válido`);
|
|
85
|
-
* break;
|
|
86
|
-
* case 'FILE_TOO_LARGE':
|
|
87
|
-
* toast.error(`${file.name} es muy grande`);
|
|
88
|
-
* break;
|
|
89
|
-
* case 'MAX_FILES_EXCEEDED':
|
|
90
|
-
* toast.error('Demasiados archivos');
|
|
91
|
-
* break;
|
|
92
|
-
* }
|
|
93
|
-
* });
|
|
94
|
-
* };
|
|
95
|
-
* ```
|
|
96
|
-
*
|
|
97
|
-
* ### Notas Importantes
|
|
98
|
-
* - El `onError` se ejecuta automáticamente cada vez que `validateFiles` encuentra errores
|
|
99
|
-
* - Puedes acceder al archivo original para mostrar información personalizada
|
|
100
|
-
* - Los mensajes de error son genéricos, puedes crear los tuyos propios basándote en el `errorCode`
|
|
86
|
+
* `onError` fires with a `FileError[]` whenever files are rejected. Each entry
|
|
87
|
+
* includes `errorCode` (`INVALID_EXTENSION`, `FILE_TOO_LARGE`, `MAX_FILES_EXCEEDED`)
|
|
88
|
+
* for granular error handling.
|
|
101
89
|
*/
|
|
102
90
|
export const WithErrorHandling: Story = {
|
|
103
91
|
args: {
|
|
104
92
|
multiple: true,
|
|
105
93
|
maxFiles: 2,
|
|
106
|
-
maxFileSize: 2 * 1024 * 1024,
|
|
94
|
+
maxFileSize: 2 * 1024 * 1024,
|
|
107
95
|
allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
|
|
108
96
|
onError: (fileErrors: FileError[]) => {
|
|
109
|
-
console.log("Errores de archivos:", fileErrors);
|
|
110
97
|
alert(
|
|
111
98
|
fileErrors
|
|
112
|
-
.map(
|
|
113
|
-
({ file, errorMessage, errorCode }) =>
|
|
114
|
-
`${file.name}: ${errorMessage} (${errorCode})`,
|
|
115
|
-
)
|
|
99
|
+
.map(({ file, errorCode }) => `${file.name}: ${errorCode}`)
|
|
116
100
|
.join("\n"),
|
|
117
101
|
);
|
|
118
102
|
},
|
|
@@ -174,37 +158,33 @@ export const CustomFilePreview: Story = {
|
|
|
174
158
|
};
|
|
175
159
|
|
|
176
160
|
/**
|
|
177
|
-
*
|
|
161
|
+
* Use primitives directly for full layout control. `DropzoneTrigger` with `render`
|
|
162
|
+
* turns any element into the drag target and click handler.
|
|
178
163
|
*
|
|
179
|
-
* Este ejemplo muestra como armar el componente Dropzone usando los componentes primitivos.
|
|
180
|
-
*
|
|
181
|
-
* ##### Estructura
|
|
182
164
|
* ```tsx
|
|
183
|
-
* <
|
|
184
|
-
* <
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* </UploadContent>
|
|
191
|
-
* </UploadRoot>
|
|
165
|
+
* <DropzoneRoot>
|
|
166
|
+
* <DropzoneTrigger render={<Button>Upload</Button>} />
|
|
167
|
+
* <DropzoneContent>
|
|
168
|
+
* <DropzoneAcceptedFiles />
|
|
169
|
+
* <DropzoneRejectedFiles />
|
|
170
|
+
* </DropzoneContent>
|
|
171
|
+
* </DropzoneRoot>
|
|
192
172
|
* ```
|
|
193
173
|
*/
|
|
194
|
-
export const
|
|
195
|
-
render: () =>
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
174
|
+
export const Primitive: Story = {
|
|
175
|
+
render: () => (
|
|
176
|
+
<DropzoneRoot>
|
|
177
|
+
<DropzoneTrigger
|
|
178
|
+
render={
|
|
199
179
|
<Button>
|
|
200
180
|
<Camera /> Upload
|
|
201
181
|
</Button>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
</
|
|
208
|
-
|
|
209
|
-
|
|
182
|
+
}
|
|
183
|
+
/>
|
|
184
|
+
<DropzoneContent>
|
|
185
|
+
<DropzoneAcceptedFiles />
|
|
186
|
+
<DropzoneRejectedFiles />
|
|
187
|
+
</DropzoneContent>
|
|
188
|
+
</DropzoneRoot>
|
|
189
|
+
),
|
|
210
190
|
};
|