@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,45 +1,55 @@
1
- var L = Object.defineProperty;
2
- var _ = (o, e, t) => e in o ? L(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
- var r = (o, e, t) => _(o, typeof e != "symbol" ? e + "" : e, t);
4
- import { ModificationDescription as p } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as h } from "../../../common-control.js";
6
- import { LAYOUT_OPTIONS as N, DEFAULT_MOBILE_ROW_SPACING as m, DEFAULT_MOBILE_COLUMN_SPACING as d, DEFAULT_ROW_SPACING as l, DEFAULT_COLUMN_SPACING as g } from "../../constants/layout.js";
1
+ var O = Object.defineProperty;
2
+ var p = (o, e, t) => e in o ? O(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
+ var r = (o, e, t) => p(o, typeof e != "symbol" ? e + "" : e, t);
4
+ import { ModificationDescription as h } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as N } from "../../../common-control.js";
6
+ import { ATTR_DATA_CUSTOM_ATTRIBUTES as L } from "../../constants/selectors.js";
7
+ import { LAYOUT_OPTIONS as T, DEFAULT_MOBILE_ROW_SPACING as m, DEFAULT_MOBILE_COLUMN_SPACING as d, DEFAULT_ROW_SPACING as l, DEFAULT_COLUMN_SPACING as g } from "../../constants/layout.js";
8
+ import { DEFAULT_COMPOSITION as _ } from "../../constants/defaultConfig.js";
7
9
  import { RecommendationConfigService as s } from "../../services/configService.js";
