basalt-ui 1.23.1 → 1.25.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.
Files changed (63) hide show
  1. package/AGENTS.md +11 -6
  2. package/CHANGELOG.md +21 -0
  3. package/MIGRATING.md +138 -4
  4. package/README.md +123 -32
  5. package/agent/rules/basalt-data.md +104 -12
  6. package/agent/rules/basalt-query.md +57 -0
  7. package/agent/skills/basalt-app/SKILL.md +7 -0
  8. package/dist/cli/index.d.ts +4 -2
  9. package/dist/cli/index.d.ts.map +1 -1
  10. package/dist/cli/index.js +101 -17
  11. package/dist/dashboard/empty-state.d.ts +6 -2
  12. package/dist/dashboard/empty-state.d.ts.map +1 -1
  13. package/dist/dashboard/empty-state.js +1 -1
  14. package/dist/dashboard/index.d.ts +1 -0
  15. package/dist/dashboard/index.d.ts.map +1 -1
  16. package/dist/dashboard/index.js +8 -0
  17. package/dist/dashboard/query-state.d.ts +96 -0
  18. package/dist/dashboard/query-state.d.ts.map +1 -0
  19. package/dist/dashboard/query-state.js +123 -0
  20. package/dist/dashboard/query-state.test.d.ts +2 -0
  21. package/dist/dashboard/query-state.test.d.ts.map +1 -0
  22. package/dist/data/data-table.d.ts +125 -8
  23. package/dist/data/data-table.d.ts.map +1 -1
  24. package/dist/data/data-table.js +121 -13
  25. package/dist/data/data-table.test.d.ts +2 -0
  26. package/dist/data/data-table.test.d.ts.map +1 -0
  27. package/dist/data/table.d.ts +2 -2
  28. package/dist/data/table.d.ts.map +1 -1
  29. package/dist/guard/index.d.ts.map +1 -1
  30. package/dist/guard/index.js +21 -6
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +7 -1
  34. package/dist/query/error-message.d.ts +41 -0
  35. package/dist/query/error-message.d.ts.map +1 -0
  36. package/dist/query/error-message.js +30 -0
  37. package/dist/query/error-message.test.d.ts +2 -0
  38. package/dist/query/error-message.test.d.ts.map +1 -0
  39. package/dist/query/index.d.ts +1 -19
  40. package/dist/query/index.d.ts.map +1 -1
  41. package/dist/query/index.js +2 -14
  42. package/dist/surfaces.d.ts +1 -1
  43. package/dist/surfaces.js +1 -1
  44. package/llms.txt +3 -3
  45. package/package.json +1 -1
  46. package/src/cli/check-theme.test.ts +34 -0
  47. package/src/cli/index.ts +222 -44
  48. package/src/cli/tokens-css.test.ts +73 -6
  49. package/src/cli/toolchain-wiring.test.ts +146 -0
  50. package/src/dashboard/empty-state.tsx +9 -3
  51. package/src/dashboard/index.ts +11 -0
  52. package/src/dashboard/query-state.test.tsx +152 -0
  53. package/src/dashboard/query-state.tsx +272 -0
  54. package/src/data/data-table.test.tsx +349 -0
  55. package/src/data/data-table.tsx +359 -42
  56. package/src/data/table.ts +8 -1
  57. package/src/guard/check-source.test.ts +141 -0
  58. package/src/guard/index.ts +102 -8
  59. package/src/index.ts +9 -0
  60. package/src/query/error-message.test.ts +67 -0
  61. package/src/query/error-message.ts +70 -0
  62. package/src/query/index.ts +2 -34
  63. package/src/surfaces.ts +1 -1
