1
0
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:
Ian Storm Taylor 2016-06-24 18:01:34 -07:00
parent 0af3dbcc79
commit d9a33657a8
20 changed files with 291 additions and 20 deletions

View File

@ -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 })
},

View File

@ -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
}
/**

View File

@ -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',

View File

@ -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()
},
/**

View File

@ -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

View File

@ -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()
}

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: word

View File

@ -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

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: list-item
nodes:
- kind: text
ranges:
- text: fragment

View File

@ -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()
}

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: wofragmentrd

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: list-item
nodes:
- kind: text
ranges:
- text: fragment

View File

@ -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()
}

View File

@ -0,0 +1,11 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: link
nodes:
- kind: text
ranges:
- text: word

View File

@ -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

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: list-item
nodes:
- kind: text
ranges:
- text: fragment

View File

@ -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()
}

View File

@ -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

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: wofragmentother