@tailor-platform/app-shell 0.36.0 → 1.0.1

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.
@@ -728,6 +728,18 @@ declare namespace Body_2 {
728
728
  var displayName: string;
729
729
  }
730
730
 
731
+ /**
732
+ * Builds a Tailor Platform QueryInput-compatible type from table metadata.
733
+ */
734
+ export declare type BuildQueryVariables<TTable extends TableMetadata> = {
735
+ [F in TTable["fields"][number] as F extends {
736
+ readonly name: infer N extends string;
737
+ readonly type: infer T;
738
+ } ? T extends keyof FieldTypeToFilterConfigType ? FieldTypeToFilterConfigType[T] extends never ? never : N : never : never]?: F extends {
739
+ readonly type: infer T extends keyof FieldTypeToFilterConfigType & keyof FieldTypeToTSType;
740
+ } ? FieldTypeToFilterConfigType[T] extends infer FCT extends FilterConfig["type"] ? FilterInputForFieldType<FCT, ResolveFieldValueType<F, T>> : never : never;
741
+ };
742
+
731
743
  /**
732
744
  * A styled button component with multiple variants and sizes.
733
745
  *
@@ -804,6 +816,139 @@ declare namespace Close_2 {
804
816
  var displayName: string;
805
817
  }
806
818
 
819
+ /**
820
+ * Collection control interface for UI components to interact with
821
+ * filter, sort, and pagination state.
822
+ */
823
+ export declare interface CollectionControl<TFieldName extends string = string, TFilter = Filter<TFieldName>> {
824
+ filters: Filter<TFieldName>[];
825
+ addFilter<F extends TFieldName>(field: F, operator: OperatorForField<TFilter, F>, value: unknown): void;
826
+ setFilters: (filters: Filter<TFieldName>[]) => void;
827
+ removeFilter(field: TFieldName): void;
828
+ clearFilters: () => void;
829
+ sortStates: SortState[];
830
+ setSort(field: TFieldName, direction?: "Asc" | "Desc"): void;
831
+ clearSort: () => void;
832
+ pageSize: number;
833
+ setPageSize: (size: number) => void;
834
+ /** Navigate to the next page. Mode-aware: pushes cursor in forward, pops stack in backward. */
835
+ goToNextPage: (pageInfo: Pick<PageInfo, "endCursor">) => void;
836
+ /** Navigate to the previous page. Mode-aware: pops stack in forward, pushes cursor in backward. */
837
+ goToPrevPage: (pageInfo: Pick<PageInfo, "startCursor">) => void;
838
+ resetPage: () => void;
839
+ goToFirstPage: () => void;
840
+ /**
841
+ * Navigate to the last page.
842
+ *
843
+ * When `total` is provided, the hook computes `total % pageSize` so the last
844
+ * page aligns with forward-pagination boundaries. Without `total`, it falls
845
+ * back to a full `pageSize` fetch.
846
+ */
847
+ goToLastPage: (total?: number | null) => void;
848
+ /**
849
+ * Determine whether a previous page exists, given the server's `pageInfo`.
850
+ * Accounts for the current pagination direction and cursor stack.
851
+ */
852
+ getHasPrevPage: (pageInfo: Pick<PageInfo, "hasPreviousPage">) => boolean;
853
+ /**
854
+ * Determine whether a next page exists, given the server's `pageInfo`.
855
+ * Accounts for the current pagination direction and cursor stack.
856
+ */
857
+ getHasNextPage: (pageInfo: Pick<PageInfo, "hasNextPage">) => boolean;
858
+ /**
859
+ * Monotonically increasing counter that bumps on every pagination reset.
860
+ * Used internally by `usePageCounter` to detect external resets (e.g.
861
+ * filter/sort changes) and sync `currentPage`.
862
+ */
863
+ resetCount: number;
864
+ }
865
+
866
+ /**
867
+ * Provider that shares collection control state via React Context.
868
+ *
869
+ * > **Note:** `DataTable.Root` automatically wraps its children with this provider
870
+ * > when `control` is supplied to `useDataTable`. Use `CollectionControlProvider`
871
+ * > directly only when you need to share control state with components that live
872
+ * > **outside** of `DataTable.Root` (e.g. a sibling filter panel or pagination
873
+ * > widget). Wrapping `DataTable.Root` with an outer `CollectionControlProvider`
874
+ * > while also passing `control` to `useDataTable` results in a double-wrap: the
875
+ * > inner provider (created by `DataTable.Root`) will silently shadow the outer
876
+ * > one for all descendants inside the table.
877
+ *
878
+ * @example
879
+ * ```tsx
880
+ * const { variables, control } = useCollectionVariables({ params: { pageSize: 20 } });
881
+ *
882
+ * <CollectionControlProvider value={control}>
883
+ * <FilterPanel />
884
+ * <DataTable.Root>...</DataTable.Root>
885
+ * <Pagination />
886
+ * </CollectionControlProvider>
887
+ * ```
888
+ */
889
+ export declare function CollectionControlProvider({ value, children, }: {
890
+ value: CollectionControl;
891
+ children: ReactNode;
892
+ }): JSX.Element;
893
+
894
+ /**
895
+ * GraphQL collection result format (Tailor Platform standard).
896
+ */
897
+ export declare interface CollectionResult<T> {
898
+ edges: {
899
+ node: T;
900
+ }[];
901
+ pageInfo: PageInfo;
902
+ total?: number | null;
903
+ }
904
+
905
+ /**
906
+ * Collection variables split into explicit sub-properties.
907
+ */
908
+ export declare interface CollectionVariables {
909
+ query: Record<string, Record<string, unknown>> | undefined;
910
+ order: {
911
+ field: string;
912
+ direction: "Asc" | "Desc";
913
+ }[] | undefined;
914
+ pagination: PaginationVariables;
915
+ }
916
+
917
+ /**
918
+ * A column definition for DataTable.
919
+ */
920
+ export declare interface Column<TRow extends Record<string, unknown>> {
921
+ /** Column header text. Omit for action or icon-only columns. */
922
+ label?: string;
923
+ /** Renders the cell content for a given row. Required. */
924
+ render: (row: TRow) => ReactNode;
925
+ /**
926
+ * Stable identifier used for column visibility toggling and as the React key.
927
+ * Falls back to `label` when omitted. Set this explicitly when `label` is
928
+ * absent or not unique.
929
+ */
930
+ id?: string;
931
+ /** Fixed column width in pixels. When omitted the column sizes naturally. */
932
+ width?: number;
933
+ /**
934
+ * Extracts the raw value from a row for purposes such as sorting or
935
+ * clipboard copying. Not used for rendering — use `render` for that.
936
+ */
937
+ accessor?: (row: TRow) => unknown;
938
+ /**
939
+ * Sort configuration. When set, the column header becomes clickable and
940
+ * cycles through `Asc → Desc → undefined`.
941
+ * Use `fieldTypeToSortConfig` or `inferColumns` to derive this automatically.
942
+ */
943
+ sort?: SortConfig;
944
+ /**
945
+ * Filter configuration. When set, this column appears as an option in
946
+ * `DataTable.Filters`.
947
+ * Use `fieldTypeToFilterConfig` or `inferColumns` to derive this automatically.
948
+ */
949
+ filter?: FilterConfig;
950
+ }
951
+
807
952
  /**
808
953
  * Supported area values for Layout.Column
809
954
  */
