@useinsider/guido 3.0.0 → 3.1.0

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 (101) hide show
  1. package/dist/components/organisms/header/EditorActions.vue.js +10 -8
  2. package/dist/components/organisms/header/EditorActions.vue2.js +41 -31
  3. package/dist/components/organisms/header/MigrationConfirmModal.vue.js +17 -0
  4. package/dist/components/organisms/header/MigrationConfirmModal.vue2.js +39 -0
  5. package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +1 -1
  6. package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +1 -1
  7. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +1 -1
  8. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +2 -2
  9. package/dist/config/compiler/unsubscribeCompilerRules.js +14 -14
  10. package/dist/config/compiler/utils/recommendationCompilerUtils.js +29 -18
  11. package/dist/config/i18n/en/labels.json.js +8 -3
  12. package/dist/config/migrator/itemsBlockMigrator.js +135 -131
  13. package/dist/config/migrator/recommendationMigrator.js +58 -54
  14. package/dist/enums/block.js +4 -0
  15. package/dist/extensions/Blocks/Items/block.js +30 -21
  16. package/dist/extensions/Blocks/Items/iconsRegistry.js +7 -6
  17. package/dist/extensions/Blocks/Items/items.css.js +48 -0
  18. package/dist/extensions/Blocks/Recommendation/block.js +64 -34
  19. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  20. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  21. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +36 -34
  22. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +15 -12
  23. package/dist/extensions/Blocks/Recommendation/controls/cardBackground/index.js +4 -4
  24. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +693 -144
  25. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/index.js +78 -0
  26. package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +15 -15
  27. package/dist/extensions/Blocks/Recommendation/controls/main/currency.js +24 -24
  28. package/dist/extensions/Blocks/Recommendation/controls/main/filters.js +2 -2
  29. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +107 -78
  30. package/dist/extensions/Blocks/Recommendation/controls/{layout/index.js → main/layoutOrientation.js} +34 -23
  31. package/dist/extensions/Blocks/Recommendation/controls/main/locale.js +2 -2
  32. package/dist/extensions/Blocks/Recommendation/controls/main/productCount.js +58 -0
  33. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +150 -64
  34. package/dist/extensions/Blocks/Recommendation/controls/main/shuffle.js +2 -2
  35. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +202 -200
  36. package/dist/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.js +25 -8
  37. package/dist/extensions/Blocks/Recommendation/controls/name/textTrim.js +6 -5
  38. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textAfter.js +8 -8
  39. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textBefore.js +21 -21
  40. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textAfter.js +13 -13
  41. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textBefore.js +17 -17
  42. package/dist/extensions/Blocks/Recommendation/controls/spacing/index.js +94 -100
  43. package/dist/extensions/Blocks/Recommendation/controls/syncInfoMessage.js +65 -0
  44. package/dist/extensions/Blocks/Recommendation/extension.js +20 -18
  45. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +5 -4
  46. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +209 -2
  47. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +135 -111
  48. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +9 -7
  49. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +63 -34
  50. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +25 -28
  51. package/dist/extensions/Blocks/Recommendation/templates/index.js +8 -8
  52. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +28 -13
  53. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +25 -44
  54. package/dist/extensions/Blocks/Recommendation/templates/utils.js +62 -38
  55. package/dist/extensions/Blocks/common-control.js +96 -39
  56. package/dist/guido.css +1 -1
  57. package/dist/src/@types/extensions/block.d.ts +2 -0
  58. package/dist/src/App.vue.d.ts +3 -1
  59. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  60. package/dist/src/components/organisms/header/MigrationConfirmModal.vue.d.ts +6 -0
  61. package/dist/src/components/wrappers/WpModal.vue.d.ts +2 -2
  62. package/dist/src/enums/block.d.ts +4 -0
  63. package/dist/src/extensions/Blocks/Items/block.d.ts +3 -1
  64. package/dist/src/extensions/Blocks/Recommendation/block.d.ts +4 -1
  65. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +2 -1
  66. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +9 -1
  67. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  68. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +10 -0
  69. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +134 -44
  70. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/index.d.ts +105 -0
  71. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +3 -2
  72. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +5 -1
  73. package/dist/src/extensions/Blocks/Recommendation/controls/{layout/index.d.ts → main/layoutOrientation.d.ts} +3 -3
  74. package/dist/src/extensions/Blocks/Recommendation/controls/main/productCount.d.ts +28 -0
  75. package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +38 -20
  76. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +6 -2
  77. package/dist/src/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.d.ts +23 -1
  78. package/dist/src/extensions/Blocks/Recommendation/controls/spacing/index.d.ts +8 -18
  79. package/dist/src/extensions/Blocks/Recommendation/controls/syncInfoMessage.d.ts +34 -0
  80. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +2 -0
  81. package/dist/src/extensions/Blocks/Recommendation/templates/grid/elementRenderer.d.ts +1 -1
  82. package/dist/src/extensions/Blocks/Recommendation/templates/list/elementRenderer.d.ts +1 -1
  83. package/dist/src/extensions/Blocks/Recommendation/templates/list/template.d.ts +10 -4
  84. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +37 -2
  85. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +13 -0
  86. package/dist/src/extensions/Blocks/common-control.d.ts +29 -2
  87. package/dist/src/stores/template.d.ts +29 -0
  88. package/dist/src/utils/migrationBannerHtml.d.ts +2 -0
  89. package/dist/static/assets/info.svg.js +5 -0
  90. package/dist/static/styles/components/wide-panel.css.js +1 -0
  91. package/dist/static/styles/customEditorStyle.css.js +9 -0
  92. package/dist/static/styles/variables.css.js +3 -0
  93. package/dist/stores/template.js +15 -0
  94. package/dist/utils/migrationBannerHtml.js +21 -0
  95. package/package.json +3 -2
  96. package/dist/src/extensions/Blocks/Recommendation/controls/cardBackgroundColorControl.d.ts +0 -25
  97. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscountTextAfterControl.d.ts +0 -15
  98. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscountTextBeforeControl.d.ts +0 -15
  99. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPriceTextAfterControl.d.ts +0 -15
  100. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPriceTextBeforeControl.d.ts +0 -15
  101. package/dist/src/extensions/Blocks/Recommendation/controls/spacingControl.d.ts +0 -60
