mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-23 15:32:59 +02:00
resolve leaf component issues with focus and blur transformations - closes #297
This commit is contained in:
@@ -5,6 +5,24 @@ import React from 'react'
|
|||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import getWindow from 'get-window'
|
import getWindow from 'get-window'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the child is a descendant of parent.
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param child
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isDescendant(parent, child) {
|
||||||
|
if (!child || !parent) return false
|
||||||
|
let node = child.parentNode
|
||||||
|
while (node != null) {
|
||||||
|
if (node == parent) return true
|
||||||
|
node = node.parentNode
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debugger.
|
* Debugger.
|
||||||
*
|
*
|
||||||
@@ -136,9 +154,6 @@ class Leaf extends React.Component {
|
|||||||
const { state, ranges, isVoid } = this.props
|
const { state, ranges, isVoid } = this.props
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
|
|
||||||
// If the selection is blurred we have nothing to do.
|
|
||||||
if (selection.isBlurred) return
|
|
||||||
|
|
||||||
let { anchorOffset, focusOffset } = selection
|
let { anchorOffset, focusOffset } = selection
|
||||||
const { node, index } = this.props
|
const { node, index } = this.props
|
||||||
const { start, end } = OffsetKey.findBounds(index, ranges)
|
const { start, end } = OffsetKey.findBounds(index, ranges)
|
||||||
@@ -148,6 +163,36 @@ class Leaf extends React.Component {
|
|||||||
const hasFocus = selection.hasFocusBetween(node, start, end)
|
const hasFocus = selection.hasFocusBetween(node, start, end)
|
||||||
if (!hasAnchor && !hasFocus) return
|
if (!hasAnchor && !hasFocus) return
|
||||||
|
|
||||||
|
// We have a selection to render, so prepare a few things...
|
||||||
|
const ref = ReactDOM.findDOMNode(this)
|
||||||
|
const el = findDeepestNode(ref)
|
||||||
|
const window = getWindow(el)
|
||||||
|
|
||||||
|
// If no selection is defined we're probably in a node environment and we can skip the selection magic.
|
||||||
|
if (!window || !window.getSelection) return
|
||||||
|
|
||||||
|
const native = window.getSelection()
|
||||||
|
const parent = ref.closest('[contenteditable]')
|
||||||
|
|
||||||
|
// If the selection is blurred but the DOM selection still focuses this leaf,
|
||||||
|
// we need to clean up the ranges and blur the contenteditable.
|
||||||
|
if (selection.isBlurred && (hasAnchor || hasFocus)) {
|
||||||
|
// We need to make sure that the selection from our state is up-to-date with the native selection.
|
||||||
|
// We can do this by checking if native.anchorNode is a descendant of our contenteditable parent, which is this
|
||||||
|
// slate instance. If it's not, the selection is no longer under this instance's responsibility and not further
|
||||||
|
// action is required.
|
||||||
|
if (!isDescendant(parent, native.anchorNode)) return
|
||||||
|
|
||||||
|
// Apparently our selection is blurred, but the DOM still has ranges in the nodes we manage. To fix this,
|
||||||
|
// we blur the contenteditable and remove all ranges.
|
||||||
|
native.removeAllRanges()
|
||||||
|
if (parent) parent.blur()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the selection is blurred we have nothing to do.
|
||||||
|
if (selection.isBlurred) return
|
||||||
|
|
||||||
// If the leaf is a void leaf, ensure that it has no width. This is due to
|
// If the leaf is a void leaf, ensure that it has no width. This is due to
|
||||||
// void nodes always rendering an empty leaf, for browser compatibility.
|
// void nodes always rendering an empty leaf, for browser compatibility.
|
||||||
if (isVoid) {
|
if (isVoid) {
|
||||||
@@ -155,10 +200,10 @@ class Leaf extends React.Component {
|
|||||||
focusOffset = 0
|
focusOffset = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a selection to render, so prepare a few things...
|
// In firefox it is not enough to create a range, you also need to focus the contenteditable element.
|
||||||
const el = findDeepestNode(ReactDOM.findDOMNode(this))
|
function focus() {
|
||||||
const window = getWindow(el)
|
if (parent) setTimeout(() => parent.focus(), 0)
|
||||||
const native = window.getSelection()
|
}
|
||||||
|
|
||||||
// If both the start and end are here, set the selection all at once.
|
// If both the start and end are here, set the selection all at once.
|
||||||
if (hasAnchor && hasFocus) {
|
if (hasAnchor && hasFocus) {
|
||||||
@@ -167,6 +212,7 @@ class Leaf extends React.Component {
|
|||||||
range.setStart(el, anchorOffset - start)
|
range.setStart(el, anchorOffset - start)
|
||||||
native.addRange(range)
|
native.addRange(range)
|
||||||
native.extend(el, focusOffset - start)
|
native.extend(el, focusOffset - start)
|
||||||
|
focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +225,10 @@ class Leaf extends React.Component {
|
|||||||
const range = window.document.createRange()
|
const range = window.document.createRange()
|
||||||
range.setStart(el, anchorOffset - start)
|
range.setStart(el, anchorOffset - start)
|
||||||
native.addRange(range)
|
native.addRange(range)
|
||||||
|
focus()
|
||||||
} else if (hasFocus) {
|
} else if (hasFocus) {
|
||||||
native.extend(el, focusOffset - start)
|
native.extend(el, focusOffset - start)
|
||||||
|
focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +242,7 @@ class Leaf extends React.Component {
|
|||||||
const range = window.document.createRange()
|
const range = window.document.createRange()
|
||||||
range.setStart(el, focusOffset - start)
|
range.setStart(el, focusOffset - start)
|
||||||
native.addRange(range)
|
native.addRange(range)
|
||||||
|
focus()
|
||||||
} else if (hasAnchor) {
|
} else if (hasAnchor) {
|
||||||
const endNode = native.focusNode
|
const endNode = native.focusNode
|
||||||
const endOffset = native.focusOffset
|
const endOffset = native.focusOffset
|
||||||
@@ -202,6 +251,7 @@ class Leaf extends React.Component {
|
|||||||
range.setStart(el, anchorOffset - start)
|
range.setStart(el, anchorOffset - start)
|
||||||
native.addRange(range)
|
native.addRange(range)
|
||||||
native.extend(endNode, endOffset)
|
native.extend(endNode, endOffset)
|
||||||
|
focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user