Add prettier for codestyle and re-format everything (#1294)

This commit is contained in:
Erik Michelson 2021-06-06 23:14:00 +02:00 committed by GitHub
parent 8b78154075
commit 0aae1f70d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
319 changed files with 4809 additions and 3936 deletions

View file

@ -15,5 +15,9 @@ 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

@ -28,5 +28,5 @@ export const AbcFrame: React.FC<AbcFrameProps> = ({ code }) => {
})
}, [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

@ -11,12 +11,19 @@ import { AbcFrame } from './abc-frame'
export class AbcReplacer implements ComponentReplacer {
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]) {
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

@ -14,12 +14,15 @@ 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

@ -22,9 +22,7 @@ export class AsciinemaReplacer extends ComponentReplacer {
const attributes = getAttributesFromHedgeDocTag(node, 'asciinema')
if (attributes && attributes.id) {
const asciinemaId = attributes.id
return (
<AsciinemaFrame id={ asciinemaId }/>
)
return <AsciinemaFrame id={asciinemaId} />
}
}
}

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

@ -12,7 +12,7 @@ const isColorExtraElement = (node: DomElement | undefined): boolean => {
if (!node || !node.attribs || !node.attribs.class || !node.attribs['data-color']) {
return false
}
return (node.name === 'span' && node.attribs.class === 'quote-extra')
return node.name === 'span' && node.attribs.class === 'quote-extra'
}
const findQuoteOptionsParent = (nodes: DomElement[]): DomElement | undefined => {
@ -25,7 +25,11 @@ const findQuoteOptionsParent = (nodes: DomElement[]): DomElement | undefined =>
}
export class ColoredBlockquoteReplacer extends ComponentReplacer {
public getReplacement(node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): ReactElement | undefined {
public getReplacement(
node: DomElement,
subNodeTransform: SubNodeTransform,
nativeRenderer: NativeRenderer
): ReactElement | undefined {
if (node.name !== 'blockquote' || !node.children || node.children.length < 1) {
return
}
@ -38,12 +42,12 @@ export class ColoredBlockquoteReplacer extends ComponentReplacer {
if (!optionsTag) {
return
}
paragraph.children = childElements.filter(elem => !isColorExtraElement(elem))
paragraph.children = childElements.filter((elem) => !isColorExtraElement(elem))
const attributes = optionsTag.attribs
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']};` })
return nativeRenderer()
}
}

View file

@ -9,22 +9,26 @@ import { parseCsv } from './csv-parser'
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)
const expected = [
['A', 'B', 'C'],
['D', 'E', 'F'],
['G', 'H', 'I']
]
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)
const expected = [
['A', 'B', 'C'],
['G', 'H', 'I']
]
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,6 +10,5 @@ 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

@ -11,7 +11,14 @@ import { CsvTable } from './csv-table'
export class CsvReplacer extends ComponentReplacer {
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]) {
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 +36,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

@ -15,7 +15,13 @@ export interface CsvTableProps {
tableColumnClassName?: string
}
export const CsvTable: React.FC<CsvTableProps> = ({ code, delimiter, showHeader, tableRowClassName, tableColumnClassName }) => {
export const CsvTable: React.FC<CsvTableProps> = ({
code,
delimiter,
showHeader,
tableRowClassName,
tableColumnClassName
}) => {
const { rowsWithColumns, headerRow } = useMemo(() => {
const rowsWithColumns = parseCsv(code.trim(), delimiter)
let headerRow: string[] = []
@ -29,17 +35,11 @@ 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,23 @@ 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>
))
}
{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

@ -11,12 +11,19 @@ import { FlowChart } from './flowchart/flowchart'
export class FlowchartReplacer extends ComponentReplacer {
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]) {
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

@ -46,17 +46,17 @@ export const FlowChart: React.FC<FlowChartProps> = ({ code }) => {
.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>)
<Alert variant={'danger'}>
<Trans i18nKey={'renderer.flowchart.invalidSyntax'} />
</Alert>
)
} else {
return <div ref={ diagramRef } data-cy={ 'flowchart' } className={ 'text-center' }/>
return <div ref={diagramRef} data-cy={'flowchart'} className={'text-center'} />
}
}

