|
| 1 | +<template> |
| 2 | + <div class="markdown-body" ref="markdown-it-vue-container"></div> |
| 3 | +</template> |
| 4 | + |
| 5 | +<script> |
| 6 | +import MarkdownIt from 'markdown-it' |
| 7 | +import MarkdownItEmoji from 'markdown-it-emoji' |
| 8 | +import MarkdownItSubscript from 'markdown-it-sub' |
| 9 | +import MarkdownItSuperscript from 'markdown-it-sup' |
| 10 | +import MarkdownItFootnote from 'markdown-it-footnote' |
| 11 | +import MarkdownItDeflist from 'markdown-it-deflist' |
| 12 | +import MarkdownItAbbreviation from 'markdown-it-abbr' |
| 13 | +import MarkdownItInsert from 'markdown-it-ins' |
| 14 | +import MarkdownItMark from 'markdown-it-mark' |
| 15 | +import MarkdownItKatex from 'markdown-it-katex' |
| 16 | +import MarkdownItTasklists from 'markdown-it-task-lists' |
| 17 | +import MarkdownItLatex from 'markdown-it-latex' |
| 18 | +import MarkdownItContainer from 'markdown-it-container' |
| 19 | +import MarkdownItGithubToc from 'markdown-it-github-toc' |
| 20 | +import MarkdownItSourceMap from 'markdown-it-source-map' |
| 21 | +import MarkdownItLinkAttributes from './markdown-it-link-attributes' |
| 22 | +import MarkdownItFlowchart from './markdown-it-plugin-flowchart' |
| 23 | +import MarkdownItFontAwsome from './markdown-it-font-awsome' |
| 24 | +import 'github-markdown-css' |
| 25 | +import 'markdown-it-latex/dist/index.css' |
| 26 | +
|
| 27 | +import flowchart from 'flowchart.js' |
| 28 | +
|
| 29 | +const DEFAULT_OPTIONS_LINK_ATTRIBUTES = { |
| 30 | + attrs: { |
| 31 | + target: '_blank', |
| 32 | + rel: 'noopener' |
| 33 | + } |
| 34 | +} |
| 35 | +const DEFAULT_OPTIONS_KATEX = { throwOnError: false, errorColor: '#cc0000' } |
| 36 | +const DEFAULT_OPTIONS_TASKLISTS = null |
| 37 | +const DEFAULT_OPTIONS_GITHUBTOC = { |
| 38 | + tocFirstLevel: 2, |
| 39 | + tocLastLevel: 3, |
| 40 | + tocClassName: 'toc', |
| 41 | + anchorLinkSymbol: '', |
| 42 | + anchorLinkSpace: false, |
| 43 | + anchorClassName: 'anchor', |
| 44 | + anchorLinkSymbolClassName: 'octicon octicon-link' |
| 45 | +} |
| 46 | +
|
| 47 | +export default { |
| 48 | + name: 'markdown-it-vue-light', |
| 49 | + props: { |
| 50 | + content: { |
| 51 | + type: String |
| 52 | + }, |
| 53 | + options: { |
| 54 | + type: Object, |
| 55 | + default() { |
| 56 | + return { |
| 57 | + markdownIt: { |
| 58 | + linkify: true |
| 59 | + }, |
| 60 | + linkAttributes: DEFAULT_OPTIONS_LINK_ATTRIBUTES, |
| 61 | + katex: DEFAULT_OPTIONS_KATEX, |
| 62 | + tasklists: DEFAULT_OPTIONS_TASKLISTS, |
| 63 | + githubToc: DEFAULT_OPTIONS_GITHUBTOC |
| 64 | + } |
| 65 | + } |
| 66 | + } |
| 67 | + }, |
| 68 | + watch: { |
| 69 | + content: { |
| 70 | + immediate: true, |
| 71 | + handler(val) { |
| 72 | + this.$nextTick(() => { |
| 73 | + this.$refs['markdown-it-vue-container'].innerHTML = this.md.render( |
| 74 | + val |
| 75 | + ) |
| 76 | + // render flowchart |
| 77 | + document.querySelectorAll('.md-flowchart').forEach(element => { |
| 78 | + try { |
| 79 | + let code = element.textContent |
| 80 | + let chart = flowchart.parse(code) |
| 81 | + element.textContent = '' |
| 82 | + chart.drawSVG(element) |
| 83 | + } catch (e) { |
| 84 | + element.outerHTML = `<pre>flowchart complains: ${e}</pre>` |
| 85 | + } |
| 86 | + }) |
| 87 | +
|
| 88 | + // emit event |
| 89 | + this.$emit('render-complete') |
| 90 | + }) |
| 91 | + } |
| 92 | + } |
| 93 | + }, |
| 94 | + data() { |
| 95 | + const optMarkdownIt = this.options.markdownIt |
| 96 | + const linkAttributes = this.options.linkAttributes || DEFAULT_OPTIONS_LINK_ATTRIBUTES |
| 97 | + const optKatex = this.options.katex || DEFAULT_OPTIONS_KATEX |
| 98 | + const optTasklists = this.options.tasklists || DEFAULT_OPTIONS_TASKLISTS |
| 99 | + const optGithubToc = this.options.githubToc || DEFAULT_OPTIONS_GITHUBTOC |
| 100 | +
|
| 101 | + let md = new MarkdownIt(optMarkdownIt) |
| 102 | + .use(MarkdownItEmoji) |
| 103 | + .use(MarkdownItSubscript) |
| 104 | + .use(MarkdownItSuperscript) |
| 105 | + .use(MarkdownItFootnote) |
| 106 | + .use(MarkdownItDeflist) |
| 107 | + .use(MarkdownItAbbreviation) |
| 108 | + .use(MarkdownItInsert) |
| 109 | + .use(MarkdownItMark) |
| 110 | + .use(MarkdownItLatex) |
| 111 | + .use(MarkdownItFlowchart) |
| 112 | + .use(MarkdownItSourceMap) |
| 113 | + .use(MarkdownItLinkAttributes, linkAttributes) |
| 114 | + .use(MarkdownItKatex, optKatex) |
| 115 | + .use(MarkdownItTasklists, optTasklists) |
| 116 | + .use(MarkdownItFontAwsome) |
| 117 | + .use(MarkdownItGithubToc, optGithubToc) |
| 118 | + .use(MarkdownItContainer, 'warning', { |
| 119 | + validate: function(params) { |
| 120 | + return params.trim() === 'warning' |
| 121 | + }, |
| 122 | + render: (tokens, idx) => { |
| 123 | + if (tokens[idx].nesting === 1) { |
| 124 | + const icon = `<i class="markdown-it-vue-alert-icon markdown-it-vue-alert-icon-warning"><svg viewBox="64 64 896 896" data-icon="exclamation-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path></svg></i>` |
| 125 | + return `<div class="markdown-it-vue-alter markdown-it-vue-alter-warning">${icon}` |
| 126 | + } else { |
| 127 | + return '</div>' |
| 128 | + } |
| 129 | + } |
| 130 | + }) |
| 131 | + .use(MarkdownItContainer, 'info', { |
| 132 | + validate: function(params) { |
| 133 | + return params.trim() === 'info' |
| 134 | + }, |
| 135 | + render: (tokens, idx) => { |
| 136 | + if (tokens[idx].nesting === 1) { |
| 137 | + const icon = `<i class="markdown-it-vue-alert-icon markdown-it-vue-alert-icon-info"><svg viewBox="64 64 896 896" data-icon="info-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path></svg></i>` |
| 138 | + return `<div class="markdown-it-vue-alter markdown-it-vue-alter-info">${icon}` |
| 139 | + } else { |
| 140 | + return '</div>' |
| 141 | + } |
| 142 | + } |
| 143 | + }) |
| 144 | + .use(MarkdownItContainer, 'success', { |
| 145 | + validate: function(params) { |
| 146 | + return params.trim() === 'success' |
| 147 | + }, |
| 148 | + render: (tokens, idx) => { |
| 149 | + if (tokens[idx].nesting === 1) { |
| 150 | + const icon = `<i class="markdown-it-vue-alert-icon markdown-it-vue-alert-icon-success"><svg viewBox="64 64 896 896" data-icon="check-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path></svg></i>` |
| 151 | + return `<div class="markdown-it-vue-alter markdown-it-vue-alter-success">${icon}` |
| 152 | + } else { |
| 153 | + return '</div>' |
| 154 | + } |
| 155 | + } |
| 156 | + }) |
| 157 | + .use(MarkdownItContainer, 'error', { |
| 158 | + validate: function(params) { |
| 159 | + return params.trim() === 'error' |
| 160 | + }, |
| 161 | + render: (tokens, idx) => { |
| 162 | + if (tokens[idx].nesting === 1) { |
| 163 | + const icon = `<i class="markdown-it-vue-alert-icon markdown-it-vue-alert-icon-error"><svg viewBox="64 64 896 896" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></i>` |
| 164 | + return `<div class="markdown-it-vue-alter markdown-it-vue-alter-error">${icon}` |
| 165 | + } else { |
| 166 | + return '</div>' |
| 167 | + } |
| 168 | + } |
| 169 | + }) |
| 170 | + return { |
| 171 | + md: md |
| 172 | + } |
| 173 | + }, |
| 174 | + methods: { |
| 175 | + use(plugin, options) { |
| 176 | + this.md.use(plugin, options) |
| 177 | + }, |
| 178 | + get() { |
| 179 | + return this.md |
| 180 | + } |
| 181 | + } |
| 182 | +} |
| 183 | +</script> |
| 184 | + |
| 185 | +<style lange="scss"> |
| 186 | +.markdown-it-vue-alter-info { |
| 187 | + border: 1px solid #91d5ff; |
| 188 | + background-color: #e6f7ff; |
| 189 | +} |
| 190 | +.markdown-it-vue-alert-icon-info { |
| 191 | + color: #1890ff; |
| 192 | +} |
| 193 | +.markdown-it-vue-alter-success { |
| 194 | + border: 1px solid #b7eb8f; |
| 195 | + background-color: #f6ffed; |
| 196 | +} |
| 197 | +.markdown-it-vue-alert-icon-success { |
| 198 | + color: #52c41a; |
| 199 | +} |
| 200 | +.markdown-it-vue-alter-error { |
| 201 | + border: 1px solid #f5222d; |
| 202 | + background-color: #fff1f0; |
| 203 | +} |
| 204 | +.markdown-it-vue-alert-icon-error { |
| 205 | + color: #f5222d; |
| 206 | +} |
| 207 | +.markdown-it-vue-alter-warning { |
| 208 | + border: 1px solid #ffe58f; |
| 209 | + background-color: #fffbe6; |
| 210 | +} |
| 211 | +.markdown-it-vue-alert-icon-warning { |
| 212 | + color: #faad14; |
| 213 | +} |
| 214 | +.markdown-it-vue-alter { |
| 215 | + border-radius: 0; |
| 216 | + border: 0; |
| 217 | + margin-bottom: 0; |
| 218 | + display: inline-flex; |
| 219 | + font-family: 'Chinese Quote', -apple-system, BlinkMacSystemFont, 'Segoe UI', |
| 220 | + 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', |
| 221 | + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', |
| 222 | + 'Segoe UI Symbol'; |
| 223 | + font-size: 14px; |
| 224 | + font-variant: tabular-nums; |
| 225 | + line-height: 1.5; |
| 226 | + color: rgba(0, 0, 0, 0.65); |
| 227 | + box-sizing: border-box; |
| 228 | + padding: 0; |
| 229 | + list-style: none; |
| 230 | + position: relative; |
| 231 | + padding: 8px 15px 8px 37px; |
| 232 | + border-radius: 4px; |
| 233 | + width: 100%; |
| 234 | + margin-bottom: 16px; |
| 235 | +} |
| 236 | +.markdown-it-vue-alter p { |
| 237 | + margin-bottom: 2px; |
| 238 | +} |
| 239 | +
|
| 240 | +.markdown-it-vue-alert-icon { |
| 241 | + top: 11.5px; |
| 242 | + left: 16px; |
| 243 | + position: absolute; |
| 244 | +} |
| 245 | +</style> |
0 commit comments