@useinsider/guido 2.1.0-beta.7d0f92a → 2.1.0-beta.7ffef92

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 (62) hide show
  1. package/README.md +36 -0
  2. package/dist/@types/config/schemas.js +70 -65
  3. package/dist/components/Guido.vue.js +1 -1
  4. package/dist/components/Guido.vue2.js +69 -58
  5. package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +11 -13
  6. package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +55 -24
  7. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue.js +3 -3
  8. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue2.js +34 -21
  9. package/dist/components/organisms/extensions/recommendation/Filters.vue.js +9 -9
  10. package/dist/components/organisms/extensions/recommendation/Filters.vue2.js +44 -35
  11. package/dist/composables/useBlocksConfig.js +26 -16
  12. package/dist/composables/useHtmlValidator.js +107 -119
  13. package/dist/config/compiler/utils/recommendationCompilerUtils.js +27 -22
  14. package/dist/config/migrator/itemsBlockMigrator.js +101 -97
  15. package/dist/enums/defaults.js +8 -4
  16. package/dist/extensions/Blocks/Recommendation/block.js +26 -23
  17. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +27 -26
  18. package/dist/extensions/Blocks/Recommendation/constants/layout.js +6 -4
  19. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +12 -10
  20. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +124 -72
  21. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +87 -37
  22. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +138 -117
  23. package/dist/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.js +21 -0
  24. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +179 -133
  25. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +25 -30
  26. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +27 -30
  27. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +20 -25
  28. package/dist/extensions/Blocks/Recommendation/templates/utils.js +43 -31
  29. package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +8 -8
  30. package/dist/extensions/Blocks/Recommendation/validation/filterSchema.js +29 -0
  31. package/dist/extensions/ModulesTabIcons/extension.js +17 -0
  32. package/dist/guido.css +1 -1
  33. package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +257 -187
  34. package/dist/services/recommendationApi.js +11 -9
  35. package/dist/services/stripoApi.js +20 -17
  36. package/dist/src/@types/config/schemas.d.ts +8 -0
  37. package/dist/src/components/organisms/extensions/recommendation/FilterItem.vue.d.ts +1 -0
  38. package/dist/src/components/organisms/extensions/recommendation/Filters.vue.d.ts +15 -1
  39. package/dist/src/composables/useConfig.d.ts +4 -0
  40. package/dist/src/enums/defaults.d.ts +4 -0
  41. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +2 -2
  42. package/dist/src/extensions/Blocks/Recommendation/constants/layout.d.ts +6 -0
  43. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +4 -0
  44. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +17 -1
  45. package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +22 -4
  46. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +9 -2
  47. package/dist/src/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.d.ts +29 -0
  48. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +25 -2
  49. package/dist/src/extensions/Blocks/Recommendation/templates/grid/template.d.ts +1 -1
  50. package/dist/src/extensions/Blocks/Recommendation/templates/index.d.ts +2 -0
  51. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +12 -3
  52. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  53. package/dist/src/extensions/Blocks/Recommendation/validation/filterSchema.d.ts +15 -0
  54. package/dist/src/extensions/ModulesTabIcons/extension.d.ts +2 -0
  55. package/dist/src/stores/config.d.ts +36 -0
  56. package/dist/static/styles/components/notification.css.js +1 -0
  57. package/dist/static/styles/components/version-history.css.js +10 -2
  58. package/dist/static/styles/components/wide-panel.css.js +18 -2
  59. package/dist/static/styles/customEditorStyle.css.js +10 -9
  60. package/dist/static/templates/empty/index.html.js +74 -0
  61. package/dist/static/templates/empty/style.css.js +779 -0
  62. package/package.json +1 -1
