mail-editor-pancake 0.0.7 → 0.0.9
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 +174 -3
- package/package.json +1 -1
- package/packages/blocks/dist/index.d.ts +12 -0
- package/packages/blocks/dist/index.js +186 -148
- package/packages/blocks/dist/index.js.map +1 -1
- package/packages/blocks/src/index.ts +2 -0
- package/packages/blocks/src/social.ts +8 -11
- package/packages/blocks/src/socialGroup.ts +8 -11
- package/packages/blocks/src/socialShared.ts +52 -0
- package/packages/blocks/src/text.ts +21 -14
- package/packages/core/dist/index.d.ts +99 -5
- package/packages/core/dist/index.js +2475 -1480
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/style.css +1 -1
- package/packages/core/src/editor/Canvas.ts +77 -34
- package/packages/core/src/editor/ColorPickerPopover.ts +291 -0
- package/packages/core/src/editor/Editor.ts +269 -32
- 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 +316 -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 +427 -33
- package/packages/core/src/index.ts +23 -1
- package/packages/core/src/store/store.ts +44 -3
- package/packages/core/src/types.ts +8 -0
- package/packages/core/src/utils/richHtmlInsert.ts +16 -0
- package/packages/core/src/utils/richTextCommand.ts +30 -0
- package/packages/core/src/utils/sectionLayout.ts +13 -0
- package/packages/core/src/variables/index.ts +91 -0
|
@@ -28,10 +28,10 @@ export const textBlock = defineBlock<TextProps>({
|
|
|
28
28
|
content:
|
|
29
29
|
'<p style="margin:0;">在此处输入文本。<strong>双击</strong>进入编辑模式,<em>选中文字</em>会出现工具条;支持变量 {{user.name}}</p>',
|
|
30
30
|
align: 'left',
|
|
31
|
-
color: '
|
|
32
|
-
fontSize: '
|
|
31
|
+
color: '',
|
|
32
|
+
fontSize: '',
|
|
33
33
|
fontFamily: '',
|
|
34
|
-
fontWeight: '
|
|
34
|
+
fontWeight: '',
|
|
35
35
|
lineHeight: '',
|
|
36
36
|
paddingTop: 8,
|
|
37
37
|
paddingRight: 0,
|
|
@@ -41,7 +41,7 @@ export const textBlock = defineBlock<TextProps>({
|
|
|
41
41
|
schema: [
|
|
42
42
|
{
|
|
43
43
|
key: 'align',
|
|
44
|
-
label: '
|
|
44
|
+
label: '对齐',
|
|
45
45
|
type: 'select',
|
|
46
46
|
selectVariant: 'segmented',
|
|
47
47
|
options: [
|
|
@@ -50,14 +50,15 @@ export const textBlock = defineBlock<TextProps>({
|
|
|
50
50
|
{ label: '右', value: 'right' },
|
|
51
51
|
],
|
|
52
52
|
},
|
|
53
|
-
{ key: 'color', label: '
|
|
54
|
-
{ key: 'fontSize', label: '
|
|
53
|
+
{ key: 'color', label: '颜色', type: 'color', placeholder: '继承全局' },
|
|
54
|
+
{ key: 'fontSize', label: '字号', type: 'text', inheritGlobal: true },
|
|
55
55
|
{ key: 'fontFamily', label: '字体', type: 'text', placeholder: '继承全局' },
|
|
56
56
|
{
|
|
57
57
|
key: 'fontWeight',
|
|
58
58
|
label: '字重',
|
|
59
59
|
type: 'select',
|
|
60
60
|
selectVariant: 'segmented',
|
|
61
|
+
inheritGlobal: true,
|
|
61
62
|
options: [...FONT_WEIGHT_STEP_OPTIONS],
|
|
62
63
|
},
|
|
63
64
|
{
|
|
@@ -98,20 +99,26 @@ export const textBlock = defineBlock<TextProps>({
|
|
|
98
99
|
toMjml: (p) => {
|
|
99
100
|
const padding = `${p.paddingTop}px ${p.paddingRight}px ${p.paddingBottom}px ${p.paddingLeft}px`;
|
|
100
101
|
const ff = p.fontFamily ? ` font-family="${escapeAttr(p.fontFamily)}"` : '';
|
|
101
|
-
const
|
|
102
|
-
const fw =
|
|
102
|
+
const fwRaw = String(p.fontWeight ?? '').trim();
|
|
103
|
+
const fw = fwRaw ? ` font-weight="${escapeAttr(normalizeFontWeightStep(fwRaw))}"` : '';
|
|
103
104
|
const lh = String(p.lineHeight ?? '').trim();
|
|
104
105
|
const lhAttr = lh ? ` line-height="${escapeAttr(lh)}"` : '';
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const colorRaw = String(p.color ?? '').trim();
|
|
107
|
+
const colorAttr = colorRaw ? ` color="${escapeAttr(colorRaw)}"` : '';
|
|
108
|
+
const fsRaw = String(p.fontSize ?? '').trim();
|
|
109
|
+
const fsAttr = fsRaw ? ` font-size="${escapeAttr(fsRaw)}"` : '';
|
|
110
|
+
return `<mj-text align="${p.align}"${colorAttr}${fsAttr}${lhAttr}${ff}${fw} padding="${padding}">${p.content}</mj-text>`;
|
|
108
111
|
},
|
|
109
112
|
renderPreview: (p) => {
|
|
110
113
|
const ff = p.fontFamily ? `font-family:${p.fontFamily};` : '';
|
|
111
|
-
const
|
|
112
|
-
const
|
|
114
|
+
const fwRaw = String(p.fontWeight ?? '').trim();
|
|
115
|
+
const fwCss = fwRaw ? `font-weight:${normalizeFontWeightStep(fwRaw)};` : '';
|
|
113
116
|
const lhRaw = String(p.lineHeight ?? '').trim();
|
|
114
117
|
const lh = lhRaw ? `line-height:${lhRaw};` : 'line-height:normal;';
|
|
115
|
-
|
|
118
|
+
const colorRaw = String(p.color ?? '').trim();
|
|
119
|
+
const colorCss = colorRaw ? `color:${colorRaw};` : '';
|
|
120
|
+
const fsRaw = String(p.fontSize ?? '').trim();
|
|
121
|
+
const fsCss = fsRaw ? `font-size:${fsRaw};` : '';
|
|
122
|
+
return `<div class="sm-text-content" style="text-align:${p.align};${colorCss}${fsCss}${fwCss}${lh}${ff}padding:${p.paddingTop}px ${p.paddingRight}px ${p.paddingBottom}px ${p.paddingLeft}px;">${p.content}</div>`;
|
|
116
123
|
},
|
|
117
124
|
});
|
|
@@ -72,8 +72,32 @@ export declare interface BlockSchemaField {
|
|
|
72
72
|
step?: number;
|
|
73
73
|
placeholder?: string;
|
|
74
74
|
help?: string;
|
|
75
|
+
/** 为 true 时右栏显示「继承全局」开关:开=留空跟随邮件全局,关=写入当前全局值为块级固定值 */
|
|
76
|
+
inheritGlobal?: boolean;
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
export declare interface BodyVariableInsert {
|
|
80
|
+
content: string;
|
|
81
|
+
asHtml: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 插入链接 / 图片等元素片段 */
|
|
85
|
+
export declare function buildBodyVariableElementInsert(v: Variable, options?: {
|
|
86
|
+
linkColor?: string;
|
|
87
|
+
token?: string;
|
|
88
|
+
}): BodyVariableInsert;
|
|
89
|
+
|
|
90
|
+
/** @deprecated 使用 buildBodyVariableKeyInsert / buildBodyVariableElementInsert */
|
|
91
|
+
export declare function buildBodyVariableInsert(v: Variable, options?: {
|
|
92
|
+
linkColor?: string;
|
|
93
|
+
token?: string;
|
|
94
|
+
}): BodyVariableInsert;
|
|
95
|
+
|
|
96
|
+
/** 插入变量 key(`{{key}}`) */
|
|
97
|
+
export declare function buildBodyVariableKeyInsert(v: Variable): BodyVariableInsert;
|
|
98
|
+
|
|
99
|
+
export declare function buildLinkVariableHtml(token: string, linkColor: string): string;
|
|
100
|
+
|
|
77
101
|
export declare interface Column {
|
|
78
102
|
id: string;
|
|
79
103
|
attrs: ColumnAttrs;
|
|
@@ -87,6 +111,9 @@ declare interface ColumnAttrs {
|
|
|
87
111
|
|
|
88
112
|
export declare function createSection(layout: SectionLayout): Section;
|
|
89
113
|
|
|
114
|
+
/** 无后端 sample 时按 kind / label 生成预览用示例值(仅预览弹窗等场景) */
|
|
115
|
+
export declare function defaultVariableSample(v: Pick<Variable, 'key' | 'label' | 'kind' | 'sample'>): string;
|
|
116
|
+
|
|
90
117
|
/** 工厂函数,便于外部以函数式风格定义组件。 */
|
|
91
118
|
export declare function defineBlock<P extends object>(def: BlockDefinition<P>): BlockDefinition<P>;
|
|
92
119
|
|
|
@@ -162,6 +189,11 @@ declare interface EditorOptions {
|
|
|
162
189
|
imageAssets?: ImageAssetsHandlers;
|
|
163
190
|
/** 右栏控件形态等可选 UI 偏好。 */
|
|
164
191
|
ui?: EditorUiOptions;
|
|
192
|
+
/**
|
|
193
|
+
* 顶栏「重置内容」恢复的目标文档(与 `initialDoc` 独立;编辑已保存邮件时仍应指向业务预置模板)。
|
|
194
|
+
* 未传时与构造时的 `initialDoc` 合并结果一致。
|
|
195
|
+
*/
|
|
196
|
+
presetDoc?: Partial<EmailDoc>;
|
|
165
197
|
}
|
|
166
198
|
|
|
167
199
|
/** 编辑器 Chrome 配色:`system` 由 `prefers-color-scheme` 与 `data-sm-theme` 共同决定。邮件画布 `.sm-canvas` 默认仍为白纸以贴近成品。 */
|
|
@@ -194,6 +226,10 @@ export declare interface EditorUiOptions {
|
|
|
194
226
|
* 为 true 时隐藏顶栏「全屏」图标按钮(浏览器原生全屏 API,作用于编辑器根节点)。
|
|
195
227
|
*/
|
|
196
228
|
hideTopbarFullscreen?: boolean;
|
|
229
|
+
/** 为 true 时隐藏顶栏「清空画布」按钮。 */
|
|
230
|
+
hideTopbarClearCanvas?: boolean;
|
|
231
|
+
/** 为 true 时隐藏顶栏「重置内容」按钮。 */
|
|
232
|
+
hideTopbarResetContent?: boolean;
|
|
197
233
|
}
|
|
198
234
|
|
|
199
235
|
/**
|
|
@@ -335,6 +371,8 @@ declare interface InlineEditableConfig<P extends object = Record<string, unknown
|
|
|
335
371
|
placeholder?: string;
|
|
336
372
|
}
|
|
337
373
|
|
|
374
|
+
export declare function isTwoColumnLayout(layout: SectionLayout): layout is TwoColumnLayout;
|
|
375
|
+
|
|
338
376
|
declare type Listener = (detail?: DocChangedDetail) => void;
|
|
339
377
|
|
|
340
378
|
/**
|
|
@@ -366,6 +404,12 @@ export declare class MailEditor {
|
|
|
366
404
|
private changeTimer;
|
|
367
405
|
/** 显式品牌色时覆盖 CSS 变量;未设置则由 styles 按 light/dark 使用默认紫/靛 */
|
|
368
406
|
private accentColorOverride;
|
|
407
|
+
/** 顶栏开关:是否在画布上始终显示 Section / Block 轻量虚线边框 */
|
|
408
|
+
private showLayoutBorders;
|
|
409
|
+
/** 「重置内容」时恢复的文档快照(与当前画布内容无关) */
|
|
410
|
+
private presetContentDoc;
|
|
411
|
+
/** 宿主通过 setVariables 注入的列表;setValue 恢复文档后仍会写回,避免被 doc JSON 里的空数组覆盖 */
|
|
412
|
+
private configuredVariables;
|
|
369
413
|
private systemThemeMq;
|
|
370
414
|
private readonly _onSystemThemeMqChange;
|
|
371
415
|
private readonly _onFullscreenChange;
|
|
@@ -379,8 +423,30 @@ export declare class MailEditor {
|
|
|
379
423
|
constructor(opts: EditorOptions);
|
|
380
424
|
setValue(doc: EmailDoc): void;
|
|
381
425
|
getValue(): EmailDoc;
|
|
426
|
+
/**
|
|
427
|
+
* 清空画布:移除所有 Section/Block,保留 meta、styles、variables。
|
|
428
|
+
* 会提交内联编辑并清空选中。
|
|
429
|
+
*/
|
|
430
|
+
clearCanvas(): void;
|
|
431
|
+
/** 将画布恢复为构造时 `presetDoc`(或 `initialDoc`)对应的预置内容。 */
|
|
432
|
+
resetToPreset(): void;
|
|
433
|
+
/** 更新「重置内容」的目标(例如宿主异步加载默认模板后)。 */
|
|
434
|
+
setPresetDoc(partial: Partial<EmailDoc>): void;
|
|
382
435
|
setVariables(vars: Variable[]): void;
|
|
383
436
|
getVariables(): Variable[];
|
|
437
|
+
private _applyConfiguredVariables;
|
|
438
|
+
/**
|
|
439
|
+
* 插入变量 key(`{{key}}`)。
|
|
440
|
+
* @returns 是否成功插入
|
|
441
|
+
*/
|
|
442
|
+
insertVariableKey(v: Variable): boolean;
|
|
443
|
+
/**
|
|
444
|
+
* 插入链接 / 图片变量对应元素(`<a>` / image 块等)。
|
|
445
|
+
* 非 link/image 时退化为 insertVariableKey。
|
|
446
|
+
*/
|
|
447
|
+
insertVariableElement(v: Variable): boolean;
|
|
448
|
+
/** 同 {@link insertVariableKey} */
|
|
449
|
+
insertVariable(v: Variable): boolean;
|
|
384
450
|
/** 当前界面主题(`system` 不解析为 light/dark)。 */
|
|
385
451
|
getTheme(): EditorTheme;
|
|
386
452
|
/** 切换界面主题;写入 `data-sm-theme`,顶栏选择器同步。 */
|
|
@@ -414,12 +480,16 @@ export declare class MailEditor {
|
|
|
414
480
|
private _bindKeyboard;
|
|
415
481
|
private _onChange;
|
|
416
482
|
private _showVariablePopover;
|
|
417
|
-
private
|
|
483
|
+
private _showToast;
|
|
484
|
+
private _insertImageVariable;
|
|
485
|
+
/** 在焦点处插入文本或 HTML;失败时返回 false */
|
|
486
|
+
private _insertAtFocus;
|
|
418
487
|
private _showExport;
|
|
419
488
|
private _showPreview;
|
|
420
489
|
private _bindFullscreen;
|
|
421
490
|
private _unbindFullscreen;
|
|
422
491
|
private _toggleFullscreen;
|
|
492
|
+
private _toggleLayoutBorders;
|
|
423
493
|
}
|
|
424
494
|
|
|
425
495
|
export declare function metaWidthInputString(raw: unknown): string;
|
|
@@ -449,6 +519,9 @@ export declare function mjRawEmailTableWrap(paddingCss: string, cellInnerHtml: s
|
|
|
449
519
|
/** 将任意历史写法映射到最近一档(normal→400,bold→700 等) */
|
|
450
520
|
export declare function normalizeFontWeightStep(raw: string | undefined | null): FontWeightStep;
|
|
451
521
|
|
|
522
|
+
/** 规范化 Variable(补 kind、sample) */
|
|
523
|
+
export declare function normalizeVariable(v: Variable): Variable;
|
|
524
|
+
|
|
452
525
|
/**
|
|
453
526
|
* 打开内置图库弹层:搜索、分页、选图插入;可选「链接添加」「上传到图库」「删除」由 adapter 决定。
|
|
454
527
|
*/
|
|
@@ -468,12 +541,16 @@ export declare function parseMetaWidthFromUserInput(input: string): number | str
|
|
|
468
541
|
/** Section 右栏输入;空字符串表示清除宽度 */
|
|
469
542
|
export declare function parseSectionWidthFromUserInput(input: string): string | number | undefined;
|
|
470
543
|
|
|
544
|
+
/** @deprecated 请改用 {@link pruneSectionIfEmpty},避免误删多个空 Section */
|
|
545
|
+
export declare function pruneEmptySections(draft: EmailDoc): void;
|
|
546
|
+
|
|
471
547
|
/**
|
|
472
|
-
*
|
|
473
|
-
* 在内容块被删除或拖走后调用,可与 `autoWrapSection`
|
|
474
|
-
*
|
|
548
|
+
* 若指定 Section 的每一列都没有 Block,则从文档中移除该 Section。
|
|
549
|
+
* 在内容块被删除或拖走后调用,可与 `autoWrapSection` 搭配,避免单列 Section 在唯一块离场后仍残留空壳;
|
|
550
|
+
* 多列 Section 始终保留,便于继续向各列拖入内容。
|
|
551
|
+
* 仅处理刚变空的 Section,不会误删用户事先添加、尚未拖入内容的空布局。
|
|
475
552
|
*/
|
|
476
|
-
export declare function
|
|
553
|
+
export declare function pruneSectionIfEmpty(draft: EmailDoc, sectionId: string): void;
|
|
477
554
|
|
|
478
555
|
/**
|
|
479
556
|
* 组件注册表。所有内置/自定义组件都通过 `defineBlock` 注册进来;
|
|
@@ -571,6 +648,9 @@ export { Selection_2 as Selection }
|
|
|
571
648
|
|
|
572
649
|
declare type SelectionListener = () => void;
|
|
573
650
|
|
|
651
|
+
/** 仅更新 layout 字段;两列间切换时列数不变,列内块按列下标保留 */
|
|
652
|
+
export declare function setSectionLayout(section: Section, layout: SectionLayout): void;
|
|
653
|
+
|
|
574
654
|
/**
|
|
575
655
|
* 极小的不可变 store + 发布订阅 + history。
|
|
576
656
|
* 所有变更都通过 `update(fn)` 走结构化克隆,保证:
|
|
@@ -607,12 +687,26 @@ export declare class Store {
|
|
|
607
687
|
private _notify;
|
|
608
688
|
}
|
|
609
689
|
|
|
690
|
+
/** 从 Mustache token 提取 key,如 `{{username}}` → `username` */
|
|
691
|
+
export declare function tokenToVariableKey(token: string): string;
|
|
692
|
+
|
|
693
|
+
/** 两列 Section 的列宽比例(左栏拖入默认 `1-1`,右栏可改为 `1-2` / `2-1`) */
|
|
694
|
+
export declare const TWO_COLUMN_LAYOUTS: readonly ["1-1", "1-2", "2-1"];
|
|
695
|
+
|
|
696
|
+
export declare type TwoColumnLayout = (typeof TWO_COLUMN_LAYOUTS)[number];
|
|
697
|
+
|
|
610
698
|
export declare interface Variable {
|
|
611
699
|
/** Mustache 风格变量名,例如 user.name */
|
|
612
700
|
key: string;
|
|
613
701
|
label: string;
|
|
614
702
|
/** 预览数据,仅设计态预览使用 */
|
|
615
703
|
sample?: string;
|
|
704
|
+
/** 插入语义:纯文本占位 / 链接片段 / 图片块 */
|
|
705
|
+
kind?: 'text' | 'link' | 'image';
|
|
616
706
|
}
|
|
617
707
|
|
|
708
|
+
export declare type VariableKind = 'text' | 'link' | 'image';
|
|
709
|
+
|
|
710
|
+
export declare function variablePlaceholder(key: string): string;
|
|
711
|
+
|
|
618
712
|
export { }
|