Fix the caret flushing behavior when moving out of a Mathjax element (#1605)

* Fix the caret flushing behavior when moving out of a Mathjax element

* Fix typing issue and add docs for ContentEditableAPI
This commit is contained in:
Henrik Giesel 2022-01-19 01:17:53 +01:00 committed by GitHub
parent 0bb04e2951
commit f90895995f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 49 deletions

View File

@ -10,9 +10,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { Writable } from "svelte/store";
import { updateAllState } from "../components/WithState.svelte";
import actionList from "../sveltelib/action-list";
import contentEditableAPI, {
saveLocation,
initialFocusHandling,
import {
customFocusHandling,
preventBuiltinContentEditableShortcuts,
} from "./content-editable";
import type { ContentEditableAPI } from "./content-editable";
@ -33,14 +32,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let api: Partial<ContentEditableAPI>;
Object.assign(api, contentEditableAPI);
const { setupFocusHandling, flushCaret } = customFocusHandling();
Object.assign(api, { flushCaret });
</script>
<anki-editable
contenteditable="true"
use:resolve
use:saveLocation
use:initialFocusHandling
use:setupFocusHandling
use:preventBuiltinContentEditableShortcuts
use:mirrorAction={mirrorOptions}
use:managerAction={{}}

View File

@ -9,16 +9,6 @@ import { isApplePlatform } from "../lib/platform";
import { bridgeCommand } from "../lib/bridgecommand";
import type { SelectionLocation } from "../domlib/location";
const locationEvents: (() => void)[] = [];
function flushLocation(): void {
let removeEvent: (() => void) | undefined;
while ((removeEvent = locationEvents.pop())) {
removeEvent();
}
}
function safePlaceCaretAfterContent(editable: HTMLElement): void {
/**
* Workaround: If you try to invoke an IME after calling
@ -44,37 +34,53 @@ function onFocus(location: SelectionLocation | null): () => void {
};
}
function onBlur(this: HTMLElement): void {
prepareFocusHandling(this, saveSelection(this));
interface CustomFocusHandlingAPI {
setupFocusHandling(element: HTMLElement): { destroy(): void };
flushCaret(): void;
}
function prepareFocusHandling(
editable: HTMLElement,
latestLocation: SelectionLocation | null = null,
): void {
const removeOnFocus = on(editable, "focus", onFocus(latestLocation), {
once: true,
});
export function customFocusHandling(): CustomFocusHandlingAPI {
const focusHandlingEvents: (() => void)[] = [];
locationEvents.push(
removeOnFocus,
on(editable, "pointerdown", removeOnFocus, { once: true }),
);
}
function flushEvents(): void {
let removeEvent: (() => void) | undefined;
export function initialFocusHandling(editable: HTMLElement): void {
prepareFocusHandling(editable);
}
while ((removeEvent = focusHandlingEvents.pop())) {
removeEvent();
}
}
/* Must execute before DOMMirror */
export function saveLocation(editable: HTMLElement): { destroy(): void } {
const removeOnBlur = on(editable, "blur", onBlur);
function prepareFocusHandling(
editable: HTMLElement,
latestLocation: SelectionLocation | null = null,
): void {
const off = on(editable, "focus", onFocus(latestLocation), { once: true });
focusHandlingEvents.push(off, on(editable, "pointerdown", off, { once: true }));
}
/**
* Must execute before DOMMirror.
*/
function onBlur(this: HTMLElement): void {
prepareFocusHandling(this, saveSelection(this));
}
function setupFocusHandling(editable: HTMLElement): { destroy(): void } {
prepareFocusHandling(editable);
const off = on(editable, "blur", onBlur);
return {
destroy() {
flushEvents();
off();
},
};
}
return {
destroy() {
removeOnBlur();
flushLocation();
},
setupFocusHandling,
flushCaret: flushEvents,
};
}
@ -91,11 +97,10 @@ export function preventBuiltinContentEditableShortcuts(editable: HTMLElement): v
/** API */
export interface ContentEditableAPI {
flushLocation(): void;
/**
* Can be used to turn off the caret restoring functionality of
* the ContentEditable. Can be used when you want to set the caret
* yourself.
*/
flushCaret(): void;
}
const contentEditableApi: ContentEditableAPI = {
flushLocation,
};
export default contentEditableApi;

View File

@ -16,7 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { Mathjax } from "../../editable/mathjax-element";
const { container, api } = getRichTextInput();
const { flushLocation, preventResubscription } = api;
const { flushCaret, preventResubscription } = api;
const code = writable("");
@ -40,7 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let selectAll = false;
function placeHandle(after: boolean): void {
flushLocation();
flushCaret();
if (after) {
(mathjaxElement as any).placeCaretAfter();