Adjust editor config (#976)

* Adjust editor config

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
Co-authored-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Tilman Vatteroth 2021-02-03 22:13:04 +01:00 committed by GitHub
parent 0180c75e55
commit e12dc523f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
301 changed files with 4393 additions and 3741 deletions

View file

@ -15,5 +15,5 @@ export type NativeRenderer = () => ReactElement
export type MarkdownItPlugin = MarkdownIt.PluginSimple | MarkdownIt.PluginWithOptions | MarkdownIt.PluginWithParams
export abstract class ComponentReplacer {
public abstract getReplacement (node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined);
public abstract getReplacement(node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined);
}

View file

@ -21,8 +21,11 @@ export const AbcFrame: React.FC<AbcFrameProps> = ({ code }) => {
const actualContainer = container.current
import(/* webpackChunkName: "abc.js" */ 'abcjs').then((imp) => {
imp.renderAbc(actualContainer, code)
}).catch(() => { console.error('error while loading abcjs') })
})
.catch(() => {
console.error('error while loading abcjs')
})
}, [code])
return <div ref={container} className={'abcjs-score bg-white text-black text-center overflow-x-auto'}/>
return <div ref={ container } className={ 'abcjs-score bg-white text-black text-center overflow-x-auto' }/>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { AbcFrame } from './abc-frame'
export class AbcReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'abc' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <AbcFrame code={code}/>
return <AbcFrame code={ code }/>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { OneClickEmbedding } from '../one-click-frame/one-click-embedding'
@ -14,12 +14,12 @@ export interface AsciinemaFrameProps {
export const AsciinemaFrame: React.FC<AsciinemaFrameProps> = ({ id }) => {
return (
<OneClickEmbedding
containerClassName={'embed-responsive embed-responsive-16by9'}
previewContainerClassName={'embed-responsive-item'}
hoverIcon={'play'}
loadingImageUrl={`https://asciinema.org/a/${id}.png`}>
<iframe className='embed-responsive-item' title={`asciinema cast ${id}`}
src={`https://asciinema.org/a/${id}/embed?autoplay=1`}/>
containerClassName={ 'embed-responsive embed-responsive-16by9' }
previewContainerClassName={ 'embed-responsive-item' }
hoverIcon={ 'play' }
loadingImageUrl={ `https://asciinema.org/a/${ id }.png` }>
<iframe className='embed-responsive-item' title={ `asciinema cast ${ id }` }
src={ `https://asciinema.org/a/${ id }/embed?autoplay=1` }/>
</OneClickEmbedding>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import MarkdownIt from 'markdown-it'
@ -16,19 +16,19 @@ import { replaceAsciinemaLink } from './replace-asciinema-link'
export class AsciinemaReplacer extends ComponentReplacer {
private counterMap: Map<string, number> = new Map<string, number>()
public getReplacement (node: DomElement): React.ReactElement | undefined {
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceAsciinemaLink)
}
public getReplacement(node: DomElement): React.ReactElement | undefined {
const attributes = getAttributesFromHedgeDocTag(node, 'asciinema')
if (attributes && attributes.id) {
const asciinemaId = attributes.id
const count = (this.counterMap.get(asciinemaId) || 0) + 1
this.counterMap.set(asciinemaId, count)
return (
<AsciinemaFrame id={asciinemaId}/>
<AsciinemaFrame id={ asciinemaId }/>
)
}
}
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceAsciinemaLink)
}
}

View file

