@useinsider/guido 3.7.2-beta.d7d2e30 → 3.7.2-beta.e9ee855

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 (23) hide show
  1. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +8 -8
  2. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +15 -12
  3. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  4. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +16 -15
  5. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue.js +4 -4
  6. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue2.js +38 -35
  7. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue.js +1 -1
  8. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue2.js +54 -51
  9. package/dist/composables/usePreviewInteractionGuard.js +17 -0
  10. package/dist/composables/useSave.js +14 -14
  11. package/dist/composables/validators/useUnsubscribeBlockValidator.js +26 -17
  12. package/dist/extensions/Blocks/Items/block.js +29 -48
  13. package/dist/extensions/Blocks/Items/utils/nodeConfigUtils.js +45 -62
  14. package/dist/extensions/Blocks/Unsubscribe/block.js +40 -37
  15. package/dist/extensions/Blocks/Unsubscribe/control.js +17 -14
  16. package/dist/guido.css +1 -1
  17. package/dist/src/composables/usePreviewInteractionGuard.d.ts +3 -0
  18. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +1 -0
  19. package/dist/src/extensions/Blocks/Items/block.d.ts +0 -8
  20. package/dist/src/extensions/Blocks/Items/utils/nodeConfigUtils.d.ts +1 -1
  21. package/dist/src/stores/unsubscribe.d.ts +11 -1
  22. package/dist/stores/unsubscribe.js +8 -7
  23. package/package.json +1 -1
@@ -1,20 +1,20 @@
1
- import { BlockId as u } from "../../../enums/block.js";
1
+ import { BlockId as g } from "../../../enums/block.js";
2
2
  import { useOnboardingStore as p } from "../../../stores/onboarding.js";
