diff --git a/packages/slate/src/commands/at-range.js b/packages/slate/src/commands/at-range.js index ed2b598b1..53d30f3ac 100644 --- a/packages/slate/src/commands/at-range.js +++ b/packages/slate/src/commands/at-range.js @@ -668,6 +668,7 @@ Commands.insertFragmentAtRange = (change, range, fragment) => { const lastChild = fragment.nodes.last() const firstBlock = blocks.first() const lastBlock = blocks.last() + const insertionNode = findInsertionNode(fragment, document, startBlock.key) // If the fragment only contains a void block, use `insertBlock` instead. if (firstBlock === lastBlock && change.isVoid(firstBlock)) { @@ -675,9 +676,12 @@ Commands.insertFragmentAtRange = (change, range, fragment) => { return } - // If the fragment starts or ends with single nested block, (e.g., table), - // do not merge this fragment with existing blocks. - if (firstChild.hasBlockChildren() || lastChild.hasBlockChildren()) { + // If inserting the entire fragment and it starts or ends with a single + // nested block, e.g. a table, we do not merge it with existing blocks. + if ( + insertionNode === fragment && + (firstChild.hasBlockChildren() || lastChild.hasBlockChildren()) + ) { fragment.nodes.reverse().forEach(node => { change.insertBlockAtRange(range, node) }) @@ -685,17 +689,18 @@ Commands.insertFragmentAtRange = (change, range, fragment) => { } // If the first and last block aren't the same, we need to insert all of the - // nodes after the fragment's first block at the index. + // nodes after the insertion node's first block at the index. if (firstBlock != lastBlock) { - const lonelyParent = fragment.getFurthest( + const lonelyParent = insertionNode.getFurthest( firstBlock.key, p => p.nodes.size == 1 ) const lonelyChild = lonelyParent || firstBlock - const startIndex = parent.nodes.indexOf(startBlock) - fragment = fragment.removeNode(lonelyChild.key) - fragment.nodes.forEach((node, i) => { + const startIndex = parent.nodes.indexOf(startBlock) + const excludingLonelyChild = insertionNode.removeNode(lonelyChild.key) + + excludingLonelyChild.nodes.forEach((node, i) => { const newIndex = startIndex + i + 1 change.insertNodeByKey(parent.key, newIndex, node) }) @@ -750,6 +755,34 @@ Commands.insertFragmentAtRange = (change, range, fragment) => { }) } +const findInsertionNode = (fragment, document, startKey) => { + const hasSingleNode = object => object && object.nodes.size === 1 + const firstNode = object => object && object.nodes.first() + let node = fragment + + if (hasSingleNode(fragment)) { + let fragmentInner = firstNode(fragment) + + const matches = documentNode => documentNode.type === fragmentInner.type + let documentInner = document.getFurthest(startKey, matches) + + if (documentInner === document.getParent(startKey)) node = fragmentInner + + while (hasSingleNode(fragmentInner) && hasSingleNode(documentInner)) { + fragmentInner = firstNode(fragmentInner) + documentInner = firstNode(documentInner) + + if (fragmentInner.type === documentInner.type) { + node = fragmentInner + } else { + break + } + } + } + + return node +} + /** * Insert an `inline` node at `range`. * diff --git a/packages/slate/test/commands/at-current-range/insert-fragment/merge-deep-nested.js b/packages/slate/test/commands/at-current-range/insert-fragment/merge-deep-nested.js new file mode 100644 index 000000000..f96c6ddcf --- /dev/null +++ b/packages/slate/test/commands/at-current-range/insert-fragment/merge-deep-nested.js @@ -0,0 +1,49 @@ +/** @jsx h */ + +import h from '../../../helpers/h' + +export default function(change) { + change.insertFragment( + + + + 2 + 3 + + + + ) +} + +export const input = ( + + + + + + + 1 + + + + + + +) + +export const output = ( + + + + + + 12 + + 3 + + + + + + +) diff --git a/packages/slate/test/commands/at-current-range/insert-fragment/merge-lists.js b/packages/slate/test/commands/at-current-range/insert-fragment/merge-lists.js new file mode 100644 index 000000000..e5f77feaf --- /dev/null +++ b/packages/slate/test/commands/at-current-range/insert-fragment/merge-lists.js @@ -0,0 +1,41 @@ +/** @jsx h */ + +import h from '../../../helpers/h' + +export default function(change) { + change.insertFragment( + + + 3 + 4 + + + ) +} + +export const input = ( + + + + 1 + + 2 + + + + +) + +export const output = ( + + + + 1 + 23 + + 4 + + + + +) diff --git a/packages/slate/test/commands/at-current-range/insert-fragment/nested-block-fragment-nested-blocks.js b/packages/slate/test/commands/at-current-range/insert-fragment/nested-block-fragment-nested-blocks.js index 4b44fc20c..0ba73cfa1 100644 --- a/packages/slate/test/commands/at-current-range/insert-fragment/nested-block-fragment-nested-blocks.js +++ b/packages/slate/test/commands/at-current-range/insert-fragment/nested-block-fragment-nested-blocks.js @@ -29,14 +29,10 @@ export const output = ( - wo + woone - one - two + tword - - rd -