@izumisy-tailor/tailor-data-viewer 0.1.33 → 0.1.35

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
@@ -13,6 +13,7 @@ A React component library for building data exploration interfaces with GraphQL
13
13
  - **Sorting & Pagination**: Full cursor-based pagination support
14
14
  - **CSV Export**: Download current view as CSV
15
15
  - **Single Record View**: Detailed single record view with all relations
16
+ - **Custom Labels**: Internationalization support for field names and UI text
16
17
 
17
18
  ## Installation
18
19
 
@@ -55,6 +56,11 @@ const fetcher = createDefaultFetcher({
55
56
  export const DataViewer = createDataViewer({
56
57
  metadata: tableMetadata,
57
58
  fetcher,
59
+ // Optional: Custom labels for fields and UI text
60
+ labels: {
61
+ "Task:status": "ステータス",
62
+ "$:refresh": "Refresh",
63
+ },
58
64
  });
59
65
  ```
60
66
 
@@ -101,6 +107,11 @@ const fetcher = createDefaultFetcher({
101
107
  export const DataViewer = createDataViewer({
102
108
  metadata: tableMetadata,
103
109
  fetcher,
110
+ // Optional: Custom labels for fields and UI text
111
+ labels: {
112
+ "orders:status": "Order Status",
113
+ "*:createdAt": "Created Date",
114
+ },
104
115
  });
105
116
 
106
117
  export const savedViewStore = createIndexedDBStore();
@@ -147,6 +158,11 @@ const fetcher = createDefaultFetcher({
147
158
  export const DataViewer = createDataViewer({
148
159
  metadata: tableMetadata,
149
160
  fetcher,
161
+ // Optional: Custom labels and renderers
162
+ labels: {
163
+ "User:name": "User Name",
164
+ "*:createdAt": "Created",
165
+ },
150
166
  });
151
167
  ```
152
168
 
package/docs/API.md CHANGED
@@ -25,81 +25,6 @@ const DataViewer = createDataViewer({
25
25
  // - DataViewer.fetcher - The fetcher passed to createDataViewer
26
26
  ```
27
27
 
28
- ## GraphQL Fetcher
29
-
30
- ### `GraphQLFetcher`
31
-
32
- Interface for GraphQL data fetching. Implement this interface to use your own GraphQL client.
33
-
34
- ```tsx
35
- interface GraphQLFetcher {
36
- execute: <T = unknown>(
37
- query: string,
38
- variables?: Record<string, unknown>,
39
- options?: GraphQLFetcherOptions,
40
- ) => Promise<GraphQLFetcherResult<T>>;
41
- }
42
-
43
- interface GraphQLFetcherOptions {
44
- signal?: AbortSignal;
45
- }
46
-
47
- interface GraphQLFetcherResult<T> {
48
- data: T | null;
49
- errors?: GraphQLError[];
50
- }
51
- ```
52
-
53
- ### `createDefaultFetcher`
54
-
55
- Creates a default GraphQL fetcher using `graphql-request`.
56
-
57
- ```tsx
58
- import { createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
59
-
60
- const fetcher = createDefaultFetcher({
61
- endpoint: "https://your-app.tailor.tech/graphql",
62
- headers: { "X-Custom-Header": "value" }, // optional
63
- credentials: "include", // optional, default: "include"
64
- });
65
- ```
66
-
67
- ### `createUrqlFetcher`
68
-
69
- Creates a GraphQL fetcher from an existing urql Client.
70
-
71
- ```tsx
72
- import { createClient } from "@urql/core";
73
- import { createUrqlFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
74
-
75
- const urqlClient = createClient({
76
- url: "https://your-app.tailor.tech/graphql",
77
- fetchOptions: {
78
- headers: { "Authorization": "Bearer token" },
79
- },
80
- });
81
-
82
- const fetcher = createUrqlFetcher({ client: urqlClient });
83
- ```
84
-
85
- ### Custom Fetcher Implementation
86
-
87
- You can implement your own fetcher for any GraphQL client:
88
-
89
- ```tsx
90
- const customFetcher: GraphQLFetcher = {
91
- execute: async (query, variables, options) => {
92
- const response = await fetch("/graphql", {
93
- method: "POST",
94
- headers: { "Content-Type": "application/json" },
95
- body: JSON.stringify({ query, variables }),
96
- signal: options?.signal,
97
- });
98
- return response.json();
99
- },
100
- };
101
- ```
102
-
103
28
  ## Components
104
29
 
105
30
  ### `<DataViewer />`
@@ -234,246 +159,3 @@ interface RelationMetadata {
234
159
  }
235
160
  ```
236
161
 
237
- ## Custom Cell Renderers
238
-
239
- ### Overview
240
-
241
- Custom cell renderers allow you to customize how cells are displayed in the DataTable. You can define renderers by field name (with optional wildcard matching) or by field type.
242
-
243
- ### Types
244
-
245
- ```tsx
246
- interface CellRendererProps {
247
- value: unknown;
248
- field: FieldMetadata;
249
- row: Record<string, unknown>;
250
- rowIndex: number;
251
- tableName: string;
252
- tableMetadata: TableMetadata;
253
- }
254
-
255
- type CellRenderer = (props: CellRendererProps) => ReactNode;
256
-
257
- interface CellRenderers {
258
- /** Renderers by "tableName:fieldName" pattern */
259
- byFieldName?: Record<string, CellRenderer>;
260
- /** Renderers by field type (fallback) */
261
- byFieldType?: Partial<Record<FieldType, CellRenderer>>;
262
- }
263
-
264
- interface Renderers {
265
- cell?: CellRenderers;
266
- }
267
- ```
268
-
269
- ### Key Format
270
-
271
- The `byFieldName` object uses a `tableName:fieldName` key format:
272
-
273
- | Key | Description | Matches |
274
- |-----|-------------|---------|
275
- | `orders:status` | Exact match | orders.status |
276
- | `orders:*Email` | Suffix match (camelCase) | orders.supplierEmail, orders.buyerEmail |
277
- | `orders:*.email` | Suffix match (dot) | orders.supplier.email |
278
- | `*:status` | Cross-table exact | *.status |
279
- | `*:*Email` | Cross-table suffix | *.supplierEmail, *.buyerEmail |
280
-
281
- ### Resolution Priority
282
-
283
- 1. `byFieldName["orders:supplier.email"]` — table-specific + exact match (highest)
284
- 2. `byFieldName["orders:*.email"]` — table-specific + suffix match
285
- 3. `byFieldName["*:supplier.email"]` — cross-table + exact match
286
- 4. `byFieldName["*:*.email"]` — cross-table + suffix match
287
- 5. `byFieldType["string"]` — type-based fallback
288
- 6. Default formatting (lowest)
289
-
290
- ### Usage with createDataViewer (Recommended)
291
-
292
- Set renderers at the `createDataViewer` level to apply them across all tables:
293
-
294
- ```tsx
295
- import { createDataViewer, createDefaultFetcher, builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
296
- import { tableMetadata } from "./generated/metadata";
297
-
298
- const { EmailLink, StatusBadge, BooleanIcon, RelativeTime, TruncatedId } = builtInRenderers;
299
-
300
- const fetcher = createDefaultFetcher({
301
- endpoint: "https://your-app.tailor.tech",
302
- });
303
-
304
- export const DataViewer = createDataViewer({
305
- metadata: tableMetadata,
306
- fetcher,
307
- // Renderers are applied across all tables
308
- renderers: {
309
- cell: {
310
- byFieldName: {
311
- "*:*Email": EmailLink,
312
- "*:createdAt": RelativeTime,
313
- "*:id": TruncatedId,
314
- "orders:status": StatusBadge({
315
- colorMap: { pending: "yellow", approved: "green", rejected: "red" },
316
- }),
317
- },
318
- byFieldType: {
319
- boolean: BooleanIcon,
320
- },
321
- },
322
- },
323
- });
324
- ```
325
-
326
- ### Usage with DataTable Props
327
-
328
- You can also pass renderers directly to `DataTable` to override or add renderers for specific pages:
329
-
330
- ```tsx
331
- import { DataTable, builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
332
-
333
- const { EmailLink, StatusBadge, BooleanIcon, RelativeTime, TruncatedId } = builtInRenderers;
334
-
335
- <DataTable
336
- renderers={{
337
- cell: {
338
- byFieldName: {
339
- // Table-specific renderers
340
- "orders:status": StatusBadge({
341
- colorMap: {
342
- "pending": "yellow",
343
- "approved": "green",
344
- "rejected": "red",
345
- },
346
- }),
347
- "invoices:status": StatusBadge({
348
- colorMap: {
349
- "draft": "gray",
350
- "sent": "blue",
351
- "paid": "green",
352
- },
353
- }),
354
-
355
- // Cross-table suffix match (all *Email fields)
356
- "*:*Email": EmailLink,
357
-
358
- // Cross-table exact match
359
- "*:createdAt": RelativeTime,
360
- "*:id": TruncatedId,
361
-
362
- // Custom renderer with click handler
363
- "orders:orderId": ({ value, row }) => (
364
- <button
365
- onClick={() => navigate(`/orders/${value}`)}
366
- className="text-blue-600 hover:underline"
367
- >
368
- {value}
369
- </button>
370
- ),
371
- },
372
- byFieldType: {
373
- boolean: BooleanIcon,
374
- datetime: RelativeTime,
375
- },
376
- },
377
- }}
378
- />
379
- ```
380
-
381
- ## Built-in Renderers
382
-
383
- The library provides several built-in cell renderers for common use cases.
384
-
385
- ```tsx
386
- import { builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
387
-
388
- const { EmailLink, BooleanIcon, RelativeTime, TruncatedId, ArrayBadges, StatusBadge } = builtInRenderers;
389
- ```
390
-
391
- ### EmailLink
392
-
393
- Renders email values as `mailto:` links. Empty/null values display as "-".
394
-
395
- ```tsx
396
- "*:*Email": EmailLink
397
- // supplierEmail → <a href="mailto:supplier@example.com">supplier@example.com</a>
398
- ```
399
-
400
- ### BooleanIcon
401
-
402
- Renders boolean values as icons: ✓ (green) for true, ✗ (gray) for false.
403
-
404
- ```tsx
405
- byFieldType: {
406
- boolean: BooleanIcon
407
- }
408
- ```
409
-
410
- ### RelativeTime
411
-
412
- Renders datetime values as relative time (e.g., "3日前"). Hover shows absolute datetime. Auto-updates every minute.
413
-
414
- ```tsx
415
- "*:createdAt": RelativeTime
416
- // 2024-01-01T10:00:00Z → "3日前" (with tooltip showing "2024/01/01 10:00:00")
417
- ```
418
-
419
- ### TruncatedId
420
-
421
- Renders long IDs (UUIDs) truncated to 8 characters with "...". Hover shows full ID. Click copies to clipboard.
422
-
423
- ```tsx
424
- "*:id": TruncatedId
425
- // "550e8400-e29b-41d4-a716-446655440000" → "550e8400..." (click to copy)
426
- ```
427
-
428
- ### ArrayBadges
429
-
430
- Renders array values as a horizontal list of badges. Shows "+N" when more than 5 items.
431
-
432
- ```tsx
433
- byFieldType: {
434
- array: ArrayBadges
435
- }
436
- // ["tag1", "tag2", "tag3"] → [tag1] [tag2] [tag3]
437
- ```
438
-
439
- ### StatusBadge
440
-
441
- Factory function that creates a colored badge renderer based on value-to-color mapping.
442
-
443
- ```tsx
444
- StatusBadge({
445
- colorMap: {
446
- "pending": "yellow", // Preset color
447
- "approved": "green",
448
- "rejected": "red",
449
- "pending*": "yellow", // Prefix match: pending, pending_review
450
- "*approved": "green", // Suffix match: manager_approved
451
- "*error*": "red", // Contains match: validation_error
452
- },
453
- defaultColor: "gray", // Default for unmatched values
454
- })
455
- ```
456
-
457
- **Preset Colors:**
458
- - `gray`: #F3F4F6 bg, #374151 text
459
- - `red`: #FEE2E2 bg, #991B1B text
460
- - `yellow`: #FEF3C7 bg, #92400E text
461
- - `green`: #D1FAE5 bg, #065F46 text
462
- - `blue`: #DBEAFE bg, #1E40AF text
463
-
464
- **Custom Colors:**
465
- ```tsx
466
- StatusBadge({
467
- colorMap: {
468
- "custom_status": { bg: "#E0F2FE", text: "#0369A1" },
469
- },
470
- })
471
- ```
472
-
473
- **Pattern Priority:**
474
- 1. Exact match (`"approved"`)
475
- 2. Prefix match (`"approved*"`)
476
- 3. Suffix match (`"*approved"`)
477
- 4. Contains match (`"*approved*"`)
478
- 5. `defaultColor`
479
-
package/docs/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Documentation
2
+
3
+ Documentation for `@izumisy-tailor/tailor-data-viewer`.
4
+
5
+ ## Getting Started
6
+
7
+ - [API Reference](API.md) - Core API reference (Factory Function, Components, Hooks, Types)
8
+
9
+ ## Components
10
+
11
+ - [DataTable](data-table.md) - Main data table component with row actions and click handlers
12
+
13
+ ## Configuration
14
+
15
+ - [GraphQL Fetcher](fetcher.md) - GraphQL fetcher configuration and custom implementation
16
+ - [Custom Labels](labels.md) - Customizing field names, table names, and UI text (i18n support)
17
+ - [Custom Renderers](custom-renderers.md) - Custom cell renderers and built-in renderers
18
+
19
+ ## Advanced Usage
20
+
21
+ - [Compositional API](compositional-api.md) - Building flexible UIs by composing components
22
+ - [Saved View Store](saved-view-store.md) - View persistence and restoration
23
+ - [AppShell Module](app-shell-module.md) - AppShell integration module
@@ -28,6 +28,11 @@ const fetcher = createDefaultFetcher({
28
28
  export const DataViewer = createDataViewer({
29
29
  metadata: tableMetadata,
30
30
  fetcher,
31
+ // Optional: Custom labels for fields and UI text
32
+ labels: {
33
+ "Task:status": "ステータス",
34
+ "Task:createdAt": "作成日時",
35
+ },
31
36
  });
32
37
  ```
33
38
 
@@ -86,6 +91,8 @@ const fetcher = createDefaultFetcher({
86
91
  const DataViewer = createDataViewer({
87
92
  metadata: tableMetadata, // Generated from TailorDB schema
88
93
  fetcher,
94
+ labels: { ... }, // Optional: Custom labels
95
+ renderers: { ... }, // Optional: Custom cell renderers
89
96
  });
90
97
 
91
98
  // Returns:
@@ -93,6 +100,7 @@ const DataViewer = createDataViewer({
93
100
  // - DataViewer.ToolbarProvider - Toolbar state provider
94
101
  // - DataViewer.metadata - The metadata passed to createDataViewer
95
102
  // - DataViewer.fetcher - The fetcher passed to createDataViewer
103
+ // - DataViewer.labels - The labels passed to createDataViewer
96
104
  ```
97
105
 
98
106
  ### DataViewer.TableDataProvider
@@ -254,6 +262,26 @@ const {
254
262
  } = useDataViewer();
255
263
  ```
256
264
 
265
+ ### useLabels
266
+
267
+ Access label resolution for fields and UI text.
268
+
269
+ ```tsx
270
+ const { getLabel, labels } = useLabels();
271
+
272
+ // Field label (with wildcard fallback)
273
+ const statusLabel = getLabel("orders:status");
274
+ // → labels["orders:status"] → labels["*:status"] → "status"
275
+
276
+ // Table label
277
+ const tableLabel = getLabel("orders");
278
+ // → labels["orders"] → "orders"
279
+
280
+ // UI label (with DEFAULT_UI_LABELS fallback)
281
+ const refreshLabel = getLabel("$:refresh");
282
+ // → labels["$:refresh"] → DEFAULT_UI_LABELS["$:refresh"] → "$:refresh"
283
+ ```
284
+
257
285
  ### useTableDataContext
258
286
 
259
287
  Access data fetching state and pagination controls.
@@ -0,0 +1,230 @@
1
+ # Custom Cell Renderers
2
+
3
+ ## Overview
4
+
5
+ Custom cell renderers allow you to customize how cells are displayed in the DataTable. You can define renderers by field name (with optional wildcard matching) or by field type.
6
+
7
+ ## Types
8
+
9
+ ```tsx
10
+ interface CellRendererProps {
11
+ value: unknown;
12
+ field: FieldMetadata;
13
+ row: Record<string, unknown>;
14
+ rowIndex: number;
15
+ tableName: string;
16
+ tableMetadata: TableMetadata;
17
+ }
18
+
19
+ type CellRenderer = (props: CellRendererProps) => ReactNode;
20
+
21
+ interface CellRenderers {
22
+ /** Renderers by "tableName:fieldName" pattern */
23
+ byFieldName?: Record<string, CellRenderer>;
24
+ /** Renderers by field type (fallback) */
25
+ byFieldType?: Partial<Record<FieldType, CellRenderer>>;
26
+ }
27
+
28
+ interface Renderers {
29
+ cell?: CellRenderers;
30
+ }
31
+ ```
32
+
33
+ ## Key Format
34
+
35
+ The `byFieldName` object uses a `tableName:fieldName` key format:
36
+
37
+ | Key | Description | Matches |
38
+ |-----|-------------|---------|
39
+ | `orders:status` | Exact match | orders.status |
40
+ | `orders:*Email` | Suffix match (camelCase) | orders.supplierEmail, orders.buyerEmail |
41
+ | `orders:*.email` | Suffix match (dot) | orders.supplier.email |
42
+ | `*:status` | Cross-table exact | *.status |
43
+ | `*:*Email` | Cross-table suffix | *.supplierEmail, *.buyerEmail |
44
+
45
+ ## Resolution Priority
46
+
47
+ 1. `byFieldName["orders:supplier.email"]` — table-specific + exact match (highest)
48
+ 2. `byFieldName["orders:*.email"]` — table-specific + suffix match
49
+ 3. `byFieldName["*:supplier.email"]` — cross-table + exact match
50
+ 4. `byFieldName["*:*.email"]` — cross-table + suffix match
51
+ 5. `byFieldType["string"]` — type-based fallback
52
+ 6. Default formatting (lowest)
53
+
54
+ ## Usage with createDataViewer (Recommended)
55
+
56
+ Set renderers at the `createDataViewer` level to apply them across all tables:
57
+
58
+ ```tsx
59
+ import { createDataViewer, createDefaultFetcher, builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
60
+ import { tableMetadata } from "./generated/metadata";
61
+
62
+ const { EmailLink, StatusBadge, BooleanIcon, TruncatedId } = builtInRenderers;
63
+
64
+ const fetcher = createDefaultFetcher({
65
+ endpoint: "https://your-app.tailor.tech",
66
+ });
67
+
68
+ export const DataViewer = createDataViewer({
69
+ metadata: tableMetadata,
70
+ fetcher,
71
+ // Renderers are applied across all tables
72
+ renderers: {
73
+ cell: {
74
+ byFieldName: {
75
+ "*:*Email": EmailLink,
76
+ "*:id": TruncatedId,
77
+ "orders:status": StatusBadge({
78
+ colorMap: { pending: "yellow", approved: "green", rejected: "red" },
79
+ }),
80
+ },
81
+ byFieldType: {
82
+ boolean: BooleanIcon,
83
+ },
84
+ },
85
+ },
86
+ });
87
+ ```
88
+
89
+ ## Usage with DataTable Props
90
+
91
+ You can also pass renderers directly to `DataTable` to override or add renderers for specific pages:
92
+
93
+ ```tsx
94
+ import { DataTable, builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
95
+
96
+ const { EmailLink, StatusBadge, BooleanIcon, TruncatedId } = builtInRenderers;
97
+
98
+ <DataTable
99
+ renderers={{
100
+ cell: {
101
+ byFieldName: {
102
+ // Table-specific renderers
103
+ "orders:status": StatusBadge({
104
+ colorMap: {
105
+ "pending": "yellow",
106
+ "approved": "green",
107
+ "rejected": "red",
108
+ },
109
+ }),
110
+ "invoices:status": StatusBadge({
111
+ colorMap: {
112
+ "draft": "gray",
113
+ "sent": "blue",
114
+ "paid": "green",
115
+ },
116
+ }),
117
+
118
+ // Cross-table suffix match (all *Email fields)
119
+ "*:*Email": EmailLink,
120
+
121
+ // Cross-table exact match
122
+ "*:id": TruncatedId,
123
+
124
+ // Custom renderer with click handler
125
+ "orders:orderId": ({ value, row }) => (
126
+ <button
127
+ onClick={() => navigate(`/orders/${value}`)}
128
+ className="text-blue-600 hover:underline"
129
+ >
130
+ {value}
131
+ </button>
132
+ ),
133
+ },
134
+ byFieldType: {
135
+ boolean: BooleanIcon,
136
+ },
137
+ },
138
+ }}
139
+ />
140
+ ```
141
+
142
+ ## Built-in Renderers
143
+
144
+ The library provides several built-in cell renderers for common use cases.
145
+
146
+ ```tsx
147
+ import { builtInRenderers } from "@izumisy-tailor/tailor-data-viewer/component";
148
+
149
+ const { EmailLink, BooleanIcon, TruncatedId, ArrayBadges, StatusBadge } = builtInRenderers;
150
+ ```
151
+
152
+ ### EmailLink
153
+
154
+ Renders email values as `mailto:` links. Empty/null values display as "-".
155
+
156
+ ```tsx
157
+ "*:*Email": EmailLink
158
+ // supplierEmail → <a href="mailto:supplier@example.com">supplier@example.com</a>
159
+ ```
160
+
161
+ ### BooleanIcon
162
+
163
+ Renders boolean values as icons: ✓ (green) for true, ✗ (gray) for false.
164
+
165
+ ```tsx
166
+ byFieldType: {
167
+ boolean: BooleanIcon
168
+ }
169
+ ```
170
+
171
+ ### TruncatedId
172
+
173
+ Renders long IDs (UUIDs) truncated to 8 characters with "...". Hover shows full ID. Click copies to clipboard.
174
+
175
+ ```tsx
176
+ "*:id": TruncatedId
177
+ // "550e8400-e29b-41d4-a716-446655440000" → "550e8400..." (click to copy)
178
+ ```
179
+
180
+ ### ArrayBadges
181
+
182
+ Renders array values as a horizontal list of badges. Shows "+N" when more than 5 items.
183
+
184
+ ```tsx
185
+ byFieldType: {
186
+ array: ArrayBadges
187
+ }
188
+ // ["tag1", "tag2", "tag3"] → [tag1] [tag2] [tag3]
189
+ ```
190
+
191
+ ### StatusBadge
192
+
193
+ Factory function that creates a colored badge renderer based on value-to-color mapping.
194
+
195
+ ```tsx
196
+ StatusBadge({
197
+ colorMap: {
198
+ "pending": "yellow", // Preset color
199
+ "approved": "green",
200
+ "rejected": "red",
201
+ "pending*": "yellow", // Prefix match: pending, pending_review
202
+ "*approved": "green", // Suffix match: manager_approved
203
+ "*error*": "red", // Contains match: validation_error
204
+ },
205
+ defaultColor: "gray", // Default for unmatched values
206
+ })
207
+ ```
208
+
209
+ **Preset Colors:**
210
+ - `gray`: #F3F4F6 bg, #374151 text
211
+ - `red`: #FEE2E2 bg, #991B1B text
212
+ - `yellow`: #FEF3C7 bg, #92400E text
213
+ - `green`: #D1FAE5 bg, #065F46 text
214
+ - `blue`: #DBEAFE bg, #1E40AF text
215
+
216
+ **Custom Colors:**
217
+ ```tsx
218
+ StatusBadge({
219
+ colorMap: {
220
+ "custom_status": { bg: "#E0F2FE", text: "#0369A1" },
221
+ },
222
+ })
223
+ ```
224
+
225
+ **Pattern Priority:**
226
+ 1. Exact match (`"approved"`)
227
+ 2. Prefix match (`"approved*"`)
228
+ 3. Suffix match (`"*approved"`)
229
+ 4. Contains match (`"*approved*"`)
230
+ 5. `defaultColor`