@izumisy-tailor/tailor-data-viewer 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -1,21 +1,21 @@
1
1
  # Tailor Data Viewer
2
2
 
3
- A React component library for building data exploration interfaces with GraphQL backend support. Provides a tab-based spreadsheet-like UI for viewing and managing table data with relation navigation.
3
+ A low-level React component library for building data table interfaces with Tailor Platform (GraphQL) backends. Provides composable hooks and compound components for query parameter management, table rendering, filtering, sorting, and pagination.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Tab-based Interface**: Excel-like sheet management with multiple tabs
8
- - **Table Selection**: Role-based access control for tables
9
- - **Column Selection**: Dynamic column visibility with manyToOne relation expansion
10
- - **Relation Navigation**: Expandable inline relations with "Open as Sheet" functionality
11
- - **Search & Filter**: AND-based filtering on string, number, boolean, and enum fields
12
- - **View Persistence**: Save and load views with filter and column configurations
13
- - **Sorting & Pagination**: Full cursor-based pagination support
14
- - **CSV Export**: Download current view as CSV
15
- - **Single Record View**: Detailed single record view with all relations
16
- - **Custom Labels**: Internationalization support for field names and UI text
17
- - **Custom Renderers**: Built-in and custom cell renderers with pattern matching support
18
- - **File Type Support**: Automatic query expansion and thumbnail renderers for TailorDB File fields
7
+ - **Separation of Concerns**: Data fetching, query parameter management, and UI are fully decoupled
8
+ - **`useCollectionParams` Hook**: Manages filter, sort, and pagination state; outputs Tailor Platform-compatible GraphQL variables
9
+ - **`CollectionParams.Provider`**: Shares query parameters via React Context across sibling components
10
+ - **`Table.*` Compound Components**: Static, unstyled table primitives (`<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>`, `<td>`)
11
+ - **`DataTable.*` Compound Components**: Data-bound table with sort indicators, cell renderers, and `useDataTable` integration
12
+ - **`useDataTable` Hook**: Integrates data, column visibility, row operations (optimistic updates), and props generators
13
+ - **Column Definition Helpers**: `field()` for data columns with sort/filter, `display()` for render-only columns
14
+ - **Metadata-based Inference**: `inferColumnHelper()` auto-derives sort/filter config from generated table metadata
15
+ - **Utility Components**: `ColumnSelector`, `CsvButton`, `SearchFilterForm`, `Pagination` all props-based, spreadable from hooks
16
+ - **Multi-sort Support**: Multiple simultaneous sort fields
17
+ - **Optimistic Updates**: `updateRow`, `deleteRow`, `insertRow` with rollback
18
+ - **Presentation Agnostic**: Same `useCollectionParams` can drive tables, kanbans, calendars, etc.
19
19
 
20
20
  ## Installation
21
21
 
@@ -39,209 +39,156 @@ This provides AI-optimized documentation for better code generation and assistan
39
39
 
40
40
  ### Peer Dependencies
41
41
 
42
- This library requires the following peer dependencies:
43
-
44
42
  ```bash
45
43
  npm install react react-dom
46
44
  ```
47
45
 
48
- ## Usage
49
-
50
- There are two ways to use Data Viewer:
51
-
52
- 1. **AppShell Module** - Recommended for Tailor Platform apps
53
- 2. **Standalone Component** - For custom React apps
54
-
55
- ### AppShell Module (Recommended)
56
-
57
- For Tailor Platform apps using `@tailor-platform/app-shell`:
58
-
59
- ```typescript
60
- // src/shared/data-viewer.ts
61
- import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
62
- import { tableMetadata } from "./generated/table-metadata";
63
-
64
- const fetcher = createDefaultFetcher({
65
- endpoint: import.meta.env.VITE_APP_URL,
66
- });
67
-
68
- export const DataViewer = createDataViewer({
69
- metadata: tableMetadata,
70
- fetcher,
71
- // Optional: Custom labels for fields and UI text
72
- labels: {
73
- "Task:status": "ステータス",
74
- "$:refresh": "Refresh",
75
- },
76
- });
77
- ```
78
-
79
- ```typescript
80
- // src/pages/data-view/index.ts
81
- import { createDataViewModule } from "@izumisy-tailor/tailor-data-viewer/app-shell";
82
- import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
83
- import { DataViewer } from "../../shared/data-viewer";
84
-
85
- export const dataViewModule = createDataViewModule({
86
- dataViewer: DataViewer,
87
- savedViewStore: createIndexedDBStore(),
88
- });
89
- ```
90
-
91
- ```typescript
92
- // src/app.tsx
93
- import { AppShell } from "@tailor-platform/app-shell";
94
- import { dataViewModule } from "./pages/data-view";
95
-
96
- export const App = () => (
97
- <AppShell
98
- modules={[dataViewModule]}
99
- />
100
- );
101
- ```
102
-
103
- For detailed options, see [AppShell Module Integration](./docs/app-shell-module.md).
104
-
105
- ### Standalone Explorer View
106
-
107
- For custom React apps without AppShell, use `DataViewer.ExplorerView`. This provides a tab-based UI where users can select tables, create filters, and save views:
46
+ ## Quick Start
108
47
 
