@izumisy-tailor/tailor-data-viewer 0.2.21 → 0.2.22
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,5 +1,11 @@
|
|
|
1
1
|
import { createContext, useContext } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Column,
|
|
4
|
+
PageInfo,
|
|
5
|
+
RowAction,
|
|
6
|
+
RowOperations,
|
|
7
|
+
SortState,
|
|
8
|
+
} from "../types";
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
11
|
* Context value provided by `DataTable.Provider`.
|
|
@@ -218,30 +218,21 @@ function DataTableBody({
|
|
|
218
218
|
<Table.Body className={className}>
|
|
219
219
|
{loading && (!rows || rows.length === 0) && (
|
|
220
220
|
<Table.Row>
|
|
221
|
-
<Table.Cell
|
|
222
|
-
colSpan={totalColSpan}
|
|
223
|
-
className="h-24 text-center"
|
|
224
|
-
>
|
|
221
|
+
<Table.Cell colSpan={totalColSpan} className="h-24 text-center">
|
|
225
222
|
<span className="text-muted-foreground">Loading...</span>
|
|
226
223
|
</Table.Cell>
|
|
227
224
|
</Table.Row>
|
|
228
225
|
)}
|
|
229
226
|
{error && (
|
|
230
227
|
<Table.Row>
|
|
231
|
-
<Table.Cell
|
|
232
|
-
colSpan={totalColSpan}
|
|
233
|
-
className="h-24 text-center"
|
|
234
|
-
>
|
|
228
|
+
<Table.Cell colSpan={totalColSpan} className="h-24 text-center">
|
|
235
229
|
<span className="text-destructive">Error: {error.message}</span>
|
|
236
230
|
</Table.Cell>
|
|
237
231
|
</Table.Row>
|
|
238
232
|
)}
|
|
239
233
|
{!loading && !error && (!rows || rows.length === 0) && (
|
|
240
234
|
<Table.Row>
|
|
241
|
-
<Table.Cell
|
|
242
|
-
colSpan={totalColSpan}
|
|
243
|
-
className="h-24 text-center"
|
|
244
|
-
>
|
|
235
|
+
<Table.Cell colSpan={totalColSpan} className="h-24 text-center">
|
|
245
236
|
<span className="text-muted-foreground">No data</span>
|
|
246
237
|
</Table.Cell>
|
|
247
238
|
</Table.Row>
|
|
@@ -374,15 +365,16 @@ function RowActionsMenu<TRow extends Record<string, unknown>>({
|
|
|
374
365
|
}, [open]);
|
|
375
366
|
|
|
376
367
|
// Close on Escape
|
|
377
|
-
const handleKeyDown = useCallback(
|
|
378
|
-
(e
|
|
379
|
-
|
|
380
|
-
},
|
|
381
|
-
[],
|
|
382
|
-
);
|
|
368
|
+
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
369
|
+
if (e.key === "Escape") setOpen(false);
|
|
370
|
+
}, []);
|
|
383
371
|
|
|
384
372
|
return (
|
|
385
|
-
<div
|
|
373
|
+
<div
|
|
374
|
+
className="relative inline-block"
|
|
375
|
+
ref={menuRef}
|
|
376
|
+
onKeyDown={handleKeyDown}
|
|
377
|
+
>
|
|
386
378
|
<button
|
|
387
379
|
type="button"
|
|
388
380
|
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent"
|
|
@@ -73,6 +73,43 @@ describe("createColumnHelper()", () => {
|
|
|
73
73
|
expect(col.width).toBe(100);
|
|
74
74
|
expect(typeof col.render).toBe("function");
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
it("inferColumns returns column/columns helpers with TRow bound", () => {
|
|
78
|
+
type TaskRow = { id: string; title: string; status: string };
|
|
79
|
+
const metadata = {
|
|
80
|
+
name: "task",
|
|
81
|
+
pluralForm: "tasks",
|
|
82
|
+
readAllowedRoles: [],
|
|
83
|
+
fields: [
|
|
84
|
+
{ name: "id", type: "uuid", required: true },
|
|
85
|
+
{ name: "title", type: "string", required: true },
|
|
86
|
+
{
|
|
87
|
+
name: "status",
|
|
88
|
+
type: "enum",
|
|
89
|
+
required: true,
|
|
90
|
+
enumValues: ["todo", "done"],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
} as const;
|
|
94
|
+
|
|
95
|
+
const { inferColumns, display } = createColumnHelper<TaskRow>();
|
|
96
|
+
const { column, columns } = inferColumns(metadata);
|
|
97
|
+
|
|
98
|
+
// column() works with auto-detection
|
|
99
|
+
const titleCol = column("title");
|
|
100
|
+
expect(titleCol.dataKey).toBe("title");
|
|
101
|
+
expect(titleCol.sort).toEqual({ type: "string" });
|
|
102
|
+
|
|
103
|
+
// columns() works
|
|
104
|
+
const cols = columns(["title", "status"]);
|
|
105
|
+
expect(cols).toHaveLength(2);
|
|
106
|
+
|
|
107
|
+
// display still works alongside inferColumns
|
|
108
|
+
const actionsCol = display("actions", {
|
|
109
|
+
render: (row) => `${row.title}`,
|
|
110
|
+
});
|
|
111
|
+
expect(actionsCol.kind).toBe("display");
|
|
112
|
+
});
|
|
76
113
|
});
|
|
77
114
|
|
|
78
115
|
describe("fieldTypeToSortConfig", () => {
|
|
@@ -87,21 +87,27 @@ export function display<TRow extends Record<string, unknown>>(
|
|
|
87
87
|
// =============================================================================
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* Create `field` and `
|
|
90
|
+
* Create `field`, `display`, and `inferColumns` helpers with TRow bound once.
|
|
91
91
|
*
|
|
92
|
-
* This avoids repeating the row type parameter on every
|
|
92
|
+
* This avoids repeating the row type parameter on every helper call.
|
|
93
|
+
* Use `inferColumns(tableMetadata)` to create metadata-inferred columns
|
|
94
|
+
* without specifying TRow again.
|
|
93
95
|
*
|
|
94
96
|
* @example
|
|
95
97
|
* ```tsx
|
|
96
98
|
* type Order = { id: string; title: string; amount: number };
|
|
97
99
|
*
|
|
98
|
-
* const { field, display } = createColumnHelper<Order>();
|
|
100
|
+
* const { field, display, inferColumns } = createColumnHelper<Order>();
|
|
99
101
|
*
|
|
100
|
-
*
|
|
102
|
+
* // Manual columns
|
|
103
|
+
* const manualColumns = [
|
|
101
104
|
* field("title", { label: "Title", sort: { type: "string" } }),
|
|
102
|
-
* field("amount", { label: "Amount", sort: { type: "number" } }),
|
|
103
105
|
* display("actions", { render: (row) => <ActionMenu row={row} /> }),
|
|
104
106
|
* ];
|
|
107
|
+
*
|
|
108
|
+
* // Metadata-inferred columns (no need to pass TRow again)
|
|
109
|
+
* const { column } = inferColumns(tableMetadata.order);
|
|
110
|
+
* const inferredColumns = [column("title"), column("amount")];
|
|
105
111
|
* ```
|
|
106
112
|
*/
|
|
107
113
|
export function createColumnHelper<TRow extends Record<string, unknown>>(): {
|
|
@@ -113,10 +119,23 @@ export function createColumnHelper<TRow extends Record<string, unknown>>(): {
|
|
|
113
119
|
id: string,
|
|
114
120
|
options: DisplayColumnOptions<TRow>,
|
|
115
121
|
) => DisplayColumn<TRow>;
|
|
122
|
+
inferColumns: <const TTable extends TableMetadata>(
|
|
123
|
+
tableMetadata: TTable,
|
|
124
|
+
) => {
|
|
125
|
+
column: (
|
|
126
|
+
dataKey: TableFieldName<TTable>,
|
|
127
|
+
options?: MetadataFieldOptions<TRow>,
|
|
128
|
+
) => FieldColumn<TRow>;
|
|
129
|
+
columns: (
|
|
130
|
+
dataKeys: TableFieldName<TTable>[],
|
|
131
|
+
options?: MetadataFieldsOptions,
|
|
132
|
+
) => FieldColumn<TRow>[];
|
|
133
|
+
};
|
|
116
134
|
} {
|
|
117
135
|
return {
|
|
118
136
|
field: (dataKey, options) => field<TRow>(dataKey, options),
|
|
119
137
|
display: (id, options) => display<TRow>(id, options),
|
|
138
|
+
inferColumns: (tableMetadata) => inferColumnHelper<TRow>(tableMetadata),
|
|
120
139
|
};
|
|
121
140
|
}
|
|
122
141
|
|
package/src/component/types.ts
CHANGED
|
@@ -405,9 +405,11 @@ export interface UseCollectionOptions<
|
|
|
405
405
|
* Methods that accept a field name are typed with `TFieldName` so that
|
|
406
406
|
* auto-completion works when a concrete union is supplied.
|
|
407
407
|
*
|
|
408
|
-
* **Note:** Methods use *method syntax*
|
|
408
|
+
* **Note:** Methods that accept `TFieldName` use *method syntax* intentionally
|
|
409
409
|
* so that `UseCollectionReturn<"a" | "b">` remains assignable to
|
|
410
410
|
* `UseCollectionReturn<string>` (bivariant method check).
|
|
411
|
+
* Methods that don't depend on `TFieldName` use property syntax so they
|
|
412
|
+
* can be safely destructured without triggering `unbound-method` lint rules.
|
|
411
413
|
*
|
|
412
414
|
* @typeParam TFieldName - Union of allowed field name strings (default: `string`).
|
|
413
415
|
* @typeParam TQueryArgs - Type returned by `toQueryArgs()`. Contains both
|
|
@@ -428,7 +430,7 @@ export interface UseCollectionReturn<
|
|
|
428
430
|
* const [result] = useQuery({ ...collection.toQueryArgs() });
|
|
429
431
|
* ```
|
|
430
432
|
*/
|
|
431
|
-
toQueryArgs()
|
|
433
|
+
toQueryArgs: () => TQueryArgs;
|
|
432
434
|
|
|
433
435
|
// Filter operations
|
|
434
436
|
/** Current active filters */
|
|
@@ -440,11 +442,11 @@ export interface UseCollectionReturn<
|
|
|
440
442
|
value: unknown,
|
|
441
443
|
): void;
|
|
442
444
|
/** Replace all filters at once */
|
|
443
|
-
setFilters(filters: Filter[])
|
|
445
|
+
setFilters: (filters: Filter[]) => void;
|
|
444
446
|
/** Remove filter for a specific field */
|
|
445
447
|
removeFilter(field: TFieldName): void;
|
|
446
448
|
/** Clear all filters */
|
|
447
|
-
clearFilters()
|
|
449
|
+
clearFilters: () => void;
|
|
448
450
|
|
|
449
451
|
// Sort operations
|
|
450
452
|
/** Current sort states (supports multi-sort) */
|
|
@@ -456,7 +458,7 @@ export interface UseCollectionReturn<
|
|
|
456
458
|
append?: boolean,
|
|
457
459
|
): void;
|
|
458
460
|
/** Clear all sort states */
|
|
459
|
-
clearSort()
|
|
461
|
+
clearSort: () => void;
|
|
460
462
|
|
|
461
463
|
// Pagination operations
|
|
462
464
|
/** Number of items per page */
|
|
@@ -466,17 +468,17 @@ export interface UseCollectionReturn<
|
|
|
466
468
|
/** Current pagination direction */
|
|
467
469
|
paginationDirection: "forward" | "backward";
|
|
468
470
|
/** Navigate to next page using endCursor from pageInfo */
|
|
469
|
-
nextPage(endCursor: string)
|
|
471
|
+
nextPage: (endCursor: string) => void;
|
|
470
472
|
/** Navigate to previous page using startCursor from pageInfo */
|
|
471
|
-
prevPage(startCursor: string)
|
|
473
|
+
prevPage: (startCursor: string) => void;
|
|
472
474
|
/** Reset to first page */
|
|
473
|
-
resetPage()
|
|
475
|
+
resetPage: () => void;
|
|
474
476
|
/** Whether there is a previous page (from GraphQL pageInfo) */
|
|
475
477
|
hasPrevPage: boolean;
|
|
476
478
|
/** Whether there is a next page (from GraphQL pageInfo) */
|
|
477
479
|
hasNextPage: boolean;
|
|
478
480
|
/** Set pageInfo from graphql result to track hasPrevPage/hasNextPage */
|
|
479
|
-
setPageInfo(pageInfo: PageInfo)
|
|
481
|
+
setPageInfo: (pageInfo: PageInfo) => void;
|
|
480
482
|
}
|
|
481
483
|
|
|
482
484
|
// =============================================================================
|