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 = {}
|
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.
|
* Set `properties` on the top-level state's data.
|
||||||
*
|
*
|
||||||
|
@@ -91,33 +91,6 @@ class Content extends React.Component {
|
|||||||
this.tmp.forces = 0
|
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:
|
* 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 there are no ranges, the editor was blurred natively.
|
||||||
if (!native.rangeCount) {
|
if (!native.rangeCount) {
|
||||||
data.selection = selection.set('isFocused', false)
|
data.selection = selection.set('isFocused', false)
|
||||||
data.isNative = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, determine the Slate selection from the native one.
|
// Otherwise, determine the Slate selection from the native one.
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
import Types from 'prop-types'
|
import Types from 'prop-types'
|
||||||
|
|
||||||
import OffsetKey from '../utils/offset-key'
|
import OffsetKey from '../utils/offset-key'
|
||||||
import SlateTypes from '../utils/prop-types'
|
import SlateTypes from '../utils/prop-types'
|
||||||
import findDeepestNode from '../utils/find-deepest-node'
|
|
||||||
import { IS_FIREFOX } from '../constants/environment'
|
import { IS_FIREFOX } from '../constants/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,18 +43,6 @@ class Leaf extends React.Component {
|
|||||||
text: Types.string.isRequired,
|
text: Types.string.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param {Object} props
|
|
||||||
*/
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.tmp = {}
|
|
||||||
this.tmp.renders = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
*
|
*
|
||||||
@@ -86,12 +72,6 @@ class Leaf extends React.Component {
|
|||||||
return true
|
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.
|
// Otherwise, don't update.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -110,16 +90,10 @@ class Leaf extends React.Component {
|
|||||||
index
|
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 })
|
this.debug('render', { props })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span key={this.tmp.renders} data-offset-key={offsetKey}>
|
<span data-offset-key={offsetKey}>
|
||||||
{this.renderMarks(props)}
|
{this.renderMarks(props)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
@@ -45,7 +45,6 @@ class Change {
|
|||||||
this.state = state
|
this.state = state
|
||||||
this.operations = []
|
this.operations = []
|
||||||
this.flags = pick(attrs, ['merge', 'save'])
|
this.flags = pick(attrs, ['merge', 'save'])
|
||||||
this.setIsNative(attrs.isNative === undefined ? false : attrs.isNative)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -20,7 +20,6 @@ const DEFAULTS = {
|
|||||||
selection: Selection.create(),
|
selection: Selection.create(),
|
||||||
history: History.create(),
|
history: History.create(),
|
||||||
data: new Map(),
|
data: new Map(),
|
||||||
isNative: false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import Base64 from '../serializers/base-64'
|
import Base64 from '../serializers/base-64'
|
||||||
import Block from '../models/block'
|
import Block from '../models/block'
|
||||||
import Character from '../models/character'
|
|
||||||
import Content from '../components/content'
|
import Content from '../components/content'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import Inline from '../models/inline'
|
import Inline from '../models/inline'
|
||||||
@@ -50,10 +49,6 @@ function Plugin(options = {}) {
|
|||||||
const schema = editor.getSchema()
|
const schema = editor.getSchema()
|
||||||
const prevState = editor.getState()
|
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
|
// PERF: Skip normalizing if the document hasn't changed, since the core
|
||||||
// schema only normalizes changes to the document, not selection.
|
// schema only normalizes changes to the document, not selection.
|
||||||
if (prevState && state.document == prevState.document) return
|
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
|
* On before input, correct any browser inconsistencies.
|
||||||
* input behavior, to avoid a re-render for performance.
|
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
@@ -73,110 +67,39 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onBeforeInput(e, data, change, editor) {
|
function onBeforeInput(e, data, change, editor) {
|
||||||
|
debug('onBeforeInput', { data })
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
const { state } = change
|
const { state } = change
|
||||||
const { document, startKey, startBlock, startOffset, startInline, startText } = state
|
const { selection } = state
|
||||||
const pText = startBlock.getPreviousText(startKey)
|
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
||||||
const pInline = pText && startBlock.getClosestInline(pText.key)
|
|
||||||
const nText = startBlock.getNextText(startKey)
|
|
||||||
const nInline = nText && startBlock.getClosestInline(nText.key)
|
|
||||||
|
|
||||||
// Determine what the characters would be if natively inserted.
|
// COMPAT: In iOS, when using predictive text suggestions, the native
|
||||||
const schema = editor.getSchema()
|
// selection will be changed to span the existing word, so that the word is
|
||||||
const decorators = document.getDescendantDecorators(startKey, schema)
|
// replaced. But the `select` fires after the `beforeInput` event, even
|
||||||
const initialChars = startText.getDecorations(decorators)
|
// though the native selection is updated. So we need to manually check if
|
||||||
const prevChar = startOffset === 0 ? null : initialChars.get(startOffset - 1)
|
// the selection has gotten out of sync, and adjust it if so. (03/18/2017)
|
||||||
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)
|
|
||||||
const window = getWindow(e.target)
|
const window = getWindow(e.target)
|
||||||
const native = window.getSelection()
|
const native = window.getSelection()
|
||||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
const a = getPoint(native.anchorNode, native.anchorOffset, state, editor)
|
||||||
const anchorPoint = getPoint(anchorNode, anchorOffset, state, editor)
|
const f = getPoint(native.focusNode, native.focusOffset, state, editor)
|
||||||
const focusPoint = getPoint(focusNode, focusOffset, state, editor)
|
const hasMismatch = a && f && (
|
||||||
if (anchorPoint && focusPoint) {
|
anchorKey != a.key ||
|
||||||
const { selection } = state
|
anchorOffset != a.offset ||
|
||||||
if (
|
focusKey != f.key ||
|
||||||
selection.anchorKey !== anchorPoint.key ||
|
focusOffset != f.offset
|
||||||
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))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If `isNative`, set the flag on the change.
|
if (hasMismatch) {
|
||||||
if (isNative) {
|
change.select({
|
||||||
change.setIsNative(true)
|
anchorKey: a.key,
|
||||||
|
anchorOffset: a.offset,
|
||||||
|
focusKey: f.key,
|
||||||
|
focusOffset: f.offset
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, prevent default so that the DOM remains untouched.
|
change.insertText(e.data)
|
||||||
else {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('onBeforeInput', { data, isNative })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user