3
- import { getMigrationBannerHtml as C } from "../../../utils/migrationBannerHtml.js";
4
- import { Block as I, BlockCompositionType as y, ModificationDescription as s } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { SETTINGS_ENUMS as c, DefaultConfigValues as a } from "./enums/settingsEnums.js";
6
- import { getDefaultTemplate as h } from "./template.js";
7
- import { getItemsBlockContainer as l, getItemsBlockConfig as m, getDefaultItemsBlockConfig as b } from "./utils/nodeConfigUtils.js";
8
- const d = u.Items;
9
- class E extends I {
3
+ import { getMigrationBannerHtml as f } from "../../../utils/migrationBannerHtml.js";
4
+ import { Block as u, BlockCompositionType as I, ModificationDescription as o } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { SETTINGS_ENUMS as a, DefaultConfigValues as i } from "./enums/settingsEnums.js";
6
+ import { getDefaultTemplate as C } from "./template.js";
7
+ import { getItemsBlockContainer as y, getItemsBlockConfig as b, getDefaultItemsBlockConfig as h } from "./utils/nodeConfigUtils.js";
8
+ const c = g.Items;
9
+ class E extends u {
10
10
  getId() {
11
- return d;
11
+ return c;
12
12
  }
13
13
  getIcon() {
14
14
  return "items-icon";
15
15
  }
16
16
  getBlockCompositionType() {
17
- return y.CONTAINER;
17
+ return I.CONTAINER;
18
18
  }
19
19
  getName() {
20
20
  return this.api.translate("Items");
@@ -23,60 +23,41 @@ class E extends I {
23
23
  return this.api.translate("Items lets you display personalized products based on user behavior.");
24
24
  }
25
25
  getSettingsPanelTitleHtml() {
26
- return C(
27
- d,
26
+ return f(
27
+ c,
28
28
  this.api.translate("Items"),
29
29
  this.api.translate("This block is switched from the Old Version to the New Version. We recommend you check the Items block and test your message to ensure it works properly.")
30
30
  );
31
31
  }
32
32
  getTemplate() {
33
- return h({
34
- orientation: c.ORIENTATION.VERTICAL,
35
- itemsType: c.ITEMS_TYPE.CART_ITEMS,
33
+ return C({
34
+ orientation: a.ORIENTATION.VERTICAL,
35
+ itemsType: a.ITEMS_TYPE.CART_ITEMS,
36
36
  itemId: "{{Abandoned Cart Item (1) Url}}",
37
- currencySymbol: a.productPriceCurrencySymbolControlValue,
38
- currencyLocation: a.productPriceCurrencyLocationControlValue,
39
- formattedPrice: a.productPriceFormattedControlValue === "1"
37
+ currencySymbol: i.productPriceCurrencySymbolControlValue,
38
+ currencyLocation: i.productPriceCurrencyLocationControlValue,
39
+ formattedPrice: i.productPriceFormattedControlValue === "1"
40
40
  });
41
41
  }
42
42
  allowInnerBlocksDND() {
43
43
  return !1;
44
44
  }
45
- canBeSavedAsModule() {
46
- return !0;
47
- }
48
- onCreated(i) {
49
- const n = this.api.getDocumentModifier(), r = this.api.getDocumentRootCssNode();
50
- r.querySelector('[product-attr="imageSrc"] img') || n.modifyCss(r).appendRule('[product-attr="imageSrc"] img {object-fit: contain;}');
51
- const t = l(i);
52
- if (!t)
45
+ onCreated(n) {
46
+ const l = this.api.getDocumentModifier(), r = this.api.getDocumentRootCssNode();
47
+ r.querySelector('[product-attr="imageSrc"] img') || l.modifyCss(r).appendRule('[product-attr="imageSrc"] img {object-fit: contain;}');
48
+ const e = y(n);
49
+ if (!e)
53
50
  return;
54
- const e = t.getNodeConfig(), g = e && Object.keys(e).length > 0, o = m(i);
55
- if (o != null && o.initialized)
56
- g ? o.blockInstanceId || this.api.getDocumentModifier().modifyHtml(t).setNodeConfig({ ...o, blockInstanceId: String(Date.now()) }).apply(new s("Assign block instance ID to block")) : this.api.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new s("Migrate legacy config to nodeConfig"));
51
+ const s = e.getNodeConfig(), m = s && Object.keys(s).length > 0, t = b(n);
52
+ if (t != null && t.initialized)
53
+ m ? t.blockInstanceId || this.api.getDocumentModifier().modifyHtml(e).setNodeConfig({ ...t, blockInstanceId: String(Date.now()) }).apply(new o("Assign block instance ID to block")) : this.api.getDocumentModifier().modifyHtml(e).setNodeConfig(t).apply(new o("Migrate legacy config to nodeConfig"));
57
54
  else {
58
- const f = b();
59
- this.api.getDocumentModifier().modifyHtml(t).setNodeConfig(f).apply(new s("Initialize Items block with default configuration")), p().startOnboarding("itemsOnboarding");
55
+ const d = h();
56
+ this.api.getDocumentModifier().modifyHtml(e).setNodeConfig(d).apply(new o("Initialize Items block with default configuration")), p().startOnboarding("itemsOnboarding");
60
57
  }
61
58
  }
62
- /**
63
- * Re-seeds nodeConfig from the persisted esd-ext-config when a saved module
64
- * surfaces via document load. Stripo strips esd-ext-config and never restores it
65
- * into nodeConfig, so without this a reused module would reset to defaults.
66
- * Guarded to the nodeConfig-empty case so it runs once and never loops.
67
- */
68
- onDocumentChanged(i) {
69
- const n = l(i);
70
- if (!n)
71
- return;
72
- const r = n.getNodeConfig();
73
- if (r && Object.keys(r).length > 0)
74
- return;
75
- const e = m(i);
76
- e != null && e.initialized && this.api.getDocumentModifier().modifyHtml(n).setNodeConfig(e).apply(new s("Recover Items block config from saved module"));
77
- }
78
59
  }
79
60
  export {
80
- d as BLOCK_ID,
61
+ c as BLOCK_ID,
81
62
  E as ItemsBlock
82
63
  };
@@ -1,36 +1,36 @@
1
1
  import { ModificationDescription as b } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- import { DefaultConfigValues as i, SETTINGS_ENUMS as n, ItemInCartOptions as g } from "../enums/settingsEnums.js";
2
+ import { DefaultConfigValues as o, SETTINGS_ENUMS as n, ItemInCartOptions as g } from "../enums/settingsEnums.js";
3
3
  function _() {
4
4
  return String(Date.now() + Math.floor(Math.random() * 1e3));
5
5
  }
6
6
  const E = (r) => r.replace(/\$/g, "$$$$");
7
- function y() {
7
+ function S() {
8
8
  return {
9
9
  initialized: !0,
10
10
  blockInstanceId: _(),
11
11
  source: n.ITEMS_TYPE.CART_ITEMS,
12
12
  type: n.ITEMS_TYPE.CART_ITEMS,
13
- itemsSelectValue: i.cartItemsSelectControlValue,
13
+ itemsSelectValue: o.cartItemsSelectControlValue,
14
14
  orientation: n.ORIENTATION.VERTICAL,
15
- nameTrimming: i.productNameTrimmingControlValue === "1",
16
- priceHideDiscount: i.productPriceHideDiscountControlValue === "1",
17
- priceFormatted: i.productPriceFormattedControlValue === "1",
15
+ nameTrimming: o.productNameTrimmingControlValue === "1",
16
+ priceHideDiscount: o.productPriceHideDiscountControlValue === "1",
17
+ priceFormatted: o.productPriceFormattedControlValue === "1",
18
18
  priceSinglePrice: !1,
19
- priceCurrencySymbol: i.productPriceCurrencySymbolControlValue,
20
- priceCurrencyLocation: i.productPriceCurrencyLocationControlValue,
19
+ priceCurrencySymbol: o.productPriceCurrencySymbolControlValue,
20
+ priceCurrencyLocation: o.productPriceCurrencyLocationControlValue,
21
21
  priceOrientation: "horizontal",
22
- quantityControlEnabled: i.productQuantityControlEnabled === "1",
23
- buttonLink: i.productButtonLinkControlValue,
24
- imageLink: i.productImageLinkControlValue,
22
+ quantityControlEnabled: o.productQuantityControlEnabled === "1",
23
+ buttonLink: o.productButtonLinkControlValue,
24
+ imageLink: o.productImageLinkControlValue,
25
25
  buttonLabel: "Buy",
26
26
  buttonFullWidth: !0,
27
27
  // Default to full width (es-fw class)
28
- imageVisible: i.productImageVisible === "1",
29
- nameVisible: i.productNameVisible === "1",
30
- quantityVisible: i.productQuantityVisible === "1",
31
- priceVisible: i.productPriceVisible === "1",
32
- originalPriceVisible: i.productOriginalPriceVisible === "1",
33
- buttonVisible: i.productButtonVisible === "1"
28
+ imageVisible: o.productImageVisible === "1",
29
+ nameVisible: o.productNameVisible === "1",
30
+ quantityVisible: o.productQuantityVisible === "1",
31
+ priceVisible: o.productPriceVisible === "1",
32
+ originalPriceVisible: o.productOriginalPriceVisible === "1",
33
+ buttonVisible: o.productButtonVisible === "1"
34
34
  };
35
35
  }
36
36
  function p(r) {
@@ -50,26 +50,26 @@ function m(r) {
50
50
  PURCHASED_ITEMS: n.ITEMS_TYPE.PURCHASED_ITEMS
51
51
  }[r] || n.ITEMS_TYPE.CART_ITEMS : n.ITEMS_TYPE.CART_ITEMS;
52
52
  }
53
- function f(r, t) {
53
+ function I(r, t) {
54
54
  if (!r)
55
- return i.cartItemsSelectControlValue;
55
+ return o.cartItemsSelectControlValue;
56
56
  if (r.includes("{{"))
57
57
  return r;
58
58
  if (/^\d+$/.test(r)) {
59
- const e = parseInt(r) - 1, o = g[t];
60
- if (o && o[e])
61
- return o[e].value;
59
+ const e = parseInt(r) - 1, i = g[t];
60
+ if (i && i[e])
61
+ return i[e].value;
62
62
  }
63
- return i.cartItemsSelectControlValue;
63
+ return o.cartItemsSelectControlValue;
64
64
  }
65
65
  function C(r) {
66
66
  const t = r.querySelector("esd-config-block");
67
67
  if (!t)
68
68
  return null;
69
- const e = (u, s) => u == null ? s : u === "1" || u === "true", o = (u, s) => u || s, c = t.getAttribute("data-type"), a = m(c), d = t.getAttribute("data-cart_items_select_control_value"), l = f(d, a);
69
+ const e = (c, s) => c == null ? s : c === "1" || c === "true", i = (c, s) => c || s, u = t.getAttribute("data-type"), a = m(u), d = t.getAttribute("data-cart_items_select_control_value"), l = I(d, a);
70
70
  return {
71
71
  initialized: e(t.getAttribute("data-initialized"), !1),
72
- blockInstanceId: o(
72
+ blockInstanceId: i(
73
73
  t.getAttribute("data-block-instance-id"),
74
74
  _()
75
75
  ),
@@ -93,28 +93,28 @@ function C(r) {
93
93
  t.getAttribute("data-product_price_control_single_price"),
94
94
  !1
95
95
  ),
96
- priceCurrencySymbol: o(
96
+ priceCurrencySymbol: i(
97
97
  t.getAttribute("data-product_price_control_currency_symbol"),
98
- i.productPriceCurrencySymbolControlValue
98
+ o.productPriceCurrencySymbolControlValue
99
99
  ),
100
- priceCurrencyLocation: o(
100
+ priceCurrencyLocation: i(
101
101
  t.getAttribute("data-product_price_currency_location"),
102
- i.productPriceCurrencyLocationControlValue
102
+ o.productPriceCurrencyLocationControlValue
103
103
  ),
104
104
  priceOrientation: t.getAttribute("data-product_original_price_control_orientation") || "horizontal",
105
105
  quantityControlEnabled: e(
106
106
  t.getAttribute("data-product_quantity_control_enabled"),
107
107
  !0
108
108
  ),
109
- buttonLink: o(
109
+ buttonLink: i(
110
110
  t.getAttribute("data-product_button_link"),
111
- i.productButtonLinkControlValue
111
+ o.productButtonLinkControlValue
112
112
  ),
113
- imageLink: o(
113
+ imageLink: i(
114
114
  t.getAttribute("data-product_image_link"),
115
- i.productImageLinkControlValue
115
+ o.productImageLinkControlValue
116
116
  ),
117
- buttonLabel: o(
117
+ buttonLabel: i(
118
118
  t.getAttribute("data-product_button_control_label"),
119
119
  "Buy"
120
120
  ),
@@ -149,45 +149,28 @@ function C(r) {
149
149
  )
150
150
  };
151
151
  }
152
- function I(r) {
153
- if (typeof r.getAttribute != "function")
154
- return null;
155
- const t = r.getAttribute("esd-ext-config");
156
- if (!t)
157
- return null;
158
- try {
159
- const e = JSON.parse(t);
160
- if (e && e.initialized)
161
- return e;
162
- } catch {
163
- }
164
- return null;
165
- }
166
- function A(r) {
152
+ function y(r) {
167
153
  const t = p(r);
168
154
  if (!t)
169
155
  return null;
170
156
  const e = t.getNodeConfig();
171
157
  if (e && e.initialized)
172
158
  return e;
173
- const o = I(t);
174
- if (o)
175
- return o;
176
- const c = C(t);
177
- return c || null;
159
+ const i = C(t);
160
+ return i || null;
178
161
  }
179
- function V(r, t, e, o) {
180
- const c = p(r);
181
- if (!c)
162
+ function A(r, t, e, i) {
163
+ const u = p(r);
164
+ if (!u)
182
165
  return;
183
- const d = { ...c.getNodeConfig() || {}, ...e }, l = o ?? t.getDocumentModifier();
184
- l.modifyHtml(c).setNodeConfig(d), o || l.apply(new b("Update Items block configuration"));
166
+ const d = { ...u.getNodeConfig() || {}, ...e }, l = i ?? t.getDocumentModifier();
167
+ l.modifyHtml(u).setNodeConfig(d), i || l.apply(new b("Update Items block configuration"));
185
168
  }
186
169
  export {
187
170
  E as escapeReplacement,
188
171
  _ as generateBlockInstanceId,
189
- y as getDefaultItemsBlockConfig,
190
- A as getItemsBlockConfig,
172
+ S as getDefaultItemsBlockConfig,
173
+ y as getItemsBlockConfig,
191
174
  p as getItemsBlockContainer,
192
- V as setItemsBlockConfig
175
+ A as setItemsBlockConfig
193
176
  };
@@ -1,19 +1,20 @@
1
1
  var S = Object.defineProperty;
2
- var L = (c, o, e) => o in c ? S(c, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[o] = e;
3
- var a = (c, o, e) => L(c, typeof o != "symbol" ? o + "" : o, e);
4
- import { useToaster as f } from "../../../composables/useToaster.js";
2
+ var f = (c, o, e) => o in c ? S(c, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[o] = e;
3
+ var a = (c, o, e) => f(c, typeof o != "symbol" ? o + "" : o, e);
4
+ import { useToaster as L } from "../../../composables/useToaster.js";
5
5
  import { ToasterTypeOptions as A } from "../../../enums/toaster.js";
6
6
  import { PAGE_TYPES as h } from "../../../enums/unsubscribe.js";
7
- import { useUnsubscribeStore as u } from "../../../stores/unsubscribe.js";
8
- import { Block as y, BlockCompositionType as T, ContextActionType as k, ModificationDescription as d, BlockType as I } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
9
- import { getDefaultTemplate as v } from "./template.js";
7
+ import { useEditorStore as y } from "../../../stores/editor.js";
8
+ import { useUnsubscribeStore as l } from "../../../stores/unsubscribe.js";
9
+ import { Block as T, BlockCompositionType as I, ContextActionType as B, ModificationDescription as d, BlockType as v } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
10
+ import { getDefaultTemplate as C } from "./template.js";
10
11
  import { UNSUBSCRIBE_BLOCK_SELECTOR as m, DATA_ATTRIBUTES as n, UNSUBSCRIBE_EVENTS as b } from "./utils/constants.js";
11
- import { parsePageList as B } from "./utils/utils.js";
12
- const C = "unsubscribe-block", E = 'a[data-unsubscribe-link="true"]', N = "{{ins-unsubscribe-link}}", U = "https://academy.insiderone.com/docs/adding-unsubscribe-pages-into-emails", R = "Removing the unsubscribe text leaves an empty block, but an active unsubscribe link is required. Undo your last action to restore the text, or delete the unsubscribe block entirely.", D = {
12
+ import { parsePageList as _ } from "./utils/utils.js";
13
+ const N = "unsubscribe-block", E = 'a[data-unsubscribe-link="true"]', U = "{{ins-unsubscribe-link}}", R = "https://academy.insiderone.com/docs/adding-unsubscribe-pages-into-emails", D = "Removing the unsubscribe text leaves an empty block, but an active unsubscribe link is required. Undo your last action to restore the text, or delete the unsubscribe block entirely.", w = {
13
14
  [h.GLOBAL_UNSUBSCRIBE]: "{{ins-global-unsubscribe-link}}",
14
15
  [h.SUBSCRIPTION_PREFERENCE_CENTER]: "{{ins-preferences-unsubscribe-link}}"
15
- }, w = 3;
16
- class H extends y {
16
+ }, P = 3;
17
+ class W extends T {
17
18
  constructor() {
18
19
  super();
19
20
  a(this, "selectEventListener", null);
@@ -29,13 +30,13 @@ class H extends y {
29
30
  a(this, "guardAttemptsByBlockId", /* @__PURE__ */ new Map());
30
31
  }
31
32
  getId() {
32
- return C;
33
+ return N;
33
34
  }
34
35
  getIcon() {
35
36
  return "unsubscribe-icon";
36
37
  }
37
38
  getBlockCompositionType() {
38
- return T.BLOCK;
39
+ return I.BLOCK;
39
40
  }
40
41
  getName() {
41
42
  return this.api.translate("Unsubscribe Block");
@@ -44,15 +45,16 @@ class H extends y {
44
45
  return this.api.translate("Unsubscribe Block Description");
45
46
  }
46
47
  getTemplate() {
47
- return v();
48
+ return C();
48
49
  }
49
50
  getContextActionsIds() {
50
- return [k.MOVE, k.REMOVE];
51
+ return [B.MOVE, B.REMOVE];
51
52
  }
52
53
  onSelect(e) {
53
- this.currentNode = e;
54
+ if (this.currentNode = e, y().isPreviewModeOpen)
55
+ return;
54
56
  const t = this._getOrAssignBlockId(e);
55
- t && this.linkStateByBlockId.set(t, this._hasUnsubscribeLink(e)), !("getAttribute" in e && e.getAttribute("data-migration")) && (this._resetStoreState(), this._loadBlockState(e), this._setupSelectEventListener(), this._setupCancelEventListener(), this._checkExistingBlocks(), this._openDrawer());
57
+ t && this.linkStateByBlockId.set(t, this._hasUnsubscribeLink(e)), !("getAttribute" in e && e.getAttribute("data-migration")) && (this._resetStoreState(), t && (l().pendingBlockId = t), this._loadBlockState(e), this._setupSelectEventListener(), this._setupCancelEventListener(t), this._checkExistingBlocks(), this._openDrawer());
56
58
  }
57
59
  onCreated(e) {
58
60
  this._ensureLeadingTextGuard(e);
@@ -107,9 +109,9 @@ class H extends y {
107
109
  const g = this._getOrAssignBlockId(e);
108
110
  if (!g)
109
111
  return;
110
- const _ = this.guardAttemptsByBlockId.get(g) ?? 0;
111
- if (!(_ >= w)) {
112
- this.guardAttemptsByBlockId.set(g, _ + 1);
112
+ const k = this.guardAttemptsByBlockId.get(g) ?? 0;
113
+ if (!(k >= P)) {
114
+ this.guardAttemptsByBlockId.set(g, k + 1);
113
115
  try {
114
116
  this.api.getDocumentModifier().modifyHtml(r).prepend(" ").apply(new d("Ensure unsubscribe link text guard"));
115
117
  } catch (p) {
@@ -154,8 +156,8 @@ class H extends y {
154
156
  const t = this.api.getDocumentRoot();
155
157
  t && "querySelectorAll" in t && t.querySelectorAll(m).forEach((r) => {
156
158
  if ("getAttribute" in r) {
157
- const i = r.getAttribute(n.BLOCK_ID), l = i ? parseInt(i) : 0;
158
- l > e && (e = l);
159
+ const i = r.getAttribute(n.BLOCK_ID), u = i ? parseInt(i) : 0;
160
+ u > e && (e = u);
159
161
  }
160
162
  });
161
163
  } catch {
@@ -164,14 +166,14 @@ class H extends y {
164
166
  }
165
167
  _warnLinkRemoved() {
166
168
  try {
167
- const { showToaster: e } = f();
169
+ const { showToaster: e } = L();
168
170
  e({
169
171
  type: A.Warning,
170
- message: this.api.translate(R),
172
+ message: this.api.translate(D),
171
173
  actionButton: {
172
174
  text: this.api.translate("Visit Academy"),
173
175
  onClick: () => {
174
- window.open(U, "_blank", "noopener,noreferrer");
176
+ window.open(R, "_blank", "noopener,noreferrer");
175
177
  }
176
178
  }
177
179
  });
@@ -188,9 +190,10 @@ class H extends y {
188
190
  _removeSelectEventListener() {
189
191
  this.selectEventListener && (document.removeEventListener(b.SELECT, this.selectEventListener), this.selectEventListener = null);
190
192
  }
191
- _setupCancelEventListener() {
192
- this._removeCancelEventListener(), this.cancelEventListener = () => {
193
- this._handleCancel();
193
+ _setupCancelEventListener(e) {
194
+ this._removeCancelEventListener(), this.cancelEventListener = (t) => {
195
+ const { detail: s } = t, r = (s == null ? void 0 : s.blockId) ?? null;
196
+ r !== null && r !== e || this._handleCancel();
194
197
  }, document.addEventListener(b.CANCEL, this.cancelEventListener);
195
198
  }
196
199
  _removeCancelEventListener() {
@@ -200,7 +203,7 @@ class H extends y {
200
203
  try {
201
204
  if (!this.currentNode)
202
205
  return;
203
- this.api.getDocumentModifier().modifyHtml(this.currentNode).replaceWith(`<${I.EMPTY_CONTAINER}/>`).apply(new d("Removed unsubscribe block due to cancel"));
206
+ this.api.getDocumentModifier().modifyHtml(this.currentNode).replaceWith(`<${v.EMPTY_CONTAINER}/>`).apply(new d("Removed unsubscribe block due to cancel"));
204
207
  } catch (e) {
205
208
  console.warn("[UnsubscribeBlock] Failed to remove unsubscribe block:", e);
206
209
  }
@@ -218,19 +221,19 @@ class H extends y {
218
221
  this.api.getDocumentModifier().modifyHtml(s).setAttribute("href", r).apply(new d(`Updated unsubscribe link to ${r}`)), this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute(n.PAGE_TYPE, e.toString()).setAttribute(n.PAGE_LIST, t).apply(new d("Updated unsubscribe block metadata"));
219
222
  }
220
223
  _getMergeTag(e) {
221
- return D[e] ?? N;
224
+ return w[e] ?? U;
222
225
  }
223
226
  _openDrawer() {
224
227
  if (!(this.currentNode && this.currentNode.getAttribute("data-unsubscribe-page-type")))
225
228
  try {
226
- const e = u();
229
+ const e = l();
227
230
  e.typeSelectionDrawerStatus = !0;
228
231
  } catch (e) {
229
232
  console.error("[UnsubscribeBlock] Failed to open drawer:", e);
230
233
  }
231
234
  }
232
235
  _checkExistingBlocks() {
233
- const e = u();
236
+ const e = l();
234
237
  e.isGlobalUnsubscribeDisabled = !1, e.isSubscriptionPreferencesCenterDisabled = !1, this.api.getDocumentRoot().querySelectorAll(m).forEach((s) => {
235
238
  if ("getAttribute" in s) {
236
239
  const r = s.getAttribute(n.PAGE_TYPE);
@@ -247,11 +250,11 @@ class H extends y {
247
250
  const t = e.getAttribute(n.PAGE_TYPE), s = e.getAttribute(n.PAGE_LIST);
248
251
  if (!t || !s)
249
252
  return;
250
- const r = u(), i = Number(t), l = B(s);
251
- await r.fetchTemplates(), r.setCollectionWithoutAutoSelection(i), r.loadSelectedTemplates(l);
253
+ const r = l(), i = Number(t), u = _(s);
254
+ await r.fetchTemplates(), r.setCollectionWithoutAutoSelection(i), r.loadSelectedTemplates(u);
252
255
  }
253
256
  _resetStoreState() {
254
- u().$reset();
257
+ l().$reset();
255
258
  }
256
259
  _removeBlockTemplatesFromStore(e) {
257
260
  if (!("getAttribute" in e))
@@ -259,11 +262,11 @@ class H extends y {
259
262
  const t = e.getAttribute(n.PAGE_LIST);
260
263
  if (!t)
261
264
  return;
262
- const s = u(), r = B(t);
265
+ const s = l(), r = _(t);
263
266
  s.removeUnsubscribePages(r);
264
267
  }
265
268
  }
266
269
  export {
267
- C as UNSUBSCRIBE_BLOCK_ID,
268
- H as UnsubscribeBlock
270
+ N as UNSUBSCRIBE_BLOCK_ID,
271
+ W as UnsubscribeBlock
269
272
  };
@@ -1,18 +1,19 @@
1
1
  var T = Object.defineProperty;
2
- var p = (o, s, e) => s in o ? T(o, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[s] = e;
3
- var c = (o, s, e) => p(o, typeof s != "symbol" ? s + "" : s, e);
2
+ var p = (a, s, e) => s in a ? T(a, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[s] = e;
3
+ var c = (a, s, e) => p(a, typeof s != "symbol" ? s + "" : s, e);
4
+ import { useEditorStore as _ } from "../../../stores/editor.js";
4
5
  import { useUnsubscribeStore as l } from "../../../stores/unsubscribe.js";
5
- import { Control as _, UEAttr as i, UIElementType as n } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { Control as g, UEAttr as i, UIElementType as n } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
7
  import { DATA_ATTRIBUTES as d } from "./utils/constants.js";
7
8
  import { parsePageList as h } from "./utils/utils.js";
8
- const g = "ui-elements-unsubscribe", r = {
9
+ const v = "ui-elements-unsubscribe", r = {
9
10
  SELECT_BUTTON: "selectTemplateButton",
10
11
  PREV_BUTTON: "prevButton",
11
12
  NEXT_BUTTON: "nextButton",
12
13
  COUNTER_TEXT: "counterText",
13
14
  PREVIEW_IMAGE: "previewImage"
14
15
  };
15
- class B extends _ {
16
+ class w extends g {
16
17
  constructor() {
17
18
  super(...arguments);
18
19
  c(this, "currentPreviewIndex", 0);
@@ -22,7 +23,7 @@ class B extends _ {
22
23
  c(this, "lastParsedAttribute");
23
24
  }
24
25
  getId() {
25
- return g;
26
+ return v;
26
27
  }
27
28
  getTemplate() {
28
29
  return `
@@ -61,6 +62,8 @@ class B extends _ {
61
62
  }
62
63
  _onButtonClick() {
63
64
  try {
65
+ if (_().isPreviewModeOpen)
66
+ return;
64
67
  const e = l();
65
68
  this._reseedSelectionFromBlock(), e.activeType = e.getSelectedCollection[this.currentPreviewIndex], e.pageSelectionUpdateStatus = !0, e.pageSelectionDrawerStatus = !0;
66
69
  } catch (e) {
@@ -71,8 +74,8 @@ class B extends _ {
71
74
  const e = l();
72
75
  if (e.$reset(), !this.currentNode || !("getAttribute" in this.currentNode))
73
76
  return;
74
- const t = this.currentNode.getAttribute(d.PAGE_TYPE), a = this.currentNode.getAttribute(d.PAGE_LIST);
75
- !t || !a || (e.setCollectionWithoutAutoSelection(Number(t)), e.loadSelectedTemplates(h(a)));
77
+ const t = this.currentNode.getAttribute(d.PAGE_TYPE), o = this.currentNode.getAttribute(d.PAGE_LIST);
78
+ !t || !o || (e.setCollectionWithoutAutoSelection(Number(t)), e.loadSelectedTemplates(h(o)));
76
79
  }
77
80
  _onPrevClick() {
78
81
  this.currentPreviewIndex > 0 && (this.currentPreviewIndex--, this._updatePreview(), this._updateCounter());
@@ -100,8 +103,8 @@ class B extends _ {
100
103
  }
101
104
  }
102
105
  _updateCounter() {
103
- const e = this.currentPreviewIndex + 1, t = this.totalTemplates, a = this.currentPreviewIndex === 0, u = this.currentPreviewIndex >= t - 1;
104
- this.api.setUIEAttribute(r.PREV_BUTTON, i.BUTTON.disabled, a), this.api.setUIEAttribute(r.NEXT_BUTTON, i.BUTTON.disabled, u), this.api.setUIEAttribute(
106
+ const e = this.currentPreviewIndex + 1, t = this.totalTemplates, o = this.currentPreviewIndex === 0, u = this.currentPreviewIndex >= t - 1;
107
+ this.api.setUIEAttribute(r.PREV_BUTTON, i.BUTTON.disabled, o), this.api.setUIEAttribute(r.NEXT_BUTTON, i.BUTTON.disabled, u), this.api.setUIEAttribute(
105
108
  r.COUNTER_TEXT,
106
109
  i.LABEL.text,
107
110
  `${this.api.translate("Showing")} ${e} ${this.api.translate("of")} ${t}`
@@ -142,8 +145,8 @@ class B extends _ {
142
145
  </${n.LABEL}>
143
146
  `;
144
147
  }
145
- _getIconButton(e, t, a) {
146
- const u = a ? `${i.BUTTON.disabled}="true"` : "";
148
+ _getIconButton(e, t, o) {
149
+ const u = o ? `${i.BUTTON.disabled}="true"` : "";
147
150
  return `
148
151
  <${n.BUTTON}
149
152
  id="${e}"
@@ -172,6 +175,6 @@ class B extends _ {
172
175
  }
173
176
  }
174
177
  export {
175
- g as UNSUBSCRIBE_CONTROL_ID,
176
- B as UnsubscribeControl
178
+ v as UNSUBSCRIBE_CONTROL_ID,
179
+ w as UnsubscribeControl
177
180
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- .gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-82128f7d]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-50dac6de]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-50dac6de]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-50dac6de]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-f6a8cb4c]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-f6a8cb4c]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-f6a8cb4c]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-f6a8cb4c]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-f6a8cb4c]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
1
+ .gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-82128f7d]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-50dac6de]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-50dac6de]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-50dac6de]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-ec5c2ac6]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-ec5c2ac6]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-ec5c2ac6]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-ec5c2ac6]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-ec5c2ac6]{object-fit:cover;transform:scale(1)}[data-v-811cff80] .guido__verion-history-view-option-selection-desktop svg,[data-v-811cff80] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-811cff80] .in-segments-wrapper__button_selected,[data-v-811cff80] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-06afaecb]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-06afaecb]{min-height:504px}.iframe-wrapper[data-v-cbafc185]{width:258px}.iframe-scaled[data-v-cbafc185]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
@@ -0,0 +1,3 @@
1
+ export declare const usePreviewInteractionGuard: () => {
2
+ blockPreviewInteractions: (iframe: HTMLIFrameElement) => void;
3
+ };
@@ -1,3 +1,4 @@
1
1
  export declare const useUnsubscribeBlockValidator: () => {
2
2
  validateUnsubscribeBlockUniqueness: (html: string) => boolean;
3
+ validateUnsubscribeBlockHasTemplate: (html: string) => boolean;
3
4
  };
@@ -24,13 +24,5 @@ export declare class ItemsBlock extends Block {
24
24
  getSettingsPanelTitleHtml(): string;
25
25
  getTemplate(): string;
26
26
  allowInnerBlocksDND(): boolean;
27
- canBeSavedAsModule(): boolean;
28
27
  onCreated(node: ImmutableHtmlNode): void;
29
- /**
30
- * Re-seeds nodeConfig from the persisted esd-ext-config when a saved module
31
- * surfaces via document load. Stripo strips esd-ext-config and never restores it
32
- * into nodeConfig, so without this a reused module would reset to defaults.
33
- * Guarded to the nodeConfig-empty case so it runs once and never loops.
34
- */
35
- onDocumentChanged(node: ImmutableHtmlNode): void;
36
28
  }
@@ -55,7 +55,7 @@ export declare function getItemsBlockContainer(currentNode: ImmutableHtmlNode |
55
55
  /**
56
56
  * Gets the node configuration from the Items block.
57
57
  * Uses Stripo V2's getNodeConfig() API.
58
- * Falls back to the persisted esd-ext-config (saved modules), then legacy config block.
58
+ * Falls back to migrating from legacy config block if nodeConfig is empty.
59
59
  * @param currentNode - The current node from the control
60
60
  * @returns The ItemsBlockConfig object or null if not found
61
61
  */