@ai-matrx/records-ui 0.60.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/index.cjs +171 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -4
- package/dist/index.d.ts +106 -4
- package/dist/index.js +171 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.61.0
|
|
4
|
+
|
|
5
|
+
**A link to a board opens a board, and a cell that is still resolving says what
|
|
6
|
+
it is waiting for.** (lane FRONT-DOOR; VERIFIER-8 HIGH-2 and MEDIUM-1.)
|
|
7
|
+
|
|
8
|
+
* **`?view=` was read by nobody.**
|
|
9
|
+
`/data-v2/<table>?view=kanban` returned 200 and rendered the grid — 40 rows,
|
|
10
|
+
grid headers — and nothing on the screen said the view had been dropped. So a
|
|
11
|
+
bookmarked board was a grid and the link an office manager sent every morning
|
|
12
|
+
had never once opened the board. `TablePage` takes `activeView` (the raw
|
|
13
|
+
`?view=` string) and `onViewChanged`, and the five views — grid, kanban,
|
|
14
|
+
calendar, gallery, dashboards — all deep-link. The ADDRESS OUTRANKS the saved
|
|
15
|
+
view's layout until the person picks another, and then it FOLLOWS them: half a
|
|
16
|
+
deep link is a link that works when you arrive and lies when you copy it out of
|
|
17
|
+
the bar afterwards. `pageViewFromParam` also takes the words people actually
|
|
18
|
+
type (`board`, `cards`, `table`, `dashboard`), and a `?view=` this build has no
|
|
19
|
+
view for is SAID (`unknownViewLine`) rather than silently turned into a grid.
|
|
20
|
+
New: `PAGE_VIEWS`, `PAGE_VIEW_LABEL`, `pageViewFromParam`, `unknownViewLine`,
|
|
21
|
+
`openingView`, type `PageView`.
|
|
22
|
+
* **The relation resolver's per-cell fetch is batched, and it was latent.**
|
|
23
|
+
Measured on the real Rincon Plumbing Jobs grid on 2026-09-21: rows at 2,184 ms,
|
|
24
|
+
the CUSTOMER column resolved at 3,017 ms to real names, about 100 ms of
|
|
25
|
+
"Loading…" and ZERO `record_read` calls — so the 10-13 second column VERIFIER-8
|
|
26
|
+
recorded on 0.57 is already gone. What was still there was the shape behind it:
|
|
27
|
+
the chase for an id the bulk read did not cover fired per cell and committed its
|
|
28
|
+
own `setNames`, so forty uncovered cells cost forty round trips AND forty
|
|
29
|
+
re-renders of the whole grid. Ids now gather and the page asks ONCE A TICK, six
|
|
30
|
+
lanes at a time, with ONE commit for every name it learned.
|
|
31
|
+
* **A cell still resolving after one second says what it went to read.**
|
|
32
|
+
"Loading…" is honest for a moment and a lie with a spinner on it after that.
|
|
33
|
+
New: `useLabelWait`, `SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS`; `scalarText` and
|
|
34
|
+
`renderValue` take the sentence as a last argument (optional — every existing
|
|
35
|
+
call site is unchanged).
|
|
36
|
+
|
|
37
|
+
**Consumer action:** a host route that serves a table page should read `?view=`
|
|
38
|
+
and pass `activeView` + `onViewChanged` to `TablePage`, or its view links stay
|
|
39
|
+
dead. `matrx-frontend`'s `/data-v2/[tableId]` does this as of the same session.
|
|
40
|
+
|
|
3
41
|
## 0.59.0
|
|
4
42
|
|
|
5
43
|
**Three things the browser walks found by using the builders on real businesses.**
|
package/dist/index.cjs
CHANGED
|
@@ -110,6 +110,8 @@ __export(src_exports, {
|
|
|
110
110
|
NO_SHARE_REASON: () => NO_SHARE_REASON,
|
|
111
111
|
NO_UPLOAD_REASON: () => NO_UPLOAD_REASON,
|
|
112
112
|
NotifyRuleEditor: () => NotifyRuleEditor,
|
|
113
|
+
PAGE_VIEWS: () => PAGE_VIEWS,
|
|
114
|
+
PAGE_VIEW_LABEL: () => PAGE_VIEW_LABEL,
|
|
113
115
|
PARITY_LABEL: () => PARITY_LABEL,
|
|
114
116
|
PARITY_MADE_OF: () => PARITY_MADE_OF,
|
|
115
117
|
Peek: () => Peek,
|
|
@@ -133,6 +135,7 @@ __export(src_exports, {
|
|
|
133
135
|
RefusalNotice: () => RefusalNotice,
|
|
134
136
|
RelationPicker: () => RelationPicker,
|
|
135
137
|
SAVED_VIEWS_UNAVAILABLE: () => SAVED_VIEWS_UNAVAILABLE,
|
|
138
|
+
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS: () => SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
136
139
|
SERIES_COLORS: () => SERIES_COLORS,
|
|
137
140
|
SOMEBODY: () => SOMEBODY,
|
|
138
141
|
STORE_ANSWERS_THESE: () => STORE_ANSWERS_THESE,
|
|
@@ -203,6 +206,9 @@ __export(src_exports, {
|
|
|
203
206
|
machineIdentityIn: () => machineIdentityIn,
|
|
204
207
|
memberName: () => memberName,
|
|
205
208
|
nextInWords: () => nextInWords,
|
|
209
|
+
openingRail: () => openingRail,
|
|
210
|
+
openingView: () => openingView,
|
|
211
|
+
pageViewFromParam: () => pageViewFromParam,
|
|
206
212
|
parityTypesWithNoExplanation: () => parityTypesWithNoExplanation,
|
|
207
213
|
parseGridPresentation: () => parseGridPresentation,
|
|
208
214
|
personActor: () => personActor,
|
|
@@ -228,10 +234,12 @@ __export(src_exports, {
|
|
|
228
234
|
tableName: () => tableName,
|
|
229
235
|
tableRightsAt: () => tableRightsAt,
|
|
230
236
|
tokenFor: () => tokenFor,
|
|
237
|
+
unknownViewLine: () => unknownViewLine,
|
|
231
238
|
useCanShare: () => useCanShare,
|
|
232
239
|
useEmbedHandshake: () => useEmbedHandshake,
|
|
233
240
|
useEnrichCells: () => useEnrichCells,
|
|
234
241
|
useGridEditing: () => useGridEditing,
|
|
242
|
+
useLabelWait: () => useLabelWait,
|
|
235
243
|
useMeasuredWidth: () => useMeasuredWidth,
|
|
236
244
|
useMyChecklistSteps: () => useMyChecklistSteps,
|
|
237
245
|
useRecordLabels: () => useRecordLabels,
|
|
@@ -1399,8 +1407,13 @@ var LabelContext = (0, import_react11.createContext)({
|
|
|
1399
1407
|
// provides one, so this is only ever a bare unit test.
|
|
1400
1408
|
lookup: () => null,
|
|
1401
1409
|
want: () => {
|
|
1402
|
-
}
|
|
1410
|
+
},
|
|
1411
|
+
waiting: () => null
|
|
1403
1412
|
});
|
|
1413
|
+
var SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS = 1e3;
|
|
1414
|
+
function waitingLineFor(field, target) {
|
|
1415
|
+
return field.type === "list" ? "Reading this column\u2019s list of choices\u2026" : `Reading the ${target ?? "records"} this column points at\u2026`;
|
|
1416
|
+
}
|
|
1404
1417
|
function idsOf(value) {
|
|
1405
1418
|
if (Array.isArray(value)) return value.map(String).filter(isId);
|
|
1406
1419
|
if (typeof value === "string" && isId(value)) return [value];
|
|
@@ -1417,13 +1430,48 @@ function RecordLabelProvider({ children }) {
|
|
|
1417
1430
|
const client = (0, import_react12.useRecordsClient)();
|
|
1418
1431
|
const [names, setNames] = (0, import_react11.useState)({});
|
|
1419
1432
|
const [settled, setSettled] = (0, import_react11.useState)({});
|
|
1433
|
+
const [waitingLines, setWaitingLines] = (0, import_react11.useState)({});
|
|
1420
1434
|
const asked = (0, import_react11.useRef)(/* @__PURE__ */ new Set());
|
|
1421
1435
|
const chased = (0, import_react11.useRef)(/* @__PURE__ */ new Set());
|
|
1436
|
+
const timers = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
|
|
1437
|
+
const startWaitClock = (0, import_react11.useCallback)((field, target) => {
|
|
1438
|
+
if (timers.current.has(field.id)) return;
|
|
1439
|
+
timers.current.set(
|
|
1440
|
+
field.id,
|
|
1441
|
+
setTimeout(() => {
|
|
1442
|
+
timers.current.delete(field.id);
|
|
1443
|
+
setWaitingLines(
|
|
1444
|
+
(current) => current[field.id] ? current : { ...current, [field.id]: waitingLineFor(field, target) }
|
|
1445
|
+
);
|
|
1446
|
+
}, SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS)
|
|
1447
|
+
);
|
|
1448
|
+
}, []);
|
|
1449
|
+
const stopWaitClock = (0, import_react11.useCallback)((fieldId) => {
|
|
1450
|
+
const timer = timers.current.get(fieldId);
|
|
1451
|
+
if (timer) {
|
|
1452
|
+
clearTimeout(timer);
|
|
1453
|
+
timers.current.delete(fieldId);
|
|
1454
|
+
}
|
|
1455
|
+
setWaitingLines((current) => {
|
|
1456
|
+
if (!current[fieldId]) return current;
|
|
1457
|
+
const next = { ...current };
|
|
1458
|
+
delete next[fieldId];
|
|
1459
|
+
return next;
|
|
1460
|
+
});
|
|
1461
|
+
}, []);
|
|
1462
|
+
(0, import_react11.useEffect)(
|
|
1463
|
+
() => () => {
|
|
1464
|
+
for (const timer of timers.current.values()) clearTimeout(timer);
|
|
1465
|
+
timers.current.clear();
|
|
1466
|
+
},
|
|
1467
|
+
[]
|
|
1468
|
+
);
|
|
1422
1469
|
const want = (0, import_react11.useCallback)(
|
|
1423
1470
|
(field) => {
|
|
1424
1471
|
if (!pointsAtRecords(field)) return;
|
|
1425
1472
|
if (asked.current.has(field.id)) return;
|
|
1426
1473
|
asked.current.add(field.id);
|
|
1474
|
+
startWaitClock(field, null);
|
|
1427
1475
|
void (async () => {
|
|
1428
1476
|
if (field.type === "list") {
|
|
1429
1477
|
const result = await client.fieldOptions({ field_id: field.id });
|
|
@@ -1437,11 +1485,13 @@ function RecordLabelProvider({ children }) {
|
|
|
1437
1485
|
});
|
|
1438
1486
|
}
|
|
1439
1487
|
setSettled((current) => ({ ...current, [field.id]: true }));
|
|
1488
|
+
stopWaitClock(field.id);
|
|
1440
1489
|
return;
|
|
1441
1490
|
}
|
|
1442
1491
|
const target = field.relation_target;
|
|
1443
1492
|
if (!target) {
|
|
1444
1493
|
setSettled((current) => ({ ...current, [field.id]: true }));
|
|
1494
|
+
stopWaitClock(field.id);
|
|
1445
1495
|
return;
|
|
1446
1496
|
}
|
|
1447
1497
|
const [rows, tables] = await Promise.all([
|
|
@@ -1459,24 +1509,54 @@ function RecordLabelProvider({ children }) {
|
|
|
1459
1509
|
});
|
|
1460
1510
|
}
|
|
1461
1511
|
setSettled((current) => ({ ...current, [field.id]: true }));
|
|
1512
|
+
stopWaitClock(field.id);
|
|
1462
1513
|
})();
|
|
1463
1514
|
},
|
|
1464
|
-
[client]
|
|
1515
|
+
[client, startWaitClock, stopWaitClock]
|
|
1465
1516
|
);
|
|
1517
|
+
const queued = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
|
|
1518
|
+
const flushing = (0, import_react11.useRef)(null);
|
|
1519
|
+
const flush = (0, import_react11.useCallback)(() => {
|
|
1520
|
+
flushing.current = null;
|
|
1521
|
+
const batch = [...queued.current.entries()];
|
|
1522
|
+
queued.current.clear();
|
|
1523
|
+
if (batch.length === 0) return;
|
|
1524
|
+
void (async () => {
|
|
1525
|
+
const learned = {};
|
|
1526
|
+
const LANES = 6;
|
|
1527
|
+
let at = 0;
|
|
1528
|
+
await Promise.all(
|
|
1529
|
+
Array.from({ length: Math.min(LANES, batch.length) }, async () => {
|
|
1530
|
+
for (; ; ) {
|
|
1531
|
+
const next = batch[at];
|
|
1532
|
+
at += 1;
|
|
1533
|
+
if (!next) return;
|
|
1534
|
+
const [id] = next;
|
|
1535
|
+
const result = await client.recordRead({ record_id: id });
|
|
1536
|
+
learned[id] = result.ok ? recordName(result.data.document, null, "Untitled") : WITHHELD_RECORD_LABEL;
|
|
1537
|
+
}
|
|
1538
|
+
})
|
|
1539
|
+
);
|
|
1540
|
+
setNames((current) => ({ ...current, ...learned }));
|
|
1541
|
+
for (const [, field] of batch) stopWaitClock(field.id);
|
|
1542
|
+
})();
|
|
1543
|
+
}, [client, stopWaitClock]);
|
|
1466
1544
|
const chase = (0, import_react11.useCallback)(
|
|
1467
1545
|
(field, id) => {
|
|
1468
1546
|
const key = `${field.id}:${id}`;
|
|
1469
1547
|
if (chased.current.has(key)) return;
|
|
1470
1548
|
chased.current.add(key);
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
...current,
|
|
1475
|
-
[id]: result.ok ? recordName(result.data.document, null, "Untitled") : WITHHELD_RECORD_LABEL
|
|
1476
|
-
}));
|
|
1477
|
-
})();
|
|
1549
|
+
queued.current.set(id, field);
|
|
1550
|
+
startWaitClock(field, null);
|
|
1551
|
+
if (flushing.current === null) flushing.current = setTimeout(flush, 0);
|
|
1478
1552
|
},
|
|
1479
|
-
[
|
|
1553
|
+
[flush, startWaitClock]
|
|
1554
|
+
);
|
|
1555
|
+
(0, import_react11.useEffect)(
|
|
1556
|
+
() => () => {
|
|
1557
|
+
if (flushing.current !== null) clearTimeout(flushing.current);
|
|
1558
|
+
},
|
|
1559
|
+
[]
|
|
1480
1560
|
);
|
|
1481
1561
|
const lookup = (0, import_react11.useCallback)(
|
|
1482
1562
|
(field, id) => {
|
|
@@ -1487,7 +1567,8 @@ function RecordLabelProvider({ children }) {
|
|
|
1487
1567
|
},
|
|
1488
1568
|
[names, settled, chase]
|
|
1489
1569
|
);
|
|
1490
|
-
const
|
|
1570
|
+
const waiting = (0, import_react11.useCallback)((field) => waitingLines[field.id] ?? null, [waitingLines]);
|
|
1571
|
+
const value = (0, import_react11.useMemo)(() => ({ lookup, want, waiting }), [lookup, want, waiting]);
|
|
1491
1572
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LabelContext.Provider, { value, children });
|
|
1492
1573
|
}
|
|
1493
1574
|
function useRecordLabels(fields) {
|
|
@@ -1497,6 +1578,10 @@ function useRecordLabels(fields) {
|
|
|
1497
1578
|
}, [fields, want]);
|
|
1498
1579
|
return lookup;
|
|
1499
1580
|
}
|
|
1581
|
+
function useLabelWait(field) {
|
|
1582
|
+
const { waiting } = (0, import_react11.useContext)(LabelContext);
|
|
1583
|
+
return field ? waiting(field) : null;
|
|
1584
|
+
}
|
|
1500
1585
|
|
|
1501
1586
|
// src/values.tsx
|
|
1502
1587
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -1510,7 +1595,7 @@ var ABSENCE_WORDS = {
|
|
|
1510
1595
|
refused: "refused",
|
|
1511
1596
|
conflicting: "conflicting"
|
|
1512
1597
|
};
|
|
1513
|
-
function renderValue(field, value, document2, labels) {
|
|
1598
|
+
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
1514
1599
|
const envelope = envelopeFor(document2, field.key);
|
|
1515
1600
|
if (envelope?.absent) {
|
|
1516
1601
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs italic text-muted-foreground", title: `VAL-2: ${envelope.absent}`, children: ABSENCE_WORDS[String(envelope.absent)] ?? String(envelope.absent) });
|
|
@@ -1531,13 +1616,13 @@ function renderValue(field, value, document2, labels) {
|
|
|
1531
1616
|
if (typeof value === "boolean") {
|
|
1532
1617
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: kind === "checkbox" ? "text-sm" : "", title: value ? "Yes" : "No", children: value ? "\u2713" : "\u2014" });
|
|
1533
1618
|
}
|
|
1534
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: (0, import_design_system5.cn)("truncate", kind === "number" || kind === "currency" || kind === "percent" ? "tabular-nums" : ""), children: scalarText(field, value, labels) });
|
|
1619
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: (0, import_design_system5.cn)("truncate", kind === "number" || kind === "currency" || kind === "percent" ? "tabular-nums" : ""), children: scalarText(field, value, labels, stillWaitingFor) });
|
|
1535
1620
|
}
|
|
1536
|
-
function scalarText(field, value, labels) {
|
|
1621
|
+
function scalarText(field, value, labels, stillWaitingFor) {
|
|
1537
1622
|
if (value === null || value === void 0) return "\u2014";
|
|
1538
1623
|
const kind = editorKindFor(field);
|
|
1539
1624
|
if (isId(value) && (field.type === "list" || field.type === "relation")) {
|
|
1540
|
-
return labels?.(field, String(value)) ?? "Loading\u2026";
|
|
1625
|
+
return labels?.(field, String(value)) ?? stillWaitingFor ?? "Loading\u2026";
|
|
1541
1626
|
}
|
|
1542
1627
|
if (typeof value === "number") {
|
|
1543
1628
|
if (kind === "percent") return `${value}%`;
|
|
@@ -1569,9 +1654,10 @@ function RecordValue({
|
|
|
1569
1654
|
format
|
|
1570
1655
|
}) {
|
|
1571
1656
|
const labels = useRecordLabels(ONE_FIELD_ARRAY_CACHE(field));
|
|
1657
|
+
const stillWaitingFor = useLabelWait(field);
|
|
1572
1658
|
const formatted = format ? (0, import_data_table.formatColumnValue)({ format }, value) : null;
|
|
1573
1659
|
if (formatted) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_data_table.FormattedCell, { result: formatted });
|
|
1574
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: renderValue(field, value, document2, labels) });
|
|
1660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: renderValue(field, value, document2, labels, stillWaitingFor) });
|
|
1575
1661
|
}
|
|
1576
1662
|
var ONE_FIELD_ARRAYS = /* @__PURE__ */ new WeakMap();
|
|
1577
1663
|
function ONE_FIELD_ARRAY_CACHE(field) {
|
|
@@ -6380,6 +6466,37 @@ var LAYOUT_FIELD_KIND = {
|
|
|
6380
6466
|
calendar: "date",
|
|
6381
6467
|
gallery: null
|
|
6382
6468
|
};
|
|
6469
|
+
var PAGE_VIEWS = [...VIEW_LAYOUTS, "dashboards"];
|
|
6470
|
+
var PAGE_VIEW_LABEL = {
|
|
6471
|
+
...LAYOUT_LABEL,
|
|
6472
|
+
dashboards: "Dashboards"
|
|
6473
|
+
};
|
|
6474
|
+
var PAGE_VIEW_SPELLINGS = {
|
|
6475
|
+
grid: "grid",
|
|
6476
|
+
table: "grid",
|
|
6477
|
+
rows: "grid",
|
|
6478
|
+
list: "grid",
|
|
6479
|
+
kanban: "kanban",
|
|
6480
|
+
board: "kanban",
|
|
6481
|
+
pipeline: "kanban",
|
|
6482
|
+
calendar: "calendar",
|
|
6483
|
+
month: "calendar",
|
|
6484
|
+
schedule: "calendar",
|
|
6485
|
+
gallery: "gallery",
|
|
6486
|
+
cards: "gallery",
|
|
6487
|
+
dashboards: "dashboards",
|
|
6488
|
+
dashboard: "dashboards",
|
|
6489
|
+
charts: "dashboards"
|
|
6490
|
+
};
|
|
6491
|
+
function pageViewFromParam(raw) {
|
|
6492
|
+
if (typeof raw !== "string") return null;
|
|
6493
|
+
const token = raw.trim().toLowerCase();
|
|
6494
|
+
if (token === "") return null;
|
|
6495
|
+
return PAGE_VIEW_SPELLINGS[token] ?? null;
|
|
6496
|
+
}
|
|
6497
|
+
function unknownViewLine(raw) {
|
|
6498
|
+
return `The link asked for a view called \u201C${raw}\u201D, which this table has no view for, so you are looking at ${PAGE_VIEW_LABEL.grid}. The views this table has are ${PAGE_VIEWS.map((v) => PAGE_VIEW_LABEL[v]).join(", ")} \u2014 pick one above and the address follows it.`;
|
|
6499
|
+
}
|
|
6383
6500
|
var LAYOUT_NO_FIELD = {
|
|
6384
6501
|
grid: "",
|
|
6385
6502
|
kanban: "This table has no choice field yet, so there is nothing to make columns from. Add a field that holds one of a set of choices \u2014 Add field, then Select \u2014 and it appears here.",
|
|
@@ -14517,6 +14634,19 @@ function surfaceChosen(current, asking) {
|
|
|
14517
14634
|
function openingRail(activeRecordId) {
|
|
14518
14635
|
return activeRecordId ? { rail: "record", record: activeRecordId } : { rail: "none", record: null };
|
|
14519
14636
|
}
|
|
14637
|
+
function openingView(activeView, activeDashboardId) {
|
|
14638
|
+
const named = pageViewFromParam(activeView);
|
|
14639
|
+
if (named === null) {
|
|
14640
|
+
const asked = typeof activeView === "string" ? activeView.trim() : "";
|
|
14641
|
+
return {
|
|
14642
|
+
main: activeDashboardId ? "dashboards" : "records",
|
|
14643
|
+
layout: null,
|
|
14644
|
+
unknown: asked === "" ? null : asked
|
|
14645
|
+
};
|
|
14646
|
+
}
|
|
14647
|
+
if (named === "dashboards") return { main: "dashboards", layout: null, unknown: null };
|
|
14648
|
+
return { main: "records", layout: named, unknown: null };
|
|
14649
|
+
}
|
|
14520
14650
|
function TablePage({
|
|
14521
14651
|
tableId,
|
|
14522
14652
|
pageSize = 100,
|
|
@@ -14525,6 +14655,8 @@ function TablePage({
|
|
|
14525
14655
|
leaveLabel = "Back to your tables",
|
|
14526
14656
|
activeDashboardId,
|
|
14527
14657
|
activeRecordId,
|
|
14658
|
+
activeView,
|
|
14659
|
+
onViewChanged,
|
|
14528
14660
|
className
|
|
14529
14661
|
}) {
|
|
14530
14662
|
const client = (0, import_react110.useRecordsClient)();
|
|
@@ -14533,22 +14665,37 @@ function TablePage({
|
|
|
14533
14665
|
const organizationId = (0, import_react110.useRecordsClient)().config.organizationId;
|
|
14534
14666
|
const [view, setView] = (0, import_react109.useState)(null);
|
|
14535
14667
|
const opening = openingRail(activeRecordId);
|
|
14668
|
+
const opened = openingView(activeView, activeDashboardId);
|
|
14536
14669
|
const [asking, setAsking] = (0, import_react109.useState)(null);
|
|
14670
|
+
const [layoutFromLink, setLayoutFromLink] = (0, import_react109.useState)(opened.layout);
|
|
14537
14671
|
const [surface, setSurface] = (0, import_react109.useState)({
|
|
14538
|
-
main:
|
|
14672
|
+
main: opened.main,
|
|
14539
14673
|
rail: opening.rail
|
|
14540
14674
|
});
|
|
14541
14675
|
const { main, rail } = surface;
|
|
14542
14676
|
const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
|
|
14543
14677
|
const [openRecord, setOpenRecord] = (0, import_react109.useState)(opening.record);
|
|
14544
14678
|
const viewVersion = useRecordVersion(view?.id ?? null);
|
|
14545
|
-
const
|
|
14679
|
+
const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
|
|
14680
|
+
const press = (pressed) => {
|
|
14681
|
+
const next = chooseSurface(surface, pressed);
|
|
14682
|
+
setSurface(next);
|
|
14683
|
+
if (next.main !== surface.main) {
|
|
14684
|
+
onViewChanged?.(next.main === "dashboards" ? "dashboards" : shownLayout);
|
|
14685
|
+
}
|
|
14686
|
+
};
|
|
14546
14687
|
const show = (next) => press({ rail: next });
|
|
14547
14688
|
(0, import_react109.useEffect)(() => {
|
|
14548
14689
|
if (!activeRecordId) return;
|
|
14549
14690
|
setOpenRecord(activeRecordId);
|
|
14550
14691
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
14551
14692
|
}, [activeRecordId]);
|
|
14693
|
+
(0, import_react109.useEffect)(() => {
|
|
14694
|
+
const named = pageViewFromParam(activeView);
|
|
14695
|
+
if (named === null) return;
|
|
14696
|
+
setLayoutFromLink(named === "dashboards" ? null : named);
|
|
14697
|
+
setSurface((now) => ({ ...now, main: named === "dashboards" ? "dashboards" : "records" }));
|
|
14698
|
+
}, [activeView]);
|
|
14552
14699
|
const patchView = (0, import_react109.useCallback)(
|
|
14553
14700
|
async (patch) => {
|
|
14554
14701
|
const current = view;
|
|
@@ -14702,11 +14849,16 @@ function TablePage({
|
|
|
14702
14849
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(ExportMenu, { tableId })
|
|
14703
14850
|
] }),
|
|
14704
14851
|
main === "dashboards" ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
14852
|
+
opened.unknown ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: unknownViewLine(opened.unknown) }) : null,
|
|
14705
14853
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14706
14854
|
ViewSwitcher,
|
|
14707
14855
|
{
|
|
14708
|
-
view: view ?? defaultView(tableId),
|
|
14856
|
+
view: { ...view ?? defaultView(tableId), layout: shownLayout },
|
|
14709
14857
|
pageSize,
|
|
14858
|
+
onLayoutChange: (layout) => {
|
|
14859
|
+
setLayoutFromLink(layout);
|
|
14860
|
+
onViewChanged?.(layout);
|
|
14861
|
+
},
|
|
14710
14862
|
...view ? { onViewChange: (patch) => void patchView(patch) } : {},
|
|
14711
14863
|
...rights.write ? { onNewRecordForm: () => setRail("new-record") } : {},
|
|
14712
14864
|
onOpenRecord: (recordId) => {
|