@ai-matrx/records-ui 0.93.3 → 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 +10 -0
- package/dist/index.cjs +396 -227
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +397 -228
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -7978,6 +7978,10 @@ function useMergedGridControls(args) {
|
|
|
7978
7978
|
(columnKey) => enabled && fieldOf2(columnKey) ? headerMenuItems(columnKey, ctx) : void 0,
|
|
7979
7979
|
[enabled, ctx, fieldOf2]
|
|
7980
7980
|
);
|
|
7981
|
+
const columnsControl = (0, import_react40.useMemo)(
|
|
7982
|
+
() => enabled ? { order, hidden, onChange: keepColumns } : void 0,
|
|
7983
|
+
[enabled, order, hidden, keepColumns]
|
|
7984
|
+
);
|
|
7981
7985
|
const close = () => setDialog(null);
|
|
7982
7986
|
const dialogs = !enabled ? null : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
7983
7987
|
colours.dialogs,
|
|
@@ -8109,7 +8113,7 @@ function useMergedGridControls(args) {
|
|
|
8109
8113
|
headerMenuFor,
|
|
8110
8114
|
filterFor,
|
|
8111
8115
|
contextMenu,
|
|
8112
|
-
columns:
|
|
8116
|
+
columns: columnsControl,
|
|
8113
8117
|
dialogs,
|
|
8114
8118
|
addColumn: () => setDialog({ kind: "insert", anchor: null, side: "right" }),
|
|
8115
8119
|
sheetTools: enabled ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
@@ -9639,7 +9643,43 @@ function useGridUndo(args) {
|
|
|
9639
9643
|
[push, undo, redo, held, busy, notice, dismiss]
|
|
9640
9644
|
);
|
|
9641
9645
|
}
|
|
9642
|
-
function
|
|
9646
|
+
function useUndoSource(undo) {
|
|
9647
|
+
const box = (0, import_react52.useRef)({
|
|
9648
|
+
undo,
|
|
9649
|
+
face: { back: undo.depth.back, forward: undo.depth.forward, busy: undo.busy },
|
|
9650
|
+
listeners: /* @__PURE__ */ new Set()
|
|
9651
|
+
});
|
|
9652
|
+
box.current.undo = undo;
|
|
9653
|
+
const source = (0, import_react52.useMemo)(
|
|
9654
|
+
() => ({
|
|
9655
|
+
face: () => box.current.face,
|
|
9656
|
+
subscribe: (listener) => {
|
|
9657
|
+
box.current.listeners.add(listener);
|
|
9658
|
+
return () => {
|
|
9659
|
+
box.current.listeners.delete(listener);
|
|
9660
|
+
};
|
|
9661
|
+
},
|
|
9662
|
+
undo: () => void box.current.undo.undo(),
|
|
9663
|
+
redo: () => void box.current.undo.redo()
|
|
9664
|
+
}),
|
|
9665
|
+
[]
|
|
9666
|
+
);
|
|
9667
|
+
const { back, forward } = undo.depth;
|
|
9668
|
+
const busy = undo.busy;
|
|
9669
|
+
(0, import_react52.useLayoutEffect)(() => {
|
|
9670
|
+
const held = box.current.face;
|
|
9671
|
+
if (held.back === back && held.forward === forward && held.busy === busy) return;
|
|
9672
|
+
box.current.face = { back, forward, busy };
|
|
9673
|
+
for (const listener of box.current.listeners) listener();
|
|
9674
|
+
}, [back, forward, busy]);
|
|
9675
|
+
return source;
|
|
9676
|
+
}
|
|
9677
|
+
function UndoRedoLive({ source }) {
|
|
9678
|
+
const face = (0, import_react52.useSyncExternalStore)(source.subscribe, source.face, source.face);
|
|
9679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(UndoRedoPair, { face, onUndo: source.undo, onRedo: source.redo });
|
|
9680
|
+
}
|
|
9681
|
+
function UndoRedoPair({ face, onUndo, onRedo }) {
|
|
9682
|
+
const undo = { depth: { back: face.back, forward: face.forward }, busy: face.busy };
|
|
9643
9683
|
if (undo.depth.back === 0 && undo.depth.forward === 0) return null;
|
|
9644
9684
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
9645
9685
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
@@ -9651,7 +9691,7 @@ function UndoRedoButtons({ undo }) {
|
|
|
9651
9691
|
title: undo.depth.back > 0 ? `Undo (Cmd-Z) \xB7 ${undo.depth.back} ${undo.depth.back === 1 ? "step" : "steps"} back` : "Nothing to undo",
|
|
9652
9692
|
"aria-label": `Undo, ${undo.depth.back} ${undo.depth.back === 1 ? "step" : "steps"}`,
|
|
9653
9693
|
disabled: undo.busy || undo.depth.back === 0,
|
|
9654
|
-
onClick:
|
|
9694
|
+
onClick: onUndo,
|
|
9655
9695
|
children: [
|
|
9656
9696
|
"Undo ",
|
|
9657
9697
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ml-1 tabular-nums text-muted-foreground", "data-records-undo-count": "", children: undo.depth.back })
|
|
@@ -9667,7 +9707,7 @@ function UndoRedoButtons({ undo }) {
|
|
|
9667
9707
|
title: undo.depth.forward > 0 ? `Redo (Shift-Cmd-Z) \xB7 ${undo.depth.forward} ${undo.depth.forward === 1 ? "step" : "steps"} forward` : "Nothing to redo",
|
|
9668
9708
|
"aria-label": `Redo, ${undo.depth.forward} ${undo.depth.forward === 1 ? "step" : "steps"}`,
|
|
9669
9709
|
disabled: undo.busy || undo.depth.forward === 0,
|
|
9670
|
-
onClick:
|
|
9710
|
+
onClick: onRedo,
|
|
9671
9711
|
children: [
|
|
9672
9712
|
"Redo ",
|
|
9673
9713
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ml-1 tabular-nums text-muted-foreground", "data-records-redo-count": "", children: undo.depth.forward })
|
|
@@ -11013,6 +11053,7 @@ function Grid({
|
|
|
11013
11053
|
);
|
|
11014
11054
|
const rowVersion = (0, import_react60.useCallback)((row) => editingNow.current.rowSignature(row.id), []);
|
|
11015
11055
|
const undo = useGridUndo({ tableId, reload: records.reload, forget: editing.forget });
|
|
11056
|
+
const undoSource = useUndoSource(undo);
|
|
11016
11057
|
pushWritten.current = (write) => {
|
|
11017
11058
|
const field2 = (fields.data ?? []).find((f) => f.key === write.key);
|
|
11018
11059
|
const name = changedRecordName(table.data, rows.find((r) => r.id === write.rowId)?.document ?? {}, write.key, write.after);
|
|
@@ -11564,9 +11605,14 @@ function Grid({
|
|
|
11564
11605
|
);
|
|
11565
11606
|
const footerNow = (0, import_react60.useRef)({
|
|
11566
11607
|
summaryCell: () => null,
|
|
11567
|
-
addLine: () => null
|
|
11608
|
+
addLine: () => null,
|
|
11609
|
+
choose: () => void 0,
|
|
11610
|
+
add: () => void 0
|
|
11568
11611
|
});
|
|
11569
11612
|
footerNow.current = {
|
|
11613
|
+
// The footer is memoised on what it shows (`footerRows` below), so its buttons act through here.
|
|
11614
|
+
choose: chooseSummary,
|
|
11615
|
+
add: () => void addRowHere(),
|
|
11570
11616
|
summaryCell: (columnId) => {
|
|
11571
11617
|
const field2 = ordered.find((f) => f.key === columnId);
|
|
11572
11618
|
if (!field2) return null;
|
|
@@ -11579,11 +11625,11 @@ function Grid({
|
|
|
11579
11625
|
value: summaries.values[columnId],
|
|
11580
11626
|
rowCount: summaries.rowCount,
|
|
11581
11627
|
summaries,
|
|
11582
|
-
onChoose: (op2) =>
|
|
11628
|
+
onChoose: (op2) => footerNow.current.choose(columnId, op2)
|
|
11583
11629
|
}
|
|
11584
11630
|
);
|
|
11585
11631
|
},
|
|
11586
|
-
addLine: () => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AddRowLine, { onAdd: () =>
|
|
11632
|
+
addLine: () => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AddRowLine, { onAdd: () => footerNow.current.add(), busy: added.busy })
|
|
11587
11633
|
};
|
|
11588
11634
|
const addColumnNow = (0, import_react60.useRef)(controls.addColumn);
|
|
11589
11635
|
addColumnNow.current = controls.addColumn;
|
|
@@ -11698,13 +11744,24 @@ function Grid({
|
|
|
11698
11744
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- `hasTalk`/`hasAgent` stand for the host ports the JSX reads; handlers run through `rowActionsNow`.
|
|
11699
11745
|
[canWrite, mayWriteRow, tableActions.runnable, tableActions.agent, runner.busy, hasTalk, hasAgent, editable, rights.write]
|
|
11700
11746
|
);
|
|
11701
|
-
const
|
|
11747
|
+
const headerHeld = (0, import_react60.useRef)(/* @__PURE__ */ new Map());
|
|
11748
|
+
const menuLatest = (0, import_react60.useRef)(/* @__PURE__ */ new Map());
|
|
11749
|
+
const addColumnDef = (0, import_react60.useMemo)(() => addColumnColumn(() => addColumnNow.current()), []);
|
|
11750
|
+
const drawnColumns = (0, import_react60.useMemo)(
|
|
11702
11751
|
() => {
|
|
11703
|
-
const
|
|
11704
|
-
|
|
11752
|
+
const filterFor = controls.filterFor;
|
|
11753
|
+
const drawn = withHeaderMenus(
|
|
11754
|
+
bodyColumns,
|
|
11755
|
+
(key) => holdHeaderMenu(headerHeld.current, menuLatest.current, key, controls.headerMenuFor(key)),
|
|
11756
|
+
(key) => holdByContent(headerHeld.current, `filter:${key}`, filterFor(key))
|
|
11757
|
+
);
|
|
11758
|
+
return merged && rights.structure && editable ? [...drawn, addColumnDef] : drawn;
|
|
11705
11759
|
},
|
|
11706
|
-
[bodyColumns, controls.headerMenuFor, controls.filterFor, merged, rights.structure, editable]
|
|
11760
|
+
[bodyColumns, controls.headerMenuFor, controls.filterFor, merged, rights.structure, editable, addColumnDef]
|
|
11707
11761
|
);
|
|
11762
|
+
const columnsHeld = (0, import_react60.useRef)(null);
|
|
11763
|
+
const columns = columnsHeld.current !== null && sameColumns(columnsHeld.current, drawnColumns) ? columnsHeld.current : drawnColumns;
|
|
11764
|
+
columnsHeld.current = columns;
|
|
11708
11765
|
const [localColumns, setLocalColumns] = (0, import_react60.useState)(
|
|
11709
11766
|
null
|
|
11710
11767
|
);
|
|
@@ -11777,6 +11834,274 @@ function Grid({
|
|
|
11777
11834
|
} : {}
|
|
11778
11835
|
};
|
|
11779
11836
|
}, [presentation, onPresentationChange, writeLook, merged, phone]);
|
|
11837
|
+
const queryTotal = records.data?.total ?? rows.length;
|
|
11838
|
+
const tableQuery = (0, import_react60.useMemo)(
|
|
11839
|
+
() => merged ? {
|
|
11840
|
+
// THE STORE ANSWERS THE QUERY (tranche 6k): `data` is already the page.
|
|
11841
|
+
mode: "controlled",
|
|
11842
|
+
state: query,
|
|
11843
|
+
totalItems: queryTotal,
|
|
11844
|
+
onStateChange: setQuery,
|
|
11845
|
+
sourceProcessing: { search: "source", columnFilters: "source", sort: "source" }
|
|
11846
|
+
} : { mode: "controlled-local", state: query, onStateChange: setQuery },
|
|
11847
|
+
[merged, query, queryTotal]
|
|
11848
|
+
);
|
|
11849
|
+
const showAddLine = mayAddRow && rows.length > 0;
|
|
11850
|
+
const showSummaryLine = rows.length > 0 && (!merged || Object.keys(chosenSummaries).length > 0);
|
|
11851
|
+
const footerFace = JSON.stringify([
|
|
11852
|
+
showAddLine ? added.busy : null,
|
|
11853
|
+
showSummaryLine ? [chosenSummaries, summaries.values, summaries.rowCount, summaries.loading] : null
|
|
11854
|
+
]);
|
|
11855
|
+
const footerRows = (0, import_react60.useMemo)(
|
|
11856
|
+
() => [...showAddLine ? [ADD_ROW] : [], ...showSummaryLine ? [SUMMARY_ROW] : []],
|
|
11857
|
+
// `footerFace`, `ordered` and the summaries' operations are what the footer's cells draw.
|
|
11858
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
11859
|
+
[showAddLine, showSummaryLine, footerFace, ordered, summaries.operations, summaries.askOperations]
|
|
11860
|
+
);
|
|
11861
|
+
const coverageTotal = typeof records.data?.total === "number" ? records.data.total : null;
|
|
11862
|
+
const tableCoverage = (0, import_react60.useMemo)(
|
|
11863
|
+
() => ({
|
|
11864
|
+
answeredBy: merged ? "source" : "client",
|
|
11865
|
+
...merged && coverageTotal !== null ? { matched: coverageTotal } : {},
|
|
11866
|
+
noun: "record",
|
|
11867
|
+
...coverageTotal !== null ? { total: coverageTotal } : {}
|
|
11868
|
+
}),
|
|
11869
|
+
[merged, coverageTotal]
|
|
11870
|
+
);
|
|
11871
|
+
const sheetNow = (0, import_react60.useRef)({ merged, writeRange, editing, fields: fields.data ?? [], mayWriteRow, notify: host.notify });
|
|
11872
|
+
sheetNow.current = { merged, writeRange, editing, fields: fields.data ?? [], mayWriteRow, notify: host.notify };
|
|
11873
|
+
const hasNotify = Boolean(host.notify);
|
|
11874
|
+
const readOnlyReason = !editable || !rights.known || rights.write ? "Editing is off here, so this table can be copied but not changed." : rights.why("write");
|
|
11875
|
+
const spreadsheetConfig = (0, import_react60.useMemo)(
|
|
11876
|
+
() => canWrite && editable ? {
|
|
11877
|
+
enabled: true,
|
|
11878
|
+
writableColumnIds: pasteableKeys,
|
|
11879
|
+
onPatch: (patches, intent) => {
|
|
11880
|
+
const now = sheetNow.current;
|
|
11881
|
+
if (now.merged) {
|
|
11882
|
+
void now.writeRange(patches, intent);
|
|
11883
|
+
return;
|
|
11884
|
+
}
|
|
11885
|
+
for (const patch of patches) {
|
|
11886
|
+
now.editing.setCell(patch.rowId, patch.columnId, patch.value);
|
|
11887
|
+
}
|
|
11888
|
+
},
|
|
11889
|
+
...merged ? { undoDepth: 0, onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11890
|
+
columnRefusal: (columnId) => {
|
|
11891
|
+
const field2 = sheetNow.current.fields.find((f) => f.key === columnId);
|
|
11892
|
+
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11893
|
+
return `${fieldName(field2)} cannot take a pasted value \u2014 ${fieldTypeLabel(field2)} is worked out or chosen, not typed.`;
|
|
11894
|
+
},
|
|
11895
|
+
validateCell: ({ columnId, value }) => {
|
|
11896
|
+
const field2 = sheetNow.current.fields.find((f) => f.key === columnId);
|
|
11897
|
+
if (!field2) return `This table has no column called ${columnId}.`;
|
|
11898
|
+
const judged = valueFromPastedText(field2, value === null ? "" : String(value));
|
|
11899
|
+
return "refusal" in judged ? judged.refusal : void 0;
|
|
11900
|
+
},
|
|
11901
|
+
onPastePlan: receivePastePlan,
|
|
11902
|
+
/*
|
|
11903
|
+
* TYPE-TO-EDIT (design-system 0.43 `onEditRequest`): the cell a person is on
|
|
11904
|
+
* opens ITS OWN editor — the Field's — holding what they typed (a letter, an
|
|
11905
|
+
* IME composition, an Option/AltGr character, dictation). A worked-out column
|
|
11906
|
+
* or a row this person may not write answers false, so nothing is swallowed.
|
|
11907
|
+
*/
|
|
11908
|
+
onEditRequest: (address, seed) => {
|
|
11909
|
+
const now = sheetNow.current;
|
|
11910
|
+
const field2 = now.fields.find((f) => f.key === address.columnId);
|
|
11911
|
+
if (!field2 || !fieldIsEditable(field2) || !now.mayWriteRow(address.rowId)) return false;
|
|
11912
|
+
if (seed !== null && seed !== "" && typingOpens(field2) === "none") return false;
|
|
11913
|
+
now.editing.begin({ rowId: address.rowId, key: field2.key }, seed);
|
|
11914
|
+
return true;
|
|
11915
|
+
},
|
|
11916
|
+
...hasNotify ? { notify: (message) => sheetNow.current.notify?.success(message) } : {}
|
|
11917
|
+
} : {
|
|
11918
|
+
/*
|
|
11919
|
+
* RANGE SELECTION AND COPY FOR A READER (merge step 3, 0.42 `readOnly`). A person
|
|
11920
|
+
* who may not change this table may still drag out a range, select all and press
|
|
11921
|
+
* Cmd-C for a real Excel/Sheets block — the UX walk graded range selection BROKEN
|
|
11922
|
+
* on a table whose writes were refused. Every write gesture (cut, paste, clear,
|
|
11923
|
+
* fill, undo) says why it cannot, in the store's own words.
|
|
11924
|
+
*/
|
|
11925
|
+
enabled: true,
|
|
11926
|
+
readOnly: true,
|
|
11927
|
+
...merged ? { onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
11928
|
+
readOnlyReason
|
|
11929
|
+
},
|
|
11930
|
+
[canWrite, editable, pasteableKeys, merged, followSelection, receivePastePlan, hasNotify, readOnlyReason]
|
|
11931
|
+
);
|
|
11932
|
+
const toolbarFields = fields.data;
|
|
11933
|
+
const hasSeveralRows = rows.length > 1;
|
|
11934
|
+
const editingBusy = editing.busy;
|
|
11935
|
+
const addRecord = editing.addRecord;
|
|
11936
|
+
const phoneSheetTools = merged && phone ? controls.sheetTools : null;
|
|
11937
|
+
const tableToolbar = (0, import_react60.useMemo)(
|
|
11938
|
+
() => ({
|
|
11939
|
+
search: true,
|
|
11940
|
+
searchPlaceholder: "Search records",
|
|
11941
|
+
/*
|
|
11942
|
+
* A PHONE (tranche 6o): search and the one Tools button sit side by side at the START of
|
|
11943
|
+
* the row, so Tools is never scrolled out of reach; the search is the same query.
|
|
11944
|
+
*/
|
|
11945
|
+
...merged && phone ? {
|
|
11946
|
+
customSearch: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", "data-records-grid-phone-search": "", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
11947
|
+
"input",
|
|
11948
|
+
{
|
|
11949
|
+
type: "search",
|
|
11950
|
+
value: query.search,
|
|
11951
|
+
placeholder: "Search",
|
|
11952
|
+
"aria-label": "Search records",
|
|
11953
|
+
onChange: (event) => {
|
|
11954
|
+
const search = event.target.value;
|
|
11955
|
+
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
11956
|
+
},
|
|
11957
|
+
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",
|
|
11958
|
+
style: { fontSize: "16px" }
|
|
11959
|
+
}
|
|
11960
|
+
) })
|
|
11961
|
+
} : {},
|
|
11962
|
+
/* ONE ROW (lane TABLE-PAGE-CHROME): on a table page the grid's search, columns and
|
|
11963
|
+
sort are drawn INTO the page's toolbar row, never a second bar under it. */
|
|
11964
|
+
...toolbarSlot !== void 0 ? { portalInto: toolbarSlot, singleRow: true } : {},
|
|
11965
|
+
// SCR-2 / SCR-4: PRESENT for an admin, ABSENT for everyone else.
|
|
11966
|
+
// There is no third state: this package ships no disabled variant of
|
|
11967
|
+
// these controls, so a member cannot be shown a dead one by mistake.
|
|
11968
|
+
/*
|
|
11969
|
+
* WHERE THE GRID'S CONTROLS GO (lane C, review 2026-09-26 D6). Inline when the row has
|
|
11970
|
+
* room; behind the row's "…" once the page row is folded that far (Undo / Redo and New
|
|
11971
|
+
* record stay beside it — the primary action is never folded); on a phone EVERY control
|
|
11972
|
+
* — the table's own (clear, copy/export, group, columns), these, the search, the sort and
|
|
11973
|
+
* the page's filter, layout and share — in ONE "Tools" sheet. Never dropped.
|
|
11974
|
+
*/
|
|
11975
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
11976
|
+
manual ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
11977
|
+
SortStateControl,
|
|
11978
|
+
{
|
|
11979
|
+
...handOrderSortState({
|
|
11980
|
+
sortName: columnSort ? columnLabel(toolbarFields ?? [], columnSort.id) : null,
|
|
11981
|
+
direction: columnSort?.direction ?? null,
|
|
11982
|
+
onBack: () => setQuery((held) => ({ ...held, sort: null, page: 1 })),
|
|
11983
|
+
...viewOrder?.onUseSort && columnSort ? {
|
|
11984
|
+
onUseSort: () => {
|
|
11985
|
+
const sort = columnSort;
|
|
11986
|
+
const chosen = { field: sort.id, direction: sort.direction };
|
|
11987
|
+
setHeldOrder({ order: "sorted", sorts: [chosen] });
|
|
11988
|
+
viewOrder.onUseSort?.(chosen);
|
|
11989
|
+
host.notify?.success(
|
|
11990
|
+
`Sorted by ${columnLabel(toolbarFields ?? [], sort.id)} ${sortArrow(sort.direction)}. ${MANUAL_SORT.replacedToast}`
|
|
11991
|
+
);
|
|
11992
|
+
}
|
|
11993
|
+
} : {}
|
|
11994
|
+
})
|
|
11995
|
+
}
|
|
11996
|
+
) : null,
|
|
11997
|
+
viewId && viewOrder?.onPlaced && hasSeveralRows ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", variant: "ghost", onClick: () => setReordering(true), "data-records-reorder-open": "", children: "Reorder" }) : null,
|
|
11998
|
+
!merged && Boolean(editable) && rights.write ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", disabled: editingBusy, onClick: () => void addRecord(), children: editingBusy ? "Adding\u2026" : "New record" }) : null,
|
|
11999
|
+
rights.structure && onAddField ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AddFieldButton, { onClick: onAddField }) : null,
|
|
12000
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12001
|
+
GridLayoutMenu,
|
|
12002
|
+
{
|
|
12003
|
+
look,
|
|
12004
|
+
firstColumn: sortedKeys[0] ? {
|
|
12005
|
+
key: sortedKeys[0],
|
|
12006
|
+
name: (() => {
|
|
12007
|
+
const first = (toolbarFields ?? []).find((f) => f.key === sortedKeys[0]);
|
|
12008
|
+
return first ? fieldName(first) : humanize(sortedKeys[0]);
|
|
12009
|
+
})()
|
|
12010
|
+
} : null,
|
|
12011
|
+
onChange: changeLayout,
|
|
12012
|
+
kept: Boolean(onPresentationChange)
|
|
12013
|
+
}
|
|
12014
|
+
)
|
|
12015
|
+
] }),
|
|
12016
|
+
...merged ? {
|
|
12017
|
+
overflow: {
|
|
12018
|
+
mode: phone ? "sheet" : toolbarFold2 >= 3 ? "menu" : "inline",
|
|
12019
|
+
pinned: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
12020
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(UndoRedoLive, { source: undoSource }),
|
|
12021
|
+
Boolean(editable) && rights.write ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", className: "shrink-0", disabled: added.busy, onClick: () => addRowRef.current(), children: added.busy ? "Adding\u2026" : "New record" }) : null
|
|
12022
|
+
] }),
|
|
12023
|
+
...phone ? {
|
|
12024
|
+
sheetTop: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5 pb-1", "data-records-grid-sheet-top": "", "data-matrx-table-tools-keep-open": "", children: [
|
|
12025
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12026
|
+
"input",
|
|
12027
|
+
{
|
|
12028
|
+
type: "search",
|
|
12029
|
+
value: query.search,
|
|
12030
|
+
placeholder: "Search",
|
|
12031
|
+
"aria-label": "Search records",
|
|
12032
|
+
onChange: (event) => {
|
|
12033
|
+
const search = event.target.value;
|
|
12034
|
+
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
12035
|
+
},
|
|
12036
|
+
className: "h-10 w-full min-w-0 rounded-md border border-input bg-background px-2 text-foreground placeholder:text-muted-foreground",
|
|
12037
|
+
style: { fontSize: "16px" }
|
|
12038
|
+
}
|
|
12039
|
+
),
|
|
12040
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-grid-sheet-sort": "", children: [
|
|
12041
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "shrink-0", children: "Sort by" }),
|
|
12042
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
12043
|
+
"select",
|
|
12044
|
+
{
|
|
12045
|
+
"aria-label": "Sort by",
|
|
12046
|
+
value: query.sort ? `${query.sort.id}:${query.sort.direction}` : "",
|
|
12047
|
+
onChange: (event) => {
|
|
12048
|
+
const [id, direction] = event.target.value.split(":");
|
|
12049
|
+
setQuery((held) => ({
|
|
12050
|
+
...held,
|
|
12051
|
+
page: 1,
|
|
12052
|
+
sort: id ? { id, direction: direction === "desc" ? "desc" : "asc" } : null
|
|
12053
|
+
}));
|
|
12054
|
+
},
|
|
12055
|
+
className: "h-10 min-w-0 flex-1 rounded-md border border-input bg-background px-2 text-foreground",
|
|
12056
|
+
style: { fontSize: "16px" },
|
|
12057
|
+
children: [
|
|
12058
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "No sort" }),
|
|
12059
|
+
ordered.flatMap((field2) => [
|
|
12060
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: `${field2.key}:asc`, children: `${fieldName(field2)} \u2191` }, `${field2.key}:asc`),
|
|
12061
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: `${field2.key}:desc`, children: `${fieldName(field2)} \u2193` }, `${field2.key}:desc`)
|
|
12062
|
+
])
|
|
12063
|
+
]
|
|
12064
|
+
}
|
|
12065
|
+
)
|
|
12066
|
+
] }),
|
|
12067
|
+
phoneSheetTools
|
|
12068
|
+
] }),
|
|
12069
|
+
...sheetExtras ? { sheetExtras } : {}
|
|
12070
|
+
} : {}
|
|
12071
|
+
}
|
|
12072
|
+
} : {}
|
|
12073
|
+
}),
|
|
12074
|
+
[
|
|
12075
|
+
merged,
|
|
12076
|
+
phone,
|
|
12077
|
+
query.search,
|
|
12078
|
+
query.sort,
|
|
12079
|
+
toolbarSlot,
|
|
12080
|
+
manual,
|
|
12081
|
+
columnSort,
|
|
12082
|
+
toolbarFields,
|
|
12083
|
+
viewOrder,
|
|
12084
|
+
host,
|
|
12085
|
+
viewId,
|
|
12086
|
+
hasSeveralRows,
|
|
12087
|
+
editable,
|
|
12088
|
+
rights.write,
|
|
12089
|
+
rights.structure,
|
|
12090
|
+
editingBusy,
|
|
12091
|
+
addRecord,
|
|
12092
|
+
onAddField,
|
|
12093
|
+
look,
|
|
12094
|
+
sortedKeys,
|
|
12095
|
+
changeLayout,
|
|
12096
|
+
onPresentationChange,
|
|
12097
|
+
toolbarFold2,
|
|
12098
|
+
undoSource,
|
|
12099
|
+
added.busy,
|
|
12100
|
+
ordered,
|
|
12101
|
+
phoneSheetTools,
|
|
12102
|
+
sheetExtras
|
|
12103
|
+
]
|
|
12104
|
+
);
|
|
11780
12105
|
const labelOfField = (0, import_react60.useCallback)(
|
|
11781
12106
|
(key) => {
|
|
11782
12107
|
const held = (fields.data ?? []).find((f) => f.key === key);
|
|
@@ -11945,25 +12270,12 @@ function Grid({
|
|
|
11945
12270
|
viewTabs: false,
|
|
11946
12271
|
stickyHeader: true,
|
|
11947
12272
|
pageSize,
|
|
11948
|
-
query:
|
|
11949
|
-
// THE STORE ANSWERS THE QUERY (tranche 6k): `data` is already the page.
|
|
11950
|
-
mode: "controlled",
|
|
11951
|
-
state: query,
|
|
11952
|
-
totalItems: records.data?.total ?? rows.length,
|
|
11953
|
-
onStateChange: setQuery,
|
|
11954
|
-
sourceProcessing: { search: "source", columnFilters: "source", sort: "source" }
|
|
11955
|
-
} : { mode: "controlled-local", state: query, onStateChange: setQuery },
|
|
12273
|
+
query: tableQuery,
|
|
11956
12274
|
tableClassName: (0, import_design_system35.cn)(
|
|
11957
12275
|
!look.wrap && "[&_tbody_td]:max-w-[28rem] [&_tbody_td]:overflow-hidden [&_tbody_td]:text-ellipsis [&_tbody_td]:whitespace-nowrap! [&_tbody_td_*]:whitespace-nowrap!",
|
|
11958
12276
|
look.fit === "scroll" && "sm:[&>table]:w-max! sm:[&>table]:max-w-none! sm:[&>table]:min-w-full!"
|
|
11959
12277
|
),
|
|
11960
|
-
|
|
11961
|
-
const footerRows = [
|
|
11962
|
-
...mayAddRow && rows.length > 0 ? [ADD_ROW] : [],
|
|
11963
|
-
...rows.length > 0 && (!merged || Object.keys(chosenSummaries).length > 0) ? [SUMMARY_ROW] : []
|
|
11964
|
-
];
|
|
11965
|
-
return footerRows.length > 0 ? { footerRows } : {};
|
|
11966
|
-
})(),
|
|
12278
|
+
footerRows,
|
|
11967
12279
|
...copyConfig ? { copy: copyConfig } : {},
|
|
11968
12280
|
...offerSelection ? {
|
|
11969
12281
|
selection: {
|
|
@@ -11997,75 +12309,15 @@ function Grid({
|
|
|
11997
12309
|
] })
|
|
11998
12310
|
}
|
|
11999
12311
|
} : {},
|
|
12000
|
-
detail:
|
|
12001
|
-
window:
|
|
12312
|
+
detail: DETAIL_OFF,
|
|
12313
|
+
window: WINDOW_OFF,
|
|
12002
12314
|
...controls.contextMenu ? { contextMenu: controls.contextMenu } : {},
|
|
12003
|
-
|
|
12004
|
-
spreadsheet: {
|
|
12005
|
-
enabled: true,
|
|
12006
|
-
writableColumnIds: pasteableKeys,
|
|
12007
|
-
onPatch: (patches, intent) => {
|
|
12008
|
-
if (merged) {
|
|
12009
|
-
void writeRange(patches, intent);
|
|
12010
|
-
return;
|
|
12011
|
-
}
|
|
12012
|
-
for (const patch of patches) {
|
|
12013
|
-
editing.setCell(patch.rowId, patch.columnId, patch.value);
|
|
12014
|
-
}
|
|
12015
|
-
},
|
|
12016
|
-
...merged ? { undoDepth: 0, onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
12017
|
-
columnRefusal: (columnId) => {
|
|
12018
|
-
const field2 = (fields.data ?? []).find((f) => f.key === columnId);
|
|
12019
|
-
if (!field2) return `This table has no column called ${columnId}.`;
|
|
12020
|
-
return `${fieldName(field2)} cannot take a pasted value \u2014 ${fieldTypeLabel(field2)} is worked out or chosen, not typed.`;
|
|
12021
|
-
},
|
|
12022
|
-
validateCell: ({ columnId, value }) => {
|
|
12023
|
-
const field2 = (fields.data ?? []).find((f) => f.key === columnId);
|
|
12024
|
-
if (!field2) return `This table has no column called ${columnId}.`;
|
|
12025
|
-
const judged = valueFromPastedText(field2, value === null ? "" : String(value));
|
|
12026
|
-
return "refusal" in judged ? judged.refusal : void 0;
|
|
12027
|
-
},
|
|
12028
|
-
onPastePlan: receivePastePlan,
|
|
12029
|
-
/*
|
|
12030
|
-
* TYPE-TO-EDIT (design-system 0.43 `onEditRequest`): the cell a person is on
|
|
12031
|
-
* opens ITS OWN editor — the Field's — holding what they typed (a letter, an
|
|
12032
|
-
* IME composition, an Option/AltGr character, dictation). A worked-out column
|
|
12033
|
-
* or a row this person may not write answers false, so nothing is swallowed.
|
|
12034
|
-
*/
|
|
12035
|
-
onEditRequest: (address, seed) => {
|
|
12036
|
-
const field2 = (fields.data ?? []).find((f) => f.key === address.columnId);
|
|
12037
|
-
if (!field2 || !fieldIsEditable(field2) || !mayWriteRow(address.rowId)) return false;
|
|
12038
|
-
if (seed !== null && seed !== "" && typingOpens(field2) === "none") return false;
|
|
12039
|
-
editing.begin({ rowId: address.rowId, key: field2.key }, seed);
|
|
12040
|
-
return true;
|
|
12041
|
-
},
|
|
12042
|
-
...host.notify ? { notify: (message) => host.notify?.success(message) } : {}
|
|
12043
|
-
}
|
|
12044
|
-
} : {
|
|
12045
|
-
/*
|
|
12046
|
-
* RANGE SELECTION AND COPY FOR A READER (merge step 3, 0.42 `readOnly`). A person
|
|
12047
|
-
* who may not change this table may still drag out a range, select all and press
|
|
12048
|
-
* Cmd-C for a real Excel/Sheets block — the UX walk graded range selection BROKEN
|
|
12049
|
-
* on a table whose writes were refused. Every write gesture (cut, paste, clear,
|
|
12050
|
-
* fill, undo) says why it cannot, in the store's own words.
|
|
12051
|
-
*/
|
|
12052
|
-
spreadsheet: {
|
|
12053
|
-
enabled: true,
|
|
12054
|
-
readOnly: true,
|
|
12055
|
-
...merged ? { onSelectionChange: followSelection, controllerRef: sheetController } : {},
|
|
12056
|
-
readOnlyReason: !editable || !rights.known || rights.write ? "Editing is off here, so this table can be copied but not changed." : rights.why("write")
|
|
12057
|
-
}
|
|
12058
|
-
},
|
|
12315
|
+
spreadsheet: spreadsheetConfig,
|
|
12059
12316
|
...groupingConfig ? { grouping: groupingConfig } : {},
|
|
12060
12317
|
columnState,
|
|
12061
12318
|
...look.rowHeight ? { rowHeight: look.rowHeight } : {},
|
|
12062
|
-
virtualize: fills ?
|
|
12063
|
-
coverage:
|
|
12064
|
-
answeredBy: merged ? "source" : "client",
|
|
12065
|
-
...merged && typeof records.data?.total === "number" ? { matched: records.data.total } : {},
|
|
12066
|
-
noun: "record",
|
|
12067
|
-
...typeof records.data?.total === "number" ? { total: records.data.total } : {}
|
|
12068
|
-
},
|
|
12319
|
+
virtualize: fills ? VIRTUALIZE_FILLED : true,
|
|
12320
|
+
coverage: tableCoverage,
|
|
12069
12321
|
...tableStyleProp ? { tableStyle: tableStyleProp } : {},
|
|
12070
12322
|
...onOpenRecord ? { onRowOpen: (row) => onOpenRecord(row.id) } : {},
|
|
12071
12323
|
...offersRowActions ? { rowActions } : {},
|
|
@@ -12077,142 +12329,7 @@ function Grid({
|
|
|
12077
12329
|
...mayAddRow ? { addFirst: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AddFirstRowButton, { onAdd: () => void addRowHere(), busy: added.busy }) } : {},
|
|
12078
12330
|
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."
|
|
12079
12331
|
}),
|
|
12080
|
-
toolbar:
|
|
12081
|
-
search: true,
|
|
12082
|
-
searchPlaceholder: "Search records",
|
|
12083
|
-
/*
|
|
12084
|
-
* A PHONE (tranche 6o): search and the one Tools button sit side by side at the START of
|
|
12085
|
-
* the row, so Tools is never scrolled out of reach; the search is the same query.
|
|
12086
|
-
*/
|
|
12087
|
-
...merged && phone ? {
|
|
12088
|
-
customSearch: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", "data-records-grid-phone-search": "", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12089
|
-
"input",
|
|
12090
|
-
{
|
|
12091
|
-
type: "search",
|
|
12092
|
-
value: query.search,
|
|
12093
|
-
placeholder: "Search",
|
|
12094
|
-
"aria-label": "Search records",
|
|
12095
|
-
onChange: (event) => {
|
|
12096
|
-
const search = event.target.value;
|
|
12097
|
-
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
12098
|
-
},
|
|
12099
|
-
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",
|
|
12100
|
-
style: { fontSize: "16px" }
|
|
12101
|
-
}
|
|
12102
|
-
) })
|
|
12103
|
-
} : {},
|
|
12104
|
-
/* ONE ROW (lane TABLE-PAGE-CHROME): on a table page the grid's search, columns and
|
|
12105
|
-
sort are drawn INTO the page's toolbar row, never a second bar under it. */
|
|
12106
|
-
...toolbarSlot !== void 0 ? { portalInto: toolbarSlot, singleRow: true } : {},
|
|
12107
|
-
// SCR-2 / SCR-4: PRESENT for an admin, ABSENT for everyone else.
|
|
12108
|
-
// There is no third state: this package ships no disabled variant of
|
|
12109
|
-
// these controls, so a member cannot be shown a dead one by mistake.
|
|
12110
|
-
/*
|
|
12111
|
-
* WHERE THE GRID'S CONTROLS GO (lane C, review 2026-09-26 D6). Inline when the row has
|
|
12112
|
-
* room; behind the row's "…" once the page row is folded that far (Undo / Redo and New
|
|
12113
|
-
* record stay beside it — the primary action is never folded); on a phone EVERY control
|
|
12114
|
-
* — the table's own (clear, copy/export, group, columns), these, the search, the sort and
|
|
12115
|
-
* the page's filter, layout and share — in ONE "Tools" sheet. Never dropped.
|
|
12116
|
-
*/
|
|
12117
|
-
actions: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
12118
|
-
manual ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12119
|
-
SortStateControl,
|
|
12120
|
-
{
|
|
12121
|
-
...handOrderSortState({
|
|
12122
|
-
sortName: columnSort ? columnLabel(fields.data ?? [], columnSort.id) : null,
|
|
12123
|
-
direction: columnSort?.direction ?? null,
|
|
12124
|
-
onBack: () => setQuery((held) => ({ ...held, sort: null, page: 1 })),
|
|
12125
|
-
...viewOrder?.onUseSort && columnSort ? {
|
|
12126
|
-
onUseSort: () => {
|
|
12127
|
-
const sort = columnSort;
|
|
12128
|
-
const chosen = { field: sort.id, direction: sort.direction };
|
|
12129
|
-
setHeldOrder({ order: "sorted", sorts: [chosen] });
|
|
12130
|
-
viewOrder.onUseSort?.(chosen);
|
|
12131
|
-
host.notify?.success(
|
|
12132
|
-
`Sorted by ${columnLabel(fields.data ?? [], sort.id)} ${sortArrow(sort.direction)}. ${MANUAL_SORT.replacedToast}`
|
|
12133
|
-
);
|
|
12134
|
-
}
|
|
12135
|
-
} : {}
|
|
12136
|
-
})
|
|
12137
|
-
}
|
|
12138
|
-
) : null,
|
|
12139
|
-
viewId && viewOrder?.onPlaced && rows.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", variant: "ghost", onClick: () => setReordering(true), "data-records-reorder-open": "", children: "Reorder" }) : null,
|
|
12140
|
-
!merged && Boolean(editable) && rights.write ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", disabled: editing.busy, onClick: () => void editing.addRecord(), children: editing.busy ? "Adding\u2026" : "New record" }) : null,
|
|
12141
|
-
rights.structure && onAddField ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AddFieldButton, { onClick: onAddField }) : null,
|
|
12142
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12143
|
-
GridLayoutMenu,
|
|
12144
|
-
{
|
|
12145
|
-
look,
|
|
12146
|
-
firstColumn: sortedKeys[0] ? {
|
|
12147
|
-
key: sortedKeys[0],
|
|
12148
|
-
name: (() => {
|
|
12149
|
-
const first = (fields.data ?? []).find((f) => f.key === sortedKeys[0]);
|
|
12150
|
-
return first ? fieldName(first) : humanize(sortedKeys[0]);
|
|
12151
|
-
})()
|
|
12152
|
-
} : null,
|
|
12153
|
-
onChange: changeLayout,
|
|
12154
|
-
kept: Boolean(onPresentationChange)
|
|
12155
|
-
}
|
|
12156
|
-
)
|
|
12157
|
-
] }),
|
|
12158
|
-
...merged ? {
|
|
12159
|
-
overflow: {
|
|
12160
|
-
mode: phone ? "sheet" : toolbarFold2 >= 3 ? "menu" : "inline",
|
|
12161
|
-
pinned: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
12162
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(UndoRedoButtons, { undo }),
|
|
12163
|
-
Boolean(editable) && rights.write ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_design_system35.Button, { size: "sm", className: "shrink-0", disabled: added.busy, onClick: () => void addRowHere(), children: added.busy ? "Adding\u2026" : "New record" }) : null
|
|
12164
|
-
] }),
|
|
12165
|
-
...phone ? {
|
|
12166
|
-
sheetTop: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5 pb-1", "data-records-grid-sheet-top": "", "data-matrx-table-tools-keep-open": "", children: [
|
|
12167
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12168
|
-
"input",
|
|
12169
|
-
{
|
|
12170
|
-
type: "search",
|
|
12171
|
-
value: query.search,
|
|
12172
|
-
placeholder: "Search",
|
|
12173
|
-
"aria-label": "Search records",
|
|
12174
|
-
onChange: (event) => {
|
|
12175
|
-
const search = event.target.value;
|
|
12176
|
-
setQuery((held) => ({ ...held, search, page: 1 }));
|
|
12177
|
-
},
|
|
12178
|
-
className: "h-10 w-full min-w-0 rounded-md border border-input bg-background px-2 text-foreground placeholder:text-muted-foreground",
|
|
12179
|
-
style: { fontSize: "16px" }
|
|
12180
|
-
}
|
|
12181
|
-
),
|
|
12182
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-grid-sheet-sort": "", children: [
|
|
12183
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "shrink-0", children: "Sort by" }),
|
|
12184
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
12185
|
-
"select",
|
|
12186
|
-
{
|
|
12187
|
-
"aria-label": "Sort by",
|
|
12188
|
-
value: query.sort ? `${query.sort.id}:${query.sort.direction}` : "",
|
|
12189
|
-
onChange: (event) => {
|
|
12190
|
-
const [id, direction] = event.target.value.split(":");
|
|
12191
|
-
setQuery((held) => ({
|
|
12192
|
-
...held,
|
|
12193
|
-
page: 1,
|
|
12194
|
-
sort: id ? { id, direction: direction === "desc" ? "desc" : "asc" } : null
|
|
12195
|
-
}));
|
|
12196
|
-
},
|
|
12197
|
-
className: "h-10 min-w-0 flex-1 rounded-md border border-input bg-background px-2 text-foreground",
|
|
12198
|
-
style: { fontSize: "16px" },
|
|
12199
|
-
children: [
|
|
12200
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "No sort" }),
|
|
12201
|
-
ordered.flatMap((field2) => [
|
|
12202
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: `${field2.key}:asc`, children: `${fieldName(field2)} \u2191` }, `${field2.key}:asc`),
|
|
12203
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: `${field2.key}:desc`, children: `${fieldName(field2)} \u2193` }, `${field2.key}:desc`)
|
|
12204
|
-
])
|
|
12205
|
-
]
|
|
12206
|
-
}
|
|
12207
|
-
)
|
|
12208
|
-
] }),
|
|
12209
|
-
controls.sheetTools
|
|
12210
|
-
] }),
|
|
12211
|
-
...sheetExtras ? { sheetExtras } : {}
|
|
12212
|
-
} : {}
|
|
12213
|
-
}
|
|
12214
|
-
} : {}
|
|
12215
|
-
}
|
|
12332
|
+
toolbar: tableToolbar
|
|
12216
12333
|
},
|
|
12217
12334
|
`records-grid-${tableId}:${openingKey}`
|
|
12218
12335
|
)
|
|
@@ -12314,6 +12431,58 @@ function Grid({
|
|
|
12314
12431
|
}
|
|
12315
12432
|
) });
|
|
12316
12433
|
}
|
|
12434
|
+
var DETAIL_OFF = { enabled: false };
|
|
12435
|
+
var WINDOW_OFF = { enabled: false };
|
|
12436
|
+
var VIRTUALIZE_FILLED = { threshold: 40 };
|
|
12437
|
+
function menuFace(items) {
|
|
12438
|
+
return JSON.stringify(items, (_key, value) => typeof value === "function" ? "\u0192" : value);
|
|
12439
|
+
}
|
|
12440
|
+
function menuThroughLatest(items, at, latest) {
|
|
12441
|
+
return items.map((item) => {
|
|
12442
|
+
const path = `${at}/${item.id}`;
|
|
12443
|
+
return {
|
|
12444
|
+
...item,
|
|
12445
|
+
...item.onSelect ? { onSelect: () => latest.get(path)?.() } : {},
|
|
12446
|
+
...item.submenu ? { submenu: menuThroughLatest(item.submenu, path, latest) } : {}
|
|
12447
|
+
};
|
|
12448
|
+
});
|
|
12449
|
+
}
|
|
12450
|
+
function bindLatest(items, at, latest) {
|
|
12451
|
+
for (const item of items) {
|
|
12452
|
+
const path = `${at}/${item.id}`;
|
|
12453
|
+
if (item.onSelect) latest.set(path, item.onSelect);
|
|
12454
|
+
if (item.submenu) bindLatest(item.submenu, path, latest);
|
|
12455
|
+
}
|
|
12456
|
+
}
|
|
12457
|
+
function holdHeaderMenu(held, latest, key, items) {
|
|
12458
|
+
if (!items) return items;
|
|
12459
|
+
const at = `menu:${key}`;
|
|
12460
|
+
bindLatest(items, at, latest);
|
|
12461
|
+
const sig = menuFace(items);
|
|
12462
|
+
const was = held.get(at);
|
|
12463
|
+
if (was && was.sig === sig) return was.value;
|
|
12464
|
+
const value = menuThroughLatest(items, at, latest);
|
|
12465
|
+
held.set(at, { sig, value });
|
|
12466
|
+
return value;
|
|
12467
|
+
}
|
|
12468
|
+
function holdByContent(held, at, value) {
|
|
12469
|
+
if (value === void 0) return value;
|
|
12470
|
+
const sig = JSON.stringify(value);
|
|
12471
|
+
const was = held.get(at);
|
|
12472
|
+
if (was && was.sig === sig) return was.value;
|
|
12473
|
+
held.set(at, { sig, value });
|
|
12474
|
+
return value;
|
|
12475
|
+
}
|
|
12476
|
+
function sameColumns(a, b) {
|
|
12477
|
+
if (a === b) return true;
|
|
12478
|
+
if (a.length !== b.length) return false;
|
|
12479
|
+
return a.every((column, i) => {
|
|
12480
|
+
const other = b[i];
|
|
12481
|
+
if (column === other) return true;
|
|
12482
|
+
const keys = Object.keys(column);
|
|
12483
|
+
return keys.length === Object.keys(other).length && keys.every((key) => Object.is(column[key], other[key]));
|
|
12484
|
+
});
|
|
12485
|
+
}
|
|
12317
12486
|
function withHeaderMenus(columns, menuFor, filterFor) {
|
|
12318
12487
|
return columns.map((column) => {
|
|
12319
12488
|
const items = menuFor(String(column.id));
|