mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-09 00:36:41 +02:00
got insert fragment working
This commit is contained in:
@@ -8,7 +8,7 @@ import Mark from './mark'
|
|||||||
import Selection from './selection'
|
import Selection from './selection'
|
||||||
import Transforms from './transforms'
|
import Transforms from './transforms'
|
||||||
import Text from './text'
|
import Text from './text'
|
||||||
import { List, Map, Set } from 'immutable'
|
import { List, Map, OrderedSet, Set } from 'immutable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node.
|
* Node.
|
||||||
@@ -228,6 +228,21 @@ const Node = {
|
|||||||
return this.nodes.find(node => node.key == key)
|
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`.
|
* Get a descendant node by `key`.
|
||||||
*
|
*
|
||||||
@@ -355,12 +370,14 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
getHighestOnlyChildParent(key) {
|
getHighestOnlyChildParent(key) {
|
||||||
|
let child = this.getChild(key)
|
||||||
let match = null
|
let match = null
|
||||||
let parent
|
let parent
|
||||||
|
|
||||||
while (parent = this.getParent(child)) {
|
while (parent = this.getParent(child)) {
|
||||||
if (parent == null || parent.nodes.size > 1) return match
|
if (parent == null || parent.nodes.size > 1) return match
|
||||||
match = parent
|
match = parent
|
||||||
|
child = parent
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -639,8 +656,13 @@ const Node = {
|
|||||||
insertChildrenAfter(key, nodes) {
|
insertChildrenAfter(key, nodes) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
const child = this.getChild(key)
|
const child = this.getChild(key)
|
||||||
const index = this.nodex.indexOf(child)
|
const index = this.nodes.indexOf(child)
|
||||||
nodes = this.nodes.splice(index + 1, 0, nodes)
|
|
||||||
|
nodes = this.nodes
|
||||||
|
.slice(0, index + 1)
|
||||||
|
.concat(nodes)
|
||||||
|
.concat(this.nodes.slice(index + 1))
|
||||||
|
|
||||||
return this.merge({ nodes })
|
return this.merge({ nodes })
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -656,7 +678,12 @@ const Node = {
|
|||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
const child = this.getChild(key)
|
const child = this.getChild(key)
|
||||||
const index = this.nodex.indexOf(child)
|
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 })
|
return this.merge({ nodes })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -411,9 +411,18 @@ class State extends Record(DEFAULTS) {
|
|||||||
insertFragment(fragment) {
|
insertFragment(fragment) {
|
||||||
let state = this
|
let state = this
|
||||||
let { document, selection } = state
|
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',
|
'deleteAtRange',
|
||||||
'deleteBackwardAtRange',
|
'deleteBackwardAtRange',
|
||||||
'deleteForwardAtRange',
|
'deleteForwardAtRange',
|
||||||
|
'insertFragmentAtRange',
|
||||||
'insertTextAtRange',
|
'insertTextAtRange',
|
||||||
'markAtRange',
|
'markAtRange',
|
||||||
'setBlockAtRange',
|
'setBlockAtRange',
|
||||||
@@ -71,6 +72,7 @@ const STATE_TRANSFORMS = [
|
|||||||
'delete',
|
'delete',
|
||||||
'deleteBackward',
|
'deleteBackward',
|
||||||
'deleteForward',
|
'deleteForward',
|
||||||
|
'insertFragment',
|
||||||
'insertText',
|
'insertText',
|
||||||
'mark',
|
'mark',
|
||||||
'setBlock',
|
'setBlock',
|
||||||
|
@@ -163,6 +163,7 @@ const Transforms = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
insertFragmentAtRange(range, fragment) {
|
insertFragmentAtRange(range, fragment) {
|
||||||
|
debugger
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
let node = this
|
let node = this
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ const Transforms = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the fragment is empty, do nothing.
|
// If the fragment is empty, do nothing.
|
||||||
if (!fragment.nodes.size) return node
|
if (!fragment.length) return node
|
||||||
|
|
||||||
// Split the inlines if need be.
|
// Split the inlines if need be.
|
||||||
if (!node.isInlineSplitAtRange(range)) {
|
if (!node.isInlineSplitAtRange(range)) {
|
||||||
@@ -181,37 +182,39 @@ const Transforms = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert the contents of the first block into the block at the cursor.
|
// 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
|
const { startKey, endKey } = range
|
||||||
let block = node.getClosestBlock(startKey)
|
let block = node.getClosestBlock(startKey)
|
||||||
let start = node.getDescendant(startKey)
|
let start = node.getDescendant(startKey)
|
||||||
if (!range.isAtEndOf(start)) start = node.getPreviousText(start)
|
if (!range.isAtEndOf(start)) start = node.getPreviousText(start)
|
||||||
|
|
||||||
const child = block.getHighestChild(start)
|
const startChild = block.getHighestChild(start)
|
||||||
const nextChild = block.getNextSibling(child)
|
const nextChild = block.getNextSibling(startChild)
|
||||||
|
|
||||||
|
const blocks = fragment.getDeepestBlocks()
|
||||||
|
const firstBlock = blocks.first()
|
||||||
|
let lastBlock = blocks.last()
|
||||||
|
|
||||||
block = block.insertChildrenAfter(startChild, firstBlock.nodes)
|
block = block.insertChildrenAfter(startChild, firstBlock.nodes)
|
||||||
node = node.updateDescendant(block)
|
node = node.updateDescendant(block)
|
||||||
|
|
||||||
// If there are no other siblings, that's it.
|
// 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...
|
// Otherwise, remove the fragment's first block's highest solo parent...
|
||||||
let highestParent = getHighestSoloParent(firstBlock)
|
let highestParent = fragment.getHighestOnlyChildParent(firstBlock)
|
||||||
if (highestParent) fragment = fragment.removeDescendant(highestParent)
|
fragment = fragment.removeDescendant(highestParent || firstBlock)
|
||||||
|
|
||||||
// Then, add the inlines after the cursor from the current block to the
|
// Then, add the inlines after the cursor from the current block to the
|
||||||
// start of the last block in the fragment.
|
// 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)
|
block = block.removeChildrenAfterIncluding(nextChild)
|
||||||
|
node = node.updateDescendant(block)
|
||||||
|
|
||||||
// Finally, add the fragment's children after the block.
|
// Finally, add the fragment's children after the block.
|
||||||
node = node.insertChildrenAfter(block, fragment.nodes)
|
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
|
Reference in New Issue
Block a user