mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-24 23:16:52 +00:00
Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
@@ -8,7 +8,7 @@ const BLOG = {
|
|||||||
KEYWORDS: 'Notion, 博客', // 网站关键词 英文逗号隔开
|
KEYWORDS: 'Notion, 博客', // 网站关键词 英文逗号隔开
|
||||||
NOTION_PAGE_ID: process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', // Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
NOTION_PAGE_ID: process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', // Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
||||||
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||||
DEBUG_BUTTON: true, // 是否显示调试按钮,可以用来调试不同的主题和样式配置
|
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
||||||
|
|
||||||
THEME: process.env.NEXT_PUBLIC_THEME || 'next', // 主题, 支持 ['Next','Hexo',"Fukasawa','Medium']
|
THEME: process.env.NEXT_PUBLIC_THEME || 'next', // 主题, 支持 ['Next','Hexo',"Fukasawa','Medium']
|
||||||
LANG: 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more.
|
LANG: 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more.
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import * as ThemeMap from '@/themes'
|
|
||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @returns 调试面板
|
|
||||||
*/
|
|
||||||
export function DebugButton () {
|
|
||||||
const [show, setShow] = useState(false)
|
|
||||||
const GlobalConfig = useGlobal()
|
|
||||||
const { theme, setTheme } = GlobalConfig
|
|
||||||
const allThemes = Object.keys(ThemeMap)
|
|
||||||
function toggleShow () {
|
|
||||||
setShow(!show)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换主题
|
|
||||||
*/
|
|
||||||
function changeTheme () {
|
|
||||||
const currentIndex = allThemes.indexOf(theme)
|
|
||||||
const newIndex = currentIndex < allThemes.length - 1 ? currentIndex + 1 : 0
|
|
||||||
setTheme(allThemes[newIndex])
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div className={`w-full text-sm font-sans h-72 p-5 bg-white fixed right-0 bottom-0 z-40 shadow-card duration-200 ${show ? '' : '-bottom-72'}`}>
|
|
||||||
<div className='flex space-x-1'>
|
|
||||||
<div className='font-bold'>当前主题:</div>
|
|
||||||
<div>{theme}</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex space-x-1'>
|
|
||||||
<div className='font-bold'>所有主题:</div>
|
|
||||||
<div>{allThemes.join(',')}</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex space-x-1'>
|
|
||||||
<div className='bg-blue-500 text-white p-2 cursor-pointer' onClick={changeTheme}>更换主题</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className='font-bold w-18'>所有配置:</div>
|
|
||||||
<div><p>{JSON.stringify(GlobalConfig)}</p></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="fixed right-20 bottom-12 z-50">
|
|
||||||
<div className="bg-gray-50 text-sm dark:bg-black dark:text-white shadow-2xl p-2.5 rounded-md bg-opacity-75 cursor-pointer" onClick={toggleShow}>调试按钮</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
124
components/DebugPanel.js
Normal file
124
components/DebugPanel.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import * as ThemeMap from '@/themes'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import Select from './Select'
|
||||||
|
import { ALL_THEME } from '@/lib/theme'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns 调试面板
|
||||||
|
*/
|
||||||
|
export function DebugPanel () {
|
||||||
|
const [show, setShow] = useState(false)
|
||||||
|
const GlobalConfig = useGlobal()
|
||||||
|
const router = useRouter()
|
||||||
|
const { theme, setTheme } = GlobalConfig
|
||||||
|
const themeOptions = []
|
||||||
|
ALL_THEME.forEach(t => {
|
||||||
|
themeOptions.push({ value: t, text: t })
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleShow () {
|
||||||
|
setShow(!show)
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTheme () {
|
||||||
|
const currentIndex = ALL_THEME.indexOf(theme)
|
||||||
|
const newIndex = currentIndex < ALL_THEME.length - 1 ? currentIndex + 1 : 0
|
||||||
|
changeTheme(ALL_THEME[newIndex])
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 切换主题
|
||||||
|
*/
|
||||||
|
function changeTheme (theme) {
|
||||||
|
router.query.theme = ''
|
||||||
|
setTheme(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterResult (text) {
|
||||||
|
switch (text) {
|
||||||
|
case 'true':
|
||||||
|
return <span className='text-green-500'>true</span>
|
||||||
|
case 'false':
|
||||||
|
return <span className='text-red-500'>false</span>
|
||||||
|
case '':
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 调试按钮 */}
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{ writingMode: 'vertical-lr' }}
|
||||||
|
className={`bg-black text-white shadow-2xl p-2.5 rounded-l-xl cursor-pointer ${show ? 'right-96' : 'right-0'} fixed bottom-36 duration-200 z-50`}
|
||||||
|
onClick={toggleShow}
|
||||||
|
>
|
||||||
|
{show
|
||||||
|
? (
|
||||||
|
<i className="fas fa-times"> 关闭调试</i>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<i className="fas fa-tools"> 打开调试</i>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={` ${
|
||||||
|
show ? 'shadow-card' : '-right-96'
|
||||||
|
} w-96 overflow-y-scroll font-sans h-full p-5 bg-white fixed right-0 bottom-0 z-50 duration-200`}
|
||||||
|
>
|
||||||
|
<div className="flex space-x-1 my-12">
|
||||||
|
<Select
|
||||||
|
label="主题切换"
|
||||||
|
value={theme}
|
||||||
|
options={themeOptions}
|
||||||
|
onChange={changeTheme}
|
||||||
|
/>
|
||||||
|
<div className="p-2 cursor-pointer" onClick={switchTheme}>
|
||||||
|
<i className="fas fa-sync" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="font-bold w-18 border-b my-2">
|
||||||
|
站点配置[blog.config.js]
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">
|
||||||
|
{Object.keys(BLOG).map(k => (
|
||||||
|
<div key={k} className="justify-between flex py-1">
|
||||||
|
<span className="bg-blue-400 p-0.5 rounded text-white mr-2">
|
||||||
|
{k}
|
||||||
|
</span>
|
||||||
|
<span className="whitespace-nowrap">
|
||||||
|
{filterResult(BLOG[k] + '')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-bold w-18 border-b my-2">
|
||||||
|
主题配置{'(config_' + theme + '.js)'}:
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">
|
||||||
|
{Object.keys(ThemeMap[theme].THEME_CONFIG).map(k => (
|
||||||
|
<div key={k} className="justify-between flex py-1">
|
||||||
|
<span className="bg-indigo-500 p-0.5 rounded text-white mr-2">
|
||||||
|
{k}
|
||||||
|
</span>
|
||||||
|
<span className="whitespace-nowrap">
|
||||||
|
{filterResult(ThemeMap[theme].THEME_CONFIG[k] + '')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
40
components/Select.js
Normal file
40
components/Select.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下拉单选框
|
||||||
|
*/
|
||||||
|
class Select extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.handleChange = this.handleChange.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange (event) {
|
||||||
|
const { onChange } = this.props
|
||||||
|
onChange(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className='py-1 space-x-3'>
|
||||||
|
<label className='text-gray-500'>{this.props.label}</label>
|
||||||
|
<select value={this.props.value} onChange={this.handleChange} className='border p-1 rounded cursor-pointer'>
|
||||||
|
{this.props.options?.map(o => (
|
||||||
|
<option key={o.value} value={o.value}>
|
||||||
|
{o.text}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Select.defaultProps = {
|
||||||
|
label: '',
|
||||||
|
value: '1',
|
||||||
|
options: [
|
||||||
|
{ value: '1', text: '选项1' },
|
||||||
|
{ value: '2', text: '选项2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
export default Select
|
||||||
@@ -44,7 +44,7 @@ const SideBarDrawer = ({ children, isOpen, onOpen, onClose, className }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div id='sidebar-wrapper' className={' ' + className}>
|
return <div id='sidebar-wrapper' className={' ' + className}>
|
||||||
<div id='sidebar-drawer' className={`${isOpen ? 'ml-0' : '-ml-80'} bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-50`}>
|
<div id='sidebar-drawer' className={`${isOpen ? 'ml-0' : '-ml-80'} bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-40`}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
{/* 背景蒙版 */}
|
{/* 背景蒙版 */}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import lang from './lang'
|
import lang from './lang'
|
||||||
import { useContext, createContext, useState } from 'react'
|
import { useContext, createContext, useState } from 'react'
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import { initDarkMode } from './theme'
|
import { ALL_THEME, initDarkMode } from './theme'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
const GlobalContext = createContext()
|
const GlobalContext = createContext()
|
||||||
let hasInit = false
|
let hasInit = false
|
||||||
|
|
||||||
@@ -32,8 +33,13 @@ export function GlobalContextProvider ({ children }) {
|
|||||||
hasInit = true
|
hasInit = true
|
||||||
initLocale(locale, updateLocale)
|
initLocale(locale, updateLocale)
|
||||||
initDarkMode(isDarkMode, updateDarkMode)
|
initDarkMode(isDarkMode, updateDarkMode)
|
||||||
|
// 读取浏览器参数中的主题
|
||||||
|
const userTheme = Router?.router?.query?.theme
|
||||||
|
if (userTheme && ALL_THEME.indexOf(userTheme) > -1) {
|
||||||
|
setTheme(userTheme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 100)
|
}, 50)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GlobalContext.Provider value={{ onLoading, locale, isDarkMode, updateDarkMode, theme, setTheme }}>
|
<GlobalContext.Provider value={{ onLoading, locale, isDarkMode, updateDarkMode, theme, setTheme }}>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import cookie from 'react-cookies'
|
import cookie from 'react-cookies'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
|
export const ALL_THEME = ['next', 'fukasawa', 'hexo', 'empty', 'medium']
|
||||||
/**
|
/**
|
||||||
* 初始化主题
|
* 初始化主题
|
||||||
* @param isDarkMode
|
* @param isDarkMode
|
||||||
|
|||||||
14
pages/404.js
14
pages/404.js
@@ -1,14 +0,0 @@
|
|||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import * as ThemeMap from '@/themes'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义404界面
|
|
||||||
* @returns {JSX.Element}
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function Custom404 (props) {
|
|
||||||
const { theme } = useGlobal()
|
|
||||||
const ThemeComponents = ThemeMap[theme]
|
|
||||||
return <ThemeComponents.Layout404 {...props}/>
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ import BLOG from '@/blog.config'
|
|||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import Custom404 from './404'
|
|
||||||
import * as ThemeMap from '@/themes'
|
import * as ThemeMap from '@/themes'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +13,7 @@ const Slug = (props) => {
|
|||||||
const { theme } = useGlobal()
|
const { theme } = useGlobal()
|
||||||
const ThemeComponents = ThemeMap[theme]
|
const ThemeComponents = ThemeMap[theme]
|
||||||
if (!props.post) {
|
if (!props.post) {
|
||||||
return <Custom404 {...props} />
|
return <ThemeComponents.Layout404 {...props}/>
|
||||||
}
|
}
|
||||||
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={false}/>
|
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={false}/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import 'prismjs/themes/prism-okaidia.css'
|
|||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { GlobalContextProvider } from '@/lib/global'
|
import { GlobalContextProvider } from '@/lib/global'
|
||||||
import { DebugButton } from '@/components/DebugButton'
|
import { DebugPanel } from '@/components/DebugPanel'
|
||||||
|
|
||||||
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
||||||
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
|
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
|
||||||
@@ -24,7 +24,7 @@ const GoogleAdsense = dynamic(() => import('@/components/GoogleAdsense'), { ssr:
|
|||||||
const MyApp = ({ Component, pageProps }) => {
|
const MyApp = ({ Component, pageProps }) => {
|
||||||
return (
|
return (
|
||||||
<GlobalContextProvider>
|
<GlobalContextProvider>
|
||||||
{BLOG.DEBUG_BUTTON && <DebugButton/>}
|
{BLOG.DEBUG && <DebugPanel/>}
|
||||||
{BLOG.ANALYTICS_ACKEE_TRACKER && <Ackee />}
|
{BLOG.ANALYTICS_ACKEE_TRACKER && <Ackee />}
|
||||||
{BLOG.ANALYTICS_GOOGLE_ID && <Gtag />}
|
{BLOG.ANALYTICS_GOOGLE_ID && <Gtag />}
|
||||||
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <Busuanzi/>}
|
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <Busuanzi/>}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import BLOG from '@/blog.config'
|
|||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import Custom404 from '../404'
|
|
||||||
import * as ThemeMap from '@/themes'
|
import * as ThemeMap from '@/themes'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +13,7 @@ const Slug = (props) => {
|
|||||||
const { theme } = useGlobal()
|
const { theme } = useGlobal()
|
||||||
const ThemeComponents = ThemeMap[theme]
|
const ThemeComponents = ThemeMap[theme]
|
||||||
if (!props.post) {
|
if (!props.post) {
|
||||||
return <Custom404 {...props} />
|
return <ThemeComponents.Layout404 {...props}/>
|
||||||
}
|
}
|
||||||
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true}/>
|
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true}/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import BLOG from '@/blog.config'
|
|||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import Custom404 from '../404'
|
|
||||||
import * as ThemeMap from '@/themes'
|
import * as ThemeMap from '@/themes'
|
||||||
|
|
||||||
const Page = (props) => {
|
const Page = (props) => {
|
||||||
const { theme } = useGlobal()
|
const { theme } = useGlobal()
|
||||||
const ThemeComponents = ThemeMap[theme]
|
const ThemeComponents = ThemeMap[theme]
|
||||||
if (!props?.meta) {
|
if (!props?.meta) {
|
||||||
return <Custom404 {...props} />
|
return <ThemeComponents.Layout404 {...props}/>
|
||||||
}
|
}
|
||||||
return <ThemeComponents.LayoutPage {...props} />
|
return <ThemeComponents.LayoutPage {...props} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
|
||||||
|
|
||||||
* @returns {JSX.Element}
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
const LayoutBase = (props) => {
|
|
||||||
const { children, meta } = props
|
|
||||||
return <div>
|
|
||||||
<CommonHead meta={meta} />
|
|
||||||
<main id='wrapper' className='flex justify-center flex-1 pb-12'>
|
|
||||||
{children}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LayoutBase
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import LayoutBase from './LayoutBase'
|
|
||||||
|
|
||||||
export const LayoutIndex = (props) => {
|
|
||||||
// const { posts, tags, meta, categories, postCount, latestPosts } = props
|
|
||||||
return <LayoutBase {...props}>Index</LayoutBase>
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import LayoutBase from './LayoutBase'
|
|
||||||
|
|
||||||
export const LayoutSearch = (props) => {
|
|
||||||
return <LayoutBase {...props}>
|
|
||||||
</LayoutBase>
|
|
||||||
}
|
|
||||||
37
themes/empty/LayoutBase.js
Normal file
37
themes/empty/LayoutBase.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||||
|
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const LayoutBase = props => {
|
||||||
|
const { children, meta } = props
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<CommonHead meta={meta} />
|
||||||
|
{/* 导航菜单 */}
|
||||||
|
<div className="w-full flex justify-center my-2">
|
||||||
|
<nav className="max-w-6xl space-x-3 underline">
|
||||||
|
<Link href="/">
|
||||||
|
<a>首页</a>
|
||||||
|
</Link>
|
||||||
|
<Link href="/category">
|
||||||
|
<a>分类</a>
|
||||||
|
</Link>
|
||||||
|
<Link href="/tag">
|
||||||
|
<a>标签</a>
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
{/* 内容主体 */}
|
||||||
|
<main id="wrapper" className="flex justify-center flex-1 pb-12">
|
||||||
|
<div className="max-w-6xl px-3">{children}</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutBase
|
||||||
18
themes/empty/LayoutIndex.js
Normal file
18
themes/empty/LayoutIndex.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import LayoutBase from './LayoutBase'
|
||||||
|
|
||||||
|
export const LayoutIndex = props => {
|
||||||
|
const { posts } = props
|
||||||
|
return (
|
||||||
|
<LayoutBase {...props}>
|
||||||
|
{posts.map(p => (
|
||||||
|
<div key={p.id} className='border my-12'>
|
||||||
|
<Link href={`/article/${p.slug}`}>
|
||||||
|
<a className='underline cursor-pointer'>{p.title}</a>
|
||||||
|
</Link>
|
||||||
|
<div>{p.summary}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</LayoutBase>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
themes/empty/LayoutSearch.js
Normal file
42
themes/empty/LayoutSearch.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import LayoutBase from './LayoutBase'
|
||||||
|
|
||||||
|
export const LayoutSearch = props => {
|
||||||
|
const { keyword, posts } = props
|
||||||
|
const router = useRouter()
|
||||||
|
const currentSearch = keyword || router?.query?.s
|
||||||
|
let handleTextColor = false
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (currentSearch && !handleTextColor) {
|
||||||
|
const container = document.getElementById('container')
|
||||||
|
if (container && container.innerHTML) {
|
||||||
|
const re = new RegExp(`${currentSearch}`, 'gim')
|
||||||
|
container.innerHTML = container.innerHTML.replace(
|
||||||
|
re,
|
||||||
|
`<span class='text-red-500 border-b border-dashed'>${currentSearch}</span>`
|
||||||
|
)
|
||||||
|
handleTextColor = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutBase {...props}>
|
||||||
|
<h2>Search</h2>
|
||||||
|
<div id="container">
|
||||||
|
{posts.map(p => (
|
||||||
|
<div key={p.id} className="border my-12">
|
||||||
|
<Link href={`/article/${p.slug}`}>
|
||||||
|
<a className="underline cursor-pointer">{p.title}</a>
|
||||||
|
</Link>
|
||||||
|
<div>{p.summary}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</LayoutBase>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,14 +6,20 @@ import 'prismjs/components/prism-javascript'
|
|||||||
import 'prismjs/components/prism-markup'
|
import 'prismjs/components/prism-markup'
|
||||||
import 'prismjs/components/prism-python'
|
import 'prismjs/components/prism-python'
|
||||||
import 'prismjs/components/prism-typescript'
|
import 'prismjs/components/prism-typescript'
|
||||||
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
|
import {
|
||||||
|
Code,
|
||||||
|
Collection,
|
||||||
|
CollectionRow,
|
||||||
|
Equation,
|
||||||
|
NotionRenderer
|
||||||
|
} from 'react-notion-x'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
const mapPageUrl = id => {
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LayoutSlug = (props) => {
|
export const LayoutSlug = props => {
|
||||||
const { post } = props
|
const { post } = props
|
||||||
const meta = {
|
const meta = {
|
||||||
title: `${post.title} | ${BLOG.TITLE}`,
|
title: `${post.title} | ${BLOG.TITLE}`,
|
||||||
@@ -27,25 +33,27 @@ export const LayoutSlug = (props) => {
|
|||||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <LayoutBase {...props} meta={meta}>
|
return (
|
||||||
<h1>Slug - {post?.title}</h1>
|
<LayoutBase {...props} meta={meta}>
|
||||||
<p>
|
<div>
|
||||||
{/* Notion文章主体 */}
|
<h2>{post?.title}</h2>
|
||||||
<section id='notion-article' className='px-1'>
|
<p>
|
||||||
{post.blockMap && (
|
<section id="notion-article" className="px-1">
|
||||||
<NotionRenderer
|
{post.blockMap && (
|
||||||
recordMap={post.blockMap}
|
<NotionRenderer
|
||||||
mapPageUrl={mapPageUrl}
|
recordMap={post.blockMap}
|
||||||
components={{
|
mapPageUrl={mapPageUrl}
|
||||||
equation: Equation,
|
components={{
|
||||||
code: Code,
|
equation: Equation,
|
||||||
collectionRow: CollectionRow,
|
code: Code,
|
||||||
collection: Collection
|
collectionRow: CollectionRow,
|
||||||
}}
|
collection: Collection
|
||||||
/>
|
}}
|
||||||
)}
|
/>
|
||||||
</section>
|
)}
|
||||||
</p>
|
</section>
|
||||||
|
</p>
|
||||||
</LayoutBase>
|
</div>
|
||||||
|
</LayoutBase>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ const LayoutBase = (props) => {
|
|||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<AsideLeft {...props}/>
|
<AsideLeft {...props}/>
|
||||||
<main id='wrapper' className='flex w-full py-8 justify-center'>
|
<main id='wrapper' className='flex w-full py-8 justify-center'>
|
||||||
<div className='2xl:max-w-6xl md:max-w-3xl w-full'>
|
<div className='2xl:max-w-6xl md:max-w-4xl w-full'>
|
||||||
<div> {headerSlot} </div>
|
<div> {headerSlot} </div>
|
||||||
<div>{children}</div>
|
<div>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -14,7 +14,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
|
|
||||||
<div className='lg:p-8 p-4 flex flex-col w-full'>
|
<div className='lg:p-8 p-4 flex flex-col w-full'>
|
||||||
<Link href={`${BLOG.PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.PATH}/article/${post.slug}`} passHref>
|
||||||
<a className={`cursor-pointer hover:underline text-3xl font-sans ${showPreview ? 'justify-center' : 'justify-start'} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}>
|
<a className={`cursor-pointer hover:underline text-2xl font-sans ${showPreview ? 'justify-center' : 'justify-start'} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}>
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -51,7 +51,7 @@ export default function Header () {
|
|||||||
const scrollS = window.scrollY
|
const scrollS = window.scrollY
|
||||||
const nav = document.querySelector('#sticky-nav')
|
const nav = document.querySelector('#sticky-nav')
|
||||||
|
|
||||||
if (scrollS < 300) {
|
if (scrollS < 500) {
|
||||||
nav && nav.classList.replace('bg-white', 'bg-none')
|
nav && nav.classList.replace('bg-white', 'bg-none')
|
||||||
nav && nav.classList.replace('text-black', 'text-white')
|
nav && nav.classList.replace('text-black', 'text-white')
|
||||||
} else {
|
} else {
|
||||||
@@ -59,6 +59,7 @@ export default function Header () {
|
|||||||
nav && nav.classList.replace('text-white', 'text-black')
|
nav && nav.classList.replace('text-white', 'text-black')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 自动滚动
|
||||||
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
|
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
|
||||||
) {
|
) {
|
||||||
autoScroll = true
|
autoScroll = true
|
||||||
@@ -98,7 +99,7 @@ export default function Header () {
|
|||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
id="header"
|
id="header"
|
||||||
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black"
|
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${CONFIG_HEXO.HOME_BANNER_IMAGE}")`
|
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${CONFIG_HEXO.HOME_BANNER_IMAGE}")`
|
||||||
@@ -23,7 +23,7 @@ const MenuButtonGroupTop = (props) => {
|
|||||||
if (link.show) {
|
if (link.show) {
|
||||||
return <Link key={`${link.to}`} title={link.to} href={link.to} >
|
return <Link key={`${link.to}`} title={link.to} href={link.to} >
|
||||||
<a className={'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'} >
|
<a className={'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'} >
|
||||||
<div className='w-full flex dark:text-white text-sm items-center justify-center hover:scale-105 duration-200 transform'>
|
<div className='w-full flex text-sm items-center justify-center hover:scale-105 transform'>
|
||||||
<i className={`${link.icon} mr-1`}/>
|
<i className={`${link.icon} mr-1`}/>
|
||||||
<div className='text-center'>{link.name}</div>
|
<div className='text-center'>{link.name}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -20,7 +20,7 @@ const SearchDrawer = ({ cRef, slot }) => {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div id='search-drawer-wrapper' ref={searchDrawer} className='hidden'>
|
<div id='search-drawer-wrapper' ref={searchDrawer} className='hidden'>
|
||||||
<div className='flex-col fixed px-5 w-full left-0 top-14 z-50 justify-center'>
|
<div className='flex-col fixed px-5 w-full left-0 top-14 z-40 justify-center'>
|
||||||
<div className='md:max-w-3xl w-full mx-auto animate__animated animate__faster animate__fadeIn'>
|
<div className='md:max-w-3xl w-full mx-auto animate__animated animate__faster animate__fadeIn'>
|
||||||
<SearchInput cRef={searchInputRef} />
|
<SearchInput cRef={searchInputRef} />
|
||||||
{slot}
|
{slot}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user