diff options
author | silverwind <me@silverwind.io> | 2020-08-04 21:56:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-04 20:56:37 +0100 |
commit | e61c09ed7331e6ea72e4d9f08c0490713598942d (patch) | |
tree | febdc85a173312afbf293304a3c46dfe498c7725 /web_src | |
parent | 5e5c893555411bcbeedc4a3f0d853731b63f05c4 (diff) | |
download | gitea-e61c09ed7331e6ea72e4d9f08c0490713598942d.tar.gz gitea-e61c09ed7331e6ea72e4d9f08c0490713598942d.zip |
Add loading spinners and mermaid error handling (#12358)
- Add loading spinners on editor and mermaid renderers
- Add error handling and inline error box for mermaid
- Fix Mermaid rendering by using the .init api
Diffstat (limited to 'web_src')
-rw-r--r-- | web_src/js/markdown/content.js | 2 | ||||
-rw-r--r-- | web_src/js/markdown/mermaid.js | 55 | ||||
-rw-r--r-- | web_src/less/_markdown.less | 22 | ||||
-rw-r--r-- | web_src/less/features/animations.less | 34 | ||||
-rw-r--r-- | web_src/less/index.less | 2 | ||||
-rw-r--r-- | web_src/less/markdown/mermaid.less | 12 | ||||
-rw-r--r-- | web_src/less/themes/theme-arc-green.less | 12 |
7 files changed, 120 insertions, 19 deletions
diff --git a/web_src/js/markdown/content.js b/web_src/js/markdown/content.js index f41800ee30..918cd6fe81 100644 --- a/web_src/js/markdown/content.js +++ b/web_src/js/markdown/content.js @@ -1,5 +1,5 @@ import {renderMermaid} from './mermaid.js'; export default async function renderMarkdownContent() { - await renderMermaid(document.querySelectorAll('.language-mermaid')); + await renderMermaid(document.querySelectorAll('code.language-mermaid')); } diff --git a/web_src/js/markdown/mermaid.js b/web_src/js/markdown/mermaid.js index 1fda101dc0..a518bc7345 100644 --- a/web_src/js/markdown/mermaid.js +++ b/web_src/js/markdown/mermaid.js @@ -1,23 +1,56 @@ -import {random} from '../utils.js'; +const MAX_SOURCE_CHARACTERS = 5000; + +function displayError(el, err) { + el.closest('pre').classList.remove('is-loading'); + const errorNode = document.createElement('div'); + errorNode.setAttribute('class', 'ui message error markdown-block-error mono'); + errorNode.textContent = err.str || err.message || String(err); + el.closest('pre').before(errorNode); +} export async function renderMermaid(els) { if (!els || !els.length) return; - const {mermaidAPI} = await import(/* webpackChunkName: "mermaid" */'mermaid'); + const mermaid = await import(/* webpackChunkName: "mermaid" */'mermaid'); - mermaidAPI.initialize({ - startOnLoad: false, + mermaid.initialize({ + mermaid: { + startOnLoad: false, + }, + flowchart: { + useMaxWidth: true, + htmlLabels: false, + }, theme: 'neutral', securityLevel: 'strict', }); for (const el of els) { - mermaidAPI.render(`mermaid-${random(12)}`, el.textContent, (svg, bindFunctions) => { - const div = document.createElement('div'); - div.classList.add('mermaid-chart'); - div.innerHTML = svg; - if (typeof bindFunctions === 'function') bindFunctions(div); - el.closest('pre').replaceWith(div); - }); + if (el.textContent.length > MAX_SOURCE_CHARACTERS) { + displayError(el, new Error(`Mermaid source of ${el.textContent.length} characters exceeds the maximum allowed length of ${MAX_SOURCE_CHARACTERS}.`)); + continue; + } + + let valid; + try { + valid = mermaid.parse(el.textContent); + } catch (err) { + displayError(el, err); + } + + if (!valid) { + el.closest('pre').classList.remove('is-loading'); + continue; + } + + try { + mermaid.init(undefined, el, (id) => { + const svg = document.getElementById(id); + svg.classList.add('mermaid-chart'); + svg.closest('pre').replaceWith(svg); + }); + } catch (err) { + displayError(el, err); + } } } diff --git a/web_src/less/_markdown.less b/web_src/less/_markdown.less index 0f57bc4449..1b9c412f6b 100644 --- a/web_src/less/_markdown.less +++ b/web_src/less/_markdown.less @@ -495,10 +495,20 @@ } } -.mermaid-chart { - display: flex; - justify-content: center; - align-items: center; - padding: 1rem; - margin: 1rem 0; +.markdown-block-error { + margin-bottom: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; + box-shadow: none !important; + font-size: 85% !important; + white-space: pre !important; + padding: .5rem 1rem !important; + text-align: left !important; +} + +.markdown-block-error + pre { + border-top: none !important; + margin-top: 0 !important; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; } diff --git a/web_src/less/features/animations.less b/web_src/less/features/animations.less new file mode 100644 index 0000000000..65ff1fef3f --- /dev/null +++ b/web_src/less/features/animations.less @@ -0,0 +1,34 @@ +@keyframes isloadingspin { + 0% { transform: translate(-50%, -50%) rotate(0deg); } + 100% { transform: translate(-50%, -50%) rotate(360deg); } +} + +.is-loading { + background: transparent !important; + color: transparent !important; + border: transparent !important; + pointer-events: none !important; + position: relative !important; + overflow: hidden !important; +} + +.is-loading:after { + content: ""; + position: absolute; + display: block; + width: 4rem; + height: 4rem; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + animation: isloadingspin 500ms infinite linear; + border-width: 4px; + border-style: solid; + border-color: #ececec #ececec #666 #666; + border-radius: 100%; +} + +.markdown pre.is-loading, +.editor-loading.is-loading { + height: 12rem; +} diff --git a/web_src/less/index.less b/web_src/less/index.less index 33bd41e6f6..ef38f863cd 100644 --- a/web_src/less/index.less +++ b/web_src/less/index.less @@ -1,5 +1,7 @@ @import "~font-awesome/css/font-awesome.css"; @import "./vendor/gitGraph.css"; +@import "./features/animations.less"; +@import "./markdown/mermaid.less"; @import "_svg"; @import "_tribute"; diff --git a/web_src/less/markdown/mermaid.less b/web_src/less/markdown/mermaid.less new file mode 100644 index 0000000000..2b7951eec9 --- /dev/null +++ b/web_src/less/markdown/mermaid.less @@ -0,0 +1,12 @@ +.mermaid-chart { + display: flex; + justify-content: center; + align-items: center; + padding: 1rem; + margin: 1rem 0; +} + +/* mermaid's errorRenderer seems to unavoidably spew stuff into <body>, hide it */ +body > div[id*="mermaid-"] { + display: none !important; +} diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less index 839cf89b1f..8de66fd251 100644 --- a/web_src/less/themes/theme-arc-green.less +++ b/web_src/less/themes/theme-arc-green.less @@ -1260,7 +1260,8 @@ input { border-color: #794f31; } -.ui.red.message { +.ui.red.message, +.ui.error.message { background-color: rgba(80, 23, 17, .6); color: #f9cbcb; box-shadow: 0 0 0 1px rgba(121, 71, 66, .5) inset, 0 0 0 0 transparent; @@ -1923,3 +1924,12 @@ footer .container .links > * { .mermaid-chart { filter: invert(84%) hue-rotate(180deg); } + +.is-loading:after { + border-color: #4a4c58 #4a4c58 #d7d7da #d7d7da; +} + +.markdown-block-error { + border: 1px solid rgba(121, 71, 66, .5) !important; + border-bottom: none !important; +} |