hedgedoc/src/components/markdown-renderer/full-markdown-renderer.tsx
Tilman Vatteroth 7f6e0e53a7
Add custom intro page by fetching markdown content from a file (#697)
Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
2021-02-08 15:03:11 +01:00

106 lines
4.1 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { TocAst } from 'markdown-it-toc-done-right'
import React, { Ref, useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { NoteFrontmatter, RawNoteFrontmatter } from '../editor-page/note-frontmatter/note-frontmatter'
import { BasicMarkdownRenderer } from './basic-markdown-renderer'
import { useExtractFirstHeadline } from './hooks/use-extract-first-headline'
import { usePostFrontmatterOnChange } from './hooks/use-post-frontmatter-on-change'
import { usePostTocAstOnChange } from './hooks/use-post-toc-ast-on-change'
import { useReplacerInstanceListCreator } from './hooks/use-replacer-instance-list-creator'
import { InvalidYamlAlert } from './invalid-yaml-alert'
import { FullMarkdownItConfigurator } from './markdown-it-configurator/FullMarkdownItConfigurator'
import { ImageClickHandler } from './replace-components/image/image-replacer'
import { LineMarkers } from './replace-components/linemarker/line-number-marker'
import { AdditionalMarkdownRendererProps, LineMarkerPosition } from './types'
import { useCalculateLineMarkerPosition } from './utils/calculate-line-marker-positions'
export interface FullMarkdownRendererProps {
onFirstHeadingChange?: (firstHeading: string | undefined) => void
onLineMarkerPositionChanged?: (lineMarkerPosition: LineMarkerPosition[]) => void
onFrontmatterChange?: (frontmatter: NoteFrontmatter | undefined) => void
onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void
onTocChange?: (ast: TocAst) => void
rendererRef?: Ref<HTMLDivElement>
baseUrl?: string
onImageClick?: ImageClickHandler
}
export const FullMarkdownRenderer: React.FC<FullMarkdownRendererProps & AdditionalMarkdownRendererProps> = (
{
onFirstHeadingChange,
onLineMarkerPositionChanged,
onFrontmatterChange,
onTaskCheckedChange,
onTocChange,
content,
className,
rendererRef,
baseUrl,
onImageClick
}) => {
const allReplacers = useReplacerInstanceListCreator(onTaskCheckedChange, onImageClick, baseUrl)
useTranslation()
const [showYamlError, setShowYamlError] = useState(false)
const hasNewYamlError = useRef(false)
const rawMetaRef = useRef<RawNoteFrontmatter>()
const firstHeadingRef = useRef<string>()
const documentElement = useRef<HTMLDivElement>(null)
const currentLineMarkers = useRef<LineMarkers[]>()
usePostFrontmatterOnChange(rawMetaRef.current, firstHeadingRef.current, onFrontmatterChange, onFirstHeadingChange)
useCalculateLineMarkerPosition(documentElement, currentLineMarkers.current, onLineMarkerPositionChanged, documentElement.current?.offsetTop ?? 0)
useExtractFirstHeadline(documentElement, content, onFirstHeadingChange)
const tocAst = useRef<TocAst>()
usePostTocAstOnChange(tocAst, onTocChange)
const markdownIt = useMemo(() => {
return (new FullMarkdownItConfigurator(
!!onFrontmatterChange,
errorState => hasNewYamlError.current = errorState,
rawMeta => {
rawMetaRef.current = rawMeta
},
toc => {
tocAst.current = toc
},
onLineMarkerPositionChanged === undefined
? undefined
: lineMarkers => {
currentLineMarkers.current = lineMarkers
}
)).buildConfiguredMarkdownIt()
}, [onLineMarkerPositionChanged, onFrontmatterChange])
const clearFrontmatter = useCallback(() => {
hasNewYamlError.current = false
rawMetaRef.current = undefined
}, [])
const checkYamlErrorState = useCallback(() => {
if (hasNewYamlError.current !== showYamlError) {
setShowYamlError(hasNewYamlError.current)
}
}, [setShowYamlError, showYamlError])
return (
<div ref={ rendererRef } className={ 'position-relative' }>
<InvalidYamlAlert showYamlError={ showYamlError }/>
<BasicMarkdownRenderer
className={ className }
content={ content }
componentReplacers={ allReplacers }
markdownIt={ markdownIt }
documentReference={ documentElement }
onBeforeRendering={ clearFrontmatter }
onAfterRendering={ checkYamlErrorState }/>
</div>
)
}