@proveanything/smartlinks-utils-ui 0.12.10 → 0.12.11

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.
@@ -842,6 +842,25 @@ interface RecordSlotContext {
842
842
  * to populate this directly.
843
843
  */
844
844
  actions?: RecordAction[];
845
+ /**
846
+ * Facet keys carried by the row's enclosing group header, when the rail
847
+ * is grouped by facet identity (Rules tab default). The default row
848
+ * renderer uses this to drop the facet prefix from chips whose facet is
849
+ * already named in the group header (so under "BREAD TYPE" you see
850
+ * "Slice" instead of "Bread Type: Slice"). Empty when ungrouped.
851
+ */
852
+ groupFacetKeys?: readonly string[];
853
+ /**
854
+ * Number of archived (history) records collapsed under this row's
855
+ * lifecycle slot. The default row renderer surfaces this as an inline
856
+ * pill the admin can click to expand/collapse the archived rows
857
+ * underneath. Undefined when there is no history block.
858
+ */
859
+ historyCount?: number;
860
+ /** Whether the history block for this row is currently expanded. */
861
+ historyExpanded?: boolean;
862
+ /** Toggle the history block. Only present when `historyCount > 0`. */
863
+ onToggleHistory?: () => void;
845
864
  }
846
865
  /**
847
866
  * Declarative column definition for the built-in default item table.
@@ -1110,6 +1129,12 @@ interface RailConfig<TData = unknown> {
1110
1129
  key: string;
1111
1130
  label: string;
1112
1131
  icon?: ReactNode;
1132
+ /**
1133
+ * Optional facet keys this group represents (Rules tab default
1134
+ * grouping populates this). Forwarded to row ctx so the default row
1135
+ * renderer can drop redundant facet prefixes from rule chips.
1136
+ */
1137
+ facetKeys?: readonly string[];
1113
1138
  /**
1114
1139
  * Whether this bucket should be expanded by default (records-driven
1115
1140
  * accordion mode). Last-write-wins per key. Has no effect in the
@@ -15,6 +15,7 @@ import { createPortal } from 'react-dom';
15
15
  // src/components/RecordsAdmin/data/recordCache.ts
16
16
  var RECORD_LIST_QK = ["records-admin", "list"];
17
17
  var COLLECTION_ITEMS_QK = ["records-admin", "collection-items"];
18
+ var SCOPE_COUNTS_QK = ["records-admin", "scope-counts"];
18
19
  var matchesCtx = (queryKey, prefix, ctx) => {
19
20
  if (queryKey.length < prefix.length + 3) return false;
20
21
  for (let i = 0; i < prefix.length; i += 1) {
@@ -87,6 +88,54 @@ function patchRecordIntoCaches(queryClient, ctx, record) {
87
88
  return replaceOrAppend(prev, record);
88
89
  });
89
90
  }
91
+ patchRecordIntoScopeCounts(queryClient, ctx, record);
92
+ }
93
+ var matchesScopeCountsCtx = (queryKey, ctx) => {
94
+ if (queryKey.length < 5) return false;
95
+ if (queryKey[0] !== SCOPE_COUNTS_QK[0]) return false;
96
+ if (queryKey[1] !== SCOPE_COUNTS_QK[1]) return false;
97
+ if (queryKey[2] !== ctx.collectionId) return false;
98
+ if (queryKey[3] !== ctx.appId) return false;
99
+ const slot = queryKey[4];
100
+ const expected = ctx.recordType ?? null;
101
+ if (slot !== expected && !(slot == null && expected == null)) return false;
102
+ return true;
103
+ };
104
+ function patchRecordIntoScopeCounts(queryClient, ctx, record) {
105
+ if (!record || !record.id) return;
106
+ const all = queryClient.getQueriesData({
107
+ queryKey: SCOPE_COUNTS_QK
108
+ });
109
+ for (const [key, cache] of all) {
110
+ if (!cache || !Array.isArray(cache.records)) continue;
111
+ if (!matchesScopeCountsCtx(key, ctx)) continue;
112
+ queryClient.setQueryData(key, (prev) => {
113
+ if (!prev || !Array.isArray(prev.records)) return prev;
114
+ const idx = prev.records.findIndex((r) => r.id === record.id);
115
+ if (idx === -1) {
116
+ return { ...prev, records: [...prev.records, record] };
117
+ }
118
+ const next = [...prev.records];
119
+ next[idx] = record;
120
+ return { ...prev, records: next };
121
+ });
122
+ }
123
+ }
124
+ function removeRecordFromScopeCounts(queryClient, ctx, recordId) {
125
+ if (!recordId) return;
126
+ const all = queryClient.getQueriesData({
127
+ queryKey: SCOPE_COUNTS_QK
128
+ });
129
+ for (const [key, cache] of all) {
130
+ if (!cache || !Array.isArray(cache.records)) continue;
131
+ if (!matchesScopeCountsCtx(key, ctx)) continue;
132
+ queryClient.setQueryData(key, (prev) => {
133
+ if (!prev || !Array.isArray(prev.records)) return prev;
134
+ const next = prev.records.filter((r) => r.id !== recordId);
135
+ if (next.length === prev.records.length) return prev;
136
+ return { ...prev, records: next };
137
+ });
138
+ }
90
139
  }
91
140
  function removeRecordFromCaches(queryClient, ctx, recordId) {
92
141
  if (!recordId) return;
@@ -104,6 +153,7 @@ function removeRecordFromCaches(queryClient, ctx, recordId) {
104
153
  return next ?? prev;
105
154
  });
106
155
  }
156
+ removeRecordFromScopeCounts(queryClient, ctx, recordId);
107
157
  }
108
158
  var DEFAULT_ICONS = {
109
159
  scope: {
@@ -4097,13 +4147,30 @@ var RuleLabelLookupProvider = ({
4097
4147
  return /* @__PURE__ */ jsx(RuleLabelLookupContext.Provider, { value: v, children });
