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:
parent
0bb04e2951
commit
f90895995f
@ -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={{}}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user