@ai-matrx/records-ui 0.93.2 → 0.93.4
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 +17 -0
- package/dist/index.cjs +665 -424
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +579 -338
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4012,7 +4012,20 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow,
|
|
|
4012
4012
|
}
|
|
4013
4013
|
return null;
|
|
4014
4014
|
}, [client, tableId, reload, order]);
|
|
4015
|
+
const rowSignature = useCallback5(
|
|
4016
|
+
(rowId) => {
|
|
4017
|
+
const out = [];
|
|
4018
|
+
if (editing?.rowId === rowId) out.push("open", editing.key, draft);
|
|
4019
|
+
const prefix = `${rowId}::`;
|
|
4020
|
+
for (const id in optimistic) if (id.startsWith(prefix)) out.push("held", id, optimistic[id]);
|
|
4021
|
+
for (const id in saving) if (id.startsWith(prefix)) out.push("saving", id);
|
|
4022
|
+
for (const id in refusals) if (id.startsWith(prefix)) out.push("refused", id, refusals[id]);
|
|
4023
|
+
return out;
|
|
4024
|
+
},
|
|
4025
|
+
[editing, draft, optimistic, saving, refusals]
|
|
4026
|
+
);
|
|
4015
4027
|
return {
|
|
4028
|
+
rowSignature,
|
|
4016
4029
|
valueFor,
|
|
4017
4030
|
editing,
|
|
4018
4031
|
draft,
|
|
@@ -7699,6 +7712,10 @@ function useMergedGridControls(args) {
|
|
|
7699
7712
|
(columnKey) => enabled && fieldOf2(columnKey) ? headerMenuItems(columnKey, ctx) : void 0,
|
|
7700
7713
|
[enabled, ctx, fieldOf2]
|
|
7701
7714
|
);
|
|
7715
|
+
const columnsControl = useMemo14(
|
|
7716
|
+
() => enabled ? { order, hidden, onChange: keepColumns } : void 0,
|
|
7717
|
+
[enabled, order, hidden, keepColumns]
|
|
7718
|
+
);
|
|
7702
7719
|
const close = () => setDialog(null);
|
|
7703
7720
|
const dialogs = !enabled ? null : /* @__PURE__ */ jsxs24(Fragment8, { children: [
|
|
7704
7721
|
colours.dialogs,
|
|
@@ -7830,7 +7847,7 @@ function useMergedGridControls(args) {
|
|
|
7830
7847
|
headerMenuFor,
|
|
7831
7848
|
filterFor,
|
|
7832
7849
|
contextMenu,
|
|
7833
|
-
columns:
|
|
7850
|
+
columns: columnsControl,
|
|
7834
7851
|
dialogs,
|
|
7835
7852
|
addColumn: () => setDialog({ kind: "insert", anchor: null, side: "right" }),
|
|
7836
7853
|
sheetTools: enabled ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
|
|
@@ -7936,11 +7953,12 @@ function placePinnedRows(pageRows, pinned, anchors) {
|
|
|
7936
7953
|
}
|
|
7937
7954
|
function withAddRowCells(columns, line) {
|
|
7938
7955
|
if (line === null) return columns;
|
|
7956
|
+
const draw = () => typeof line === "function" ? line() : line;
|
|
7939
7957
|
return columns.map((column, index) => {
|
|
7940
7958
|
const own2 = column.cell;
|
|
7941
7959
|
return {
|
|
7942
7960
|
...column,
|
|
7943
|
-
cell: (row, rowIndex) => row.id === ADD_ROW_ID ? index === 0 ?
|
|
7961
|
+
cell: (row, rowIndex) => row.id === ADD_ROW_ID ? index === 0 ? draw() : null : own2 ? own2(row, rowIndex) : null
|
|
7944
7962
|
};
|
|
7945
7963
|
});
|
|
7946
7964
|
}
|
|
@@ -7963,6 +7981,7 @@ function AddRowLine({ onAdd, busy }) {
|
|
|
7963
7981
|
}
|
|
7964
7982
|
);
|
|
7965
7983
|
}
|
|
7984
|
+
var drawNothing = () => null;
|
|
7966
7985
|
function addColumnColumn(onAdd) {
|
|
7967
7986
|
return {
|
|
7968
7987
|
id: ADD_COLUMN_ID,
|
|
@@ -7982,7 +8001,8 @@ function addColumnColumn(onAdd) {
|
|
|
7982
8001
|
children: "+"
|
|
7983
8002
|
}
|
|
7984
8003
|
),
|
|
7985
|
-
|
|
8004
|
+
// ONE function for the life of the module: a body cell that draws nothing never changes.
|
|
8005
|
+
cell: drawNothing,
|
|
7986
8006
|
sortable: false,
|
|
7987
8007
|
filter: false,
|
|
7988
8008
|
hideable: false,
|
|
@@ -9277,7 +9297,7 @@ function GridLayoutMenu({
|
|
|
9277
9297
|
}
|
|
9278
9298
|
|
|
9279
9299
|
// src/gridUndo.tsx
|
|
9280
|
-
import { useCallback as useCallback13, useEffect as useEffect21, useMemo as useMemo18, useRef as useRef11, useState as useState26 } from "react";
|
|
9300
|
+
import { useCallback as useCallback13, useEffect as useEffect21, useLayoutEffect, useMemo as useMemo18, useRef as useRef11, useState as useState26, useSyncExternalStore } from "react";
|
|
9281
9301
|
import { useRecordsClient as useRecordsClient16 } from "@ai-matrx/records/react";
|
|
9282
9302
|
import { announceRecordChanged } from "@ai-matrx/records/core";
|
|
9283
9303
|
import { Button as Button23 } from "@ai-matrx/design-system";
|
|
@@ -9383,7 +9403,43 @@ function useGridUndo(args) {
|
|
|
9383
9403
|
[push, undo, redo, held, busy, notice, dismiss]
|
|
9384
9404
|
);
|
|
9385
9405
|
}
|
|
9386
|
-
function
|
|
9406
|
+
function useUndoSource(undo) {
|
|
9407
|
+
const box = useRef11({
|
|
9408
|
+
undo,
|
|
9409
|
+
face: { back: undo.depth.back, forward: undo.depth.forward, busy: undo.busy },
|
|
9410
|
+
listeners: /* @__PURE__ */ new Set()
|
|
9411
|
+
});
|
|
9412
|
+
box.current.undo = undo;
|
|
9413
|
+
const source = useMemo18(
|
|
9414
|
+
() => ({
|
|
9415
|
+
face: () => box.current.face,
|
|
9416
|
+
subscribe: (listener) => {
|
|
9417
|
+
box.current.listeners.add(listener);
|
|
9418
|
+
return () => {
|
|
9419
|
+
box.current.listeners.delete(listener);
|
|
9420
|
+
};
|
|
9421
|
+
},
|
|
9422
|
+
undo: () => void box.current.undo.undo(),
|
|
9423
|
+
redo: () => void box.current.undo.redo()
|
|
9424
|
+
}),
|
|
9425
|
+
[]
|
|
9426
|
+
);
|
|
9427
|
+
const { back, forward } = undo.depth;
|
|
9428
|
+
const busy = undo.busy;
|
|
9429
|
+
useLayoutEffect(() => {
|
|
9430
|
+
const held = box.current.face;
|
|
9431
|
+
if (held.back === back && held.forward === forward && held.busy === busy) return;
|
|
9432
|
+
box.current.face = { back, forward, busy };
|
|
9433
|
+
for (const listener of box.current.listeners) listener();
|
|
9434
|
+
}, [back, forward, busy]);
|
|
9435
|
+
return source;
|
|
9436
|
+
}
|
|
9437
|
+
function UndoRedoLive({ source }) {
|
|
9438
|
+
const face = useSyncExternalStore(source.subscribe, source.face, source.face);
|
|
9439
|
+
return /* @__PURE__ */ jsx37(UndoRedoPair, { face, onUndo: source.undo, onRedo: source.redo });
|
|
9440
|
+
}
|
|
9441
|
+
function UndoRedoPair({ face, onUndo, onRedo }) {
|
|
9442
|
+
const undo = { depth: { back: face.back, forward: face.forward }, busy: face.busy };
|
|
9387
9443
|
if (undo.depth.back === 0 && undo.depth.forward === 0) return null;
|
|
9388
9444
|
return /* @__PURE__ */ jsxs32(Fragment13, { children: [
|
|
9389
9445
|
/* @__PURE__ */ jsxs32(
|
|
@@ -9395,7 +9451,7 @@ function UndoRedoButtons({ undo }) {
|
|
|
9395
9451
|
title: undo.depth.back > 0 ? `Undo (Cmd-Z) \xB7 ${undo.depth.back} ${undo.depth.back === 1 ? "step" : "steps"} back` : "Nothing to undo",
|
|
9396
9452
|
"aria-label": `Undo, ${undo.depth.back} ${undo.depth.back === 1 ? "step" : "steps"}`,
|
|
9397
9453
|
disabled: undo.busy || undo.depth.back === 0,
|
|
9398
|
-
onClick:
|
|
9454
|
+
onClick: onUndo,
|
|
9399
9455
|
children: [
|
|
9400
9456
|
"Undo ",
|
|
9401
9457
|
/* @__PURE__ */ jsx37("span", { className: "ml-1 tabular-nums text-muted-foreground", "data-records-undo-count": "", children: undo.depth.back })
|
|
@@ -9411,7 +9467,7 @@ function UndoRedoButtons({ undo }) {
|
|
|
9411
9467
|
title: undo.depth.forward > 0 ? `Redo (Shift-Cmd-Z) \xB7 ${undo.depth.forward} ${undo.depth.forward === 1 ? "step" : "steps"} forward` : "Nothing to redo",
|
|
9412
9468
|
"aria-label": `Redo, ${undo.depth.forward} ${undo.depth.forward === 1 ? "step" : "steps"}`,
|
|
9413
9469
|
disabled: undo.busy || undo.depth.forward === 0,
|
|
9414
|
-
onClick:
|
|
9470
|
+
onClick: onRedo,
|
|
9415
9471
|
children: [
|
|
9416
9472
|
"Redo ",
|
|
9417
9473
|
/* @__PURE__ */ jsx37("span", { className: "ml-1 tabular-nums text-muted-foreground", "data-records-redo-count": "", children: undo.depth.forward })
|
|
@@ -10615,6 +10671,8 @@ function HandOrderDialog({
|
|
|
10615
10671
|
import { Fragment as Fragment15, jsx as jsx42, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
10616
10672
|
var NO_COLUMNS_YET = "This table has no columns yet";
|
|
10617
10673
|
var NO_COLUMNS_WHY = "A column is what a record holds \u2014 a name, a date, an amount, a person. Add the first one and the grid appears with it; everything else on this page works the moment it exists.";
|
|
10674
|
+
var NO_SUMMARIES = Object.freeze({});
|
|
10675
|
+
var rowIdOf = (row) => row.id;
|
|
10618
10676
|
function Grid({
|
|
10619
10677
|
tableId,
|
|
10620
10678
|
pageSize = 50,
|
|
@@ -10764,7 +10822,17 @@ function Grid({
|
|
|
10764
10822
|
onEnded: (exit, from, how) => sheetController.current?.endEdit(exit, { rowId: from.rowId, columnId: from.key }, how)
|
|
10765
10823
|
} : {}
|
|
10766
10824
|
});
|
|
10825
|
+
const editingNow = useRef14(editing);
|
|
10826
|
+
editingNow.current = editing;
|
|
10827
|
+
const editingHandle = useMemo22(
|
|
10828
|
+
() => new Proxy({}, {
|
|
10829
|
+
get: (_target, key) => editingNow.current[key]
|
|
10830
|
+
}),
|
|
10831
|
+
[]
|
|
10832
|
+
);
|
|
10833
|
+
const rowVersion = useCallback16((row) => editingNow.current.rowSignature(row.id), []);
|
|
10767
10834
|
const undo = useGridUndo({ tableId, reload: records.reload, forget: editing.forget });
|
|
10835
|
+
const undoSource = useUndoSource(undo);
|
|
10768
10836
|
pushWritten.current = (write) => {
|
|
10769
10837
|
const field2 = (fields.data ?? []).find((f) => f.key === write.key);
|
|
10770
10838
|
const name = changedRecordName(table.data, rows.find((r) => r.id === write.rowId)?.document ?? {}, write.key, write.after);
|
|
@@ -10834,7 +10902,7 @@ function Grid({
|
|
|
10834
10902
|
const [selectedIds, setSelectedIds] = useState31([]);
|
|
10835
10903
|
const offerSelection = canWrite && (merged || tableActions.runnable.length > 0);
|
|
10836
10904
|
const [reordering, setReordering] = useState31(false);
|
|
10837
|
-
const chosenSummaries = localSummaries ?? presentation.summaries ??
|
|
10905
|
+
const chosenSummaries = localSummaries ?? presentation.summaries ?? NO_SUMMARIES;
|
|
10838
10906
|
const summaries = useColumnSummaries({ tableId, chosen: chosenSummaries, filter: question.filter, version: records.data });
|
|
10839
10907
|
const [duplicated, setDuplicated] = useState31(null);
|
|
10840
10908
|
const duplicate = useCallback16(
|
|
@@ -11283,7 +11351,7 @@ function Grid({
|
|
|
11283
11351
|
() => ordered.map(
|
|
11284
11352
|
(field2) => columnForField(
|
|
11285
11353
|
field2,
|
|
11286
|
-
canWrite ?
|
|
11354
|
+
canWrite ? editingHandle : null,
|
|
11287
11355
|
mayWriteRow,
|
|
11288
11356
|
whyNotRow,
|
|
11289
11357
|
{
|
|
@@ -11302,7 +11370,7 @@ function Grid({
|
|
|
11302
11370
|
[
|
|
11303
11371
|
ordered,
|
|
11304
11372
|
canWrite,
|
|
11305
|
-
|
|
11373
|
+
editingHandle,
|
|
11306
11374
|
mayWriteRow,
|
|
11307
11375
|
whyNotRow,
|
|
11308
11376
|
presentation.formats,
|
|
@@ -11314,9 +11382,39 @@ function Grid({
|
|
|
11314
11382
|
pinCell
|
|
11315
11383
|
]
|
|
11316
11384
|
);
|
|
11317
|
-
const
|
|
11318
|
-
() =>
|
|
11319
|
-
|
|
11385
|
+
const footerNow = useRef14({
|
|
11386
|
+
summaryCell: () => null,
|
|
11387
|
+
addLine: () => null,
|
|
11388
|
+
choose: () => void 0,
|
|
11389
|
+
add: () => void 0
|
|
11390
|
+
});
|
|
11391
|
+
footerNow.current = {
|
|
11392
|
+
// The footer is memoised on what it shows (`footerRows` below), so its buttons act through here.
|
|
11393
|
+
choose: chooseSummary,
|
|
11394
|
+
add: () => void addRowHere(),
|
|
11395
|
+
summaryCell: (columnId) => {
|
|
11396
|
+
const field2 = ordered.find((f) => f.key === columnId);
|
|
11397
|
+
if (!field2) return null;
|
|
11398
|
+
return /* @__PURE__ */ jsx42(
|
|
11399
|
+
SummaryCell,
|
|
11400
|
+
{
|
|
11401
|
+
field: field2,
|
|
11402
|
+
firstColumn: ordered[0]?.key === columnId,
|
|
11403
|
+
chosen: chosenSummaries[columnId],
|
|
11404
|
+
value: summaries.values[columnId],
|
|
11405
|
+
rowCount: summaries.rowCount,
|
|
11406
|
+
summaries,
|
|
11407
|
+
onChoose: (op2) => footerNow.current.choose(columnId, op2)
|
|
11408
|
+
}
|
|
11409
|
+
);
|
|
11410
|
+
},
|
|
11411
|
+
addLine: () => /* @__PURE__ */ jsx42(AddRowLine, { onAdd: () => footerNow.current.add(), busy: added.busy })
|
|
11412
|
+
};
|
|
11413
|
+
const addColumnNow = useRef14(controls.addColumn);
|
|
11414
|
+
addColumnNow.current = controls.addColumn;
|
|
11415
|
+
const bodyColumns = useMemo22(
|
|
11416
|
+
() => withAddRowCells(
|
|
11417
|
+
withSummaryCells(
|
|
11320
11418
|
// THE ONE WAY A ROW OPENS ITS RECORD (VERIFIER-17): an Open control on the first cell,
|
|
11321
11419
|
// and Space on any cell. Absent when the host cannot open a record.
|
|
11322
11420
|
withOpenRecord(
|
|
@@ -11326,27 +11424,123 @@ function Grid({
|
|
|
11326
11424
|
// The merged grid's handle sits at the START of the row, where Airtable's expand sits.
|
|
11327
11425
|
merged ? "start" : "end"
|
|
11328
11426
|
),
|
|
11329
|
-
(columnId) =>
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11427
|
+
(columnId) => footerNow.current.summaryCell(columnId)
|
|
11428
|
+
),
|
|
11429
|
+
mayAddRow ? () => footerNow.current.addLine() : null
|
|
11430
|
+
),
|
|
11431
|
+
[fieldColumns, onOpenRecord, table.data, merged, mayAddRow]
|
|
11432
|
+
);
|
|
11433
|
+
const isRowSelectable = useCallback16((row) => row.id !== SUMMARY_ROW.id && mayWriteRow(row.id), [mayWriteRow]);
|
|
11434
|
+
const copyConfig = useMemo22(
|
|
11435
|
+
() => table.data ? gridCopyConfig({
|
|
11436
|
+
tableId,
|
|
11437
|
+
tableName: tableName(table.data),
|
|
11438
|
+
fields: ordered,
|
|
11439
|
+
rowLabel: (row) => recordNameIn(table.data, row.document ?? {})
|
|
11440
|
+
}) : null,
|
|
11441
|
+
[tableId, table.data, ordered]
|
|
11442
|
+
);
|
|
11443
|
+
const tableStyleProp = useMemo22(
|
|
11444
|
+
() => tableStyleIsEmpty2(paint.style) ? null : {
|
|
11445
|
+
style: paint.style,
|
|
11446
|
+
// A choice is read as its OPTION (`paintByOption`): the rule, the value and the
|
|
11447
|
+
// colour a column keeps for it all name the same option, however it is stored.
|
|
11448
|
+
documentOf: (row) => paint.documentOf(row.document ?? {}),
|
|
11449
|
+
// A value its column keeps a colour for wears THAT colour (`choiceLooks.ts`);
|
|
11450
|
+
// any other value still paints from its word.
|
|
11451
|
+
choiceColorFor: paint.choiceColorFor
|
|
11452
|
+
},
|
|
11453
|
+
[paint]
|
|
11454
|
+
);
|
|
11455
|
+
const rowActionsNow = useRef14({ runner, handToAgent, host, table: table.data, duplicate });
|
|
11456
|
+
rowActionsNow.current = { runner, handToAgent, host, table: table.data, duplicate };
|
|
11457
|
+
const offersRowActions = canWrite || Boolean(host.talkToRecord) || Boolean(host.runAgentAction && tableActions.agent.length > 0);
|
|
11458
|
+
const hasTalk = Boolean(host.talkToRecord);
|
|
11459
|
+
const hasAgent = Boolean(host.runAgentAction);
|
|
11460
|
+
const rowActions = useCallback16(
|
|
11461
|
+
(row) => /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11462
|
+
canWrite && mayWriteRow(row.id) ? tableActions.runnable.map((action) => /* @__PURE__ */ jsx42(
|
|
11463
|
+
ActionButton,
|
|
11464
|
+
{
|
|
11465
|
+
action,
|
|
11466
|
+
busy: runner.busy === action.id,
|
|
11467
|
+
onPress: () => rowActionsNow.current.runner.press(action, [row.id])
|
|
11468
|
+
},
|
|
11469
|
+
action.id
|
|
11470
|
+
)) : null,
|
|
11471
|
+
host.runAgentAction ? tableActions.agent.map((action) => /* @__PURE__ */ jsx42(
|
|
11472
|
+
ActionButton,
|
|
11473
|
+
{
|
|
11474
|
+
action,
|
|
11475
|
+
busy: false,
|
|
11476
|
+
onPress: () => rowActionsNow.current.handToAgent(action, row)
|
|
11477
|
+
},
|
|
11478
|
+
action.id
|
|
11479
|
+
)) : null,
|
|
11480
|
+
host.talkToRecord ? /* @__PURE__ */ jsx42(
|
|
11481
|
+
Button27,
|
|
11482
|
+
{
|
|
11483
|
+
size: "sm",
|
|
11484
|
+
variant: "ghost",
|
|
11485
|
+
onClick: () => rowActionsNow.current.host.talkToRecord?.({
|
|
11486
|
+
tableId,
|
|
11487
|
+
recordId: row.id,
|
|
11488
|
+
// ONE NAMING CALL (`names.ts`), not this file's own join
|
|
11489
|
+
// of a Table's title column to a document.
|
|
11490
|
+
title: recordNameIn(
|
|
11491
|
+
rowActionsNow.current.table,
|
|
11492
|
+
row.document ?? {}
|
|
11493
|
+
)
|
|
11494
|
+
}),
|
|
11495
|
+
children: "Talk"
|
|
11496
|
+
}
|
|
11497
|
+
) : null,
|
|
11498
|
+
Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(
|
|
11499
|
+
Button27,
|
|
11500
|
+
{
|
|
11501
|
+
size: "sm",
|
|
11502
|
+
variant: "ghost",
|
|
11503
|
+
"data-records-duplicate": row.id,
|
|
11504
|
+
title: "Duplicate this record",
|
|
11505
|
+
onClick: (event) => {
|
|
11506
|
+
event.stopPropagation();
|
|
11507
|
+
void rowActionsNow.current.duplicate(row);
|
|
11508
|
+
},
|
|
11509
|
+
children: "Duplicate"
|
|
11344
11510
|
}
|
|
11345
|
-
)
|
|
11346
|
-
|
|
11511
|
+
) : null,
|
|
11512
|
+
canWrite && mayWriteRow(row.id) ? /* @__PURE__ */ jsx42(
|
|
11513
|
+
Button27,
|
|
11514
|
+
{
|
|
11515
|
+
size: "sm",
|
|
11516
|
+
variant: "ghost",
|
|
11517
|
+
title: "Delete this record",
|
|
11518
|
+
onClick: () => setDeleting(row),
|
|
11519
|
+
children: "Delete"
|
|
11520
|
+
}
|
|
11521
|
+
) : null
|
|
11522
|
+
] }),
|
|
11523
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- `hasTalk`/`hasAgent` stand for the host ports the JSX reads; handlers run through `rowActionsNow`.
|
|
11524
|
+
[canWrite, mayWriteRow, tableActions.runnable, tableActions.agent, runner.busy, hasTalk, hasAgent, editable, rights.write]
|
|
11525
|
+
);
|
|
11526
|
+
const headerHeld = useRef14(/* @__PURE__ */ new Map());
|
|
11527
|
+
const menuLatest = useRef14(/* @__PURE__ */ new Map());
|
|
11528
|
+
const addColumnDef = useMemo22(() => addColumnColumn(() => addColumnNow.current()), []);
|
|
11529
|
+
const drawnColumns = useMemo22(
|
|
11530
|
+
() => {
|
|
11531
|
+
const filterFor = controls.filterFor;
|
|
11532
|
+
const drawn = withHeaderMenus(
|
|
11533
|
+
bodyColumns,
|
|
11534
|
+
(key) => holdHeaderMenu(headerHeld.current, menuLatest.current, key, controls.headerMenuFor(key)),
|
|
11535
|
+
(key) => holdByContent(headerHeld.current, `filter:${key}`, filterFor(key))
|
|
11536
|
+
);
|
|
11537
|
+
return merged && rights.structure && editable ? [...drawn, addColumnDef] : drawn;
|
|
11347
11538
|
},
|
|
11348
|
-
[
|
|
11539
|
+
[bodyColumns, controls.headerMenuFor, controls.filterFor, merged, rights.structure, editable, addColumnDef]
|
|
11349
11540
|
);
|
|
11541
|
+
const columnsHeld = useRef14(null);
|
|
11542
|
+
const columns = columnsHeld.current !== null && sameColumns(columnsHeld.current, drawnColumns) ? columnsHeld.current : drawnColumns;
|
|
11543
|
+
columnsHeld.current = columns;
|
|
11350
11544
|
const [localColumns, setLocalColumns] = useState31(
|
|
11351
11545
|
null
|
|
11352
11546
|
);
|
|
@@ -11419,6 +11613,274 @@ function Grid({
|
|
|
11419
11613
|
} : {}
|
|
11420
11614
|
};
|
|
11421
11615
|
}, [presentation, onPresentationChange, writeLook, merged, phone]);
|
|
11616
|
+
const queryTotal = records.data?.total ?? rows.length;
|
|
11617
|
+
const tableQuery = useMemo22(
|
|
11618
|
+
() => merged ? {
|
|
11619
|
+
// THE STORE ANSWERS THE QUERY (tranche 6k): `data` is already the page.
|
|
11620
|
+
mode: "controlled",
|
|
11621
|
+
state: query,
|
|
11622
|
+
totalItems: queryTotal,
|
|
11623
|
+
onStateChange: setQuery,
|
|
11624
|
+
sourceProcessing: { search: "source", columnFilters: "source", sort: "source" }
|
|
11625
|
+
} : { mode: "controlled-local", state: query, onStateChange: setQuery },
|
|
11626
|
+
[merged, query, queryTotal]
|
|
11627
|
+
);
|
|
11628
|
+
const showAddLine = mayAddRow && rows.length > 0;
|
|
11629
|
+
const showSummaryLine = rows.length > 0 && (!merged || Object.keys(chosenSummaries).length > 0);
|
|
11630
|
+
const footerFace = JSON.stringify([
|
|
11631
|
+
showAddLine ? added.busy : null,
|
|
11632
|
+
showSummaryLine ? [chosenSummaries, summaries.values, summaries.rowCount, summaries.loading] : null
|
|
11633
|
+
]);
|
|
11634
|
+
const footerRows = useMemo22(
|
|
11635
|
+
() => [...showAddLine ? [ADD_ROW] : [], ...showSummaryLine ? [SUMMARY_ROW] : []],
|
|
11636
|
+
// `footerFace`, `ordered` and the summaries' operations are what the footer's cells draw.
|
|
11637
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
11638
|
+
[showAddLine, showSummaryLine, footerFace, ordered, summaries.operations, summaries.askOperations]
|
|
11639
|
+
);
|
|
11640
|
+
const coverageTotal = typeof records.data?.total === "number" ? records.data.total : null;
|
|
11641
|
+
const tableCoverage = useMemo22(
|
|
11642
|
+
() => ({
|
|
11643
|
+
answeredBy: merged ? "source" : "client",
|
|
11644
|
+
...merged && coverageTotal !== null ? { matched: coverageTotal } : {},
|
|
11645
|
+
noun: "record",
|
|
11646
|
+
...coverageTotal !== null ? { total: coverageTotal } : {}
|
|
11647
|
+
}),
|
|
11648
|
+
[merged, coverageTotal]
|
|
11649
|
+
);
|
|
11650
|
+
const sheetNow = useRef14({ merged, writeRange, editing, fields: fields.data ?? [], mayWriteRow, notify: host.notify });
|
|
11651
|
+
sheetNow.current = { merged, writeRange, editing, fields: fields.data ?? [], mayWriteRow, notify: host.notify };
|
|
11652
|
+
const hasNotify = Boolean(host.notify);
|
|
11653
|
+
const readOnlyReason = !editable || !rights.known || rights.write ? "Editing is off here, so this table can be copied but not changed." : rights.why("write");
|
|
11654
|
+
const spreadsheetConfig = useMemo22(
|
|
11655
|
+
() => canWrite && editable ? {
|
|
11656
|
+
enabled: true,
|
|
11657
|
+
writableColumnIds: pasteableKeys,
|
|
11658
|
+
onPatch: (patches, intent) => {
|
|
11659
|
+
const now = sheetNow.current;
|
|
11660
|
+
if (now.merged) {
|
|
11661
|
+
void now.writeRange(patches, intent);
|
|
11662
|
+
return;
|
|
11663
|
+
}
|
|
11664
|
+
for (const patch of patches) {
|
|
11665
|
+
now.editing.setCell(patch.rowId, patch.columnId, patch.value);
|
|
11666
|
+
}
|
|
11667
|
+
},
|
|
11668
|
+
...merged ? { undoDepth: 0, onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11669
|
+
columnRefusal: (columnId) => {
|
|
11670
|
+
const field2 = sheetNow.current.fields.find((f) => f.key === columnId);
|
|
11671
|
+
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11672
|
+
return `${fieldName(field2)} cannot take a pasted value \u2014 ${fieldTypeLabel(field2)} is worked out or chosen, not typed.`;
|
|
11673
|
+
},
|
|
11674
|
+
validateCell: ({ columnId, value }) => {
|
|
11675
|
+
const field2 = sheetNow.current.fields.find((f) => f.key === columnId);
|
|
11676
|
+
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11677
|
+
const judged = valueFromPastedText(field2, value === null ? "" : String(value));
|
|
11678
|
+
return "refusal" in judged ? judged.refusal : void 0;
|
|
11679
|
+
},
|
|
11680
|
+
onPastePlan: receivePastePlan,
|
|
11681
|
+
/*
|
|
11682
|
+
* TYPE-TO-EDIT (design-system 0.43 `onEditRequest`): the cell a person is on
|
|
11683
|
+
* opens ITS OWN editor — the Field's — holding what they typed (a letter, an
|
|
11684
|
+
* IME composition, an Option/AltGr character, dictation). A worked-out column
|
|
11685
|
+
* or a row this person may not write answers false, so nothing is swallowed.
|
|
11686
|
+
*/
|
|
11687
|
+
onEditRequest: (address, seed) => {
|
|
11688
|
+
const now = sheetNow.current;
|
|
11689
|
+
const field2 = now.fields.find((f) => f.key === address.columnId);
|
|
11690
|
+
if (!field2 || !fieldIsEditable(field2) || !now.mayWriteRow(address.rowId)) return false;
|
|
11691
|
+
if (seed !== null && seed !== "" && typingOpens(field2) === "none") return false;
|
|
11692
|
+
now.editing.begin({ rowId: address.rowId, key: field2.key }, seed);
|
|
11693
|
+
return true;
|
|
11694
|
+
},
|
|
11695
|
+
...hasNotify ? { notify: (message) => sheetNow.current.notify?.success(message) } : {}
|
|
11696
|
+
} : {
|
|
11697
|
+
/*
|
|
11698
|
+
* RANGE SELECTION AND COPY FOR A READER (merge step 3, 0.42 `readOnly`). A person
|
|
11699
|
+
* who may not change this table may still drag out a range, select all and press
|
|
11700
|
+
* Cmd-C for a real Excel/Sheets block — the UX walk graded range selection BROKEN
|
|
11701
|
+
* on a table whose writes were refused. Every write gesture (cut, paste, clear,
|
|
11702
|
+
* fill, undo) says why it cannot, in the store's own words.
|
|
11703
|
+
*/
|
|
11704
|
+
enabled: true,
|
|
11705
|
+
readOnly: true,
|
|
11706
|
+
...merged ? { onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11707
|
+
readOnlyReason
|
|
11708
|
+
},
|
|
11709
|
+
[canWrite, editable, pasteableKeys, merged, followSelection, receivePastePlan, hasNotify, readOnlyReason]
|
|
11710
|
+
);
|
|
11711
|
+
const toolbarFields = fields.data;
|
|
11712
|
+
const hasSeveralRows = rows.length > 1;
|
|
11713
|
+
const editingBusy = editing.busy;
|
|
11714
|
+
const addRecord = editing.addRecord;
|
|
11715
|
+
const phoneSheetTools = merged && phone ? controls.sheetTools : null;
|
|
11716
|
+
const tableToolbar = useMemo22(
|
|
11717
|
+
() => ({
|
|
11718
|
+
search: true,
|
|
11719
|
+
searchPlaceholder: "Search records",
|
|
11720
|
+
/*
|
|
11721
|
+
* A PHONE (tranche 6o): search and the one Tools button sit side by side at the START of
|
|
11722
|
+
* the row, so Tools is never scrolled out of reach; the search is the same query.
|
|
11723
|
+
*/
|
|
11724
|
+
...merged && phone ? {
|
|
11725
|
+
customSearch: /* @__PURE__ */ jsx42("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", "data-records-grid-phone-search": "", children: /* @__PURE__ */ jsx42(
|
|
11726
|
+
"input",
|
|
11727
|
+
{
|
|
11728
|
+
type: "search",
|
|
11729
|
+
value: query.search,
|
|
11730
|
+
placeholder: "Search",
|
|
11731
|
+
"aria-label": "Search records",
|
|
11732
|
+
onChange: (event) => {
|
|
11733
|
+
const search = event.target.value;
|
|
11734
|
+
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11735
|
+
},
|
|
11736
|
+
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",
|
|
11737
|
+
style: { fontSize: "16px" }
|
|
11738
|
+
}
|
|
11739
|
+
) })
|
|
11740
|
+
} : {},
|
|
11741
|
+
/* ONE ROW (lane TABLE-PAGE-CHROME): on a table page the grid's search, columns and
|
|
11742
|
+
sort are drawn INTO the page's toolbar row, never a second bar under it. */
|
|
11743
|
+
...toolbarSlot !== void 0 ? { portalInto: toolbarSlot, singleRow: true } : {},
|
|
11744
|
+
// SCR-2 / SCR-4: PRESENT for an admin, ABSENT for everyone else.
|
|
11745
|
+
// There is no third state: this package ships no disabled variant of
|
|
11746
|
+
// these controls, so a member cannot be shown a dead one by mistake.
|
|
11747
|
+
/*
|
|
11748
|
+
* WHERE THE GRID'S CONTROLS GO (lane C, review 2026-09-26 D6). Inline when the row has
|
|
11749
|
+
* room; behind the row's "…" once the page row is folded that far (Undo / Redo and New
|
|
11750
|
+
* record stay beside it — the primary action is never folded); on a phone EVERY control
|
|
11751
|
+
* — the table's own (clear, copy/export, group, columns), these, the search, the sort and
|
|
11752
|
+
* the page's filter, layout and share — in ONE "Tools" sheet. Never dropped.
|
|
11753
|
+
*/
|
|
11754
|
+
actions: /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11755
|
+
manual ? /* @__PURE__ */ jsx42(
|
|
11756
|
+
SortStateControl,
|
|
11757
|
+
{
|
|
11758
|
+
...handOrderSortState({
|
|
11759
|
+
sortName: columnSort ? columnLabel(toolbarFields ?? [], columnSort.id) : null,
|
|
11760
|
+
direction: columnSort?.direction ?? null,
|
|
11761
|
+
onBack: () => setQuery((held) => ({ ...held, sort: null, page: 1 })),
|
|
11762
|
+
...viewOrder?.onUseSort && columnSort ? {
|
|
11763
|
+
onUseSort: () => {
|
|
11764
|
+
const sort = columnSort;
|
|
11765
|
+
const chosen = { field: sort.id, direction: sort.direction };
|
|
11766
|
+
setHeldOrder({ order: "sorted", sorts: [chosen] });
|
|
11767
|
+
viewOrder.onUseSort?.(chosen);
|
|
11768
|
+
host.notify?.success(
|
|
11769
|
+
`Sorted by ${columnLabel(toolbarFields ?? [], sort.id)} ${sortArrow(sort.direction)}. ${MANUAL_SORT.replacedToast}`
|
|
11770
|
+
);
|
|
11771
|
+
}
|
|
11772
|
+
} : {}
|
|
11773
|
+
})
|
|
11774
|
+
}
|
|
11775
|
+
) : null,
|
|
11776
|
+
viewId && viewOrder?.onPlaced && hasSeveralRows ? /* @__PURE__ */ jsx42(Button27, { size: "sm", variant: "ghost", onClick: () => setReordering(true), "data-records-reorder-open": "", children: "Reorder" }) : null,
|
|
11777
|
+
!merged && Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(Button27, { size: "sm", disabled: editingBusy, onClick: () => void addRecord(), children: editingBusy ? "Adding\u2026" : "New record" }) : null,
|
|
11778
|
+
rights.structure && onAddField ? /* @__PURE__ */ jsx42(AddFieldButton, { onClick: onAddField }) : null,
|
|
11779
|
+
/* @__PURE__ */ jsx42(
|
|
11780
|
+
GridLayoutMenu,
|
|
11781
|
+
{
|
|
11782
|
+
look,
|
|
11783
|
+
firstColumn: sortedKeys[0] ? {
|
|
11784
|
+
key: sortedKeys[0],
|
|
11785
|
+
name: (() => {
|
|
11786
|
+
const first = (toolbarFields ?? []).find((f) => f.key === sortedKeys[0]);
|
|
11787
|
+
return first ? fieldName(first) : humanize(sortedKeys[0]);
|
|
11788
|
+
})()
|
|
11789
|
+
} : null,
|
|
11790
|
+
onChange: changeLayout,
|
|
11791
|
+
kept: Boolean(onPresentationChange)
|
|
11792
|
+
}
|
|
11793
|
+
)
|
|
11794
|
+
] }),
|
|
11795
|
+
...merged ? {
|
|
11796
|
+
overflow: {
|
|
11797
|
+
mode: phone ? "sheet" : toolbarFold2 >= 3 ? "menu" : "inline",
|
|
11798
|
+
pinned: /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11799
|
+
/* @__PURE__ */ jsx42(UndoRedoLive, { source: undoSource }),
|
|
11800
|
+
Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(Button27, { size: "sm", className: "shrink-0", disabled: added.busy, onClick: () => addRowRef.current(), children: added.busy ? "Adding\u2026" : "New record" }) : null
|
|
11801
|
+
] }),
|
|
11802
|
+
...phone ? {
|
|
11803
|
+
sheetTop: /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1.5 pb-1", "data-records-grid-sheet-top": "", "data-matrx-table-tools-keep-open": "", children: [
|
|
11804
|
+
/* @__PURE__ */ jsx42(
|
|
11805
|
+
"input",
|
|
11806
|
+
{
|
|
11807
|
+
type: "search",
|
|
11808
|
+
value: query.search,
|
|
11809
|
+
placeholder: "Search",
|
|
11810
|
+
"aria-label": "Search records",
|
|
11811
|
+
onChange: (event) => {
|
|
11812
|
+
const search = event.target.value;
|
|
11813
|
+
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11814
|
+
},
|
|
11815
|
+
className: "h-10 w-full min-w-0 rounded-md border border-input bg-background px-2 text-foreground placeholder:text-muted-foreground",
|
|
11816
|
+
style: { fontSize: "16px" }
|
|
11817
|
+
}
|
|
11818
|
+
),
|
|
11819
|
+
/* @__PURE__ */ jsxs36("label", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-grid-sheet-sort": "", children: [
|
|
11820
|
+
/* @__PURE__ */ jsx42("span", { className: "shrink-0", children: "Sort by" }),
|
|
11821
|
+
/* @__PURE__ */ jsxs36(
|
|
11822
|
+
"select",
|
|
11823
|
+
{
|
|
11824
|
+
"aria-label": "Sort by",
|
|
11825
|
+
value: query.sort ? `${query.sort.id}:${query.sort.direction}` : "",
|
|
11826
|
+
onChange: (event) => {
|
|
11827
|
+
const [id, direction] = event.target.value.split(":");
|
|
11828
|
+
setQuery((held) => ({
|
|
11829
|
+
...held,
|
|
11830
|
+
page: 1,
|
|
11831
|
+
sort: id ? { id, direction: direction === "desc" ? "desc" : "asc" } : null
|
|
11832
|
+
}));
|
|
11833
|
+
},
|
|
11834
|
+
className: "h-10 min-w-0 flex-1 rounded-md border border-input bg-background px-2 text-foreground",
|
|
11835
|
+
style: { fontSize: "16px" },
|
|
11836
|
+
children: [
|
|
11837
|
+
/* @__PURE__ */ jsx42("option", { value: "", children: "No sort" }),
|
|
11838
|
+
ordered.flatMap((field2) => [
|
|
11839
|
+
/* @__PURE__ */ jsx42("option", { value: `${field2.key}:asc`, children: `${fieldName(field2)} \u2191` }, `${field2.key}:asc`),
|
|
11840
|
+
/* @__PURE__ */ jsx42("option", { value: `${field2.key}:desc`, children: `${fieldName(field2)} \u2193` }, `${field2.key}:desc`)
|
|
11841
|
+
])
|
|
11842
|
+
]
|
|
11843
|
+
}
|
|
11844
|
+
)
|
|
11845
|
+
] }),
|
|
11846
|
+
phoneSheetTools
|
|
11847
|
+
] }),
|
|
11848
|
+
...sheetExtras ? { sheetExtras } : {}
|
|
11849
|
+
} : {}
|
|
11850
|
+
}
|
|
11851
|
+
} : {}
|
|
11852
|
+
}),
|
|
11853
|
+
[
|
|
11854
|
+
merged,
|
|
11855
|
+
phone,
|
|
11856
|
+
query.search,
|
|
11857
|
+
query.sort,
|
|
11858
|
+
toolbarSlot,
|
|
11859
|
+
manual,
|
|
11860
|
+
columnSort,
|
|
11861
|
+
toolbarFields,
|
|
11862
|
+
viewOrder,
|
|
11863
|
+
host,
|
|
11864
|
+
viewId,
|
|
11865
|
+
hasSeveralRows,
|
|
11866
|
+
editable,
|
|
11867
|
+
rights.write,
|
|
11868
|
+
rights.structure,
|
|
11869
|
+
editingBusy,
|
|
11870
|
+
addRecord,
|
|
11871
|
+
onAddField,
|
|
11872
|
+
look,
|
|
11873
|
+
sortedKeys,
|
|
11874
|
+
changeLayout,
|
|
11875
|
+
onPresentationChange,
|
|
11876
|
+
toolbarFold2,
|
|
11877
|
+
undoSource,
|
|
11878
|
+
added.busy,
|
|
11879
|
+
ordered,
|
|
11880
|
+
phoneSheetTools,
|
|
11881
|
+
sheetExtras
|
|
11882
|
+
]
|
|
11883
|
+
);
|
|
11422
11884
|
const labelOfField = useCallback16(
|
|
11423
11885
|
(key) => {
|
|
11424
11886
|
const held = (fields.data ?? []).find((f) => f.key === key);
|
|
@@ -11579,46 +12041,27 @@ function Grid({
|
|
|
11579
12041
|
...fills ? { className: "min-h-0 flex-1" } : {},
|
|
11580
12042
|
data: rows,
|
|
11581
12043
|
columns,
|
|
11582
|
-
getRowId:
|
|
12044
|
+
getRowId: rowIdOf,
|
|
12045
|
+
rowVersion,
|
|
11583
12046
|
isLoading: nothingToShowYet,
|
|
11584
12047
|
isFetching: refreshing,
|
|
11585
12048
|
density: host.density ?? "condensed",
|
|
11586
12049
|
viewTabs: false,
|
|
11587
12050
|
stickyHeader: true,
|
|
11588
12051
|
pageSize,
|
|
11589
|
-
query:
|
|
11590
|
-
// THE STORE ANSWERS THE QUERY (tranche 6k): `data` is already the page.
|
|
11591
|
-
mode: "controlled",
|
|
11592
|
-
state: query,
|
|
11593
|
-
totalItems: records.data?.total ?? rows.length,
|
|
11594
|
-
onStateChange: setQuery,
|
|
11595
|
-
sourceProcessing: { search: "source", columnFilters: "source", sort: "source" }
|
|
11596
|
-
} : { mode: "controlled-local", state: query, onStateChange: setQuery },
|
|
12052
|
+
query: tableQuery,
|
|
11597
12053
|
tableClassName: cn20(
|
|
11598
12054
|
!look.wrap && "[&_tbody_td]:max-w-[28rem] [&_tbody_td]:overflow-hidden [&_tbody_td]:text-ellipsis [&_tbody_td]:whitespace-nowrap! [&_tbody_td_*]:whitespace-nowrap!",
|
|
11599
12055
|
look.fit === "scroll" && "sm:[&>table]:w-max! sm:[&>table]:max-w-none! sm:[&>table]:min-w-full!"
|
|
11600
12056
|
),
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
...mayAddRow && rows.length > 0 ? [ADD_ROW] : [],
|
|
11604
|
-
...rows.length > 0 && (!merged || Object.keys(chosenSummaries).length > 0) ? [SUMMARY_ROW] : []
|
|
11605
|
-
];
|
|
11606
|
-
return footerRows.length > 0 ? { footerRows } : {};
|
|
11607
|
-
})(),
|
|
11608
|
-
...table.data ? {
|
|
11609
|
-
copy: gridCopyConfig({
|
|
11610
|
-
tableId,
|
|
11611
|
-
tableName: tableName(table.data),
|
|
11612
|
-
fields: ordered,
|
|
11613
|
-
rowLabel: (row) => recordNameIn(table.data, row.document ?? {})
|
|
11614
|
-
})
|
|
11615
|
-
} : {},
|
|
12057
|
+
footerRows,
|
|
12058
|
+
...copyConfig ? { copy: copyConfig } : {},
|
|
11616
12059
|
...offerSelection ? {
|
|
11617
12060
|
selection: {
|
|
11618
12061
|
selectedIds,
|
|
11619
12062
|
onSelectedIdsChange: setSelectedIds,
|
|
11620
12063
|
noun,
|
|
11621
|
-
isRowSelectable
|
|
12064
|
+
isRowSelectable,
|
|
11622
12065
|
actions: (_selected, ids) => /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11623
12066
|
merged ? /* @__PURE__ */ jsx42(
|
|
11624
12067
|
BulkActions,
|
|
@@ -11645,154 +12088,18 @@ function Grid({
|
|
|
11645
12088
|
] })
|
|
11646
12089
|
}
|
|
11647
12090
|
} : {},
|
|
11648
|
-
detail:
|
|
11649
|
-
window:
|
|
12091
|
+
detail: DETAIL_OFF,
|
|
12092
|
+
window: WINDOW_OFF,
|
|
11650
12093
|
...controls.contextMenu ? { contextMenu: controls.contextMenu } : {},
|
|
11651
|
-
|
|
11652
|
-
spreadsheet: {
|
|
11653
|
-
enabled: true,
|
|
11654
|
-
writableColumnIds: pasteableKeys,
|
|
11655
|
-
onPatch: (patches, intent) => {
|
|
11656
|
-
if (merged) {
|
|
11657
|
-
void writeRange(patches, intent);
|
|
11658
|
-
return;
|
|
11659
|
-
}
|
|
11660
|
-
for (const patch of patches) {
|
|
11661
|
-
editing.setCell(patch.rowId, patch.columnId, patch.value);
|
|
11662
|
-
}
|
|
11663
|
-
},
|
|
11664
|
-
...merged ? { undoDepth: 0, onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11665
|
-
columnRefusal: (columnId) => {
|
|
11666
|
-
const field2 = (fields.data ?? []).find((f) => f.key === columnId);
|
|
11667
|
-
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11668
|
-
return `${fieldName(field2)} cannot take a pasted value \u2014 ${fieldTypeLabel(field2)} is worked out or chosen, not typed.`;
|
|
11669
|
-
},
|
|
11670
|
-
validateCell: ({ columnId, value }) => {
|
|
11671
|
-
const field2 = (fields.data ?? []).find((f) => f.key === columnId);
|
|
11672
|
-
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11673
|
-
const judged = valueFromPastedText(field2, value === null ? "" : String(value));
|
|
11674
|
-
return "refusal" in judged ? judged.refusal : void 0;
|
|
11675
|
-
},
|
|
11676
|
-
onPastePlan: receivePastePlan,
|
|
11677
|
-
/*
|
|
11678
|
-
* TYPE-TO-EDIT (design-system 0.43 `onEditRequest`): the cell a person is on
|
|
11679
|
-
* opens ITS OWN editor — the Field's — holding what they typed (a letter, an
|
|
11680
|
-
* IME composition, an Option/AltGr character, dictation). A worked-out column
|
|
11681
|
-
* or a row this person may not write answers false, so nothing is swallowed.
|
|
11682
|
-
*/
|
|
11683
|
-
onEditRequest: (address, seed) => {
|
|
11684
|
-
const field2 = (fields.data ?? []).find((f) => f.key === address.columnId);
|
|
11685
|
-
if (!field2 || !fieldIsEditable(field2) || !mayWriteRow(address.rowId)) return false;
|
|
11686
|
-
if (seed !== null && seed !== "" && typingOpens(field2) === "none") return false;
|
|
11687
|
-
editing.begin({ rowId: address.rowId, key: field2.key }, seed);
|
|
11688
|
-
return true;
|
|
11689
|
-
},
|
|
11690
|
-
...host.notify ? { notify: (message) => host.notify?.success(message) } : {}
|
|
11691
|
-
}
|
|
11692
|
-
} : {
|
|
11693
|
-
/*
|
|
11694
|
-
* RANGE SELECTION AND COPY FOR A READER (merge step 3, 0.42 `readOnly`). A person
|
|
11695
|
-
* who may not change this table may still drag out a range, select all and press
|
|
11696
|
-
* Cmd-C for a real Excel/Sheets block — the UX walk graded range selection BROKEN
|
|
11697
|
-
* on a table whose writes were refused. Every write gesture (cut, paste, clear,
|
|
11698
|
-
* fill, undo) says why it cannot, in the store's own words.
|
|
11699
|
-
*/
|
|
11700
|
-
spreadsheet: {
|
|
11701
|
-
enabled: true,
|
|
11702
|
-
readOnly: true,
|
|
11703
|
-
...merged ? { onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11704
|
-
readOnlyReason: !editable || !rights.known || rights.write ? "Editing is off here, so this table can be copied but not changed." : rights.why("write")
|
|
11705
|
-
}
|
|
11706
|
-
},
|
|
12094
|
+
spreadsheet: spreadsheetConfig,
|
|
11707
12095
|
...groupingConfig ? { grouping: groupingConfig } : {},
|
|
11708
12096
|
columnState,
|
|
11709
12097
|
...look.rowHeight ? { rowHeight: look.rowHeight } : {},
|
|
11710
|
-
virtualize: fills ?
|
|
11711
|
-
coverage:
|
|
11712
|
-
|
|
11713
|
-
...merged && typeof records.data?.total === "number" ? { matched: records.data.total } : {},
|
|
11714
|
-
noun: "record",
|
|
11715
|
-
...typeof records.data?.total === "number" ? { total: records.data.total } : {}
|
|
11716
|
-
},
|
|
11717
|
-
...!tableStyleIsEmpty2(paint.style) ? {
|
|
11718
|
-
tableStyle: {
|
|
11719
|
-
style: paint.style,
|
|
11720
|
-
// A choice is read as its OPTION (`paintByOption`): the rule, the value and the
|
|
11721
|
-
// colour a column keeps for it all name the same option, however it is stored.
|
|
11722
|
-
documentOf: (row) => paint.documentOf(row.document ?? {}),
|
|
11723
|
-
// A value its column keeps a colour for wears THAT colour (`choiceLooks.ts`);
|
|
11724
|
-
// any other value still paints from its word.
|
|
11725
|
-
choiceColorFor: paint.choiceColorFor
|
|
11726
|
-
}
|
|
11727
|
-
} : {},
|
|
12098
|
+
virtualize: fills ? VIRTUALIZE_FILLED : true,
|
|
12099
|
+
coverage: tableCoverage,
|
|
12100
|
+
...tableStyleProp ? { tableStyle: tableStyleProp } : {},
|
|
11728
12101
|
...onOpenRecord ? { onRowOpen: (row) => onOpenRecord(row.id) } : {},
|
|
11729
|
-
...
|
|
11730
|
-
// ABSENT, NEVER DEAD: a row this person may not delete carries no
|
|
11731
|
-
// Delete at all, rather than one that refuses when pressed — and a
|
|
11732
|
-
// deployment with no chat surface bound carries no Talk.
|
|
11733
|
-
rowActions: (row) => /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11734
|
-
canWrite && mayWriteRow(row.id) ? tableActions.runnable.map((action) => /* @__PURE__ */ jsx42(
|
|
11735
|
-
ActionButton,
|
|
11736
|
-
{
|
|
11737
|
-
action,
|
|
11738
|
-
busy: runner.busy === action.id,
|
|
11739
|
-
onPress: () => runner.press(action, [row.id])
|
|
11740
|
-
},
|
|
11741
|
-
action.id
|
|
11742
|
-
)) : null,
|
|
11743
|
-
host.runAgentAction ? tableActions.agent.map((action) => /* @__PURE__ */ jsx42(
|
|
11744
|
-
ActionButton,
|
|
11745
|
-
{
|
|
11746
|
-
action,
|
|
11747
|
-
busy: false,
|
|
11748
|
-
onPress: () => handToAgent(action, row)
|
|
11749
|
-
},
|
|
11750
|
-
action.id
|
|
11751
|
-
)) : null,
|
|
11752
|
-
host.talkToRecord ? /* @__PURE__ */ jsx42(
|
|
11753
|
-
Button27,
|
|
11754
|
-
{
|
|
11755
|
-
size: "sm",
|
|
11756
|
-
variant: "ghost",
|
|
11757
|
-
onClick: () => host.talkToRecord?.({
|
|
11758
|
-
tableId,
|
|
11759
|
-
recordId: row.id,
|
|
11760
|
-
// ONE NAMING CALL (`names.ts`), not this file's own join
|
|
11761
|
-
// of a Table's title column to a document.
|
|
11762
|
-
title: recordNameIn(
|
|
11763
|
-
table.data,
|
|
11764
|
-
row.document ?? {}
|
|
11765
|
-
)
|
|
11766
|
-
}),
|
|
11767
|
-
children: "Talk"
|
|
11768
|
-
}
|
|
11769
|
-
) : null,
|
|
11770
|
-
Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(
|
|
11771
|
-
Button27,
|
|
11772
|
-
{
|
|
11773
|
-
size: "sm",
|
|
11774
|
-
variant: "ghost",
|
|
11775
|
-
"data-records-duplicate": row.id,
|
|
11776
|
-
title: "Duplicate this record",
|
|
11777
|
-
onClick: (event) => {
|
|
11778
|
-
event.stopPropagation();
|
|
11779
|
-
void duplicate(row);
|
|
11780
|
-
},
|
|
11781
|
-
children: "Duplicate"
|
|
11782
|
-
}
|
|
11783
|
-
) : null,
|
|
11784
|
-
canWrite && mayWriteRow(row.id) ? /* @__PURE__ */ jsx42(
|
|
11785
|
-
Button27,
|
|
11786
|
-
{
|
|
11787
|
-
size: "sm",
|
|
11788
|
-
variant: "ghost",
|
|
11789
|
-
title: "Delete this record",
|
|
11790
|
-
onClick: () => setDeleting(row),
|
|
11791
|
-
children: "Delete"
|
|
11792
|
-
}
|
|
11793
|
-
) : null
|
|
11794
|
-
] })
|
|
11795
|
-
} : {},
|
|
12102
|
+
...offersRowActions ? { rowActions } : {},
|
|
11796
12103
|
emptyState: emptyWords({
|
|
11797
12104
|
error: records.error,
|
|
11798
12105
|
asked: questionIsAsked({ search: query.search, columnFilters: query.columnFilters, filter }),
|
|
@@ -11801,142 +12108,7 @@ function Grid({
|
|
|
11801
12108
|
...mayAddRow ? { addFirst: /* @__PURE__ */ jsx42(AddFirstRowButton, { onAdd: () => void addRowHere(), busy: added.busy }) } : {},
|
|
11802
12109
|
emptyDescription: rights.write ? "Add the first one and it appears here." : rights.known ? `Nothing has been written to this table. ${rights.why("write")}` : "Nothing has been written to this table."
|
|
11803
12110
|
}),
|
|
11804
|
-
toolbar:
|
|
11805
|
-
search: true,
|
|
11806
|
-
searchPlaceholder: "Search records",
|
|
11807
|
-
/*
|
|
11808
|
-
* A PHONE (tranche 6o): search and the one Tools button sit side by side at the START of
|
|
11809
|
-
* the row, so Tools is never scrolled out of reach; the search is the same query.
|
|
11810
|
-
*/
|
|
11811
|
-
...merged && phone ? {
|
|
11812
|
-
customSearch: /* @__PURE__ */ jsx42("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", "data-records-grid-phone-search": "", children: /* @__PURE__ */ jsx42(
|
|
11813
|
-
"input",
|
|
11814
|
-
{
|
|
11815
|
-
type: "search",
|
|
11816
|
-
value: query.search,
|
|
11817
|
-
placeholder: "Search",
|
|
11818
|
-
"aria-label": "Search records",
|
|
11819
|
-
onChange: (event) => {
|
|
11820
|
-
const search = event.target.value;
|
|
11821
|
-
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11822
|
-
},
|
|
11823
|
-
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",
|
|
11824
|
-
style: { fontSize: "16px" }
|
|
11825
|
-
}
|
|
11826
|
-
) })
|
|
11827
|
-
} : {},
|
|
11828
|
-
/* ONE ROW (lane TABLE-PAGE-CHROME): on a table page the grid's search, columns and
|
|
11829
|
-
sort are drawn INTO the page's toolbar row, never a second bar under it. */
|
|
11830
|
-
...toolbarSlot !== void 0 ? { portalInto: toolbarSlot, singleRow: true } : {},
|
|
11831
|
-
// SCR-2 / SCR-4: PRESENT for an admin, ABSENT for everyone else.
|
|
11832
|
-
// There is no third state: this package ships no disabled variant of
|
|
11833
|
-
// these controls, so a member cannot be shown a dead one by mistake.
|
|
11834
|
-
/*
|
|
11835
|
-
* WHERE THE GRID'S CONTROLS GO (lane C, review 2026-09-26 D6). Inline when the row has
|
|
11836
|
-
* room; behind the row's "…" once the page row is folded that far (Undo / Redo and New
|
|
11837
|
-
* record stay beside it — the primary action is never folded); on a phone EVERY control
|
|
11838
|
-
* — the table's own (clear, copy/export, group, columns), these, the search, the sort and
|
|
11839
|
-
* the page's filter, layout and share — in ONE "Tools" sheet. Never dropped.
|
|
11840
|
-
*/
|
|
11841
|
-
actions: /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11842
|
-
manual ? /* @__PURE__ */ jsx42(
|
|
11843
|
-
SortStateControl,
|
|
11844
|
-
{
|
|
11845
|
-
...handOrderSortState({
|
|
11846
|
-
sortName: columnSort ? columnLabel(fields.data ?? [], columnSort.id) : null,
|
|
11847
|
-
direction: columnSort?.direction ?? null,
|
|
11848
|
-
onBack: () => setQuery((held) => ({ ...held, sort: null, page: 1 })),
|
|
11849
|
-
...viewOrder?.onUseSort && columnSort ? {
|
|
11850
|
-
onUseSort: () => {
|
|
11851
|
-
const sort = columnSort;
|
|
11852
|
-
const chosen = { field: sort.id, direction: sort.direction };
|
|
11853
|
-
setHeldOrder({ order: "sorted", sorts: [chosen] });
|
|
11854
|
-
viewOrder.onUseSort?.(chosen);
|
|
11855
|
-
host.notify?.success(
|
|
11856
|
-
`Sorted by ${columnLabel(fields.data ?? [], sort.id)} ${sortArrow(sort.direction)}. ${MANUAL_SORT.replacedToast}`
|
|
11857
|
-
);
|
|
11858
|
-
}
|
|
11859
|
-
} : {}
|
|
11860
|
-
})
|
|
11861
|
-
}
|
|
11862
|
-
) : null,
|
|
11863
|
-
viewId && viewOrder?.onPlaced && rows.length > 1 ? /* @__PURE__ */ jsx42(Button27, { size: "sm", variant: "ghost", onClick: () => setReordering(true), "data-records-reorder-open": "", children: "Reorder" }) : null,
|
|
11864
|
-
!merged && Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(Button27, { size: "sm", disabled: editing.busy, onClick: () => void editing.addRecord(), children: editing.busy ? "Adding\u2026" : "New record" }) : null,
|
|
11865
|
-
rights.structure && onAddField ? /* @__PURE__ */ jsx42(AddFieldButton, { onClick: onAddField }) : null,
|
|
11866
|
-
/* @__PURE__ */ jsx42(
|
|
11867
|
-
GridLayoutMenu,
|
|
11868
|
-
{
|
|
11869
|
-
look,
|
|
11870
|
-
firstColumn: sortedKeys[0] ? {
|
|
11871
|
-
key: sortedKeys[0],
|
|
11872
|
-
name: (() => {
|
|
11873
|
-
const first = (fields.data ?? []).find((f) => f.key === sortedKeys[0]);
|
|
11874
|
-
return first ? fieldName(first) : humanize(sortedKeys[0]);
|
|
11875
|
-
})()
|
|
11876
|
-
} : null,
|
|
11877
|
-
onChange: changeLayout,
|
|
11878
|
-
kept: Boolean(onPresentationChange)
|
|
11879
|
-
}
|
|
11880
|
-
)
|
|
11881
|
-
] }),
|
|
11882
|
-
...merged ? {
|
|
11883
|
-
overflow: {
|
|
11884
|
-
mode: phone ? "sheet" : toolbarFold2 >= 3 ? "menu" : "inline",
|
|
11885
|
-
pinned: /* @__PURE__ */ jsxs36(Fragment15, { children: [
|
|
11886
|
-
/* @__PURE__ */ jsx42(UndoRedoButtons, { undo }),
|
|
11887
|
-
Boolean(editable) && rights.write ? /* @__PURE__ */ jsx42(Button27, { size: "sm", className: "shrink-0", disabled: added.busy, onClick: () => void addRowHere(), children: added.busy ? "Adding\u2026" : "New record" }) : null
|
|
11888
|
-
] }),
|
|
11889
|
-
...phone ? {
|
|
11890
|
-
sheetTop: /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1.5 pb-1", "data-records-grid-sheet-top": "", "data-matrx-table-tools-keep-open": "", children: [
|
|
11891
|
-
/* @__PURE__ */ jsx42(
|
|
11892
|
-
"input",
|
|
11893
|
-
{
|
|
11894
|
-
type: "search",
|
|
11895
|
-
value: query.search,
|
|
11896
|
-
placeholder: "Search",
|
|
11897
|
-
"aria-label": "Search records",
|
|
11898
|
-
onChange: (event) => {
|
|
11899
|
-
const search = event.target.value;
|
|
11900
|
-
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11901
|
-
},
|
|
11902
|
-
className: "h-10 w-full min-w-0 rounded-md border border-input bg-background px-2 text-foreground placeholder:text-muted-foreground",
|
|
11903
|
-
style: { fontSize: "16px" }
|
|
11904
|
-
}
|
|
11905
|
-
),
|
|
11906
|
-
/* @__PURE__ */ jsxs36("label", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-grid-sheet-sort": "", children: [
|
|
11907
|
-
/* @__PURE__ */ jsx42("span", { className: "shrink-0", children: "Sort by" }),
|
|
11908
|
-
/* @__PURE__ */ jsxs36(
|
|
11909
|
-
"select",
|
|
11910
|
-
{
|
|
11911
|
-
"aria-label": "Sort by",
|
|
11912
|
-
value: query.sort ? `${query.sort.id}:${query.sort.direction}` : "",
|
|
11913
|
-
onChange: (event) => {
|
|
11914
|
-
const [id, direction] = event.target.value.split(":");
|
|
11915
|
-
setQuery((held) => ({
|
|
11916
|
-
...held,
|
|
11917
|
-
page: 1,
|
|
11918
|
-
sort: id ? { id, direction: direction === "desc" ? "desc" : "asc" } : null
|
|
11919
|
-
}));
|
|
11920
|
-
},
|
|
11921
|
-
className: "h-10 min-w-0 flex-1 rounded-md border border-input bg-background px-2 text-foreground",
|
|
11922
|
-
style: { fontSize: "16px" },
|
|
11923
|
-
children: [
|
|
11924
|
-
/* @__PURE__ */ jsx42("option", { value: "", children: "No sort" }),
|
|
11925
|
-
ordered.flatMap((field2) => [
|
|
11926
|
-
/* @__PURE__ */ jsx42("option", { value: `${field2.key}:asc`, children: `${fieldName(field2)} \u2191` }, `${field2.key}:asc`),
|
|
11927
|
-
/* @__PURE__ */ jsx42("option", { value: `${field2.key}:desc`, children: `${fieldName(field2)} \u2193` }, `${field2.key}:desc`)
|
|
11928
|
-
])
|
|
11929
|
-
]
|
|
11930
|
-
}
|
|
11931
|
-
)
|
|
11932
|
-
] }),
|
|
11933
|
-
controls.sheetTools
|
|
11934
|
-
] }),
|
|
11935
|
-
...sheetExtras ? { sheetExtras } : {}
|
|
11936
|
-
} : {}
|
|
11937
|
-
}
|
|
11938
|
-
} : {}
|
|
11939
|
-
}
|
|
12111
|
+
toolbar: tableToolbar
|
|
11940
12112
|
},
|
|
11941
12113
|
`records-grid-${tableId}:${openingKey}`
|
|
11942
12114
|
)
|
|
@@ -12038,6 +12210,58 @@ function Grid({
|
|
|
12038
12210
|
}
|
|
12039
12211
|
) });
|
|
12040
12212
|
}
|
|
12213
|
+
var DETAIL_OFF = { enabled: false };
|
|
12214
|
+
var WINDOW_OFF = { enabled: false };
|
|
12215
|
+
var VIRTUALIZE_FILLED = { threshold: 40 };
|
|
12216
|
+
function menuFace(items) {
|
|
12217
|
+
return JSON.stringify(items, (_key, value) => typeof value === "function" ? "\u0192" : value);
|
|
12218
|
+
}
|
|
12219
|
+
function menuThroughLatest(items, at, latest) {
|
|
12220
|
+
return items.map((item) => {
|
|
12221
|
+
const path = `${at}/${item.id}`;
|
|
12222
|
+
return {
|
|
12223
|
+
...item,
|
|
12224
|
+
...item.onSelect ? { onSelect: () => latest.get(path)?.() } : {},
|
|
12225
|
+
...item.submenu ? { submenu: menuThroughLatest(item.submenu, path, latest) } : {}
|
|
12226
|
+
};
|
|
12227
|
+
});
|
|
12228
|
+
}
|
|
12229
|
+
function bindLatest(items, at, latest) {
|
|
12230
|
+
for (const item of items) {
|
|
12231
|
+
const path = `${at}/${item.id}`;
|
|
12232
|
+
if (item.onSelect) latest.set(path, item.onSelect);
|
|
12233
|
+
if (item.submenu) bindLatest(item.submenu, path, latest);
|
|
12234
|
+
}
|
|
12235
|
+
}
|
|
12236
|
+
function holdHeaderMenu(held, latest, key, items) {
|
|
12237
|
+
if (!items) return items;
|
|
12238
|
+
const at = `menu:${key}`;
|
|
12239
|
+
bindLatest(items, at, latest);
|
|
12240
|
+
const sig = menuFace(items);
|
|
12241
|
+
const was = held.get(at);
|
|
12242
|
+
if (was && was.sig === sig) return was.value;
|
|
12243
|
+
const value = menuThroughLatest(items, at, latest);
|
|
12244
|
+
held.set(at, { sig, value });
|
|
12245
|
+
return value;
|
|
12246
|
+
}
|
|
12247
|
+
function holdByContent(held, at, value) {
|
|
12248
|
+
if (value === void 0) return value;
|
|
12249
|
+
const sig = JSON.stringify(value);
|
|
12250
|
+
const was = held.get(at);
|
|
12251
|
+
if (was && was.sig === sig) return was.value;
|
|
12252
|
+
held.set(at, { sig, value });
|
|
12253
|
+
return value;
|
|
12254
|
+
}
|
|
12255
|
+
function sameColumns(a, b) {
|
|
12256
|
+
if (a === b) return true;
|
|
12257
|
+
if (a.length !== b.length) return false;
|
|
12258
|
+
return a.every((column, i) => {
|
|
12259
|
+
const other = b[i];
|
|
12260
|
+
if (column === other) return true;
|
|
12261
|
+
const keys = Object.keys(column);
|
|
12262
|
+
return keys.length === Object.keys(other).length && keys.every((key) => Object.is(column[key], other[key]));
|
|
12263
|
+
});
|
|
12264
|
+
}
|
|
12041
12265
|
function withHeaderMenus(columns, menuFor, filterFor) {
|
|
12042
12266
|
return columns.map((column) => {
|
|
12043
12267
|
const items = menuFor(String(column.id));
|
|
@@ -26424,6 +26648,7 @@ function useEmbedHandshake(args) {
|
|
|
26424
26648
|
}
|
|
26425
26649
|
|
|
26426
26650
|
// src/RecordsMount.tsx
|
|
26651
|
+
import { useRef as useRef26 } from "react";
|
|
26427
26652
|
import { RecordsProvider } from "@ai-matrx/records/react";
|
|
26428
26653
|
import { jsx as jsx91 } from "react/jsx-runtime";
|
|
26429
26654
|
var STORE_DECIDES_REASON = "This host offers exactly what the record store says this person may do: the level comes back with the table on the read door, so a control they cannot use is never drawn in the first place.";
|
|
@@ -26436,10 +26661,26 @@ function RecordsMount({
|
|
|
26436
26661
|
letTheStoreDecideRights,
|
|
26437
26662
|
children
|
|
26438
26663
|
}) {
|
|
26439
|
-
const bound =
|
|
26664
|
+
const bound = useShallowStableHost(host);
|
|
26440
26665
|
void letTheStoreDecideRights;
|
|
26441
26666
|
return /* @__PURE__ */ jsx91(RecordsProvider, { config, children: /* @__PURE__ */ jsx91(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ jsx91(RecordLabelProvider, { children }) }) });
|
|
26442
26667
|
}
|
|
26668
|
+
function useShallowStableHost(host) {
|
|
26669
|
+
const held = useRef26(null);
|
|
26670
|
+
const next = host ?? {};
|
|
26671
|
+
const previous = held.current;
|
|
26672
|
+
if (previous !== null && sameParts(previous, next)) return previous;
|
|
26673
|
+
const bound = { ...next };
|
|
26674
|
+
held.current = bound;
|
|
26675
|
+
return bound;
|
|
26676
|
+
}
|
|
26677
|
+
function sameParts(a, b) {
|
|
26678
|
+
const aKeys = Object.keys(a);
|
|
26679
|
+
const bKeys = Object.keys(b);
|
|
26680
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
26681
|
+
for (const key of aKeys) if (a[key] !== b[key]) return false;
|
|
26682
|
+
return true;
|
|
26683
|
+
}
|
|
26443
26684
|
function personActor(userId) {
|
|
26444
26685
|
return userId ? { actor: "user", user_id: userId } : { actor: "user" };
|
|
26445
26686
|
}
|
|
@@ -27042,7 +27283,7 @@ function TablesHome({ onOpenTable, organizationName, makingOnly = false, classNa
|
|
|
27042
27283
|
}
|
|
27043
27284
|
|
|
27044
27285
|
// src/TablePage.tsx
|
|
27045
|
-
import { useCallback as useCallback51, useEffect as useEffect72, useRef as
|
|
27286
|
+
import { useCallback as useCallback51, useEffect as useEffect72, useRef as useRef27, useState as useState84 } from "react";
|
|
27046
27287
|
import { archivedByLine as archivedByLine2 } from "@ai-matrx/records";
|
|
27047
27288
|
import { useFields as useFields26, useRecordsClient as useRecordsClient64, useTable as useTable24 } from "@ai-matrx/records/react";
|
|
27048
27289
|
import {
|
|
@@ -27684,7 +27925,7 @@ function TablePage({
|
|
|
27684
27925
|
lookDiffers: view ? lookDiffers(view, look) : false
|
|
27685
27926
|
};
|
|
27686
27927
|
const shownSignature = JSON.stringify(shownReport);
|
|
27687
|
-
const reportRef =
|
|
27928
|
+
const reportRef = useRef27(onShownViewChange);
|
|
27688
27929
|
reportRef.current = onShownViewChange;
|
|
27689
27930
|
useEffect72(() => {
|
|
27690
27931
|
reportRef.current?.(JSON.parse(shownSignature));
|