mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-21 13:51:59 +02:00
more stuff, started adding auto-markdown example
This commit is contained in:
parent
8bf081d26f
commit
eae70f0f17
32
Makefile
32
Makefile
@ -27,17 +27,21 @@ dist: ./node_modules $(shell find ./lib)
|
||||
@ $(babel) --out-dir ./dist ./lib
|
||||
@ 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.
|
||||
example-basic: ./node_modules
|
||||
@ $(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
|
||||
# Build the plain-text example.
|
||||
example-plain-text: ./node_modules
|
||||
@ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js
|
||||
|
||||
# Build the richtext example.
|
||||
example-richtext: ./node_modules
|
||||
@ $(browserify) --debug --transform babelify --outfile ./examples/richtext/build.js ./examples/richtext/index.js
|
||||
# Build the rich-text example.
|
||||
example-rich-text: ./node_modules
|
||||
@ $(browserify) --debug --transform babelify --outfile ./examples/rich-text/build.js ./examples/rich-text/index.js
|
||||
|
||||
# Lint the sources files with Standard JS.
|
||||
lint: ./node_modules
|
||||
@ -63,17 +67,21 @@ test-browser: ./node_modules ./test/support/build.js
|
||||
test-server: ./node_modules
|
||||
@ $(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-example-basic: ./node_modules
|
||||
@ $(MAKE) example-basic browserify=$(watchify)
|
||||
|
||||
# Watch the plaintext example.
|
||||
watch-example-plaintext: ./node_modules
|
||||
@ $(MAKE) example-plaintext browserify=$(watchify)
|
||||
# Watch the plain-text example.
|
||||
watch-example-plain-text: ./node_modules
|
||||
@ $(MAKE) example-plain-text browserify=$(watchify)
|
||||
|
||||
# Watch the richtext example.
|
||||
watch-example-richtext: ./node_modules
|
||||
@ $(MAKE) example-richtext browserify=$(watchify)
|
||||
# Watch the rich-text example.
|
||||
watch-example-rich-text: ./node_modules
|
||||
@ $(MAKE) example-rich-text browserify=$(watchify)
|
||||
|
||||
# Phony targets.
|
||||
.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>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Editor | Plaintext Example</title>
|
||||
<title>Editor | Plain Text Example</title>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
<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>
|
||||
<head>
|
||||
<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="index.css">
|
||||
</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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -60,8 +80,7 @@ class State extends Record(DEFAULTS) {
|
||||
*/
|
||||
|
||||
get currentCharacters() {
|
||||
const { document, selection } = this
|
||||
return document.getCharactersAtRange(selection)
|
||||
return this.document.getCharactersAtRange(this.selection)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,8 +90,7 @@ class State extends Record(DEFAULTS) {
|
||||
*/
|
||||
|
||||
get currentMarks() {
|
||||
const { document, selection } = this
|
||||
return document.getMarksAtRange(selection)
|
||||
return this.document.getMarksAtRange(this.selection)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,8 +100,7 @@ class State extends Record(DEFAULTS) {
|
||||
*/
|
||||
|
||||
get currentWrappingNodes() {
|
||||
const { document, selection, textNodes } = this
|
||||
return document.getWrappingNodesAtRange(selection)
|
||||
return this.document.getWrappingNodesAtRange(this.selection)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,8 +110,7 @@ class State extends Record(DEFAULTS) {
|
||||
*/
|
||||
|
||||
get currentTextNodes() {
|
||||
const { document, selection } = this
|
||||
return document.getTextNodesAtRange(selection)
|
||||
return this.document.getTextNodesAtRange(this.selection)
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user