@ai-matrx/design-system 0.39.5 → 0.41.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 (45) hide show
  1. package/CHANGELOG.md +48 -5
  2. package/dist/data-table/copy-helpers.d.cts +2 -2
  3. package/dist/data-table/copy-helpers.d.ts +2 -2
  4. package/dist/data-table/filter-engine.d.cts +2 -2
  5. package/dist/data-table/filter-engine.d.ts +2 -2
  6. package/dist/data-table/host.d.cts +2 -2
  7. package/dist/data-table/host.d.ts +2 -2
  8. package/dist/data-table/index.cjs +308 -12
  9. package/dist/data-table/index.cjs.map +1 -1
  10. package/dist/data-table/index.d.cts +9 -4
  11. package/dist/data-table/index.d.ts +9 -4
  12. package/dist/data-table/index.js +308 -12
  13. package/dist/data-table/index.js.map +1 -1
  14. package/dist/data-table/infer-filter.d.cts +2 -2
  15. package/dist/data-table/infer-filter.d.ts +2 -2
  16. package/dist/data-table/layered-filters.d.cts +2 -2
  17. package/dist/data-table/layered-filters.d.ts +2 -2
  18. package/dist/data-table/query-control.d.cts +2 -2
  19. package/dist/data-table/query-control.d.ts +2 -2
  20. package/dist/data-table/types.cjs.map +1 -1
  21. package/dist/data-table/types.d.cts +2 -2
  22. package/dist/data-table/types.d.ts +2 -2
  23. package/dist/data-table/url-state.d.cts +2 -2
  24. package/dist/data-table/url-state.d.ts +2 -2
  25. package/dist/data-table/validation.d.cts +1 -1
  26. package/dist/data-table/validation.d.ts +1 -1
  27. package/dist/data-table/xlsx.d.cts +2 -2
  28. package/dist/data-table/xlsx.d.ts +2 -2
  29. package/dist/field-formats/index.cjs +269 -5
  30. package/dist/field-formats/index.cjs.map +1 -1
  31. package/dist/field-formats/index.d.cts +2 -2
  32. package/dist/field-formats/index.d.ts +2 -2
  33. package/dist/field-formats/index.js +269 -5
  34. package/dist/field-formats/index.js.map +1 -1
  35. package/dist/formulas/index.cjs +38 -5
  36. package/dist/formulas/index.cjs.map +1 -1
  37. package/dist/formulas/index.d.cts +39 -2
  38. package/dist/formulas/index.d.ts +39 -2
  39. package/dist/formulas/index.js +38 -5
  40. package/dist/formulas/index.js.map +1 -1
  41. package/dist/{types-D7O3uOIs.d.cts → types-DNkbHRVK.d.cts} +10 -1
  42. package/dist/{types-p13OrDj-.d.ts → types-Dx5hJgCi.d.ts} +10 -1
  43. package/dist/{types-CJWU1Gae.d.cts → types-JnZD8wM0.d.cts} +27 -2
  44. package/dist/{types-CJWU1Gae.d.ts → types-JnZD8wM0.d.ts} +27 -2
  45. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { h as MatrxDataTableProps, i as TableViewSnapshot, d as MatrxDataTableRecordControls, j as ColumnMarker, M as MatrxColumnDef } from '../types-D7O3uOIs.cjs';
