mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
完成部分页面
This commit is contained in:
@@ -7,6 +7,7 @@ const BLOG = {
|
||||
link: 'https://tangly1024.com', // 网站地址
|
||||
keywords: ['Notion', '写作', '博客'], // 网站关键词
|
||||
lang: 'zh-CN', // ['zh-CN','en-US'] default lang => see /lib/lang.js for more.
|
||||
beian: '闽ICP备20010331号', // 备案号
|
||||
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
|
||||
appearance: 'auto', // ['light', 'dark', 'auto'],
|
||||
|
||||
@@ -36,7 +36,7 @@ const CommonHead = ({ meta }) => {
|
||||
<meta name='twitter:card' content='summary_large_image' />
|
||||
<meta name='twitter:description' content={description} />
|
||||
<meta name='twitter:title' content={title} />
|
||||
{meta.type === 'article' && (
|
||||
{meta?.type === 'article' && (
|
||||
<>
|
||||
<meta
|
||||
property='article:published_time'
|
||||
|
||||
5
themes/Fukasawa/Layout404.js
Normal file
5
themes/Fukasawa/Layout404.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const Layout404 = (props) => {
|
||||
return <LayoutBase {...props}>404</LayoutBase>
|
||||
}
|
||||
7
themes/Fukasawa/LayoutArchive.js
Normal file
7
themes/Fukasawa/LayoutArchive.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutArchive = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
Archive Page
|
||||
</LayoutBase>
|
||||
}
|
||||
48
themes/Fukasawa/LayoutBase.js
Normal file
48
themes/Fukasawa/LayoutBase.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import AsideLeft from './components/AsideLeft'
|
||||
|
||||
/**
|
||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||
* @param children
|
||||
* @param layout
|
||||
* @param tags
|
||||
* @param meta
|
||||
* @param post
|
||||
* @param currentSearch
|
||||
* @param currentCategory
|
||||
* @param currentTag
|
||||
* @param categories
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const LayoutBase = ({
|
||||
children,
|
||||
headerSlot,
|
||||
tags,
|
||||
meta,
|
||||
post,
|
||||
postCount,
|
||||
sideBarSlot,
|
||||
floatSlot,
|
||||
rightAreaSlot,
|
||||
currentSearch,
|
||||
currentCategory,
|
||||
currentTag,
|
||||
categories
|
||||
}) => {
|
||||
return (<>
|
||||
<CommonHead meta={meta} />
|
||||
<div className='flex flex-between'>
|
||||
<AsideLeft tags={tags} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
||||
<main id='wrapper' className='flex flex-grow py-8 justify-center'>
|
||||
<div className='2xl:max-w-6xl md:max-w-3xl max-w-md w-full'>
|
||||
<div> {headerSlot} </div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</>)
|
||||
}
|
||||
|
||||
export default LayoutBase
|
||||
7
themes/Fukasawa/LayoutCategory.js
Normal file
7
themes/Fukasawa/LayoutCategory.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutCategory = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
Category
|
||||
</LayoutBase>
|
||||
}
|
||||
7
themes/Fukasawa/LayoutCategoryIndex.js
Normal file
7
themes/Fukasawa/LayoutCategoryIndex.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutCategoryIndex = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
Category
|
||||
</LayoutBase>
|
||||
}
|
||||
10
themes/Fukasawa/LayoutIndex.js
Normal file
10
themes/Fukasawa/LayoutIndex.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BlogListPage from './components/BlogListPage'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutIndex = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
|
||||
<BlogListPage posts={props.posts} postCount={props.postCount}/>
|
||||
|
||||
</LayoutBase>
|
||||
}
|
||||
10
themes/Fukasawa/LayoutPage.js
Normal file
10
themes/Fukasawa/LayoutPage.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BlogListPage from './components/BlogListPage'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutPage = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
|
||||
<BlogListPage posts={props.posts} postCount={props.postCount}/>
|
||||
|
||||
</LayoutBase>
|
||||
}
|
||||
30
themes/Fukasawa/LayoutSearch.js
Normal file
30
themes/Fukasawa/LayoutSearch.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutSearch = (props) => {
|
||||
let filteredPosts
|
||||
const searchKey = getSearchKey()
|
||||
if (searchKey) {
|
||||
filteredPosts = props.posts.filter(post => {
|
||||
const tagContent = post.tags ? post.tags.join(' ') : ''
|
||||
const searchContent = post.title + post.summary + tagContent
|
||||
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
|
||||
})
|
||||
} else {
|
||||
filteredPosts = props.posts
|
||||
}
|
||||
|
||||
console.log(filteredPosts)
|
||||
|
||||
return <LayoutBase {...props}>
|
||||
Search {searchKey}
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
function getSearchKey () {
|
||||
const router = useRouter()
|
||||
if (router.query && router.query.s) {
|
||||
return router.query.s
|
||||
}
|
||||
return null
|
||||
}
|
||||
13
themes/Fukasawa/LayoutSlug.js
Normal file
13
themes/Fukasawa/LayoutSlug.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'prismjs'
|
||||
import 'prismjs/components/prism-bash'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutSlug = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
首页
|
||||
</LayoutBase>
|
||||
}
|
||||
7
themes/Fukasawa/LayoutTag.js
Normal file
7
themes/Fukasawa/LayoutTag.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutTag = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
Tag - {props.tag}
|
||||
</LayoutBase>
|
||||
}
|
||||
7
themes/Fukasawa/LayoutTagIndex.js
Normal file
7
themes/Fukasawa/LayoutTagIndex.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutTagIndex = (props) => {
|
||||
return <LayoutBase {...props}>
|
||||
Tag - {props.tag}
|
||||
</LayoutBase>
|
||||
}
|
||||
47
themes/Fukasawa/components/AsideLeft.js
Normal file
47
themes/Fukasawa/components/AsideLeft.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Logo from './Logo'
|
||||
import GroupCategory from './GroupCategory'
|
||||
import GroupMenu from './GroupMenu'
|
||||
import GroupTag from './GroupTag'
|
||||
import SearchInput from './SearchInput'
|
||||
import SiteInfo from './SiteInfo'
|
||||
|
||||
function AsideLeft ({ tags, currentTag, categories, currentCategory }) {
|
||||
return <div className='w-72 bg-white min-h-screen px-10 py-14'>
|
||||
|
||||
<Logo />
|
||||
|
||||
<section className='flex flex-col text-gray-600'>
|
||||
<hr className='w-12 my-8' />
|
||||
<GroupMenu/>
|
||||
</section>
|
||||
|
||||
<section className='flex flex-col text-gray-600'>
|
||||
<hr className='w-12 my-8' />
|
||||
<SearchInput/>
|
||||
</section>
|
||||
|
||||
<section className='flex flex-col'>
|
||||
<hr className='w-12 my-8 ' />
|
||||
{ BLOG.description }
|
||||
</section>
|
||||
|
||||
<section className='flex flex-col'>
|
||||
<hr className='w-12 my-8 ' />
|
||||
<GroupTag tags={tags} currentTag={currentTag}/>
|
||||
</section>
|
||||
|
||||
<section className='flex flex-col'>
|
||||
<hr className='w-12 my-8 ' />
|
||||
<GroupCategory categories={categories} currentCategory={currentCategory}/>
|
||||
</section>
|
||||
|
||||
<section className='flex flex-col'>
|
||||
<hr className='w-12 my-8 ' />
|
||||
<SiteInfo/>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
export default AsideLeft
|
||||
39
themes/Fukasawa/components/BlogCard.js
Normal file
39
themes/Fukasawa/components/BlogCard.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import CONFIG_FUKA from '../config_fuka'
|
||||
import Card from './Card'
|
||||
|
||||
const BlogCard = ({ post, showSummary }) => {
|
||||
const showPreview = CONFIG_FUKA.POST_LIST_PREVIEW && post.blockMap
|
||||
return (
|
||||
<Card className='w-full max-w-sm p-2'>
|
||||
<div key={post.id} className='animate__animated animate__fadeIn flex flex-col-reverse justify-between duration-300'>
|
||||
|
||||
<div className='p-2 flex flex-col w-full'>
|
||||
<Link href={`${BLOG.path}/article/${post.slug}`} passHref>
|
||||
<a className={`cursor-pointer font-bold hover:underline text-xl flex ${showPreview ? 'justify-center' : 'justify-start'} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}>
|
||||
{post.title}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
{(!showPreview || showSummary) && <p className='mt-4 mb-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7 overflow-hidden'>
|
||||
{post.summary}
|
||||
</p>}
|
||||
</div>
|
||||
|
||||
{CONFIG_FUKA.POST_LIST_COVER && post?.page_cover && (
|
||||
<Link href={`${BLOG.path}/article/${post.slug}`} passHref>
|
||||
<div className='h-40 w-full relative duration-200 cursor-pointer transform overflow-hidden'>
|
||||
<Image className='hover:scale-105 transform duration-500' src={post?.page_cover} alt={post.title} layout='fill' objectFit='cover' loading='lazy' />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div >
|
||||
</Card>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogCard
|
||||
12
themes/Fukasawa/components/BlogListEmpty.js
Normal file
12
themes/Fukasawa/components/BlogListEmpty.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogListEmpty = ({ currentSearch }) => {
|
||||
return <div className='flex items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogListEmpty
|
||||
36
themes/Fukasawa/components/BlogListPage.js
Normal file
36
themes/Fukasawa/components/BlogListPage.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import BlogCard from './BlogCard'
|
||||
import PaginationNumber from './PaginationNumber'
|
||||
import BLOG from '@/blog.config'
|
||||
import BlogPostListEmpty from './BlogListEmpty'
|
||||
|
||||
/**
|
||||
* 文章列表分页表格
|
||||
* @param page 当前页
|
||||
* @param posts 所有文章
|
||||
* @param tags 所有标签
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogListPage = ({ page = 1, posts = [], postCount }) => {
|
||||
const totalPage = Math.ceil(postCount / BLOG.postsPerPage)
|
||||
|
||||
if (!posts || posts.length === 0) {
|
||||
return <BlogPostListEmpty />
|
||||
} else {
|
||||
return (
|
||||
<div id="container">
|
||||
{/* 文章列表 */}
|
||||
<div className="grid 2xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-1 ">
|
||||
{posts.map(post => (
|
||||
<div key={post.id} className='justify-center flex'>
|
||||
<BlogCard key={post.id} post={post} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<PaginationNumber page={page} totalPage={totalPage} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default BlogListPage
|
||||
9
themes/Fukasawa/components/Card.js
Normal file
9
themes/Fukasawa/components/Card.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const Card = ({ children, headerSlot, className }) => {
|
||||
return <div className={className}>
|
||||
<>{headerSlot}</>
|
||||
<section className="shadow px-2 py-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200">
|
||||
{children}
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
export default Card
|
||||
24
themes/Fukasawa/components/GroupCategory.js
Normal file
24
themes/Fukasawa/components/GroupCategory.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { faFolder, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
function GroupCategory ({ currentCategory, categories }) {
|
||||
return <>
|
||||
<div id='category-list' className='dark:border-gray-600 flex flex-wrap'>
|
||||
{Object.keys(categories).map(category => {
|
||||
const selected = currentCategory === category
|
||||
return <Link key={category} href={`/category/${category}`} passHref>
|
||||
<a className={(selected
|
||||
? 'hover:text-white dark:hover:text-white bg-gray-600 text-white '
|
||||
: 'dark:text-gray-400 text-gray-500 hover:text-white hover:bg-gray-500 dark:hover:text-white') +
|
||||
' text-sm w-full items-center duration-300 px-2 cursor-pointer py-1 font-light'}>
|
||||
<FontAwesomeIcon icon={selected ? faFolderOpen : faFolder} className={`${selected ? 'text-white' : 'text-gray-400'} mr-2`} />{category}({categories[category]})
|
||||
</a>
|
||||
</Link>
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default GroupCategory
|
||||
38
themes/Fukasawa/components/GroupMenu.js
Normal file
38
themes/Fukasawa/components/GroupMenu.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG_FUKA from '../config_fuka'
|
||||
|
||||
function GroupMenu () {
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
|
||||
const links = [
|
||||
{ id: 0, name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ id: 1, name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_FUKA.MENU_CATEGORY },
|
||||
{ id: 2, name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_FUKA.MENU_TAG },
|
||||
{ id: 3, name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG_FUKA.MENU_ARCHIVE },
|
||||
{ id: 4, name: locale.NAV.ABOUT, to: '/about', show: CONFIG_FUKA.MENU_ABOUT }
|
||||
]
|
||||
return <nav id='nav' className='font-sans text-sm'>
|
||||
{links.map(link => {
|
||||
if (link.show) {
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
return <Link key={`${link.id}-${link.to}`} title={link.to} href={link.to} >
|
||||
<a className={'py-0.5 duration-500 justify-between text-gray-500 hover:text-black cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')} >
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={ 'text-gray-500 hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</a>
|
||||
</Link>
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})}
|
||||
</nav>
|
||||
}
|
||||
|
||||
export default GroupMenu
|
||||
24
themes/Fukasawa/components/GroupTag.js
Normal file
24
themes/Fukasawa/components/GroupTag.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
/**
|
||||
* 标签组
|
||||
* @param tags
|
||||
* @param currentTag
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
function GroupTag ({ tags, currentTag }) {
|
||||
if (!tags) return <></>
|
||||
return (
|
||||
<div id='tags-group' className='dark:border-gray-600 w-66 space-y-2'>
|
||||
{
|
||||
tags.map(tag => {
|
||||
const selected = tag.name === currentTag
|
||||
return <TagItemMini key={tag.name} tag={tag} selected={selected} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default GroupTag
|
||||
12
themes/Fukasawa/components/Logo.js
Normal file
12
themes/Fukasawa/components/Logo.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
|
||||
function Logo () {
|
||||
return <section className='flex'>
|
||||
<Link href='/'>
|
||||
<a className='hover:bg-black hover:text-white border-black border-2 duration-500 px-4 py-2 cursor-pointer font-black'>{BLOG.title}</a>
|
||||
</Link>
|
||||
</section>
|
||||
}
|
||||
|
||||
export default Logo
|
||||
94
themes/Fukasawa/components/PaginationNumber.js
Normal file
94
themes/Fukasawa/components/PaginationNumber.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
/**
|
||||
* 数字翻页插件
|
||||
* @param page 当前页码
|
||||
* @param showNext 是否有下一页
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PaginationNumber = ({ page, totalPage }) => {
|
||||
const router = useRouter()
|
||||
const currentPage = +page
|
||||
const showNext = page !== totalPage
|
||||
const pages = generatePages(page, currentPage, totalPage)
|
||||
|
||||
return (
|
||||
<div className='my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-gray-700 dark:text-gray-300 py-3 shadow space-x-2'>
|
||||
|
||||
{/* 上一页 */}
|
||||
<Link
|
||||
href={ {
|
||||
pathname: (currentPage - 1 === 1 ? `${BLOG.path || '/'}` : `/page/${currentPage - 1}`), query: router.query.s ? { s: router.query.s } : {}
|
||||
} } passHref >
|
||||
<div
|
||||
rel='prev'
|
||||
className={`${currentPage === 1 ? 'invisible' : 'block'} border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleLeft}/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{pages}
|
||||
|
||||
{/* 下一页 */}
|
||||
<Link href={ { pathname: `/page/${currentPage + 1}`, query: router.query.s ? { s: router.query.s } : {} } } passHref>
|
||||
<div
|
||||
rel='next'
|
||||
className={`${+showNext ? 'block' : 'invisible'} border-t-2 border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleRight}/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getPageElement (page, currentPage) {
|
||||
return <Link href={page === 1 ? '/' : `/page/${page}`} key={page} passHref>
|
||||
<a className={(page + '' === currentPage + '' ? 'font-bold bg-gray-500 dark:bg-gray-400 text-white ' : 'border-t-2 duration-500 border-white hover:border-gray-400 ') +
|
||||
' border-white dark:border-gray-700 dark:hover:border-gray-400 cursor-pointer w-6 text-center font-light hover:font-bold'}>
|
||||
{page}
|
||||
</a>
|
||||
</Link>
|
||||
}
|
||||
function generatePages (page, currentPage, totalPage) {
|
||||
const pages = []
|
||||
const groupCount = 7 // 最多显示页签数
|
||||
if (totalPage <= groupCount) {
|
||||
for (let i = 1; i <= totalPage; i++) {
|
||||
pages.push(getPageElement(i, page))
|
||||
}
|
||||
} else {
|
||||
pages.push(getPageElement(1, page))
|
||||
const dynamicGroupCount = groupCount - 2
|
||||
let startPage = currentPage - 2
|
||||
if (startPage <= 1) {
|
||||
startPage = 2
|
||||
}
|
||||
if (startPage + dynamicGroupCount > totalPage) {
|
||||
startPage = totalPage - dynamicGroupCount
|
||||
}
|
||||
if (startPage > 2) {
|
||||
pages.push(<div key={-1}>... </div>)
|
||||
}
|
||||
|
||||
for (let i = 0; i < dynamicGroupCount; i++) {
|
||||
if (startPage + i < totalPage) {
|
||||
pages.push(getPageElement(startPage + i, page))
|
||||
}
|
||||
}
|
||||
|
||||
if (startPage + dynamicGroupCount < totalPage) {
|
||||
pages.push(<div key={-2}>... </div>)
|
||||
}
|
||||
|
||||
pages.push(getPageElement(totalPage, page))
|
||||
}
|
||||
return pages
|
||||
}
|
||||
export default PaginationNumber
|
||||
68
themes/Fukasawa/components/SearchInput.js
Normal file
68
themes/Fukasawa/components/SearchInput.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useImperativeHandle, useRef, useState } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faSearch, faSpinner, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
const [searchKey, setSearchKey] = useState(currentSearch || '')
|
||||
const [onLoading, setLoadingState] = useState(false)
|
||||
const router = useRouter()
|
||||
const searchInputRef = useRef()
|
||||
useImperativeHandle(cRef, () => {
|
||||
return {
|
||||
focus: () => {
|
||||
searchInputRef?.current?.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
const handleSearch = (key) => {
|
||||
if (key && key !== '') {
|
||||
setLoadingState(true)
|
||||
router.push({ pathname: '/search', query: { s: key } }).then(r => {
|
||||
setLoadingState(false)
|
||||
})
|
||||
} else {
|
||||
router.push({ pathname: '/' }).then(r => {
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleKeyUp = (e) => {
|
||||
if (e.keyCode === 13) { // 回车
|
||||
handleSearch(searchInputRef.current.value)
|
||||
} else if (e.keyCode === 27) { // ESC
|
||||
cleanSearch()
|
||||
}
|
||||
}
|
||||
const cleanSearch = () => {
|
||||
searchInputRef.current.value = ''
|
||||
setSearchKey('')
|
||||
}
|
||||
|
||||
const updateSearchKey = (val) => {
|
||||
setSearchKey(val)
|
||||
}
|
||||
|
||||
return <div className='flex w-full bg-gray-100'>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={searchKey}
|
||||
/>
|
||||
|
||||
<div className='-ml-8 cursor-pointer dark:bg-gray-600 dark:hover:bg-gray-800 float-right items-center justify-center py-2'
|
||||
onClick={() => { handleSearch(searchKey) }}>
|
||||
<FontAwesomeIcon spin={onLoading} icon={onLoading ? faSpinner : faSearch} className='hover:text-black transform duration-200 text-gray-500 cursor-pointer' />
|
||||
</div>
|
||||
|
||||
{(searchKey && searchKey.length &&
|
||||
<div className='-ml-12 cursor-pointer dark:bg-gray-600 dark:hover:bg-gray-800 float-right items-center justify-center py-2'>
|
||||
<FontAwesomeIcon icon={faTimes} className='hover:text-black transform duration-200 text-gray-400 cursor-pointer' onClick={cleanSearch} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default SearchInput
|
||||
26
themes/Fukasawa/components/SiteInfo.js
Normal file
26
themes/Fukasawa/components/SiteInfo.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { faEye, faShieldAlt, faUsers } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
function SiteInfo ({ title }) {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const startYear = BLOG.since && BLOG.since !== currentYear && BLOG.since + '-'
|
||||
return (
|
||||
<footer
|
||||
className='leading-6 justify-start w-full text-gray-400 text-xs font-sans'
|
||||
>
|
||||
<span> © {`${startYear}${currentYear}`} <span> <a href={BLOG.link} className='text-gray-500 dark:text-gray-300 '>{BLOG.author}</a>. <br /></span>
|
||||
|
||||
<span>Powered by <a href='https://github.com/tangly1024/NotionNext' className='underline font-bold text-gray-500 dark:text-gray-300'>NotionNext</a>.</span><br /></span>
|
||||
|
||||
{BLOG.beian && <><FontAwesomeIcon icon={faShieldAlt} /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.beian}</a><br/></>}
|
||||
|
||||
<span className='hidden busuanzi_container_site_pv'> <FontAwesomeIcon icon={faEye} /><span className='px-1 busuanzi_value_site_pv'> </span> </span>
|
||||
<span className='pl-2 hidden busuanzi_container_site_uv'> <FontAwesomeIcon icon={faUsers} /> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
|
||||
<br />
|
||||
<h1>{title}</h1>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
export default SiteInfo
|
||||
17
themes/Fukasawa/components/TagItemMini.js
Normal file
17
themes/Fukasawa/components/TagItemMini.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { faTag } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link'
|
||||
|
||||
const TagItemMini = ({ tag, selected = false }) => {
|
||||
return <Link key={tag} href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`} passHref>
|
||||
<a className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200
|
||||
mr-2 py-0.5 px-1 text-xs whitespace-nowrap dark:hover:text-white
|
||||
${selected
|
||||
? 'text-white dark:text-gray-300 bg-black dark:bg-black dark:hover:bg-gray-900'
|
||||
: `text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}` }>
|
||||
<div className='font-light dark:text-gray-400'>{selected && <FontAwesomeIcon icon={faTag} className='mr-1'/>} {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
|
||||
</a>
|
||||
</Link>
|
||||
}
|
||||
|
||||
export default TagItemMini
|
||||
12
themes/Fukasawa/config_fuka.js
Normal file
12
themes/Fukasawa/config_fuka.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const FUKA_CONFIG = {
|
||||
|
||||
POST_LIST_COVER: true, // 文章列表显示图片封面
|
||||
|
||||
// 菜单
|
||||
MENU_ABOUT: true, // 显示关于
|
||||
MENU_CATEGORY: true, // 显示分类
|
||||
MENU_TAG: true, // 显示标签
|
||||
MENU_ARCHIVE: true, // 显示归档
|
||||
MENU_SEARCH: true // 显示搜索
|
||||
}
|
||||
export default FUKA_CONFIG
|
||||
10
themes/Fukasawa/index.js
Normal file
10
themes/Fukasawa/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export { LayoutIndex } from './LayoutIndex'
|
||||
export { LayoutSearch } from './LayoutSearch'
|
||||
export { LayoutArchive } from './LayoutArchive'
|
||||
export { LayoutSlug } from './LayoutSlug'
|
||||
export { Layout404 } from './Layout404'
|
||||
export { LayoutCategory } from './LayoutCategory'
|
||||
export { LayoutCategoryIndex } from './LayoutCategoryIndex'
|
||||
export { LayoutPage } from './LayoutPage'
|
||||
export { LayoutTag } from './LayoutTag'
|
||||
export { LayoutTagIndex } from './LayoutTagIndex'
|
||||
@@ -2,5 +2,6 @@
|
||||
* 直接将./NEXT 替换成对应的主题路径
|
||||
*/
|
||||
|
||||
export * from './NEXT' // 切换主题
|
||||
// export * from './Empty' // 切换主题
|
||||
// export * from './NEXT' // 切换主题
|
||||
export * from './Fukasawa'
|
||||
// export * from './Empty'
|
||||
|
||||
Reference in New Issue
Block a user