mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-05 15:10:26 +00:00
Merge branch 'main' into pr/emengweb/1007
This commit is contained in:
@@ -3,6 +3,7 @@ import { ArticleLock } from './components/ArticleLock'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Comment from '@/components/Comment'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
@@ -17,10 +18,10 @@ export const LayoutSlug = props => {
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id="notion-article" className="px-2">
|
||||
|
||||
{post && <>
|
||||
<ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post}/>
|
||||
</>}
|
||||
</div>}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
|
||||
export const BlogListPage = props => {
|
||||
const { page = 1, posts, postCount } = props
|
||||
@@ -22,46 +23,8 @@ export const BlogListPage = props => {
|
||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`}>
|
||||
|
||||
<div id="container">
|
||||
{posts?.map(p => (
|
||||
<article key={p.id} className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${p.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{p.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${p.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{p.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{p.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{p.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{p.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${p.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${p?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
{posts?.map(post => (
|
||||
<BlogPostCard key={post.id} post = {post}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import throttle from 'lodash.throttle'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
|
||||
export const BlogListScroll = props => {
|
||||
@@ -35,8 +35,9 @@ export const BlogListScroll = props => {
|
||||
handleGetMore()
|
||||
}
|
||||
}, 500))
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', scrollTrigger)
|
||||
|
||||
return () => {
|
||||
@@ -44,60 +45,22 @@ export const BlogListScroll = props => {
|
||||
}
|
||||
})
|
||||
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
return (
|
||||
<div id="container" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
||||
{postsToShow.map(p => (
|
||||
<article key={p.id} className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${p.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{p.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${p.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{p.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`} ref={targetRef}>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{p.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{p.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{p.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${p.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${p?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
{postsToShow?.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} />
|
||||
))}
|
||||
|
||||
<div
|
||||
onClick={handleGetMore}
|
||||
className="w-full my-4 py-4 text-center cursor-pointer "
|
||||
>
|
||||
{' '}
|
||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
||||
</div>
|
||||
<div
|
||||
onClick={handleGetMore}
|
||||
className="w-full my-4 py-4 text-center cursor-pointer "
|
||||
>
|
||||
{' '}
|
||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
51
themes/example/components/BlogPostCard.js
Normal file
51
themes/example/components/BlogPostCard.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
import Link from 'next/link'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post }) => {
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
return <article className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${post.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {post.date?.start_date || post.createdTime}
|
||||
<TwikooCommentCount post={post} className='pl-1'/>
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${post.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{post.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{post.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${post?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
}
|
||||
|
||||
export default BlogPostCard
|
||||
@@ -1,6 +1,7 @@
|
||||
import TagItemMini from './TagItemMini'
|
||||
import Comment from '@/components/Comment'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import formatDate from '@/lib/formatDate'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
@@ -89,6 +90,11 @@ export default function ArticleDetail(props) {
|
||||
{post && <NotionPage post={post} />}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
</section>
|
||||
|
||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||
{/* 文章内嵌广告 */}
|
||||
<ins className="adsbygoogle"
|
||||
|
||||
@@ -47,7 +47,7 @@ const PaginationSimple = ({ page, showNext }) => {
|
||||
{locale.PAGINATION.NEXT}→
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default PaginationSimple
|
||||
|
||||
@@ -11,6 +11,7 @@ import ArticleAdjacent from './components/ArticleAdjacent'
|
||||
import ArticleCopyright from './components/ArticleCopyright'
|
||||
import ArticleRecommend from './components/ArticleRecommend'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
@@ -68,6 +69,8 @@ export const LayoutSlug = props => {
|
||||
data-ad-slot="3806269138" />
|
||||
</section>
|
||||
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{post.type === 'Post' && <ArticleCopyright {...props} /> }
|
||||
{post.type === 'Post' && <ArticleRecommend {...props} /> }
|
||||
{post.type === 'Post' && <ArticleAdjacent {...props} /> }
|
||||
|
||||
@@ -2,6 +2,7 @@ import BLOG from '@/blog.config'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
/**
|
||||
* 博客列表的文字内容
|
||||
@@ -26,7 +27,7 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
|
||||
{/* 分类 */}
|
||||
<div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 `}
|
||||
>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
@@ -37,6 +38,8 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
|
||||
<TwikooCommentCount className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400' post={post}/>
|
||||
</div>
|
||||
|
||||
{/* 摘要 */}
|
||||
@@ -80,7 +83,7 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Typed from 'typed.js'
|
||||
import CONFIG_HEXO from '../config_hexo'
|
||||
import NavButtonGroup from './NavButtonGroup'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
let wrapperTop = 0
|
||||
let windowTop = 0
|
||||
@@ -17,6 +18,7 @@ const enableAutoScroll = false // 是否开启自动吸附滚动
|
||||
const Header = props => {
|
||||
const [typed, changeType] = useState()
|
||||
const { siteInfo } = props
|
||||
const { locale } = useGlobal()
|
||||
useEffect(() => {
|
||||
updateHeaderHeight()
|
||||
|
||||
@@ -103,7 +105,7 @@ const Header = props => {
|
||||
onClick={() => { window.scrollTo({ top: wrapperTop, behavior: 'smooth' }) }}
|
||||
className="cursor-pointer w-full text-center py-4 text-3xl absolute bottom-10 text-white"
|
||||
>
|
||||
<div class="opacity-70 animate-bounce text-xs">点此继续</div>
|
||||
<div className="opacity-70 animate-bounce text-xs">{locale.COMMON.START_READING}</div>
|
||||
<i className='opacity-70 animate-bounce fas fa-angle-down' />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -25,21 +25,23 @@ const HexoRecentComments = (props) => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card >
|
||||
<div className=" mb-2 px-1 justify-between">
|
||||
<i className="mr-2 fas fas fa-comment" />
|
||||
{locale.COMMON.RECENT_COMMENTS}
|
||||
</div>
|
||||
<Card >
|
||||
<div className=" mb-2 px-1 justify-between">
|
||||
<i className="mr-2 fas fas fa-comment" />
|
||||
{locale.COMMON.RECENT_COMMENTS}
|
||||
</div>
|
||||
|
||||
{onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>}
|
||||
{!onLoading && comments && comments.length === 0 && <div>No Comments</div>}
|
||||
{!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'>
|
||||
<div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} />
|
||||
<div className='dark:text-gray-400 text-gray-400 text-sm text-right cursor-pointer hover:text-red-500 hover:underline pt-1 pr-2'><Link href={{ pathname: comment.url, hash: comment.objectId, query: { target: 'comment' } }}>--{comment.nick}</Link></div>
|
||||
</div>)}
|
||||
{onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>}
|
||||
{!onLoading && comments && comments.length === 0 && <div>No Comments</div>}
|
||||
{!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'>
|
||||
<div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} />
|
||||
<div className='dark:text-gray-400 text-gray-400 text-sm text-right cursor-pointer hover:text-red-500 hover:underline pt-1 pr-2'>
|
||||
<Link href={{ pathname: comment.url, hash: comment.objectId, query: { target: 'comment' } }}>--{comment.nick}</Link>
|
||||
</div>
|
||||
</div>)}
|
||||
|
||||
</Card>
|
||||
);
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default HexoRecentComments
|
||||
|
||||
@@ -47,7 +47,7 @@ const PaginationNumber = ({ page, totalPage }) => {
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function getPageElement(page, currentPage, pagePrefix) {
|
||||
@@ -66,7 +66,7 @@ function getPageElement(page, currentPage, pagePrefix) {
|
||||
{page}
|
||||
|
||||
</Link>)
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function generatePages(pagePrefix, page, currentPage, totalPage) {
|
||||
|
||||
@@ -51,9 +51,15 @@ const LayoutBase = props => {
|
||||
{headerSlot}
|
||||
|
||||
<main id="wrapper" className="flex-1 w-full py-8 md:px-8 lg:px-24 relative">
|
||||
{/* 嵌入区域 */}
|
||||
<div id="container-slot" className={`w-full max-w-6xl ${props?.post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
||||
{props.containerSlot}
|
||||
</div>
|
||||
|
||||
<div id="container-inner" className="w-full max-w-6xl mx-auto lg:flex lg:space-x-4 justify-center relative z-10">
|
||||
{onLoading ? <LoadingCover /> : children}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{/* 左下角悬浮 */}
|
||||
@@ -61,15 +67,12 @@ const LayoutBase = props => {
|
||||
<Live2D />
|
||||
</div>
|
||||
|
||||
<div className="bottom-40 right-2 fixed justify-end z-20">
|
||||
<FloatDarkModeButton />
|
||||
</div>
|
||||
|
||||
{/* 右下角悬浮 */}
|
||||
<div className={ (show ? ' opacity-100 fixed ' : ' hidden opacity-0 ') + ' transition-all duration-200 bottom-12 right-2 justify-end z-20' }>
|
||||
<div className= ' justify-center flex flex-col items-center cursor-pointer '>
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
<div className="bottom-40 right-2 fixed justify-end space-y-2 z-20">
|
||||
<FloatDarkModeButton />
|
||||
<JumpToTopButton />
|
||||
{/* 可扩展的右下角悬浮 */}
|
||||
{props.floatRightBottom}
|
||||
</div>
|
||||
|
||||
<Footer title={siteInfo?.title || BLOG.TITLE} />
|
||||
|
||||
@@ -5,9 +5,10 @@ import Header from './components/Header'
|
||||
import CONFIG_MATERY from './config_matery'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import React from 'react'
|
||||
import Announcement from './components/Announcement'
|
||||
|
||||
export const LayoutIndex = (props) => {
|
||||
return <LayoutBase {...props} headerSlot={CONFIG_MATERY.HOME_BANNER_ENABLE && <Header {...props} />}>
|
||||
return <LayoutBase {...props} containerSlot={ <Announcement {...props}/>} headerSlot={CONFIG_MATERY.HOME_BANNER_ENABLE && <Header {...props} />}>
|
||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Catalog from './components/Catalog'
|
||||
import JumpToCommentButton from './components/JumpToCommentButton'
|
||||
import throttle from 'lodash.throttle'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import Announcement from './components/Announcement'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
@@ -24,6 +26,7 @@ export const LayoutSlug = props => {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
}, throttleMs))
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
@@ -43,84 +46,82 @@ export const LayoutSlug = props => {
|
||||
{...props}
|
||||
showCategory={false}
|
||||
showTag={false}
|
||||
floatRightBottom={<JumpToCommentButton />}
|
||||
>
|
||||
|
||||
<div id='inner-wrapper'>
|
||||
<div className={'w-full lg:max-w-3xl 2xl:max-w-4xl'}>
|
||||
<div className="-mt-32 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
<div id='inner-wrapper' className={'w-full lg:max-w-3xl 2xl:max-w-4xl'} >
|
||||
{/* 文章主体卡片 */}
|
||||
<div className="-mt-32 transition-all duration-300 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id="container" className="overflow-x-auto md:w-full px-3 ">
|
||||
{post?.type && post?.type === 'Post' && <>
|
||||
<div
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="100"
|
||||
data-aos-once="false"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className='px-10'>
|
||||
<ArticleInfo post={post} />
|
||||
</div>
|
||||
<hr />
|
||||
</>}
|
||||
|
||||
<div className='lg:px-10 subpixel-antialiased'>
|
||||
<article itemScope >
|
||||
{/* Notion文章主体 */}
|
||||
<section id='notion-article' className='justify-center mx-auto max-w-2xl lg:max-w-full'>
|
||||
{post && <NotionPage post={post} />}
|
||||
</section>
|
||||
|
||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||
{/* 文章内嵌广告 */}
|
||||
<ins className="adsbygoogle"
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
data-adtest="on"
|
||||
data-ad-layout="in-article"
|
||||
data-ad-format="fluid"
|
||||
data-ad-client="ca-pub-2708419466378217"
|
||||
data-ad-slot="3806269138" />
|
||||
</section>
|
||||
|
||||
{/* 文章版权说明 */}
|
||||
{post.type === 'Post' && <ArticleCopyright {...props} />}
|
||||
|
||||
</article>
|
||||
|
||||
<hr className='border-dashed' />
|
||||
|
||||
{/* 评论互动 */}
|
||||
<div className="overflow-x-auto dark:bg-hexo-black-gray px-3">
|
||||
<Comment frontMatter={post} />
|
||||
</div>
|
||||
{!lock && <div id="container" className="overflow-x-auto md:w-full px-3 ">
|
||||
{post?.type && post?.type === 'Post' && <>
|
||||
<div
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="100"
|
||||
data-aos-once="false"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className='px-10'>
|
||||
<ArticleInfo post={post} />
|
||||
</div>
|
||||
<hr />
|
||||
</>}
|
||||
|
||||
</div>}
|
||||
</div>
|
||||
<div className='lg:px-10 subpixel-antialiased'>
|
||||
<article itemScope >
|
||||
{/* Notion文章主体 */}
|
||||
<section id='notion-article' className='justify-center mx-auto max-w-2xl lg:max-w-full'>
|
||||
{post && <NotionPage post={post} />}
|
||||
</section>
|
||||
|
||||
{/* 文章推荐 */}
|
||||
{post.type === 'Post' && <ArticleAdjacent {...props} />}
|
||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||
{/* 文章内嵌广告 */}
|
||||
<ins className="adsbygoogle"
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
data-adtest="on"
|
||||
data-ad-layout="in-article"
|
||||
data-ad-format="fluid"
|
||||
data-ad-client="ca-pub-2708419466378217"
|
||||
data-ad-slot="3806269138" />
|
||||
</section>
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{/* 文章版权说明 */}
|
||||
{post.type === 'Post' && <ArticleCopyright {...props} />}
|
||||
|
||||
{/* 文章目录 */}
|
||||
{post?.toc?.length > 0 && <div id='toc-wrapper' style={{ zIndex: '-1' }} className='absolute top-0 w-full h-full xl:block hidden lg:max-w-3xl 2xl:max-w-4xl' >
|
||||
<div data-aos-delay="200"
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="200"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className='relative h-full'>
|
||||
<div className='float-right xl:-mr-72 xl:w-72 w-56 -mr-56 h-full mt-40'>
|
||||
<div className='sticky top-24'>
|
||||
<Catalog toc={post.toc} />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<hr className='border-dashed' />
|
||||
|
||||
{/* 评论互动 */}
|
||||
<div className="overflow-x-auto dark:bg-hexo-black-gray px-3">
|
||||
<Comment frontMatter={post} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
|
||||
<div className='fixed bottom-28 right-4'>
|
||||
<JumpToCommentButton />
|
||||
</div>
|
||||
{/* 底部文章推荐 */}
|
||||
{post.type === 'Post' && <ArticleAdjacent {...props} />}
|
||||
|
||||
<Announcement {...props}/>
|
||||
|
||||
{/* 右侧文章目录 */}
|
||||
{post?.toc?.length > 0 && <div id='toc-wrapper' style={{ zIndex: '-1' }} className='absolute top-0 w-full h-full xl:block hidden lg:max-w-3xl 2xl:max-w-4xl' >
|
||||
<div data-aos-delay="200"
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="200"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className='relative h-full'>
|
||||
<div className='float-right xl:-mr-72 xl:w-72 w-56 -mr-56 h-full mt-40'>
|
||||
<div className='sticky top-24'>
|
||||
<Catalog toc={post.toc} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
29
themes/matery/components/Announcement.js
Normal file
29
themes/matery/components/Announcement.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const NotionPage = dynamic(() => import('@/components/NotionPage'))
|
||||
|
||||
const Announcement = ({ notice }) => {
|
||||
const { locale } = useGlobal()
|
||||
if (!notice) {
|
||||
return <></>
|
||||
}
|
||||
return <div className="px-3 w-full">
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="500"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="mb-4 p-2 overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
<div className="text-sm flex flex-nowrap justify-between">
|
||||
<div className="font-light text-gray-600 dark:text-gray-200">
|
||||
<i className="mx-2 fas fa-bullhorn" />{locale.COMMON.ANNOUNCEMENT}
|
||||
</div>
|
||||
</div>
|
||||
{notice && (<div id="announcement-content">
|
||||
<NotionPage post={notice} className='text-center ' />
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
export default Announcement
|
||||
@@ -3,6 +3,7 @@ import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
// import Image from 'next/image'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
@@ -14,84 +15,87 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPageCover = CONFIG_MATERY.POST_LIST_COVER && post?.page_cover
|
||||
const delay = (index % 3) * 300
|
||||
return (
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="w-full mb-4 overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="w-full mb-4 overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className="flex flex-col h-80 justify-between">
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className="flex flex-col h-80 justify-between">
|
||||
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div
|
||||
className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
alt={post.title}
|
||||
className="opacity-50 h-full w-full hover:scale-125 rounded-t-md transform object-cover duration-500"
|
||||
/>
|
||||
<span className='absolute bottom-0 left-0 text-white p-6 text-2xl replace break-words w-full' > {post.title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div
|
||||
className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
alt={post.title}
|
||||
className="opacity-50 h-full w-full hover:scale-125 rounded-t-md transform object-cover duration-500"
|
||||
/>
|
||||
<span className='absolute bottom-0 left-0 text-white p-6 text-2xl replace break-words w-full' > {post.title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* 文字描述 */}
|
||||
<div >
|
||||
{/* 描述 */}
|
||||
<div className="px-4 flex flex-col w-full text-gray-700 dark:text-gray-300">
|
||||
{/* 文字描述 */}
|
||||
<div >
|
||||
{/* 描述 */}
|
||||
<div className="px-4 flex flex-col w-full text-gray-700 dark:text-gray-300">
|
||||
|
||||
{(!showPreview || showSummary) && post.summary && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '4', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace my-2 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
{(!showPreview || showSummary) && post.summary && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '4', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace my-2 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className='text-gray-800 justify-between flex my-2 dark:text-gray-300'>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
<div className='text-gray-800 justify-between flex my-2 dark:text-gray-300'>
|
||||
<div>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
<i className="far fa-clock mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
<i className="far fa-clock mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
|
||||
</Link>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
</Link>
|
||||
<TwikooCommentCount post={post} className='hover:underline cursor-pointer text-sm'/>
|
||||
</div>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{post?.tagItems && post?.tagItems.length > 0 && (<>
|
||||
<hr />
|
||||
<div className="text-gray-400 justify-between flex px-5 py-3">
|
||||
<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>
|
||||
</div>
|
||||
{post?.tagItems && post?.tagItems.length > 0 && (<>
|
||||
<hr />
|
||||
<div className="text-gray-400 justify-between flex px-5 py-3">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||
<div id="container" className='w-full'>
|
||||
<div className='pt-6'></div>
|
||||
{/* 文章列表 */}
|
||||
<div className="pt-4 flex flex-wrap pb-24" >
|
||||
<div className="pt-4 flex flex-wrap pb-12" >
|
||||
{posts.map(post => (
|
||||
<div key={post.id} className='xl:w-1/3 md:w-1/2 w-full p-4'> <BlogPostCard index={posts.indexOf(post)} post={post} siteInfo={siteInfo} /></div>
|
||||
))}
|
||||
|
||||
@@ -57,7 +57,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_MA
|
||||
return <div id='container' ref={targetRef} className='w-full'>
|
||||
|
||||
{/* 文章列表 */}
|
||||
<div className="pt-4 flex flex-wrap pb-24" >
|
||||
<div className="pt-4 flex flex-wrap pb-12" >
|
||||
{postsToShow.map(post => (
|
||||
<div key={post.id} className='xl:w-1/3 md:w-1/2 w-full p-4'>
|
||||
<BlogPostCard index={posts.indexOf(post)} post={post} siteInfo={siteInfo} />
|
||||
|
||||
@@ -21,9 +21,8 @@ export default function FloatDarkModeButton() {
|
||||
|
||||
return (
|
||||
<div className={'justify-center items-center text-center' } onClick={handleChangeDarkMode}>
|
||||
<i id="darkModeButton"
|
||||
className={`${isDarkMode ? 'fa-sun' : 'fa-moon'} fas transform hover:scale-105 duration-200
|
||||
text-2xl text-white bg-indigo-700 px-3 py-2.5 rounded-full dark:bg-black cursor-pointer`} />
|
||||
<i id="darkModeButton" className={`${isDarkMode ? 'fa-sun' : 'fa-moon'} fas transform hover:scale-105 duration-200
|
||||
text-white bg-indigo-700 w-10 h-10 py-2.5 rounded-full dark:bg-black cursor-pointer`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import Typed from 'typed.js'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
let wrapperTop = 0
|
||||
let windowTop = 0
|
||||
@@ -17,6 +18,8 @@ const throttleMs = 200
|
||||
const Header = props => {
|
||||
const [typed, changeType] = useState()
|
||||
const { siteInfo } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
useEffect(() => {
|
||||
scrollTrigger()
|
||||
updateHeaderHeight()
|
||||
@@ -96,8 +99,8 @@ const Header = props => {
|
||||
<span id='typed' />
|
||||
</div>
|
||||
<div onClick={() => { window.scrollTo({ top: wrapperTop, behavior: 'smooth' }) }}
|
||||
className="mt-12 border cursor-pointer w-40 text-center pt-4 pb-3 text-md text-white hover:bg-orange-600 duration-300 rounded-3xl">
|
||||
<i className='animate-bounce fas fa-angle-double-down' /> <span>开始阅读</span>
|
||||
className="mt-12 border cursor-pointer w-40 text-center pt-4 pb-3 text-md text-white hover:bg-orange-600 duration-300 rounded-3xl z-40">
|
||||
<i className='animate-bounce fas fa-angle-double-down' /> <span>{locale.COMMON.START_READING}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,11 +17,10 @@ const JumpToCommentButton = () => {
|
||||
}
|
||||
}
|
||||
|
||||
return (<div
|
||||
onClick={navToComment}
|
||||
className='flex space-x-1 items-center justify-center cursor-pointer transform hover:scale-105 duration-200 w-7 h-7 text-center'>
|
||||
<i className='fas fa-comments text-xl text-white bg-indigo-700 py-3 px-2 rounded-full' />
|
||||
</div>)
|
||||
return <div className={'justify-center items-center text-center'} onClick={navToComment}>
|
||||
<i id="darkModeButton" className={`fas fa-comments transform hover:scale-105 duration-200 text-white
|
||||
text-sm bg-indigo-700 w-10 h-10 rounded-full dark:bg-black cursor-pointer py-3`} />
|
||||
</div>
|
||||
}
|
||||
|
||||
export default JumpToCommentButton
|
||||
|
||||
@@ -16,11 +16,14 @@ const JumpToTopButton = ({ showPercent = true, percent }) => {
|
||||
if (!CONFIG_MATERY.WIDGET_TO_TOP) {
|
||||
return <></>
|
||||
}
|
||||
return (<div className=' drop-shadow-md space-x-1 items-center justify-center transform hover:scale-105 duration-200 px-3 py-2 text-center text-white bg-indigo-700 dark:bg-hexo-black-gray rounded-full'
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} >
|
||||
<div title={locale.POST.TOP} ><i className='fas fa-arrow-up text-2xl rounded-full' /></div>
|
||||
{showPercent && (<div className='text-md hidden lg:block'>{percent}</div>)}
|
||||
</div>)
|
||||
|
||||
return <div data-aos="fade-left"
|
||||
data-aos-duration="300"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className={'justify-center items-center text-center'} onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} >
|
||||
<i id="darkModeButton" title={locale.POST.TOP} className={`fas fa-arrow-up transform hover:scale-105 duration-200 text-white
|
||||
bg-indigo-700 w-10 h-10 rounded-full dark:bg-black cursor-pointer py-2.5`} />
|
||||
</div>
|
||||
}
|
||||
|
||||
export default JumpToTopButton
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
@@ -13,6 +12,7 @@ const PaginationSimple = ({ page, totalPage }) => {
|
||||
const router = useRouter()
|
||||
const currentPage = +page
|
||||
const showNext = currentPage < totalPage
|
||||
const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
|
||||
|
||||
return (
|
||||
<div className="my-10 mx-6 flex justify-between font-medium text-black dark:text-gray-100 space-x-2">
|
||||
@@ -20,8 +20,8 @@ const PaginationSimple = ({ page, totalPage }) => {
|
||||
href={{
|
||||
pathname:
|
||||
currentPage - 1 === 1
|
||||
? `${BLOG.SUB_PATH || '/'}`
|
||||
: `/page/${currentPage - 1}`,
|
||||
? `${pagePrefix}/`
|
||||
: `${pagePrefix}/page/${currentPage - 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
passHref
|
||||
@@ -36,7 +36,7 @@ const PaginationSimple = ({ page, totalPage }) => {
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: `/page/${currentPage + 1}`,
|
||||
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
passHref
|
||||
|
||||
@@ -13,6 +13,7 @@ import ArticleAround from './components/ArticleAround'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, prev, next, siteInfo, lock, validPassword } = props
|
||||
@@ -85,12 +86,17 @@ export const LayoutSlug = props => {
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{/* 文章分类和标签信息 */}
|
||||
<div className='flex justify-between'>
|
||||
{CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category} />}
|
||||
<div>
|
||||
{CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{post.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
|
||||
@@ -6,6 +6,7 @@ import React from 'react'
|
||||
import CONFIG_MEDIUM from '../config_medium'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const showPreview = CONFIG_MEDIUM.POST_LIST_PREVIEW && post.blockMap
|
||||
@@ -43,13 +44,9 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
}
|
||||
>
|
||||
<div className="text-sm py-1">{post.date?.start_date}</div>
|
||||
{CONFIG_MEDIUM.POST_LIST_CATEGORY && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{CONFIG_MEDIUM.POST_LIST_TAG &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
{CONFIG_MEDIUM.POST_LIST_CATEGORY && <CategoryItem category={post.category} />}
|
||||
{CONFIG_MEDIUM.POST_LIST_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
<TwikooCommentCount post={post} className='hover:underline'/>
|
||||
</div>
|
||||
|
||||
<div className="flex"></div>
|
||||
|
||||
@@ -48,7 +48,7 @@ const PaginationSimple = ({ page, totalPage }) => {
|
||||
{locale.PAGINATION.NEXT}→
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default PaginationSimple
|
||||
|
||||
@@ -8,8 +8,7 @@ import SideAreaLeft from './components/SideAreaLeft'
|
||||
import SideAreaRight from './components/SideAreaRight'
|
||||
import TopNav from './components/TopNav'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import CONFIG_NEXT from './config_next'
|
||||
import Live2D from '@/components/Live2D'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -22,12 +21,13 @@ import BLOG from '@/blog.config'
|
||||
const LayoutBase = (props) => {
|
||||
const { children, headerSlot, meta, sideBarSlot, floatSlot, rightAreaSlot, siteInfo } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const targetRef = React.useRef(null)
|
||||
const floatButtonGroup = React.useRef(null)
|
||||
const targetRef = useRef(null)
|
||||
const floatButtonGroup = useRef(null)
|
||||
const leftAreaSlot = <Live2D/>
|
||||
|
||||
const [show, switchShow] = React.useState(false)
|
||||
const [percent, changePercent] = React.useState(0) // 页面阅读百分比
|
||||
const [showRightFloat, switchShow] = useState(false)
|
||||
const [percent, changePercent] = useState(0) // 页面阅读百分比
|
||||
|
||||
const scrollListener = () => {
|
||||
const targetRef = document.getElementById('wrapper')
|
||||
const clientHeight = targetRef?.clientHeight
|
||||
@@ -37,13 +37,13 @@ const LayoutBase = (props) => {
|
||||
if (per > 100) per = 100
|
||||
const shouldShow = scrollY > 100 && per > 0
|
||||
|
||||
if (shouldShow !== show) {
|
||||
if (shouldShow !== showRightFloat) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
changePercent(per)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// facebook messenger 插件需要调整右下角悬浮按钮的高度
|
||||
const fb = document.getElementsByClassName('fb-customerchat')
|
||||
if (fb.length === 0) {
|
||||
@@ -54,7 +54,7 @@ const LayoutBase = (props) => {
|
||||
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
}, [show])
|
||||
}, [showRightFloat])
|
||||
|
||||
return (<div id='theme-next'>
|
||||
|
||||
@@ -78,7 +78,7 @@ const LayoutBase = (props) => {
|
||||
|
||||
{/* 右下角悬浮 */}
|
||||
<div ref={floatButtonGroup} className='right-8 bottom-12 lg:right-2 fixed justify-end z-20 font-sans'>
|
||||
<div className={(show ? 'animate__animated ' : 'hidden') + ' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '}>
|
||||
<div className={(showRightFloat ? 'animate__animated ' : 'hidden') + ' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '}>
|
||||
<JumpToTopButton percent={percent}/>
|
||||
<JumpToBottomButton />
|
||||
<FloatDarkModeButton/>
|
||||
@@ -91,8 +91,4 @@ const LayoutBase = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
LayoutBase.propTypes = {
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
export default LayoutBase
|
||||
|
||||
@@ -14,7 +14,11 @@ const Announcement = ({ post, className }) => {
|
||||
<i className="mr-2 fas fa-bullhorn" />{locale.COMMON.ANNOUNCEMENT}
|
||||
</div>
|
||||
</div>
|
||||
{post && (<div id="announcement-content">
|
||||
{post && (<div id="announcement-content" data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="200"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom">
|
||||
<NotionPage post={post} className='text-center ' />
|
||||
</div>)}
|
||||
</>
|
||||
|
||||
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
|
||||
import BlogAround from './BlogAround'
|
||||
import Comment from '@/components/Comment'
|
||||
import RecommendPosts from './RecommendPosts'
|
||||
import ShareBar from './ShareBar'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import TagItem from './TagItem'
|
||||
import formatDate from '@/lib/formatDate'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
@@ -94,6 +94,10 @@ export default function ArticleDetail(props) {
|
||||
</section>
|
||||
|
||||
{showArticleInfo && <>
|
||||
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
|
||||
{/* 版权声明 */}
|
||||
{post.type === 'Post' && <ArticleCopyright author={BLOG.AUTHOR} url={url} />}
|
||||
|
||||
@@ -124,7 +128,6 @@ export default function ArticleDetail(props) {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<ShareBar post={post} />
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -8,6 +8,7 @@ import TagItemMini from './TagItemMini'
|
||||
import CONFIG_NEXT from '../config_next'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const { locale } = useGlobal()
|
||||
@@ -22,6 +23,10 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
<Link
|
||||
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||
passHref
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className={`cursor-pointer hover:underline text-3xl ${showPreview ? 'text-center' : ''
|
||||
} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}>
|
||||
|
||||
@@ -29,17 +34,20 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 `}
|
||||
>
|
||||
<div data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="100"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'} flex-wrap dark:text-gray-500 text-gray-400 `}>
|
||||
|
||||
<div>
|
||||
{post.category && (
|
||||
<>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline transform">
|
||||
className="hover:text-blue-500 dark:hover:text-blue-400 cursor-pointer font-light text-sm hover:underline transform">
|
||||
|
||||
<i className="mr-1 fas fa-folder" />
|
||||
{post.category}
|
||||
@@ -48,27 +56,29 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
<span className="mx-2">|</span>
|
||||
</>
|
||||
)}
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
{post.date?.start_date}
|
||||
|
||||
</Link>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="hover:text-blue-500 dark:hover:text-blue-400 font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
{post.date?.start_date}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
<TwikooCommentCount post={post} className='hover:text-blue-500 dark:hover:text-blue-400 hover:underline text-sm'/>
|
||||
|
||||
<div className="hover:text-blue-500 dark:hover:text-blue-400 md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(!showPreview || showSummary) && !post.results && (
|
||||
<p className="mt-4 mb-24 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
<p data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="100"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="mt-4 mb-12 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
@@ -83,7 +93,11 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
)}
|
||||
|
||||
{showPreview && post?.blockMap && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<div data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="100"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"className="overflow-ellipsis truncate">
|
||||
<NotionPage post={post} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
const Card = ({ children, headerSlot, className }) => {
|
||||
return <div
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="300"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className={className}>
|
||||
const Card = (props) => {
|
||||
const { children, headerSlot } = props
|
||||
return <div {...props}>
|
||||
<>{headerSlot}</>
|
||||
<section className="shadow px-2 py-4 bg-white dark:bg-hexo-black-gray hover:shadow-xl duration-200">
|
||||
{children}
|
||||
|
||||
@@ -4,12 +4,21 @@ import React from 'react'
|
||||
const Logo = props => {
|
||||
const { siteInfo, className } = props
|
||||
return (
|
||||
<Link href='/' passHref legacyBehavior>
|
||||
<div className={'flex flex-col justify-center items-center cursor-pointer bg-black dark:bg-gray-800 space-y-3 font-bold ' + className}>
|
||||
<div className='font-serif text-xl text-white'> {siteInfo?.title}</div>
|
||||
<div className='text-sm text-gray-300 font-light text-center'> {siteInfo?.description}</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
<Link href='/' passHref legacyBehavior>
|
||||
<div className={'flex flex-col justify-center items-center cursor-pointer bg-black dark:bg-gray-800 space-y-3 font-bold ' + className}>
|
||||
<div data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className='font-serif text-xl text-white'> {siteInfo?.title}</div>
|
||||
<div data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="300"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className='text-sm text-gray-300 font-light text-center'> {siteInfo?.description}</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
export default Logo
|
||||
|
||||
@@ -34,7 +34,12 @@ export const MenuList = (props) => {
|
||||
return (
|
||||
<>
|
||||
{/* 大屏模式菜单 */}
|
||||
<nav id='nav' className='hidden md:block leading-8 text-gray-500 dark:text-gray-400 font-sans'>
|
||||
<nav id='nav' data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="400"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className='hidden md:block leading-8 text-gray-500 dark:text-gray-400 font-sans'>
|
||||
{links.map(link => link && link.show && <MenuItemDrop key={link?.to} link={link} />)}
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
@@ -14,6 +13,8 @@ const PaginationSimple = ({ page, showNext }) => {
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const currentPage = +page
|
||||
const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
|
||||
|
||||
return (
|
||||
<div
|
||||
data-aos="fade-down"
|
||||
@@ -25,8 +26,8 @@ const PaginationSimple = ({ page, showNext }) => {
|
||||
href={{
|
||||
pathname:
|
||||
currentPage - 1 === 1
|
||||
? `${BLOG.SUB_PATH || '/'}`
|
||||
: `/page/${currentPage - 1}`,
|
||||
? `${pagePrefix}/`
|
||||
: `${pagePrefix}/page/${currentPage - 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
passHref
|
||||
|
||||
@@ -41,11 +41,11 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
searchInputRef.current.value = ''
|
||||
setShowClean(false)
|
||||
}
|
||||
function lockSearchInput () {
|
||||
function lockSearchInput() {
|
||||
lock = true
|
||||
}
|
||||
|
||||
function unLockSearchInput () {
|
||||
function unLockSearchInput() {
|
||||
lock = false
|
||||
}
|
||||
const [showClean, setShowClean] = useState(false)
|
||||
@@ -61,31 +61,36 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='flex w-full bg-gray-100'>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`}
|
||||
className={'outline-none w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-800 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
onCompositionEnd={unLockSearchInput}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={currentSearch || ''}
|
||||
/>
|
||||
return <div data-aos="fade-down"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay="200"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className='flex w-full bg-gray-100'>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`}
|
||||
className={'outline-none w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-800 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
onCompositionEnd={unLockSearchInput}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={currentSearch || ''}
|
||||
/>
|
||||
|
||||
<div className='-ml-8 cursor-pointer float-right items-center justify-center py-2'
|
||||
onClick={handleSearch}>
|
||||
<i className={`hover:text-black transform duration-200 text-gray-500 cursor-pointer fas ${onLoading ? 'fa-spinner animate-spin' : 'fa-search'}`} />
|
||||
</div>
|
||||
<div className='-ml-8 cursor-pointer float-right items-center justify-center py-2'
|
||||
onClick={handleSearch}>
|
||||
<i className={`hover:text-black transform duration-200 text-gray-500 cursor-pointer fas ${onLoading ? 'fa-spinner animate-spin' : 'fa-search'}`} />
|
||||
</div>
|
||||
|
||||
{(showClean &&
|
||||
<div className='-ml-12 cursor-pointer dark:bg-gray-600 dark:hover:bg-gray-800 float-right items-center justify-center py-2'>
|
||||
<i className='hover:text-black transform duration-200 text-gray-400 cursor-pointer fas fa-times' onClick={cleanSearch} />
|
||||
{(showClean &&
|
||||
<div className='-ml-12 cursor-pointer dark:bg-gray-600 dark:hover:bg-gray-800 float-right items-center justify-center py-2'>
|
||||
<i className='hover:text-black transform duration-200 text-gray-400 cursor-pointer fas fa-times' onClick={cleanSearch} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default SearchInput
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import QRCode from 'qrcode.react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG_NEXT from '../config_next'
|
||||
|
||||
const ShareBar = ({ post }) => {
|
||||
const router = useRouter()
|
||||
const [qrCodeShow, setQrCodeShow] = React.useState(false)
|
||||
const { locale } = useGlobal()
|
||||
if (!CONFIG_NEXT.ARTICLE_SHARE) {
|
||||
return <></>
|
||||
}
|
||||
const shareUrl = BLOG.LINK + router.asPath
|
||||
|
||||
// 二维码悬浮
|
||||
const btnRef = React.createRef()
|
||||
const popoverRef = React.createRef()
|
||||
|
||||
const openPopover = () => {
|
||||
createPopper(btnRef.current, popoverRef.current, {
|
||||
placement: 'top'
|
||||
})
|
||||
setQrCodeShow(true)
|
||||
}
|
||||
const closePopover = () => {
|
||||
setQrCodeShow(false)
|
||||
}
|
||||
|
||||
const copyUrl = () => {
|
||||
copy(shareUrl)
|
||||
alert(locale.COMMON.URL_COPIED)
|
||||
}
|
||||
|
||||
return <>
|
||||
<div
|
||||
className='py-2 text-gray-500 text-center space-x-2 flex my-1 dark:text-gray-200 overflow-visible'>
|
||||
<div className='hidden md:block text-gray-800 dark:text-gray-300 mr-2 my-2 whitespace-nowrap'>{locale.COMMON.SHARE}:</div>
|
||||
<div className='text-3xl cursor-pointer'>
|
||||
<a className='text-blue-700' href={`https://www.facebook.com/sharer.php?u=${shareUrl}`} >
|
||||
<i className='fab fa-facebook-square'/>
|
||||
</a>
|
||||
</div>
|
||||
<div className='text-3xl cursor-pointer'>
|
||||
<a className='text-blue-400' target='_blank' rel='noreferrer' href={`https://twitter.com/intent/tweet?title=${post.title}&url${shareUrl}`} >
|
||||
<i className='fab fa-twitter-square'/>
|
||||
</a>
|
||||
</div>
|
||||
<div className='text-3xl cursor-pointer'>
|
||||
<a className='text-blue-500' href={`https://telegram.me/share/url?url=${shareUrl}&text=${post.title}`} >
|
||||
<i className='fab fa-telegram'/>
|
||||
</a>
|
||||
</div>
|
||||
<div className='cursor-pointer text-2xl'>
|
||||
<a className='text-green-600' ref={btnRef} onMouseEnter={openPopover} onMouseLeave={closePopover}>
|
||||
<i className='fab fa-weixin'/>
|
||||
<div ref={popoverRef} className={(qrCodeShow ? 'opacity-100 ' : 'invisible opacity-0') + ' transition-all duration-200 text-center py-2'}>
|
||||
<div className='p-2 bg-white border-0 duration-200 transform block z-40 font-normal shadow-xl mr-10'>
|
||||
<QRCode value={shareUrl} fgColor='#000000' />
|
||||
</div>
|
||||
<span className='bg-white text-black font-semibold p-1 mb-0 rounded-t-lg text-sm mx-auto mr-10'>
|
||||
{locale.COMMON.SCAN_QR_CODE}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div className='cursor-pointer text-2xl'>
|
||||
<a className='text-red-600' target='_blank' rel='noreferrer' href={`https://service.weibo.com/share/share.php?url=${shareUrl}&title=${post.title}`} >
|
||||
<i className='fab fa-weibo'/>
|
||||
</a>
|
||||
</div>
|
||||
<div className='cursor-pointer text-2xl'>
|
||||
<a className='text-blue-400' target='_blank' rel='noreferrer' href={`http://connect.qq.com/widget/shareqq/index.html?url=${shareUrl}&sharesource=qzone&title=${post.title}&desc=${post.summary}`} >
|
||||
<i className='fab fa-qq'/>
|
||||
</a>
|
||||
</div>
|
||||
<div className='cursor-pointer text-2xl'>
|
||||
<a className='text-yellow-600' onClick={copyUrl} >
|
||||
<i className='fas fa-link'/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
export default ShareBar
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from 'react'
|
||||
import ShareBar from './ShareBar'
|
||||
|
||||
/**
|
||||
* 悬浮在屏幕右下角,分享按钮
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const ShareButton = ({ post }) => {
|
||||
const [popoverShow, setPopoverShow] = React.useState(false)
|
||||
const btnRef = React.createRef()
|
||||
|
||||
const openPopover = () => {
|
||||
setPopoverShow(true)
|
||||
}
|
||||
const closePopover = () => {
|
||||
setPopoverShow(false)
|
||||
}
|
||||
return (
|
||||
<div className='my-2'
|
||||
onMouseEnter={() => { openPopover() }}
|
||||
onMouseLeave={() => { closePopover() }}>
|
||||
<div className={(popoverShow ? 'opacity-100' : 'opacity-0') + ' duration-200 ease-in-out font-normal'}>
|
||||
<ShareBar post={post}/>
|
||||
</div>
|
||||
<div ref={btnRef}
|
||||
className='z-20 border dark:border-gray-500 dark:bg-gray-600 bg-white cursor-pointer text-md hover:shadow-2xl shadow-lg'>
|
||||
<i className='fas fa-share-alt-square transform duration-200 hover:scale-150 dark:text-gray-200 p-4' title='share' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShareButton
|
||||
@@ -26,14 +26,10 @@ const SideAreaLeft = props => {
|
||||
return <aside id='left' className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'ml-4' : 'mr-4') + ' hidden lg:block flex-col w-60 z-20 relative'}>
|
||||
|
||||
<section
|
||||
data-aos="fade-down"
|
||||
data-aos-duration="300"
|
||||
data-aos-once="false"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className='w-60'>
|
||||
{/* 菜单 */}
|
||||
<section className='shadow hidden lg:block mb-5 pb-4 bg-white dark:bg-hexo-black-gray hover:shadow-xl duration-200'>
|
||||
<Logo {...props} className='h-32' />
|
||||
<Logo className='h-32' {...props} />
|
||||
<div className='pt-2 px-2 font-sans'>
|
||||
<MenuList allowCollapse={true} {...props} />
|
||||
</div>
|
||||
|
||||
@@ -43,6 +43,7 @@ const SideAreaRight = (props) => {
|
||||
</Card>}
|
||||
|
||||
<div className="sticky top-0 space-y-4 w-full">
|
||||
|
||||
{announcementVisible && <Card>
|
||||
<Announcement post={notice} />
|
||||
</Card>}
|
||||
|
||||
@@ -30,7 +30,6 @@ const CONFIG_NEXT = {
|
||||
WIDGET_DARK_MODE: false, // 显示日间/夜间模式切换
|
||||
WIDGET_TOC: true, // 移动端显示悬浮目录
|
||||
|
||||
ARTICLE_SHARE: process.env.NEXT_PUBLIC_ARTICLE_SHARE || false, // 文章分享功能
|
||||
ARTICLE_RELATE_POSTS: true, // 相关文章推荐
|
||||
ARTICLE_COPYRIGHT: true // 文章版权声明
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import BLOG from '@/blog.config'
|
||||
import Announcement from './components/Announcement'
|
||||
import { BlogListPage } from './components/BlogListPage'
|
||||
import { BlogListScroll } from './components/BlogListScroll'
|
||||
import LayoutBase from './LayoutBase'
|
||||
@@ -7,6 +8,7 @@ import LayoutBase from './LayoutBase'
|
||||
export const LayoutIndex = props => {
|
||||
return (
|
||||
<LayoutBase {...props}>
|
||||
<Announcement {...props} />
|
||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import NotionPage from '@/components/NotionPage'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Comment from '@/components/Comment'
|
||||
import { ArticleFooter } from './components/ArticleFooter'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
@@ -21,6 +22,7 @@ export const LayoutSlug = props => {
|
||||
{post && <>
|
||||
<ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post}/>
|
||||
<ArticleFooter />
|
||||
</>}
|
||||
|
||||
18
themes/nobelium/components/Announcement.js
Normal file
18
themes/nobelium/components/Announcement.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const NotionPage = dynamic(() => import('@/components/NotionPage'))
|
||||
|
||||
const Announcement = ({ notice, className }) => {
|
||||
if (notice?.blockMap) {
|
||||
return <div className={className}>
|
||||
<section id='announcement-wrapper' className='mb-10'>
|
||||
{notice && (<div id="announcement-content">
|
||||
<NotionPage post={notice} className='text-center ' />
|
||||
</div>)}
|
||||
</section>
|
||||
</div>
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
export default Announcement
|
||||
@@ -4,6 +4,7 @@ import NotionPage from '@/components/NotionPage'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Comment from '@/components/Comment'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword, prev, next } = props
|
||||
@@ -22,6 +23,8 @@ export const LayoutSlug = props => {
|
||||
{post && <>
|
||||
<ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{post.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
<Comment frontMatter={post}/>
|
||||
</>}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import CONFIG_SIMPLE from '../config_simple'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
export const BlogItem = props => {
|
||||
const { post } = props
|
||||
@@ -18,6 +19,7 @@ export const BlogItem = props => {
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
<span> <i className="fa-regular fa-user"></i> <a href={CONFIG_SIMPLE.AUTHOR_LINK}>{BLOG.AUTHOR}</a></span>
|
||||
<span> - <i className="fa-regular fa-clock"></i> {post.date?.start_date || post.createdTime}</span>
|
||||
<span> - <TwikooCommentCount post={post}/></span>
|
||||
{post.category && <span> - <i className="fa-regular fa-folder"></i> <a href={`/category/${post.category}`} className="hover:text-red-400 transition-all duration-200">{post.category}</a></span>}
|
||||
{post.tags && post.tags?.length > 0 && post.tags.map(t => <span key={t}> / <Link href={`/tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user