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

Refactor to utils (#2522)

* Refactored set-selection-from-dom into utils as prep for Android support

* Added and refactored to use set-text-from-dom-node with improved set selection after input
This commit is contained in:
Sunny Hirai
2019-01-04 12:02:33 -08:00
committed by GitHub
parent c6a48a1b9e
commit 92c1ffbcd3
3 changed files with 139 additions and 128 deletions

View File

@@ -8,11 +8,12 @@ import { IS_IOS } from 'slate-dev-environment'
import cloneFragment from '../utils/clone-fragment' import cloneFragment from '../utils/clone-fragment'
import findDOMNode from '../utils/find-dom-node' import findDOMNode from '../utils/find-dom-node'
import findNode from '../utils/find-node' import findNode from '../utils/find-node'
import findPoint from '../utils/find-point'
import findRange from '../utils/find-range' import findRange from '../utils/find-range'
import getEventRange from '../utils/get-event-range' import getEventRange from '../utils/get-event-range'
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 setSelectionFromDom from '../utils/set-selection-from-dom'
import setTextFromDomNode from '../utils/set-text-from-dom-node'
/** /**
* Debug. * Debug.
@@ -409,62 +410,15 @@ function AfterPlugin(options = {}) {
*/ */
function onInput(event, editor, next) { function onInput(event, editor, next) {
debug('onInput')
const window = getWindow(event.target) const window = getWindow(event.target)
const { value } = editor
// Get the selection point. // Get the selection point.
const native = window.getSelection() const selection = window.getSelection()
const { anchorNode } = native const { anchorNode } = selection
const point = findPoint(anchorNode, 0, editor)
if (!point) return next()
// Get the text node and leaf in question. setTextFromDomNode(window, editor, anchorNode)
const { document, selection } = value setSelectionFromDom(window, editor, selection)
const node = document.getDescendant(point.key)
const block = document.getClosestBlock(node.key)
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 } = anchorNode
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 next()
debug('onInput', { event })
// 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.key, start)
.moveFocusTo(point.key, end)
entire = document.resolveRange(entire)
// Change the current value to have the leaf's text replaced.
editor.insertTextAtRange(entire, textContent, leaf.marks).select(corrected)
next() next()
} }
@@ -677,82 +631,9 @@ function AfterPlugin(options = {}) {
function onSelect(event, editor, next) { function onSelect(event, editor, next) {
debug('onSelect', { event }) debug('onSelect', { event })
const window = getWindow(event.target) const window = getWindow(event.target)
const { value } = editor const selection = window.getSelection()
const { document } = value setSelectionFromDom(window, editor, selection)
const native = window.getSelection()
// If there are no ranges, the editor was blurred natively.
if (!native.rangeCount) {
editor.blur()
return
}
// Otherwise, determine the Slate selection from the native one.
let range = findRange(native, editor)
if (!range) {
return
}
const { anchor, focus } = range
const anchorText = document.getNode(anchor.key)
const focusText = document.getNode(focus.key)
const anchorInline = document.getClosestInline(anchor.key)
const focusInline = document.getClosestInline(focus.key)
const focusBlock = document.getClosestBlock(focus.key)
const anchorBlock = document.getClosestBlock(anchor.key)
// COMPAT: If the anchor point is at the start of a non-void, and the
// focus point is inside a void node with an offset that isn't `0`, set
// the focus offset to `0`. This is due to void nodes <span>'s being
// positioned off screen, resulting in the offset always being greater
// than `0`. Since we can't know what it really should be, and since an
// offset of `0` is less destructive because it creates a hanging
// selection, go with `0`. (2017/09/07)
if (
anchorBlock &&
!editor.isVoid(anchorBlock) &&
anchor.offset == 0 &&
focusBlock &&
editor.isVoid(focusBlock) &&
focus.offset != 0
) {
range = range.setFocus(focus.setOffset(0))
}
// COMPAT: If the selection is at the end of a non-void inline node, and
// there is a node after it, put it in the node after instead. This
// standardizes the behavior, since it's indistinguishable to the user.
if (
anchorInline &&
!editor.isVoid(anchorInline) &&
anchor.offset == anchorText.text.length
) {
const block = document.getClosestBlock(anchor.key)
const nextText = block.getNextText(anchor.key)
if (nextText) range = range.moveAnchorTo(nextText.key, 0)
}
if (
focusInline &&
!editor.isVoid(focusInline) &&
focus.offset == focusText.text.length
) {
const block = document.getClosestBlock(focus.key)
const nextText = block.getNextText(focus.key)
if (nextText) range = range.moveFocusTo(nextText.key, 0)
}
let selection = document.createSelection(range)
selection = selection.setIsFocused(true)
// Preserve active marks from the current selection.
// They will be cleared by `editor.select` if the selection actually moved.
selection = selection.set('marks', value.selection.marks)
editor.select(selection)
next() next()
} }

View File

@@ -0,0 +1,77 @@
import findRange from './find-range'
export default function setSelectionFromDOM(window, editor, domSelection) {
const { value } = editor
const { document } = value
// If there are no ranges, the editor was blurred natively.
if (!domSelection.rangeCount) {
editor.blur()
return
}
// Otherwise, determine the Slate selection from the native one.
let range = findRange(domSelection, editor)
if (!range) {
return
}
const { anchor, focus } = range
const anchorText = document.getNode(anchor.key)
const focusText = document.getNode(focus.key)
const anchorInline = document.getClosestInline(anchor.key)
const focusInline = document.getClosestInline(focus.key)
const focusBlock = document.getClosestBlock(focus.key)
const anchorBlock = document.getClosestBlock(anchor.key)
// COMPAT: If the anchor point is at the start of a non-void, and the
// focus point is inside a void node with an offset that isn't `0`, set
// the focus offset to `0`. This is due to void nodes <span>'s being
// positioned off screen, resulting in the offset always being greater
// than `0`. Since we can't know what it really should be, and since an
// offset of `0` is less destructive because it creates a hanging
// selection, go with `0`. (2017/09/07)
if (
anchorBlock &&
!editor.isVoid(anchorBlock) &&
anchor.offset == 0 &&
focusBlock &&
editor.isVoid(focusBlock) &&
focus.offset != 0
) {
range = range.setFocus(focus.setOffset(0))
}
// COMPAT: If the selection is at the end of a non-void inline node, and
// there is a node after it, put it in the node after instead. This
// standardizes the behavior, since it's indistinguishable to the user.
if (
anchorInline &&
!editor.isVoid(anchorInline) &&
anchor.offset == anchorText.text.length
) {
const block = document.getClosestBlock(anchor.key)
const nextText = block.getNextText(anchor.key)
if (nextText) range = range.moveAnchorTo(nextText.key, 0)
}
if (
focusInline &&
!editor.isVoid(focusInline) &&
focus.offset == focusText.text.length
) {
const block = document.getClosestBlock(focus.key)
const nextText = block.getNextText(focus.key)
if (nextText) range = range.moveFocusTo(nextText.key, 0)
}
let selection = document.createSelection(range)
selection = selection.setIsFocused(true)
// Preserve active marks from the current selection.
// They will be cleared by `editor.select` if the selection actually moved.
selection = selection.set('marks', value.selection.marks)
editor.select(selection)
}

View File

@@ -0,0 +1,53 @@
import findPoint from './find-point'
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.key)
const block = document.getClosestBlock(node.key)
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.key, start)
.moveFocusTo(point.key, end)
entire = document.resolveRange(entire)
// Change the current value to have the leaf's text replaced.
editor.insertTextAtRange(entire, textContent, leaf.marks)
}