@ai-matrx/records-ui 0.110.7 → 0.110.9

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.d.cts CHANGED
@@ -2287,6 +2287,12 @@ interface GridProps {
2287
2287
  * row and hands the term through `searchOverride` (Spaces N-19). Default `true`.
2288
2288
  */
2289
2289
  searchBox?: boolean | undefined;
2290
+ /**
2291
+ * `true`: a row action the person's level refuses (Archive record for a content editor, who may
2292
+ * change rows but not delete them) is left out of the row menu instead of drawn disabled with its
2293
+ * reason. Default `false` (disabled, saying why). Spaces sets it: Notion offers only what works.
2294
+ */
2295
+ hideRefusedRowActions?: boolean | undefined;
2290
2296
  /** The new-row line's words ("New page"). Default "Add row". */
2291
2297
  newRowLabel?: string | undefined;
2292
2298
  /**
@@ -4044,6 +4050,8 @@ interface ViewSwitcherProps {
4044
4050
  * row and hands the term through `searchOverride` (Spaces N-19). Default `true`.
4045
4051
  */
4046
4052
  searchBox?: boolean | undefined;
4053
+ /** `GridProps.hideRefusedRowActions`. */
4054
+ hideRefusedRowActions?: boolean | undefined;
4047
4055
  /** The grid's new-row line words (`GridProps.newRowLabel`). */
4048
4056
  newRowLabel?: string | undefined;
4049
4057
  className?: string | undefined;
@@ -4075,7 +4083,7 @@ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?:
4075
4083
  */
4076
4084
  membership: string;
4077
4085
  };
4078
- declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onCloseRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize: pageSizeAsked, titleField, filter, onClearFilter, chooser, viewId, onOrderPlaced, footer, embedded, sortOverride, searchOverride, onSearchChange, searchBox, newRowLabel, className, }: ViewSwitcherProps): react.JSX.Element;
4086
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onCloseRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize: pageSizeAsked, titleField, filter, onClearFilter, chooser, viewId, onOrderPlaced, footer, embedded, sortOverride, searchOverride, onSearchChange, searchBox, hideRefusedRowActions, newRowLabel, className, }: ViewSwitcherProps): react.JSX.Element;
4079
4087
 
4080
4088
  /** The host app's saved zone (IANA). `null` / junk clears it: the device zone applies. */
4081
4089
  declare function setPersonTimeZone(next: string | null | undefined): void;
package/dist/index.d.ts CHANGED
@@ -2287,6 +2287,12 @@ interface GridProps {
2287
2287
  * row and hands the term through `searchOverride` (Spaces N-19). Default `true`.
2288
2288
  */
2289
2289
  searchBox?: boolean | undefined;
2290
+ /**
2291
+ * `true`: a row action the person's level refuses (Archive record for a content editor, who may
2292
+ * change rows but not delete them) is left out of the row menu instead of drawn disabled with its
2293
+ * reason. Default `false` (disabled, saying why). Spaces sets it: Notion offers only what works.
2294
+ */
2295
+ hideRefusedRowActions?: boolean | undefined;
2290
2296
  /** The new-row line's words ("New page"). Default "Add row". */
2291
2297
  newRowLabel?: string | undefined;
2292
2298
  /**
@@ -4044,6 +4050,8 @@ interface ViewSwitcherProps {
4044
4050
  * row and hands the term through `searchOverride` (Spaces N-19). Default `true`.
4045
4051
  */
4046
4052
  searchBox?: boolean | undefined;
4053
+ /** `GridProps.hideRefusedRowActions`. */
4054
+ hideRefusedRowActions?: boolean | undefined;
4047
4055
  /** The grid's new-row line words (`GridProps.newRowLabel`). */
4048
4056
  newRowLabel?: string | undefined;
4049
4057
  className?: string | undefined;
@@ -4075,7 +4083,7 @@ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?:
4075
4083
  */
4076
4084
  membership: string;
4077
4085
  };
4078
- declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onCloseRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize: pageSizeAsked, titleField, filter, onClearFilter, chooser, viewId, onOrderPlaced, footer, embedded, sortOverride, searchOverride, onSearchChange, searchBox, newRowLabel, className, }: ViewSwitcherProps): react.JSX.Element;
4086
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onCloseRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize: pageSizeAsked, titleField, filter, onClearFilter, chooser, viewId, onOrderPlaced, footer, embedded, sortOverride, searchOverride, onSearchChange, searchBox, hideRefusedRowActions, newRowLabel, className, }: ViewSwitcherProps): react.JSX.Element;
4079
4087
 
4080
4088
  /** The host app's saved zone (IANA). `null` / junk clears it: the device zone applies. */
4081
4089
  declare function setPersonTimeZone(next: string | null | undefined): void;
package/dist/index.js CHANGED
@@ -1092,6 +1092,11 @@ function inColumnWords(error, fields) {
1092
1092
  }
