@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.
- package/dist/config/compiler/htmlCompilerRules.js +1 -1
- package/dist/extensions/Blocks/Recommendation/block.js +209 -139
- package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +18 -23
- package/dist/extensions/Blocks/Recommendation/controls/main/index.js +171 -141
- package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +68 -59
- package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +249 -248
- package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +62 -45
- package/dist/extensions/Blocks/Recommendation/utils/strategyCommit.js +52 -31
- package/dist/package.json.js +1 -1
- package/dist/src/extensions/Blocks/Recommendation/block.d.ts +23 -7
- package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +6 -0
- package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +5 -2
- package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +6 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/nodeConfigWriter.d.ts +4 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/strategyCommit.d.ts +11 -0
- package/dist/static/styles/components/tabs.css.js +5 -0
- package/package.json +2 -2
|
@@ -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
|
|
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
|
|
2
|
-
var
|
|
3
|
-
var
|
|
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
|
|
6
|
-
import { getMigrationBannerHtml as
|
|
7
|
-
import { Block as
|
|
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
|
|
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
|
|
13
|
-
import { useRecommendationExtensionStore as
|
|
14
|
-
import { getDefaultTemplate as
|
|
15
|
-
import { useRecommendationBlockWarning as
|
|
16
|
-
import { registerNodeConfigWriter as
|
|
17
|
-
import { refreshDuplicatedStrategyBlocks as
|
|
18
|
-
const
|
|
19
|
-
let
|
|
20
|
-
class
|
|
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
|
-
|
|
27
|
+
u(this, "_pendingBlockId", null);
|
|
28
28
|
/** Ids already reset under their new name, so the deferred pass skips their stale snapshot. */
|
|
29
|
-
|
|
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
|
-
|
|
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
|
|
37
|
+
return y;
|
|
35
38
|
}
|
|
36
39
|
getIcon() {
|
|
37
40
|
return "recommendation-icon";
|
|
38
41
|
}
|
|
39
42
|
getBlockCompositionType() {
|
|
40
|
-
return
|
|
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
|
|
52
|
-
|
|
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
|
|
75
|
-
return this._pendingBlockId =
|
|
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(
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
if (this._detectDuplicate(
|
|
88
|
-
this._handleDuplicate(
|
|
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
|
-
|
|
92
|
-
this._getBlockElement(
|
|
93
|
-
|
|
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(
|
|
98
|
-
const
|
|
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
|
-
|
|
109
|
+
e,
|
|
101
110
|
{
|
|
102
111
|
recommendationId: i,
|
|
103
|
-
configVersion:
|
|
104
|
-
...
|
|
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
|
-
...
|
|
117
|
+
...s ? { strategy: "" } : {}
|
|
109
118
|
}
|
|
110
|
-
),
|
|
111
|
-
if (
|
|
119
|
+
), l = p();
|
|
120
|
+
if (l.setCurrentBlock(i), this._stampHidePrice(e, n.priceHideIfSameAsDiscounted), o) {
|
|
112
121
|
I(this.api);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
currentNode:
|
|
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
|
-
|
|
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(
|
|
129
|
-
if (!(!
|
|
130
|
-
if (!this._getRecommendationId(
|
|
131
|
-
const
|
|
132
|
-
this._assignRecommendationId(
|
|
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
|
-
|
|
135
|
-
{ recommendationId:
|
|
152
|
+
e,
|
|
153
|
+
{ recommendationId: t },
|
|
136
154
|
"Assign recommendation ID to legacy block"
|
|
137
155
|
);
|
|
138
156
|
}
|
|
139
|
-
this._healLingeringDuplicate(
|
|
157
|
+
this._healLingeringDuplicate(e), a.needsMigration(e) && this._migrateFromLegacy(e), this._healHidePriceAttr(e);
|
|
140
158
|
try {
|
|
141
|
-
|
|
142
|
-
const
|
|
159
|
+
B || (I(this.api), B = !0);
|
|
160
|
+
const t = a.getConfig(e), i = this._getBlockElement(e);
|
|
143
161
|
if (i) {
|
|
144
|
-
const
|
|
145
|
-
O(i) !==
|
|
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(
|
|
158
|
-
const
|
|
159
|
-
|
|
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
|
-
|
|
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
|
|
185
|
-
const
|
|
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(`.${
|
|
189
|
-
if ("getAttribute" in
|
|
190
|
-
const
|
|
191
|
-
|
|
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
|
-
|
|
198
|
-
}), Math.max(
|
|
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(
|
|
225
|
+
_scheduleArrivalReset(e) {
|
|
208
226
|
g().isFeatureEnabled("reusableRecommendationStrategy") && setTimeout(() => {
|
|
209
|
-
const
|
|
210
|
-
|
|
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
|
|
219
|
-
*
|
|
220
|
-
*
|
|
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(
|
|
226
|
-
const
|
|
227
|
-
if (!(
|
|
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
|
|
233
|
-
`.${
|
|
294
|
+
const r = i.querySelectorAll(
|
|
295
|
+
`.${d}[${f}="${t}"]`
|
|
234
296
|
);
|
|
235
|
-
if (
|
|
297
|
+
if (r.length <= 1)
|
|
236
298
|
return;
|
|
237
|
-
const
|
|
238
|
-
if (!
|
|
299
|
+
const s = this._getBlockElement(e);
|
|
300
|
+
if (!s)
|
|
239
301
|
return;
|
|
240
|
-
let
|
|
241
|
-
for (let
|
|
242
|
-
if (
|
|
243
|
-
|
|
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 (
|
|
308
|
+
if (n <= 0)
|
|
247
309
|
return;
|
|
248
|
-
this._reassignDuplicateId(
|
|
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(
|
|
315
|
+
_detectDuplicate(e) {
|
|
254
316
|
try {
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
257
|
-
return
|
|
258
|
-
`.${
|
|
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(
|
|
266
|
-
const i = this._reassignDuplicateId(
|
|
267
|
-
|
|
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
|
|
277
|
-
*
|
|
278
|
-
*
|
|
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(
|
|
281
|
-
const i = this._generateNextId(),
|
|
282
|
-
return this._unsettledDuplicateIds.add(i), this._assignRecommendationId(
|
|
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
|
-
|
|
354
|
+
e,
|
|
285
355
|
{ recommendationId: i },
|
|
286
|
-
`Reassign recommendation ID on duplicate (was ${
|
|
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(
|
|
297
|
-
if (!("getAttribute" in
|
|
366
|
+
_reassignInstanceClass(e, t, i) {
|
|
367
|
+
if (!("getAttribute" in e))
|
|
298
368
|
return;
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
`Reassign recommendation instance class ${
|
|
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(
|
|
311
|
-
const i = this._getBlockElement(
|
|
380
|
+
_assignRecommendationId(e, t) {
|
|
381
|
+
const i = this._getBlockElement(e);
|
|
312
382
|
if (!i)
|
|
313
383
|
return;
|
|
314
|
-
const
|
|
315
|
-
|
|
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(
|
|
322
|
-
const i = this._getBlockElement(
|
|
323
|
-
i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(
|
|
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(
|
|
330
|
-
const
|
|
331
|
-
if (!
|
|
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 } =
|
|
334
|
-
this._stampHidePrice(
|
|
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(
|
|
340
|
-
const
|
|
341
|
-
if (!
|
|
409
|
+
_getRecommendationId(e) {
|
|
410
|
+
const t = this._getBlockElement(e);
|
|
411
|
+
if (!t || !("getAttribute" in t))
|
|
342
412
|
return null;
|
|
343
|
-
const i =
|
|
413
|
+
const i = t.getAttribute(f);
|
|
344
414
|
if (!i)
|
|
345
415
|
return null;
|
|
346
|
-
const
|
|
347
|
-
return Number.isNaN(
|
|
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(
|
|
353
|
-
if ("getAttribute" in
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
356
|
-
return
|
|
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
|
|
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(
|
|
364
|
-
|
|
433
|
+
_migrateFromLegacy(e) {
|
|
434
|
+
a.migrateFromDataAttributes(this.api, e);
|
|
365
435
|
}
|
|
366
436
|
}
|
|
367
437
|
export {
|
|
368
|
-
|
|
369
|
-
|
|
438
|
+
y as BLOCK_ID,
|
|
439
|
+
te as RecommendationBlock
|
|
370
440
|
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { UEAttr as
|
|
5
|
-
import { CommonControl as
|
|
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
|
|
8
|
-
import { mapLegacyStrategy as
|
|
9
|
-
const
|
|
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
|
|
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
|
-
|
|
17
|
+
d(this, "store", u());
|
|
18
18
|
}
|
|
19
19
|
getId() {
|
|
20
|
-
return
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
107
|
+
C as ALGORITHM_CONTROL_ID,
|
|
108
|
+
S as AlgorithmControl
|
|
114
109
|
};
|