@useinsider/guido 3.2.0-beta.8e8f902 → 3.2.0-beta.8f147c3

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 (72) hide show
  1. package/README.md +1 -25
  2. package/dist/@types/config/schemas.js +163 -109
  3. package/dist/components/Guido.vue.js +4 -4
  4. package/dist/components/Guido.vue2.js +77 -77
  5. package/dist/components/organisms/header/RightSlot.vue.js +8 -8
  6. package/dist/components/organisms/header/RightSlot.vue2.js +8 -9
  7. package/dist/composables/useSave.js +15 -15
  8. package/dist/config/migrator/index.js +9 -9
  9. package/dist/config/migrator/recommendation/compositionMapper.js +98 -0
  10. package/dist/config/migrator/recommendation/extractors.js +27 -0
  11. package/dist/config/migrator/recommendation/htmlBuilder.js +486 -0
  12. package/dist/config/migrator/recommendation/parseLegacyConfig.js +33 -0
  13. package/dist/config/migrator/recommendation/settingsMapper.js +78 -0
  14. package/dist/config/migrator/recommendation/themeMapper.js +93 -0
  15. package/dist/config/migrator/recommendationMigrator.js +60 -290
  16. package/dist/enums/extensions/recommendationBlock.js +2 -1
  17. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +5 -5
  18. package/dist/extensions/Blocks/Recommendation/services/configService.js +65 -29
  19. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +8 -8
  20. package/dist/extensions/Blocks/Recommendation/templates/utils.js +1 -1
  21. package/dist/extensions/Blocks/Recommendation/utils/preserveTextStyles.js +13 -22
  22. package/dist/guido.css +1 -1
  23. package/dist/node_modules/valibot/dist/index.js +450 -235
  24. package/dist/services/templateLibraryApi.js +4 -5
  25. package/dist/src/@types/config/defaults.d.ts +5 -1
  26. package/dist/src/@types/config/index.d.ts +3 -3
  27. package/dist/src/@types/config/schemas.d.ts +213 -4
  28. package/dist/src/@types/config/types.d.ts +9 -1
  29. package/dist/src/components/Guido.vue.d.ts +1 -1
  30. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  31. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
  32. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
  33. package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
  34. package/dist/src/composables/useConfig.d.ts +56 -2
  35. package/dist/src/composables/useSave.d.ts +1 -1
  36. package/dist/src/config/migrator/index.d.ts +2 -1
  37. package/dist/src/config/migrator/recommendation/compositionMapper.d.ts +2 -0
  38. package/dist/src/config/migrator/recommendation/compositionMapper.test.d.ts +1 -0
  39. package/dist/src/config/migrator/recommendation/extractors.d.ts +7 -0
  40. package/dist/src/config/migrator/recommendation/htmlBuilder.d.ts +11 -0
  41. package/dist/src/config/migrator/recommendation/parseLegacyConfig.d.ts +15 -0
  42. package/dist/src/config/migrator/recommendation/parseLegacyConfig.test.d.ts +1 -0
  43. package/dist/src/config/migrator/recommendation/settingsMapper.d.ts +7 -0
  44. package/dist/src/config/migrator/recommendation/settingsMapper.test.d.ts +1 -0
  45. package/dist/src/config/migrator/recommendation/themeMapper.d.ts +5 -0
  46. package/dist/src/config/migrator/recommendation/themeMapper.test.d.ts +1 -0
  47. package/dist/src/config/migrator/recommendation/types.d.ts +205 -0
  48. package/dist/src/config/migrator/recommendationMigrator.d.ts +13 -1
  49. package/dist/src/config/migrator/recommendationMigrator.test.d.ts +1 -0
  50. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +10 -0
  51. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +1 -1
  52. package/dist/src/extensions/Blocks/Recommendation/utils/preserveTextStyles.d.ts +0 -3
  53. package/dist/src/stores/config.d.ts +504 -18
  54. package/dist/src/stores/editor.d.ts +0 -23
  55. package/dist/src/utils/htmlEscape.d.ts +5 -0
  56. package/dist/stores/editor.js +1 -3
  57. package/dist/utils/htmlEscape.js +13 -0
  58. package/dist/utils/pairProductVariables.js +88 -89
  59. package/package.json +5 -1
  60. package/dist/components/organisms/AutoSaveController.vue.js +0 -17
  61. package/dist/components/organisms/AutoSaveController.vue2.js +0 -13
  62. package/dist/components/organisms/header/AutoSaveToggle.vue.js +0 -22
  63. package/dist/components/organisms/header/AutoSaveToggle.vue2.js +0 -19
  64. package/dist/composables/useAutoSave.js +0 -71
  65. package/dist/extensions/Blocks/Recommendation/templates/grid/migration.js +0 -251
  66. package/dist/src/components/organisms/AutoSaveController.vue.d.ts +0 -2
  67. package/dist/src/components/organisms/header/AutoSaveToggle.vue.d.ts +0 -2
  68. package/dist/src/composables/useAutoSave.d.ts +0 -3
  69. package/dist/src/stores/autosave.d.ts +0 -12
  70. package/dist/src/utils/timeUtil.d.ts +0 -8
  71. package/dist/stores/autosave.js +0 -17
  72. package/dist/utils/timeUtil.js +0 -19
