mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-15 15:44:45 -04:00
Add read-only view (/s/note) (#563)
* Update Link classes to allow tooltips/titles * Added read-only-view, Move note title extraction into separate file (cherry picked from commit be23083ca3966f26b1b841d5cf4f21e299c8a55a) (cherry picked from commit cbc595d3fc336b0a649c396dfae30fa08082384c) * Optimized look of document-infobar (cherry picked from commit 0176668b156da3fd7c534161a839ca0e3495119c) # Conflicts: # src/components/editor/document-bar/document-info/document-info-time-line.tsx * Show help-button only in Editor-variant of AppBar (cherry picked from commit 3c26e1619c774fe162cb3d8fae9e79ced92c9c3e) * Update CHANGELOG (cherry picked from commit d0d29e7d408515cc8f86df45d13fff60d741873e) * Move motd-banner to top of page (cherry picked from commit 43a9a274bf5da3fdf640ec905ab38153c81b014b) * Refactor isInline to size property (cherry picked from commit cb4ee74b7c97ec9711946f28924e9c890b752ea3) # Conflicts: # src/components/editor/document-bar/document-info/document-info-time-line.tsx * Add size attribute to user-avatar (cherry picked from commit 9629b58911b9d4f3aed81ef8c271fbc8e5a15aa4) * Add mode-enum to app-bar (cherry picked from commit 08f95be58974468c1e2897b475e5e3235b79c230) * Split DocumentRenderPane into scrollable- and non-scrollable variant (cherry picked from commit 44dd27edfd967745c548f7ae1fd2047e812cdc22) * Removed unnecessary className
This commit is contained in:
parent
36679753d7
commit
9e9108ec9a
23 changed files with 379 additions and 143 deletions
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import React, { RefObject, useState } from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import useResizeObserver from 'use-resize-observer'
|
||||
import { TocAst } from '../../../external-types/markdown-it-toc-done-right/interface'
|
||||
|
@ -6,119 +6,48 @@ import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
|||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
import { LineMarkerPosition } from '../../markdown-renderer/types'
|
||||
import { FullMarkdownRenderer } from '../../markdown-renderer/full-markdown-renderer'
|
||||
import { ScrollProps, ScrollState } from '../scroll/scroll-props'
|
||||
import { findLineMarks } from '../scroll/utils'
|
||||
import { TableOfContents } from '../table-of-contents/table-of-contents'
|
||||
import { YAMLMetaData } from '../yaml-metadata/yaml-metadata'
|
||||
|
||||
interface DocumentRenderPaneProps {
|
||||
export interface DocumentRenderPaneProps {
|
||||
content: string
|
||||
extraClasses?: string
|
||||
onFirstHeadingChange: (firstHeading: string | undefined) => void
|
||||
onLineMarkerPositionChanged?: (lineMarkerPosition: LineMarkerPosition[]) => void
|
||||
onMetadataChange: (metaData: YAMLMetaData | undefined) => void
|
||||
onMouseEnterRenderer?: () => void
|
||||
onScrollRenderer?: () => void
|
||||
onTaskCheckedChange: (lineInMarkdown: number, checked: boolean) => void
|
||||
rendererReference?: RefObject<HTMLDivElement>
|
||||
wide?: boolean
|
||||
}
|
||||
|
||||
export const DocumentRenderPane: React.FC<DocumentRenderPaneProps & ScrollProps> = ({
|
||||
export const DocumentRenderPane: React.FC<DocumentRenderPaneProps> = ({
|
||||
content,
|
||||
extraClasses,
|
||||
onFirstHeadingChange,
|
||||
onMakeScrollSource,
|
||||
onLineMarkerPositionChanged,
|
||||
onMetadataChange,
|
||||
onScroll,
|
||||
onMouseEnterRenderer,
|
||||
onScrollRenderer,
|
||||
onTaskCheckedChange,
|
||||
scrollState,
|
||||
rendererReference,
|
||||
wide
|
||||
}) => {
|
||||
const [tocAst, setTocAst] = useState<TocAst>()
|
||||
const renderer = useRef<HTMLDivElement>(null)
|
||||
const { width } = useResizeObserver({ ref: renderer })
|
||||
const lastScrollPosition = useRef<number>()
|
||||
const [lineMarks, setLineMarks] = useState<LineMarkerPosition[]>()
|
||||
|
||||
const { width } = useResizeObserver(rendererReference ? { ref: rendererReference } : undefined)
|
||||
const realWidth = width || 0
|
||||
|
||||
const scrollTo = useCallback((targetPosition:number):void => {
|
||||
if (!renderer.current || targetPosition === lastScrollPosition.current) {
|
||||
return
|
||||
}
|
||||
lastScrollPosition.current = targetPosition
|
||||
renderer.current.scrollTo({
|
||||
top: targetPosition
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!renderer.current || !lineMarks || lineMarks.length === 0 || !scrollState) {
|
||||
return
|
||||
}
|
||||
if (scrollState.firstLineInView < lineMarks[0].line) {
|
||||
scrollTo(0)
|
||||
return
|
||||
}
|
||||
if (scrollState.firstLineInView > lineMarks[lineMarks.length - 1].line) {
|
||||
scrollTo(renderer.current.offsetHeight)
|
||||
return
|
||||
}
|
||||
const { lastMarkBefore, firstMarkAfter } = findLineMarks(lineMarks, scrollState.firstLineInView)
|
||||
const positionBefore = lastMarkBefore ? lastMarkBefore.position : lineMarks[0].position
|
||||
const positionAfter = firstMarkAfter ? firstMarkAfter.position : renderer.current.offsetHeight
|
||||
const lastMarkBeforeLine = lastMarkBefore ? lastMarkBefore.line : 1
|
||||
const firstMarkAfterLine = firstMarkAfter ? firstMarkAfter.line : content.split('\n').length
|
||||
const lineCount = firstMarkAfterLine - lastMarkBeforeLine
|
||||
const blockHeight = positionAfter - positionBefore
|
||||
const lineHeight = blockHeight / lineCount
|
||||
const position = positionBefore + (scrollState.firstLineInView - lastMarkBeforeLine) * lineHeight + scrollState.scrolledPercentage / 100 * lineHeight
|
||||
const correctedPosition = Math.floor(position)
|
||||
scrollTo(correctedPosition)
|
||||
}, [content, lineMarks, scrollState, scrollTo])
|
||||
|
||||
const userScroll = useCallback(() => {
|
||||
if (!renderer.current || !lineMarks || lineMarks.length === 0 || !onScroll) {
|
||||
return
|
||||
}
|
||||
|
||||
const scrollTop = renderer.current.scrollTop
|
||||
|
||||
const lineMarksBeforeScrollTop = lineMarks.filter(lineMark => lineMark.position <= scrollTop)
|
||||
if (lineMarksBeforeScrollTop.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const lineMarksAfterScrollTop = lineMarks.filter(lineMark => lineMark.position > scrollTop)
|
||||
if (lineMarksAfterScrollTop.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const beforeLineMark = lineMarksBeforeScrollTop
|
||||
.reduce((prevLineMark, currentLineMark) =>
|
||||
prevLineMark.line >= currentLineMark.line ? prevLineMark : currentLineMark)
|
||||
|
||||
const afterLineMark = lineMarksAfterScrollTop
|
||||
.reduce((prevLineMark, currentLineMark) =>
|
||||
prevLineMark.line < currentLineMark.line ? prevLineMark : currentLineMark)
|
||||
|
||||
const componentHeight = afterLineMark.position - beforeLineMark.position
|
||||
const distanceToBefore = scrollTop - beforeLineMark.position
|
||||
const percentageRaw = (distanceToBefore / componentHeight)
|
||||
const lineCount = afterLineMark.line - beforeLineMark.line
|
||||
const line = Math.floor(lineCount * percentageRaw + beforeLineMark.line)
|
||||
const lineHeight = componentHeight / lineCount
|
||||
const innerScrolling = Math.floor((distanceToBefore % lineHeight) / lineHeight * 100)
|
||||
|
||||
const newScrollState: ScrollState = { firstLineInView: line, scrolledPercentage: innerScrolling }
|
||||
onScroll(newScrollState)
|
||||
}, [lineMarks, onScroll])
|
||||
|
||||
return (
|
||||
<div className={'bg-light flex-fill pb-5 flex-row d-flex w-100 h-100 overflow-y-scroll'}
|
||||
ref={renderer} onScroll={userScroll} onMouseEnter={onMakeScrollSource}>
|
||||
<div className={`bg-light flex-fill pb-5 flex-row d-flex w-100 h-100 ${extraClasses ?? ''}`}
|
||||
ref={rendererReference} onScroll={onScrollRenderer} onMouseEnter={onMouseEnterRenderer}>
|
||||
<div className={'col-md'}/>
|
||||
<div className={'bg-light flex-fill'}>
|
||||
<FullMarkdownRenderer
|
||||
className={'flex-fill mb-3'}
|
||||
content={content}
|
||||
onFirstHeadingChange={onFirstHeadingChange}
|
||||
onLineMarkerPositionChanged={setLineMarks}
|
||||
onLineMarkerPositionChanged={onLineMarkerPositionChanged}
|
||||
onMetaDataChange={onMetadataChange}
|
||||
onTaskCheckedChange={onTaskCheckedChange}
|
||||
onTocChange={(tocAst) => setTocAst(tocAst)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue