diff --git a/themes/hexo/components/Header.js b/themes/hexo/components/Header.js new file mode 100644 index 00000000..9c3da194 --- /dev/null +++ b/themes/hexo/components/Header.js @@ -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 && ( +
+
+
+ + {locale.COMMON.CATEGORY} +
+ + {locale.COMMON.MORE} + +
+ +
+ )} + + {tags && ( +
+
+
+ + {locale.COMMON.TAGS} +
+ + {locale.COMMON.MORE} + +
+
+ +
+
+ )} + + ) + + return ( +
+ + + {/* 导航栏 */} + + + {/* 折叠侧边栏 */} + + + +
+ ) +} + +export default Header diff --git a/themes/hexo/components/PostHeader.js b/themes/hexo/components/PostHeader.js deleted file mode 100644 index aa94acb9..00000000 --- a/themes/hexo/components/PostHeader.js +++ /dev/null @@ -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
- } - - const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover - - return ( - - ) -} diff --git a/themes/hexo/components/PostHero.js b/themes/hexo/components/PostHero.js new file mode 100644 index 00000000..4cedf597 --- /dev/null +++ b/themes/hexo/components/PostHero.js @@ -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
+ } + + const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover + + return ( + + ) +} diff --git a/themes/hexo/components/TopNav.js b/themes/hexo/components/TopNav.js deleted file mode 100644 index 6f199fa6..00000000 --- a/themes/hexo/components/TopNav.js +++ /dev/null @@ -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 && ( -
-
-
{locale.COMMON.CATEGORY}
- - - {locale.COMMON.MORE} - - -
- -
- )} - - {tags && ( -
-
-
{locale.COMMON.TAGS}
- - - {locale.COMMON.MORE} - - -
-
- -
-
- )} - - - return (
- - - {/* 导航栏 */} - - - {/* 折叠侧边栏 */} - - - -
) -} - -export default TopNav diff --git a/themes/hexo/index.js b/themes/hexo/index.js index 664b9698..8dd08ec9 100644 --- a/themes/hexo/index.js +++ b/themes/hexo/index.js @@ -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 ? ( - + ) : router.route === '/' && siteConfig('HEXO_HOME_BANNER_ENABLE', null, CONFIG) ? ( @@ -89,7 +89,7 @@ const LayoutBase = props => {