@boxcustodia/library 2.0.0-alpha.12 → 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.
Files changed (174) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1087 -720
  3. package/dist/index.es.js +7011 -56097
  4. package/dist/theme.css +1 -1
  5. package/package.json +34 -26
  6. package/src/__doc__/Examples.tsx +1 -1
  7. package/src/__doc__/Intro.mdx +3 -3
  8. package/src/__doc__/Tabs.mdx +112 -0
  9. package/src/__doc__/V2.mdx +1246 -0
  10. package/src/components/accordion/accordion.stories.tsx +143 -0
  11. package/src/components/accordion/accordion.tsx +135 -0
  12. package/src/components/accordion/index.ts +1 -0
  13. package/src/components/alert/alert.stories.tsx +24 -4
  14. package/src/components/alert/alert.tsx +17 -9
  15. package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
  16. package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
  17. package/src/components/alert-dialog/alert-dialog.tsx +58 -10
  18. package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
  19. package/src/components/auto-complete/auto-complete.tsx +420 -68
  20. package/src/components/auto-complete/index.ts +0 -1
  21. package/src/components/avatar/avatar.stories.tsx +162 -21
  22. package/src/components/avatar/avatar.tsx +79 -20
  23. package/src/components/button/button.stories.tsx +219 -294
  24. package/src/components/button/button.test.tsx +10 -17
  25. package/src/components/button/button.tsx +78 -19
  26. package/src/components/button/components/base-button.tsx +30 -53
  27. package/src/components/button/index.ts +0 -1
  28. package/src/components/calendar/calendar.stories.tsx +1 -1
  29. package/src/components/calendar/calendar.tsx +4 -4
  30. package/src/components/card/card.stories.tsx +141 -69
  31. package/src/components/card/card.tsx +155 -54
  32. package/src/components/center/center.stories.tsx +22 -39
  33. package/src/components/checkbox/checkbox.stories.tsx +25 -5
  34. package/src/components/checkbox/checkbox.tsx +76 -15
  35. package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
  36. package/src/components/checkbox-group/checkbox-group.tsx +84 -3
  37. package/src/components/combobox/combobox.stories.tsx +33 -23
  38. package/src/components/combobox/combobox.tsx +99 -77
  39. package/src/components/date-picker/date-input.stories.tsx +14 -6
  40. package/src/components/date-picker/date-input.tsx +2 -2
  41. package/src/components/date-picker/date-picker.model.ts +13 -4
  42. package/src/components/date-picker/date-picker.stories.tsx +38 -12
  43. package/src/components/date-picker/date-picker.tsx +28 -14
  44. package/src/components/dialog/dialog.stories.tsx +18 -0
  45. package/src/components/dialog/dialog.test.tsx +1 -1
  46. package/src/components/dialog/dialog.tsx +51 -20
  47. package/src/components/divider/divider.stories.tsx +126 -51
  48. package/src/components/divider/divider.tsx +16 -16
  49. package/src/components/dropzone/dropzone.stories.tsx +71 -90
  50. package/src/components/dropzone/dropzone.tsx +383 -105
  51. package/src/components/dropzone/index.ts +0 -1
  52. package/src/components/empty/empty.stories.tsx +165 -0
  53. package/src/components/empty/empty.tsx +156 -0
  54. package/src/components/empty/index.ts +1 -0
  55. package/src/components/field/field.stories.tsx +227 -4
  56. package/src/components/field/field.tsx +77 -42
  57. package/src/components/form/form.stories.tsx +320 -197
  58. package/src/components/form/form.tsx +3 -23
  59. package/src/components/index.ts +2 -6
  60. package/src/components/input/input.stories.tsx +5 -5
  61. package/src/components/input/input.tsx +4 -4
  62. package/src/components/kbd/kbd.stories.tsx +1 -0
  63. package/src/components/label/label.stories.tsx +16 -0
  64. package/src/components/label/label.tsx +13 -2
  65. package/src/components/loader/loader.stories.tsx +7 -5
  66. package/src/components/loader/loader.tsx +8 -3
  67. package/src/components/menu/menu-primitives.tsx +207 -196
  68. package/src/components/menu/menu.stories.tsx +276 -146
  69. package/src/components/menu/menu.tsx +146 -54
  70. package/src/components/number-input/number-input.stories.tsx +27 -4
  71. package/src/components/number-input/number-input.test.tsx +2 -2
  72. package/src/components/number-input/number-input.tsx +31 -33
  73. package/src/components/otp/index.ts +1 -0
  74. package/src/components/otp/otp.stories.tsx +209 -0
  75. package/src/components/otp/otp.tsx +100 -0
  76. package/src/components/pagination/index.ts +1 -0
  77. package/src/components/pagination/pagination.model.ts +2 -0
  78. package/src/components/pagination/pagination.stories.tsx +154 -59
  79. package/src/components/pagination/pagination.test.tsx +122 -57
  80. package/src/components/pagination/pagination.tsx +575 -77
  81. package/src/components/password/password.stories.tsx +18 -3
  82. package/src/components/password/password.tsx +29 -9
  83. package/src/components/popover/popover.stories.tsx +26 -5
  84. package/src/components/popover/popover.tsx +15 -23
  85. package/src/components/progress/progress.stories.tsx +1 -0
  86. package/src/components/radio-group/index.ts +1 -0
  87. package/src/components/radio-group/radio-group.stories.tsx +251 -0
  88. package/src/components/radio-group/radio-group.tsx +212 -0
  89. package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
  90. package/src/components/select/select.stories.tsx +118 -19
  91. package/src/components/select/select.tsx +67 -62
  92. package/src/components/skeleton/skeleton.stories.tsx +1 -0
  93. package/src/components/stack/stack.stories.tsx +179 -89
  94. package/src/components/stack/stack.tsx +2 -2
  95. package/src/components/stepper/index.ts +1 -1
  96. package/src/components/stepper/stepper.stories.tsx +767 -83
  97. package/src/components/stepper/stepper.test.tsx +18 -18
  98. package/src/components/stepper/stepper.tsx +554 -0
  99. package/src/components/switch/switch.stories.tsx +15 -1
  100. package/src/components/switch/switch.tsx +17 -4
  101. package/src/components/table/index.ts +0 -2
  102. package/src/components/table/table.stories.tsx +131 -18
  103. package/src/components/table/table.test.tsx +1 -1
  104. package/src/components/table/table.tsx +183 -77
  105. package/src/components/tabs/tabs.stories.tsx +373 -155
  106. package/src/components/tabs/tabs.test.tsx +12 -12
  107. package/src/components/tabs/tabs.tsx +72 -149
  108. package/src/components/tag/index.ts +0 -1
  109. package/src/components/tag/tag.stories.tsx +155 -120
  110. package/src/components/tag/tag.tsx +47 -95
  111. package/src/components/textarea/textarea.stories.tsx +8 -22
  112. package/src/components/textarea/textarea.tsx +17 -79
  113. package/src/components/timeline/timeline.stories.tsx +323 -42
  114. package/src/components/timeline/timeline.tsx +359 -132
  115. package/src/components/toast/toast.stories.tsx +1 -0
  116. package/src/components/tooltip/tooltip.tsx +11 -9
  117. package/src/components/tree/index.ts +0 -1
  118. package/src/components/tree/tree.stories.tsx +365 -408
  119. package/src/components/tree/tree.test.tsx +163 -0
  120. package/src/components/tree/tree.tsx +212 -36
  121. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
  122. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
  123. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
  124. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
  125. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
  126. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
  127. package/src/hooks/usePagination/usePagination.tsx +36 -24
  128. package/src/styles/theme.css +1 -1
  129. package/src/utils/form.tsx +67 -37
  130. package/src/utils/index.ts +1 -1
  131. package/src/__doc__/Migration.mdx +0 -475
  132. package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
  133. package/src/components/background-image/background-image.stories.tsx +0 -21
  134. package/src/components/background-image/background-image.test.tsx +0 -29
  135. package/src/components/background-image/background-image.tsx +0 -23
  136. package/src/components/background-image/index.ts +0 -1
  137. package/src/components/button/button.variants.ts +0 -44
  138. package/src/components/button/components/loader-overlay.tsx +0 -21
  139. package/src/components/button/components/loading-icon.tsx +0 -47
  140. package/src/components/dropzone/upload-primitives.tsx +0 -310
  141. package/src/components/dropzone/use-dropzone.ts +0 -122
  142. package/src/components/empty-state/empty-state.stories.tsx +0 -56
  143. package/src/components/empty-state/empty-state.tsx +0 -39
  144. package/src/components/empty-state/index.ts +0 -1
  145. package/src/components/heading/heading.stories.tsx +0 -74
  146. package/src/components/heading/heading.tsx +0 -28
  147. package/src/components/heading/heading.variants.ts +0 -27
  148. package/src/components/heading/index.ts +0 -1
  149. package/src/components/kbd/kbd.variants.ts +0 -26
  150. package/src/components/menu/util/render-menu-item.tsx +0 -54
  151. package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
  152. package/src/components/multi-select/index.ts +0 -1
  153. package/src/components/multi-select/multi-select.stories.tsx +0 -294
  154. package/src/components/multi-select/multi-select.tsx +0 -300
  155. package/src/components/multi-select/multi-select.variants.ts +0 -22
  156. package/src/components/pagination/components/pagination-option.tsx +0 -27
  157. package/src/components/show/index.ts +0 -1
  158. package/src/components/show/show.stories.tsx +0 -197
  159. package/src/components/show/show.test.tsx +0 -41
  160. package/src/components/show/show.tsx +0 -16
  161. package/src/components/stepper/Stepper.tsx +0 -190
  162. package/src/components/stepper/context/stepper-context.tsx +0 -11
  163. package/src/components/table/table-primitives.tsx +0 -122
  164. package/src/components/table/table.model.ts +0 -20
  165. package/src/components/table-pagination/index.ts +0 -2
  166. package/src/components/table-pagination/table-pagination.model.ts +0 -2
  167. package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
  168. package/src/components/table-pagination/table-pagination.test.tsx +0 -32
  169. package/src/components/table-pagination/table-pagination.tsx +0 -108
  170. package/src/components/tabs/context/tabs-context.tsx +0 -14
  171. package/src/components/tag/tag.variants.ts +0 -31
  172. package/src/components/timeline/timeline-status.ts +0 -5
  173. package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
  174. 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 const DatePicker = (props: DatePickerProps) => {
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,
@@ -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 className="p-2" data-slot={`${slot}-footer`}>
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
- onChange,
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) => onChange?.(next ?? null),
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
- onChange,
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) => onChange?.(next ?? EMPTY_RANGE),
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
- onChange,
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) => 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.
@@ -11,7 +11,7 @@ import {
11
11
  DialogTitle,
12
12
  DialogTrigger,
13
13
  } from "../../components";
