BookStack/resources/js/markdown/display.js
2024-10-04 14:36:20 +01:00

110 lines
3.2 KiB
JavaScript

import {patchDomFromHtmlString} from '../services/vdom.ts';
export class Display {
/**
* @param {MarkdownEditor} editor
*/
constructor(editor) {
this.editor = editor;
this.container = editor.config.displayEl;
this.doc = null;
this.lastDisplayClick = 0;
if (this.container.contentDocument.readyState === 'complete') {
this.onLoad();
} else {
this.container.addEventListener('load', this.onLoad.bind(this));
}
this.updateVisibility(editor.settings.get('showPreview'));
editor.settings.onChange('showPreview', show => this.updateVisibility(show));
}
updateVisibility(show) {
const wrap = this.container.closest('.markdown-editor-wrap');
wrap.style.display = show ? null : 'none';
}
onLoad() {
this.doc = this.container.contentDocument;
this.loadStylesIntoDisplay();
this.doc.body.className = 'page-content';
// Prevent markdown display link click redirect
this.doc.addEventListener('click', this.onDisplayClick.bind(this));
}
/**
* @param {MouseEvent} event
*/
onDisplayClick(event) {
const isDblClick = Date.now() - this.lastDisplayClick < 300;
const link = event.target.closest('a');
if (link !== null) {
event.preventDefault();
window.open(link.getAttribute('href'));
return;
}
const drawing = event.target.closest('[drawio-diagram]');
if (drawing !== null && isDblClick) {
this.editor.actions.editDrawing(drawing);
return;
}
this.lastDisplayClick = Date.now();
}
loadStylesIntoDisplay() {
this.doc.documentElement.classList.add('markdown-editor-display');
// Set display to be dark mode if parent is
if (document.documentElement.classList.contains('dark-mode')) {
this.doc.documentElement.style.backgroundColor = '#222';
this.doc.documentElement.classList.add('dark-mode');
}
this.doc.head.innerHTML = '';
const styles = document.head.querySelectorAll('style,link[rel=stylesheet]');
for (const style of styles) {
const copy = style.cloneNode(true);
this.doc.head.appendChild(copy);
}
}
/**
* Patch the display DOM with the given HTML content.
* @param {String} html
*/
patchWithHtml(html) {
const {body} = this.doc;
if (body.children.length === 0) {
const wrap = document.createElement('div');
this.doc.body.append(wrap);
}
const target = body.children[0];
patchDomFromHtmlString(target, html);
}
/**
* Scroll to the given block index within the display content.
* Will scroll to the end if the index is -1.
* @param {Number} index
*/
scrollToIndex(index) {
const elems = this.doc.body?.children[0]?.children;
if (elems && elems.length <= index) return;
const topElem = (index === -1) ? elems[elems.length - 1] : elems[index];
topElem.scrollIntoView({block: 'start', inline: 'nearest', behavior: 'smooth'});
}
}