Fix Mathjax sometimes being deleted even though it's not selected (#1696)

* Fix frame element being deleted, when a frame handle is deleted from while selected

* Fix mixing up preceding/following
This commit is contained in:
Henrik Giesel 2022-02-27 08:58:01 +01:00 committed by GitHub
parent 5963791d85
commit 55c64e5b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 31 deletions

View File

@ -13,7 +13,7 @@ import {
import { on } from "../lib/events";
import type { FrameHandle } from "./frame-handle";
import {
checkWhetherMovingIntoHandle,
checkHandles,
frameElementTagName,
FrameEnd,
FrameStart,
@ -32,43 +32,39 @@ function restoreFrameHandles(mutations: MutationRecord[]): void {
continue;
}
/**
* In some rare cases, nodes might be inserted into the frame itself.
* For example after using execCommand.
*/
const placement = node.compareDocumentPosition(framed);
// In some rare cases, nodes might be inserted into the frame itself.
// For example after using execCommand.
const placement = framed.compareDocumentPosition(node);
if (placement & Node.DOCUMENT_POSITION_FOLLOWING) {
referenceNode = moveChildOutOfElement(frameElement, node, "afterend");
continue;
} else if (placement & Node.DOCUMENT_POSITION_PRECEDING) {
if (placement & Node.DOCUMENT_POSITION_PRECEDING) {
referenceNode = moveChildOutOfElement(
frameElement,
node,
"beforebegin",
);
continue;
} else if (placement & Node.DOCUMENT_POSITION_FOLLOWING) {
referenceNode = moveChildOutOfElement(frameElement, node, "afterend");
}
}
for (const node of mutation.removedNodes) {
if (
/* avoid triggering when (un)mounting whole frame */
mutations.length === 1 &&
nodeIsElement(node) &&
isFrameHandle(node)
) {
/* When deleting from _outer_ position in FrameHandle to _inner_ position */
frameElement.remove();
if (!isFrameHandle(node)) {
continue;
}
if (
nodeIsElement(node) &&
isFrameHandle(node) &&
frameElement.isConnected &&
!frameElement.block
/* avoid triggering when (un)mounting whole frame */
mutations.length === 1 &&
!node.partiallySelected
) {
// Similar to a "movein", this could be considered a
// "deletein" event and could get some special treatment, e.g.
// first highlight the entire frame-element.
frameElement.remove();
continue;
}
if (frameElement.isConnected && !frameElement.block) {
frameElement.refreshHandles();
continue;
}
@ -248,7 +244,7 @@ function checkIfInsertingLineBreakAdjacentToBlockFrame() {
}
function onSelectionChange() {
checkWhetherMovingIntoHandle();
checkHandles();
checkIfInsertingLineBreakAdjacentToBlockFrame();
}
@ -259,7 +255,7 @@ document.addEventListener("selectionchange", onSelectionChange);
* <anki-frame>
* <frame-handle-start> </frame-handle-start>
* <your-element ... />
* <frame-handle-end> </frame-handle-start>
* <frame-handle-end> </frame-handle-end>
* </anki-frame>
*/
export function frameElement(element: HTMLElement, block: boolean): FrameElement {

View File

@ -120,6 +120,14 @@ export abstract class FrameHandle extends HTMLElement {
return ["data-frames"];
}
/**
* When a deletion is trigger with a FrameHandle selected, it will be treated
* differently depending on whether it is selected:
* - If partially selected, it should be restored (unless the frame element
* is also selected).
* - Otherwise, it should be deleted along with the frame element.
*/
partiallySelected = false;
frames?: string;
constructor() {
@ -273,15 +281,28 @@ export class FrameEnd extends FrameHandle {
}
}
export function checkWhetherMovingIntoHandle(): void {
function checkWhetherMovingIntoHandle(selection: Selection, handle: FrameHandle): void {
if (selection.anchorNode === handle.firstChild && isSelectionCollapsed(selection)) {
handle.notifyMoveIn(selection.anchorOffset);
}
}
function checkWhetherSelectingHandle(selection: Selection, handle: FrameHandle): void {
handle.partiallySelected =
handle.firstChild && !isSelectionCollapsed(selection)
? selection.containsNode(handle.firstChild)
: false;
}
export function checkHandles(): void {
for (const handle of handles) {
const selection = getSelection(handle)!;
if (
selection.anchorNode === handle.firstChild &&
isSelectionCollapsed(selection)
) {
handle.notifyMoveIn(selection.anchorOffset);
if (selection.rangeCount === 0) {
return;
}
checkWhetherMovingIntoHandle(selection, handle);
checkWhetherSelectingHandle(selection, handle);
}
}