ORY编辑器 (4)教程

ReactJS例子

在本节中,我们将创建一个使用ORY编辑器的最小化react应用程序。在我们跳过之前,请确保node.js已安装在您的系统上。

目前,ORY编辑器只能通过npm获得,并且在ReactJS环境中工作得最好。

ReactJS

我们的目标是创建一个使用编辑器的ReactJS应用程序。为了在本教程中搭建react应用程序,我们使用create-react-app

1
2
$ npm i -g create-react-app
$ create-react-app .

使用npm安装编辑器:

1
$ npm i --save ory-editor

接下来,打开文件src/components/App.js,包括ORY编辑器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import React, {Component} from 'react'

// The editor core
import Editor, { Editable, createEmptyState } from 'ory-editor-core'
import 'ory-editor-core/lib/index.css' // 我们还希望加载样式表

// 需要我们的ui组件(可选)。您也可以实现和使用自己的ui !
import { Trash, DisplayModeToggle, Toolbar } from 'ory-editor-ui'
import 'ory-editor-ui/lib/index.css'

// 加载一些示例插件:
import slate from 'ory-editor-plugins-slate' // T富文本区插件
import 'ory-editor-plugins-slate/lib/index.css' // 丰富文本区域插件的样式表
import parallax from 'ory-editor-plugins-parallax-background' // 视差背景图像插件
import 'ory-editor-plugins-parallax-background/lib/index.css' // 视差背景图像的样式表
require('react-tap-event-plugin')() // react-tap-event-plugin is required by material-ui which is used by ory-editor-ui so we need to call it here

// 定义我们想要使用的插件。我们只有板岩和视差可用,所以加载它们.
const plugins = {
content: [slate()], // 为内容单元格定义插件。要导入多个插件,请使用[slate(),image, spacer, divider]
layout: [parallax({ defaultPlugin: slate() })] // 定义布局单元格的插件
}

// Creates an empty editable
const content = createEmptyState()

// Instantiate the editor
const editor = new Editor({
plugins,
// pass the content state - you can add multiple editables here
editables: [content],
})

class App extends Component {
render() {
return (
<div>
<div className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h2>Welcome to React</h2>
</div>

{/* Content area */}
<Editable editor={editor} id={content.id}/>

{/* Default user interface */}
<Trash editor={editor}/>
<DisplayModeToggle editor={editor}/>
<Toolbar editor={editor}/>
</div>
);
}
}

export default App;

就是这样,恭喜你!你现在应该看到这样的东西:

react-example-app

编写插件

编写一个内容插件

当然,您并不局限于此功能,而且可以轻松编写自己的插件。插件有两个部分,一个插件定义和一个ReactJS组件。一个最小的插件定义如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, {Component} from 'react'

// 显然,您并不局限于material-ui,但是我们非常喜欢material-ui svg图标
import StarIcon from 'material-ui/svg-icons/toggle/star'

// This is the ReactJS component which you can find below this snippet
import InputTextField from './Component'

export default {
Component: InputTextField,
IconComponent: <StarIcon />,
name: 'example/content/input-text-field',
version: '0.0.1',
text: 'Input Text Field'
}

一个极简插件的例子可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import React from 'react'

// A callback function for the input field
const onInput = (onChange) => {
return (e) => {
// Dispatch the onChange action with the new value
onChange({
value: e.target.value
})
}
}

const InputTextField = (props) => {
const {
state: { value },
readOnly,
onChange
} = props

// If readOnly is false, it means that we are in edit mode!
if (!readOnly) {
return (
<div className="my-plugin">
<input
type="text"
onChange={onInput(onChange)} value={value} />
</div>
)
}

// If we are not in edit mode, remove the input field
return (
<div className="my-plugin">
{value}
</div>
)
}

export default InputTextField

当然,还有更多的设置和回调可用。我们鼓励您查看关于这个主题的API文档!

确保onChange prop不会传递给HTML元素(例如{...props}),因为这会用该元素发出的任何更改事件覆盖插件的状态。这适用于内容和布局插件。

编写布局插件

当然,您并不局限于此功能,而且可以轻松编写自己的插件。插件有两个部分,一个插件定义和一个ReactJS组件。一个布局插件将需要一个初始的子元素,否则,它将自动被销毁。一个最小的布局插件定义如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React from 'react'
import slate from 'ory-editor-plugins-slate'

// You are obviously not limited to material-ui, but we really enjoy
// the material-ui svg icons!
import CropSquare from 'material-ui/svg-icons/image/crop-square'

const BlackBorderPlugin = ({ children }) => (
<div style={{ border: '1px solid black', padding: '16px' }}>
{children}
</div>
)

