diff --git a/.eslintrc b/.eslintrc index 8ca14f48b..112e4cae2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,7 @@ }, "env": { "browser": true, + "es6": true, "mocha": true, "node": true }, diff --git a/docs/Readme.md b/docs/Readme.md index 81f41b995..c790b8848 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -30,7 +30,7 @@ ## Slate Core * [Block](./reference/slate/block.md) -* [Change](./reference/slate/change.md) +* [Commands](./reference/slate/commands.md) * [Data](./reference/slate/data.md) * [Decoration](./reference/slate/decoration.md) * [Document](./reference/slate/document.md) diff --git a/docs/guides/commands-and-queries.md b/docs/guides/commands-and-queries.md index 8ae07ae8d..27262c0c7 100644 --- a/docs/guides/commands-and-queries.md +++ b/docs/guides/commands-and-queries.md @@ -1,6 +1,6 @@ # Commands & Queries -All commands to a Slate editor's value, whether it's the `selection`, `document`, `history`, etc. happen via "commands" that are applied to a [`Change`](../reference/slate/change.md). +All commands to a Slate editor's value, whether it's the `selection`, `document`, `history`, etc. happen via "commands". Under the covers, Slate takes care of converting each command into a set of low-level [operations](../reference/slate/operation.md) that are applied to produce a new value. This is what makes collaborative editing implementations possible. But you don't have to worry about that, because it happens automatically. @@ -17,7 +17,7 @@ And if the API for commands was verbose, or if it required lots of in between st To solve this, Slate has very expressive, chainable commands. Like this: ```js -change +editor .focus() .moveToRangeOfDocument() .delete() @@ -85,30 +85,29 @@ When you decide you want to make a change to the Slate value, you're almost alwa ### 1. In Slate Handlers -The first place, is inside a Slate-controlled event handler, like `onKeyDown` or `onPaste`. These handlers take a signature of `event, change, next`. That `change` argument is a [`Change`](../reference/slate/change.md) object that you can manipulate. For example... +The first place, is inside a Slate-controlled event handler, like `onKeyDown` or `onPaste`. These handlers take a signature of `event, editor, next`. For example... ```js -function onKeyDown(event, change, next) { +function onKeyDown(event, editor, next) { if (event.key == 'Enter') { - change.splitBlock() + editor.splitBlock() + } else { + return next() } } ``` -Any commands you call will be applied, and when the event handler stack is finished resolving, the editor will automatically update with those commands. +Any commands you call will be applied immediately to the `editor` object, and flushed to the `onChange` handler on the next tick. ### 2. From Custom Node Components -The second place is inside a custom node component. For example, you might have an `` component and you want to make a change when the image is clicked. - -In that case, you'll need to use the `change()` method on the Slate [``](../reference/slate-react/editor.md) which you have available as `props.editor`. For example... +The second place is inside a custom node component. For example, you might have an `` component and you want to make a change when the image is clicked. For example... ```js class Image extends React.Component { onClick = event => { - this.props.editor.change(change => { - change.removeNodeByKey(this.props.node.key) - }) + const { editor, node } = this.props + editor.removeNodeByKey(node.key) } render() { @@ -117,7 +116,7 @@ class Image extends React.Component { } ``` -The `editor.change()` method will create a new [`Change`](../reference/slate/change.md) object for you, based on the editor's current plugins and value. You can then call any commands you want, and the new value will be applied to the editor. +The `` node component will be passed the `editor` and the `node` it represents as props, so you can use these to trigger commands. ### 3. From Schema Rules @@ -130,9 +129,9 @@ The third place you may perform change operations—for more complex use cases nodes: [{ match: { type: 'item' } }], - normalize: (change, error) => { + normalize: (editor, error) => { if (error.code == 'child_type_invalid') { - change.wrapBlockByKey(error.child.key, 'item') + editor.wrapBlockByKey(error.child.key, 'item') } } } @@ -140,7 +139,7 @@ The third place you may perform change operations—for more complex use cases } ``` -When a rule's validation fails, Slate passes a [`Change`](../reference/slate/change.md) object to the `normalize` function of the rule, if one exists. You can use this object to apply the commands necessary to make your document valid on the next normalization pass. +When a rule's validation fails, Slate passes the editor to the `normalize` function of the rule, if one exists. You can use these normalizing funcrtions to apply the commands necessary to make your document valid on the next normalization pass. ### 4. From Outside Slate @@ -148,25 +147,28 @@ The last place is from outside of Slate. Sometimes you'll have components that l editor in the render tree, and you'll need to explicitly pass them a reference to the Slate `editor` to run changes. In React you do this with the `ref={}` prop... ```js - + this.editor = editor} + ... +/> ``` -Which gives you a reference to the Slate editor. And from there you can use the same `editor.change` syntax from above to apply changes. +Which gives you a reference to the Slate editor. And from there you can use the same commands syntax from above to apply changes. ## Running Queries Queries are similar to commands, but instead of manipulating the current value of the editor, they return information about the current value, or a specific node, etc. -By default, Slate only defines two queries: `isAtomic` for marks and decorations, and `isVoid` for nodes. You can access them directly on the change object: +By default, Slate only defines two queries: `isAtomic` for marks and decorations, and `isVoid` for nodes. You can access them directly on the editor: ```js -const isVoid = change.isVoid(node) +const isVoid = editor.isVoid(node) ``` But you can also define your own queries that are specific to your schema. For example, you might use a query to determine whether the "bold" mark is active... ```js -const isBold = change.isBoldActive(value) +const isBold = editor.isBoldActive(value) ``` And then use that information to mark the bold button in your editor's toolbar as active or not. @@ -175,17 +177,17 @@ And then use that information to mark the bold button in your editor' In addition to using the built-in commands, if your editor is of any complexity you'll want to write your own reusable commands. That way, you can reuse a single `insertImage` change instead of constantly writing `insertBlock(...args)`. -To do that, you can define commands in your own Slate plugin, which will be made available as methods on the `change` object specific to your editor. For example, here are two simple block inserting commands... +To do that, you can define commands in your own Slate plugin, which will be made available as methods on the `editor` object. For example, here are two simple block inserting commands... ```js const yourPlugin = { commands: { - insertParagraph(change) { - change.insertBlock('paragraph') + insertParagraph(editor) { + editor.insertBlock('paragraph') }, - insertImage(change, src) { - change.insertBlock({ + insertImage(editor, src) { + editor.insertBlock({ type: 'image', data: { src }, }) @@ -197,7 +199,7 @@ const yourPlugin = { Notice how rewriting that image inserting logic multiple times without having it encapsulated in a single function would get tedious. Now with those change functions defined, you can reuse them! ```js -change.insertImage('https://google.com/logo.png') +editor.insertImage('https://google.com/logo.png') ``` And any arguments you pass in are sent to your custom command functions. @@ -214,10 +216,10 @@ const yourPlugin = { } ``` -And then you can use them: +And then you can use them right from the `editor` instance: ```js -change.getActiveListItem() +editor.getActiveListItem() ``` This reusability is key to being able to organize your commands and queries, and compose them together to create more advanced behaviors. diff --git a/docs/guides/plugins.md b/docs/guides/plugins.md index 60cdb5947..21bf9dcf3 100644 --- a/docs/guides/plugins.md +++ b/docs/guides/plugins.md @@ -17,16 +17,18 @@ So a basic plugin might look like this: ```js export default function MySlatePlugin(options) { return { - onKeyDown(event, change, next) { + onKeyDown(event, editor, next) { if (event.key == options.key) { - change.blur() - return true + editor.blur() + } else { + return next() } }, - onClick(event, change, next) { - if (change.value.selection.isBlurred) { - change.moveToRangeOfDocument().focus() - return true + onClick(event, editor, next) { + if (editor.value.selection.isBlurred) { + editor.moveToRangeOfDocument().focus() + } else { + return next() } }, } @@ -37,8 +39,6 @@ It focuses the editor and selects everything when it is clicked, and it blurs th Notice how it's able to define a set of behaviors, reacting to different events, that work together to form a single "feature" in the editor. That's what makes Slate's plugins a powerful form of encapsulation. -Also notice how it returns `true`. This is a convention that allows other plugins in the stack to know that an event has been "handled" and that they can abort without duplicating logic. - ## The Plugins "Stack" Slate's editor takes a list of plugins as one of its arguments. We refer to this list as the plugins "stack". It is very similar to "middleware" from Express or Koa, except instead of just a single stack of handler functions, there are multiple stacks for each type of request. @@ -120,9 +120,11 @@ For example, you may have a simple `Hotkey` plugin that makes binding behaviors ```js function Hotkey(hotkey, fn) { return { - onKeyDown(event, change, editor) { + onKeyDown(event, editor, next) { if (isHotkey(hotkey, event)) { - change.call(fn) + editor.command(fn) + } else { + return next() } }, } @@ -134,7 +136,7 @@ That pseudo-code allows you to encapsulate the hotkey-binding logic in one place ```js const plugins = [ ..., - Hotkey('cmd+b', change => change.addMark('bold')), + Hotkey('cmd+b', editor => editor.addMark('bold')), ] ``` diff --git a/docs/guides/rendering.md b/docs/guides/rendering.md index 336ea0ef6..cfe6b9db7 100644 --- a/docs/guides/rendering.md +++ b/docs/guides/rendering.md @@ -13,7 +13,7 @@ Using custom components for the nodes and marks is the most common rendering nee The function is called with the node's props, including `props.node` which is the node itself. You can use these to determine what to render. For example, you can render nodes using simple HTML elements: ```js -function renderNode(props, next) { +function renderNode(props, editor, next) { const { node, attributes, children } = props switch (node.type) { @@ -36,7 +36,7 @@ function renderNode(props, next) { You don't have to use simple HTML elements, you can use your own custom React components too: ```js -function renderNode(props, next) { +function renderNode(props, editor, next) { switch (props.node.type) { case 'paragraph': return @@ -55,7 +55,7 @@ And you can just as easily put that `renderNode` logic into a plugin, and pass t ```js function SomeRenderingPlugin() { return { - renderNode(props, next) { + renderNode(props, editor, next) { ... } } @@ -75,7 +75,7 @@ const plugins = [ Marks work the same way, except they invoke the `renderMark` function. Like so: ```js -function renderMark(props, next) { +function renderMark(props, editor, next) { const { children, mark, attributes } = props switch (mark.type) { case 'bold': @@ -107,8 +107,8 @@ By default Slate will render a placeholder for you which mimics the native DOM ` However sometimes you want to customize things. Or maybe you want to render placeholders inside specific blocks like inside an image caption. To do that, you can define your own `renderPlaceholder` function: ```js -function renderPlaceholder(props, next) { - const { node, editor } = props +function renderPlaceholder(props, editor, next) { + const { node } = props if (node.object != 'block') return next() if (node.type != 'caption') return next() if (node.text != '') return next() @@ -138,7 +138,7 @@ Not only can you control the rendering behavior of the components inside the edi This sounds weird, but it can be pretty useful if you want to render additional top-level elements from inside a plugin. To do so, you use the `renderEditor` function: ```js -function renderEditor(props, next) { +function renderEditor(props, editor, next) { const { editor } = props const wordCount = countWords(editor.value.text) const children = next() diff --git a/docs/guides/schemas.md b/docs/guides/schemas.md index 2c28d28ff..6eae3a9ce 100644 --- a/docs/guides/schemas.md +++ b/docs/guides/schemas.md @@ -73,9 +73,9 @@ const schema = { nodes: [{ match: [{ type: 'paragraph' }, { type: 'image' }], }], - normalize: (change, error) => { + normalize: (editor, error) => { if (error.code == 'child_type_invalid') { - change.setNodeByKey(error.child.key, { type: 'paragraph' }) + editor.setNodeByKey(error.child.key, { type: 'paragraph' }) } } }, @@ -100,13 +100,13 @@ Sometimes though, the declarative validation syntax isn't fine-grained enough to When you define a `normalizeNode` function, you either return nothing if the node's already valid, or you return a normalizer function that will make the node valid if it isn't. Here's an example: ```js -function normalizeNode(node, next) { +function normalizeNode(node, editor, next) { const { nodes } = node if (node.object !== 'block') return next() if (nodes.size !== 3) return next() if (nodes.first().object !== 'text') return next() if (nodes.last().object !== 'text') return next() - return change => change.removeNodeByKey(node.key) + return () => editor.removeNodeByKey(node.key) } ``` diff --git a/docs/reference/slate-react/editor.md b/docs/reference/slate-react/editor.md index 8427912f9..fc13a484d 100644 --- a/docs/reference/slate-react/editor.md +++ b/docs/reference/slate-react/editor.md @@ -159,64 +159,3 @@ const plugins = [ ### `schema` To see how these properties behave, check out the [Plugins reference](./plugins.md). - -## Instance Methods - -### `blur` - -`blur() => Void` - -Programmatically blur the editor. - -### `change` - -`change(fn) => Void` -`change(fn, ...args) => Void` - -Programmatically invoke a change `fn` on the editor. The function will be invoked with a new `change` object representing the editor's current value. - -If extra `...args` are passed in, the change `fn` will be invoked with `(change, ...args)`, so you can use this as a shorthand for performing single-function changes. - -### `command` - -`command(name, ...args) => Void` - -Invoke a command by `name` on the editor with `args`. - -### `event` - -`event(handler, event, ...args) => Any` - -Programmatically invoke an `event` on the editor. This isn't something you should normally use except in test environments. - -### `focus` - -`focus() => Void` - -Programmatically focus the editor. - -### `query` - -`query(name, ...args) => Any` - -Invoke a query by `name` on the editor with `args`, returning its result. - -### `run` - -`run(key, ...args) => Any` - -Run the middleware stack by `key` with `args`, returning its result. - -## Instance Properties - -### `readOnly` - -`Boolean` - -Whether the editor is currently read-only or not. - -### `value` - -`Value` - -The editor's current value. diff --git a/docs/reference/slate-react/plugins.md b/docs/reference/slate-react/plugins.md index 9e4541498..316f9e79e 100644 --- a/docs/reference/slate-react/plugins.md +++ b/docs/reference/slate-react/plugins.md @@ -29,19 +29,19 @@ In addition to the [core plugin hooks](../slate/plugins.md), when using `slate-r } ``` -The event hooks have a signature of `(event, change, next)`—the `event` is a React object that you are used to from React's event handlers. +The event hooks have a signature of `(event, editor, next)`—the `event` is a React object that you are used to from React's event handlers. -The rendering hooks are just like render props common to other React API's, and receive `(props, next)`. For more information, see the [Rendering](./rendering.md) reference. +The rendering hooks are just like render props common to other React API's, and receive `(props, editor, next)`. For more information, see the [Rendering](./rendering.md) reference. ### `decorateNode` -`Function decorateNode(node: Node, next: Function) => Array|Void` +`Function decorateNode(node: Node, editor: Editor, next: Function) => Array|Void` The `decorateNode` hook takes a `node` and returns an array of decorations with marks to be applied to the node when it is rendered. ### `onBeforeInput` -`Function onBeforeInput(event: Event, change: Change, next: Function) => Boolean` +`Function onBeforeInput(event: Event, editor: Editor, next: Function) => Boolean` This handler is called right before a string of text is inserted into the `contenteditable` element. @@ -49,37 +49,37 @@ Make sure to `event.preventDefault()` if you do not want the default insertion b ### `onBlur` -`Function onBlur(event: Event, change: Change, next: Function) => Boolean` +`Function onBlur(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when the editor's `contenteditable` element is blurred. ### `onFocus` -`Function onFocus(event: Event, change: Change, next: Function) => Boolean` +`Function onFocus(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when the editor's `contenteditable` element is focused. ### `onCopy` -`Function onCopy(event: Event, change: Change, next: Function) => Boolean` +`Function onCopy(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when there is a copy event in the editor's `contenteditable` element. ### `onCut` -`Function onCut(event: Event, change: Change, next: Function) => Boolean` +`Function onCut(event: Event, editor: Editor, next: Function) => Boolean` This handler is equivalent to the `onCopy` handler. ### `onDrop` -`Function onDrop(event: Event, change: Change, next: Function) => Boolean` +`Function onDrop(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when the user drops content into the `contenteditable` element. The event is already prevented by default, so you must define a value change to have any affect occur. ### `onKeyDown` -`Function onKeyDown(event: Event, change: Change, next: Function) => Boolean` +`Function onKeyDown(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when any key is pressed in the `contenteditable` element, before any action is taken. @@ -87,19 +87,19 @@ Make sure to `event.preventDefault()` if you do not want the default insertion b ### `onKeyUp` -`Function onKeyUp(event: Event, change: Change, next: Function) => Boolean` +`Function onKeyUp(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when any key is released in the `contenteditable` element. ### `onPaste` -`Function onPaste(event: Event, change: Change, next: Function) => Boolean` +`Function onPaste(event: Event, editor: Editor, next: Function) => Boolean` This handler is called when the user pastes content into the `contenteditable` element. The event is already prevented by default, so you must define a value change to have any affect occur. ### `onSelect` -`Function onSelect(event: Event, change: Change, next: Function) => Boolean` +`Function onSelect(event: Event, editor: Editor, next: Function) => Boolean` This handler is called whenever the native DOM selection changes. @@ -107,13 +107,13 @@ This handler is called whenever the native DOM selection changes. ### `renderEditor` -`Function renderEditor(props: Object, next: Function) => ReactNode|Void` +`Function renderEditor(props: Object, editor: Editor, next: Function) => ReactNode|Void` The `renderEditor` property allows you to define higher-order-component-like behavior. It is passed all of the properties of the editor, including `props.children`. You can then choose to wrap the existing `children` in any custom elements or proxy the properties however you choose. This can be useful for rendering toolbars, styling the editor, rendering validation, etc. Remember that the `renderEditor` function has to render `props.children` for editor's content to render. ### `renderMark` -`Function renderMark(props: Object, next: Function) => ReactNode|Void` +`Function renderMark(props: Object, editor: Editor, next: Function) => ReactNode|Void` Render a `Mark` with `props`. The `props` object contains: @@ -134,7 +134,7 @@ You must spread the `props.attributes` onto the top-level DOM node you use to re ### `renderNode` -`Function renderNode(props: Object, next: Function) => ReactNode|Void` +`Function renderNode(props: Object, editor: Editor, next: Function) => ReactNode|Void` Render a `Node` with `props`. The `props` object contains: @@ -155,7 +155,7 @@ You must spread the `props.attributes` onto the top-level DOM node you use to re ### `renderPlaceholder` -`Function renderPlaceholder(props: Object, next: Function) => ReactNode|Void` +`Function renderPlaceholder(props: Object, editor: Editor, next: Function) => ReactNode|Void` Render the placeholder that is shown when the editor has no `value`. The `props` object contains: @@ -170,6 +170,6 @@ The `placeholder` prop that was passed to the editor can be found at `editor.pro ### `shouldNodeComponentUpdate` -`Function shouldNodeComponentUpdate(previousProps: Object, props: Object, next: Function) => Boolean|Void` +`Function shouldNodeComponentUpdate(previousProps: Object, props: Object, editor: Editor, next: Function) => Boolean|Void` If this function returns `true`, it can force updating the node's component where otherwise it wouldn't for performance. diff --git a/docs/reference/slate-react/rendering.md b/docs/reference/slate-react/rendering.md index 4ab023a02..7a4fd0677 100644 --- a/docs/reference/slate-react/rendering.md +++ b/docs/reference/slate-react/rendering.md @@ -56,9 +56,7 @@ const value = editor.value ``` ```js -editor.change(change => { - change.moveToRangeOfDocument().delete() -}) +editor.moveToRangeOfDocument().delete() ``` ### `isSelected` diff --git a/docs/reference/slate-react/utils.md b/docs/reference/slate-react/utils.md index e8115f402..b926d491f 100644 --- a/docs/reference/slate-react/utils.md +++ b/docs/reference/slate-react/utils.md @@ -24,8 +24,7 @@ React-specific utility functions for Slate that may be useful in certain use cas During a cut or copy event, sets `fragment` as the Slate document fragment to be copied. ```js -function onCopy(event, change, next) { - const { editor } = change +function onCopy(event, editor, next) { const fragment = // ... create a fragment from a set of nodes ... if (fragment) { @@ -38,14 +37,13 @@ function onCopy(event, change, next) { Note that calling `cloneFragment` should be the last thing you do in your event handler. If you change the window selection after calling `cloneFragment`, the browser may copy the wrong content. If you need to perform an action after calling `cloneFragment`, wrap it in `requestAnimationFrame`: ```js -function onCut(event, change, next) { - const { editor } = change +function onCut(event, editor, next) { const fragment = // ... create a fragment from a set of nodes ... if (fragment) { cloneFragment(event, editor, fragment) window.requestAnimationFrame(() => { - editor.change(change => change.delete()) + editor.delete() }) return true } @@ -73,8 +71,8 @@ function componentDidUpdate() { Find the DOM range from a Slate [`Range`](../slate/range.md). ```js -function onChange(change) { - const { value } = change +function onChange(editor) { + const { value } = editor const range = findDOMRange(value.selection) // Do something with the DOM `range`... } @@ -119,7 +117,7 @@ function onSomeNativeEvent() { Get the affected Slate range from a DOM `event` and Slate `editor`. ```js -function onDrop(event, change, next) { +function onDrop(event, editor, next) { const targetRange = getEventRange(event, editor) // Do something at the drop `targetRange`... } @@ -132,7 +130,7 @@ function onDrop(event, change, next) { Get the Slate-related data from a DOM `event` and Slate `value`. ```js -function onDrop(event, change, next) { +function onDrop(event, editor, next) { const transfer = getEventTransfer(event) const { type, node } = transfer @@ -149,8 +147,8 @@ function onDrop(event, change, next) { Sets the Slate-related `data` with `type` on an `event`. The `type` must be one of the types Slate recognizes: `'fragment'`, `'html'`, `'node'`, `'rich'`, or `'text'`. ```js -function onDragStart(event, change, next) { - const { value } = change +function onDragStart(event, editor, next) { + const { value } = editor const { startNode } = value setEventTransfer(event, 'node', startNode) } diff --git a/docs/reference/slate/change.md b/docs/reference/slate/commands.md similarity index 63% rename from docs/reference/slate/change.md rename to docs/reference/slate/commands.md index b34a74a5e..c0d6651a3 100644 --- a/docs/reference/slate/change.md +++ b/docs/reference/slate/commands.md @@ -1,149 +1,40 @@ -# `Change` +# Commands -A change allows you to define a series of changes you'd like to make to the current [`Value`](./value.md). +The core `Editor` ships with a bunch of built-in commands that provide common behaviors for rich text editors. -All changes are performed through `Change` objects, so that a history of changes can be preserved for use in undo/redo operations, and to make collaborative editing possible. +## Current Selection Commands -## Properties - -### `editor` - -`Editor` - -A reference to the editor the change is being made in. - -### `object` - -`String` - -A string with a value of `'change'`. - -### `value` - -`Value` - -A [`Value`](./value.md) with the change's current operations applied. Each time you run a new change function this property will be updated. - -## Methods - -### `call` - -`call(fn: Function, ...args) => Change` - -This method calls the provided function with the current instance of the `Change` object as the first argument and passes through the remaining `args`. - -The purpose of `call` is to enable custom change methods to exist and called in a chain. For example: - -```js -function addBoldMark(change) { - change.addMark('bold_mark') -} - -function insertParagraph(change) { - change.insertBlock('paragraph_block') -} - -function onSomeEvent(event, change) { - change - .call(insertParagraph) - .insertText('Some text...') - .extendToStartOfBlock() - .call(addBoldMark) - .collapseToEnd() -} -``` - -### `normalize` - -`normalize() => Void` - -This method normalizes the document with the value's schema. This should run automatically-you should not need to call this method unless you have manually disabled normalization (and you should rarely, if ever, need to manually disable normalization). The vast majority of changes, whether by the user or invoked programmatically, will run `normalize` by default to ensure the document is always in adherence to its schema. - -> 🤖 If you must use this method, use it sparingly and strategically. Calling this method can be very expensive as it will run normalization on all of the nodes in your document. - -### `withoutNormalizing` - -`withoutNormalizing(fn: Function) => Change` - -This method calls the provided function with the current instance of the `Change` object as the first argument. Normalization does not occur while the fuction is executing, and is instead deferred to be be run immediately after it completes. - -This method can be used to allow a sequence of change operations that should not be interrupted by normalization. For example: - -```js -function removeManyNodes(node) { - const toRemove = node.nodes.filter(n => n.object != 'block') - if (!toRemove.size) return - - change.withoutNormalizing(() => { - toRemove.forEach(child => { - change.removeNodeByKey(child.key) - }) - }) -} -``` - -### `withoutSaving` - -`withoutSaving(fn: Function) => Change` - -By default all new operations are saved to the editor's history. If you have changes that you don't want to show up in the history when the user presses cmd+z, you can use `withoutSaving` to skip those changes. - -```js -change.withoutSaving(() => { - change.setValue({ decorations }) -}) -``` - -However, be sure you know what you are doing because this will create changes that cannot be undone by the user, and might result in confusing behaviors. - -### `withoutMerging` - -`withoutMerging(fn: Function) => Change` - -Usually, all of the operations in a `Change` are grouped into a single save point in the editor's history. However, sometimes you may want more control over this, to be able to create distinct save points in a single change. To do that, you can use the `withoutMerging` helper. - -## Full Value Change - -### `setValue` - -`setValue(properties: Object, [options: Object]) => Change`
-`setValue(value: Value, [options: Object]) => Change` (see warning below) - -Set the entire `value` using either a `properties` object or a `Value` object. Can be used to set `value.data` and other properties that cannot otherwise be easily set using the available methods. - -## Current Selection Changes - -These changes act on the `document` based on the current `selection`. They are equivalent to calling the [Document Range Changes](#document-range-changes) with the current selection as the `range` argument, but they are there for convenience, since you often want to act with the current selection, as a user would. +These commands act on the `document` based on the current `selection`. They are equivalent to calling the [Document Range Commands](#document-range-commands) with the current selection as the `range` argument, but they are there for convenience, since you often want to act with the current selection, as a user would. ### `addMark` -`addMark(mark: Mark) => Change`
-`addMark(properties: Object) => Change`
-`addMark(type: String) => Change` +`addMark(mark: Mark) => Editor`
+`addMark(properties: Object) => Editor`
+`addMark(type: String) => Editor` Add a [`mark`](./mark.md) to the characters in the current selection. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `delete` -`delete() => Change` +`delete() => Editor` Delete everything in the current selection. ### `insertBlock` -`insertBlock(block: Block) => Change`
-`insertBlock(properties: Object) => Change`
-`insertBlock(type: String) => Change` +`insertBlock(block: Block) => Editor`
+`insertBlock(properties: Object) => Editor`
+`insertBlock(type: String) => Editor` ### `deleteBackward` -`deleteBackward(n: Number) => Change` +`deleteBackward(n: Number) => Editor` Delete backward `n` characters at the current cursor. If the selection is expanded, this method is equivalent to a regular [`delete()`](#delete). `n` defaults to `1`. ### `deleteForward` -`deleteForward(n: Number) => Change` +`deleteForward(n: Number) => Editor` Delete forward `n` characters at the current cursor. If the selection is expanded, this method is equivalent to a regular [`delete()`](#delete). `n` defaults to `1`. @@ -151,375 +42,375 @@ Insert a new block at the same level as the current block, splitting the current ### `insertFragment` -`insertFragment(fragment: Document) => Change` +`insertFragment(fragment: Document) => Editor` Insert a [`fragment`](./document.md) at the current selection. If the selection is expanded, it will be deleted first. ### `insertInline` -`insertInline(inline: Inline) => Change`
-`insertInline(properties: Object) => Change` +`insertInline(inline: Inline) => Editor`
+`insertInline(properties: Object) => Editor` Insert a new inline at the current cursor position, splitting the text to make room if it is non-empty. If the selection is expanded, it will be deleted first. ### `insertText` -`insertText(text: String) => Change` +`insertText(text: String) => Editor` Insert a string of `text` at the current selection. If the selection is expanded, it will be deleted first. ### `setBlocks` -`setBlocks(properties: Object) => Change`
-`setBlocks(type: String) => Change` +`setBlocks(properties: Object) => Editor`
+`setBlocks(type: String) => Editor` Set the `properties` of the [`Blocks`](./block.md) in the current selection. For convenience, you can pass a `type` string to set the blocks' type only. ### `setInlines` -`setInlines(properties: Object) => Change`
-`setInlines(type: String) => Change` +`setInlines(properties: Object) => Editor`
+`setInlines(type: String) => Editor` Set the `properties` of the [`Inlines`](./inline.md) nodes in the current selection. For convenience, you can pass a `type` string to set the inline nodes' type only. ### `splitBlock` -`splitBlock(depth: Number) => Change` +`splitBlock(depth: Number) => Editor` Split the [`Block`](./block.md) in the current selection by `depth` levels. If the selection is expanded, it will be deleted first. `depth` defaults to `1`. ### `splitInline` -`splitInline(depth: Number) => Change` +`splitInline(depth: Number) => Editor` Split the [`Inline`](./inline.md) node in the current selection by `depth` levels. If the selection is expanded, it will be deleted first. `depth` defaults to `Infinity`. ### `removeMark` -`removeMark(mark: Mark) => Change`
-`removeMark(properties: Object) => Change`
-`removeMark(type: String) => Change` +`removeMark(mark: Mark) => Editor`
+`removeMark(properties: Object) => Editor`
+`removeMark(type: String) => Editor` Remove a [`mark`](./mark.md) from the characters in the current selection. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `replaceMark` -`replaceMark(oldMark: Mark, newMark: Mark) => Change`
-`replaceMark(oldProperties: Object, newProperties: Object) => Change`
-`replaceMark(oldType: String, newType: String) => Change` +`replaceMark(oldMark: Mark, newMark: Mark) => Editor`
+`replaceMark(oldProperties: Object, newProperties: Object) => Editor`
+`replaceMark(oldType: String, newType: String) => Editor` Replace a [`mark`](./mark.md) in the characters in the current selection. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `toggleMark` -`toggleMark(mark: Mark) => Change`
-`toggleMark(properties: Object) => Change`
-`toggleMark(type: String) => Change` +`toggleMark(mark: Mark) => Editor`
+`toggleMark(properties: Object) => Editor`
+`toggleMark(type: String) => Editor` Add or remove a [`mark`](./mark.md) from the characters in the current selection, depending on it already exists on any or not. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `unwrapBlock` -`unwrapBlock(type: String) => Change`
-`unwrapBlock(properties: Object) => Change`
+`unwrapBlock(type: String) => Editor`
+`unwrapBlock(properties: Object) => Editor`
Unwrap all [`Block`](./block.md) nodes in the current selection that match a `type` and/or `data`. ### `unwrapInline` -`unwrapInline(type: String) => Change`
-`unwrapInline(properties: Object) => Change`
+`unwrapInline(type: String) => Editor`
+`unwrapInline(properties: Object) => Editor`
Unwrap all [`Inline`](./inline.md) nodes in the current selection that match a `type` and/or `data`. ### `wrapBlock` -`wrapBlock(type: String) => Change`
-`wrapBlock(properties: Object) => Change`
+`wrapBlock(type: String) => Editor`
+`wrapBlock(properties: Object) => Editor`
Wrap the [`Block`](./block.md) nodes in the current selection with a new [`Block`](./block.md) node of `type`, with optional `data`. ### `wrapInline` -`wrapInline(type: String) => Change`
-`wrapInline(properties: Object) => Change`
+`wrapInline(type: String) => Editor`
+`wrapInline(properties: Object) => Editor`
Wrap the [`Inline`](./inline.md) nodes in the current selection with a new [`Inline`](./inline.md) node of `type`, with optional `data`. ### `wrapText` -`wrapText(prefix: String, [suffix: String]) => Change` +`wrapText(prefix: String, [suffix: String]) => Editor` Surround the text in the current selection with `prefix` and `suffix` strings. If the `suffix` is ommitted, the `prefix` will be used instead. -## Selection Changes +## Selection Commands -These changes change the current `selection`, without touching the `document`. +These commands change the current `selection`, without touching the `document`. ### `blur` -`blur() => Change` +`blur() => Editor` Blur the current selection. ### `deselect` -`deselect() => Change` +`deselect() => Editor` Unset the selection. ### `flip` -`flip() => Change` +`flip() => Editor` Flip the selection. ### `focus` -`focus() => Change` +`focus() => Editor` Focus the current selection. ### `move{Point}Backward` -`move{Point}Backward(n: Number) => Change` +`move{Point}Backward(n: Number) => Editor` Move the `{Point}` of the selection backward `n` characters. Where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}Forward` -`move{Point}Forward(n: Number) => Change` +`move{Point}Forward(n: Number) => Editor` Move the `{Point}` of the selection forward `n` characters. Where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To` -`moveTo(path: List, offset: Number) => Change` -`moveTo(key: String, offset: Number) => Change` -`moveTo(offset: Number) => Change` +`moveTo(path: List, offset: Number) => Editor` +`moveTo(key: String, offset: Number) => Editor` +`moveTo(offset: Number) => Editor` Move the `{Point}` of the selection to a new `path` or `key` and `offset`. Where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `moveTo{Point}` -`moveTo{Point}() => Change` +`moveTo{Point}() => Editor` Collapse the current selection to one of its points. Where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. ### `move{Point}To{Edge}OfBlock` -`move{Point}To{Edge}OfBlock() => Change` +`move{Point}To{Edge}OfBlock() => Editor` Move the current selection to the `{Edge}` of the closest block parent. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}OfDocument` -`move{Point}To{Edge}OfDocument() => Change` +`move{Point}To{Edge}OfDocument() => Editor` Move the current selection to the `{Edge}` of the document. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}OfInline` -`move{Point}To{Edge}OfInline() => Change` +`move{Point}To{Edge}OfInline() => Editor` Move the current selection to the `{Edge}` of the closest inline parent. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}OfNode` -`move{Point}To{Edge}OfNode(node: Node) => Change` +`move{Point}To{Edge}OfNode(node: Node) => Editor` Move the current selection to the `{Edge}` of a `node`. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}OfText` -`move{Point}To{Edge}OfText() => Change` +`move{Point}To{Edge}OfText() => Editor` Move the current selection to the `{Edge}` of the current text node. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}Of{Direction}Block` -`move{Point}To{Edge}Of{Direction}Block() => Change` +`move{Point}To{Edge}Of{Direction}Block() => Editor` Move the current selection to the `{Edge}` of the closest block parent. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}Of{Direction}Inline` -`move{Point}To{Edge}Of{Direction}Inline() => Change` +`move{Point}To{Edge}Of{Direction}Inline() => Editor` Move the current selection to the `{Edge}` of the closest inline parent. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `move{Point}To{Edge}Of{Direction}Text` -`move{Point}To{Edge}Of{Direction}Text() => Change` +`move{Point}To{Edge}Of{Direction}Text() => Editor` Move the current selection to the `{Edge}` of the current text node. Where `{Edge}` is either `Start` or `End`. And where `{Point}` is either `Anchor`, `Focus`, `Start` or `End`. You can also omit `{Point}` to move both the anchor and focus points at the same time. ### `moveToRangeOf` -`moveToRangeOf(node: Node) => Change` +`moveToRangeOf(node: Node) => Editor` Move the current selection's anchor point to the start of a `node` and its focus point to the end of the `node`. ### `moveToRangeOfDocument` -`moveToRangeOfDocument() => Change` +`moveToRangeOfDocument() => Editor` Move the current selection's anchor point to the start of the document and its focus point to the end of the document, selecting everything. ### `select` -`select(properties: Range || Object) => Change` +`select(properties: Range || Object) => Editor` Set the current selection to a range with merged `properties`. The `properties` can either be a [`Range`](./range.md) object or a plain JavaScript object of selection properties. -## Document Range Changes +## Document Range Commands -These changes act on a specific [`Range`](./range.md) of the document. +These commands act on a specific [`Range`](./range.md) of the document. ### `addMarkAtRange` -`addMarkAtRange(range: Range, mark: Mark) => Change`
-`addMarkAtRange(range: Range, properties: Object) => Change`
-`addMarkAtRange(range: Range, type: String) => Change` +`addMarkAtRange(range: Range, mark: Mark) => Editor`
+`addMarkAtRange(range: Range, properties: Object) => Editor`
+`addMarkAtRange(range: Range, type: String) => Editor` Add a [`mark`](./mark.md) to the characters in a `range`. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `deleteAtRange` -`deleteAtRange(range: Range, ) => Change` +`deleteAtRange(range: Range, ) => Editor` Delete everything in a `range`. ### `deleteBackwardAtRange` -`deleteBackwardAtRange(range: Range, n: Number) => Change` +`deleteBackwardAtRange(range: Range, n: Number) => Editor` Delete backward `n` characters at a `range`. If the `range` is expanded, this method is equivalent to a regular [`delete()`](#delete). `n` defaults to `1`. ### `deleteForwardAtRange` -`deleteForwardAtRange(range: Range, n: Number) => Change` +`deleteForwardAtRange(range: Range, n: Number) => Editor` Delete forward `n` characters at a `range`. If the `range` is expanded, this method is equivalent to a regular [`delete()`](#delete). `n` defaults to `1`. ### `insertBlockAtRange` -`insertBlockAtRange(range: Range, block: Block) => Change`
-`insertBlockAtRange(range: Range, properties: Object) => Change`
-`insertBlockAtRange(range: Range, type: String) => Change` +`insertBlockAtRange(range: Range, block: Block) => Editor`
+`insertBlockAtRange(range: Range, properties: Object) => Editor`
+`insertBlockAtRange(range: Range, type: String) => Editor` Insert a new block at the same level as the leaf block at a `range`, splitting the current block to make room if it is non-empty. If the selection is expanded, it will be deleted first. ### `insertFragmentAtRange` -`insertFragmentAtRange(range: Range, fragment: Document) => Change` +`insertFragmentAtRange(range: Range, fragment: Document) => Editor` Insert a [`fragment`](./document.md) at a `range`. If the selection is expanded, it will be deleted first. ### `insertInlineAtRange` -`insertInlineAtRange(range: Range, inline: Inline) => Change`
-`insertInlineAtRange(range: Range, properties: Object) => Change` +`insertInlineAtRange(range: Range, inline: Inline) => Editor`
+`insertInlineAtRange(range: Range, properties: Object) => Editor` Insert a new inline at a `range`, splitting the text to make room if it is non-empty. If the selection is expanded, it will be deleted first. ### `insertTextAtRange` -`insertTextAtRange(range: Range, text: String) => Change` +`insertTextAtRange(range: Range, text: String) => Editor` Insert a string of `text` at a `range`. If the selection is expanded, it will be deleted first. ### `setBlocksAtRange` -`setBlocksAtRange(range: Range, properties: Object) => Change`
-`setBlocks(range: Range, type: String) => Change` +`setBlocksAtRange(range: Range, properties: Object) => Editor`
+`setBlocks(range: Range, type: String) => Editor` Set the `properties` of the [`Blocks`](./block.md) in a `range`. For convenience, you can pass a `type` string to set the blocks' type only. ### `setInlinesAtRange` -`setInlinesAtRange(range: Range, properties: Object) => Change`
-`setInlines(range: Range, type: String) => Change` +`setInlinesAtRange(range: Range, properties: Object) => Editor`
+`setInlines(range: Range, type: String) => Editor` Set the `properties` of the [`Inlines`](./inline.md) nodes in a `range`. For convenience, you can pass a `type` string to set the inline nodes' type only. ### `splitBlockAtRange` -`splitBlockAtRange(range: Range, depth: Number) => Change` +`splitBlockAtRange(range: Range, depth: Number) => Editor` Split the [`Block`](./block.md) in a `range` by `depth` levels. If the selection is expanded, it will be deleted first. `depth` defaults to `1`. ### `splitInlineAtRange` -`splitInlineAtRange(range: Range, depth: Number) => Change` +`splitInlineAtRange(range: Range, depth: Number) => Editor` Split the [`Inline`](./inline.md) node in a `range` by `depth` levels. If the selection is expanded, it will be deleted first. `depth` defaults to `Infinity`. ### `removeMarkAtRange` -`removeMarkAtRange(range: Range, mark: Mark) => Change`
-`removeMarkAtRange(range: Range, properties: Object) => Change`
-`removeMarkAtRange(range: Range, type: String) => Change` +`removeMarkAtRange(range: Range, mark: Mark) => Editor`
+`removeMarkAtRange(range: Range, properties: Object) => Editor`
+`removeMarkAtRange(range: Range, type: String) => Editor` Remove a [`mark`](./mark.md) from the characters in a `range`. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `toggleMarkAtRange` -`toggleMarkAtRange(range: Range, mark: Mark) => Change`
-`toggleMarkAtRange(range: Range, properties: Object) => Change`
-`toggleMarkAtRange(range: Range, type: String) => Change` +`toggleMarkAtRange(range: Range, mark: Mark) => Editor`
+`toggleMarkAtRange(range: Range, properties: Object) => Editor`
+`toggleMarkAtRange(range: Range, type: String) => Editor` Add or remove a [`mark`](./mark.md) from the characters in a `range`, depending on whether any of them already have the mark. For convenience, you can pass a `type` string or `properties` object to implicitly create a [`Mark`](./mark.md) of that type. ### `unwrapBlockAtRange` -`unwrapBlockAtRange(range: Range, properties: Object) => Change`
-`unwrapBlockAtRange(range: Range, type: String) => Change` +`unwrapBlockAtRange(range: Range, properties: Object) => Editor`
+`unwrapBlockAtRange(range: Range, type: String) => Editor` Unwrap all [`Block`](./block.md) nodes in a `range` that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `unwrapInlineAtRange` -`unwrapInlineAtRange(range: Range, properties: Object) => Change`
-`unwrapInlineAtRange(range: Range, type: String) => Change` +`unwrapInlineAtRange(range: Range, properties: Object) => Editor`
+`unwrapInlineAtRange(range: Range, type: String) => Editor` Unwrap all [`Inline`](./inline.md) nodes in a `range` that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `wrapBlockAtRange` -`wrapBlockAtRange(range: Range, properties: Object) => Change`
-`wrapBlockAtRange(range: Range, type: String) => Change` +`wrapBlockAtRange(range: Range, properties: Object) => Editor`
+`wrapBlockAtRange(range: Range, type: String) => Editor` Wrap the [`Block`](./block.md) nodes in a `range` with a new [`Block`](./block.md) node with `properties`. For convenience, you can pass a `type` string or `properties` object. ### `wrapInlineAtRange` -`wrapInlineAtRange(range: Range, properties: Object) => Change`
-`wrapInlineAtRange(range: Range, type: String) => Change` +`wrapInlineAtRange(range: Range, properties: Object) => Editor`
+`wrapInlineAtRange(range: Range, type: String) => Editor` Wrap the [`Inline`](./inline.md) nodes in a `range` with a new [`Inline`](./inline.md) node with `properties`. For convenience, you can pass a `type` string or `properties` object. ### `wrapTextAtRange` -`wrapTextAtRange(range: Range, prefix: String, [suffix: String]) => Change` +`wrapTextAtRange(range: Range, prefix: String, [suffix: String]) => Editor` Surround the text in a `range` with `prefix` and `suffix` strings. If the `suffix` is ommitted, the `prefix` will be used instead. -## Node Changes +## Node Commands -These changes are lower-level, and act on a specific node by its `key` or `path`. They're often used in your custom components because you'll have access to `props.node`. +These commands are lower-level, and act on a specific node by its `key` or `path`. They're often used in your custom components because you'll have access to `props.node`. ### `addMarkByKey/Path` -`addMarkByKey(key: String, offset: Number, length: Number, mark: Mark) => Change` -`addMarkByPath(path: List, offset: Number, length: Number, mark: Mark) => Change` +`addMarkByKey(key: String, offset: Number, length: Number, mark: Mark) => Editor` +`addMarkByPath(path: List, offset: Number, length: Number, mark: Mark) => Editor` Add a `mark` to `length` characters starting at an `offset` in a [`Node`](./node.md) by its `key` or `path`. ### `insertNodeByKey/Path` -`insertNodeByKey(key: String, index: Number, node: Node) => Change` -`insertNodeByPath(path: List, index: Number, node: Node) => Change` +`insertNodeByKey(key: String, index: Number, node: Node) => Editor` +`insertNodeByPath(path: List, index: Number, node: Node) => Editor` Insert a `node` at `index` inside a parent [`Node`](./node.md) by its `key` or `path`. @@ -532,144 +423,190 @@ Insert a [`Fragment`](./fragment.md) at `index` inside a parent [`Node`](./node. ### `insertTextByKey/Path` -`insertTextByKey(key: String, offset: Number, text: String, [marks: Set]) => Change` -`insertTextByPath(path: List, offset: Number, text: String, [marks: Set]) => Change` +`insertTextByKey(key: String, offset: Number, text: String, [marks: Set]) => Editor` +`insertTextByPath(path: List, offset: Number, text: String, [marks: Set]) => Editor` Insert `text` at an `offset` in a [`Text Node`](./text.md) by its `key` with optional `marks`. ### `mergeNodeByKey/Path` -`mergeNodeByKey(key: String) => Change` -`mergeNodeByPath(path: List) => Change` +`mergeNodeByKey(key: String) => Editor` +`mergeNodeByPath(path: List) => Editor` Merge a [`Node`](./node.md) by its `key` or `path` with its previous sibling. ### `moveNodeByKey/Path` -`moveNodeByKey(key: String, newKey: String, newIndex: Number) => Change` -`moveNodeByPath(path: List, newKey: String, newIndex: Number) => Change` +`moveNodeByKey(key: String, newKey: String, newIndex: Number) => Editor` +`moveNodeByPath(path: List, newKey: String, newIndex: Number) => Editor` Move a [`Node`](./node.md) by its `key` or `path` to a new parent node with its `newKey` and at a `newIndex`. ### `removeMarkByKey/Path` -`removeMarkByKey(key: String, offset: Number, length: Number, mark: Mark) => Change` -`removeMarkByPath(path: List, offset: Number, length: Number, mark: Mark) => Change` +`removeMarkByKey(key: String, offset: Number, length: Number, mark: Mark) => Editor` +`removeMarkByPath(path: List, offset: Number, length: Number, mark: Mark) => Editor` Remove a `mark` from `length` characters starting at an `offset` in a [`Node`](./node.md) by its `key` or `path`. ### `removeNodeByKey/Path` -`removeNodeByKey(key: String) => Change` -`removeNodeByPath(path: List) => Change` +`removeNodeByKey(key: String) => Editor` +`removeNodeByPath(path: List) => Editor` Remove a [`Node`](./node.md) from the document by its `key` or `path`. ### `replaceNodeByKey/Path` -`replaceNodeByKey(key: String, node: Node) => Change` -`replaceNodeByPath(path: List, node: Node) => Change` +`replaceNodeByKey(key: String, node: Node) => Editor` +`replaceNodeByPath(path: List, node: Node) => Editor` Replace a [`Node`](./node.md) in the document with a new [`Node`](./node.md) by its `key` or `path`. ### `removeTextByKey/Path` -`removeTextByKey(key: String, offset: Number, length: Number) => Change` -`removeTextByPath(path: List, offset: Number, length: Number) => Change` +`removeTextByKey(key: String, offset: Number, length: Number) => Editor` +`removeTextByPath(path: List, offset: Number, length: Number) => Editor` Remove `length` characters of text starting at an `offset` in a [`Node`](./node.md) by its `key` or `path`. ### `setMarkByKey/Path` -`setMarkByKey(key: String, offset: Number, length: Number, mark: Mark, properties: Object) => Change` -`setMarkByPath(path: List, offset: Number, length: Number, mark: Mark, properties: Object) => Change` +`setMarkByKey(key: String, offset: Number, length: Number, mark: Mark, properties: Object) => Editor` +`setMarkByPath(path: List, offset: Number, length: Number, mark: Mark, properties: Object) => Editor` Set a dictionary of `properties` on a [`mark`](./mark.md) on a [`Node`](./node.md) by its `key` or `path`. ### `setNodeByKey/Path` -`setNodeByKey(key: String, properties: Object) => Change`
-`setNodeByKey(key: String, type: String) => Change` -`setNodeByPath(path: List, properties: Object) => Change`
-`setNodeByPath(path: List, type: String) => Change` +`setNodeByKey(key: String, properties: Object) => Editor`
+`setNodeByKey(key: String, type: String) => Editor` +`setNodeByPath(path: List, properties: Object) => Editor`
+`setNodeByPath(path: List, type: String) => Editor` Set a dictionary of `properties` on a [`Node`](./node.md) by its `key` or `path`. For convenience, you can pass a `type` string or `properties` object. ### `splitNodeByKey/Path` -`splitNodeByKey(key: String, offset: Number) => Change` -`splitNodeByPath(path: List, offset: Number) => Change` +`splitNodeByKey(key: String, offset: Number) => Editor` +`splitNodeByPath(path: List, offset: Number) => Editor` Split a node by its `key` or `path` at an `offset`. ### `unwrapInlineByKey/Path` -`unwrapInlineByKey(key: String, properties: Object) => Change`
-`unwrapInlineByKey(key: String, type: String) => Change` -`unwrapInlineByPath(path: List, properties: Object) => Change`
-`unwrapInlineByPath(path: List, type: String) => Change` +`unwrapInlineByKey(key: String, properties: Object) => Editor`
+`unwrapInlineByKey(key: String, type: String) => Editor` +`unwrapInlineByPath(path: List, properties: Object) => Editor`
+`unwrapInlineByPath(path: List, type: String) => Editor` Unwrap all inner content of an [`Inline`](./inline.md) node by its `key` or `path` that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `unwrapBlockByKey/Path` -`unwrapBlockByKey(key: String, properties: Object) => Change`
-`unwrapBlockByKey(key: String, type: String) => Change` -`unwrapBlockByPath(path: List, properties: Object) => Change`
-`unwrapBlockByPath(path: List, type: String) => Change` +`unwrapBlockByKey(key: String, properties: Object) => Editor`
+`unwrapBlockByKey(key: String, type: String) => Editor` +`unwrapBlockByPath(path: List, properties: Object) => Editor`
+`unwrapBlockByPath(path: List, type: String) => Editor` Unwrap all inner content of a [`Block`](./block.md) node by its `key` or `path` that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `unwrapNodeByKey/Path` -`unwrapNodeByKey(key: String) => Change` -`unwrapNodeByPath(path: List) => Change` +`unwrapNodeByKey(key: String) => Editor` +`unwrapNodeByPath(path: List) => Editor` Unwrap a single node from its parent. If the node is surrounded with siblings, its parent will be split. If the node is the only child, the parent is removed, and simply replaced by the node itself. Cannot unwrap a root node. ### `wrapBlockByKey/Path` -`wrapBlockByKey(key: String, properties: Object) => Change`
-`wrapBlockByKey(key: String, type: String) => Change` -`wrapBlockByPath(path: List, properties: Object) => Change`
-`wrapBlockByPath(path: List, type: String) => Change` +`wrapBlockByKey(key: String, properties: Object) => Editor`
+`wrapBlockByKey(key: String, type: String) => Editor` +`wrapBlockByPath(path: List, properties: Object) => Editor`
+`wrapBlockByPath(path: List, type: String) => Editor` Wrap the given node in a [`Block`](./block.md) node that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `wrapInlineByKey/Path` -`wrapInlineByKey(key: String, properties: Object) => Change`
-`wrapInlineByKey(key: String, type: String) => Change` -`wrapInlineByPath(path: List, properties: Object) => Change`
-`wrapInlineByPath(path: List, type: String) => Change` +`wrapInlineByKey(key: String, properties: Object) => Editor`
+`wrapInlineByKey(key: String, type: String) => Editor` +`wrapInlineByPath(path: List, properties: Object) => Editor`
+`wrapInlineByPath(path: List, type: String) => Editor` Wrap the given node in a [`Inline`](./inline.md) node that match `properties`. For convenience, you can pass a `type` string or `properties` object. ### `wrapNodeByKey/Path` -`wraNodeByKey(key: String, parent: Node) => Change`
-`wraNodeByPath(path: List, parent: Node) => Change`
+`wraNodeByKey(key: String, parent: Node) => Editor`
+`wraNodeByPath(path: List, parent: Node) => Editor`
Wrap the node with the specified key with the parent [`Node`](./node.md). This will clear all children of the parent. -## History Changes +## History Commands -These changes use the history to undo/redo previously made changes. +These commands use the history to undo/redo previously made changes. ### `redo` -`redo() => Change` +`redo() => Editor` Move forward one step in the history. ### `undo` -`undo() => Change` +`undo() => Editor` Move backward one step in the history. ### `snapshotSelection` -`snapshotSelection() => Change` +`snapshotSelection() => Editor` Snapshot `value.selection` for `undo` purposes, useful with delete operations like `change.removeNodeByKey(focusBlock.key).undo()`. + +## Miscellaneous Commands + +### `normalize` + +`normalize() => Editor` + +This method normalizes the document with the value's schema. This should run automatically-you should not need to call this method unless you have manually disabled normalization (and you should rarely, if ever, need to manually disable normalization). The vast majority of changes, whether by the user or invoked programmatically, will run `normalize` by default to ensure the document is always in adherence to its schema. + +> 🤖 If you must use this method, use it sparingly and strategically. Calling this method can be very expensive as it will run normalization on all of the nodes in your document. + +### `withoutNormalizing` + +`withoutNormalizing(fn: Function) => Editor` + +This method calls the provided function with the current instance of the `Change` object as the first argument. Normalization does not occur while the fuction is executing, and is instead deferred to be be run immediately after it completes. + +This method can be used to allow a sequence of change operations that should not be interrupted by normalization. For example: + +```js +editor.withoutNormalizing(() => { + node.nodes.filter(n => n.object != 'block').forEach(child => { + editor.removeNodeByKey(child.key) + }) +}) +``` + +### `withoutSaving` + +`withoutSaving(fn: Function) => Editor` + +By default all new operations are saved to the editor's history. If you have changes that you don't want to show up in the history when the user presses cmd+z, you can use `withoutSaving` to skip those changes. + +```js +change.withoutSaving(() => { + change.setDecorations(decorations) +}) +``` + +However, be sure you know what you are doing because this will create changes that cannot be undone by the user, and might result in confusing behaviors. + +### `withoutMerging` + +`withoutMerging(fn: Function) => Editor` + +Usually, all of the operations in a `Change` are grouped into a single save point in the editor's history. However, sometimes you may want more control over this, to be able to create distinct save points in a single change. To do that, you can use the `withoutMerging` helper. diff --git a/docs/reference/slate/editor.md b/docs/reference/slate/editor.md index 556d77e73..f3a04d068 100644 --- a/docs/reference/slate/editor.md +++ b/docs/reference/slate/editor.md @@ -23,7 +23,13 @@ new Editor({ `Function onChange(change: Change)` -A change handler that will be called with the `change` that applied the change. When the `onChange` handler is called, the `editor.value` will already reflect the new state. +A change handler that will be called asynchronously with the `change` that applied the change. When the `onChange` handler is called, the `editor.value` will already reflect the new state. + +### `operations` + +`List` + +An immutable list of [`Operation`](./operation.md) models that have already been applied to the editor in the current change. As soon as the first operation is added, the `onChange` is queued to run on the next tick. ### `plugins` @@ -47,67 +53,100 @@ A [`Value`](../slate/value.md) object representing the current value of the edit ## Methods -### `change` - -`change(fn) => Void` -`change(fn, ...args) => Void` - -Programmatically invoke a change `fn` on the editor. The function will be invoked with a new `change` object representing the editor's current value. - -If extra `...args` are passed in, the change `fn` will be invoked with `(change, ...args)`, so you can use this as a shorthand for performing single-function changes. - ### `command` -`command(name, ...args) => Void` +`command(type: String, ...args) => Editor` +`command(fn: Function, ...args) => Editor` -Invoke a command by `name` on the editor with `args`. +```js +editor.command('insertText', 'word') +editor.command((editor, text) => { ... }, 'word') +``` -### `event` +Invoke a command by `type` on the editor with `args`. -`event(handler, event, ...args) => Any` +Alternatively, the `type` argument can be a function, which will be invoked with `(editor, ...args)`. This is helpful in situations where you want write one-off commands with customized logic. -Programmatically invoke an `event` on the editor. This isn't something you should normally use except in test environments. +### `flush` -### `focus` +`flush() => Editor` -`focus() => Void` +```js +editor.flush() +``` -Programmatically focus the editor. +Synchronously flush the current changes to editor, calling `onChange`. + +> 🤖 In normal operation you never need to use this method! However, it can be helpful for writing tests to be able to keep the entire test synchronous. ### `query` -`query(name, ...args) => Any` +`query(type: String, ...args) => Any` +`query(fn: Function, ...args) => Editor` -Invoke a query by `name` on the editor with `args`, returning its result. +```js +editor.query('isLinkActive') +editor.query(editor => { ... }) +``` + +Invoke a query by `type` on the editor with `args`, returning its result. + +Alternatively, the `type` argument can be a function, which will be invoked with `(editor, ...args)`. This is helpful in situations where you want write one-off queries with customized logic. ### `registerCommand` -`registerCommand(command: String) => Void` +`registerCommand(type: String) => Void` -Register a new `command` by name with the editor. This will make the command available as a method on the editor's `Change` objects. +```js +editor.registerCommand('insertLink') +``` + +Add a new command by `type` with the editor. This will make the command available as a top-level method on the `editor`. + +> 🤖 Note that this method only registers the command with the editor, creating the top-level command method. It does not define the queries behaviors, which are defined with the `onCommand` middleware. ### `registerQuery` -`registerQuery(query: String) => Void` +`registerQuery(type: String) => Void` -Register a new `query` by name with the editor. This will make the query available as a method on the editor's `Change` objects. +```js +editor.registerQuery('isLinkActive') +``` + +Add a new query by `type` with the editor. This will make the query available as a top-level method on the `editor`. + +> 🤖 Note that this method only registers the query with the editor, creating the top-level query method. It does not define the queries behaviors, which are defined with the `onQuery` middleware. ### `run` `run(key, ...args) => Any` +```js +editor.run('onKeyDown', { key: 'Tab', ... }) +``` + Run the middleware stack by `key` with `args`, returning its result. +> 🤖 In normal operation you never need to use this method! However, it's useful for writing tests to be able to simulate plugin behaviors. + ### `setReadOnly` `setReadOnly(readOnly: Boolean) => Editor` +```js +editor.setReadOnly(true) +``` + Set the editor's `readOnly` state. ### `setValue` `setValue(value: Value, options: Object) => Editor` +```js +editor.setValue(value) +``` + Set the editor's `value` state. You can optionally provide a `normalize` option to either for the editor to completely re-normalize the new value based on its schema or not. By default, the editor will re-normalize only if the value is not equal to its previously seen value (which it knows was normalized). diff --git a/docs/reference/slate/operation.md b/docs/reference/slate/operation.md index 0abcc55a5..925a4151a 100644 --- a/docs/reference/slate/operation.md +++ b/docs/reference/slate/operation.md @@ -2,7 +2,7 @@ An operation is the lowest-level description of a specific change to a part of Slate's value. They are designed to be collaborative-editing friendly. -All of the [`Change`](./change.md) methods result in operations being created and applied to a [`Value`](./value.md) They're accessible via the `change.operations` property. +All of the [`Commands`](./commands.md) methods result in operations being created and applied to a [`Value`](./value.md) They're accessible via the `editor.operations` property. There are a handful of Slate operation types. The goal is to have the fewest possible types, while still maintaining the necessary semantics for collaborative editing to work. diff --git a/docs/reference/slate/plugins.md b/docs/reference/slate/plugins.md index 634724951..8bce44ed6 100644 --- a/docs/reference/slate/plugins.md +++ b/docs/reference/slate/plugins.md @@ -23,17 +23,17 @@ When a hook is triggered, the middleware function is passed a set of arguments, ### `normalizeNode` -`Function normalizeNode(node: Node, next: Function) => Function(change: Change)|Void` +`Function normalizeNode(node: Node, editor: Editor, next: Function) => Function(editor: Editor)|Void` The `normalizeNode` hook takes a `node` and either returns `undefined` if the node is valid, or a change function that normalizes the node into a valid state if not. ### `onChange` -`onChange(change: Change, next: Function) => Void` +`onChange(editor: Editor, next: Function) => Void` ```js { - onChange(change, next) { + onChange(editor, next) { ... return next() } @@ -44,11 +44,11 @@ The `onChange` hook is called whenever a new change is about to be applied to an ### `onCommand` -`onCommand(command: Object, change: Change, ) => Void` +`onCommand(command: Object, editor: Editor, next: Function) => Void` ```js { - onCommand(command, change, next) { + onCommand(command, editor, next) { const { type, args } = command if (type === 'wrapQuote') { @@ -90,11 +90,11 @@ The `onConstruct` hook is called when a new instance of `Editor` is created. Thi ### `onQuery` -`onQuery(query: Object, next: Function) => Void` +`onQuery(query: Object, editor: Editor, next: Function) => Void` ```js { - onQuery(query, next) { + onQuery(query, editor, next) { const { type, args } = query if (type === 'getActiveList') { @@ -119,7 +119,7 @@ The `onQuery` hook is a low-level way to have access to all of the queries passi ### `validateNode` -`Function validateNode(node: Node, next: Function) => SlateError|Void` +`Function validateNode(node: Node, editor: Editor, next: Function) => SlateError|Void` The `validateNode` hook takes a `node` and either returns `undefined` if the node is valid, or a `SlateError` object if it is invalid. @@ -142,16 +142,16 @@ In addition to the middleware functions, Slate also provides three shorthands wh ```js { commands: { - setHeader(change, level) { - change.setBlocks({ type: 'header', data: { level }}) + setHeader(editor, level) { + editor.setBlocks({ type: 'header', data: { level }}) } } } ``` -The `commands` shorthand defines a set of custom commands that are made available in the editor, and as first-class methods on the `change` objects created by the editor. +The `commands` shorthand defines a set of custom commands that are made available in the editor, and as first-class methods on the `editor`. -Each command has a signature of `(change, ...args)`. +Each command has a signature of `(editor, ...args)`. ### `queries` @@ -167,7 +167,7 @@ Each command has a signature of `(change, ...args)`. } ``` -The `queries` shorthand defines a set of custom queries that are made available in the editor, and as first-class methods on the `change` objects created by the editor. +The `queries` shorthand defines a set of custom queries that are made available in the editor, and as first-class methods on the `editor`. Each query has a signature of `(editor, ...args)`. diff --git a/docs/reference/slate/schema.md b/docs/reference/slate/schema.md index 16fc04d65..7175bffd2 100644 --- a/docs/reference/slate/schema.md +++ b/docs/reference/slate/schema.md @@ -211,17 +211,17 @@ Will validate a node's marks. The `marks` definitions can declare the `type` pro ### `normalize` -`normalize(change: Change, error: SlateError) => Void` +`normalize(editor: Editor, error: SlateError) => Void` ```js { - normalize: (change, error) => { + normalize: (editor, error) => { switch (error.code) { case 'child_object_invalid': - change.wrapBlockByKey(error.child.key, 'paragraph') + editor.wrapBlockByKey(error.child.key, 'paragraph') return case 'child_type_invalid': - change.setNodeByKey(error.child.key, 'paragraph') + editor.setNodeByKey(error.child.key, 'paragraph') return } } @@ -288,7 +288,7 @@ Will validate a node's text with a regex or function. ## Errors -When supplying your own `normalize` property for a schema rule, it will be called with `(change, error)`. The error `code` will be one of a set of potential code strings, and it will contain additional helpful properties depending on the type of error. +When supplying your own `normalize` property for a schema rule, it will be called with `(editor, error)`. The error `code` will be one of a set of potential code strings, and it will contain additional helpful properties depending on the type of error. ### `'child_object_invalid'` diff --git a/docs/walkthroughs/adding-event-handlers.md b/docs/walkthroughs/adding-event-handlers.md index 5106725e1..fb115d324 100644 --- a/docs/walkthroughs/adding-event-handlers.md +++ b/docs/walkthroughs/adding-event-handlers.md @@ -41,7 +41,7 @@ class App extends React.Component { } // Define a new handler which prints the key that was pressed. - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { console.log(event.key) return next() } @@ -74,7 +74,7 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { // Return with no changes if the keypress is not '&' if (event.key !== '&') return next() @@ -82,7 +82,7 @@ class App extends React.Component { event.preventDefault() // Change the value by inserting 'and' at the cursor's position. - change.insertText('and') + editor.insertText('and') return true } @@ -100,7 +100,7 @@ class App extends React.Component { With that added, try typing `&`, and you should see it suddenly become `and` instead! -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! +This offers a sense of what can be done with Slate's event handlers. Each one will be called with the `event` object, and the `editor` that lets you perform commands. Simple!

