mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-08-03 13:41:44 +03:00
115 lines
3.6 KiB
TypeScript
115 lines
3.6 KiB
TypeScript
import { patchDomFromHtmlString } from '../services/vdom';
|
|
import {MarkdownEditor} from "./index.mjs";
|
|
|
|
export class Display {
|
|
protected editor: MarkdownEditor;
|
|
protected container: HTMLIFrameElement;
|
|
protected doc: Document | null = null;
|
|
protected lastDisplayClick: number = 0;
|
|
|
|
constructor(editor: MarkdownEditor) {
|
|
this.editor = editor;
|
|
this.container = editor.config.displayEl;
|
|
|
|
if (this.container.contentDocument?.readyState === 'complete') {
|
|
this.onLoad();
|
|
} else {
|
|
this.container.addEventListener('load', this.onLoad.bind(this));
|
|
}
|
|
|
|
this.updateVisibility(Boolean(editor.settings.get('showPreview')));
|
|
editor.settings.onChange('showPreview', (show) => this.updateVisibility(Boolean(show)));
|
|
}
|
|
|
|
protected updateVisibility(show: boolean): void {
|
|
const wrap = this.container.closest('.markdown-editor-wrap') as HTMLElement;
|
|
wrap.style.display = show ? '' : 'none';
|
|
}
|
|
|
|
protected onLoad(): void {
|
|
this.doc = this.container.contentDocument;
|
|
|
|
if (!this.doc) return;
|
|
|
|
this.loadStylesIntoDisplay();
|
|
this.doc.body.className = 'page-content';
|
|
|
|
// Prevent markdown display link click redirect
|
|
this.doc.addEventListener('click', this.onDisplayClick.bind(this));
|
|
}
|
|
|
|
protected onDisplayClick(event: MouseEvent): void {
|
|
const isDblClick = Date.now() - this.lastDisplayClick < 300;
|
|
|
|
const link = (event.target as Element).closest('a');
|
|
if (link !== null) {
|
|
event.preventDefault();
|
|
const href = link.getAttribute('href');
|
|
if (href) {
|
|
window.open(href);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const drawing = (event.target as Element).closest('[drawio-diagram]') as HTMLElement;
|
|
if (drawing !== null && isDblClick) {
|
|
this.editor.actions.editDrawing(drawing);
|
|
return;
|
|
}
|
|
|
|
this.lastDisplayClick = Date.now();
|
|
}
|
|
|
|
protected loadStylesIntoDisplay(): void {
|
|
if (!this.doc) return;
|
|
|
|
this.doc.documentElement.classList.add('markdown-editor-display');
|
|
|
|
// Set display to be dark mode if the 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) as HTMLElement;
|
|
this.doc.head.appendChild(copy);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Patch the display DOM with the given HTML content.
|
|
*/
|
|
public patchWithHtml(html: string): void {
|
|
if (!this.doc) return;
|
|
|
|
const { body } = this.doc;
|
|
|
|
if (body.children.length === 0) {
|
|
const wrap = document.createElement('div');
|
|
this.doc.body.append(wrap);
|
|
}
|
|
|
|
const target = body.children[0] as HTMLElement;
|
|
|
|
patchDomFromHtmlString(target, html);
|
|
}
|
|
|
|
/**
|
|
* Scroll to the given block index within the display content.
|
|
* Will scroll to the end if the index is -1.
|
|
*/
|
|
public scrollToIndex(index: number): void {
|
|
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 as Element).scrollIntoView({
|
|
block: 'start',
|
|
inline: 'nearest',
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
} |