@ai-matrx/records-ui 0.93.30 → 0.93.37
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 +42 -0
- package/dist/index.cjs +185 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +104 -37
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -597,7 +597,7 @@ import {
|
|
|
597
597
|
|
|
598
598
|
// src/editors.tsx
|
|
599
599
|
import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef5, useState as useState9 } from "react";
|
|
600
|
-
import { allowsOtherValues, choiceValuesOf, coerceTypedAnswer, decideTypedChoice, isTheChosen, optionKey } from "@ai-matrx/records";
|
|
600
|
+
import { allowsOtherValues, choiceValuesOf, coerceTypedAnswer, decideTypedChoice, isTheChosen, optionKey as optionKey2 } from "@ai-matrx/records";
|
|
601
601
|
import { useOptionalRecordsClient as useOptionalRecordsClient5 } from "@ai-matrx/records/react";
|
|
602
602
|
import {
|
|
603
603
|
BasicInput as BasicInput5,
|
|
@@ -1325,6 +1325,7 @@ import { Badge, Button as Button5 } from "@ai-matrx/design-system";
|
|
|
1325
1325
|
// src/labels.tsx
|
|
1326
1326
|
import { createContext as createContext2, useCallback, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo4, useRef, useState as useState4 } from "react";
|
|
1327
1327
|
import { useRecordsClient } from "@ai-matrx/records/react";
|
|
1328
|
+
import { optionKey } from "@ai-matrx/records";
|
|
1328
1329
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1329
1330
|
var LabelContext = createContext2({
|
|
1330
1331
|
// With no provider the resolver is ABSENT, not silently empty: a caller gets
|
|
@@ -1345,6 +1346,9 @@ function idsOf(value) {
|
|
|
1345
1346
|
return [];
|
|
1346
1347
|
}
|
|
1347
1348
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1349
|
+
function choiceKeyName(fieldId, key) {
|
|
1350
|
+
return `${fieldId}\0key\0${key}`;
|
|
1351
|
+
}
|
|
1348
1352
|
function isId(value) {
|
|
1349
1353
|
return typeof value === "string" && UUID.test(value);
|
|
1350
1354
|
}
|
|
@@ -1411,7 +1415,10 @@ function RecordLabelProvider({ children }) {
|
|
|
1411
1415
|
setNames((current) => {
|
|
1412
1416
|
const next = { ...current };
|
|
1413
1417
|
for (const option of result.data ?? []) {
|
|
1414
|
-
|
|
1418
|
+
const words2 = recordName(option.data, null, "Unnamed choice");
|
|
1419
|
+
next[option.id] = words2;
|
|
1420
|
+
const key = optionKey(option);
|
|
1421
|
+
if (key) next[choiceKeyName(field2.id, key)] = words2;
|
|
1415
1422
|
}
|
|
1416
1423
|
return next;
|
|
1417
1424
|
});
|
|
@@ -1475,14 +1482,26 @@ function RecordLabelProvider({ children }) {
|
|
|
1475
1482
|
},
|
|
1476
1483
|
[]
|
|
1477
1484
|
);
|
|
1485
|
+
const rereadFor = useRef(/* @__PURE__ */ new Set());
|
|
1478
1486
|
const lookup = useCallback(
|
|
1479
1487
|
(field2, id) => {
|
|
1488
|
+
if (field2.type === "list" && !isId(id)) {
|
|
1489
|
+
const named = names[choiceKeyName(field2.id, id)];
|
|
1490
|
+
if (named !== void 0) return named;
|
|
1491
|
+
const once = choiceKeyName(field2.id, id);
|
|
1492
|
+
if (settled[field2.id] && !rereadFor.current.has(once)) {
|
|
1493
|
+
rereadFor.current.add(once);
|
|
1494
|
+
asked2.current.delete(field2.id);
|
|
1495
|
+
want(field2);
|
|
1496
|
+
}
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1480
1499
|
const known = names[id];
|
|
1481
1500
|
if (known !== void 0) return known;
|
|
1482
1501
|
if (settled[field2.id]) chase(field2, id);
|
|
1483
1502
|
return null;
|
|
1484
1503
|
},
|
|
1485
|
-
[names, settled, chase]
|
|
1504
|
+
[names, settled, chase, want]
|
|
1486
1505
|
);
|
|
1487
1506
|
const waiting = useCallback((field2) => waitingLines[field2.id] ?? null, [waitingLines]);
|
|
1488
1507
|
const value = useMemo4(() => ({ lookup, want, waiting }), [lookup, want, waiting]);
|
|
@@ -2564,7 +2583,7 @@ function TypedTextControl({ field: field2, value, onChange, disabled, id, onComm
|
|
|
2564
2583
|
" ",
|
|
2565
2584
|
needsChoices ? String(
|
|
2566
2585
|
(Array.isArray(withoutAddition(judged.value).value) ? withoutAddition(judged.value).value : [withoutAddition(judged.value).value]).map((k) => {
|
|
2567
|
-
const option = choices?.find((o) =>
|
|
2586
|
+
const option = choices?.find((o) => optionKey2(o) === k);
|
|
2568
2587
|
return option ? recordName(option.data, null, String(k)) : String(k);
|
|
2569
2588
|
}).join(", ")
|
|
2570
2589
|
) : (() => {
|
|
@@ -2836,18 +2855,18 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
2836
2855
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
2837
2856
|
if (field2.multi) {
|
|
2838
2857
|
const chosen = options.filter((option) => choiceValuesOf(value).some((v) => isTheChosen(option, v)));
|
|
2839
|
-
const picked = new Set(chosen.map((option) =>
|
|
2858
|
+
const picked = new Set(chosen.map((option) => optionKey2(option)));
|
|
2840
2859
|
const toggles = /* @__PURE__ */ jsx12("div", { id: autoOpen ? void 0 : id, className: "flex flex-wrap gap-1", children: options.map((option) => /* @__PURE__ */ jsx12(
|
|
2841
2860
|
Button6,
|
|
2842
2861
|
{
|
|
2843
2862
|
type: "button",
|
|
2844
2863
|
size: "sm",
|
|
2845
|
-
variant: picked.has(
|
|
2864
|
+
variant: picked.has(optionKey2(option)) ? "default" : "outline",
|
|
2846
2865
|
disabled,
|
|
2847
2866
|
onClick: () => {
|
|
2848
2867
|
const next = new Set(picked);
|
|
2849
|
-
if (next.has(
|
|
2850
|
-
else next.add(
|
|
2868
|
+
if (next.has(optionKey2(option))) next.delete(optionKey2(option));
|
|
2869
|
+
else next.add(optionKey2(option));
|
|
2851
2870
|
onChange(next.size === 0 ? null : [...next]);
|
|
2852
2871
|
},
|
|
2853
2872
|
children: titleOf(option)
|
|
@@ -2873,7 +2892,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
2873
2892
|
return /* @__PURE__ */ jsxs10(
|
|
2874
2893
|
Select2,
|
|
2875
2894
|
{
|
|
2876
|
-
value: current ?
|
|
2895
|
+
value: current ? optionKey2(current) : "",
|
|
2877
2896
|
disabled: disabled ?? false,
|
|
2878
2897
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
2879
2898
|
...autoOpen ? {
|
|
@@ -2885,7 +2904,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
2885
2904
|
} : {},
|
|
2886
2905
|
children: [
|
|
2887
2906
|
/* @__PURE__ */ jsx12(SelectTrigger2, { id, size: "sm", children: /* @__PURE__ */ jsx12(SelectValue2, { placeholder: "Choose" }) }),
|
|
2888
|
-
/* @__PURE__ */ jsx12(SelectContent2, { children: options.map((option) => /* @__PURE__ */ jsx12(SelectItem2, { value:
|
|
2907
|
+
/* @__PURE__ */ jsx12(SelectContent2, { children: options.map((option) => /* @__PURE__ */ jsx12(SelectItem2, { value: optionKey2(option), children: titleOf(option) }, option.id)) })
|
|
2889
2908
|
]
|
|
2890
2909
|
}
|
|
2891
2910
|
);
|
|
@@ -2933,7 +2952,7 @@ function OpenChoicePicker({
|
|
|
2933
2952
|
if (field2.multi) {
|
|
2934
2953
|
const existing = current.map((v) => {
|
|
2935
2954
|
const option = options.find((o) => isTheChosen(o, v));
|
|
2936
|
-
return option ?
|
|
2955
|
+
return option ? optionKey2(option) : String(v);
|
|
2937
2956
|
});
|
|
2938
2957
|
const has = existing.some((v) => v.toLowerCase() === word.toLowerCase());
|
|
2939
2958
|
const list = has ? existing.filter((v) => v.toLowerCase() !== word.toLowerCase()) : [...existing, word];
|
|
@@ -3006,7 +3025,7 @@ function OpenChoicePicker({
|
|
|
3006
3025
|
})),
|
|
3007
3026
|
onPick: (optionId) => {
|
|
3008
3027
|
const option = options.find((o) => o.id === optionId);
|
|
3009
|
-
if (option) choose(
|
|
3028
|
+
if (option) choose(optionKey2(option));
|
|
3010
3029
|
},
|
|
3011
3030
|
empty: q === "" ? /* @__PURE__ */ jsx12("p", { className: "p-2 text-xs text-muted-foreground", children: "No choices yet \u2014 type one to add it." }) : null,
|
|
3012
3031
|
add,
|
|
@@ -3933,6 +3952,10 @@ function scalarText(field2, value, labels, stillWaitingFor) {
|
|
|
3933
3952
|
if (isId(value) && (field2.type === "list" || field2.type === "relation")) {
|
|
3934
3953
|
return labels?.(field2, String(value)) ?? stillWaitingFor ?? "Loading\u2026";
|
|
3935
3954
|
}
|
|
3955
|
+
if (field2.type === "list" && typeof value === "string" && labels) {
|
|
3956
|
+
const words2 = labels(field2, value);
|
|
3957
|
+
if (words2) return words2;
|
|
3958
|
+
}
|
|
3936
3959
|
const shownAs = shownAsOf(field2);
|
|
3937
3960
|
if (shownAs) {
|
|
3938
3961
|
const said = formatFieldValue3(value, shownAs);
|
|
@@ -4533,6 +4556,13 @@ function GridCell({
|
|
|
4533
4556
|
onAskWhoChanged
|
|
4534
4557
|
}) {
|
|
4535
4558
|
const ui = useRecordsUi();
|
|
4559
|
+
const saidAt = useRef8(0);
|
|
4560
|
+
const sayWhyNot = () => {
|
|
4561
|
+
if (Date.now() - saidAt.current < 4e3) return;
|
|
4562
|
+
saidAt.current = Date.now();
|
|
4563
|
+
const why = fieldIsEditable(field2) ? `${fieldName(field2)} \u2014 ${whyNot ?? "you cannot change this record"}` : `${fieldName(field2)} is worked out by the system, so it cannot be typed in`;
|
|
4564
|
+
ui.notify?.error(why);
|
|
4565
|
+
};
|
|
4536
4566
|
const open = Boolean(
|
|
4537
4567
|
editing && editing.editing?.rowId === row.id && editing.editing.key === field2.key
|
|
4538
4568
|
);
|
|
@@ -4651,6 +4681,7 @@ function GridCell({
|
|
|
4651
4681
|
event.preventDefault();
|
|
4652
4682
|
event.stopPropagation();
|
|
4653
4683
|
if (editable) editing?.begin({ rowId: row.id, key: field2.key });
|
|
4684
|
+
else sayWhyNot();
|
|
4654
4685
|
},
|
|
4655
4686
|
onKeyDown: (event) => {
|
|
4656
4687
|
if (editable && (event.key === "Enter" || event.key === "F2")) {
|
|
@@ -4666,7 +4697,9 @@ function GridCell({
|
|
|
4666
4697
|
if (editable && isTypedCharacter(event) && typingOpens(field2) !== "none") {
|
|
4667
4698
|
event.preventDefault();
|
|
4668
4699
|
editing?.begin({ rowId: row.id, key: field2.key }, event.key);
|
|
4700
|
+
return;
|
|
4669
4701
|
}
|
|
4702
|
+
if (!editable && (event.key === "Enter" || event.key === "F2" || isTypedCharacter(event))) sayWhyNot();
|
|
4670
4703
|
},
|
|
4671
4704
|
tabIndex: editable || onAskWhoChanged ? 0 : void 0,
|
|
4672
4705
|
children: /* @__PURE__ */ jsx18(
|
|
@@ -5425,16 +5458,26 @@ import { tableShapeChanged, useRecordsClient as useRecordsClient6 } from "@ai-ma
|
|
|
5425
5458
|
import { Button as Button12, Select as Select3, SelectContent as SelectContent3, SelectItem as SelectItem3, SelectTrigger as SelectTrigger3, SelectValue as SelectValue3 } from "@ai-matrx/design-system";
|
|
5426
5459
|
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
5427
5460
|
var RETYPE_TARGETS = [
|
|
5428
|
-
{ behaviour: "text", label: "Text", holds: "words" },
|
|
5429
|
-
{ behaviour: "range", label: "Number", holds: "a number" },
|
|
5430
|
-
|
|
5461
|
+
{ id: "text", behaviour: "text", kind: null, label: "Text", holds: "words" },
|
|
5462
|
+
{ id: "number", behaviour: "range", kind: "number", label: "Number", holds: "a number" },
|
|
5463
|
+
// A DATE is a `range` of kind date: `migrate_retype` alone cannot say "date" (it keeps the old
|
|
5464
|
+
// kind, so every date would be kept aside), so this one goes through `custom.field_update`'s own
|
|
5465
|
+
// retype arm (`parity_type: datetime`), which converts on the SAME trigger and keeps what does not
|
|
5466
|
+
// fit in `_retired` with the reason — measured on the live store 2026-09-28.
|
|
5467
|
+
{ id: "date", behaviour: "range", kind: "date", label: "Date", holds: "a date" },
|
|
5468
|
+
{ id: "checkbox", behaviour: "boolean", kind: null, label: "Yes / no", holds: "a yes or a no" }
|
|
5431
5469
|
];
|
|
5432
5470
|
var PREVIEW_GROUPS = 1e3;
|
|
5433
5471
|
function retypeableFrom(field2) {
|
|
5434
5472
|
const behaviour = String(field2.type);
|
|
5435
|
-
|
|
5473
|
+
return behaviour === "text" || behaviour === "range" || behaviour === "boolean";
|
|
5474
|
+
}
|
|
5475
|
+
function currentTarget(field2) {
|
|
5436
5476
|
const kind = String(editorKindFor(field2));
|
|
5437
|
-
|
|
5477
|
+
if (String(field2.type) === "boolean") return "checkbox";
|
|
5478
|
+
if (kind === "datetime" || kind === "date") return "date";
|
|
5479
|
+
if (String(field2.type) === "range") return "number";
|
|
5480
|
+
return "text";
|
|
5438
5481
|
}
|
|
5439
5482
|
function FieldRetype({ field: field2, tableId, onRetyped }) {
|
|
5440
5483
|
const client = useRecordsClient6();
|
|
@@ -5445,7 +5488,7 @@ function FieldRetype({ field: field2, tableId, onRetyped }) {
|
|
|
5445
5488
|
const [busy, setBusy] = useState17(false);
|
|
5446
5489
|
const [said, setSaid] = useState17(null);
|
|
5447
5490
|
const name = fieldName(field2);
|
|
5448
|
-
const targets = RETYPE_TARGETS.filter((t) => t.
|
|
5491
|
+
const targets = RETYPE_TARGETS.filter((t) => t.id !== currentTarget(field2));
|
|
5449
5492
|
useEffect14(() => {
|
|
5450
5493
|
if (!to) return;
|
|
5451
5494
|
let cancelled = false;
|
|
@@ -5460,7 +5503,7 @@ function FieldRetype({ field: field2, tableId, onRetyped }) {
|
|
|
5460
5503
|
return;
|
|
5461
5504
|
}
|
|
5462
5505
|
const groups = answer.data.map((row) => ({ value: row.groups[field2.key] ?? null, count: row.row_count }));
|
|
5463
|
-
setPreview(previewRetype({ type: to.behaviour }, groups, answer.data.length >= PREVIEW_GROUPS));
|
|
5506
|
+
setPreview(previewRetype({ type: to.behaviour, kind: to.kind }, groups, answer.data.length >= PREVIEW_GROUPS));
|
|
5464
5507
|
});
|
|
5465
5508
|
return () => {
|
|
5466
5509
|
cancelled = true;
|
|
@@ -5470,22 +5513,35 @@ function FieldRetype({ field: field2, tableId, onRetyped }) {
|
|
|
5470
5513
|
return /* @__PURE__ */ jsxs18("p", { className: "text-[11px] text-muted-foreground", "data-records-retype": "not-here", children: [
|
|
5471
5514
|
name,
|
|
5472
5515
|
" holds ",
|
|
5473
|
-
String(
|
|
5474
|
-
",
|
|
5516
|
+
String(field2.type) === "formula" ? "a value the table works out" : "records picked from a list or another table",
|
|
5517
|
+
", and the store keeps those as record ids \u2014 turned into words or numbers they would read as ids, so it is not changed here."
|
|
5475
5518
|
] });
|
|
5476
5519
|
}
|
|
5477
5520
|
const change = async () => {
|
|
5478
5521
|
if (!to) return;
|
|
5479
5522
|
setBusy(true);
|
|
5480
5523
|
setRefusal(null);
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
if (
|
|
5484
|
-
|
|
5485
|
-
|
|
5524
|
+
let holding;
|
|
5525
|
+
let aside;
|
|
5526
|
+
if (to.id === "date") {
|
|
5527
|
+
const done = await client.fieldUpdate({ field_id: field2.id, patch: { parity_type: "datetime" } });
|
|
5528
|
+
setBusy(false);
|
|
5529
|
+
if (!done.ok) {
|
|
5530
|
+
setRefusal(done.error);
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5533
|
+
holding = preview?.converts ?? 0;
|
|
5534
|
+
aside = preview?.keptAside ?? 0;
|
|
5535
|
+
} else {
|
|
5536
|
+
const done = await client.migrateRetype({ field_id: field2.id, to: to.behaviour });
|
|
5537
|
+
setBusy(false);
|
|
5538
|
+
if (!done.ok) {
|
|
5539
|
+
setRefusal(done.error);
|
|
5540
|
+
return;
|
|
5541
|
+
}
|
|
5542
|
+
holding = done.data.records_still_holding_a_value ?? 0;
|
|
5543
|
+
aside = done.data.values_in_retired_for_this_field ?? 0;
|
|
5486
5544
|
}
|
|
5487
|
-
const holding = done.data.records_still_holding_a_value ?? 0;
|
|
5488
|
-
const aside = done.data.values_in_retired_for_this_field ?? 0;
|
|
5489
5545
|
setSaid(
|
|
5490
5546
|
`${name} now holds ${to.holds}. ${holding} ${holding === 1 ? "record holds" : "records hold"} a value` + (aside > 0 ? `; ${aside} ${aside === 1 ? "value that did not fit was" : "values that did not fit were"} kept with ${aside === 1 ? "its record" : "their records"}, with the reason.` : ".")
|
|
5491
5547
|
);
|
|
@@ -5496,9 +5552,9 @@ function FieldRetype({ field: field2, tableId, onRetyped }) {
|
|
|
5496
5552
|
};
|
|
5497
5553
|
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-1.5", "data-records-retype": "", children: [
|
|
5498
5554
|
/* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5499
|
-
/* @__PURE__ */ jsxs18(Select3, { value: to?.
|
|
5555
|
+
/* @__PURE__ */ jsxs18(Select3, { value: to?.id ?? "", onValueChange: (next) => setTo(targets.find((t) => t.id === next) ?? null), children: [
|
|
5500
5556
|
/* @__PURE__ */ jsx22(SelectTrigger3, { size: "sm", "aria-label": "Change what this column holds", className: "w-auto min-w-[12rem]", children: /* @__PURE__ */ jsx22(SelectValue3, { placeholder: "Change what this column holds" }) }),
|
|
5501
|
-
/* @__PURE__ */ jsx22(SelectContent3, { children: targets.map((t) => /* @__PURE__ */ jsx22(SelectItem3, { value: t.
|
|
5557
|
+
/* @__PURE__ */ jsx22(SelectContent3, { children: targets.map((t) => /* @__PURE__ */ jsx22(SelectItem3, { value: t.id, children: t.label }, t.id)) })
|
|
5502
5558
|
] }),
|
|
5503
5559
|
to ? /* @__PURE__ */ jsx22(Button12, { size: "sm", disabled: busy || asking || preview === null, onClick: () => void change(), "data-records-retype-go": "", children: busy ? "Changing\u2026" : `Change to ${to.label}` }) : null,
|
|
5504
5560
|
to ? /* @__PURE__ */ jsx22(Button12, { size: "sm", variant: "ghost", onClick: () => setTo(null), disabled: busy, children: "Cancel" }) : null
|
|
@@ -7317,7 +7373,7 @@ function entityReferenceKindsOf(field2) {
|
|
|
7317
7373
|
|
|
7318
7374
|
// src/gridColors.tsx
|
|
7319
7375
|
import { useCallback as useCallback8, useEffect as useEffect18, useMemo as useMemo12, useState as useState21 } from "react";
|
|
7320
|
-
import { optionKey as
|
|
7376
|
+
import { optionKey as optionKey3, optionLabel as optionLabel2 } from "@ai-matrx/records";
|
|
7321
7377
|
import { useRecordsClient as useRecordsClient10 } from "@ai-matrx/records/react";
|
|
7322
7378
|
import {
|
|
7323
7379
|
Button as Button15,
|
|
@@ -7392,7 +7448,7 @@ function useChoiceOptions(fields, keys) {
|
|
|
7392
7448
|
function lookOf(option) {
|
|
7393
7449
|
const raw = option.data?.["color"];
|
|
7394
7450
|
const color = typeof raw === "string" && STYLE_COLORS4.includes(raw) ? raw : void 0;
|
|
7395
|
-
return { id: String(option.id), key:
|
|
7451
|
+
return { id: String(option.id), key: optionKey3(option), label: optionLabel2(option), ...color ? { color } : {} };
|
|
7396
7452
|
}
|
|
7397
7453
|
function paintByOption(style, fields, index, lookup) {
|
|
7398
7454
|
if (index.size === 0) return { style, documentOf: (doc) => doc, choiceColorFor: lookup };
|
|
@@ -8074,7 +8130,7 @@ function SummarySheetControls({
|
|
|
8074
8130
|
|
|
8075
8131
|
// src/pasteValues.ts
|
|
8076
8132
|
import { predictValueRefusals } from "@ai-matrx/records/core";
|
|
8077
|
-
import { coerceTypedAnswer as coerceTypedAnswer2, optionKey as
|
|
8133
|
+
import { coerceTypedAnswer as coerceTypedAnswer2, optionKey as optionKey4 } from "@ai-matrx/records";
|
|
8078
8134
|
function wordForStoredChoice(options, value) {
|
|
8079
8135
|
if (options === void 0 || typeof value !== "string" || value === "") return null;
|
|
8080
8136
|
for (const label of options.labels) {
|
|
@@ -8087,7 +8143,7 @@ function choiceOptionsOf(options) {
|
|
|
8087
8143
|
const labels = [];
|
|
8088
8144
|
for (const option of options) {
|
|
8089
8145
|
const id = option.id;
|
|
8090
|
-
const key =
|
|
8146
|
+
const key = optionKey4(option);
|
|
8091
8147
|
const label = recordName(option.data, null, "");
|
|
8092
8148
|
if (label) {
|
|
8093
8149
|
labels.push(label);
|
|
@@ -9492,7 +9548,10 @@ function columnForField(field2, editing, mayWriteRow, whyNotRow, look, onAskWhoC
|
|
|
9492
9548
|
// The type is in the header's tooltip rather than a second header row: one
|
|
9493
9549
|
// row of headers, always — and it is the parity type's own WORD, not a
|
|
9494
9550
|
// token (`Long text`, never `long_text`).
|
|
9495
|
-
|
|
9551
|
+
// Top-aligned where a value may wrap to several lines; on a phone a cell never wraps (one line,
|
|
9552
|
+
// scrolled sideways) and the row is as tall as its 44 px tick box, so the value sits in the
|
|
9553
|
+
// middle beside the box — top-aligned it sat ~10 px above it (merged-grid review 2, 390 px).
|
|
9554
|
+
className: "sm:align-top max-sm:align-middle",
|
|
9496
9555
|
...{ title: fieldTypeLabel(field2) }
|
|
9497
9556
|
};
|
|
9498
9557
|
}
|
|
@@ -9522,6 +9581,12 @@ function PastePreview({
|
|
|
9522
9581
|
"aria-label": "What this paste will write",
|
|
9523
9582
|
"data-matrx-paste-preview": true,
|
|
9524
9583
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
|
|
9584
|
+
onKeyDown: (event) => {
|
|
9585
|
+
if (event.key !== "Escape") return;
|
|
9586
|
+
event.preventDefault();
|
|
9587
|
+
event.stopPropagation();
|
|
9588
|
+
if (!busy) onCancel();
|
|
9589
|
+
},
|
|
9525
9590
|
children: /* @__PURE__ */ jsxs30("div", { className: "flex max-h-[80vh] w-full max-w-2xl flex-col gap-3 overflow-auto rounded-lg border bg-background p-4 shadow-lg", children: [
|
|
9526
9591
|
/* @__PURE__ */ jsxs30("div", { className: "flex items-baseline justify-between gap-3", children: [
|
|
9527
9592
|
/* @__PURE__ */ jsx34("h2", { className: "text-sm font-medium", children: outcome ? "What was written" : "Before this is written" }),
|
|
@@ -9597,11 +9662,13 @@ function PastePreview({
|
|
|
9597
9662
|
". Add the columns first and paste again."
|
|
9598
9663
|
] }) : null,
|
|
9599
9664
|
/* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-end gap-2 pt-1", children: [
|
|
9600
|
-
/* @__PURE__ */ jsx34(Button22, { size: "sm", variant: "ghost", onClick: onCancel, disabled: busy, children: "Cancel" }),
|
|
9665
|
+
/* @__PURE__ */ jsx34(Button22, { size: "sm", variant: "ghost", onClick: onCancel, disabled: busy, autoFocus: nothingToDo, children: "Cancel" }),
|
|
9601
9666
|
/* @__PURE__ */ jsx34(
|
|
9602
9667
|
Button22,
|
|
9603
9668
|
{
|
|
9604
9669
|
size: "sm",
|
|
9670
|
+
autoFocus: !nothingToDo,
|
|
9671
|
+
"data-matrx-paste-confirm": "",
|
|
9605
9672
|
disabled: busy || nothingToDo,
|
|
9606
9673
|
onClick: () => {
|
|
9607
9674
|
setBusy(true);
|
|
@@ -9621,7 +9688,7 @@ function Outcome({ outcome, onClose }) {
|
|
|
9621
9688
|
return /* @__PURE__ */ jsxs30(Fragment11, { children: [
|
|
9622
9689
|
/* @__PURE__ */ jsx34("p", { className: "text-sm", "data-matrx-paste-outcome": whole ? "whole" : "partial", children: whole ? `All ${outcome.readBack} ${outcome.readBack === 1 ? "value is" : "values are"} in the table \u2014 read back out of it, not just accepted.` : `${outcome.readBack} of ${outcome.readBack + outcome.missing} values are in the table. The rest are not, and nothing was lost from what you copied \u2014 it is still on your clipboard.` }),
|
|
9623
9690
|
outcome.refusals.length > 0 ? /* @__PURE__ */ jsx34("ul", { className: "flex flex-col gap-1", children: outcome.refusals.slice(0, 8).map((why, index) => /* @__PURE__ */ jsx34("li", { className: "text-xs text-muted-foreground", children: why }, index)) }) : null,
|
|
9624
|
-
/* @__PURE__ */ jsx34("div", { className: "flex justify-end pt-1", children: /* @__PURE__ */ jsx34(Button22, { size: "sm", onClick: onClose, children: "Close" }) })
|
|
9691
|
+
/* @__PURE__ */ jsx34("div", { className: "flex justify-end pt-1", children: /* @__PURE__ */ jsx34(Button22, { size: "sm", onClick: onClose, autoFocus: true, children: "Close" }) })
|
|
9625
9692
|
] });
|
|
9626
9693
|
}
|
|
9627
9694
|
function PlanTable({ plan }) {
|