@useinsider/guido 3.21.1-beta.7e3b468 → 3.21.1

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.
@@ -88,7 +88,7 @@ const d = [
88
88
  /<!--\[if\s\(mso 16\)\]>\s*<style\s*\S*>\s*a\s*\{\s*text-decoration\s*:\s*none\s*;*\s*\}\s*<\/style>\s*<!\[endif\]-->/g,
89
89
  /<!--\[if\sgte mso 9\]>\s*<style\s*\S*>\s*sup\s*\{\s*font-size\s*:\s*100%\s*!important\s*;*\s*\}\s*<\/style>\s*<!\[endif\]-->/g,
90
90
  /<!--\[if\sgte mso 9\]>\s*<noscript>\s*<xml>\s*<o:OfficeDocumentSettings>\s*<o:AllowPNG>\s*<\/o:AllowPNG>\s*<o:PixelsPerInch>\d+<\/o:PixelsPerInch>\s*<\/o:OfficeDocumentSettings>\s*<\/xml>\s*<\/noscript>\s*<!\[endif\]-->/g,
91
- /<!--\[if mso\]>\s*<xml>\s*<w:WordDocument\s+xmlns:w="urn:schemas-microsoft-com:office:word">\s*<w:DontUseAdvancedTypographyReadingMail><\/w:DontUseAdvancedTypographyReadingMail>\s*<\/w:WordDocument>\s*<\/xml>\s*<!\[endif\]-->/g,
91
+ /<!--\[if mso\]>\s*<xml>\s*<w:WordDocument\s+xmlns:w="urn:schemas-microsoft-com:office:word">\s*<w:DontUseAdvancedTypographyReadingMail\s*(?:\/>|><\/w:DontUseAdvancedTypographyReadingMail>)\s*<\/w:WordDocument>\s*<\/xml>\s*<!\[endif\]-->/g,
92
92
  /<\!--\[if \!mso\]><\!--\s*\d*\s*-->\s*<\!--\[if \!mso\]><\!--\s*\d*\s*-->\s*<\!--<!\[endif\]-->/g,
93
93
  /<!--\[if\s\!mso\]>\s*<!-- \d -->/g
94
94
  ],
