@ai-matrx/records-ui 0.102.2 → 0.102.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 +3160 -2878
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -3
- package/dist/index.d.ts +90 -3
- package/dist/index.js +4117 -3843
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +261 -153
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +371 -271
- package/dist/pickers.js.map +1 -1
- package/package.json +4 -4
package/dist/pickers.cjs
CHANGED
|
@@ -561,29 +561,121 @@ var import_design_system4 = require("@ai-matrx/design-system");
|
|
|
561
561
|
var import_react2 = require("react");
|
|
562
562
|
var import_design_system2 = require("@ai-matrx/design-system");
|
|
563
563
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
564
|
+
function Glyph({ d, className }) {
|
|
565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, className, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d }) });
|
|
566
|
+
}
|
|
567
|
+
var TICK = "m5 12 4 4L19 6";
|
|
568
|
+
var CROSS = "M18 6 6 18M6 6l12 12";
|
|
569
|
+
var PLUS = "M12 5v14M5 12h14";
|
|
564
570
|
function useListKeys(args) {
|
|
565
|
-
const { count, resetOn, onPick, onNothing, onClose } = args;
|
|
571
|
+
const { count, resetOn, onPick, onNothing, onClose, onFinish, onBackspaceEmpty, searchEmpty } = args;
|
|
566
572
|
const [highlight, setHighlight] = (0, import_react2.useState)(0);
|
|
567
|
-
(0, import_react2.
|
|
573
|
+
const [moved, setMoved] = (0, import_react2.useState)(false);
|
|
574
|
+
(0, import_react2.useEffect)(() => {
|
|
575
|
+
setHighlight(0);
|
|
576
|
+
setMoved(false);
|
|
577
|
+
}, [resetOn]);
|
|
568
578
|
const at = count === 0 ? -1 : Math.min(highlight, count - 1);
|
|
569
579
|
const onKeyDown = (event) => {
|
|
580
|
+
if (event.key === "Tab") return;
|
|
570
581
|
event.stopPropagation();
|
|
571
582
|
if (event.key === "ArrowDown") {
|
|
572
583
|
event.preventDefault();
|
|
584
|
+
setMoved(true);
|
|
573
585
|
if (count > 0) setHighlight((h) => (Math.min(h, count - 1) + 1) % count);
|
|
574
586
|
} else if (event.key === "ArrowUp") {
|
|
575
587
|
event.preventDefault();
|
|
588
|
+
setMoved(true);
|
|
576
589
|
if (count > 0) setHighlight((h) => (Math.min(h, count - 1) - 1 + count) % count);
|
|
577
590
|
} else if (event.key === "Enter") {
|
|
578
591
|
event.preventDefault();
|
|
579
|
-
if (
|
|
592
|
+
if (onFinish && searchEmpty && !moved) onFinish();
|
|
593
|
+
else if (at >= 0) onPick(at);
|
|
580
594
|
else onNothing?.();
|
|
581
595
|
} else if (event.key === "Escape") {
|
|
582
596
|
event.preventDefault();
|
|
583
597
|
onClose();
|
|
598
|
+
} else if (event.key === "Backspace" && searchEmpty && onBackspaceEmpty) {
|
|
599
|
+
event.preventDefault();
|
|
600
|
+
onBackspaceEmpty();
|
|
584
601
|
}
|
|
585
602
|
};
|
|
586
|
-
return { highlight: at, onKeyDown };
|
|
603
|
+
return { highlight: at, setHighlight, onKeyDown };
|
|
604
|
+
}
|
|
605
|
+
function PickRow({
|
|
606
|
+
label,
|
|
607
|
+
detail,
|
|
608
|
+
title,
|
|
609
|
+
chip,
|
|
610
|
+
picked,
|
|
611
|
+
highlighted,
|
|
612
|
+
disabled,
|
|
613
|
+
attrs,
|
|
614
|
+
icon,
|
|
615
|
+
onPick,
|
|
616
|
+
onPoint
|
|
617
|
+
}) {
|
|
618
|
+
const ref = (0, import_react2.useRef)(null);
|
|
619
|
+
(0, import_react2.useEffect)(() => {
|
|
620
|
+
if (highlighted) ref.current?.scrollIntoView?.({ block: "nearest" });
|
|
621
|
+
}, [highlighted]);
|
|
622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
623
|
+
"button",
|
|
624
|
+
{
|
|
625
|
+
ref,
|
|
626
|
+
type: "button",
|
|
627
|
+
role: "option",
|
|
628
|
+
"aria-selected": picked,
|
|
629
|
+
disabled: disabled ?? false,
|
|
630
|
+
onClick: onPick,
|
|
631
|
+
onMouseMove: onPoint,
|
|
632
|
+
title: title ?? label,
|
|
633
|
+
...attrs ?? {},
|
|
634
|
+
"data-pick-highlighted": highlighted ? "" : void 0,
|
|
635
|
+
className: (0, import_design_system2.cn)(
|
|
636
|
+
"flex w-full min-w-0 items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none disabled:opacity-50",
|
|
637
|
+
highlighted ? "bg-accent text-accent-foreground" : ""
|
|
638
|
+
),
|
|
639
|
+
children: [
|
|
640
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex size-3.5 shrink-0 items-center justify-center", children: [
|
|
641
|
+
picked ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "data-pick-tick": "", className: "contents", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Glyph, { d: TICK, className: "size-3.5" }) }) : null,
|
|
642
|
+
icon === "plus" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Glyph, { d: PLUS, className: "size-3.5 text-muted-foreground" }) : null
|
|
643
|
+
] }),
|
|
644
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "min-w-0 flex-1", children: [
|
|
645
|
+
chip ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: (0, import_design_system2.cn)("inline-flex max-w-full truncate rounded-md border px-1.5 py-0.5 text-xs font-medium", chip), "data-records-choice-chip": "", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "block truncate", children: label }),
|
|
646
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "block truncate text-xs text-muted-foreground", "data-tell-apart": "", children: detail }) : null
|
|
647
|
+
] })
|
|
648
|
+
]
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
function PickChips({ chips }) {
|
|
653
|
+
if (chips.length === 0) return null;
|
|
654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-wrap gap-1 px-1 pb-1", "data-pick-chips": "", children: chips.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
655
|
+
"span",
|
|
656
|
+
{
|
|
657
|
+
"data-pick-chip": "",
|
|
658
|
+
title: c.label,
|
|
659
|
+
className: (0, import_design_system2.cn)(
|
|
660
|
+
"inline-flex max-w-full items-center gap-1 rounded-md border px-1.5 py-0.5 text-xs font-medium",
|
|
661
|
+
c.chip ?? "bg-secondary text-secondary-foreground"
|
|
662
|
+
),
|
|
663
|
+
children: [
|
|
664
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "min-w-0 truncate", children: c.label }),
|
|
665
|
+
c.onRemove ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
666
|
+
"button",
|
|
667
|
+
{
|
|
668
|
+
type: "button",
|
|
669
|
+
"aria-label": `Remove ${c.label}`,
|
|
670
|
+
onClick: c.onRemove,
|
|
671
|
+
className: "shrink-0 rounded-sm opacity-60 hover:opacity-100",
|
|
672
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Glyph, { d: CROSS, className: "size-3" })
|
|
673
|
+
}
|
|
674
|
+
) : null
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
c.id
|
|
678
|
+
)) });
|
|
587
679
|
}
|
|
588
680
|
function PickOrAdd({
|
|
589
681
|
triggerLabel,
|
|
@@ -600,30 +692,51 @@ function PickOrAdd({
|
|
|
600
692
|
add,
|
|
601
693
|
foot,
|
|
602
694
|
footNote,
|
|
603
|
-
onEnterWithNoMatch
|
|
695
|
+
onEnterWithNoMatch,
|
|
696
|
+
anchored,
|
|
697
|
+
children,
|
|
698
|
+
chips,
|
|
699
|
+
multi
|
|
604
700
|
}) {
|
|
605
|
-
const
|
|
701
|
+
const addRow = add && !foot ? add : null;
|
|
702
|
+
const count = items.length + (addRow ? 1 : 0);
|
|
703
|
+
const removable = (chips ?? []).filter((c) => c.onRemove);
|
|
606
704
|
const keys = useListKeys({
|
|
607
|
-
count
|
|
705
|
+
count,
|
|
608
706
|
resetOn: search,
|
|
609
707
|
onPick: (index) => {
|
|
610
708
|
const item = items[index];
|
|
611
|
-
if (item)
|
|
709
|
+
if (item) {
|
|
710
|
+
if (!item.disabled) onPick(item.id);
|
|
711
|
+
} else if (addRow && !addRow.disabled) addRow.onAdd();
|
|
612
712
|
},
|
|
613
713
|
onNothing: onEnterWithNoMatch ?? (add && !add.disabled ? add.onAdd : void 0),
|
|
614
|
-
onClose: () => onOpenChange(false)
|
|
714
|
+
onClose: () => onOpenChange(false),
|
|
715
|
+
...multi ? { onFinish: () => onOpenChange(false) } : {},
|
|
716
|
+
...removable.length > 0 ? { onBackspaceEmpty: () => removable[removable.length - 1]?.onRemove?.() } : {},
|
|
717
|
+
searchEmpty: search === ""
|
|
615
718
|
});
|
|
719
|
+
const hasFoot = Boolean(foot) || Boolean(footNote);
|
|
616
720
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_design_system2.Popover, { open, onOpenChange, children: [
|
|
617
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system2.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system2.Button, { id: triggerId, type: "button", size: "sm", variant: "outline", "aria-label": triggerLabel, children: triggerText }) }),
|
|
721
|
+
anchored ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system2.PopoverAnchor, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex min-h-6 w-full min-w-0 flex-wrap items-center gap-1", "aria-label": triggerLabel, children }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system2.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system2.Button, { id: triggerId, type: "button", size: "sm", variant: "outline", "aria-label": triggerLabel, children: triggerText }) }),
|
|
618
722
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
619
723
|
import_design_system2.PopoverContent,
|
|
620
724
|
{
|
|
621
725
|
sizing: "content",
|
|
622
|
-
|
|
726
|
+
padding: "xs",
|
|
623
727
|
align: "start",
|
|
728
|
+
side: "bottom",
|
|
729
|
+
sideOffset: anchored ? 2 : 4,
|
|
730
|
+
animated: false,
|
|
624
731
|
"data-matrx-cell-editor": "",
|
|
625
|
-
onKeyDown: (event) =>
|
|
732
|
+
onKeyDown: (event) => {
|
|
733
|
+
if (event.key !== "Tab") event.stopPropagation();
|
|
734
|
+
},
|
|
735
|
+
onCloseAutoFocus: (event) => {
|
|
736
|
+
if (anchored) event.preventDefault();
|
|
737
|
+
},
|
|
626
738
|
children: [
|
|
739
|
+
chips ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PickChips, { chips }) : null,
|
|
627
740
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
628
741
|
import_design_system2.BasicInput,
|
|
629
742
|
{
|
|
@@ -634,53 +747,46 @@ function PickOrAdd({
|
|
|
634
747
|
"aria-label": `Search \u2014 ${triggerLabel}`,
|
|
635
748
|
autoFocus: true,
|
|
636
749
|
autoComplete: "off",
|
|
637
|
-
className: "mb-1 h-
|
|
750
|
+
className: "mb-1 h-8 text-sm"
|
|
638
751
|
}
|
|
639
752
|
),
|
|
640
753
|
notice,
|
|
641
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_design_system2.ScrollArea, { className: "max-h-
|
|
642
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
item.
|
|
653
|
-
index === keys.highlight
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_design_system2.ScrollArea, { className: "max-h-64", children: [
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { role: "listbox", "aria-label": triggerLabel, "aria-multiselectable": multi ? true : void 0, children: [
|
|
756
|
+
items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
757
|
+
PickRow,
|
|
758
|
+
{
|
|
759
|
+
label: item.label,
|
|
760
|
+
detail: item.detail,
|
|
761
|
+
title: item.title,
|
|
762
|
+
chip: item.chip,
|
|
763
|
+
picked: item.picked,
|
|
764
|
+
disabled: item.disabled,
|
|
765
|
+
attrs: item.attrs,
|
|
766
|
+
highlighted: index === keys.highlight,
|
|
767
|
+
onPick: () => onPick(item.id),
|
|
768
|
+
onPoint: () => keys.setHighlight(index)
|
|
769
|
+
},
|
|
770
|
+
item.id
|
|
771
|
+
)),
|
|
772
|
+
addRow ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
773
|
+
PickRow,
|
|
774
|
+
{
|
|
775
|
+
label: addRow.label,
|
|
776
|
+
picked: false,
|
|
777
|
+
icon: "plus",
|
|
778
|
+
disabled: addRow.disabled,
|
|
779
|
+
attrs: { "data-pick-add": "" },
|
|
780
|
+
highlighted: items.length === keys.highlight,
|
|
781
|
+
onPick: addRow.onAdd,
|
|
782
|
+
onPoint: () => keys.setHighlight(items.length)
|
|
783
|
+
}
|
|
784
|
+
) : null
|
|
785
|
+
] }),
|
|
668
786
|
items.length === 0 && empty ? empty : null
|
|
669
787
|
] }),
|
|
670
788
|
hasFoot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-1 border-t pt-1", children: [
|
|
671
|
-
foot
|
|
672
|
-
import_design_system2.Button,
|
|
673
|
-
{
|
|
674
|
-
type: "button",
|
|
675
|
-
size: "sm",
|
|
676
|
-
variant: "ghost",
|
|
677
|
-
title: add.label,
|
|
678
|
-
className: "w-full justify-start text-xs",
|
|
679
|
-
disabled: add.disabled ?? false,
|
|
680
|
-
onClick: add.onAdd,
|
|
681
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "block w-full truncate text-left", children: add.label })
|
|
682
|
-
}
|
|
683
|
-
) : null),
|
|
789
|
+
foot,
|
|
684
790
|
footNote
|
|
685
791
|
] }) : null
|
|
686
792
|
]
|
|
@@ -1000,13 +1106,19 @@ var import_jsx_runtime4 = (
|
|
|
1000
1106
|
require("react/jsx-runtime")
|
|
1001
1107
|
);
|
|
1002
1108
|
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.";
|
|
1003
|
-
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true, onPicked }) {
|
|
1109
|
+
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true, onPicked, autoOpen, onClose }) {
|
|
1004
1110
|
const client = (0, import_react4.useOptionalRecordsClient)();
|
|
1005
1111
|
const [rows, setRows] = (0, import_react3.useState)(null);
|
|
1006
1112
|
const [words, setWords] = (0, import_react3.useState)({});
|
|
1007
1113
|
const [refusal2, setRefusal] = (0, import_react3.useState)(null);
|
|
1008
1114
|
const [search, setSearch] = (0, import_react3.useState)("");
|
|
1009
|
-
const [open,
|
|
1115
|
+
const [open, setOpenState] = (0, import_react3.useState)(autoOpen === true);
|
|
1116
|
+
const onCloseRef = (0, import_react3.useRef)(onClose);
|
|
1117
|
+
onCloseRef.current = onClose;
|
|
1118
|
+
const setOpen = (0, import_react3.useCallback)((next) => {
|
|
1119
|
+
setOpenState(next);
|
|
1120
|
+
if (!next) onCloseRef.current?.();
|
|
1121
|
+
}, []);
|
|
1010
1122
|
const [target, setTarget] = (0, import_react3.useState)(null);
|
|
1011
1123
|
const [targetRefusal, setTargetRefusal] = (0, import_react3.useState)(null);
|
|
1012
1124
|
const [creating, setCreating] = (0, import_react3.useState)(false);
|
|
@@ -1138,22 +1250,28 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1138
1250
|
};
|
|
1139
1251
|
const apart = tellApart(rows ?? [], titleOf);
|
|
1140
1252
|
const visible = (0, import_records3.rankTypedMatches)(rows ?? [], search, (row) => [titleOf(row.id), apart.get(row.id) ?? ""]);
|
|
1253
|
+
const anchored = autoOpen === true;
|
|
1254
|
+
const cellChips = picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1255
|
+
RecordChip,
|
|
1256
|
+
{
|
|
1257
|
+
title: titleOf(recordId),
|
|
1258
|
+
...disabled || anchored ? {} : { onRemove: () => toggle(recordId) }
|
|
1259
|
+
},
|
|
1260
|
+
recordId
|
|
1261
|
+
));
|
|
1141
1262
|
return (
|
|
1142
1263
|
// Never wider than where it is drawn (a grid cell beside a sticky column: B3-07) — chips truncate, the button wraps.
|
|
1143
1264
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_design_system4.cn)("flex min-w-0 max-w-full flex-wrap items-center gap-1", className), children: [
|
|
1144
|
-
|
|
1145
|
-
RecordChip,
|
|
1146
|
-
{
|
|
1147
|
-
title: titleOf(recordId),
|
|
1148
|
-
...disabled ? {} : { onRemove: () => toggle(recordId) }
|
|
1149
|
-
},
|
|
1150
|
-
recordId
|
|
1151
|
-
)),
|
|
1265
|
+
anchored && !disabled ? null : cellChips,
|
|
1152
1266
|
disabled ? null : (
|
|
1153
1267
|
// THE ONE "PICK OR ADD" BUILDER (PickOrAdd.tsx) — the choice editor draws the same one.
|
|
1268
|
+
// In a grid cell it hangs from the cell's own chips; what is picked heads the list with its ×.
|
|
1154
1269
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1155
1270
|
PickOrAdd,
|
|
1156
1271
|
{
|
|
1272
|
+
anchored,
|
|
1273
|
+
multi: field.multi,
|
|
1274
|
+
chips: picked.map((recordId) => ({ id: recordId, label: titleOf(recordId), onRemove: () => toggle(recordId) })),
|
|
1157
1275
|
triggerId: id,
|
|
1158
1276
|
triggerLabel: `Pick for ${fieldName(field)}`,
|
|
1159
1277
|
triggerText: picked.length === 0 ? "Pick" : "Change",
|
|
@@ -1240,7 +1358,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1240
1358
|
}
|
|
1241
1359
|
}
|
|
1242
1360
|
)
|
|
1243
|
-
) : null
|
|
1361
|
+
) : null,
|
|
1362
|
+
children: anchored ? cellChips : null
|
|
1244
1363
|
}
|
|
1245
1364
|
)
|
|
1246
1365
|
)
|
|
@@ -2402,16 +2521,6 @@ function PersonPicker({ field, value, onChange, disabled, id, className, autoOpe
|
|
|
2402
2521
|
};
|
|
2403
2522
|
}, [client, personTable, picked.length, people.length]);
|
|
2404
2523
|
const listed = (0, import_records7.rankTypedMatches)(members ?? [], search, memberName);
|
|
2405
|
-
const keys = useListKeys({
|
|
2406
|
-
count: listed.length,
|
|
2407
|
-
resetOn: search,
|
|
2408
|
-
onPick: (index) => {
|
|
2409
|
-
const member = listed[index];
|
|
2410
|
-
if (member && !busy) void pickRef.current?.(member);
|
|
2411
|
-
},
|
|
2412
|
-
onClose: () => setOpen(false)
|
|
2413
|
-
});
|
|
2414
|
-
const pickRef = (0, import_react17.useRef)(null);
|
|
2415
2524
|
if (!membersPort || !client) {
|
|
2416
2525
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: (0, import_design_system10.cn)("text-xs text-muted-foreground", className), children: NO_MEMBERS_REASON });
|
|
2417
2526
|
}
|
|
@@ -2450,7 +2559,6 @@ function PersonPicker({ field, value, onChange, disabled, id, className, autoOpe
|
|
|
2450
2559
|
else next.add(resolved.data);
|
|
2451
2560
|
onChange(next.size === 0 ? null : [...next]);
|
|
2452
2561
|
};
|
|
2453
|
-
pickRef.current = pick;
|
|
2454
2562
|
const drop = (recordId) => {
|
|
2455
2563
|
const next = picked.filter((id2) => id2 !== recordId);
|
|
2456
2564
|
onChange(next.length === 0 ? null : field.multi ? next : next[0] ?? null);
|
|
@@ -2459,79 +2567,56 @@ function PersonPicker({ field, value, onChange, disabled, id, className, autoOpe
|
|
|
2459
2567
|
const pickedUserIds = new Set(
|
|
2460
2568
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
2461
2569
|
);
|
|
2570
|
+
const anchored = autoOpen === true;
|
|
2571
|
+
const cellChips = picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2572
|
+
RecordChip,
|
|
2573
|
+
{
|
|
2574
|
+
title: nameOfRecord(recordId),
|
|
2575
|
+
...disabled || anchored ? {} : { onRemove: () => drop(recordId) }
|
|
2576
|
+
},
|
|
2577
|
+
recordId
|
|
2578
|
+
));
|
|
2462
2579
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: (0, import_design_system10.cn)("flex flex-wrap items-center gap-1", className), children: [
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
)),
|
|
2471
|
-
disabled ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system10.Popover, { open, onOpenChange: setOpen, children: [
|
|
2472
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system10.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2473
|
-
import_design_system10.Button,
|
|
2474
|
-
{
|
|
2475
|
-
id,
|
|
2476
|
-
type: "button",
|
|
2477
|
-
size: "sm",
|
|
2478
|
-
variant: "outline",
|
|
2479
|
-
"aria-label": `Pick a person for ${fieldName(field)}`,
|
|
2480
|
-
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
2481
|
-
}
|
|
2482
|
-
) }),
|
|
2483
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2484
|
-
import_design_system10.PopoverContent,
|
|
2580
|
+
anchored && !disabled ? null : cellChips,
|
|
2581
|
+
disabled ? null : (
|
|
2582
|
+
// THE ONE "PICK OR ADD" BUILDER (lane CELL-EDITORS, 2026-10-06): the person list used to carry
|
|
2583
|
+
// its own copy of the list's layout and keys; it now draws the same rows, ticks, highlight and
|
|
2584
|
+
// head chips as every other picker, and in a grid cell hangs from the cell.
|
|
2585
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2586
|
+
PickOrAdd,
|
|
2485
2587
|
{
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2588
|
+
anchored,
|
|
2589
|
+
multi: field.multi,
|
|
2590
|
+
triggerId: id,
|
|
2591
|
+
triggerLabel: `Pick a person for ${fieldName(field)}`,
|
|
2592
|
+
triggerText: picked.length === 0 ? "Pick a person" : "Change",
|
|
2593
|
+
open,
|
|
2594
|
+
onOpenChange: setOpen,
|
|
2595
|
+
search,
|
|
2596
|
+
onSearch: setSearch,
|
|
2597
|
+
chips: picked.map((recordId) => ({ id: recordId, label: nameOfRecord(recordId), onRemove: () => drop(recordId) })),
|
|
2598
|
+
items: visible.map((member) => ({
|
|
2599
|
+
id: member.userId,
|
|
2600
|
+
label: memberName(member),
|
|
2601
|
+
// A name and an email with nothing between them read as one run-on string (verdict,
|
|
2602
|
+
// 19 September). They are two facts, so they are drawn as two.
|
|
2603
|
+
detail: member.email && member.email !== memberName(member) ? member.email : null,
|
|
2604
|
+
picked: pickedUserIds.has(member.userId),
|
|
2605
|
+
disabled: busy
|
|
2606
|
+
})),
|
|
2607
|
+
onPick: (userId) => {
|
|
2608
|
+
const member = visible.find((m) => m.userId === userId);
|
|
2609
|
+
if (member && !busy) void pick(member);
|
|
2610
|
+
},
|
|
2611
|
+
notice: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2505
2612
|
error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefusalLine, { error, className: "p-2" }) : null,
|
|
2506
|
-
members === null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading the members\u2026" }) : null
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
{
|
|
2511
|
-
type: "button",
|
|
2512
|
-
disabled: busy,
|
|
2513
|
-
"data-pick-highlighted": index === keys.highlight ? "" : void 0,
|
|
2514
|
-
onClick: () => void pick(member),
|
|
2515
|
-
className: (0, import_design_system10.cn)(
|
|
2516
|
-
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
2517
|
-
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : "",
|
|
2518
|
-
index === keys.highlight ? "ring-1 ring-ring" : ""
|
|
2519
|
-
),
|
|
2520
|
-
children: [
|
|
2521
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: memberName(member) }),
|
|
2522
|
-
member.email && member.email !== memberName(member) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "ml-1.5 opacity-60", children: [
|
|
2523
|
-
"\xB7 ",
|
|
2524
|
-
member.email
|
|
2525
|
-
] }) : null
|
|
2526
|
-
]
|
|
2527
|
-
}
|
|
2528
|
-
) }, member.userId)) }),
|
|
2529
|
-
members !== null && visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: members.length === 0 ? "This organization has no other members yet, so there is nobody to assign." : "Nobody in this organization matches." }) : null
|
|
2530
|
-
] })
|
|
2531
|
-
]
|
|
2613
|
+
members === null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading the members\u2026" }) : null
|
|
2614
|
+
] }),
|
|
2615
|
+
empty: members !== null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: members.length === 0 ? "This organization has no other members yet, so there is nobody to assign." : "Nobody in this organization matches." }) : null,
|
|
2616
|
+
children: anchored ? cellChips : null
|
|
2532
2617
|
}
|
|
2533
2618
|
)
|
|
2534
|
-
|
|
2619
|
+
)
|
|
2535
2620
|
] });
|
|
2536
2621
|
}
|
|
2537
2622
|
|
|
@@ -3320,7 +3405,7 @@ function FieldControl({ field, value, onChange, disabled, id, autoOpen, onClose,
|
|
|
3320
3405
|
}
|
|
3321
3406
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
3322
3407
|
case "relation":
|
|
3323
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RelationPicker, { field, value, onChange, disabled, id: inputId, onPicked: onCommit });
|
|
3408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RelationPicker, { field, value, onChange, disabled, id: inputId, onPicked: onCommit, autoOpen, onClose });
|
|
3324
3409
|
default:
|
|
3325
3410
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
3326
3411
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
@@ -3697,7 +3782,9 @@ function OpenChoicePicker({
|
|
|
3697
3782
|
});
|
|
3698
3783
|
};
|
|
3699
3784
|
const offList = q !== "" && decided.decision !== "chosen" && decided.decision !== "empty";
|
|
3700
|
-
const
|
|
3785
|
+
const [askingFor, setAskingFor] = (0, import_react24.useState)(null);
|
|
3786
|
+
const asks = offList && decided.decision === "ask" && (visible.length === 0 || askingFor === q);
|
|
3787
|
+
const foot = asks ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3701
3788
|
ChoiceAskButtons,
|
|
3702
3789
|
{
|
|
3703
3790
|
field,
|
|
@@ -3713,17 +3800,36 @@ function OpenChoicePicker({
|
|
|
3713
3800
|
}
|
|
3714
3801
|
}
|
|
3715
3802
|
) : null;
|
|
3716
|
-
const add = offList && decided.decision === "add" ? { label: `Add \u201C${q}\u201D to the choices`, onAdd: addTyped, disabled: adding } : offList && decided.decision === "keep_as_typed" ? { label: `Keep \u201C${q}\u201D as typed`, onAdd: () => choose(q) } : null;
|
|
3803
|
+
const add = offList && decided.decision === "ask" && !asks ? { label: `Add \u201C${q}\u201D\u2026`, onAdd: () => setAskingFor(q) } : offList && decided.decision === "add" ? { label: `Add \u201C${q}\u201D to the choices`, onAdd: addTyped, disabled: adding } : offList && decided.decision === "keep_as_typed" ? { label: `Keep \u201C${q}\u201D as typed`, onAdd: () => choose(q) } : null;
|
|
3804
|
+
const pickedOptions = current.map((v) => options.find((o) => (0, import_records10.isTheChosen)(o, v)));
|
|
3805
|
+
const cellChips = pickedWords.map((word, i) => {
|
|
3806
|
+
const chip = chipOf(pickedOptions[i], word);
|
|
3807
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system14.Badge, { variant: "outline", className: (0, import_design_system14.cn)("text-[11px]", chip.className), "data-records-choice-chip": "", children: word }, word);
|
|
3808
|
+
});
|
|
3809
|
+
const headChips = pickedWords.map((word, i) => {
|
|
3810
|
+
const option = pickedOptions[i];
|
|
3811
|
+
return {
|
|
3812
|
+
id: `${word}-${i}`,
|
|
3813
|
+
label: word,
|
|
3814
|
+
chip: chipOf(option, word).className,
|
|
3815
|
+
onRemove: () => {
|
|
3816
|
+
if (field.multi) choose(option ? (0, import_records10.optionKey)(option) : String(current[i]));
|
|
3817
|
+
else onChange(null);
|
|
3818
|
+
}
|
|
3819
|
+
};
|
|
3820
|
+
});
|
|
3821
|
+
const anchored = autoOpen === true;
|
|
3717
3822
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { id, className: "flex flex-wrap items-center gap-1", children: [
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system14.Badge, { variant: "outline", className: (0, import_design_system14.cn)("text-[11px]", chip.className), "data-records-choice-chip": "", children: word }, word);
|
|
3721
|
-
}),
|
|
3722
|
-
disabled ? null : (
|
|
3823
|
+
anchored || disabled ? null : cellChips,
|
|
3824
|
+
disabled ? cellChips : (
|
|
3723
3825
|
// THE ONE "PICK OR ADD" BUILDER (PickOrAdd.tsx) — the relation picker draws the same one.
|
|
3826
|
+
// In a grid cell it hangs from the cell's own chips; no button is drawn in the cell.
|
|
3724
3827
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3725
3828
|
PickOrAdd,
|
|
3726
3829
|
{
|
|
3830
|
+
anchored,
|
|
3831
|
+
chips: headChips,
|
|
3832
|
+
multi: field.multi,
|
|
3727
3833
|
triggerLabel: `Pick for ${fieldName(field)}`,
|
|
3728
3834
|
triggerText: pickedWords.length === 0 ? "Choose" : "Change",
|
|
3729
3835
|
open,
|
|
@@ -3732,6 +3838,7 @@ function OpenChoicePicker({
|
|
|
3732
3838
|
onSearch: (next) => {
|
|
3733
3839
|
setSearch(next);
|
|
3734
3840
|
setAddRefused(null);
|
|
3841
|
+
setAskingFor(null);
|
|
3735
3842
|
},
|
|
3736
3843
|
items: visible.map((option) => ({
|
|
3737
3844
|
id: option.id,
|
|
@@ -3746,14 +3853,15 @@ function OpenChoicePicker({
|
|
|
3746
3853
|
empty: q === "" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "No choices yet \u2014 type one to add it." }) : null,
|
|
3747
3854
|
add,
|
|
3748
3855
|
foot,
|
|
3749
|
-
...
|
|
3856
|
+
...asks && !adding ? { onEnterWithNoMatch: addTyped } : {},
|
|
3750
3857
|
footNote: addRefused ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalLine, { error: addRefused }) : offList && decided.decision === "not_a_choice" ? (
|
|
3751
3858
|
// never_add on a column that takes only its choices: said, never a silent nothing.
|
|
3752
3859
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3753
3860
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { "data-records-typed-refusal": "", className: "p-2 text-xs text-muted-foreground", children: notAChoiceSentence(field, [q]) }),
|
|
3754
3861
|
autoOpen ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { "data-records-choice-ask": "", className: "px-2 pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system14.Button, { type: "button", size: "sm", variant: "ghost", className: "h-6 px-2 text-xs", onClick: () => setOpen(false), children: "Cancel" }) }) : null
|
|
3755
3862
|
] })
|
|
3756
|
-
) : null
|
|
3863
|
+
) : null,
|
|
3864
|
+
children: anchored ? cellChips : null
|
|
3757
3865
|
}
|
|
3758
3866
|
)
|
|
3759
3867
|
)
|