1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-16 20:24:01 +02:00

fix to use index instead of start and end in offset keys

This commit is contained in:
Ian Storm Taylor
2016-07-22 12:03:55 -07:00
parent f1124b3441
commit ac59e94a15
7 changed files with 93 additions and 57 deletions

View File

@@ -379,8 +379,8 @@ class Content extends React.Component {
} }
const { anchorNode, anchorOffset, focusNode, focusOffset } = native const { anchorNode, anchorOffset, focusNode, focusOffset } = native
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset) const anchor = OffsetKey.findPoint(anchorNode, anchorOffset, state)
const focus = OffsetKey.findPoint(focusNode, focusOffset) const focus = OffsetKey.findPoint(focusNode, focusOffset, state)
state = state state = state
.transform() .transform()

View File

@@ -14,11 +14,10 @@ class Leaf extends React.Component {
*/ */
static propTypes = { static propTypes = {
end: React.PropTypes.number.isRequired, index: React.PropTypes.number.isRequired,
marks: React.PropTypes.object.isRequired, marks: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired, node: React.PropTypes.object.isRequired,
renderMark: React.PropTypes.func.isRequired, renderMark: React.PropTypes.func.isRequired,
start: React.PropTypes.number.isRequired,
state: React.PropTypes.object.isRequired, state: React.PropTypes.object.isRequired,
text: React.PropTypes.string.isRequired text: React.PropTypes.string.isRequired
}; };
@@ -31,18 +30,19 @@ class Leaf extends React.Component {
*/ */
shouldComponentUpdate(props) { shouldComponentUpdate(props) {
const { start, end, node, state } = props const { index, node, state } = props
const { selection } = state const { selection } = state
const should = ( if (
selection.hasEdgeBetween(node, start, end) || props.index != this.props.index ||
props.start != this.props.start ||
props.end != this.props.end ||
props.text != this.props.text || props.text != this.props.text ||
props.marks != this.props.marks props.marks != this.props.marks
) ) {
return true
}
return should const { start, end } = OffsetKey.findBounds(node.key, index, state)
return selection.hasEdgeBetween(node, start, end)
} }
componentDidMount() { componentDidMount() {
@@ -61,7 +61,8 @@ class Leaf extends React.Component {
if (!selection.isFocused) return if (!selection.isFocused) return
const { anchorOffset, focusOffset } = selection const { anchorOffset, focusOffset } = selection
const { node, start, end } = this.props const { node, index } = this.props
const { start, end } = OffsetKey.findBounds(node.key, index, state)
// If neither matches, the selection doesn't start or end here, so exit. // If neither matches, the selection doesn't start or end here, so exit.
const hasAnchor = selection.hasAnchorBetween(node, start, end) const hasAnchor = selection.hasAnchorBetween(node, start, end)
@@ -119,11 +120,10 @@ class Leaf extends React.Component {
} }
render() { render() {
const { node, text, marks, start, end, renderMark } = this.props const { node, index, text, marks, renderMark } = this.props
const offsetKey = OffsetKey.stringify({ const offsetKey = OffsetKey.stringify({
key: node.key, key: node.key,
start, index
end
}) })
const style = marks.reduce((memo, mark) => { const style = marks.reduce((memo, mark) => {

View File

@@ -84,16 +84,13 @@ class Text extends React.Component {
const { node, renderMark, state } = this.props const { node, renderMark, state } = this.props
const text = range.text const text = range.text
const marks = range.marks const marks = range.marks
const start = offset
const end = offset + text.length
return ( return (
<Leaf <Leaf
key={`${node.key}-${index}`} key={`${node.key}-${index}`}
index={index}
state={state} state={state}
node={node} node={node}
start={start}
end={end}
text={text} text={text}
marks={marks} marks={marks}
renderMark={renderMark} renderMark={renderMark}

View File

@@ -62,12 +62,10 @@ class Void extends React.Component {
const child = node.getTexts().first() const child = node.getTexts().first()
const text = '' const text = ''
const marks = Mark.createSet() const marks = Mark.createSet()
const start = 0 const index = 0
const end = 0
const offsetKey = OffsetKey.stringify({ const offsetKey = OffsetKey.stringify({
key: child.key, key: child.key,
start, index
end
}) })
return ( return (
@@ -77,8 +75,7 @@ class Void extends React.Component {
key={offsetKey} key={offsetKey}
state={state} state={state}
node={child} node={child}
start={start} index={index}
end={end}
text={text} text={text}
marks={marks} marks={marks}
/> />

View File

@@ -1,6 +1,8 @@
import Character from './character' import Character from './character'
import Mark from './mark' import Mark from './mark'
import groupByMarks from '../utils/group-by-marks'
import memoize from '../utils/memoize'
import uid from '../utils/uid' import uid from '../utils/uid'
import { List, Record } from 'immutable' import { List, Record } from 'immutable'
@@ -101,6 +103,16 @@ class Text extends new Record(DEFAULTS) {
}) })
} }
/**
* Get the characters grouped by marks.
*
* @return {List}
*/
getRanges() {
return groupByMarks(this.decorations || this.characters)
}
/** /**
* Remove characters from the text node from `start` to `end`. * Remove characters from the text node from `start` to `end`.
* *
@@ -152,6 +164,14 @@ class Text extends new Record(DEFAULTS) {
} }
/**
* Memoize read methods.
*/
memoize(Text.prototype, [
'getRanges'
])
/** /**
* Export. * Export.
*/ */

View File

@@ -29,12 +29,12 @@ function memoize(object, properties) {
object[property] = function (...args) { object[property] = function (...args) {
const keys = [property, ...args, LEAF] const keys = [property, ...args, LEAF]
const cache = this.cache = this.cache || new Map() const cache = this.__cache = this.__cache || new Map()
if (cache.hasIn(keys)) return cache.getIn(keys) if (cache.hasIn(keys)) return cache.getIn(keys)
const value = original.apply(this, args) const value = original.apply(this, args)
this.cache = cache.setIn(keys, value) this.__cache = cache.setIn(keys, value)
return value return value
} }
} }

View File

@@ -3,7 +3,7 @@
* Offset key parser regex. * Offset key parser regex.
*/ */
const PARSER = /^(\w+)(?::(\d+)-(\d+))?$/ const PARSER = /^(\w+)(?:-(\d+))?$/
/** /**
* Offset key attribute name. * Offset key attribute name.
@@ -13,26 +13,51 @@ const ATTRIBUTE = 'data-offset-key'
const SELECTOR = `[${ATTRIBUTE}]` const SELECTOR = `[${ATTRIBUTE}]`
/** /**
* From a `node`, find the closest parent's offset key. * Find the start and end bounds from a node's `key` and `index`.
* *
* @param {Node} node * @param {String} key
* @param {Number} index
* @param {State} state
* @return {Object}
*/
function findBounds(key, index, state) {
const text = state.document.assertDescendant(key)
const ranges = text.getRanges()
const range = ranges.get(index)
const start = ranges
.slice(0, index)
.reduce((memo, r) => {
return memo += r.text.length
}, 0)
return {
start,
end: start + range.text.length
}
}
/**
* From a `element`, find the closest parent's offset key.
*
* @param {Element} element
* @return {String} key * @return {String} key
*/ */
function findKey(node) { function findKey(element) {
if (node.nodeType == 3) node = node.parentNode if (element.nodeType == 3) element = element.parentNode
// If a parent with an offset key exists, use it. // If a parent with an offset key exists, use it.
const parent = node.closest(SELECTOR) const parent = element.closest(SELECTOR)
if (parent) return parent.getAttribute(ATTRIBUTE) if (parent) return parent.getAttribute(ATTRIBUTE)
// Otherwise, if a child with an offset key exists, use it. // Otherwise, if a child with an offset key exists, use it.
const child = node.querySelector(SELECTOR) const child = element.querySelector(SELECTOR)
if (child) return child.getAttribute(ATTRIBUTE) if (child) return child.getAttribute(ATTRIBUTE)
// Otherwise, move up the tree looking for cousin offset keys in parents. // Otherwise, move up the tree looking for cousin offset keys in parents.
while (node = node.parentNode) { while (element = element.parentNode) {
const cousin = node.querySelector(SELECTOR) const cousin = element.querySelector(SELECTOR)
if (cousin) return cousin.getAttribute(ATTRIBUTE) if (cousin) return cousin.getAttribute(ATTRIBUTE)
} }
@@ -41,24 +66,26 @@ function findKey(node) {
} }
/** /**
* From a `node` and `offset`, find the closest parent's point. * Find the selection point from an `element`, `offset`, and list of `ranges`.
* *
* @param {Node} node * @param {Element} element
* @param {Offset} offset * @param {Offset} offset
* @param {State} state
* @return {String} key * @return {String} key
*/ */
function findPoint(node, offset) { function findPoint(element, offset, state) {
const key = findKey(node) const offsetKey = findKey(element)
const parsed = parse(key) const { key, index } = parse(offsetKey)
const { start, end } = findBounds(key, index, state)
// Don't let the offset be outside the start and end bounds. // Don't let the offset be outside of the start and end bounds.
offset = parsed.start + offset offset = start + offset
offset = Math.max(offset, parsed.start) offset = Math.max(offset, start)
offset = Math.min(offset, parsed.end) offset = Math.min(offset, end)
return { return {
key: parsed.key, key,
offset offset
} }
} }
@@ -67,21 +94,16 @@ function findPoint(node, offset) {
* Parse an offset key `string`. * Parse an offset key `string`.
* *
* @param {String} string * @param {String} string
* @return {Object} parsed * @return {Object}
*/ */
function parse(string) { function parse(string) {
const matches = PARSER.exec(string) const matches = PARSER.exec(string)
if (!matches) throw new Error(`Invalid offset key string "${string}".`) if (!matches) throw new Error(`Invalid offset key string "${string}".`)
const [ original, key, index ] = matches
let [ original, key, start, end ] = matches
start = parseInt(start, 10)
end = parseInt(end, 10)
return { return {
key, key,
start, index
end
} }
} }
@@ -90,13 +112,12 @@ function parse(string) {
* *
* @param {Object} object * @param {Object} object
* @property {String} key * @property {String} key
* @property {Number} start * @property {Number} index
* @property {Number} end
* @return {String} key * @return {String} key
*/ */
function stringify(object) { function stringify(object) {
return `${object.key}:${object.start}-${object.end}` return `${object.key}-${object.index}`
} }
/** /**
@@ -104,6 +125,7 @@ function stringify(object) {
*/ */
export default { export default {
findBounds,
findKey, findKey,
findPoint, findPoint,
parse, parse,