1093
1093
 
1094
1094
  // src/askOnce.ts
1095
+ import { onTableStructureChanged } from "@ai-matrx/records/core";
1096
+ var structureEpoch = 0;
1097
+ onTableStructureChanged(() => {
1098
+ structureEpoch += 1;
1099
+ });
1095
1100
  var asked = /* @__PURE__ */ new WeakMap();
1096
1101
  var byPerson = /* @__PURE__ */ new WeakMap();
1097
1102
  function heldFor(client) {
@@ -1122,7 +1127,7 @@ function askOnce(client, key, ask, freshMs) {
1122
1127
  const held = heldFor(client);
1123
1128
  const now = Date.now();
1124
1129
  const kept = held.get(key);
1125
- if (kept && now - kept.at <= freshMs) return kept.answer;
1130
+ if (kept && kept.epoch === structureEpoch && now - kept.at <= freshMs) return kept.answer;
1126
1131
  const answer = ask().then(
1127
1132
  (result) => {
1128
1133
  if (!result.ok) held.delete(key);
@@ -1133,17 +1138,18 @@ function askOnce(client, key, ask, freshMs) {
1133
1138
  throw thrown;
1134
1139
  }
1135
1140
  );
1136
- held.set(key, { at: now, answer });
1141
+ held.set(key, { at: now, epoch: structureEpoch, answer });
1137
1142
  return answer;
1138
1143
  }
1139
1144
  function forgetAsked(client, prefix) {
1140
1145
  const held = heldFor(client);
1141
1146
  for (const key of [...held.keys()]) if (key.startsWith(prefix)) held.delete(key);
1142
1147
  }
1143
- var FIELD_OPTIONS_FRESH_MS = 2e3;
1148
+ var FIELD_OPTIONS_FRESH_MS = 1e4;
1144
1149
  function fieldOptionsOnce(client, fieldId) {
1145
1150
  return askOnce(client, `field_options:${fieldId}`, () => client.fieldOptions({ field_id: fieldId }), FIELD_OPTIONS_FRESH_MS);
1146
1151
  }
1152
+ var ENRICH_CELLS_FRESH_MS = 5e3;
1147
1153
 
1148
1154
  // src/labels.tsx
1149
1155
  import { createContext as createContext3, useCallback, useContext as useContext3, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
@@ -2238,15 +2244,18 @@ function rowSection(rowId, ctx, rowText) {
2238
2244
  items.push({ id: "row-history", label: "Row history", icon: "history", onSelect: () => a.openRecord?.(rowId) });
2239
2245
  }
2240
2246
  items.push(...ctx.extras?.row?.(rowId) ?? []);
2241
- items.push({
2242
- id: "row-archive",
2243
- label: "Archive record",
2244
- icon: "trash",
2245
- destructive: true,
2246
- separatorBefore: true,
2247
- disabledReason: mayWrite ? void 0 : ctx.whyNotWrite(rowId),
2248
- onSelect: () => a.archiveRow(rowId)
2249
- });
2247
+ const mayRemove = mayWrite && (ctx.mayRemoveRow ? ctx.mayRemoveRow(rowId) : true);
2248
+ if (mayRemove || !ctx.hideRefusedRowActions) {
2249
+ items.push({
2250
+ id: "row-archive",
2251
+ label: "Archive record",
2252
+ icon: "trash",
2253
+ destructive: true,
2254
+ separatorBefore: true,
2255
+ disabledReason: mayRemove ? void 0 : !mayWrite ? ctx.whyNotWrite(rowId) : ctx.whyNotRemove?.(rowId) ?? ctx.whyNotWrite(rowId),
2256
+ onSelect: () => a.archiveRow(rowId)
2257
+ });
2258
+ }
2250
2259
  return { id: "row", title: ctx.extras?.rowTitle?.(rowId) || "Row", items };
2251
2260
  }
2252
2261
  function columnSection(columnId2, columnLabel2, ctx) {
@@ -16011,6 +16020,9 @@ function useMergedGridControls(args) {
16011
16020
  canWrite,
16012
16021
  mayWriteRow,
16013
16022
  whyNotRow,
16023
+ mayRemoveRow,
16024
+ whyNotRemoveRow,
16025
+ hideRefusedRowActions,
16014
16026
  rights,
16015
16027
  editable,
16016
16028
  presentation,
@@ -16268,6 +16280,9 @@ function useMergedGridControls(args) {
16268
16280
  titleField: table?.title_field ?? null,
16269
16281
  mayWriteRow: (rowId) => canWrite && mayWriteRow(rowId),
16270
16282
  whyNotWrite: (rowId) => !editable ? "Editing is off here." : rowId ? whyNotRow(rowId) : rights.why("write"),
16283
+ mayRemoveRow: (rowId) => canWrite && mayRemoveRow(rowId),
16284
+ whyNotRemove: (rowId) => rowId ? whyNotRemoveRow(rowId) : rights.why("remove"),
16285
+ hideRefusedRowActions,
16271
16286
  mayAddRecord: editable && rights.write,
16272
16287
  whyNotAdd: editable ? rights.why("write") : "Editing is off here.",
16273
16288
  mayStructure,
@@ -18562,12 +18577,14 @@ function useEnrichCells(tableId, recordIds) {
18562
18577
  const [cells, setCells] = useState51(/* @__PURE__ */ new Map());
18563
18578
  const [error, setError] = useState51(null);
18564
18579
  const key = (recordIds ?? []).join(",");
18565
- const refresh = useCallback20(async () => {
18580
+ const load = useCallback20(async (fresh) => {
18566
18581
  if (!tableId || !recordIds || recordIds.length === 0) {
18567
18582
  setCells(/* @__PURE__ */ new Map());
18568
18583
  return;
18569
18584
  }
18570
- const answered = await client.enrichCells({ table_id: tableId, record_ids: recordIds });
18585
+ const asking = `enrich_cells:${tableId}:${key}`;
18586
+ if (fresh) forgetAsked(client, asking);
18587
+ const answered = await askOnce(client, asking, () => client.enrichCells({ table_id: tableId, record_ids: recordIds }), ENRICH_CELLS_FRESH_MS);
18571
18588
  if (!answered.ok) {
18572
18589
  setError(answered.error);
18573
18590
  return;
@@ -18577,9 +18594,10 @@ function useEnrichCells(tableId, recordIds) {
18577
18594
  for (const cell2 of answered.data) next.set(`${cell2.record_id}:${cell2.field_key}`, cell2);
18578
18595
  setCells(next);
18579
18596
  }, [client, tableId, key]);
18597
+ const refresh = useCallback20(() => load(true), [load]);
18580
18598
  useEffect42(() => {
18581
- void refresh();
18582
- }, [refresh]);
18599
+ void load(false);
18600
+ }, [load]);
18583
18601
  return { cells, refresh, error };
18584
18602
  }
18585
18603
  var NEVER2 = "Fill it in once and leave it alone";
@@ -23593,7 +23611,8 @@ function GridBody({
23593
23611
  sortOverride,
23594
23612
  searchOverride,
23595
23613
  onSearchChange,
23596
- searchBox = true
23614
+ searchBox = true,
23615
+ hideRefusedRowActions = false
23597
23616
  }) {
23598
23617
  const toolbarSlot = useTableToolbarSlot();
23599
23618
  const tableCard = useMatrxTableCard();
@@ -23832,6 +23851,14 @@ function GridBody({
23832
23851
  (rowId) => askPerRow ? rowRights(rowId)?.write ?? false : rights.write,
23833
23852
  [askPerRow, rowRights, rights.write]
23834
23853
  );
23854
+ const mayRemoveRow = useCallback28(
23855
+ (rowId) => askPerRow ? rowRights(rowId)?.remove ?? false : rights.remove,
23856
+ [askPerRow, rowRights, rights.remove]
23857
+ );
23858
+ const whyNotRemoveRow = useCallback28(
23859
+ (rowId) => askPerRow ? rowRights(rowId)?.why("remove") ?? rights.why("remove") : rights.why("remove"),
23860
+ [askPerRow, rowRights, rights]
23861
+ );
23835
23862
  const whyNotRow = useCallback28(
23836
23863
  (rowId) => askPerRow ? rowRights(rowId)?.why("write") ?? rights.why("write") : rights.why("write"),
23837
23864
  [askPerRow, rowRights, rights]
@@ -24502,6 +24529,9 @@ function GridBody({
24502
24529
  canWrite,
24503
24530
  mayWriteRow,
24504
24531
  whyNotRow,
24532
+ mayRemoveRow,
24533
+ whyNotRemoveRow,
24534
+ hideRefusedRowActions,
24505
24535
  rights,
24506
24536
  editable: Boolean(editable),
24507
24537
  readOnly,
@@ -34123,6 +34153,7 @@ function ViewSwitcher({
34123
34153
  searchOverride,
34124
34154
  onSearchChange,
34125
34155
  searchBox,
34156
+ hideRefusedRowActions,
34126
34157
  newRowLabel,
34127
34158
  className
34128
34159
  }) {
@@ -34168,6 +34199,7 @@ function ViewSwitcher({
34168
34199
  ...searchOverride !== void 0 ? { searchOverride } : {},
34169
34200
  ...onSearchChange ? { onSearchChange } : {},
34170
34201
  ...searchBox === false ? { searchBox } : {},
34202
+ ...hideRefusedRowActions ? { hideRefusedRowActions } : {},
34171
34203
  ...newRowLabel ? { newRowLabel } : {},
34172
34204
  ...filter ? { filter } : {},
34173
34205
  ...onClearFilter ? { onClearFilter } : {},