@useinsider/guido 2.2.0-beta.fa4cec8 → 3.0.0-beta.2235a35

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 (95) hide show
  1. package/README.md +41 -2
  2. package/dist/@types/config/schemas.js +1 -1
  3. package/dist/components/Guido.vue.js +1 -1
  4. package/dist/components/Guido.vue2.js +66 -66
  5. package/dist/components/organisms/base/Toaster.vue.js +4 -4
  6. package/dist/components/organisms/base/Toaster.vue2.js +12 -9
  7. package/dist/components/organisms/email-preview/desktop-preview/EmailSizeIndicator.vue.js +5 -5
  8. package/dist/components/organisms/email-preview/desktop-preview/EmailSizeIndicator.vue2.js +2 -2
  9. package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +11 -13
  10. package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +54 -23
  11. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue.js +7 -5
  12. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue2.js +34 -21
  13. package/dist/components/organisms/extensions/recommendation/Filters.vue.js +11 -11
  14. package/dist/components/organisms/extensions/recommendation/Filters.vue2.js +48 -36
  15. package/dist/components/organisms/extensions/recommendation/LogicAdapter.vue2.js +11 -9
  16. package/dist/composables/useRecommendation.js +9 -9
  17. package/dist/composables/useSave.js +16 -12
  18. package/dist/composables/useStripo.js +66 -62
  19. package/dist/composables/useStripoEventHandler.js +27 -12
  20. package/dist/composables/useSyncModuleExtractor.js +45 -0
  21. package/dist/config/compiler/utils/recommendationCompilerUtils.js +1 -1
  22. package/dist/config/migrator/recommendationMigrator.js +1 -1
  23. package/dist/enums/unsubscribe.js +25 -21
  24. package/dist/extensions/Blocks/Recommendation/block.js +1 -1
  25. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +36 -33
  26. package/dist/extensions/Blocks/Recommendation/constants/layout.js +16 -14
  27. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +15 -13
  28. package/dist/extensions/Blocks/Recommendation/controls/button/index.js +9 -9
  29. package/dist/extensions/Blocks/Recommendation/controls/image/index.js +1 -1
  30. package/dist/extensions/Blocks/Recommendation/controls/layout/index.js +41 -29
  31. package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +16 -16
  32. package/dist/extensions/Blocks/Recommendation/controls/main/currency.js +30 -32
  33. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +194 -104
  34. package/dist/extensions/Blocks/Recommendation/controls/main/locale.js +9 -9
  35. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +46 -38
  36. package/dist/extensions/Blocks/Recommendation/controls/main/shuffle.js +16 -16
  37. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +291 -217
  38. package/dist/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.js +14 -14
  39. package/dist/extensions/Blocks/Recommendation/controls/name/index.js +10 -10
  40. package/dist/extensions/Blocks/Recommendation/controls/name/textTrim.js +5 -5
  41. package/dist/extensions/Blocks/Recommendation/controls/oldPrice/index.js +14 -14
  42. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/index.js +9 -9
  43. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textAfter.js +3 -3
  44. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textBefore.js +1 -1
  45. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/index.js +9 -9
  46. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textAfter.js +3 -3
  47. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textBefore.js +13 -13
  48. package/dist/extensions/Blocks/Recommendation/controls/price/index.js +3 -3
  49. package/dist/extensions/Blocks/Recommendation/controls/spacing/index.js +223 -99
  50. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +172 -85
  51. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +2 -2
  52. package/dist/extensions/Blocks/Recommendation/templates/grid/migration.js +1 -1
  53. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +2 -2
  54. package/dist/extensions/Blocks/Recommendation/templates/utils.js +32 -32
  55. package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +8 -8
  56. package/dist/extensions/Blocks/Recommendation/validation/filterSchema.js +29 -0
  57. package/dist/guido.css +1 -1
  58. package/dist/services/stripoApi.js +55 -19
  59. package/dist/src/@types/config/schemas.d.ts +1 -1
  60. package/dist/src/@types/events.d.ts +38 -2
  61. package/dist/src/App.vue.d.ts +0 -1
  62. package/dist/src/components/Guido.vue.d.ts +2 -2
  63. package/dist/src/components/organisms/extensions/recommendation/FilterItem.vue.d.ts +1 -0
  64. package/dist/src/components/organisms/extensions/recommendation/Filters.vue.d.ts +17 -1
  65. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  66. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
  67. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
  68. package/dist/src/composables/useGuidoActions.d.ts +1 -1
  69. package/dist/src/composables/useSave.d.ts +2 -2
  70. package/dist/src/composables/useStripo.d.ts +2 -2
  71. package/dist/src/composables/useSyncModuleExtractor.d.ts +4 -0
  72. package/dist/src/enums/unsubscribe.d.ts +3 -0
  73. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +6 -0
  74. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +3 -3
  75. package/dist/src/extensions/Blocks/Recommendation/constants/layout.d.ts +6 -2
  76. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +8 -1
  77. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +38 -10
  78. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +27 -14
  79. package/dist/src/extensions/Blocks/Recommendation/controls/spacing/index.d.ts +50 -17
  80. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +19 -2
  81. package/dist/src/extensions/Blocks/Recommendation/templates/grid/migration.d.ts +1 -1
  82. package/dist/src/extensions/Blocks/Recommendation/templates/index.d.ts +4 -2
  83. package/dist/src/extensions/Blocks/Recommendation/templates/list/migration.d.ts +1 -1
  84. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +1 -1
  85. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +8 -0
  86. package/dist/src/extensions/Blocks/Recommendation/validation/filterSchema.d.ts +15 -0
  87. package/dist/src/main.d.ts +3 -1
  88. package/dist/src/mock/api/settings.d.ts +2 -0
  89. package/dist/src/services/stripoApi.d.ts +5 -0
  90. package/dist/src/stores/editor.d.ts +23 -0
  91. package/dist/src/utils/templatePreparation.d.ts +1 -1
  92. package/dist/static/styles/customEditorStyle.css.js +50 -23
  93. package/dist/stores/editor.js +2 -1
  94. package/dist/utils/templatePreparation.js +1 -1
  95. package/package.json +2 -2