109
48
  ```tsx
110
- // src/lib/data-viewer.ts (create once per app)
111
- import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
112
- import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
113
- import { tableMetadata } from "./generated/table-metadata";
114
-
115
- const fetcher = createDefaultFetcher({
116
- endpoint: "https://your-app.tailor.tech/graphql",
117
- });
118
-
119
- export const DataViewer = createDataViewer({
120
- metadata: tableMetadata,
121
- fetcher,
122
- // Optional: Custom labels for fields and UI text
123
- labels: {
124
- "orders:status": "Order Status",
125
- "*:createdAt": "Created Date",
126
- },
127
- });
49
+ import {
50
+ useCollectionParams,
51
+ useDataTable,
52
+ CollectionParams,
53
+ DataTable,
54
+ Pagination,
55
+ field,
56
+ display,
57
+ } from "@izumisy-tailor/tailor-data-viewer/component";
58
+ import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
128
59
 
129
- export const savedViewStore = createIndexedDBStore();
130
- ```
60
+ // 1. Define columns
61
+ const columns = [
62
+ field<Order>("name", {
63
+ label: "Name",
64
+ sort: { type: "string" },
65
+ filter: { type: "string" },
66
+ }),
67
+ field<Order>("amount", {
68
+ label: "Amount",
69
+ sort: { type: "number" },
70
+ filter: { type: "number" },
71
+ }),
72
+ field<Order>("status", {
73
+ label: "Status",
74
+ filter: {
75
+ type: "enum",
76
+ options: [
77
+ { value: "DRAFT", label: "Draft" },
78
+ { value: "APPROVED", label: "Approved" },
79
+ ],
80
+ },
81
+ }),
82
+ display<Order>("actions", {
83
+ width: 50,
84
+ render: (row) => <button onClick={() => handleEdit(row)}>Edit</button>,
85
+ }),
86
+ ];
131
87
 
132
- ```tsx
133
- // src/App.tsx
134
- import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
135
- import { DataViewer, savedViewStore } from "./lib/data-viewer";
88
+ // 2. Build a page
89
+ function OrdersPage() {
90
+ const params = useCollectionParams({ pageSize: 20 });
91
+ const [result] = useQuery({ query: GET_ORDERS, variables: params.variables });
136
92
 
137
- function App() {
138
- return (
139
- <DataViewer.ExplorerView savedViewStore={savedViewStore} />
140
- );
141
- }
93
+ const table = useDataTable<Order>({
94
+ columns,
95
+ data: result.data?.orders,
96
+ loading: result.fetching,
97
+ collectionParams: params,
98
+ });
142
99
 
143
- // With initial view loaded from URL
144
- function AppWithViewId() {
145
- const viewId = useSearchParams().get("viewId") ?? undefined;
146
100
  return (
147
- <DataViewer.ExplorerView
148
- savedViewStore={savedViewStore}
149
- initialViewId={viewId}
150
- />
101
+ <CollectionParams.Provider value={params}>
102
+ <DataTable.Root {...table.rootProps}>
103
+ <DataTable.Headers />
104
+ <DataTable.Body />
105
+ </DataTable.Root>
106
+ <Pagination {...table} />
107
+ </CollectionParams.Provider>
151
108
  );
152
109
  }
153
110
  ```
154
111
 
155
- For store options, see [Saved View Store](./docs/saved-view-store.md).
112
+ ## API Overview
156
113
 
157
- ### Compositional API
114
+ ### `useCollectionParams(options?)`
158
115
 
