fix: calc toc scroller range when entry content changed

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-08-30 15:47:05 +08:00
parent 1708533f79
commit 66035fea72
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
4 changed files with 32 additions and 33 deletions

View File

@ -1,5 +1,6 @@
import * as HoverCard from "@radix-ui/react-hover-card"
import { getViewport } from "@renderer/atoms/hooks/viewport"
import { getElementTop } from "@renderer/lib/dom"
import { springScrollToElement } from "@renderer/lib/scroller"
import { cn } from "@renderer/lib/utils"
import { useGetWrappedElementPosition } from "@renderer/providers/wrapped-element-provider"
@ -98,13 +99,24 @@ export const Toc: Component = ({ className }) => {
const titleBetweenPositionTopRangeMap = [] as [number, number][]
for (let i = 0; i < $headings.length - 1; i++) {
const $heading = $headings[i]
const $nextHeading = $headings[i + 1]
const top = Number.parseInt($heading.dataset["containerTop"] || "0")
const nextTop = Number.parseInt(
$nextHeading.dataset["containerTop"] || "0",
)
titleBetweenPositionTopRangeMap.push([top, nextTop])
const headingTop =
Number.parseInt($heading.dataset["containerTop"] || "0") ||
getElementTop($heading)
if (!$heading.dataset) {
// @ts-expect-error
$heading.dataset["containerTop"] = headingTop.toString()
}
const $nextHeading = $headings[i + 1]
const nextTop = getElementTop($nextHeading)
if (!$nextHeading.dataset) {
// @ts-expect-error
$nextHeading.dataset["containerTop"] = nextTop.toString()
}
titleBetweenPositionTopRangeMap.push([headingTop, nextTop])
}
return titleBetweenPositionTopRangeMap
}, [$headings])
@ -136,7 +148,6 @@ export const Toc: Component = ({ className }) => {
// current top is this range, the precent is ?
const precent = (actualTop - start) / (end - start)
// console.log("currentRange", currentRange, precent)
// position , precent
setCurrentScrollRange([currentRangeIndex, precent])
} else {

View File

@ -1,7 +1,6 @@
import { springScrollToElement } from "@renderer/lib/scroller"
import { cn } from "@renderer/lib/utils"
import { useWrappedElementSize } from "@renderer/providers/wrapped-element-provider"
import { useContext, useId, useLayoutEffect, useRef, useState } from "react"
import { useContext, useId, useRef } from "react"
import { useScrollViewElement } from "../../scroll-area/hooks"
import { MarkdownRenderContainerRefContext } from "../context"
@ -23,22 +22,9 @@ export const createHeadingRenderer =
const renderContainer = useContext(MarkdownRenderContainerRefContext)
const ref = useRef<HTMLHeadingElement>(null)
const [currentTitleTop, setCurrentTitleTop] = useState(0)
const { h } = useWrappedElementSize()
useLayoutEffect(() => {
const $heading = ref.current
if (!$heading) return
// const { top } = $heading.getBoundingClientRect()
// // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-layout-effect
// setCurrentTitleTop(top | 0)
const top = getElementTop($heading)
setCurrentTitleTop(top)
}, [h])
return (
<As
ref={ref}
data-container-top={currentTitleTop}
{...rest}
data-rid={rid}
className={cn(rest.className, "group relative")}
@ -69,13 +55,3 @@ export const createHeadingRenderer =
</As>
)
}
const getElementTop = (element: HTMLElement) => {
let actualTop = element.offsetTop
let current = element.offsetParent as HTMLElement
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent as HTMLElement
}
return actualTop
}

View File

@ -12,3 +12,13 @@ export const nextFrame = (fn: (...args: any[]) => any) => {
})
})
}
export const getElementTop = (element: HTMLElement) => {
let actualTop = element.offsetTop
let current = element.offsetParent as HTMLElement
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent as HTMLElement
}
return actualTop
}

View File

@ -445,8 +445,10 @@ const ContainerToc: FC = () => {
<div className="sticky top-0">
<Toc
className={cn(
"flex flex-col items-end animate-in fade-in-0 slide-in-from-bottom-12 easing-spring-soft @[900px]:items-start",
"flex flex-col items-end animate-in fade-in-0 slide-in-from-bottom-12 easing-spring-soft",
"max-h-[calc(100vh-100px)] overflow-auto scrollbar-none",
"@[600px]:-translate-x-16 @[900px]:translate-x-0 @[900px]:items-start",
)}
/>
</div>