mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 23:16:49 +00:00
搜索按钮、菜单栏
This commit is contained in:
@@ -1,35 +1,17 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import PaginationNumber from './PaginationNumber'
|
||||
|
||||
export const BlogListPage = props => {
|
||||
const { page = 1, posts, postCount } = props
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const totalPage = Math.ceil(
|
||||
postCount / parseInt(siteConfig('POSTS_PER_PAGE'))
|
||||
)
|
||||
const currentPage = +page
|
||||
|
||||
const showPrev = currentPage > 1
|
||||
const showNext = page < totalPage
|
||||
const pagePrefix = router.asPath
|
||||
.split('?')[0]
|
||||
.replace(/\/page\/[1-9]\d*/, '')
|
||||
.replace(/\/$/, '')
|
||||
|
||||
const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG)
|
||||
const { posts } = props
|
||||
const showPageCover = siteConfig('MOVIE_POST_LIST_COVER', null, CONFIG)
|
||||
|
||||
return (
|
||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'} mb-12`}>
|
||||
<div
|
||||
id="posts-wrapper"
|
||||
className="grid md:grid-cols-2 md:gap-12 lg:grid-cols-3 lg:gap-20 xl:gap-24 2xl:grid-cols-4"
|
||||
>
|
||||
id='posts-wrapper'
|
||||
className='grid md:grid-cols-2 md:gap-12 lg:grid-cols-3 lg:gap-20 xl:gap-24 2xl:grid-cols-4'>
|
||||
{posts?.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} />
|
||||
))}
|
||||
|
||||
@@ -1,42 +1,36 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import CONFIG from '../config'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { formatDateFmt } from '@/lib/utils/formatDate'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
// 主题默认强制显示图片
|
||||
if (post && !post.pageCoverThumbnail) {
|
||||
post.pageCoverThumbnail =
|
||||
siteInfo?.pageCover || siteConfig('RANDOM_IMAGE_URL')
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover || siteConfig('RANDOM_IMAGE_URL')
|
||||
}
|
||||
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<article className="w-full mb-4 cursor-pointer overflow-hidden shadow-movie dark:bg-hexo-black-gray">
|
||||
<article className='w-full mb-4 cursor-pointer overflow-hidden shadow-movie dark:bg-hexo-black-gray'>
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className="group flex flex-col aspect-[2/3] justify-between relative">
|
||||
<div className='group flex flex-col aspect-[2/3] justify-between relative'>
|
||||
{/* 图片 填充卡片 */}
|
||||
<div className="flex flex-grow w-full h-full relative duration-200 cursor-pointer transform overflow-hidden">
|
||||
<div className='flex flex-grow w-full h-full relative duration-200 cursor-pointer transform overflow-hidden'>
|
||||
<LazyImage
|
||||
src={post?.pageCoverThumbnail}
|
||||
alt={post.title}
|
||||
className="h-full w-full group-hover:scale-105 transform object-cover duration-500"
|
||||
className='h-full w-full group-hover:scale-105 transform object-cover duration-500'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-32 z-20">
|
||||
<div className='absolute bottom-32 z-20'>
|
||||
{post?.tagItems && post?.tagItems.length > 0 && (
|
||||
<>
|
||||
<div className="px-6 justify-between flex p-2">
|
||||
<div className='px-6 justify-between flex p-2'>
|
||||
{post.tagItems.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
@@ -45,13 +39,13 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
)}
|
||||
</div>
|
||||
{/* 阴影遮罩 */}
|
||||
<h2 className="absolute bottom-10 px-6 transition-all duration-200 text-white text-2xl font-semibold break-words shadow-text z-20">
|
||||
<h2 className='absolute bottom-10 px-6 transition-all duration-200 text-white text-2xl font-semibold break-words shadow-text z-20'>
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
{post.title}
|
||||
</h2>
|
||||
|
||||
<div className="h-3/4 w-full absolute left-0 bottom-0 z-10">
|
||||
<div className="h-full w-full absolute opacity-80 group-hover:opacity-100 transition-all duration-1000 bg-gradient-to-b from-transparent to-black"></div>
|
||||
<div className='h-3/4 w-full absolute left-0 bottom-0 z-10'>
|
||||
<div className='h-full w-full absolute opacity-80 group-hover:opacity-100 transition-all duration-1000 bg-gradient-to-b from-transparent to-black'></div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -1,26 +1,163 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuListTop } from './MenuListTop'
|
||||
import { useMovieGlobal } from '..'
|
||||
import Collapse from '@/components/Collapse'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
/**
|
||||
* 网站顶部
|
||||
* @returns
|
||||
*/
|
||||
export const Header = props => {
|
||||
const { collapseRef, searchModal } = useMovieGlobal()
|
||||
const router = useRouter()
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [showSearch, setShowSearch] = useState(false)
|
||||
const toggleMenuOpen = () => {
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
let links = [
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
to: '/',
|
||||
show: siteConfig('MOVIE_MENU_INDEX', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
show: siteConfig('MOVIE_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
show: siteConfig('MOVIE_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
links = links.concat(customNav)
|
||||
}
|
||||
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
if (links[i].id !== i) {
|
||||
links[i].id = i
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 开启自定义菜单,则覆盖Page生成的菜单
|
||||
if (siteConfig('CUSTOM_MENU')) {
|
||||
links = customMenu
|
||||
}
|
||||
|
||||
// 展示搜索框
|
||||
const toggleShowSearchInput = () => {
|
||||
if (siteConfig('ALGOLIA_APP_ID')) {
|
||||
searchModal.current.openSearch()
|
||||
} else {
|
||||
setShowSearch(!showSearch)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (showSearch) {
|
||||
setTimeout(() => {
|
||||
document.getElementById('search').focus()
|
||||
}, 100);
|
||||
}
|
||||
}, [showSearch])
|
||||
|
||||
const onKeyUp = e => {
|
||||
if (e.keyCode === 13) {
|
||||
const search = document.getElementById('search').value
|
||||
if (search) {
|
||||
router.push({ pathname: '/search/' + search })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
const search = document.getElementById('search').value
|
||||
if (search) {
|
||||
router.push({ pathname: '/search/' + search })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="w-full px-8 relative z-20">
|
||||
<div className="mx-auto md:flex justify-between items-center">
|
||||
<>
|
||||
<header className='w-full px-8 relative z-20 flex lg:flex-row md:flex-col justify-between items-center'>
|
||||
<Link
|
||||
href="/"
|
||||
className="py-6 w-full text-3xl font-bold text-center md:text-left md:w-auto text-gray-dark no-underline flex justify-center items-center"
|
||||
>
|
||||
href='/'
|
||||
className='whitespace-nowrap py-6 text-2xl md:text-3xl font-bold text-gray-dark no-underline flex items-center'>
|
||||
{siteConfig('TITLE')}
|
||||
</Link>
|
||||
<div className="w-full md:w-auto text-center md:text-right">
|
||||
|
||||
<div className='md:w-auto text-center flex'>
|
||||
{/* 右侧菜单 */}
|
||||
<MenuListTop {...props} />
|
||||
<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center w-full hidden md:flex'>
|
||||
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
|
||||
</nav>
|
||||
|
||||
<div onClick={toggleShowSearchInput} className='flex items-center cursor-pointer'>
|
||||
<i className='fas fa-search dark:text-white'></i>
|
||||
</div>
|
||||
<div className={`${showSearch ? 'top-16 visible opacity-100' : 'top-10 invisible opacity-0'} duration-200 transition-all max-w-md absolute w-80 right-4 p-2 flex flex-col gap-2`}>
|
||||
<input
|
||||
autoFocus
|
||||
id='search'
|
||||
onClick={toggleShowSearchInput}
|
||||
onKeyUp={onKeyUp}
|
||||
className='float-left w-full outline-none h-full outline-blue-200 p-2 rounded text-black bg-gray-100'
|
||||
aria-label='Submit search'
|
||||
type='search'
|
||||
name='s'
|
||||
autoComplete='off'
|
||||
placeholder='Type then hit enter to search...'
|
||||
/>
|
||||
<button onClick={handleSearch} className='w-full bg-[#383838] rounded py-2'>{locale.COMMON.SEARCH} 搜索</button>
|
||||
</div>
|
||||
|
||||
{/* 移动端按钮 */}
|
||||
<div className='md:hidden'>
|
||||
<div onClick={toggleMenuOpen} className='w-8 cursor-pointer'>
|
||||
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
|
||||
<Collapse collapseRef={collapseRef} type='vertical' isOpen={isOpen}>
|
||||
{/* 移动端菜单 */}
|
||||
<menu id='nav-menu-mobile' className='block md:hidden my-auto justify-start'>
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link &&
|
||||
link.show && (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={param => collapseRef.current?.updateCollapseHeight(param)}
|
||||
key={index}
|
||||
link={link}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</menu>
|
||||
</Collapse>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
77
themes/movie/components/MenuItemCollapse.js
Normal file
77
themes/movie/components/MenuItemCollapse.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import Collapse from '@/components/Collapse'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
/**
|
||||
* 折叠菜单
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const MenuItemCollapse = props => {
|
||||
const { link } = props
|
||||
const [show, changeShow] = useState(false)
|
||||
const hasSubMenu = link?.subMenus?.length > 0
|
||||
|
||||
const [isOpen, changeIsOpen] = useState(false)
|
||||
|
||||
const toggleShow = () => {
|
||||
changeShow(!show)
|
||||
}
|
||||
|
||||
const toggleOpenSubMenu = () => {
|
||||
changeIsOpen(!isOpen)
|
||||
}
|
||||
|
||||
if (!link || !link.show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='select-none w-full px-6 py-2 text-left ' onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
|
||||
className='flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest'>
|
||||
<span className=' transition-all items-center duration-200'>
|
||||
{link?.icon && <i className={link.icon + ' mr-4'} />}
|
||||
{link?.name}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
{hasSubMenu && (
|
||||
<div
|
||||
onClick={hasSubMenu ? toggleOpenSubMenu : null}
|
||||
className='flex items-center justify-between pl-2 pr-4 cursor-pointer dark:text-gray-200 no-underline tracking-widest'>
|
||||
<span className='transition-all items-center duration-200'>
|
||||
{link?.icon && <i className={link.icon + ' mr-4'} />}
|
||||
{link?.name}
|
||||
</span>
|
||||
<i
|
||||
className={`select-none px-2 fas fa-chevron-left transition-all duration-200 ${isOpen ? '-rotate-90' : ''} text-gray-400`}></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 折叠子菜单 */}
|
||||
{hasSubMenu && (
|
||||
<Collapse isOpen={isOpen} onHeightChange={props.onHeightChange} className='rounded-xl'>
|
||||
{link.subMenus.map((sLink, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className='dark:text-gray-200 text-left px-3 justify-start tracking-widest transition-all duration-200 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
|
||||
<span className='text-sm ml-4 whitespace-nowrap'>
|
||||
{link?.icon && <i className={sLink.icon + ' mr-2'} />} {sLink.title}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<Link
|
||||
href={link?.to}
|
||||
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
|
||||
className=" menu-link pl-2 pr-4 no-underline tracking-widest pb-1"
|
||||
className="select-none menu-link pl-2 pr-4 no-underline tracking-widest pb-1"
|
||||
>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
{hasSubMenu && <i className="px-2 fa fa-angle-down"></i>}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
export const MenuListTop = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
to: '/',
|
||||
show: siteConfig('HEXO_MENU_INDEX', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
links = links.concat(customNav)
|
||||
}
|
||||
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
if (links[i].id !== i) {
|
||||
links[i].id = i
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 开启自定义菜单,则覆盖Page生成的菜单
|
||||
if (siteConfig('CUSTOM_MENU')) {
|
||||
links = customMenu
|
||||
}
|
||||
|
||||
if (!links || links.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav id="nav-mobile" className="leading-8 justify-center w-full flex">
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link && link.show && <MenuItemDrop key={index} link={link} />
|
||||
)}
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
/**
|
||||
* 菜单导航
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export const Nav = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
show: siteConfig('EXAMPLE_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
show: siteConfig('EXAMPLE_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
show: siteConfig('EXAMPLE_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
show: siteConfig('EXAMPLE_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
links = links.concat(customNav)
|
||||
}
|
||||
|
||||
// 如果 开启自定义菜单,则不再使用 Page生成菜单。
|
||||
if (siteConfig('CUSTOM_MENU')) {
|
||||
links = customMenu
|
||||
}
|
||||
|
||||
if (!links || links.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="w-full bg-white md:pt-0 px-6 relative z-30 border-t border-b border-gray-light dark:border-hexo-black-gray dark:bg-black">
|
||||
<div className="container mx-auto max-w-4xl md:flex justify-between items-center text-sm md:text-md md:justify-start">
|
||||
<ul className="w-full text-center md:text-left flex flex-wrap justify-center items-stretch md:justify-start md:items-start">
|
||||
{links.map((link, index) => (
|
||||
<MenuItemDrop key={index} link={link} />
|
||||
))}
|
||||
</ul>
|
||||
{/* <div className="w-full md:w-1/3 text-center md:text-right"> */}
|
||||
{/* <!-- extra links --> */}
|
||||
{/* </div> */}
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -3,12 +3,12 @@
|
||||
*/
|
||||
const CONFIG = {
|
||||
// 菜单配置
|
||||
EXAMPLE_MENU_CATEGORY: true, // 显示分类
|
||||
EXAMPLE_MENU_TAG: true, // 显示标签
|
||||
EXAMPLE_MENU_ARCHIVE: true, // 显示归档
|
||||
EXAMPLE_MENU_SEARCH: true, // 显示搜索
|
||||
MOVIE_MENU_CATEGORY: true, // 显示分类
|
||||
MOVIE_MENU_TAG: true, // 显示标签
|
||||
MOVIE_MENU_ARCHIVE: true, // 显示归档
|
||||
MOVIE_MENU_SEARCH: true, // 显示搜索
|
||||
|
||||
EXAMPLE_POST_LIST_COVER: true // 列表显示文章封面
|
||||
MOVIE_POST_LIST_COVER: true // 列表显示文章封面
|
||||
|
||||
}
|
||||
export default CONFIG
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import CONFIG from './config'
|
||||
import { createContext, useContext, useEffect, useRef } from 'react'
|
||||
import { Header } from './components/Header'
|
||||
import { Nav } from './components/Nav'
|
||||
import { Footer } from './components/Footer'
|
||||
import { Title } from './components/Title'
|
||||
import { SideBar } from './components/SideBar'
|
||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||
import Comment from '@/components/Comment'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||
import ArchiveDateList from './components/ArchiveDateList'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import BlogListGroupByDate from './components/BlogListGroupByDate'
|
||||
import { BlogListPage } from './components/BlogListPage'
|
||||
import { BlogListScroll } from './components/BlogListScroll'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Comment from '@/components/Comment'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import BlogListGroupByDate from './components/BlogListGroupByDate'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import TagItem from './components/TagItem'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import { Style } from './style'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||
import CategoryGroup from './components/CategoryGroup'
|
||||
import { formatDateFmt } from '@/lib/utils/formatDate'
|
||||
import ArchiveDateList from './components/ArchiveDateList'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import { Footer } from './components/Footer'
|
||||
import { Header } from './components/Header'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||
import TagGroups from './components/TagGroups'
|
||||
import TagItem from './components/TagItem'
|
||||
import CONFIG from './config'
|
||||
import { Style } from './style'
|
||||
|
||||
// 主题全局状态
|
||||
const ThemeGlobalMovie = createContext()
|
||||
@@ -47,56 +42,44 @@ export const useMovieGlobal = () => useContext(ThemeGlobalMovie)
|
||||
const LayoutBase = props => {
|
||||
const { children, slotTop } = props
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
const router = useRouter()
|
||||
const { category, tag } = props
|
||||
const searchModal = useRef(null)
|
||||
const collapseRef = useRef(null)
|
||||
|
||||
// 增加一个状态以触发 Transition 组件的动画
|
||||
// const [showTransition, setShowTransition] = useState(true)
|
||||
// useEffect(() => {
|
||||
// // 当 location 或 children 发生变化时,触发动画
|
||||
// setShowTransition(false)
|
||||
// setTimeout(() => setShowTransition(true), 5)
|
||||
// }, [onLoading])
|
||||
const searchModal = useRef(null)
|
||||
const [expandMenu, updateExpandMenu] = useState(false)
|
||||
|
||||
return (
|
||||
<ThemeGlobalMovie.Provider value={{ searchModal }}>
|
||||
<ThemeGlobalMovie.Provider value={{ searchModal, expandMenu, updateExpandMenu, collapseRef }}>
|
||||
<div
|
||||
id="theme-movie"
|
||||
className={`${siteConfig('FONT_STYLE')} dark:text-gray-300 bg-white dark:bg-[#2A2A2A] scroll-smooth min-h-screen flex flex-col justify-between`}
|
||||
>
|
||||
id='theme-movie'
|
||||
className={`${siteConfig('FONT_STYLE')} dark:text-gray-300 duration-300 transition-all bg-white dark:bg-[#2A2A2A] scroll-smooth min-h-screen flex flex-col justify-between`}>
|
||||
<Style />
|
||||
|
||||
{/* 页头 */}
|
||||
<Header {...props} />
|
||||
|
||||
{/* 主体 */}
|
||||
<div id="container-inner" className="w-full relative z-10">
|
||||
<div id='container-inner' className='w-full relative z-10'>
|
||||
{/* 标题栏 */}
|
||||
{/* {fullWidth ? null : <Title {...props} />} */}
|
||||
|
||||
<div
|
||||
id="container-wrapper"
|
||||
id='container-wrapper'
|
||||
className={
|
||||
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
||||
? 'flex-row-reverse'
|
||||
: '') +
|
||||
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') +
|
||||
'relative mx-auto justify-center md:flex items-start py-8 px-2'
|
||||
}
|
||||
>
|
||||
}>
|
||||
{/* 内容 */}
|
||||
<div className={`w-full ${fullWidth ? '' : ''} xl:px-32 lg:px-4`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
enter="transition ease-in-out duration-700 transform order-first"
|
||||
enterFrom="opacity-0 translate-y-16"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 -translate-y-16"
|
||||
unmount={false}
|
||||
>
|
||||
enter='transition ease-in-out duration-700 transform order-first'
|
||||
enterFrom='opacity-0 translate-y-16'
|
||||
enterTo='opacity-100'
|
||||
leave='transition ease-in-out duration-300 transform'
|
||||
leaveFrom='opacity-100 translate-y-0'
|
||||
leaveTo='opacity-0 -translate-y-16'
|
||||
unmount={false}>
|
||||
{/* 嵌入模块 */}
|
||||
{slotTop}
|
||||
{children}
|
||||
@@ -112,7 +95,7 @@ const LayoutBase = props => {
|
||||
<AlgoliaSearchModal cRef={searchModal} {...props} />
|
||||
|
||||
{/* 回顶按钮 */}
|
||||
<div className="fixed right-4 bottom-4 z-10">
|
||||
<div className='fixed right-4 bottom-4 z-10'>
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,15 +118,7 @@ const LayoutIndex = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutPostList = props => {
|
||||
return (
|
||||
<>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogListPage {...props} />
|
||||
) : (
|
||||
<BlogListScroll {...props} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
return <>{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}</>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,16 +149,18 @@ const LayoutSlug = props => {
|
||||
}, [post])
|
||||
return (
|
||||
<>
|
||||
{lock ? (
|
||||
<ArticleLock validPassword={validPassword} />
|
||||
) : (
|
||||
<div id="article-wrapper" className="px-2">
|
||||
{!lock
|
||||
? (
|
||||
<div id='article-wrapper' className='px-2'>
|
||||
<ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post} />
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
: (
|
||||
<ArticleLock validPassword={validPassword} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -215,27 +192,27 @@ const Layout404 = props => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-52">
|
||||
<h2 className="text-4xl">{locale.COMMON.NO_RESULTS_FOUND}</h2>
|
||||
<hr className="my-4" />
|
||||
<div className="max-w-md relative">
|
||||
<div className='h-52'>
|
||||
<h2 className='text-4xl'>{locale.COMMON.NO_RESULTS_FOUND}</h2>
|
||||
<hr className='my-4' />
|
||||
<div className='max-w-md relative'>
|
||||
<input
|
||||
autoFocus
|
||||
id="search"
|
||||
id='search'
|
||||
onClick={toggleShowSearchInput}
|
||||
onKeyUp={onKeyUp}
|
||||
className="float-left w-full outline-none h-full p-2 rounded dark:bg-[#383838] bg-gray-100"
|
||||
aria-label="Submit search"
|
||||
type="search"
|
||||
name="s"
|
||||
autoComplete="off"
|
||||
placeholder="Type then hit enter to search..."
|
||||
className='float-left w-full outline-none h-full p-2 rounded dark:bg-[#383838] bg-gray-100'
|
||||
aria-label='Submit search'
|
||||
type='search'
|
||||
name='s'
|
||||
autoComplete='off'
|
||||
placeholder='Type then hit enter to search...'
|
||||
/>
|
||||
<i className="fas fa-search absolute right-0 my-auto p-2"></i>
|
||||
<i className='fas fa-search absolute right-0 my-auto p-2'></i>
|
||||
</div>
|
||||
</div>
|
||||
{/* 底部导航 */}
|
||||
<div className="h-full flex-grow grid grid-cols-4 gap-4">
|
||||
<div className='h-full flex-grow grid grid-cols-4 gap-4'>
|
||||
<LatestPostsGroup {...props} />
|
||||
<CategoryGroup {...props} />
|
||||
<ArchiveDateList {...props} />
|
||||
@@ -282,13 +259,9 @@ const LayoutArchive = props => {
|
||||
const { archivePosts } = props
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10 pb-20 md:py-12 p-3 min-h-screen w-full">
|
||||
<div className='mb-10 pb-20 md:py-12 p-3 min-h-screen w-full'>
|
||||
{Object.keys(archivePosts).map(archiveTitle => (
|
||||
<BlogListGroupByDate
|
||||
key={archiveTitle}
|
||||
archiveTitle={archiveTitle}
|
||||
archivePosts={archivePosts}
|
||||
/>
|
||||
<BlogListGroupByDate key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
@@ -304,7 +277,7 @@ const LayoutCategoryIndex = props => {
|
||||
const { categoryOptions } = props
|
||||
return (
|
||||
<>
|
||||
<div id="category-list" className="duration-200 flex flex-wrap">
|
||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||
{categoryOptions?.map(category => (
|
||||
<CategoryItem key={category.name} category={category} />
|
||||
))}
|
||||
@@ -322,7 +295,7 @@ const LayoutTagIndex = props => {
|
||||
const { tagOptions } = props
|
||||
return (
|
||||
<>
|
||||
<div id="tags-list" className="duration-200 flex flex-wrap">
|
||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||
{tagOptions.map(tag => (
|
||||
<TagItem key={tag.name} tag={tag} />
|
||||
))}
|
||||
@@ -332,14 +305,14 @@ const LayoutTagIndex = props => {
|
||||
}
|
||||
|
||||
export {
|
||||
CONFIG as THEME_CONFIG,
|
||||
LayoutBase,
|
||||
LayoutIndex,
|
||||
LayoutSearch,
|
||||
LayoutArchive,
|
||||
LayoutSlug,
|
||||
Layout404,
|
||||
LayoutPostList,
|
||||
LayoutArchive,
|
||||
LayoutBase,
|
||||
LayoutCategoryIndex,
|
||||
LayoutTagIndex
|
||||
LayoutIndex,
|
||||
LayoutPostList,
|
||||
LayoutSearch,
|
||||
LayoutSlug,
|
||||
LayoutTagIndex,
|
||||
CONFIG as THEME_CONFIG
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user