hedgedoc/frontend/src/components/render-page/hooks/sync-scroll/use-scroll-to-line-mark.ts
Tilman Vatteroth 762a0a850e
fix: Move content into to frontend directory
Doing this BEFORE the merge prevents a lot of merge conflicts.

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
2022-11-20 19:48:40 +01:00

69 lines
2.7 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { RefObject } from 'react'
import { useCallback, useEffect, useRef } from 'react'
import type { ScrollState } from '../../../editor-page/synced-scroll/scroll-props'
import { findLineMarks } from '../../../editor-page/synced-scroll/utils'
import type { LineMarkerPosition } from '../../../markdown-renderer/extensions/linemarker/types'
/**
* Scrolls the given container to the correct {@link LineMarkerPosition}.
*
* @param scrollState The current {@link ScrollState}
* @param lineMarks An array of the current {@link LineMarkerPosition LineMarkerPositions}.
* @param contentLineCount The number of lines
* @param scrollContainer The container to scroll in
*/
export const useScrollToLineMark = (
scrollState: ScrollState | undefined,
lineMarks: LineMarkerPosition[] | undefined,
contentLineCount: number,
scrollContainer: RefObject<HTMLElement>
): void => {
const lastScrollPosition = useRef<number>()
const scrollTo = useCallback(
(targetPosition: number): void => {
if (!scrollContainer.current || targetPosition === lastScrollPosition.current) {
return
}
lastScrollPosition.current = targetPosition
scrollContainer.current.scrollTo({
top: targetPosition
})
},
[scrollContainer]
)
useEffect(() => {
if (!scrollContainer.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(scrollContainer.current.offsetHeight)
return
}
const { lastMarkBefore, firstMarkAfter } = findLineMarks(lineMarks, scrollState.firstLineInView)
const positionBefore = lastMarkBefore ? lastMarkBefore.position : lineMarks[0].position
const positionAfter = firstMarkAfter ? firstMarkAfter.position : scrollContainer.current.offsetHeight
const lastMarkBeforeLine = lastMarkBefore ? lastMarkBefore.line : 1
const firstMarkAfterLine = firstMarkAfter ? firstMarkAfter.line : contentLineCount
const linesBetweenMarkers = firstMarkAfterLine - lastMarkBeforeLine
const blockHeight = positionAfter - positionBefore
const lineHeight = blockHeight / linesBetweenMarkers
const position =
positionBefore +
(scrollState.firstLineInView - lastMarkBeforeLine) * lineHeight +
(scrollState.scrolledPercentage / 100) * lineHeight
const correctedPosition = Math.floor(position)
scrollTo(correctedPosition)
}, [contentLineCount, lineMarks, scrollContainer, scrollState, scrollTo])
}