mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 06:31:28 +02:00
Remove is native (#1088)
* start removing isNative * more * remove more isNative logic * fix linter
This commit is contained in:
@@ -7,19 +7,6 @@
|
||||
|
||||
const Changes = {}
|
||||
|
||||
/**
|
||||
* Set the `isNative` flag on the underlying state to prevent re-renders.
|
||||
*
|
||||
* @param {Change} change
|
||||
* @param {Boolean} value
|
||||
*/
|
||||
|
||||
Changes.setIsNative = (change, value) => {
|
||||
let { state } = change
|
||||
state = state.set('isNative', value)
|
||||
change.state = state
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on the top-level state's data.
|
||||
*
|
||||
|
@@ -91,33 +91,6 @@ class Content extends React.Component {
|
||||
this.tmp.forces = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the component update?
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} state
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
shouldComponentUpdate = (props, state) => {
|
||||
// If the readOnly state has changed, we need to re-render so that
|
||||
// the cursor will be added or removed again.
|
||||
if (props.readOnly != this.props.readOnly) return true
|
||||
|
||||
// If the state has been changed natively, never re-render, or else we'll
|
||||
// end up duplicating content.
|
||||
if (props.state.isNative) return false
|
||||
|
||||
return (
|
||||
props.className != this.props.className ||
|
||||
props.schema != this.props.schema ||
|
||||
props.autoCorrect != this.props.autoCorrect ||
|
||||
props.spellCheck != this.props.spellCheck ||
|
||||
props.state != this.props.state ||
|
||||
props.style != this.props.style
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When the editor first mounts in the DOM we need to:
|
||||
*
|
||||
@@ -722,7 +695,6 @@ class Content extends React.Component {
|
||||
// If there are no ranges, the editor was blurred natively.
|
||||
if (!native.rangeCount) {
|
||||
data.selection = selection.set('isFocused', false)
|
||||
data.isNative = true
|
||||
}
|
||||
|
||||
// Otherwise, determine the Slate selection from the native one.
|
||||
|
@@ -1,12 +1,10 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Types from 'prop-types'
|
||||
|
||||
import OffsetKey from '../utils/offset-key'
|
||||
import SlateTypes from '../utils/prop-types'
|
||||
import findDeepestNode from '../utils/find-deepest-node'
|
||||
import { IS_FIREFOX } from '../constants/environment'
|
||||
|
||||
/**
|
||||
@@ -45,18 +43,6 @@ class Leaf extends React.Component {
|
||||
text: Types.string.isRequired,
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.tmp = {}
|
||||
this.tmp.renders = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug.
|
||||
*
|
||||
@@ -86,12 +72,6 @@ class Leaf extends React.Component {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the DOM text does not equal the `text` property, re-render, this can
|
||||
// happen because React gets out of sync when previously natively rendered.
|
||||
const el = findDeepestNode(ReactDOM.findDOMNode(this))
|
||||
const text = this.renderText(props)
|
||||
if (el.textContent != text) return true
|
||||
|
||||
// Otherwise, don't update.
|
||||
return false
|
||||
}
|
||||
@@ -110,16 +90,10 @@ class Leaf extends React.Component {
|
||||
index
|
||||
})
|
||||
|
||||
// Increment the renders key, which forces a re-render whenever this
|
||||
// component is told it should update. This is required because "native"
|
||||
// renders where we don't update the leaves cause React's internal state to
|
||||
// get out of sync, causing it to not realize the DOM needs updating.
|
||||
this.tmp.renders++
|
||||
|
||||
this.debug('render', { props })
|
||||
|
||||
return (
|
||||
<span key={this.tmp.renders} data-offset-key={offsetKey}>
|
||||
<span data-offset-key={offsetKey}>
|
||||
{this.renderMarks(props)}
|
||||
</span>
|
||||
)
|
||||
|
@@ -45,7 +45,6 @@ class Change {
|
||||
this.state = state
|
||||
this.operations = []
|
||||
this.flags = pick(attrs, ['merge', 'save'])
|
||||
this.setIsNative(attrs.isNative === undefined ? false : attrs.isNative)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,7 +20,6 @@ const DEFAULTS = {
|
||||
selection: Selection.create(),
|
||||
history: History.create(),
|
||||
data: new Map(),
|
||||
isNative: false,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,7 +1,6 @@
|
||||
|
||||
import Base64 from '../serializers/base-64'
|
||||
import Block from '../models/block'
|
||||
import Character from '../models/character'
|
||||
import Content from '../components/content'
|
||||
import Debug from 'debug'
|
||||
import Inline from '../models/inline'
|
||||
@@ -50,10 +49,6 @@ function Plugin(options = {}) {
|
||||
const schema = editor.getSchema()
|
||||
const prevState = editor.getState()
|
||||
|
||||
// PERF: Skip normalizing if the change is native, since we know that it
|
||||
// can't have changed anything that requires a core schema fix.
|
||||
if (state.isNative) return
|
||||
|
||||
// PERF: Skip normalizing if the document hasn't changed, since the core
|
||||
// schema only normalizes changes to the document, not selection.
|
||||
if (prevState && state.document == prevState.document) return
|
||||
@@ -63,8 +58,7 @@ function Plugin(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* On before input, see if we can let the browser continue with it's native
|
||||
* input behavior, to avoid a re-render for performance.
|
||||
* On before input, correct any browser inconsistencies.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {Object} data
|
||||
@@ -73,110 +67,39 @@ function Plugin(options = {}) {
|
||||
*/
|
||||
|
||||
function onBeforeInput(e, data, change, editor) {
|
||||
debug('onBeforeInput', { data })
|
||||
e.preventDefault()
|
||||
|
||||
const { state } = change
|
||||
const { document, startKey, startBlock, startOffset, startInline, startText } = state
|
||||
const pText = startBlock.getPreviousText(startKey)
|
||||
const pInline = pText && startBlock.getClosestInline(pText.key)
|
||||
const nText = startBlock.getNextText(startKey)
|
||||
const nInline = nText && startBlock.getClosestInline(nText.key)
|
||||
const { selection } = state
|
||||
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
||||
|
||||
// Determine what the characters would be if natively inserted.
|
||||
const schema = editor.getSchema()
|
||||
const decorators = document.getDescendantDecorators(startKey, schema)
|
||||
const initialChars = startText.getDecorations(decorators)
|
||||
const prevChar = startOffset === 0 ? null : initialChars.get(startOffset - 1)
|
||||
const nextChar = startOffset === initialChars.size ? null : initialChars.get(startOffset)
|
||||
const char = Character.create({
|
||||
text: e.data,
|
||||
// When cursor is at start of a range of marks, without preceding text,
|
||||
// the native behavior is to insert inside the range of marks.
|
||||
marks: (
|
||||
(prevChar && prevChar.marks) ||
|
||||
(nextChar && nextChar.marks) ||
|
||||
[]
|
||||
)
|
||||
})
|
||||
|
||||
const chars = initialChars.insert(startOffset, char)
|
||||
|
||||
// COMPAT: In iOS, when choosing from the predictive text suggestions, the
|
||||
// native selection will be changed to span the existing word, so that the word
|
||||
// is replaced. But the `select` event for this change doesn't fire until after
|
||||
// the `beforeInput` event, even though the native selection is updated. So we
|
||||
// need to manually adjust the selection to be in sync. (03/18/2017)
|
||||
// COMPAT: In iOS, when using predictive text suggestions, the native
|
||||
// selection will be changed to span the existing word, so that the word is
|
||||
// replaced. But the `select` fires after the `beforeInput` event, even
|
||||
// though the native selection is updated. So we need to manually check if
|
||||
// the selection has gotten out of sync, and adjust it if so. (03/18/2017)
|
||||
const window = getWindow(e.target)
|
||||
const native = window.getSelection()
|
||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
||||
const anchorPoint = getPoint(anchorNode, anchorOffset, state, editor)
|
||||
const focusPoint = getPoint(focusNode, focusOffset, state, editor)
|
||||
if (anchorPoint && focusPoint) {
|
||||
const { selection } = state
|
||||
if (
|
||||
selection.anchorKey !== anchorPoint.key ||
|
||||
selection.anchorOffset !== anchorPoint.offset ||
|
||||
selection.focusKey !== focusPoint.key ||
|
||||
selection.focusOffset !== focusPoint.offset
|
||||
) {
|
||||
change = change
|
||||
.select({
|
||||
anchorKey: anchorPoint.key,
|
||||
anchorOffset: anchorPoint.offset,
|
||||
focusKey: focusPoint.key,
|
||||
focusOffset: focusPoint.offset
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Determine what the characters should be, if not natively inserted.
|
||||
change.insertText(e.data)
|
||||
const next = change.state
|
||||
const nextText = next.startText
|
||||
const nextChars = nextText.getDecorations(decorators)
|
||||
|
||||
// We do not have to re-render if the current selection is collapsed, the
|
||||
// current node is not empty, there are no marks on the cursor, the cursor
|
||||
// is not at the edge of an inline node, the cursor isn't at the starting
|
||||
// edge of a text node after an inline node, and the natively inserted
|
||||
// characters would be the same as the non-native.
|
||||
const isNative = (
|
||||
// If the selection is expanded, we don't know what the edit will look
|
||||
// like so we can't let it happen natively.
|
||||
(state.isCollapsed) &&
|
||||
// If the selection has marks, then we need to render it non-natively
|
||||
// because we need to create the new marks as well.
|
||||
(state.selection.marks == null) &&
|
||||
// If the text node in question has no content, browsers might do weird
|
||||
// things so we need to insert it normally instead.
|
||||
(state.startText.text != '') &&
|
||||
// COMPAT: Browsers do weird things when typing at the edges of inline
|
||||
// nodes, so we can't let them render natively. (?)
|
||||
(!startInline || !state.selection.isAtStartOf(startInline)) &&
|
||||
(!startInline || !state.selection.isAtEndOf(startInline)) &&
|
||||
// COMPAT: In Chrome & Safari, it isn't possible to have a selection at
|
||||
// the starting edge of a text node after another inline node. It will
|
||||
// have been automatically changed. So we can't render natively because
|
||||
// the cursor isn't technically in the right spot. (2016/12/01)
|
||||
(!(pInline && !pInline.isVoid && startOffset == 0)) &&
|
||||
(!(nInline && !nInline.isVoid && startOffset == startText.text.length)) &&
|
||||
// COMPAT: When inserting a Space character, Chrome will sometimes
|
||||
// split the text node into two adjacent text nodes. See:
|
||||
// https://github.com/ianstormtaylor/slate/issues/938
|
||||
(!(e.data === ' ' && IS_CHROME)) &&
|
||||
// If the
|
||||
(chars.equals(nextChars))
|
||||
const a = getPoint(native.anchorNode, native.anchorOffset, state, editor)
|
||||
const f = getPoint(native.focusNode, native.focusOffset, state, editor)
|
||||
const hasMismatch = a && f && (
|
||||
anchorKey != a.key ||
|
||||
anchorOffset != a.offset ||
|
||||
focusKey != f.key ||
|
||||
focusOffset != f.offset
|
||||
)
|
||||
|
||||
// If `isNative`, set the flag on the change.
|
||||
if (isNative) {
|
||||
change.setIsNative(true)
|
||||
if (hasMismatch) {
|
||||
change.select({
|
||||
anchorKey: a.key,
|
||||
anchorOffset: a.offset,
|
||||
focusKey: f.key,
|
||||
focusOffset: f.offset
|
||||
})
|
||||
}
|
||||
|
||||
// Otherwise, prevent default so that the DOM remains untouched.
|
||||
else {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
debug('onBeforeInput', { data, isNative })
|
||||
change.insertText(e.data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user