@ -19,6 +19,6 @@ export const replaceAsciinemaLink: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-asciinema id="${match}"></app-asciinema>`
return `<app-asciinema id="${ match }"></app-asciinema>`
}
}

View file

@ -10,18 +10,21 @@ describe('test CSV parser', () => {
it('normal table', () => {
const input = 'A;B;C\nD;E;F\nG;H;I'
const expected = [['A', 'B', 'C'], ['D', 'E', 'F'], ['G', 'H', 'I']]
expect(parseCsv(input, ';')).toEqual(expected)
expect(parseCsv(input, ';'))
.toEqual(expected)
})
it('blank lines', () => {
const input = 'A;B;C\n\nG;H;I'
const expected = [['A', 'B', 'C'], ['G', 'H', 'I']]
expect(parseCsv(input, ';')).toEqual(expected)
expect(parseCsv(input, ';'))
.toEqual(expected)
})
it('items with delimiter', () => {
const input = 'A;B;C\n"D;E;F"\nG;H;I'
const expected = [['A', 'B', 'C'], ['"D;E;F"'], ['G', 'H', 'I']]
expect(parseCsv(input, ';')).toEqual(expected)
expect(parseCsv(input, ';'))
.toEqual(expected)
})
})

View file

@ -10,5 +10,6 @@ export const parseCsv = (csvText: string, csvColumnDelimiter: string): string[][
return []
}
const splitRegex = new RegExp(`${csvColumnDelimiter}(?=(?:[^"]*"[^"]*")*[^"]*$)`)
return rows.filter(row => row !== '').map(row => row.split(splitRegex))
return rows.filter(row => row !== '')
.map(row => row.split(splitRegex))
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,7 +10,7 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { CsvTable } from './csv-table'
export class CsvReplacer extends ComponentReplacer {
public getReplacement (codeNode: DomElement): React.ReactElement | undefined {
public getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'csv' || !codeNode.children || !codeNode.children[0]) {
return
}
@ -29,6 +29,6 @@ export class CsvReplacer extends ComponentReplacer {
showHeader = extraInfos[3] !== undefined
}
return <CsvTable code={code} delimiter={delimiter} showHeader={showHeader}/>
return <CsvTable code={ code } delimiter={ delimiter } showHeader={ showHeader }/>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useMemo } from 'react'
import { parseCsv } from './csv-parser'
@ -29,17 +29,17 @@ export const CsvTable: React.FC<CsvTableProps> = ({ code, delimiter, showHeader,
if (row !== []) {
return (
<thead>
<tr>
{
row.map((column, columnNumber) => (
<th
key={`header-${columnNumber}`}
>
{column}
</th>
))
}
</tr>
<tr>
{
row.map((column, columnNumber) => (
<th
key={ `header-${ columnNumber }` }
>
{ column }
</th>
))
}
</tr>
</thead>
)
}
@ -48,30 +48,30 @@ export const CsvTable: React.FC<CsvTableProps> = ({ code, delimiter, showHeader,
const renderTableBody = (rows: string[][]) => {
return (
<tbody>
{
rows.map((row, rowNumber) => (
<tr className={tableRowClassName} key={`row-${rowNumber}`}>
{
row.map((column, columnIndex) => (
<td
className={tableColumnClassName}
key={`cell-${rowNumber}-${columnIndex}`}
>
{column.replace(/^"|"$/g, '')}
</td>
))
}
</tr>
))
}
{
rows.map((row, rowNumber) => (
<tr className={ tableRowClassName } key={ `row-${ rowNumber }` }>
{
row.map((column, columnIndex) => (
<td
className={ tableColumnClassName }
key={ `cell-${ rowNumber }-${ columnIndex }` }
>
{ column.replace(/^"|"$/g, '') }
</td>
))
}
</tr>
))
}
</tbody>
)
}
return (
<table className={'csv-html-table table-striped'}>
{renderTableHeader(headerRow)}
{renderTableBody(rowsWithColumns)}
<table className={ 'csv-html-table table-striped' }>
{ renderTableHeader(headerRow) }
{ renderTableBody(rowsWithColumns) }
</table>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { FlowChart } from './flowchart/flowchart'
export class FlowchartReplacer extends ComponentReplacer {
public getReplacement (codeNode: DomElement): React.ReactElement | undefined {
public getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'flow' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <FlowChart code={code}/>
return <FlowChart code={ code }/>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap'
@ -41,19 +41,23 @@ export const FlowChart: React.FC<FlowChartProps> = ({ code }) => {
} catch (error) {
setError(true)
}
}).catch(() => { console.error('error while loading flowchart.js') })
})
.catch(() => {
console.error('error while loading flowchart.js')
})
return () => {
Array.from(currentDiagramRef.children).forEach(value => value.remove())
Array.from(currentDiagramRef.children)
.forEach(value => value.remove())
}
}, [code, darkModeActivated])
if (error) {
return (
<Alert variant={'danger'}>
<Trans i18nKey={'renderer.flowchart.invalidSyntax'}/>
<Alert variant={ 'danger' }>
<Trans i18nKey={ 'renderer.flowchart.invalidSyntax' }/>
</Alert>)
} else {
return <div ref={diagramRef} className={'text-center'}/>
return <div ref={ diagramRef } className={ 'text-center' }/>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import './gist-frame.scss'
@ -29,7 +29,7 @@ export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
</style>
<script type="text/javascript">
function doLoad() {
window.parent.postMessage({eventType: 'gistResize', size: document.body.scrollHeight, id: '${id}'}, '*')
window.parent.postMessage({eventType: 'gistResize', size: document.body.scrollHeight, id: '${ id }'}, '*')
tweakLinks();
}
function tweakLinks() {
@ -41,7 +41,7 @@ export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
</script>
</head>
<body onload="doLoad()">
<script type="text/javascript" src="https://gist.github.com/${id}.js"></script>
<script type="text/javascript" src="https://gist.github.com/${ id }.js"></script>
</body>
</html>`)
}, [id])
@ -67,9 +67,9 @@ export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
<iframe
sandbox="allow-scripts allow-top-navigation-by-user-activation allow-popups"
width='100%'
height={`${frameHeight}px`}
height={ `${ frameHeight }px` }
frameBorder='0'
title={`gist ${id}`}
src={`data:text/html;base64,${btoa(iframeHtml)}`}/>
title={ `gist ${ id }` }
src={ `data:text/html;base64,${ btoa(iframeHtml) }` }/>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import MarkdownIt from 'markdown-it'
@ -19,22 +19,23 @@ import { replaceLegacyGistShortCode } from './replace-legacy-gist-short-code'
export class GistReplacer extends ComponentReplacer {
private counterMap: Map<string, number> = new Map<string, number>()
public getReplacement (node: DomElement): React.ReactElement | undefined {
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceGistLink)
markdownItRegex(markdownIt, replaceLegacyGistShortCode)
}
public getReplacement(node: DomElement): React.ReactElement | undefined {
const attributes = getAttributesFromHedgeDocTag(node, 'gist')
if (attributes && attributes.id) {
const gistId = attributes.id
const count = (this.counterMap.get(gistId) || 0) + 1
this.counterMap.set(gistId, count)
return (
<OneClickEmbedding previewContainerClassName={'gist-frame'} loadingImageUrl={preview} hoverIcon={'github'} tooltip={'click to load gist'}>
<GistFrame id={gistId}/>
<OneClickEmbedding previewContainerClassName={ 'gist-frame' } loadingImageUrl={ preview } hoverIcon={ 'github' }
tooltip={ 'click to load gist' }>
<GistFrame id={ gistId }/>
</OneClickEmbedding>
)
}
}
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceGistLink)
markdownItRegex(markdownIt, replaceLegacyGistShortCode)
}
}

View file

@ -19,6 +19,6 @@ export const replaceGistLink: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-gist id="${match}"></app-gist>`
return `<app-gist id="${ match }"></app-gist>`
}
}

View file

@ -14,6 +14,6 @@ export const replaceLegacyGistShortCode: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-gist id="${match}"></app-gist>`
return `<app-gist id="${ match }"></app-gist>`
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap'
@ -22,7 +22,8 @@ export const GraphvizFrame: React.FC<GraphvizFrameProps> = ({ code }) => {
}
setError(error)
console.error(error)
container.current.querySelectorAll('svg').forEach(child => child.remove())
container.current.querySelectorAll('svg')
.forEach(child => child.remove())
}, [])
useEffect(() => {
@ -43,19 +44,22 @@ export const GraphvizFrame: React.FC<GraphvizFrameProps> = ({ code }) => {
useWorker: false,
zoom: false
})
.onerror(showError)
.renderDot(code)
.onerror(showError)
.renderDot(code)
} catch (error) {
showError(error)
}
}).catch(() => { console.error('error while loading graphviz') })
})
.catch(() => {
console.error('error while loading graphviz')
})
}, [code, error, showError])
return <Fragment>
<ShowIf condition={!!error}>
<Alert variant={'warning'}>{error}</Alert>
<ShowIf condition={ !!error }>
<Alert variant={ 'warning' }>{ error }</Alert>
</ShowIf>
<div className={'text-center overflow-x-auto'} ref={container} />
<div className={ 'text-center overflow-x-auto' } ref={ container }/>
</Fragment>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { GraphvizFrame } from './graphviz-frame'
export class GraphvizReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'graphviz' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <GraphvizFrame code={code}/>
return <GraphvizFrame code={ code }/>
}
}

View file

@ -17,6 +17,7 @@
code.hljs {
background-color: rgba(27, 31, 35, .05);
body.dark & {
background-color: rgb(27, 31, 35);
}

View file

@ -28,38 +28,44 @@ export const escapeHtml = (unsafe: string): string => {
const replaceCode = (code: string): ReactElement[][] => {
return code.split('\n')
.filter(line => !!line)
.map(line => ReactHtmlParser(line))
.filter(line => !!line)
.map(line => ReactHtmlParser(line))
}
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, startLineNumber, wrapLines }) => {
const [dom, setDom] = useState<ReactElement[]>()
useEffect(() => {
import(/* webpackChunkName: "highlight.js" */ '../../../../common/hljs/hljs').then(( hljs) => {
const languageSupported = (lang: string) => hljs.default.listLanguages().includes(lang)
import(/* webpackChunkName: "highlight.js" */ '../../../../common/hljs/hljs').then((hljs) => {
const languageSupported = (lang: string) => hljs.default.listLanguages()
.includes(lang)
const unreplacedCode = !!language && languageSupported(language) ? hljs.default.highlight(language, code).value : escapeHtml(code)
const replacedDom = replaceCode(unreplacedCode).map((line, index) => (
<Fragment key={index}>
<span className={'linenumber'}>
const replacedDom = replaceCode(unreplacedCode)
.map((line, index) => (
<Fragment key={ index }>
<span className={ 'linenumber' }>
{ (startLineNumber || 1) + index }
</span>
<div className={'codeline'}>
{line}
</div>
</Fragment>
))
<div className={ 'codeline' }>
{ line }
</div>
</Fragment>
))
setDom(replacedDom)
}).catch(() => { console.error('error while loading highlight.js') })
})
.catch(() => {
console.error('error while loading highlight.js')
})
}, [code, language, startLineNumber])
return (
<Fragment>
<code className={`hljs ${startLineNumber !== undefined ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
<code
className={ `hljs ${ startLineNumber !== undefined ? 'showGutter' : '' } ${ wrapLines ? 'wrapLines' : '' }` }>
{ dom }
</code>
<div className={'text-right button-inside'}>
<CopyToClipboardButton content={code} data-cy="copy-code-button"/>
<div className={ 'text-right button-inside' }>
<CopyToClipboardButton content={ code } data-cy="copy-code-button"/>
</div>
</Fragment>)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,9 +10,9 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { HighlightedCode } from './highlighted-code/highlighted-code'
export class HighlightedCodeReplacer extends ComponentReplacer {
private lastLineNumber = 0;
private lastLineNumber = 0
public getReplacement (codeNode: DomElement): React.ReactElement | undefined {
public getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || !codeNode.children || !codeNode.children[0]) {
return
}
@ -36,9 +36,10 @@ export class HighlightedCodeReplacer extends ComponentReplacer {
if (showLineNumbers) {
this.lastLineNumber = startLineNumber + code.split('\n')
.filter(line => !!line).length
.filter(line => !!line).length
}
return <HighlightedCode language={language} startLineNumber={showLineNumbers ? startLineNumber : undefined} wrapLines={wrapLines} code={code}/>
return <HighlightedCode language={ language } startLineNumber={ showLineNumbers ? startLineNumber : undefined }
wrapLines={ wrapLines } code={ code }/>
}
}

View file

@ -7,7 +7,7 @@
import React from 'react'
import { Modal } from 'react-bootstrap'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import "./lightbox.scss"
import './lightbox.scss'
import { ProxyImageFrame } from './proxy-image-frame'
export interface ImageLightboxModalProps {
@ -21,20 +21,21 @@ export interface ImageLightboxModalProps {
export const ImageLightboxModal: React.FC<ImageLightboxModalProps> = ({ show, onHide, src, alt, title }) => {
return (
<Modal
animation={true}
centered={true}
dialogClassName={'text-dark lightbox'}
show={show && !!src}
onHide={onHide}
size={'xl'}>
<Modal.Header closeButton={true}>
<Modal.Title className={'h6'}>
<ForkAwesomeIcon icon={'picture-o'}/>
animation={ true }
centered={ true }
dialogClassName={ 'text-dark lightbox' }
show={ show && !!src }
onHide={ onHide }
size={ 'xl' }>
<Modal.Header closeButton={ true }>
<Modal.Title className={ 'h6' }>
<ForkAwesomeIcon icon={ 'picture-o' }/>
&nbsp;
<span>{alt ?? title ?? ''}</span>
<span>{ alt ?? title ?? '' }</span>
</Modal.Title>
</Modal.Header>
<ProxyImageFrame alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide}/>
<ProxyImageFrame alt={ alt } src={ src } title={ title } className={ 'w-100 cursor-zoom-out' }
onClick={ onHide }/>
</Modal>
)
}

View file

@ -14,22 +14,22 @@ export type ImageClickHandler = (event: React.MouseEvent<HTMLImageElement, Mouse
export class ImageReplacer extends ComponentReplacer {
private readonly clickHandler?: ImageClickHandler
constructor (clickHandler?: ImageClickHandler) {
constructor(clickHandler?: ImageClickHandler) {
super()
this.clickHandler = clickHandler
}
public getReplacement (node: DomElement): React.ReactElement | undefined {
public getReplacement(node: DomElement): React.ReactElement | undefined {
if (node.name === 'img' && node.attribs) {
return <ProxyImageFrame
id={node.attribs.id}
className={`${node.attribs.class} cursor-zoom-in`}
src={node.attribs.src}
alt={node.attribs.alt}
title={node.attribs.title}
width={node.attribs.width}
height={node.attribs.height}
onClick={this.clickHandler}
id={ node.attribs.id }
className={ `${ node.attribs.class } cursor-zoom-in` }
src={ node.attribs.src }
alt={ node.attribs.alt }
title={ node.attribs.title }
width={ node.attribs.width }
height={ node.attribs.height }
onClick={ this.clickHandler }
/>
}
}

View file

@ -28,6 +28,6 @@ export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>
.catch(err => console.error(err))
}, [imageProxyEnabled, src])
return <img src={imageProxyEnabled ? imageUrl : (src ?? '')} title={title ?? alt ?? ''} alt={alt} {...props}/>
return <img src={ imageProxyEnabled ? imageUrl : (src ?? '') } title={ title ?? alt ?? '' } alt={ alt } { ...props }/>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import MarkdownIt from 'markdown-it'
@ -27,15 +27,6 @@ const getNodeIfInlineKatex = (node: DomElement): (DomElement | undefined) => {
const KaTeX = React.lazy(() => import(/* webpackChunkName: "katex" */ '@matejmazur/react-katex'))
export class KatexReplacer extends ComponentReplacer {
public getReplacement (node: DomElement): React.ReactElement | undefined {
const katex = getNodeIfKatexBlock(node) || getNodeIfInlineKatex(node)
if (katex?.children && katex.children[0]) {
const mathJaxContent = katex.children[0]?.data as string
const isInline = (katex.attribs?.inline) !== undefined
return <KaTeX block={!isInline} math={mathJaxContent} errorColor={'#cc0000'}/>
}
}
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = mathJax({
beforeMath: '<app-katex>',
afterMath: '</app-katex>',
@ -44,4 +35,13 @@ export class KatexReplacer extends ComponentReplacer {
beforeDisplayMath: '<app-katex>',
afterDisplayMath: '</app-katex>'
})
public getReplacement(node: DomElement): React.ReactElement | undefined {
const katex = getNodeIfKatexBlock(node) || getNodeIfInlineKatex(node)
if (katex?.children && katex.children[0]) {
const mathJaxContent = katex.children[0]?.data as string
const isInline = (katex.attribs?.inline) !== undefined
return <KaTeX block={ !isInline } math={ mathJaxContent } errorColor={ '#cc0000' }/>
}
}
}

View file

@ -38,14 +38,14 @@ export const lineNumberMarker: MarkdownIt.PluginWithOptions<LineNumberMarkerOpti
return ''
}
// noinspection CheckTagEmptyBody
return `<app-linemarker data-start-line='${startLineNumber}' data-end-line='${endLineNumber}'></app-linemarker>`
return `<app-linemarker data-start-line='${ startLineNumber }' data-end-line='${ endLineNumber }'></app-linemarker>`
}
const insertNewLineMarker = (startLineNumber: number, endLineNumber: number, tokenPosition: number, level: number, tokens: Token[]) => {
const startToken = new Token('app_linemarker', 'app-linemarker', 0)
startToken.level = level
startToken.attrPush(['data-start-line', `${startLineNumber}`])
startToken.attrPush(['data-end-line', `${endLineNumber}`])
startToken.attrPush(['data-start-line', `${ startLineNumber }`])
startToken.attrPush(['data-end-line', `${ endLineNumber }`])
tokens.splice(tokenPosition, 0, startToken)
}

View file

@ -1,14 +1,14 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import { ComponentReplacer } from '../ComponentReplacer'
export class LinemarkerReplacer extends ComponentReplacer {
public getReplacement (codeNode: DomElement): null | undefined {
public getReplacement(codeNode: DomElement): null | undefined {
return codeNode.name === 'app-linemarker' ? null : undefined
}
}

View file

@ -9,17 +9,18 @@ import { ComponentReplacer, NativeRenderer, SubNodeTransform } from '../Componen
export const createJumpToMarkClickEventHandler = (id: string) => {
return (event: React.MouseEvent<HTMLElement, MouseEvent>): void => {
document.getElementById(id)?.scrollIntoView()
document.getElementById(id)
?.scrollIntoView()
event.preventDefault()
}
}
export class LinkReplacer extends ComponentReplacer {
constructor (private baseUrl?: string) {
constructor(private baseUrl?: string) {
super()
}
public getReplacement (node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined) {
public getReplacement(node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined) {
if (node.name !== 'a' || !node.attribs || !node.attribs.href) {
return undefined
}
@ -36,12 +37,12 @@ export class LinkReplacer extends ComponentReplacer {
}
if (isJumpMark) {
return <span onClick={createJumpToMarkClickEventHandler(id)}>
{nativeRenderer()}
return <span onClick={ createJumpToMarkClickEventHandler(id) }>
{ nativeRenderer() }
</span>
} else {
node.attribs.rel = "noreferer noopener"
node.attribs.target = "_blank"
node.attribs.rel = 'noreferer noopener'
node.attribs.target = '_blank'
return nativeRenderer()
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -49,23 +49,25 @@ export const MarkmapFrame: React.FC<MarkmapFrameProps> = ({ code }) => {
try {
const svg: SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.setAttribute('width', '100%')
actualContainer.querySelectorAll('svg').forEach(child => child.remove())
actualContainer.querySelectorAll('svg')
.forEach(child => child.remove())
actualContainer.appendChild(svg)
markmapLoader(svg, code)
} catch(error) {
} catch (error) {
console.error(error)
}
}).catch(() => {
console.error('error while loading markmap')
})
.catch(() => {
console.error('error while loading markmap')
})
}, [code])
return (
<Fragment>
<div className={'text-center'} ref={diagramContainer}/>
<div className={'text-right button-inside'}>
<LockButton locked={disablePanAndZoom} onLockedChanged={(newState => setDisablePanAndZoom(newState))}
title={disablePanAndZoom ? t('renderer.markmap.locked') : t('renderer.markmap.unlocked')}/>
<div className={ 'text-center' } ref={ diagramContainer }/>
<div className={ 'text-right button-inside' }>
<LockButton locked={ disablePanAndZoom } onLockedChanged={ (newState => setDisablePanAndZoom(newState)) }
title={ disablePanAndZoom ? t('renderer.markmap.locked') : t('renderer.markmap.unlocked') }/>
</div>
</Fragment>
)

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { loadCSS, loadJS } from 'markmap-common'
import { Transformer } from 'markmap-lib'
@ -18,7 +18,8 @@ export const markmapLoader = (svg: SVGSVGElement, code: string): void => {
loadCSS(styles)
}
if (scripts) {
loadJS(scripts).catch(console.log)
loadJS(scripts)
.catch(console.log)
}
Markmap.create(svg, {}, root)

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { MarkmapFrame } from './markmap-frame'
export class MarkmapReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'markmap' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <MarkmapFrame code={code}/>
return <MarkmapFrame code={ code }/>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap'
@ -30,7 +30,10 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
import('mermaid').then((mermaid) => {
mermaid.default.initialize({ startOnLoad: false })
mermaidInitialized = true
}).catch(() => { console.error('error while loading mermaid') })
})
.catch(() => {
console.error('error while loading mermaid')
})
}
}, [])
@ -40,7 +43,8 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
if (!diagramContainer.current) {
return
}
diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove())
diagramContainer.current.querySelectorAll('svg')
.forEach(child => child.remove())
}, [setError])
useEffect(() => {
@ -49,25 +53,26 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
}
import('mermaid').then((mermaid) => {
try {
if (!diagramContainer.current) {
return
}
mermaid.default.parse(code)
delete diagramContainer.current.dataset.processed
diagramContainer.current.textContent = code
mermaid.default.init(diagramContainer.current)
setError(undefined)
if (!diagramContainer.current) {
return
}
mermaid.default.parse(code)
delete diagramContainer.current.dataset.processed
diagramContainer.current.textContent = code
mermaid.default.init(diagramContainer.current)
setError(undefined)
} catch (error) {
const message = (error as MermaidParseError).str
showError(message || t('renderer.mermaid.unknownError'))
}
}).catch(() => showError('Error while loading mermaid'))
})
.catch(() => showError('Error while loading mermaid'))
}, [code, showError, t])
return <Fragment>
<ShowIf condition={!!error}>
<Alert variant={'warning'}>{error}</Alert>
<ShowIf condition={ !!error }>
<Alert variant={ 'warning' }>{ error }</Alert>
</ShowIf>
<div className={'text-center mermaid text-black'} ref={diagramContainer}/>
<div className={ 'text-center mermaid text-black' } ref={ diagramContainer }/>
</Fragment>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { MermaidChart } from './mermaid-chart'
export class MermaidReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'mermaid' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <MermaidChart code={code}/>
return <MermaidChart code={ code }/>
}
}

