mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Merge branch 'main' into develop
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=3.6.4
|
||||
NEXT_PUBLIC_VERSION=3.6.7
|
||||
75
README.md
75
README.md
@@ -1,7 +1,5 @@
|
||||
# NotionNext
|
||||
|
||||
一个使用 NextJS + Notion API 实现的,部署在 Vercel 上的静态博客系统。为Notion和所有创作者设计。
|
||||
|
||||
<p>
|
||||
<a aria-label="GitHub commit activity" href="https://github.com/tangly1024/NotionNext/commits/main" title="GitHub commit activity">
|
||||
<img src="https://img.shields.io/github/commit-activity/m/tangly1024/NotionNext?style=for-the-badge"/>
|
||||
@@ -17,74 +15,20 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
演示地址:[https://preview.tangly1024.com/](https://preview.tangly1024.com/)
|
||||
一个使用 NextJS + Notion API 实现的,部署在 Vercel 上的静态博客系统。为Notion和所有创作者设计。
|
||||
|
||||
## 继承自Nobelium的亮点 ✨
|
||||
|
||||
**🚀 秒开,设备全适配**
|
||||
## 预览效果
|
||||
|
||||
- 快速的页面渲染和响应式设计
|
||||
- 高效编译器的快速静态页面生成
|
||||
|
||||
**🤖 自动,无需重新部署**
|
||||
|
||||
- 部署在免费、高速的 Vercel 平台
|
||||
- 支持增量式更新,更新文章后无需重复部署
|
||||
|
||||
**🚙 全功能,完全不操心**
|
||||
|
||||
- 评论、搜索、标签、分类
|
||||
- 订阅、网站统计
|
||||
- 本地化多语言
|
||||
- 服务端渲染、优秀的SEO
|
||||
|
||||
**🎨 美观,轻松自定义**
|
||||
- 丰富的配置项,更支持多语言
|
||||
- 使用 Tailwind CSS,轻松实现二次开发
|
||||
|
||||
## 特色
|
||||
- 支持更多的页面,功能,更多特性、欢迎移步[我的博客](https://tangly1024.com/article/notion-next)查看
|
||||
- 支持多主题切换
|
||||
在线演示:[https://preview.tangly1024.com/](https://preview.tangly1024.com/) ,项目支持多主题切换,没找到喜欢的主题?[贡献](/CONTRIBUTING.md)一个吧~
|
||||
|
||||
| Next | Medium | Hexo | Fukasawa |
|
||||
|--|--|--|--|
|
||||
| <img src='./docs/theme-next.png' width='300'/> [预览NEXT](https://preview.tangly1024.com/?theme=next) | <img src='./docs/theme-medium.png' width='300'/> [预览MEDIUM](https://preview.tangly1024.com/?theme=medium) | <img src='./docs/theme-hexo.png' width='300'/> [预览HEXO](https://preview.tangly1024.com/?theme=hexo) | <img src='./docs/theme-fukasawa.png' width='300'/> [预览FUKASAWA](https://preview.tangly1024.com/?theme=fukasawa) |
|
||||
|
||||
*只需修改`blog.config.js`文件的`THEME`即可实现主题切换。* 没找到喜欢的主题?[贡献](/CONTRIBUTING.md)一个吧~
|
||||
## 我要如何开始?
|
||||
|
||||
|
||||
|
||||
## 快速起步
|
||||
|
||||
- 给这个项目点个小星星 😉
|
||||
- 将 [这个 Notion 模板](https://tanghh.notion.site/02ab3b8678004aa69e9e415905ef32a5) 制作副本,并分享这个页面给所有人
|
||||
- [Fork](https://github.com/tangly1024/NotionNext/fork) 这个项目
|
||||
- _(可选)_ 用自己的图片替换 `/public` 文件夹里的 `avatar.jpg`、`favicon.svg` 和 `favicon.ico`
|
||||
- 在 `blog.config.js` 配置相关选项,`NOTION_PAGE_ID`: 你刚刚分享出去的 Notion 页面网址中的页面 ID,通常是网址中工作区地址后的 32 位字符串
|
||||
- 在 [Vercel](https://vercel.com)中部署项目
|
||||
- **稍微等等就可以访问了!** 简单吗?
|
||||
|
||||
更多项目特性及配置的说明,请移步 [NotionNext文档](https://docs.tangly1024.com/zh) 查看,文档正在完善中,欢迎 [参与编辑](https://github.com/tangly1024/nextjs-docs-notion-next)
|
||||
|
||||
## 快速开发
|
||||
- 需要安装Nodejs环境
|
||||
```bash
|
||||
yarn # 安装依赖
|
||||
yarn run dev # 本地开发
|
||||
yarn run build # 本地打包编译
|
||||
yarn run start # 本地启动NextJS服务
|
||||
```
|
||||
|
||||
## 引用技术
|
||||
|
||||
- **框架**: [Next.js](https://nextjs.org)
|
||||
- **样式**: [Tailwind CSS](https://www.tailwindcss.cn/) 和 `@tailwindcss/jit` compiler
|
||||
- **渲染**: [React-notion-x](https://github.com/NotionX/react-notion-x)
|
||||
- **评论**: [Giscus](https://giscus.app/zh-CN), [Gitalk](https://gitalk.github.io), [Cusdis](https://cusdis.com), [Utterances](https://utteranc.es)
|
||||
- **图标**:[fontawesome v5.15](https://fontawesome.com/v5.15/icons?d=gallery)
|
||||
|
||||
## 更新日志
|
||||
请移步 [更新文档](https://docs.tangly1024.com/zh/changelog)查看
|
||||
只需几分钟即可搭建您的个人站点,欢迎移步[我的博客](https://tangly1024.com/article/notion-next) 查看教程
|
||||
|
||||
|
||||
## 致谢
|
||||
@@ -120,12 +64,17 @@ yarn run start # 本地启动NextJS服务
|
||||
<td align="center"><a href="https://github.com/SwwweetOrange"><img src="https://avatars.githubusercontent.com/u/71168966" width="64px;" alt="SwwweetOrange"/><br/><sub><b>SwwweetOrange</b></sub></a><br/><a href="https://github.com/tangly1024/NotionNext/commits?author=SwwweetOrange" title="SwwweetOrange" >🔧 🐛</a></td>
|
||||
|
||||
<td align="center"><a href="https://github.com/Ylarod"><img src="https://avatars.githubusercontent.com/u/30978685" width="64px;" alt="Ylarod"/><br/><sub><b>Ylarod</b></sub></a><br/><a href="https://github.com/tangly1024/NotionNext/commits?author=Ylarod" title="Ylarod" >🔧 🐛</a></td>
|
||||
v
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
十分期待你的[贡献](/CONTRIBUTING.md),一起来完善这个项目~
|
||||
## 引用技术
|
||||
|
||||
- **框架**: [Next.js](https://nextjs.org)
|
||||
- **样式**: [Tailwind CSS](https://www.tailwindcss.cn/) 和 `@tailwindcss/jit` compiler
|
||||
- **渲染**: [React-notion-x](https://github.com/NotionX/react-notion-x)
|
||||
- **评论**: [Giscus](https://giscus.app/zh-CN), [Gitalk](https://gitalk.github.io), [Cusdis](https://cusdis.com), [Utterances](https://utteranc.es)
|
||||
- **图标**: [Fontawesome](https://fontawesome.com/v6/icons/)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
@@ -43,6 +43,12 @@ const CommonHead = ({ meta, children }) => {
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:title" content={title} />
|
||||
|
||||
{ BLOG.CUSTOM_FONT
|
||||
? BLOG.CUSTOM_FONT_URL?.map(fontUrl =>
|
||||
<link href={`${fontUrl}`} key={fontUrl} rel="stylesheet"/>)
|
||||
: <link href='https://fonts.font.im/css2?family=Noto+Serif+SC&display=optional' rel="stylesheet"/> }
|
||||
|
||||
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <meta name="referrer" content="no-referrer-when-downgrade" />}
|
||||
{meta?.type === 'Post' && (
|
||||
<>
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'prismjs/plugins/line-numbers/prism-line-numbers.css'
|
||||
// 所有语言的prismjs 使用autoloader引入
|
||||
import 'prismjs/plugins/autoloader/prism-autoloader'
|
||||
// mermaid图
|
||||
import mermaid from 'mermaid'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
/**
|
||||
@@ -18,6 +17,7 @@ import BLOG from '@/blog.config'
|
||||
const PrismMac = () => {
|
||||
React.useEffect(() => {
|
||||
renderPrismMac()
|
||||
renderMermaid()
|
||||
|
||||
// 折叠代码行号bug
|
||||
const observer = new MutationObserver(mutationsList => {
|
||||
@@ -35,21 +35,10 @@ const PrismMac = () => {
|
||||
return <></>
|
||||
}
|
||||
|
||||
function renderPrismMac() {
|
||||
const container = document?.getElementById('container-inner')
|
||||
const codeToolBars = container?.getElementsByClassName('code-toolbar')
|
||||
|
||||
// Add line numbers
|
||||
const codeBlocks = container?.getElementsByTagName('pre')
|
||||
if (codeBlocks) {
|
||||
Array.from(codeBlocks).forEach(item => {
|
||||
if (!item.classList.contains('line-numbers')) {
|
||||
item.classList.add('line-numbers')
|
||||
item.style.whiteSpace = 'pre-wrap'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将mermaid语言 渲染成图片
|
||||
*/
|
||||
const renderMermaid = async() => {
|
||||
// 支持 Mermaid
|
||||
const mermaidPres = document.querySelectorAll('pre.notion-code.language-mermaid')
|
||||
if (mermaidPres) {
|
||||
@@ -73,9 +62,26 @@ function renderPrismMac() {
|
||||
}
|
||||
}
|
||||
if (needLoad) {
|
||||
mermaid.contentLoaded()
|
||||
const asyncMermaid = await import('mermaid')
|
||||
asyncMermaid.default.contentLoaded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderPrismMac() {
|
||||
const container = document?.getElementById('container-inner')
|
||||
const codeToolBars = container?.getElementsByClassName('code-toolbar')
|
||||
|
||||
// Add line numbers
|
||||
const codeBlocks = container?.getElementsByTagName('pre')
|
||||
if (codeBlocks) {
|
||||
Array.from(codeBlocks).forEach(item => {
|
||||
if (!item.classList.contains('line-numbers')) {
|
||||
item.classList.add('line-numbers')
|
||||
item.style.whiteSpace = 'pre-wrap'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重新渲染之前检查所有的多余text
|
||||
|
||||
|
||||
@@ -94,11 +94,6 @@ bszTag = {
|
||||
}
|
||||
}
|
||||
|
||||
// 修复Node同构代码的问题
|
||||
if (typeof document !== 'undefined') {
|
||||
fetch()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetch
|
||||
}
|
||||
|
||||
@@ -50,4 +50,4 @@ const fontFamilies = {
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = fontFamilies
|
||||
module.exports = { fontFamilies }
|
||||
|
||||
@@ -14,7 +14,7 @@ import { isIterable } from '../utils'
|
||||
* @returns {Promise<{}|*[]>}
|
||||
*/
|
||||
export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
||||
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
if (!allPosts || !categoryOptions) {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isIterable } from '../utils'
|
||||
* @returns {Promise<{}|*[]>}
|
||||
*/
|
||||
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
|
||||
if (!allPosts || !tagOptions) {
|
||||
return []
|
||||
|
||||
@@ -212,17 +212,16 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) {
|
||||
collectionData.push(properties)
|
||||
}
|
||||
}
|
||||
// 读取映射 配置
|
||||
let postCount = 0
|
||||
|
||||
// 获取page作为自定义菜单
|
||||
const customNav = getCustomNav({ allPages: collectionData.filter(post => post.type === 'Page' && post.status === 'Published') })
|
||||
|
||||
// 文章计数
|
||||
let postCount = 0
|
||||
const allPages = collectionData.filter(post => {
|
||||
if (post.type === 'Post' && post.status === 'Published') {
|
||||
postCount++
|
||||
}
|
||||
|
||||
return post &&
|
||||
post.type &&
|
||||
(post.type === 'Post' || post.type === 'Page') &&
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NotionAPI } from 'notion-client'
|
||||
import BLOG from '@/blog.config'
|
||||
import formatDate from '../formatDate'
|
||||
import { defaultMapImageUrl } from 'react-notion-x'
|
||||
import md5 from 'js-md5'
|
||||
|
||||
export default async function getPageProperties(id, block, schema, authToken, tagOptions, siteInfo) {
|
||||
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
||||
@@ -89,6 +90,7 @@ export default async function getPageProperties(id, block, schema, authToken, ta
|
||||
properties.pageIcon = getImageUrl(block[id].value?.format?.page_icon, block[id].value) ?? ''
|
||||
properties.page_cover = getImageUrl(block[id].value?.format?.page_cover, block[id].value) ?? siteInfo?.pageCover
|
||||
properties.content = value.content ?? []
|
||||
properties.password = properties.password ? md5(properties.slug + properties.password) : ''
|
||||
properties.tagItems = properties?.tags?.map(tag => {
|
||||
return { name: tag, color: tagOptions?.find(t => t.value === tag)?.color || 'gray' }
|
||||
}) || []
|
||||
|
||||
90
lib/notion/getPageTableOfContents.js
Normal file
90
lib/notion/getPageTableOfContents.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { getTextContent } from 'notion-utils'
|
||||
|
||||
const indentLevels = {
|
||||
header: 0,
|
||||
sub_header: 1,
|
||||
sub_sub_header: 2
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/NotionX/react-notion-x/blob/master/packages/notion-utils/src/get-page-table-of-contents.ts
|
||||
* Gets the metadata for a table of contents block by parsing the page's
|
||||
* H1, H2, and H3 elements.
|
||||
*/
|
||||
export const getPageTableOfContents = (page, recordMap) => {
|
||||
const contents = (page.content ?? [])
|
||||
const toc = getBlockHeader(contents, recordMap)
|
||||
const indentLevelStack = [
|
||||
{
|
||||
actual: -1,
|
||||
effective: -1
|
||||
}
|
||||
]
|
||||
|
||||
// Adjust indent levels to always change smoothly.
|
||||
// This is a little tricky, but the key is that when increasing indent levels,
|
||||
// they should never jump more than one at a time.
|
||||
for (const tocItem of toc) {
|
||||
const { indentLevel } = tocItem
|
||||
const actual = indentLevel
|
||||
|
||||
do {
|
||||
const prevIndent = indentLevelStack[indentLevelStack.length - 1]
|
||||
const { actual: prevActual, effective: prevEffective } = prevIndent
|
||||
|
||||
if (actual > prevActual) {
|
||||
tocItem.indentLevel = prevEffective + 1
|
||||
indentLevelStack.push({
|
||||
actual,
|
||||
effective: tocItem.indentLevel
|
||||
})
|
||||
} else if (actual === prevActual) {
|
||||
tocItem.indentLevel = prevEffective
|
||||
break
|
||||
} else {
|
||||
indentLevelStack.pop()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
} while (true)
|
||||
}
|
||||
|
||||
return toc
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写获取目录方法
|
||||
*/
|
||||
function getBlockHeader(contents, recordMap, toc) {
|
||||
if (!toc) {
|
||||
toc = []
|
||||
}
|
||||
if (!contents) {
|
||||
return toc
|
||||
}
|
||||
|
||||
for (const blockId of contents) {
|
||||
const block = recordMap.block[blockId]?.value
|
||||
if (!block) {
|
||||
continue
|
||||
}
|
||||
const { type } = block
|
||||
if (type.indexOf('header') >= 0) {
|
||||
const existed = toc.find(e => e.id === blockId)
|
||||
if (!existed) {
|
||||
toc.push({
|
||||
id: blockId,
|
||||
type,
|
||||
text: getTextContent(block.properties?.title),
|
||||
indentLevel: indentLevels[type]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (block.content?.length > 0) {
|
||||
getBlockHeader(block.content, recordMap, toc)
|
||||
}
|
||||
}
|
||||
|
||||
return toc
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "3.6.4",
|
||||
"version": "3.6.7",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -30,6 +30,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"feed": "^4.2.2",
|
||||
"gitalk": "^1.7.2",
|
||||
"js-md5": "^0.7.3",
|
||||
"localStorage": "^1.0.4",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"mark.js": "^8.11.1",
|
||||
|
||||
@@ -8,6 +8,8 @@ import { idToUuid } from 'notion-utils'
|
||||
import Router from 'next/router'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { getNotion } from '@/lib/notion/getNotion'
|
||||
import md5 from 'js-md5'
|
||||
import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents'
|
||||
|
||||
/**
|
||||
* 根据notion的slug访问页面
|
||||
@@ -28,6 +30,11 @@ const Slug = props => {
|
||||
if (post?.password && post?.password !== '') {
|
||||
setLock(true)
|
||||
} else {
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
setLock(false)
|
||||
}
|
||||
}, [post])
|
||||
@@ -51,10 +58,12 @@ const Slug = props => {
|
||||
* 验证文章密码
|
||||
* @param {*} result
|
||||
*/
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
const validPassword = passInput => {
|
||||
if (passInput && md5(post.slug + passInput) === post.password) {
|
||||
setLock(false)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
props = { ...props, lock, setLock, validPassword }
|
||||
|
||||
@@ -23,6 +23,8 @@ import { GlobalContextProvider } from '@/lib/global'
|
||||
import { DebugPanel } from '@/components/DebugPanel'
|
||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||
import { Fireworks } from '@/components/Fireworks'
|
||||
import React from 'react'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
|
||||
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
||||
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
|
||||
@@ -47,10 +49,13 @@ const MyApp = ({ Component, pageProps }) => {
|
||||
{JSON.parse(BLOG.FIREWORKS) && <Fireworks/>}
|
||||
</>
|
||||
|
||||
// 延迟加载fontAwesome
|
||||
React.useEffect(() => {
|
||||
loadExternalResource(BLOG.FONT_AWESOME_PATH, 'css')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<GlobalContextProvider>
|
||||
{/* FontawesomeCDN */}
|
||||
<link rel="stylesheet" href={BLOG.FONT_AWESOME_PATH} referrerPolicy="no-referrer" />
|
||||
{externalPlugins}
|
||||
<Component {...pageProps} />
|
||||
</GlobalContextProvider>
|
||||
|
||||
@@ -15,10 +15,6 @@ class MyDocument extends Document {
|
||||
<Head>
|
||||
<link rel='icon' href='/favicon.ico' />
|
||||
<link rel='icon' href='/favicon.svg' type='image/svg+xml' />
|
||||
{ BLOG.CUSTOM_FONT
|
||||
? BLOG.CUSTOM_FONT_URL?.map(fontUrl =>
|
||||
<link href={`${fontUrl}`} key={fontUrl} rel='stylesheet' />)
|
||||
: <link href='https://fonts.font.im/css2?family=Noto+Serif+SC&display=swap' rel='stylesheet' /> }
|
||||
<CommonScript />
|
||||
</Head>
|
||||
|
||||
|
||||
@@ -132,11 +132,6 @@ nav {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.dark .glassmorphism {
|
||||
background: rgba(31, 41, 55, 0.75);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.medium-zoom-overlay {
|
||||
background: none !important;
|
||||
|
||||
@@ -392,7 +392,7 @@
|
||||
|
||||
.notion-h {
|
||||
position: relative;
|
||||
display: block;
|
||||
/* display: block; */
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
padding: 3px 2px;
|
||||
@@ -427,7 +427,7 @@
|
||||
margin-top: 2px;
|
||||
} */
|
||||
.notion-h2 {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.4em;
|
||||
margin-top: 1.1em;
|
||||
}
|
||||
.notion-h3 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const BLOG = require('./blog.config')
|
||||
const fontFamilies = require('./lib/font')
|
||||
const { fontFamilies } = require('./lib/font')
|
||||
|
||||
module.exports = {
|
||||
purge: ['./pages/**/*.js', './components/**/*.js', './layouts/**/*.js', './themes/**/*.js'],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
@@ -12,15 +11,10 @@ export const LayoutSlug = props => {
|
||||
return <LayoutBase {...props} />
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
return (
|
||||
<LayoutBase {...props}>
|
||||
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id="notion-article" className="px-2">
|
||||
|
||||
|
||||
@@ -8,14 +8,12 @@ import { useGlobal } from '@/lib/global'
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
if (!validPassword(p?.value)) {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { RecentComments } from '@waline/client'
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { RecentComments } from '@waline/client'
|
||||
|
||||
/**
|
||||
* @see https://waline.js.org/guide/get-started.html
|
||||
|
||||
@@ -2,7 +2,8 @@ import BLOG from '@/blog.config'
|
||||
import Live2D from '@/components/Live2D'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import ExampleRecentComments from './ExampleRecentComments'
|
||||
import dynamic from 'next/dynamic'
|
||||
const ExampleRecentComments = dynamic(() => import('./ExampleRecentComments'))
|
||||
|
||||
export const SideBar = (props) => {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
|
||||
export const LayoutSlug = (props) => {
|
||||
const { post, lock, validPassword } = props
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const { lock, validPassword } = props
|
||||
return (
|
||||
<LayoutBase {...props} >
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import { useGlobal } from '@/lib/global'
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
if (!validPassword(p?.value)) {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
|
||||
@@ -39,7 +39,7 @@ export const LayoutArchive = (props) => {
|
||||
}, [])
|
||||
return <LayoutBase {...props} >
|
||||
<Card className='w-full'>
|
||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 min-h-full">
|
||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 min-h-full dark:bg-hexo-black-gray">
|
||||
{Object.keys(archivePosts).map(archiveTitle => (
|
||||
<BlogPostArchive
|
||||
key={archiveTitle}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import { useRef } from 'react'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import HeaderArticle from './components/HeaderArticle'
|
||||
@@ -15,6 +14,7 @@ import { isBrowser } from '@/lib/utils'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
const drawerRight = useRef(null)
|
||||
|
||||
if (!post) {
|
||||
return <LayoutBase
|
||||
@@ -25,12 +25,6 @@ export const LayoutSlug = props => {
|
||||
></LayoutBase>
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const drawerRight = useRef(null)
|
||||
const targetRef = isBrowser() ? document.getElementById('container') : null
|
||||
|
||||
const floatSlot = <>
|
||||
@@ -53,7 +47,7 @@ export const LayoutSlug = props => {
|
||||
floatSlot={floatSlot}
|
||||
>
|
||||
<div className="w-full lg:shadow-sm lg:hover:shadow lg:border lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id="container" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@ import { useGlobal } from '@/lib/global'
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
if (!validPassword(p?.value)) {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
|
||||
@@ -4,12 +4,13 @@ import formatDate from '@/lib/formatDate'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
export default function HeaderArticle({ post, siteInfo }) {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
if (!post) {
|
||||
return <></>
|
||||
}
|
||||
const headerImage = post?.page_cover ? `url("${post.page_cover}")` : `url("${siteInfo?.pageCover}")`
|
||||
|
||||
const { locale } = useGlobal()
|
||||
const date = formatDate(
|
||||
post?.date?.start_date || post?.createdTime,
|
||||
locale.LOCALE
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import { RecentComments } from '@waline/client'
|
||||
import BLOG from '@/blog.config'
|
||||
import Card from '@/themes/hexo/components/Card'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { RecentComments } from '@waline/client'
|
||||
|
||||
/**
|
||||
* @see https://waline.js.org/guide/get-started.html
|
||||
|
||||
@@ -14,7 +14,7 @@ export function InfoCard (props) {
|
||||
}}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120}/>
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120} alt={BLOG.AUTHOR}/>
|
||||
</div>
|
||||
<div className='text-center text-xl pb-4'>{BLOG.AUTHOR}</div>
|
||||
<div className='text-sm text-center'>{BLOG.BIO}</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ const NavButtonGroup = (props) => {
|
||||
return <nav id='home-nav-button' className={'md:h-52 md:mt-6 xl:mt-32 px-5 py-2 mt-8 flex flex-wrap md:max-w-5xl space-y-2 md:space-y-0 md:flex justify-center max-h-80 overflow-auto'}>
|
||||
{categories.map(category => {
|
||||
return <Link key={`${category.name}`} title={`${category.name}`} href={`/category/${category.name}`} passHref>
|
||||
<a className='text-center w-full md:mx-6 md:w-40 md:h-14 lg:h-20 h-14 justify-center items-center flex border-2 cursor-pointer rounded-lg glassmorphism hover:bg-white hover:text-black duration-200 font-bold hover:scale-110 transform'>{category.name}</a>
|
||||
<a className='text-center w-full md:mx-6 md:w-40 md:h-14 lg:h-20 h-14 justify-center items-center flex border-2 cursor-pointer rounded-lg glassmorphism hover:bg-white hover:text-black duration-200 font-bold hover:scale-110 transform'>{category.name}</a>
|
||||
</Link>
|
||||
})}
|
||||
</nav>
|
||||
|
||||
@@ -6,9 +6,10 @@ import Catalog from './Catalog'
|
||||
import { InfoCard } from './InfoCard'
|
||||
import { AnalyticsCard } from './AnalyticsCard'
|
||||
import CONFIG_HEXO from '../config_hexo'
|
||||
import HexoRecentComments from './HexoRecentComments'
|
||||
import BLOG from '@/blog.config'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const HexoRecentComments = dynamic(() => import('./HexoRecentComments'))
|
||||
/**
|
||||
* Hexo主题右侧栏
|
||||
* @param {*} props
|
||||
|
||||
@@ -5,7 +5,7 @@ import BLOG from '@/blog.config'
|
||||
|
||||
export const LayoutCategory = props => {
|
||||
const { category } = props
|
||||
const slotTop = <div className='flex items-center font-sans p-8'><div className='text-xl'><i className='mr-2 fas fa-th' />分类:</div>{category}</div>
|
||||
const slotTop = <div className='flex items-center font-sans py-8'><div className='text-xl'><i className='mr-2 fas fa-th' />分类:</div>{category}</div>
|
||||
|
||||
return <LayoutBase {...props} slotTop={slotTop}>
|
||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import React from 'react'
|
||||
@@ -16,11 +14,6 @@ export const LayoutSlug = props => {
|
||||
/>
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const slotRight = post?.toc && post?.toc?.length > 3 && (
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
||||
<Catalog toc={post.toc} />
|
||||
@@ -29,7 +22,7 @@ export const LayoutSlug = props => {
|
||||
|
||||
return (
|
||||
<LayoutBase showInfoCard={true} slotRight={slotRight} {...props} >
|
||||
{!lock ? <ArticleDetail {...props} /> : <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
{!lock ? <ArticleDetail {...props} /> : <ArticleLock validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import BlogPostListPage from './components/BlogPostListPage'
|
||||
|
||||
export const LayoutTag = (props) => {
|
||||
const { tag } = props
|
||||
const slotTop = <div className='flex items-center font-sans p-8'><div className='text-xl'><i className='mr-2 fas fa-tag'/>标签:</div>{tag}</div>
|
||||
const slotTop = <div className='flex items-center font-sans py-8'><div className='text-xl'><i className='mr-2 fas fa-tag'/>标签:</div>{tag}</div>
|
||||
|
||||
return <LayoutBase {...props} slotTop={slotTop}>
|
||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const ArticleDetail = props => {
|
||||
<Link href="/about" passHref>
|
||||
<div className='flex pt-2'>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={siteInfo?.icon} className='rounded-full cursor-pointer' width={22} />
|
||||
<img src={siteInfo?.icon} className='rounded-full cursor-pointer' width={22} alt={BLOG.AUTHOR} />
|
||||
|
||||
<div className="mr-3 ml-2 my-auto text-green-500 cursor-pointer">
|
||||
{BLOG.AUTHOR}
|
||||
|
||||
@@ -8,14 +8,12 @@ import { useGlobal } from '@/lib/global'
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
if (!validPassword(p?.value)) {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
|
||||
@@ -9,7 +9,7 @@ const InfoCard = (props) => {
|
||||
<div className='items-center justify-center font-sans '>
|
||||
<div className='hover:scale-105 transform duration-200 cursor-pointer flex justify-center' onClick={ () => { Router.push('/about') }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120}/>
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120} alt={BLOG.AUTHOR}/>
|
||||
</div>
|
||||
<div className='text-xl py-2 hover:scale-105 transform duration-200 flex justify-center dark:text-gray-300'>{BLOG.AUTHOR}</div>
|
||||
<div className='font-light text-gray-600 mb-2 hover:scale-105 transform duration-200 flex justify-center dark:text-gray-400'>{BLOG.BIO}</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const LayoutArchive = (props) => {
|
||||
|
||||
return (
|
||||
<LayoutBase {...props}>
|
||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 shadow-md min-h-full">
|
||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-hexo-black-gray shadow-md min-h-full">
|
||||
{Object.keys(archivePosts).map(archiveTitle => (
|
||||
<BlogPostArchive
|
||||
key={archiveTitle}
|
||||
|
||||
@@ -6,7 +6,7 @@ export const LayoutCategoryIndex = (props) => {
|
||||
const { allPosts, categories } = props
|
||||
const { locale } = useGlobal()
|
||||
return <LayoutBase totalPosts={allPosts} {...props}>
|
||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow h-full'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray px-10 py-10 shadow h-full'>
|
||||
<div className='dark:text-gray-200 mb-5'>
|
||||
<i className='mr-4 fas faTh' />{locale.COMMON.CATEGORY}:
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import TocDrawerButton from './components/TocDrawerButton'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import Card from './components/Card'
|
||||
@@ -12,11 +11,6 @@ import { isBrowser } from '@/lib/utils'
|
||||
|
||||
export const LayoutSlug = (props) => {
|
||||
const { post, latestPosts, lock, validPassword } = props
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const drawerRight = useRef(null)
|
||||
const targetRef = isBrowser() ? document.getElementById('container') : null
|
||||
const floatSlot = post?.toc?.length > 1
|
||||
@@ -45,7 +39,7 @@ export const LayoutSlug = (props) => {
|
||||
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{/* 悬浮目录按钮 */}
|
||||
<div className='block lg:hidden'>
|
||||
|
||||
@@ -6,7 +6,7 @@ export const LayoutTagIndex = (props) => {
|
||||
const { tags } = props
|
||||
const { locale } = useGlobal()
|
||||
return <LayoutBase {...props}>
|
||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow h-full'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray px-10 py-10 shadow h-full'>
|
||||
<div className='dark:text-gray-200 mb-5'><i className='fas fa-tags mr-4'/>{locale.COMMON.TAGS}:</div>
|
||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||
{ tags.map(tag => {
|
||||
|
||||
@@ -8,14 +8,12 @@ import { useGlobal } from '@/lib/global'
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
if (!validPassword(p?.value)) {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
|
||||
@@ -86,7 +86,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
|
||||
<div className="text-right border-t pt-8 border-dashed">
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`}>
|
||||
<a className="hover:bg-opacity-100 hover:underline transform duration-300 p-3 text-white bg-gray-800 dark:bg-black cursor-pointer">
|
||||
<a className="hover:bg-opacity-100 hover:underline transform duration-300 p-3 text-white bg-gray-800 cursor-pointer">
|
||||
{locale.COMMON.ARTICLE_DETAIL}
|
||||
<i className="ml-1 fas fa-angle-right" />
|
||||
</a>
|
||||
|
||||
@@ -14,7 +14,7 @@ const Footer = ({ title }) => {
|
||||
|
||||
return (
|
||||
<footer
|
||||
className='dark:bg-gray-900 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 dark:text-gray-400'
|
||||
className='dark:bg-gray-800 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 bg-white dark:text-gray-400'
|
||||
>
|
||||
<DarkModeButton/>
|
||||
<span>
|
||||
|
||||
@@ -9,7 +9,7 @@ const InfoCard = (props) => {
|
||||
<div className='flex flex-col items-center justify-center '>
|
||||
<div className='hover:rotate-45 hover:scale-125 transform duration-200 cursor-pointer' onClick={ () => { Router.push('/') }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120}/>
|
||||
<img src={siteInfo?.icon} className='rounded-full' width={120} alt={BLOG.AUTHOR}/>
|
||||
</div>
|
||||
<div className='text-2xl font-serif dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.AUTHOR}</div>
|
||||
<div className='font-light dark:text-white py-2 hover:scale-105 transform duration-200 text-center'>{BLOG.BIO}</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react'
|
||||
const Logo = props => {
|
||||
const { siteInfo, className } = props
|
||||
return <Link href='/' passHref>
|
||||
<div className={'flex flex-col justify-center items-center cursor-pointer bg-black space-y-3 font-bold ' + className}>
|
||||
<div className={'flex flex-col justify-center items-center cursor-pointer bg-black dark:bg-gray-800 space-y-3 font-bold ' + className}>
|
||||
<div className='font-serif text-xl text-white'> {siteInfo?.title}</div>
|
||||
<div className='text-sm text-gray-300 font-light text-center'> {siteInfo?.description}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { RecentComments } from '@waline/client'
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { RecentComments } from '@waline/client'
|
||||
|
||||
/**
|
||||
* @see https://waline.js.org/guide/get-started.html
|
||||
|
||||
@@ -16,7 +16,7 @@ const PaginationNumber = ({ page, totalPage }) => {
|
||||
const pages = generatePages(pagePrefix, page, currentPage, totalPage)
|
||||
|
||||
return (
|
||||
<div className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-gray-700 dark:text-gray-300 py-3 shadow space-x-2">
|
||||
<div className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-hexo-black-gray dark:text-gray-300 py-3 shadow space-x-2">
|
||||
{/* 上一页 */}
|
||||
<Link
|
||||
href={{
|
||||
|
||||
@@ -66,7 +66,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`}
|
||||
className={'w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
|
||||
className={'w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-800 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
|
||||
@@ -6,8 +6,9 @@ import CategoryGroup from './CategoryGroup'
|
||||
import TagGroups from './TagGroups'
|
||||
import CONFIG_NEXT from '../config_next'
|
||||
import { useRouter } from 'next/router'
|
||||
import NextRecentComments from './NextRecentComments'
|
||||
import BLOG from '@/blog.config'
|
||||
import dynamic from 'next/dynamic'
|
||||
const NextRecentComments = dynamic(() => import('./NextRecentComments'))
|
||||
|
||||
/**
|
||||
* 侧边平铺
|
||||
|
||||
@@ -11,7 +11,7 @@ let windowTop = 0
|
||||
* @constructor
|
||||
*/
|
||||
const StickyBar = ({ children }) => {
|
||||
if (!children) return <></>
|
||||
// 滚动页面时导航条样式调整
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
if (CONFIG_NEXT.NAV_TYPE === 'normal') {
|
||||
return
|
||||
@@ -37,9 +37,11 @@ const StickyBar = ({ children }) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!children) return <></>
|
||||
|
||||
return (
|
||||
<div id='sticky-bar' className='sticky flex-grow justify-center top-0 duration-500 z-10 pb-16'>
|
||||
<div className='bg-white dark:bg-black px-5 absolute shadow-md w-full scroll-hidden'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray px-5 absolute shadow-md w-full scroll-hidden'>
|
||||
<div id='tag-container' className="md:pl-3 overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,6 @@ import Progress from './Progress'
|
||||
* @constructor
|
||||
*/
|
||||
const Toc = ({ toc }) => {
|
||||
// 无目录就直接返回空
|
||||
if (!toc || toc.length < 1) {
|
||||
return <></>
|
||||
}
|
||||
// 监听滚动事件
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('scroll', actionSectionScrollSpy)
|
||||
@@ -58,6 +54,11 @@ const Toc = ({ toc }) => {
|
||||
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
|
||||
}, throttleMs))
|
||||
|
||||
// 无目录就直接返回空
|
||||
if (!toc || toc.length < 1) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <div className='px-3'>
|
||||
<div className='w-full pb-1'>
|
||||
<Progress />
|
||||
|
||||
@@ -89,7 +89,7 @@ const TopNav = (props) => {
|
||||
|
||||
{/* 导航栏 */}
|
||||
<div id='sticky-nav' className={`${CONFIG_NEXT.NAV_TYPE !== 'normal' ? 'fixed' : ''} lg:relative w-full top-0 z-20 transform duration-500`}>
|
||||
<div className='w-full flex justify-between items-center p-4 bg-black text-white'>
|
||||
<div className='w-full flex justify-between items-center p-4 bg-black dark:bg-gray-800 text-white'>
|
||||
{/* 左侧LOGO 标题 */}
|
||||
<div className='flex flex-none flex-grow-0'>
|
||||
<div onClick={toggleMenuOpen} className='w-8 cursor-pointer'>
|
||||
|
||||
Reference in New Issue
Block a user