@ai-matrx/records-ui 0.90.0 → 0.90.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.
- package/CHANGELOG.md +27 -0
- package/dist/index.cjs +75 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +75 -38
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.90.2
|
|
4
|
+
### Unreleased
|
|
5
|
+
|
|
6
|
+
### Changed — a removal says it can be restored from Trash (lane STORE-RESTORE-DOORS, 2026-09-26)
|
|
7
|
+
- Removing a column (`FieldEditor`, `TableSettings`) confirms with the shared `archiveConfirmSentence`
|
|
8
|
+
plus "Its values stay in every record and come back with it." — the store now brings a removed Field
|
|
9
|
+
back from Trash with its values (`custom.field_restore`); the old copy said the values lived only in history.
|
|
10
|
+
- `DashboardCanvas`: removing a dashboard takes a second press with the same sentence (it was one press
|
|
11
|
+
labelled "Delete"); it comes back from Trash through `custom.dashboard_restore`.
|
|
12
|
+
- `NotifyRuleEditor`: "Stop telling me" archives the subscription's Rule and now asks once, naming Trash
|
|
13
|
+
(`custom.rule_restore`).
|
|
14
|
+
- Consumer action: none.
|
|
15
|
+
|
|
16
|
+
## 0.90.1 (2026-09-26 — merged grid 6n/6o live-walk fixes; needs @ai-matrx/design-system 0.44.3)
|
|
17
|
+
|
|
18
|
+
- **The header's "+" is a button of its own, not a button inside the column-reorder button**
|
|
19
|
+
(found live: a real click on "+" did nothing, and React reported a nested button). The column
|
|
20
|
+
declares `reorderable: false` (design-system 0.44.3), so its label is never a drag handle and it
|
|
21
|
+
never moves among the data columns.
|
|
22
|
+
- **Phone (6o), found live at 375 px:** the Tools button was scrolled past search and a 144 px "No
|
|
23
|
+
groups" picker, off the right edge. Search and Tools now sit side by side at the START of the row
|
|
24
|
+
(the same search query), and an ungrouped view draws no group picker on a phone.
|
|
25
|
+
- **A key is never spread into JSX** (found live on the merged grid): `TablePage` spread
|
|
26
|
+
`{ key: "share" }` into `<ShareControl>`; the key is passed directly now, and
|
|
27
|
+
`a-key-is-never-spread-into-jsx.test.tsx` is a census of this package's source (it names the old
|
|
28
|
+
line on 0.90.0).
|
|
29
|
+
|
|
3
30
|
## 0.90.0 (2026-09-26 — merged grid tranches 6n + 6o, choice colours by option, DOM budget; needs @ai-matrx/records 0.58.22)
|
|
4
31
|
|
|
5
32
|
- **6n · a row is added where you are.** The merged grid draws an "Add row" line under the last
|
package/dist/index.cjs
CHANGED
|
@@ -269,6 +269,8 @@ __export(src_exports, {
|
|
|
269
269
|
formFromSummary: () => formFromSummary,
|
|
270
270
|
formPresentation: () => formPresentation,
|
|
271
271
|
formatForField: () => formatForField,
|
|
272
|
+
gridContextKey: () => gridContextKey,
|
|
273
|
+
gridContextSnapshot: () => gridContextSnapshot,
|
|
272
274
|
groupInbox: () => groupInbox,
|
|
273
275
|
groupLabel: () => groupLabel,
|
|
274
276
|
handOrderLine: () => handOrderLine,
|
|
@@ -5349,9 +5351,8 @@ function FieldEditor({ tableId, field: field2, onSaved, onCancel, onRemoved, cla
|
|
|
5349
5351
|
onCancel ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system17.Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
|
|
5350
5352
|
field2 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ml-auto flex items-center gap-2", children: askingToRemove ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
5351
5353
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-destructive", children: [
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
" takes it off every record of this table. The values stay in the record's history and nothing else is deleted."
|
|
5354
|
+
archiveConfirmSentence(`the field \u201C${fieldName(field2)}\u201D`),
|
|
5355
|
+
" Its values stay in every record and come back with it."
|
|
5355
5356
|
] }),
|
|
5356
5357
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system17.Button, { size: "sm", variant: "destructive", disabled: mutation.saving, onClick: () => void remove(), children: "Remove it" }),
|
|
5357
5358
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system17.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(false), children: "Keep it" })
|
|
@@ -7316,6 +7317,8 @@ function addColumnColumn(onAdd) {
|
|
|
7316
7317
|
sortable: false,
|
|
7317
7318
|
filter: false,
|
|
7318
7319
|
hideable: false,
|
|
7320
|
+
// A control, never a drag handle: no button inside the reorder button, and it stays last.
|
|
7321
|
+
reorderable: false,
|
|
7319
7322
|
resizable: false,
|
|
7320
7323
|
compact: true,
|
|
7321
7324
|
width: 40
|
|
@@ -10653,6 +10656,7 @@ function Grid({
|
|
|
10653
10656
|
const view = presentation.grouping;
|
|
10654
10657
|
const field2 = view?.field ?? null;
|
|
10655
10658
|
if (field2 === null && !onPresentationChange) return null;
|
|
10659
|
+
if (field2 === null && merged && phone) return null;
|
|
10656
10660
|
const aggregates = view && Object.keys(view.aggregates ?? {}).length > 0 ? view.aggregates : field2 ? { [field2]: { kind: "count" } } : void 0;
|
|
10657
10661
|
return {
|
|
10658
10662
|
columnId: field2,
|
|
@@ -10683,7 +10687,7 @@ function Grid({
|
|
|
10683
10687
|
}
|
|
10684
10688
|
} : {}
|
|
10685
10689
|
};
|
|
10686
|
-
}, [presentation, onPresentationChange, writeLook]);
|
|
10690
|
+
}, [presentation, onPresentationChange, writeLook, merged, phone]);
|
|
10687
10691
|
const labelOfField = (0, import_react57.useCallback)(
|
|
10688
10692
|
(key) => {
|
|
10689
10693
|
const held = (fields.data ?? []).find((f) => f.key === key);
|
|
@@ -11065,6 +11069,41 @@ function Grid({
|
|
|
11065
11069
|
toolbar: {
|
|
11066
11070
|
search: true,
|
|
11067
11071
|
searchPlaceholder: "Search records",
|
|
11072
|
+
/*
|
|
11073
|
+
* A PHONE (tranche 6o): search and the one Tools button sit side by side at the START of
|
|
11074
|
+
* the row, so Tools is never scrolled out of reach; the search is the same query.
|
|
11075
|
+
*/
|
|
11076
|
+
...merged && phone ? {
|
|
11077
|
+
customSearch: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", "data-records-grid-phone-search": "", children: [
|
|
11078
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11079
|
+
"input",
|
|
11080
|
+
{
|
|
11081
|
+
type: "search",
|
|
11082
|
+
value: query.search,
|
|
11083
|
+
placeholder: "Search",
|
|
11084
|
+
"aria-label": "Search records",
|
|
11085
|
+
onChange: (event) => {
|
|
11086
|
+
const search = event.target.value;
|
|
11087
|
+
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11088
|
+
},
|
|
11089
|
+
className: "h-8 w-full min-w-0 rounded-md border border-input bg-background px-2 text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
11090
|
+
style: { fontSize: "16px" }
|
|
11091
|
+
}
|
|
11092
|
+
),
|
|
11093
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11094
|
+
import_design_system32.Button,
|
|
11095
|
+
{
|
|
11096
|
+
size: "sm",
|
|
11097
|
+
variant: "outline",
|
|
11098
|
+
className: "h-8 shrink-0",
|
|
11099
|
+
"data-records-grid-tools": "",
|
|
11100
|
+
"aria-haspopup": "dialog",
|
|
11101
|
+
onClick: () => setToolsOpen(true),
|
|
11102
|
+
children: "Tools"
|
|
11103
|
+
}
|
|
11104
|
+
)
|
|
11105
|
+
] })
|
|
11106
|
+
} : {},
|
|
11068
11107
|
/* ONE ROW (lane TABLE-PAGE-CHROME): on a table page the grid's search, columns and
|
|
11069
11108
|
sort are drawn INTO the page's toolbar row, never a second bar under it. */
|
|
11070
11109
|
...toolbarSlot !== void 0 ? { portalInto: toolbarSlot, singleRow: true } : {},
|
|
@@ -11115,32 +11154,18 @@ function Grid({
|
|
|
11115
11154
|
)
|
|
11116
11155
|
] });
|
|
11117
11156
|
if (!merged || !phone) return all;
|
|
11118
|
-
return /* @__PURE__ */ (0, import_jsx_runtime39.
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
"
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
),
|
|
11131
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_design_system32.BottomSheet, { open: toolsOpen, onOpenChange: setToolsOpen, title: "Table tools", surface: "solid", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11132
|
-
"div",
|
|
11133
|
-
{
|
|
11134
|
-
"data-records-grid-tools-sheet": "",
|
|
11135
|
-
className: "flex flex-col items-stretch gap-1 px-3 pb-6 [&>button]:justify-start [&_button]:min-h-10",
|
|
11136
|
-
onClickCapture: (event) => {
|
|
11137
|
-
const target = event.target.closest("button");
|
|
11138
|
-
if (target && !target.hasAttribute("aria-haspopup")) setToolsOpen(false);
|
|
11139
|
-
},
|
|
11140
|
-
children: all
|
|
11141
|
-
}
|
|
11142
|
-
) })
|
|
11143
|
-
] });
|
|
11157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_jsx_runtime39.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_design_system32.BottomSheet, { open: toolsOpen, onOpenChange: setToolsOpen, title: "Table tools", surface: "solid", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11158
|
+
"div",
|
|
11159
|
+
{
|
|
11160
|
+
"data-records-grid-tools-sheet": "",
|
|
11161
|
+
className: "flex flex-col items-stretch gap-1 px-3 pb-6 [&>button]:justify-start [&_button]:min-h-10",
|
|
11162
|
+
onClickCapture: (event) => {
|
|
11163
|
+
const target = event.target.closest("button");
|
|
11164
|
+
if (target && !target.hasAttribute("aria-haspopup")) setToolsOpen(false);
|
|
11165
|
+
},
|
|
11166
|
+
children: all
|
|
11167
|
+
}
|
|
11168
|
+
) }) });
|
|
11144
11169
|
})()
|
|
11145
11170
|
}
|
|
11146
11171
|
},
|
|
@@ -13723,9 +13748,8 @@ function TableSettings({
|
|
|
13723
13748
|
shape.error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RefusalNotice, { error: shape.error }) : null,
|
|
13724
13749
|
askingToRemove ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded border border-destructive/40 px-2 py-1.5", children: [
|
|
13725
13750
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "text-destructive", children: [
|
|
13726
|
-
|
|
13727
|
-
|
|
13728
|
-
" takes it off every record of this table. The values stay in each record's history and nothing else is deleted."
|
|
13751
|
+
archiveConfirmSentence(`the field \u201C${fieldName(askingToRemove)}\u201D`),
|
|
13752
|
+
" Its values stay in every record and come back with it."
|
|
13729
13753
|
] }),
|
|
13730
13754
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
13731
13755
|
import_design_system41.Button,
|
|
@@ -21413,6 +21437,7 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
|
|
|
21413
21437
|
const [views, setViews] = (0, import_react126.useState)(null);
|
|
21414
21438
|
const [error, setError] = (0, import_react126.useState)(null);
|
|
21415
21439
|
const [busy, setBusy] = (0, import_react126.useState)(false);
|
|
21440
|
+
const [stopping, setStopping] = (0, import_react126.useState)(null);
|
|
21416
21441
|
const load = (0, import_react126.useCallback)(async () => {
|
|
21417
21442
|
const [held, offered] = await Promise.all([
|
|
21418
21443
|
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
@@ -21509,6 +21534,7 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
|
|
|
21509
21534
|
await load();
|
|
21510
21535
|
}
|
|
21511
21536
|
async function unsubscribe(ruleId) {
|
|
21537
|
+
setStopping(null);
|
|
21512
21538
|
setBusy(true);
|
|
21513
21539
|
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
21514
21540
|
setBusy(false);
|
|
@@ -21570,14 +21596,18 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
|
|
|
21570
21596
|
children: "Use for this form"
|
|
21571
21597
|
}
|
|
21572
21598
|
) : null,
|
|
21573
|
-
rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime73.
|
|
21599
|
+
rights.write && !(onChoose && held.rule_id === chosenRuleId) && stopping === held.rule_id ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: (0, import_design_system65.cn)("flex flex-wrap items-center gap-2", onChoose ? void 0 : "ml-auto"), children: [
|
|
21600
|
+
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
21601
|
+
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(import_design_system65.Button, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
21602
|
+
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(import_design_system65.Button, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
21603
|
+
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
|
|
21574
21604
|
import_design_system65.Button,
|
|
21575
21605
|
{
|
|
21576
21606
|
size: "sm",
|
|
21577
21607
|
variant: "ghost",
|
|
21578
21608
|
className: onChoose ? void 0 : "ml-auto",
|
|
21579
21609
|
disabled: busy,
|
|
21580
|
-
onClick: () =>
|
|
21610
|
+
onClick: () => setStopping(held.rule_id),
|
|
21581
21611
|
children: "Stop telling me"
|
|
21582
21612
|
}
|
|
21583
21613
|
) : null
|
|
@@ -23313,6 +23343,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
23313
23343
|
const [boards, setBoards] = (0, import_react139.useState)(null);
|
|
23314
23344
|
const [error, setError] = (0, import_react139.useState)(null);
|
|
23315
23345
|
const [activeId, setActiveId] = (0, import_react139.useState)(activeDashboardId ?? null);
|
|
23346
|
+
const [askingToRemove, setAskingToRemove] = (0, import_react139.useState)(null);
|
|
23316
23347
|
const [run, setRun] = (0, import_react139.useState)(null);
|
|
23317
23348
|
const [running, setRunning] = (0, import_react139.useState)(false);
|
|
23318
23349
|
const [question, setQuestion] = (0, import_react139.useState)("");
|
|
@@ -23423,6 +23454,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
23423
23454
|
else setError(again.error);
|
|
23424
23455
|
}, [client, activeId, filter, grain]);
|
|
23425
23456
|
async function remove(target) {
|
|
23457
|
+
setAskingToRemove(null);
|
|
23426
23458
|
const gone = await client.dashboardDelete({ dashboard_id: target.id });
|
|
23427
23459
|
if (!gone.ok) {
|
|
23428
23460
|
setError(gone.error);
|
|
@@ -23511,7 +23543,11 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
23511
23543
|
children: scheduling ? "Done" : "Send on a schedule"
|
|
23512
23544
|
}
|
|
23513
23545
|
),
|
|
23514
|
-
rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime80.
|
|
23546
|
+
rights.admin && askingToRemove === board.id ? /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(import_jsx_runtime80.Fragment, { children: [
|
|
23547
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence(`the dashboard \u201C${board.name}\u201D`) }),
|
|
23548
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_design_system72.Button, { size: "sm", variant: "destructive", onClick: () => void remove(board), children: "Remove it" }),
|
|
23549
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_design_system72.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(null), children: "Keep it" })
|
|
23550
|
+
] }) : rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_design_system72.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(board.id), children: "Remove" }) : null
|
|
23515
23551
|
] }) : null
|
|
23516
23552
|
] }),
|
|
23517
23553
|
board && scheduling ? (
|
|
@@ -26929,8 +26965,9 @@ function TablePage({
|
|
|
26929
26965
|
name: table.data?.name,
|
|
26930
26966
|
may: rights.share,
|
|
26931
26967
|
initiallyOpen: shareFromLink || shareAsked > 0,
|
|
26932
|
-
...phone ? {
|
|
26933
|
-
}
|
|
26968
|
+
...phone ? { className: "hidden" } : {}
|
|
26969
|
+
},
|
|
26970
|
+
phone ? `share-${shareAsked}` : "share"
|
|
26934
26971
|
),
|
|
26935
26972
|
/* @__PURE__ */ (0, import_jsx_runtime94.jsxs)(import_design_system85.DropdownMenu, { children: [
|
|
26936
26973
|
/* @__PURE__ */ (0, import_jsx_runtime94.jsx)(import_design_system85.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(import_design_system85.Button, { size: "sm", variant: "ghost", "aria-label": "Table menu", title: "Table menu", "data-table-menu": "", children: /* @__PURE__ */ (0, import_jsx_runtime94.jsxs)("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "currentColor", "aria-hidden": "true", children: [
|