@standhigher/react-email-editor 0.4.1 → 0.5.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.
- package/README.md +17 -15
- package/README.zh-CN.md +17 -15
- package/dist/index.d.ts +44 -17
- package/dist/index.js +453 -249
- package/dist/styles.css +41 -31
- package/docs/advanced-examples.md +5 -1
- package/docs/advanced-examples.zh-CN.md +5 -1
- package/docs/api.md +24 -10
- package/docs/api.zh-CN.md +24 -10
- package/docs/changelog.md +169 -0
- package/{CHANGELOG.md → docs/changelog.zh-CN.md} +71 -0
- package/docs/custom-blocks.md +3 -3
- package/docs/custom-blocks.zh-CN.md +3 -3
- package/docs/release.md +38 -0
- package/docs/release.zh-CN.md +38 -0
- package/docs/template-engines.md +73 -0
- package/docs/template-engines.zh-CN.md +73 -0
- package/package.json +22 -12
- package/docs/assets/architecture.png +0 -0
- package/docs/assets/architecture.zh-CN.png +0 -0
package/docs/custom-blocks.md
CHANGED
|
@@ -130,7 +130,7 @@ Preview precedence is node defaultData > templateData > variables, merged by top
|
|
|
130
130
|
| `controlPath` | `string` | Control path, such as terms[0].text; empty when the plugin is missing. |
|
|
131
131
|
| `message` | `string` | Reason validation failed. |
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
Invalid basic document structure throws a `TypeError`; documents are not rejected based on their `version` value. validate() does not check renderer execution; export can still fail due to a rendering error.
|
|
134
134
|
|
|
135
135
|
## 2. defineBlock(options)
|
|
136
136
|
|
|
@@ -164,7 +164,7 @@ const banner = defineBlock({
|
|
|
164
164
|
| --- | --- | --- |
|
|
165
165
|
| `type` | Yes | Stable unique type. Use an application namespace, such as my-app.banner. Do not casually change persisted types. |
|
|
166
166
|
| `title` | Yes | Library and inspector name. type/title cannot be blank. |
|
|
167
|
-
| `icon` | No |
|
|
167
|
+
| `icon` | No | HTML string or ReactNode icon; defaults to the first character of the trimmed `title`, preserving case and displaying it as text, e.g. `Coupon` → `C`, `优惠券` → `优`. |
|
|
168
168
|
| `description` | No | Block description. |
|
|
169
169
|
| `override` | No | Defaults to false; allows replacing a built-in with the same type during registration. |
|
|
170
170
|
| `controls` | No | ControlSchema object defining content controls in declaration order, with inferred value types. Defaults to an empty object. |
|
|
@@ -467,7 +467,7 @@ Links allow http/https/mailto/tel, anchors, or templates; images allow http/http
|
|
|
467
467
|
|
|
468
468
|
EmailBlockNode contains required id, type, controls and optional style/defaultData. EmailDocument contains version, body, blocks, and optional subject. Register matching plugins after JSON reload; JSON does not store components or renderers.
|
|
469
469
|
|
|
470
|
-
|
|
470
|
+
New documents use the format identifier **4**. `version` has type `number` and is only an identifier; loading checks the actual structure and preserves the supplied version. The entire API uses controls, with no old API aliases, old-data compatibility, or automatic migration. Changing an old JSON version number cannot make nodes without controls pass structural checks. Create new data with createEmailDocument() and config.createBlock().
|
|
471
471
|
|
|
472
472
|
Missing plugins produce a canvas placeholder and preserve the node; email export throws. Canvas, Inspector, and canvas rendering errors are isolated by block with retry. Email rendering errors identify the block type, ID, and reason. getData()/getDocument() and onChange/onBodyChange arguments provide independent snapshots.
|
|
473
473
|
|
|
@@ -129,7 +129,7 @@ function useCouponConfig() {
|
|
|
129
129
|
| `controlPath` | `string` | 控件路径,例如 `terms[0].text`;插件缺失时为空字符串。 |
|
|
130
130
|
| `message` | `string` | 校验失败原因。 |
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
文档基本结构不合法时直接抛出 `TypeError`,不根据 `version` 值拒绝文档。`validate()` 不检查渲染器能否成功运行,导出仍可能因渲染错误失败。
|
|
133
133
|
|
|
134
134
|
## 2. defineBlock(options)
|
|
135
135
|
|
|
@@ -163,7 +163,7 @@ const banner = defineBlock({
|
|
|
163
163
|
| --- | --- | --- |
|
|
164
164
|
| `type` | 是 | 唯一且稳定的类型标识;建议业务命名空间,例如 `my-app.banner`。持久化后不能随意变更。 |
|
|
165
165
|
| `title` | 是 | 区块库和检查器名称。`type` / `title` 不允许为空白。 |
|
|
166
|
-
| `icon` | 否 | 字符串或 ReactNode
|
|
166
|
+
| `icon` | 否 | HTML 字符串或 ReactNode 图标;省略时使用 `title` 去除首尾空白后的第一个字符,保留大小写并按纯文本显示,例如 `Coupon` → `C`、`优惠券` → `优`。 |
|
|
167
167
|
| `description` | 否 | 区块说明。 |
|
|
168
168
|
| `override` | 否 | 默认 `false`;允许在注册时替换同类型的内置区块。 |
|
|
169
169
|
| `controls` | 否 | `ControlSchema` 对象;按声明顺序定义内容控件,自动推导值类型,默认空对象。 |
|
|
@@ -466,7 +466,7 @@ renderEmail: ({ controls, email }) => email.html`
|
|
|
466
466
|
|
|
467
467
|
节点使用 `EmailBlockNode`,包含必填 id、type、controls,以及可选 style、defaultData。文档使用 `EmailDocument`,包含 version、body、blocks 和可选 subject。保存 JSON 后重新加载时,仍需注册同 type 的插件,JSON 不保存组件或渲染函数。
|
|
468
468
|
|
|
469
|
-
|
|
469
|
+
新建文档的格式标识为 **4**。`version` 类型为 `number`,只做标识;接入依据实际数据结构,并保留传入的版本号。全链路使用 controls,不提供旧 API 别名、旧数据兼容或自动迁移;仅修改旧 JSON 的版本号不能让缺失 controls 的节点通过结构检查。请用 createEmailDocument() 和 config.createBlock() 创建新数据。
|
|
470
470
|
|
|
471
471
|
缺少插件时画布显示占位并保留原始节点,邮件导出报错。Canvas、Inspector 及画布渲染异常按区块隔离并提供重试;邮件渲染错误包含区块 type、id 和原因。getData()/getDocument() 和 onChange/onBodyChange 参数均为独立快照。
|
|
472
472
|
|
package/docs/release.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# npm release guide
|
|
2
|
+
|
|
3
|
+
English | [简体中文](./release.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
## Toolchain and package configuration
|
|
6
|
+
|
|
7
|
+
Development uses Node 24 LTS (`.nvmrc`) and npm 11.13.0 (`packageManager`). The complete test suite needs Node 22.18+. These development requirements are not consumer requirements, so the package does not declare `engines`. React / React DOM 18 remain the supported peers; React 19 requires a separate compatibility validation.
|
|
8
|
+
|
|
9
|
+
The manifest declares MIT licensing, repository, homepage, issue tracker and the public npm registry. `sideEffects: ["**/*.css"]` preserves CSS imports while allowing unused JavaScript modules to be removed. Actual tree shaking depends on module contents and the consumer's bundler.
|
|
10
|
+
|
|
11
|
+
The package includes built assets, Markdown documentation and SVGs under `docs/` (including [English](./changelog.md) and [Chinese](./changelog.zh-CN.md) changelogs), plus both READMEs. npm automatically includes `package.json` and `LICENSE`. Source, playground, tests, caches and PNG originals are excluded.
|
|
12
|
+
|
|
13
|
+
## README images
|
|
14
|
+
|
|
15
|
+
Both READMEs use absolute CDN URLs pinned to the publicly published `0.4.1` package, which contains the English and Chinese PNG and SVG assets. This avoids relative-path resolution on npm, GitHub branch synchronization and shipping PNG copies in every release.
|
|
16
|
+
|
|
17
|
+
When updating a diagram, host the new image at a stable public URL before changing either README. Do not reference an unpublished version or a `latest` URL that may no longer contain PNGs. Verify an unauthenticated `200` response and the correct image Content-Type.
|
|
18
|
+
|
|
19
|
+
README language switches, documentation, changelog and section links use absolute GitHub `blob/main` URLs. Push the corresponding commit before publishing so the linked READMEs, `docs/` and changelog exist on GitHub; local-only files still return 404. Relative links inside documentation pages are resolved by GitHub against their directories.
|
|
20
|
+
|
|
21
|
+
## Local checks and publishing
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm ci
|
|
25
|
+
npm run check
|
|
26
|
+
npm pack --dry-run
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`npm run check` runs type checking, linting, build and Node tests in order. Tests cover package contents, README image URLs and consumer CSS preservation. `npm test` requires an existing build. `prepack` builds assets; `prepublishOnly` runs the complete check before local publishing.
|
|
30
|
+
|
|
31
|
+
To publish locally, first update both changelogs and select an unpublished version:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm login --registry=https://registry.npmjs.org/
|
|
35
|
+
npm publish
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The npm account needs permission for the `@standhigher` scope and any required 2FA. Published versions cannot be overwritten. Committing or pushing code does not automatically publish a package.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# npm 发布指南
|
|
2
|
+
|
|
3
|
+
[English](./release.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
## 工具链与包配置
|
|
6
|
+
|
|
7
|
+
开发默认使用 Node 24 LTS(`.nvmrc`)和 npm 11.13.0(`packageManager`)。完整测试最低需要 Node 22.18;这不是消费方安装要求,因此包没有添加 `engines` 限制。当前仍只声明 React 18 / React DOM 18 支持,React 19 需完成独立兼容性验证后再扩大 peer 范围。
|
|
8
|
+
|
|
9
|
+
`package.json` 声明 MIT 许可证、仓库、主页、Issue 地址及公开 npm registry。`sideEffects: ["**/*.css"]` 保留消费方的 CSS 导入,允许 bundler 丢弃未使用的 JavaScript 模块;实际裁剪效果取决于模块内容和消费方 bundler。
|
|
10
|
+
|
|
11
|
+
`files` 只发布构建产物、`docs/` 下的 Markdown 文档和 SVG(含[英文](./changelog.md)与[中文](./changelog.zh-CN.md)更新日志),以及中英文 README;npm 自动包含 `package.json` 和 `LICENSE`。源码、playground、测试、本地缓存和 PNG 原图不进入包。
|
|
12
|
+
|
|
13
|
+
## README 图片
|
|
14
|
+
|
|
15
|
+
npm README 使用固定版本的绝对 CDN 地址,图片来自已公开发布的 `0.4.1` 包。中英文 PNG 和 SVG 源文件均由该版本提供,不依赖本地相对路径或 GitHub 分支同步,也不需要在每个新版包中重复分发 PNG。
|
|
16
|
+
|
|
17
|
+
更新架构图时,先将新图片放到可公开读取的稳定地址,再更新两个 README 的图片与查看链接。不要把地址替换成尚未发布的版本,也不要使用会随 `latest` 变化且可能缺失 PNG 的路径。图片地址应无需登录,返回 `200` 和正确的图片 Content-Type。
|
|
18
|
+
|
|
19
|
+
README 的语言切换、文档、更新日志和章节导航使用 GitHub `blob/main` 绝对地址。发布前须先推送相应提交,确保 GitHub 上的 README、`docs/` 与更新日志均已同步;仅在本地存在的文档仍会返回 404。文档页内部的相对链接继续由 GitHub 按目录解析。
|
|
20
|
+
|
|
21
|
+
## 本地检查与发布
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm ci
|
|
25
|
+
npm run check
|
|
26
|
+
npm pack --dry-run
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`npm run check` 顺序执行类型检查、lint、构建和 Node 测试;测试包含发布文件范围、README 图片地址及消费方 CSS 保留验证。`npm test` 只执行测试,需先构建。`prepack` 负责构建;本地 `npm publish` 的 `prepublishOnly` 会执行完整校验。
|
|
30
|
+
|
|
31
|
+
需要本地手动发布时,在确认版本未发布、中英文更新日志已同步后执行:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm login --registry=https://registry.npmjs.org/
|
|
35
|
+
npm publish
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
发布需要 npm 账号对 `@standhigher` scope 的权限及账号要求的 2FA。已发布的相同版本不能覆盖。代码提交或推送不会自动发布 npm 包。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Template preview and custom engines
|
|
2
|
+
|
|
3
|
+
English | [简体中文](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Inject a synchronous `TemplateEngine` with `useEmailEditorConfig({ templateEngine })`. Canvas `renderedHtml`, editor preview, `getRenderedHtml()` and `getData().renderedHtml` share it. Rich-text editing regions keep editable source tokens.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface TemplateEngine {
|
|
9
|
+
render: (template: string, data: TemplateData) => string;
|
|
10
|
+
validate?: (template: string) => TemplateValidationIssue[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface TemplateValidationIssue {
|
|
14
|
+
message: string;
|
|
15
|
+
offset?: number; // Zero-based character offset in the original template
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TemplateEngineOptions {
|
|
19
|
+
templateEngine?: TemplateEngine;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Wrap your own engine; this package does not bundle a complete Go engine. `renderTemplatePreview(template, data?, { templateEngine })` and `validateTemplate(template, { templateEngine })` use the same engine selection. Omitted engines use the exported `defaultTemplateEngine`. Replacing the engine refreshes rendering without resetting the document or invoking document-change callbacks.
|
|
24
|
+
|
|
25
|
+
- Each block's complete `renderEmail` HTML is sent to the selected engine after the same listed-token dot normalization used by sending HTML: exact `variables` tokens gain a leading dot while whitespace and trim markers are preserved. Other expressions pass through unchanged. Standalone preview passes the caller's template directly. No default parser runs first. Variables cannot span blocks because blocks execute separately.
|
|
26
|
+
- Preview data must be structured-cloneable. The engine receives a detached copy; mutating it does not change caller data or stored documents.
|
|
27
|
+
- The engine owns functions, pipelines, scope and escaping. Output is not escaped again. Match the backend's `text/template` or `html/template` behavior and escaping policy; the adapter alone does not guarantee equivalent engines.
|
|
28
|
+
- Rendering returns a string synchronously; Promises are unsupported. Existing `Error` objects propagate unchanged; other thrown values become an `Error` with a nonempty message and the original `cause`, for both rendering and validation. There is no fallback. The UI uses its existing error and Retry display; explicit API calls throw.
|
|
29
|
+
- Without data, the basic engine preserves source. An explicitly selected external engine still runs with `{}`.
|
|
30
|
+
- `validateTemplate()` only invokes the selected optional validator on explicit calls. `[]` means no issues were found; `undefined` means validation is unavailable. The basic engine has no Go validator. Rendering, `editor.validate()` and saving never invoke template validation automatically.
|
|
31
|
+
- `getHtml()`, `getJson()` and default `documentToHtml()` preserve templates. Existing listed-token dot normalization still applies. Engines only run when template resolution is requested.
|
|
32
|
+
|
|
33
|
+
The basic engine supports field paths, dot context, `$` / `$.field`, legacy bare paths, booleans/nil, decimal numbers, JSON-compatible double-quoted strings, basic `if/else if/else`, `range/else`, `with/else` and whitespace trimming. Arrays retain order; object keys are sorted. Missing fields produce empty strings and interpolated values are HTML-escaped.
|
|
34
|
+
|
|
35
|
+
Field names support Unicode letters, digits and underscores, starting with a letter or underscore. Conditions treat `null/undefined/false/0/empty strings`, empty arrays, plain objects without own enumerable keys and empty `Map/Set` values as false; nonempty collections and other objects such as `Date` are true. This only defines `Map/Set` truthiness, without adding field-access or iteration semantics.
|
|
36
|
+
|
|
37
|
+
Functions (including `eq/ne/and/or/not/len/index/printf/html`), pipelines, parentheses, local variables, template definitions/calls, comments and other full Go features require an external engine. Unsupported syntax or incomplete control structures preserve the **entire input for that render**, without partial evaluation. In the editor this preserves that block's HTML template.
|
|
38
|
+
|
|
39
|
+
## Integration example
|
|
40
|
+
|
|
41
|
+
Wrap your template library as TemplateEngine and pass the same adapter to editing and standalone preview. The caller provides templateEngine in this example:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import {
|
|
45
|
+
EmailEditor, useEmailEditor, useEmailEditorConfig,
|
|
46
|
+
renderTemplatePreview, validateTemplate,
|
|
47
|
+
type TemplateEngine, type TemplateData,
|
|
48
|
+
} from "@standhigher/react-email-editor";
|
|
49
|
+
|
|
50
|
+
interface EmailPageProps {
|
|
51
|
+
templateEngine: TemplateEngine;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function EmailPage({ templateEngine }: EmailPageProps) {
|
|
55
|
+
const config = useEmailEditorConfig({ templateEngine });
|
|
56
|
+
const editor = useEmailEditor({ config });
|
|
57
|
+
return <EmailEditor editor={editor} height={720} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function previewHtml(
|
|
61
|
+
template: string,
|
|
62
|
+
data: TemplateData,
|
|
63
|
+
templateEngine: TemplateEngine,
|
|
64
|
+
) {
|
|
65
|
+
return renderTemplatePreview(template, data, { templateEngine });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function inspectTemplate(template: string, templateEngine: TemplateEngine) {
|
|
69
|
+
return validateTemplate(template, { templateEngine });
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
validateTemplate() returns undefined when validation is unavailable and [] when no issues are found. It never blocks saving automatically. Rendering is per block and synchronous; the engine owns escaping. The basic engine preserves the entire input when it contains unsupported functions or pipelines.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 模板预览与自定义引擎
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
通过 `useEmailEditorConfig({ templateEngine })` 注入同一个同步适配器,画布生成的 `renderedHtml`、编辑器预览、`getRenderedHtml()` 和 `getData().renderedHtml` 都使用该引擎。富文本编辑区域继续显示可编辑的模板原文。
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface TemplateEngine {
|
|
9
|
+
render: (template: string, data: TemplateData) => string;
|
|
10
|
+
validate?: (template: string) => TemplateValidationIssue[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface TemplateValidationIssue {
|
|
14
|
+
message: string;
|
|
15
|
+
offset?: number; // 从 0 开始的原始模板字符位置
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TemplateEngineOptions {
|
|
19
|
+
templateEngine?: TemplateEngine;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
业务侧将自己的引擎包装为 `TemplateEngine`,包不额外安装完整 Go 引擎。`renderTemplatePreview(template, data?, { templateEngine })` 和 `validateTemplate(template, { templateEngine })` 使用相同的选择规则;省略引擎时使用公开的 `defaultTemplateEngine`。配置引用稳定时会复用;更换引擎对象会刷新展示与导出,不重置文档,也不触发文档变更回调。
|
|
24
|
+
|
|
25
|
+
- 引擎收到每个区块 `renderEmail` 生成的完整 HTML;预览与发送先按 `variables` 清单精确匹配 token 并补点,保留空白与 trim 标记,其他表达式原样传递,不先运行默认解析器。单独调用 `renderTemplatePreview()` 时则直接传入调用者的模板。区块分别执行,不能跨区块声明和引用局部变量。
|
|
26
|
+
- `TemplateData` 是键值对象;预览数据需可被 `structuredClone()` 复制。传给引擎的是独立副本,修改该副本不影响文档或调用方数据。
|
|
27
|
+
- 外部引擎负责函数、管道、作用域和 HTML 转义,包不再转义其返回值。应选择与后端一致的 `text/template` 或 `html/template` 语义及转义策略;仅注入适配器不保证不同引擎之间语义一致。
|
|
28
|
+
- 接口同步返回字符串,不支持 Promise。已有 `Error` 原样抛出,其他异常归一化为带原始 `cause` 和非空消息的 `Error`;渲染与校验遵循同一规则。界面使用现有错误提示及 Retry,不静默回退到默认引擎。显式 API 调用抛出异常。
|
|
29
|
+
- 默认引擎省略 `data` 时保留原文;显式配置外部引擎时,即使没有预览数据也调用它,并传入 `{}`。
|
|
30
|
+
- `validateTemplate()` 仅在显式调用时执行,返回诊断数组;`[]` 表示校验器未发现问题,`undefined` 表示所选引擎没有提供校验能力。默认引擎不提供 Go 语法校验;渲染、`editor.validate()` 和保存均不会自动执行模板校验。
|
|
31
|
+
- `getHtml()`、`getJson()` 和默认 `documentToHtml()` 保留模板;原有变量列表补点规则不变。引擎只在请求解析模板时运行。
|
|
32
|
+
|
|
33
|
+
默认引擎支持 `.field`、嵌套字段、当前上下文 `.`、根变量 `$` / `$.field`,以及历史支持的无点字段名;支持 `true/false/nil`、十进制数、JSON 兼容的双引号字符串,基础 `if/else if/else`、`range/else`、`with/else` 和 `{{- -}}` 空白裁剪。数组按顺序遍历,对象按键排序;`range/with` 切换当前上下文,`if` 保持上下文。缺失字段输出空字符串,普通插值做 HTML 转义。
|
|
34
|
+
|
|
35
|
+
字段名支持 Unicode 字母、数字和下划线,首字符为字母或下划线。条件中的 `null/undefined/false/0/空字符串`、空数组、无自有可枚举键的普通对象及空 `Map/Set` 为假;非空集合和其他对象(如 `Date`)为真。此处 `Map/Set` 仅约定条件真值,不扩展字段访问或遍历语义。
|
|
36
|
+
|
|
37
|
+
默认引擎不支持函数调用(含 `eq/ne/and/or/not/len/index/printf/html`)、管道、括号表达式、局部变量、模板定义/调用、注释和其他完整 Go 语法。输入包含不支持的语法或不完整的控制结构时,**保留该次渲染的完整输入,不执行部分替换**;编辑器中意味着保留对应区块的 HTML 模板。需要完整 Go 兼容性时接入外部引擎。
|
|
38
|
+
|
|
39
|
+
## 接入示例
|
|
40
|
+
|
|
41
|
+
业务侧把实际模板库封装为 `TemplateEngine`,再把同一个适配器用于编辑器和独立预览。下面示例的 `templateEngine` 由调用方提供:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import {
|
|
45
|
+
EmailEditor, useEmailEditor, useEmailEditorConfig,
|
|
46
|
+
renderTemplatePreview, validateTemplate,
|
|
47
|
+
type TemplateEngine, type TemplateData,
|
|
48
|
+
} from "@standhigher/react-email-editor";
|
|
49
|
+
|
|
50
|
+
interface EmailPageProps {
|
|
51
|
+
templateEngine: TemplateEngine;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function EmailPage({ templateEngine }: EmailPageProps) {
|
|
55
|
+
const config = useEmailEditorConfig({ templateEngine });
|
|
56
|
+
const editor = useEmailEditor({ config });
|
|
57
|
+
return <EmailEditor editor={editor} height={720} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function previewHtml(
|
|
61
|
+
template: string,
|
|
62
|
+
data: TemplateData,
|
|
63
|
+
templateEngine: TemplateEngine,
|
|
64
|
+
) {
|
|
65
|
+
return renderTemplatePreview(template, data, { templateEngine });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function inspectTemplate(template: string, templateEngine: TemplateEngine) {
|
|
69
|
+
return validateTemplate(template, { templateEngine });
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`validateTemplate()` 返回 `undefined` 表示该引擎不提供校验,`[]` 表示校验器未发现问题。校验按需调用,不自动阻止保存。渲染以区块为单位;引擎负责转义,不支持异步返回。默认引擎不计算函数或管道,遇到未支持语法会保留完整输入。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standhigher/react-email-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React email template editor with drag-and-drop blocks, rich text, JSON storage, and HTML export.",
|
|
6
6
|
"keywords": [
|
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
"go-template"
|
|
18
18
|
],
|
|
19
19
|
"author": "Nono",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/standhigher/react-email-editor.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/standhigher/react-email-editor#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/standhigher/react-email-editor/issues"
|
|
28
|
+
},
|
|
29
|
+
"packageManager": "npm@11.13.0",
|
|
20
30
|
"exports": {
|
|
21
31
|
".": {
|
|
22
32
|
"types": "./dist/index.d.ts",
|
|
@@ -27,24 +37,29 @@
|
|
|
27
37
|
"main": "./dist/index.js",
|
|
28
38
|
"module": "./dist/index.js",
|
|
29
39
|
"types": "./dist/index.d.ts",
|
|
40
|
+
"sideEffects": ["**/*.css"],
|
|
30
41
|
"files": [
|
|
31
42
|
"dist",
|
|
32
|
-
"docs",
|
|
43
|
+
"docs/**/*.md",
|
|
44
|
+
"docs/**/*.svg",
|
|
33
45
|
"README.md",
|
|
34
|
-
"README.zh-CN.md"
|
|
35
|
-
"CHANGELOG.md"
|
|
46
|
+
"README.zh-CN.md"
|
|
36
47
|
],
|
|
37
48
|
"publishConfig": {
|
|
38
|
-
"access": "public"
|
|
49
|
+
"access": "public",
|
|
50
|
+
"registry": "https://registry.npmjs.org/"
|
|
39
51
|
},
|
|
40
52
|
"scripts": {
|
|
41
53
|
"dev": "vite --config playground/vite.config.ts",
|
|
42
54
|
"build": "tsup",
|
|
43
55
|
"build:watch": "tsup --watch src",
|
|
44
|
-
"
|
|
56
|
+
"test": "node --test tests/*.test.mjs tests/*.test.ts",
|
|
57
|
+
"check": "npm run typecheck && npm run lint && npm run build && npm test",
|
|
58
|
+
"prepack": "npm run build",
|
|
59
|
+
"prepublishOnly": "npm run check",
|
|
45
60
|
"typecheck": "tsc --noEmit && tsc --noEmit -p playground",
|
|
46
61
|
"lint": "oxlint . --deny-warnings --report-unused-disable-directives",
|
|
47
|
-
"lint:fix": "
|
|
62
|
+
"lint:fix": "npm run lint -- --fix"
|
|
48
63
|
},
|
|
49
64
|
"peerDependencies": {
|
|
50
65
|
"react": "^18",
|
|
@@ -63,11 +78,6 @@
|
|
|
63
78
|
"typescript": "^5",
|
|
64
79
|
"vite": "^8.3.5"
|
|
65
80
|
},
|
|
66
|
-
"pnpm": {
|
|
67
|
-
"onlyBuiltDependencies": [
|
|
68
|
-
"esbuild"
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
81
|
"dependencies": {
|
|
72
82
|
"@codemirror/commands": "^6.11.1",
|
|
73
83
|
"@codemirror/language": "^6.13.1",
|
|
Binary file
|
|
Binary file
|