@standhigher/react-email-editor 0.3.0 → 0.4.1

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.
@@ -0,0 +1,84 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1300" viewBox="0 0 1440 1300" role="img" aria-labelledby="title description" xml:lang="en"><title id="title">Project module architecture</title><desc id="description">8 connected modules: choose content → edit → preview → save or export. Content block library, Drag-and-drop canvas, Block &amp; body settings, Subject &amp; personalization, Desktop / mobile preview, Custom blocks &amp; resources, Document &amp; editing state, Email rendering &amp; validation.</desc><defs><marker id="arrow" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto"><path d="M1 1 L7 4.5 L1 8" fill="none" stroke="#91a2b8" stroke-width="1.6"/></marker></defs><g font-family="'PingFang SC','Helvetica Neue',Arial,sans-serif">
2
+ <rect x="0" y="0" width="1440" height="1300" rx="0" fill="#ffffff" stroke="#ffffff"/>
3
+ <text x="48" y="59" font-size="38" font-weight="700" fill="#152033">Project module architecture</text>
4
+ <text x="48" y="101" font-size="23" font-weight="400" fill="#64748b">8 connected modules: choose content → edit → preview → save or export</text>
5
+ <rect x="48" y="135" width="1344" height="88" rx="16" fill="#eff4fb" stroke="#d8e1ed"/>
6
+ <text x="72" y="173" font-size="26" font-weight="650" fill="#152033">Embed in your application</text>
7
+ <text x="72" y="205" font-size="22" font-weight="400" fill="#475569">Add email editing to your website, admin app or business system</text>
8
+ <path d="M720 223 V250" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
9
+ <rect x="48" y="262" width="1344" height="514" rx="20" fill="#f8fafc" stroke="#ccd7e5"/>
10
+ <text x="72" y="305" font-size="27" font-weight="700" fill="#152033">Modules people interact with</text>
11
+ <text x="560" y="304" font-size="21" font-weight="400" fill="#64748b">Six core capabilities for creating an email</text>
12
+ <rect x="72" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
13
+ <rect x="90" y="350" width="38" height="34" rx="9" fill="#efeaff" stroke="#efeaff"/>
14
+ <text x="97" y="375" font-size="18" font-weight="700" fill="#6653c1">01</text>
15
+ <text x="142" y="376" font-size="23" font-weight="650" fill="#152033">Content block library</text>
16
+ <text x="92" y="420" font-size="20" font-weight="400" fill="#475569">Heading, text, button and image</text>
17
+ <text x="92" y="453" font-size="20" font-weight="400" fill="#475569">Divider, spacer and custom HTML</text>
18
+ <text x="92" y="486" font-size="20" font-weight="400" fill="#475569">Drag the content into your email</text>
19
+ <rect x="512" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
20
+ <rect x="530" y="350" width="38" height="34" rx="9" fill="#e7f1ff" stroke="#e7f1ff"/>
21
+ <text x="537" y="375" font-size="18" font-weight="700" fill="#266ab5">02</text>
22
+ <text x="582" y="376" font-size="23" font-weight="650" fill="#152033">Drag-and-drop canvas</text>
23
+ <text x="532" y="420" font-size="20" font-weight="400" fill="#475569">Reorder, duplicate and delete blocks</text>
24
+ <text x="532" y="453" font-size="20" font-weight="400" fill="#475569">Edit text and rich text directly</text>
25
+ <text x="532" y="486" font-size="20" font-weight="400" fill="#475569">See the layout while you edit</text>
26
+ <rect x="952" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
27
+ <rect x="970" y="350" width="38" height="34" rx="9" fill="#fff1e1" stroke="#fff1e1"/>
28
+ <text x="977" y="375" font-size="18" font-weight="700" fill="#a3652d">03</text>
29
+ <text x="1022" y="376" font-size="23" font-weight="650" fill="#152033">Block &amp; body settings</text>
30
+ <text x="972" y="420" font-size="20" font-weight="400" fill="#475569">Change content, colors and borders</text>
31
+ <text x="972" y="453" font-size="20" font-weight="400" fill="#475569">Adjust width, padding and spacing</text>
32
+ <text x="972" y="486" font-size="20" font-weight="400" fill="#475569">Set email-wide fonts and background</text>
33
+ <rect x="72" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
34
+ <rect x="90" y="563" width="38" height="34" rx="9" fill="#e7f6ef" stroke="#e7f6ef"/>
35
+ <text x="97" y="588" font-size="18" font-weight="700" fill="#268163">04</text>
36
+ <text x="142" y="589" font-size="23" font-weight="650" fill="#152033">Subject &amp; personalization</text>
37
+ <text x="92" y="633" font-size="20" font-weight="400" fill="#475569">Set the email subject</text>
38
+ <text x="92" y="666" font-size="20" font-weight="400" fill="#475569">Insert customer or store variables</text>
39
+ <text x="92" y="699" font-size="20" font-weight="400" fill="#475569">See replacements with sample data</text>
40
+ <rect x="512" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
41
+ <rect x="530" y="563" width="38" height="34" rx="9" fill="#e7f1ff" stroke="#e7f1ff"/>
42
+ <text x="537" y="588" font-size="18" font-weight="700" fill="#266ab5">05</text>
43
+ <text x="582" y="589" font-size="23" font-weight="650" fill="#152033">Desktop / mobile preview</text>
44
+ <text x="532" y="633" font-size="20" font-weight="400" fill="#475569">Switch between desktop and mobile</text>
45
+ <text x="532" y="666" font-size="20" font-weight="400" fill="#475569">Preview resolved template variables</text>
46
+ <text x="532" y="699" font-size="20" font-weight="400" fill="#475569">Review content and layout before sending</text>
47
+ <rect x="952" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
48
+ <rect x="970" y="563" width="38" height="34" rx="9" fill="#efeaff" stroke="#efeaff"/>
49
+ <text x="977" y="588" font-size="18" font-weight="700" fill="#6653c1">06</text>
50
+ <text x="1022" y="589" font-size="23" font-weight="650" fill="#152033">Custom blocks &amp; resources</text>
51
+ <text x="972" y="633" font-size="20" font-weight="400" fill="#475569">Add business blocks, e.g. product cards</text>
52
+ <text x="972" y="666" font-size="20" font-weight="400" fill="#475569">Customize editing UI and controls</text>
53
+ <text x="972" y="699" font-size="20" font-weight="400" fill="#475569">Connect your image resource picker</text>
54
+ <path d="M494 426 H504" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
55
+ <path d="M944 426 H934" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
56
+ <path d="M720 776 V812" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" />
57
+ <text x="48" y="826" font-size="24" font-weight="650" fill="#475569">Shared capabilities supporting all editing modules</text>
58
+ <path d="M720 845 V856 H386 V876" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
59
+ <path d="M720 856 H1054 V876" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
60
+ <rect x="48" y="889" width="642" height="153" rx="16" fill="#f3efff" stroke="#dcd1f5"/>
61
+ <rect x="70" y="909" width="38" height="34" rx="9" fill="#e7defb" stroke="#e7defb"/>
62
+ <text x="77" y="934" font-size="18" font-weight="700" fill="#6653c1">07</text>
63
+ <text x="122" y="936" font-size="26" font-weight="650" fill="#40347a">Document &amp; editing state</text>
64
+ <text x="72" y="976" font-size="21" font-weight="400" fill="#475569">Keep content, block order, styles and subject together</text>
65
+ <text x="72" y="1010" font-size="21" font-weight="400" fill="#475569">Load, replace and reset; editor instances stay isolated</text>
66
+ <rect x="750" y="889" width="642" height="153" rx="16" fill="#eef5ff" stroke="#cddff7"/>
67
+ <rect x="772" y="909" width="38" height="34" rx="9" fill="#dfeaff" stroke="#dfeaff"/>
68
+ <text x="779" y="934" font-size="18" font-weight="700" fill="#266ab5">08</text>
69
+ <text x="824" y="936" font-size="26" font-weight="650" fill="#225897">Email rendering &amp; validation</text>
70
+ <text x="774" y="976" font-size="21" font-weight="400" fill="#475569">Validate controls and blocks; generate email HTML</text>
71
+ <text x="774" y="1010" font-size="21" font-weight="400" fill="#475569">Keep templates for delivery or resolve data for preview</text>
72
+ <path d="M690 965 H738" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
73
+ <path d="M369 1042 V1081" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
74
+ <path d="M1071 1042 V1081" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
75
+ <rect x="48" y="1094" width="642" height="137" rx="16" fill="#edf8f1" stroke="#cce4d5"/>
76
+ <text x="72" y="1134" font-size="27" font-weight="700" fill="#286844">JSON document</text>
77
+ <text x="72" y="1170" font-size="23" font-weight="400" fill="#475569">Your app saves the draft</text>
78
+ <text x="72" y="1202" font-size="23" font-weight="400" fill="#475569">Load it later and continue editing</text>
79
+ <rect x="750" y="1094" width="642" height="137" rx="16" fill="#edf5ff" stroke="#cddff7"/>
80
+ <text x="774" y="1134" font-size="27" font-weight="700" fill="#225897">Email HTML</text>
81
+ <text x="774" y="1170" font-size="23" font-weight="400" fill="#475569">Export templates or preview output</text>
82
+ <text x="774" y="1202" font-size="23" font-weight="400" fill="#475569">Pass it to your email delivery system</text>
83
+ <text x="48" y="1272" font-size="21" font-weight="400" fill="#64748b">This project builds, previews and exports emails. Your app handles storage, uploads and delivery.</text>
84
+ </g></svg>
@@ -0,0 +1,84 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1300" viewBox="0 0 1440 1300" role="img" aria-labelledby="title description" xml:lang="zh-CN"><title id="title">项目模块架构图</title><desc id="description">8 个模块协同:选内容 → 编邮件 → 看预览 → 保存或导出. 内容区块库, 拖拽编辑画布, 属性与主体设置, 主题与个性化, 桌面 / 移动预览, 自定义区块与资源, 文档与编辑状态, 邮件渲染与校验.</desc><defs><marker id="arrow" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto"><path d="M1 1 L7 4.5 L1 8" fill="none" stroke="#91a2b8" stroke-width="1.6"/></marker></defs><g font-family="'PingFang SC','Helvetica Neue',Arial,sans-serif">
2
+ <rect x="0" y="0" width="1440" height="1300" rx="0" fill="#ffffff" stroke="#ffffff"/>
3
+ <text x="48" y="59" font-size="38" font-weight="700" fill="#152033">项目模块架构图</text>
4
+ <text x="48" y="101" font-size="23" font-weight="400" fill="#64748b">8 个模块协同:选内容 → 编邮件 → 看预览 → 保存或导出</text>
5
+ <rect x="48" y="135" width="1344" height="88" rx="16" fill="#eff4fb" stroke="#d8e1ed"/>
6
+ <text x="72" y="173" font-size="26" font-weight="650" fill="#152033">接入你的应用</text>
7
+ <text x="72" y="205" font-size="22" font-weight="400" fill="#475569">网站、运营后台或业务系统,把邮件编辑能力嵌入现有页面</text>
8
+ <path d="M720 223 V250" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
9
+ <rect x="48" y="262" width="1344" height="514" rx="20" fill="#f8fafc" stroke="#ccd7e5"/>
10
+ <text x="72" y="305" font-size="27" font-weight="700" fill="#152033">用户直接操作的模块</text>
11
+ <text x="560" y="304" font-size="21" font-weight="400" fill="#64748b">制作一封邮件时使用的 6 项核心能力</text>
12
+ <rect x="72" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
13
+ <rect x="90" y="350" width="38" height="34" rx="9" fill="#efeaff" stroke="#efeaff"/>
14
+ <text x="97" y="375" font-size="18" font-weight="700" fill="#6653c1">01</text>
15
+ <text x="142" y="376" font-size="25" font-weight="650" fill="#152033">内容区块库</text>
16
+ <text x="92" y="420" font-size="23" font-weight="400" fill="#475569">标题、正文、按钮、图片</text>
17
+ <text x="92" y="453" font-size="23" font-weight="400" fill="#475569">分割线、留白、自定义 HTML</text>
18
+ <text x="92" y="486" font-size="23" font-weight="400" fill="#475569">把需要的内容拖入邮件</text>
19
+ <rect x="512" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
20
+ <rect x="530" y="350" width="38" height="34" rx="9" fill="#e7f1ff" stroke="#e7f1ff"/>
21
+ <text x="537" y="375" font-size="18" font-weight="700" fill="#266ab5">02</text>
22
+ <text x="582" y="376" font-size="25" font-weight="650" fill="#152033">拖拽编辑画布</text>
23
+ <text x="532" y="420" font-size="23" font-weight="400" fill="#475569">排列、复制、删除内容区块</text>
24
+ <text x="532" y="453" font-size="23" font-weight="400" fill="#475569">直接编辑文字与富文本</text>
25
+ <text x="532" y="486" font-size="23" font-weight="400" fill="#475569">边编辑边查看邮件布局</text>
26
+ <rect x="952" y="332" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
27
+ <rect x="970" y="350" width="38" height="34" rx="9" fill="#fff1e1" stroke="#fff1e1"/>
28
+ <text x="977" y="375" font-size="18" font-weight="700" fill="#a3652d">03</text>
29
+ <text x="1022" y="376" font-size="25" font-weight="650" fill="#152033">属性与主体设置</text>
30
+ <text x="972" y="420" font-size="23" font-weight="400" fill="#475569">修改区块内容、颜色与边框</text>
31
+ <text x="972" y="453" font-size="23" font-weight="400" fill="#475569">调整宽度、内边距与间距</text>
32
+ <text x="972" y="486" font-size="23" font-weight="400" fill="#475569">统一设置邮件字体与背景</text>
33
+ <rect x="72" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
34
+ <rect x="90" y="563" width="38" height="34" rx="9" fill="#e7f6ef" stroke="#e7f6ef"/>
35
+ <text x="97" y="588" font-size="18" font-weight="700" fill="#268163">04</text>
36
+ <text x="142" y="589" font-size="25" font-weight="650" fill="#152033">主题与个性化</text>
37
+ <text x="92" y="633" font-size="23" font-weight="400" fill="#475569">设置邮件主题</text>
38
+ <text x="92" y="666" font-size="23" font-weight="400" fill="#475569">插入姓名、店铺等模板变量</text>
39
+ <text x="92" y="699" font-size="23" font-weight="400" fill="#475569">使用示例数据查看替换结果</text>
40
+ <rect x="512" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
41
+ <rect x="530" y="563" width="38" height="34" rx="9" fill="#e7f1ff" stroke="#e7f1ff"/>
42
+ <text x="537" y="588" font-size="18" font-weight="700" fill="#266ab5">05</text>
43
+ <text x="582" y="589" font-size="25" font-weight="650" fill="#152033">桌面 / 移动预览</text>
44
+ <text x="532" y="633" font-size="23" font-weight="400" fill="#475569">切换桌面与手机显示效果</text>
45
+ <text x="532" y="666" font-size="23" font-weight="400" fill="#475569">预览模板变量替换后的内容</text>
46
+ <text x="532" y="699" font-size="23" font-weight="400" fill="#475569">发送前检查邮件内容与版式</text>
47
+ <rect x="952" y="545" width="416" height="188" rx="16" fill="#ffffff" stroke="#d8e1ed"/>
48
+ <rect x="970" y="563" width="38" height="34" rx="9" fill="#efeaff" stroke="#efeaff"/>
49
+ <text x="977" y="588" font-size="18" font-weight="700" fill="#6653c1">06</text>
50
+ <text x="1022" y="589" font-size="25" font-weight="650" fill="#152033">自定义区块与资源</text>
51
+ <text x="972" y="633" font-size="23" font-weight="400" fill="#475569">增加商品卡片等业务区块</text>
52
+ <text x="972" y="666" font-size="23" font-weight="400" fill="#475569">定制编辑画布和属性控件</text>
53
+ <text x="972" y="699" font-size="23" font-weight="400" fill="#475569">接入已有图片资源选择器</text>
54
+ <path d="M494 426 H504" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
55
+ <path d="M944 426 H934" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
56
+ <path d="M720 776 V812" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" />
57
+ <text x="48" y="826" font-size="24" font-weight="650" fill="#475569">支撑所有编辑模块的共享能力</text>
58
+ <path d="M720 845 V856 H386 V876" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
59
+ <path d="M720 856 H1054 V876" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
60
+ <rect x="48" y="889" width="642" height="153" rx="16" fill="#f3efff" stroke="#dcd1f5"/>
61
+ <rect x="70" y="909" width="38" height="34" rx="9" fill="#e7defb" stroke="#e7defb"/>
62
+ <text x="77" y="934" font-size="18" font-weight="700" fill="#6653c1">07</text>
63
+ <text x="122" y="936" font-size="26" font-weight="650" fill="#40347a">文档与编辑状态</text>
64
+ <text x="72" y="976" font-size="23" font-weight="400" fill="#475569">统一记录邮件内容、顺序、样式与主题</text>
65
+ <text x="72" y="1010" font-size="23" font-weight="400" fill="#475569">支持载入、替换、重置;多个编辑器互不干扰</text>
66
+ <rect x="750" y="889" width="642" height="153" rx="16" fill="#eef5ff" stroke="#cddff7"/>
67
+ <rect x="772" y="909" width="38" height="34" rx="9" fill="#dfeaff" stroke="#dfeaff"/>
68
+ <text x="779" y="934" font-size="18" font-weight="700" fill="#266ab5">08</text>
69
+ <text x="824" y="936" font-size="26" font-weight="650" fill="#225897">邮件渲染与校验</text>
70
+ <text x="774" y="976" font-size="23" font-weight="400" fill="#475569">检查控件值与区块配置,生成邮件 HTML</text>
71
+ <text x="774" y="1010" font-size="23" font-weight="400" fill="#475569">保留模板供发送使用,或解析示例数据用于预览</text>
72
+ <path d="M690 965 H738" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
73
+ <path d="M369 1042 V1081" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
74
+ <path d="M1071 1042 V1081" fill="none" stroke="#91a2b8" stroke-width="2.5" stroke-linejoin="round" marker-end="url(#arrow)"/>
75
+ <rect x="48" y="1094" width="642" height="137" rx="16" fill="#edf8f1" stroke="#cce4d5"/>
76
+ <text x="72" y="1134" font-size="27" font-weight="700" fill="#286844">JSON 文档</text>
77
+ <text x="72" y="1170" font-size="23" font-weight="400" fill="#475569">交给应用保存草稿</text>
78
+ <text x="72" y="1202" font-size="23" font-weight="400" fill="#475569">下次载入,继续编辑</text>
79
+ <rect x="750" y="1094" width="642" height="137" rx="16" fill="#edf5ff" stroke="#cddff7"/>
80
+ <text x="774" y="1134" font-size="27" font-weight="700" fill="#225897">邮件 HTML</text>
81
+ <text x="774" y="1170" font-size="23" font-weight="400" fill="#475569">导出邮件模板或预览结果</text>
82
+ <text x="774" y="1202" font-size="23" font-weight="400" fill="#475569">交给应用的邮件发送系统</text>
83
+ <text x="48" y="1272" font-size="22" font-weight="400" fill="#64748b">本项目负责邮件制作、预览与导出;数据存储、文件上传、实际发信由接入应用完成。</text>
84
+ </g></svg>
@@ -0,0 +1,113 @@
1
+ # Basic examples
2
+
3
+ [简体中文](./basic-examples.zh-CN.md) | [API reference](./api.md) | [Advanced examples](./advanced-examples.md)
4
+
5
+ Import `@standhigher/react-email-editor/styles.css` once from your global stylesheet entry. Each component example can live in its own `.tsx` file. Add `"use client"` with Next.js App Router.
6
+
7
+ ## 1. Empty editor and JSON saving
8
+
9
+ ```tsx
10
+ import { useState } from "react";
11
+ import { EmailEditor, useEmailEditor } from "@standhigher/react-email-editor";
12
+
13
+ export function EmailComposer() {
14
+ const editor = useEmailEditor();
15
+ const [savedJson, setSavedJson] = useState("");
16
+
17
+ return (
18
+ <>
19
+ <EmailEditor editor={editor} height={720} />
20
+ <button type="button" onClick={() => setSavedJson(editor.getJson())}>Save JSON</button>
21
+ <output>{savedJson ? "Saved in page state" : "Not saved yet"}</output>
22
+ </>
23
+ );
24
+ }
25
+ ```
26
+
27
+ This example saves to React state, which does not survive a page reload. Your app implements database or file persistence. getDocument() returns a detached JSON object, getJson() a formatted string, and getHtml() template HTML for a sending system.
28
+
29
+ ## 2. Initial content, document switching and reset
30
+
31
+ ```tsx
32
+ import {
33
+ BLOCK_TYPES, createBlockNode, createEmailDocument,
34
+ EmailEditor, useEmailEditor, type EmailDocument,
35
+ } from "@standhigher/react-email-editor";
36
+
37
+ const heading = createBlockNode(BLOCK_TYPES.heading);
38
+ if (heading) heading.controls.html = "<h1>Welcome to our newsletter</h1>";
39
+ const initialDocument = createEmailDocument(heading ? [heading] : []);
40
+
41
+ interface RestorableEditorProps {
42
+ savedDocument: EmailDocument;
43
+ }
44
+
45
+ export function RestorableEditor({ savedDocument }: RestorableEditorProps) {
46
+ const editor = useEmailEditor({ initialDocument });
47
+ return (
48
+ <>
49
+ <EmailEditor editor={editor} height={640} />
50
+ <button type="button" onClick={() => editor.setDocument(savedDocument)}>Load saved document</button>
51
+ <button type="button" onClick={() => editor.reset()}>Restore initial content</button>
52
+ </>
53
+ );
54
+ }
55
+ ```
56
+
57
+ Your app loads and validates savedDocument as a current v4 document. TypeScript types do not replace runtime JSON validation. initialDocument is read once; use setDocument() for later replacements. reset() restores the first initialized content, not the most recently loaded document.
58
+
59
+ ## 3. Body styles, variables and preview export
60
+
61
+ ```tsx
62
+ import { useState } from "react";
63
+ import {
64
+ BLOCK_TYPES, createBlockNode, createEmailDocument, EmailEditor, useEmailEditor,
65
+ } from "@standhigher/react-email-editor";
66
+
67
+ const text = createBlockNode(BLOCK_TYPES.text);
68
+ if (text) text.controls.html = "<p>Hello, {{ customer_name }}, welcome to {{ store_name }}</p>";
69
+ const initialDocument = createEmailDocument(text ? [text] : []);
70
+
71
+ export function PersonalizedEditor() {
72
+ const [html, setHtml] = useState("");
73
+ const editor = useEmailEditor({
74
+ initialDocument,
75
+ body: { contentWidth: 640, contentPadding: "24px", backgroundColor: "#f7f7f7" },
76
+ variables: [
77
+ { name: "{{ store_name }}", description: "Store name", value: "Example Store" },
78
+ { name: "{{ customer_name }}", value: "Sam" },
79
+ ],
80
+ });
81
+ return (
82
+ <>
83
+ <EmailEditor editor={editor} height={720} />
84
+ <button type="button" onClick={() => setHtml(editor.getHtml())}>Read template HTML</button>
85
+ <button type="button" onClick={() => setHtml(editor.getRenderedHtml())}>Read preview HTML</button>
86
+ <pre>{html}</pre>
87
+ </>
88
+ );
89
+ }
90
+ ```
91
+
92
+ Template export preserves variables and dots listed tokens: {{ store_name }} becomes {{ .store_name }}. Preview export substitutes Example Store and other values. The editor includes desktop and mobile preview. This example displays source in a pre element; your app can download or upload it. See [validated saving](./advanced-examples.md#1-validate-before-async-saving) for export error handling.
93
+
94
+ ## 4. Export without mounting the editor
95
+
96
+ ```ts
97
+ import {
98
+ BLOCK_TYPES, createBlockNode, createEmailDocument, documentToHtml,
99
+ } from "@standhigher/react-email-editor";
100
+
101
+ const block = createBlockNode(BLOCK_TYPES.html);
102
+ if (!block) throw new Error("HTML block is not registered");
103
+ block.controls.html = "<p>Hello, {{.customer.name}}</p>";
104
+ const document = createEmailDocument([block]);
105
+
106
+ export const templateHtml = documentToHtml(document);
107
+ export const previewHtml = documentToHtml(document, undefined, {
108
+ resolveHtmlTemplates: true,
109
+ templateData: { customer: { name: "Sam" } },
110
+ });
111
+ ```
112
+
113
+ Each export contains one doctype, html, head and body. Custom blocks need a shared registry; see [advanced examples](./advanced-examples.md#3-business-block-with-multiple-rich-text-regions-and-a-list).
@@ -0,0 +1,113 @@
1
+ # 基础场景示例
2
+
3
+ [English](./basic-examples.md) | [API 参考](./api.zh-CN.md) | [复杂场景](./advanced-examples.zh-CN.md)
4
+
5
+ 在应用全局样式入口引入一次 `@standhigher/react-email-editor/styles.css`。以下每段组件示例可以独立放入一个 `.tsx` 文件;Next.js App Router 中添加 `"use client"`。
6
+
7
+ ## 1. 空编辑器与保存 JSON
8
+
9
+ ```tsx
10
+ import { useState } from "react";
11
+ import { EmailEditor, useEmailEditor } from "@standhigher/react-email-editor";
12
+
13
+ export function EmailComposer() {
14
+ const editor = useEmailEditor();
15
+ const [savedJson, setSavedJson] = useState("");
16
+
17
+ return (
18
+ <>
19
+ <EmailEditor editor={editor} height={720} />
20
+ <button type="button" onClick={() => setSavedJson(editor.getJson())}>保存 JSON</button>
21
+ <output>{savedJson ? "已保存到当前页面状态" : "尚未保存"}</output>
22
+ </>
23
+ );
24
+ }
25
+ ```
26
+
27
+ 该示例保存到 React 状态,刷新后不会保留;数据库或文件持久化由应用实现。`getDocument()` 返回 JSON 对象快照,`getJson()` 返回格式化字符串,`getHtml()` 返回可交给发送系统的模板 HTML。
28
+
29
+ ## 2. 初始内容、切换文档与重置
30
+
31
+ ```tsx
32
+ import {
33
+ BLOCK_TYPES, createBlockNode, createEmailDocument,
34
+ EmailEditor, useEmailEditor, type EmailDocument,
35
+ } from "@standhigher/react-email-editor";
36
+
37
+ const heading = createBlockNode(BLOCK_TYPES.heading);
38
+ if (heading) heading.controls.html = "<h1>欢迎订阅</h1>";
39
+ const initialDocument = createEmailDocument(heading ? [heading] : []);
40
+
41
+ interface RestorableEditorProps {
42
+ savedDocument: EmailDocument;
43
+ }
44
+
45
+ export function RestorableEditor({ savedDocument }: RestorableEditorProps) {
46
+ const editor = useEmailEditor({ initialDocument });
47
+ return (
48
+ <>
49
+ <EmailEditor editor={editor} height={640} />
50
+ <button type="button" onClick={() => editor.setDocument(savedDocument)}>载入已保存文档</button>
51
+ <button type="button" onClick={() => editor.reset()}>恢复初始内容</button>
52
+ </>
53
+ );
54
+ }
55
+ ```
56
+
57
+ `savedDocument` 应由应用读取并校验为当前 v4 文档;TypeScript 类型不替代运行时 JSON 校验。`initialDocument` 只读取一次;后续切换用 `setDocument()`。`reset()` 恢复首次初始化内容,不是最近载入的文档。
58
+
59
+ ## 3. 主体样式、变量与预览导出
60
+
61
+ ```tsx
62
+ import { useState } from "react";
63
+ import {
64
+ BLOCK_TYPES, createBlockNode, createEmailDocument, EmailEditor, useEmailEditor,
65
+ } from "@standhigher/react-email-editor";
66
+
67
+ const text = createBlockNode(BLOCK_TYPES.text);
68
+ if (text) text.controls.html = "<p>你好,{{ customer_name }},欢迎来到 {{ store_name }}</p>";
69
+ const initialDocument = createEmailDocument(text ? [text] : []);
70
+
71
+ export function PersonalizedEditor() {
72
+ const [html, setHtml] = useState("");
73
+ const editor = useEmailEditor({
74
+ initialDocument,
75
+ body: { contentWidth: 640, contentPadding: "24px", backgroundColor: "#f7f7f7" },
76
+ variables: [
77
+ { name: "{{ store_name }}", description: "店铺名称", value: "示例店铺" },
78
+ { name: "{{ customer_name }}", value: "李小明" },
79
+ ],
80
+ });
81
+ return (
82
+ <>
83
+ <EmailEditor editor={editor} height={720} />
84
+ <button type="button" onClick={() => setHtml(editor.getHtml())}>读取模板 HTML</button>
85
+ <button type="button" onClick={() => setHtml(editor.getRenderedHtml())}>读取预览 HTML</button>
86
+ <pre>{html}</pre>
87
+ </>
88
+ );
89
+ }
90
+ ```
91
+
92
+ 模板导出保留变量,并把列出的 `{{ store_name }}` 补成 `{{ .store_name }}`;预览导出替换为示例店铺等文本。编辑器自带桌面与移动端预览。这里用 `<pre>` 展示导出源码;应用可自行下载或上传。生产导出错误处理见[校验后保存](./advanced-examples.zh-CN.md#1-校验后异步保存)。
93
+
94
+ ## 4. 不挂载编辑器直接导出
95
+
96
+ ```ts
97
+ import {
98
+ BLOCK_TYPES, createBlockNode, createEmailDocument, documentToHtml,
99
+ } from "@standhigher/react-email-editor";
100
+
101
+ const block = createBlockNode(BLOCK_TYPES.html);
102
+ if (!block) throw new Error("HTML 区块未注册");
103
+ block.controls.html = "<p>你好,{{.customer.name}}</p>";
104
+ const document = createEmailDocument([block]);
105
+
106
+ export const templateHtml = documentToHtml(document);
107
+ export const previewHtml = documentToHtml(document, undefined, {
108
+ resolveHtmlTemplates: true,
109
+ templateData: { customer: { name: "李小明" } },
110
+ });
111
+ ```
112
+
113
+ 每份导出只有一套 doctype、html、head 和 body。自定义区块需共享注册表,见[复杂场景](./advanced-examples.zh-CN.md#3-业务区块多区域富文本与列表)。
@@ -8,18 +8,20 @@ This guide follows “example → parameters → return values → behavior” f
8
8
  | --- | --- |
9
9
  | `EmailControls` | Declare controls, value types, defaults, validation, and editing components. |
10
10
  | `defineBlock()` | Combine controls, styles, editing UI, and email output into a block plugin. |
11
- | `createEmailEditorConfig()` | Register plugins and provide block creation, document validation, and HTML export. |
11
+ | `useEmailEditorConfig()` | Register plugins and provide block creation, document validation, and HTML export. |
12
12
 
13
13
  Built-in blocks use the same mechanism. Configuration holds no document or selection state and can be shared by multiple editors. `EmailDocument` is the source of truth for document state; the React editing session handles selection, dragging, and updates.
14
14
 
15
15
  ## Complete integration example
16
16
 
17
17
  ```tsx
18
+ import { useState } from "react";
18
19
  import {
19
20
  EmailEditor,
20
21
  EmailControls,
21
22
  createEmailDocument,
22
- createEmailEditorConfig,
23
+ useEmailEditorConfig,
24
+ useEmailEditor,
23
25
  defineBlock,
24
26
  } from "@standhigher/react-email-editor";
25
27
  import "@standhigher/react-email-editor/styles.css";
@@ -40,17 +42,12 @@ const coupon = defineBlock({
40
42
  `,
41
43
  });
42
44
 
43
- // Keep outside the component, or stabilize the reference with useMemo.
44
- const config = createEmailEditorConfig({ plugins: [coupon] });
45
- const initialDocument = createEmailDocument([config.createBlock(coupon.type)]);
46
-
47
45
  export function EmailPage() {
48
- return <EmailEditor config={config} initialDocument={initialDocument} />;
46
+ const config = useEmailEditorConfig({ plugins: [coupon] });
47
+ const [initialDocument] = useState(() => createEmailDocument([config.createBlock(coupon.type)]));
48
+ const editor = useEmailEditor({ config, initialDocument });
49
+ return <EmailEditor editor={editor} />;
49
50
  }
50
-
51
- // Standalone export uses the same configuration without mounting the editor.
52
- const issues = config.validate(initialDocument);
53
- const html = config.toHtml(initialDocument);
54
51
  ```
55
52
 
56
53
  At declaration time, `controls` contains control definitions. In nodes, Canvas/Inspector props, and render contexts, it contains their values:
@@ -66,29 +63,28 @@ controls: { code: EmailControls.text({ label: "Code", defaultValue: "SAVE20" })
66
63
  controls.code // string
67
64
  ```
68
65
 
69
- ## 1. createEmailEditorConfig(options?)
66
+ ## 1. useEmailEditorConfig(options?)
70
67
 
71
68
  ### Example
72
69
 
73
- ```ts
74
- const config = createEmailEditorConfig({
75
- plugins: [coupon],
76
- builtins: ["heading", "text", "image", "button"],
77
- pickImage: async ({ value, label, signal }) => {
78
- // Open your application's asset picker; initialize with value,
79
- // use label as its title, and cancel requests with signal.
80
- // Return an image URL, or null when canceled.
81
- return null;
82
- },
83
- });
84
-
85
- const block = config.createBlock("my-app.coupon");
86
- const document = createEmailDocument([block]);
87
- const html = config.toHtml(document);
88
- const preview = config.toHtml(document, { resolveHtmlTemplates: true });
70
+ ```tsx
71
+ function useCouponConfig() {
72
+ return useEmailEditorConfig({
73
+ plugins: [coupon],
74
+ builtins: ["heading", "text", "image", "button"],
75
+ pickImage: async ({ value, label, signal }) => {
76
+ // Open your application's asset picker; initialize with value,
77
+ // use label as its title, and cancel requests with signal.
78
+ // Return an image URL, or null when canceled.
79
+ return null;
80
+ },
81
+ });
82
+ }
89
83
  ```
90
84
 
91
- ### Parameters: EmailEditorConfigOptions
85
+ Call this Hook at the top level of a React component or custom Hook. Stable `plugins`, `builtins`, and `pickImage` references retain configuration identity. Updates preserve instance documents. Removing a plugin used by a document preserves its data and produces a missing-plugin validation issue. Configuration methods `toHtml(document)` and `validate(document)` accept any document; instance methods `getHtml()` and `validate()` use the current document.
86
+
87
+ ### Parameters: UseEmailEditorConfigOptions
92
88
 
93
89
  All options are optional.
94
90
 
@@ -482,10 +478,10 @@ Missing plugins produce a canvas placeholder and preserve the node; email export
482
478
  | `ControlComponentProps<T>` | Single-control React editor props. |
483
479
  | `BlockPlugin/BlockPluginOptions` | Block definition and declaration options. |
484
480
  | `PluginEditorProps/PluginRenderContext` | Editing component props and rendering context. |
485
- | `EmailEditorConfig/EmailEditorConfigOptions` | Registration configuration and options. |
481
+ | `EmailEditorConfig/UseEmailEditorConfigOptions` | Registration configuration and options. |
486
482
  | `ImagePickerContext/ValidationIssue` | Image picker context and validation results. |
487
483
  | `ControlOption/StyleControlConfig/BlockInspectorSection` | Options, style controls, and inspector sections. |
488
484
  | `ControlEditorProps/EditableRichTextProps` | Reusable control and rich-text component props. |
489
485
  | `EmailMarkup/EmailTools` | Email fragments and rendering helpers. |
490
486
 
491
- See [README](../README.md) for editor props, ref methods, body, and variables. Runnable examples: [coupon](../playground/plugins/coupon.ts), [product card](../playground/plugins/product.ts), and [shared configuration](../playground/plugins/config.ts).
487
+ See the [API reference](./api.md) for editor Hooks, instance methods, body, and variables, and [advanced examples](./advanced-examples.md) for business integration. Runnable examples: [coupon](../playground/plugins/coupon.ts), [product card](../playground/plugins/product.ts), and [shared configuration](../playground/plugins/config.ts).
@@ -8,18 +8,20 @@
8
8
  | --- | --- |
9
9
  | `EmailControls` | 声明控件、值类型、默认值、校验和编辑组件。 |
10
10
  | `defineBlock()` | 组合控件、样式、编辑界面和邮件输出,形成一个区块插件。 |
11
- | `createEmailEditorConfig()` | 注册插件,提供区块创建、文档校验和 HTML 导出。 |
11
+ | `useEmailEditorConfig()` | 注册插件,提供区块创建、文档校验和 HTML 导出。 |
12
12
 
13
13
  内置区块使用相同机制。配置不持有具体文档或选中状态,可以被多个编辑器复用。`EmailDocument` 是文档状态的事实源,React 编辑会话负责选择、拖拽和更新。
14
14
 
15
15
  ## 完整接入示例
16
16
 
17
17
  ```tsx
18
+ import { useState } from "react";
18
19
  import {
19
20
  EmailEditor,
20
21
  EmailControls,
21
22
  createEmailDocument,
22
- createEmailEditorConfig,
23
+ useEmailEditorConfig,
24
+ useEmailEditor,
23
25
  defineBlock,
24
26
  } from "@standhigher/react-email-editor";
25
27
  import "@standhigher/react-email-editor/styles.css";
@@ -40,17 +42,12 @@ const coupon = defineBlock({
40
42
  `,
41
43
  });
42
44
 
43
- // 放在组件外,或通过 useMemo 保持引用稳定。
44
- const config = createEmailEditorConfig({ plugins: [coupon] });
45
- const initialDocument = createEmailDocument([config.createBlock(coupon.type)]);
46
-
47
45
  export function EmailPage() {
48
- return <EmailEditor config={config} initialDocument={initialDocument} />;
46
+ const config = useEmailEditorConfig({ plugins: [coupon] });
47
+ const [initialDocument] = useState(() => createEmailDocument([config.createBlock(coupon.type)]));
48
+ const editor = useEmailEditor({ config, initialDocument });
49
+ return <EmailEditor editor={editor} />;
49
50
  }
50
-
51
- // 独立导出也使用同一配置,无需挂载编辑器。
52
- const issues = config.validate(initialDocument);
53
- const html = config.toHtml(initialDocument);
54
51
  ```
55
52
 
56
53
  `controls` 在声明阶段保存控件定义,在节点、Canvas/Inspector Props 和渲染上下文中保存对应的值:
@@ -66,28 +63,27 @@ controls: { code: EmailControls.text({ label: "优惠码", defaultValue: "SAVE20
66
63
  controls.code // string
67
64
  ```
68
65
 
69
- ## 1. createEmailEditorConfig(options?)
66
+ ## 1. useEmailEditorConfig(options?)
70
67
 
71
68
  ### 示例
72
69
 
73
- ```ts
74
- const config = createEmailEditorConfig({
75
- plugins: [coupon],
76
- builtins: ["heading", "text", "image", "button"],
77
- pickImage: async ({ value, label, signal }) => {
78
- // 调用应用自己的素材库;使用 value 初始化,label 作标题,signal 取消请求。
79
- // 返回图片 URL;取消选择时返回 null。
80
- return null;
81
- },
82
- });
83
-
84
- const block = config.createBlock("my-app.coupon");
85
- const document = createEmailDocument([block]);
86
- const html = config.toHtml(document);
87
- const preview = config.toHtml(document, { resolveHtmlTemplates: true });
70
+ ```tsx
71
+ function useCouponConfig() {
72
+ return useEmailEditorConfig({
73
+ plugins: [coupon],
74
+ builtins: ["heading", "text", "image", "button"],
75
+ pickImage: async ({ value, label, signal }) => {
76
+ // 调用应用自己的素材库;使用 value 初始化,label 作标题,signal 取消请求。
77
+ // 返回图片 URL;取消选择时返回 null。
78
+ return null;
79
+ },
80
+ });
81
+ }
88
82
  ```
89
83
 
90
- ### 参数:EmailEditorConfigOptions
84
+ 配置 Hook 必须在 React 组件或自定义 Hook 顶层调用。保持 `plugins`、`builtins` 和 `pickImage` 引用稳定会保留配置引用;更新配置不会重置已有实例文档。移除仍被文档使用的插件后,校验报告缺失插件,原始数据保留。配置的 `toHtml(document)` 和 `validate(document)` 面向任意文档;实例的 `getHtml()` 和 `validate()` 面向当前文档。
85
+
86
+ ### 参数:UseEmailEditorConfigOptions
91
87
 
92
88
  所有参数均可选。
93
89
 
@@ -481,10 +477,10 @@ renderEmail: ({ controls, email }) => email.html`
481
477
  | `ControlComponentProps<T>` | 单控件 React 编辑组件。 |
482
478
  | `BlockPlugin/BlockPluginOptions` | 区块定义及声明参数。 |
483
479
  | `PluginEditorProps/PluginRenderContext` | 编辑组件 Props / 邮件渲染上下文。 |
484
- | `EmailEditorConfig/EmailEditorConfigOptions` | 注册配置及参数。 |
480
+ | `EmailEditorConfig/UseEmailEditorConfigOptions` | 注册配置及参数。 |
485
481
  | `ImagePickerContext/ValidationIssue` | 图片资源适配器 / 校验结果。 |
486
482
  | `ControlOption/StyleControlConfig/BlockInspectorSection` | 选项 / 样式控件 / 检查器分组。 |
487
483
  | `ControlEditorProps/EditableRichTextProps` | 控件复用和富文本编辑组件 Props。 |
488
484
  | `EmailMarkup/EmailTools` | 邮件片段及工具集。 |
489
485
 
490
- 编辑器 Props、ref 方法、body 和 variables 的完整接入见 [README](../README.zh-CN.md)。可运行示例见 [优惠券](../playground/plugins/coupon.ts)、[商品卡片](../playground/plugins/product.ts)及[统一配置](../playground/plugins/config.ts)。
486
+ 编辑器 Hooks、实例方法、body 和 variables 的完整接入见 [API 参考](./api.zh-CN.md),业务集成见[复杂场景](./advanced-examples.zh-CN.md)。可运行示例见 [优惠券](../playground/plugins/coupon.ts)、[商品卡片](../playground/plugins/product.ts)及[统一配置](../playground/plugins/config.ts)。