@ai-matrx/records-ui 0.93.29 → 0.93.33

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/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
- next[option.id] = recordName(option.data, null, "Unnamed choice");
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) => optionKey(o) === k);
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) => optionKey(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(optionKey(option)) ? "default" : "outline",
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(optionKey(option))) next.delete(optionKey(option));
2850
- else next.add(optionKey(option));
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 ? optionKey(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: optionKey(option), children: titleOf(option) }, option.id)) })
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 ? optionKey(option) : String(v);
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(optionKey(option));
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(
@@ -7317,7 +7350,7 @@ function entityReferenceKindsOf(field2) {
7317
7350
 
7318
7351
  // src/gridColors.tsx
7319
7352
  import { useCallback as useCallback8, useEffect as useEffect18, useMemo as useMemo12, useState as useState21 } from "react";
7320
- import { optionKey as optionKey2, optionLabel as optionLabel2 } from "@ai-matrx/records";
7353
+ import { optionKey as optionKey3, optionLabel as optionLabel2 } from "@ai-matrx/records";
7321
7354
  import { useRecordsClient as useRecordsClient10 } from "@ai-matrx/records/react";
7322
7355
  import {
7323
7356
  Button as Button15,
@@ -7390,7 +7423,9 @@ function useChoiceOptions(fields, keys) {
7390
7423
  return index;
7391
7424
  }
7392
7425
  function lookOf(option) {
7393
- return { id: String(option.id), key: optionKey2(option), label: optionLabel2(option) };
7426
+ const raw = option.data?.["color"];
7427
+ const color = typeof raw === "string" && STYLE_COLORS4.includes(raw) ? raw : void 0;
7428
+ return { id: String(option.id), key: optionKey3(option), label: optionLabel2(option), ...color ? { color } : {} };
7394
7429
  }
7395
7430
  function paintByOption(style, fields, index, lookup) {
7396
7431
  if (index.size === 0) return { style, documentOf: (doc) => doc, choiceColorFor: lookup };
@@ -7414,10 +7449,13 @@ function paintByOption(style, fields, index, lookup) {
7414
7449
  }
7415
7450
  return out ?? doc;
7416
7451
  },
7452
+ // A CHOICE PAINTS IN ITS OWN COLOUR (DATA-V2-BASICS-2 C1b): an option kept in its own Table
7453
+ // carries the colour its owner gave it; coloured by Status, a "Verified" (green) row was tinted
7454
+ // pink by the hash. The option's colour first, then the column's stored colours, then the hash.
7417
7455
  choiceColorFor: (fieldKey, value) => {
7418
7456
  const option = optionFor(index.get(fieldKey), value);
7419
7457
  if (!option) return lookup(fieldKey, value);
7420
- return lookup(fieldKey, option.label) ?? lookup(fieldKey, option.key);
7458
+ return option.color ?? lookup(fieldKey, option.label) ?? lookup(fieldKey, option.key);
7421
7459
  }
7422
7460
  };
7423
7461
  }
@@ -8069,7 +8107,7 @@ function SummarySheetControls({
8069
8107
 
8070
8108
  // src/pasteValues.ts
8071
8109
  import { predictValueRefusals } from "@ai-matrx/records/core";
8072
- import { coerceTypedAnswer as coerceTypedAnswer2, optionKey as optionKey3 } from "@ai-matrx/records";
8110
+ import { coerceTypedAnswer as coerceTypedAnswer2, optionKey as optionKey4 } from "@ai-matrx/records";
8073
8111
  function wordForStoredChoice(options, value) {
8074
8112
  if (options === void 0 || typeof value !== "string" || value === "") return null;
8075
8113
  for (const label of options.labels) {
@@ -8082,7 +8120,7 @@ function choiceOptionsOf(options) {
8082
8120
  const labels = [];
8083
8121
  for (const option of options) {
8084
8122
  const id = option.id;
8085
- const key = optionKey3(option);
8123
+ const key = optionKey4(option);
8086
8124
  const label = recordName(option.data, null, "");
8087
8125
  if (label) {
8088
8126
  labels.push(label);