跳至正文

文档对应 SoEditor 1.3.0

React 表单示例

SoEditor 1.3.0
为网站 CMS 而生

让内容编辑回归简单

一篇文章、一张产品介绍,或下一次发布。用熟悉的工具,把内容写好。

WYSIWYGHTML SourceCMS

点击后加载 · 内容仅保留在当前页面

安装与运行

/cms 组件从 1.3.0 起正式发布;原有 useSoEditorWorkspace 入口继续兼容。

sh
pnpm add @soeditor/editor@1.3.0 @soeditor/react@1.3.0 react react-dom

点击示例的“开始体验”运行已发布的软件包。下载完整示例源码,解压后执行 pnpm installpnpm dev,打开 react.html

体验步骤

  1. 编辑文章文字,查看下面的绑定 HTML 是否同步。
  2. 点击“替换 HTML”,确认外部状态更新到编辑器;切换只读后不能继续编辑。
  3. 点击“提交表单”查看 textarea 的规范 HTML;“重置表单”恢复本次挂载的初始值。
  4. 点击“HTML Source”观察首次加载;“重建示例”用当前绑定值创建新实例。结束体验会移除整个 iframe。

完整示例源码

ts
import { createElement as h, StrictMode, useRef, useState } from 'react';
import type { FormEvent } from 'react';
import { createRoot } from 'react-dom/client';
import { SoEditor } from '@soeditor/react/cms';
import type { ClassicEditor } from '@soeditor/editor/cms';
import { createEditor, initial, label, options } from './shared.js';

function ArticleForm() {
    const [html, setHtml] = useState(initial);
    const [readonly, setReadonly] = useState(false);
    const [version, setVersion] = useState(0);
    const [output, setOutput] = useState('');
    const [status, setStatus] = useState(label('正在加载', 'Loading'));
    const editor = useRef<ClassicEditor | undefined>(undefined);
    const button = (zh: string, en: string, onClick: () => void) =>
        h('button', { type: 'button', onClick }, label(zh, en));
    return h(
        'form',
        {
            onSubmit: (event: FormEvent<HTMLFormElement>) => {
                event.preventDefault();
                setOutput(
                    String(new FormData(event.currentTarget).get('body')),
                );
            },
        },
        h(
            'p',
            { className: 'notice' },
            label(
                'React 组件预览:内容仅保存在本页。',
                'React component preview: content stays in this page.',
            ),
        ),
        h(
            'div',
            { className: 'controls' },
            button('替换 HTML', 'Replace HTML', () =>
                setHtml('<h2>Updated article</h2><p>External value</p>'),
            ),
            button('切换只读', 'Toggle readonly', () => setReadonly(!readonly)),
            button('读取 HTML', 'Read HTML', () =>
                setOutput(editor.current?.getData() ?? ''),
            ),
            button('HTML Source', 'HTML Source', () => {
                void editor.current?.setWorkspaceView('source');
            }),
            button('重建示例', 'Recreate demo', () => {
                editor.current = undefined;
                setVersion(version + 1);
            }),
            h('button', { type: 'submit' }, label('提交表单', 'Submit form')),
            h('button', { type: 'reset' }, label('重置表单', 'Reset form')),
        ),
        h('p', { role: 'status' }, status),
        h(SoEditor, {
            key: version,
            name: 'body',
            value: html,
            onChange: setHtml,
            readonly,
            options,
            createEditor,
            onReady: (instance) => {
                editor.current = instance;
                setStatus(label('就绪', 'Ready'));
                document.body.dataset.ready = 'true';
            },
            onError: (error) => setStatus(String(error)),
        }),
        h('output', { 'data-bound-value': '' }, html),
        h('pre', null, output),
    );
}
const host = document.getElementById('app');
if (!host) throw new Error('Missing app host');
const root = createRoot(host);
root.render(h(StrictMode, null, h(ArticleForm)));
window.addEventListener('pagehide', () => root.unmount(), { once: true });
ts
import '@soeditor/editor/cms/styles.css';
import '../style.css';
import type { CreateClassicEditorOptions } from '@soeditor/editor/cms';

export const locale =
    new URLSearchParams(location.search).get('lang') === 'en' ? 'en' : 'zh-CN';
document.documentElement.lang = locale;
export const label = (zh: string, en: string) => (locale === 'en' ? en : zh);
export const initial = '<h2>CMS article</h2><p>Edit this content.</p>';
export const options: CreateClassicEditorOptions = {
    locale,
    ariaLabel: label('文章正文', 'Article HTML'),
    editingModes: ['wysiwyg', 'source'],
    minHeight: 240,
};
export const createEditor = async (
    host: HTMLElement,
    configuration: CreateClassicEditorOptions,
) => {
    const cms = await import('@soeditor/editor/cms/optional');
    return cms.createClassicEditor(host, configuration);
};
html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="robots" content="noindex, nofollow" />
        <title>SoEditor react demo</title>
    </head>
    <body>
        <div id="app"></div>
        <script type="module" src="./frameworks/react.ts"></script>
    </body>
</html>

接入说明

面向网站 CMS 的 HTML 编辑器 · MIT