14
- import { click } from "../../utils";
14
+ import { click } from "../../utils/tests";
15
15
 
16
16
  describe("Dialog component", () => {
17
17
  const setup = (
@@ -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
- * Pass `closeProps` to customise the close button.
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
- /** Props forwarded to the close button primitive. */
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
- closeProps={closeProps}
258
- portalProps={portalProps}
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 && <DialogTitle>{title}</DialogTitle>}
286
+ <DialogHeader className={classNames?.header}>
287
+ {title && (
288
+ <DialogTitle className={classNames?.title}>{title}</DialogTitle>
289
+ )}
263
290
  {description && (
264
- <DialogDescription>{description}</DialogDescription>
291
+ <DialogDescription className={classNames?.description}>
292
+ {description}
293
+ </DialogDescription>
265
294
  )}
266
295
  </DialogHeader>
267
296
  )}
268
297
  {children}
269
- {footer && <DialogFooter>{footer}</DialogFooter>}
298
+ {footer && (
299
+ <DialogFooter className={classNames?.footer}>{footer}</DialogFooter>
300
+ )}
270
301
  </DialogPopup>
271
302
  </DialogRoot>
272
303
  );
@@ -1,70 +1,145 @@
1
- import { Meta, StoryObj } from "@storybook/react-vite";
2
- import { CatIcon, Github, PencilIcon } from "lucide-react";
3
- import { Button, Divider } from "../../components";
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Divider } from "../../components";
4
3
 