@@ -814,6 +959,59 @@ declare type ColumnArea = "left" | "main" | "right";
814
959
  */
815
960
  declare type ColumnCount = 1 | 2 | 3;
816
961
 
962
+ /**
963
+ * Object returned by `createColumnHelper`.
964
+ */
965
+ declare interface ColumnHelper<TRow extends Record<string, unknown>> {
966
+ /**
967
+ * Define a column with an explicit render function.
968
+ *
969
+ * @example
970
+ * ```tsx
971
+ * column({ label: "Name", render: (row) => row.name })
972
+ * column({ label: "Actions", render: (row) => <button>Edit {row.name}</button> })
973
+ * ```
974
+ */
975
+ column: (options: Column<TRow>) => Column<TRow>;
976
+ /**
977
+ * Bind table metadata once and return a per-field column factory.
978
+ *
979
+ * The returned factory **throws at call time** if the given field name is not
980
+ * found in `tableMetadata`. This typically happens at module initialisation
981
+ * (i.e. when columns are defined outside a component), so it surfaces as an
982
+ * uncaught error rather than a render error. Field-name narrowing is only
983
+ * active when `tableMetadata` is typed from an `as const` source (e.g. a
984
+ * file generated by `appShellMetadataPlugin`).
985
+ *
986
+ * @throws {Error} If the field name is not found in the provided table metadata.
987
+ *
988
+ * @example
989
+ * ```tsx
990
+ * const infer = inferColumns(tableMetadata.order);
991
+ * const columns = [
992
+ * column(infer("title")),
993
+ * column({
994
+ * ...infer("status"),
995
+ * render: (row) => <StatusBadge value={row.status} />
996
+ * }),
997
+ * ];
998
+ * ```
999
+ */
1000
+ inferColumns: <const TTable extends TableMetadata = TableMetadata>(tableMetadata: TTable) => ColumnInferFn<TRow, TTable>;
1001
+ }
1002
+
1003
+ /**
1004
+ * Per-field column factory returned by `inferColumns(tableMetadata)`.
1005
+ *
1006
+ * Derives label, sort config, and filter config from the field's metadata.
1007
+ * Sort and filter can be suppressed per call via `options`.
1008
+ *
1009
+ * @param dataKey - A field name from the bound table metadata.
1010
+ * @param options - Optional per-column overrides (label, width, sort, filter).
1011
+ * @throws {Error} If `dataKey` is not found in the metadata passed to `inferColumns`.
1012
+ */
1013
+ declare type ColumnInferFn<TRow extends Record<string, unknown>, TTable extends TableMetadata = TableMetadata> = (dataKey: TableFieldName<TTable>, options?: MetadataFieldOptions) => Column<TRow>;
1014
+
817
1015
  /**
818
1016
  * Props for individual Layout.Column component
819
1017
  */