View file

@ -18,7 +18,7 @@ interface resizeEvent {
export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
const iframeHtml = useMemo(() => {
return (`
return `
<html lang="en">
<head>
<base target="_parent">
@ -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,20 +41,23 @@ 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>`)
</html>`
}, [id])
const [frameHeight, setFrameHeight] = useState(0)
const sizeMessage = useCallback((message: MessageEvent) => {
const data = message.data as resizeEvent
if (data.id !== id) {
return
}
setFrameHeight(data.size)
}, [id])
const sizeMessage = useCallback(
(message: MessageEvent) => {
const data = message.data as resizeEvent
if (data.id !== id) {
return
}
setFrameHeight(data.size)
},
[id]
)
useEffect(() => {
window.addEventListener('message', sizeMessage)
@ -65,12 +68,13 @@ export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
return (
<iframe
sandbox="allow-scripts allow-top-navigation-by-user-activation allow-popups"
data-cy={ 'gh-gist' }
sandbox='allow-scripts allow-top-navigation-by-user-activation allow-popups'
data-cy={'gh-gist'}
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

@ -28,11 +28,11 @@ export class GistReplacer extends ComponentReplacer {
const gistId = attributes.id
return (
<OneClickEmbedding
previewContainerClassName={ 'gist-frame' }
loadingImageUrl={ preview }
hoverIcon={ 'github' }
tooltip={ 'click to load gist' }>
<GistFrame id={ gistId }/>
previewContainerClassName={'gist-frame'}
loadingImageUrl={preview}
hoverIcon={'github'}
tooltip={'click to load gist'}>
<GistFrame id={gistId} />
</OneClickEmbedding>
)
}

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

@ -23,8 +23,7 @@ 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())
}, [])
const frontendBaseUrl = useFrontendBaseUrl()
@ -35,20 +34,21 @@ export const GraphvizFrame: React.FC<GraphvizFrameProps> = ({ code }) => {
}
const actualContainer = container.current
import(/* webpackChunkName: "d3-graphviz" */'@hpcc-js/wasm')
import(/* webpackChunkName: "d3-graphviz" */ '@hpcc-js/wasm')
.then((wasmPlugin) => {
wasmPlugin.wasmFolder(`${ frontendBaseUrl }/static/js`)
wasmPlugin.wasmFolder(`${frontendBaseUrl}/static/js`)
})
.then(() => import(/* webpackChunkName: "d3-graphviz" */ 'd3-graphviz'))
.then((graphvizImport) => {
try {
setError(undefined)
graphvizImport.graphviz(actualContainer, {
useWorker: false,
zoom: false
})
.onerror(showError)
.renderDot(code)
graphvizImport
.graphviz(actualContainer, {
useWorker: false,
zoom: false
})
.onerror(showError)
.renderDot(code)
} catch (error) {
showError(error)
}
@ -60,10 +60,10 @@ export const GraphvizFrame: React.FC<GraphvizFrameProps> = ({ code }) => {
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' } data-cy={ 'graphviz' } ref={ container }/>
<div className={'text-center overflow-x-auto'} data-cy={'graphviz'} ref={container} />
</Fragment>
)
}

View file

@ -11,12 +11,19 @@ import { GraphvizFrame } from './graphviz-frame'
export class GraphvizReplacer implements ComponentReplacer {
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]) {
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

@ -11,8 +11,8 @@ import '../../../utils/button-inside.scss'
import './highlighted-code.scss'
export interface HighlightedCodeProps {
code: string,
language?: string,
code: string
language?: string
startLineNumber?: number
wrapLines: boolean
}
@ -30,47 +30,46 @@ const escapeHtml = (unsafe: string): string => {
}
const replaceCode = (code: string): ReactElement[][] => {
return code.split('\n')
.filter(line => !!line)
.map(line => ReactHtmlParser(line))
return code
.split('\n')
.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)
const unreplacedCode = !!language && languageSupported(language) ? hljs.default.highlight(code, { language }).value : escapeHtml(code)
const replacedDom = replaceCode(unreplacedCode)
.map((line, index) => (
<Fragment key={ index }>
<span className={ 'linenumber' }>
{ (startLineNumber || 1) + index }
</span>
<div className={ 'codeline' }>
{ line }
</div>
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(code, { language }).value
: escapeHtml(code)
const replacedDom = replaceCode(unreplacedCode).map((line, index) => (
<Fragment key={index}>
<span className={'linenumber'}>{(startLineNumber || 1) + index}</span>
<div className={'codeline'}>{line}</div>
</Fragment>
))
setDom(replacedDom)
})
.catch(() => {
console.error('error while loading highlight.js')
})
setDom(replacedDom)
})
.catch(() => {
console.error('error while loading highlight.js')
})
}, [code, language, startLineNumber])
return (
<Fragment>
<code
className={ `hljs ${ startLineNumber !== undefined ? 'showGutter' : '' } ${ wrapLines ? 'wrapLines' : '' }` }>
{ dom }
<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>)
</Fragment>
)
}
export default HighlightedCode

