Merge pull request #235 from tangly1024/preview

3.3.5 样式微调、Draggable组件
This commit is contained in:
tangly1024
2022-05-12 15:02:49 +08:00
committed by GitHub
33 changed files with 398 additions and 182 deletions

View File

@@ -1,2 +1,2 @@
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
NEXT_PUBLIC_VERSION=3.3.4
NEXT_PUBLIC_VERSION=3.3.5

74
components/Collapse.js Normal file
View File

@@ -0,0 +1,74 @@
import React from 'react'
/**
* 折叠面板组件,支持水平折叠、垂直折叠
* @param {type:['horizontal','vertical'],isOpen} props
* @returns
*/
const Collapse = props => {
const collapseRef = React.useRef(null)
const type = props.type || 'vertical'
const collapseSection = element => {
const sectionHeight = element.scrollHeight
const sectionWidth = element.scrollWidth
requestAnimationFrame(function () {
switch (type) {
case 'horizontal':
element.style.width = sectionWidth + 'px'
requestAnimationFrame(function () {
element.style.width = 0 + 'px'
})
break
case 'vertical':
element.style.height = sectionHeight + 'px'
requestAnimationFrame(function () {
element.style.height = 0 + 'px'
})
}
})
}
/**
* 展开
* @param {*} element
*/
const expandSection = element => {
const sectionHeight = element.scrollHeight
const sectionWidth = element.scrollWidth
let clearTime = 0
switch (type) {
case 'horizontal':
element.style.width = sectionWidth + 'px'
clearTime = setTimeout(() => {
element.style.width = 'auto'
}, 400)
break
case 'vertical':
element.style.height = sectionHeight + 'px'
clearTime = setTimeout(() => {
element.style.height = 'auto'
}, 400)
}
clearTimeout(clearTime)
}
React.useEffect(() => {
const element = collapseRef.current
if (props.isOpen) {
expandSection(element)
} else {
collapseSection(element)
}
}, [props.isOpen])
return (
<div ref={collapseRef} style={type === 'vertical' ? { height: '0px' } : { width: '0px' }} className={'overflow-hidden duration-200 ' + props.className }>
{props.children}
</div>
)
}
Collapse.defaultProps = { isOpen: false }
export default Collapse

View File

@@ -1,38 +0,0 @@
import React, { useEffect, useRef } from 'react'
const Collapse = props => {
const collapseRef = useRef(null)
const collapseSection = element => {
const sectionWidth = element.scrollWidth
requestAnimationFrame(function () {
element.style.width = sectionWidth + 'px'
requestAnimationFrame(function () {
element.style.width = 0 + 'px'
})
})
}
const expandSection = element => {
const sectionWidth = element.scrollWidth
element.style.width = sectionWidth + 'px'
const clearTime = setTimeout(() => {
element.style.width = 'auto'
}, 400)
clearTimeout(clearTime)
}
useEffect(() => {
const element = collapseRef.current
if (props.isOpen) {
expandSection(element)
} else {
collapseSection(element)
}
}, [props.isOpen])
return (
<div ref={collapseRef} style={{ width: '0px' }} className={'overflow-hidden duration-200 ' + props.className}>
{props.children}
</div>
)
}
Collapse.defaultProps = { isOpen: false }
export default Collapse

View File

@@ -0,0 +1,21 @@
import { useGlobal } from '@/lib/global'
import { saveDarkModeToCookies } from '@/lib/theme'
const DarkModeButton = (props) => {
const { isDarkMode, updateDarkMode } = useGlobal()
// 用户手动设置主题
const handleChangeDarkMode = () => {
const newStatus = !isDarkMode
saveDarkModeToCookies(newStatus)
updateDarkMode(newStatus)
const htmlElement = document.getElementsByTagName('html')[0]
htmlElement.classList?.remove(newStatus ? 'light' : 'dark')
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
}
return <div className={'z-10 duration-200 text-xl cursor-pointer py-2 ' + props.className}>
<i id='darkModeButton' className={`hover:scale-125 transform duration-200 fas ${isDarkMode ? 'fa-sun' : 'fa-moon'}`}
onClick={handleChangeDarkMode} />
</div>
}
export default DarkModeButton