@@ -1,43 +1,46 @@
1
- var B = Object.defineProperty;
2
- var b = (a, l, t) => l in a ? B(a, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[l] = t;
3
- var d = (a, l, t) => b(a, typeof l != "symbol" ? l + "" : l, t);
1
+ var A = Object.defineProperty;
2
+ var D = (m, c, e) => c in m ? A(m, c, { enumerable: !0, configurable: !0, writable: !0, value: e }) : m[c] = e;
3
+ var u = (m, c, e) => D(m, typeof c != "symbol" ? c + "" : c, e);
4
4
  import { useConfig as g } from "../../../composables/useConfig.js";
5
- import { BlockId as C } from "../../../enums/block.js";
6
- import { getMigrationBannerHtml as A } from "../../../utils/migrationBannerHtml.js";
7
- import { Block as E, BlockCompositionType as S, ModificationDescription as p } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { BlockId as b } from "../../../enums/block.js";
6
+ import { getMigrationBannerHtml as E } from "../../../utils/migrationBannerHtml.js";
7
+ import { Block as S, BlockCompositionType as v, ModificationDescription as _ } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
8
  import { CURRENT_CONFIG_VERSION as N, PRE_RRS_CONFIG_VERSION as w } from "./constants/defaultConfig.js";
9
- import { RECOMMENDATION_ID_ATTR as m } from "./constants/selectors.js";
9
+ import { RECOMMENDATION_ID_ATTR as f } from "./constants/selectors.js";
10
10
  import { regenerateMobileProductRows as M } from "./controls/main/utils.js";
11
11
  import { ensureMobileCssRulesExist as I, setMobileLayoutOptOut as R, hasMobileLayoutOptOut as O } from "./controls/mobileLayout/cssRules.js";
12
- import { RecommendationConfigService as s } from "./services/configService.js";
13
- import { useRecommendationExtensionStore as h } from "./store/recommendation.js";
14
- import { getDefaultTemplate as x } from "./templates/grid/template.js";
15
- import { useRecommendationBlockWarning as T } from "./useRecommendationBlockWarning.js";
16
- import { registerNodeConfigWriter as H } from "./utils/nodeConfigWriter.js";
17
- import { refreshDuplicatedStrategyBlocks as $ } from "./utils/strategyCommit.js";
18
- const k = C.Recommendation, u = "recommendation-block-v2", D = "hide-price";
19
- let y = !1;
20
- class Y extends E {
12
+ import { RecommendationConfigService as a } from "./services/configService.js";
13
+ import { useRecommendationExtensionStore as p } from "./store/recommendation.js";
14
+ import { getDefaultTemplate as T } from "./templates/grid/template.js";
15
+ import { useRecommendationBlockWarning as x } from "./useRecommendationBlockWarning.js";
16
+ import { registerNodeConfigWriter as k, readBlockNodeConfigs as $ } from "./utils/nodeConfigWriter.js";
17
+ import { refreshArrivedStrategyBlocks as q, resolveSourceConfig as H, refreshDuplicatedStrategyBlocks as F } from "./utils/strategyCommit.js";
18
+ const y = b.Recommendation, d = "recommendation-block-v2", C = "hide-price";
19
+ let B = !1;
20
+ class te extends S {
21
21
  constructor() {
22
22
  super();
23
23
  /**
24
24
  * Stores the ID generated in getTemplate() so onCreated() can reuse it.
25
25
  * This avoids generating a new (different) ID in onCreated().
26
26
  */
27
- d(this, "_pendingBlockId", null);
27
+ u(this, "_pendingBlockId", null);
28
28
  /** Ids already reset under their new name, so the deferred pass skips their stale snapshot. */
29
- d(this, "_resetSourceIds", /* @__PURE__ */ new Set());
29
+ u(this, "_resetSourceIds", /* @__PURE__ */ new Set());
30
30
  /** Ids handed to duplicates the document root does not show yet: a copied module renames all its blocks at once. */
31
- d(this, "_unsettledDuplicateIds", /* @__PURE__ */ new Set());
31
+ u(this, "_unsettledDuplicateIds", /* @__PURE__ */ new Set());
32
+ u(this, "_arrivedBlocks", []);
33
+ /** Strategy each in-flight arrival or duplicate refresh is fetching, by block id, so a second one does not race it. */
34
+ u(this, "_refreshClaims", /* @__PURE__ */ new Map());
32
35
  }
33
36
  getId() {
34
- return k;
37
+ return y;
35
38
  }
36
39
  getIcon() {
37
40
  return "recommendation-icon";
38
41
  }
39
42
  getBlockCompositionType() {
40
- return S.CONTAINER;
43
+ return v.CONTAINER;
41
44
  }
42
45
  getName() {
43
46
  return this.api.translate("Recommendation Block");
@@ -48,8 +51,8 @@ class Y extends E {
48
51
  );
49
52
  }
50
53
  getSettingsPanelTitleHtml() {
51
- return A(
52
- k,
54
+ return E(
55
+ y,
53
56
  this.api.translate("Recommendation Block"),
54
57
  this.api.translate("This block is switched from the Old Version to the New Version. We recommend you check the Recommendation block and test your message to ensure it works properly.")
55
58
  );
@@ -71,8 +74,8 @@ class Y extends E {
71
74
  * attribute and overwrites dynamically added classes via setAttribute.
72
75
  */
73
76
  getTemplate() {
74
- const t = this._generateNextId();
75
- return this._pendingBlockId = t, x(t);
77
+ const e = this._generateNextId();
78
+ return this._pendingBlockId = e, T(e);
76
79
  }
77
80
  /**
78
81
  * Called when a new block is dropped into the template
@@ -81,42 +84,57 @@ class Y extends E {
81
84
  * and assigns the recommendation-id attribute + initializes configuration.
82
85
  * @param node - The newly created block node
83
86
  */
84
- onCreated(t) {
85
- const e = this._getRecommendationId(t);
86
- if (e !== null && e > 0) {
87
- if (this._detectDuplicate(e)) {
88
- this._handleDuplicate(t, e), this._scheduleArrivalReset(t);
87
+ onCreated(e) {
88
+ const t = this._getRecommendationId(e);
89
+ if (t !== null && t > 0) {
90
+ if (this._pendingBlockId = null, this._detectDuplicate(t)) {
91
+ this._handleDuplicate(e, t), this._scheduleArrivalReset(e);
89
92
  return;
90
93
  }
91
- this._pendingBlockId === null && setTimeout(() => {
92
- this._getBlockElement(t) && this._detectDuplicate(e) && this._handleDuplicate(t, e);
93
- }, 0), this._scheduleArrivalReset(t);
94
+ setTimeout(() => {
95
+ if (this._getBlockElement(e)) {
96
+ if (this._detectDuplicate(t)) {
97
+ this._handleDuplicate(e, t);
98
+ return;
99
+ }
100
+ this._queueArrivalRefresh(e);
101
+ }
102
+ }, 0), this._scheduleArrivalReset(e);
94
103
  return;
95
104
  }
96
105
  const i = this._pendingBlockId ?? this._generateNextId();
97
- this._pendingBlockId = null, this._assignRecommendationId(t, i);
98
- const n = g().isFeatureEnabled("reusableRecommendationStrategy"), o = n && !s.hasConfig(t), { config: r, wasFreshDrop: c } = s.initializeConfig(
106
+ this._pendingBlockId = null, this._assignRecommendationId(e, i);
107
+ const r = g().isFeatureEnabled("reusableRecommendationStrategy"), s = r && !a.hasConfig(e), { config: n, wasFreshDrop: o } = a.initializeConfig(
99
108
  this.api,
100
- t,
109
+ e,
101
110
  {
102
111
  recommendationId: i,
103
- configVersion: n ? N : w,
104
- ...o ? { partnerName: g().partnerName.value } : {},
112
+ configVersion: r ? N : w,
113
+ ...s ? { partnerName: g().partnerName.value } : {},
105
114
  // An RRS block picks a strategy, not an algorithm, and its algorithm control is
106
115
  // hidden. Keeping the `mostPopular` default would bake a most-popular feed URL
107
116
  // at save for a block the user never configured.
108
- ...o ? { strategy: "" } : {}
117
+ ...s ? { strategy: "" } : {}
109
118
  }
110
- ), f = h();
111
- if (f.setCurrentBlock(i), this._stampHidePrice(t, r.priceHideIfSameAsDiscounted), c) {
119
+ ), l = p();
120
+ if (l.setCurrentBlock(i), this._stampHidePrice(e, n.priceHideIfSameAsDiscounted), o) {
112
121
  I(this.api);
113
- const _ = this._getBlockElement(t);
114
- _ && (R(this.api, _, !0), M({
115
- currentNode: t,
122
+ const h = this._getBlockElement(e);
123
+ h && (R(this.api, h, !0), M({
124
+ currentNode: e,
116
125
  documentModifier: this.api.getDocumentModifier()
117
126
  }));
118
127
  }
119
- f.patchCurrentBlockConfig({ language: r.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
128
+ l.patchCurrentBlockConfig({ language: n.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
129
+ }
130
+ /** Refreshes every block of an opened template: Stripo restores a stored document without `onCreated`. */
131
+ onDocumentInit() {
132
+ try {
133
+ const e = this.api.getDocumentRoot();
134
+ e && "querySelectorAll" in e && e.querySelectorAll(`.${d}`).forEach((t) => this._queueArrivalRefresh(t));
135
+ } catch (e) {
136
+ console.error("[recommendation] refreshing an opened strategy failed", e);
137
+ }
120
138
  }
121
139
  /**
122
140
  * Called when the document changes or template is loaded
@@ -125,24 +143,24 @@ class Y extends E {
125
143
  * and assigns recommendation-id to legacy blocks that lack one.
126
144
  * @param node - The block node that may need migration
127
145
  */
128
- onDocumentChanged(t) {
129
- if (!(!t || !("getNodeConfig" in t))) {
130
- if (!this._getRecommendationId(t)) {
131
- const e = this._generateNextId();
132
- this._assignRecommendationId(t, e), s.hasConfig(t) && s.updateConfig(
146
+ onDocumentChanged(e) {
147
+ if (!(!e || !("getNodeConfig" in e))) {
148
+ if (!this._getRecommendationId(e)) {
149
+ const t = this._generateNextId();
150
+ this._assignRecommendationId(e, t), a.hasConfig(e) && a.updateConfig(
133
151
  this.api,
134
- t,
135
- { recommendationId: e },
152
+ e,
153
+ { recommendationId: t },
136
154
  "Assign recommendation ID to legacy block"
137
155
  );
138
156
  }
139
- this._healLingeringDuplicate(t), s.needsMigration(t) && this._migrateFromLegacy(t), this._healHidePriceAttr(t);
157
+ this._healLingeringDuplicate(e), a.needsMigration(e) && this._migrateFromLegacy(e), this._healHidePriceAttr(e);
140
158
  try {
141
- y || (I(this.api), y = !0);
142
- const e = s.getConfig(t), i = this._getBlockElement(t);
159
+ B || (I(this.api), B = !0);
160
+ const t = a.getConfig(e), i = this._getBlockElement(e);
143
161
  if (i) {
144
- const n = !e.mobileLayoutEnabled;
145
- O(i) !== n && R(this.api, i, n);
162
+ const r = !t.mobileLayoutEnabled;
163
+ O(i) !== r && R(this.api, i, r);
146
164
  }
147
165
  } catch {
148
166
  }
@@ -154,9 +172,9 @@ class Y extends E {
154
172
  * Cleans up the per-block state from the Pinia store.
155
173
  * @param node - The block node being deleted
156
174
  */
157
- onDelete(t) {
158
- const e = this._getRecommendationId(t);
159
- e && h().removeBlockState(e);
175
+ onDelete(e) {
176
+ const t = this._getRecommendationId(e);
177
+ t && p().removeBlockState(t);
160
178
  }
161
179
  /**
162
180
  * Warns (dark advisory toaster) when the design holds more than one live
@@ -172,7 +190,7 @@ class Y extends E {
172
190
  */
173
191
  _warnIfMultipleBlocks() {
174
192
  try {
175
- T().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
193
+ x().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
176
194
  } catch {
177
195
  }
178
196
  }
@@ -181,21 +199,21 @@ class Y extends E {
181
199
  * in the document and finding the maximum existing ID + 1.
182
200
  */
183
201
  _generateNextId() {
184
- let t = 0;
185
- const e = /* @__PURE__ */ new Set();
202
+ let e = 0;
203
+ const t = /* @__PURE__ */ new Set();
186
204
  try {
187
205
  const i = this.api.getDocumentRoot();
188
- i && "querySelectorAll" in i && i.querySelectorAll(`.${u}`).forEach((o) => {
189
- if ("getAttribute" in o) {
190
- const r = o.getAttribute(m), c = r ? parseInt(r) : 0;
191
- e.add(c), c > t && (t = c);
206
+ i && "querySelectorAll" in i && i.querySelectorAll(`.${d}`).forEach((s) => {
207
+ if ("getAttribute" in s) {
208
+ const n = s.getAttribute(f), o = n ? parseInt(n) : 0;
209
+ t.add(o), o > e && (e = o);
192
210
  }
193
211
  });
194
212
  } catch {
195
213
  }
196
214
  return this._unsettledDuplicateIds.forEach((i) => {
197
- e.has(i) && this._unsettledDuplicateIds.delete(i);
198
- }), Math.max(t, ...this._unsettledDuplicateIds) + 1;
215
+ t.has(i) && this._unsettledDuplicateIds.delete(i);
216
+ }), Math.max(e, ...this._unsettledDuplicateIds) + 1;
199
217
  }
200
218
  /**
201
219
  * Drops the "already fetched" claims of a block that arrived holding a baked id
@@ -204,67 +222,119 @@ class Y extends E {
204
222
  * Deferred a tick: the collision handlers rename a sibling first, and either sibling can be
205
223
  * the renamed one depending on where the module was dropped (SD-149916).
206
224
  */
207
- _scheduleArrivalReset(t) {
225
+ _scheduleArrivalReset(e) {
208
226
  g().isFeatureEnabled("reusableRecommendationStrategy") && setTimeout(() => {
209
- const e = this._getRecommendationId(t);
210
- e !== null && e > 0 && !this._resetSourceIds.delete(e) && h().resetArrivedBlock(e);
227
+ const t = this._getRecommendationId(e);
228
+ t !== null && t > 0 && !this._resetSourceIds.delete(t) && p().resetArrivedBlock(t);
211
229
  }, 0);
212
230
  }
231
+ /**
232
+ * Re-reads the strategy of a block that arrived with saved cards, without waiting for a click.
233
+ * Blocks of one opened template or dropped module arrive in one tick and refresh together.
234
+ */
235
+ _queueArrivalRefresh(e, t) {
236
+ g().isFeatureEnabled("reusableRecommendationStrategy") && (this._arrivedBlocks.push({ node: e, id: t }) > 1 || setTimeout(() => {
237
+ const i = this._arrivedBlocks;
238
+ this._arrivedBlocks = [];
239
+ try {
240
+ this._refreshArrivedBlocks(i);
241
+ } catch (r) {
242
+ console.error("[recommendation] refreshing an arrived strategy failed", r);
243
+ }
244
+ }, 0));
245
+ }
246
+ _refreshArrivedBlocks(e) {
247
+ k(this.api);
248
+ const t = /* @__PURE__ */ new Set(), i = e.flatMap(({ node: r, id: s }) => {
249
+ const n = s ?? this._getRecommendationId(r), o = this._getBlockElement(r);
250
+ if (!n || !o || t.has(n))
251
+ return [];
252
+ const l = a.getConfig(o);
253
+ if (!this._canClaimRefresh(n, l.strategyId))
254
+ return [];
255
+ const h = { strategyId: l.strategyId };
256
+ return t.add(n), this._refreshClaims.set(n, h), [{ id: n, config: l, claim: h }];
257
+ });
258
+ i.length && q(i.map(({ id: r, config: s }) => ({ id: r, config: s }))).catch((r) => console.error("[recommendation] refreshing an arrived strategy failed", r)).finally(() => i.forEach(({ id: r, claim: s }) => this._releaseRefreshClaim(r, s)));
259
+ }
260
+ /**
261
+ * A new document reports each block to onCreated and onDocumentInit, not always in one batch, so a
262
+ * claimed block refreshes again only once the document shows it under another strategy (a restored version).
263
+ */
264
+ _canClaimRefresh(e, t) {
265
+ const i = this._refreshClaims.get(e);
266
+ if (!i)
267
+ return !0;
268
+ if (i.strategyId === t)
269
+ return !1;
270
+ const r = $(e);
271
+ return r.length > 0 && r.every((s) => s.strategyId === t);
272
+ }
273
+ _releaseRefreshClaim(e, t) {
274
+ this._refreshClaims.get(e) === t && this._refreshClaims.delete(e);
275
+ }
213
276
  /**
214
277
  * Heals a duplicate that slipped past the create-time `_detectDuplicate`
215
278
  * scan (or that was persisted by an older template before SD-142352).
216
279
  * When two blocks share a `recommendation-id`, the first occurrence in DOM
217
280
  * order is treated as the source and any later sibling is reassigned a
218
- * fresh id. The store entry and active-block focus are intentionally not
219
- * touched here: each block's persisted `esd-ext-config` carries its own
220
- * truth and the regular save-time hydration (`hydrateRecommendationStore`)
221
- * will seed the new id from that. Skipping `setCurrentBlock` avoids
281
+ * fresh id and queued for the arrival refresh, which seeds the new id's
282
+ * store entry from its persisted `esd-ext-config`. The source's store
283
+ * entry is not cloned and focus is not moved. Skipping `setCurrentBlock` avoids
222
284
  * yanking the user's current selection mid-edit when the heal fires from
223
285
  * `onDocumentChanged`.
224
286
  */
225
- _healLingeringDuplicate(t) {
226
- const e = this._getRecommendationId(t);
227
- if (!(e === null || e <= 0))
287
+ _healLingeringDuplicate(e) {
288
+ const t = this._getRecommendationId(e);
289
+ if (!(t === null || t <= 0))
228
290
  try {
229
291
  const i = this.api.getDocumentRoot();
230
292
  if (!i || !("querySelectorAll" in i))
231
293
  return;
232
- const n = i.querySelectorAll(
233
- `.${u}[${m}="${e}"]`
294
+ const r = i.querySelectorAll(
295
+ `.${d}[${f}="${t}"]`
234
296
  );
235
- if (n.length <= 1)
297
+ if (r.length <= 1)
236
298
  return;
237
- const o = this._getBlockElement(t);
238
- if (!o)
299
+ const s = this._getBlockElement(e);
300
+ if (!s)
239
301
  return;
240
- let r = -1;
241
- for (let c = 0; c < n.length; c++)
242
- if (n[c] === o) {
243
- r = c;
302
+ let n = -1;
303
+ for (let o = 0; o < r.length; o++)
304
+ if (r[o] === s) {
305
+ n = o;
244
306
  break;
245
307
  }
246
- if (r <= 0)
308
+ if (n <= 0)
247
309
  return;
248
- this._reassignDuplicateId(t, e);
310
+ this._queueArrivalRefresh(e, this._reassignDuplicateId(e, t));
249
311
  } catch {
250
312
  }
251
313
  }
252
314
  /** True when another block already holds the same id — Stripo's duplication signal. */
253
- _detectDuplicate(t) {
315
+ _detectDuplicate(e) {
254
316
  try {
255
- const e = this.api.getDocumentRoot();
256
- if (e && "querySelectorAll" in e)
257
- return e.querySelectorAll(
258
- `.${u}[${m}="${t}"]`
317
+ const t = this.api.getDocumentRoot();
318
+ if (t && "querySelectorAll" in t)
319
+ return t.querySelectorAll(
320
+ `.${d}[${f}="${e}"]`
259
321
  ).length > 1;
260
322
  } catch {
261
323
  }
262
324
  return !1;
263
325
  }
264
326
  /** Assigns a fresh id to a duplicated block and syncs DOM, node config and store. */
265
- _handleDuplicate(t, e) {
266
- const i = this._reassignDuplicateId(t, e), n = h();
267
- n.cloneBlockState(e, i), n.setCurrentBlock(i), g().isFeatureEnabled("reusableRecommendationStrategy") && (this._resetSourceIds.add(e), H(this.api), $(e, i, s.getConfig(t)).catch((o) => console.error("[recommendation] refreshing a duplicated strategy failed", o))), this._warnIfMultipleBlocks();
327
+ _handleDuplicate(e, t) {
328
+ const i = this._reassignDuplicateId(e, t), r = p();
329
+ if (r.cloneBlockState(t, i), r.setCurrentBlock(i), g().isFeatureEnabled("reusableRecommendationStrategy")) {
330
+ this._resetSourceIds.add(t), k(this.api);
331
+ const s = a.getConfig(e), n = [
332
+ { id: t, claim: { strategyId: H(t, s).strategyId } },
333
+ { id: i, claim: { strategyId: s.strategyId } }
334
+ ].filter(({ id: o }) => !this._refreshClaims.has(o));
335
+ n.forEach(({ id: o, claim: l }) => this._refreshClaims.set(o, l)), F(t, i, s).catch((o) => console.error("[recommendation] refreshing a duplicated strategy failed", o)).finally(() => n.forEach(({ id: o, claim: l }) => this._releaseRefreshClaim(o, l)));
336
+ }
337
+ this._warnIfMultipleBlocks();
268
338
  }
269
339
  /**
270
340
  * Core id-reassignment for a duplicated block: rewrites the DOM
@@ -273,17 +343,17 @@ class Y extends E {
273
343
  *
274
344
  * Used by `_handleDuplicate` at create-time (which additionally clones the
275
345
  * store entry and focuses the new block) and by `_healLingeringDuplicate`
276
- * at document-change time (which intentionally skips store/focus side
277
- * effects so older bug-affected templates can self-repair without
278
- * disturbing the user's current selection).
346
+ * at document-change time (which skips the clone and focus change so older
347
+ * bug-affected templates can self-repair without disturbing the user's
348
+ * current selection, and queues the arrival refresh for the new id).
279
349
  */
280
- _reassignDuplicateId(t, e) {
281
- const i = this._generateNextId(), n = this._getBlockElement(t);
282
- return this._unsettledDuplicateIds.add(i), this._assignRecommendationId(t, i), n && this._reassignInstanceClass(n, e, i), s.hasConfig(t) && s.updateConfig(
350
+ _reassignDuplicateId(e, t) {
351
+ const i = this._generateNextId(), r = this._getBlockElement(e);
352
+ return this._unsettledDuplicateIds.add(i), this._assignRecommendationId(e, i), r && this._reassignInstanceClass(r, t, i), a.hasConfig(e) && a.updateConfig(
283
353
  this.api,
284
- t,
354
+ e,
285
355
  { recommendationId: i },
286
- `Reassign recommendation ID on duplicate (was ${e})`
356
+ `Reassign recommendation ID on duplicate (was ${t})`
287
357
  ), i;
288
358
  }
289
359
  /**
@@ -293,12 +363,12 @@ class Y extends E {
293
363
  * the source's class would both leak stylistic edits across blocks and
294
364
  * cause the mail render to apply the source block's config to the clone.
295
365
  */
296
- _reassignInstanceClass(t, e, i) {
297
- if (!("getAttribute" in t))
366
+ _reassignInstanceClass(e, t, i) {
367
+ if (!("getAttribute" in e))
298
368
  return;
299
- const n = `ins-recommendation-v3-block-${e}`, o = `ins-recommendation-v3-block-${i}`, r = this.api.getDocumentModifier();
300
- r.modifyHtml(t).removeClass(n).setClass(o), r.apply(new p(
301
- `Reassign recommendation instance class ${n} -> ${o}`
369
+ const r = `ins-recommendation-v3-block-${t}`, s = `ins-recommendation-v3-block-${i}`, n = this.api.getDocumentModifier();
370
+ n.modifyHtml(e).removeClass(r).setClass(s), n.apply(new _(
371
+ `Reassign recommendation instance class ${r} -> ${s}`
302
372
  ));
303
373
  }
304
374
  /**
@@ -307,64 +377,64 @@ class Y extends E {
307
377
  * HTML by getTemplate(), since Stripo's Angular rendering overwrites dynamically
308
378
  * added classes via setAttribute.
309
379
  */
310
- _assignRecommendationId(t, e) {
311
- const i = this._getBlockElement(t);
380
+ _assignRecommendationId(e, t) {
381
+ const i = this._getBlockElement(e);
312
382
  if (!i)
313
383
  return;
314
- const n = this.api.getDocumentModifier();
315
- n.modifyHtml(i).setAttribute(m, e.toString()), n.apply(new p(`Assign recommendation ID ${e}`));
384
+ const r = this.api.getDocumentModifier();
385
+ r.modifyHtml(i).setAttribute(f, t.toString()), r.apply(new _(`Assign recommendation ID ${t}`));
316
386
  }
317
387
  /**
318
388
  * Stamps the `hide-price` attribute (read by email-service at send time) on
319
389
  * the block element from the config flag.
320
390
  */
321
- _stampHidePrice(t, e) {
322
- const i = this._getBlockElement(t);
323
- i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(D, String(e)).apply(new p("Stamp hide-price"));
391
+ _stampHidePrice(e, t) {
392
+ const i = this._getBlockElement(e);
393
+ i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(C, String(t)).apply(new _("Stamp hide-price"));
324
394
  }
325
395
  /**
326
396
  * Stamps `hide-price` from config only when the block lacks the attribute —
327
397
  * heals templates saved before this feature without dirtying current ones.
328
398
  */
329
- _healHidePriceAttr(t) {
330
- const e = this._getBlockElement(t);
331
- if (!e || !("getAttribute" in e) || e.getAttribute(D) !== null)
399
+ _healHidePriceAttr(e) {
400
+ const t = this._getBlockElement(e);
401
+ if (!t || !("getAttribute" in t) || t.getAttribute(C) !== null)
332
402
  return;
333
- const { priceHideIfSameAsDiscounted: i } = s.getConfig(t);
334
- this._stampHidePrice(t, i);
403
+ const { priceHideIfSameAsDiscounted: i } = a.getConfig(e);
404
+ this._stampHidePrice(e, i);
335
405
  }
336
406
  /**
337
407
  * Gets the recommendation-id from a block node
338
408
  */
339
- _getRecommendationId(t) {
340
- const e = this._getBlockElement(t);
341
- if (!e || !("getAttribute" in e))
409
+ _getRecommendationId(e) {
410
+ const t = this._getBlockElement(e);
411
+ if (!t || !("getAttribute" in t))
342
412
  return null;
343
- const i = e.getAttribute(m);
413
+ const i = t.getAttribute(f);
344
414
  if (!i)
345
415
  return null;
346
- const n = parseInt(i);
347
- return Number.isNaN(n) ? null : n;
416
+ const r = parseInt(i);
417
+ return Number.isNaN(r) ? null : r;
348
418
  }
349
419
  /**
350
420
  * Gets the block element (the element with BLOCK_CLASS)
351
421
  */
352
- _getBlockElement(t) {
353
- if ("getAttribute" in t) {
354
- const e = t.getAttribute("class");
355
- if (e && e.includes(u))
356
- return t;
422
+ _getBlockElement(e) {
423
+ if ("getAttribute" in e) {
424
+ const t = e.getAttribute("class");
425
+ if (t && t.includes(d))
426
+ return e;
357
427
  }
358
- return "querySelector" in t ? t.querySelector(`.${u}`) ?? null : null;
428
+ return "querySelector" in e ? e.querySelector(`.${d}`) ?? null : null;
359
429
  }
360
430
  /**
361
431
  * Migrate configuration from legacy format
362
432
  */
363
- _migrateFromLegacy(t) {
364
- s.migrateFromDataAttributes(this.api, t);
433
+ _migrateFromLegacy(e) {
434
+ a.migrateFromDataAttributes(this.api, e);
365
435
  }
366
436
  }
367
437
  export {
368
- k as BLOCK_ID,
369
- Y as RecommendationBlock
438
+ y as BLOCK_ID,
439
+ te as RecommendationBlock
370
440
  };
@@ -1,23 +1,23 @@
1
- var d = Object.defineProperty;
2
- var h = (r, s, t) => s in r ? d(r, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[s] = t;
3
- var c = (r, s, t) => h(r, typeof s != "symbol" ? s + "" : s, t);
4
- import { UEAttr as l } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as g } from "../../../common-control.js";
1
+ var h = Object.defineProperty;
2
+ var l = (r, s, t) => s in r ? h(r, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[s] = t;
3
+ var d = (r, s, t) => l(r, typeof s != "symbol" ? s + "" : s, t);
4
+ import { UEAttr as g } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as m } from "../../../common-control.js";
6
6
  import { RecommendationConfigService as n } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as m } from "../../store/recommendation.js";
8
- import { mapLegacyStrategy as u } from "../../utils/legacyStrategyMap.js";
9
- const p = "recommendation-algorithm-control", i = {
7
+ import { useRecommendationExtensionStore as u } from "../../store/recommendation.js";
8
+ import { mapLegacyStrategy as p } from "../../utils/legacyStrategyMap.js";
9
+ const C = "recommendation-algorithm-control", i = {
10
10
  ALGORITHM: "strategy",
11
11
  PRODUCT_IDS: "productIds"
12
12
  };
13
- class R extends g {
13
+ class S extends m {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  // Store is used ONLY for API-fetched data (algorithms list), not for config
17
- c(this, "store", m());
17
+ d(this, "store", u());
18
18
  }
19
19
  getId() {
20
- return p;
20
+ return C;
21
21
  }
22
22
  getTemplate() {
23
23
  return `
@@ -46,10 +46,10 @@ class R extends g {
46
46
  super.onTemplateNodeUpdated(t), this._initializeSelectItems(), this._setFormValues();
47
47
  }
48
48
  _setFormValues() {
49
- const t = n.getConfig(this.currentNode), e = u(t.strategy) ?? t.strategy;
49
+ const t = n.getConfig(this.currentNode), e = p(t.strategy) ?? t.strategy;
50
50
  this._setProductIdsVisibility(e);
51
- const o = { [i.ALGORITHM]: e }, a = t.productIds.join(",");
52
- this.api.getValues()[i.PRODUCT_IDS] !== a && (o[i.PRODUCT_IDS] = a), this.api.updateValues(o);
51
+ const o = { [i.ALGORITHM]: e }, a = t.productIds.join(","), c = this.api.getValues()[i.PRODUCT_IDS];
52
+ c !== a && c !== this.store.recommendationConfigs.productIds.join(",") && (o[i.PRODUCT_IDS] = a), this.api.updateValues(o);
53
53
  }
54
54
  _initializeSelectItems() {
55
55
  var e;
@@ -58,7 +58,7 @@ class R extends g {
58
58
  try {
59
59
  this.api.setUIEAttribute(
60
60
  i.ALGORITHM,
61
- l.SELECTPICKER.items,
61
+ g.SELECTPICKER.items,
62
62
  t
63
63
  );
64
64
  } catch (o) {
@@ -93,12 +93,7 @@ class R extends g {
93
93
  return;
94
94
  }
95
95
  const o = t === "" ? [] : t.split(",");
96
- n.updateConfig(
97
- this.api,
98
- this.currentNode,
99
- { productIds: o },
100
- "Updated product IDs"
101
- ), this.store.patchCurrentBlockConfig({ productIds: o });
96
+ this.store.recommendationConfigs.productIds.join(",") !== t && this.store.patchCurrentBlockConfig({ productIds: o });
102
97
  }
103
98
  _listenToFormUpdates() {
104
99
  this.api.onValueChanged(i.ALGORITHM, (t) => {
@@ -109,6 +104,6 @@ class R extends g {
109
104
  }
110
105
  }
111
106
  export {
112
- p as ALGORITHM_CONTROL_ID,
113
- R as AlgorithmControl
107
+ C as ALGORITHM_CONTROL_ID,
108
+ S as AlgorithmControl
114
109
  };