@@ -1,65 +1,77 @@
1
- var d = Object.defineProperty;
2
- var m = (c, s, t) => s in c ? d(c, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : c[s] = t;
3
- var n = (c, s, t) => m(c, typeof s != "symbol" ? s + "" : s, t);
4
- import { CommonControl as h } from "../../../common-control.js";
5
- import { RecommendationConfigService as f } from "../../services/configService.js";
6
- import { useRecommendationExtensionStore as C } from "../../store/recommendation.js";
7
- import { AlgorithmControl as p } from "./algorithm.js";
8
- import { ALGORITHM_CONTROL_ID as W } from "./algorithm.js";
9
- import { CurrencyControl as g } from "./currency.js";
10
- import { CURRENCY_CONTROL_ID as H } from "./currency.js";
11
- import { FiltersControl as R } from "./filters.js";
12
- import { FILTERS_CONTROL_ID as j } from "./filters.js";
13
- import { LocaleControl as y } from "./locale.js";
14
- import { LOCALE_CONTROL_ID as K } from "./locale.js";
15
- import { ProductLayoutControl as _ } from "./productLayout.js";
16
- import { PRODUCT_LAYOUT_CONTROL_ID as J } from "./productLayout.js";
17
- import { ShuffleControl as N } from "./shuffle.js";
18
- import { SHUFFLE_CONTROL_ID as X } from "./shuffle.js";
19
- import { regenerateProductRowsWithStyles as b, getBlockElement as P, updateProductContentInPlace as I } from "./utils.js";
20
- import { formatProductPrice as tt, getCardComposition as et, getCurrentLayout as ot, reapplySpacing as rt, regenerateMobileProductRows as nt, regenerateProductRows as it, setCurrencyAttributes as st, updatePricesInPlace as ct, updateSingleProductContent as at } from "./utils.js";
21
- import { useDebounceFn as l } from "../../../../../node_modules/@vueuse/shared/index.js";
22
- const T = "recommendation-id", S = "ui-elements-recommendation-block";
23
- class $ extends h {
1
+ var g = Object.defineProperty;
2
+ var C = (d, c, t) => c in d ? g(d, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[c] = t;
3
+ var i = (d, c, t) => C(d, typeof c != "symbol" ? c + "" : c, t);
4
+ import { CommonControl as p } from "../../../common-control.js";
5
+ import { DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
6
+ import { RecommendationConfigService as m } from "../../services/configService.js";
7
+ import { useRecommendationExtensionStore as y } from "../../store/recommendation.js";
8
+ import { AlgorithmControl as R } from "./algorithm.js";
9
+ import { ALGORITHM_CONTROL_ID as H } from "./algorithm.js";
10
+ import { CurrencyControl as N } from "./currency.js";
11
+ import { CURRENCY_CONTROL_ID as K } from "./currency.js";
12
+ import { FiltersControl as b } from "./filters.js";
13
+ import { FILTERS_CONTROL_ID as J } from "./filters.js";
14
+ import { LocaleControl as _ } from "./locale.js";
15
+ import { LOCALE_CONTROL_ID as X } from "./locale.js";
16
+ import { ProductLayoutControl as I } from "./productLayout.js";
17
+ import { PRODUCT_LAYOUT_CONTROL_ID as tt } from "./productLayout.js";
18
+ import { ShuffleControl as S } from "./shuffle.js";
19
+ import { SHUFFLE_CONTROL_ID as ot } from "./shuffle.js";
20
+ import { getBlockElement as P, updateProductContentInPlace as T, regenerateProductRowsWithStyles as L } from "./utils.js";
21
+ import { adjustProductsToSize as nt, formatProductPrice as st, getCardComposition as it, getCurrentLayout as at, reapplySpacing as ct, regenerateMobileProductRows as lt, regenerateProductRows as ut, setCurrencyAttributes as dt, updatePricesInPlace as mt, updateSingleProductContent as ht } from "./utils.js";
22
+ import { useDebounceFn as h } from "../../../../../node_modules/@vueuse/shared/index.js";
23
+ const k = "recommendation-id", D = "ui-elements-recommendation-block";
24
+ class W extends p {
24
25
  constructor() {
25
26
  super(...arguments);
26
- n(this, "store", C());
27
- n(this, "storeUnsubscription", () => {
27
+ i(this, "store", y());
28
+ i(this, "storeUnsubscription", () => {
28
29
  });
29
30
  // Sub-control instances for lifecycle management
30
- n(this, "algorithmControl", null);
31
- n(this, "localeControl", null);
32
- n(this, "currencyControl", null);
33
- n(this, "productLayoutControl", null);
34
- n(this, "filtersControl", null);
35
- n(this, "shuffleControl", null);
31
+ i(this, "algorithmControl", null);
32
+ i(this, "localeControl", null);
33
+ i(this, "currencyControl", null);
34
+ i(this, "productLayoutControl", null);
35
+ i(this, "filtersControl", null);
36
+ i(this, "shuffleControl", null);
36
37
  /**
37
38
  * Debounced product fetch to prevent rapid API calls during config changes
38
39
  */
39
- n(this, "_debouncedFetchProducts", l(() => {
40
+ i(this, "_debouncedFetchProducts", h(() => {
40
41
  this.store.fetchRecommendationProducts();
41
42
  }, 500));
42
43
  /**
43
- * Debounced regeneration when products arrive from API
44
- * Tries in-place update first to preserve styles, falls back to full regeneration
44
+ * Debounced content update when products arrive from API.
45
+ *
46
+ * Tries in-place update first (preserves user-applied styles) — this succeeds
47
+ * when the product count matches the DOM (algorithm/locale/currency changes).
48
+ *
49
+ * Falls back to full regeneration when product count differs from DOM — this
50
+ * happens after "Number of Products" changes where the DOM still has the old
51
+ * count. The store pads products to the configured size, so in-place only
52
+ * fails when the size actually changed.
45
53
  */
46
- n(this, "_debouncedRegenerateWithProducts", l(() => {
54
+ i(this, "_debouncedRegenerateWithProducts", h(() => {
47
55
  const t = this.store.recommendationProducts;
48
56
  if (!this.currentNode || !this.api)
49
57
  return;
50
58
  const e = this.api.getDocumentModifier();
51
- I({
59
+ T({
52
60
  currentNode: this.currentNode,
53
61
  documentModifier: e,
54
62
  products: t
55
- }) || this._regenerateWithProducts(t);
63
+ }) || L({
64
+ currentNode: this.currentNode,
65
+ documentModifier: e,
66
+ products: t
67
+ });
56
68
  }, 100));
57
69
  }
58
70
  getId() {
59
- return S;
71
+ return D;
60
72
  }
61
73
  getTemplate() {
62
- return this.algorithmControl = new p(), this.localeControl = new y(), this.currencyControl = new g(), this.productLayoutControl = new _(), this.filtersControl = new R(), this.shuffleControl = new N(), `
74
+ return this.algorithmControl = new R(), this.localeControl = new _(), this.currencyControl = new N(), this.productLayoutControl = new I(), this.filtersControl = new b(), this.shuffleControl = new S(), `
63
75
  <div class="recommendation-controls-container">
64
76
  ${this.algorithmControl.getTemplate()}
65
77
  ${this.localeControl.getTemplate()}
@@ -79,35 +91,20 @@ class $ extends h {
79
91
  }
80
92
  await this._fetchBlockData(t), this._initializeSubControls();
81
93
  }
82
- /**
83
- * Immediately regenerate products with styles (no debounce)
84
- * Used for initial load after fetch completes
85
- */
86
- _regenerateWithProducts(t) {
87
- if (!this.currentNode || !this.api) {
88
- console.warn("[Recommendation] Cannot regenerate - missing currentNode or api");
89
- return;
90
- }
91
- b({
92
- currentNode: this.currentNode,
93
- documentModifier: this.api.getDocumentModifier(),
94
- products: t
95
- });
96
- }
97
94
  onTemplateNodeUpdated(t) {
98
- var i;
95
+ var r;
99
96
  super.onTemplateNodeUpdated(t);
100
97
  const e = this._getRecommendationIdFromNode(t);
101
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), this._syncNodeConfigToStore(), e !== null && !((i = this.store.blockStates[e]) != null && i.isInitialized) && this._fetchBlockData(e), [
98
+ e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), this._syncNodeConfigToStore(), e !== null && !((r = this.store.blockStates[e]) != null && r.isInitialized) && this._fetchBlockData(e), [
102
99
  this.algorithmControl,
103
100
  this.localeControl,
104
101
  this.currencyControl,
105
102
  this.productLayoutControl,
106
103
  this.filtersControl,
107
104
  this.shuffleControl
108
- ].forEach((r) => {
109
- var a;
110
- r != null && r.api && (r.currentNode = t, (a = r.onTemplateNodeUpdated) == null || a.call(r, t));
105
+ ].forEach((n) => {
106
+ var s;
107
+ n != null && n.api && (n.currentNode = t, (s = n.onTemplateNodeUpdated) == null || s.call(n, t));
111
108
  });
112
109
  }
113
110
  onDestroy() {
@@ -152,13 +149,17 @@ class $ extends h {
152
149
  * values are being prepared for the upcoming initial fetch.
153
150
  */
154
151
  _syncNodeConfigToStore() {
155
- const t = f.getConfig(this.currentNode);
152
+ var r;
153
+ const t = m.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
156
154
  this.store.patchCurrentBlockConfig({
157
155
  strategy: t.strategy,
158
156
  language: t.language,
159
157
  size: t.size,
160
158
  productIds: t.productIds,
161
- filters: t.filters,
159
+ // Only sync filters from node config during initial load.
160
+ // After initialization, the Pinia store is the source of truth
161
+ // for filters (edited via the filter drawer).
162
+ ...o ? {} : { filters: t.filters },
162
163
  shuffleProducts: t.shuffleProducts,
163
164
  currencySettings: {
164
165
  name: t.currency.code,
@@ -172,22 +173,84 @@ class $ extends h {
172
173
  }, { triggerRefetch: !1 });
173
174
  }
174
175
  /**
175
- * Fetches initial data for a block (filters, algorithms, products).
176
+ * Fetches initial data for a block in three phases:
177
+ * 1. Shared reference data (algorithms, currencies, filters) — parallel
178
+ * 2. Smart defaults for new blocks (currency, algorithm) — sequential
179
+ * 3. Product data with correct defaults — sequential
180
+ *
176
181
  * Shared by onRender() and onTemplateNodeUpdated() to avoid duplication.
177
182
  * Marks the block as initialized to prevent redundant fetches on re-selection.
178
183
  */
179
184
  async _fetchBlockData(t) {
180
185
  t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
181
- this.store.fetchRecommendationFilters(),
182
186
  this.store.fetchRecommendationCreateData(),
183
- this.store.fetchRecommendationProducts()
184
- ])).forEach((o, i) => {
185
- o.status === "rejected" && console.warn(`Recommendation block: ${[
186
- "fetchRecommendationFilters",
187
- "fetchRecommendationCreateData",
188
- "fetchRecommendationProducts"
189
- ][i]} failed`, o.reason);
190
- });
187
+ this.store.fetchRecommendationFilters()
188
+ ])).forEach((o, r) => {
189
+ o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, o.reason);
190
+ }), this._applySmartDefaults();
191
+ try {
192
+ await this.store.fetchRecommendationProducts();
193
+ } catch (o) {
194
+ console.warn("Recommendation block: fetchRecommendationProducts failed", o);
195
+ }
196
+ }
197
+ /**
198
+ * Applies smart defaults for newly dropped blocks.
199
+ *
200
+ * For new blocks (config still matches hardcoded defaults), validates that
201
+ * the default currency and algorithm are available from the API response.
202
+ * If not, falls back to the first available option.
203
+ *
204
+ * Saved templates with user-customized config are left unchanged because
205
+ * their values won't match the hardcoded defaults.
206
+ */
207
+ _applySmartDefaults() {
208
+ if (!this.currentNode || !this.api)
209
+ return;
210
+ const t = m.getConfig(this.currentNode), e = {};
211
+ let o = null, r = null, n = null;
212
+ if (t.currency.code === a.currency.code) {
213
+ const { currencyList: s } = this.store;
214
+ s.length > 0 && (s.some(
215
+ (u) => u.value === `price.${a.currency.code}`
216
+ ) || (o = s[0].value.replace("price.", ""), e.currency = {
217
+ ...a.currency,
218
+ code: o,
219
+ symbol: o
220
+ }));
221
+ }
222
+ if (t.strategy === a.strategy) {
223
+ const s = this.store.getActivePredictiveAlgorithms;
224
+ s.length > 0 && (s.some(
225
+ (u) => u.value === a.strategy
226
+ ) || (r = s[0].value, e.strategy = r));
227
+ }
228
+ if (t.language === a.language) {
229
+ const s = this.store.getLanguages;
230
+ s.length > 0 && (s.some(
231
+ (u) => u.value === a.language
232
+ ) || (n = s[0].value, e.language = n));
233
+ }
234
+ !o && !r && !n || (m.updateConfig(
235
+ this.api,
236
+ this.currentNode,
237
+ e,
238
+ "Applied smart defaults"
239
+ ), this.store.patchCurrentBlockConfig({
240
+ ...o ? {
241
+ currencySettings: {
242
+ name: o,
243
+ value: o,
244
+ symbol: o,
245
+ alignment: a.currency.alignment === "before" ? "0" : "1",
246
+ decimalCount: a.currency.decimalCount.toString(),
247
+ decimalSeparator: a.currency.decimalSeparator,
248
+ thousandSeparator: a.currency.thousandSeparator
249
+ }
250
+ } : {},
251
+ ...r ? { strategy: r } : {},
252
+ ...n ? { language: n } : {}
253
+ }, { triggerRefetch: !1 }));
191
254
  }
192
255
  /**
193
256
  * Reads the recommendation-id attribute from the block element within the node
@@ -196,11 +259,11 @@ class $ extends h {
196
259
  const e = P(t);
197
260
  if (!e || !("getAttribute" in e))
198
261
  return null;
199
- const o = e.getAttribute(T);
262
+ const o = e.getAttribute(k);
200
263
  if (!o)
201
264
  return null;
202
- const i = parseInt(o);
203
- return Number.isNaN(i) ? null : i;
265
+ const r = parseInt(o);
266
+ return Number.isNaN(r) ? null : r;
204
267
  }
205
268
  /**
206
269
  * Listen to store changes that require product refresh or regeneration.
@@ -208,43 +271,70 @@ class $ extends h {
208
271
  * Uses configVersion counter (incremented only by patchCurrentBlockConfig)
209
272
  * to distinguish user-initiated config changes from internal mutations
210
273
  * (e.g., fetchRecommendationCreateData setting preferred currency).
274
+ *
275
+ * Tracks currentRecommendationId to detect block switches. When the user
276
+ * selects a different recommendation block, the proxy getters (e.g.,
277
+ * recommendationProducts) return the new block's data — a different array
278
+ * reference that would be falsely detected as "new products arrived".
279
+ * We skip that tick and update tracking references instead.
211
280
  */
212
281
  _listenStateUpdates() {
213
282
  const { store: t } = this;
214
- let e = t.recommendationProducts, o = t.$state.configVersion;
283
+ let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
215
284
  this.storeUnsubscription = t.$subscribe(() => {
216
- const i = t.$state.configVersion;
217
- i !== o && (o = i, this._debouncedFetchProducts());
218
- const r = t.recommendationProducts, a = r !== e, u = Array.isArray(r) && r.length > 0;
219
- a && u && (e = r, this._debouncedRegenerateWithProducts());
285
+ const n = t.currentRecommendationId;
286
+ if (n !== r) {
287
+ r = n, e = t.recommendationProducts, o = t.$state.configVersion;
288
+ return;
289
+ }
290
+ const s = t.$state.configVersion;
291
+ s !== o && (o = s, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
292
+ const l = t.recommendationProducts, u = l !== e, f = Array.isArray(l) && l.length > 0;
293
+ u && f && (e = l, this._debouncedRegenerateWithProducts());
220
294
  });
221
295
  }
296
+ /**
297
+ * Persists the current filter state from Pinia store to the Stripo node config.
298
+ * This ensures filters survive template save/reload cycles.
299
+ */
300
+ _persistFiltersToNodeConfig() {
301
+ if (!this.currentNode || !this.api)
302
+ return;
303
+ const { filters: t } = this.store.recommendationConfigs;
304
+ m.updateConfig(
305
+ this.api,
306
+ this.currentNode,
307
+ { filters: t },
308
+ "Update recommendation filters"
309
+ );
310
+ }
222
311
  }
223
312
  export {
224
- W as ALGORITHM_CONTROL_ID,
225
- p as AlgorithmControl,
226
- S as CONTROL_BLOCK_ID,
227
- H as CURRENCY_CONTROL_ID,
228
- g as CurrencyControl,
229
- j as FILTERS_CONTROL_ID,
230
- R as FiltersControl,
231
- K as LOCALE_CONTROL_ID,
232
- y as LocaleControl,
233
- J as PRODUCT_LAYOUT_CONTROL_ID,
234
- _ as ProductLayoutControl,
235
- $ as RecommendationBlockControl,
236
- X as SHUFFLE_CONTROL_ID,
237
- N as ShuffleControl,
238
- tt as formatProductPrice,
313
+ H as ALGORITHM_CONTROL_ID,
314
+ R as AlgorithmControl,
315
+ D as CONTROL_BLOCK_ID,
316
+ K as CURRENCY_CONTROL_ID,
317
+ N as CurrencyControl,
318
+ J as FILTERS_CONTROL_ID,
319
+ b as FiltersControl,
320
+ X as LOCALE_CONTROL_ID,
321
+ _ as LocaleControl,
322
+ tt as PRODUCT_LAYOUT_CONTROL_ID,
323
+ I as ProductLayoutControl,
324
+ W as RecommendationBlockControl,
325
+ ot as SHUFFLE_CONTROL_ID,
326
+ S as ShuffleControl,
327
+ nt as adjustProductsToSize,
328
+ st as formatProductPrice,
239
329
  P as getBlockElement,
240
- et as getCardComposition,
241
- ot as getCurrentLayout,
242
- rt as reapplySpacing,
243
- nt as regenerateMobileProductRows,
244
- it as regenerateProductRows,
245
- b as regenerateProductRowsWithStyles,
246
- st as setCurrencyAttributes,
247
- ct as updatePricesInPlace,
248
- I as updateProductContentInPlace,
249
- at as updateSingleProductContent
330
+ it as getCardComposition,
331
+ at as getCurrentLayout,
332
+ ct as reapplySpacing,
333
+ lt as regenerateMobileProductRows,
334
+ ut as regenerateProductRows,
335
+ L as regenerateProductRowsWithStyles,
336
+ dt as setCurrencyAttributes,
337
+ mt as updatePricesInPlace,
338
+ T as updateProductContentInPlace,
339
+ ht as updateSingleProductContent
250
340
  };
@@ -2,20 +2,20 @@ var s = Object.defineProperty;
2
2
  var r = (o, t, e) => t in o ? s(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
3
3
  var a = (o, t, e) => r(o, typeof t != "symbol" ? t + "" : t, e);
4
4
  import { UEAttr as l } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as m } from "../../../common-control.js";
5
+ import { CommonControl as c } from "../../../common-control.js";
6
6
  import { RecommendationConfigService as i } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as c } from "../../store/recommendation.js";
8
- const g = "recommendation-locale-control", n = {
7
+ import { useRecommendationExtensionStore as g } from "../../store/recommendation.js";
8
+ const m = "recommendation-locale-control", n = {
9
9
  LOCALE: "language"
10
10
  };
11
- class C extends m {
11
+ class E extends c {
12
12
  constructor() {
13
13
  super(...arguments);
14
14
  // Store is used ONLY for API-fetched data (language options), not for config
15
- a(this, "store", c());
15
+ a(this, "store", g());
16
16
  }
17
17
  getId() {
18
- return g;
18
+ return m;
19
19
  }
20
20
  getTemplate() {
21
21
  return `
@@ -51,7 +51,7 @@ class C extends m {
51
51
  );
52
52
  }
53
53
  _onLocaleChange(e) {
54
- this.currentNode && (i.updateConfig(
54
+ !this.currentNode || i.getConfig(this.currentNode).language === e || (i.updateConfig(
55
55
  this.api,
56
56
  this.currentNode,
57
57
  { language: e },
@@ -65,6 +65,6 @@ class C extends m {
65
65
  }
66
66
  }
67
67
  export {
68
- g as LOCALE_CONTROL_ID,
69
- C as LocaleControl
68
+ m as LOCALE_CONTROL_ID,
69
+ E as LocaleControl
70
70
  };
@@ -1,41 +1,41 @@
1
- var _ = Object.defineProperty;
2
- var c = (i, o, t) => o in i ? _(i, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[o] = t;
3
- var d = (i, o, t) => c(i, typeof o != "symbol" ? o + "" : o, t);
4
- import { EditorStatePropertyType as u, PreviewDeviceMode as R } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as h } from "../../../common-control.js";
6
- import { MAX_PRODUCT_COUNT as C, MAX_PRODUCTS_PER_ROW as p, MAX_MOBILE_PRODUCTS_PER_ROW as m } from "../../constants/layout.js";
7
- import { RecommendationConfigService as s } from "../../services/configService.js";
1
+ var u = Object.defineProperty;
2
+ var _ = (n, r, t) => r in n ? u(n, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[r] = t;
3
+ var a = (n, r, t) => _(n, typeof r != "symbol" ? r + "" : r, t);
4
+ import { EditorStatePropertyType as c, PreviewDeviceMode as C } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as R } from "../../../common-control.js";
6
+ import { MAX_PRODUCT_COUNT as h, MAX_PRODUCTS_PER_ROW as p, MAX_MOBILE_PRODUCTS_PER_ROW as g, DEFAULT_COLUMN_SPACING as m, DEFAULT_MOBILE_COLUMN_SPACING as O } from "../../constants/layout.js";
7
+ import { RecommendationConfigService as i } from "../../services/configService.js";
8
8
  import { useRecommendationExtensionStore as l } from "../../store/recommendation.js";
9
- import { ensureMobileCssRulesExist as O } from "../mobileLayout/cssRules.js";
10
- import { getCurrentLayout as I, regenerateMobileProductRows as g, regenerateProductRowsWithStyles as b } from "./utils.js";
11
- import { useDebounceFn as N } from "../../../../../node_modules/@vueuse/shared/index.js";
12
- const P = "recommendation-product-layout-control", e = {
9
+ import { ensureMobileCssRulesExist as I } from "../mobileLayout/cssRules.js";
10
+ import { getCurrentLayout as N, regenerateMobileProductRows as f, adjustProductsToSize as b, regenerateProductRowsWithStyles as P } from "./utils.js";
11
+ import { useDebounceFn as L } from "../../../../../node_modules/@vueuse/shared/index.js";
12
+ const M = "recommendation-product-layout-control", e = {
13
13
  PRODUCT_COUNT: "size",
14
14
  PRODUCT_IN_ROW: "cardsInRow",
15
15
  PRODUCT_IN_ROW_LABEL: "cardsInRowLabel",
16
16
  MOBILE_CARDS_IN_ROW: "mobileCardsInRow",
17
17
  MOBILE_CARDS_IN_ROW_LABEL: "mobileCardsInRowLabel"
18
18
  };
19
- class S extends h {
19
+ class V extends R {
20
20
  constructor() {
21
21
  super(...arguments);
22
22
  // Store is used for backward compatibility with product fetching and regeneration
23
- d(this, "store", l());
24
- d(this, "storeUnsubscription", () => {
23
+ a(this, "store", l());
24
+ a(this, "storeUnsubscription", () => {
25
25
  });
26
- d(this, "_debouncedRegenerateProductRows", N(() => {
26
+ a(this, "_debouncedRegenerateProductRows", L(() => {
27
27
  this._regenerateProductRows();
28
28
  }, 500));
29
29
  }
30
30
  getId() {
31
- return P;
31
+ return M;
32
32
  }
33
33
  getTemplate() {
34
34
  return `
35
35
  <div class="product-layout-control-container">
36
36
  ${this._GuTwoColumns([
37
37
  this._GuLabel({ text: "Number of Products" }),
38
- this._GuCounter({ name: e.PRODUCT_COUNT, maxValue: C }),
38
+ this._GuCounter({ name: e.PRODUCT_COUNT, maxValue: h }),
39
39
  this._GuLabel({
40
40
  text: "Products in One Row on Desktop",
41
41
  name: e.PRODUCT_IN_ROW_LABEL
@@ -47,7 +47,7 @@ class S extends h {
47
47
  }),
48
48
  this._GuCounter({
49
49
  name: e.MOBILE_CARDS_IN_ROW,
50
- maxValue: m
50
+ maxValue: g
51
51
  })
52
52
  ])}
53
53
  </div>
@@ -63,7 +63,7 @@ class S extends h {
63
63
  this.storeUnsubscription();
64
64
  }
65
65
  _setFormValues() {
66
- const t = s.getConfig(this.currentNode);
66
+ const t = i.getConfig(this.currentNode);
67
67
  this.api.updateValues({
68
68
  [e.PRODUCT_COUNT]: t.size,
69
69
  [e.PRODUCT_IN_ROW]: t.cardsInRow,
@@ -75,7 +75,7 @@ class S extends h {
75
75
  * using Stripo's EditorStatePropertyType API.
76
76
  */
77
77
  _isMobileMode() {
78
- return this.api.getEditorState()[u.previewDeviceMode] === R.MOBILE;
78
+ return this.api.getEditorState()[c.previewDeviceMode] === C.MOBILE;
79
79
  }
80
80
  /**
81
81
  * Updates counter visibility based on layout orientation and editor mode.
@@ -84,8 +84,8 @@ class S extends h {
84
84
  * - Grid + mobile mode: show mobile counter, hide desktop counter
85
85
  */
86
86
  _updateProductsInRowVisibility() {
87
- const r = (s.getConfig(this.currentNode).layout || I(this.currentNode)) === "grid", n = this._isMobileMode();
88
- this.api.setVisibility(e.PRODUCT_IN_ROW, r && !n), this.api.setVisibility(e.PRODUCT_IN_ROW_LABEL, r && !n), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW, r && n), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW_LABEL, r && n);
87
+ const o = (i.getConfig(this.currentNode).layout || N(this.currentNode)) === "grid", d = this._isMobileMode();
88
+ this.api.setVisibility(e.PRODUCT_IN_ROW, o && !d), this.api.setVisibility(e.PRODUCT_IN_ROW_LABEL, o && !d), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW, o && d), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW_LABEL, o && d);
89
89
  }
90
90
  /**
91
91
  * Subscribes to editor preview mode changes via Stripo API.
@@ -94,43 +94,51 @@ class S extends h {
94
94
  */
95
95
  _subscribeToEditorModeChanges() {
96
96
  this.api.onEditorStatePropUpdated(
97
- u.previewDeviceMode,
97
+ c.previewDeviceMode,
98
98
  () => {
99
99
  this._updateProductsInRowVisibility();
100
100
  }
101
101
  );
102
102
  }
103
103
  _onProductCountChange(t) {
104
- this.currentNode && (s.updateConfig(
104
+ !this.currentNode || i.getConfig(this.currentNode).size === t || (i.updateConfig(
105
105
  this.api,
106
106
  this.currentNode,
107
107
  { size: t },
108
108
  `Changed product count to ${t}`
109
- ), this.store.patchCurrentBlockConfig({ size: t }), this._debouncedRegenerateProductRows());
109
+ ), this.store.patchCurrentBlockConfig({ size: t }));
110
110
  }
111
111
  _onProductsInRowChange(t) {
112
- this.currentNode && (s.updateConfig(
112
+ if (!this.currentNode || i.getConfig(this.currentNode).cardsInRow === t)
113
+ return;
114
+ const o = t === 1 ? 0 : m;
115
+ i.updateConfig(
113
116
  this.api,
114
117
  this.currentNode,
115
- { cardsInRow: t },
118
+ { cardsInRow: t, columnSpacing: o },
116
119
  `Changed products per row to ${t}`
117
- ), this.store.patchCurrentBlockConfig({ cardsInRow: t }), this._debouncedRegenerateProductRows());
120
+ ), this.store.patchCurrentBlockConfig({ cardsInRow: t }, { triggerRefetch: !1 }), this._debouncedRegenerateProductRows();
118
121
  }
119
122
  _onMobileCardsInRowChange(t) {
120
- this.currentNode && (s.updateConfig(
123
+ if (!this.currentNode || i.getConfig(this.currentNode).mobileCardsInRow === t)
124
+ return;
125
+ const o = t === 1 ? 0 : O;
126
+ i.updateConfig(
121
127
  this.api,
122
128
  this.currentNode,
123
- { mobileCardsInRow: t },
129
+ { mobileCardsInRow: t, mobileColumnSpacing: o },
124
130
  `Changed mobile products per row to ${t}`
125
- ), O(this.api), g({
131
+ ), I(this.api), f({
126
132
  currentNode: this.currentNode,
127
133
  documentModifier: this.api.getDocumentModifier()
128
- }));
134
+ });
129
135
  }
130
136
  _regenerateProductRows() {
131
- b({
137
+ const t = i.getConfig(this.currentNode), s = parseInt(t.size) || 6, o = b(this.store.recommendationProducts, s);
138
+ P({
132
139
  currentNode: this.currentNode,
133
- documentModifier: this.api.getDocumentModifier()
140
+ documentModifier: this.api.getDocumentModifier(),
141
+ products: o
134
142
  });
135
143
  }
136
144
  _listenToFormUpdates() {
@@ -149,12 +157,12 @@ class S extends h {
149
157
  _listenStateUpdates() {
150
158
  let t = this.store.recommendationConfigs.orientation;
151
159
  this.storeUnsubscription = this.store.$subscribe(() => {
152
- const a = this.store.recommendationConfigs.orientation;
153
- a !== t && (t = a, this._updateProductsInRowVisibility());
160
+ const s = this.store.recommendationConfigs.orientation;
161
+ s !== t && (t = s, this._updateProductsInRowVisibility());
154
162
  });
155
163
  }
156
164
  }
157
165
  export {
158
- P as PRODUCT_LAYOUT_CONTROL_ID,
159
- S as ProductLayoutControl
166
+ M as PRODUCT_LAYOUT_CONTROL_ID,
167
+ V as ProductLayoutControl
160
168
  };