mail-editor-pancake 0.0.9 → 0.1.0

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 (42) hide show
  1. package/README.md +461 -303
  2. package/package.json +2 -2
  3. package/packages/blocks/dist/index.js +153 -150
  4. package/packages/blocks/dist/index.js.map +1 -1
  5. package/packages/blocks/src/html.ts +11 -3
  6. package/packages/blocks/src/text.ts +8 -3
  7. package/packages/core/dist/index.d.ts +220 -10
  8. package/packages/core/dist/index.js +3963 -2250
  9. package/packages/core/dist/index.js.map +1 -1
  10. package/packages/core/dist/style.css +1 -1
  11. package/packages/core/package.json +1 -1
  12. package/packages/core/src/editor/BlockCodeModal.ts +84 -2
  13. package/packages/core/src/editor/Canvas.ts +182 -26
  14. package/packages/core/src/editor/Editor.ts +414 -68
  15. package/packages/core/src/editor/ExportModal.ts +1 -1
  16. package/packages/core/src/editor/ImportDocModal.ts +129 -0
  17. package/packages/core/src/editor/InlineEditor.ts +238 -24
  18. package/packages/core/src/editor/LeftPanel.ts +55 -14
  19. package/packages/core/src/editor/Modal.ts +20 -1
  20. package/packages/core/src/editor/PreviewModal.ts +2 -2
  21. package/packages/core/src/editor/RichTextToolbar.ts +125 -18
  22. package/packages/core/src/editor/RightPanel.ts +165 -7
  23. package/packages/core/src/editor/Topbar.ts +306 -132
  24. package/packages/core/src/editor/VariablePickerPanel.ts +145 -0
  25. package/packages/core/src/editor/styles.css +410 -52
  26. package/packages/core/src/index.ts +32 -2
  27. package/packages/core/src/renderer/index.ts +9 -1
  28. package/packages/core/src/renderer/mjml.ts +4 -5
  29. package/packages/core/src/store/store.ts +13 -2
  30. package/packages/core/src/types.ts +65 -2
  31. package/packages/core/src/utils/docClipboard.ts +89 -0
  32. package/packages/core/src/utils/dynamicVariantHtml.ts +45 -0
  33. package/packages/core/src/utils/dynamicVariantKey.ts +24 -0
  34. package/packages/core/src/utils/dynamicVariantSection.ts +98 -0
  35. package/packages/core/src/utils/emailListStyles.ts +188 -0
  36. package/packages/core/src/utils/inlineListEditing.ts +774 -0
  37. package/packages/core/src/utils/lockedMjml.ts +146 -12
  38. package/packages/core/src/utils/modalSize.ts +27 -0
  39. package/packages/core/src/utils/richHtmlEmpty.ts +43 -0
  40. package/packages/core/src/utils/sectionLayout.ts +2 -2
  41. package/packages/core/src/variables/index.ts +3 -4
  42. package/playground/vanilla/src/main.ts +26 -1
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "mail-editor-pancake",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
4
4
  "private": false,
5
- "description": "面向运营的轻量邮件可视化编辑器(monorepo 根)",
5
+ "description": "面向运营的轻量邮件可视化编辑器",
6
6
  "devDependencies": {
7
7
  "@biomejs/biome": "^1.9.4",
8
8
  "@types/node": "^25.6.0",
@@ -1,4 +1,4 @@
1
- import { defineBlock as h, blockButtonWidthCss as F, docContentWidthCss as L, mjRawEmailTableWrap as V, mjRawCellTypographyFromStyles as U, normalizeFontWeightStep as f, FONT_WEIGHT_STEP_OPTIONS as M } from "@simple-mail/core";
1
+ import { defineBlock as h, blockButtonWidthCss as F, docContentWidthCss as L, htmlContainsMjmlTags as U, mjRawEmailTableWrap as J, mjRawCellTypographyFromStyles as G, normalizeFontWeightStep as y, FONT_WEIGHT_STEP_OPTIONS as j, isRichHtmlEffectivelyEmpty as q } from "@simple-mail/core";
2
2
  const m = {
3
3
  html: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="3.5" y="4" width="17" height="16" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M8 8.5l3 3.5-3 3.5M16 8.5l-3 3.5 3 3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',
4
4
  text: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M5 6h14M5 10h14M5 14h10M5 18h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg>',
@@ -11,7 +11,7 @@ const m = {
11
11
  social: '<svg width="24" height="24" viewBox="0 0 24 24"><circle cx="6" cy="12" r="2.5" stroke="currentColor" stroke-width="1.4" fill="none"/><circle cx="12" cy="12" r="2.5" stroke="currentColor" stroke-width="1.4" fill="none"/><circle cx="18" cy="12" r="2.5" stroke="currentColor" stroke-width="1.4" fill="none"/></svg>',
12
12
  footer: '<svg width="24" height="24" viewBox="0 0 24 24"><rect x="3.5" y="5" width="17" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4" fill="none"/><path d="M5 18h14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',
13
13
  socialGroup: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="3" y="5" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/><circle cx="8" cy="12" r="2" stroke="currentColor" stroke-width="1.4"/><circle cx="12" cy="12" r="2" stroke="currentColor" stroke-width="1.4"/><circle cx="16" cy="12" r="2" stroke="currentColor" stroke-width="1.4"/></svg>'
14
- }, x = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), G = h({
14
+ }, f = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), K = h({
15
15
  type: "button",
16
16
  name: "按钮",
17
17
  category: "content",
@@ -69,22 +69,22 @@ const m = {
69
69
  placeholder: "按钮文字"
70
70
  },
71
71
  toMjml: (e) => {
72
- const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = `${e.innerPaddingV}px ${e.innerPaddingH}px`, n = F(e.width), i = n ? ` width="${x(n)}"` : "";
73
- return `<mj-button href="${x(e.href)}" background-color="${x(
72
+ const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = `${e.innerPaddingV}px ${e.innerPaddingH}px`, n = F(e.width), i = n ? ` width="${f(n)}"` : "";
73
+ return `<mj-button href="${f(e.href)}" background-color="${f(
74
74
  e.backgroundColor
75
- )}" color="${x(e.color)}" border-radius="${e.borderRadius}px" font-size="${x(
75
+ )}" color="${f(e.color)}" border-radius="${e.borderRadius}px" font-size="${f(
76
76
  e.fontSize
77
- )}" align="${e.align}" padding="${t}" inner-padding="${o}"${i}>${x(
77
+ )}" align="${e.align}" padding="${t}" inner-padding="${o}"${i}>${f(
78
78
  e.text
