Set caret inside chemical expression in Mathjax (#1763)

This commit is contained in:
Henrik Giesel 2022-03-31 15:39:49 +02:00 committed by GitHub
parent 6718e67883
commit 67c1f7368e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 51 deletions

View file

@ -41,11 +41,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let image: HTMLImageElement; let image: HTMLImageElement;
export function moveCaretAfter(): void { export function moveCaretAfter(position?: [number, number]): void {
// This should trigger a focusing of the Mathjax Handle // This should trigger a focusing of the Mathjax Handle
image.dispatchEvent( image.dispatchEvent(
new CustomEvent("movecaretafter", { new CustomEvent("movecaretafter", {
detail: image, detail: { image, position },
bubbles: true, bubbles: true,
composed: true, composed: true,
}), }),

View file

@ -121,7 +121,15 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
}); });
if (this.hasAttribute("focusonmount")) { if (this.hasAttribute("focusonmount")) {
this.component.moveCaretAfter(); let position: [number, number] | undefined = undefined;
if (this.getAttribute("focusonmount")!.length > 0) {
position = this.getAttribute("focusonmount")!
.split(",")
.map(Number) as [number, number];
}
this.component.moveCaretAfter(position);
} }
this.setAttribute("contentEditable", "false"); this.setAttribute("contentEditable", "false");

View file

@ -52,9 +52,12 @@ export const gutterOptions: CodeMirror.EditorConfiguration = {
foldGutter: true, foldGutter: true,
}; };
export function focusAndCaretAfter(editor: CodeMirror.Editor): void { export function focusAndSetCaret(
editor: CodeMirror.Editor,
position: CodeMirror.Position = { line: editor.lineCount(), ch: 0 },
): void {
editor.focus(); editor.focus();
editor.setCursor(editor.lineCount(), 0); editor.setCursor(position);
} }
interface OpenCodeMirrorOptions { interface OpenCodeMirrorOptions {

View file

@ -35,7 +35,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
function onMathjaxChemistry(): void { function onMathjaxChemistry(): void {
surround("<anki-mathjax focusonmount>\\ce{", "}</anki-mathjax>"); surround('<anki-mathjax focusonmount="0,4">\\ce{', "}</anki-mathjax>");
} }
function onLatex(): void { function onLatex(): void {

View file

@ -10,7 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import * as tr from "../../lib/ftl"; import * as tr from "../../lib/ftl";
import { noop } from "../../lib/functional"; import { noop } from "../../lib/functional";
import { getPlatformString } from "../../lib/shortcuts"; import { getPlatformString } from "../../lib/shortcuts";
import { baseOptions, focusAndCaretAfter, latex } from "../code-mirror"; import { baseOptions, focusAndSetCaret, latex } from "../code-mirror";
import type { CodeMirrorAPI } from "../CodeMirror.svelte"; import type { CodeMirrorAPI } from "../CodeMirror.svelte";
import CodeMirror from "../CodeMirror.svelte"; import CodeMirror from "../CodeMirror.svelte";
@ -33,15 +33,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
mode: latex, mode: latex,
}; };
/* These are not reactive, but only operate on initialization */
export let position: CodeMirrorLib.Position | undefined = undefined;
export let selectAll: boolean; export let selectAll: boolean;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let codeMirror = {} as CodeMirrorAPI; let codeMirror = {} as CodeMirrorAPI;
onMount(() => onMount(async () => {
codeMirror.editor.then((editor) => { const editor = await codeMirror.editor;
focusAndCaretAfter(editor);
focusAndSetCaret(editor, position);
if (selectAll) { if (selectAll) {
editor.execCommand("selectAll"); editor.execCommand("selectAll");
@ -83,8 +86,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
direction = undefined; direction = undefined;
}, },
); );
}), });
);
/** /**
* Escape characters which are technically legal in Mathjax, but confuse HTML. * Escape characters which are technically legal in Mathjax, but confuse HTML.

View file

@ -3,6 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import type CodeMirrorLib from "codemirror";
import { onDestroy, onMount, tick } from "svelte"; import { onDestroy, onMount, tick } from "svelte";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
@ -26,9 +27,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let allow = noop; let allow = noop;
let unsubscribe = noop; let unsubscribe = noop;
function showHandle(image: HTMLImageElement): void { let selectAll = false;
allow = preventResubscription(); let position: CodeMirrorLib.Position | undefined = undefined;
function showHandle(image: HTMLImageElement, pos?: CodeMirrorLib.Position): void {
allow = preventResubscription();
position = pos;
/* Setting the activeImage and mathjaxElement to a non-nullish value is
* what triggers the Mathjax editor to show */
activeImage = image; activeImage = image;
mathjaxElement = activeImage.closest(Mathjax.tagName)!; mathjaxElement = activeImage.closest(Mathjax.tagName)!;
@ -38,8 +45,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}); });
} }
let selectAll = false;
function placeHandle(after: boolean): void { function placeHandle(after: boolean): void {
editable.focusHandler.flushCaret(); editable.focusHandler.flushCaret();
@ -52,6 +57,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
async function resetHandle(): Promise<void> { async function resetHandle(): Promise<void> {
selectAll = false; selectAll = false;
position = undefined;
if (activeImage && mathjaxElement) { if (activeImage && mathjaxElement) {
unsubscribe(); unsubscribe();
@ -72,9 +78,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
async function showAutofocusHandle({ async function showAutofocusHandle({
detail, detail,
}: CustomEvent<HTMLImageElement>): Promise<void> { }: CustomEvent<{
image: HTMLImageElement;
position?: [number, number];
}>): Promise<void> {
let position: CodeMirrorLib.Position | undefined = undefined;
await resetHandle(); await resetHandle();
showHandle(detail);
if (detail.position) {
const [line, ch] = detail.position;
position = { line, ch };
}
showHandle(detail.image, position);
} }
async function showSelectAll({ async function showSelectAll({
@ -138,6 +155,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
element={mathjaxElement} element={mathjaxElement}
{code} {code}
{selectAll} {selectAll}
{position}
bind:updateSelection bind:updateSelection
on:reset={resetHandle} on:reset={resetHandle}
on:moveoutstart={() => { on:moveoutstart={() => {

View file

@ -3,6 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import type CodeMirrorLib from "codemirror";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
@ -15,7 +16,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let element: Element; export let element: Element;
export let code: Writable<string>; export let code: Writable<string>;
export let selectAll: boolean; export let selectAll: boolean;
export let position: CodeMirrorLib.Position | undefined;
const acceptShortcut = "Enter"; const acceptShortcut = "Enter";
const newlineShortcut = "Shift+Enter"; const newlineShortcut = "Shift+Enter";
@ -40,6 +43,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{newlineShortcut} {newlineShortcut}
{code} {code}
{selectAll} {selectAll}
{position}
on:blur={() => dispatch("reset")} on:blur={() => dispatch("reset")}
on:moveoutstart on:moveoutstart
on:moveoutend on:moveoutend