@useinsider/guido 3.4.1-beta.6ed05e2 → 3.4.1-beta.8f6f02b
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/extensions/Blocks/Recommendation/block.js +90 -64
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +33 -42
- package/dist/src/extensions/Blocks/Recommendation/block.d.ts +11 -14
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +3 -6
- package/dist/utils/templatePreparation.js +64 -69
- package/package.json +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { BlockId as
|
|
5
|
-
import { getMigrationBannerHtml as
|
|
6
|
-
import { Block as
|
|
7
|
-
import { regenerateMobileProductRows as
|
|
8
|
-
import { ensureMobileCssRulesExist as
|
|
1
|
+
var B = Object.defineProperty;
|
|
2
|
+
var C = (a, s, t) => s in a ? B(a, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[s] = t;
|
|
3
|
+
var p = (a, s, t) => C(a, typeof s != "symbol" ? s + "" : s, t);
|
|
4
|
+
import { BlockId as R } from "../../../enums/block.js";
|
|
5
|
+
import { getMigrationBannerHtml as b } from "../../../utils/migrationBannerHtml.js";
|
|
6
|
+
import { Block as A, BlockCompositionType as y, ModificationDescription as f } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
|
|
7
|
+
import { regenerateMobileProductRows as D } from "./controls/main/utils.js";
|
|
8
|
+
import { ensureMobileCssRulesExist as h, setMobileLayoutOptOut as I, hasMobileLayoutOptOut as E } from "./controls/mobileLayout/cssRules.js";
|
|
9
9
|
import { RecommendationConfigService as c } from "./services/configService.js";
|
|
10
|
-
import { useRecommendationExtensionStore as
|
|
11
|
-
import { getDefaultTemplate as
|
|
12
|
-
const
|
|
13
|
-
let
|
|
14
|
-
class q extends
|
|
10
|
+
import { useRecommendationExtensionStore as g } from "./store/recommendation.js";
|
|
11
|
+
import { getDefaultTemplate as N } from "./templates/grid/template.js";
|
|
12
|
+
const k = R.Recommendation, m = "recommendation-block-v2", u = "recommendation-id";
|
|
13
|
+
let _ = !1;
|
|
14
|
+
class q extends A {
|
|
15
15
|
constructor() {
|
|
16
16
|
super();
|
|
17
17
|
/**
|
|
18
18
|
* Stores the ID generated in getTemplate() so onCreated() can reuse it.
|
|
19
19
|
* This avoids generating a new (different) ID in onCreated().
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
p(this, "_pendingBlockId", null);
|
|
22
22
|
}
|
|
23
23
|
getId() {
|
|
24
|
-
return
|
|
24
|
+
return k;
|
|
25
25
|
}
|
|
26
26
|
getIcon() {
|
|
27
27
|
return "recommendation-icon";
|
|
28
28
|
}
|
|
29
29
|
getBlockCompositionType() {
|
|
30
|
-
return
|
|
30
|
+
return y.CONTAINER;
|
|
31
31
|
}
|
|
32
32
|
getName() {
|
|
33
33
|
return this.api.translate("Recommendation Block");
|
|
@@ -38,8 +38,8 @@ class q extends b {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
getSettingsPanelTitleHtml() {
|
|
41
|
-
return
|
|
42
|
-
|
|
41
|
+
return b(
|
|
42
|
+
k,
|
|
43
43
|
this.api.translate("Recommendation Block"),
|
|
44
44
|
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.")
|
|
45
45
|
);
|
|
@@ -59,7 +59,7 @@ class q extends b {
|
|
|
59
59
|
*/
|
|
60
60
|
getTemplate() {
|
|
61
61
|
const t = this._generateNextId();
|
|
62
|
-
return this._pendingBlockId = t,
|
|
62
|
+
return this._pendingBlockId = t, N(t);
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Called when a new block is dropped into the template
|
|
@@ -70,19 +70,21 @@ class q extends b {
|
|
|
70
70
|
*/
|
|
71
71
|
onCreated(t) {
|
|
72
72
|
const e = this._getRecommendationId(t);
|
|
73
|
-
if (e !== null && e > 0)
|
|
73
|
+
if (e !== null && e > 0) {
|
|
74
|
+
this._detectDuplicate(e) && this._handleDuplicate(t, e);
|
|
74
75
|
return;
|
|
75
|
-
|
|
76
|
-
this._pendingBlockId
|
|
77
|
-
|
|
76
|
+
}
|
|
77
|
+
const i = this._pendingBlockId ?? this._generateNextId();
|
|
78
|
+
this._pendingBlockId = null, this._assignRecommendationId(t, i);
|
|
79
|
+
const { config: n, wasFreshDrop: o } = c.initializeConfig(
|
|
78
80
|
this.api,
|
|
79
81
|
t,
|
|
80
|
-
{ recommendationId:
|
|
81
|
-
), r =
|
|
82
|
-
if (r.setCurrentBlock(
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
82
|
+
{ recommendationId: i }
|
|
83
|
+
), r = g();
|
|
84
|
+
if (r.setCurrentBlock(i), o) {
|
|
85
|
+
h(this.api);
|
|
86
|
+
const l = this._getBlockElement(t);
|
|
87
|
+
l && (I(this.api, l, !0), D({
|
|
86
88
|
currentNode: t,
|
|
87
89
|
documentModifier: this.api.getDocumentModifier()
|
|
88
90
|
}));
|
|
@@ -109,11 +111,11 @@ class q extends b {
|
|
|
109
111
|
}
|
|
110
112
|
c.needsMigration(t) && this._migrateFromLegacy(t);
|
|
111
113
|
try {
|
|
112
|
-
|
|
113
|
-
const e = c.getConfig(t),
|
|
114
|
-
if (
|
|
114
|
+
_ || (h(this.api), _ = !0);
|
|
115
|
+
const e = c.getConfig(t), i = this._getBlockElement(t);
|
|
116
|
+
if (i) {
|
|
115
117
|
const n = !e.mobileLayoutEnabled;
|
|
116
|
-
|
|
118
|
+
E(i) !== n && I(this.api, i, n);
|
|
117
119
|
}
|
|
118
120
|
} catch {
|
|
119
121
|
}
|
|
@@ -127,26 +129,7 @@ class q extends b {
|
|
|
127
129
|
*/
|
|
128
130
|
onDelete(t) {
|
|
129
131
|
const e = this._getRecommendationId(t);
|
|
130
|
-
e &&
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Lifecycle hook fired when Stripo's UI Duplicate button copies a block.
|
|
134
|
-
* Stripo passes an `HtmlNodeModifier` already targeting the cloned node;
|
|
135
|
-
* its mutations are committed when this method returns. We stamp a fresh
|
|
136
|
-
* `recommendation-id` on the clone and seed its blockStates entry from
|
|
137
|
-
* the source so the duplicate enters the DOM as a fully independent block
|
|
138
|
-
* — no shared id, no shared store, no API refetch flash.
|
|
139
|
-
*
|
|
140
|
-
* Stripo does NOT fire `onCreated` for blocks created via duplicate
|
|
141
|
-
* (per Block.d.ts:54-69 — `onCreated` is drag-and-drop only), so this
|
|
142
|
-
* hook is the sole production path for clone id assignment.
|
|
143
|
-
*/
|
|
144
|
-
onCopy(t) {
|
|
145
|
-
const e = t.getTargetNode(), o = this._getRecommendationId(e), n = this._generateNextId(), s = c.getConfig(e);
|
|
146
|
-
if (t.setAttribute(l, n.toString()).setNodeConfig({ ...s, recommendationId: n }), o !== null && o > 0) {
|
|
147
|
-
const r = m();
|
|
148
|
-
r.cloneBlockState(o, n), r.setCurrentBlock(n);
|
|
149
|
-
}
|
|
132
|
+
e && g().removeBlockState(e);
|
|
150
133
|
}
|
|
151
134
|
/**
|
|
152
135
|
* Generates the next unique recommendation ID by scanning all existing blocks
|
|
@@ -156,9 +139,9 @@ class q extends b {
|
|
|
156
139
|
let t = 0;
|
|
157
140
|
try {
|
|
158
141
|
const e = this.api.getDocumentRoot();
|
|
159
|
-
e && "querySelectorAll" in e && e.querySelectorAll(`.${
|
|
142
|
+
e && "querySelectorAll" in e && e.querySelectorAll(`.${m}`).forEach((n) => {
|
|
160
143
|
if ("getAttribute" in n) {
|
|
161
|
-
const
|
|
144
|
+
const o = n.getAttribute(u), r = o ? parseInt(o) : 0;
|
|
162
145
|
r > t && (t = r);
|
|
163
146
|
}
|
|
164
147
|
});
|
|
@@ -166,6 +149,49 @@ class q extends b {
|
|
|
166
149
|
}
|
|
167
150
|
return t + 1;
|
|
168
151
|
}
|
|
152
|
+
/** True when another block already holds the same id — Stripo's duplication signal. */
|
|
153
|
+
_detectDuplicate(t) {
|
|
154
|
+
try {
|
|
155
|
+
const e = this.api.getDocumentRoot();
|
|
156
|
+
if (e && "querySelectorAll" in e)
|
|
157
|
+
return e.querySelectorAll(
|
|
158
|
+
`.${m}[${u}="${t}"]`
|
|
159
|
+
).length > 1;
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
return !1;
|
|
163
|
+
}
|
|
164
|
+
/** Assigns a fresh id to a duplicated block and syncs DOM, node config and store. */
|
|
165
|
+
_handleDuplicate(t, e) {
|
|
166
|
+
const i = this._generateNextId(), n = this._getBlockElement(t);
|
|
167
|
+
this._assignRecommendationId(t, i), n && this._reassignInstanceClass(n, e, i), c.hasConfig(t) && c.updateConfig(
|
|
168
|
+
this.api,
|
|
169
|
+
t,
|
|
170
|
+
{ recommendationId: i },
|
|
171
|
+
`Reassign recommendation ID on duplicate (was ${e})`
|
|
172
|
+
);
|
|
173
|
+
const o = g();
|
|
174
|
+
o.cloneBlockState(e, i), o.setCurrentBlock(i);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rewrites `ins-recommendation-v3-block-{id}` on the clone — the instance
|
|
178
|
+
* class scopes per-block CSS rules, so leaving the source's class would
|
|
179
|
+
* make stylistic edits leak between source and clone.
|
|
180
|
+
*/
|
|
181
|
+
_reassignInstanceClass(t, e, i) {
|
|
182
|
+
if (!("getAttribute" in t))
|
|
183
|
+
return;
|
|
184
|
+
const n = t.getAttribute("class");
|
|
185
|
+
if (!n)
|
|
186
|
+
return;
|
|
187
|
+
const o = `ins-recommendation-v3-block-${e}`, r = `ins-recommendation-v3-block-${i}`;
|
|
188
|
+
if (!n.includes(o))
|
|
189
|
+
return;
|
|
190
|
+
const l = n.replace(o, r), d = this.api.getDocumentModifier();
|
|
191
|
+
d.modifyHtml(t).setAttribute("class", l), d.apply(new f(
|
|
192
|
+
`Reassign recommendation instance class ${o} -> ${r}`
|
|
193
|
+
));
|
|
194
|
+
}
|
|
169
195
|
/**
|
|
170
196
|
* Assigns a recommendation-id attribute to the block element.
|
|
171
197
|
* The instance class (ins-recommendation-v3-block-{id}) is embedded in the template
|
|
@@ -173,11 +199,11 @@ class q extends b {
|
|
|
173
199
|
* added classes via setAttribute.
|
|
174
200
|
*/
|
|
175
201
|
_assignRecommendationId(t, e) {
|
|
176
|
-
const
|
|
177
|
-
if (!
|
|
202
|
+
const i = this._getBlockElement(t);
|
|
203
|
+
if (!i)
|
|
178
204
|
return;
|
|
179
205
|
const n = this.api.getDocumentModifier();
|
|
180
|
-
n.modifyHtml(
|
|
206
|
+
n.modifyHtml(i).setAttribute(u, e.toString()), n.apply(new f(`Assign recommendation ID ${e}`));
|
|
181
207
|
}
|
|
182
208
|
/**
|
|
183
209
|
* Gets the recommendation-id from a block node
|
|
@@ -186,10 +212,10 @@ class q extends b {
|
|
|
186
212
|
const e = this._getBlockElement(t);
|
|
187
213
|
if (!e || !("getAttribute" in e))
|
|
188
214
|
return null;
|
|
189
|
-
const
|
|
190
|
-
if (!
|
|
215
|
+
const i = e.getAttribute(u);
|
|
216
|
+
if (!i)
|
|
191
217
|
return null;
|
|
192
|
-
const n = parseInt(
|
|
218
|
+
const n = parseInt(i);
|
|
193
219
|
return Number.isNaN(n) ? null : n;
|
|
194
220
|
}
|
|
195
221
|
/**
|
|
@@ -198,10 +224,10 @@ class q extends b {
|
|
|
198
224
|
_getBlockElement(t) {
|
|
199
225
|
if ("getAttribute" in t) {
|
|
200
226
|
const e = t.getAttribute("class");
|
|
201
|
-
if (e && e.includes(
|
|
227
|
+
if (e && e.includes(m))
|
|
202
228
|
return t;
|
|
203
229
|
}
|
|
204
|
-
return "querySelector" in t ? t.querySelector(`.${
|
|
230
|
+
return "querySelector" in t ? t.querySelector(`.${m}`) ?? null : null;
|
|
205
231
|
}
|
|
206
232
|
/**
|
|
207
233
|
* Migrate configuration from legacy format
|
|
@@ -211,6 +237,6 @@ class q extends b {
|
|
|
211
237
|
}
|
|
212
238
|
}
|
|
213
239
|
export {
|
|
214
|
-
|
|
240
|
+
k as BLOCK_ID,
|
|
215
241
|
q as RecommendationBlock
|
|
216
242
|
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { getRecommendationFeedSourceMaps as
|
|
1
|
+
import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as C } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
2
|
import { useRecommendationApi as y } from "../../../../services/recommendationApi.js";
|
|
3
3
|
import { useConfigStore as G } from "../../../../stores/config.js";
|
|
4
4
|
import { defineStore as P } from "pinia";
|
|
5
|
-
import { DEFAULT_CARDS_IN_ROW as
|
|
6
|
-
import { EXCLUDED_ALGORITHM_IDS as
|
|
7
|
-
import { getDefaultProducts as
|
|
5
|
+
import { DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
|
|
6
|
+
import { EXCLUDED_ALGORITHM_IDS as D } from "../constants/defaultConfig.js";
|
|
7
|
+
import { getDefaultProducts as S } from "../templates/utils.js";
|
|
8
8
|
import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
|
|
9
|
-
import { isFilterValid as
|
|
10
|
-
import { isConfigValid as
|
|
9
|
+
import { isFilterValid as v } from "../validation/filterSchema.js";
|
|
10
|
+
import { isConfigValid as w } from "../validation/requiredFields.js";
|
|
11
11
|
const h = y();
|
|
12
12
|
let m = null, u = null, d = null;
|
|
13
|
-
function
|
|
13
|
+
function I() {
|
|
14
14
|
return {
|
|
15
|
-
cardsInRow:
|
|
15
|
+
cardsInRow: F,
|
|
16
16
|
currencySettings: {
|
|
17
17
|
name: "USD",
|
|
18
18
|
value: "USD",
|
|
@@ -38,9 +38,9 @@ function k() {
|
|
|
38
38
|
customAttributes: []
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
function
|
|
41
|
+
function k() {
|
|
42
42
|
return {
|
|
43
|
-
recommendationConfigs:
|
|
43
|
+
recommendationConfigs: I(),
|
|
44
44
|
recommendationProducts: [],
|
|
45
45
|
filterStatus: !1,
|
|
46
46
|
filterSelectionDrawerStatus: !1,
|
|
@@ -69,7 +69,7 @@ const N = () => ({
|
|
|
69
69
|
* This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
|
|
70
70
|
*/
|
|
71
71
|
recommendationConfigs(t) {
|
|
72
|
-
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs :
|
|
72
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : I();
|
|
73
73
|
},
|
|
74
74
|
/**
|
|
75
75
|
* Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
|
|
@@ -114,8 +114,8 @@ const N = () => ({
|
|
|
114
114
|
return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
|
|
115
115
|
},
|
|
116
116
|
getActivePredictiveAlgorithms: (t) => {
|
|
117
|
-
const e =
|
|
118
|
-
return t.activePredictiveAlgorithms.filter((n) => !
|
|
117
|
+
const e = g(), r = [];
|
|
118
|
+
return t.activePredictiveAlgorithms.filter((n) => !D.includes(n)).forEach((n) => {
|
|
119
119
|
r.push(...e.filter((c) => c.id === n));
|
|
120
120
|
}), r.map((n) => ({
|
|
121
121
|
text: n.name,
|
|
@@ -155,7 +155,7 @@ const N = () => ({
|
|
|
155
155
|
setCurrentBlock(t) {
|
|
156
156
|
this.blockStates[t] || (this.blockStates = {
|
|
157
157
|
...this.blockStates,
|
|
158
|
-
[t]:
|
|
158
|
+
[t]: k()
|
|
159
159
|
}), this.currentRecommendationId = t;
|
|
160
160
|
},
|
|
161
161
|
/**
|
|
@@ -175,31 +175,22 @@ const N = () => ({
|
|
|
175
175
|
}
|
|
176
176
|
},
|
|
177
177
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* without re-fetching from the API (SD-142352). No-op when the source
|
|
181
|
-
* was never selected (no entry in blockStates) — the caller falls back
|
|
182
|
-
* to setCurrentBlock's default initialization.
|
|
178
|
+
* Deep-clones a block's state and mirrors its campaign URL under a new id.
|
|
179
|
+
* Invoked from `onCreated` when a duplicate is detected (SD-142352).
|
|
183
180
|
*/
|
|
184
181
|
cloneBlockState(t, e) {
|
|
185
182
|
const r = this.blockStates[t];
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
...
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
recommendedProducts: [...r.recommendationConfigs.recommendedProducts],
|
|
198
|
-
currencySettings: { ...r.recommendationConfigs.currencySettings }
|
|
199
|
-
},
|
|
200
|
-
filterSnapshot: null,
|
|
201
|
-
filterSelectionDrawerStatus: !1
|
|
202
|
-
}
|
|
183
|
+
if (r) {
|
|
184
|
+
const o = structuredClone(r);
|
|
185
|
+
o.recommendationConfigs.id = e, this.blockStates = {
|
|
186
|
+
...this.blockStates,
|
|
187
|
+
[e]: o
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const n = t.toString(), c = e.toString(), i = this.recommendationCampaignUrls[n];
|
|
191
|
+
i && (this.recommendationCampaignUrls = {
|
|
192
|
+
...this.recommendationCampaignUrls,
|
|
193
|
+
[c]: i
|
|
203
194
|
});
|
|
204
195
|
},
|
|
205
196
|
/**
|
|
@@ -232,7 +223,7 @@ const N = () => ({
|
|
|
232
223
|
decimalCount: String(e.currencyDecimalCount),
|
|
233
224
|
decimalSeparator: r(e.currencyDecimalSeparator, ","),
|
|
234
225
|
thousandSeparator: r(e.currencyThousandSeparator, ".")
|
|
235
|
-
}, c = !this.blockStates[t], i = c ?
|
|
226
|
+
}, c = !this.blockStates[t], i = c ? k() : this.blockStates[t];
|
|
236
227
|
i.recommendationConfigs = {
|
|
237
228
|
...i.recommendationConfigs,
|
|
238
229
|
strategy: e.strategy,
|
|
@@ -387,7 +378,7 @@ const N = () => ({
|
|
|
387
378
|
const n = [...e.recommendationConfigs.filters];
|
|
388
379
|
n[r] = {
|
|
389
380
|
...t,
|
|
390
|
-
isValid:
|
|
381
|
+
isValid: v(t)
|
|
391
382
|
}, e.recommendationConfigs.filters = n;
|
|
392
383
|
}
|
|
393
384
|
},
|
|
@@ -433,7 +424,7 @@ const N = () => ({
|
|
|
433
424
|
* every block's recommendationConfigs across user edits.
|
|
434
425
|
*/
|
|
435
426
|
hasInvalidBlock() {
|
|
436
|
-
return Object.values(this.blockStates).some((t) => !
|
|
427
|
+
return Object.values(this.blockStates).some((t) => !w(t.recommendationConfigs, this));
|
|
437
428
|
},
|
|
438
429
|
// ====================================================================
|
|
439
430
|
// Per-Block Product Fetching
|
|
@@ -454,7 +445,7 @@ const N = () => ({
|
|
|
454
445
|
},
|
|
455
446
|
async _doFetchProducts() {
|
|
456
447
|
var p;
|
|
457
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p =
|
|
448
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = g().find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
|
|
458
449
|
locale: r.language,
|
|
459
450
|
currency: r.currencySettings.value,
|
|
460
451
|
partnerName: o.partnerName,
|
|
@@ -470,10 +461,10 @@ const N = () => ({
|
|
|
470
461
|
f = [];
|
|
471
462
|
}
|
|
472
463
|
if (this.blockStates[t]) {
|
|
473
|
-
const l = f.length > 0 ? f :
|
|
464
|
+
const l = f.length > 0 ? f : S(s);
|
|
474
465
|
l.length < s ? this.blockStates[t].recommendationProducts = [
|
|
475
466
|
...l,
|
|
476
|
-
...
|
|
467
|
+
...S(s - l.length)
|
|
477
468
|
] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
|
|
478
469
|
}
|
|
479
470
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Supports multiple block instances with unique recommendation-id attributes.
|
|
7
7
|
*/
|
|
8
8
|
import type { BlockId } from '@@/Types/extensions/block';
|
|
9
|
-
import type {
|
|
9
|
+
import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
|
|
10
10
|
import { Block, BlockCompositionType } from '@stripoinc/ui-editor-extensions';
|
|
11
11
|
export declare const BLOCK_ID: BlockId;
|
|
12
12
|
export declare class RecommendationBlock extends Block {
|
|
@@ -55,24 +55,21 @@ export declare class RecommendationBlock extends Block {
|
|
|
55
55
|
* @param node - The block node being deleted
|
|
56
56
|
*/
|
|
57
57
|
onDelete(node: ImmutableHtmlNode): void;
|
|
58
|
-
/**
|
|
59
|
-
* Lifecycle hook fired when Stripo's UI Duplicate button copies a block.
|
|
60
|
-
* Stripo passes an `HtmlNodeModifier` already targeting the cloned node;
|
|
61
|
-
* its mutations are committed when this method returns. We stamp a fresh
|
|
62
|
-
* `recommendation-id` on the clone and seed its blockStates entry from
|
|
63
|
-
* the source so the duplicate enters the DOM as a fully independent block
|
|
64
|
-
* — no shared id, no shared store, no API refetch flash.
|
|
65
|
-
*
|
|
66
|
-
* Stripo does NOT fire `onCreated` for blocks created via duplicate
|
|
67
|
-
* (per Block.d.ts:54-69 — `onCreated` is drag-and-drop only), so this
|
|
68
|
-
* hook is the sole production path for clone id assignment.
|
|
69
|
-
*/
|
|
70
|
-
onCopy(modifier: HtmlNodeModifier): void;
|
|
71
58
|
/**
|
|
72
59
|
* Generates the next unique recommendation ID by scanning all existing blocks
|
|
73
60
|
* in the document and finding the maximum existing ID + 1.
|
|
74
61
|
*/
|
|
75
62
|
private _generateNextId;
|
|
63
|
+
/** True when another block already holds the same id — Stripo's duplication signal. */
|
|
64
|
+
private _detectDuplicate;
|
|
65
|
+
/** Assigns a fresh id to a duplicated block and syncs DOM, node config and store. */
|
|
66
|
+
private _handleDuplicate;
|
|
67
|
+
/**
|
|
68
|
+
* Rewrites `ins-recommendation-v3-block-{id}` on the clone — the instance
|
|
69
|
+
* class scopes per-block CSS rules, so leaving the source's class would
|
|
70
|
+
* make stylistic edits leak between source and clone.
|
|
71
|
+
*/
|
|
72
|
+
private _reassignInstanceClass;
|
|
76
73
|
/**
|
|
77
74
|
* Assigns a recommendation-id attribute to the block element.
|
|
78
75
|
* The instance class (ins-recommendation-v3-block-{id}) is embedded in the template
|
|
@@ -217,13 +217,10 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
|
|
|
217
217
|
*/
|
|
218
218
|
removeBlockState(id: number): void;
|
|
219
219
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* without re-fetching from the API (SD-142352). No-op when the source
|
|
223
|
-
* was never selected (no entry in blockStates) — the caller falls back
|
|
224
|
-
* to setCurrentBlock's default initialization.
|
|
220
|
+
* Deep-clones a block's state and mirrors its campaign URL under a new id.
|
|
221
|
+
* Invoked from `onCreated` when a duplicate is detected (SD-142352).
|
|
225
222
|
*/
|
|
226
|
-
cloneBlockState(
|
|
223
|
+
cloneBlockState(sourceId: number, newId: number): void;
|
|
227
224
|
/**
|
|
228
225
|
* Marks a block as initialized (initial API data has been fetched).
|
|
229
226
|
* Automatically cleaned up when removeBlockState deletes the block entry.
|
|
@@ -1,106 +1,101 @@
|
|
|
1
|
-
import { useActionsApi as
|
|
2
|
-
import { useHtmlCompiler as
|
|
3
|
-
import { DEFAULT_CURRENCY as
|
|
4
|
-
import { BLOCK_ROOT_SELECTOR as R, CONTAINER_SELECTOR as N } from "../extensions/Blocks/Recommendation/constants/selectors.js";
|
|
1
|
+
import { useActionsApi as C } from "../composables/useActionsApi.js";
|
|
2
|
+
import { useHtmlCompiler as T } from "../composables/useHtmlCompiler.js";
|
|
3
|
+
import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as s } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
|
|
5
4
|
import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
6
|
-
import { DATA_ATTRIBUTES as
|
|
7
|
-
import { parsePageList as
|
|
8
|
-
import { useDynamicContentStore as
|
|
9
|
-
import { useUnsubscribeStore as
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
return
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
}),
|
|
5
|
+
import { DATA_ATTRIBUTES as S } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
|
|
6
|
+
import { parsePageList as D } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
|
|
7
|
+
import { useDynamicContentStore as P } from "../stores/dynamic-content.js";
|
|
8
|
+
import { useUnsubscribeStore as w } from "../stores/unsubscribe.js";
|
|
9
|
+
function E(i, o) {
|
|
10
|
+
const t = new DOMParser().parseFromString(i, "text/html").querySelectorAll(`[${S.PAGE_LIST}]`), c = [];
|
|
11
|
+
return t.forEach((r) => {
|
|
12
|
+
const a = r.getAttribute(S.PAGE_LIST);
|
|
13
|
+
a && c.push(...D(a));
|
|
14
|
+
}), o.filter((r) => c.includes(r));
|
|
16
15
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return m.querySelectorAll(R).forEach((o) => {
|
|
21
|
-
if (!o.querySelector(N))
|
|
22
|
-
return;
|
|
23
|
-
const e = Number(o.getAttribute("recommendation-id"));
|
|
24
|
-
Number.isFinite(e) && e > 0 && n.add(e);
|
|
25
|
-
}), n;
|
|
26
|
-
}
|
|
27
|
-
function x(s) {
|
|
28
|
-
const a = new DOMParser().parseFromString(s, "text/html").querySelectorAll(".recommendation-block-v2");
|
|
29
|
-
if (a.length === 0)
|
|
16
|
+
async function U(i) {
|
|
17
|
+
const m = new DOMParser().parseFromString(i, "text/html").querySelectorAll(".recommendation-block-v2");
|
|
18
|
+
if (m.length === 0)
|
|
30
19
|
return;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
var
|
|
34
|
-
const c =
|
|
20
|
+
const l = b();
|
|
21
|
+
m.forEach((t) => {
|
|
22
|
+
var g, f, u, n, y;
|
|
23
|
+
const c = t.getAttribute("recommendation-id"), r = c ? Number(c) : NaN;
|
|
35
24
|
if (!Number.isFinite(r))
|
|
36
25
|
return;
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
26
|
+
const a = t.getAttribute("esd-ext-config");
|
|
27
|
+
if (!a)
|
|
39
28
|
return;
|
|
40
29
|
let e;
|
|
41
30
|
try {
|
|
42
|
-
e = JSON.parse(
|
|
31
|
+
e = JSON.parse(a);
|
|
43
32
|
} catch {
|
|
44
33
|
return;
|
|
45
34
|
}
|
|
46
35
|
if (!e || typeof e != "object" || Array.isArray(e))
|
|
47
36
|
return;
|
|
48
|
-
const
|
|
49
|
-
strategy: e.strategy ??
|
|
50
|
-
language: e.language ??
|
|
51
|
-
size: e.size ??
|
|
37
|
+
const p = {
|
|
38
|
+
strategy: e.strategy ?? s.strategy,
|
|
39
|
+
language: e.language ?? s.language,
|
|
40
|
+
size: e.size ?? s.size,
|
|
52
41
|
// Spread the default arrays so each block gets a fresh reference
|
|
53
42
|
// instead of sharing the singleton in DEFAULT_NODE_CONFIG.
|
|
54
|
-
productIds: e.productIds ?? [...
|
|
55
|
-
filters: e.filters ?? [...
|
|
56
|
-
shuffleProducts: e.shuffleProducts ??
|
|
57
|
-
currencyCode: ((
|
|
58
|
-
currencyAlignment: ((
|
|
59
|
-
currencyDecimalCount: ((
|
|
60
|
-
currencyDecimalSeparator: ((
|
|
61
|
-
currencyThousandSeparator: ((
|
|
43
|
+
productIds: e.productIds ?? [...s.productIds],
|
|
44
|
+
filters: e.filters ?? [...s.filters],
|
|
45
|
+
shuffleProducts: e.shuffleProducts ?? s.shuffleProducts,
|
|
46
|
+
currencyCode: ((g = e.currency) == null ? void 0 : g.code) ?? d.code,
|
|
47
|
+
currencyAlignment: ((f = e.currency) == null ? void 0 : f.alignment) ?? d.alignment,
|
|
48
|
+
currencyDecimalCount: ((u = e.currency) == null ? void 0 : u.decimalCount) ?? d.decimalCount,
|
|
49
|
+
currencyDecimalSeparator: ((n = e.currency) == null ? void 0 : n.decimalSeparator) ?? d.decimalSeparator,
|
|
50
|
+
currencyThousandSeparator: ((y = e.currency) == null ? void 0 : y.thousandSeparator) ?? d.thousandSeparator
|
|
62
51
|
};
|
|
63
|
-
|
|
52
|
+
l.seedBlockUrlConfig(r, p);
|
|
64
53
|
});
|
|
54
|
+
try {
|
|
55
|
+
await l.fetchRecommendationCreateData();
|
|
56
|
+
} catch (t) {
|
|
57
|
+
console.warn(
|
|
58
|
+
"Recommendation reference data pre-load failed; validator will skip the availability check.",
|
|
59
|
+
t
|
|
60
|
+
);
|
|
61
|
+
}
|
|
65
62
|
}
|
|
66
|
-
const
|
|
67
|
-
const
|
|
63
|
+
const _ = () => {
|
|
64
|
+
const i = P(), o = w(), { getCompiledEmail: m, getTemplateData: l } = C(), { compileHtml: t } = T();
|
|
68
65
|
return {
|
|
69
66
|
prepareTemplateDetails: async () => {
|
|
70
|
-
const { html: r, ampHtml:
|
|
67
|
+
const { html: r, ampHtml: a = "", ampErrors: e = [] } = await m({
|
|
71
68
|
minimize: !0,
|
|
72
69
|
resetDataSavedFlag: !1
|
|
73
|
-
}), { html:
|
|
74
|
-
|
|
75
|
-
const { compiledHtml:
|
|
76
|
-
Object.entries(A.recommendationCampaignUrls).filter(([D]) => T.has(Number(D)))
|
|
77
|
-
);
|
|
70
|
+
}), { html: p, css: g, syncModulesIds: f = [] } = await l();
|
|
71
|
+
o.selectedUnsubscribePages.length && await o.fetchTemplates(), await U(p);
|
|
72
|
+
const { compiledHtml: u, stats: n, appliedRules: y } = t(r), h = i.getSelectedDynamicContentList, A = b();
|
|
78
73
|
return console.debug("HTML Compilation Stats:", {
|
|
79
|
-
originalSize:
|
|
80
|
-
compiledSize:
|
|
81
|
-
reduction: `${
|
|
82
|
-
appliedRules:
|
|
83
|
-
executionTime: `${
|
|
74
|
+
originalSize: n.originalSize,
|
|
75
|
+
compiledSize: n.compiledSize,
|
|
76
|
+
reduction: `${n.reductionPercentage.toFixed(2)}%`,
|
|
77
|
+
appliedRules: y,
|
|
78
|
+
executionTime: `${n.executionTime.toFixed(2)}ms`
|
|
84
79
|
}), {
|
|
85
80
|
dynamicContentList: h,
|
|
86
|
-
compiledHtml:
|
|
87
|
-
rawHtml:
|
|
88
|
-
css:
|
|
89
|
-
ampHtml:
|
|
81
|
+
compiledHtml: u,
|
|
82
|
+
rawHtml: p,
|
|
83
|
+
css: g,
|
|
84
|
+
ampHtml: a,
|
|
90
85
|
ampErrors: e,
|
|
91
|
-
modules:
|
|
86
|
+
modules: f.map(Number),
|
|
92
87
|
recommendation: {
|
|
93
|
-
campaignUrls:
|
|
88
|
+
campaignUrls: A.recommendationCampaignUrls,
|
|
94
89
|
configs: {}
|
|
95
90
|
},
|
|
96
91
|
unsubscribe: {
|
|
97
|
-
status:
|
|
98
|
-
config:
|
|
92
|
+
status: o.unsubscribePagesStatus,
|
|
93
|
+
config: E(u, o.selectedUnsubscribePages)
|
|
99
94
|
}
|
|
100
95
|
};
|
|
101
96
|
}
|
|
102
97
|
};
|
|
103
98
|
};
|
|
104
99
|
export {
|
|
105
|
-
|
|
100
|
+
_ as useTemplatePreparation
|
|
106
101
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useinsider/guido",
|
|
3
|
-
"version": "3.4.1-beta.
|
|
3
|
+
"version": "3.4.1-beta.8f6f02b",
|
|
4
4
|
"description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
|
|
5
5
|
"main": "./dist/guido.umd.cjs",
|
|
6
6
|
"module": "./dist/library.js",
|