79
79
  )}</mj-button>`;
80
80
  },
81
81
  renderPreview: (e) => {
82
82
  const t = F(e.width), o = t ? `width:${t};max-width:100%;box-sizing:border-box;` : "";
83
83
  return `<div style="padding:${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px;text-align:${e.align};">
84
- <a href="${x(e.href)}" style="display:inline-block;background:${e.backgroundColor};border-radius:${e.borderRadius}px;padding:${e.innerPaddingV}px ${e.innerPaddingH}px;text-decoration:none;font-size:${e.fontSize};line-height:1.2;${o}"><span class="sm-button-text" style="color:${e.color}">${x(e.text)}</span></a>
84
+ <a href="${f(e.href)}" style="display:inline-block;background:${e.backgroundColor};border-radius:${e.borderRadius}px;padding:${e.innerPaddingV}px ${e.innerPaddingH}px;text-decoration:none;font-size:${e.fontSize};line-height:1.2;${o}"><span class="sm-button-text" style="color:${e.color}">${f(e.text)}</span></a>
85
85
  </div>`;
86
86
  }
87
- }), P = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), q = h({
87
+ }), A = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), D = h({
88
88
  type: "divider",
89
89
  name: "分隔线",
90
90
  category: "content",
@@ -103,11 +103,11 @@ const m = {
103
103
  { key: "paddingTop", label: "上边距", type: "number", min: 0, max: 80 },
104
104
  { key: "paddingBottom", label: "下边距", type: "number", min: 0, max: 80 }
105
105
  ],
106
- toMjml: (e) => `<mj-divider border-color="${P(e.color)}" border-width="${e.thickness}px" width="${P(
106
+ toMjml: (e) => `<mj-divider border-color="${A(e.color)}" border-width="${e.thickness}px" width="${A(
107
107
  e.width
108
108
  )}" padding="${e.paddingTop}px 0px ${e.paddingBottom}px 0px" />`,
109
109
  renderPreview: (e) => `<div style="padding:${e.paddingTop}px 0 ${e.paddingBottom}px 0;"><div style="border-top:${e.thickness}px solid ${e.color};width:${e.width};margin:0 auto;"></div></div>`
