From 66035fea72e3ce0c6fbcd8380fbdef612e052cb4 Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 30 Aug 2024 15:47:05 +0800 Subject: [PATCH] fix: calc toc scroller range when entry content changed Signed-off-by: Innei --- .../components/ui/markdown/components/Toc.tsx | 25 +++++++++++++----- .../ui/markdown/renderers/Heading.tsx | 26 +------------------ src/renderer/src/lib/dom.ts | 10 +++++++ .../src/modules/entry-content/index.tsx | 4 ++- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/renderer/src/components/ui/markdown/components/Toc.tsx b/src/renderer/src/components/ui/markdown/components/Toc.tsx index f17aea1a0..b4404e508 100644 --- a/src/renderer/src/components/ui/markdown/components/Toc.tsx +++ b/src/renderer/src/components/ui/markdown/components/Toc.tsx @@ -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 { diff --git a/src/renderer/src/components/ui/markdown/renderers/Heading.tsx b/src/renderer/src/components/ui/markdown/renderers/Heading.tsx index ce5faf510..2005e1006 100644 --- a/src/renderer/src/components/ui/markdown/renderers/Heading.tsx +++ b/src/renderer/src/components/ui/markdown/renderers/Heading.tsx @@ -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(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 ( ) } - -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 -} diff --git a/src/renderer/src/lib/dom.ts b/src/renderer/src/lib/dom.ts index c87735001..42cc33de7 100644 --- a/src/renderer/src/lib/dom.ts +++ b/src/renderer/src/lib/dom.ts @@ -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 +} diff --git a/src/renderer/src/modules/entry-content/index.tsx b/src/renderer/src/modules/entry-content/index.tsx index c900a05a3..c00d1a392 100644 --- a/src/renderer/src/modules/entry-content/index.tsx +++ b/src/renderer/src/modules/entry-content/index.tsx @@ -445,8 +445,10 @@ const ContainerToc: FC = () => {