Skip to content

Commit d55db06

Browse files
committed
MD Editor: Added plaintext/cm switching
Also aligned the construction of the inputs where possible.
1 parent 6b4b500 commit d55db06

File tree

6 files changed

+83
-44
lines changed

6 files changed

+83
-44
lines changed

resources/js/markdown/codemirror.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,48 @@
1-
import {provideKeyBindings} from './shortcuts';
2-
import {EditorView, ViewUpdate} from "@codemirror/view";
3-
import {MarkdownEditor} from "./index.mjs";
1+
import {EditorView, KeyBinding, ViewUpdate} from "@codemirror/view";
42
import {CodeModule} from "../global";
53
import {MarkdownEditorEventMap} from "./dom-handlers";
4+
import {MarkdownEditorShortcutMap} from "./shortcuts";
5+
6+
/**
7+
* Convert editor shortcuts to CodeMirror keybinding format.
8+
*/
9+
export function shortcutsToKeyBindings(shortcuts: MarkdownEditorShortcutMap): KeyBinding[] {
10+
const keyBindings = [];
11+
12+
const wrapAction = (action: () => void) => () => {
13+
action();
14+
return true;
15+
};
16+
17+
for (const [shortcut, action] of Object.entries(shortcuts)) {
18+
keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
19+
}
20+
21+
return keyBindings;
22+
}
623

