@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.
Files changed (174) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1083 -717
  3. package/dist/index.es.js +7059 -56179
  4. package/dist/theme.css +1 -1
  5. package/package.json +34 -26
  6. package/src/__doc__/Changelog.mdx +6 -6
  7. package/src/__doc__/Examples.tsx +1 -1
  8. package/src/__doc__/Intro.mdx +3 -3
  9. package/src/__doc__/Tabs.mdx +112 -0
  10. package/src/__doc__/V2.mdx +1245 -0
  11. package/src/components/accordion/accordion.stories.tsx +143 -0
  12. package/src/components/accordion/accordion.tsx +135 -0
  13. package/src/components/accordion/index.ts +1 -0
  14. package/src/components/alert/alert.stories.tsx +24 -4
  15. package/src/components/alert/alert.tsx +17 -9
  16. package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
  17. package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
  18. package/src/components/alert-dialog/alert-dialog.tsx +58 -10
  19. package/src/components/auto-complete/auto-complete.stories.tsx +615 -200
  20. package/src/components/auto-complete/auto-complete.tsx +420 -68
  21. package/src/components/auto-complete/index.ts +0 -1
  22. package/src/components/avatar/avatar.stories.tsx +162 -21
  23. package/src/components/avatar/avatar.tsx +79 -20
  24. package/src/components/button/button.stories.tsx +236 -294
  25. package/src/components/button/button.test.tsx +10 -17
  26. package/src/components/button/button.tsx +53 -18
  27. package/src/components/button/components/base-button.tsx +25 -53
  28. package/src/components/button/index.ts +0 -1
  29. package/src/components/calendar/calendar.stories.tsx +1 -1
  30. package/src/components/calendar/calendar.tsx +4 -4
  31. package/src/components/card/card.stories.tsx +140 -69
  32. package/src/components/card/card.tsx +155 -54
  33. package/src/components/center/center.stories.tsx +22 -39
  34. package/src/components/checkbox/checkbox.stories.tsx +25 -5
  35. package/src/components/checkbox/checkbox.tsx +76 -15
  36. package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
  37. package/src/components/checkbox-group/checkbox-group.tsx +84 -3
  38. package/src/components/combobox/combobox.stories.tsx +33 -23
  39. package/src/components/combobox/combobox.tsx +120 -104
  40. package/src/components/date-picker/date-input.stories.tsx +14 -6
  41. package/src/components/date-picker/date-input.tsx +3 -3
  42. package/src/components/date-picker/date-picker.model.ts +13 -4
  43. package/src/components/date-picker/date-picker.stories.tsx +38 -12
  44. package/src/components/date-picker/date-picker.tsx +29 -15
  45. package/src/components/dialog/dialog.stories.tsx +18 -0
  46. package/src/components/dialog/dialog.test.tsx +1 -1
  47. package/src/components/dialog/dialog.tsx +51 -20
  48. package/src/components/divider/divider.stories.tsx +6 -0
  49. package/src/components/dropzone/dropzone.stories.tsx +70 -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 +164 -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 +226 -3
  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 +5 -5
  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 +275 -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 +29 -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 +153 -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 +26 -10
  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 +766 -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 +372 -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 +147 -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 +322 -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 +364 -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 +69 -37
  130. package/src/utils/index.ts +1 -1
  131. package/src/__doc__/Migration.mdx +0 -451
  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,182 +1,399 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useState } from "react";
3
2
  import {
4
- Button,
5
- Input,
6
- TabContent,
7
- TabList,
8
- Tabs,
9
- TabTrigger,
10
- } from "../../components";
11
- import { useStep } from "../../hooks";
3
+ BellIcon,
4
+ CreditCardIcon,
5
+ LockIcon,
6
+ PaletteIcon,
7
+ UserIcon,
8
+ } from "lucide-react";
9
+ import React from "react";
10
+ import { Button } from "../../components";
11
+ import { Tabs, TabsList, TabsPanel, TabsPrimitive, TabsTab } from "./tabs";
12
12
 
