mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-09 07:26:47 +00:00
feature:
目录组件百分比进度
This commit is contained in:
@@ -165,10 +165,13 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
|
|||||||
{/* 悬浮目录按钮 */}
|
{/* 悬浮目录按钮 */}
|
||||||
<div className="block lg:hidden">
|
<div className="block lg:hidden">
|
||||||
<TocDrawerButton onClick={() => { drawerRight.current.handleSwitchVisible() }} />
|
<TocDrawerButton onClick={() => { drawerRight.current.handleSwitchVisible() }} />
|
||||||
<TocDrawer post={post} cRef={drawerRight} />
|
<TocDrawer post={post} cRef={drawerRight} targetRef={targetRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Progress targetRef={targetRef} />
|
{/* 移动端顶部进度条 */}
|
||||||
|
{/* <div className='fixed left-0 top-0 w-full z-40 block md:hidden'>
|
||||||
|
<Progress targetRef={targetRef} />
|
||||||
|
</div> */}
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import smoothscroll from 'smoothscroll-polyfill'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const JumpToBottomButton = ({ targetRef, showPercent = true }) => {
|
const JumpToBottomButton = ({ targetRef, showPercent = false }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import smoothscroll from 'smoothscroll-polyfill'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const JumpToTopButton = ({ targetRef, showPercent = true }) => {
|
const JumpToTopButton = ({ targetRef, showPercent = false }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
@@ -39,7 +39,7 @@ const JumpToTopButton = ({ targetRef, showPercent = true }) => {
|
|||||||
|
|
||||||
return (<div id='jump-to-top' className='right-3 fixed flex bottom-48 duration-500 z-20'>
|
return (<div id='jump-to-top' className='right-3 fixed flex bottom-48 duration-500 z-20'>
|
||||||
<div onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
<div onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||||
className={(show ? '' : 'hidden') + ' animate__fadeInRight animate__animated animate__faster shadow-card rounded-xl glassmorphism py-1 cursor-pointer '}>
|
className={(show ? '' : 'hidden') + ' animate__fadeInRight animate__animated animate__faster shadow-card rounded-xl glassmorphism py-3 cursor-pointer '}>
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<div className='w-10 dark:text-gray-200 transform hover:scale-150 duration-200 text-xs' title={locale.POST.TOP} >
|
<div className='w-10 dark:text-gray-200 transform hover:scale-150 duration-200 text-xs' title={locale.POST.TOP} >
|
||||||
<FontAwesomeIcon icon={faArrowUp} />
|
<FontAwesomeIcon icon={faArrowUp} />
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
const Progress = ({ targetRef }) => {
|
const Progress = ({ targetRef }) => {
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
if (targetRef.current) {
|
if (targetRef?.current) {
|
||||||
const clientHeight = targetRef ? (targetRef.current.clientHeight) : 0
|
const clientHeight = targetRef ? (targetRef.current.clientHeight) : 0
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
const fullHeight = clientHeight - window.outerHeight
|
const fullHeight = clientHeight - window.outerHeight
|
||||||
@@ -23,8 +23,9 @@ const Progress = ({ targetRef }) => {
|
|||||||
return () => document.removeEventListener('scroll', scrollListener)
|
return () => document.removeEventListener('scroll', scrollListener)
|
||||||
}, [percent])
|
}, [percent])
|
||||||
|
|
||||||
return (<div className='h-1 fixed left-0 top-0 w-full shadow-2xl z-40 bg-blue-200'>
|
return (<div className='h-4 w-full shadow-2xl bg-purple-400'>
|
||||||
<div className='h-1 bg-blue-600 fixed left-0 top-0 duration-200 rounded' style={{ width: `${percent}%` }}/>
|
<div className='text-center w-full absolute text-white text-xs'>{percent}%</div>
|
||||||
|
<div className='h-4 bg-purple-700 duration-200 rounded-r' style={{ width: `${percent}%` }}/>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { faAngleDoubleRight, faChartBar, faThList } from '@fortawesome/free-soli
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import Progress from './Progress'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 侧边平铺
|
* 侧边平铺
|
||||||
@@ -21,7 +22,7 @@ import React from 'react'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, currentCategory, currentSearch }) => {
|
const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, currentCategory, currentSearch, targetRef }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const showToc = post && post.toc && post.toc.length > 1
|
const showToc = post && post.toc && post.toc.length > 1
|
||||||
const postCount = posts?.length || 0
|
const postCount = posts?.length || 0
|
||||||
@@ -71,10 +72,10 @@ const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, curren
|
|||||||
|
|
||||||
{showToc && (
|
{showToc && (
|
||||||
<section className='sticky top-8 pb-20 rounded-xl shadow-md bg-white dark:bg-gray-800 hover:shadow-2xl duration-200'>
|
<section className='sticky top-8 pb-20 rounded-xl shadow-md bg-white dark:bg-gray-800 hover:shadow-2xl duration-200'>
|
||||||
<div className='border-b text-2xl bg-white text-black rounded-t-xl dark:border-gray-700 dark:bg-gray-700 dark:text-white py-6 px-6'>
|
<div className='border-b text-center text-2xl bg-white text-black rounded-t-xl dark:border-gray-700 dark:bg-gray-700 dark:text-white py-6 px-6'>
|
||||||
{locale.COMMON.TABLE_OF_CONTENTS}
|
{locale.COMMON.TABLE_OF_CONTENTS}
|
||||||
</div>
|
</div>
|
||||||
<Toc toc={post.toc} />
|
<Toc toc={post.toc} targetRef={targetRef} />
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import { uuidToId } from 'notion-utils'
|
import { uuidToId } from 'notion-utils'
|
||||||
|
import Progress from './Progress'
|
||||||
// import { cs } from 'react-notion-x'
|
// import { cs } from 'react-notion-x'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +10,7 @@ import { uuidToId } from 'notion-utils'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const Toc = ({ toc }) => {
|
const Toc = ({ toc, targetRef }) => {
|
||||||
// 无目录就直接返回空
|
// 无目录就直接返回空
|
||||||
if (!toc || toc.length < 1) return <></>
|
if (!toc || toc.length < 1) return <></>
|
||||||
|
|
||||||
@@ -51,6 +52,9 @@ const Toc = ({ toc }) => {
|
|||||||
}, throttleMs))
|
}, throttleMs))
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
<div className='w-full'>
|
||||||
|
<Progress targetRef={targetRef}/>
|
||||||
|
</div>
|
||||||
<nav className=' dark:text-gray-100 bg-white dark:bg-gray-800 overflow-y-auto scroll-hidden p-6'>
|
<nav className=' dark:text-gray-100 bg-white dark:bg-gray-800 overflow-y-auto scroll-hidden p-6'>
|
||||||
{toc.map((tocItem) => {
|
{toc.map((tocItem) => {
|
||||||
const id = uuidToId(tocItem.id)
|
const id = uuidToId(tocItem.id)
|
||||||
@@ -58,9 +62,9 @@ const Toc = ({ toc }) => {
|
|||||||
<a
|
<a
|
||||||
key={id}
|
key={id}
|
||||||
href={`#${id}`}
|
href={`#${id}`}
|
||||||
className={`notion-table-of-contents-item duration-300 transform font-light
|
className={`notion-table-of-contents-item duration-300 transform font-mono
|
||||||
notion-table-of-contents-item-indent-level-${tocItem.indentLevel}
|
notion-table-of-contents-item-indent-level-${tocItem.indentLevel}
|
||||||
${activeSection === id && ' font-bold text-blue-400 dark:text-white'}`}
|
${activeSection === id && ' font-bold text-purple-500 dark:text-purple-400'}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useGlobal } from '@/lib/global'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const TocDrawer = ({ post, cRef }) => {
|
const TocDrawer = ({ post, cRef, targetRef }) => {
|
||||||
// 暴露给父组件 通过cRef.current.handleMenuClick 调用
|
// 暴露给父组件 通过cRef.current.handleMenuClick 调用
|
||||||
useImperativeHandle(cRef, () => {
|
useImperativeHandle(cRef, () => {
|
||||||
return {
|
return {
|
||||||
@@ -26,13 +26,13 @@ const TocDrawer = ({ post, cRef }) => {
|
|||||||
{/* 侧边菜单 */}
|
{/* 侧边菜单 */}
|
||||||
<div
|
<div
|
||||||
className={(showDrawer ? 'animate__slideInRight ' : ' -mr-72 animate__slideOutRight') +
|
className={(showDrawer ? 'animate__slideInRight ' : ' -mr-72 animate__slideOutRight') +
|
||||||
' shadow-xl animate__animated animate__faster max-h-96 ' +
|
' shadow-card animate__animated animate__faster max-h-96 ' +
|
||||||
' w-60 duration-200 fixed right-4 top-16 rounded overflow-y-auto'}>
|
' w-60 duration-200 fixed right-4 top-16 rounded overflow-y-auto'}>
|
||||||
{post && <>
|
{post && <>
|
||||||
<div className='text-xl font-bold text-black dark:text-white bg-gray-200 dark:bg-gray-600 py-2 px-6'>
|
<div className='text-xl font-bold text-center text-black dark:text-white bg-white dark:bg-gray-600 py-2 px-6'>
|
||||||
{locale.COMMON.TABLE_OF_CONTENTS}
|
{locale.COMMON.TABLE_OF_CONTENTS}
|
||||||
</div>
|
</div>
|
||||||
<Toc toc={post.toc}/>
|
<Toc toc={post.toc} targetRef={targetRef}/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const BaseLayout = ({
|
|||||||
<div className='flex justify-center flex-1 mx-auto md:pt-8 pb-12'>
|
<div className='flex justify-center flex-1 mx-auto md:pt-8 pb-12'>
|
||||||
|
|
||||||
<div id='left' className='hidden lg:block flex-col w-72'>
|
<div id='left' className='hidden lg:block flex-col w-72'>
|
||||||
<SideAreaLeft title={meta.title} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory} />
|
<SideAreaLeft targetRef={targetRef} title={meta.title} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='center' className='flex-grow max-w-4xl min-h-screen md:mx-10' ref={targetRef}>
|
<div id='center' className='flex-grow max-w-4xl min-h-screen md:mx-10' ref={targetRef}>
|
||||||
@@ -94,7 +94,7 @@ const BaseLayout = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer title={meta.title}/>
|
<Footer title={meta.title}/>
|
||||||
<JumpToTopButton targetRef={targetRef} showPercent={true} />
|
<JumpToTopButton targetRef={targetRef} showPercent={false} />
|
||||||
<JumpToBottomButton targetRef={targetRef} showPercent={false}/>
|
<JumpToBottomButton targetRef={targetRef} showPercent={false}/>
|
||||||
<FloatDarkModeButton/>
|
<FloatDarkModeButton/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
7
lib/cache/cache_manager.js
vendored
7
lib/cache/cache_manager.js
vendored
@@ -1,4 +1,5 @@
|
|||||||
import { getCacheFromMemory, setCacheToMemory } from '@/lib/cache/memory_cache'
|
// import { getCacheFromMemory, setCacheToMemory } from '@/lib/cache/memory_cache'
|
||||||
|
import { getCacheFromFile, setCacheToFile } from './local_file_cache'
|
||||||
const enableCache = true // 生产环境禁用
|
const enableCache = true // 生产环境禁用
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,7 +11,7 @@ export async function getDataFromCache (key) {
|
|||||||
if (!enableCache) {
|
if (!enableCache) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const dataFromCache = await getCacheFromMemory(key)
|
const dataFromCache = await getCacheFromFile(key)
|
||||||
if (JSON.stringify(dataFromCache) === '[]') {
|
if (JSON.stringify(dataFromCache) === '[]') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -21,5 +22,5 @@ export async function setDataToCache (key, data) {
|
|||||||
if (!enableCache || !data) {
|
if (!enableCache || !data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await setCacheToMemory(key, data)
|
await setCacheToFile(key, data)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user