@boxcustodia/library 2.0.0-alpha.13 → 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 (173) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1083 -715
  3. package/dist/index.es.js +7077 -56175
  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 +119 -103
  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 +6 -0
  48. package/src/components/dropzone/dropzone.stories.tsx +71 -90
  49. package/src/components/dropzone/dropzone.tsx +383 -105
  50. package/src/components/dropzone/index.ts +0 -1
  51. package/src/components/empty/empty.stories.tsx +165 -0
  52. package/src/components/empty/empty.tsx +156 -0
  53. package/src/components/empty/index.ts +1 -0
  54. package/src/components/field/field.stories.tsx +226 -3
  55. package/src/components/field/field.tsx +77 -42
  56. package/src/components/form/form.stories.tsx +320 -197
  57. package/src/components/form/form.tsx +3 -23
  58. package/src/components/index.ts +2 -6
  59. package/src/components/input/input.stories.tsx +5 -5
  60. package/src/components/input/input.tsx +4 -4
  61. package/src/components/kbd/kbd.stories.tsx +1 -0
  62. package/src/components/label/label.stories.tsx +16 -0
  63. package/src/components/label/label.tsx +13 -2
  64. package/src/components/loader/loader.stories.tsx +7 -5
  65. package/src/components/loader/loader.tsx +8 -3
  66. package/src/components/menu/menu-primitives.tsx +207 -196
  67. package/src/components/menu/menu.stories.tsx +276 -146
  68. package/src/components/menu/menu.tsx +146 -54
  69. package/src/components/number-input/number-input.stories.tsx +27 -4
  70. package/src/components/number-input/number-input.test.tsx +2 -2
  71. package/src/components/number-input/number-input.tsx +25 -29
  72. package/src/components/otp/index.ts +1 -0
  73. package/src/components/otp/otp.stories.tsx +209 -0
  74. package/src/components/otp/otp.tsx +100 -0
  75. package/src/components/pagination/index.ts +1 -0
  76. package/src/components/pagination/pagination.model.ts +2 -0
  77. package/src/components/pagination/pagination.stories.tsx +154 -59
  78. package/src/components/pagination/pagination.test.tsx +122 -57
  79. package/src/components/pagination/pagination.tsx +575 -77
  80. package/src/components/password/password.stories.tsx +18 -3
  81. package/src/components/password/password.tsx +26 -10
  82. package/src/components/popover/popover.stories.tsx +26 -5
  83. package/src/components/popover/popover.tsx +15 -23
  84. package/src/components/progress/progress.stories.tsx +1 -0
  85. package/src/components/radio-group/index.ts +1 -0
  86. package/src/components/radio-group/radio-group.stories.tsx +251 -0
  87. package/src/components/radio-group/radio-group.tsx +212 -0
  88. package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
  89. package/src/components/select/select.stories.tsx +118 -19
  90. package/src/components/select/select.tsx +67 -62
  91. package/src/components/skeleton/skeleton.stories.tsx +1 -0
  92. package/src/components/stack/stack.stories.tsx +179 -89
  93. package/src/components/stack/stack.tsx +2 -2
  94. package/src/components/stepper/index.ts +1 -1
  95. package/src/components/stepper/stepper.stories.tsx +767 -83
  96. package/src/components/stepper/stepper.test.tsx +18 -18
  97. package/src/components/stepper/stepper.tsx +554 -0
  98. package/src/components/switch/switch.stories.tsx +15 -1
  99. package/src/components/switch/switch.tsx +17 -4
  100. package/src/components/table/index.ts +0 -2
  101. package/src/components/table/table.stories.tsx +131 -18
  102. package/src/components/table/table.test.tsx +1 -1
  103. package/src/components/table/table.tsx +183 -77
  104. package/src/components/tabs/tabs.stories.tsx +373 -155
  105. package/src/components/tabs/tabs.test.tsx +12 -12
  106. package/src/components/tabs/tabs.tsx +72 -149
  107. package/src/components/tag/index.ts +0 -1
  108. package/src/components/tag/tag.stories.tsx +155 -120
  109. package/src/components/tag/tag.tsx +47 -95
  110. package/src/components/textarea/textarea.stories.tsx +8 -22
  111. package/src/components/textarea/textarea.tsx +17 -79
  112. package/src/components/timeline/timeline.stories.tsx +323 -42
  113. package/src/components/timeline/timeline.tsx +359 -132
  114. package/src/components/toast/toast.stories.tsx +1 -0
  115. package/src/components/tooltip/tooltip.tsx +11 -9
  116. package/src/components/tree/index.ts +0 -1
  117. package/src/components/tree/tree.stories.tsx +365 -408
  118. package/src/components/tree/tree.test.tsx +163 -0
  119. package/src/components/tree/tree.tsx +212 -36
  120. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
  121. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
  122. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
  123. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
  124. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
  125. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
  126. package/src/hooks/usePagination/usePagination.tsx +36 -24
  127. package/src/styles/theme.css +1 -1
  128. package/src/utils/form.tsx +67 -37
  129. package/src/utils/index.ts +1 -1
  130. package/src/__doc__/Migration.mdx +0 -451
  131. package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
  132. package/src/components/background-image/background-image.stories.tsx +0 -21
  133. package/src/components/background-image/background-image.test.tsx +0 -29
  134. package/src/components/background-image/background-image.tsx +0 -23
  135. package/src/components/background-image/index.ts +0 -1
  136. package/src/components/button/button.variants.ts +0 -44
  137. package/src/components/button/components/loader-overlay.tsx +0 -21
  138. package/src/components/button/components/loading-icon.tsx +0 -47
  139. package/src/components/dropzone/upload-primitives.tsx +0 -310
  140. package/src/components/dropzone/use-dropzone.ts +0 -122
  141. package/src/components/empty-state/empty-state.stories.tsx +0 -56
  142. package/src/components/empty-state/empty-state.tsx +0 -39
  143. package/src/components/empty-state/index.ts +0 -1
  144. package/src/components/heading/heading.stories.tsx +0 -74
  145. package/src/components/heading/heading.tsx +0 -28
  146. package/src/components/heading/heading.variants.ts +0 -27
  147. package/src/components/heading/index.ts +0 -1
  148. package/src/components/kbd/kbd.variants.ts +0 -26
  149. package/src/components/menu/util/render-menu-item.tsx +0 -54
  150. package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
  151. package/src/components/multi-select/index.ts +0 -1
  152. package/src/components/multi-select/multi-select.stories.tsx +0 -294
  153. package/src/components/multi-select/multi-select.tsx +0 -300
  154. package/src/components/multi-select/multi-select.variants.ts +0 -22
  155. package/src/components/pagination/components/pagination-option.tsx +0 -27
  156. package/src/components/show/index.ts +0 -1
  157. package/src/components/show/show.stories.tsx +0 -197
  158. package/src/components/show/show.test.tsx +0 -41
  159. package/src/components/show/show.tsx +0 -16
  160. package/src/components/stepper/Stepper.tsx +0 -190
  161. package/src/components/stepper/context/stepper-context.tsx +0 -11
  162. package/src/components/table/table-primitives.tsx +0 -122
  163. package/src/components/table/table.model.ts +0 -20
  164. package/src/components/table-pagination/index.ts +0 -2
  165. package/src/components/table-pagination/table-pagination.model.ts +0 -2
  166. package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
  167. package/src/components/table-pagination/table-pagination.test.tsx +0 -32
  168. package/src/components/table-pagination/table-pagination.tsx +0 -108
  169. package/src/components/tabs/context/tabs-context.tsx +0 -14
  170. package/src/components/tag/tag.variants.ts +0 -31
  171. package/src/components/timeline/timeline-status.ts +0 -5
  172. package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
  173. package/src/components/tree/tree-primitives.tsx +0 -126
