1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-19 05:31:56 +02:00

add Node.createRange for range resolution (#2025)

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

Improvement.

#### What's the new behavior?

Adds a `Node.createRange` method for more easily creating ranges that are normalized to the current document. As well as a `Node.resolveRange` lower-level method for just doing the normalization on an existing range.

Later we can deprecate `Range.normalize` since it should be on the node instead.

#### 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`.)

#### Does this fix any issues or need any specific reviewers?

Fixes: #2011
This commit is contained in:
Ian Storm Taylor
2018-08-03 14:16:47 -07:00
committed by GitHub
parent 8a1d75025a
commit de3420b0f1
8 changed files with 69 additions and 46 deletions

View File

@@ -344,10 +344,11 @@ function AfterPlugin() {
// Determine what the selection should be after changing the text. // Determine what the selection should be after changing the text.
const delta = textContent.length - text.length const delta = textContent.length - text.length
const corrected = selection.collapseToEnd().move(delta) const corrected = selection.collapseToEnd().move(delta)
const entire = selection let entire = selection
.moveAnchorTo(point.key, start) .moveAnchorTo(point.key, start)
.moveFocusTo(point.key, end) .moveFocusTo(point.key, end)
.normalize(document)
entire = document.normalizeRange(entire)
// Change the current value to have the leaf's text replaced. // Change the current value to have the leaf's text replaced.
change.insertTextAtRange(entire, textContent, leaf.marks).select(corrected) change.insertTextAtRange(entire, textContent, leaf.marks).select(corrected)
@@ -583,7 +584,7 @@ function AfterPlugin() {
if (next) range = range.moveFocusTo(next.key, 0) if (next) range = range.moveFocusTo(next.key, 0)
} }
range = range.normalize(document) range = document.normalizeRange(range)
change.select(range) change.select(range)
} }

View File

@@ -1,6 +1,5 @@
import getWindow from 'get-window' import getWindow from 'get-window'
import isBackward from 'selection-is-backward' import isBackward from 'selection-is-backward'
import { Range } from 'slate'
import { IS_IE, IS_EDGE } from 'slate-dev-environment' import { IS_IE, IS_EDGE } from 'slate-dev-environment'
import findPoint from './find-point' import findPoint from './find-point'
@@ -60,7 +59,8 @@ function findRange(native, value) {
} }
} }
let range = Range.create({ const { document } = value
const range = document.createRange({
anchorKey: anchor.key, anchorKey: anchor.key,
anchorOffset: anchor.offset, anchorOffset: anchor.offset,
focusKey: focus.key, focusKey: focus.key,
@@ -69,7 +69,6 @@ function findRange(native, value) {
isFocused: true, isFocused: true,
}) })
range = range.normalize(value.document)
return range return range
} }

View File