4
+ /**
5
+ * Visual separator between sections or inline elements.
6
+ *
7
+ * Built on the Base UI `Separator` primitive. Supports `orientation` (`horizontal` | `vertical`)
8
+ * and a `render` prop for polymorphic composition. The DOM element receives `data-orientation`
9
+ * which can be used as a CSS selector for conditional styles.
10
+ */
5
11
  const meta: Meta<typeof Divider> = {
6
- title: "others/Divider",
12
+ title: "Components/Divider",
7
13
  component: Divider,
14
+ args: {
15
+ orientation: "horizontal",
16
+ },
17
+ argTypes: {
18
+ orientation: {
19
+ control: "radio",
20
+ options: ["horizontal", "vertical"],
21
+ },
22
+ },
23
+ parameters: {
24
+ layout: "centered",
25
+ },
8
26
  };
9
27
 
10
28
  export default meta;
11
- type Story = StoryObj<typeof meta>;
29
+ type Story = StoryObj<typeof Divider>;
30
+
31
+ export const Default: Story = {
32
+ render: () => (
33
+ <div className="w-72 rounded-lg border p-4 text-sm">
34
+ <div className="flex items-center justify-between">
35
+ <span className="font-medium">Deployment #847</span>
36
+ <span className="text-xs text-green-500">● Live</span>
37
+ </div>
38
+ <Divider className="my-3" />
39
+ <div className="space-y-1.5 text-muted-foreground">
40
+ <div className="flex justify-between">
41
+ <span>Triggered by</span>
42
+ <span className="text-foreground">fbaigorria</span>
43
+ </div>
44
+ <div className="flex justify-between">
45
+ <span>Branch</span>
46
+ <span className="text-foreground">develop</span>
47
+ </div>
48
+ <div className="flex justify-between">
49
+ <span>Duration</span>
50
+ <span className="text-foreground">1m 43s</span>
51
+ </div>
52
+ </div>
53
+ <Divider className="my-3" />
54
+ <div className="flex items-center justify-center gap-4 text-muted-foreground">
55
+ <span>2 warnings</span>
56
+ <Divider orientation="vertical" className="h-4" />
57
+ <span>0 errors</span>
58
+ <Divider orientation="vertical" className="h-4" />
59
+ <span>127 tests</span>
60
+ </div>
61
+ </div>
62
+ ),
63
+ };
12
64
 
