@useinsider/guido 1.0.3-beta.e0403cf → 1.0.3-beta.e32f324

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 (47) hide show
  1. package/README.md +3 -0
  2. package/dist/components/Guido.vue.js +1 -1
  3. package/dist/components/Guido.vue2.js +79 -73
  4. package/dist/components/organisms/header/RightSlot.vue.js +1 -1
  5. package/dist/components/organisms/header/RightSlot.vue2.js +23 -22
  6. package/dist/composables/useGuidoActions.js +19 -9
  7. package/dist/composables/useStripo.js +37 -35
  8. package/dist/config/compiler/unsubscribeCompilerRules.js +33 -28
  9. package/dist/enums/defaults.js +2 -1
  10. package/dist/enums/unsubscribe.js +27 -25
  11. package/dist/extensions/Blocks/Checkbox/control.js +15 -14
  12. package/dist/extensions/Blocks/RadioButton/control.js +18 -17
  13. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +1 -1
  14. package/dist/extensions/DynamicContent/dynamic-content-modal.js +25 -19
  15. package/dist/extensions/DynamicContent/dynamic-content.js +128 -33
  16. package/dist/guido.css +1 -1
  17. package/dist/src/@types/generic.d.ts +27 -6
  18. package/dist/src/components/Guido.vue.d.ts +3 -2
  19. package/dist/src/composables/useGuidoActions.d.ts +9 -0
  20. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +1 -1
  21. package/dist/src/extensions/DynamicContent/dynamic-content-modal.d.ts +9 -2
  22. package/dist/src/extensions/DynamicContent/dynamic-content.d.ts +52 -2
  23. package/dist/src/stores/config.d.ts +3 -2
  24. package/dist/src/stores/dynamic-content.d.ts +3 -3
  25. package/dist/src/stores/editor.d.ts +1 -1
  26. package/dist/src/stores/onboarding.d.ts +1 -1
  27. package/dist/src/stores/preview.d.ts +1 -1
  28. package/dist/src/stores/recommendation.d.ts +1 -1
  29. package/dist/src/stores/save-as-template.d.ts +1 -1
  30. package/dist/src/stores/toaster.d.ts +1 -1
  31. package/dist/src/stores/unsubscribe.d.ts +1 -1
  32. package/dist/src/stores/version-history.d.ts +1 -1
  33. package/dist/src/utils/environmentUtil.d.ts +5 -0
  34. package/dist/src/utils/genericUtil.d.ts +1 -0
  35. package/dist/stores/config.js +5 -5
  36. package/dist/stores/dynamic-content.js +2 -2
  37. package/dist/stores/editor.js +1 -1
  38. package/dist/stores/onboarding.js +27 -27
  39. package/dist/stores/preview.js +1 -1
  40. package/dist/stores/recommendation.js +3 -3
  41. package/dist/stores/save-as-template.js +2 -2
  42. package/dist/stores/toaster.js +1 -1
  43. package/dist/stores/unsubscribe.js +1 -1
  44. package/dist/stores/version-history.js +4 -4
  45. package/dist/utils/environmentUtil.js +4 -0
  46. package/dist/utils/genericUtil.js +20 -9
  47. package/package.json +1 -1
