@ai-matrx/records-ui 0.93.140 → 0.93.146

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.
@@ -42,12 +42,18 @@ declare function whatYouMayDoWithTable(table: Table | null | undefined, level: P
42
42
 
43
43
  /**
44
44
  * A Lucide icon NAME, kebab-case — the same currency `Table.icon` already uses.
45
- * Closed on purpose: each renderer maps every member to its own component, and a
45
+ * Closed on purpose: each renderer maps every member to its own glyph, and a
46
46
  * new member fails that renderer's typecheck until it is mapped.
47
47
  */
48
48
  type ObjectActionIcon = "folder-open" | "external-link" | "link" | "pencil" | "copy" | "folder-input" | "star" | "star-off" | "share-2" | "download" | "upload" | "columns-3" | "settings" | "history" | "blocks" | "file-text" | "calendar-clock" | "list-checks" | "bell" | "door-open" | "inbox" | "layout-dashboard" | "archive-restore" | "archive" | "link-2" | "layout-grid";
49
49
  /** The groups, in their one order. Separators fall out of group boundaries. */
50
50
  type ObjectActionGroup = "open" | "organize" | "share" | "manage" | "built-on" | "danger";
51
+ /**
52
+ * THE STANDARD VERBS every object's menu shares — what the icon strip draws. One
53
+ * per meaning; an object maps its action onto the verb, never a second "share".
54
+ */
55
+ type StandardVerbId = "open" | "open-new-tab" | "copy-link" | "rename" | "duplicate" | "move" | "favorite" | "share" | "export" | "import" | "settings" | "history" | "archive";
56
+ declare const STANDARD_VERBS: readonly StandardVerbId[];
51
57
  interface ObjectActionGroupDef {
52
58
  id: ObjectActionGroup;
53
59
  /** Present only on a group that renders as a submenu. */
@@ -64,6 +70,10 @@ interface ObjectAction {
64
70
  label: string;
65
71
  icon: ObjectActionIcon;
66
72
  group: ObjectActionGroup;
73
+ /** The standard verb this action IS (a strip icon). Absent = a feature row. */
74
+ verb?: StandardVerbId;
75
+ /** A feature row's place among the rows, 1 first. Ignored when `verb` is set. */
76
+ rank?: number;
67
77
  /** Display-only hint ("⌘D"). */
68
78
  shortcut?: string;
69
79
  destructive?: boolean;
@@ -101,14 +111,26 @@ interface TableActionHost {
101
111
  addColumn?: Handler;
102
112
  settings?: Handler;
103
113
  history?: Handler;
114
+ /** Make the layout on screen the table's default. Leave out when it already is. */
115
+ makeDefault?: Handler;
116
+ /** The layout on screen, for "Make Kanban the default". */
117
+ layoutLabel?: string;
104
118
  /** The "Built on it" destinations, one handler for all eight. */
105
119
  openBuiltOn?: (destination: BuiltOnDestination, table: TableActionSubject) => void;
106
120
  archive?: Handler;
107
121
  /**
108
- * ANOTHER LANE'S ENTRIES ("Link a record…", a hub tile's verbs). Return the
109
- * same ids whatever `rights` say — express rights as `disabledReason`. Each
110
- * entry lands at the END of its named group, in the order returned; `danger`
111
- * is refused (Archive is always last) and so is an id already in the list.
122
+ * Why an action whose handler is left out is unavailable here, by action id
123
+ * (≤ 60 chars) — e.g. `make-default: "Already the default"`. Unnamed ids say
124
+ * "Not available here". Never used when rights already refuse.
125
+ */
126
+ unavailableReasons?: Readonly<Record<string, string>>;
127
+ /**
128
+ * ANOTHER LANE'S ENTRIES ("Link a record…", a hub tile's verbs, a feature's own
129
+ * row such as "When a row changes, run an agent…"). Return the same ids whatever
130
+ * `rights` say — express rights as `disabledReason`. Each entry lands at the END
131
+ * of its named group, in the order returned. Refused, loudly: the `danger` group
132
+ * (Archive is always last), a group that does not exist, and an id already in
133
+ * the list.
112
134
  */
113
135
  extend?: (table: TableActionSubject, rights: WhatYouMayDo) => readonly ObjectAction[];
114
136
  }
@@ -127,14 +149,15 @@ declare const ACTION_PLATFORM_TABLE_REASON = "Platform tables can't be changed h
127
149
  /** Why `rights` refuse `what`, in one short line — or null when they allow it. */
128
150
  declare function rightsReason(rights: WhatYouMayDo, what: Capability, table?: TableActionSubject): string | null;
129
151
  /**
130
- * THE TABLE'S ACTIONS, in the one order. Same ids, same order, for every seat
131
- * and every host — only `disabledReason` varies.
152
+ * THE TABLE'S ACTIONS, in the one order. Same ids, same order, same verbs and
153
+ * ranks, for every seat and every host — only `disabledReason` varies.
132
154
  */
133
155
  declare function tableActions(input: TableActionsInput): ObjectAction[];
134
156
  /**
135
157
  * Another lane's entries, placed deterministically: each at the end of its
136
- * group, in the order given. Loud on the two mistakes that would break the
137
- * invariants — a `danger` entry (Archive must stay last) and a duplicate id.
158
+ * group, in the order given. Loud on every mistake that would break the
159
+ * invariants — a `danger` entry (Archive must stay last), a group that does not
160
+ * exist (it would be dropped by every renderer) and a duplicate id.
138
161
  */
139
162
  declare function withExtensions(base: readonly ObjectAction[], extra: readonly ObjectAction[]): ObjectAction[];
140
163
  /** A run of actions that renders together: a plain section, or one submenu. */
@@ -162,7 +185,25 @@ type ObjectActionDropdownEntry = {
162
185
  actions: ObjectAction[];
163
186
  };
164
187
  declare function toDropdownEntries(actions: readonly ObjectAction[]): ObjectActionDropdownEntry[];
165
- /** The action ids a dropdown draws, in order — what G1 compares. */
188
+ /** The actions a dropdown draws, in order — what G1 compares. */
189
+ declare function dropdownActions(entries: readonly ObjectActionDropdownEntry[]): ObjectAction[];
190
+ /** The action ids a dropdown draws, in order. */
166
191
  declare function dropdownActionIds(entries: readonly ObjectActionDropdownEntry[]): string[];
192
+ /** The icon-strip menu: standard verbs as icons, then the first feature rows, then the rest. */
193
+ interface StripAndRows {
194
+ /** Every action with a `verb`, in the list's order. */
195
+ strip: ObjectAction[];
196
+ /** The first `rowCount` feature actions by rank (ties keep list order). */
197
+ rows: ObjectAction[];
198
+ /** The remaining feature actions, behind "More … options", same ordering. */
199
+ more: ObjectAction[];
200
+ }
201
+ declare const STRIP_FEATURE_ROWS = 4;
202
+ /**
203
+ * THE STRIP RENDERER'S SPLIT of the same list. Nothing is added or dropped: every
204
+ * id lands in exactly one of strip / rows / more (G1). Unranked feature actions
205
+ * (an extension that gave none) follow every ranked one.
206
+ */
207
+ declare function toStripAndRows(actions: readonly ObjectAction[], rowCount?: number): StripAndRows;
167
208
 
168
- export { ACTION_CHECKING_ACCESS_REASON as A, type BuiltOnDestination as B, type Capability as C, DEFAULT_ACTION_ORIGIN as D, KERNEL_REASON as K, LEVEL_WORD as L, NOT_ANSWERED_YET as N, type ObjectAction as O, type TableActionHost as T, type WhatYouMayDo as W, ACTION_NOT_HERE_REASON as a, ACTION_NO_ACCESS_REASON as b, ACTION_PLATFORM_TABLE_REASON as c, OBJECT_ACTION_GROUPS as d, type ObjectActionDropdownEntry as e, type ObjectActionGroup as f, type ObjectActionGroupDef as g, type ObjectActionIcon as h, type ObjectActionSection as i, type TableActionSubject as j, type TableActionsInput as k, dropdownActionIds as l, groupObjectActions as m, levelNeededFor as n, tableLink as o, toDropdownEntries as p, whatYouMayDoWithTable as q, rightsReason as r, withExtensions as s, tableActions as t, whatYouMayDo as w };
209
+ export { ACTION_CHECKING_ACCESS_REASON as A, type BuiltOnDestination as B, type Capability as C, DEFAULT_ACTION_ORIGIN as D, KERNEL_REASON as K, LEVEL_WORD as L, NOT_ANSWERED_YET as N, type ObjectAction as O, STANDARD_VERBS as S, type TableActionHost as T, type WhatYouMayDo as W, type ObjectActionIcon as a, ACTION_NOT_HERE_REASON as b, ACTION_NO_ACCESS_REASON as c, ACTION_PLATFORM_TABLE_REASON as d, OBJECT_ACTION_GROUPS as e, type ObjectActionDropdownEntry as f, type ObjectActionGroup as g, type ObjectActionGroupDef as h, type ObjectActionSection as i, STRIP_FEATURE_ROWS as j, type StandardVerbId as k, type StripAndRows as l, type TableActionSubject as m, type TableActionsInput as n, dropdownActionIds as o, dropdownActions as p, groupObjectActions as q, levelNeededFor as r, rightsReason as s, tableActions as t, tableLink as u, toDropdownEntries as v, toStripAndRows as w, whatYouMayDo as x, whatYouMayDoWithTable as y, withExtensions as z };
package/dist/pickers.cjs CHANGED
@@ -26,6 +26,7 @@ __export(pickers_exports, {
26
26
  DATA_BINDING_MISSING: () => DATA_BINDING_MISSING,
27
27
  DATA_BINDING_SHAPES: () => DATA_BINDING_SHAPES,
28
28
  DataBindingPicker: () => DataBindingPicker,
29
+ HeldChangeDecision: () => HeldChangeDecision,
29
30
  RecordNameText: () => RecordNameText,
30
31
  RecordPicker: () => RecordPicker,
31
32
  TableNameText: () => TableNameText,
@@ -407,21 +408,35 @@ function BarcodeControl({ value, onChange, disabled, id }) {
407
408
 
408
409
  // src/parity.ts
409
410
  var import_records2 = require("@ai-matrx/records");
411
+ var import_text_case = require("@ai-matrx/kit/text-case");
412
+
413
+ // src/docs.ts
414
+ function isSignatureField(field) {
415
+ return field.type === "text" && (field.format ?? "").toLowerCase() === "signature";
416
+ }
417
+
418
+ // src/parity.ts
419
+ var kernelId = (name) => import_records2.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
420
+ var KERNEL_FILE_TABLE = kernelId("File");
421
+ var KERNEL_PERSON_TABLE = kernelId("Person");
422
+ var PARITY_MADE_OF = Object.fromEntries(
423
+ import_records2.PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
424
+ );
425
+ var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
426
+ function editorKindFor(field) {
427
+ return (0, import_records2.fieldKindFor)(field, KERNEL);
428
+ }
410
429
 
411
430
  // src/names.ts
412
431
  var import_uuid = require("@ai-matrx/kit/uuid");
413
- function humanize(token) {
414
- const words = token.replace(/[_\-.]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
415
- if (words === "") return token;
416
- return words.charAt(0).toUpperCase() + words.slice(1);
417
- }
432
+ var import_text_case2 = require("@ai-matrx/kit/text-case");
418
433
  function fieldName(field) {
419
434
  const label = (field.label ?? "").trim();
420
- if (label !== "" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return humanize(label);
435
+ if (label !== "" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return (0, import_text_case2.humanizeIdentifier)(label);
421
436
  if (label !== "") return label;
422
437
  const name = (field.name ?? "").trim();
423
438
  if (name !== "") return name;
424
- return humanize(field.key);
439
+ return (0, import_text_case2.humanizeIdentifier)(field.key);
425
440
  }
426
441
  function recordName(document2, titleKey, fallback = "Untitled") {
427
442
  const data = document2 ?? {};
@@ -444,23 +459,7 @@ function rowNameIn(table, row, fallback = "Untitled") {
444
459
  return recordName(row?.document, table?.title_field ?? null, fallback);
445
460
  }
446
461
  var WITHHELD_RECORD_LABEL = "A record you have not been given access to";
447
-
448
- // src/docs.ts
449
- function isSignatureField(field) {
450
- return field.type === "text" && (field.format ?? "").toLowerCase() === "signature";
451
- }
452
-
453
- // src/parity.ts
454
- var kernelId = (name) => import_records2.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
455
- var KERNEL_FILE_TABLE = kernelId("File");
456
- var KERNEL_PERSON_TABLE = kernelId("Person");
457
- var PARITY_MADE_OF = Object.fromEntries(
458
- import_records2.PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
459
- );
460
- var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
461
- function editorKindFor(field) {
462
- return (0, import_records2.fieldKindFor)(field, KERNEL);
463
- }
462
+ var UNREAD_RECORD_LABEL = "Couldn't load this name";
464
463
 
465
464
  // src/RelationPicker.tsx
466
465
  var import_react3 = require("react");
@@ -943,7 +942,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
943
942
  const learned = {};
944
943
  for (const id2 of wanted) {
945
944
  const said = result.ok ? result.data[id2] : void 0;
946
- learned[id2] = typeof said === "string" && said !== "" ? said : WITHHELD_RECORD_LABEL;
945
+ learned[id2] = !result.ok ? UNREAD_RECORD_LABEL : typeof said === "string" && said !== "" ? said : WITHHELD_RECORD_LABEL;
947
946
  }
948
947
  setWords((current) => ({ ...current, ...learned }));
949
948
  });
@@ -1538,7 +1537,7 @@ function RecordLabelProvider({ children }) {
1538
1537
  });
1539
1538
  for (const id of wanted) {
1540
1539
  const words = result.ok ? result.data[id] : void 0;
1541
- learned[id] = typeof words === "string" && words !== "" ? words : WITHHELD_RECORD_LABEL;
1540
+ learned[id] = !result.ok ? UNREAD_RECORD_LABEL : typeof words === "string" && words !== "" ? words : WITHHELD_RECORD_LABEL;
1542
1541
  }
1543
1542
  if (result.ok) for (const id of result.archived ?? []) archivedNow[`${field.id}:${id}`] = true;
1544
1543
  })
@@ -4190,6 +4189,7 @@ var import_design_system18 = require("@ai-matrx/design-system");
4190
4189
 
4191
4190
  // src/kindValues.ts
4192
4191
  var import_directives = require("@ai-matrx/content-ir/directives");
4192
+ var import_text_case3 = require("@ai-matrx/kit/text-case");
4193
4193
  var KIND_KEY = "__kind";
4194
4194
  var WHOLE_FENCE = /^\s*```(?:matrx|json)?[ \t]*\r?\n?([\s\S]*?)\r?\n?[ \t]*```\s*$/;
4195
4195
  function asObject(value) {
@@ -4235,8 +4235,7 @@ function kindValueWords(kv) {
4235
4235
  return kindLabel(kv.kind);
4236
4236
  }
4237
4237
  function kindLabel(kind) {
4238
- const words = kind.replace(/^directive_v\d+_/, "").replace(/_v\d+$/, "").replace(/_/g, " ").trim();
4239
- return words ? words.charAt(0).toUpperCase() + words.slice(1) : "Structured value";
4238
+ return (0, import_text_case3.humanizeIdentifier)(kind.replace(/^directive_v\d+_/, "").replace(/_v\d+$/, "")) || "Structured value";
4240
4239
  }
4241
4240
 
4242
4241
  // src/ReferenceChips.tsx
@@ -4484,6 +4483,7 @@ function TableNameText({ dataSource, tableId }) {
4484
4483
  const tables = useTablesAnywhere(dataSource);
4485
4484
  if (!tableId) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "\u2014" });
4486
4485
  if (tables.loading) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "\u2026" });
4486
+ if (tables.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "Couldn't load the table's name" });
4487
4487
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: tables.rows.find((t) => t.table_id === tableId)?.table_name ?? "\u2014" });
4488
4488
  }
