@izumisy-tailor/tailor-data-viewer 0.2.19 → 0.2.20

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@izumisy-tailor/tailor-data-viewer",
3
3
  "private": false,
4
- "version": "0.2.19",
4
+ "version": "0.2.20",
5
5
  "type": "module",
6
6
  "description": "Flexible data viewer component for Tailor Platform",
7
7
  "files": [
@@ -15,7 +15,7 @@ const CollectionContext = createContext<UseCollectionReturn<
15
15
  *
16
16
  * <CollectionProvider value={collection}>
17
17
  * <FilterPanel />
18
- * <DataTable.Root {...table.rootProps}>...</DataTable.Root>
18
+ * <DataTable.Root>...</DataTable.Root>
19
19
  * <Pagination {...table} />
20
20
  * </CollectionProvider>
21
21
  * ```
@@ -1,7 +1,9 @@
1
1
  import { render, screen } from "@testing-library/react";
2
2
  import { describe, it, expect } from "vitest";
3
3
  import { Table } from "./table";
4
- import { DataTable } from "./data-table/index";
4
+ import { DataTable } from "./data-table/data-table";
5
+ import { DataTableContext } from "./data-table/data-table-context";
6
+ import type { DataTableContextValue } from "./data-table/data-table-context";
5
7
  import type { Column } from "./types";
6
8
 
7
9
  describe("Table (static)", () => {
@@ -65,13 +67,51 @@ const testRows: TestRow[] = [
65
67
  { id: "2", name: "Bob", status: "Inactive" },
66
68
  ];
67
69
 
70
+ const noopRowOps = {
71
+ updateRow: () => ({ rollback: () => {} }),
72
+ deleteRow: () => ({
73
+ rollback: () => {},
74
+ deletedRow: {} as TestRow,
75
+ }),
76
+ insertRow: () => ({ rollback: () => {} }),
77
+ };
78
+
79
+ const defaultPageInfo = {
80
+ hasNextPage: false,
81
+ endCursor: null,
82
+ hasPreviousPage: false,
83
+ startCursor: null,
84
+ };
85
+
86
+ function createCtx(
87
+ overrides: Partial<DataTableContextValue<TestRow>> = {},
88
+ ): DataTableContextValue<TestRow> {
89
+ return {
90
+ columns: testColumns,
91
+ rows: testRows,
92
+ loading: false,
93
+ error: null,
94
+ sortStates: [],
95
+ visibleColumns: testColumns,
96
+ isColumnVisible: () => true,
97
+ toggleColumn: () => {},
98
+ showAllColumns: () => {},
99
+ hideAllColumns: () => {},
100
+ pageInfo: defaultPageInfo,
101
+ ...noopRowOps,
102
+ ...overrides,
103
+ };
104
+ }
105
+
68
106
  describe("DataTable", () => {
69
107
  it("renders data-bound table with auto-generated rows", () => {
70
108
  render(
71
- <DataTable.Root columns={testColumns} rows={testRows}>
72
- <DataTable.Headers />
73
- <DataTable.Body />
74
- </DataTable.Root>,
109
+ <DataTableContext.Provider value={createCtx()}>
110
+ <DataTable.Root>
111
+ <DataTable.Headers />
112
+ <DataTable.Body />
113
+ </DataTable.Root>
114
+ </DataTableContext.Provider>,
75
115
  );
76
116
 
77
117
  expect(screen.getByText("Name")).toBeInTheDocument();
@@ -83,10 +123,12 @@ describe("DataTable", () => {
83
123
 
84
124
  it("renders display columns via render function", () => {
85
125
  render(
86
- <DataTable.Root columns={testColumns} rows={testRows}>
87
- <DataTable.Headers />
88
- <DataTable.Body />
89
- </DataTable.Root>,
126
+ <DataTableContext.Provider value={createCtx()}>
127
+ <DataTable.Root>
128
+ <DataTable.Headers />
129
+ <DataTable.Body />
130
+ </DataTable.Root>
131
+ </DataTableContext.Provider>,
90
132
  );
91
133
 
92
134
  expect(screen.getByText("Edit Alice")).toBeInTheDocument();
@@ -95,10 +137,12 @@ describe("DataTable", () => {
95
137
 
96
138
  it("shows loading state", () => {
97
139
  render(
98
- <DataTable.Root columns={testColumns} rows={[]} loading>
99
- <DataTable.Headers />
100
- <DataTable.Body />
101
- </DataTable.Root>,
140
+ <DataTableContext.Provider value={createCtx({ rows: [], loading: true })}>
141
+ <DataTable.Root>
142
+ <DataTable.Headers />
143
+ <DataTable.Body />
144
+ </DataTable.Root>
145
+ </DataTableContext.Provider>,
102
146
  );
103
147
 
104
148
  expect(screen.getByText("Loading...")).toBeInTheDocument();
@@ -107,10 +151,12 @@ describe("DataTable", () => {
107
151
  it("shows error state", () => {
108
152
  const err = new Error("Something went wrong");
109
153
  render(
110
- <DataTable.Root columns={testColumns} rows={[]} error={err}>
111
- <DataTable.Headers />
112
- <DataTable.Body />
113
- </DataTable.Root>,
154
+ <DataTableContext.Provider value={createCtx({ rows: [], error: err })}>
155
+ <DataTable.Root>
156
+ <DataTable.Headers />
157
+ <DataTable.Body />
158
+ </DataTable.Root>
159
+ </DataTableContext.Provider>,
114
160
  );
115
161
 
116
162
  expect(screen.getByText("Error: Something went wrong")).toBeInTheDocument();
@@ -118,10 +164,12 @@ describe("DataTable", () => {
118
164
 
119
165
  it("shows empty state", () => {
120
166
  render(
121
- <DataTable.Root columns={testColumns} rows={[]}>
122
- <DataTable.Headers />
123
- <DataTable.Body />
124
- </DataTable.Root>,
167
+ <DataTableContext.Provider value={createCtx({ rows: [] })}>
168
+ <DataTable.Root>
169
+ <DataTable.Headers />
170
+ <DataTable.Body />
171
+ </DataTable.Root>
172
+ </DataTableContext.Provider>,
125
173
  );
126
174
 
127
175
  expect(screen.getByText("No data")).toBeInTheDocument();
@@ -129,14 +177,16 @@ describe("DataTable", () => {
129
177
 
130
178
  it("renders sort indicator on sorted column", () => {
131
179
  render(
132
- <DataTable.Root
133
- columns={testColumns}
134
- rows={testRows}
135
- sortStates={[{ field: "name", direction: "Asc" }]}
180
+ <DataTableContext.Provider
181
+ value={createCtx({
182
+ sortStates: [{ field: "name", direction: "Asc" }],
183
+ })}
136
184
  >
137
- <DataTable.Headers />
138
- <DataTable.Body />
139
- </DataTable.Root>,
185
+ <DataTable.Root>
186
+ <DataTable.Headers />
187
+ <DataTable.Body />
188
+ </DataTable.Root>
189
+ </DataTableContext.Provider>,
140
190
  );
141
191
 
142
192
  expect(screen.getByText("▲")).toBeInTheDocument();
@@ -144,14 +194,16 @@ describe("DataTable", () => {
144
194
 
145
195
  it("supports custom rendering with children", () => {
146
196
  render(
147
- <DataTable.Root columns={testColumns} rows={testRows}>
148
- <DataTable.Headers />
149
- <DataTable.Body>
150
- <DataTable.Row>
151
- <DataTable.Cell>Custom Cell</DataTable.Cell>
152
- </DataTable.Row>
153
- </DataTable.Body>
154
- </DataTable.Root>,
197
+ <DataTableContext.Provider value={createCtx()}>
198
+ <DataTable.Root>
199
+ <DataTable.Headers />
200
+ <DataTable.Body>
201
+ <DataTable.Row>
202
+ <DataTable.Cell>Custom Cell</DataTable.Cell>
203
+ </DataTable.Row>
204
+ </DataTable.Body>
205
+ </DataTable.Root>
206
+ </DataTableContext.Provider>,
155
207
  );
156
208
 
157
209
  expect(screen.getByText("Custom Cell")).toBeInTheDocument();
@@ -2,7 +2,7 @@ import { createContext, useContext } from "react";
2
2
  import type { Column, PageInfo, RowOperations, SortState } from "../types";
3
3
 
4
4
  /**
5
- * Context value provided by `DataTable.Root` or `DataTable.Provider`.
5
+ * Context value provided by `DataTable.Provider`.
6
6
  *
7
7
  * Exposes row operations for optimistic updates, table state, column
8
8
  * visibility, and page info so that `DataTable.Headers` / `DataTable.Body`
@@ -14,7 +14,7 @@ export interface DataTableContextValue<TRow extends Record<string, unknown>> {
14
14
  deleteRow: RowOperations<TRow>["deleteRow"];
15
15
  insertRow: RowOperations<TRow>["insertRow"];
16
16
 
17
- // Table state propagated from DataTable.Root / DataTable.Provider
17
+ // Table state propagated from DataTable.Provider
18
18
  columns: Column<TRow>[];
19
19
  rows: TRow[];
20
20
  loading: boolean;
@@ -40,9 +40,9 @@ const DataTableContext = createContext<DataTableContextValue<any> | null>(null);
40
40
  export { DataTableContext };
41
41
 
42
42
  /**
43
- * Hook to access row operations from the nearest `DataTable.Root`.
43
+ * Hook to access row operations from the nearest `DataTable.Provider`.
44
44
  *
45
- * @throws Error if used outside of `DataTable.Root`.
45
+ * @throws Error if used outside of `DataTable.Provider`.
46
46
  *
47
47
  * @example
48
48
  * ```tsx
@@ -57,7 +57,9 @@ export function useDataTableContext<
57
57
  >(): DataTableContextValue<TRow> {
58
58
  const ctx = useContext(DataTableContext);
59
59
  if (!ctx) {
60
- throw new Error("useDataTableContext must be used within <DataTable.Root>");
60
+ throw new Error(
61
+ "useDataTableContext must be used within <DataTable.Provider>",
62
+ );
61
63
  }
62
64
  return ctx as DataTableContextValue<TRow>;
63
65
  }
@@ -7,12 +7,7 @@ import {
7
7
  import { cn } from "../lib/utils";
8
8
  import { CollectionProvider } from "../collection/collection-provider";
9
9
  import { Table } from "../table";
10
- import type {
11
- Column,
12
- DataTableRootProps,
13
- PageInfo,
14
- UseDataTableReturn,
15
- } from "../types";
10
+ import type { Column, UseDataTableReturn } from "../types";
16
11
  import {
17
12
  DataTableContext,
18
13
  type DataTableContextValue,
@@ -23,66 +18,19 @@ import {
23
18
  // =============================================================================
24
19
 
25
20
  /**
26
- * Internal noop row operations used when none are provided.
21
+ * Root container for the DataTable compound component.
22
+ *
23
+ * Must be used within `DataTable.Provider`.
24
+ * Renders a `<Table.Root>` wrapper; all data is read from context.
27
25
  */
28
- const noopRowOps = {
29
- updateRow: () => ({ rollback: () => {} }),
30
- deleteRow: () => ({
31
- rollback: () => {},
32
- deletedRow: {} as Record<string, unknown>,
33
- }),
34
- insertRow: () => ({ rollback: () => {} }),
35
- };
36
-
37
- const defaultPageInfo: PageInfo = {
38
- hasNextPage: false,
39
- endCursor: null,
40
- hasPreviousPage: false,
41
- startCursor: null,
42
- };
43
-
44
- function DataTableRoot<TRow extends Record<string, unknown>>({
45
- columns = [] as Column<TRow>[],
46
- rows = [] as TRow[],
47
- loading = false,
48
- error = null,
49
- sortStates = [],
50
- onSort,
51
- rowOperations,
26
+ function DataTableRoot({
52
27
  children,
53
- }: DataTableRootProps<TRow>) {
54
- // When rendered inside DataTable.Provider, inherit missing fields from context
55
- const parentCtx = useContext(
56
- DataTableContext,
57
- ) as DataTableContextValue<TRow> | null;
58
-
59
- const contextValue: DataTableContextValue<TRow> = {
60
- updateRow: (rowOperations?.updateRow ??
61
- noopRowOps.updateRow) as DataTableContextValue<TRow>["updateRow"],
62
- deleteRow: (rowOperations?.deleteRow ??
63
- noopRowOps.deleteRow) as DataTableContextValue<TRow>["deleteRow"],
64
- insertRow: (rowOperations?.insertRow ??
65
- noopRowOps.insertRow) as DataTableContextValue<TRow>["insertRow"],
66
- columns,
67
- rows,
68
- loading,
69
- error,
70
- sortStates,
71
- onSort,
72
- // Inherit column visibility & pageInfo from parent context (DataTable.Provider)
73
- visibleColumns: parentCtx?.visibleColumns ?? columns,
74
- isColumnVisible: parentCtx?.isColumnVisible ?? (() => true),
75
- toggleColumn: parentCtx?.toggleColumn ?? (() => {}),
76
- showAllColumns: parentCtx?.showAllColumns ?? (() => {}),
77
- hideAllColumns: parentCtx?.hideAllColumns ?? (() => {}),
78
- pageInfo: parentCtx?.pageInfo ?? defaultPageInfo,
79
- };
80
-
81
- return (
82
- <DataTableContext.Provider value={contextValue}>
83
- <Table.Root>{children}</Table.Root>
84
- </DataTableContext.Provider>
85
- );
28
+ className,
29
+ }: {
30
+ children: ReactNode;
31
+ className?: string;
32
+ }) {
33
+ return <Table.Root className={className}>{children}</Table.Root>;
86
34
  }
87
35
 
88
36
  // =============================================================================
@@ -125,8 +73,8 @@ function DataTableProviderComponent<TRow extends Record<string, unknown>>({
125
73
  rows: value.rows,
126
74
  loading: value.loading,
127
75
  error: value.error,
128
- sortStates: value.rootProps.sortStates ?? [],
129
- onSort: value.rootProps.onSort,
76
+ sortStates: value.sortStates ?? [],
77
+ onSort: value.onSort,
130
78
  updateRow: value.updateRow,
131
79
  deleteRow: value.deleteRow,
132
80
  insertRow: value.insertRow,
@@ -160,14 +108,12 @@ function DataTableProviderComponent<TRow extends Record<string, unknown>>({
160
108
  // DataTable.Headers
161
109
  // =============================================================================
162
110
 
163
- interface DataTableHeadersProps {
164
- className?: string;
165
- }
166
-
167
- function DataTableHeaders({ className }: DataTableHeadersProps) {
111
+ function DataTableHeaders({ className }: { className?: string }) {
168
112
  const ctx = useContext(DataTableContext);
169
113
  if (!ctx) {
170
- throw new Error("<DataTable.Headers> must be used within <DataTable.Root>");
114
+ throw new Error(
115
+ "<DataTable.Headers> must be used within <DataTable.Provider>",
116
+ );
171
117
  }
172
118
  const { columns, sortStates, onSort } = ctx;
173
119
 
@@ -225,15 +171,18 @@ function SortIndicator({ direction }: { direction: "Asc" | "Desc" }) {
225
171
  // DataTable.Body
226
172
  // =============================================================================
227
173
 
228
- interface DataTableBodyProps {
174
+ function DataTableBody({
175
+ children,
176
+ className,
177
+ }: {
229
178
  children?: ReactNode;
230
179
  className?: string;
231
- }
232
-
233
- function DataTableBody({ children, className }: DataTableBodyProps) {
180
+ }) {
234
181
  const ctx = useContext(DataTableContext);
235
182
  if (!ctx) {
236
- throw new Error("<DataTable.Body> must be used within <DataTable.Root>");
183
+ throw new Error(
184
+ "<DataTable.Body> must be used within <DataTable.Provider>",
185
+ );
237
186
  }
238
187
  const { columns, rows, loading, error } = ctx;
239
188
 
@@ -276,19 +225,19 @@ function DataTableBody({ children, className }: DataTableBodyProps) {
276
225
  </Table.Row>
277
226
  )}
278
227
  {rows?.map((row, rowIndex) => (
279
- <DataTableRow key={rowIndex} row={row}>
228
+ <Table.Row key={rowIndex}>
280
229
  {columns?.map((col) => {
281
230
  const key = col.kind === "field" ? col.dataKey : col.id;
282
231
  return (
283
- <DataTableCell
232
+ <Table.Cell
284
233
  key={key}
285
- row={row}
286
- column={col}
287
- rowIndex={rowIndex}
288
- />
234
+ style={col.width ? { width: col.width } : undefined}
235
+ >
236
+ {resolveContent(row, col, rowIndex)}
237
+ </Table.Cell>
289
238
  );
290
239
  })}
291
- </DataTableRow>
240
+ </Table.Row>
292
241
  ))}
293
242
  </Table.Body>
294
243
  );
@@ -298,82 +247,54 @@ function DataTableBody({ children, className }: DataTableBodyProps) {
298
247
  // DataTable.Row
299
248
  // =============================================================================
300
249
 
301
- interface DataTableRowComponentProps<
302
- TRow extends Record<string, unknown>,
303
- > extends Omit<ComponentProps<"tr">, "children"> {
304
- row?: TRow;
305
- children?: ReactNode;
306
- }
307
-
308
- function DataTableRow<TRow extends Record<string, unknown>>({
309
- children,
310
- ...restProps
311
- }: DataTableRowComponentProps<TRow>) {
312
- return <Table.Row {...restProps}>{children}</Table.Row>;
250
+ /**
251
+ * Thin wrapper around `Table.Row` for use in custom rendering within
252
+ * `DataTable.Body`.
253
+ */
254
+ function DataTableRow(props: ComponentProps<"tr">) {
255
+ return <Table.Row {...props} />;
313
256
  }
314
257
 
315
258
  // =============================================================================
316
259
  // DataTable.Cell
317
260
  // =============================================================================
318
261
 
319
- interface DataTableCellComponentProps<
320
- TRow extends Record<string, unknown>,
321
- > extends ComponentProps<"td"> {
322
- row?: TRow;
323
- column?: Column<TRow>;
324
- rowIndex?: number;
262
+ /**
263
+ * Thin wrapper around `Table.Cell` for use in custom rendering within
264
+ * `DataTable.Body`.
265
+ */
266
+ function DataTableCell(props: ComponentProps<"td">) {
267
+ return <Table.Cell {...props} />;
325
268
  }
326
269
 
327
- function DataTableCell<TRow extends Record<string, unknown>>({
328
- row,
329
- column,
330
- rowIndex = 0,
331
- children,
332
- className,
333
- ...restProps
334
- }: DataTableCellComponentProps<TRow>) {
335
- const content = resolveContent(children, row, column, rowIndex);
336
-
337
- return (
338
- <Table.Cell
339
- className={className}
340
- style={column?.width ? { width: column.width } : undefined}
341
- {...restProps}
342
- >
343
- {content}
344
- </Table.Cell>
345
- );
346
- }
270
+ // =============================================================================
271
+ // Helpers
272
+ // =============================================================================
347
273
 
348
274
  /**
349
- * Resolve cell content from children, row data, and column definition.
275
+ * Resolve cell content from row data and column definition.
350
276
  */
351
277
  function resolveContent<TRow extends Record<string, unknown>>(
352
- children: ReactNode,
353
- row: TRow | undefined,
354
- column: Column<TRow> | undefined,
278
+ row: TRow,
279
+ column: Column<TRow>,
355
280
  rowIndex: number,
356
281
  ): ReactNode {
357
- if (!children && row && column) {
358
- switch (column.kind) {
359
- case "field": {
360
- const value = row[column.dataKey];
361
- if (column.renderer) {
362
- return createElement(column.renderer, {
363
- value,
364
- row,
365
- rowIndex,
366
- column,
367
- });
368
- }
369
- return formatValue(value);
282
+ switch (column.kind) {
283
+ case "field": {
284
+ const value = row[column.dataKey];
285
+ if (column.renderer) {
286
+ return createElement(column.renderer, {
287
+ value,
288
+ row,
289
+ rowIndex,
290
+ column,
291
+ });
370
292
  }
371
- case "display":
372
- return column.render(row);
293
+ return formatValue(value);
373
294
  }
295
+ case "display":
296
+ return column.render(row);
374
297
  }
375
-
376
- return children;
377
298
  }
378
299
 
379
300
  /**
@@ -394,12 +315,11 @@ function formatValue(value: unknown): ReactNode {
394
315
  /**
395
316
  * Data-bound table compound component.
396
317
  *
397
- * Use with `useDataTable()` hook which provides `rootProps` to spread,
398
- * or wrap with `DataTable.Provider` for context-based usage.
318
+ * Use with `useDataTable()` hook and wrap with `DataTable.Provider`
319
+ * for context-based data flow.
399
320
  *
400
321
  * @example
401
322
  * ```tsx
402
- * // Context-based (recommended)
403
323
  * const table = useDataTable({ columns, data, loading, collection });
404
324
  *
405
325
  * <DataTable.Provider value={table}>
@@ -411,12 +331,6 @@ function formatValue(value: unknown): ReactNode {
411
331
  * </DataTable.Root>
412
332
  * <Pagination />
413
333
  * </DataTable.Provider>
414
- *
415
- * // Props-based (still supported)
416
- * <DataTable.Root {...table.rootProps}>
417
- * <DataTable.Headers />
418
- * <DataTable.Body />
419
- * </DataTable.Root>
420
334
  * ```
421
335
  */
422
336
  export const DataTable = {
@@ -234,46 +234,23 @@ describe("useDataTable", () => {
234
234
  });
235
235
 
236
236
  // ---------------------------------------------------------------------------
237
- // Props generators
237
+ // Sort state
238
238
  // ---------------------------------------------------------------------------
239
- describe("props generators", () => {
240
- it("rootProps contains columns, rows, loading, error", () => {
241
- const { result } = renderHook(() =>
242
- useDataTable<TestRow>({
243
- columns: testColumns,
244
- data: testData,
245
- loading: true,
246
- }),
247
- );
248
-
249
- const { rootProps } = result.current;
250
- expect(rootProps.columns).toHaveLength(4);
251
- expect(rootProps.rows).toHaveLength(3);
252
- expect(rootProps.loading).toBe(true);
253
- });
254
-
255
- it("getRowProps returns row", () => {
239
+ describe("sort state", () => {
240
+ it("sortStates is empty when no collection is provided", () => {
256
241
  const { result } = renderHook(() =>
257
242
  useDataTable<TestRow>({ columns: testColumns, data: testData }),
258
243
  );
259
244
 
260
- const rowProps = result.current.getRowProps(result.current.rows[0]);
261
- expect(rowProps.row.name).toBe("Alice");
245
+ expect(result.current.sortStates).toEqual([]);
262
246
  });
263
247
 
264
- it("getCellProps returns row, column, rowIndex", () => {
248
+ it("onSort is undefined when no collection is provided", () => {
265
249
  const { result } = renderHook(() =>
266
250
  useDataTable<TestRow>({ columns: testColumns, data: testData }),
267
251
  );
268
252
 
269
- const cellProps = result.current.getCellProps(
270
- result.current.rows[0],
271
- testColumns[0],
272
- 0,
273
- );
274
- expect(cellProps.row.name).toBe("Alice");
275
- expect(cellProps.column).toBe(testColumns[0]);
276
- expect(cellProps.rowIndex).toBe(0);
253
+ expect(result.current.onSort).toBeUndefined();
277
254
  });
278
255
  });
279
256
  });
@@ -1,17 +1,15 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import type {
3
3
  Column,
4
- DataTableCellProps,
5
- DataTableRootProps,
6
- DataTableRowProps,
7
4
  PageInfo,
5
+ SortState,
8
6
  UseDataTableOptions,
9
7
  UseDataTableReturn,
10
8
  } from "../types";
11
9
 
12
10
  /**
13
11
  * Hook that integrates data management, column visibility, row operations, and
14
- * props generators for the `DataTable.*` compound component.
12
+ * sort/pagination state for the `DataTable.*` compound component.
15
13
  *
16
14
  * @example
17
15
  * ```tsx
@@ -25,10 +23,12 @@ import type {
25
23
  * collection,
26
24
  * });
27
25
  *
28
- * <DataTable.Root {...table.rootProps}>
29
- * <DataTable.Headers />
30
- * <DataTable.Body />
31
- * </DataTable.Root>
26
+ * <DataTable.Provider value={table}>
27
+ * <DataTable.Root>
28
+ * <DataTable.Headers />
29
+ * <DataTable.Body />
30
+ * </DataTable.Root>
31
+ * </DataTable.Provider>
32
32
  * ```
33
33
  */
34
34
  export function useDataTable<TRow extends Record<string, unknown>>(
@@ -194,64 +194,30 @@ export function useDataTable<TRow extends Record<string, unknown>>(
194
194
  );
195
195
 
196
196
  // ---------------------------------------------------------------------------
197
- // Props generators
197
+ // Sort (delegated from collection)
198
198
  // ---------------------------------------------------------------------------
199
- const rootProps = useMemo<DataTableRootProps<TRow>>(() => {
200
- return {
201
- columns: visibleColumns,
202
- rows,
203
- loading,
204
- error,
205
- onSort: collection
206
- ? (field: string, direction?: "Asc" | "Desc") =>
207
- collection.setSort(field, direction)
208
- : undefined,
209
- sortStates: collection?.sortStates,
210
- rowOperations: { updateRow, deleteRow, insertRow },
211
- children: null,
212
- };
213
- }, [
214
- visibleColumns,
215
- rows,
216
- loading,
217
- error,
218
- collection,
219
- updateRow,
220
- deleteRow,
221
- insertRow,
222
- ]);
223
-
224
- const getRowProps = useCallback(
225
- (row: TRow): DataTableRowProps<TRow> => ({ row }),
226
- [],
227
- );
228
-
229
- const getCellProps = useCallback(
230
- (
231
- row: TRow,
232
- column: Column<TRow>,
233
- rowIndex: number,
234
- ): DataTableCellProps<TRow> => ({
235
- row,
236
- column,
237
- rowIndex,
238
- }),
239
- [],
240
- );
199
+ const sortStates = useMemo<SortState[]>(() => {
200
+ return collection?.sortStates ?? [];
201
+ }, [collection?.sortStates]);
202
+
203
+ const onSort = useMemo<
204
+ ((field: string, direction?: "Asc" | "Desc") => void) | undefined
205
+ >(() => {
206
+ if (!collection) return undefined;
207
+ return (field: string, direction?: "Asc" | "Desc") =>
208
+ collection.setSort(field, direction);
209
+ }, [collection]);
241
210
 
242
211
  // ---------------------------------------------------------------------------
243
212
  // Return
244
213
  // ---------------------------------------------------------------------------
245
214
  return {
246
- // Props generators
247
- rootProps,
248
- getRowProps,
249
- getCellProps,
250
-
251
215
  // Data
252
216
  rows,
253
217
  loading,
254
218
  error,
219
+ sortStates,
220
+ onSort,
255
221
 
256
222
  // Pagination
257
223
  pageInfo,
@@ -23,9 +23,6 @@ export type {
23
23
  UseDataTableOptions,
24
24
  UseDataTableReturn,
25
25
  RowOperations,
26
- DataTableRootProps,
27
- DataTableRowProps,
28
- DataTableCellProps,
29
26
  FieldName,
30
27
  TableFieldName,
31
28
  OrderableFieldName,
@@ -62,7 +59,7 @@ export {
62
59
  export { Table } from "./table";
63
60
 
64
61
  // DataTable (data-bound)
65
- export { DataTable } from "./data-table/index";
62
+ export { DataTable } from "./data-table/data-table";
66
63
  export { useDataTable } from "./data-table/use-data-table";
67
64
  export { useDataTableContext } from "./data-table/data-table-context";
68
65
 
@@ -511,63 +511,10 @@ export interface RowOperations<TRow extends Record<string, unknown>> {
511
511
  insertRow: (row: TRow) => { rollback: () => void };
512
512
  }
513
513
 
514
- /**
515
- * Props for `DataTable.Root` component (generated by `useDataTable`).
516
- */
517
- export interface DataTableRootProps<TRow extends Record<string, unknown>> {
518
- /** Visible column definitions */
519
- columns: Column<TRow>[];
520
- /** Row data */
521
- rows: TRow[];
522
- /** Loading state */
523
- loading?: boolean;
524
- /** Error */
525
- error?: Error | null;
526
- /** Sort handler (connected to collection) */
527
- onSort?: (field: string, direction?: "Asc" | "Desc") => void;
528
- /** Current sort states */
529
- sortStates?: SortState[];
530
- /** Row operations (provided to children via Context) */
531
- rowOperations?: RowOperations<TRow>;
532
- children: ReactNode;
533
- }
534
-
535
- /**
536
- * Props for `DataTable.Row` component.
537
- */
538
- export interface DataTableRowProps<TRow extends Record<string, unknown>> {
539
- /** The row data */
540
- row: TRow;
541
- }
542
-
543
- /**
544
- * Props for `DataTable.Cell` component.
545
- */
546
- export interface DataTableCellProps<TRow extends Record<string, unknown>> {
547
- /** The row data */
548
- row: TRow;
549
- /** The column definition */
550
- column: Column<TRow>;
551
- /** The row index */
552
- rowIndex: number;
553
- }
554
-
555
514
  /**
556
515
  * Return type of `useDataTable` hook.
557
516
  */
558
517
  export interface UseDataTableReturn<TRow extends Record<string, unknown>> {
559
- // Props generators (for spreading)
560
- /** Props for DataTable.Root */
561
- rootProps: DataTableRootProps<TRow>;
562
- /** Get props for a DataTable.Row */
563
- getRowProps: (row: TRow) => DataTableRowProps<TRow>;
564
- /** Get props for a DataTable.Cell */
565
- getCellProps: (
566
- row: TRow,
567
- column: Column<TRow>,
568
- rowIndex: number,
569
- ) => DataTableCellProps<TRow>;
570
-
571
518
  // Data
572
519
  /** Row data extracted from collection result */
573
520
  rows: TRow[];
@@ -575,6 +522,10 @@ export interface UseDataTableReturn<TRow extends Record<string, unknown>> {
575
522
  loading: boolean;
576
523
  /** Error */
577
524
  error: Error | null;
525
+ /** Current sort states */
526
+ sortStates: SortState[];
527
+ /** Sort handler (connected to collection) */
528
+ onSort?: (field: string, direction?: "Asc" | "Desc") => void;
578
529
 
579
530
  // Pagination (delegated from collection)
580
531
  /** Page info from GraphQL response */