This commit is contained in:
GH Action - Upstream Sync
2024-05-30 01:09:37 +00:00
22 changed files with 337 additions and 221 deletions

View File

@@ -57,12 +57,10 @@ export default function CustomContextMenu(props) {
}
/**
* 鼠标点击事件
* 鼠标点击即关闭菜单
*/
const handleClick = event => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setShow(false)
}
setShow(false)
}
window.addEventListener('contextmenu', handleContextMenu)
@@ -88,7 +86,6 @@ export default function CustomContextMenu(props) {
function handleScrollTop() {
window.scrollTo({ top: 0, behavior: 'smooth' })
setShow(false)
}
function handleCopyLink() {
@@ -96,12 +93,12 @@ export default function CustomContextMenu(props) {
navigator.clipboard
.writeText(url)
.then(() => {
console.log('页面地址已复制')
// console.log('页面地址已复制')
alert(`${locale.COMMON.PAGE_URL_COPIED} : ${url}`)
})
.catch(error => {
console.error('复制页面地址失败:', error)
})
setShow(false)
}
/**
@@ -130,8 +127,6 @@ export default function CustomContextMenu(props) {
} else {
// alert("Please select some text first.");
}
setShow(false)
}
function handleChangeDarkMode() {

View File

@@ -20,6 +20,8 @@ export default function LazyImage({
style
}) {
const maxWidth = siteConfig('IMAGE_COMPRESS_WIDTH')
const defaultPlaceholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
const imageRef = useRef(null)
const [adjustedSrc, setAdjustedSrc] = useState(
placeholderSrc || siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
@@ -37,6 +39,14 @@ export default function LazyImage({
onLoad() // 触发传递的onLoad回调函数
}
}
/**
* 图片加载失败回调
*/
const handleImageError = () => {
if (imageRef.current) {
imageRef.current.src = defaultPlaceholderSrc
}
}
useEffect(() => {
const adjustedImageSrc = adjustImgSize(src, maxWidth)
@@ -71,7 +81,8 @@ export default function LazyImage({
ref: imageRef,
src: priority ? adjustedSrc : placeholderSrc,
alt: alt,
onLoad: handleImageLoad
onLoad: handleImageLoad,
onError: handleImageError // 添加onError处理函数
}
if (id) {

View File

@@ -33,10 +33,14 @@ export default {
COPYRIGHT: 'Copyright',
AUTHOR: 'Author',
URL: 'URL',
NOW: 'NOW',
RECOMMEND_BADGES: 'Recommend',
BLOG: 'Blog',
POSTS: 'Posts',
ARTICLE: 'Article',
VISITORS: 'Visitors',
VIEWS: 'Views',
PAGE_URL_COPIED: 'Page URL copied',
COPYRIGHT_NOTICE:
'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
RESULT_OF_SEARCH: 'Results Found',

View File

@@ -35,10 +35,14 @@ export default {
AUTHOR: '作者',
URL: '链接',
ANALYTICS: '统计',
RECOMMEND_BADGES: '荐',
BLOG: '博客',
NOW: '此刻',
POSTS: '篇文章',
ARTICLE: '文章',
VISITORS: '位访客',
VIEWS: '次查看',
PAGE_URL_COPIED: '页面地址已复制',
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
RESULT_OF_SEARCH: '篇搜索到的结果',
NO_RESULTS_FOUND: '没有找到文章',

View File

@@ -1451,6 +1451,7 @@ code[class*='language-'] {
.notion-collection-card-property .notion-page-title-text {
border-bottom: 0 none;
@apply dark:text-gray-200;
}
.notion-collection-card-property

View File

@@ -50,11 +50,11 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
{/* 分类 */}
{post?.category && (
<div
className={`flex mb-1 items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-500 text-gray-600 `}>
className={`flex mb-1 items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-300 text-gray-600 hover:text-indigo-700 dark:hover:text-yellow-500`}>
<Link
passHref
href={`/category/${post.category}`}
className='cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:hover:text-yellow-700 dark:text-gray-600 transform'>
className='cursor-pointer text-xs font-normal menu-link '>
{post.category}
</Link>
</div>

View File

@@ -29,23 +29,40 @@ export default function CategoryBar(props) {
}
}
return <div id='category-bar' className={`flex flex-nowrap justify-between items-center h-12 mb-4 space-x-2 w-full lg:bg-white dark:lg:bg-[#1e1e1e]
return (
<div
id='category-bar'
className={`flex flex-nowrap justify-between items-center h-12 mb-4 space-x-2 w-full lg:bg-white dark:lg:bg-[#1e1e1e]
${border ? 'lg:border lg:hover:border dark:lg:border-gray-800 hover:border-indigo-600 dark:hover:border-yellow-600 ' : ''} py-2 lg:px-2 rounded-xl transition-colors duration-200`}>
<div
id='category-bar-items'
ref={categoryBarItemsRef}
className='scroll-smooth max-w-4xl rounded-lg scroll-hidden flex justify-start flex-nowrap items-center overflow-x-scroll'>
<MenuItem href='/' name={locale.NAV.INDEX} />
{categoryOptions?.map((c, index) => (
<MenuItem key={index} href={`/category/${c.name}`} name={c.name} />
))}
</div>
<div id='category-bar-items' ref={categoryBarItemsRef} className='scroll-smooth max-w-4xl rounded-lg scroll-hidden flex justify-start flex-nowrap items-center overflow-x-scroll'>
<MenuItem href='/' name={locale.NAV.INDEX} />
{categoryOptions?.map((c, index) => <MenuItem key={index} href={`/category/${c.name}`} name={c.name} />)}
</div>
<div id='category-bar-next' className='flex items-center justify-center'>
<div id='right' className='cursor-pointer mx-2' onClick={handleToggleScroll}>
{scrollRight ? <ChevronDoubleLeft className={'w-5 h-5'} /> : <ChevronDoubleRight className={'w-5 h-5'} /> }
</div>
<Link href='/category' className='whitespace-nowrap font-bold text-gray-900 dark:text-white transition-colors duration-200 hover:text-indigo-600'>
{locale.MENU.CATEGORY}
</Link>
<div id='category-bar-next' className='flex items-center justify-center'>
<div
id='right'
className='cursor-pointer mx-2 dark:text-gray-300 dark:hover:text-yellow-600 hover:text-indigo-600'
onClick={handleToggleScroll}>
{scrollRight ? (
<ChevronDoubleLeft className={'w-5 h-5'} />
) : (
<ChevronDoubleRight className={'w-5 h-5'} />
)}
</div>
<Link
href='/category'
className='whitespace-nowrap font-bold text-gray-900 dark:text-white transition-colors duration-200 hover:text-indigo-600 dark:hover:text-yellow-600'>
{locale.MENU.CATEGORY}
</Link>
</div>
</div>
)
}
/**
@@ -57,7 +74,10 @@ const MenuItem = ({ href, name }) => {
const router = useRouter()
const { category } = router.query
const selected = category === name
return <div className={`whitespace-nowrap mr-2 duration-200 transition-all font-bold px-2 py-0.5 rounded-md text-gray-900 dark:text-white hover:text-white hover:bg-indigo-600 dark:hover:bg-yellow-600 ${selected ? 'text-white bg-indigo-600 dark:bg-yellow-600' : ''}`}>
<Link href={href}>{name}</Link>
return (
<div
className={`whitespace-nowrap mr-2 duration-200 transition-all font-bold px-2 py-0.5 rounded-md text-gray-900 dark:text-white hover:text-white hover:bg-indigo-600 dark:hover:bg-yellow-600 ${selected ? 'text-white bg-indigo-600 dark:bg-yellow-600' : ''}`}>
<Link href={href}>{name}</Link>
</div>
)
}

View File

@@ -1,49 +1,66 @@
import SocialButton from './SocialButton'
import { siteConfig } from '@/lib/config'
import SocialButton from './SocialButton'
const Footer = ({ title }) => {
const d = new Date()
const currentYear = d.getFullYear()
const since = siteConfig('SINCE')
const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
const copyrightDate =
parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
return (
<footer
className='relative flex-shrink-0 bg-white dark:bg-[#1a191d] justify-center text-center m-auto w-full leading-6 text-gray-600 dark:text-gray-100 text-sm'
>
<footer className='relative flex-shrink-0 bg-white dark:bg-[#1a191d] justify-center text-center m-auto w-full leading-6 text-gray-600 dark:text-gray-100 text-sm'>
{/* 颜色过度区 */}
<div
id='color-transition'
className='h-32 bg-gradient-to-b from-[#f7f9fe] to-white dark:bg-[#1a191d] dark:from-inherit dark:to-inherit'
/>
{/* 颜色过度区 */}
<div id='color-transition' className='h-32 bg-gradient-to-b from-[#f7f9fe] to-white dark:bg-[#1a191d] dark:from-inherit dark:to-inherit'>
{/* 社交按钮 */}
<div className='w-full h-24'>
<SocialButton />
</div>
</div>
<br />
{/* 社交按钮 */}
<div className='w-full h-24'>
<SocialButton />
</div>
<br />
{/* 底部页面信息 */}
<div id='footer-bottom' className='w-full h-20 flex flex-col p-3 lg:flex-row justify-between px-6 items-center bg-[#f1f3f7] dark:bg-[#30343f]'>
<div id='footer-bottom-left'>
NotionNext {siteConfig('VERSION')} <i className='fas fa-copyright' /> {`${copyrightDate}`} <i className='mx-1 animate-pulse fas fa-heart' /> <a href={siteConfig('LINK')} className='underline font-bold dark:text-gray-300 '>{siteConfig('AUTHOR')}</a>.
</div>
<div id='footer-bottom-right'>
{siteConfig('BEI_AN') && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{siteConfig('BEI_AN')}</a></>}
<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>
{/* <h1 className='text-xs pt-4 text-light-400 dark:text-gray-400'>{title} {siteConfig('BIO') && <>|</>} {siteConfig('BIO')}</h1> */}
</div>
{/* 底部页面信息 */}
<div
id='footer-bottom'
className='w-full h-20 flex flex-col p-3 lg:flex-row justify-between px-6 items-center bg-[#f1f3f7] dark:bg-[#21232A] border-t dark:border-t-[#3D3D3F]'>
<div id='footer-bottom-left'>
NotionNext {siteConfig('VERSION')} <i className='fas fa-copyright' />{' '}
{`${copyrightDate}`} <i className='mx-1 animate-pulse fas fa-heart' />{' '}
<a
href={siteConfig('LINK')}
className='underline font-bold dark:text-gray-300 '>
{siteConfig('AUTHOR')}
</a>
.
</div>
</footer >
<div id='footer-bottom-right'>
{siteConfig('BEI_AN') && (
<>
<i className='fas fa-shield-alt' />{' '}
<a href='https://beian.miit.gov.cn/' className='mr-2'>
{siteConfig('BEI_AN')}
</a>
</>
)}
<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>
{/* <h1 className='text-xs pt-4 text-light-400 dark:text-gray-400'>{title} {siteConfig('BIO') && <>|</>} {siteConfig('BIO')}</h1> */}
</div>
</div>
</footer>
)
}

View File

@@ -1,6 +1,7 @@
import { siteConfig } from '@/lib/config'
import { isBrowser } from '@/lib/utils'
import throttle from 'lodash.throttle'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useRef, useState } from 'react'
import DarkModeButton from './DarkModeButton'
import Logo from './Logo'
@@ -21,6 +22,7 @@ const Header = props => {
const [navBgWhite, setBgWhite] = useState(false)
const [activeIndex, setActiveIndex] = useState(0)
const router = useRouter()
const slideOverRef = useRef()
const toggleMenuOpen = () => {
@@ -34,15 +36,15 @@ const Header = props => {
throttle(() => {
const scrollS = window.scrollY
// 导航栏设置 白色背景
if (scrollS <= 0) {
if (scrollS <= 1) {
setFixedNav(false)
setBgWhite(false)
setTextWhite(false)
// 文章详情页特殊处理
if (document.querySelector('#post-bg')) {
if (document?.querySelector('#post-bg')) {
setFixedNav(true)
setTextWhite(true)
setBgWhite(false)
}
} else {
// 向下滚动后的导航样式
@@ -50,19 +52,21 @@ const Header = props => {
setTextWhite(false)
setBgWhite(true)
}
}, 200)
}, 100)
)
useEffect(() => {
scrollTrigger()
}, [router])
// 监听滚动
useEffect(() => {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
}, [])
// 监听导航栏显示文字
// 导航栏根据滚动轮播菜单内容
useEffect(() => {
let prevScrollY = 0
let ticking = false
@@ -71,17 +75,14 @@ const Header = props => {
if (!ticking) {
window.requestAnimationFrame(() => {
const currentScrollY = window.scrollY
if (currentScrollY > prevScrollY) {
setActiveIndex(1) // 向下滚动时设置activeIndex为1
} else {
setActiveIndex(0) // 向上滚动时设置activeIndex为0
}
prevScrollY = currentScrollY
ticking = false
})
ticking = true
}
}
@@ -131,6 +132,11 @@ const Header = props => {
}
`}</style>
{/* fixed时留白高度 */}
{fixedNav && !document?.querySelector('#post-bg') && (
<div className='h-16'></div>
)}
{/* 顶部导航菜单栏 */}
<nav
id='nav'
@@ -158,7 +164,7 @@ const Header = props => {
</div>
{/* 右侧固定 */}
<div className='flex flex-shrink-0 justify-center items-center'>
<div className='flex flex-shrink-0 justify-end items-center w-48'>
<RandomPostButton {...props} />
<SearchButton {...props} />
{!JSON.parse(siteConfig('THEME_SWITCH')) && (

View File

@@ -2,6 +2,7 @@
import { ArrowSmallRight, PlusSmall } from '@/components/HeroIcons'
import LazyImage from '@/components/LazyImage'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useImperativeHandle, useRef, useState } from 'react'
@@ -206,6 +207,7 @@ function GroupMenu() {
*/
function TopGroup(props) {
const { latestPosts, allNavPages, siteInfo } = props
const { locale } = useGlobal()
const todayCardRef = useRef()
function handleMouseLeave() {
todayCardRef.current.coverUp()
@@ -238,7 +240,7 @@ function TopGroup(props) {
</div>
{/* hover 悬浮的 ‘荐’ 字 */}
<div className='opacity-0 group-hover:opacity-100 -translate-x-4 group-hover:translate-x-0 duration-200 transition-all absolute -top-2 -left-2 bg-indigo-600 dark:bg-yellow-600 text-white rounded-xl overflow-hidden pr-2 pb-2 pl-4 pt-4 text-xs'>
{locale.COMMON.RECOMMEND_BADGES}
</div>
</div>
</Link>
@@ -304,6 +306,7 @@ function getTopPosts({ latestPosts, allNavPages }) {
function TodayCard({ cRef, siteInfo }) {
const router = useRouter()
const link = siteConfig('HEO_HERO_TITLE_LINK', null, CONFIG)
const { locale } = useGlobal()
// 卡牌是否盖住下层
const [isCoverUp, setIsCoverUp] = useState(true)
@@ -348,7 +351,7 @@ function TodayCard({ cRef, siteInfo }) {
isCoverUp
? 'opacity-100 cursor-pointer'
: 'opacity-0 transform scale-110 pointer-events-none'
} shadow transition-all duration-200 today-card h-full bg-[#0E57D5] rounded-xl relative overflow-hidden flex items-end`}>
} shadow transition-all duration-200 today-card h-full bg-[#0E57D5] dark:bg-yellow-500 rounded-xl relative overflow-hidden flex items-end`}>
<div
id='today-card-info'
className='z-10 flex justify-between w-full relative text-white p-10 items-end'>
@@ -364,12 +367,14 @@ function TodayCard({ cRef, siteInfo }) {
onClick={handleClickMore}
className={`'${
isCoverUp ? '' : 'hidden pointer-events-none '
} flex items-center px-3 h-10 justify-center bg-[#425aef] hover:bg-[#4259efcb] transition-colors duration-100 rounded-3xl`}>
} flex items-center px-3 h-10 justify-center bg-[#425aef] hover:bg-[#4259efcb] dark:bg-yellow-500 dark:hover:bg-yellow-600 transition-colors duration-100 rounded-3xl`}>
<PlusSmall
className={'w-6 h-6 mr-2 bg-white rounded-full stroke-indigo-400'}
className={
'w-6 h-6 mr-2 bg-white rounded-full stroke-indigo-400 dark:stroke-yellow-400'
}
/>
<div id='more' className='select-none'>
更多推荐
{locale.COMMON.MORE}
</div>
</div>
</div>

View File

@@ -50,13 +50,13 @@ export default function LatestPostsGroupMini({ latestPosts, siteInfo }) {
</div>
<div
className={
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
(selected ? ' text-indigo-400 ' : 'dark:text-gray-200') +
' text-sm overflow-x-hidden hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
' hover:text-indigo-400 cursor-pointer items-center flex'
' hover:text-indigo-400 dark:hover:text-yellow-600 cursor-pointer items-center flex'
}>
<div>
<div className='line-clamp-2 menu-link'>{post.title}</div>
<div className='text-gray-500'>{post.lastEditedDay}</div>
<div className='text-gray-400'>{post.lastEditedDay}</div>
</div>
</div>
</Link>

View File

@@ -28,7 +28,7 @@ export const MenuItemCollapse = ({ link }) => {
return (
<>
<div
className='select-none w-full px-2 py-2 border rounded-xl text-left dark:bg-hexo-black-gray'
className='select-none w-full p-2 border dark:border-gray-600 rounded-lg text-left dark:bg-[#1e1e1e]'
onClick={toggleShow}>
{!hasSubMenu && (
<Link
@@ -62,7 +62,7 @@ export const MenuItemCollapse = ({ link }) => {
return (
<div
key={index}
className='dark:bg-black dark:text-gray-200 text-left px-3 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
className='dark:bg-hexo-black-gray dark:text-gray-200 text-left px-3 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
<Link href={sLink.href} target={link?.target}>
<span className='text-sm ml-4 whitespace-nowrap'>
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}

View File

@@ -36,12 +36,12 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && (
<ul
style={{ backdropFilter: 'blur(3px)' }}
className={`${show ? 'visible opacity-100 top-14' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-xl bg-white transition-all duration-300 z-20 absolute`}>
className={`${show ? 'visible opacity-100 top-14' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-xl bg-white dark:bg-[#1e1e1e] border dark:border-gray-700 transition-all duration-300 z-20 absolute`}>
{link.subMenus.map((sLink, index) => {
return (
<li
key={index}
className='cursor-pointer hover:bg-blue-600 hover:text-white text-gray-900 tracking-widest transition-all duration-200 dark:border-gray-700 py-1 pr-6 pl-3'>
className='cursor-pointer hover:bg-blue-600 dark:hover:bg-yellow-600 hover:text-white text-gray-900 dark:text-gray-100 tracking-widest transition-all duration-200 dark:border-gray-700 py-1 pr-6 pl-3'>
<Link href={sLink.href} target={link?.target}>
<span className='text-sm text-nowrap font-extralight'>
{link?.icon && <i className={sLink?.icon}> &nbsp; </i>}

View File

@@ -48,7 +48,7 @@ export const MenuListSide = props => {
}
return (
<nav className='flex-col space-y-2'>
<nav className='flex-col space-y-1'>
{links?.map((link, index) => (
<MenuItemCollapse key={index} link={link} />
))}

View File

@@ -1,14 +1,15 @@
import { ArrowRightCircle } from '@/components/HeroIcons'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import CONFIG from '../config'
import Swipe from './Swipe'
import { siteConfig } from '@/lib/config'
/**
* 通知横幅
*/
export function NoticeBar() {
let notices = siteConfig('HEO_NOTICE_BAR', null, CONFIG)
const { locale } = useGlobal()
if (typeof notices === 'string') {
notices = JSON.parse(notices)
}
@@ -17,14 +18,16 @@ export function NoticeBar() {
}
return (
<div className="max-w-[86rem] w-full mx-auto flex h-12 mb-4 px-5 font-bold">
<div className="animate__animated animate__fadeIn animate__fast group cursor-pointer bg-white dark:bg-[#1e1e1e] dark:text-white hover:border-indigo-600 dark:hover:border-yellow-600 border dark:border-gray-700 duration-200 hover:shadow-md transition-all rounded-xl w-full h-full flex items-center justify-between px-5">
<span className='whitespace-nowrap'>此刻</span>
<div className="w-full h-full hover:text-indigo-600 flex justify-center items-center">
<Swipe items={notices} />
</div>
<div><ArrowRightCircle className={'w-5 h-5'} /></div>
</div>
<div className='max-w-[86rem] w-full mx-auto flex h-12 mb-4 px-5 font-bold'>
<div className='animate__animated animate__fadeIn animate__fast group cursor-pointer bg-white dark:bg-[#1e1e1e] dark:text-white hover:border-indigo-600 dark:hover:border-yellow-600 border dark:border-gray-700 duration-200 hover:shadow-md transition-all rounded-xl w-full h-full flex items-center justify-between px-5'>
<span className='whitespace-nowrap'>{locale.COMMON.NOW}</span>
<div className='w-full h-full hover:text-indigo-600 dark:hover:text-yellow-600 flex justify-center items-center'>
<Swipe items={notices} />
</div>
<div>
<ArrowRightCircle className={'w-5 h-5'} />
</div>
</div>
</div>
)
}

View File

@@ -56,7 +56,7 @@ const PaginationNumber = ({ page, totalPage }) => {
}}
rel='prev'
className={`${currentPage === 1 ? 'invisible' : 'block'}`}>
<div className='relative w-24 h-10 flex items-center transition-all duration-200 justify-center py-2 px-2 bg-white dark:bg-[#1e1e1e] border rounded-lg cursor-pointer group'>
<div className='hover:border-indigo-600 dark:hover:border-yellow-600 relative w-24 h-10 flex items-center transition-all duration-200 justify-center py-2 px-2 bg-white dark:bg-[#1e1e1e] border dark:border-gray-600 rounded-lg cursor-pointer group'>
<i className='fas fa-angle-left mr-2 transition-all duration-200 transform group-hover:-translate-x-4' />
<div className='absolute translate-x-4 ml-2 opacity-0 transition-all duration-200 group-hover:opacity-100 group-hover:translate-x-0'>
{locale.PAGINATION.PREV}
@@ -69,7 +69,7 @@ const PaginationNumber = ({ page, totalPage }) => {
{pages}
{/* 跳转页码 */}
<div className='bg-white hover:bg-gray-100 dark:hover:bg-yellow-600 dark:bg-[#1e1e1e] h-10 border flex justify-center items-center rounded-lg group hover:border-indigo-600 transition-all duration-200'>
<div className='bg-white hover:bg-gray-100 dark:hover:bg-yellow-600 dark:bg-[#1e1e1e] h-10 border dark:border-gray-600 flex justify-center items-center rounded-lg group hover:border-indigo-600 transition-all duration-200'>
<input
value={value}
className='w-0 group-hover:w-20 group-hover:px-3 transition-all duration-200 bg-gray-100 border-none outline-none h-full rounded-lg'
@@ -90,7 +90,7 @@ const PaginationNumber = ({ page, totalPage }) => {
}}
rel='next'
className={`${+showNext ? 'block' : 'invisible'} `}>
<div className='relative w-24 h-10 flex items-center transition-all duration-200 justify-center py-2 px-2 bg-white dark:bg-[#1e1e1e] border rounded-lg cursor-pointer group'>
<div className='hover:border-indigo-600 dark:hover:border-yellow-600 relative w-24 h-10 flex items-center transition-all duration-200 justify-center py-2 px-2 bg-white dark:bg-[#1e1e1e] border dark:border-gray-600 rounded-lg cursor-pointer group'>
<i className='fas fa-angle-right mr-2 transition-all duration-200 transform group-hover:translate-x-6' />
<div className='absolute -translate-x-10 ml-2 opacity-0 transition-all duration-200 group-hover:opacity-100 group-hover:-translate-x-2'>
{locale.PAGINATION.NEXT}

View File

@@ -10,21 +10,28 @@ import CONFIG from '../config'
* @returns
*/
export default function PostAdjacent({ prev, next }) {
const [isScrollEnd, setIsScrollEnd] = useState(false)
const [isShow, setIsShow] = useState(false)
const router = useRouter()
useEffect(() => {
setIsScrollEnd(false)
setIsShow(false)
}, [router])
useEffect(() => {
// 文章是否已经到了底部
const targetElement = document.getElementById('article-end')
// 文章到底部时显示下一篇文章推荐
const articleEnd = document.getElementById('article-end')
const footerBottom = document.getElementById('footer-bottom')
const handleIntersect = entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setIsScrollEnd(true)
if (entry.target === articleEnd) {
if (entry.isIntersecting) {
setIsShow(true)
}
} else if (entry.target === footerBottom) {
if (entry.isIntersecting) {
setIsShow(false)
}
}
})
}
@@ -36,9 +43,12 @@ export default function PostAdjacent({ prev, next }) {
}
const observer = new IntersectionObserver(handleIntersect, options)
observer.observe(targetElement)
if (articleEnd) observer.observe(articleEnd)
if (footerBottom) observer.observe(footerBottom)
return () => {
if (articleEnd) observer.unobserve(articleEnd)
if (footerBottom) observer.unobserve(footerBottom)
observer.disconnect()
}
}, [])
@@ -75,7 +85,7 @@ export default function PostAdjacent({ prev, next }) {
<div
id='pc-next-post'
className={`hidden md:block fixed z-40 right-24 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
className={`hidden md:block fixed z-40 right-24 bottom-4 duration-200 transition-all ${isShow ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
<Link
href={`/${next.slug}`}
className='cursor-pointer drop-shadow-xl duration transition-all h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white dark:text-gray-300 dark:hover:text-yellow-600 hover:font-bold hover:text-blue-600 rounded-lg flex flex-col justify-between'>

View File

@@ -12,13 +12,13 @@ import WavesArea from './WavesArea'
* @param {*} param0
* @returns
*/
export default function PostHeader({ post, siteInfo }) {
export default function PostHeader({ post, siteInfo, isDarkMode }) {
if (!post) {
return <></>
}
// 文章头图
const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover
const ANALYTICS_BUSUANZI_ENABLE = siteConfig('ANALYTICS_BUSUANZI_ENABLE')
return (
<div
id='post-bg'
@@ -31,19 +31,21 @@ export default function PostHeader({ post, siteInfo }) {
height: 100%;
top: 0;
left: 0;
box-shadow: 110px -130px 300px 60px #0060e0 inset;
box-shadow: 110px -130px 500px 100px ${isDarkMode
? '#CA8A04'
: '#0060e0'} inset;
}
`}</style>
<div
style={{ backdropFilter: 'blur(15px)' }}
className={
'bg-[#0060e0] absolute top-0 w-full h-full py-10 flex justify-center items-center'
}>
className={`${isDarkMode ? 'bg-[#CA8A04]' : '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)' }}
style={{
filter: 'blur(15px)'
}}
className='coverdiv lg:translate-x-96 opacity-50 lg:rotate-12'>
<LazyImage
id='post-cover'
@@ -57,7 +59,7 @@ export default function PostHeader({ post, siteInfo }) {
id='post-info'
className='absolute top-48 z-10 flex flex-col space-y-4 lg:-mt-12 w-full max-w-[86rem] px-5'>
{/* 分类+标签 */}
<div className='flex justify-center md:justify-start items-center'>
<div className='flex justify-center md:justify-start items-center gap-4'>
{post.category && (
<>
<Link
@@ -65,7 +67,7 @@ export default function PostHeader({ post, siteInfo }) {
className='mr-4'
passHref
legacyBehavior>
<div className='cursor-pointer font-sm font-bold px-3 py-1 rounded-lg bg-blue-500 hover:bg-white text-white hover:text-blue-500 duration-200 '>
<div className='cursor-pointer font-sm font-bold px-3 py-1 rounded-lg hover:bg-white text-white bg-blue-500 dark:bg-yellow-500 hover:text-blue-500 duration-200 '>
{post.category}
</div>
</Link>
@@ -101,8 +103,8 @@ export default function PostHeader({ post, siteInfo }) {
</div>
{/* 标题底部补充信息 */}
<section className='flex-wrap shadow-text-md flex text-sm justify-center md: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'>
<section className='flex-wrap dark:text-gray-200 text-opacity-70 shadow-text-md flex text-sm justify-center md:justify-start mt-4 text-white font-light leading-8'>
<div className='flex justify-center '>
<div className='mr-2'>
<WordCount />
</div>
@@ -124,7 +126,8 @@ export default function PostHeader({ post, siteInfo }) {
</div>
</div>
{JSON.parse(siteConfig('ANALYTICS_BUSUANZI_ENABLE')) && (
{/* 阅读统计 */}
{ANALYTICS_BUSUANZI_ENABLE && (
<div className='busuanzi_container_page_pv font-light mr-2'>
<i className='fa-solid fa-fire-flame-curved'></i>{' '}
<span className='mr-2 busuanzi_value_page_pv' />

View File

@@ -50,7 +50,7 @@ export default function SideRight(props) {
{/* 最新文章列表 */}
<div
className={
'border dark:border-gray-700 dark:bg-[#1e1e1e] dark:text-white rounded-xl lg:p-6 p-4 hidden lg:block bg-white'
'border hover:border-indigo-600 dark:hover:border-yellow-600 duration-200 dark:border-gray-700 dark:bg-[#1e1e1e] dark:text-white rounded-xl lg:p-6 p-4 hidden lg:block bg-white'
}>
<LatestPostsGroupMini {...props} />
</div>
@@ -61,7 +61,10 @@ export default function SideRight(props) {
<Live2D />
{/* 标签和成绩 */}
<Card className={'bg-white dark:bg-[#1e1e1e] dark:text-white'}>
<Card
className={
'bg-white dark:bg-[#1e1e1e] dark:text-white hover:border-indigo-600 dark:hover:border-yellow-600 duration-200'
}>
<TagGroups tags={sortedTags} currentTag={currentTag} />
<hr className='mx-1 flex border-dashed relative my-4' />
<AnalyticsCard {...props} />

View File

@@ -1,10 +1,17 @@
import { Fragment, useImperativeHandle, useRef, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import DarkModeButton from '@/components/DarkModeButton'
import { useGlobal } from '@/lib/global'
import { Dialog, Transition } from '@headlessui/react'
import Link from 'next/link'
import TagGroups from './TagGroups'
import { useRouter } from 'next/router'
import {
Fragment,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react'
import { MenuListSide } from './MenuListSide'
import TagGroups from './TagGroups'
/**
* 侧边抽屉
@@ -13,101 +20,105 @@ import { MenuListSide } from './MenuListSide'
export default function SlideOver(props) {
const { cRef, tagOptions } = props
const [open, setOpen] = useState(false)
const { locale } = useGlobal()
const router = useRouter()
/**
* 函数组件暴露方法useImperativeHandle
*/
* 函数组件暴露方法useImperativeHandle
**/
useImperativeHandle(cRef, () => ({
toggleSlideOvers: toggleSlideOvers
}))
/**
* 开关侧拉抽屉
*/
const toggleSlideOvers = () => {
setOpen(!open)
}
/**
* 自动关闭抽屉
*/
useEffect(() => {
setOpen(false)
}, [router])
return (
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-20" onClose={setOpen}>
<Transition.Child
<Transition.Root show={open} as={Fragment}>
<Dialog as='div' className='relative z-20' onClose={setOpen}>
<Transition.Child
as={Fragment}
enter='ease-in-out duration-500'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in-out duration-500'
leaveFrom='opacity-100'
leaveTo='opacity-0'>
<div className='fixed inset-0 glassmorphism bg-black bg-opacity-30 transition-opacity' />
</Transition.Child>
<div className='fixed inset-0 overflow-hidden'>
<div className='absolute inset-0 overflow-hidden'>
<div className='pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10'>
<Transition.Child
as={Fragment}
enter='transform transition ease-in-out duration-500 sm:duration-700'
enterFrom='translate-x-full'
enterTo='translate-x-0'
leave='transform transition ease-in-out duration-500 sm:duration-700'
leaveFrom='translate-x-0'
leaveTo='translate-x-full'>
<Dialog.Panel className='pointer-events-auto relative w-96 max-w-md'>
<Transition.Child
as={Fragment}
enter="ease-in-out duration-500"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 glassmorphism bg-black bg-opacity-30 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 overflow-hidden">
<div className="absolute inset-0 overflow-hidden">
<div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<Dialog.Panel className="pointer-events-auto relative w-96 max-w-md">
<Transition.Child
as={Fragment}
enter="ease-in-out duration-500"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="absolute left-0 top-0 -ml-8 flex pr-2 pt-4 sm:-ml-10 sm:pr-4">
<button
type="button"
className="rounded-md text-gray-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-white"
onClick={() => setOpen(false)}
>
<span className="sr-only">Close panel</span>
<i className="fa-solid fa-xmark px-2"></i>
</button>
</div>
</Transition.Child>
{/* 内容 */}
<div className="flex h-full flex-col overflow-y-scroll bg-white dark:bg-[#18171d] py-6 shadow-xl">
<div className="relative mt-6 flex-1 flex-col space-y-3 px-4 sm:px-6 dark:text-white ">
<section className='space-y-2 flex flex-col'>
<div>功能</div>
{/* 切换深色模式 */}
<DarkModeBlockButton />
</section>
<section className='space-y-2 flex flex-col'>
<div>博客</div>
{/* 导航按钮 */}
<div className='gap-2 grid grid-cols-2'>
<Button title={'主页'} url={'/'} />
<Button title={'关于'} url={'/about'} />
</div>
{/* 用户自定义菜单 */}
<MenuListSide {...props}/>
</section>
<section className='space-y-2 flex flex-col'>
<div>标签</div>
<TagGroups tags={tagOptions} />
</section>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
enter='ease-in-out duration-500'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in-out duration-500'
leaveFrom='opacity-100'
leaveTo='opacity-0'>
<div className='absolute left-0 top-0 -ml-8 flex pr-2 pt-4 sm:-ml-10 sm:pr-4'>
<button
type='button'
className='rounded-md text-gray-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-white'
onClick={() => setOpen(false)}>
<span className='sr-only'>Close panel</span>
<i className='fa-solid fa-xmark px-2'></i>
</button>
</div>
</div>
</Dialog>
</Transition.Root>
</Transition.Child>
{/* 内容 */}
<div className='flex h-full flex-col overflow-y-scroll bg-white dark:bg-[#18171d] py-6 shadow-xl'>
<div className='relative mt-6 flex-1 flex-col space-y-3 px-4 sm:px-6 dark:text-white '>
<section className='space-y-2 flex flex-col'>
{/* 切换深色模式 */}
<DarkModeBlockButton />
</section>
<section className='space-y-2 flex flex-col'>
<div>{locale.COMMON.BLOG}</div>
{/* 导航按钮 */}
<div className='gap-2 grid grid-cols-2'>
<Button title={'主页'} url={'/'} />
<Button title={'关于'} url={'/about'} />
</div>
{/* 用户自定义菜单 */}
<MenuListSide {...props} />
</section>
<section className='space-y-2 flex flex-col'>
<div>{locale.COMMON.TAGS}</div>
<TagGroups tags={tagOptions} />
</section>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
)
}
@@ -116,19 +127,34 @@ export default function SlideOver(props) {
*/
function DarkModeBlockButton() {
const darkModeRef = useRef()
const { isDarkMode, locale } = useGlobal()
function handleChangeDarkMode() {
darkModeRef?.current?.handleChangeDarkMode()
}
return <button onClick={handleChangeDarkMode} className={'group duration-200 hover:text-white hover:shadow-md hover:bg-blue-600 flex justify-between items-center px-2 py-2 border dark:border-gray-600 bg-white dark:bg-[#ff953e] rounded-lg'}>
<DarkModeButton cRef={darkModeRef} className='group-hover:text-white' /> 显示模式
return (
<button
onClick={handleChangeDarkMode}
className={
'group duration-200 hover:text-white hover:shadow-md hover:bg-blue-600 flex justify-between items-center px-2 py-2 border dark:border-gray-600 bg-white dark:bg-[#ff953e] rounded-lg'
}>
<DarkModeButton cRef={darkModeRef} className='group-hover:text-white' />{' '}
{isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}
</button>
)
}
/**
* 一个简单的按钮
*/
function Button({ title, url }) {
return <Link href={url} className={'duration-200 hover:text-white hover:shadow-md flex cursor-pointer justify-between items-center px-2 py-2 border dark:border-gray-600 bg-white hover:bg-blue-600 dark:bg-[#1e1e1e] rounded-lg'}>
{title}
return (
<Link
href={url}
className={
'duration-200 hover:text-white hover:shadow-md flex cursor-pointer justify-between items-center px-2 py-2 border dark:border-gray-600 bg-white hover:bg-blue-600 dark:bg-[#1e1e1e] rounded-lg'
}>
{title}
</Link>
)
}

View File

@@ -7,8 +7,13 @@ const TagItemMini = ({ tag, selected = false }) => {
key={tag}
href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`}
passHref
className={'cursor-pointer inline-block hover:text-white hover:bg-indigo-600 dark:hover:bg-yellow-600 px-2 py-1 rounded-2xl dark:text-white duration-200 text-sm whitespace-nowrap ' }>
<div className='font-light flex items-center'><HashTag className='text-gray-500 stroke-2 mr-0.5 w-3 h-3'/> {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
className={
'cursor-pointer inline-block hover:text-white hover:bg-indigo-600 dark:hover:bg-yellow-600 px-2 py-1 rounded-2xl dark:text-white duration-200 text-sm whitespace-nowrap '
}>
<div className='font-light flex items-center'>
<HashTag className='stroke-2 mr-0.5 w-3 h-3' />{' '}
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
</div>
</Link>
)
}

View File

@@ -52,7 +52,7 @@ const LayoutBase = props => {
const { children, slotTop, className } = props
// 全屏模式下的最大宽度
const { fullWidth } = useGlobal()
const { fullWidth, isDarkMode } = useGlobal()
const router = useRouter()
const headerSlot = (
@@ -67,7 +67,7 @@ const LayoutBase = props => {
<Hero {...props} />
</>
) : null}
{fullWidth ? null : <PostHeader {...props} />}
{fullWidth ? null : <PostHeader {...props} isDarkMode={isDarkMode} />}
</header>
)
@@ -284,18 +284,18 @@ const LayoutSlug = props => {
return (
<>
<div
className={`w-full ${fullWidth ? '' : 'xl:max-w-5xl'} ${hasCode ? 'xl:w-[73.15vw]' : ''} lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article`}>
className={`article h-full w-full ${fullWidth ? '' : 'xl:max-w-5xl'} ${hasCode ? 'xl:w-[73.15vw]' : ''} lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600`}>
{/* 文章锁 */}
{lock && <PostLock validPassword={validPassword} />}
{!lock && (
<div
id='article-wrapper'
className='overflow-x-auto flex-grow mx-auto md:w-full md:px-5 '>
<div id='article-wrapper' className='mx-auto md:w-full md:px-5'>
{/* 文章主体 */}
<article
itemScope
itemType='https://schema.org/Movie'
data-wow-delay='.2s'
className='wow fadeInUp subpixel-antialiased overflow-y-hidden'>
className='wow fadeInUp'>
{/* Notion文章主体 */}
<section className='px-5 justify-center mx-auto'>
<WWAds orientation='horizontal' className='w-full' />
@@ -311,12 +311,14 @@ const LayoutSlug = props => {
<PostCopyright {...props} />
{/* 文章推荐 */}
<PostRecommend {...props} />
{/* 上一篇\下一篇文章 */}
<PostAdjacent {...props} />
</div>
)}
</article>
{/* 上一篇\下一篇文章 */}
<PostAdjacent {...props} />
{/* 评论区 */}
{fullWidth ? null : (
<div className={`${commentEnable && post ? '' : 'hidden'}`}>
<hr className='my-4 border-dashed' />
@@ -337,6 +339,7 @@ const LayoutSlug = props => {
</div>
)}
</div>
<FloatTocButton {...props} />
</>
)