159
- For more control over the UI layout and behavior, use the compositional (compound component) API with `createDataViewer`:
116
+ Manages filter, sort, and pagination state. Returns Tailor Platform-compatible `variables` for GraphQL queries.
160
117
 
161
118
  ```tsx
162
- // src/shared/data-viewer.ts
163
- import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
164
- import { tableMetadata } from "./generated/table-metadata";
165
-
166
- const fetcher = createDefaultFetcher({
167
- endpoint: "https://your-app.tailor.tech",
119
+ const params = useCollectionParams({
120
+ pageSize: 20,
121
+ initialSort: [{ field: "createdAt", direction: "Desc" }],
168
122
  });
169
123
 
170
- export const DataViewer = createDataViewer({
171
- metadata: tableMetadata,
172
- fetcher,
173
- // Optional: Custom labels and renderers
174
- labels: {
175
- "User:name": "User Name",
176
- "*:createdAt": "Created",
177
- },
178
- });
179
- ```
124
+ // Pass variables to any GraphQL client
125
+ const [result] = useQuery({ query: GET_ORDERS, variables: params.variables });
180
126
 
181
- ```tsx
182
- // src/pages/users/page.tsx
183
- import { DataViewer } from "../../shared/data-viewer";
184
- import {
185
- DataTable,
186
- ColumnSelector,
187
- SearchFilterForm,
188
- CsvButton,
189
- RefreshButton,
190
- useDataViewer,
191
- useTableDataContext,
192
- } from "@izumisy-tailor/tailor-data-viewer/component";
127
+ // Filter operations
128
+ params.addFilter("status", "ACTIVE", "eq");
129
+ params.setFilters([{ field: "status", fieldType: "enum", operator: "eq", value: "ACTIVE" }]);
130
+ params.removeFilter("status");
131
+ params.clearFilters();
193
132
 
194
- function CustomDataViewer() {
195
- return (
196
- <DataViewer.TableDataProvider
197
- tableName="User"
198
- columns={["id", "name", "email"]}
199
- initialData={{
200
- sort: { field: "name", direction: "Asc" },
201
- }}
202
- >
203
- <DataViewer.ToolbarProvider>
204
- <ColumnSelector />
205
- <SearchFilterForm />
206
- <CsvButton />
207
- <RefreshButton />
208
- </DataViewer.ToolbarProvider>
209
- <DataTable onRecordClick={(id) => console.log(id)} />
210
- </DataViewer.TableDataProvider>
211
- );
212
- }
133
+ // Sort operations
134
+ params.setSort("createdAt", "Desc");
135
+ params.setSort("name", "Asc", true); // append for multi-sort
136
+ params.clearSort();
137
+
138
+ // Pagination
139
+ params.nextPage(endCursor);
140
+ params.prevPage();
141
+ params.resetPage();
213
142
  ```
214
143
 
215
- This approach allows you to:
216
- - Customize the layout and styling
217
- - Add your own components alongside Data Viewer components
218
- - Access internal state via hooks (`useDataViewer`, `useTableDataContext`)
219
- - Share the same DataViewer instance across multiple pages
144
+ ### `CollectionParams.Provider` / `useCollectionParamsContext()`
220
145
 
221
- For detailed documentation, see [Compositional API](./docs/compositional-api.md).
146
+ Shares `useCollectionParams` return value via Context. Child components access it with `useCollectionParamsContext()`.
222
147
 
223
- ### About savedViewStore
148
+ ```tsx
149
+ <CollectionParams.Provider value={params}>
150
+ <StatusFilter /> {/* useCollectionParamsContext() inside */}
151
+ <DataTable.Root {...table.rootProps}>
152
+ <DataTable.Headers />
153
+ <DataTable.Body />
154
+ </DataTable.Root>
155
+ <Pagination {...table} />
156
+ </CollectionParams.Provider>
157
+ ```
224
158
 
225
- `savedViewStore` is a storage implementation for persisting views created in Data Viewer (such as filter settings and column visibility settings). Two built-in stores are provided:
159
+ Provider is optional for simple cases, pass params directly via props.
226
160
 
227
- - **IndexedDB Store** (`createIndexedDBStore`): Stores data in browser's IndexedDB. Ideal for development, personal use, and offline support
228
- - **TailorDB Store** (`createTailorDBStore`): Stores data on the server side. Ideal for sharing views across teams
161
+ ### Column Definition Helpers
229
162
 