@@ -1274,6 +1472,23 @@ declare interface CreatableProps<T extends object> {
1274
1472
  */
1275
1473
  export declare function createAuthClient(config: AuthClientConfig): EnhancedAuthClient;
1276
1474
 
1475
+ /**
1476
+ * Factory that captures the row type once and returns `column` and `inferColumns`
1477
+ * with `TRow` already bound.
1478
+ *
1479
+ * @example
1480
+ * ```tsx
1481
+ * const { column, inferColumns } = createColumnHelper<Order>();
1482
+ *
1483
+ * const infer = inferColumns(tableMetadata.order);
1484
+ * const columns = [
1485
+ * column(infer("title")),
1486
+ * column({ label: "Actions", render: (row) => <button>Edit {row.name}</button> }),
1487
+ * ];
1488
+ * ```
1489
+ */
1490
+ export declare function createColumnHelper<TRow extends Record<string, unknown>>(): ColumnHelper<TRow>;
1491
+
1277
1492
  /**
1278
1493
  * Create a type-safe path builder from route definitions.
1279
1494
  *
@@ -1430,6 +1645,181 @@ export declare type CsvSchema = {
1430
1645
  columns: readonly CsvColumn[];
1431
1646
  };
1432
1647
 
1648
+ export declare const DataTable: {
1649
+ /**
1650
+ * Context/provider root. Wraps all other `DataTable.*` components and
1651
+ * wires state from `useDataTable()` into context.
1652
+ */
1653
+ readonly Root: typeof DataTableRoot;
1654
+ /**
1655
+ * Container for toolbar content (column visibility, search, etc.).
1656
+ * Place inside `DataTable.Root`, before `DataTable.Table`.
1657
+ */
1658
+ readonly Toolbar: typeof DataTableToolbar;
1659
+ /**
1660
+ * Auto-generated filter chips from column filter configs.
1661
+ *
1662
+ * **Requires `control`** — `useDataTable()` must receive `control` from
1663
+ * `useCollectionVariables()`, otherwise this component throws at render time.
1664
+ */
1665
+ readonly Filters: typeof DataTableFilters;
1666
+ /**
1667
+ * Renders `<table>` with built-in `Headers` and `Body`.
1668
+ * Place inside `DataTable.Root`.
1669
+ */
1670
+ readonly Table: typeof DataTableTable;
1671
+ /**
1672
+ * Footer container for pagination and other footer content.
1673
+ * Place inside `DataTable.Root`, after `DataTable.Table`.
1674
+ */
1675
+ readonly Footer: typeof DataTableFooter;
1676
+ /**
1677
+ * Pre-built pagination controls. Place inside `DataTable.Footer`.
1678
+ *
1679
+ * **Requires `control`** — `useDataTable()` must receive `control` from
1680
+ * `useCollectionVariables()`, otherwise this component throws at render time.
1681
+ *
1682
+ * **Go-to-first / go-to-last buttons** — Rendered only when `totalPages` is
1683
+ * non-null (i.e. the backend returns a total count). When `totalPages` is
1684
+ * `null`, these buttons and the page counter are omitted.
1685
+ */
1686
+ readonly Pagination: typeof DataTablePagination;
1687
+ };
1688
+
1689
+ /**
1690
+ * Context value provided by `DataTable.Root`.
1691
+ *
1692
+ * `control` is intentionally excluded from this context: collection control
1693
+ * (filters, sort, and pagination) is managed by `CollectionControlProvider`
1694
+ * via a separate context. Keeping them separate means `DataTable.Root` works
1695
+ * without any collection control (read-only / static tables), while
1696
+ * `CollectionControlProvider` can optionally wrap it when filtering and
1697
+ * pagination are needed. This also avoids coupling `DataTableContextValue`
1698
+ * to `UseDataTableReturn` — changes to `UseDataTableReturn` require an
1699
+ * explicit decision about whether they should appear in the context.
1700
+ */
1701
+ export declare interface DataTableContextValue<TRow extends Record<string, unknown>> {
1702
+ rows: TRow[];
1703
+ loading: boolean;
1704
+ error: Error | null;
1705
+ sortStates: SortState[];
1706
+ onSort?: (field: string, direction?: "Asc" | "Desc") => void;
1707
+ pageInfo: PageInfo;
1708
+ total: number | null;
1709
+ totalPages: number | null;
1710
+ /** 1-based page counter, automatically kept in sync by navigation actions. */
1711
+ currentPage: number;
1712
+ goToNextPage: (pageInfo: Pick<PageInfo, "endCursor">) => void;
1713
+ goToPrevPage: (pageInfo: Pick<PageInfo, "startCursor">) => void;
1714
+ /** Jump to the first page. */
1715
+ goToFirstPage: () => void;
1716
+ /** Jump to the last page. Only functional when `totalPages` is known. */
1717
+ goToLastPage: () => void;
1718
+ hasPrevPage: boolean;
1719
+ hasNextPage: boolean;
1720
+ /**
1721
+ * The resolved page size from the collection control. `0` when no control is
1722
+ * configured. Used to render the correct number of skeleton / ghost rows.
1723
+ */
1724
+ pageSize: number;
1725
+ /** Change the page size and reset to the first page. */
1726
+ setPageSize: (size: number) => void;
1727
+ columns: Column<TRow>[];
1728
+ visibleColumns: Column<TRow>[];
1729
+ toggleColumn: (fieldOrId: string) => void;
1730
+ showAllColumns: () => void;
1731
+ hideAllColumns: () => void;
1732
+ isColumnVisible: (fieldOrId: string) => boolean;
1733
+ onClickRow?: (row: TRow) => void;
1734
+ rowActions?: RowAction<TRow>[];
1735
+ selectedIds: string[];
1736
+ isRowSelected: (row: TRow) => boolean;
1737
+ toggleRowSelection?: (row: TRow) => void;
1738
+ /**
1739
+ * Selects all rows on the **current page** only. Cross-page selection is not supported.
1740
+ * Undefined when `onSelectionChange` is not provided.
1741
+ */
1742
+ selectAllRows?: () => void;
1743
+ clearSelection?: () => void;
1744
+ isAllSelected: boolean;
1745
+ isIndeterminate: boolean;
1746
+ }
1747
+
1748
+ /**
1749
+ * Data input for `useDataTable` hook.
1750
+ */
1751
+ export declare interface DataTableData<TRow> {
1752
+ rows: TRow[];
1753
+ pageInfo?: PageInfo;
1754
+ total?: number | null;
1755
+ }
1756
+
1757
+ /** Use `DataTable.Filters` instead of calling this directly. */
1758
+ declare function DataTableFilters({ className }: {
1759
+ className?: string;
1760
+ }): JSX.Element | null;
1761
+
1762
+ declare namespace DataTableFilters {
1763
+ var displayName: string;
1764
+ }
1765
+
1766
+ /** Use `DataTable.Footer` instead of calling this directly. */
1767
+ declare function DataTableFooter({ children, className }: {
1768
+ children: ReactNode;
1769
+ className?: string;
1770
+ }): JSX.Element;
1771
+
1772
+ declare namespace DataTableFooter {
1773
+ var displayName: string;
1774
+ }
1775
+
1776
+ /** Use `DataTable.Pagination` instead of calling this directly. */
1777
+ declare function DataTablePagination({ pageSizeOptions }?: DataTablePaginationProps): JSX.Element;
1778
+
1779
+ declare namespace DataTablePagination {
1780
+ var displayName: string;
1781
+ }
1782
+
1783
+ export declare interface DataTablePaginationProps {
1784
+ /**
1785
+ * Available page-size options shown in a dropdown selector.
1786
+ * When provided, a page-size switcher is rendered.
1787
+ */
1788
+ pageSizeOptions?: number[];
1789
+ }
1790
+
1791
+ /** Use `DataTable.Root` instead of calling this directly. */
1792
+ declare function DataTableRoot<TRow extends Record<string, unknown>>({ value, children, className, }: DataTableRootProps<TRow>): JSX.Element;
1793
+
1794
+ declare namespace DataTableRoot {
1795
+ var displayName: string;
1796
+ }
1797
+
1798
+ export declare interface DataTableRootProps<TRow extends Record<string, unknown>> {
1799
+ value: UseDataTableReturn<TRow>;
1800
+ children: ReactNode;
1801
+ className?: string;
1802
+ }
1803
+
1804
+ /** Use `DataTable.Table` instead of calling this directly. */
1805
+ declare function DataTableTable({ className }: {
1806
+ className?: string;
1807
+ }): JSX.Element;
1808
+
1809
+ declare namespace DataTableTable {
1810
+ var displayName: string;
1811
+ }
1812
+
1813
+ /** Use `DataTable.Toolbar` instead of calling this directly. */
1814
+ declare function DataTableToolbar({ children, className }: {
1815
+ children: ReactNode;
1816
+ className?: string;
1817
+ }): JSX.Element;
1818
+
1819
+ declare namespace DataTableToolbar {
1820
+ var displayName: string;
1821
+ }
1822
+
1433
1823
  /**
1434
1824
  * Date format options
1435
1825
  */
