mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 23:40:35 -05:00
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
|
import { getNextBlock } from '../node/Block';
|
||
|
import {
|
||
|
fixContainer,
|
||
|
mergeWithBlock,
|
||
|
mergeContainers,
|
||
|
} from '../node/MergeSplit';
|
||
|
import { detach } from '../node/Node';
|
||
|
import {
|
||
|
rangeDoesEndAtBlockBoundary,
|
||
|
getStartBlockOfRange,
|
||
|
} from '../range/Block';
|
||
|
import {
|
||
|
moveRangeBoundariesUpTree,
|
||
|
moveRangeBoundariesDownTree,
|
||
|
} from '../range/Boundaries';
|
||
|
import { deleteContentsOfRange } from '../range/InsertDelete';
|
||
|
import { afterDelete, detachUneditableNode } from './KeyHelpers';
|
||
|
|
||
|
import type { Squire } from '../Editor';
|
||
|
|
||
|
// ---
|
||
|
|
||
|
const Delete = (self: Squire, event: KeyboardEvent, range: Range): void => {
|
||
|
const root = self._root;
|
||
|
let current: Node | null;
|
||
|
let next: Node | null;
|
||
|
let originalRange: Range;
|
||
|
let cursorContainer: Node;
|
||
|
let cursorOffset: number;
|
||
|
let nodeAfterCursor: Node;
|
||
|
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 end of block, merge next into this block
|
||
|
} else if (rangeDoesEndAtBlockBoundary(range, root)) {
|
||
|
event.preventDefault();
|
||
|
current = getStartBlockOfRange(range, root);
|
||
|
if (!current) {
|
||
|
return;
|
||
|
}
|
||
|
// In case inline data has somehow got between blocks.
|
||
|
fixContainer(current.parentNode!, root);
|
||
|
// Now get next block
|
||
|
next = getNextBlock(current, root);
|
||
|
// Must not be at the very end of the text area.
|
||
|
if (next) {
|
||
|
// If not editable, just delete whole block.
|
||
|
if (!(next as HTMLElement).isContentEditable) {
|
||
|
detachUneditableNode(next, root);
|
||
|
return;
|
||
|
}
|
||
|
// Otherwise merge.
|
||
|
mergeWithBlock(current, next, range, root);
|
||
|
// If deleted line between containers, merge newly adjacent
|
||
|
// containers.
|
||
|
next = current.parentNode!;
|
||
|
while (next !== root && !next.nextSibling) {
|
||
|
next = next.parentNode!;
|
||
|
}
|
||
|
if (next !== root && (next = next.nextSibling)) {
|
||
|
mergeContainers(next, root);
|
||
|
}
|
||
|
self.setSelection(range);
|
||
|
self._updatePath(range, true);
|
||
|
}
|
||
|
// Otherwise, leave to browser but check afterwards whether it has
|
||
|
// left behind an empty inline tag.
|
||
|
} else {
|
||
|
// But first check if the cursor is just before an IMG tag. If so,
|
||
|
// delete it ourselves, because the browser won't if it is not
|
||
|
// inline.
|
||
|
originalRange = range.cloneRange();
|
||
|
moveRangeBoundariesUpTree(range, root, root, root);
|
||
|
cursorContainer = range.endContainer;
|
||
|
cursorOffset = range.endOffset;
|
||
|
if (cursorContainer instanceof Element) {
|
||
|
nodeAfterCursor = cursorContainer.childNodes[cursorOffset];
|
||
|
if (nodeAfterCursor && nodeAfterCursor.nodeName === 'IMG') {
|
||
|
event.preventDefault();
|
||
|
detach(nodeAfterCursor);
|
||
|
moveRangeBoundariesDownTree(range);
|
||
|
afterDelete(self, range);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
self.setSelection(originalRange);
|
||
|
setTimeout(() => {
|
||
|
afterDelete(self);
|
||
|
}, 0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// ---
|
||
|
|
||
|
export { Delete };
|