230
- For more details, see [Saved View Store](./docs/saved-view-store.md).
163
+ #### `field(dataKey, options?)`
231
164
 
232
- ### Generating Table Metadata
165
+ Defines a data-bound column. Supports sort and filter configuration.
233
166
 
234
- This library includes a custom generator for [Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/sdk) that automatically generates table metadata from your TailorDB schema.
167
+ ```tsx
168
+ field("name", {
169
+ label: "Name",
170
+ sort: { type: "string" },
171
+ filter: { type: "string" },
172
+ renderer: ({ value }) => <strong>{value}</strong>,
173
+ })
174
+ ```
235
175
 
236
- #### Setup
176
+ #### `display(id, options)`
237
177
 
238
- 1. Install the generator package in your Tailor Platform SDK project:
178
+ Defines a render-only column (no sort/filter).
239
179
 
240
- ```bash
241
- npm install @izumisy-tailor/tailor-data-viewer
180
+ ```tsx
181
+ display("actions", {
182
+ width: 50,
183
+ render: (row) => <ActionMenu row={row} />,
184
+ })
242
185
  ```
243
186
 
244
- 2. Configure the generator in your `tailor.config.ts`:
187
+ ### Table Metadata Generator
188
+
189
+ This library includes a metadata generator for [Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/sdk) that produces type-safe table metadata with `as const` assertions. The generated metadata is used by `inferColumnHelper()` for automatic sort/filter configuration.
190
+
191
+ 1. Configure the generator in your `tailor.config.ts`:
245
192
 
246
193
  ```typescript
247
194
  import { defineConfig, defineGenerators } from "@tailor-platform/sdk";
@@ -251,7 +198,6 @@ export const generators = defineGenerators(
251
198
  dataViewerMetadataGenerator({
252
199
  distPath: "src/generated/data-viewer-metadata.generated.ts",
253
200
  }),
254
- // ... other generators
255
201
  );
256
202
 
