mail-editor-pancake 0.0.8 → 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 (48) hide show
  1. package/README.md +498 -218
  2. package/package.json +2 -2
  3. package/packages/blocks/dist/index.d.ts +12 -0
  4. package/packages/blocks/dist/index.js +187 -145
  5. package/packages/blocks/dist/index.js.map +1 -1
  6. package/packages/blocks/src/html.ts +11 -3
  7. package/packages/blocks/src/index.ts +2 -0
  8. package/packages/blocks/src/social.ts +8 -11
  9. package/packages/blocks/src/socialGroup.ts +8 -11
  10. package/packages/blocks/src/socialShared.ts +52 -0
  11. package/packages/blocks/src/text.ts +8 -3
  12. package/packages/core/dist/index.d.ts +275 -12
  13. package/packages/core/dist/index.js +4099 -2194
  14. package/packages/core/dist/index.js.map +1 -1
  15. package/packages/core/dist/style.css +1 -1
  16. package/packages/core/package.json +1 -1
  17. package/packages/core/src/editor/BlockCodeModal.ts +84 -2
  18. package/packages/core/src/editor/Canvas.ts +191 -26
  19. package/packages/core/src/editor/Editor.ts +589 -76
  20. package/packages/core/src/editor/ExportModal.ts +1 -1
  21. package/packages/core/src/editor/ImportDocModal.ts +129 -0
  22. package/packages/core/src/editor/InlineEditor.ts +246 -22
  23. package/packages/core/src/editor/LeftPanel.ts +55 -14
  24. package/packages/core/src/editor/Modal.ts +20 -1
  25. package/packages/core/src/editor/PreviewModal.ts +2 -2
  26. package/packages/core/src/editor/RichTextToolbar.ts +125 -18
  27. package/packages/core/src/editor/RightPanel.ts +165 -7
  28. package/packages/core/src/editor/Topbar.ts +306 -132
  29. package/packages/core/src/editor/VariablePickerPanel.ts +145 -0
  30. package/packages/core/src/editor/styles.css +458 -59
  31. package/packages/core/src/index.ts +43 -2
  32. package/packages/core/src/renderer/index.ts +9 -1
  33. package/packages/core/src/renderer/mjml.ts +4 -5
  34. package/packages/core/src/store/store.ts +13 -2
  35. package/packages/core/src/types.ts +67 -2
  36. package/packages/core/src/utils/docClipboard.ts +89 -0
  37. package/packages/core/src/utils/dynamicVariantHtml.ts +45 -0
  38. package/packages/core/src/utils/dynamicVariantKey.ts +24 -0
  39. package/packages/core/src/utils/dynamicVariantSection.ts +98 -0
  40. package/packages/core/src/utils/emailListStyles.ts +188 -0
  41. package/packages/core/src/utils/inlineListEditing.ts +774 -0
  42. package/packages/core/src/utils/lockedMjml.ts +146 -12
  43. package/packages/core/src/utils/modalSize.ts +27 -0
  44. package/packages/core/src/utils/richHtmlEmpty.ts +43 -0
  45. package/packages/core/src/utils/richHtmlInsert.ts +16 -0
  46. package/packages/core/src/utils/sectionLayout.ts +2 -2
  47. package/packages/core/src/variables/index.ts +90 -0
  48. package/playground/vanilla/src/main.ts +26 -1
