group-nav-page

This commit is contained in:
tangly1024
2023-06-23 23:35:03 +08:00
parent b5c1752666
commit c14ca330eb
16 changed files with 182 additions and 219 deletions

View File

@@ -1,27 +1,25 @@
import BLOG from '@/blog.config'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'
const BlogPostCard = ({ post, showSummary }) => {
const BlogPostCard = ({ post }) => {
const router = useRouter()
const currentSelected = router.asPath.split('?')[0] === '/' + post.slug
return (
<div
key={post.id}
className="mb-6"
>
<div key={post.id} className="py-0.5">
<div className="flex flex-col w-full">
<Link
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className={
'cursor-pointer font-bold hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-green-500 dark:hover:text-green-400'
`${currentSelected ? 'bg-gray-500 text-white' : 'text-gray-700 dark:text-gray-300 '} hover:font-bold py-0.5 px-1 text-sm cursor-pointer`
}>
<div>
{post.category} - {post.title}
</div>
</Link>
</div>
</div>
)

View File

@@ -1,9 +1,6 @@
import BLOG from '@/blog.config'
import BlogPostCard from './BlogPostCard'
import BlogPostListEmpty from './BlogPostListEmpty'
import throttle from 'lodash.throttle'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import React, { useRef } from 'react'
/**
* 博客列表滚动分页
@@ -13,85 +10,19 @@ import { useRouter } from 'next/router'
* @constructor
*/
const BlogPostListScroll = ({ posts = [], currentSearch }) => {
const postsPerPage = BLOG.POSTS_PER_PAGE
const [page, updatePage] = useState(1)
let filteredPosts = Object.assign(posts)
const router = useRouter()
const searchKey = getSearchKey(router)
if (searchKey) {
filteredPosts = posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const searchContent = post.title + post.summary + tagContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
}
const postsToShow = getPostByPage(page, filteredPosts, postsPerPage)
let hasMore = false
if (filteredPosts) {
const totalCount = filteredPosts.length
hasMore = page * postsPerPage < totalCount
}
const handleGetMore = () => {
if (!hasMore) return
updatePage(page + 1)
}
// 监听滚动自动分页加载
const scrollTrigger = useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
// 监听滚动
useEffect(() => {
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
})
const targetRef = useRef(null)
const filteredPosts = Object.assign(posts)
if (!postsToShow || postsToShow.length === 0) {
if (!filteredPosts || filteredPosts.length === 0) {
return <BlogPostListEmpty currentSearch={currentSearch} />
} else {
return <div id='container' ref={targetRef} className='w-full'>
{/* 文章列表 */}
<div className='space-y-1 lg:space-y-4'>
{postsToShow?.map(post => (
<BlogPostCard key={post.id} post={post} showSummary={true} />
))}
</div>
</div>
{/* 文章列表 */}
{filteredPosts?.map(post => (
<BlogPostCard key={post.id} post={post} showSummary={true} />
))}
</div>
}
}
/**
* 获取从第1页到指定页码的文章
* @param page 第几页
* @param totalPosts 所有文章
* @param postsPerPage 每页文章数量
* @returns {*}
*/
const getPostByPage = function (page, totalPosts, postsPerPage) {
return totalPosts.slice(
0,
postsPerPage * page
)
}
function getSearchKey(router) {
if (router.query && router.query.s) {
return router.query.s
}
return null
}
export default BlogPostListScroll

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import throttle from 'lodash.throttle'
import { uuidToId } from 'notion-utils'
import Progress from './Progress'
import { isBrowser } from '@/lib/utils'
/**
* 目录导航组件
@@ -9,11 +9,9 @@ import Progress from './Progress'
* @returns {JSX.Element}
* @constructor
*/
const Catalog = ({ toc }) => {
const Catalog = ({ post }) => {
const tocIds = []
// 目录自动滚动
const tRef = useRef(null)
const toc = post?.toc
// 同步选中目录事件
const [activeSection, setActiveSection] = useState(null)
@@ -24,7 +22,7 @@ const Catalog = ({ toc }) => {
return () => {
window.removeEventListener('scroll', actionSectionScrollSpy)
}
}, [])
}, [post])
const throttleMs = 200
const actionSectionScrollSpy = useCallback(throttle(() => {
@@ -51,19 +49,18 @@ const Catalog = ({ toc }) => {
}
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
if (isBrowser()) {
document?.getElementById('toc-wrapper')?.scrollTo({ top: 28 * index, behavior: 'smooth' })
}
}, throttleMs))
// 无目录就直接返回空
if (!toc || toc.length < 1) {
return <></>
return null
}
return <div className='px-3'>
<div className='w-full mt-2 mb-4'>
<Progress />
</div>
<div className='overflow-y-auto max-h-96 overscroll-none scroll-hidden' ref={tRef}>
return <>
<div id='toc-wrapper' className='overflow-y-auto max-h-96 overscroll-none scroll-hidden'>
<nav className='h-full text-black'>
{toc.map((tocItem) => {
const id = uuidToId(tocItem.id)
@@ -76,7 +73,7 @@ const Catalog = ({ toc }) => {
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}
>
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }}
className={`${activeSection === id && ' font-bold text-green-500 underline'}`}
className={`${activeSection === id && ' font-bold text-gray-500 underline'}`}
>
{tocItem.text}
</span>
@@ -85,7 +82,7 @@ const Catalog = ({ toc }) => {
})}
</nav>
</div>
</div>
</>
}
export default Catalog

