matery主题支持algolia;hexo组件名微调

This commit is contained in:
tangly1024.com
2024-04-28 14:46:21 +08:00
parent e8aa7d98b2
commit cda0890785
10 changed files with 569 additions and 472 deletions

View File

@@ -0,0 +1,187 @@
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useRef, useState } from 'react'
import CONFIG from '../config'
import CategoryGroup from './CategoryGroup'
import Logo from './Logo'
import { MenuListTop } from './MenuListTop'
import SearchButton from './SearchButton'
import SearchDrawer from './SearchDrawer'
import SideBar from './SideBar'
import SideBarDrawer from './SideBarDrawer'
import TagGroups from './TagGroups'
let windowTop = 0
/**
* 顶部导航
* @param {*} param0
* @returns
*/
const Header = props => {
const searchDrawer = useRef()
const { tags, currentTag, categories, currentCategory } = props
const { locale } = useGlobal()
const router = useRouter()
const [isOpen, changeShow] = useState(false)
const showSearchButton = siteConfig('HEXO_MENU_SEARCH', false, CONFIG)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
const toggleSideBarClose = () => {
changeShow(false)
}
// 监听滚动
useEffect(() => {
window.addEventListener('scroll', topNavStyleHandler)
router.events.on('routeChangeComplete', topNavStyleHandler)
topNavStyleHandler()
return () => {
router.events.off('routeChangeComplete', topNavStyleHandler)
window.removeEventListener('scroll', topNavStyleHandler)
}
}, [])
const throttleMs = 200
const topNavStyleHandler = useCallback(
throttle(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
// 首页和文章页会有头图
const header = document.querySelector('#header')
// 导航栏和头图是否重叠
const scrollInHeader =
header && (scrollS < 10 || scrollS < header?.clientHeight - 50) // 透明导航条的条件
// const textWhite = header && scrollInHeader
if (scrollInHeader) {
nav && nav.classList.replace('bg-white', 'bg-none')
nav && nav.classList.replace('border', 'border-transparent')
nav && nav.classList.replace('drop-shadow-md', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-white')
nav && nav.classList.replace('border-transparent', 'border')
nav && nav.classList.replace('shadow-none', 'drop-shadow-md')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (scrollInHeader) {
nav && nav.classList.replace('text-black', 'text-white')
} else {
nav && nav.classList.replace('text-white', 'text-black')
}
// 导航栏不在头图里,且页面向下滚动一定程度 隐藏导航栏
const showNav =
scrollS <= windowTop ||
scrollS < 5 ||
(header && scrollS <= header.clientHeight + 100)
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
}, throttleMs)
)
const searchDrawerSlot = (
<>
{categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'>
<i className='mr-2 fas fa-th-list' />
{locale.COMMON.CATEGORY}
</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<CategoryGroup
currentCategory={currentCategory}
categories={categories}
/>
</section>
)}
{tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'>
<i className='mr-2 fas fa-tag' />
{locale.COMMON.TAGS}
</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
)}
</>
)
return (
<div id='top-nav' className='z-40'>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot} />
{/* 导航栏 */}
<div
id='sticky-nav'
style={{ backdropFilter: 'blur(3px)' }}
className={
'top-0 duration-300 transition-all shadow-none fixed bg-none dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform border-transparent dark:border-transparent'
}>
<div className='w-full flex justify-between items-center px-4 py-2'>
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 flex justify-end items-center '>
<div className='hidden lg:flex'>
{' '}
<MenuListTop {...props} />
</div>
<div
onClick={toggleMenuOpen}
className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? (
<i className='fas fa-times' />
) : (
<i className='fas fa-bars' />
)}
</div>
{showSearchButton && <SearchButton />}
</div>
</div>
</div>
{/* 折叠侧边栏 */}
<SideBarDrawer isOpen={isOpen} onClose={toggleSideBarClose}>
<SideBar {...props} />
</SideBarDrawer>
</div>
)
}
export default Header

View File

