mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 23:16:47 +00:00
theme-game 部分样式
This commit is contained in:
@@ -76,9 +76,13 @@ nav {
|
||||
}
|
||||
|
||||
.shadow-card {
|
||||
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px, rgba(0, 0, 0, 0.07) 0px 2px 4px,
|
||||
rgba(0, 0, 0, 0.07) 0px 4px 8px, rgba(0, 0, 0, 0.07) 0px 8px 16px,
|
||||
rgba(0, 0, 0, 0.07) 0px 16px 32px, rgba(0, 0, 0, 0.07) 0px 32px 64px;
|
||||
box-shadow:
|
||||
rgba(0, 0, 0, 0.07) 0px 1px 2px,
|
||||
rgba(0, 0, 0, 0.07) 0px 2px 4px,
|
||||
rgba(0, 0, 0, 0.07) 0px 4px 8px,
|
||||
rgba(0, 0, 0, 0.07) 0px 8px 16px,
|
||||
rgba(0, 0, 0, 0.07) 0px 16px 32px,
|
||||
rgba(0, 0, 0, 0.07) 0px 32px 64px;
|
||||
}
|
||||
|
||||
.gt-meta {
|
||||
@@ -106,7 +110,6 @@ nav {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
|
||||
.medium-zoom-overlay {
|
||||
background: none !important;
|
||||
/* background: rgba(0, 0, 0, 0.01) none repeat scroll 0% 0% !important; */
|
||||
@@ -157,7 +160,7 @@ nav {
|
||||
|
||||
/* twikoo 评论区超链接样式 */
|
||||
.tk-main a {
|
||||
@apply text-blue-700
|
||||
@apply text-blue-700;
|
||||
}
|
||||
|
||||
/* twikoo 内置的 element-ui 加载样式 */
|
||||
@@ -167,7 +170,7 @@ nav {
|
||||
|
||||
/* Webmention style */
|
||||
.webmention-block {
|
||||
background: rgba(0, 116, 222, .2);
|
||||
background: rgba(0, 116, 222, 0.2);
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -176,11 +179,11 @@ nav {
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
margin-bottom: .5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.webmention-block-intro a {
|
||||
color: #0000EE;
|
||||
color: #0000ee;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -197,14 +200,14 @@ nav {
|
||||
|
||||
.webmention-counts .count {
|
||||
font-weight: bold;
|
||||
margin-right: .2rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
/* .webmention-counts .counts > span {
|
||||
margin-right: .8rem;
|
||||
} */
|
||||
.webmention-counts .counts>span:not(:last-child):after {
|
||||
content: " • ";
|
||||
.webmention-counts .counts > span:not(:last-child):after {
|
||||
content: ' • ';
|
||||
}
|
||||
|
||||
a.avatar-wrapper {
|
||||
@@ -221,7 +224,7 @@ a.avatar-wrapper {
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
margin: 0;
|
||||
border: 3px solid rgba(0, 116, 222, .5);
|
||||
border: 3px solid rgba(0, 116, 222, 0.5);
|
||||
}
|
||||
|
||||
.replies {
|
||||
@@ -235,7 +238,7 @@ a.avatar-wrapper {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
align-items: flex-start;
|
||||
margin-top: .6rem;
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.reply p {
|
||||
@@ -255,4 +258,9 @@ a.avatar-wrapper {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.writing-vertical {
|
||||
writing-mode: vertical-rl; /* 竖向排列从右向左 */
|
||||
text-orientation: upright; /* 文字方向正常 */
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleFooter = props => {
|
||||
const router = useRouter()
|
||||
const { locale } = useGlobal()
|
||||
|
||||
return <div className="flex justify-between font-medium text-gray-500 dark:text-gray-400">
|
||||
<a>
|
||||
<button
|
||||
onClick={() => router.push('/')}
|
||||
className="mt-2 cursor-pointer hover:text-black dark:hover:text-gray-100"
|
||||
>
|
||||
← {locale.POST.BACK}
|
||||
</button>
|
||||
</a>
|
||||
<a>
|
||||
<button
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||
className="mt-2 cursor-pointer hover:text-black dark:hover:text-gray-100"
|
||||
>
|
||||
↑ {locale.POST.TOP}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
import Image from 'next/image'
|
||||
import TagItem from './TagItem'
|
||||
import md5 from 'js-md5'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
|
||||
export const ArticleInfo = (props) => {
|
||||
const { post } = props
|
||||
|
||||
const emailHash = md5(siteConfig('CONTACT_EMAIL', '#'))
|
||||
|
||||
return <section className="flex-wrap flex mt-2 text-gray--600 dark:text-gray-400 font-light leading-8">
|
||||
<div>
|
||||
|
||||
<h1 className="font-bold text-3xl text-black dark:text-white">
|
||||
<NotionIcon icon={post?.pageIcon} />{post?.title}
|
||||
</h1>
|
||||
|
||||
{post?.type !== 'Page' && <>
|
||||
<nav className="flex mt-7 items-start text-gray-500 dark:text-gray-400">
|
||||
<div className="flex mb-4">
|
||||
<a href={siteConfig('CONTACT_GITHUB', '#')} className="flex">
|
||||
<Image
|
||||
alt={siteConfig('AUTHOR')}
|
||||
width={24}
|
||||
height={24}
|
||||
src={`https://gravatar.com/avatar/${emailHash}`}
|
||||
className="rounded-full"
|
||||
/>
|
||||
<p className="ml-2 md:block">{siteConfig('AUTHOR')}</p>
|
||||
</a>
|
||||
<span className="block"> / </span>
|
||||
</div>
|
||||
<div className="mr-2 mb-4 md:ml-0">
|
||||
{post?.publishDay}
|
||||
</div>
|
||||
{post?.tags && (
|
||||
<div className="flex flex-nowrap max-w-full overflow-x-auto article-tags">
|
||||
{post?.tags.map(tag => (
|
||||
<TagItem key={tag} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<span className="hidden busuanzi_container_page_pv mr-2">
|
||||
<i className='mr-1 fas fa-eye' />
|
||||
|
||||
<span className="mr-2 busuanzi_value_page_pv" />
|
||||
</span>
|
||||
</nav>
|
||||
</>}
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
}
|
||||
@@ -1,51 +1,13 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { GameListIndexCombine } from './GameListIndexCombine'
|
||||
|
||||
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 = currentPage < totalPage && posts?.length > 0
|
||||
const pagePrefix = router.asPath
|
||||
.split('?')[0]
|
||||
.replace(/\/page\/[1-9]\d*/, '')
|
||||
.replace(/\/$/, '')
|
||||
|
||||
return (
|
||||
<div className='w-full md:pr-12 my-6'>
|
||||
<div id='posts-wrapper'>
|
||||
<>
|
||||
<div id='posts-wrapper' className='my-4 select-none'>
|
||||
<GameListIndexCombine {...props} />
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between text-xs'>
|
||||
<Link
|
||||
href={{
|
||||
pathname: currentPage - 1 === 1 ? `${pagePrefix}/` : `${pagePrefix}/page/${currentPage - 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
className={`${showPrev ? ' ' : ' invisible block pointer-events-none '}no-underline py-2 px-3 rounded`}>
|
||||
<button rel='prev' className='block cursor-pointer'>
|
||||
← {locale.PAGINATION.PREV}
|
||||
</button>
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
className={`${showNext ? ' ' : 'invisible pointer-events-none '} no-underline py-2 px-3 rounded`}>
|
||||
<button rel='next' className='block cursor-pointer'>
|
||||
{locale.PAGINATION.NEXT} →
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* 这里不显示分页组件,首页只展示部分即可 */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { GameListIndexCombine } from './GameListIndexCombine'
|
||||
|
||||
export const BlogListScroll = props => {
|
||||
const { posts } = props
|
||||
@@ -12,9 +12,8 @@ export const BlogListScroll = props => {
|
||||
const [page, updatePage] = useState(1)
|
||||
|
||||
let hasMore = false
|
||||
const postsToShow = posts && Array.isArray(posts)
|
||||
? deepClone(posts).slice(0, parseInt(siteConfig('POSTS_PER_PAGE')) * page)
|
||||
: []
|
||||
const postsToShow =
|
||||
posts && Array.isArray(posts) ? deepClone(posts).slice(0, parseInt(siteConfig('POSTS_PER_PAGE')) * page) : []
|
||||
|
||||
if (posts) {
|
||||
const totalCount = posts.length
|
||||
@@ -28,56 +27,33 @@ export const BlogListScroll = props => {
|
||||
const targetRef = useRef(null)
|
||||
|
||||
// 监听滚动自动分页加载
|
||||
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))
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div id="posts-wrapper" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
||||
{postsToShow.map(p => (
|
||||
<article key={p.id} className="mb-12" >
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${p.slug}`}
|
||||
className="text-black text-xl md:text-2xl no-underline hover:underline">
|
||||
{p.title}
|
||||
</Link>
|
||||
</h2>
|
||||
<>
|
||||
<div id='posts-wrapper' className='my-4' ref={targetRef}>
|
||||
<GameListIndexCombine posts={postsToShow} />
|
||||
</div>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700">
|
||||
by <a href="#" className="text-gray-700">{siteConfig('AUTHOR')}</a> on {p.date?.start_date || p.createdTime}
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href="#" className="text-gray-700">{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 leading-normal">
|
||||
{p.summary}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
|
||||
<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 onClick={handleGetMore} className='w-full my-4 py-4 text-center cursor-pointer '>
|
||||
{' '}
|
||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
31
themes/game/components/DarkModeButton.js
Normal file
31
themes/game/components/DarkModeButton.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useImperativeHandle } from 'react'
|
||||
|
||||
/**
|
||||
* 深色模式按钮
|
||||
*/
|
||||
const DarkModeButton = props => {
|
||||
const { cRef, className } = props
|
||||
const { isDarkMode, toggleDarkMode } = useGlobal()
|
||||
|
||||
/**
|
||||
* 对外暴露方法
|
||||
*/
|
||||
useImperativeHandle(cRef, () => {
|
||||
return {
|
||||
handleChangeDarkMode: () => {
|
||||
toggleDarkMode()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={toggleDarkMode}
|
||||
className={`${className || ''} flex items-center`}>
|
||||
<i className={`fas mr-2 ${isDarkMode ? 'fa-sun' : 'fa-moon px-0.5'}`} />
|
||||
{isDarkMode ? 'Dark Mode' : 'Light Mode'}{' '}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default DarkModeButton
|
||||
@@ -1,34 +1,32 @@
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const Footer = props => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const { post } = props
|
||||
const fullWidth = post?.fullWidth ?? false
|
||||
const since = siteConfig('SINCE')
|
||||
const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||
const copyrightDate =
|
||||
parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||
|
||||
return (
|
||||
<footer
|
||||
className={`z-10 relative mt-6 flex-shrink-0 m-auto w-full text-gray-500 dark:text-gray-400 transition-all ${
|
||||
!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'
|
||||
}`}>
|
||||
<DarkModeButton className='text-center py-4' />
|
||||
<hr className='border-gray-200 dark:border-gray-600' />
|
||||
<div className='my-4 text-sm leading-6'>
|
||||
<div className='flex align-baseline justify-between flex-wrap'>
|
||||
<span className='dark:text-gray-200 no-underline ml-4'>
|
||||
Powered by
|
||||
<a href='https://github.com/tangly1024/NotionNext' className=' hover:underline'>
|
||||
{' '}
|
||||
NotionNext {siteConfig('VERSION')}{' '}
|
||||
</a>
|
||||
</span>
|
||||
<p>
|
||||
© {siteConfig('TITLE')} {copyrightDate}
|
||||
</p>
|
||||
</div>
|
||||
className={`z-10 relative mt-6 flex-shrink-0 m-auto w-full dark:text-gray-200 `}>
|
||||
<hr className='my-2 border-black dark:border-gray-100' />
|
||||
{/* 页面底部 */}
|
||||
<div className='w-full flex justify-between p-4 '>
|
||||
<p>
|
||||
© {siteConfig('TITLE')} {copyrightDate}
|
||||
</p>
|
||||
<p>{siteConfig('DESCRIPTION')}</p>
|
||||
|
||||
<span className='dark:text-gray-200 no-underline ml-4'>
|
||||
Powered by
|
||||
<a
|
||||
href='https://github.com/tangly1024/NotionNext'
|
||||
className=' hover:underline'>
|
||||
{' '}
|
||||
NotionNext {siteConfig('VERSION')}{' '}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ export const GameListIndexCombine = ({ posts }) => {
|
||||
if (recommend) {
|
||||
// 4合一卡组
|
||||
let groupItems = []
|
||||
|
||||
while (gamesClone?.length > 0) {
|
||||
index++
|
||||
|
||||
@@ -38,16 +39,18 @@ export const GameListIndexCombine = ({ posts }) => {
|
||||
const item = gamesClone.shift()
|
||||
if (item.tags?.some(t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG))) {
|
||||
components.push(<GameItem key={index} item={item} isLargeCard={true} />)
|
||||
break
|
||||
continue
|
||||
} else {
|
||||
groupItems.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
if (groupItems.length === 4 || (gamesClone.length === 0 && groupItems.length > 4)) {
|
||||
if (groupItems.length === 4) {
|
||||
components.push(<GameItemGroup key={index} items={groupItems} />)
|
||||
// 清空4合一卡片
|
||||
groupItems = []
|
||||
} else {
|
||||
// 剩余的4合一不满4个的给他放大卡
|
||||
while (groupItems.length > 0) {
|
||||
const item = groupItems.shift()
|
||||
components.push(<GameItem key={index++} item={item} isLargeCard={true} />)
|
||||
|
||||
@@ -40,7 +40,8 @@ export const GameListNormal = ({ games, maxCount = 18 }) => {
|
||||
* @returns
|
||||
*/
|
||||
const GameItem = ({ item }) => {
|
||||
const { title, img } = item
|
||||
const { title } = item
|
||||
const img = item.pageCoverThumbnail
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
const video = item?.ext?.video
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { useState } from 'react'
|
||||
import { useGameGlobal } from '..'
|
||||
|
||||
/**
|
||||
* 游戏列表- 最近游戏
|
||||
* @returns
|
||||
*/
|
||||
export const GameListRecent = ({ maxCount = 14 }) => {
|
||||
const { recentGames } = useGlobal()
|
||||
const { recentGames } = useGameGlobal()
|
||||
const gamesClone = deepClone(recentGames)
|
||||
// 构造一个List<Component>
|
||||
const components = []
|
||||
@@ -31,8 +31,7 @@ export const GameListRecent = ({ maxCount = 14 }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='text-white text-lg pb-1 p-2'>Recent Played</div>
|
||||
<div className='game-list-recent-wrapper w-full max-w-full overflow-x-auto p-2'>
|
||||
<div className='game-list-recent-wrapper w-full max-w-full overflow-x-auto pt-4 px-2'>
|
||||
<div className='game-grid md:flex grid grid-flow-col gap-2'>
|
||||
{components?.map((ItemComponent, index) => {
|
||||
return ItemComponent
|
||||
@@ -67,6 +66,9 @@ const GameItem = ({ item }) => {
|
||||
title={title}
|
||||
className={`card-single h-28 w-28 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
||||
group hover:border-purple-400`}>
|
||||
<div className='absolute right-0.5 top-1 z-20'>
|
||||
<i className='fas fa-clock-rotate-left w-6 h-6 flex items-center justify-center shadow rounded-full bg-white text-blue-500 text-sm' />
|
||||
</div>
|
||||
<div className='absolute text-sm bottom-2 transition-all duration-200 text-white z-30'>{title}</div>
|
||||
<div className='h-1/2 w-full absolute left-0 bottom-0 z-20 opacity-75 transition-all duration-200'>
|
||||
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
||||
|
||||
41
themes/game/components/GroupCategory.js
Normal file
41
themes/game/components/GroupCategory.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
function GroupCategory({ currentCategory, categoryOptions }) {
|
||||
if (!categoryOptions) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link className='mx-2' href='/category'>
|
||||
<i className='fas fa-bars' />
|
||||
</Link>
|
||||
<div id='category-list' className='dark:border-gray-600 flex py-1'>
|
||||
{categoryOptions.map(category => {
|
||||
const selected = currentCategory === category.name
|
||||
return (
|
||||
<Link
|
||||
key={category.name}
|
||||
href={`/category/${category.name}`}
|
||||
passHref
|
||||
className={` ${
|
||||
selected
|
||||
? 'bg-green-500 text-white '
|
||||
: 'dark:text-gray-300 hover:bg-green-500 rounded-lg hover:text-white'
|
||||
} whitespace-nowrap overflow-ellipsis w-full items-center px-2 cursor-pointer py-1 font-bold`}>
|
||||
{/* <i
|
||||
className={`${selected ? 'text-white fa-folder-open' : 'fa-folder text-gray-400'} fas mr-2`}
|
||||
/> */}
|
||||
{category.name}
|
||||
{/* <span className='text-xs flex items-start pl-2 h-full'>
|
||||
{category.count}
|
||||
</span> */}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default GroupCategory
|
||||
28
themes/game/components/GroupTag.js
Normal file
28
themes/game/components/GroupTag.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
/**
|
||||
* 标签组
|
||||
* @param tags
|
||||
* @param currentTag
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
function GroupTag({ tagOptions, currentTag }) {
|
||||
if (!tagOptions) return <></>
|
||||
return (
|
||||
<>
|
||||
<Link href='/tag'>
|
||||
<i className='fas fa-tags p-2' />
|
||||
</Link>
|
||||
<div id='tags-group' className='flex flex-wrap p-1 gap-2'>
|
||||
{tagOptions?.slice(0, 20)?.map(tag => {
|
||||
const selected = tag.name === currentTag
|
||||
return <TagItemMini key={tag.name} tag={tag} selected={selected} />
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default GroupTag
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Image from 'next/image'
|
||||
import { useGameGlobal } from '..'
|
||||
import Logo from './Logo'
|
||||
|
||||
/**
|
||||
@@ -7,10 +6,10 @@ import Logo from './Logo'
|
||||
* @returns
|
||||
*/
|
||||
export default function Header() {
|
||||
const { setSideBarVisible } = useGlobal()
|
||||
const { setSideBarVisible } = useGameGlobal()
|
||||
return (
|
||||
<header className='z-20'>
|
||||
<div className='w-full h-16 rounded-md bg-white shadow-card dark:bg-[#1F2030] flex justify-between items-center px-4'>
|
||||
<div className='w-full h-16 rounded-md bg-white shadow-md hover:shadow-xl transition-shadow duration-200 dark:bg-[#1F2030] flex justify-between items-center px-4'>
|
||||
<Logo />
|
||||
|
||||
<button
|
||||
@@ -18,7 +17,7 @@ export default function Header() {
|
||||
onClick={() => {
|
||||
setSideBarVisible(true)
|
||||
}}>
|
||||
<Image src='/svg/search.svg' className='mr-2' width={20} height={20} />
|
||||
<i className='fas fa-search' />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,30 +1,72 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useGameGlobal } from '..'
|
||||
import CONFIG from '../config'
|
||||
import DarkModeButton from './DarkModeButton'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
export const MenuList = () => {
|
||||
/**
|
||||
* 导航菜单
|
||||
*/
|
||||
export const MenuList = props => {
|
||||
const { setSideBarVisible } = useGameGlobal()
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
const defaultLinks = [
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fas fa-home',
|
||||
name: locale.NAV.INDEX,
|
||||
to: '/' || '/',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
show: siteConfig('GAME_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
show: siteConfig('GAME_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
let links = [].concat(defaultLinks)
|
||||
if (customNav) {
|
||||
links = defaultLinks.concat(customNav)
|
||||
}
|
||||
|
||||
// 如果 开启自定义菜单,则覆盖Page生成的菜单
|
||||
if (siteConfig('CUSTOM_MENU')) {
|
||||
links = customMenu
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='dark:text-white'>
|
||||
<ul>
|
||||
<li className='py-4 px-2 font-bold hover:underline'>
|
||||
<Link href='/' passHref>
|
||||
<span className='flex items-center gap-2'>
|
||||
<i className='fas fa-home' />
|
||||
Home
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className='py-4 px-2 font-bold hover:underline'>
|
||||
<button
|
||||
className='flex items-center gap-2'
|
||||
onClick={() => {
|
||||
setSideBarVisible(true)
|
||||
}}>
|
||||
<i className='fas fa-search' />
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul>
|
||||
<li className='py-4 px-2 font-bold'>
|
||||
<button
|
||||
className='flex items-center gap-2'
|
||||
onClick={() => {
|
||||
setSideBarVisible(true)
|
||||
}}>
|
||||
<i className='fas fa-search' />
|
||||
<span>Search</span>
|
||||
</button>
|
||||
<button className='flex items-center gap-2'>
|
||||
{/* 切换深色模式 */}
|
||||
<DarkModeButton className='text-center py-4' />
|
||||
</button>
|
||||
</li>
|
||||
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link && link.show && <MenuItemDrop key={index} link={link} />
|
||||
)}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import Collapse from '@/components/Collapse'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import RandomPostButton from './RandomPostButton'
|
||||
import SearchButton from './SearchButton'
|
||||
import { SvgIcon } from './SvgIcon'
|
||||
|
||||
const Nav = props => {
|
||||
const { navBarTitle, fullWidth, siteInfo } = props
|
||||
const useSticky = !JSON.parse(siteConfig('GAME_AUTO_COLLAPSE_NAV_BAR', true))
|
||||
const navRef = useRef(null)
|
||||
const sentinalRef = useRef([])
|
||||
const handler = ([entry]) => {
|
||||
if (navRef && navRef.current && useSticky) {
|
||||
if (!entry.isIntersecting && entry !== undefined) {
|
||||
navRef.current?.classList.add('sticky-nav-full')
|
||||
} else {
|
||||
navRef.current?.classList.remove('sticky-nav-full')
|
||||
}
|
||||
} else {
|
||||
navRef.current?.classList.add('remove-sticky')
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
const obvserver = new window.IntersectionObserver(handler)
|
||||
obvserver.observe(sentinalRef.current)
|
||||
return () => {
|
||||
if (sentinalRef.current) obvserver.unobserve(sentinalRef.current)
|
||||
}
|
||||
}, [sentinalRef])
|
||||
return (
|
||||
<>
|
||||
<div className='observer-element h-4 md:h-12' ref={sentinalRef}></div>
|
||||
<div
|
||||
className={`sticky-nav m-auto w-full h-6 flex flex-row justify-between items-center mb-2 md:mb-12 py-8 bg-opacity-60 ${
|
||||
!fullWidth ? 'max-w-3xl px-4' : 'px-4 md:px-24'
|
||||
}`}
|
||||
id='sticky-nav'
|
||||
ref={navRef}>
|
||||
<div className='flex items-center'>
|
||||
<Link href='/' aria-label={siteConfig('TITLE')}>
|
||||
<div className='h-6 w-6'>
|
||||
{/* <SvgIcon/> */}
|
||||
{siteConfig('GAME_NAV_NOTION_ICON', null, CONFIG) ? (
|
||||
<LazyImage src={siteInfo?.icon} width={24} height={24} alt={siteConfig('AUTHOR')} />
|
||||
) : (
|
||||
<SvgIcon />
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
{navBarTitle ? (
|
||||
<p className='ml-2 font-medium text-gray-800 dark:text-gray-300 header-name'>{navBarTitle}</p>
|
||||
) : (
|
||||
<p className='ml-2 font-medium text-gray-800 dark:text-gray-300 header-name whitespace-nowrap'>
|
||||
{siteConfig('TITLE')}
|
||||
{/* ,{' '}<span className="font-normal">{siteConfig('DESCRIPTION')}</span> */}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<NavBar {...props} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const NavBar = props => {
|
||||
const { customMenu, customNav } = props
|
||||
const [isOpen, changeOpen] = useState(false)
|
||||
const toggleOpen = () => {
|
||||
changeOpen(!isOpen)
|
||||
}
|
||||
const collapseRef = useRef(null)
|
||||
|
||||
const { locale } = useGlobal()
|
||||
let links = [
|
||||
{
|
||||
id: 2,
|
||||
name: locale.NAV.RSS,
|
||||
to: '/feed',
|
||||
show: siteConfig('ENABLE_RSS') && siteConfig('GAME_MENU_RSS', null, CONFIG),
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
show: siteConfig('GAME_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
show: siteConfig('GAME_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
show: siteConfig('GAME_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('GAME_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 (
|
||||
<div className='flex-shrink-0 flex'>
|
||||
<ul className='hidden md:flex flex-row'>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemDrop key={index} link={link} />
|
||||
))}
|
||||
</ul>
|
||||
<div className='md:hidden'>
|
||||
<Collapse collapseRef={collapseRef} isOpen={isOpen} type='vertical' className='fixed top-16 right-6'>
|
||||
<div className='dark:border-black bg-white dark:bg-black rounded border p-2 text-sm'>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse
|
||||
key={index}
|
||||
link={link}
|
||||
onHeightChange={param => collapseRef.current?.updateCollapseHeight(param)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
{JSON.parse(siteConfig('GAME_MENU_RANDOM_POST', null, CONFIG)) && <RandomPostButton {...props} />}
|
||||
{JSON.parse(siteConfig('GAME_MENU_SEARCH_BUTTON', null, CONFIG)) && <SearchButton {...props} />}
|
||||
<i
|
||||
onClick={toggleOpen}
|
||||
className='fas fa-bars cursor-pointer px-5 flex justify-center items-center md:hidden'></i>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Nav
|
||||
@@ -1,9 +0,0 @@
|
||||
import { MenuList } from './MenuList'
|
||||
|
||||
export default function NavBar({ className }) {
|
||||
return (
|
||||
<nav className={className}>
|
||||
<MenuList />
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
53
themes/game/components/PostInfo.js
Normal file
53
themes/game/components/PostInfo.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import Link from 'next/link'
|
||||
import TagItem from './TagItem'
|
||||
|
||||
/**
|
||||
* 文章详情页说明信息
|
||||
*/
|
||||
export default function PostInfo(props) {
|
||||
const { post } = props
|
||||
|
||||
return (
|
||||
<section className='flex-wrap flex mt-2 text-gray--600 dark:text-gray-400 font-light leading-8'>
|
||||
<div>
|
||||
<div>
|
||||
{post?.type !== 'Page' && (
|
||||
<>
|
||||
<Link
|
||||
href={`/category/${post?.category}`}
|
||||
passHref
|
||||
className='cursor-pointer text-xs font-bold hover:underline mr-2'>
|
||||
{post?.category}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className='font-bold text-3xl text-black dark:text-white'>
|
||||
<NotionIcon icon={post?.pageIcon} />
|
||||
{post?.title}
|
||||
</h1>
|
||||
|
||||
{post?.type !== 'Page' && (
|
||||
<>
|
||||
<nav className='flex my-2 items-start text-gray-500 dark:text-gray-400'>
|
||||
{post?.tags && (
|
||||
<div className='flex flex-nowrap max-w-full overflow-x-auto article-tags'>
|
||||
{post?.tags.map(tag => (
|
||||
<TagItem key={tag} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<span className='hidden busuanzi_container_page_pv mr-2'>
|
||||
<i className='mr-1 fas fa-fire' />
|
||||
|
||||
<span className='mr-2 busuanzi_value_page_pv' />
|
||||
</span>
|
||||
</nav>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useGameGlobal } from '..'
|
||||
import CONFIG from '../config'
|
||||
import { GameListNormal } from './GameListNormal'
|
||||
import Logo from './Logo'
|
||||
|
||||
@@ -7,9 +10,9 @@ import Logo from './Logo'
|
||||
* 侧拉抽屉的内容
|
||||
*/
|
||||
export default function SideBarContent() {
|
||||
const { allGames, sideBarVisible, setSideBarVisible, filterGames, setFilterGames } = useGameGlobal()
|
||||
const { allNavPages, sideBarVisible, setSideBarVisible, filterGames, setFilterGames } = useGameGlobal()
|
||||
const inputRef = useRef(null) // 创建对输入框的引用
|
||||
|
||||
const allGames = deepClone(allNavPages)
|
||||
useEffect(() => {
|
||||
if (sideBarVisible) {
|
||||
setTimeout(() => {
|
||||
@@ -21,7 +24,9 @@ export default function SideBarContent() {
|
||||
const handleSearch = e => {
|
||||
const search = e.target.value
|
||||
if (!search || search === '') {
|
||||
setFilterGames(allGames?.filter(item => item.recommend))
|
||||
setFilterGames(
|
||||
allGames?.filter(item => item.tags?.some(t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG)))
|
||||
)
|
||||
return
|
||||
}
|
||||
setFilterGames(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const TagItem = ({ tag }) => (
|
||||
(<Link href={`/tag/${encodeURIComponent(tag)}`}>
|
||||
<p className="mr-1 rounded-full px-2 py-1 border leading-none text-sm dark:border-gray-600">
|
||||
<Link href={`/tag/${encodeURIComponent(tag)}`}>
|
||||
<p className='cursor-pointer hover:bg-gray-50 dark:hover:bg-hexo-black-gray mr-1 rounded-full px-2 py-1 border leading-none text-sm dark:border-gray-600'>
|
||||
{tag}
|
||||
</p>
|
||||
</Link>)
|
||||
</Link>
|
||||
)
|
||||
|
||||
export default TagItem
|
||||
|
||||
21
themes/game/components/TagItemMini.js
Normal file
21
themes/game/components/TagItemMini.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const TagItemMini = ({ tag, selected = false }) => {
|
||||
return (
|
||||
<Link
|
||||
key={tag}
|
||||
href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`}
|
||||
className={` rounded hover:text-white hover:bg-green-500 text-black dark:text-white dark:bg-gray-800 py-0.5 px-1 `}
|
||||
passHref>
|
||||
{/* # {tag.name} */}
|
||||
<span className='flex flex-nowrap cursor-pointer'>
|
||||
# <span>{tag.name}</span>{' '}
|
||||
<span className='h-full flex items-start text-xs ml-1'>
|
||||
{tag.count ? `${tag.count}` : ''}
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default TagItemMini
|
||||
@@ -1,9 +1,11 @@
|
||||
import CusdisComponent from '@/components/CusdisComponent'
|
||||
import Comment from '@/components/Comment'
|
||||
import { Draggable } from '@/components/Draggable'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { deepClone, isBrowser } from '@/lib/utils'
|
||||
import { deepClone, isBrowser, shuffleArray } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||
import Announcement from './components/Announcement'
|
||||
@@ -16,8 +18,11 @@ import FullScreen from './components/FullScreen'
|
||||
import { GameListIndexCombine } from './components/GameListIndexCombine'
|
||||
import { GameListRelate } from './components/GameListRealate'
|
||||
import { GameListRecent } from './components/GameListRecent'
|
||||
import GroupCategory from './components/GroupCategory'
|
||||
import GroupTag from './components/GroupTag'
|
||||
import Header from './components/Header'
|
||||
import NavBar from './components/NavBar'
|
||||
import { MenuList } from './components/MenuList'
|
||||
import PostInfo from './components/PostInfo'
|
||||
import SearchNavBar from './components/SearchNavBar'
|
||||
import SideBarContent from './components/SideBarContent'
|
||||
import SideBarDrawer from './components/SideBarDrawer'
|
||||
@@ -47,14 +52,22 @@ const LayoutBase = props => {
|
||||
|
||||
const [filterGames, setFilterGames] = useState(
|
||||
deepClone(
|
||||
allNavPages?.filter(item => item.tags?.some(t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG)))
|
||||
allNavPages?.filter(item =>
|
||||
item.tags?.some(
|
||||
t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
const [recentGames, setRecentGames] = useState([])
|
||||
const [sideBarVisible, setSideBarVisible] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setRecentGames(localStorage.getItem('recent_games') ? JSON.parse(localStorage.getItem('recent_games')) : [])
|
||||
setRecentGames(
|
||||
localStorage.getItem('recent_games')
|
||||
? JSON.parse(localStorage.getItem('recent_games'))
|
||||
: []
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@@ -74,37 +87,34 @@ const LayoutBase = props => {
|
||||
id='theme-game'
|
||||
className={`${siteConfig('FONT_STYLE')} w-full h-full min-h-screen justify-center bg-[#83FFE7] dark:bg-black dark:text-gray-300 scroll-smooth`}>
|
||||
<Style />
|
||||
|
||||
{/* 左右布局 */}
|
||||
<div id='wrapper' className={'relative flex justify-between w-full h-full mx-auto'}>
|
||||
{/* 左侧 */}
|
||||
<div
|
||||
id='wrapper'
|
||||
className={'relative flex justify-between w-full h-full mx-auto'}>
|
||||
{/* PC端左侧 */}
|
||||
<div className='w-52 hidden xl:block relative z-10'>
|
||||
<div className='py-4 px-2 sticky top-0 h-screen flex flex-col justify-between'>
|
||||
{/* 顶部 */}
|
||||
|
||||
<div className=''>
|
||||
<div className='select-none'>
|
||||
<Header />
|
||||
<NavBar />
|
||||
<nav
|
||||
className={` dark:text-white bg-white dark:bg-hexo-black-gray my-4 rounded-md px-2`}>
|
||||
<MenuList {...props} />
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* 左侧广告栏目 */}
|
||||
<div className='w-full'>
|
||||
<AdSlot />
|
||||
<AdSlot />
|
||||
<AdSlot />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧 */}
|
||||
<main className='flex-grow w-full overflow-x-auto'>
|
||||
{children}
|
||||
<main className='flex-grow w-full h-full flex flex-col min-h-screen overflow-x-auto'>
|
||||
<div className='flex-grow h-full'>{children}</div>
|
||||
|
||||
<div className='ads w-full justify-center flex p-2'>
|
||||
<AdSlot type='flow' />
|
||||
</div>
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -127,11 +137,38 @@ const LayoutBase = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutIndex = props => {
|
||||
const { tagOptions, currentTag, categoryOptions, currentCategory } = props
|
||||
return (
|
||||
<>
|
||||
{/* 首页移动端顶部导航 */}
|
||||
<div className='p-2 xl:hidden'>
|
||||
<Header />
|
||||
</div>
|
||||
{/* 最近游戏 */}
|
||||
<GameListRecent />
|
||||
{/* 游戏列表 */}
|
||||
<LayoutPostList {...props} />
|
||||
<Announcement {...props} />
|
||||
|
||||
{/* 主区域下方 */}
|
||||
<div className='w-full bg-white dark:bg-hexo-black-gray rounded-lg p-2'>
|
||||
{/* 标签汇总 */}
|
||||
<GroupCategory
|
||||
categoryOptions={categoryOptions}
|
||||
currentCategory={currentCategory}
|
||||
/>
|
||||
<hr />
|
||||
<GroupTag tagOptions={tagOptions} currentTag={currentTag} />
|
||||
</div>
|
||||
|
||||
{/* 广告 */}
|
||||
<div className='w-full'>
|
||||
<AdSlot type='in-article' />
|
||||
</div>
|
||||
|
||||
{/* 站点公告信息 */}
|
||||
<div className='w-full bg-white dark:bg-hexo-black-gray rounded-lg p-2'>
|
||||
<Announcement {...props} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -159,9 +196,9 @@ const LayoutPostList = props => {
|
||||
<>
|
||||
{tag && <SearchNavBar {...props} />}
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogListPage {...props} posts={filteredBlogPosts} />
|
||||
<BlogListPage posts={filteredBlogPosts} />
|
||||
) : (
|
||||
<BlogListScroll {...props} posts={filteredBlogPosts} />
|
||||
<BlogListScroll posts={filteredBlogPosts} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
@@ -224,7 +261,11 @@ const LayoutArchive = props => {
|
||||
<>
|
||||
<div className='mb-10 pb-20 md:py-12 p-3 min-h-screen w-full'>
|
||||
{Object.keys(archivePosts).map(archiveTitle => (
|
||||
<BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />
|
||||
<BlogArchiveItem
|
||||
key={archiveTitle}
|
||||
archiveTitle={archiveTitle}
|
||||
archivePosts={archivePosts}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
@@ -237,23 +278,19 @@ const LayoutArchive = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, allNavPages, lock, validPassword } = props
|
||||
const game = post
|
||||
const { post, allNavPages, recommendPosts, lock, validPassword } = props
|
||||
const game = deepClone(post)
|
||||
const [loading, setLoading] = useState(false)
|
||||
// const [url, setUrl] = useState(game?.ext?.href)
|
||||
// 替换成随机推荐和相关游戏
|
||||
const relateGames = allNavPages
|
||||
const randomGames = allNavPages
|
||||
const relateGames = recommendPosts
|
||||
const randomGames = shuffleArray(deepClone(allNavPages))
|
||||
|
||||
// 将当前游戏加入到最近游玩
|
||||
useEffect(() => {
|
||||
// if (!url || url !== game?.ext?.href) {
|
||||
// // 游戏路径
|
||||
// setUrl(game?.ext?.href)
|
||||
// }
|
||||
|
||||
// 更新最新游戏
|
||||
const recentGames = localStorage.getItem('recent_games') ? JSON.parse(localStorage.getItem('recent_games')) : []
|
||||
const recentGames = localStorage.getItem('recent_games')
|
||||
? JSON.parse(localStorage.getItem('recent_games'))
|
||||
: []
|
||||
|
||||
const existedIndex = recentGames.findIndex(item => item?.id === game?.id)
|
||||
if (existedIndex === -1) {
|
||||
@@ -275,15 +312,21 @@ const LayoutSlug = props => {
|
||||
}
|
||||
|
||||
// 绑定加载事件
|
||||
if (iframe.attachEvent) {
|
||||
iframe.attachEvent('onload', iframeLoaded)
|
||||
if (iframe?.attachEvent) {
|
||||
iframe?.attachEvent('onload', iframeLoaded)
|
||||
} else {
|
||||
iframe.onload = iframeLoaded
|
||||
if (iframe) iframe.onload = iframeLoaded
|
||||
}
|
||||
|
||||
// 更改iFrame的title
|
||||
if (document?.getElementById('game-wrapper')?.contentDocument.querySelector('title')?.textContent) {
|
||||
document.getElementById('game-wrapper').contentDocument.querySelector('title').textContent = `${
|
||||
if (
|
||||
document
|
||||
?.getElementById('game-wrapper')
|
||||
?.contentDocument.querySelector('title')?.textContent
|
||||
) {
|
||||
document
|
||||
.getElementById('game-wrapper')
|
||||
.contentDocument.querySelector('title').textContent = `${
|
||||
game?.title || ''
|
||||
} - Play ${game?.title || ''} on ${siteConfig('TITLE')}`
|
||||
}
|
||||
@@ -294,103 +337,101 @@ const LayoutSlug = props => {
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && (
|
||||
<div id='article-wrapper' className='px-2'>
|
||||
<>
|
||||
{/* <ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post} />
|
||||
<ArticleFooter /> */}
|
||||
{/* 游戏区域 */}
|
||||
<div className='game-detail-wrapper w-full grow flex md:px-2'>
|
||||
{/* 移动端返回主页按钮 */}
|
||||
<Draggable stick='left'>
|
||||
<div
|
||||
style={{ left: '0px', top: '1rem' }}
|
||||
className='fixed xl:hidden group space-x-1 flex items-center z-20 pr-3 pl-1 bg-[#202030] rounded-r-2xl shadow-lg '>
|
||||
<Link href='/' className='px-1 py-3 hover:scale-125 duration-200 transition-all' passHref>
|
||||
<i className='fas fa-arrow-left' />
|
||||
</Link>{' '}
|
||||
<span
|
||||
className='text-white font-serif'
|
||||
onClick={() => {
|
||||
document.querySelector('.game-info').scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'end',
|
||||
inline: 'nearest'
|
||||
})
|
||||
}}>
|
||||
G
|
||||
</span>
|
||||
</div>
|
||||
</Draggable>
|
||||
<div id='article-wrapper' className='md:px-2'>
|
||||
{/* 游戏区域 */}
|
||||
<div className='game-detail-wrapper w-full grow flex md:px-2'>
|
||||
{/* 移动端返回主页按钮 */}
|
||||
<Draggable stick='left'>
|
||||
<div
|
||||
style={{ left: '0px', top: '1rem' }}
|
||||
className='fixed xl:hidden group space-x-1 flex items-center z-20 pr-3 pl-1 bg-[#202030] rounded-r-2xl shadow-lg '>
|
||||
<Link
|
||||
href='/'
|
||||
className='px-1 py-3 hover:scale-125 duration-200 transition-all'
|
||||
passHref>
|
||||
<i className='fas fa-arrow-left' />
|
||||
</Link>{' '}
|
||||
<span
|
||||
className='text-white font-serif'
|
||||
onClick={() => {
|
||||
document.querySelector('.game-info').scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'end',
|
||||
inline: 'nearest'
|
||||
})
|
||||
}}>
|
||||
G
|
||||
</span>
|
||||
</div>
|
||||
</Draggable>
|
||||
|
||||
<div className='w-full py-1 md:py-4'>
|
||||
<div className='bg-black w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md relative'>
|
||||
{/* Loading遮罩 */}
|
||||
{loading && (
|
||||
<div className='absolute z-20 w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md overflow-hidden '>
|
||||
<div className='z-20 absolute bg-black bg-opacity-75 w-full h-full flex flex-col gap-4 justify-center items-center'>
|
||||
<h2 className='text-3xl text-white flex gap-2'>
|
||||
<i className='fas fa-spinner animate-spin'></i>
|
||||
{siteConfig('TITLE')}
|
||||
</h2>
|
||||
<h3 className='text-xl text-white'>{siteConfig('DESCRIPTION')}</h3>
|
||||
</div>
|
||||
|
||||
{/* 游戏封面图 */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
{game?.img && <img src={game?.img} className='w-full h-full blur-md absolute top-0 left-0 z-0' />}
|
||||
<div className='w-full py-1 md:py-4'>
|
||||
{/* 游戏区 */}
|
||||
<div className='bg-black w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md relative'>
|
||||
{/* Loading遮罩 */}
|
||||
{loading && (
|
||||
<div className='absolute z-20 w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md overflow-hidden '>
|
||||
<div className='z-20 absolute bg-black bg-opacity-75 w-full h-full flex flex-col gap-4 justify-center items-center'>
|
||||
<h2 className='text-3xl text-white flex gap-2'>
|
||||
<i className='fas fa-spinner animate-spin'></i>
|
||||
{siteConfig('TITLE')}
|
||||
</h2>
|
||||
<h3 className='text-xl text-white'>
|
||||
{siteConfig('DESCRIPTION')}
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<iframe
|
||||
id='game-wrapper'
|
||||
className={`w-full xl:h-[calc(100vh-8rem)] h-screen md:rounded-md overflow-hidden ${game?.ext?.href ? '' : 'hidden'}`}
|
||||
style={{
|
||||
position: 'relative'
|
||||
}}
|
||||
src={game?.ext?.href}></iframe>
|
||||
|
||||
{/* 游戏窗口装饰器 */}
|
||||
{game && !loading && (
|
||||
<div className='game-decorator bg-[#0B0D14] right-0 bottom-0 flex justify-center h-12 md:w-12 z-10 md:absolute'>
|
||||
{/* 加入全屏按钮 */}
|
||||
<FullScreen />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 游戏资讯 */}
|
||||
<div className='game-info dark:text-white py-4 mt-8 md:mt-0'>
|
||||
<div className='w-full'>
|
||||
<GameListRelate posts={relateGames} />
|
||||
{/* 游戏封面图 */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
{game?.img && (
|
||||
<img
|
||||
src={game?.img}
|
||||
className='w-full h-full blur-md absolute top-0 left-0 z-0'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h1 className='text-2xl px-2 py-2 xl:px-0'>{game?.title}</h1>
|
||||
<h2 className='px-2 py-2 xl:px-0'>
|
||||
Play {game?.title || ''} on {siteConfig('TITLE', '')}
|
||||
</h2>
|
||||
<p className='px-2 py-2 xl:px-0'>{siteConfig('DESCRIPTION')}</p>
|
||||
<AdSlot />
|
||||
)}
|
||||
|
||||
{game && (
|
||||
<div>
|
||||
<div className='py-2 text-2xl dark:text-white'>Comment</div>
|
||||
<CusdisComponent frontMatter={game} />
|
||||
</div>
|
||||
)}
|
||||
<iframe
|
||||
id='game-wrapper'
|
||||
className={`w-full xl:h-[calc(100vh-8rem)] h-screen md:rounded-md overflow-hidden ${game?.ext?.href ? '' : 'hidden'}`}
|
||||
style={{
|
||||
position: 'relative'
|
||||
}}
|
||||
src={game?.ext?.href}></iframe>
|
||||
|
||||
{/* 游戏窗口装饰器 */}
|
||||
{game && !loading && (
|
||||
<div className='game-decorator bg-[#0B0D14] right-0 bottom-0 flex justify-center h-12 md:w-12 z-10 md:absolute'>
|
||||
{/* 加入全屏按钮 */}
|
||||
<FullScreen />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 游戏资讯 */}
|
||||
<div className='game-info dark:text-white py-4 px-2 mt-8 md:mt-0'>
|
||||
{/* 关联游戏 */}
|
||||
<div className='w-full'>
|
||||
<GameListRelate posts={relateGames} />
|
||||
</div>
|
||||
|
||||
{game && (
|
||||
<div className='bg-white shadow-md my-2 p-2 rounded-md dark:bg-black'>
|
||||
<PostInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
{/* 广告嵌入 */}
|
||||
<AdSlot />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='xl:hidden py-2'>
|
||||
<Header />
|
||||
</div>
|
||||
{/* 其它游戏列表 */}
|
||||
<div>
|
||||
<GameListIndexCombine posts={randomGames} />
|
||||
</div>
|
||||
</>
|
||||
{/* 其它游戏列表 */}
|
||||
<GameListIndexCombine posts={randomGames} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@@ -416,15 +457,21 @@ const LayoutCategoryIndex = props => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||
<div
|
||||
id='category-list'
|
||||
className='duration-200 flex flex-wrap my-4 gap-2'>
|
||||
{categoryOptions?.map(category => {
|
||||
return (
|
||||
<Link key={category.name} href={`/category/${category.name}`} passHref legacyBehavior>
|
||||
<Link
|
||||
key={category.name}
|
||||
href={`/category/${category.name}`}
|
||||
passHref
|
||||
legacyBehavior>
|
||||
<div
|
||||
className={
|
||||
'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'
|
||||
'bg-white rounded-lg hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'
|
||||
}>
|
||||
<i className='mr-4 fas fa-folder' />
|
||||
{/* <i className='mr-4 fas fa-folder' /> */}
|
||||
{category.name}({category.count})
|
||||
</div>
|
||||
</Link>
|
||||
@@ -445,20 +492,17 @@ const LayoutTagIndex = props => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||
<div id='tags-list' className='duration-200 flex flex-wrap my-4 gap-2'>
|
||||
{tagOptions.map(tag => {
|
||||
return (
|
||||
<div key={tag.name} className='p-2'>
|
||||
<Link
|
||||
key={tag}
|
||||
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||
passHref
|
||||
className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white 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'>
|
||||
<i className='mr-1 fas fa-tag' /> {tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
key={tag.name}
|
||||
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||
passHref
|
||||
className={` select-none cursor-pointer flex bg-white rounded-lg hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white hover:shadow-xl dark:bg-gray-800`}>
|
||||
<i className='mr-1 fas fa-tag' />{' '}
|
||||
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
@@ -468,14 +512,15 @@ const LayoutTagIndex = props => {
|
||||
}
|
||||
|
||||
export {
|
||||
Layout404,
|
||||
LayoutArchive,
|
||||
LayoutBase,
|
||||
LayoutCategoryIndex,
|
||||
LayoutIndex,
|
||||
LayoutPostList,
|
||||
LayoutSearch,
|
||||
LayoutSlug,
|
||||
LayoutTagIndex,
|
||||
CONFIG as THEME_CONFIG
|
||||
Layout404,
|
||||
LayoutArchive,
|
||||
LayoutBase,
|
||||
LayoutCategoryIndex,
|
||||
LayoutIndex,
|
||||
LayoutPostList,
|
||||
LayoutSearch,
|
||||
LayoutSlug,
|
||||
LayoutTagIndex,
|
||||
CONFIG as THEME_CONFIG
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user