mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Set caret inside chemical expression in Mathjax (#1763)
This commit is contained in:
parent
6718e67883
commit
67c1f7368e
7 changed files with 86 additions and 51 deletions
|
@ -41,11 +41,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
let image: HTMLImageElement;
|
||||
|
||||
export function moveCaretAfter(): void {
|
||||
export function moveCaretAfter(position?: [number, number]): void {
|
||||
// This should trigger a focusing of the Mathjax Handle
|
||||
image.dispatchEvent(
|
||||
new CustomEvent("movecaretafter", {
|
||||
detail: image,
|
||||
detail: { image, position },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
|
|
|
@ -121,7 +121,15 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
|
|||
});
|
||||
|
||||
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");
|
||||
|
|
|
@ -52,9 +52,12 @@ export const gutterOptions: CodeMirror.EditorConfiguration = {
|
|||
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.setCursor(editor.lineCount(), 0);
|
||||
editor.setCursor(position);
|
||||
}
|
||||
|
||||
interface OpenCodeMirrorOptions {
|
||||
|
|
|
@ -35,7 +35,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
}
|
||||
|
||||
function onMathjaxChemistry(): void {
|
||||
surround("<anki-mathjax focusonmount>\\ce{", "}</anki-mathjax>");
|
||||
surround('<anki-mathjax focusonmount="0,4">\\ce{', "}</anki-mathjax>");
|
||||
}
|
||||
|
||||
function onLatex(): void {
|
||||
|
|
|
@ -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 { noop } from "../../lib/functional";
|
||||
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 CodeMirror from "../CodeMirror.svelte";
|
||||
|
||||
|
@ -33,58 +33,60 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
mode: latex,
|
||||
};
|
||||
|
||||
/* These are not reactive, but only operate on initialization */
|
||||
export let position: CodeMirrorLib.Position | undefined = undefined;
|
||||
export let selectAll: boolean;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let codeMirror = {} as CodeMirrorAPI;
|
||||
|
||||
onMount(() =>
|
||||
codeMirror.editor.then((editor) => {
|
||||
focusAndCaretAfter(editor);
|
||||
onMount(async () => {
|
||||
const editor = await codeMirror.editor;
|
||||
|
||||
if (selectAll) {
|
||||
editor.execCommand("selectAll");
|
||||
}
|
||||
focusAndSetCaret(editor, position);
|
||||
|
||||
let direction: "start" | "end" | undefined = undefined;
|
||||
if (selectAll) {
|
||||
editor.execCommand("selectAll");
|
||||
}
|
||||
|
||||
editor.on(
|
||||
"keydown",
|
||||
(_instance: CodeMirrorLib.Editor, event: KeyboardEvent): void => {
|
||||
if (event.key === "ArrowLeft") {
|
||||
direction = "start";
|
||||
} else if (event.key === "ArrowRight") {
|
||||
direction = "end";
|
||||
}
|
||||
},
|
||||
);
|
||||
let direction: "start" | "end" | undefined = undefined;
|
||||
|
||||
editor.on(
|
||||
"beforeSelectionChange",
|
||||
(
|
||||
instance: CodeMirrorLib.Editor,
|
||||
obj: CodeMirrorLib.EditorSelectionChange,
|
||||
): void => {
|
||||
const { anchor } = obj.ranges[0];
|
||||
editor.on(
|
||||
"keydown",
|
||||
(_instance: CodeMirrorLib.Editor, event: KeyboardEvent): void => {
|
||||
if (event.key === "ArrowLeft") {
|
||||
direction = "start";
|
||||
} else if (event.key === "ArrowRight") {
|
||||
direction = "end";
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (anchor["hitSide"]) {
|
||||
if (instance.getValue().length === 0) {
|
||||
if (direction) {
|
||||
dispatch(`moveout${direction}`);
|
||||
}
|
||||
} else if (anchor.line === 0 && anchor.ch === 0) {
|
||||
dispatch("moveoutstart");
|
||||
} else {
|
||||
dispatch("moveoutend");
|
||||
editor.on(
|
||||
"beforeSelectionChange",
|
||||
(
|
||||
instance: CodeMirrorLib.Editor,
|
||||
obj: CodeMirrorLib.EditorSelectionChange,
|
||||
): void => {
|
||||
const { anchor } = obj.ranges[0];
|
||||
|
||||
if (anchor["hitSide"]) {
|
||||
if (instance.getValue().length === 0) {
|
||||
if (direction) {
|
||||
dispatch(`moveout${direction}`);
|
||||
}
|
||||
} else if (anchor.line === 0 && anchor.ch === 0) {
|
||||
dispatch("moveoutstart");
|
||||
} else {
|
||||
dispatch("moveoutend");
|
||||
}
|
||||
}
|
||||
|
||||
direction = undefined;
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
direction = undefined;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Escape characters which are technically legal in Mathjax, but confuse HTML.
|
||||
|
|
|
@ -3,6 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type CodeMirrorLib from "codemirror";
|
||||
import { onDestroy, onMount, tick } from "svelte";
|
||||
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 unsubscribe = noop;
|
||||
|
||||
function showHandle(image: HTMLImageElement): void {
|
||||
allow = preventResubscription();
|
||||
let selectAll = false;
|
||||
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;
|
||||
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 {
|
||||
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> {
|
||||
selectAll = false;
|
||||
position = undefined;
|
||||
|
||||
if (activeImage && mathjaxElement) {
|
||||
unsubscribe();
|
||||
|
@ -72,9 +78,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
async function showAutofocusHandle({
|
||||
detail,
|
||||
}: CustomEvent<HTMLImageElement>): Promise<void> {
|
||||
}: CustomEvent<{
|
||||
image: HTMLImageElement;
|
||||
position?: [number, number];
|
||||
}>): Promise<void> {
|
||||
let position: CodeMirrorLib.Position | undefined = undefined;
|
||||
|
||||
await resetHandle();
|
||||
showHandle(detail);
|
||||
|
||||
if (detail.position) {
|
||||
const [line, ch] = detail.position;
|
||||
position = { line, ch };
|
||||
}
|
||||
|
||||
showHandle(detail.image, position);
|
||||
}
|
||||
|
||||
async function showSelectAll({
|
||||
|
@ -138,6 +155,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
element={mathjaxElement}
|
||||
{code}
|
||||
{selectAll}
|
||||
{position}
|
||||
bind:updateSelection
|
||||
on:reset={resetHandle}
|
||||
on:moveoutstart={() => {
|
||||
|
|
|
@ -3,6 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type CodeMirrorLib from "codemirror";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
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 code: Writable<string>;
|
||||
|
||||
export let selectAll: boolean;
|
||||
export let position: CodeMirrorLib.Position | undefined;
|
||||
|
||||
const acceptShortcut = "Enter";
|
||||
const newlineShortcut = "Shift+Enter";
|
||||
|
@ -40,6 +43,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{newlineShortcut}
|
||||
{code}
|
||||
{selectAll}
|
||||
{position}
|
||||
on:blur={() => dispatch("reset")}
|
||||
on:moveoutstart
|
||||
on:moveoutend
|
||||
|
|
Loading…
Reference in a new issue