1
0
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:
Ian Storm Taylor
2016-06-17 19:57:37 -07:00
parent 0caee5b687
commit 1413d57a04
11 changed files with 214 additions and 54 deletions

View File

@@ -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

View File

@@ -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 })
}} }}
/> />

View 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>

View 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)

View File

@@ -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.
* *

View File

@@ -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
}

View File

@@ -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('')
} }

View File

@@ -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.

View File

@@ -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 {}
} }
} }

View 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
}

View File

@@ -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",