@@ -1,4 +1,4 @@
1
- import { defineBlock as h, blockButtonWidthCss as A, docContentWidthCss as H, mjRawEmailTableWrap as _, mjRawCellTypographyFromStyles as G, normalizeFontWeightStep as k, FONT_WEIGHT_STEP_OPTIONS as L } 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
- }, $ = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), q = 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 = A(e.width), i = n ? ` width="${$(n)}"` : "";
73
- return `<mj-button href="${$(e.href)}" background-color="${$(
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="${$(e.color)}" border-radius="${e.borderRadius}px" font-size="${$(
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}>${$(
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
- const t = A(e.width), o = t ? `width:${t};max-width:100%;box-sizing:border-box;` : "";
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="${$(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}">${$(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
- }), E = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), K = 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="${E(e.color)}" border-width="${e.thickness}px" width="${E(
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
- }), C = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), X = 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",
@@ -197,26 +197,26 @@ const m = {
197
197
  placeholder: "编写覆盖在图片上的文案…"
198
198
  },
199
199
  toMjml: (e, t) => {
200
- const o = H(t.doc.meta.width), n = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, i = `${e.height}px`;
200
+ const o = L(t.doc.meta.width), n = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, i = `${e.height}px`;
201
201
  return `<mj-hero
202
202
  mode="fixed-height"
203
203
  height="${i}"
204
204
  background-width="${o}"
205
205
  background-height="${i}"
206
- background-url="${C(e.backgroundUrl)}"
207
- background-color="${C(e.backgroundColor)}"
206
+ background-url="${k(e.backgroundUrl)}"
207
+ background-color="${k(e.backgroundColor)}"
208
208
  padding="${n}"
209
209
  vertical-align="${e.verticalAlign}"
210
210
  >
211
211
  <mj-text
212
212
  align="${e.textAlign}"
213
- color="${C(e.textColor)}"
213
+ color="${k(e.textColor)}"
214
214
  padding="0"
215
215
  >${e.content}</mj-text>
216
216
  </mj-hero>`;
217
217
  },
218
218
  renderPreview: (e, t) => {
219
- const o = H(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`, r = C(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(${r});
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};
@@ -232,7 +232,7 @@ const m = {
232
232
  align-items:${n};
233
233
  justify-content:${e.textAlign === "right" ? "flex-end" : e.textAlign === "center" ? "center" : "flex-start"};
234
234
  padding:${i};
235
- color:${C(e.textColor)};
235
+ color:${k(e.textColor)};
236
236
  overflow:hidden;
237
237
  border-radius:2px;
238
238
  "
@@ -242,13 +242,13 @@ const m = {
242
242
  style="
243
243
  width:100%;
244
244
  text-align:${e.textAlign};
245
- color:${C(e.textColor)};
245
+ color:${k(e.textColor)};
246
246
  text-shadow:0 1px 2px rgba(0,0,0,0.45);
247
247
  "
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 _(o, e.html, G(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
- }), M = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), Y = 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",
@@ -325,16 +328,16 @@ const m = {
325
328
  { key: "paddingTop", label: "内边距", type: "spacing" }
326
329
  ],
327
330
  toMjml: (e) => {
328
- const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.href ? ` href="${M(e.href)}"` : "", n = ` alt="${M(e.alt || "")}"`, i = e.height != null && e.height > 0 ? ` height="${e.height}px"` : "";
329
- return `<mj-image src="${M(e.src)}"${n}${o} width="${e.width}px"${i} align="${e.align}" padding="${t}" />`;
331
+ const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.href ? ` href="${w(e.href)}"` : "", n = ` alt="${w(e.alt || "")}"`, i = e.height != null && e.height > 0 ? ` height="${e.height}px"` : "";
332
+ return `<mj-image src="${w(e.src)}"${n}${o} width="${e.width}px"${i} align="${e.align}" padding="${t}" />`;
330
333
  },
331
334
  renderPreview: (e) => {
332
335
  const t = e.align === "center" ? "margin:0 auto;" : e.align === "right" ? "margin-left:auto;" : "", o = e.height != null && e.height > 0 ? e.height : 0, n = o > 0 ? `width:${e.width}px;height:${o}px;object-fit:cover;object-position:center;` : `width:${e.width}px;height:auto;`;
333
- return `<div style="padding:${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px;text-align:${e.align};"><img src="${M(
336
+ return `<div style="padding:${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px;text-align:${e.align};"><img src="${w(
334
337
  e.src
335
- )}" alt="${M(e.alt)}" style="display:block;${t}max-width:100%;${n}border:0;outline:none;" /></div>`;
338
+ )}" alt="${w(e.alt)}" style="display:block;${t}max-width:100%;${n}border:0;outline:none;" /></div>`;
336
339
  }