4098
4148
  };
4099
4149
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4100
- const { selected, onSelect, isDirty, hasError, onCopy, onDuplicate, onCopyAndNewRule, actions } = ctx;
4150
+ const {
4151
+ selected,
4152
+ onSelect,
4153
+ isDirty,
4154
+ hasError,
4155
+ onCopy,
4156
+ onDuplicate,
4157
+ onCopyAndNewRule,
4158
+ actions,
4159
+ historyCount,
4160
+ historyExpanded,
4161
+ onToggleHistory
4162
+ } = ctx;
4101
4163
  const ruleLabelLookup = useRuleLabelLookup();
4102
4164
  const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
4103
4165
  const tone = resolveTone(void 0, record.status);
4104
4166
  const ruleSummary = formatFacetRule(record.facetRule, ruleLabelLookup);
4105
4167
  const ruleClauses = summarizeFacetRule(record.facetRule, ruleLabelLookup);
4106
4168
  const isRuleRecord = ruleClauses.length > 0;
4169
+ const groupKeys = ctx.groupFacetKeys;
4170
+ const singleGroupFacet = groupKeys && groupKeys.length === 1 ? groupKeys[0] : null;
4171
+ const displayClauses = singleGroupFacet ? ruleClauses.map(
4172
+ (c) => c.facetKey === singleGroupFacet ? { ...c, label: c.values.length <= 3 ? c.values.join(", ") : `${c.values.slice(0, 2).join(", ")} +${c.values.length - 2}` } : c
4173
+ ) : ruleClauses;
4107
4174
  const i18n = ctx.i18n ?? DEFAULT_I18N;
4108
4175
  const subtitle = record.subtitle ?? (isRuleRecord ? null : ruleSummary) ?? (tone === "missing" ? i18n.subtitleEmpty : tone === "own" ? i18n.subtitleConfigured : i18n.subtitleInherited);
