mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 15:23:29 -05:00
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
|
import type { Squire } from '../Editor';
|
||
|
import { getPreviousBlock } from '../node/Block';
|
||
|
import {
|
||
|
fixContainer,
|
||
|
mergeContainers,
|
||
|
mergeWithBlock,
|
||
|
} from '../node/MergeSplit';
|
||
|
import { getNearest } from '../node/Node';
|
||
|
import {
|
||
|
getStartBlockOfRange,
|
||
|
rangeDoesStartAtBlockBoundary,
|
||
|
} from '../range/Block';
|
||
|
import { deleteContentsOfRange } from '../range/InsertDelete';
|
||
|
import { afterDelete, detachUneditableNode } from './KeyHelpers';
|
||
|
|
||
|
// ---
|
||
|
|
||
|
const Backspace = (self: Squire, event: KeyboardEvent, range: Range): void => {
|
||
|
const root: Element = self._root;
|
||
|
self._removeZWS();
|
||
|
// Record undo checkpoint.
|
||
|
self.saveUndoState(range);
|
||
|
// If not collapsed, delete contents
|
||
|
if (!range.collapsed) {
|
||
|
event.preventDefault();
|
||
|
deleteContentsOfRange(range, root);
|
||
|
afterDelete(self, range);
|
||
|
// If at beginning of block, merge with previous
|
||
|
} else if (rangeDoesStartAtBlockBoundary(range, root)) {
|
||
|
event.preventDefault();
|
||
|
const startBlock = getStartBlockOfRange(range, root);
|
||
|
if (!startBlock) {
|
||
|
return;
|
||
|
}
|
||
|
let current = startBlock;
|
||
|
// In case inline data has somehow got between blocks.
|
||
|
fixContainer(current.parentNode!, root);
|
||
|
// Now get previous block
|
||
|
const previous = getPreviousBlock(current, root);
|
||
|
// Must not be at the very beginning of the text area.
|
||
|
if (previous) {
|
||
|
// If not editable, just delete whole block.
|
||
|
if (!(previous as HTMLElement).isContentEditable) {
|
||
|
detachUneditableNode(previous, root);
|
||
|
return;
|
||
|
}
|
||
|
// Otherwise merge.
|
||
|
mergeWithBlock(previous, current, range, root);
|
||
|
// If deleted line between containers, merge newly adjacent
|
||
|
// containers.
|
||
|
current = previous.parentNode as HTMLElement;
|
||
|
while (current !== root && !current.nextSibling) {
|
||
|
current = current.parentNode as HTMLElement;
|
||
|
}
|
||
|
if (
|
||
|
current !== root &&
|
||
|
(current = current.nextSibling as HTMLElement)
|
||
|
) {
|
||
|
mergeContainers(current, root);
|
||
|
}
|
||
|
self.setSelection(range);
|
||
|
// If at very beginning of text area, allow backspace
|
||
|
// to break lists/blockquote.
|
||
|
} else if (current) {
|
||
|
if (
|
||
|
getNearest(current, root, 'UL') ||
|
||
|
getNearest(current, root, 'OL')
|
||
|
) {
|
||
|
// Break list
|
||
|
self.decreaseListLevel(range);
|
||
|
return;
|
||
|
} else if (getNearest(current, root, 'BLOCKQUOTE')) {
|
||
|
// Break blockquote
|
||
|
self.removeQuote(range);
|
||
|
return;
|
||
|
}
|
||
|
self.setSelection(range);
|
||
|
self._updatePath(range, true);
|
||
|
}
|
||
|
// Otherwise, leave to browser but check afterwards whether it has
|
||
|
// left behind an empty inline tag.
|
||
|
} else {
|
||
|
self.setSelection(range);
|
||
|
setTimeout(() => {
|
||
|
afterDelete(self);
|
||
|
}, 0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// ---
|
||
|
|
||
|
export { Backspace };
|