@@ -1,25 +1,26 @@
1
- import { useTranslations as e } from "../composables/useTranslations.js";
2
- const s = e(), R = {
1
+ import { useTranslations as n } from "../composables/useTranslations.js";
2
+ import { getEnvironmentPrefix as R } from "../utils/environmentUtil.js";
3
+ const e = n(), I = {
3
4
  UNSUBSCRIBE_LINK_TYPE: 1,
4
5
  PREFERENCES_LINK_TYPE: 3
5
- }, S = {
6
+ }, r = {
6
7
  UNSUBSCRIBE_LINK_REGEX: /{{ins-unsubscribe-link}}/g,
7
8
  DATA_OGSB_BUTTON_CSS_REGEX: "\\[data-ogsb\\]\\s*\\.es-button\\.es-button-[0-9]+\\s*\\{(?:[^\\}]*)\\}",
8
9
  GLOBAL_UNSUBSCRIBE_LINK_REGEX: /{{ins-global-unsubscribe-link}}/g,
9
10
  PREFERENCES_UNSUBSCRIBE_LINK_REGEX: /{{ins-preferences-unsubscribe-link}}/g
10
- }, N = {
11
- UNSUBSCRIBE_URL: "https://mail.useinsider.com/user/v1/unsub",
12
- PREFERENCES_URL: "https://mail.useinsider.com/user/v1/prefs"
13
- }, _ = {
11
+ }, s = R(), _ = {
12
+ UNSUBSCRIBE_URL: `https://mail.${s}.com/user/v1/unsub`,
13
+ PREFERENCES_URL: `https://mail.${s}.com/user/v1/prefs`
14
+ }, i = "iid", B = {
14
15
  name: "Global Unsubscribe",
15
16
  sendGridId: "G"
16
- }, I = "https://academy.useinsider.com/docs/global-unsubscribe-preference-center", r = "/email/unsubscribe-pages", E = {
17
+ }, C = "https://academy.useinsider.com/docs/global-unsubscribe-preference-center", c = "/email/unsubscribe-pages", E = {
17
18
  GLOBAL_UNSUBSCRIBE: 1,
18
19
  GLOBAL_UNSUBSCRIBE_CONFIRMATION_PAGE: 2,
19
20
  SUBSCRIPTION_PREFERENCE_CENTER: 3,
20
21
  SUBSCRIPTION_PREFERENCE_CONFIRMATION: 4,
21
22
  RESUBSCRIBE: 5
22
- }, i = {
23
+ }, t = {
23
24
  [E.GLOBAL_UNSUBSCRIBE]: [
24
25
  E.GLOBAL_UNSUBSCRIBE,
25
26
  E.GLOBAL_UNSUBSCRIBE_CONFIRMATION_PAGE,
@@ -29,26 +30,27 @@ const s = e(), R = {
29
30
  E.SUBSCRIPTION_PREFERENCE_CENTER,
30
31
  E.SUBSCRIPTION_PREFERENCE_CONFIRMATION
31
32
  ]
32
- }, B = {
33
- [E.GLOBAL_UNSUBSCRIBE]: s("unsubscription-preference.type-global-unsubscribe"),
34
- [E.GLOBAL_UNSUBSCRIBE_CONFIRMATION_PAGE]: s("unsubscription-preference.type-global-unsubscription-confirmation"),
35
- [E.RESUBSCRIBE]: s("unsubscription-preference.type-resubscribe"),
36
- [E.SUBSCRIPTION_PREFERENCE_CENTER]: s("unsubscription-preference.type-subscription-preferences-center"),
37
- [E.SUBSCRIPTION_PREFERENCE_CONFIRMATION]: s("unsubscription-preference.type-subscription-preferences-confirmation")
38
- }, C = {
33
+ }, U = {
34
+ [E.GLOBAL_UNSUBSCRIBE]: e("unsubscription-preference.type-global-unsubscribe"),
35
+ [E.GLOBAL_UNSUBSCRIBE_CONFIRMATION_PAGE]: e("unsubscription-preference.type-global-unsubscription-confirmation"),
36
+ [E.RESUBSCRIBE]: e("unsubscription-preference.type-resubscribe"),
37
+ [E.SUBSCRIPTION_PREFERENCE_CENTER]: e("unsubscription-preference.type-subscription-preferences-center"),
38
+ [E.SUBSCRIPTION_PREFERENCE_CONFIRMATION]: e("unsubscription-preference.type-subscription-preferences-confirmation")
39
+ }, o = {
39
40
  default: "{{ins-unsubscribe-link}}",
40
41
  [E.GLOBAL_UNSUBSCRIBE]: "{{ins-global-unsubscribe-link}}",
41
42
  [E.SUBSCRIPTION_PREFERENCE_CENTER]: "{{ins-preferences-unsubscribe-link}}"
42
43
  };
43
44
  export {
44
- I as ACADEMY_LINK,
45
- _ as DEFAULT_UNSUBSCRIBE_GROUP,
46
- S as LINK_REGEXES,
47
- R as LINK_TYPES,
48
- C as MERGE_TAGS,
45
+ C as ACADEMY_LINK,
46
+ B as DEFAULT_UNSUBSCRIBE_GROUP,
47
+ i as INSIDER_ID,
48
+ r as LINK_REGEXES,
49
+ I as LINK_TYPES,
50
+ o as MERGE_TAGS,
49
51
  E as PAGE_TYPES,
50
- i as TYPE_COLLECTIONS,
51
- B as TYPE_TRANSLATIONS,
52
- r as UNSUBSCRIBE_PAGES_LINK,
53
- N as URLS
52
+ t as TYPE_COLLECTIONS,
53
+ U as TYPE_TRANSLATIONS,
54
+ c as UNSUBSCRIBE_PAGES_LINK,
55
+ _ as URLS
54
56
  };
@@ -2,9 +2,9 @@ var d = Object.defineProperty;
2
2
  var l = (r, n, e) => n in r ? d(r, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[n] = e;
3
3
  var o = (r, n, e) => l(r, typeof n != "symbol" ? n + "" : n, e);
4
4
  import { useHttp as L } from "../../../composables/useHttp.js";
5
- import { DEFAULT_UNSUBSCRIBE_GROUP as u } from "../../../enums/unsubscribe.js";
5
+ import { DEFAULT_UNSUBSCRIBE_GROUP as c } from "../../../enums/unsubscribe.js";
6
6
  import { Control as h, UIElementType as i, UEAttr as t, ModificationDescription as p } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
7
- const m = "ui-elements-checkbox", a = "select", { get: C } = L();
7
+ const m = "ui-elements-checkbox", u = "select", { get: C } = L();
8
8
  class S extends h {
9
9
  constructor() {
10
10
  super(...arguments);
@@ -16,14 +16,15 @@ class S extends h {
16
16
  return m;
17
17
  }
18
18
  _setFormValues() {
19
- if (this.currentNode && "getAttribute" in this.currentNode) {
19
+ if (this.selectedUnsubGroup = "", this.currentNode && "getAttribute" in this.currentNode) {
20
20
  const e = this.currentNode.getAttribute("id");
21
21
  if (e) {
22
- const s = e === u.sendGridId ? e : Number(e);
23
- s && (this.selectedUnsubGroup = s, this.api.updateValues({
24
- [a]: this.selectedUnsubGroup
25
- }));
22
+ const s = e === c.sendGridId ? e : Number(e);
23
+ s && (this.selectedUnsubGroup = s);
26
24
  }
25
+ this.api.updateValues({
26
+ [u]: this.selectedUnsubGroup
27
+ });
27
28
  }
28
29
  }
29
30
  _getLabel(e, s = `${Math.random()}`) {
@@ -54,7 +55,7 @@ class S extends h {
54
55
  </${i.LABEL}>
55
56
 
56
57
  <${i.SELECTPICKER}
57
- ${t.SELECTPICKER.name}="${a}"
58
+ ${t.SELECTPICKER.name}="${u}"
58
59
  ${t.SELECTPICKER.placeholder}="${this.api.translate("Select Unsubscribe Group")}">
59
60
  ${this._getSelect()}
60
61
  </${i.SELECTPICKER}>
@@ -66,21 +67,21 @@ class S extends h {
66
67
  this.currentNode && this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute("id", e.toString()).apply(new p(`Updated text to ${e}`));
67
68
  }
68
69
  _listenToFormUpdates() {
69
- this.api.onValueChanged(a, (e) => this._onSelectChange(e));
70
+ this.api.onValueChanged(u, (e) => this._onSelectChange(e));
70
71
  }
71
72
  onTemplateNodeUpdated(e) {
72
- this.currentNode = e;
73
+ this.currentNode = e, this._setFormValues();
73
74
  }
74
75
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
75
76
  async onRender() {
76
77
  const e = await C(
77
78
  "/unsubscribe-groups/unsubscribe-list"
78
- ), s = [u, ...e.data], E = s.map((c) => ({
79
- [t.SELECT_ITEM.text]: c.name,
80
- [t.SELECT_ITEM.value]: c.sendGridId
79
+ ), s = [c, ...e.data], E = s.map((a) => ({
80
+ [t.SELECT_ITEM.text]: a.name,
81
+ [t.SELECT_ITEM.value]: a.sendGridId
81
82
  }));
82
83
  this.unsubList = s, this.api.setUIEAttribute(
83
- a,
84
+ u,
84
85
  t.SELECTPICKER.items,
85
86
  E
86
87
  ), this._setFormValues(), this._listenToFormUpdates();
@@ -2,10 +2,10 @@ var d = Object.defineProperty;
2
2
  var l = (r, n, t) => n in r ? d(r, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[n] = t;
3
3
  var o = (r, n, t) => l(r, typeof n != "symbol" ? n + "" : n, t);
4
4
  import { useHttp as L } from "../../../composables/useHttp.js";
5
- import { DEFAULT_UNSUBSCRIBE_GROUP as E } from "../../../enums/unsubscribe.js";
5
+ import { DEFAULT_UNSUBSCRIBE_GROUP as c } from "../../../enums/unsubscribe.js";
6
6
  import { Control as p, UIElementType as i, UEAttr as e, ModificationDescription as h } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
7
- const m = "ui-elements-radio-button", a = "select", { get: $ } = L();
8
- class _ extends p {
7
+ const m = "ui-elements-radio-button", u = "select", { get: $ } = L();
8
+ class S extends p {
9
9
  constructor() {
10
10
  super(...arguments);
11
11
  o(this, "currentNode");
@@ -16,15 +16,16 @@ class _ extends p {
16
16
  return m;
17
17
  }
18
18
  _setFormValues() {
19
- if (this.currentNode && "getAttribute" in this.currentNode) {
19
+ if (this.selectedUnsubGroup = "", this.currentNode && "getAttribute" in this.currentNode) {
20
20
  const t = this.currentNode.getAttribute("id");
21
21
  if (t) {
22
- const s = t === E.sendGridId ? t : Number(t);
23
- s && (this.selectedUnsubGroup = s, this.api.updateValues({
24
- [a]: this.selectedUnsubGroup
25
- }));
22
+ const s = t === c.sendGridId ? t : Number(t);
23
+ s && (this.selectedUnsubGroup = s);
26
24
  }
27
25
  }
26
+ this.api.updateValues({
27
+ [u]: this.selectedUnsubGroup
28
+ });
28
29
  }
29
30
  _getLabel(t, s = `${Math.random()}`) {
30
31
  return `
@@ -54,7 +55,7 @@ class _ extends p {
54
55
  </${i.LABEL}>
55
56
 
56
57
  <${i.SELECTPICKER}
57
- ${e.SELECTPICKER.name}="${a}"
58
+ ${e.SELECTPICKER.name}="${u}"
58
59
  ${e.SELECTPICKER.placeholder}="${this.api.translate("Select Unsubscribe Group")}">
59
60
  ${this._getSelect()}
60
61
  </${i.SELECTPICKER}>
@@ -66,27 +67,27 @@ class _ extends p {
66
67
  this.currentNode && this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute("id", t.toString()).apply(new h(`Updated text to ${t}`));
67
68
  }
68
69
  _listenToFormUpdates() {
69
- this.api.onValueChanged(a, (t) => this._onSelectChange(t));
70
+ this.api.onValueChanged(u, (t) => this._onSelectChange(t));
70
71
  }
71
72
  onTemplateNodeUpdated(t) {
72
- this.currentNode = t;
73
+ this.currentNode = t, this._setFormValues();
73
74
  }
74
75
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
75
76
  async onRender() {
76
77
  const t = await $(
77
78
  "/unsubscribe-groups/unsubscribe-list"
78
- ), s = [E, ...t.data], c = s.map((u) => ({
79
- [e.SELECT_ITEM.text]: u.name,
80
- [e.SELECT_ITEM.value]: u.sendGridId
79
+ ), s = [c, ...t.data], E = s.map((a) => ({
80
+ [e.SELECT_ITEM.text]: a.name,
81
+ [e.SELECT_ITEM.value]: a.sendGridId
81
82
  }));
82
83
  this.unsubList = s, this.api.setUIEAttribute(
83
- a,
84
+ u,
84
85
  e.SELECTPICKER.items,
85
- c
86
+ E
86
87
  ), this._setFormValues(), this._listenToFormUpdates();
87
88
  }
88
89
  }
89
90
  export {
90
91
  m as CONTROL_BLOCK_ID,
91
- _ as RadioButtonControl
92
+ S as RadioButtonControl
92
93
  };
@@ -133,7 +133,7 @@ const a = c(), d = () => ({
133
133
  filterSelectionDrawerStatus: !1,
134
134
  filterList: {},
135
135
  filterGroup: 1
136
- }), G = p("recommendationExtension", {
136
+ }), G = p("guidoRecommendationExtension", {
137
137
  state: () => d(),
138
138
  getters: {
139
139
  hasFilters: (e) => !!e.recommendationConfigs.filters.length,
@@ -1,27 +1,33 @@
1
- var i = Object.defineProperty;
2
- var d = (t, e, n) => e in t ? i(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
- var a = (t, e, n) => d(t, typeof e != "symbol" ? e + "" : e, n);
4
- class y {
1
+ var d = Object.defineProperty;
2
+ var m = (t, e, n) => e in t ? d(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
+ var c = (t, e, n) => m(t, typeof e != "symbol" ? e + "" : e, n);
4
+ class C {
5
5
  constructor() {
6
- a(this, "selectedDynamicContent", null);
7
- a(this, "dataSelectCallback", () => {
6
+ c(this, "selectedDynamicContent", null);
7
+ c(this, "dataSelectCallback", () => {
8
8
  });
9
+ c(this, "closeDynamicContentHandler", null);
9
10
  }
10
- openMergeTagsLibrary(e, n) {
11
- this.dataSelectCallback = n, this.selectedDynamicContent = e;
12
- const s = new CustomEvent("dynamic-content:open", {
13
- detail: {
14
- attribute: e
15
- }
11
+ /**
12
+ * Opens the merge tags library modal
13
+ * @param dynamicContent - Currently selected dynamic content (if any)
14
+ * @param onDataSelectCallback - Callback invoked when user selects a merge tag
15
+ * @param position - Optional position data to place the modal
16
+ */
17
+ openMergeTagsLibrary(e, n, l) {
18
+ this.closeDynamicContentHandler && (document.removeEventListener("dynamic-content:close", this.closeDynamicContentHandler), this.closeDynamicContentHandler = null), this.dataSelectCallback = n, this.selectedDynamicContent = e;
19
+ const o = {
20
+ attribute: e,
21
+ position: l
22
+ }, s = new CustomEvent("dynamic-content:open", {
23
+ detail: o
16
24
  });
17
- document.dispatchEvent(s);
18
- const o = (l) => {
19
- const c = l.detail;
20
- this.selectedDynamicContent = c, c.text && c.value && this.dataSelectCallback(c), document.removeEventListener("dynamic-content:close", o);
21
- };
22
- document.addEventListener("dynamic-content:close", o);
25
+ document.dispatchEvent(s), this.closeDynamicContentHandler = (i) => {
26
+ const a = i.detail;
27
+ this.selectedDynamicContent = a, a.text && a.value && this.dataSelectCallback(a);
28
+ }, document.addEventListener("dynamic-content:close", this.closeDynamicContentHandler);
23
29
  }
24
30
  }
25
31
  export {
26
- y as ExternalMergeTagsLibrary
32
+ C as ExternalMergeTagsLibrary
27
33
  };
@@ -1,61 +1,156 @@
1
- var o = Object.defineProperty;
2
- var g = (n, t, e) => t in n ? o(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
3
- var i = (n, t, e) => g(n, typeof t != "symbol" ? t + "" : t, e);
4
- import { dynamicContentToMergeTags as m } from "../../utils/genericUtil.js";
5
- import { UIElement as c, UIElementType as r, UEAttr as l, UIElementTagRegistry as d } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
- import { ExternalMergeTagsLibrary as T } from "./dynamic-content-modal.js";
7
- const a = "external-dynamic-content-ui-element", y = "button-add-dynamic-content";
8
- class p extends d {
9
- registerUiElements(t) {
10
- t[r.MERGETAGS] = a;
1
+ var c = Object.defineProperty;
2
+ var g = (o, s, t) => s in o ? c(o, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[s] = t;
3
+ var r = (o, s, t) => g(o, typeof s != "symbol" ? s + "" : s, t);
4
+ import { mergeTagToDynamicContent as d, dynamicContentToMergeTags as m } from "../../utils/genericUtil.js";
5
+ import { UIElement as u, UIElementType as a, UEAttr as h, UIElementTagRegistry as T } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { ExternalMergeTagsLibrary as y } from "./dynamic-content-modal.js";
7
+ const l = "external-dynamic-content-ui-element", C = "button-add-dynamic-content";
8
+ class p extends T {
9
+ registerUiElements(s) {
10
+ s[a.MERGETAGS] = l;
11
11
  }
12
12
  }
13
- class C extends c {
13
+ class b extends u {
14
14
  constructor() {
15
15
  super(...arguments);
16
- i(this, "mergeTagsButton", null);
17
- i(this, "listener", () => {
18
- });
19
- i(this, "mergeTagsLibrary", null);
20
- i(this, "selectedDynamicContent", { value: "", text: "" });
16
+ r(this, "mergeTagsButton", null);
17
+ r(this, "mergeTagsLibrary", null);
18
+ r(this, "clickListener", null);
19
+ r(this, "lastClickedElement", null);
20
+ r(this, "lastClickedFrame", null);
21
+ r(this, "mousedownListener", null);
21
22
  }
22
23
  getId() {
23
- return a;
24
+ return l;
24
25
  }
25
- onRender(e) {
26
- var s;
27
- this.listener = this._onClick.bind(this), this.mergeTagsButton = e.querySelector("#guido__btn-add-dynamic-content"), (s = this.mergeTagsButton) == null || s.addEventListener("click", this.listener);
26
+ onRender(t) {
27
+ var e;
28
+ this.mergeTagsButton = t.querySelector("#guido__btn-add-dynamic-content"), this.clickListener = this._onClick.bind(this), (e = this.mergeTagsButton) == null || e.addEventListener("click", this.clickListener), this._setupCoordinateTracking();
28
29
  }
29
30
  onDestroy() {
30
- var e;
31
- (e = this.mergeTagsButton) == null || e.removeEventListener("click", this.listener);
31
+ var t;
32
+ this.clickListener && ((t = this.mergeTagsButton) == null || t.removeEventListener("click", this.clickListener)), this._cleanupCoordinateTracking(), this.mergeTagsButton = null, this.clickListener = null, this.lastClickedElement = null, this.lastClickedFrame = null;
32
33
  }
33
34
  _onClick() {
34
35
  this.openMergeTagLibrary();
35
36
  }
37
+ /**
38
+ * Called when Stripo detects merge tag interactions
39
+ * This is the key method that receives notifications about merge tag clicks
40
+ * @param name - Attribute name (e.g., 'mergeTag')
41
+ * @param value - MergeTag object with label, value, selStart, selEnd, etc.
42
+ */
43
+ onAttributeUpdated(t, e) {
44
+ if (t !== "mergeTag" || !(e != null && e.value))
45
+ return;
46
+ const n = d(e), i = this._getLastClickedPosition();
47
+ this.lastClickedElement = null, this.lastClickedFrame = null, this._openDynamicContentLibrary(n, i);
48
+ }
36
49
  openMergeTagLibrary() {
37
- this.mergeTagsLibrary || (this.mergeTagsLibrary = new T()), this.mergeTagsLibrary.openMergeTagsLibrary(
38
- this.selectedDynamicContent,
39
- (e) => {
40
- this.api.onValueChanged(m([e])[0]);
41
- }
50
+ const t = this._getButtonPosition(), e = { value: "", text: "" };
51
+ this._openDynamicContentLibrary(e, t);
52
+ }
53
+ /**
54
+ * Opens the dynamic content library for external apps
55
+ * Dispatches event with dynamic content data and position
56
+ */
57
+ _openDynamicContentLibrary(t, e) {
58
+ this.mergeTagsLibrary || (this.mergeTagsLibrary = new y()), this.mergeTagsLibrary.openMergeTagsLibrary(
59
+ t,
60
+ (n) => {
61
+ n.text && n.value ? this.api.triggerValueChange(m([n])[0]) : this.api.triggerValueChange(null);
62
+ },
63
+ e
42
64
  );
43
65
  }
66
+ /**
67
+ * Gets the button's position on the screen
68
+ * Used to position the modal or anything else near the button
69
+ */
70
+ _getButtonPosition() {
71
+ if (!this.mergeTagsButton)
72
+ return { x: 0, y: 0, width: 0, height: 0 };
73
+ const { x: t, y: e, width: n, height: i } = this.mergeTagsButton.getBoundingClientRect();
74
+ return { x: t, y: e, width: n, height: i };
75
+ }
76
+ /**
77
+ * Sets up coordinate tracking for merge tag clicks in the iframe
78
+ * Workaround from Stripo intercepting click events - tracks mousedown events instead
79
+ */
80
+ _setupCoordinateTracking() {
81
+ var e;
82
+ const t = this._getEditorFrame();
83
+ (e = t == null ? void 0 : t.contentWindow) != null && e.document && (this.mousedownListener = (n) => {
84
+ const i = this._findMergeTagElement(n.target);
85
+ i && (this.lastClickedElement = i, this.lastClickedFrame = t);
86
+ }, t.contentWindow.document.addEventListener("mousedown", this.mousedownListener));
87
+ }
88
+ /**
89
+ * Gets the editor iframe element
90
+ */
91
+ _getEditorFrame() {
92
+ var n;
93
+ const t = document.querySelector("ui-editor");
94
+ if (!(t != null && t.shadowRoot))
95
+ return null;
96
+ const e = t.shadowRoot.querySelector("iframe");
97
+ return (n = e == null ? void 0 : e.contentWindow) != null && n.document ? e : null;
98
+ }
99
+ /**
100
+ * Finds merge tag element in the element tree
101
+ */
102
+ _findMergeTagElement(t) {
103
+ return t.closest(".target, .esd-merge-tag");
104
+ }
105
+ /**
106
+ * Gets the position of the last clicked element, calculated on-demand
107
+ * This ensures we always use the correct element's position even with rapid clicks
108
+ */
109
+ _getLastClickedPosition() {
110
+ if (!(!this.lastClickedElement || !this.lastClickedFrame))
111
+ return this._calculateAbsolutePosition(this.lastClickedFrame, this.lastClickedElement);
112
+ }
113
+ /**
114
+ * Calculates absolute position of element relative to page
115
+ */
116
+ _calculateAbsolutePosition(t, e) {
117
+ const n = t.getBoundingClientRect(), i = e.getBoundingClientRect();
118
+ return {
119
+ x: n.x + i.x,
120
+ y: n.y + i.y,
121
+ width: i.width,
122
+ height: i.height
123
+ };
124
+ }
125
+ /**
126
+ * Cleans up the coordinate tracking event listener
127
+ */
128
+ _cleanupCoordinateTracking() {
129
+ var e;
130
+ if (!this.mousedownListener)
131
+ return;
132
+ const t = this._getEditorFrame();
133
+ (e = t == null ? void 0 : t.contentWindow) != null && e.document && t.contentWindow.document.removeEventListener("mousedown", this.mousedownListener), this.mousedownListener = null;
134
+ }
135
+ /**
136
+ * Returns the HTML template for the "Add Dynamic Content" button
137
+ * This button is placed in the merge tags selector UI
138
+ */
44
139
  getTemplate() {
45
140
  return `
46
141
  <div>
47
- <${r.BUTTON}
142
+ <${a.BUTTON}
48
143
  id="guido__btn-add-dynamic-content"
49
144
  class="btn btn-primary"
50
- ${l.BUTTON.name}="${y}">
51
- <${r.ICON} src="plus" class="icon icon-button color-primary">
52
- </${r.ICON}>
145
+ ${h.BUTTON.name}="${C}">
146
+ <${a.ICON} src="plus" class="icon icon-button color-primary">
147
+ </${a.ICON}>
53
148
  ${this.api.translate("Add Dynamic Content")}
54
- </${r.BUTTON}>
149
+ </${a.BUTTON}>
55
150
  </div>`;
56
151
  }
57
152
  }
58
153
  export {
59
154
  p as DynamicContentTagRegistry,
60
- C as DynamicContentUiElementExtension
155
+ b as DynamicContentUiElementExtension
61
156
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- .gap-16[data-v-6562e38c],.gap-16[data-v-1ccb6d4a]{gap:16px}[data-v-0502bceb] .in-button-v2__wrapper{line-height:0}[data-v-b5997368] .in-segments-wrapper .in-tooltip-wrapper__box{text-align:left}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history-wrapper[data-v-52a77eec]{gap:8px}.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}.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}.guido-loading__wrapper[data-v-dced5582]{height:100%;top:75px}.guido-editor__wrapper[data-v-3c910a93],.guido-editor__container[data-v-3c910a93]{width:100%;height:calc(100vh - 128px)}.guido-editor__no-header[data-v-3c910a93]{height:calc(100vh - 75px)}[data-v-70835920] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-a86fc486]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-a86fc486]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-a86fc486]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-a86fc486]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-a86fc486]{object-fit:cover;transform:scale(1)}[data-v-dd57102d] .guido__verion-history-view-option-selection-desktop svg,[data-v-dd57102d] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-dd57102d] .in-segments-wrapper__button_selected,[data-v-dd57102d] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-7a09985c]{gap:16px}[data-v-c2adc57d] .in-progress-wrapper__progress p span:last-child{display:none!important}.desktop-preview-container[data-v-2dd60b0c],[data-v-2dd60b0c] .desktop-preview-container .in-container{min-height:720px!important;height:100%}.cropped-text[data-v-f20b3a9b]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[data-v-d3c52b44] .vueperslides__bullets,[data-v-a408dcea] .vueperslides__bullets{pointer-events:none!important}[data-v-a408dcea] .vueperslides__parallax-wrapper{height:110px!important}
1
+ .gap-16[data-v-6562e38c],.gap-16[data-v-1ccb6d4a]{gap:16px}[data-v-0502bceb] .in-button-v2__wrapper{line-height:0}[data-v-b5997368] .in-segments-wrapper .in-tooltip-wrapper__box{text-align:left}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history-wrapper[data-v-52a77eec]{gap:8px}.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}.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}.guido-loading__wrapper[data-v-dced5582]{height:100%;top:75px}.guido-editor__wrapper[data-v-23c413c9],.guido-editor__container[data-v-23c413c9]{width:100%;height:calc(100vh - 128px)}.guido-editor__no-header[data-v-23c413c9]{height:calc(100vh - 75px)}[data-v-70835920] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-a86fc486]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-a86fc486]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-a86fc486]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-a86fc486]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-a86fc486]{object-fit:cover;transform:scale(1)}[data-v-dd57102d] .guido__verion-history-view-option-selection-desktop svg,[data-v-dd57102d] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-dd57102d] .in-segments-wrapper__button_selected,[data-v-dd57102d] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-7a09985c]{gap:16px}[data-v-c2adc57d] .in-progress-wrapper__progress p span:last-child{display:none!important}.desktop-preview-container[data-v-2dd60b0c],[data-v-2dd60b0c] .desktop-preview-container .in-container{min-height:720px!important;height:100%}.cropped-text[data-v-f20b3a9b]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[data-v-d3c52b44] .vueperslides__bullets,[data-v-a408dcea] .vueperslides__bullets{pointer-events:none!important}[data-v-a408dcea] .vueperslides__parallax-wrapper{height:110px!important}
@@ -7,6 +7,7 @@ type Features = {
7
7
  saveAsTemplate?: boolean;
8
8
  versionHistory?: boolean;
9
9
  testMessage?: boolean;
10
+ displayConditions?: boolean;
10
11
  };
11
12
  type Partner = {
12
13
  partnerName: string;
@@ -37,15 +38,39 @@ export type TemplateConfig = {
37
38
  selectedUnsubscribePages?: number[];
38
39
  variationId?: string;
39
40
  };
40
- export type DynamicContent = {
41
+ export type BaseDynamicContent = {
41
42
  value: string;
42
- text: string;
43
43
  fallback?: string;
44
44
  format?: {
45
45
  key: string;
46
46
  value: string;
47
47
  };
48
48
  };
49
+ export type DynamicContent = BaseDynamicContent & {
50
+ text: string;
51
+ };
52
+ export type MergeTag = BaseDynamicContent & {
53
+ label: string;
54
+ selStart?: number;
55
+ selEnd?: number;
56
+ hidden?: boolean;
57
+ };
58
+ export type PositionData = {
59
+ x: number;
60
+ y: number;
61
+ width?: number;
62
+ height?: number;
63
+ };
64
+ export type DynamicContentEvent = {
65
+ attribute: DynamicContent;
66
+ position?: PositionData;
67
+ };
68
+ export type MergeTagClickEvent = {
69
+ position: PositionData;
70
+ element: HTMLElement;
71
+ tagValue: string | null;
72
+ tagLabel: string | null;
73
+ };
49
74
  export interface EmailHeader {
50
75
  senderName: string;
51
76
  subject: string;
@@ -61,10 +86,6 @@ export interface TooltipOptions {
61
86
  };
62
87
  preventXss?: boolean;
63
88
  }
64
- export type MergeTag = {
65
- label: string;
66
- value: string;
67
- };
68
89
  export type TextValueObject = {
69
90
  text: string;
70
91
  value: string;
@@ -1,4 +1,4 @@
1
- import type { DynamicContent, GuidoConfig, TemplateConfig } from '@@/Types/generic';
1
+ import type { DynamicContent, GuidoConfig, PositionData, TemplateConfig } from '@@/Types/generic';
2
2
  import type { SavedTemplateDetails } from '@@/Types/stripo';
3
3
  type __VLS_Props = {
4
4
  templateId: string;
@@ -20,13 +20,14 @@ declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__
20
20
  hasChanges: import("vue").ComputedRef<boolean>;
21
21
  saveSilent: () => Promise<SavedTemplateDetails | undefined> | undefined;
22
22
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
23
- "dynamic-content:open": (detail: DynamicContent | null) => void;
23
+ "dynamic-content:open": (detail: DynamicContent | null, position?: PositionData | undefined) => void;
24
24
  back: () => void;
25
25
  "save:start": () => void;
26
26
  "save:complete": (data: Omit<SavedTemplateDetails, "forceRecreate">) => void;
27
27
  "on-change": (hasChanges: boolean) => void;
28
28
  ready: () => void;
29
29
  "onboarding:finished": () => void;
30
+ "test-email:click": () => void;
30
31
  }, string, Readonly<import("vue").ExtractPropTypes<__VLS_TypePropsToOption<__VLS_Props>>>, {}>;
31
32
  export default _default;
32
33
  type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
@@ -3,9 +3,11 @@ import type { InjectionKey } from 'vue';
3
3
  export type BackHandler = () => void;
4
4
  export type SaveStartHandler = () => void;
5
5
  export type SaveCompleteHandler = (template: Omit<SavedTemplateDetails, 'forceRecreate'>) => void;
6
+ export type TestEmailClickHandler = () => void;
6
7
  export declare const BACK_KEY: InjectionKey<BackHandler>;
7
8
  export declare const SAVE_START_KEY: InjectionKey<SaveStartHandler>;
8
9
  export declare const SAVE_COMPLETE_KEY: InjectionKey<SaveCompleteHandler>;
10
+ export declare const TEST_EMAIL_CLICK_KEY: InjectionKey<TestEmailClickHandler>;
9
11
  /**
10
12
  * Provides Guido action handlers to child components
11
13
  */
@@ -13,6 +15,7 @@ export declare const provideGuidoActions: (actions: {
13
15
  onBack: BackHandler;
14
16
  onSaveStart: SaveStartHandler;
15
17
  onSaveComplete: SaveCompleteHandler;
18
+ onTestEmailClick: TestEmailClickHandler;
16
19
  }) => void;
17
20
  /**
18
21
  * Provides individual Guido action handlers
@@ -35,6 +38,11 @@ export declare const useSaveStart: () => SaveStartHandler;
35
38
  * @returns Save complete function
36
39
  */
37
40
  export declare const useSaveComplete: () => SaveCompleteHandler;
41
+ /**
42
+ * Hook to use the test email click handler
43
+ * @returns Test email click function
44
+ */
45
+ export declare const useTestEmailClick: () => TestEmailClickHandler;
38
46
  /**
39
47
  * Convenience hook to get all Guido actions
40
48
  * @returns Object containing all action handlers
@@ -43,4 +51,5 @@ export declare const useGuidoActions: () => {
43
51
  back: BackHandler;
44
52
  saveStart: SaveStartHandler;
45
53
  saveComplete: SaveCompleteHandler;
54
+ testEmailClick: TestEmailClickHandler;
46
55
  };
@@ -34,7 +34,7 @@ interface StoreState {
34
34
  filterList: FiltersResponse;
35
35
  filterGroup: number;
36
36
  }
37
- export declare const useRecommendationExtensionStore: import("pinia").StoreDefinition<"recommendationExtension", StoreState, {
37
+ export declare const useRecommendationExtensionStore: import("pinia").StoreDefinition<"guidoRecommendationExtension", StoreState, {
38
38
  hasFilters: (state: {
39
39
  recommendationCampaignUrls: Record<string, string>;
40
40
  recommendationConfigs: {
@@ -1,6 +1,13 @@
1
- import type { DynamicContent } from '@@/Types/generic';
1
+ import type { DynamicContent, PositionData } from '@@/Types/generic';
2
2
  export declare class ExternalMergeTagsLibrary {
3
3
  selectedDynamicContent: DynamicContent | null;
4
4
  dataSelectCallback: (data: DynamicContent) => void;
5
- openMergeTagsLibrary(dynamicContent: DynamicContent, onDataSelectCallback: (data: DynamicContent) => void): void;
5
+ private closeDynamicContentHandler;
6
+ /**
7
+ * Opens the merge tags library modal
8
+ * @param dynamicContent - Currently selected dynamic content (if any)
9
+ * @param onDataSelectCallback - Callback invoked when user selects a merge tag
10
+ * @param position - Optional position data to place the modal
11
+ */
12
+ openMergeTagsLibrary(dynamicContent: DynamicContent, onDataSelectCallback: (data: DynamicContent) => void, position?: PositionData): void;
6
13
  }