@useinsider/guido 3.20.1 → 3.21.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 (56) hide show
  1. package/dist/components/Guido.vue.js +1 -1
  2. package/dist/components/Guido.vue2.js +65 -64
  3. package/dist/components/organisms/AutoSaveController.vue2.js +10 -9
  4. package/dist/components/organisms/header/EditorActions.vue.js +4 -4
  5. package/dist/components/organisms/header/EditorActions.vue2.js +53 -51
  6. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +3 -3
  7. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +23 -22
  8. package/dist/composables/useAutoSave.js +46 -44
  9. package/dist/composables/useLegacyStrategyConversion.js +16 -16
  10. package/dist/composables/useSave.js +26 -23
  11. package/dist/composables/useSaveValidation.js +113 -114
  12. package/dist/composables/useSaveValidationRollout.js +17 -6
  13. package/dist/composables/useStripo.js +4 -3
  14. package/dist/extensions/Blocks/Items/block.js +43 -42
  15. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +296 -223
  16. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +221 -203
  17. package/dist/extensions/Blocks/Recommendation/utils/blockRefresh.js +19 -0
  18. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +51 -36
  19. package/dist/extensions/Blocks/Recommendation/utils/strategyCommit.js +31 -35
  20. package/dist/extensions/Blocks/Unsubscribe/block.js +70 -75
  21. package/dist/guido.css +1 -1
  22. package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +271 -192
  23. package/dist/package.json.js +1 -1
  24. package/dist/services/settingsApi.js +11 -0
  25. package/dist/src/components/Guido.vue.d.ts +1 -1
  26. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  27. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
  28. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
  29. package/dist/src/composables/useAutoSave.d.ts +4 -1
  30. package/dist/src/composables/useSaveValidationRollout.d.ts +1 -0
  31. package/dist/src/extensions/Blocks/Checkbox/controls/blockBackground/index.d.ts +1 -0
  32. package/dist/src/extensions/Blocks/CouponBlock/controls/index.d.ts +8 -0
  33. package/dist/src/extensions/Blocks/Items/controls/index.d.ts +40 -0
  34. package/dist/src/extensions/Blocks/RadioButton/controls/blockBackground/index.d.ts +1 -0
  35. package/dist/src/extensions/Blocks/Recommendation/controls/blockBackground/index.d.ts +1 -0
  36. package/dist/src/extensions/Blocks/Recommendation/controls/button/index.d.ts +11 -0
  37. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/index.d.ts +7 -0
  38. package/dist/src/extensions/Blocks/Recommendation/controls/image/index.d.ts +2 -0
  39. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +18 -3
  40. package/dist/src/extensions/Blocks/Recommendation/controls/name/index.d.ts +7 -0
  41. package/dist/src/extensions/Blocks/Recommendation/controls/oldPrice/index.d.ts +7 -0
  42. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscount/index.d.ts +7 -0
  43. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPrice/index.d.ts +7 -0
  44. package/dist/src/extensions/Blocks/Recommendation/controls/price/index.d.ts +7 -0
  45. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +7 -0
  46. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +9 -1
  47. package/dist/src/extensions/Blocks/Recommendation/utils/blockRefresh.d.ts +17 -0
  48. package/dist/src/extensions/Blocks/Recommendation/utils/nodeConfigWriter.d.ts +7 -0
  49. package/dist/src/extensions/Blocks/Unsubscribe/block.d.ts +1 -6
  50. package/dist/src/extensions/Blocks/_Boilerplate/controls/blockBackground/index.d.ts +1 -0
  51. package/dist/src/extensions/Blocks/controlFactories.d.ts +22 -0
  52. package/dist/src/services/settingsApi.d.ts +6 -0
  53. package/dist/src/stores/setup-settings.d.ts +4 -0
  54. package/dist/static/styles/components/button.css.js +5 -0
  55. package/dist/stores/setup-settings.js +10 -0
  56. package/package.json +4 -4
