@tailor-platform/app-shell 0.36.0 → 1.0.0

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,122 @@ 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
+ cursor: string | null;
835
+ paginationDirection: "forward" | "backward";
836
+ nextPage: (cursor: string) => void;
837
+ prevPage: (cursor: string) => void;
838
+ resetPage: () => void;
839
+ goToFirstPage: () => void;
840
+ /**
841
+ * Navigate to the last page.
842
+ *
843
+ * Requests the last `pageSize` items by setting `paginationDirection` to
844
+ * `"backward"` with no cursor.
845
+ */
846
+ goToLastPage: () => void;
847
+ }
848
+
849
+ /**
850
+ * Provider that shares collection control state via React Context.
851
+ *
852
+ * > **Note:** `DataTable.Root` automatically wraps its children with this provider
853
+ * > when `control` is supplied to `useDataTable`. Use `CollectionControlProvider`
854
+ * > directly only when you need to share control state with components that live
855
+ * > **outside** of `DataTable.Root` (e.g. a sibling filter panel or pagination
856
+ * > widget). Wrapping `DataTable.Root` with an outer `CollectionControlProvider`
857
+ * > while also passing `control` to `useDataTable` results in a double-wrap: the
858
+ * > inner provider (created by `DataTable.Root`) will silently shadow the outer
859
+ * > one for all descendants inside the table.
860
+ *
861
+ * @example
862
+ * ```tsx
863
+ * const { variables, control } = useCollectionVariables({ params: { pageSize: 20 } });
864
+ *
865
+ * <CollectionControlProvider value={control}>
866
+ * <FilterPanel />
867
+ * <DataTable.Root>...</DataTable.Root>
868
+ * <Pagination />
869
+ * </CollectionControlProvider>
870
+ * ```
871
+ */
872
+ export declare function CollectionControlProvider({ value, children, }: {
873
+ value: CollectionControl;
874
+ children: ReactNode;
875
+ }): JSX.Element;
876
+
877
+ /**
878
+ * GraphQL collection result format (Tailor Platform standard).
879
+ */
880
+ export declare interface CollectionResult<T> {
881
+ edges: {
882
+ node: T;
883
+ }[];
884
+ pageInfo: PageInfo;
885
+ total?: number | null;
886
+ }
887
+
888
+ /**
889
+ * Collection variables split into explicit sub-properties.
890
+ */
891
+ export declare interface CollectionVariables {
892
+ query: Record<string, Record<string, unknown>> | undefined;
893
+ order: {
894
+ field: string;
895
+ direction: "Asc" | "Desc";
896
+ }[] | undefined;
897
+ pagination: PaginationVariables;
898
+ }
899
+
900
+ /**
901
+ * A column definition for DataTable.
902
+ */
903
+ export declare interface Column<TRow extends Record<string, unknown>> {
904
+ /** Column header text. Omit for action or icon-only columns. */
905
+ label?: string;
906
+ /** Renders the cell content for a given row. Required. */
907
+ render: (row: TRow) => ReactNode;
908
+ /**
909
+ * Stable identifier used for column visibility toggling and as the React key.
910
+ * Falls back to `label` when omitted. Set this explicitly when `label` is
911
+ * absent or not unique.
912
+ */
913
+ id?: string;
914
+ /** Fixed column width in pixels. When omitted the column sizes naturally. */
915
+ width?: number;
916
+ /**
917
+ * Extracts the raw value from a row for purposes such as sorting or
918
+ * clipboard copying. Not used for rendering — use `render` for that.
919
+ */
920
+ accessor?: (row: TRow) => unknown;
921
+ /**
922
+ * Sort configuration. When set, the column header becomes clickable and
923
+ * cycles through `Asc → Desc → undefined`.
924
+ * Use `fieldTypeToSortConfig` or `inferColumns` to derive this automatically.
925
+ */
926
+ sort?: SortConfig;
927
+ /**
928
+ * Filter configuration. When set, this column appears as an option in
929
+ * `DataTable.Filters`.
930
+ * Use `fieldTypeToFilterConfig` or `inferColumns` to derive this automatically.
931
+ */
932
+ filter?: FilterConfig;
933
+ }
934
+
807
935
  /**
808
936
  * Supported area values for Layout.Column
809
937
  */
@@ -814,6 +942,59 @@ declare type ColumnArea = "left" | "main" | "right";
814
942
  */
815
943
  declare type ColumnCount = 1 | 2 | 3;
816
944
 
