@izumisy-tailor/tailor-data-viewer 0.2.14 → 0.2.16
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 +2 -2
- package/package.json +1 -1
- package/src/component/collection/use-collection.test.ts +46 -25
- package/src/component/collection/use-collection.ts +62 -53
- package/src/component/collection/use-collection.typetest.ts +140 -21
- package/src/component/data-table/use-data-table.test.ts +8 -1
- package/src/component/data-table/use-data-table.ts +23 -4
- package/src/component/field-helpers.test.ts +19 -45
- package/src/component/field-helpers.ts +13 -22
- package/src/component/index.ts +4 -1
- package/src/component/pagination.tsx +7 -2
- package/src/component/types.ts +168 -43
package/src/component/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type {
|
|
3
3
|
FieldType,
|
|
4
|
+
TableMetadata,
|
|
4
5
|
TableMetadataMap,
|
|
5
6
|
} from "../generator/metadata-generator";
|
|
6
7
|
|
|
@@ -144,24 +145,40 @@ export interface Filter<TFieldName extends string = string> {
|
|
|
144
145
|
* // | { field: "status"; operator: "eq" | "ne" | "in" | "notIn"; value: unknown }
|
|
145
146
|
* ```
|
|
146
147
|
*/
|
|
148
|
+
/**
|
|
149
|
+
* Metadata-aware filter type from a single table metadata object.
|
|
150
|
+
*
|
|
151
|
+
* Given table metadata, produces a discriminated union over each field
|
|
152
|
+
* where the `operator` is narrowed to only those valid for that field's type.
|
|
153
|
+
*
|
|
154
|
+
* @typeParam TTable - A single table metadata object (as const).
|
|
155
|
+
*/
|
|
156
|
+
export type TableMetadataFilter<TTable extends TableMetadata> =
|
|
157
|
+
TTable["fields"][number] extends infer F
|
|
158
|
+
? F extends { readonly name: infer N; readonly type: infer T }
|
|
159
|
+
? N extends string
|
|
160
|
+
? T extends keyof FieldTypeToFilterConfigType
|
|
161
|
+
? FieldTypeToFilterConfigType[T] extends never
|
|
162
|
+
? never
|
|
163
|
+
: {
|
|
164
|
+
field: N;
|
|
165
|
+
operator: OperatorForFilterType[FieldTypeToFilterConfigType[T]];
|
|
166
|
+
value: unknown;
|
|
167
|
+
}
|
|
168
|
+
: never
|
|
169
|
+
: never
|
|
170
|
+
: never
|
|
171
|
+
: never;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Metadata-aware filter type.
|
|
175
|
+
*
|
|
176
|
+
* @deprecated Use `TableMetadataFilter<TMetadata[TTableName]>` instead.
|
|
177
|
+
*/
|
|
147
178
|
export type MetadataFilter<
|
|
148
179
|
TMetadata extends TableMetadataMap,
|
|
149
180
|
TTableName extends string & keyof TMetadata,
|
|
150
|
-
> = TMetadata[TTableName]
|
|
151
|
-
? F extends { readonly name: infer N; readonly type: infer T }
|
|
152
|
-
? N extends string
|
|
153
|
-
? T extends keyof FieldTypeToFilterConfigType
|
|
154
|
-
? FieldTypeToFilterConfigType[T] extends never
|
|
155
|
-
? never
|
|
156
|
-
: {
|
|
157
|
-
field: N;
|
|
158
|
-
operator: OperatorForFilterType[FieldTypeToFilterConfigType[T]];
|
|
159
|
-
value: unknown;
|
|
160
|
-
}
|
|
161
|
-
: never
|
|
162
|
-
: never
|
|
163
|
-
: never
|
|
164
|
-
: never;
|
|
181
|
+
> = TableMetadataFilter<TMetadata[TTableName]>;
|
|
165
182
|
|
|
166
183
|
/**
|
|
167
184
|
* Active sort state for a single field.
|
|
@@ -177,6 +194,8 @@ export interface SortState {
|
|
|
177
194
|
export interface PageInfo {
|
|
178
195
|
hasNextPage: boolean;
|
|
179
196
|
endCursor: string | null;
|
|
197
|
+
hasPreviousPage: boolean;
|
|
198
|
+
startCursor: string | null;
|
|
180
199
|
}
|
|
181
200
|
|
|
182
201
|
// =============================================================================
|
|
@@ -211,8 +230,14 @@ export interface QueryVariables {
|
|
|
211
230
|
* performed by `ValidateCollectionQuery` (specifically `CheckOrderField`).
|
|
212
231
|
*/
|
|
213
232
|
order?: { field: string; direction: "Asc" | "Desc" }[];
|
|
214
|
-
|
|
233
|
+
/** Forward pagination: number of items to fetch */
|
|
234
|
+
first?: number | null;
|
|
235
|
+
/** Forward pagination: cursor to start after */
|
|
215
236
|
after?: string | null;
|
|
237
|
+
/** Backward pagination: number of items to fetch from the end */
|
|
238
|
+
last?: number | null;
|
|
239
|
+
/** Backward pagination: cursor to fetch before */
|
|
240
|
+
before?: string | null;
|
|
216
241
|
}
|
|
217
242
|
|
|
218
243
|
// =============================================================================
|
|
@@ -438,14 +463,20 @@ export interface UseCollectionReturn<
|
|
|
438
463
|
pageSize: number;
|
|
439
464
|
/** Current cursor position */
|
|
440
465
|
cursor: string | null;
|
|
441
|
-
/**
|
|
466
|
+
/** Current pagination direction */
|
|
467
|
+
paginationDirection: "forward" | "backward";
|
|
468
|
+
/** Navigate to next page using endCursor from pageInfo */
|
|
442
469
|
nextPage(endCursor: string): void;
|
|
443
|
-
/** Navigate to previous page */
|
|
444
|
-
prevPage(): void;
|
|
470
|
+
/** Navigate to previous page using startCursor from pageInfo */
|
|
471
|
+
prevPage(startCursor: string): void;
|
|
445
472
|
/** Reset to first page */
|
|
446
473
|
resetPage(): void;
|
|
447
|
-
/** Whether there is a previous page */
|
|
474
|
+
/** Whether there is a previous page (from GraphQL pageInfo) */
|
|
448
475
|
hasPrevPage: boolean;
|
|
476
|
+
/** Whether there is a next page (from GraphQL pageInfo) */
|
|
477
|
+
hasNextPage: boolean;
|
|
478
|
+
/** Set pageInfo from graphql result to track hasPrevPage/hasNextPage */
|
|
479
|
+
setPageInfo(pageInfo: PageInfo): void;
|
|
449
480
|
}
|
|
450
481
|
|
|
451
482
|
// =============================================================================
|
|
@@ -551,9 +582,11 @@ export interface UseDataTableReturn<TRow extends Record<string, unknown>> {
|
|
|
551
582
|
/** Navigate to next page */
|
|
552
583
|
nextPage: (endCursor: string) => void;
|
|
553
584
|
/** Navigate to previous page */
|
|
554
|
-
prevPage: () => void;
|
|
585
|
+
prevPage: (startCursor: string) => void;
|
|
555
586
|
/** Whether a previous page exists */
|
|
556
587
|
hasPrevPage: boolean;
|
|
588
|
+
/** Whether a next page exists */
|
|
589
|
+
hasNextPage: boolean;
|
|
557
590
|
|
|
558
591
|
// Column management
|
|
559
592
|
/** All column definitions */
|
|
@@ -583,16 +616,30 @@ export interface UseDataTableReturn<TRow extends Record<string, unknown>> {
|
|
|
583
616
|
// =============================================================================
|
|
584
617
|
|
|
585
618
|
/**
|
|
586
|
-
* Extract field names from table metadata.
|
|
619
|
+
* Extract field names from a single table metadata object.
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```ts
|
|
623
|
+
* type Names = TableFieldName<typeof tableMetadata.task>;
|
|
624
|
+
* // → "id" | "title" | "status" | "dueDate" | ...
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
export type TableFieldName<TTable extends TableMetadata> =
|
|
628
|
+
TTable["fields"][number] extends { readonly name: infer N }
|
|
629
|
+
? N extends string
|
|
630
|
+
? N
|
|
631
|
+
: never
|
|
632
|
+
: never;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Extract field names from table metadata map + table name.
|
|
636
|
+
*
|
|
637
|
+
* @deprecated Use `TableFieldName<TMetadata[TTableName]>` instead.
|
|
587
638
|
*/
|
|
588
639
|
export type FieldName<
|
|
589
640
|
TMetadata extends TableMetadataMap,
|
|
590
641
|
TTableName extends keyof TMetadata,
|
|
591
|
-
> = TMetadata[TTableName]
|
|
592
|
-
? N extends string
|
|
593
|
-
? N
|
|
594
|
-
: never
|
|
595
|
-
: never;
|
|
642
|
+
> = TableFieldName<TMetadata[TTableName]>;
|
|
596
643
|
|
|
597
644
|
/**
|
|
598
645
|
* Field types that support ordering.
|
|
@@ -609,19 +656,16 @@ type OrderableFieldType =
|
|
|
609
656
|
| "enum";
|
|
610
657
|
|
|
611
658
|
/**
|
|
612
|
-
* Extract only orderable field names from table metadata.
|
|
659
|
+
* Extract only orderable field names from a single table metadata object.
|
|
613
660
|
*
|
|
614
661
|
* Fields whose `type` is not in `OrderableFieldType` (e.g. `uuid`, `array`,
|
|
615
662
|
* `nested`, `file`) are excluded. This allows `CheckOrderField` to compare
|
|
616
663
|
* only the fields that Tailor Platform actually exposes in the
|
|
617
664
|
* `OrderFieldEnum`, avoiding false positives for fields like `id`.
|
|
618
665
|
*/
|
|
619
|
-
export type
|
|
620
|
-
TMetadata extends TableMetadataMap,
|
|
621
|
-
TTableName extends keyof TMetadata,
|
|
622
|
-
> =
|
|
666
|
+
export type TableOrderableFieldName<TTable extends TableMetadata> =
|
|
623
667
|
Extract<
|
|
624
|
-
|
|
668
|
+
TTable["fields"][number],
|
|
625
669
|
{ readonly type: OrderableFieldType }
|
|
626
670
|
> extends { readonly name: infer N }
|
|
627
671
|
? N extends string
|
|
@@ -629,6 +673,16 @@ export type OrderableFieldName<
|
|
|
629
673
|
: never
|
|
630
674
|
: never;
|
|
631
675
|
|
|
676
|
+
/**
|
|
677
|
+
* Extract only orderable field names from table metadata map + table name.
|
|
678
|
+
*
|
|
679
|
+
* @deprecated Use `TableOrderableFieldName<TMetadata[TTableName]>` instead.
|
|
680
|
+
*/
|
|
681
|
+
export type OrderableFieldName<
|
|
682
|
+
TMetadata extends TableMetadataMap,
|
|
683
|
+
TTableName extends keyof TMetadata,
|
|
684
|
+
> = TableOrderableFieldName<TMetadata[TTableName]>;
|
|
685
|
+
|
|
632
686
|
/**
|
|
633
687
|
* Extract the `order[].field` union type from a GraphQL variables type.
|
|
634
688
|
*
|
|
@@ -718,9 +772,58 @@ type CheckFirstVariable<TQuery> =
|
|
|
718
772
|
};
|
|
719
773
|
|
|
720
774
|
/**
|
|
721
|
-
* Rule 2 —
|
|
722
|
-
|
|
723
|
-
|
|
775
|
+
* Rule 2 — The query must declare `$after` as a variable.
|
|
776
|
+
*/
|
|
777
|
+
type CheckAfterVariable<TQuery> =
|
|
778
|
+
"after" extends keyof ExtractQueryVariables<TQuery>
|
|
779
|
+
? Pass
|
|
780
|
+
: {
|
|
781
|
+
__afterVariableError: `Query must declare an $after variable (e.g. $after: String).`;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Rule 3 — The query must declare `$last` as a variable.
|
|
786
|
+
*/
|
|
787
|
+
type CheckLastVariable<TQuery> =
|
|
788
|
+
"last" extends keyof ExtractQueryVariables<TQuery>
|
|
789
|
+
? Pass
|
|
790
|
+
: {
|
|
791
|
+
__lastVariableError: `Query must declare a $last variable (e.g. $last: Int).`;
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Rule 4 — The query must declare `$before` as a variable.
|
|
796
|
+
*/
|
|
797
|
+
type CheckBeforeVariable<TQuery> =
|
|
798
|
+
"before" extends keyof ExtractQueryVariables<TQuery>
|
|
799
|
+
? Pass
|
|
800
|
+
: {
|
|
801
|
+
__beforeVariableError: `Query must declare a $before variable (e.g. $before: String).`;
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Rule 5 — The query must declare `$query` as a variable.
|
|
806
|
+
*/
|
|
807
|
+
type CheckQueryVariable<TQuery> =
|
|
808
|
+
"query" extends keyof ExtractQueryVariables<TQuery>
|
|
809
|
+
? Pass
|
|
810
|
+
: {
|
|
811
|
+
__queryVariableError: `Query must declare a $query variable (e.g. $query: XxxQueryInput!).`;
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Rule 6 — The query must declare `$order` as a variable.
|
|
816
|
+
*/
|
|
817
|
+
type CheckOrderVariable<TQuery> =
|
|
818
|
+
"order" extends keyof ExtractQueryVariables<TQuery>
|
|
819
|
+
? Pass
|
|
820
|
+
: {
|
|
821
|
+
__orderVariableError: `Query must declare an $order variable (e.g. $order: [XxxOrderInput]).`;
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Rule 7 — When metadata is provided, metadata field names must be
|
|
826
|
+
* assignable to the `field` enum inside the `OrderInput` type.
|
|
724
827
|
*/
|
|
725
828
|
type CheckOrderField<
|
|
726
829
|
TQuery,
|
|
@@ -734,9 +837,8 @@ type CheckOrderField<
|
|
|
734
837
|
: Pass;
|
|
735
838
|
|
|
736
839
|
/**
|
|
737
|
-
* Rule
|
|
738
|
-
*
|
|
739
|
-
* Skipped when `TFieldName` is the generic `string` (no metadata).
|
|
840
|
+
* Rule 8 — When metadata is provided, metadata field names must be a
|
|
841
|
+
* subset of the `QueryInput` type's keys.
|
|
740
842
|
*/
|
|
741
843
|
type CheckQueryInput<
|
|
742
844
|
TQuery,
|
|
@@ -757,8 +859,13 @@ type CheckQueryInput<
|
|
|
757
859
|
* intersecting independent rule types:
|
|
758
860
|
*
|
|
759
861
|
* 1. **`CheckFirstVariable`** — `$first` must exist.
|
|
760
|
-
* 2. **`
|
|
761
|
-
* 3. **`
|
|
862
|
+
* 2. **`CheckAfterVariable`** — `$after` must exist.
|
|
863
|
+
* 3. **`CheckLastVariable`** — `$last` must exist.
|
|
864
|
+
* 4. **`CheckBeforeVariable`** — `$before` must exist.
|
|
865
|
+
* 5. **`CheckQueryVariable`** — `$query` must exist.
|
|
866
|
+
* 6. **`CheckOrderVariable`** — `$order` must exist.
|
|
867
|
+
* 7. **`CheckOrderField`** — OrderInput field compatibility (metadata-aware).
|
|
868
|
+
* 8. **`CheckQueryInput`** — QueryInput key compatibility (metadata-aware).
|
|
762
869
|
*
|
|
763
870
|
* Each rule resolves to `{}` on pass or `{ __xxxError: "…" }` on fail.
|
|
764
871
|
* A failing rule adds a phantom error property that makes `TQuery`
|
|
@@ -778,9 +885,20 @@ export type ValidateCollectionQuery<
|
|
|
778
885
|
ExtractQueryVariables<TQuery> extends never
|
|
779
886
|
? TQuery // No gql-tada type info → skip validation
|
|
780
887
|
: string extends TFieldName
|
|
781
|
-
? TQuery &
|
|
888
|
+
? TQuery &
|
|
889
|
+
CheckFirstVariable<TQuery> &
|
|
890
|
+
CheckAfterVariable<TQuery> &
|
|
891
|
+
CheckLastVariable<TQuery> &
|
|
892
|
+
CheckBeforeVariable<TQuery> &
|
|
893
|
+
CheckQueryVariable<TQuery> &
|
|
894
|
+
CheckOrderVariable<TQuery>
|
|
782
895
|
: TQuery &
|
|
783
896
|
CheckFirstVariable<TQuery> &
|
|
897
|
+
CheckAfterVariable<TQuery> &
|
|
898
|
+
CheckLastVariable<TQuery> &
|
|
899
|
+
CheckBeforeVariable<TQuery> &
|
|
900
|
+
CheckQueryVariable<TQuery> &
|
|
901
|
+
CheckOrderVariable<TQuery> &
|
|
784
902
|
CheckOrderField<TQuery, TOrderableFieldName> &
|
|
785
903
|
CheckQueryInput<TQuery, TFieldName>;
|
|
786
904
|
|
|
@@ -791,6 +909,11 @@ export type ValidateCollectionQuery<
|
|
|
791
909
|
*/
|
|
792
910
|
export type HasCollectionQueryError<T> = T extends
|
|
793
911
|
| { __firstVariableError: string }
|
|
912
|
+
| { __afterVariableError: string }
|
|
913
|
+
| { __lastVariableError: string }
|
|
914
|
+
| { __beforeVariableError: string }
|
|
915
|
+
| { __queryVariableError: string }
|
|
916
|
+
| { __orderVariableError: string }
|
|
794
917
|
| { __orderFieldError: string }
|
|
795
918
|
| { __queryInputError: string }
|
|
796
919
|
? true
|
|
@@ -982,9 +1105,11 @@ export interface PaginationProps {
|
|
|
982
1105
|
/** Navigate to next page */
|
|
983
1106
|
nextPage: (endCursor: string) => void;
|
|
984
1107
|
/** Navigate to previous page */
|
|
985
|
-
prevPage: () => void;
|
|
1108
|
+
prevPage: (startCursor: string) => void;
|
|
986
1109
|
/** Whether a previous page exists */
|
|
987
1110
|
hasPrevPage: boolean;
|
|
1111
|
+
/** Whether a next page exists */
|
|
1112
|
+
hasNextPage: boolean;
|
|
988
1113
|
}
|
|
989
1114
|
|
|
990
1115
|
// =============================================================================
|