Next:
Defining Custom Block Nodes

diff --git a/docs/walkthroughs/applying-custom-formatting.md b/docs/walkthroughs/applying-custom-formatting.md index 3f413b60e..5c492c211 100644 --- a/docs/walkthroughs/applying-custom-formatting.md +++ b/docs/walkthroughs/applying-custom-formatting.md @@ -20,12 +20,12 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (event.key != '`' || !event.ctrlKey) return next() event.preventDefault() - const isCode = change.value.blocks.some(block => block.type == 'code') + const isCode = editor.value.blocks.some(block => block.type == 'code') - change.setBlocks(isCode ? 'paragraph' : 'code') + editor.setBlocks(isCode ? 'paragraph' : 'code') return true } @@ -40,7 +40,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -63,7 +63,7 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (!event.ctrlKey) return next() // Decide what to do based on the key code... @@ -71,15 +71,13 @@ class App extends React.Component { // When "B" is pressed, add a "bold" mark to the text. case 'b': { event.preventDefault() - change.addMark('bold') - return true + editor.addMark('bold') } // When "`" is pressed, keep our existing code block logic. case '`': { - const isCode = change.value.blocks.some(block => block.type == 'code') + const isCode = editor.value.blocks.some(block => block.type == 'code') event.preventDefault() - change.setBlocks(isCode ? 'paragraph' : 'code') - return true + editor.setBlocks(isCode ? 'paragraph' : 'code') } // Otherwise, let other plugins handle it. default: { @@ -99,7 +97,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -139,20 +137,18 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (!event.ctrlKey) return next() switch (event.key) { case 'b': { event.preventDefault() - change.toggleMark('bold') - return true + editor.toggleMark('bold') } case '`': { - const isCode = change.value.blocks.some(block => block.type == 'code') + const isCode = editor.value.blocks.some(block => block.type == 'code') event.preventDefault() - change.setBlocks(isCode ? 'paragraph' : 'code') - return true + editor.setBlocks(isCode ? 'paragraph' : 'code') } default: { return next() @@ -173,7 +169,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -183,7 +179,7 @@ class App extends React.Component { } // Add a `renderMark` method to render marks. - renderMark = (props, next) => { + renderMark = (props, editor, next) => { switch (props.mark.type) { case 'bold': return diff --git a/docs/walkthroughs/defining-custom-block-nodes.md b/docs/walkthroughs/defining-custom-block-nodes.md index 936f64cf9..20a5148af 100644 --- a/docs/walkthroughs/defining-custom-block-nodes.md +++ b/docs/walkthroughs/defining-custom-block-nodes.md @@ -20,11 +20,10 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (event.key != '&') return next() event.preventDefault() - change.insertText('and') - return true + editor.insertText('and') } render() { @@ -82,11 +81,10 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (event.key != '&') return next() event.preventDefault() - change.insertText('and') - return true + editor.insertText('and') } render() { @@ -102,7 +100,7 @@ class App extends React.Component { } // Add a `renderNode` method to render a `CodeNode` for code blocks. - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -133,7 +131,7 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { // Return with no changes if it's not the "`" key with ctrl pressed. if (event.key != '`' || !event.ctrlKey) return next() @@ -141,8 +139,7 @@ class App extends React.Component { event.preventDefault() // Otherwise, set the currently selected blocks type to "code". - change.setBlocks('code') - return true + editor.setBlocks('code') } render() { @@ -156,7 +153,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -191,17 +188,16 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (event.key != '`' || !event.ctrlKey) return next() event.preventDefault() // Determine whether any of the currently selected blocks are code blocks. - const isCode = change.value.blocks.some(block => block.type == 'code') + const isCode = editor.value.blocks.some(block => block.type == 'code') // Toggle the block type depending on `isCode`. - change.setBlocks(isCode ? 'paragraph' : 'code') - return true + editor.setBlocks(isCode ? 'paragraph' : 'code') } render() { @@ -215,7 +211,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return diff --git a/docs/walkthroughs/saving-and-loading-html-content.md b/docs/walkthroughs/saving-and-loading-html-content.md index fba88efa5..7817469fd 100644 --- a/docs/walkthroughs/saving-and-loading-html-content.md +++ b/docs/walkthroughs/saving-and-loading-html-content.md @@ -264,7 +264,7 @@ class App extends React.Component { ) } - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return ( @@ -286,7 +286,7 @@ class App extends React.Component { } // Add a `renderMark` method to render marks. - renderMark = (props, next) => { + renderMark = (props, editor, next) => { const { mark, attributes } = props switch (mark.type) { case 'bold': diff --git a/docs/walkthroughs/using-plugins.md b/docs/walkthroughs/using-plugins.md index 0837389aa..628aa00f9 100644 --- a/docs/walkthroughs/using-plugins.md +++ b/docs/walkthroughs/using-plugins.md @@ -22,11 +22,10 @@ class App extends React.Component { this.setState({ value }) } - onKeyDown = (event, change, next) => { + onKeyDown = (event, editor, next) => { if (event.key != 'b' || !event.ctrlKey) return next() event.preventDefault() - change.toggleMark('bold') - return true + editor.toggleMark('bold') } render() { @@ -40,7 +39,7 @@ class App extends React.Component { ) } - renderMark = (props, next) => { + renderMark = (props, editor, next) => { switch (props.mark.type) { case 'bold': return {props.children} @@ -72,7 +71,7 @@ function MarkHotkey(options) { // Return our "plugin" object, containing the `onKeyDown` handler. return { - onKeyDown(event, change, next) { + onKeyDown(event, editor, next) { // If it doesn't match our `key`, let other plugins handle it. if (!event.ctrlKey || event.key != key) return next() @@ -80,8 +79,7 @@ function MarkHotkey(options) { event.preventDefault() // Toggle the mark `type`. - change.toggleMark(type) - return true + editor.toggleMark(type) }, } } @@ -122,7 +120,7 @@ class App extends React.Component { ) } - renderMark = (props, next) => { + renderMark = (props, editor, next) => { switch (props.mark.type) { case 'bold': return {props.children} @@ -167,7 +165,7 @@ class App extends React.Component { ) } - renderMark = (props, next) => { + renderMark = (props, editor, next) => { switch (props.mark.type) { case 'bold': return {props.children} diff --git a/examples/check-lists/index.js b/examples/check-lists/index.js index c4cbd6355..4b79b42ca 100644 --- a/examples/check-lists/index.js +++ b/examples/check-lists/index.js @@ -51,7 +51,7 @@ class CheckListItem extends React.Component { onChange = event => { const checked = event.target.checked const { editor, node } = this.props - editor.change(c => c.setNodeByKey(node.key, { data: { checked } })) + editor.setNodeByKey(node.key, { data: { checked } }) } /** @@ -124,7 +124,7 @@ class CheckLists extends React.Component { * @return {Element} */ - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'check-list-item': return @@ -136,7 +136,7 @@ class CheckLists extends React.Component { /** * On change, save the new value. * - * @param {Change} change + * @param {Editor} editor */ onChange = ({ value }) => { @@ -153,15 +153,15 @@ class CheckLists extends React.Component { * then turn it back into a paragraph. * * @param {Event} event - * @param {Change} change + * @param {Editor} editor * @param {Function} next */ - onKeyDown = (event, change, next) => { - const { value } = change + onKeyDown = (event, editor, next) => { + const { value } = editor if (event.key == 'Enter' && value.startBlock.type == 'check-list-item') { - change.splitBlock().setBlocks({ data: { checked: false } }) + editor.splitBlock().setBlocks({ data: { checked: false } }) return } @@ -171,7 +171,7 @@ class CheckLists extends React.Component { value.startBlock.type == 'check-list-item' && value.selection.startOffset == 0 ) { - change.setBlocks('paragraph') + editor.setBlocks('paragraph') return } diff --git a/examples/code-highlighting/index.js b/examples/code-highlighting/index.js index 208637b05..9589e43f3 100644 --- a/examples/code-highlighting/index.js +++ b/examples/code-highlighting/index.js @@ -17,9 +17,7 @@ function CodeBlock(props) { const language = node.data.get('language') function onChange(event) { - editor.change(c => - c.setNodeByKey(node.key, { data: { language: event.target.value } }) - ) + editor.setNodeByKey(node.key, { data: { language: event.target.value } }) } return ( @@ -106,7 +104,7 @@ class CodeHighlighting extends React.Component { * @return {Element} */ - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'code': return @@ -124,7 +122,7 @@ class CodeHighlighting extends React.Component { * @return {Element} */ - renderMark = (props, next) => { + renderMark = (props, editor, next) => { const { children, mark, attributes } = props switch (mark.type) { @@ -160,7 +158,7 @@ class CodeHighlighting extends React.Component { /** * On change, save the new value. * - * @param {Change} change + * @param {Editor} editor */ onChange = ({ value }) => { @@ -171,17 +169,16 @@ class CodeHighlighting extends React.Component { * On key down inside code blocks, insert soft new lines. * * @param {Event} event - * @param {Change} change + * @param {Editor} editor * @param {Function} next - * @return {Change} */ - onKeyDown = (event, change, next) => { - const { value } = change + onKeyDown = (event, editor, next) => { + const { value } = editor const { startBlock } = value if (event.key === 'Enter' && startBlock.type === 'code') { - change.insertText('\n') + editor.insertText('\n') return } @@ -195,7 +192,7 @@ class CodeHighlighting extends React.Component { * @return {Array} */ - decorateNode = (node, next) => { + decorateNode = (node, editor, next) => { const others = next() || [] if (node.type != 'code') return others diff --git a/examples/embeds/index.js b/examples/embeds/index.js index 7595bef28..e4c05d52c 100644 --- a/examples/embeds/index.js +++ b/examples/embeds/index.js @@ -62,7 +62,7 @@ class Embeds extends React.Component { * @param {Function} next */ - renderNode = (props, next) => { + renderNode = (props, editor, next) => { switch (props.node.type) { case 'video': return