110
- }), b = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), J = h({
110
+ }), b = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), X = h({
111
111
  type: "custom:footer",
112
112
  name: "页脚",
113
113
  category: "custom",
@@ -136,7 +136,7 @@ const m = {
136
136
  ${b(e.address)}<br/>
137
137
  <a href="${b(e.unsubscribeHref)}" style="color:${e.textColor};">${b(e.unsubscribeText)}</a>
138
138
  </div>`
139
- }), k = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), K = h({
139
+ }), k = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), Y = h({
140
140
  type: "hero",
141
141
  name: "Hero 大图",
142
142
  category: "content",
@@ -216,7 +216,7 @@ const m = {
216
216
  </mj-hero>`;
217
217
  },
218
218
  renderPreview: (e, t) => {
219
- const o = L(t.doc.meta.width), n = e.verticalAlign === "top" ? "flex-start" : e.verticalAlign === "bottom" ? "flex-end" : "center", i = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, a = k(e.backgroundUrl);
219
+ const o = L(t.doc.meta.width), n = e.verticalAlign === "top" ? "flex-start" : e.verticalAlign === "bottom" ? "flex-end" : "center", i = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, c = k(e.backgroundUrl);
220
220
  return `<div
221
221
  style="
222
222
  box-sizing:border-box;
@@ -224,7 +224,7 @@ const m = {
224
224
  max-width:${o};
225
225
  min-height:${e.height}px;
226
226
  margin:0 auto;
227
- background-image:linear-gradient(to bottom,rgba(0,0,0,0.25),rgba(0,0,0,0.35)),url(${a});
227
+ background-image:linear-gradient(to bottom,rgba(0,0,0,0.25),rgba(0,0,0,0.35)),url(${c});
228
228
  background-size:cover;
229
229
  background-position:center;
230
230
  background-color:${e.backgroundColor};
@@ -248,7 +248,7 @@ const m = {
248
248
  >${e.content}</div>
249
249
  </div>`;
250
250
  }
251
- }), D = h({
251
+ }), Z = h({
252
252
  type: "html",
253
253
  name: "HTML",
254
254
  category: "content",
@@ -265,7 +265,7 @@ const m = {
265
265
  key: "html",
266
266
  label: "HTML",
267
267
  type: "textarea",
268
- help: "插入 MJML 的 mj-raw。邮件客户端常会过滤脚本与不支持的标签。"
268
+ help: "只写 HTML(如 <a>、<table>),不要写 <mj-text> 等 MJML 标签;MJML 标签在 mj-raw 内不会被编译。简单链接建议用「文本」块。"
269
269
  },
270
270
  { key: "paddingTop", label: "内边距", type: "spacing" }
271
271
  ],
@@ -278,10 +278,13 @@ const m = {
278
278
  },
279
279
  toMjml: (e, t) => {
280
280
  const o = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`;
281
- return V(o, e.html, U(t.doc.styles));
281
+ return J(o, e.html, G(t.doc.styles));
282
282
  },
283
- renderPreview: (e) => `<div class="sm-html-content" style="padding:${`${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`};min-height:1em;">${e.html}</div>`
284
- }), w = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), X = h({
283
+ renderPreview: (e) => {
284
+ const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = U(e.html) ? '<div class="sm-html-mjml-warn">检测到 MJML 标签,此处应只写 HTML,预览可能显示异常。</div>' : "";
285
+ return `<div class="sm-html-content" style="padding:${t};min-height:1em;">${o}${e.html}</div>`;
286
+ }
287
+ }), w = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), Q = h({
285
288
  type: "image",
286
289
  name: "图片",
287
290
  category: "content",
@@ -334,7 +337,7 @@ const m = {
334
337
  e.src
335
338
  )}" alt="${w(e.alt)}" style="display:block;${t}max-width:100%;${n}border:0;outline:none;" /></div>`;
336
339
  }
337
- }), B = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), Y = h({
340
+ }), M = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), ee = h({
338
341
  type: "custom:logo",
339
342
  name: "公司 Logo",
340
343
  category: "custom",
@@ -361,25 +364,25 @@ const m = {
361
364
  ]
362
365
  }
363
366
  ],
364
- toMjml: (e) => `<mj-image src="${B(e.src)}" href="${B(e.href)}" width="${e.width}px" align="${e.align}" alt="logo" padding="8px 0px 8px 0px" />`,
367
+ toMjml: (e) => `<mj-image src="${M(e.src)}" href="${M(e.href)}" width="${e.width}px" align="${e.align}" alt="logo" padding="8px 0px 8px 0px" />`,
365
368
  renderPreview: (e) => {
366
369
  const t = e.align === "center" ? "margin:0 auto;" : e.align === "right" ? "margin-left:auto;" : "";
367
- return `<div style="padding:8px 0;text-align:${e.align};"><img src="${B(
370
+ return `<div style="padding:8px 0;text-align:${e.align};"><img src="${M(
368
371
  e.src
369
372
  )}" alt="logo" style="display:block;${t}width:${e.width}px;max-width:100%;height:auto;" /></div>`;
370
373
  }
371
374
  });