@@ -1,55 +1,54 @@
1
- var d = Object.defineProperty;
2
- var m = (l, n, t) => n in l ? d(l, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[n] = t;
3
- var i = (l, n, t) => m(l, typeof n != "symbol" ? n + "" : n, t);
4
- import { CommonControl as h } from "../../../common-control.js";
1
+ var m = Object.defineProperty;
2
+ var h = (c, s, t) => s in c ? m(c, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : c[s] = t;
3
+ var n = (c, s, t) => h(c, typeof s != "symbol" ? s + "" : s, t);
4
+ import { CommonControl as f } from "../../../common-control.js";
5
+ import { RecommendationConfigService as l } from "../../services/configService.js";
5
6
  import { useRecommendationExtensionStore as C } from "../../store/recommendation.js";
6
- import { AlgorithmControl as f } from "./algorithm.js";
7
- import { ALGORITHM_CONTROL_ID as M } from "./algorithm.js";
8
- import { CurrencyControl as p } from "./currency.js";
9
- import { CURRENCY_CONTROL_ID as W } from "./currency.js";
10
- import { FiltersControl as g } from "./filters.js";
11
- import { FILTERS_CONTROL_ID as H } from "./filters.js";
12
- import { LocaleControl as R } from "./locale.js";
13
- import { LOCALE_CONTROL_ID as j } from "./locale.js";
14
- import { ProductLayoutControl as _ } from "./productLayout.js";
15
- import { PRODUCT_LAYOUT_CONTROL_ID as K } from "./productLayout.js";
16
- import { ShuffleControl as N } from "./shuffle.js";
17
- import { SHUFFLE_CONTROL_ID as J } from "./shuffle.js";
18
- import { regenerateProductRowsWithStyles as P, getBlockElement as b, updateProductContentInPlace as y } from "./utils.js";
19
- import { formatProductPrice as X, getCardComposition as Z, getCurrentLayout as tt, reapplySpacing as et, regenerateProductRows as ot, setCurrencyAttributes as rt, updatePricesInPlace as it, updateSingleProductContent as nt } from "./utils.js";
20
- import { useDebounceFn as c } from "../../../../../node_modules/@vueuse/shared/index.js";
21
- const T = "recommendation-id", L = "ui-elements-recommendation-block";
22
- class x extends h {
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 N } from "./locale.js";
14
+ import { LOCALE_CONTROL_ID as K } from "./locale.js";
15
+ import { ProductLayoutControl as y } from "./productLayout.js";
16
+ import { PRODUCT_LAYOUT_CONTROL_ID as J } from "./productLayout.js";
17
+ import { ShuffleControl as _ } from "./shuffle.js";
18
+ import { SHUFFLE_CONTROL_ID as X } from "./shuffle.js";
19
+ import { regenerateProductRowsWithStyles as b, getBlockElement as I, updateProductContentInPlace as P } from "./utils.js";
20
+ import { formatProductPrice as tt, getCardComposition as et, getCurrentLayout as ot, reapplySpacing as rt, regenerateMobileProductRows as it, regenerateProductRows as nt, setCurrencyAttributes as st, updatePricesInPlace as ct, updateSingleProductContent as at } from "./utils.js";
21
+ import { useDebounceFn as u } from "../../../../../node_modules/@vueuse/shared/index.js";
22
+ const T = "recommendation-id", S = "ui-elements-recommendation-block";
23
+ class z extends f {
23
24
  constructor() {
24
25
  super(...arguments);
25
- i(this, "store", C());
26
- i(this, "storeUnsubscription", () => {
26
+ n(this, "store", C());
27
+ n(this, "storeUnsubscription", () => {
27
28
  });
28
- // Track if initial data has been fetched per block ID to avoid redundant API calls
29
- i(this, "initializedBlocks", /* @__PURE__ */ new Map());
30
29
  // Sub-control instances for lifecycle management
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);
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);
37
36
  /**
38
37
  * Debounced product fetch to prevent rapid API calls during config changes
39
38
  */
40
- i(this, "_debouncedFetchProducts", c(() => {
39
+ n(this, "_debouncedFetchProducts", u(() => {
41
40
  this.store.fetchRecommendationProducts();
42
41
  }, 500));
43
42
  /**
44
43
  * Debounced regeneration when products arrive from API
45
44
  * Tries in-place update first to preserve styles, falls back to full regeneration
46
45
  */
47
- i(this, "_debouncedRegenerateWithProducts", c(() => {
46
+ n(this, "_debouncedRegenerateWithProducts", u(() => {
48
47
  const t = this.store.recommendationProducts;
49
48
  if (!this.currentNode || !this.api)
50
49
  return;
51
50
  const e = this.api.getDocumentModifier();
52
- y({
51
+ P({
53
52
  currentNode: this.currentNode,
54
53
  documentModifier: e,
55
54
  products: t
@@ -57,10 +56,10 @@ class x extends h {
57
56
  }, 100));
58
57
  }
59
58
  getId() {
60
- return L;
59
+ return S;
61
60
  }
62
61
  getTemplate() {
63
- return this.algorithmControl = new f(), this.localeControl = new R(), this.currencyControl = new p(), this.productLayoutControl = new _(), this.filtersControl = new g(), this.shuffleControl = new N(), `
62
+ return this.algorithmControl = new p(), this.localeControl = new N(), this.currencyControl = new g(), this.productLayoutControl = new y(), this.filtersControl = new R(), this.shuffleControl = new _(), `
64
63
  <div class="recommendation-controls-container">
65
64
  ${this.algorithmControl.getTemplate()}
66
65
  ${this.localeControl.getTemplate()}
@@ -72,8 +71,9 @@ class x extends h {
72
71
  `;
73
72
  }
74
73
  async onRender() {
74
+ var e;
75
75
  const t = this._getRecommendationIdFromNode(this.currentNode) ?? this.store.currentRecommendationId;
76
- if (t !== null && this.store.setCurrentBlock(t), this._listenStateUpdates(), t !== null && this.initializedBlocks.get(t)) {
76
+ if (t !== null && this.store.setCurrentBlock(t), this._listenStateUpdates(), t !== null && ((e = this.store.blockStates[t]) != null && e.isInitialized)) {
77
77
  this._initializeSubControls();
78
78
  return;
79
79
  }
@@ -88,25 +88,26 @@ class x extends h {
88
88
  console.warn("[Recommendation] Cannot regenerate - missing currentNode or api");
89
89
  return;
90
90
  }
91
- P({
91
+ b({
92
92
  currentNode: this.currentNode,
93
93
  documentModifier: this.api.getDocumentModifier(),
94
94
  products: t
95
95
  });
96
96
  }
97
97
  onTemplateNodeUpdated(t) {
98
+ var r;
98
99
  super.onTemplateNodeUpdated(t);
99
100
  const e = this._getRecommendationIdFromNode(t);
100
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && !this.initializedBlocks.get(e) && this._fetchBlockData(e), [
101
+ 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), [
101
102
  this.algorithmControl,
102
103
  this.localeControl,
103
104
  this.currencyControl,
104
105
  this.productLayoutControl,
105
106
  this.filtersControl,
106
107
  this.shuffleControl
107
- ].forEach((r) => {
108
- var s;
109
- r != null && r.api && (r.currentNode = t, (s = r.onTemplateNodeUpdated) == null || s.call(r, t));
108
+ ].forEach((i) => {
109
+ var a;
110
+ i != null && i.api && (i.currentNode = t, (a = i.onTemplateNodeUpdated) == null || a.call(i, t));
110
111
  });
111
112
  }
112
113
  onDestroy() {
@@ -139,13 +140,48 @@ class x extends h {
139
140
  e && (e.api = this.api, e.currentNode = this.currentNode, (o = e.onRender) == null || o.call(e));
140
141
  });
141
142
  }
143
+ /**
144
+ * Syncs persisted node config into the Pinia store's block state.
145
+ *
146
+ * setCurrentBlock() creates a default entry (USD, en_US, mostPopular).
147
+ * For saved templates, the real config lives in the node (e.g., TRY, tr_TR).
148
+ * This method reads it and patches the store so fetchRecommendationProducts()
149
+ * uses the correct values.
150
+ *
151
+ * Uses triggerRefetch: false because the fetch hasn't happened yet —
152
+ * values are being prepared for the upcoming initial fetch.
153
+ */
154
+ _syncNodeConfigToStore() {
155
+ var r;
156
+ const t = l.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
157
+ this.store.patchCurrentBlockConfig({
158
+ strategy: t.strategy,
159
+ language: t.language,
160
+ size: t.size,
161
+ productIds: t.productIds,
162
+ // Only sync filters from node config during initial load.
163
+ // After initialization, the Pinia store is the source of truth
164
+ // for filters (edited via the filter drawer).
165
+ ...o ? {} : { filters: t.filters },
166
+ shuffleProducts: t.shuffleProducts,
167
+ currencySettings: {
168
+ name: t.currency.code,
169
+ value: t.currency.code,
170
+ symbol: t.currency.symbol,
171
+ alignment: t.currency.alignment === "before" ? "0" : "1",
172
+ decimalCount: t.currency.decimalCount.toString(),
173
+ decimalSeparator: t.currency.decimalSeparator,
174
+ thousandSeparator: t.currency.thousandSeparator
175
+ }
176
+ }, { triggerRefetch: !1 });
177
+ }
142
178
  /**
143
179
  * Fetches initial data for a block (filters, algorithms, products).
144
180
  * Shared by onRender() and onTemplateNodeUpdated() to avoid duplication.
145
181
  * Marks the block as initialized to prevent redundant fetches on re-selection.
146
182
  */
147
183
  async _fetchBlockData(t) {
148
- t !== null && this.initializedBlocks.set(t, !0), (await Promise.allSettled([
184
+ t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
149
185
  this.store.fetchRecommendationFilters(),
150
186
  this.store.fetchRecommendationCreateData(),
151
187
  this.store.fetchRecommendationProducts()
@@ -161,7 +197,7 @@ class x extends h {
161
197
  * Reads the recommendation-id attribute from the block element within the node
162
198
  */
163
199
  _getRecommendationIdFromNode(t) {
164
- const e = b(t);
200
+ const e = I(t);
165
201
  if (!e || !("getAttribute" in e))
166
202
  return null;
167
203
  const o = e.getAttribute(T);
@@ -182,36 +218,52 @@ class x extends h {
182
218
  let e = t.recommendationProducts, o = t.$state.configVersion;
183
219
  this.storeUnsubscription = t.$subscribe(() => {
184
220
  const r = t.$state.configVersion;
185
- r !== o && (o = r, this._debouncedFetchProducts());
186
- const s = t.recommendationProducts, a = s !== e, u = Array.isArray(s) && s.length > 0;
187
- a && u && (e = s, this._debouncedRegenerateWithProducts());
221
+ r !== o && (o = r, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
222
+ const i = t.recommendationProducts, a = i !== e, d = Array.isArray(i) && i.length > 0;
223
+ a && d && (e = i, this._debouncedRegenerateWithProducts());
188
224
  });
189
225
  }
226
+ /**
227
+ * Persists the current filter state from Pinia store to the Stripo node config.
228
+ * This ensures filters survive template save/reload cycles.
229
+ */
230
+ _persistFiltersToNodeConfig() {
231
+ if (!this.currentNode || !this.api)
232
+ return;
233
+ const { filters: t } = this.store.recommendationConfigs;
234
+ l.updateConfig(
235
+ this.api,
236
+ this.currentNode,
237
+ { filters: t },
238
+ "Update recommendation filters"
239
+ );
240
+ }
190
241
  }
191
242
  export {
192
- M as ALGORITHM_CONTROL_ID,
193
- f as AlgorithmControl,
194
- L as CONTROL_BLOCK_ID,
195
- W as CURRENCY_CONTROL_ID,
196
- p as CurrencyControl,
197
- H as FILTERS_CONTROL_ID,
198
- g as FiltersControl,
199
- j as LOCALE_CONTROL_ID,
200
- R as LocaleControl,
201
- K as PRODUCT_LAYOUT_CONTROL_ID,
202
- _ as ProductLayoutControl,
203
- x as RecommendationBlockControl,
204
- J as SHUFFLE_CONTROL_ID,
205
- N as ShuffleControl,
206
- X as formatProductPrice,
207
- b as getBlockElement,
208
- Z as getCardComposition,
209
- tt as getCurrentLayout,
210
- et as reapplySpacing,
211
- ot as regenerateProductRows,
212
- P as regenerateProductRowsWithStyles,
213
- rt as setCurrencyAttributes,
214
- it as updatePricesInPlace,
215
- y as updateProductContentInPlace,
216
- nt as updateSingleProductContent
243
+ W as ALGORITHM_CONTROL_ID,
244
+ p as AlgorithmControl,
245
+ S as CONTROL_BLOCK_ID,
246
+ H as CURRENCY_CONTROL_ID,
247
+ g as CurrencyControl,
248
+ j as FILTERS_CONTROL_ID,
249
+ R as FiltersControl,
250
+ K as LOCALE_CONTROL_ID,
251
+ N as LocaleControl,
252
+ J as PRODUCT_LAYOUT_CONTROL_ID,
253
+ y as ProductLayoutControl,
254
+ z as RecommendationBlockControl,
255
+ X as SHUFFLE_CONTROL_ID,
256
+ _ as ShuffleControl,
257
+ tt as formatProductPrice,
258
+ I as getBlockElement,
259
+ et as getCardComposition,
260
+ ot as getCurrentLayout,
261
+ rt as reapplySpacing,
262
+ it as regenerateMobileProductRows,
263
+ nt as regenerateProductRows,
264
+ b as regenerateProductRowsWithStyles,
265
+ st as setCurrencyAttributes,
266
+ ct as updatePricesInPlace,
267
+ P as updateProductContentInPlace,
268
+ at as updateSingleProductContent
217
269
  };
@@ -1,45 +1,60 @@
1
- var d = Object.defineProperty;
2
- var a = (i, e, t) => e in i ? d(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
- var n = (i, e, t) => a(i, typeof e != "symbol" ? e + "" : e, t);
4
- import { CommonControl as c } from "../../../common-control.js";
5
- import { MAX_PRODUCT_COUNT as _, MAX_PRODUCTS_PER_ROW as h } from "../../constants/layout.js";
6
- import { RecommendationConfigService as r } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as R } from "../../store/recommendation.js";
8
- import { getCurrentLayout as C, regenerateProductRowsWithStyles as p } from "./utils.js";
9
- import { useDebounceFn as m } from "../../../../../node_modules/@vueuse/shared/index.js";
10
- const l = "recommendation-product-layout-control", o = {
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";
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 = {
11
13
  PRODUCT_COUNT: "size",
12
14
  PRODUCT_IN_ROW: "cardsInRow",
13
- PRODUCT_IN_ROW_LABEL: "cardsInRowLabel"
15
+ PRODUCT_IN_ROW_LABEL: "cardsInRowLabel",
16
+ MOBILE_CARDS_IN_ROW: "mobileCardsInRow",
17
+ MOBILE_CARDS_IN_ROW_LABEL: "mobileCardsInRowLabel"
14
18
  };
15
- class I extends c {
19
+ class S extends h {
16
20
  constructor() {
17
21
  super(...arguments);
18
22
  // Store is used for backward compatibility with product fetching and regeneration
19
- n(this, "store", R());
20
- n(this, "storeUnsubscription", () => {
23
+ d(this, "store", l());
24
+ d(this, "storeUnsubscription", () => {
21
25
  });
22
- n(this, "_debouncedRegenerateProductRows", m(() => {
26
+ d(this, "_debouncedRegenerateProductRows", N(() => {
23
27
  this._regenerateProductRows();
24
28
  }, 500));
25
29
  }
26
30
  getId() {
27
- return l;
31
+ return P;
28
32
  }
29
33
  getTemplate() {
30
34
  return `
31
35
  <div class="product-layout-control-container">
32
36
  ${this._GuTwoColumns([
33
37
  this._GuLabel({ text: "Number of Products" }),
34
- this._GuCounter({ name: o.PRODUCT_COUNT, maxValue: _ }),
35
- this._GuLabel({ text: "Products in One Row", name: o.PRODUCT_IN_ROW_LABEL }),
36
- this._GuCounter({ name: o.PRODUCT_IN_ROW, maxValue: h })
38
+ this._GuCounter({ name: e.PRODUCT_COUNT, maxValue: C }),
39
+ this._GuLabel({
40
+ text: "Products in One Row on Desktop",
41
+ name: e.PRODUCT_IN_ROW_LABEL
42
+ }),
43
+ this._GuCounter({ name: e.PRODUCT_IN_ROW, maxValue: p }),
44
+ this._GuLabel({
45
+ text: "Products in One Row on Mobile",
46
+ name: e.MOBILE_CARDS_IN_ROW_LABEL
47
+ }),
48
+ this._GuCounter({
49
+ name: e.MOBILE_CARDS_IN_ROW,
50
+ maxValue: m
51
+ })
37
52
  ])}
38
53
  </div>
39
54
  `;
40
55
  }
41
56
  onRender() {
42
- this._setFormValues(), this._updateProductsInRowVisibility(), this._listenToFormUpdates(), this._listenStateUpdates();
57
+ this._setFormValues(), this._updateProductsInRowVisibility(), this._listenToFormUpdates(), this._listenStateUpdates(), this._subscribeToEditorModeChanges();
43
58
  }
44
59
  onTemplateNodeUpdated(t) {
45
60
  super.onTemplateNodeUpdated(t), this._setFormValues(), this._updateProductsInRowVisibility();
@@ -48,23 +63,45 @@ class I extends c {
48
63
  this.storeUnsubscription();
49
64
  }
50
65
  _setFormValues() {
51
- const t = r.getConfig(this.currentNode);
66
+ const t = s.getConfig(this.currentNode);
52
67
  this.api.updateValues({
53
- [o.PRODUCT_COUNT]: t.size,
54
- [o.PRODUCT_IN_ROW]: t.cardsInRow
68
+ [e.PRODUCT_COUNT]: t.size,
69
+ [e.PRODUCT_IN_ROW]: t.cardsInRow,
70
+ [e.MOBILE_CARDS_IN_ROW]: t.mobileCardsInRow
55
71
  });
56
72
  }
57
73
  /**
58
- * Updates "Products in One Row" visibility based on layout orientation
59
- * This control is hidden for list layout (products always take full width)
60
- * Reads from node config first, falls back to DOM
74
+ * Checks if the editor is currently in mobile preview mode
75
+ * using Stripo's EditorStatePropertyType API.
76
+ */
77
+ _isMobileMode() {
78
+ return this.api.getEditorState()[u.previewDeviceMode] === R.MOBILE;
79
+ }
80
+ /**
81
+ * Updates counter visibility based on layout orientation and editor mode.
82
+ * - List layout: hide both counters (products always full-width)
83
+ * - Grid + desktop mode: show desktop counter, hide mobile counter
84
+ * - Grid + mobile mode: show mobile counter, hide desktop counter
61
85
  */
62
86
  _updateProductsInRowVisibility() {
63
- const u = (r.getConfig(this.currentNode).layout || C(this.currentNode)) === "grid";
64
- this.api.setVisibility(o.PRODUCT_IN_ROW, u), this.api.setVisibility(o.PRODUCT_IN_ROW_LABEL, u);
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);
89
+ }
90
+ /**
91
+ * Subscribes to editor preview mode changes via Stripo API.
92
+ * When the user switches between desktop/mobile preview, toggles
93
+ * which "Products in One Row" counter is visible.
94
+ */
95
+ _subscribeToEditorModeChanges() {
96
+ this.api.onEditorStatePropUpdated(
97
+ u.previewDeviceMode,
98
+ () => {
99
+ this._updateProductsInRowVisibility();
100
+ }
101
+ );
65
102
  }
66
103
  _onProductCountChange(t) {
67
- this.currentNode && (r.updateConfig(
104
+ this.currentNode && (s.updateConfig(
68
105
  this.api,
69
106
  this.currentNode,
70
107
  { size: t },
@@ -72,24 +109,37 @@ class I extends c {
72
109
  ), this.store.patchCurrentBlockConfig({ size: t }), this._debouncedRegenerateProductRows());
73
110
  }
74
111
  _onProductsInRowChange(t) {
75
- this.currentNode && (r.updateConfig(
112
+ this.currentNode && (s.updateConfig(
76
113
  this.api,
77
114
  this.currentNode,
78
115
  { cardsInRow: t },
79
116
  `Changed products per row to ${t}`
80
117
  ), this.store.patchCurrentBlockConfig({ cardsInRow: t }), this._debouncedRegenerateProductRows());
81
118
  }
119
+ _onMobileCardsInRowChange(t) {
120
+ this.currentNode && (s.updateConfig(
121
+ this.api,
122
+ this.currentNode,
123
+ { mobileCardsInRow: t },
124
+ `Changed mobile products per row to ${t}`
125
+ ), O(this.api), g({
126
+ currentNode: this.currentNode,
127
+ documentModifier: this.api.getDocumentModifier()
128
+ }));
129
+ }
82
130
  _regenerateProductRows() {
83
- p({
131
+ b({
84
132
  currentNode: this.currentNode,
85
133
  documentModifier: this.api.getDocumentModifier()
86
134
  });
87
135
  }
88
136
  _listenToFormUpdates() {
89
- this.api.onValueChanged(o.PRODUCT_COUNT, (t) => {
137
+ this.api.onValueChanged(e.PRODUCT_COUNT, (t) => {
90
138
  this._onProductCountChange(t.toString());
91
- }), this.api.onValueChanged(o.PRODUCT_IN_ROW, (t) => {
139
+ }), this.api.onValueChanged(e.PRODUCT_IN_ROW, (t) => {
92
140
  this._onProductsInRowChange(Number(t));
141
+ }), this.api.onValueChanged(e.MOBILE_CARDS_IN_ROW, (t) => {
142
+ this._onMobileCardsInRowChange(Number(t));
93
143
  });
94
144
  }
95
145
  /**
@@ -99,12 +149,12 @@ class I extends c {
99
149
  _listenStateUpdates() {
100
150
  let t = this.store.recommendationConfigs.orientation;
101
151
  this.storeUnsubscription = this.store.$subscribe(() => {
102
- const s = this.store.recommendationConfigs.orientation;
103
- s !== t && (t = s, this._updateProductsInRowVisibility());
152
+ const a = this.store.recommendationConfigs.orientation;
153
+ a !== t && (t = a, this._updateProductsInRowVisibility());
104
154
  });
105
155
  }
106
156
  }
107
157
  export {
108
- l as PRODUCT_LAYOUT_CONTROL_ID,
109
- I as ProductLayoutControl
158
+ P as PRODUCT_LAYOUT_CONTROL_ID,
159
+ S as ProductLayoutControl
110
160
  };