138
components/Draggable.js Normal file
View File

@@ -0,0 +1,138 @@
import React from 'react'
/**
* 可拖拽组件
*/
export const Draggable = (props) => {
const { children } = props
let currentObj, offsetX, offsetY// 初始化变量,定义备用变量
React.useEffect(() => {
const draggableElements = document.getElementsByClassName('draggable')
// 标准化鼠标事件对象
function e(event) { // 定义事件对象标准化函数
if (!event) { // 兼容IE浏览器
event = window.event
event.target = event.srcElement
event.layerX = event.offsetX
event.layerY = event.offsetY
}
// 移动端
if (event.type === 'touchstart' || event.type === 'touchmove') {
event.clientX = event.touches[0].clientX
event.clientY = event.touches[0].clientY
}
event.mx = event.pageX || event.clientX + document.body.scrollLeft
// 计算鼠标指针的x轴距离
event.my = event.pageY || event.clientY + document.body.scrollTop
// 计算鼠标指针的y轴距离
return event // 返回标准化的事件对象
}
// 定义鼠标事件处理函数
// document.pointerdown = start
document.onmousedown = start
document.ontouchstart = start
function start (event) { // 按下鼠标时,初始化处理
if (!draggableElements) return
event = e(event)// 获取标准事件对象
for (const drag of draggableElements) {
// 判断鼠标点击的区域是否是拖拽框内
if (inDragBox(event, drag)) {
currentObj = drag.firstElementChild
}
}
if (currentObj) {
if (event.type === 'touchstart') {
document.documentElement.style.overflow = 'hidden' // 防止页面一起滚动
}
offsetX = event.mx - currentObj.offsetLeft
offsetY = event.my - currentObj.offsetTop
document.onmousemove = move// 注册鼠标移动事件处理函数
document.ontouchmove = move
document.onmouseup = stop// 注册松开鼠标事件处理函数
document.ontouchend = stop
}
}
function move(event) { // 鼠标移动处理函数
event = e(event)
if (currentObj) {
const left = event.mx - offsetX// 定义拖动元素的x轴距离
const top = event.my - offsetY// 定义拖动元素的y轴距离
currentObj.style.left = left + 'px'// 定义拖动元素的x轴距离
currentObj.style.top = top + 'px'// 定义拖动元素的y轴距离
checkInWindow()
}
}
function stop(event) { // 松开鼠标处理函数
event = e(event)
// 释放所有操作对象
document.documentElement.style.overflow = 'auto' // 解除页面滚动限制
currentObj = document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
}
/**
* 鼠标是否在可拖拽区域内
* @param {*} event
* @returns
*/
function inDragBox(event, drag) {
const { clientX, clientY } = event // 鼠标位置
const { offsetHeight, offsetWidth, offsetTop, offsetLeft } = drag.firstElementChild // 窗口位置
const horizontal = clientX > offsetLeft && clientX < offsetLeft + offsetWidth
const vertical = clientY > offsetTop && clientY < offsetTop + offsetHeight
if (horizontal && vertical) {
return true
}
return false
}
/**
* 若超出窗口则吸附。
*/
function checkInWindow() {
// 检查是否悬浮在窗口内
for (const drag of draggableElements) {
// 判断鼠标点击的区域是否是拖拽框内
const { offsetHeight, offsetWidth, offsetTop, offsetLeft } = drag.firstElementChild
const { clientHeight, clientWidth } = document.documentElement
if (offsetTop < 0) {
drag.firstElementChild.style.top = 0
}
if (offsetTop > (clientHeight - offsetHeight)) {
drag.firstElementChild.style.top = clientHeight - offsetHeight + 'px'
}
if (offsetLeft < 0) {
drag.firstElementChild.style.left = 0
}
if (offsetLeft > (clientWidth - offsetWidth)) {
drag.firstElementChild.style.left = clientWidth - offsetWidth + 'px'
}
}
}
window.addEventListener('resize', checkInWindow)
return () => {
return () => {
window.removeEventListener('resize', checkInWindow)
}
}
}, [])
return <div className='draggable cursor-move'>
{children}
</div>
}
Draggable.defaultProps = { left: 0, top: 0 }