@@ -1,85 +0,0 @@
import Link from 'next/link'
import TagItemMini from './TagItemMini'
import { useGlobal } from '@/lib/global'
import NotionIcon from '@/components/NotionIcon'
import LazyImage from '@/components/LazyImage'
import { formatDateFmt } from '@/lib/utils/formatDate'
import { siteConfig } from '@/lib/config'
export default function PostHeader({ post, siteInfo }) {
const { locale, fullWidth } = useGlobal()
if (!post) {
return <></>
}
// 文章全屏隐藏标头
if (fullWidth) {
return <div className='my-8'/>
}
const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover
return (
<div id="header" className="w-full h-96 relative md:flex-shrink-0 z-10" >
<LazyImage priority={true} src={headerImage} className='w-full h-full object-cover object-center absolute top-0'/>
<header id='article-header-cover'
className="bg-black bg-opacity-70 absolute top-0 w-full h-96 py-10 flex justify-center items-center ">
<div className='mt-10'>
<div className='mb-3 flex justify-center'>
{post.category && <>
<Link href={`/category/${post.category}`} passHref legacyBehavior>
<div className="cursor-pointer px-2 py-1 mb-2 border rounded-sm dark:border-white text-sm font-medium hover:underline duration-200 shadow-text-md text-white">
{post.category}
</div>
</Link>
</>}
</div>
{/* 文章Title */}
<div className="leading-snug font-bold xs:text-4xl sm:text-4xl md:text-5xl md:leading-snug text-4xl shadow-text-md flex justify-center text-center text-white">
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} className='text-4xl mx-1' />}{post.title}
</div>
<section className="flex-wrap shadow-text-md flex text-sm justify-center mt-4 text-white dark:text-gray-400 font-light leading-8">
<div className='flex justify-center dark:text-gray-200 text-opacity-70'>
{post?.type !== 'Page' && (
<>
<Link
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
passHref
className="pl-1 mr-2 cursor-pointer hover:underline">
{locale.COMMON.POST_TIME}: {post?.publishDay}
</Link>
</>
)}
<div className="pl-1 mr-2">
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay}
</div>
</div>
{JSON.parse(siteConfig('ANALYTICS_BUSUANZI_ENABLE')) && <div className="busuanzi_container_page_pv font-light mr-2">
<span className="mr-2 busuanzi_value_page_pv" />
{locale.COMMON.VIEWS}
</div>}
</section>
<div className='mt-4 mb-1'>
{post.tagItems && (
<div className="flex justify-center flex-nowrap overflow-x-auto">
{post.tagItems.map(tag => (
<TagItemMini key={tag.name} tag={tag} />
))}
</div>
)}
</div>
</div>
</header>
</div>
)
}

View File

@@ -0,0 +1,99 @@
import LazyImage from '@/components/LazyImage'
import NotionIcon from '@/components/NotionIcon'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { formatDateFmt } from '@/lib/utils/formatDate'
import Link from 'next/link'
import TagItemMini from './TagItemMini'
/**
* 文章详情页的Hero块
*/
export default function PostHero({ post, siteInfo }) {
const { locale, fullWidth } = useGlobal()
if (!post) {
return <></>
}
// 文章全屏隐藏标头
if (fullWidth) {
return <div className='my-8' />
}
const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover
return (
<div id='header' className='w-full h-96 relative md:flex-shrink-0 z-10'>
<LazyImage
priority={true}
src={headerImage}
className='w-full h-full object-cover object-center absolute top-0'
/>
<header
id='article-header-cover'
className='bg-black bg-opacity-70 absolute top-0 w-full h-96 py-10 flex justify-center items-center '>
<div className='mt-10'>
<div className='mb-3 flex justify-center'>
{post.category && (
<>
<Link
href={`/category/${post.category}`}
passHref
legacyBehavior>
<div className='cursor-pointer px-2 py-1 mb-2 border rounded-sm dark:border-white text-sm font-medium hover:underline duration-200 shadow-text-md text-white'>
{post.category}
</div>
</Link>
</>
)}
</div>
{/* 文章Title */}
<div className='leading-snug font-bold xs:text-4xl sm:text-4xl md:text-5xl md:leading-snug text-4xl shadow-text-md flex justify-center text-center text-white'>
{siteConfig('POST_TITLE_ICON') && (
<NotionIcon icon={post.pageIcon} className='text-4xl mx-1' />
)}
{post.title}
</div>
<section className='flex-wrap shadow-text-md flex text-sm justify-center mt-4 text-white dark:text-gray-400 font-light leading-8'>
<div className='flex justify-center dark:text-gray-200 text-opacity-70'>
{post?.type !== 'Page' && (
<>
<Link
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
passHref
className='pl-1 mr-2 cursor-pointer hover:underline'>
{locale.COMMON.POST_TIME}: {post?.publishDay}
</Link>
</>
)}
<div className='pl-1 mr-2'>
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay}
</div>
</div>
{JSON.parse(siteConfig('ANALYTICS_BUSUANZI_ENABLE')) && (
<div className='busuanzi_container_page_pv font-light mr-2'>
<span className='mr-2 busuanzi_value_page_pv' />
{locale.COMMON.VIEWS}
</div>
)}
</section>
<div className='mt-4 mb-1'>
{post.tagItems && (
<div className='flex justify-center flex-nowrap overflow-x-auto'>
{post.tagItems.map(tag => (
<TagItemMini key={tag.name} tag={tag} />
))}
</div>
)}
</div>
</div>
</header>
</div>
)
}

