@ai-matrx/design-system 0.48.16 → 0.49.2

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 (40) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +16 -0
  3. package/dist/data-table/copy-helpers.d.cts +1 -1
  4. package/dist/data-table/copy-helpers.d.ts +1 -1
  5. package/dist/data-table/filter-engine.d.cts +1 -1
  6. package/dist/data-table/filter-engine.d.ts +1 -1
  7. package/dist/data-table/host.d.cts +1 -1
  8. package/dist/data-table/host.d.ts +1 -1
  9. package/dist/data-table/index.cjs +191 -48
  10. package/dist/data-table/index.cjs.map +1 -1
  11. package/dist/data-table/index.d.cts +2 -2
  12. package/dist/data-table/index.d.ts +2 -2
  13. package/dist/data-table/index.js +191 -48
  14. package/dist/data-table/index.js.map +1 -1
  15. package/dist/data-table/infer-filter.d.cts +1 -1
  16. package/dist/data-table/infer-filter.d.ts +1 -1
  17. package/dist/data-table/layered-filters.d.cts +1 -1
  18. package/dist/data-table/layered-filters.d.ts +1 -1
  19. package/dist/data-table/pagination.cjs +12 -2
  20. package/dist/data-table/pagination.cjs.map +1 -1
  21. package/dist/data-table/pagination.d.cts +6 -1
  22. package/dist/data-table/pagination.d.ts +6 -1
  23. package/dist/data-table/pagination.js +12 -2
  24. package/dist/data-table/pagination.js.map +1 -1
  25. package/dist/data-table/query-control.d.cts +1 -1
  26. package/dist/data-table/query-control.d.ts +1 -1
  27. package/dist/data-table/types.cjs.map +1 -1
  28. package/dist/data-table/types.d.cts +1 -1
  29. package/dist/data-table/types.d.ts +1 -1
  30. package/dist/data-table/url-state.d.cts +1 -1
  31. package/dist/data-table/url-state.d.ts +1 -1
  32. package/dist/data-table/xlsx.d.cts +1 -1
  33. package/dist/data-table/xlsx.d.ts +1 -1
  34. package/dist/index.cjs +1 -1
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.js +1 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/{types-bIcXnlYS.d.cts → types-ClxgLUFi.d.cts} +34 -1
  39. package/dist/{types-DVTNnzU5.d.ts → types-DlgTSJsC.d.ts} +34 -1
  40. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,8 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.49.2
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/design-system/v0.48.16..npm/design-system/v0.49.2 -- apps/shared/design-system`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.49.1
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/design-system/v0.48.16..npm/design-system/v0.49.1 -- apps/shared/design-system`).
13
+ No source changes intended and no consumer action required.
14
+
15
+ ## 0.49.0
16
+ <<<<<<< origin/main
17
+
18
+ - Added optional canonical table KPIs and aligned column totals through `summary`. Aggregates use filtered rows before pagination; source-provided totals carry their own declared scope. Loading, unavailable and retained stale results remain explicit. Existing `footerRows` behavior is preserved.
19
+ - Consumer action: pass metric callbacks and column-ID totals to `summary` where aggregates are useful. Desktop column resizing remains enabled by default.
20
+
21
+ ## 0.48.16
22
+ ### Unreleased
23
+ =======
24
+ >>>>>>> HEAD
25
+
26
+ - Added optional canonical table KPIs and aligned column totals through `summary`. Aggregates use filtered rows before pagination; source-provided totals carry their own declared scope. Loading, unavailable and retained stale results remain explicit. Existing `footerRows` behavior is preserved.
27
+ - Consumer action: pass metric callbacks and column-ID totals to `summary` where aggregates are useful. Desktop column resizing remains enabled by default.
28
+
3
29
  ## 0.48.16
4
30
  ### Unreleased
5
31
 