337
- }), R = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), Z = 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="${R(e.src)}" href="${R(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="${R(
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 Q(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 ee = [
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
- ], te = [
385
+ ], oe = [
383
386
  { label: "微信", value: "wechat" },
384
387
  { label: "微博", value: "weibo" },
385
388
  { label: "X", value: "x" },
@@ -390,7 +393,7 @@ const ee = [
390
393
  { label: "YouTube", value: "youtube" },
391
394
  { label: "Reddit", value: "reddit" },
392
395
  { label: "网页", value: "web" }
393
- ], oe = {
396
+ ], ne = {
394
397
  wechat: { color: "#1AAD19", preview: "信" },
395
398
  weibo: { color: "#E6162D", preview: "微" },
396
399
  x: { color: "#000000", preview: "X" },
@@ -405,74 +408,103 @@ const ee = [
405
408
  rabbit: { color: "#FF2442", preview: "R" },
406
409
  website: { color: "#6b7280", preview: "◇" },
407
410
  web: { color: "#6b7280", preview: "◇" }
408
- }, p = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), ne = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
409
- function P(e) {
410
- return oe[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: "·" };
414
+ }
415
+ const le = "https://www.mailjet.com/images/theme/v1/icons/ico-social/", re = {
416
+ facebook: "facebook.png",
417
+ twitter: "twitter.png",
418
+ x: "twitter-x.png",
419
+ google: "google-plus.png",
420
+ pinterest: "pinterest.png",
421
+ linkedin: "linkedin.png",
422
+ instagram: "instagram.png",
423
+ web: "web.png",
424
+ snapchat: "snapchat.png",
425
+ youtube: "youtube.png",
426
+ tumblr: "tumblr.png",
427
+ github: "github.png",
428
+ xing: "xing.png",
429
+ vimeo: "vimeo.png",
430
+ medium: "medium.png",
431
+ soundcloud: "soundcloud.png",
432
+ dribbble: "dribbble.png"
433
+ };
434
+ function ae(e) {
435
+ const t = re[E(e)];
436
+ return t ? `${le}${t}` : null;
437
+ }
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(
441
+ o / 2
442
+ )}px;font-weight:600;flex-shrink:0;overflow:hidden;">${s.preview}</span>`;
411
443
  }
412
- function T(e) {
444
+ function S(e) {
413
445
  return `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`;
414
446
  }
415
- function N(e, t) {
447
+ function I(e, t) {
416
448
  const o = Math.max(1, Number(e) || 28), n = Number(t);
417
449
  if (!Number.isFinite(n) || n <= 0) return "0px";
418
450
  const i = o / 2;
419
451
  return n >= 999 || n >= i ? "999px" : `${Math.min(n, o)}px`;
420
452
  }
421
- function V(e, t) {
453
+ function ce(e, t) {
422
454
  const o = Math.max(1, Number(e) || 28), n = Number(t);
423
455
  if (!Number.isFinite(n) || n <= 0) return "0";
424
456
  const i = o / 2;
425
457
  return n >= 999 || n >= i ? "50%" : `${Math.min(n, o)}px`;
426
458
  }
427
- function F(e, t = 10) {
459
+ function C(e, t = 10) {
428
460
  const o = Number(e);
429
461
  return !Number.isFinite(o) || o < 0 ? t : Math.min(64, Math.round(o));
430
462
  }
431
- function I(e) {
463
+ function O(e) {
432
464
  return e === "right" ? "flex-end" : e === "left" ? "flex-start" : "center";
433
465
  }
434
- function O(e, t) {
435
- var a;
436
- const o = t.labelFontSize > 0 ? t.labelFontSize : 13, n = Math.max(8, Math.min(48, o)), i = k(t.labelFontWeight), r = ((a = t.labelColor) == null ? void 0 : a.trim()) || "#333333", d = p(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);
437
469
  return e.map((l) => {
438
- var v, S, y;
439
- const c = P(l.network), u = ((v = l.backgroundColor) == null ? void 0 : v.trim()) || c.color, g = [
440
- `name="${p(Q(l.network))}"`,
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))}"`,
441
473
  `href="${p(l.href)}"`,
442
- `background-color="${p(u)}"`,
443
- `color="${p(r)}"`,
474
+ `background-color="${p(r)}"`,
475
+ `color="${p(c)}"`,
444
476
  `font-size="${n}px"`,
445
477
  `font-weight="${d}"`
446
- ].join(" "), w = (S = l.iconSrc) == null ? void 0 : S.trim(), x = w ? `${g} src="${p(w)}"` : g, f = ((y = l.label) == null ? void 0 : y.trim()) ?? "";
447
- return f ? ` <mj-social-element ${x}>
448
- ${ne(f)}
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)}
449
481
  </mj-social-element>` : ` <mj-social-element ${x} />`;
450
482
  }).join(`
451
483
  `);
452
484
  }
453
- const U = [
485
+ const V = [
454
486
  { network: "x", href: "https://x.com/" },
455
487
  { network: "rabbit", href: "https://example.com" },
456
488
  { network: "facebook", href: "https://facebook.com/" },
457
489
  { network: "tiktok", href: "https://www.tiktok.com/" },
458
490
  { network: "ig", href: "https://instagram.com/" }
459
491
  ];
460
- function W(e) {
492
+ function P(e) {
461
493
  var o;
462
494
  if ((o = e.elements) != null && o.length) return e.elements;
463
495
  const t = e.links;
464
496
  return Array.isArray(t) && t.length ? t.map((n) => ({
465
497
  network: String(n.network ?? "web"),
466
498
  href: String(n.href ?? "")
467
- })) : U;
499
+ })) : V;
468
500
  }
469
- const ie = h({
501
+ const se = h({
470
502
  type: "custom:social",
471
503
  name: "社交链接",
472
504
  category: "custom",
473
505
  icon: m.social,
474
506
  defaultProps: {
475
- elements: U,
507
+ elements: V,
476
508
  align: "center",
477
509
  iconSize: 28,
478
510
  iconBorderRadius: 999,
@@ -490,7 +522,7 @@ const ie = h({
490
522
  key: "elements",
491
523
  label: "社交链接",
492
524
  type: "socialLinkList",
493
- options: ee,
525
+ options: te,
494
526
  help: "平台:X、Rabbit、Facebook、TikTok、Instagram (ig)。可填标签、自定义图标 URL。MJML 侧 Rabbit/TikTok 使用 web 占位图标,需要品牌图时请填「图标 URL」。"
495
527
  },
496
528
  {
@@ -506,7 +538,7 @@ const ie = h({
506
538
  label: "标签字重",
507
539
  type: "select",
508
540
  selectVariant: "segmented",
509
- options: [...L]
541
+ options: [...j]
510
542
  },
511
543
  { key: "labelColor", label: "标签颜色", type: "color" },
512
544
  {
@@ -542,37 +574,41 @@ const ie = h({
542
574
  { key: "paddingTop", label: "外边距", type: "spacing" }
543
575
  ],
544
576
  toMjml: (e) => {
545
- const t = T(e), o = N(e.iconSize, e.iconBorderRadius ?? 999), n = F(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, r = W(e).filter((l) => {
546
- var c;
547
- return (c = l.href) == null ? void 0 : c.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();
548
580
  }), d = {
549
581
  labelFontSize: Number(e.labelFontSize ?? 13),
550
- labelFontWeight: k(String(e.labelFontWeight ?? "400")),
582
+ labelFontWeight: y(String(e.labelFontWeight ?? "400")),
551
583
  labelColor: String(e.labelColor ?? "#333333")
552
- }, a = O(r, d);
553
- return a.trim() ? `<mj-social ${i}>
554
- ${a}
584
+ }, s = _(c, d);
585
+ return s.trim() ? `<mj-social ${i}>
586
+ ${s}
555
587
  </mj-social>` : `<mj-social ${i}></mj-social>`;
556
588
  },
557
589
  renderPreview: (e) => {
558
- var u;
559
- const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = k(e.labelFontWeight), i = ((u = e.labelColor) == null ? void 0 : u.trim()) || "#333333", r = V(e.iconSize, e.iconBorderRadius ?? 999), d = F(e.iconSpacing, 10), a = I(e.align), l = W(e).filter((s) => {
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) => {
560
592
  var g;
561
- return (g = s.href) == null ? void 0 : g.trim();
562
- }).map((s) => {
563
- var y, z, j;
564
- const g = P(s.network), w = ((y = s.backgroundColor) == null ? void 0 : y.trim()) || g.color, x = ((z = s.label) == null ? void 0 : z.trim()) ?? "", f = (j = s.iconSrc) == null ? void 0 : j.trim(), v = f ? `<img src="${p(f)}" alt="" style="width:${e.iconSize}px;height:${e.iconSize}px;object-fit:cover;border-radius:${r};flex-shrink:0;" />` : `<span style="display:inline-flex;width:${e.iconSize}px;height:${e.iconSize}px;border-radius:${r};background:${w};color:#fff;align-items:center;justify-content:center;font-size:${Math.round(
565
- e.iconSize / 2
566
- )}px;font-weight:600;flex-shrink:0;overflow:hidden;">${g.preview}</span>`, S = x ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
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,
598
+ iconSize: e.iconSize,
599
+ iconBorderRadius: e.iconBorderRadius ?? 999,
600
+ iconSrc: r.iconSrc,
601
+ backgroundColor: r.backgroundColor
602
+ }), $ = g ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
567
603
  i
568
- )} !important;line-height:1.2;">${p(x)}</span>` : "";
604
+ )} !important;line-height:1.2;">${p(g)}</span>` : "";
569
605
  return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
570
- s.href
571
- )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${v}</a>${S}</span>`;
572
- }).join(""), c = T(e);
573
- return `<div style="display:flex;flex-wrap:wrap;justify-content:${a};align-items:center;gap:${d}px;padding:${c};">${l || '<span style="color:#9ca3af;font-size:12px;">请填写链接 URL</span>'}</div>`;
606
+ r.href
607
+ )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${u}</a>${$}</span>`;
608
+ }).join(""), l = S(e);
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>`;
574
610
  }
575
- }), le = h({
611
+ }), de = h({
576
612
  type: "social-group",
577
613
  name: "社交组",
578
614
  category: "content",
@@ -599,7 +635,7 @@ ${a}
599
635
  key: "elements",
600
636
  label: "社交链接",
601
637
  type: "socialLinkList",
602
- options: te,
638
+ options: oe,
603
639
  help: "每行:平台、链接、可选标签文字、自定义图标 URL、图标背景色。标签的字号/字重/颜色在下方统一设置。"
604
640
  },
605
641
  {
@@ -615,7 +651,7 @@ ${a}
615
651
  label: "标签字重",
616
652
  type: "select",
617
653
  selectVariant: "segmented",
618
- options: [...L]
654
+ options: [...j]
619
655
  },
620
656
  { key: "labelColor", label: "标签颜色", type: "color" },
621
657
  {
@@ -651,37 +687,41 @@ ${a}
651
687
  { key: "paddingTop", label: "外边距", type: "spacing" }
652
688
  ],
653
689
  toMjml: (e) => {
654
- const t = T(e), o = N(e.iconSize, e.iconBorderRadius ?? 999), n = F(e.iconSpacing, 10), i = `align="${e.align}" icon-size="${e.iconSize}px" mode="horizontal" border-radius="${o}" inner-padding="${n}px" padding="${t}"`, r = e.elements.filter((l) => {
655
- var c;
656
- return (c = l.href) == null ? void 0 : c.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();
657
693
  }), d = {
658
694
  labelFontSize: Number(e.labelFontSize ?? 13),
659
- labelFontWeight: k(String(e.labelFontWeight ?? "400")),
695
+ labelFontWeight: y(String(e.labelFontWeight ?? "400")),
660
696
  labelColor: String(e.labelColor ?? "#333333")
661
- }, a = O(r, d);
662
- return a.trim() ? `<mj-social ${i}>
663
- ${a}
697
+ }, s = _(c, d);
698
+ return s.trim() ? `<mj-social ${i}>
699
+ ${s}
664
700
  </mj-social>` : `<mj-social ${i}></mj-social>`;
665
701
  },
666
702
  renderPreview: (e) => {
667
- var u;
668
- const t = e.labelFontSize != null && e.labelFontSize > 0 ? e.labelFontSize : 13, o = Math.max(8, Math.min(48, t)), n = k(e.labelFontWeight), i = ((u = e.labelColor) == null ? void 0 : u.trim()) || "#333333", r = V(e.iconSize, e.iconBorderRadius ?? 999), d = F(e.iconSpacing, 10), a = I(e.align), l = e.elements.filter((s) => {
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) => {
669
705
  var g;
670
- return (g = s.href) == null ? void 0 : g.trim();
671
- }).map((s) => {
672
- var y, z, j;
673
- const g = P(s.network), w = ((y = s.backgroundColor) == null ? void 0 : y.trim()) || g.color, x = ((z = s.label) == null ? void 0 : z.trim()) ?? "", f = (j = s.iconSrc) == null ? void 0 : j.trim(), v = f ? `<img src="${p(f)}" alt="" style="width:${e.iconSize}px;height:${e.iconSize}px;object-fit:cover;border-radius:${r};flex-shrink:0;" />` : `<span style="display:inline-flex;width:${e.iconSize}px;height:${e.iconSize}px;border-radius:${r};background:${w};color:#fff;align-items:center;justify-content:center;font-size:${Math.round(
674
- e.iconSize / 2
675
- )}px;font-weight:600;flex-shrink:0;overflow:hidden;">${g.preview}</span>`, S = x ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
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,
711
+ iconSize: e.iconSize,
712
+ iconBorderRadius: e.iconBorderRadius ?? 999,
713
+ iconSrc: r.iconSrc,
714
+ backgroundColor: r.backgroundColor
715
+ }), $ = g ? `<span style="font-size:${o}px !important;font-weight:${n};color:${p(
676
716
  i
677
- )} !important;line-height:1.2;">${p(x)}</span>` : "";
717
+ )} !important;line-height:1.2;">${p(g)}</span>` : "";
678
718
  return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
679
- s.href
680
- )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${v}</a>${S}</span>`;
681
- }).join(""), c = T(e);
682
- return `<div style="display:flex;flex-wrap:wrap;justify-content:${a};align-items:center;gap:${d}px;padding:${c};">${l || '<span style="color:#9ca3af;font-size:12px;">请添加社交链接</span>'}</div>`;
719
+ r.href
720
+ )}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${u}</a>${$}</span>`;
721
+ }).join(""), l = S(e);
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>`;
683
723
  }
684
- }), re = h({
724
+ }), ge = h({
685
725
  type: "spacer",
686
726
  name: "间距",
687
727
  category: "content",
@@ -690,7 +730,7 @@ ${a}
690
730
  schema: [{ key: "height", label: "高度 (px)", type: "number", min: 1, max: 200 }],
691
731
  toMjml: (e) => `<mj-spacer height="${e.height}px" />`,
692
732
  renderPreview: (e) => `<div style="height:${e.height}px;line-height:${e.height}px;"></div>`
693
- }), B = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), ae = h({
733
+ }), v = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"), H = "输入文本…", pe = h({
694
734
  type: "text",
695
735
  name: "文本",
696
736
  category: "content",
@@ -729,7 +769,7 @@ ${a}
729
769
  type: "select",
730
770
  selectVariant: "segmented",
731
771
  inheritGlobal: !0,
732
- options: [...L]
772
+ options: [...j]
733
773
  },
734
774
  {
735
775
  key: "lineHeight",
@@ -764,51 +804,53 @@ ${a}
764
804
  mode: "rich",
765
805
  multiline: !0,
766
806
  propKey: "content",
767
- placeholder: "输入文本…"
807
+ placeholder: H
768
808
  },
769
809
  toMjml: (e) => {
770
- const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.fontFamily ? ` font-family="${B(e.fontFamily)}"` : "", n = String(e.fontWeight ?? "").trim(), i = n ? ` font-weight="${B(k(n))}"` : "", r = String(e.lineHeight ?? "").trim(), d = r ? ` line-height="${B(r)}"` : "", a = String(e.color ?? "").trim(), l = a ? ` color="${B(a)}"` : "", c = String(e.fontSize ?? "").trim(), u = c ? ` font-size="${B(c)}"` : "";
771
- return `<mj-text align="${e.align}"${l}${u}${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>`;
772
812
  },
773
813
  renderPreview: (e) => {
774
- const t = e.fontFamily ? `font-family:${e.fontFamily};` : "", o = String(e.fontWeight ?? "").trim(), n = o ? `font-weight:${k(o)};` : "", i = String(e.lineHeight ?? "").trim(), r = i ? `line-height:${i};` : "line-height:normal;", d = String(e.color ?? "").trim(), a = d ? `color:${d};` : "", l = String(e.fontSize ?? "").trim(), c = l ? `font-size:${l};` : "";
775
- return `<div class="sm-text-content" style="text-align:${e.align};${a}${c}${n}${r}${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>`;
776
816
  }
