mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-31 15:10:11 +00:00
Merge branch 'main' into original-fix
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import Link from 'next/link'
|
||||
import { useMediumGlobal } from '@/themes/medium'
|
||||
import { useMediumGlobal } from '..'
|
||||
import JumpToTopButton from './JumpToTopButton'
|
||||
|
||||
export default function BottomMenuBar ({ post, className }) {
|
||||
export default function BottomMenuBar({ post, className }) {
|
||||
const { tocVisible, changeTocVisible } = useMediumGlobal()
|
||||
const showTocButton = post?.toc?.length > 0
|
||||
|
||||
@@ -11,24 +11,34 @@ export default function BottomMenuBar ({ post, className }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'sticky z-10 bottom-0 w-full h-12 bg-white dark:bg-hexo-black-gray ' + className}>
|
||||
<div
|
||||
className={
|
||||
'sticky z-10 bottom-0 w-full h-12 bg-white dark:bg-hexo-black-gray ' +
|
||||
className
|
||||
}>
|
||||
<div className='flex justify-between h-full shadow-card'>
|
||||
<Link href='/search' passHref legacyBehavior>
|
||||
<div className='flex w-full items-center justify-center cursor-pointer'>
|
||||
<i className='fas fa-search'/>
|
||||
<i className='fas fa-search' />
|
||||
</div>
|
||||
</Link>
|
||||
<div className='flex w-full items-center justify-center cursor-pointer z-20'>
|
||||
<JumpToTopButton/>
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
{showTocButton && <div onClick={toggleToc} className='flex w-full items-center justify-center cursor-pointer z-30'>
|
||||
<i className='fas fa-list-ol ' />
|
||||
</div>}
|
||||
{ !showTocButton && <Link href='/' passHref legacyBehavior>
|
||||
<div className='flex w-full items-center justify-center cursor-pointer'>
|
||||
<i className='fas fa-home' />
|
||||
{showTocButton && (
|
||||
<div
|
||||
onClick={toggleToc}
|
||||
className='flex w-full items-center justify-center cursor-pointer z-30'>
|
||||
<i className='fas fa-list-ol ' />
|
||||
</div>
|
||||
</Link>}
|
||||
)}
|
||||
{!showTocButton && (
|
||||
<Link href='/' passHref legacyBehavior>
|
||||
<div className='flex w-full items-center justify-center cursor-pointer'>
|
||||
<i className='fas fa-home' />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { uuidToId } from 'notion-utils'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import Progress from './Progress'
|
||||
|
||||
/**
|
||||
@@ -27,65 +27,73 @@ const Catalog = ({ toc }) => {
|
||||
}, [])
|
||||
|
||||
const throttleMs = 200
|
||||
const actionSectionScrollSpy = useCallback(throttle(() => {
|
||||
const sections = document.getElementsByClassName('notion-h')
|
||||
let prevBBox = null
|
||||
let currentSectionId = activeSection
|
||||
for (let i = 0; i < sections.length; ++i) {
|
||||
const section = sections[i]
|
||||
if (!section || !(section instanceof Element)) continue
|
||||
if (!currentSectionId) {
|
||||
currentSectionId = section.getAttribute('data-id')
|
||||
const actionSectionScrollSpy = useCallback(
|
||||
throttle(() => {
|
||||
const sections = document.getElementsByClassName('notion-h')
|
||||
let prevBBox = null
|
||||
let currentSectionId = activeSection
|
||||
for (let i = 0; i < sections.length; ++i) {
|
||||
const section = sections[i]
|
||||
if (!section || !(section instanceof Element)) continue
|
||||
if (!currentSectionId) {
|
||||
currentSectionId = section.getAttribute('data-id')
|
||||
}
|
||||
const bbox = section.getBoundingClientRect()
|
||||
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
|
||||
const offset = Math.max(150, prevHeight / 4)
|
||||
// GetBoundingClientRect returns values relative to viewport
|
||||
if (bbox.top - offset < 0) {
|
||||
currentSectionId = section.getAttribute('data-id')
|
||||
prevBBox = bbox
|
||||
continue
|
||||
}
|
||||
// No need to continue loop, if last element has been detected
|
||||
break
|
||||
}
|
||||
const bbox = section.getBoundingClientRect()
|
||||
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
|
||||
const offset = Math.max(150, prevHeight / 4)
|
||||
// GetBoundingClientRect returns values relative to viewport
|
||||
if (bbox.top - offset < 0) {
|
||||
currentSectionId = section.getAttribute('data-id')
|
||||
prevBBox = bbox
|
||||
continue
|
||||
}
|
||||
// No need to continue loop, if last element has been detected
|
||||
break
|
||||
}
|
||||
setActiveSection(currentSectionId)
|
||||
const index = tocIds.indexOf(currentSectionId) || 0
|
||||
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
|
||||
}, throttleMs))
|
||||
setActiveSection(currentSectionId)
|
||||
const index = tocIds.indexOf(currentSectionId) || 0
|
||||
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
|
||||
}, throttleMs)
|
||||
)
|
||||
|
||||
// 无目录就直接返回空
|
||||
if (!toc || toc.length < 1) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <div className='px-3'>
|
||||
<div className='w-full mt-2 mb-4'>
|
||||
<Progress />
|
||||
return (
|
||||
<div className='px-3'>
|
||||
<div className='w-full mt-2 mb-4'>
|
||||
<Progress />
|
||||
</div>
|
||||
<div
|
||||
className='overflow-y-auto max-h-44 overscroll-none scroll-hidden'
|
||||
ref={tRef}>
|
||||
<nav className='h-full text-black'>
|
||||
{toc.map(tocItem => {
|
||||
const id = uuidToId(tocItem.id)
|
||||
tocIds.push(id)
|
||||
return (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
className={`notion-table-of-contents-item duration-300 transform font-light dark:text-gray-300
|
||||
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} catalog-item `}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
marginLeft: tocItem.indentLevel * 16
|
||||
}}
|
||||
className={`truncate ${activeSection === id ? 'font-bold text-green-500 underline' : ''}`}>
|
||||
{tocItem.text}
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div className='overflow-y-auto max-h-44 overscroll-none scroll-hidden' ref={tRef}>
|
||||
<nav className='h-full text-black'>
|
||||
{toc.map((tocItem) => {
|
||||
const id = uuidToId(tocItem.id)
|
||||
tocIds.push(id)
|
||||
return (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
className={`notion-table-of-contents-item duration-300 transform font-light dark:text-gray-300
|
||||
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}
|
||||
>
|
||||
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }}
|
||||
className={`truncate ${activeSection === id ? 'font-bold text-green-500 underline' : ''}`}
|
||||
>
|
||||
{tocItem.text}
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Catalog
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BeiAnGongAn } from '@/components/BeiAnGongAn'
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
@@ -5,24 +6,51 @@ const Footer = ({ title }) => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const since = siteConfig('SINCE')
|
||||
const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||
const copyrightDate =
|
||||
parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||
|
||||
return (
|
||||
<footer
|
||||
className='z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 relative'
|
||||
>
|
||||
<DarkModeButton/>
|
||||
<i className='fas fa-copyright' /> {`${copyrightDate}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={siteConfig('LINK')} className='underline font-bold text-gray-500 dark:text-gray-300 '>{siteConfig('AUTHOR')}</a>.<br/>
|
||||
|
||||
{siteConfig('BEI_AN') && <><i className='fas fa-shield-alt'/> <a href='https://beian.miit.gov.cn/' className='mr-2'>{siteConfig('BEI_AN')}</a><br/></>}
|
||||
|
||||
<span className='hidden busuanzi_container_site_pv'>
|
||||
<i className='fas fa-eye'/><span className='px-1 busuanzi_value_site_pv'> </span> </span>
|
||||
<span className='pl-2 hidden busuanzi_container_site_uv'>
|
||||
<i className='fas fa-users'/> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
|
||||
<br/>
|
||||
<footer className='z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 relative'>
|
||||
<DarkModeButton />
|
||||
<i className='fas fa-copyright' /> {`${copyrightDate}`}
|
||||
<span>
|
||||
<i className='mx-1 animate-pulse fas fa-heart' />
|
||||
<a
|
||||
href={siteConfig('LINK')}
|
||||
className='underline font-bold text-gray-500 dark:text-gray-300 '>
|
||||
{siteConfig('AUTHOR')}
|
||||
</a>
|
||||
.<br />
|
||||
{siteConfig('BEI_AN') && (
|
||||
<>
|
||||
<i className='fas fa-shield-alt' />
|
||||
<a href='https://beian.miit.gov.cn/' className='mr-2'>
|
||||
{siteConfig('BEI_AN')}
|
||||
</a>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<BeiAnGongAn />
|
||||
<span className='hidden busuanzi_container_site_pv'>
|
||||
<i className='fas fa-eye' />
|
||||
<span className='px-1 busuanzi_value_site_pv'> </span>
|
||||
</span>
|
||||
<span className='pl-2 hidden busuanzi_container_site_uv'>
|
||||
<i className='fas fa-users' />
|
||||
<span className='px-1 busuanzi_value_site_uv'> </span>
|
||||
</span>
|
||||
<br />
|
||||
<h1>{title}</h1>
|
||||
<span className='text-xs font-serif'>Powered by <a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {siteConfig('VERSION')}</a>.</span></span>
|
||||
<span className='text-xs font-serif'>
|
||||
Powered by
|
||||
<a
|
||||
href='https://github.com/tangly1024/NotionNext'
|
||||
className='underline text-gray-500 dark:text-gray-300'>
|
||||
NotionNext {siteConfig('VERSION')}
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</span>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function LogoBar (props) {
|
||||
export default function LogoBar(props) {
|
||||
return (
|
||||
<div id='top-wrapper' className='w-full flex items-center '>
|
||||
<Link href='/' className='text-md md:text-xl dark:text-gray-200'>
|
||||
{siteConfig('TITLE')}
|
||||
</Link>
|
||||
<Link href='/' className='logo text-md md:text-xl dark:text-gray-200'>
|
||||
{siteConfig('TITLE')}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMediumGlobal } from '..'
|
||||
import Catalog from './Catalog'
|
||||
import { useMediumGlobal } from '@/themes/medium'
|
||||
|
||||
/**
|
||||
* 悬浮抽屉目录
|
||||
@@ -13,22 +13,36 @@ const TocDrawer = ({ post, cRef }) => {
|
||||
const switchVisible = () => {
|
||||
changeTocVisible(!tocVisible)
|
||||
}
|
||||
return <>
|
||||
<div id='medium-toc-float' className='fixed top-0 right-0 z-40'>
|
||||
{/* 侧边菜单 */}
|
||||
<div
|
||||
className={(tocVisible ? 'animate__slideInRight ' : ' -mr-72 animate__slideOutRight') +
|
||||
' overflow-y-hidden shadow-card w-60 duration-200 fixed right-1 bottom-16 rounded py-2 bg-white dark:bg-gray-600'}>
|
||||
{post && <>
|
||||
<div className='dark:text-gray-400 text-gray-600 h-56'>
|
||||
<Catalog toc={post.toc}/>
|
||||
</div>
|
||||
</>}
|
||||
return (
|
||||
<>
|
||||
<div id='medium-toc-float' className='fixed top-0 right-0 z-40'>
|
||||
{/* 侧边菜单 */}
|
||||
<div
|
||||
className={
|
||||
(tocVisible
|
||||
? 'animate__slideInRight '
|
||||
: ' -mr-72 animate__slideOutRight') +
|
||||
' overflow-y-hidden shadow-card w-60 duration-200 fixed right-1 bottom-16 rounded py-2 bg-white dark:bg-gray-600'
|
||||
}>
|
||||
{post && (
|
||||
<>
|
||||
<div className='dark:text-gray-400 text-gray-600 h-56'>
|
||||
<Catalog toc={post.toc} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 背景蒙版 */}
|
||||
<div id='right-drawer-background' className={(tocVisible ? 'block' : 'hidden') + ' fixed top-0 left-0 z-30 w-full h-full'}
|
||||
onClick={switchVisible} />
|
||||
</>
|
||||
{/* 背景蒙版 */}
|
||||
<div
|
||||
id='right-drawer-background'
|
||||
className={
|
||||
(tocVisible ? 'block' : 'hidden') +
|
||||
' fixed top-0 left-0 z-30 w-full h-full'
|
||||
}
|
||||
onClick={switchVisible}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default TocDrawer
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import CONFIG from './config'
|
||||
import { useState, createContext, useContext, useEffect } from 'react'
|
||||
import Footer from './components/Footer'
|
||||
import InfoCard from './components/InfoCard'
|
||||
import RevolverMaps from './components/RevolverMaps'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import TopNavBar from './components/TopNavBar'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import BottomMenuBar from './components/BottomMenuBar'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import Comment from '@/components/Comment'
|
||||
import Live2D from '@/components/Live2D'
|
||||
import Announcement from './components/Announcement'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import Catalog from './components/Catalog'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import TagGroups from './components/TagGroups'
|
||||
import CategoryGroup from './components/CategoryGroup'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import Announcement from './components/Announcement'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import ArticleInfo from './components/ArticleInfo'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||
import BlogPostBar from './components/BlogPostBar'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Comment from '@/components/Comment'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import BottomMenuBar from './components/BottomMenuBar'
|
||||
import Catalog from './components/Catalog'
|
||||
import CategoryGroup from './components/CategoryGroup'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import Footer from './components/Footer'
|
||||
import InfoCard from './components/InfoCard'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import RevolverMaps from './components/RevolverMaps'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import TagGroups from './components/TagGroups'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import Link from 'next/link'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
import TopNavBar from './components/TopNavBar'
|
||||
import CONFIG from './config'
|
||||
import { Style } from './style'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import ArticleInfo from './components/ArticleInfo'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
// 主题全局状态
|
||||
const ThemeGlobalMedium = createContext()
|
||||
@@ -51,86 +51,96 @@ const LayoutBase = props => {
|
||||
const router = useRouter()
|
||||
const [tocVisible, changeTocVisible] = useState(false)
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
const [slotRight, setSlotRight] = useState(null);
|
||||
const [slotRight, setSlotRight] = useState(null)
|
||||
|
||||
useEffect(()=> {
|
||||
useEffect(() => {
|
||||
if (post?.toc?.length > 0) {
|
||||
setSlotRight(
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS}>
|
||||
<Catalog toc={post?.toc} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
} else {
|
||||
setSlotRight(null);
|
||||
setSlotRight(null)
|
||||
}
|
||||
},[post])
|
||||
}, [post])
|
||||
|
||||
const slotTop = <BlogPostBar {...props} />
|
||||
|
||||
return (
|
||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
||||
{/* CSS样式 */}
|
||||
<Style />
|
||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
||||
{/* CSS样式 */}
|
||||
<Style />
|
||||
|
||||
<div id='theme-medium' className={`${siteConfig('FONT_STYLE')} bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300 scroll-smooth`}>
|
||||
<div
|
||||
id='theme-medium'
|
||||
className={`${siteConfig('FONT_STYLE')} bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300 scroll-smooth`}>
|
||||
<main
|
||||
id='wrapper'
|
||||
className={
|
||||
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
||||
? 'flex-row-reverse'
|
||||
: '') + 'relative flex justify-between w-full h-full mx-auto'
|
||||
}>
|
||||
{/* 桌面端左侧菜单 */}
|
||||
{/* <LeftMenuBar/> */}
|
||||
|
||||
<main id='wrapper' className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
|
||||
{/* 桌面端左侧菜单 */}
|
||||
{/* <LeftMenuBar/> */}
|
||||
{/* 主区 */}
|
||||
<div id='container-wrapper' className='w-full relative z-10'>
|
||||
{/* 顶部导航栏 */}
|
||||
<TopNavBar {...props} />
|
||||
|
||||
{/* 主区 */}
|
||||
<div id='container-wrapper' className='w-full relative z-10'>
|
||||
<div
|
||||
id='container-inner'
|
||||
className={`px-7 ${fullWidth ? '' : 'max-w-5xl'} justify-center mx-auto min-h-screen`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
enter='transition ease-in-out duration-700 transform order-first'
|
||||
enterFrom='opacity-0 translate-y-16'
|
||||
enterTo='opacity-100'
|
||||
leave='transition ease-in-out duration-300 transform'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0 -translate-y-16'
|
||||
unmount={false}>
|
||||
{slotTop}
|
||||
{children}
|
||||
</Transition>
|
||||
|
||||
{/* 顶部导航栏 */}
|
||||
<TopNavBar {...props} />
|
||||
|
||||
<div id='container-inner' className={`px-7 ${fullWidth ? '' : 'max-w-5xl'} justify-center mx-auto min-h-screen`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
enter="transition ease-in-out duration-700 transform order-first"
|
||||
enterFrom="opacity-0 translate-y-16"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0 -translate-y-16"
|
||||
unmount={false}
|
||||
>
|
||||
{slotTop}
|
||||
{children}
|
||||
</Transition>
|
||||
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
|
||||
{/* 底部 */}
|
||||
<Footer title={siteConfig('TITLE')} />
|
||||
</div>
|
||||
|
||||
{/* 桌面端右侧 */}
|
||||
{fullWidth
|
||||
? null
|
||||
: <div className={`hidden xl:block border-l dark:border-transparent w-80 flex-shrink-0 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<Tabs>
|
||||
{slotRight}
|
||||
<div key={locale.NAV.ABOUT}>
|
||||
{router.pathname !== '/search' && <SearchInput className='mt-6 mb-12' />}
|
||||
{showInfoCard && <InfoCard {...props} />}
|
||||
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
||||
</div>
|
||||
</Tabs>
|
||||
<Announcement post={notice} />
|
||||
<Live2D />
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
</main>
|
||||
|
||||
{/* 移动端底部导航栏 */}
|
||||
<BottomMenuBar {...props} className='block md:hidden' />
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
</ThemeGlobalMedium.Provider>
|
||||
|
||||
{/* 底部 */}
|
||||
<Footer title={siteConfig('TITLE')} />
|
||||
</div>
|
||||
|
||||
{/* 桌面端右侧 */}
|
||||
{fullWidth ? null : (
|
||||
<div
|
||||
className={`hidden xl:block border-l dark:border-transparent w-80 flex-shrink-0 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<Tabs>
|
||||
{slotRight}
|
||||
<div key={locale.NAV.ABOUT}>
|
||||
{router.pathname !== '/search' && (
|
||||
<SearchInput className='mt-6 mb-12' />
|
||||
)}
|
||||
{showInfoCard && <InfoCard {...props} />}
|
||||
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) ===
|
||||
'true' && <RevolverMaps />}
|
||||
</div>
|
||||
</Tabs>
|
||||
<Announcement post={notice} />
|
||||
<Live2D />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
||||
{/* 移动端底部导航栏 */}
|
||||
<BottomMenuBar {...props} className='block md:hidden' />
|
||||
</div>
|
||||
</ThemeGlobalMedium.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,7 +150,7 @@ const LayoutBase = props => {
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const LayoutIndex = (props) => {
|
||||
const LayoutIndex = props => {
|
||||
return <LayoutPostList {...props} />
|
||||
}
|
||||
|
||||
@@ -148,10 +158,16 @@ const LayoutIndex = (props) => {
|
||||
* 博客列表
|
||||
* @returns
|
||||
*/
|
||||
const LayoutPostList = (props) => {
|
||||
return <>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||
const LayoutPostList = props => {
|
||||
return (
|
||||
<>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogPostListPage {...props} />
|
||||
) : (
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,64 +179,75 @@ const LayoutSlug = props => {
|
||||
const { post, prev, next, lock, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
const slotRight = post?.toc && post?.toc?.length >= 3 && (
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
||||
<Catalog toc={post?.toc} />
|
||||
</div>
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS}>
|
||||
<Catalog toc={post?.toc} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const router = useRouter()
|
||||
const waiting404 = siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||
useEffect(() => {
|
||||
// 404
|
||||
if (!post) {
|
||||
setTimeout(() => {
|
||||
if (isBrowser) {
|
||||
const article = document.getElementById('notion-article')
|
||||
if (!article) {
|
||||
router.push('/404').then(() => {
|
||||
console.warn('找不到页面', router.asPath)
|
||||
})
|
||||
setTimeout(
|
||||
() => {
|
||||
if (isBrowser) {
|
||||
const article = document.querySelector(
|
||||
'#article-wrapper #notion-article'
|
||||
)
|
||||
if (!article) {
|
||||
router.push('/404').then(() => {
|
||||
console.warn('找不到页面', router.asPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
||||
},
|
||||
waiting404
|
||||
)
|
||||
}
|
||||
}, [post])
|
||||
|
||||
return (
|
||||
<div {...props} >
|
||||
{/* 文章锁 */}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
<div>
|
||||
{/* 文章锁 */}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id='article-wrapper'>
|
||||
{!lock && post && (
|
||||
<div>
|
||||
{/* 文章信息 */}
|
||||
<ArticleInfo {...props} />
|
||||
|
||||
{/* 文章信息 */}
|
||||
<ArticleInfo {...props} />
|
||||
{/* Notion文章主体 */}
|
||||
<article id='article-wrapper' className='px-1 max-w-4xl'>
|
||||
{post && <NotionPage post={post} />}
|
||||
</article>
|
||||
|
||||
{/* Notion文章主体 */}
|
||||
<section className="px-1 max-w-4xl">
|
||||
{post && (<NotionPage post={post} />)}
|
||||
</section>
|
||||
{/* 文章底部区域 */}
|
||||
<section>
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{/* 文章分类和标签信息 */}
|
||||
<div className='flex justify-between'>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_CATEGORY', null, CONFIG) &&
|
||||
post?.category && <CategoryItem category={post?.category} />}
|
||||
<div>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* 上一篇下一篇文章 */}
|
||||
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
{/* 评论区 */}
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
|
||||
{/* 文章底部区域 */}
|
||||
<section>
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{/* 文章分类和标签信息 */}
|
||||
<div className='flex justify-between'>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_CATEGORY', null, CONFIG) && post?.category && <CategoryItem category={post?.category} />}
|
||||
<div>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_TAG', null, CONFIG) && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 上一篇下一篇文章 */}
|
||||
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
{/* 评论区 */}
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
|
||||
{/* 移动端目录 */}
|
||||
<TocDrawer {...props} />
|
||||
</div>}
|
||||
{/* 移动端目录 */}
|
||||
<TocDrawer {...props} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -229,7 +256,7 @@ const LayoutSlug = props => {
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const LayoutSearch = (props) => {
|
||||
const LayoutSearch = props => {
|
||||
const { locale } = useGlobal()
|
||||
const { keyword } = props
|
||||
const router = useRouter()
|
||||
@@ -248,23 +275,32 @@ const LayoutSearch = (props) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <>
|
||||
return (
|
||||
<>
|
||||
{/* 搜索导航栏 */}
|
||||
<div className='py-12'>
|
||||
<div className='pb-4 w-full'>{locale.NAV.SEARCH}</div>
|
||||
<SearchInput currentSearch={currentSearch} {...props} />
|
||||
{!currentSearch && (
|
||||
<>
|
||||
<TagGroups {...props} />
|
||||
<CategoryGroup {...props} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 搜索导航栏 */}
|
||||
<div className='py-12'>
|
||||
<div className='pb-4 w-full'>{locale.NAV.SEARCH}</div>
|
||||
<SearchInput currentSearch={currentSearch} {...props} />
|
||||
{!currentSearch && <>
|
||||
<TagGroups {...props} />
|
||||
<CategoryGroup {...props} />
|
||||
</>}
|
||||
{/* 文章列表 */}
|
||||
{currentSearch && (
|
||||
<div>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogPostListPage {...props} />
|
||||
) : (
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 文章列表 */}
|
||||
{currentSearch && <div>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||
</div>}
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,12 +311,17 @@ const LayoutSearch = (props) => {
|
||||
const LayoutArchive = props => {
|
||||
const { archivePosts } = props
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10 pb-20 md:py-12 py-3 min-h-full">
|
||||
{Object.keys(archivePosts)?.map(archiveTitle => <BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
<>
|
||||
<div className='mb-10 pb-20 md:py-12 py-3 min-h-full'>
|
||||
{Object.keys(archivePosts)?.map(archiveTitle => (
|
||||
<BlogArchiveItem
|
||||
key={archiveTitle}
|
||||
archiveTitle={archiveTitle}
|
||||
archivePosts={archivePosts}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -325,33 +366,37 @@ const Layout404 = props => {
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const LayoutCategoryIndex = (props) => {
|
||||
const LayoutCategoryIndex = props => {
|
||||
const { categoryOptions } = props
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<>
|
||||
<div className='bg-white dark:bg-gray-700 py-10'>
|
||||
<div className='dark:text-gray-200 mb-5'>
|
||||
<i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}:
|
||||
<>
|
||||
<div className='bg-white dark:bg-gray-700 py-10'>
|
||||
<div className='dark:text-gray-200 mb-5'>
|
||||
<i className='mr-4 fas fa-th' />
|
||||
{locale.COMMON.CATEGORY}:
|
||||
</div>
|
||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||
{categoryOptions?.map(category => {
|
||||
return (
|
||||
<Link
|
||||
key={category.name}
|
||||
href={`/category/${category.name}`}
|
||||
passHref
|
||||
legacyBehavior>
|
||||
<div
|
||||
className={
|
||||
'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'
|
||||
}>
|
||||
<i className='mr-4 fas fa-folder' />
|
||||
{category.name}({category.count})
|
||||
</div>
|
||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||
{categoryOptions?.map(category => {
|
||||
return (
|
||||
<Link
|
||||
key={category.name}
|
||||
href={`/category/${category.name}`}
|
||||
passHref
|
||||
legacyBehavior>
|
||||
<div
|
||||
className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
|
||||
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -364,35 +409,35 @@ const LayoutTagIndex = props => {
|
||||
const { tagOptions } = props
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<>
|
||||
<div className="bg-white dark:bg-gray-700 py-10">
|
||||
<div className="dark:text-gray-200 mb-5">
|
||||
<i className="mr-4 fas fa-tag" />
|
||||
{locale.COMMON.TAGS}:
|
||||
</div>
|
||||
<div id="tags-list" className="duration-200 flex flex-wrap">
|
||||
{tagOptions?.map(tag => {
|
||||
return (
|
||||
<div key={tag.name} className="p-2">
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<>
|
||||
<div className='bg-white dark:bg-gray-700 py-10'>
|
||||
<div className='dark:text-gray-200 mb-5'>
|
||||
<i className='mr-4 fas fa-tag' />
|
||||
{locale.COMMON.TAGS}:
|
||||
</div>
|
||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||
{tagOptions?.map(tag => {
|
||||
return (
|
||||
<div key={tag.name} className='p-2'>
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
CONFIG as THEME_CONFIG,
|
||||
Layout404,
|
||||
LayoutArchive,
|
||||
LayoutBase,
|
||||
LayoutCategoryIndex,
|
||||
LayoutIndex,
|
||||
LayoutPostList,
|
||||
LayoutSearch,
|
||||
LayoutArchive,
|
||||
LayoutSlug,
|
||||
Layout404,
|
||||
LayoutCategoryIndex,
|
||||
LayoutTagIndex
|
||||
LayoutTagIndex,
|
||||
CONFIG as THEME_CONFIG
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user