mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-05 15:10:26 +00:00
@@ -4,8 +4,12 @@ const BLOG = {
|
|||||||
email: 'tlyong1992@hotmail.com',
|
email: 'tlyong1992@hotmail.com',
|
||||||
link: 'https://tangly1024.com',
|
link: 'https://tangly1024.com',
|
||||||
description: '分享编程技术与记录生活',
|
description: '分享编程技术与记录生活',
|
||||||
headerStrings: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页文字
|
keywords: ['Notion', '写作', '博客'],
|
||||||
bannerImage: './bg_image.jpg', // 首图
|
home: { // 首页
|
||||||
|
showHomeBanner: false, // 首页是否显示大图及标语 [true,false]
|
||||||
|
homeBannerStrings: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
|
||||||
|
homeBannerImage: './bg_image.jpg' // 首图
|
||||||
|
},
|
||||||
lang: 'zh-CN', // ['zh-CN','en-US'] default lang => see /lib/lang.js for more.
|
lang: 'zh-CN', // ['zh-CN','en-US'] default lang => see /lib/lang.js for more.
|
||||||
notionPageId: process.env.NOTION_PAGE_ID || 'bee1fccfa3bd47a1a7be83cc71372d83', // Important page_id!!!
|
notionPageId: process.env.NOTION_PAGE_ID || 'bee1fccfa3bd47a1a7be83cc71372d83', // Important page_id!!!
|
||||||
notionAccessToken: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
notionAccessToken: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||||
@@ -17,13 +21,33 @@ const BLOG = {
|
|||||||
since: 2020, // if leave this empty, current year will be used.
|
since: 2020, // if leave this empty, current year will be used.
|
||||||
postsPerPage: 6, // post counts per page
|
postsPerPage: 6, // post counts per page
|
||||||
sortByDate: false,
|
sortByDate: false,
|
||||||
showAbout: true, // WIP 是否显示关于
|
autoCollapsedNavBar: true, // the automatically collapsed navigation bar
|
||||||
showArchive: true, // WIP 是否显示归档
|
menu: { // menu config
|
||||||
autoCollapsedNavBar: false, // the automatically collapsed navigation bar
|
showAbout: false,
|
||||||
socialLink: 'https://weibo.com/u/2245301913',
|
showCategory: true,
|
||||||
seo: {
|
showTag: true,
|
||||||
keywords: ['Notion', '写作', '博客'],
|
showArchive: true,
|
||||||
googleSiteVerification: '' // Remove the value or replace it with your own google site verification code
|
showSearch: true
|
||||||
|
},
|
||||||
|
widget: {
|
||||||
|
showPet: false, // 是否显示宠物挂件
|
||||||
|
petLink: 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
||||||
|
showToTop: true,
|
||||||
|
showToBottom: true,
|
||||||
|
showDarkMode: true,
|
||||||
|
showToc: true,
|
||||||
|
showShareBar: false,
|
||||||
|
showRelatePosts: false,
|
||||||
|
showCopyRight: false,
|
||||||
|
showLatestPost: false,
|
||||||
|
showCategoryList: false,
|
||||||
|
showTagList: false
|
||||||
|
},
|
||||||
|
socialLink: { // 社交链接
|
||||||
|
weibo: 'https://weibo.com/tangly1024',
|
||||||
|
twitter: 'https://twitter.com/troy1024_1',
|
||||||
|
github: 'https://github.com/tangly1024',
|
||||||
|
telegram: 'https://t.me/tangly_1024'
|
||||||
},
|
},
|
||||||
analytics: {
|
analytics: {
|
||||||
provider: 'ga', // Currently we support Google Analytics and Ackee, please fill with 'ga' or 'ackee', leave it empty to disable it.
|
provider: 'ga', // Currently we support Google Analytics and Ackee, please fill with 'ga' or 'ackee', leave it empty to disable it.
|
||||||
@@ -36,12 +60,11 @@ const BLOG = {
|
|||||||
measurementId: 'G-68EK0W049N' // e.g: G-XXXXXXXXXX
|
measurementId: 'G-68EK0W049N' // e.g: G-XXXXXXXXXX
|
||||||
},
|
},
|
||||||
baiduAnalytics: 'f683ef76f06bb187cbed5546f6f28f28', // e.g only need xxxxx -> https://hm.baidu.com/hm.js?[xxxxx]
|
baiduAnalytics: 'f683ef76f06bb187cbed5546f6f28f28', // e.g only need xxxxx -> https://hm.baidu.com/hm.js?[xxxxx]
|
||||||
busuanzi: true, // see http://busuanzi.ibruce.info/
|
busuanzi: true, // 展示网站阅读量访问数 see http://busuanzi.ibruce.info/
|
||||||
cnzzAnalytics: '' // 站长统计id only need xxxxxxxx -> https://s9.cnzz.com/z_stat.php?id=[xxxxxxxx]&web_id=[xxxxxxx]
|
cnzzAnalytics: '' // 站长统计id only need xxxxxxxx -> https://s9.cnzz.com/z_stat.php?id=[xxxxxxxx]&web_id=[xxxxxxx]
|
||||||
},
|
},
|
||||||
comment: {
|
comment: { // support provider: gitalk, utterances, cusdis
|
||||||
// support provider: gitalk, utterances, cusdis
|
provider: '', // leave it empty if you don't need any comment plugin
|
||||||
provider: 'cusdis', // leave it empty if you don't need any comment plugin
|
|
||||||
gitalkConfig: {
|
gitalkConfig: {
|
||||||
repo: 'NotionNext', // The repository of store comments
|
repo: 'NotionNext', // The repository of store comments
|
||||||
owner: 'tangly1024',
|
owner: 'tangly1024',
|
||||||
@@ -57,14 +80,15 @@ const BLOG = {
|
|||||||
},
|
},
|
||||||
utterancesConfig: {
|
utterancesConfig: {
|
||||||
repo: 'tangly1024/NotionNext'
|
repo: 'tangly1024/NotionNext'
|
||||||
}
|
},
|
||||||
|
DaoVoiceId: '', // DaoVoice http://dashboard.daovoice.io/get-started
|
||||||
|
TidioId: '' // https://www.tidio.com/
|
||||||
|
},
|
||||||
|
seo: {
|
||||||
|
googleSiteVerification: '' // Remove the value or replace it with your own google site verification code
|
||||||
},
|
},
|
||||||
googleAdsenseId: 'ca-pub-2708419466378217', // 谷歌广告ID
|
googleAdsenseId: 'ca-pub-2708419466378217', // 谷歌广告ID
|
||||||
DaoVoiceId: '', // 在线聊天 DaoVoice http://dashboard.daovoice.io/get-started
|
isProd: process.env.VERCEL_ENV === 'production'// distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
||||||
TidioId: '', // 在线聊天 https://www.tidio.com/
|
|
||||||
isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
|
||||||
showPet: true // 详情页是否显示宠物挂件
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// export default BLOG
|
// export default BLOG
|
||||||
module.exports = BLOG
|
module.exports = BLOG
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { faChartBar } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计网站信息
|
* 统计网站信息
|
||||||
@@ -11,7 +9,7 @@ export default function Analytics ({ postCount }) {
|
|||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className='px-5 text-sm font-light pb-1 text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faChartBar} className='mr-2' />{locale.COMMON.ANALYTICS}</div>
|
{/* <div className='px-5 text-sm font-light pb-1 text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faChartBar} className='mr-2' />{locale.COMMON.ANALYTICS}</div> */}
|
||||||
<div className='mt-2 text-center dark:text-gray-300 font-light text-xs'>
|
<div className='mt-2 text-center dark:text-gray-300 font-light text-xs'>
|
||||||
<span className='px-1 '>
|
<span className='px-1 '>
|
||||||
<strong className='font-medium'>{postCount}</strong>{locale.COMMON.POSTS}</span>
|
<strong className='font-medium'>{postCount}</strong>{locale.COMMON.POSTS}</span>
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
export default function ArticleCopyright ({ author, url }) {
|
export default function ArticleCopyright ({ author, url }) {
|
||||||
|
if (!BLOG.widget?.showCopyRight) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
return <section className="dark:text-gray-300 mt-6">
|
return <section className="dark:text-gray-300 mt-6">
|
||||||
<div className="text-2xl mb-2">{locale.COMMON.COPYRIGHT}</div>
|
<div className="text-2xl mb-2">{locale.COMMON.COPYRIGHT}</div>
|
||||||
|
|||||||
@@ -51,16 +51,16 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
|
|||||||
const attachZoomRef = attachZoom
|
const attachZoomRef = attachZoom
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<div id="article-wrapper" ref={targetRef} className="overflow-x-auto flex-grow max-w-5xl mx-auto w-screen md:w-full ">
|
<div id="article-wrapper" ref={targetRef} className="shadow md:hover:shadow-2xl duration-300 overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||||
<article itemScope itemType="https://schema.org/Movie"
|
<article itemScope itemType="https://schema.org/Movie"
|
||||||
className="shadow md:hover:shadow-2xl duration-300 subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 xl:px-32 dark:border-gray-700 bg-white dark:bg-gray-800"
|
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-gray-800"
|
||||||
>
|
>
|
||||||
|
|
||||||
<header className='animate__slideInDown animate__animated'>
|
<header className='animate__slideInDown animate__animated'>
|
||||||
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
||||||
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img alt={post.title} ref={attachZoomRef}src={post?.page_cover} className='object-center' />
|
<img alt={post.title} ref={attachZoomRef}src={post?.page_cover} className='object-center w-full' />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
|
|||||||
</article>
|
</article>
|
||||||
|
|
||||||
{/* 评论互动 */}
|
{/* 评论互动 */}
|
||||||
<div className="mt-5 lg:px-40 md:hover:shadow-2xl duration-200 shadow w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-700">
|
<div className="lg:px-40 md:hover:shadow-2xl duration-200 shadow w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-800">
|
||||||
<Comment frontMatter={post} />
|
<Comment frontMatter={post} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,7 +177,6 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 宠物 */}
|
{/* 宠物 */}
|
||||||
{BLOG.showPet && <Live2D/>}
|
|
||||||
<Live2D/>
|
<Live2D/>
|
||||||
|
|
||||||
</>)
|
</>)
|
||||||
|
|||||||
@@ -26,42 +26,46 @@ const Comment = ({ frontMatter }) => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { theme } = useGlobal()
|
const { theme } = useGlobal()
|
||||||
|
|
||||||
return <div className='comment text-gray-800 dark:text-gray-300'>
|
return (
|
||||||
{BLOG.comment.provider === 'gitalk' && (<div className='m-10'>
|
BLOG.comment.provider !== '' && (
|
||||||
<GitalkComponent
|
<div className='comment mt-5 text-gray-800 dark:text-gray-300'>
|
||||||
options={{
|
{BLOG.comment.provider === 'gitalk' && (<div className='m-10'>
|
||||||
id: frontMatter.id,
|
<GitalkComponent
|
||||||
title: frontMatter.title,
|
options={{
|
||||||
clientID: BLOG.comment.gitalkConfig.clientID,
|
id: frontMatter.id,
|
||||||
clientSecret: BLOG.comment.gitalkConfig.clientSecret,
|
title: frontMatter.title,
|
||||||
repo: BLOG.comment.gitalkConfig.repo,
|
clientID: BLOG.comment.gitalkConfig.clientID,
|
||||||
owner: BLOG.comment.gitalkConfig.owner,
|
clientSecret: BLOG.comment.gitalkConfig.clientSecret,
|
||||||
admin: BLOG.comment.gitalkConfig.admin,
|
repo: BLOG.comment.gitalkConfig.repo,
|
||||||
distractionFreeMode: BLOG.comment.gitalkConfig.distractionFreeMode
|
owner: BLOG.comment.gitalkConfig.owner,
|
||||||
}}
|
admin: BLOG.comment.gitalkConfig.admin,
|
||||||
/>
|
distractionFreeMode: BLOG.comment.gitalkConfig.distractionFreeMode
|
||||||
</div>)}
|
}}
|
||||||
{BLOG.comment.provider === 'utterances' && (<div className='m-10'>
|
/>
|
||||||
<UtterancesComponent issueTerm={frontMatter.id} className='px-2' />
|
</div>)}
|
||||||
</div>
|
{BLOG.comment.provider === 'utterances' && (<div className='m-10'>
|
||||||
)}
|
<UtterancesComponent issueTerm={frontMatter.id} className='px-2' />
|
||||||
{BLOG.comment.provider === 'cusdis' && (<>
|
</div>
|
||||||
<script defer src='https://cusdis.com/js/widget/lang/zh-cn.js' />
|
)}
|
||||||
<div className='m-10'>
|
{BLOG.comment.provider === 'cusdis' && (<>
|
||||||
<CusdisComponent
|
<script defer src='https://cusdis.com/js/widget/lang/zh-cn.js' />
|
||||||
attrs={{
|
<div className='m-10'>
|
||||||
host: BLOG.comment.cusdisConfig.host,
|
<CusdisComponent
|
||||||
appId: BLOG.comment.cusdisConfig.appId,
|
attrs={{
|
||||||
pageId: frontMatter.id,
|
host: BLOG.comment.cusdisConfig.host,
|
||||||
pageTitle: frontMatter.title,
|
appId: BLOG.comment.cusdisConfig.appId,
|
||||||
pageUrl: BLOG.link + router.asPath,
|
pageId: frontMatter.id,
|
||||||
theme: theme
|
pageTitle: frontMatter.title,
|
||||||
}}
|
pageUrl: BLOG.link + router.asPath,
|
||||||
lang={BLOG.lang.toLowerCase()}
|
theme: theme
|
||||||
/>
|
}}
|
||||||
|
lang={BLOG.lang.toLowerCase()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
</>)}
|
)
|
||||||
</div>
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Comment
|
export default Comment
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const CommonHead = ({ meta }) => {
|
|||||||
const title = meta?.title || BLOG.title
|
const title = meta?.title || BLOG.title
|
||||||
const description = meta?.description || BLOG.description
|
const description = meta?.description || BLOG.description
|
||||||
const type = meta?.type || 'website'
|
const type = meta?.type || 'website'
|
||||||
const keywords = meta?.tags || BLOG.seo.keywords
|
const keywords = meta?.tags || BLOG.keywords
|
||||||
|
|
||||||
return <Head>
|
return <Head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ import { useEffect, useState } from 'react'
|
|||||||
import { loadUserThemeFromCookies, saveTheme, useGlobal } from '@/lib/global'
|
import { loadUserThemeFromCookies, saveTheme, useGlobal } from '@/lib/global'
|
||||||
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons'
|
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
export default function FloatDarkModeButton () {
|
export default function FloatDarkModeButton () {
|
||||||
|
if (!BLOG.widget?.showDarkMode) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
@@ -13,9 +17,10 @@ export default function FloatDarkModeButton () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
scrollListener()
|
||||||
document.addEventListener('scroll', scrollListener)
|
document.addEventListener('scroll', scrollListener)
|
||||||
return () => document.removeEventListener('scroll', scrollListener)
|
return () => document.removeEventListener('scroll', scrollListener)
|
||||||
})
|
}, [show])
|
||||||
|
|
||||||
const { changeTheme } = useGlobal()
|
const { changeTheme } = useGlobal()
|
||||||
const userTheme = loadUserThemeFromCookies()
|
const userTheme = loadUserThemeFromCookies()
|
||||||
@@ -31,11 +36,12 @@ export default function FloatDarkModeButton () {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
id='float-dark-mode-button'
|
||||||
onClick={handleChangeDarkMode}
|
onClick={handleChangeDarkMode}
|
||||||
className={
|
className={
|
||||||
(show ? '' : 'hidden lg:block') +
|
(show ? '' : ' hidden ') +
|
||||||
' animate__fadeInRight px-3.5 py-3 animate__animated animate__faster shadow-card fixed right-3 bottom-24 z-10 duration-200 text-xs cursor-pointer rounded-xl' +
|
' animate__fadeInRight animate__animated animate__faster fixed right-1 bottom-28 z-10 duration-500 text-xs cursor-pointer ' +
|
||||||
' text-black shadow-card dark:border-gray-500 glassmorphism dark:bg-gray-700 dark:text-gray-200'
|
' text-black dark:border-gray-500 flex justify-center items-center w-8 h-8 glassmorphism dark:bg-gray-700 dark:text-gray-200'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function Header () {
|
|||||||
if (!typed && window && document.getElementById('typed')) {
|
if (!typed && window && document.getElementById('typed')) {
|
||||||
changeType(
|
changeType(
|
||||||
new Typed('#typed', {
|
new Typed('#typed', {
|
||||||
strings: BLOG.headerStrings,
|
strings: BLOG.home.homeBannerStrings,
|
||||||
typeSpeed: 200,
|
typeSpeed: 200,
|
||||||
backSpeed: 100,
|
backSpeed: 100,
|
||||||
backDelay: 400,
|
backDelay: 400,
|
||||||
@@ -96,10 +96,10 @@ export default function Header () {
|
|||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
id="header"
|
id="header"
|
||||||
className="duration-500 md:bg-fixed w-full bg-cover bg-center lg:-mt-14 h-screen bg-black"
|
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${BLOG.bannerImage}")`
|
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${BLOG.home.homeBannerImage}")`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute flex h-full items-center lg:-mt-14 justify-center w-full text-4xl md:text-7xl text-white">
|
<div className="absolute flex h-full items-center lg:-mt-14 justify-center w-full text-4xl md:text-7xl text-white">
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import BLOG from '@/blog.config'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import SocialButton from './SocialButton'
|
||||||
|
|
||||||
const InfoCard = ({ postCount }) => {
|
const InfoCard = ({ postCount }) => {
|
||||||
return <>
|
return <>
|
||||||
<div className='flex flex-col items-center justify-center cursor-pointer' onClick={ () => { Router.push('/') }}>
|
<div className='flex flex-col items-center justify-center '>
|
||||||
<div className='hover:rotate-45 hover:scale-125 transform duration-200'>
|
<div className='hover:rotate-45 hover:scale-125 transform duration-200 cursor-pointer' onClick={ () => { Router.push('/about') }}>
|
||||||
<Image
|
<Image
|
||||||
alt={BLOG.title}
|
alt={BLOG.title}
|
||||||
width={120}
|
width={120}
|
||||||
@@ -17,6 +18,8 @@ const InfoCard = ({ postCount }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-3xl font-serif dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.title}</div>
|
<div className='text-3xl font-serif dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.title}</div>
|
||||||
|
<div className='font-light dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.description}</div>
|
||||||
|
<SocialButton/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { faArrowDown } from '@fortawesome/free-solid-svg-icons'
|
import { faArrowDown } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
@@ -13,6 +14,9 @@ import smoothscroll from 'smoothscroll-polyfill'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const JumpToBottomButton = ({ targetRef, showPercent = false }) => {
|
const JumpToBottomButton = ({ targetRef, showPercent = false }) => {
|
||||||
|
if (!BLOG.widget?.showToBottom) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
@@ -36,15 +40,13 @@ const JumpToBottomButton = ({ targetRef, showPercent = false }) => {
|
|||||||
return () => document.removeEventListener('scroll', scrollListener)
|
return () => document.removeEventListener('scroll', scrollListener)
|
||||||
}, [show])
|
}, [show])
|
||||||
|
|
||||||
return (<div id='jump-to-top' className='right-3 fixed flex bottom-36 duration-500 z-20'>
|
return (<div id='jump-to-top' className='right-1 fixed flex bottom-36 z-20'>
|
||||||
<div onClick={() => window.scrollTo({ top: targetRef.current.clientHeight, behavior: 'smooth' })}
|
<div onClick={() => window.scrollTo({ top: targetRef.current.clientHeight, behavior: 'smooth' })}
|
||||||
className={(show ? '' : 'hidden') + ' animate__fadeInRight animate__animated animate__faster shadow-card rounded-xl glassmorphism py-3 cursor-pointer '}>
|
className={(show ? '' : 'hidden') + ' animate__fadeInRight duration-500 animate__animated animate__faster glassmorphism flex justify-center items-center w-8 h-8 cursor-pointer '}>
|
||||||
<div className='text-center'>
|
<div className='dark:text-gray-200 transform hover:scale-150 text-xs duration-200' title={locale.POST.TOP} >
|
||||||
<div className='w-10 dark:text-gray-200 transform hover:scale-150 duration-200 text-xs' title={locale.POST.TOP} >
|
|
||||||
<FontAwesomeIcon icon={faArrowDown} />
|
<FontAwesomeIcon icon={faArrowDown} />
|
||||||
</div>
|
</div>
|
||||||
{showPercent && (<div className='w-10 text-xs dark:text-gray-200'>{percent}</div>)}
|
{showPercent && (<div className='w-10 text-xs dark:text-gray-200'>{percent}</div>)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useGlobal } from '@/lib/global'
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faArrowUp } from '@fortawesome/free-solid-svg-icons'
|
import { faArrowUp } from '@fortawesome/free-solid-svg-icons'
|
||||||
import smoothscroll from 'smoothscroll-polyfill'
|
import smoothscroll from 'smoothscroll-polyfill'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到网页顶部
|
* 跳转到网页顶部
|
||||||
@@ -13,6 +14,9 @@ import smoothscroll from 'smoothscroll-polyfill'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const JumpToTopButton = ({ targetRef, showPercent = false }) => {
|
const JumpToTopButton = ({ targetRef, showPercent = false }) => {
|
||||||
|
if (!BLOG.widget?.showToTop) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
@@ -37,15 +41,13 @@ const JumpToTopButton = ({ targetRef, showPercent = false }) => {
|
|||||||
return () => document.removeEventListener('scroll', scrollListener)
|
return () => document.removeEventListener('scroll', scrollListener)
|
||||||
}, [show])
|
}, [show])
|
||||||
|
|
||||||
return (<div id='jump-to-top' className='right-3 fixed flex bottom-48 duration-500 z-20'>
|
return (<div id='jump-to-top' className='right-1 fixed flex bottom-44 z-20'>
|
||||||
<div onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
<div onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||||
className={(show ? '' : 'hidden') + ' animate__fadeInRight animate__animated animate__faster shadow-card rounded-xl glassmorphism py-3 cursor-pointer '}>
|
className={(show ? '' : 'hidden') + ' animate__fadeInRight duration-500 animate__animated animate__faster flex justify-center items-center w-8 h-8 glassmorphism cursor-pointer '}>
|
||||||
<div className='text-center'>
|
<div className='dark:text-gray-200 transform hover:scale-150 text-xs duration-200' title={locale.POST.TOP} >
|
||||||
<div className='w-10 dark:text-gray-200 transform hover:scale-150 duration-200 text-xs' title={locale.POST.TOP} >
|
|
||||||
<FontAwesomeIcon icon={faArrowUp} />
|
<FontAwesomeIcon icon={faArrowUp} />
|
||||||
</div>
|
</div>
|
||||||
{showPercent && (<div className='w-10 text-xs dark:text-gray-200'>{percent}</div>)}
|
{showPercent && (<div className='w-10 text-xs dark:text-gray-200'>{percent}</div>)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const LatestPostsGroup = ({ posts, sliceCount = 5 }) => {
|
|||||||
<div className={ (selected ? 'text-white bg-gray-600 ' : 'text-gray-500 dark:text-gray-400 ') + ' text-xs py-1.5 flex overflow-x-hidden whitespace-nowrap hover:bg-gray-500 px-3 w-full ' +
|
<div className={ (selected ? 'text-white bg-gray-600 ' : 'text-gray-500 dark:text-gray-400 ') + ' text-xs py-1.5 flex overflow-x-hidden whitespace-nowrap hover:bg-gray-500 px-3 w-full ' +
|
||||||
'hover:text-white dark:hover:text-white cursor-pointer' }>
|
'hover:text-white dark:hover:text-white cursor-pointer' }>
|
||||||
<FontAwesomeIcon icon={faFileAlt} className='mr-2'/>
|
<FontAwesomeIcon icon={faFileAlt} className='mr-2'/>
|
||||||
{post.title}
|
<div className='truncate'>{post.title}</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
import { useEffect } from 'react'
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
let hasLoad = false
|
let hasLoad = false
|
||||||
export default function Live2D () {
|
export default function Live2D () {
|
||||||
useEffect(() => {
|
if (BLOG.widget?.showPet && typeof window !== 'undefined' && !hasLoad) {
|
||||||
if (window && !hasLoad) {
|
initLive2D()
|
||||||
initLive2D()
|
hasLoad = true
|
||||||
hasLoad = true
|
}
|
||||||
}
|
|
||||||
})
|
return <div className='fixed right-0 bottom-0 hidden md:block lg:mr-24 2xl:mr-40 z-20'>
|
||||||
return <div className='fixed left-0 bottom-0 hidden md:block lg:ml-24 2xl:ml-36'>
|
|
||||||
<canvas id="live2d"className='animate__slideInLeft animate__animated' width="280" height="250"></canvas>
|
<canvas id="live2d"className='animate__slideInLeft animate__animated' width="280" height="250"></canvas>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -22,7 +21,7 @@ function initLive2D () {
|
|||||||
loadExternalResource('https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/live2d.min.js', 'js')
|
loadExternalResource('https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/live2d.min.js', 'js')
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
// https://github.com/xiazeyu/live2d-widget-models
|
// https://github.com/xiazeyu/live2d-widget-models
|
||||||
loadlive2d('live2d', 'https://raw.githubusercontent.com/xiazeyu/live2d-widget-models/master/packages/live2d-widget-model-wanko/assets/wanko.model.json')
|
loadlive2d('live2d', BLOG.widget.petLink)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Link from 'next/link'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faArchive, faHome, faInfoCircle } from '@fortawesome/free-solid-svg-icons'
|
import { faArchive, faHome, faInfoCircle, faTag, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||||
import BLOG from 'blog.config'
|
import BLOG from 'blog.config'
|
||||||
|
|
||||||
const MenuButtonGroup = ({ allowCollapse = false }) => {
|
const MenuButtonGroup = ({ allowCollapse = false }) => {
|
||||||
@@ -11,19 +11,12 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const links = [
|
const links = [
|
||||||
{ id: 0, icon: faHome, name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
{ id: 0, icon: faHome, name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||||
{ id: 1, icon: faArchive, name: locale.NAV.ARCHIVE, to: '/archive', show: BLOG.showArchive },
|
{ id: 1, icon: faArchive, name: locale.NAV.ARCHIVE, to: '/archive', show: BLOG.menu.showArchive },
|
||||||
{ id: 2, icon: faInfoCircle, name: locale.NAV.ABOUT, to: '/about', show: BLOG.showAbout }
|
{ id: 2, icon: faThList, name: locale.COMMON.CATEGORY, to: '/category', show: BLOG.menu.showCategory },
|
||||||
// { id: 7, icon: 'faGithub', name: 'Github', to: 'https://github.com/tangly1024', show: true },
|
{ id: 3, icon: faTag, name: locale.COMMON.TAGS, to: '/tag', show: BLOG.menu.showTag },
|
||||||
// { id: 5, icon: 'faWeibo', name: '微博', to: 'https://weibo.com/tangly1024', show: true },
|
{ id: 4, icon: faInfoCircle, name: locale.NAV.ABOUT, to: '/about', show: BLOG.menu.showAbout }
|
||||||
// { id: 4, icon: 'faEnvelope', name: locale.NAV.MAIL, to: 'mailto:tlyong1992@hotmail.com', show: true }
|
|
||||||
// { id: 2, icon: 'faRssSquare', name: locale.NAV.RSS, to: '/feed', show: true },
|
|
||||||
// { id: 3, icon: 'faCompass', name: '发现', to: 'https://search.tangly1024.com/', show: true }
|
|
||||||
// { id: 6, icon: 'faMapMarker', name: 'Fuzhou', to: '#', show: true },
|
|
||||||
// { id: 8, icon: 'faTwitter', name: 'Twitter', to: 'https://twitter.com/troy1024_1', show: true },
|
|
||||||
// { id: 9, icon: 'faTelegram', name: 'Telegram', to: 'https://t.me/tangly_1024', show: true }
|
|
||||||
]
|
]
|
||||||
return <nav id='nav'>
|
return <nav id='nav' className='leading-8 text-gray-500 dark:text-gray-400 '>
|
||||||
<div className='leading-8 text-gray-500 dark:text-gray-400 '>
|
|
||||||
{links.map(link => {
|
{links.map(link => {
|
||||||
if (link.show) {
|
if (link.show) {
|
||||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||||
@@ -40,7 +33,6 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
|
|||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>
|
</nav>
|
||||||
</nav>
|
|
||||||
}
|
}
|
||||||
export default MenuButtonGroup
|
export default MenuButtonGroup
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const Progress = ({ targetRef }) => {
|
const Progress = ({ targetRef, showPercent = true }) => {
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
if (targetRef?.current) {
|
if (targetRef?.current) {
|
||||||
@@ -14,6 +14,7 @@ const Progress = ({ targetRef }) => {
|
|||||||
const fullHeight = clientHeight - window.outerHeight
|
const fullHeight = clientHeight - window.outerHeight
|
||||||
let per = parseFloat(((scrollY / fullHeight * 100)).toFixed(0))
|
let per = parseFloat(((scrollY / fullHeight * 100)).toFixed(0))
|
||||||
if (per > 100) per = 100
|
if (per > 100) per = 100
|
||||||
|
if (per < 0) per = 0
|
||||||
changePercent(per)
|
changePercent(per)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,9 +25,10 @@ const Progress = ({ targetRef }) => {
|
|||||||
}, [percent])
|
}, [percent])
|
||||||
|
|
||||||
return (<div className='h-4 w-full shadow-2xl bg-gray-400'>
|
return (<div className='h-4 w-full shadow-2xl bg-gray-400'>
|
||||||
|
<div className='h-4 bg-gray-600 duration-200' style={{ width: `${percent}%` }}>
|
||||||
<div className='h-4 bg-gray-600 duration-200' style={{ width: `${percent}%` }}><div className='text-right text-white text-xs'>{percent}%</div></div>
|
{showPercent && <div className='text-right text-white text-xs'>{percent}%</div>}
|
||||||
</div>)
|
</div>
|
||||||
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Progress
|
export default Progress
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 展示文章推荐
|
* 展示文章推荐
|
||||||
*/
|
*/
|
||||||
const RecommendPosts = ({ recommendPosts }) => {
|
const RecommendPosts = ({ recommendPosts }) => {
|
||||||
if (!recommendPosts || recommendPosts.length < 1) {
|
if (!BLOG.widget?.showRelatePosts || !recommendPosts || recommendPosts.length < 1) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
|||||||
35
components/SearchDrawer.js
Normal file
35
components/SearchDrawer.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Router } from 'next/router'
|
||||||
|
import { useImperativeHandle, useRef } from 'react'
|
||||||
|
import SearchInput from './SearchInput'
|
||||||
|
const SearchDrawer = ({ cRef }) => {
|
||||||
|
const searchDrawer = useRef()
|
||||||
|
const searchInputRef = useRef()
|
||||||
|
useImperativeHandle(cRef, () => {
|
||||||
|
return {
|
||||||
|
show: () => {
|
||||||
|
searchDrawer?.current?.classList?.remove('hidden')
|
||||||
|
searchInputRef?.current?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const hidden = () => {
|
||||||
|
searchDrawer?.current?.classList?.add('hidden')
|
||||||
|
}
|
||||||
|
Router.events.on('routeChangeComplete', (...args) => {
|
||||||
|
hidden()
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<div id='search-drawer-wrapper' ref={searchDrawer} className='hidden'>
|
||||||
|
<div className='flex absolute px-5 w-full h-full left-0 top-14 z-50 justify-center'>
|
||||||
|
<div className='md:max-w-3xl w-full mx-auto'>
|
||||||
|
<SearchInput cRef={searchInputRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 背景蒙版 */}
|
||||||
|
<div id='search-drawer-background' onClick={hidden} className='animate__animated animate__faster animate__fadeIn fixed glassmorphism top-0 left-0 z-30 w-full h-full' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchDrawer
|
||||||
@@ -17,6 +17,9 @@ import {
|
|||||||
import { faLink } from '@fortawesome/free-solid-svg-icons'
|
import { faLink } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
const ShareBar = ({ post }) => {
|
const ShareBar = ({ post }) => {
|
||||||
|
if (!BLOG.widget?.showShareBar) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const shareUrl = BLOG.link + router.asPath
|
const shareUrl = BLOG.link + router.asPath
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import Toc from '@/components/Toc'
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Analytics from './Analytics'
|
import Analytics from './Analytics'
|
||||||
|
import Tabs from '@/components/Tabs'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 侧边平铺
|
* 侧边平铺
|
||||||
@@ -22,37 +24,33 @@ const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, curren
|
|||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const showToc = post && post.toc && post.toc.length > 1
|
const showToc = post && post.toc && post.toc.length > 1
|
||||||
const postCount = posts?.length || 0
|
const postCount = posts?.length || 0
|
||||||
return <>
|
return <aside id='left' className='hidden lg:block flex-col w-60 mr-4'>
|
||||||
|
|
||||||
<section className={(!post ? 'sticky top-8 ' : ' ') + ' w-60'}>
|
<section className='sticky top-8 w-60'>
|
||||||
<section className='shadow hidden lg:block mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200 py-6'>
|
|
||||||
<InfoCard postCount={postCount} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* 菜单 */}
|
{/* 菜单 */}
|
||||||
<section className='shadow hidden lg:block mb-5 py-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
<section className='shadow hidden lg:block mb-5 py-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
||||||
<MenuButtonGroup allowCollapse={true} />
|
<MenuButtonGroup allowCollapse={true} />
|
||||||
<div className='px-5 pt-2'>
|
{BLOG.menu.showSearch && <div className='px-5 pt-2'>
|
||||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
||||||
</div>
|
</div>}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* 统计 */}
|
<Tabs>
|
||||||
<section className='shadow hidden lg:block mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200 py-4'>
|
{showToc && (
|
||||||
<Analytics postCount={postCount}/>
|
<div key={locale.COMMON.TABLE_OF_CONTENTS} className='dark:text-gray-400 text-gray-600 bg-white dark:bg-gray-800 duration-200'>
|
||||||
</section>
|
<Toc toc={post.toc} targetRef={targetRef} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div key={locale.NAV.ABOUT} className='mb-5 bg-white dark:bg-gray-800 duration-200 py-6'>
|
||||||
|
<InfoCard postCount={postCount} />
|
||||||
|
<Analytics postCount={postCount}/>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{showToc && (
|
</aside>
|
||||||
<section className='shadow sticky top-8 pb-20 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
|
||||||
<div className='border-b text-center text-2xl bg-white text-black dark:border-gray-700 dark:bg-gray-700 dark:text-white py-6 px-6'>
|
|
||||||
{locale.COMMON.TABLE_OF_CONTENTS}
|
|
||||||
</div>
|
|
||||||
<Toc toc={post.toc} targetRef={targetRef} />
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
export default SideAreaLeft
|
export default SideAreaLeft
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
||||||
import TagGroups from '@/components/TagGroups'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { faAngleDoubleRight, faArchive, faTags, faThList } from '@fortawesome/free-solid-svg-icons'
|
import { faAngleDoubleRight, faAngleRight, faArchive, faTags, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import CategoryGroup from '@/components/CategoryGroup'
|
import CategoryGroup from './CategoryGroup'
|
||||||
|
import TagGroups from './TagGroups'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 侧边平铺
|
* 侧边平铺
|
||||||
@@ -31,23 +32,12 @@ const SideAreaRight = ({
|
|||||||
targetRef
|
targetRef
|
||||||
}) => {
|
}) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
// const postCount = posts?.length || 0
|
const { widget } = BLOG
|
||||||
// const showToc = post && post.toc && post.toc.length > 1
|
if (!widget?.showCategoryList && !widget.showTagList && !widget.showLatestPost) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (<aside id='right' className='hidden 2xl:block flex-col w-60 ml-4'>
|
||||||
<>
|
|
||||||
|
|
||||||
{/* <section className='hidden lg:block mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200 py-8 '>
|
|
||||||
<InfoCard postCount={postCount} />
|
|
||||||
</section> */}
|
|
||||||
|
|
||||||
{/* 菜单 */}
|
|
||||||
{/* <section className='hidden lg:block mb-5 py-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
|
||||||
<MenuButtonGroup allowCollapse={true} />
|
|
||||||
<div className='px-5 pt-2'>
|
|
||||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
|
||||||
</div>
|
|
||||||
</section> */}
|
|
||||||
|
|
||||||
<section className="shadow mb-5 py-4 px-2 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
<section className="shadow mb-5 py-4 px-2 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
||||||
{/* 展示广告 */}
|
{/* 展示广告 */}
|
||||||
@@ -62,26 +52,16 @@ const SideAreaRight = ({
|
|||||||
></ins>
|
></ins>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* <Analytics postCount={postCount}/> */}
|
|
||||||
|
|
||||||
<div className="sticky top-8">
|
<div className="sticky top-8">
|
||||||
{/* {showToc && (
|
|
||||||
<section className='pb-10 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
|
||||||
<div className='border-b text-center text-2xl bg-white text-black dark:border-gray-700 dark:bg-gray-700 dark:text-white py-6 px-6'>
|
|
||||||
{locale.COMMON.TABLE_OF_CONTENTS}
|
|
||||||
</div>
|
|
||||||
<Toc toc={post.toc} targetRef={targetRef} />
|
|
||||||
</section>
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
{/* 分类 */}
|
{/* 分类 */}
|
||||||
{categories && (
|
{widget?.showCategoryList && categories && (
|
||||||
<section className='shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
<section className='shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
||||||
<div className='text-sm px-5 mb-2 flex flex-nowrap justify-between font-light'>
|
<div className='text-sm px-5 mb-2 flex flex-nowrap justify-between font-light'>
|
||||||
<div className='pb-1 text-gray-600 dark:text-gray-300'><FontAwesomeIcon icon={faThList} className='mr-2' />{locale.COMMON.CATEGORY}</div>
|
<div className='pb-1 text-gray-600 dark:text-gray-300'><FontAwesomeIcon icon={faThList} className='mr-2' />{locale.COMMON.CATEGORY}</div>
|
||||||
<Link href='/category' passHref>
|
<Link href='/category' passHref>
|
||||||
<a className='text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
|
<a className='text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
|
||||||
{locale.COMMON.MORE} <FontAwesomeIcon icon={faAngleDoubleRight} />
|
{locale.COMMON.MORE} <FontAwesomeIcon icon={faAngleRight} />
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,7 +70,7 @@ const SideAreaRight = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 最新文章 */}
|
{/* 最新文章 */}
|
||||||
{posts && (
|
{widget?.showLatestPost && posts && (
|
||||||
<section className="shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
<section className="shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
||||||
<div className="text-sm pb-2 px-5 flex flex-nowrap justify-between">
|
<div className="text-sm pb-2 px-5 flex flex-nowrap justify-between">
|
||||||
<div className="font-light text-gray-600 dark:text-gray-300">
|
<div className="font-light text-gray-600 dark:text-gray-300">
|
||||||
@@ -103,12 +83,7 @@ const SideAreaRight = ({
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* <section className="shadow py-4 px-5 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
{widget?.showTagList && tags && (
|
||||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch}/>
|
|
||||||
</section> */}
|
|
||||||
|
|
||||||
{/* 标签云 */}
|
|
||||||
{tags && (
|
|
||||||
<section className="shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
<section className="shadow py-4 mb-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
||||||
<div className="text-sm pb-1 px-5 flex flex-nowrap justify-between font-light dark:text-gray-200">
|
<div className="text-sm pb-1 px-5 flex flex-nowrap justify-between font-light dark:text-gray-200">
|
||||||
<div className="text-gray-600 dark:text-gray-200">
|
<div className="text-gray-600 dark:text-gray-200">
|
||||||
@@ -129,7 +104,7 @@ const SideAreaRight = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default SideAreaRight
|
export default SideAreaRight
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
import { faGithub, faTelegram, faTwitter, faWeibo } from '@fortawesome/free-brands-svg-icons'
|
import { faGithub, faTelegram, faTwitter, faWeibo } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
import { faEnvelope, faRss } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
@@ -8,19 +10,25 @@ import React from 'react'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const SocialButton = () => {
|
const SocialButton = () => {
|
||||||
return <div className='w-52'>
|
return <div className='w-52 justify-center flex-wrap flex'>
|
||||||
<div className='space-x-3 text-xl text-gray-600 dark:text-gray-400 px-6'>
|
<div className='space-x-3 text-xl text-gray-600 dark:text-gray-400 '>
|
||||||
<a target='_blank' rel='noreferrer' title={'github'} href={'https://github.com/tangly1024'} >
|
{BLOG.socialLink.github && <a target='_blank' rel='noreferrer' title={'github'} href={BLOG.socialLink.github} >
|
||||||
<FontAwesomeIcon icon={faGithub} className='transform hover:scale-125 duration-150'/>
|
<FontAwesomeIcon icon={faGithub} className='transform hover:scale-125 duration-150'/>
|
||||||
</a>
|
</a>}
|
||||||
<a target='_blank' rel='noreferrer' title={'twitter'} href={'https://twitter.com/troy1024_1'} >
|
{BLOG.socialLink.twitter && <a target='_blank' rel='noreferrer' title={'twitter'} href={BLOG.socialLink.twitter} >
|
||||||
<FontAwesomeIcon icon={faTwitter} className='transform hover:scale-125 duration-150'/>
|
<FontAwesomeIcon icon={faTwitter} className='transform hover:scale-125 duration-150'/>
|
||||||
</a>
|
</a>}
|
||||||
<a target='_blank' rel='noreferrer' href={'https://t.me/tangly_1024'} title={'telegram'} >
|
{BLOG.socialLink.telegram && <a target='_blank' rel='noreferrer' href={BLOG.socialLink.telegram} title={'telegram'} >
|
||||||
<FontAwesomeIcon icon={faTelegram} className='transform hover:scale-125 duration-150'/>
|
<FontAwesomeIcon icon={faTelegram} className='transform hover:scale-125 duration-150'/>
|
||||||
</a>
|
</a>}
|
||||||
<a target='_blank' rel='noreferrer' title={'weibo'} href={'https://weibo.com/tangly1024'} >
|
{BLOG.socialLink.weibo && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.socialLink.weibo} >
|
||||||
<FontAwesomeIcon icon={faWeibo} className='transform hover:scale-125 duration-150'/>
|
<FontAwesomeIcon icon={faWeibo} className='transform hover:scale-125 duration-150'/>
|
||||||
|
</a>}
|
||||||
|
{BLOG.email && <a target='_blank' rel='noreferrer' title={'email'} href={`mailto:${BLOG.email}`} >
|
||||||
|
<FontAwesomeIcon icon={faEnvelope} className='transform hover:scale-125 duration-150'/>
|
||||||
|
</a>}
|
||||||
|
<a target='_blank' rel='noreferrer' title={'RSS'} href={'/feed'} >
|
||||||
|
<FontAwesomeIcon icon={faRss} className='transform hover:scale-125 duration-150'/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
51
components/Tabs.js
Normal file
51
components/Tabs.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs切换标签
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const Tabs = ({ children }) => {
|
||||||
|
if (!children) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = children.length
|
||||||
|
children.forEach(e => {
|
||||||
|
if (!e) {
|
||||||
|
count--
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (count === 1) {
|
||||||
|
return <section className='shadow'>
|
||||||
|
{children}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
const [currentTab, setCurrentTab] = useState(0)
|
||||||
|
function tabClickHandle (i) {
|
||||||
|
setCurrentTab(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section >
|
||||||
|
{<div className='shadow hidden lg:block mb-5 py-4 px-5 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
||||||
|
<ul className='flex justify-center space-x-5 pb-4 dark:text-gray-400 text-gray-600'>
|
||||||
|
{children.map((item, index) => {
|
||||||
|
return <li key={index} className={currentTab === index ? 'font-black border-b' : 'font-extralight cursor-pointer'} onClick={() => { tabClickHandle(index) }}>
|
||||||
|
{item?.key}
|
||||||
|
</li>
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
{children.map((item, index) => {
|
||||||
|
return <section key={index} className={`${currentTab === index ? 'block' : 'hidden'}`}>
|
||||||
|
{item}
|
||||||
|
</section>
|
||||||
|
})}
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
</section>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tabs
|
||||||
@@ -7,7 +7,7 @@ import BLOG from '@/blog.config'
|
|||||||
*/
|
*/
|
||||||
const ThirdPartyScript = () => {
|
const ThirdPartyScript = () => {
|
||||||
return (<>
|
return (<>
|
||||||
{BLOG.DaoVoiceId && (<>
|
{BLOG.comment?.DaoVoiceId && (<>
|
||||||
{/* DaoVoice 反馈 */}
|
{/* DaoVoice 反馈 */}
|
||||||
<script async dangerouslySetInnerHTML={{
|
<script async dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
@@ -18,7 +18,7 @@ const ThirdPartyScript = () => {
|
|||||||
<script async dangerouslySetInnerHTML={{
|
<script async dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
daovoice('init', {
|
daovoice('init', {
|
||||||
app_id: "${BLOG.DaoVoiceId}"
|
app_id: "${BLOG.comment.DaoVoiceId}"
|
||||||
});
|
});
|
||||||
daovoice('update');
|
daovoice('update');
|
||||||
`
|
`
|
||||||
@@ -30,10 +30,10 @@ const ThirdPartyScript = () => {
|
|||||||
{BLOG.googleAdsenseId && (<script data-ad-client={BLOG.googleAdsenseId} async
|
{BLOG.googleAdsenseId && (<script data-ad-client={BLOG.googleAdsenseId} async
|
||||||
src='https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'/>)}
|
src='https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'/>)}
|
||||||
|
|
||||||
{BLOG.TidioId && (<>
|
{BLOG.comment?.TidioId && (<>
|
||||||
{/* Tidio在线反馈 */}
|
{/* Tidio在线反馈 */}
|
||||||
<script async
|
<script async
|
||||||
src={`//code.tidio.co/${BLOG.TidioId}.js`}
|
src={`//code.tidio.co/${BLOG.comment.TidioId}.js`}
|
||||||
/>
|
/>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const Toc = ({ toc, targetRef }) => {
|
|||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<Progress targetRef={targetRef}/>
|
<Progress targetRef={targetRef}/>
|
||||||
</div>
|
</div>
|
||||||
<nav className=' dark:text-gray-400 text-gray-600 bg-white dark:bg-gray-800 overflow-y-auto scroll-hidden p-6'>
|
<nav className=' overflow-y-auto scroll-hidden'>
|
||||||
{toc.map((tocItem) => {
|
{toc.map((tocItem) => {
|
||||||
const id = uuidToId(tocItem.id)
|
const id = uuidToId(tocItem.id)
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ const TocDrawer = ({ post, cRef, targetRef }) => {
|
|||||||
<div className='text-xl font-bold text-center text-black dark:text-white bg-white dark:bg-gray-600 py-2 px-6'>
|
<div className='text-xl font-bold text-center text-black dark:text-white bg-white dark:bg-gray-600 py-2 px-6'>
|
||||||
{locale.COMMON.TABLE_OF_CONTENTS}
|
{locale.COMMON.TABLE_OF_CONTENTS}
|
||||||
</div>
|
</div>
|
||||||
<Toc toc={post.toc} targetRef={targetRef}/>
|
<div className='p-6 dark:text-gray-400 text-gray-600 bg-white dark:bg-gray-800'>
|
||||||
|
<Toc toc={post.toc} targetRef={targetRef}/>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faListOl } from '@fortawesome/free-solid-svg-icons'
|
import { faListOl } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点击召唤目录抽屉
|
* 点击召唤目录抽屉
|
||||||
@@ -11,6 +12,9 @@ import { faListOl } from '@fortawesome/free-solid-svg-icons'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const TocDrawerButton = (props) => {
|
const TocDrawerButton = (props) => {
|
||||||
|
if (!BLOG.widget?.showToc) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const [show, switchShow] = useState(false)
|
const [show, switchShow] = useState(false)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
@@ -27,10 +31,10 @@ const TocDrawerButton = (props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='toc-drawer-button' className='right-3 fixed bottom-60 duration-500 z-20'>
|
<div id='toc-drawer-button' className='right-1 fixed bottom-52 duration-500 z-20'>
|
||||||
<div onClick={props.onClick} className={(show ? 'animate__fadeInRight' : 'hidden') + ' py-3 px-3.5 animate__animated glassmorphism rounded-xl cursor-pointer shadow-card' }>
|
<div onClick={props.onClick} className={(show ? 'animate__fadeInRight' : 'hidden') + ' animate__animated glassmorphism cursor-pointer' }>
|
||||||
<div className='dark:text-gray-200 text-center transform hover:scale-150 duration-200 text-xs' title={locale.POST.TOP} >
|
<div className='dark:text-gray-200 text-center transform hover:scale-150 duration-200 text-xs flex justify-center items-center w-8 h-8' title={locale.POST.TOP} >
|
||||||
<FontAwesomeIcon icon={faListOl} />
|
<FontAwesomeIcon icon={faListOl}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import SideBarDrawer from '@/components/SideBarDrawer'
|
import SideBarDrawer from '@/components/SideBarDrawer'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { faBars, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useCallback, useEffect, useRef } from 'react'
|
import { useCallback, useEffect, useRef } from 'react'
|
||||||
import Image from 'next/image'
|
import SearchDrawer from './SearchDrawer'
|
||||||
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
|
|
||||||
let windowTop = 0
|
let windowTop = 0
|
||||||
@@ -18,6 +18,7 @@ let windowTop = 0
|
|||||||
const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, autoHide = true }) => {
|
const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, autoHide = true }) => {
|
||||||
const drawer = useRef()
|
const drawer = useRef()
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
const searchDrawer = useRef()
|
||||||
|
|
||||||
const scrollTrigger = useCallback(throttle(() => {
|
const scrollTrigger = useCallback(throttle(() => {
|
||||||
const scrollS = window.scrollY
|
const scrollS = window.scrollY
|
||||||
@@ -30,22 +31,22 @@ const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, au
|
|||||||
nav && nav.classList.replace('-top-16', 'top-0')
|
nav && nav.classList.replace('-top-16', 'top-0')
|
||||||
windowTop = scrollS
|
windowTop = scrollS
|
||||||
}
|
}
|
||||||
}, 200))
|
}, 200), [])
|
||||||
|
|
||||||
// 监听滚动
|
// 监听滚动
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoHide) {
|
if (BLOG.autoCollapsedNavBar) {
|
||||||
scrollTrigger()
|
scrollTrigger()
|
||||||
window.addEventListener('scroll', scrollTrigger)
|
window.addEventListener('scroll', scrollTrigger)
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
autoHide && window.removeEventListener('scroll', scrollTrigger)
|
BLOG.autoCollapsedNavBar && window.removeEventListener('scroll', scrollTrigger)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
return (<div id='top-nav' className='sticky top-0 z-40 block lg:hidden'>
|
||||||
return (<div id='top-nav'>
|
|
||||||
{/* 侧面抽屉 */}
|
{/* 侧面抽屉 */}
|
||||||
<SideBarDrawer post={post} currentTag={currentTag} cRef={drawer} tags={tags} posts={posts} categories={categories} currentCategory={currentCategory}/>
|
<SideBarDrawer post={post} currentTag={currentTag} cRef={drawer} tags={tags} posts={posts} categories={categories} currentCategory={currentCategory}/>
|
||||||
|
<SearchDrawer cRef={searchDrawer}/>
|
||||||
|
|
||||||
{/* 导航栏 */}
|
{/* 导航栏 */}
|
||||||
<div id='sticky-nav' className='flex animate__animated animate__fadeIn fixed lg:relative w-full top-0 z-20 transform duration-500'>
|
<div id='sticky-nav' className='flex animate__animated animate__fadeIn fixed lg:relative w-full top-0 z-20 transform duration-500'>
|
||||||
@@ -53,16 +54,10 @@ const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, au
|
|||||||
{/* 左侧LOGO 标题 */}
|
{/* 左侧LOGO 标题 */}
|
||||||
<div className='flex flex-none flex-grow-0'>
|
<div className='flex flex-none flex-grow-0'>
|
||||||
<div onClick={() => { drawer.current.handleSwitchSideDrawerVisible() }}
|
<div onClick={() => { drawer.current.handleSwitchSideDrawerVisible() }}
|
||||||
className='w-8 cursor-pointer dark:text-gray-300 block lg:hidden'>
|
className='w-8 cursor-pointer dark:text-gray-300'>
|
||||||
<FontAwesomeIcon icon={faBars} size={'lg'}/>
|
<FontAwesomeIcon icon={faBars} size={'lg'}/>
|
||||||
</div>
|
</div>
|
||||||
<div className='relative w-10 hidden lg:block' ><Image
|
|
||||||
alt={BLOG.title}
|
|
||||||
layout='fill'
|
|
||||||
loading='lazy'
|
|
||||||
src='/favicon.svg'
|
|
||||||
className='border-black'
|
|
||||||
/></div>
|
|
||||||
<Link href='/' passHref>
|
<Link href='/' passHref>
|
||||||
<a>
|
<a>
|
||||||
<h1 className='cursor-pointer ml-2 w-full hover:scale-105 duration-200 transform font-serif dark:text-gray-200 whitespace-nowrap overflow-x-hidden'>{ BLOG.title }</h1>
|
<h1 className='cursor-pointer ml-2 w-full hover:scale-105 duration-200 transform font-serif dark:text-gray-200 whitespace-nowrap overflow-x-hidden'>{ BLOG.title }</h1>
|
||||||
@@ -72,15 +67,9 @@ const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, au
|
|||||||
|
|
||||||
{/* 右侧功能 */}
|
{/* 右侧功能 */}
|
||||||
<div className='mr-1 flex flex-nowrap flex-grow justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
<div className='mr-1 flex flex-nowrap flex-grow justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
||||||
<Link href='/'>
|
<div className="cursor-pointer block lg:hidden" onClick={() => { searchDrawer?.current?.show() }}>
|
||||||
<a>{locale.NAV.INDEX}</a>
|
<FontAwesomeIcon icon={faSearch} className="mr-2" />{locale.NAV.SEARCH}
|
||||||
</Link>
|
</div>
|
||||||
<Link href='/archive'>
|
|
||||||
<a>{locale.NAV.ARCHIVE}</a>
|
|
||||||
</Link>
|
|
||||||
<Link href='/about'>
|
|
||||||
<a>{locale.NAV.ABOUT}</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,10 +54,8 @@ const BaseLayout = ({
|
|||||||
<>{headerSlot}</>
|
<>{headerSlot}</>
|
||||||
|
|
||||||
<main id='wrapper' className='flex justify-center flex-1 mx-auto md:pt-14 pb-12'>
|
<main id='wrapper' className='flex justify-center flex-1 mx-auto md:pt-14 pb-12'>
|
||||||
<aside id='left' className='hidden lg:block flex-col w-60 mr-4'>
|
<SideAreaLeft targetRef={targetRef} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
||||||
<SideAreaLeft targetRef={targetRef} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
<section id='center' className='flex-grow mt-14 md:mt-0 max-w-5xl min-h-screen' ref={targetRef}>
|
||||||
</aside>
|
|
||||||
<section id='center' className='flex-grow mt-14 md:mt-0 max-w-4xl min-h-screen' ref={targetRef}>
|
|
||||||
{onLoading
|
{onLoading
|
||||||
? <LoadingCover/>
|
? <LoadingCover/>
|
||||||
: <>
|
: <>
|
||||||
@@ -65,9 +63,7 @@ const BaseLayout = ({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
<aside id='right' className='hidden 2xl:block flex-col w-60 ml-4'>
|
<SideAreaRight targetRef={targetRef} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
||||||
<SideAreaRight targetRef={targetRef} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
|
||||||
</aside>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Footer title={meta.title}/>
|
<Footer title={meta.title}/>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export async function getAllPosts ({ notionPageData, from, includePage = false }
|
|||||||
for (let i = 0; i < pageIds.length; i++) {
|
for (let i = 0; i < pageIds.length; i++) {
|
||||||
const id = pageIds[i]
|
const id = pageIds[i]
|
||||||
const properties = (await getPageProperties(id, pageBlock, schema)) || null
|
const properties = (await getPageProperties(id, pageBlock, schema)) || null
|
||||||
|
properties.slug = properties.slug ?? properties.id
|
||||||
properties.createdTime = new Date(pageBlock[id].value?.created_time).toString()
|
properties.createdTime = new Date(pageBlock[id].value?.created_time).toString()
|
||||||
properties.lastEditedTime = new Date(pageBlock[id].value?.last_edited_time).toString()
|
properties.lastEditedTime = new Date(pageBlock[id].value?.last_edited_time).toString()
|
||||||
properties.fullWidth = pageBlock[id].value?.format?.page_full_width ?? false
|
properties.fullWidth = pageBlock[id].value?.format?.page_full_width ?? false
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export function generateRss (posts) {
|
|||||||
posts.forEach(post => {
|
posts.forEach(post => {
|
||||||
feed.addItem({
|
feed.addItem({
|
||||||
title: post.title,
|
title: post.title,
|
||||||
id: `${BLOG.link}/${post.slug}`,
|
id: `${BLOG.link}/article/${post.slug}`,
|
||||||
link: `${BLOG.link}/${post.slug}`,
|
link: `${BLOG.link}/article/${post.slug}`,
|
||||||
description: post.summary,
|
description: post.summary,
|
||||||
date: new Date(post?.date?.start_date || post.createdTime)
|
date: new Date(post?.date?.start_date || post.createdTime)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const About = ({ post, blockMap, tags, prev, next, allPosts, categories }) => {
|
|||||||
return <Custom404 />
|
return <Custom404 />
|
||||||
}
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
post.title = locale.NAV.ABOUT
|
||||||
const meta = {
|
const meta = {
|
||||||
title: `${locale.NAV.ABOUT} | ${BLOG.title} `,
|
title: `${locale.NAV.ABOUT} | ${BLOG.title} `,
|
||||||
description: post.summary,
|
description: post.summary,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const Index = ({ allPosts, tags, categories }) => {
|
|||||||
<BlogPostArchive key={archiveTitle} posts={archivePosts[archiveTitle]} archiveTitle={archiveTitle}/>
|
<BlogPostArchive key={archiveTitle} posts={archivePosts[archiveTitle]} archiveTitle={archiveTitle}/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{BLOG.showPet && <Live2D/>}
|
<Live2D/>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function getStaticProps () {
|
|||||||
const Index = ({ allPosts, tags, meta, categories }) => {
|
const Index = ({ allPosts, tags, meta, categories }) => {
|
||||||
return (
|
return (
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
headerSlot={<Header />}
|
headerSlot={BLOG.home.showHomeBanner && <Header />}
|
||||||
meta={meta}
|
meta={meta}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
totalPosts={allPosts}
|
totalPosts={allPosts}
|
||||||
|
|||||||
@@ -46,14 +46,12 @@ const Search = ({ allPosts, tags, categories }) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<BaseLayout meta={meta} tags={tags} totalPosts={allPosts} currentSearch={searchKey} categories={categories}>
|
<BaseLayout meta={meta} tags={tags} totalPosts={allPosts} currentSearch={searchKey} categories={categories}>
|
||||||
<div className=''>
|
|
||||||
<StickyBar>
|
<StickyBar>
|
||||||
<div className='p-4 dark:text-gray-200'><FontAwesomeIcon icon={faSearch} className='mr-1'/> 搜索词: {searchKey}</div>
|
<div className='p-4 dark:text-gray-200'><FontAwesomeIcon icon={faSearch} className='mr-1'/> {locale.NAV.SEARCH}: {searchKey}</div>
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
<div className='md:mt-5'>
|
<div className='md:mt-5'>
|
||||||
<BlogPostListScroll posts={filteredPosts} tags={tags} currentSearch={searchKey} />
|
<BlogPostListScroll posts={filteredPosts} tags={tags} currentSearch={searchKey} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,12 +151,12 @@ nav {
|
|||||||
|
|
||||||
.glassmorphism{
|
.glassmorphism{
|
||||||
background: hsla(0, 0%, 100%, .75);
|
background: hsla(0, 0%, 100%, .75);
|
||||||
-webkit-backdrop-filter: blur(5px);
|
-webkit-backdrop-filter: blur(10px);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .glassmorphism{
|
.dark .glassmorphism{
|
||||||
background: rgba(31, 41, 55, .75);
|
background: rgba(31, 41, 55, .75);
|
||||||
-webkit-backdrop-filter: blur(3px);
|
-webkit-backdrop-filter: blur(10px);
|
||||||
backdrop-filter: blur(3px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user