export default {
Component: BlackBorderPlugin,
IconComponent: <CropSquare />,
name: 'example/layout/black-border',
version: '0.0.1',
text: 'Black border',
createInitialChildren: () => ({
id: v4(),
rows: [
{
id: v4(),
cells: [
{
content: {
plugin: slate(),
state: slate().createInitialState()
},
id: v4()
}
]
}
]
})
}

在这个例子中,最初的子元素是一个slate插件。

渲染HTML

ory-editor-renderer包附带了一个轻量级HTML renderer模块。您可以将其用于服务器端渲染和内容客户端渲染。

1
2
3
4
5
6
7
8
9
10
11
12
import { HTMLRenderer } from 'ory-editor-renderer'

const state = { /* ... */ }
const plugins = {
layout: [/* ... */],
content: [/* ... */]
}

const element = document.getElementById('editable')
ReactDOM.render((
<HTMLRenderer state={content[0]} plugins={plugins}/>
), element)

保存和恢复编辑器内容

使用onChange回调获取编辑器状态的副本,以便保存到持久存储。然后可以将状态加载到编辑器中,或者由ory-editor-renderer包用于将其渲染为HTML。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, {Component} from 'react'
import Editor, { Editable, createEmptyState } from 'ory-editor-core'
import slate from 'ory-editor-plugins-slate' // The rich text area plugin
import { Trash, DisplayModeToggle, Toolbar } from 'ory-editor-ui'
const EditorPlugins = {
content: [slate()],
layout: [/* ... */],
};

function saveToDatabase(state) {
return fetch('/my/save/url', { method: 'POST', body: state });
}

class MyEditor extends Component {
componentWillMount() {
this.editorState = this.props.content || createEmptyState();
this.editor = new Editor({ EditorPlugins, editables: [content] });
}
render() {
return (
<div className="my-editor">
<toolbar>
<button onClick={() => saveToDatabase(this.editorState)}>Save</button>
</toolbar>
<Editable editor={editor} id={content.id} onChange={state => (this.editorState = state)} />
<Trash editor={editor}/>
<DisplayModeToggle editor={editor}/>
<Toolbar editor={editor}/>
</div>
)
}
}

然后可以通过以下操作获取和呈现状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React, {Component} from 'react'

import { HTMLRenderer } from 'ory-editor-renderer'
import { createEmptyState } from 'ory-editor-core'

class MyEditorRenderer extends Component {

componentWillMount() {
this.plugins = {
this.setState({ contents: createEmptyState() });
fetch('/my/save/url').then((savedState) => {
this.setState({ contents: savedState });
})
}

render() {
return (
<div className="my-editor">
<HTMLRenderer state={this.state.contents} plugins={EditorPlugins} />
</div>
)
}
}

const element = document.getElementById('editable')
ReactDOM.render((
<MyEditorRenderer />
), element)

处理本地拖动事件

ORY编辑器能够处理原生拖放事件。原生事件包括链接、文本和图像的拖动。可以通过编写NativePlugin并在实例化期间传递它来启用本机拖动支持。在本例中,我们将使用默认插件,并在稍后介绍如何创建自己的插件。

1
2
3
4
5
6
7
8
9
import native from 'ory-editor-plugins-default-native'

const editor = new Editor({
plugins: {
layout: [],
content: [],
native
}
})

如果原生未定义或为空,则禁用原生拖动。这是默认设置。

编写本地插件就像编写内容或布局插件一样。唯一的区别是,我们的本地插件必须封装在一个接收三个参数的工厂中——hover, monitor, component。悬停是当前正在悬停的单元格或行。Monitor是来自react-dnd的DropTargetMonitor,组件是当前悬停的单元格或行的响应组件。

总之,一个示例插件是这样的:

1
2
3
4
5
6
7
8
9
import React from 'react'

const Native = () => <div>native</div>

export default (hover, monitor, component) => ({
Component: Native,
name: 'my-native-plugin',
version: '0.0.1'
})

因为这个插件被封装在工厂中,所以我们能够根据接收到的属性修改它的行为。其中一个例子是将项目的数据添加到初始状态,以便以后使用。

1
2
3
4
5
6
export default (hover, monitor, component) => ({
// ...
createInitialState: () => ({
item: monitor.getItem()
})
})

在默认情况下,编辑器假定删除本地元素会创建一个内容单元格。要更改此行为,请使用键type:

1
2
3
4
export default (hover, monitor, component) => ({
// ...
type: 'layout'
})

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2021 朝着牛逼的道路一路狂奔 All Rights Reserved.

访客数 : | 访问量 :