4109
4176
  return /* @__PURE__ */ jsxs(
@@ -4111,7 +4178,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4111
4178
  {
4112
4179
  "data-selected": selected ? "true" : "false",
4113
4180
  "data-tone": tone,
4114
- className: cn("ra-row", compact && "ra-row-compact"),
4181
+ className: cn("ra-row", compact && "ra-row-compact", isRuleRecord && !compact && "ra-row-rule"),
4115
4182
  style: compact ? { paddingTop: "0.4rem", paddingBottom: "0.4rem" } : void 0,
4116
4183
  children: [
4117
4184
  /* @__PURE__ */ jsxs("button", { type: "button", onClick: onSelect, className: "ra-row-hit", children: [
@@ -4125,7 +4192,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4125
4192
  }
4126
4193
  ) }),
4127
4194
  /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
4128
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
4195
+ !(isRuleRecord && !compact) && /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
4129
4196
  !compact && isRuleRecord && /* @__PURE__ */ jsxs(
4130
4197
  "div",
4131
4198
  {
@@ -4133,10 +4200,10 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4133
4200
  title: ruleSummary ?? void 0,
4134
4201
  "aria-label": ruleSummary ?? void 0,
4135
4202
  children: [
4136
- ruleClauses.slice(0, 4).map((clause, i) => /* @__PURE__ */ jsx("span", { className: "ra-rule-chip", children: clause.label }, `${clause.facetKey}-${i}`)),
4137
- ruleClauses.length > 4 && /* @__PURE__ */ jsxs("span", { className: "ra-rule-chip ra-rule-chip-more", children: [
4203
+ displayClauses.slice(0, 4).map((clause, i) => /* @__PURE__ */ jsx("span", { className: "ra-rule-chip", children: clause.label }, `${clause.facetKey}-${i}`)),
4204
+ displayClauses.length > 4 && /* @__PURE__ */ jsxs("span", { className: "ra-rule-chip ra-rule-chip-more", children: [
4138
4205
  "+",
4139
- ruleClauses.length - 4
4206
+ displayClauses.length - 4
4140
4207
  ] })
4141
4208
  ]
4142
4209
  }
@@ -4171,6 +4238,25 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4171
4238
  }
4172
4239
  )
4173
4240
  ] }),
4241
+ historyCount && historyCount > 0 && onToggleHistory ? /* @__PURE__ */ jsxs(
4242
+ "button",
4243
+ {
4244
+ type: "button",
4245
+ className: "ra-row-history-pill",
4246
+ "data-expanded": historyExpanded ? "true" : "false",
4247
+ onClick: (e) => {
4248
+ e.stopPropagation();
4249
+ onToggleHistory();
4250
+ },
4251
+ "aria-expanded": historyExpanded,
4252
+ "aria-label": historyExpanded ? `Hide ${historyCount} archived` : `Show ${historyCount} archived`,
4253
+ title: historyExpanded ? `Hide ${historyCount} archived` : `Show ${historyCount} archived`,
4254
+ children: [
4255
+ /* @__PURE__ */ jsx("span", { className: "ra-row-history-pill-count", children: historyCount }),
4256
+ /* @__PURE__ */ jsx("span", { className: "ra-row-history-pill-label", children: "archived" })
4257
+ ]
4258
+ }
4259
+ ) : null,
4174
4260
  (onCopy || onDuplicate || onCopyAndNewRule || actions && actions.length > 0) && /* @__PURE__ */ jsx(
4175
4261
  RowContextMenu,
4176
4262
  {
@@ -4232,7 +4318,7 @@ var RecordList = ({
4232
4318
  buttons[nextIdx].scrollIntoView({ block: "nearest" });
4233
4319
  }
4234
4320
  }, []);
4235
- const buildCtx = (item) => {
4321
+ const buildCtx = (item, groupFacetKeys) => {
4236
4322
  const cb = rowClipboard ? rowClipboard(item) : null;
4237
4323
  const extraActions = rowActions ? rowActions(item) ?? void 0 : void 0;
4238
4324
  const itemAnchorKey = anchorKey(item.scope);
@@ -4249,6 +4335,7 @@ var RecordList = ({
4249
4335
  isDirty: dirtyIdMatch || dirtyAnchorMatch || idInStore || anchorInStore,
4250
4336
  hasError: errorInStore,
4251
4337
  i18n,
4338
+ groupFacetKeys,
4252
4339
  ...cb ?? {},
4253
4340
  actions: extraActions && extraActions.length > 0 ? extraActions : void 0
4254
4341
  };
@@ -4276,43 +4363,30 @@ var RecordList = ({
4276
4363
  return next;
4277
4364
  });
4278
4365
  }, []);
4279
- const renderItems = (rows) => {
4366
+ const renderItems = (rows, groupFacetKeys) => {
4280
4367
  const compact = presentation === "compact";
4281
4368
  return /* @__PURE__ */ jsx("ul", { children: rows.map((item, idx) => {
4282
- const ctx = buildCtx(item);
4369
+ const ctx = buildCtx(item, groupFacetKeys);
4283
4370
  const key = item.id ?? (anchorKey(item.scope) || `pos:${idx}`);
4284
4371
  const sKey = historyBySlot ? slotKey(item) : null;
4285
4372
  const history = sKey ? historyBySlot.get(sKey) : void 0;
4286
4373
  const expanded = sKey ? expandedSlots.has(sKey) : false;
4287
- const showLabel = i18n?.historyDisclosureShow ?? "Show {n} archived";
4288
- const hideLabel = i18n?.historyDisclosureHide ?? "Hide {n} archived";
4289
- const label = (expanded ? hideLabel : showLabel).replace("{n}", String(history?.length ?? 0));
4374
+ const historyCount = history?.length ?? 0;
4375
+ const ctxWithHistory = historyCount > 0 && sKey ? { ...ctx, historyCount, historyExpanded: expanded, onToggleHistory: () => toggleSlot(sKey) } : ctx;
4290
4376
  return /* @__PURE__ */ jsxs("li", { children: [
4291
- renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }),
4292
- history && history.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "ra-history-block", children: [
4293
- expanded ? /* @__PURE__ */ jsx("ul", { className: "ra-history-rows", "aria-label": "Archived records", children: history.map((h, hIdx) => {
4294
- const hCtx = buildCtx(h);
4295
- const hKey = h.id ?? `hist:${idx}:${hIdx}`;
4296
- const badged = {
4297
- ...h,
4298
- badges: [
4299
- ...h.badges ?? [],
4300
- { label: h.lifecycleStatus ?? "archived", tone: "warning" }
4301
- ]
4302
- };
4303
- return /* @__PURE__ */ jsx("li", { className: "ra-history-row", "data-history": "true", children: renderListRow ? renderListRow(badged, hCtx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: badged, ctx: hCtx, compact }) }, hKey);
4304
- }) }) : null,
4305
- /* @__PURE__ */ jsx(
4306
- "button",
4307
- {
4308
- type: "button",
4309
- className: "ra-history-disclosure",
4310
- onClick: () => sKey && toggleSlot(sKey),
4311
- "aria-expanded": expanded,
4312
- children: label
4313
- }
4314
- )
4315
- ] }) : null
4377
+ renderListRow ? renderListRow(item, ctxWithHistory) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx: ctxWithHistory, compact }),
4378
+ history && history.length > 0 ? /* @__PURE__ */ jsx("div", { className: "ra-history-block", children: expanded ? /* @__PURE__ */ jsx("ul", { className: "ra-history-rows", "aria-label": "Archived records", children: history.map((h, hIdx) => {
4379
+ const hCtx = buildCtx(h, groupFacetKeys);
4380
+ const hKey = h.id ?? `hist:${idx}:${hIdx}`;
4381
+ const badged = {
4382
+ ...h,
4383
+ badges: [
4384
+ ...h.badges ?? [],
4385
+ { label: h.lifecycleStatus ?? "archived", tone: "warning" }
4386
+ ]
4387
+ };
4388
+ return /* @__PURE__ */ jsx("li", { className: "ra-history-row", "data-history": "true", children: renderListRow ? renderListRow(badged, hCtx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: badged, ctx: hCtx, compact }) }, hKey);
4389
+ }) }) : null }) : null
4316
4390
  ] }, key);
4317
4391
  }) });
