import CONFIG from './config'
import FloatDarkModeButton from './components/FloatDarkModeButton'
import Footer from './components/Footer'
import JumpToBottomButton from './components/JumpToBottomButton'
import JumpToTopButton from './components/JumpToTopButton'
import SideAreaLeft from './components/SideAreaLeft'
import SideAreaRight from './components/SideAreaRight'
import TopNav from './components/TopNav'
import { useGlobal } from '@/lib/global'
import { createContext, useContext, useEffect, useRef, useState } from 'react'
import BlogPostListScroll from './components/BlogPostListScroll'
import BlogPostListPage from './components/BlogPostListPage'
import StickyBar from './components/StickyBar'
import { isBrowser } from '@/lib/utils'
import TocDrawerButton from './components/TocDrawerButton'
import TocDrawer from './components/TocDrawer'
import { ArticleLock } from './components/ArticleLock'
import BlogPostArchive from './components/BlogPostArchive'
import TagItem from './components/TagItem'
import { useRouter } from 'next/router'
import ArticleDetail from './components/ArticleDetail'
import Link from 'next/link'
import BlogListBar from './components/BlogListBar'
import { Style } from './style'
import replaceSearchResult from '@/components/Mark'
import { siteConfig } from '@/lib/config'
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
import Announcement from './components/Announcement'
import Card from './components/Card'
import { LAYOUT_MAPPINGS } from '@/blog.config'
// 主题全局状态
const ThemeGlobalNext = createContext()
export const useNextGlobal = () => useContext(ThemeGlobalNext)
/**
* 基础布局 采用左中右三栏布局,移动端使用顶部导航栏
* @returns {JSX.Element}
* @constructor
*/
const LayoutBase = (props) => {
const { children, headerSlot, rightAreaSlot, post } = props
const targetRef = useRef(null)
const floatButtonGroup = useRef(null)
const [showRightFloat, switchShow] = useState(false)
const [percent, changePercent] = useState(0) // 页面阅读百分比
const scrollListener = () => {
const targetRef = document.getElementById('wrapper')
const clientHeight = targetRef?.clientHeight
const scrollY = window.pageYOffset
const fullHeight = clientHeight - window.outerHeight
let per = parseFloat(((scrollY / fullHeight * 100)).toFixed(0))
if (per > 100) per = 100
const shouldShow = scrollY > 100 && per > 0
if (shouldShow !== showRightFloat) {
switchShow(shouldShow)
}
changePercent(per)
}
useEffect(() => {
// facebook messenger 插件需要调整右下角悬浮按钮的高度
const fb = document.getElementsByClassName('fb-customerchat')
if (fb.length === 0) {
floatButtonGroup?.current?.classList.replace('bottom-24', 'bottom-12')
} else {
floatButtonGroup?.current?.classList.replace('bottom-12', 'bottom-24')
}
document.addEventListener('scroll', scrollListener)
return () => document.removeEventListener('scroll', scrollListener)
}, [showRightFloat])
// 悬浮抽屉
const drawerRight = useRef(null)
const floatSlot =
{
drawerRight?.current?.handleSwitchVisible()
}} />
const tocRef = isBrowser ? document.getElementById('article-wrapper') : null
const searchModal = useRef(null)
return (
{/* 移动端顶部导航栏 */}
<>{headerSlot}>
{/* 顶部黑线装饰 */}
{/* 主区 */}
{/* 左侧栏样式 */}
{/* 中央内容 */}
{/* 右侧栏样式 */}
{siteConfig('NEXT_RIGHT_BAR', null, CONFIG) && }
{/* 悬浮目录按钮 */}
{post &&
}
{/* 右下角悬浮 */}
{/* 页脚 */}
)
}
/**
* 首页
* 首页就是一个博客列表
* @param {*} props
* @returns
*/
const LayoutIndex = (props) => {
const { notice } = props
return <>
{/* 首页移动端顶部显示公告 */}
{siteConfig('POST_LIST_STYLE') !== 'page'
?
:
}
>
}
/**
* 博客列表
* @param {*} props
* @returns
*/
const LayoutPostList = (props) => {
return <>
{siteConfig('POST_LIST_STYLE') !== 'page'
?
:
}
>
}
/**
* 搜索
* @param {*} props
* @returns
*/
const LayoutSearch = (props) => {
const { locale } = useGlobal()
const { posts, keyword } = props
useEffect(() => {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,
target: {
element: 'span',
className: 'text-red-500 border-b border-dashed'
}
})
}
}, [])
return (
<>
{' '}
{posts?.length} {locale.COMMON.RESULT_OF_SEARCH}
{siteConfig('POST_LIST_STYLE') !== 'page'
?
:
}
>
)
}
/**
* 404
* @param {*} props
* @returns
*/
const Layout404 = props => {
const router = useRouter()
useEffect(() => {
// 延时3秒如果加载失败就返回首页
setTimeout(() => {
const article = isBrowser && document.getElementById('article-wrapper')
if (!article) {
router.push('/').then(() => {
// console.log('找不到页面', router.asPath)
})
}
}, 3000)
}, [])
return <>
>
}
/**
* 归档
* @param {*} props
* @returns
*/
const LayoutArchive = (props) => {
const { archivePosts } = props
return (
<>
{Object.keys(archivePosts).map(archiveTitle => (
))}
>
)
}
/**
* 文章详情
* @param {*} props
* @returns
*/
const LayoutSlug = (props) => {
const { post, lock, validPassword } = props
const router = useRouter()
useEffect(() => {
// 404
if (!post) {
setTimeout(() => {
if (isBrowser) {
const article = document.getElementById('notion-article')
if (!article) {
router.push('/404').then(() => {
console.warn('找不到页面', router.asPath)
})
}
}
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
}
}, [post])
return (
<>
{post && !lock && }
{post && lock && }
>
)
}
/**
* 分类列表
* @param {*} props
* @returns
*/
const LayoutCategoryIndex = (props) => {
const { allPosts, categoryOptions } = props
const { locale } = useGlobal()
return (
{locale.COMMON.CATEGORY}:
{categoryOptions?.map(category => {
return (
{category.name}({category.count})
)
})}
)
}
/**
* 标签列表
* @param {*} props
* @returns
*/
const LayoutTagIndex = (props) => {
const { tagOptions } = props
const { locale } = useGlobal()
return <>
>
}
/**
* 根据路径 获取对应的layout
* @param {*} path
* @returns
*/
const getLayoutNameByPath = (path) => {
// 检查特殊处理的路径
if (LAYOUT_MAPPINGS[path]) {
return LAYOUT_MAPPINGS[path];
} else {
// 没有特殊处理的路径返回默认layout名称
return 'LayoutSlug';
}
}
export {
CONFIG as THEME_CONFIG,
LayoutBase,
LayoutIndex,
LayoutSearch,
LayoutArchive,
LayoutSlug,
Layout404,
LayoutCategoryIndex,
LayoutPostList,
LayoutTagIndex,
getLayoutNameByPath
}