32
+ - **The pager never says "0 rows" over a failed read** (2026-09-27, seen live on `/administration/automation/scheduling/runs` with the runs read failing: body "Couldn't load runs", footer "0 rows"). When `read.status` is `"error"` and no rows are on screen, the footer shows "—" labelled "Row count unavailable"; while `read.status` is `"loading"` with no rows it shows no count. A failed refresh over kept rows still shows the real count (stale-while-error). A caller's `paginationLabelFormat` is not drawn in those states either. New optional pager prop `countUnavailable` (`GenericTablePagination`). Consumer action: none. Proof: `MatrxDataTable.read-outcome-pager.test.tsx`, 3 of 6 red on 0.48.15.
6
33
  - **The layout menu's promises, kept** (merged-grid review 2, fix lane E, 2026-09-27). (1) **Freeze holds with tick boxes:** the band waited forever for the selection column to be measured, which only happened after every column was resized; the selection column is now declared at `SELECTION_COLUMN_PX` (36) while a column is frozen, so the offset is known. On a phone the FIRST frozen column now stays too, capped at 40vw (`FROZEN_PHONE_*`); the rest of the band scrolls. (2) **Compact is compact:** a `rowHeight` below the density's own draws cells unpadded and row buttons at 24 px. (3) **`fitToWidth`** (new prop): every data column gets its proportional share of the measured container (`fit-columns.ts`), `table-layout: fixed`, never a sideways scroll (from `sm` up). (4) **`rowActionsHeader`** (new prop): controls drawn in the Actions header. Proof: `MatrxDataTable.layout-look.test.tsx`, 5 of 7 red on the previous table.
7
34
  - **A submenu opens beside its parent, not on top of it**: `DropdownMenuSubContent` and `ContextMenuSubContent` are portalled through the container seam — the parent's animation transform made it the containing block of the submenu's `position: fixed`. Proof: `menus.test.tsx`, red on the in-place submenu.
8
35
  - **The number filter box reads like its neighbours**: 12 px, not monospace 16 px (16 px only below `sm`, for iOS). Proof: `header-number-filter-and-clear-sort.test.tsx`.
package/README.md CHANGED
@@ -162,6 +162,22 @@ The gate validates declarations and exports against the packed tarball, installs
162
162
 
163
163
  ## Data tables
164
164
 