View file

@ -13,7 +13,13 @@ export class HighlightedCodeReplacer extends ComponentReplacer {
private lastLineNumber = 0
public getReplacement(codeNode: DomElement): React.ReactElement | undefined {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || !codeNode.children || !codeNode.children[0]) {
if (
codeNode.name !== 'code' ||
!codeNode.attribs ||
!codeNode.attribs['data-highlight-language'] ||
!codeNode.children ||
!codeNode.children[0]
) {
return
}
@ -31,18 +37,21 @@ export class HighlightedCodeReplacer extends ComponentReplacer {
wrapLines = extraInfos[3] === '!'
}
const startLineNumber = startLineNumberAttribute === '+' ? this.lastLineNumber : (parseInt(startLineNumberAttribute) || 1)
const startLineNumber =
startLineNumberAttribute === '+' ? this.lastLineNumber : parseInt(startLineNumberAttribute) || 1
const code = codeNode.children[0].data as string
if (showLineNumbers) {
this.lastLineNumber = startLineNumber + code.split('\n')
.filter(line => !!line).length
this.lastLineNumber = startLineNumber + code.split('\n').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

@ -21,21 +21,20 @@ 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

@ -9,7 +9,7 @@ import React from 'react'
import { ComponentReplacer } from '../ComponentReplacer'
import { ProxyImageFrame } from './proxy-image-frame'
export type ImageClickHandler = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => void;
export type ImageClickHandler = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => void
export class ImageReplacer extends ComponentReplacer {
private readonly clickHandler?: ImageClickHandler
@ -21,16 +21,18 @@ export class ImageReplacer extends ComponentReplacer {
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 }
/>
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}
/>
)
}
}
}

View file

@ -9,13 +9,7 @@ import { useSelector } from 'react-redux'
import { getProxiedUrl } from '../../../../api/media'
import { ApplicationState } from '../../../../redux'
export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = (
{
src,
title,
alt,
...props
}) => {
export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = ({ src, title, alt, ...props }) => {
const [imageUrl, setImageUrl] = useState('')
const imageProxyEnabled = useSelector((state: ApplicationState) => state.config.useImageProxy)
@ -24,10 +18,9 @@ export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>
return
}
getProxiedUrl(src)
.then(proxyResponse => setImageUrl(proxyResponse.src))
.catch(err => console.error(err))
.then((proxyResponse) => setImageUrl(proxyResponse.src))
.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

@ -11,17 +11,17 @@ import React from 'react'
import { ComponentReplacer } from '../ComponentReplacer'
import './katex.scss'
const getNodeIfKatexBlock = (node: DomElement): (DomElement | undefined) => {
const getNodeIfKatexBlock = (node: DomElement): DomElement | undefined => {
if (node.name !== 'p' || !node.children || node.children.length === 0) {
return
}
return node.children.find((subnode) => {
return (subnode.name === 'app-katex' && subnode.attribs?.inline === undefined)
return subnode.name === 'app-katex' && subnode.attribs?.inline === undefined
})
}
const getNodeIfInlineKatex = (node: DomElement): (DomElement | undefined) => {
return (node.name === 'app-katex' && node.attribs?.inline !== undefined) ? node : undefined
const getNodeIfInlineKatex = (node: DomElement): DomElement | undefined => {
return node.name === 'app-katex' && node.attribs?.inline !== undefined ? node : undefined
}
const KaTeX = React.lazy(() => import(/* webpackChunkName: "katex" */ '@matejmazur/react-katex'))
@ -40,8 +40,8 @@ export class KatexReplacer extends ComponentReplacer {
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' }/>
const isInline = katex.attribs?.inline !== undefined
return <KaTeX block={!isInline} math={mathJaxContent} errorColor={'#cc0000'} />
}
}
}

View file

@ -12,67 +12,74 @@ export interface LineMarkers {
endLine: number
}
export type LineNumberMarkerOptions = (lineMarkers: LineMarkers[]) => void;
export type LineNumberMarkerOptions = (lineMarkers: LineMarkers[]) => void
/**
* This plugin adds markers to the dom, that are used to map line numbers to dom elements.
* It also provides a list of line numbers for the top level dom elements.
*/
export const lineNumberMarker: (options: LineNumberMarkerOptions) => MarkdownIt.PluginSimple = (options) => (md: MarkdownIt) => {
// add app_linemarker token before each opening or self-closing level-0 tag
md.core.ruler.push('line_number_marker', (state) => {
const lineMarkers: LineMarkers[] = []
tagTokens(state.tokens, lineMarkers)
if (options) {
options(lineMarkers)
export const lineNumberMarker: (options: LineNumberMarkerOptions) => MarkdownIt.PluginSimple =
(options) => (md: MarkdownIt) => {
// add app_linemarker token before each opening or self-closing level-0 tag
md.core.ruler.push('line_number_marker', (state) => {
const lineMarkers: LineMarkers[] = []
tagTokens(state.tokens, lineMarkers)
if (options) {
options(lineMarkers)
}
return true
})
md.renderer.rules.app_linemarker = (tokens: Token[], index: number): string => {
const startLineNumber = tokens[index].attrGet('data-start-line')
const endLineNumber = tokens[index].attrGet('data-end-line')
if (!startLineNumber || !endLineNumber) {
// don't render broken linemarkers without a linenumber
return ''
}
// noinspection CheckTagEmptyBody
return `<app-linemarker data-start-line='${startLineNumber}' data-end-line='${endLineNumber}'></app-linemarker>`
}
return true
})
md.renderer.rules.app_linemarker = (tokens: Token[], index: number): string => {
const startLineNumber = tokens[index].attrGet('data-start-line')
const endLineNumber = tokens[index].attrGet('data-end-line')
if (!startLineNumber || !endLineNumber) {
// don't render broken linemarkers without a linenumber
return ''
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}`])
tokens.splice(tokenPosition, 0, startToken)
}
// noinspection CheckTagEmptyBody
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 }`])
tokens.splice(tokenPosition, 0, startToken)
}
const tagTokens = (tokens: Token[], lineMarkers: LineMarkers[]) => {
for (let tokenPosition = 0; tokenPosition < tokens.length; tokenPosition++) {
const token = tokens[tokenPosition]
if (token.hidden) {
continue
}
const tagTokens = (tokens: Token[], lineMarkers: LineMarkers[]) => {
for (let tokenPosition = 0; tokenPosition < tokens.length; tokenPosition++) {
const token = tokens[tokenPosition]
if (token.hidden) {
continue
}
if (!token.map) {
continue
}
if (!token.map) {
continue
}
const startLineNumber = token.map[0] + 1
const endLineNumber = token.map[1] + 1
const startLineNumber = token.map[0] + 1
const endLineNumber = token.map[1] + 1
if (token.level === 0) {
lineMarkers.push({ startLine: startLineNumber, endLine: endLineNumber })
}
if (token.level === 0) {
lineMarkers.push({ startLine: startLineNumber, endLine: endLineNumber })
}
insertNewLineMarker(startLineNumber, endLineNumber, tokenPosition, token.level, tokens)
tokenPosition += 1
insertNewLineMarker(startLineNumber, endLineNumber, tokenPosition, token.level, tokens)
tokenPosition += 1
if (token.children) {
tagTokens(token.children, lineMarkers)
if (token.children) {
tagTokens(token.children, lineMarkers)
}
}
}
}
}

View file

@ -9,8 +9,7 @@ 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()
}
}
@ -20,7 +19,11 @@ export class LinkReplacer extends ComponentReplacer {
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
}
@ -29,7 +32,7 @@ export class LinkReplacer extends ComponentReplacer {
// eslint-disable-next-line no-script-url
if (url.startsWith('data:') || url.startsWith('javascript:')) {
return <span>{ node.attribs.href }</span>
return <span>{node.attribs.href}</span>
}
const isJumpMark = url.substr(0, 1) === '#'
@ -43,9 +46,7 @@ export class LinkReplacer extends ComponentReplacer {
}
if (isJumpMark) {
return <span onClick={ createJumpToMarkClickEventHandler(id) }>
{ nativeRenderer() }
</span>
return <span onClick={createJumpToMarkClickEventHandler(id)}>{nativeRenderer()}</span>
} else {
node.attribs.rel = 'noreferer noopener'
node.attribs.target = '_blank'

View file

@ -45,13 +45,12 @@ export const MarkmapFrame: React.FC<MarkmapFrameProps> = ({ code }) => {
return
}
const actualContainer = diagramContainer.current
import(/* webpackChunkName: "markmap" */'./markmap-loader')
import(/* webpackChunkName: "markmap" */ './markmap-loader')
.then(({ markmapLoader }) => {
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) {
@ -64,11 +63,14 @@ export const MarkmapFrame: React.FC<MarkmapFrameProps> = ({ code }) => {
}, [code])
return (
<div data-cy={ 'markmap' }>
<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 data-cy={'markmap'}>
<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>
</div>
)

View file

@ -11,12 +11,19 @@ import { MarkmapFrame } from './markmap-frame'
export class MarkmapReplacer implements ComponentReplacer {
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]) {
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

@ -27,7 +27,7 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
useEffect(() => {
if (!mermaidInitialized) {
import(/* webpackChunkName: "mermaid" */'mermaid')
import(/* webpackChunkName: "mermaid" */ 'mermaid')
.then((mermaid) => {
mermaid.default.initialize({ startOnLoad: false })
mermaidInitialized = true
@ -38,21 +38,23 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
}
}, [])
const showError = useCallback((error: string) => {
setError(error)
console.error(error)
if (!diagramContainer.current) {
return
}
diagramContainer.current.querySelectorAll('svg')
.forEach(child => child.remove())
}, [setError])
const showError = useCallback(
(error: string) => {
setError(error)
console.error(error)
if (!diagramContainer.current) {
return
}
diagramContainer.current.querySelectorAll('svg').forEach((child) => child.remove())
},
[setError]
)
useEffect(() => {
if (!diagramContainer.current) {
return
}
import(/* webpackChunkName: "mermaid" */'mermaid')
import(/* webpackChunkName: "mermaid" */ 'mermaid')
.then((mermaid) => {
try {
if (!diagramContainer.current) {
@ -71,10 +73,12 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
.catch(() => showError('Error while loading mermaid'))
}, [code, showError, t])
return <Fragment>
<ShowIf condition={ !!error }>
<Alert variant={ 'warning' }>{ error }</Alert>
</ShowIf>
<div className={ 'text-center mermaid text-black' } ref={ diagramContainer }/>
</Fragment>
return (
<Fragment>
<ShowIf condition={!!error}>
<Alert variant={'warning'}>{error}</Alert>
</ShowIf>
<div className={'text-center mermaid text-black'} ref={diagramContainer} />
</Fragment>
)
}

View file

@ -11,12 +11,19 @@ import { MermaidChart } from './mermaid-chart'
export class MermaidReplacer implements ComponentReplacer {
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]) {
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

@ -22,7 +22,17 @@ interface OneClickFrameProps {
onActivate?: () => void
}
export const OneClickEmbedding: React.FC<OneClickFrameProps> = ({ previewContainerClassName, containerClassName, onImageFetch, loadingImageUrl, children, tooltip, hoverIcon, hoverTextI18nKey, onActivate }) => {
export const OneClickEmbedding: React.FC<OneClickFrameProps> = ({
previewContainerClassName,
containerClassName,
onImageFetch,
loadingImageUrl,
children,
tooltip,
hoverIcon,
hoverTextI18nKey,
onActivate
}) => {
const [showFrame, setShowFrame] = useState(false)
const [previewImageUrl, setPreviewImageUrl] = useState(loadingImageUrl)
@ -47,22 +57,24 @@ export const OneClickEmbedding: React.FC<OneClickFrameProps> = ({ previewContain
}, [onImageFetch])
return (
<span className={ containerClassName }>
<ShowIf condition={ showFrame }>
{ children }
</ShowIf>
<ShowIf condition={ !showFrame }>
<span className={ `one-click-embedding ${ previewContainerClassName || '' }` } onClick={ showChildren }>
<ShowIf condition={ !!previewImageUrl }>
<ProxyImageFrame className={ 'one-click-embedding-preview' } src={ previewImageUrl } alt={ tooltip || '' }
title={ tooltip || '' }/>
<span className={containerClassName}>
<ShowIf condition={showFrame}>{children}</ShowIf>
<ShowIf condition={!showFrame}>
<span className={`one-click-embedding ${previewContainerClassName || ''}`} onClick={showChildren}>
<ShowIf condition={!!previewImageUrl}>
<ProxyImageFrame
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

@ -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 }/>
<br />
<TranslatedExternalLink i18nKey={'common.readForMoreInfo'} className={'text-primary'} href={links.faq} />
</Alert>
)
}

View file

@ -12,15 +12,24 @@ import { DeprecationWarning } from './deprecation-warning'
export class SequenceDiagramReplacer implements ComponentReplacer {
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]) {
if (
codeNode.name !== 'code' ||
!codeNode.attribs ||
!codeNode.attribs['data-highlight-language'] ||
codeNode.attribs['data-highlight-language'] !== 'sequence' ||
!codeNode.children ||
!codeNode.children[0]
) {
return
}
const code = codeNode.children[0].data as string
return <Fragment>
<DeprecationWarning/>
<MermaidChart code={ 'sequenceDiagram\n' + code }/>
</Fragment>
return (
<Fragment>
<DeprecationWarning />
<MermaidChart code={'sequenceDiagram\n' + code} />
</Fragment>
)
}
}

View file

@ -25,19 +25,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 }
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'] }
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']}
/>
)
}

View file

@ -6,8 +6,11 @@
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

@ -39,20 +39,21 @@ export const VegaChart: React.FC<VegaChartProps> = ({ code }) => {
}
const spec = JSON.parse(code) as VisualizationSpec
embed.default(diagramContainer.current, spec, {
actions: {
export: true,
source: false,
compiled: false,
editor: false
},
i18n: {
PNG_ACTION: t('renderer.vega-lite.png'),
SVG_ACTION: t('renderer.vega-lite.svg')
}
})
.then(() => setError(undefined))
.catch((error: Error) => showError(error.message))
embed
.default(diagramContainer.current, spec, {
actions: {
export: true,
source: false,
compiled: false,
editor: false
},
i18n: {
PNG_ACTION: t('renderer.vega-lite.png'),
SVG_ACTION: t('renderer.vega-lite.svg')
}
})
.then(() => setError(undefined))
.catch((error: Error) => showError(error.message))
} catch (error) {
showError(t('renderer.vega-lite.errorJson'))
}
@ -62,12 +63,14 @@ export const VegaChart: React.FC<VegaChartProps> = ({ code }) => {
})
}, [code, showError, t])
return <Fragment>
<ShowIf condition={ !!error }>
<Alert variant={ 'danger' }>{ error }</Alert>
</ShowIf>
<div className={ 'text-center' }>
<div ref={ diagramContainer }/>
</div>
</Fragment>
return (
<Fragment>
<ShowIf condition={!!error}>
<Alert variant={'danger'}>{error}</Alert>
</ShowIf>
<div className={'text-center'}>
<div ref={diagramContainer} />
</div>
</Fragment>
)
}

View file

@ -11,12 +11,19 @@ import { VegaChart } from './vega-chart'
export class VegaReplacer implements ComponentReplacer {
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]) {
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

@ -10,7 +10,9 @@ const protocolRegex = /(?:http(?:s)?:\/\/)?/
const domainRegex = /(?:player\.)?(?:vimeo\.com\/)(?:(?:channels|album|ondemand|groups)\/\w+\/)?(?:video\/)?/
const idRegex = /([\d]{6,11})/
const tailRegex = /(?:[?#].*)?/
const vimeoVideoUrlRegex = new RegExp(`(?:${protocolRegex.source}${domainRegex.source}${idRegex.source}${tailRegex.source})`)
const vimeoVideoUrlRegex = new RegExp(
`(?:${protocolRegex.source}${domainRegex.source}${idRegex.source}${tailRegex.source})`
)
const linkRegex = new RegExp(`^${vimeoVideoUrlRegex.source}$`, 'i')
export const replaceVimeoLink: RegexOptions = {
@ -19,6 +21,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

@ -19,14 +19,14 @@ 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'
})
if (response.status !== 200) {
throw new Error('Error while loading data from vimeo api')
}
const vimeoResponse: VimeoApiResponse[] = await response.json() as VimeoApiResponse[]
const vimeoResponse: VimeoApiResponse[] = (await response.json()) as VimeoApiResponse[]
if (vimeoResponse[0] && vimeoResponse[0].thumbnail_large) {
return vimeoResponse[0].thumbnail_large
@ -36,13 +36,18 @@ 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

@ -24,7 +24,7 @@ export class VimeoReplacer extends ComponentReplacer {
const attributes = getAttributesFromHedgeDocTag(node, 'vimeo')
if (attributes && attributes.id) {
const videoId = attributes.id
return <VimeoFrame id={ videoId }/>
return <VimeoFrame id={videoId} />
}
}
}

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

@ -11,7 +11,9 @@ const subdomainRegex = /(?:www.)?/
const pathRegex = /(?:youtube(?:-nocookie)?\.com\/(?:[^\\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)/
const idRegex = /([^"&?\\/\s]{11})/
const tailRegex = /(?:[?&#].*)?/
const youtubeVideoUrlRegex = new RegExp(`(?:${protocolRegex.source}${subdomainRegex.source}${pathRegex.source}${idRegex.source}${tailRegex.source})`)
const youtubeVideoUrlRegex = new RegExp(
`(?:${protocolRegex.source}${subdomainRegex.source}${pathRegex.source}${idRegex.source}${tailRegex.source})`
)
const linkRegex = new RegExp(`^${youtubeVideoUrlRegex.source}$`, 'i')
export const replaceYouTubeLink: RegexOptions = {
@ -20,6 +22,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

@ -13,12 +13,17 @@ 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={ `https://i.ytimg.com/vi/${ id }/maxresdefault.jpg` }>
<iframe className='embed-responsive-item' title={ `youtube video of ${ id }` }
src={ `https://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={`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`}>
<iframe
className='embed-responsive-item'
title={`youtube video of ${id}`}
src={`https://www.youtube-nocookie.com/embed/${id}?autoplay=1`}
allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
/>
</OneClickEmbedding>
)
}

View file

@ -24,7 +24,7 @@ export class YoutubeReplacer extends ComponentReplacer {
const attributes = getAttributesFromHedgeDocTag(node, 'youtube')
if (attributes && attributes.id) {
const videoId = attributes.id
return <YouTubeFrame id={ videoId }/>
return <YouTubeFrame id={videoId} />
}
}
}