mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-09-01 11:12:42 +02:00
fix offset calculations and splitting near inlines
This commit is contained in:
@@ -987,7 +987,13 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
getTextAtOffset(offset) {
|
getTextAtOffset(offset) {
|
||||||
|
// PERF: Add a few shortcuts for the obvious cases.
|
||||||
|
if (offset == 0) return this.getFirstText()
|
||||||
|
if (offset == this.length) return this.getLastText()
|
||||||
|
if (offset < 0 || offset > this.length) return null
|
||||||
|
|
||||||
let length = 0
|
let length = 0
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.getTexts()
|
.getTexts()
|
||||||
.find((text, i, texts) => {
|
.find((text, i, texts) => {
|
||||||
@@ -998,7 +1004,7 @@ const Node = {
|
|||||||
// the furthest text node at the offset, and it will also match.
|
// the furthest text node at the offset, and it will also match.
|
||||||
if (next && next.length == 0) return false
|
if (next && next.length == 0) return false
|
||||||
|
|
||||||
return length >= offset
|
return length > offset
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1332,6 +1338,7 @@ const Node = {
|
|||||||
let one
|
let one
|
||||||
let two
|
let two
|
||||||
|
|
||||||
|
|
||||||
if (node.kind != 'text') {
|
if (node.kind != 'text') {
|
||||||
child = node.getTextAtOffset(offset)
|
child = node.getTextAtOffset(offset)
|
||||||
}
|
}
|
||||||
|
@@ -399,67 +399,48 @@ function setSelection(state, operation) {
|
|||||||
|
|
||||||
function splitNode(state, operation) {
|
function splitNode(state, operation) {
|
||||||
const { path, offset, count } = operation
|
const { path, offset, count } = operation
|
||||||
const { document } = state
|
let { document, selection } = state
|
||||||
|
|
||||||
if (offset === undefined) {
|
// If there's no offset, it's using the `count` instead.
|
||||||
return state.merge({
|
if (offset == null) {
|
||||||
document: document.splitNodeAfter(path, count)
|
document = document.splitNodeAfter(path, count)
|
||||||
// No need to update selection
|
state = state.merge({ document })
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
// Update document
|
|
||||||
let newDocument = document.splitNode(path, offset)
|
|
||||||
|
|
||||||
// Update selection
|
|
||||||
let { selection } = state
|
|
||||||
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
|
||||||
|
|
||||||
const node = document.assertPath(path)
|
|
||||||
// The text node that was split
|
|
||||||
const splittedText = node.kind == 'text'
|
|
||||||
? node
|
|
||||||
: node.getTextAtOffset(offset)
|
|
||||||
const textOffset = node.kind == 'text'
|
|
||||||
? offset
|
|
||||||
: offset - node.getOffset(splittedText.key)
|
|
||||||
|
|
||||||
// Should we update the selection ?
|
|
||||||
const shouldUpdateAnchor = splittedText.key == anchorKey && textOffset <= anchorOffset
|
|
||||||
const shouldUpdateFocus = splittedText.key == focusKey && textOffset <= focusOffset
|
|
||||||
if (shouldUpdateFocus || shouldUpdateAnchor) {
|
|
||||||
// The node next to `node`, resulting from the split
|
|
||||||
const secondNode = newDocument.getNextSibling(node.key)
|
|
||||||
let secondText, newOffset
|
|
||||||
|
|
||||||
if (shouldUpdateAnchor) {
|
|
||||||
newOffset = anchorOffset - textOffset
|
|
||||||
secondText = secondNode.kind == 'text'
|
|
||||||
? secondNode
|
|
||||||
: secondNode.getTextAtOffset(newOffset)
|
|
||||||
selection = selection.merge({
|
|
||||||
anchorKey: secondText.key,
|
|
||||||
anchorOffset: newOffset
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldUpdateFocus) {
|
|
||||||
newOffset = focusOffset - textOffset
|
|
||||||
secondText = secondNode.kind == 'text'
|
|
||||||
? secondNode
|
|
||||||
: secondNode.getTextAtOffset(newOffset)
|
|
||||||
selection = selection.merge({
|
|
||||||
focusKey: secondText.key,
|
|
||||||
focusOffset: newOffset
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state = state.merge({
|
|
||||||
document: newDocument,
|
|
||||||
selection
|
|
||||||
})
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise, split using the `offset`, but calculate a few things first.
|
||||||
|
const node = document.assertPath(path)
|
||||||
|
const text = node.kind == 'text' ? node : node.getTextAtOffset(offset)
|
||||||
|
const textOffset = node.kind == 'text' ? offset : offset - node.getOffset(text.key)
|
||||||
|
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
||||||
|
|
||||||
|
document = document.splitNode(path, offset)
|
||||||
|
|
||||||
|
// Determine whether we need to update the selection.
|
||||||
|
const splitAnchor = text.key == anchorKey && textOffset <= anchorOffset
|
||||||
|
const splitFocus = text.key == focusKey && textOffset <= focusOffset
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// If either the anchor of focus was after the split, we need to update them.
|
||||||
|
if (splitFocus || splitAnchor) {
|
||||||
|
const nextText = document.getNextText(text.key)
|
||||||
|
|
||||||
|
if (splitAnchor) {
|
||||||
|
selection = selection.merge({
|
||||||
|
anchorKey: nextText.key,
|
||||||
|
anchorOffset: anchorOffset - textOffset
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitFocus) {
|
||||||
|
selection = selection.merge({
|
||||||
|
focusKey: nextText.key,
|
||||||
|
focusOffset: focusOffset - textOffset
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.merge({ document, selection })
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
|
@@ -842,13 +842,16 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
|
|||||||
transform.splitNodeByKey(block.key, offset, OPTS)
|
transform.splitNodeByKey(block.key, offset, OPTS)
|
||||||
state = transform.state
|
state = transform.state
|
||||||
document = state.document
|
document = state.document
|
||||||
const extra = document.getPreviousSibling(firstMatch.key)
|
|
||||||
|
|
||||||
children.forEach((child, i) => {
|
children.forEach((child, i) => {
|
||||||
|
if (i == 0) {
|
||||||
|
const extra = child
|
||||||
|
child = document.getNextBlock(child.key)
|
||||||
|
transform.removeNodeByKey(extra.key, OPTS)
|
||||||
|
}
|
||||||
|
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, OPTS)
|
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
transform.removeNodeByKey(extra.key, OPTS)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
import assert from 'assert'
|
||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTexts()
|
||||||
|
const third = texts.get(2)
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: third.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: third.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const next = state
|
||||||
|
.transform()
|
||||||
|
.moveTo(range)
|
||||||
|
.splitBlock()
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
const updated = next.document.getTexts().get(2)
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
next.selection.toJS(),
|
||||||
|
range.collapseToStartOf(updated).toJS()
|
||||||
|
)
|
||||||
|
|
||||||
|
return next
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
||||||
|
- kind: inline
|
||||||
|
type: link
|
||||||
|
data:
|
||||||
|
href: 'website.com'
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: hyperlink
|
||||||
|
- kind: text
|
||||||
|
text: word
|
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
||||||
|
- kind: inline
|
||||||
|
type: link
|
||||||
|
data:
|
||||||
|
href: 'website.com'
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: hyperlink
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
@@ -19,9 +19,11 @@ export default function (state) {
|
|||||||
.unwrapBlock('quote')
|
.unwrapBlock('quote')
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
|
const updated = next.document.getTexts().get(2)
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
next.selection.toJS(),
|
next.selection.toJS(),
|
||||||
range.toJS()
|
range.merge({ anchorKey: updated.key }).toJS()
|
||||||
)
|
)
|
||||||
|
|
||||||
return next
|
return next
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTexts()
|
||||||
|
const third = texts.get(2)
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: third.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: third.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.splitBlockAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
||||||
|
- kind: inline
|
||||||
|
type: emoji
|
||||||
|
isVoid: true
|
||||||
|
- kind: text
|
||||||
|
text: word
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
||||||
|
- kind: inline
|
||||||
|
type: emoji
|
||||||
|
isVoid: true
|
||||||
|
- kind: text
|
||||||
|
text: ""
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
text: word
|
Reference in New Issue
Block a user