Skip to content

Commit

Permalink
fix: toc mask overflow
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Sep 14, 2023
1 parent b246808 commit 23dfb47
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 55 deletions.
53 changes: 3 additions & 50 deletions src/components/widgets/shared/ArticleRightAside.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
'use client'

import React, { useEffect, useRef, useState } from 'react'
import React from 'react'
import clsx from 'clsx'
import type { TocAsideRef } from '../toc'

import { useIsMobile, useViewport } from '~/atoms'
import { useIsMobile } from '~/atoms'

import { TocAside } from '../toc'
import { ReadIndicator } from './ReadIndicator'
Expand All @@ -17,60 +16,14 @@ export const ArticleRightAside: Component = ({ children }) => {
}

const ArticleRightAsideImpl: Component = ({ children }) => {
const asideRef = useRef<TocAsideRef>(null)
const [isScrollToBottom, setIsScrollToBottom] = useState(false)
const [isScrollToTop, setIsScrollToTop] = useState(false)
const [canScroll, setCanScroll] = useState(false)
const h = useViewport((v) => v.h)
useEffect(() => {
const $ = asideRef.current?.getContainer()
if (!$) return

// if $ can not scroll, return null
if ($.scrollHeight <= $.clientHeight + 2) return

setCanScroll(true)

const handler = () => {
// 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)
}
}
$.addEventListener('scroll', handler)

handler()

return () => {
$.removeEventListener('scroll', handler)
}
}, [h])

return (
<aside className="sticky top-[120px] mt-[120px] h-[calc(100vh-6rem-4.5rem-150px-120px)]">
<div className="relative h-full">
<TocAside
as="div"
className="static ml-4"
treeClassName={clsx(
'absolute h-full min-h-[120px] flex flex-col',
isScrollToBottom && 'mask-t',
isScrollToTop && 'mask-b',
canScroll && !isScrollToBottom && !isScrollToTop && 'mask-both',
)}
treeClassName={clsx('absolute h-full min-h-[120px] flex flex-col')}
accessory={ReadIndicator}
ref={asideRef}
/>
</div>
{React.cloneElement(children as any, {
Expand Down
11 changes: 10 additions & 1 deletion src/components/widgets/toc/TocTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, {
useMemo,
useRef,
} from 'react'
import clsx from 'clsx'
import { m } from 'framer-motion'
import { atom, useAtom } from 'jotai'
import type { FC } from 'react'
Expand All @@ -17,6 +18,7 @@ import type { ITocItem } from './TocItem'
import { Divider } from '~/components/ui/divider'
import { RightToLeftTransitionView } from '~/components/ui/transition/RightToLeftTransitionView'
import { useStateToRef } from '~/hooks/common/use-state-ref'
import { useMaskScrollArea } from '~/hooks/shared/use-mask-scrollarea'
import { clsxm } from '~/lib/helper'
import { springScrollToElement } from '~/lib/scroller'

Expand Down Expand Up @@ -125,6 +127,10 @@ export const TocTree: Component<
? accessory
: React.createElement(accessory as FC)
}, [accessory])

const [scrollContainerRef, scrollClassname] =
useMaskScrollArea<HTMLUListElement>()

return (
<ul
className={clsxm(
Expand All @@ -133,7 +139,10 @@ export const TocTree: Component<
)}
ref={containerRef}
>
<ul className="overflow-auto scrollbar-none">
<ul
className={clsx('overflow-auto scrollbar-none', scrollClassname)}
ref={scrollContainerRef}
>
{toc?.map((heading) => {
return (
<MemoedItem
Expand Down
65 changes: 65 additions & 0 deletions src/hooks/shared/use-mask-scrollarea.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useEffect, useRef, useState } from 'react'
import clsx from 'clsx'

import { useViewport } from '~/atoms'

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

export const useMaskScrollArea = <T extends HTMLElement = HTMLElement>() => {
const containerRef = useRef<T>(null)
const [isScrollToBottom, setIsScrollToBottom] = useState(false)
const [isScrollToTop, setIsScrollToTop] = useState(false)
const [canScroll, setCanScroll] = useState(false)
const h = useViewport((v) => v.h)

const eventHandler = useEventCallback(() => {
const $ = containerRef.current

if (!$) return

// if $ can not scroll, return null
if ($.scrollHeight <= $.clientHeight + 2) return

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)
}
})
useEffect(() => {
const $ = containerRef.current
if (!$) return
$.addEventListener('scroll', eventHandler)

eventHandler()

return () => {
$.removeEventListener('scroll', eventHandler)
}
}, [eventHandler])

useEffect(() => {
eventHandler()
}, [eventHandler, h])

return [
containerRef,
clsx(
isScrollToBottom && 'mask-t',
isScrollToTop && 'mask-b',
canScroll && !isScrollToBottom && !isScrollToTop && 'mask-both',
),
]
}
8 changes: 4 additions & 4 deletions src/styles/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,22 @@
.mask-both {
mask-image: linear-gradient(
rgba(255, 255, 255, 0) 0%,
rgb(255, 255, 255) 10%,
rgb(255, 255, 255) 90%,
rgb(255, 255, 255) 20px,
rgb(255, 255, 255) calc(100% - 20px),
rgba(255, 255, 255, 0) 100%
);
}

.mask-b {
mask-image: linear-gradient(
rgb(255, 255, 255) 90%,
rgb(255, 255, 255) calc(100% - 20px),
rgba(255, 255, 255, 0) 100%
);
}

.mask-t {
mask-image: linear-gradient(
rgba(255, 255, 255, 0) 0%,
rgb(255, 255, 255) 10%
rgb(255, 255, 255) 20px
);
}

1 comment on commit 23dfb47

@vercel
Copy link

@vercel vercel bot commented on 23dfb47 Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

shiro-git-main-innei.vercel.app
springtide.vercel.app
shiro-innei.vercel.app
innei.in

Please sign in to comment.