4318
4392
  };
@@ -4385,7 +4459,7 @@ var GroupedList = ({
4385
4459
  }
4386
4460
  ) : null
4387
4461
  ] }),
4388
- /* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items) })
4462
+ /* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items, g.facetKeys) })
4389
4463
  ] }, g.key);
4390
4464
  }) });
4391
4465
  };
@@ -8964,8 +9038,12 @@ var EditorPoolBody = ({
8964
9038
  }) => {
8965
9039
  const ctx = useEditorSlotContext(editorId);
8966
9040
  if (!ctx) return null;
8967
- return renderEditor(ctx);
9041
+ return /* @__PURE__ */ jsx(CallbackRenderer, { render: renderEditor, args: ctx });
8968
9042
  };
9043
+ var CallbackRenderer = ({
9044
+ render,
9045
+ args
9046
+ }) => /* @__PURE__ */ jsx(Fragment, { children: render(args) });
8969
9047
  var TOP_LEVEL_SCOPES = ["collection", "rule", "product"];
8970
9048
  var OPT_IN_TOP_LEVEL_SCOPES = ["all"];
8971
9049
  var DRAFT_ID3 = "__draft__";
@@ -9171,7 +9249,10 @@ function RecordsAdminShellInner(props) {
9171
9249
  labels: actionLabels,
9172
9250
  icons: actionIcons
9173
9251
  } = actions ?? {};