945
+ /**
946
+ * Object returned by `createColumnHelper`.
947
+ */
948
+ declare interface ColumnHelper<TRow extends Record<string, unknown>> {
949
+ /**
950
+ * Define a column with an explicit render function.
951
+ *
952
+ * @example
953
+ * ```tsx
954
+ * column({ label: "Name", render: (row) => row.name })
955
+ * column({ label: "Actions", render: (row) => <button>Edit {row.name}</button> })
956
+ * ```
957
+ */
958
+ column: (options: Column<TRow>) => Column<TRow>;
959
+ /**
960
+ * Bind table metadata once and return a per-field column factory.
961
+ *
962
+ * The returned factory **throws at call time** if the given field name is not
963
+ * found in `tableMetadata`. This typically happens at module initialisation
964
+ * (i.e. when columns are defined outside a component), so it surfaces as an
965
+ * uncaught error rather than a render error. Field-name narrowing is only
966
+ * active when `tableMetadata` is typed from an `as const` source (e.g. a
967
+ * file generated by `appShellMetadataPlugin`).
968
+ *
969
+ * @throws {Error} If the field name is not found in the provided table metadata.
970
+ *
971
+ * @example
972
+ * ```tsx
973
+ * const infer = inferColumns(tableMetadata.order);
974
+ * const columns = [
975
+ * column(infer("title")),
976
+ * column({
977
+ * ...infer("status"),
978
+ * render: (row) => <StatusBadge value={row.status} />
979
+ * }),
980
+ * ];
981
+ * ```
982
+ */
983
+ inferColumns: <const TTable extends TableMetadata = TableMetadata>(tableMetadata: TTable) => ColumnInferFn<TRow, TTable>;
984
+ }
985
+
986
+ /**
987
+ * Per-field column factory returned by `inferColumns(tableMetadata)`.
988
+ *
989
+ * Derives label, sort config, and filter config from the field's metadata.
990
+ * Sort and filter can be suppressed per call via `options`.
991
+ *
992
+ * @param dataKey - A field name from the bound table metadata.
993
+ * @param options - Optional per-column overrides (label, width, sort, filter).
994
+ * @throws {Error} If `dataKey` is not found in the metadata passed to `inferColumns`.
995
+ */
996
+ declare type ColumnInferFn<TRow extends Record<string, unknown>, TTable extends TableMetadata = TableMetadata> = (dataKey: TableFieldName<TTable>, options?: MetadataFieldOptions) => Column<TRow>;
997
+
817
998
  /**
818
999
  * Props for individual Layout.Column component
819
1000
  */