View File

@@ -1,159 +0,0 @@
import { useGlobal } from '@/lib/global'
import Link from 'next/link'
import { useCallback, useEffect, useRef, useState } from 'react'
import CategoryGroup from './CategoryGroup'
import Logo from './Logo'
import SearchDrawer from './SearchDrawer'
import TagGroups from './TagGroups'
import { MenuListTop } from './MenuListTop'
import throttle from 'lodash.throttle'
import SideBar from './SideBar'
import SideBarDrawer from './SideBarDrawer'
import { siteConfig } from '@/lib/config'
import SearchButton from './SearchButton'
import CONFIG from '../config'
import { useRouter } from 'next/router'
let windowTop = 0
/**
* 顶部导航
* @param {*} param0
* @returns
*/
const TopNav = props => {
const searchDrawer = useRef()
const { tags, currentTag, categories, currentCategory } = props
const { locale } = useGlobal()
const router = useRouter()
const [isOpen, changeShow] = useState(false)
const showSearchButton = siteConfig('HEXO_MENU_SEARCH', false, CONFIG)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
const toggleSideBarClose = () => {
changeShow(false)
}
// 监听滚动
useEffect(() => {
window.addEventListener('scroll', topNavStyleHandler)
router.events.on('routeChangeComplete', topNavStyleHandler)
topNavStyleHandler()
return () => {
router.events.off('routeChangeComplete', topNavStyleHandler)
window.removeEventListener('scroll', topNavStyleHandler)
}
}, [])
const throttleMs = 200
const topNavStyleHandler = useCallback(throttle(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
// 首页和文章页会有头图
const header = document.querySelector('#header')
// 导航栏和头图是否重叠
const scrollInHeader = header && (scrollS < 10 || scrollS < header?.clientHeight - 50) // 透明导航条的条件
// const textWhite = header && scrollInHeader
if (scrollInHeader) {
nav && nav.classList.replace('bg-white', 'bg-none')
nav && nav.classList.replace('border', 'border-transparent')
nav && nav.classList.replace('drop-shadow-md', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-white')
nav && nav.classList.replace('border-transparent', 'border')
nav && nav.classList.replace('shadow-none', 'drop-shadow-md')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (scrollInHeader) {
nav && nav.classList.replace('text-black', 'text-white')
} else {
nav && nav.classList.replace('text-white', 'text-black')
}
// 导航栏不在头图里,且页面向下滚动一定程度 隐藏导航栏
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight + 100)
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
}, throttleMs)
)
const searchDrawerSlot = <>
{categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-th-list' />{locale.COMMON.CATEGORY}</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<CategoryGroup currentCategory={currentCategory} categories={categories} />
</section>
)}
{tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-tag' />{locale.COMMON.TAGS}</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
)}
</>
return (<div id='top-nav' className='z-40'>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot} />
{/* 导航栏 */}
<div id='sticky-nav' style={{ backdropFilter: 'blur(3px)' }} className={'top-0 duration-300 transition-all shadow-none fixed bg-none dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform border-transparent dark:border-transparent'}>
<div className='w-full flex justify-between items-center px-4 py-2'>
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 flex justify-end items-center '>
<div className='hidden lg:flex'> <MenuListTop {...props} /></div>
<div onClick={toggleMenuOpen} className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
{showSearchButton && <SearchButton />}
</div>
</div>
</div>
{/* 折叠侧边栏 */}
<SideBarDrawer isOpen={isOpen} onClose={toggleSideBarClose}>
<SideBar {...props} />
</SideBarDrawer>
</div>)
}
export default TopNav

View File

