medium-menu

This commit is contained in:
tangly1024.com
2023-03-23 12:31:59 +08:00
parent 9db5ba7011
commit 7861c6d6a9
8 changed files with 230 additions and 98 deletions

View File

@@ -28,28 +28,30 @@ const LayoutBase = props => {
return (
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
<CommonHead meta={meta} />
<div id='theme-medium' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
<CommonHead meta={meta} />
<main id='wrapper' className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
{/* 桌面端左侧菜单 */}
{/* <LeftMenuBar/> */}
<div id='container-inner' className='w-full relative z-10'>
{/* 移动端顶部菜单 */}
{/* 顶部导航栏 */}
<TopNavBar {...props} />
<div className='px-7 max-w-5xl justify-center mx-auto min-h-screen'>
<div id='container-inner' className='px-7 max-w-5xl justify-center mx-auto min-h-screen'>
{slotTop}
{children}
{/* 回顶按钮 */}
<div
data-aos="fade-up"
data-aos-duration="300"
data-aos-once="false"
data-aos-anchor-placement="top-center"
className='fixed xl:right-80 right-2 mr-10 bottom-24 hidden lg:block z-20'>
<i className='fas fa-chevron-up cursor-pointer p-2 rounded-full border' onClick={() => { window.scrollTo({ top: 0, behavior: 'smooth' }) }}/>
data-aos="fade-up"
data-aos-duration="300"
data-aos-once="false"
data-aos-anchor-placement="top-center"
className='fixed xl:right-80 right-2 mr-10 bottom-24 hidden lg:block z-20'>
<i className='fas fa-chevron-up cursor-pointer p-2 rounded-full border' onClick={() => { window.scrollTo({ top: 0, behavior: 'smooth' }) }} />
</div>
</div>
@@ -73,6 +75,7 @@ const LayoutBase = props => {
</div>
</main>
{/* 移动端底部导航栏 */}
<BottomMenuBar {...props} className='block md:hidden' />
</div>
</ThemeGlobalMedium.Provider>

View File

@@ -1,51 +0,0 @@
import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useGlobal } from '@/lib/global'
import CONFIG_MEDIUM from '../config_medium'
function GroupMenu ({ customMenu, customNav }) {
const { locale } = useGlobal()
const router = useRouter()
let links = [
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
{ name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_MEDIUM.MENU_CATEGORY },
{ name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_MEDIUM.MENU_TAG },
{ name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG_MEDIUM.MENU_ARCHIVE }
// { name: locale.NAV.SEARCH, to: '/search', show: CONFIG_MEDIUM.MENU_SEARCH }
]
if (customNav) {
links = links.concat(customNav)
}
return (
<nav id='nav' className=' text-md'>
{links.map(link => {
if (link.show) {
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return (
<Link
key={`${link.to}`}
title={link.to}
href={link.to}
className={'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'text-black' : ' ')}>
<div className='my-auto items-center justify-center flex '>
<div className={ 'hover:text-black'}>{link.name}</div>
</div>
{link.slot}
</Link>
)
} else {
return null
}
})}
</nav>
)
}
export default GroupMenu

View File

@@ -0,0 +1,35 @@
import React from 'react'
import { useGlobal } from '@/lib/global'
import CONFIG_MEDIUM from '../config_medium'
import BLOG from '@/blog.config'
import { MenuItemCollapse } from './MenuItemCollapse'
export const MenuBarMobile = (props) => {
const { customMenu, customNav } = props
const { locale } = useGlobal()
let links = [
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
{ name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_MEDIUM.MENU_CATEGORY },
{ name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_MEDIUM.MENU_TAG },
{ name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG_MEDIUM.MENU_ARCHIVE }
// { name: locale.NAV.SEARCH, to: '/search', show: CONFIG_MEDIUM.MENU_SEARCH }
]
if (customNav) {
links = links.concat(customNav)
}
// 如果 开启自定义菜单,则不再使用 Page生成菜单。
if (BLOG.CUSTOM_MENU) {
links = customMenu
}
return (
<nav id='nav' className=' text-md'>
{/* {links.map(link => <NormalMenu key={link.id} link={link}/>)} */}
{links.map(link => <MenuItemCollapse onHeightChange={props.onHeightChange} key={link.id} link={link}/>)}
</nav>
)
}

View File

@@ -0,0 +1,62 @@
import Collapse from '@/components/Collapse'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
/**
* 折叠菜单
* @param {*} param0
* @returns
*/
export const MenuItemCollapse = (props) => {
const { link } = props
const [show, changeShow] = useState(false)
const hasSubMenu = link?.subMenus?.length > 0
const [isOpen, changeIsOpen] = useState(false)
const router = useRouter()
if (!link || !link.show) {
return null
}
const selected = (router.pathname === link.to) || (router.asPath === link.to)
const toggleShow = () => {
changeShow(!show)
}
const toggleOpenSubMenu = () => {
changeIsOpen(!isOpen)
}
return <>
<div className={ (selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600') + ' px-5 w-full text-left duration-200 dark:bg-hexo-black-gray dark:border-black'} onClick={toggleShow} >
{!hasSubMenu && <Link href={link?.to} className='py-2 w-full my-auto items-center justify-between flex '>
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
</Link>}
{hasSubMenu && <div
onClick={hasSubMenu ? toggleOpenSubMenu : null}
className="py-2 font-extralight flex justify-between cursor-pointer dark:text-gray-200 no-underline tracking-widest">
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
<div className='inline-flex items-center '><i className='px-2 fa fa-plus text-gray-400'></i></div>
</div>}
</div>
{/* 折叠子菜单 */}
{hasSubMenu && <Collapse isOpen={isOpen} onHeightChange={props.onHeightChange}>
{link.subMenus.map(sLink => {
return <div key={sLink.id} className='
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
<Link href={sLink.to}>
<div><div className={`${sLink.icon} text-center w-3 mr-3 text-xs`} />{sLink.title}</div>
</Link>
</div>
})}
</Collapse>}
</>
}

View File

@@ -0,0 +1,48 @@
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'
export const MenuItemDrop = ({ link }) => {
const [show, changeShow] = useState(false)
const router = useRouter()
if (!link || !link.show) {
return null
}
const hasSubMenu = link?.subMenus?.length > 0
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return <li className='cursor-pointer list-none items-center flex' onMouseOver={() => changeShow(true)} onMouseOut={() => changeShow(false)} >
{hasSubMenu &&
<div className={'h-full whitespace-nowrap px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<div className='px-1'>
{link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
</div>
</div>
}
{!hasSubMenu &&
<div className={'h-full whitespace-nowrap px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<Link href={link?.to} className='px-1'>
{link?.name}
</Link>
</div>
}
{/* 子菜单 */}
{hasSubMenu && <ul className={`${show ? 'visible opacity-100' : 'invisible opacity-0'} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 top-12 absolute block drop-shadow-lg `}>
{link.subMenus.map(sLink => {
return <li key={sLink.id} className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-2'>
<Link href={sLink.to}>
<span className='text-xs font-extralight'>{sLink.title}</span>
</Link>
</li>
})}
</ul>}
</li>
}

View File

@@ -0,0 +1,27 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
export const NormalMenu = props => {
const { link } = props
const router = useRouter()
if (!link || !link.show) {
return null
}
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return <Link
key={`${link.to}`}
title={link.to}
href={link.to}
className={'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'text-black' : ' ')}>
<div className='my-auto items-center justify-center flex '>
<div className={ 'hover:text-black'}>{link.name}</div>
</div>
{link.slot}
</Link>
}

View File

@@ -0,0 +1,24 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
export const MenuItemPCNormal = props => {
const { link } = props
const router = useRouter()
const selected = (router.pathname === link.to) || (router.asPath === link.to)
if (!link || !link.show) {
return null
}
return <Link
key={`${link.id}-${link.to}`}
title={link.to}
href={link.to}
className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<div className='items-center justify-center flex '>
<i className={link.icon} />
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
</div>
{link.slot}
</Link>
}

View File

@@ -1,11 +1,11 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import LogoBar from './LogoBar'
import React from 'react'
import React, { useRef } from 'react'
import Collapse from '@/components/Collapse'
import GroupMenu from './GroupMenu'
import { MenuBarMobile } from './MenuBarMobile'
import { useGlobal } from '@/lib/global'
import CONFIG_MEDIUM from '../config_medium'
import BLOG from '@/blog.config'
import { MenuItemDrop } from './MenuItemDrop'
/**
* 顶部导航栏 + 菜单
@@ -13,9 +13,9 @@ import CONFIG_MEDIUM from '../config_medium'
* @returns
*/
export default function TopNavBar(props) {
const { className, customNav } = props
const router = useRouter()
const { className, customNav, customMenu } = props
const [isOpen, changeShow] = React.useState(false)
const collapseRef = useRef(null)
const { locale } = useGlobal()
@@ -26,59 +26,43 @@ export default function TopNavBar(props) {
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG_MEDIUM.MENU_SEARCH }
]
const navs = defaultLinks.concat(customNav)
let links = defaultLinks.concat(customNav)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
// 如果 开启自定义菜单则覆盖Page生成的菜单
if (BLOG.CUSTOM_MENU) {
links = customMenu
}
return (
<div id='top-nav' className={'sticky top-0 lg:relative w-full z-40 ' + className}>
{/* 折叠菜单 */}
<Collapse type='vertical' isOpen={isOpen} className='md:hidden'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 px-7 lg:hidden '>
<GroupMenu {...props} />
{/* 移动端折叠菜单 */}
<Collapse type='vertical'collapseRef={collapseRef} isOpen={isOpen} className='md:hidden'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 lg:hidden '>
<MenuBarMobile {...props} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />
</div>
</Collapse>
{/* 导航栏菜单 */}
<div className='flex w-full h-12 shadow bg-white dark:bg-hexo-black-gray px-7 items-between'>
{/* 图标Logo */}
{/* 左侧图标Logo */}
<LogoBar {...props} />
{/* 右侧功能 */}
{/* 折叠按钮、仅移动端显示 */}
<div className='mr-1 flex md:hidden justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
<div onClick={toggleMenuOpen} className='cursor-pointer'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
</div>
{/* 顶部菜单 */}
{/* 桌面端顶部菜单 */}
<div className='hidden md:flex'>
{navs && navs.map(link => {
if (link?.show) {
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return (
<Link
key={`${link.id}-${link.to}`}
title={link.to}
href={link.to}
target={link.to.indexOf('http') === 0 ? '_blank' : '_self'}
className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<div className='items-center justify-center flex '>
<i className={link.icon} />
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
</div>
{link.slot}
</Link>
)
} else {
return null
}
})}
{links && links.map(link => <MenuItemDrop key={link.id} link={link}/>)}
</div>
</div>
</div>