@useinsider/guido 3.15.0 → 3.15.1-beta.6da4177

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.
Files changed (24) hide show
  1. package/dist/composables/useHtmlValidator.js +50 -50
  2. package/dist/config/i18n/en/labels.json.js +7 -3
  3. package/dist/config/migrator/recommendation/compositionMapper.js +26 -23
  4. package/dist/config/migrator/recommendation/htmlBuilder.js +156 -155
  5. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +18 -15
  6. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +363 -322
  7. package/dist/extensions/Blocks/Recommendation/controls/main/layoutOrientation.js +8 -8
  8. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +271 -231
  9. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +16 -16
  10. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +16 -16
  11. package/dist/extensions/Blocks/Recommendation/templates/utils.js +120 -120
  12. package/dist/extensions/Blocks/Recommendation/utils/captureStyleTemplates.js +55 -54
  13. package/dist/extensions/Blocks/Recommendation/utils/compositionKeys.js +89 -0
  14. package/dist/extensions/Blocks/common-control.js +42 -35
  15. package/dist/src/composables/useHtmlValidator.d.ts +3 -2
  16. package/dist/src/config/migrator/recommendation/types.d.ts +1 -0
  17. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  18. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +6 -0
  19. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +27 -5
  20. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +4 -2
  21. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +12 -11
  22. package/dist/src/extensions/Blocks/Recommendation/utils/compositionKeys.d.ts +47 -0
  23. package/dist/src/extensions/Blocks/common-control.d.ts +3 -1
  24. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  import { extractWrapperTags as C } from "../../../../utils/preserveTextStyles.js";
2
2
  import { RecommendationBlockId as c } from "../constants/blockIds.js";
3
3
  import { DESKTOP_CONTAINER_SELECTOR as S, ATTR_PRODUCT_NAME as E, ATTR_PRODUCT_PRICE as p, ATTR_PRODUCT_OLD_PRICE as y, ATTR_PRODUCT_OMNIBUS_PRICE as A, ATTR_PRODUCT_OMNIBUS_DISCOUNT as R, ATTR_PRODUCT_BUTTON as P, ATTR_PRODUCT_IMAGE as q, ATTR_PRODUCT_ATTR as x } from "../constants/selectors.js";
