mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 15:09:13 +00:00
theme-heo
This commit is contained in:
@@ -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'>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
67
components/WordCount.js
Normal 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> {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标签和 之类的特殊符合
|
||||
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
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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: '关闭调试',
|
||||
|
||||
@@ -443,6 +443,7 @@ summary > .notion-h {
|
||||
|
||||
.notion-h:hover .notion-hash-link {
|
||||
opacity: 1;
|
||||
@apply dark:fill-gray-200
|
||||
}
|
||||
|
||||
.notion-hash-link {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>{' '}
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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>
|
||||
})}
|
||||
|
||||
@@ -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>
|
||||
|
||||
63
themes/heo/components/LatestPostsGroupMini.js
Normal file
63
themes/heo/components/LatestPostsGroupMini.js
Normal 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>)
|
||||
)
|
||||
})}
|
||||
</>
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>}
|
||||
|
||||
|
||||
@@ -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>
|
||||
</>)
|
||||
|
||||
@@ -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 />
|
||||
|
||||
19
themes/heo/components/RandomPostButton.js
Normal file
19
themes/heo/components/RandomPostButton.js
Normal 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>
|
||||
)
|
||||
}
|
||||
7
themes/heo/components/SearchButton.js
Normal file
7
themes/heo/components/SearchButton.js
Normal 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>
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
111
themes/heo/components/SlideOver.js
Normal file
111
themes/heo/components/SlideOver.js
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
30
themes/heo/components/TouchMeCard.js
Normal file
30
themes/heo/components/TouchMeCard.js
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -8,6 +8,7 @@ const Style = () => {
|
||||
return <style jsx global>{`
|
||||
body {
|
||||
background-color: #f7f9fe;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// 公告栏中的字体固定白色
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useRef } from 'react'
|
||||
export default function InformationButton() {
|
||||
const slideOversRef = useRef({})
|
||||
const toggleCollapsed = () => {
|
||||
console.log(slideOversRef)
|
||||
slideOversRef.current.toggleSlideOvers()
|
||||
}
|
||||
|
||||
|
||||
39
themes/plog/components/LogoBar.js
Normal file
39
themes/plog/components/LogoBar.js
Normal 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>
|
||||
}
|
||||
@@ -90,7 +90,7 @@ const LayoutBase = props => {
|
||||
|
||||
</main>
|
||||
|
||||
{/* 弹出框 */}
|
||||
{/* 弹出框 - 用于放大显示首页图片等作用 */}
|
||||
<Modal {...props}/>
|
||||
|
||||
{/* 桌面端底部导航栏 */}
|
||||
|
||||
Reference in New Issue
Block a user