4489
4489
  function ColumnValuesText({
@@ -4499,8 +4499,10 @@ function ColumnValuesWords({ tableId, value }) {
4499
4499
  const fields = (0, import_react33.useFields)(tableId);
4500
4500
  const labels = useRecordLabels(fields.data ?? void 0);
4501
4501
  if (fields.loading) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "\u2026" });
4502
+ if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "Couldn't load this table's columns" });
4502
4503
  const parts = [];
4503
4504
  for (const [key, stored] of Object.entries(value)) {
4505
+ if (key.startsWith("_")) continue;
4504
4506
  const field = (fields.data ?? []).find((f) => f.key === key);
4505
4507
  if (parts.length) parts.push(/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: ", " }, `${key}-sep`));
4506
4508
  parts.push(
@@ -4526,7 +4528,53 @@ function RecordNameWords({ tableId, recordId }) {
4526
4528
  const table = (0, import_react33.useTable)(tableId);
4527
4529
  const record = (0, import_react33.useRecord)(recordId);
4528
4530
  if (record.loading || table.loading) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "\u2026" });
4531
+ if (record.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "Couldn't load this record" });
4529
4532
  if (!record.data) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: "A record you cannot open" });
4530
4533
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: recordName(record.data.document, table.data?.title_field ?? null) });
4531
4534
  }
