-import {provideKeyBindings} from './shortcuts';
-import {debounce} from '../services/util';
-import {Clipboard} from '../services/clipboard';
-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";
/**
- * Initiate the codemirror instance for the markdown editor.
+ * Convert editor shortcuts to CodeMirror keybinding format.
*/
-export async function init(editor: MarkdownEditor): Promise<EditorView> {
- const Code = await window.importVersioned('code') as (typeof import('../code/index.mjs'));
+export function shortcutsToKeyBindings(shortcuts: MarkdownEditorShortcutMap): KeyBinding[] {
+ const keyBindings = [];
- function onViewUpdate(v: ViewUpdate) {
- if (v.docChanged) {
- editor.actions.updateAndRender();
- }
- }
-
- const onScrollDebounced = debounce(editor.actions.syncDisplayPosition.bind(editor.actions), 100, false);
- let syncActive = editor.settings.get('scrollSync');
- editor.settings.onChange('scrollSync', val => {
- syncActive = val;
- });
-
- const domEventHandlers = {
- // Handle scroll to sync display view
- scroll: (event: Event) => syncActive && onScrollDebounced(event),
- // Handle image & content drag n drop
- drop: (event: DragEvent) => {
- if (!event.dataTransfer) {
- return;
- }
-
- const templateId = event.dataTransfer.getData('bookstack/template');
- if (templateId) {
- event.preventDefault();
- editor.actions.insertTemplate(templateId, event.pageX, event.pageY);
- }
+ const wrapAction = (action: () => void) => () => {
+ action();
+ return true;
+ };
- const clipboard = new Clipboard(event.dataTransfer);
- const clipboardImages = clipboard.getImages();
- if (clipboardImages.length > 0) {
- event.stopPropagation();
- event.preventDefault();
- editor.actions.insertClipboardImages(clipboardImages, event.pageX, event.pageY);
- }
- },
- // Handle dragover event to allow as drop-target in chrome
- dragover: (event: DragEvent) => {
- event.preventDefault();
- },
- // Handle image paste
- paste: (event: ClipboardEvent) => {
- if (!event.clipboardData) {
- return;
- }
+ for (const [shortcut, action] of Object.entries(shortcuts)) {
+ keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
+ }
- const clipboard = new Clipboard(event.clipboardData);
+ return keyBindings;
+}
- // Don't handle the event ourselves if no items exist of contains table-looking data
- if (!clipboard.hasItems() || clipboard.containsTabularData()) {
- return;
- }
+/**
+ * Initiate the codemirror instance for the Markdown editor.
+ */
+export async function init(
+ input: HTMLTextAreaElement,
+ shortcuts: MarkdownEditorShortcutMap,
+ domEventHandlers: MarkdownEditorEventMap,
+ onChange: () => void
+): Promise<EditorView> {
+ const Code = await window.importVersioned('code') as CodeModule;
- const images = clipboard.getImages();
- for (const image of images) {
- editor.actions.uploadImage(image);
- }
- },
- };
+ function onViewUpdate(v: ViewUpdate) {
+ if (v.docChanged) {
+ 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.