Skip to content

Commit

Permalink
feat: read indicator
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <tukon479@gmail.com>
  • Loading branch information
Innei committed Jun 20, 2023
1 parent ce48b33 commit 30d5395
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 11 deletions.
11 changes: 11 additions & 0 deletions src/app/notes/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ClientOnly } from '~/components/common/ClientOnly'
import { PageDataHolder } from '~/components/common/PageHolder'
import { MdiClockOutline } from '~/components/icons/clock'
import { useSetHeaderMetaInfo } from '~/components/layout/header/internal/hooks'
import { Divider } from '~/components/ui/divider'
import { FloatPopover } from '~/components/ui/float-popover'
import { Loading } from '~/components/ui/loading'
import { Markdown } from '~/components/ui/markdown'
Expand All @@ -33,6 +34,7 @@ import { NoteLayoutRightSidePortal } from '~/providers/note/right-side-provider'
import { parseDate } from '~/utils/datetime'
import { springScrollToTop } from '~/utils/scroller'

import { ReadIndicator } from '../../../components/common/ReadIndicator'
import { NoteActionAside } from '../../../components/widgets/note/NoteActionAside'
import { NoteHideIfSecret } from '../../../components/widgets/note/NoteHideIfSecret'
import { NoteMetaBar } from '../../../components/widgets/note/NoteMetaBar'
Expand Down Expand Up @@ -114,6 +116,7 @@ const NotePage = memo(({ note }: { note: NoteModel }) => {
<TocAside
className="sticky top-[120px] ml-4 mt-[120px]"
treeClassName="max-h-[calc(100vh-6rem-4.5rem-300px)] h-[calc(100vh-6rem-4.5rem-300px)] min-h-[120px] relative"
accessory={NoteReadIndicator}
>
<NoteActionAside className="translate-y-full" />
</TocAside>
Expand All @@ -130,6 +133,14 @@ const NotePage = memo(({ note }: { note: NoteModel }) => {
</Suspense>
)
})
const NoteReadIndicator = () => {
return (
<li>
<Divider />
<ReadIndicator className="text-sm" />
</li>
)
}

const NoteTitle = () => {
const note = useNoteData()
Expand Down
28 changes: 28 additions & 0 deletions src/components/common/ReadIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
'use client'

import type { ElementType } from 'react'

import {
useArticleElementPositsion,
useArticleElementSize,
} from '~/providers/article/article-element-provider'
import { usePageScrollLocationSelector } from '~/providers/root/page-scroll-info-provider'
import { clsxm } from '~/utils/helper'

export const ReadIndicator: Component<{
as?: ElementType
}> = ({ className, as }) => {
const { y } = useArticleElementPositsion()
const { h } = useArticleElementSize()
const readPercent = usePageScrollLocationSelector((scrollTop) => {
return Math.floor(Math.min(Math.max(0, ((scrollTop - y) / h) * 100), 100))
})
const As = as || 'span'
return (
<As className={clsxm('text-gray-800 dark:text-neutral-300', className)}>
{readPercent}%
</As>
)
}
37 changes: 28 additions & 9 deletions src/components/widgets/toc/Toc.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'
import { motion } from 'framer-motion'
import { atom, useAtom } from 'jotai'
import type { FC } from 'react'
import type { ITocItem } from './TocItem'

import { RightToLeftTransitionView } from '~/components/ui/transition/RightToLeftTransitionView'
Expand All @@ -15,10 +16,14 @@ export type TocAsideProps = {
treeClassName?: string
}

export const TocAside: Component<TocAsideProps> = ({
interface TocSharedProps {
accessory?: React.ReactNode | React.FC
}
export const TocAside: Component<TocAsideProps & TocSharedProps> = ({
className,
children,
treeClassName,
accessory,
}) => {
const containerRef = useRef<HTMLUListElement>(null)
const $article = useArticleElement()
Expand Down Expand Up @@ -91,19 +96,30 @@ export const TocAside: Component<TocAsideProps> = ({
rootDepth={rootDepth}
containerRef={containerRef}
className={clsxm('absolute max-h-[75vh]', treeClassName)}
accessory={accessory}
/>
{children}
</aside>
)
}

const TocTree: Component<{
toc: ITocItem[]
activeId: string | null
setActiveId: (id: string | null) => void
rootDepth: number
containerRef: React.MutableRefObject<HTMLUListElement | null>
}> = ({ toc, activeId, setActiveId, rootDepth, containerRef, className }) => {
const TocTree: Component<
{
toc: ITocItem[]
activeId: string | null
setActiveId: (id: string | null) => void
rootDepth: number
containerRef: React.MutableRefObject<HTMLUListElement | null>
} & TocSharedProps
> = ({
toc,
activeId,
setActiveId,
rootDepth,
containerRef,
className,
accessory,
}) => {
const handleScrollTo = useCallback(
(i: number, $el: HTMLElement | null, anchorId: string) => {
if ($el) {
Expand Down Expand Up @@ -134,6 +150,9 @@ const TocTree: Component<{
/>
)
})}
{React.isValidElement(accessory)
? accessory
: React.createElement(accessory as FC)}
</ul>
)
}
Expand Down
18 changes: 16 additions & 2 deletions src/providers/article/article-element-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ const [
w: 0,
})

const [
ArticleElementPositsionProviderInternal,
useArticleElementPositsion,
useSetArticleElementPositsion,
] = createContextState({
x: 0,
y: 0,
})

const [
IsEOArticleElementProviderInternal,
useIsEOArticleElement,
Expand All @@ -29,6 +38,7 @@ const [
const Providers = [
<ArticleElementProviderInternal key="ArticleElementProviderInternal" />,
<ArticleElementSizeProviderInternal key="ArticleElementSizeProviderInternal" />,
<ArticleElementPositsionProviderInternal key="ArticleElementPositsionProviderInternal" />,
<IsEOArticleElementProviderInternal key="IsEOArticleElementProviderInternal" />,
]
const ArticleElementProvider: Component = ({ children, className }) => {
Expand All @@ -41,16 +51,19 @@ const ArticleElementProvider: Component = ({ children, className }) => {
}
const ArticleElementResizeObserver = () => {
const setSize = useSetArticleElementSize()
const setPos = useSetArticleElementPositsion()
const $article = useArticleElement()
useIsomorphicLayoutEffect(() => {
if (!$article) return
const { height, width } = $article.getBoundingClientRect()
const { height, width, x, y } = $article.getBoundingClientRect()
setSize({ h: height, w: width })
setPos({ x, y })

const observer = new ResizeObserver((entries) => {
const entry = entries[0]
const { height, width } = entry.contentRect
const { height, width, x, y } = entry.contentRect
setSize({ h: height, w: width })
setPos({ x, y })
})
observer.observe($article)
return () => {
Expand Down Expand Up @@ -107,4 +120,5 @@ export {
useArticleElement,
useIsEOArticleElement,
useArticleElementSize,
useArticleElementPositsion,
}

1 comment on commit 30d5395

@vercel
Copy link

@vercel vercel bot commented on 30d5395 Jun 20, 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:

springtide – ./

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

Please sign in to comment.