65
+ /**
66
+ * Default orientation. Renders a full-width 1px horizontal line via `data-[orientation=horizontal]`.
67
+ * Commonly used to separate named sections inside a panel or card.
68
+ */
13
69
  export const Horizontal: Story = {
70
+ render: () => (
71
+ <div className="w-72 rounded-lg border p-4 space-y-4">
72
+ <div>
73
+ <p className="text-sm font-medium">Profile</p>
74
+ <p className="text-xs text-muted-foreground">Franco Baigorria</p>
75
+ </div>
76
+ <Divider />
77
+ <div>
78
+ <p className="text-sm font-medium">Team</p>
79
+ <p className="text-xs text-muted-foreground">Engineering</p>
80
+ </div>
81
+ <Divider />
82
+ <div>
83
+ <p className="text-sm font-medium">Role</p>
84
+ <p className="text-xs text-muted-foreground">Frontend Architect</p>
85
+ </div>
86
+ </div>
87
+ ),
88
+ };
89
+
90
+ /**
91
+ * In vertical orientation the component uses `self-stretch` to fill the full height of
92
+ * the flex container. Wrapping in a `div` with `flex` and a defined height is the expected pattern.
93
+ */
94
+ export const Vertical: Story = {
14
95
  args: {
15
- orientation: "horizontal",
96
+ orientation: "vertical",
16
97
  },
17
- decorators: [
18
- (Story) => (
19
- <div className="w-20 m-auto rounded flex flex-col items-center gap-2">
20
- <Button size="icon">
21
- <PencilIcon />
22
- </Button>
23
- <Story />
24
- <Button size="icon">
25
- <CatIcon />
26
- </Button>
27
- <Story />
28
- <Button size="icon">
29
- <Github />
30
- </Button>
31
- </div>
32
- ),
33
- ],
98
+ render: (args) => (
99
+ <div className="flex h-10 items-center gap-4">
100
+ <span className="text-sm">Section A</span>
101
+ <Divider {...args} />
102
+ <span className="text-sm">Section B</span>
103
+ <Divider {...args} />
104
+ <span className="text-sm">Section C</span>
105
+ </div>
106
+ ),
34
107
  };
