theme-heo

This commit is contained in:
tangly1024.com
2023-07-17 18:39:11 +08:00
parent 1d8188633b
commit f4d54c83f5
37 changed files with 736 additions and 280 deletions

View File

@@ -54,7 +54,7 @@ const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
ssr: false
})
const Comment = ({ frontMatter }) => {
const Comment = ({ frontMatter, className }) => {
const router = useRouter()
if (isBrowser() && ('giscus' in router.query || router.query.target === 'comment')) {
@@ -70,7 +70,7 @@ const Comment = ({ frontMatter }) => {
}
return (
<div id='comment' className='comment mt-5 text-gray-800 dark:text-gray-300'>
<div id='comment' className={`comment mt-5 text-gray-800 dark:text-gray-300 ${className || ''}`}>
<Tabs>
{BLOG.COMMENT_TWIKOO_ENV_ID && (<div key='Twikoo'>

View File

@@ -17,13 +17,13 @@ export default function FlipCard(props) {
<div className={`flip-card-front ${props.className || ''}`} onMouseEnter={handleCardFlip}>
{props.frontContent}
</div>
<div className={`flip-card-back ${props.className || ''}`} onMouseOut={handleCardFlip}>
<div className={`flip-card-back ${props.className || ''}`} onMouseLeave={handleCardFlip}>
{props.backContent}
</div>
<style jsx>{`
.flip-card {
width: auto;
height: auto;
width: 100%;
height: 100%;
display: inline-block;
position: relative;
transform-style: preserve-3d;
@@ -41,12 +41,10 @@ export default function FlipCard(props) {
.flip-card-front {
z-index: 2;
transform: rotateY(0);
background-color: #f1f1f1;
}
.flip-card-back {
transform: rotateY(180deg);
background-color: #888;
}
.flip-card.flipped {

View File

@@ -23,15 +23,15 @@ const ThemeSwitch = () => {
return (<>
<Draggable>
<div id="draggableBox" style={{ left: '10px', top: '85vh' }} className="fixed dark:text-white bg-gray-50 dark:bg-black z-50 border dark:border-gray-800 rounded-xl shadow-card">
<div className="py-2 flex items-center text-sm px-2">
<DarkModeButton className='mr-1'/>
<select value={theme} onChange={onSelectChange} name="cars" className=' outline-none dark:text-white bg-gray-50 dark:bg-black uppercase cursor-pointer'>
<div id="draggableBox" style={{ left: '10px', top: '85vh' }} className="fixed dark:text-white bg-gray-50 dark:bg-black z-50 border dark:border-gray-800 rounded-2xl shadow-card">
<div className="p-3 flex items-center text-sm">
<DarkModeButton className='mr-2'/>
<select value={theme} onChange={onSelectChange} name="cars" className='appearance-none outline-none dark:text-white bg-gray-50 dark:bg-black uppercase cursor-pointer'>
{THEMES?.map(t => {
return <option key={t} value={t}>{t}</option>
})}
</select>
<i className='fas fa-palette pl-1' />
<i class="fa-solid fa-paintbrush pl-2"></i>
</div>
</div>
</Draggable>

67
components/WordCount.js Normal file
View File

@@ -0,0 +1,67 @@
import { useGlobal } from '@/lib/global'
import { useEffect } from 'react'
/**
* 字数统计
* @returns
*/
export default function WordCount() {
const { locale } = useGlobal()
useEffect(() => {
countWords()
})
return <span id='wordCountWrapper' className='flex gap-3 font-light'>
<span className='flex whitespace-nowrap items-center'>
<i className='pl-1 pr-2 fas fa-file-word' />
<span id='wordCount'>0</span>
</span>
<span className='flex whitespace-nowrap items-center'>
<i className='mr-1 fas fa-clock' />
<span></span>
<span id='readTime'>0</span>&nbsp;{locale.COMMON.MINUTE}
</span>
</span>
}
/**
* 更新字数统计和阅读时间
*/
function countWords() {
const articleText = deleteHtmlTag(document.getElementById('notion-article')?.innerHTML)
const wordCount = fnGetCpmisWords(articleText)
// 阅读速度 300-500每分钟
document.getElementById('wordCount').innerHTML = wordCount
document.getElementById('readTime').innerHTML = Math.floor(wordCount / 400) + 1
const wordCountWrapper = document.getElementById('wordCountWrapper')
wordCountWrapper.classList.remove('hidden')
}
// 去除html标签
function deleteHtmlTag(str) {
if (!str) {
return ''
}
str = str.replace(/<[^>]+>|&[^>]+;/g, '').trim()// 去掉所有的html标签和&nbsp;之类的特殊符合
return str
}
// 用word方式计算正文字数
function fnGetCpmisWords(str) {
if (!str) {
return 0
}
let sLen = 0
try {
// eslint-disable-next-line no-irregular-whitespace
str = str.replace(/(\r\n+|\s+| +)/g, '龘')
// eslint-disable-next-line no-control-regex
str = str.replace(/[\x00-\xff]/g, 'm')
str = str.replace(/m+/g, '*')
str = str.replace(/龘+/g, '')
sLen = str.length
} catch (e) {
}
return sLen
}

View File

@@ -35,6 +35,7 @@ export default {
SUBMIT: 'Submit',
POST_TIME: 'Post on',
LAST_EDITED_TIME: 'Last edited',
COMMENTS: 'Comments',
RECENT_COMMENTS: 'Recent Comments',
DEBUG_OPEN: 'Debug',
DEBUG_CLOSE: 'Close',

View File

@@ -12,7 +12,7 @@ export default {
COMMON: {
MORE: '更多',
NO_MORE: '没有更多了',
LATEST_POSTS: '最新文章',
LATEST_POSTS: '最新发布',
TAGS: '标签',
NO_TAG: 'NoTag',
CATEGORY: '分类',
@@ -37,6 +37,7 @@ export default {
SUBMIT: '提交',
POST_TIME: '发布于',
LAST_EDITED_TIME: '最后更新',
COMMENTS: '评论',
RECENT_COMMENTS: '最新评论',
DEBUG_OPEN: '开启调试',
DEBUG_CLOSE: '关闭调试',

View File

@@ -443,6 +443,7 @@ summary > .notion-h {
.notion-h:hover .notion-hash-link {
opacity: 1;
@apply dark:fill-gray-200
}
.notion-hash-link {

View File

@@ -1,4 +1,5 @@
import Link from 'next/link'
import { useEffect, useState } from 'react'
import CONFIG from '../config'
/**
@@ -6,28 +7,77 @@ import CONFIG from '../config'
* @param {prev,next} param0
* @returns
*/
export default function ArticleAdjacent ({ prev, next }) {
export default function ArticleAdjacent({ prev, next }) {
const [isScrollEnd, setIsScrollEnd] = useState(false)
useEffect(() => {
// 文章是否已经到了底部
const targetElement = document.getElementById('article-end')
const handleIntersect = (entries) => {
entries.forEach((entry) => {
console.log(entry.isIntersecting)
if (entry.isIntersecting) {
setIsScrollEnd(true)
} else {
// setIsScrollEnd(false)
}
})
}
const options = {
root: null,
rootMargin: '0px',
threshold: 0.1
}
const observer = new IntersectionObserver(handleIntersect, options)
observer.observe(targetElement)
return () => {
observer.disconnect()
}
}, [])
if (!prev || !next || !CONFIG.ARTICLE_ADJACENT) {
return <></>
}
return (
<section className='pt-8 text-gray-800 items-center text-xs md:text-sm flex justify-between m-1 '>
<Link
href={`/${prev.slug}`}
passHref
className='py-1 cursor-pointer hover:underline justify-start items-center dark:text-white flex w-full h-full duration-200'>
<div id='article-end'>
{/* 移动端 */}
<section className='lg:hidden pt-8 text-gray-800 items-center text-xs md:text-sm flex flex-col m-1 '>
<Link
href={`/${prev.slug}`}
passHref
className='cursor-pointer justify-between space-y-1 px-5 py-6 rounded-t-xl dark:bg-[#1e1e1e] border dark:border-gray-600 border-b-0 items-center dark:text-white flex flex-col w-full h-18 duration-200'
>
<div className='flex justify-start items-center w-full'>上一篇</div>
<div className='flex justify-center items-center text-lg font-bold'>{prev.title}</div>
</Link>
<Link
href={`/${next.slug}`}
passHref
className='cursor-pointer justify-between space-y-1 px-5 py-6 rounded-b-xl dark:bg-[#1e1e1e] border dark:border-gray-600 items-center dark:text-white flex flex-col w-full h-18 duration-200'
>
<div className='flex justify-start items-center w-full'>下一篇</div>
<div className='flex justify-center items-center text-lg font-bold'>{next.title}</div>
</Link>
</section>
<i className='mr-1 fas fa-angle-left' />{prev.title}
{/* 桌面端 */}
</Link>
<Link
href={`/${next.slug}`}
passHref
className='py-1 cursor-pointer hover:underline justify-end items-center dark:text-white flex w-full h-full duration-200'>
{next.title}
<i className='ml-1 my-1 fas fa-angle-right' />
<div id='pc-next-post' className={`hidden md:block fixed right-4 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
<Link
href={`/${next.slug}`}
className='cursor-pointer duration transition-all w-52 h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white hover:text-white hover:bg-gray-400 rounded-lg flex flex-col justify-between'
>
<div className='text-xs'>下一篇</div>
<hr />
<div>{next?.title}</div>
</Link>
</div>
</Link>
</section>
</div>
)
}

View File

@@ -13,53 +13,58 @@ export default function ArticleRecommend({ recommendPosts, siteInfo }) {
if (
!CONFIG.ARTICLE_RECOMMEND ||
!recommendPosts ||
recommendPosts.length === 0
!recommendPosts ||
recommendPosts.length === 0
) {
return <></>
}
return (
<div className="pt-8">
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
<div className='dark:text-gray-300'>
<i className="mr-2 fas fa-thumbs-up" />
{locale.COMMON.RELATE_POSTS}
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{recommendPosts.map(post => {
const headerImage = post?.pageCoverThumbnail
? `url("${post.pageCoverThumbnail}")`
: `url("${siteInfo?.pageCover}")`
<div className="pt-8 hidden md:block">
return (
(<Link
key={post.id}
title={post.title}
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className="flex h-40 cursor-pointer overflow-hidden">
<div
className="h-full w-full bg-cover bg-center bg-no-repeat hover:scale-110 transform duration-200"
style={{ backgroundImage: headerImage }}
>
<div className="flex items-center justify-center bg-black bg-opacity-60 hover:bg-opacity-10 w-full h-full duration-300 ">
<div className=" text-sm text-white text-center shadow-text">
<div>
<i className="fas fa-calendar-alt mr-1" />
{post.date?.start_date}
</div>
<div className="">{post.title}</div>
</div>
{/* 推荐文章 */}
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
<div className='dark:text-gray-300'>
<i className="mr-2 fas fa-thumbs-up" />
{locale.COMMON.RELATE_POSTS}
</div>
</div>
</div>
</Link>)
)
})}
</div>
</div>
{/* 文章列表 */}
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{recommendPosts.map(post => {
const headerImage = post?.pageCoverThumbnail
? `url("${post.pageCoverThumbnail}")`
: `url("${siteInfo?.pageCover}")`
return (
(<Link
key={post.id}
title={post.title}
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className="flex h-40 cursor-pointer overflow-hidden rounded-2xl">
<div
className="h-full w-full bg-cover bg-center bg-no-repeat hover:scale-110 transform duration-200"
style={{ backgroundImage: headerImage }}
>
<div className="flex items-center justify-center bg-black bg-opacity-60 hover:bg-opacity-10 w-full h-full duration-300 ">
<div className=" text-sm text-white text-center shadow-text">
<div>
<i className="fas fa-calendar-alt mr-1" />
{post.date?.start_date}
</div>
<div className="">{post.title}</div>
</div>
</div>
</div>
</Link>)
)
})}
</div>
</div>
)
}

View File

@@ -1,6 +1,8 @@
import React from 'react'
import Link from 'next/link'
import BLOG from '@/blog.config'
import CONFIG from '../config'
import TagItemMini from './TagItemMini'
/**
* 博客归档列表
* @param posts 所有文章
@@ -8,40 +10,75 @@ import BLOG from '@/blog.config'
* @returns {JSX.Element}
* @constructor
*/
const BlogPostArchive = ({ posts = [], archiveTitle }) => {
const BlogPostArchive = ({ posts = [], archiveTitle, siteInfo }) => {
if (!posts || posts.length === 0) {
return <></>
} else {
return (
<div>
<div
className="pt-16 pb-4 text-3xl dark:text-gray-300"
id={archiveTitle}
>
{archiveTitle}
</div>
<ul>
{posts?.map(post => (
<li
key={post.id}
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
>
<div id={post?.publishTime}>
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
&nbsp;
<Link
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className="dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
<div className=''>
<div
className="pb-4 dark:text-gray-300"
id={archiveTitle}
>
{archiveTitle}
</div>
<ul>
{posts?.map(post => {
const showPreview = CONFIG.POST_LIST_PREVIEW && post.blockMap
if (post && !post.pageCoverThumbnail && CONFIG.POST_LIST_COVER_DEFAULT) {
post.pageCoverThumbnail = siteInfo?.pageCover
}
const showPageCover = CONFIG.POST_LIST_COVER && post?.pageCoverThumbnail && !showPreview
return <div key={post.id} className={'cursor-pointer flex flex-row mb-4 h-20 md:flex-row group w-full dark:border-gray-600 hover:border-indigo-600 dark:hover:border-yellow-600 duration-300 transition-colors justify-between overflow-hidden'}>
{post.title}
{/* 图片封面 */}
{showPageCover && (
<div>
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
<div className={'rounded-xl bg-center bg-cover w-40 h-20'} style={{ backgroundImage: `url('${post?.pageCoverThumbnail}')` }} />
</Link>
</div>
)}
</Link>
</div>
</li>
))}
</ul>
</div>
{/* 文字区块 */}
<div className={'flex px-2 flex-col justify-between w-full'}>
<div>
{/* 分类 */}
{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 `}>
<Link passHref href={`/category/${post.category}`}
className="cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:text-gray-600 transform">
{post.category}
</Link>
</div>}
{/* 标题 */}
<Link
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className={' group-hover:text-indigo-700 group-hover:dark:text-indigo-400 text-black dark:text-gray-100 dark:group-hover:text-yellow-600 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'}>
<span className='menu-link '>{post.title}</span>
</Link>
</div>
{/* 摘要 */}
<p className="line-clamp-2 replace my-3 2xl:my-1 text-gray-700 dark:text-gray-300 text-sm font-light leading-tight">
{post.summary}
</p>
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
<div>
{' '}
{post.tagItems?.map(tag => (
<TagItemMini key={tag.name} tag={tag} />
))}
</div>
</div>
</div>
</div>
})}
</ul>
</div>
)
}
}

View File

@@ -29,7 +29,7 @@ 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 `}>
<Link passHref href={`/category/${post.category}`}
className="cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:text-gray-600 transform">
className="cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:hover:text-yellow-700 dark:text-gray-600 transform">
{post.category}
</Link>
</div>}
@@ -38,7 +38,7 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
<Link
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className={' group-hover:text-indigo-700 group-hover:dark:text-indigo-400 text-black dark:text-gray-100 dark:group-hover:text-yellow-600 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'}>
className={' group-hover:text-indigo-700 dark:hover:text-yellow-700 dark:group-hover:text-yellow-600 text-black dark:text-gray-100 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'}>
<span className='menu-link '>{post.title}</span>
</Link>
</div>

View File

@@ -1,5 +1,5 @@
const Card = ({ children, headerSlot, className }) => {
return <div className={`${className || ''} card border dark:border-gray-600 rounded-xl lg:p-6 p-4`}>
return <div className={`${className || ''} card border dark:border-gray-700 rounded-xl lg:p-6 p-4`}>
<>{headerSlot}</>
<section>
{children}

View File

@@ -61,13 +61,13 @@ const Catalog = ({ toc }) => {
return <></>
}
return <div className='px-3 py-1'>
return <div className='px-3 py-1 dark:text-white text-black'>
<div className='w-full'><i className='mr-1 fas fa-stream' />{locale.COMMON.TABLE_OF_CONTENTS}</div>
<div className='w-full py-3'>
<Progress />
</div>
<div className='overflow-y-auto max-h-36 lg:max-h-96 overscroll-none scroll-hidden' ref={tRef}>
<nav className='h-full text-black'>
<nav className='h-full'>
{toc.map((tocItem) => {
const id = uuidToId(tocItem.id)
tocIds.push(id)

View File

@@ -8,11 +8,11 @@ import { useRouter } from 'next/router'
* @returns
*/
export default function CategoryBar(props) {
const { categoryOptions } = props
const { categoryOptions, border = true } = props
const { locale } = useGlobal()
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] lg:border dark:lg:border-gray-800
py-2 lg:px-2 rounded-xl lg:hover:border hover:border-indigo-600 transition-colors duration-200">
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' className='rounded-lg scroll-hidden flex justify-start flex-nowrap items-center overflow-x-scroll'>
<MenuItem href='/' name={locale.NAV.INDEX} />

View File

@@ -177,11 +177,14 @@ function TopGroup(props) {
<div id='top-group' className='w-full flex space-x-3 xl:space-x-0 xl:grid xl:grid-cols-3 xl:gap-3'>
{latestPosts?.map(p => {
return <Link href={`${BLOG.SUB_PATH}/${p?.slug}`} key={p.id}>
<div className='cursor-pointer h-[164px] group relative flex flex-col w-52 xl:w-full overflow-hidden shadow bg-white rounded-xl'>
<div className='cursor-pointer h-[164px] group relative flex flex-col w-52 xl:w-full overflow-hidden shadow bg-white dark:bg-black dark:text-white rounded-xl'>
{/* eslint-disable-next-line */}
<img className='h-24 object-cover' src={p?.pageCoverThumbnail || siteInfo?.pageCover} />
<div className='group-hover:text-indigo-600 line-clamp-2 overflow-hidden m-2 font-semibold'>{p?.title}</div>
<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 text-white rounded-xl overflow-hidden pr-2 pb-2 pl-4 pt-4 text-xs'></div>
<div className='group-hover:text-indigo-600 group-hover:text-yellow-600 line-clamp-2 overflow-hidden m-2 font-semibold'>{p?.title}</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'>
</div>
</div>
</Link>
})}

View File

@@ -1,6 +1,7 @@
import BLOG from '@/blog.config'
import { ArrowRightCircle, GlobeAlt } from '@/components/HeroIcons'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
import CONFIG from '../config'
import Announcement from './Announcement'
@@ -13,13 +14,19 @@ import Card from './Card'
*/
export function InfoCard(props) {
const { siteInfo, notice } = props
const router = useRouter()
// 在文章详情页特殊处理
const isSlugPage = router.pathname === '/[...slug]'
return (
<Card className='bg-[#4f65f0] dark:bg-yellow-600 text-white flex flex-col w-72'>
<Card className='bg-[#4f65f0] dark:bg-yellow-600 text-white flex flex-col w-72 overflow-hidden relative'>
{/* 信息卡牌第一行 */}
<div className='flex justify-between'>
{/* 问候语 */}
<GreetingsWords />
<div className='justify-center items-center flex dark:text-gray-100 transform duration-200 cursor-pointer'>
<div className={`${isSlugPage ? 'absolute right-0 -mt-8 -mr-5 hover:opacity-0 hover:scale-150 blur' : 'cursor-pointer'} justify-center items-center flex dark:text-gray-100 transform transitaion-all duration-200`}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={siteInfo?.icon} className='rounded-full' width={28} alt={BLOG.AUTHOR} />
<img src={siteInfo?.icon} className='rounded-full' width={isSlugPage ? 100 : 28} alt={BLOG.AUTHOR} />
</div>
</div>
@@ -27,14 +34,21 @@ export function InfoCard(props) {
{BLOG.AUTHOR}
</h2>
{/* 公告栏 */}
<div>
<Announcement post={notice} style={{ color: 'white !important' }} />
</div>
<div className='flex justify-between'>
<div className='flex space-x-3 '>
<div className='bg-indigo-400 p-2 rounded-full hover:bg-white hover:text-black transition-colors duration-200'><Link href='/about'><GlobeAlt className={'w-6 h-6'} /></Link></div>
<div className='bg-indigo-400 p-2 rounded-full w-10 items-center flex justify-center hover:bg-white hover:text-black transition-colors duration-200'><Link href='https://github.com/tangly1024/NotionNext'><i className='fab fa-github text-xl'></i></Link></div>
<div className='flex space-x-3 hover:text-black dark:hover:text-white'>
{/* 两个社交按钮 */}
<div className='bg-indigo-400 p-2 rounded-full transition-colors duration-200 dark:bg-yellow-500 dark:hover:bg-black hover:bg-white'>
<Link href='/about'><GlobeAlt className={'w-6 h-6'} /></Link>
</div>
<div className='bg-indigo-400 p-2 rounded-full w-10 items-center flex justify-center transition-colors duration-200 dark:bg-yellow-500 dark:hover:bg-black hover:bg-white'>
<Link href='https://github.com/tangly1024/NotionNext'><i className='fab fa-github text-xl' />
</Link>
</div>
</div>
<MoreButton />
</div>
@@ -54,9 +68,9 @@ function GreetingsWords() {
setGreeting(greetings[randomIndex])
}
return <div onClick={handleChangeGreeting} className=' select-none cursor-pointer py-1 px-2 bg-indigo-400 text-sm rounded-lg hover:bg-indigo-50 hover:text-indigo-950 duration-200 transition-colors'>
{greeting}
</div>
return <div onClick={handleChangeGreeting} className=' select-none cursor-pointer py-1 px-2 bg-indigo-400 hover:bg-indigo-50 hover:text-indigo-950 dark:bg-yellow-500 dark:hover:text-white dark:hover:bg-black text-sm rounded-lg duration-200 transition-colors'>
{greeting}
</div>
}
/**
@@ -65,8 +79,8 @@ function GreetingsWords() {
*/
function MoreButton() {
return <Link href='/about'>
<div className={'group bg-indigo-400 hover:bg-white hover:text-black flex items-center transition-colors duration-200 py-2 px-3 rounded-full space-x-1'}>
<ArrowRightCircle className={'group-hover:stroke-black w-6 h-6 transition-all duration-100'} />
<div className={'group bg-indigo-400 dark:bg-yellow-500 hover:bg-white dark:hover:bg-black hover:text-black dark:hover:text-white flex items-center transition-colors duration-200 py-2 px-3 rounded-full space-x-1'}>
<ArrowRightCircle className={'group-hover:stroke-black dark:group-hover:stroke-white w-6 h-6 transition-all duration-100'} />
<div className='font-bold'>了解更多</div>
</div>
</Link>

View File

@@ -0,0 +1,63 @@
import BLOG from '@/blog.config'
import { useGlobal } from '@/lib/global'
// import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
/**
* 最新文章列表
* @param posts 所有文章数据
* @param sliceCount 截取展示的数量 默认6
* @constructor
*/
export default function LatestPostsGroupMini ({ latestPosts, siteInfo }) {
// 获取当前路径
const currentPath = useRouter().asPath
const { locale } = useGlobal()
if (!latestPosts) {
return <></>
}
return <>
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
<div>
<i className="mr-2 fas fas fa-history" />
{locale.COMMON.LATEST_POSTS}
</div>
</div>
{latestPosts.map(post => {
const selected = currentPath === `${BLOG.SUB_PATH}/${post.slug}`
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
return (
(<Link
key={post.id}
title={post.title}
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className={'my-3 flex'}>
<div className="w-20 h-14 overflow-hidden relative">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={`${headerImage}`} className='object-cover w-full h-full rounded-lg'/>
</div>
<div
className={
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
' 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'
}
>
<div>
<div className='line-clamp-2 menu-link'>{post.title}</div>
<div className="text-gray-500">{post.lastEditedTime}</div>
</div>
</div>
</Link>)
)
})}
</>
}

View File

@@ -10,16 +10,15 @@ const Logo = props => {
<div className='flex justify-center items-center cursor-pointer font-extrabold'>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={siteInfo?.icon} width={24} height={24} alt={BLOG.AUTHOR} className='mr-4 hidden md:block' />
<div id='logo-text' className='group hover:bg-indigo-600 rounded-2xl'>
<div className='group-hover:opacity-0 opacity-100 visible group-hover:invisible text-lg my-auto rounded dark:border-white duration-200 transition-all '>
<div id='logo-text' className='group rounded-2xl flex relative'>
<div className='group-hover:opacity-0 opacity-100 visible group-hover:invisible text-lg my-auto rounded dark:border-white duration-200'>
{siteInfo?.title || BLOG.TITLE}
</div>
<div className='group-hover:opacity-100 opacity-0 invisible group-hover:visible absolute top-4 justify-center px-8 py-1 transition-all duration-200'>
<Home className={'w-6 h-6 stroke-white stroke-2'}/>
<div className='flex rounded-2xl group-hover:absolute group-hover:bg-indigo-600 w-full group-hover:opacity-100 opacity-0 invisible group-hover:visible absolute justify-center px-8 py-1 duration-200'>
<Home className={'w-6 h-6 stroke-white stroke-2 '}/>
</div>
</div>
</div>
</Link>
)
}

View File

@@ -11,18 +11,19 @@ export const MenuItemDrop = ({ link }) => {
return <div onMouseOver={() => changeShow(true)} onMouseOut={() => changeShow(false)} >
{/* 不含子菜单 */}
{!hasSubMenu &&
<Link
href={link?.to}
className="font-sans menu-link pl-2 pr-4 no-underline tracking-widest pb-1">
{link?.icon && <i className={link?.icon}/>} {link?.name}
className="font-sans hover:bg-black hover:bg-opacity-10 rounded-2xl flex justify-center items-center px-3 py-1 no-underline tracking-widest">
{link?.icon && <i className={link?.icon} />} {link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
</Link>}
{/* 含子菜单的按钮 */}
{hasSubMenu && <>
<div className='cursor-pointer font-sans menu-link pl-2 pr-4 no-underline tracking-widest pb-1'>
{link?.icon && <i className={link?.icon}/>} {link?.name}
<i className={`px-2 fa fa-angle-down duration-300 ${show ? 'rotate-180' : 'rotate-0'}`}></i>
<div className='cursor-pointer font-sans hover:bg-black hover:bg-opacity-10 rounded-2xl flex justify-center items-center px-3 py-1 no-underline tracking-widest'>
{link?.icon && <i className={link?.icon} />} {link?.name}
</div>
</>}

View File

@@ -1,21 +1,24 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import Logo from './Logo'
import { MenuListTop } from './MenuListTop'
import throttle from 'lodash.throttle'
import RandomPostButton from './RandomPostButton'
import SearchButton from './SearchButton'
import SlideOver from './SlideOver'
/**
* 顶部导航
* @param {*} param0
* @returns
*/
const NavBar = props => {
const [isOpen, changeShow] = useState(false)
const [fixedNav, setFixedNav] = useState(false)
const [textWhite, setTextWhite] = useState(false)
const [navBgWhite, setBgWhite] = useState(false)
const slideOverRef = useRef()
const toggleMenuOpen = () => {
changeShow(!isOpen)
slideOverRef?.current?.toggleSlideOvers()
}
// 监听滚动
@@ -41,36 +44,45 @@ const NavBar = props => {
setBgWhite(false)
// 文章详情页特殊处理
const postHeader = document.querySelector('#post-bg')
if (postHeader) {
if (document.querySelector('#post-bg')) {
setFixedNav(true)
setTextWhite(true)
setBgWhite(false)
}
return
} else {
// 向下滚动后的导航样式
setFixedNav(true)
setTextWhite(false)
setBgWhite(true)
}
// 向下滚动后的导航样式
setFixedNav(true)
setTextWhite(false)
setBgWhite(true)
}, throttleMs))
return (<>
{/* 头条 */}
{/* 顶部导航菜单栏 */}
<nav id='nav' className={`${fixedNav ? 'fixed' : 'relative bg-none'} ${textWhite ? 'text-white ' : 'text-black dark:text-white'} ${navBgWhite ? 'bg-white dark:bg-[#18171d]' : 'bg-none'} z-20 h-16 top-0 w-full`}>
<div className='flex h-full mx-auto justify-between items-center max-w-[86rem] px-8'>
{/* 左侧logo */}
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 justify-end items-center '>
{/* 中间菜单 */}
<div className='mr-1 justify-end items-center hidden lg:block'>
<div className='hidden lg:flex'> <MenuListTop {...props} /></div>
<div onClick={toggleMenuOpen} className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
{/* 右侧固定 */}
<div className='flex justify-center items-center space-x-1'>
<RandomPostButton {...props} />
<SearchButton />
{/* 移动端菜单按钮 */}
<div onClick={toggleMenuOpen} className='flex lg:hidden w-8 justify-center items-center h-8 cursor-pointer'>
<i className='fas fa-bars' />
</div>
</div>
{/* 右边侧拉抽屉 */}
<SlideOver cRef={slideOverRef} {...props}/>
</div>
</nav>
</>)

View File

@@ -1,13 +1,11 @@
import Link from 'next/link'
import TagItemMini from './TagItemMini'
import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import NotionIcon from '@/components/NotionIcon'
import WavesArea from './WavesArea'
import { HashTag } from '@/components/HeroIcons'
import WordCount from '@/components/WordCount'
export default function PostHeader({ post, siteInfo }) {
const { locale } = useGlobal()
if (!post) {
return <></>
}
@@ -15,66 +13,83 @@ export default function PostHeader({ post, siteInfo }) {
const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover
return (
<div id="post-bg" className="w-full h-[30rem] relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat z-10">
<div id='post-bg' className="w-full h-[30rem] relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat z-10 mb-5">
<style jsx>{`
.coverdiv:after {
position: absolute;
content: '';
width: 100%;
height: 100%;
top: 0;
left: 0;
box-shadow: 110px -130px 300px 60px #0060e0 inset;
}
`}</style>
<div id='article-header-cover' style={{ backdropFilter: 'blur(15px)' }} className="bg-[#0060e0] absolute top-0 w-full h-full py-10 flex justify-center items-center ">
<div style={{ backdropFilter: 'blur(15px)' }} className={'bg-[#0060e0] absolute top-0 w-full h-full py-10 flex justify-center items-center'}>
<div id='post-cover-wrapper' style={{ filter: 'blur(15px)' }} className='opacity-50 rotate-12 translate-x-12 -mr-60'>
{/* 文章背景图 */}
<div id='post-cover-wrapper' style={{ filter: 'blur(15px)' }} className='coverdiv translate-x-96 loaded opacity-50 rotate-12'>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img id='post-cover' style={{ boxShadow: 'box-shadow:110px -130px 300px 60px #4d240c inset' }} className='w-full h-full object-cover opacity-80 min-w-[50vw] min-h-[20rem]' src={headerImage}/>
<img id='post-cover' className='w-full h-full object-cover opacity-80 max-h-[50rem] min-w-[50vw] min-h-[20rem]' src={headerImage} />
</div>
<div id='post-info' className='absolute z-10 flex flex-col w-full max-w-[86rem] px-5'>
<div className='mb-3 flex justify-start'>
{/* 文章文字描述 */}
<div id='post-info' className='absolute top-48 z-10 flex flex-col -mt-12 w-full max-w-[86rem] px-5'>
{/* 分类+标签 */}
<div className='mb-4 flex justify-center md:justify-start items-center'>
{post.category && <>
<Link href={`/category/${post.category}`} passHref legacyBehavior>
<div className="cursor-pointer px-2 py-1 mb-2 border rounded-sm dark:border-white text-sm font-medium hover:underline duration-200 shadow-text-md text-white">
<Link href={`/category/${post.category}`} 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 ">
{post.category}
</div>
</Link>
</>}
{post.tagItems && (
<div className="hidden md:flex justify-center flex-nowrap overflow-x-auto">
{post.tagItems.map(tag => (
<Link
key={tag}
href={`/tag/${encodeURIComponent(tag.name)}`}
passHref
className={'cursor-pointer inline-block text-gray-50 hover:text-white duration-200 py-0.5 px-1 whitespace-nowrap '}>
<div className='font-light flex items-center'><HashTag className='text-gray-200 stroke-2 mr-0.5 w-3 h-3' /> {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
</Link>
))}
</div>
)}
</div>
{/* 文章Title */}
<div className="max-w-5xl font-bold xs:text-4xl sm:text-4xl md:text-4xl md:leading-snug shadow-text-md flex justify-start text-white">
<span><NotionIcon icon={post.pageIcon} className='text-4xl mx-1' /></span>{post.title}
<div className="max-w-5xl my-4 font-bold text-3xl lg:text-5xl md:leading-snug shadow-text-md flex justify-center md:justify-start text-white">
<NotionIcon icon={post.pageIcon} />{post.title}
</div>
<section className="flex-wrap shadow-text-md flex text-sm justify-start mt-4 text-white dark:text-gray-400 font-light leading-8">
{/* 标题底部补充信息 */}
<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'>
<div className='mr-2'><WordCount /></div>
{post?.type !== 'Page' && (
<>
<Link
href={`/archive#${post?.publishTime?.substr(0, 7)}`}
passHref
className="pl-1 mr-2 cursor-pointer hover:underline">
{locale.COMMON.POST_TIME}:{post?.publishTime}
<i className="fa-solid fa-calendar-days"></i> {post?.publishTime}
</Link>
</>
)}
<div className="pl-1 mr-2">
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
</div>
</div>
{BLOG.ANALYTICS_BUSUANZI_ENABLE && <div className="busuanzi_container_page_pv font-light mr-2">
<span className="mr-2 busuanzi_value_page_pv" />
{locale.COMMON.VIEWS}
<i className="fa-solid fa-fire-flame-curved"></i> <span className="mr-2 busuanzi_value_page_pv" />
</div>}
</section>
<div className='mt-4 mb-1'>
{post.tagItems && (
<div className="flex justify-center flex-nowrap overflow-x-auto">
{post.tagItems.map(tag => (
<TagItemMini key={tag.name} tag={tag} />
))}
</div>
)}
</div>
</div>
<WavesArea />

View File

@@ -0,0 +1,19 @@
import { useRouter } from 'next/router'
/**
* 随机跳转到一个文章
*/
export default function RandomPostButton(props) {
const { latestPosts } = props
const router = useRouter()
function handleClick() {
const randomIndex = Math.floor(Math.random() * latestPosts.length)
const randomPost = latestPosts[randomIndex]
router.push(randomPost.slug)
}
return (
<div title={'随机前往一篇文章'} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all' onClick={handleClick}>
<i className="fa-solid fa-podcast"></i>
</div>
)
}

View File

@@ -0,0 +1,7 @@
import Link from 'next/link'
export default function SearchButton() {
return <Link href="/search" className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
<i className="fa-solid fa-magnifying-glass"></i>
</Link>
}

View File

@@ -69,7 +69,7 @@ const SearchInput = props => {
ref={searchInputRef}
type="text"
className={
'outline-none w-full text-sm pl-5 rounded-lg transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'
'outline-none w-full text-sm pl-5 rounded-lg transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-white dark:bg-gray-500'
}
onKeyUp={handleKeyUp}
onCompositionStart={lockSearchInput}

View File

@@ -23,9 +23,8 @@ export default function SearchNav(props) {
<div className="my-6 px-2">
<SearchInput cRef={cRef} {...props} />
{/* 分类 */}
<Card className="w-full mt-4">
<div className="dark:text-gray-200 mb-5 mx-3">
<i className="mr-4 fas fa-th" />
<Card className="w-full mt-4 bg-white dark:bg-[#1a191d]">
<div className="dark:text-gray-200 mb-5 mx-3 text-3xl">
{locale.COMMON.CATEGORY}:
</div>
<div id="category-list" className="duration-200 flex flex-wrap mx-8">
@@ -38,7 +37,7 @@ export default function SearchNav(props) {
legacyBehavior>
<div
className={
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-indigo-400 hover:text-white'
' duration-300 dark:hover:text-white dark:text-gray-200 rounded-2xl px-3 cursor-pointer py-1 hover:bg-indigo-600 dark:hover:bg-yellow-600 hover:text-white'
}
>
<i className="mr-4 fas fa-folder" />
@@ -50,9 +49,8 @@ export default function SearchNav(props) {
</div>
</Card>
{/* 标签 */}
<Card className="w-full mt-4">
<div className="dark:text-gray-200 mb-5 ml-4">
<i className="mr-4 fas fa-tag" />
<Card className="w-full mt-4 bg-white dark:bg-[#1a191d]">
<div className="dark:text-gray-200 mb-5 ml-4 text-3xl">
{locale.COMMON.TAGS}:
</div>
<div id="tags-list" className="duration-200 flex flex-wrap ml-8">

View File

@@ -4,9 +4,9 @@ import Catalog from './Catalog'
import { InfoCard } from './InfoCard'
import dynamic from 'next/dynamic'
import Live2D from '@/components/Live2D'
import FlipCard from '@/components/FlipCard'
import Link from 'next/link'
import { AnalyticsCard } from './AnalyticsCard'
import TouchMeCard from './TouchMeCard'
import LatestPostsGroupMini from './LatestPostsGroupMini'
const FaceBookPage = dynamic(
() => {
@@ -32,47 +32,37 @@ export default function SideRight(props) {
currentTag, rightAreaSlot
} = props
console.log('props', props)
return (
<div id='sideRight' className='hidden xl:block w-72 space-y-4'>
<div id='sideRight' className='hidden xl:block w-72 space-y-4 h-full'>
<InfoCard {...props} className='w-72' />
<div className={'relative h-28 border rounded-xl lg:p-6 p-4 bg-[#4f65f0] text-white flex flex-col'}>
<div className='sticky top-20 space-y-4'>
{post && post.toc && post.toc.length > 0 && (
<Card className='bg-white dark:bg-[#1e1e1e]'>
<Catalog toc={post.toc} />
</Card>
)}
<FlipCard
className='cursor-pointer'
frontContent={
<div>
<h2 className='font-[1000] text-3xl'>交流频道</h2>
<h3 className='pt-2'>加入我们的社群讨论分享</h3>
<div className='absolute left-0 top-0 w-full h-full' style={{ background: 'url(https://bu.dusays.com/2023/05/16/64633c4cd36a9.png) center center no-repeat' }}></div>
</div>}
{/* 联系交流群 */}
<TouchMeCard />
backContent={<div className='font-[1000] text-xl'>
<Link href='https://docs.tangly1024.com/article/how-to-question'>
点击查看联系方式
</Link>
</div>}
/>
</div>
{/* 标签和成绩 */}
<Card className={'sticky top-20 bg-white'}>
<TagGroups tags={tagOptions} currentTag={currentTag} />
<hr className='mx-1 flex border-dashed relative my-2'/>
<AnalyticsCard {...props} />
</Card>
<div className='sticky top-20'>
{post && post.toc && post.toc.length > 1 && <Card>
<Catalog toc={post.toc} />
</Card>}
{/* 最新文章列表 */}
<div className={'border dark:border-gray-700 rounded-xl lg:p-6 p-4 hidden lg:block bg-white'}>
<LatestPostsGroupMini {...props} />
</div>
{rightAreaSlot}
<FaceBookPage />
<Live2D />
{/* 标签和成绩 */}
<Card className={'bg-white dark:bg-[#1e1e1e] dark:text-white'}>
<TagGroups tags={tagOptions} currentTag={currentTag} />
<hr className='mx-1 flex border-dashed relative my-4' />
<AnalyticsCard {...props} />
</Card>
</div>
</div>

View File

@@ -0,0 +1,111 @@
import { Fragment, useImperativeHandle, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import DarkModeButton from '@/components/DarkModeButton'
import Link from 'next/link'
import TagGroups from './TagGroups'
/**
* 侧边抽屉
*/
export default function SlideOver(props) {
const { cRef, tagOptions } = props
const [open, setOpen] = useState(false)
/**
* 函数组件暴露方法useImperativeHandle
*/
useImperativeHandle(cRef, () => ({
toggleSlideOvers: toggleSlideOvers
}))
const toggleSlideOvers = () => {
setOpen(!open)
}
return (
<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="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-300 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>
<div className={'flex justify-between items-center px-2 py-2 border dark:border-gray-600 bg-white dark:bg-[#ff953e] rounded-lg'}> <DarkModeButton /> 显示模式</div>
</section>
<section className='space-y-2 flex flex-col'>
<div>博客</div>
<div className='gap-2 grid grid-cols-2'>
<Link href='/' className={'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'}>
主页
</Link>
<Link href='/about' className={'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'}>
关于
</Link>
</div>
</section>
<section className='space-y-2 flex flex-col'>
<div>标签</div>
<TagGroups tags={tagOptions}/>
</section>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
)
}

View File

@@ -7,7 +7,7 @@ import Link from 'next/link'
* @returns {JSX.Element}
* @constructor
*/
const TagGroups = ({ tags, currentTag }) => {
const TagGroups = ({ tags, className }) => {
if (!tags) return <></>
return (
<div id='tags-group' className='dark:border-gray-700 space-y-2'>
@@ -17,8 +17,8 @@ const TagGroups = ({ tags, currentTag }) => {
<Link passHref
key={tag}
href={`/tag/${encodeURIComponent(tag.name)}`}
className={'cursor-pointer inline-block whitespace-nowrap '}>
<div className='flex items-center hover:bg-blue-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'>
className={'cursor-pointer inline-block whitespace-nowrap'}>
<div className={`${className || ''} flex items-center hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all`}>
<div className='text-lg'>{tag.name} </div>{tag.count ? <sup className='relative ml-1'>{tag.count}</sup> : <></>}
</div>

View File

@@ -7,9 +7,8 @@ const TagItemMini = ({ tag, selected = false }) => {
key={tag}
href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`}
passHref
className={'cursor-pointer inline-block hover:text-indigo-600 dark:text-white duration-200 py-0.5 px-1 text-sm whitespace-nowrap ' }>
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>
</Link>
)
}

View File

@@ -0,0 +1,30 @@
import FlipCard from '@/components/FlipCard'
import Link from 'next/link'
/**
* 交流频道
* @returns
*/
export default function TouchMeCard() {
return (
<div className={'relative h-28 text-white flex flex-col'}>
<FlipCard
className='cursor-pointer lg:p-6 p-4 border rounded-xl bg-[#4f65f0] dark:bg-yellow-600 dark:border-gray-600'
frontContent={
<div className='h-full'>
<h2 className='font-[1000] text-3xl'>交流频道</h2>
<h3 className='pt-2'>加入我们的社群讨论分享</h3>
<div className='absolute left-0 top-0 w-full h-full' style={{ background: 'url(https://bu.dusays.com/2023/05/16/64633c4cd36a9.png) center center no-repeat' }}></div>
</div>}
backContent={<div className='font-[1000] text-xl h-full'>
<Link href='https://docs.tangly1024.com/article/how-to-question'>
点击加入社群
</Link>
</div>}
/>
</div>
)
}

View File

@@ -1,7 +1,12 @@
import { useGlobal } from '@/lib/global'
/**
* 文章波浪动画
*/
export default function WavesArea() {
const { isDarkMode } = useGlobal()
const color = isDarkMode ? '#18171d' : '#f7f9fe'
return (
<section className="main-hero-waves-area waves-area w-full absolute left-0 z-10 bottom-0">
<svg className="waves-svg w-full h-[60px]" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
@@ -24,25 +29,25 @@ export default function WavesArea() {
.parallax > use:nth-child(1) {
animation-delay: -2s;
animation-duration: 7s;
fill: #f7f9fe;
fill: ${color};
opacity: 0.5;
}
.parallax > use:nth-child(2) {
animation-delay: -3s;
animation-duration: 10s;
fill: #f7f9fe;
fill: ${color};
opacity: 0.6;
}
.parallax > use:nth-child(3) {
animation-delay: -4s;
animation-duration: 13s;
fill: #f7f9fe;
fill: ${color};
opacity: 0.7;
}
.parallax > use:nth-child(4) {
animation-delay: -5s;
animation-duration: 20s;
fill: #f7f9fe;
fill: ${color};
}
@keyframes move-forever {

View File

@@ -1,7 +1,7 @@
import CONFIG from './config'
import CommonHead from '@/components/CommonHead'
import { useEffect, useRef } from 'react'
import { useEffect } from 'react'
import Footer from './components/Footer'
import SideRight from './components/SideRight'
import NavBar from './components/NavBar'
@@ -13,12 +13,10 @@ import BlogPostListScroll from './components/BlogPostListScroll'
import Hero from './components/Hero'
import { useRouter } from 'next/router'
import Mark from 'mark.js'
import Card from './components/Card'
import SearchNav from './components/SearchNav'
import BlogPostArchive from './components/BlogPostArchive'
import { ArticleLock } from './components/ArticleLock'
import PostHeader from './components/PostHeader'
import TocDrawer from './components/TocDrawer'
import Comment from '@/components/Comment'
import NotionPage from '@/components/NotionPage'
import ArticleAdjacent from './components/ArticleAdjacent'
@@ -49,7 +47,7 @@ const LayoutBase = props => {
}
return (
<div id='theme-heo' className='bg-[#f7f9fe] dark:bg-[#18171d] h-full min-h-screen flex flex-col overflow-hidden'>
<div id='theme-heo' className='bg-[#f7f9fe] dark:bg-[#18171d] h-full min-h-screen flex flex-col'>
{/* 网页SEO */}
<CommonHead meta={meta} siteInfo={siteInfo} />
<Style />
@@ -67,10 +65,11 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveFrom="opacity-100"
leaveTo="opacity-0 -translate-y-16"
className='w-full h-auto'
unmount={false}
>
{/* 主区上部嵌入 */}
@@ -157,6 +156,11 @@ const LayoutSearch = props => {
const { keyword } = props
const router = useRouter()
const currentSearch = keyword || router?.query?.s
const headerSlot = <header className='post-bg'>
{/* 顶部导航 */}
<div id='nav-bar-wrapper'><NavBar {...props} /></div>
<PostHeader {...props} />
</header>
useEffect(() => {
setTimeout(() => {
@@ -177,7 +181,7 @@ const LayoutSearch = props => {
})
return (
<LayoutBase {...props} currentSearch={currentSearch} className='pt-8'>
<LayoutBase {...props} currentSearch={currentSearch} headerSlot={headerSlot}>
{!currentSearch
? <SearchNav {...props} />
: <div id="posts-wrapper"> {BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />} </div>}
@@ -192,9 +196,22 @@ const LayoutSearch = props => {
*/
const LayoutArchive = (props) => {
const { archivePosts } = props
return <LayoutBase {...props} className='pt-8'>
<Card className='w-full'>
<div className="mb-10 pb-20 bg-white md:p-12 p-3 min-h-full dark:bg-hexo-black-gray">
// 右侧栏
const slotRight = <SideRight {...props} />
const headerSlot = <header>
{/* 顶部导航 */}
<div id='nav-bar-wrapper' className='h-16'><NavBar {...props} /></div>
</header>
// 归档页顶部显示条,如果是默认归档则不显示。分类详情页显示分类列表,标签详情页显示当前标签
return <LayoutBase {...props} slotRight={slotRight} headerSlot={headerSlot}>
<div className='p-5 rounded-xl border dark:border-gray-600 max-w-6xl w-full bg-white dark:bg-[#1e1e1e]'>
{/* 文章分类条 */}
<CategoryBar {...props} border={false} />
<div className='px-3'>
{Object.keys(archivePosts).map(archiveTitle => (
<BlogPostArchive
key={archiveTitle}
@@ -203,7 +220,7 @@ const LayoutArchive = (props) => {
/>
))}
</div>
</Card>
</div>
</LayoutBase>
}
@@ -214,12 +231,11 @@ const LayoutArchive = (props) => {
*/
const LayoutSlug = props => {
const { post, lock, validPassword } = props
const drawerRight = useRef(null)
const { locale } = useGlobal()
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
// 右侧栏
const slotRight = <SideRight {...props} />
const headerSlot = <header>
const headerSlot = <header className='post-bg'>
{/* 顶部导航 */}
<div id='nav-bar-wrapper'><NavBar {...props} /></div>
<PostHeader {...props} />
@@ -227,7 +243,7 @@ const LayoutSlug = props => {
return (
<LayoutBase {...props} headerSlot={headerSlot} showCategory={false} showTag={false} slotRight={slotRight}>
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black article">
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article">
{lock && <ArticleLock validPassword={validPassword} />}
{!lock && <div id="article-wrapper" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
@@ -241,8 +257,12 @@ const LayoutSlug = props => {
{/* 分享 */}
<ShareBar post={post} />
{post?.type === 'Post' && <>
{/* 版权 */}
<ArticleCopyright {...props} />
{/* 文章推荐 */}
<ArticleRecommend {...props} />
{/* 上一篇\下一篇文章 */}
<ArticleAdjacent {...props} />
</>}
@@ -251,16 +271,13 @@ const LayoutSlug = props => {
<div className='pt-4 border-dashed'></div>
{/* 评论互动 */}
<div className="duration-200 overflow-x-auto bg-white dark:bg-hexo-black-gray px-3">
<Comment frontMatter={post} />
<div className="duration-200 overflow-x-auto px-3">
<div className='text-2xl dark:text-white'><i className='fas fa-comment mr-1' />{locale.COMMON.COMMENTS}</div>
<Comment frontMatter={post} className='' />
</div>
</div>}
</div>
<div className='block lg:hidden'>
<TocDrawer post={post} cRef={drawerRight} targetRef={targetRef} />
</div>
</LayoutBase>
)
}
@@ -274,7 +291,7 @@ const Layout404 = props => {
const { meta, siteInfo } = props
const { onLoading } = useGlobal()
return (
<div id='theme-heo' className='bg-[#f7f9fe] h-full min-h-screen flex flex-col overflow-hidden'>
<div id='theme-heo' className='bg-[#f7f9fe] h-full min-h-screen flex flex-col'>
{/* 网页SEO */}
<CommonHead meta={meta} siteInfo={siteInfo} />
<Style />

View File

@@ -8,6 +8,7 @@ const Style = () => {
return <style jsx global>{`
body {
background-color: #f7f9fe;
overflow-x: hidden;
}
// 公告栏中的字体固定白色

View File

@@ -1,11 +1,10 @@
import Link from 'next/link'
import BLOG from '@/blog.config'
import { useGlobal } from '@/lib/global'
import CONFIG from '../config'
import { SvgIcon } from './SvgIcon'
import { MenuItemDrop } from './MenuItemDrop'
import FullScreenButton from '@/components/FullScreenButton'
import InformationButton from './InformationButton'
import LogoBar from './LogoBar'
/**
* 桌面端底部导航
@@ -13,35 +12,10 @@ import InformationButton from './InformationButton'
* @returns
*/
const BottomNav = props => {
const { navBarTitle, siteInfo } = props
return <>
<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">
{/* <SvgIcon/> */}
{CONFIG.NAV_NOTION_ICON
/* eslint-disable-next-line @next/next/no-img-element */
? <img src={siteInfo?.icon} className='rounded-full' width={24} height={24} alt={BLOG.AUTHOR} />
: <SvgIcon />}
</div>
</Link>
{navBarTitle
? (
<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">
<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>
{/* 左侧logo文字栏 */}
<LogoBar {...props}/>
{/* 右下角菜单栏 */}
<MenuList {...props} />
</div>

View File

@@ -9,7 +9,6 @@ import { useRef } from 'react'
export default function InformationButton() {
const slideOversRef = useRef({})
const toggleCollapsed = () => {
console.log(slideOversRef)
slideOversRef.current.toggleSlideOvers()
}

View File

@@ -0,0 +1,39 @@
import BLOG from '@/blog.config'
import Link from 'next/link'
import CONFIG from '../config'
import { SvgIcon } from './SvgIcon'
/**
* logo文字栏
* @param {*} props
* @returns
*/
export default function LogoBar(props) {
const { navBarTitle, siteInfo } = props
return <div className="flex items-center">
<Link href="/" aria-label={BLOG.title}>
<div className="h-6 w-6">
{/* <SvgIcon/> */}
{CONFIG.NAV_NOTION_ICON
/* eslint-disable-next-line @next/next/no-img-element */
? <img src={siteInfo?.icon} className='rounded-full' width={24} height={24} alt={BLOG.AUTHOR} />
: <SvgIcon />}
</div>
</Link>
{navBarTitle
? (
<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">
<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>
}

View File

@@ -90,7 +90,7 @@ const LayoutBase = props => {
</main>
{/* 弹出框 */}
{/* 弹出框 - 用于放大显示首页图片等作用 */}
<Modal {...props}/>
{/* 桌面端底部导航栏 */}