@useinsider/guido 3.15.0 → 3.16.0-beta.0faee0c

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 (81) hide show
  1. package/dist/@types/config/schemas.js +108 -92
  2. package/dist/components/Guido.vue.js +1 -1
  3. package/dist/components/Guido.vue2.js +78 -75
  4. package/dist/composables/useActionsApi.js +30 -22
  5. package/dist/composables/useEmailTemplateApplier.js +25 -23
  6. package/dist/composables/useHtmlValidator.js +108 -106
  7. package/dist/composables/useStripo.js +30 -29
  8. package/dist/composables/useTimerClone.js +21 -19
  9. package/dist/config/i18n/en/labels.json.js +7 -3
  10. package/dist/config/migrator/checkboxMigrator.js +20 -19
  11. package/dist/config/migrator/radioButtonMigrator.js +26 -25
  12. package/dist/config/migrator/recommendation/compositionMapper.js +26 -23
  13. package/dist/config/migrator/recommendation/htmlBuilder.js +156 -155
  14. package/dist/extensions/Blocks/Checkbox/constants/controlIds.js +4 -0
  15. package/dist/extensions/Blocks/Checkbox/constants/selectors.js +6 -0
  16. package/dist/extensions/Blocks/Checkbox/controls/blockBackground/index.js +10 -0
  17. package/dist/extensions/Blocks/Checkbox/extension.js +6 -5
  18. package/dist/extensions/Blocks/Checkbox/settingsPanel.js +15 -14
  19. package/dist/extensions/Blocks/Checkbox/template.js +10 -9
  20. package/dist/extensions/Blocks/Items/block.js +44 -42
  21. package/dist/extensions/Blocks/Items/template.js +86 -86
  22. package/dist/extensions/Blocks/RadioButton/constants/controlIds.js +4 -0
  23. package/dist/extensions/Blocks/RadioButton/constants/selectors.js +6 -0
  24. package/dist/extensions/Blocks/RadioButton/controls/blockBackground/index.js +10 -0
  25. package/dist/extensions/Blocks/RadioButton/extension.js +5 -4
  26. package/dist/extensions/Blocks/RadioButton/settingsPanel.js +12 -11
  27. package/dist/extensions/Blocks/RadioButton/template.js +16 -9
  28. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +18 -15
  29. package/dist/extensions/Blocks/Recommendation/controls/cardBackground/index.js +5 -9
  30. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +363 -322
  31. package/dist/extensions/Blocks/Recommendation/controls/main/layoutOrientation.js +8 -8
  32. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +271 -231
  33. package/dist/extensions/Blocks/Recommendation/controls/spacing/index.js +98 -113
  34. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +21 -0
  35. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +16 -16
  36. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +16 -16
  37. package/dist/extensions/Blocks/Recommendation/templates/utils.js +120 -120
  38. package/dist/extensions/Blocks/Recommendation/utils/captureStyleTemplates.js +55 -54
  39. package/dist/extensions/Blocks/Recommendation/utils/compositionKeys.js +89 -0
  40. package/dist/extensions/Blocks/common-control.js +42 -35
  41. package/dist/extensions/Blocks/controlFactories.js +117 -115
  42. package/dist/guido.css +1 -1
  43. package/dist/library.js +11 -7
  44. package/dist/node_modules/valibot/dist/index.js +230 -216
  45. package/dist/public/trackingIds.js +8 -0
  46. package/dist/src/@types/config/schemas.d.ts +20 -0
  47. package/dist/src/composables/useActionsApi.d.ts +1 -1
  48. package/dist/src/composables/useConfig.d.ts +4 -0
  49. package/dist/src/composables/useHtmlValidator.d.ts +3 -2
  50. package/dist/src/config/migrator/recommendation/types.d.ts +1 -0
  51. package/dist/src/extensions/Blocks/Checkbox/constants/controlIds.d.ts +7 -0
  52. package/dist/src/extensions/Blocks/Checkbox/constants/index.d.ts +10 -0
  53. package/dist/src/extensions/Blocks/Checkbox/constants/selectors.d.ts +16 -0
  54. package/dist/src/extensions/Blocks/Checkbox/controls/blockBackground/index.d.ts +14 -0
  55. package/dist/src/extensions/Blocks/RadioButton/constants/controlIds.d.ts +7 -0
  56. package/dist/src/extensions/Blocks/RadioButton/constants/index.d.ts +10 -0
  57. package/dist/src/extensions/Blocks/RadioButton/constants/selectors.d.ts +16 -0
  58. package/dist/src/extensions/Blocks/RadioButton/controls/blockBackground/index.d.ts +14 -0
  59. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  60. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +6 -0
  61. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +27 -5
  62. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +4 -2
  63. package/dist/src/extensions/Blocks/Recommendation/controls/spacing/index.d.ts +5 -0
  64. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +12 -11
  65. package/dist/src/extensions/Blocks/Recommendation/utils/compositionKeys.d.ts +47 -0
  66. package/dist/src/extensions/Blocks/_Boilerplate/constants/controlIds.d.ts +7 -0
  67. package/dist/src/extensions/Blocks/_Boilerplate/constants/index.d.ts +10 -0
  68. package/dist/src/extensions/Blocks/_Boilerplate/constants/selectors.d.ts +16 -0
  69. package/dist/src/extensions/Blocks/_Boilerplate/controls/blockBackground/index.d.ts +14 -0
  70. package/dist/src/extensions/Blocks/common-control.d.ts +3 -1
  71. package/dist/src/library.d.ts +1 -0
  72. package/dist/src/public/trackingIds.d.ts +9 -0
  73. package/dist/src/stores/config.d.ts +36 -0
  74. package/dist/src/stores/editor.d.ts +25 -0
  75. package/dist/src/utils/linkTrackingIds.d.ts +30 -0
  76. package/dist/stores/editor.js +4 -1
  77. package/dist/stores/onboarding.js +11 -7
  78. package/dist/utils/linkTrackingIds.js +56 -0
  79. package/dist/utils/pairProductVariables.js +105 -99
  80. package/dist/utils/templatePreparation.js +72 -61
  81. package/package.json +5 -1
