diff --git a/.changeset/sharp-donkeys-sparkle.md b/.changeset/sharp-donkeys-sparkle.md new file mode 100644 index 000000000..e1da53aa9 --- /dev/null +++ b/.changeset/sharp-donkeys-sparkle.md @@ -0,0 +1,5 @@ +--- +'slate': patch +--- + +Fix setNodes when called with 'split' and a collapsed range diff --git a/packages/slate/src/interfaces/range.ts b/packages/slate/src/interfaces/range.ts index 0f68e1789..16352acbb 100644 --- a/packages/slate/src/interfaces/range.ts +++ b/packages/slate/src/interfaces/range.ts @@ -222,12 +222,16 @@ export const Range: RangeInterface = { let affinityFocus: 'forward' | 'backward' | null if (affinity === 'inward') { + // If the range is collapsed, make sure to use the same affinity to + // avoid the two points passing each other and expanding in the opposite + // direction + const isCollapsed = Range.isCollapsed(r) if (Range.isForward(r)) { affinityAnchor = 'forward' - affinityFocus = 'backward' + affinityFocus = isCollapsed ? affinityAnchor : 'backward' } else { affinityAnchor = 'backward' - affinityFocus = 'forward' + affinityFocus = isCollapsed ? affinityAnchor : 'forward' } } else if (affinity === 'outward') { if (Range.isForward(r)) { diff --git a/packages/slate/src/transforms/node.ts b/packages/slate/src/transforms/node.ts index 00567dfd7..d4036a61e 100644 --- a/packages/slate/src/transforms/node.ts +++ b/packages/slate/src/transforms/node.ts @@ -594,6 +594,14 @@ export const NodeTransforms: NodeTransforms = { } if (split && Range.isRange(at)) { + if ( + Range.isCollapsed(at) && + Editor.leaf(editor, at.anchor)[0].text.length > 0 + ) { + // If the range is collapsed in a non-empty node and 'split' is true, there's nothing to + // set that won't get normalized away + return + } const rangeRef = Editor.rangeRef(editor, at, { affinity: 'inward' }) const [start, end] = Range.edges(at) const splitMode = mode === 'lowest' ? 'lowest' : 'highest' diff --git a/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx b/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx new file mode 100644 index 000000000..003e7507b --- /dev/null +++ b/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx @@ -0,0 +1,34 @@ +import { Range } from 'slate' + +export const input = { + anchor: { + path: [0, 0], + offset: 1, + }, + focus: { + path: [0, 0], + offset: 1, + }, +} +export const test = value => { + return Range.transform( + value, + { + type: 'split_node', + path: [0, 0], + position: 1, + properties: {}, + }, + { affinity: 'inward' } + ) +} +export const output = { + anchor: { + path: [0, 1], + offset: 0, + }, + focus: { + path: [0, 1], + offset: 0, + }, +} diff --git a/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx b/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx new file mode 100644 index 000000000..9b36beb26 --- /dev/null +++ b/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx @@ -0,0 +1,27 @@ +/** @jsx jsx */ +import { Transforms, Text } from 'slate' +import { jsx } from '../../..' + +export const run = editor => { + Transforms.setNodes( + editor, + { someKey: true }, + { match: Text.isText, split: true } + ) +} +export const input = ( + + + w + ord + + +) +export const output = ( + + + w + ord + + +)