mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-09-02 19:52:32 +02:00
add check list example
This commit is contained in:
8
examples/check-lists/Readme.md
Normal file
8
examples/check-lists/Readme.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
# Rich Text Example
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This example shows you can add a very different concepts together: key commands, toolbars, and custom formatting, to get the functionality you'd expect from a rich text editor. Of course this is just the beginning, you can layer in whatever other behaviors you want!
|
||||||
|
|
||||||
|
Check out the [Examples readme](..) to see how to run it!
|
147
examples/check-lists/index.js
Normal file
147
examples/check-lists/index.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
import { Editor, Raw } from '../..'
|
||||||
|
import React from 'react'
|
||||||
|
import initialState from './state.json'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check list item.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CheckListItem extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On change, set the new checked value on the block.
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
|
||||||
|
onChange = (e) => {
|
||||||
|
const checked = e.target.checked
|
||||||
|
const { editor, node } = this.props
|
||||||
|
const state = editor
|
||||||
|
.getState()
|
||||||
|
.transform()
|
||||||
|
.setNodeByKey(node.key, { data: { checked }})
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
editor.onChange(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a check list item, using `contenteditable="false"` to embed the
|
||||||
|
* checkbox right next to the block's text.
|
||||||
|
*
|
||||||
|
* @return {Element}
|
||||||
|
*/
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
const { attributes, children, node } = this.props
|
||||||
|
const checked = node.data.get('checked')
|
||||||
|
return (
|
||||||
|
<div {...attributes} className="check-list-item">
|
||||||
|
<div contentEditable={false}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a schema.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
nodes: {
|
||||||
|
'check-list-item': CheckListItem,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rich text example.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CheckLists extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize the initial editor state.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
state = {
|
||||||
|
state: Raw.deserialize(initialState, { terse: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On change, save the new state.
|
||||||
|
*
|
||||||
|
* @param {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
onChange = (state) => {
|
||||||
|
this.setState({ state })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On key down, if enter is pressed inside of a check list item, make sure
|
||||||
|
* that when it is split the new item starts unchecked.
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {State} state
|
||||||
|
* @return {State|Void}
|
||||||
|
*/
|
||||||
|
|
||||||
|
onKeyDown = (e, data, state) => {
|
||||||
|
if (data.key != 'enter') return
|
||||||
|
if (state.startBlock.type != 'check-list-item') return
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.splitBlock()
|
||||||
|
.setBlock({ data: { checked: false }})
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render.
|
||||||
|
*
|
||||||
|
* @return {Element}
|
||||||
|
*/
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="editor">
|
||||||
|
<Editor
|
||||||
|
spellCheck
|
||||||
|
placeholder={'Enter some text...'}
|
||||||
|
schema={schema}
|
||||||
|
state={this.state.state}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default CheckLists
|
134
examples/check-lists/state.json
Normal file
134
examples/check-lists/state.json
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "With Slate you can build complex block types that have their own embeded content and behaviors, like rendering checkboxes inside check list items!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": true,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Slide to the left."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": true,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Slide to the right."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": false,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Criss-cross."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": true,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Criss-cross!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": false,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Cha cha real smooth…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "check-list-item",
|
||||||
|
"data": {
|
||||||
|
"checked": false,
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Let's go to work!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Try it out for yourself!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -203,3 +203,12 @@ input:focus {
|
|||||||
.emoji.selected {
|
.emoji.selected {
|
||||||
outline: 2px solid blue;
|
outline: 2px solid blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.check-list-item + .check-list-item {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list-item [contenteditable="false"] {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.75em;
|
||||||
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import { Router, Route, Link, IndexRedirect, hashHistory } from 'react-router'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import AutoMarkdown from './auto-markdown'
|
import AutoMarkdown from './auto-markdown'
|
||||||
|
import CheckLists from './check-lists'
|
||||||
import CodeHighlighting from './code-highlighting'
|
import CodeHighlighting from './code-highlighting'
|
||||||
import Embeds from './embeds'
|
import Embeds from './embeds'
|
||||||
import Emojis from './emojis'
|
import Emojis from './emojis'
|
||||||
@@ -101,6 +102,7 @@ class App extends React.Component {
|
|||||||
{this.renderTab('Embeds', 'embeds')}
|
{this.renderTab('Embeds', 'embeds')}
|
||||||
{this.renderTab('Emojis', 'emojis')}
|
{this.renderTab('Emojis', 'emojis')}
|
||||||
{this.renderTab('Tables', 'tables')}
|
{this.renderTab('Tables', 'tables')}
|
||||||
|
{this.renderTab('Check Lists', 'check-lists')}
|
||||||
{this.renderTab('Code Highlighting', 'code-highlighting')}
|
{this.renderTab('Code Highlighting', 'code-highlighting')}
|
||||||
{this.renderTab('Paste HTML', 'paste-html')}
|
{this.renderTab('Paste HTML', 'paste-html')}
|
||||||
{this.renderTab('Read-only', 'read-only')}
|
{this.renderTab('Read-only', 'read-only')}
|
||||||
@@ -153,6 +155,7 @@ const router = (
|
|||||||
<IndexRedirect to="rich-text" />
|
<IndexRedirect to="rich-text" />
|
||||||
|
|
||||||
<Route path="auto-markdown" component={AutoMarkdown} />
|
<Route path="auto-markdown" component={AutoMarkdown} />
|
||||||
|
<Route path="check-lists" component={CheckLists} />
|
||||||
<Route path="code-highlighting" component={CodeHighlighting} />
|
<Route path="code-highlighting" component={CodeHighlighting} />
|
||||||
<Route path="embeds" component={Embeds} />
|
<Route path="embeds" component={Embeds} />
|
||||||
<Route path="emojis" component={Emojis} />
|
<Route path="emojis" component={Emojis} />
|
||||||
|
Reference in New Issue
Block a user