@ai-matrx/records-ui 0.95.3 → 0.95.10
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 +127 -0
- package/dist/index.cjs +7265 -6066
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -26
- package/dist/index.d.ts +149 -26
- package/dist/index.js +6894 -5685
- package/dist/index.js.map +1 -1
- package/dist/object-actions.cjs +17 -12
- package/dist/object-actions.cjs.map +1 -1
- package/dist/object-actions.d.cts +1 -1
- package/dist/object-actions.d.ts +1 -1
- package/dist/object-actions.js +19 -12
- package/dist/object-actions.js.map +1 -1
- package/dist/{objectActions-DozKWxks.d.cts → objectActions-CUR8KdPr.d.cts} +11 -2
- package/dist/{objectActions-DozKWxks.d.ts → objectActions-CUR8KdPr.d.ts} +11 -2
- package/dist/pickers.cjs +98 -34
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.d.cts +4 -0
- package/dist/pickers.d.ts +4 -0
- package/dist/pickers.js +98 -34
- package/dist/pickers.js.map +1 -1
- package/package.json +3 -3
package/dist/pickers.d.cts
CHANGED
|
@@ -10,6 +10,10 @@ interface TableAnywhere {
|
|
|
10
10
|
organization_name: string;
|
|
11
11
|
kind: string | null;
|
|
12
12
|
kept_by_the_app: boolean;
|
|
13
|
+
/** When the table last changed (the door's own column) — what tells two same-named tables apart. */
|
|
14
|
+
updated_at?: string | null;
|
|
15
|
+
/** Who made it, when the door says. */
|
|
16
|
+
created_by_name?: string | null;
|
|
13
17
|
}
|
|
14
18
|
interface TablesAnywhere {
|
|
15
19
|
loading: boolean;
|
package/dist/pickers.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ interface TableAnywhere {
|
|
|
10
10
|
organization_name: string;
|
|
11
11
|
kind: string | null;
|
|
12
12
|
kept_by_the_app: boolean;
|
|
13
|
+
/** When the table last changed (the door's own column) — what tells two same-named tables apart. */
|
|
14
|
+
updated_at?: string | null;
|
|
15
|
+
/** Who made it, when the door says. */
|
|
16
|
+
created_by_name?: string | null;
|
|
13
17
|
}
|
|
14
18
|
interface TablesAnywhere {
|
|
15
19
|
loading: boolean;
|
package/dist/pickers.js
CHANGED
|
@@ -5,6 +5,40 @@ import { useEffect as useEffect12, useMemo as useMemo7, useState as useState16 }
|
|
|
5
5
|
import { RecordsProvider as RecordsProvider2, useFields, useRecords, useTable } from "@ai-matrx/records/react";
|
|
6
6
|
import { Button as Button9, CreatablePicker } from "@ai-matrx/design-system";
|
|
7
7
|
|
|
8
|
+
// src/askOnce.ts
|
|
9
|
+
var asked = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
function askOnce(client, key, ask, freshMs) {
|
|
11
|
+
let held = asked.get(client);
|
|
12
|
+
if (!held) {
|
|
13
|
+
held = /* @__PURE__ */ new Map();
|
|
14
|
+
asked.set(client, held);
|
|
15
|
+
}
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
const kept = held.get(key);
|
|
18
|
+
if (kept && now - kept.at <= freshMs) return kept.answer;
|
|
19
|
+
const answer = ask().then(
|
|
20
|
+
(result) => {
|
|
21
|
+
if (!result.ok) held.delete(key);
|
|
22
|
+
return result;
|
|
23
|
+
},
|
|
24
|
+
(thrown) => {
|
|
25
|
+
held.delete(key);
|
|
26
|
+
throw thrown;
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
held.set(key, { at: now, answer });
|
|
30
|
+
return answer;
|
|
31
|
+
}
|
|
32
|
+
function forgetAsked(client, prefix) {
|
|
33
|
+
const held = asked.get(client);
|
|
34
|
+
if (!held) return;
|
|
35
|
+
for (const key of [...held.keys()]) if (key.startsWith(prefix)) held.delete(key);
|
|
36
|
+
}
|
|
37
|
+
var FIELD_OPTIONS_FRESH_MS = 2e3;
|
|
38
|
+
function fieldOptionsOnce(client, fieldId) {
|
|
39
|
+
return askOnce(client, `field_options:${fieldId}`, () => client.fieldOptions({ field_id: fieldId }), FIELD_OPTIONS_FRESH_MS);
|
|
40
|
+
}
|
|
41
|
+
|
|
8
42
|
// src/editors.tsx
|
|
9
43
|
import { useCallback as useCallback4, useEffect as useEffect10, useRef as useRef9, useState as useState14 } from "react";
|
|
10
44
|
import { allowsOtherValues, choiceValuesOf, coerceTypedAnswer, newerKindOf as newerKindOf4, decideTypedChoice, isTheChosen, optionKey as optionKey2, rankTypedMatches as rankTypedMatches3, readTypedTime as readTypedTime2, typedDateText as typedDateText2, typedTimeText as typedTimeText2 } from "@ai-matrx/records";
|
|
@@ -715,7 +749,12 @@ var REMEDY = {
|
|
|
715
749
|
unreachable: "Nothing was changed. Check your connection and open this again.",
|
|
716
750
|
needs_approval: "Nothing was changed yet. The people who can approve it have been asked.",
|
|
717
751
|
stale_read: "Open this again in a moment and it will be up to date.",
|
|
718
|
-
|
|
752
|
+
// 🚨 ONE REMEDY THAT IS TRUE OF EVERY DOOR (lane CHAIR-STORE-PERF, 2026-10-03). The store cancels the
|
|
753
|
+
// WHOLE statement at its clock, so a timed-out door wrote nothing — a create, a column add and a page
|
|
754
|
+
// read alike. "Try a smaller page" was written for the page read and was printed to a person adding
|
|
755
|
+
// one row to a three-row table (clone walk, 2026-10-03; the clock was a lock another lane's
|
|
756
|
+
// rehearsal held, not the row). A door whose page can shrink says so in its own hint, which wins here.
|
|
757
|
+
timed_out: "Nothing was changed. Try again in a moment.",
|
|
719
758
|
internal: "Nothing you did caused this. Try again, and tell us if it keeps happening."
|
|
720
759
|
};
|
|
721
760
|
var ENGINEER_TEXT = [
|
|
@@ -955,7 +994,7 @@ function tellApart(rows, nameOf) {
|
|
|
955
994
|
// src/RelationPicker.tsx
|
|
956
995
|
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
957
996
|
var NO_STORE_ON_THIS_SCREEN_REASON = "This question points at a record in somebody's own data, and a page answered without an account is never allowed to search that. Leave it blank \u2014 whoever receives your answer will fill it in.";
|
|
958
|
-
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true }) {
|
|
997
|
+
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true, onPicked }) {
|
|
959
998
|
const client = useOptionalRecordsClient();
|
|
960
999
|
const [rows, setRows] = useState3(null);
|
|
961
1000
|
const [words, setWords] = useState3({});
|
|
@@ -1047,8 +1086,10 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1047
1086
|
const full = field.multi && field.relation_max !== null && picked.length >= field.relation_max;
|
|
1048
1087
|
const toggle = (recordId) => {
|
|
1049
1088
|
if (!field.multi) {
|
|
1050
|
-
|
|
1089
|
+
const choosing = picked[0] !== recordId;
|
|
1090
|
+
onChange(choosing ? recordId : null);
|
|
1051
1091
|
setOpen(false);
|
|
1092
|
+
if (choosing && onPicked) setTimeout(onPicked, 0);
|
|
1052
1093
|
return;
|
|
1053
1094
|
}
|
|
1054
1095
|
const next = new Set(picked);
|
|
@@ -1324,31 +1365,31 @@ var FILES = /* @__PURE__ */ new WeakMap();
|
|
|
1324
1365
|
function signatureFilesOf(client, recordId) {
|
|
1325
1366
|
let held = SOURCES.get(client);
|
|
1326
1367
|
if (!held) SOURCES.set(client, held = /* @__PURE__ */ new Map());
|
|
1327
|
-
let
|
|
1328
|
-
if (!
|
|
1329
|
-
|
|
1368
|
+
let asked3 = held.get(recordId);
|
|
1369
|
+
if (!asked3) {
|
|
1370
|
+
asked3 = client.recordValuesVersioned({ record_id: recordId }).then((answered) => {
|
|
1330
1371
|
if (!answered.ok) return {};
|
|
1331
1372
|
return Object.fromEntries(answered.data.map((v) => [v.field_key, signatureFileOf(v.source)]));
|
|
1332
1373
|
});
|
|
1333
|
-
held.set(recordId,
|
|
1374
|
+
held.set(recordId, asked3);
|
|
1334
1375
|
}
|
|
1335
|
-
return
|
|
1376
|
+
return asked3;
|
|
1336
1377
|
}
|
|
1337
1378
|
function drawingOf(client, fileId) {
|
|
1338
1379
|
let held = FILES.get(client);
|
|
1339
1380
|
if (!held) FILES.set(client, held = /* @__PURE__ */ new Map());
|
|
1340
|
-
let
|
|
1341
|
-
if (!
|
|
1342
|
-
|
|
1381
|
+
let asked3 = held.get(fileId);
|
|
1382
|
+
if (!asked3) {
|
|
1383
|
+
asked3 = client.recordRead({ record_id: fileId }).then((answered) => {
|
|
1343
1384
|
if (!answered.ok) return { src: null, hostFileId: null };
|
|
1344
1385
|
const doc = answered.data.document ?? {};
|
|
1345
1386
|
const content = drawnSignatureOf(doc["content"]);
|
|
1346
1387
|
const hostFileId = typeof doc["file_id"] === "string" && doc["file_id"] !== "" ? doc["file_id"] : null;
|
|
1347
1388
|
return { src: content, hostFileId };
|
|
1348
1389
|
});
|
|
1349
|
-
held.set(fileId,
|
|
1390
|
+
held.set(fileId, asked3);
|
|
1350
1391
|
}
|
|
1351
|
-
return
|
|
1392
|
+
return asked3;
|
|
1352
1393
|
}
|
|
1353
1394
|
function DrawnImage({ src, inCell }) {
|
|
1354
1395
|
return /* @__PURE__ */ jsx6(
|
|
@@ -1595,7 +1636,7 @@ function RecordLabelProvider({ children }) {
|
|
|
1595
1636
|
const [archivedRefs, setArchivedRefs] = useState5({});
|
|
1596
1637
|
const [settled, setSettled] = useState5({});
|
|
1597
1638
|
const [waitingLines, setWaitingLines] = useState5({});
|
|
1598
|
-
const
|
|
1639
|
+
const asked3 = useRef3(/* @__PURE__ */ new Set());
|
|
1599
1640
|
const chased = useRef3(/* @__PURE__ */ new Set());
|
|
1600
1641
|
const timers = useRef3(/* @__PURE__ */ new Map());
|
|
1601
1642
|
const startWaitClock = useCallback((field, target) => {
|
|
@@ -1633,12 +1674,12 @@ function RecordLabelProvider({ children }) {
|
|
|
1633
1674
|
const want = useCallback(
|
|
1634
1675
|
(field) => {
|
|
1635
1676
|
if (!pointsAtRecords(field)) return;
|
|
1636
|
-
if (
|
|
1637
|
-
|
|
1677
|
+
if (asked3.current.has(field.id)) return;
|
|
1678
|
+
asked3.current.add(field.id);
|
|
1638
1679
|
startWaitClock(field, null);
|
|
1639
1680
|
void (async () => {
|
|
1640
1681
|
if (field.type === "list") {
|
|
1641
|
-
const result = await client
|
|
1682
|
+
const result = await fieldOptionsOnce(client, field.id);
|
|
1642
1683
|
if (result.ok) {
|
|
1643
1684
|
setNames((current) => {
|
|
1644
1685
|
const next = { ...current };
|
|
@@ -1722,7 +1763,7 @@ function RecordLabelProvider({ children }) {
|
|
|
1722
1763
|
const once = choiceKeyName(field.id, id);
|
|
1723
1764
|
if (settled[field.id] && !rereadFor.current.has(once)) {
|
|
1724
1765
|
rereadFor.current.add(once);
|
|
1725
|
-
|
|
1766
|
+
asked3.current.delete(field.id);
|
|
1726
1767
|
want(field);
|
|
1727
1768
|
}
|
|
1728
1769
|
return null;
|
|
@@ -2964,15 +3005,15 @@ function TypedDateHelper({ kind, text, onText, label }) {
|
|
|
2964
3005
|
// src/choiceNudge.ts
|
|
2965
3006
|
import { useEffect as useEffect9, useState as useState13 } from "react";
|
|
2966
3007
|
import { useOptionalRecordsClient as useOptionalRecordsClient6 } from "@ai-matrx/records/react";
|
|
2967
|
-
var
|
|
3008
|
+
var asked2 = /* @__PURE__ */ new WeakMap();
|
|
2968
3009
|
function choiceNudgeOf(client) {
|
|
2969
|
-
let answer =
|
|
3010
|
+
let answer = asked2.get(client);
|
|
2970
3011
|
if (!answer) {
|
|
2971
3012
|
answer = Promise.resolve().then(() => client.choiceNudge()).then(
|
|
2972
3013
|
(result) => result.ok ? result.data : "ask",
|
|
2973
3014
|
() => "ask"
|
|
2974
3015
|
);
|
|
2975
|
-
|
|
3016
|
+
asked2.set(client, answer);
|
|
2976
3017
|
}
|
|
2977
3018
|
return answer;
|
|
2978
3019
|
}
|
|
@@ -3120,7 +3161,7 @@ function TypedTextControl({ field, value, onChange, disabled, id, onCommit, onCa
|
|
|
3120
3161
|
useEffect10(() => {
|
|
3121
3162
|
if (!needsChoices || !client) return;
|
|
3122
3163
|
let cancelled = false;
|
|
3123
|
-
void client
|
|
3164
|
+
void fieldOptionsOnce(client, field.id).then((result) => {
|
|
3124
3165
|
if (!cancelled) setChoices(result.ok ? inTheStoresOrder(result.data ?? []) : []);
|
|
3125
3166
|
});
|
|
3126
3167
|
return () => {
|
|
@@ -3284,7 +3325,7 @@ function FieldControl({ field, value, onChange, disabled, id, autoOpen, onClose,
|
|
|
3284
3325
|
}
|
|
3285
3326
|
return /* @__PURE__ */ jsx16(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
3286
3327
|
case "relation":
|
|
3287
|
-
return /* @__PURE__ */ jsx16(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
3328
|
+
return /* @__PURE__ */ jsx16(RelationPicker, { field, value, onChange, disabled, id: inputId, onPicked: onCommit });
|
|
3288
3329
|
default:
|
|
3289
3330
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
3290
3331
|
return /* @__PURE__ */ jsx16(
|
|
@@ -3454,7 +3495,7 @@ function OptionControl({ field, value, onChange, disabled, id, autoOpen, onClose
|
|
|
3454
3495
|
useEffect10(() => {
|
|
3455
3496
|
if (!client) return;
|
|
3456
3497
|
let cancelled = false;
|
|
3457
|
-
void client
|
|
3498
|
+
void fieldOptionsOnce(client, field.id).then((result) => {
|
|
3458
3499
|
if (cancelled) return;
|
|
3459
3500
|
if (result.ok) setOptions(inTheStoresOrder(result.data ?? []));
|
|
3460
3501
|
else setRefusal(result.error);
|
|
@@ -3517,7 +3558,10 @@ function OptionControl({ field, value, onChange, disabled, id, autoOpen, onClose
|
|
|
3517
3558
|
initialSearch,
|
|
3518
3559
|
addsChoices,
|
|
3519
3560
|
allowOther: allowsOtherValues(field),
|
|
3520
|
-
onChoicesChanged: () =>
|
|
3561
|
+
onChoicesChanged: () => {
|
|
3562
|
+
if (client) forgetAsked(client, `field_options:${field.id}`);
|
|
3563
|
+
setReread((n) => n + 1);
|
|
3564
|
+
}
|
|
3521
3565
|
}
|
|
3522
3566
|
);
|
|
3523
3567
|
}
|
|
@@ -3943,20 +3987,40 @@ function TablePicker({ dataSource, value, onChange, disabled, tables }) {
|
|
|
3943
3987
|
const options = tablePickerEntries(
|
|
3944
3988
|
list.rows.map((t) => ({ ...t, id: t.table_id, name: t.table_name })),
|
|
3945
3989
|
{ keep: value }
|
|
3946
|
-
).entries.map(({ table: t }) =>
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3990
|
+
).entries.map(({ table: t }) => t);
|
|
3991
|
+
const sameName = /* @__PURE__ */ new Map();
|
|
3992
|
+
for (const t of options) {
|
|
3993
|
+
const key = `${t.organization_id}|${t.table_name.trim().toLowerCase()}`;
|
|
3994
|
+
sameName.set(key, (sameName.get(key) ?? 0) + 1);
|
|
3995
|
+
}
|
|
3996
|
+
const pickerOptions = options.map((t) => {
|
|
3997
|
+
const twin = (sameName.get(`${t.organization_id}|${t.table_name.trim().toLowerCase()}`) ?? 0) > 1;
|
|
3998
|
+
const row = t;
|
|
3999
|
+
const second = [
|
|
4000
|
+
row.created_by_name ? `by ${row.created_by_name}` : null,
|
|
4001
|
+
row.updated_at ? `changed ${new Date(row.updated_at).toLocaleDateString()}` : null
|
|
4002
|
+
].filter(Boolean).join(" \xB7 ");
|
|
4003
|
+
return {
|
|
4004
|
+
value: t.table_id,
|
|
4005
|
+
// The organization hint already tells two same-named tables apart across organizations.
|
|
4006
|
+
label: t.table_name,
|
|
4007
|
+
hint: t.organization_name,
|
|
4008
|
+
keywords: t.organization_name,
|
|
4009
|
+
...twin && second ? {
|
|
4010
|
+
render: /* @__PURE__ */ jsxs16("span", { className: "flex min-w-0 flex-col", children: [
|
|
4011
|
+
/* @__PURE__ */ jsx19("span", { className: "truncate", children: t.table_name }),
|
|
4012
|
+
/* @__PURE__ */ jsx19("span", { className: "truncate text-[11px] text-muted-foreground", children: second })
|
|
4013
|
+
] })
|
|
4014
|
+
} : {}
|
|
4015
|
+
};
|
|
4016
|
+
});
|
|
3953
4017
|
const missing = Boolean(value) && !list.loading && !list.error && !list.rows.some((t) => t.table_id === value);
|
|
3954
4018
|
return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1", children: [
|
|
3955
4019
|
/* @__PURE__ */ jsx19(
|
|
3956
4020
|
CreatablePicker,
|
|
3957
4021
|
{
|
|
3958
4022
|
value: value || null,
|
|
3959
|
-
options,
|
|
4023
|
+
options: pickerOptions,
|
|
3960
4024
|
onSelect: onChange,
|
|
3961
4025
|
placeholder: missing ? "Table not available" : "Choose a table",
|
|
3962
4026
|
searchPlaceholder: "Search tables",
|
|
@@ -4478,7 +4542,7 @@ function referencesOf(field, value, kind, labels) {
|
|
|
4478
4542
|
}));
|
|
4479
4543
|
}
|
|
4480
4544
|
var ARCHIVED_REFERENCE_WORD = "Archived";
|
|
4481
|
-
var ARCHIVED_REFERENCE_TIP = "In
|
|
4545
|
+
var ARCHIVED_REFERENCE_TIP = "In the Archived view; restore it to use it again";
|
|
4482
4546
|
function ReferenceChip({ reference, waiting }) {
|
|
4483
4547
|
const host = useRecordsUi();
|
|
4484
4548
|
const words = reference.label ?? (reference.token === "record" ? waiting ?? "Loading\u2026" : WITHHELD_ENTITY_LABEL);
|