@@ -1,6 +1,5 @@
import getWindow from 'get-window' import getWindow from 'get-window'
import { Range } from 'slate'
import findNode from './find-node' import findNode from './find-node'
import findRange from './find-range' import findRange from './find-range'
@@ -35,7 +34,7 @@ function getEventRange(event, value) {
: y - rect.top < rect.top + rect.height - y : y - rect.top < rect.top + rect.height - y
const text = node.getFirstText() const text = node.getFirstText()
const range = Range.create() const range = document.createRange()
if (isPrevious) { if (isPrevious) {
const previousText = document.getPreviousText(text.key) const previousText = document.getPreviousText(text.key)

View File

@@ -3,7 +3,6 @@ import Inline from '../models/inline'
import Mark from '../models/mark' import Mark from '../models/mark'
import Node from '../models/node' import Node from '../models/node'
import PathUtils from '../utils/path-utils' import PathUtils from '../utils/path-utils'
import Range from '../models/range'
/** /**
* Changes. * Changes.
@@ -343,12 +342,12 @@ Changes.replaceTextByPath = (
length = node.text.length - offset length = node.text.length - offset
} }
const range = Range.create({ const range = document.createRange({
anchorPath: path, anchorPath: path,
focusPath: path, focusPath: path,
anchorOffset: offset, anchorOffset: offset,
focusOffset: offset + length, focusOffset: offset + length,
}).normalize(document) })
let activeMarks = document.getActiveMarksAtRange(range) let activeMarks = document.getActiveMarksAtRange(range)
@@ -358,7 +357,8 @@ Changes.replaceTextByPath = (
// Do not use mark at index when marks and activeMarks are both empty // Do not use mark at index when marks and activeMarks are both empty
marks = activeMarks ? activeMarks : [] marks = activeMarks ? activeMarks : []
} else if (activeMarks) { } else if (activeMarks) {
// Do not use `has` because we may want to reset marks like font-size with an updated data; // Do not use `has` because we may want to reset marks like font-size with
// an updated data;
activeMarks = activeMarks.filter( activeMarks = activeMarks.filter(
activeMark => !marks.find(m => activeMark.type === m.type) activeMark => !marks.find(m => activeMark.type === m.type)
) )

View File

@@ -25,7 +25,7 @@ Changes.select = (change, properties, options = {}) => {
const { value } = change const { value } = change
const { document, selection } = value const { document, selection } = value
const props = {} const props = {}
const next = selection.merge(properties).normalize(document) const next = document.createRange(selection.merge(properties))
// Re-compute the properties, to ensure that we get their normalized values. // Re-compute the properties, to ensure that we get their normalized values.
properties = pick(next, Object.keys(properties)) properties = pick(next, Object.keys(properties))
@@ -325,7 +325,7 @@ PROXY_TRANSFORMS.forEach(method => {
const { value } = change const { value } = change
const { document, selection } = value const { document, selection } = value
let next = selection[method](...args) let next = selection[method](...args)
if (normalize) next = next.normalize(document) if (normalize) next = document.createRange(next)
change.select(next) change.select(next)
} }
}) })

View File

@@ -1,13 +1,9 @@
/**
* Dependencies.
*/
import isPlainObject from 'is-plain-object' import isPlainObject from 'is-plain-object'
import logger from 'slate-dev-logger' import logger from 'slate-dev-logger'
import { List, Map, Record } from 'immutable' import { List, Map, Record } from 'immutable'
import MODEL_TYPES, { isType } from '../constants/model-types'
import KeyUtils from '../utils/key-utils' import KeyUtils from '../utils/key-utils'
import MODEL_TYPES, { isType } from '../constants/model-types'
/** /**
* Default properties. * Default properties.

View File

@@ -205,6 +205,19 @@ class Node {
return ret return ret
} }
/**
* Create a new range with `properties` relative to the node.
*
* @param {Object|Range} properties
* @return {Range}
*/
createRange(properties) {
properties = Range.createProperties(properties)
const range = this.resolveRange(properties)
return range
}
/** /**
* Recursively filter all descendant nodes with `iterator`. * Recursively filter all descendant nodes with `iterator`.
* *
@@ -275,7 +288,7 @@ class Node {
*/ */
getActiveMarksAtRange(range) { getActiveMarksAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return Set() if (range.isUnset) return Set()
if (range.isCollapsed) { if (range.isCollapsed) {
@@ -397,7 +410,7 @@ class Node {
*/ */
getBlocksAtRangeAsArray(range) { getBlocksAtRangeAsArray(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return [] if (range.isUnset) return []
const { startKey, endKey } = range const { startKey, endKey } = range
@@ -465,7 +478,7 @@ class Node {
*/ */
getCharactersAtRange(range) { getCharactersAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return List() if (range.isUnset) return List()
const { startKey, endKey, startOffset, endOffset } = range const { startKey, endKey, startOffset, endOffset } = range
@@ -672,7 +685,7 @@ class Node {
*/ */
getFragmentAtRange(range) { getFragmentAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) { if (range.isUnset) {
return Document.create() return Document.create()
@@ -839,7 +852,7 @@ class Node {
*/ */
getInlinesAtRangeAsArray(range) { getInlinesAtRangeAsArray(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return [] if (range.isUnset) return []
const array = this.getTextsAtRangeAsArray(range) const array = this.getTextsAtRangeAsArray(range)
@@ -892,7 +905,7 @@ class Node {
*/ */
getInsertMarksAtRange(range) { getInsertMarksAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return Set() if (range.isUnset) return Set()
if (range.isCollapsed) { if (range.isCollapsed) {
@@ -1178,7 +1191,7 @@ class Node {
*/ */
getOffsetAtRange(range) { getOffsetAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) { if (range.isUnset) {
throw new Error('The range cannot be unset to calculcate its offset.') throw new Error('The range cannot be unset to calculcate its offset.')
@@ -1213,7 +1226,7 @@ class Node {
*/ */
getOrderedMarksAtRange(range) { getOrderedMarksAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return OrderedSet() if (range.isUnset) return OrderedSet()
if (range.isCollapsed) { if (range.isCollapsed) {
@@ -1537,7 +1550,7 @@ class Node {
*/ */
getTextsAtRange(range) { getTextsAtRange(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return List() if (range.isUnset) return List()
const { startKey, endKey } = range const { startKey, endKey } = range
const list = new List( const list = new List(
@@ -1555,7 +1568,7 @@ class Node {
*/ */
getTextsAtRangeAsArray(range) { getTextsAtRangeAsArray(range) {
range = range.normalize(this) range = this.resolveRange(range)
if (range.isUnset) return [] if (range.isUnset) return []
const { startKey, endKey } = range const { startKey, endKey } = range
const texts = this.getTextsBetweenPositionsAsArray(startKey, endKey) const texts = this.getTextsBetweenPositionsAsArray(startKey, endKey)
@@ -1835,6 +1848,18 @@ class Node {
return ret return ret
} }
/**
* Normalize the node with a `schema`.
*
* @param {Schema} schema
* @return {Function|Void}
*/
normalize(schema) {
const normalizer = schema.normalizeNode(this)
return normalizer
}
/** /**
* Attempt to "refind" a node by a previous `path`, falling back to looking * Attempt to "refind" a node by a previous `path`, falling back to looking
* it up by `key` again. * it up by `key` again.
@@ -1977,6 +2002,20 @@ class Node {
return path return path
} }
/**
* Resolve a `range`, relative to the node, ensuring that the keys and
* offsets in the range exist and that they are synced with the paths.
*
* @param {Range|Object} range
* @return {Range}
*/
resolveRange(range) {
range = Range.create(range)
range = range.normalize(this)
return range
}
/** /**
* Set `properties` on a node. * Set `properties` on a node.
* *
@@ -2046,17 +2085,6 @@ class Node {
return ret return ret
} }
/**
* Normalize the node with a `schema`.
*
* @param {Schema} schema
* @return {Function|Void}
*/
normalize(schema) {
return schema.normalizeNode(this)
}
/** /**
* Validate the node against a `schema`. * Validate the node against a `schema`.
* *
@@ -2138,7 +2166,7 @@ class Node {
'The `Node.isInRange` method is deprecated. Use the new `PathUtils.compare` helper instead.' 'The `Node.isInRange` method is deprecated. Use the new `PathUtils.compare` helper instead.'
) )
range = range.normalize(this) range = this.resolveRange(range)
const node = this const node = this
const { startKey, endKey, isCollapsed } = range const { startKey, endKey, isCollapsed } = range

View File

@@ -119,7 +119,7 @@ class Value extends Record(DEFAULTS) {
if (text) selection = selection.collapseToStartOf(text) if (text) selection = selection.collapseToStartOf(text)
} }
selection = selection.normalize(document) selection = document.createRange(selection)
let value = new Value({ let value = new Value({
data, data,
@@ -978,8 +978,8 @@ class Value extends Record(DEFAULTS) {
setSelection(properties) { setSelection(properties) {
let value = this let value = this
let { document, selection } = value let { document, selection } = value
selection = selection.merge(properties) const next = selection.merge(properties)
selection = selection.normalize(document) selection = document.createRange(next)
value = value.set('selection', selection) value = value.set('selection', selection)
return value return value
} }
@@ -1036,14 +1036,14 @@ class Value extends Record(DEFAULTS) {
if (selection) { if (selection) {
let next = selection.isSet ? iterator(selection) : selection let next = selection.isSet ? iterator(selection) : selection
if (!next) next = selection.deselect() if (!next) next = selection.deselect()
if (next !== selection) next = next.normalize(document) if (next !== selection) next = document.createRange(next)
value = value.set('selection', next) value = value.set('selection', next)
} }
if (decorations) { if (decorations) {
let next = decorations.map(decoration => { let next = decorations.map(decoration => {
let n = decoration.isSet ? iterator(decoration) : decoration let n = decoration.isSet ? iterator(decoration) : decoration
if (n && n !== decoration) n = n.normalize(document) if (n && n !== decoration) n = document.createRange(n)
return n return n
}) })