8
- import { useRecommendationExtensionStore as A } from "../../store/recommendation.js";
9
- import { getCurrentLayout as C, getBlockElement as O, regenerateProductRowsWithStyles as f } from "../main/utils.js";
10
- const S = "recommendation-layout-control", a = {
10
+ import { useRecommendationExtensionStore as f } from "../../store/recommendation.js";
11
+ import { getCurrentLayout as C, getBlockElement as I, regenerateProductRowsWithStyles as S } from "./utils.js";
12
+ const U = "recommendation-layout-orientation-control", A = "recommendationInfoBannerTest", a = {
11
13
  LAYOUT: "layout"
12
- }, n = {
14
+ }, y = "data-card-composition", i = {
13
15
  LAYOUT: "data-layout",
14
16
  COLUMN_SPACING: "data-column-spacing",
15
17
  ROW_SPACING: "data-row-spacing",
16
18
  MOBILE_COLUMN_SPACING: "data-mobile-column-spacing",
17
19
  MOBILE_ROW_SPACING: "data-mobile-row-spacing"
18
20
  };
19
- class G extends h {
21
+ class Y extends N {
20
22
  constructor() {
21
23
  super(...arguments);
22
- r(this, "store", A());
24
+ r(this, "store", f());
23
25
  r(this, "isChangingLayout", !1);
24
26
  }
25
27
  getId() {
26
- return S;
28
+ return U;
27
29
  }
28
30
  getTemplate() {
29
31
  return `
30
32
  <div class="layout-control-container">
33
+ ${this._GuOnPageMessage({ name: A })}
34
+
31
35
  ${this._GuTwoColumns([
32
- this._GuLabel({ text: "Layout Orientation" }),
36
+ this._GuLabel({ text: this.api.translate("Layout Orientation") }),
33
37
  this._GuRadioButton({
34
38
  name: a.LAYOUT,
35
- buttons: N
39
+ buttons: T
36
40
  })
37
41
  ])}
38
42
  </div>
39
43
  `;
40
44
  }
41
45
  onRender() {
42
- this._setFormValues(), this._listenToFormUpdates();
46
+ this._setInfoMessageValue(
47
+ A,
48
+ this.api.translate(
49
+ `Note that updating the Layout Orientation, Number of Products and
50
+ Products in One Row settings resets the style of your Recommendation block.`
51
+ )
52
+ ), this._setFormValues(), this._listenToFormUpdates();
43
53
  }
44
54
  onTemplateNodeUpdated(t) {
45
55
  super.onTemplateNodeUpdated(t), this._setFormValues();
@@ -57,7 +67,7 @@ class G extends h {
57
67
  _onLayoutChange(t) {
58
68
  if (this.isChangingLayout || !this.currentNode || (s.getConfig(this.currentNode).layout || C(this.currentNode)) === t)
59
69
  return;
60
- const c = O(this.currentNode);
70
+ const c = I(this.currentNode);
61
71
  if (c) {
62
72
  this.isChangingLayout = !0;
63
73
  try {
@@ -73,8 +83,8 @@ class G extends h {
73
83
  },
74
84
  `Changed layout to ${t}`
75
85
  ), this.store.patchCurrentBlockConfig({ orientation: t });
76
- const i = this.api.getDocumentModifier().modifyHtml(c).setAttribute(n.LAYOUT, t).setAttribute(n.COLUMN_SPACING, g.toString()).setAttribute(n.ROW_SPACING, l.toString()).setAttribute(n.MOBILE_COLUMN_SPACING, d.toString()).setAttribute(n.MOBILE_ROW_SPACING, m.toString());
77
- t === "list" ? (i.setClass("es-m-p0"), i.setClass("ins-recommendation-list-layout")) : (i.removeClass("es-m-p0"), i.removeClass("ins-recommendation-list-layout")), i.apply(new p(`Update layout to ${t}`)), this._regenerateProductRows(t);
86
+ const n = this.api.getDocumentModifier().modifyHtml(c).setAttribute(i.LAYOUT, t).setAttribute(i.COLUMN_SPACING, g.toString()).setAttribute(i.ROW_SPACING, l.toString()).setAttribute(i.MOBILE_COLUMN_SPACING, d.toString()).setAttribute(i.MOBILE_ROW_SPACING, m.toString());
87
+ t === "list" ? (n.setClass("es-m-p0"), n.setClass("ins-recommendation-list-layout")) : (n.removeClass("es-m-p0"), n.removeClass("ins-recommendation-list-layout")), n.setAttribute(y, _.join(",")).setAttribute(L, "[]"), n.apply(new h(`Update layout to ${t}`)), this._regenerateProductRows(t);
78
88
  } finally {
79
89
  this.isChangingLayout = !1;
80
90
  }
@@ -86,10 +96,11 @@ class G extends h {
86
96
  * @param layout - The layout to use for regeneration (passed explicitly to avoid stale DOM reads)
87
97
  */
88
98
  _regenerateProductRows(t) {
89
- this.currentNode && f({
99
+ this.currentNode && S({
90
100
  currentNode: this.currentNode,
91
101
  documentModifier: this.api.getDocumentModifier(),
92
- layout: t
102
+ layout: t,
103
+ composition: _
93
104
  });
94
105
  }
95
106
  _listenToFormUpdates() {
@@ -99,6 +110,6 @@ class G extends h {
99
110
  }
100
111
  }
101
112
  export {
102
- S as LAYOUT_CONTROL_ID,
103
- G as LayoutControl
113
+ U as LAYOUT_ORIENTATION_CONTROL_ID,
114
+ Y as LayoutOrientationControl
104
115
  };
@@ -21,10 +21,10 @@ class E extends c {
21
21
  return `
22
22
  <div class="locale-control-container">
23
23
  ${this._GuTwoColumns([
24
- this._GuLabel({ text: "Recommendation Locale" }),
24
+ this._GuLabel({ text: this.api.translate("Recommendation Locale") }),
25
25
  this._GuSelect({
26
26
  name: n.LOCALE,
27
- placeholder: "Select Recommendation Locale",
27
+ placeholder: this.api.translate("Select Recommendation Locale"),
28
28
  options: this.store.getLanguages
29
29
  })
30
30
  ])}
@@ -0,0 +1,58 @@
1
+ var s = Object.defineProperty;
2
+ var u = (e, o, t) => o in e ? s(e, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[o] = t;
3
+ var i = (e, o, t) => u(e, typeof o != "symbol" ? o + "" : o, t);
4
+ import { CommonControl as a } from "../../../common-control.js";
5
+ import { MAX_PRODUCT_COUNT as c } from "../../constants/layout.js";
6
+ import { RecommendationConfigService as n } from "../../services/configService.js";
7
+ import { useRecommendationExtensionStore as d } from "../../store/recommendation.js";
8
+ const C = "recommendation-product-count-control", r = {
9
+ PRODUCT_COUNT: "size"
10
+ };
11
+ class g extends a {
12
+ constructor() {
13
+ super(...arguments);
14
+ i(this, "store", d());
15
+ }
16
+ getId() {
17
+ return C;
18
+ }
19
+ getTemplate() {
20
+ return `
21
+ <div class="product-count-control-container">
22
+ ${this._GuTwoColumns([
23
+ this._GuLabel({ text: this.api.translate("Number of Products") }),
24
+ this._GuCounter({ name: r.PRODUCT_COUNT, maxValue: c })
25
+ ])}
26
+ </div>
27
+ `;
28
+ }
29
+ onRender() {
30
+ this._setFormValues(), this._listenToFormUpdates();
31
+ }
32
+ onTemplateNodeUpdated(t) {
33
+ super.onTemplateNodeUpdated(t), this._setFormValues();
34
+ }
35
+ _setFormValues() {
36
+ const t = n.getConfig(this.currentNode);
37
+ this.api.updateValues({
38
+ [r.PRODUCT_COUNT]: t.size
39
+ });
40
+ }
41
+ _onProductCountChange(t) {
42
+ !this.currentNode || n.getConfig(this.currentNode).size === t || (n.updateConfig(
43
+ this.api,
44
+ this.currentNode,
45
+ { size: t },
46
+ `Changed product count to ${t}`
47
+ ), this.store.patchCurrentBlockConfig({ size: t }));
48
+ }
49
+ _listenToFormUpdates() {
50
+ this.api.onValueChanged(r.PRODUCT_COUNT, (t) => {
51
+ this._onProductCountChange(t.toString());
52
+ });
53
+ }
54
+ }
55
+ export {
56
+ C as PRODUCT_COUNT_CONTROL_ID,
57
+ g as ProductCountControl
58
+ };
@@ -1,60 +1,80 @@
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";
1
+ var I = Object.defineProperty;
2
+ var E = (a, n, t) => n in a ? I(a, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[n] = t;
3
+ var u = (a, n, t) => E(a, typeof n != "symbol" ? n + "" : n, t);
4
+ import { EditorStatePropertyType as O, PreviewDeviceMode as f, UEAttr as N } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as T } from "../../../common-control.js";
6
+ import { MAX_PRODUCTS_PER_ROW as p, MAX_MOBILE_PRODUCTS_PER_ROW as M, DEFAULT_MOBILE_CARDS_IN_ROW as L, DEFAULT_COLUMN_SPACING as P, DEFAULT_MOBILE_COLUMN_SPACING as y } from "../../constants/layout.js";
7
7
  import { RecommendationConfigService as i } from "../../services/configService.js";
8
- import { useRecommendationExtensionStore as l } from "../../store/recommendation.js";
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
- PRODUCT_COUNT: "size",
8
+ import { useRecommendationExtensionStore as D } from "../../store/recommendation.js";
9
+ import { setMobileLayoutOptOut as m, ensureMobileCssRulesExist as C } from "../mobileLayout/cssRules.js";
10
+ import { getCurrentLayout as w, getBlockElement as U, regenerateProductRowsWithStyles as b, regenerateMobileProductRows as g, adjustProductsToSize as A } from "./utils.js";
11
+ import { useDebounceFn as B } from "../../../../../node_modules/@vueuse/shared/index.js";
12
+ const S = "recommendation-product-layout-control", e = {
13
+ // "Products in One Row" — shown when toggle is OFF
14
14
  PRODUCT_IN_ROW: "cardsInRow",
15
15
  PRODUCT_IN_ROW_LABEL: "cardsInRowLabel",
16
+ // "Products in One Row on Desktop" — shown when toggle is ON + desktop preview
17
+ PRODUCT_IN_ROW_DESKTOP: "cardsInRowDesktop",
18
+ PRODUCT_IN_ROW_DESKTOP_LABEL: "cardsInRowDesktopLabel",
19
+ // "Products in One Row on Mobile" — shown when toggle is ON + mobile preview
16
20
  MOBILE_CARDS_IN_ROW: "mobileCardsInRow",
17
- MOBILE_CARDS_IN_ROW_LABEL: "mobileCardsInRowLabel"
21
+ MOBILE_CARDS_IN_ROW_LABEL: "mobileCardsInRowLabel",
22
+ // Toggle
23
+ MOBILE_LAYOUT_TOGGLE: "mobileLayoutEnabled",
24
+ MOBILE_LAYOUT_LABEL: "mobileLayoutLabel"
18
25
  };
19
- class V extends R {
26
+ class v extends T {
20
27
  constructor() {
21
28
  super(...arguments);
22
29
  // Store is used for backward compatibility with product fetching and regeneration
23
- a(this, "store", l());
24
- a(this, "storeUnsubscription", () => {
30
+ u(this, "store", D());
31
+ u(this, "storeUnsubscription", () => {
25
32
  });
26
- a(this, "_debouncedRegenerateProductRows", L(() => {
33
+ u(this, "_debouncedRegenerateProductRows", B(() => {
27
34
  this._regenerateProductRows();
28
35
  }, 500));
29
36
  }
30
37
  getId() {
31
- return M;
38
+ return S;
32
39
  }
33
40
  getTemplate() {
34
41
  return `
35
- <div class="product-layout-control-container">
42
+ <div class="product-layout-control-container" data-product-layout-control>
36
43
  ${this._GuTwoColumns([
37
- this._GuLabel({ text: "Number of Products" }),
38
- this._GuCounter({ name: e.PRODUCT_COUNT, maxValue: h }),
39
44
  this._GuLabel({
40
- text: "Products in One Row on Desktop",
45
+ text: this.api.translate("Products in One Row"),
41
46
  name: e.PRODUCT_IN_ROW_LABEL
42
47
  }),
43
48
  this._GuCounter({ name: e.PRODUCT_IN_ROW, maxValue: p }),
44
49
  this._GuLabel({
45
- text: "Products in One Row on Mobile",
50
+ text: this.api.translate("Products in One Row on Desktop"),
51
+ name: e.PRODUCT_IN_ROW_DESKTOP_LABEL
52
+ }),
53
+ this._GuCounter({ name: e.PRODUCT_IN_ROW_DESKTOP, maxValue: p }),
54
+ this._GuLabel({
55
+ text: this.api.translate("Products in One Row on Mobile"),
46
56
  name: e.MOBILE_CARDS_IN_ROW_LABEL
47
57
  }),
48
58
  this._GuCounter({
49
59
  name: e.MOBILE_CARDS_IN_ROW,
50
- maxValue: g
51
- })
60
+ maxValue: M
61
+ }),
62
+ this._GuLabel({
63
+ text: this.api.translate("Mobile Layout Optimization"),
64
+ name: e.MOBILE_LAYOUT_LABEL,
65
+ hint: this.api.translate(
66
+ `Mobile Layout Optimization lets you display up to 2 products in one row.
67
+ Note that enabling Mobile Layout Optimization increases the size of
68
+ your recommendation block.`
69
+ )
70
+ }),
71
+ this._GuToggle(e.MOBILE_LAYOUT_TOGGLE)
52
72
  ])}
53
73
  </div>
54
74
  `;
55
75
  }
56
76
  onRender() {
57
- this._setFormValues(), this._updateProductsInRowVisibility(), this._listenToFormUpdates(), this._listenStateUpdates(), this._subscribeToEditorModeChanges();
77
+ this._initializeMobileLayoutToggle(), this._setFormValues(), this._updateProductsInRowVisibility(), this._listenToFormUpdates(), this._listenStateUpdates(), this._listenEditorModeChanges();
58
78
  }
59
79
  onTemplateNodeUpdated(t) {
60
80
  super.onTemplateNodeUpdated(t), this._setFormValues(), this._updateProductsInRowVisibility();
@@ -65,53 +85,104 @@ class V extends R {
65
85
  _setFormValues() {
66
86
  const t = i.getConfig(this.currentNode);
67
87
  this.api.updateValues({
68
- [e.PRODUCT_COUNT]: t.size,
69
88
  [e.PRODUCT_IN_ROW]: t.cardsInRow,
89
+ [e.PRODUCT_IN_ROW_DESKTOP]: t.cardsInRow,
90
+ [e.MOBILE_LAYOUT_TOGGLE]: t.mobileLayoutEnabled,
70
91
  [e.MOBILE_CARDS_IN_ROW]: t.mobileCardsInRow
71
92
  });
72
93
  }
73
94
  /**
74
- * Checks if the editor is currently in mobile preview mode
75
- * using Stripo's EditorStatePropertyType API.
95
+ * Returns true when the editor preview is in mobile mode.
76
96
  */
77
- _isMobileMode() {
78
- return this.api.getEditorState()[c.previewDeviceMode] === C.MOBILE;
97
+ _isMobilePreview() {
98
+ return this.api.getEditorState()[O.previewDeviceMode] === f.MOBILE;
79
99
  }
80
100
  /**
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
101
+ * Updates counter visibility based on layout, mobileLayoutEnabled, and editor preview mode.
102
+ *
103
+ * Three mutually exclusive rows exist in the template:
104
+ * 1. "Products in One Row" — generic, shown when toggle is OFF
105
+ * 2. "Products in One Row on Desktop" — shown when toggle is ON + desktop preview
106
+ * 3. "Products in One Row on Mobile" — shown when toggle is ON + mobile preview
107
+ *
108
+ * Only one row is visible at a time. This avoids setUIEAttribute on hidden
109
+ * elements, which Stripo does not apply reliably.
85
110
  */
86
111
  _updateProductsInRowVisibility() {
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);
112
+ var R;
113
+ const t = i.getConfig(this.currentNode), o = (t.layout || w(this.currentNode)) === "grid", { mobileLayoutEnabled: r } = t, d = this._isMobilePreview();
114
+ this.api.setVisibility(e.MOBILE_LAYOUT_TOGGLE, o), this.api.setVisibility(e.MOBILE_LAYOUT_LABEL, o);
115
+ const c = o && !r, _ = o && r && !d, l = o && r && d;
116
+ this.api.setVisibility(e.PRODUCT_IN_ROW, c), this.api.setVisibility(e.PRODUCT_IN_ROW_LABEL, c), this.api.setVisibility(e.PRODUCT_IN_ROW_DESKTOP, _), this.api.setVisibility(e.PRODUCT_IN_ROW_DESKTOP_LABEL, _), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW, l), this.api.setVisibility(e.MOBILE_CARDS_IN_ROW_LABEL, l);
117
+ const h = (R = this.getContainer()) == null ? void 0 : R.querySelector("[data-product-layout-control]");
118
+ h && (h.style.display = o ? "" : "none");
89
119
  }
90
120
  /**
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.
121
+ * Initializes the mobile layout toggle using the same pattern as shuffle.ts.
122
+ * Sets the initial UI attribute value from the node config.
94
123
  */
95
- _subscribeToEditorModeChanges() {
96
- this.api.onEditorStatePropUpdated(
97
- c.previewDeviceMode,
98
- () => {
99
- this._updateProductsInRowVisibility();
100
- }
124
+ _initializeMobileLayoutToggle() {
125
+ const t = i.getConfig(this.currentNode);
126
+ this.api.setUIEAttribute(
127
+ e.MOBILE_LAYOUT_TOGGLE,
128
+ N.SELECTPICKER.items,
129
+ t.mobileLayoutEnabled
101
130
  );
102
131
  }
103
- _onProductCountChange(t) {
104
- !this.currentNode || i.getConfig(this.currentNode).size === t || (i.updateConfig(
105
- this.api,
106
- this.currentNode,
107
- { size: t },
108
- `Changed product count to ${t}`
109
- ), this.store.patchCurrentBlockConfig({ size: t }));
132
+ /**
133
+ * Handles mobile layout toggle change.
134
+ * When enabled: opts in to mobile layout, restores previous mobileCardsInRow.
135
+ * When disabled: snapshots current mobileCardsInRow, resets to 1, opts out.
136
+ */
137
+ _onMobileLayoutToggleChange(t) {
138
+ if (!this.currentNode)
139
+ return;
140
+ const s = i.getConfig(this.currentNode);
141
+ if (s.mobileLayoutEnabled === t)
142
+ return;
143
+ const o = U(this.currentNode);
144
+ if (t) {
145
+ const r = s.previousMobileCardsInRow || L;
146
+ i.updateConfig(
147
+ this.api,
148
+ this.currentNode,
149
+ {
150
+ mobileLayoutEnabled: !0,
151
+ mobileCardsInRow: r
152
+ },
153
+ "Enabled mobile layout optimization"
154
+ ), this.store.patchCurrentBlockConfig(
155
+ { mobileLayoutEnabled: !0 },
156
+ { triggerRefetch: !1 }
157
+ ), o && m(this.api, o, !1), C(this.api), b({
158
+ currentNode: this.currentNode,
159
+ documentModifier: this.api.getDocumentModifier()
160
+ });
161
+ } else {
162
+ const r = s.mobileCardsInRow;
163
+ i.updateConfig(
164
+ this.api,
165
+ this.currentNode,
166
+ {
167
+ mobileLayoutEnabled: !1,
168
+ previousMobileCardsInRow: r,
169
+ mobileCardsInRow: L
170
+ },
171
+ "Disabled mobile layout optimization"
172
+ ), this.store.patchCurrentBlockConfig(
173
+ { mobileLayoutEnabled: !1 },
174
+ { triggerRefetch: !1 }
175
+ ), o && m(this.api, o, !0), g({
176
+ currentNode: this.currentNode,
177
+ documentModifier: this.api.getDocumentModifier()
178
+ });
179
+ }
180
+ this._setFormValues(), this._updateProductsInRowVisibility();
110
181
  }
111
182
  _onProductsInRowChange(t) {
112
183
  if (!this.currentNode || i.getConfig(this.currentNode).cardsInRow === t)
113
184
  return;
114
- const o = t === 1 ? 0 : m;
185
+ const o = t === 1 ? 0 : P;
115
186
  i.updateConfig(
116
187
  this.api,
117
188
  this.currentNode,
@@ -122,37 +193,39 @@ class V extends R {
122
193
  _onMobileCardsInRowChange(t) {
123
194
  if (!this.currentNode || i.getConfig(this.currentNode).mobileCardsInRow === t)
124
195
  return;
125
- const o = t === 1 ? 0 : O;
196
+ const o = t === 1 ? 0 : y;
126
197
  i.updateConfig(
127
198
  this.api,
128
199
  this.currentNode,
129
200
  { mobileCardsInRow: t, mobileColumnSpacing: o },
130
201
  `Changed mobile products per row to ${t}`
131
- ), I(this.api), f({
202
+ ), C(this.api), g({
132
203
  currentNode: this.currentNode,
133
204
  documentModifier: this.api.getDocumentModifier()
134
205
  });
135
206
  }
136
207
  _regenerateProductRows() {
137
- const t = i.getConfig(this.currentNode), s = parseInt(t.size) || 6, o = b(this.store.recommendationProducts, s);
138
- P({
208
+ const t = i.getConfig(this.currentNode), s = parseInt(t.size) || 6, o = A(this.store.recommendationProducts, s);
209
+ b({
139
210
  currentNode: this.currentNode,
140
211
  documentModifier: this.api.getDocumentModifier(),
141
212
  products: o
142
213
  });
143
214
  }
144
215
  _listenToFormUpdates() {
145
- this.api.onValueChanged(e.PRODUCT_COUNT, (t) => {
146
- this._onProductCountChange(t.toString());
147
- }), this.api.onValueChanged(e.PRODUCT_IN_ROW, (t) => {
216
+ this.api.onValueChanged(e.PRODUCT_IN_ROW, (t) => {
148
217
  this._onProductsInRowChange(Number(t));
218
+ }), this.api.onValueChanged(e.PRODUCT_IN_ROW_DESKTOP, (t) => {
219
+ this._onProductsInRowChange(Number(t));
220
+ }), this.api.onValueChanged(e.MOBILE_LAYOUT_TOGGLE, (t) => {
221
+ this._onMobileLayoutToggleChange(!!t);
149
222
  }), this.api.onValueChanged(e.MOBILE_CARDS_IN_ROW, (t) => {
150
223
  this._onMobileCardsInRowChange(Number(t));
151
224
  });
152
225
  }
153
226
  /**
154
227
  * Subscribe to store changes to update visibility when layout changes
155
- * This is still needed because layout changes come from LayoutControl
228
+ * This is still needed because layout changes come from LayoutOrientationControl
156
229
  */
157
230
  _listenStateUpdates() {
158
231
  let t = this.store.recommendationConfigs.orientation;
@@ -161,8 +234,21 @@ class V extends R {
161
234
  s !== t && (t = s, this._updateProductsInRowVisibility());
162
235
  });
163
236
  }
237
+ /**
238
+ * Subscribe to editor preview mode changes (desktop ↔ mobile).
239
+ * When the user switches preview mode and mobile layout is enabled,
240
+ * we swap which "Products in One Row" counter is visible.
241
+ */
242
+ _listenEditorModeChanges() {
243
+ this.api.onEditorStatePropUpdated(
244
+ O.previewDeviceMode,
245
+ () => {
246
+ i.getConfig(this.currentNode).mobileLayoutEnabled && this._updateProductsInRowVisibility();
247
+ }
248
+ );
249
+ }
164
250
  }
165
251
  export {
166
- M as PRODUCT_LAYOUT_CONTROL_ID,
167
- V as ProductLayoutControl
252
+ S as PRODUCT_LAYOUT_CONTROL_ID,
253
+ v as ProductLayoutControl
168
254
  };
@@ -19,9 +19,9 @@ class S extends l {
19
19
  }
20
20
  getTemplate() {
21
21
  return `
22
- <div class="shuffle-control-container">
22
+ <div class="shuffle-control-container" style="border-bottom: 1px solid #e0e0e0;">
23
23
  ${this._GuTwoColumns([
24
- this._GuLabel({ text: "Shuffle Recommended Products" }),
24
+ this._GuLabel({ text: this.api.translate("Shuffle Recommended Products") }),
25
25
  this._GuToggle(i.SHUFFLE_PRODUCTS)
26
26
  ])}
27
27
  </div>