mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-01 07:26:47 +00:00
nav-bar
This commit is contained in:
@@ -1,73 +0,0 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react'
|
|
||||||
import Logo from './Logo'
|
|
||||||
|
|
||||||
import { MenuListTop } from './MenuListTop'
|
|
||||||
import throttle from 'lodash.throttle'
|
|
||||||
/**
|
|
||||||
* 顶部导航
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const Header = props => {
|
|
||||||
const [isOpen, changeShow] = useState(false)
|
|
||||||
const [fixedNav, setHeaderBgShow] = useState(false)
|
|
||||||
const [whiteTitle, setWhiteTitle] = useState(false)
|
|
||||||
|
|
||||||
const toggleMenuOpen = () => {
|
|
||||||
changeShow(!isOpen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听滚动
|
|
||||||
useEffect(() => {
|
|
||||||
scrollTrigger()
|
|
||||||
window.addEventListener('scroll', scrollTrigger)
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('scroll', scrollTrigger)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const throttleMs = 200
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据滚动条,切换导航栏样式
|
|
||||||
*/
|
|
||||||
const scrollTrigger = useCallback(throttle(() => {
|
|
||||||
const scrollS = window.scrollY
|
|
||||||
const header = document.querySelector('#header')
|
|
||||||
const postHeader = document.querySelector('#post-bg')
|
|
||||||
|
|
||||||
// 导航栏设置 白色背景
|
|
||||||
if (header && scrollS > 0) {
|
|
||||||
setHeaderBgShow(true)
|
|
||||||
setWhiteTitle(false)
|
|
||||||
} else {
|
|
||||||
if (postHeader) {
|
|
||||||
setWhiteTitle(true)
|
|
||||||
}
|
|
||||||
setHeaderBgShow(false)
|
|
||||||
}
|
|
||||||
}, throttleMs))
|
|
||||||
|
|
||||||
return (<>
|
|
||||||
{/* 头条 */}
|
|
||||||
<header id='header' className={' h-16 w-full z-20 '}>
|
|
||||||
<nav className={`${fixedNav ? 'fixed bg-white' : 'relative bg-none'} ${whiteTitle ? 'text-white' : 'text-black'} h-16 top-0 w-full`}>
|
|
||||||
<div className='flex h-full mx-auto justify-between items-center max-w-[86rem] px-8'>
|
|
||||||
<div className='flex'>
|
|
||||||
<Logo {...props} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 右侧功能 */}
|
|
||||||
<div className='mr-1 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
</>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Header
|
|
||||||
@@ -6,6 +6,8 @@ import Link from 'next/link'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useImperativeHandle, useRef, useState } from 'react'
|
import { useImperativeHandle, useRef, useState } from 'react'
|
||||||
import CONFIG from '../config'
|
import CONFIG from '../config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 顶部英雄区
|
* 顶部英雄区
|
||||||
@@ -15,15 +17,28 @@ import CONFIG from '../config'
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Hero = props => {
|
const Hero = props => {
|
||||||
|
const { onLoading } = useGlobal()
|
||||||
return (
|
return (
|
||||||
<div id="hero-wrapper" className='recent-top-post-group w-full overflow-hidden select-none px-5 mb-4'>
|
<div id="hero-wrapper" className='recent-top-post-group w-full overflow-hidden select-none px-5 mb-4'>
|
||||||
<hero id="hero" style={{ zIndex: 1 }} className="recent-post-top rounded-[12px] 2xl:px-5 recent-top-post-group max-w-[86rem] overflow-x-scroll w-full mx-auto flex-row flex-nowrap flex relative space-x-3" >
|
<Transition
|
||||||
{/* 左侧banner组 */}
|
show={!onLoading}
|
||||||
<BannerGroup {...props} />
|
appear={true}
|
||||||
|
enter="transition ease-in-out duration-700 transform order-first"
|
||||||
|
enterFrom="opacity-0 -translate-y-16"
|
||||||
|
enterTo="opacity-100 translate-y-0"
|
||||||
|
leave="transition ease-in-out duration-300 transform"
|
||||||
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
|
leaveTo="opacity-0 translate-y-16"
|
||||||
|
unmount={false}
|
||||||
|
>
|
||||||
|
<hero id="hero" style={{ zIndex: 1 }} className="recent-post-top rounded-[12px] 2xl:px-5 recent-top-post-group max-w-[86rem] overflow-x-scroll w-full mx-auto flex-row flex-nowrap flex relative space-x-3" >
|
||||||
|
{/* 左侧banner组 */}
|
||||||
|
<BannerGroup {...props} />
|
||||||
|
|
||||||
{/* 右侧置顶文章组 */}
|
{/* 右侧置顶文章组 */}
|
||||||
<TopGroup {...props} />
|
<TopGroup {...props} />
|
||||||
</hero>
|
</hero>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -186,8 +201,8 @@ function TodayCard({ cRef }) {
|
|||||||
const [isCoverUp, setIsCoverUp] = useState(true)
|
const [isCoverUp, setIsCoverUp] = useState(true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 外部可以调用此方法
|
* 外部可以调用此方法
|
||||||
*/
|
*/
|
||||||
useImperativeHandle(cRef, () => {
|
useImperativeHandle(cRef, () => {
|
||||||
return {
|
return {
|
||||||
coverUp: () => {
|
coverUp: () => {
|
||||||
@@ -197,18 +212,18 @@ function TodayCard({ cRef }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点击更多
|
* 点击更多
|
||||||
* @param {*} e
|
* @param {*} e
|
||||||
*/
|
*/
|
||||||
function handleClickMore(e) {
|
function handleClickMore(e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
setIsCoverUp(false)
|
setIsCoverUp(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点击卡片跳转的链接
|
* 点击卡片跳转的链接
|
||||||
* @param {*} e
|
* @param {*} e
|
||||||
*/
|
*/
|
||||||
function handleCardClick(e) {
|
function handleCardClick(e) {
|
||||||
router.push('https://tangly1024.com')
|
router.push('https://tangly1024.com')
|
||||||
}
|
}
|
||||||
|
|||||||
79
themes/heo/components/NavBar.js
Normal file
79
themes/heo/components/NavBar.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import Logo from './Logo'
|
||||||
|
|
||||||
|
import { MenuListTop } from './MenuListTop'
|
||||||
|
import throttle from 'lodash.throttle'
|
||||||
|
/**
|
||||||
|
* 顶部导航
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const NavBar = props => {
|
||||||
|
const [isOpen, changeShow] = useState(false)
|
||||||
|
const [fixedNav, setFixedNav] = useState(false)
|
||||||
|
const [textWhite, setTextWhite] = useState(false)
|
||||||
|
const [navBgWhite, setBgWhite] = useState(false)
|
||||||
|
|
||||||
|
const toggleMenuOpen = () => {
|
||||||
|
changeShow(!isOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听滚动
|
||||||
|
useEffect(() => {
|
||||||
|
scrollTrigger()
|
||||||
|
window.addEventListener('scroll', scrollTrigger)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', scrollTrigger)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const throttleMs = 200
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据滚动条,切换导航栏样式
|
||||||
|
*/
|
||||||
|
const scrollTrigger = useCallback(throttle(() => {
|
||||||
|
const scrollS = window.scrollY
|
||||||
|
|
||||||
|
// 导航栏设置 白色背景
|
||||||
|
if (scrollS <= 0) {
|
||||||
|
setFixedNav(false)
|
||||||
|
setBgWhite(false)
|
||||||
|
|
||||||
|
// 文章详情页特殊处理
|
||||||
|
const postHeader = document.querySelector('#post-bg')
|
||||||
|
if (postHeader) {
|
||||||
|
setFixedNav(true)
|
||||||
|
setTextWhite(true)
|
||||||
|
setBgWhite(false)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向下滚动后的导航样式
|
||||||
|
setFixedNav(true)
|
||||||
|
setTextWhite(false)
|
||||||
|
setBgWhite(true)
|
||||||
|
}, throttleMs))
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
{/* 头条 */}
|
||||||
|
<nav id='nav' className={`${fixedNav ? 'fixed' : 'relative bg-none'} ${textWhite ? 'text-white ' : 'text-black'} ${navBgWhite ? 'bg-white' : 'bg-none'} z-20 h-16 top-0 w-full`}>
|
||||||
|
<div className='flex h-full mx-auto justify-between items-center max-w-[86rem] px-8'>
|
||||||
|
<div className='flex'>
|
||||||
|
<Logo {...props} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 右侧功能 */}
|
||||||
|
<div className='mr-1 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavBar
|
||||||
@@ -17,15 +17,15 @@ export default function PostHeader({ post, siteInfo }) {
|
|||||||
return (
|
return (
|
||||||
<div id="post-bg" className="w-full h-[30rem] relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat z-10">
|
<div id="post-bg" className="w-full h-[30rem] relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat z-10">
|
||||||
|
|
||||||
<header id='article-header-cover' style={{ backdropFilter: 'blur(15px)' }} className="bg-[#0060e0] absolute top-0 w-full h-full py-10 flex justify-center items-center ">
|
<div id='article-header-cover' style={{ backdropFilter: 'blur(15px)' }} className="bg-[#0060e0] absolute top-0 w-full h-full py-10 flex justify-center items-center ">
|
||||||
|
|
||||||
<div id='post-cover-wrapper' style={{ filter: 'blur(15px)' }} className='opacity-50 rotate-12 translate-x-12 -mr-60'>
|
<div id='post-cover-wrapper' style={{ filter: 'blur(15px)' }} className='opacity-50 rotate-12 translate-x-12 -mr-60'>
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img id='post-cover' style={{ boxShadow: 'box-shadow:110px -130px 300px 60px #4d240c inset' }} className='w-full h-full object-cover opacity-80 min-w-[50vw] min-h-[20rem]' src={headerImage}/>
|
<img id='post-cover' style={{ boxShadow: 'box-shadow:110px -130px 300px 60px #4d240c inset' }} className='w-full h-full object-cover opacity-80 min-w-[50vw] min-h-[20rem]' src={headerImage}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='post-info' className='absolute z-10 flex flex-col'>
|
<div id='post-info' className='absolute z-10 flex flex-col w-full max-w-[86rem] px-5'>
|
||||||
<div className='mb-3 flex justify-center'>
|
<div className='mb-3 flex justify-start'>
|
||||||
{post.category && <>
|
{post.category && <>
|
||||||
<Link href={`/category/${post.category}`} passHref legacyBehavior>
|
<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">
|
<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">
|
||||||
@@ -36,11 +36,11 @@ export default function PostHeader({ post, siteInfo }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 文章Title */}
|
{/* 文章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">
|
<div className="max-w-5xl font-bold xs:text-4xl sm:text-4xl md:text-4xl md:leading-snug shadow-text-md flex justify-start text-white">
|
||||||
<NotionIcon icon={post.pageIcon} className='text-4xl mx-1' />{post.title}
|
<span><NotionIcon icon={post.pageIcon} className='text-4xl mx-1' /></span>{post.title}
|
||||||
</div>
|
</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">
|
<section className="flex-wrap shadow-text-md flex text-sm justify-start mt-4 text-white dark:text-gray-400 font-light leading-8">
|
||||||
|
|
||||||
<div className='flex justify-center dark:text-gray-200 text-opacity-70'>
|
<div className='flex justify-center dark:text-gray-200 text-opacity-70'>
|
||||||
{post?.type !== 'Page' && (
|
{post?.type !== 'Page' && (
|
||||||
@@ -79,7 +79,7 @@ export default function PostHeader({ post, siteInfo }) {
|
|||||||
|
|
||||||
<WavesArea />
|
<WavesArea />
|
||||||
|
|
||||||
</header>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import CommonHead from '@/components/CommonHead'
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import SideRight from './components/SideRight'
|
import SideRight from './components/SideRight'
|
||||||
import Header from './components/Header'
|
import NavBar from './components/NavBar'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||||
@@ -53,23 +53,8 @@ const LayoutBase = props => {
|
|||||||
<CommonHead meta={meta} siteInfo={siteInfo} />
|
<CommonHead meta={meta} siteInfo={siteInfo} />
|
||||||
<Style />
|
<Style />
|
||||||
|
|
||||||
{/* 顶部导航 */}
|
{/* 顶部嵌入 导航栏,首页放hero,文章页放文章详情 */}
|
||||||
<Header {...props} />
|
{headerSlot}
|
||||||
|
|
||||||
{/* 顶部嵌入 首页放hero,文章页放大图 */}
|
|
||||||
<Transition
|
|
||||||
show={!onLoading}
|
|
||||||
appear={true}
|
|
||||||
enter="transition ease-in-out duration-700 transform order-first"
|
|
||||||
enterFrom="opacity-0 -translate-y-16"
|
|
||||||
enterTo="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-300 transform"
|
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
|
||||||
leaveTo="opacity-0 translate-y-16"
|
|
||||||
unmount={false}
|
|
||||||
>
|
|
||||||
{headerSlot}
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
{/* 主区块 */}
|
{/* 主区块 */}
|
||||||
<main id="wrapper-outer" className={'flex-grow w-full max-w-[86rem] mx-auto relative px-5'}>
|
<main id="wrapper-outer" className={'flex-grow w-full max-w-[86rem] mx-auto relative px-5'}>
|
||||||
@@ -124,12 +109,13 @@ const LayoutBase = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = (props) => {
|
||||||
// 博客列表上方嵌入一个 通知横幅和英雄块
|
const headerSlot = <header>
|
||||||
const headerSlot = <>
|
{/* 顶部导航 */}
|
||||||
{/* 通知横幅 */}
|
<div id='nav-bar-wrapper' className='h-16'><NavBar {...props} /></div>
|
||||||
<NoticeBar />
|
{/* 通知横幅 */}
|
||||||
<Hero {...props} />
|
<NoticeBar />
|
||||||
</>
|
<Hero {...props} />
|
||||||
|
</header>
|
||||||
|
|
||||||
return <LayoutPostList {...props} headerSlot={headerSlot} />
|
return <LayoutPostList {...props} headerSlot={headerSlot} />
|
||||||
}
|
}
|
||||||
@@ -144,7 +130,7 @@ const LayoutPostList = (props) => {
|
|||||||
const slotRight = <SideRight {...props} />
|
const slotRight = <SideRight {...props} />
|
||||||
|
|
||||||
return <LayoutBase {...props} slotRight={slotRight}>
|
return <LayoutBase {...props} slotRight={slotRight}>
|
||||||
{/* 文章分类条 */}
|
{/* 文章分类条 */}
|
||||||
<CategoryBar {...props} />
|
<CategoryBar {...props} />
|
||||||
|
|
||||||
{BLOG.POST_LIST_STYLE === 'page'
|
{BLOG.POST_LIST_STYLE === 'page'
|
||||||
@@ -224,8 +210,14 @@ const LayoutSlug = props => {
|
|||||||
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
||||||
// 右侧栏
|
// 右侧栏
|
||||||
const slotRight = <SideRight {...props} />
|
const slotRight = <SideRight {...props} />
|
||||||
|
const headerSlot = <header>
|
||||||
|
{/* 顶部导航 */}
|
||||||
|
<div id='nav-bar-wrapper'><NavBar {...props} /></div>
|
||||||
|
<PostHeader {...props} />
|
||||||
|
</header>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} headerSlot={<PostHeader {...props} />} showCategory={false} showTag={false} slotRight={slotRight}>
|
<LayoutBase {...props} headerSlot={headerSlot} showCategory={false} showTag={false} slotRight={slotRight}>
|
||||||
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black article">
|
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black article">
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
@@ -308,8 +300,13 @@ const Layout404 = props => {
|
|||||||
const LayoutCategoryIndex = props => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { categoryOptions } = props
|
const { categoryOptions } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
const headerSlot = <header>
|
||||||
|
{/* 顶部导航 */}
|
||||||
|
<div id='nav-bar-wrapper' className='h-16'><NavBar {...props} /></div>
|
||||||
|
</header>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} className='mt-8'>
|
<LayoutBase {...props} className='mt-8' headerSlot={headerSlot}>
|
||||||
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
||||||
{locale.COMMON.CATEGORY}
|
{locale.COMMON.CATEGORY}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user