anki/ts/editable/ContentEditable.svelte

102 lines
2.6 KiB
Svelte

<!--
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 { Writable } from "svelte/store";
import { updateAllState } from "../components/WithState.svelte";
import { saveSelection, restoreSelection } from "../domlib/location";
import { on, preventDefault } from "../lib/events";
import { caretToEnd } from "../lib/dom";
import { registerShortcut } from "../lib/shortcuts";
export let nodes: Writable<DocumentFragment>;
export let resolve: (editable: HTMLElement) => void;
export let mirror: (
editable: HTMLElement,
params: { store: Writable<DocumentFragment> },
) => void;
export let inputManager: (editable: HTMLElement) => void;
let removeOnFocus: () => void;
let removeOnPointerdown: () => void;
function onBlur(): void {
const location = saveSelection(editable);
removeOnFocus = on(
editable,
"focus",
() => {
if (location) {
try {
restoreSelection(editable, location);
} catch {
caretToEnd(editable);
}
}
},
{ once: true },
);
removeOnPointerdown = on(editable, "pointerdown", () => removeOnFocus?.(), {
once: true,
});
}
/* must execute before DOMMirror */
function saveLocation(editable: HTMLElement) {
const removeOnBlur = on(editable, "blur", onBlur);
return {
destroy() {
removeOnBlur();
removeOnFocus?.();
removeOnPointerdown?.();
},
};
}
let editable: HTMLElement;
$: if (editable) {
for (const keyCombination of [
"Control+B",
"Control+U",
"Control+I",
"Control+R",
]) {
registerShortcut(preventDefault, keyCombination, editable);
}
}
</script>
<anki-editable
contenteditable="true"
bind:this={editable}
use:resolve
use:saveLocation
use:mirror={{ store: nodes }}
use:inputManager
on:focus
on:blur
on:click={updateAllState}
on:keyup={updateAllState}
/>
<style lang="scss">
anki-editable {
display: block;
overflow-wrap: break-word;
overflow: auto;
padding: 6px;
&:focus {
outline: none;
}
}
/* editable-base.scss contains styling targeting user HTML */
</style>