@standhigher/react-email-editor 0.1.0 → 0.3.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.
- package/CHANGELOG.md +63 -0
- package/README.md +96 -182
- package/README.zh-CN.md +96 -182
- package/dist/index.d.ts +325 -104
- package/dist/index.js +4730 -3310
- package/dist/styles.css +858 -394
- package/docs/custom-blocks.md +491 -0
- package/docs/custom-blocks.zh-CN.md +490 -0
- package/package.json +23 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
## 0.3.0 — 2026-10-28
|
|
4
|
+
|
|
5
|
+
### 新增与改进
|
|
6
|
+
|
|
7
|
+
- 新增 `defineBlock()`、`EmailControls` 和 `createEmailEditorConfig()`,编辑器与独立邮件导出共用插件配置;内置区块库支持筛选、排序和显式覆盖。
|
|
8
|
+
- 区块插件支持自定义控件组件、React Canvas/Inspector、多区域 `EditableRichText`、`ControlEditor`、声明式嵌套列表、图片资源选择器与统一邮件 HTML 工具。
|
|
9
|
+
- 新增 `config.validate()`,汇总数字范围、选项、URL/图片协议、列表数量及业务校验问题;`setControls()` 支持一次更新多个关联控件。
|
|
10
|
+
- 补充中英文自定义区块 API 文档、优惠券及商品卡片示例,以及组件级回归测试。
|
|
11
|
+
|
|
12
|
+
### 修复
|
|
13
|
+
|
|
14
|
+
- 修改链接 URL 或打开方式时保留已有的显式颜色和下划线,重置后恢复继承 BODY。
|
|
15
|
+
- 画布主体使用与预览及邮件导出一致的基础排版,修复 `em` 内边距、边框等尺寸的计算差异。
|
|
16
|
+
- 修复列表异步回写覆盖后续编辑或排序的问题;按稳定行标识合并最新值,删除或卸载后忽略过期结果,新行不复用已删除行的标识。
|
|
17
|
+
- 自定义控件接收独立的值、主体配置和变量快照,避免直接修改参数污染宿主;自定义 Canvas/Inspector 的变量同步隔离。
|
|
18
|
+
- 可选下拉框显示空选项并支持清空;控件标签正确关联下拉框。
|
|
19
|
+
- 检查器富文本继承主体及已启用的区块排版、段落、列表和链接样式,主体配置变化后同步更新。
|
|
20
|
+
- 缺失插件或邮件导出失败时明确报错;画布和检查器异常按区块隔离,并保留原始节点数据。
|
|
21
|
+
|
|
22
|
+
### 不兼容变更
|
|
23
|
+
|
|
24
|
+
- 自定义区块改为 `defineBlock` + `EmailControls` + `createEmailEditorConfig`,组件接收 `config`;移除旧 `blocks` Prop、公开 `BlockDefinition/FieldConfig/FieldType` 和 `resolveBlockStyleFields`,不保留适配层。控件工厂使用 `EmailControls.text(...)`,区块及列表通过 `controls` 声明控件(不保留 `field` / `fields` 别名);运行时、持久化节点统一使用 `controls`,样式声明使用 `styleControls`,更新方法使用 `setControl/setControls`,配套类型、`ControlEditor` 和 `controlPath` 同步更名。内容默认值来自控件声明,渲染函数改为 `renderEmail`,文档格式升为 v4,不兼容旧数据、不提供自动迁移。
|
|
25
|
+
- 邮件主体设置统一改为 `body`:文档使用 `EmailDocument.body`,组件使用 `body` / `onBodyChange`,类型及工具改为 `EmailBodySettings`、`DEFAULT_EMAIL_BODY`、`resolveEmailBody`;区块渲染上下文和 `BlockRenderOptions` 同步使用 `body`。移除旧 Global API,不保留别名或迁移代码。
|
|
26
|
+
- 文档版本升为 `4`,不支持旧版本。BODY 的 General 提供文字颜色、链接颜色、字体、字重和链接下划线开关;Content 提供唯一主体 Background、内容宽度、Padding 和区块间距,边框颜色、宽度及样式收进默认折叠的 Advanced: Border。配置同步用于画布、预览和导出,内容显式格式保持优先。仅保留 `contentPadding` 内边距,移除 `outerPadding`、主体对齐、外侧背景和圆角配置;主体固定居中,外侧固定白色。
|
|
27
|
+
|
|
28
|
+
## 0.2.0 — 2026-10-27
|
|
29
|
+
|
|
30
|
+
### 不兼容变更
|
|
31
|
+
|
|
32
|
+
- 文档格式由 `version: 1` 升为 `version: 2`,旧版本会被拒绝,不提供自动迁移。请使用 `createEmailDocument()` 创建当前版本文档;升级已有数据时,需同时调整区块字段和样式,不能只修改版本号。
|
|
33
|
+
- Heading、Text、Button 的文字格式统一存于 `fields.html`;Button 不再读取 `fields.label` 和 `style.element` 中的文字样式。`EmailElementStyle` 不再继承 `EmailTypographyStyle`。
|
|
34
|
+
- 内置区块按各自能力声明 `styleFields`,不再读取未启用的样式;移除内置区块的 `dimension.height` 和区块级 `typography` 配置。自定义区块仍可显式启用这些字段,`exactStyleFields` 同时约束默认样式、画布和邮件输出。
|
|
35
|
+
- HTML 区块保留源码内部的段落、列表、链接和显式样式,不再注入正文排版;未声明的属性仍可继承全局样式。
|
|
36
|
+
|
|
37
|
+
### 新增与改进
|
|
38
|
+
|
|
39
|
+
- Button 支持画布内富文本编辑,采用单段内容,粘贴多段时以空格连接;邮件输出保留基础行内格式,并只生成一个按钮外层链接。
|
|
40
|
+
- 新增 `BlockInspectorSection`、`inspectorSections`、`RichTextProfile` 和 `inlineEditProfile`,支持自定义检查器分组与富文本编辑能力;独立 `RichText` 支持 `profile`。
|
|
41
|
+
- 统一颜色面板、HEX 输入和颜色重置;链接编辑改为居中弹窗,支持颜色、下划线和打开方式。
|
|
42
|
+
- 优化区块操作栏、拖拽反馈、三栏布局、工具栏换行和预览高度;预览跟随文档内容展示。
|
|
43
|
+
- 按钮默认填充改为黑色,按钮和图片的链接默认为空,空白链接不生成跳转;全局内容、区块、按钮和图片边框默认透明。
|
|
44
|
+
- 精简中英文 README,补充 npm keywords,发布包包含中文 README 和更新日志。
|
|
45
|
+
|
|
46
|
+
### 修复与验证
|
|
47
|
+
|
|
48
|
+
- 修复富文本格式操作后的光标、选区和格式保留,以及列表切换时的间距和缩进保留。
|
|
49
|
+
- 修复按钮导出对模板表达式中引号、比较符、注释和空格的处理。
|
|
50
|
+
- 接入 Oxlint,补充区块配置、富文本、列表、按钮和颜色转换回归测试。
|
|
51
|
+
|
|
52
|
+
## 0.1.0 — 2026-10-27
|
|
53
|
+
|
|
54
|
+
首个正式发布版本(对应提交 `48011d9`)。
|
|
55
|
+
|
|
56
|
+
- 提供 React 18 编辑器、拖拽区块、富文本编辑、检查器及桌面/移动预览。
|
|
57
|
+
- 支持 JSON 文档、全局和区块样式、HTML 源码编辑、自定义区块与 Go template 预览。
|
|
58
|
+
- 提供组件 ref 数据读取、独立 HTML 渲染、ESM、TypeScript 类型和独立 CSS 入口。
|
|
59
|
+
- 提供 Vite playground、构建和本地 link 联调流程。
|
|
60
|
+
|
|
61
|
+
## 0.0.0-stage — 2026-10-27
|
|
62
|
+
|
|
63
|
+
- 发布前的包分发验证版本。
|
package/README.md
CHANGED
|
@@ -2,244 +2,158 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](./README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
A React email editor with drag-and-drop blocks, inline rich text,
|
|
5
|
+
A React email template editor with drag-and-drop blocks, inline rich text, desktop/mobile previews, JSON storage, and HTML export.
|
|
6
6
|
|
|
7
|
-
## Installation
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
|
-
Requires React 18 and React DOM 18.
|
|
9
|
+
Requires React 18 and React DOM 18. Includes ESM and TypeScript declarations.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
pnpm add @standhigher/react-email-editor
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
Import the stylesheet once in your application and use a ref to read the document and HTML. Your application handles persistence.
|
|
16
18
|
|
|
17
19
|
```tsx
|
|
18
20
|
import { useRef } from "react";
|
|
19
21
|
import {
|
|
22
|
+
BLOCK_TYPES,
|
|
20
23
|
EmailEditor,
|
|
24
|
+
createBlockNode,
|
|
25
|
+
createEmailDocument,
|
|
21
26
|
type EmailEditorHandle,
|
|
27
|
+
type Variable,
|
|
22
28
|
} from "@standhigher/react-email-editor";
|
|
23
29
|
import "@standhigher/react-email-editor/styles.css";
|
|
24
30
|
|
|
31
|
+
const text = createBlockNode(BLOCK_TYPES.text);
|
|
32
|
+
if (text) text.controls.html = "<p>Hello, {{ store_name }}</p>";
|
|
33
|
+
const initialDocument = createEmailDocument(text ? [text] : []);
|
|
34
|
+
const variables: Variable[] = [
|
|
35
|
+
{ name: "{{ store_name }}", description: "Store name", value: "Example Store" },
|
|
36
|
+
];
|
|
37
|
+
|
|
25
38
|
export function EmailPage() {
|
|
26
39
|
const editorRef = useRef<EmailEditorHandle>(null);
|
|
27
40
|
|
|
28
|
-
function
|
|
41
|
+
function save() {
|
|
29
42
|
const data = editorRef.current?.getData();
|
|
30
43
|
if (!data) return;
|
|
31
|
-
//
|
|
32
|
-
// to your application's save or export handler.
|
|
44
|
+
// Persist data.document in your app; use data.html to export the template.
|
|
33
45
|
console.log(data);
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
return (
|
|
37
49
|
<>
|
|
38
|
-
<EmailEditor
|
|
39
|
-
|
|
50
|
+
<EmailEditor
|
|
51
|
+
ref={editorRef}
|
|
52
|
+
initialDocument={initialDocument}
|
|
53
|
+
variables={variables}
|
|
54
|
+
height={720}
|
|
55
|
+
/>
|
|
56
|
+
<button type="button" onClick={save}>Save</button>
|
|
40
57
|
</>
|
|
41
58
|
);
|
|
42
59
|
}
|
|
43
60
|
```
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
## Local development
|
|
48
|
-
|
|
49
|
-
Use pnpm and a current Node.js LTS release; Node.js 22.18+ is recommended for the repository's tooling and TypeScript tests.
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pnpm install --frozen-lockfile
|
|
53
|
-
pnpm dev
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Open [http://localhost:5173](http://localhost:5173). The playground loads `src/` directly with hot reload; no initial build is needed. It is not included in the published package. Reuse an existing development server when one is already running.
|
|
57
|
-
|
|
58
|
-
| Command | Purpose |
|
|
59
|
-
| --- | --- |
|
|
60
|
-
| `pnpm dev` | Start the local playground on port 5173. |
|
|
61
|
-
| `pnpm build` | Generate ESM, type declarations, and CSS in `dist/`. |
|
|
62
|
-
| `pnpm build:watch` | Rebuild `dist/` when files in `src/` change; does not start a preview server. |
|
|
63
|
-
| `pnpm typecheck` | Type-check the library and playground. |
|
|
64
|
-
|
|
65
|
-
To integrate with another local project, set its dependency to the library's root directory, adjusting the relative path as needed:
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{
|
|
69
|
-
"dependencies": {
|
|
70
|
-
"@standhigher/react-email-editor": "link:../react-email-editor"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Run `pnpm build` in this repository first, then keep `pnpm build:watch` running during development. In the consuming project, run `pnpm install` and use its development server. Package imports, including CSS, resolve to `dist/`.
|
|
62
|
+
Omit `initialDocument` to start empty. In Next.js App Router, add `"use client"` to the integration component and import CSS from an allowed global stylesheet entry. When using `height="100%"`, give the parent a definite height.
|
|
76
63
|
|
|
77
|
-
|
|
64
|
+
## Props
|
|
78
65
|
|
|
79
|
-
|
|
66
|
+
All props are optional.
|
|
80
67
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| Prop | Type | Default / behavior |
|
|
68
|
+
| Prop | Type | Behavior |
|
|
84
69
|
| --- | --- | --- |
|
|
85
|
-
| `initialDocument` | `EmailDocument` |
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `variables` | `Variable[]` |
|
|
89
|
-
| `
|
|
90
|
-
| `onChange` | `(document: EmailDocument) => void` | Called
|
|
91
|
-
| `
|
|
70
|
+
| `initialDocument` | `EmailDocument` | Read on first mount only. Remount with a new React `key` to switch documents. |
|
|
71
|
+
| `height` | `number \| string` | Defaults to `"100%"`; numbers are pixels. |
|
|
72
|
+
| `body` | `Partial<EmailBodySettings>` | Overrides email body layout, appearance, and default typography; later changes merge into the current document. Omitting a field does not restore its default. |
|
|
73
|
+
| `variables` | `Variable[]` | Variables for insertion and preview. Defaults to `[]`; Personalization controls are hidden when empty. |
|
|
74
|
+
| `config` | `EmailEditorConfig` | Registers plugins, resource adapters, and library order through `createEmailEditorConfig()`. |
|
|
75
|
+
| `onChange` | `(document: EmailDocument) => void` | Called on document changes, including external `body` updates that change document values. |
|
|
76
|
+
| `onBodyChange` | `(body: EmailBodySettings) => void` | Called for body style edits inside the editor, excluding external `body` updates. |
|
|
92
77
|
|
|
93
|
-
|
|
78
|
+
Body style example: `body={{ contentWidth: 640, contentPadding: "24px" }}`. Defaults are exported as `DEFAULT_EMAIL_BODY`; normalize a partial configuration with `resolveEmailBody()`.
|
|
94
79
|
|
|
95
|
-
|
|
80
|
+
The **BODY** panel stores its settings in `EmailDocument.body` and applies them to the canvas, preview, and HTML export:
|
|
96
81
|
|
|
97
|
-
|
|
82
|
+
- **General**: text color, link color, font family, font weight, and a link underline switch.
|
|
83
|
+
- **Content**: Background, content width, Padding (`contentPadding`), and block gap. **Advanced: Border** is collapsed by default and contains border color, width, and style. Background is the single background color of the email content container; the surrounding area is white. The container is always centered. Padding applies inside the content border and is the only body padding setting; `outerPadding` has been removed.
|
|
98
84
|
|
|
99
|
-
|
|
85
|
+
Text and links inherit BODY defaults unless explicitly formatted in the rich-text toolbar. Editing only a link URL or target preserves explicit link styles; resetting a link style restores BODY inheritance. Base font size (16px), line height (1.5), and paragraph/list spacing use internal defaults. HTML blocks preserve their source formatting; their wrapper inherits body typography without rewriting links or lists. BODY does not expose alignment or corner radius.
|
|
100
86
|
|
|
101
|
-
|
|
102
|
-
import {
|
|
103
|
-
BLOCK_TYPES,
|
|
104
|
-
EmailEditor,
|
|
105
|
-
createBlockNode,
|
|
106
|
-
createEmailDocument,
|
|
107
|
-
type Variable,
|
|
108
|
-
} from "@standhigher/react-email-editor";
|
|
87
|
+
Built-in blocks: `heading`, `text`, `button`, `image`, `divider`, `spacer`, and `html`, available through `BLOCK_TYPES`.
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
if (text) text.fields.html = "<p>Hello {{ store_name }}</p>";
|
|
89
|
+
## Save and export
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
|
|
91
|
+
| Ref method | Returns |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `getData()` | `{ document, json, html, renderedHtml }` |
|
|
94
|
+
| `getDocument()` | An independent `EmailDocument` snapshot to persist and pass back as `initialDocument`. |
|
|
95
|
+
| `getJson()` | A formatted document JSON string. |
|
|
96
|
+
| `getHtml()` | A complete email HTML document preserving template expressions. |
|
|
97
|
+
| `getRenderedHtml()` | A complete email HTML document with templates resolved using preview data. |
|
|
98
|
+
|
|
99
|
+
Render a document without mounting the editor:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { documentToHtml } from "@standhigher/react-email-editor";
|
|
103
|
+
|
|
104
|
+
const html = documentToHtml(initialDocument, undefined, { variables });
|
|
105
|
+
const previewHtml = documentToHtml(initialDocument, undefined, {
|
|
106
|
+
variables,
|
|
107
|
+
resolveHtmlTemplates: true,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
{ name: "{{ store_name }}", description: "Store name", value: "Demo Store" },
|
|
118
|
-
];
|
|
111
|
+
A variable's `name` is inserted verbatim; `description` and preview `value` are optional. Export adds a missing dot only to tokens matching `variables[].name` exactly, preserving whitespace: `{{ store_name }}` → `{{ .store_name }}`. Other Go template expressions remain unchanged. Preview supports a subset of Go template syntax; data precedence is block `defaultData` > `templateData` (`documentToHtml` option) > `variables`.
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<EmailEditor
|
|
123
|
-
initialDocument={initialDocument}
|
|
124
|
-
variables={variables}
|
|
125
|
-
height={720}
|
|
126
|
-
/>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
```
|
|
113
|
+
The document format is under development. Only `version: 4` is accepted, with no automatic migration of older documents. Create data with `createEmailDocument()` and `createBlockNode()`. See [CHANGELOG](./CHANGELOG.md) for upgrade notes.
|
|
130
114
|
|
|
131
|
-
|
|
115
|
+
## Custom blocks
|
|
132
116
|
|
|
133
|
-
|
|
134
|
-
| --- | --- | --- |
|
|
135
|
-
| `version` | `typeof DOCUMENT_VERSION` | Current version: `1`. Set by `createEmailDocument()`. |
|
|
136
|
-
| `subject` | `string` (optional) | Email subject. The editor input is limited to 128 characters. |
|
|
137
|
-
| `global` | `EmailGlobalSettings` | Global email appearance; defaults are populated by the helper. |
|
|
138
|
-
| `blocks` | `EmailBlockNode[]` | Ordered nodes, each with a unique `id`, a `type`, and `fields`, plus optional `style` and preview `defaultData`. |
|
|
139
|
-
|
|
140
|
-
The document format is under development; older block fields are not migrated automatically. See the [document types](./src/types/document.ts) for the full model.
|
|
141
|
-
|
|
142
|
-
### Global styles
|
|
143
|
-
|
|
144
|
-
Pass partial settings through `global`, for example `<EmailEditor global={{ contentWidth: 640, outerPadding: "24px" }} />`. `DEFAULT_EMAIL_GLOBAL` exposes all defaults; `resolveEmailGlobal()` fills missing values and normalizes input.
|
|
145
|
-
|
|
146
|
-
These settings control email content. Editor selection outlines and toolbars are not included in exported HTML.
|
|
147
|
-
|
|
148
|
-
| Parameter | Type / values | Default | Description |
|
|
149
|
-
| --- | --- | --- | --- |
|
|
150
|
-
| `contentWidth` | `number` | `600` | Content width in pixels, rounded and clamped to 320–900. |
|
|
151
|
-
| `contentAlign` | `"left" \| "center" \| "right"` | `"center"` | Horizontal position of the content. |
|
|
152
|
-
| `backgroundColor` | `string` | `"#ffffff"` | Outer email background. |
|
|
153
|
-
| `contentBackgroundColor` | `string` | `"#ffffff"` | Content background. |
|
|
154
|
-
| `contentPadding` | `string` | `"0px"` | Content inner spacing. |
|
|
155
|
-
| `outerPadding` | `string` | `"0px"` | Spacing outside the content. |
|
|
156
|
-
| `blockGap` | `string` | `"4px"` | Gap between blocks. |
|
|
157
|
-
| `textColor` | `string` | `"#000000"` | Default text color. |
|
|
158
|
-
| `textAlign` | `"left" \| "center" \| "right" \| "justify"` | `"left"` | Default text alignment. |
|
|
159
|
-
| `fontFamily` | `string` | `"Arial, Helvetica, sans-serif"` | Default font stack. |
|
|
160
|
-
| `fontWeight` | `"normal" \| "bold"` | `"normal"` | Default font weight. |
|
|
161
|
-
| `defaultFontSize` | `string` | `"16px"` | Default font size. |
|
|
162
|
-
| `defaultLineHeight` | `string` | `"1.5"` | Default line height. |
|
|
163
|
-
| `paragraphSpacing` | `string` | `"6px"` | Gap between adjacent top-level paragraphs. |
|
|
164
|
-
| `listSpacing` | `string` | `"4px"` | Top and bottom margins of the entire list (`ul` / `ol`). |
|
|
165
|
-
| `listIndent` | `string` | `"22px"` | List indentation. |
|
|
166
|
-
| `linkColor` | `string` | `"#005bd3"` | Default link color. |
|
|
167
|
-
| `linkDecoration` | `"underline" \| "none"` | `"underline"` | Default link decoration. |
|
|
168
|
-
| `borderWidth` | `string` | `"0px"` | Content border width. |
|
|
169
|
-
| `borderStyle` | `"none" \| "solid" \| "dashed" \| "dotted"` | `"solid"` | Content border style. |
|
|
170
|
-
| `borderColor` | `string` | `"#000000"` | Content border color. |
|
|
171
|
-
| `borderRadius` | `string` | `"0px"` | One radius or four, ordered top-left, top-right, bottom-right, bottom-left. |
|
|
172
|
-
|
|
173
|
-
Use CSS length strings such as `"16px"` for spacing and sizes. Padding accepts shorthand such as `"12px 24px"`; line height also accepts a unitless value. Desktop canvas and preview follow `contentWidth`; mobile uses a 360px viewport.
|
|
174
|
-
|
|
175
|
-
Global spacing and length settings accept non-negative values in `px`, `em`, `rem`, or `%`; unitless lengths are normalized to px, except for line height. Unsupported values such as negative lengths, `calc()`, `var()`, `vh`, or `vw` fall back to the corresponding global default.
|
|
176
|
-
|
|
177
|
-
### Variables and template data
|
|
117
|
+
Declare controls and email output with `defineBlock()`, then register once:
|
|
178
118
|
|
|
179
119
|
```tsx
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
120
|
+
const coupon = defineBlock({
|
|
121
|
+
type: "my-app.coupon",
|
|
122
|
+
title: "Coupon",
|
|
123
|
+
controls: {
|
|
124
|
+
code: EmailControls.text({ label: "Code", defaultValue: "SAVE20" }),
|
|
125
|
+
},
|
|
126
|
+
renderEmail: ({ controls, email }) => email.html`<p>${controls.code}</p>`,
|
|
127
|
+
});
|
|
128
|
+
const config = createEmailEditorConfig({ plugins: [coupon] });
|
|
129
|
+
|
|
130
|
+
<EmailEditor config={config} />;
|
|
131
|
+
const block = config.createBlock("my-app.coupon");
|
|
132
|
+
const html = config.toHtml(createEmailDocument([block]));
|
|
185
133
|
```
|
|
186
134
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
| Field | Type | Description |
|
|
190
|
-
| --- | --- | --- |
|
|
191
|
-
| `name` | `string` | Required. Token displayed and inserted as written, including spaces. Example: `{{ store_name }}`. |
|
|
192
|
-
| `description` | `string` (optional) | Secondary text in the variable menu; omitted when empty or absent. |
|
|
193
|
-
| `value` | `string` (optional) | Replacement text used in previews. |
|
|
194
|
-
|
|
195
|
-
Tokens may include a leading dot. Template HTML export preserves spaces and adds a missing dot only when the token exactly matches `variables[].name`, including spaces: `{{ store_name }}` becomes `{{ .store_name }}`. Other Go template actions are left unchanged. Export does not substitute preview values by default.
|
|
196
|
-
|
|
197
|
-
Use a block node's `defaultData: Record<string, unknown>` for structured preview data, including arrays for `{{range .products}}`. Block data overrides variables with the same name. Preview resolution applies to every block's rendered HTML, including custom blocks. Write template expressions directly in an HTML block's `fields.html`; the preview renderer supports a subset of Go template syntax.
|
|
135
|
+
Import `defineBlock`, `EmailControls`, and `createEmailEditorConfig` from the package. Control values are inferred; the inspector and canvas work automatically. Plugins support rich text, editable lists, image resource adapters, custom control components, and optional React `Inspector` / `Canvas` components. `EditableRichText` supports multiple editing regions, while `ControlEditor` reuses declared controls inside custom inspectors.
|
|
198
136
|
|
|
199
|
-
|
|
137
|
+
`config.validate(document)` returns `ValidationIssue[]` with block IDs and control paths for export preflight. Number ranges, options, URL/image protocols, list counts, and custom validation also apply to the inspector and email export. Custom Canvas/Inspector components can call `setControls(patch)` to update related controls in one document change and one `onChange` callback. Declaration metadata is copied and frozen, parsed controls are reused within each context, and `onChange` / `onBodyChange` receive independent data snapshots.
|
|
200
138
|
|
|
201
|
-
|
|
139
|
+
The editor and standalone export share one configuration. Use `builtins` to filter and order the library; replacing a built-in requires `override: true`. Declarations, runtime contexts, and persisted nodes all use `controls`. Documents use v4, with no old-data compatibility. The old `blocks`, `defaultFields`, `inlineEdit`, and `render()` integration is removed. See the [custom block API guide](./docs/custom-blocks.md) ([简体中文](./docs/custom-blocks.zh-CN.md)) and runnable `playground/plugins/` examples.
|
|
202
140
|
|
|
203
|
-
|
|
204
|
-
| --- | --- |
|
|
205
|
-
| `style.dimension` | Container `padding`, outer spacing via `margin`, and optional `height` in px. Height acts as a minimum including padding. |
|
|
206
|
-
| `style.decorations` | Container background, border, and corner radius. |
|
|
207
|
-
| `style.typography` | Block text styles overriding global typography. |
|
|
208
|
-
| `style.element` | Element-specific styles for buttons, images, and dividers, such as size and alignment. |
|
|
209
|
-
|
|
210
|
-
Heading and Text blocks start with `16px` padding. HTML blocks retain explicitly authored inline styles. See [EmailBlockStyle](./src/types/document.ts) for individual fields.
|
|
211
|
-
|
|
212
|
-
To add a custom block, pass a stable `BlockDefinition[]` through `blocks`. `type`, `title`, and `render(ctx)` are required. Optional `fields` and `styleFields` configure the inspector; `defaultFields`, `defaultStyle`, and `defaultData` initialize new nodes. `render(ctx)` returns HTML and receives `mode: "canvas" | "email"`.
|
|
213
|
-
|
|
214
|
-
```tsx
|
|
215
|
-
import { EmailEditor, type BlockDefinition } from "@standhigher/react-email-editor";
|
|
141
|
+
## Local development
|
|
216
142
|
|
|
217
|
-
|
|
218
|
-
type: "notice",
|
|
219
|
-
title: "Notice",
|
|
220
|
-
defaultStyle: { dimension: { padding: "16px" } },
|
|
221
|
-
render: () => '<p style="margin:0;color:#555555;">Thanks for subscribing.</p>',
|
|
222
|
-
}];
|
|
143
|
+
Node.js 22.18+ and pnpm are recommended:
|
|
223
144
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
145
|
+
```bash
|
|
146
|
+
pnpm install --frozen-lockfile
|
|
147
|
+
pnpm dev
|
|
227
148
|
```
|
|
228
149
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
## Reading and exporting data
|
|
232
|
-
|
|
233
|
-
Call these methods through `useRef<EmailEditorHandle>(null)`:
|
|
150
|
+
Open [http://localhost:5173](http://localhost:5173). The playground loads `src/` directly; reuse an existing server. Validation:
|
|
234
151
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
| `getRenderedHtml()` | `string` | Complete email HTML with templates resolved using variables and block preview data. |
|
|
242
|
-
|
|
243
|
-
Mutating a returned document snapshot does not change the editor. Use `onChange` to subscribe to document changes.
|
|
152
|
+
```bash
|
|
153
|
+
pnpm typecheck
|
|
154
|
+
pnpm lint
|
|
155
|
+
pnpm build
|
|
156
|
+
node --test tests/*.test.mjs tests/*.test.ts
|
|
157
|
+
```
|
|
244
158
|
|
|
245
|
-
For
|
|
159
|
+
For local consumers, use `link:../react-email-editor`, run `pnpm build` first, and keep `pnpm build:watch` running to update `dist/`. Both projects must share one React instance. Vite can use `resolve.dedupe: ["react", "react-dom"]`; Next.js can use `transpilePackages: ["@standhigher/react-email-editor"]`, with Turbopack's `root` covering the linked directory.
|