BookStack/resources/js/components/markdown-editor.js

127 lines
4.3 KiB
JavaScript

import {debounce} from "../services/util";
import {Component} from "./component";
import {init as initEditor} from "../markdown/editor";
export class MarkdownEditor extends Component {
setup() {
this.elem = this.$el;
this.pageId = this.$opts.pageId;
this.textDirection = this.$opts.textDirection;
this.imageUploadErrorText = this.$opts.imageUploadErrorText;
this.serverUploadLimitText = this.$opts.serverUploadLimitText;
this.display = this.$refs.display;
this.input = this.$refs.input;
this.settingContainer = this.$refs.settingContainer;
this.editor = null;
initEditor({
pageId: this.pageId,
container: this.elem,
displayEl: this.display,
inputEl: this.input,
drawioUrl: this.getDrawioUrl(),
text: {
serverUploadLimit: this.serverUploadLimitText,
imageUploadError: this.imageUploadErrorText,
},
settings: this.loadSettings(),
}).then(editor => {
this.editor = editor;
this.setupListeners();
this.emitEditorEvents();
this.scrollToTextIfNeeded();
this.editor.actions.updateAndRender();
});
}
emitEditorEvents() {
window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
markdownIt: this.editor.markdown.getRenderer(),
displayEl: this.display,
codeMirrorInstance: this.editor.cm,
});
}
setupListeners() {
// Button actions
this.elem.addEventListener('click', event => {
let button = event.target.closest('button[data-action]');
if (button === null) return;
const action = button.getAttribute('data-action');
if (action === 'insertImage') this.editor.actions.insertImage();
if (action === 'insertLink') this.editor.actions.showLinkSelector();
if (action === 'insertDrawing' && (event.ctrlKey || event.metaKey)) {
this.editor.actions.showImageManager();
return;
}
if (action === 'insertDrawing') this.editor.actions.startDrawing();
if (action === 'fullscreen') this.editor.actions.fullScreen();
});
// Mobile section toggling
this.elem.addEventListener('click', event => {
const toolbarLabel = event.target.closest('.editor-toolbar-label');
if (!toolbarLabel) return;
const currentActiveSections = this.elem.querySelectorAll('.markdown-editor-wrap');
for (const activeElem of currentActiveSections) {
activeElem.classList.remove('active');
}
toolbarLabel.closest('.markdown-editor-wrap').classList.add('active');
});
// Setting changes
this.settingContainer.addEventListener('change', e => {
const actualInput = e.target.parentNode.querySelector('input[type="hidden"]');
const name = actualInput.getAttribute('name');
const value = actualInput.getAttribute('value');
window.$http.patch('/preferences/update-boolean', {name, value});
this.editor.settings.set(name, value === 'true');
});
// Refresh CodeMirror on container resize
const resizeDebounced = debounce(() => this.editor.cm.refresh(), 100, false);
const observer = new ResizeObserver(resizeDebounced);
observer.observe(this.elem);
}
loadSettings() {
const settings = {};
const inputs = this.settingContainer.querySelectorAll('input[type="hidden"]');
for (const input of inputs) {
settings[input.getAttribute('name')] = input.value === 'true';
}
return settings;
}
scrollToTextIfNeeded() {
const queryParams = (new URL(window.location)).searchParams;
const scrollText = queryParams.get('content-text');
if (scrollText) {
this.editor.actions.scrollToText(scrollText);
}
}
/**
* Get the URL for the configured drawio instance.
* @returns {String}
*/
getDrawioUrl() {
const drawioAttrEl = document.querySelector('[drawio-url]');
if (!drawioAttrEl) {
return '';
}
return drawioAttrEl.getAttribute('drawio-url') || '';
}
}