mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-21 13:51:59 +02:00
got insert fragment working
This commit is contained in:
parent
0af3dbcc79
commit
d9a33657a8
@ -8,7 +8,7 @@ import Mark from './mark'
|
||||
import Selection from './selection'
|
||||
import Transforms from './transforms'
|
||||
import Text from './text'
|
||||
import { List, Map, Set } from 'immutable'
|
||||
import { List, Map, OrderedSet, Set } from 'immutable'
|
||||
|
||||
/**
|
||||
* Node.
|
||||
@ -228,6 +228,21 @@ const Node = {
|
||||
return this.nodes.find(node => node.key == key)
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all of the blocks closest to text nodes.
|
||||
*
|
||||
* @return {List} nodes
|
||||
*/
|
||||
|
||||
getDeepestBlocks() {
|
||||
const texts = this.getTextNodes()
|
||||
const set = texts.reduce((set, text) => {
|
||||
return set.add(this.getClosestBlock(text))
|
||||
}, new OrderedSet())
|
||||
|
||||
return set.toList()
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a descendant node by `key`.
|
||||
*
|
||||
@ -355,12 +370,14 @@ const Node = {
|
||||
*/
|
||||
|
||||
getHighestOnlyChildParent(key) {
|
||||
let child = this.getChild(key)
|
||||
let match = null
|
||||
let parent
|
||||
|
||||
while (parent = this.getParent(child)) {
|
||||
if (parent == null || parent.nodes.size > 1) return match
|
||||
match = parent
|
||||
child = parent
|
||||
}
|
||||
},
|
||||
|
||||
@ -639,8 +656,13 @@ const Node = {
|
||||
insertChildrenAfter(key, nodes) {
|
||||
key = normalizeKey(key)
|
||||
const child = this.getChild(key)
|
||||
const index = this.nodex.indexOf(child)
|
||||
nodes = this.nodes.splice(index + 1, 0, nodes)
|
||||
const index = this.nodes.indexOf(child)
|
||||
|
||||
nodes = this.nodes
|
||||
.slice(0, index + 1)
|
||||
.concat(nodes)
|
||||
.concat(this.nodes.slice(index + 1))
|
||||
|
||||
return this.merge({ nodes })
|
||||
},
|
||||
|
||||
@ -656,7 +678,12 @@ const Node = {
|
||||
key = normalizeKey(key)
|
||||
const child = this.getChild(key)
|
||||
const index = this.nodex.indexOf(child)
|
||||
nodes = this.nodes.splice(index, 0, nodes)
|
||||
|
||||
nodes = this.nodes
|
||||
.slice(0, index)
|
||||
.concat(nodes)
|
||||
.concat(this.nodes.slice(index))
|
||||
|
||||
return this.merge({ nodes })
|
||||
},
|
||||
|
||||
|
@ -411,9 +411,18 @@ class State extends Record(DEFAULTS) {
|
||||
insertFragment(fragment) {
|
||||
let state = this
|
||||
let { document, selection } = state
|
||||
let after = selection
|
||||
|
||||
//
|
||||
// If there's nothing in the fragment, do nothing.
|
||||
if (!fragment.length) return state
|
||||
|
||||
// Insert the fragment.
|
||||
document = document.insertFragmentAtRange(selection, fragment)
|
||||
|
||||
// Determine what the selection should be after inserting.
|
||||
const last = fragment.getTextNodes().last()
|
||||
selection = selection.moveToEndOf(last)
|
||||
state = state.merge({ document, selection })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,7 @@ const DOCUMENT_TRANSFORMS = [
|
||||
'deleteAtRange',
|
||||
'deleteBackwardAtRange',
|
||||
'deleteForwardAtRange',
|
||||
'insertFragmentAtRange',
|
||||
'insertTextAtRange',
|
||||
'markAtRange',
|
||||
'setBlockAtRange',
|
||||
@ -71,6 +72,7 @@ const STATE_TRANSFORMS = [
|
||||
'delete',
|
||||
'deleteBackward',
|
||||
'deleteForward',
|
||||
'insertFragment',
|
||||
'insertText',
|
||||
'mark',
|
||||
'setBlock',
|
||||
|
@ -163,6 +163,7 @@ const Transforms = {
|
||||
*/
|
||||
|
||||
insertFragmentAtRange(range, fragment) {
|
||||
debugger
|
||||
range = range.normalize(this)
|
||||
let node = this
|
||||
|
||||
@ -173,7 +174,7 @@ const Transforms = {
|
||||
}
|
||||
|
||||
// If the fragment is empty, do nothing.
|
||||
if (!fragment.nodes.size) return node
|
||||
if (!fragment.length) return node
|
||||
|
||||
// Split the inlines if need be.
|
||||
if (!node.isInlineSplitAtRange(range)) {
|
||||
@ -181,37 +182,39 @@ const Transforms = {
|
||||
}
|
||||
|
||||
// Insert the contents of the first block into the block at the cursor.
|
||||
const texts = fragment.getTextNodes()
|
||||
const firstText = texts.first()
|
||||
const lastText = texts.last()
|
||||
const firstBlock = fragment.getClosestBlock(firstText)
|
||||
let lastBlock = fragment.getClosestBlock(lastText)
|
||||
|
||||
const { startKey, endKey } = range
|
||||
let block = node.getClosestBlock(startKey)
|
||||
let start = node.getDescendant(startKey)
|
||||
if (!range.isAtEndOf(start)) start = node.getPreviousText(start)
|
||||
|
||||
const child = block.getHighestChild(start)
|
||||
const nextChild = block.getNextSibling(child)
|
||||
const startChild = block.getHighestChild(start)
|
||||
const nextChild = block.getNextSibling(startChild)
|
||||
|
||||
const blocks = fragment.getDeepestBlocks()
|
||||
const firstBlock = blocks.first()
|
||||
let lastBlock = blocks.last()
|
||||
|
||||
block = block.insertChildrenAfter(startChild, firstBlock.nodes)
|
||||
node = node.updateDescendant(block)
|
||||
|
||||
// If there are no other siblings, that's it.
|
||||
if (firstBlock == lastBlock) return node
|
||||
if (firstBlock == lastBlock) return node.normalize()
|
||||
|
||||
// Otherwise, remove the fragment's first block's highest solo parent...
|
||||
let highestParent = getHighestSoloParent(firstBlock)
|
||||
if (highestParent) fragment = fragment.removeDescendant(highestParent)
|
||||
let highestParent = fragment.getHighestOnlyChildParent(firstBlock)
|
||||
fragment = fragment.removeDescendant(highestParent || firstBlock)
|
||||
|
||||
// Then, add the inlines after the cursor from the current block to the
|
||||
// start of the last block in the fragment.
|
||||
lastBlock = lastBlock.concatChildren(block.getChildrenAfter(nextChild))
|
||||
lastBlock = lastBlock.concatChildren(block.getChildrenAfterIncluding(nextChild))
|
||||
fragment = fragment.updateDescendant(lastBlock)
|
||||
|
||||
block = block.removeChildrenAfterIncluding(nextChild)
|
||||
node = node.updateDescendant(block)
|
||||
|
||||
// Finally, add the fragment's children after the block.
|
||||
node = node.insertChildrenAfter(block, fragment.nodes)
|
||||
return node
|
||||
return node.normalize()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,14 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment one
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment two
|
@ -0,0 +1,25 @@
|
||||
|
||||
import path from 'path'
|
||||
import readMetadata from 'read-metadata'
|
||||
import { Raw } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const file = path.resolve(__dirname, 'fragment.yaml')
|
||||
const raw = readMetadata.sync(file)
|
||||
const fragment = Raw.deserialize(raw).document
|
||||
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 2,
|
||||
focusKey: first.key,
|
||||
focusOffset: 2
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.insertFragmentAtRange(range, fragment)
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,14 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: wofragment one
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment tword
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment
|
@ -0,0 +1,25 @@
|
||||
|
||||
import path from 'path'
|
||||
import readMetadata from 'read-metadata'
|
||||
import { Raw } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const file = path.resolve(__dirname, 'fragment.yaml')
|
||||
const raw = readMetadata.sync(file)
|
||||
const fragment = Raw.deserialize(raw).document
|
||||
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 2,
|
||||
focusKey: first.key,
|
||||
focusOffset: 2
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.insertFragmentAtRange(range, fragment)
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: wofragmentrd
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment
|
@ -0,0 +1,25 @@
|
||||
|
||||
import path from 'path'
|
||||
import readMetadata from 'read-metadata'
|
||||
import { Raw } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const file = path.resolve(__dirname, 'fragment.yaml')
|
||||
const raw = readMetadata.sync(file)
|
||||
const fragment = Raw.deserialize(raw).document
|
||||
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 2,
|
||||
focusKey: first.key,
|
||||
focusOffset: 2
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.insertFragmentAtRange(range, fragment)
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,20 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: wo
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: rd
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: list-item
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: fragment
|
@ -0,0 +1,26 @@
|
||||
|
||||
import path from 'path'
|
||||
import readMetadata from 'read-metadata'
|
||||
import { Raw } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const file = path.resolve(__dirname, 'fragment.yaml')
|
||||
const raw = readMetadata.sync(file)
|
||||
const fragment = Raw.deserialize(raw).document
|
||||
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const last = texts.last()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 2,
|
||||
focusKey: last.key,
|
||||
focusOffset: 2
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.insertFragmentAtRange(range, fragment)
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: another
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: wofragmentother
|
Loading…
x
Reference in New Issue
Block a user