@@ -19,9 +19,10 @@ import BlogPostListPage from './components/BlogPostListPage'
import BlogPostListScroll from './components/BlogPostListScroll'
import Card from './components/Card'
import Footer from './components/Footer'
import Header from './components/Header'
import Hero from './components/Hero'
import JumpToCommentButton from './components/JumpToCommentButton'
import PostHeader from './components/PostHeader'
import PostHero from './components/PostHero'
import RightFloatArea from './components/RightFloatArea'
import SearchNav from './components/SearchNav'
import SideRight from './components/SideRight'
@@ -29,7 +30,6 @@ import SlotBar from './components/SlotBar'
import TagItemMini from './components/TagItemMini'
import TocDrawer from './components/TocDrawer'
import TocDrawerButton from './components/TocDrawerButton'
import TopNav from './components/TopNav'
import CONFIG from './config'
import { Style } from './style'
@@ -54,7 +54,7 @@ const LayoutBase = props => {
const router = useRouter()
const headerSlot = post ? (
<PostHeader {...props} />
<PostHero {...props} />
) : router.route === '/' &&
siteConfig('HEXO_HOME_BANNER_ENABLE', null, CONFIG) ? (
<Hero {...props} />
@@ -89,7 +89,7 @@ const LayoutBase = props => {
<Style />
{/* 顶部导航 */}
<TopNav {...props} />
<Header {...props} />
{/* 顶部嵌入 */}
<Transition

View File

@@ -0,0 +1,190 @@
import SideBarDrawer from '@/components/SideBarDrawer'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import Link from 'next/link'
import { useCallback, useEffect, useRef, useState } from 'react'
import CONFIG from '../config'
import CategoryGroup from './CategoryGroup'
import Logo from './Logo'
import { MenuListTop } from './MenuListTop'
import SearchButton from './SearchButton'
import SearchDrawer from './SearchDrawer'
import SideBar from './SideBar'
import TagGroups from './TagGroups'
let windowTop = 0
/**
* 顶部导航(页头)
* @param {*} param0
* @returns
*/
const Header = props => {
const { tags, currentTag, categories, currentCategory } = props
const { locale } = useGlobal()
const searchDrawer = useRef()
const { isDarkMode } = useGlobal()
const throttleMs = 200
const showSearchButton = siteConfig('MATERY_MENU_SEARCH', false, CONFIG)
const scrollTrigger = useCallback(
throttle(() => {
requestAnimationFrame(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav =
scrollS <= windowTop ||
scrollS < 5 ||
(header && scrollS <= header.clientHeight * 2) // 非首页无大图时影藏顶部 滚动条置顶时隐藏// 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = header && scrollS < 300 // 透明导航条的条件
if (navTransparent) {
nav && nav.classList.replace('bg-indigo-700', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('shadow-xl', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-indigo-700')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('shadow-none', 'shadow-xl')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
})
}, throttleMs)
)
const navDarkMode = () => {
const nav = document.getElementById('sticky-nav')
const header = document.querySelector('#header')
if (!isDarkMode && nav && header) {
if (window.scrollY < header.clientHeight) {
nav?.classList?.add('dark')
} else {
nav?.classList?.remove('dark')
}
}
}
// 监听滚动
useEffect(() => {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
}, [])
const [isOpen, changeShow] = useState(false)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
const toggleMenuClose = () => {
changeShow(false)
}
const searchDrawerSlot = (
<>
{categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'>
<i className='mr-2 fas fa-th-list' />
{locale.COMMON.CATEGORY}
</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<CategoryGroup
currentCategory={currentCategory}
categories={categories}
/>
</section>
)}
{tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'>
<i className='mr-2 fas fa-tag' />
{locale.COMMON.TAGS}
</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
)}
</>
)
return (
<div id='top-nav'>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot} />
{/* 导航栏 */}
<div
id='sticky-nav'
className={
'flex justify-center top-0 shadow-none fixed bg-none dark:bg-hexo-black-gray text-gray-200 w-full z-30 transform transition-all duration-200'
}>
<div className='w-full max-w-6xl flex justify-between items-center px-4 py-2'>
{/* 左侧功能 */}
<div className='justify-start items-center block lg:hidden '>
<div
onClick={toggleMenuOpen}
className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? (
<i className='fas fa-times' />
) : (
<i className='fas fa-bars' />
)}
</div>
</div>
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 justify-end items-center flex'>
<div className='hidden lg:flex'>
{' '}
<MenuListTop {...props} />
</div>
{showSearchButton && <SearchButton />}
</div>
</div>
</div>
<SideBarDrawer isOpen={isOpen} onClose={toggleMenuClose}>
<SideBar {...props} />
</SideBarDrawer>
</div>
)
}
export default Header

View File

@@ -5,7 +5,7 @@ import { siteConfig } from '@/lib/config'
/**
* 文章背景图
*/
export default function PostHeader({ post, siteInfo }) {
export default function PostHero({ post, siteInfo }) {
const headerImage = post?.pageCoverThumbnail
? post?.pageCoverThumbnail
: siteInfo?.pageCover

View File

@@ -0,0 +1,34 @@
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import { useMateryGlobal } from '..'
/**
* 搜索按钮
* @returns
*/
export default function SearchButton(props) {
const { locale } = useGlobal()
const router = useRouter()
const { searchModal } = useMateryGlobal()
function handleSearch() {
if (siteConfig('ALGOLIA_APP_ID')) {
searchModal.current.openSearch()
} else {
router.push('/search')
}
}
return (
<>
<div
onClick={handleSearch}
title={locale.NAV.SEARCH}
alt={locale.NAV.SEARCH}
className='cursor-pointer dark:text-white hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
<i title={locale.NAV.SEARCH} className='fa-solid fa-magnifying-glass' />
</div>
</>
)
}

View File

@@ -1,164 +0,0 @@
import { useGlobal } from '@/lib/global'
import Link from 'next/link'
import { useCallback, useEffect, useRef, useState } from 'react'
import CategoryGroup from './CategoryGroup'
import Logo from './Logo'
import SearchDrawer from './SearchDrawer'
import TagGroups from './TagGroups'
import { MenuListTop } from './MenuListTop'
import SideBarDrawer from '@/components/SideBarDrawer'
import SideBar from './SideBar'
import throttle from 'lodash.throttle'
let windowTop = 0
/**
* 顶部导航
* @param {*} param0
* @returns
*/
const TopNav = props => {
const { tags, currentTag, categories, currentCategory } = props
const { locale } = useGlobal()
const searchDrawer = useRef()
const { isDarkMode } = useGlobal()
const throttleMs = 200
const scrollTrigger = useCallback(throttle(() => {
requestAnimationFrame(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight * 2)// 非首页无大图时影藏顶部 滚动条置顶时隐藏// 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = header && scrollS < 300 // 透明导航条的条件
if (navTransparent) {
nav && nav.classList.replace('bg-indigo-700', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('shadow-xl', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-indigo-700')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('shadow-none', 'shadow-xl')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
})
}, throttleMs))
const navDarkMode = () => {
const nav = document.getElementById('sticky-nav')
const header = document.querySelector('#header')
if (!isDarkMode && nav && header) {
if (window.scrollY < header.clientHeight) {
nav?.classList?.add('dark')
} else {
nav?.classList?.remove('dark')
}
}
}
// 监听滚动
useEffect(() => {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
}, [])
const [isOpen, changeShow] = useState(false)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
const toggleMenuClose = () => {
changeShow(false)
}
const searchDrawerSlot = <>
{categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-th-list' />{locale.COMMON.CATEGORY}</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<CategoryGroup currentCategory={currentCategory} categories={categories} />
</section>
)}
{tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-tag' />{locale.COMMON.TAGS}</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
)}
</>
return (
<div id='top-nav'>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot} />
{/* 导航栏 */}
<div id='sticky-nav' className={'flex justify-center top-0 shadow-none fixed bg-none dark:bg-hexo-black-gray text-gray-200 w-full z-30 transform transition-all duration-200'}>
<div className='w-full max-w-6xl flex justify-between items-center px-4 py-2'>
{/* 左侧功能 */}
<div className='justify-start items-center block lg:hidden '>
<div onClick={toggleMenuOpen} className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
</div>
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 justify-end items-center '>
<div className='hidden lg:flex'> <MenuListTop {...props} /></div>
<div className='block lg:hidden'><Link href={'/search'} passHref>
<i className='fas fa-search' />
</Link></div>
</div>
</div>
</div>
<SideBarDrawer isOpen={isOpen} onClose={toggleMenuClose}>
<SideBar {...props} />
</SideBarDrawer>
</div>
)
}
export default TopNav

View File

@@ -9,9 +9,10 @@ import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { loadWowJS } from '@/lib/plugins/wow'
import { isBrowser } from '@/lib/utils'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { createContext, useContext, useEffect, useRef } from 'react'
import Announcement from './components/Announcement'
import ArticleAdjacent from './components/ArticleAdjacent'
import ArticleCopyright from './components/ArticleCopyright'
@@ -24,16 +25,25 @@ import BlogPostListScroll from './components/BlogPostListScroll'
import Card from './components/Card'
import CatalogWrapper from './components/CatalogWrapper'
import Footer from './components/Footer'
import Header from './components/Header'
import Hero from './components/Hero'
import JumpToCommentButton from './components/JumpToCommentButton'
import PostHeader from './components/PostHeader'
import PostHero from './components/PostHero'
import RightFloatButtons from './components/RightFloatButtons'
import SearchNave from './components/SearchNav'
import TagItemMiddle from './components/TagItemMiddle'
import TopNav from './components/TopNav'
import CONFIG from './config'
import { Style } from './style'
const AlgoliaSearchModal = dynamic(
() => import('@/components/AlgoliaSearchModal'),
{ ssr: false }
)
// 主题全局状态
const ThemeGlobalMatery = createContext()
export const useMateryGlobal = () => useContext(ThemeGlobalMatery)
/**
* 基础布局
* 采用左右两侧布局,移动端使用顶部导航栏
@@ -60,74 +70,59 @@ const LayoutBase = props => {
router.route === '/' ? (
<Hero {...props} />
) : post && !fullWidth ? (
<PostHeader {...props} />
<PostHero {...props} />
) : null
const floatRightBottom = post ? <JumpToCommentButton /> : null
// Algolia搜索框
const searchModal = useRef(null)
return (
<div
id='theme-matery'
className={`${siteConfig('FONT_STYLE')} min-h-screen flex flex-col justify-between bg-hexo-background-gray dark:bg-black w-full scroll-smooth`}>
<Style />
<ThemeGlobalMatery.Provider value={{ searchModal }}>
<div
id='theme-matery'
className={`${siteConfig('FONT_STYLE')} min-h-screen flex flex-col justify-between bg-hexo-background-gray dark:bg-black w-full scroll-smooth`}>
<Style />
{/* 顶部导航栏 */}
<TopNav {...props} />
{/* 顶部导航栏 */}
<Header {...props} />
{/* 顶部嵌入 */}
{/* <Transition
show={!onLoading}
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 -translate-y-16"
enterTo="opacity-100 w-full"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-16"
unmount={false}
> */}
{headerSlot}
{/* </Transition> */}
{/* 顶部嵌入 */}
{headerSlot}
<main
id='wrapper'
className={`${siteConfig('MATERY_HOME_BANNER_ENABLE', null, CONFIG) ? '' : 'pt-16'} flex-1 w-full py-8 md:px-8 lg:px-24 relative`}>
{/* 嵌入区域 */}
<div
id='container-slot'
className={`w-full ${fullWidth ? '' : 'max-w-6xl'} ${post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
{containerSlot}
<main
id='wrapper'
className={`${siteConfig('MATERY_HOME_BANNER_ENABLE', null, CONFIG) ? '' : 'pt-16'} flex-1 w-full py-8 md:px-8 lg:px-24 relative`}>
{/* 嵌入区域 */}
<div
id='container-slot'
className={`w-full ${fullWidth ? '' : 'max-w-6xl'} ${post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
{containerSlot}
</div>
<div
id='container-inner'
className={`w-full min-h-fit ${fullWidth ? '' : 'max-w-6xl'} mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
{children}
</div>
</main>
{/* 左下角悬浮 */}
<div className='bottom-4 -left-14 fixed justify-end z-40'>
<Live2D />
</div>
<div
id='container-inner'
className={`w-full min-h-fit ${fullWidth ? '' : 'max-w-6xl'} mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
{/* <Transition
show={!onLoading}
appear={true}
enter='transition ease-in-out duration-700 transform order-first'
enterFrom='opacity-0 translate-y-16'
enterTo='opacity-100 w-full'
leave='transition ease-in-out duration-300 transform'
leaveFrom='opacity-100 translate-y-0'
leaveTo='opacity-0 -translate-y-16'
unmount={false}> */}
{children}
{/* </Transition> */}
</div>
</main>
{/* 右下角悬浮 */}
<RightFloatButtons {...props} floatRightBottom={floatRightBottom} />
{/* 左下角悬浮 */}
<div className='bottom-4 -left-14 fixed justify-end z-40'>
<Live2D />
{/* 全文搜索 */}
<AlgoliaSearchModal cRef={searchModal} {...props} />
{/* 页脚 */}
<Footer title={siteConfig('TITLE')} />
</div>
{/* 右下角悬浮 */}
<RightFloatButtons {...props} floatRightBottom={floatRightBottom} />
{/* 页脚 */}
<Footer title={siteConfig('TITLE')} />
</div>
</ThemeGlobalMatery.Provider>
)
}