@izumisy-tailor/tailor-data-viewer 0.2.5 → 0.2.7
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 +1 -1
- package/src/component/collection-params/use-collection-params.test.ts +0 -79
- package/src/component/collection-params/use-collection-params.ts +42 -64
- package/src/component/index.ts +2 -0
- package/src/component/search-filter-form.tsx +3 -1
- package/src/component/types.ts +106 -26
package/package.json
CHANGED
|
@@ -44,7 +44,6 @@ describe("useCollectionParams", () => {
|
|
|
44
44
|
initialFilters: [
|
|
45
45
|
{
|
|
46
46
|
field: "status",
|
|
47
|
-
fieldType: "enum",
|
|
48
47
|
operator: "eq",
|
|
49
48
|
value: "ACTIVE",
|
|
50
49
|
},
|
|
@@ -101,13 +100,11 @@ describe("useCollectionParams", () => {
|
|
|
101
100
|
result.current.setFilters([
|
|
102
101
|
{
|
|
103
102
|
field: "status",
|
|
104
|
-
fieldType: "enum",
|
|
105
103
|
operator: "eq",
|
|
106
104
|
value: "ACTIVE",
|
|
107
105
|
},
|
|
108
106
|
{
|
|
109
107
|
field: "amount",
|
|
110
|
-
fieldType: "number",
|
|
111
108
|
operator: "gte",
|
|
112
109
|
value: 1000,
|
|
113
110
|
},
|
|
@@ -308,7 +305,6 @@ describe("useCollectionParams", () => {
|
|
|
308
305
|
initialFilters: [
|
|
309
306
|
{
|
|
310
307
|
field: "status",
|
|
311
|
-
fieldType: "enum",
|
|
312
308
|
operator: "eq",
|
|
313
309
|
value: "ACTIVE",
|
|
314
310
|
},
|
|
@@ -374,81 +370,6 @@ describe("useCollectionParams", () => {
|
|
|
374
370
|
expect(result.current.variables).toEqual({ first: 10 });
|
|
375
371
|
});
|
|
376
372
|
|
|
377
|
-
it("auto-detects fieldType from metadata for string field", () => {
|
|
378
|
-
const { result } = renderHook(() =>
|
|
379
|
-
useCollectionParams({
|
|
380
|
-
metadata: testMetadata,
|
|
381
|
-
tableName: "task",
|
|
382
|
-
}),
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
act(() => {
|
|
386
|
-
result.current.addFilter("title", "foo");
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
expect(result.current.filters[0].fieldType).toBe("string");
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it("auto-detects fieldType from metadata for enum field", () => {
|
|
393
|
-
const { result } = renderHook(() =>
|
|
394
|
-
useCollectionParams({
|
|
395
|
-
metadata: testMetadata,
|
|
396
|
-
tableName: "task",
|
|
397
|
-
}),
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
act(() => {
|
|
401
|
-
result.current.addFilter("status", "todo");
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
expect(result.current.filters[0].fieldType).toBe("enum");
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
it("auto-detects fieldType from metadata for date field", () => {
|
|
408
|
-
const { result } = renderHook(() =>
|
|
409
|
-
useCollectionParams({
|
|
410
|
-
metadata: testMetadata,
|
|
411
|
-
tableName: "task",
|
|
412
|
-
}),
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
act(() => {
|
|
416
|
-
result.current.addFilter("dueDate", "2026-01-01");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
expect(result.current.filters[0].fieldType).toBe("date");
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
it("auto-detects fieldType from metadata for uuid field", () => {
|
|
423
|
-
const { result } = renderHook(() =>
|
|
424
|
-
useCollectionParams({
|
|
425
|
-
metadata: testMetadata,
|
|
426
|
-
tableName: "task",
|
|
427
|
-
}),
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
act(() => {
|
|
431
|
-
result.current.addFilter("id", "some-uuid");
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
expect(result.current.filters[0].fieldType).toBe("uuid");
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
it("auto-detects fieldType from metadata for number field", () => {
|
|
438
|
-
const { result } = renderHook(() =>
|
|
439
|
-
useCollectionParams({
|
|
440
|
-
metadata: testMetadata,
|
|
441
|
-
tableName: "task",
|
|
442
|
-
}),
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
act(() => {
|
|
446
|
-
result.current.addFilter("count", 42);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
expect(result.current.filters[0].fieldType).toBe("number");
|
|
450
|
-
});
|
|
451
|
-
|
|
452
373
|
it("applies typed initialSort", () => {
|
|
453
374
|
const { result } = renderHook(() =>
|
|
454
375
|
useCollectionParams({
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import { useCallback, useMemo,
|
|
2
|
-
import type {
|
|
3
|
-
FieldType,
|
|
4
|
-
TableMetadataMap,
|
|
5
|
-
} from "../../generator/metadata-generator";
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import type { TableMetadataMap } from "../../generator/metadata-generator";
|
|
6
3
|
import type {
|
|
7
4
|
Filter,
|
|
8
5
|
FilterOperator,
|
|
6
|
+
MetadataFilter,
|
|
9
7
|
QueryVariables,
|
|
10
8
|
SortState,
|
|
11
9
|
UseCollectionParamsOptions,
|
|
12
10
|
UseCollectionParamsReturn,
|
|
13
|
-
|
|
14
|
-
MatchingTableName,
|
|
11
|
+
ExtractQueryVariables,
|
|
15
12
|
} from "../types";
|
|
16
|
-
import { fieldTypeToFilterConfig } from "../types";
|
|
17
13
|
import type { FieldName } from "../types";
|
|
18
14
|
|
|
19
15
|
// -----------------------------------------------------------------------------
|
|
@@ -24,29 +20,49 @@ import type { FieldName } from "../types";
|
|
|
24
20
|
* Hook for managing collection query parameters (filters, sort, pagination)
|
|
25
21
|
* with metadata-based field name typing and automatic `fieldType` detection.
|
|
26
22
|
*
|
|
23
|
+
* When `query` is provided, the output `variables` is typed to match
|
|
24
|
+
* the GraphQL query's expected variables (e.g. `VariablesOf<typeof QUERY>`).
|
|
25
|
+
* The `query` value is only used for type inference and ignored at runtime.
|
|
26
|
+
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```tsx
|
|
29
29
|
* import { tableMetadata } from "./generated/data-viewer-metadata.generated";
|
|
30
30
|
*
|
|
31
|
+
* // Without query — variables typed as QueryVariables<FieldName>
|
|
31
32
|
* const params = useCollectionParams({
|
|
32
33
|
* metadata: tableMetadata,
|
|
33
34
|
* tableName: "task",
|
|
34
35
|
* pageSize: 20,
|
|
35
36
|
* });
|
|
36
37
|
*
|
|
37
|
-
*
|
|
38
|
-
* params
|
|
38
|
+
* // With query — variables typed as VariablesOf<typeof GET_TASKS>
|
|
39
|
+
* const params = useCollectionParams({
|
|
40
|
+
* metadata: tableMetadata,
|
|
41
|
+
* tableName: "task",
|
|
42
|
+
* query: GET_TASKS,
|
|
43
|
+
* pageSize: 20,
|
|
44
|
+
* });
|
|
39
45
|
* ```
|
|
40
46
|
*/
|
|
41
47
|
export function useCollectionParams<
|
|
42
48
|
const TMetadata extends TableMetadataMap,
|
|
43
49
|
TTableName extends string & keyof TMetadata,
|
|
50
|
+
TQuery = never,
|
|
44
51
|
>(
|
|
45
|
-
options: UseCollectionParamsOptions<
|
|
52
|
+
options: UseCollectionParamsOptions<
|
|
53
|
+
FieldName<TMetadata, TTableName>,
|
|
54
|
+
MetadataFilter<TMetadata, TTableName>
|
|
55
|
+
> & {
|
|
46
56
|
metadata: TMetadata;
|
|
47
57
|
tableName: TTableName;
|
|
58
|
+
query?: TQuery;
|
|
48
59
|
},
|
|
49
|
-
): UseCollectionParamsReturn<
|
|
60
|
+
): UseCollectionParamsReturn<
|
|
61
|
+
FieldName<TMetadata, TTableName>,
|
|
62
|
+
[TQuery] extends [never]
|
|
63
|
+
? QueryVariables<FieldName<TMetadata, TTableName>>
|
|
64
|
+
: ExtractQueryVariables<TQuery>
|
|
65
|
+
>;
|
|
50
66
|
|
|
51
67
|
/**
|
|
52
68
|
* Hook for managing collection query parameters (filters, sort, pagination).
|
|
@@ -54,50 +70,28 @@ export function useCollectionParams<
|
|
|
54
70
|
* Produces `variables` in Tailor Platform format that can be passed directly
|
|
55
71
|
* to a GraphQL query (e.g. urql's `useQuery`).
|
|
56
72
|
*
|
|
73
|
+
* When `query` is provided, the output `variables` is typed to match
|
|
74
|
+
* the GraphQL query's expected variables.
|
|
75
|
+
*
|
|
57
76
|
* @example
|
|
58
77
|
* ```tsx
|
|
59
78
|
* const params = useCollectionParams({ pageSize: 20 });
|
|
60
79
|
* const [result] = useQuery({ query: GET_ORDERS, variables: params.variables });
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
export function useCollectionParams(
|
|
64
|
-
options?: UseCollectionParamsOptions,
|
|
65
|
-
): UseCollectionParamsReturn;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Hook for managing collection query parameters (filters, sort, pagination)
|
|
69
|
-
* with an explicit `TVariables` type for the output `variables`.
|
|
70
80
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* When `metadata` is provided, `tableName` is constrained to tables
|
|
74
|
-
* whose fields are a superset of the extracted field names.
|
|
75
|
-
*
|
|
76
|
-
* @typeParam TVariables - The exact type for the output `variables` property.
|
|
77
|
-
* Typically `VariablesOf<typeof YOUR_QUERY>` from gql-tada.
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```tsx
|
|
81
|
-
* import type { VariablesOf } from "gql.tada";
|
|
82
|
-
*
|
|
83
|
-
* const params = useCollectionParams<VariablesOf<typeof GET_ORDERS>>({
|
|
84
|
-
* metadata: tableMetadata,
|
|
85
|
-
* tableName: "order", // ← constrained to tables matching order[].field
|
|
86
|
-
* pageSize: 20,
|
|
87
|
-
* });
|
|
88
|
-
*
|
|
89
|
-
* params.addFilter("status", "ACTIVE"); // ← field name auto-completed
|
|
90
|
-
* const [result] = useQuery({ query: GET_ORDERS, variables: params.variables });
|
|
81
|
+
* // With query — variables auto-typed
|
|
82
|
+
* const params = useCollectionParams({ query: GET_ORDERS, pageSize: 20 });
|
|
91
83
|
* ```
|
|
92
84
|
*/
|
|
93
|
-
export function useCollectionParams<
|
|
94
|
-
options?: UseCollectionParamsOptions
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
: MatchingTableName<TableMetadataMap, ExtractOrderField<TVariables>>;
|
|
85
|
+
export function useCollectionParams<TQuery = never>(
|
|
86
|
+
options?: UseCollectionParamsOptions & {
|
|
87
|
+
query?: TQuery;
|
|
88
|
+
metadata?: never;
|
|
89
|
+
tableName?: never;
|
|
99
90
|
},
|
|
100
|
-
): UseCollectionParamsReturn<
|
|
91
|
+
): UseCollectionParamsReturn<
|
|
92
|
+
string,
|
|
93
|
+
[TQuery] extends [never] ? QueryVariables : ExtractQueryVariables<TQuery>
|
|
94
|
+
>;
|
|
101
95
|
|
|
102
96
|
// -----------------------------------------------------------------------------
|
|
103
97
|
// Implementation
|
|
@@ -112,15 +106,8 @@ export function useCollectionParams(
|
|
|
112
106
|
initialFilters = [],
|
|
113
107
|
initialSort = [],
|
|
114
108
|
pageSize: initialPageSize = 20,
|
|
115
|
-
metadata,
|
|
116
|
-
tableName,
|
|
117
109
|
} = options;
|
|
118
110
|
|
|
119
|
-
// Keep a ref to the resolved fields list so callbacks can look up fieldType.
|
|
120
|
-
const fieldsRef = useRef(
|
|
121
|
-
metadata && tableName ? metadata[tableName]?.fields : undefined,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
111
|
// ---------------------------------------------------------------------------
|
|
125
112
|
// State
|
|
126
113
|
// ---------------------------------------------------------------------------
|
|
@@ -137,17 +124,8 @@ export function useCollectionParams(
|
|
|
137
124
|
(field: string, value: unknown, operator: FilterOperator = "eq") => {
|
|
138
125
|
setFiltersState((prev) => {
|
|
139
126
|
const existing = prev.findIndex((f) => f.field === field);
|
|
140
|
-
// Auto-detect fieldType from metadata when available
|
|
141
|
-
const fieldMeta = fieldsRef.current?.find((f) => f.name === field);
|
|
142
|
-
const detectedType: Filter["fieldType"] = fieldMeta
|
|
143
|
-
? (fieldTypeToFilterConfig(
|
|
144
|
-
fieldMeta.type as FieldType,
|
|
145
|
-
fieldMeta.enumValues as readonly string[] | undefined,
|
|
146
|
-
)?.type ?? "string")
|
|
147
|
-
: "string";
|
|
148
127
|
const newFilter: Filter = {
|
|
149
128
|
field,
|
|
150
|
-
fieldType: detectedType,
|
|
151
129
|
operator,
|
|
152
130
|
value,
|
|
153
131
|
};
|
package/src/component/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type {
|
|
|
5
5
|
SelectOption,
|
|
6
6
|
FilterOperator,
|
|
7
7
|
Filter,
|
|
8
|
+
MetadataFilter,
|
|
8
9
|
SortState,
|
|
9
10
|
PageInfo,
|
|
10
11
|
QueryVariables,
|
|
@@ -28,6 +29,7 @@ export type {
|
|
|
28
29
|
DataTableCellProps,
|
|
29
30
|
FieldName,
|
|
30
31
|
ExtractOrderField,
|
|
32
|
+
ExtractQueryVariables,
|
|
31
33
|
MatchingTableName,
|
|
32
34
|
MetadataFieldOptions,
|
|
33
35
|
MetadataFieldsOptions,
|
|
@@ -116,7 +116,9 @@ export function SearchFilterForm<TRow extends Record<string, unknown>>({
|
|
|
116
116
|
// Reset operator when field changes
|
|
117
117
|
useEffect(() => {
|
|
118
118
|
if (selectedFilterConfig) {
|
|
119
|
-
const ops = OPERATORS_BY_FILTER_TYPE[
|
|
119
|
+
const ops = OPERATORS_BY_FILTER_TYPE[
|
|
120
|
+
selectedFilterConfig.type
|
|
121
|
+
] as readonly FilterOperator[];
|
|
120
122
|
if (ops.length > 0 && !ops.includes(selectedOperator)) {
|
|
121
123
|
setSelectedOperator(ops[0]);
|
|
122
124
|
}
|
package/src/component/types.ts
CHANGED
|
@@ -39,39 +39,60 @@ export type FilterConfig =
|
|
|
39
39
|
| { type: "uuid" };
|
|
40
40
|
|
|
41
41
|
// =============================================================================
|
|
42
|
-
// Filter Operators
|
|
42
|
+
// Filter Operators (Single Source of Truth)
|
|
43
43
|
// =============================================================================
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
* Available filter operators (Tailor Platform compliant).
|
|
47
|
-
*/
|
|
48
|
-
export type FilterOperator =
|
|
49
|
-
| "eq"
|
|
50
|
-
| "ne"
|
|
51
|
-
| "contains"
|
|
52
|
-
| "startsWith"
|
|
53
|
-
| "endsWith"
|
|
54
|
-
| "gt"
|
|
55
|
-
| "gte"
|
|
56
|
-
| "lt"
|
|
57
|
-
| "lte"
|
|
58
|
-
| "between"
|
|
59
|
-
| "in"
|
|
60
|
-
| "notIn";
|
|
61
|
-
|
|
62
45
|
/**
|
|
63
46
|
* Operators available per filter type (Tailor Platform schema).
|
|
47
|
+
*
|
|
48
|
+
* This `as const satisfies` definition is the **single source of truth**.
|
|
49
|
+
* Both the `FilterOperator` union and the `OperatorForFilterType` mapping
|
|
50
|
+
* are derived from it, eliminating duplication.
|
|
64
51
|
*/
|
|
65
|
-
export const OPERATORS_BY_FILTER_TYPE
|
|
66
|
-
FilterConfig["type"],
|
|
67
|
-
FilterOperator[]
|
|
68
|
-
> = {
|
|
52
|
+
export const OPERATORS_BY_FILTER_TYPE = {
|
|
69
53
|
string: ["eq", "ne", "contains", "startsWith", "endsWith"],
|
|
70
54
|
number: ["eq", "ne", "gt", "gte", "lt", "lte", "between"],
|
|
71
55
|
date: ["eq", "ne", "gt", "gte", "lt", "lte", "between"],
|
|
72
56
|
enum: ["eq", "ne", "in", "notIn"],
|
|
73
57
|
boolean: ["eq", "ne"],
|
|
74
58
|
uuid: ["eq", "ne", "in", "notIn"],
|
|
59
|
+
} as const satisfies Record<FilterConfig["type"], readonly string[]>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Maps each filter type to the union of operators it supports.
|
|
63
|
+
* Derived from `OPERATORS_BY_FILTER_TYPE`.
|
|
64
|
+
*/
|
|
65
|
+
export type OperatorForFilterType = {
|
|
66
|
+
[T in FilterConfig["type"]]: (typeof OPERATORS_BY_FILTER_TYPE)[T][number];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Union of all available filter operators.
|
|
71
|
+
* Derived from `OPERATORS_BY_FILTER_TYPE`.
|
|
72
|
+
*/
|
|
73
|
+
export type FilterOperator = OperatorForFilterType[FilterConfig["type"]];
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Filter Type → FieldType Mapping (type-level)
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Type-level mapping from `FieldType` (metadata) to `FilterConfig["type"]`.
|
|
81
|
+
* Mirrors the runtime `fieldTypeToFilterConfig` function.
|
|
82
|
+
* Types that don't support filtering map to `never`.
|
|
83
|
+
*/
|
|
84
|
+
export type FieldTypeToFilterConfigType = {
|
|
85
|
+
string: "string";
|
|
86
|
+
number: "number";
|
|
87
|
+
boolean: "boolean";
|
|
88
|
+
uuid: "uuid";
|
|
89
|
+
datetime: "date";
|
|
90
|
+
date: "date";
|
|
91
|
+
time: "date";
|
|
92
|
+
enum: "enum";
|
|
93
|
+
array: never;
|
|
94
|
+
nested: never;
|
|
95
|
+
file: never;
|
|
75
96
|
};
|
|
76
97
|
|
|
77
98
|
// =============================================================================
|
|
@@ -80,14 +101,53 @@ export const OPERATORS_BY_FILTER_TYPE: Record<
|
|
|
80
101
|
|
|
81
102
|
/**
|
|
82
103
|
* Active filter state.
|
|
104
|
+
*
|
|
105
|
+
* @typeParam TFieldName - Union of allowed field name strings (default: `string`).
|
|
83
106
|
*/
|
|
84
|
-
export interface Filter {
|
|
85
|
-
field:
|
|
86
|
-
fieldType: FilterConfig["type"];
|
|
107
|
+
export interface Filter<TFieldName extends string = string> {
|
|
108
|
+
field: TFieldName;
|
|
87
109
|
operator: FilterOperator;
|
|
88
110
|
value: unknown;
|
|
89
111
|
}
|
|
90
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Metadata-aware filter type.
|
|
115
|
+
*
|
|
116
|
+
* Given table metadata, produces a discriminated union over each field
|
|
117
|
+
* where the `operator` is narrowed to only those valid for that field's type.
|
|
118
|
+
* Fields whose `FieldType` doesn't support filtering (array, nested, file) are excluded.
|
|
119
|
+
*
|
|
120
|
+
* @typeParam TMetadata - The `tableMetadata` object type (as const).
|
|
121
|
+
* @typeParam TTableName - The table name key in `TMetadata`.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* // If table "task" has fields: name (string), priority (number), status (enum)
|
|
126
|
+
* type F = MetadataFilter<typeof tableMetadata, "task">;
|
|
127
|
+
* // | { field: "name"; operator: "eq" | "ne" | "contains" | ...; value: unknown }
|
|
128
|
+
* // | { field: "priority"; operator: "eq" | "ne" | "gt" | ...; value: unknown }
|
|
129
|
+
* // | { field: "status"; operator: "eq" | "ne" | "in" | "notIn"; value: unknown }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export type MetadataFilter<
|
|
133
|
+
TMetadata extends TableMetadataMap,
|
|
134
|
+
TTableName extends string & keyof TMetadata,
|
|
135
|
+
> = TMetadata[TTableName]["fields"][number] extends infer F
|
|
136
|
+
? F extends { readonly name: infer N; readonly type: infer T }
|
|
137
|
+
? N extends string
|
|
138
|
+
? T extends keyof FieldTypeToFilterConfigType
|
|
139
|
+
? FieldTypeToFilterConfigType[T] extends never
|
|
140
|
+
? never
|
|
141
|
+
: {
|
|
142
|
+
field: N;
|
|
143
|
+
operator: OperatorForFilterType[FieldTypeToFilterConfigType[T]];
|
|
144
|
+
value: unknown;
|
|
145
|
+
}
|
|
146
|
+
: never
|
|
147
|
+
: never
|
|
148
|
+
: never
|
|
149
|
+
: never;
|
|
150
|
+
|
|
91
151
|
/**
|
|
92
152
|
* Active sort state for a single field.
|
|
93
153
|
*/
|
|
@@ -270,9 +330,10 @@ export type ColumnDefinition<TRow extends Record<string, unknown>> =
|
|
|
270
330
|
*/
|
|
271
331
|
export interface UseCollectionParamsOptions<
|
|
272
332
|
TFieldName extends string = string,
|
|
333
|
+
TFilter extends Filter<TFieldName> = Filter<TFieldName>,
|
|
273
334
|
> {
|
|
274
335
|
/** Initial filters to apply */
|
|
275
|
-
initialFilters?:
|
|
336
|
+
initialFilters?: TFilter[];
|
|
276
337
|
/** Initial sort states */
|
|
277
338
|
initialSort?: { field: TFieldName; direction: "Asc" | "Desc" }[];
|
|
278
339
|
/** Number of items per page (default: 20) */
|
|
@@ -506,6 +567,25 @@ export type ExtractOrderField<T> = T extends {
|
|
|
506
567
|
: string
|
|
507
568
|
: string;
|
|
508
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Extract the variables type from a GraphQL query object.
|
|
572
|
+
*
|
|
573
|
+
* Works with `TypedDocumentNode` (used by gql-tada, graphql-codegen, etc.)
|
|
574
|
+
* which stores the variables type in the `__variablesType` brand property.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```ts
|
|
578
|
+
* const GET_ORDERS = graphql(`query Orders($first: Int, ...) { ... }`);
|
|
579
|
+
* type Vars = ExtractQueryVariables<typeof GET_ORDERS>;
|
|
580
|
+
* // → { first?: number | null; order?: OrderInput[] | null; ... }
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
export type ExtractQueryVariables<T> = T extends {
|
|
584
|
+
__variablesType?: infer V;
|
|
585
|
+
}
|
|
586
|
+
? NonNullable<V>
|
|
587
|
+
: never;
|
|
588
|
+
|
|
509
589
|
/**
|
|
510
590
|
* Find table names in metadata whose fields are a superset of `TFieldName`.
|
|
511
591
|
*
|