mail-editor-pancake 0.0.7 → 0.0.8
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.
- package/README.md +50 -1
- package/package.json +1 -1
- package/packages/blocks/dist/index.js +88 -89
- package/packages/blocks/dist/index.js.map +1 -1
- package/packages/blocks/src/text.ts +21 -14
- package/packages/core/dist/index.d.ts +45 -4
- package/packages/core/dist/index.js +2252 -1449
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/style.css +1 -1
- package/packages/core/src/editor/Canvas.ts +68 -34
- package/packages/core/src/editor/ColorPickerPopover.ts +291 -0
- package/packages/core/src/editor/Editor.ts +72 -2
- package/packages/core/src/editor/ExportModal.ts +2 -1
- package/packages/core/src/editor/FocusBreadcrumb.ts +120 -0
- package/packages/core/src/editor/InlineEditor.ts +306 -65
- package/packages/core/src/editor/LeftPanel.ts +1 -2
- package/packages/core/src/editor/RichTextToolbar.ts +66 -44
- package/packages/core/src/editor/RightPanel.ts +491 -74
- package/packages/core/src/editor/SourceView.ts +2 -1
- package/packages/core/src/editor/Topbar.ts +98 -7
- package/packages/core/src/editor/styles.css +378 -25
- package/packages/core/src/index.ts +12 -1
- package/packages/core/src/store/store.ts +44 -3
- package/packages/core/src/types.ts +6 -0
- package/packages/core/src/utils/richTextCommand.ts +30 -0
- package/packages/core/src/utils/sectionLayout.ts +13 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Doc → Section → Column → Block // 仅四层,不允许 Block 再含
|
|
|
23
23
|
- **界面主题**:顶栏 **太阳 / 月亮 / 显示器** 图标切换浅色、深色、跟随系统(`prefers-color-scheme`);中间邮件画布仍为白纸以贴近成品
|
|
24
24
|
- **品牌色**:可选 `accentColor` / `setAccentColor`,覆盖强调色与选区色;可选顶栏拾色器
|
|
25
25
|
- **仅搭正文**:`ui.hideMailMeta` 隐藏主题 / Preheader 与顶栏「邮件设置」,保留版式宽度与全局样式
|
|
26
|
+
- **画布清空 / 重置**:顶栏「清空画布」「重置内容」;`presetDoc` 与 `initialDoc` 分离,编辑已保存邮件时重置仍回到业务预置模板
|
|
26
27
|
- 包体可控:核心 + MJML + CodeMirror + 富文本,gzip ≈ 586KB
|
|
27
28
|
|
|
28
29
|
## 仓库结构
|
|
@@ -67,6 +68,12 @@ const editor = new MailEditor({
|
|
|
67
68
|
variables: [{ key: 'user.name', label: '用户名', sample: '张三' }],
|
|
68
69
|
sections: [],
|
|
69
70
|
},
|
|
71
|
+
/**
|
|
72
|
+
* 顶栏「重置内容」恢复的目标(与 initialDoc 独立)。
|
|
73
|
+
* 编辑页打开已保存邮件时:initialDoc = 当前稿,presetDoc = 业务默认模板。
|
|
74
|
+
* 未传时与 initialDoc 合并结果一致。
|
|
75
|
+
*/
|
|
76
|
+
// presetDoc: defaultTemplatePartial,
|
|
70
77
|
/** 右栏控件形态等,见下文「UI 选项 ui」 */
|
|
71
78
|
// ui: { preferSliderControls: true, hideMailMeta: true },
|
|
72
79
|
// autoWrapSection: true(默认)— 把 Block 拖到 Section 之间空白处时
|
|
@@ -85,6 +92,36 @@ const { mjml, html } = editor.export({ withSampleVariables: true });
|
|
|
85
92
|
|
|
86
93
|
// 整份替换文档(例如切换模板);会先失焦右栏避免旧值残留
|
|
87
94
|
// editor.setValue(nextDoc);
|
|
95
|
+
|
|
96
|
+
// 清空画布(仅 sections 置空,保留 meta / styles / variables;可 ⌘Z 撤销)
|
|
97
|
+
// editor.clearCanvas();
|
|
98
|
+
|
|
99
|
+
// 恢复为 presetDoc(或构造时 initialDoc)快照
|
|
100
|
+
// editor.resetToPreset();
|
|
101
|
+
|
|
102
|
+
// 异步加载默认模板后更新重置目标
|
|
103
|
+
// editor.setPresetDoc(defaultTemplatePartial);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 画布清空与重置
|
|
107
|
+
|
|
108
|
+
顶栏位于 **撤销 / 重做** 右侧(可用 `ui` 隐藏,见下表):
|
|
109
|
+
|
|
110
|
+
| 按钮 | 行为 |
|
|
111
|
+
|------|------|
|
|
112
|
+
| **清空画布** | 移除所有 Section / Block;`meta`、`styles`、`variables` 不变。操作前浏览器 `confirm` 确认;记入撤销栈(⌘Z 可恢复)。 |
|
|
113
|
+
| **重置内容** | 整份替换为 **`presetDoc`** 快照(未传 `presetDoc` 时等同构造时的 `initialDoc` 合并结果)。操作前 `confirm` 确认;**不**走撤销栈(与 `setValue` 相同,会清空 history)。 |
|
|
114
|
+
|
|
115
|
+
**宿主常见写法**:新建页 `initialDoc` 与 `presetDoc` 同为默认模板;编辑页 `initialDoc` 为接口返回的 `jsonContent`,`presetDoc` 仍为业务预置结构,避免「重置」把用户带回打开时的草稿。
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const editor = new MailEditor({
|
|
119
|
+
container: el,
|
|
120
|
+
blocks: allBlocks,
|
|
121
|
+
initialDoc: loadedFromApi, // 当前画布
|
|
122
|
+
presetDoc: businessDefaultTemplate, // 顶栏「重置内容」
|
|
123
|
+
onChange: (doc) => save(doc),
|
|
124
|
+
});
|
|
88
125
|
```
|
|
89
126
|
|
|
90
127
|
### 界面主题
|
|
@@ -120,6 +157,11 @@ editor.getTheme();
|
|
|
120
157
|
|------|------|
|
|
121
158
|
| `preferSliderControls?: boolean` | 为 `true` 时右栏数值、内边距、全局/组件字号等使用滑块等增强控件。默认 `false`。 |
|
|
122
159
|
| `hideMailMeta?: boolean` | 为 `true` 时:**不展示**右栏「主题」「Preheader」以及顶栏「邮件设置」按钮;右栏文档级面板改为 **「版式」(内容宽度)+「全局样式」**。`doc.meta.subject` / `preheader` 仍在数据模型中,导出 MJML 仍会生成 `<mj-title>`(可为空)、有值时才生成 `<mj-preview>`,适合发件主题由宿主系统单独维护的场景。 |
|
|
160
|
+
| `hideTopbarTitle?: boolean` | 隐藏顶栏左侧产品标题(嵌入宿主页时常用)。 |
|
|
161
|
+
| `hideTopbarMailSettings?: boolean` | 隐藏顶栏「邮件设置」按钮(与 `hideMailMeta` 叠加使用)。 |
|
|
162
|
+
| `hideTopbarFullscreen?: boolean` | 隐藏顶栏全屏按钮。 |
|
|
163
|
+
| `hideTopbarClearCanvas?: boolean` | 隐藏顶栏「清空画布」。 |
|
|
164
|
+
| `hideTopbarResetContent?: boolean` | 隐藏顶栏「重置内容」。 |
|
|
123
165
|
|
|
124
166
|
### 图片资源 `imageAssets`
|
|
125
167
|
|
|
@@ -317,6 +359,8 @@ expandPaletteDrop?: (createBlock: (type: string) => Block) => Block[];
|
|
|
317
359
|
| 删除 | 选中后按 Delete/Backspace;或工具条 🗑 |
|
|
318
360
|
| 复制 | 工具条 ⎘ 图标 |
|
|
319
361
|
| 撤销 / 重做 | ⌘Z / ⌘⇧Z(顶栏按钮也行) |
|
|
362
|
+
| 清空画布 | 顶栏「清空画布」;移除全部 Section/Block,保留全局样式与变量(可撤销) |
|
|
363
|
+
| 重置内容 | 顶栏「重置内容」;恢复为 `presetDoc`(或构造时的 `initialDoc`) |
|
|
320
364
|
| 插入变量 | 顶栏 `{{ }}`:编辑中插到光标处;否则插到聚焦输入框 |
|
|
321
365
|
| 切换源码 / 设计 | 顶栏切换 |
|
|
322
366
|
| 导出 HTML | 顶栏右上 |
|
|
@@ -332,7 +376,12 @@ expandPaletteDrop?: (createBlock: (type: string) => Block) => Block[];
|
|
|
332
376
|
|
|
333
377
|
## 数据模型 速览
|
|
334
378
|
|
|
335
|
-
`EmailDoc` 为单一事实来源;设计 / 导出 / 宿主保存均围绕该 JSON
|
|
379
|
+
`EmailDoc` 为单一事实来源;设计 / 导出 / 宿主保存均围绕该 JSON。
|
|
380
|
+
|
|
381
|
+
- **`initialDoc`**:`Partial<EmailDoc>`,与默认空邮件合并,作为**首次进入画布**的内容。
|
|
382
|
+
- **`presetDoc`**:可选,与默认空邮件合并,作为顶栏 **「重置内容」** 的目标;未传时与 `initialDoc` 相同。编辑已保存邮件时应单独传入业务默认模板,勿与 `initialDoc` 混用。
|
|
383
|
+
- **`editor.setValue(doc)`**:运行期整份替换(如切换模板);会清空撤销栈;调用前会尽量失焦右栏输入。
|
|
384
|
+
- **`editor.clearCanvas()`** / **`editor.resetToPreset()`** / **`editor.setPresetDoc(partial)`**:与顶栏按钮等价,供宿主程序化调用。
|
|
336
385
|
|
|
337
386
|
```ts
|
|
338
387
|
interface EmailDoc {
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineBlock as h, blockButtonWidthCss as A, docContentWidthCss as H, mjRawEmailTableWrap as _, mjRawCellTypographyFromStyles as
|
|
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";
|
|
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
|
-
},
|
|
14
|
+
}, $ = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), q = h({
|
|
15
15
|
type: "button",
|
|
16
16
|
name: "按钮",
|
|
17
17
|
category: "content",
|
|
@@ -69,19 +69,19 @@ 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="${
|
|
73
|
-
return `<mj-button href="${
|
|
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="${$(
|
|
74
74
|
e.backgroundColor
|
|
75
|
-
)}" color="${
|
|
75
|
+
)}" color="${$(e.color)}" border-radius="${e.borderRadius}px" font-size="${$(
|
|
76
76
|
e.fontSize
|
|
77
|
-
)}" align="${e.align}" padding="${t}" inner-padding="${o}"${i}>${
|
|
77
|
+
)}" align="${e.align}" padding="${t}" inner-padding="${o}"${i}>${$(
|
|
78
78
|
e.text
|
|
79
79
|
)}</mj-button>`;
|
|
80
80
|
},
|
|
81
81
|
renderPreview: (e) => {
|
|
82
82
|
const t = A(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="${
|
|
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>
|
|
85
85
|
</div>`;
|
|
86
86
|
}
|
|
87
87
|
}), E = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), K = h({
|
|
@@ -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
|
-
}),
|
|
139
|
+
}), C = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), X = h({
|
|
140
140
|
type: "hero",
|
|
141
141
|
name: "Hero 大图",
|
|
142
142
|
category: "content",
|
|
@@ -203,20 +203,20 @@ const m = {
|
|
|
203
203
|
height="${i}"
|
|
204
204
|
background-width="${o}"
|
|
205
205
|
background-height="${i}"
|
|
206
|
-
background-url="${
|
|
207
|
-
background-color="${
|
|
206
|
+
background-url="${C(e.backgroundUrl)}"
|
|
207
|
+
background-color="${C(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="${
|
|
213
|
+
color="${C(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`,
|
|
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);
|
|
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(${
|
|
227
|
+
background-image:linear-gradient(to bottom,rgba(0,0,0,0.25),rgba(0,0,0,0.35)),url(${r});
|
|
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:${
|
|
235
|
+
color:${C(e.textColor)};
|
|
236
236
|
overflow:hidden;
|
|
237
237
|
border-radius:2px;
|
|
238
238
|
"
|
|
@@ -242,7 +242,7 @@ const m = {
|
|
|
242
242
|
style="
|
|
243
243
|
width:100%;
|
|
244
244
|
text-align:${e.textAlign};
|
|
245
|
-
color:${
|
|
245
|
+
color:${C(e.textColor)};
|
|
246
246
|
text-shadow:0 1px 2px rgba(0,0,0,0.45);
|
|
247
247
|
"
|
|
248
248
|
>${e.content}</div>
|
|
@@ -278,7 +278,7 @@ 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,
|
|
281
|
+
return _(o, e.html, G(t.doc.styles));
|
|
282
282
|
},
|
|
283
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
284
|
}), M = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), Y = h({
|
|
@@ -405,14 +405,14 @@ const ee = [
|
|
|
405
405
|
rabbit: { color: "#FF2442", preview: "R" },
|
|
406
406
|
website: { color: "#6b7280", preview: "◇" },
|
|
407
407
|
web: { color: "#6b7280", preview: "◇" }
|
|
408
|
-
},
|
|
408
|
+
}, p = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), ne = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
409
409
|
function P(e) {
|
|
410
410
|
return oe[e] ?? { color: "#6b7280", preview: "·" };
|
|
411
411
|
}
|
|
412
412
|
function T(e) {
|
|
413
413
|
return `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`;
|
|
414
414
|
}
|
|
415
|
-
function
|
|
415
|
+
function N(e, t) {
|
|
416
416
|
const o = Math.max(1, Number(e) || 28), n = Number(t);
|
|
417
417
|
if (!Number.isFinite(n) || n <= 0) return "0px";
|
|
418
418
|
const i = o / 2;
|
|
@@ -432,21 +432,21 @@ function I(e) {
|
|
|
432
432
|
return e === "right" ? "flex-end" : e === "left" ? "flex-start" : "center";
|
|
433
433
|
}
|
|
434
434
|
function O(e, t) {
|
|
435
|
-
var
|
|
436
|
-
const o = t.labelFontSize > 0 ? t.labelFontSize : 13, n = Math.max(8, Math.min(48, o)), i = k(t.labelFontWeight),
|
|
437
|
-
return e.map((
|
|
438
|
-
var v, S,
|
|
439
|
-
const
|
|
440
|
-
`name="${
|
|
441
|
-
`href="${
|
|
442
|
-
`background-color="${
|
|
443
|
-
`color="${
|
|
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);
|
|
437
|
+
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))}"`,
|
|
441
|
+
`href="${p(l.href)}"`,
|
|
442
|
+
`background-color="${p(u)}"`,
|
|
443
|
+
`color="${p(r)}"`,
|
|
444
444
|
`font-size="${n}px"`,
|
|
445
|
-
`font-weight="${
|
|
446
|
-
].join(" "), w = (S =
|
|
447
|
-
return
|
|
448
|
-
${ne(
|
|
449
|
-
</mj-social-element>` : ` <mj-social-element ${
|
|
445
|
+
`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)}
|
|
449
|
+
</mj-social-element>` : ` <mj-social-element ${x} />`;
|
|
450
450
|
}).join(`
|
|
451
451
|
`);
|
|
452
452
|
}
|
|
@@ -457,7 +457,7 @@ const U = [
|
|
|
457
457
|
{ network: "tiktok", href: "https://www.tiktok.com/" },
|
|
458
458
|
{ network: "ig", href: "https://instagram.com/" }
|
|
459
459
|
];
|
|
460
|
-
function
|
|
460
|
+
function W(e) {
|
|
461
461
|
var o;
|
|
462
462
|
if ((o = e.elements) != null && o.length) return e.elements;
|
|
463
463
|
const t = e.links;
|
|
@@ -542,35 +542,35 @@ const ie = h({
|
|
|
542
542
|
{ key: "paddingTop", label: "外边距", type: "spacing" }
|
|
543
543
|
],
|
|
544
544
|
toMjml: (e) => {
|
|
545
|
-
const t = T(e), o =
|
|
546
|
-
var
|
|
547
|
-
return (
|
|
548
|
-
}),
|
|
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();
|
|
548
|
+
}), d = {
|
|
549
549
|
labelFontSize: Number(e.labelFontSize ?? 13),
|
|
550
550
|
labelFontWeight: k(String(e.labelFontWeight ?? "400")),
|
|
551
551
|
labelColor: String(e.labelColor ?? "#333333")
|
|
552
|
-
},
|
|
553
|
-
return
|
|
554
|
-
${
|
|
552
|
+
}, a = O(r, d);
|
|
553
|
+
return a.trim() ? `<mj-social ${i}>
|
|
554
|
+
${a}
|
|
555
555
|
</mj-social>` : `<mj-social ${i}></mj-social>`;
|
|
556
556
|
},
|
|
557
557
|
renderPreview: (e) => {
|
|
558
|
-
var
|
|
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 = ((
|
|
560
|
-
var
|
|
561
|
-
return (
|
|
562
|
-
}).map((
|
|
563
|
-
var
|
|
564
|
-
const
|
|
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) => {
|
|
560
|
+
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
565
|
e.iconSize / 2
|
|
566
|
-
)}px;font-weight:600;flex-shrink:0;overflow:hidden;">${
|
|
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(
|
|
567
567
|
i
|
|
568
|
-
)} !important;line-height:1.2;">${
|
|
569
|
-
return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${
|
|
570
|
-
|
|
568
|
+
)} !important;line-height:1.2;">${p(x)}</span>` : "";
|
|
569
|
+
return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
|
|
570
|
+
s.href
|
|
571
571
|
)}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${v}</a>${S}</span>`;
|
|
572
|
-
}).join(""),
|
|
573
|
-
return `<div style="display:flex;flex-wrap:wrap;justify-content:${
|
|
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>`;
|
|
574
574
|
}
|
|
575
575
|
}), le = h({
|
|
576
576
|
type: "social-group",
|
|
@@ -651,35 +651,35 @@ ${d}
|
|
|
651
651
|
{ key: "paddingTop", label: "外边距", type: "spacing" }
|
|
652
652
|
],
|
|
653
653
|
toMjml: (e) => {
|
|
654
|
-
const t = T(e), o =
|
|
655
|
-
var
|
|
656
|
-
return (
|
|
657
|
-
}),
|
|
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();
|
|
657
|
+
}), d = {
|
|
658
658
|
labelFontSize: Number(e.labelFontSize ?? 13),
|
|
659
659
|
labelFontWeight: k(String(e.labelFontWeight ?? "400")),
|
|
660
660
|
labelColor: String(e.labelColor ?? "#333333")
|
|
661
|
-
},
|
|
662
|
-
return
|
|
663
|
-
${
|
|
661
|
+
}, a = O(r, d);
|
|
662
|
+
return a.trim() ? `<mj-social ${i}>
|
|
663
|
+
${a}
|
|
664
664
|
</mj-social>` : `<mj-social ${i}></mj-social>`;
|
|
665
665
|
},
|
|
666
666
|
renderPreview: (e) => {
|
|
667
|
-
var
|
|
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 = ((
|
|
669
|
-
var
|
|
670
|
-
return (
|
|
671
|
-
}).map((
|
|
672
|
-
var
|
|
673
|
-
const
|
|
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) => {
|
|
669
|
+
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
674
|
e.iconSize / 2
|
|
675
|
-
)}px;font-weight:600;flex-shrink:0;overflow:hidden;">${
|
|
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(
|
|
676
676
|
i
|
|
677
|
-
)} !important;line-height:1.2;">${
|
|
678
|
-
return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${
|
|
679
|
-
|
|
677
|
+
)} !important;line-height:1.2;">${p(x)}</span>` : "";
|
|
678
|
+
return `<span style="display:inline-flex;align-items:center;gap:6px;"><a href="${p(
|
|
679
|
+
s.href
|
|
680
680
|
)}" style="display:inline-flex;align-items:center;text-decoration:none;line-height:0;">${v}</a>${S}</span>`;
|
|
681
|
-
}).join(""),
|
|
682
|
-
return `<div style="display:flex;flex-wrap:wrap;justify-content:${
|
|
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>`;
|
|
683
683
|
}
|
|
684
684
|
}), re = h({
|
|
685
685
|
type: "spacer",
|
|
@@ -698,10 +698,10 @@ ${d}
|
|
|
698
698
|
defaultProps: {
|
|
699
699
|
content: '<p style="margin:0;">在此处输入文本。<strong>双击</strong>进入编辑模式,<em>选中文字</em>会出现工具条;支持变量 {{user.name}}</p>',
|
|
700
700
|
align: "left",
|
|
701
|
-
color: "
|
|
702
|
-
fontSize: "
|
|
701
|
+
color: "",
|
|
702
|
+
fontSize: "",
|
|
703
703
|
fontFamily: "",
|
|
704
|
-
fontWeight: "
|
|
704
|
+
fontWeight: "",
|
|
705
705
|
lineHeight: "",
|
|
706
706
|
paddingTop: 8,
|
|
707
707
|
paddingRight: 0,
|
|
@@ -711,7 +711,7 @@ ${d}
|
|
|
711
711
|
schema: [
|
|
712
712
|
{
|
|
713
713
|
key: "align",
|
|
714
|
-
label: "
|
|
714
|
+
label: "对齐",
|
|
715
715
|
type: "select",
|
|
716
716
|
selectVariant: "segmented",
|
|
717
717
|
options: [
|
|
@@ -720,14 +720,15 @@ ${d}
|
|
|
720
720
|
{ label: "右", value: "right" }
|
|
721
721
|
]
|
|
722
722
|
},
|
|
723
|
-
{ key: "color", label: "
|
|
724
|
-
{ key: "fontSize", label: "
|
|
723
|
+
{ key: "color", label: "颜色", type: "color", placeholder: "继承全局" },
|
|
724
|
+
{ key: "fontSize", label: "字号", type: "text", inheritGlobal: !0 },
|
|
725
725
|
{ key: "fontFamily", label: "字体", type: "text", placeholder: "继承全局" },
|
|
726
726
|
{
|
|
727
727
|
key: "fontWeight",
|
|
728
728
|
label: "字重",
|
|
729
729
|
type: "select",
|
|
730
730
|
selectVariant: "segmented",
|
|
731
|
+
inheritGlobal: !0,
|
|
731
732
|
options: [...L]
|
|
732
733
|
},
|
|
733
734
|
{
|
|
@@ -766,14 +767,12 @@ ${d}
|
|
|
766
767
|
placeholder: "输入文本…"
|
|
767
768
|
},
|
|
768
769
|
toMjml: (e) => {
|
|
769
|
-
const t = `${e.paddingTop}px ${e.paddingRight}px ${e.paddingBottom}px ${e.paddingLeft}px`, o = e.fontFamily ? ` font-family="${B(e.fontFamily)}"` : "", n =
|
|
770
|
-
return `<mj-text align="${e.align}"
|
|
771
|
-
e.fontSize
|
|
772
|
-
)}"${g}${o}${i} padding="${t}">${e.content}</mj-text>`;
|
|
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>`;
|
|
773
772
|
},
|
|
774
773
|
renderPreview: (e) => {
|
|
775
|
-
const t = e.fontFamily ? `font-family:${e.fontFamily};` : "", o =
|
|
776
|
-
return `<div class="sm-text-content" style="text-align:${e.align}
|
|
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>`;
|
|
777
776
|
}
|
|
778
777
|
}), ce = [
|
|
779
778
|
ae,
|
|
@@ -781,7 +780,7 @@ ${d}
|
|
|
781
780
|
Y,
|
|
782
781
|
le,
|
|
783
782
|
X,
|
|
784
|
-
|
|
783
|
+
q,
|
|
785
784
|
K,
|
|
786
785
|
re
|
|
787
786
|
], se = [Z, ie, J], ge = [...ce, ...se];
|
|
@@ -789,9 +788,9 @@ export {
|
|
|
789
788
|
te as SOCIAL_NETWORK_OPTIONS,
|
|
790
789
|
ge as allBlocks,
|
|
791
790
|
ce as builtinBlocks,
|
|
792
|
-
|
|
791
|
+
q as buttonBlock,
|
|
793
792
|
K as dividerBlock,
|
|
794
|
-
|
|
793
|
+
p as escAttr,
|
|
795
794
|
ne as escMjmlText,
|
|
796
795
|
se as exampleCustomBlocks,
|
|
797
796
|
I as flexJustifyFromAlign,
|
|
@@ -806,7 +805,7 @@ export {
|
|
|
806
805
|
ie as socialBlock,
|
|
807
806
|
le as socialGroupBlock,
|
|
808
807
|
V as socialIconBorderRadiusCss,
|
|
809
|
-
|
|
808
|
+
N as socialIconBorderRadiusMjml,
|
|
810
809
|
F as socialIconSpacingPx,
|
|
811
810
|
P as socialMeta,
|
|
812
811
|
re as spacerBlock,
|