@@ -1948,7 +2338,7 @@ declare type FieldConfig = FieldDivider | FieldDefinition;
1948
2338
  */
1949
2339
  declare interface FieldDefinition {
1950
2340
  /** Field type determines rendering (defaults to "text") */
1951
- type?: FieldType;
2341
+ type?: FieldType_2;
1952
2342
  /** Path to the value in the data object (supports dot notation) */
1953
2343
  key: string;
1954
2344
  /** Display label for the field */
@@ -1992,6 +2382,22 @@ declare interface FieldMeta {
1992
2382
  truncateLines?: number;
1993
2383
  }
1994
2384
 
2385
+ /**
2386
+ * Metadata for a single field.
2387
+ */
2388
+ export declare interface FieldMetadata {
2389
+ readonly name: string;
2390
+ readonly type: FieldType;
2391
+ readonly required: boolean;
2392
+ readonly enumValues?: readonly string[];
2393
+ readonly arrayItemType?: FieldType;
2394
+ readonly description?: string;
2395
+ readonly relation?: {
2396
+ readonly fieldName: string;
2397
+ readonly targetTable: string;
2398
+ };
2399
+ }
2400
+
1995
2401
  declare type FieldRootProps = Pick<React_2.ComponentProps<typeof Field_2.Root>, "name" | "disabled" | "validate" | "validationMode" | "validationDebounceTime" | "className" | "style"> & {
1996
2402
  children: React_2.ReactNode;
1997
2403
  /** Whether the field has been blurred. Maps to Base UI's `touched`. */
@@ -2036,10 +2442,101 @@ declare type FieldsetRootProps = Pick<React_2.ComponentProps<typeof Fieldset_2.R
2036
2442
  children: React_2.ReactNode;
2037
2443
  };
2038
2444
 
2445
+ /**
2446
+ * Field type mapping for Data View.
2447
+ */
2448
+ export declare type FieldType = "string" | "number" | "boolean" | "uuid" | "datetime" | "date" | "time" | "enum" | "array" | "nested" | "file";
2449
+
2039
2450
  /**
2040
2451
  * Supported field types for DescriptionCard
2041
2452
  */
2042
- declare type FieldType = "text" | "badge" | "money" | "date" | "link" | "address" | "reference";
2453
+ declare type FieldType_2 = "text" | "badge" | "money" | "date" | "link" | "address" | "reference";
2454
+
2455
+ /**
2456
+ * Map metadata field type to FilterConfig.
2457
+ * Returns undefined for types that don't support filtering.
2458
+ */
2459
+ export declare function fieldTypeToFilterConfig(field: string, type: FieldType, enumValues?: readonly string[]): FilterConfig | undefined;
2460
+
2461
+ /**
2462
+ * Type-level mapping from `FieldType` (metadata) to `FilterConfig["type"]`.
2463
+ */
2464
+ declare type FieldTypeToFilterConfigType = {
2465
+ string: "string";
2466
+ number: "number";
2467
+ boolean: "boolean";
2468
+ uuid: "uuid";
2469
+ datetime: "date";
2470
+ date: "date";
2471
+ time: "date";
2472
+ enum: "enum";
2473
+ array: never;
2474
+ nested: never;
2475
+ file: never;
2476
+ };
2477
+
2478
+ /**
2479
+ * Map metadata field type to SortConfig.
2480
+ * Returns undefined for types that don't support sorting.
2481
+ */
2482
+ export declare function fieldTypeToSortConfig(field: string, type: FieldType): SortConfig | undefined;
2483
+
2484
+ declare type FieldTypeToTSType = {
2485
+ string: string;
2486
+ number: number;
2487
+ boolean: boolean;
2488
+ uuid: string;
2489
+ datetime: string;
2490
+ date: string;
2491
+ time: string;
2492
+ enum: string;
2493
+ };
2494
+
2495
+ /**
2496
+ * Active filter state.
2497
+ *
2498
+ * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
2499
+ */
2500
+ export declare interface Filter<TFieldName extends string = string> {
2501
+ field: TFieldName;
2502
+ operator: FilterOperator;
2503
+ value: unknown;
2504
+ }
2505
+
2506
+ /**
2507
+ * Filter configuration for a column.
2508
+ * The `type` determines which operators are available.
2509
+ * The `field` identifies the backend field name used for filtering.
2510
+ */
2511
+ export declare type FilterConfig = {
2512
+ field: string;
2513
+ type: "string";
2514
+ } | {
2515
+ field: string;
2516
+ type: "number";
2517
+ } | {
2518
+ field: string;
2519
+ type: "date";
2520
+ } | {
2521
+ field: string;
2522
+ type: "enum";
2523
+ options: SelectOption[];
2524
+ } | {
2525
+ field: string;
2526
+ type: "boolean";
2527
+ } | {
2528
+ field: string;
2529
+ type: "uuid";
2530
+ };
2531
+
2532
+ declare type FilterInputForFieldType<TFilterConfigType extends FilterConfig["type"], TValue> = {
2533
+ [Op in OperatorForFilterType[TFilterConfigType]]?: OperatorValueType<Op, TValue>;
2534
+ };
2535
+
2536
+ /**
2537
+ * Union of all available filter operators.
2538
+ */
2539
+ export declare type FilterOperator = OperatorForFilterType[FilterConfig["type"]];
2043
2540
 
2044
2541
  /** The table footer section (`<tfoot>`). */
2045
2542
  declare function Footer({ className, ...props }: React_2.ComponentProps<"tfoot">): JSX.Element;
@@ -2552,6 +3049,26 @@ declare namespace MenuSeparator {
2552
3049
  var displayName: string;
2553
3050
  }
2554
3051
 
3052
+ /**
3053
+ * Options for metadata-based single field inference.
3054
+ */
3055
+ export declare interface MetadataFieldOptions {
3056
+ /** Override the column header text. Defaults to the field's `description` or `name` from metadata. */
3057
+ label?: string;
3058
+ /** Fixed column width in pixels. When omitted the column sizes naturally. */
3059
+ width?: number;
3060
+ /**
3061
+ * Set to `false` to suppress the auto-generated sort config for this field.
3062
+ * Defaults to `true` (sort is enabled when the field type supports it).
3063
+ */
3064
+ sort?: boolean;
3065
+ /**
3066
+ * Set to `false` to suppress the auto-generated filter config for this field.
3067
+ * Defaults to `true` (filter is enabled when the field type supports it).
3068
+ */
3069
+ filter?: boolean;
3070
+ }
3071
+
2555
3072
  /**
2556
3073
  * MetricCard — Compact card for dashboard KPI display (title, value, optional trend and description).
2557
3074
  *
@@ -2621,6 +3138,54 @@ declare type Module = Omit<CommonPageResource, "meta"> & {
2621
3138
 
2622
3139
  declare type Modules = Array<Module>;
2623
3140
 
3141
+ /**
3142
+ * Extract the node type from a cursor connection result.
3143
+ */
3144
+ export declare type NodeType<T extends {
3145
+ edges: {
3146
+ node: unknown;
3147
+ }[];
3148
+ } | null | undefined> = NonNullable<T>["edges"][number]["node"];
3149
+
3150
+ /**
3151
+ * Resolve the operator union for a specific field within a filter type.
3152
+ */
3153
+ declare type OperatorForField<TFilter, F extends string> = [Extract<TFilter, {
3154
+ field: F;
3155
+ }>] extends [
3156
+ never
3157
+ ] ? FilterOperator : Extract<TFilter, {
3158
+ field: F;
3159
+ }> extends {
3160
+ operator: infer O;
3161
+ } ? O : FilterOperator;
3162
+
3163
+ /**
3164
+ * Maps each filter type to the union of operators it supports.
3165
+ */
3166
+ declare type OperatorForFilterType = {
3167
+ [T in FilterConfig["type"]]: (typeof OPERATORS_BY_FILTER_TYPE)[T][number];
3168
+ };
3169
+
3170
+ /**
3171
+ * Operators available per filter type (Tailor Platform schema).
3172
+ */
3173
+ export declare const OPERATORS_BY_FILTER_TYPE: {
3174
+ readonly string: readonly ["eq", "ne", "contains", "notContains", "hasPrefix", "hasSuffix", "notHasPrefix", "notHasSuffix", "in", "nin"];
3175
+ readonly number: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "between", "in", "nin"];
3176
+ readonly date: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "between", "in", "nin"];
3177
+ readonly enum: readonly ["eq", "ne", "in", "nin"];
3178
+ readonly boolean: readonly ["eq", "ne"];
3179
+ readonly uuid: readonly ["eq", "ne", "in", "nin"];
3180
+ };
3181
+
3182
+ declare type OperatorValueType<TOp extends string, TValue> = TOp extends "in" | "nin" ? TValue[] : TOp extends "between" ? {
3183
+ min: TValue;
3184
+ max: TValue;
3185
+ } : TValue;
3186
+
3187
+ declare type OrderableFieldType = "string" | "number" | "boolean" | "datetime" | "date" | "time" | "enum";
3188
+
2624
3189
  /**
2625
3190
  * A React component that can be used as a page in file-based routing.
2626
3191
  *
@@ -2645,11 +3210,31 @@ declare type PageEntry = {
2645
3210
  component: PageComponent;
2646
3211
  };
2647
3212
 
3213
+ /**
3214
+ * Page info for cursor-based pagination (Relay Connection spec).
3215
+ */
3216
+ export declare interface PageInfo {
3217
+ hasNextPage: boolean;
3218
+ hasPreviousPage: boolean;
3219
+ endCursor: string | null;
3220
+ startCursor: string | null;
3221
+ }
3222
+
2648
3223
  export declare type PageMeta = {
2649
3224
  title: string;
2650
3225
  icon?: ReactNode;
2651
3226
  };