4
- export { A as AnyOfColumnSearch, k as CellEditType, l as CellEditsMap, f as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, m as MatrxDataTableContextMenuConfig, g as MatrxDataTableCopyConfig, n as MatrxDataTableCoverageConfig, c as MatrxDataTableDensity, o as MatrxDataTableDetailConfig, p as MatrxDataTableEditConfig, q as MatrxDataTableEmptyState, r as MatrxDataTableExpandedDetailConfig, s as MatrxDataTableFacetsConfig, t as MatrxDataTableFilterUi, u as MatrxDataTableGroupingConfig, v as MatrxDataTableHierarchyConfig, w as MatrxDataTableHierarchyMove, x as MatrxDataTableMobileCardControls, y as MatrxDataTableMultiExpandedDetailConfig, z as MatrxDataTableQueryControl, e as MatrxDataTableQueryState, B as MatrxDataTableSelectionConfig, D as MatrxDataTableSingleExpandedDetailConfig, E as MatrxDataTableSourceProcessing, F as MatrxDataTableSpreadsheetConfig, G as MatrxDataTableStyleConfig, H as MatrxDataTableToolbar, I as MatrxDataTableUrlStateConfig, J as MatrxDataTableVirtualizeConfig, K as MatrxDataTableWindowConfig, N as MatrxTablePagination, O as SortDirection, S as SortState, P as TableRankedSearchOptions, b as TableSavedViewsProps, T as TableSearchMatchMode, Q as TableSearchRole, R as TableSearchScope, U as TableViewQuery, V as TextFilterMode, W as ToolbarFacet, X as inferTableSearchRole, Y as rankTableSearchRows, Z as resolveTableSearchScopes } from '../types-D7O3uOIs.cjs';
3
+ import { h as MatrxDataTableProps, i as TableViewSnapshot, d as MatrxDataTableRecordControls, j as ColumnMarker, M as MatrxColumnDef } from '../types-DNkbHRVK.cjs';
4
+ export { A as AnyOfColumnSearch, k as CellEditType, l as CellEditsMap, f as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, m as MatrxDataTableContextMenuConfig, g as MatrxDataTableCopyConfig, n as MatrxDataTableCoverageConfig, c as MatrxDataTableDensity, o as MatrxDataTableDetailConfig, p as MatrxDataTableEditConfig, q as MatrxDataTableEmptyState, r as MatrxDataTableExpandedDetailConfig, s as MatrxDataTableFacetsConfig, t as MatrxDataTableFilterUi, u as MatrxDataTableGroupingConfig, v as MatrxDataTableHierarchyConfig, w as MatrxDataTableHierarchyMove, x as MatrxDataTableMobileCardControls, y as MatrxDataTableMultiExpandedDetailConfig, z as MatrxDataTableQueryControl, e as MatrxDataTableQueryState, B as MatrxDataTableSelectionConfig, D as MatrxDataTableSingleExpandedDetailConfig, E as MatrxDataTableSourceProcessing, F as MatrxDataTableSpreadsheetConfig, G as MatrxDataTableStyleConfig, H as MatrxDataTableToolbar, I as MatrxDataTableUrlStateConfig, J as MatrxDataTableVirtualizeConfig, K as MatrxDataTableWindowConfig, N as MatrxTablePagination, O as SortDirection, S as SortState, P as TableRankedSearchOptions, b as TableSavedViewsProps, T as TableSearchMatchMode, Q as TableSearchRole, R as TableSearchScope, U as TableViewQuery, V as TextFilterMode, W as ToolbarFacet, X as inferTableSearchRole, Y as rankTableSearchRows, Z as resolveTableSearchScopes } from '../types-DNkbHRVK.cjs';
5
5
  export { MatrxDataTableHost, MatrxDataTableProvider, MatrxTableRowAlchemy, TableDoors, TableEntityRefProps, TableLinkProps, TablePeekProps, TableSidePanelProps, TableToolbarActionProps, TableWindowPanelProps, useMatrxDataTableHost, useMatrxTableRowAlchemy } from './host.cjs';
6
6
  import { CopyControlProps } from './copy-types.cjs';
7
7
  export { AgentPayloadInput } from './copy-types.cjs';
