mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-24 07:52:50 +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:
@@ -5,6 +5,7 @@ import { ReactEditor } from './react-editor'
|
|||||||
import { Key } from '../utils/key'
|
import { Key } from '../utils/key'
|
||||||
import { EDITOR_TO_ON_CHANGE, NODE_TO_KEY } from '../utils/weak-maps'
|
import { EDITOR_TO_ON_CHANGE, NODE_TO_KEY } from '../utils/weak-maps'
|
||||||
import { isDOMText, getPlainText } from '../utils/dom'
|
import { isDOMText, getPlainText } from '../utils/dom'
|
||||||
|
import { findCurrentLineRange } from '../utils/lines'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `withReact` adds React and DOM specific behaviors to the editor.
|
* `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) => {
|
export const withReact = <T extends Editor>(editor: T) => {
|
||||||
const e = editor as T & ReactEditor
|
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) => {
|
e.apply = (op: Operation) => {
|
||||||
const matches: [Path, Key][] = []
|
const matches: [Path, Key][] = []
|
||||||
|
79
packages/slate-react/src/utils/lines.ts
Normal file
79
packages/slate-react/src/utils/lines.ts
Normal 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)
|
||||||
|
}
|
Reference in New Issue
Block a user