View File

@@ -1,17 +1,22 @@
import { useGlobal } from '@/lib/global'
import React from 'react'
import { Draggable } from './Draggable'
/**
*
* @returns 主题切换
*/
export function ThemeSwitch () {
export function ThemeSwitch() {
const { theme, switchTheme } = useGlobal()
return (
<div draggable="true" className="fixed left-4 bottom-12 text-white bg-black rounded z-50">
<div className="p-2 cursor-pointer" onClick={switchTheme}>
<i className="fas fa-sync mr-1" />
切换主题{theme}
</div>
</div>
return (<>
<Draggable>
<div id="draggableBox" style={{ left: '10px', top: '90vh' }} className="fixed text-white bg-black z-50 rounded-lg shadow-card">
<div className="p-2 flex items-center">
<i className="fas fa-palette mr-1 cursor-move" />
<div className='uppercase font-sans whitespace-nowrap' >{theme} <i className='fas fa-sync cursor-pointer border-l pl-2' title='click to change theme' alt='click to change theme' onClick={switchTheme} /></div>
</div>
</div>
</Draggable>
</>
)
}

View File

@@ -1,6 +1,6 @@
{
"name": "notion-next",
"version": "3.3.4",
"version": "3.3.5",
"homepage": "https://github.com/tangly1024/NotionNext.git",
"license": "MIT",
"repository": {

View File

@@ -15,7 +15,7 @@ import JumpToTopButton from './components/JumpToTopButton'
const LayoutBase = props => {
const { children, meta } = props
return (
<div className='dark:text-gray-300'>
<div className='dark:text-gray-300 bg-white dark:bg-black'>
<CommonHead meta={meta} />
{/* 顶栏LOGO */}
<Header {...props} />
@@ -24,7 +24,7 @@ const LayoutBase = props => {
<Nav {...props} />
{/* 主体 */}
<div className="w-full bg-white">
<div className="w-full">
<Title {...props} />

View File

@@ -28,9 +28,9 @@ export const ArticleLock = props => {
<div className='text-center space-y-3'>
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
<div className='flex'>
<input id="password" type='password' className='w-full text-sm pl-5 rounded-l transition focus:shadow-lgfont-light leading-10 text-black bg-white dark:bg-gray-500'></input>
<input id="password" type='password' className='w-full text-sm pl-5 rounded-l transition font-light leading-10 text-black dark:bg-gray-500 bg-gray-50'></input>
<div onClick={submitPassword} className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 rounded-r duration-300 bg-gray-300" >
<i className={'duration-200 cursor-pointer fas fa-key'} >&nbsp;{locale.COMMON.SUBMIT}</i>
<i className={'duration-200 cursor-pointer fas fa-key dark:text-black'} >&nbsp;{locale.COMMON.SUBMIT}</i>
</div>
</div>
<div id='tips'>

View File

@@ -24,19 +24,19 @@ export const BlogList = (props) => {
<article key={p.id} className="mb-12" >
<h2 className="mb-4">
<Link href={`/article/${p.slug}`}>
<a className="text-black text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
<a className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
</Link>
</h2>
<div className="mb-4 text-sm text-gray-700">
by <a href="#" className="text-gray-700">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
<span className="font-bold mx-1"> | </span>
<a href="#" className="text-gray-700">{p.category}</a>
<a href="#" className="text-gray-700 dark:text-gray-300">{p.category}</a>
{/* <span className="font-bold mx-1"> | </span> */}
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
</div>
<p className="text-gray-700 leading-normal">
<p className="text-gray-700 dark:text-gray-400 leading-normal">
{p.summary}
</p>
{/* 搜索结果 */}

View File

@@ -1,45 +1,21 @@
import BLOG from '@/blog.config'
import DarkModeButton from '@/components/DarkModeButton'
export const Footer = (props) => {
const d = new Date()
const currentYear = d.getFullYear()
const startYear = BLOG.SINCE && BLOG.SINCE !== currentYear && BLOG.SINCE + '-'
// {/* 页脚 */}
// <footer className="font-sans dark:bg-gray-900 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6">
// <i className="fas fa-copyright" /> {`${startYear}${currentYear}`}{' '}
return <footer className="w-full bg-white px-6 border-t dark:border-hexo-black-gray dark:bg-hexo-black-gray ">
<DarkModeButton className='text-center pt-4'/>
// <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 />
// <h1>{meta?.title || siteInfo?.title}</h1>
// <span className='text-xs font-serif'>
// Powered by{' '}
// <a
// href="https://github.com/tangly1024/NotionNext"
// className="underline dark:text-gray-300"
// >
// NotionNext {BLOG.VERSION}
// </a>
// .
// </span>
// </footer>
return <footer className="w-full bg-white px-6 border-t">
<div className="container mx-auto max-w-4xl py-6 flex flex-wrap md:flex-no-wrap justify-between items-center text-sm">
&copy;{`${startYear}${currentYear}`} {BLOG.AUTHOR}. All rights reserved.
<div className="pt-4 md:p-0 text-center md:text-right text-xs">
<div className="container mx-auto max-w-4xl py-6 md:flex flex-wrap md:flex-no-wrap md:justify-between items-center text-sm">
<div className='text-center'> &copy;{`${startYear}${currentYear}`} {BLOG.AUTHOR}. All rights reserved.</div>
<div className="md:p-0 text-center md:text-right text-xs">
{/* 右侧链接 */}
{/* <a href="#" className="text-black no-underline hover:underline">Privacy Policy</a> */}
{BLOG.BEI_AN && (<a href="https://beian.miit.gov.cn/" className="text-black no-underline hover:underline ml-4">{BLOG.BEI_AN} </a>)}
<span className='text-black no-underline ml-4'>
{BLOG.BEI_AN && (<a href="https://beian.miit.gov.cn/" className="text-black dark:text-gray-200 no-underline hover:underline ml-4">{BLOG.BEI_AN} </a>)}
<span className='dark:text-gray-200 no-underline ml-4'>
Powered by
<a href="https://github.com/tangly1024/NotionNext" className=' hover:underline'> NotionNext {BLOG.VERSION} </a>
</span>

View File

@@ -7,7 +7,7 @@ import Link from 'next/link'
export const Header = (props) => {
const { siteInfo } = props
return <header className="w-full px-6 bg-white">
return <header className="w-full px-6 bg-white dark:bg-black">
<div className="container mx-auto max-w-4xl md:flex justify-between items-center">
<Link href='/'>
<a className="py-6 w-full text-center md:text-left md:w-auto text-gray-dark no-underline flex justify-center items-center">

View File

@@ -20,12 +20,12 @@ export const Nav = (props) => {
links = links.concat(customNav)
}
return <nav className="w-full bg-white md:pt-0 px-6 relative z-20 border-t border-b border-gray-light">
return <nav className="w-full bg-white md:pt-0 px-6 relative z-20 border-t border-b border-gray-light dark:border-hexo-black-gray dark:bg-black">
<div className="container mx-auto max-w-4xl md:flex justify-between items-center text-sm md:text-md md:justify-start">
<div className="w-full md:w-2/3 text-center md:text-left py-4 flex flex-wrap justify-center items-stretch md:justify-start md:items-start">
{links.map(link => {
return link && <Link href={link.to}>
<a className="px-2 md:pl-0 md:mr-3 md:pr-3 text-gray-700 no-underline md:border-r border-gray-light">
return link && <Link href={link.to} key={link.to}>
<a className="px-2 md:pl-0 md:mr-3 md:pr-3 text-gray-700 dark:text-gray-200 no-underline md:border-r border-gray-light">
{link.name}
</a>
</Link>

View File

@@ -8,7 +8,7 @@ export const SideBar = (props) => {
return <div className="w-full md:w-64 sticky top-8">
<aside className="rounded shadow overflow-hidden mb-6">
<h3 className="text-sm bg-gray-100 text-gray-700 py-3 px-4 border-b">{locale.COMMON.CATEGORY}</h3>
<h3 className="text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b">{locale.COMMON.CATEGORY}</h3>
<div className="p-4">
<ul className="list-reset leading-normal">
@@ -23,7 +23,7 @@ export const SideBar = (props) => {
</aside>
<aside className="rounded shadow overflow-hidden mb-6">
<h3 className="text-sm bg-gray-100 text-gray-700 py-3 px-4 border-b">{locale.COMMON.LATEST_POSTS}</h3>
<h3 className="text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b">{locale.COMMON.LATEST_POSTS}</h3>
<div className="p-4">
<ul className="list-reset leading-normal">

View File

@@ -10,7 +10,7 @@ export const Title = (props) => {
const title = post?.title || siteInfo?.description
const description = post?.description || BLOG.AUTHOR
return <div className="text-center px-6 py-12 mb-6 bg-gray-100 border-b">
return <div className="text-center px-6 py-12 mb-6 bg-gray-100 dark:bg-hexo-black-gray dark:border-hexo-black-gray border-b">
<h1 className=" text-xl md:text-4xl pb-4">{title}</h1>
<p className="leading-loose text-gray-dark">
{description}

View File

@@ -11,12 +11,12 @@ export default function ArticleAround ({ prev, next }) {
}
return <section className='text-gray-800 h-28 flex items-center justify-between space-x-5 my-4'>
<Link href={`/article/${prev.slug}`} passHref>
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-gray-500 hover:text-white duration-300'>
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
</a>
</Link>
<Link href={`/article/${next.slug}`} passHref>
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-gray-500 hover:text-white duration-300'>{next.title}
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>{next.title}
<i className='ml-1 my-1 fas fa-angle-double-right' />
</a>
</Link>

View File

@@ -25,7 +25,7 @@ export default function ArticleDetail(props) {
</div>
)}
<article itemScope itemType="https://schema.org/Movie"
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-32 dark:border-gray-700 bg-white dark:bg-gray-800"
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-32 dark:border-gray-700 bg-white dark:bg-hexo-black-gray"
>
<header className='animate__slideInDown animate__animated'>
@@ -92,7 +92,7 @@ export default function ArticleDetail(props) {
<ArticleAround prev={prev} next={next} />
{/* 评论互动 */}
<div className="duration-200 shadow px-12 w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-800">
<div className="duration-200 shadow px-12 w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-hexo-black-gray">
<Comment frontMatter={post} />
</div>
</div>)

View File

@@ -6,11 +6,12 @@ import SearchInput from './SearchInput'
import SiteInfo from './SiteInfo'
import Catalog from './Catalog'
import { useRouter } from 'next/router'
import DarkModeButton from '@/components/DarkModeButton'
function AsideLeft (props) {
const { tags, currentTag, categories, currentCategory, post, slot, siteInfo } = props
const router = useRouter()
return <div className='w-72 bg-white dark:bg-gray-800 min-h-screen px-10 py-14 hidden lg:block'>
return <div className='w-72 bg-white dark:bg-hexo-black-gray min-h-screen px-10 py-14 hidden lg:block'>
<Logo {...props}/>
<section className='flex flex-col text-gray-600'>
@@ -43,12 +44,17 @@ function AsideLeft (props) {
<SiteInfo/>
</section>
<section className='flex justify-center dark:text-gray-200'>
<DarkModeButton/>
</section>
<section className='sticky top-0 pt-12'>
<Catalog toc={post?.toc}/>
<div className='flex justify-center'>
<div>{slot}</div>
</div>
</section>
</div>
}

View File

@@ -1,7 +1,7 @@
const Card = ({ children, headerSlot, className }) => {
return <div className={className}>
<>{headerSlot}</>
<section className="shadow mb-4 p-2 bg-white dark:bg-gray-800 hover:shadow-lg duration-200">
<section className="shadow mb-4 p-2 bg-white dark:bg-hexo-black-gray hover:shadow-lg duration-200">
{children}
</section>
</div>

View File

@@ -1,38 +0,0 @@
import React, { useEffect, useRef } from 'react'
const Collapse = props => {
const collapseRef = useRef(null)
const collapseSection = element => {
const sectionHeight = element.scrollHeight
requestAnimationFrame(function () {
element.style.height = sectionHeight + 'px'
requestAnimationFrame(function () {
element.style.height = 0 + 'px'
})
})
}
const expandSection = element => {
const sectionHeight = element.scrollHeight
element.style.height = sectionHeight + 'px'
const clearTime = setTimeout(() => {
element.style.height = 'auto'
}, 400)
clearTimeout(clearTime)
}
useEffect(() => {
const element = collapseRef.current
if (props.isOpen) {
expandSection(element)
} else {
collapseSection(element)
}
}, [props.isOpen])
return (
<div ref={collapseRef} style={{ height: '0px' }} className='overflow-hidden duration-200'>
{props.children}
</div>
)
}
Collapse.defaultProps = { isOpen: false }
export default Collapse

View File

@@ -53,7 +53,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
<input
ref={searchInputRef}
type='text'
className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-800 dark:text-white'}
onKeyUp={handleKeyUp}
onCompositionStart={lockSearchInput}
onCompositionUpdate={lockSearchInput}

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import Collapse from './Collapse'
import Collapse from '@/components/Collapse'
import GroupMenu from './GroupMenu'
import Logo from './Logo'
import SearchInput from './SearchInput'
@@ -20,7 +20,7 @@ const TopNav = props => {
{/* 导航栏 */}
<div id='sticky-nav' className={'lg:relative w-full top-0 z-20 transform duration-500 bg-white dark:bg-black'}>
<Collapse isOpen={isOpen}>
<Collapse type='vertical' isOpen={isOpen}>
<div className='py-1 px-5'>
<GroupMenu {...props} />
<SearchInput {...props} />
@@ -36,8 +36,8 @@ const TopNav = props => {
{/* 右侧功能 */}
<div className='mr-1 flex justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
<div onClick={toggleMenuOpen} className='w-18 cursor-pointer'>
菜单 {isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
<div onClick={toggleMenuOpen} className='cursor-pointer'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
</div>
</div>

View File

@@ -1,5 +1,6 @@
import React from 'react'
import BLOG from '@/blog.config'
import DarkModeButton from '@/components/DarkModeButton'
const Footer = ({ title }) => {
const d = new Date()
@@ -9,6 +10,8 @@ const Footer = ({ title }) => {
<footer
className='font-sans dark:bg-black flex-shrink-0 bg-hexo-light-gray justify-center text-center m-auto w-full leading-6 text-gray-600 dark:text-gray-100 text-sm p-6'
>
<DarkModeButton/>
<i className='fas fa-copyright' /> {`${startYear}${currentYear}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={BLOG.LINK} className='underline font-bold dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br/>
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br/></>}

View File

@@ -13,7 +13,7 @@ export default function SideRight(props) {
} = props
return (
<div className={'space-y-4 lg:w-80 lg:pt-0 lg:px-2 pt-4'}>
<div className={'space-y-4 lg:w-80 lg:pt-0 px-2 pt-4'}>
<InfoCard {...props} />
{CONFIG_HEXO.WIDGET_ANALYTICS && <AnalyticsCard {...props} />}

View File

@@ -102,7 +102,7 @@ const TopNav = props => {
</div>
</div>
<Collapse isOpen={isOpen} className='shadow-xl'>
<Collapse type='vertical' isOpen={isOpen} className='shadow-xl'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 px-5 lg:hidden '>
<MenuList {...props}/>
</div>

View File

@@ -17,7 +17,7 @@ export const LayoutSearch = (props) => {
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`)
}
},
100)
100)
})
return <LayoutBase {...props}>
<div className='py-12'>

View File

@@ -4,7 +4,7 @@ import JumpToTopButton from './JumpToTopButton'
export default function BottomMenuBar ({ className }) {
return (
<div className={'sticky bottom-0 w-full h-12 bg-white ' + className}>
<div className={'sticky 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='/' passHref>
<div className='flex w-full items-center justify-center cursor-pointer'>

View File

@@ -1,5 +1,6 @@
import React from 'react'
import BLOG from '@/blog.config'
import DarkModeButton from '@/components/DarkModeButton'
const Footer = ({ title }) => {
const d = new Date()
@@ -9,6 +10,7 @@ const Footer = ({ title }) => {
<footer
className='dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-gray-400 text-sm p-6'
>
<DarkModeButton/>
<i className='fas fa-copyright' /> {`${startYear}${currentYear}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br/>
{BLOG.BEI_AN && <><i className='fas fa-shield-alt'/> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br/></>}

View File

@@ -0,0 +1,43 @@
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 ({ 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='font-sans text-sm'>
{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} >
<a 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}
</a>
</Link>
} else {
return null
}
})}
</nav>
}
export default GroupMenu

View File

@@ -1,40 +1,60 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import LogoBar from './LogoBar'
import React from 'react'
import Collapse from '@/components/Collapse'
import GroupMenu from './GroupMenu'
/**
* 顶部导航栏 + 菜单
* @param {} param0
* @returns
*/
export default function TopNavBar (props) {
export default function TopNavBar(props) {
const { className, customNav } = props
const router = useRouter()
const [isOpen, changeShow] = React.useState(false)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
return <div id='top-nav' className={'sticky top-0 lg:relative w-full z-40 ' + className}>
<div className='flex w-full h-12 shadow bg-white dark:bg-hexo-black-gray px-5 items-between'>
<LogoBar {...props}/>
<Collapse type='vertical' isOpen={isOpen} className='md:hidden'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 px-5 lg:hidden '>
<GroupMenu {...props} />
</div>
</Collapse>
{/* 顶部菜单 */}
<div className='flex'>
{customNav && customNav.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} >
<a target={link.to.indexOf('http') === 0 ? '_blank' : '_self'} className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 hover:underline 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 className='flex w-full h-12 shadow bg-white dark:bg-hexo-black-gray px-5 items-between'>
<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>
{link.slot}
</a>
</Link>
} else {
return null
}
})}
</div>
</div>
{/* 顶部菜单 */}
<div className='hidden md:flex'>
{customNav && customNav.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} >
<a target={link.to.indexOf('http') === 0 ? '_blank' : '_self'} className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 hover:underline 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}
</a>
</Link>
} else {
return null
}
})}
</div>
</div>
</div>
</div>
}

View File

@@ -1,16 +1,18 @@
import { useGlobal } from '@/lib/global'
import { loadDarkModeFromCookies, saveDarkModeToCookies } from '@/lib/theme'
import { saveDarkModeToCookies } from '@/lib/theme'
const DarkModeButton = () => {
const { updateDarkMode } = useGlobal()
const isDarkMode = loadDarkModeFromCookies()
const { isDarkMode, updateDarkMode } = useGlobal()
// 用户手动设置主题
const handleChangeDarkMode = () => {
const newTheme = (isDarkMode ? 'dark' : 'light')
saveDarkModeToCookies(newTheme)
updateDarkMode(newTheme)
const newStatus = !isDarkMode
saveDarkModeToCookies(newStatus)
updateDarkMode(newStatus)
const htmlElement = document.getElementsByTagName('html')[0]
htmlElement.classList?.remove(newStatus ? 'light' : 'dark')
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
}
return <div className='z-10 duration-200 text-xs cursor-pointer py-1.5 px-1'>
<i id='darkModeButton' className={`hover:scale-125 transform duration-200 fas ${isDarkMode ? 'fa-sun' : 'fa-moon'}`}
onClick={handleChangeDarkMode} />

View File

@@ -1,5 +1,6 @@
import React from 'react'
import BLOG from '@/blog.config'
import DarkModeButton from '@/components/DarkModeButton'
const Footer = ({ title }) => {
const d = new Date()
@@ -9,6 +10,7 @@ const Footer = ({ title }) => {
<footer
className='dark:bg-gray-900 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 dark:text-gray-400'
>
<DarkModeButton/>
<i className='fas fa-copyright' /> {`${startYear}${currentYear}`} <span><i className='mx-1 animate-pulse fas fas-heart' /> <a href={BLOG.LINK} className='underline font-bold '>{BLOG.AUTHOR}</a>.<br />
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br /></>}

View File

@@ -109,7 +109,7 @@ const TopNav = (props) => {
</div>
</div>
<Collapse isOpen={isOpen}>
<Collapse type='vertical' isOpen={isOpen}>
<div className='bg-white py-1 px-5'>
<MenuButtonGroup {...props} from='top'/>
</div>