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
-