View file

@ -4,6 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
.mermaid>svg {
.mermaid > svg {
background-color: #f8f9fa;
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useEffect, useState } from 'react'
import { Trans } from 'react-i18next'
@ -36,29 +36,32 @@ export const OneClickEmbedding: React.FC<OneClickFrameProps> = ({ previewContain
if (!onImageFetch) {
return
}
onImageFetch().then((imageLink) => {
setPreviewImageUrl(imageLink)
}).catch((message) => {
console.error(message)
})
onImageFetch()
.then((imageLink) => {
setPreviewImageUrl(imageLink)
})
.catch((message) => {
console.error(message)
})
}, [onImageFetch])
return (
<span className={ containerClassName }>
<ShowIf condition={showFrame}>
{children}
<ShowIf condition={ showFrame }>
{ children }
</ShowIf>
<ShowIf condition={!showFrame}>
<span className={`one-click-embedding ${previewContainerClassName || ''}`} onClick={showChildren}>
<ShowIf condition={!!previewImageUrl}>
<img className={'one-click-embedding-preview'} src={previewImageUrl} alt={tooltip || ''} title={tooltip || ''}/>
<ShowIf condition={ !showFrame }>
<span className={ `one-click-embedding ${ previewContainerClassName || '' }` } onClick={ showChildren }>
<ShowIf condition={ !!previewImageUrl }>
<img className={ 'one-click-embedding-preview' } src={ previewImageUrl } alt={ tooltip || '' }
title={ tooltip || '' }/>
</ShowIf>
<ShowIf condition={!!hoverIcon}>
<ShowIf condition={ !!hoverIcon }>
<span className='one-click-embedding-icon text-center'>
<i className={`fa fa-${hoverIcon as string} fa-5x mb-2`} />
<ShowIf condition={!!hoverTextI18nKey}>
<br />
<Trans i18nKey={hoverTextI18nKey} />
<i className={ `fa fa-${ hoverIcon as string } fa-5x mb-2` }/>
<ShowIf condition={ !!hoverTextI18nKey }>
<br/>
<Trans i18nKey={ hoverTextI18nKey }/>
</ShowIf>
</span>
</ShowIf>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import { ReactElement } from 'react'
@ -25,7 +25,7 @@ const findQuoteOptionsParent = (nodes: DomElement[]): DomElement | undefined =>
}
export class QuoteOptionsReplacer extends ComponentReplacer {
public getReplacement (node: DomElement):ReactElement|undefined {
public getReplacement(node: DomElement): ReactElement | undefined {
if (node.name !== 'blockquote' || !node.children || node.children.length < 1) {
return
}
@ -43,6 +43,6 @@ export class QuoteOptionsReplacer extends ComponentReplacer {
if (!attributes || !attributes['data-color']) {
return
}
node.attribs = Object.assign(node.attribs || {}, { style: `border-left-color: ${attributes['data-color']};` })
node.attribs = Object.assign(node.attribs || {}, { style: `border-left-color: ${ attributes['data-color'] };` })
}
}

View file

@ -14,12 +14,12 @@ export const DeprecationWarning: React.FC = () => {
useTranslation()
return (
<Alert data-cy={'yaml'} className={'mt-2'} variant={'warning'}>
<span className={'text-wrap'}>
<Trans i18nKey={'renderer.sequence.deprecationWarning'}/>
<Alert data-cy={ 'yaml' } className={ 'mt-2' } variant={ 'warning' }>
<span className={ 'text-wrap' }>
<Trans i18nKey={ 'renderer.sequence.deprecationWarning' }/>
</span>
<br/>
<TranslatedExternalLink i18nKey={'common.readForMoreInfo'} className={'text-primary'} href={links.faq}/>
<TranslatedExternalLink i18nKey={ 'common.readForMoreInfo' } className={ 'text-primary' } href={ links.faq }/>
</Alert>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React, { Fragment } from 'react'
@ -11,7 +11,7 @@ import { MermaidChart } from '../mermaid/mermaid-chart'
import { DeprecationWarning } from './deprecation-warning'
export class SequenceDiagramReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'sequence' || !codeNode.children || !codeNode.children[0]) {
return
}
@ -20,7 +20,7 @@ export class SequenceDiagramReplacer implements ComponentReplacer {
return <Fragment>
<DeprecationWarning/>
<MermaidChart code={'sequenceDiagram\n' + code}/>
<MermaidChart code={ 'sequenceDiagram\n' + code }/>
</Fragment>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React, { ReactElement } from 'react'
@ -11,7 +11,7 @@ import { ComponentReplacer } from '../ComponentReplacer'
export class TaskListReplacer extends ComponentReplacer {
onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void
constructor (onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void) {
constructor(onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void) {
super()
this.onTaskCheckedChange = onTaskCheckedChange
}
@ -23,19 +23,19 @@ export class TaskListReplacer extends ComponentReplacer {
}
}
public getReplacement (node: DomElement): (ReactElement | undefined) {
public getReplacement(node: DomElement): (ReactElement | undefined) {
if (node.attribs?.class !== 'task-list-item-checkbox') {
return
}
return (
<input
disabled={this.onTaskCheckedChange === undefined}
disabled={ this.onTaskCheckedChange === undefined }
className="task-list-item-checkbox"
type="checkbox"
checked={node.attribs.checked !== undefined}
onChange={this.handleCheckboxChange}
id={node.attribs.id}
data-line={node.attribs['data-line']}
checked={ node.attribs.checked !== undefined }
onChange={ this.handleCheckboxChange }
id={ node.attribs.id }
data-line={ node.attribs['data-line'] }
/>
)
}

View file

@ -6,8 +6,8 @@
import { DomElement } from 'domhandler'
export const getAttributesFromHedgeDocTag = (node: DomElement, tagName: string): ({ [s: string]: string; }|undefined) => {
if (node.name !== `app-${tagName}` || !node.attribs) {
export const getAttributesFromHedgeDocTag = (node: DomElement, tagName: string): ({ [s: string]: string; } | undefined) => {
if (node.name !== `app-${ tagName }` || !node.attribs) {
return
}
return node.attribs

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap'
@ -50,20 +50,23 @@ export const VegaChart: React.FC<VegaChartProps> = ({ code }) => {
SVG_ACTION: t('renderer.vega-lite.svg')
}
})
.then(() => setError(undefined))
.catch(err => showError(err))
.then(() => setError(undefined))
.catch(err => showError(err))
} catch (err) {
showError(t('renderer.vega-lite.errorJson'))
}
}).catch(() => { console.error('error while loading vega-light') })
})
.catch(() => {
console.error('error while loading vega-light')
})
}, [code, showError, t])
return <Fragment>
<ShowIf condition={!!error}>
<Alert variant={'danger'}>{error}</Alert>
<ShowIf condition={ !!error }>
<Alert variant={ 'danger' }>{ error }</Alert>
</ShowIf>
<div className={'text-center'}>
<div ref={diagramContainer}/>
<div className={ 'text-center' }>
<div ref={ diagramContainer }/>
</div>
</Fragment>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React from 'react'
@ -10,13 +10,13 @@ import { ComponentReplacer } from '../ComponentReplacer'
import { VegaChart } from './vega-chart'
export class VegaReplacer implements ComponentReplacer {
getReplacement (codeNode: DomElement): React.ReactElement | undefined {
getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || codeNode.attribs['data-highlight-language'] !== 'vega-lite' || !codeNode.children || !codeNode.children[0]) {
return
}
const code = codeNode.children[0].data as string
return <VegaChart code={code}/>
return <VegaChart code={ code }/>
}
}