9174
- const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
9252
+ const i18n = useMemo(
9253
+ () => ({ ...DEFAULT_I18N, ...i18nOverride ?? {} }),
9254
+ [i18nOverride]
9255
+ );
9175
9256
  const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
9176
9257
  const deepLinkState = useDeepLinkState(deepLink);
9177
9258
  const multiOpenWarnedRef = useRef(false);
@@ -9981,7 +10062,13 @@ function RecordsAdminShellInner(props) {
9981
10062
  ] });
9982
10063
  }
9983
10064
  const previewAnchorRef = previewReopenAnchorRef;
9984
- const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
10065
+ const previewBody = renderPreview && effectivePreviewScope ? /* @__PURE__ */ jsx(
10066
+ CallbackRenderer,
10067
+ {
10068
+ render: renderPreview,
10069
+ args: { resolved: editorCtx.value, previewScope: effectivePreviewScope }
10070
+ }
10071
+ ) : null;
9985
10072
  const scopePicker = previewScopePicker && effectivePreviewScope ? /* @__PURE__ */ jsx(
9986
10073
  PreviewScopePicker,
9987
10074
  {
@@ -10139,7 +10226,13 @@ function RecordsAdminShellInner(props) {
10139
10226
  renderEditor
10140
10227
  }
10141
10228
  ),
10142
- fallback: renderEditor(editorCtx)
10229
+ fallback: /* @__PURE__ */ jsx(
10230
+ CallbackRenderer,
10231
+ {
10232
+ render: renderEditor,
10233
+ args: editorCtx
10234
+ }
10235
+ )
10143
10236
  }
10144
10237
  )
10145
10238
  }
@@ -10327,7 +10420,7 @@ function RecordsAdminShellInner(props) {
10327
10420
  if (keys.length === 0) return null;
10328
10421
  const key = `facets:${keys.join("|")}`;
10329
10422
  const label2 = keys.map((k) => ruleLabelLookup?.facetLabel?.(k) ?? k).join(" \xB7 ");
10330
- return { key, label: label2 };
10423
+ return { key, label: label2, facetKeys: keys };
10331
10424
  };
10332
10425
  }, [groupBy, isRuleTab, isAllTab, isCollection, ruleLabelLookup]);
10333
10426
  const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
@@ -11175,7 +11268,7 @@ function RecordsAdminShellInner(props) {
11175
11268
  isGlobalTab && !isCollection && !hasSingletonConflicts ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
11176
11269
  leftLoading && /* @__PURE__ */ jsx(LoadingState, {}),
11177
11270
  !leftLoading && leftError && /* @__PURE__ */ jsx(ErrorState, { error: leftError }),
11178
- !leftLoading && !leftError && decoratedLeftItems.length === 0 && (renderEmptyState ? renderEmptyState({ scope: activeScope }) : /* @__PURE__ */ jsx(
11271
+ !leftLoading && !leftError && decoratedLeftItems.length === 0 && (renderEmptyState ? /* @__PURE__ */ jsx(CallbackRenderer, { render: renderEmptyState, args: { scope: activeScope } }) : /* @__PURE__ */ jsx(
11179
11272
  EmptyState,
11180
11273
  {
11181
11274
  icon: search ? icons.empty.search : icons.empty.default,