refactor: optimize TOC and scroll position handling

pull/18314/head
ZeroZ_JQ 1 year ago
parent 81a492fc23
commit f54cadfb48

@ -1,6 +1,6 @@
'use client' 'use client'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiListUnordered } from '@remixicon/react' import { RiListUnordered } from '@remixicon/react'
@ -46,7 +46,7 @@ const useToc = (apiBaseUrl: string, locale: string) => {
} }
} }
const timeoutId = setTimeout(extractTOC, 500) const timeoutId = setTimeout(extractTOC, 0)
return () => clearTimeout(timeoutId) return () => clearTimeout(timeoutId)
}, [locale, apiBaseUrl]) }, [locale, apiBaseUrl])
@ -55,6 +55,7 @@ const useToc = (apiBaseUrl: string, locale: string) => {
const useScrollPosition = (headingElements: HTMLElement[]) => { const useScrollPosition = (headingElements: HTMLElement[]) => {
const [activeIndex, setActiveIndex] = useState<number | null>(null) const [activeIndex, setActiveIndex] = useState<number | null>(null)
const activeIndexRef = useRef<number | null>(null)
useEffect(() => { useEffect(() => {
const scrollContainer = document.querySelector('.scroll-container') const scrollContainer = document.querySelector('.scroll-container')
@ -86,8 +87,10 @@ const useScrollPosition = (headingElements: HTMLElement[]) => {
currentActiveIndex = 0 currentActiveIndex = 0
} }
if (currentActiveIndex !== activeIndex) if (currentActiveIndex !== activeIndexRef.current) {
activeIndexRef.current = currentActiveIndex
setActiveIndex(currentActiveIndex) setActiveIndex(currentActiveIndex)
}
} }
const throttledScrollHandler = throttle(handleScroll, 100) const throttledScrollHandler = throttle(handleScroll, 100)
@ -98,27 +101,36 @@ const useScrollPosition = (headingElements: HTMLElement[]) => {
scrollContainer.removeEventListener('scroll', throttledScrollHandler) scrollContainer.removeEventListener('scroll', throttledScrollHandler)
throttledScrollHandler.cancel() throttledScrollHandler.cancel()
} }
}, [headingElements, activeIndex]) }, [headingElements])
return activeIndex return activeIndex
} }
const useResponsiveToc = () => { const useResponsiveToc = () => {
const [isTocExpanded, setIsTocExpanded] = useState(false) const [isTocExpanded, setIsTocExpanded] = useState(false)
const userToggled = useRef(false)
useEffect(() => { useEffect(() => {
const mediaQuery = window.matchMedia('(min-width: 1280px)') const mediaQuery = window.matchMedia('(min-width: 1280px)')
setIsTocExpanded(mediaQuery.matches)
if (!userToggled.current)
setIsTocExpanded(mediaQuery.matches)
const handleChange = (e: MediaQueryListEvent) => { const handleChange = (e: MediaQueryListEvent) => {
setIsTocExpanded(e.matches) if (!userToggled.current)
setIsTocExpanded(e.matches)
} }
mediaQuery.addEventListener('change', handleChange) mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange) return () => mediaQuery.removeEventListener('change', handleChange)
}, []) }, [])
return { isTocExpanded, setIsTocExpanded } const setTocExpandedWithTracking = (expanded: boolean) => {
userToggled.current = true
setIsTocExpanded(expanded)
}
return { isTocExpanded, setIsTocExpanded: setTocExpandedWithTracking }
} }
const Doc = ({ apiBaseUrl }: DocProps) => { const Doc = ({ apiBaseUrl }: DocProps) => {
@ -129,7 +141,6 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
const activeIndex = useScrollPosition(headingElements) const activeIndex = useScrollPosition(headingElements)
const { theme } = useTheme() const { theme } = useTheme()
// Handle TOC item click
const handleTocClick = (e: React.MouseEvent<HTMLAnchorElement>, item: TocItem) => { const handleTocClick = (e: React.MouseEvent<HTMLAnchorElement>, item: TocItem) => {
e.preventDefault() e.preventDefault()
@ -203,7 +214,9 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
</button> </button>
)} )}
</div> </div>
<article className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'dark:prose-invert')}> <article
className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'dark:prose-invert')}
>
{Template} {Template}
</article> </article>
</div> </div>

@ -1,5 +1,5 @@
'use client' 'use client'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiListUnordered } from '@remixicon/react' import { RiListUnordered } from '@remixicon/react'
@ -54,7 +54,7 @@ const useToc = (appDetail: any, locale: string) => {
} }
} }
const timeoutId = setTimeout(extractTOC, 500) const timeoutId = setTimeout(extractTOC, 0)
return () => clearTimeout(timeoutId) return () => clearTimeout(timeoutId)
}, [appDetail, locale]) }, [appDetail, locale])
@ -63,6 +63,7 @@ const useToc = (appDetail: any, locale: string) => {
const useScrollPosition = (headingElements: HTMLElement[]) => { const useScrollPosition = (headingElements: HTMLElement[]) => {
const [activeIndex, setActiveIndex] = useState<number | null>(null) const [activeIndex, setActiveIndex] = useState<number | null>(null)
const activeIndexRef = useRef<number | null>(null)
useEffect(() => { useEffect(() => {
const scrollContainer = document.querySelector('.overflow-auto') const scrollContainer = document.querySelector('.overflow-auto')
@ -97,8 +98,10 @@ const useScrollPosition = (headingElements: HTMLElement[]) => {
currentActiveIndex = 0 currentActiveIndex = 0
} }
if (currentActiveIndex !== activeIndex) if (currentActiveIndex !== activeIndexRef.current) {
activeIndexRef.current = currentActiveIndex
setActiveIndex(currentActiveIndex) setActiveIndex(currentActiveIndex)
}
} }
const throttledScrollHandler = throttle(handleScroll, 100) const throttledScrollHandler = throttle(handleScroll, 100)
@ -109,27 +112,36 @@ const useScrollPosition = (headingElements: HTMLElement[]) => {
scrollContainer.removeEventListener('scroll', throttledScrollHandler) scrollContainer.removeEventListener('scroll', throttledScrollHandler)
throttledScrollHandler.cancel() throttledScrollHandler.cancel()
} }
}, [headingElements, activeIndex]) }, [headingElements])
return activeIndex return activeIndex
} }
const useResponsiveToc = () => { const useResponsiveToc = () => {
const [isTocExpanded, setIsTocExpanded] = useState(false) const [isTocExpanded, setIsTocExpanded] = useState(false)
const userToggled = useRef(false)
useEffect(() => { useEffect(() => {
const mediaQuery = window.matchMedia('(min-width: 1280px)') const mediaQuery = window.matchMedia('(min-width: 1280px)')
setIsTocExpanded(mediaQuery.matches)
if (!userToggled.current)
setIsTocExpanded(mediaQuery.matches)
const handleChange = (e: MediaQueryListEvent) => { const handleChange = (e: MediaQueryListEvent) => {
setIsTocExpanded(e.matches) if (!userToggled.current)
setIsTocExpanded(e.matches)
} }
mediaQuery.addEventListener('change', handleChange) mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange) return () => mediaQuery.removeEventListener('change', handleChange)
}, []) }, [])
return { isTocExpanded, setIsTocExpanded } const setTocExpandedWithTracking = (expanded: boolean) => {
userToggled.current = true
setIsTocExpanded(expanded)
}
return { isTocExpanded, setIsTocExpanded: setTocExpandedWithTracking }
} }
const Doc = ({ appDetail }: IDocProps) => { const Doc = ({ appDetail }: IDocProps) => {

Loading…
Cancel
Save