@@ -16,7 +16,7 @@ export { DateColumnFilterValue, DateFilterBounds, TIME_RANGE_PRESETS, TIME_RANGE
16
16
  export { ScrollPaginationContainer, UseScrollPaginationOptions, UseScrollPaginationResult, useScrollPagination } from './scroll-pagination.cjs';
17
17
  export { tableUrlParamPrefix, useTableUrlState } from './url-state.cjs';
18
18
  export { BOOLEAN_TRUE_COLOR, CELL_TINT_CLASS, COLOR_RULE_OPS, COLOR_RULE_OP_LABELS, ChoiceColorLookup, ColorBy, ColorRule, ColorRuleOp, EMPTY_TABLE_STYLE, OPS_WITH_VALUE, ROW_TINT_CLASS, STYLE_COLORS, STYLE_COLOR_LABELS, SWATCH_CLASS, StyleColor, StylePath, StyleTarget, TABLE_STYLE_METADATA_KEY, TABLE_STYLE_VERSION, TableStyle, applyStylePath, colorForChoice, evaluateRule, isStyleColor, parseTableStyle, resolveCellColor, resolveRowColor, stylePath, tableStyleFromMetadata, tableStyleIsEmpty } from './table-style.cjs';
19
- import { c as FormatResult } from '../types-CJWU1Gae.cjs';
19
+ import { c as FormatResult } from '../types-JnZD8wM0.cjs';
20
20
  export { EMPTY_GROUP_KEY, GroupAggregateColumn, GroupAggregateKind, GroupAggregateResult, GroupAggregateSpec, GroupOrder, GroupRowsOptions, GroupedRenderRow, TableGroup, coerceGroupNumber, flattenGroups, formatGroupNumber, groupKeyOf, groupRows, groupSummary, isBlankGroupValue, isGroupCollapsed, toggleGroupCollapsed } from './grouping.cjs';
21
21
  export { DEFAULT_OVERSCAN, ScrollToIndexInput, VariableWindowInput, WindowInput, WindowResult, computeVariableWindow, computeWindow, scrollOffsetForIndex } from './virtual-window.cjs';
22
22
  export { CoverageInput, CoverageResult, CoverageScope, PARTIAL_BADGE, UNKNOWN_TOTAL_BADGE, describeCoverage } from './coverage.cjs';
@@ -56,11 +56,16 @@ interface TableTitleRowProps {
56
56
  tabs?: ReactNode;
57
57
  actions?: ReactNode;
58
58
  children?: ReactNode;
59
+ /**
60
+ * One row at every width: nothing wraps onto a second line; the row scrolls sideways on a
61
+ * narrow screen instead (the search keeps its place in the row).
62
+ */
63
+ singleRow?: boolean;
59
64
  className?: string;
60
65
  }
61
66
  /** Shared responsive title composition. Hosts retain their existing query,
62
67
  * navigation and persistence controllers; this component owns layout only. */
63
- declare function TableTitleRow({ title, headingLevel, context, count, search, tabs, actions, children, className, }: TableTitleRowProps): React.JSX.Element;
68
+ declare function TableTitleRow({ title, headingLevel, context, count, search, tabs, actions, children, singleRow, className, }: TableTitleRowProps): React.JSX.Element;
64
69
 
65
70
  interface SavedViewsControlProps {
66
71
  views: ReadonlyArray<{
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { h as MatrxDataTableProps, i as TableViewSnapshot, d as MatrxDataTableRecordControls, j as ColumnMarker, M as MatrxColumnDef } from '../types-p13OrDj-.js';
4
- export { A as AnyOfColumnSearch, k as CellEditType, l as CellEditsMap, f as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, m as MatrxDataTableContextMenuConfig, g as MatrxDataTableCopyConfig, n as MatrxDataTableCoverageConfig, c as MatrxDataTableDensity, o as MatrxDataTableDetailConfig, p as MatrxDataTableEditConfig, q as MatrxDataTableEmptyState, r as MatrxDataTableExpandedDetailConfig, s as MatrxDataTableFacetsConfig, t as MatrxDataTableFilterUi, u as MatrxDataTableGroupingConfig, v as MatrxDataTableHierarchyConfig, w as MatrxDataTableHierarchyMove, x as MatrxDataTableMobileCardControls, y as MatrxDataTableMultiExpandedDetailConfig, z as MatrxDataTableQueryControl, e as MatrxDataTableQueryState, B as MatrxDataTableSelectionConfig, D as MatrxDataTableSingleExpandedDetailConfig, E as MatrxDataTableSourceProcessing, F as MatrxDataTableSpreadsheetConfig, G as MatrxDataTableStyleConfig, H as MatrxDataTableToolbar, I as MatrxDataTableUrlStateConfig, J as MatrxDataTableVirtualizeConfig, K as MatrxDataTableWindowConfig, N as MatrxTablePagination, O as SortDirection, S as SortState, P as TableRankedSearchOptions, b as TableSavedViewsProps, T as TableSearchMatchMode, Q as TableSearchRole, R as TableSearchScope, U as TableViewQuery, V as TextFilterMode, W as ToolbarFacet, X as inferTableSearchRole, Y as rankTableSearchRows, Z as resolveTableSearchScopes } from '../types-p13OrDj-.js';
3
+ import { h as MatrxDataTableProps, i as TableViewSnapshot, d as MatrxDataTableRecordControls, j as ColumnMarker, M as MatrxColumnDef } from '../types-Dx5hJgCi.js';
4
+ export { A as AnyOfColumnSearch, k as CellEditType, l as CellEditsMap, f as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, m as MatrxDataTableContextMenuConfig, g as MatrxDataTableCopyConfig, n as MatrxDataTableCoverageConfig, c as MatrxDataTableDensity, o as MatrxDataTableDetailConfig, p as MatrxDataTableEditConfig, q as MatrxDataTableEmptyState, r as MatrxDataTableExpandedDetailConfig, s as MatrxDataTableFacetsConfig, t as MatrxDataTableFilterUi, u as MatrxDataTableGroupingConfig, v as MatrxDataTableHierarchyConfig, w as MatrxDataTableHierarchyMove, x as MatrxDataTableMobileCardControls, y as MatrxDataTableMultiExpandedDetailConfig, z as MatrxDataTableQueryControl, e as MatrxDataTableQueryState, B as MatrxDataTableSelectionConfig, D as MatrxDataTableSingleExpandedDetailConfig, E as MatrxDataTableSourceProcessing, F as MatrxDataTableSpreadsheetConfig, G as MatrxDataTableStyleConfig, H as MatrxDataTableToolbar, I as MatrxDataTableUrlStateConfig, J as MatrxDataTableVirtualizeConfig, K as MatrxDataTableWindowConfig, N as MatrxTablePagination, O as SortDirection, S as SortState, P as TableRankedSearchOptions, b as TableSavedViewsProps, T as TableSearchMatchMode, Q as TableSearchRole, R as TableSearchScope, U as TableViewQuery, V as TextFilterMode, W as ToolbarFacet, X as inferTableSearchRole, Y as rankTableSearchRows, Z as resolveTableSearchScopes } from '../types-Dx5hJgCi.js';
5
5
  export { MatrxDataTableHost, MatrxDataTableProvider, MatrxTableRowAlchemy, TableDoors, TableEntityRefProps, TableLinkProps, TablePeekProps, TableSidePanelProps, TableToolbarActionProps, TableWindowPanelProps, useMatrxDataTableHost, useMatrxTableRowAlchemy } from './host.js';
6
6
  import { CopyControlProps } from './copy-types.js';
7
7
  export { AgentPayloadInput } from './copy-types.js';
@@ -16,7 +16,7 @@ export { DateColumnFilterValue, DateFilterBounds, TIME_RANGE_PRESETS, TIME_RANGE
16
16
  export { ScrollPaginationContainer, UseScrollPaginationOptions, UseScrollPaginationResult, useScrollPagination } from './scroll-pagination.js';
17
17
  export { tableUrlParamPrefix, useTableUrlState } from './url-state.js';
18
18
  export { BOOLEAN_TRUE_COLOR, CELL_TINT_CLASS, COLOR_RULE_OPS, COLOR_RULE_OP_LABELS, ChoiceColorLookup, ColorBy, ColorRule, ColorRuleOp, EMPTY_TABLE_STYLE, OPS_WITH_VALUE, ROW_TINT_CLASS, STYLE_COLORS, STYLE_COLOR_LABELS, SWATCH_CLASS, StyleColor, StylePath, StyleTarget, TABLE_STYLE_METADATA_KEY, TABLE_STYLE_VERSION, TableStyle, applyStylePath, colorForChoice, evaluateRule, isStyleColor, parseTableStyle, resolveCellColor, resolveRowColor, stylePath, tableStyleFromMetadata, tableStyleIsEmpty } from './table-style.js';
19
- import { c as FormatResult } from '../types-CJWU1Gae.js';
19
+ import { c as FormatResult } from '../types-JnZD8wM0.js';
20
20
  export { EMPTY_GROUP_KEY, GroupAggregateColumn, GroupAggregateKind, GroupAggregateResult, GroupAggregateSpec, GroupOrder, GroupRowsOptions, GroupedRenderRow, TableGroup, coerceGroupNumber, flattenGroups, formatGroupNumber, groupKeyOf, groupRows, groupSummary, isBlankGroupValue, isGroupCollapsed, toggleGroupCollapsed } from './grouping.js';
21
21
  export { DEFAULT_OVERSCAN, ScrollToIndexInput, VariableWindowInput, WindowInput, WindowResult, computeVariableWindow, computeWindow, scrollOffsetForIndex } from './virtual-window.js';
22
22
  export { CoverageInput, CoverageResult, CoverageScope, PARTIAL_BADGE, UNKNOWN_TOTAL_BADGE, describeCoverage } from './coverage.js';
@@ -56,11 +56,16 @@ interface TableTitleRowProps {
56
56
  tabs?: ReactNode;
57
57
  actions?: ReactNode;
58
58
  children?: ReactNode;
59
+ /**
60
+ * One row at every width: nothing wraps onto a second line; the row scrolls sideways on a
61
+ * narrow screen instead (the search keeps its place in the row).
62
+ */
63
+ singleRow?: boolean;
59
64
  className?: string;
60
65
  }
61
66
  /** Shared responsive title composition. Hosts retain their existing query,
62
67
  * navigation and persistence controllers; this component owns layout only. */
63
- declare function TableTitleRow({ title, headingLevel, context, count, search, tabs, actions, children, className, }: TableTitleRowProps): React.JSX.Element;
68
+ declare function TableTitleRow({ title, headingLevel, context, count, search, tabs, actions, children, singleRow, className, }: TableTitleRowProps): React.JSX.Element;
64
69
 
65
70
  interface SavedViewsControlProps {
66
71
  views: ReadonlyArray<{
@@ -13,6 +13,7 @@ import {
13
13
  useRef as useRef16,
14
14
  useState as useState22
15
15
  } from "react";
16
+ import { createPortal as createPortal2 } from "react-dom";
16
17
  import {
17
18
  DndContext,
18
19
  DragOverlay,
@@ -941,6 +942,7 @@ function TableTitleRow({
941
942
  tabs,
942
943
  actions,
943
944
  children,
945
+ singleRow = false,
944
946
  className
945
947
  }) {
946
948
  const Heading = headingLevel === 1 ? "h1" : "h2";
@@ -949,7 +951,8 @@ function TableTitleRow({
949
951
  {
950
952
  "data-matrx-table-toolbar": true,
951
953
  className: cn(
952
- "relative flex min-w-0 shrink-0 flex-wrap items-center gap-2",
954
+ "relative flex min-w-0 shrink-0 items-center gap-2",
955
+ singleRow ? "flex-nowrap overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" : "flex-wrap",
953
956
  className
954
957
  ),
955
958
  children: [
@@ -968,7 +971,7 @@ function TableTitleRow({
968
971
  "div",
969
972
  {
970
973
  "data-matrx-table-title-search": true,
971
- className: "order-last w-full min-w-0 sm:order-none sm:w-64 sm:shrink-0 sm:max-w-md",
974
+ className: singleRow ? "w-44 min-w-[9rem] shrink-0 sm:w-64 sm:max-w-md" : "order-last w-full min-w-0 sm:order-none sm:w-64 sm:shrink-0 sm:max-w-md",
972
975
  children: search
973
976
  }
974
977
  ) : null,
@@ -978,7 +981,7 @@ function TableTitleRow({
978
981
  "div",
979
982
  {
980
983
  "data-matrx-table-actions": true,
981
- className: "ml-auto flex min-w-0 flex-wrap items-center gap-0",
984
+ className: cn("ml-auto flex min-w-0 items-center gap-0", singleRow ? "shrink-0 flex-nowrap" : "flex-wrap"),
982
985
  children: actions
983
986
  }
984
987
  ) : null
@@ -10184,6 +10187,35 @@ function toText(value) {
10184
10187
  return String(value);
10185
10188
  return null;
10186
10189
  }
10190
+ function parseTimeOfDay(value) {
10191
+ if (value instanceof Date) {
10192
+ return { h: value.getHours(), m: value.getMinutes(), s: value.getSeconds() };
10193
+ }
10194
+ if (typeof value !== "string") return null;
10195
+ const text = value.trim();
10196
+ if (!text) return null;
10197
+ const m = /^(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?\s*([aApP]\.?[mM]\.?)?$/.exec(text);
10198
+ if (m) {
10199
+ let h = Number(m[1]);
10200
+ const min = m[2] ? Number(m[2]) : 0;
10201
+ const sec = m[3] ? Number(m[3]) : 0;
10202
+ const ampm = m[4]?.toLowerCase().replace(/\./g, "");
10203
+ if (ampm) {
10204
+ if (h < 1 || h > 12) return null;
10205
+ if (ampm === "pm" && h !== 12) h += 12;
10206
+ if (ampm === "am" && h === 12) h = 0;
10207
+ }
10208
+ if (h > 23 || min > 59 || sec > 59) return null;
10209
+ return { h, m: min, s: sec };
10210
+ }
10211
+ if (/\d{4}-\d{2}-\d{2}T/.test(text)) {
10212
+ const d = new Date(text);
10213
+ if (!Number.isNaN(d.getTime())) {
10214
+ return { h: d.getHours(), m: d.getMinutes(), s: d.getSeconds() };
10215
+ }
10216
+ }
10217
+ return null;
10218
+ }
10187
10219
  var BARE_CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]00:00(?::00(?:\.0+)?)?)?$/;
10188
10220
  function toDate(value) {
10189
10221
  if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
@@ -10304,6 +10336,23 @@ var DEFS = [
10304
10336
  },
10305
10337
  parse: (raw) => raw === "" || raw == null ? null : String(raw).trim()
10306
10338
  },
10339
+ {
10340
+ // A postal address. Free text by design (addresses have no one shape
10341
+ // worldwide); the cell opens it in maps, which is the thing people do
10342
+ // with an address in a table.
10343
+ id: "address",
10344
+ label: "Address",
10345
+ description: "Street address \u2014 click to open in maps",
10346
+ group: "Text",
10347
+ base: "string",
10348
+ editor: "text",
10349
+ rich: true,
10350
+ format: (v) => {
10351
+ const t = toText(v)?.trim();
10352
+ return t ? t : null;
10353
+ },
10354
+ parse: (raw) => raw === "" || raw == null ? null : String(raw)
10355
+ },
10307
10356
  {
10308
10357
  id: "url",
10309
10358
  label: "Link",
@@ -10437,6 +10486,26 @@ var DEFS = [
10437
10486
  },
10438
10487
  parse: (raw) => toNumber(raw)
10439
10488
  },
10489
+ {
10490
+ // A percentage DRAWN as a bar — task completion, quota used, a score out of
10491
+ // 100. Same storage and scale rule as `percent`; only the rendering differs.
10492
+ id: "progress",
10493
+ label: "Progress bar",
10494
+ description: "A percentage shown as a bar",
10495
+ group: "Numbers",
10496
+ base: "number",
10497
+ alsoAccepts: ["integer"],
10498
+ editor: "number",
10499
+ rich: true,
10500
+ optionKeys: ["percentScale"],
10501
+ format: (v, o) => {
10502
+ const n2 = toNumber(v);
10503
+ if (n2 === null) return null;
10504
+ const shown = o.percentScale === "fraction" ? n2 * 100 : n2;
10505
+ return `${groupedNumber(shown, { precision: 0 })}%`;
10506
+ },
10507
+ parse: (raw) => toNumber(raw)
10508
+ },
10440
10509
  {
10441
10510
  id: "duration",
10442
10511
  label: "Duration",
@@ -10505,13 +10574,16 @@ var DEFS = [
10505
10574
  alsoAccepts: ["number"],
10506
10575
  editor: "number",
10507
10576
  numericAlign: true,
10577
+ // The name says BYTES on purpose: this cell holds a genuine byte length,
10578
+ // never a character or item count, and `formatFileSize` is only honest
10579
+ // about a quantity that is actually bytes.
10508
10580
  format: (v) => {
10509
- const n2 = toNumber(v);
10510
- return n2 === null ? null : formatFileSize(n2);
10581
+ const byteLength = toNumber(v);
10582
+ return byteLength === null ? null : formatFileSize(byteLength);
10511
10583
  },
10512
10584
  parse: (raw) => {
10513
- const n2 = toNumber(raw);
10514
- return n2 === null ? null : Math.trunc(n2);
10585
+ const byteLength = toNumber(raw);
10586
+ return byteLength === null ? null : Math.trunc(byteLength);
10515
10587
  }
10516
10588
  },
10517
10589
  // ── Choice ────────────────────────────────────────────────────────────────
@@ -10572,6 +10644,87 @@ var DEFS = [
10572
10644
  },
10573
10645
  parse: (raw) => raw === "" || raw == null ? null : String(raw)
10574
10646
  },
10647
+ {
10648
+ // A TIME OF DAY with no date: opening hours, a shift start, a meeting slot.
10649
+ // Stored as the string the <input type="time"> gives ("HH:MM" or
10650
+ // "HH:MM:SS"), shown in the viewer's locale ("2:30 PM" / "14:30"). The
10651
+ // storage type stays `string`; sorting is lexical on the 24-hour form,
10652
+ // which is chronological — the one reason the stored form is 24-hour.
10653
+ id: "time",
10654
+ label: "Time",
10655
+ description: "Time of day, without a date",
10656
+ group: "Dates",
10657
+ base: "string",
10658
+ editor: "time",
10659
+ optionKeys: ["timeSeconds"],
10660
+ format: (v, o) => {
10661
+ const parts = parseTimeOfDay(v);
10662
+ if (!parts) return null;
10663
+ const d = new Date(2e3, 0, 1, parts.h, parts.m, parts.s);
10664
+ return d.toLocaleTimeString(void 0, {
10665
+ hour: "numeric",
10666
+ minute: "2-digit",
10667
+ ...o.timeSeconds ? { second: "2-digit" } : {}
10668
+ });
10669
+ },
10670
+ // Normalise to the 24-hour "HH:MM[:SS]" form; a value that is not a time
10671
+ // is handed back unchanged so the validation layer can refuse it by name.
10672
+ parse: (raw) => {
10673
+ if (raw == null || raw === "") return null;
10674
+ const parts = parseTimeOfDay(raw);
10675
+ if (!parts) return raw;
10676
+ const two = (n2) => String(n2).padStart(2, "0");
10677
+ const fields = parts.s > 0 ? [parts.h, parts.m, parts.s] : [parts.h, parts.m];
10678
+ return fields.map(two).join(":");
10679
+ }
10680
+ },
10681
+ {
10682
+ // Assigned BY THE DATABASE when the row is inserted (trigger
10683
+ // `_udt_autonumber`), so it is the same number whichever door made the row
10684
+ // and never shifts when another row is deleted. Stored in the cell as an
10685
+ // integer; every editor refuses it. `prefix` turns 42 into "INV-42".
10686
+ id: "autonumber",
10687
+ label: "Autonumber",
10688
+ description: "A number the table assigns to each new row \u2014 never typed",
10689
+ group: "Numbers",
10690
+ base: "integer",
10691
+ alsoAccepts: ["number"],
10692
+ editor: "computed",
10693
+ numericAlign: true,
10694
+ optionKeys: ["prefix"],
10695
+ format: (v, o) => {
10696
+ const n2 = toNumber(v);
10697
+ if (n2 === null) return null;
10698
+ return `${o.prefix ?? ""}${Math.trunc(n2)}`;
10699
+ },
10700
+ parse: () => null
10701
+ },
10702
+ // ── System columns ────────────────────────────────────────────────────────
10703
+ // Filled from the ROW'S OWN RECORD on read (`withComputedColumns` in
10704
+ // `@ai-matrx/design-system/formulas`), never typed and never stored in the
10705
+ // cell. `editor: "computed"` makes every editor refuse them.
10706
+ {
10707
+ id: "created_time",
10708
+ label: "Created time",
10709
+ description: "When the row was added \u2014 filled in automatically",
10710
+ group: "Dates",
10711
+ base: "string",
10712
+ editor: "computed",
10713
+ optionKeys: ["dateStyle"],
10714
+ format: (v, o) => FIELD_FORMATS.datetime.format(v, o),
10715
+ parse: () => null
10716
+ },
10717
+ {
10718
+ id: "modified_time",
10719
+ label: "Last modified time",
10720
+ description: "When the row last changed \u2014 filled in automatically",
10721
+ group: "Dates",
10722
+ base: "string",
10723
+ editor: "computed",
10724
+ optionKeys: ["dateStyle"],
10725
+ format: (v, o) => FIELD_FORMATS.datetime.format(v, o),
10726
+ parse: () => null
10727
+ },
10575
10728
  {
10576
10729
  id: "relative_time",
10577
10730
  label: "Relative time",
@@ -10628,6 +10781,44 @@ var DEFS = [
10628
10781
  return toList(raw) ?? raw;
10629
10782
  }
10630
10783
  },
10784
+ {
10785
+ // FILES ATTACHED TO THE ROW. The cell stores the files feature's file ids
10786
+ // (`files.files.id`), never a URL or a name: a rename or a move in Files
10787
+ // changes nothing here, and access is the file's own (RLS) — a reader who
10788
+ // may see the row but not the file gets the chip's honest "no access", not
10789
+ // a broken link. Drawn by `AttachmentChips`, edited by `AttachmentInput`
10790
+ // through the ONE file picker.
10791
+ id: "attachment",
10792
+ label: "Attachments",
10793
+ description: "Files from your Files, shown as chips",
10794
+ group: "Structured",
10795
+ base: "array",
10796
+ // Not `string`: on a text column it must appear under "Other kinds" so
10797
+ // picking it retypes the column to a list, rather than storing a JSON
10798
+ // string of ids in a text cell.
10799
+ alsoAccepts: ["json"],
10800
+ editor: "attachment",
10801
+ rich: true,
10802
+ format: (v) => {
10803
+ const list = toList(v);
10804
+ if (list === null) return null;
10805
+ const n2 = list.filter((i) => String(i).trim()).length;
10806
+ return n2 === 0 ? "" : n2 === 1 ? "1 file" : `${n2} files`;
10807
+ },
10808
+ parse: (raw) => {
10809
+ if (raw == null || raw === "") return null;
10810
+ if (Array.isArray(raw)) return raw.map((i) => String(i).trim()).filter(Boolean);
10811
+ const text = String(raw).trim();
10812
+ if (text.startsWith("[")) {
10813
+ try {
10814
+ const parsed = JSON.parse(text);
10815
+ if (Array.isArray(parsed)) return parsed.map((i) => String(i).trim()).filter(Boolean);
10816
+ } catch {
10817
+ }
10818
+ }
10819
+ return text.split(",").map((s) => s.trim()).filter(Boolean);
10820
+ }
10821
+ },
10631
10822
  {
10632
10823
  id: "tags",
10633
10824
  label: "Tags",
@@ -10691,6 +10882,82 @@ var DEFS = [
10691
10882
  return trimmed === "" ? null : trimmed;
10692
10883
  }
10693
10884
  },
10885
+ {
10886
+ // A PERSON: one member of the organization the table belongs to, stored as
10887
+ // the user id. It is a choice column whose options are the members —
10888
+ // supplied at runtime by whoever renders it (features/data-tables/
10889
+ // person-choices.tsx), the same door a structured-list-bound column uses —
10890
+ // so the cell picker, the chip, filters, color-by and the agent scope are
10891
+ // all the choice system's, with no second implementation.
10892
+ id: "person",
10893
+ label: "Person",
10894
+ description: "Someone in your organization \u2014 pick from its members",
10895
+ group: "Choice",
10896
+ base: "string",
10897
+ editor: "select",
10898
+ rich: true,
10899
+ format: (v, o) => FIELD_FORMATS.choice.format(v, o),
10900
+ parse: (raw) => FIELD_FORMATS.choice.parse(raw, {})
10901
+ },
10902
+ {
10903
+ // A RECORD OF ANOTHER TABLE. The cell stores that record's ID; the screen
10904
+ // shows its WORDS. `person` is the precedent and the template — it has
10905
+ // stored a user id and displayed a name through `choiceMap` since
10906
+ // 2026-09-21 — so `relation` is a member of CHOICE_FORMAT_IDS and every
10907
+ // one of the ten readers that print a cell resolves it through the path
10908
+ // they already use for `person`. There is no eleventh place to remember.
10909
+ //
10910
+ // THREE STATES, THREE RENDERINGS, NO FOURTH (OLD-TABLES-CUTOVER §3.3):
10911
+ // resolved the target's words
10912
+ // unresolvable the amber fallback WITH the id, as an identifier — never
10913
+ // a blank, and never the bare uuid as if it were text, so a
10914
+ // person can act on it and support can trace it
10915
+ // withheld platform.relation_withheld_label(), with NO id — a cell
10916
+ // the reader may not see never leaks the identifier through
10917
+ // the gap between the two stores' refusals
10918
+ //
10919
+ // `format` therefore returns null (the fallback law's "cannot interpret")
10920
+ // for anything the resolver did not turn into words, and the renderer is
10921
+ // what distinguishes unresolvable from withheld. It never prints a bare id.
10922
+ id: "relation",
10923
+ label: "Relation",
10924
+ description: "A record of another table \u2014 pick it, and its name is shown",
10925
+ group: "Choice",
10926
+ base: "string",
10927
+ alsoAccepts: ["array", "json"],
10928
+ editor: "select",
10929
+ rich: true,
10930
+ optionKeys: ["relation_target", "relation_max", "on_delete", "display"],
10931
+ // IT DOES NOT DELEGATE TO `choice`, AND THAT IS THE WHOLE POINT.
10932
+ // `choice.format` deliberately returns the RAW VALUE when no options have
10933
+ // resolved yet — a structured list still loading should not make every cell
10934
+ // look off-list. For a label column that is right: the raw value IS the
10935
+ // label. For an identifier column it is the defect §3.3 names, and it is
10936
+ // how the first cut of this wave put four raw uuids on a real grid: the
10937
+ // fallback arm never ran, because returning the uuid counted as SUCCESS.
10938
+ //
10939
+ // Here, a value that did not resolve to words is a MISMATCH — `null` — so
10940
+ // the renderer's three-state arm owns it and shows the amber fallback with
10941
+ // the id marked as an identifier, or the withheld sentence. The id never
10942
+ // reaches the eye as if it were the cell's contents.
10943
+ format: (v, options) => {
10944
+ const list = toList(v);
10945
+ const values = list !== null ? list : v == null ? [] : [v];
10946
+ if (values.length === 0) return "";
10947
+ const words2 = [];
10948
+ for (const entry of values) {
10949
+ const text = toText(entry);
10950
+ if (text === null) return null;
10951
+ const trimmed = text.trim();
10952
+ if (trimmed === "") continue;
10953
+ const match = findChoice(options.choices, trimmed);
10954
+ if (!match) return null;
10955
+ words2.push(match.label ?? match.value);
10956
+ }
10957
+ return words2.length === 0 ? "" : words2.join(", ");
10958
+ },
10959
+ parse: (raw) => FIELD_FORMATS.choice.parse(raw, {})
10960
+ },
10694
10961
  {
10695
10962
  id: "multi_choice",
10696
10963
  label: "Multi-choice",
@@ -10735,7 +11002,7 @@ var DEFS = [
10735
11002
  // A formula column stores NOTHING. `metadata.format.options.formula` holds
10736
11003
  // the expression; the stored cell is always null; the value is computed on
10737
11004
  // read from the other columns of the same row
10738
- // (`features/data-tables/formulas.ts`). Grouped under Structured on purpose
11005
+ // (`@ai-matrx/design-system/formulas`). Grouped under Structured on purpose
10739
11006
  // — the picker's groups are a fixed vocabulary, and a formula is structure
10740
11007
  // over a plain string column, not a sixth family.
10741
11008
  //
@@ -13429,6 +13696,11 @@ function MatrxDataTableCore({
13429
13696
  const rowCopyInMenu = rowCopyPlacement === "menu" && Boolean(rowActions);
13430
13697
  const showInlineRowCopy = showRowCopy && !rowCopyInMenu;
13431
13698
  const showToolbarCopy = effectiveCopy !== void 0 && effectiveCopy.showToolbar !== false;
13699
+ const placeToolbar = (node) => {
13700
+ if (!toolbar || !("portalInto" in toolbar)) return node;
13701
+ const target = toolbar.portalInto;
13702
+ return target ? createPortal2(node, target) : null;
13703
+ };
13432
13704
  const renderRowActions = (row, controls) => {
13433
13705
  const actions = rowActions?.(row, controls);
13434
13706
  const scopedActions = /* @__PURE__ */ jsx42(MatrxTableRowControlsScope, { value: controls, children: actions });
@@ -14256,9 +14528,10 @@ function MatrxDataTableCore({
14256
14528
  notice,
14257
14529
  savedViewError ? /* @__PURE__ */ jsx42("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: savedViewError }) : null,
14258
14530
  toolbarActionError ? /* @__PURE__ */ jsx42("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: toolbarActionError }) : null,
14259
- !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__ */ jsxs32(
14531
+ 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__ */ jsxs32(
14260
14532
  TableTitleRow,
14261
14533
  {
14534
+ ...toolbar?.singleRow ? { singleRow: true } : {},
14262
14535
  tabs: viewTabs ? /* @__PURE__ */ jsx42(TableViewWorkspace, { snapshot: currentViewSnapshot, onApply: applySavedView, related: relatedTableActions }, tableId ?? "table") : void 0,
14263
14536
  ...toolbar?.title === void 0 ? {} : { title: toolbar.title },
14264
14537
  ...toolbar?.titleCount === void 0 ? {} : { count: toolbar.titleCount },
@@ -14393,8 +14666,31 @@ function MatrxDataTableCore({
14393
14666
  "div",
14394
14667
  {
14395
14668
  "data-matrx-table-actions": true,
14396
- className: "ml-auto flex min-w-0 flex-wrap items-center gap-0",
14669
+ className: cn(
14670
+ "ml-auto flex min-w-0 items-center gap-0",
14671
+ toolbar?.singleRow ? "shrink-0 flex-nowrap" : "flex-wrap"
14672
+ ),
14397
14673
  children: [
14674
+ toolbar?.singleRow && coverageResult && coverageResult.notice ? /* @__PURE__ */ jsxs32(Tooltip, { children: [
14675
+ /* @__PURE__ */ jsx42(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs32(
14676
+ "span",
14677
+ {
14678
+ "data-matrx-table-coverage": true,
14679
+ "data-matrx-table-coverage-scope": coverageResult.scope,
14680
+ role: "status",
14681
+ tabIndex: 0,
14682
+ className: "mr-1 inline-flex shrink-0 items-center whitespace-nowrap rounded-md border border-amber-300/70 bg-amber-50 px-2 py-0.5 text-[11px] font-semibold text-amber-900 dark:border-amber-500/40 dark:bg-amber-950/40 dark:text-amber-200",
14683
+ children: [
14684
+ coverageResult.badge,
14685
+ /* @__PURE__ */ jsxs32("span", { className: "sr-only", children: [
14686
+ ": ",
14687
+ coverageResult.notice
14688
+ ] })
14689
+ ]
14690
+ }
14691
+ ) }),
14692
+ /* @__PURE__ */ jsx42(TooltipContent, { className: "max-w-xs", children: coverageResult.notice })
14693
+ ] }) : null,
14398
14694
  /* @__PURE__ */ jsx42(
14399
14695
  TableToolbarAction,
14400
14696
  {
@@ -14537,9 +14833,9 @@ function MatrxDataTableCore({
14537
14833
  )
14538
14834
  ]
14539
14835
  }
14540
- ),
14836
+ )),
14541
14837
  /* @__PURE__ */ jsxs32("div", { "data-matrx-table-body-region": true, className: "flex min-h-0 flex-1 flex-col gap-2", children: [
14542
- coverageResult && coverageResult.notice ? /* @__PURE__ */ jsxs32(
14838
+ coverageResult && coverageResult.notice && !toolbar?.singleRow ? /* @__PURE__ */ jsxs32(
14543
14839
  "div",
14544
14840
  {
14545
14841
  "data-matrx-table-coverage": true,