372
- function H(e) {
375
+ function E(e) {
373
376
  const t = e.trim();
374
377
  return t === "ig" ? "instagram" : t === "rabbit" || t === "tiktok" || t === "reddit" ? "web" : t;
375
378
  }
376
- const Z = [
379
+ const te = [
377
380
  { label: "X", value: "x" },
378
381
  { label: "Rabbit", value: "rabbit" },
379
382
  { label: "Facebook", value: "facebook" },
380
383
  { label: "TikTok", value: "tiktok" },
381
384
  { label: "Instagram", value: "ig" }
382
- ], Q = [
385
+ ], oe = [
383
386
  { label: "微信", value: "wechat" },
384
387
  { label: "微博", value: "weibo" },
385
388
  { label: "X", value: "x" },
@@ -390,7 +393,7 @@ const Z = [
390
393
  { label: "YouTube", value: "youtube" },
391
394
  { label: "Reddit", value: "reddit" },
392
395
  { label: "网页", value: "web" }
393
- ], ee = {
396
+ ], ne = {
394
397
  wechat: { color: "#1AAD19", preview: "信" },
395
398
  weibo: { color: "#E6162D", preview: "微" },
396
399
  x: { color: "#000000", preview: "X" },
@@ -405,11 +408,11 @@ const Z = [
405
408
  rabbit: { color: "#FF2442", preview: "R" },
406
409
  website: { color: "#6b7280", preview: "◇" },
407
410
  web: { color: "#6b7280", preview: "◇" }
408
- }, g = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), te = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
409
- function E(e) {
410
- return ee[e] ?? { color: "#6b7280", preview: "·" };
411
+ }, p = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), ie = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
412
+ function N(e) {
413
+ return ne[e] ?? { color: "#6b7280", preview: "·" };
411
414
  }
412
- const oe = "https://www.mailjet.com/images/theme/v1/icons/ico-social/", ne = {
415
+ const le = "https://www.mailjet.com/images/theme/v1/icons/ico-social/", re = {
413
416
  facebook: "facebook.png",
414
417
  twitter: "twitter.png",
415
418
  x: "twitter-x.png",
@@ -428,26 +431,26 @@ const oe = "https://www.mailjet.com/images/theme/v1/icons/ico-social/", ne = {
428
431
  soundcloud: "soundcloud.png",
429
432
  dribbble: "dribbble.png"
430
433
  };
431
- function ie(e) {
432
- const t = ne[H(e)];
433
- return t ? `${oe}${t}` : null;
434
+ function ae(e) {
435
+ const t = re[E(e)];
436
+ return t ? `${le}${t}` : null;
434
437
  }
435
- function N(e) {
436
- const { network: t, iconSize: o, iconBorderRadius: n, iconSrc: i, backgroundColor: a } = e, d = le(o, n), c = E(t), l = (a == null ? void 0 : a.trim()) || c.color, r = (i == null ? void 0 : i.trim()) || ie(t);
437
- return r ? `<span style="display:inline-flex;width:${o}px;height:${o}px;border-radius:${d};background:${l};overflow:hidden;align-items:center;justify-content:center;flex-shrink:0;line-height:0;"><img src="${g(r)}" alt="" width="${o}" height="${o}" style="display:block;width:${o}px;height:${o}px;border-radius:${d};" /></span>` : `<span style="display:inline-flex;width:${o}px;height:${o}px;border-radius:${d};background:${l};color:#fff;align-items:center;justify-content:center;font-size:${Math.round(
438
+ function W(e) {
439
+ const { network: t, iconSize: o, iconBorderRadius: n, iconSrc: i, backgroundColor: c } = e, d = ce(o, n), s = N(t), l = (c == null ? void 0 : c.trim()) || s.color, a = (i == null ? void 0 : i.trim()) || ae(t);
440
+ return a ? `<span style="display:inline-flex;width:${o}px;height:${o}px;border-radius:${d};background:${l};overflow:hidden;align-items:center;justify-content:center;flex-shrink:0;line-height:0;"><img src="${p(a)}" alt="" width="${o}" height="${o}" style="display:block;width:${o}px;height:${o}px;border-radius:${d};" /></span>` : `<span style="display:inline-flex;width:${o}px;height:${o}px;border-radius:${d};background:${l};color:#fff;align-items:center;justify-content:center;font-size:${Math.round(
438
441
  o / 2
439
- )}px;font-weight:600;flex-shrink:0;overflow:hidden;">${c.preview}</span>`;
442
+ )}px;font-weight:600;flex-shrink:0;overflow:hidden;">${s.preview}</span>`;
440
443
  }
441
444
  function S(e) {
442
445
  return `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`;
443
446
  }
444
- function W(e, t) {
447
+ function I(e, t) {
445
448
  const o = Math.max(1, Number(e) || 28), n = Number(t);
446
449
  if (!Number.isFinite(n) || n <= 0) return "0px";
447
450
  const i = o / 2;
448
451
  return n >= 999 || n >= i ? "999px" : `${Math.min(n, o)}px`;
449
452
  }
450
- function le(e, t) {
453
+ function ce(e, t) {
451
454
  const o = Math.max(1, Number(e) || 28), n = Number(t);
452
455
  if (!Number.isFinite(n) || n <= 0) return "0";
453
456
  const i = o / 2;
@@ -457,51 +460,51 @@ function C(e, t = 10) {
457
460
  const o = Number(e);
458
461
  return !Number.isFinite(o) || o < 0 ? t : Math.min(64, Math.round(o));
459
462
  }
460
- function I(e) {
463
+ function O(e) {
461
464
  return e === "right" ? "flex-end" : e === "left" ? "flex-start" : "center";
462
465
  }
463
- function O(e, t) {
464
- var c;
465
- const o = t.labelFontSize > 0 ? t.labelFontSize : 13, n = Math.max(8, Math.min(48, o)), i = f(t.labelFontWeight), a = ((c = t.labelColor) == null ? void 0 : c.trim()) || "#333333", d = g(i);
466
+ function _(e, t) {
467
+ var s;
468
+ const o = t.labelFontSize > 0 ? t.labelFontSize : 13, n = Math.max(8, Math.min(48, o)), i = y(t.labelFontWeight), c = ((s = t.labelColor) == null ? void 0 : s.trim()) || "#333333", d = p(i);
466
469
  return e.map((l) => {
467
- var j, T, R;
468
- const r = E(l.network), s = ((j = l.backgroundColor) == null ? void 0 : j.trim()) || r.color, y = [
469
- `name="${g(H(l.network))}"`,
470
- `href="${g(l.href)}"`,
471
- `background-color="${g(s)}"`,
472
- `color="${g(a)}"`,
470
+ var T, z, R;
471
+ const a = N(l.network), r = ((T = l.backgroundColor) == null ? void 0 : T.trim()) || a.color, u = [
472
+ `name="${p(E(l.network))}"`,
473
+ `href="${p(l.href)}"`,
474
+ `background-color="${p(r)}"`,
475
+ `color="${p(c)}"`,
473
476
  `font-size="${n}px"`,
474
477
  `font-weight="${d}"`
475
- ].join(" "), $ = (T = l.iconSrc) == null ? void 0 : T.trim(), u = $ ? `${y} src="${g($)}"` : y, z = ((R = l.label) == null ? void 0 : R.trim()) ?? "";
476
- return z ? ` <mj-social-element ${u}>
477
- ${te(z)}
478
- </mj-social-element>` : ` <mj-social-element ${u} />`;
478
+ ].join(" "), $ = (z = l.iconSrc) == null ? void 0 : z.trim(), x = $ ? `${u} src="${p($)}"` : u, B = ((R = l.label) == null ? void 0 : R.trim()) ?? "";
479
+ return B ? ` <mj-social-element ${x}>
480
+ ${ie(B)}
481
+ </mj-social-element>` : ` <mj-social-element ${x} />`;
479
482
  }).join(`
480
483
  `);
481
484
  }
482
- const _ = [
485
+ const V = [
483
486
  { network: "x", href: "https://x.com/" },
484
487
  { network: "rabbit", href: "https://example.com" },
485
488
  { network: "facebook", href: "https://facebook.com/" },
486
489
  { network: "tiktok", href: "https://www.tiktok.com/" },
487
490
  { network: "ig", href: "https://instagram.com/" }
488
491
  ];
489
- function A(e) {
492
+ function P(e) {
490
493
  var o;
491
494
  if ((o = e.elements) != null && o.length) return e.elements;
492
495
  const t = e.links;
493
496
  return Array.isArray(t) && t.length ? t.map((n) => ({
494
497
  network: String(n.network ?? "web"),
495
498
  href: String(n.href ?? "")
496
- })) : _;
499
+ })) : V;
497
500
  }
498
- const re = h({
501
+ const se = h({
499
502
  type: "custom:social",
500
503
  name: "社交链接",
501
504
  category: "custom",
502
505
  icon: m.social,
503
506
  defaultProps: {
504
- elements: _,
507
+ elements: V,
505
508
  align: "center",
506
509
  iconSize: 28,
507
510
  iconBorderRadius: 999,
@@ -519,7 +522,7 @@ const re = h({
519
522
  key: "elements",
520
523
  label: "社交链接",
521
524
  type: "socialLinkList",
522
- options: Z,
525
+ options: te,
523
526
  help: "平台:X、Rabbit、Facebook、TikTok、Instagram (ig)。可填标签、自定义图标 URL。MJML 侧 Rabbit/TikTok 使用 web 占位图标,需要品牌图时请填「图标 URL」。"
524
527
  },
525
528
  {
@@ -535,7 +538,7 @@ const re = h({
535
538
  label: "标签字重",
536
539
  type: "select",
537
540
  selectVariant: "segmented",
538
- options: [...M]
541
+ options: [...j]
539
542
  },
540
543
  { key: "labelColor", label: "标签颜色", type: "color" },
541
544
  {
@@ -571,41 +574,41 @@ const re = h({
571
574
  { key: "paddingTop", label: "外边距", type: "spacing" }
572
575
  ],
573
576
  toMjml: (e) => {
574
- const t = S(e), o = W(e.iconSize, e.iconBorderRadius ?? 999), n = C(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, a = A(e).filter((l) => {
575
- var r;
576
- return (r = l.href) == null ? void 0 : r.trim();
577
+ const t = S(e), o = I(e.iconSize, e.iconBorderRadius ?? 999), n = C(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, c = P(e).filter((l) => {
578
+ var a;
579
+ return (a = l.href) == null ? void 0 : a.trim();
577
580
  }), d = {
578
581
  labelFontSize: Number(e.labelFontSize ?? 13),
579
- labelFontWeight: f(String(e.labelFontWeight ?? "400")),
582
+ labelFontWeight: y(String(e.labelFontWeight ?? "400")),
580
583
  labelColor: String(e.labelColor ?? "#333333")
581
- }, c = O(a, d);
582
- return c.trim() ? `<mj-social ${i}>
583
- ${c}
584
+ }, s = _(c, d);
585
+ return s.trim() ? `<mj-social ${i}>
586
+ ${s}
584
587
  </mj-social>` : `<mj-social ${i}></mj-social>`;
585
588
  },
586
589
  renderPreview: (e) => {
587
- var r;
588
- const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = f(e.labelFontWeight), i = ((r = e.labelColor) == null ? void 0 : r.trim()) || "#333333", a = C(e.iconSpacing, 10), d = I(e.align), c = A(e).filter((s) => {
589
- var p;
590
- return (p = s.href) == null ? void 0 : p.trim();
591
- }).map((s) => {
592
- var u;
593
- const p = ((u = s.label) == null ? void 0 : u.trim()) ?? "", y = N({
594
- network: s.network,
590
+ var a;
591
+ const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = y(e.labelFontWeight), i = ((a = e.labelColor) == null ? void 0 : a.trim()) || "#333333", c = C(e.iconSpacing, 10), d = O(e.align), s = P(e).filter((r) => {
592
+ var g;
593
+ return (g = r.href) == null ? void 0 : g.trim();
594
+ }).map((r) => {
595
+ var x;
596
+ const g = ((x = r.label) == null ? void 0 : x.trim()) ?? "", u = W({
597
+ network: r.network,
595
598
  iconSize: e.iconSize,
596
599
  iconBorderRadius: e.iconBorderRadius ?? 999,
597
- iconSrc: s.iconSrc,
598
- backgroundColor: s.backgroundColor
599
- }), $ = p ? `<span style="font-size:${o}px !important;font-weight:${n};color:${g(
600
+ iconSrc: r.iconSrc,
601
+ backgroundColor: r.backgroundColor
602
+ }), $ = g ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
600
603
  i
601
- )} !important;line-height:1.2;">${g(p)}</span>` : "";
602
- return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${g(
603
- s.href
604
- )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${y}</a>${$}</span>`;
604
+ )} !important;line-height:1.2;">${p(g)}</span>` : "";
605
+ return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
606
+ r.href
607
+ )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${u}</a>${$}</span>`;
605
608
  }).join(""), l = S(e);
606
- return `<div style="display:flex;flex-wrap:wrap;justify-content:${d};align-items:center;gap:${a}px;padding:${l};">${c || '<span style="color:#9ca3af;font-size:12px;">请填写链接 URL</span>'}</div>`;
609
+ return `<div style="display:flex;flex-wrap:wrap;justify-content:${d};align-items:center;gap:${c}px;padding:${l};">${s || '<span style="color:#9ca3af;font-size:12px;">请填写链接 URL</span>'}</div>`;
607
610
  }
608
- }), ae = h({
611
+ }), de = h({
609
612
  type: "social-group",
610
613
  name: "社交组",
611
614
  category: "content",
@@ -632,7 +635,7 @@ ${c}
632
635
  key: "elements",
633
636
  label: "社交链接",
634
637
  type: "socialLinkList",
635
- options: Q,
638
+ options: oe,
636
639
  help: "每行:平台、链接、可选标签文字、自定义图标 URL、图标背景色。标签的字号/字重/颜色在下方统一设置。"
637
640
  },
638
641
  {
@@ -648,7 +651,7 @@ ${c}
648
651
  label: "标签字重",
649
652
  type: "select",
650
653
  selectVariant: "segmented",
651
- options: [...M]
654
+ options: [...j]
652
655
  },
653
656
  { key: "labelColor", label: "标签颜色", type: "color" },
654
657
  {
@@ -684,41 +687,41 @@ ${c}
684
687
  { key: "paddingTop", label: "外边距", type: "spacing" }
685
688
  ],
686
689
  toMjml: (e) => {
687
- const t = S(e), o = W(e.iconSize, e.iconBorderRadius ?? 999), n = C(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, a = e.elements.filter((l) => {
688
- var r;
689
- return (r = l.href) == null ? void 0 : r.trim();
690
+ const t = S(e), o = I(e.iconSize, e.iconBorderRadius ?? 999), n = C(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, c = e.elements.filter((l) => {
691
+ var a;
692
+ return (a = l.href) == null ? void 0 : a.trim();
690
693
  }), d = {
691
694
  labelFontSize: Number(e.labelFontSize ?? 13),
692
- labelFontWeight: f(String(e.labelFontWeight ?? "400")),
695
+ labelFontWeight: y(String(e.labelFontWeight ?? "400")),
693
696
  labelColor: String(e.labelColor ?? "#333333")
694
- }, c = O(a, d);
695
- return c.trim() ? `<mj-social ${i}>
696
- ${c}
697
+ }, s = _(c, d);
698
+ return s.trim() ? `<mj-social ${i}>
699
+ ${s}
697
700
  </mj-social>` : `<mj-social ${i}></mj-social>`;
698
701
  },
699
702
  renderPreview: (e) => {
700
- var r;
701
- const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = f(e.labelFontWeight), i = ((r = e.labelColor) == null ? void 0 : r.trim()) || "#333333", a = C(e.iconSpacing, 10), d = I(e.align), c = e.elements.filter((s) => {
702
- var p;
703
- return (p = s.href) == null ? void 0 : p.trim();
704
- }).map((s) => {
705
- var u;
706
- const p = ((u = s.label) == null ? void 0 : u.trim()) ?? "", y = N({
707
- network: s.network,
703
+ var a;
704
+ const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = y(e.labelFontWeight), i = ((a = e.labelColor) == null ? void 0 : a.trim()) || "#333333", c = C(e.iconSpacing, 10), d = O(e.align), s = e.elements.filter((r) => {
705
+ var g;
706
+ return (g = r.href) == null ? void 0 : g.trim();
707
+ }).map((r) => {
708
+ var x;
709
+ const g = ((x = r.label) == null ? void 0 : x.trim()) ?? "", u = W({
710
+ network: r.network,
708
711
  iconSize: e.iconSize,
709
712
  iconBorderRadius: e.iconBorderRadius ?? 999,
710
- iconSrc: s.iconSrc,
711
- backgroundColor: s.backgroundColor
712
- }), $ = p ? `<span style="font-size:${o}px !important;font-weight:${n};color:${g(
713
+ iconSrc: r.iconSrc,
714
+ backgroundColor: r.backgroundColor
715
+ }), $ = g ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
713
716
  i
714
- )} !important;line-height:1.2;">${g(p)}</span>` : "";
715
- return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${g(
716
- s.href
717
- )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${y}</a>${$}</span>`;
717
+ )} !important;line-height:1.2;">${p(g)}</span>` : "";
718
+ return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
719
+ r.href
720
+ )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${u}</a>${$}</span>`;
718
721
  }).join(""), l = S(e);
719
- return `<div style="display:flex;flex-wrap:wrap;justify-content:${d};align-items:center;gap:${a}px;padding:${l};">${c || '<span style="color:#9ca3af;font-size:12px;">请添加社交链接</span>'}</div>`;
722
+ return `<div style="display:flex;flex-wrap:wrap;justify-content:${d};align-items:center;gap:${c}px;padding:${l};">${s || '<span style="color:#9ca3af;font-size:12px;">请添加社交链接</span>'}</div>`;
720
723
  }
721
- }), ce = h({
724
+ }), ge = h({
722
725
  type: "spacer",
723
726
  name: "间距",
724
727
  category: "content",
@@ -727,7 +730,7 @@ ${c}
727
730
  schema: [{ key: "height", label: "高度 (px)", type: "number", min: 1, max: 200 }],
728
731
  toMjml: (e) => `<mj-spacer height="${e.height}px" />`,
729
732
  renderPreview: (e) => `<div style="height:${e.height}px;line-height:${e.height}px;"></div>`
730
- }), v = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), se = h({
733
+ }), v = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), H = "输入文本…", pe = h({
731
734
  type: "text",
732
735
  name: "文本",
733
736
  category: "content",
@@ -766,7 +769,7 @@ ${c}
766
769
  type: "select",
767
770
  selectVariant: "segmented",
768
771
  inheritGlobal: !0,
769
- options: [...M]
772
+ options: [...j]
770
773
  },
771
774
  {
772
775
  key: "lineHeight",
@@ -801,53 +804,53 @@ ${c}
801
804
  mode: "rich",
802
805
  multiline: !0,
803
806
  propKey: "content",
804
- placeholder: "输入文本…"
807
+ placeholder: H
805
808
  },
806
809
  toMjml: (e) => {
807
- const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.fontFamily ? ` font-family="${v(e.fontFamily)}"` : "", n = String(e.fontWeight ?? "").trim(), i = n ? ` font-weight="${v(f(n))}"` : "", a = String(e.lineHeight ?? "").trim(), d = a ? ` line-height="${v(a)}"` : "", c = String(e.color ?? "").trim(), l = c ? ` color="${v(c)}"` : "", r = String(e.fontSize ?? "").trim(), s = r ? ` font-size="${v(r)}"` : "";
808
- return `<mj-text align="${e.align}"${l}${s}${d}${o}${i} padding="${t}">${e.content}</mj-text>`;
810
+ const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.fontFamily ? ` font-family="${v(e.fontFamily)}"` : "", n = String(e.fontWeight ?? "").trim(), i = n ? ` font-weight="${v(y(n))}"` : "", c = String(e.lineHeight ?? "").trim(), d = c ? ` line-height="${v(c)}"` : "", s = String(e.color ?? "").trim(), l = s ? ` color="${v(s)}"` : "", a = String(e.fontSize ?? "").trim(), r = a ? ` font-size="${v(a)}"` : "";
811
+ return `<mj-text align="${e.align}"${l}${r}${d}${o}${i} padding="${t}">${e.content}</mj-text>`;
809
812
  },
810
813
  renderPreview: (e) => {
811
- const t = e.fontFamily ? `font-family:${e.fontFamily};` : "", o = String(e.fontWeight ?? "").trim(), n = o ? `font-weight:${f(o)};` : "", i = String(e.lineHeight ?? "").trim(), a = i ? `line-height:${i};` : "line-height:normal;", d = String(e.color ?? "").trim(), c = d ? `color:${d};` : "", l = String(e.fontSize ?? "").trim(), r = l ? `font-size:${l};` : "";
812
- return `<div class="sm-text-content" style="text-align:${e.align};${c}${r}${n}${a}${t}padding:${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px;">${e.content}</div>`;
814
+ const t = e.fontFamily ? `font-family:${e.fontFamily};` : "", o = String(e.fontWeight ?? "").trim(), n = o ? `font-weight:${y(o)};` : "", i = String(e.lineHeight ?? "").trim(), c = i ? `line-height:${i};` : "line-height:normal;", d = String(e.color ?? "").trim(), s = d ? `color:${d};` : "", l = String(e.fontSize ?? "").trim(), a = l ? `font-size:${l};` : "", r = q(e.content), g = r ? " is-empty" : "", u = r ? ` data-placeholder="${H}"` : "";
815
+ return `<div class="sm-text-content${g}"${u} style="text-align:${e.align};${s}${a}${n}${c}${t}padding:${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px;">${e.content}</div>`;
813
816
  }
814
- }), de = [
815
- se,
816
- D,
817
- X,
818
- ae,
817
+ }), he = [
818
+ pe,
819
+ Z,
820
+ Q,
821
+ de,
822
+ Y,
819
823
  K,
820
- G,
821
- q,
822
- ce
823
- ], ge = [Y, re, J], he = [...de, ...ge];
824
+ D,
825
+ ge
826
+ ], me = [ee, se, X], ue = [...he, ...me];
824
827
  export {
825
- Q as SOCIAL_NETWORK_OPTIONS,
826
- he as allBlocks,
827
- de as builtinBlocks,
828
- G as buttonBlock,
829
- q as dividerBlock,
830
- g as escAttr,
831
- te as escMjmlText,
832
- ge as exampleCustomBlocks,
833
- I as flexJustifyFromAlign,
834
- J as footerBlock,
835
- K as heroBlock,
836
- D as htmlBlock,
837
- X as imageBlock,
838
- Y as logoBlock,
839
- ie as mjSocialDefaultIconSrc,
840
- H as mjSocialElementName,
841
- O as mjSocialElementsLines,
828
+ oe as SOCIAL_NETWORK_OPTIONS,
829
+ ue as allBlocks,
830
+ he as builtinBlocks,
831
+ K as buttonBlock,
832
+ D as dividerBlock,
833
+ p as escAttr,
834
+ ie as escMjmlText,
835
+ me as exampleCustomBlocks,
836
+ O as flexJustifyFromAlign,
837
+ X as footerBlock,
838
+ Y as heroBlock,
839
+ Z as htmlBlock,
840
+ Q as imageBlock,
841
+ ee as logoBlock,
842
+ ae as mjSocialDefaultIconSrc,
843
+ E as mjSocialElementName,
844
+ _ as mjSocialElementsLines,
842
845
  S as paddingQuad,
843
- N as renderSocialIconPreviewHtml,
844
- re as socialBlock,
845
- ae as socialGroupBlock,
846
- le as socialIconBorderRadiusCss,
847
- W as socialIconBorderRadiusMjml,
846
+ W as renderSocialIconPreviewHtml,
847
+ se as socialBlock,
848
+ de as socialGroupBlock,
849
+ ce as socialIconBorderRadiusCss,
850
+ I as socialIconBorderRadiusMjml,
848
851
  C as socialIconSpacingPx,
849
- E as socialMeta,
850
- ce as spacerBlock,
851
- se as textBlock
852
+ N as socialMeta,
853
+ ge as spacerBlock,
854
+ pe as textBlock
852
855
  };
853
856
  //# sourceMappingURL=index.js.map