@@ -1,8 +1,8 @@
1
- var I = Object.defineProperty;
2
- var O = (T, r, e) => r in T ? I(T, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : T[r] = e;
3
- var u = (T, r, e) => O(T, typeof r != "symbol" ? r + "" : r, e);
4
- import { Control as c, UIElementType as n, UEAttr as $ } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- class _ extends c {
1
+ var c = Object.defineProperty;
2
+ var i = (s, r, e) => r in s ? c(s, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[r] = e;
3
+ var u = (s, r, e) => i(s, typeof r != "symbol" ? r + "" : r, e);
4
+ import { Control as I, UIElementType as n, UEAttr as $ } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ class d extends I {
6
6
  constructor() {
7
7
  super(...arguments);
8
8
  u(this, "currentNode");
@@ -47,17 +47,66 @@ class _ extends c {
47
47
  const t = this.getBlockInstanceId();
48
48
  if (!t)
49
49
  return !1;
50
- const E = t !== this.lastBlockInstanceId;
51
- return e(), E && (this.lastBlockInstanceId = t), E;
50
+ const o = t !== this.lastBlockInstanceId;
51
+ return e(), o && (this.lastBlockInstanceId = t), o;
52
52
  }
53
- _GuLabel({ text: e, name: t = "" }) {
53
+ _GuLabel({ text: e, name: t = "", hint: o }) {
54
54
  return `
55
- <${n.LABEL}
56
- ${$.LABEL.text}="${e}"
57
- ${$.LABEL.name}="${t || `${e} Label`}">
55
+ <${n.LABEL}
56
+ ${$.LABEL.text}="${e}"
57
+ ${$.LABEL.name}="${t || `${e} Label`}"
58
+ ${o ? `${$.LABEL.hint}="${o}"` : ""}>
58
59
  </${n.LABEL}>
59
60
  `;
60
61
  }
62
+ _GuMessage({ name: e = "", type: t = "info" }) {
63
+ return `
64
+ <${n.MESSAGE}
65
+ ${$.MESSAGE.type}="${t}"
66
+ ${$.MESSAGE.name}="${e}"
67
+ >
68
+ </${n.MESSAGE}>
69
+ `;
70
+ }
71
+ /**
72
+ * Returns template HTML for an info message with an optional icon.
73
+ * Use in getTemplate() — the icon is registered in a hidden store so Stripo initializes it.
74
+ * Pair with _setInfoMessageValue() in onRender() to set the text content.
75
+ */
76
+ _GuOnPageMessage({ name: e, icon: t = "not-found", type: o = "info" }) {
77
+ return `
78
+ <div data-info-message="${e}" style="display: none; padding: 16px 16px 0;">
79
+ <div class="icon-store" style="display: none;">
80
+ <div data-icon-key="${e}">
81
+ ${this._GuIcon({ src: t, className: "icon" })}
82
+ </div>
83
+ </div>
84
+ ${this._GuMessage({ name: e, type: o })}
85
+ </div>
86
+ `;
87
+ }
88
+ /**
89
+ * Sets the info message value with the pre-registered icon.
90
+ * Use in onRender() — reads the icon's outerHTML from the hidden store
91
+ * and combines it with the text in Stripo's alert-message layout.
92
+ */
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) ?? "";
95
+ this.api.setUIEAttribute(
96
+ e,
97
+ "value",
98
+ `
99
+ <div class="service-element alert-message-main" style="align-items: start;">
100
+ ${l}
101
+ <div class="service-element alert-message-text">
102
+ ${t}
103
+ </div>
104
+ </div>
105
+ `
106
+ );
107
+ const T = o.querySelector(`[data-info-message="${e}"]`);
108
+ T && (T.style.display = "");
109
+ }
61
110
  _GuToggle(e) {
62
111
  return `
63
112
  <${n.SWITCHER}
@@ -72,72 +121,80 @@ class _ extends c {
72
121
  ${$.SELECT_ITEM.value}="${t}">
73
122
  </${n.SELECT_ITEM}>`;
74
123
  }
75
- _GuSelect({ name: e, placeholder: t, options: E, className: o = "es-180w" }) {
124
+ _GuSelect({ name: e, placeholder: t, options: o, className: E = "es-180w" }) {
76
125
  return `
77
126
  <${n.SELECTPICKER}
78
- class="${o}"
127
+ class="${E}"
79
128
  ${$.SELECTPICKER.name}="${e}"
80
129
  ${$.SELECTPICKER.placeholder}="${t}">
81
- ${E.map((a) => this._GuSelectItem(a)).join("")}
130
+ ${o.map((a) => this._GuSelectItem(a)).join("")}
82
131
  </${n.SELECTPICKER}>
83
132
  `;
84
133
  }
85
- _GuTextInput({ name: e, placeholder: t, className: E = "", disabled: o = !1 }) {
134
+ _GuTextInput({ name: e, placeholder: t, className: o = "", disabled: E = !1 }) {
86
135
  return `
87
136
  <${n.TEXT}
88
- class=${E}
137
+ class=${o}
89
138
  ${$.TEXT.name}="${e}"
90
139
  placeholder="${t || e}"
91
- ${o ? `${$.TEXT.disabled}="true"` : ""}>
140
+ ${E ? `${$.TEXT.disabled}="true"` : ""}>
92
141
  </${n.TEXT}>
93
142
  `;
94
143
  }
95
- _GuCounter({ name: e, maxValue: t, minValue: E = 1, step: o = 1 }) {
144
+ _GuCounter({ name: e, maxValue: t, minValue: o = 1, step: E = 1 }) {
96
145
  return `
97
146
  <${n.COUNTER}
98
147
  ${$.COUNTER.name}="${e}"
99
- ${$.COUNTER.minValue}="${E}"
148
+ ${$.COUNTER.minValue}="${o}"
100
149
  ${$.COUNTER.maxValue}="${t}"
101
- ${$.COUNTER.step}="${o}">
150
+ ${$.COUNTER.step}="${E}">
102
151
  </${n.COUNTER}>
103
152
  `;
104
153
  }
105
- _GuRadioButtonItem({ text: e, value: t, icon: E }) {
154
+ _GuRadioButtonItem({ text: e, value: t, icon: o }) {
106
155
  return `
107
156
  <${n.RADIO_ITEM}
108
157
  ${$.RADIO_ITEM.value}="${t}"
109
158
  ${e ? `${$.RADIO_ITEM.text}="${e}"` : ""}
110
- ${E ? `${$.RADIO_ITEM.icon}="${E}"` : ""}>
159
+ ${o ? `${$.RADIO_ITEM.icon}="${o}"` : ""}>
111
160
  </${n.RADIO_ITEM}>
112
161
  `;
113
162
  }
114
- _GuRadioButton({ name: e, buttons: t, id: E = "" }) {
163
+ _GuRadioButton({ name: e, buttons: t, id: o = "" }) {
115
164
  return `
116
165
  <${n.RADIO_BUTTONS}
117
- ${E ? `id="${E}"` : ""}
166
+ ${o ? `id="${o}"` : ""}
118
167
  ${$.RADIO_BUTTONS.name}="${e}">
119
- ${t.map((o) => this._GuRadioButtonItem(o)).join("")}
168
+ ${t.map((E) => this._GuRadioButtonItem(E)).join("")}
120
169
  </${n.RADIO_BUTTONS}>
121
170
  `;
122
171
  }
123
- _GuButton({ name: e, label: t, id: E = "" }) {
172
+ _GuButton({ name: e, label: t, id: o = "" }) {
124
173
  return `
125
174
  <${n.BUTTON}
126
175
  ${$.BUTTON.name}="${e}"
127
- ${E ? `id="${E}"` : ""}
176
+ ${o ? `id="${o}"` : ""}
128
177
  ${$.BUTTON.caption}="${t}"}>
129
178
  </${n.BUTTON}>
130
179
  `;
131
180
  }
181
+ _GuIcon({ src: e, className: t = "" }) {
182
+ return `
183
+ <${n.ICON}
184
+ ${$.ICON.src}="${e}"
185
+ ${t ? `class="${t}"` : ""}>
186
+ </${n.ICON}>
187
+ `;
188
+ }
132
189
  /**
133
190
  *
134
191
  * @param param0
135
192
  * @returns It returns a button with an icon.
136
193
  */
137
- _GuIconButton({ name: e, icon: t, className: E = "" }) {
194
+ _GuIconButton({ name: e, icon: t, className: o = "" }) {
138
195
  return `
139
196
  <${n.BUTTON}
140
- class="${E}"
197
+ class="${o}"
141
198
  ${$.BUTTON.name}="${e}"
142
199
  ${$.BUTTON.icon}="${t}">
143
200
  </${n.BUTTON}>
@@ -165,19 +222,19 @@ class _ extends c {
165
222
  * @returns HTML string for the orderable control
166
223
  */
167
224
  _GuOrderable(e, t) {
168
- let E = "";
225
+ let o = "";
169
226
  t.forEach((a) => {
170
227
  const l = $.ORDERABLE_ITEM && "name" in $.ORDERABLE_ITEM ? $.ORDERABLE_ITEM.name : "name";
171
- E += `
228
+ o += `
172
229
  <${n.ORDERABLE_ITEM} ${l}="${a.key}">
173
230
  ${a.content}
174
231
  </${n.ORDERABLE_ITEM}>
175
232
  `;
176
233
  });
177
- const o = $.ORDERABLE && "name" in $.ORDERABLE ? $.ORDERABLE.name : "name";
234
+ const E = $.ORDERABLE && "name" in $.ORDERABLE ? $.ORDERABLE.name : "name";
178
235
  return `
179
- <${n.ORDERABLE} ${o}="${e}">
180
- ${E}
236
+ <${n.ORDERABLE} ${E}="${e}">
237
+ ${o}
181
238
  </${n.ORDERABLE}>
182
239
  `;
183
240
  }
@@ -218,16 +275,16 @@ class _ extends c {
218
275
  `;
219
276
  }
220
277
  _GuRadioButtons(e, t) {
221
- const E = t.map((o) => `
278
+ const o = t.map((E) => `
222
279
  <${n.RADIO_ITEM}
223
- ${$.RADIO_ITEM.text}="${o.text}"
224
- ${$.RADIO_ITEM.value}="${o.value}">
280
+ ${$.RADIO_ITEM.text}="${E.text}"
281
+ ${$.RADIO_ITEM.value}="${E.value}">
225
282
  </${n.RADIO_ITEM}>
226
283
  `).join("");
227
284
  return `
228
285
  <${n.RADIO_BUTTONS}
229
286
  ${$.RADIO_BUTTONS.name}="${e}">
230
- ${E}
287
+ ${o}
231
288
  </${n.RADIO_BUTTONS}>
232
289
  `;
233
290
  }
@@ -241,5 +298,5 @@ class _ extends c {
241
298
  }
242
299
  }
243
300
  export {
244
- _ as CommonControl
301
+ d as CommonControl
245
302
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- .gap-16[data-v-3b53a736],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-079d2bf7] .in-progress-wrapper__progress p span:last-child{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.editor-actions[data-v-17dd4d8b]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-a26d7792]{position:relative;width:100%;height:calc(100vh - 128px)}.guido-editor__container[data-v-a26d7792]{width:100%;height:calc(100vh - 128px)}.guido-editor__no-header[data-v-a26d7792]{height:calc(100vh - 75px)}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-d3c52b44] .vueperslides__bullets,[data-v-dd1a237a] .vueperslides__bullets{pointer-events:none!important}[data-v-dd1a237a] .vueperslides__parallax-wrapper{height:110px!important}[data-v-a408dcea] .vueperslides__bullets{pointer-events:none!important}[data-v-a408dcea] .vueperslides__parallax-wrapper{height:110px!important}
1
+ .gap-16[data-v-3b53a736],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-079d2bf7] .in-progress-wrapper__progress p span:last-child{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.editor-actions[data-v-acff76a8]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-a26d7792]{position:relative;width:100%;height:calc(100vh - 128px)}.guido-editor__container[data-v-a26d7792]{width:100%;height:calc(100vh - 128px)}.guido-editor__no-header[data-v-a26d7792]{height:calc(100vh - 75px)}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-29b9af29] .vueperslides__bullets,[data-v-dd1a237a] .vueperslides__bullets{pointer-events:none!important}[data-v-dd1a237a] .vueperslides__parallax-wrapper{height:110px!important}[data-v-d073b1dc] .vueperslides__bullets{pointer-events:none!important}[data-v-d073b1dc] .vueperslides__parallax-wrapper{height:110px!important}
@@ -0,0 +1,2 @@
1
+ import { BlockId as BlockIdEnum } from '@@/Enums/block';
2
+ export type BlockId = `${BlockIdEnum}`;
@@ -1,3 +1,5 @@
1
- import '../node_modules/@useinsider/design-system-vue/dist/design-system-vue.css';
1
+ import '@useinsider/design-system-vue/style';
2
+ import '@useinsider/design-system-vue/fonts.css';
3
+ import '@useinsider/design-system-vue/utilities.css';
2
4
  declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
3
5
  export default _default;
@@ -1,4 +1,4 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
- handleSave: (isSilent: boolean) => Promise<Omit<import("../../../@types/stripo").SavedTemplateDetails, "metadata"> | undefined>;
2
+ handleSave: (isSilent: boolean) => Promise<Omit<import("../../../@types/stripo.js").SavedTemplateDetails, "metadata"> | undefined> | undefined;
3
3
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
4
4
  export default _default;
@@ -0,0 +1,6 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {
2
+ open: () => void;
3
+ }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
4
+ confirm: () => void;
5
+ }, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
6
+ export default _default;
@@ -3,7 +3,7 @@ type __VLS_Props = {
3
3
  id: string;
4
4
  title: string;
5
5
  description?: string;
6
- size?: 'small' | 'medium' | 'large';
6
+ size?: 'X-small' | 'small' | 'medium' | 'large';
7
7
  footerButtonOptions?: FooterButtonGroup;
8
8
  closeButtonStatus?: boolean;
9
9
  closeOnOutsideClick?: boolean;
@@ -33,7 +33,7 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<
33
33
  footerStatus: boolean;
34
34
  }>>>, {
35
35
  description: string;
36
- size: "small" | "medium" | "large";
36
+ size: "X-small" | "small" | "medium" | "large";
37
37
  closeOnOutsideClick: boolean;
38
38
  footerButtonOptions: FooterButtonGroup;
39
39
  closeButtonStatus: boolean;
@@ -0,0 +1,4 @@
1
+ export declare enum BlockId {
2
+ Items = "items-block",
3
+ Recommendation = "recommendation-block"
4
+ }
@@ -1,5 +1,6 @@
1
+ import type { BlockId } from '@@/Types/extensions/block';
1
2
  import { Block, BlockCompositionType, ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
2
- export declare const BLOCK_ID = "items-block";
3
+ export declare const BLOCK_ID: BlockId;
3
4
  /**
4
5
  * Items Block extension for Stripo email editor.
5
6
  *
@@ -20,6 +21,7 @@ export declare class ItemsBlock extends Block {
20
21
  getBlockCompositionType(): BlockCompositionType;
21
22
  getName(): string;
22
23
  getDescription(): string;
24
+ getSettingsPanelTitleHtml(): string;
23
25
  getTemplate(): string;
24
26
  allowInnerBlocksDND(): boolean;
25
27
  onCreated(node: ImmutableHtmlNode): void;
@@ -5,9 +5,10 @@
5
5
  * Handles block lifecycle including configuration initialization and migration.
6
6
  * Supports multiple block instances with unique recommendation-id attributes.
7
7
  */
8
+ import type { BlockId } from '@@/Types/extensions/block';
8
9
  import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
9
10
  import { Block, BlockCompositionType } from '@stripoinc/ui-editor-extensions';
10
- export declare const BLOCK_ID = "recommendation-block";
11
+ export declare const BLOCK_ID: BlockId;
11
12
  export declare class RecommendationBlock extends Block {
12
13
  /**
13
14
  * Stores the ID generated in getTemplate() so onCreated() can reuse it.
@@ -20,6 +21,8 @@ export declare class RecommendationBlock extends Block {
20
21
  getBlockCompositionType(): BlockCompositionType;
21
22
  getName(): string;
22
23
  getDescription(): string;
24
+ getSettingsPanelTitleHtml(): string;
25
+ allowInnerBlocksDND(): boolean;
23
26
  /**
24
27
  * Returns the template HTML for a new recommendation block.
25
28
  * Generates a unique recommendation ID and embeds the instance class
@@ -9,5 +9,6 @@ export declare enum RecommendationBlockId {
9
9
  OLD_PRICE = "recommendation-block-old-price",
10
10
  OMNIBUS_PRICE = "recommendation-block-omnibus-price",
11
11
  OMNIBUS_DISCOUNT = "recommendation-block-omnibus-discount",
12
- IMAGE = "recommendation-block-image"
12
+ IMAGE = "recommendation-block-image",
13
+ CUSTOM_ATTRIBUTE = "recommendation-block-custom-attribute"
13
14
  }
@@ -55,5 +55,13 @@ export declare enum RecommendationControlId {
55
55
  OMNIBUS_DISCOUNT_TEXT_BEFORE = "recommendation-block-omnibus-discount-text-before-control",
56
56
  OMNIBUS_DISCOUNT_TEXT_AFTER = "recommendation-block-omnibus-discount-text-after-control",
57
57
  IMAGE_SIZE = "recommendation-block-image-size-control",
58
- IMAGE_MARGINS = "recommendation-block-image-margins-control"
58
+ IMAGE_MARGINS = "recommendation-block-image-margins-control",
59
+ CUSTOM_ATTR_ALIGN = "recommendation-block-custom-attr-align-control",
60
+ CUSTOM_ATTR_BACKGROUND = "recommendation-block-custom-attr-background-control",
61
+ CUSTOM_ATTR_COLOR = "recommendation-block-custom-attr-color-control",
62
+ CUSTOM_ATTR_FONT_FAMILY = "recommendation-block-custom-attr-font-family-control",
63
+ CUSTOM_ATTR_PADDINGS = "recommendation-block-custom-attr-paddings-control",
64
+ CUSTOM_ATTR_SIZE = "recommendation-block-custom-attr-size-control",
65
+ CUSTOM_ATTR_STYLE = "recommendation-block-custom-attr-style-control",
66
+ SYNC_INFO_MESSAGE = "recommendation-block-sync-info-message"
59
67
  }
@@ -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, 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, } from './selectors';
11
+ export { BLOCK_ROOT_SELECTOR, CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, MOBILE_ROW_SELECTOR, 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, } 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';
@@ -40,3 +40,13 @@ export declare const ATTR_PRODUCT_OLD_PRICE = "productOldPrice";
40
40
  export declare const ATTR_PRODUCT_OMNIBUS_PRICE = "productOmnibusPrice";
41
41
  export declare const ATTR_PRODUCT_OMNIBUS_DISCOUNT = "productOmnibusDiscount";
42
42
  export declare const ATTR_PRODUCT_BUTTON = "productButton";
43
+ /**
44
+ * Custom attribute constants
45
+ * Used for dynamically added product attributes in Card Composition
46
+ */
47
+ /** Prefix for custom attribute keys in composition arrays (e.g., "customAttr:rating_star") */
48
+ export declare const ATTR_CUSTOM_PREFIX = "customAttr:";
49
+ /** HTML data attribute storing JSON array of added custom attribute names */
50
+ export declare const ATTR_DATA_CUSTOM_ATTRIBUTES = "data-custom-attributes";
51
+ /** HTML attribute on <td> elements identifying the product attribute for compiler template variable generation */
52
+ export declare const ATTR_PRODUCT_ATTR = "product-attr";
@@ -4,92 +4,182 @@ export declare const COMPOSITION_CONTROL_BLOCK_ID = "ui-elements-recommendation-
4
4
  export interface CardCompositionItem {
5
5
  key: string;
6
6
  label: string;
7
- visible: boolean;
8
7
  }
9
8
  export declare class RecommendationCardCompositionControl extends CommonControl {
10
9
  private store;
11
- private unsubscribeOrientation;
10
+ private unsubscribeStore;
11
+ private eventController;
12
+ /**
13
+ * Guard flag: when true, onTemplateNodeUpdated skips _initializeComposition.
14
+ * Used during _onReorder to prevent multiple intermediate rebuilds.
15
+ */
16
+ private reorderInProgress;
12
17
  getId(): string;
13
18
  getTemplate(): string;
14
19
  onRender(): void;
15
20
  onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
21
+ onDestroy(): void;
22
+ private _initializeComposition;
23
+ private _registerValueChangeListeners;
24
+ private _readCompositionFromNode;
25
+ private _readCustomAttributesFromNode;
26
+ private _readVisibilityFromRows;
27
+ private _getDefaultVisibilities;
28
+ private _extractVisibilityFromRows;
29
+ private _parseVisibilityValue;
30
+ private _mergeWithDefaults;
31
+ private _buildVisibilityValues;
16
32
  /**
17
- * Creates the HTML content for an orderable item with label and toggle
33
+ * Renders orderable items into the composition list.
34
+ * Returns `true` if a full DOM rebuild occurred, `false` if existing
35
+ * elements were reordered in-place (preserving UE-SELECT state).
18
36
  */
19
- private _createItemContent;
37
+ private _renderOrderableItems;
20
38
  /**
21
- * Registers event listeners for composition and visibility changes
39
+ * Attempts to reorder existing orderable-item elements to match the composition order.
40
+ * If the list already contains the same set of items (possibly in a different order),
41
+ * performs a lightweight DOM reorder instead of a full innerHTML rebuild.
42
+ * Returns true if reorder was performed, false if a full rebuild is needed.
22
43
  */
23
- private _registerValueChangeListeners;
44
+ private _tryReorderInPlace;
45
+ private _createBuiltInItemHtml;
46
+ private _createCustomItemHtml;
24
47
  /**
25
- * Initializes composition order and visibility state from the current node
48
+ * Builds select options from the store's filterList.
49
+ * Falls back to a single option for the currently selected attribute.
26
50
  */
27
- private _initializeComposition;
51
+ private _getSelectOptions;
28
52
  /**
29
- * Reads composition order from node's data-card-composition attribute
30
- * Falls back to default order if attribute is not present
53
+ * Initializes custom attribute selects: sets their options and current value.
54
+ * Selects are pre-allocated in getTemplate() and moved into items by
55
+ * _moveSelectsIntoItems — this method only updates their data.
31
56
  */
32
- private _readCompositionFromNode;
57
+ private _initializeCustomSelects;
33
58
  /**
34
- * Builds visibility values object from the visibility map
59
+ * Moves UE-SWITCHER elements from the hidden toggle store into orderable items.
60
+ * Stripo initializes toggles at template parse time; moving the DOM node preserves bindings.
35
61
  */
36
- private _buildVisibilityValues;
62
+ private _moveTogglesIntoItems;
37
63
  /**
38
- * Read visibility state from individual row elements' data-visibility attributes
39
- * This ensures toggles reflect the actual DOM state
64
+ * Moves pre-allocated UE-SELECT elements from the hidden custom-select-store
65
+ * into orderable item slots. Same pattern as _moveTogglesIntoItems.
40
66
  */
41
- private _readVisibilityFromRows;
67
+ private _moveSelectsIntoItems;
42
68
  /**
43
- * Returns default visibility values for all items
69
+ * Rescues UE-SWITCHER elements from orderable items back to the hidden toggle store.
70
+ * Must be called before re-rendering the list with innerHTML to prevent toggle destruction.
71
+ * Without this, onTemplateNodeUpdated → _renderOrderableItems → innerHTML would destroy
72
+ * previously-moved toggles, making them permanently lost.
44
73
  */
45
- private _getDefaultVisibilities;
74
+ private _rescueTogglesToStore;
46
75
  /**
47
- * Extracts visibility values from DOM nodes
76
+ * Rescues UE-SELECT elements from orderable items back to the hidden custom-select-store.
77
+ * Same rescue pattern as _rescueTogglesToStore — prevents innerHTML from destroying them.
48
78
  */
49
- private _extractVisibilityFromRows;
79
+ private _rescueSelectsToStore;
50
80
  /**
51
- * Parses visibility value from string to boolean
52
- * Accepts "1", "true" as true, everything else as false
81
+ * Moves pre-allocated UE-BUTTON delete elements from the hidden custom-delete-store
82
+ * into orderable item slots. Same pattern as _moveSelectsIntoItems.
53
83
  */
54
- private _parseVisibilityValue;
84
+ private _moveDeleteButtonsIntoItems;
55
85
  /**
56
- * Merges extracted visibilities with default values for missing keys
86
+ * Rescues UE-BUTTON delete elements from orderable items back to the hidden custom-delete-store.
87
+ * Same rescue pattern as _rescueSelectsToStore — prevents innerHTML from destroying them.
57
88
  */
58
- private _mergeWithDefaults;
89
+ private _rescueDeleteButtonsToStore;
59
90
  /**
60
- * Apply the reordered composition to the block's HTML structure
61
- * Updates the data-card-composition attribute and reorders product attributes
62
- * Note: Reordering is only applied for grid layout
91
+ * Moves pre-allocated reorder icon UE-BUTTON elements from the hidden reorder-icon-store
92
+ * into orderable item drag-handle slots. Same pattern as _moveDeleteButtonsIntoItems.
63
93
  */
64
- private _applyCompositionToBlock;
94
+ private _moveReorderIconsIntoItems;
65
95
  /**
66
- * Reorders attribute rows within each product card based on composition order
67
- * Targets the tbody inside each product-attribute-cell to preserve card structure
96
+ * Rescues reorder icon UE-BUTTON elements from orderable items back to the hidden reorder-icon-store.
97
+ * Same rescue pattern as _rescueDeleteButtonsToStore prevents innerHTML from destroying them.
68
98
  */
69
- private _reorderProductAttributes;
99
+ private _rescueReorderIconsToStore;
100
+ private _setupEventListeners;
101
+ private _setupDragAndDrop;
102
+ private _setupDeleteHandler;
103
+ private _onAddAttribute;
70
104
  /**
71
- * Builds HTML string with attributes ordered according to composition
105
+ * Removes a single custom attribute by its index in the customAttrs array.
106
+ * Index-based to correctly handle duplicate attributes.
72
107
  */
73
- private _buildCompositionHtml;
108
+ private _onDeleteCustomAttribute;
109
+ /**
110
+ * Handles changing a custom attribute's selection via its inline _GuSelect.
111
+ * Uses the customIndex to target only the specific instance, supporting duplicates.
112
+ */
113
+ private _onCustomAttributeChanged;
114
+ private _onReorder;
115
+ private _injectCustomAttributeHtml;
116
+ private _injectGridAttributeRow;
117
+ private _injectListAttributeRow;
118
+ /**
119
+ * Removes a custom attribute by rebuilding product card content without it.
120
+ * The composition parameter should already have the deleted key removed.
121
+ */
122
+ private _removeCustomAttributeHtml;
123
+ /**
124
+ * Atomically updates both composition and custom-attributes in a single apply()
125
+ * so that onTemplateNodeUpdated fires only once with fully consistent state.
126
+ * Two separate apply() calls would cause an intermediate onTemplateNodeUpdated
127
+ * where composition is updated but customAttributes still has the old order,
128
+ * producing a flicker on the custom attribute dropdowns.
129
+ */
130
+ private _updateBothAttributes;
131
+ /**
132
+ * Reorders attribute rows within each product card based on composition order.
133
+ * Only used for grid layout (list layout has fixed 3-column structure).
134
+ */
135
+ private _reorderProductAttributes;
74
136
  /**
75
- * Apply visibility changes to the block's HTML structure
76
- * Updates display style and data-visibility attribute for all matching elements
77
- * - <tr> elements: use display: none / table-row
78
- * - <td> elements: use display: none / table-cell
137
+ * Builds HTML string with attributes ordered according to composition.
138
+ * Queries existing rows from the container by data-attribute-type.
79
139
  */
140
+ private _buildCompositionHtml;
80
141
  private _applyVisibilityToBlock;
81
142
  /**
82
- * Gets the current layout orientation from store or DOM
143
+ * Finds the composition array index for the Nth custom attribute (0-based).
144
+ * Scans left-to-right, counting only entries with the custom prefix.
145
+ * The customIndex maps 1:1 with the customAttrs array order.
83
146
  */
147
+ private _findNthCustomKeyIndex;
84
148
  private _getCurrentLayout;
85
149
  /**
86
- * Updates orderable state based on layout orientation
87
- * Adds/removes disabled class to hide drag icons for list layout
150
+ * Extracts background color properties from existing card elements.
151
+ * Checks both `.product-card-segment` (where applyCardBackgroundColor sets bg for grid)
152
+ * and `.product-card-wrapper` (where CardBackgroundColorControl sets bg).
153
+ * Used when injecting new attribute cells to match the card's current background.
154
+ */
155
+ private _extractSegmentBgFromCard;
156
+ private _getControlContainer;
157
+ /**
158
+ * Adds/removes orderable-disabled class based on layout orientation.
159
+ * List layout hides drag handles via CSS and disables draggable attribute
160
+ * to prevent native browser drag-and-drop from working without the handle.
88
161
  */
89
162
  private _updateOrderableState;
90
163
  /**
91
- * Subscribe to store orientation changes
92
- * Updates orderable state when layout changes via the layout control
164
+ * Disables the "Add Attribute" button when the custom attribute limit is reached
165
+ * or when all available filters have already been added (no unused attributes left).
166
+ */
167
+ private _updateAddButtonState;
168
+ /**
169
+ * Subscribes to store changes for orientation and filterList updates.
170
+ * When filterList changes (e.g. after async fetch), re-renders dropdowns with new options.
171
+ */
172
+ private _subscribeToStoreChanges;
173
+ /**
174
+ * Looks up the display name for an attribute from the store's filterList.
175
+ * Falls back to Title Case conversion of the snake_case attribute name.
176
+ */
177
+ private _getDisplayNameForAttribute;
178
+ /**
179
+ * Resolves the display content for a custom attribute cell.
180
+ * Uses the real product value from the store when available, falls back to displayName.
93
181
  */
94
- private _subscribeToOrientationChanges;
182
+ private _resolveAttributeContent;
183
+ private _getGridCellHtml;
184
+ private _getListRowHtml;
95
185
  }