Also aligned the construction of the inputs where possible.
-import {provideKeyBindings} from './shortcuts';
-import {EditorView, ViewUpdate} from "@codemirror/view";
-import {MarkdownEditor} from "./index.mjs";
+import {EditorView, KeyBinding, ViewUpdate} from "@codemirror/view";
import {CodeModule} from "../global";
import {MarkdownEditorEventMap} from "./dom-handlers";
+import {MarkdownEditorShortcutMap} from "./shortcuts";
+
+/**
+ * Convert editor shortcuts to CodeMirror keybinding format.
+ */
+export function shortcutsToKeyBindings(shortcuts: MarkdownEditorShortcutMap): KeyBinding[] {
+ const keyBindings = [];
+
+ const wrapAction = (action: () => void) => () => {
+ action();
+ return true;
+ };
+
+ for (const [shortcut, action] of Object.entries(shortcuts)) {
+ keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
+ }
+
+ return keyBindings;
+}
/**
* Initiate the codemirror instance for the Markdown editor.
*/
-export function init(editor: MarkdownEditor, Code: CodeModule, domEventHandlers: MarkdownEditorEventMap): EditorView {
+export async function init(
+ input: HTMLTextAreaElement,
+ shortcuts: MarkdownEditorShortcutMap,
+ domEventHandlers: MarkdownEditorEventMap,
+ onChange: () => void
+): Promise<EditorView> {
+ const Code = await window.importVersioned('code') as CodeModule;
+
function onViewUpdate(v: ViewUpdate) {
if (v.docChanged) {
- editor.actions.updateAndRender();
+ onChange();
}
}
-
const cm = Code.markdownEditor(
- editor.config.inputEl,
+ input,
onViewUpdate,
domEventHandlers,
- provideKeyBindings(editor),
+ shortcutsToKeyBindings(shortcuts),
);
// Add editor view to the window for easy access/debugging.
import {Settings} from './settings';
import {listenToCommonEvents} from './common-events';
import {init as initCodemirror} from './codemirror';
-import {CodeModule} from "../global";
import {MarkdownEditorInput} from "./inputs/interface";
import {CodemirrorInput} from "./inputs/codemirror";
import {TextareaInput} from "./inputs/textarea";
* Initiate a new Markdown editor instance.
*/
export async function init(config: MarkdownEditorConfig): Promise<MarkdownEditor> {
- // const Code = await window.importVersioned('code') as CodeModule;
-
const editor: MarkdownEditor = {
config,
markdown: new Markdown(),
editor.display = new Display(editor);
const eventHandlers = getMarkdownDomEventHandlers(editor);
- // TODO - Switching
- // const codeMirror = initCodemirror(editor, Code);
- // editor.input = new CodemirrorInput(codeMirror);
- editor.input = new TextareaInput(
- config.inputEl,
- provideShortcutMap(editor),
- eventHandlers
- );
+ const shortcuts = provideShortcutMap(editor);
+ const onInputChange = () => editor.actions.updateAndRender();
+
+ const initCodemirrorInput: () => Promise<MarkdownEditorInput> = async () => {
+ const codeMirror = await initCodemirror(config.inputEl, shortcuts, eventHandlers, onInputChange);
+ return new CodemirrorInput(codeMirror);
+ };
+ const initTextAreaInput: () => Promise<MarkdownEditorInput> = async () => {
+ return new TextareaInput(config.inputEl, shortcuts, eventHandlers, onInputChange);
+ };
+ const isPlainEditor = Boolean(editor.settings.get('plainEditor'));
+ editor.input = await (isPlainEditor ? initTextAreaInput() : initCodemirrorInput());
+ editor.settings.onChange('plainEditor', async (value) => {
+ const isPlain = Boolean(value);
+ const newInput = await (isPlain ? initTextAreaInput() : initCodemirrorInput());
+ editor.input.teardown();
+ editor.input = newInput;
+ });
// window.devinput = editor.input;
listenToCommonEvents(editor);
this.cm = cm;
}
+ teardown(): void {
+ this.cm.destroy();
+ }
+
focus(): void {
if (!this.cm.hasFocus) {
this.cm.focus();
* Search and return a line range which includes the provided text.
*/
searchForLineContaining(text: string): MarkdownEditorInputSelection|null;
+
+ /**
+ * Tear down the input.
+ */
+ teardown(): void;
}
\ No newline at end of file
protected input: HTMLTextAreaElement;
protected shortcuts: MarkdownEditorShortcutMap;
protected events: MarkdownEditorEventMap;
-
- constructor(input: HTMLTextAreaElement, shortcuts: MarkdownEditorShortcutMap, events: MarkdownEditorEventMap) {
+ protected onChange: () => void;
+ protected eventController = new AbortController();
+
+ constructor(
+ input: HTMLTextAreaElement,
+ shortcuts: MarkdownEditorShortcutMap,
+ events: MarkdownEditorEventMap,
+ onChange: () => void
+ ) {
this.input = input;
this.shortcuts = shortcuts;
this.events = events;
+ this.onChange = onChange;
this.onKeyDown = this.onKeyDown.bind(this);
this.configureListeners();
+
+ this.input.style.removeProperty("display");
+ }
+
+ teardown() {
+ this.eventController.abort('teardown');
}
configureListeners(): void {
- // TODO - Teardown handling
- this.input.addEventListener('keydown', this.onKeyDown);
+ // Keyboard shortcuts
+ this.input.addEventListener('keydown', this.onKeyDown, {signal: this.eventController.signal});
+ // Shared event listeners
for (const [name, listener] of Object.entries(this.events)) {
- this.input.addEventListener(name, listener);
+ this.input.addEventListener(name, listener, {signal: this.eventController.signal});
}
+
+ // Input change handling
+ this.input.addEventListener('input', () => {
+ this.onChange();
+ }, {signal: this.eventController.signal});
}
onKeyDown(e: KeyboardEvent) {
import {MarkdownEditor} from "./index.mjs";
-import {KeyBinding} from "@codemirror/view";
export type MarkdownEditorShortcutMap = Record<string, () => void>;
return shortcuts;
}
-
-/**
- * Get the editor shortcuts in CodeMirror keybinding format.
- */
-export function provideKeyBindings(editor: MarkdownEditor): KeyBinding[] {
- const shortcuts = provideShortcutMap(editor);
- const keyBindings = [];
-
- const wrapAction = (action: ()=>void) => () => {
- action();
- return true;
- };
-
- for (const [shortcut, action] of Object.entries(shortcuts)) {
- keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
- }
-
- return keyBindings;
-}