1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-03-06 05:49:47 +01:00
Ian Storm Taylor 8dd919dc34
remove change, fold into editor (#2337)
#### Is this adding or improving a _feature_ or fixing a _bug_?

Improvement / debt.

#### What's the new behavior?

This pull request removes the `Change` object as we know it, and folds all of its behaviors into the new `Editor` controller instead, simplifying a lot of the confusion around what is a "change vs. editor" and when to use which. It makes the standard API a **lot** nicer to use I think.

---

###### NEW

**The `editor.command` and `editor.query` methods can take functions.** Previously they only accepted a `type` string and would look up the command or query by type. Now, they also accept a custom function. This is helpful for plugin authors, who want to accept a "command option", since it gives users more flexibility to write one-off commands or queries. For example a plugin could be passed either:

```js
Hotkey({
  hotkey: 'cmd+b',
  command: 'addBoldMark',
})
```

Or a custom command function:

```js
Hotkey({
  hotkey: 'cmd+b',
  command: editor => editor.addBoldMark().moveToEnd()
})
```

###### BREAKING

**The `Change` object has been removed.** The `Change` object as we know it previously has been removed, and all of its behaviors have been folded into the `Editor` controller. This includes the top-level commands and queries methods, as well as methods like `applyOperation` and `normalize`. _All places that used to receive `change` now receive `editor`, which is API equivalent._

**Changes are now flushed to `onChange` asynchronously.** Previously this was done synchronously, which resulted in some strange race conditions in React environments. Now they will always be flushed asynchronously, just like `setState`.

**The `render*` and `decorate*` middleware signatures have changed!** Previously the `render*` and `decorate*` middleware was passed `(props, next)`. However now, for consistency with the other middleware they are all passed `(props, editor, next)`. This way, all middleware always receive `editor` and `next` as their final two arguments.

**The `normalize*` and `validate*` middleware signatures have changed!** Previously the `normalize*` and `validate*` middleware was passed `(node, next)`. However now, for consistency with the other middleware they are all passed `(node, editor, next)`. This way, all middleware always receive `editor` and `next` as their final two arguments.

**The `editor.event` method has been removed.** Previously this is what you'd use when writing tests to simulate events being fired—which were slightly different to other running other middleware. With the simplification to the editor and to the newly-consistent middleware signatures, you can now use `editor.run` directly to simulate events:

```js
editor.run('onKeyDown', { key: 'Tab', ... })
```

###### DEPRECATED

**The `editor.change` method is deprecated.** With the removal of the `Change` object, there's no need anymore to create the small closures with `editor.change()`. Instead you can directly invoke commands on the editor in series, and all of the changes will be emitted asynchronously on the next tick.

```js
editor
  .insertText('word')
  .moveFocusForward(10)
  .addMark('bold')
```

**The `applyOperations` method is deprecated.** Instead you can loop a set of operations and apply each one using `applyOperation`. This is to reduce the number of methods exposed on the `Editor` to keep it simpler.

**The `change.call` method is deprecated.** Previously this was used to call a one-off function as a change method. Now this behavior is equivalent to calling `editor.command(fn)` instead.

---

Fixes: https://github.com/ianstormtaylor/slate/issues/2334
Fixes: https://github.com/ianstormtaylor/slate/issues/2282
2018-10-27 12:18:23 -07:00

3.7 KiB

Utils

import {
  cloneFragment,
  findDOMNode,
  findDOMRange,
  findNode,
  findRange,
  getEventRange,
  getEventTransfer,
  setEventTransfer,
} from 'slate-react'

React-specific utility functions for Slate that may be useful in certain use cases.

Functions

cloneFragment

cloneFragment(event: DOMEvent|ReactEvent, editor: Editor, fragment: Document)

During a cut or copy event, sets fragment as the Slate document fragment to be copied.

function onCopy(event, editor, next) {
  const fragment = // ... create a fragment from a set of nodes ...

  if (fragment) {
    cloneFragment(event, editor, fragment)
    return true
  }
}

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:

function onCut(event, editor, next) {
  const fragment = // ... create a fragment from a set of nodes ...

  if (fragment) {
    cloneFragment(event, editor, fragment)
    window.requestAnimationFrame(() => {
      editor.delete()
    })
    return true
  }
}

findDOMNode

findDOMNode(node: Node) => DOMElement

Find the DOM node from a Slate Node. Modelled after React's built-in findDOMNode helper.

function componentDidUpdate() {
  const { node } = this.props
  const element = findDOMNode(node)
  // Do something with the DOM `element`...
}

findDOMRange

findDOMRange(range: Range) => DOMRange

Find the DOM range from a Slate Range.

function onChange(editor) {
  const { value } = editor
  const range = findDOMRange(value.selection)
  // Do something with the DOM `range`...
}

findNode

findNode(element: DOMElement, editor: Editor) => Node

Find the Slate node from a DOM element and Slate editor.

function onSomeNativeEvent(event) {
  const node = findNode(event.target, editor)
  // Do something with `node`...
}

findRange

findRange(selection: DOMSelection, editor: Editor) => Range findRange(range: DOMRange, editor: Editor) => Range

Find the Slate range from a DOM range or selection and a Slate editor.

function onSomeNativeEvent() {
  // You can find a range from a native DOM selection...
  const nativeSelection = window.getSelection()
  const range = findRange(nativeSelection, editor)

  // ...or from a native DOM range...
  const nativeRange = nativeSelection.getRangeAt(0)
  const range = findRange(nativeRange, editor)
}

getEventRange

getEventRange(event: DOMEvent|ReactEvent, editor: Editor) => Range

Get the affected Slate range from a DOM event and Slate editor.

function onDrop(event, editor, next) {
  const targetRange = getEventRange(event, editor)
  // Do something at the drop `targetRange`...
}

getEventTransfer

getEventTransfer(event: DOMEvent|ReactEvent) => Object

Get the Slate-related data from a DOM event and Slate value.

function onDrop(event, editor, next) {
  const transfer = getEventTransfer(event)
  const { type, node } = transfer

  if (type == 'node') {
    // Do something with `node`...
  }
}

setEventTransfer

setEventTransfer(event: DOMEvent|ReactEvent, type: String, data: Any)

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'.

function onDragStart(event, editor, next) {
  const { value } = editor
  const { startNode } = value
  setEventTransfer(event, 'node', startNode)
}