mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
magzine基本组件
This commit is contained in:
@@ -1664,9 +1664,8 @@ code[class*='language-'] {
|
||||
}
|
||||
|
||||
/* NOTION CSS OVERRIDE */
|
||||
|
||||
.notion {
|
||||
@apply dark:text-gray-300;
|
||||
@apply dark:text-gray-100;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.notion,
|
||||
|
||||
@@ -10,7 +10,7 @@ const Announcement = ({ post, className }) => {
|
||||
<div className={className}>
|
||||
<section
|
||||
id='announcement-wrapper'
|
||||
className='dark:text-gray-300 rounded-xl px-2 py-4'>
|
||||
className='dark:text-gray-300 rounded-xl px-2'>
|
||||
{/* <div><i className='mr-2 fas fa-bullhorn' />{locale.COMMON.ANNOUNCEMENT}</div> */}
|
||||
{post && (
|
||||
<div id='announcement-content'>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from 'next/link'
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
||||
export default function ArchiveItem({ archiveTitle, archivePosts }) {
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
@@ -32,7 +32,7 @@ export default function ArticleInfo(props) {
|
||||
<i className='far fa-calendar-check mr-2' />
|
||||
{post?.lastEditedDay}
|
||||
</span>
|
||||
<div className='hidden busuanzi_container_page_pv font-light mr-2 whitespace-nowrap'>
|
||||
<div className='hidden busuanzi_container_page_pv mr-2 whitespace-nowrap'>
|
||||
<i className='mr-1 fas fa-eye' />
|
||||
<span className='busuanzi_value_page_pv' />
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export const ArticleLock = props => {
|
||||
}
|
||||
}}
|
||||
ref={passwordInputRef} // 绑定ref到passwordInputRef变量
|
||||
className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'></input>
|
||||
className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 leading-10 text-black bg-gray-100 dark:bg-gray-500'></input>
|
||||
<div
|
||||
onClick={submitPassword}
|
||||
className='px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-gray-500 hover:bg-gray-400 text-white rounded-r duration-300'>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogPostListEmpty
|
||||
@@ -77,7 +77,7 @@ const Catalog = ({ toc }) => {
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
className={`notion-table-of-contents-item duration-300 transform font-light dark:text-gray-300
|
||||
className={`notion-table-of-contents-item duration-300 transform dark:text-gray-300
|
||||
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}>
|
||||
<span
|
||||
style={{
|
||||
|
||||
@@ -8,13 +8,13 @@ export default function CategoryItem({ selected, category, categoryCount }) {
|
||||
className={
|
||||
(selected
|
||||
? ' bg-gray-600 text-white '
|
||||
: 'dark:text-gray-400 text-gray-500 ') +
|
||||
' flex text-sm items-center duration-300 cursor-pointer py-1 font-light px-2 whitespace-nowrap'
|
||||
: 'dark:text-gray-400 text-gray-900 ') +
|
||||
' hover:underline flex text-sm items-center duration-300 cursor-pointer py-1 whitespace-nowrap'
|
||||
}>
|
||||
<div>
|
||||
<i
|
||||
{/* <i
|
||||
className={`mr-2 fas ${selected ? 'fa-folder-open' : 'fa-folder'}`}
|
||||
/>
|
||||
/> */}
|
||||
{category} {categoryCount && `(${categoryCount})`}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -1,28 +1,56 @@
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import SocialButton from './SocialButton'
|
||||
|
||||
const Footer = ({ title }) => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
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 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 relative'
|
||||
>
|
||||
<DarkModeButton/>
|
||||
<i className='fas fa-copyright' /> {`${copyrightDate}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={siteConfig('LINK')} className='underline font-bold text-gray-500 dark:text-gray-300 '>{siteConfig('AUTHOR')}</a>.<br/>
|
||||
|
||||
{siteConfig('BEI_AN') && <><i className='fas fa-shield-alt'/> <a href='https://beian.miit.gov.cn/' className='mr-2'>{siteConfig('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/>
|
||||
<footer className='z-10 bg-black text-white flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 relative'>
|
||||
<DarkModeButton />
|
||||
<i className='fas fa-copyright' /> {`${copyrightDate}`}{' '}
|
||||
<span>
|
||||
<i className='mx-1 animate-pulse fas fa-heart' />{' '}
|
||||
<a
|
||||
href={siteConfig('LINK')}
|
||||
className='underline font-bold justify-start '>
|
||||
{siteConfig('AUTHOR')}
|
||||
</a>
|
||||
.<br />
|
||||
{siteConfig('BEI_AN') && (
|
||||
<>
|
||||
<i className='fas fa-shield-alt' />{' '}
|
||||
<a href='https://beian.miit.gov.cn/' className='mr-2'>
|
||||
{siteConfig('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>
|
||||
<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 {siteConfig('VERSION')}</a>.</span></span>
|
||||
<span className='text-xs font-serif'>
|
||||
Powered by{' '}
|
||||
<a
|
||||
href='https://github.com/tangly1024/NotionNext'
|
||||
className='underline justify-start text-white'>
|
||||
NotionNext {siteConfig('VERSION')}
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</span>
|
||||
<SocialButton />
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function Header(props) {
|
||||
const { customNav, customMenu } = props
|
||||
const [isOpen, changeShow] = useState(false)
|
||||
const collapseRef = useRef(null)
|
||||
const lastScrollY = useRef(0) // 用于存储上一次的滚动位置
|
||||
|
||||
const { locale } = useGlobal()
|
||||
|
||||
@@ -66,16 +67,39 @@ export default function Header(props) {
|
||||
|
||||
const scrollTrigger = throttle(() => {
|
||||
const scrollS = window.scrollY
|
||||
const nav = document.querySelector('#top-navbar')
|
||||
if (scrollS === lastScrollY.current) return // 如果滚动位置没有变化,则不做任何操作
|
||||
|
||||
const narrowNav = scrollS > 50
|
||||
const nav = document.querySelector('#top-navbar')
|
||||
const narrowNav = scrollS > 60
|
||||
if (narrowNav) {
|
||||
nav && nav.classList.replace('h-20', 'h-14')
|
||||
} else {
|
||||
nav && nav.classList.replace('h-14', 'h-20')
|
||||
}
|
||||
|
||||
lastScrollY.current = scrollS // 更新上一次的滚动位置
|
||||
}, throttleMs)
|
||||
|
||||
const [showSearchInput, changeShowSearchInput] = useState(false)
|
||||
|
||||
// 展示搜索框
|
||||
const toggleShowSearchInput = () => {
|
||||
if (siteConfig('ALGOLIA_APP_ID')) {
|
||||
searchModal.current.openSearch()
|
||||
} else {
|
||||
changeShowSearchInput(!showSearchInput)
|
||||
}
|
||||
}
|
||||
|
||||
const onKeyUp = e => {
|
||||
if (e.keyCode === 13) {
|
||||
const search = document.getElementById('simple-search').value
|
||||
if (search) {
|
||||
router.push({ pathname: '/search/' + search })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 开启自定义菜单,则覆盖Page生成的菜单
|
||||
if (siteConfig('CUSTOM_MENU')) {
|
||||
links = customMenu
|
||||
@@ -94,25 +118,61 @@ export default function Header(props) {
|
||||
{/* 导航栏菜单内容 */}
|
||||
<div
|
||||
id='top-navbar'
|
||||
className='px-4 flex w-full mx-auto max-w-screen-xl h-20 transition-all duration-200 items-between'>
|
||||
{/* 左侧图标Logo */}
|
||||
<LogoBar {...props} />
|
||||
className='flex w-full mx-auto max-w-7xl h-20 transition-all duration-200 items-center justify-between'>
|
||||
{/* 搜索栏 */}
|
||||
{showSearchInput && (
|
||||
<input
|
||||
autoFocus
|
||||
id='simple-search'
|
||||
onKeyUp={onKeyUp}
|
||||
className='outline-none flex flex-row text-base relative w-full border-b py-2'
|
||||
aria-label='Submit search'
|
||||
type='search'
|
||||
name='s'
|
||||
autoComplete='off'
|
||||
placeholder='Type then hit enter to search...'
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 移动端折叠按钮 */}
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-lg space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? (
|
||||
<i className='fas fa-times' />
|
||||
) : (
|
||||
<i className='fas fa-bars' />
|
||||
)}
|
||||
{/* 默认菜单 */}
|
||||
{!showSearchInput && (
|
||||
<>
|
||||
{/* 左侧图标Logo */}
|
||||
<div className='flex gap-x-8 h-full'>
|
||||
<LogoBar {...props} />
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex items-center gap-x-2'>
|
||||
{links &&
|
||||
links?.map(link => (
|
||||
<MenuItemDrop key={link?.id} link={link} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 右侧移动端折叠按钮 */}
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-lg space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? (
|
||||
<i className='fas fa-times' />
|
||||
) : (
|
||||
<i className='fas fa-bars' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex items-center'>
|
||||
{links &&
|
||||
links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{/* 搜索按钮 */}
|
||||
<div className='text-center items-center cursor-pointer'>
|
||||
<i
|
||||
className={
|
||||
showSearchInput
|
||||
? 'fa-regular fa-circle-xmark'
|
||||
: 'fa-solid fa-magnifying-glass' + ' align-middle'
|
||||
}
|
||||
onClick={toggleShowSearchInput}></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
62
themes/magzine/components/Hero.js
Normal file
62
themes/magzine/components/Hero.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// import { useGlobal } from '@/lib/global'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import PostItemCardTop from './PostItemCardTop'
|
||||
import PostItemCardWide from './PostItemCardWide'
|
||||
|
||||
/**
|
||||
* 首页主宣传
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const Hero = ({ posts }) => {
|
||||
// 获取置顶文章与次要文章
|
||||
const postTop = posts[0]
|
||||
const post1 = posts[1]
|
||||
const post2 = posts[2]
|
||||
// 首屏信息栏按钮文字
|
||||
const banner = siteConfig('MAGZINE_HOME_BANNER_ENABLE')
|
||||
const button = siteConfig('MAGZINE_HOME_BUTTON')
|
||||
const text = siteConfig('MAGZINE_HOME_BUTTON_TEXT')
|
||||
const url = siteConfig('MAGZINE_HOME_BUTTON_URL')
|
||||
const title = siteConfig('MAGZINE_HOME_TITLE')
|
||||
const description = siteConfig('MAGZINE_HOME_DESCRIPTION')
|
||||
const tips = siteConfig('MAGZINE_HOME_TIPS')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='w-full mx-auto max-w-7xl xl:flex justify-between'>
|
||||
{/* 左侧一篇主要置顶文章 */}
|
||||
<div className='basis-1/2 mb-6'>
|
||||
<PostItemCardTop post={postTop} />
|
||||
</div>
|
||||
{/* 右侧 */}
|
||||
<div>
|
||||
{/* 首屏介绍 */}
|
||||
{banner && (
|
||||
<div className='flex flex-col p-5 gap-y-5 dark items-center justify-between w-full bg-black text-white'>
|
||||
{/* 首屏导航按钮 */}
|
||||
<h2 className='text-2xl font-semibold'>{title}</h2>
|
||||
<h3 className='text-sm'>{description}</h3>
|
||||
{button && (
|
||||
<div className='mt-2 text-center px-6 py-3 font-semibold rounded-3xl text-black bg-[#7BE986] hover:bg-[#62BA6B]'>
|
||||
<Link href={url}>{text}</Link>
|
||||
</div>
|
||||
)}
|
||||
<span className='text-xs'>{tips}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 两篇次要文章 */}
|
||||
<div className='py-4'>
|
||||
<hr className='mb-8' />
|
||||
<PostItemCardWide post={post1} />
|
||||
<hr className='mb-8' />
|
||||
<PostItemCardWide post={post2} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Hero
|
||||
@@ -1,7 +1,5 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Router from 'next/router'
|
||||
import SocialButton from './SocialButton'
|
||||
|
||||
/**
|
||||
* 用户信息卡
|
||||
@@ -10,8 +8,9 @@ import SocialButton from './SocialButton'
|
||||
*/
|
||||
const InfoCard = props => {
|
||||
const { siteInfo } = props
|
||||
|
||||
return (
|
||||
<div id='info-card' className='py-4'>
|
||||
<div id='info-card'>
|
||||
<div className='items-center justify-start'>
|
||||
<div
|
||||
className='hover:scale-105 transform duration-200 cursor-pointer flex justify-start'
|
||||
@@ -24,13 +23,12 @@ const InfoCard = props => {
|
||||
alt={siteConfig('AUTHOR')}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-xl py-2 hover:scale-105 transform duration-200 flex justify-start dark:text-gray-300'>
|
||||
<div className='text-xl py-2 hover:scale-105 transform duration-200 flex justify-start '>
|
||||
{siteConfig('AUTHOR')}
|
||||
</div>
|
||||
<div className='font-light text-gray-600 mb-2 hover:scale-105 transform duration-200 flex justify-start dark:text-gray-400'>
|
||||
<div className='text-gray-100 mb-2 hover:scale-105 transform duration-200 flex justify-start'>
|
||||
{siteConfig('BIO')}
|
||||
</div>
|
||||
<SocialButton />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import CONFIG from '../config'
|
||||
|
||||
/**
|
||||
* 跳转到网页顶部
|
||||
@@ -10,18 +10,23 @@ import { siteConfig } from '@/lib/config'
|
||||
* @constructor
|
||||
*/
|
||||
const JumpToTopButton = ({ showPercent = false, percent, className }) => {
|
||||
if (!siteConfig('MEDIUM_WIDGET_TO_TOP', null, CONFIG)) {
|
||||
if (!siteConfig('MAGZINE_WIDGET_TO_TOP', null, CONFIG)) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<div
|
||||
id="jump-to-top"
|
||||
data-aos="fade-up"
|
||||
data-aos-duration="300"
|
||||
data-aos-once="false"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className='fixed xl:right-80 right-2 mr-10 bottom-24 z-20'>
|
||||
<i className='fas fa-chevron-up cursor-pointer p-2 rounded-full border bg-white dark:bg-hexo-black-gray' onClick={() => { window.scrollTo({ top: 0, behavior: 'smooth' }) }} />
|
||||
id='jump-to-top'
|
||||
data-aos='fade-up'
|
||||
data-aos-duration='300'
|
||||
data-aos-once='false'
|
||||
data-aos-anchor-placement='top-center'
|
||||
className='fixed xl:right-80 right-2 mr-10 bottom-24 z-20'>
|
||||
<i
|
||||
className='fas fa-chevron-up cursor-pointer p-2 rounded-full border bg-white dark:bg-hexo-black-gray'
|
||||
onClick={() => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ export default function LogoBar(props) {
|
||||
const { siteInfo } = props
|
||||
return (
|
||||
<div id='top-wrapper' className='w-full flex items-center '>
|
||||
<Link href='/' className='flex text-md md:text-xl dark:text-gray-200'>
|
||||
<Link
|
||||
href='/'
|
||||
className='flex text-md font-semibold md:text-xl hover:bg-black hover:text-white p-2 rounded-xl duration-200 dark:text-gray-200'>
|
||||
{/* <LazyImage
|
||||
src={siteInfo?.icon}
|
||||
width={24}
|
||||
|
||||
@@ -12,17 +12,17 @@ export const MenuBarMobile = props => {
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { name: locale.NAV.SEARCH, href: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
]
|
||||
|
||||
@@ -16,22 +16,22 @@ export const MenuItemDrop = ({ link }) => {
|
||||
|
||||
return (
|
||||
<li
|
||||
className='cursor-pointer list-none items-center flex mx-2'
|
||||
className='cursor-pointer list-none items-center h-full'
|
||||
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 ' +
|
||||
'h-full whitespace-nowrap duration-300 text-md justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected
|
||||
? 'bg-gray-600 text-white hover:text-white'
|
||||
: 'hover:text-gray-600')
|
||||
}>
|
||||
<div>
|
||||
<div className='items-center flex gap-x-1'>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
{hasSubMenu && (
|
||||
<i
|
||||
className={`px-2 fas fa-chevron-down duration-500 transition-all ${show ? ' rotate-180' : ''}`}></i>
|
||||
className={`px-1 fas fa-chevron-down duration-500 transition-all ${show ? ' rotate-180' : ''}`}></i>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,7 +40,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 ' +
|
||||
'px-3 gap-x-1 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected
|
||||
? 'bg-gray-600 text-white hover:text-white'
|
||||
: 'hover:text-gray-600')
|
||||
@@ -54,14 +54,14 @@ export const MenuItemDrop = ({ link }) => {
|
||||
{/* 子菜单 */}
|
||||
{hasSubMenu && (
|
||||
<ul
|
||||
className={`${show ? 'visible opacity-100 top-12 ' : 'invisible opacity-0 top-10 '} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
|
||||
className={`${show ? 'visible opacity-100 top-14 ' : 'invisible opacity-0 top-10 '} absolute border bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 block rounded-lg drop-shadow-lg p-4 `}>
|
||||
{link?.subMenus?.map(sLink => {
|
||||
return (
|
||||
<li
|
||||
key={sLink.id}
|
||||
className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
className='text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-xs font-extralight'>
|
||||
<span className='text-sm hover:underline'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
</span>
|
||||
|
||||
@@ -9,9 +9,9 @@ import CONFIG from '../config'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const PostItemCard = ({ post, showSummary }) => {
|
||||
const showPreview =
|
||||
siteConfig('MEDIUM_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
siteConfig('MAGZINE_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
@@ -29,7 +29,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
'cursor-pointer font-bold hover:underline text-3xl leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
}>
|
||||
<h2>
|
||||
{siteConfig('MEDIUM_POST_LIST_COVER', null, CONFIG) && (
|
||||
{siteConfig('MAGZINE_POST_LIST_COVER', null, CONFIG) && (
|
||||
<div className='w-full max-h-96 object-cover overflow-hidden mb-2'>
|
||||
<LazyImage
|
||||
src={post.pageCoverThumbnail}
|
||||
@@ -50,10 +50,10 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
'flex mt-2 items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}>
|
||||
<div className='text-sm py-1'>{post.date?.start_date}</div>
|
||||
{siteConfig('MEDIUM_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
{siteConfig('MAGZINE_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{siteConfig('MEDIUM_POST_LIST_TAG', null, CONFIG) &&
|
||||
{siteConfig('MAGZINE_POST_LIST_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
@@ -63,7 +63,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
<div className='flex'></div>
|
||||
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className='my-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
<main className='my-4 text-gray-700 dark:text-gray-300 text-sm leading-7'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
@@ -89,4 +89,4 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogPostCard
|
||||
export default PostItemCard
|
||||
45
themes/magzine/components/PostItemCardSimple.js
Normal file
45
themes/magzine/components/PostItemCardSimple.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import CategoryItem from './CategoryItem'
|
||||
|
||||
/**
|
||||
* 不带图片
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const PostItemCardSimple = ({ post, showSummary }) => {
|
||||
const showPreview =
|
||||
siteConfig('MAGZINE_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
className='mb-6 max-w-7xl border-t mr-8 py-2 gap-y-4 flex flex-col dark:border-gray-800 '>
|
||||
<div className='flex mr-2 items-center'>
|
||||
{siteConfig('MAGZINE_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 文章标题 */}
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer hover:underline text-lg leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
}>
|
||||
<h2>
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}
|
||||
{post.title}
|
||||
</h2>
|
||||
</Link>
|
||||
|
||||
<div className='text-sm py-2 text-gray-700'>{post.date?.start_date}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostItemCardSimple
|
||||
@@ -13,9 +13,9 @@ import TagItemMini from './TagItemMini'
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
const PostItemCardTop = ({ post, showSummary }) => {
|
||||
const showPreview =
|
||||
siteConfig('MEDIUM_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
siteConfig('MAGZINE_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
@@ -26,12 +26,12 @@ const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
data-aos-anchor-placement='top-bottom'
|
||||
className='mb-6 max-w-7xl '>
|
||||
<div className='flex flex-col w-full'>
|
||||
{siteConfig('MEDIUM_POST_LIST_COVER', null, CONFIG) && (
|
||||
{siteConfig('MAGZINE_POST_LIST_COVER', null, CONFIG) && (
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer hover:underline font-bold text-3xl leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
'cursor-pointer hover:underline text-4xl leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
}>
|
||||
<div className='w-full max-h-80 object-cover overflow-hidden mb-2'>
|
||||
<LazyImage
|
||||
@@ -43,15 +43,15 @@ const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<div className='flex py-2 items-center'>
|
||||
{siteConfig('MEDIUM_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<div className='flex py-2 mr-2 items-center'>
|
||||
{siteConfig('MAGZINE_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}>
|
||||
{siteConfig('MEDIUM_POST_LIST_TAG', null, CONFIG) &&
|
||||
{siteConfig('MAGZINE_POST_LIST_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
@@ -62,9 +62,9 @@ const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer hover:underline font-bold text-3xl leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
'cursor-pointer hover:underline leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
}>
|
||||
<h2 className='text-2xl'>
|
||||
<h2 className='text-4xl'>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
@@ -75,7 +75,7 @@ const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
<div className='flex'></div>
|
||||
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className='my-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
<main className='my-4 text-gray-900 dark:text-gray-300 text-lg leading-7'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
@@ -103,4 +103,4 @@ const BlogPostCardTop = ({ post, showSummary }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogPostCardTop
|
||||
export default PostItemCardTop
|
||||
@@ -8,21 +8,24 @@ import CONFIG from '../config'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
const BlogPostCardHorizontal = ({ post, showSummary }) => {
|
||||
/**
|
||||
* 水平左右布局的博客卡片
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const PostItemCardWide = ({ post, showSummary }) => {
|
||||
const showPreview =
|
||||
siteConfig('MEDIUM_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
siteConfig('MAGZINE_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
className='flex justify-between space-x-6 mb-6 border-top border-gray-200'>
|
||||
<div key={post.id} className='flex justify-between space-x-6 mb-6 '>
|
||||
{/* 卡牌左侧 */}
|
||||
<div className='h-40 w-96'>
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
' cursor-pointer font-bold hover:underline text-xl leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
' cursor-pointer font-semibold hover:underline text-xl leading-tight dark:text-gray-300 dark:hover:text-gray-400'
|
||||
}>
|
||||
<h3 className='max-w-80 break-words'>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
@@ -32,22 +35,8 @@ const BlogPostCardHorizontal = ({ post, showSummary }) => {
|
||||
</h3>
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={
|
||||
'flex mt-2 items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}>
|
||||
<div className='text-sm py-1'>{post.date?.start_date}</div>
|
||||
{siteConfig('MEDIUM_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{siteConfig('MEDIUM_POST_LIST_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className='my-4 line-clamp-2 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
<main className='my-4 line-clamp-2 text-gray-900 dark:text-gray-300 text-sm'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
@@ -68,6 +57,20 @@ const BlogPostCardHorizontal = ({ post, showSummary }) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={
|
||||
'flex mt-2 items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}>
|
||||
{siteConfig('MAGZINE_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{siteConfig('MAGZINE_POST_LIST_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
<div className='text-sm py-1'>{post.date?.start_date}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 卡牌右侧图片 */}
|
||||
@@ -82,4 +85,4 @@ const BlogPostCardHorizontal = ({ post, showSummary }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogPostCardHorizontal
|
||||
export default PostItemCardWide
|
||||
19
themes/magzine/components/PostListEmpty.js
Normal file
19
themes/magzine/components/PostListEmpty.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>
|
||||
{locale.COMMON.NO_RESULTS_FOUND}{' '}
|
||||
{currentSearch && <div>{currentSearch}</div>}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default PostListEmpty
|
||||
38
themes/magzine/components/PostListHorizontal.js
Normal file
38
themes/magzine/components/PostListHorizontal.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import Link from 'next/link'
|
||||
import PostItemCardSimple from './PostItemCardSimple'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
|
||||
/**
|
||||
* 博文水平列表
|
||||
* 含封面
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PostListHorizontal = ({ title, href, posts }) => {
|
||||
if (!posts || posts.length === 0) {
|
||||
return <PostListEmpty />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full py-10 bg-[#F6F6F1]'>
|
||||
<div className='max-w-7xl w-full mx-auto'>
|
||||
{/* 标题 */}
|
||||
<div className='flex justify-between items-center py-6'>
|
||||
<h3 className='text-2xl'>{title}</h3>
|
||||
<Link className='text-lg underline' href={href}>
|
||||
<span>查看全部</span>
|
||||
<i className='ml-2 fas fa-arrow-right' />
|
||||
</Link>
|
||||
</div>
|
||||
{/* 列表 */}
|
||||
<ul className='flex'>
|
||||
{posts?.map(p => {
|
||||
return <PostItemCardSimple key={p.id} post={p} />
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostListHorizontal
|
||||
@@ -1,8 +1,8 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import BlogPostListEmpty from './BlogPostListEmpty'
|
||||
import PaginationSimple from './PaginationSimple'
|
||||
import PostItemCard from './PostItemCard'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
|
||||
/**
|
||||
* 文章列表分页表格
|
||||
@@ -12,13 +12,13 @@ import PaginationSimple from './PaginationSimple'
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
||||
const PostListPage = ({ page = 1, posts = [], postCount }) => {
|
||||
const { NOTION_CONFIG } = useGlobal()
|
||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||
const totalPage = Math.ceil(postCount / POSTS_PER_PAGE)
|
||||
|
||||
if (!posts || posts.length === 0) {
|
||||
return <BlogPostListEmpty />
|
||||
return <PostListEmpty />
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -26,7 +26,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
||||
<div id='posts-wrapper'>
|
||||
{/* 文章列表 */}
|
||||
{posts?.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} />
|
||||
<PostItemCard key={post.id} post={post} />
|
||||
))}
|
||||
</div>
|
||||
<PaginationSimple page={page} totalPage={totalPage} />
|
||||
@@ -34,4 +34,4 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogPostListPage
|
||||
export default PostListPage
|
||||
@@ -3,8 +3,8 @@ import { useGlobal } from '@/lib/global'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import BlogPostListEmpty from './BlogPostListEmpty'
|
||||
import PostItemCard from './PostItemCard'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
|
||||
/**
|
||||
* 博客列表滚动分页
|
||||
@@ -13,7 +13,7 @@ import BlogPostListEmpty from './BlogPostListEmpty'
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListScroll = ({ posts = [], currentSearch }) => {
|
||||
const PostListScroll = ({ posts = [], currentSearch }) => {
|
||||
const { NOTION_CONFIG } = useGlobal()
|
||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||
const [page, updatePage] = useState(1)
|
||||
@@ -67,14 +67,14 @@ const BlogPostListScroll = ({ posts = [], currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
if (!postsToShow || postsToShow.length === 0) {
|
||||
return <BlogPostListEmpty currentSearch={currentSearch} />
|
||||
return <PostListEmpty currentSearch={currentSearch} />
|
||||
} else {
|
||||
return (
|
||||
<div id='posts-wrapper' 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} />
|
||||
<PostItemCard key={post.id} post={post} showSummary={true} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -104,4 +104,4 @@ const getPostByPage = function (page, totalPosts, POSTS_PER_PAGE) {
|
||||
return totalPosts.slice(0, POSTS_PER_PAGE * page)
|
||||
}
|
||||
|
||||
export default BlogPostListScroll
|
||||
export default PostListScroll
|
||||
37
themes/magzine/components/PostListSimpleHorizontal.js
Normal file
37
themes/magzine/components/PostListSimpleHorizontal.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import Link from 'next/link'
|
||||
import PostItemCardSimple from './PostItemCardSimple'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
|
||||
/**
|
||||
* 博文水平列表;不带封面图
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PostSimpleListHorizontal = ({ title, href, posts }) => {
|
||||
if (!posts || posts.length === 0) {
|
||||
return <PostListEmpty />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full py-10 bg-[#F6F6F1]'>
|
||||
<div className='max-w-7xl w-full mx-auto'>
|
||||
{/* 标题 */}
|
||||
<div className='flex justify-between items-center py-6'>
|
||||
<h3 className='text-2xl'>{title}</h3>
|
||||
<Link className='text-lg underline' href={href}>
|
||||
<span>查看全部</span>
|
||||
<i className='ml-2 fas fa-arrow-right' />
|
||||
</Link>
|
||||
</div>
|
||||
{/* 列表 */}
|
||||
<ul className='flex'>
|
||||
{posts?.map(p => {
|
||||
return <PostItemCardSimple key={p.id} post={p} />
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostSimpleListHorizontal
|
||||
@@ -5,7 +5,7 @@ import { useGlobal } from '@/lib/global'
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function BlogPostBar(props) {
|
||||
export default function PostListSlotBar(props) {
|
||||
const { tag, category } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
@@ -21,14 +21,15 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
|
||||
setLoadingState(true)
|
||||
location.href = '/search/' + key
|
||||
} else {
|
||||
router.push({ pathname: '/' }).then(r => {
|
||||
})
|
||||
router.push({ pathname: '/' }).then(r => {})
|
||||
}
|
||||
}
|
||||
const handleKeyUp = (e) => {
|
||||
if (e.keyCode === 13) { // 回车
|
||||
const handleKeyUp = e => {
|
||||
if (e.keyCode === 13) {
|
||||
// 回车
|
||||
handleSearch(searchInputRef.current.value)
|
||||
} else if (e.keyCode === 27) { // ESC
|
||||
} else if (e.keyCode === 27) {
|
||||
// ESC
|
||||
cleanSearch()
|
||||
}
|
||||
}
|
||||
@@ -37,7 +38,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
|
||||
}
|
||||
|
||||
const [showClean, setShowClean] = useState(false)
|
||||
const updateSearchKey = (val) => {
|
||||
const updateSearchKey = val => {
|
||||
if (lock) {
|
||||
return
|
||||
}
|
||||
@@ -49,38 +50,48 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
|
||||
setShowClean(false)
|
||||
}
|
||||
}
|
||||
function lockSearchInput () {
|
||||
function lockSearchInput() {
|
||||
lock = true
|
||||
}
|
||||
|
||||
function unLockSearchInput () {
|
||||
function unLockSearchInput() {
|
||||
lock = false
|
||||
}
|
||||
|
||||
return <div className={'flex w-full bg-gray-100 ' + className}>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
className={'outline-none w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
onCompositionEnd={unLockSearchInput}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={currentSearch}
|
||||
/>
|
||||
return (
|
||||
<div className={'flex w-full bg-gray-100 ' + className}>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
className={
|
||||
'outline-none w-full text-sm pl-2 transition focus:shadow-lg leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'
|
||||
}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
onCompositionEnd={unLockSearchInput}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={currentSearch}
|
||||
/>
|
||||
|
||||
<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'} `} />
|
||||
</div>
|
||||
|
||||
{(showClean &&
|
||||
<div className='-ml-12 cursor-pointer float-right items-center justify-center py-2'>
|
||||
<i className='fas fa-times hover:text-black transform duration-200 text-gray-400 cursor-pointer dark:hover:text-gray-300' onClick={cleanSearch} />
|
||||
<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'} `}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showClean && (
|
||||
<div className='-ml-12 cursor-pointer float-right items-center justify-center py-2'>
|
||||
<i
|
||||
className='fas fa-times hover:text-black transform duration-200 text-gray-400 cursor-pointer dark:hover:text-gray-300'
|
||||
onClick={cleanSearch}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchInput
|
||||
|
||||
@@ -8,12 +8,15 @@ const TagItemMini = ({ tag, selected = false }) => {
|
||||
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
|
||||
${selected
|
||||
? 'text-white dark:text-gray-300 bg-black dark:bg-black dark:hover:bg-gray-900'
|
||||
: `text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}` }>
|
||||
|
||||
<div className='font-light dark:text-gray-400'>{selected && <i className='mr-1 fas fa-tag'/>} {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
|
||||
|
||||
${
|
||||
selected
|
||||
? 'text-white dark:text-gray-300 dark:hover:bg-gray-900'
|
||||
: `text-gray-900 hover:shadow-xl dark:border-gray-400 dark:bg-gray-800`
|
||||
}`}>
|
||||
<div className=' dark:text-gray-400'>
|
||||
{/* {selected && <i className='mr-1 fas fa-tag'/>} */}#
|
||||
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,25 +24,25 @@ export default function TopNavBar(props) {
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('MEDIUM_MENU_SEARCH', null, CONFIG)
|
||||
show: siteConfig('MAGZINE_MENU_SEARCH', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,24 +1,35 @@
|
||||
const CONFIG = {
|
||||
// 首屏信息栏按钮文字
|
||||
MAGZINE_HOME_BANNER_ENABLE: true, // 首屏右上角的宣传位
|
||||
MAGZINE_HOME_BUTTON: true,
|
||||
MAGZINE_HOME_BUTTON_URL: '/about',
|
||||
MAGZINE_HOME_BUTTON_TEXT: '了解更多',
|
||||
|
||||
MAGZINE_HOME_TITLE: '立即开创您的在线业务。完全免费。',
|
||||
MAGZINE_HOME_DESCRIPTION:
|
||||
'借助NotionNext,获得助您开创、经营和扩展业务所需的全部工具和帮助。',
|
||||
MAGZINE_HOME_TIPS: 'AI时代来临,这是属于超级个体的狂欢盛宴!',
|
||||
|
||||
// Style
|
||||
MEDIUM_RIGHT_PANEL_DARK: process.env.NEXT_PUBLIC_MEDIUM_RIGHT_DARK || false, // 右侧面板深色模式
|
||||
MAGZINE_RIGHT_PANEL_DARK: process.env.NEXT_PUBLIC_MAGZINE_RIGHT_DARK || false, // 右侧面板深色模式
|
||||
|
||||
MEDIUM_POST_LIST_COVER: true, // 文章列表显示图片封面
|
||||
MEDIUM_POST_LIST_PREVIEW: true, // 列表显示文章预览
|
||||
MEDIUM_POST_LIST_CATEGORY: true, // 列表显示文章分类
|
||||
MEDIUM_POST_LIST_TAG: true, // 列表显示文章标签
|
||||
MAGZINE_POST_LIST_COVER: true, // 文章列表显示图片封面
|
||||
MAGZINE_POST_LIST_PREVIEW: true, // 列表显示文章预览
|
||||
MAGZINE_POST_LIST_CATEGORY: true, // 列表显示文章分类
|
||||
MAGZINE_POST_LIST_TAG: true, // 列表显示文章标签
|
||||
|
||||
MEDIUM_POST_DETAIL_CATEGORY: true, // 文章显示分类
|
||||
MEDIUM_POST_DETAIL_TAG: true, // 文章显示标签
|
||||
MAGZINE_POST_DETAIL_CATEGORY: true, // 文章显示分类
|
||||
MAGZINE_POST_DETAIL_TAG: true, // 文章显示标签
|
||||
|
||||
// 菜单
|
||||
MEDIUM_MENU_CATEGORY: true, // 显示分类
|
||||
MEDIUM_MENU_TAG: true, // 显示标签
|
||||
MEDIUM_MENU_ARCHIVE: true, // 显示归档
|
||||
MEDIUM_MENU_SEARCH: true, // 显示搜索
|
||||
MAGZINE_MENU_CATEGORY: true, // 显示分类
|
||||
MAGZINE_MENU_TAG: true, // 显示标签
|
||||
MAGZINE_MENU_ARCHIVE: true, // 显示归档
|
||||
MAGZINE_MENU_SEARCH: true, // 显示搜索
|
||||
|
||||
// Widget
|
||||
MEDIUM_WIDGET_REVOLVER_MAPS: process.env.NEXT_PUBLIC_WIDGET_REVOLVER_MAPS || 'false', // 地图插件
|
||||
MEDIUM_WIDGET_TO_TOP: true // 跳回顶部
|
||||
MAGZINE_WIDGET_REVOLVER_MAPS:
|
||||
process.env.NEXT_PUBLIC_WIDGET_REVOLVER_MAPS || 'false', // 地图插件
|
||||
MAGZINE_WIDGET_TO_TOP: true // 跳回顶部
|
||||
}
|
||||
export default CONFIG
|
||||
|
||||
@@ -8,21 +8,20 @@ import { isBrowser } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import Announcement from './components/Announcement'
|
||||
import ArchiveItem from './components/ArchiveItem'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import ArticleInfo from './components/ArticleInfo'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||
import BlogPostCardHorizontal from './components/BlogPostCardHorizontal'
|
||||
import BlogPostCardTop from './components/BlogPostCardTop'
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import Catalog from './components/Catalog'
|
||||
import CategoryGroup from './components/CategoryGroup'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import Footer from './components/Footer'
|
||||
import Header from './components/Header'
|
||||
import InfoCard from './components/InfoCard'
|
||||
import Hero from './components/Hero'
|
||||
import PostListHorizontal from './components/PostListHorizontal'
|
||||
import PostListPage from './components/PostListPage'
|
||||
import PostListScroll from './components/PostListScroll'
|
||||
import PostSimpleListHorizontal from './components/PostListSimpleHorizontal'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import TagGroups from './components/TagGroups'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
@@ -81,33 +80,72 @@ const LayoutBase = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutIndex = props => {
|
||||
const { posts, notice } = props
|
||||
const top = posts[0]
|
||||
const post1 = posts[1]
|
||||
const post2 = posts[2]
|
||||
return (
|
||||
<div className='container mx-auto max-w-7xl'>
|
||||
{/* 首屏文章 */}
|
||||
const { posts, allNavPages } = props
|
||||
// 最新文章 从第4个元素开始截取出4个
|
||||
const newPosts = posts.slice(3, 7)
|
||||
|
||||
<div className='md:flex justify-between py-10 md:py-16'>
|
||||
<div className='basis-1/2 mb-6'>
|
||||
<BlogPostCardTop post={top} />
|
||||
</div>
|
||||
<div className='px-10'>
|
||||
<div className='flex justify-between px-4 w-full'>
|
||||
<InfoCard {...props} />
|
||||
<Announcement post={notice} />
|
||||
</div>
|
||||
{/* 两篇主要文章 */}
|
||||
<div>
|
||||
<BlogPostCardHorizontal post={post1} />
|
||||
<BlogPostCardHorizontal post={post2} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// 按分类将文章分组成文件夹
|
||||
const categoryFolders = groupArticles(allNavPages.slice(8))
|
||||
|
||||
return (
|
||||
<div className='py-10 md:py-18'>
|
||||
{/* 首屏宣传区块 */}
|
||||
<Hero posts={posts} />
|
||||
|
||||
{/* 最新文章区块 */}
|
||||
<PostSimpleListHorizontal
|
||||
title='最新文章'
|
||||
href='/archive'
|
||||
posts={newPosts}
|
||||
/>
|
||||
|
||||
{/* 不同的分类文章列表 */}
|
||||
{categoryFolders?.map((categoryGroup, index) => {
|
||||
if (
|
||||
!categoryGroup ||
|
||||
!categoryGroup.items ||
|
||||
categoryGroup.items.length < 1
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<PostListHorizontal
|
||||
title={categoryGroup?.category}
|
||||
href={`/category/${categoryGroup?.category}`}
|
||||
posts={categoryGroup?.items}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// 按照分类将文章分组成文件夹
|
||||
function groupArticles(allPosts) {
|
||||
if (!allPosts) {
|
||||
return []
|
||||
}
|
||||
const groups = []
|
||||
|
||||
for (let i = 0; i < allPosts.length; i++) {
|
||||
const item = allPosts[i]
|
||||
const categoryName = item?.category ? item?.category : '' // 将 category 转换为字符串
|
||||
|
||||
let existingGroup = groups.find(group => group.category === categoryName) // 搜索同名的最后一个分组
|
||||
|
||||
if (existingGroup && existingGroup.category === categoryName) {
|
||||
// 如果分组已存在,并且该分组中的文章数量小于4,添加文章
|
||||
if (existingGroup.items.length < 4) {
|
||||
existingGroup.items.push(item)
|
||||
}
|
||||
} else {
|
||||
// 新建分组,并添加当前文章
|
||||
groups.push({ category: categoryName, items: [item] })
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
/**
|
||||
* 博客列表
|
||||
@@ -117,9 +155,9 @@ const LayoutPostList = props => {
|
||||
return (
|
||||
<>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogPostListPage {...props} />
|
||||
<PostListPage {...props} />
|
||||
) : (
|
||||
<BlogPostListScroll {...props} />
|
||||
<PostListScroll {...props} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
@@ -180,10 +218,10 @@ const LayoutSlug = props => {
|
||||
<ShareBar post={post} />
|
||||
{/* 文章分类和标签信息 */}
|
||||
<div className='flex justify-between'>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_CATEGORY', null, CONFIG) &&
|
||||
{siteConfig('MAGZINE_POST_DETAIL_CATEGORY', null, CONFIG) &&
|
||||
post?.category && <CategoryItem category={post?.category} />}
|
||||
<div>
|
||||
{siteConfig('MEDIUM_POST_DETAIL_TAG', null, CONFIG) &&
|
||||
{siteConfig('MAGZINE_POST_DETAIL_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
@@ -245,9 +283,9 @@ const LayoutSearch = props => {
|
||||
{currentSearch && (
|
||||
<div>
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogPostListPage {...props} />
|
||||
<PostListPage {...props} />
|
||||
) : (
|
||||
<BlogPostListScroll {...props} />
|
||||
<PostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -266,7 +304,7 @@ const LayoutArchive = props => {
|
||||
<>
|
||||
<div className='mb-10 pb-20 md:py-12 py-3 min-h-full'>
|
||||
{Object.keys(archivePosts)?.map(archiveTitle => (
|
||||
<BlogArchiveItem
|
||||
<ArchiveItem
|
||||
key={archiveTitle}
|
||||
archiveTitle={archiveTitle}
|
||||
archivePosts={archivePosts}
|
||||
|
||||
Reference in New Issue
Block a user