257
203
  export default defineConfig({
@@ -260,30 +206,202 @@ export default defineConfig({
260
206
  });
261
207
  ```
262
208
 
263
- 3. Run the generator:
209
+ 2. Run the generator:
264
210
 
265
211
  ```bash
266
212
  tailor-sdk generate
267
213
  ```
268
214
 
269
- This will generate a metadata file at the specified `distPath` containing type-safe metadata for all your TailorDB types, including fields, relations, and role-based access control settings.
215
+ ### `inferColumnHelper(metadata, tableName)`
216
+
217
+ `field()` requires manually specifying `sort`/`filter` type configs and enum `options` for every column. `inferColumnHelper()` eliminates this boilerplate by automatically deriving these from the generated table metadata. Based on each field's type (string, number, date, enum, etc.), the appropriate `SortConfig` / `FilterConfig` is set automatically, and enum fields get their options populated from the schema.
218
+
219
+ ```tsx
220
+ import { inferColumnHelper, display } from "@izumisy-tailor/tailor-data-viewer/component";
221
+ import { tableMetadata } from "./generated/data-viewer-metadata.generated";
222
+
223
+ const { column, columns } = inferColumnHelper(tableMetadata, "task");
224
+
225
+ const taskColumns = [
226
+ column("title"), // sort/filter auto-configured from metadata
227
+ column("status"), // enum options auto-derived
228
+ column("dueDate"), // date type auto-detected
229
+ column("priority", { sort: false }), // override: disable sort
230
+ ...columns(["name", "email"]), // batch definition
231
+ display("actions", {
232
+ render: (row) => <ActionMenu row={row} />,
233
+ }),
234
+ ];
235
+ ```
236
+
237
+ `inferColumnHelper()` can be freely mixed with manual `field()` / `display()` definitions. Use `field()` for fields not in the metadata or those requiring custom configuration, and `inferColumnHelper()` for everything else.
238
+
239
+ ### `useDataTable(options)`
240
+
241
+ Integrates data, column visibility, row operations, and props generators.
242
+
243
+ ```tsx
244
+ const table = useDataTable<Order>({
245
+ columns,
246
+ data: result.data?.orders, // CollectionResult<Order>
247
+ loading: result.fetching,
248
+ error: result.error,
249
+ collectionParams: params,
250
+ });
270
251
 
271
- ## API Reference
252
+ // Spread props to components
253
+ <DataTable.Root {...table.rootProps}>...</DataTable.Root>
254
+ <ColumnSelector {...table} />
255
+ <CsvButton {...table} />
256
+ <Pagination {...table} />
257
+
258
+ // Column visibility
259
+ table.toggleColumn("amount");
260
+ table.showAllColumns();
261
+ table.hideAllColumns();
262
+ table.isColumnVisible("amount");
263
+
264
+ // Optimistic updates
265
+ const { rollback } = table.updateRow(rowId, { status: "APPROVED" });
266
+ const { rollback, deletedRow } = table.deleteRow(rowId);
267
+ const { rollback } = table.insertRow(newRow);
268
+ ```
269
+
270
+ ### `Table.*` — Static Table Components
271
+
272
+ Low-level table primitives without data binding. Use for fully custom layouts or skeleton loading.
273
+
274
+ ```tsx
275
+ <Table.Root>
276
+ <Table.Headers>
277
+ <Table.HeaderRow>
278
+ <Table.HeaderCell>Name</Table.HeaderCell>
279
+ <Table.HeaderCell>Status</Table.HeaderCell>
280
+ </Table.HeaderRow>
281
+ </Table.Headers>
282
+ <Table.Body>
283
+ <Table.Row>
284
+ <Table.Cell>Order A</Table.Cell>
285
+ <Table.Cell>Approved</Table.Cell>
286
+ </Table.Row>
287
+ </Table.Body>
288
+ </Table.Root>
289
+ ```
272
290
 
273
- See [API.md](API.md) for detailed API documentation including components, hooks, and types.
291
+ ### `DataTable.*` Data-bound Table Components
292
+
293
+ Pair with `useDataTable` for automatic header sorting, cell rendering, and row operations.
294
+
295
+ ```tsx
296
+ // Basic usage (spread rootProps)
297
+ <DataTable.Root {...table.rootProps}>
298
+ <DataTable.Headers />
299
+ <DataTable.Body />
300
+ </DataTable.Root>
301
+
302
+ // Custom row rendering
303
+ <DataTable.Root {...table.rootProps}>
304
+ <DataTable.Headers />
305
+ <DataTable.Body>
306
+ {table.rows.map((row, rowIndex) => (
307
+ <DataTable.Row
308
+ key={row.id}
309
+ {...table.getRowProps(row)}
310
+ onClick={() => navigate(`/orders/${row.id}`)}
311
+ >
312
+ {table.visibleColumns.map((col) => (
313
+ <DataTable.Cell key={col.id} {...table.getCellProps(row, col, rowIndex)} />
314
+ ))}
315
+ </DataTable.Row>
316
+ ))}
317
+ </DataTable.Body>
318
+ </DataTable.Root>
319
+ ```
320
+
321
+ ### Utility Components
322
+
323
+ All utility components are props-based and designed to be used with spread from `useDataTable` / `useCollectionParams`.
324
+
325
+ | Component | Spread from | Description |
326
+ |-----------|------------|-------------|
327
+ | `ColumnSelector` | `{...table}` | Column visibility toggle UI |
328
+ | `CsvButton` | `{...table}` | Export visible data as CSV |
329
+ | `SearchFilterForm` | `{...table, ...params}` | Multi-field filter form with operator selection |
330
+ | `Pagination` | `{...table}` | Previous/Next page controls |
331
+
332
+ ```tsx
333
+ <SearchFilterForm {...table} {...params} />
334
+ <ColumnSelector {...table} />
335
+ <CsvButton {...table} filename="orders-export" />
336
+ <Pagination {...table} />
337
+ ```
338
+
339
+ ### Optimistic Updates in Cell Renderers
340
+
341
+ Use `useDataTableContext()` inside `DataTable.Root` to access row operations from custom cell renderers.
342
+
343
+ ```tsx
344
+ const StatusEditor: CellRenderer<Order> = ({ value, row }) => {
345
+ const { updateRow } = useDataTableContext<Order>();
346
+ const [updateOrder] = useMutation(UPDATE_ORDER);
347
+
348
+ const handleChange = async (newStatus: string) => {
349
+ const { rollback } = updateRow(row.id, { status: newStatus });
350
+ try {
351
+ await updateOrder({ id: row.id, input: { status: newStatus } });
352
+ } catch (error) {
353
+ rollback();
354
+ }
355
+ };
356
+
357
+ return <StatusSelect value={value} onChange={handleChange} />;
358
+ };
359
+ ```
360
+
361
+ ### Relation Fields
362
+
363
+ Use `display()` with GraphQL query expansion to show relation data.
364
+
365
+ ```tsx
366
+ // Include relations in your GraphQL query
367
+ const GET_MEMBERSHIPS = graphql(`
368
+ query SupplierGroupMemberships {
369
+ supplierGroupMemberships {
370
+ edges {
371
+ node {
372
+ id
373
+ supplier { companyName, contactName }
374
+ addedBy { name }
375
+ createdAt
376
+ }
377
+ }
378
+ }
379
+ }
380
+ `);
381
+
382
+ // Display relation fields with display()
383
+ const columns = [
384
+ display("supplierCompanyName", {
385
+ label: "Company",
386
+ render: (row) => row.supplier?.companyName ?? "-",
387
+ }),
388
+ display("addedByName", {
389
+ label: "Added By",
390
+ render: (row) => row.addedBy?.name ?? "-",
391
+ }),
392
+ field("createdAt", { label: "Created", sort: { type: "date" } }),
393
+ ];
394
+ ```
274
395
 
275
396
  ## Styling
276
397
 
277
- The library uses Tailwind CSS classes. Import the base theme or provide your own CSS variables:
398
+ The library uses Tailwind CSS classes. Import the included theme or provide your own CSS variables:
278
399
 
279
400
  ```css
280
401
  /* Option 1: Import included theme */
281
402
  @import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
282
403
 
283
- /* Option 2: Use with Tailor app-shell */
284
- @import "@tailor-platform/app-shell/theme.css";
285
-
286
- /* Option 3: Define your own CSS variables */
404
+ /* Option 2: Define your own CSS variables */
287
405
  :root {
288
406
  --background: 0 0% 100%;
289
407
  --foreground: 222.2 84% 4.9%;
@@ -292,6 +410,14 @@ The library uses Tailwind CSS classes. Import the base theme or provide your own
292
410
  }
293
411
  ```
294
412
 
413
+ ## Exports
414
+
415
+ | Entry Point | Description |
416
+ |-------------|-------------|
417
+ | `@izumisy-tailor/tailor-data-viewer/component` | Hooks, components, helpers, and types |
418
+ | `@izumisy-tailor/tailor-data-viewer/generator` | Metadata generator for Tailor SDK |
419
+ | `@izumisy-tailor/tailor-data-viewer/styles` | CSS theme file |
420
+
295
421
  ## Development
296
422
 
297
423
  See the [Development Guide](./DEVELOPMENT.md) for setup and contribution instructions.
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.0",
4
+ "version": "0.2.2",
5
5
  "type": "module",
6
6
  "description": "Flexible data viewer component for Tailor Platform",
7
7
  "files": [
@@ -36,7 +36,10 @@ export function CollectionParamsProvider({
36
36
  /**
37
37
  * Hook to access collection params from the nearest `CollectionParams.Provider`.
38
38
  *
39
- * Returns the same interface as `useCollectionParams()`.
39
+ * Returns the same interface as `useCollectionParams()`. Pass a `TFieldName`
40
+ * type parameter to narrow method arguments like `addFilter` / `setSort`.
41
+ *
42
+ * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
40
43
  *
41
44
  * @throws Error if used outside of `CollectionParams.Provider`.
42
45
  *
@@ -46,16 +49,22 @@ export function CollectionParamsProvider({
46
49
  * const { filters, addFilter, removeFilter } = useCollectionParamsContext();
47
50
  * // ...
48
51
  * }
52
+ *
53
+ * // With typed field names:
54
+ * type TaskField = FieldName<typeof tableMetadata, "task">;
55
+ * const { addFilter } = useCollectionParamsContext<TaskField>();
49
56
  * ```
50
57
  */
51
- export function useCollectionParamsContext(): UseCollectionParamsReturn {
58
+ export function useCollectionParamsContext<
59
+ TFieldName extends string = string,
60
+ >(): UseCollectionParamsReturn<TFieldName> {
52
61
  const ctx = useContext(CollectionParamsContext);
53
62
  if (!ctx) {
54
63
  throw new Error(
55
64
  "useCollectionParamsContext must be used within <CollectionParams.Provider>",
56
65
  );
57
66
  }
58
- return ctx;
67
+ return ctx as UseCollectionParamsReturn<TFieldName>;
59
68
  }
60
69
 
61
70
  /**