@@ -1,103 +1,106 @@
1
- var tt = Object.defineProperty;
2
- var et = (g, b, t) => b in g ? tt(g, b, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[b] = t;
3
- var v = (g, b, t) => et(g, typeof b != "symbol" ? b + "" : b, t);
4
- import { UIElementType as f, UEAttr as D, ModificationDescription as T } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as rt } from "../../../common-control.js";
6
- import { ATTR_PRODUCT_IMAGE as k, ATTR_PRODUCT_NAME as it, ATTR_PRODUCT_PRICE as S, ATTR_PRODUCT_OLD_PRICE as N, ATTR_PRODUCT_OMNIBUS_PRICE as ot, ATTR_PRODUCT_OMNIBUS_DISCOUNT as st, ATTR_PRODUCT_BUTTON as U, ATTR_DATA_CUSTOM_ATTRIBUTES as F, ATTR_CUSTOM_PREFIX as m, BUILT_IN_DEFAULT_ATTRIBUTES as nt } from "../../constants/selectors.js";
7
- import { DEFAULT_COMPOSITION as x, DEFAULT_VISIBILITY as M } from "../../constants/defaultConfig.js";
8
- import { RecommendationConfigService as y } from "../../services/configService.js";
9
- import { useRecommendationExtensionStore as lt } from "../../store/recommendation.js";
10
- import { ATTRIBUTE_CELL_CLASS as at, gridElementRenderer as ct, DEFAULT_CELL_PADDING as dt, buildFillerCell as ut } from "../../templates/grid/elementRenderer.js";
11
- import { listElementRenderer as mt } from "../../templates/list/elementRenderer.js";
12
- import { resolveInlinePriceOrder as ht, toDisplayName as pt, isDefaultAttribute as gt, toDisplayableAttributeValue as bt, buildElementRenderer as V } from "../../templates/utils.js";
13
- import { getTableDisplayValue as ft } from "../../utils/tagName.js";
14
- import { isPartnerManagedBlock as _t, regenerateProductRowsWithStyles as yt, getCurrentLayout as Ct } from "../main/utils.js";
15
- const St = "ui-elements-recommendation-card-composition", $ = ".recommendation-attribute-row", H = ".product-card-wrapper > tbody", j = ".product-info-cell > table > tbody", W = "data-card-composition", C = "data-attribute-type", E = "data-visibility", K = {
1
+ var ot = Object.defineProperty;
2
+ var st = (h, p, t) => p in h ? ot(h, p, { enumerable: !0, configurable: !0, writable: !0, value: t }) : h[p] = t;
3
+ var $ = (h, p, t) => st(h, typeof p != "symbol" ? p + "" : p, t);
4
+ import { UIElementType as g, UEAttr as N, ModificationDescription as A } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as nt } from "../../../common-control.js";
6
+ import { ATTR_PRODUCT_IMAGE as B, ATTR_PRODUCT_NAME as lt, ATTR_PRODUCT_PRICE as C, ATTR_PRODUCT_OLD_PRICE as v, ATTR_PRODUCT_OMNIBUS_PRICE as at, ATTR_PRODUCT_OMNIBUS_DISCOUNT as ct, ATTR_PRODUCT_BUTTON as q, ATTR_DATA_CARD_COMPOSITION as V, ATTR_DATA_CUSTOM_ATTRIBUTES as j, ATTR_CUSTOM_PREFIX as I, SELECTOR_ATTRIBUTE_ROW as R, ATTR_DATA_ATTRIBUTE_TYPE as E, BUILT_IN_DEFAULT_ATTRIBUTES as dt } from "../../constants/selectors.js";
7
+ import { DEFAULT_COMPOSITION as K, DEFAULT_VISIBILITY as W } from "../../constants/defaultConfig.js";
8
+ import { RecommendationConfigService as _ } from "../../services/configService.js";
9
+ import { useRecommendationExtensionStore as ut } from "../../store/recommendation.js";
10
+ import { ATTRIBUTE_CELL_CLASS as mt, gridElementRenderer as ht, DEFAULT_CELL_PADDING as pt, buildFillerCell as gt } from "../../templates/grid/elementRenderer.js";
11
+ import { listElementRenderer as bt } from "../../templates/list/elementRenderer.js";
12
+ import { resolveInlinePriceOrder as ft, toDisplayName as _t, toDisplayableAttributeValue as yt, buildElementRenderer as z } from "../../templates/utils.js";
13
+ import { normalizeCompositionKeys as Ct, normalizeCustomAttrValues as St, compositionKeyToProductAttr as w, resolveRowCompositionKey as At, filterToProductAttrValue as y, findAttributeRow as Tt, rewriteLegacyRowKeyHtml as G, bareAttributeName as D, isDefaultProductAttr as $t, productAttrToCompositionKey as k } from "../../utils/compositionKeys.js";
14
+ import { getTableDisplayValue as It } from "../../utils/tagName.js";
15
+ import { isPartnerManagedBlock as vt, regenerateProductRowsWithStyles as Nt, getCurrentLayout as Rt } from "../main/utils.js";
16
+ const Et = "ui-elements-recommendation-card-composition", U = ".product-card-wrapper > tbody", Y = ".product-info-cell > table > tbody", O = "data-visibility", X = {
16
17
  ADD_ATTRIBUTE: "addAttribute"
17
- }, _ = 5, O = "reorderIcon_", At = '<svg width="14" height="10" viewBox="0 0 14 10" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><circle cx="2" cy="2.5" r="1.2"/><circle cx="7" cy="2.5" r="1.2"/><circle cx="12" cy="2.5" r="1.2"/><circle cx="2" cy="7.5" r="1.2"/><circle cx="7" cy="7.5" r="1.2"/><circle cx="12" cy="7.5" r="1.2"/></svg>', h = [
18
- { key: k, label: "Product Image" },
19
- { key: it, label: "Product Name" },
20
- { key: S, label: "Product Price" },
21
- { key: N, label: "Product Original Price" },
22
- { key: ot, label: "Omnibus Price" },
23
- { key: st, label: "Omnibus Discount" },
24
- { key: U, label: "Product Button" }
25
- ], Tt = new Set(h.map((g) => g.key)), I = "customAttr_", R = "deleteAttr_";
26
- class qt extends rt {
18
+ }, b = 5, L = "reorderIcon_", Dt = '<svg width="14" height="10" viewBox="0 0 14 10" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><circle cx="2" cy="2.5" r="1.2"/><circle cx="7" cy="2.5" r="1.2"/><circle cx="12" cy="2.5" r="1.2"/><circle cx="2" cy="7.5" r="1.2"/><circle cx="7" cy="7.5" r="1.2"/><circle cx="12" cy="7.5" r="1.2"/></svg>', m = [
19
+ { key: B, label: "Product Image" },
20
+ { key: lt, label: "Product Name" },
21
+ { key: C, label: "Product Price" },
22
+ { key: v, label: "Product Original Price" },
23
+ { key: at, label: "Omnibus Price" },
24
+ { key: ct, label: "Omnibus Discount" },
25
+ { key: q, label: "Product Button" }
26
+ ], Ot = new Set(m.map((h) => h.key)), T = "customAttr_", P = "deleteAttr_";
27
+ class Wt extends nt {
27
28
  constructor() {
28
29
  super(...arguments);
29
- v(this, "store", lt());
30
- v(this, "unsubscribeStore", null);
31
- v(this, "eventController", null);
30
+ $(this, "store", ut());
31
+ $(this, "unsubscribeStore", null);
32
+ $(this, "eventController", null);
32
33
  /**
33
34
  * Guard flag: when true, onTemplateNodeUpdated skips _initializeComposition.
34
35
  * Used during _onReorder to prevent multiple intermediate rebuilds.
35
36
  */
36
- v(this, "reorderInProgress", !1);
37
+ $(this, "reorderInProgress", !1);
37
38
  }
38
39
  getId() {
39
- return St;
40
+ return Et;
40
41
  }
41
42
  // ========================================================================
42
43
  // Lifecycle
43
44
  // ========================================================================
44
45
  getTemplate() {
45
- const t = h.map((a) => `
46
- <div data-toggle-key="${a.key}" style="display: none;">
47
- ${this._GuToggle(`visibility_${a.key}`)}
46
+ const t = m.map((d) => `
47
+ <div data-toggle-key="${d.key}" style="display: none;">
48
+ ${this._GuToggle(`visibility_${d.key}`)}
48
49
  </div>
49
50
  `).join(""), e = Array.from(
50
- { length: _ },
51
- (a, d) => `
52
- <div data-custom-select-key="${I}${d}" style="display: none;">
51
+ { length: b },
52
+ (d, c) => `
53
+ <div data-custom-select-key="${T}${c}" style="display: none;">
53
54
  ${this._GuSelect({
54
- name: `${I}${d}`,
55
+ name: `${T}${c}`,
55
56
  placeholder: this.api.translate("Select Attribute"),
56
- options: []
57
+ options: [],
58
+ // A partner's attribute list runs long; search beats scrolling.
59
+ searchable: !0
57
60
  })}
58
61
  </div>
59
62
  `
60
- ).join(""), r = h.length + _, o = Array.from(
63
+ ).join(""), r = m.length + b, i = Array.from(
61
64
  { length: r },
62
- (a, d) => `
63
- <div data-reorder-icon-key="${O}${d}" style="display: none;">
64
- <${f.BUTTON}
65
+ (d, c) => `
66
+ <div data-reorder-icon-key="${L}${c}" style="display: none;">
67
+ <${g.BUTTON}
65
68
  class="drag-handle-btn flat-inline flat-white"
66
- ${D.BUTTON.name}="${O}${d}"
69
+ ${N.BUTTON.name}="${L}${c}"
67
70
  >
68
- <${f.ICON}
71
+ <${g.ICON}
69
72
  src="reorder"
70
73
  class="icon-button"
71
- ></${f.ICON}>
72
- </${f.BUTTON}>
74
+ ></${g.ICON}>
75
+ </${g.BUTTON}>
73
76
  </div>
74
77
  `
75
- ).join(""), i = Array.from(
76
- { length: _ },
77
- (a, d) => `
78
- <div data-custom-delete-key="${R}${d}" style="display: none;">
79
- <${f.BUTTON}
78
+ ).join(""), o = Array.from(
79
+ { length: b },
80
+ (d, c) => `
81
+ <div data-custom-delete-key="${P}${c}" style="display: none;">
82
+ <${g.BUTTON}
80
83
  class="custom-attr-delete flat-inline flat-white"
81
- ${D.BUTTON.name}="${R}${d}"
84
+ ${N.BUTTON.name}="${P}${c}"
82
85
  >
83
- <${f.ICON}
86
+ <${g.ICON}
84
87
  src="delete"
85
88
  class="icon-button"
86
- ></${f.ICON}>
87
- </${f.BUTTON}>
89
+ ></${g.ICON}>
90
+ </${g.BUTTON}>
88
91
  </div>
89
92
  `
90
93
  ).join(""), s = "https://academy.insiderone.com/docs/new-editor-email-recommendation-block", n = this.api.translate(
91
94
  "Drag and drop the card elements to reorder them, adjust their visibility or add new attributes up to 5."
92
- ), c = this.api.translate("For more information, you can"), l = this.api.translate("visit Academy");
95
+ ), l = this.api.translate("For more information, you can"), a = this.api.translate("visit Academy");
93
96
  return `
94
97
  <div class="recommendation-controls-container" data-card-composition-control>
95
98
  <div class="container">
96
99
  <p class="card-composition-description">
97
100
  ${n}
98
- ${c}
101
+ ${l}
99
102
  <!-- cspell:disable-next-line -->
100
- <a href="${s}" target="_blank" rel="noopener noreferrer">${l}</a>.
103
+ <a href="${s}" target="_blank" rel="noopener noreferrer">${a}</a>.
101
104
  </p>
102
105
  </div>
103
106
 
@@ -110,17 +113,17 @@ class qt extends rt {
110
113
  </div>
111
114
 
112
115
  <div class="custom-delete-store" style="display: none;">
113
- ${i}
116
+ ${o}
114
117
  </div>
115
118
 
116
119
  <div class="reorder-icon-store" style="display: none;">
117
- ${o}
120
+ ${i}
118
121
  </div>
119
122
 
120
123
  <div class="orderable-list" data-composition-list></div>
121
124
 
122
125
  ${this._GuButton({
123
- name: K.ADD_ATTRIBUTE,
126
+ name: X.ADD_ATTRIBUTE,
124
127
  label: this.api.translate("Add Attribute"),
125
128
  id: "guido__btn-add-attribute"
126
129
  })}
@@ -141,7 +144,7 @@ class qt extends rt {
141
144
  const t = (r = this.getContainer()) == null ? void 0 : r.querySelector("[data-card-composition-control]");
142
145
  if (!t)
143
146
  return;
144
- const e = _t(this.currentNode);
147
+ const e = vt(this.currentNode);
145
148
  t.style.pointerEvents = e ? "none" : "", t.style.opacity = e ? "0.5" : "";
146
149
  }
147
150
  onTemplateNodeUpdated(t) {
@@ -154,19 +157,19 @@ class qt extends rt {
154
157
  // Initialization
155
158
  // ========================================================================
156
159
  _initializeComposition() {
157
- const t = this._readCompositionFromNode(), e = this._readCustomAttributesFromNode(), r = this._readVisibilityFromRows(), o = this._renderOrderableItems(t, e), i = this._buildVisibilityValues(r);
158
- this.api.updateValues(i), o && this._initializeCustomSelects(e), this._updateAddButtonState();
160
+ const t = this._readCompositionFromNode(), e = this._readCustomAttributesFromNode(), r = this._readVisibilityFromRows(), i = this._renderOrderableItems(t, e), o = this._buildVisibilityValues(r);
161
+ this.api.updateValues(o), i && this._initializeCustomSelects(e), this._updateAddButtonState();
159
162
  }
160
163
  _registerValueChangeListeners() {
161
- h.forEach((t) => {
164
+ m.forEach((t) => {
162
165
  this.api.onValueChanged(`visibility_${t.key}`, (e) => {
163
166
  this._applyVisibilityToBlock(t.key, e);
164
167
  });
165
168
  });
166
- for (let t = 0; t < _; t++) {
167
- const e = `${I}${t}`, r = t;
168
- this.api.onValueChanged(e, (o) => {
169
- this._onCustomAttributeChanged(r, o);
169
+ for (let t = 0; t < b; t++) {
170
+ const e = `${T}${t}`, r = t;
171
+ this.api.onValueChanged(e, (i) => {
172
+ this._onCustomAttributeChanged(r, i);
170
173
  });
171
174
  }
172
175
  }
@@ -175,56 +178,64 @@ class qt extends rt {
175
178
  // ========================================================================
176
179
  _readCompositionFromNode() {
177
180
  if (!this.currentNode || !("getAttribute" in this.currentNode))
178
- return [...x];
179
- const t = this.currentNode.getAttribute(W);
181
+ return [...K];
182
+ const t = this.currentNode.getAttribute(V);
180
183
  if (t)
181
- return t.split(",").filter(Boolean);
182
- const { composition: e } = y.getConfig(this.currentNode);
183
- return e != null && e.length ? [...e] : [...x];
184
+ return this._normalizeComposition(t.split(",").filter(Boolean));
185
+ const { composition: e } = _.getConfig(this.currentNode);
186
+ return e != null && e.length ? this._normalizeComposition([...e]) : [...K];
187
+ }
188
+ /** Upgrades legacy bare-name custom keys to the canonical form. (SD-147101) */
189
+ _normalizeComposition(t) {
190
+ return Ct(t, this.currentNode, this.store.filterList);
184
191
  }
185
192
  _readCustomAttributesFromNode() {
186
193
  if (!this.currentNode || !("getAttribute" in this.currentNode))
187
194
  return [];
188
- const t = this.currentNode.getAttribute(F);
195
+ const t = this.currentNode.getAttribute(j);
189
196
  if (t)
190
197
  try {
191
- return JSON.parse(t);
198
+ return St(
199
+ JSON.parse(t).map((e) => String(e)),
200
+ this.currentNode,
201
+ this.store.filterList
202
+ );
192
203
  } catch {
193
204
  return [];
194
205
  }
195
- return this._readCompositionFromNode().filter((e) => e.startsWith(m)).map((e) => e.substring(m.length));
206
+ return this._readCompositionFromNode().filter((e) => e.startsWith(I)).map(w);
196
207
  }
197
208
  _readVisibilityFromRows() {
198
209
  if (!this.currentNode)
199
210
  return this._getDefaultVisibilities();
200
- const t = Array.from(this.currentNode.querySelectorAll($)), e = this._extractVisibilityFromRows(t);
211
+ const t = Array.from(this.currentNode.querySelectorAll(R)), e = this._extractVisibilityFromRows(t);
201
212
  if (Object.keys(e).length > 0)
202
213
  return this._mergeWithDefaults(e);
203
- const r = y.getConfig(this.currentNode).visibility;
214
+ const r = _.getConfig(this.currentNode).visibility;
204
215
  return this._mergeWithDefaults({ ...r });
205
216
  }
206
217
  _getDefaultVisibilities() {
207
- return { ...M };
218
+ return { ...W };
208
219
  }
209
220
  _extractVisibilityFromRows(t) {
210
221
  const e = {};
211
222
  return t.forEach((r) => {
212
223
  if (!("getAttribute" in r))
213
224
  return;
214
- const o = r.getAttribute(C), i = r.getAttribute(E);
215
- o && i !== null && (e[o] = this._parseVisibilityValue(i));
225
+ const i = At(r), o = r.getAttribute(O);
226
+ i && o !== null && (e[i] = this._parseVisibilityValue(o));
216
227
  }), e;
217
228
  }
218
229
  _parseVisibilityValue(t) {
219
230
  return t === "1" || t === "true";
220
231
  }
221
232
  _mergeWithDefaults(t) {
222
- return Object.entries(M).forEach(([e, r]) => {
233
+ return Object.entries(W).forEach(([e, r]) => {
223
234
  e in t || (t[e] = r);
224
235
  }), t;
225
236
  }
226
237
  _buildVisibilityValues(t) {
227
- return h.reduce((e, r) => (e[`visibility_${r.key}`] = t[r.key] ?? !0, e), {});
238
+ return m.reduce((e, r) => (e[`visibility_${r.key}`] = t[r.key] ?? !0, e), {});
228
239
  }
229
240
  // ========================================================================
230
241
  // UI Rendering (Orderable List)
@@ -238,30 +249,30 @@ class qt extends rt {
238
249
  const r = this._getControlContainer();
239
250
  if (!r)
240
251
  return !1;
241
- const o = r.querySelector("[data-composition-list]");
242
- if (!o)
252
+ const i = r.querySelector("[data-composition-list]");
253
+ if (!i)
243
254
  return !1;
244
- const i = !y.getConfig(this.currentNode).priceMovedToNextLine;
245
- if (!(o.dataset.inlinePrices !== String(i)) && this._tryReorderInPlace(o, t))
255
+ const o = !_.getConfig(this.currentNode).priceMovedToNextLine;
256
+ if (!(i.dataset.inlinePrices !== String(o)) && this._tryReorderInPlace(i, t))
246
257
  return !1;
247
- o.dataset.inlinePrices = String(i);
248
- const n = new Set(e), c = ht(t);
249
- let l = 0, a = 0;
250
- const d = t.map((u) => {
251
- if (i && (u === S || u === N))
252
- return u === c.anchor ? this._createPriceGroupItemHtml(a++, c.originalFirst) : "";
253
- if (Tt.has(u)) {
254
- const p = h.find((A) => A.key === u);
255
- return this._createBuiltInItemHtml(p, a++);
258
+ i.dataset.inlinePrices = String(o);
259
+ const n = new Set(e), l = ft(t);
260
+ let a = 0, d = 0;
261
+ const c = t.map((u) => {
262
+ if (o && (u === C || u === v))
263
+ return u === l.anchor ? this._createPriceGroupItemHtml(d++, l.originalFirst) : "";
264
+ if (Ot.has(u)) {
265
+ const f = m.find((S) => S.key === u);
266
+ return this._createBuiltInItemHtml(f, d++);
256
267
  }
257
- if (u.startsWith(m)) {
258
- const p = u.substring(m.length);
259
- if (n.has(p))
260
- return this._createCustomItemHtml(u, l++, a++);
268
+ if (u.startsWith(I)) {
269
+ const f = w(u);
270
+ if (n.has(f))
271
+ return this._createCustomItemHtml(u, a++, d++);
261
272
  }
262
273
  return "";
263
274
  }).join("");
264
- return this._rescueTogglesToStore(r), this._rescueSelectsToStore(r), this._rescueDeleteButtonsToStore(r), this._rescueReorderIconsToStore(r), o.innerHTML = d, this._moveTogglesIntoItems(r), this._moveSelectsIntoItems(r, e.length), this._moveDeleteButtonsIntoItems(r, e.length), this._moveReorderIconsIntoItems(r, a), !0;
275
+ return this._rescueTogglesToStore(r), this._rescueSelectsToStore(r), this._rescueDeleteButtonsToStore(r), this._rescueReorderIconsToStore(r), i.innerHTML = c, this._moveTogglesIntoItems(r), this._moveSelectsIntoItems(r, e.length), this._moveDeleteButtonsIntoItems(r, e.length), this._moveReorderIconsIntoItems(r, d), !0;
265
276
  }
266
277
  /**
267
278
  * Attempts to reorder existing orderable-item elements to match the composition order.
@@ -273,27 +284,27 @@ class qt extends rt {
273
284
  const r = Array.from(t.querySelectorAll(".orderable-item"));
274
285
  if (r.length !== e.length)
275
286
  return !1;
276
- const o = r.map((l) => l.dataset.key).filter(Boolean);
277
- if (o.length !== e.length)
287
+ const i = r.map((a) => a.dataset.key).filter(Boolean);
288
+ if (i.length !== e.length)
278
289
  return !1;
279
- const i = [...o].sort().join(","), s = [...e].sort().join(",");
280
- if (i !== s || e.some((l) => l.startsWith(m)))
290
+ const o = [...i].sort().join(","), s = [...e].sort().join(",");
291
+ if (o !== s || e.some((a) => a.startsWith(I)))
281
292
  return !1;
282
293
  const n = /* @__PURE__ */ new Map();
283
- r.forEach((l) => {
284
- const { key: a } = l.dataset;
285
- if (a) {
286
- const d = n.get(a) || [];
287
- d.push(l), n.set(a, d);
294
+ r.forEach((a) => {
295
+ const { key: d } = a.dataset;
296
+ if (d) {
297
+ const c = n.get(d) || [];
298
+ c.push(a), n.set(d, c);
288
299
  }
289
300
  });
290
- const c = /* @__PURE__ */ new Map();
291
- return e.forEach((l) => {
292
- const a = n.get(l);
293
- if (!a)
301
+ const l = /* @__PURE__ */ new Map();
302
+ return e.forEach((a) => {
303
+ const d = n.get(a);
304
+ if (!d)
294
305
  return;
295
- const d = c.get(l) || 0;
296
- c.set(l, d + 1), a[d] && t.appendChild(a[d]);
306
+ const c = l.get(a) || 0;
307
+ l.set(a, c + 1), d[c] && t.appendChild(d[c]);
297
308
  }), !0;
298
309
  }
299
310
  _createBuiltInItemHtml(t, e) {
@@ -314,19 +325,19 @@ class qt extends rt {
314
325
  * plain drag handle (no UE button → untouched by the reorder-icon rescue).
315
326
  */
316
327
  _createPriceGroupItemHtml(t, e) {
317
- var n, c;
318
- const r = ((n = h.find((l) => l.key === S)) == null ? void 0 : n.label) ?? "Product Price", o = ((c = h.find((l) => l.key === N)) == null ? void 0 : c.label) ?? "Product Original Price", i = (l, a) => `
319
- <div class="price-suborderable-item" draggable="true" data-subkey="${l}">
320
- <span class="sub-drag-handle">${At}</span>
321
- <span class="item-label">${this.api.translate(a)}</span>
322
- </div>`, s = e ? i(N, o) + i(S, r) : i(S, r) + i(N, o);
328
+ var n, l;
329
+ const r = ((n = m.find((a) => a.key === C)) == null ? void 0 : n.label) ?? "Product Price", i = ((l = m.find((a) => a.key === v)) == null ? void 0 : l.label) ?? "Product Original Price", o = (a, d) => `
330
+ <div class="price-suborderable-item" draggable="true" data-subkey="${a}">
331
+ <span class="sub-drag-handle">${Dt}</span>
332
+ <span class="item-label">${this.api.translate(d)}</span>
333
+ </div>`, s = e ? o(v, i) + o(C, r) : o(C, r) + o(v, i);
323
334
  return `
324
335
  <div class="orderable-item price-group-item" draggable="true"
325
- data-key="${S}" data-group="prices">
336
+ data-key="${C}" data-group="prices">
326
337
  <div class="price-group-header">
327
338
  <span class="drag-handle" data-reorder-icon-slot="${t}"></span>
328
339
  <span class="item-label">${this.api.translate("Product Prices")}</span>
329
- <div class="item-action" data-action-for="${S}"></div>
340
+ <div class="item-action" data-action-for="${C}"></div>
330
341
  </div>
331
342
  <div class="price-suborderable-list">${s}</div>
332
343
  </div>
@@ -346,19 +357,34 @@ class qt extends rt {
346
357
  `;
347
358
  }
348
359
  /**
349
- * Builds select options from the store's filterList.
360
+ * Dropdown label of an attribute. A partner can own a default and a custom
361
+ * attribute under the same name; the type is spelled out only then, so partners
362
+ * without a collision keep clean labels. (SD-147101)
363
+ */
364
+ _toOptionLabel(t, e, r) {
365
+ return r ? `${t} (${this.api.translate(e ? "Default" : "Custom")})` : t;
366
+ }
367
+ /**
368
+ * Builds select options from the store's filterList. Option values are
369
+ * `product-attr` values, so a default attribute and a same-named custom one
370
+ * never collapse onto a single option. (SD-147101)
350
371
  * Falls back to a single option for the currently selected attribute.
351
372
  */
352
373
  _getSelectOptions(t, e = []) {
353
374
  const r = this._getAddableFilters();
354
- if (r.length > 0) {
355
- const i = new Set(e);
356
- return i.delete(t), r.filter((s) => !i.has(s.attributeName)).map((s) => ({
357
- text: s.displayName,
358
- value: s.attributeName
359
- }));
360
- }
361
- return [{ text: this._getDisplayNameForAttribute(t), value: t }];
375
+ if (r.length === 0)
376
+ return [{ text: this._getDisplayNameForAttribute(t), value: t }];
377
+ const i = /* @__PURE__ */ new Map();
378
+ r.forEach((s) => i.set(s.attributeName, (i.get(s.attributeName) ?? 0) + 1));
379
+ const o = new Set(e);
380
+ return o.delete(t), r.filter((s) => !o.has(y(s))).map((s) => ({
381
+ text: this._toOptionLabel(
382
+ s.displayName,
383
+ s.type === "defaultAttribute",
384
+ (i.get(s.attributeName) ?? 0) > 1
385
+ ),
386
+ value: y(s)
387
+ }));
362
388
  }
363
389
  /**
364
390
  * Initializes custom attribute selects: sets their options and current value.
@@ -368,8 +394,8 @@ class qt extends rt {
368
394
  _initializeCustomSelects(t) {
369
395
  t.length !== 0 && setTimeout(() => {
370
396
  t.forEach((e, r) => {
371
- const o = `${I}${r}`, i = this._getSelectOptions(e, t);
372
- this.api.setUIEAttribute(o, D.SELECTPICKER.items, i), this.api.updateValues({ [o]: e });
397
+ const i = `${T}${r}`, o = this._getSelectOptions(e, t);
398
+ this.api.setUIEAttribute(i, N.SELECTPICKER.items, o), this.api.updateValues({ [i]: e });
373
399
  });
374
400
  }, 0);
375
401
  }
@@ -378,11 +404,11 @@ class qt extends rt {
378
404
  * Stripo initializes toggles at template parse time; moving the DOM node preserves bindings.
379
405
  */
380
406
  _moveTogglesIntoItems(t) {
381
- h.forEach((e) => {
382
- const r = t.querySelector(`[data-toggle-key="${e.key}"]`), o = t.querySelector(`[data-action-for="${e.key}"]`);
383
- if (r && o) {
384
- const i = r.querySelector("ue-switcher");
385
- i && o.appendChild(i);
407
+ m.forEach((e) => {
408
+ const r = t.querySelector(`[data-toggle-key="${e.key}"]`), i = t.querySelector(`[data-action-for="${e.key}"]`);
409
+ if (r && i) {
410
+ const o = r.querySelector("ue-switcher");
411
+ o && i.appendChild(o);
386
412
  }
387
413
  });
388
414
  }
@@ -392,9 +418,9 @@ class qt extends rt {
392
418
  */
393
419
  _moveSelectsIntoItems(t, e) {
394
420
  for (let r = 0; r < e; r++) {
395
- const o = `${I}${r}`, i = t.querySelector(`[data-custom-select-key="${o}"]`), s = t.querySelector(`[data-custom-select-slot="${r}"]`);
396
- if (i && s) {
397
- const n = i.querySelector("ue-select");
421
+ const i = `${T}${r}`, o = t.querySelector(`[data-custom-select-key="${i}"]`), s = t.querySelector(`[data-custom-select-slot="${r}"]`);
422
+ if (o && s) {
423
+ const n = o.querySelector("ue-select");
398
424
  n && s.appendChild(n);
399
425
  }
400
426
  }
@@ -406,11 +432,11 @@ class qt extends rt {
406
432
  * previously-moved toggles, making them permanently lost.
407
433
  */
408
434
  _rescueTogglesToStore(t) {
409
- h.forEach((e) => {
410
- const r = t.querySelector(`[data-toggle-key="${e.key}"]`), o = t.querySelector(`[data-action-for="${e.key}"]`);
411
- if (o) {
412
- const i = o.querySelector("ue-switcher");
413
- i && r && r.appendChild(i);
435
+ m.forEach((e) => {
436
+ const r = t.querySelector(`[data-toggle-key="${e.key}"]`), i = t.querySelector(`[data-action-for="${e.key}"]`);
437
+ if (i) {
438
+ const o = i.querySelector("ue-switcher");
439
+ o && r && r.appendChild(o);
414
440
  }
415
441
  });
416
442
  }
@@ -419,11 +445,11 @@ class qt extends rt {
419
445
  * Same rescue pattern as _rescueTogglesToStore — prevents innerHTML from destroying them.
420
446
  */
421
447
  _rescueSelectsToStore(t) {
422
- for (let e = 0; e < _; e++) {
423
- const r = `${I}${e}`, o = t.querySelector(`[data-custom-select-key="${r}"]`), i = t.querySelector(`[data-custom-select-slot="${e}"]`);
424
- if (i) {
425
- const s = i.querySelector("ue-select");
426
- s && o && o.appendChild(s);
448
+ for (let e = 0; e < b; e++) {
449
+ const r = `${T}${e}`, i = t.querySelector(`[data-custom-select-key="${r}"]`), o = t.querySelector(`[data-custom-select-slot="${e}"]`);
450
+ if (o) {
451
+ const s = o.querySelector("ue-select");
452
+ s && i && i.appendChild(s);
427
453
  }
428
454
  }
429
455
  }
@@ -433,9 +459,9 @@ class qt extends rt {
433
459
  */
434
460
  _moveDeleteButtonsIntoItems(t, e) {
435
461
  for (let r = 0; r < e; r++) {
436
- const o = `${R}${r}`, i = t.querySelector(`[data-custom-delete-key="${o}"]`), s = t.querySelector(`[data-custom-delete-slot="${r}"]`);
437
- if (i && s) {
438
- const n = i.querySelector("ue-button");
462
+ const i = `${P}${r}`, o = t.querySelector(`[data-custom-delete-key="${i}"]`), s = t.querySelector(`[data-custom-delete-slot="${r}"]`);
463
+ if (o && s) {
464
+ const n = o.querySelector("ue-button");
439
465
  n && s.appendChild(n);
440
466
  }
441
467
  }
@@ -445,11 +471,11 @@ class qt extends rt {
445
471
  * Same rescue pattern as _rescueSelectsToStore — prevents innerHTML from destroying them.
446
472
  */
447
473
  _rescueDeleteButtonsToStore(t) {
448
- for (let e = 0; e < _; e++) {
449
- const r = `${R}${e}`, o = t.querySelector(`[data-custom-delete-key="${r}"]`), i = t.querySelector(`[data-custom-delete-slot="${e}"]`);
450
- if (i) {
451
- const s = i.querySelector("ue-button");
452
- s && o && o.appendChild(s);
474
+ for (let e = 0; e < b; e++) {
475
+ const r = `${P}${e}`, i = t.querySelector(`[data-custom-delete-key="${r}"]`), o = t.querySelector(`[data-custom-delete-slot="${e}"]`);
476
+ if (o) {
477
+ const s = o.querySelector("ue-button");
478
+ s && i && i.appendChild(s);
453
479
  }
454
480
  }
455
481
  }
@@ -459,9 +485,9 @@ class qt extends rt {
459
485
  */
460
486
  _moveReorderIconsIntoItems(t, e) {
461
487
  for (let r = 0; r < e; r++) {
462
- const o = `${O}${r}`, i = t.querySelector(`[data-reorder-icon-key="${o}"]`), s = t.querySelector(`[data-reorder-icon-slot="${r}"]`);
463
- if (i && s) {
464
- const n = i.querySelector("ue-button");
488
+ const i = `${L}${r}`, o = t.querySelector(`[data-reorder-icon-key="${i}"]`), s = t.querySelector(`[data-reorder-icon-slot="${r}"]`);
489
+ if (o && s) {
490
+ const n = o.querySelector("ue-button");
465
491
  n && s.appendChild(n);
466
492
  }
467
493
  }
@@ -471,12 +497,12 @@ class qt extends rt {
471
497
  * Same rescue pattern as _rescueDeleteButtonsToStore — prevents innerHTML from destroying them.
472
498
  */
473
499
  _rescueReorderIconsToStore(t) {
474
- const e = h.length + _;
500
+ const e = m.length + b;
475
501
  for (let r = 0; r < e; r++) {
476
- const o = `${O}${r}`, i = t.querySelector(`[data-reorder-icon-key="${o}"]`), s = t.querySelector(`[data-reorder-icon-slot="${r}"]`);
502
+ const i = `${L}${r}`, o = t.querySelector(`[data-reorder-icon-key="${i}"]`), s = t.querySelector(`[data-reorder-icon-slot="${r}"]`);
477
503
  if (s) {
478
504
  const n = s.querySelector("ue-button");
479
- n && i && i.appendChild(n);
505
+ n && o && o.appendChild(n);
480
506
  }
481
507
  }
482
508
  }
@@ -488,10 +514,10 @@ class qt extends rt {
488
514
  const { signal: t } = this.eventController, e = this._getControlContainer();
489
515
  if (!e)
490
516
  return;
491
- const r = e.querySelector("[data-composition-list]"), o = e.querySelector("#guido__btn-add-attribute");
492
- r && (this._setupDragAndDrop(r, t), this._setupDeleteHandler(r, t)), o && o.addEventListener("click", () => {
493
- const i = new Set(this._readCustomAttributesFromNode()), s = this._getAddableFilters().find((n) => !i.has(n.attributeName));
494
- s && this._onAddAttribute(s.attributeName, s.displayName);
517
+ const r = e.querySelector("[data-composition-list]"), i = e.querySelector("#guido__btn-add-attribute");
518
+ r && (this._setupDragAndDrop(r, t), this._setupDeleteHandler(r, t)), i && i.addEventListener("click", () => {
519
+ const o = new Set(this._readCustomAttributesFromNode()), s = this._getAddableFilters().find((n) => !o.has(y(n)));
520
+ s && this._onAddAttribute(y(s), s.displayName);
495
521
  }, { signal: t });
496
522
  }
497
523
  /**
@@ -502,11 +528,11 @@ class qt extends rt {
502
528
  _extractCompositionOrder(t) {
503
529
  const e = [];
504
530
  return t.querySelectorAll(".orderable-item").forEach((r) => {
505
- const o = r;
506
- o.dataset.group === "prices" ? o.querySelectorAll(".price-suborderable-item").forEach((i) => {
507
- const s = i.dataset.subkey;
531
+ const i = r;
532
+ i.dataset.group === "prices" ? i.querySelectorAll(".price-suborderable-item").forEach((o) => {
533
+ const s = o.dataset.subkey;
508
534
  s && e.push(s);
509
- }) : o.dataset.key && e.push(o.dataset.key);
535
+ }) : i.dataset.key && e.push(i.dataset.key);
510
536
  }), e;
511
537
  }
512
538
  _setupDragAndDrop(t, e) {
@@ -529,42 +555,42 @@ class qt extends rt {
529
555
  * identically.
530
556
  */
531
557
  _registerDragHandlers(t, e, r) {
532
- let o = null, i = null;
558
+ let i = null, o = null;
533
559
  const s = (n) => n.target.closest(r.itemSelector);
534
560
  t.addEventListener("dragstart", (n) => {
535
- var a;
536
- const c = n.target;
537
- if (r.ignoreSelector && c.closest(r.ignoreSelector))
561
+ var d;
562
+ const l = n.target;
563
+ if (r.ignoreSelector && l.closest(r.ignoreSelector))
538
564
  return;
539
- const l = s(n);
540
- l && (o = l, l.classList.add("dragging"), (a = n.dataTransfer) == null || a.setData("text/plain", l.dataset.key ?? l.dataset.subkey ?? ""));
565
+ const a = s(n);
566
+ a && (i = a, a.classList.add("dragging"), (d = n.dataTransfer) == null || d.setData("text/plain", a.dataset.key ?? a.dataset.subkey ?? ""));
541
567
  }, { signal: e }), t.addEventListener("dragend", () => {
542
- o == null || o.classList.remove("dragging"), i == null || i.classList.remove("drag-over"), o = null, i = null;
568
+ i == null || i.classList.remove("dragging"), o == null || o.classList.remove("drag-over"), i = null, o = null;
543
569
  }, { signal: e }), t.addEventListener("dragover", (n) => {
544
- if (!o)
570
+ if (!i)
545
571
  return;
546
- const c = s(n);
547
- if (!c)
572
+ const l = s(n);
573
+ if (!l)
548
574
  return;
549
575
  n.preventDefault();
550
- const l = c !== o ? c : null;
551
- l !== i && (i == null || i.classList.remove("drag-over"), i = l, i == null || i.classList.add("drag-over"));
576
+ const a = l !== i ? l : null;
577
+ a !== o && (o == null || o.classList.remove("drag-over"), o = a, o == null || o.classList.add("drag-over"));
552
578
  }, { signal: e }), t.addEventListener("drop", (n) => {
553
- var d;
554
- const c = o && s(n);
555
- if (!o || !c || c === o)
579
+ var c;
580
+ const l = i && s(n);
581
+ if (!i || !l || l === i)
556
582
  return;
557
583
  n.preventDefault();
558
- const l = c.getBoundingClientRect(), a = n.clientY < l.top + l.height / 2;
559
- (d = r.getDropParent(c)) == null || d.insertBefore(o, a ? c : c.nextSibling), i == null || i.classList.remove("drag-over"), o.classList.remove("dragging"), o = null, i = null, this._onReorder(this._extractCompositionOrder(t));
584
+ const a = l.getBoundingClientRect(), d = n.clientY < a.top + a.height / 2;
585
+ (c = r.getDropParent(l)) == null || c.insertBefore(i, d ? l : l.nextSibling), o == null || o.classList.remove("drag-over"), i.classList.remove("dragging"), i = null, o = null, this._onReorder(this._extractCompositionOrder(t));
560
586
  }, { signal: e });
561
587
  }
562
588
  _setupDeleteHandler(t, e) {
563
589
  t.addEventListener("click", (r) => {
564
- const i = r.target.closest(".custom-attr-delete");
565
- if (!i)
590
+ const o = r.target.closest(".custom-attr-delete");
591
+ if (!o)
566
592
  return;
567
- const s = i.closest("[data-custom-index]"), n = s == null ? void 0 : s.dataset.customIndex;
593
+ const s = o.closest("[data-custom-index]"), n = s == null ? void 0 : s.dataset.customIndex;
568
594
  n !== void 0 && this._onDeleteCustomAttribute(Number(n));
569
595
  }, { signal: e });
570
596
  }
@@ -572,10 +598,10 @@ class qt extends rt {
572
598
  // Actions (Add, Delete, Reorder)
573
599
  // ========================================================================
574
600
  _onAddAttribute(t, e) {
575
- const r = `${m}${t}`, o = this._readCompositionFromNode();
576
- o.push(r);
577
- const i = [...this._readCustomAttributesFromNode(), t];
578
- this._updateBothAttributes(o, i), this._injectCustomAttributeHtml(t, e, r, o), this._renderOrderableItems(o, i), this._initializeCustomSelects(i), this._updateAddButtonState();
601
+ const r = k(t), i = this._readCompositionFromNode();
602
+ i.push(r);
603
+ const o = [...this._readCustomAttributesFromNode(), t];
604
+ this._updateBothAttributes(i, o), this._injectCustomAttributeHtml(t, e, r, i), this._renderOrderableItems(i, o), this._initializeCustomSelects(o), this._updateAddButtonState();
579
605
  }
580
606
  /**
581
607
  * Removes a single custom attribute by its index in the customAttrs array.
@@ -585,7 +611,7 @@ class qt extends rt {
585
611
  const e = this._readCustomAttributesFromNode();
586
612
  if (e[t] === void 0)
587
613
  return;
588
- const o = this._readCompositionFromNode(), i = this._findNthCustomKeyIndex(o, t), s = o.filter((c, l) => l !== i), n = e.filter((c, l) => l !== t);
614
+ const i = this._readCompositionFromNode(), o = this._findNthCustomKeyIndex(i, t), s = i.filter((l, a) => a !== o), n = e.filter((l, a) => a !== t);
589
615
  this._updateBothAttributes(s, n), this._removeCustomAttributeHtml(s), this._renderOrderableItems(s, n), this._initializeCustomSelects(n), this._updateAddButtonState();
590
616
  }
591
617
  /**
@@ -593,21 +619,21 @@ class qt extends rt {
593
619
  * Uses the customIndex to target only the specific instance, supporting duplicates.
594
620
  */
595
621
  _onCustomAttributeChanged(t, e) {
596
- const r = this._readCustomAttributesFromNode(), o = r[t];
597
- if (o === void 0 || o === e)
622
+ const r = this._readCustomAttributesFromNode(), i = r[t];
623
+ if (i === void 0 || i === e)
598
624
  return;
599
- const i = `${m}${e}`, s = this._readCompositionFromNode(), n = this._findNthCustomKeyIndex(s, t);
600
- n !== -1 && (s[n] = i), r[t] = e;
601
- const c = this._getDisplayNameForAttribute(e);
602
- this._updateBothAttributes(s, r), this._injectCustomAttributeHtml(e, c, i, s), this._renderOrderableItems(s, r), this._initializeCustomSelects(r);
625
+ const o = k(e), s = this._readCompositionFromNode(), n = this._findNthCustomKeyIndex(s, t);
626
+ n !== -1 && (s[n] = o), r[t] = e;
627
+ const l = this._getDisplayNameForAttribute(e);
628
+ this._updateBothAttributes(s, r), this._injectCustomAttributeHtml(e, l, o, s), this._renderOrderableItems(s, r), this._initializeCustomSelects(r);
603
629
  }
604
630
  _onReorder(t) {
605
- const e = t.filter((r) => r.startsWith(m)).map((r) => r.substring(m.length));
631
+ const e = t.filter((r) => r.startsWith(I)).map(w);
606
632
  this.reorderInProgress = !0;
607
633
  try {
608
634
  this._updateBothAttributes(t, e);
609
- const r = !y.getConfig(this.currentNode).priceMovedToNextLine;
610
- this._getCurrentLayout() === "grid" && !r ? this._reorderProductAttributes(t) : this.currentNode && yt({
635
+ const r = !_.getConfig(this.currentNode).priceMovedToNextLine;
636
+ this._getCurrentLayout() === "grid" && !r ? this._reorderProductAttributes(t) : this.currentNode && Nt({
611
637
  currentNode: this.currentNode,
612
638
  documentModifier: this.api.getDocumentModifier()
613
639
  });
@@ -619,67 +645,75 @@ class qt extends rt {
619
645
  // ========================================================================
620
646
  // HTML Injection / Removal (Product Card DOM)
621
647
  // ========================================================================
622
- _injectCustomAttributeHtml(t, e, r, o) {
648
+ /**
649
+ * Outer HTML of an existing attribute row, rewritten to the canonical key when
650
+ * the row still carries a legacy bare-name one. Copying rows this way lets the
651
+ * DOM converge on the next composition change without writing on open.
652
+ */
653
+ _getExistingRowHtml(t, e) {
654
+ const r = Tt(t, e);
655
+ if (r && "getOuterHTML" in r)
656
+ return G(r.getOuterHTML(), e);
657
+ const i = t.querySelector(
658
+ `${R}[${E}="${e}"]`
659
+ );
660
+ return i && "getOuterHTML" in i ? G(i.getOuterHTML(), e) : "";
661
+ }
662
+ _injectCustomAttributeHtml(t, e, r, i) {
623
663
  if (!this.currentNode)
624
664
  return;
625
- this._getCurrentLayout() === "grid" ? this._injectGridAttributeRow(t, e, r, o) : this._injectListAttributeRow(t, e, r, o);
665
+ this._getCurrentLayout() === "grid" ? this._injectGridAttributeRow(t, e, r, i) : this._injectListAttributeRow(t, e, r, i);
626
666
  }
627
- _injectGridAttributeRow(t, e, r, o) {
628
- const i = this.currentNode.querySelectorAll(H);
629
- if (!(i != null && i.length))
667
+ _injectGridAttributeRow(t, e, r, i) {
668
+ const o = this.currentNode.querySelectorAll(U);
669
+ if (!(o != null && o.length))
630
670
  return;
631
- const s = y.getConfig(this.currentNode), c = `0 ${Math.floor(s.columnSpacing / 2)}px`, l = this.api.getDocumentModifier(), a = this.store.recommendationProducts.length;
632
- let d = 0;
633
- i.forEach((u) => {
634
- var w;
635
- const p = u.querySelector($), A = ((w = p == null ? void 0 : p.querySelectorAll(`.${at}`)) == null ? void 0 : w.length) || 1, q = (100 / A).toFixed(2), { bgStyle: G, bgAttr: z } = this._extractSegmentBgFromCard(u), L = a > 0 ? Math.min(A, a - d) : A, Y = o.map((B) => {
636
- if (B === r) {
637
- const X = Array.from(
638
- { length: L },
639
- ($t, Q) => {
640
- const Z = this._resolveAttributeContent(
671
+ const s = _.getConfig(this.currentNode), l = `0 ${Math.floor(s.columnSpacing / 2)}px`, a = this.api.getDocumentModifier(), d = this.store.recommendationProducts.length;
672
+ let c = 0;
673
+ o.forEach((u) => {
674
+ var F;
675
+ const f = u.querySelector(R), S = ((F = f == null ? void 0 : f.querySelectorAll(`.${mt}`)) == null ? void 0 : F.length) || 1, x = (100 / S).toFixed(2), { bgStyle: J, bgAttr: Q } = this._extractSegmentBgFromCard(u), H = d > 0 ? Math.min(S, d - c) : S, Z = i.map((M) => {
676
+ if (M === r) {
677
+ const tt = Array.from(
678
+ { length: H },
679
+ (Lt, rt) => {
680
+ const it = this._resolveAttributeContent(
641
681
  t,
642
682
  e,
643
- d + Q
683
+ c + rt
644
684
  );
645
685
  return this._getGridCellHtml(
646
686
  t,
647
- Z,
648
- q,
649
- G,
650
- z,
651
- c
687
+ it,
688
+ x,
689
+ J,
690
+ Q,
691
+ l
652
692
  );
653
693
  }
654
- ).join(""), J = ut(q, c).repeat(A - L);
655
- return `<tr class="recommendation-attribute-row" ${C}="${r}" ${E}="1">${X}${J}</tr>`;
694
+ ).join(""), et = gt(x, l).repeat(S - H);
695
+ return `<tr class="recommendation-attribute-row" ${E}="${r}" ${O}="1">${tt}${et}</tr>`;
656
696
  }
657
- const P = u.querySelector(
658
- `${$}[${C}="${B}"]`
659
- );
660
- return P && "getOuterHTML" in P ? P.getOuterHTML() : "";
697
+ return this._getExistingRowHtml(u, M);
661
698
  }).join("");
662
- d += L, a > 0 && d >= a && (d = 0), l.modifyHtml(u).setInnerHtml(Y);
663
- }), l.apply(new T(`${this.api.translate("Add custom attribute")}: ${e}`));
699
+ c += H, d > 0 && c >= d && (c = 0), a.modifyHtml(u).setInnerHtml(Z);
700
+ }), a.apply(new A(`${this.api.translate("Add custom attribute")}: ${e}`));
664
701
  }
665
- _injectListAttributeRow(t, e, r, o) {
666
- const i = this.currentNode.querySelectorAll(j);
667
- if (!(i != null && i.length))
702
+ _injectListAttributeRow(t, e, r, i) {
703
+ const o = this.currentNode.querySelectorAll(Y);
704
+ if (!(o != null && o.length))
668
705
  return;
669
- const s = o.filter((c) => c !== k && c !== U), n = this.api.getDocumentModifier();
670
- i.forEach((c, l) => {
671
- const a = s.map((d) => {
672
- if (d === r) {
673
- const p = this._resolveAttributeContent(t, e, l);
674
- return this._getListRowHtml(t, p, r);
706
+ const s = i.filter((l) => l !== B && l !== q), n = this.api.getDocumentModifier();
707
+ o.forEach((l, a) => {
708
+ const d = s.map((c) => {
709
+ if (c === r) {
710
+ const u = this._resolveAttributeContent(t, e, a);
711
+ return this._getListRowHtml(t, u, r);
675
712
  }
676
- const u = c.querySelector(
677
- `${$}[${C}="${d}"]`
678
- );
679
- return u && "getOuterHTML" in u ? u.getOuterHTML() : "";
713
+ return this._getExistingRowHtml(l, c);
680
714
  }).join("");
681
- n.modifyHtml(c).setInnerHtml(a);
682
- }), n.apply(new T(`${this.api.translate("Add custom attribute")}: ${e}`));
715
+ n.modifyHtml(l).setInnerHtml(d);
716
+ }), n.apply(new A(`${this.api.translate("Add custom attribute")}: ${e}`));
683
717
  }
684
718
  /**
685
719
  * Removes a custom attribute by rebuilding product card content without it.
@@ -690,19 +724,19 @@ class qt extends rt {
690
724
  return;
691
725
  const e = this._getCurrentLayout(), r = this.api.getDocumentModifier();
692
726
  if (e === "grid") {
693
- const o = this.currentNode.querySelectorAll(H);
694
- o == null || o.forEach((i) => {
695
- const s = this._buildCompositionHtml(i, t);
696
- r.modifyHtml(i).setInnerHtml(s);
727
+ const i = this.currentNode.querySelectorAll(U);
728
+ i == null || i.forEach((o) => {
729
+ const s = this._buildCompositionHtml(o, t);
730
+ r.modifyHtml(o).setInnerHtml(s);
697
731
  });
698
732
  } else {
699
- const o = t.filter((s) => s !== k && s !== U), i = this.currentNode.querySelectorAll(j);
700
- i == null || i.forEach((s) => {
701
- const n = this._buildCompositionHtml(s, o);
733
+ const i = t.filter((s) => s !== B && s !== q), o = this.currentNode.querySelectorAll(Y);
734
+ o == null || o.forEach((s) => {
735
+ const n = this._buildCompositionHtml(s, i);
702
736
  r.modifyHtml(s).setInnerHtml(n);
703
737
  });
704
738
  }
705
- r.apply(new T(this.api.translate("Remove custom attribute")));
739
+ r.apply(new A(this.api.translate("Remove custom attribute")));
706
740
  }
707
741
  // ========================================================================
708
742
  // DOM Mutation (Block Root Attributes, Reorder)
@@ -718,10 +752,10 @@ class qt extends rt {
718
752
  if (!this.currentNode)
719
753
  return;
720
754
  const r = {
721
- ...y.getConfig(this.currentNode),
755
+ ..._.getConfig(this.currentNode),
722
756
  composition: t
723
757
  };
724
- this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute(W, t.join(",")).setAttribute(F, JSON.stringify(e)).setNodeConfig(r).apply(new T(this.api.translate("Update card composition")));
758
+ this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute(V, t.join(",")).setAttribute(j, JSON.stringify(e)).setNodeConfig(r).apply(new A(this.api.translate("Update card composition")));
725
759
  }
726
760
  /**
727
761
  * Reorders attribute rows within each product card based on composition order.
@@ -730,24 +764,21 @@ class qt extends rt {
730
764
  _reorderProductAttributes(t) {
731
765
  if (!this.currentNode)
732
766
  return;
733
- const e = this.currentNode.querySelectorAll(H);
767
+ const e = this.currentNode.querySelectorAll(U);
734
768
  if (!(e != null && e.length))
735
769
  return;
736
770
  const r = this.api.getDocumentModifier();
737
- e.forEach((o) => {
738
- const i = this._buildCompositionHtml(o, t);
739
- r.modifyHtml(o).setInnerHtml(i);
740
- }), r.apply(new T(this.api.translate("Reorder product attributes")));
771
+ e.forEach((i) => {
772
+ const o = this._buildCompositionHtml(i, t);
773
+ r.modifyHtml(i).setInnerHtml(o);
774
+ }), r.apply(new A(this.api.translate("Reorder product attributes")));
741
775
  }
742
776
  /**
743
777
  * Builds HTML string with attributes ordered according to composition.
744
778
  * Queries existing rows from the container by data-attribute-type.
745
779
  */
746
780
  _buildCompositionHtml(t, e) {
747
- return e.reduce((r, o) => {
748
- const i = t.querySelector(`${$}[${C}="${o}"]`);
749
- return i && "getOuterHTML" in i ? r + i.getOuterHTML() : r;
750
- }, "");
781
+ return e.reduce((r, i) => r + this._getExistingRowHtml(t, i), "");
751
782
  }
752
783
  // ========================================================================
753
784
  // Visibility
@@ -755,19 +786,19 @@ class qt extends rt {
755
786
  _applyVisibilityToBlock(t, e) {
756
787
  if (!this.currentNode)
757
788
  return;
758
- const r = this.currentNode.querySelectorAll(`${$}[${C}="${t}"]`);
789
+ const r = this.currentNode.querySelectorAll(`${R}[${E}="${t}"]`);
759
790
  if (!(r != null && r.length))
760
791
  return;
761
- const o = e ? "1" : "0", i = e ? this.api.translate("visible") : this.api.translate("hidden"), s = `${this.api.translate("Set visibility")}: ${t} → ${i}`, n = this.api.getDocumentModifier();
762
- r.forEach((l) => {
763
- const a = ft(l), d = e ? a : "none";
764
- n.modifyHtml(l).setStyle("display", d).setAttribute(E, o);
792
+ const i = e ? "1" : "0", o = e ? this.api.translate("visible") : this.api.translate("hidden"), s = `${this.api.translate("Set visibility")}: ${t} → ${o}`, n = this.api.getDocumentModifier();
793
+ r.forEach((a) => {
794
+ const d = It(a), c = e ? d : "none";
795
+ n.modifyHtml(a).setStyle("display", c).setAttribute(O, i);
765
796
  });
766
- const c = {
767
- ...y.getConfig(this.currentNode),
797
+ const l = {
798
+ ..._.getConfig(this.currentNode),
768
799
  visibility: { ...this._readVisibilityFromRows(), [t]: e }
769
800
  };
770
- n.modifyHtml(this.currentNode).setNodeConfig(c), n.apply(new T(s));
801
+ n.modifyHtml(this.currentNode).setNodeConfig(l), n.apply(new A(s));
771
802
  }
772
803
  // ========================================================================
773
804
  // Utilities
@@ -779,16 +810,16 @@ class qt extends rt {
779
810
  */
780
811
  _findNthCustomKeyIndex(t, e) {
781
812
  let r = 0;
782
- for (let o = 0; o < t.length; o++)
783
- if (t[o].startsWith(m)) {
813
+ for (let i = 0; i < t.length; i++)
814
+ if (t[i].startsWith(I)) {
784
815
  if (r === e)
785
- return o;
816
+ return i;
786
817
  r++;
787
818
  }
788
819
  return -1;
789
820
  }
790
821
  _getCurrentLayout() {
791
- return this.store.recommendationConfigs.orientation || Ct(this.currentNode);
822
+ return this.store.recommendationConfigs.orientation || Rt(this.currentNode);
792
823
  }
793
824
  /**
794
825
  * Extracts background color properties from existing card elements.
@@ -797,7 +828,7 @@ class qt extends rt {
797
828
  * Used when injecting new attribute cells to match the card's current background.
798
829
  */
799
830
  _extractSegmentBgFromCard(t) {
800
- var o;
831
+ var i;
801
832
  const e = t.querySelector(".product-card-segment");
802
833
  if (e && "getAttribute" in e) {
803
834
  const s = (e.getAttribute("style") || "").match(/background-color:\s*([^;]+)/);
@@ -806,12 +837,12 @@ class qt extends rt {
806
837
  return { bgStyle: `background-color: ${s[1].trim()};`, bgAttr: n };
807
838
  }
808
839
  }
809
- const r = (o = this.currentNode) == null ? void 0 : o.querySelector(".product-card-wrapper");
840
+ const r = (i = this.currentNode) == null ? void 0 : i.querySelector(".product-card-wrapper");
810
841
  if (r && "getStyle" in r) {
811
- const i = r.getStyle("background-color");
812
- if (i && i !== "transparent") {
842
+ const o = r.getStyle("background-color");
843
+ if (o && o !== "transparent") {
813
844
  const s = "getAttribute" in r && r.getAttribute("bgcolor") || "";
814
- return { bgStyle: `background-color: ${i};`, bgAttr: s };
845
+ return { bgStyle: `background-color: ${o};`, bgAttr: s };
815
846
  }
816
847
  }
817
848
  return { bgStyle: "", bgAttr: "" };
@@ -840,11 +871,11 @@ class qt extends rt {
840
871
  * or when all available filters have already been added (no unused attributes left).
841
872
  */
842
873
  _updateAddButtonState() {
843
- const t = this._readCustomAttributesFromNode(), e = t.length >= _, r = new Set(t), o = this._getAddableFilters(), i = o.length > 0 && o.every((s) => r.has(s.attributeName));
874
+ const t = this._readCustomAttributesFromNode(), e = t.length >= b, r = new Set(t), i = this._getAddableFilters(), o = i.length > 0 && i.every((s) => r.has(y(s)));
844
875
  this.api.setUIEAttribute(
845
- K.ADD_ATTRIBUTE,
846
- D.BUTTON.disabled,
847
- e || i ? "true" : "false"
876
+ X.ADD_ATTRIBUTE,
877
+ N.BUTTON.disabled,
878
+ e || o ? "true" : "false"
848
879
  );
849
880
  }
850
881
  /**
@@ -857,54 +888,64 @@ class qt extends rt {
857
888
  this.unsubscribeStore = this.store.$subscribe(() => {
858
889
  const r = this.store.recommendationConfigs.orientation;
859
890
  r !== t && (t = r, this._updateOrderableState());
860
- const o = Object.keys(this.store.filterList).sort().join(",");
861
- o !== e && (e = o, this._initializeComposition());
891
+ const i = Object.keys(this.store.filterList).sort().join(",");
892
+ i !== e && (e = i, this._initializeComposition());
862
893
  });
863
894
  }
864
895
  /**
865
- * Returns filters eligible for the custom attribute dropdown,
866
- * excluding default attributes already covered by built-in toggle items.
896
+ * Returns filters eligible for the custom attribute dropdown, excluding default
897
+ * attributes already covered by built-in toggle items.
898
+ *
899
+ * Deduped by canonical value: a partner can own two backend rows of the same type
900
+ * and name (e.g. two `defaultAttribute` "discount"), which are indistinguishable
901
+ * once reduced to a `product-attr`. Left in, they render as identical options and
902
+ * skew the collision count that decides the (Default)/(Custom) suffix. (SD-147101)
867
903
  */
868
904
  _getAddableFilters() {
869
- return Object.values(this.store.filterList).filter((t) => !(t.type === "defaultAttribute" && nt.has(t.attributeName)));
905
+ const t = /* @__PURE__ */ new Set();
906
+ return Object.values(this.store.filterList).filter((e) => !(e.type === "defaultAttribute" && dt.has(e.attributeName))).filter((e) => {
907
+ const r = y(e);
908
+ return t.has(r) ? !1 : (t.add(r), !0);
909
+ });
870
910
  }
871
911
  /**
872
- * Looks up the display name for an attribute from the store's filterList.
873
- * Falls back to Title Case conversion of the snake_case attribute name.
912
+ * Looks up the display name for a `product-attr` value from the store's filterList.
913
+ * Matches on the canonical value, so a default and a same-named custom attribute
914
+ * resolve to their own entry. Falls back to Title Case of the bare name.
874
915
  */
875
916
  _getDisplayNameForAttribute(t) {
876
- const e = Object.values(this.store.filterList).find((r) => r.attributeName === t);
877
- return e ? e.displayName : pt(t);
917
+ const e = Object.values(this.store.filterList).find((r) => y(r) === t);
918
+ return e ? e.displayName : _t(D(t));
878
919
  }
879
920
  /**
880
921
  * Resolves the display content for a custom attribute cell.
881
922
  * Uses the real product value from the store when available, falls back to displayName.
882
923
  */
883
924
  _resolveAttributeContent(t, e, r) {
884
- var n;
885
- const i = this.store.recommendationProducts[r], s = gt(t, this.store.filterList) ? i == null ? void 0 : i[t] : (n = i == null ? void 0 : i.product_attributes) == null ? void 0 : n[t];
886
- return bt(s) ?? e;
887
- }
888
- _getGridCellHtml(t, e, r, o = "", i = "", s = "") {
889
- const n = `${m}${t}`, c = V(ct, [n], this.store.filterList), l = {
890
- [t]: e,
891
- product_attributes: { [t]: e }
925
+ var l;
926
+ const o = this.store.recommendationProducts[r], s = D(t), n = $t(t) ? o == null ? void 0 : o[s] : (l = o == null ? void 0 : o.product_attributes) == null ? void 0 : l[s];
927
+ return yt(n) ?? e;
928
+ }
929
+ _getGridCellHtml(t, e, r, i = "", o = "", s = "") {
930
+ const n = k(t), l = D(t), a = z(ht, [n], this.store.filterList), d = {
931
+ [l]: e,
932
+ product_attributes: { [l]: e }
892
933
  };
893
- let a = c[n](l);
894
- return a = a.replace("<td", `<td width="${r}%"`), s && (a = a.replace(
895
- `padding: ${dt}`,
934
+ let c = a[n](d);
935
+ return c = c.replace("<td", `<td width="${r}%"`), s && (c = c.replace(
936
+ `padding: ${pt}`,
896
937
  `padding: ${s}`
897
- )), o && (a = a.replace(/style="table-layout: fixed;"/, `style="table-layout: fixed; ${o}"`)), i && (a = a.replace(/border="0"/, `border="0" bgcolor="${i}"`)), a;
938
+ )), i && (c = c.replace(/style="table-layout: fixed;"/, `style="table-layout: fixed; ${i}"`)), o && (c = c.replace(/border="0"/, `border="0" bgcolor="${o}"`)), c;
898
939
  }
899
940
  _getListRowHtml(t, e, r) {
900
- const o = V(mt, [r], this.store.filterList), i = {
901
- [t]: e,
902
- product_attributes: { [t]: e }
903
- }, n = o[r](i).replace(/<tr>/, "").replace(/<\/tr>/, "");
904
- return `<tr class="recommendation-attribute-row" ${C}="${r}" ${E}="1">${n}</tr>`;
941
+ const i = D(t), o = z(bt, [r], this.store.filterList), s = {
942
+ [i]: e,
943
+ product_attributes: { [i]: e }
944
+ }, l = o[r](s).replace(/<tr>/, "").replace(/<\/tr>/, "");
945
+ return `<tr class="recommendation-attribute-row" ${E}="${r}" ${O}="1">${l}</tr>`;
905
946
  }
906
947
  }
907
948
  export {
908
- St as COMPOSITION_CONTROL_BLOCK_ID,
909
- qt as RecommendationCardCompositionControl
949
+ Et as COMPOSITION_CONTROL_BLOCK_ID,
950
+ Wt as RecommendationCardCompositionControl
910
951
  };