mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-01 21:10:14 +02:00
add plaintext example
This commit is contained in:
28
Makefile
28
Makefile
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
# Binaries.
|
# Binaries.
|
||||||
bin = ./node_modules/.bin
|
bin = ./node_modules/.bin
|
||||||
|
babel = $(bin)/babel
|
||||||
browserify = $(bin)/browserify
|
browserify = $(bin)/browserify
|
||||||
standard = $(bin)/standard
|
standard = $(bin)/standard
|
||||||
mocha = $(bin)/mocha
|
mocha = $(bin)/mocha
|
||||||
@@ -19,12 +20,21 @@ endif
|
|||||||
|
|
||||||
# Remove the generated files.
|
# Remove the generated files.
|
||||||
clean:
|
clean:
|
||||||
@ rm -rf ./node_modules
|
@ rm -rf ./dist ./node_modules
|
||||||
|
|
||||||
# Build the examples.
|
# Build the source.
|
||||||
examples: ./node_modules
|
dist: ./node_modules $(shell find ./lib)
|
||||||
|
@ $(babel) --out-dir ./dist ./lib
|
||||||
|
@ touch ./dist
|
||||||
|
|
||||||
|
# Build the basic example.
|
||||||
|
example-basic: ./node_modules
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/basic/build.js ./examples/basic/index.js
|
@ $(browserify) --debug --transform babelify --outfile ./examples/basic/build.js ./examples/basic/index.js
|
||||||
|
|
||||||
|
# Build the plaintext example.
|
||||||
|
example-plaintext: ./node_modules
|
||||||
|
@ $(browserify) --debug --transform babelify --outfile ./examples/plaintext/build.js ./examples/plaintext/index.js
|
||||||
|
|
||||||
# Lint the sources files with Standard JS.
|
# Lint the sources files with Standard JS.
|
||||||
lint: ./node_modules
|
lint: ./node_modules
|
||||||
@ $(standard) ./lib
|
@ $(standard) ./lib
|
||||||
@@ -36,7 +46,7 @@ node_modules: ./package.json
|
|||||||
|
|
||||||
# Build the test source.
|
# Build the test source.
|
||||||
test/support/build.js: ./node_modules $(shell find ./lib) ./test/browser.js
|
test/support/build.js: ./node_modules $(shell find ./lib) ./test/browser.js
|
||||||
@ $(browserify) --transform babelify --outfile ./test/support/build.js ./test/browser.js
|
@ $(browserify) --debug --transform babelify --outfile ./test/support/build.js ./test/browser.js
|
||||||
|
|
||||||
# Run the tests.
|
# Run the tests.
|
||||||
test: test-browser test-server
|
test: test-browser test-server
|
||||||
@@ -49,9 +59,13 @@ test-browser: ./node_modules ./test/support/build.js
|
|||||||
test-server: ./node_modules
|
test-server: ./node_modules
|
||||||
@ $(mocha) --reporter spec --timeout 5000 ./test/server.js
|
@ $(mocha) --reporter spec --timeout 5000 ./test/server.js
|
||||||
|
|
||||||
# Watch the examples.
|
# Watch the basic example.
|
||||||
watch-examples: ./node_modules
|
watch-example-basic: ./node_modules
|
||||||
@ $(MAKE) examples browserify=$(watchify)
|
@ $(MAKE) example-basic browserify=$(watchify)
|
||||||
|
|
||||||
|
# Watch the plaintext example.
|
||||||
|
watch-example-plaintext: ./node_modules
|
||||||
|
@ $(MAKE) example-plaintext browserify=$(watchify)
|
||||||
|
|
||||||
# Phony targets.
|
# Phony targets.
|
||||||
.PHONY: examples
|
.PHONY: examples
|
||||||
|
@@ -75,9 +75,6 @@ function renderNode(node) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: {
|
|
||||||
throw new Error(`Unknown node type "${node.type}".`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +85,6 @@ function renderMark(mark) {
|
|||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: {
|
|
||||||
throw new Error(`Unknown mark type "${mark.type}".`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +106,7 @@ class App extends React.Component {
|
|||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={(state) => {
|
onChange={(state) => {
|
||||||
console.log('State:', state.toJS())
|
console.log('State:', state.toJS())
|
||||||
|
console.log('Content:', Raw.serialize(state))
|
||||||
this.setState({ state })
|
this.setState({ state })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
10
examples/plaintext/index.html
Normal file
10
examples/plaintext/index.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Editor | Basic Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main></main>
|
||||||
|
<script src="build.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
44
examples/plaintext/index.js
Normal file
44
examples/plaintext/index.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
import Editor from '../..'
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import { Plaintext } from '../..'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const state = 'A string of plain text.'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
state: Plaintext.deserialize(state)
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Editor
|
||||||
|
state={this.state.state}
|
||||||
|
onChange={(state) => {
|
||||||
|
console.log('State:', state.toJS())
|
||||||
|
console.log('Content:', Plaintext.serialize(state))
|
||||||
|
this.setState({ state })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const app = <App />
|
||||||
|
const root = document.body.querySelector('main')
|
||||||
|
ReactDOM.render(app, root)
|
@@ -46,6 +46,16 @@ class Editor extends React.Component {
|
|||||||
this.setState({ plugins })
|
this.setState({ plugins })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the editor's current `state`.
|
||||||
|
*
|
||||||
|
* @return {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
getState() {
|
||||||
|
return this.props.state
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the `state` changes, pass through plugins, then bubble up.
|
* When the `state` changes, pass through plugins, then bubble up.
|
||||||
*
|
*
|
||||||
@@ -65,16 +75,6 @@ class Editor extends React.Component {
|
|||||||
this.props.onChange(state)
|
this.props.onChange(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the editor's current `state`.
|
|
||||||
*
|
|
||||||
* @return {State} state
|
|
||||||
*/
|
|
||||||
|
|
||||||
getState() {
|
|
||||||
return this.props.state
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When an event by `name` fires, pass it through the plugins, and update the
|
* When an event by `name` fires, pass it through the plugins, and update the
|
||||||
* state if one of them chooses to.
|
* state if one of them chooses to.
|
||||||
@@ -102,15 +102,47 @@ class Editor extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Content
|
<Content
|
||||||
renderMark={this.props.renderMark}
|
|
||||||
renderNode={this.props.renderNode}
|
|
||||||
state={this.props.state}
|
state={this.props.state}
|
||||||
onChange={state => this.onChange(state)}
|
onChange={state => this.onChange(state)}
|
||||||
onKeyDown={e => this.onEvent('onKeyDown', e)}
|
onKeyDown={e => this.onEvent('onKeyDown', e)}
|
||||||
|
renderMark={mark => this.renderMark(mark)}
|
||||||
|
renderNode={node => this.renderNode(node)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a `node`, cascading through the plugins.
|
||||||
|
*
|
||||||
|
* @param {Node} node
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderNode(node) {
|
||||||
|
for (const plugin of this.state.plugins) {
|
||||||
|
if (!plugin.renderNode) continue
|
||||||
|
const component = plugin.renderNode(node, this.props.state, this)
|
||||||
|
if (component) return component
|
||||||
|
throw new Error('No renderer found for node.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a `mark`, cascading through the plugins.
|
||||||
|
*
|
||||||
|
* @param {Mark} mark
|
||||||
|
* @return {Object} style
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderMark(mark) {
|
||||||
|
for (const plugin of this.state.plugins) {
|
||||||
|
if (!plugin.renderMark) continue
|
||||||
|
const style = plugin.renderMark(mark, this.props.state, this)
|
||||||
|
if (style) return style
|
||||||
|
throw new Error('No renderer found for mark.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the editor's current plugins from `props` when they change.
|
* Resolve the editor's current plugins from `props` when they change.
|
||||||
*
|
*
|
||||||
|
30
lib/index.js
30
lib/index.js
@@ -1,36 +1,24 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Components.
|
* Editor.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Editor from './components/editor'
|
import Editor from './components/editor'
|
||||||
|
export default Editor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models.
|
* Models.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Character from './models/character'
|
export { default as Character } from './models/character'
|
||||||
import Node from './models/node'
|
export { default as Node } from './models/node'
|
||||||
import Selection from './models/selection'
|
export { default as Selection } from './models/selection'
|
||||||
import State from './models/state'
|
export { default as State } from './models/state'
|
||||||
import Text from './models/text'
|
export { default as Text } from './models/text'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializers.
|
* Serializers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Raw from './serializers/raw'
|
export { default as Raw } from './serializers/raw'
|
||||||
|
export { default as Plaintext } from './serializers/plaintext'
|
||||||
/**
|
|
||||||
* Export.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default Editor
|
|
||||||
export {
|
|
||||||
Character,
|
|
||||||
Node,
|
|
||||||
Raw,
|
|
||||||
Selection,
|
|
||||||
State,
|
|
||||||
Text
|
|
||||||
}
|
|
||||||
|
@@ -66,9 +66,7 @@ class Node extends NodeRecord {
|
|||||||
get text() {
|
get text() {
|
||||||
return this
|
return this
|
||||||
.filterNodes(node => node.type == 'text')
|
.filterNodes(node => node.type == 'text')
|
||||||
.map(node => node.characters)
|
.map(node => node.text)
|
||||||
.flatten()
|
|
||||||
.map(character => character.text)
|
|
||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -459,12 +459,14 @@ class State extends StateRecord {
|
|||||||
let { characters } = startNode
|
let { characters } = startNode
|
||||||
|
|
||||||
// Create a list of the new characters, with the right marks.
|
// Create a list of the new characters, with the right marks.
|
||||||
const { marks } = characters.get(startOffset)
|
const marks = characters.has(startOffset)
|
||||||
|
? characters.get(startOffset).marks
|
||||||
|
: null
|
||||||
|
|
||||||
const newCharacters = data.split('').reduce((list, char) => {
|
const newCharacters = data.split('').reduce((list, char) => {
|
||||||
return list.push(Character.create({
|
const obj = { text: char }
|
||||||
text: char,
|
if (marks) obj.marks = marks
|
||||||
marks
|
return list.push(Character.create(obj))
|
||||||
}))
|
|
||||||
}, Character.createList())
|
}, Character.createList())
|
||||||
|
|
||||||
// Splice in the new characters.
|
// Splice in the new characters.
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
import React from 'react'
|
||||||
import keycode from 'keycode'
|
import keycode from 'keycode'
|
||||||
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
|
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
|
||||||
|
|
||||||
@@ -62,6 +63,28 @@ export default {
|
|||||||
console.log('Unhandled key down.')
|
console.log('Unhandled key down.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default `node` renderer.
|
||||||
|
*
|
||||||
|
* @param {Node} node
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderNode(node) {
|
||||||
|
return (props) => <div>{props.children}</div>
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default `mark` renderer.
|
||||||
|
*
|
||||||
|
* @param {Mark} mark
|
||||||
|
* @return {Object} style
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderMark(mark) {
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
53
lib/serializers/plaintext.js
Normal file
53
lib/serializers/plaintext.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
import Character from '../models/character'
|
||||||
|
import Node from '../models/node'
|
||||||
|
import Text from '../models/text'
|
||||||
|
import State from '../models/state'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a `state` into a plaintext `string`.
|
||||||
|
*
|
||||||
|
* @param {State} state
|
||||||
|
* @return {String} string
|
||||||
|
*/
|
||||||
|
|
||||||
|
function serialize(state) {
|
||||||
|
return state.nodes
|
||||||
|
.map(node => node.text)
|
||||||
|
.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a plaintext `string` into a `state`.
|
||||||
|
*
|
||||||
|
* @param {String} string
|
||||||
|
* @return {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
function deserialize(string) {
|
||||||
|
const characters = string
|
||||||
|
.split('')
|
||||||
|
.reduce((list, char) => {
|
||||||
|
return list.push(Character.create({ text: char }))
|
||||||
|
}, Character.createList())
|
||||||
|
|
||||||
|
const text = Text.create({ characters })
|
||||||
|
const texts = Node.createMap([text])
|
||||||
|
const node = Node.create({
|
||||||
|
type: 'paragraph',
|
||||||
|
nodes: texts,
|
||||||
|
})
|
||||||
|
|
||||||
|
const nodes = Node.createMap([node])
|
||||||
|
const state = State.create({ nodes })
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
serialize,
|
||||||
|
deserialize
|
||||||
|
}
|
@@ -13,6 +13,7 @@
|
|||||||
"uid": "0.0.2"
|
"uid": "0.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"babel-cli": "^6.10.1",
|
||||||
"babel-core": "^6.9.1",
|
"babel-core": "^6.9.1",
|
||||||
"babel-polyfill": "^6.9.1",
|
"babel-polyfill": "^6.9.1",
|
||||||
"babel-preset-es2015": "^6.9.0",
|
"babel-preset-es2015": "^6.9.0",
|
||||||
|
Reference in New Issue
Block a user