@@ -0,0 +1,123 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Alert, Button, Center, Group, Loader, Stack, Text } from "@mantine/core";
3
+ import { toErrorMessage } from "../query/error-message.js";
4
+ import { EmptyState } from "./empty-state.js";
5
+ function LoadingState({
6
+ variant = "page",
7
+ label = "Loading"
8
+ }) {
9
+ const loader = /* @__PURE__ */ jsx(Loader, { size: "sm", "aria-label": label });
10
+ if (variant === "section") return loader;
11
+ return /* @__PURE__ */ jsx(Center, { py: 64, children: loader });
12
+ }
13
+ function ErrorState({
14
+ error,
15
+ title = "Something went wrong",
16
+ fallback = "The request failed.",
17
+ onRetry,
18
+ retrying = false,
19
+ variant = "page",
20
+ action
21
+ }) {
22
+ const alert = /* @__PURE__ */ jsx(Alert, { color: "red", variant: "light", title, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
23
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: toErrorMessage(error, fallback) }),
24
+ (onRetry ?? action) !== void 0 && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
25
+ onRetry && /* @__PURE__ */ jsx(Button, { size: "xs", variant: "default", loading: retrying, onClick: onRetry, children: "Retry" }),
26
+ action
27
+ ] })
28
+ ] }) });
29
+ if (variant === "section") return alert;
30
+ return /* @__PURE__ */ jsx(Stack, { py: "md", children: alert });
31
+ }
32
+ function defaultIsEmpty(data) {
33
+ if (data === null || data === void 0) return true;
34
+ if (Array.isArray(data)) return data.length === 0;
35
+ if (typeof data === "object" && Array.isArray(data.data)) {
36
+ return data.data.length === 0;
37
+ }
38
+ return false;
39
+ }
40
+ const FETCH_STATUSES = /* @__PURE__ */ new Set(["fetching", "paused", "idle"]);
41
+ function assertQueryStateLike(query) {
42
+ const bad = (detail) => {
43
+ throw new Error(
44
+ `QueryState: \`query\` ${detail}. It must carry { data, isError, error, fetchStatus, refetch } \u2014 a TanStack UseQueryResult does; a hand-composed one must spell every field, because a missing branch flag renders a false claim about the data instead of an error.`
45
+ );
46
+ };
47
+ if (query === null || typeof query !== "object")
48
+ bad(`is ${query === null ? "null" : typeof query}`);
49
+ const q = query;
50
+ if (!("data" in q)) bad("has no `data` key");
51
+ if (typeof q["isError"] !== "boolean") bad("has no boolean `isError`");
52
+ if (typeof q["fetchStatus"] !== "string" || !FETCH_STATUSES.has(q["fetchStatus"]))
53
+ bad(`has fetchStatus=${JSON.stringify(q["fetchStatus"])}, not 'fetching' | 'paused' | 'idle'`);
54
+ if (typeof q["refetch"] !== "function") bad("has no `refetch()`");
55
+ }
56
+ function QueryState({
57
+ query,
58
+ children,
59
+ empty,
60
+ isEmpty,
61
+ errorTitle = "Could not load",
62
+ errorFallback = "The request failed.",
63
+ errorAction,
64
+ variant = "page",
65
+ loading
66
+ }) {
67
+ assertQueryStateLike(query);
68
+ const { data, isError, error, fetchStatus } = query;
69
+ const retrying = fetchStatus === "fetching";
70
+ const retry = () => void query.refetch();
71
+ if (isError && data === void 0) {
72
+ return /* @__PURE__ */ jsx(
73
+ ErrorState,
74
+ {
75
+ error,
76
+ title: errorTitle,
77
+ fallback: errorFallback,
78
+ onRetry: retry,
79
+ retrying,
80
+ variant,
81
+ ...errorAction !== void 0 && { action: errorAction }
82
+ }
83
+ );
84
+ }
85
+ if (data === void 0) {
86
+ if (fetchStatus === "idle" && !isError) return empty ? renderEmpty(empty, variant) : null;
87
+ return loading ?? /* @__PURE__ */ jsx(LoadingState, { variant });
88
+ }
89
+ const emptyNow = isEmpty ? isEmpty(data) : defaultIsEmpty(data);
90
+ const body = emptyNow ? empty ? renderEmpty(empty, variant) : null : typeof children === "function" ? children(data) : children;
91
+ if (!isError) return body;
92
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
93
+ /* @__PURE__ */ jsx(
94
+ ErrorState,
95
+ {
96
+ error,
97
+ title: "Showing cached data",
98
+ fallback: "The last refresh failed.",
99
+ onRetry: retry,
100
+ retrying,
101
+ variant: "section"
102
+ }
103
+ ),
104
+ body
105
+ ] });
106
+ }
107
+ function renderEmpty(empty, variant) {
108
+ return /* @__PURE__ */ jsx(
109
+ EmptyState,
110
+ {
111
+ title: empty.title,
112
+ variant,
113
+ ...empty.description !== void 0 && { description: empty.description },
114
+ ...empty.icon !== void 0 && { icon: empty.icon },
115
+ ...empty.action !== void 0 && { action: empty.action }
116
+ }
117
+ );
118
+ }
119
+ export {
120
+ ErrorState,
121
+ LoadingState,
122
+ QueryState
123
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=query-state.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-state.test.d.ts","sourceRoot":"","sources":["../../src/dashboard/query-state.test.tsx"],"names":[],"mappings":""}
@@ -1,5 +1,29 @@
1
- import type { ColumnDef, ColumnPinningState, PaginationState, SortingState } from '@tanstack/react-table';
1
+ import type { MantineSpacing } from '@mantine/core';
2
+ import type { ColumnDef, ColumnFiltersState, ColumnPinningState, PaginationState, RowData, SortingState } from '@tanstack/react-table';
2
3
  import type { ReactNode } from 'react';
4
+ /** Horizontal alignment of a column's header AND its cells. */
5
+ export type DataTableAlign = 'left' | 'center' | 'right';
6
+ /**
7
+ * Alignment lives on the column def's `meta`, not on a parallel prop array, so it travels with the
8
+ * column it describes and survives reordering.
9
+ *
10
+ * Declared as a module augmentation of TanStack's own `ColumnMeta` so `meta: { aling: 'right' }` is
11
+ * a TYPE error (excess-property check on the object literal) rather than a silently ignored key —
12
+ * and a wrong VALUE (`'end'`, `'centre'`) throws at render. A misspelled alignment that quietly
13
+ * left-aligns a money column is the class of defect that looks correct in review.
14
+ */
15
+ declare module '@tanstack/react-table' {
16
+ interface ColumnMeta<TData extends RowData, TValue> {
17
+ align?: DataTableAlign;
18
+ /**
19
+ * Opt OUT of the automatic mono-numeral cell style. The table applies it whenever the cell's
20
+ * raw value is a number — which is right for a plain figure and wrong for a numeric accessor
21
+ * whose `cell` renders its own chrome (a coloured `Text`, a `Badge`, a sparkline), because the
22
+ * `<td>`'s mono font then overrides what the cell asked for.
23
+ */
24
+ numeral?: boolean;
25
+ }
26
+ }
3
27
  /** A single selectable option inside a {@link DataTableFacet}'s Select/MultiSelect. */
4
28
  export type DataTableFacetOption = {
5
29
  /** The raw filter value, compared against the column's stringified cell value. */
@@ -50,15 +74,43 @@ export type BasaltDataTableProps<T> = {
50
74
  */
51
75
  columns: ColumnDef<T, any>[];
52
76
  /**
53
- * Enable column sorting via clickable headers.
77
+ * Enable column sorting via clickable headers. Client-side — the rows in `data` are what get
78
+ * reordered.
79
+ *
80
+ * Under `manualPagination` that is one page, so leaving this on without also passing
81
+ * `manualSorting` is a contract violation and throws in dev — see `manualPagination`.
54
82
  * @default true
55
83
  */
56
84
  enableSorting?: boolean;
57
- /** Stripe alternate rows. Forwarded to Mantine `Table`. */
58
- striped?: boolean;
85
+ /**
86
+ * Hand sorting to the server: the table stops reordering `data` locally and renders it in the
87
+ * order given, while the headers keep toggling `SortingState` and reporting it through
88
+ * `onSortingChange` — which is where you re-request the sorted page.
89
+ *
90
+ * This is the required companion to `manualPagination` whenever sort headers are live; without
91
+ * it a server-paginated table sorts one page and the "of N" bar presents that as a sort of all N.
92
+ * @default false
93
+ * @example
94
+ * <BasaltDataTable
95
+ * data={page.rows}
96
+ * columns={columns}
97
+ * enablePagination
98
+ * manualPagination
99
+ * manualSorting
100
+ * rowCount={page.total}
101
+ * onSortingChange={(s) => refetch({ sort: s[0] })}
102
+ * />
103
+ */
104
+ manualSorting?: boolean;
105
+ /** Stripe alternate rows. Forwarded to Mantine `Table` — `'odd'`/`'even'` pick the phase. */
106
+ striped?: boolean | 'odd' | 'even';
59
107
  /** Highlight hovered rows. Forwarded to Mantine `Table`. */
60
108
  highlightOnHover?: boolean;
61
- /** Rendered when `data` is empty. Falls back to a simple message when omitted. */
109
+ /**
110
+ * Rendered when no rows are VISIBLE — `data` is empty, or a filter/page matches none of it.
111
+ * Falls back to a simple message when omitted. (Before 1.25.0 this tracked `data.length` alone,
112
+ * so a search matching nothing rendered a blank `<tbody>` with no message at all.)
113
+ */
62
114
  emptyState?: ReactNode;
63
115
  /**
64
116
  * When true, renders skeleton placeholder rows instead of the empty-state branch.
@@ -117,6 +169,18 @@ export type BasaltDataTableProps<T> = {
117
169
  * facets={[{ columnId: 'department', label: 'Department', options: departmentOptions }]}
118
170
  */
119
171
  facets?: DataTableFacet[];
172
+ /** Called whenever the faceted column filters change — the seam for server-side faceting. */
173
+ onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
174
+ /**
175
+ * Hand filtering to the server: the table stops narrowing `data` locally and renders every row
176
+ * given, while the search input and facets keep reporting through `onGlobalFilterChange` /
177
+ * `onColumnFiltersChange` — which is where you re-request the filtered page.
178
+ *
179
+ * Required alongside `manualPagination` whenever `enableGlobalFilter` or `facets` is set;
180
+ * without it the controls narrow one page while "of N" keeps counting the whole set.
181
+ * @default false
182
+ */
183
+ manualFiltering?: boolean;
120
184
  /**
121
185
  * Right-aligned toolbar slot (e.g. an "Export" button). Renders the toolbar row even when no
122
186
  * search input or facets are configured.
@@ -145,6 +209,21 @@ export type BasaltDataTableProps<T> = {
145
209
  * Disables local pagination slicing — pass the already-paginated page of `data` plus `rowCount`
146
210
  * (and optionally `pageCount`) for server-driven pagination. `onPaginationChange` is where you
147
211
  * fetch the next page.
212
+ *
213
+ * **It imposes a contract, enforced at render.** `data` is now one page, but the pagination bar
214
+ * says "Showing 1–25 of 412" — so every OTHER client-side control becomes a claim about 412 rows
215
+ * that it can only make about 25. Each has to be resolved explicitly, because only the call site
216
+ * knows whether the server does the work:
217
+ *
218
+ * - `rowCount` (or `pageCount`) must be given, or "of N" counts the page and the pager collapses
219
+ * to a single page nobody can leave;
220
+ * - sorting must be handed over with `manualSorting`, or switched off with `enableSorting={false}`;
221
+ * - `enableGlobalFilter` / `facets`, if used, must be handed over with `manualFiltering`;
222
+ * - `enablePagination` must be on, or `manualPagination` is inert and the page renders as if it
223
+ * were the whole table.
224
+ *
225
+ * Unresolved, each throws in dev and degrades to the honest render in production (no sort
226
+ * headers, no filter controls, no "of N") — never to the plausible wrong answer.
148
227
  * @default false
149
228
  * @example
150
229
  * <BasaltDataTable
@@ -173,13 +252,51 @@ export type BasaltDataTableProps<T> = {
173
252
  enablePinning?: boolean;
174
253
  /** Initial column-pinning state — which column ids stick to the left/right edge. */
175
254
  initialColumnPinning?: ColumnPinningState;
255
+ /**
256
+ * Caps the scrolling body (px number or CSS length) so a long table cannot blow a card out
257
+ * vertically — the header stays put and the rows scroll under it. Renders Mantine's
258
+ * `Table.ScrollContainer type="native"`, which is the same node the docs sanction as the raw
259
+ * escape for a bespoke table: the blessed lane and the escape hatch produce identical DOM, so
260
+ * adopting one does not contradict the other.
261
+ *
262
+ * `type="native"` is required rather than preferred: `ScrollArea`'s custom viewport is the
263
+ * positioning context a sticky `<thead>` resolves against, so the default `'scrollarea'` type
264
+ * pins the header to the page viewport instead of the table's box.
265
+ */
266
+ maxHeight?: number | string;
267
+ /**
268
+ * `min-width` below which the body scrolls horizontally. Setting either this or `maxHeight`
269
+ * turns on the scroll container; `maxHeight` alone implies `minWidth: 0`.
270
+ */
271
+ minWidth?: number | string;
272
+ /** Sticky header row. Pair with `maxHeight` to stick within the table rather than the page. */
273
+ stickyHeader?: boolean;
274
+ /** Offset for a sticky header sitting under a fixed app header (px number or CSS length). */
275
+ stickyHeaderOffset?: number | string;
276
+ /** Vertical cell padding. Forwarded to Mantine `Table`. */
277
+ verticalSpacing?: MantineSpacing;
278
+ /** Horizontal cell padding. Forwarded to Mantine `Table`. */
279
+ horizontalSpacing?: MantineSpacing;
280
+ /** Row separators. Forwarded to Mantine `Table`. @default true (Mantine's own default) */
281
+ withRowBorders?: boolean;
282
+ /**
283
+ * Outer table border. Turn it OFF for a table that already sits inside a `Card`/`ChartCard`,
284
+ * where the card owns the frame and a second one reads as a nested box.
285
+ * @default true
286
+ */
287
+ withTableBorder?: boolean;
176
288
  };
177
289
  /**
178
290
  * A sortable data table backed by TanStack Table and rendered with Mantine primitives.
179
291
  * Generic over `T` — column types are inferred from `ColumnDef<T>`.
180
292
  *
181
- * Sorting is local/client-side (no `any`). When `enableSorting` is `false` the table
182
- * is read-only. An `emptyState` node is rendered when `data` is empty.
293
+ * Sorting is local/client-side by default (no `any`); `manualSorting` hands it to the server.
294
+ * When `enableSorting` is `false` the table is read-only. An `emptyState` node is rendered
295
+ * whenever no rows are visible — empty `data`, or a filter that matches none of it.
296
+ *
297
+ * `manualPagination` imposes a contract on the rest of the props (see its docblock): every
298
+ * client-side control it would turn into a false claim about the unseen rows must be handed to the
299
+ * server or switched off, and an unresolved one throws in dev rather than misreporting.
183
300
  *
184
301
  * Toolbar (search + facets), pagination, and column pinning are opt-in chrome layered on top —
185
302
  * see the corresponding props below. None of them change rendering unless explicitly enabled.
@@ -194,5 +311,5 @@ export type BasaltDataTableProps<T> = {
194
311
  * emptyState={<Text c="dimmed">No users found.</Text>}
195
312
  * />
196
313
  */
197
- export declare function BasaltDataTable<T>({ data, columns, enableSorting, striped, highlightOnHover, emptyState, isLoading, skeletonRows, initialSorting, onSortingChange, enableGlobalFilter, globalFilterPlaceholder, searchIcon, initialGlobalFilter, onGlobalFilterChange, facets, toolbarActions, enablePagination, pageSizeOptions, initialPagination, onPaginationChange, manualPagination, rowCount, pageCount, enablePinning, initialColumnPinning, }: BasaltDataTableProps<T>): import("react").JSX.Element;
314
+ export declare function BasaltDataTable<T>({ data, columns, enableSorting, manualSorting, striped, highlightOnHover, emptyState, isLoading, skeletonRows, initialSorting, onSortingChange, enableGlobalFilter, globalFilterPlaceholder, searchIcon, initialGlobalFilter, onGlobalFilterChange, facets, onColumnFiltersChange, manualFiltering, toolbarActions, enablePagination, pageSizeOptions, initialPagination, onPaginationChange, manualPagination, rowCount, pageCount, enablePinning, initialColumnPinning, maxHeight, minWidth, stickyHeader, stickyHeaderOffset, verticalSpacing, horizontalSpacing, withRowBorders, withTableBorder, }: BasaltDataTableProps<T>): import("react").JSX.Element;
198
315
  //# sourceMappingURL=data-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/data/data-table.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/data/data-table.tsx"],"names":[],"mappings":"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"}
@@ -20,6 +20,17 @@ import {
20
20
  } from "@tanstack/react-table";
21
21
  import { useCallback, useMemo, useState } from "react";
22
22
  import { alpha, VX } from "../tokens/index.js";
23
+ const ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right"]);
24
+ function resolveAlign(column) {
25
+ const align = column.columnDef.meta?.align;
26
+ if (align === void 0) return void 0;
27
+ if (!ALIGNMENTS.has(align)) {
28
+ throw new Error(
29
+ `BasaltDataTable: column "${column.id}" has meta.align=${JSON.stringify(align)} \u2014 expected 'left' | 'center' | 'right'.`
30
+ );
31
+ }
32
+ return align;
33
+ }
23
34
  function SortIndicator({ direction }) {
24
35
  if (!direction) return null;
25
36
  return /* @__PURE__ */ jsx(Box, { component: "span", ml: 4, "aria-hidden": true, children: direction === "asc" ? "\u2191" : "\u2193" });
@@ -74,10 +85,43 @@ function getPinnedCellStyle(table, column) {
74
85
  boxShadow: isEdge ? `-4px 0 6px -4px ${alpha(VX.ink, 0.18)}` : void 0
75
86
  };
76
87
  }
88
+ function isDev() {
89
+ return process.env["NODE_ENV"] !== "production";
90
+ }
91
+ const BREACH_REMEDY = {
92
+ inert: "`enablePagination` is not set, so `manualPagination` never reaches the table and the server page renders as if it were the whole set, with no bar to say otherwise. Pass `enablePagination`, or drop `manualPagination`.",
93
+ total: 'neither `rowCount` nor `pageCount` was given, so "of N" counts only the rows on this page and the pager collapses to one page nobody can leave. Pass `rowCount={total}`.',
94
+ sorting: 'client-side sorting is still armed. It reorders only the rows in `data` \u2014 one page \u2014 while the header chevron and "of N" together present it as a sort of all of them. Pass `manualSorting` and sort in the request you make from `onSortingChange`, or `enableSorting={false}`.',
95
+ filtering: 'the search input / facets still filter client-side. They narrow only the rows in `data` \u2014 one page \u2014 while "of N" keeps counting the whole set. Pass `manualFiltering` and filter in the request you make from `onGlobalFilterChange` / `onColumnFiltersChange`, or drop `enableGlobalFilter` / `facets`.'
96
+ };
97
+ function manualPaginationBreaches(config) {
98
+ if (!config.manualPagination) return [];
99
+ if (!config.enablePagination) return ["inert"];
100
+ const breaches = [];
101
+ if (config.rowCount === void 0 && config.pageCount === void 0) breaches.push("total");
102
+ const anyColumnMaySort = config.columns.some((column) => column.enableSorting !== false);
103
+ if (config.enableSorting && anyColumnMaySort && !config.manualSorting) breaches.push("sorting");
104
+ if ((config.enableGlobalFilter || config.hasFacets) && !config.manualFiltering)
105
+ breaches.push("filtering");
106
+ return breaches;
107
+ }
108
+ function manualPaginationMessage(breaches) {
109
+ return "BasaltDataTable: `manualPagination` is set, so `data` is one server page \u2014 but:\n \xB7 " + breaches.map((breach) => BREACH_REMEDY[breach]).join("\n \xB7 ") + "\nEach has to be resolved explicitly: only the call site knows whether the server does the work, so there is no default that is not a guess about the data.";
110
+ }
111
+ const reportedBreaches = /* @__PURE__ */ new Set();
112
+ function enforceManualPaginationContract(breaches) {
113
+ if (breaches.length === 0) return;
114
+ const message = manualPaginationMessage(breaches);
115
+ if (isDev()) throw new Error(message);
116
+ if (reportedBreaches.has(message)) return;
117
+ reportedBreaches.add(message);
118
+ console.error(message);
119
+ }
77
120
  function BasaltDataTable({
78
121
  data,
79
122
  columns,
80
123
  enableSorting = true,
124
+ manualSorting = false,
81
125
  striped,
82
126
  highlightOnHover,
83
127
  emptyState,
@@ -91,6 +135,8 @@ function BasaltDataTable({
91
135
  initialGlobalFilter,
92
136
  onGlobalFilterChange,
93
137
  facets,
138
+ onColumnFiltersChange,
139
+ manualFiltering = false,
94
140
  toolbarActions,
95
141
  enablePagination = false,
96
142
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
@@ -100,7 +146,15 @@ function BasaltDataTable({
100
146
  rowCount,
101
147
  pageCount,
102
148
  enablePinning = false,
103
- initialColumnPinning
149
+ initialColumnPinning,
150
+ maxHeight,
151
+ minWidth,
152
+ stickyHeader,
153
+ stickyHeaderOffset,
154
+ verticalSpacing,
155
+ horizontalSpacing,
156
+ withRowBorders,
157
+ withTableBorder = true
104
158
  }) {
105
159
  const [sorting, setSorting] = useState(initialSorting ?? []);
106
160
  const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter ?? "");
@@ -129,6 +183,16 @@ function BasaltDataTable({
129
183
  },
130
184
  [onGlobalFilterChange]
131
185
  );
186
+ const handleColumnFiltersChange = useCallback(
187
+ (updater) => {
188
+ setColumnFilters((prev) => {
189
+ const next = typeof updater === "function" ? updater(prev) : updater;
190
+ onColumnFiltersChange?.(next);
191
+ return next;
192
+ });
193
+ },
194
+ [onColumnFiltersChange]
195
+ );
132
196
  const handlePaginationChange = useCallback(
133
197
  (updater) => {
134
198
  setPagination((prev) => {
@@ -151,6 +215,23 @@ function BasaltDataTable({
151
215
  return { ...column, filterFn };
152
216
  });
153
217
  }, [columns, facets]);
218
+ const hasFacets = Boolean(facets && facets.length > 0);
219
+ const breaches = manualPaginationBreaches({
220
+ columns,
221
+ enablePagination,
222
+ manualPagination,
223
+ rowCount,
224
+ pageCount,
225
+ enableSorting,
226
+ manualSorting,
227
+ enableGlobalFilter,
228
+ hasFacets,
229
+ manualFiltering
230
+ });
231
+ enforceManualPaginationContract(breaches);
232
+ const sortingEnabled = enableSorting && !breaches.includes("sorting");
233
+ const filteringEnabled = !breaches.includes("filtering");
234
+ const totalIsAuthoritative = !breaches.includes("total");
154
235
  const table = useReactTable({
155
236
  data,
156
237
  columns: tableColumns,
@@ -163,35 +244,51 @@ function BasaltDataTable({
163
244
  },
164
245
  onSortingChange: handleSortingChange,
165
246
  onGlobalFilterChange: handleGlobalFilterChange,
166
- onColumnFiltersChange: setColumnFilters,
247
+ onColumnFiltersChange: handleColumnFiltersChange,
167
248
  onColumnPinningChange: setColumnPinning,
168
249
  ...enablePagination && { onPaginationChange: handlePaginationChange },
169
250
  getCoreRowModel: getCoreRowModel(),
170
251
  getSortedRowModel: getSortedRowModel(),
171
252
  getFilteredRowModel: getFilteredRowModel(),
253
+ // Both row models stay installed and are BYPASSED by these flags, which is TanStack's own
254
+ // seam — so a degraded table renders `data` in the order and completeness the server sent it.
255
+ // Forced only by a BREACH, never by `enableSorting={false}` on its own: that pair still applies
256
+ // an `initialSorting` today (it disables the header control, not the row model) and a table
257
+ // that is not misreporting has no business losing it.
258
+ manualSorting: manualSorting || breaches.includes("sorting"),
259
+ manualFiltering: manualFiltering || breaches.includes("filtering"),
172
260
  ...enablePagination && {
173
261
  getPaginationRowModel: getPaginationRowModel(),
174
262
  manualPagination,
175
263
  ...manualPagination && rowCount !== void 0 && { rowCount },
176
264
  ...manualPagination && pageCount !== void 0 && { pageCount }
177
265
  },
178
- enableSorting,
266
+ enableSorting: sortingEnabled,
179
267
  enableColumnPinning: enablePinning
180
268
  });
181
- const showToolbar = enableGlobalFilter || Boolean(facets && facets.length > 0) || Boolean(toolbarActions);
269
+ const showSearch = enableGlobalFilter && filteringEnabled;
270
+ const showFacets = hasFacets && filteringEnabled;
271
+ const showToolbar = showSearch || showFacets || Boolean(toolbarActions);
182
272
  const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups();
273
+ const rows = table.getRowModel().rows;
183
274
  const tableNode = /* @__PURE__ */ jsxs(
184
275
  Table,
185
276
  {
186
277
  ...striped !== void 0 && { striped },
187
278
  ...highlightOnHover !== void 0 && { highlightOnHover },
188
- withTableBorder: true,
279
+ ...stickyHeader !== void 0 && { stickyHeader },
280
+ ...stickyHeaderOffset !== void 0 && { stickyHeaderOffset },
281
+ ...verticalSpacing !== void 0 && { verticalSpacing },
282
+ ...horizontalSpacing !== void 0 && { horizontalSpacing },
283
+ ...withRowBorders !== void 0 && { withRowBorders },
284
+ withTableBorder,
189
285
  children: [
190
286
  /* @__PURE__ */ jsx(Table.Thead, { children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(Table.Tr, { children: headerGroup.headers.map((header) => {
191
- const canSort = enableSorting && header.column.getCanSort();
287
+ const canSort = sortingEnabled && header.column.getCanSort();
192
288
  const sorted = header.column.getIsSorted();
193
289
  const toggleSorting = header.column.getToggleSortingHandler();
194
290
  const pinnedStyle = enablePinning ? getPinnedCellStyle(table, header.column) : void 0;
291
+ const align = resolveAlign(header.column);
195
292
  return /* @__PURE__ */ jsxs(
196
293
  Table.Th,
197
294
  {
@@ -205,6 +302,7 @@ function BasaltDataTable({
205
302
  "aria-sort": canSort ? sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none" : void 0,
206
303
  style: {
207
304
  ...canSort ? { cursor: "pointer", userSelect: "none" } : void 0,
305
+ ...align !== void 0 && { textAlign: align },
208
306
  ...pinnedStyle
209
307
  },
210
308
  children: [
@@ -215,7 +313,7 @@ function BasaltDataTable({
215
313
  header.id
216
314
  );
217
315
  }) }, headerGroup.id)) }),
218
- /* @__PURE__ */ jsx(Table.Tbody, { children: isLoading ? Array.from({ length: skeletonRows }, (_, rowIndex) => /* @__PURE__ */ jsx(Table.Tr, { children: columns.map((_2, colIndex) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Skeleton, { height: 16, radius: "sm" }) }, `skeleton-${rowIndex}-${colIndex}`)) }, `skeleton-${rowIndex}`)) : data.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columns.length, children: emptyState ?? /* @__PURE__ */ jsx(Text, { c: "dimmed", ta: "center", size: "sm", py: "sm", children: "No data to display." }) }) }) : table.getRowModel().rows.map((row) => {
316
+ /* @__PURE__ */ jsx(Table.Tbody, { children: isLoading ? Array.from({ length: skeletonRows }, (_, rowIndex) => /* @__PURE__ */ jsx(Table.Tr, { children: columns.map((_2, colIndex) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Skeleton, { height: 16, radius: "sm" }) }, `skeleton-${rowIndex}-${colIndex}`)) }, `skeleton-${rowIndex}`)) : rows.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columns.length, children: emptyState ?? /* @__PURE__ */ jsx(Text, { c: "dimmed", ta: "center", size: "sm", py: "sm", children: "No data to display." }) }) }) : rows.map((row) => {
219
317
  const cells = enablePinning ? [
220
318
  ...row.getLeftVisibleCells(),
221
319
  ...row.getCenterVisibleCells(),
@@ -223,11 +321,13 @@ function BasaltDataTable({
223
321
  ] : row.getVisibleCells();
224
322
  return /* @__PURE__ */ jsx(Table.Tr, { children: cells.map((cell) => {
225
323
  const pinnedStyle = enablePinning ? getPinnedCellStyle(table, cell.column) : void 0;
324
+ const align = resolveAlign(cell.column);
226
325
  return /* @__PURE__ */ jsx(
227
326
  Table.Td,
228
327
  {
229
328
  style: {
230
- ...typeof cell.getValue() === "number" ? NUMERIC_CELL_STYLE : void 0,
329
+ ...typeof cell.getValue() === "number" && cell.column.columnDef.meta?.numeral !== false ? NUMERIC_CELL_STYLE : void 0,
330
+ ...align !== void 0 && { textAlign: align },
231
331
  ...pinnedStyle
232
332
  },
233
333
  children: flexRender(cell.column.columnDef.cell, cell.getContext())
@@ -239,6 +339,7 @@ function BasaltDataTable({
239
339
  ]
240
340
  }
241
341
  );
342
+ const scrolls = maxHeight !== void 0 || minWidth !== void 0;
242
343
  const paginationState = table.getState().pagination;
243
344
  const total = table.getRowCount();
244
345
  const rangeStart = total === 0 ? 0 : paginationState.pageIndex * paginationState.pageSize + 1;
@@ -246,7 +347,7 @@ function BasaltDataTable({
246
347
  return /* @__PURE__ */ jsxs(Fragment, { children: [
247
348
  showToolbar && /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", wrap: "wrap", gap: "xs", mb: "xs", children: [
248
349
  /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", align: "flex-end", children: [
249
- enableGlobalFilter && /* @__PURE__ */ jsx(
350
+ showSearch && /* @__PURE__ */ jsx(
250
351
  TextInput,
251
352
  {
252
353
  size: "xs",
@@ -258,7 +359,7 @@ function BasaltDataTable({
258
359
  w: 220
259
360
  }
260
361
  ),
261
- facets?.map((facet) => {
362
+ showFacets && facets?.map((facet) => {
262
363
  const column = table.getColumn(facet.columnId);
263
364
  if (!column) return null;
264
365
  if (facet.multiple) {
@@ -297,15 +398,22 @@ function BasaltDataTable({
297
398
  ] }),
298
399
  toolbarActions
299
400
  ] }),
300
- enablePinning ? /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: tableNode }) : tableNode,
401
+ scrolls ? /* @__PURE__ */ jsx(
402
+ Table.ScrollContainer,
403
+ {
404
+ type: "native",
405
+ minWidth: minWidth ?? 0,
406
+ ...maxHeight !== void 0 && { maxHeight },
407
+ children: tableNode
408
+ }
409
+ ) : enablePinning ? /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: tableNode }) : tableNode,
301
410
  enablePagination && /* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "xs", wrap: "wrap", gap: "xs", align: "center", children: [
302
411
  /* @__PURE__ */ jsxs(Text, { style: RANGE_LABEL_STYLE, children: [
303
412
  "Showing ",
304
413
  rangeStart,
305
414
  "\u2013",
306
415
  rangeEnd,
307
- " of ",
308
- total
416
+ totalIsAuthoritative ? ` of ${total}` : ""
309
417
  ] }),
310
418
  /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "center", children: [
311
419
  /* @__PURE__ */ jsx(
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data-table.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table.test.d.ts","sourceRoot":"","sources":["../../src/data/data-table.test.tsx"],"names":[],"mappings":""}
@@ -19,6 +19,6 @@
19
19
  * <BasaltDataTable data={rows} columns={columns} enableGlobalFilter enablePagination />
20
20
  */
21
21
  export { BasaltDataTable } from './data-table.js';
22
- export type { BasaltDataTableProps, DataTableFacet, DataTableFacetOption } from './data-table.js';
23
- export { createColumnHelper, useReactTable, flexRender, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, type ColumnDef, type SortingState, type ColumnHelper, type PaginationState, type ColumnPinningState, } from '@tanstack/react-table';
22
+ export type { BasaltDataTableProps, DataTableAlign, DataTableFacet, DataTableFacetOption, } from './data-table.js';
23
+ export { createColumnHelper, useReactTable, flexRender, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, type ColumnDef, type SortingState, type ColumnHelper, type PaginationState, type ColumnPinningState, type ColumnFiltersState, } from '@tanstack/react-table';
24
24
  //# sourceMappingURL=table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/data/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAS9F,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/data/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,cAAc,CAAA;AASrB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAEvB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"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"}