@@ -1274,6 +1455,23 @@ declare interface CreatableProps<T extends object> {
1274
1455
  */
1275
1456
  export declare function createAuthClient(config: AuthClientConfig): EnhancedAuthClient;
1276
1457
 
1458
+ /**
1459
+ * Factory that captures the row type once and returns `column` and `inferColumns`
1460
+ * with `TRow` already bound.
1461
+ *
1462
+ * @example
1463
+ * ```tsx
1464
+ * const { column, inferColumns } = createColumnHelper<Order>();
1465
+ *
1466
+ * const infer = inferColumns(tableMetadata.order);
1467
+ * const columns = [
1468
+ * column(infer("title")),
1469
+ * column({ label: "Actions", render: (row) => <button>Edit {row.name}</button> }),
1470
+ * ];
1471
+ * ```
1472
+ */
1473
+ export declare function createColumnHelper<TRow extends Record<string, unknown>>(): ColumnHelper<TRow>;
1474
+
1277
1475
  /**
1278
1476
  * Create a type-safe path builder from route definitions.
1279
1477
  *
@@ -1430,6 +1628,173 @@ export declare type CsvSchema = {
1430
1628
  columns: readonly CsvColumn[];
1431
1629
  };
1432
1630
 
1631
+ export declare const DataTable: {
1632
+ /**
1633
+ * Context/provider root. Wraps all other `DataTable.*` components and
1634
+ * wires state from `useDataTable()` into context.
1635
+ */
1636
+ readonly Root: typeof DataTableRoot;
1637
+ /**
1638
+ * Container for toolbar content (column visibility, search, etc.).
1639
+ * Place inside `DataTable.Root`, before `DataTable.Table`.
1640
+ */
1641
+ readonly Toolbar: typeof DataTableToolbar;
1642
+ /**
1643
+ * Auto-generated filter chips from column filter configs.
1644
+ *
1645
+ * **Requires `control`** — `useDataTable()` must receive `control` from
1646
+ * `useCollectionVariables()`, otherwise this component throws at render time.
1647
+ */
1648
+ readonly Filters: typeof DataTableFilters;
1649
+ /**
1650
+ * Renders `<table>` with built-in `Headers` and `Body`.
1651
+ * Place inside `DataTable.Root`.
1652
+ */
1653
+ readonly Table: typeof DataTableTable;
1654
+ /**
1655
+ * Footer container for pagination and other footer content.
1656
+ * Place inside `DataTable.Root`, after `DataTable.Table`.
1657
+ */
1658
+ readonly Footer: typeof DataTableFooter;
1659
+ /**
1660
+ * Pre-built pagination controls. Place inside `DataTable.Footer`.
1661
+ *
1662
+ * **Requires `control`** — `useDataTable()` must receive `control` from
1663
+ * `useCollectionVariables()`, otherwise this component throws at render time.
1664
+ *
1665
+ * **Go-to-first / go-to-last buttons** — Rendered only when `totalPages` is
1666
+ * non-null (i.e. the backend returns a total count). When `totalPages` is
1667
+ * `null`, these buttons and the page counter are omitted.
1668
+ */
1669
+ readonly Pagination: typeof DataTablePagination;
1670
+ };
1671
+
1672
+ /**
1673
+ * Context value provided by `DataTable.Root`.
1674
+ *
1675
+ * `control` is intentionally excluded from this context: collection control
1676
+ * (filters, sort, and pagination) is managed by `CollectionControlProvider`
1677
+ * via a separate context. Keeping them separate means `DataTable.Root` works
1678
+ * without any collection control (read-only / static tables), while
1679
+ * `CollectionControlProvider` can optionally wrap it when filtering and
1680
+ * pagination are needed. This also avoids coupling `DataTableContextValue`
1681
+ * to `UseDataTableReturn` — changes to `UseDataTableReturn` require an
1682
+ * explicit decision about whether they should appear in the context.
1683
+ */
1684
+ export declare interface DataTableContextValue<TRow extends Record<string, unknown>> {
1685
+ rows: TRow[];
1686
+ loading: boolean;
1687
+ error: Error | null;
1688
+ sortStates: SortState[];
1689
+ onSort?: (field: string, direction?: "Asc" | "Desc") => void;
1690
+ pageInfo: PageInfo;
1691
+ total: number | null;
1692
+ totalPages: number | null;
1693
+ nextPage: (cursor: string) => void;
1694
+ prevPage: (cursor: string) => void;
1695
+ hasPrevPage: boolean;
1696
+ hasNextPage: boolean;
1697
+ /**
1698
+ * The resolved page size from the collection control. `0` when no control is
1699
+ * configured. Used to render the correct number of skeleton / ghost rows.
1700
+ */
1701
+ pageSize: number;
1702
+ columns: Column<TRow>[];
1703
+ visibleColumns: Column<TRow>[];
1704
+ toggleColumn: (fieldOrId: string) => void;
1705
+ showAllColumns: () => void;
1706
+ hideAllColumns: () => void;
1707
+ isColumnVisible: (fieldOrId: string) => boolean;
1708
+ onClickRow?: (row: TRow) => void;
1709
+ rowActions?: RowAction<TRow>[];
1710
+ selectedIds: string[];
1711
+ isRowSelected: (row: TRow) => boolean;
1712
+ toggleRowSelection?: (row: TRow) => void;
1713
+ /**
1714
+ * Selects all rows on the **current page** only. Cross-page selection is not supported.
1715
+ * Undefined when `onSelectionChange` is not provided.
1716
+ */
1717
+ selectAllRows?: () => void;
1718
+ clearSelection?: () => void;
1719
+ isAllSelected: boolean;
1720
+ isIndeterminate: boolean;
1721
+ }
1722
+
1723
+ /**
1724
+ * Data input for `useDataTable` hook.
1725
+ */
1726
+ export declare interface DataTableData<TRow> {
1727
+ rows: TRow[];
1728
+ pageInfo?: PageInfo;
1729
+ total?: number | null;
1730
+ }
1731
+
1732
+ /** Use `DataTable.Filters` instead of calling this directly. */
1733
+ declare function DataTableFilters({ className }: {
1734
+ className?: string;
1735
+ }): JSX.Element | null;
1736
+
1737
+ declare namespace DataTableFilters {
1738
+ var displayName: string;
1739
+ }
1740
+
1741
+ /** Use `DataTable.Footer` instead of calling this directly. */
1742
+ declare function DataTableFooter({ children, className }: {
1743
+ children: ReactNode;
1744
+ className?: string;
1745
+ }): JSX.Element;
1746
+
1747
+ declare namespace DataTableFooter {
1748
+ var displayName: string;
1749
+ }
1750
+
1751
+ /** Use `DataTable.Pagination` instead of calling this directly. */
1752
+ declare function DataTablePagination({ pageSizeOptions }?: DataTablePaginationProps): JSX.Element;
1753
+
1754
+ declare namespace DataTablePagination {
1755
+ var displayName: string;
1756
+ }
1757
+
1758
+ export declare interface DataTablePaginationProps {
1759
+ /**
1760
+ * Available page-size options shown in a dropdown selector.
1761
+ * When provided, a page-size switcher is rendered.
1762
+ */
1763
+ pageSizeOptions?: number[];
1764
+ }
1765
+
1766
+ /** Use `DataTable.Root` instead of calling this directly. */
1767
+ declare function DataTableRoot<TRow extends Record<string, unknown>>({ value, children, className, }: DataTableRootProps<TRow>): JSX.Element;
1768
+
1769
+ declare namespace DataTableRoot {
1770
+ var displayName: string;
1771
+ }
1772
+
1773
+ export declare interface DataTableRootProps<TRow extends Record<string, unknown>> {
1774
+ value: UseDataTableReturn<TRow>;
1775
+ children: ReactNode;
1776
+ className?: string;
1777
+ }
1778
+
1779
+ /** Use `DataTable.Table` instead of calling this directly. */
1780
+ declare function DataTableTable({ className }: {
1781
+ className?: string;
1782
+ }): JSX.Element;
1783
+
1784
+ declare namespace DataTableTable {
1785
+ var displayName: string;
1786
+ }
1787
+
1788
+ /** Use `DataTable.Toolbar` instead of calling this directly. */
1789
+ declare function DataTableToolbar({ children, className }: {
1790
+ children: ReactNode;
1791
+ className?: string;
1792
+ }): JSX.Element;
1793
+
1794
+ declare namespace DataTableToolbar {
1795
+ var displayName: string;
1796
+ }
1797
+
1433
1798
  /**
1434
1799
  * Date format options
1435
1800
  */
@@ -1948,7 +2313,7 @@ declare type FieldConfig = FieldDivider | FieldDefinition;
1948
2313
  */
