Skip to content

Commit

Permalink
feat: page paginator
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Jun 26, 2023
1 parent 5c0f853 commit a4b28a1
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 91 deletions.
131 changes: 40 additions & 91 deletions src/app/(page-detail)/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,52 @@
'use client'

import { useEffect } from 'react'
import { Balancer } from 'react-wrap-balancer'
import type { Image } from '@mx-space/api-client'
import type { PropsWithChildren } from 'react'

import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks'
import { Markdown } from '~/components/ui/markdown'
import { BottomToUpSoftScaleTransitionView } from '~/components/ui/transition/BottomToUpSoftScaleTransitionView'
import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView'
import { TocAside } from '~/components/widgets/toc'
import { noopArr } from '~/lib/noop'
import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider'
import { useCurrentPageDataSelector } from '~/providers/page/CurrentPageDataProvider'
import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider'
import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider'

import Loading from './loading'
import {
HeaderMetaInfoSetting,
MarkdownImageRecordProviderInternal,
PageLoading,
PagePaginator,
PageSubTitle,
PageTitle,
PostMarkdown,
} from './pageExtra'

const PageDetail = () => {
const id = useCurrentPageDataSelector((p) => p?.id)
const title = useCurrentPageDataSelector((p) => p?.title)
const subtitle = useCurrentPageDataSelector((p) => p?.subtitle)
if (!id) {
return <Loading />
}

return (
<div>
<HeaderMetaInfoSetting />
<article className="prose">
<header className="mb-8">
<BottomToUpSoftScaleTransitionView delay={0}>
<h1 className="text-center lg:text-left">
<Balancer>{title}</Balancer>
</h1>
</BottomToUpSoftScaleTransitionView>

<BottomToUpSoftScaleTransitionView delay={200}>
<p className="text-center text-lg text-gray-600/70 dark:text-neutral-400 lg:text-left">
{subtitle}
</p>
</BottomToUpSoftScaleTransitionView>
</header>
<WrappedElementProvider>
<MarkdownImageRecordProviderInternal>
<BottomToUpTransitionView delay={600}>
<PostMarkdown />
</BottomToUpTransitionView>
</MarkdownImageRecordProviderInternal>

<LayoutRightSidePortal>
<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"
/>
</LayoutRightSidePortal>
</WrappedElementProvider>
</article>
</div>
<PageLoading>
<div className="relative w-full min-w-0">
<HeaderMetaInfoSetting />
<article className="prose">
<header className="mb-8">
<BottomToUpSoftScaleTransitionView delay={0}>
<PageTitle />
</BottomToUpSoftScaleTransitionView>

<BottomToUpSoftScaleTransitionView delay={200}>
<PageSubTitle />
</BottomToUpSoftScaleTransitionView>
</header>
<WrappedElementProvider>
<MarkdownImageRecordProviderInternal>
<BottomToUpTransitionView delay={600}>
<PostMarkdown />
</BottomToUpTransitionView>
</MarkdownImageRecordProviderInternal>

<LayoutRightSidePortal>
<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"
/>
</LayoutRightSidePortal>
</WrappedElementProvider>
</article>
<PagePaginator />
</div>
</PageLoading>
)
}

const PostMarkdown = () => {
const text = useCurrentPageDataSelector((data) => data?.text)
if (!text) return null

return <Markdown value={text} as="main" className="min-w-0 overflow-hidden" />
}
const MarkdownImageRecordProviderInternal = (props: PropsWithChildren) => {
const images = useCurrentPageDataSelector(
(data) => data?.images || (noopArr as Image[]),
)
if (!images) return null

return (
<MarkdownImageRecordProvider images={images}>
{props.children}
</MarkdownImageRecordProvider>
)
}

const HeaderMetaInfoSetting = () => {
const setHeaderMetaInfo = useSetHeaderMetaInfo()
const meta = useCurrentPageDataSelector((data) => {
if (!data) return null

return {
title: data.title,
description: data.subtitle || '',
slug: `/${data.slug}`,
}
})

useEffect(() => {
if (meta) setHeaderMetaInfo(meta)
}, [meta])

return null
}

