1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-23 15:32:59 +02:00

feat: delete current line when deleting backward with line unit (#3364)

* feat: delete current line when deleting backward with line unit

* Apply BrentFarase suggestions from code review

Co-authored-by: Brent Farese <25846953+BrentFarese@users.noreply.github.com>

* Update prettier formats and rebase on master

Co-authored-by: Brent Farese <25846953+BrentFarese@users.noreply.github.com>
This commit is contained in:
Austin Green
2021-03-31 12:55:44 -07:00
committed by GitHub
parent f8be509e4d
commit 4dd8b98f7f
2 changed files with 109 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import { ReactEditor } from './react-editor'
import { Key } from '../utils/key'
import { EDITOR_TO_ON_CHANGE, NODE_TO_KEY } from '../utils/weak-maps'
import { isDOMText, getPlainText } from '../utils/dom'
import { findCurrentLineRange } from '../utils/lines'
/**
* `withReact` adds React and DOM specific behaviors to the editor.
@@ -17,7 +18,35 @@ import { isDOMText, getPlainText } from '../utils/dom'
export const withReact = <T extends Editor>(editor: T) => {
const e = editor as T & ReactEditor
const { apply, onChange } = e
const { apply, onChange, deleteBackward } = e
e.deleteBackward = unit => {
if (unit !== 'line') {
return deleteBackward(unit)
}
if (editor.selection && Range.isCollapsed(editor.selection)) {
const parentBlockEntry = Editor.above(editor, {
match: n => Editor.isBlock(editor, n),
at: editor.selection,
})
if (parentBlockEntry) {
const [, parentBlockPath] = parentBlockEntry
const parentElementRange = Editor.range(
editor,
parentBlockPath,
editor.selection.anchor
)
const currentLineRange = findCurrentLineRange(e, parentElementRange)
if (!Range.isCollapsed(currentLineRange)) {
Transforms.delete(editor, { at: currentLineRange })
}
}
}
}
e.apply = (op: Operation) => {
const matches: [Path, Key][] = []

View File

@@ -0,0 +1,79 @@
/**
* Utilities for single-line deletion
*/
import { Range, Editor } from 'slate'
import { ReactEditor } from '..'
const doRectsIntersect = (rect: DOMRect, compareRect: DOMRect) => {
const middle = (compareRect.top + compareRect.bottom) / 2
return rect.top <= middle && rect.bottom >= middle
}
const areRangesSameLine = (
editor: ReactEditor,
range1: Range,
range2: Range
) => {
const rect1 = ReactEditor.toDOMRange(editor, range1).getBoundingClientRect()
const rect2 = ReactEditor.toDOMRange(editor, range2).getBoundingClientRect()
return doRectsIntersect(rect1, rect2) && doRectsIntersect(rect2, rect1)
}
/**
* A helper utility that returns the end portion of a `Range`
* which is located on a single line.
*
* @param {Editor} editor The editor object to compare against
* @param {Range} parentRange The parent range to compare against
* @returns {Range} A valid portion of the parentRange which is one a single line
*/
export const findCurrentLineRange = (
editor: ReactEditor,
parentRange: Range
): Range => {
const parentRangeBoundary = Editor.range(editor, Range.end(parentRange))
const positions = Array.from(Editor.positions(editor, { at: parentRange }))
let left = 0
let right = positions.length
let middle = Math.floor(right / 2)
if (
areRangesSameLine(
editor,
Editor.range(editor, positions[left]),
parentRangeBoundary
)
) {
return Editor.range(editor, positions[left], parentRangeBoundary)
}
if (positions.length < 2) {
return Editor.range(
editor,
positions[positions.length - 1],
parentRangeBoundary
)
}
while (middle !== positions.length && middle !== left) {
if (
areRangesSameLine(
editor,
Editor.range(editor, positions[middle]),
parentRangeBoundary
)
) {
right = middle
} else {
left = middle
}
middle = Math.floor((left + right) / 2)
}
return Editor.range(editor, positions[right], parentRangeBoundary)
}