mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-11 01:33:58 +02:00
more stuff, started adding auto-markdown example
This commit is contained in:
32
Makefile
32
Makefile
@@ -27,17 +27,21 @@ dist: ./node_modules $(shell find ./lib)
|
|||||||
@ $(babel) --out-dir ./dist ./lib
|
@ $(babel) --out-dir ./dist ./lib
|
||||||
@ touch ./dist
|
@ touch ./dist
|
||||||
|
|
||||||
|
# Build the auto-markdown example.
|
||||||
|
example-auto-markdown: ./node_modules
|
||||||
|
@ $(browserify) --debug --transform babelify --outfile ./examples/auto-markdown/build.js ./examples/auto-markdown/index.js
|
||||||
|
|
||||||
# Build the basic example.
|
# Build the basic example.
|
||||||
example-basic: ./node_modules
|
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.
|
# Build the plain-text example.
|
||||||
example-plaintext: ./node_modules
|
example-plain-text: ./node_modules
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/plaintext/build.js ./examples/plaintext/index.js
|
@ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js
|
||||||
|
|
||||||
# Build the richtext example.
|
# Build the rich-text example.
|
||||||
example-richtext: ./node_modules
|
example-rich-text: ./node_modules
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/richtext/build.js ./examples/richtext/index.js
|
@ $(browserify) --debug --transform babelify --outfile ./examples/rich-text/build.js ./examples/rich-text/index.js
|
||||||
|
|
||||||
# Lint the sources files with Standard JS.
|
# Lint the sources files with Standard JS.
|
||||||
lint: ./node_modules
|
lint: ./node_modules
|
||||||
@@ -63,17 +67,21 @@ 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 auto-markdown example.
|
||||||
|
watch-example-auto-markdown: ./node_modules
|
||||||
|
@ $(MAKE) example-auto-markdown browserify=$(watchify)
|
||||||
|
|
||||||
# Watch the basic example.
|
# Watch the basic example.
|
||||||
watch-example-basic: ./node_modules
|
watch-example-basic: ./node_modules
|
||||||
@ $(MAKE) example-basic browserify=$(watchify)
|
@ $(MAKE) example-basic browserify=$(watchify)
|
||||||
|
|
||||||
# Watch the plaintext example.
|
# Watch the plain-text example.
|
||||||
watch-example-plaintext: ./node_modules
|
watch-example-plain-text: ./node_modules
|
||||||
@ $(MAKE) example-plaintext browserify=$(watchify)
|
@ $(MAKE) example-plain-text browserify=$(watchify)
|
||||||
|
|
||||||
# Watch the richtext example.
|
# Watch the rich-text example.
|
||||||
watch-example-richtext: ./node_modules
|
watch-example-rich-text: ./node_modules
|
||||||
@ $(MAKE) example-richtext browserify=$(watchify)
|
@ $(MAKE) example-rich-text browserify=$(watchify)
|
||||||
|
|
||||||
# Phony targets.
|
# Phony targets.
|
||||||
.PHONY: examples
|
.PHONY: examples
|
||||||
|
12
examples/auto-markdown/index.html
Normal file
12
examples/auto-markdown/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Editor | Auto-markdown Example</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main></main>
|
||||||
|
<script src="build.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
187
examples/auto-markdown/index.js
Normal file
187
examples/auto-markdown/index.js
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
|
||||||
|
import Editor, { Mark, Raw } from '../..'
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import keycode from 'keycode'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'Since it\'s rich text, you can do things like turn a selection of text ',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block-quote',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'A wise quote.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'Try it out for yourself!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
state: Raw.deserialize(state)
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="editor">
|
||||||
|
<Editor
|
||||||
|
state={this.state.state}
|
||||||
|
renderNode={node => this.renderNode(node)}
|
||||||
|
renderMark={mark => this.renderMark(mark)}
|
||||||
|
onKeyDown={(e, state) => this.onKeyDown(e, state)}
|
||||||
|
onChange={(state) => {
|
||||||
|
console.groupCollapsed('Change!')
|
||||||
|
console.log('Document:', state.document.toJS())
|
||||||
|
console.log('Selection:', state.selection.toJS())
|
||||||
|
console.log('Content:', Raw.serialize(state))
|
||||||
|
console.groupEnd()
|
||||||
|
this.setState({ state })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNode(node) {
|
||||||
|
switch (node.type) {
|
||||||
|
case 'block-quote': {
|
||||||
|
return (props) => <blockquote>{props.children}</blockquote>
|
||||||
|
}
|
||||||
|
case 'bulleted-list': {
|
||||||
|
return (props) => <ul>{props.chidlren}</ul>
|
||||||
|
}
|
||||||
|
case 'heading-one': {
|
||||||
|
return (props) => <h1>{props.children}</h1>
|
||||||
|
}
|
||||||
|
case 'heading-two': {
|
||||||
|
return (props) => <h2>{props.children}</h2>
|
||||||
|
}
|
||||||
|
case 'heading-three': {
|
||||||
|
return (props) => <h3>{props.children}</h3>
|
||||||
|
}
|
||||||
|
case 'heading-four': {
|
||||||
|
return (props) => <h4>{props.children}</h4>
|
||||||
|
}
|
||||||
|
case 'heading-five': {
|
||||||
|
return (props) => <h5>{props.children}</h5>
|
||||||
|
}
|
||||||
|
case 'heading-six': {
|
||||||
|
return (props) => <h6>{props.children}</h6>
|
||||||
|
}
|
||||||
|
case 'list-item': {
|
||||||
|
return (props) => <li>{props.chidlren}</li>
|
||||||
|
}
|
||||||
|
case 'numbered-list': {
|
||||||
|
return (props) => <ol>{props.children}</ol>
|
||||||
|
}
|
||||||
|
case 'paragraph': {
|
||||||
|
return (props) => <p>{props.children}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(e, state) {
|
||||||
|
const key = keycode(e.which)
|
||||||
|
if (key != 'space') return
|
||||||
|
if (state.isCurrentlyExpanded) return
|
||||||
|
let { selection } = state
|
||||||
|
const { currentTextNodes, document } = state
|
||||||
|
const { startOffset } = selection
|
||||||
|
const node = currentTextNodes.first()
|
||||||
|
const { text } = node
|
||||||
|
const chars = text.slice(0, startOffset).replace(/\s*/g, '')
|
||||||
|
let transform = state.transform()
|
||||||
|
|
||||||
|
switch (chars) {
|
||||||
|
case '#':
|
||||||
|
transform = transform.setType('header-one')
|
||||||
|
break
|
||||||
|
case '##':
|
||||||
|
transform = transform.setType('header-two')
|
||||||
|
break
|
||||||
|
case '###':
|
||||||
|
transform = transform.setType('header-three')
|
||||||
|
break
|
||||||
|
case '####':
|
||||||
|
transform = transform.setType('header-four')
|
||||||
|
break
|
||||||
|
case '#####':
|
||||||
|
transform = transform.setType('header-five')
|
||||||
|
break
|
||||||
|
case '######':
|
||||||
|
transform = transform.setType('header-six')
|
||||||
|
break
|
||||||
|
case '>':
|
||||||
|
transform = transform.setType('block-quote')
|
||||||
|
break
|
||||||
|
case '-':
|
||||||
|
transform = transform.setType('list-item')
|
||||||
|
if (wrapper.type == 'paragraph') transform = transform.setType('bulleted-list')
|
||||||
|
if (wrapper.type == 'bulleted-list') transform = transform.wrap('list-item')
|
||||||
|
if (wrapper.type == 'list-item') transform = transform.wrap('unordered-list')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
state = transform
|
||||||
|
.deleteAtRange(selection.extendBackwardToStartOf(node))
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
selection = selection.moveToStartOf(node)
|
||||||
|
state = state.merge({ selection })
|
||||||
|
e.preventDefault()
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const app = <App />
|
||||||
|
const root = document.body.querySelector('main')
|
||||||
|
ReactDOM.render(app, root)
|
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Editor | Plaintext Example</title>
|
<title>Editor | Plain Text Example</title>
|
||||||
<link rel="stylesheet" href="index.css">
|
<link rel="stylesheet" href="index.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
56
examples/rich-text/index.css
Normal file
56
examples/rich-text/index.css
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
html {
|
||||||
|
background: #eee;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 40em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 2px solid #ddd;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: #aaa;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor > * > * + * {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
margin: 0 -10px;
|
||||||
|
padding: 1px 0 9px 8px;
|
||||||
|
border-bottom: 2px solid #eee;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu > * {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu > * + * {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button[data-active="true"] {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Editor | Richtext Example</title>
|
<title>Editor | Rich Text Example</title>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
<link rel="stylesheet" href="index.css">
|
<link rel="stylesheet" href="index.css">
|
||||||
</head>
|
</head>
|
@@ -54,6 +54,55 @@ const state = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'Since it\'s rich text, you can do things like turn a selection of text ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'bold',
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: 'bold'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},{
|
||||||
|
text: ', or add a semanticlly rendered block quote in the middle of the page, like this:'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block-quote',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'A wise quote.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
text: 'Try it out for yourself!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
39
examples/test/index.html
Normal file
39
examples/test/index.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Editor | Basic Example</title>
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div contenteditable>
|
||||||
|
<p>
|
||||||
|
Some text.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Some text.
|
||||||
|
</p>
|
||||||
|
<blockquote>
|
||||||
|
<p>
|
||||||
|
Some text.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Some text.
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
<p></p>
|
||||||
|
<p></p>
|
||||||
|
<table border>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>one</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td>two</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -53,6 +53,26 @@ class State extends Record(DEFAULTS) {
|
|||||||
return new State(properties)
|
return new State(properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current selection collapsed?
|
||||||
|
*
|
||||||
|
* @return {Boolean} isCollapsed
|
||||||
|
*/
|
||||||
|
|
||||||
|
get isCurrentlyCollapsed() {
|
||||||
|
return this.selection.isCollapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current selection expanded?
|
||||||
|
*
|
||||||
|
* @return {Boolean} isExpanded
|
||||||
|
*/
|
||||||
|
|
||||||
|
get isCurrentlyExpanded() {
|
||||||
|
return this.selection.isExpanded
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the characters in the current selection.
|
* Get the characters in the current selection.
|
||||||
*
|
*
|
||||||
@@ -60,8 +80,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get currentCharacters() {
|
get currentCharacters() {
|
||||||
const { document, selection } = this
|
return this.document.getCharactersAtRange(this.selection)
|
||||||
return document.getCharactersAtRange(selection)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,8 +90,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get currentMarks() {
|
get currentMarks() {
|
||||||
const { document, selection } = this
|
return this.document.getMarksAtRange(this.selection)
|
||||||
return document.getMarksAtRange(selection)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,8 +100,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get currentWrappingNodes() {
|
get currentWrappingNodes() {
|
||||||
const { document, selection, textNodes } = this
|
return this.document.getWrappingNodesAtRange(this.selection)
|
||||||
return document.getWrappingNodesAtRange(selection)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,8 +110,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get currentTextNodes() {
|
get currentTextNodes() {
|
||||||
const { document, selection } = this
|
return this.document.getTextNodesAtRange(this.selection)
|
||||||
return document.getTextNodesAtRange(selection)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user