diff --git a/Makefile b/Makefile
index 93158a0e1..0e2769dcf 100644
--- a/Makefile
+++ b/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
diff --git a/examples/richtext/index.css b/examples/auto-markdown/index.css
similarity index 100%
rename from examples/richtext/index.css
rename to examples/auto-markdown/index.css
diff --git a/examples/auto-markdown/index.html b/examples/auto-markdown/index.html
new file mode 100644
index 000000000..2ee7e77dd
--- /dev/null
+++ b/examples/auto-markdown/index.html
@@ -0,0 +1,12 @@
+
+
+
+ Editor | Auto-markdown Example
+
+
+
+
+
+
+
+
diff --git a/examples/auto-markdown/index.js b/examples/auto-markdown/index.js
new file mode 100644
index 000000000..a09ad523f
--- /dev/null
+++ b/examples/auto-markdown/index.js
@@ -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 (
+
+ 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 })
+ }}
+ />
+
+ )
+ }
+
+ renderNode(node) {
+ switch (node.type) {
+ case 'block-quote': {
+ return (props) => {props.children}
+ }
+ case 'bulleted-list': {
+ return (props) =>
+ }
+ case 'heading-one': {
+ return (props) => {props.children}
+ }
+ case 'heading-two': {
+ return (props) => {props.children}
+ }
+ case 'heading-three': {
+ return (props) => {props.children}
+ }
+ case 'heading-four': {
+ return (props) => {props.children}
+ }
+ case 'heading-five': {
+ return (props) => {props.children}
+ }
+ case 'heading-six': {
+ return (props) => {props.children}
+ }
+ case 'list-item': {
+ return (props) => {props.chidlren}
+ }
+ case 'numbered-list': {
+ return (props) => {props.children}
+ }
+ case 'paragraph': {
+ return (props) => {props.children}
+ }
+ }
+ }
+
+ 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 =
+const root = document.body.querySelector('main')
+ReactDOM.render(app, root)
diff --git a/examples/plaintext/index.css b/examples/plain-text/index.css
similarity index 100%
rename from examples/plaintext/index.css
rename to examples/plain-text/index.css
diff --git a/examples/plaintext/index.html b/examples/plain-text/index.html
similarity index 79%
rename from examples/plaintext/index.html
rename to examples/plain-text/index.html
index 869565479..811089bbf 100644
--- a/examples/plaintext/index.html
+++ b/examples/plain-text/index.html
@@ -1,7 +1,7 @@
- Editor | Plaintext Example
+ Editor | Plain Text Example
diff --git a/examples/plaintext/index.js b/examples/plain-text/index.js
similarity index 100%
rename from examples/plaintext/index.js
rename to examples/plain-text/index.js
diff --git a/examples/rich-text/index.css b/examples/rich-text/index.css
new file mode 100644
index 000000000..0c9a20b69
--- /dev/null
+++ b/examples/rich-text/index.css
@@ -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;
+}
diff --git a/examples/richtext/index.html b/examples/rich-text/index.html
similarity index 85%
rename from examples/richtext/index.html
rename to examples/rich-text/index.html
index a0b66ac19..66be0c316 100644
--- a/examples/richtext/index.html
+++ b/examples/rich-text/index.html
@@ -1,7 +1,7 @@
- Editor | Richtext Example
+ Editor | Rich Text Example
diff --git a/examples/richtext/index.js b/examples/rich-text/index.js
similarity index 84%
rename from examples/richtext/index.js
rename to examples/rich-text/index.js
index a97bbd999..ef768ad42 100644
--- a/examples/richtext/index.js
+++ b/examples/rich-text/index.js
@@ -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!'
+ }
+ ]
+ }
+ ]
}
]
}
diff --git a/examples/test/index.html b/examples/test/index.html
new file mode 100644
index 000000000..4b9fa08f3
--- /dev/null
+++ b/examples/test/index.html
@@ -0,0 +1,39 @@
+
+
+
+ Editor | Basic Example
+
+
+
+
+
+
+ Some text.
+
+
+ Some text.
+
+
+
+ Some text.
+
+
+ Some text.
+
+
+
+
+
+
+ 1 |
+ one |
+
+
+ 2 |
+ two |
+
+
+
+
+
+
diff --git a/lib/models/state.js b/lib/models/state.js
index 747d72aab..5b23d7360 100644
--- a/lib/models/state.js
+++ b/lib/models/state.js
@@ -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)
}
/**