@proveanything/smartlinks-utils-ui 0.7.4 → 0.7.5

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.
@@ -727,6 +727,14 @@ interface RecordsAdminShellProps<TData = unknown> {
727
727
  csvSchema?: CsvSchema<TData>;
728
728
  classify?: (record: RecordSummary<TData>) => RecordStatus;
729
729
  defaultData?: () => TData;
730
+ /**
731
+ * Optional derivation for the label that represents an in-flight draft
732
+ * in the unsaved-changes tray. Receives the editor's current value and
733
+ * its scope; return a short, human-readable title (or `undefined` to
734
+ * defer to the built-in heuristic). Useful when your record shape nests
735
+ * the title under a custom key the heuristic doesn't know about.
736
+ */
737
+ deriveDraftLabel?: (value: TData, scope: ParsedRef) => string | undefined;
730
738
  /**
731
739
  * Which layouts the rail offers. Default `['list']`. When more than one is
732
740
  * supplied, a switcher appears above the list and the choice persists
@@ -1566,6 +1574,15 @@ interface UseRecordEditorArgs<T> {
1566
1574
  * back a concrete `recordId`.
1567
1575
  */
1568
1576
  createMode?: boolean;
1577
+ /**
1578
+ * Optional host-supplied derivation for the label this editor registers
1579
+ * with the unsaved-changes tray. Receives the in-flight value and the
1580
+ * scope; should return a short, human-readable title. When omitted (or
1581
+ * when it returns `undefined`), the hook falls back to a built-in heuristic
1582
+ * that scans common title-shaped fields (`title`, `display.title`,
1583
+ * `name`, `label`, `heading`, `question`, `slug`).
1584
+ */
1585
+ deriveDraftLabel?: (value: T, scope: ParsedRef) => string | undefined;
1569
1586
  }
1570
1587
  declare function useRecordEditor<T>(args: UseRecordEditorArgs<T>): EditorContext<T>;
1571
1588
 
@@ -692,7 +692,8 @@ function useRecordEditor(args) {
692
692
  onSaveError,
693
693
  reseed = "always",
694
694
  initialFacetRule = null,
695
- createMode = false
695
+ createMode = false,
696
+ deriveDraftLabel
696
697
  } = args;
697
698
  const queryClient = useQueryClient();
698
699
  const draftStore = useDirtyDraftStore();
