mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
new-theme-plog
This commit is contained in:
@@ -32,4 +32,23 @@ const ArrowPath = ({ className }) => {
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||
</svg>
|
||||
}
|
||||
export { Moon, Sun, Home, User, ArrowPath }
|
||||
|
||||
const ChevronLeft = ({ className }) => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={className}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
const ChevronRight = ({ className }) => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={className}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
const InformationCircle = ({ className }) => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={className}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
export { Moon, Sun, Home, User, ArrowPath, ChevronLeft, ChevronRight, InformationCircle }
|
||||
|
||||
@@ -5,6 +5,7 @@ import CONFIG from '../config'
|
||||
import { SvgIcon } from './SvgIcon'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import FullScreenButton from '@/components/FullScreenButton'
|
||||
import InformationButton from './InformationButton'
|
||||
|
||||
/**
|
||||
* 桌面端底部导航
|
||||
@@ -15,7 +16,7 @@ const BottomNav = props => {
|
||||
const { navBarTitle, siteInfo } = props
|
||||
|
||||
return <>
|
||||
<div id="bottom-nav" style={{ backgroundColor: '#00000063' }} className={'px-4 hidden glassmorphism md:fixed bottom-0 w-screen py-4 md:flex flex-row justify-between items-center'}>
|
||||
<div id="bottom-nav" style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} className={'z-20 px-4 hidden glassmorphism md:fixed bottom-0 w-screen py-4 md:flex flex-row justify-between items-center'}>
|
||||
<div className="flex items-center">
|
||||
<Link href="/" aria-label={BLOG.title}>
|
||||
<div className="h-6 w-6">
|
||||
@@ -28,17 +29,20 @@ const BottomNav = props => {
|
||||
</Link>
|
||||
{navBarTitle
|
||||
? (
|
||||
<p className="ml-2 font-medium text-gray-800 dark:text-gray-300 header-name">
|
||||
{navBarTitle}
|
||||
</p>
|
||||
<Link href="/" aria-label={BLOG.title}>
|
||||
<p className="ml-2 font-medium text-gray-800 dark:text-gray-300 header-name">
|
||||
{navBarTitle}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
: (
|
||||
<p className="ml-2 font-medium text-gray-800 dark:text-gray-300 header-name">
|
||||
{siteInfo?.title}
|
||||
<Link href="/" aria-label={BLOG.title}> {siteInfo?.title}</Link>
|
||||
{' '}<span className="font-normal text-sm text-gray-00 dark:text-gray-400">{siteInfo?.description}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 右下角菜单栏 */}
|
||||
<MenuList {...props} />
|
||||
</div>
|
||||
</>
|
||||
@@ -78,7 +82,10 @@ const MenuList = props => {
|
||||
<ul className="hidden md:flex flex-row">
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
<li className='my-auto px-2'>
|
||||
<FullScreenButton/>
|
||||
<FullScreenButton />
|
||||
</li>
|
||||
<li className='my-auto px-2'>
|
||||
<InformationButton/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import Vercel from '@/components/Vercel'
|
||||
|
||||
export const Footer = (props) => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const { post } = props
|
||||
const fullWidth = post?.fullWidth ?? false
|
||||
|
||||
const copyrightDate = (function() {
|
||||
if (Number.isInteger(BLOG.SINCE) && BLOG.SINCE < currentYear) {
|
||||
@@ -15,18 +12,11 @@ export const Footer = (props) => {
|
||||
return currentYear
|
||||
})()
|
||||
|
||||
return <footer
|
||||
className={`z-10 relative mt-6 flex-shrink-0 m-auto w-full text-gray-500 dark:text-gray-400 transition-all ${
|
||||
!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'
|
||||
}`}
|
||||
>
|
||||
<DarkModeButton className='text-center pt-4'/>
|
||||
<hr className="border-gray-200 dark:border-gray-600" />
|
||||
return <footer className={'z-10 relative mt-6 flex-shrink-0 m-auto w-full text-gray-500 dark:text-gray-400 transition-all' } >
|
||||
<div className="my-4 text-sm leading-6">
|
||||
<div className="flex align-baseline justify-between flex-wrap">
|
||||
<p>
|
||||
© {BLOG.AUTHOR} {copyrightDate}
|
||||
</p>
|
||||
<div className="flex align-baseline justify-start flex-wrap space-x-6">
|
||||
<div> © {BLOG.AUTHOR} {copyrightDate} </div>
|
||||
<div>Powered By <a href="https://github.com/tangly1024/NotionNext" className='underline'>NotionNext {BLOG.VERSION}</a></div>
|
||||
<Vercel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
23
themes/plog/components/InformationButton.js
Normal file
23
themes/plog/components/InformationButton.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { InformationCircle } from '@/components/HeroIcons'
|
||||
import SlideOvers from './SlideOvers'
|
||||
import { useRef } from 'react'
|
||||
|
||||
/**
|
||||
* 显示网站用户信息按钮
|
||||
* @returns
|
||||
*/
|
||||
export default function InformationButton() {
|
||||
const slideOversRef = useRef({})
|
||||
const toggleCollapsed = () => {
|
||||
console.log(slideOversRef)
|
||||
slideOversRef.current.toggleSlideOvers()
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className='cursor-pointer' onClick={toggleCollapsed}>
|
||||
<InformationCircle className={'w-5 h-5'} />
|
||||
</div>
|
||||
|
||||
<SlideOvers cRef={slideOversRef} />
|
||||
</>
|
||||
}
|
||||
@@ -28,8 +28,8 @@ export const MenuItemDrop = ({ link }) => {
|
||||
|
||||
{/* 子菜单 */}
|
||||
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 bottom-16 ' : 'invisible opacity-0 top-10 '} border-gray-100 bg-white rounded-lg dark:bg-black dark:border-gray-800 bg-opacity-60 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
|
||||
{link.subMenus.map(sLink => {
|
||||
return <li key={sLink.id} className='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-3'>
|
||||
{link.subMenus.map((sLink, index) => {
|
||||
return <li key={index} className='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-3'>
|
||||
<Link href={sLink.to}>
|
||||
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Fragment, useRef, useState } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { usePlogGlobal } from '..'
|
||||
import { ArrowPath } from '@/components/HeroIcons'
|
||||
import { ArrowPath, ChevronLeft, ChevronRight } from '@/components/HeroIcons'
|
||||
import Link from 'next/link'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
/**
|
||||
* 弹出框
|
||||
*/
|
||||
export default function Modal(props) {
|
||||
const { showModal, setShowModal, modalContent } = usePlogGlobal()
|
||||
const { siteInfo } = props
|
||||
const { showModal, setShowModal, modalContent, setModalContent } = usePlogGlobal()
|
||||
const { siteInfo, posts } = props
|
||||
const cancelButtonRef = useRef(null)
|
||||
const img = modalContent?.pageCover || siteInfo?.pageCover
|
||||
const imgRef = useRef(null)
|
||||
@@ -18,7 +19,7 @@ export default function Modal(props) {
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// 在图片加载完成时设置loading为false
|
||||
async function handleImageLoad() {
|
||||
function handleImageLoad() {
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
@@ -27,6 +28,28 @@ export default function Modal(props) {
|
||||
setShowModal(false)
|
||||
setLoading(true)
|
||||
}
|
||||
|
||||
// 修改当前显示的遮罩内容
|
||||
function prev() {
|
||||
const index = posts.findIndex(post => post.slug === modalContent.slug)
|
||||
if (index === 0) {
|
||||
setModalContent(posts[posts.length - 1])
|
||||
} else {
|
||||
setModalContent(posts[index - 1])
|
||||
}
|
||||
setLoading(true)
|
||||
}
|
||||
// 下一个
|
||||
const next = () => {
|
||||
const index = posts.findIndex(post => post.slug === modalContent.slug)
|
||||
if (index === posts.length - 1) {
|
||||
setModalContent(posts[0])
|
||||
} else {
|
||||
setModalContent(posts[index + 1])
|
||||
}
|
||||
setLoading(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition.Root show={showModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={handleClose}>
|
||||
@@ -40,7 +63,7 @@ export default function Modal(props) {
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div style={{ backgroundColor: '#00000063' }} className="fixed inset-0 glassmorphism transition-opacity" />
|
||||
<div style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} className="fixed inset-0 glassmorphism transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
@@ -59,20 +82,37 @@ export default function Modal(props) {
|
||||
<div className={`bg-hexo-black-gray w-32 h-32 flex justify-center items-center ${loading ? '' : 'hidden'}`}>
|
||||
<ArrowPath className='w-10 h-10 animate-spin text-gray-200' />
|
||||
</div>
|
||||
|
||||
{/* 添加onLoad事件处理函数 */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={img} ref={imgRef} className={'w-full max-w-7xl max-h-[90vh] shadow-xl'} onLoad={handleImageLoad} style={{ display: loading ? 'none' : 'block' }} />
|
||||
{!loading && <div className='absolute bottom-0 left-0 m-4'>
|
||||
<div className='flex'>
|
||||
<h2 style={{ textShadow: '0.1em 0.1em 0.2em black' }} className='text-5xl text-white mb-4 px-2 py-1 rounded-lg'>{modalContent?.title}</h2>
|
||||
</div>
|
||||
<div style={{ textShadow: '0.1em 0.1em 0.2em black' }} className={'text-gray-50 rounded-lg p-2'}>{modalContent?.summary}</div>
|
||||
{modalContent?.category && <div className='flex '>
|
||||
<Link href={`/category/${modalContent?.category}`} className='text-xs rounded-lg mt-3 px-2 py-1 bg-black bg-opacity-20 text-white hover:bg-blue-700 hover:text-white duration-200'>
|
||||
{modalContent?.category}
|
||||
<img src={img} ref={imgRef} className={`w-full max-w-7xl max-h-[90vh] shadow-xl ${!loading ? ' animate__animated animate__fadeIn' : ''}`} onLoad={handleImageLoad} style={{ display: loading ? 'none' : 'block' }} />
|
||||
|
||||
{!loading && (<>
|
||||
<div className='absolute bottom-0 left-0 m-4 z-20'>
|
||||
<div className='flex'>
|
||||
<h2 style={{ textShadow: '0.1em 0.1em 0.2em black' }} className='text-5xl text-white mb-4 px-2 py-1 rounded-lg'>{modalContent?.title}</h2>
|
||||
</div>
|
||||
|
||||
<Link href={`${BLOG.SUB_PATH}/${modalContent.slug}`}>
|
||||
<div style={{ textShadow: '0.1em 0.1em 0.2em black' }} className={'cursor-pointer text-gray-50 rounded-lg p-2'}>
|
||||
{modalContent?.summary}
|
||||
</div>
|
||||
</Link>
|
||||
</div>}
|
||||
</div>}
|
||||
|
||||
{modalContent?.category && (
|
||||
<div className='flex'>
|
||||
<Link href={`/category/${modalContent?.category}`} className='text-xs rounded-lg mt-3 px-2 py-1 bg-black bg-opacity-20 text-white hover:bg-blue-700 hover:text-white duration-200'>
|
||||
{modalContent?.category}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='z-10 absolute hover:opacity-50 opacity-0 duration-200 transition-opacity w-full top-0 left-0 px-4 h-full items-center flex justify-between'>
|
||||
<div onClick={prev}><ChevronLeft className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' /></div>
|
||||
<div onClick={next}><ChevronRight className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' /></div>
|
||||
</div>
|
||||
</>)}
|
||||
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
|
||||
79
themes/plog/components/SlideOvers.js
Normal file
79
themes/plog/components/SlideOvers.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Fragment, useRef, useImperativeHandle, useState } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Footer } from './Footer'
|
||||
import SocialButton from './SocialButton'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
/**
|
||||
* 侧拉抽屉
|
||||
* @returns
|
||||
*/
|
||||
export default function SlideOvers({ children, cRef }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const slideOversRef = useRef({})
|
||||
/**
|
||||
* 函数组件暴露方法
|
||||
*/
|
||||
useImperativeHandle(cRef, () => ({
|
||||
toggleSlideOvers: toggleSlideOvers
|
||||
}))
|
||||
|
||||
const toggleSlideOvers = () => {
|
||||
setOpen(!open)
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Dialog ref={slideOversRef} as="div" className="relative" onClose={setOpen}>
|
||||
{/* 遮罩 */}
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} className="fixed inset-0 z-10 glassmorphism transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-hidden z-10">
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="pointer-events-none fixed inset-x-0 bottom-0 flex max-h-full pb-16">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transform transition ease-in-out duration-500"
|
||||
enterFrom="translate-y-full"
|
||||
enterTo="translate-y-0"
|
||||
leave="transform transition ease-in-out duration-500"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-full"
|
||||
>
|
||||
<Dialog.Panel style={{ backgroundColor: 'rgba(0, 0, 0, 0.6)' }} className="pointer-events-auto relative y-screen max-h-md glassmorphism w-screen p-4 mb-2">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-in-out duration-500"
|
||||
enterFrom="opacity-0 transition-y-32"
|
||||
enterTo="opacity-100 transition-y-0"
|
||||
leave="ease-in-out duration-500"
|
||||
leaveFrom="opacity-100 transition-y-0"
|
||||
leaveTo="opacity-0 transition-y-32"
|
||||
>
|
||||
<div className='max-w-7xl mx-auto space-y-6'>
|
||||
<h2 className='text-4xl text-gray-200'>关于{BLOG.AUTHOR}</h2>
|
||||
<h2 className='text-2xl text-gray-400'>{BLOG.BIO}</h2>
|
||||
<h2 className='text-4xl text-gray-200'>联系我</h2>
|
||||
<SocialButton/>
|
||||
<Footer/>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
)
|
||||
}
|
||||
45
themes/plog/components/SocialButton.js
Normal file
45
themes/plog/components/SocialButton.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import React from 'react'
|
||||
|
||||
/**
|
||||
* 社交联系方式按钮组
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const SocialButton = () => {
|
||||
return <div className='justify-start flex-wrap flex mx-1'>
|
||||
<div className='space-x-3 text-2xl text-gray-600 dark:text-gray-400 text-center'>
|
||||
{BLOG.CONTACT_GITHUB && <a target='_blank' rel='noreferrer' title={'github'} href={BLOG.CONTACT_GITHUB} >
|
||||
<i className='fab fa-github transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_TWITTER && <a target='_blank' rel='noreferrer' title={'twitter'} href={BLOG.CONTACT_TWITTER} >
|
||||
<i className='fab fa-twitter transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
||||
<i className='fab fa-telegram transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_LINKEDIN && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_LINKEDIN} title={'linkedIn'} >
|
||||
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
||||
<i className='fab fa-weibo transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_INSTAGRAM && <a target='_blank' rel='noreferrer' title={'instagram'} href={BLOG.CONTACT_INSTAGRAM} >
|
||||
<i className='fab fa-instagram transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_EMAIL && <a target='_blank' rel='noreferrer' title={'email'} href={`mailto:${BLOG.CONTACT_EMAIL}`} >
|
||||
<i className='fas fa-envelope transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.ENABLE_RSS && <a target='_blank' rel='noreferrer' title={'RSS'} href={'/feed'} >
|
||||
<i className='fas fa-rss transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_BILIBILI && <a target='_blank' rel='noreferrer' title={'bilibili'} href={BLOG.CONTACT_BILIBILI} >
|
||||
<i className='fab fa-bilibili transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
{BLOG.CONTACT_YOUTUBE && <a target='_blank' rel='noreferrer' title={'youtube'} href={BLOG.CONTACT_YOUTUBE} >
|
||||
<i className='fab fa-youtube transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
export default SocialButton
|
||||
@@ -68,7 +68,7 @@ const LayoutBase = props => {
|
||||
<Nav {...props} />
|
||||
|
||||
{/* 主区 */}
|
||||
<main id='out-wrapper' className={'relative m-auto flex-grow w-full transition-all pb-12'}>
|
||||
<main id='out-wrapper' className={'relative m-auto flex-grow w-full transition-all pb-16'}>
|
||||
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
@@ -179,7 +179,7 @@ const LayoutSlug = props => {
|
||||
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id="article-wrapper" className="px-2">
|
||||
{!lock && <div id="article-wrapper" className="px-2 max-w-6xl mx-auto">
|
||||
<>
|
||||
<ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
|
||||
Reference in New Issue
Block a user