@standhigher/react-email-editor 0.2.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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # 更新日志
2
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
+
3
28
  ## 0.2.0 — 2026-10-27
4
29
 
5
30
  ### 不兼容变更
package/README.md CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  import "@standhigher/react-email-editor/styles.css";
30
30
 
31
31
  const text = createBlockNode(BLOCK_TYPES.text);
32
- if (text) text.fields.html = "<p>Hello, {{ store_name }}</p>";
32
+ if (text) text.controls.html = "<p>Hello, {{ store_name }}</p>";
33
33
  const initialDocument = createEmailDocument(text ? [text] : []);
34
34
  const variables: Variable[] = [
35
35
  { name: "{{ store_name }}", description: "Store name", value: "Example Store" },
@@ -69,13 +69,20 @@ All props are optional.
69
69
  | --- | --- | --- |
70
70
  | `initialDocument` | `EmailDocument` | Read on first mount only. Remount with a new React `key` to switch documents. |
71
71
  | `height` | `number \| string` | Defaults to `"100%"`; numbers are pixels. |
72
- | `global` | `Partial<EmailGlobalSettings>` | Overrides global styles; later changes merge into the current document. Omitting a field does not restore its default. |
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
73
  | `variables` | `Variable[]` | Variables for insertion and preview. Defaults to `[]`; Personalization controls are hidden when empty. |
74
- | `blocks` | `BlockDefinition[]` | Adds custom blocks; matching types replace built-in definitions. |
75
- | `onChange` | `(document: EmailDocument) => void` | Called on document changes, including external `global` updates that change document values. |
76
- | `onGlobalChange` | `(global: EmailGlobalSettings) => void` | Called for global style edits inside the editor, excluding external `global` updates. |
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. |
77
77
 
78
- Global style example: `global={{ contentWidth: 640, outerPadding: "24px" }}`. Defaults are exported as `DEFAULT_EMAIL_GLOBAL`.
78
+ Body style example: `body={{ contentWidth: 640, contentPadding: "24px" }}`. Defaults are exported as `DEFAULT_EMAIL_BODY`; normalize a partial configuration with `resolveEmailBody()`.
79
+
80
+ The **BODY** panel stores its settings in `EmailDocument.body` and applies them to the canvas, preview, and HTML export:
81
+
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.
84
+
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.
79
86
 
80
87
  Built-in blocks: `heading`, `text`, `button`, `image`, `divider`, `spacer`, and `html`, available through `BLOCK_TYPES`.
81
88
 
@@ -103,13 +110,33 @@ const previewHtml = documentToHtml(initialDocument, undefined, {
103
110
 
104
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`.
105
112
 
106
- The document format is under development. Only `version: 2` is accepted, with no automatic migration of older documents. Create data with `createEmailDocument()` and `createBlockNode()`. See [CHANGELOG](./CHANGELOG.md) for upgrade notes.
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.
107
114
 
108
115
  ## Custom blocks
109
116
 
110
- Pass `BlockDefinition[]` through `blocks`. Required fields are `type`, `title`, and `render(ctx)`, which returns HTML. Use `fields` / `styleFields` for inspector controls and `defaultFields` / `defaultStyle` / `defaultData` for initial values. The render context includes `mode: "canvas" | "email"`.
117
+ Declare controls and email output with `defineBlock()`, then register once:
118
+
119
+ ```tsx
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]));
133
+ ```
134
+
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.
136
+
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.
111
138
 
112
- Built-in text formatting lives in `fields.html`, container styles in `style.dimension` / `style.decorations`, and button, image, and divider element styles in `style.element`. Rendering uses only enabled style fields. See [BlockDefinition](https://github.com/standhigher/react-email-editor/blob/main/src/types/block.ts) and [EmailDocument](https://github.com/standhigher/react-email-editor/blob/main/src/types/document.ts) for full types. When calling `documentToHtml` directly, pass the complete definition list including custom blocks as its second argument.
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.
113
140
 
114
141
  ## Local development
115
142
 
package/README.zh-CN.md CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  import "@standhigher/react-email-editor/styles.css";
30
30
 
31
31
  const text = createBlockNode(BLOCK_TYPES.text);
32
- if (text) text.fields.html = "<p>你好,{{ store_name }}</p>";
32
+ if (text) text.controls.html = "<p>你好,{{ store_name }}</p>";
33
33
  const initialDocument = createEmailDocument(text ? [text] : []);
34
34
  const variables: Variable[] = [
35
35
  { name: "{{ store_name }}", description: "店铺名称", value: "示例店铺" },
@@ -69,13 +69,20 @@ export function EmailPage() {
69
69
  | --- | --- | --- |
70
70
  | `initialDocument` | `EmailDocument` | 仅首次挂载读取;切换文档时通过新的 React `key` 重新挂载。 |
71
71
  | `height` | `number \| string` | 默认 `"100%"`;数字为 px。 |
72
- | `global` | `Partial<EmailGlobalSettings>` | 覆盖全局样式,后续更新合并到当前文档;省略字段不会恢复默认值。 |
72
+ | `body` | `Partial<EmailBodySettings>` | 覆盖邮件主体的布局、外观和默认排版,后续更新合并到当前文档;省略字段不会恢复默认值。 |
73
73
  | `variables` | `Variable[]` | 插入和预览变量;默认 `[]`,为空时隐藏 Personalization 入口。 |
74
- | `blocks` | `BlockDefinition[]` | 添加自定义区块,同名 `type` 覆盖内置定义。 |
75
- | `onChange` | `(document: EmailDocument) => void` | 文档变化时触发,包括实际改变文档值的外部 `global` 更新。 |
76
- | `onGlobalChange` | `(global: EmailGlobalSettings) => void` | 编辑器内修改全局样式时触发,外部 `global` 更新不触发。 |
74
+ | `config` | `EmailEditorConfig` | 通过 `createEmailEditorConfig({ plugins })` 注册区块、资源适配器和区块库顺序。 |
75
+ | `onChange` | `(document: EmailDocument) => void` | 文档变化时触发,包括实际改变文档值的外部 `body` 更新。 |
76
+ | `onBodyChange` | `(body: EmailBodySettings) => void` | 编辑器内修改邮件主体配置时触发,外部 `body` 更新不触发。 |
77
77
 
78
- 全局样式示例:`global={{ contentWidth: 640, outerPadding: "24px" }}`。默认值通过 `DEFAULT_EMAIL_GLOBAL` 导出。
78
+ 邮件主体示例:`body={{ contentWidth: 640, contentPadding: "24px" }}`。默认值通过 `DEFAULT_EMAIL_BODY` 导出;可使用 `resolveEmailBody()` 归一化部分配置。
79
+
80
+ **BODY** 面板的配置存于 `EmailDocument.body`,同步用于画布、预览和最终邮件 HTML:
81
+
82
+ - **General**:文字颜色、链接颜色、字体、字重和链接下划线开关。
83
+ - **Content**:Background、内容宽度、Padding(`contentPadding`)和区块间距。默认折叠的 **Advanced: Border** 包含边框颜色、宽度和样式。Background 是唯一的邮件主体内容容器背景色,外侧区域为白色,主体固定居中。Padding 作用于内容边框内部,是唯一的主体内边距配置;已移除 `outerPadding`。
84
+
85
+ 文字和链接默认继承 BODY 配置,富文本工具栏中单独设置的格式优先。仅修改链接 URL 或打开方式会保留显式链接样式;重置链接样式后恢复继承 BODY。基础字号(16px)、行高(1.5)以及段落、列表间距使用内部默认值。HTML 区块保留源码格式,外层继承主体排版,不改写内部链接和列表。BODY 不提供对齐和圆角配置。
79
86
 
80
87
  内置区块:`heading`、`text`、`button`、`image`、`divider`、`spacer`、`html`,均可通过 `BLOCK_TYPES` 引用。
81
88
 
@@ -103,13 +110,33 @@ const previewHtml = documentToHtml(initialDocument, undefined, {
103
110
 
104
111
  变量的 `name` 原样插入,`description` 和预览值 `value` 可选。导出时仅对与 `variables[].name` 原文精确匹配的 token 补点并保留空格,如 `{{ store_name }}` → `{{ .store_name }}`;其他 Go template 表达式保持原样。预览支持部分 Go template 语法,数据优先级为区块 `defaultData` > `templateData`(`documentToHtml` 选项)> `variables`。
105
112
 
106
- 当前文档格式仍处于开发阶段,仅接受 `version: 2`,不自动迁移旧文档。使用 `createEmailDocument()` 和 `createBlockNode()` 创建数据。升级差异见 [CHANGELOG](./CHANGELOG.md)。
113
+ 当前文档格式仍处于开发阶段,仅接受 `version: 4`,不自动迁移旧文档。使用 `createEmailDocument()` 和 `createBlockNode()` 创建数据。升级差异见 [CHANGELOG](./CHANGELOG.md)。
107
114
 
108
115
  ## 自定义区块
109
116
 
110
- 通过 `blocks` 传入 `BlockDefinition[]`,必填项为 `type`、`title` 和 `render(ctx)`,返回 HTML 字符串。`fields` / `styleFields` 配置检查器,`defaultFields` / `defaultStyle` / `defaultData` 设置初始值。`render` 接收 `mode: "canvas" | "email"`。
117
+ 使用 `defineBlock()` 声明控件及 `renderEmail()`,通过 `createEmailEditorConfig()` 注册:
118
+
119
+ ```tsx
120
+ const coupon = defineBlock({
121
+ type: "my-app.coupon",
122
+ title: "优惠券",
123
+ controls: {
124
+ code: EmailControls.text({ label: "优惠码", 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]));
133
+ ```
134
+
135
+ 从包入口导入 `defineBlock`、`EmailControls`、`createEmailEditorConfig`。控件值自动推导,默认生成检查器和画布,支持富文本、列表、图片资源适配器、自定义控件组件及 React `Inspector` / `Canvas`。`EditableRichText` 支持多区域编辑,`ControlEditor` 可在自定义检查器复用声明的控件。
136
+
137
+ `config.validate(document)` 返回带区块 ID 和控件路径的 `ValidationIssue[]`,用于导出前预检;数字范围、选项、URL/图片协议、列表数量和业务校验也用于检查器及邮件导出。自定义 Canvas/Inspector 可用 `setControls(patch)` 一次更新多个关联控件值并触发一次 `onChange`。声明元数据采用隔离冻结快照,控件值解析结果在每次上下文中复用,`onChange` / `onBodyChange` 返回独立数据快照。
111
138
 
112
- 内置文字格式存于 `fields.html`,容器样式存于 `style.dimension` / `style.decorations`,按钮、图片和分割线自身样式存于 `style.element`。渲染仅使用区块启用的样式字段。完整定义见 [BlockDefinition](https://github.com/standhigher/react-email-editor/blob/main/src/types/block.ts) 和 [EmailDocument](https://github.com/standhigher/react-email-editor/blob/main/src/types/document.ts)。独立调用 `documentToHtml` 时,第二个参数需传入包含自定义区块的完整定义列表。
139
+ 编辑器与独立导出共用配置;`builtins` 可筛选和排序内置区块,覆盖内置区块必须显式声明 `override: true`。声明、运行时和持久化节点统一使用 `controls`;邮件文档为 v4,不兼容旧数据,不保留旧 `blocks`、`defaultFields`、`inlineEdit` 或 `render()` 接入方式。完整接入、类型、渲染工具和异常规则见[自定义区块 API 文档](./docs/custom-blocks.zh-CN.md)([English](./docs/custom-blocks.md)),可运行示例见 `playground/plugins/`。
113
140
 
114
141
  ## 本地开发
115
142