@proveanything/smartlinks-utils-ui 0.10.0 → 0.10.3

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.
@@ -2055,6 +2055,15 @@ declare const useProductBrowse: (args: UseProductBrowseArgs) => {
2055
2055
  refetch: () => Promise<void>;
2056
2056
  };
2057
2057
 
2058
+ interface FacetValueShape {
2059
+ key?: string;
2060
+ name?: string;
2061
+ }
2062
+ interface FacetShape {
2063
+ key?: string;
2064
+ name?: string;
2065
+ values?: FacetValueShape[];
2066
+ }
2058
2067
  interface UseFacetBrowseArgs {
2059
2068
  SL: SmartLinksSDK;
2060
2069
  collectionId: string;
@@ -2075,6 +2084,12 @@ declare const useFacetBrowse: ({ SL, collectionId, existing, search, filter, ena
2075
2084
  isLoading: boolean;
2076
2085
  error: Error | null;
2077
2086
  refetch: () => void;
2087
+ /** Raw canonical facet vocabulary as returned by the SDK. Use this
2088
+ * (NOT `items`) when you need the authoritative list of facets and
2089
+ * their values — `items` is the record-merged list and may include
2090
+ * rows whose scope's `facetId` doesn't match a canonical key, which
2091
+ * would otherwise show up as duplicate facet groups. */
2092
+ vocabulary: FacetShape[];
2078
2093
  };
2079
2094
 
2080
2095
  interface CollectedRecord<T = unknown> {
@@ -2427,8 +2442,17 @@ interface Props<T> {
2427
2442
  selectedItemId?: string;
2428
2443
  isLoading: boolean;
2429
2444
  error: Error | null;
2430
- /** Retained for API stability back nav now lives in EditorItemNav. */
2445
+ /** Back to the items list (mirrors the editor-header "Back to list"). */
2431
2446
  onBack?: () => void;
2447
+ /**
2448
+ * Optional context label rendered next to the back arrow. Two parts:
2449
+ * - `kind` — short noun ("Rule", "Product", "Global", "Filtered")
2450
+ * - `summary` — the specific value when relevant (rule summary, product
2451
+ * name, …); omitted for kinds that need no qualifier.
2452
+ * The shell composes both from the active scope + selection.
2453
+ */
2454
+ contextKind?: string;
2455
+ contextSummary?: string | null;
2432
2456
  onSelect: (itemId: string) => void;
2433
2457
  /**
2434
2458
  * Set of keys (recordIds + scope refs) currently dirty in the shell-level
@@ -2439,9 +2463,9 @@ interface Props<T> {
2439
2463
  dirtyKeys?: ReadonlySet<string>;
2440
2464
  /** Subset of `dirtyKeys` whose last save attempt failed. */
2441
2465
  errorKeys?: ReadonlySet<string>;
2442
- i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody'>;
2466
+ i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody' | 'backToList'>;
2443
2467
  }
2444
- declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onSelect, dirtyKeys, errorKeys, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
2468
+ declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, contextKind, contextSummary, dirtyKeys, errorKeys, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
2445
2469
 
2446
2470
  interface ClipboardEntry<T = unknown> {
2447
2471
  value: T;
@@ -817,7 +817,13 @@ var useFacetBrowse = ({
817
817
  counts,
818
818
  isLoading: query.isLoading,
819
819
  error: query.error ?? null,
820
- refetch
820
+ refetch,
821
+ /** Raw canonical facet vocabulary as returned by the SDK. Use this
822
+ * (NOT `items`) when you need the authoritative list of facets and
823
+ * their values — `items` is the record-merged list and may include
824
+ * rows whose scope's `facetId` doesn't match a canonical key, which
825
+ * would otherwise show up as duplicate facet groups. */
826
+ vocabulary: query.data ?? []
821
827
  };
822
828
  };
823
829
  var QK2 = ["records-admin", "product-browse"];
@@ -3697,6 +3703,8 @@ var FacetList = RecordList;
3697
3703
  var VariantList = RecordList;
3698
3704
  var BatchList = RecordList;
3699
3705
  var COLLAPSED_FACET_CAP = 6;
3706
+ var COLLAPSED_VALUE_CAP = 12;
3707
+ var VALUE_SEARCH_THRESHOLD = 12;
3700
3708
  function FacetBrowseFilter({
3701
3709
  facets,
3702
3710
  value,
@@ -3710,12 +3718,33 @@ function FacetBrowseFilter({
3710
3718
  );
3711
3719
  const [openKey, setOpenKey] = useState(value?.facetKey ?? firstUsable ?? null);
3712
3720
  const [allExpanded, setAllExpanded] = useState(false);
3721
+ const [valuesExpanded, setValuesExpanded] = useState({});
3722
+ const [valueSearch, setValueSearch] = useState({});
3713
3723
  const effectiveOpen = value?.facetKey ?? openKey ?? firstUsable ?? null;
3714
3724
  if (isLoading) {
3715
3725
  return /* @__PURE__ */ jsx("div", { className: "ra-rule-filters", "aria-busy": "true", children: /* @__PURE__ */ jsx("div", { className: "ra-rule-filters-row", children: /* @__PURE__ */ jsx("span", { className: "ra-rule-filter-chip", "data-active": "false", children: "Loading facets\u2026" }) }) });
3716
3726
  }
3717
3727
  if (!facets.length) return null;
3718
3728
  const openFacet = facets.find((f) => f.key === effectiveOpen) ?? null;
3729
+ const openValuesExpanded = openFacet ? !!valuesExpanded[openFacet.key] : false;
3730
+ const openValueSearch = openFacet ? valueSearch[openFacet.key] ?? "" : "";
3731
+ const openValuesFiltered = useMemo(() => {
3732
+ if (!openFacet) return [];
3733
+ const q = openValueSearch.trim().toLowerCase();
3734
+ if (!q) return openFacet.values;
3735
+ return openFacet.values.filter(
3736
+ (v) => `${v.label ?? ""} ${v.key}`.toLowerCase().includes(q)
3737
+ );
3738
+ }, [openFacet, openValueSearch]);
3739
+ const valueOverflow = openFacet ? openValuesFiltered.length - COLLAPSED_VALUE_CAP : 0;
3740
+ const visibleValues = openFacet ? openValuesExpanded || valueOverflow <= 0 ? openValuesFiltered : (() => {
3741
+ const head = openValuesFiltered.slice(0, COLLAPSED_VALUE_CAP);
3742
+ const activeVal = value && value.facetKey === openFacet.key ? openFacet.values.find((v) => v.key === value.facetValue) : null;
3743
+ if (activeVal && !head.includes(activeVal)) {
3744
+ return [...head.slice(0, COLLAPSED_VALUE_CAP - 1), activeVal];
3745
+ }
3746
+ return head;
3747
+ })() : [];
3719
3748
  const overflow = facets.length - COLLAPSED_FACET_CAP;
3720
3749
  let visibleFacets = facets;
3721
3750
  if (!allExpanded && overflow > 0) {
@@ -3764,30 +3793,57 @@ function FacetBrowseFilter({
3764
3793
  }
3765
3794
  )
3766
3795
  ] }),
3767
- openFacet && openFacet.values.length > 0 && /* @__PURE__ */ jsx(
3796
+ openFacet && openFacet.values.length > 0 && /* @__PURE__ */ jsxs(
3768
3797
  "div",
3769
3798
  {
3770
3799
  className: "ra-rule-filters-row",
3771
3800
  role: "group",
3772
3801
  "aria-label": `Pick a ${openFacet.label ?? openFacet.key} value`,
3773
- style: { paddingLeft: "0.25rem" },
3774
- children: openFacet.values.map((v) => {
3775
- const active = value?.facetKey === openFacet.key && value?.facetValue === v.key;
3776
- return /* @__PURE__ */ jsx(
3802
+ style: { paddingLeft: "0.25rem", maxHeight: "8.5rem", overflowY: "auto" },
3803
+ children: [
3804
+ openFacet.values.length > VALUE_SEARCH_THRESHOLD && /* @__PURE__ */ jsx(
3805
+ "input",
3806
+ {
3807
+ type: "search",
3808
+ value: openValueSearch,
3809
+ onChange: (e) => setValueSearch((s) => ({ ...s, [openFacet.key]: e.target.value })),
3810
+ placeholder: `Search ${openFacet.label ?? openFacet.key}\u2026`,
3811
+ "aria-label": `Search ${openFacet.label ?? openFacet.key} values`,
3812
+ className: "ra-rule-filter-chip",
3813
+ style: { minWidth: "8rem", writingMode: "horizontal-tb" }
3814
+ }
3815
+ ),
3816
+ visibleValues.map((v) => {
3817
+ const active = value?.facetKey === openFacet.key && value?.facetValue === v.key;
3818
+ return /* @__PURE__ */ jsx(
3819
+ "button",
3820
+ {
3821
+ type: "button",
3822
+ className: "ra-rule-filter-chip",
3823
+ "data-tone": "complexity",
3824
+ "data-active": active ? "true" : "false",
3825
+ "aria-pressed": active,
3826
+ onClick: () => onChange(active ? null : { facetKey: openFacet.key, facetValue: v.key }),
3827
+ title: `Filter to ${openFacet.label ?? openFacet.key} = ${v.label ?? v.key}`,
3828
+ children: /* @__PURE__ */ jsx("span", { className: "ra-rule-filter-chip-label", children: v.label ?? v.key })
3829
+ },
3830
+ v.key
3831
+ );
3832
+ }),
3833
+ valueOverflow > 0 && /* @__PURE__ */ jsx(
3777
3834
  "button",
3778
3835
  {
3779
3836
  type: "button",
3837
+ onClick: () => setValuesExpanded((s) => ({ ...s, [openFacet.key]: !openValuesExpanded })),
3780
3838
  className: "ra-rule-filter-chip",
3781
- "data-tone": "complexity",
3782
- "data-active": active ? "true" : "false",
3783
- "aria-pressed": active,
3784
- onClick: () => onChange(active ? null : { facetKey: openFacet.key, facetValue: v.key }),
3785
- title: `Filter to ${openFacet.label ?? openFacet.key} = ${v.label ?? v.key}`,
3786
- children: /* @__PURE__ */ jsx("span", { className: "ra-rule-filter-chip-label", children: v.label ?? v.key })
3787
- },
3788
- v.key
3789
- );
3790
- })
3839
+ "data-active": "false",
3840
+ "aria-expanded": openValuesExpanded,
3841
+ title: openValuesExpanded ? "Show fewer values" : `Show all ${openValuesFiltered.length} values`,
3842
+ children: /* @__PURE__ */ jsx("span", { className: "ra-rule-filter-chip-label", children: openValuesExpanded ? "Show fewer" : `Show all (${openValuesFiltered.length})` })
3843
+ }
3844
+ ),
3845
+ openValuesFiltered.length === 0 && /* @__PURE__ */ jsx("span", { className: "ra-rule-filter-clear", style: { cursor: "default", textDecoration: "none" }, children: "No values match." })
3846
+ ]
3791
3847
  }
3792
3848
  ),
3793
3849
  value && /* @__PURE__ */ jsx(
@@ -5950,67 +6006,92 @@ function SiblingRail({
5950
6006
  selectedItemId,
5951
6007
  isLoading,
5952
6008
  error,
6009
+ onBack,
5953
6010
  onSelect,
6011
+ contextKind,
6012
+ contextSummary,
5954
6013
  dirtyKeys,
5955
6014
  errorKeys,
5956
6015
  i18n
5957
6016
  }) {
5958
6017
  const ruleLabelLookup = useRuleLabelLookup();
5959
- return /* @__PURE__ */ jsx("div", { className: "ra-sibling-rail", children: /* @__PURE__ */ jsxs("div", { className: "ra-sibling-body", children: [
5960
- isLoading && /* @__PURE__ */ jsx(LoadingState, {}),
5961
- !isLoading && error && /* @__PURE__ */ jsx(ErrorState, { error }),
5962
- !isLoading && !error && items.length === 0 && /* @__PURE__ */ jsx(EmptyState, { title: i18n.noItemsTitle, body: i18n.noItemsBody }),
5963
- !isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item, idx) => {
5964
- const id = item.itemId ?? "";
5965
- const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
5966
- const akey = anchorKey(item.scope);
5967
- const selected = selectedItemId === id;
5968
- const isDirty = !!(id && dirtyKeys?.has(id) || akey && dirtyKeys?.has(akey));
5969
- const hasError = !!(id && errorKeys?.has(id) || akey && errorKeys?.has(akey));
5970
- const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
5971
- const isTargeted = ruleClauses.length > 0;
5972
- const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
5973
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6018
+ return /* @__PURE__ */ jsxs("div", { className: "ra-sibling-rail", children: [
6019
+ (onBack || contextKind) && /* @__PURE__ */ jsxs("div", { className: "ra-sibling-context", children: [
6020
+ onBack && /* @__PURE__ */ jsx(
5974
6021
  "button",
5975
6022
  {
5976
6023
  type: "button",
5977
- onClick: () => onSelect(id),
5978
- className: "ra-row",
5979
- "data-selected": selected,
5980
- children: [
5981
- /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
5982
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
5983
- item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
5984
- ] }),
5985
- isTargeted && /* @__PURE__ */ jsx(
5986
- "span",
5987
- {
5988
- className: "ra-row-rule-pip",
5989
- title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
5990
- "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
5991
- children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
5992
- }
5993
- ),
5994
- hasError ? /* @__PURE__ */ jsx(
5995
- "span",
5996
- {
5997
- className: "ra-error-pip",
5998
- title: "Save failed",
5999
- "aria-label": "Save failed"
6000
- }
6001
- ) : isDirty ? /* @__PURE__ */ jsx(
6002
- "span",
6003
- {
6004
- className: "ra-dirty-pip",
6005
- title: "Unsaved changes",
6006
- "aria-label": "Unsaved changes"
6007
- }
6008
- ) : null
6009
- ]
6024
+ onClick: onBack,
6025
+ className: "ra-sibling-context-back",
6026
+ "aria-label": i18n.backToList,
6027
+ title: i18n.backToList,
6028
+ children: /* @__PURE__ */ jsx(ArrowLeft, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
6010
6029
  }
6011
- ) }, key);
6012
- }) })
6013
- ] }) });
6030
+ ),
6031
+ contextKind && /* @__PURE__ */ jsxs("div", { className: "ra-sibling-context-label", title: contextSummary ? `${contextKind} \xB7 ${contextSummary}` : contextKind, children: [
6032
+ /* @__PURE__ */ jsx("span", { className: "ra-sibling-context-kind", children: contextKind }),
6033
+ contextSummary && /* @__PURE__ */ jsxs(Fragment, { children: [
6034
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ra-sibling-context-sep", children: "\xB7" }),
6035
+ /* @__PURE__ */ jsx("span", { className: "ra-sibling-context-summary", children: contextSummary })
6036
+ ] })
6037
+ ] })
6038
+ ] }),
6039
+ /* @__PURE__ */ jsxs("div", { className: "ra-sibling-body", children: [
6040
+ isLoading && /* @__PURE__ */ jsx(LoadingState, {}),
6041
+ !isLoading && error && /* @__PURE__ */ jsx(ErrorState, { error }),
6042
+ !isLoading && !error && items.length === 0 && /* @__PURE__ */ jsx(EmptyState, { title: i18n.noItemsTitle, body: i18n.noItemsBody }),
6043
+ !isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item, idx) => {
6044
+ const id = item.itemId ?? "";
6045
+ const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
6046
+ const akey = anchorKey(item.scope);
6047
+ const selected = selectedItemId === id;
6048
+ const isDirty = !!(id && dirtyKeys?.has(id) || akey && dirtyKeys?.has(akey));
6049
+ const hasError = !!(id && errorKeys?.has(id) || akey && errorKeys?.has(akey));
6050
+ const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
6051
+ const isTargeted = ruleClauses.length > 0;
6052
+ const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
6053
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6054
+ "button",
6055
+ {
6056
+ type: "button",
6057
+ onClick: () => onSelect(id),
6058
+ className: "ra-row",
6059
+ "data-selected": selected,
6060
+ children: [
6061
+ /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
6062
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
6063
+ item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
6064
+ ] }),
6065
+ isTargeted && /* @__PURE__ */ jsx(
6066
+ "span",
6067
+ {
6068
+ className: "ra-row-rule-pip",
6069
+ title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6070
+ "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6071
+ children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
6072
+ }
6073
+ ),
6074
+ hasError ? /* @__PURE__ */ jsx(
6075
+ "span",
6076
+ {
6077
+ className: "ra-error-pip",
6078
+ title: "Save failed",
6079
+ "aria-label": "Save failed"
6080
+ }
6081
+ ) : isDirty ? /* @__PURE__ */ jsx(
6082
+ "span",
6083
+ {
6084
+ className: "ra-dirty-pip",
6085
+ title: "Unsaved changes",
6086
+ "aria-label": "Unsaved changes"
6087
+ }
6088
+ ) : null
6089
+ ]
6090
+ }
6091
+ ) }, key);
6092
+ }) })
6093
+ ] })
6094
+ ] });
6014
6095
  }
