mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +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 { 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()
|
||||||
|
@@ -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) => {
|
||||||
|
@@ -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}
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
|
Reference in New Issue
Block a user