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

deprecate isVoid and related properties/methods (#2102)

#### Is this adding or improving a _feature_ or fixing a _bug_?

Improvement.

#### What's the new behavior?

This deprecates the `node.isVoid` property in favor of using `schema.isVoid(node)`, which will allow us to remove the hardcoded "void" concept from the data model, and have it depend on the schema instead. 

This allows you to build different kinds of editors, with different void semantics, depending on your use case, without having this information hardcoded in the data itself. Even switching the `isVoid` semantics on the fly based on a user toggling a setting for example.

#### How does this change work?

This is the first step, which just deprecates `node.isVoid`, and provides the new alternative of `schema.isVoid(node)`, while still using the `isVoid` value of nodes under the covers.

The next step is to change the logic to search the schema for real, and completely remove the `isVoid` value from nodes.

#### Have you checked that...?

<!-- 
Please run through this checklist for your pull request: 
-->

* [x] The new code matches the existing patterns and styles.
* [x] The tests pass with `yarn test`.
* [x] The linter passes with `yarn lint`. (Fix errors with `yarn prettier`.)
* [x] The relevant examples still work. (Run examples with `yarn watch`.)
This commit is contained in:
Ian Storm Taylor
2018-08-21 15:52:44 -07:00
committed by GitHub
parent 3ac1b13ce3
commit 00d5785226
18 changed files with 204 additions and 102 deletions

View File

@@ -126,11 +126,13 @@ class Leaf extends React.Component {
*/
renderText() {
const { block, node, parent, text, index, leaves } = this.props
const { block, node, editor, parent, text, index, leaves } = this.props
const { value } = editor
const { schema } = value
// COMPAT: Render text inside void nodes with a zero-width space.
// So the node can contain selection but the text is not visible.
if (parent.isVoid) {
if (schema.isVoid(parent)) {
return <span data-slate-zero-width="z">{'\u200B'}</span>
}

View File

@@ -131,7 +131,7 @@ class Node extends React.Component {
readOnly,
} = this.props
const { value } = editor
const { selection } = value
const { selection, schema } = value
const { stack } = editor
const indexes = node.getSelectionIndexes(selection, isSelected)
const decs = decorations.concat(node.getDecorations(stack))
@@ -184,7 +184,11 @@ class Node extends React.Component {
children,
})
return node.isVoid ? <Void {...this.props}>{element}</Void> : element
return schema.isVoid(node) ? (
<Void {...this.props}>{element}</Void>
) : (
element
)
}
/**

View File

@@ -163,9 +163,11 @@ function AfterPlugin() {
if (editor.props.readOnly) return true
const { value } = change
const { document } = value
const { document, schema } = value
const node = findNode(event.target, value)
const isVoid = node && (node.isVoid || document.hasVoidParent(node.key))
const ancestors = document.getAncestors(node.key)
const isVoid =
node && (schema.isVoid(node) || ancestors.some(a => schema.isVoid(a)))
if (isVoid) {
// COMPAT: In Chrome & Safari, selections that are at the zero offset of
@@ -209,10 +211,10 @@ function AfterPlugin() {
// If user cuts a void block node or a void inline node,
// manually removes it since selection is collapsed in this case.
const { value } = change
const { endBlock, endInline, selection } = value
const { endBlock, endInline, selection, schema } = value
const { isCollapsed } = selection
const isVoidBlock = endBlock && endBlock.isVoid && isCollapsed
const isVoidInline = endInline && endInline.isVoid && isCollapsed
const isVoidBlock = endBlock && schema.isVoid(endBlock) && isCollapsed
const isVoidInline = endInline && schema.isVoid(endInline) && isCollapsed
if (isVoidBlock) {
editor.change(c => c.removeNodeByKey(endBlock.key))
@@ -264,9 +266,11 @@ function AfterPlugin() {
isDraggingInternally = true
const { value } = change
const { document } = value
const { document, schema } = value
const node = findNode(event.target, value)
const isVoid = node && (node.isVoid || document.hasVoidParent(node.key))
const ancestors = document.getAncestors(node.key)
const isVoid =
node && (schema.isVoid(node) || ancestors.some(a => schema.isVoid(a)))
if (isVoid) {
const encoded = Base64.serializeNode(node, { preserveKeys: true })
@@ -290,7 +294,7 @@ function AfterPlugin() {
debug('onDrop', { event })
const { value } = change
const { document, selection } = value
const { document, selection, schema } = value
const window = getWindow(event.target)
let target = getEventRange(event, value)
if (!target) return
@@ -322,7 +326,7 @@ function AfterPlugin() {
if (type == 'text' || type == 'html') {
const { anchor } = target
let hasVoidParent = document.hasVoidParent(anchor.key)
let hasVoidParent = document.hasVoidParent(anchor.key, schema)
if (hasVoidParent) {
let n = document.getNode(anchor.key)
@@ -330,7 +334,7 @@ function AfterPlugin() {
while (hasVoidParent) {
n = document.getNextText(n.key)
if (!n) break
hasVoidParent = document.hasVoidParent(n.key)
hasVoidParent = document.hasVoidParent(n.key, schema)
}
if (n) change.moveToStartOfNode(n)
@@ -451,6 +455,7 @@ function AfterPlugin() {
debug('onKeyDown', { event })
const { value } = change
const { schema } = value
// COMPAT: In iOS, some of these hotkeys are handled in the
// `onNativeBeforeInput` handler of the `<Content>` component in order to
@@ -522,7 +527,7 @@ function AfterPlugin() {
if (Hotkeys.isMoveBackward(event)) {
const { document, isInVoid, previousText, startText } = value
const isPreviousInVoid =
previousText && document.hasVoidParent(previousText.key)
previousText && document.hasVoidParent(previousText.key, schema)
if (isInVoid || isPreviousInVoid || startText.text == '') {
event.preventDefault()
@@ -532,7 +537,8 @@ function AfterPlugin() {
if (Hotkeys.isMoveForward(event)) {
const { document, isInVoid, nextText, startText } = value
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
const isNextInVoid =
nextText && document.hasVoidParent(nextText.key, schema)
if (isInVoid || isNextInVoid || startText.text == '') {
event.preventDefault()
@@ -543,7 +549,7 @@ function AfterPlugin() {
if (Hotkeys.isExtendBackward(event)) {
const { document, isInVoid, previousText, startText } = value
const isPreviousInVoid =
previousText && document.hasVoidParent(previousText.key)
previousText && document.hasVoidParent(previousText.key, schema)
if (isInVoid || isPreviousInVoid || startText.text == '') {
event.preventDefault()
@@ -553,7 +559,8 @@ function AfterPlugin() {
if (Hotkeys.isExtendForward(event)) {
const { document, isInVoid, nextText, startText } = value
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
const isNextInVoid =
nextText && document.hasVoidParent(nextText.key, schema)
if (isInVoid || isNextInVoid || startText.text == '') {
event.preventDefault()
@@ -583,8 +590,8 @@ function AfterPlugin() {
if (type == 'text' || type == 'html') {
if (!text) return
const { value } = change
const { document, selection, startBlock } = value
if (startBlock.isVoid) return
const { document, selection, startBlock, schema } = value
if (schema.isVoid(startBlock)) return
const defaultBlock = startBlock
const defaultMarks = document.getInsertMarksAtRange(selection)
@@ -607,7 +614,7 @@ function AfterPlugin() {
const window = getWindow(event.target)
const { value } = change
const { document } = value
const { document, schema } = value
const native = window.getSelection()
// If there are no ranges, the editor was blurred natively.
@@ -637,10 +644,10 @@ function AfterPlugin() {
// selection, go with `0`. (2017/09/07)
if (
anchorBlock &&
!anchorBlock.isVoid &&
!schema.isVoid(anchorBlock) &&
anchor.offset == 0 &&
focusBlock &&
focusBlock.isVoid &&
schema.isVoid(focusBlock) &&
focus.offset != 0
) {
range = range.setFocus(focus.setOffset(0))
@@ -651,7 +658,7 @@ function AfterPlugin() {
// standardizes the behavior, since it's indistinguishable to the user.
if (
anchorInline &&
!anchorInline.isVoid &&
!schema.isVoid(anchorInline) &&
anchor.offset == anchorText.text.length
) {
const block = document.getClosestBlock(anchor.key)
@@ -661,7 +668,7 @@ function AfterPlugin() {
if (
focusInline &&
!focusInline.isVoid &&
!schema.isVoid(focusInline) &&
focus.offset == focusText.text.length
) {
const block = document.getClosestBlock(focus.key)

View File

@@ -66,6 +66,7 @@ function BeforePlugin() {
if (editor.props.readOnly) return true
const { value } = change
const { schema } = value
const { relatedTarget, target } = event
const window = getWindow(target)
@@ -93,7 +94,8 @@ function BeforePlugin() {
// editable section of an element that isn't a void node (eg. a list item
// of the check list example).
const node = findNode(relatedTarget, value)
if (el.contains(relatedTarget) && node && !node.isVoid) return true
if (el.contains(relatedTarget) && node && !schema.isVoid(node))
return true
}
debug('onBlur', { event })
@@ -269,8 +271,10 @@ function BeforePlugin() {
// call `preventDefault` to signal that drops are allowed.
// When the target is editable, dropping is already allowed by
// default, and calling `preventDefault` hides the cursor.
const { value } = editor
const { schema } = value
const node = findNode(event.target, editor.value)
if (node.isVoid) event.preventDefault()
if (schema.isVoid(node)) event.preventDefault()
// COMPAT: IE won't call onDrop on contentEditables unless the
// default dragOver is prevented:

View File

@@ -26,9 +26,10 @@ function cloneFragment(
) {
const window = getWindow(event.target)
const native = window.getSelection()
const { schema } = value
const { start, end } = value.selection
const startVoid = value.document.getClosestVoid(start.key)
const endVoid = value.document.getClosestVoid(end.key)
const startVoid = value.document.getClosestVoid(start.key, schema)
const endVoid = value.document.getClosestVoid(end.key, schema)
// If the selection is collapsed, and it isn't inside a void node, abort.
if (native.isCollapsed && !startVoid) return

View File

@@ -19,14 +19,14 @@ function getEventRange(event, value) {
const { x, y, target } = event
if (x == null || y == null) return null
const { document } = value
const { document, schema } = value
const node = findNode(target, value)
if (!node) return null
// If the drop target is inside a void node, move it into either the next or
// previous node, depending on which side the `x` and `y` coordinates are
// closest to.
if (node.isVoid) {
if (schema.isVoid(node)) {
const rect = target.getBoundingClientRect()
const isPrevious =
node.object == 'inline'