35
108
 
36
109
  /**
37
- * Para poder desplegar el componente `Divider` de manera vertical, se debe utilizar el atributo `orientation` con el valor `vertical`
38
- * y setear una altura en el contenedor
110
+ * The `render` prop replaces the rendered DOM element. Useful for swapping semantics
111
+ * to `<hr>` or any other element without losing component styles.
39
112
  *
40
113
  * ```tsx
41
- * <div className="h-16"> // <- Altura fija
42
- * <PencilIcon />
43
- * <Divider orientation="vertical" /> // <- vertical
44
- * <CatIcon />
45
- * </div>
114
+ * <Divider render={<hr />} />
46
115
  * ```
47
116
  */
48
-
49
- export const Vertical: Story = {
50
- args: {
51
- orientation: "vertical",
117
+ export const Render: Story = {
118
+ render: (args) => (
119
+ <div className="w-64">
120
+ <Divider {...args} render={<hr />} />
121
+ </div>
122
+ ),
123
+ parameters: {
124
+ docs: {
125
+ source: {
126
+ code: `<Divider render={<hr />} />`,
127
+ },
128
+ },
52
129
  },
53
- decorators: [
54
- (Story) => (
55
- <div className="h-16 w-36 m-auto rounded flex items-center gap-2">
56
- <Button size="icon">
57
- <PencilIcon />
58
- </Button>
59
- <Story />
60
- <Button size="icon">
61
- <CatIcon />
62
- </Button>
63
- <Story />
64
- <Button size="icon">
65
- <Github />
66
- </Button>
67
- </div>
68
- ),
69
- ],
130
+ };
131
+
132
+ /**
133
+ * `className` accepts Tailwind classes to override color, thickness, or spacing.
134
+ * `data-[orientation=horizontal]` and `data-[orientation=vertical]` are available
135
+ * as CSS selectors for advanced conditional styles.
136
+ */
137
+ export const Custom: Story = {
138
+ render: () => (
139
+ <div className="space-y-4 w-64">
140
+ <Divider className="bg-primary" />
141
+ <Divider className="bg-error" />
142
+ <Divider className="border-t border-dashed border-input bg-transparent" />
143
+ </div>
144
+ ),
70
145
  };
@@ -1,23 +1,23 @@
1
- import { HTMLProps } from "react";
2
- import { cn } from "../../lib";
3
-
4
- type Props = {
5
- orientation?: "horizontal" | "vertical";
6
- } & HTMLProps<HTMLHRElement>;
7
-
8
- export const Divider = (props: Props) => {
9
- const { className, orientation = "horizontal" } = props;
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
2
+ import type React from "react";
3
+ import { cn } from "tailwind-variants";
10
4
 
5
+ export function Divider({
6
+ className,
7
+ orientation = "horizontal",
8
+ ...props
9
+ }: SeparatorPrimitive.Props): React.ReactElement {
11
10
  return (
12
- <hr
13
- {...props}
14
- aria-orientation={orientation}
11
+ <SeparatorPrimitive
15
12
  className={cn(
16
- "shrink-0 bg-input",
17
- orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
13
+ "shrink-0 bg-input data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:not-[[class^='h-']]:not-[[class*='_h-']]:self-stretch",
18
14
  className,
19
15
  )}
20
- data-slot="divider"
16
+ data-slot="separator"
17
+ orientation={orientation}
18
+ {...props}
21
19
  />
22
20
  );
23
- };
21
+ }
22
+
23
+ export { SeparatorPrimitive };