724
/**
825
* Initiate the codemirror instance for the Markdown editor.
926
*/
10-
export function init(editor: MarkdownEditor, Code: CodeModule, domEventHandlers: MarkdownEditorEventMap): EditorView {
27+
export async function init(
28+
input: HTMLTextAreaElement,
29+
shortcuts: MarkdownEditorShortcutMap,
30+
domEventHandlers: MarkdownEditorEventMap,
31+
onChange: () => void
32+
): Promise<EditorView> {
33+
const Code = await window.importVersioned('code') as CodeModule;
34+
1135
function onViewUpdate(v: ViewUpdate) {
1236
if (v.docChanged) {
13-
editor.actions.updateAndRender();
37+
onChange();
1438
}
1539
}
1640

17-
1841
const cm = Code.markdownEditor(
19-
editor.config.inputEl,
42+
input,
2043
onViewUpdate,
2144
domEventHandlers,
22-
provideKeyBindings(editor),
45+
shortcutsToKeyBindings(shortcuts),
2346
);
2447

2548
// Add editor view to the window for easy access/debugging.

resources/js/markdown/index.mts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {Actions} from './actions';
44
import {Settings} from './settings';
55
import {listenToCommonEvents} from './common-events';
66
import {init as initCodemirror} from './codemirror';
7-
import {CodeModule} from "../global";
87
import {MarkdownEditorInput} from "./inputs/interface";
98
import {CodemirrorInput} from "./inputs/codemirror";
109
import {TextareaInput} from "./inputs/textarea";
@@ -34,8 +33,6 @@ export interface MarkdownEditor {
3433
* Initiate a new Markdown editor instance.
3534
*/
3635
export async function init(config: MarkdownEditorConfig): Promise<MarkdownEditor> {
37-
// const Code = await window.importVersioned('code') as CodeModule;
38-
3936
const editor: MarkdownEditor = {
4037
config,
4138
markdown: new Markdown(),
@@ -46,15 +43,25 @@ export async function init(config: MarkdownEditorConfig): Promise<MarkdownEditor
4643
editor.display = new Display(editor);
4744

4845
const eventHandlers = getMarkdownDomEventHandlers(editor);
49-
// TODO - Switching
50-
// const codeMirror = initCodemirror(editor, Code);
51-
// editor.input = new CodemirrorInput(codeMirror);
52-
editor.input = new TextareaInput(
53-
config.inputEl,
54-
provideShortcutMap(editor),
55-
eventHandlers
56-
);
46+
const shortcuts = provideShortcutMap(editor);
47+
const onInputChange = () => editor.actions.updateAndRender();
48+
49+
const initCodemirrorInput: () => Promise<MarkdownEditorInput> = async () => {
50+
const codeMirror = await initCodemirror(config.inputEl, shortcuts, eventHandlers, onInputChange);
51+
return new CodemirrorInput(codeMirror);
52+
};
53+
const initTextAreaInput: () => Promise<MarkdownEditorInput> = async () => {
54+
return new TextareaInput(config.inputEl, shortcuts, eventHandlers, onInputChange);
55+
};
5756

57+
const isPlainEditor = Boolean(editor.settings.get('plainEditor'));
58+
editor.input = await (isPlainEditor ? initTextAreaInput() : initCodemirrorInput());
59+
editor.settings.onChange('plainEditor', async (value) => {
60+
const isPlain = Boolean(value);
61+
const newInput = await (isPlain ? initTextAreaInput() : initCodemirrorInput());
62+
editor.input.teardown();
63+
editor.input = newInput;
64+
});
5865
// window.devinput = editor.input;
5966

6067
listenToCommonEvents(editor);

resources/js/markdown/inputs/codemirror.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export class CodemirrorInput implements MarkdownEditorInput {
1010
this.cm = cm;
1111
}
1212

13+
teardown(): void {
14+
this.cm.destroy();
15+
}
16+
1317
focus(): void {
1418
if (!this.cm.hasFocus) {
1519
this.cm.focus();

resources/js/markdown/inputs/interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,9 @@ export interface MarkdownEditorInput {
7373
* Search and return a line range which includes the provided text.
7474
*/
7575
searchForLineContaining(text: string): MarkdownEditorInputSelection|null;
76+
77+
/**
78+
* Tear down the input.
79+
*/
80+
teardown(): void;
7681
}

resources/js/markdown/inputs/textarea.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,43 @@ export class TextareaInput implements MarkdownEditorInput {
88
protected input: HTMLTextAreaElement;
99
protected shortcuts: MarkdownEditorShortcutMap;
1010
protected events: MarkdownEditorEventMap;
11-
12-
constructor(input: HTMLTextAreaElement, shortcuts: MarkdownEditorShortcutMap, events: MarkdownEditorEventMap) {
11+
protected onChange: () => void;
12+
protected eventController = new AbortController();
13+
14+
constructor(
15+
input: HTMLTextAreaElement,
16+
shortcuts: MarkdownEditorShortcutMap,
17+
events: MarkdownEditorEventMap,
18+
onChange: () => void
19+
) {
1320
this.input = input;
1421
this.shortcuts = shortcuts;
1522
this.events = events;
23+
this.onChange = onChange;
1624

1725
this.onKeyDown = this.onKeyDown.bind(this);
1826
this.configureListeners();
27+
28+
this.input.style.removeProperty("display");
29+
}
30+
31+
teardown() {
32+
this.eventController.abort('teardown');
1933
}
2034

2135
configureListeners(): void {
22-
// TODO - Teardown handling
23-
this.input.addEventListener('keydown', this.onKeyDown);
36+
// Keyboard shortcuts
37+
this.input.addEventListener('keydown', this.onKeyDown, {signal: this.eventController.signal});
2438

39+
// Shared event listeners
2540
for (const [name, listener] of Object.entries(this.events)) {
26-
this.input.addEventListener(name, listener);
41+
this.input.addEventListener(name, listener, {signal: this.eventController.signal});
2742
}
43+
44+
// Input change handling
45+
this.input.addEventListener('input', () => {
46+
this.onChange();
47+
}, {signal: this.eventController.signal});
2848
}
2949

3050
onKeyDown(e: KeyboardEvent) {

resources/js/markdown/shortcuts.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {MarkdownEditor} from "./index.mjs";
2-
import {KeyBinding} from "@codemirror/view";
32

43
export type MarkdownEditorShortcutMap = Record<string, () => void>;
54

@@ -42,22 +41,3 @@ export function provideShortcutMap(editor: MarkdownEditor): MarkdownEditorShortc
4241

4342
return shortcuts;
4443
}
45-
46-
/**
47-
* Get the editor shortcuts in CodeMirror keybinding format.
48-
*/
49-
export function provideKeyBindings(editor: MarkdownEditor): KeyBinding[] {
50-
const shortcuts = provideShortcutMap(editor);
51-
const keyBindings = [];
52-
53-
const wrapAction = (action: ()=>void) => () => {
54-
action();
55-
return true;
56-
};
57-
58-
for (const [shortcut, action] of Object.entries(shortcuts)) {
59-
keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
60-
}
61-
62-
return keyBindings;
63-
}

0 commit comments

Comments
 (0)