@standhigher/react-email-editor 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +244 -2
- package/README.zh-CN.md +245 -0
- package/dist/index.d.ts +325 -0
- package/dist/index.js +4586 -0
- package/dist/styles.css +1619 -0
- package/package.json +68 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Standhigher Tech Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,245 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @standhigher/react-email-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
A React email editor with drag-and-drop blocks, inline rich text, and desktop/mobile previews. Store content as JSON and export email HTML through a component ref.
|
|
6
|
+
|
|
7
|
+
## Installation and integration
|
|
8
|
+
|
|
9
|
+
Requires React 18 and React DOM 18. The package provides an ESM entry and TypeScript declarations.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @standhigher/react-email-editor
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Import the stylesheet once in your application. This example renders an empty editor and reads its current data:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useRef } from "react";
|
|
19
|
+
import {
|
|
20
|
+
EmailEditor,
|
|
21
|
+
type EmailEditorHandle,
|
|
22
|
+
} from "@standhigher/react-email-editor";
|
|
23
|
+
import "@standhigher/react-email-editor/styles.css";
|
|
24
|
+
|
|
25
|
+
export function EmailPage() {
|
|
26
|
+
const editorRef = useRef<EmailEditorHandle>(null);
|
|
27
|
+
|
|
28
|
+
function readData() {
|
|
29
|
+
const data = editorRef.current?.getData();
|
|
30
|
+
if (!data) return;
|
|
31
|
+
// Pass data.document, data.json, data.html, or data.renderedHtml
|
|
32
|
+
// to your application's save or export handler.
|
|
33
|
+
console.log(data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<EmailEditor ref={editorRef} height={720} />
|
|
39
|
+
<button type="button" onClick={readData}>Read data</button>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Saving is handled by your application. In Next.js App Router, put this integration in a Client Component with `"use client"`. When using `height="100%"`, give its parent a definite height.
|
|
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/`.
|
|
76
|
+
|
|
77
|
+
Both projects must resolve the same React instance. For Vite consumers, use `resolve.dedupe: ["react", "react-dom"]` if needed. Next.js consumers can use `transpilePackages: ["@standhigher/react-email-editor"]`; when an external link is used with Turbopack, `turbopack.root` must include both projects.
|
|
78
|
+
|
|
79
|
+
## Component props
|
|
80
|
+
|
|
81
|
+
All props are optional. Types are exported from `@standhigher/react-email-editor`.
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default / behavior |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `initialDocument` | `EmailDocument` | Empty document. Read only on the first mount. |
|
|
86
|
+
| `global` | `Partial<EmailGlobalSettings>` | No external overrides. Supplied fields override the initial document's global settings; later updates merge into the current document. |
|
|
87
|
+
| `blocks` | `BlockDefinition[]` | `[]`. Adds custom block types; matching types replace built-in definitions. Other built-in blocks remain available. |
|
|
88
|
+
| `variables` | `Variable[]` | `[]`. Variables available for insertion and preview substitution. |
|
|
89
|
+
| `height` | `number \| string` | `"100%"`. A number is interpreted as pixels; a string is a CSS height. |
|
|
90
|
+
| `onChange` | `(document: EmailDocument) => void` | Called when the document changes, including edits, reset, and external global updates that change its values. |
|
|
91
|
+
| `onGlobalChange` | `(global: EmailGlobalSettings) => void` | Called when global settings are edited in the editor. External `global` updates and reset do not call it. |
|
|
92
|
+
|
|
93
|
+
Reset restores the initial document snapshot and reapplies the current `global` overrides. To load a different document, remount with a new React `key`, for example `<EmailEditor key={documentId} initialDocument={document} />`. Changing `initialDocument` alone does not replace the open document. Omitting a previously supplied `global` field does not restore its default.
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
### Document
|
|
98
|
+
|
|
99
|
+
Use `createEmailDocument()` and `createBlockNode()` to populate defaults:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import {
|
|
103
|
+
BLOCK_TYPES,
|
|
104
|
+
EmailEditor,
|
|
105
|
+
createBlockNode,
|
|
106
|
+
createEmailDocument,
|
|
107
|
+
type Variable,
|
|
108
|
+
} from "@standhigher/react-email-editor";
|
|
109
|
+
|
|
110
|
+
const text = createBlockNode(BLOCK_TYPES.text);
|
|
111
|
+
if (text) text.fields.html = "<p>Hello {{ store_name }}</p>";
|
|
112
|
+
|
|
113
|
+
export const initialDocument = createEmailDocument(text ? [text] : []);
|
|
114
|
+
initialDocument.subject = "Welcome";
|
|
115
|
+
|
|
116
|
+
const variables: Variable[] = [
|
|
117
|
+
{ name: "{{ store_name }}", description: "Store name", value: "Demo Store" },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
export function TemplateEmailPage() {
|
|
121
|
+
return (
|
|
122
|
+
<EmailEditor
|
|
123
|
+
initialDocument={initialDocument}
|
|
124
|
+
variables={variables}
|
|
125
|
+
height={720}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Pass both `initialDocument` and `variables` as shown so the token has a preview value and can gain its missing dot on export. `createBlockNode()` returns `null` for an unknown block type. Built-in types are `heading`, `text`, `button`, `image`, `divider`, `spacer`, and `html`, also available through `BLOCK_TYPES`.
|
|
132
|
+
|
|
133
|
+
| Document field | Type | Description |
|
|
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
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import type { Variable } from "@standhigher/react-email-editor";
|
|
181
|
+
|
|
182
|
+
export const variables: Variable[] = [
|
|
183
|
+
{ name: "{{ store_name }}", description: "Store name", value: "Demo Store" },
|
|
184
|
+
];
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Pass this array through `variables` on `EmailEditor`.
|
|
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.
|
|
198
|
+
|
|
199
|
+
### Block configuration
|
|
200
|
+
|
|
201
|
+
Block node styles are grouped by purpose:
|
|
202
|
+
|
|
203
|
+
| Style group | Purpose |
|
|
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";
|
|
216
|
+
|
|
217
|
+
const customBlocks: BlockDefinition[] = [{
|
|
218
|
+
type: "notice",
|
|
219
|
+
title: "Notice",
|
|
220
|
+
defaultStyle: { dimension: { padding: "16px" } },
|
|
221
|
+
render: () => '<p style="margin:0;color:#555555;">Thanks for subscribing.</p>',
|
|
222
|
+
}];
|
|
223
|
+
|
|
224
|
+
export function CustomEmailPage() {
|
|
225
|
+
return <EmailEditor blocks={customBlocks} height={720} />;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
By default, common container spacing and decoration controls are added to custom blocks. Set `exactStyleFields: true` to show only the supplied style fields; existing node styles still apply when rendering. Full definitions: [BlockDefinition, FieldConfig, and StyleFieldConfig](./src/types/block.ts).
|
|
230
|
+
|
|
231
|
+
## Reading and exporting data
|
|
232
|
+
|
|
233
|
+
Call these methods through `useRef<EmailEditorHandle>(null)`:
|
|
234
|
+
|
|
235
|
+
| Method | Return type | Description |
|
|
236
|
+
| --- | --- | --- |
|
|
237
|
+
| `getData()` | `EmailEditorData` | Returns `{ document, json, html, renderedHtml }`. |
|
|
238
|
+
| `getDocument()` | `EmailDocument` | Independent document snapshot for storage or later editing. |
|
|
239
|
+
| `getJson()` | `string` | Formatted document JSON. |
|
|
240
|
+
| `getHtml()` | `string` | Complete email HTML. Template actions stay in place; only tokens from `variables` gain a missing dot. |
|
|
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.
|
|
244
|
+
|
|
245
|
+
For rendering without mounting the editor, use `documentToHtml(document, definitions?, options?)`. Definitions default to `DEFAULT_BLOCKS`; include custom definitions when the document uses them. `options` accepts `resolveHtmlTemplates` (default `false`), `variables`, and `templateData`. When templates stay unresolved, `variables` supplies the only tokens that gain a missing dot. When resolving, data precedence is block `defaultData` over `templateData` over `variables`.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# @standhigher/react-email-editor
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
React 邮件编辑器,支持区块拖拽、富文本编辑和桌面/移动预览。内容以 JSON 保存,通过组件 ref 导出邮件 HTML。
|
|
6
|
+
|
|
7
|
+
## 安装与接入
|
|
8
|
+
|
|
9
|
+
需要 React 18 和 React DOM 18。组件库提供 ESM 入口和 TypeScript 类型声明。
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @standhigher/react-email-editor
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
在应用中引入一次样式。以下示例展示空编辑器,并读取当前数据:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useRef } from "react";
|
|
19
|
+
import {
|
|
20
|
+
EmailEditor,
|
|
21
|
+
type EmailEditorHandle,
|
|
22
|
+
} from "@standhigher/react-email-editor";
|
|
23
|
+
import "@standhigher/react-email-editor/styles.css";
|
|
24
|
+
|
|
25
|
+
export function EmailPage() {
|
|
26
|
+
const editorRef = useRef<EmailEditorHandle>(null);
|
|
27
|
+
|
|
28
|
+
function readData() {
|
|
29
|
+
const data = editorRef.current?.getData();
|
|
30
|
+
if (!data) return;
|
|
31
|
+
// 将 data.document、data.json、data.html 或 data.renderedHtml
|
|
32
|
+
// 交给应用自己的保存或导出逻辑。
|
|
33
|
+
console.log(data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<EmailEditor ref={editorRef} height={720} />
|
|
39
|
+
<button type="button" onClick={readData}>读取数据</button>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
保存逻辑由接入方实现。Next.js App Router 中,接入编辑器的组件需使用 `"use client"`。使用 `height="100%"` 时,父容器需要有明确高度。
|
|
46
|
+
|
|
47
|
+
## 本地启动
|
|
48
|
+
|
|
49
|
+
使用 pnpm 和当前受支持的 Node.js LTS;建议使用 Node.js 22.18 或更新版本,以兼容仓库工具链和 TypeScript 测试。
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm install --frozen-lockfile
|
|
53
|
+
pnpm dev
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
打开 [http://localhost:5173](http://localhost:5173)。playground 直接加载 `src/` 并支持热更新,无需提前构建,也不进入发布包。已有开发服务时直接复用。
|
|
57
|
+
|
|
58
|
+
| 命令 | 用途 |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `pnpm dev` | 启动本地 playground,端口为 5173。 |
|
|
61
|
+
| `pnpm build` | 在 `dist/` 生成 ESM、类型声明和 CSS。 |
|
|
62
|
+
| `pnpm build:watch` | 监听 `src/` 改动并重新构建 `dist/`,不启动预览服务。 |
|
|
63
|
+
| `pnpm typecheck` | 检查组件库和 playground 的类型。 |
|
|
64
|
+
|
|
65
|
+
在另一个本地项目联调时,将其依赖指向组件库根目录,按实际位置调整相对路径:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@standhigher/react-email-editor": "link:../react-email-editor"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
先在本仓库执行 `pnpm build`,持续开发时保持 `pnpm build:watch` 运行。消费方执行 `pnpm install`,并使用自己的开发服务。包入口与 CSS 均从 `dist/` 读取。
|
|
76
|
+
|
|
77
|
+
两个项目应使用同一个 React 实例。Vite 消费方可按需设置 `resolve.dedupe: ["react", "react-dom"]`。Next.js 消费方可设置 `transpilePackages: ["@standhigher/react-email-editor"]`;使用 Turbopack 且链接目录在项目外时,`turbopack.root` 需覆盖两个项目。
|
|
78
|
+
|
|
79
|
+
## 组件参数
|
|
80
|
+
|
|
81
|
+
所有 Props 均为可选项,相关类型从 `@standhigher/react-email-editor` 导出。
|
|
82
|
+
|
|
83
|
+
| 参数 | 类型 | 默认值 / 行为 |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `initialDocument` | `EmailDocument` | 默认空文档,仅在首次挂载时读取。 |
|
|
86
|
+
| `global` | `Partial<EmailGlobalSettings>` | 默认无外部覆盖。传入字段覆盖初始文档的全局设置;后续更新会合并到当前文档。 |
|
|
87
|
+
| `blocks` | `BlockDefinition[]` | `[]`。新增自定义区块;同名 `type` 覆盖内置定义,其余内置区块保留。 |
|
|
88
|
+
| `variables` | `Variable[]` | `[]`。用于插入和预览替换的变量列表。 |
|
|
89
|
+
| `height` | `number \| string` | `"100%"`。数字按 px 处理,字符串使用 CSS 高度值。 |
|
|
90
|
+
| `onChange` | `(document: EmailDocument) => void` | 文档变化时触发,包括编辑、重置,以及实际改变文档值的外部全局设置更新。 |
|
|
91
|
+
| `onGlobalChange` | `(global: EmailGlobalSettings) => void` | 在编辑器中修改全局设置时触发;外部 `global` 更新和重置不触发此回调。 |
|
|
92
|
+
|
|
93
|
+
Reset 恢复首次初始化的文档快照,并重新应用当前 `global` 覆盖项。切换文档时使用新的 React `key` 重新挂载,例如 `<EmailEditor key={documentId} initialDocument={document} />`;仅修改 `initialDocument` 不会替换已打开的文档。省略之前传入的某个 `global` 字段,不会将其恢复为默认值。
|
|
94
|
+
|
|
95
|
+
## 配置说明
|
|
96
|
+
|
|
97
|
+
### 文档配置
|
|
98
|
+
|
|
99
|
+
通过 `createEmailDocument()` 和 `createBlockNode()` 补齐默认值:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import {
|
|
103
|
+
BLOCK_TYPES,
|
|
104
|
+
EmailEditor,
|
|
105
|
+
createBlockNode,
|
|
106
|
+
createEmailDocument,
|
|
107
|
+
type Variable,
|
|
108
|
+
} from "@standhigher/react-email-editor";
|
|
109
|
+
|
|
110
|
+
const text = createBlockNode(BLOCK_TYPES.text);
|
|
111
|
+
if (text) text.fields.html = "<p>你好,{{ store_name }}</p>";
|
|
112
|
+
|
|
113
|
+
export const initialDocument = createEmailDocument(text ? [text] : []);
|
|
114
|
+
initialDocument.subject = "欢迎订阅";
|
|
115
|
+
|
|
116
|
+
const variables: Variable[] = [
|
|
117
|
+
{ name: "{{ store_name }}", description: "店铺名称", value: "示例店铺" },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
export function TemplateEmailPage() {
|
|
121
|
+
return (
|
|
122
|
+
<EmailEditor
|
|
123
|
+
initialDocument={initialDocument}
|
|
124
|
+
variables={variables}
|
|
125
|
+
height={720}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
如示例所示,同时传入 `initialDocument` 和 `variables`,使变量拥有预览值,并能在导出时补点。`createBlockNode()` 遇到未知类型时返回 `null`。内置区块有 `heading`、`text`、`button`、`image`、`divider`、`spacer` 和 `html`,可通过 `BLOCK_TYPES` 引用。
|
|
132
|
+
|
|
133
|
+
| 文档字段 | 类型 | 说明 |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `version` | `typeof DOCUMENT_VERSION` | 当前版本为 `1`,由 `createEmailDocument()` 填充。 |
|
|
136
|
+
| `subject` | `string`,可选 | 邮件主题,编辑器输入上限为 128 个字符。 |
|
|
137
|
+
| `global` | `EmailGlobalSettings` | 邮件全局样式,创建函数会补齐默认值。 |
|
|
138
|
+
| `blocks` | `EmailBlockNode[]` | 按展示顺序排列的区块。每项包含唯一 `id`、`type`、`fields`,以及可选的 `style` 和预览数据 `defaultData`。 |
|
|
139
|
+
|
|
140
|
+
文档结构仍处于开发阶段,不会自动迁移旧版区块字段。完整模型见[文档类型定义](./src/types/document.ts)。
|
|
141
|
+
|
|
142
|
+
### 全局样式
|
|
143
|
+
|
|
144
|
+
通过 `global` 传入部分设置,例如 `<EmailEditor global={{ contentWidth: 640, outerPadding: "24px" }} />`。`DEFAULT_EMAIL_GLOBAL` 导出完整默认值;`resolveEmailGlobal()` 用于补齐和归一化配置。
|
|
145
|
+
|
|
146
|
+
这些设置控制邮件内容。编辑器的选中边框、工具栏等辅助界面不会进入导出的 HTML。
|
|
147
|
+
|
|
148
|
+
| 参数 | 类型 / 可选值 | 默认值 | 说明 |
|
|
149
|
+
| --- | --- | --- | --- |
|
|
150
|
+
| `contentWidth` | `number` | `600` | 内容宽度,单位 px;取整并限制在 320–900。 |
|
|
151
|
+
| `contentAlign` | `"left" \| "center" \| "right"` | `"center"` | 内容整体的水平位置。 |
|
|
152
|
+
| `backgroundColor` | `string` | `"#ffffff"` | 邮件外层背景色。 |
|
|
153
|
+
| `contentBackgroundColor` | `string` | `"#ffffff"` | 内容背景色。 |
|
|
154
|
+
| `contentPadding` | `string` | `"0px"` | 内容内边距。 |
|
|
155
|
+
| `outerPadding` | `string` | `"0px"` | 内容外侧留白。 |
|
|
156
|
+
| `blockGap` | `string` | `"4px"` | 区块间距。 |
|
|
157
|
+
| `textColor` | `string` | `"#000000"` | 默认文字颜色。 |
|
|
158
|
+
| `textAlign` | `"left" \| "center" \| "right" \| "justify"` | `"left"` | 默认文字对齐。 |
|
|
159
|
+
| `fontFamily` | `string` | `"Arial, Helvetica, sans-serif"` | 默认字体列表。 |
|
|
160
|
+
| `fontWeight` | `"normal" \| "bold"` | `"normal"` | 默认字重。 |
|
|
161
|
+
| `defaultFontSize` | `string` | `"16px"` | 默认字号。 |
|
|
162
|
+
| `defaultLineHeight` | `string` | `"1.5"` | 默认行高。 |
|
|
163
|
+
| `paragraphSpacing` | `string` | `"6px"` | 相邻顶层段落的间距。 |
|
|
164
|
+
| `listSpacing` | `string` | `"4px"` | 列表整体(`ul` / `ol`)的上下外边距。 |
|
|
165
|
+
| `listIndent` | `string` | `"22px"` | 列表缩进。 |
|
|
166
|
+
| `linkColor` | `string` | `"#005bd3"` | 默认链接颜色。 |
|
|
167
|
+
| `linkDecoration` | `"underline" \| "none"` | `"underline"` | 默认链接装饰。 |
|
|
168
|
+
| `borderWidth` | `string` | `"0px"` | 内容边框宽度。 |
|
|
169
|
+
| `borderStyle` | `"none" \| "solid" \| "dashed" \| "dotted"` | `"solid"` | 内容边框样式。 |
|
|
170
|
+
| `borderColor` | `string` | `"#000000"` | 内容边框颜色。 |
|
|
171
|
+
| `borderRadius` | `string` | `"0px"` | 单个圆角值,或按左上、右上、右下、左下排列的四个值。 |
|
|
172
|
+
|
|
173
|
+
间距和尺寸使用 `"16px"` 等 CSS 长度字符串;内边距支持 `"12px 24px"` 等简写,行高也支持无单位数值。桌面画布和预览跟随 `contentWidth`,移动预览的视口宽度为 360px。
|
|
174
|
+
|
|
175
|
+
全局间距和长度配置支持非负的 `px`、`em`、`rem`、`%` 值;除行高外,无单位长度会补为 px。不支持的值(如负长度、`calc()`、`var()`、`vh`、`vw`)会回退为对应全局参数的默认值。
|
|
176
|
+
|
|
177
|
+
### 变量与模板数据
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import type { Variable } from "@standhigher/react-email-editor";
|
|
181
|
+
|
|
182
|
+
export const variables: Variable[] = [
|
|
183
|
+
{ name: "{{ store_name }}", description: "店铺名称", value: "示例店铺" },
|
|
184
|
+
];
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
通过 `EmailEditor` 的 `variables` 参数传入此数组。
|
|
188
|
+
|
|
189
|
+
| 字段 | 类型 | 说明 |
|
|
190
|
+
| --- | --- | --- |
|
|
191
|
+
| `name` | `string` | 必填。下拉展示并原样插入的变量,保留空格,例如 `{{ store_name }}`。 |
|
|
192
|
+
| `description` | `string`,可选 | 下拉选项副文案;空字符串或不传时不显示。 |
|
|
193
|
+
| `value` | `string`,可选 | 预览时使用的替换文本。 |
|
|
194
|
+
|
|
195
|
+
变量可以带前导点。导出模板 HTML 时,仅对与 `variables[].name` 原文精确匹配(包括空格)的 token 补齐缺失的点,并保留空格,例如 `{{ store_name }}` 变为 `{{ .store_name }}`。其他 Go template 表达式保持原样,默认不会替换为预览值。
|
|
196
|
+
|
|
197
|
+
区块节点的 `defaultData: Record<string, unknown>` 可提供结构化预览数据,例如供 `{{range .products}}` 使用的数组。同名数据以区块 `defaultData` 为准,覆盖全局变量。预览解析适用于所有区块渲染出的 HTML,也包括自定义区块。HTML 区块的模板表达式直接写在 `fields.html` 中;预览渲染器支持部分 Go template 语法。
|
|
198
|
+
|
|
199
|
+
### 区块配置
|
|
200
|
+
|
|
201
|
+
区块节点的样式按用途分组:
|
|
202
|
+
|
|
203
|
+
| 样式分组 | 用途 |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| `style.dimension` | 容器内边距 `padding`、外侧留白 `margin`,以及可选的 px 高度 `height`;高度包含内边距,作为最小高度。 |
|
|
206
|
+
| `style.decorations` | 容器背景、边框和圆角。 |
|
|
207
|
+
| `style.typography` | 区块文字样式,覆盖全局排版设置。 |
|
|
208
|
+
| `style.element` | 按钮、图片和分割线等元素自身的样式,例如尺寸和对齐。 |
|
|
209
|
+
|
|
210
|
+
Heading 和 Text 默认包含 `16px` 内边距。HTML 区块保留作者显式编写的内联样式。各字段见 [EmailBlockStyle](./src/types/document.ts)。
|
|
211
|
+
|
|
212
|
+
通过 `blocks` 传入稳定的 `BlockDefinition[]` 添加自定义区块。必填项为 `type`、`title` 和 `render(ctx)`;可选的 `fields`、`styleFields` 配置检查器,`defaultFields`、`defaultStyle`、`defaultData` 设置新建节点的初始值。`render(ctx)` 返回 HTML 字符串,接收 `mode: "canvas" | "email"`。
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { EmailEditor, type BlockDefinition } from "@standhigher/react-email-editor";
|
|
216
|
+
|
|
217
|
+
const customBlocks: BlockDefinition[] = [{
|
|
218
|
+
type: "notice",
|
|
219
|
+
title: "Notice",
|
|
220
|
+
defaultStyle: { dimension: { padding: "16px" } },
|
|
221
|
+
render: () => '<p style="margin:0;color:#555555;">感谢订阅。</p>',
|
|
222
|
+
}];
|
|
223
|
+
|
|
224
|
+
export function CustomEmailPage() {
|
|
225
|
+
return <EmailEditor blocks={customBlocks} height={720} />;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
默认会为自定义区块补充通用容器间距和装饰控件。设置 `exactStyleFields: true` 后,只展示传入的样式字段;节点上已有样式仍会参与渲染。完整定义见 [BlockDefinition、FieldConfig 和 StyleFieldConfig](./src/types/block.ts)。
|
|
230
|
+
|
|
231
|
+
## 读取与导出数据
|
|
232
|
+
|
|
233
|
+
通过 `useRef<EmailEditorHandle>(null)` 调用以下方法:
|
|
234
|
+
|
|
235
|
+
| 方法 | 返回类型 | 说明 |
|
|
236
|
+
| --- | --- | --- |
|
|
237
|
+
| `getData()` | `EmailEditorData` | 返回 `{ document, json, html, renderedHtml }`。 |
|
|
238
|
+
| `getDocument()` | `EmailDocument` | 返回独立文档快照,可保存或用于下次编辑。 |
|
|
239
|
+
| `getJson()` | `string` | 返回格式化的文档 JSON。 |
|
|
240
|
+
| `getHtml()` | `string` | 返回完整邮件 HTML,保留模板表达式,仅为 `variables` 中的 token 补齐缺失的点。 |
|
|
241
|
+
| `getRenderedHtml()` | `string` | 使用全局变量和区块预览数据解析模板,返回完整邮件 HTML。 |
|
|
242
|
+
|
|
243
|
+
修改返回的文档快照不会改变编辑器内部状态。可使用 `onChange` 订阅文档变化。
|
|
244
|
+
|
|
245
|
+
不挂载编辑器时,可调用 `documentToHtml(document, definitions?, options?)`。`definitions` 默认为 `DEFAULT_BLOCKS`,文档使用自定义区块时需包含对应定义。`options` 支持 `resolveHtmlTemplates`(默认 `false`)、`variables` 和 `templateData`。未开启解析时,仅对传入 `variables` 中的 token 补点。开启解析时,数据优先级为区块 `defaultData` 高于 `templateData`,高于 `variables`。
|