@@ -0,0 +1,93 @@
1
+ function r(n, t, o) {
2
+ o != null && o !== "" && n.push(`${t}: ${o}`);
3
+ }
4
+ function $(n) {
5
+ if (!n)
6
+ return null;
7
+ const t = n.top ?? n.all ?? 0, o = n.right ?? n.all ?? 0, i = n.bottom ?? n.all ?? 0, l = n.left ?? n.all ?? 0;
8
+ return t === 0 && o === 0 && i === 0 && l === 0 ? null : `${t}px ${o}px ${i}px ${l}px`;
9
+ }
10
+ function j(n) {
11
+ if (!n)
12
+ return null;
13
+ const { top: t, right: o, bottom: i, left: l } = n;
14
+ return t === void 0 ? null : t === o && t === i && t === l ? t : null;
15
+ }
16
+ function S(n, t, o, i) {
17
+ const l = j(o);
18
+ if (l !== null) {
19
+ r(n, `border-${t}`, i(l));
20
+ return;
21
+ }
22
+ o && (r(n, `border-top-${t}`, o.top !== void 0 ? i(o.top) : null), r(n, `border-right-${t}`, o.right !== void 0 ? i(o.right) : null), r(n, `border-bottom-${t}`, o.bottom !== void 0 ? i(o.bottom) : null), r(n, `border-left-${t}`, o.left !== void 0 ? i(o.left) : null));
23
+ }
24
+ function C(n) {
25
+ if (!n)
26
+ return null;
27
+ if (typeof n.radius == "number")
28
+ return `${n.radius}px`;
29
+ if (typeof n.lt == "number" || typeof n.rt == "number" || typeof n.rb == "number" || typeof n.lb == "number") {
30
+ const t = n.lt ?? 0, o = n.rt ?? 0, i = n.rb ?? 0, l = n.lb ?? 0;
31
+ return `${t}px ${o}px ${i}px ${l}px`;
32
+ }
33
+ return null;
34
+ }
35
+ function I(n) {
36
+ var t, o;
37
+ return ((t = n.textLineSpacing) == null ? void 0 : t.lineSpacing) ?? ((o = n.textLineSpacing) == null ? void 0 : o["font-lineSpacing"]);
38
+ }
39
+ function L(n) {
40
+ var f, u, a, g, d, b, p, s;
41
+ const t = [], o = [], i = (f = n == null ? void 0 : n.textAlign) == null ? void 0 : f.align, l = $((u = n == null ? void 0 : n.textPadding) == null ? void 0 : u.desktop), x = (a = n == null ? void 0 : n.textStyle) == null ? void 0 : a["font-weight"], c = (g = n == null ? void 0 : n.textStyle) == null ? void 0 : g["font-style"];
42
+ return r(t, "background-color", (d = n == null ? void 0 : n.textBackgroundColor) == null ? void 0 : d.color), r(t, "padding", l), r(o, "font-family", (b = n == null ? void 0 : n.textStyle) == null ? void 0 : b["font-family"]), r(o, "font-size", (p = n == null ? void 0 : n.textStyle) == null ? void 0 : p["font-size"]), r(o, "font-weight", x), r(o, "font-style", c), r(o, "color", (s = n == null ? void 0 : n.textColor) == null ? void 0 : s.color), r(o, "line-height", I(n ?? {})), r(o, "text-align", i), {
43
+ tdStyle: t.join("; "),
44
+ pStyle: o.join("; "),
45
+ align: i || void 0,
46
+ wrapBold: x === "bold",
47
+ wrapItalic: c === "italic",
48
+ textTrimming: (n == null ? void 0 : n.textTrimming) === !0
49
+ };
50
+ }
51
+ function N(n) {
52
+ var u, a, g, d, b, p, s, k, w, P, B, z;
53
+ const t = [], o = [], i = [];
54
+ r(t, "padding", $((u = n == null ? void 0 : n.buttonExternalPadding) == null ? void 0 : u.desktop)), r(i, "padding", $((a = n == null ? void 0 : n.buttonInternalPadding) == null ? void 0 : a.desktop)), r(i, "font-family", (g = n == null ? void 0 : n.buttonStyle) == null ? void 0 : g["font-family"]), r(i, "font-size", (d = n == null ? void 0 : n.buttonStyle) == null ? void 0 : d["font-size"]), r(i, "font-weight", (b = n == null ? void 0 : n.buttonStyle) == null ? void 0 : b["font-weight"]), r(i, "font-style", (p = n == null ? void 0 : n.buttonStyle) == null ? void 0 : p["font-style"]);
55
+ const l = (s = n == null ? void 0 : n.buttonColor) == null ? void 0 : s.color, x = (k = n == null ? void 0 : n.textButtonColor) == null ? void 0 : k.color;
56
+ r(o, "background", l), r(i, "background", l), r(i, "color", x);
57
+ const c = n == null ? void 0 : n.buttonBorder;
58
+ S(o, "width", c == null ? void 0 : c.width, (y) => `${y}px`), S(o, "style", c == null ? void 0 : c.style, (y) => y), S(o, "color", c == null ? void 0 : c.color, (y) => y);
59
+ const f = C(n == null ? void 0 : n.buttonBorderRadius);
60
+ return f && (r(o, "border-radius", f), r(i, "border-radius", f)), ((w = n == null ? void 0 : n.buttonStretch) == null ? void 0 : w.stretched) === !1 ? (r(o, "display", "inline-block"), r(i, "display", "inline-block")) : ((P = n == null ? void 0 : n.buttonStretch) == null ? void 0 : P.stretched) === !0 && (r(o, "display", "block"), r(i, "display", "block")), {
61
+ tdStyle: t.join("; "),
62
+ align: ((B = n == null ? void 0 : n.buttonAlign) == null ? void 0 : B.align) || void 0,
63
+ spanStyle: o.join("; "),
64
+ aStyle: i.join("; "),
65
+ text: (z = n == null ? void 0 : n.buttonText) == null ? void 0 : z.text
66
+ };
67
+ }
68
+ function T(n) {
69
+ if (typeof n == "number" && Number.isFinite(n) && n >= 0)
70
+ return Math.trunc(n);
71
+ if (typeof n == "string" && /^\d+$/.test(n.trim())) {
72
+ const t = Number(n.trim());
73
+ if (Number.isFinite(t) && t >= 0)
74
+ return t;
75
+ }
76
+ }
77
+ function A(n) {
78
+ var t, o;
79
+ return {
80
+ width: T((t = n == null ? void 0 : n.imageSize) == null ? void 0 : t.width),
81
+ height: T((o = n == null ? void 0 : n.imageSize) == null ? void 0 : o.height)
82
+ };
83
+ }
84
+ function F(n, t) {
85
+ const o = (n ?? "").trim().replace(/;$/, "").trim(), i = (t ?? "").trim().replace(/;$/, "").trim();
86
+ return o ? i ? `${o}; ${i}` : o : i;
87
+ }
88
+ export {
89
+ F as appendStyle,
90
+ N as mapButtonTheme,
91
+ A as mapImageTheme,
92
+ L as mapTextTheme
93
+ };
@@ -1,297 +1,67 @@
1
- var h = Object.defineProperty;
2
- var T = (d, e, t) => e in d ? h(d, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[e] = t;
3
- var A = (d, e, t) => T(d, typeof e != "symbol" ? e + "" : e, t);
4
- import { BLOCK_ID as S } from "../../extensions/Blocks/Recommendation/block.js";
5
- import P, { prepareProductRows as q } from "../../extensions/Blocks/Recommendation/templates/grid/migration.js";
6
- import { useTemplateStore as w } from "../../stores/template.js";
7
- import { getDefaultProducts as $ } from "../../extensions/Blocks/Recommendation/templates/utils.js";
8
- class B {
9
- constructor() {
10
- A(this, "parser");
11
- this.parser = new DOMParser();
12
- }
13
- migrate(e) {
14
- try {
15
- const t = this.parser.parseFromString(e, "text/html"), s = t.querySelectorAll(
16
- 'td.ins-recommendation-v3-block-1:not(.recommendation-block-v2), td.product-block[esd-handler-name*="EmailRecommendationV3"]:not(.recommendation-block-v2)'
17
- );
18
- return w().$patch((i) => {
19
- i.migrations = { ...i.migrations, [S]: s.length };
20
- }), s.length === 0 ? e : (s.forEach((i) => {
21
- const r = i.getAttribute("id"), n = this.extractBgColor(i), u = this.extractTitle(i), c = this.extractProductRows(i), l = P.replace("{-{-TITLE-}-}", u).replace("{-{-PRODUCT_ROWS-}-}", c), a = this.parser.parseFromString(
22
- `<table id="tempDoc"><tbody><tr>${l}</tr></tbody></table>`,
23
- "text/html"
24
- ).querySelector(".recommendation-block-v2");
25
- a && i.parentNode && (r && a.setAttribute("id", r), n && a.setAttribute("bgcolor", n), i.parentNode.replaceChild(a, i));
26
- }), t.documentElement.outerHTML);
27
- } catch (t) {
28
- return console.error("RecommendationMigrator failed:", t), e;
29
- }
30
- }
31
- /**
32
- * Extracts background color from old block
33
- * @param block - The old recommendation block element
34
- * @returns The background color value or empty string
35
- */
36
- extractBgColor(e) {
37
- const t = e.getAttribute("bgcolor");
38
- if (t)
39
- return t.trim();
40
- const s = e.getAttribute("style");
41
- if (s) {
42
- const o = s.match(/background-color\s*:\s*([^;]+)/i);
43
- if (o && o[1])
44
- return o[1].trim();
45
- }
46
- return "";
47
- }
48
- /**
49
- * Extracts background color from product card wrapper element
50
- * @param cardElement - The product card TD element
51
- * @returns The background color value or empty string
52
- */
53
- extractCardBgColor(e) {
54
- const t = e.getAttribute("bgcolor");
55
- if (t)
56
- return t.trim();
57
- const s = e.getAttribute("style");
58
- if (s) {
59
- const o = s.match(/background-color\s*:\s*([^;]+)/i);
60
- if (o && o[1])
61
- return o[1].trim();
62
- }
63
- return "";
64
- }
65
- /**
66
- * Extracts title from old block and converts to new structure
67
- * @param block - The old recommendation block element
68
- * @returns HTML string for the title block
69
- */
70
- extractTitle(e) {
71
- var g, a;
72
- const t = e.querySelector(".ext-recommendation-title");
73
- if (!t)
74
- return this.buildTitleBlock({
75
- text: "You May Also Like!",
76
- isBold: !0,
77
- isItalic: !1,
78
- align: "center",
79
- styles: "font-size: 28px; color: #333333;"
80
- });
81
- const s = t.querySelector("p");
82
- if (!s) {
83
- const p = ((g = t.textContent) == null ? void 0 : g.trim()) || "You May Also Like!", b = t.getAttribute("align") || "center";
84
- return this.buildTitleBlock({
85
- text: p,
86
- isBold: !0,
87
- isItalic: !1,
88
- align: b,
89
- styles: "font-size: 28px; color: #333333;"
90
- });
91
- }
92
- const o = ((a = s.textContent) == null ? void 0 : a.trim()) || "You May Also Like!", i = t.getAttribute("align") || s.getAttribute("align") || "center", r = s.getAttribute("style") || "", n = /font-weight\s*:\s*bold/i.test(r) || !!s.querySelector("b, strong"), u = /font-style\s*:\s*italic/i.test(r) || !!s.querySelector("i, em"), c = this.removeStyleProperties(r, ["font-weight", "font-style"]), l = this.convertInlineToBlock(c);
93
- return this.buildTitleBlock({
94
- text: o,
95
- isBold: n,
96
- isItalic: u,
97
- align: i,
98
- styles: l
1
+ import { BlockId as f } from "../../enums/block.js";
2
+ import { useTemplateStore as u } from "../../stores/template.js";
3
+ import { mapComposition as b } from "./recommendation/compositionMapper.js";
4
+ import { extractTitleText as y, extractCardBgColor as v } from "./recommendation/extractors.js";
5
+ import { buildBlockHtml as C } from "./recommendation/htmlBuilder.js";
6
+ import { parseRecommendationId as R, parseLegacyConfig as S } from "./recommendation/parseLegacyConfig.js";
7
+ import { mapSettings as w } from "./recommendation/settingsMapper.js";
8
+ const x = 'td[esd-dev-product-config][class*="ins-recommendation-v3-block-"]:not(.recommendation-block-v2), td.product-block[esd-handler-name*="EmailRecommendationV3"][esd-dev-product-config]:not(.recommendation-block-v2)';
9
+ function M(i, o, c) {
10
+ var s;
11
+ const t = R(o);
12
+ if (t === null)
13
+ return console.warn(
14
+ "[RecommendationMigrator] Skipping block — missing/invalid `ins-recommendation-v3-campaign-id`"
15
+ ), !1;
16
+ const r = S(o);
17
+ if (!r)
18
+ return console.warn(
19
+ `[RecommendationMigrator] Aborting block id=${t} \`esd-dev-product-config\` missing or unable to be parsed`
20
+ ), !1;
21
+ const n = c[String(t)];
22
+ if (!n)
23
+ return console.warn(
24
+ `[RecommendationMigrator] Aborting block id=${t} — recommendationConfigs entry missing in Guido Config`
25
+ ), !1;
26
+ const m = w(n, r, t), e = b((s = r.composition) == null ? void 0 : s.variables), d = y(o), l = v(o), g = {
27
+ ...m,
28
+ composition: e.composition,
29
+ visibility: e.visibility,
30
+ textTrimming: e.anyTextTrimming
31
+ }, p = C({
32
+ nodeConfig: g,
33
+ composition: e,
34
+ cardBg: l,
35
+ titleText: d,
36
+ legacyId: o.getAttribute("id") ?? void 0,
37
+ legacyBgColor: o.getAttribute("bgcolor") ?? void 0
38
+ }), a = i.parseFromString(`<table><tbody><tr>${p}</tr></tbody></table>`, "text/html").querySelector(".recommendation-block-v2");
39
+ return !a || !o.parentNode ? (console.warn(
40
+ `[RecommendationMigrator] Aborting block id=${t} — failed to construct new HTML element`
41
+ ), !1) : (o.parentNode.replaceChild(a, o), !0);
42
+ }
43
+ function H(i, o = {}) {
44
+ const c = new DOMParser(), t = c.parseFromString(i, "text/html"), r = Array.from(t.querySelectorAll(x));
45
+ try {
46
+ u().$patch((e) => {
47
+ e.migrations = {
48
+ ...e.migrations,
49
+ [f.Recommendation]: r.length
50
+ };
99
51
  });
52
+ } catch {
100
53
  }
101
- /**
102
- * Extracts product configuration from old block and generates new product rows
103
- * @param block - The old recommendation block element
104
- * @returns HTML string for product rows
105
- */
106
- extractProductRows(e) {
107
- const t = this.extractProductConfig(e);
108
- if (!t)
109
- return console.warn("[RecommendationMigrator] No product config found, using defaults"), this.generateProductRows(6, 3, {});
110
- const { totalCount: s, productsPerRow: o } = t, i = this.extractProductStyles(e);
111
- return this.generateProductRows(s, o, i);
112
- }
113
- /**
114
- * Extracts Stripo padding/margin utility classes from a class string
115
- * @param classString - Space-separated class string from element
116
- * @returns Filtered Stripo utility classes as a space-separated string
117
- */
118
- extractStripoClasses(e) {
119
- return e ? e.split(/\s+/).filter((o) => /^es-(p\d+|m[-\d])/.test(o)).join(" ") : "";
120
- }
121
- /**
122
- * Extracts product configuration from old block
123
- * @param block - The old recommendation block element
124
- * @returns Object with totalCount and productsPerRow, or null if parsing fails
125
- */
126
- extractProductConfig(e) {
127
- const t = e.getAttribute("esd-dev-product-config");
128
- if (!t)
129
- return null;
54
+ if (r.length === 0)
55
+ return i;
56
+ let n = 0;
57
+ return r.forEach((m) => {
130
58
  try {
131
- const s = this.unescapeJsonString(t), o = JSON.parse(s);
132
- return {
133
- totalCount: o.totalCount ?? 6,
134
- productsPerRow: o.rowCount ?? 3
135
- };
136
- } catch (s) {
137
- return console.error("[RecommendationMigrator] Failed to parse product config:", s), console.error(
138
- "[RecommendationMigrator] Error details:",
139
- s instanceof Error ? s.message : String(s)
140
- ), null;
141
- }
142
- }
143
- /**
144
- * Decodes HTML entities from JSON string
145
- * @param text - The encoded string
146
- * @returns Decoded string
147
- */
148
- unescapeJsonString(e) {
149
- return e.replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&#x27;/g, "'").replace(/&quot;/g, '"').replace(/\\\\"/g, "'");
150
- }
151
- /**
152
- * Generates product rows using prepareProductRows with default product data
153
- * @param totalCount - Total number of products to display
154
- * @param productsPerRow - Number of products in each row
155
- * @param extractedStyles - Styles extracted from old product elements
156
- * @returns HTML string for product rows with applied styles
157
- */
158
- generateProductRows(e, t, s) {
159
- const o = $(), i = [];
160
- for (let n = 0; n < e; n++) {
161
- const u = o[n % o.length];
162
- i.push({ ...u });
163
- }
164
- let r = q(i, t);
165
- return r = this.applyExtractedStyles(r, s), r;
166
- }
167
- /**
168
- * Extracts styles from old product elements
169
- * @param block - The old recommendation block element
170
- * @returns Object with extracted styles for each element type
171
- */
172
- extractProductStyles(e) {
173
- const t = {}, s = e.querySelector(".product-card");
174
- if (!s)
175
- return console.warn("[RecommendationMigrator] No product card found, using default styles"), t;
176
- const o = this.extractCardBgColor(s);
177
- o && (t.cardBgColor = o);
178
- const i = s.querySelector(".ext-product-image");
179
- if (i) {
180
- const l = i.querySelector("img"), g = l == null ? void 0 : l.getAttribute("width"), a = i.getAttribute("style") || "", p = i.getAttribute("align") || "center", b = i.getAttribute("class") || "", m = this.extractStripoClasses(b);
181
- t.imageWidth = g || "120", t.imageAlign = p, t.imageTdStyle = a, t.imageClasses = m;
182
- }
183
- const r = s.querySelector(".ext-product-name");
184
- if (r) {
185
- const l = r.querySelector("p"), g = (l == null ? void 0 : l.getAttribute("style")) || "", a = r.getAttribute("align") || "center", p = r.getAttribute("style") || "", b = r.getAttribute("class") || "", m = this.extractStripoClasses(b);
186
- t.nameStyle = g, t.nameAlign = a, t.nameTdStyle = p, t.nameClasses = m;
187
- }
188
- const n = s.querySelector(".ext-product-price");
189
- if (n) {
190
- const l = n.querySelector("p"), g = (l == null ? void 0 : l.getAttribute("style")) || "", a = n.getAttribute("align") || "center", p = n.getAttribute("style") || "", b = n.getAttribute("class") || "", m = this.extractStripoClasses(b);
191
- t.priceStyle = g, t.priceAlign = a, t.priceTdStyle = p, t.priceClasses = m;
192
- }
193
- const u = s.querySelector(".ext-product-original-price");
194
- if (u) {
195
- const l = u.querySelector("p"), g = (l == null ? void 0 : l.getAttribute("style")) || "", a = u.getAttribute("align") || "center", p = u.getAttribute("style") || "", b = u.getAttribute("class") || "", m = this.extractStripoClasses(b);
196
- t.oldPriceStyle = g, t.oldPriceAlign = a, t.oldPriceTdStyle = p, t.oldPriceClasses = m;
197
- }
198
- const c = s.querySelector(".ext-product-button");
199
- if (c) {
200
- const l = c.querySelector(".es-button-border"), g = c.querySelector("a.es-button"), a = (l == null ? void 0 : l.getAttribute("style")) || "", p = (g == null ? void 0 : g.getAttribute("style")) || "", b = c.getAttribute("align") || "center", m = c.getAttribute("style") || "", f = c.getAttribute("class") || "", C = this.extractStripoClasses(f), y = ((c == null ? void 0 : c.textContent) || "").trim();
201
- t.buttonBorderStyle = a, t.buttonLinkStyle = p, t.buttonAlign = b, t.buttonTdStyle = m, t.buttonClasses = C, t.buttonText = y;
59
+ M(c, m, o) && (n += 1);
60
+ } catch (e) {
61
+ console.error("[RecommendationMigrator] Unexpected error migrating block:", e);
202
62
  }
203
- return t;
204
- }
205
- /**
206
- * Applies extracted styles to the generated HTML
207
- * @param html - The generated HTML from prepareProductRows
208
- * @param extractedStyles - Styles extracted from old product elements
209
- * @returns HTML with applied styles
210
- */
211
- applyExtractedStyles(e, t) {
212
- if (!t || Object.keys(t).length === 0)
213
- return e;
214
- const s = this.parser.parseFromString(`<table>${e}</table>`, "text/html");
215
- t.cardBgColor && s.querySelectorAll(".product-attribute-cell").forEach((r) => {
216
- r.setAttribute("bgcolor", t.cardBgColor);
217
- }), (t.imageWidth || t.imageAlign || t.imageTdStyle || t.imageClasses) && s.querySelectorAll(".product-image").forEach((r) => {
218
- if (t.imageAlign && r.setAttribute("align", t.imageAlign), t.imageTdStyle && r.setAttribute("style", t.imageTdStyle), t.imageClasses) {
219
- const c = `${r.getAttribute("class") || ""} ${t.imageClasses}`.trim();
220
- r.setAttribute("class", c);
221
- }
222
- const n = r.querySelector("img");
223
- n && t.imageWidth && n.setAttribute("width", t.imageWidth);
224
- }), (t.nameStyle || t.nameAlign || t.nameTdStyle || t.nameClasses) && s.querySelectorAll(".product-name").forEach((r) => {
225
- if (t.nameAlign && r.setAttribute("align", t.nameAlign), t.nameTdStyle && r.setAttribute("style", t.nameTdStyle), t.nameClasses) {
226
- const c = `${r.getAttribute("class") || ""} ${t.nameClasses}`.trim();
227
- r.setAttribute("class", c);
228
- }
229
- const n = r.querySelector("p");
230
- n && t.nameStyle && n.setAttribute("style", t.nameStyle);
231
- }), (t.priceStyle || t.priceAlign || t.priceTdStyle || t.priceClasses) && s.querySelectorAll(".product-price").forEach((r) => {
232
- if (t.priceAlign && r.setAttribute("align", t.priceAlign), t.priceTdStyle && r.setAttribute("style", t.priceTdStyle), t.priceClasses) {
233
- const c = `${r.getAttribute("class") || ""} ${t.priceClasses}`.trim();
234
- r.setAttribute("class", c);
235
- }
236
- const n = r.querySelector("p");
237
- n && t.priceStyle && n.setAttribute("style", t.priceStyle);
238
- }), (t.oldPriceStyle || t.oldPriceAlign || t.oldPriceTdStyle || t.oldPriceClasses) && s.querySelectorAll(".product-old-price").forEach((r) => {
239
- if (t.oldPriceAlign && r.setAttribute("align", t.oldPriceAlign), t.oldPriceTdStyle && r.setAttribute("style", t.oldPriceTdStyle), t.oldPriceClasses) {
240
- const c = `${r.getAttribute("class") || ""} ${t.oldPriceClasses}`.trim();
241
- r.setAttribute("class", c);
242
- }
243
- const n = r.querySelector("p");
244
- n && t.oldPriceStyle && n.setAttribute("style", t.oldPriceStyle);
245
- }), (t.buttonBorderStyle || t.buttonLinkStyle || t.buttonAlign || t.buttonTdStyle || t.buttonClasses) && s.querySelectorAll(".product-button").forEach((r) => {
246
- if (t.buttonAlign && r.setAttribute("align", t.buttonAlign), t.buttonTdStyle && r.setAttribute("style", t.buttonTdStyle), t.buttonClasses) {
247
- const l = `${r.getAttribute("class") || ""} ${t.buttonClasses}`.trim();
248
- r.setAttribute("class", l);
249
- }
250
- const n = r.querySelector(".es-button-border");
251
- n && t.buttonBorderStyle && n.setAttribute("style", t.buttonBorderStyle);
252
- const u = r.querySelector("a.es-button");
253
- u && (t.buttonLinkStyle && u.setAttribute("style", t.buttonLinkStyle), t.buttonText && (u.textContent = t.buttonText));
254
- });
255
- const o = s.querySelector("table");
256
- return (o == null ? void 0 : o.innerHTML) || e;
257
- }
258
- /**
259
- * Builds title block with proper structure
260
- */
261
- buildTitleBlock(e) {
262
- let t = e.text;
263
- e.isBold && e.isItalic ? t = `<strong path="1,0"><em path="1,0,0">${t}</em></strong>` : e.isBold ? t = `<strong path="1,0">${t}</strong>` : e.isItalic && (t = `<em path="1,0">${t}</em>`);
264
- const s = e.align ? ` align="${e.align}"` : "", o = e.styles ? ` style="${e.styles}"` : "";
265
- return `
266
- <td class="esd-block-text es-p10t es-p10b es-p20l es-p20r"${s}>
267
- <p path="1"${o}>
268
- ${t}
269
- </p>
270
- </td>
271
- `;
272
- }
273
- /**
274
- * Removes specified style properties from a style string
275
- */
276
- removeStyleProperties(e, t) {
277
- return e ? t.reduce((o, i) => {
278
- const r = new RegExp(`${i}\\s*:\\s*[^;]*;?`, "gi");
279
- return o.replace(r, "");
280
- }, e).replace(/;\s*;/g, ";").replace(/^;|;$/g, "").trim() : "";
281
- }
282
- /**
283
- * Converts inline display to block display
284
- */
285
- convertInlineToBlock(e) {
286
- if (!e)
287
- return "";
288
- let t = e.replace(/display\s*:\s*inline/gi, "display: block");
289
- return /display\s*:/i.test(t) || (t = t ? `${t}; display: block` : "display: block"), t.replace(/;\s*;/g, ";").replace(/^;|;$/g, "").replace(/"/g, "'").trim();
290
- }
291
- }
292
- function v(d) {
293
- return new B().migrate(d);
63
+ }), n > 0 && console.info(`[RecommendationMigrator] Migrated ${n} legacy Recommendation block(s)`), t.documentElement.outerHTML;
294
64
  }
295
65
  export {
296
- v as migrateRecommendation
66
+ H as migrateRecommendation
297
67
  };
@@ -18,7 +18,8 @@ const s = {
18
18
  { text: "after the amount", value: "1" }
19
19
  ], d = [
20
20
  { text: "dot(.)", value: "." },
21
- { text: "comma(,)", value: "," }
21
+ { text: "comma(,)", value: "," },
22
+ { text: "space( )", value: " " }
22
23
  ], p = [
23
24
  { text: "0", value: "0" },
24
25
  { text: "1", value: "1" },
@@ -1,4 +1,4 @@
1
- import { DEFAULT_MOBILE_ROW_SPACING as R, DEFAULT_MOBILE_COLUMN_SPACING as U, DEFAULT_ROW_SPACING as i, DEFAULT_COLUMN_SPACING as s, DEFAULT_MOBILE_CARDS_IN_ROW as o, DEFAULT_CARDS_IN_ROW as C } from "./layout.js";
1
+ import { DEFAULT_COLUMN_SPACING as R, DEFAULT_ROW_SPACING as U, DEFAULT_MOBILE_ROW_SPACING as i, DEFAULT_MOBILE_COLUMN_SPACING as s, DEFAULT_MOBILE_CARDS_IN_ROW as o, DEFAULT_CARDS_IN_ROW as C } from "./layout.js";
2
2
  import { ATTR_PRODUCT_IMAGE as _, ATTR_PRODUCT_NAME as e, ATTR_PRODUCT_OLD_PRICE as t, ATTR_PRODUCT_PRICE as T, ATTR_PRODUCT_OMNIBUS_PRICE as r, ATTR_PRODUCT_OMNIBUS_DISCOUNT as I, ATTR_PRODUCT_BUTTON as n } from "./selectors.js";
3
3
  const O = {
4
4
  code: "USD",
@@ -44,10 +44,10 @@ const O = {
44
44
  mobileCardsInRow: o,
45
45
  mobileLayoutEnabled: !1,
46
46
  previousMobileCardsInRow: o,
47
- columnSpacing: s,
48
- rowSpacing: i,
49
- mobileColumnSpacing: U,
50
- mobileRowSpacing: R,
47
+ columnSpacing: R,
48
+ rowSpacing: U,
49
+ mobileColumnSpacing: s,
50
+ mobileRowSpacing: i,
51
51
  // Composition
52
52
  composition: a,
53
53
  visibility: E,
@@ -1,8 +1,14 @@
1
- import { ModificationDescription as f } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- import { CURRENT_CONFIG_VERSION as u, DEFAULT_NODE_CONFIG as e } from "../constants/defaultConfig.js";
3
- import { setCurrencyAttributes as g } from "../controls/main/utils.js";
4
- import { hasMinimalConfig as a } from "../types/nodeConfig.js";
5
- class C {
1
+ import { ModificationDescription as h } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
+ import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as r } from "../constants/defaultConfig.js";
3
+ import { setCurrencyAttributes as D } from "../controls/main/utils.js";
4
+ import { hasMinimalConfig as A } from "../types/nodeConfig.js";
5
+ function S(e) {
6
+ return e === "." || e === "," || e === " ";
7
+ }
8
+ function N(e) {
9
+ return e === "." || e === "," || e === " " || e === "";
10
+ }
11
+ class V {
6
12
  // ========================================================================
7
13
  // Read Operations
8
14
  // ========================================================================
@@ -42,7 +48,7 @@ class C {
42
48
  return !1;
43
49
  try {
44
50
  const t = i.getNodeConfig();
45
- return a(t);
51
+ return A(t);
46
52
  } catch {
47
53
  return !1;
48
54
  }
@@ -77,8 +83,8 @@ class C {
77
83
  * @returns The new complete configuration
78
84
  */
79
85
  static updateConfig(i, t, o, n) {
80
- const s = this.getConfig(t), r = this.deepMerge(s, o);
81
- return this.saveConfig(i, t, r, n), r;
86
+ const c = this.getConfig(t), u = this.deepMerge(c, o);
87
+ return this.saveConfig(i, t, u, n), u;
82
88
  }
83
89
  /**
84
90
  * Initialize configuration for a new block
@@ -95,7 +101,7 @@ class C {
95
101
  */
96
102
  static initializeConfig(i, t, o) {
97
103
  const n = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
98
- return this.saveConfig(i, t, n, "Initialize recommendation block"), g({
104
+ return this.saveConfig(i, t, n, "Initialize recommendation block"), D({
99
105
  currentNode: t,
100
106
  documentModifier: i.getDocumentModifier(),
101
107
  currency: n.currency
@@ -112,9 +118,9 @@ class C {
112
118
  */
113
119
  static saveConfig(i, t, o, n) {
114
120
  try {
115
- i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new f(n));
116
- } catch (s) {
117
- console.warn("[RecommendationConfigService] Failed to save config:", s);
121
+ i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new h(n));
122
+ } catch (c) {
123
+ console.warn("[RecommendationConfigService] Failed to save config:", c);
118
124
  }
119
125
  }
120
126
  // ========================================================================
@@ -125,23 +131,53 @@ class C {
125
131
  *
126
132
  * Reads existing data-attributes and creates a proper node config.
127
133
  * Used when loading templates created before node config was implemented.
134
+ *
135
+ * Sources, in priority order (later overrides earlier):
136
+ * 1. `esd-ext-config` JSON blob — emitted by the recommendation migrator and
137
+ * by hand-authored new templates. Carries the full RecommendationNodeConfig
138
+ * (strategy, language, currency, filters, productIds, etc.) which the
139
+ * discrete data-* attrs below cannot capture.
140
+ * 2. Discrete `data-*` attributes — runtime source of truth for
141
+ * layout/composition/spacing; controls keep these in sync as the user
142
+ * edits, so we let them override the JSON blob if they disagree.
143
+ * 3. `currency-*` attributes — durable currency source on the block element.
128
144
  * @param api - Stripo extension API with document modifier
129
145
  * @param node - The block node to migrate
130
146
  * @returns The migrated configuration
131
147
  */
132
148
  static migrateFromDataAttributes(i, t) {
133
149
  const o = {
134
- configVersion: u
150
+ configVersion: l
135
151
  };
136
152
  if ("getAttribute" in t && typeof t.getAttribute == "function") {
137
- const n = t.getAttribute("data-layout");
138
- n === "list" || n === "horizontal" ? o.layout = "list" : (n === "grid" || n === "vertical") && (o.layout = "grid");
139
- const s = t.getAttribute("data-card-composition");
140
- s && (o.composition = s.split(",").filter(Boolean));
141
- const r = t.getAttribute("data-column-spacing");
142
- r && (o.columnSpacing = parseInt(r) || e.columnSpacing);
143
- const c = t.getAttribute("data-row-spacing");
144
- c && (o.rowSpacing = parseInt(c) || e.rowSpacing);
153
+ const n = t.getAttribute("esd-ext-config");
154
+ if (n)
155
+ try {
156
+ const s = JSON.parse(n);
157
+ s && typeof s == "object" && (Object.assign(o, s), o.configVersion = l);
158
+ } catch {
159
+ }
160
+ const c = t.getAttribute("data-layout");
161
+ c === "list" || c === "horizontal" ? o.layout = "list" : (c === "grid" || c === "vertical") && (o.layout = "grid");
162
+ const u = t.getAttribute("data-card-composition");
163
+ u && (o.composition = u.split(",").filter(Boolean));
164
+ const p = t.getAttribute("data-column-spacing");
165
+ p && (o.columnSpacing = parseInt(p) || r.columnSpacing);
166
+ const b = t.getAttribute("data-row-spacing");
167
+ if (b && (o.rowSpacing = parseInt(b) || r.rowSpacing), !o.currency) {
168
+ const s = t.getAttribute("currency"), y = t.getAttribute("currency-symbol"), d = t.getAttribute("currency-alignment"), f = t.getAttribute("currency-thousand-separator"), g = t.getAttribute("currency-decimal-separator"), m = t.getAttribute("currency-decimal-count");
169
+ if (s || y || d || f || g || m) {
170
+ const a = r.currency, C = m ? parseInt(m) : NaN;
171
+ o.currency = {
172
+ code: s ?? a.code,
173
+ symbol: y ?? a.symbol,
174
+ alignment: d === "0" ? "before" : "after",
175
+ decimalCount: Number.isFinite(C) ? C : a.decimalCount,
176
+ decimalSeparator: S(g) ? g : a.decimalSeparator,
177
+ thousandSeparator: N(f) ? f : a.thousandSeparator
178
+ };
179
+ }
180
+ }
145
181
  }
146
182
  return this.initializeConfig(i, t, o);
147
183
  }
@@ -151,7 +187,7 @@ class C {
151
187
  * @returns True if migration is needed
152
188
  */
153
189
  static needsMigration(i) {
154
- return i ? this.hasConfig(i) ? this.getConfigVersion(i) < u : !0 : !1;
190
+ return i ? this.hasConfig(i) ? this.getConfigVersion(i) < l : !0 : !1;
155
191
  }
156
192
  // ========================================================================
157
193
  // Internal Helpers
@@ -161,12 +197,12 @@ class C {
161
197
  */
162
198
  static cloneDefaults() {
163
199
  return {
164
- ...e,
165
- currency: { ...e.currency },
166
- omnibusPrice: { ...e.omnibusPrice },
167
- omnibusDiscount: { ...e.omnibusDiscount },
168
- composition: [...e.composition],
169
- visibility: { ...e.visibility },
200
+ ...r,
201
+ currency: { ...r.currency },
202
+ omnibusPrice: { ...r.omnibusPrice },
203
+ omnibusDiscount: { ...r.omnibusDiscount },
204
+ composition: [...r.composition],
205
+ visibility: { ...r.visibility },
170
206
  filters: [],
171
207
  productIds: [],
172
208
  recommendationId: 0
@@ -236,5 +272,5 @@ class C {
236
272
  }
237
273
  }
238
274
  export {
239
- C as RecommendationConfigService
275
+ V as RecommendationConfigService
240
276
  };