777
- }), ce = [
778
- ae,
779
- D,
817
+ }), he = [
818
+ pe,
819
+ Z,
820
+ Q,
821
+ de,
780
822
  Y,
781
- le,
782
- X,
783
- q,
784
823
  K,
785
- re
786
- ], se = [Z, ie, J], ge = [...ce, ...se];
824
+ D,
825
+ ge
826
+ ], me = [ee, se, X], ue = [...he, ...me];
787
827
  export {
788
- te as SOCIAL_NETWORK_OPTIONS,
789
- ge as allBlocks,
790
- ce as builtinBlocks,
791
- q as buttonBlock,
792
- K as dividerBlock,
828
+ oe as SOCIAL_NETWORK_OPTIONS,
829
+ ue as allBlocks,
830
+ he as builtinBlocks,
831
+ K as buttonBlock,
832
+ D as dividerBlock,
793
833
  p as escAttr,
794
- ne as escMjmlText,
795
- se as exampleCustomBlocks,
796
- I as flexJustifyFromAlign,
797
- J as footerBlock,
798
- X as heroBlock,
799
- D as htmlBlock,
800
- Y as imageBlock,
801
- Z as logoBlock,
802
- Q as mjSocialElementName,
803
- O as mjSocialElementsLines,
804
- T as paddingQuad,
805
- ie as socialBlock,
806
- le as socialGroupBlock,
807
- V as socialIconBorderRadiusCss,
808
- N as socialIconBorderRadiusMjml,
809
- F as socialIconSpacingPx,
810
- P as socialMeta,
811
- re as spacerBlock,
812
- ae as textBlock
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,
845
+ S as paddingQuad,
846
+ W as renderSocialIconPreviewHtml,
847
+ se as socialBlock,
848
+ de as socialGroupBlock,
849
+ ce as socialIconBorderRadiusCss,
850
+ I as socialIconBorderRadiusMjml,
851
+ C as socialIconSpacingPx,
852
+ N as socialMeta,
853
+ ge as spacerBlock,
854
+ pe as textBlock
813
855
  };
814
856
  //# sourceMappingURL=index.js.map