Skip to content

Commit

Permalink
Fix Mathjax sometimes being deleted even though it's not selected (#1696
Browse files Browse the repository at this point in the history
)

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

* Fix mixing up preceding/following
  • Loading branch information
hgiesel committed Feb 27, 2022
1 parent 5963791 commit 55c64e5
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 28 deletions.
40 changes: 18 additions & 22 deletions ts/editable/frame-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { on } from "../lib/events";
import type { FrameHandle } from "./frame-handle";
import {
checkWhetherMovingIntoHandle,
checkHandles,
frameElementTagName,
FrameEnd,
FrameStart,
Expand All @@ -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 (!isFrameHandle(node)) {
continue;
}

if (
/* avoid triggering when (un)mounting whole frame */
mutations.length === 1 &&
nodeIsElement(node) &&
isFrameHandle(node)
!node.partiallySelected
) {
/* When deleting from _outer_ position in FrameHandle to _inner_ position */
// 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 (
nodeIsElement(node) &&
isFrameHandle(node) &&
frameElement.isConnected &&
!frameElement.block
) {
if (frameElement.isConnected && !frameElement.block) {
frameElement.refreshHandles();
continue;
}
Expand Down Expand Up @@ -248,7 +244,7 @@ function checkIfInsertingLineBreakAdjacentToBlockFrame() {
}

function onSelectionChange() {
checkWhetherMovingIntoHandle();
checkHandles();
checkIfInsertingLineBreakAdjacentToBlockFrame();
}

Expand All @@ -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 {
Expand Down
33 changes: 27 additions & 6 deletions ts/editable/frame-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 55c64e5

Please sign in to comment.