refactor: reorganize props and locations of markdown renderers

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-04-11 17:12:30 +02:00
parent a36d063da4
commit 6b308e58be
No known key found for this signature in database
GPG key ID: 42498463316F048B
13 changed files with 214 additions and 243 deletions

View file

@ -10,16 +10,17 @@ import { isTestMode } from '../../../utils/test-modes'
import { ShowIf } from '../../common/show-if/show-if' import { ShowIf } from '../../common/show-if/show-if'
import { WaitSpinner } from '../../common/wait-spinner/wait-spinner' import { WaitSpinner } from '../../common/wait-spinner/wait-spinner'
import { useExtensionEventEmitter } from '../../markdown-renderer/hooks/use-extension-event-emitter' import { useExtensionEventEmitter } from '../../markdown-renderer/hooks/use-extension-event-emitter'
import type { RendererProps } from '../../render-page/markdown-document' import type { CommonMarkdownRendererProps } from '../../render-page/renderers/common-markdown-renderer-props'
import { useEditorReceiveHandler } from '../../render-page/window-post-message-communicator/hooks/use-editor-receive-handler' import { useEditorReceiveHandler } from '../../render-page/window-post-message-communicator/hooks/use-editor-receive-handler'
import type { import type {
ExtensionEvent, ExtensionEvent,
OnHeightChangeMessage, OnHeightChangeMessage,
RendererType,
SetScrollStateMessage SetScrollStateMessage
} from '../../render-page/window-post-message-communicator/rendering-message' } from '../../render-page/window-post-message-communicator/rendering-message'
import type { RendererType } from '../../render-page/window-post-message-communicator/rendering-message'
import { CommunicationMessageType } from '../../render-page/window-post-message-communicator/rendering-message' import { CommunicationMessageType } from '../../render-page/window-post-message-communicator/rendering-message'
import { useEditorToRendererCommunicator } from '../render-context/editor-to-renderer-communicator-context-provider' import { useEditorToRendererCommunicator } from '../render-context/editor-to-renderer-communicator-context-provider'
import type { ScrollProps } from '../synced-scroll/scroll-props'
import { useEffectOnRenderTypeChange } from './hooks/use-effect-on-render-type-change' import { useEffectOnRenderTypeChange } from './hooks/use-effect-on-render-type-change'
import { useForceRenderPageUrlOnIframeLoadCallback } from './hooks/use-force-render-page-url-on-iframe-load-callback' import { useForceRenderPageUrlOnIframeLoadCallback } from './hooks/use-force-render-page-url-on-iframe-load-callback'
import { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer' import { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer'
@ -27,7 +28,7 @@ import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer
import { useSendScrollState } from './hooks/use-send-scroll-state' import { useSendScrollState } from './hooks/use-send-scroll-state'
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react' import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
export interface RenderIframeProps extends RendererProps { export interface RenderIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> {
rendererType: RendererType rendererType: RendererType
forcedDarkMode?: DarkModePreference forcedDarkMode?: DarkModePreference
frameClasses?: string frameClasses?: string
@ -90,11 +91,6 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
onRendererStatusChange?.(rendererReady) onRendererStatusChange?.(rendererReady)
}, [onRendererStatusChange, rendererReady]) }, [onRendererStatusChange, rendererReady])
useEditorReceiveHandler(
CommunicationMessageType.ENABLE_RENDERER_SCROLL_SOURCE,
useCallback(() => onMakeScrollSource?.(), [onMakeScrollSource])
)
const eventEmitter = useExtensionEventEmitter() const eventEmitter = useExtensionEventEmitter()
useEditorReceiveHandler( useEditorReceiveHandler(
@ -147,12 +143,16 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
useEffectOnRenderTypeChange(rendererType, onIframeLoad) useEffectOnRenderTypeChange(rendererType, onIframeLoad)
useSendAdditionalConfigurationToRenderer(rendererReady, forcedDarkMode) useSendAdditionalConfigurationToRenderer(rendererReady, forcedDarkMode)
useSendMarkdownToRenderer(markdownContentLines, rendererReady) useSendMarkdownToRenderer(markdownContentLines, rendererReady)
useSendScrollState(scrollState, rendererReady)
useSendScrollState(scrollState, rendererReady)
useEditorReceiveHandler( useEditorReceiveHandler(
CommunicationMessageType.SET_SCROLL_STATE, CommunicationMessageType.SET_SCROLL_STATE,
useCallback((values: SetScrollStateMessage) => onScroll?.(values.scrollState), [onScroll]) useCallback((values: SetScrollStateMessage) => onScroll?.(values.scrollState), [onScroll])
) )
useEditorReceiveHandler(
CommunicationMessageType.ENABLE_RENDERER_SCROLL_SOURCE,
useCallback(() => onMakeScrollSource?.(), [onMakeScrollSource])
)
return ( return (
<Fragment> <Fragment>

View file

@ -1,15 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Ref } from 'react'
export interface CommonMarkdownRendererProps {
baseUrl: string
outerContainerRef?: Ref<HTMLDivElement>
newlinesAreBreaks?: boolean
lineOffset?: number
className?: string
markdownContentLines: string[]
}

View file

@ -1,75 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { cypressId } from '../../utils/cypress-attribute'
import type { CommonMarkdownRendererProps } from './common-markdown-renderer-props'
import { HeadlineAnchorsMarkdownExtension } from './extensions/headline-anchors-markdown-extension'
import type { LineMarkers } from './extensions/linemarker/add-line-marker-markdown-it-plugin'
import { LinemarkerMarkdownExtension } from './extensions/linemarker/linemarker-markdown-extension'
import type { LineMarkerPosition } from './extensions/linemarker/types'
import { useCalculateLineMarkerPosition } from './hooks/use-calculate-line-marker-positions'
import { useMarkdownExtensions } from './hooks/use-markdown-extensions'
import { MarkdownToReact } from './markdown-to-react/markdown-to-react'
import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
export interface DocumentMarkdownRendererProps extends CommonMarkdownRendererProps {
onLineMarkerPositionChanged?: (lineMarkerPosition: LineMarkerPosition[]) => void
}
/**
* Renders the note as normal document.
*
* @param className Additional class names directly given to the div
* @param markdownContentLines The markdown lines
* @param onLineMarkerPositionChanged The callback to call with changed {@link LineMarkers}
* @param baseUrl The base url of the renderer
* @param outerContainerRef A reference for the outer container
* @param newlinesAreBreaks If newlines are rendered as breaks or not
*/
export const DocumentMarkdownRenderer: React.FC<DocumentMarkdownRendererProps> = ({
className,
markdownContentLines,
onLineMarkerPositionChanged,
baseUrl,
outerContainerRef,
newlinesAreBreaks
}) => {
const markdownBodyRef = useRef<HTMLDivElement>(null)
const currentLineMarkers = useRef<LineMarkers[]>()
const extensions = useMarkdownExtensions(
baseUrl,
useMemo(
() => [
new HeadlineAnchorsMarkdownExtension(),
new LinemarkerMarkdownExtension((values) => (currentLineMarkers.current = values))
],
[]
)
)
useTranslation()
useCalculateLineMarkerPosition(markdownBodyRef, currentLineMarkers.current, onLineMarkerPositionChanged)
return (
<div ref={outerContainerRef} className={`position-relative`}>
<div
{...cypressId('markdown-body')}
ref={markdownBodyRef}
data-word-count-target={true}
className={`${className ?? ''} markdown-body w-100 d-flex flex-column align-items-center`}>
<MarkdownToReact
markdownContentLines={markdownContentLines}
markdownRenderExtensions={extensions}
newlinesAreBreaks={newlinesAreBreaks}
allowHtml={true}
/>
</div>
</div>
)
}
export default DocumentMarkdownRenderer

View file

@ -1,104 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../hooks/common/use-application-state'
import type { ScrollProps } from '../editor-page/synced-scroll/scroll-props'
import { DocumentMarkdownRenderer } from '../markdown-renderer/document-markdown-renderer'
import { DocumentTocSidebar } from './document-toc-sidebar'
import { useDocumentSyncScrolling } from './hooks/sync-scroll/use-document-sync-scrolling'
import styles from './markdown-document.module.scss'
import useResizeObserver from '@react-hook/resize-observer'
import type { MutableRefObject } from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
export interface RendererProps extends ScrollProps {
documentRenderPaneRef?: MutableRefObject<HTMLDivElement | null>
markdownContentLines: string[]
onHeightChange?: (height: number) => void
}
export interface MarkdownDocumentProps extends RendererProps {
additionalOuterContainerClasses?: string
additionalRendererClasses?: string
disableToc?: boolean
baseUrl: string
newLinesAreBreaks?: boolean
}
/**
* Renders a Markdown document and handles scrolling, yaml metadata and a floating table of contents.
*
* @param additionalOuterContainerClasses Additional classes given to the outer container directly
* @param additionalRendererClasses Additional classes given {@link DocumentMarkdownRenderer} directly
* @param onMakeScrollSource The callback to call if a change of the scroll source is requested-
* @param baseUrl The base url for the renderer
* @param markdownContentLines The current content of the markdown document.
* @param onScroll The callback to call if the renderer is scrolling.
* @param scrollState The current {@link ScrollState}
* @param onHeightChange The callback to call if the height of the document changes
* @param disableToc If the table of contents should be disabled.
* @param newLinesAreBreaks Defines if the provided markdown content should treat new lines as breaks
*/
export const MarkdownDocument: React.FC<MarkdownDocumentProps> = ({
additionalOuterContainerClasses,
additionalRendererClasses,
onMakeScrollSource,
baseUrl,
markdownContentLines,
onScroll,
scrollState,
onHeightChange,
disableToc,
newLinesAreBreaks
}) => {
const rendererRef = useRef<HTMLDivElement | null>(null)
const [rendererSize, setRendererSize] = useState<DOMRectReadOnly>()
useResizeObserver(rendererRef.current, (entry) => {
setRendererSize(entry.contentRect)
})
useEffect(() => onHeightChange?.((rendererSize?.height ?? 0) + 1), [rendererSize, onHeightChange])
const internalDocumentRenderPaneRef = useRef<HTMLDivElement>(null)
const [internalDocumentRenderPaneSize, setInternalDocumentRenderPaneSize] = useState<DOMRectReadOnly>()
useResizeObserver(internalDocumentRenderPaneRef.current, (entry) =>
setInternalDocumentRenderPaneSize(entry.contentRect)
)
const contentLineCount = useMemo(() => markdownContentLines.length, [markdownContentLines])
const [recalculateLineMarkers, onUserScroll] = useDocumentSyncScrolling(
internalDocumentRenderPaneRef,
rendererRef,
contentLineCount,
scrollState,
onScroll
)
return (
<div
className={`${styles['markdown-document']} ${additionalOuterContainerClasses ?? ''}`}
ref={internalDocumentRenderPaneRef}
onScroll={onUserScroll}
data-scroll-element={true}
onMouseEnter={onMakeScrollSource}
onTouchStart={onMakeScrollSource}>
<div className={styles['markdown-document-side']} />
<div className={styles['markdown-document-content']}>
<DocumentMarkdownRenderer
outerContainerRef={rendererRef}
className={`mb-3 ${additionalRendererClasses ?? ''}`}
markdownContentLines={markdownContentLines}
onLineMarkerPositionChanged={recalculateLineMarkers}
baseUrl={baseUrl}
newlinesAreBreaks={newLinesAreBreaks}
/>
</div>
<DocumentTocSidebar
width={internalDocumentRenderPaneSize?.width ?? 0}
baseUrl={baseUrl}
disableToc={disableToc ?? false}
/>
</div>
)
}

View file

@ -7,13 +7,14 @@ import { setDarkModePreference } from '../../redux/dark-mode/methods'
import { useRendererToEditorCommunicator } from '../editor-page/render-context/renderer-to-editor-communicator-context-provider' import { useRendererToEditorCommunicator } from '../editor-page/render-context/renderer-to-editor-communicator-context-provider'
import type { ScrollState } from '../editor-page/synced-scroll/scroll-props' import type { ScrollState } from '../editor-page/synced-scroll/scroll-props'
import { eventEmitterContext } from '../markdown-renderer/hooks/use-extension-event-emitter' import { eventEmitterContext } from '../markdown-renderer/hooks/use-extension-event-emitter'
import { SlideshowMarkdownRenderer } from '../markdown-renderer/slideshow-markdown-renderer' import { DocumentMarkdownRenderer } from './renderers/document/document-markdown-renderer'
import { MarkdownDocument } from './markdown-document' import { SimpleMarkdownRenderer } from './renderers/simple/simple-markdown-renderer'
import { SlideshowMarkdownRenderer } from './renderers/slideshow/slideshow-markdown-renderer'
import { useRendererReceiveHandler } from './window-post-message-communicator/hooks/use-renderer-receive-handler' import { useRendererReceiveHandler } from './window-post-message-communicator/hooks/use-renderer-receive-handler'
import type { BaseConfiguration } from './window-post-message-communicator/rendering-message' import type { BaseConfiguration } from './window-post-message-communicator/rendering-message'
import { CommunicationMessageType, RendererType } from './window-post-message-communicator/rendering-message' import { CommunicationMessageType, RendererType } from './window-post-message-communicator/rendering-message'
import { countWords } from './word-counter' import { countWords } from './word-counter'
import type { SlideOptions } from '@hedgedoc/commons/src/title-extraction/types/slide-show-options' import type { SlideOptions } from '@hedgedoc/commons'
import { EventEmitter2 } from 'eventemitter2' import { EventEmitter2 } from 'eventemitter2'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@ -24,12 +25,10 @@ export const RenderPageContent: React.FC = () => {
const [markdownContentLines, setMarkdownContentLines] = useState<string[]>([]) const [markdownContentLines, setMarkdownContentLines] = useState<string[]>([])
const [scrollState, setScrollState] = useState<ScrollState>({ firstLineInView: 1, scrolledPercentage: 0 }) const [scrollState, setScrollState] = useState<ScrollState>({ firstLineInView: 1, scrolledPercentage: 0 })
const [baseConfiguration, setBaseConfiguration] = useState<BaseConfiguration | undefined>(undefined) const [baseConfiguration, setBaseConfiguration] = useState<BaseConfiguration | undefined>(undefined)
const [slideOptions, setSlideOptions] = useState<SlideOptions>()
const [newLinesAreBreaks, setNewLinesAreBreaks] = useState<boolean>(true)
const communicator = useRendererToEditorCommunicator() const communicator = useRendererToEditorCommunicator()
const sendScrolling = useRef<boolean>(false) const sendScrolling = useRef<boolean>(false)
const [newLinesAreBreaks, setNewLinesAreBreaks] = useState<boolean>(true)
const [slideOptions, setSlideOptions] = useState<SlideOptions>()
useRendererReceiveHandler( useRendererReceiveHandler(
CommunicationMessageType.SET_SLIDE_OPTIONS, CommunicationMessageType.SET_SLIDE_OPTIONS,
@ -119,8 +118,7 @@ export const RenderPageContent: React.FC = () => {
switch (baseConfiguration.rendererType) { switch (baseConfiguration.rendererType) {
case RendererType.DOCUMENT: case RendererType.DOCUMENT:
return ( return (
<MarkdownDocument <DocumentMarkdownRenderer
additionalOuterContainerClasses={'vh-100 bg-light'}
markdownContentLines={markdownContentLines} markdownContentLines={markdownContentLines}
onMakeScrollSource={onMakeScrollSource} onMakeScrollSource={onMakeScrollSource}
scrollState={scrollState} scrollState={scrollState}
@ -135,20 +133,17 @@ export const RenderPageContent: React.FC = () => {
<SlideshowMarkdownRenderer <SlideshowMarkdownRenderer
markdownContentLines={markdownContentLines} markdownContentLines={markdownContentLines}
baseUrl={baseConfiguration.baseUrl} baseUrl={baseConfiguration.baseUrl}
scrollState={scrollState} newLinesAreBreaks={newLinesAreBreaks}
slideOptions={slideOptions} slideOptions={slideOptions}
newlinesAreBreaks={newLinesAreBreaks}
/> />
) )
case RendererType.SIMPLE: case RendererType.SIMPLE:
return ( return (
<MarkdownDocument <SimpleMarkdownRenderer
additionalOuterContainerClasses={'vh-100 bg-light overflow-y-hidden'}
markdownContentLines={markdownContentLines} markdownContentLines={markdownContentLines}
baseUrl={baseConfiguration.baseUrl} baseUrl={baseConfiguration.baseUrl}
disableToc={true}
onHeightChange={onHeightChange}
newLinesAreBreaks={newLinesAreBreaks} newLinesAreBreaks={newLinesAreBreaks}
onHeightChange={onHeightChange}
/> />
) )
default: default:

View file

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export interface CommonMarkdownRendererProps {
baseUrl: string
newLinesAreBreaks?: boolean
markdownContentLines: string[]
}
export interface HeightChangeRendererProps {
onHeightChange?: (height: number) => void
}

View file

@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { cypressId } from '../../../../utils/cypress-attribute'
import type { ScrollProps } from '../../../editor-page/synced-scroll/scroll-props'
import { HeadlineAnchorsMarkdownExtension } from '../../../markdown-renderer/extensions/headline-anchors-markdown-extension'
import type { LineMarkers } from '../../../markdown-renderer/extensions/linemarker/add-line-marker-markdown-it-plugin'
import { LinemarkerMarkdownExtension } from '../../../markdown-renderer/extensions/linemarker/linemarker-markdown-extension'
import { useCalculateLineMarkerPosition } from '../../../markdown-renderer/hooks/use-calculate-line-marker-positions'
import { useMarkdownExtensions } from '../../../markdown-renderer/hooks/use-markdown-extensions'
import { MarkdownToReact } from '../../../markdown-renderer/markdown-to-react/markdown-to-react'
import { useDocumentSyncScrolling } from '../../hooks/sync-scroll/use-document-sync-scrolling'
import type { CommonMarkdownRendererProps, HeightChangeRendererProps } from '../common-markdown-renderer-props'
import { DocumentTocSidebar } from './document-toc-sidebar'
import styles from './markdown-document.module.scss'
import useResizeObserver from '@react-hook/resize-observer'
import React, { useEffect, useMemo, useRef, useState } from 'react'
export type DocumentMarkdownRendererProps = CommonMarkdownRendererProps & ScrollProps & HeightChangeRendererProps
/**
* Renders a Markdown document and handles scrolling, yaml metadata and a floating table of contents.
*
* @param onMakeScrollSource The callback to call if a change of the scroll source is requested-
* @param baseUrl The base url for the renderer
* @param markdownContentLines The current content of the Markdown document.
* @param onScroll The callback to call if the renderer is scrolling.
* @param scrollState The current {@link ScrollState}
* @param onHeightChange The callback to call if the height of the document changes
* @param newlinesAreBreaks Defines if the provided markdown content should treat new lines as breaks
*/
export const DocumentMarkdownRenderer: React.FC<DocumentMarkdownRendererProps> = ({
onMakeScrollSource,
baseUrl,
markdownContentLines,
onScroll,
scrollState,
onHeightChange,
newLinesAreBreaks
}) => {
const rendererRef = useRef<HTMLDivElement | null>(null)
const [rendererSize, setRendererSize] = useState<DOMRectReadOnly>()
useResizeObserver(rendererRef.current, (entry) => {
setRendererSize(entry.contentRect)
})
useEffect(() => onHeightChange?.((rendererSize?.height ?? 0) + 1), [rendererSize, onHeightChange])
const internalDocumentRenderPaneRef = useRef<HTMLDivElement>(null)
const [internalDocumentRenderPaneSize, setInternalDocumentRenderPaneSize] = useState<DOMRectReadOnly>()
useResizeObserver(internalDocumentRenderPaneRef.current, (entry) =>
setInternalDocumentRenderPaneSize(entry.contentRect)
)
const contentLineCount = useMemo(() => markdownContentLines.length, [markdownContentLines])
const [recalculateLineMarkers, onUserScroll] = useDocumentSyncScrolling(
internalDocumentRenderPaneRef,
rendererRef,
contentLineCount,
scrollState,
onScroll
)
const markdownBodyRef = useRef<HTMLDivElement>(null)
const currentLineMarkers = useRef<LineMarkers[]>()
const extensions = useMarkdownExtensions(
baseUrl,
useMemo(
() => [
new HeadlineAnchorsMarkdownExtension(),
new LinemarkerMarkdownExtension((values) => (currentLineMarkers.current = values))
],
[]
)
)
useCalculateLineMarkerPosition(markdownBodyRef, currentLineMarkers.current, recalculateLineMarkers)
return (
<div
className={`${styles['markdown-document']} vh-100 bg-light`}
ref={internalDocumentRenderPaneRef}
onScroll={onUserScroll}
data-scroll-element={true}
onMouseEnter={onMakeScrollSource}
onTouchStart={onMakeScrollSource}>
<div className={styles['markdown-document-side']} />
<div className={styles['markdown-document-content']}>
<div ref={rendererRef} className={`position-relative`}>
<div
{...cypressId('markdown-body')}
ref={markdownBodyRef}
data-word-count-target={true}
className={`mb-3 markdown-body w-100 d-flex flex-column align-items-center`}>
<MarkdownToReact
markdownContentLines={markdownContentLines}
markdownRenderExtensions={extensions}
newlinesAreBreaks={newLinesAreBreaks}
allowHtml={true}
/>
</div>
</div>
</div>
<DocumentTocSidebar width={internalDocumentRenderPaneSize?.width ?? 0} baseUrl={baseUrl} />
</div>
)
}

View file

@ -3,9 +3,8 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ShowIf } from '../common/show-if/show-if' import { TableOfContentsMarkdownExtension } from '../../../markdown-renderer/extensions/table-of-contents/table-of-contents-markdown-extension'
import { TableOfContentsMarkdownExtension } from '../markdown-renderer/extensions/table-of-contents/table-of-contents-markdown-extension' import { useExtensionEventEmitterHandler } from '../../../markdown-renderer/hooks/use-extension-event-emitter'
import { useExtensionEventEmitterHandler } from '../markdown-renderer/hooks/use-extension-event-emitter'
import styles from './markdown-document.module.scss' import styles from './markdown-document.module.scss'
import { WidthBasedTableOfContents } from './width-based-table-of-contents' import { WidthBasedTableOfContents } from './width-based-table-of-contents'
import type { TocAst } from '@hedgedoc/markdown-it-plugins' import type { TocAst } from '@hedgedoc/markdown-it-plugins'
@ -13,18 +12,15 @@ import React, { useState } from 'react'
export interface DocumentTocSidebarProps { export interface DocumentTocSidebarProps {
width: number width: number
disableToc: boolean
baseUrl: string baseUrl: string
} }
export const DocumentTocSidebar: React.FC<DocumentTocSidebarProps> = ({ disableToc, width, baseUrl }) => { export const DocumentTocSidebar: React.FC<DocumentTocSidebarProps> = ({ width, baseUrl }) => {
const [tocAst, setTocAst] = useState<TocAst>() const [tocAst, setTocAst] = useState<TocAst>()
useExtensionEventEmitterHandler(TableOfContentsMarkdownExtension.EVENT_NAME, setTocAst) useExtensionEventEmitterHandler(TableOfContentsMarkdownExtension.EVENT_NAME, setTocAst)
return ( return (
<div className={`${styles['markdown-document-side']} pt-4`}> <div className={`${styles['markdown-document-side']} pt-4`}>
<ShowIf condition={!!tocAst && !disableToc}> <WidthBasedTableOfContents tocAst={tocAst as TocAst} baseUrl={baseUrl} width={width} />
<WidthBasedTableOfContents tocAst={tocAst as TocAst} baseUrl={baseUrl} width={width} />
</ShowIf>
</div> </div>
) )
} }

View file

@ -3,8 +3,8 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { TableOfContents } from '../editor-page/table-of-contents/table-of-contents' import { TableOfContents } from '../../../editor-page/table-of-contents/table-of-contents'
import { TableOfContentsHoveringButton } from './markdown-toc-button/table-of-contents-hovering-button' import { TableOfContentsHoveringButton } from '../../markdown-toc-button/table-of-contents-hovering-button'
import type { TocAst } from '@hedgedoc/markdown-it-plugins' import type { TocAst } from '@hedgedoc/markdown-it-plugins'
import React from 'react' import React from 'react'

View file

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { cypressId } from '../../../../utils/cypress-attribute'
import { useMarkdownExtensions } from '../../../markdown-renderer/hooks/use-markdown-extensions'
import { MarkdownToReact } from '../../../markdown-renderer/markdown-to-react/markdown-to-react'
import type { CommonMarkdownRendererProps, HeightChangeRendererProps } from '../common-markdown-renderer-props'
import useResizeObserver from '@react-hook/resize-observer'
import React, { useEffect, useRef, useState } from 'react'
export type SimpleMarkdownRendererProps = CommonMarkdownRendererProps & HeightChangeRendererProps
/**
* Renders just the given Markdown content without scrolling, slideshow, toc notifying and other additions.
*
* @param additionalOuterContainerClasses Additional classes given to the outer container directly
* @param baseUrl The base url for the renderer
* @param markdownContentLines The current content of the markdown document.
* @param onHeightChange The callback to call if the height of the document changes
*/
export const SimpleMarkdownRenderer: React.FC<SimpleMarkdownRendererProps> = ({
baseUrl,
markdownContentLines,
onHeightChange,
newLinesAreBreaks
}) => {
const rendererRef = useRef<HTMLDivElement | null>(null)
const [rendererSize, setRendererSize] = useState<DOMRectReadOnly>()
useResizeObserver(rendererRef.current, (entry) => {
setRendererSize(entry.contentRect)
})
useEffect(() => onHeightChange?.((rendererSize?.height ?? 0) + 1), [rendererSize, onHeightChange])
const extensions = useMarkdownExtensions(baseUrl, [])
return (
<div className={`vh-100 bg-transparent overflow-y-hidden`}>
<div ref={rendererRef} className={`position-relative`}>
<div
{...cypressId('markdown-body')}
data-word-count-target={true}
className={`markdown-body w-100 d-flex flex-column align-items-center`}>
<MarkdownToReact
markdownContentLines={markdownContentLines}
markdownRenderExtensions={extensions}
newlinesAreBreaks={newLinesAreBreaks}
allowHtml={true}
/>
</div>
</div>
</div>
)
}

View file

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

View file

@ -3,13 +3,12 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { ScrollProps } from '../editor-page/synced-scroll/scroll-props' import { RevealMarkdownExtension } from '../../../markdown-renderer/extensions/reveal/reveal-markdown-extension'
import type { CommonMarkdownRendererProps } from './common-markdown-renderer-props' import { useMarkdownExtensions } from '../../../markdown-renderer/hooks/use-markdown-extensions'
import { RevealMarkdownExtension } from './extensions/reveal/reveal-markdown-extension' import { REVEAL_STATUS, useReveal } from '../../../markdown-renderer/hooks/use-reveal'
import { useMarkdownExtensions } from './hooks/use-markdown-extensions' import { MarkdownToReact } from '../../../markdown-renderer/markdown-to-react/markdown-to-react'
import { REVEAL_STATUS, useReveal } from './hooks/use-reveal' import type { CommonMarkdownRendererProps } from '../common-markdown-renderer-props'
import { LoadingSlide } from './loading-slide' import { LoadingSlide } from './loading-slide'
import { MarkdownToReact } from './markdown-to-react/markdown-to-react'
import type { SlideOptions } from '@hedgedoc/commons' import type { SlideOptions } from '@hedgedoc/commons'
import React, { useMemo, useRef } from 'react' import React, { useMemo, useRef } from 'react'
@ -23,14 +22,12 @@ export interface SlideshowMarkdownRendererProps extends CommonMarkdownRendererPr
* @param className Additional class names directly given to the div * @param className Additional class names directly given to the div
* @param markdownContentLines The markdown lines * @param markdownContentLines The markdown lines
* @param baseUrl The base url of the renderer * @param baseUrl The base url of the renderer
* @param newlinesAreBreaks If newlines are rendered as breaks or not * @param newLinesAreBreaks If newlines are rendered as breaks or not
* @param slideOptions The {@link SlideOptions} to use
*/ */
export const SlideshowMarkdownRenderer: React.FC<SlideshowMarkdownRendererProps & ScrollProps> = ({ export const SlideshowMarkdownRenderer: React.FC<SlideshowMarkdownRendererProps> = ({
className,
markdownContentLines, markdownContentLines,
baseUrl, baseUrl,
newlinesAreBreaks, newLinesAreBreaks,
slideOptions slideOptions
}) => { }) => {
const markdownBodyRef = useRef<HTMLDivElement>(null) const markdownBodyRef = useRef<HTMLDivElement>(null)
@ -49,17 +46,17 @@ export const SlideshowMarkdownRenderer: React.FC<SlideshowMarkdownRendererProps
markdownContentLines={markdownContentLines} markdownContentLines={markdownContentLines}
markdownRenderExtensions={extensions} markdownRenderExtensions={extensions}
allowHtml={true} allowHtml={true}
newlinesAreBreaks={newlinesAreBreaks} newlinesAreBreaks={newLinesAreBreaks}
/> />
) : ( ) : (
<LoadingSlide /> <LoadingSlide />
), ),
[extensions, markdownContentLines, newlinesAreBreaks, revealStatus] [extensions, markdownContentLines, newLinesAreBreaks, revealStatus]
) )
return ( return (
<div className={'reveal'}> <div className={'reveal'}>
<div ref={markdownBodyRef} className={`${className ?? ''} slides`}> <div ref={markdownBodyRef} className={`slides`}>
{slideShowDOM} {slideShowDOM}
</div> </div>
</div> </div>