2652
3227
 
3228
+ /**
3229
+ * Pagination variables for cursor-based pagination (Tailor Platform format).
3230
+ */
3231
+ export declare interface PaginationVariables {
3232
+ first?: number;
3233
+ after?: string | null;
3234
+ last?: number;
3235
+ before?: string | null;
3236
+ }
3237
+
2653
3238
  /** The row data passed to `onValidate`, after transforms have been applied. */
2654
3239
  export declare type ParsedRow = {
2655
3240
  /** 0-based row index, stable across edits. */
@@ -2724,6 +3309,10 @@ declare namespace RadioItemIndicator {
2724
3309
  */
2725
3310
  export declare const redirectTo: (to: string) => GuardResult;
2726
3311
 
3312
+ declare type ResolveFieldValueType<F, T extends keyof FieldTypeToTSType> = T extends "enum" ? F extends {
3313
+ readonly enumValues: readonly (infer V extends string)[];
3314
+ } ? V : string : FieldTypeToTSType[T];
3315
+
2727
3316
  /**
2728
3317
  * A resource that can be included in the sub-content in the root resource.
2729
3318
  *
@@ -3001,6 +3590,18 @@ declare namespace Row {
3001
3590
  var displayName: string;
3002
3591
  }
3003
3592
 
3593
+ /**
3594
+ * A single row action definition for the actions column.
3595
+ */
3596
+ export declare interface RowAction<TRow extends Record<string, unknown>> {
3597
+ id: string;
3598
+ label: string;
3599
+ icon?: ReactNode;
3600
+ variant?: "default" | "destructive";
3601
+ isDisabled?: (row: TRow) => boolean;
3602
+ onClick: (row: TRow) => void;
3603
+ }
3604
+
3004
3605
  /**
3005
3606
  * An async search source that can be plugged into the CommandPalette.
3006
3607
  *
@@ -3099,6 +3700,14 @@ declare namespace SelectItem {
3099
3700
  var displayName: string;
3100
3701
  }
3101
3702
 
3703
+ /**
3704
+ * Select option for enum/dropdown filters.
3705
+ */
3706
+ export declare interface SelectOption {
3707
+ value: string;
3708
+ label: string;
3709
+ }
3710
+
3102
3711
  declare interface SelectPropsBase<T> {
3103
3712
  /** Placeholder text shown when no value is selected */
3104
3713
  placeholder?: string;
@@ -3519,6 +4128,33 @@ export declare type SidebarLayoutProps = {
3519
4128
  */
3520
4129
  export declare const SidebarSeparator: () => JSX.Element;
3521
4130
 
4131
+ /**
4132
+ * Sort configuration for a column.
4133
+ * The `type` determines how values are compared when sorting.
4134
+ * The `field` identifies the backend field name used for ordering.
4135
+ */
4136
+ export declare type SortConfig = {
4137
+ field: string;
4138
+ type: "string";
4139
+ } | {
4140
+ field: string;
4141
+ type: "number";
4142
+ } | {
4143
+ field: string;
4144
+ type: "date";
4145
+ } | {
4146
+ field: string;
4147
+ type: "boolean";
4148
+ };
4149
+
4150
+ /**
4151
+ * Active sort state for a single field.
4152
+ */
4153
+ export declare interface SortState {
4154
+ field: string;
4155
+ direction: "Asc" | "Desc";
4156
+ }
4157
+
3522
4158
  /** Groups all parts of a submenu. */
3523
4159
  declare function SubmenuRoot({ children, ...props }: SubmenuRootProps): JSX.Element;
3524
4160
 
@@ -3548,6 +4184,57 @@ export declare const Table: {
3548
4184
  Caption: typeof Caption;
3549
4185
  };
3550
4186
 
4187
+ /**
4188
+ * Extract field names from a single table metadata object.
4189
+ */
4190
+ export declare type TableFieldName<TTable extends TableMetadata> = TTable["fields"][number] extends {
4191
+ readonly name: infer N;
4192
+ } ? N extends string ? N : never : never;
4193
+
4194
+ /**
4195
+ * Metadata for a single table.
4196
+ */
4197
+ export declare interface TableMetadata {
4198
+ readonly name: string;
4199
+ readonly pluralForm: string;
4200
+ readonly description?: string;
4201
+ readonly fields: readonly FieldMetadata[];
4202
+ readonly relations?: readonly {
4203
+ readonly relationType: "manyToOne" | "oneToOne" | "oneToMany";
4204
+ readonly fieldName: string;
4205
+ readonly targetTable: string;
4206
+ readonly foreignKeyField: string;
4207
+ }[];
4208
+ }
4209
+
4210
+ /**
4211
+ * Metadata-aware filter type from a single table metadata object.
4212
+ */
4213
+ export declare type TableMetadataFilter<TTable extends TableMetadata> = TTable["fields"][number] extends infer F ? F extends {
4214
+ readonly name: infer N;
4215
+ readonly type: infer T;
4216
+ } ? N extends string ? T extends keyof FieldTypeToFilterConfigType ? FieldTypeToFilterConfigType[T] extends never ? never : {
4217
+ field: N;
4218
+ operator: OperatorForFilterType[FieldTypeToFilterConfigType[T]];
4219
+ value: unknown;
4220
+ } : never : never : never : never;
4221
+
4222
+ /**
4223
+ * Map of all tables.
4224
+ */
4225
+ export declare type TableMetadataMap = {
4226
+ readonly [key: string]: TableMetadata;
4227
+ };
4228
+
4229
+ /**
4230
+ * Extract only orderable field names from a single table metadata object.
4231
+ */
4232
+ export declare type TableOrderableFieldName<TTable extends TableMetadata> = Extract<TTable["fields"][number], {
4233
+ readonly type: OrderableFieldType;
4234
+ }> extends {
4235
+ readonly name: infer N;
4236
+ } ? N extends string ? N : never : never;
4237
+
3551
4238
  declare type Theme = "dark" | "light" | "system";
3552
4239
 
3553
4240
  declare type ThemeProviderState = {
@@ -3936,6 +4623,90 @@ export declare const useAuthSuspense: () => {
3936
4623
  checkAuthStatus: () => Promise<AuthState>;
3937
4624
  };
3938
4625
 
4626
+ /**
4627
+ * Hook to access collection control from the nearest `CollectionControlProvider`.
4628
+ *
4629
+ * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
4630
+ *
4631
+ * @throws Error if used outside of `CollectionControlProvider`.
4632
+ */
4633
+ export declare function useCollectionControl<TFieldName extends string = string>(): CollectionControl<TFieldName>;
4634
+
4635
+ /**
4636
+ * Options for `useCollectionVariables` hook.
4637
+ */
4638
+ export declare interface UseCollectionOptions<TFieldName extends string = string, TFilter extends Filter<TFieldName> = Filter<TFieldName>> {
4639
+ params?: {
4640
+ initialFilters?: TFilter[];
4641
+ initialSort?: {
4642
+ field: TFieldName;
4643
+ direction: "Asc" | "Desc";
4644
+ }[];
4645
+ pageSize?: number;
4646
+ };
4647
+ }
4648
+
4649
+ /**
4650
+ * Return type of `useCollectionVariables` hook.
4651
+ */
4652
+ export declare interface UseCollectionReturn<TFieldName extends string = string, TVariables = CollectionVariables, TFilter = Filter<TFieldName>> {
4653
+ variables: TVariables;
4654
+ control: CollectionControl<TFieldName, TFilter>;
4655
+ }
4656
+
4657
+ /**
4658
+ * Hook for managing collection query parameters (filters, sort, pagination)
4659
+ * with metadata-based field name typing and automatic `fieldType` detection.
4660
+ *
4661
+ * Returns `variables` with `query`, `order`, and `pagination` sub-properties
4662
+ * that can be mapped to GraphQL query variables.
4663
+ *
4664
+ * @example
4665
+ * ```tsx
4666
+ * import { tableMetadata } from "./generated/data-viewer-metadata.generated";
4667
+ *
4668
+ * const { variables } = useCollectionVariables({
4669
+ * tableMetadata: tableMetadata.task,
4670
+ * params: { pageSize: 20 },
4671
+ * });
4672
+ * const { query, order, pagination } = variables;
4673
+ * const [result] = useQuery({
4674
+ * query: GET_TASKS,
4675
+ * variables: { ...pagination, query, order },
4676
+ * });
4677
+ * ```
4678
+ */
4679
+ export declare function useCollectionVariables<const TTable extends TableMetadata>(options: UseCollectionOptions<TableFieldName<TTable>, TableMetadataFilter<TTable>> & {
4680
+ tableMetadata: TTable;
4681
+ }): UseCollectionReturn<TableFieldName<TTable>, {
4682
+ query: BuildQueryVariables<TTable> | undefined;
4683
+ order: {
4684
+ field: TableOrderableFieldName<TTable>;
4685
+ direction: "Asc" | "Desc";
4686
+ }[] | undefined;
4687
+ pagination: PaginationVariables;
4688
+ }, TableMetadataFilter<TTable>>;
4689
+
4690
+ /**
4691
+ * Hook for managing collection query parameters (filters, sort, pagination).
4692
+ *
4693
+ * Returns `variables` with `query`, `order`, and `pagination` sub-properties
4694
+ * that can be mapped to GraphQL query variables.
4695
+ *
4696
+ * @example
4697
+ * ```tsx
4698
+ * const { variables } = useCollectionVariables({ params: { pageSize: 20 } });
4699
+ * const { query, order, pagination } = variables;
4700
+ * const [result] = useQuery({
4701
+ * query: GET_ORDERS,
4702
+ * variables: { ...pagination, query, order },
4703
+ * });
4704
+ * ```
4705
+ */
4706
+ export declare function useCollectionVariables(options: UseCollectionOptions & {
4707
+ tableMetadata?: never;
4708
+ }): UseCollectionReturn<string, CollectionVariables>;
4709
+
3939
4710
  /**
3940
4711
  * Hook that encapsulates the "creatable" combobox pattern — allowing users
3941
4712
  * to create new items on-the-fly from the dropdown.
@@ -4095,6 +4866,158 @@ declare type UseCsvImporterOptions<T extends CsvSchema> = {
4095
4866
  maxFileSize?: number;
4096
4867
  };
4097
4868
 
4869
+ /**
4870
+ * Hook that integrates data management, column visibility, row operations, and
4871
+ * sort/pagination state for the `DataTable.*` compound component.
4872
+ *
4873
+ * @example
4874
+ * ```tsx
4875
+ * const { variables, control } = useCollectionVariables({ params: { pageSize: 20 } });
4876
+ * const [result] = useQuery({
4877
+ * query: GET_ORDERS,
4878
+ * variables: { ...variables.pagination, query: variables.query, order: variables.order },
4879
+ * });
4880
+ *
4881
+ * const table = useDataTable<Order>({
4882
+ * columns,
4883
+ * data: {
4884
+ * rows: result.data?.orders?.edges.map(e => e.node) ?? [],
4885
+ * pageInfo: { ... },
4886
+ * total: result.data?.orders?.total,
4887
+ * },
4888
+ * loading: result.fetching,
4889
+ * control,
4890
+ * });
4891
+ *
4892
+ * <DataTable.Root value={table}>
4893
+ * <DataTable.Table />
4894
+ * <DataTable.Footer>
4895
+ * <DataTable.Pagination />
4896
+ * </DataTable.Footer>
4897
+ * </DataTable.Root>
4898
+ * ```
4899
+ */
4900
+ export declare function useDataTable<TRow extends Record<string, unknown>, TFieldName extends string = string, TFilter extends Filter<TFieldName> = Filter<TFieldName>>(options: UseDataTableOptions<TRow, TFieldName, TFilter>): UseDataTableReturn<TRow>;
4901
+
4902
+ /**
4903
+ * Hook to access the full DataTable state (rows, columns, sorting, pagination,
4904
+ * column visibility, and row operations) from the nearest `DataTable.Root`.
4905
+ *
4906
+ * This hook is intentionally part of the public API to support **custom
4907
+ * sub-components** rendered inside `DataTable.Root`. Use it when the built-in
4908
+ * `DataTable.*` sub-components (Table, Toolbar, Pagination, etc.) are not
4909
+ * sufficient and you need to build your own compound component that reads from
4910
+ * or writes to the same DataTable context — for example, a column-visibility
4911
+ * toggle panel or a custom row-action toolbar.
4912
+ *
4913
+ * For most use cases, prefer passing values from `useDataTable()` as props
4914
+ * rather than reaching into the context directly.
4915
+ *
4916
+ * @throws Error if used outside of `DataTable.Root`.
4917
+ */
4918
+ export declare function useDataTableContext<TRow extends Record<string, unknown>>(): DataTableContextValue<TRow>;
4919
+
4920
+ /**
4921
+ * Options for `useDataTable` hook.
4922
+ */
4923
+ export declare type UseDataTableOptions<TRow extends Record<string, unknown>, TFieldName extends string = string, TFilter extends Filter<TFieldName> = Filter<TFieldName>> = {
4924
+ /** Column definitions that describe what to render in each column. */
4925
+ columns: Column<TRow>[];
4926
+ /**
4927
+ * Fetched data to display. Pass `undefined` while loading; the table will
4928
+ * show a loading state automatically.
4929
+ */
4930
+ data: DataTableData<TRow> | undefined;
4931
+ /** When `true`, the table renders a loading indicator. */
4932
+ loading?: boolean;
4933
+ /** If set, an error message is rendered in the table body. */
4934
+ error?: Error | null;
4935
+ /**
4936
+ * Collection control returned by `useCollectionVariables()`. Required when
4937
+ * using `DataTable.Pagination` or `DataTable.Filters`.
4938
+ */
4939
+ control?: CollectionControl<TFieldName, TFilter>;
4940
+ /** Called when the user clicks a row. Adds a pointer cursor to rows. */
4941
+ onClickRow?: (row: TRow) => void;
4942
+ /**
4943
+ * Per-row action items rendered in a kebab-menu column on the right.
4944
+ * The column is omitted when this array is empty or not provided.
4945
+ */
4946
+ rowActions?: RowAction<TRow>[];
4947
+ /**
4948
+ * Called with the current array of selected row IDs whenever the selection
4949
+ * changes. Providing this prop enables the checkbox selection column.
4950
+ * Selection is ID-based (`row.id`) and persists across page changes.
4951
+ *
4952
+ * **Requirement:** Each row must have a string or number `id` field.
4953
+ * Rows without `id` are excluded from selection.
4954
+ *
4955
+ * **Note:** `selectAllRows` (triggered by the header checkbox) selects only
4956
+ * the rows on the **current page**, not all pages.
4957
+ */
4958
+ onSelectionChange?: (ids: string[]) => void;
4959
+ };
4960
+
4961
+ /**
4962
+ * Return type of `useDataTable` hook.
4963
+ */
4964
+ export declare interface UseDataTableReturn<TRow extends Record<string, unknown>> {
4965
+ rows: TRow[];
4966
+ loading: boolean;
4967
+ error: Error | null;
4968
+ sortStates: SortState[];
4969
+ onSort?: (field: string, direction?: "Asc" | "Desc") => void;
4970
+ pageInfo: PageInfo;
4971
+ total: number | null;
4972
+ totalPages: number | null;
4973
+ /** 1-based page counter, automatically kept in sync by navigation actions. */
4974
+ currentPage: number;
4975
+ /** Navigate to the next page. Mode-aware: pushes cursor in forward, pops stack in backward. */
4976
+ goToNextPage: (pageInfo: Pick<PageInfo, "endCursor">) => void;
4977
+ /** Navigate to the previous page. Mode-aware: pops stack in forward, pushes cursor in backward. */
4978
+ goToPrevPage: (pageInfo: Pick<PageInfo, "startCursor">) => void;
4979
+ /** Jump to the first page. */
4980
+ goToFirstPage: () => void;
4981
+ /** Jump to the last page. Only functional when `totalPages` is known. */
4982
+ goToLastPage: () => void;
4983
+ /** Change the page size and reset to the first page. */
4984
+ setPageSize: (size: number) => void;
4985
+ hasPrevPage: boolean;
4986
+ hasNextPage: boolean;
4987
+ columns: Column<TRow>[];
4988
+ visibleColumns: Column<TRow>[];
4989
+ toggleColumn: (fieldOrId: string) => void;
4990
+ showAllColumns: () => void;
4991
+ hideAllColumns: () => void;
4992
+ isColumnVisible: (fieldOrId: string) => boolean;
4993
+ /**
4994
+ * The resolved page size derived from the collection control.
4995
+ * `0` when no control (or `pageSize`) is configured.
4996
+ * Used by `DataTable.Table` to render the correct number of skeleton / ghost rows.
4997
+ */
4998
+ pageSize: number;
4999
+ /**
5000
+ * The collection control passed to `useDataTable`, stored here for
5001
+ * forwarding to `DataTable.Root`.
5002
+ *
5003
+ * **Note:** The type is widened to `CollectionControl` (i.e.
5004
+ * `CollectionControl<string>`) here — field-name and operator narrowing from
5005
+ * `useCollectionVariables({ tableMetadata })` is lost. For type-safe
5006
+ * `addFilter` calls, use the `control` returned directly by
5007
+ * `useCollectionVariables` rather than accessing it via this field.
5008
+ */
5009
+ control: CollectionControl | undefined;
5010
+ onClickRow?: (row: TRow) => void;
5011
+ rowActions?: RowAction<TRow>[];
5012
+ selectedIds: string[];
5013
+ isRowSelected: (row: TRow) => boolean;
5014
+ toggleRowSelection?: (row: TRow) => void;
5015
+ selectAllRows?: () => void;
5016
+ clearSelection?: () => void;
5017
+ isAllSelected: boolean;
5018
+ isIndeterminate: boolean;
5019
+ }
5020
+
4098
5021
  export { useLocation }
4099
5022
 
4100
5023
  export { useNavigate }