` element. If some of its characters are also italic, it might be split up into multiple elements—one `wo ` and one `rd `.
diff --git a/docs/walkthroughs/adding-event-handlers.md b/docs/walkthroughs/adding-event-handlers.md
index f84dfb444..240d7c8c7 100644
--- a/docs/walkthroughs/adding-event-handlers.md
+++ b/docs/walkthroughs/adding-event-handlers.md
@@ -6,11 +6,11 @@
Okay, so you've got Slate installed and rendered on the page, and when you type in it, you can see the changes reflected. But you want to do more than just type a plaintext string.
-What makes Slate great is how easy it is to customize. Just like other React components you're used to, Slate allows you to pass in handlers that are triggered on certain events. You've already seen on the `onChange` handler can be used to store the changed editor value, but let's try add something more...
+What makes Slate great is how easy it is to customize. Just like other React components you're used to, Slate allows you to pass in handlers that are triggered on certain events. You've already seen how the `onChange` handler can be used to store the changed editor value, but let's try adding more...
-We'll show you how to use the `onKeyDown` handler to change the editor's content when the user presses a button.
+Let's use the `onKeyDown` handler to change the editor's content when we press a key.
-So we start with our app from earlier:
+Here's our app from earlier:
```js
class App extends React.Component {
@@ -28,7 +28,7 @@ class App extends React.Component {
}
```
-And now we'll add an `onKeyDown` handler:
+Now we add an `onKeyDown` handler:
```js
class App extends React.Component {
@@ -57,9 +57,9 @@ class App extends React.Component {
}
```
-Okay cool, so now when you press a key in the editor, you'll see the key's code printed to the console. Not very useful, but at least we know it's working.
+Cool, now when a key is pressed in the editor, its corresponding keycode is logged in the console.
-Now we want to make it actually change the content. For the purposes of our example, let's say we want to make it so that whenever a user types `&` we actually add `and` to the content.
+Now we want to make it actually change the content. For the purposes of our example, let's implement turning all ampersand, `&`, keystrokes into the word `and` upon being typed.
Our `onKeyDown` handler might look like this:
@@ -74,13 +74,13 @@ class App extends React.Component {
}
onKeyDown = (event, change) => {
- // Return with no changes if it's not the "&" key.
- if (event.key != '&') return
+ // Return with no changes if the keypress is not '&'
+ if (event.key !== '&') return
// Prevent the ampersand character from being inserted.
event.preventDefault()
- // Change the value by inserting "and" at the cursor's position.
+ // Change the value by inserting 'and' at the cursor's position.
change.insertText('and')
return true
}
@@ -97,9 +97,9 @@ class App extends React.Component {
}
```
-With that added, try typing `&`, and you should see it automatically become `and` instead!
+With that added, try typing `&`, and you should see it suddenly become `and` instead!
-That gives you a sense for what you can do with Slate's event handlers. Each one will be called with the `event` object, and a `change` object that lets you perform changes to the editor's value. Simple!
+This offers a sense of what can be done with Slate's event handlers. Each one will be called with the `event` object, and a `change` object that lets you perform changes to the editor's value. Simple!
Next: Defining Custom Block Nodes
diff --git a/docs/walkthroughs/saving-and-loading-html-content.md b/docs/walkthroughs/saving-and-loading-html-content.md
index 34baadeed..2f1f8d91f 100644
--- a/docs/walkthroughs/saving-and-loading-html-content.md
+++ b/docs/walkthroughs/saving-and-loading-html-content.md
@@ -268,13 +268,14 @@ class App extends React.Component {
// Add a `renderMark` method to render marks.
renderMark = props => {
- switch (props.mark.type) {
+ const { mark, attributes } = props
+ switch (mark.type) {
case 'bold':
- return {props.children}
+ return {props.children}
case 'italic':
- return {props.children}
+ return {props.children}
case 'underline':
- return {props.children}
+ return {props.children}
}
}
}
diff --git a/docs/walkthroughs/using-plugins.md b/docs/walkthroughs/using-plugins.md
index a43c3b096..b54f3dada 100644
--- a/docs/walkthroughs/using-plugins.md
+++ b/docs/walkthroughs/using-plugins.md
@@ -43,7 +43,7 @@ class App extends React.Component {
renderMark = props => {
switch (props.mark.type) {
case 'bold':
- return {props.children}
+ return {props.children}
}
}
}
diff --git a/examples/code-highlighting/index.js b/examples/code-highlighting/index.js
index 64c8929bc..d24b50e00 100644
--- a/examples/code-highlighting/index.js
+++ b/examples/code-highlighting/index.js
@@ -136,16 +136,32 @@ class CodeHighlighting extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'comment':
- return {children}
+ return (
+
+ {children}
+
+ )
case 'keyword':
- return {children}
+ return (
+
+ {children}
+
+ )
case 'tag':
- return {children}
+ return (
+
+ {children}
+
+ )
case 'punctuation':
- return {children}
+ return (
+
+ {children}
+
+ )
}
}
diff --git a/examples/hovering-menu/index.js b/examples/hovering-menu/index.js
index fb5a50533..e659e6c43 100644
--- a/examples/hovering-menu/index.js
+++ b/examples/hovering-menu/index.js
@@ -187,16 +187,16 @@ class HoveringMenu extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
}
}
}
diff --git a/examples/huge-document/index.js b/examples/huge-document/index.js
index b4993f821..a0817da3b 100644
--- a/examples/huge-document/index.js
+++ b/examples/huge-document/index.js
@@ -110,16 +110,16 @@ class HugeDocument extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
}
}
}
diff --git a/examples/markdown-preview/index.js b/examples/markdown-preview/index.js
index 9bd7812a0..a86f14a91 100644
--- a/examples/markdown-preview/index.js
+++ b/examples/markdown-preview/index.js
@@ -69,19 +69,20 @@ class MarkdownPreview extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
case 'title': {
return (
{children}
+ return (
+
+ {children}
+
+ )
}
case 'list': {
return (
{
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
}
}
}
diff --git a/examples/rich-text/index.js b/examples/rich-text/index.js
index c0a0c5f69..b158cab4d 100644
--- a/examples/rich-text/index.js
+++ b/examples/rich-text/index.js
@@ -302,16 +302,16 @@ class RichTextExample extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
}
}
}
diff --git a/examples/search-highlighting/index.js b/examples/search-highlighting/index.js
index 684da78b1..c7cf59a1a 100644
--- a/examples/search-highlighting/index.js
+++ b/examples/search-highlighting/index.js
@@ -140,10 +140,14 @@ class SearchHighlighting extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'highlight':
- return {children}
+ return (
+
+ {children}
+
+ )
}
}
}
diff --git a/examples/syncing-operations/index.js b/examples/syncing-operations/index.js
index b7c7fd8c3..611eac221 100644
--- a/examples/syncing-operations/index.js
+++ b/examples/syncing-operations/index.js
@@ -197,16 +197,16 @@ class SyncingEditor extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
case 'code':
- return {children}
+ return {children}
case 'italic':
- return {children}
+ return {children}
case 'underlined':
- return {children}
+ return {children}
}
}
}
diff --git a/examples/tables/index.js b/examples/tables/index.js
index e8a61e7df..45145b3f4 100644
--- a/examples/tables/index.js
+++ b/examples/tables/index.js
@@ -160,10 +160,10 @@ class Tables extends React.Component {
*/
renderMark = props => {
- const { children, mark } = props
+ const { children, mark, attributes } = props
switch (mark.type) {
case 'bold':
- return {children}
+ return {children}
}
}
}
diff --git a/packages/slate-react/package.json b/packages/slate-react/package.json
index d0e07b4df..ef5a49511 100644
--- a/packages/slate-react/package.json
+++ b/packages/slate-react/package.json
@@ -13,7 +13,7 @@
"lib/"
],
"dependencies": {
- "debug": "^2.3.2",
+ "debug": "^3.1.0",
"get-window": "^1.1.1",
"is-window": "^1.0.2",
"keycode": "^2.1.2",
diff --git a/packages/slate-react/src/components/leaf.js b/packages/slate-react/src/components/leaf.js
index 339c9bb5c..e2b8e3f40 100644
--- a/packages/slate-react/src/components/leaf.js
+++ b/packages/slate-react/src/components/leaf.js
@@ -99,9 +99,21 @@ class Leaf extends React.Component {
const { marks, node, offset, text, editor } = this.props
const { stack } = editor
const leaf = this.renderText()
+ const attributes = {
+ 'data-slate-leaf': true,
+ }
return marks.reduce((children, mark) => {
- const props = { editor, mark, marks, node, offset, text, children }
+ const props = {
+ editor,
+ mark,
+ marks,
+ node,
+ offset,
+ text,
+ children,
+ attributes,
+ }
const element = stack.find('renderMark', props)
return element || children
}, leaf)
diff --git a/packages/slate-react/src/plugins/after.js b/packages/slate-react/src/plugins/after.js
index 9524a4c03..2bb32c148 100644
--- a/packages/slate-react/src/plugins/after.js
+++ b/packages/slate-react/src/plugins/after.js
@@ -305,8 +305,8 @@ function AfterPlugin() {
// Get the selection point.
const native = window.getSelection()
- const { anchorNode, anchorOffset } = native
- const point = findPoint(anchorNode, anchorOffset, value)
+ const { anchorNode } = native
+ const point = findPoint(anchorNode, 0, value)
if (!point) return
// Get the text node and leaf in question.
@@ -323,7 +323,7 @@ function AfterPlugin() {
leaves.find(r => {
start = end
end += r.text.length
- if (end >= point.offset) return true
+ if (end > point.offset) return true
}) || lastLeaf
// Get the text information.
diff --git a/packages/slate-react/test/rendering/fixtures/custom-decorator.js b/packages/slate-react/test/rendering/fixtures/custom-decorator.js
index 39c003267..467eabae1 100644
--- a/packages/slate-react/test/rendering/fixtures/custom-decorator.js
+++ b/packages/slate-react/test/rendering/fixtures/custom-decorator.js
@@ -17,7 +17,7 @@ function decorateNode(block) {
}
function Bold(props) {
- return React.createElement('strong', null, props.children)
+ return React.createElement('strong', { ...props.attributes }, props.children)
}
function renderMark(props) {
@@ -45,7 +45,7 @@ export const output = `
o
- n
+ n
e
diff --git a/packages/slate-react/test/rendering/fixtures/custom-mark.js b/packages/slate-react/test/rendering/fixtures/custom-mark.js
index c76464a9c..7dca0c526 100644
--- a/packages/slate-react/test/rendering/fixtures/custom-mark.js
+++ b/packages/slate-react/test/rendering/fixtures/custom-mark.js
@@ -4,7 +4,7 @@ import React from 'react'
import h from '../../helpers/h'
function Bold(props) {
- return React.createElement('strong', null, props.children)
+ return React.createElement('strong', { ...props.attributes }, props.children)
}
function renderMark(props) {
@@ -33,7 +33,7 @@ export const output = `
one
- two
+ two
three
diff --git a/packages/slate/package.json b/packages/slate/package.json
index c31f9d211..ac68e57bd 100644
--- a/packages/slate/package.json
+++ b/packages/slate/package.json
@@ -13,7 +13,7 @@
"lib/"
],
"dependencies": {
- "debug": "^2.3.2",
+ "debug": "^3.1.0",
"direction": "^0.1.5",
"esrever": "^0.2.0",
"is-empty": "^1.0.0",
diff --git a/packages/slate/src/changes/at-current-range.js b/packages/slate/src/changes/at-current-range.js
index 7107b244d..0fc894638 100644
--- a/packages/slate/src/changes/at-current-range.js
+++ b/packages/slate/src/changes/at-current-range.js
@@ -40,6 +40,11 @@ PROXY_TRANSFORMS.forEach(method => {
const { selection } = value
const methodAtRange = `${method}AtRange`
change[methodAtRange](selection, ...args)
+ if (method.match(/Backward$/)) {
+ change.collapseToStart()
+ } else if (method.match(/Forward$/)) {
+ change.collapseToEnd()
+ }
}
})
@@ -217,8 +222,12 @@ Changes.insertText = (change, text, marks) => {
Changes.splitBlock = (change, depth = 1) => {
const { value } = change
- const { selection } = value
+ const { selection, document } = value
+ const marks = selection.marks || document.getInsertMarksAtRange(selection)
change.splitBlockAtRange(selection, depth).collapseToEnd()
+ if (marks && marks.size !== 0) {
+ change.select({ marks })
+ }
}
/**
diff --git a/packages/slate/src/changes/at-range.js b/packages/slate/src/changes/at-range.js
index 28f7c85e1..5d429b90d 100644
--- a/packages/slate/src/changes/at-range.js
+++ b/packages/slate/src/changes/at-range.js
@@ -671,7 +671,11 @@ Changes.insertFragmentAtRange = (change, range, fragment, options = {}) => {
// If the range is expanded, delete it first.
if (range.isExpanded) {
change.deleteAtRange(range, { normalize: false })
- range = range.collapseToStart()
+ if (change.value.document.getDescendant(range.startKey)) {
+ range = range.collapseToStart()
+ } else {
+ range = range.collapseTo(range.endKey, 0)
+ }
}
// If the fragment is empty, there's nothing to do after deleting.
@@ -988,12 +992,7 @@ Changes.setInlineAtRange = (...args) => {
Changes.splitBlockAtRange = (change, range, height = 1, options = {}) => {
const normalize = change.getFlag('normalize', options)
- if (range.isExpanded) {
- change.deleteAtRange(range, { normalize })
- range = range.collapseToStart()
- }
-
- const { startKey, startOffset } = range
+ const { startKey, startOffset, endOffset, endKey } = range
const { value } = change
const { document } = value
let node = document.assertDescendant(startKey)
@@ -1006,7 +1005,19 @@ Changes.splitBlockAtRange = (change, range, height = 1, options = {}) => {
h++
}
- change.splitDescendantsByKey(node.key, startKey, startOffset, { normalize })
+ change.splitDescendantsByKey(node.key, startKey, startOffset, {
+ normalize: normalize && range.isCollapsed,
+ })
+
+ if (range.isExpanded) {
+ if (range.isBackward) range = range.flip()
+ const nextBlock = change.value.document.getNextBlock(node.key)
+ range = range.moveAnchorToStartOf(nextBlock)
+ if (startKey === endKey) {
+ range = range.moveFocusTo(range.anchorKey, endOffset - startOffset)
+ }
+ change.deleteAtRange(range, { normalize })
+ }
}
/**
diff --git a/packages/slate/src/changes/with-schema.js b/packages/slate/src/changes/with-schema.js
index 4e6368742..91aeca0ef 100644
--- a/packages/slate/src/changes/with-schema.js
+++ b/packages/slate/src/changes/with-schema.js
@@ -47,7 +47,9 @@ Changes.normalizeNodeByKey = (change, key) => {
if (!ancestors) return
ancestors.forEach(ancestor => {
- normalizeNode(change, ancestor, schema)
+ if (change.value.document.getDescendant(ancestor.key)) {
+ normalizeNode(change, ancestor, schema)
+ }
})
}
diff --git a/packages/slate/test/changes/at-current-range/delete-forward/start-text-middle-inline.js b/packages/slate/test/changes/at-current-range/delete-forward/start-text-middle-inline.js
index bd536ff9b..8a038852e 100644
--- a/packages/slate/test/changes/at-current-range/delete-forward/start-text-middle-inline.js
+++ b/packages/slate/test/changes/at-current-range/delete-forward/start-text-middle-inline.js
@@ -23,9 +23,8 @@ export const output = (
-
- wo
+ wo
diff --git a/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js b/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js
new file mode 100644
index 000000000..e75eae19e
--- /dev/null
+++ b/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js
@@ -0,0 +1,44 @@
+/** @jsx h */
+
+import h from '../../../helpers/h'
+
+const fragment = (
+
+ fragment zero
+ fragment one
+ fragment two
+
+)
+export default function(change) {
+ change.insertFragment(fragment)
+}
+
+export const input = (
+
+
+ zero
+
+ one
+
+
+ two
+
+
+
+)
+
+// The cursor position of insertFragment has some problems;
+// If you submit PR to fixed cursor position restore in insertFragment;
+// Please change this test as well
+export const output = (
+
+
+ zero
+ fragment zero
+ fragment one
+
+ fragment twotwo
+
+
+
+)
diff --git a/packages/slate/test/changes/at-current-range/split-block/with-delete-hanging-selection.js b/packages/slate/test/changes/at-current-range/split-block/with-delete-hanging-selection.js
new file mode 100644
index 000000000..96f269c1d
--- /dev/null
+++ b/packages/slate/test/changes/at-current-range/split-block/with-delete-hanging-selection.js
@@ -0,0 +1,33 @@
+/** @jsx h */
+
+import h from '../../../helpers/h'
+
+export default function(change) {
+ change.splitBlock()
+}
+
+export const input = (
+
+
+ zero
+
+ word
+
+
+ cat is cute
+
+
+
+)
+
+export const output = (
+
+
+ zero
+
+
+ cat is cute
+
+
+
+)
diff --git a/packages/slate/test/changes/at-current-range/split-block/with-marks.js b/packages/slate/test/changes/at-current-range/split-block/with-marks.js
new file mode 100644
index 000000000..e9f808591
--- /dev/null
+++ b/packages/slate/test/changes/at-current-range/split-block/with-marks.js
@@ -0,0 +1,38 @@
+/** @jsx h */
+
+import h from '../../../helpers/h'
+
+export default function(change) {
+ change
+ .addMark('italic')
+ .splitBlock()
+ .insertText('cat is cute')
+}
+
+export const input = (
+
+
+
+ word
+
+
+
+
+)
+
+export const output = (
+
+
+
+ word
+
+
+
+
+ cat is cute
+
+
+
+
+
+)
diff --git a/yarn.lock b/yarn.lock
index d0be67aea..7efb30d0f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2326,7 +2326,7 @@ debug@2.2.0:
dependencies:
ms "0.7.1"
-debug@2.6.9, debug@^2.3.2, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
+debug@2.6.9, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies: