1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-16 20:24:01 +02:00

Reconcile dom node (#2801)

* Add reconcileDOMNode command fixes set-text-from-dom-node

* Fix linting and cleanup unused files

* Removed unnecessary console log
This commit is contained in:
Sunny Hirai
2019-05-17 15:26:48 -07:00
committed by GitHub
parent 48eb6700b7
commit 79a2d21571
6 changed files with 67 additions and 74 deletions

View File

@@ -1,4 +1,3 @@
import getSelectionFromDom from '../../utils/get-selection-from-dom'
import ElementSnapshot from './element-snapshot' import ElementSnapshot from './element-snapshot'
import SELECTORS from '../../constants/selectors' import SELECTORS from '../../constants/selectors'
@@ -51,7 +50,7 @@ export default class DomSnapshot {
} }
this.snapshot = new ElementSnapshot(elements) this.snapshot = new ElementSnapshot(elements)
this.selection = getSelectionFromDom(window, editor, domSelection) this.selection = editor.findSelection(domSelection)
} }
/** /**

View File

@@ -5,7 +5,6 @@ import pick from 'lodash/pick'
import { ANDROID_API_VERSION } from 'slate-dev-environment' import { ANDROID_API_VERSION } from 'slate-dev-environment'
import fixSelectionInZeroWidthBlock from './fix-selection-in-zero-width-block' import fixSelectionInZeroWidthBlock from './fix-selection-in-zero-width-block'
import getSelectionFromDom from '../../utils/get-selection-from-dom' import getSelectionFromDom from '../../utils/get-selection-from-dom'
import setTextFromDomNode from '../../utils/set-text-from-dom-node'
import isInputDataEnter from './is-input-data-enter' import isInputDataEnter from './is-input-data-enter'
import isInputDataLastChar from './is-input-data-last-char' import isInputDataLastChar from './is-input-data-last-char'
import DomSnapshot from './dom-snapshot' import DomSnapshot from './dom-snapshot'
@@ -133,10 +132,10 @@ function AndroidPlugin() {
function reconcile(window, editor, { from }) { function reconcile(window, editor, { from }) {
debug.reconcile({ from }) debug.reconcile({ from })
const domSelection = window.getSelection() const domSelection = window.getSelection()
const selection = getSelectionFromDom(window, editor, domSelection) const selection = editor.findSelection(domSelection)
nodes.forEach(node => { nodes.forEach(node => {
setTextFromDomNode(window, editor, node) editor.reconcileDOMNode(node)
}) })
editor.select(selection) editor.select(selection)

View File

@@ -8,7 +8,6 @@ import { IS_IOS, IS_IE, IS_EDGE } from 'slate-dev-environment'
import cloneFragment from '../../utils/clone-fragment' import cloneFragment from '../../utils/clone-fragment'
import getEventTransfer from '../../utils/get-event-transfer' import getEventTransfer from '../../utils/get-event-transfer'
import setEventTransfer from '../../utils/set-event-transfer' import setEventTransfer from '../../utils/set-event-transfer'
import setTextFromDomNode from '../../utils/set-text-from-dom-node'
/** /**
* Debug. * Debug.
@@ -432,7 +431,7 @@ function AfterPlugin(options = {}) {
} }
const { anchorNode } = domSelection const { anchorNode } = domSelection
setTextFromDomNode(window, editor, anchorNode) editor.reconcileDOMNode(anchorNode)
next() next()
} }

View File

@@ -0,0 +1,60 @@
/**
* A set of commands for the React plugin.
*
* @return {Object}
*/
function CommandsPlugin() {
/**
* reconcileDOMNode takes text from inside the `domNode` and uses it to set
* the text inside the matching `node` in Slate.
*
* @param {Window} window
* @param {Editor} editor
* @param {Node} domNode
*/
function reconcileDOMNode(editor, domNode) {
const { value } = editor
const { document, selection } = value
const domElement = domNode.parentElement.closest('[data-key]')
const point = editor.findPoint(domElement, 0)
const node = document.getDescendant(point.path)
const block = document.getClosestBlock(point.path)
// Get text information
const { text } = node
let { textContent: domText } = domElement
const isLastNode = block.nodes.last() === node
const lastChar = domText.charAt(domText.length - 1)
// COMPAT: If this is the last leaf, and the DOM text ends in a new line,
// we will have added another new line in <Leaf>'s render method to account
// for browsers collapsing a single trailing new lines, so remove it.
if (isLastNode && lastChar === '\n') {
domText = domText.slice(0, -1)
}
// If the text is no different, abort.
if (text === domText) return
let entire = selection
.moveAnchorTo(point.path, 0)
.moveFocusTo(point.path, text.length)
entire = document.resolveRange(entire)
// Change the current value to have the leaf's text replaced.
editor.insertTextAtRange(entire, domText, node.marks)
return
}
return {
commands: {
reconcileDOMNode,
},
}
}
export default CommandsPlugin

View File

@@ -2,6 +2,7 @@ import PlaceholderPlugin from 'slate-react-placeholder'
import EditorPropsPlugin from './editor-props' import EditorPropsPlugin from './editor-props'
import RenderingPlugin from './rendering' import RenderingPlugin from './rendering'
import CommandsPlugin from './commands'
import QueriesPlugin from './queries' import QueriesPlugin from './queries'
import DOMPlugin from '../dom' import DOMPlugin from '../dom'
import RestoreDOMPlugin from './restore-dom' import RestoreDOMPlugin from './restore-dom'
@@ -16,6 +17,7 @@ import RestoreDOMPlugin from './restore-dom'
function ReactPlugin(options = {}) { function ReactPlugin(options = {}) {
const { placeholder = '', plugins = [] } = options const { placeholder = '', plugins = [] } = options
const renderingPlugin = RenderingPlugin(options) const renderingPlugin = RenderingPlugin(options)
const commandsPlugin = CommandsPlugin(options)
const queriesPlugin = QueriesPlugin(options) const queriesPlugin = QueriesPlugin(options)
const editorPropsPlugin = EditorPropsPlugin(options) const editorPropsPlugin = EditorPropsPlugin(options)
const domPlugin = DOMPlugin({ const domPlugin = DOMPlugin({
@@ -36,6 +38,7 @@ function ReactPlugin(options = {}) {
restoreDomPlugin, restoreDomPlugin,
placeholderPlugin, placeholderPlugin,
renderingPlugin, renderingPlugin,
commandsPlugin,
queriesPlugin, queriesPlugin,
] ]
} }

View File

@@ -1,67 +0,0 @@
import findPoint from './find-point'
/**
* setTextFromDomNode lets us take a domNode and reconcile the text in the
* editor's Document such that it reflects the text in the DOM. This is the
* opposite of what the Editor usually does which takes the Editor's Document
* and React modifies the DOM to match. The purpose of this method is for
* composition changes where we don't know what changes the user made by
* looking at events. Instead we wait until the DOM is in a safe state, we
* read from it, and update the Editor's Document.
*
* @param {Window} window
* @param {Editor} editor
* @param {Node} domNode
*/
export default function setTextFromDomNode(window, editor, domNode) {
const point = findPoint(domNode, 0, editor)
if (!point) return
// Get the text node and leaf in question.
const { value } = editor
const { document, selection } = value
const node = document.getDescendant(point.path)
const block = document.getClosestBlock(point.path)
const leaves = node.getLeaves()
const lastText = block.getLastText()
const lastLeaf = leaves.last()
let start = 0
let end = 0
const leaf =
leaves.find(r => {
start = end
end += r.text.length
if (end > point.offset) return true
}) || lastLeaf
// Get the text information.
const { text } = leaf
let { textContent } = domNode
const isLastText = node === lastText
const isLastLeaf = leaf === lastLeaf
const lastChar = textContent.charAt(textContent.length - 1)
// COMPAT: If this is the last leaf, and the DOM text ends in a new line,
// we will have added another new line in <Leaf>'s render method to account
// for browsers collapsing a single trailing new lines, so remove it.
if (isLastText && isLastLeaf && lastChar === '\n') {
textContent = textContent.slice(0, -1)
}
// If the text is no different, abort.
if (textContent === text) return
// Determine what the selection should be after changing the text.
// const delta = textContent.length - text.length
// const corrected = selection.moveToEnd().moveForward(delta)
let entire = selection
.moveAnchorTo(point.path, start)
.moveFocusTo(point.path, end)
entire = document.resolveRange(entire)
// Change the current value to have the leaf's text replaced.
editor.insertTextAtRange(entire, textContent, leaf.marks)
}