165
+ Desktop columns are adjustable by default. Optional `summary` supplies KPI
166
+ metrics above the toolbar and totals aligned to column IDs below the rows.
167
+ Callbacks receive the filtered rows before pagination, plus `isFiltered` and
168
+ `coverage` (`all`, `loaded`, or `page`). The table labels that scope and never
169
+ calls aggregates for an empty loading or failed read. Retained stale rows are
170
+ labelled; unavailable aggregates remain visible. Use `summary.source` only for
171
+ caller-provided complete source totals; missing source values are never replaced
172
+ with an aggregate of the loaded page.
173
+
174
+ ```tsx
175
+ summary={{
176
+ metrics: [{ id: "amount", label: "Amount", value: ({ rows }) => rows.reduce((n, row) => n + row.amount, 0) }],
177
+ totals: { amount: ({ rows }) => rows.reduce((n, row) => n + row.amount, 0) },
178
+ }}
179
+ ```
180
+
165
181
  ```tsx
166
182
  import { MatrxDataTable } from "@ai-matrx/design-system/data-table";
167
183
  import { usePaginatedData } from "@ai-matrx/data/react";
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.cjs';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-bIcXnlYS.cjs';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-ClxgLUFi.cjs';
3
3
  import '../content-transfer.cjs';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.js';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-DVTNnzU5.js';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-DlgTSJsC.js';
3
3
  import '../content-transfer.js';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-bIcXnlYS.cjs';
1
+ import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-ClxgLUFi.cjs';
2
2
  import '../content-transfer.cjs';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-DVTNnzU5.js';
1
+ import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-DlgTSJsC.js';
2
2
  import '../content-transfer.js';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import { ComponentType, ReactNode, MouseEventHandler } from 'react';
3
3
  import { CopyControlProps } from './copy-types.cjs';
4
4
  export { AgentPayloadInput, AiCustomSource, AiVariant, CopyExportConfig } from './copy-types.cjs';
5
- import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-bIcXnlYS.cjs';
5
+ import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-ClxgLUFi.cjs';
6
6
  import { MatrxTableMenuTarget, MatrxTableMenuPayload, MatrxTableMenuSectionsResolver, MatrxTableMenuIconName } from './menu-targets.cjs';
7
7
  export { MatrxTableMenuItem, MatrxTableMenuSection } from './menu-targets.cjs';
8
8
  import '../content-transfer.cjs';
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import { ComponentType, ReactNode, MouseEventHandler } from 'react';
3
3
  import { CopyControlProps } from './copy-types.js';
4
4
  export { AgentPayloadInput, AiCustomSource, AiVariant, CopyExportConfig } from './copy-types.js';
5
- import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-DVTNnzU5.js';
5
+ import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-DlgTSJsC.js';
6
6
  import { MatrxTableMenuTarget, MatrxTableMenuPayload, MatrxTableMenuSectionsResolver, MatrxTableMenuIconName } from './menu-targets.js';
7
7
  export { MatrxTableMenuItem, MatrxTableMenuSection } from './menu-targets.js';
8
8
  import '../content-transfer.js';
@@ -36503,7 +36503,8 @@ function GenericTablePagination({
36503
36503
  labelFormat,
36504
36504
  compact = false,
36505
36505
  hideEntriesInfo = false,
36506
- progressive
36506
+ progressive,
36507
+ countUnavailable
36507
36508
  }) {
36508
36509
  const host = useMatrxDataTableHost();
36509
36510
  const positivePageSizes = pageSizeOptions.filter((size) => size > 0);
@@ -36578,7 +36579,16 @@ function GenericTablePagination({
36578
36579
  "div",
36579
36580
  {
36580
36581
  className: cn("flex min-w-0 justify-center", infoContainerClassName),
36581
- children: showPageInfo && !hideEntriesInfo && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
36582
+ children: showPageInfo && !hideEntriesInfo && countUnavailable === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
36583
+ "span",
36584
+ {
36585
+ role: "img",
36586
+ "aria-label": "Row count unavailable",
36587
+ title: "Row count unavailable",
36588
+ className: "truncate text-sm tabular-nums text-muted-foreground",
36589
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { "aria-hidden": "true", children: "\u2014" })
36590
+ }
36591
+ ) : showPageInfo && !hideEntriesInfo && !countUnavailable && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
36582
36592
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-xs tabular-nums text-muted-foreground sm:hidden", children: labelFormat ? formatLabel(startItem, endItem, totalItems) : unknownTotal ? defaultLabelFormat(startItem, endItem, totalItems) : totalItems === 0 ? "0 rows" : progressive ? `${endItem}/${totalItems} ${progressive.loadedCount !== void 0 && progressive.loadedCount !== totalItems ? "matching" : "loaded"}` : `${startItem}\u2013${endItem} of ${totalItems}` }),
36583
36593
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
36584
36594
  "span",
@@ -48155,6 +48165,7 @@ function MatrxDataTableCore(coreProps) {
48155
48165
  selection: selectionConfig,
48156
48166
  rowActions,
48157
48167
  footerRows = [],
48168
+ summary,
48158
48169
  rowClassName,
48159
48170
  rowVersion,
48160
48171
  cellClassName,
@@ -49524,6 +49535,48 @@ function MatrxDataTableCore(coreProps) {
49524
49535
  return def !== void 0 && f.value !== def;
49525
49536
  });
49526
49537
  const hasActiveFilters = activeFilterCount > 0 || Boolean(searchValue.trim()) || intelligentSearchEnabled && searchScope !== "all" || Boolean(anyOfValue.trim()) || completeLayeredFilterRules(layeredFilters).length > 0 || facetActive;
49538
+ const summaryIsFiltered = activeFilterCount > 0 || Boolean(searchValue.trim()) || Boolean(anyOfValue.trim()) || completeLayeredFilterRules(layeredFilters).length > 0 || facetActive;
49539
+ const summaryCoverage = remoteQuery?.mode === "controlled" ? "page" : appendQuery !== null || !allRowsAreLoaded ? "loaded" : "all";
49540
+ const summaryContext = {
49541
+ rows: processed,
49542
+ isFiltered: summaryIsFiltered,
49543
+ coverage: summaryCoverage
49544
+ };
49545
+ const summaryCanReadRows = !readView.failure && (!isLoading || data.length > 0);
49546
+ const summaryUsesSource = summary?.source !== void 0;
49547
+ const summaryCaption = (() => {
49548
+ if (summaryUsesSource) {
49549
+ const sourceCaption = summary?.source?.isFiltered ? "Filtered source totals" : "Source totals";
49550
+ return readView.stale ? `${sourceCaption} (stale)` : sourceCaption;
49551
+ }
49552
+ const base = summaryCoverage === "all" ? "rows" : summaryCoverage === "loaded" ? "loaded rows" : "current page";
49553
+ const caption = summaryIsFiltered ? `Filtered ${base}` : base === "rows" ? "All rows" : base[0].toUpperCase() + base.slice(1);
49554
+ return readView.stale ? `${caption} (stale)` : caption;
49555
+ })();
49556
+ const resolveSummaryValue = (sourceValue, callback) => {
49557
+ if (!summaryCanReadRows) {
49558
+ return readView.failure ? { value: "Unavailable", unavailable: true } : { value: "Loading", unavailable: false };
49559
+ }
49560
+ if (summaryUsesSource) {
49561
+ return sourceValue === void 0 ? { value: "Unavailable", unavailable: true } : { value: sourceValue, unavailable: false };
49562
+ }
49563
+ if (!callback) return { value: "Unavailable", unavailable: true };
49564
+ try {
49565
+ return { value: callback(summaryContext), unavailable: false };
49566
+ } catch {
49567
+ return { value: "Unavailable", unavailable: true };
49568
+ }
49569
+ };
49570
+ const summaryMetrics = summary?.metrics.map((metric) => ({
49571
+ id: metric.id,
49572
+ label: metric.label,
49573
+ ...resolveSummaryValue(summary.source?.metrics?.[metric.id], metric.value)
49574
+ })) ?? [];
49575
+ const summaryTotals = Object.entries(summary?.totals ?? {}).map(([id, value]) => ({
49576
+ id,
49577
+ ...resolveSummaryValue(summary?.source?.totals?.[id], value)
49578
+ }));
49579
+ const hasSummaryTotals = summaryTotals.length > 0;
49527
49580
  const changeCount = (0, import_react22.useMemo)(() => {
49528
49581
  let n2 = 0;
49529
49582
  for (const fields of Object.values(edits)) {
@@ -50425,7 +50478,16 @@ function MatrxDataTableCore(coreProps) {
50425
50478
  controlledQuery,
50426
50479
  emitControlledQueryChange
50427
50480
  ]);
50428
- const footerEpoch = useRenderEpoch([...chromeGeometry, footerRows]);
50481
+ const footerEpoch = useRenderEpoch([
50482
+ ...chromeGeometry,
50483
+ footerRows,
50484
+ summaryContext.rows,
50485
+ summaryContext.isFiltered,
50486
+ summaryContext.coverage,
50487
+ summaryCaption,
50488
+ summaryUsesSource,
50489
+ ...summaryTotals.flatMap((total) => [total.id, total.value, total.unavailable])
50490
+ ]);
50429
50491
  const paginationEpoch = useRenderEpoch([
50430
50492
  totalItems,
50431
50493
  sourceTotal,
@@ -50962,6 +51024,25 @@ function MatrxDataTableCore(coreProps) {
50962
51024
  notice,
50963
51025
  savedViewError ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ErrorBox, { className: "shrink-0 text-xs text-destructive", message: savedViewError, operation: "Save or apply a saved view", origin: "design-system/MatrxDataTable" }) : null,
50964
51026
  toolbarActionError ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ErrorBox, { className: "shrink-0 text-xs text-destructive", message: toolbarActionError, operation: "Run a table toolbar action", origin: "design-system/MatrxDataTable" }) : null,
51027
+ summaryMetrics.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
51028
+ "section",
51029
+ {
51030
+ "data-matrx-table-summary": true,
51031
+ "data-matrx-table-summary-scope": summaryUsesSource ? "source" : summaryCoverage,
51032
+ "aria-label": `Table summary: ${summaryCaption}`,
51033
+ className: "flex shrink-0 flex-wrap items-stretch gap-2",
51034
+ children: [
51035
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex items-center px-1 text-xs font-medium text-muted-foreground", children: summaryCaption }),
51036
+ summaryMetrics.map((metric) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "min-w-24 rounded-md border border-border bg-card px-3 py-2 shadow-xs", children: [
51037
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "text-xs text-muted-foreground", children: metric.label }),
51038
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "mt-0.5 text-sm font-semibold tabular-nums text-foreground", children: [
51039
+ metric.value,
51040
+ metric.unavailable ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ErrorBox, { as: "span", className: "sr-only", message: `${metric.label} is unavailable`, operation: "Summarize this table", origin: "design-system/MatrxDataTable" }) : null
51041
+ ] })
51042
+ ] }, metric.id))
51043
+ ]
51044
+ }
51045
+ ) : null,
50965
51046
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MatrxTableChrome, { part: "toolbar", epoch: toolbarEpoch, render: () => placeToolbar(!hideToolbar && (viewTabs || toolbar?.title || toolbar?.titleCount || showSearch || toolbar?.customSearch || toolbar?.anyOf || toolbar?.layeredFilters || toolbar?.facets && toolbar.facets.length > 0 || toolbar?.leading || toolbar?.actions || toolbar?.refresh || toolbar?.add || tableId && SavedViews || hasActiveFilters || showToolbarCopy || columns.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
50966
51047
  TableTitleRow,
50967
51048
  {
@@ -51802,50 +51883,111 @@ function MatrxDataTableCore(coreProps) {
51802
51883
  }
51803
51884
  ) }) : null
51804
51885
  ] }),
51805
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MatrxTableChrome, { part: "footer", epoch: footerEpoch, render: () => footerRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("tfoot", { className: "border-t-2 border-border bg-muted/50 font-semibold", children: footerRows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("tr", { children: [
51806
- selection ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", { className: "px-2 py-2" }) : null,
51807
- columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51808
- "td",
51809
- {
51810
- "aria-hidden": "true",
51811
- "data-matrx-table-column-spacer": "start",
51812
- className: "border-0 p-0",
51813
- style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
51814
- },
51815
- "matrx-column-spacer-start"
51816
- ) : null,
51817
- renderedColumns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51818
- "td",
51819
- {
51820
- className: cn(
51821
- "px-2 py-2 align-middle",
51822
- (column.compact || resolveColumnMarker(column) !== void 0) && "px-1",
51823
- column.className,
51824
- column.align === "center" && "text-center",
51825
- column.align === "right" && "text-right"
51826
- ),
51827
- style: columnWidthVar(
51828
- resolveColumnMarker(column) && column.width === void 0 ? 40 : column.width,
51829
- windowedWidthFor(columnId(column), column.width),
51830
- columnId(column),
51831
- resizableColumns && column.resizable !== false
51832
- ),
51833
- children: renderCell(row, column, index)
51834
- },
51835
- columnId(column)
51836
- )),
51837
- columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51838
- "td",
51839
- {
51840
- "aria-hidden": "true",
51841
- "data-matrx-table-column-spacer": "end",
51842
- className: "border-0 p-0",
51843
- style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
51844
- },
51845
- "matrx-column-spacer-end"
51846
- ) : null,
51847
- showActionsCol ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", {}) : null
51848
- ] }, `footer-${index}`)) }) : null })
51886
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MatrxTableChrome, { part: "footer", epoch: footerEpoch, render: () => footerRows.length > 0 || hasSummaryTotals ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("tfoot", { className: cn(
51887
+ "border-t-2 border-border bg-muted/50 font-semibold",
51888
+ hasSummaryTotals && "sticky bottom-0 z-10 bg-muted/95 backdrop-blur-sm"
51889
+ ), children: [
51890
+ hasSummaryTotals ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
51891
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("tr", { "data-matrx-table-summary-total-caption": true, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("td", { colSpan: renderedColSpan, className: "px-2 pt-2 text-xs font-medium text-muted-foreground", children: [
51892
+ summaryCaption,
51893
+ readView.stale ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "ml-1 text-amber-700 dark:text-amber-300", children: "Retained rows may be out of date." }) : null
51894
+ ] }) }),
51895
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("tr", { "data-matrx-table-summary-totals": true, children: [
51896
+ selection ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", { className: "px-2 py-2" }) : null,
51897
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51898
+ "td",
51899
+ {
51900
+ "aria-hidden": "true",
51901
+ "data-matrx-table-column-spacer": "start",
51902
+ className: "border-0 p-0",
51903
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
51904
+ },
51905
+ "matrx-summary-column-spacer-start"
51906
+ ) : null,
51907
+ renderedColumns.map((column) => {
51908
+ const total = summaryTotals.find((candidate) => candidate.id === columnId(column));
51909
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
51910
+ "td",
51911
+ {
51912
+ className: cn(
51913
+ "px-2 py-2 align-middle",
51914
+ (column.compact || resolveColumnMarker(column) !== void 0) && "px-1",
51915
+ column.className,
51916
+ column.align === "center" && "text-center",
51917
+ column.align === "right" && "text-right"
51918
+ ),
51919
+ style: columnWidthVar(
51920
+ resolveColumnMarker(column) && column.width === void 0 ? 40 : column.width,
51921
+ windowedWidthFor(columnId(column), column.width),
51922
+ columnId(column),
51923
+ resizableColumns && column.resizable !== false
51924
+ ),
51925
+ children: [
51926
+ total?.value,
51927
+ total?.unavailable ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ErrorBox, { as: "span", className: "sr-only", message: `${columnId(column)} total is unavailable`, operation: "Total this column", origin: "design-system/MatrxDataTable" }) : null
51928
+ ]
51929
+ },
51930
+ columnId(column)
51931
+ );
51932
+ }),
51933
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51934
+ "td",
51935
+ {
51936
+ "aria-hidden": "true",
51937
+ "data-matrx-table-column-spacer": "end",
51938
+ className: "border-0 p-0",
51939
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
51940
+ },
51941
+ "matrx-summary-column-spacer-end"
51942
+ ) : null,
51943
+ showActionsCol ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", {}) : null
51944
+ ] })
51945
+ ] }) : null,
51946
+ footerRows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("tr", { children: [
51947
+ selection ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", { className: "px-2 py-2" }) : null,
51948
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51949
+ "td",
51950
+ {
51951
+ "aria-hidden": "true",
51952
+ "data-matrx-table-column-spacer": "start",
51953
+ className: "border-0 p-0",
51954
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
51955
+ },
51956
+ "matrx-column-spacer-start"
51957
+ ) : null,
51958
+ renderedColumns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51959
+ "td",
51960
+ {
51961
+ className: cn(
51962
+ "px-2 py-2 align-middle",
51963
+ (column.compact || resolveColumnMarker(column) !== void 0) && "px-1",
51964
+ column.className,
51965
+ column.align === "center" && "text-center",
51966
+ column.align === "right" && "text-right"
51967
+ ),
51968
+ style: columnWidthVar(
51969
+ resolveColumnMarker(column) && column.width === void 0 ? 40 : column.width,
51970
+ windowedWidthFor(columnId(column), column.width),
51971
+ columnId(column),
51972
+ resizableColumns && column.resizable !== false
51973
+ ),
51974
+ children: renderCell(row, column, index)
51975
+ },
51976
+ columnId(column)
51977
+ )),
51978
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
51979
+ "td",
51980
+ {
51981
+ "aria-hidden": "true",
51982
+ "data-matrx-table-column-spacer": "end",
51983
+ className: "border-0 p-0",
51984
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
51985
+ },
51986
+ "matrx-column-spacer-end"
51987
+ ) : null,
51988
+ showActionsCol ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("td", {}) : null
51989
+ ] }, `footer-${index}`))
51990
+ ] }) : null })
51849
51991
  ]
51850
51992
  }
51851
51993
  )
@@ -52011,6 +52153,7 @@ function MatrxDataTableCore(coreProps) {
52011
52153
  GenericTablePagination,
52012
52154
  {
52013
52155
  totalItems,
52156
+ ...readView.failure ? { countUnavailable: "failed" } : read?.status === "loading" && data.length === 0 ? { countUnavailable: "loading" } : {},
52014
52157
  ...paginationLabelFormat ? { labelFormat: paginationLabelFormat } : sourceTotal === void 0 ? {} : { labelFormat: (start, end, total) => `${(0, import_format3.formatCount)(start)}\u2013${(0, import_format3.formatCount)(end)} of ${(0, import_format3.formatCount)(total)} matching \xB7 ${(0, import_format3.formatCount)(data.length)} / ${(0, import_format3.formatCount)(sourceTotal)} loaded` },
52015
52158
  ...localProgressiveEnabled ? { progressive: {
52016
52159
  shownCount: progressiveRows.shownCount,
@@ -52692,7 +52835,7 @@ var DropdownMenu = DropdownMenuPrimitive.Root;
52692
52835
  var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
52693
52836
  var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
52694
52837
  var MENU_SURFACE_CLASS = "z-[10001] min-w-[8rem] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hidden overscroll-contain rounded-md border bg-popover p-1 text-popover-foreground";
52695
- var MENU_ITEM_CLASS = "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
52838
+ var MENU_ITEM_CLASS = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
52696
52839
  var MENU_INDICATOR_ITEM_CLASS = "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
52697
52840
  var DropdownMenuSubTrigger = React22.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
52698
52841
  DropdownMenuPrimitive.SubTrigger,