View file

@ -12,6 +12,6 @@ export const replaceLegacyVimeoShortCode: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-vimeo id="${match}"></app-vimeo>`
return `<app-vimeo id="${ match }"></app-vimeo>`
}
}

View file

@ -19,6 +19,6 @@ export const replaceVimeoLink: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-vimeo id="${match}"></app-vimeo>`
return `<app-vimeo id="${ match }"></app-vimeo>`
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback } from 'react'
import { OneClickEmbedding } from '../one-click-frame/one-click-embedding'
@ -19,7 +19,7 @@ export interface VimeoFrameProps {
export const VimeoFrame: React.FC<VimeoFrameProps> = ({ id }) => {
const getPreviewImageLink = useCallback(async () => {
const response = await fetch(`https://vimeo.com/api/v2/video/${id}.json`, {
const response = await fetch(`https://vimeo.com/api/v2/video/${ id }.json`, {
credentials: 'omit',
referrerPolicy: 'no-referrer'
})
@ -36,11 +36,13 @@ export const VimeoFrame: React.FC<VimeoFrameProps> = ({ id }) => {
}, [id])
return (
<OneClickEmbedding containerClassName={'embed-responsive embed-responsive-16by9'} previewContainerClassName={'embed-responsive-item'} loadingImageUrl={'https://i.vimeocdn.com/video/'} hoverIcon={'vimeo-square'}
onImageFetch={getPreviewImageLink}>
<iframe className='embed-responsive-item' title={`vimeo video of ${id}`}
src={`https://player.vimeo.com/video/${id}?autoplay=1`}
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"/>
<OneClickEmbedding containerClassName={ 'embed-responsive embed-responsive-16by9' }
previewContainerClassName={ 'embed-responsive-item' }
loadingImageUrl={ 'https://i.vimeocdn.com/video/' } hoverIcon={ 'vimeo-square' }
onImageFetch={ getPreviewImageLink }>
<iframe className='embed-responsive-item' title={ `vimeo video of ${ id }` }
src={ `https://player.vimeo.com/video/${ id }?autoplay=1` }
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"/>
</OneClickEmbedding>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import MarkdownIt from 'markdown-it'
@ -17,18 +17,18 @@ import { VimeoFrame } from './vimeo-frame'
export class VimeoReplacer extends ComponentReplacer {
private counterMap: Map<string, number> = new Map<string, number>()
public getReplacement (node: DomElement): React.ReactElement | undefined {
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceVimeoLink)
markdownItRegex(markdownIt, replaceLegacyVimeoShortCode)
}
public getReplacement(node: DomElement): React.ReactElement | undefined {
const attributes = getAttributesFromHedgeDocTag(node, 'vimeo')
if (attributes && attributes.id) {
const videoId = attributes.id
const count = (this.counterMap.get(videoId) || 0) + 1
this.counterMap.set(videoId, count)
return <VimeoFrame id={videoId}/>
return <VimeoFrame id={ videoId }/>
}
}
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceVimeoLink)
markdownItRegex(markdownIt, replaceLegacyVimeoShortCode)
}
}

View file

@ -12,6 +12,6 @@ export const replaceLegacyYoutubeShortCode: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-youtube id="${match}"></app-youtube>`
return `<app-youtube id="${ match }"></app-youtube>`
}
}

View file

@ -20,6 +20,6 @@ export const replaceYouTubeLink: RegexOptions = {
replace: (match) => {
// ESLint wants to collapse this tag, but then the tag won't be valid html anymore.
// noinspection CheckTagEmptyBody
return `<app-youtube id="${match}"></app-youtube>`
return `<app-youtube id="${ match }"></app-youtube>`
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { OneClickEmbedding } from '../one-click-frame/one-click-embedding'
@ -13,12 +13,12 @@ export interface YouTubeFrameProps {
export const YouTubeFrame: React.FC<YouTubeFrameProps> = ({ id }) => {
return (
<OneClickEmbedding containerClassName={'embed-responsive embed-responsive-16by9'}
previewContainerClassName={'embed-responsive-item'} hoverIcon={'youtube-play'}
loadingImageUrl={`//i.ytimg.com/vi/${id}/maxresdefault.jpg`}>
<iframe className='embed-responsive-item' title={`youtube video of ${id}`}
src={`//www.youtube-nocookie.com/embed/${id}?autoplay=1`}
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"/>
<OneClickEmbedding containerClassName={ 'embed-responsive embed-responsive-16by9' }
previewContainerClassName={ 'embed-responsive-item' } hoverIcon={ 'youtube-play' }
loadingImageUrl={ `//i.ytimg.com/vi/${ id }/maxresdefault.jpg` }>
<iframe className='embed-responsive-item' title={ `youtube video of ${ id }` }
src={ `//www.youtube-nocookie.com/embed/${ id }?autoplay=1` }
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"/>
</OneClickEmbedding>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import MarkdownIt from 'markdown-it'
@ -17,18 +17,18 @@ import { YouTubeFrame } from './youtube-frame'
export class YoutubeReplacer extends ComponentReplacer {
private counterMap: Map<string, number> = new Map<string, number>()
public getReplacement (node: DomElement): React.ReactElement | undefined {
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceYouTubeLink)
markdownItRegex(markdownIt, replaceLegacyYoutubeShortCode)
}
public getReplacement(node: DomElement): React.ReactElement | undefined {
const attributes = getAttributesFromHedgeDocTag(node, 'youtube')
if (attributes && attributes.id) {
const videoId = attributes.id
const count = (this.counterMap.get(videoId) || 0) + 1
this.counterMap.set(videoId, count)
return <YouTubeFrame id={videoId}/>
return <YouTubeFrame id={ videoId }/>
}
}
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = (markdownIt) => {
markdownItRegex(markdownIt, replaceYouTubeLink)
markdownItRegex(markdownIt, replaceLegacyYoutubeShortCode)
}
}