@@ -1,3 +1 @@
1
1
  export * from "./table";
2
- export * from "./table.model";
3
- export * from "./table-primitives";
@@ -1,11 +1,26 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
  import { ImageIcon } from "lucide-react";
3
3
  import { useMemo } from "react";
4
- import { Avatar, Checkbox, ColumnDef, Table, Tag } from "../../components";
4
+ import { Avatar, Checkbox, Tag } from "../../components";
5
5
  import { useSelection } from "../../hooks";
6
+ import {
7
+ ColumnDef,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRoot,
14
+ TableRow,
15
+ } from "./table";
6
16
 
17
+ /**
18
+ * Data table with typed column definitions. `onRowClick` and `onRowDoubleClick`
19
+ * fire at the row level — not per cell. For per-cell events or full markup control,
20
+ * compose with the exported primitives directly.
21
+ */
7
22
  const meta: Meta<typeof Table> = {
8
- title: "Data display/Table",
23
+ title: "Components/Table",
9
24
  component: Table,
10
25
  };
11
26
 
@@ -44,7 +59,7 @@ const data: User[] = [
44
59
  },
45
60
  {
46
61
  id: 4,
47
- name: "Doe",
62
+ name: "Smith",
48
63
  photo: "https://i.pravatar.cc/300?un=1234589",
49
64
  age: 15,
50
65
  gender: "female",
@@ -71,7 +86,6 @@ const columns: ColumnDef<User>[] = [
71
86
  key: "age",
72
87
  cell: (row) => {
73
88
  const isAdult = row.age > 18;
74
-
75
89
  return (
76
90
  <Tag variant={isAdult ? "secondary" : "error"}>
77
91
  {isAdult ? "Adult" : "Minor"}
@@ -87,31 +101,34 @@ const columns: ColumnDef<User>[] = [
87
101
 
88
102
  export const Default: Story<User> = {
89
103
  args: {
90
- columns: columns,
91
- data: data,
104
+ columns,
105
+ data,
92
106
  className: "table-fixed",
93
107
  },
94
108
  };
95
109
 
96
110
  /**
97
- * Al setear una altura en la propiedad `containerClassName` se puede hacer Scrollable
111
+ * Set a max height on `classNames.container` to clip the body and enable vertical
112
+ * scroll. The header stays sticky via `position: sticky` on `<th>`.
98
113
  *
99
114
  * ```tsx
100
- * <Table
101
- * ....
102
- * containerClassName="max-h-[500px]"
103
- * />
115
+ * <Table classNames={{ container: "max-h-[500px]" }} ... />
104
116
  * ```
105
117
  */
106
118
  export const Scrollable: Story<User> = {
107
119
  args: {
108
- columns: columns,
120
+ columns,
109
121
  data: [...data, ...data, ...data],
110
122
  className: "table-fixed",
111
- containerClassName: "max-h-[500px]",
123
+ classNames: { container: "max-h-[500px]" },
112
124
  },
113
125
  };
114
126
 
127
+ /**
128
+ * `onRowClick` and `selected` wire directly to `useSelection`. `getRowKey` is
129
+ * required here because `columnsAdapter` rebuilds the columns array on each render —
130
+ * without stable keys React reconciles by index and checkbox state drifts.
131
+ */
115
132
  export const Selection: Story<User> = {
116
133
  render: () => {
117
134
  const {
@@ -126,7 +143,7 @@ export const Selection: Story<User> = {
126
143
  const columnsAdapter = (): ColumnDef<User>[] => [
127
144
  {
128
145
  key: "id",
129
- width: 30,
146
+ width: 40,
130
147
  header: () => (
131
148
  <Checkbox
132
149
  name="selectAll"
@@ -155,15 +172,111 @@ export const Selection: Story<User> = {
155
172
  className="table-fixed"
156
173
  onRowClick={toggle}
157
174
  selected={isSelected}
175
+ getRowKey={(row) => row.id}
158
176
  />
159
177
  );
160
178
  },
161
179
  };
162
180
 
163
- export const DoubleClickAction: Story<User> = {
181
+ export const DoubleClick: Story<User> = {
164
182
  args: {
165
- columns: columns,
166
- data: data,
167
- onDoubleClick: (row: User) => alert(`clicked ${row.name}`),
183
+ columns,
184
+ data,
185
+ onRowDoubleClick: (row: User) => alert(`Double-clicked: ${row.name}`),
168
186
  },
169
187
  };
188
+
189
+ /**
190
+ * `classNames` expone los slots `container`, `thead`, `tbody`, `tr`, `th` y `td`.
191
+ * `tr` aplica sólo a las filas del body — la fila del header queda intacta.
192
+ */
193
+ export const WithClassNames: Story<User> = {
194
+ args: {
195
+ columns,
196
+ data,
197
+ classNames: {
198
+ thead: "bg-muted/50",
199
+ tr: "odd:bg-muted/20",
200
+ th: "uppercase text-xs tracking-wider",
201
+ td: "text-muted-foreground",
202
+ },
203
+ },
204
+ };
205
+
206
+ /**
207
+ * Use the exported primitives when the composite `Table` is not enough: per-cell
208
+ * events, custom `tfoot`, or arbitrary markup between sections.
209
+ *
210
+ * ```tsx
211
+ * import { TableRoot, TableHeader, TableBody, TableRow, TableHead, TableCell } from "./table";
212
+ * ```
213
+ */
214
+ export const Primitive: Story<User> = {
215
+ render: () => (
216
+ <TableRoot>
217
+ <TableHeader>
218
+ <TableRow>
219
+ <TableHead>Name</TableHead>
220
+ <TableHead>Age</TableHead>
221
+ <TableHead>Gender</TableHead>
222
+ </TableRow>
223
+ </TableHeader>
224
+ <TableBody>
225
+ {data.map((row) => (
226
+ <TableRow key={row.id} onClick={() => alert(row.name)}>
227
+ <TableCell>{row.name}</TableCell>
228
+ <TableCell>{row.age}</TableCell>
229
+ <TableCell>{row.gender}</TableCell>
230
+ </TableRow>
231
+ ))}
232
+ </TableBody>
233
+ </TableRoot>
234
+ ),
235
+ };
236
+
237
+ // ─── Changelog ───────────────────────────────────────────────────────────────
238
+ //
239
+ // v2 — Breaking changes
240
+ //
241
+ // REMOVED props (*Props escape hatches — use primitives for full prop control):
242
+ // - theadProps
243
+ // - tbodyProps
244
+ // - trProps → was also incorrectly applied to the header row
245
+ // - thProps
246
+ // - tdProps
247
+ //
248
+ // KEPT as className-only escape hatches:
249
+ // - theadClassName, tbodyClassName, trClassName, thClassName, tdClassName
250
+ // Note: trClassName now applies to body rows only, NOT the header row.
251
+ //
252
+ // CHANGED behavior:
253
+ // - onRowClick fires on <tr>, not <td>. Event no longer bubbles per-cell.
254
+ // - onDoubleClick renamed to onRowDoubleClick for consistency with onRowClick.
255
+ // Native onDoubleClick is omitted from the table spread — linter will catch misuse.
256
+ // - cell fallback uses explicit check (`column.cell ? ... : row[key]`)
257
+ // instead of truthiness (`||`). cell() returning null/0/"" no longer
258
+ // falls back to raw row value.
259
+ //
260
+ // ADDED props:
261
+ // - getRowKey: (row, index) => string | number — stable React keys
262
+ //
263
+ // CONSOLIDATED files:
264
+ // - table-primitives.tsx deleted — primitives now live in table.tsx
265
+ // - table.model.ts deleted — ColumnDef now exported from table.tsx
266
+ // - index.ts now only re-exports from "./table"
267
+ //
268
+ // Migration example for removed *Props escape hatches:
269
+ //
270
+ // Before:
271
+ // <Table tdProps={{ onClick: handleCellClick }} ... />
272
+ //
273
+ // After (use primitives):
274
+ // <TableRoot>
275
+ // <TableBody>
276
+ // {data.map(row => (
277
+ // <TableRow key={row.id}>
278
+ // <TableCell onClick={handleCellClick}>{row.name}</TableCell>
279
+ // </TableRow>
280
+ // ))}
281
+ // </TableBody>
282
+ // </TableRoot>
@@ -1,7 +1,7 @@
1
1
  import { render, screen } from "@testing-library/react";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
  import { ColumnDef, Table } from "../../components";
4
- import { click } from "../../utils";
4
+ import { click } from "../../utils/tests";
5
5
 
6
6
  describe("Table", () => {
7
7
  interface Person {
@@ -1,104 +1,210 @@
1
- import { isFunction } from "lodash";
2
- import { ComponentPropsWithoutRef, HTMLProps, JSX } from "react";
3
- import { ClassName } from "@/models";
1
+ import * as React from "react";
2
+ import { type ReactNode } from "react";
4
3
  import { cn } from "../../lib";
5
- import { ColumnDef } from "./table.model";
6
- import {
7
- TableBody,
8
- TableCell,
9
- TableHead,
10
- TableHeader,
11
- TableRoot,
12
- TableRow,
13
- } from "./table-primitives";
14
4
 
15
- type Props<T> = {
5
+ // ─── Types ───────────────────────────────────────────────────────────────────
6
+
7
+ export type ColumnDef<T> = {
8
+ header: string | (() => ReactNode);
9
+ key: keyof T;
10
+ cell?: (row: T, index: number) => ReactNode;
11
+ width?: string | number;
12
+ };
13
+
14
+ // ─── Primitives ──────────────────────────────────────────────────────────────
15
+
16
+ type TableRootProps = React.ComponentProps<"table"> & {
17
+ /** Styles applied to each internal slot. */
18
+ classNames?: {
19
+ /** Outer wrapper div with overflow handling. */
20
+ container?: string;
21
+ };
22
+ };
23
+
24
+ export function TableRoot({ className, classNames, ...props }: TableRootProps) {
25
+ return (
26
+ <div
27
+ data-slot="table-container"
28
+ className={cn(
29
+ "relative w-full overflow-auto rounded-md border",
30
+ classNames?.container,
31
+ )}
32
+ >
33
+ <table
34
+ data-slot="table"
35
+ className={cn("w-full caption-bottom text-sm", className)}
36
+ {...props}
37
+ />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ export function TableHeader({
43
+ className,
44
+ ...props
45
+ }: React.ComponentProps<"thead">) {
46
+ return (
47
+ <thead
48
+ data-slot="table-header"
49
+ className={cn("[&_tr]:border-b", className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export function TableBody({
56
+ className,
57
+ ...props
58
+ }: React.ComponentProps<"tbody">) {
59
+ return (
60
+ <tbody
61
+ data-slot="table-body"
62
+ className={cn("[&_tr:last-child]:border-0", className)}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ export function TableFooter({
69
+ className,
70
+ ...props
71
+ }: React.ComponentProps<"tfoot">) {
72
+ return (
73
+ <tfoot
74
+ data-slot="table-footer"
75
+ className={cn(
76
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
77
+ className,
78
+ )}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ export function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
85
+ return (
86
+ <tr
87
+ data-slot="table-row"
88
+ className={cn(
89
+ "border-b transition-colors hover:bg-muted data-[selected=true]:bg-muted",
90
+ className,
91
+ )}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ export function TableHead({ className, ...props }: React.ComponentProps<"th">) {
98
+ return (
99
+ <th
100
+ data-slot="table-head"
101
+ className={cn(
102
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground sticky top-0 bg-background [&:has([role=checkbox])]:pr-0",
103
+ className,
104
+ )}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+
110
+ export function TableCell({ className, ...props }: React.ComponentProps<"td">) {
111
+ return (
112
+ <td
113
+ data-slot="table-cell"
114
+ className={cn(
115
+ "p-4 align-middle [&:has([role=checkbox])]:pr-0",
116
+ className,
117
+ )}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ export function TableCaption({
124
+ className,
125
+ ...props
126
+ }: React.ComponentProps<"caption">) {
127
+ return (
128
+ <caption
129
+ data-slot="table-caption"
130
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
131
+ {...props}
132
+ />
133
+ );
134
+ }
135
+
136
+ // ─── Composite ───────────────────────────────────────────────────────────────
137
+
138
+ type TableProps<T> = {
16
139
  data: T[];
17
140
  columns: ColumnDef<T>[];
141
+ getRowKey?: (row: T, index: number) => string | number;
18
142
  onRowClick?: (row: T) => void;
19
- onDoubleClick?: (row: T) => void;
143
+ onRowDoubleClick?: (row: T) => void;
20
144
  selected?: (row: T) => boolean;
21
- } & ExtendedProps &
22
- Omit<
23
- ComponentPropsWithoutRef<typeof TableRoot>,
24
- "data" | "selected" | "onDoubleClick"
25
- >;
26
-
27
- interface ExtendedProps {
28
- theadClassName?: ClassName;
29
- theadProps?: HTMLProps<HTMLTableSectionElement>;
30
- tbodyClassName?: ClassName;
31
- tbodyProps?: HTMLProps<HTMLTableSectionElement>;
32
- trClassName?: ClassName;
33
- trProps?: HTMLProps<HTMLTableRowElement>;
34
- thClassName?: ClassName;
35
- thProps?: HTMLProps<HTMLTableCellElement>;
36
- tdClassName?: ClassName;
37
- tdProps?: Omit<HTMLProps<HTMLTableCellElement>, "onDoubleClick">;
38
- }
145
+ /** Styles applied to each internal slot. */
146
+ classNames?: {
147
+ /** Outer wrapper div with overflow handling. */
148
+ container?: string;
149
+ /** `<thead>` element. */
150
+ thead?: string;
151
+ /** `<tbody>` element. */
152
+ tbody?: string;
153
+ /** `<tr>` inside `<thead>` (the single header row). */
154
+ headerRow?: string;
155
+ /** Each `<tr>` inside `<tbody>` (the repeating body rows). */
156
+ tr?: string;
157
+ /** Header cell `<th>`. */
158
+ th?: string;
159
+ /** Body cell `<td>`. */
160
+ td?: string;
161
+ };
162
+ } & Omit<React.ComponentProps<"table">, "onDoubleClick">;
39
163
 
40
- export const Table = <T,>({
164
+ export function Table<T>({
41
165
  data = [],
42
166
  columns = [],
167
+ getRowKey,
43
168
  onRowClick,
44
- onDoubleClick,
169
+ onRowDoubleClick,
45
170
  selected,
46
- theadClassName,
47
- theadProps,
48
- tbodyClassName,
49
- tbodyProps,
50
- trClassName,
51
- trProps,
52
- thClassName,
53
- thProps,
54
- tdClassName,
55
- tdProps,
171
+ classNames,
56
172
  ...tableProps
57
- }: Props<T>) => {
173
+ }: TableProps<T>) {
58
174
  return (
59
- <TableRoot {...tableProps} className={cn(tableProps?.className)}>
60
- <TableHeader
61
- {...theadProps}
62
- className={cn(theadProps?.className, theadClassName)}
63
- >
64
- <TableRow {...trProps} className={cn(trProps?.className, trClassName)}>
175
+ <TableRoot
176
+ {...tableProps}
177
+ classNames={{ container: classNames?.container }}
178
+ >
179
+ <TableHeader className={classNames?.thead}>
180
+ <TableRow className={classNames?.headerRow}>
65
181
  {columns.map((column, i) => (
66
182
  <TableHead
67
183
  key={i}
68
- {...thProps}
69
- className={cn(thProps?.className, thClassName)}
70
- style={{
71
- width: column.width,
72
- }}
184
+ className={classNames?.th}
185
+ style={{ width: column.width }}
73
186
  >
74
- {isFunction(column.header) ? column.header?.() : column.header}
187
+ {typeof column.header === "function"
188
+ ? column.header()
189
+ : column.header}
75
190
  </TableHead>
76
191
  ))}
77
192
  </TableRow>
78
193
  </TableHeader>
79
- <TableBody
80
- {...tbodyProps}
81
- className={cn(tbodyProps?.className, tbodyClassName)}
82
- >
194
+ <TableBody className={classNames?.tbody}>
83
195
  {data.map((row, i) => (
84
196
  <TableRow
85
- key={i}
86
- {...trProps}
197
+ key={getRowKey ? getRowKey(row, i) : i}
198
+ className={classNames?.tr}
87
199
  data-selected={selected?.(row)}
88
- className={cn(trProps?.className, trClassName)}
200
+ onClick={() => onRowClick?.(row)}
201
+ onDoubleClick={() => onRowDoubleClick?.(row)}
89
202
  >
90
- {columns.map((column, i) => (
91
- <TableCell
92
- key={i}
93
- {...tdProps}
94
- onClick={(e) => {
95
- tdProps?.onClick?.(e);
96
- onRowClick?.(row);
97
- }}
98
- onDoubleClick={() => onDoubleClick?.(row)}
99
- className={cn(tdProps?.className, tdClassName)}
100
- >
101
- {(column.cell?.(row, i) as JSX.Element) || row[column.key]}
203
+ {columns.map((column, j) => (
204
+ <TableCell key={j} className={classNames?.td}>
205
+ {column.cell
206
+ ? column.cell(row, i)
207
+ : (row[column.key] as ReactNode)}
102
208
  </TableCell>
103
209
  ))}
104
210
  </TableRow>
@@ -106,4 +212,4 @@ export const Table = <T,>({
106
212
  </TableBody>
107
213
  </TableRoot>
108
214
  );
109
- };
215
+ }