@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.
- package/CHANGELOG.md +48 -5
- package/dist/data-table/copy-helpers.d.cts +2 -2
- package/dist/data-table/copy-helpers.d.ts +2 -2
- package/dist/data-table/filter-engine.d.cts +2 -2
- package/dist/data-table/filter-engine.d.ts +2 -2
- package/dist/data-table/host.d.cts +2 -2
- package/dist/data-table/host.d.ts +2 -2
- package/dist/data-table/index.cjs +308 -12
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.d.cts +9 -4
- package/dist/data-table/index.d.ts +9 -4
- package/dist/data-table/index.js +308 -12
- package/dist/data-table/index.js.map +1 -1
- package/dist/data-table/infer-filter.d.cts +2 -2
- package/dist/data-table/infer-filter.d.ts +2 -2
- package/dist/data-table/layered-filters.d.cts +2 -2
- package/dist/data-table/layered-filters.d.ts +2 -2
- package/dist/data-table/query-control.d.cts +2 -2
- package/dist/data-table/query-control.d.ts +2 -2
- package/dist/data-table/types.cjs.map +1 -1
- package/dist/data-table/types.d.cts +2 -2
- package/dist/data-table/types.d.ts +2 -2
- package/dist/data-table/url-state.d.cts +2 -2
- package/dist/data-table/url-state.d.ts +2 -2
- package/dist/data-table/validation.d.cts +1 -1
- package/dist/data-table/validation.d.ts +1 -1
- package/dist/data-table/xlsx.d.cts +2 -2
- package/dist/data-table/xlsx.d.ts +2 -2
- package/dist/field-formats/index.cjs +269 -5
- package/dist/field-formats/index.cjs.map +1 -1
- package/dist/field-formats/index.d.cts +2 -2
- package/dist/field-formats/index.d.ts +2 -2
- package/dist/field-formats/index.js +269 -5
- package/dist/field-formats/index.js.map +1 -1
- package/dist/formulas/index.cjs +38 -5
- package/dist/formulas/index.cjs.map +1 -1
- package/dist/formulas/index.d.cts +39 -2
- package/dist/formulas/index.d.ts +39 -2
- package/dist/formulas/index.js +38 -5
- package/dist/formulas/index.js.map +1 -1
- package/dist/{types-D7O3uOIs.d.cts → types-DNkbHRVK.d.cts} +10 -1
- package/dist/{types-p13OrDj-.d.ts → types-Dx5hJgCi.d.ts} +10 -1
- package/dist/{types-CJWU1Gae.d.cts → types-JnZD8wM0.d.cts} +27 -2
- package/dist/{types-CJWU1Gae.d.ts → types-JnZD8wM0.d.ts} +27 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.41.0 (2026-09-25)
|
|
4
|
+
|
|
5
|
+
### Lane MERGE-STEP-2 (data-tables-grid-overhaul, 2026-09-25)
|
|
6
|
+
|
|
7
|
+
The app's `/data` grid kept forks of `field-formats` and `formulas` after they moved here, and
|
|
8
|
+
the forks grew. Their newer behaviour moves into the package so the app can delete its copies.
|
|
9
|
+
|
|
10
|
+
* **`@ai-matrx/design-system/field-formats` gains nine formats** the app had added since the move:
|
|
11
|
+
`address` (opens in maps), `progress` (a percentage drawn as a bar), `time` (time of day, with
|
|
12
|
+
`options.timeSeconds`), `autonumber` (database-assigned, `prefix`), the system columns
|
|
13
|
+
`created_time` / `modified_time`, `attachment` (file ids from Files; `base: "array"`, also
|
|
14
|
+
accepts `json` but NOT `string`, so on a text column it is offered under "Other kinds" and
|
|
15
|
+
retypes the column instead of storing JSON in a text cell), `person` (an organization member,
|
|
16
|
+
choice-shaped) and `relation` (a record of another table: `relation_target`, `relation_max`,
|
|
17
|
+
`on_delete`, `display`; a value that did not resolve to words is a mismatch, never a bare id).
|
|
18
|
+
`FieldEditorKind` gains `"time"` and `"attachment"`; `FieldFormatId` and `FieldFormatOptions`
|
|
19
|
+
grow to match. Additive.
|
|
20
|
+
* **`@ai-matrx/design-system/formulas` gains the computed-column helpers:** `systemColumnKindOf`,
|
|
21
|
+
`SystemColumnKind`, `isAutonumberColumn`, `isComputedColumn` (THE check every write path uses to
|
|
22
|
+
refuse a computed cell). `withComputedColumns` fills `created_time` / `modified_time` columns from
|
|
23
|
+
the row's own `created_at` / `updated_at`, counts system and autonumber columns in
|
|
24
|
+
`formulaFieldNames`, and takes an optional third argument `displayValueOf(fieldName, raw)` so a
|
|
25
|
+
formula sees a relation cell's words rather than its id. Omitted, behaviour is unchanged.
|
|
26
|
+
* Tests moved with the behaviour: `field-formats/{format,address-progress-format,attachment-format,
|
|
27
|
+
time-format,identifier-formats}.test.ts`, `formulas/computed-columns.test.ts`.
|
|
28
|
+
|
|
29
|
+
**Consumer action:** matrx-frontend deletes its `lib/field-formats/{registry,types,format}.ts` and
|
|
30
|
+
`features/data-tables/{formulas,grid-selection,grid-clipboard}.ts` forks and imports these subpaths.
|
|
31
|
+
Other consumers: none.
|
|
32
|
+
|
|
33
|
+
## 0.40.0 (2026-09-25 — version and notes set by PUBLISHER-5)
|
|
34
|
+
|
|
35
|
+
### Lane TABLE-PAGE-CHROME (2026-09-25)
|
|
36
|
+
|
|
37
|
+
* **The table's toolbar can live in the page's one row.** `MatrxDataTableToolbar.portalInto`
|
|
38
|
+
(an element, or `null` while it mounts) draws the toolbar row INTO the placing page's own
|
|
39
|
+
toolbar row, so a page and its table share one row instead of stacking two bars (records-ui's
|
|
40
|
+
`TablePage`: the view picker, the layout chooser and the table's search side by side).
|
|
41
|
+
`MatrxDataTableToolbar.singleRow` (and `TableTitleRow.singleRow`) keeps the row one line at
|
|
42
|
+
every width: nothing wraps, a narrow screen scrolls it sideways, the search keeps its place.
|
|
43
|
+
With `singleRow`, what the answer covers ("Unknown total", "Partial") is a badge IN the row with the
|
|
44
|
+
whole sentence on hover and to a screen reader, never a band between the toolbar and the column
|
|
45
|
+
headers. Both are additive; without them nothing changes. Suite
|
|
46
|
+
`MatrxDataTable.toolbar-into-the-page-row.test.tsx` RED 1/2 on the prior bytes, GREEN 4/4;
|
|
47
|
+
data-table 649/649. **Consumer action:** none.
|
|
48
|
+
|
|
3
49
|
## 0.39.5
|
|
4
50
|
|
|
5
51
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -14,11 +60,8 @@ No source changes intended and no consumer action required.
|
|
|
14
60
|
|
|
15
61
|
## 0.39.3
|
|
16
62
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
No source changes intended and no consumer action required.
|
|
20
|
-
|
|
21
|
-
## Unreleased
|
|
63
|
+
The auto release stubbed this version as a docs-only republish; it carried UI-FIX-19's source
|
|
64
|
+
change below (notes placed under it by PUBLISHER-5, 2026-09-25).
|
|
22
65
|
|
|
23
66
|
### Lane UI-FIX-19 (2026-09-24)
|
|
24
67
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AgentPayloadInput } from './copy-types.cjs';
|
|
2
|
-
import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-
|
|
2
|
+
import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-DNkbHRVK.cjs';
|
|
3
3
|
import '../content-transfer.cjs';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '@ai-matrx/kit/content-transfer';
|
|
6
6
|
import '../content-transfer/icon.cjs';
|
|
7
7
|
import './table-style.cjs';
|
|
8
|
-
import '../types-
|
|
8
|
+
import '../types-JnZD8wM0.cjs';
|
|
9
9
|
import './grouping.cjs';
|
|
10
10
|
import './fill.cjs';
|
|
11
11
|
import './facets.cjs';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AgentPayloadInput } from './copy-types.js';
|
|
2
|
-
import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-
|
|
2
|
+
import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-Dx5hJgCi.js';
|
|
3
3
|
import '../content-transfer.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '@ai-matrx/kit/content-transfer';
|
|
6
6
|
import '../content-transfer/icon.js';
|
|
7
7
|
import './table-style.js';
|
|
8
|
-
import '../types-
|
|
8
|
+
import '../types-JnZD8wM0.js';
|
|
9
9
|
import './grouping.js';
|
|
10
10
|
import './fill.js';
|
|
11
11
|
import './facets.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-
|
|
1
|
+
import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-DNkbHRVK.cjs';
|
|
2
2
|
import '../content-transfer.cjs';
|
|
3
3
|
import 'react';
|
|
4
4
|
import '@ai-matrx/kit/content-transfer';
|
|
5
5
|
import '../content-transfer/icon.cjs';
|
|
6
6
|
import './copy-types.cjs';
|
|
7
7
|
import './table-style.cjs';
|
|
8
|
-
import '../types-
|
|
8
|
+
import '../types-JnZD8wM0.cjs';
|
|
9
9
|
import './grouping.cjs';
|
|
10
10
|
import './fill.cjs';
|
|
11
11
|
import './facets.cjs';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-
|
|
1
|
+
import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-Dx5hJgCi.js';
|
|
2
2
|
import '../content-transfer.js';
|
|
3
3
|
import 'react';
|
|
4
4
|
import '@ai-matrx/kit/content-transfer';
|
|
5
5
|
import '../content-transfer/icon.js';
|
|
6
6
|
import './copy-types.js';
|
|
7
7
|
import './table-style.js';
|
|
8
|
-
import '../types-
|
|
8
|
+
import '../types-JnZD8wM0.js';
|
|
9
9
|
import './grouping.js';
|
|
10
10
|
import './fill.js';
|
|
11
11
|
import './facets.js';
|
|
@@ -2,13 +2,13 @@ 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-
|
|
5
|
+
import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-DNkbHRVK.cjs';
|
|
6
6
|
import { MatrxTableMenuTarget, MatrxTableMenuPayload } from './menu-targets.cjs';
|
|
7
7
|
import '../content-transfer.cjs';
|
|
8
8
|
import '@ai-matrx/kit/content-transfer';
|
|
9
9
|
import '../content-transfer/icon.cjs';
|
|
10
10
|
import './table-style.cjs';
|
|
11
|
-
import '../types-
|
|
11
|
+
import '../types-JnZD8wM0.cjs';
|
|
12
12
|
import './grouping.cjs';
|
|
13
13
|
import './fill.cjs';
|
|
14
14
|
import './facets.cjs';
|
|
@@ -2,13 +2,13 @@ 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-
|
|
5
|
+
import { b as TableSavedViewsProps, c as MatrxDataTableDensity, d as MatrxDataTableRecordControls } from '../types-Dx5hJgCi.js';
|
|
6
6
|
import { MatrxTableMenuTarget, MatrxTableMenuPayload } from './menu-targets.js';
|
|
7
7
|
import '../content-transfer.js';
|
|
8
8
|
import '@ai-matrx/kit/content-transfer';
|
|
9
9
|
import '../content-transfer/icon.js';
|
|
10
10
|
import './table-style.js';
|
|
11
|
-
import '../types-
|
|
11
|
+
import '../types-JnZD8wM0.js';
|
|
12
12
|
import './grouping.js';
|
|
13
13
|
import './fill.js';
|
|
14
14
|
import './facets.js';
|
|
@@ -152,6 +152,7 @@ module.exports = __toCommonJS(data_table_exports);
|
|
|
152
152
|
|
|
153
153
|
// src/data-table/MatrxDataTable.tsx
|
|
154
154
|
var import_react21 = require("react");
|
|
155
|
+
var import_react_dom2 = require("react-dom");
|
|
155
156
|
var import_core3 = require("@dnd-kit/core");
|
|
156
157
|
|
|
157
158
|
// src/data-table/icons.tsx
|
|
@@ -1069,6 +1070,7 @@ function TableTitleRow({
|
|
|
1069
1070
|
tabs,
|
|
1070
1071
|
actions,
|
|
1071
1072
|
children,
|
|
1073
|
+
singleRow = false,
|
|
1072
1074
|
className
|
|
1073
1075
|
}) {
|
|
1074
1076
|
const Heading = headingLevel === 1 ? "h1" : "h2";
|
|
@@ -1077,7 +1079,8 @@ function TableTitleRow({
|
|
|
1077
1079
|
{
|
|
1078
1080
|
"data-matrx-table-toolbar": true,
|
|
1079
1081
|
className: cn(
|
|
1080
|
-
"relative flex min-w-0 shrink-0
|
|
1082
|
+
"relative flex min-w-0 shrink-0 items-center gap-2",
|
|
1083
|
+
singleRow ? "flex-nowrap overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" : "flex-wrap",
|
|
1081
1084
|
className
|
|
1082
1085
|
),
|
|
1083
1086
|
children: [
|
|
@@ -1096,7 +1099,7 @@ function TableTitleRow({
|
|
|
1096
1099
|
"div",
|
|
1097
1100
|
{
|
|
1098
1101
|
"data-matrx-table-title-search": true,
|
|
1099
|
-
className: "order-last w-full min-w-0 sm:order-none sm:w-64 sm:shrink-0 sm:max-w-md",
|
|
1102
|
+
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",
|
|
1100
1103
|
children: search
|
|
1101
1104
|
}
|
|
1102
1105
|
) : null,
|
|
@@ -1106,7 +1109,7 @@ function TableTitleRow({
|
|
|
1106
1109
|
"div",
|
|
1107
1110
|
{
|
|
1108
1111
|
"data-matrx-table-actions": true,
|
|
1109
|
-
className: "ml-auto flex min-w-0
|
|
1112
|
+
className: cn("ml-auto flex min-w-0 items-center gap-0", singleRow ? "shrink-0 flex-nowrap" : "flex-wrap"),
|
|
1110
1113
|
children: actions
|
|
1111
1114
|
}
|
|
1112
1115
|
) : null
|
|
@@ -10250,6 +10253,35 @@ function toText(value) {
|
|
|
10250
10253
|
return String(value);
|
|
10251
10254
|
return null;
|
|
10252
10255
|
}
|
|
10256
|
+
function parseTimeOfDay(value) {
|
|
10257
|
+
if (value instanceof Date) {
|
|
10258
|
+
return { h: value.getHours(), m: value.getMinutes(), s: value.getSeconds() };
|
|
10259
|
+
}
|
|
10260
|
+
if (typeof value !== "string") return null;
|
|
10261
|
+
const text = value.trim();
|
|
10262
|
+
if (!text) return null;
|
|
10263
|
+
const m = /^(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?\s*([aApP]\.?[mM]\.?)?$/.exec(text);
|
|
10264
|
+
if (m) {
|
|
10265
|
+
let h = Number(m[1]);
|
|
10266
|
+
const min = m[2] ? Number(m[2]) : 0;
|
|
10267
|
+
const sec = m[3] ? Number(m[3]) : 0;
|
|
10268
|
+
const ampm = m[4]?.toLowerCase().replace(/\./g, "");
|
|
10269
|
+
if (ampm) {
|
|
10270
|
+
if (h < 1 || h > 12) return null;
|
|
10271
|
+
if (ampm === "pm" && h !== 12) h += 12;
|
|
10272
|
+
if (ampm === "am" && h === 12) h = 0;
|
|
10273
|
+
}
|
|
10274
|
+
if (h > 23 || min > 59 || sec > 59) return null;
|
|
10275
|
+
return { h, m: min, s: sec };
|
|
10276
|
+
}
|
|
10277
|
+
if (/\d{4}-\d{2}-\d{2}T/.test(text)) {
|
|
10278
|
+
const d = new Date(text);
|
|
10279
|
+
if (!Number.isNaN(d.getTime())) {
|
|
10280
|
+
return { h: d.getHours(), m: d.getMinutes(), s: d.getSeconds() };
|
|
10281
|
+
}
|
|
10282
|
+
}
|
|
10283
|
+
return null;
|
|
10284
|
+
}
|
|
10253
10285
|
var BARE_CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]00:00(?::00(?:\.0+)?)?)?$/;
|
|
10254
10286
|
function toDate(value) {
|
|
10255
10287
|
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
|
@@ -10370,6 +10402,23 @@ var DEFS = [
|
|
|
10370
10402
|
},
|
|
10371
10403
|
parse: (raw) => raw === "" || raw == null ? null : String(raw).trim()
|
|
10372
10404
|
},
|
|
10405
|
+
{
|
|
10406
|
+
// A postal address. Free text by design (addresses have no one shape
|
|
10407
|
+
// worldwide); the cell opens it in maps, which is the thing people do
|
|
10408
|
+
// with an address in a table.
|
|
10409
|
+
id: "address",
|
|
10410
|
+
label: "Address",
|
|
10411
|
+
description: "Street address \u2014 click to open in maps",
|
|
10412
|
+
group: "Text",
|
|
10413
|
+
base: "string",
|
|
10414
|
+
editor: "text",
|
|
10415
|
+
rich: true,
|
|
10416
|
+
format: (v) => {
|
|
10417
|
+
const t = toText(v)?.trim();
|
|
10418
|
+
return t ? t : null;
|
|
10419
|
+
},
|
|
10420
|
+
parse: (raw) => raw === "" || raw == null ? null : String(raw)
|
|
10421
|
+
},
|
|
10373
10422
|
{
|
|
10374
10423
|
id: "url",
|
|
10375
10424
|
label: "Link",
|
|
@@ -10503,6 +10552,26 @@ var DEFS = [
|
|
|
10503
10552
|
},
|
|
10504
10553
|
parse: (raw) => toNumber(raw)
|
|
10505
10554
|
},
|
|
10555
|
+
{
|
|
10556
|
+
// A percentage DRAWN as a bar — task completion, quota used, a score out of
|
|
10557
|
+
// 100. Same storage and scale rule as `percent`; only the rendering differs.
|
|
10558
|
+
id: "progress",
|
|
10559
|
+
label: "Progress bar",
|
|
10560
|
+
description: "A percentage shown as a bar",
|
|
10561
|
+
group: "Numbers",
|
|
10562
|
+
base: "number",
|
|
10563
|
+
alsoAccepts: ["integer"],
|
|
10564
|
+
editor: "number",
|
|
10565
|
+
rich: true,
|
|
10566
|
+
optionKeys: ["percentScale"],
|
|
10567
|
+
format: (v, o) => {
|
|
10568
|
+
const n2 = toNumber(v);
|
|
10569
|
+
if (n2 === null) return null;
|
|
10570
|
+
const shown = o.percentScale === "fraction" ? n2 * 100 : n2;
|
|
10571
|
+
return `${groupedNumber(shown, { precision: 0 })}%`;
|
|
10572
|
+
},
|
|
10573
|
+
parse: (raw) => toNumber(raw)
|
|
10574
|
+
},
|
|
10506
10575
|
{
|
|
10507
10576
|
id: "duration",
|
|
10508
10577
|
label: "Duration",
|
|
@@ -10571,13 +10640,16 @@ var DEFS = [
|
|
|
10571
10640
|
alsoAccepts: ["number"],
|
|
10572
10641
|
editor: "number",
|
|
10573
10642
|
numericAlign: true,
|
|
10643
|
+
// The name says BYTES on purpose: this cell holds a genuine byte length,
|
|
10644
|
+
// never a character or item count, and `formatFileSize` is only honest
|
|
10645
|
+
// about a quantity that is actually bytes.
|
|
10574
10646
|
format: (v) => {
|
|
10575
|
-
const
|
|
10576
|
-
return
|
|
10647
|
+
const byteLength = toNumber(v);
|
|
10648
|
+
return byteLength === null ? null : (0, import_format3.formatFileSize)(byteLength);
|
|
10577
10649
|
},
|
|
10578
10650
|
parse: (raw) => {
|
|
10579
|
-
const
|
|
10580
|
-
return
|
|
10651
|
+
const byteLength = toNumber(raw);
|
|
10652
|
+
return byteLength === null ? null : Math.trunc(byteLength);
|
|
10581
10653
|
}
|
|
10582
10654
|
},
|
|
10583
10655
|
// ── Choice ────────────────────────────────────────────────────────────────
|
|
@@ -10638,6 +10710,87 @@ var DEFS = [
|
|
|
10638
10710
|
},
|
|
10639
10711
|
parse: (raw) => raw === "" || raw == null ? null : String(raw)
|
|
10640
10712
|
},
|
|
10713
|
+
{
|
|
10714
|
+
// A TIME OF DAY with no date: opening hours, a shift start, a meeting slot.
|
|
10715
|
+
// Stored as the string the <input type="time"> gives ("HH:MM" or
|
|
10716
|
+
// "HH:MM:SS"), shown in the viewer's locale ("2:30 PM" / "14:30"). The
|
|
10717
|
+
// storage type stays `string`; sorting is lexical on the 24-hour form,
|
|
10718
|
+
// which is chronological — the one reason the stored form is 24-hour.
|
|
10719
|
+
id: "time",
|
|
10720
|
+
label: "Time",
|
|
10721
|
+
description: "Time of day, without a date",
|
|
10722
|
+
group: "Dates",
|
|
10723
|
+
base: "string",
|
|
10724
|
+
editor: "time",
|
|
10725
|
+
optionKeys: ["timeSeconds"],
|
|
10726
|
+
format: (v, o) => {
|
|
10727
|
+
const parts = parseTimeOfDay(v);
|
|
10728
|
+
if (!parts) return null;
|
|
10729
|
+
const d = new Date(2e3, 0, 1, parts.h, parts.m, parts.s);
|
|
10730
|
+
return d.toLocaleTimeString(void 0, {
|
|
10731
|
+
hour: "numeric",
|
|
10732
|
+
minute: "2-digit",
|
|
10733
|
+
...o.timeSeconds ? { second: "2-digit" } : {}
|
|
10734
|
+
});
|
|
10735
|
+
},
|
|
10736
|
+
// Normalise to the 24-hour "HH:MM[:SS]" form; a value that is not a time
|
|
10737
|
+
// is handed back unchanged so the validation layer can refuse it by name.
|
|
10738
|
+
parse: (raw) => {
|
|
10739
|
+
if (raw == null || raw === "") return null;
|
|
10740
|
+
const parts = parseTimeOfDay(raw);
|
|
10741
|
+
if (!parts) return raw;
|
|
10742
|
+
const two = (n2) => String(n2).padStart(2, "0");
|
|
10743
|
+
const fields = parts.s > 0 ? [parts.h, parts.m, parts.s] : [parts.h, parts.m];
|
|
10744
|
+
return fields.map(two).join(":");
|
|
10745
|
+
}
|
|
10746
|
+
},
|
|
10747
|
+
{
|
|
10748
|
+
// Assigned BY THE DATABASE when the row is inserted (trigger
|
|
10749
|
+
// `_udt_autonumber`), so it is the same number whichever door made the row
|
|
10750
|
+
// and never shifts when another row is deleted. Stored in the cell as an
|
|
10751
|
+
// integer; every editor refuses it. `prefix` turns 42 into "INV-42".
|
|
10752
|
+
id: "autonumber",
|
|
10753
|
+
label: "Autonumber",
|
|
10754
|
+
description: "A number the table assigns to each new row \u2014 never typed",
|
|
10755
|
+
group: "Numbers",
|
|
10756
|
+
base: "integer",
|
|
10757
|
+
alsoAccepts: ["number"],
|
|
10758
|
+
editor: "computed",
|
|
10759
|
+
numericAlign: true,
|
|
10760
|
+
optionKeys: ["prefix"],
|
|
10761
|
+
format: (v, o) => {
|
|
10762
|
+
const n2 = toNumber(v);
|
|
10763
|
+
if (n2 === null) return null;
|
|
10764
|
+
return `${o.prefix ?? ""}${Math.trunc(n2)}`;
|
|
10765
|
+
},
|
|
10766
|
+
parse: () => null
|
|
10767
|
+
},
|
|
10768
|
+
// ── System columns ────────────────────────────────────────────────────────
|
|
10769
|
+
// Filled from the ROW'S OWN RECORD on read (`withComputedColumns` in
|
|
10770
|
+
// `@ai-matrx/design-system/formulas`), never typed and never stored in the
|
|
10771
|
+
// cell. `editor: "computed"` makes every editor refuse them.
|
|
10772
|
+
{
|
|
10773
|
+
id: "created_time",
|
|
10774
|
+
label: "Created time",
|
|
10775
|
+
description: "When the row was added \u2014 filled in automatically",
|
|
10776
|
+
group: "Dates",
|
|
10777
|
+
base: "string",
|
|
10778
|
+
editor: "computed",
|
|
10779
|
+
optionKeys: ["dateStyle"],
|
|
10780
|
+
format: (v, o) => FIELD_FORMATS.datetime.format(v, o),
|
|
10781
|
+
parse: () => null
|
|
10782
|
+
},
|
|
10783
|
+
{
|
|
10784
|
+
id: "modified_time",
|
|
10785
|
+
label: "Last modified time",
|
|
10786
|
+
description: "When the row last changed \u2014 filled in automatically",
|
|
10787
|
+
group: "Dates",
|
|
10788
|
+
base: "string",
|
|
10789
|
+
editor: "computed",
|
|
10790
|
+
optionKeys: ["dateStyle"],
|
|
10791
|
+
format: (v, o) => FIELD_FORMATS.datetime.format(v, o),
|
|
10792
|
+
parse: () => null
|
|
10793
|
+
},
|
|
10641
10794
|
{
|
|
10642
10795
|
id: "relative_time",
|
|
10643
10796
|
label: "Relative time",
|
|
@@ -10694,6 +10847,44 @@ var DEFS = [
|
|
|
10694
10847
|
return toList(raw) ?? raw;
|
|
10695
10848
|
}
|
|
10696
10849
|
},
|
|
10850
|
+
{
|
|
10851
|
+
// FILES ATTACHED TO THE ROW. The cell stores the files feature's file ids
|
|
10852
|
+
// (`files.files.id`), never a URL or a name: a rename or a move in Files
|
|
10853
|
+
// changes nothing here, and access is the file's own (RLS) — a reader who
|
|
10854
|
+
// may see the row but not the file gets the chip's honest "no access", not
|
|
10855
|
+
// a broken link. Drawn by `AttachmentChips`, edited by `AttachmentInput`
|
|
10856
|
+
// through the ONE file picker.
|
|
10857
|
+
id: "attachment",
|
|
10858
|
+
label: "Attachments",
|
|
10859
|
+
description: "Files from your Files, shown as chips",
|
|
10860
|
+
group: "Structured",
|
|
10861
|
+
base: "array",
|
|
10862
|
+
// Not `string`: on a text column it must appear under "Other kinds" so
|
|
10863
|
+
// picking it retypes the column to a list, rather than storing a JSON
|
|
10864
|
+
// string of ids in a text cell.
|
|
10865
|
+
alsoAccepts: ["json"],
|
|
10866
|
+
editor: "attachment",
|
|
10867
|
+
rich: true,
|
|
10868
|
+
format: (v) => {
|
|
10869
|
+
const list = toList(v);
|
|
10870
|
+
if (list === null) return null;
|
|
10871
|
+
const n2 = list.filter((i) => String(i).trim()).length;
|
|
10872
|
+
return n2 === 0 ? "" : n2 === 1 ? "1 file" : `${n2} files`;
|
|
10873
|
+
},
|
|
10874
|
+
parse: (raw) => {
|
|
10875
|
+
if (raw == null || raw === "") return null;
|
|
10876
|
+
if (Array.isArray(raw)) return raw.map((i) => String(i).trim()).filter(Boolean);
|
|
10877
|
+
const text = String(raw).trim();
|
|
10878
|
+
if (text.startsWith("[")) {
|
|
10879
|
+
try {
|
|
10880
|
+
const parsed = JSON.parse(text);
|
|
10881
|
+
if (Array.isArray(parsed)) return parsed.map((i) => String(i).trim()).filter(Boolean);
|
|
10882
|
+
} catch {
|
|
10883
|
+
}
|
|
10884
|
+
}
|
|
10885
|
+
return text.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10886
|
+
}
|
|
10887
|
+
},
|
|
10697
10888
|
{
|
|
10698
10889
|
id: "tags",
|
|
10699
10890
|
label: "Tags",
|
|
@@ -10757,6 +10948,82 @@ var DEFS = [
|
|
|
10757
10948
|
return trimmed === "" ? null : trimmed;
|
|
10758
10949
|
}
|
|
10759
10950
|
},
|
|
10951
|
+
{
|
|
10952
|
+
// A PERSON: one member of the organization the table belongs to, stored as
|
|
10953
|
+
// the user id. It is a choice column whose options are the members —
|
|
10954
|
+
// supplied at runtime by whoever renders it (features/data-tables/
|
|
10955
|
+
// person-choices.tsx), the same door a structured-list-bound column uses —
|
|
10956
|
+
// so the cell picker, the chip, filters, color-by and the agent scope are
|
|
10957
|
+
// all the choice system's, with no second implementation.
|
|
10958
|
+
id: "person",
|
|
10959
|
+
label: "Person",
|
|
10960
|
+
description: "Someone in your organization \u2014 pick from its members",
|
|
10961
|
+
group: "Choice",
|
|
10962
|
+
base: "string",
|
|
10963
|
+
editor: "select",
|
|
10964
|
+
rich: true,
|
|
10965
|
+
format: (v, o) => FIELD_FORMATS.choice.format(v, o),
|
|
10966
|
+
parse: (raw) => FIELD_FORMATS.choice.parse(raw, {})
|
|
10967
|
+
},
|
|
10968
|
+
{
|
|
10969
|
+
// A RECORD OF ANOTHER TABLE. The cell stores that record's ID; the screen
|
|
10970
|
+
// shows its WORDS. `person` is the precedent and the template — it has
|
|
10971
|
+
// stored a user id and displayed a name through `choiceMap` since
|
|
10972
|
+
// 2026-09-21 — so `relation` is a member of CHOICE_FORMAT_IDS and every
|
|
10973
|
+
// one of the ten readers that print a cell resolves it through the path
|
|
10974
|
+
// they already use for `person`. There is no eleventh place to remember.
|
|
10975
|
+
//
|
|
10976
|
+
// THREE STATES, THREE RENDERINGS, NO FOURTH (OLD-TABLES-CUTOVER §3.3):
|
|
10977
|
+
// resolved the target's words
|
|
10978
|
+
// unresolvable the amber fallback WITH the id, as an identifier — never
|
|
10979
|
+
// a blank, and never the bare uuid as if it were text, so a
|
|
10980
|
+
// person can act on it and support can trace it
|
|
10981
|
+
// withheld platform.relation_withheld_label(), with NO id — a cell
|
|
10982
|
+
// the reader may not see never leaks the identifier through
|
|
10983
|
+
// the gap between the two stores' refusals
|
|
10984
|
+
//
|
|
10985
|
+
// `format` therefore returns null (the fallback law's "cannot interpret")
|
|
10986
|
+
// for anything the resolver did not turn into words, and the renderer is
|
|
10987
|
+
// what distinguishes unresolvable from withheld. It never prints a bare id.
|
|
10988
|
+
id: "relation",
|
|
10989
|
+
label: "Relation",
|
|
10990
|
+
description: "A record of another table \u2014 pick it, and its name is shown",
|
|
10991
|
+
group: "Choice",
|
|
10992
|
+
base: "string",
|
|
10993
|
+
alsoAccepts: ["array", "json"],
|
|
10994
|
+
editor: "select",
|
|
10995
|
+
rich: true,
|
|
10996
|
+
optionKeys: ["relation_target", "relation_max", "on_delete", "display"],
|
|
10997
|
+
// IT DOES NOT DELEGATE TO `choice`, AND THAT IS THE WHOLE POINT.
|
|
10998
|
+
// `choice.format` deliberately returns the RAW VALUE when no options have
|
|
10999
|
+
// resolved yet — a structured list still loading should not make every cell
|
|
11000
|
+
// look off-list. For a label column that is right: the raw value IS the
|
|
11001
|
+
// label. For an identifier column it is the defect §3.3 names, and it is
|
|
11002
|
+
// how the first cut of this wave put four raw uuids on a real grid: the
|
|
11003
|
+
// fallback arm never ran, because returning the uuid counted as SUCCESS.
|
|
11004
|
+
//
|
|
11005
|
+
// Here, a value that did not resolve to words is a MISMATCH — `null` — so
|
|
11006
|
+
// the renderer's three-state arm owns it and shows the amber fallback with
|
|
11007
|
+
// the id marked as an identifier, or the withheld sentence. The id never
|
|
11008
|
+
// reaches the eye as if it were the cell's contents.
|
|
11009
|
+
format: (v, options) => {
|
|
11010
|
+
const list = toList(v);
|
|
11011
|
+
const values = list !== null ? list : v == null ? [] : [v];
|
|
11012
|
+
if (values.length === 0) return "";
|
|
11013
|
+
const words2 = [];
|
|
11014
|
+
for (const entry of values) {
|
|
11015
|
+
const text = toText(entry);
|
|
11016
|
+
if (text === null) return null;
|
|
11017
|
+
const trimmed = text.trim();
|
|
11018
|
+
if (trimmed === "") continue;
|
|
11019
|
+
const match = findChoice(options.choices, trimmed);
|
|
11020
|
+
if (!match) return null;
|
|
11021
|
+
words2.push(match.label ?? match.value);
|
|
11022
|
+
}
|
|
11023
|
+
return words2.length === 0 ? "" : words2.join(", ");
|
|
11024
|
+
},
|
|
11025
|
+
parse: (raw) => FIELD_FORMATS.choice.parse(raw, {})
|
|
11026
|
+
},
|
|
10760
11027
|
{
|
|
10761
11028
|
id: "multi_choice",
|
|
10762
11029
|
label: "Multi-choice",
|
|
@@ -10801,7 +11068,7 @@ var DEFS = [
|
|
|
10801
11068
|
// A formula column stores NOTHING. `metadata.format.options.formula` holds
|
|
10802
11069
|
// the expression; the stored cell is always null; the value is computed on
|
|
10803
11070
|
// read from the other columns of the same row
|
|
10804
|
-
// (
|
|
11071
|
+
// (`@ai-matrx/design-system/formulas`). Grouped under Structured on purpose
|
|
10805
11072
|
// — the picker's groups are a fixed vocabulary, and a formula is structure
|
|
10806
11073
|
// over a plain string column, not a sixth family.
|
|
10807
11074
|
//
|
|
@@ -13495,6 +13762,11 @@ function MatrxDataTableCore({
|
|
|
13495
13762
|
const rowCopyInMenu = rowCopyPlacement === "menu" && Boolean(rowActions);
|
|
13496
13763
|
const showInlineRowCopy = showRowCopy && !rowCopyInMenu;
|
|
13497
13764
|
const showToolbarCopy = effectiveCopy !== void 0 && effectiveCopy.showToolbar !== false;
|
|
13765
|
+
const placeToolbar = (node) => {
|
|
13766
|
+
if (!toolbar || !("portalInto" in toolbar)) return node;
|
|
13767
|
+
const target = toolbar.portalInto;
|
|
13768
|
+
return target ? (0, import_react_dom2.createPortal)(node, target) : null;
|
|
13769
|
+
};
|
|
13498
13770
|
const renderRowActions = (row, controls) => {
|
|
13499
13771
|
const actions = rowActions?.(row, controls);
|
|
13500
13772
|
const scopedActions = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MatrxTableRowControlsScope, { value: controls, children: actions });
|
|
@@ -14322,9 +14594,10 @@ function MatrxDataTableCore({
|
|
|
14322
14594
|
notice,
|
|
14323
14595
|
savedViewError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: savedViewError }) : null,
|
|
14324
14596
|
toolbarActionError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: toolbarActionError }) : null,
|
|
14325
|
-
!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_runtime42.jsxs)(
|
|
14597
|
+
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_runtime42.jsxs)(
|
|
14326
14598
|
TableTitleRow,
|
|
14327
14599
|
{
|
|
14600
|
+
...toolbar?.singleRow ? { singleRow: true } : {},
|
|
14328
14601
|
tabs: viewTabs ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TableViewWorkspace, { snapshot: currentViewSnapshot, onApply: applySavedView, related: relatedTableActions }, tableId ?? "table") : void 0,
|
|
14329
14602
|
...toolbar?.title === void 0 ? {} : { title: toolbar.title },
|
|
14330
14603
|
...toolbar?.titleCount === void 0 ? {} : { count: toolbar.titleCount },
|
|
@@ -14459,8 +14732,31 @@ function MatrxDataTableCore({
|
|
|
14459
14732
|
"div",
|
|
14460
14733
|
{
|
|
14461
14734
|
"data-matrx-table-actions": true,
|
|
14462
|
-
className:
|
|
14735
|
+
className: cn(
|
|
14736
|
+
"ml-auto flex min-w-0 items-center gap-0",
|
|
14737
|
+
toolbar?.singleRow ? "shrink-0 flex-nowrap" : "flex-wrap"
|
|
14738
|
+
),
|
|
14463
14739
|
children: [
|
|
14740
|
+
toolbar?.singleRow && coverageResult && coverageResult.notice ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(Tooltip, { children: [
|
|
14741
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
14742
|
+
"span",
|
|
14743
|
+
{
|
|
14744
|
+
"data-matrx-table-coverage": true,
|
|
14745
|
+
"data-matrx-table-coverage-scope": coverageResult.scope,
|
|
14746
|
+
role: "status",
|
|
14747
|
+
tabIndex: 0,
|
|
14748
|
+
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",
|
|
14749
|
+
children: [
|
|
14750
|
+
coverageResult.badge,
|
|
14751
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "sr-only", children: [
|
|
14752
|
+
": ",
|
|
14753
|
+
coverageResult.notice
|
|
14754
|
+
] })
|
|
14755
|
+
]
|
|
14756
|
+
}
|
|
14757
|
+
) }),
|
|
14758
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TooltipContent, { className: "max-w-xs", children: coverageResult.notice })
|
|
14759
|
+
] }) : null,
|
|
14464
14760
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
14465
14761
|
TableToolbarAction,
|
|
14466
14762
|
{
|
|
@@ -14603,9 +14899,9 @@ function MatrxDataTableCore({
|
|
|
14603
14899
|
)
|
|
14604
14900
|
]
|
|
14605
14901
|
}
|
|
14606
|
-
),
|
|
14902
|
+
)),
|
|
14607
14903
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "data-matrx-table-body-region": true, className: "flex min-h-0 flex-1 flex-col gap-2", children: [
|
|
14608
|
-
coverageResult && coverageResult.notice ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
14904
|
+
coverageResult && coverageResult.notice && !toolbar?.singleRow ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
14609
14905
|
"div",
|
|
14610
14906
|
{
|
|
14611
14907
|
"data-matrx-table-coverage": true,
|