4
- import { CSS_CLASS_TEXT_TRIM as B } from "../controls/shared/textTrimCssRules.js";
5
- import { toCustomCompositionKey as v } from "../templates/utils.js";
6
- import { hasQuerySelectorAll as _, hasGetAttribute as l, hasGetStyle as I, hasGetComputedStyle as U } from "./tagName.js";
4
+ import { CSS_CLASS_TEXT_TRIM as v } from "../controls/shared/textTrimCssRules.js";
5
+ import { toCustomCompositionKey as B } from "../templates/utils.js";
6
+ import { resolveRowCompositionKey as U } from "./compositionKeys.js";
7
+ import { hasQuerySelectorAll as _, hasGetAttribute as l, hasGetStyle as I, hasGetComputedStyle as N } from "./tagName.js";
7
8
  const s = [
8
9
  "font-size",
9
10
  "font-family",
@@ -13,14 +14,14 @@ const s = [
13
14
  "text-align",
14
15
  "line-height",
15
16
  "text-decoration"
16
- ], N = [
17
+ ], M = [
17
18
  "background",
18
19
  "background-color",
19
20
  "border-width",
20
21
  "border-color",
21
22
  "border-style",
22
23
  "border-radius"
23
- ], M = [
24
+ ], D = [
24
25
  ...s,
25
26
  "background",
26
27
  "background-color",
@@ -31,14 +32,14 @@ const s = [
31
32
  "width",
32
33
  "mso-border-alt",
33
34
  "mso-padding-alt"
34
- ], D = [
35
+ ], G = [
35
36
  "display",
36
37
  "width",
37
38
  "height",
38
39
  "max-width",
39
40
  "border-radius",
40
41
  "margin"
41
- ], b = /* @__PURE__ */ new Set(["transparent", "rgba(0, 0, 0, 0)"]), G = [
42
+ ], g = /* @__PURE__ */ new Set(["transparent", "rgba(0, 0, 0, 0)"]), L = [
42
43
  { attrKey: E, blockId: c.NAME, kind: "text" },
43
44
  { attrKey: p, blockId: c.PRICE, kind: "text" },
44
45
  { attrKey: y, blockId: c.OLD_PRICE, kind: "text" },
@@ -50,7 +51,7 @@ const s = [
50
51
  function O(t) {
51
52
  return t && "getInnerHTML" in t && typeof t.getInnerHTML == "function" ? t.getInnerHTML().trim() : "";
52
53
  }
53
- function L(t) {
54
+ function w(t) {
54
55
  return t && "getInnerText" in t && typeof t.getInnerText == "function" ? t.getInnerText().trim() : "";
55
56
  }
56
57
  function a(t, e, r) {
@@ -66,20 +67,20 @@ function k(t) {
66
67
  if (!t || !I(t))
67
68
  return;
68
69
  const e = t.getStyle("background-color") || t.getStyle("background");
69
- return e && !b.has(e) ? e : void 0;
70
+ return e && !g.has(e) ? e : void 0;
70
71
  }
71
- function g(t, e) {
72
+ function b(t, e) {
72
73
  const r = {};
73
74
  return a(r, t, e), h(r);
74
75
  }
75
- function w(t) {
76
+ function K(t) {
76
77
  const e = t.querySelector("p"), r = {};
77
78
  a(r, t, s), a(r, e, s), a(r, t.querySelector("strong"), s), a(r, t.querySelector("em"), s), a(r, t.querySelector("s"), s);
78
79
  const n = h(r), { openTags: o, closeTags: i } = C(O(e));
79
80
  return !n && !o ? null : { pStyle: n || void 0, openTags: o, closeTags: i };
80
81
  }
81
82
  function $(t) {
82
- const e = t.querySelector(".es-button-border"), r = t.querySelector("a.es-button") ?? t.querySelector("a"), n = g(e, N), o = g(r, M), i = e && "getAttribute" in e ? e.getAttribute("class") ?? "" : "", u = /\bes-fw\b/.test(i), { openTags: T, closeTags: d } = C(O(r)), m = L(r);
83
+ const e = t.querySelector(".es-button-border"), r = t.querySelector("a.es-button") ?? t.querySelector("a"), n = b(e, M), o = b(r, D), i = e && "getAttribute" in e ? e.getAttribute("class") ?? "" : "", u = /\bes-fw\b/.test(i), { openTags: T, closeTags: d } = C(O(r)), m = w(r);
83
84
  return !n && !o && !u && !T && !m ? null : {
84
85
  buttonBorderStyle: n || void 0,
85
86
  buttonAnchorStyle: o || void 0,
@@ -89,18 +90,18 @@ function $(t) {
89
90
  closeTags: d
90
91
  };
91
92
  }
92
- function K(t) {
93
- const e = g(t.querySelector("img"), D);
93
+ function H(t) {
94
+ const e = b(t.querySelector("img"), G);
94
95
  return e ? { imgStyle: e } : null;
95
96
  }
96
- function H(t, e) {
97
- return e === "button" ? $(t) : e === "image" ? K(t) : w(t);
97
+ function F(t, e) {
98
+ return e === "button" ? $(t) : e === "image" ? H(t) : K(t);
98
99
  }
99
100
  function f(t, e) {
100
101
  if (!t || !("querySelector" in t))
101
102
  return;
102
103
  const r = t.querySelector(S) ?? t;
103
- if (G.forEach(({ attrKey: i, blockId: u, kind: T }) => {
104
+ if (L.forEach(({ attrKey: i, blockId: u, kind: T }) => {
104
105
  const d = r.querySelector(`[esd-extension-block-id="${u}"]`);
105
106
  d && e(d, i, T);
106
107
  }), !_(r))
@@ -110,46 +111,46 @@ function f(t, e) {
110
111
  `[esd-extension-block-id="${c.CUSTOM_ATTRIBUTE}"]`
111
112
  ).forEach((i) => {
112
113
  const u = l(i) ? i.getAttribute(x) : null;
113
- !u || n.has(u) || (n.add(u), e(i, v(u), "text"));
114
+ !u || n.has(u) || (n.add(u), e(i, B(u), "text"));
114
115
  });
115
116
  }
116
- function F(t) {
117
+ function j(t) {
117
118
  const e = {};
118
119
  return f(t, (r, n, o) => {
119
120
  if (!("querySelector" in r))
120
121
  return;
121
- const i = H(r, o);
122
+ const i = F(r, o);
122
123
  i && (e[n] = i);
123
124
  }), e;
124
125
  }
125
- function j(t) {
126
+ function X(t) {
126
127
  if (!t || !("querySelector" in t))
127
128
  return;
128
129
  const e = t.querySelector(S) ?? t, r = e.querySelector(".product-card-segment") ?? e.querySelector(".product-card-wrapper");
129
130
  return k(r);
130
131
  }
131
- function X(t) {
132
+ function z(t) {
132
133
  if (!t)
133
134
  return;
134
135
  const e = l(t) ? t.getAttribute("bgcolor") : null;
135
- if (e && !b.has(e))
136
+ if (e && !g.has(e))
136
137
  return e;
137
138
  const r = k(t);
138
139
  if (r)
139
140
  return r;
140
- const n = U(t) ? t.getComputedStyle("background-color") : void 0;
141
- return n && !b.has(n) ? n : void 0;
141
+ const n = N(t) ? t.getComputedStyle("background-color") : void 0;
142
+ return n && !g.has(n) ? n : void 0;
142
143
  }
143
- function z(t) {
144
+ function Q(t) {
144
145
  const e = {};
145
146
  return f(t, (r, n, o) => {
146
147
  if (o !== "text")
147
148
  return;
148
- const i = X(r);
149
+ const i = z(r);
149
150
  i && (e[n] = i);
150
151
  }), e;
151
152
  }
152
- function Q(t) {
153
+ function V(t) {
153
154
  const e = {};
154
155
  return f(t, (r, n, o) => {
155
156
  if (o === "image")
@@ -158,30 +159,30 @@ function Q(t) {
158
159
  i && (e[n] = i);
159
160
  }), e;
160
161
  }
161
- const V = /^es-[pm]\d+[trbl]?$/;
162
- function W(t) {
162
+ const W = /^es-[pm]\d+[trbl]?$/;
163
+ function Y(t) {
163
164
  const e = l(t) ? t.getAttribute("class") : null;
164
165
  if (!e)
165
166
  return;
166
- const r = e.split(/\s+/).filter((n) => V.test(n) || n === B);
167
+ const r = e.split(/\s+/).filter((n) => W.test(n) || n === v);
167
168
  return r.length ? r.join(" ") : void 0;
168
169
  }
169
- function Y(t) {
170
+ function J(t) {
170
171
  if (!t || !("querySelector" in t))
171
172
  return !1;
172
173
  const e = t.querySelector(S) ?? t, r = (n) => !!(n && "querySelector" in n && n.querySelector(".product-price") && n.querySelector(".product-old-price"));
173
174
  return r(e.querySelector(`[data-attribute-type="${p}"]`)) || r(e.querySelector(`[data-attribute-type="${y}"]`));
174
175
  }
175
- function J(t) {
176
- const e = {}, r = Y(t);
176
+ function Z(t) {
177
+ const e = {}, r = J(t);
177
178
  return f(t, (n, o, i) => {
178
179
  if (i === "image" || r && (o === p || o === y))
179
180
  return;
180
- const u = W(n);
181
+ const u = Y(n);
181
182
  u && (e[o] = u);
182
183
  }), e;
183
184
  }
184
- function Z(t) {
185
+ function tt(t) {
185
186
  const e = {};
186
187
  return f(t, (r, n) => {
187
188
  if (n !== A && n !== R || !l(r))
@@ -190,7 +191,7 @@ function Z(t) {
190
191
  (o != null || i != null) && (e[n] = { before: o ?? void 0, after: i ?? void 0 });
191
192
  }), e;
192
193
  }
193
- function tt(t) {
194
+ function et(t) {
194
195
  const e = {};
195
196
  if (!t || !("querySelector" in t))
196
197
  return e;
@@ -198,28 +199,28 @@ function tt(t) {
198
199
  return _(r) && r.querySelectorAll(".recommendation-attribute-row").forEach((n) => {
199
200
  if (!l(n))
200
201
  return;
201
- const o = n.getAttribute("data-attribute-type"), i = n.getAttribute("data-visibility");
202
+ const o = U(n), i = n.getAttribute("data-visibility");
202
203
  o && i != null && (e[o] = i !== "0");
203
204
  }), e;
204
205
  }
205
- function ct(t) {
206
+ function at(t) {
206
207
  return {
207
- styleTemplates: F(t),
208
- cardBackgroundColor: j(t),
209
- cellBackgroundColors: z(t),
210
- cellAlignments: Q(t),
211
- cellClasses: J(t),
212
- omnibusTexts: Z(t),
213
- visibility: tt(t)
208
+ styleTemplates: j(t),
209
+ cardBackgroundColor: X(t),
210
+ cellBackgroundColors: Q(t),
211
+ cellAlignments: V(t),
212
+ cellClasses: Z(t),
213
+ omnibusTexts: tt(t),
214
+ visibility: et(t)
214
215
  };
215
216
  }
216
217
  export {
217
- F as captureAttributeStyleTemplates,
218
- j as captureCardBackgroundColor,
219
- Q as captureCellAlignments,
220
- z as captureCellBackgroundColors,
221
- J as captureCellClasses,
222
- Z as captureOmnibusTexts,
223
- ct as captureStyles,
224
- tt as captureVisibility
218
+ j as captureAttributeStyleTemplates,
219
+ X as captureCardBackgroundColor,
220
+ V as captureCellAlignments,
221
+ Q as captureCellBackgroundColors,
222
+ Z as captureCellClasses,
223
+ tt as captureOmnibusTexts,
224
+ at as captureStyles,
225
+ et as captureVisibility
225
226
  };
@@ -0,0 +1,89 @@
1
+ import { ATTR_CUSTOM_PREFIX as s, ATTR_PRODUCT_ATTR as i, PRODUCT_ATTRIBUTE_PREFIX as f, ATTR_DATA_ATTRIBUTE_TYPE as l, SELECTOR_ATTRIBUTE_ROW as R } from "../constants/selectors.js";
2
+ function d(t) {
3
+ return !t.startsWith(f);
4
+ }
5
+ function y(t) {
6
+ return t.startsWith(f) ? t.substring(f.length) : t;
7
+ }
8
+ function T(t) {
9
+ return t.startsWith(s) ? t.substring(s.length) : t;
10
+ }
11
+ function A(t) {
12
+ return `${s}${t}`;
13
+ }
14
+ function P(t) {
15
+ return t.type === "defaultAttribute" ? t.attributeName : `${f}${t.attributeName}`;
16
+ }
17
+ function _(t, r) {
18
+ const n = Object.values(r).filter((e) => e.attributeName === t);
19
+ return n.find((e) => e.type === "defaultAttribute") ?? n[0];
20
+ }
21
+ function a(t, r) {
22
+ return t && "getAttribute" in t ? t.getAttribute(r) : null;
23
+ }
24
+ function m(t, r, n = {}) {
25
+ const e = `${s}${t}`, u = r == null ? void 0 : r.querySelector(
26
+ `${R}[${l}="${e}"]`
27
+ ), c = a(u, i) ?? a(u == null ? void 0 : u.querySelector(`[${i}]`), i);
28
+ if (c)
29
+ return c;
30
+ const o = _(t, n);
31
+ return o ? P(o) : t;
32
+ }
33
+ function g(t, r, n = {}) {
34
+ return t.startsWith(f) ? t : m(t, r, n);
35
+ }
36
+ function C(t, r, n = {}) {
37
+ return t.map((e) => {
38
+ if (!e.startsWith(s))
39
+ return e;
40
+ const u = T(e);
41
+ return A(g(u, r, n));
42
+ });
43
+ }
44
+ function E(t, r, n = {}) {
45
+ return t.map((e) => g(e, r, n));
46
+ }
47
+ function S(t) {
48
+ const r = a(t, l);
49
+ if (!(r != null && r.startsWith(s)))
50
+ return r;
51
+ const n = "querySelector" in t ? t.querySelector(`[${i}]`) : void 0, e = a(n, i);
52
+ return e ? A(e) : r;
53
+ }
54
+ function $(t, r, n) {
55
+ const e = a(t, i), u = a(t.querySelector(`[${i}]`), i), c = e ?? u;
56
+ return c === null ? n : c === r;
57
+ }
58
+ function W(t, r) {
59
+ const n = (h) => t.querySelector(
60
+ `${R}[${l}="${h}"]`
61
+ );
62
+ if (!r.startsWith(s))
63
+ return n(r) ?? null;
64
+ const e = T(r), u = n(r);
65
+ if (u && $(u, e, !0))
66
+ return u;
67
+ const c = A(y(e)), o = c === r ? void 0 : n(c);
68
+ return o && $(o, e, !1) ? o : null;
69
+ }
70
+ function q(t, r) {
71
+ const n = T(r), e = A(y(n));
72
+ return e === r ? t : t.replace(
73
+ `${l}="${e}"`,
74
+ `${l}="${r}"`
75
+ );
76
+ }
77
+ export {
78
+ y as bareAttributeName,
79
+ T as compositionKeyToProductAttr,
80
+ P as filterToProductAttrValue,
81
+ W as findAttributeRow,
82
+ _ as findFilterByBareName,
83
+ d as isDefaultProductAttr,
84
+ C as normalizeCompositionKeys,
85
+ E as normalizeCustomAttrValues,
86
+ A as productAttrToCompositionKey,
87
+ S as resolveRowCompositionKey,
88
+ q as rewriteLegacyRowKeyHtml
89
+ };
@@ -47,15 +47,15 @@ class d extends I {
47
47
  const t = this.getBlockInstanceId();
48
48
  if (!t)
49
49
  return !1;
50
- const o = t !== this.lastBlockInstanceId;
51
- return e(), o && (this.lastBlockInstanceId = t), o;
50
+ const E = t !== this.lastBlockInstanceId;
51
+ return e(), E && (this.lastBlockInstanceId = t), E;
52
52
  }
53
- _GuLabel({ text: e, name: t = "", hint: o }) {
53
+ _GuLabel({ text: e, name: t = "", hint: E }) {
54
54
  return `
55
55
  <${n.LABEL}
56
56
  ${$.LABEL.text}="${e}"
57
57
  ${$.LABEL.name}="${t || `${e} Label`}"
58
- ${o ? `${$.LABEL.hint}="${o}"` : ""}>
58
+ ${E ? `${$.LABEL.hint}="${E}"` : ""}>
59
59
  </${n.LABEL}>
60
60
  `;
61
61
  }
@@ -73,7 +73,7 @@ class d extends I {
73
73
  * Use in getTemplate() — the icon is registered in a hidden store so Stripo initializes it.
74
74
  * Pair with _setInfoMessageValue() in onRender() to set the text content.
75
75
  */
76
- _GuOnPageMessage({ name: e, icon: t = "not-found", type: o = "info" }) {
76
+ _GuOnPageMessage({ name: e, icon: t = "not-found", type: E = "info" }) {
77
77
  return `
78
78
  <div data-info-message="${e}" style="display: none; padding: 16px 16px 0;">
79
79
  <div class="icon-store" style="display: none;">
@@ -81,7 +81,7 @@ class d extends I {
81
81
  ${this._GuIcon({ src: t, className: "icon" })}
82
82
  </div>
83
83
  </div>
84
- ${this._GuMessage({ name: e, type: o })}
84
+ ${this._GuMessage({ name: e, type: E })}
85
85
  </div>
86
86
  `;
87
87
  }
@@ -91,7 +91,7 @@ class d extends I {
91
91
  * and combines it with the text in Stripo's alert-message layout.
92
92
  */
93
93
  _setInfoMessageValue(e, t) {
94
- const o = this.getContainer(), E = o.querySelector(`[data-icon-key="${e}"]`), a = E == null ? void 0 : E.querySelector("ue-icon-component"), l = (a == null ? void 0 : a.outerHTML) ?? "";
94
+ const E = this.getContainer(), o = E.querySelector(`[data-icon-key="${e}"]`), a = o == null ? void 0 : o.querySelector("ue-icon-component"), l = (a == null ? void 0 : a.outerHTML) ?? "";
95
95
  this.api.setUIEAttribute(
96
96
  e,
97
97
  "value",
@@ -104,7 +104,7 @@ class d extends I {
104
104
  </div>
105
105
  `
106
106
  );
107
- const T = o.querySelector(`[data-info-message="${e}"]`);
107
+ const T = E.querySelector(`[data-info-message="${e}"]`);
108
108
  T && (T.style.display = "");
109
109
  }
110
110
  _GuToggle(e) {
@@ -121,59 +121,66 @@ class d extends I {
121
121
  ${$.SELECT_ITEM.value}="${t}">
122
122
  </${n.SELECT_ITEM}>`;
123
123
  }
124
- _GuSelect({ name: e, placeholder: t, options: o, className: E = "es-180w" }) {
124
+ _GuSelect({
125
+ name: e,
126
+ placeholder: t,
127
+ options: E,
128
+ className: o = "es-180w",
129
+ searchable: a = !1
130
+ }) {
125
131
  return `
126
132
  <${n.SELECTPICKER}
127
- class="${E}"
133
+ class="${o}"
128
134
  ${$.SELECTPICKER.name}="${e}"
135
+ ${a ? `${$.SELECTPICKER.searchable}="true"` : ""}
129
136
  ${$.SELECTPICKER.placeholder}="${t}">
130
- ${o.map((a) => this._GuSelectItem(a)).join("")}
137
+ ${E.map((l) => this._GuSelectItem(l)).join("")}
131
138
  </${n.SELECTPICKER}>
132
139
  `;
133
140
  }
134
- _GuTextInput({ name: e, placeholder: t, className: o = "", disabled: E = !1 }) {
141
+ _GuTextInput({ name: e, placeholder: t, className: E = "", disabled: o = !1 }) {
135
142
  return `
136
143
  <${n.TEXT}
137
- class=${o}
144
+ class=${E}
138
145
  ${$.TEXT.name}="${e}"
139
146
  placeholder="${t || e}"
140
- ${E ? `${$.TEXT.disabled}="true"` : ""}>
147
+ ${o ? `${$.TEXT.disabled}="true"` : ""}>
141
148
  </${n.TEXT}>
142
149
  `;
143
150
  }
144
- _GuCounter({ name: e, maxValue: t, minValue: o = 1, step: E = 1 }) {
151
+ _GuCounter({ name: e, maxValue: t, minValue: E = 1, step: o = 1 }) {
145
152
  return `
146
153
  <${n.COUNTER}
147
154
  ${$.COUNTER.name}="${e}"
148
- ${$.COUNTER.minValue}="${o}"
155
+ ${$.COUNTER.minValue}="${E}"
149
156
  ${$.COUNTER.maxValue}="${t}"
150
- ${$.COUNTER.step}="${E}">
157
+ ${$.COUNTER.step}="${o}">
151
158
  </${n.COUNTER}>
152
159
  `;
153
160
  }
154
- _GuRadioButtonItem({ text: e, value: t, icon: o }) {
161
+ _GuRadioButtonItem({ text: e, value: t, icon: E }) {
155
162
  return `
156
163
  <${n.RADIO_ITEM}
157
164
  ${$.RADIO_ITEM.value}="${t}"
158
165
  ${e ? `${$.RADIO_ITEM.text}="${e}"` : ""}
159
- ${o ? `${$.RADIO_ITEM.icon}="${o}"` : ""}>
166
+ ${E ? `${$.RADIO_ITEM.icon}="${E}"` : ""}>
160
167
  </${n.RADIO_ITEM}>
161
168
  `;
162
169
  }
163
- _GuRadioButton({ name: e, buttons: t, id: o = "" }) {
170
+ _GuRadioButton({ name: e, buttons: t, id: E = "" }) {
164
171
  return `
165
172
  <${n.RADIO_BUTTONS}
166
- ${o ? `id="${o}"` : ""}
173
+ ${E ? `id="${E}"` : ""}
167
174
  ${$.RADIO_BUTTONS.name}="${e}">
168
- ${t.map((E) => this._GuRadioButtonItem(E)).join("")}
175
+ ${t.map((o) => this._GuRadioButtonItem(o)).join("")}
169
176
  </${n.RADIO_BUTTONS}>
170
177
  `;
171
178
  }
172
- _GuButton({ name: e, label: t, id: o = "" }) {
179
+ _GuButton({ name: e, label: t, id: E = "" }) {
173
180
  return `
174
181
  <${n.BUTTON}
175
182
  ${$.BUTTON.name}="${e}"
176
- ${o ? `id="${o}"` : ""}
183
+ ${E ? `id="${E}"` : ""}
177
184
  ${$.BUTTON.caption}="${t}"}>
178
185
  </${n.BUTTON}>
179
186
  `;
@@ -191,10 +198,10 @@ class d extends I {
191
198
  * @param param0
192
199
  * @returns It returns a button with an icon.
193
200
  */
194
- _GuIconButton({ name: e, icon: t, className: o = "" }) {
201
+ _GuIconButton({ name: e, icon: t, className: E = "" }) {
195
202
  return `
196
203
  <${n.BUTTON}
197
- class="${o}"
204
+ class="${E}"
198
205
  ${$.BUTTON.name}="${e}"
199
206
  ${$.BUTTON.icon}="${t}">
200
207
  </${n.BUTTON}>
@@ -222,19 +229,19 @@ class d extends I {
222
229
  * @returns HTML string for the orderable control
223
230
  */
224
231
  _GuOrderable(e, t) {
225
- let o = "";
232
+ let E = "";
226
233
  t.forEach((a) => {
227
234
  const l = $.ORDERABLE_ITEM && "name" in $.ORDERABLE_ITEM ? $.ORDERABLE_ITEM.name : "name";
228
- o += `
235
+ E += `
229
236
  <${n.ORDERABLE_ITEM} ${l}="${a.key}">
230
237
  ${a.content}
231
238
  </${n.ORDERABLE_ITEM}>
232
239
  `;
233
240
  });
234
- const E = $.ORDERABLE && "name" in $.ORDERABLE ? $.ORDERABLE.name : "name";
241
+ const o = $.ORDERABLE && "name" in $.ORDERABLE ? $.ORDERABLE.name : "name";
235
242
  return `
236
- <${n.ORDERABLE} ${E}="${e}">
237
- ${o}
243
+ <${n.ORDERABLE} ${o}="${e}">
244
+ ${E}
238
245
  </${n.ORDERABLE}>
239
246
  `;
240
247
  }
@@ -275,16 +282,16 @@ class d extends I {
275
282
  `;
276
283
  }
277
284
  _GuRadioButtons(e, t) {
278
- const o = t.map((E) => `
285
+ const E = t.map((o) => `
279
286
  <${n.RADIO_ITEM}
280
- ${$.RADIO_ITEM.text}="${E.text}"
281
- ${$.RADIO_ITEM.value}="${E.value}">
287
+ ${$.RADIO_ITEM.text}="${o.text}"
288
+ ${$.RADIO_ITEM.value}="${o.value}">
282
289
  </${n.RADIO_ITEM}>
283
290
  `).join("");
284
291
  return `
285
292
  <${n.RADIO_BUTTONS}
286
293
  ${$.RADIO_BUTTONS.name}="${e}">
287
- ${o}
294
+ ${E}
288
295
  </${n.RADIO_BUTTONS}>
289
296
  `;
290
297
  }
@@ -14,8 +14,9 @@ export declare function parseRecommendationVariable(field: string): {
14
14
  * `{{recoId_index_attribute}}` variables: the built-in compiler attributes plus
15
15
  * each account attribute in its canonical token form — bare for
16
16
  * `defaultAttribute`, `product_attribute.<name>` for `productAttribute`. Using
17
- * `resolveProductAttrValue` (the same producer the block builder uses) keeps the
18
- * validator's notion of "valid form" identical to what is actually emitted.
17
+ * `filterToProductAttrValue` (the same producer the block builder uses) keeps the
18
+ * validator's notion of "valid form" identical to what is actually emitted, and
19
+ * admits both forms when a default and a custom attribute share a name.
19
20
  */
20
21
  export declare function buildPartnerAttributeNameSet(filterList: FiltersResponse): Set<string>;
21
22
  /**
@@ -193,6 +193,7 @@ export type PerElementStyles = Record<string, MappedTextStyle | MappedButtonStyl
193
193
  export interface MappedComposition {
194
194
  composition: string[];
195
195
  visibility: Record<string, boolean>;
196
+ /** Canonical `product-attr` values, mirrored into `data-custom-attributes`. */
196
197
  customAttributes: string[];
197
198
  /** Mapping of customAttr key → full `product-attr` value. */
198
199
  customAttrValues: Record<string, string>;
@@ -8,6 +8,6 @@
8
8
  */
9
9
  export { RecommendationBlockId } from './blockIds';
10
10
  export { RecommendationControlId } from './controlIds';
11
- export { BLOCK_ROOT_SELECTOR, CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, MOBILE_ROW_SELECTOR, CSS_CLASS_RECO_BUTTON, CSS_CLASS_SKIP_COMPILE, RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, ATTR_CUSTOM_PREFIX, ATTR_DATA_CUSTOM_ATTRIBUTES, ATTR_PRODUCT_ATTR, PRODUCT_ATTRIBUTE_PREFIX, BUILT_IN_DEFAULT_ATTRIBUTES, } from './selectors';
11
+ export { BLOCK_ROOT_SELECTOR, CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, MOBILE_ROW_SELECTOR, CSS_CLASS_RECO_BUTTON, CSS_CLASS_SKIP_COMPILE, RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, ATTR_CUSTOM_PREFIX, ATTR_DATA_CARD_COMPOSITION, ATTR_DATA_CUSTOM_ATTRIBUTES, ATTR_PRODUCT_ATTR, SELECTOR_ATTRIBUTE_ROW, ATTR_DATA_ATTRIBUTE_TYPE, PRODUCT_ATTRIBUTE_PREFIX, BUILT_IN_DEFAULT_ATTRIBUTES, } from './selectors';
12
12
  export { LAYOUT_VALUES, LAYOUT_OPTIONS, DEFAULT_PRODUCTS_PER_ROW, DEFAULT_CARDS_IN_ROW, DEFAULT_MOBILE_CARDS_IN_ROW, MAX_PRODUCT_COUNT, MIN_PRODUCT_COUNT, MAX_PRODUCTS_PER_ROW, MIN_PRODUCTS_PER_ROW, MAX_MOBILE_PRODUCTS_PER_ROW, MIN_MOBILE_PRODUCTS_PER_ROW, DEFAULT_COLUMN_SPACING, DEFAULT_ROW_SPACING, DEFAULT_MOBILE_COLUMN_SPACING, DEFAULT_MOBILE_ROW_SPACING, MIN_SPACING, MAX_SPACING, SPACING_STEP, } from './layout';
13
13
  export { DEFAULT_NODE_CONFIG, DEFAULT_CURRENCY, DEFAULT_COMPOSITION, DEFAULT_VISIBILITY, CURRENT_CONFIG_VERSION, EXCLUDED_ALGORITHM_IDS, } from './defaultConfig';
@@ -66,8 +66,14 @@ export declare const ATTR_PRODUCT_BUTTON = "productButton";
66
66
  export declare const ATTR_CUSTOM_PREFIX = "customAttr:";
67
67
  /** HTML data attribute storing JSON array of added custom attribute names */
68
68
  export declare const ATTR_DATA_CUSTOM_ATTRIBUTES = "data-custom-attributes";
69
+ /** HTML data attribute on the block root holding the comma-joined composition order */
70
+ export declare const ATTR_DATA_CARD_COMPOSITION = "data-card-composition";
69
71
  /** HTML attribute on <td> elements identifying the product attribute for compiler template variable generation */
70
72
  export declare const ATTR_PRODUCT_ATTR = "product-attr";
73
+ /** Row wrapper of one composition entry inside a product card */
74
+ export declare const SELECTOR_ATTRIBUTE_ROW = ".recommendation-attribute-row";
75
+ /** HTML attribute on an attribute row holding its composition key */
76
+ export declare const ATTR_DATA_ATTRIBUTE_TYPE = "data-attribute-type";
71
77
  /** Prefix `resolveProductAttrValue` puts on custom (non-default) product attributes in `product-attr` values */
72
78
  export declare const PRODUCT_ATTRIBUTE_PREFIX = "product_attribute.";
73
79
  /**
@@ -29,6 +29,8 @@ export declare class RecommendationCardCompositionControl extends CommonControl
29
29
  private _initializeComposition;
30
30
  private _registerValueChangeListeners;
31
31
  private _readCompositionFromNode;
32
+ /** Upgrades legacy bare-name custom keys to the canonical form. (SD-147101) */
33
+ private _normalizeComposition;
32
34
  private _readCustomAttributesFromNode;
33
35
  private _readVisibilityFromRows;
34
36
  private _getDefaultVisibilities;
@@ -61,7 +63,15 @@ export declare class RecommendationCardCompositionControl extends CommonControl
61
63
  private _createPriceGroupItemHtml;
62
64
  private _createCustomItemHtml;
63
65
  /**
64
- * Builds select options from the store's filterList.
66
+ * Dropdown label of an attribute. A partner can own a default and a custom
67
+ * attribute under the same name; the type is spelled out only then, so partners
68
+ * without a collision keep clean labels. (SD-147101)
69
+ */
70
+ private _toOptionLabel;
71
+ /**
72
+ * Builds select options from the store's filterList. Option values are
73
+ * `product-attr` values, so a default attribute and a same-named custom one
74
+ * never collapse onto a single option. (SD-147101)
65
75
  * Falls back to a single option for the currently selected attribute.
66
76
  */
67
77
  private _getSelectOptions;
@@ -144,6 +154,12 @@ export declare class RecommendationCardCompositionControl extends CommonControl
144
154
  */
145
155
  private _onCustomAttributeChanged;
146
156
  private _onReorder;
157
+ /**
158
+ * Outer HTML of an existing attribute row, rewritten to the canonical key when
159
+ * the row still carries a legacy bare-name one. Copying rows this way lets the
160
+ * DOM converge on the next composition change without writing on open.
161
+ */
162
+ private _getExistingRowHtml;
147
163
  private _injectCustomAttributeHtml;
148
164
  private _injectGridAttributeRow;
149
165
  private _injectListAttributeRow;
@@ -204,13 +220,19 @@ export declare class RecommendationCardCompositionControl extends CommonControl
204
220
  */
205
221
  private _subscribeToStoreChanges;
206
222
  /**
207
- * Returns filters eligible for the custom attribute dropdown,
208
- * excluding default attributes already covered by built-in toggle items.
223
+ * Returns filters eligible for the custom attribute dropdown, excluding default
224
+ * attributes already covered by built-in toggle items.
225
+ *
226
+ * Deduped by canonical value: a partner can own two backend rows of the same type
227
+ * and name (e.g. two `defaultAttribute` "discount"), which are indistinguishable
228
+ * once reduced to a `product-attr`. Left in, they render as identical options and
229
+ * skew the collision count that decides the (Default)/(Custom) suffix. (SD-147101)
209
230
  */
210
231
  private _getAddableFilters;
211
232
  /**
212
- * Looks up the display name for an attribute from the store's filterList.
213
- * Falls back to Title Case conversion of the snake_case attribute name.
233
+ * Looks up the display name for a `product-attr` value from the store's filterList.
234
+ * Matches on the canonical value, so a default and a same-named custom attribute
235
+ * resolve to their own entry. Falls back to Title Case of the bare name.
214
236
  */
215
237
  private _getDisplayNameForAttribute;
216
238
  /**