1949
2314
  declare interface FieldDefinition {
1950
2315
  /** Field type determines rendering (defaults to "text") */
1951
- type?: FieldType;
2316
+ type?: FieldType_2;
1952
2317
  /** Path to the value in the data object (supports dot notation) */
1953
2318
  key: string;
1954
2319
  /** Display label for the field */
@@ -1992,6 +2357,22 @@ declare interface FieldMeta {
1992
2357
  truncateLines?: number;
1993
2358
  }
1994
2359
 
2360
+ /**
2361
+ * Metadata for a single field.
2362
+ */
2363
+ export declare interface FieldMetadata {
2364
+ readonly name: string;
2365
+ readonly type: FieldType;
2366
+ readonly required: boolean;
2367
+ readonly enumValues?: readonly string[];
2368
+ readonly arrayItemType?: FieldType;
2369
+ readonly description?: string;
2370
+ readonly relation?: {
2371
+ readonly fieldName: string;
2372
+ readonly targetTable: string;
2373
+ };
2374
+ }
2375
+
1995
2376
  declare type FieldRootProps = Pick<React_2.ComponentProps<typeof Field_2.Root>, "name" | "disabled" | "validate" | "validationMode" | "validationDebounceTime" | "className" | "style"> & {
1996
2377
  children: React_2.ReactNode;
1997
2378
  /** Whether the field has been blurred. Maps to Base UI's `touched`. */
@@ -2036,10 +2417,101 @@ declare type FieldsetRootProps = Pick<React_2.ComponentProps<typeof Fieldset_2.R
2036
2417
  children: React_2.ReactNode;
2037
2418
  };
2038
2419
 
2420
+ /**
2421
+ * Field type mapping for Data View.
2422
+ */
2423
+ export declare type FieldType = "string" | "number" | "boolean" | "uuid" | "datetime" | "date" | "time" | "enum" | "array" | "nested" | "file";
2424
+
2039
2425
  /**
2040
2426
  * Supported field types for DescriptionCard
2041
2427
  */
2042
- declare type FieldType = "text" | "badge" | "money" | "date" | "link" | "address" | "reference";
2428
+ declare type FieldType_2 = "text" | "badge" | "money" | "date" | "link" | "address" | "reference";
2429
+
2430
+ /**
2431
+ * Map metadata field type to FilterConfig.
2432
+ * Returns undefined for types that don't support filtering.
2433
+ */
2434
+ export declare function fieldTypeToFilterConfig(field: string, type: FieldType, enumValues?: readonly string[]): FilterConfig | undefined;
2435
+
2436
+ /**
2437
+ * Type-level mapping from `FieldType` (metadata) to `FilterConfig["type"]`.
2438
+ */
2439
+ declare type FieldTypeToFilterConfigType = {
2440
+ string: "string";
2441
+ number: "number";
2442
+ boolean: "boolean";
2443
+ uuid: "uuid";
2444
+ datetime: "date";
2445
+ date: "date";
2446
+ time: "date";
2447
+ enum: "enum";
2448
+ array: never;
2449
+ nested: never;
2450
+ file: never;
2451
+ };
2452
+
2453
+ /**
2454
+ * Map metadata field type to SortConfig.
2455
+ * Returns undefined for types that don't support sorting.
2456
+ */
2457
+ export declare function fieldTypeToSortConfig(field: string, type: FieldType): SortConfig | undefined;
2458
+
2459
+ declare type FieldTypeToTSType = {
2460
+ string: string;
2461
+ number: number;
2462
+ boolean: boolean;
2463
+ uuid: string;
2464
+ datetime: string;
2465
+ date: string;
2466
+ time: string;
2467
+ enum: string;
2468
+ };
2469
+
2470
+ /**
2471
+ * Active filter state.
2472
+ *
2473
+ * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
2474
+ */
2475
+ export declare interface Filter<TFieldName extends string = string> {
2476
+ field: TFieldName;
2477
+ operator: FilterOperator;
2478
+ value: unknown;
2479
+ }
2480
+
2481
+ /**
2482
+ * Filter configuration for a column.
2483
+ * The `type` determines which operators are available.
2484
+ * The `field` identifies the backend field name used for filtering.
2485
+ */
2486
+ export declare type FilterConfig = {
2487
+ field: string;
2488
+ type: "string";
2489
+ } | {
2490
+ field: string;
2491
+ type: "number";
2492
+ } | {
2493
+ field: string;
2494
+ type: "date";
2495
+ } | {
2496
+ field: string;
2497
+ type: "enum";
2498
+ options: SelectOption[];
2499
+ } | {
2500
+ field: string;
2501
+ type: "boolean";
2502
+ } | {
2503
+ field: string;
2504
+ type: "uuid";
2505
+ };
2506
+
2507
+ declare type FilterInputForFieldType<TFilterConfigType extends FilterConfig["type"], TValue> = {
2508
+ [Op in OperatorForFilterType[TFilterConfigType]]?: OperatorValueType<Op, TValue>;
2509
+ };
2510
+
2511
+ /**
2512
+ * Union of all available filter operators.
2513
+ */
2514
+ export declare type FilterOperator = OperatorForFilterType[FilterConfig["type"]];
2043
2515
 
2044
2516
  /** The table footer section (`<tfoot>`). */
2045
2517
  declare function Footer({ className, ...props }: React_2.ComponentProps<"tfoot">): JSX.Element;
@@ -2552,6 +3024,26 @@ declare namespace MenuSeparator {
2552
3024
  var displayName: string;
2553
3025
  }
2554
3026
 
3027
+ /**
3028
+ * Options for metadata-based single field inference.
3029
+ */
3030
+ export declare interface MetadataFieldOptions {
3031
+ /** Override the column header text. Defaults to the field's `description` or `name` from metadata. */
3032
+ label?: string;
3033
+ /** Fixed column width in pixels. When omitted the column sizes naturally. */
3034
+ width?: number;
3035
+ /**
3036
+ * Set to `false` to suppress the auto-generated sort config for this field.
3037
+ * Defaults to `true` (sort is enabled when the field type supports it).
3038
+ */
3039
+ sort?: boolean;
3040
+ /**
3041
+ * Set to `false` to suppress the auto-generated filter config for this field.
3042
+ * Defaults to `true` (filter is enabled when the field type supports it).
3043
+ */
3044
+ filter?: boolean;
3045
+ }
3046
+
2555
3047
  /**
2556
3048
  * MetricCard — Compact card for dashboard KPI display (title, value, optional trend and description).
2557
3049
  *
@@ -2621,6 +3113,54 @@ declare type Module = Omit<CommonPageResource, "meta"> & {
2621
3113
 
2622
3114
  declare type Modules = Array<Module>;
2623
3115
 
3116
+ /**
3117
+ * Extract the node type from a cursor connection result.
3118
+ */
3119
+ export declare type NodeType<T extends {
3120
+ edges: {
3121
+ node: unknown;
3122
+ }[];
3123
+ } | null | undefined> = NonNullable<T>["edges"][number]["node"];
3124
+
3125
+ /**
3126
+ * Resolve the operator union for a specific field within a filter type.
3127
+ */
3128
+ declare type OperatorForField<TFilter, F extends string> = [Extract<TFilter, {
3129
+ field: F;
3130
+ }>] extends [
3131
+ never
3132
+ ] ? FilterOperator : Extract<TFilter, {
3133
+ field: F;
3134
+ }> extends {
3135
+ operator: infer O;
3136
+ } ? O : FilterOperator;
3137
+
3138
+ /**
3139
+ * Maps each filter type to the union of operators it supports.
3140
+ */
3141
+ declare type OperatorForFilterType = {
3142
+ [T in FilterConfig["type"]]: (typeof OPERATORS_BY_FILTER_TYPE)[T][number];
3143
+ };
3144
+
3145
+ /**
3146
+ * Operators available per filter type (Tailor Platform schema).
3147
+ */
3148
+ export declare const OPERATORS_BY_FILTER_TYPE: {
3149
+ readonly string: readonly ["eq", "ne", "contains", "notContains", "hasPrefix", "hasSuffix", "notHasPrefix", "notHasSuffix", "in", "nin"];
3150
+ readonly number: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "between", "in", "nin"];
3151
+ readonly date: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "between", "in", "nin"];
3152
+ readonly enum: readonly ["eq", "ne", "in", "nin"];
3153
+ readonly boolean: readonly ["eq", "ne"];
3154
+ readonly uuid: readonly ["eq", "ne", "in", "nin"];
3155
+ };
3156
+
3157
+ declare type OperatorValueType<TOp extends string, TValue> = TOp extends "in" | "nin" ? TValue[] : TOp extends "between" ? {
3158
+ min: TValue;
3159
+ max: TValue;
3160
+ } : TValue;
3161
+
3162
+ declare type OrderableFieldType = "string" | "number" | "boolean" | "datetime" | "date" | "time" | "enum";
3163
+
2624
3164
  /**
2625
3165
  * A React component that can be used as a page in file-based routing.
2626
3166
  *
@@ -2645,11 +3185,31 @@ declare type PageEntry = {
2645
3185
  component: PageComponent;
2646
3186
  };
2647
3187
 
3188
+ /**
3189
+ * Page info for cursor-based pagination (Relay Connection spec).
3190
+ */
3191
+ export declare interface PageInfo {
3192
+ hasNextPage: boolean;
3193
+ hasPreviousPage: boolean;
3194
+ endCursor: string | null;
3195
+ startCursor: string | null;
3196
+ }
3197
+
2648
3198
  export declare type PageMeta = {
2649
3199
  title: string;
2650
3200
  icon?: ReactNode;
2651
3201
  };
2652
3202
 
3203
+ /**
3204
+ * Pagination variables for cursor-based pagination (Tailor Platform format).
3205
+ */
3206
+ export declare interface PaginationVariables {
3207
+ first?: number;
3208
+ after?: string | null;
3209
+ last?: number;
3210
+ before?: string | null;
3211
+ }
3212
+
2653
3213
  /** The row data passed to `onValidate`, after transforms have been applied. */
2654
3214
  export declare type ParsedRow = {
2655
3215
  /** 0-based row index, stable across edits. */
@@ -2724,6 +3284,10 @@ declare namespace RadioItemIndicator {
2724
3284
  */
2725
3285
  export declare const redirectTo: (to: string) => GuardResult;
2726
3286
 
3287
+ declare type ResolveFieldValueType<F, T extends keyof FieldTypeToTSType> = T extends "enum" ? F extends {
3288
+ readonly enumValues: readonly (infer V extends string)[];
3289
+ } ? V : string : FieldTypeToTSType[T];
3290
+
2727
3291
  /**
2728
3292
  * A resource that can be included in the sub-content in the root resource.
2729
3293
  *
@@ -3001,6 +3565,18 @@ declare namespace Row {
3001
3565
  var displayName: string;
3002
3566
  }
3003
3567
 
3568
+ /**
3569
+ * A single row action definition for the actions column.
3570
+ */
3571
+ export declare interface RowAction<TRow extends Record<string, unknown>> {
3572
+ id: string;
3573
+ label: string;
3574
+ icon?: ReactNode;
3575
+ variant?: "default" | "destructive";
3576
+ isDisabled?: (row: TRow) => boolean;
3577
+ onClick: (row: TRow) => void;
3578
+ }
3579
+
3004
3580
  /**
3005
3581
  * An async search source that can be plugged into the CommandPalette.
3006
3582
  *
@@ -3099,6 +3675,14 @@ declare namespace SelectItem {
3099
3675
  var displayName: string;
3100
3676
  }
3101
3677
 
3678
+ /**
3679
+ * Select option for enum/dropdown filters.
3680
+ */
3681
+ export declare interface SelectOption {
3682
+ value: string;
3683
+ label: string;
3684
+ }
3685
+
3102
3686
  declare interface SelectPropsBase<T> {
3103
3687
  /** Placeholder text shown when no value is selected */
3104
3688
  placeholder?: string;
@@ -3519,6 +4103,33 @@ export declare type SidebarLayoutProps = {
3519
4103
  */
3520
4104
  export declare const SidebarSeparator: () => JSX.Element;
3521
4105
 
4106
+ /**
4107
+ * Sort configuration for a column.
4108
+ * The `type` determines how values are compared when sorting.
4109
+ * The `field` identifies the backend field name used for ordering.
4110
+ */
4111
+ export declare type SortConfig = {
4112
+ field: string;
4113
+ type: "string";
4114
+ } | {
4115
+ field: string;
4116
+ type: "number";
4117
+ } | {
4118
+ field: string;
4119
+ type: "date";
4120
+ } | {
4121
+ field: string;
4122
+ type: "boolean";
4123
+ };
4124
+
4125
+ /**
4126
+ * Active sort state for a single field.
4127
+ */
4128
+ export declare interface SortState {
4129
+ field: string;
4130
+ direction: "Asc" | "Desc";
4131
+ }
4132
+
3522
4133
  /** Groups all parts of a submenu. */
3523
4134
  declare function SubmenuRoot({ children, ...props }: SubmenuRootProps): JSX.Element;
3524
4135
 
@@ -3548,6 +4159,57 @@ export declare const Table: {
3548
4159
  Caption: typeof Caption;
3549
4160
  };
3550
4161
 
4162
+ /**
4163
+ * Extract field names from a single table metadata object.
4164
+ */
4165
+ export declare type TableFieldName<TTable extends TableMetadata> = TTable["fields"][number] extends {
4166
+ readonly name: infer N;
4167
+ } ? N extends string ? N : never : never;
4168
+
4169
+ /**
4170
+ * Metadata for a single table.
4171
+ */
4172
+ export declare interface TableMetadata {
4173
+ readonly name: string;
4174
+ readonly pluralForm: string;
4175
+ readonly description?: string;
4176
+ readonly fields: readonly FieldMetadata[];
4177
+ readonly relations?: readonly {
4178
+ readonly relationType: "manyToOne" | "oneToOne" | "oneToMany";
4179
+ readonly fieldName: string;
4180
+ readonly targetTable: string;
4181
+ readonly foreignKeyField: string;
4182
+ }[];
4183
+ }
4184
+
4185
+ /**
4186
+ * Metadata-aware filter type from a single table metadata object.
4187
+ */
4188
+ export declare type TableMetadataFilter<TTable extends TableMetadata> = TTable["fields"][number] extends infer F ? F extends {
4189
+ readonly name: infer N;
4190
+ readonly type: infer T;
4191
+ } ? N extends string ? T extends keyof FieldTypeToFilterConfigType ? FieldTypeToFilterConfigType[T] extends never ? never : {
4192
+ field: N;
4193
+ operator: OperatorForFilterType[FieldTypeToFilterConfigType[T]];
4194
+ value: unknown;
4195
+ } : never : never : never : never;
4196
+
4197
+ /**
4198
+ * Map of all tables.
4199
+ */
4200
+ export declare type TableMetadataMap = {
4201
+ readonly [key: string]: TableMetadata;
4202
+ };
4203
+
4204
+ /**
4205
+ * Extract only orderable field names from a single table metadata object.
4206
+ */
4207
+ export declare type TableOrderableFieldName<TTable extends TableMetadata> = Extract<TTable["fields"][number], {
4208
+ readonly type: OrderableFieldType;
4209
+ }> extends {
4210
+ readonly name: infer N;
4211
+ } ? N extends string ? N : never : never;
4212
+
3551
4213
  declare type Theme = "dark" | "light" | "system";
3552
4214
 
3553
4215
  declare type ThemeProviderState = {
@@ -3936,6 +4598,90 @@ export declare const useAuthSuspense: () => {
3936
4598
  checkAuthStatus: () => Promise<AuthState>;
3937
4599
  };
3938
4600
 
4601
+ /**
4602
+ * Hook to access collection control from the nearest `CollectionControlProvider`.
4603
+ *
4604
+ * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
4605
+ *
4606
+ * @throws Error if used outside of `CollectionControlProvider`.
4607
+ */
4608
+ export declare function useCollectionControl<TFieldName extends string = string>(): CollectionControl<TFieldName>;
4609
+
4610
+ /**
4611
+ * Options for `useCollectionVariables` hook.
4612
+ */
4613
+ export declare interface UseCollectionOptions<TFieldName extends string = string, TFilter extends Filter<TFieldName> = Filter<TFieldName>> {
4614
+ params?: {
4615
+ initialFilters?: TFilter[];
4616
+ initialSort?: {
4617
+ field: TFieldName;
4618
+ direction: "Asc" | "Desc";
4619
+ }[];
4620
+ pageSize?: number;
4621
+ };
4622
+ }
4623
+
4624
+ /**
4625
+ * Return type of `useCollectionVariables` hook.
4626
+ */
4627
+ export declare interface UseCollectionReturn<TFieldName extends string = string, TVariables = CollectionVariables, TFilter = Filter<TFieldName>> {
4628
+ variables: TVariables;
4629
+ control: CollectionControl<TFieldName, TFilter>;
4630
+ }
4631
+
4632
+ /**
4633
+ * Hook for managing collection query parameters (filters, sort, pagination)
4634
+ * with metadata-based field name typing and automatic `fieldType` detection.
4635
+ *
4636
+ * Returns `variables` with `query`, `order`, and `pagination` sub-properties
4637
+ * that can be mapped to GraphQL query variables.
4638
+ *
4639
+ * @example
4640
+ * ```tsx
4641
+ * import { tableMetadata } from "./generated/data-viewer-metadata.generated";
4642
+ *
4643
+ * const { variables } = useCollectionVariables({
4644
+ * tableMetadata: tableMetadata.task,
4645
+ * params: { pageSize: 20 },
4646
+ * });
4647
+ * const { query, order, pagination } = variables;
4648
+ * const [result] = useQuery({
4649
+ * query: GET_TASKS,
4650
+ * variables: { ...pagination, query, order },
4651
+ * });
4652
+ * ```
4653
+ */
4654
+ export declare function useCollectionVariables<const TTable extends TableMetadata>(options: UseCollectionOptions<TableFieldName<TTable>, TableMetadataFilter<TTable>> & {
4655
+ tableMetadata: TTable;
4656
+ }): UseCollectionReturn<TableFieldName<TTable>, {
4657
+ query: BuildQueryVariables<TTable> | undefined;
4658
+ order: {
4659
+ field: TableOrderableFieldName<TTable>;
4660
+ direction: "Asc" | "Desc";
4661
+ }[] | undefined;
4662
+ pagination: PaginationVariables;
4663
+ }, TableMetadataFilter<TTable>>;
4664
+
4665
+ /**
4666
+ * Hook for managing collection query parameters (filters, sort, pagination).
4667
+ *
4668
+ * Returns `variables` with `query`, `order`, and `pagination` sub-properties
4669
+ * that can be mapped to GraphQL query variables.
4670
+ *
4671
+ * @example
4672
+ * ```tsx
4673
+ * const { variables } = useCollectionVariables({ params: { pageSize: 20 } });
4674
+ * const { query, order, pagination } = variables;
4675
+ * const [result] = useQuery({
4676
+ * query: GET_ORDERS,
4677
+ * variables: { ...pagination, query, order },
4678
+ * });
4679
+ * ```
4680
+ */
4681
+ export declare function useCollectionVariables(options: UseCollectionOptions & {
4682
+ tableMetadata?: never;
4683
+ }): UseCollectionReturn<string, CollectionVariables>;
4684
+
3939
4685
  /**
3940
4686
  * Hook that encapsulates the "creatable" combobox pattern — allowing users
3941
4687
  * to create new items on-the-fly from the dropdown.
@@ -4095,6 +4841,148 @@ declare type UseCsvImporterOptions<T extends CsvSchema> = {
4095
4841
  maxFileSize?: number;
4096
4842
  };
4097
4843
 
4844
+ /**
4845
+ * Hook that integrates data management, column visibility, row operations, and
4846
+ * sort/pagination state for the `DataTable.*` compound component.
4847
+ *
4848
+ * @example
4849
+ * ```tsx
4850
+ * const { variables, control } = useCollectionVariables({ params: { pageSize: 20 } });
4851
+ * const [result] = useQuery({
4852
+ * query: GET_ORDERS,
4853
+ * variables: { ...variables.pagination, query: variables.query, order: variables.order },
4854
+ * });
4855
+ *
4856
+ * const table = useDataTable<Order>({
4857
+ * columns,
4858
+ * data: {
4859
+ * rows: result.data?.orders?.edges.map(e => e.node) ?? [],
4860
+ * pageInfo: { ... },
4861
+ * total: result.data?.orders?.total,
4862
+ * },
4863
+ * loading: result.fetching,
4864
+ * control,
4865
+ * });
4866
+ *
4867
+ * <DataTable.Root value={table}>
4868
+ * <DataTable.Table />
4869
+ * <DataTable.Footer>
4870
+ * <DataTable.Pagination />
4871
+ * </DataTable.Footer>
4872
+ * </DataTable.Root>
4873
+ * ```
4874
+ */
4875
+ 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>;
4876
+
4877
+ /**
4878
+ * Hook to access the full DataTable state (rows, columns, sorting, pagination,
4879
+ * column visibility, and row operations) from the nearest `DataTable.Root`.
4880
+ *
4881
+ * This hook is intentionally part of the public API to support **custom
4882
+ * sub-components** rendered inside `DataTable.Root`. Use it when the built-in
4883
+ * `DataTable.*` sub-components (Table, Toolbar, Pagination, etc.) are not
4884
+ * sufficient and you need to build your own compound component that reads from
4885
+ * or writes to the same DataTable context — for example, a column-visibility
4886
+ * toggle panel or a custom row-action toolbar.
4887
+ *
4888
+ * For most use cases, prefer passing values from `useDataTable()` as props
4889
+ * rather than reaching into the context directly.
4890
+ *
4891
+ * @throws Error if used outside of `DataTable.Root`.
4892
+ */
4893
+ export declare function useDataTableContext<TRow extends Record<string, unknown>>(): DataTableContextValue<TRow>;
4894
+
4895
+ /**
4896
+ * Options for `useDataTable` hook.
4897
+ */
4898
+ export declare type UseDataTableOptions<TRow extends Record<string, unknown>, TFieldName extends string = string, TFilter extends Filter<TFieldName> = Filter<TFieldName>> = {
4899
+ /** Column definitions that describe what to render in each column. */
4900
+ columns: Column<TRow>[];
4901
+ /**
4902
+ * Fetched data to display. Pass `undefined` while loading; the table will
4903
+ * show a loading state automatically.
4904
+ */
4905
+ data: DataTableData<TRow> | undefined;
4906
+ /** When `true`, the table renders a loading indicator. */
4907
+ loading?: boolean;
4908
+ /** If set, an error message is rendered in the table body. */
4909
+ error?: Error | null;
4910
+ /**
4911
+ * Collection control returned by `useCollectionVariables()`. Required when
4912
+ * using `DataTable.Pagination` or `DataTable.Filters`.
4913
+ */
4914
+ control?: CollectionControl<TFieldName, TFilter>;
4915
+ /** Called when the user clicks a row. Adds a pointer cursor to rows. */
4916
+ onClickRow?: (row: TRow) => void;
4917
+ /**
4918
+ * Per-row action items rendered in a kebab-menu column on the right.
4919
+ * The column is omitted when this array is empty or not provided.
4920
+ */
4921
+ rowActions?: RowAction<TRow>[];
4922
+ /**
4923
+ * Called with the current array of selected row IDs whenever the selection
4924
+ * changes. Providing this prop enables the checkbox selection column.
4925
+ * Selection is ID-based (`row.id`) and persists across page changes.
4926
+ *
4927
+ * **Requirement:** Each row must have a string or number `id` field.
4928
+ * Rows without `id` are excluded from selection.
4929
+ *
4930
+ * **Note:** `selectAllRows` (triggered by the header checkbox) selects only
4931
+ * the rows on the **current page**, not all pages.
4932
+ */
4933
+ onSelectionChange?: (ids: string[]) => void;
4934
+ };
4935
+
4936
+ /**
4937
+ * Return type of `useDataTable` hook.
4938
+ */
4939
+ export declare interface UseDataTableReturn<TRow extends Record<string, unknown>> {
4940
+ rows: TRow[];
4941
+ loading: boolean;
4942
+ error: Error | null;
4943
+ sortStates: SortState[];
4944
+ onSort?: (field: string, direction?: "Asc" | "Desc") => void;
4945
+ pageInfo: PageInfo;
4946
+ total: number | null;
4947
+ totalPages: number | null;
4948
+ nextPage: (cursor: string) => void;
4949
+ prevPage: (cursor: string) => void;
4950
+ hasPrevPage: boolean;
4951
+ hasNextPage: boolean;
4952
+ columns: Column<TRow>[];
4953
+ visibleColumns: Column<TRow>[];
4954
+ toggleColumn: (fieldOrId: string) => void;
4955
+ showAllColumns: () => void;
4956
+ hideAllColumns: () => void;
4957
+ isColumnVisible: (fieldOrId: string) => boolean;
4958
+ /**
4959
+ * The resolved page size derived from the collection control.
4960
+ * `0` when no control (or `pageSize`) is configured.
4961
+ * Used by `DataTable.Table` to render the correct number of skeleton / ghost rows.
4962
+ */
4963
+ pageSize: number;
4964
+ /**
4965
+ * The collection control passed to `useDataTable`, stored here for
4966
+ * forwarding to `DataTable.Root`.
4967
+ *
4968
+ * **Note:** The type is widened to `CollectionControl` (i.e.
4969
+ * `CollectionControl<string>`) here — field-name and operator narrowing from
4970
+ * `useCollectionVariables({ tableMetadata })` is lost. For type-safe
4971
+ * `addFilter` calls, use the `control` returned directly by
4972
+ * `useCollectionVariables` rather than accessing it via this field.
4973
+ */
4974
+ control: CollectionControl | undefined;
4975
+ onClickRow?: (row: TRow) => void;
4976
+ rowActions?: RowAction<TRow>[];
4977
+ selectedIds: string[];
4978
+ isRowSelected: (row: TRow) => boolean;
4979
+ toggleRowSelection?: (row: TRow) => void;
4980
+ selectAllRows?: () => void;
4981
+ clearSelection?: () => void;
4982
+ isAllSelected: boolean;
4983
+ isIndeterminate: boolean;
4984
+ }
4985
+
4098
4986
  export { useLocation }
4099
4987
 
4100
4988
  export { useNavigate }