mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-15 11:44:05 +02:00
fix to use index instead of start and end in offset keys
This commit is contained in:
@@ -379,8 +379,8 @@ class Content extends React.Component {
|
||||
}
|
||||
|
||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
||||
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
|
||||
const focus = OffsetKey.findPoint(focusNode, focusOffset)
|
||||
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset, state)
|
||||
const focus = OffsetKey.findPoint(focusNode, focusOffset, state)
|
||||
|
||||
state = state
|
||||
.transform()
|
||||
|
@@ -14,11 +14,10 @@ class Leaf extends React.Component {
|
||||
*/
|
||||
|
||||
static propTypes = {
|
||||
end: React.PropTypes.number.isRequired,
|
||||
index: React.PropTypes.number.isRequired,
|
||||
marks: React.PropTypes.object.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
renderMark: React.PropTypes.func.isRequired,
|
||||
start: React.PropTypes.number.isRequired,
|
||||
state: React.PropTypes.object.isRequired,
|
||||
text: React.PropTypes.string.isRequired
|
||||
};
|
||||
@@ -31,18 +30,19 @@ class Leaf extends React.Component {
|
||||
*/
|
||||
|
||||
shouldComponentUpdate(props) {
|
||||
const { start, end, node, state } = props
|
||||
const { index, node, state } = props
|
||||
const { selection } = state
|
||||
|
||||
const should = (
|
||||
selection.hasEdgeBetween(node, start, end) ||
|
||||
props.start != this.props.start ||
|
||||
props.end != this.props.end ||
|
||||
if (
|
||||
props.index != this.props.index ||
|
||||
props.text != this.props.text ||
|
||||
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() {
|
||||
@@ -61,7 +61,8 @@ class Leaf extends React.Component {
|
||||
if (!selection.isFocused) return
|
||||
|
||||
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.
|
||||
const hasAnchor = selection.hasAnchorBetween(node, start, end)
|
||||
@@ -119,11 +120,10 @@ class Leaf extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { node, text, marks, start, end, renderMark } = this.props
|
||||
const { node, index, text, marks, renderMark } = this.props
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
key: node.key,
|
||||
start,
|
||||
end
|
||||
index
|
||||
})
|
||||
|
||||
const style = marks.reduce((memo, mark) => {
|
||||
|
@@ -84,16 +84,13 @@ class Text extends React.Component {
|
||||
const { node, renderMark, state } = this.props
|
||||
const text = range.text
|
||||
const marks = range.marks
|
||||
const start = offset
|
||||
const end = offset + text.length
|
||||
|
||||
return (
|
||||
<Leaf
|
||||
key={`${node.key}-${index}`}
|
||||
index={index}
|
||||
state={state}
|
||||
node={node}
|
||||
start={start}
|
||||
end={end}
|
||||
text={text}
|
||||
marks={marks}
|
||||
renderMark={renderMark}
|
||||
|
@@ -62,12 +62,10 @@ class Void extends React.Component {
|
||||
const child = node.getTexts().first()
|
||||
const text = ''
|
||||
const marks = Mark.createSet()
|
||||
const start = 0
|
||||
const end = 0
|
||||
const index = 0
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
key: child.key,
|
||||
start,
|
||||
end
|
||||
index
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -77,8 +75,7 @@ class Void extends React.Component {
|
||||
key={offsetKey}
|
||||
state={state}
|
||||
node={child}
|
||||
start={start}
|
||||
end={end}
|
||||
index={index}
|
||||
text={text}
|
||||
marks={marks}
|
||||
/>
|
||||
|
@@ -1,6 +1,8 @@
|
||||
|
||||
import Character from './character'
|
||||
import Mark from './mark'
|
||||
import groupByMarks from '../utils/group-by-marks'
|
||||
import memoize from '../utils/memoize'
|
||||
import uid from '../utils/uid'
|
||||
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`.
|
||||
*
|
||||
@@ -152,6 +164,14 @@ class Text extends new Record(DEFAULTS) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Memoize read methods.
|
||||
*/
|
||||
|
||||
memoize(Text.prototype, [
|
||||
'getRanges'
|
||||
])
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
@@ -29,12 +29,12 @@ function memoize(object, properties) {
|
||||
|
||||
object[property] = function (...args) {
|
||||
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)
|
||||
|
||||
const value = original.apply(this, args)
|
||||
this.cache = cache.setIn(keys, value)
|
||||
this.__cache = cache.setIn(keys, value)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* Offset key parser regex.
|
||||
*/
|
||||
|
||||
const PARSER = /^(\w+)(?::(\d+)-(\d+))?$/
|
||||
const PARSER = /^(\w+)(?:-(\d+))?$/
|
||||
|
||||
/**
|
||||
* Offset key attribute name.
|
||||
@@ -13,26 +13,51 @@ const ATTRIBUTE = 'data-offset-key'
|
||||
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
|
||||
*/
|
||||
|
||||
function findKey(node) {
|
||||
if (node.nodeType == 3) node = node.parentNode
|
||||
function findKey(element) {
|
||||
if (element.nodeType == 3) element = element.parentNode
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// Otherwise, move up the tree looking for cousin offset keys in parents.
|
||||
while (node = node.parentNode) {
|
||||
const cousin = node.querySelector(SELECTOR)
|
||||
while (element = element.parentNode) {
|
||||
const cousin = element.querySelector(SELECTOR)
|
||||
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 {State} state
|
||||
* @return {String} key
|
||||
*/
|
||||
|
||||
function findPoint(node, offset) {
|
||||
const key = findKey(node)
|
||||
const parsed = parse(key)
|
||||
function findPoint(element, offset, state) {
|
||||
const offsetKey = findKey(element)
|
||||
const { key, index } = parse(offsetKey)
|
||||
const { start, end } = findBounds(key, index, state)
|
||||
|
||||
// Don't let the offset be outside the start and end bounds.
|
||||
offset = parsed.start + offset
|
||||
offset = Math.max(offset, parsed.start)
|
||||
offset = Math.min(offset, parsed.end)
|
||||
// Don't let the offset be outside of the start and end bounds.
|
||||
offset = start + offset
|
||||
offset = Math.max(offset, start)
|
||||
offset = Math.min(offset, end)
|
||||
|
||||
return {
|
||||
key: parsed.key,
|
||||
key,
|
||||
offset
|
||||
}
|
||||
}
|
||||
@@ -67,21 +94,16 @@ function findPoint(node, offset) {
|
||||
* Parse an offset key `string`.
|
||||
*
|
||||
* @param {String} string
|
||||
* @return {Object} parsed
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
function parse(string) {
|
||||
const matches = PARSER.exec(string)
|
||||
if (!matches) throw new Error(`Invalid offset key string "${string}".`)
|
||||
|
||||
let [ original, key, start, end ] = matches
|
||||
start = parseInt(start, 10)
|
||||
end = parseInt(end, 10)
|
||||
|
||||
const [ original, key, index ] = matches
|
||||
return {
|
||||
key,
|
||||
start,
|
||||
end
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +112,12 @@ function parse(string) {
|
||||
*
|
||||
* @param {Object} object
|
||||
* @property {String} key
|
||||
* @property {Number} start
|
||||
* @property {Number} end
|
||||
* @property {Number} index
|
||||
* @return {String} key
|
||||
*/
|
||||
|
||||
function stringify(object) {
|
||||
return `${object.key}:${object.start}-${object.end}`
|
||||
return `${object.key}-${object.index}`
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +125,7 @@ function stringify(object) {
|
||||
*/
|
||||
|
||||
export default {
|
||||
findBounds,
|
||||
findKey,
|
||||
findPoint,
|
||||
parse,
|
||||
|
Reference in New Issue
Block a user