View File

@@ -5,7 +5,7 @@ import DarkModeButton from '@/components/DarkModeButton'
const Footer = ({ title }) => {
const d = new Date()
const currentYear = d.getFullYear()
const copyrightDate = (function() {
const copyrightDate = (function () {
if (Number.isInteger(BLOG.SINCE) && BLOG.SINCE < currentYear) {
return BLOG.SINCE + '-' + currentYear
}
@@ -13,22 +13,26 @@ const Footer = ({ title }) => {
})()
return (
<footer
className='z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center mx-auto w-full leading-6 text-sm px-6 pb-6 relative'
>
<DarkModeButton/>
<i className='fas fa-copyright' /> {`${copyrightDate}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br/>
<footer
className='max-w-3xl z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center mx-auto w-full leading-6 text-sm px-6 pb-6 relative'
>
<hr />
{BLOG.BEI_AN && <><i className='fas fa-shield-alt'/> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br/></>}
<div className='flex justify-between'>
<div className='flex'>© {`${copyrightDate}`} <div><i className='mx-1 animate-pulse fas fa-heart' /> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br /></div></div>
<div className='text-xs font-serif'><a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {BLOG.VERSION}</a></div>
</div>
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye'/><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users'/> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br/>
<h1>{title}</h1>
<span className='text-xs font-serif'>Powered by <a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {BLOG.VERSION}</a>.</span></span>
</footer>
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br /></>}
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye' /><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users' /> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br />
<h1>{title}</h1>
</footer>
)
}

View File

@@ -17,7 +17,7 @@ export const MenuItemDrop = ({ link }) => {
return <li className='cursor-pointer list-none items-center flex mx-2' onMouseOver={() => changeShow(true)} onMouseOut={() => changeShow(false)} >
{hasSubMenu &&
<div className={'px-3 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
<div className={'px-2 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<div>
{link?.icon && <i className={link?.icon} />} {link?.name}
@@ -27,7 +27,7 @@ export const MenuItemDrop = ({ link }) => {
}
{!hasSubMenu &&
<div className={'px-3 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
<div className={'px-2 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<Link href={link?.to}>
{link?.icon && <i className={link?.icon} />} {link?.name}

View File

@@ -7,7 +7,7 @@ export default function RevolverMaps () {
initRevolverMaps()
changeLoad(true)
}
})
}, [])
return <div id="revolvermaps" className='p-4'/>
}

View File

@@ -1,11 +1,11 @@
import { useRouter } from 'next/router'
import { useImperativeHandle, useRef, useState } from 'react'
import { useMediumGlobal } from '../LayoutBase'
let lock = false
const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
const [onLoading, setLoadingState] = useState(false)
const router = useRouter()
const SearchInput = ({ currentSearch, cRef, className }) => {
const searchInputRef = useRef()
const { setFilterPosts, allNavPages } = useMediumGlobal()
useImperativeHandle(cRef, () => {
return {
focus: () => {
@@ -15,15 +15,43 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
})
const handleSearch = () => {
const key = searchInputRef.current.value
if (key && key !== '') {
setLoadingState(true)
location.href = '/search/' + key
let keyword = searchInputRef.current.value
const filterPosts = []
if (keyword) {
keyword = keyword.trim()
} else {
router.push({ pathname: '/' }).then(r => {
})
setFilterPosts(allNavPages)
}
for (const post of allNavPages) {
const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : ''
const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : ''
const articleInfo = post.title + post.summary + tagContent + categoryContent
let hit = articleInfo.toLowerCase().indexOf(keyword) > -1
const indexContent = [post.summary]
// console.log('全文搜索缓存', cacheKey, page != null)
post.results = []
let hitCount = 0
for (const i in indexContent) {
const c = indexContent[i]
if (!c) {
continue
}
const index = c.toLowerCase().indexOf(keyword.toLowerCase())
if (index > -1) {
hit = true
hitCount += 1
post.results.push(c)
} else {
if ((post.results.length - 1) / hitCount < 3 || i === 0) {
post.results.push(c)
}
}
}
if (hit) {
filterPosts.push(post)
}
}
setFilterPosts(filterPosts)
}
const handleKeyUp = (e) => {
if (e.keyCode === 13) { // 回车
@@ -72,7 +100,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
<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 dark:hover:text-gray-300 cursor-pointer fas ${onLoading ? 'fa-spinner animate-spin' : 'fa-search'} `} />
<i className={'hover:text-black transform duration-200 text-gray-500 dark:hover:text-gray-300 cursor-pointer fas fa-search'} />
</div>
{(showClean &&

View File

@@ -6,6 +6,7 @@ import { useGlobal } from '@/lib/global'
import CONFIG_MEDIUM from '../config_medium'
import BLOG from '@/blog.config'
import { MenuItemDrop } from './MenuItemDrop'
import DarkModeButton from '@/components/DarkModeButton'
/**
* 顶部导航栏 + 菜单
@@ -67,6 +68,7 @@ export default function TopNavBar(props) {
{/* 桌面端顶部菜单 */}
<div className='hidden md:flex'>
{links && links?.map(link => <MenuItemDrop key={link?.id} link={link}/>)}
<DarkModeButton className='text-sm flex items-center h-full' />
</div>
</div>
</div>