mirror of
https://github.com/knadh/listmonk.git
synced 2025-03-01 08:45:28 +08:00
Add Markdown syntax highlighting (#2068)
Closes #2058. Co-authored-by: Kailash Nadh <kailash@nadh.in>
This commit is contained in:
parent
b0f3891629
commit
5074987544
4 changed files with 88 additions and 3 deletions
1
frontend/package.json
vendored
1
frontend/package.json
vendored
|
@ -18,6 +18,7 @@
|
|||
"codeflask": "^1.4.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"indent.js": "^0.3.5",
|
||||
"prismjs":"^1.29.0",
|
||||
"qs": "^6.10.1",
|
||||
"textversionjs": "^1.1.3",
|
||||
"tinymce": "^5.10.9",
|
||||
|
|
|
@ -243,7 +243,7 @@ body.is-noscroll {
|
|||
}
|
||||
|
||||
/* WYSIWYG / HTML code editor */
|
||||
.html-editor {
|
||||
.html-editor, .markdown-editor {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 250px;
|
||||
|
|
|
@ -80,8 +80,11 @@
|
|||
<!-- raw html editor //-->
|
||||
<html-editor v-if="form.format === 'html'" v-model="form.body" />
|
||||
|
||||
<!-- plain text / markdown editor //-->
|
||||
<b-input v-if="form.format === 'plain' || form.format === 'markdown'" v-model="form.body" @input="onEditorChange"
|
||||
<!-- markdown editor //-->
|
||||
<markdown-editor v-if="form.format === 'markdown'" v-model="form.body" />
|
||||
|
||||
<!-- plain text //-->
|
||||
<b-input v-if="form.format === 'plain'" v-model="form.body" @input="onEditorChange"
|
||||
type="textarea" name="content" ref="plainEditor" class="plain-editor" />
|
||||
|
||||
<!-- campaign preview //-->
|
||||
|
@ -136,6 +139,7 @@ import { colors, uris } from '../constants';
|
|||
import Media from '../views/Media.vue';
|
||||
import CampaignPreview from './CampaignPreview.vue';
|
||||
import HTMLEditor from './HTMLEditor.vue';
|
||||
import MarkdownEditor from './MarkdownEditor.vue';
|
||||
|
||||
const turndown = new TurndownService();
|
||||
|
||||
|
@ -158,6 +162,7 @@ export default {
|
|||
Media,
|
||||
CampaignPreview,
|
||||
'html-editor': HTMLEditor,
|
||||
'markdown-editor': MarkdownEditor,
|
||||
TinyMce,
|
||||
},
|
||||
|
||||
|
|
79
frontend/src/components/MarkdownEditor.vue
Normal file
79
frontend/src/components/MarkdownEditor.vue
Normal file
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div ref="markdownEditor" id="markdown-editor" class="markdown-editor" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'prismjs/components/prism-markdown';
|
||||
import CodeFlask from 'codeflask';
|
||||
import { colors } from '../constants';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
language: { type: String, default: 'markdown' },
|
||||
disabled: Boolean,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data: '',
|
||||
flask: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
initMarkdownEditor(body) {
|
||||
// CodeFlask editor is rendered in a shadow DOM tree to keep its styles
|
||||
// sandboxed away from the global styles.
|
||||
const el = document.createElement('code-flask');
|
||||
el.attachShadow({ mode: 'open' });
|
||||
|
||||
el.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
.codeflask .codeflask__flatten { font-size: 15px; }
|
||||
.codeflask .token.tag { font-weight: bold; color: ${colors.primary} !important; }
|
||||
.codeflask .token { color: ${colors.primary} !important; }
|
||||
.codeflask .token.heading { font-weight: bold; }
|
||||
.codeflask .token.important,.token.bold,.token.strong { font-weight: bold; }
|
||||
.codeflask .token.em,.token.italic { font-style: italic; }
|
||||
.codeflask .token.comment { color: slategray; }
|
||||
.codeflask .token.url { color: ${colors.primary}; text-decoration: underline; }
|
||||
</style>
|
||||
<div id="area"></area>
|
||||
`;
|
||||
this.$refs.markdownEditor.appendChild(el);
|
||||
|
||||
this.flask = new CodeFlask(el.shadowRoot.getElementById('area'), {
|
||||
language: this.$props.language || 'markdown',
|
||||
lineNumbers: false,
|
||||
styleParent: el.shadowRoot,
|
||||
readonly: this.disabled,
|
||||
});
|
||||
|
||||
this.flask.onUpdate((v) => {
|
||||
this.data = v;
|
||||
this.$emit('input', v);
|
||||
});
|
||||
|
||||
// Set the initial value.
|
||||
this.flask.updateCode(body);
|
||||
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('code-flask').shadowRoot.querySelector('textarea').focus();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initMarkdownEditor(this.$props.value || '');
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(newVal) {
|
||||
if (newVal !== this.data) {
|
||||
this.flask.updateCode(newVal);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
Loading…
Reference in a new issue