export default PageDetail
122 changes: 122 additions & 0 deletions src/app/(page-detail)/[slug]/pageExtra.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use client'

import { Fragment, useEffect, useMemo } from 'react'
import { Balancer } from 'react-wrap-balancer'
import Link from 'next/link'
import type { Image } from '@mx-space/api-client'
import type { PropsWithChildren } from 'react'

import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks'
import { Markdown } from '~/components/ui/markdown'
import { noopArr } from '~/lib/noop'
import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider'
import { useCurrentPageDataSelector } from '~/providers/page/CurrentPageDataProvider'
import { useAggregationSelector } from '~/providers/root/aggregation-data-provider'

import Loading from './loading'

export const PageLoading: Component = ({ children }) => {
const id = useCurrentPageDataSelector((p) => p?.id)

if (!id) {
return <Loading />
}

return children
}

export const PostMarkdown = () => {
const text = useCurrentPageDataSelector((data) => data?.text)
if (!text) return null

return <Markdown value={text} as="main" className="min-w-0 overflow-hidden" />
}
export const MarkdownImageRecordProviderInternal = (
props: PropsWithChildren,
) => {
const images = useCurrentPageDataSelector(
(data) => data?.images || (noopArr as Image[]),
)
if (!images) return null

return (
<MarkdownImageRecordProvider images={images}>
{props.children}
</MarkdownImageRecordProvider>
)
}

export const PageSubTitle = () => {
const subtitle = useCurrentPageDataSelector((data) => data?.subtitle)
return (
<p className="text-center text-lg text-gray-600/70 dark:text-neutral-400 lg:text-left">
{subtitle}
</p>
)
}
export const PageTitle = () => {
const title = useCurrentPageDataSelector((data) => data?.title)
return (
<h1 className="text-center lg:text-left">
<Balancer>{title}</Balancer>
</h1>
)
}
export const HeaderMetaInfoSetting = () => {
const setHeaderMetaInfo = useSetHeaderMetaInfo()
const meta = useCurrentPageDataSelector((data) => {
if (!data) return null

return {
title: data.title,
description: data.subtitle || '',
slug: `/${data.slug}`,
}
})

useEffect(() => {
if (meta) setHeaderMetaInfo(meta)
}, [meta])

return null
}

export const PagePaginator = () => {
const currentPageTitle = useCurrentPageDataSelector((d) => d?.title)
const pageMeta = useAggregationSelector((d) => d.pageMeta)
const pages = useMemo(() => pageMeta || [], [pageMeta])
const indexInPages = pages.findIndex((i) => i.title == currentPageTitle)
const n = pages.length
const hasNext = indexInPages + 1 < n
const hasPrev = indexInPages - 1 >= 0
return (
<div className="relative grid h-20 select-none grid-cols-2">
<div className="justify-start">
{hasPrev && (
<Fragment>
<Link
href={`/${pages[indexInPages - 1].slug}`}
className="flex flex-col justify-end text-left leading-loose"
>
<span className="text-primary">回顾一下:</span>
<span>{pages[indexInPages - 1].title}</span>
</Link>
</Fragment>
)}
</div>
<div className="justify-end">
{hasNext && (
<Fragment>
<Link
href={`/${pages[indexInPages + 1].slug}`}
className="flex flex-col justify-end text-right leading-loose"
>
<span className="text-primary">继续了解:</span>
<span>{pages[indexInPages + 1].title}</span>
</Link>
</Fragment>
)}
</div>
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/widgets/toc/TocItem.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
import { tv } from 'tailwind-variants'
import type { FC, MouseEvent } from 'react'
Expand Down
2 changes: 2 additions & 0 deletions src/providers/shared/WrappedElementProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import { memo, useEffect, useRef } from 'react'
import { createContextState } from 'foxact/create-context-state'
import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
Expand Down

0 comments on commit a4b28a1

Please sign in to comment.