4535
+
4536
+ // src/HeldChangeDecision.tsx
4537
+ var import_react34 = require("react");
4538
+ var import_react35 = require("@ai-matrx/records/react");
4539
+ var import_design_system20 = require("@ai-matrx/design-system");
4540
+ var import_jsx_runtime24 = require("react/jsx-runtime");
4541
+ function HeldChangeDecision({ dataSource, userId, organizationId, approvalId, onDecided }) {
4542
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react35.RecordsProvider, { config: { dataSource, actor: personActor(userId), organizationId }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Decision, { approvalId, onDecided }) });
4543
+ }
4544
+ function Decision({ approvalId, onDecided }) {
4545
+ const client = (0, import_react35.useRecordsClient)();
4546
+ const [may, setMay] = (0, import_react34.useState)(null);
4547
+ const [busy, setBusy] = (0, import_react34.useState)(null);
4548
+ const [error, setError] = (0, import_react34.useState)(null);
4549
+ (0, import_react34.useEffect)(() => {
4550
+ let live = true;
4551
+ void client.workApprovalMayDecide({ approval_id: approvalId }).then((res) => {
4552
+ if (!live) return;
4553
+ if (res.ok) setMay(res.data);
4554
+ else {
4555
+ setMay(false);
4556
+ setError(res.error);
4557
+ }
4558
+ });
4559
+ return () => {
4560
+ live = false;
4561
+ };
4562
+ }, [client, approvalId]);
4563
+ const decide = async (approve) => {
4564
+ setBusy(approve ? "approve" : "refuse");
4565
+ setError(null);
4566
+ const res = await client.workApprovalDecide({ approval_id: approvalId, approve });
4567
+ setBusy(null);
4568
+ if (res.ok) onDecided(approve);
4569
+ else setError(res.error);
4570
+ };
4571
+ if (may === null) return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-xs text-muted-foreground", children: "Checking who can decide\u2026" });
4572
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
4573
+ may ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex gap-2", children: [
4574
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_design_system20.Button, { type: "button", size: "sm", disabled: busy !== null, onClick: () => void decide(true), children: busy === "approve" ? "Approving\u2026" : "Approve" }),
4575
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_design_system20.Button, { type: "button", size: "sm", variant: "outline", disabled: busy !== null, onClick: () => void decide(false), children: busy === "refuse" ? "Refusing\u2026" : "Refuse" })
4576
+ ] }) : !error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-xs text-muted-foreground", children: "An owner or admin of this organization decides this." }),
4577
+ error ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(RefusalLine, { error }) : null
4578
+ ] });
4579
+ }
4532
4580
  //# sourceMappingURL=pickers.cjs.map