13
+ /**
14
+ * Accessible tab component built on Base UI Tabs primitives.
15
+ *
16
+ * `variant` lives on `TabsList`, not on `Tabs`. Two variants are available:
17
+ * `"underline"` (default) renders a line indicator at the edge of the active
18
+ * tab; `"background"` renders a rounded background behind the active tab.
19
+ *
20
+ * Use `value` + `onValueChange` for controlled state.
21
+ * Set `orientation="vertical"` on `Tabs` to switch to a column layout.
22
+ */
13
23
  const meta: Meta<typeof Tabs> = {
14
- title: "Data display/Tabs",
24
+ title: "Components/Tabs",
15
25
  component: Tabs,
16
- args: {},
26
+ parameters: {
27
+ layout: "centered",
28
+ },
29
+ subcomponents: {
30
+ TabsList: TabsList as React.ComponentType<unknown>,
31
+ TabsTab: TabsTab as React.ComponentType<unknown>,
32
+ TabsPanel: TabsPanel as React.ComponentType<unknown>,
33
+ },
17
34
  };
18
35
 
19
36
  export default meta;
20
37
  type Story = StoryObj<typeof Tabs>;
21
38
 
22
- const AccountStep = () => {
23
- return (
24
- <>
25
- <Input name="name" />
26
- <Input name="lastname" />
27
- </>
28
- );
39
+ // ---------------------------------------------------------------------------
40
+ // Shared content blocks
41
+ // ---------------------------------------------------------------------------
42
+
43
+ const AccountPanel = () => (
44
+ <div className="space-y-4 py-2">
45
+ <div className="flex items-center gap-4">
46
+ <div className="size-14 rounded-full bg-muted flex items-center justify-center text-muted-foreground">
47
+ <UserIcon className="size-6" />
48
+ </div>
49
+ <div>
50
+ <p className="font-medium">John Doe</p>
51
+ <p className="text-sm text-muted-foreground">john@example.com</p>
52
+ </div>
53
+ </div>
54
+ <div className="grid gap-3">
55
+ <div className="grid gap-1.5">
56
+ <label className="text-sm font-medium">Display name</label>
57
+ <input
58
+ defaultValue="John Doe"
59
+ className="h-9 rounded-md border bg-background px-3 text-sm outline-none focus:ring-2 focus:ring-ring"
60
+ />
61
+ </div>
62
+ <div className="grid gap-1.5">
63
+ <label className="text-sm font-medium">Email</label>
64
+ <input
65
+ defaultValue="john@example.com"
66
+ type="email"
67
+ className="h-9 rounded-md border bg-background px-3 text-sm outline-none focus:ring-2 focus:ring-ring"
68
+ />
69
+ </div>
70
+ </div>
71
+ <div className="flex justify-end">
72
+ <Button size="sm">Save changes</Button>
73
+ </div>
74
+ </div>
75
+ );
76
+
77
+ const SecurityPanel = () => (
78
+ <div className="space-y-3 py-2">
79
+ {[
80
+ {
81
+ label: "Password",
82
+ meta: "Last changed 3 months ago",
83
+ action: "Change",
84
+ },
85
+ {
86
+ label: "Two-factor authentication",
87
+ meta: "Add an extra layer of security",
88
+ action: "Enable",
89
+ },
90
+ {
91
+ label: "Active sessions",
92
+ meta: "2 devices currently signed in",
93
+ action: "Manage",
94
+ },
95
+ ].map(({ label, meta, action }) => (
96
+ <div
97
+ key={label}
98
+ className="flex items-center justify-between rounded-lg border p-3.5"
99
+ >
100
+ <div>
101
+ <p className="text-sm font-medium">{label}</p>
102
+ <p className="text-xs text-muted-foreground">{meta}</p>
103
+ </div>
104
+ <Button variant="outline" size="sm">
105
+ {action}
106
+ </Button>
107
+ </div>
108
+ ))}
109
+ </div>
110
+ );
111
+
112
+ const NotificationsPanel = () => (
113
+ <div className="space-y-3 py-2">
114
+ {[
115
+ { title: "Email notifications", desc: "Weekly digest and alerts" },
116
+ { title: "Push notifications", desc: "Desktop and mobile" },
117
+ { title: "Security alerts", desc: "Unusual sign-in activity" },
118
+ ].map(({ title, desc }) => (
119
+ <div key={title} className="flex items-center justify-between py-1">
120
+ <div>
121
+ <p className="text-sm font-medium">{title}</p>
122
+ <p className="text-xs text-muted-foreground">{desc}</p>
123
+ </div>
124
+ <div className="size-5 rounded border border-input bg-background" />
125
+ </div>
126
+ ))}
127
+ </div>
128
+ );
129
+
130
+ const BillingPanel = () => (
131
+ <div className="space-y-3 py-2">
132
+ <div className="rounded-lg border p-4 flex items-start gap-3">
133
+ <CreditCardIcon className="size-5 mt-0.5 text-muted-foreground" />
134
+ <div className="flex-1">
135
+ <p className="text-sm font-medium">Pro plan · $12/month</p>
136
+ <p className="text-xs text-muted-foreground">Renews on Jun 1, 2025</p>
137
+ </div>
138
+ <Button variant="outline" size="sm">
139
+ Manage
140
+ </Button>
141
+ </div>
142
+ <div className="rounded-lg border p-4">
143
+ <p className="text-sm font-medium mb-2">Payment method</p>
144
+ <p className="text-sm text-muted-foreground">Visa ending in 4242</p>
145
+ </div>
146
+ </div>
147
+ );
148
+
149
+ const AppearancePanel = () => (
150
+ <div className="space-y-3 py-2">
151
+ <div>
152
+ <p className="text-sm font-medium mb-2">Theme</p>
153
+ <div className="flex gap-2">
154
+ {["Light", "Dark", "System"].map((t) => (
155
+ <button
156
+ key={t}
157
+ className="rounded-md border px-3 py-1.5 text-sm data-[active]:border-primary data-[active]:bg-primary/5 cursor-pointer"
158
+ data-active={t === "Light" ? "" : undefined}
159
+ >
160
+ {t}
161
+ </button>
162
+ ))}
163
+ </div>
164
+ </div>
165
+ <div>
166
+ <p className="text-sm font-medium mb-2">Density</p>
167
+ <div className="flex gap-2">
168
+ {["Compact", "Comfortable", "Spacious"].map((d) => (
169
+ <button
170
+ key={d}
171
+ className="rounded-md border px-3 py-1.5 text-sm cursor-pointer"
172
+ >
173
+ {d}
174
+ </button>
175
+ ))}
176
+ </div>
177
+ </div>
178
+ </div>
179
+ );
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Stories
183
+ // ---------------------------------------------------------------------------
184
+
185
+ export const Default: Story = {
186
+ render: () => (
187
+ <Tabs defaultValue="account" className="w-[480px] h-[340px]">
188
+ <TabsList>
189
+ <TabsTab value="account">Account</TabsTab>
190
+ <TabsTab value="security">Security</TabsTab>
191
+ <TabsTab value="notifications">Notifications</TabsTab>
192
+ </TabsList>
193
+ <TabsPanel value="account">
194
+ <AccountPanel />
195
+ </TabsPanel>
196
+ <TabsPanel value="security">
197
+ <SecurityPanel />
198
+ </TabsPanel>
199
+ <TabsPanel value="notifications">
200
+ <NotificationsPanel />
201
+ </TabsPanel>
202
+ </Tabs>
203
+ ),
29
204
  };
30
205
 
31
- const PasswordStep = () => {
32
- return (
33
- <>
34
- <Input name="password" type="password" />
35
- <Input name="confirmation" type="password" />
36
- </>
37
- );
206
+ export const Background: Story = {
207
+ render: () => (
208
+ <Tabs defaultValue="account" className="w-[480px] h-[340px]">
209
+ <TabsList variant="background">
210
+ <TabsTab value="account">Account</TabsTab>
211
+ <TabsTab value="security">Security</TabsTab>
212
+ <TabsTab value="notifications">Notifications</TabsTab>
213
+ </TabsList>
214
+ <TabsPanel value="account">
215
+ <AccountPanel />
216
+ </TabsPanel>
217
+ <TabsPanel value="security">
218
+ <SecurityPanel />
219
+ </TabsPanel>
220
+ <TabsPanel value="notifications">
221
+ <NotificationsPanel />
222
+ </TabsPanel>
223
+ </Tabs>
224
+ ),
38
225
  };
39
226
 
40
- const FileStep = () => {
41
- return (
42
- <>
43
- <Input name="file" type="file" />
44
- </>
45
- );
227
+ export const Vertical: Story = {
228
+ render: () => (
229
+ <Tabs
230
+ defaultValue="account"
231
+ orientation="vertical"
232
+ className="w-[620px] h-[300px]"
233
+ >
234
+ <TabsList className="w-44 shrink-0">
235
+ <TabsTab value="account">Account</TabsTab>
236
+ <TabsTab value="security">Security</TabsTab>
237
+ <TabsTab value="billing">Billing</TabsTab>
238
+ <TabsTab value="appearance">Appearance</TabsTab>
239
+ </TabsList>
240
+ <div className="flex-1 min-w-0">
241
+ <TabsPanel value="account">
242
+ <AccountPanel />
243
+ </TabsPanel>
244
+ <TabsPanel value="security">
245
+ <SecurityPanel />
246
+ </TabsPanel>
247
+ <TabsPanel value="billing">
248
+ <BillingPanel />
249
+ </TabsPanel>
250
+ <TabsPanel value="appearance">
251
+ <AppearancePanel />
252
+ </TabsPanel>
253
+ </div>
254
+ </Tabs>
255
+ ),
46
256
  };
47
257
 
48
- const steps = [
49
- {
50
- value: "account",
51
- content: <AccountStep />,
52
- },
53
- {
54
- value: "password",
55
- content: <PasswordStep />,
56
- },
57
- {
58
- value: "config",
59
- content: <FileStep />,
60
- },
61
- ];
258
+ export const PillVertical: Story = {
259
+ render: () => (
260
+ <Tabs
261
+ defaultValue="account"
262
+ orientation="vertical"
263
+ className="w-[620px] h-[300px]"
264
+ >
265
+ <TabsList variant="background" className="w-44 shrink-0">
266
+ <TabsTab value="account">Account</TabsTab>
267
+ <TabsTab value="security">Security</TabsTab>
268
+ <TabsTab value="billing">Billing</TabsTab>
269
+ <TabsTab value="appearance">Appearance</TabsTab>
270
+ </TabsList>
271
+ <div className="flex-1 min-w-0">
272
+ <TabsPanel value="account">
273
+ <AccountPanel />
274
+ </TabsPanel>
275
+ <TabsPanel value="security">
276
+ <SecurityPanel />
277
+ </TabsPanel>
278
+ <TabsPanel value="billing">
279
+ <BillingPanel />
280
+ </TabsPanel>
281
+ <TabsPanel value="appearance">
282
+ <AppearancePanel />
283
+ </TabsPanel>
284
+ </div>
285
+ </Tabs>
286
+ ),
287
+ };
62
288
 
63
- export const Default: Story = {
64
- render: () => {
65
- return (
66
- <Tabs defaultValue="account" className="container">
67
- <TabList className="max-w-[500px] mx-auto">
68
- <TabTrigger value="account">Account</TabTrigger>
69
- <TabTrigger value="password">Password</TabTrigger>
70
- <TabTrigger value="config">File</TabTrigger>
71
- </TabList>
72
-
73
- <TabContent value="account" className="space-y-2 min-h-[150px]">
74
- <AccountStep />
75
- </TabContent>
76
-
77
- <TabContent value="password" className="space-y-2 min-h-[150px]">
78
- <PasswordStep />
79
- </TabContent>
80
-
81
- <TabContent value="config" className="space-y-2 min-h-[150px]">
82
- <FileStep />
83
- </TabContent>
84
- </Tabs>
85
- );
86
- },
289
+ export const WithIcons: Story = {
290
+ render: () => (
291
+ <Tabs defaultValue="account" className="w-[480px] h-[340px]">
292
+ <TabsList>
293
+ <TabsTab value="account">
294
+ <UserIcon />
295
+ Account
296
+ </TabsTab>
297
+ <TabsTab value="security">
298
+ <LockIcon />
299
+ Security
300
+ </TabsTab>
301
+ <TabsTab value="notifications">
302
+ <BellIcon />
303
+ Notifications
304
+ </TabsTab>
305
+ <TabsTab value="appearance">
306
+ <PaletteIcon />
307
+ Appearance
308
+ </TabsTab>
309
+ </TabsList>
310
+ <TabsPanel value="account">
311
+ <AccountPanel />
312
+ </TabsPanel>
313
+ <TabsPanel value="security">
314
+ <SecurityPanel />
315
+ </TabsPanel>
316
+ <TabsPanel value="notifications">
317
+ <NotificationsPanel />
318
+ </TabsPanel>
319
+ <TabsPanel value="appearance">
320
+ <AppearancePanel />
321
+ </TabsPanel>
322
+ </Tabs>
323
+ ),
87
324
  };
88
325
 
89
326
  /**
90
- * Se puede utilizar el hook `useStep` para facilitar el manejo de estado y validaciones
327
+ * Any tab can be disabled via the `disabled` prop on `TabsTab`.
328
+ * The indicator skips over disabled tabs.
91
329
  */
92
- export const Steps: Story = {
93
- render: () => {
94
- const { step, isFirstStep, isLastStep, next, back, findAndGo } =
95
- useStep(steps);
96
-
97
- return (
98
- <Tabs
99
- value={step.value}
100
- onChange={(step: string) => findAndGo({ value: step })}
101
- className="container"
102
- >
103
- <TabList className="max-w-[500px] mx-auto">
104
- {steps.map(({ value }) => (
105
- <TabTrigger key={value} value={value} className="capitalize">
106
- {value}
107
- </TabTrigger>
108
- ))}
109
- </TabList>
110
-
111
- {steps.map(({ value, content }) => (
112
- <TabContent
113
- key={value}
114
- value={value}
115
- className="space-y-2 min-h-[150px]"
116
- >
117
- {content}
118
- </TabContent>
119
- ))}
120
- <div className="flex justify-end mt-2 gap-x-2">
121
- <Button variant="ghost" disabled={isFirstStep} onClick={back}>
122
- Prev
123
- </Button>
124
- <Button disabled={isLastStep} onClick={next}>
125
- Next
126
- </Button>
127
- </div>
128
- </Tabs>
129
- );
130
- },
330
+ export const Disabled: Story = {
331
+ render: () => (
332
+ <Tabs defaultValue="account" className="w-[480px] h-[340px]">
333
+ <TabsList>
334
+ <TabsTab value="account">Account</TabsTab>
335
+ <TabsTab value="security">Security</TabsTab>
336
+ <TabsTab value="billing" disabled>
337
+ Billing
338
+ </TabsTab>
339
+ <TabsTab value="enterprise" disabled>
340
+ Enterprise
341
+ </TabsTab>
342
+ </TabsList>
343
+ <TabsPanel value="account">
344
+ <AccountPanel />
345
+ </TabsPanel>
346
+ <TabsPanel value="security">
347
+ <SecurityPanel />
348
+ </TabsPanel>
349
+ </Tabs>
350
+ ),
131
351
  };
132
352
 
133
353
  /**
134
- * Se puede utilizar el prop `variant` para cambiar el estilo del indicador
135
- * `default` (línea) y `background` (fondo)
354
+ * Use `TabsPrimitive` to access the raw Base UI API when the styled wrappers
355
+ * don't cover your use case — custom indicators, additional ARIA attributes, etc.
136
356
  */
137
- export const IndicatorVariant: Story = {
138
- render: () => {
139
- const [variant, setVariant] = useState<"default" | "background">("default");
140
-
141
- return (
142
- <div className="space-y-8">
143
- <div className="flex items-center gap-4">
144
- <label htmlFor="variant" className="text-sm font-medium">
145
- Variant:
146
- </label>
147
- <select
148
- id="variant"
149
- value={variant}
150
- onChange={(e) =>
151
- setVariant(e.target.value as "default" | "background")
152
- }
153
- className="px-3 py-1 rounded border"
154
- >
155
- <option value="default">Default</option>
156
- <option value="background">Background</option>
157
- </select>
158
- </div>
159
-
160
- <Tabs defaultValue="account" variant={variant}>
161
- <TabList>
162
- <TabTrigger value="account">Account</TabTrigger>
163
- <TabTrigger value="password">Password</TabTrigger>
164
- <TabTrigger value="config">Config</TabTrigger>
165
- </TabList>
166
-
167
- <TabContent value="account" className="space-y-2 min-h-[150px]">
168
- <div>Account</div>
169
- </TabContent>
170
-
171
- <TabContent value="password" className="space-y-2 min-h-[150px]">
172
- <div>Password</div>
173
- </TabContent>
174
-
175
- <TabContent value="config" className="space-y-2 min-h-[150px]">
176
- <div>Config</div>
177
- </TabContent>
178
- </Tabs>
179
- </div>
180
- );
357
+ export const Primitive: Story = {
358
+ render: () => (
359
+ <TabsPrimitive.Root
360
+ defaultValue="account"
361
+ className="w-[480px] h-[340px] flex flex-col gap-2"
362
+ >
363
+ <TabsPrimitive.List className="flex gap-1 border-b">
364
+ <TabsPrimitive.Tab
365
+ value="account"
366
+ className="px-4 py-2 text-sm font-medium data-active:border-b-2 data-active:border-primary cursor-pointer"
367
+ >
368
+ Account
369
+ </TabsPrimitive.Tab>
370
+ <TabsPrimitive.Tab
371
+ value="security"
372
+ className="px-4 py-2 text-sm font-medium data-active:border-b-2 data-active:border-primary cursor-pointer"
373
+ >
374
+ Security
375
+ </TabsPrimitive.Tab>
376
+ </TabsPrimitive.List>
377
+ <TabsPrimitive.Panel value="account">
378
+ <AccountPanel />
379
+ </TabsPrimitive.Panel>
380
+ <TabsPrimitive.Panel value="security">
381
+ <SecurityPanel />
382
+ </TabsPrimitive.Panel>
383
+ </TabsPrimitive.Root>
384
+ ),
385
+ parameters: {
386
+ docs: {
387
+ source: {
388
+ code: `<TabsPrimitive.Root defaultValue="account">
389
+ <TabsPrimitive.List>
390
+ <TabsPrimitive.Tab value="account">Account</TabsPrimitive.Tab>
391
+ <TabsPrimitive.Tab value="security">Security</TabsPrimitive.Tab>
392
+ </TabsPrimitive.List>
393
+ <TabsPrimitive.Panel value="account">…</TabsPrimitive.Panel>
394
+ <TabsPrimitive.Panel value="security">…</TabsPrimitive.Panel>
395
+ </TabsPrimitive.Root>`,
396
+ },
397
+ },
181
398
  },
182
399
  };
@@ -1,7 +1,7 @@
1
1
  import { render, screen, waitFor } from "@testing-library/react";
2
2
  import { describe, expect, it, vi } from "vitest";
3
- import { TabContent, TabList, Tabs, TabTrigger } from "../../components";
4
- import { click } from "../../utils";
3
+ import { TabList, Tabs, TabsContent, TabsTrigger } from "../../components";
4
+ import { click } from "../../utils/tests";
5
5
 
6
6
  describe("Tabs component", () => {
7
7
  it("should render correctly", () => {
@@ -14,8 +14,8 @@ describe("Tabs component", () => {
14
14
  render(
15
15
  <Tabs>
16
16
  <TabList>
17
- <TabTrigger value="trigger">Trigger</TabTrigger>
18
- <TabTrigger value="test">Test</TabTrigger>
17
+ <TabsTrigger value="trigger">Trigger</TabsTrigger>
18
+ <TabsTrigger value="test">Test</TabsTrigger>
19
19
  </TabList>
20
20
  </Tabs>,
21
21
  );
@@ -28,10 +28,10 @@ describe("Tabs component", () => {
28
28
  render(
29
29
  <Tabs defaultValue="trigger">
30
30
  <TabList>
31
- <TabTrigger value="trigger">Trigger</TabTrigger>
32
- <TabTrigger value="test">Test</TabTrigger>
31
+ <TabsTrigger value="trigger">Trigger</TabsTrigger>
32
+ <TabsTrigger value="test">Test</TabsTrigger>
33
33
  </TabList>
34
- <TabContent value="trigger">Contenido de trigger</TabContent>
34
+ <TabsContent value="trigger">Contenido de trigger</TabsContent>
35
35
  </Tabs>,
36
36
  );
37
37
 
@@ -41,13 +41,13 @@ describe("Tabs component", () => {
41
41
  it("should be controlled", () => {
42
42
  const mock = vi.fn();
43
43
  render(
44
- <Tabs value="trigger" onChange={mock}>
44
+ <Tabs value="trigger" onValueChange={mock}>
45
45
  <TabList>
46
- <TabTrigger value="trigger">Trigger</TabTrigger>
47
- <TabTrigger value="test">Test</TabTrigger>
46
+ <TabsTrigger value="trigger">Trigger</TabsTrigger>
47
+ <TabsTrigger value="test">Test</TabsTrigger>
48
48
  </TabList>
49
- <TabContent value="trigger">Contenido de trigger</TabContent>
50
- <TabContent value="test">Contenido de test</TabContent>
49
+ <TabsContent value="trigger">Contenido de trigger</TabsContent>
50
+ <TabsContent value="test">Contenido de test</TabsContent>
51
51
  </Tabs>,
52
52
  );
53
53