@@ -857,11 +858,29 @@ function useRecordEditor(args) {
857
858
  const anchors = parsedRefToScope(scope);
858
859
  const saveKind = resolved.recordId && resolved.source === "self" ? "update" : createMode ? "create" : "upsert";
859
860
  const deriveLabel = () => {
861
+ if (deriveDraftLabel) {
862
+ try {
863
+ const custom = deriveDraftLabel(value, scope);
864
+ if (typeof custom === "string" && custom.trim()) return custom.trim();
865
+ } catch {
866
+ }
867
+ }
868
+ const KEYS = ["title", "name", "label", "heading", "question", "slug"];
869
+ const pickString = (obj) => {
870
+ if (!obj || typeof obj !== "object") return void 0;
871
+ for (const key of KEYS) {
872
+ const raw = obj[key];
873
+ if (typeof raw === "string" && raw.trim()) return raw.trim();
874
+ }
875
+ return void 0;
876
+ };
860
877
  const v = value;
878
+ const top = pickString(v);
879
+ if (top) return top;
861
880
  if (v && typeof v === "object") {
862
- for (const key of ["title", "name", "label", "heading", "question"]) {
863
- const raw = v[key];
864
- if (typeof raw === "string" && raw.trim()) return raw.trim();
881
+ for (const wrapper of ["display", "content", "meta", "data"]) {
882
+ const nested = pickString(v[wrapper]);
883
+ if (nested) return nested;
865
884
  }
866
885
  }
867
886
  if (scope.raw?.startsWith("item:")) return "Untitled item";
@@ -4668,6 +4687,11 @@ styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(-
4668
4687
  var TOP_LEVEL_SCOPES = ["collection", "rule", "product"];
4669
4688
  var WARNED_FACET_DEPRECATED = false;
4670
4689
  var DRAFT_ID = "__draft__";
4690
+ var isDraftId = (id) => !!id && (id === DRAFT_ID || id.startsWith("draft:"));
4691
+ var mintDraftItemId = () => {
4692
+ const rand = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
4693
+ return `draft:${rand}`;
4694
+ };
4671
4695
  var productItemToSummary = (p) => {
4672
4696
  const ref = buildRef({ productId: p.id });
4673
4697
  return {
@@ -4749,7 +4773,8 @@ function RecordsAdminShellInner(props) {
4749
4773
  renderItemEmpty,
4750
4774
  collectionRailMode = "siblings",
4751
4775
  // Deep linking
4752
- deepLink
4776
+ deepLink,
4777
+ deriveDraftLabel
4753
4778
  } = props;
4754
4779
  const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
4755
4780
  const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
@@ -5060,7 +5085,7 @@ function RecordsAdminShellInner(props) {
5060
5085
  // fall back to anchor-based `match()` at the collection root and the
5061
5086
  // editor would mount against `null` data — surfacing as the host's
5062
5087
  // "Select a {noun} from the list to edit" placeholder.
5063
- recordId: isCollection && selectedItemId && selectedItemId !== DRAFT_ID ? selectedItemId : selectedRecordId && selectedRecordId !== DRAFT_ID ? selectedRecordId : void 0,
5088
+ recordId: isCollection && selectedItemId && !isDraftId(selectedItemId) ? selectedItemId : selectedRecordId && !isDraftId(selectedRecordId) ? selectedRecordId : void 0,
5064
5089
  supportedScopes: supportedForResolution,
5065
5090
  enabled: !!editingTargetScope
5066
5091
  });
@@ -5115,7 +5140,8 @@ function RecordsAdminShellInner(props) {
5115
5140
  setSelectedBatchId(void 0);
5116
5141
  }
5117
5142
  refetchAll();
5118
- }
5143
+ },
5144
+ deriveDraftLabel
5119
5145
  });
5120
5146
  useUnsavedGuard({
5121
5147
  isDirty: editorCtx.isDirty,
@@ -5386,7 +5412,7 @@ function RecordsAdminShellInner(props) {
5386
5412
  const onItemCreate = useCallback(() => {
5387
5413
  if (!isCollection) return;
5388
5414
  void runWithGuard(() => {
5389
- const id = generateItemId ? generateItemId() : DRAFT_ID;
5415
+ const id = generateItemId ? generateItemId() : mintDraftItemId();
5390
5416
  setSelectedItemId(id);
5391
5417
  onTelemetry?.({ type: "item.create", recordType, scopeRef: baseScopeRef });
5392
5418
  deepLinkState.emit({ recordId: null, scope: baseScopeRef || null }, "record.open");
@@ -5394,7 +5420,7 @@ function RecordsAdminShellInner(props) {
5394
5420
  }, [isCollection, runWithGuard, generateItemId, onTelemetry, recordType, baseScopeRef, deepLinkState, buildItemUrlValue]);
5395
5421
  const onItemDelete = useCallback(async (itemId) => {
5396
5422
  if (!isCollection) return;
5397
- if (itemId === DRAFT_ID) return;
5423
+ if (isDraftId(itemId)) return;
5398
5424
  if (onBeforeDelete) {
5399
5425
  const ok = await onBeforeDelete(editingScope ?? parseRef(""));
5400
5426
  if (!ok) return;
@@ -5475,7 +5501,7 @@ function RecordsAdminShellInner(props) {
5475
5501
  i18n: { previewAs: i18n.previewAs, previewAsDefault: i18n.previewAsDefault }
5476
5502
  }
5477
5503
  ) : null;
5478
- const itemNav = isCollection && selectedItemId && itemPosition ? /* @__PURE__ */ jsx(
5504
+ const itemNav = isCollection && selectedItemId && itemPosition && ruleWizardStep === null ? /* @__PURE__ */ jsx(
5479
5505
  EditorItemNav,
5480
5506
  {
5481
5507
  label: editorHeaderLabel,
@@ -5649,7 +5675,7 @@ function RecordsAdminShellInner(props) {
5649
5675
  }, []);
5650
5676
  const onRuleWizardCreateItem = useCallback(() => {
5651
5677
  if (!isCollection) return;
5652
- const id = generateItemId ? generateItemId() : DRAFT_ID;
5678
+ const id = generateItemId ? generateItemId() : mintDraftItemId();
5653
5679
  setSelectedItemId(id);
5654
5680
  }, [isCollection, generateItemId]);
5655
5681
  const hasGlobalRecord = useMemo(
@@ -5899,7 +5925,7 @@ function RecordsAdminShellInner(props) {
5899
5925
  className: "flex-1 grid border-t overflow-hidden",
5900
5926
  style: { gridTemplateColumns: "minmax(260px, 320px) 1fr", borderColor: "hsl(var(--ra-border))", marginTop: "0.75rem" },
5901
5927
  children: [
5902
- /* @__PURE__ */ jsx("aside", { className: "border-r overflow-hidden flex flex-col", style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" }, children: isCollection && selectedItemId && collectionRailMode === "siblings" ? /* @__PURE__ */ jsx(
5928
+ /* @__PURE__ */ jsx("aside", { className: "border-r overflow-hidden flex flex-col", style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" }, children: isCollection && selectedItemId && collectionRailMode === "siblings" && ruleWizardStep === null ? /* @__PURE__ */ jsx(
5903
5929
  SiblingRail,
5904
5930
  {
5905
5931
  items: collectionItems.items,