@@ -1,48 +1,56 @@
1
- var C = Object.defineProperty;
2
- var p = (g, u, t) => u in g ? C(g, u, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[u] = t;
3
- var i = (g, u, t) => p(g, typeof u != "symbol" ? u + "" : u, t);
4
- import { useConfig as h } from "../../../../../composables/useConfig.js";
5
- import { ModificationDescription as y } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
- import { CommonControl as N } from "../../../common-control.js";
7
- import { RRS_CONFIG_VERSION as _, DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
8
- import { RECOMMENDATION_ID_ATTR as I } from "../../constants/selectors.js";
9
- import { RecommendationConfigService as c } from "../../services/configService.js";
10
- import { useRecommendationExtensionStore as R } from "../../store/recommendation.js";
11
- import { registerNodeConfigWriter as b } from "../../utils/nodeConfigWriter.js";
12
- import { AlgorithmControl as P } from "./algorithm.js";
13
- import { ALGORITHM_CONTROL_ID as ut } from "./algorithm.js";
14
- import { CurrencyControl as T } from "./currency.js";
15
- import { CURRENCY_CONTROL_ID as gt } from "./currency.js";
16
- import { FiltersControl as S } from "./filters.js";
17
- import { FILTERS_CONTROL_ID as ft } from "./filters.js";
18
- import { LayoutOrientationControl as O } from "./layoutOrientation.js";
19
- import { LAYOUT_ORIENTATION_CONTROL_ID as Ct } from "./layoutOrientation.js";
20
- import { LocaleControl as k } from "./locale.js";
21
- import { LOCALE_CONTROL_ID as yt } from "./locale.js";
22
- import { PricePlacementControl as L } from "./pricePlacement.js";
23
- import { PRICE_PLACEMENT_CONTROL_ID as _t } from "./pricePlacement.js";
24
- import { ProductCountControl as E } from "./productCount.js";
25
- import { PRODUCT_COUNT_CONTROL_ID as Rt } from "./productCount.js";
26
- import { ProductLayoutControl as F } from "./productLayout.js";
27
- import { PRODUCT_LAYOUT_CONTROL_ID as Pt } from "./productLayout.js";
28
- import { ShuffleControl as D } from "./shuffle.js";
29
- import { SHUFFLE_CONTROL_ID as St } from "./shuffle.js";
30
- import { StrategyControl as B } from "./strategy.js";
31
- import { STRATEGY_CONTROL_ID as kt } from "./strategy.js";
32
- import { setCurrencyAttributes as w, getBlockElement as v, isPartnerManagedBlock as A, updateProductContentInPlace as x, regenerateProductRowsWithStyles as U } from "./utils.js";
33
- import { adjustProductsToSize as Et, formatProductPrice as Ft, getCardComposition as Dt, getCurrentLayout as Bt, getMainContainerTbody as wt, regenerateMobileProductRows as vt, updatePricesInPlace as At, updateSingleProductContent as xt } from "./utils.js";
34
- import { useDebounceFn as f } from "../../../../../node_modules/@vueuse/shared/index.js";
35
- const W = "ui-elements-recommendation-block";
36
- class at extends N {
1
+ var _ = Object.defineProperty;
2
+ var y = (h, c, e) => c in h ? _(h, c, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[c] = e;
3
+ var i = (h, c, e) => y(h, typeof c != "symbol" ? c + "" : c, e);
4
+ import { useConfig as g } from "../../../../../composables/useConfig.js";
5
+ import { ModificationDescription as N } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { CommonControl as P } from "../../../common-control.js";
7
+ import { RRS_CONFIG_VERSION as R, DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
8
+ import { RECOMMENDATION_ID_ATTR as k } from "../../constants/selectors.js";
9
+ import { RecommendationConfigService as l } from "../../services/configService.js";
10
+ import { useRecommendationExtensionStore as I } from "../../store/recommendation.js";
11
+ import { refreshBlockOutsidePanel as S } from "../../utils/blockRefresh.js";
12
+ import { registerNodeConfigWriter as T, writeBlockConfigOutsidePanel as m, hasEditedConfigChange as b } from "../../utils/nodeConfigWriter.js";
13
+ import { AlgorithmControl as O } from "./algorithm.js";
14
+ import { ALGORITHM_CONTROL_ID as me } from "./algorithm.js";
15
+ import { CurrencyControl as B } from "./currency.js";
16
+ import { CURRENCY_CONTROL_ID as pe } from "./currency.js";
17
+ import { FiltersControl as L } from "./filters.js";
18
+ import { FILTERS_CONTROL_ID as ye } from "./filters.js";
19
+ import { LayoutOrientationControl as E } from "./layoutOrientation.js";
20
+ import { LAYOUT_ORIENTATION_CONTROL_ID as Pe } from "./layoutOrientation.js";
21
+ import { LocaleControl as F } from "./locale.js";
22
+ import { LOCALE_CONTROL_ID as ke } from "./locale.js";
23
+ import { PricePlacementControl as D } from "./pricePlacement.js";
24
+ import { PRICE_PLACEMENT_CONTROL_ID as Se } from "./pricePlacement.js";
25
+ import { ProductCountControl as U } from "./productCount.js";
26
+ import { PRODUCT_COUNT_CONTROL_ID as be } from "./productCount.js";
27
+ import { ProductLayoutControl as v } from "./productLayout.js";
28
+ import { PRODUCT_LAYOUT_CONTROL_ID as Be } from "./productLayout.js";
29
+ import { ShuffleControl as w } from "./shuffle.js";
30
+ import { SHUFFLE_CONTROL_ID as Ee } from "./shuffle.js";
31
+ import { StrategyControl as A } from "./strategy.js";
32
+ import { STRATEGY_CONTROL_ID as De } from "./strategy.js";
33
+ import { setCurrencyAttributes as V, getBlockElement as x, isPartnerManagedBlock as W, updateProductContentInPlace as M, regenerateProductRowsWithStyles as G } from "./utils.js";
34
+ import { adjustProductsToSize as ve, formatProductPrice as we, getCardComposition as Ae, getCurrentLayout as Ve, getMainContainerTbody as xe, regenerateMobileProductRows as We, updatePricesInPlace as Me, updateSingleProductContent as Ge } from "./utils.js";
35
+ import { useDebounceFn as C } from "../../../../../node_modules/@vueuse/shared/index.js";
36
+ const f = "Update recommendation filters", z = "ui-elements-recommendation-block";
37
+ class he extends P {
37
38
  constructor() {
38
39
  super(...arguments);
39
- i(this, "store", R());
40
+ i(this, "store", I());
40
41
  i(this, "storeUnsubscription", () => {
41
42
  });
42
43
  // A config change waiting to reach the node config. Deferred so it can ride the
43
44
  // re-render's single apply() — one user action must cost one undo press.
44
45
  i(this, "pendingNodeConfigWrite", null);
45
46
  i(this, "pendingConfigFlushTimer", null);
47
+ // What the panel still owes the block it watches — finished outside the panel on teardown
48
+ // and block switch. A new generation tells debounced work queued for the old one to stand down.
49
+ i(this, "trackedBlockId", null);
50
+ i(this, "fetchedConfigVersion", 0);
51
+ i(this, "hasUnrenderedProducts", !1);
52
+ i(this, "panelGeneration", 0);
53
+ i(this, "isPanelAlive", !0);
46
54
  // Sub-control instances for lifecycle management
47
55
  i(this, "algorithmControl", null);
48
56
  i(this, "localeControl", null);
@@ -58,11 +66,14 @@ class at extends N {
58
66
  /**
59
67
  * Debounced product fetch to prevent rapid API calls during config changes
60
68
  */
61
- i(this, "_debouncedFetchProducts", f(async () => {
69
+ i(this, "_debouncedFetchProducts", C(async (e, t) => {
70
+ if (t !== this.panelGeneration)
71
+ return;
72
+ const o = this._getBlockConfigVersion(e);
62
73
  try {
63
- await this.store.fetchRecommendationProducts();
74
+ await (e === null ? this.store.fetchRecommendationProducts() : this.store.fetchRecommendationProductsForBlock(e)), t === this.panelGeneration && (this.fetchedConfigVersion = Math.max(this.fetchedConfigVersion, o));
64
75
  } finally {
65
- this._scheduleConfigFlush();
76
+ t === this.panelGeneration && this._scheduleConfigFlush();
66
77
  }
67
78
  }, 500));
68
79
  /**
@@ -76,12 +87,12 @@ class at extends N {
76
87
  * count. The store pads products to the configured size, so in-place only
77
88
  * fails when the size actually changed.
78
89
  */
79
- i(this, "_debouncedRegenerateWithProducts", f(() => {
80
- if (!this.currentNode || !this.api)
90
+ i(this, "_debouncedRegenerateWithProducts", C((e) => {
91
+ if (e !== this.panelGeneration || !this.currentNode || !this.api)
81
92
  return;
82
- const t = this._getRecommendationIdFromNode(this.currentNode), e = this.store.currentRecommendationId;
83
- if (t === null || e === null || t === e || !this.store.blockStates[t]) {
84
- this._regenerateWithProducts();
93
+ const t = this._getRecommendationIdFromNode(this.currentNode), o = this.store.currentRecommendationId;
94
+ if (t === null || o === null || t === o || !this.store.blockStates[t]) {
95
+ this.hasUnrenderedProducts = !1, this._regenerateWithProducts();
85
96
  return;
86
97
  }
87
98
  if (this.store.blockStates[t].recommendationProducts.length) {
@@ -89,13 +100,13 @@ class at extends N {
89
100
  try {
90
101
  this._regenerateWithProducts();
91
102
  } finally {
92
- this.store.setCurrentBlock(e);
103
+ this.store.setCurrentBlock(o);
93
104
  }
94
105
  }
95
106
  }, 100));
96
107
  }
97
108
  getId() {
98
- return W;
109
+ return z;
99
110
  }
100
111
  get _subControls() {
101
112
  return [
@@ -112,15 +123,15 @@ class at extends N {
112
123
  ];
113
124
  }
114
125
  getTemplate() {
115
- var t;
116
- return this.rrsEnabled = h().isFeatureEnabled("reusableRecommendationStrategy"), this._subControls.forEach((e) => {
126
+ var e;
127
+ return this.rrsEnabled = g().isFeatureEnabled("reusableRecommendationStrategy"), this._subControls.forEach((t) => {
117
128
  var o;
118
- return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
119
- }), this.algorithmControl = new P(), this.localeControl = new k(), this.currencyControl = new T(), this.productCountControl = new E(), this.productLayoutControl = new F(), this.filtersControl = new S(), this.shuffleControl = new D(), this.pricePlacementControl = new L(), this.layoutOrientationControl = new O(), this.strategyControl = this.rrsEnabled ? new B() : null, this._subControls.forEach((e) => {
120
- e && (e.api = this.api);
129
+ return (o = t == null ? void 0 : t.onDestroy) == null ? void 0 : o.call(t);
130
+ }), this.algorithmControl = new O(), this.localeControl = new F(), this.currencyControl = new B(), this.productCountControl = new U(), this.productLayoutControl = new v(), this.filtersControl = new L(), this.shuffleControl = new w(), this.pricePlacementControl = new D(), this.layoutOrientationControl = new E(), this.strategyControl = this.rrsEnabled ? new A() : null, this._subControls.forEach((t) => {
131
+ t && (t.api = this.api);
121
132
  }), `
122
133
  <div class="recommendation-controls-container">
123
- ${((t = this.strategyControl) == null ? void 0 : t.getTemplate()) ?? ""}
134
+ ${((e = this.strategyControl) == null ? void 0 : e.getTemplate()) ?? ""}
124
135
  ${this.layoutOrientationControl.getTemplate()}
125
136
  ${this.productCountControl.getTemplate()}
126
137
  ${this.productLayoutControl.getTemplate()}
@@ -133,39 +144,39 @@ class at extends N {
133
144
  </div>
134
145
  `;
135
146
  }
136
- _showControlsForBlock(t) {
137
- var n;
138
- const e = (n = this.getContainer()) == null ? void 0 : n.querySelector(".recommendation-controls-container");
139
- if (!e)
147
+ _showControlsForBlock(e) {
148
+ var s;
149
+ const t = (s = this.getContainer()) == null ? void 0 : s.querySelector(".recommendation-controls-container");
150
+ if (!t)
140
151
  return;
141
- const o = this._getRecommendationIdFromNode(t) === null ? null : c.getConfig(t), r = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _), s = this.rrsEnabled && !!o && !r;
142
- e.classList.toggle("strategy-block", r), e.classList.toggle("legacy-strategy-block", s);
152
+ const o = this._getRecommendationIdFromNode(e) === null ? null : l.getConfig(e), n = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= R), r = this.rrsEnabled && !!o && !n;
153
+ t.classList.toggle("strategy-block", n), t.classList.toggle("legacy-strategy-block", r);
143
154
  }
144
155
  async onRender() {
145
156
  var o;
146
- b(this.api);
147
- const t = this._getRecommendationIdFromNode(this.currentNode), e = t ?? this.store.currentRecommendationId;
148
- if (e !== null && this.store.setCurrentBlock(e), t !== null && this._syncNodeConfigIfChanged(t), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), t !== null && this.strategyControl && (this.strategyControl.api = this.api, this.strategyControl.currentNode = this.currentNode, this.strategyControl.onRender()), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
157
+ T(this.api);
158
+ const e = this._getRecommendationIdFromNode(this.currentNode), t = e ?? this.store.currentRecommendationId;
159
+ if (t !== null && this.store.setCurrentBlock(t), e !== null && this._syncNodeConfigIfChanged(e), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), e !== null && this.strategyControl && (this.strategyControl.api = this.api, this.strategyControl.currentNode = this.currentNode, this.strategyControl.onRender()), t !== null && ((o = this.store.blockStates[t]) != null && o.isInitialized)) {
149
160
  this._initializeSubControls();
150
161
  return;
151
162
  }
152
- await this._fetchBlockData(e), this._initializeSubControls();
163
+ await this._fetchBlockData(t), this._initializeSubControls();
153
164
  }
154
- onTemplateNodeUpdated(t) {
165
+ onTemplateNodeUpdated(e) {
155
166
  var o;
156
- super.onTemplateNodeUpdated(t);
157
- const e = this._getRecommendationIdFromNode(t);
158
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && this._syncNodeConfigIfChanged(e), this._showControlsForBlock(t), e !== null && !((o = this.store.blockStates[e]) != null && o.isInitialized) && this._fetchBlockData(e).then(() => {
167
+ super.onTemplateNodeUpdated(e);
168
+ const t = this._getRecommendationIdFromNode(e);
169
+ t !== null && t !== this.store.currentRecommendationId && this.store.setCurrentBlock(t), t !== null && this._syncNodeConfigIfChanged(t), this._showControlsForBlock(e), t !== null && !((o = this.store.blockStates[t]) != null && o.isInitialized) && this._fetchBlockData(t).then(() => {
159
170
  this._initializeSubControls();
160
- }), this._subControls.forEach((r) => {
161
- var s;
162
- r != null && r.api && (r.currentNode = t, (s = r.onTemplateNodeUpdated) == null || s.call(r, t));
171
+ }), this._subControls.forEach((n) => {
172
+ var r;
173
+ n != null && n.api && (n.currentNode = e, (r = n.onTemplateNodeUpdated) == null || r.call(n, e));
163
174
  });
164
175
  }
165
176
  onDestroy() {
166
- this.storeUnsubscription(), this._flushPendingNodeConfig(), this._subControls.forEach((t) => {
167
- var e;
168
- return (e = t == null ? void 0 : t.onDestroy) == null ? void 0 : e.call(t);
177
+ this.panelGeneration++, this.isPanelAlive = !1, this.storeUnsubscription(), this._finishBlockOutsidePanel(), this.hasUnrenderedProducts = !1, this._subControls.forEach((e) => {
178
+ var t;
179
+ return (t = e == null ? void 0 : e.onDestroy) == null ? void 0 : t.call(e);
169
180
  });
170
181
  }
171
182
  /**
@@ -173,9 +184,9 @@ class at extends N {
173
184
  * Each sub-control manages its own form values and event listeners
174
185
  */
175
186
  _initializeSubControls() {
176
- this._subControls.forEach((t) => {
177
- var e;
178
- t && (t.api = this.api, t.currentNode = this.currentNode, (e = t.onRender) == null || e.call(t));
187
+ this._subControls.forEach((e) => {
188
+ var t;
189
+ e && (e.api = this.api, e.currentNode = this.currentNode, (t = e.onRender) == null || t.call(e));
179
190
  });
180
191
  }
181
192
  /**
@@ -202,35 +213,36 @@ class at extends N {
202
213
  * that state and made a re-dropped saved module keep the defaults its wiped
203
214
  * state was rebuilt with (SD-150347).
204
215
  */
205
- _syncNodeConfigIfChanged(t) {
206
- this.pendingNodeConfigWrite && this.pendingNodeConfigWrite.blockId !== t && this._flushPendingNodeConfig();
207
- const e = this.store.blockStates[t];
208
- if (!e)
216
+ _syncNodeConfigIfChanged(e) {
217
+ var r;
218
+ this.pendingNodeConfigWrite && this.pendingNodeConfigWrite.blockId !== e && this._flushPendingNodeConfig();
219
+ const t = this.store.blockStates[e];
220
+ if (!t)
209
221
  return;
210
- const o = JSON.stringify(c.getConfig(this.currentNode)), r = e.syncedConfigFingerprint;
211
- e.syncedConfigFingerprint = o, r !== o && (this.pendingNodeConfigWrite || this._syncNodeConfigToStore());
222
+ const o = JSON.stringify(l.getConfig(this.currentNode)), n = t.syncedConfigFingerprint;
223
+ t.syncedConfigFingerprint = o, n !== o && ((r = this.pendingNodeConfigWrite) == null ? void 0 : r.blockId) !== e && this._syncNodeConfigToStore();
212
224
  }
213
225
  /** Whether the deferred write would move the node's strategyId — see the caller (SD-149916). */
214
226
  _isPendingStrategyIdChange() {
215
- const t = this.pendingNodeConfigWrite;
216
- return t ? c.getConfig(this.currentNode).strategyId !== t.updates.strategyId : !1;
227
+ const e = this.pendingNodeConfigWrite;
228
+ return e ? l.getConfig(this.currentNode).strategyId !== e.updates.strategyId : !1;
217
229
  }
218
230
  _capturePendingNodeConfigWrite() {
219
- const t = this.store.currentRecommendationId;
220
- if (!this.currentNode || !this.api || t === null)
221
- return;
222
- const { filters: e, strategy: o, strategyId: r, size: s, nodeConfigVersion: n } = this.store.recommendationConfigs, { partnerName: l } = h();
223
- this.pendingNodeConfigWrite = {
224
- blockId: t,
225
- node: this.currentNode,
226
- updates: {
227
- filters: e,
228
- strategy: o,
229
- strategyId: r,
230
- size: s,
231
- configVersion: n,
232
- ...l.value ? { partnerName: l.value } : {}
233
- }
231
+ const e = this.store.currentRecommendationId, t = e === null ? void 0 : this.store.blockStates[e];
232
+ !this.currentNode || !this.api || e === null || !t || (this.pendingNodeConfigWrite = {
233
+ blockId: e,
234
+ updates: this._getNodeConfigUpdates(t.recommendationConfigs)
235
+ });
236
+ }
237
+ _getNodeConfigUpdates(e) {
238
+ const { filters: t, strategy: o, strategyId: n, size: r, nodeConfigVersion: s } = e, { partnerName: d } = g();
239
+ return {
240
+ filters: t,
241
+ strategy: o,
242
+ strategyId: n,
243
+ size: r,
244
+ configVersion: s,
245
+ ...d.value ? { partnerName: d.value } : {}
234
246
  };
235
247
  }
236
248
  /**
@@ -243,9 +255,9 @@ class at extends N {
243
255
  * @param documentModifier - The batch to accumulate into
244
256
  * @returns Whether anything was staged
245
257
  */
246
- _stagePendingNodeConfig(t) {
247
- const e = this.pendingNodeConfigWrite;
248
- return !e || !this.currentNode ? !1 : (c.stageConfig(t, this.currentNode, e.updates), !0);
258
+ _stagePendingNodeConfig(e) {
259
+ const t = this.pendingNodeConfigWrite;
260
+ return !t || !this.currentNode ? !1 : (l.stageConfig(e, this.currentNode, t.updates), !0);
249
261
  }
250
262
  /**
251
263
  * Arms the guaranteed fallback write, once the product fetch has settled. The
@@ -262,45 +274,54 @@ class at extends N {
262
274
  }
263
275
  /**
264
276
  * Writes the deferred change standalone when the batch it was staged into never
265
- * committed. Always targets the LIVE node — the captured one is an immutable
266
- * snapshot the intervening re-render staled, and saveConfig swallows that failure.
277
+ * committed. Always targets a LIVE node — a captured one is an immutable snapshot
278
+ * the intervening re-render staled, and saveConfig swallows that failure.
267
279
  */
268
280
  _flushPendingNodeConfig() {
269
- const t = this.pendingNodeConfigWrite;
270
- if (this.pendingNodeConfigWrite = null, this._clearConfigFlush(), !t || !this.api)
271
- return;
272
- const e = t.blockId === this.store.currentRecommendationId && this.currentNode ? this.currentNode : t.node, o = c.getConfig(e);
273
- JSON.stringify(o.filters) === JSON.stringify(t.updates.filters) && o.strategy === t.updates.strategy && o.strategyId === t.updates.strategyId && o.size === t.updates.size || c.updateConfig(this.api, e, t.updates, "Update recommendation filters");
281
+ const e = this.pendingNodeConfigWrite;
282
+ if (this.pendingNodeConfigWrite = null, this._clearConfigFlush(), !(!e || !this.api)) {
283
+ if (e.blockId !== this.store.currentRecommendationId || !this.currentNode) {
284
+ const { blockId: t, updates: o } = e;
285
+ !m(t, o, f) && this.store.blockStates[t] && console.error("[recommendation] pending config write did not reach a node", { blockId: t });
286
+ return;
287
+ }
288
+ b(this.currentNode, e.updates) && l.updateConfig(
289
+ this.api,
290
+ this.currentNode,
291
+ e.updates,
292
+ f
293
+ );
294
+ }
274
295
  }
275
296
  _syncNodeConfigToStore() {
276
- var r;
277
- const t = c.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
297
+ var n;
298
+ const e = l.getConfig(this.currentNode), t = this.store.currentRecommendationId, o = t !== null && ((n = this.store.blockStates[t]) == null ? void 0 : n.isInitialized);
278
299
  this.store.patchCurrentBlockConfig({
279
- strategy: t.strategy,
280
- strategyId: t.strategyId,
281
- nodeConfigVersion: t.configVersion,
282
- language: t.language,
283
- size: t.size,
284
- productIds: t.productIds,
285
- cardsInRow: t.cardsInRow,
286
- mobileCardsInRow: t.mobileCardsInRow,
287
- mobileLayoutEnabled: t.mobileLayoutEnabled,
288
- orientation: t.layout,
300
+ strategy: e.strategy,
301
+ strategyId: e.strategyId,
302
+ nodeConfigVersion: e.configVersion,
303
+ language: e.language,
304
+ size: e.size,
305
+ productIds: e.productIds,
306
+ cardsInRow: e.cardsInRow,
307
+ mobileCardsInRow: e.mobileCardsInRow,
308
+ mobileLayoutEnabled: e.mobileLayoutEnabled,
309
+ orientation: e.layout,
289
310
  // Only sync filters from node config during initial load.
290
311
  // After initialization, the Pinia store is the source of truth
291
312
  // for filters (edited via the filter drawer).
292
- ...o ? {} : { filters: t.filters },
293
- shuffleProducts: t.shuffleProducts,
294
- priceMovedToNextLine: t.priceMovedToNextLine,
295
- priceHideIfSameAsDiscounted: t.priceHideIfSameAsDiscounted,
313
+ ...o ? {} : { filters: e.filters },
314
+ shuffleProducts: e.shuffleProducts,
315
+ priceMovedToNextLine: e.priceMovedToNextLine,
316
+ priceHideIfSameAsDiscounted: e.priceHideIfSameAsDiscounted,
296
317
  currencySettings: {
297
- name: t.currency.code,
298
- value: t.currency.code,
299
- symbol: t.currency.symbol,
300
- alignment: t.currency.alignment === "before" ? "0" : "1",
301
- decimalCount: t.currency.decimalCount.toString(),
302
- decimalSeparator: t.currency.decimalSeparator,
303
- thousandSeparator: t.currency.thousandSeparator
318
+ name: e.currency.code,
319
+ value: e.currency.code,
320
+ symbol: e.currency.symbol,
321
+ alignment: e.currency.alignment === "before" ? "0" : "1",
322
+ decimalCount: e.currency.decimalCount.toString(),
323
+ decimalSeparator: e.currency.decimalSeparator,
324
+ thousandSeparator: e.currency.thousandSeparator
304
325
  }
305
326
  }, { triggerRefetch: !1 });
306
327
  }
@@ -313,18 +334,27 @@ class at extends N {
313
334
  * Shared by onRender() and onTemplateNodeUpdated() to avoid duplication.
314
335
  * Marks the block as initialized to prevent redundant fetches on re-selection.
315
336
  */
316
- async _fetchBlockData(t) {
317
- t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
337
+ async _fetchBlockData(e) {
338
+ if (e !== null && this.store.markBlockInitialized(e), (await Promise.allSettled([
318
339
  this.store.fetchRecommendationCreateData(),
319
340
  this.store.fetchRecommendationFilters()
320
- ])).forEach((o, r) => {
321
- o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, o.reason);
322
- }), this._applySmartDefaults();
341
+ ])).forEach((n, r) => {
342
+ n.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, n.reason);
343
+ }), e !== null && this.store.currentRecommendationId !== e) {
344
+ this._refreshOutsidePanel(e, !0);
345
+ return;
346
+ }
347
+ this._applySmartDefaults();
348
+ const o = this._getBlockConfigVersion(e);
323
349
  try {
324
350
  await this.store.fetchRecommendationProducts();
325
- } catch (o) {
326
- console.warn("Recommendation block: fetchRecommendationProducts failed", o);
351
+ } catch (n) {
352
+ console.warn("Recommendation block: fetchRecommendationProducts failed", n);
327
353
  }
354
+ e !== null && !this._isPanelOn(e) && this._refreshOutsidePanel(e, this._getBlockConfigVersion(e) !== o);
355
+ }
356
+ _isPanelOn(e) {
357
+ return this.isPanelAlive && this.store.currentRecommendationId === e;
328
358
  }
329
359
  /**
330
360
  * Applies smart defaults for newly dropped blocks.
@@ -339,39 +369,39 @@ class at extends N {
339
369
  _applySmartDefaults() {
340
370
  if (!this.currentNode || !this.api)
341
371
  return;
342
- const t = c.getConfig(this.currentNode), e = {};
343
- let o = null, r = null, s = null;
344
- if (t.currency.code === a.currency.code) {
345
- const { currencyList: n } = this.store;
346
- n.length > 0 && (n.some(
347
- (d) => d.value === `price.${a.currency.code}`
348
- ) || (o = n[0].value.replace("price.", ""), e.currency = {
372
+ const e = l.getConfig(this.currentNode), t = {};
373
+ let o = null, n = null, r = null;
374
+ if (e.currency.code === a.currency.code) {
375
+ const { currencyList: s } = this.store;
376
+ s.length > 0 && (s.some(
377
+ (u) => u.value === `price.${a.currency.code}`
378
+ ) || (o = s[0].value.replace("price.", ""), t.currency = {
349
379
  ...a.currency,
350
380
  code: o,
351
381
  symbol: o
352
382
  }));
353
383
  }
354
- if (t.strategy === a.strategy) {
355
- const n = this.store.getActivePredictiveAlgorithms;
356
- n.length > 0 && (n.some(
357
- (d) => d.value === a.strategy
358
- ) || (r = n[0].value, e.strategy = r));
384
+ if (e.strategy === a.strategy) {
385
+ const s = this.store.getActivePredictiveAlgorithms;
386
+ s.length > 0 && (s.some(
387
+ (u) => u.value === a.strategy
388
+ ) || (n = s[0].value, t.strategy = n));
359
389
  }
360
- if (t.language === a.language) {
361
- const n = this.store.getLanguages;
362
- n.length > 0 && (n.some(
363
- (d) => d.value === a.language
364
- ) || (s = n[0].value, e.language = s));
390
+ if (e.language === a.language) {
391
+ const s = this.store.getLanguages;
392
+ s.length > 0 && (s.some(
393
+ (u) => u.value === a.language
394
+ ) || (r = s[0].value, t.language = r));
365
395
  }
366
- !o && !r && !s || (c.updateConfig(
396
+ !o && !n && !r || (l.updateConfig(
367
397
  this.api,
368
398
  this.currentNode,
369
- e,
399
+ t,
370
400
  "Applied smart defaults"
371
- ), o && e.currency && w({
401
+ ), o && t.currency && V({
372
402
  currentNode: this.currentNode,
373
403
  documentModifier: this.api.getDocumentModifier(),
374
- currency: e.currency
404
+ currency: t.currency
375
405
  }), this.store.patchCurrentBlockConfig({
376
406
  ...o ? {
377
407
  currencySettings: {
@@ -384,46 +414,88 @@ class at extends N {
384
414
  thousandSeparator: a.currency.thousandSeparator
385
415
  }
386
416
  } : {},
387
- ...r ? { strategy: r } : {},
388
- ...s ? { language: s } : {}
417
+ ...n ? { strategy: n } : {},
418
+ ...r ? { language: r } : {}
389
419
  }, { triggerRefetch: !1 }));
390
420
  }
391
421
  /**
392
422
  * Reads the recommendation-id attribute from the block element within the node
393
423
  */
394
- _getRecommendationIdFromNode(t) {
395
- const e = v(t);
396
- if (!e || !("getAttribute" in e))
424
+ _getRecommendationIdFromNode(e) {
425
+ const t = x(e);
426
+ if (!t || !("getAttribute" in t))
397
427
  return null;
398
- const o = e.getAttribute(I);
428
+ const o = t.getAttribute(k);
399
429
  if (!o)
400
430
  return null;
401
- const r = parseInt(o);
402
- return Number.isNaN(r) ? null : r;
431
+ const n = parseInt(o);
432
+ return Number.isNaN(n) ? null : n;
433
+ }
434
+ /**
435
+ * Lands what the panel still owes its block before it stops watching it: the config now, the
436
+ * products through a block-scoped refresh. Never throws — it runs from Stripo's teardown.
437
+ */
438
+ _finishBlockOutsidePanel({ blockId: e, fetchedConfigVersion: t, hasUnrenderedProducts: o } = this._trackedBlockSnapshot()) {
439
+ try {
440
+ this._flushPendingNodeConfig();
441
+ const n = e === null ? void 0 : this.store.blockStates[e];
442
+ if (e === null || !n)
443
+ return;
444
+ const r = t !== n.configVersion;
445
+ r && !m(
446
+ e,
447
+ this._getNodeConfigUpdates(n.recommendationConfigs),
448
+ f
449
+ ) && console.error("[recommendation] config write outside the panel did not reach a node", { blockId: e }), (r || o) && this._refreshOutsidePanel(e, r);
450
+ } catch (n) {
451
+ console.error("[recommendation] finishing the block outside the panel failed", n);
452
+ }
453
+ }
454
+ _trackedBlockSnapshot() {
455
+ return {
456
+ blockId: this.trackedBlockId,
457
+ fetchedConfigVersion: this.fetchedConfigVersion,
458
+ hasUnrenderedProducts: this.hasUnrenderedProducts
459
+ };
460
+ }
461
+ _refreshOutsidePanel(e, t) {
462
+ S(e, {
463
+ pickUpdates: (o) => this._getNodeConfigUpdates(o),
464
+ description: "Update recommendation configuration",
465
+ fetch: t
466
+ }).then((o) => {
467
+ !o && this.store.blockStates[e] && console.error("[recommendation] refresh outside the panel did not reach a node", { blockId: e });
468
+ }).catch((o) => {
469
+ console.error("[recommendation] refresh outside the panel failed", o);
470
+ });
471
+ }
472
+ _getBlockConfigVersion(e) {
473
+ var t;
474
+ return e === null ? 0 : ((t = this.store.blockStates[e]) == null ? void 0 : t.configVersion) ?? 0;
403
475
  }
404
476
  /** Draws the current block's products into this panel's node. */
405
477
  _regenerateWithProducts() {
406
478
  if (!this.currentNode || !this.api)
407
479
  return;
408
- const t = this.store.recommendationProducts, e = this.api.getDocumentModifier(), o = this._stagePendingNodeConfig(e);
409
- if (A(this.currentNode)) {
410
- o && (e.apply(new y("Update recommendation configuration")), this.pendingNodeConfigWrite = null, this._clearConfigFlush());
480
+ const e = this.store.recommendationProducts, t = this.api.getDocumentModifier(), o = this._stagePendingNodeConfig(t);
481
+ if (W(this.currentNode)) {
482
+ o && (t.apply(new N("Update recommendation configuration")), this.pendingNodeConfigWrite = null, this._clearConfigFlush());
411
483
  return;
412
484
  }
413
- x({
485
+ M({
414
486
  currentNode: this.currentNode,
415
- documentModifier: e,
416
- products: t
417
- }) || U({
487
+ documentModifier: t,
488
+ products: e
489
+ }) || G({
418
490
  currentNode: this.currentNode,
419
- documentModifier: e,
420
- products: t
491
+ documentModifier: t,
492
+ products: e
421
493
  });
422
494
  }
423
495
  /**
424
496
  * Listen to store changes that require product refresh or regeneration.
425
497
  *
426
- * Uses configVersion counter (incremented only by patchCurrentBlockConfig)
498
+ * Uses the block's configVersion counter (bumped by patchCurrentBlockConfig and legacy conversion)
427
499
  * to distinguish user-initiated config changes from internal mutations
428
500
  * (e.g., fetchRecommendationCreateData setting preferred currency).
429
501
  *
@@ -434,55 +506,56 @@ class at extends N {
434
506
  * We skip that tick and update tracking references instead.
435
507
  */
436
508
  _listenStateUpdates() {
437
- const { store: t } = this;
438
- let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
439
- this.storeUnsubscription = t.$subscribe(() => {
440
- const s = t.currentRecommendationId;
441
- if (s !== r) {
442
- r = s, e = t.recommendationProducts, o = t.$state.configVersion;
509
+ const { store: e } = this;
510
+ let t = e.recommendationProducts, o = this._getBlockConfigVersion(e.currentRecommendationId);
511
+ this.panelGeneration++, this.isPanelAlive = !0, this.trackedBlockId = e.currentRecommendationId, this.fetchedConfigVersion = o, this.hasUnrenderedProducts = !1, this.storeUnsubscription = e.$subscribe(() => {
512
+ const n = e.currentRecommendationId;
513
+ if (n !== this.trackedBlockId) {
514
+ const p = this._trackedBlockSnapshot();
515
+ this.panelGeneration++, this.trackedBlockId = n, t = e.recommendationProducts, o = this._getBlockConfigVersion(n), this.fetchedConfigVersion = o, this.hasUnrenderedProducts = !1, this._finishBlockOutsidePanel(p);
443
516
  return;
444
517
  }
445
- const n = t.$state.configVersion;
446
- n !== o && (o = n, this._capturePendingNodeConfigWrite(), this._isPendingStrategyIdChange() && this._flushPendingNodeConfig(), this._debouncedFetchProducts());
447
- const l = t.recommendationProducts, d = l !== e, m = Array.isArray(l) && l.length > 0;
448
- d && m && (e = l, this._debouncedRegenerateWithProducts());
518
+ const r = this._getBlockConfigVersion(n);
519
+ r !== o && (o = r, this._capturePendingNodeConfigWrite(), this._isPendingStrategyIdChange() && this._flushPendingNodeConfig(), this._debouncedFetchProducts(n, this.panelGeneration));
520
+ const s = e.recommendationProducts, d = s !== t, u = Array.isArray(s) && s.length > 0;
521
+ d && u && (t = s, this.hasUnrenderedProducts = !0, this._debouncedRegenerateWithProducts(this.panelGeneration));
449
522
  });
450
523
  }
451
524
  }
452
525
  export {
453
- ut as ALGORITHM_CONTROL_ID,
454
- P as AlgorithmControl,
455
- W as CONTROL_BLOCK_ID,
456
- gt as CURRENCY_CONTROL_ID,
457
- T as CurrencyControl,
458
- ft as FILTERS_CONTROL_ID,
459
- S as FiltersControl,
460
- Ct as LAYOUT_ORIENTATION_CONTROL_ID,
461
- yt as LOCALE_CONTROL_ID,
462
- O as LayoutOrientationControl,
463
- k as LocaleControl,
464
- _t as PRICE_PLACEMENT_CONTROL_ID,
465
- Rt as PRODUCT_COUNT_CONTROL_ID,
466
- Pt as PRODUCT_LAYOUT_CONTROL_ID,
467
- L as PricePlacementControl,
468
- E as ProductCountControl,
469
- F as ProductLayoutControl,
470
- at as RecommendationBlockControl,
471
- St as SHUFFLE_CONTROL_ID,
472
- kt as STRATEGY_CONTROL_ID,
473
- D as ShuffleControl,
474
- B as StrategyControl,
475
- Et as adjustProductsToSize,
476
- Ft as formatProductPrice,
477
- v as getBlockElement,
478
- Dt as getCardComposition,
479
- Bt as getCurrentLayout,
480
- wt as getMainContainerTbody,
481
- A as isPartnerManagedBlock,
482
- vt as regenerateMobileProductRows,
483
- U as regenerateProductRowsWithStyles,
484
- w as setCurrencyAttributes,
485
- At as updatePricesInPlace,
486
- x as updateProductContentInPlace,
487
- xt as updateSingleProductContent
526
+ me as ALGORITHM_CONTROL_ID,
527
+ O as AlgorithmControl,
528
+ z as CONTROL_BLOCK_ID,
529
+ pe as CURRENCY_CONTROL_ID,
530
+ B as CurrencyControl,
531
+ ye as FILTERS_CONTROL_ID,
532
+ L as FiltersControl,
533
+ Pe as LAYOUT_ORIENTATION_CONTROL_ID,
534
+ ke as LOCALE_CONTROL_ID,
535
+ E as LayoutOrientationControl,
536
+ F as LocaleControl,
537
+ Se as PRICE_PLACEMENT_CONTROL_ID,
538
+ be as PRODUCT_COUNT_CONTROL_ID,
539
+ Be as PRODUCT_LAYOUT_CONTROL_ID,
540
+ D as PricePlacementControl,
541
+ U as ProductCountControl,
542
+ v as ProductLayoutControl,
543
+ he as RecommendationBlockControl,
544
+ Ee as SHUFFLE_CONTROL_ID,
545
+ De as STRATEGY_CONTROL_ID,
546
+ w as ShuffleControl,
547
+ A as StrategyControl,
548
+ ve as adjustProductsToSize,
549
+ we as formatProductPrice,
550
+ x as getBlockElement,
551
+ Ae as getCardComposition,
552
+ Ve as getCurrentLayout,
553
+ xe as getMainContainerTbody,
554
+ W as isPartnerManagedBlock,
555
+ We as regenerateMobileProductRows,
556
+ G as regenerateProductRowsWithStyles,
557
+ V as setCurrencyAttributes,
558
+ Me as updatePricesInPlace,
559
+ M as updateProductContentInPlace,
560
+ Ge as updateSingleProductContent
488
561
  };