debian-mirror-gitlab/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
2022-01-26 12:08:38 +05:30

168 lines
4.9 KiB
JavaScript

import { debounce } from 'lodash';
import { BLOB_PREVIEW_ERROR } from '~/blob_edit/constants';
import createFlash from '~/flash';
import { sanitize } from '~/lib/dompurify';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import syntaxHighlight from '~/syntax_highlight';
import {
EXTENSION_MARKDOWN_PREVIEW_PANEL_CLASS,
EXTENSION_MARKDOWN_PREVIEW_ACTION_ID,
EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH,
EXTENSION_MARKDOWN_PREVIEW_PANEL_PARENT_CLASS,
EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY,
} from '../constants';
const fetchPreview = (text, previewMarkdownPath) => {
return axios
.post(previewMarkdownPath, {
text,
})
.then(({ data }) => {
return data.body;
});
};
const setupDomElement = ({ injectToEl = null } = {}) => {
const previewEl = document.createElement('div');
previewEl.classList.add(EXTENSION_MARKDOWN_PREVIEW_PANEL_CLASS);
previewEl.style.display = 'none';
if (injectToEl) {
injectToEl.appendChild(previewEl);
}
return previewEl;
};
export class EditorMarkdownPreviewExtension {
static get extensionName() {
return 'EditorMarkdownPreview';
}
onSetup(instance, setupOptions) {
this.preview = {
el: undefined,
action: undefined,
shown: false,
modelChangeListener: undefined,
path: setupOptions.previewMarkdownPath,
};
this.setupPreviewAction(instance);
instance.getModel().onDidChangeLanguage(({ newLanguage, oldLanguage } = {}) => {
if (newLanguage === 'markdown' && oldLanguage !== newLanguage) {
instance.setupPreviewAction();
} else {
instance.cleanup();
}
});
instance.onDidChangeModel(() => {
const model = instance.getModel();
if (model) {
const { language } = model.getLanguageIdentifier();
instance.cleanup();
if (language === 'markdown') {
instance.setupPreviewAction();
}
}
});
}
togglePreviewLayout(instance) {
const { width, height } = instance.getLayoutInfo();
const newWidth = this.preview.shown
? width / EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH
: width * EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH;
instance.layout({ width: newWidth, height });
}
togglePreviewPanel(instance) {
const parentEl = instance.getDomNode().parentElement;
const { el: previewEl } = this.preview;
parentEl.classList.toggle(EXTENSION_MARKDOWN_PREVIEW_PANEL_PARENT_CLASS);
if (previewEl.style.display === 'none') {
// Show the preview panel
this.fetchPreview(instance);
} else {
// Hide the preview panel
previewEl.style.display = 'none';
}
}
fetchPreview(instance) {
const { el: previewEl } = this.preview;
fetchPreview(instance.getValue(), this.preview.path)
.then((data) => {
previewEl.innerHTML = sanitize(data);
syntaxHighlight(previewEl.querySelectorAll('.js-syntax-highlight'));
previewEl.style.display = 'block';
})
.catch(() => createFlash(BLOB_PREVIEW_ERROR));
}
setupPreviewAction(instance) {
if (instance.getAction(EXTENSION_MARKDOWN_PREVIEW_ACTION_ID)) return;
this.preview.action = instance.addAction({
id: EXTENSION_MARKDOWN_PREVIEW_ACTION_ID,
label: __('Preview Markdown'),
keybindings: [
// eslint-disable-next-line no-bitwise,no-undef
monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_P),
],
contextMenuGroupId: 'navigation',
contextMenuOrder: 1.5,
// Method that will be executed when the action is triggered.
// @param ed The editor instance is passed in as a convenience
run(inst) {
inst.togglePreview();
},
});
}
provides() {
return {
markdownPreview: this.preview,
cleanup: (instance) => {
if (this.preview.modelChangeListener) {
this.preview.modelChangeListener.dispose();
}
this.preview.action.dispose();
if (this.preview.shown) {
this.togglePreviewPanel(instance);
this.togglePreviewLayout(instance);
}
this.preview.shown = false;
},
fetchPreview: (instance) => this.fetchPreview(instance),
setupPreviewAction: (instance) => this.setupPreviewAction(instance),
togglePreview: (instance) => {
if (!this.preview?.el) {
this.preview.el = setupDomElement({ injectToEl: instance.getDomNode().parentElement });
}
this.togglePreviewLayout(instance);
this.togglePreviewPanel(instance);
if (!this.preview?.shown) {
this.preview.modelChangeListener = instance.onDidChangeModelContent(
debounce(
this.fetchPreview.bind(this, instance),
EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY,
),
);
} else {
this.preview.modelChangeListener.dispose();
}
this.preview.shown = !this.preview?.shown;
},
};
}
}