Skip to content

Commit

Permalink
fix: toc scroller
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Dec 16, 2023
1 parent 7bf96c9 commit 10cf466
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 35 deletions.
25 changes: 17 additions & 8 deletions src/components/ui/transition/factor.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
'use client'

import { memo, useMemo } from 'react'
import { forwardRef, memo, useMemo } from 'react'
import { m } from 'framer-motion'
import type {
HTMLMotionProps,
Spring,
Target,
TargetAndTransition,
} from 'framer-motion'
import type { FC, PropsWithChildren } from 'react'
import type {
ForwardRefExoticComponent,
PropsWithChildren,
RefAttributes,
} from 'react'
import type { BaseTransitionProps } from './typings'

import { isHydrationEnded } from '~/components/common/HydrationEndDetector'
Expand All @@ -21,12 +25,13 @@ interface TransitionViewParams {
preset?: Spring
}

export const createTransitionView = (
params: TransitionViewParams,
): FC<PropsWithChildren<BaseTransitionProps>> => {
export const createTransitionView = (params: TransitionViewParams) => {
const { from, to, initial, preset } = params

const TransitionView = (props: PropsWithChildren<BaseTransitionProps>) => {
const TransitionView = forwardRef<
HTMLElement,
PropsWithChildren<BaseTransitionProps>
>((props, ref) => {
const {
timeout = {},
duration = 0.5,
Expand All @@ -40,7 +45,9 @@ export const createTransitionView = (

const { enter = delay, exit = delay } = timeout

const MotionComponent = m[as] as FC<HTMLMotionProps<any>>
const MotionComponent = m[as] as ForwardRefExoticComponent<
HTMLMotionProps<any> & RefAttributes<HTMLElement>
>

return (
<MotionComponent
Expand All @@ -53,6 +60,7 @@ export const createTransitionView = (
: initial || from,
[],
)}
ref={ref}
animate={{
...to,
transition: {
Expand All @@ -78,7 +86,8 @@ export const createTransitionView = (
{props.children}
</MotionComponent>
)
}
})
TransitionView.displayName = `forwardRef(TransitionView)`
const MemoedTransitionView = memo(TransitionView)
MemoedTransitionView.displayName = `MemoedTransitionView`
return MemoedTransitionView
Expand Down
6 changes: 3 additions & 3 deletions src/components/widgets/post/PostPinIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client'

import { memo, useState } from 'react'
import { useState } from 'react'

import { apiClient } from '~/lib/request'

import { PinIconToggle } from '../shared/PinIconToggle'

export const PostPinIcon = memo(({ pin, id }: { pin: boolean; id: string }) => {
export const PostPinIcon = ({ pin, id }: { pin: boolean; id: string }) => {
const [pinState, setPinState] = useState(pin)
return (
<PinIconToggle
Expand All @@ -21,4 +21,4 @@ export const PostPinIcon = memo(({ pin, id }: { pin: boolean; id: string }) => {
pin={pinState}
/>
)
})
}
4 changes: 2 additions & 2 deletions src/components/widgets/shared/ActionAsideContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ export const ActionAsideContainer: Component = ({ className, children }) => {
return (
<div
className={clsxm(
'absolute bottom-0 left-0 -mb-4 max-h-[300px] flex-col space-y-6 p-4 transition-all duration-200 ease-in-out',
'absolute bottom-0 left-0 -mb-4 flex max-h-[300px] flex-col gap-6 p-4 transition-all duration-200 ease-in-out',
!isEOA ? 'opacity-20 hover:opacity-100' : '',
className,
isEndOfPage && 'bottom-[100px]',
isEndOfPage && 'bottom-[-30px] flex-row',
)}
>
{children}
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/toc/TocAutoScroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useEffect } from 'react'

import { escapeSelector } from './escapeSelector'
import { escapeSelector } from '~/lib/dom'

export const TocAutoScroll: Component = () => {
useEffect(() => {
Expand Down
23 changes: 23 additions & 0 deletions src/components/widgets/toc/TocTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,28 @@ const MemoedItem = memo<{
// containerRef
} = props

const itemRef = useRef<HTMLElement>(null)

useEffect(() => {
if (!isActive) return

const $item = itemRef.current
if (!$item) return
const $container = $item.parentElement
if (!$container) return
const itemInContainerTop = $item.offsetTop
const halfOfContainerHeight = $container.clientHeight / 2
// 如果当前元素在容器的上半部分,不滚动
if (itemInContainerTop < halfOfContainerHeight) {
if ($container.scrollTop < halfOfContainerHeight) {
$container.scrollTop = 0
}
} else {
// 如果当前元素在容器的下半部分,滚动到容器中间
$container.scrollTop = itemInContainerTop + halfOfContainerHeight
}
}, [isActive])

return (
<RightToLeftTransitionView
timeout={
Expand All @@ -189,6 +211,7 @@ const MemoedItem = memo<{
key={heading.title}
as="li"
className="relative leading-none"
ref={itemRef}
>
{isActive && (
<m.span
Expand Down
3 changes: 0 additions & 3 deletions src/components/widgets/toc/escapeSelector.tsx

This file was deleted.

26 changes: 8 additions & 18 deletions src/hooks/shared/use-mask-scrollarea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useViewport } from '~/atoms'

import { useEventCallback } from '../common/use-event-callback'

const THRESHOLD = 0
export const useMaskScrollArea = <T extends HTMLElement = HTMLElement>() => {
const containerRef = useRef<T>(null)
const [isScrollToBottom, setIsScrollToBottom] = useState(false)
Expand All @@ -17,7 +18,7 @@ export const useMaskScrollArea = <T extends HTMLElement = HTMLElement>() => {

if (!$) return

// if $ can not scroll, return null
// if $ can not scroll
if ($.scrollHeight <= $.clientHeight + 2) {
setCanScroll(false)
setIsScrollToBottom(false)
Expand All @@ -27,29 +28,18 @@ export const useMaskScrollArea = <T extends HTMLElement = HTMLElement>() => {

setCanScroll(true)

// to bottom
if ($.scrollTop + $.clientHeight + 20 > $.scrollHeight) {
setIsScrollToBottom(true)
setIsScrollToTop(false)
}

// if scroll to top,
// set isScrollToTop to true
else if ($.scrollTop < 20) {
setIsScrollToTop(true)
setIsScrollToBottom(false)
} else {
setIsScrollToBottom(false)
setIsScrollToTop(false)
}
// if $ can scroll
const isScrollToBottom =
$.scrollTop + $.clientHeight >= $.scrollHeight - THRESHOLD
const isScrollToTop = $.scrollTop <= THRESHOLD
setIsScrollToBottom(isScrollToBottom)
setIsScrollToTop(isScrollToTop)
})
useEffect(() => {
const $ = containerRef.current
if (!$) return
$.addEventListener('scroll', eventHandler)

eventHandler()

return () => {
$.removeEventListener('scroll', eventHandler)
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ export const transitionViewIfSupported = (updateCb: () => any) => {
updateCb()
}
}

export function escapeSelector(selector: string) {
return selector.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, '\\$&')
}

0 comments on commit 10cf466

Please sign in to comment.