6015
6096
  var TONE_ICON = {
6016
6097
  info: Lightbulb,
@@ -6948,6 +7029,60 @@ var SaveAllProgress = ({
6948
7029
  }
6949
7030
  );
6950
7031
  };
7032
+ function PreviewReopenPill({ anchorRef, onClick, ariaLabel, title, children }) {
7033
+ const [pos, setPos] = useState(null);
7034
+ const rafRef = useRef(null);
7035
+ useLayoutEffect(() => {
7036
+ const el = anchorRef.current;
7037
+ if (!el || typeof window === "undefined") return;
7038
+ const measure = () => {
7039
+ if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
7040
+ rafRef.current = requestAnimationFrame(() => {
7041
+ const rect = el.getBoundingClientRect();
7042
+ if (rect.width === 0 && rect.height === 0) return;
7043
+ setPos({
7044
+ top: rect.top + rect.height / 2,
7045
+ right: Math.max(0, window.innerWidth - rect.right)
7046
+ });
7047
+ });
7048
+ };
7049
+ measure();
7050
+ const ro = new ResizeObserver(measure);
7051
+ ro.observe(el);
7052
+ ro.observe(document.body);
7053
+ window.addEventListener("resize", measure);
7054
+ window.addEventListener("scroll", measure, true);
7055
+ return () => {
7056
+ ro.disconnect();
7057
+ window.removeEventListener("resize", measure);
7058
+ window.removeEventListener("scroll", measure, true);
7059
+ if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
7060
+ };
7061
+ }, [anchorRef]);
7062
+ if (typeof document === "undefined" || !pos) return null;
7063
+ return createPortal(
7064
+ /* @__PURE__ */ jsx(
7065
+ "button",
7066
+ {
7067
+ type: "button",
7068
+ className: "ra-shell ra-preview-reopen ra-preview-reopen--floating",
7069
+ onClick,
7070
+ "aria-label": ariaLabel,
7071
+ title,
7072
+ style: {
7073
+ position: "fixed",
7074
+ top: pos.top,
7075
+ right: pos.right,
7076
+ // Pull half the pill width out into the gutter so it visually
7077
+ // anchors *to* the editor edge rather than sitting inside it.
7078
+ transform: "translate(50%, -50%)"
7079
+ },
7080
+ children
7081
+ }
7082
+ ),
7083
+ document.body
7084
+ );
7085
+ }
6951
7086
  var EditorMountPool = ({
6952
7087
  renderSlot,
6953
7088
  keepMountedHidden = true,
@@ -7700,6 +7835,7 @@ function RecordsAdminShellInner(props) {
7700
7835
  ]);
7701
7836
  const onLeftSelectRef = useRef(null);
7702
7837
  const onCreateItemDraftRef = useRef(null);
7838
+ const previewReopenAnchorRef = useRef(null);
7703
7839
  const { runWithGuard } = useDirtyNavigation({
7704
7840
  strategy: dirtyStrategy,
7705
7841
  isDirty: editorCtx.isDirty,
@@ -7807,6 +7943,7 @@ function RecordsAdminShellInner(props) {
7807
7943
  const itemViewCtx = baseItemViewCtx;
7808
7944
  const renderEditorWithPreview = () => {
7809
7945
  if (!editingTargetScope) return null;
7946
+ const previewAnchorRef = previewReopenAnchorRef;
7810
7947
  const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
7811
7948
  const scopePicker = previewScopePicker && effectivePreviewScope ? /* @__PURE__ */ jsx(
7812
7949
  PreviewScopePicker,
@@ -7908,15 +8045,14 @@ function RecordsAdminShellInner(props) {
7908
8045
  if (previewMode === "side") {
7909
8046
  if (!sidePreviewOpen) {
7910
8047
  return withNav(
7911
- /* @__PURE__ */ jsxs("div", { className: "relative h-full", children: [
8048
+ /* @__PURE__ */ jsxs("div", { className: "relative h-full", ref: previewAnchorRef, children: [
7912
8049
  baseEditor(),
7913
8050
  /* @__PURE__ */ jsxs(
7914
- "button",
8051
+ PreviewReopenPill,
7915
8052
  {
7916
- type: "button",
7917
- className: "ra-preview-reopen",
8053
+ anchorRef: previewAnchorRef,
7918
8054
  onClick: () => setSidePreviewOpen(true),
7919
- "aria-label": i18n.openPreview,
8055
+ ariaLabel: i18n.openPreview,
7920
8056
  title: i18n.openPreview,
7921
8057
  children: [
7922
8058
  /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" }),
@@ -8212,25 +8348,18 @@ function RecordsAdminShellInner(props) {
8212
8348
  [facetBrowseFilter]
8213
8349
  );
8214
8350
  const facetBrowseFacets = useMemo(() => {
8215
- if (!facetBrowse.items.length) return [];
8216
- const byKey = /* @__PURE__ */ new Map();
8217
- for (const it of facetBrowse.items) {
8218
- const key = it.scope.facetId;
8219
- const value = it.scope.facetValue;
8220
- if (!key || !value) continue;
8221
- const facetLabel = it.subtitle ?? key;
8222
- const valueLabel = it.label ?? value;
8223
- const existing = byKey.get(key);
8224
- if (existing) {
8225
- if (!existing.values.some((v) => v.key === value)) {
8226
- existing.values.push({ key: value, label: valueLabel });
8227
- }
8228
- } else {
8229
- byKey.set(key, { key, label: facetLabel, values: [{ key: value, label: valueLabel }] });
8230
- }
8351
+ const vocab = facetBrowse.vocabulary ?? [];
8352
+ if (!vocab.length) return [];
8353
+ const out = [];
8354
+ for (const facet of vocab) {
8355
+ const key = facet.key;
8356
+ if (!key) continue;
8357
+ const values = (facet.values ?? []).filter((v) => !!v.key).map((v) => ({ key: v.key, label: v.name ?? v.key }));
8358
+ if (!values.length) continue;
8359
+ out.push({ key, label: facet.name ?? key, values });
8231
8360
  }
8232
- return Array.from(byKey.values());
8233
- }, [facetBrowse.items]);
8361
+ return out;
8362
+ }, [facetBrowse.vocabulary]);
8234
8363
  const collectionGlobalAllRow = useMemo(
8235
8364
  () => ({
8236
8365
  id: null,
@@ -8438,6 +8567,8 @@ function RecordsAdminShellInner(props) {
8438
8567
  onSelect: onItemOpen,
8439
8568
  dirtyKeys,
8440
8569
  errorKeys,
8570
+ contextKind: activeScope === "rule" ? "Rule" : activeScope === "product" ? "Product" : activeScope === "collection" ? "Global" : activeScope === "all" ? "All records" : activeScope === "variant" ? "Variant" : activeScope === "batch" ? "Batch" : activeScope === "facet" ? "Facet" : void 0,
8571
+ contextSummary: activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
8441
8572
  i18n
8442
8573
  }
8443
8574
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [