Switch the base framework from Create React App to Next.JS

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Renovate Bot 2021-12-25 15:44:24 +00:00 committed by Tilman Vatteroth
parent a979b6ffdd
commit 77a60c6c48
361 changed files with 5130 additions and 9605 deletions

View file

@ -5,7 +5,7 @@
*/
import React, { useEffect, useRef } from 'react'
import './abc.scss'
import styles from './abc.module.scss'
import { Logger } from '../../../../utils/logger'
import type { CodeProps } from '../../replace-components/code-block-component-replacer'
import { cypressId } from '../../../../utils/cypress-attribute'
@ -29,5 +29,11 @@ export const AbcFrame: React.FC<CodeProps> = ({ code }) => {
})
}, [code])
return <div ref={container} className={'abcjs-score bg-white text-black svg-container'} {...cypressId('abcjs')} />
return (
<div
ref={container}
className={`${styles['abcjs-score']} bg-white text-black svg-container`}
{...cypressId('abcjs')}
/>
)
}

View file

@ -1,4 +1,4 @@
/*!
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
@ -6,9 +6,9 @@
.abcjs-score {
@import "../../../../style/variables";
@import "../../../../../global-styles/variables";
.markdown-body & {
:global(.markdown-body) & {
overflow-x: auto !important;
}

View file

@ -1,7 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NativeRenderer, NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import type { NodeReplacement } from '../../replace-components/component-replacer'
import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import type { Element } from 'domhandler'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { isText } from 'domhandler'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { cssColor } from './blockquote-border-color-node-preprocessor'
import Optional from 'optional-js'
import type { Text } from 'domhandler/lib/node'
@ -21,18 +21,19 @@ import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-mark
* @see BlockquoteTagMarkdownItPlugin
*/
export class BlockquoteColorExtraTagReplacer extends ComponentReplacer {
replace(element: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
replace(element: Element): NodeReplacement {
if (
element.tagName === BlockquoteExtraTagMarkdownExtension.tagName &&
element.attribs?.['data-label'] === 'color' &&
element.children !== undefined
) {
let index = 0
return Optional.ofNullable(element.children[0])
.filter(isText)
.map((child) => (child as Text).data)
.filter((content) => cssColor.test(content))
.map<NodeReplacement>((color) => (
<span className={'blockquote-extra'} style={{ color: color }}>
<span className={'blockquote-extra'} key={(index += 1)} style={{ color: color }}>
<ForkAwesomeIcon key='icon' className={'mx-1'} icon={'tag'} />
</span>
))

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NativeRenderer, NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import type { NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import type { Element } from 'domhandler'
import type { ForkAwesomeIconProps } from '../../../common/fork-awesome/fork-awesome-icon'
@ -22,7 +22,7 @@ import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-mark
* @see ColoredBlockquoteNodePreprocessor
*/
export class BlockquoteExtraTagReplacer extends ComponentReplacer {
replace(element: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
replace(element: Element, subNodeTransform: SubNodeTransform): NodeReplacement {
if (element.tagName !== BlockquoteExtraTagMarkdownExtension.tagName || !element.attribs) {
return DO_NOT_REPLACE
}

View file

@ -1,7 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useMemo } from 'react'

View file

@ -1,12 +1,12 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback } from 'react'
import { cypressId } from '../../../../utils/cypress-attribute'
import './gist-frame.scss'
import styles from './gist-frame.module.scss'
import { useResizeGistFrame } from './use-resize-gist-frame'
import type { IdProps } from '../../replace-components/custom-tag-with-id-component-replacer'
import { ClickShield } from '../../replace-components/click-shield/click-shield'
@ -41,8 +41,8 @@ export const GistFrame: React.FC<IdProps> = ({ id }) => {
title={`gist ${id}`}
src={`https://gist.github.com/${id}.pibb`}
/>
<span className={'gist-resizer-row'}>
<span className={'gist-resizer'} onMouseDown={onStart} onTouchStart={onStart} />
<span className={styles['gist-resizer-row']}>
<span className={styles['gist-resizer']} onMouseDown={onStart} onTouchStart={onStart} />
</span>
</ClickShield>
)

View file

@ -7,10 +7,10 @@
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap'
import { ShowIf } from '../../../common/show-if/show-if'
import { useFrontendBaseUrl } from '../../../../hooks/common/use-frontend-base-url'
import { Logger } from '../../../../utils/logger'
import { cypressId } from '../../../../utils/cypress-attribute'
import type { CodeProps } from '../../replace-components/code-block-component-replacer'
import { useRouter } from 'next/router'
const log = new Logger('GraphvizFrame')
@ -27,7 +27,7 @@ export const GraphvizFrame: React.FC<CodeProps> = ({ code }) => {
container.current.querySelectorAll('svg').forEach((child) => child.remove())
}, [])
const frontendBaseUrl = useFrontendBaseUrl()
const { basePath } = useRouter()
useEffect(() => {
if (!container.current) {
@ -37,7 +37,7 @@ export const GraphvizFrame: React.FC<CodeProps> = ({ code }) => {
import(/* webpackChunkName: "d3-graphviz" */ '@hpcc-js/wasm')
.then((wasmPlugin) => {
wasmPlugin.wasmFolder(`${frontendBaseUrl}/static/js`)
wasmPlugin.wasmFolder(`${basePath}/_next/static/js`)
})
.then(() => import(/* webpackChunkName: "d3-graphviz" */ 'd3-graphviz'))
.then((graphvizImport) => {
@ -57,7 +57,7 @@ export const GraphvizFrame: React.FC<CodeProps> = ({ code }) => {
.catch((error: Error) => {
log.error('Error while loading graphviz', error)
})
}, [code, error, frontendBaseUrl, showError])
}, [code, error, basePath, showError])
return (
<Fragment>

View file

@ -57,4 +57,8 @@ export class HighlightedCodeReplacer extends ComponentReplacer {
/>
)
}
reset() {
this.lastLineNumber = 0
}
}

View file

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.code-highlighter {
position: relative;
:global(code.hljs) {
overflow-x: auto;
background-color: rgba(27, 31, 35, .05);
padding: 16px;
display: grid !important;
grid-template-columns: auto minmax(0, 1fr);
:global(body.dark) & {
background-color: rgb(27, 31, 35);
}
.codeline {
grid-column: 2;
white-space: pre;
}
.linenumber {
grid-column: 1;
position: relative;
cursor: default;
z-index: 4;
padding: 0 8px 0 0;
min-width: 20px;
box-sizing: content-box;
color: #afafaf;
border-right: 3px solid #6ce26c;
flex-direction: column;
overflow: hidden;
user-select: none;
align-items: flex-end;
display: none;
}
&.showGutter {
.linenumber {
display: flex;
}
.codeline {
margin: 0 0 0 16px;
}
}
&.wrapLines .codeline {
white-space: pre-wrap;
}
}
}

View file

@ -1,66 +0,0 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.code-highlighter {
@import '../../../../../node_modules/highlight.js/styles/github';
body.dark & {
@import '../../../../../node_modules/highlight.js/styles/github-dark';
}
position: relative;
code.hljs {
overflow-x: auto;
background-color: rgba(27, 31, 35, .05);
body.dark & {
background-color: rgb(27, 31, 35);
}
body.dark &, & {
padding: 16px;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
.codeline {
grid-column: 2;
white-space: pre;
}
.linenumber {
grid-column: 1;
position: relative;
cursor: default;
z-index: 4;
padding: 0 8px 0 0;
min-width: 20px;
box-sizing: content-box;
color: #afafaf;
border-right: 3px solid #6ce26c;
flex-direction: column;
overflow: hidden;
user-select: none;
align-items: flex-end;
display: none;
}
&.showGutter {
.linenumber {
display: flex;
}
.codeline {
margin: 0 0 0 16px;
}
}
&.wrapLines .codeline {
white-space: pre-wrap;
}
}
}
}

View file

@ -8,10 +8,9 @@ import type { ReactElement } from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import convertHtmlToReact from '@hedgedoc/html-to-react'
import { CopyToClipboardButton } from '../../../common/copyable/copy-to-clipboard-button/copy-to-clipboard-button'
import '../../utils/button-inside.scss'
import './highlighted-code.scss'
import styles from './highlighted-code.module.scss'
import { Logger } from '../../../../utils/logger'
import { cypressId } from '../../../../utils/cypress-attribute'
import { cypressAttribute, cypressId } from '../../../../utils/cypress-attribute'
const log = new Logger('HighlightedCode')
@ -54,8 +53,12 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
: escapeHtml(code)
const replacedDom = replaceCode(unreplacedCode).map((line, index) => (
<Fragment key={index}>
<span className={'linenumber'}>{(startLineNumber || 1) + index}</span>
<div className={'codeline'}>{line}</div>
<span {...cypressId('linenumber')} className={styles['linenumber']}>
{(startLineNumber || 1) + index}
</span>
<div {...cypressId('codeline')} className={styles['codeline']}>
{line}
</div>
</Fragment>
))
setDom(replacedDom)
@ -65,9 +68,15 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
})
}, [code, language, startLineNumber])
const showGutter = startLineNumber !== undefined
return (
<div className={'code-highlighter'} {...cypressId('highlighted-code-block')}>
<code className={`hljs ${startLineNumber !== undefined ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
<div className={styles['code-highlighter']} {...cypressId('highlighted-code-block')}>
<code
{...cypressId('code-highlighter')}
{...cypressAttribute('showgutter', showGutter ? 'true' : 'false')}
{...cypressAttribute('wraplines', wrapLines ? 'true' : 'false')}
className={`hljs ${showGutter ? styles['showGutter'] : ''} ${wrapLines ? styles['wrapLines'] : ''}`}>
{dom}
</code>
<div className={'text-right button-inside'}>

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NativeRenderer, NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import type { NodeReplacement } from '../../replace-components/component-replacer'
import { ComponentReplacer } from '../../replace-components/component-replacer'
import type { Element } from 'domhandler'
import { ImagePlaceholder } from './image-placeholder'
@ -24,7 +24,7 @@ export class ImagePlaceholderReplacer extends ComponentReplacer {
this.countPerSourceLine = new Map<number, number>()
}
replace(node: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
replace(node: Element): NodeReplacement {
if (node.name === 'img' && node.attribs && node.attribs.src === ImagePlaceholderMarkdownExtension.PLACEHOLDER_URL) {
const lineIndex = Number(node.attribs['data-line'])
const indexInLine = this.countPerSourceLine.get(lineIndex) ?? 0

View file

@ -1,15 +1,15 @@
/*!
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.image-drop {
@import "../../../../style/variables.light.scss";
@import "../../../../../global-styles/variables.light.scss";
border: 3px dashed $dark;
body.dark & {
@import "../../../../style/variables.dark.scss";
:global(body.dark) & {
@import "../../../../../global-styles/variables.dark.scss";
border-color: $dark;
}

View file

@ -8,10 +8,11 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'
import { Button } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import './image-placeholder.scss'
import styles from './image-placeholder.module.scss'
import { acceptedMimeTypes } from '../../../common/upload-image-mimetypes'
import { useOnImageUpload } from './hooks/use-on-image-upload'
import { usePlaceholderSizeStyle } from './hooks/use-placeholder-size-style'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface PlaceholderImageFrameProps {
alt?: string
@ -83,7 +84,8 @@ export const ImagePlaceholder: React.FC<PlaceholderImageFrameProps> = ({
return (
<span
className={`image-drop d-inline-flex flex-column align-items-center ${containerDragClasses} p-1`}
{...cypressId('image-placeholder-image-drop')}
className={`${styles['image-drop']} d-inline-flex flex-column align-items-center ${containerDragClasses} p-1`}
style={containerStyle}
onDrop={onDropHandler}
onDragOver={onDragOverHandler}
@ -100,7 +102,7 @@ export const ImagePlaceholder: React.FC<PlaceholderImageFrameProps> = ({
<span className='my-2'>
<Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} />
</span>
<span className={'altText'}>{alt ?? title ?? ''}</span>
<span className={styles['altText']}>{alt ?? title ?? ''}</span>
</div>
</div>
<Button size={'sm'} variant={'primary'} onClick={uploadButtonClicked}>

View file

@ -5,7 +5,7 @@
*/
import React from 'react'
import './lightbox.scss'
import styles from './lightbox.module.scss'
import { ProxyImageFrame } from './proxy-image-frame'
import type { ModalVisibilityProps } from '../../../common/modals/common-modal'
import { CommonModal } from '../../../common/modals/common-modal'
@ -23,7 +23,7 @@ export const ImageLightboxModal: React.FC<ImageLightboxModalProps> = ({ show, on
show={show && !!src}
onHide={onHide}
showCloseButton={true}
additionalClasses={'lightbox'}
additionalClasses={styles.lightbox}
title={alt ?? title ?? ''}
titleIsI18nKey={false}>
<ProxyImageFrame alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide} />

View file

@ -1,4 +1,4 @@
/*!
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only

View file

@ -24,5 +24,7 @@ export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>
.catch((err) => log.error(err))
}, [imageProxyEnabled, src])
// The next image processor works with a whitelist of origins. Therefore we can't use it for general images.
// eslint-disable-next-line @next/next/no-img-element
return <img src={imageProxyEnabled ? imageUrl : src ?? ''} title={title ?? alt ?? ''} alt={alt} {...props} />
}

View file

@ -8,7 +8,7 @@ import type { Element } from 'domhandler'
import { isTag } from 'domhandler'
import React from 'react'
import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import './katex.scss'
import 'katex/dist/katex.min.css'
import { KatexMarkdownExtension } from './katex-markdown-extension'
/**

View file

@ -1,7 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@import '../../../../../node_modules/katex/dist/katex.min.css';

View file

@ -1,7 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'

View file

@ -9,10 +9,12 @@ import type { NodeProcessor } from '../node-preprocessors/node-processor'
import type { ComponentReplacer } from '../replace-components/component-replacer'
export abstract class MarkdownExtension {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public configureMarkdownIt(markdownIt: MarkdownIt): void {
return
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public configureMarkdownItPost(markdownIt: MarkdownIt): void {
return
}

View file

@ -7,7 +7,6 @@
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { LockButton } from '../../../common/lock-button/lock-button'
import '../../utils/button-inside.scss'
import { Logger } from '../../../../utils/logger'
import { cypressId } from '../../../../utils/cypress-attribute'
import type { CodeProps } from '../../replace-components/code-block-component-replacer'

View file

@ -8,9 +8,10 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react
import { Alert } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ShowIf } from '../../../common/show-if/show-if'
import './mermaid.scss'
import styles from './mermaid.module.scss'
import { Logger } from '../../../../utils/logger'
import type { CodeProps } from '../../replace-components/code-block-component-replacer'
import { cypressId } from '../../../../utils/cypress-attribute'
const log = new Logger('MermaidChart')
@ -78,7 +79,11 @@ export const MermaidChart: React.FC<CodeProps> = ({ code }) => {
<ShowIf condition={!!error}>
<Alert variant={'warning'}>{error}</Alert>
</ShowIf>
<div className={'text-center mermaid text-black'} ref={diagramContainer} />
<div
{...cypressId('mermaid-frame')}
className={`text-center ${styles['mermaid']} text-black`}
ref={diagramContainer}
/>
</Fragment>
)
}

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NativeRenderer, NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import type { NodeReplacement } from '../../replace-components/component-replacer'
import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { PlantumlNotConfiguredAlert } from './plantuml-not-configured-alert'
import type { Element } from 'domhandler'
export class PlantumlNotConfiguredComponentReplacer extends ComponentReplacer {
replace(node: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
replace(node: Element): NodeReplacement {
return node.tagName === 'plantuml-not-configured' ? <PlantumlNotConfiguredAlert /> : DO_NOT_REPLACE
}
}

View file

@ -42,7 +42,7 @@ const processCommentNode = (node: DataNode): void => {
return
}
for (const dataAttribute of regexResult[2].matchAll(dataAttributesSyntax)) {
for (const dataAttribute of [...regexResult[2].matchAll(dataAttributesSyntax)]) {
const attributeName = dataAttribute[1]
const attributeValue = dataAttribute[2] ?? dataAttribute[3]
if (attributeValue) {

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NativeRenderer, NodeReplacement, SubNodeTransform } from '../../replace-components/component-replacer'
import type { NodeReplacement } from '../../replace-components/component-replacer'
import { ComponentReplacer } from '../../replace-components/component-replacer'
import type { Element } from 'domhandler'
import { UploadIndicatingFrame } from './upload-indicating-frame'
@ -15,7 +15,7 @@ const uploadIdRegex = /^upload-(.+)$/
* Replaces an image tag whose url is an upload-id with the {@link UploadIndicatingFrame upload indicating frame}.
*/
export class UploadIndicatingImageFrameReplacer extends ComponentReplacer {
replace(node: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
replace(node: Element): NodeReplacement {
if (node.name === 'img' && uploadIdRegex.test(node.attribs.src)) {
return <UploadIndicatingFrame width={node.attribs.width} height={node.attribs.height} />
}

View file

@ -1,7 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback } from 'react'

View file

@ -1,7 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'