resources/js/components/markdown-editor.js
import {Component} from './component';
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.divider = this.$refs.divider;
this.displayWrap = this.$refs.displayWrap;
const {settingContainer} = this.$refs;
const settingInputs = settingContainer.querySelectorAll('input[type="checkbox"]');
this.editor = null;
window.importVersioned('markdown').then(markdown => {
return markdown.init({
pageId: this.pageId,
container: this.elem,
displayEl: this.display,
inputEl: this.input,
drawioUrl: this.getDrawioUrl(),
settingInputs: Array.from(settingInputs),
text: {
serverUploadLimit: this.serverUploadLimitText,
imageUploadError: this.imageUploadErrorText,
},
});
}).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,
cmEditorView: this.editor.cm,
});
}
setupListeners() {
// Button actions
this.elem.addEventListener('click', event => {
const button = event.target.closest('button[data-action]');
if (button === null) return;
const action = button.getAttribute('data-action');
if (action === 'insertImage') this.editor.actions.showImageInsert();
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');
});
this.handleDividerDrag();
}
handleDividerDrag() {
this.divider.addEventListener('pointerdown', () => {
const wrapRect = this.elem.getBoundingClientRect();
const moveListener = event => {
const xRel = event.pageX - wrapRect.left;
const xPct = Math.min(Math.max(20, Math.floor((xRel / wrapRect.width) * 100)), 80);
this.displayWrap.style.flexBasis = `${100 - xPct}%`;
this.editor.settings.set('editorWidth', xPct);
};
const upListener = () => {
window.removeEventListener('pointermove', moveListener);
window.removeEventListener('pointerup', upListener);
this.display.style.pointerEvents = null;
document.body.style.userSelect = null;
};
this.display.style.pointerEvents = 'none';
document.body.style.userSelect = 'none';
window.addEventListener('pointermove', moveListener);
window.addEventListener('pointerup', upListener);
});
const widthSetting = this.editor.settings.get('editorWidth');
if (widthSetting) {
this.displayWrap.style.flexBasis = `${100 - widthSetting}%`;
}
}
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') || '';
}
/**
* Get the content of this editor.
* Used by the parent page editor component.
* @return {Promise<{html: String, markdown: String}>}
*/
async getContent() {
return this.editor.actions.getContent();
}
}