mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-09 15:10:39 +00:00
Merge branch 'main' into original-main-fix-DOMException
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||||
NEXT_PUBLIC_VERSION=4.5.3
|
NEXT_PUBLIC_VERSION=4.6.1
|
||||||
|
|
||||||
|
|
||||||
# 可在此添加环境变量,去掉最左边的(# )注释即可
|
# 可在此添加环境变量,去掉最左边的(# )注释即可
|
||||||
|
|||||||
10
README_EN.md
10
README_EN.md
@@ -35,15 +35,15 @@ Live Demo:[https://preview.tangly1024.com/](https://preview.tangly1024.com/)
|
|||||||
|
|
||||||
It only takes a few minutes to set up your personal site:
|
It only takes a few minutes to set up your personal site:
|
||||||
|
|
||||||
- [Quick Deployment Tutorial - Multiple Options Available](https://tangly1024.com/article/notion-next)
|
- [Quick Deployment Tutorial - Multiple Options Available](https://docs.tangly1024.com/article/vercel-deploy-notion-next)
|
||||||
|
|
||||||
- [Customization Guide - How to Configure Feature Plugins](https://tangly1024.com/article/notion-next-guide)
|
- [Customization Guide - How to Configure Feature Plugins](https://docs.tangly1024.com/article/how-to-config-notion-next)
|
||||||
|
|
||||||
- [Development Guide - How to Conduct Local Development](https://tangly1024.com/article/how-to-develop-with-notion-next)
|
- [Development Guide - How to Conduct Local Development](https://docs.tangly1024.com/article/how-to-develop-with-notion-next)
|
||||||
|
|
||||||
- [Update Guide - How to Get the Latest Upgrade Patch](https://tangly1024.com/article/how-to-update-notionnext)
|
- [Update Guide - How to Get the Latest Upgrade Patch](https://docs.tangly1024.com/article/how-to-update-notionnext)
|
||||||
|
|
||||||
- [Version History - Check Feature Highlights for Each Version](https://tangly1024.com/article/notion-next-changelogs)
|
- [Version History - Check Feature Highlights for Each Version](https://docs.tangly1024.com/article/latest)
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const BLOG = {
|
|||||||
APPEARANCE: process.env.NEXT_PUBLIC_APPEARANCE || 'light', // ['light', 'dark', 'auto'], // light 日间模式 , dark夜间模式, auto根据时间和主题自动夜间模式
|
APPEARANCE: process.env.NEXT_PUBLIC_APPEARANCE || 'light', // ['light', 'dark', 'auto'], // light 日间模式 , dark夜间模式, auto根据时间和主题自动夜间模式
|
||||||
APPEARANCE_DARK_TIME: process.env.NEXT_PUBLIC_APPEARANCE_DARK_TIME || [18, 6], // 夜间模式起至时间,false时关闭根据时间自动切换夜间模式
|
APPEARANCE_DARK_TIME: process.env.NEXT_PUBLIC_APPEARANCE_DARK_TIME || [18, 6], // 夜间模式起至时间,false时关闭根据时间自动切换夜间模式
|
||||||
|
|
||||||
|
TAG_SORT_BY_COUNT: true, // 标签是否按照文章数量倒序排列,文章多的标签排在前。
|
||||||
IS_TAG_COLOR_DISTINGUISHED:
|
IS_TAG_COLOR_DISTINGUISHED:
|
||||||
process.env.NEXT_PUBLIC_IS_TAG_COLOR_DISTINGUISHED === 'true' || true, // 对于名称相同的tag是否区分tag的颜色
|
process.env.NEXT_PUBLIC_IS_TAG_COLOR_DISTINGUISHED === 'true' || true, // 对于名称相同的tag是否区分tag的颜色
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ const BLOG = {
|
|||||||
CONTACT_INSTAGRAM: process.env.NEXT_PUBLIC_CONTACT_INSTAGRAM || '', // 您的instagram地址
|
CONTACT_INSTAGRAM: process.env.NEXT_PUBLIC_CONTACT_INSTAGRAM || '', // 您的instagram地址
|
||||||
CONTACT_BILIBILI: process.env.NEXT_PUBLIC_CONTACT_BILIBILI || '', // B站主页
|
CONTACT_BILIBILI: process.env.NEXT_PUBLIC_CONTACT_BILIBILI || '', // B站主页
|
||||||
CONTACT_YOUTUBE: process.env.NEXT_PUBLIC_CONTACT_YOUTUBE || '', // Youtube主页
|
CONTACT_YOUTUBE: process.env.NEXT_PUBLIC_CONTACT_YOUTUBE || '', // Youtube主页
|
||||||
|
CONTACT_XIAOHONGSHU: process.env.NEXT_PUBLIC_CONTACT_XIAOHONGSHU || '', // 小红书主页
|
||||||
|
CONTACT_ZHISHIXINGQIU: process.env.NEXT_PUBLIC_CONTACT_ZHISHIXINGQIU || '', // 知识星球
|
||||||
|
CONTACT_WEHCHAT_PUBLIC: process.env.NEXT_PUBLIC_CONTACT_WEHCHAT_PUBLIC || '', // 微信公众号 格式:https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=【xxxxxx】==#wechat_redirect
|
||||||
|
|
||||||
NOTION_HOST: process.env.NEXT_PUBLIC_NOTION_HOST || 'https://www.notion.so', // Notion域名,您可以选择用自己的域名进行反向代理,如果不懂得什么是反向代理,请勿修改此项
|
NOTION_HOST: process.env.NEXT_PUBLIC_NOTION_HOST || 'https://www.notion.so', // Notion域名,您可以选择用自己的域名进行反向代理,如果不懂得什么是反向代理,请勿修改此项
|
||||||
|
|
||||||
@@ -204,9 +208,10 @@ const BLOG = {
|
|||||||
'link,wechat,qq,weibo,email,facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin', // 分享的服務,按顺序显示,逗号隔开
|
'link,wechat,qq,weibo,email,facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin', // 分享的服務,按顺序显示,逗号隔开
|
||||||
// 所有支持的分享服务:link(复制链接),wechat(微信),qq,weibo(微博),email(邮件),facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin,vkshare,okshare,tumblr,livejournal,mailru,viber,workplace,pocket,instapaper,hatena
|
// 所有支持的分享服务:link(复制链接),wechat(微信),qq,weibo(微博),email(邮件),facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin,vkshare,okshare,tumblr,livejournal,mailru,viber,workplace,pocket,instapaper,hatena
|
||||||
|
|
||||||
POST_URL_PREFIX: process.env.NEXT_PUBLIC_POST_URL_PREFIX || 'article',
|
// 文章URL前缀
|
||||||
|
POST_URL_PREFIX: process.env.NEXT_PUBLIC_POST_URL_PREFIX ?? 'article',
|
||||||
// POST类型文章的默认路径前缀,例如默认POST类型的路径是 /article/[slug]
|
// POST类型文章的默认路径前缀,例如默认POST类型的路径是 /article/[slug]
|
||||||
// 如果此项配置为 '' 空, 则文章将没有前缀路径,使用场景: 希望文章前缀路径为 /post 的情况 支持多级
|
// 如果此项配置为 '' 空, 则文章将没有前缀路径
|
||||||
// 支援類似 WP 可自訂文章連結格式的功能:https://wordpress.org/documentation/article/customize-permalinks/,目前只先實作 %year%/%month%/%day%
|
// 支援類似 WP 可自訂文章連結格式的功能:https://wordpress.org/documentation/article/customize-permalinks/,目前只先實作 %year%/%month%/%day%
|
||||||
// 例:如想連結改成前綴 article + 時間戳記,可變更為: 'article/%year%/%month%/%day%'
|
// 例:如想連結改成前綴 article + 時間戳記,可變更為: 'article/%year%/%month%/%day%'
|
||||||
|
|
||||||
@@ -540,7 +545,7 @@ const BLOG = {
|
|||||||
process.env.ENABLE_CACHE ||
|
process.env.ENABLE_CACHE ||
|
||||||
process.env.npm_lifecycle_event === 'build' ||
|
process.env.npm_lifecycle_event === 'build' ||
|
||||||
process.env.npm_lifecycle_event === 'export', // 在打包过程中默认开启缓存,开发或运行时开启此功能意义不大。
|
process.env.npm_lifecycle_event === 'export', // 在打包过程中默认开启缓存,开发或运行时开启此功能意义不大。
|
||||||
isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
isProd: process.env.VERCEL_ENV === 'production' || process.env.EXPORT, // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
||||||
BUNDLE_ANALYZER: process.env.ANALYZE === 'true' || false, // 是否展示编译依赖内容与大小
|
BUNDLE_ANALYZER: process.env.ANALYZE === 'true' || false, // 是否展示编译依赖内容与大小
|
||||||
VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号
|
VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,8 @@ import { useEffect } from 'react'
|
|||||||
export default function AOSAnimation() {
|
export default function AOSAnimation() {
|
||||||
const initAOS = async () => {
|
const initAOS = async () => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
loadExternalResource(
|
loadExternalResource('/js/aos.js', 'js'),
|
||||||
'https://cdn.bootcdn.net/ajax/libs/aos/2.3.4/aos.js',
|
loadExternalResource('/css/aos.css', 'css')
|
||||||
'js'
|
|
||||||
),
|
|
||||||
loadExternalResource(
|
|
||||||
'https://cdn.bootcdn.net/ajax/libs/aos/2.3.4/aos.css',
|
|
||||||
'css'
|
|
||||||
)
|
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
if (window.AOS) {
|
if (window.AOS) {
|
||||||
window.AOS.init()
|
window.AOS.init()
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import { useState, useImperativeHandle, useRef, useEffect, Fragment } from 'react'
|
|
||||||
import algoliasearch from 'algoliasearch'
|
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import Link from 'next/link'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import throttle from 'lodash/throttle'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import algoliasearch from 'algoliasearch'
|
||||||
|
import throttle from 'lodash/throttle'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import {
|
||||||
|
Fragment,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react'
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
|
|
||||||
const ShortCutActions = [
|
const ShortCutActions = [
|
||||||
{
|
{
|
||||||
@@ -20,7 +27,6 @@ const ShortCutActions = [
|
|||||||
key: 'Esc',
|
key: 'Esc',
|
||||||
action: '关闭'
|
action: '关闭'
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,31 +42,49 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
const [totalPage, setTotalPage] = useState(0)
|
const [totalPage, setTotalPage] = useState(0)
|
||||||
const [totalHit, setTotalHit] = useState(0)
|
const [totalHit, setTotalHit] = useState(0)
|
||||||
const [useTime, setUseTime] = useState(0)
|
const [useTime, setUseTime] = useState(0)
|
||||||
const inputRef = useRef(null)
|
|
||||||
const [activeIndex, setActiveIndex] = useState(0)
|
const [activeIndex, setActiveIndex] = useState(0)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
useHotkeys('ctrl+k', (e) => {
|
const inputRef = useRef(null)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快捷键设置
|
||||||
|
*/
|
||||||
|
useHotkeys('ctrl+k', e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setIsModalOpen(true)
|
setIsModalOpen(true)
|
||||||
})
|
})
|
||||||
// 方向键调整选中
|
// 方向键调整选中
|
||||||
useHotkeys('down', (e) => {
|
useHotkeys(
|
||||||
e.preventDefault()
|
'down',
|
||||||
if (activeIndex < searchResults.length - 1) {
|
e => {
|
||||||
setActiveIndex(activeIndex + 1)
|
e.preventDefault()
|
||||||
}
|
if (activeIndex < searchResults.length - 1) {
|
||||||
}, { enableOnFormTags: true })
|
setActiveIndex(activeIndex + 1)
|
||||||
useHotkeys('up', (e) => {
|
}
|
||||||
e.preventDefault()
|
},
|
||||||
if (activeIndex > 0) {
|
{ enableOnFormTags: true }
|
||||||
setActiveIndex(activeIndex - 1)
|
)
|
||||||
}
|
useHotkeys(
|
||||||
}, { enableOnFormTags: true })
|
'up',
|
||||||
|
e => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (activeIndex > 0) {
|
||||||
|
setActiveIndex(activeIndex - 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ enableOnFormTags: true }
|
||||||
|
)
|
||||||
// esc关闭
|
// esc关闭
|
||||||
useHotkeys('esc', (e) => {
|
useHotkeys(
|
||||||
e.preventDefault()
|
'esc',
|
||||||
setIsModalOpen(false)
|
e => {
|
||||||
}, { enableOnFormTags: true })
|
e.preventDefault()
|
||||||
|
setIsModalOpen(false)
|
||||||
|
},
|
||||||
|
{ enableOnFormTags: true }
|
||||||
|
)
|
||||||
|
|
||||||
// 跳转Search结果
|
// 跳转Search结果
|
||||||
const onJumpSearchResult = () => {
|
const onJumpSearchResult = () => {
|
||||||
if (searchResults.length > 0) {
|
if (searchResults.length > 0) {
|
||||||
@@ -68,11 +92,15 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// enter跳转
|
// enter跳转
|
||||||
useHotkeys('enter', (e) => {
|
useHotkeys(
|
||||||
if (searchResults.length > 0) {
|
'enter',
|
||||||
onJumpSearchResult(index)
|
e => {
|
||||||
}
|
if (searchResults.length > 0) {
|
||||||
}, { enableOnFormTags: true })
|
onJumpSearchResult(index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ enableOnFormTags: true }
|
||||||
|
)
|
||||||
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
setActiveIndex(0)
|
setActiveIndex(0)
|
||||||
@@ -84,6 +112,16 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
if (inputRef.current) inputRef.current.value = ''
|
if (inputRef.current) inputRef.current.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面路径变化后,自动关闭此modal
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
setIsModalOpen(false)
|
||||||
|
}, [router])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动聚焦搜索框
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isModalOpen) {
|
if (isModalOpen) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -93,9 +131,10 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
resetSearch()
|
resetSearch()
|
||||||
}
|
}
|
||||||
}, [isModalOpen])
|
}, [isModalOpen])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对外暴露方法
|
* 对外暴露方法
|
||||||
*/
|
**/
|
||||||
useImperativeHandle(cRef, () => {
|
useImperativeHandle(cRef, () => {
|
||||||
return {
|
return {
|
||||||
openSearch: () => {
|
openSearch: () => {
|
||||||
@@ -104,7 +143,10 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const client = algoliasearch(siteConfig('ALGOLIA_APP_ID'), siteConfig('ALGOLIA_SEARCH_ONLY_APP_KEY'))
|
const client = algoliasearch(
|
||||||
|
siteConfig('ALGOLIA_APP_ID'),
|
||||||
|
siteConfig('ALGOLIA_SEARCH_ONLY_APP_KEY')
|
||||||
|
)
|
||||||
const index = client.initIndex(siteConfig('ALGOLIA_INDEX'))
|
const index = client.initIndex(siteConfig('ALGOLIA_INDEX'))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +173,9 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
setTotalHit(nbHits)
|
setTotalHit(nbHits)
|
||||||
setSearchResults(hits)
|
setSearchResults(hits)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
const doms = document.getElementById('search-wrapper').getElementsByClassName('replace')
|
const doms = document
|
||||||
|
.getElementById('search-wrapper')
|
||||||
|
.getElementsByClassName('replace')
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
replaceSearchResult({
|
replaceSearchResult({
|
||||||
@@ -149,33 +193,35 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 定义节流函数,确保在用户停止输入一段时间后才会调用处理搜索的方法
|
// 定义节流函数,确保在用户停止输入一段时间后才会调用处理搜索的方法
|
||||||
const throttledHandleInputChange = useRef(throttle((query, page = 0) => {
|
const throttledHandleInputChange = useRef(
|
||||||
handleSearch(query, page);
|
throttle((query, page = 0) => {
|
||||||
}, 1000));
|
handleSearch(query, page)
|
||||||
|
}, 1000)
|
||||||
|
)
|
||||||
|
|
||||||
// 用于存储搜索延迟的计时器
|
// 用于存储搜索延迟的计时器
|
||||||
const searchTimer = useRef(null);
|
const searchTimer = useRef(null)
|
||||||
|
|
||||||
// 修改input的onChange事件处理函数
|
// 修改input的onChange事件处理函数
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = e => {
|
||||||
const query = e.target.value;
|
const query = e.target.value
|
||||||
|
|
||||||
// 如果已经有计时器在等待搜索,先清除之前的计时器
|
// 如果已经有计时器在等待搜索,先清除之前的计时器
|
||||||
if (searchTimer.current) {
|
if (searchTimer.current) {
|
||||||
clearTimeout(searchTimer.current);
|
clearTimeout(searchTimer.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置新的计时器,在用户停止输入一段时间后触发搜索
|
// 设置新的计时器,在用户停止输入一段时间后触发搜索
|
||||||
searchTimer.current = setTimeout(() => {
|
searchTimer.current = setTimeout(() => {
|
||||||
throttledHandleInputChange.current(query);
|
throttledHandleInputChange.current(query)
|
||||||
}, 800);
|
}, 800)
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换页码
|
* 切换页码
|
||||||
* @param {*} page
|
* @param {*} page
|
||||||
*/
|
*/
|
||||||
const switchPage = (page) => {
|
const switchPage = page => {
|
||||||
throttledHandleInputChange.current(keyword, page)
|
throttledHandleInputChange.current(keyword, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,58 +237,58 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id='search-wrapper'
|
||||||
className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'
|
className={`${
|
||||||
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-12 flex items-start justify-center mt-0`}
|
isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'
|
||||||
>
|
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-12 flex items-start justify-center mt-0`}>
|
||||||
{/* 模态框 */}
|
{/* 模态框 */}
|
||||||
<div
|
<div
|
||||||
className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'
|
className={`${
|
||||||
} flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}
|
isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'
|
||||||
>
|
} flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
|
||||||
<div className="flex justify-between items-center">
|
<div className='flex justify-between items-center'>
|
||||||
<div className="text-2xl text-blue-600 dark:text-yellow-600 font-bold">搜索</div>
|
<div className='text-2xl text-blue-600 dark:text-yellow-600 font-bold'>
|
||||||
|
搜索
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<i
|
<i
|
||||||
className="text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600"
|
className='text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600'
|
||||||
onClick={closeModal}
|
onClick={closeModal}></i>
|
||||||
></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type='text'
|
||||||
placeholder="在这里输入搜索关键词..."
|
placeholder='在这里输入搜索关键词...'
|
||||||
onChange={e => handleInputChange(e)}
|
onChange={e => handleInputChange(e)}
|
||||||
className="text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md"
|
className='text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md'
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 标签组 */}
|
{/* 标签组 */}
|
||||||
<div className="mb-4">
|
<div className='mb-4'>
|
||||||
<TagGroups />
|
<TagGroups />
|
||||||
</div>
|
</div>
|
||||||
{
|
{searchResults.length === 0 && keyword && !isLoading && (
|
||||||
searchResults.length === 0 && keyword && !isLoading && (
|
<div>
|
||||||
<div>
|
<p className=' text-slate-600 text-center my-4 text-base'>
|
||||||
<p className=" text-slate-600 text-center my-4 text-base"> 无法找到相关结果
|
{' '}
|
||||||
<span
|
无法找到相关结果
|
||||||
className='font-semibold'
|
<span className='font-semibold'>"{keyword}"</span>
|
||||||
>"{keyword}"</span></p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
<ul className='flex-1 overflow-auto'>
|
<ul className='flex-1 overflow-auto'>
|
||||||
{searchResults.map((result, index) => (
|
{searchResults.map((result, index) => (
|
||||||
<li key={result.objectID}
|
<li
|
||||||
|
key={result.objectID}
|
||||||
onMouseEnter={() => setActiveIndex(index)}
|
onMouseEnter={() => setActiveIndex(index)}
|
||||||
onClick={() => onJumpSearchResult(index)}
|
onClick={() => onJumpSearchResult(index)}
|
||||||
className={`cursor-pointer replace my-2 p-2 duration-100
|
className={`cursor-pointer replace my-2 p-2 duration-100
|
||||||
rounded-lg
|
rounded-lg
|
||||||
${activeIndex === index ? 'bg-blue-600 dark:bg-yellow-600' : ''}`}>
|
${activeIndex === index ? 'bg-blue-600 dark:bg-yellow-600' : ''}`}>
|
||||||
<a
|
<a
|
||||||
className={`${activeIndex === index ? ' text-white' : ' text-black dark:text-gray-300 '}`}
|
className={`${activeIndex === index ? ' text-white' : ' text-black dark:text-gray-300 '}`}>
|
||||||
>
|
|
||||||
{result.title}
|
{result.title}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -250,15 +296,22 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
</ul>
|
</ul>
|
||||||
<Pagination totalPage={totalPage} page={page} switchPage={switchPage} />
|
<Pagination totalPage={totalPage} page={page} switchPage={switchPage} />
|
||||||
<div className='flex items-center justify-between mt-2 sm:text-sm text-xs dark:text-gray-300'>
|
<div className='flex items-center justify-between mt-2 sm:text-sm text-xs dark:text-gray-300'>
|
||||||
{totalHit === 0 && (<div className='flex items-center'>
|
{totalHit === 0 && (
|
||||||
{
|
<div className='flex items-center'>
|
||||||
ShortCutActions.map((action, index) => {
|
{ShortCutActions.map((action, index) => {
|
||||||
return <Fragment key={index}><div className='border-gray-300 dark:text-gray-300 text-gray-600 px-2 rounded border inline-block'>{action.key}</div>
|
return (
|
||||||
<span className='ml-2 mr-4 text-gray-600 dark:text-gray-300'>{action.action}</span></Fragment>
|
<Fragment key={index}>
|
||||||
})
|
<div className='border-gray-300 dark:text-gray-300 text-gray-600 px-2 rounded border inline-block'>
|
||||||
}
|
{action.key}
|
||||||
</div>)
|
</div>
|
||||||
}
|
<span className='ml-2 mr-4 text-gray-600 dark:text-gray-300'>
|
||||||
|
{action.action}
|
||||||
|
</span>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
{totalHit > 0 && (
|
{totalHit > 0 && (
|
||||||
<p>
|
<p>
|
||||||
@@ -266,19 +319,18 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-600 dark:text-gray-300 text-right">
|
<div className='text-gray-600 dark:text-gray-300 text-right'>
|
||||||
<span >
|
<span>
|
||||||
<i className="fa-brands fa-algolia"></i> Algolia 提供搜索服务
|
<i className='fa-brands fa-algolia'></i> Algolia 提供搜索服务
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 遮罩 */}
|
{/* 遮罩 */}
|
||||||
<div
|
<div
|
||||||
onClick={closeModal}
|
onClick={closeModal}
|
||||||
className="z-30 fixed top-0 left-0 w-full h-full flex items-center justify-center glassmorphism"
|
className='z-30 fixed top-0 left-0 w-full h-full flex items-center justify-center glassmorphism'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -292,21 +344,31 @@ function TagGroups() {
|
|||||||
// 获取tagOptions数组前十个
|
// 获取tagOptions数组前十个
|
||||||
const firstTenTags = tagOptions?.slice(0, 10)
|
const firstTenTags = tagOptions?.slice(0, 10)
|
||||||
|
|
||||||
return <div id='tags-group' className='dark:border-gray-700 space-y-2'>
|
return (
|
||||||
{
|
<div id='tags-group' className='dark:border-gray-700 space-y-2'>
|
||||||
firstTenTags?.map((tag, index) => {
|
{firstTenTags?.map((tag, index) => {
|
||||||
return <Link passHref
|
return (
|
||||||
key={index}
|
<Link
|
||||||
href={`/tag/${encodeURIComponent(tag.name)}`}
|
passHref
|
||||||
className={'cursor-pointer inline-block whitespace-nowrap'}>
|
key={index}
|
||||||
<div className={'flex items-center text-black dark:text-gray-300 hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'}>
|
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||||
<div className='text-lg'>{tag.name} </div>{tag.count ? <sup className='relative ml-1'>{tag.count}</sup> : <></>}
|
className={'cursor-pointer inline-block whitespace-nowrap'}>
|
||||||
</div>
|
<div
|
||||||
|
className={
|
||||||
</Link>
|
'flex items-center text-black dark:text-gray-300 hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'
|
||||||
})
|
}>
|
||||||
}
|
<div className='text-lg'>{tag.name} </div>
|
||||||
</div>
|
{tag.count ? (
|
||||||
|
<sup className='relative ml-1'>{tag.count}</sup>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -321,16 +383,16 @@ function Pagination(props) {
|
|||||||
return (
|
return (
|
||||||
<div className='flex space-x-1 w-full justify-center py-1'>
|
<div className='flex space-x-1 w-full justify-center py-1'>
|
||||||
{Array.from({ length: totalPage }, (_, i) => {
|
{Array.from({ length: totalPage }, (_, i) => {
|
||||||
const classNames = page === i
|
const classNames =
|
||||||
? 'font-bold text-white bg-blue-600 dark:bg-yellow-600 rounded'
|
page === i
|
||||||
: 'hover:text-blue-600 hover:font-bold dark:text-gray-300'
|
? 'font-bold text-white bg-blue-600 dark:bg-yellow-600 rounded'
|
||||||
|
: 'hover:text-blue-600 hover:font-bold dark:text-gray-300'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => switchPage(i)}
|
onClick={() => switchPage(i)}
|
||||||
className={`text-center cursor-pointer w-6 h-6 ${classNames}`}
|
className={`text-center cursor-pointer w-6 h-6 ${classNames}`}
|
||||||
key={i}
|
key={i}>
|
||||||
>
|
|
||||||
{i + 1}
|
{i + 1}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -121,6 +121,11 @@ const Comment = ({ frontMatter, className }) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 特定文章关闭评论区
|
||||||
|
if (frontMatter?.comment === 'Hide') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={frontMatter?.id}
|
key={frontMatter?.id}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import Link from 'next/link'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useEffect, useState, useRef, useLayoutEffect } from 'react'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { saveDarkModeToLocalStorage, THEMES } from '@/themes/theme'
|
|
||||||
import useWindowSize from '@/hooks/useWindowSize'
|
import useWindowSize from '@/hooks/useWindowSize'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { THEMES, saveDarkModeToLocalStorage } from '@/themes/theme'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义右键菜单
|
* 自定义右键菜单
|
||||||
@@ -20,14 +20,14 @@ export default function CustomContextMenu(props) {
|
|||||||
const [width, setWidth] = useState(0)
|
const [width, setWidth] = useState(0)
|
||||||
const [height, setHeight] = useState(0)
|
const [height, setHeight] = useState(0)
|
||||||
|
|
||||||
const { latestPosts } = props
|
const { allNavPages } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
/**
|
/**
|
||||||
* 随机跳转文章
|
* 随机跳转文章
|
||||||
*/
|
*/
|
||||||
function handleJumpToRandomPost() {
|
function handleJumpToRandomPost() {
|
||||||
const randomIndex = Math.floor(Math.random() * latestPosts.length)
|
const randomIndex = Math.floor(Math.random() * allNavPages.length)
|
||||||
const randomPost = latestPosts[randomIndex]
|
const randomPost = allNavPages[randomIndex]
|
||||||
router.push(`${siteConfig('SUB_PATH', '')}/${randomPost?.slug}`)
|
router.push(`${siteConfig('SUB_PATH', '')}/${randomPost?.slug}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,19 +37,30 @@ export default function CustomContextMenu(props) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleContextMenu = (event) => {
|
setShow(false)
|
||||||
|
}, [router])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleContextMenu = event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
// 计算点击位置加菜单宽高是否超出屏幕,如果超出则贴边弹出
|
// 计算点击位置加菜单宽高是否超出屏幕,如果超出则贴边弹出
|
||||||
const x = (event.clientX < windowSize.width - width) ? event.clientX : windowSize.width - width
|
const x =
|
||||||
const y = (event.clientY < windowSize.height - height) ? event.clientY : windowSize.height - height
|
event.clientX < windowSize.width - width
|
||||||
|
? event.clientX
|
||||||
|
: windowSize.width - width
|
||||||
|
const y =
|
||||||
|
event.clientY < windowSize.height - height
|
||||||
|
? event.clientY
|
||||||
|
: windowSize.height - height
|
||||||
setPosition({ y: `${y}px`, x: `${x}px` })
|
setPosition({ y: `${y}px`, x: `${x}px` })
|
||||||
setShow(true)
|
setShow(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClick = (event) => {
|
/**
|
||||||
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
* 鼠标点击即关闭菜单
|
||||||
setShow(false)
|
*/
|
||||||
}
|
const handleClick = event => {
|
||||||
|
setShow(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('contextmenu', handleContextMenu)
|
window.addEventListener('contextmenu', handleContextMenu)
|
||||||
@@ -75,24 +86,24 @@ export default function CustomContextMenu(props) {
|
|||||||
|
|
||||||
function handleScrollTop() {
|
function handleScrollTop() {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
setShow(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCopyLink() {
|
function handleCopyLink() {
|
||||||
const url = window.location.href
|
const url = window.location.href
|
||||||
navigator.clipboard.writeText(url)
|
navigator.clipboard
|
||||||
|
.writeText(url)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('页面地址已复制')
|
// console.log('页面地址已复制')
|
||||||
|
alert(`${locale.COMMON.PAGE_URL_COPIED} : ${url}`)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.error('复制页面地址失败:', error)
|
console.error('复制页面地址失败:', error)
|
||||||
})
|
})
|
||||||
setShow(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换主题
|
* 切换主题
|
||||||
*/
|
*/
|
||||||
function handleChangeTheme() {
|
function handleChangeTheme() {
|
||||||
const randomTheme = THEMES[Math.floor(Math.random() * THEMES.length)] // 从THEMES数组中 随机取一个主题
|
const randomTheme = THEMES[Math.floor(Math.random() * THEMES.length)] // 从THEMES数组中 随机取一个主题
|
||||||
const query = router.query
|
const query = router.query
|
||||||
@@ -104,7 +115,7 @@ export default function CustomContextMenu(props) {
|
|||||||
* 复制内容
|
* 复制内容
|
||||||
*/
|
*/
|
||||||
function handleCopy() {
|
function handleCopy() {
|
||||||
const selectedText = document.getSelection().toString();
|
const selectedText = document.getSelection().toString()
|
||||||
if (selectedText) {
|
if (selectedText) {
|
||||||
const tempInput = document.createElement('input');
|
const tempInput = document.createElement('input');
|
||||||
tempInput.value = selectedText;
|
tempInput.value = selectedText;
|
||||||
@@ -118,8 +129,6 @@ export default function CustomContextMenu(props) {
|
|||||||
} else {
|
} else {
|
||||||
// alert("Please select some text first.");
|
// alert("Please select some text first.");
|
||||||
}
|
}
|
||||||
|
|
||||||
setShow(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChangeDarkMode() {
|
function handleChangeDarkMode() {
|
||||||
@@ -131,77 +140,140 @@ export default function CustomContextMenu(props) {
|
|||||||
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
|
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 一些配置变量
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST'
|
||||||
|
)
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY'
|
||||||
|
)
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG'
|
||||||
|
)
|
||||||
|
const CAN_COPY = siteConfig('CAN_COPY')
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK'
|
||||||
|
)
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE'
|
||||||
|
)
|
||||||
|
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH = siteConfig(
|
||||||
|
'CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH'
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
style={{ top: position.y, left: position.x }}
|
style={{ top: position.y, left: position.x }}
|
||||||
className={`${show ? '' : 'invisible opacity-0'} select-none transition-opacity duration-200 fixed z-50`}
|
className={`${show ? '' : 'invisible opacity-0'} select-none transition-opacity duration-200 fixed z-50`}>
|
||||||
>
|
{/* 菜单内容 */}
|
||||||
|
<div className='rounded-xl w-52 dark:hover:border-yellow-600 bg-white dark:bg-[#040404] dark:text-gray-200 dark:border-gray-600 p-3 border drop-shadow-lg flex-col duration-300 transition-colors'>
|
||||||
|
{/* 顶部导航按钮 */}
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<i
|
||||||
|
onClick={handleBack}
|
||||||
|
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-left'></i>
|
||||||
|
<i
|
||||||
|
onClick={handleForward}
|
||||||
|
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-right'></i>
|
||||||
|
<i
|
||||||
|
onClick={handleRefresh}
|
||||||
|
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-rotate-right'></i>
|
||||||
|
<i
|
||||||
|
onClick={handleScrollTop}
|
||||||
|
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-up'></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 菜单内容 */}
|
<hr className='my-2 border-dashed' />
|
||||||
<div className='rounded-xl w-52 dark:hover:border-yellow-600 bg-white dark:bg-[#040404] dark:text-gray-200 dark:border-gray-600 p-3 border drop-shadow-lg flex-col duration-300 transition-colors'>
|
|
||||||
{/* 顶部导航按钮 */}
|
|
||||||
<div className='flex justify-between'>
|
|
||||||
<i onClick={handleBack} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-left"></i>
|
|
||||||
<i onClick={handleForward} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-right"></i>
|
|
||||||
<i onClick={handleRefresh} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-rotate-right"></i>
|
|
||||||
<i onClick={handleScrollTop} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-up"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr className='my-2 border-dashed' />
|
|
||||||
|
|
||||||
{/* 跳转导航按钮 */}
|
|
||||||
<div className='w-full px-2'>
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST') && <div onClick={handleJumpToRandomPost} title={locale.MENU.WALK_AROUND} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-podcast mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.WALK_AROUND}</div>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY') && <Link href='/category' title={locale.MENU.CATEGORY} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-square-minus mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG') && <Link href='/tag' title={locale.MENU.TAGS} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-tag mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr className='my-2 border-dashed' />
|
|
||||||
|
|
||||||
{/* 功能按钮 */}
|
|
||||||
<div className='w-full px-2'>
|
|
||||||
|
|
||||||
{siteConfig('CAN_COPY') && (
|
|
||||||
<div onClick={handleCopy} title={locale.MENU.COPY} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-copy mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.COPY}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK') && <div onClick={handleCopyLink} title={locale.MENU.SHARE_URL} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-arrow-up-right-from-square mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.SHARE_URL}</div>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE') && <div onClick={handleChangeDarkMode} title={isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
{isDarkMode ? <i className="fa-regular fa-sun mr-2" /> : <i className="fa-regular fa-moon mr-2" />}
|
|
||||||
<div className='whitespace-nowrap'> {isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}</div>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH') && (
|
|
||||||
<div onClick={handleChangeTheme} title={locale.MENU.THEME_SWITCH} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
|
||||||
<i className="fa-solid fa-palette mr-2" />
|
|
||||||
<div className='whitespace-nowrap'>{locale.MENU.THEME_SWITCH}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* 跳转导航按钮 */}
|
||||||
|
<div className='w-full px-2'>
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST && (
|
||||||
|
<div
|
||||||
|
onClick={handleJumpToRandomPost}
|
||||||
|
title={locale.MENU.WALK_AROUND}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-podcast mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>{locale.MENU.WALK_AROUND}</div>
|
||||||
</div>
|
</div>
|
||||||
</div >
|
)}
|
||||||
|
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY && (
|
||||||
|
<Link
|
||||||
|
href='/category'
|
||||||
|
title={locale.MENU.CATEGORY}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-square-minus mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG && (
|
||||||
|
<Link
|
||||||
|
href='/tag'
|
||||||
|
title={locale.MENU.TAGS}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-tag mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className='my-2 border-dashed' />
|
||||||
|
|
||||||
|
{/* 功能按钮 */}
|
||||||
|
<div className='w-full px-2'>
|
||||||
|
{CAN_COPY && (
|
||||||
|
<div
|
||||||
|
onClick={handleCopy}
|
||||||
|
title={locale.MENU.COPY}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-copy mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>{locale.MENU.COPY}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK && (
|
||||||
|
<div
|
||||||
|
onClick={handleCopyLink}
|
||||||
|
title={locale.MENU.SHARE_URL}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-arrow-up-right-from-square mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>{locale.MENU.SHARE_URL}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE && (
|
||||||
|
<div
|
||||||
|
onClick={handleChangeDarkMode}
|
||||||
|
title={
|
||||||
|
isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE
|
||||||
|
}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
{isDarkMode ? (
|
||||||
|
<i className='fa-regular fa-sun mr-2' />
|
||||||
|
) : (
|
||||||
|
<i className='fa-regular fa-moon mr-2' />
|
||||||
|
)}
|
||||||
|
<div className='whitespace-nowrap'>
|
||||||
|
{' '}
|
||||||
|
{isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH && (
|
||||||
|
<div
|
||||||
|
onClick={handleChangeTheme}
|
||||||
|
title={locale.MENU.THEME_SWITCH}
|
||||||
|
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||||
|
<i className='fa-solid fa-palette mr-2' />
|
||||||
|
<div className='whitespace-nowrap'>
|
||||||
|
{locale.MENU.THEME_SWITCH}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import LA51 from './LA51'
|
|||||||
import TianLiGPT from './TianliGPT'
|
import TianLiGPT from './TianliGPT'
|
||||||
import WebWhiz from './Webwhiz'
|
import WebWhiz from './Webwhiz'
|
||||||
|
|
||||||
import { CUSTOM_EXTERNAL_CSS, CUSTOM_EXTERNAL_JS } from '@/blog.config'
|
import { convertInnerUrl } from '@/lib/notion/convertInnerUrl'
|
||||||
import { mapPageUrl } from '@/lib/notion/mapPageUrl'
|
|
||||||
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
@@ -44,8 +43,6 @@ const ExternalPlugin = props => {
|
|||||||
const CHATBASE_ID = siteConfig('CHATBASE_ID')
|
const CHATBASE_ID = siteConfig('CHATBASE_ID')
|
||||||
const COMMENT_DAO_VOICE_ID = siteConfig('COMMENT_DAO_VOICE_ID')
|
const COMMENT_DAO_VOICE_ID = siteConfig('COMMENT_DAO_VOICE_ID')
|
||||||
const AD_WWADS_ID = siteConfig('AD_WWADS_ID')
|
const AD_WWADS_ID = siteConfig('AD_WWADS_ID')
|
||||||
// const COMMENT_TWIKOO_ENV_ID = siteConfig('COMMENT_TWIKOO_ENV_ID')
|
|
||||||
// const COMMENT_TWIKOO_CDN_URL = siteConfig('COMMENT_TWIKOO_CDN_URL')
|
|
||||||
const COMMENT_ARTALK_SERVER = siteConfig('COMMENT_ARTALK_SERVER')
|
const COMMENT_ARTALK_SERVER = siteConfig('COMMENT_ARTALK_SERVER')
|
||||||
const COMMENT_ARTALK_JS = siteConfig('COMMENT_ARTALK_JS')
|
const COMMENT_ARTALK_JS = siteConfig('COMMENT_ARTALK_JS')
|
||||||
const COMMENT_TIDIO_ID = siteConfig('COMMENT_TIDIO_ID')
|
const COMMENT_TIDIO_ID = siteConfig('COMMENT_TIDIO_ID')
|
||||||
@@ -64,6 +61,8 @@ const ExternalPlugin = props => {
|
|||||||
const IMG_SHADOW = siteConfig('IMG_SHADOW')
|
const IMG_SHADOW = siteConfig('IMG_SHADOW')
|
||||||
const ANIMATE_CSS_URL = siteConfig('ANIMATE_CSS_URL')
|
const ANIMATE_CSS_URL = siteConfig('ANIMATE_CSS_URL')
|
||||||
const MOUSE_FOLLOW = siteConfig('MOUSE_FOLLOW')
|
const MOUSE_FOLLOW = siteConfig('MOUSE_FOLLOW')
|
||||||
|
const CUSTOM_EXTERNAL_CSS = siteConfig('CUSTOM_EXTERNAL_CSS')
|
||||||
|
const CUSTOM_EXTERNAL_JS = siteConfig('CUSTOM_EXTERNAL_JS')
|
||||||
|
|
||||||
// 自定义样式css和js引入
|
// 自定义样式css和js引入
|
||||||
if (isBrowser) {
|
if (isBrowser) {
|
||||||
@@ -106,7 +105,7 @@ const ExternalPlugin = props => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 映射url
|
// 映射url
|
||||||
mapPageUrl(props?.allNavPages)
|
convertInnerUrl(props?.allNavPages)
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const Fireworks = () => {
|
|||||||
// 异步加载
|
// 异步加载
|
||||||
async function loadFireworks() {
|
async function loadFireworks() {
|
||||||
loadExternalResource(
|
loadExternalResource(
|
||||||
'https://cdn.bootcdn.net/ajax/libs/animejs/3.2.1/anime.min.js',
|
'https://cdnjs.snrat.com/ajax/libs/animejs/3.2.1/anime.min.js',
|
||||||
'js'
|
'js'
|
||||||
).then(() => {
|
).then(() => {
|
||||||
loadExternalResource('/js/fireworks.js', 'js').then(() => {
|
loadExternalResource('/js/fireworks.js', 'js').then(() => {
|
||||||
@@ -35,7 +35,11 @@ const Fireworks = () => {
|
|||||||
loadFireworks()
|
loadFireworks()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// 在组件卸载时清理资源(如果需要)
|
// 在组件卸载时清理资源
|
||||||
|
const fireworksElements = document.getElementsByClassName('fireworks')
|
||||||
|
while (fireworksElements.length > 0) {
|
||||||
|
fireworksElements[0].parentNode.removeChild(fireworksElements[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片懒加载
|
* 图片懒加载
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
@@ -20,27 +21,52 @@ export default function LazyImage({
|
|||||||
style
|
style
|
||||||
}) {
|
}) {
|
||||||
const maxWidth = siteConfig('IMAGE_COMPRESS_WIDTH')
|
const maxWidth = siteConfig('IMAGE_COMPRESS_WIDTH')
|
||||||
|
const defaultPlaceholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||||
const imageRef = useRef(null)
|
const imageRef = useRef(null)
|
||||||
const [adjustedSrc, setAdjustedSrc] = useState(
|
const [currentSrc, setCurrentSrc] = useState(
|
||||||
placeholderSrc || siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
placeholderSrc || defaultPlaceholderSrc
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!placeholderSrc) {
|
|
||||||
placeholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片加载成功回调
|
* 占位图加载成功
|
||||||
*/
|
*/
|
||||||
const handleImageLoad = () => {
|
const handleThumbnailLoaded = () => {
|
||||||
|
if (typeof onLoad === 'function') {
|
||||||
|
// onLoad() // 触发传递的onLoad回调函数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 原图加载完成
|
||||||
|
const handleImageLoaded = img => {
|
||||||
if (typeof onLoad === 'function') {
|
if (typeof onLoad === 'function') {
|
||||||
onLoad() // 触发传递的onLoad回调函数
|
onLoad() // 触发传递的onLoad回调函数
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 图片加载失败回调
|
||||||
|
*/
|
||||||
|
const handleImageError = () => {
|
||||||
|
if (imageRef.current) {
|
||||||
|
// 尝试加载 placeholderSrc,如果失败则加载 defaultPlaceholderSrc
|
||||||
|
if (imageRef.current.src !== placeholderSrc && placeholderSrc) {
|
||||||
|
imageRef.current.src = placeholderSrc
|
||||||
|
} else {
|
||||||
|
imageRef.current.src = defaultPlaceholderSrc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const adjustedImageSrc = adjustImgSize(src, maxWidth)
|
const adjustedImageSrc =
|
||||||
setAdjustedSrc(adjustedImageSrc)
|
adjustImgSize(src, maxWidth) || defaultPlaceholderSrc
|
||||||
|
|
||||||
|
// 加载原图
|
||||||
|
const img = new Image()
|
||||||
|
img.src = adjustedImageSrc
|
||||||
|
img.onload = () => {
|
||||||
|
setCurrentSrc(adjustedImageSrc)
|
||||||
|
handleImageLoaded(adjustedImageSrc)
|
||||||
|
}
|
||||||
|
img.onerror = handleImageError
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
entries => {
|
entries => {
|
||||||
@@ -69,9 +95,10 @@ export default function LazyImage({
|
|||||||
// 动态添加width、height和className属性,仅在它们为有效值时添加
|
// 动态添加width、height和className属性,仅在它们为有效值时添加
|
||||||
const imgProps = {
|
const imgProps = {
|
||||||
ref: imageRef,
|
ref: imageRef,
|
||||||
src: priority ? adjustedSrc : placeholderSrc,
|
src: currentSrc,
|
||||||
alt: alt,
|
alt: alt,
|
||||||
onLoad: handleImageLoad
|
onLoad: handleThumbnailLoaded, // 缩略图加载完成
|
||||||
|
onError: handleImageError // 添加onError处理函数
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -95,6 +122,7 @@ export default function LazyImage({
|
|||||||
if (style) {
|
if (style) {
|
||||||
imgProps.style = style
|
imgProps.style = style
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
@@ -102,24 +130,25 @@ export default function LazyImage({
|
|||||||
{/* 预加载 */}
|
{/* 预加载 */}
|
||||||
{priority && (
|
{priority && (
|
||||||
<Head>
|
<Head>
|
||||||
<link rel='preload' as='image' href={adjustedSrc} />
|
<link rel='preload' as='image' href={adjustImgSize(src, maxWidth)} />
|
||||||
</Head>
|
</Head>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据窗口尺寸决定压缩图片宽度
|
* 根据窗口尺寸决定压缩图片宽度
|
||||||
* @param {*} src
|
* @param {*} src
|
||||||
* @param {*} maxWidth
|
* @param {*} maxWidth
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const adjustImgSize = (src, maxWidth) => {
|
const adjustImgSize = (src, maxWidth) => {
|
||||||
if (!src) {
|
if (!src) {
|
||||||
return siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
return null
|
||||||
}
|
}
|
||||||
const screenWidth = window.screen.width
|
const screenWidth =
|
||||||
|
(typeof window !== 'undefined' && window?.screen?.width) || maxWidth
|
||||||
|
|
||||||
// 屏幕尺寸大于默认图片尺寸,没必要再压缩
|
// 屏幕尺寸大于默认图片尺寸,没必要再压缩
|
||||||
if (screenWidth > maxWidth) {
|
if (screenWidth > maxWidth) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function LoadingProgress() {
|
|||||||
// 加载进度条
|
// 加载进度条
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadExternalResource(
|
loadExternalResource(
|
||||||
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js',
|
'https://cdnjs.snrat.com/ajax/libs/nprogress/0.2.0/nprogress.min.js',
|
||||||
'js'
|
'js'
|
||||||
).then(() => {
|
).then(() => {
|
||||||
if (window.NProgress) {
|
if (window.NProgress) {
|
||||||
@@ -19,7 +19,7 @@ export default function LoadingProgress() {
|
|||||||
// 调速
|
// 调速
|
||||||
window.NProgress.settings.minimun = 0.1
|
window.NProgress.settings.minimun = 0.1
|
||||||
loadExternalResource(
|
loadExternalResource(
|
||||||
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css',
|
'https://cdnjs.snrat.com/ajax/libs/nprogress/0.2.0/nprogress.min.css',
|
||||||
'css'
|
'css'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,26 @@ const MOUSE_FOLLOW = () => {
|
|||||||
loadExternalResource('/js/mouse-follow.js', 'js').then(url => {
|
loadExternalResource('/js/mouse-follow.js', 'js').then(url => {
|
||||||
window.createMouseCanvas && window.createMouseCanvas()({ type, color })
|
window.createMouseCanvas && window.createMouseCanvas()({ type, color })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// 在组件卸载时清理资源
|
||||||
|
const mouseFollowElement = document.getElementById('vixcityCanvas')
|
||||||
|
mouseFollowElement?.parentNode?.removeChild(mouseFollowElement)
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <></>
|
return (
|
||||||
|
<>
|
||||||
|
<style global jsx>
|
||||||
|
{`
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#vixcityCanvas {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
export default MOUSE_FOLLOW
|
export default MOUSE_FOLLOW
|
||||||
|
|||||||
63
components/Notification.js
Normal file
63
components/Notification.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹框通知
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const useNotification = () => {
|
||||||
|
const [message, setMessage] = useState('')
|
||||||
|
const [isVisible, setIsVisible] = useState(false)
|
||||||
|
|
||||||
|
const showNotification = msg => {
|
||||||
|
setMessage(msg)
|
||||||
|
setIsVisible(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
closeNotification()
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeNotification = () => {
|
||||||
|
setIsVisible(false)
|
||||||
|
setMessage('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试通知效果
|
||||||
|
// const toggleVisible = () => {
|
||||||
|
// setIsVisible(prev => !prev) // 使用函数式更新
|
||||||
|
// }
|
||||||
|
// useEffect(() => {
|
||||||
|
// document?.addEventListener('click', toggleVisible)
|
||||||
|
// return () => {
|
||||||
|
// document?.removeEventListener('click', toggleVisible)
|
||||||
|
// }
|
||||||
|
// }, [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知组件
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const Notification = () => {
|
||||||
|
return (
|
||||||
|
<div className={`notification fixed left-0 w-full px-2 z-20 bottom-14`}>
|
||||||
|
<div
|
||||||
|
className={` ${isVisible && message ? 'opacity-100 ' : 'invisible opacity-0 bottom-0'} transition-opacity duration-200
|
||||||
|
max-w-3xl mx-auto bg-green-500 flex items-center justify-between px-4 py-2 text-white rounded-lg shadow-lg`}>
|
||||||
|
{message}
|
||||||
|
<button
|
||||||
|
onClick={closeNotification}
|
||||||
|
className='ml-4 p-2 cursor-pointer bg-transparent text-white border-none'>
|
||||||
|
<i className='fas fa-times' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
showNotification,
|
||||||
|
closeNotification,
|
||||||
|
Notification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useNotification
|
||||||
@@ -31,7 +31,7 @@ const NotionPage = ({ post, className }) => {
|
|||||||
// 页面首次打开时执行的勾子
|
// 页面首次打开时执行的勾子
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 检测当前的url并自动滚动到对应目标
|
// 检测当前的url并自动滚动到对应目标
|
||||||
autoScrollToTarget()
|
autoScrollToHash()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// 页面文章发生变化时会执行的勾子
|
// 页面文章发生变化时会执行的勾子
|
||||||
@@ -144,9 +144,9 @@ const processGalleryImg = zoom => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据url参数自动滚动到指定区域
|
* 根据url参数自动滚动到锚位置
|
||||||
*/
|
*/
|
||||||
const autoScrollToTarget = () => {
|
const autoScrollToHash = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 跳转到指定标题
|
// 跳转到指定标题
|
||||||
const needToJumpToTitle = window.location.hash
|
const needToJumpToTitle = window.location.hash
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { useState } from 'react'
|
import LANGS from '@/lib/lang'
|
||||||
import { Draggable } from './Draggable'
|
import { getQueryParam } from '@/lib/utils'
|
||||||
import { THEMES } from '@/themes/theme'
|
import { THEMES } from '@/themes/theme'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import { useState } from 'react'
|
||||||
import DarkModeButton from './DarkModeButton'
|
import DarkModeButton from './DarkModeButton'
|
||||||
import { getQueryParam } from '@/lib/utils'
|
import { Draggable } from './Draggable'
|
||||||
import LANGS from '@/lib/lang'
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns 主题切换
|
* @returns 主题切换
|
||||||
*/
|
*/
|
||||||
const ThemeSwitch = () => {
|
const ThemeSwitch = () => {
|
||||||
const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } = useGlobal()
|
const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } =
|
||||||
|
useGlobal()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentTheme = getQueryParam(router.asPath, 'theme') || theme
|
const currentTheme = getQueryParam(router.asPath, 'theme') || theme
|
||||||
// const currentLang = getQueryParam(router.asPath, 'lang') || lang
|
// const currentLang = getQueryParam(router.asPath, 'lang') || lang
|
||||||
@@ -19,8 +20,12 @@ const ThemeSwitch = () => {
|
|||||||
|
|
||||||
// 修改当前路径url中的 theme 参数
|
// 修改当前路径url中的 theme 参数
|
||||||
// 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme
|
// 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme
|
||||||
const onThemeSelectChange = (e) => {
|
const onThemeSelectChange = e => {
|
||||||
document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
|
document.ontouchmove =
|
||||||
|
document.ontouchend =
|
||||||
|
document.onmousemove =
|
||||||
|
document.onmouseup =
|
||||||
|
null
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const newTheme = e.target.value
|
const newTheme = e.target.value
|
||||||
const query = router.query
|
const query = router.query
|
||||||
@@ -28,59 +33,96 @@ const ThemeSwitch = () => {
|
|||||||
router.push({ pathname: router.pathname, query }).then(() => {
|
router.push({ pathname: router.pathname, query }).then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}, 500);
|
}, 500)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLangSelectChange = (e) => {
|
const onLangSelectChange = e => {
|
||||||
document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
|
document.ontouchmove =
|
||||||
|
document.ontouchend =
|
||||||
|
document.onmousemove =
|
||||||
|
document.onmouseup =
|
||||||
|
null
|
||||||
const newLang = e.target.value
|
const newLang = e.target.value
|
||||||
changeLang(newLang)
|
changeLang(newLang)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<>
|
return (
|
||||||
<Draggable>
|
<>
|
||||||
<div id="draggableBox" style={{ left: '0px', top: '80vh' }} className="fixed group space-y-2 overflow-hidden z-50 p-3 flex flex-col items-start dark:text-white bg-white dark:bg-black rounded-xl shadow-lg ">
|
<Draggable>
|
||||||
{/* 深色按钮 */}
|
<div
|
||||||
<div className="text-sm flex items-center w-0 group-hover:w-32 transition-all duration-200">
|
id='draggableBox'
|
||||||
<DarkModeButton />
|
style={{ left: '0px', top: '80vh' }}
|
||||||
<div onClick={toggleDarkMode} className='cursor-pointer w-0 group-hover:w-24 transition-all duration-200 overflow-hidden whitespace-nowrap pl-1 h-auto'>{isDarkMode ? locale.MENU.DARK_MODE : locale.MENU.LIGHT_MODE}</div>
|
className='fixed group flex flex-col items-start space-y-2 overflow-hidden z-50 p-3
|
||||||
</div>
|
dark:text-white bg-white dark:bg-black
|
||||||
|
rounded-xl shadow-lg hover:scale-105 hover:shadow-2xl '>
|
||||||
{/* 翻译按钮 */}
|
{/* 主题切换按钮 */}
|
||||||
<div className="text-sm flex items-center group-hover:w-32 transition-all duration-200">
|
<div className='text-sm flex items-center group-hover:w-32 duration-200'>
|
||||||
<i className="fa-solid fa-language w-5" />
|
<i className='fa-solid fa-palette w-5' />
|
||||||
<div className='w-0 group-hover:w-24 transition-all duration-200 overflow-hidden'>
|
<div className='w-0 group-hover:w-24 duration-200 overflow-hidden'>
|
||||||
<label htmlFor="langSelect" className="sr-only">选择语言:</label>
|
<label htmlFor='themeSelect' className='sr-only'>
|
||||||
<select id="langSelect" value={lang} onChange={onLangSelectChange} name="themes" className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
|
{locale.COMMON.THEME}
|
||||||
{Object.keys(LANGS)?.map(t => {
|
</label>
|
||||||
return <option key={t} value={t}>{LANGS[t].LOCALE}</option>
|
<select
|
||||||
})}
|
id='themeSelect'
|
||||||
</select>
|
value={currentTheme}
|
||||||
</div>
|
onChange={onThemeSelectChange}
|
||||||
</div>
|
name='themes'
|
||||||
|
className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
|
||||||
{/* 主题切换按钮 */}
|
{THEMES?.map(t => {
|
||||||
<div className="text-sm flex items-center group-hover:w-32 transition-all duration-200">
|
return (
|
||||||
<i className="fa-solid fa-palette w-5" />
|
<option key={t} value={t}>
|
||||||
<div className='w-0 group-hover:w-24 transition-all duration-200 overflow-hidden'>
|
{t}
|
||||||
<label htmlFor="themeSelect" className="sr-only">选择主题:</label>
|
</option>
|
||||||
<select id="themeSelect" value={currentTheme} onChange={onThemeSelectChange} name="themes" className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
|
)
|
||||||
{THEMES?.map(t => {
|
})}
|
||||||
return <option key={t} value={t}>{t}</option>
|
</select>
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Draggable>
|
</div>
|
||||||
|
|
||||||
{/* 切换主题加载时的全屏遮罩 */}
|
{/* 深色按钮 */}
|
||||||
<div className={`${isLoading ? 'opacity-90 ' : 'opacity-0'}
|
<div className='text-sm flex items-center w-0 group-hover:w-32 duration-200'>
|
||||||
w-screen h-screen glassmorphism bg-black text-white shadow-text flex justify-center items-center
|
<DarkModeButton />
|
||||||
transition-all fixed top-0 left-0 pointer-events-none duration-1000 z-50 shadow-inner`}>
|
<div
|
||||||
<i className='text-3xl mr-5 fas fa-spinner animate-spin' />
|
onClick={toggleDarkMode}
|
||||||
|
className='cursor-pointer w-0 group-hover:w-24 duration-200 overflow-hidden whitespace-nowrap pl-1 h-auto'>
|
||||||
|
{isDarkMode ? locale.MENU.DARK_MODE : locale.MENU.LIGHT_MODE}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 翻译按钮 */}
|
||||||
|
<div className='text-sm flex items-center group-hover:w-32 duration-200'>
|
||||||
|
<i className='fa-solid fa-language w-5' />
|
||||||
|
<div className='w-0 group-hover:w-24 duration-200 overflow-hidden'>
|
||||||
|
<label htmlFor='langSelect' className='sr-only'>
|
||||||
|
Language Select
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id='langSelect'
|
||||||
|
value={lang}
|
||||||
|
onChange={onLangSelectChange}
|
||||||
|
name='themes'
|
||||||
|
className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
|
||||||
|
{Object.keys(LANGS)?.map(t => {
|
||||||
|
return (
|
||||||
|
<option key={t} value={t}>
|
||||||
|
{LANGS[t].LOCALE}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
{/* 切换主题加载时的全屏遮罩 */}
|
||||||
|
<div
|
||||||
|
className={`${isLoading ? 'opacity-90 ' : 'opacity-0'}
|
||||||
|
w-screen h-screen glassmorphism bg-black text-white shadow-text flex justify-center items-center
|
||||||
|
fixed top-0 left-0 pointer-events-none duration-1000 z-50 shadow-inner`}>
|
||||||
|
<i className='text-3xl mr-5 fas fa-spinner animate-spin' />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import { useGlobal } from './global'
|
import { useGlobal } from './global'
|
||||||
import { deepClone } from './utils'
|
import { deepClone, isUrl } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取配置顺序
|
* 读取配置顺序
|
||||||
@@ -19,7 +19,7 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 特殊配置处理;某些配置只在服务端生效;而Global的NOTION_CONFIG仅限前端组件使用,因此需要从extendConfig中读取
|
// 特殊配置处理;以下配置只在服务端生效;而Global的NOTION_CONFIG仅限前端组件使用,因此需要从extendConfig中读取
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'NEXT_REVALIDATE_SECOND':
|
case 'NEXT_REVALIDATE_SECOND':
|
||||||
case 'POST_RECOMMEND_COUNT':
|
case 'POST_RECOMMEND_COUNT':
|
||||||
@@ -32,6 +32,8 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
|||||||
case 'POST_LIST_STYLE':
|
case 'POST_LIST_STYLE':
|
||||||
case 'POST_LIST_PREVIEW':
|
case 'POST_LIST_PREVIEW':
|
||||||
case 'POST_URL_PREFIX_MAPPING_CATEGORY':
|
case 'POST_URL_PREFIX_MAPPING_CATEGORY':
|
||||||
|
case 'IS_TAG_COLOR_DISTINGUISHED':
|
||||||
|
case 'TAG_SORT_BY_COUNT':
|
||||||
return convertVal(extendConfig[key] || defaultVal || BLOG[key])
|
return convertVal(extendConfig[key] || defaultVal || BLOG[key])
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -53,8 +55,8 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
|||||||
let siteInfo = null
|
let siteInfo = null
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
val = global.NOTION_CONFIG?.[key]
|
|
||||||
siteInfo = global.siteInfo
|
siteInfo = global.siteInfo
|
||||||
|
val = global.NOTION_CONFIG?.[key] || global.THEME_CONFIG?.[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
@@ -89,46 +91,62 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
|||||||
return defaultVal
|
return defaultVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从Notion_CONFIG读取的配置通常都是字符串,适当转义
|
|
||||||
return convertVal(val)
|
return convertVal(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置默认都是string类型;
|
* 从环境变量和NotionConfig读取的配置都是string类型;
|
||||||
* 识别配置的值是否数字、布尔、[]数组,若是则转成对应类型
|
* 这里识别出配置的字符值若为否 数字、布尔、[]数组,{}对象,若是则转成对应类型
|
||||||
|
* 使用JSON和eval两个函数
|
||||||
* @param {*} val
|
* @param {*} val
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const convertVal = val => {
|
export const convertVal = val => {
|
||||||
if (typeof val === 'string') {
|
// 如果传入参数本身就是obj、数组、boolean 就无需处理
|
||||||
// 解析布尔
|
if (typeof val !== 'string' || !val) {
|
||||||
if (val === 'true' || val === 'false') {
|
|
||||||
return JSON.parse(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析数字,parseInt将字符串转换为数字
|
|
||||||
if (/^\d+$/.test(val)) {
|
|
||||||
return parseInt(val)
|
|
||||||
}
|
|
||||||
// 转移 [] , {} 这种json串为json对象
|
|
||||||
try {
|
|
||||||
const parsedJson = JSON.parse(val)
|
|
||||||
// 检查解析后的结果是否是对象或数组
|
|
||||||
if (typeof parsedJson === 'object' && parsedJson !== null) {
|
|
||||||
return parsedJson
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// JSON 解析失败,返回原始字符串值
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(val)
|
|
||||||
} catch (error) {
|
|
||||||
// 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
|
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析数字,parseInt将字符串转换为数字
|
||||||
|
if (/^\d+$/.test(val)) {
|
||||||
|
return parseInt(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测是否url
|
||||||
|
if (isUrl(val)) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
// 检测是否url
|
||||||
|
if (val === 'true' || val === 'false') {
|
||||||
|
return JSON.parse(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置值前可能有污染的空格
|
||||||
|
if (val.indexOf('[') < 0 && val.indexOf('{') < 0) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换 [] , {} , true/false 这类字符串为对象
|
||||||
|
try {
|
||||||
|
// 尝试解析json
|
||||||
|
const parsedJson = JSON.parse(val)
|
||||||
|
if (parsedJson !== null) {
|
||||||
|
return parsedJson
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// try {
|
||||||
|
// // 尝试解析对象,对象解析能力不如上一步的json
|
||||||
|
// const evalObj = eval('(' + val + ')')
|
||||||
|
// if (evalObj !== null) {
|
||||||
|
// return evalObj
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// // Ojbject 解析失败,返回原始字符串值
|
||||||
|
// return val
|
||||||
|
// }
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ import getAllPageIds from '@/lib/notion/getAllPageIds'
|
|||||||
import { getAllTags } from '@/lib/notion/getAllTags'
|
import { getAllTags } from '@/lib/notion/getAllTags'
|
||||||
import { getConfigMapFromConfigPage } from '@/lib/notion/getNotionConfig'
|
import { getConfigMapFromConfigPage } from '@/lib/notion/getNotionConfig'
|
||||||
import getPageProperties, {
|
import getPageProperties, {
|
||||||
adjustPageProperties
|
adjustPageProperties
|
||||||
} from '@/lib/notion/getPageProperties'
|
} from '@/lib/notion/getPageProperties'
|
||||||
import { fetchInBatches, getPostBlocks } from '@/lib/notion/getPostBlocks'
|
import { fetchInBatches, getPage } from '@/lib/notion/getPostBlocks'
|
||||||
import { compressImage, mapImgUrl } from '@/lib/notion/mapImage'
|
import { compressImage, mapImgUrl } from '@/lib/notion/mapImage'
|
||||||
import { deepClone } from '@/lib/utils'
|
import { deepClone } from '@/lib/utils'
|
||||||
import { idToUuid } from 'notion-utils'
|
import { idToUuid } from 'notion-utils'
|
||||||
import { siteConfig } from '../config'
|
import { siteConfig } from '../config'
|
||||||
import { extractLangId, extractLangPrefix } from '../utils/pageId'
|
import { extractLangId, extractLangPrefix, getShortId } from '../utils/pageId'
|
||||||
|
|
||||||
export { getAllTags } from '../notion/getAllTags'
|
export { getAllTags } from '../notion/getAllTags'
|
||||||
export { getPost } from '../notion/getNotionPost'
|
export { getPost } from '../notion/getNotionPost'
|
||||||
export { getPostBlocks } from '../notion/getPostBlocks'
|
export { getPage as getPostBlocks } from '../notion/getPostBlocks'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取博客数据; 基于Notion实现
|
* 获取博客数据; 基于Notion实现
|
||||||
@@ -77,7 +77,18 @@ export async function getNotionPageData({ pageId, from }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 返回给前端的数据做处理
|
// 返回给前端的数据做处理
|
||||||
const db = deepClone(data)
|
return handleDataBeforeReturn(deepClone(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回给浏览器前端的数据处理
|
||||||
|
* 适当脱敏
|
||||||
|
* 减少体积
|
||||||
|
* 其它处理
|
||||||
|
* @param {*} db
|
||||||
|
*/
|
||||||
|
function handleDataBeforeReturn(db) {
|
||||||
|
// 清理多余数据
|
||||||
delete db.block
|
delete db.block
|
||||||
delete db.schema
|
delete db.schema
|
||||||
delete db.rawMetadata
|
delete db.rawMetadata
|
||||||
@@ -91,42 +102,88 @@ export async function getNotionPageData({ pageId, from }) {
|
|||||||
// 清理多余的块
|
// 清理多余的块
|
||||||
if (db?.notice) {
|
if (db?.notice) {
|
||||||
db.notice = cleanBlock(db?.notice)
|
db.notice = cleanBlock(db?.notice)
|
||||||
|
delete db.notice?.id
|
||||||
}
|
}
|
||||||
if (db?.post) {
|
|
||||||
db.post = cleanBlock(db?.post)
|
db.tagOptions = cleanIds(db?.tagOptions)
|
||||||
}
|
db.categoryOptions = cleanIds(db?.categoryOptions)
|
||||||
|
db.customMenu = cleanIds(db?.customMenu)
|
||||||
|
|
||||||
|
// db.latestPosts = shortenIds(db?.latestPosts)
|
||||||
|
db.allNavPages = shortenIds(db?.allNavPages)
|
||||||
|
// db.allPages = cleanBlocks(db?.allPages)
|
||||||
|
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理一组数据的id
|
||||||
|
* @param {*} items
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function shortenIds(items) {
|
||||||
|
if (items && Array.isArray(items)) {
|
||||||
|
return deepClone(
|
||||||
|
items.map(item => {
|
||||||
|
item.short_id = getShortId(item.id)
|
||||||
|
delete item.id
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理一组数据的id
|
||||||
|
* @param {*} items
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function cleanIds(items) {
|
||||||
|
if (items && Array.isArray(items)) {
|
||||||
|
return deepClone(
|
||||||
|
items.map(item => {
|
||||||
|
delete item.id
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清理block数据
|
* 清理block数据
|
||||||
*/
|
*/
|
||||||
function cleanBlock(post) {
|
function cleanBlock(item) {
|
||||||
|
const post = deepClone(item)
|
||||||
const pageBlock = post?.blockMap?.block
|
const pageBlock = post?.blockMap?.block
|
||||||
for (const i in pageBlock) {
|
// delete post?.id
|
||||||
pageBlock[i] = cleanBlock(pageBlock[i])
|
|
||||||
delete pageBlock[i]?.role
|
|
||||||
delete pageBlock[i]?.value?.version
|
|
||||||
delete pageBlock[i]?.value?.created_by_table
|
|
||||||
delete pageBlock[i]?.value?.created_by_id
|
|
||||||
delete pageBlock[i]?.value?.last_edited_by_table
|
|
||||||
delete pageBlock[i]?.value?.last_edited_by_id
|
|
||||||
delete pageBlock[i]?.value?.space_id
|
|
||||||
delete pageBlock[i]?.value?.version
|
|
||||||
delete pageBlock[i]?.value?.format?.copied_from_pointer
|
|
||||||
delete pageBlock[i]?.value?.format?.block_locked_by
|
|
||||||
delete pageBlock[i]?.value?.parent_table
|
|
||||||
delete pageBlock[i]?.value?.copied_from_pointer
|
|
||||||
delete pageBlock[i]?.value?.copied_from
|
|
||||||
delete pageBlock[i]?.value?.created_by_table
|
|
||||||
delete pageBlock[i]?.value?.created_by_id
|
|
||||||
delete pageBlock[i]?.value?.last_edited_by_table
|
|
||||||
delete pageBlock[i]?.value?.last_edited_by_id
|
|
||||||
delete pageBlock[i]?.value?.permissions
|
|
||||||
delete pageBlock[i]?.value?.alive
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete post?.blockMap?.collection
|
// delete post?.blockMap?.collection
|
||||||
|
|
||||||
|
if (pageBlock) {
|
||||||
|
for (const i in pageBlock) {
|
||||||
|
pageBlock[i] = cleanBlock(pageBlock[i])
|
||||||
|
delete pageBlock[i]?.role
|
||||||
|
delete pageBlock[i]?.value?.version
|
||||||
|
delete pageBlock[i]?.value?.created_by_table
|
||||||
|
delete pageBlock[i]?.value?.created_by_id
|
||||||
|
delete pageBlock[i]?.value?.last_edited_by_table
|
||||||
|
delete pageBlock[i]?.value?.last_edited_by_id
|
||||||
|
delete pageBlock[i]?.value?.space_id
|
||||||
|
delete pageBlock[i]?.value?.version
|
||||||
|
delete pageBlock[i]?.value?.format?.copied_from_pointer
|
||||||
|
delete pageBlock[i]?.value?.format?.block_locked_by
|
||||||
|
delete pageBlock[i]?.value?.parent_table
|
||||||
|
delete pageBlock[i]?.value?.copied_from_pointer
|
||||||
|
delete pageBlock[i]?.value?.copied_from
|
||||||
|
delete pageBlock[i]?.value?.created_by_table
|
||||||
|
delete pageBlock[i]?.value?.created_by_id
|
||||||
|
delete pageBlock[i]?.value?.last_edited_by_table
|
||||||
|
delete pageBlock[i]?.value?.last_edited_by_id
|
||||||
|
delete pageBlock[i]?.value?.permissions
|
||||||
|
delete pageBlock[i]?.value?.alive
|
||||||
|
}
|
||||||
|
}
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +347,6 @@ export function getNavPages({ allPages }) {
|
|||||||
return (
|
return (
|
||||||
post &&
|
post &&
|
||||||
post?.slug &&
|
post?.slug &&
|
||||||
!post?.slug?.startsWith('http') &&
|
|
||||||
post?.type === 'Post' &&
|
post?.type === 'Post' &&
|
||||||
post?.status === 'Published'
|
post?.status === 'Published'
|
||||||
)
|
)
|
||||||
@@ -320,7 +376,7 @@ async function getNotice(post) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
post.blockMap = await getPostBlocks(post.id, 'data-notice')
|
post.blockMap = await getPage(post.id, 'data-notice')
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +428,7 @@ const EmptyData = pageId => {
|
|||||||
*/
|
*/
|
||||||
async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||||
console.log('[Fetching Data]', pageId, from)
|
console.log('[Fetching Data]', pageId, from)
|
||||||
const pageRecordMap = await getPostBlocks(pageId, from)
|
const pageRecordMap = await getPage(pageId, from)
|
||||||
if (!pageRecordMap) {
|
if (!pageRecordMap) {
|
||||||
console.error('can`t get Notion Data ; Which id is: ', pageId)
|
console.error('can`t get Notion Data ; Which id is: ', pageId)
|
||||||
return {}
|
return {}
|
||||||
@@ -455,6 +511,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
|||||||
adjustPageProperties(element, NOTION_CONFIG)
|
adjustPageProperties(element, NOTION_CONFIG)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 站点基础信息
|
||||||
const siteInfo = getSiteInfo({ collection, block, pageId })
|
const siteInfo = getSiteInfo({ collection, block, pageId })
|
||||||
|
|
||||||
// 文章计数
|
// 文章计数
|
||||||
@@ -468,7 +525,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
|||||||
return (
|
return (
|
||||||
post &&
|
post &&
|
||||||
post?.slug &&
|
post?.slug &&
|
||||||
!post?.slug?.startsWith('http') &&
|
// !post?.slug?.startsWith('http') &&
|
||||||
(post?.status === 'Invisible' || post?.status === 'Published')
|
(post?.status === 'Invisible' || post?.status === 'Published')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -490,11 +547,17 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
|||||||
)
|
)
|
||||||
})?.[0]
|
})?.[0]
|
||||||
)
|
)
|
||||||
|
// 所有分类
|
||||||
const categoryOptions = getAllCategories({
|
const categoryOptions = getAllCategories({
|
||||||
allPages,
|
allPages,
|
||||||
categoryOptions: getCategoryOptions(schema)
|
categoryOptions: getCategoryOptions(schema)
|
||||||
})
|
})
|
||||||
const tagOptions = getAllTags({ allPages, tagOptions: getTagOptions(schema) })
|
// 所有标签
|
||||||
|
const tagOptions = getAllTags({
|
||||||
|
allPages,
|
||||||
|
tagOptions: getTagOptions(schema),
|
||||||
|
NOTION_CONFIG
|
||||||
|
})
|
||||||
// 旧的菜单
|
// 旧的菜单
|
||||||
const customNav = getCustomNav({
|
const customNav = getCustomNav({
|
||||||
allPages: collectionData.filter(
|
allPages: collectionData.filter(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
THEMES,
|
THEMES,
|
||||||
|
getThemeConfig,
|
||||||
initDarkMode,
|
initDarkMode,
|
||||||
saveDarkModeToLocalStorage
|
saveDarkModeToLocalStorage
|
||||||
} from '@/themes/theme'
|
} from '@/themes/theme'
|
||||||
@@ -29,11 +30,14 @@ export function GlobalContextProvider(props) {
|
|||||||
tagOptions,
|
tagOptions,
|
||||||
NOTION_CONFIG
|
NOTION_CONFIG
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || LANG) // 默认语言
|
const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || LANG) // 默认语言
|
||||||
const [locale, updateLocale] = useState(
|
const [locale, updateLocale] = useState(
|
||||||
generateLocaleDict(NOTION_CONFIG?.LANG || LANG)
|
generateLocaleDict(NOTION_CONFIG?.LANG || LANG)
|
||||||
) // 默认语言
|
) // 默认语言
|
||||||
const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题
|
const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题
|
||||||
|
const [THEME_CONFIG, SET_THEME_CONFIG] = useState(null) // 主题配置
|
||||||
|
|
||||||
const defaultDarkMode = NOTION_CONFIG?.APPEARANCE || APPEARANCE
|
const defaultDarkMode = NOTION_CONFIG?.APPEARANCE || APPEARANCE
|
||||||
const [isDarkMode, updateDarkMode] = useState(defaultDarkMode === 'dark') // 默认深色模式
|
const [isDarkMode, updateDarkMode] = useState(defaultDarkMode === 'dark') // 默认深色模式
|
||||||
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
|
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
|
||||||
@@ -54,6 +58,12 @@ export function GlobalContextProvider(props) {
|
|||||||
return newTheme
|
return newTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 抓取主题配置
|
||||||
|
const updateThemeConfig = async theme => {
|
||||||
|
const config = await getThemeConfig(theme)
|
||||||
|
SET_THEME_CONFIG(config)
|
||||||
|
}
|
||||||
|
|
||||||
// 切换深色模式
|
// 切换深色模式
|
||||||
const toggleDarkMode = () => {
|
const toggleDarkMode = () => {
|
||||||
const newStatus = !isDarkMode
|
const newStatus = !isDarkMode
|
||||||
@@ -79,7 +89,10 @@ export function GlobalContextProvider(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initDarkMode(updateDarkMode, defaultDarkMode)
|
initDarkMode(updateDarkMode, defaultDarkMode)
|
||||||
initLocale(lang, locale, updateLang, updateLocale)
|
initLocale(lang, locale, updateLang, updateLocale)
|
||||||
redirectUserLang(NOTION_PAGE_ID)
|
// 可以
|
||||||
|
if (NOTION_CONFIG?.REDIRECT_LANG) {
|
||||||
|
redirectUserLang(NOTION_PAGE_ID)
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// 加载进度条
|
// 加载进度条
|
||||||
@@ -96,6 +109,9 @@ export function GlobalContextProvider(props) {
|
|||||||
setOnLoading(false)
|
setOnLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentTheme = router?.query?.theme || theme
|
||||||
|
updateThemeConfig(currentTheme)
|
||||||
|
|
||||||
router.events.on('routeChangeStart', handleStart)
|
router.events.on('routeChangeStart', handleStart)
|
||||||
router.events.on('routeChangeError', handleStop)
|
router.events.on('routeChangeError', handleStop)
|
||||||
router.events.on('routeChangeComplete', handleStop)
|
router.events.on('routeChangeComplete', handleStop)
|
||||||
@@ -111,6 +127,7 @@ export function GlobalContextProvider(props) {
|
|||||||
value={{
|
value={{
|
||||||
fullWidth,
|
fullWidth,
|
||||||
NOTION_CONFIG,
|
NOTION_CONFIG,
|
||||||
|
THEME_CONFIG,
|
||||||
toggleDarkMode,
|
toggleDarkMode,
|
||||||
onLoading,
|
onLoading,
|
||||||
setOnLoading,
|
setOnLoading,
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ export default {
|
|||||||
INDEX: 'Home',
|
INDEX: 'Home',
|
||||||
RSS: 'RSS',
|
RSS: 'RSS',
|
||||||
SEARCH: 'Search',
|
SEARCH: 'Search',
|
||||||
|
NAVIGATOR: 'NAV',
|
||||||
ABOUT: 'About',
|
ABOUT: 'About',
|
||||||
MAIL: 'E-Mail',
|
MAIL: 'E-Mail',
|
||||||
ARCHIVE: 'Archive'
|
ARCHIVE: 'Archive'
|
||||||
},
|
},
|
||||||
COMMON: {
|
COMMON: {
|
||||||
|
THEME: 'Theme',
|
||||||
|
ARTICLE_LIST: 'Article List',
|
||||||
|
RECOMMEND_POSTS: 'Recommend Posts',
|
||||||
MORE: 'More',
|
MORE: 'More',
|
||||||
NO_MORE: 'No More',
|
NO_MORE: 'No More',
|
||||||
LATEST_POSTS: 'Latest posts',
|
LATEST_POSTS: 'Latest posts',
|
||||||
@@ -33,15 +37,21 @@ export default {
|
|||||||
COPYRIGHT: 'Copyright',
|
COPYRIGHT: 'Copyright',
|
||||||
AUTHOR: 'Author',
|
AUTHOR: 'Author',
|
||||||
URL: 'URL',
|
URL: 'URL',
|
||||||
|
NOW: 'NOW',
|
||||||
|
RECOMMEND_BADGES: 'Recommend',
|
||||||
|
BLOG: 'Blog',
|
||||||
POSTS: 'Posts',
|
POSTS: 'Posts',
|
||||||
ARTICLE: 'Article',
|
ARTICLE: 'Article',
|
||||||
VISITORS: 'Visitors',
|
VISITORS: 'Visitors',
|
||||||
VIEWS: 'Views',
|
VIEWS: 'Views',
|
||||||
COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
|
PAGE_URL_COPIED: 'Page URL copied',
|
||||||
|
COPYRIGHT_NOTICE:
|
||||||
|
'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
|
||||||
RESULT_OF_SEARCH: 'Results Found',
|
RESULT_OF_SEARCH: 'Results Found',
|
||||||
ARTICLE_DETAIL: 'Article Details',
|
ARTICLE_DETAIL: 'Article Details',
|
||||||
PASSWORD_ERROR: 'Password Error!',
|
PASSWORD_ERROR: 'Password Error!',
|
||||||
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
||||||
|
ARTICLE_UNLOCK_TIPS: 'Article Unlocked',
|
||||||
NO_RESULTS_FOUND: 'No results found.',
|
NO_RESULTS_FOUND: 'No results found.',
|
||||||
SUBMIT: 'Submit',
|
SUBMIT: 'Submit',
|
||||||
POST_TIME: 'Post on',
|
POST_TIME: 'Post on',
|
||||||
@@ -55,7 +65,8 @@ export default {
|
|||||||
START_READING: 'Start Reading',
|
START_READING: 'Start Reading',
|
||||||
MINUTE: 'min',
|
MINUTE: 'min',
|
||||||
WORD_COUNT: 'Words',
|
WORD_COUNT: 'Words',
|
||||||
READ_TIME: 'Read Time'
|
READ_TIME: 'Read Time',
|
||||||
|
NEXT_POST: '下一篇'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: 'Prev',
|
PREV: 'Prev',
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ export default {
|
|||||||
ARTICLE: '記事',
|
ARTICLE: '記事',
|
||||||
VISITORS: '人の訪問者',
|
VISITORS: '人の訪問者',
|
||||||
VIEWS: '回の閲覧',
|
VIEWS: '回の閲覧',
|
||||||
COPYRIGHT_NOTICE: 'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。',
|
COPYRIGHT_NOTICE:
|
||||||
|
'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。',
|
||||||
RESULT_OF_SEARCH: '個の検索結果',
|
RESULT_OF_SEARCH: '個の検索結果',
|
||||||
ARTICLE_DETAIL: '記事の詳細',
|
ARTICLE_DETAIL: '記事の詳細',
|
||||||
PASSWORD_ERROR: 'パスワードが違います!',
|
PASSWORD_ERROR: 'パスワードが違います!',
|
||||||
ARTICLE_LOCK_TIPS: 'この記事はロックされています。アクセスパスワードを入力してください。',
|
ARTICLE_LOCK_TIPS:
|
||||||
|
'この記事はロックされています。アクセスパスワードを入力してください。',
|
||||||
|
ARTICLE_UNLOCK_TIPS: '記事がロック解除されました',
|
||||||
SUBMIT: '送信',
|
SUBMIT: '送信',
|
||||||
POST_TIME: '公開日',
|
POST_TIME: '公開日',
|
||||||
LAST_EDITED_TIME: '最終更新日',
|
LAST_EDITED_TIME: '最終更新日',
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export default {
|
|||||||
ARCHIVE: '归档'
|
ARCHIVE: '归档'
|
||||||
},
|
},
|
||||||
COMMON: {
|
COMMON: {
|
||||||
|
THEME: 'Theme',
|
||||||
|
ARTICLE_LIST: '文章列表',
|
||||||
|
RECOMMEND_POSTS: '推荐文章',
|
||||||
MORE: '更多',
|
MORE: '更多',
|
||||||
NO_MORE: '没有更多了',
|
NO_MORE: '没有更多了',
|
||||||
LATEST_POSTS: '最新发布',
|
LATEST_POSTS: '最新发布',
|
||||||
@@ -35,16 +38,21 @@ export default {
|
|||||||
AUTHOR: '作者',
|
AUTHOR: '作者',
|
||||||
URL: '链接',
|
URL: '链接',
|
||||||
ANALYTICS: '统计',
|
ANALYTICS: '统计',
|
||||||
|
RECOMMEND_BADGES: '荐',
|
||||||
|
BLOG: '博客',
|
||||||
|
NOW: '此刻',
|
||||||
POSTS: '篇文章',
|
POSTS: '篇文章',
|
||||||
ARTICLE: '文章',
|
ARTICLE: '文章',
|
||||||
VISITORS: '位访客',
|
VISITORS: '位访客',
|
||||||
VIEWS: '次查看',
|
VIEWS: '次查看',
|
||||||
|
PAGE_URL_COPIED: '页面地址已复制',
|
||||||
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
|
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
|
||||||
RESULT_OF_SEARCH: '篇搜索到的结果',
|
RESULT_OF_SEARCH: '篇搜索到的结果',
|
||||||
NO_RESULTS_FOUND: '没有找到文章',
|
NO_RESULTS_FOUND: '没有找到文章',
|
||||||
ARTICLE_DETAIL: '文章详情',
|
ARTICLE_DETAIL: '文章详情',
|
||||||
PASSWORD_ERROR: '密码错误!',
|
PASSWORD_ERROR: '密码错误!',
|
||||||
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
||||||
|
ARTICLE_UNLOCK_TIPS: '文章已解锁',
|
||||||
SUBMIT: '提交',
|
SUBMIT: '提交',
|
||||||
POST_TIME: '发布于',
|
POST_TIME: '发布于',
|
||||||
LAST_EDITED_TIME: '最后更新',
|
LAST_EDITED_TIME: '最后更新',
|
||||||
@@ -57,7 +65,8 @@ export default {
|
|||||||
START_READING: '开始阅读',
|
START_READING: '开始阅读',
|
||||||
MINUTE: '分钟',
|
MINUTE: '分钟',
|
||||||
WORD_COUNT: '字数',
|
WORD_COUNT: '字数',
|
||||||
READ_TIME: '阅读时长'
|
READ_TIME: '阅读时长',
|
||||||
|
NEXT_POST: '下一篇'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: '上页',
|
PREV: '上页',
|
||||||
|
|||||||
@@ -5,7 +5,39 @@ export default {
|
|||||||
RSS: '訂閱',
|
RSS: '訂閱',
|
||||||
SEARCH: '搜尋',
|
SEARCH: '搜尋',
|
||||||
ABOUT: '關於',
|
ABOUT: '關於',
|
||||||
MAIL: '電郵'
|
MAIL: '電郵',
|
||||||
|
NAVIGATOR: '導航',
|
||||||
|
ARCHIVE: '封存'
|
||||||
|
},
|
||||||
|
COMMON: {
|
||||||
|
ARTICLE_LIST: '文章列表',
|
||||||
|
MORE: '更多',
|
||||||
|
NO_MORE: '沒有更多了',
|
||||||
|
LATEST_POSTS: '最新文章',
|
||||||
|
TAGS: '標籤',
|
||||||
|
NO_TAG: '無標籤',
|
||||||
|
CATEGORY: '分類',
|
||||||
|
SHARE: '分享',
|
||||||
|
SCAN_QR_CODE: 'QRCode',
|
||||||
|
URL_COPIED: '連結已複製!',
|
||||||
|
TABLE_OF_CONTENTS: '目錄',
|
||||||
|
RELATE_POSTS: '相關文章',
|
||||||
|
COPYRIGHT: '著作權',
|
||||||
|
AUTHOR: '作者',
|
||||||
|
URL: '連結',
|
||||||
|
ANALYTICS: '分析',
|
||||||
|
POSTS: '篇文章',
|
||||||
|
ARTICLE: '文章',
|
||||||
|
VISITORS: '位訪客',
|
||||||
|
VIEWS: '次查看',
|
||||||
|
COPYRIGHT_NOTICE: '本文採用 CC BY-NC-SA 4.0 許可協議,轉載請註明出處。',
|
||||||
|
RESULT_OF_SEARCH: '篇搜尋到的结果',
|
||||||
|
ARTICLE_DETAIL: '完整文章',
|
||||||
|
PASSWORD_ERROR: '密碼錯誤!',
|
||||||
|
ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼',
|
||||||
|
SUBMIT: '提交',
|
||||||
|
POST_TIME: '发布于',
|
||||||
|
LAST_EDITED_TIME: '最后更新'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: '上一頁',
|
PREV: '上一頁',
|
||||||
@@ -13,7 +45,7 @@ export default {
|
|||||||
},
|
},
|
||||||
SEARCH: {
|
SEARCH: {
|
||||||
ARTICLES: '搜尋文章',
|
ARTICLES: '搜尋文章',
|
||||||
TAGS: '搜尋標簽'
|
TAGS: '搜尋標籤'
|
||||||
},
|
},
|
||||||
POST: {
|
POST: {
|
||||||
BACK: '返回',
|
BACK: '返回',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export default {
|
|||||||
ARCHIVE: '封存'
|
ARCHIVE: '封存'
|
||||||
},
|
},
|
||||||
COMMON: {
|
COMMON: {
|
||||||
|
ARTICLE_LIST: '文章列表',
|
||||||
MORE: '更多',
|
MORE: '更多',
|
||||||
NO_MORE: '沒有更多了',
|
NO_MORE: '沒有更多了',
|
||||||
LATEST_POSTS: '最新文章',
|
LATEST_POSTS: '最新文章',
|
||||||
|
|||||||
@@ -1,33 +1,44 @@
|
|||||||
import { uuidToId } from 'notion-utils'
|
import { idToUuid } from 'notion-utils'
|
||||||
import { checkStrIsNotionId, getLastPartOfUrl, isBrowser } from '../utils'
|
import { checkStrIsNotionId, getLastPartOfUrl, isBrowser } from '../utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理页面内连接跳转:
|
* 处理页面内连接跳转:
|
||||||
* 1. 若是本站域名,则在当前窗口打开、不开新窗口
|
* 1.若是本站域名,则在当前窗口打开、不开新窗口
|
||||||
* 2. 若是Notion笔记中的内链,尝试转换成博客中现有的文章地址
|
* 2.url是notion-id,转成站内文章链接
|
||||||
*/
|
*/
|
||||||
export const mapPageUrl = allPages => {
|
export const convertInnerUrl = allPages => {
|
||||||
if (isBrowser) {
|
if (isBrowser) {
|
||||||
|
const allAnchorTags = document
|
||||||
|
?.getElementById('notion-article')
|
||||||
|
?.getElementsByTagName('a')
|
||||||
|
|
||||||
|
if (!allAnchorTags) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const currentURL = window.location.origin + window.location.pathname
|
const currentURL = window.location.origin + window.location.pathname
|
||||||
const allAnchorTags = document.getElementsByTagName('a') // 或者使用 document.querySelectorAll('a') 获取 NodeList
|
// url替换成slug
|
||||||
for (const anchorTag of allAnchorTags) {
|
for (const anchorTag of allAnchorTags) {
|
||||||
// 检查url
|
// 检查url
|
||||||
if (anchorTag?.href) {
|
if (anchorTag?.href) {
|
||||||
// 如果url是一个Notion_id,尝试匹配成博客的文章内链
|
// 如果url是一个Notion_id,尝试匹配成博客的文章内链
|
||||||
const slug = getLastPartOfUrl(anchorTag.href)
|
const slug = getLastPartOfUrl(anchorTag.href)
|
||||||
if (checkStrIsNotionId(slug)) {
|
if (checkStrIsNotionId(slug)) {
|
||||||
const slugPage = allPages?.find(page => uuidToId(page.id) === slug)
|
const slugPage = allPages?.find(page => {
|
||||||
|
return idToUuid(slug).indexOf(page.short_id) === 0
|
||||||
|
})
|
||||||
if (slugPage) {
|
if (slugPage) {
|
||||||
anchorTag.href = slugPage?.href
|
anchorTag.href = slugPage?.href
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 链接在当前页面打开
|
||||||
|
for (const anchorTag of allAnchorTags) {
|
||||||
if (anchorTag?.target === '_blank') {
|
if (anchorTag?.target === '_blank') {
|
||||||
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
|
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
|
||||||
const hrefWithRelativeHash =
|
const hrefWithRelativeHash =
|
||||||
currentURL.split('#')[0] + anchorTag.href.split('#')[1]
|
currentURL.split('#')[0] || '' + anchorTag.href.split('#')[1] || ''
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currentURL === hrefWithoutQueryHash ||
|
currentURL === hrefWithoutQueryHash ||
|
||||||
currentURL === hrefWithRelativeHash
|
currentURL === hrefWithRelativeHash
|
||||||
@@ -4,7 +4,7 @@ import { isIterable } from '../utils'
|
|||||||
* 获取所有文章的标签
|
* 获取所有文章的标签
|
||||||
* @param allPosts
|
* @param allPosts
|
||||||
* @param sliceCount 默认截取数量为12,若为0则返回全部
|
* @param sliceCount 默认截取数量为12,若为0则返回全部
|
||||||
* @param tagOptions tags的下拉选项
|
* @param categoryOptions categories的下拉选项
|
||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,8 +13,14 @@ import { isIterable } from '../utils'
|
|||||||
* @param allPosts
|
* @param allPosts
|
||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
export function getAllCategories({
|
||||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
allPages,
|
||||||
|
categoryOptions,
|
||||||
|
sliceCount = 0
|
||||||
|
}) {
|
||||||
|
const allPosts = allPages?.filter(
|
||||||
|
page => page.type === 'Post' && page.status === 'Published'
|
||||||
|
)
|
||||||
if (!allPosts || !categoryOptions) {
|
if (!allPosts || !categoryOptions) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { siteConfig } from '../config'
|
||||||
import { isIterable } from '../utils'
|
import { isIterable } from '../utils'
|
||||||
import BLOG from '@/blog.config'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有文章的标签
|
* 获取所有文章的标签
|
||||||
@@ -8,8 +8,15 @@ import BLOG from '@/blog.config'
|
|||||||
* @param tagOptions tags的下拉选项
|
* @param tagOptions tags的下拉选项
|
||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
export function getAllTags({
|
||||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
allPages,
|
||||||
|
sliceCount = 0,
|
||||||
|
tagOptions,
|
||||||
|
NOTION_CONFIG
|
||||||
|
}) {
|
||||||
|
const allPosts = allPages?.filter(
|
||||||
|
page => page.type === 'Post' && page.status === 'Published'
|
||||||
|
)
|
||||||
|
|
||||||
if (!allPosts || !tagOptions) {
|
if (!allPosts || !tagOptions) {
|
||||||
return []
|
return []
|
||||||
@@ -27,7 +34,12 @@ export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const list = []
|
const list = []
|
||||||
const { IS_TAG_COLOR_DISTINGUISHED } = BLOG
|
const IS_TAG_COLOR_DISTINGUISHED = siteConfig(
|
||||||
|
'IS_TAG_COLOR_DISTINGUISHED',
|
||||||
|
false,
|
||||||
|
NOTION_CONFIG
|
||||||
|
)
|
||||||
|
const TAG_SORT_BY_COUNT = siteConfig('TAG_SORT_BY_COUNT', true, NOTION_CONFIG)
|
||||||
if (isIterable(tagOptions)) {
|
if (isIterable(tagOptions)) {
|
||||||
if (!IS_TAG_COLOR_DISTINGUISHED) {
|
if (!IS_TAG_COLOR_DISTINGUISHED) {
|
||||||
// 如果不区分颜色, 那么不同颜色相同名称的tag当做同一种tag
|
// 如果不区分颜色, 那么不同颜色相同名称的tag当做同一种tag
|
||||||
@@ -52,7 +64,10 @@ export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 按照数量排序
|
// 按照数量排序
|
||||||
// list.sort((a, b) => b.count - a.count)
|
if (TAG_SORT_BY_COUNT) {
|
||||||
|
list.sort((a, b) => b.count - a.count)
|
||||||
|
}
|
||||||
|
|
||||||
if (sliceCount && sliceCount > 0) {
|
if (sliceCount && sliceCount > 0) {
|
||||||
return list.slice(0, sliceCount)
|
return list.slice(0, sliceCount)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { getDateValue, getTextContent } from 'notion-utils'
|
import { getDateValue, getTextContent } from 'notion-utils'
|
||||||
import { deepClone } from '../utils'
|
import { deepClone } from '../utils'
|
||||||
import getAllPageIds from './getAllPageIds'
|
import getAllPageIds from './getAllPageIds'
|
||||||
import { getPostBlocks } from './getPostBlocks'
|
import { getPage } from './getPostBlocks'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从Notion中读取Config配置表
|
* 从Notion中读取Config配置表
|
||||||
@@ -41,12 +41,12 @@ export async function getConfigMapFromConfigPage(allPages) {
|
|||||||
}
|
}
|
||||||
const configPageId = configPage.id
|
const configPageId = configPage.id
|
||||||
// console.log('[Notion配置]请求配置数据 ', configPage.id)
|
// console.log('[Notion配置]请求配置数据 ', configPage.id)
|
||||||
let pageRecordMap = await getPostBlocks(configPageId, 'config-table')
|
let pageRecordMap = await getPage(configPageId, 'config-table')
|
||||||
// console.log('配置中心Page', configPageId, pageRecordMap)
|
// console.log('配置中心Page', configPageId, pageRecordMap)
|
||||||
let content = pageRecordMap.block[configPageId].value.content
|
let content = pageRecordMap.block[configPageId].value.content
|
||||||
for (const table of ['Config-Table', 'CONFIG-TABLE']) {
|
for (const table of ['Config-Table', 'CONFIG-TABLE']) {
|
||||||
if (content) break
|
if (content) break
|
||||||
pageRecordMap = await getPostBlocks(configPageId, table)
|
pageRecordMap = await getPage(configPageId, table)
|
||||||
content = pageRecordMap.block[configPageId].value.content
|
content = pageRecordMap.block[configPageId].value.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
|
|||||||
import { idToUuid } from 'notion-utils'
|
import { idToUuid } from 'notion-utils'
|
||||||
import { defaultMapImageUrl } from 'react-notion-x'
|
import { defaultMapImageUrl } from 'react-notion-x'
|
||||||
import formatDate from '../utils/formatDate'
|
import formatDate from '../utils/formatDate'
|
||||||
import { getPostBlocks } from './getPostBlocks'
|
import { getPage } from './getPostBlocks'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据页面ID获取内容
|
* 根据页面ID获取内容
|
||||||
@@ -10,7 +10,7 @@ import { getPostBlocks } from './getPostBlocks'
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function getPost(pageId) {
|
export async function getPost(pageId) {
|
||||||
const blockMap = await getPostBlocks(pageId, 'slug')
|
const blockMap = await getPage(pageId, 'slug')
|
||||||
if (!blockMap) {
|
if (!blockMap) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import formatDate from '../utils/formatDate'
|
|||||||
import md5 from 'js-md5'
|
import md5 from 'js-md5'
|
||||||
import { siteConfig } from '../config'
|
import { siteConfig } from '../config'
|
||||||
import {
|
import {
|
||||||
checkContainHttp,
|
checkStartWithHttp,
|
||||||
convertUrlStartWithOneSlash,
|
convertUrlStartWithOneSlash,
|
||||||
sliceUrlFromHttp
|
getLastSegmentFromUrl
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
import { extractLangPrefix } from '../utils/pageId'
|
||||||
import { mapImgUrl } from './mapImage'
|
import { mapImgUrl } from './mapImage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +95,7 @@ export default async function getPageProperties(
|
|||||||
properties.type = properties.type?.[0] || ''
|
properties.type = properties.type?.[0] || ''
|
||||||
properties.status = properties.status?.[0] || ''
|
properties.status = properties.status?.[0] || ''
|
||||||
properties.category = properties.category?.[0] || ''
|
properties.category = properties.category?.[0] || ''
|
||||||
|
properties.comment = properties.comment?.[0] || ''
|
||||||
|
|
||||||
// 映射值:用户个性化type和status字段的下拉框选项,在此映射回代码的英文标识
|
// 映射值:用户个性化type和status字段的下拉框选项,在此映射回代码的英文标识
|
||||||
mapProperties(properties)
|
mapProperties(properties)
|
||||||
@@ -185,26 +187,38 @@ export function adjustPageProperties(properties, NOTION_CONFIG) {
|
|||||||
properties.name = properties.title ?? ''
|
properties.name = properties.title ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开启伪静态路径
|
// http or https 开头的视为外链
|
||||||
if (JSON.parse(NOTION_CONFIG?.PSEUDO_STATIC || BLOG.PSEUDO_STATIC)) {
|
if (checkStartWithHttp(properties?.href)) {
|
||||||
if (
|
properties.href = properties?.slug
|
||||||
!properties?.href?.endsWith('.html') &&
|
|
||||||
!properties?.href?.startsWith('http')
|
|
||||||
) {
|
|
||||||
properties.href += '.html'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查处理外链
|
|
||||||
properties.href = checkContainHttp(properties?.href)
|
|
||||||
? sliceUrlFromHttp(properties?.href)
|
|
||||||
: convertUrlStartWithOneSlash(properties?.href)
|
|
||||||
|
|
||||||
// 设置链接在页内或新页面打开
|
|
||||||
if (properties.href?.indexOf('http') === 0) {
|
|
||||||
properties.target = '_blank'
|
properties.target = '_blank'
|
||||||
} else {
|
} else {
|
||||||
properties.target = '_self'
|
properties.target = '_self'
|
||||||
|
// 伪静态路径右侧拼接.html
|
||||||
|
if (siteConfig('PSEUDO_STATIC', false, NOTION_CONFIG)) {
|
||||||
|
if (
|
||||||
|
!properties?.href?.endsWith('.html') &&
|
||||||
|
properties?.href !== '' &&
|
||||||
|
properties?.href !== '#' &&
|
||||||
|
properties?.href !== '/'
|
||||||
|
) {
|
||||||
|
properties.href += '.html'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 相对路径转绝对路径:url左侧拼接 /
|
||||||
|
properties.href = convertUrlStartWithOneSlash(properties?.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果跳转链接是多语言,则在新窗口打开
|
||||||
|
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
|
||||||
|
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
|
||||||
|
for (let index = 0; index < siteIds.length; index++) {
|
||||||
|
const siteId = siteIds[index]
|
||||||
|
const prefix = extractLangPrefix(siteId)
|
||||||
|
if (getLastSegmentFromUrl(properties.href) === prefix) {
|
||||||
|
properties.target = '_blank'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 密码字段md5
|
// 密码字段md5
|
||||||
@@ -221,6 +235,10 @@ export function adjustPageProperties(properties, NOTION_CONFIG) {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function generateCustomizeSlug(postProperties, NOTION_CONFIG) {
|
function generateCustomizeSlug(postProperties, NOTION_CONFIG) {
|
||||||
|
// 外链不处理
|
||||||
|
if (checkStartWithHttp(postProperties.slug)) {
|
||||||
|
return postProperties.slug
|
||||||
|
}
|
||||||
let fullPrefix = ''
|
let fullPrefix = ''
|
||||||
const allSlugPatterns = siteConfig(
|
const allSlugPatterns = siteConfig(
|
||||||
'POST_URL_PREFIX',
|
'POST_URL_PREFIX',
|
||||||
@@ -272,5 +290,10 @@ function generateCustomizeSlug(postProperties, NOTION_CONFIG) {
|
|||||||
if (fullPrefix.endsWith('/')) {
|
if (fullPrefix.endsWith('/')) {
|
||||||
fullPrefix = fullPrefix.substring(0, fullPrefix.length - 1) // 去掉尾部部的"/"
|
fullPrefix = fullPrefix.substring(0, fullPrefix.length - 1) // 去掉尾部部的"/"
|
||||||
}
|
}
|
||||||
return `${fullPrefix}/${postProperties.slug ?? postProperties.id}`
|
|
||||||
|
if(fullPrefix){
|
||||||
|
return `${fullPrefix}/${postProperties.slug ?? postProperties.id}`
|
||||||
|
}else{
|
||||||
|
return `${postProperties.slug ?? postProperties.id}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { deepClone, delay } from '../utils'
|
|||||||
* @param {*} slice
|
* @param {*} slice
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function getPostBlocks(id, from, slice) {
|
export async function getPage(id, from, slice) {
|
||||||
const cacheKey = 'page_block_' + id
|
const cacheKey = 'page_block_' + id
|
||||||
let pageBlock = await getDataFromCache(cacheKey)
|
let pageBlock = await getDataFromCache(cacheKey)
|
||||||
if (pageBlock) {
|
if (pageBlock) {
|
||||||
@@ -27,28 +27,6 @@ export async function getPostBlocks(id, from, slice) {
|
|||||||
return pageBlock
|
return pageBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 针对在getDataBaseInfoByNotionAPI=>getPostBlocks中获取不到的溢出的block-id,用此方法另外抓取
|
|
||||||
* @param {*} id
|
|
||||||
* @param {*} from
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function getSingleBlock(id, from) {
|
|
||||||
const cacheKey = 'single_block_' + id
|
|
||||||
let pageBlock = await getDataFromCache(cacheKey)
|
|
||||||
if (pageBlock) {
|
|
||||||
// console.log('[API<<--缓存]', `from:${from}`, cacheKey)
|
|
||||||
return pageBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
pageBlock = await getPageWithRetry(id, 'single_' + from)
|
|
||||||
|
|
||||||
if (pageBlock) {
|
|
||||||
await setDataToCache(cacheKey, pageBlock)
|
|
||||||
}
|
|
||||||
return pageBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用接口,失败会重试
|
* 调用接口,失败会重试
|
||||||
* @param {*} id
|
* @param {*} id
|
||||||
@@ -103,50 +81,70 @@ export async function getPageWithRetry(id, from, retryAttempts = 3) {
|
|||||||
function filterPostBlocks(id, blockMap, slice) {
|
function filterPostBlocks(id, blockMap, slice) {
|
||||||
const clonePageBlock = deepClone(blockMap)
|
const clonePageBlock = deepClone(blockMap)
|
||||||
let count = 0
|
let count = 0
|
||||||
|
const blocksToProcess = Object.keys(clonePageBlock?.block || {})
|
||||||
|
|
||||||
// 循环遍历文档的每个block
|
// 循环遍历文档的每个block
|
||||||
for (const i in clonePageBlock?.block) {
|
for (let i = 0; i < blocksToProcess.length; i++) {
|
||||||
const b = clonePageBlock?.block[i]
|
const blockId = blocksToProcess[i]
|
||||||
if (slice && slice > 0 && count > slice) {
|
const b = clonePageBlock?.block[blockId]
|
||||||
delete clonePageBlock?.block[i]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// 当BlockId等于PageId时移除
|
|
||||||
if (b?.value?.id === id) {
|
|
||||||
// 此block含有敏感信息
|
|
||||||
delete b?.value?.properties
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
count++
|
if (slice && slice > 0 && count > slice) {
|
||||||
// 处理 c++、c#、汇编等语言名字映射
|
delete clonePageBlock?.block[blockId]
|
||||||
if (b?.value?.type === 'code') {
|
continue
|
||||||
if (b?.value?.properties?.language?.[0][0] === 'C++') {
|
}
|
||||||
b.value.properties.language[0][0] = 'cpp'
|
|
||||||
}
|
|
||||||
if (b?.value?.properties?.language?.[0][0] === 'C#') {
|
|
||||||
b.value.properties.language[0][0] = 'csharp'
|
|
||||||
}
|
|
||||||
if (b?.value?.properties?.language?.[0][0] === 'Assembly') {
|
|
||||||
b.value.properties.language[0][0] = 'asm6502'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是文件,或嵌入式PDF,需要重新加密签名
|
// 当BlockId等于PageId时移除
|
||||||
if (
|
if (b?.value?.id === id) {
|
||||||
(b?.value?.type === 'file' ||
|
// 此block含有敏感信息
|
||||||
b?.value?.type === 'pdf' ||
|
delete b?.value?.properties
|
||||||
b?.value?.type === 'video' ||
|
continue
|
||||||
b?.value?.type === 'audio') &&
|
}
|
||||||
b?.value?.properties?.source?.[0][0] &&
|
|
||||||
b?.value?.properties?.source?.[0][0].indexOf('amazonaws.com') > 0
|
count++
|
||||||
) {
|
|
||||||
const oldUrl = b?.value?.properties?.source?.[0][0]
|
if (b?.value?.type === 'sync_block' && b?.value?.children) {
|
||||||
const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}`
|
const childBlocks = b.value.children
|
||||||
b.value.properties.source[0][0] = newUrl
|
// 移除同步块
|
||||||
|
delete clonePageBlock.block[blockId]
|
||||||
|
// 用子块替代同步块
|
||||||
|
childBlocks.forEach((childBlock, index) => {
|
||||||
|
const newBlockId = `${blockId}_child_${index}`
|
||||||
|
clonePageBlock.block[newBlockId] = childBlock
|
||||||
|
blocksToProcess.splice(i + index + 1, 0, newBlockId)
|
||||||
|
})
|
||||||
|
// 重新处理新加入的子块
|
||||||
|
i--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 c++、c#、汇编等语言名字映射
|
||||||
|
if (b?.value?.type === 'code') {
|
||||||
|
if (b?.value?.properties?.language?.[0][0] === 'C++') {
|
||||||
|
b.value.properties.language[0][0] = 'cpp'
|
||||||
|
}
|
||||||
|
if (b?.value?.properties?.language?.[0][0] === 'C#') {
|
||||||
|
b.value.properties.language[0][0] = 'csharp'
|
||||||
|
}
|
||||||
|
if (b?.value?.properties?.language?.[0][0] === 'Assembly') {
|
||||||
|
b.value.properties.language[0][0] = 'asm6502'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是文件,或嵌入式PDF,需要重新加密签名
|
||||||
|
if (
|
||||||
|
(b?.value?.type === 'file' ||
|
||||||
|
b?.value?.type === 'pdf' ||
|
||||||
|
b?.value?.type === 'video' ||
|
||||||
|
b?.value?.type === 'audio') &&
|
||||||
|
b?.value?.properties?.source?.[0][0] &&
|
||||||
|
b?.value?.properties?.source?.[0][0].indexOf('amazonaws.com') > 0
|
||||||
|
) {
|
||||||
|
const oldUrl = b?.value?.properties?.source?.[0][0]
|
||||||
|
const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}`
|
||||||
|
b.value.properties.source[0][0] = newUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 去掉不用的字段
|
// 去掉不用的字段
|
||||||
if (id === BLOG.NOTION_PAGE_ID) {
|
if (id === BLOG.NOTION_PAGE_ID) {
|
||||||
return clonePageBlock
|
return clonePageBlock
|
||||||
@@ -162,6 +160,11 @@ function filterPostBlocks(id, blockMap, slice) {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const fetchInBatches = async (ids, batchSize = 100) => {
|
export const fetchInBatches = async (ids, batchSize = 100) => {
|
||||||
|
// 如果 ids 不是数组,则将其转换为数组
|
||||||
|
if (!Array.isArray(ids)) {
|
||||||
|
ids = [ids]
|
||||||
|
}
|
||||||
|
|
||||||
const authToken = BLOG.NOTION_ACCESS_TOKEN || null
|
const authToken = BLOG.NOTION_ACCESS_TOKEN || null
|
||||||
const api = new NotionAPI({
|
const api = new NotionAPI({
|
||||||
authToken,
|
authToken,
|
||||||
@@ -171,7 +174,7 @@ export const fetchInBatches = async (ids, batchSize = 100) => {
|
|||||||
let fetchedBlocks = {}
|
let fetchedBlocks = {}
|
||||||
for (let i = 0; i < ids.length; i += batchSize) {
|
for (let i = 0; i < ids.length; i += batchSize) {
|
||||||
const batch = ids.slice(i, i + batchSize)
|
const batch = ids.slice(i, i + batchSize)
|
||||||
console.log('[API-->>请求] Fetching missing blocks', ids.length)
|
console.log('[API-->>请求] Fetching missing blocks', batch, ids.length)
|
||||||
const start = new Date().getTime()
|
const start = new Date().getTime()
|
||||||
const pageChunk = await api.getBlocks(batch)
|
const pageChunk = await api.getBlocks(batch)
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ const compressImage = (image, width, quality = 50, fmt = 'webp') => {
|
|||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (image.includes(".svg")) return image
|
||||||
|
|
||||||
if (!width || width === 0) {
|
if (!width || width === 0) {
|
||||||
width = siteConfig('IMAGE_COMPRESS_WIDTH')
|
width = siteConfig('IMAGE_COMPRESS_WIDTH')
|
||||||
}
|
}
|
||||||
|
|||||||
37
lib/password.js
Normal file
37
lib/password.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { isBrowser } from './utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认密码
|
||||||
|
* 用户可以通过url中拼接参数,输入密码
|
||||||
|
* 输入过一次的密码会被存储在浏览器中,便于下一次免密访问
|
||||||
|
* 返回的是一组历史密码,便于客户端多次尝试
|
||||||
|
*/
|
||||||
|
export const getPasswordQuery = path => {
|
||||||
|
// 使用 URL 对象解析 URL
|
||||||
|
const url = new URL(path, isBrowser ? window.location.origin : '')
|
||||||
|
|
||||||
|
// 获取查询参数
|
||||||
|
const queryParams = Object.fromEntries(url.searchParams.entries())
|
||||||
|
|
||||||
|
// 请求中带着密码
|
||||||
|
if (queryParams.password) {
|
||||||
|
// 将已输入密码作为默认密码存放在 localStorage,便于下次读取并自动尝试
|
||||||
|
localStorage.setItem('password_default', queryParams.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取路径部分
|
||||||
|
const cleanedPath = url.pathname
|
||||||
|
|
||||||
|
// 从 localStorage 中获取相关密码
|
||||||
|
const storedPassword = localStorage.getItem('password_' + cleanedPath)
|
||||||
|
const defaultPassword = localStorage.getItem('password_default')
|
||||||
|
|
||||||
|
// 将所有密码存储在一个数组中,并过滤掉无效值
|
||||||
|
const passwords = [
|
||||||
|
queryParams.password,
|
||||||
|
storedPassword,
|
||||||
|
defaultPassword
|
||||||
|
].filter(Boolean)
|
||||||
|
|
||||||
|
return passwords
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import algoliasearch from 'algoliasearch'
|
|||||||
* 生成全文索引
|
* 生成全文索引
|
||||||
* @param {*} allPages
|
* @param {*} allPages
|
||||||
*/
|
*/
|
||||||
const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
const generateAlgoliaSearch = async ({ allPages, force = false }) => {
|
||||||
allPages?.forEach(p => {
|
allPages?.forEach(p => {
|
||||||
// 判断这篇文章是否需要重新创建索引
|
// 判断这篇文章是否需要重新创建索引
|
||||||
if (p && !p.password) {
|
if (p && !p.password) {
|
||||||
@@ -19,7 +19,7 @@ const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
|||||||
* 上传数据
|
* 上传数据
|
||||||
* 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引
|
* 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引
|
||||||
*/
|
*/
|
||||||
const uploadDataToAlgolia = async(post) => {
|
const uploadDataToAlgolia = async post => {
|
||||||
// Connect and authenticate with your Algolia app
|
// Connect and authenticate with your Algolia app
|
||||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
||||||
|
|
||||||
@@ -61,14 +61,18 @@ const uploadDataToAlgolia = async(post) => {
|
|||||||
summary: post.summary,
|
summary: post.summary,
|
||||||
lastEditedDate: post.lastEditedDate, // 更新文章时间
|
lastEditedDate: post.lastEditedDate, // 更新文章时间
|
||||||
lastIndexDate: new Date(), // 更新索引时间
|
lastIndexDate: new Date(), // 更新索引时间
|
||||||
content: truncate(getPageContentText(post, post.blockMap), 9000) // 索引9000个字节,因为api限制总请求内容上限1万个字节
|
content: truncate(getPageContentText(post, post.blockMap), 8192) // 索引8192个字符,API限制总请求内容上限1万个字节
|
||||||
}
|
}
|
||||||
// console.log('更新Algolia索引', record)
|
// console.log('更新Algolia索引', record)
|
||||||
index.saveObject(record).wait().then(r => {
|
index
|
||||||
console.log('Algolia索引更新', r)
|
.saveObject(record)
|
||||||
}).catch(err => {
|
.wait()
|
||||||
console.log('Algolia异常', err)
|
.then(r => {
|
||||||
})
|
console.log('Algolia索引更新', r)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log('Algolia异常', err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,12 @@ export const memorize = Component => {
|
|||||||
return memo(MemoizedComponent)
|
return memo(MemoizedComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换外链
|
/**
|
||||||
|
* 诸如 article/https://test.com 等被错误拼接前缀的slug进行处理
|
||||||
|
* 转换为 https://test.com
|
||||||
|
* @param {*} str
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function sliceUrlFromHttp(str) {
|
export function sliceUrlFromHttp(str) {
|
||||||
// 检查字符串是否包含http
|
// 检查字符串是否包含http
|
||||||
if (str?.includes('http:') || str?.includes('https:')) {
|
if (str?.includes('http:') || str?.includes('https:')) {
|
||||||
@@ -81,12 +86,25 @@ export function convertUrlStartWithOneSlash(str) {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是一个相对或绝对路径的ur类
|
||||||
|
* @param {*} str
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isUrl(str) {
|
||||||
|
if (!str) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return str?.indexOf('/') === 0 || checkStartWithHttp(str)
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否外链
|
// 检查是否外链
|
||||||
export function checkContainHttp(str) {
|
export function checkStartWithHttp(str) {
|
||||||
// 检查字符串是否包含http
|
// 检查字符串是否包含http
|
||||||
if (str?.includes('http:') || str?.includes('https:')) {
|
if (str?.indexOf('http:') === 0 || str?.indexOf('https:') === 0) {
|
||||||
// 如果包含,找到http的位置
|
// 如果包含,找到http的位置
|
||||||
return str?.indexOf('http') > -1
|
return true
|
||||||
} else {
|
} else {
|
||||||
// 不包含
|
// 不包含
|
||||||
return false
|
return false
|
||||||
@@ -354,3 +372,19 @@ export const scanAndConvertToLinks = node => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取url最后一个斜杆后面的内容
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getLastSegmentFromUrl(url) {
|
||||||
|
if (!url) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
// 去掉 URL 中的查询参数部分
|
||||||
|
const trimmedUrl = url.split('?')[0]
|
||||||
|
// 获取最后一个斜杠后面的内容
|
||||||
|
const segments = trimmedUrl.split('/')
|
||||||
|
return segments[segments.length - 1]
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,4 +30,18 @@ function extractLangId(str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { extractLangPrefix, extractLangId }
|
/**
|
||||||
|
* 列表中用过来区分page只需要端的id足够
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getShortId(uuid) {
|
||||||
|
if (!uuid || uuid.indexOf('-') < 0) {
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
// 找到第一个 '-' 的位置
|
||||||
|
const index = uuid.indexOf('-')
|
||||||
|
// 截取从开始到第一个 '-' 之前的部分
|
||||||
|
return uuid.substring(0, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { extractLangPrefix, extractLangId, getShortId }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 文章相关工具
|
* 文章相关工具
|
||||||
*/
|
*/
|
||||||
import { checkContainHttp } from '.'
|
import { checkStartWithHttp } from '.'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文章的关联推荐文章列表,目前根据标签关联性筛选
|
* 获取文章的关联推荐文章列表,目前根据标签关联性筛选
|
||||||
@@ -50,7 +50,7 @@ export function checkSlugHasNoSlash(row) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
(slug.match(/\//g) || []).length === 0 &&
|
(slug.match(/\//g) || []).length === 0 &&
|
||||||
!checkContainHttp(slug) &&
|
!checkStartWithHttp(slug) &&
|
||||||
row.type.indexOf('Menu') < 0
|
row.type.indexOf('Menu') < 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ export function checkSlugHasOneSlash(row) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
(slug.match(/\//g) || []).length === 1 &&
|
(slug.match(/\//g) || []).length === 1 &&
|
||||||
!checkContainHttp(slug) &&
|
!checkStartWithHttp(slug) &&
|
||||||
row.type.indexOf('Menu') < 0
|
row.type.indexOf('Menu') < 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,6 @@ export function checkSlugHasMorThanTwoSlash(row) {
|
|||||||
return (
|
return (
|
||||||
(slug.match(/\//g) || []).length >= 2 &&
|
(slug.match(/\//g) || []).length >= 2 &&
|
||||||
row.type.indexOf('Menu') < 0 &&
|
row.type.indexOf('Menu') < 0 &&
|
||||||
!checkContainHttp(slug)
|
!checkStartWithHttp(slug)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
182
next.config.js
182
next.config.js
@@ -32,6 +32,16 @@ const locales = (function () {
|
|||||||
return langs
|
return langs
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
// 编译前执行
|
||||||
|
// const preBuild = (function () {
|
||||||
|
// // 删除 public/sitemap.xml 文件 ; 否则会和/pages/sitemap.xml.js 冲突。
|
||||||
|
// const sitemapPath = path.resolve(__dirname, 'public', 'sitemap.xml')
|
||||||
|
// if (fs.existsSync(sitemapPath)) {
|
||||||
|
// fs.unlinkSync(sitemapPath)
|
||||||
|
// console.log('Deleted existing sitemap.xml from public directory')
|
||||||
|
// }
|
||||||
|
// })()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描指定目录下的文件夹名,用于获取所有主题
|
* 扫描指定目录下的文件夹名,用于获取所有主题
|
||||||
* @param {*} directory
|
* @param {*} directory
|
||||||
@@ -53,7 +63,20 @@ function scanSubdirectories(directory) {
|
|||||||
return subdirectories
|
return subdirectories
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('next').NextConfig}
|
||||||
|
*/
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
|
output: process.env.EXPORT ? 'export' : undefined,
|
||||||
|
// 多语言, 在export时禁用
|
||||||
|
i18n: process.env.EXPORT
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
defaultLocale: BLOG.LANG.slice(0, 2),
|
||||||
|
// 支持的所有多语言,按需填写即可
|
||||||
|
locales
|
||||||
|
},
|
||||||
images: {
|
images: {
|
||||||
// 图片压缩
|
// 图片压缩
|
||||||
formats: ['image/avif', 'image/webp'],
|
formats: ['image/avif', 'image/webp'],
|
||||||
@@ -71,90 +94,87 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 默认将feed重定向至 /public/rss/feed.xml
|
// 默认将feed重定向至 /public/rss/feed.xml
|
||||||
async redirects() {
|
redirects: process.env.EXPORT
|
||||||
return [
|
? undefined
|
||||||
{
|
: async () => {
|
||||||
source: '/feed',
|
return [
|
||||||
destination: '/rss/feed.xml',
|
|
||||||
permanent: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 多语言, 在export时禁用
|
|
||||||
i18n:
|
|
||||||
process.env.npm_lifecycle_event === 'export'
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
defaultLocale: BLOG.LANG.slice(0, 2),
|
|
||||||
// 支持的所有多语言,按需填写即可
|
|
||||||
locales
|
|
||||||
},
|
|
||||||
// 重写url
|
|
||||||
async rewrites() {
|
|
||||||
// 处理多语言重定向
|
|
||||||
const langsRewrites = []
|
|
||||||
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
|
|
||||||
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
|
|
||||||
const langs = []
|
|
||||||
for (let index = 0; index < siteIds.length; index++) {
|
|
||||||
const siteId = siteIds[index]
|
|
||||||
const prefix = extractLangPrefix(siteId)
|
|
||||||
// 如果包含前缀 例如 zh , en 等
|
|
||||||
if (prefix) {
|
|
||||||
langs.push(prefix)
|
|
||||||
}
|
|
||||||
console.log('[Locales]', siteId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 映射多语言
|
|
||||||
// 示例: source: '/:locale(zh|en)/:path*' ; :locale() 会将语言放入重写后的 `?locale=` 中。
|
|
||||||
langsRewrites.push(
|
|
||||||
{
|
|
||||||
source: `/:locale(${langs.join('|')})/:path*`,
|
|
||||||
destination: '/:path*'
|
|
||||||
},
|
|
||||||
// 匹配没有路径的情况,例如 [domain]/zh 或 [domain]/en
|
|
||||||
{
|
|
||||||
source: `/:locale(${langs.join('|')})`,
|
|
||||||
destination: '/'
|
|
||||||
},
|
|
||||||
// 匹配没有路径的情况,例如 [domain]/zh/ 或 [domain]/en/
|
|
||||||
{
|
|
||||||
source: `/:locale(${langs.join('|')})/`,
|
|
||||||
destination: '/'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
...langsRewrites,
|
|
||||||
// 伪静态重写
|
|
||||||
{
|
|
||||||
source: '/:path*.html',
|
|
||||||
destination: '/:path*'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
async headers() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: '/:path*{/}?',
|
|
||||||
headers: [
|
|
||||||
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
|
|
||||||
{ key: 'Access-Control-Allow-Origin', value: '*' },
|
|
||||||
{
|
{
|
||||||
key: 'Access-Control-Allow-Methods',
|
source: '/feed',
|
||||||
value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT'
|
destination: '/rss/feed.xml',
|
||||||
},
|
permanent: true
|
||||||
{
|
|
||||||
key: 'Access-Control-Allow-Headers',
|
|
||||||
value:
|
|
||||||
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
// 重写url
|
||||||
},
|
rewrites: process.env.EXPORT
|
||||||
|
? undefined
|
||||||
|
: async () => {
|
||||||
|
// 处理多语言重定向
|
||||||
|
const langsRewrites = []
|
||||||
|
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
|
||||||
|
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
|
||||||
|
const langs = []
|
||||||
|
for (let index = 0; index < siteIds.length; index++) {
|
||||||
|
const siteId = siteIds[index]
|
||||||
|
const prefix = extractLangPrefix(siteId)
|
||||||
|
// 如果包含前缀 例如 zh , en 等
|
||||||
|
if (prefix) {
|
||||||
|
langs.push(prefix)
|
||||||
|
}
|
||||||
|
console.log('[Locales]', siteId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射多语言
|
||||||
|
// 示例: source: '/:locale(zh|en)/:path*' ; :locale() 会将语言放入重写后的 `?locale=` 中。
|
||||||
|
langsRewrites.push(
|
||||||
|
{
|
||||||
|
source: `/:locale(${langs.join('|')})/:path*`,
|
||||||
|
destination: '/:path*'
|
||||||
|
},
|
||||||
|
// 匹配没有路径的情况,例如 [domain]/zh 或 [domain]/en
|
||||||
|
{
|
||||||
|
source: `/:locale(${langs.join('|')})`,
|
||||||
|
destination: '/'
|
||||||
|
},
|
||||||
|
// 匹配没有路径的情况,例如 [domain]/zh/ 或 [domain]/en/
|
||||||
|
{
|
||||||
|
source: `/:locale(${langs.join('|')})/`,
|
||||||
|
destination: '/'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...langsRewrites,
|
||||||
|
// 伪静态重写
|
||||||
|
{
|
||||||
|
source: '/:path*.html',
|
||||||
|
destination: '/:path*'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
headers: process.env.EXPORT
|
||||||
|
? undefined
|
||||||
|
: async () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '/:path*{/}?',
|
||||||
|
headers: [
|
||||||
|
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
|
||||||
|
{ key: 'Access-Control-Allow-Origin', value: '*' },
|
||||||
|
{
|
||||||
|
key: 'Access-Control-Allow-Methods',
|
||||||
|
value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Access-Control-Allow-Headers',
|
||||||
|
value:
|
||||||
|
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
webpack: (config, { dev, isServer }) => {
|
webpack: (config, { dev, isServer }) => {
|
||||||
// 动态主题:添加 resolve.alias 配置,将动态路径映射到实际路径
|
// 动态主题:添加 resolve.alias 配置,将动态路径映射到实际路径
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "notion-next",
|
"name": "notion-next",
|
||||||
"version": "4.5.3",
|
"version": "4.6.1",
|
||||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -17,8 +17,9 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"post-build": "next-sitemap --config next-sitemap.config.js",
|
"post-build": "next-sitemap --config next-sitemap.config.js",
|
||||||
"export": "next build && next-sitemap --config next-sitemap.config.js && next export",
|
"export": "cross-env EXPORT=true next build && next-sitemap --config next-sitemap.config.js",
|
||||||
"bundle-report": "cross-env ANALYZE=true yarn build"
|
"bundle-report": "cross-env ANALYZE=true next build",
|
||||||
|
"build-all-in-dev": "cross-env VERCEL_ENV=production next build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.7.15",
|
"@headlessui/react": "^1.7.15",
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
"js-md5": "^0.7.3",
|
"js-md5": "^0.7.3",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"next": "13.3.1",
|
"next": "14.2.4",
|
||||||
"notion-client": "6.15.6",
|
"notion-client": "6.15.6",
|
||||||
"notion-utils": "6.15.6",
|
"notion-utils": "6.15.6",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
const from = 'slug-paths'
|
const from = 'slug-paths'
|
||||||
const { allPages } = await getGlobalData({ from })
|
const { allPages } = await getGlobalData({ from })
|
||||||
|
const paths = allPages
|
||||||
|
?.filter(row => checkSlugHasMorThanTwoSlash(row))
|
||||||
|
.map(row => ({
|
||||||
|
params: {
|
||||||
|
prefix: row.slug.split('/')[0],
|
||||||
|
slug: row.slug.split('/')[1],
|
||||||
|
suffix: row.slug.split('/').slice(2)
|
||||||
|
}
|
||||||
|
}))
|
||||||
return {
|
return {
|
||||||
paths: allPages
|
paths: paths,
|
||||||
?.filter(row => checkSlugHasMorThanTwoSlash(row))
|
|
||||||
.map(row => ({
|
|
||||||
params: {
|
|
||||||
prefix: row.slug.split('/')[0],
|
|
||||||
slug: row.slug.split('/')[1],
|
|
||||||
suffix: row.slug.split('/').slice(1)
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
fallback: true
|
fallback: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,16 +83,18 @@ export async function getStaticProps({
|
|||||||
props.post = null
|
props.post = null
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文章内容加载
|
// 文章内容加载
|
||||||
if (!props?.posts?.blockMap) {
|
if (!props?.post?.blockMap) {
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
||||||
}
|
}
|
||||||
// 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
// 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
||||||
@@ -122,11 +124,13 @@ export async function getStaticProps({
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,16 +72,18 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) {
|
|||||||
props.post = null
|
props.post = null
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文章内容加载
|
// 文章内容加载
|
||||||
if (!props?.posts?.blockMap) {
|
if (!props?.post?.blockMap) {
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
||||||
}
|
}
|
||||||
// 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
// 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
||||||
@@ -111,11 +113,13 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
import useNotification from '@/components/Notification'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData'
|
import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents'
|
import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents'
|
||||||
|
import { getPasswordQuery } from '@/lib/password'
|
||||||
import { uploadDataToAlgolia } from '@/lib/plugins/algolia'
|
import { uploadDataToAlgolia } from '@/lib/plugins/algolia'
|
||||||
import { checkSlugHasNoSlash, getRecommendPost } from '@/lib/utils/post'
|
import { checkSlugHasNoSlash, getRecommendPost } from '@/lib/utils/post'
|
||||||
import { getLayoutByTheme } from '@/themes/theme'
|
import { getLayoutByTheme } from '@/themes/theme'
|
||||||
@@ -19,9 +22,11 @@ import { useEffect, useState } from 'react'
|
|||||||
const Slug = props => {
|
const Slug = props => {
|
||||||
const { post } = props
|
const { post } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
|
||||||
// 文章锁🔐
|
// 文章锁🔐
|
||||||
const [lock, setLock] = useState(post?.password && post?.password !== '')
|
const [lock, setLock] = useState(post?.password && post?.password !== '')
|
||||||
|
const { showNotification, Notification } = useNotification()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证文章密码
|
* 验证文章密码
|
||||||
@@ -36,6 +41,7 @@ const Slug = props => {
|
|||||||
setLock(false)
|
setLock(false)
|
||||||
// 输入密码存入localStorage,下次自动提交
|
// 输入密码存入localStorage,下次自动提交
|
||||||
localStorage.setItem('password_' + router.asPath, passInput)
|
localStorage.setItem('password_' + router.asPath, passInput)
|
||||||
|
showNotification(locale.COMMON.ARTICLE_UNLOCK_TIPS) // 设置解锁成功提示显示
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -56,10 +62,14 @@ const Slug = props => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从localStorage中读取上次记录 自动提交密码
|
// 读取上次记录 自动提交密码
|
||||||
const passInput = localStorage.getItem('password_' + router.asPath)
|
const passInputs = getPasswordQuery(router.asPath)
|
||||||
if (passInput) {
|
if (passInputs.length > 0) {
|
||||||
validPassword(passInput)
|
for (const passInput of passInputs) {
|
||||||
|
if (validPassword(passInput)) {
|
||||||
|
break // 密码验证成功,停止尝试
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
@@ -83,7 +93,12 @@ const Slug = props => {
|
|||||||
theme: siteConfig('THEME'),
|
theme: siteConfig('THEME'),
|
||||||
router: useRouter()
|
router: useRouter()
|
||||||
})
|
})
|
||||||
return <Layout {...props} />
|
return (
|
||||||
|
<>
|
||||||
|
<Layout {...props} />
|
||||||
|
{post?.password && post?.password !== '' && !lock && <Notification />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
@@ -109,7 +124,7 @@ export async function getStaticProps({ params: { prefix }, locale }) {
|
|||||||
let fullSlug = prefix
|
let fullSlug = prefix
|
||||||
const from = `slug-props-${fullSlug}`
|
const from = `slug-props-${fullSlug}`
|
||||||
const props = await getGlobalData({ from, locale })
|
const props = await getGlobalData({ from, locale })
|
||||||
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
|
if (siteConfig('PSEUDO_STATIC', false, props.NOTION_CONFIG)) {
|
||||||
if (!fullSlug.endsWith('.html')) {
|
if (!fullSlug.endsWith('.html')) {
|
||||||
fullSlug += '.html'
|
fullSlug += '.html'
|
||||||
}
|
}
|
||||||
@@ -119,7 +134,7 @@ export async function getStaticProps({ params: { prefix }, locale }) {
|
|||||||
props.post = props?.allPages?.find(p => {
|
props.post = props?.allPages?.find(p => {
|
||||||
return (
|
return (
|
||||||
p.type.indexOf('Menu') < 0 &&
|
p.type.indexOf('Menu') < 0 &&
|
||||||
(p.slug === fullSlug || p.id === idToUuid(fullSlug))
|
(p.slug === prefix || p.id === idToUuid(prefix))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -136,16 +151,18 @@ export async function getStaticProps({ params: { prefix }, locale }) {
|
|||||||
props.post = null
|
props.post = null
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文章内容加载
|
// 文章内容加载
|
||||||
if (!props?.posts?.blockMap) {
|
if (!props?.post?.blockMap) {
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,11 +193,13 @@ export async function getStaticProps({ params: { prefix }, locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,11 +61,13 @@ export async function getStaticProps({ locale }) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,13 @@ export async function getStaticProps({ params: { category }, locale }) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,13 @@ export async function getStaticProps({ params: { category, page } }) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ export async function getStaticPaths() {
|
|||||||
// 处理文章页数
|
// 处理文章页数
|
||||||
const postCount = categoryPosts.length
|
const postCount = categoryPosts.length
|
||||||
const totalPages = Math.ceil(
|
const totalPages = Math.ceil(
|
||||||
postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
)
|
)
|
||||||
if (totalPages > 1) {
|
if (totalPages > 1) {
|
||||||
for (let i = 1; i <= totalPages; i++) {
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ export async function getStaticProps({ locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,11 +69,13 @@ export async function getStaticProps(req) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function getStaticPaths({ locale }) {
|
|||||||
const from = 'page-paths'
|
const from = 'page-paths'
|
||||||
const { postCount, NOTION_CONFIG } = await getGlobalData({ from, locale })
|
const { postCount, NOTION_CONFIG } = await getGlobalData({ from, locale })
|
||||||
const totalPages = Math.ceil(
|
const totalPages = Math.ceil(
|
||||||
postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
// remove first page, we 're not gonna handle that.
|
// remove first page, we 're not gonna handle that.
|
||||||
@@ -69,11 +69,13 @@ export async function getStaticProps({ params: { page } }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,11 +47,13 @@ export async function getStaticProps({ params: { keyword }, locale }) {
|
|||||||
props.keyword = keyword
|
props.keyword = keyword
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,11 +45,13 @@ export async function getStaticProps({ params: { keyword, page }, locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ export async function getStaticProps({ locale }) {
|
|||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ export async function getStaticProps(req) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ export async function getStaticProps(req) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export const getServerSideProps = async ctx => {
|
|||||||
'Cache-Control',
|
'Cache-Control',
|
||||||
'public, max-age=3600, stale-while-revalidate=59'
|
'public, max-age=3600, stale-while-revalidate=59'
|
||||||
)
|
)
|
||||||
console.log('fff', fields)
|
|
||||||
return getServerSideSitemap(ctx, fields)
|
return getServerSideSitemap(ctx, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ function generateLocalesSitemap(link, allPages, locale) {
|
|||||||
priority: '0.7'
|
priority: '0.7'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loc: `${link}${locale}/feed`,
|
loc: `${link}${locale}/rss/feed.xml`,
|
||||||
lastmod: new Date().toISOString().split('T')[0],
|
lastmod: new Date().toISOString().split('T')[0],
|
||||||
changefreq: 'daily',
|
changefreq: 'daily',
|
||||||
priority: '0.7'
|
priority: '0.7'
|
||||||
|
|||||||
@@ -45,11 +45,13 @@ export async function getStaticProps({ params: { tag }, locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,13 @@ export async function getStaticProps({ params: { tag, page }, locale }) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ export async function getStaticPaths() {
|
|||||||
// 处理文章页数
|
// 处理文章页数
|
||||||
const postCount = tagPosts.length
|
const postCount = tagPosts.length
|
||||||
const totalPages = Math.ceil(
|
const totalPages = Math.ceil(
|
||||||
postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
)
|
)
|
||||||
if (totalPages > 1) {
|
if (totalPages > 1) {
|
||||||
for (let i = 1; i <= totalPages; i++) {
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ export async function getStaticProps(req) {
|
|||||||
delete props.allPages
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: siteConfig(
|
revalidate: process.env.EXPORT
|
||||||
'NEXT_REVALIDATE_SECOND',
|
? undefined
|
||||||
BLOG.NEXT_REVALIDATE_SECOND,
|
: siteConfig(
|
||||||
props.NOTION_CONFIG
|
'NEXT_REVALIDATE_SECOND',
|
||||||
)
|
BLOG.NEXT_REVALIDATE_SECOND,
|
||||||
|
props.NOTION_CONFIG
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
916
public/css/aos.css
Normal file
916
public/css/aos.css
Normal file
@@ -0,0 +1,916 @@
|
|||||||
|
[data-aos][data-aos][data-aos-duration='50'],
|
||||||
|
body[data-aos-duration='50'] [data-aos] {
|
||||||
|
transition-duration: 50ms;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='50'],
|
||||||
|
body[data-aos-delay='50'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='50'].aos-animate,
|
||||||
|
body[data-aos-delay='50'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 50ms;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='100'],
|
||||||
|
body[data-aos-duration='100'] [data-aos] {
|
||||||
|
transition-duration: 0.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='100'],
|
||||||
|
body[data-aos-delay='100'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='100'].aos-animate,
|
||||||
|
body[data-aos-delay='100'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='150'],
|
||||||
|
body[data-aos-duration='150'] [data-aos] {
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='150'],
|
||||||
|
body[data-aos-delay='150'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='150'].aos-animate,
|
||||||
|
body[data-aos-delay='150'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='200'],
|
||||||
|
body[data-aos-duration='200'] [data-aos] {
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='200'],
|
||||||
|
body[data-aos-delay='200'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='200'].aos-animate,
|
||||||
|
body[data-aos-delay='200'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='250'],
|
||||||
|
body[data-aos-duration='250'] [data-aos] {
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='250'],
|
||||||
|
body[data-aos-delay='250'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='250'].aos-animate,
|
||||||
|
body[data-aos-delay='250'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='300'],
|
||||||
|
body[data-aos-duration='300'] [data-aos] {
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='300'],
|
||||||
|
body[data-aos-delay='300'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='300'].aos-animate,
|
||||||
|
body[data-aos-delay='300'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='350'],
|
||||||
|
body[data-aos-duration='350'] [data-aos] {
|
||||||
|
transition-duration: 0.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='350'],
|
||||||
|
body[data-aos-delay='350'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='350'].aos-animate,
|
||||||
|
body[data-aos-delay='350'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='400'],
|
||||||
|
body[data-aos-duration='400'] [data-aos] {
|
||||||
|
transition-duration: 0.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='400'],
|
||||||
|
body[data-aos-delay='400'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='400'].aos-animate,
|
||||||
|
body[data-aos-delay='400'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='450'],
|
||||||
|
body[data-aos-duration='450'] [data-aos] {
|
||||||
|
transition-duration: 0.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='450'],
|
||||||
|
body[data-aos-delay='450'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='450'].aos-animate,
|
||||||
|
body[data-aos-delay='450'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='500'],
|
||||||
|
body[data-aos-duration='500'] [data-aos] {
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='500'],
|
||||||
|
body[data-aos-delay='500'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='500'].aos-animate,
|
||||||
|
body[data-aos-delay='500'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='550'],
|
||||||
|
body[data-aos-duration='550'] [data-aos] {
|
||||||
|
transition-duration: 0.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='550'],
|
||||||
|
body[data-aos-delay='550'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='550'].aos-animate,
|
||||||
|
body[data-aos-delay='550'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='600'],
|
||||||
|
body[data-aos-duration='600'] [data-aos] {
|
||||||
|
transition-duration: 0.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='600'],
|
||||||
|
body[data-aos-delay='600'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='600'].aos-animate,
|
||||||
|
body[data-aos-delay='600'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='650'],
|
||||||
|
body[data-aos-duration='650'] [data-aos] {
|
||||||
|
transition-duration: 0.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='650'],
|
||||||
|
body[data-aos-delay='650'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='650'].aos-animate,
|
||||||
|
body[data-aos-delay='650'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='700'],
|
||||||
|
body[data-aos-duration='700'] [data-aos] {
|
||||||
|
transition-duration: 0.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='700'],
|
||||||
|
body[data-aos-delay='700'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='700'].aos-animate,
|
||||||
|
body[data-aos-delay='700'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='750'],
|
||||||
|
body[data-aos-duration='750'] [data-aos] {
|
||||||
|
transition-duration: 0.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='750'],
|
||||||
|
body[data-aos-delay='750'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='750'].aos-animate,
|
||||||
|
body[data-aos-delay='750'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='800'],
|
||||||
|
body[data-aos-duration='800'] [data-aos] {
|
||||||
|
transition-duration: 0.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='800'],
|
||||||
|
body[data-aos-delay='800'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='800'].aos-animate,
|
||||||
|
body[data-aos-delay='800'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='850'],
|
||||||
|
body[data-aos-duration='850'] [data-aos] {
|
||||||
|
transition-duration: 0.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='850'],
|
||||||
|
body[data-aos-delay='850'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='850'].aos-animate,
|
||||||
|
body[data-aos-delay='850'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='900'],
|
||||||
|
body[data-aos-duration='900'] [data-aos] {
|
||||||
|
transition-duration: 0.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='900'],
|
||||||
|
body[data-aos-delay='900'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='900'].aos-animate,
|
||||||
|
body[data-aos-delay='900'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='950'],
|
||||||
|
body[data-aos-duration='950'] [data-aos] {
|
||||||
|
transition-duration: 0.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='950'],
|
||||||
|
body[data-aos-delay='950'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='950'].aos-animate,
|
||||||
|
body[data-aos-delay='950'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 0.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1000'],
|
||||||
|
body[data-aos-duration='1000'] [data-aos] {
|
||||||
|
transition-duration: 1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1000'],
|
||||||
|
body[data-aos-delay='1000'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1000'].aos-animate,
|
||||||
|
body[data-aos-delay='1000'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1050'],
|
||||||
|
body[data-aos-duration='1050'] [data-aos] {
|
||||||
|
transition-duration: 1.05s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1050'],
|
||||||
|
body[data-aos-delay='1050'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1050'].aos-animate,
|
||||||
|
body[data-aos-delay='1050'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.05s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1100'],
|
||||||
|
body[data-aos-duration='1100'] [data-aos] {
|
||||||
|
transition-duration: 1.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1100'],
|
||||||
|
body[data-aos-delay='1100'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1100'].aos-animate,
|
||||||
|
body[data-aos-delay='1100'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1150'],
|
||||||
|
body[data-aos-duration='1150'] [data-aos] {
|
||||||
|
transition-duration: 1.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1150'],
|
||||||
|
body[data-aos-delay='1150'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1150'].aos-animate,
|
||||||
|
body[data-aos-delay='1150'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1200'],
|
||||||
|
body[data-aos-duration='1200'] [data-aos] {
|
||||||
|
transition-duration: 1.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1200'],
|
||||||
|
body[data-aos-delay='1200'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1200'].aos-animate,
|
||||||
|
body[data-aos-delay='1200'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1250'],
|
||||||
|
body[data-aos-duration='1250'] [data-aos] {
|
||||||
|
transition-duration: 1.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1250'],
|
||||||
|
body[data-aos-delay='1250'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1250'].aos-animate,
|
||||||
|
body[data-aos-delay='1250'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1300'],
|
||||||
|
body[data-aos-duration='1300'] [data-aos] {
|
||||||
|
transition-duration: 1.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1300'],
|
||||||
|
body[data-aos-delay='1300'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1300'].aos-animate,
|
||||||
|
body[data-aos-delay='1300'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1350'],
|
||||||
|
body[data-aos-duration='1350'] [data-aos] {
|
||||||
|
transition-duration: 1.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1350'],
|
||||||
|
body[data-aos-delay='1350'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1350'].aos-animate,
|
||||||
|
body[data-aos-delay='1350'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1400'],
|
||||||
|
body[data-aos-duration='1400'] [data-aos] {
|
||||||
|
transition-duration: 1.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1400'],
|
||||||
|
body[data-aos-delay='1400'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1400'].aos-animate,
|
||||||
|
body[data-aos-delay='1400'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1450'],
|
||||||
|
body[data-aos-duration='1450'] [data-aos] {
|
||||||
|
transition-duration: 1.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1450'],
|
||||||
|
body[data-aos-delay='1450'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1450'].aos-animate,
|
||||||
|
body[data-aos-delay='1450'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1500'],
|
||||||
|
body[data-aos-duration='1500'] [data-aos] {
|
||||||
|
transition-duration: 1.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1500'],
|
||||||
|
body[data-aos-delay='1500'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1500'].aos-animate,
|
||||||
|
body[data-aos-delay='1500'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1550'],
|
||||||
|
body[data-aos-duration='1550'] [data-aos] {
|
||||||
|
transition-duration: 1.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1550'],
|
||||||
|
body[data-aos-delay='1550'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1550'].aos-animate,
|
||||||
|
body[data-aos-delay='1550'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1600'],
|
||||||
|
body[data-aos-duration='1600'] [data-aos] {
|
||||||
|
transition-duration: 1.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1600'],
|
||||||
|
body[data-aos-delay='1600'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1600'].aos-animate,
|
||||||
|
body[data-aos-delay='1600'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1650'],
|
||||||
|
body[data-aos-duration='1650'] [data-aos] {
|
||||||
|
transition-duration: 1.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1650'],
|
||||||
|
body[data-aos-delay='1650'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1650'].aos-animate,
|
||||||
|
body[data-aos-delay='1650'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1700'],
|
||||||
|
body[data-aos-duration='1700'] [data-aos] {
|
||||||
|
transition-duration: 1.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1700'],
|
||||||
|
body[data-aos-delay='1700'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1700'].aos-animate,
|
||||||
|
body[data-aos-delay='1700'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1750'],
|
||||||
|
body[data-aos-duration='1750'] [data-aos] {
|
||||||
|
transition-duration: 1.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1750'],
|
||||||
|
body[data-aos-delay='1750'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1750'].aos-animate,
|
||||||
|
body[data-aos-delay='1750'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1800'],
|
||||||
|
body[data-aos-duration='1800'] [data-aos] {
|
||||||
|
transition-duration: 1.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1800'],
|
||||||
|
body[data-aos-delay='1800'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1800'].aos-animate,
|
||||||
|
body[data-aos-delay='1800'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1850'],
|
||||||
|
body[data-aos-duration='1850'] [data-aos] {
|
||||||
|
transition-duration: 1.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1850'],
|
||||||
|
body[data-aos-delay='1850'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1850'].aos-animate,
|
||||||
|
body[data-aos-delay='1850'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1900'],
|
||||||
|
body[data-aos-duration='1900'] [data-aos] {
|
||||||
|
transition-duration: 1.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1900'],
|
||||||
|
body[data-aos-delay='1900'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1900'].aos-animate,
|
||||||
|
body[data-aos-delay='1900'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='1950'],
|
||||||
|
body[data-aos-duration='1950'] [data-aos] {
|
||||||
|
transition-duration: 1.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1950'],
|
||||||
|
body[data-aos-delay='1950'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='1950'].aos-animate,
|
||||||
|
body[data-aos-delay='1950'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 1.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2000'],
|
||||||
|
body[data-aos-duration='2000'] [data-aos] {
|
||||||
|
transition-duration: 2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2000'],
|
||||||
|
body[data-aos-delay='2000'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2000'].aos-animate,
|
||||||
|
body[data-aos-delay='2000'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2050'],
|
||||||
|
body[data-aos-duration='2050'] [data-aos] {
|
||||||
|
transition-duration: 2.05s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2050'],
|
||||||
|
body[data-aos-delay='2050'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2050'].aos-animate,
|
||||||
|
body[data-aos-delay='2050'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.05s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2100'],
|
||||||
|
body[data-aos-duration='2100'] [data-aos] {
|
||||||
|
transition-duration: 2.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2100'],
|
||||||
|
body[data-aos-delay='2100'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2100'].aos-animate,
|
||||||
|
body[data-aos-delay='2100'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.1s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2150'],
|
||||||
|
body[data-aos-duration='2150'] [data-aos] {
|
||||||
|
transition-duration: 2.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2150'],
|
||||||
|
body[data-aos-delay='2150'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2150'].aos-animate,
|
||||||
|
body[data-aos-delay='2150'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.15s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2200'],
|
||||||
|
body[data-aos-duration='2200'] [data-aos] {
|
||||||
|
transition-duration: 2.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2200'],
|
||||||
|
body[data-aos-delay='2200'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2200'].aos-animate,
|
||||||
|
body[data-aos-delay='2200'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.2s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2250'],
|
||||||
|
body[data-aos-duration='2250'] [data-aos] {
|
||||||
|
transition-duration: 2.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2250'],
|
||||||
|
body[data-aos-delay='2250'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2250'].aos-animate,
|
||||||
|
body[data-aos-delay='2250'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.25s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2300'],
|
||||||
|
body[data-aos-duration='2300'] [data-aos] {
|
||||||
|
transition-duration: 2.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2300'],
|
||||||
|
body[data-aos-delay='2300'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2300'].aos-animate,
|
||||||
|
body[data-aos-delay='2300'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2350'],
|
||||||
|
body[data-aos-duration='2350'] [data-aos] {
|
||||||
|
transition-duration: 2.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2350'],
|
||||||
|
body[data-aos-delay='2350'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2350'].aos-animate,
|
||||||
|
body[data-aos-delay='2350'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.35s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2400'],
|
||||||
|
body[data-aos-duration='2400'] [data-aos] {
|
||||||
|
transition-duration: 2.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2400'],
|
||||||
|
body[data-aos-delay='2400'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2400'].aos-animate,
|
||||||
|
body[data-aos-delay='2400'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.4s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2450'],
|
||||||
|
body[data-aos-duration='2450'] [data-aos] {
|
||||||
|
transition-duration: 2.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2450'],
|
||||||
|
body[data-aos-delay='2450'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2450'].aos-animate,
|
||||||
|
body[data-aos-delay='2450'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.45s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2500'],
|
||||||
|
body[data-aos-duration='2500'] [data-aos] {
|
||||||
|
transition-duration: 2.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2500'],
|
||||||
|
body[data-aos-delay='2500'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2500'].aos-animate,
|
||||||
|
body[data-aos-delay='2500'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.5s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2550'],
|
||||||
|
body[data-aos-duration='2550'] [data-aos] {
|
||||||
|
transition-duration: 2.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2550'],
|
||||||
|
body[data-aos-delay='2550'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2550'].aos-animate,
|
||||||
|
body[data-aos-delay='2550'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.55s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2600'],
|
||||||
|
body[data-aos-duration='2600'] [data-aos] {
|
||||||
|
transition-duration: 2.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2600'],
|
||||||
|
body[data-aos-delay='2600'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2600'].aos-animate,
|
||||||
|
body[data-aos-delay='2600'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.6s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2650'],
|
||||||
|
body[data-aos-duration='2650'] [data-aos] {
|
||||||
|
transition-duration: 2.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2650'],
|
||||||
|
body[data-aos-delay='2650'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2650'].aos-animate,
|
||||||
|
body[data-aos-delay='2650'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.65s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2700'],
|
||||||
|
body[data-aos-duration='2700'] [data-aos] {
|
||||||
|
transition-duration: 2.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2700'],
|
||||||
|
body[data-aos-delay='2700'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2700'].aos-animate,
|
||||||
|
body[data-aos-delay='2700'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.7s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2750'],
|
||||||
|
body[data-aos-duration='2750'] [data-aos] {
|
||||||
|
transition-duration: 2.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2750'],
|
||||||
|
body[data-aos-delay='2750'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2750'].aos-animate,
|
||||||
|
body[data-aos-delay='2750'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.75s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2800'],
|
||||||
|
body[data-aos-duration='2800'] [data-aos] {
|
||||||
|
transition-duration: 2.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2800'],
|
||||||
|
body[data-aos-delay='2800'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2800'].aos-animate,
|
||||||
|
body[data-aos-delay='2800'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.8s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2850'],
|
||||||
|
body[data-aos-duration='2850'] [data-aos] {
|
||||||
|
transition-duration: 2.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2850'],
|
||||||
|
body[data-aos-delay='2850'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2850'].aos-animate,
|
||||||
|
body[data-aos-delay='2850'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.85s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2900'],
|
||||||
|
body[data-aos-duration='2900'] [data-aos] {
|
||||||
|
transition-duration: 2.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2900'],
|
||||||
|
body[data-aos-delay='2900'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2900'].aos-animate,
|
||||||
|
body[data-aos-delay='2900'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.9s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='2950'],
|
||||||
|
body[data-aos-duration='2950'] [data-aos] {
|
||||||
|
transition-duration: 2.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2950'],
|
||||||
|
body[data-aos-delay='2950'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='2950'].aos-animate,
|
||||||
|
body[data-aos-delay='2950'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 2.95s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-duration='3000'],
|
||||||
|
body[data-aos-duration='3000'] [data-aos] {
|
||||||
|
transition-duration: 3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='3000'],
|
||||||
|
body[data-aos-delay='3000'] [data-aos] {
|
||||||
|
transition-delay: 0;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-delay='3000'].aos-animate,
|
||||||
|
body[data-aos-delay='3000'] [data-aos].aos-animate {
|
||||||
|
transition-delay: 3s;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='linear'],
|
||||||
|
body[data-aos-easing='linear'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.25, 0.25, 0.75, 0.75);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease'],
|
||||||
|
body[data-aos-easing='ease'] [data-aos] {
|
||||||
|
transition-timing-function: ease;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in'],
|
||||||
|
body[data-aos-easing='ease-in'] [data-aos] {
|
||||||
|
transition-timing-function: ease-in;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out'],
|
||||||
|
body[data-aos-easing='ease-out'] [data-aos] {
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out'],
|
||||||
|
body[data-aos-easing='ease-in-out'] [data-aos] {
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-back'],
|
||||||
|
body[data-aos-easing='ease-in-back'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out-back'],
|
||||||
|
body[data-aos-easing='ease-out-back'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out-back'],
|
||||||
|
body[data-aos-easing='ease-in-out-back'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-sine'],
|
||||||
|
body[data-aos-easing='ease-in-sine'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out-sine'],
|
||||||
|
body[data-aos-easing='ease-out-sine'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out-sine'],
|
||||||
|
body[data-aos-easing='ease-in-out-sine'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-quad'],
|
||||||
|
body[data-aos-easing='ease-in-quad'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out-quad'],
|
||||||
|
body[data-aos-easing='ease-out-quad'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out-quad'],
|
||||||
|
body[data-aos-easing='ease-in-out-quad'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-cubic'],
|
||||||
|
body[data-aos-easing='ease-in-cubic'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out-cubic'],
|
||||||
|
body[data-aos-easing='ease-out-cubic'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out-cubic'],
|
||||||
|
body[data-aos-easing='ease-in-out-cubic'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-quart'],
|
||||||
|
body[data-aos-easing='ease-in-quart'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-out-quart'],
|
||||||
|
body[data-aos-easing='ease-out-quart'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
[data-aos][data-aos][data-aos-easing='ease-in-out-quart'],
|
||||||
|
body[data-aos-easing='ease-in-out-quart'] [data-aos] {
|
||||||
|
transition-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
||||||
|
}
|
||||||
|
[data-aos^='fade'][data-aos^='fade'] {
|
||||||
|
opacity: 0;
|
||||||
|
transition-property: opacity, transform;
|
||||||
|
}
|
||||||
|
[data-aos^='fade'][data-aos^='fade'].aos-animate {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-up'] {
|
||||||
|
transform: translate3d(0, 100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-down'] {
|
||||||
|
transform: translate3d(0, -100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-right'] {
|
||||||
|
transform: translate3d(-100px, 0, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-left'] {
|
||||||
|
transform: translate3d(100px, 0, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-up-right'] {
|
||||||
|
transform: translate3d(-100px, 100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-up-left'] {
|
||||||
|
transform: translate3d(100px, 100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-down-right'] {
|
||||||
|
transform: translate3d(-100px, -100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos='fade-down-left'] {
|
||||||
|
transform: translate3d(100px, -100px, 0);
|
||||||
|
}
|
||||||
|
[data-aos^='zoom'][data-aos^='zoom'] {
|
||||||
|
opacity: 0;
|
||||||
|
transition-property: opacity, transform;
|
||||||
|
}
|
||||||
|
[data-aos^='zoom'][data-aos^='zoom'].aos-animate {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateZ(0) scale(1);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-in'] {
|
||||||
|
transform: scale(0.6);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-in-up'] {
|
||||||
|
transform: translate3d(0, 100px, 0) scale(0.6);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-in-down'] {
|
||||||
|
transform: translate3d(0, -100px, 0) scale(0.6);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-in-right'] {
|
||||||
|
transform: translate3d(-100px, 0, 0) scale(0.6);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-in-left'] {
|
||||||
|
transform: translate3d(100px, 0, 0) scale(0.6);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-out'] {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-out-up'] {
|
||||||
|
transform: translate3d(0, 100px, 0) scale(1.2);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-out-down'] {
|
||||||
|
transform: translate3d(0, -100px, 0) scale(1.2);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-out-right'] {
|
||||||
|
transform: translate3d(-100px, 0, 0) scale(1.2);
|
||||||
|
}
|
||||||
|
[data-aos='zoom-out-left'] {
|
||||||
|
transform: translate3d(100px, 0, 0) scale(1.2);
|
||||||
|
}
|
||||||
|
[data-aos^='slide'][data-aos^='slide'] {
|
||||||
|
transition-property: transform;
|
||||||
|
}
|
||||||
|
[data-aos^='slide'][data-aos^='slide'].aos-animate {
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
[data-aos='slide-up'] {
|
||||||
|
transform: translate3d(0, 100%, 0);
|
||||||
|
}
|
||||||
|
[data-aos='slide-down'] {
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
[data-aos='slide-right'] {
|
||||||
|
transform: translate3d(-100%, 0, 0);
|
||||||
|
}
|
||||||
|
[data-aos='slide-left'] {
|
||||||
|
transform: translate3d(100%, 0, 0);
|
||||||
|
}
|
||||||
|
[data-aos^='flip'][data-aos^='flip'] {
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transition-property: transform;
|
||||||
|
}
|
||||||
|
[data-aos='flip-left'] {
|
||||||
|
transform: perspective(2500px) rotateY(-100deg);
|
||||||
|
}
|
||||||
|
[data-aos='flip-left'].aos-animate {
|
||||||
|
transform: perspective(2500px) rotateY(0);
|
||||||
|
}
|
||||||
|
[data-aos='flip-right'] {
|
||||||
|
transform: perspective(2500px) rotateY(100deg);
|
||||||
|
}
|
||||||
|
[data-aos='flip-right'].aos-animate {
|
||||||
|
transform: perspective(2500px) rotateY(0);
|
||||||
|
}
|
||||||
|
[data-aos='flip-up'] {
|
||||||
|
transform: perspective(2500px) rotateX(-100deg);
|
||||||
|
}
|
||||||
|
[data-aos='flip-up'].aos-animate {
|
||||||
|
transform: perspective(2500px) rotateX(0);
|
||||||
|
}
|
||||||
|
[data-aos='flip-down'] {
|
||||||
|
transform: perspective(2500px) rotateX(100deg);
|
||||||
|
}
|
||||||
|
[data-aos='flip-down'].aos-animate {
|
||||||
|
transform: perspective(2500px) rotateX(0);
|
||||||
|
}
|
||||||
672
public/js/aos.js
Normal file
672
public/js/aos.js
Normal file
@@ -0,0 +1,672 @@
|
|||||||
|
!(function (e, t) {
|
||||||
|
'object' == typeof exports && 'object' == typeof module
|
||||||
|
? (module.exports = t())
|
||||||
|
: 'function' == typeof define && define.amd
|
||||||
|
? define([], t)
|
||||||
|
: 'object' == typeof exports
|
||||||
|
? (exports.AOS = t())
|
||||||
|
: (e.AOS = t())
|
||||||
|
})(this, function () {
|
||||||
|
return (function (e) {
|
||||||
|
function t(o) {
|
||||||
|
if (n[o]) return n[o].exports
|
||||||
|
var i = (n[o] = { exports: {}, id: o, loaded: !1 })
|
||||||
|
return e[o].call(i.exports, i, i.exports, t), (i.loaded = !0), i.exports
|
||||||
|
}
|
||||||
|
var n = {}
|
||||||
|
return (t.m = e), (t.c = n), (t.p = 'dist/'), t(0)
|
||||||
|
})([
|
||||||
|
function (e, t, n) {
|
||||||
|
'use strict'
|
||||||
|
function o(e) {
|
||||||
|
return e && e.__esModule ? e : { default: e }
|
||||||
|
}
|
||||||
|
var i =
|
||||||
|
Object.assign ||
|
||||||
|
function (e) {
|
||||||
|
for (var t = 1; t < arguments.length; t++) {
|
||||||
|
var n = arguments[t]
|
||||||
|
for (var o in n)
|
||||||
|
Object.prototype.hasOwnProperty.call(n, o) && (e[o] = n[o])
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
},
|
||||||
|
r = n(1),
|
||||||
|
a = (o(r), n(6)),
|
||||||
|
u = o(a),
|
||||||
|
c = n(7),
|
||||||
|
s = o(c),
|
||||||
|
f = n(8),
|
||||||
|
d = o(f),
|
||||||
|
l = n(9),
|
||||||
|
p = o(l),
|
||||||
|
m = n(10),
|
||||||
|
b = o(m),
|
||||||
|
v = n(11),
|
||||||
|
y = o(v),
|
||||||
|
g = n(14),
|
||||||
|
h = o(g),
|
||||||
|
w = [],
|
||||||
|
k = !1,
|
||||||
|
x = {
|
||||||
|
offset: 120,
|
||||||
|
delay: 0,
|
||||||
|
easing: 'ease',
|
||||||
|
duration: 400,
|
||||||
|
disable: !1,
|
||||||
|
once: !1,
|
||||||
|
startEvent: 'DOMContentLoaded',
|
||||||
|
throttleDelay: 99,
|
||||||
|
debounceDelay: 50,
|
||||||
|
disableMutationObserver: !1
|
||||||
|
},
|
||||||
|
j = function () {
|
||||||
|
var e =
|
||||||
|
arguments.length > 0 && void 0 !== arguments[0] && arguments[0]
|
||||||
|
if ((e && (k = !0), k))
|
||||||
|
return (w = (0, y.default)(w, x)), (0, b.default)(w, x.once), w
|
||||||
|
},
|
||||||
|
O = function () {
|
||||||
|
;(w = (0, h.default)()), j()
|
||||||
|
},
|
||||||
|
M = function () {
|
||||||
|
w.forEach(function (e, t) {
|
||||||
|
e.node.removeAttribute('data-aos'),
|
||||||
|
e.node.removeAttribute('data-aos-easing'),
|
||||||
|
e.node.removeAttribute('data-aos-duration'),
|
||||||
|
e.node.removeAttribute('data-aos-delay')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
S = function (e) {
|
||||||
|
return (
|
||||||
|
e === !0 ||
|
||||||
|
('mobile' === e && p.default.mobile()) ||
|
||||||
|
('phone' === e && p.default.phone()) ||
|
||||||
|
('tablet' === e && p.default.tablet()) ||
|
||||||
|
('function' == typeof e && e() === !0)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ = function (e) {
|
||||||
|
;(x = i(x, e)), (w = (0, h.default)())
|
||||||
|
var t = document.all && !window.atob
|
||||||
|
return S(x.disable) || t
|
||||||
|
? M()
|
||||||
|
: (x.disableMutationObserver ||
|
||||||
|
d.default.isSupported() ||
|
||||||
|
(console.info(
|
||||||
|
'\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n '
|
||||||
|
),
|
||||||
|
(x.disableMutationObserver = !0)),
|
||||||
|
document
|
||||||
|
.querySelector('body')
|
||||||
|
.setAttribute('data-aos-easing', x.easing),
|
||||||
|
document
|
||||||
|
.querySelector('body')
|
||||||
|
.setAttribute('data-aos-duration', x.duration),
|
||||||
|
document
|
||||||
|
.querySelector('body')
|
||||||
|
.setAttribute('data-aos-delay', x.delay),
|
||||||
|
'DOMContentLoaded' === x.startEvent &&
|
||||||
|
['complete', 'interactive'].indexOf(document.readyState) > -1
|
||||||
|
? j(!0)
|
||||||
|
: 'load' === x.startEvent
|
||||||
|
? window.addEventListener(x.startEvent, function () {
|
||||||
|
j(!0)
|
||||||
|
})
|
||||||
|
: document.addEventListener(x.startEvent, function () {
|
||||||
|
j(!0)
|
||||||
|
}),
|
||||||
|
window.addEventListener(
|
||||||
|
'resize',
|
||||||
|
(0, s.default)(j, x.debounceDelay, !0)
|
||||||
|
),
|
||||||
|
window.addEventListener(
|
||||||
|
'orientationchange',
|
||||||
|
(0, s.default)(j, x.debounceDelay, !0)
|
||||||
|
),
|
||||||
|
window.addEventListener(
|
||||||
|
'scroll',
|
||||||
|
(0, u.default)(function () {
|
||||||
|
;(0, b.default)(w, x.once)
|
||||||
|
}, x.throttleDelay)
|
||||||
|
),
|
||||||
|
x.disableMutationObserver || d.default.ready('[data-aos]', O),
|
||||||
|
w)
|
||||||
|
}
|
||||||
|
e.exports = { init: _, refresh: j, refreshHard: O }
|
||||||
|
},
|
||||||
|
function (e, t) {},
|
||||||
|
,
|
||||||
|
,
|
||||||
|
,
|
||||||
|
,
|
||||||
|
function (e, t) {
|
||||||
|
;(function (t) {
|
||||||
|
'use strict'
|
||||||
|
function n(e, t, n) {
|
||||||
|
function o(t) {
|
||||||
|
var n = b,
|
||||||
|
o = v
|
||||||
|
return (b = v = void 0), (k = t), (g = e.apply(o, n))
|
||||||
|
}
|
||||||
|
function r(e) {
|
||||||
|
return (k = e), (h = setTimeout(f, t)), M ? o(e) : g
|
||||||
|
}
|
||||||
|
function a(e) {
|
||||||
|
var n = e - w,
|
||||||
|
o = e - k,
|
||||||
|
i = t - n
|
||||||
|
return S ? j(i, y - o) : i
|
||||||
|
}
|
||||||
|
function c(e) {
|
||||||
|
var n = e - w,
|
||||||
|
o = e - k
|
||||||
|
return void 0 === w || n >= t || n < 0 || (S && o >= y)
|
||||||
|
}
|
||||||
|
function f() {
|
||||||
|
var e = O()
|
||||||
|
return c(e) ? d(e) : void (h = setTimeout(f, a(e)))
|
||||||
|
}
|
||||||
|
function d(e) {
|
||||||
|
return (h = void 0), _ && b ? o(e) : ((b = v = void 0), g)
|
||||||
|
}
|
||||||
|
function l() {
|
||||||
|
void 0 !== h && clearTimeout(h), (k = 0), (b = w = v = h = void 0)
|
||||||
|
}
|
||||||
|
function p() {
|
||||||
|
return void 0 === h ? g : d(O())
|
||||||
|
}
|
||||||
|
function m() {
|
||||||
|
var e = O(),
|
||||||
|
n = c(e)
|
||||||
|
if (((b = arguments), (v = this), (w = e), n)) {
|
||||||
|
if (void 0 === h) return r(w)
|
||||||
|
if (S) return (h = setTimeout(f, t)), o(w)
|
||||||
|
}
|
||||||
|
return void 0 === h && (h = setTimeout(f, t)), g
|
||||||
|
}
|
||||||
|
var b,
|
||||||
|
v,
|
||||||
|
y,
|
||||||
|
g,
|
||||||
|
h,
|
||||||
|
w,
|
||||||
|
k = 0,
|
||||||
|
M = !1,
|
||||||
|
S = !1,
|
||||||
|
_ = !0
|
||||||
|
if ('function' != typeof e) throw new TypeError(s)
|
||||||
|
return (
|
||||||
|
(t = u(t) || 0),
|
||||||
|
i(n) &&
|
||||||
|
((M = !!n.leading),
|
||||||
|
(S = 'maxWait' in n),
|
||||||
|
(y = S ? x(u(n.maxWait) || 0, t) : y),
|
||||||
|
(_ = 'trailing' in n ? !!n.trailing : _)),
|
||||||
|
(m.cancel = l),
|
||||||
|
(m.flush = p),
|
||||||
|
m
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function o(e, t, o) {
|
||||||
|
var r = !0,
|
||||||
|
a = !0
|
||||||
|
if ('function' != typeof e) throw new TypeError(s)
|
||||||
|
return (
|
||||||
|
i(o) &&
|
||||||
|
((r = 'leading' in o ? !!o.leading : r),
|
||||||
|
(a = 'trailing' in o ? !!o.trailing : a)),
|
||||||
|
n(e, t, { leading: r, maxWait: t, trailing: a })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function i(e) {
|
||||||
|
var t = 'undefined' == typeof e ? 'undefined' : c(e)
|
||||||
|
return !!e && ('object' == t || 'function' == t)
|
||||||
|
}
|
||||||
|
function r(e) {
|
||||||
|
return (
|
||||||
|
!!e && 'object' == ('undefined' == typeof e ? 'undefined' : c(e))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function a(e) {
|
||||||
|
return (
|
||||||
|
'symbol' == ('undefined' == typeof e ? 'undefined' : c(e)) ||
|
||||||
|
(r(e) && k.call(e) == d)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function u(e) {
|
||||||
|
if ('number' == typeof e) return e
|
||||||
|
if (a(e)) return f
|
||||||
|
if (i(e)) {
|
||||||
|
var t = 'function' == typeof e.valueOf ? e.valueOf() : e
|
||||||
|
e = i(t) ? t + '' : t
|
||||||
|
}
|
||||||
|
if ('string' != typeof e) return 0 === e ? e : +e
|
||||||
|
e = e.replace(l, '')
|
||||||
|
var n = m.test(e)
|
||||||
|
return n || b.test(e) ? v(e.slice(2), n ? 2 : 8) : p.test(e) ? f : +e
|
||||||
|
}
|
||||||
|
var c =
|
||||||
|
'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator
|
||||||
|
? function (e) {
|
||||||
|
return typeof e
|
||||||
|
}
|
||||||
|
: function (e) {
|
||||||
|
return e &&
|
||||||
|
'function' == typeof Symbol &&
|
||||||
|
e.constructor === Symbol &&
|
||||||
|
e !== Symbol.prototype
|
||||||
|
? 'symbol'
|
||||||
|
: typeof e
|
||||||
|
},
|
||||||
|
s = 'Expected a function',
|
||||||
|
f = NaN,
|
||||||
|
d = '[object Symbol]',
|
||||||
|
l = /^\s+|\s+$/g,
|
||||||
|
p = /^[-+]0x[0-9a-f]+$/i,
|
||||||
|
m = /^0b[01]+$/i,
|
||||||
|
b = /^0o[0-7]+$/i,
|
||||||
|
v = parseInt,
|
||||||
|
y =
|
||||||
|
'object' == ('undefined' == typeof t ? 'undefined' : c(t)) &&
|
||||||
|
t &&
|
||||||
|
t.Object === Object &&
|
||||||
|
t,
|
||||||
|
g =
|
||||||
|
'object' == ('undefined' == typeof self ? 'undefined' : c(self)) &&
|
||||||
|
self &&
|
||||||
|
self.Object === Object &&
|
||||||
|
self,
|
||||||
|
h = y || g || Function('return this')(),
|
||||||
|
w = Object.prototype,
|
||||||
|
k = w.toString,
|
||||||
|
x = Math.max,
|
||||||
|
j = Math.min,
|
||||||
|
O = function () {
|
||||||
|
return h.Date.now()
|
||||||
|
}
|
||||||
|
e.exports = o
|
||||||
|
}).call(
|
||||||
|
t,
|
||||||
|
(function () {
|
||||||
|
return this
|
||||||
|
})()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
;(function (t) {
|
||||||
|
'use strict'
|
||||||
|
function n(e, t, n) {
|
||||||
|
function i(t) {
|
||||||
|
var n = b,
|
||||||
|
o = v
|
||||||
|
return (b = v = void 0), (O = t), (g = e.apply(o, n))
|
||||||
|
}
|
||||||
|
function r(e) {
|
||||||
|
return (O = e), (h = setTimeout(f, t)), M ? i(e) : g
|
||||||
|
}
|
||||||
|
function u(e) {
|
||||||
|
var n = e - w,
|
||||||
|
o = e - O,
|
||||||
|
i = t - n
|
||||||
|
return S ? x(i, y - o) : i
|
||||||
|
}
|
||||||
|
function s(e) {
|
||||||
|
var n = e - w,
|
||||||
|
o = e - O
|
||||||
|
return void 0 === w || n >= t || n < 0 || (S && o >= y)
|
||||||
|
}
|
||||||
|
function f() {
|
||||||
|
var e = j()
|
||||||
|
return s(e) ? d(e) : void (h = setTimeout(f, u(e)))
|
||||||
|
}
|
||||||
|
function d(e) {
|
||||||
|
return (h = void 0), _ && b ? i(e) : ((b = v = void 0), g)
|
||||||
|
}
|
||||||
|
function l() {
|
||||||
|
void 0 !== h && clearTimeout(h), (O = 0), (b = w = v = h = void 0)
|
||||||
|
}
|
||||||
|
function p() {
|
||||||
|
return void 0 === h ? g : d(j())
|
||||||
|
}
|
||||||
|
function m() {
|
||||||
|
var e = j(),
|
||||||
|
n = s(e)
|
||||||
|
if (((b = arguments), (v = this), (w = e), n)) {
|
||||||
|
if (void 0 === h) return r(w)
|
||||||
|
if (S) return (h = setTimeout(f, t)), i(w)
|
||||||
|
}
|
||||||
|
return void 0 === h && (h = setTimeout(f, t)), g
|
||||||
|
}
|
||||||
|
var b,
|
||||||
|
v,
|
||||||
|
y,
|
||||||
|
g,
|
||||||
|
h,
|
||||||
|
w,
|
||||||
|
O = 0,
|
||||||
|
M = !1,
|
||||||
|
S = !1,
|
||||||
|
_ = !0
|
||||||
|
if ('function' != typeof e) throw new TypeError(c)
|
||||||
|
return (
|
||||||
|
(t = a(t) || 0),
|
||||||
|
o(n) &&
|
||||||
|
((M = !!n.leading),
|
||||||
|
(S = 'maxWait' in n),
|
||||||
|
(y = S ? k(a(n.maxWait) || 0, t) : y),
|
||||||
|
(_ = 'trailing' in n ? !!n.trailing : _)),
|
||||||
|
(m.cancel = l),
|
||||||
|
(m.flush = p),
|
||||||
|
m
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function o(e) {
|
||||||
|
var t = 'undefined' == typeof e ? 'undefined' : u(e)
|
||||||
|
return !!e && ('object' == t || 'function' == t)
|
||||||
|
}
|
||||||
|
function i(e) {
|
||||||
|
return (
|
||||||
|
!!e && 'object' == ('undefined' == typeof e ? 'undefined' : u(e))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function r(e) {
|
||||||
|
return (
|
||||||
|
'symbol' == ('undefined' == typeof e ? 'undefined' : u(e)) ||
|
||||||
|
(i(e) && w.call(e) == f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function a(e) {
|
||||||
|
if ('number' == typeof e) return e
|
||||||
|
if (r(e)) return s
|
||||||
|
if (o(e)) {
|
||||||
|
var t = 'function' == typeof e.valueOf ? e.valueOf() : e
|
||||||
|
e = o(t) ? t + '' : t
|
||||||
|
}
|
||||||
|
if ('string' != typeof e) return 0 === e ? e : +e
|
||||||
|
e = e.replace(d, '')
|
||||||
|
var n = p.test(e)
|
||||||
|
return n || m.test(e) ? b(e.slice(2), n ? 2 : 8) : l.test(e) ? s : +e
|
||||||
|
}
|
||||||
|
var u =
|
||||||
|
'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator
|
||||||
|
? function (e) {
|
||||||
|
return typeof e
|
||||||
|
}
|
||||||
|
: function (e) {
|
||||||
|
return e &&
|
||||||
|
'function' == typeof Symbol &&
|
||||||
|
e.constructor === Symbol &&
|
||||||
|
e !== Symbol.prototype
|
||||||
|
? 'symbol'
|
||||||
|
: typeof e
|
||||||
|
},
|
||||||
|
c = 'Expected a function',
|
||||||
|
s = NaN,
|
||||||
|
f = '[object Symbol]',
|
||||||
|
d = /^\s+|\s+$/g,
|
||||||
|
l = /^[-+]0x[0-9a-f]+$/i,
|
||||||
|
p = /^0b[01]+$/i,
|
||||||
|
m = /^0o[0-7]+$/i,
|
||||||
|
b = parseInt,
|
||||||
|
v =
|
||||||
|
'object' == ('undefined' == typeof t ? 'undefined' : u(t)) &&
|
||||||
|
t &&
|
||||||
|
t.Object === Object &&
|
||||||
|
t,
|
||||||
|
y =
|
||||||
|
'object' == ('undefined' == typeof self ? 'undefined' : u(self)) &&
|
||||||
|
self &&
|
||||||
|
self.Object === Object &&
|
||||||
|
self,
|
||||||
|
g = v || y || Function('return this')(),
|
||||||
|
h = Object.prototype,
|
||||||
|
w = h.toString,
|
||||||
|
k = Math.max,
|
||||||
|
x = Math.min,
|
||||||
|
j = function () {
|
||||||
|
return g.Date.now()
|
||||||
|
}
|
||||||
|
e.exports = n
|
||||||
|
}).call(
|
||||||
|
t,
|
||||||
|
(function () {
|
||||||
|
return this
|
||||||
|
})()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
'use strict'
|
||||||
|
function n(e) {
|
||||||
|
var t = void 0,
|
||||||
|
o = void 0,
|
||||||
|
i = void 0
|
||||||
|
for (t = 0; t < e.length; t += 1) {
|
||||||
|
if (((o = e[t]), o.dataset && o.dataset.aos)) return !0
|
||||||
|
if ((i = o.children && n(o.children))) return !0
|
||||||
|
}
|
||||||
|
return !1
|
||||||
|
}
|
||||||
|
function o() {
|
||||||
|
return (
|
||||||
|
window.MutationObserver ||
|
||||||
|
window.WebKitMutationObserver ||
|
||||||
|
window.MozMutationObserver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function i() {
|
||||||
|
return !!o()
|
||||||
|
}
|
||||||
|
function r(e, t) {
|
||||||
|
var n = window.document,
|
||||||
|
i = o(),
|
||||||
|
r = new i(a)
|
||||||
|
;(u = t),
|
||||||
|
r.observe(n.documentElement, {
|
||||||
|
childList: !0,
|
||||||
|
subtree: !0,
|
||||||
|
removedNodes: !0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function a(e) {
|
||||||
|
e &&
|
||||||
|
e.forEach(function (e) {
|
||||||
|
var t = Array.prototype.slice.call(e.addedNodes),
|
||||||
|
o = Array.prototype.slice.call(e.removedNodes),
|
||||||
|
i = t.concat(o)
|
||||||
|
if (n(i)) return u()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var u = function () {}
|
||||||
|
t.default = { isSupported: i, ready: r }
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
'use strict'
|
||||||
|
function n(e, t) {
|
||||||
|
if (!(e instanceof t))
|
||||||
|
throw new TypeError('Cannot call a class as a function')
|
||||||
|
}
|
||||||
|
function o() {
|
||||||
|
return navigator.userAgent || navigator.vendor || window.opera || ''
|
||||||
|
}
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var i = (function () {
|
||||||
|
function e(e, t) {
|
||||||
|
for (var n = 0; n < t.length; n++) {
|
||||||
|
var o = t[n]
|
||||||
|
;(o.enumerable = o.enumerable || !1),
|
||||||
|
(o.configurable = !0),
|
||||||
|
'value' in o && (o.writable = !0),
|
||||||
|
Object.defineProperty(e, o.key, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return function (t, n, o) {
|
||||||
|
return n && e(t.prototype, n), o && e(t, o), t
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
r =
|
||||||
|
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i,
|
||||||
|
a =
|
||||||
|
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,
|
||||||
|
u =
|
||||||
|
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i,
|
||||||
|
c =
|
||||||
|
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,
|
||||||
|
s = (function () {
|
||||||
|
function e() {
|
||||||
|
n(this, e)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
i(e, [
|
||||||
|
{
|
||||||
|
key: 'phone',
|
||||||
|
value: function () {
|
||||||
|
var e = o()
|
||||||
|
return !(!r.test(e) && !a.test(e.substr(0, 4)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mobile',
|
||||||
|
value: function () {
|
||||||
|
var e = o()
|
||||||
|
return !(!u.test(e) && !c.test(e.substr(0, 4)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tablet',
|
||||||
|
value: function () {
|
||||||
|
return this.mobile() && !this.phone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
t.default = new s()
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
'use strict'
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var n = function (e, t, n) {
|
||||||
|
var o = e.node.getAttribute('data-aos-once')
|
||||||
|
t > e.position
|
||||||
|
? e.node.classList.add('aos-animate')
|
||||||
|
: 'undefined' != typeof o &&
|
||||||
|
('false' === o || (!n && 'true' !== o)) &&
|
||||||
|
e.node.classList.remove('aos-animate')
|
||||||
|
},
|
||||||
|
o = function (e, t) {
|
||||||
|
var o = window.pageYOffset,
|
||||||
|
i = window.innerHeight
|
||||||
|
e.forEach(function (e, r) {
|
||||||
|
n(e, i + o, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
t.default = o
|
||||||
|
},
|
||||||
|
function (e, t, n) {
|
||||||
|
'use strict'
|
||||||
|
function o(e) {
|
||||||
|
return e && e.__esModule ? e : { default: e }
|
||||||
|
}
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var i = n(12),
|
||||||
|
r = o(i),
|
||||||
|
a = function (e, t) {
|
||||||
|
return (
|
||||||
|
e.forEach(function (e, n) {
|
||||||
|
e.node.classList.add('aos-init'),
|
||||||
|
(e.position = (0, r.default)(e.node, t.offset))
|
||||||
|
}),
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
t.default = a
|
||||||
|
},
|
||||||
|
function (e, t, n) {
|
||||||
|
'use strict'
|
||||||
|
function o(e) {
|
||||||
|
return e && e.__esModule ? e : { default: e }
|
||||||
|
}
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var i = n(13),
|
||||||
|
r = o(i),
|
||||||
|
a = function (e, t) {
|
||||||
|
var n = 0,
|
||||||
|
o = 0,
|
||||||
|
i = window.innerHeight,
|
||||||
|
a = {
|
||||||
|
offset: e.getAttribute('data-aos-offset'),
|
||||||
|
anchor: e.getAttribute('data-aos-anchor'),
|
||||||
|
anchorPlacement: e.getAttribute('data-aos-anchor-placement')
|
||||||
|
}
|
||||||
|
switch (
|
||||||
|
(a.offset && !isNaN(a.offset) && (o = parseInt(a.offset)),
|
||||||
|
a.anchor &&
|
||||||
|
document.querySelectorAll(a.anchor) &&
|
||||||
|
(e = document.querySelectorAll(a.anchor)[0]),
|
||||||
|
(n = (0, r.default)(e).top),
|
||||||
|
a.anchorPlacement)
|
||||||
|
) {
|
||||||
|
case 'top-bottom':
|
||||||
|
break
|
||||||
|
case 'center-bottom':
|
||||||
|
n += e.offsetHeight / 2
|
||||||
|
break
|
||||||
|
case 'bottom-bottom':
|
||||||
|
n += e.offsetHeight
|
||||||
|
break
|
||||||
|
case 'top-center':
|
||||||
|
n += i / 2
|
||||||
|
break
|
||||||
|
case 'bottom-center':
|
||||||
|
n += i / 2 + e.offsetHeight
|
||||||
|
break
|
||||||
|
case 'center-center':
|
||||||
|
n += i / 2 + e.offsetHeight / 2
|
||||||
|
break
|
||||||
|
case 'top-top':
|
||||||
|
n += i
|
||||||
|
break
|
||||||
|
case 'bottom-top':
|
||||||
|
n += e.offsetHeight + i
|
||||||
|
break
|
||||||
|
case 'center-top':
|
||||||
|
n += e.offsetHeight / 2 + i
|
||||||
|
}
|
||||||
|
return a.anchorPlacement || a.offset || isNaN(t) || (o = t), n + o
|
||||||
|
}
|
||||||
|
t.default = a
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
'use strict'
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var n = function (e) {
|
||||||
|
for (
|
||||||
|
var t = 0, n = 0;
|
||||||
|
e && !isNaN(e.offsetLeft) && !isNaN(e.offsetTop);
|
||||||
|
|
||||||
|
)
|
||||||
|
(t += e.offsetLeft - ('BODY' != e.tagName ? e.scrollLeft : 0)),
|
||||||
|
(n += e.offsetTop - ('BODY' != e.tagName ? e.scrollTop : 0)),
|
||||||
|
(e = e.offsetParent)
|
||||||
|
return { top: n, left: t }
|
||||||
|
}
|
||||||
|
t.default = n
|
||||||
|
},
|
||||||
|
function (e, t) {
|
||||||
|
'use strict'
|
||||||
|
Object.defineProperty(t, '__esModule', { value: !0 })
|
||||||
|
var n = function (e) {
|
||||||
|
return (
|
||||||
|
(e = e || document.querySelectorAll('[data-aos]')),
|
||||||
|
Array.prototype.map.call(e, function (e) {
|
||||||
|
return { node: e }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
t.default = n
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
@@ -57,10 +57,10 @@ function createFireworks({ config, anime }) {
|
|||||||
function updateCoords(e) {
|
function updateCoords(e) {
|
||||||
pointerX =
|
pointerX =
|
||||||
e.clientX ||
|
e.clientX ||
|
||||||
(e.touches[0] ? e.touches[0].clientX : e.changedTouches[0].clientX)
|
(e?.touches[0] ? e.touches[0].clientX : e.changedTouches[0].clientX)
|
||||||
pointerY =
|
pointerY =
|
||||||
e.clientY ||
|
e.clientY ||
|
||||||
(e.touches[0] ? e.touches[0].clientY : e.changedTouches[0].clientY)
|
(e?.touches[0] ? e.touches[0].clientY : e.changedTouches[0].clientY)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setParticuleDirection(p) {
|
function setParticuleDirection(p) {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ function createSakura() {
|
|||||||
'style',
|
'style',
|
||||||
'position: fixed;left: 0;top: 0;pointer-events: none;'
|
'position: fixed;left: 0;top: 0;pointer-events: none;'
|
||||||
)
|
)
|
||||||
canvas.setAttribute('id', id)
|
canvas.setAttribute('id', 'sakura')
|
||||||
document.getElementsByTagName('body')[0].appendChild(canvas)
|
document.getElementsByTagName('body')[0].appendChild(canvas)
|
||||||
cxt = canvas.getContext('2d')
|
cxt = canvas.getContext('2d')
|
||||||
var sakuraList = new SakuraList()
|
var sakuraList = new SakuraList()
|
||||||
|
|||||||
626
public/js/typed.min.js
vendored
Normal file
626
public/js/typed.min.js
vendored
Normal file
@@ -0,0 +1,626 @@
|
|||||||
|
/*!
|
||||||
|
*
|
||||||
|
* typed.js - A JavaScript Typing Animation Library
|
||||||
|
* Author: Matt Boldt <me@mattboldt.com>
|
||||||
|
* Version: v2.0.12
|
||||||
|
* Url: https://github.com/mattboldt/typed.js
|
||||||
|
* License(s): MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
;(function (t, e) {
|
||||||
|
'object' == typeof exports && 'object' == typeof module
|
||||||
|
? (module.exports = e())
|
||||||
|
: 'function' == typeof define && define.amd
|
||||||
|
? define([], e)
|
||||||
|
: 'object' == typeof exports
|
||||||
|
? (exports.Typed = e())
|
||||||
|
: (t.Typed = e())
|
||||||
|
})(this, function () {
|
||||||
|
return (function (t) {
|
||||||
|
function e(n) {
|
||||||
|
if (s[n]) return s[n].exports
|
||||||
|
var i = (s[n] = { exports: {}, id: n, loaded: !1 })
|
||||||
|
return t[n].call(i.exports, i, i.exports, e), (i.loaded = !0), i.exports
|
||||||
|
}
|
||||||
|
var s = {}
|
||||||
|
return (e.m = t), (e.c = s), (e.p = ''), e(0)
|
||||||
|
})([
|
||||||
|
function (t, e, s) {
|
||||||
|
'use strict'
|
||||||
|
function n(t, e) {
|
||||||
|
if (!(t instanceof e))
|
||||||
|
throw new TypeError('Cannot call a class as a function')
|
||||||
|
}
|
||||||
|
Object.defineProperty(e, '__esModule', { value: !0 })
|
||||||
|
var i = (function () {
|
||||||
|
function t(t, e) {
|
||||||
|
for (var s = 0; s < e.length; s++) {
|
||||||
|
var n = e[s]
|
||||||
|
;(n.enumerable = n.enumerable || !1),
|
||||||
|
(n.configurable = !0),
|
||||||
|
'value' in n && (n.writable = !0),
|
||||||
|
Object.defineProperty(t, n.key, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return function (e, s, n) {
|
||||||
|
return s && t(e.prototype, s), n && t(e, n), e
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
r = s(1),
|
||||||
|
o = s(3),
|
||||||
|
a = (function () {
|
||||||
|
function t(e, s) {
|
||||||
|
n(this, t), r.initializer.load(this, s, e), this.begin()
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
i(t, [
|
||||||
|
{
|
||||||
|
key: 'toggle',
|
||||||
|
value: function () {
|
||||||
|
this.pause.status ? this.start() : this.stop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'stop',
|
||||||
|
value: function () {
|
||||||
|
this.typingComplete ||
|
||||||
|
this.pause.status ||
|
||||||
|
(this.toggleBlinking(!0),
|
||||||
|
(this.pause.status = !0),
|
||||||
|
this.options.onStop(this.arrayPos, this))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'start',
|
||||||
|
value: function () {
|
||||||
|
this.typingComplete ||
|
||||||
|
(this.pause.status &&
|
||||||
|
((this.pause.status = !1),
|
||||||
|
this.pause.typewrite
|
||||||
|
? this.typewrite(
|
||||||
|
this.pause.curString,
|
||||||
|
this.pause.curStrPos
|
||||||
|
)
|
||||||
|
: this.backspace(
|
||||||
|
this.pause.curString,
|
||||||
|
this.pause.curStrPos
|
||||||
|
),
|
||||||
|
this.options.onStart(this.arrayPos, this)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'destroy',
|
||||||
|
value: function () {
|
||||||
|
this.reset(!1), this.options.onDestroy(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reset',
|
||||||
|
value: function () {
|
||||||
|
var t =
|
||||||
|
arguments.length <= 0 ||
|
||||||
|
void 0 === arguments[0] ||
|
||||||
|
arguments[0]
|
||||||
|
clearInterval(this.timeout),
|
||||||
|
this.replaceText(''),
|
||||||
|
this.cursor &&
|
||||||
|
this.cursor.parentNode &&
|
||||||
|
(this.cursor.parentNode.removeChild(this.cursor),
|
||||||
|
(this.cursor = null)),
|
||||||
|
(this.strPos = 0),
|
||||||
|
(this.arrayPos = 0),
|
||||||
|
(this.curLoop = 0),
|
||||||
|
t &&
|
||||||
|
(this.insertCursor(),
|
||||||
|
this.options.onReset(this),
|
||||||
|
this.begin())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'begin',
|
||||||
|
value: function () {
|
||||||
|
var t = this
|
||||||
|
this.options.onBegin(this),
|
||||||
|
(this.typingComplete = !1),
|
||||||
|
this.shuffleStringsIfNeeded(this),
|
||||||
|
this.insertCursor(),
|
||||||
|
this.bindInputFocusEvents && this.bindFocusEvents(),
|
||||||
|
(this.timeout = setTimeout(function () {
|
||||||
|
t.currentElContent && 0 !== t.currentElContent.length
|
||||||
|
? t.backspace(
|
||||||
|
t.currentElContent,
|
||||||
|
t.currentElContent.length
|
||||||
|
)
|
||||||
|
: t.typewrite(
|
||||||
|
t.strings[t.sequence[t.arrayPos]],
|
||||||
|
t.strPos
|
||||||
|
)
|
||||||
|
}, this.startDelay))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'typewrite',
|
||||||
|
value: function (t, e) {
|
||||||
|
var s = this
|
||||||
|
this.fadeOut &&
|
||||||
|
this.el.classList.contains(this.fadeOutClass) &&
|
||||||
|
(this.el.classList.remove(this.fadeOutClass),
|
||||||
|
this.cursor &&
|
||||||
|
this.cursor.classList.remove(this.fadeOutClass))
|
||||||
|
var n = this.humanizer(this.typeSpeed),
|
||||||
|
i = 1
|
||||||
|
return this.pause.status === !0
|
||||||
|
? void this.setPauseStatus(t, e, !0)
|
||||||
|
: void (this.timeout = setTimeout(function () {
|
||||||
|
e = o.htmlParser.typeHtmlChars(t, e, s)
|
||||||
|
var n = 0,
|
||||||
|
r = t.substr(e)
|
||||||
|
if ('^' === r.charAt(0) && /^\^\d+/.test(r)) {
|
||||||
|
var a = 1
|
||||||
|
;(r = /\d+/.exec(r)[0]),
|
||||||
|
(a += r.length),
|
||||||
|
(n = parseInt(r)),
|
||||||
|
(s.temporaryPause = !0),
|
||||||
|
s.options.onTypingPaused(s.arrayPos, s),
|
||||||
|
(t = t.substring(0, e) + t.substring(e + a)),
|
||||||
|
s.toggleBlinking(!0)
|
||||||
|
}
|
||||||
|
if ('`' === r.charAt(0)) {
|
||||||
|
for (
|
||||||
|
;
|
||||||
|
'`' !== t.substr(e + i).charAt(0) &&
|
||||||
|
(i++, !(e + i > t.length));
|
||||||
|
|
||||||
|
);
|
||||||
|
var u = t.substring(0, e),
|
||||||
|
l = t.substring(u.length + 1, e + i),
|
||||||
|
c = t.substring(e + i + 1)
|
||||||
|
;(t = u + l + c), i--
|
||||||
|
}
|
||||||
|
s.timeout = setTimeout(function () {
|
||||||
|
s.toggleBlinking(!1),
|
||||||
|
e >= t.length
|
||||||
|
? s.doneTyping(t, e)
|
||||||
|
: s.keepTyping(t, e, i),
|
||||||
|
s.temporaryPause &&
|
||||||
|
((s.temporaryPause = !1),
|
||||||
|
s.options.onTypingResumed(s.arrayPos, s))
|
||||||
|
}, n)
|
||||||
|
}, n))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'keepTyping',
|
||||||
|
value: function (t, e, s) {
|
||||||
|
0 === e &&
|
||||||
|
(this.toggleBlinking(!1),
|
||||||
|
this.options.preStringTyped(this.arrayPos, this)),
|
||||||
|
(e += s)
|
||||||
|
var n = t.substr(0, e)
|
||||||
|
this.replaceText(n), this.typewrite(t, e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'doneTyping',
|
||||||
|
value: function (t, e) {
|
||||||
|
var s = this
|
||||||
|
this.options.onStringTyped(this.arrayPos, this),
|
||||||
|
this.toggleBlinking(!0),
|
||||||
|
(this.arrayPos === this.strings.length - 1 &&
|
||||||
|
(this.complete(),
|
||||||
|
this.loop === !1 || this.curLoop === this.loopCount)) ||
|
||||||
|
(this.timeout = setTimeout(function () {
|
||||||
|
s.backspace(t, e)
|
||||||
|
}, this.backDelay))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'backspace',
|
||||||
|
value: function (t, e) {
|
||||||
|
var s = this
|
||||||
|
if (this.pause.status === !0)
|
||||||
|
return void this.setPauseStatus(t, e, !1)
|
||||||
|
if (this.fadeOut) return this.initFadeOut()
|
||||||
|
this.toggleBlinking(!1)
|
||||||
|
var n = this.humanizer(this.backSpeed)
|
||||||
|
this.timeout = setTimeout(function () {
|
||||||
|
e = o.htmlParser.backSpaceHtmlChars(t, e, s)
|
||||||
|
var n = t.substr(0, e)
|
||||||
|
if ((s.replaceText(n), s.smartBackspace)) {
|
||||||
|
var i = s.strings[s.arrayPos + 1]
|
||||||
|
i && n === i.substr(0, e)
|
||||||
|
? (s.stopNum = e)
|
||||||
|
: (s.stopNum = 0)
|
||||||
|
}
|
||||||
|
e > s.stopNum
|
||||||
|
? (e--, s.backspace(t, e))
|
||||||
|
: e <= s.stopNum &&
|
||||||
|
(s.arrayPos++,
|
||||||
|
s.arrayPos === s.strings.length
|
||||||
|
? ((s.arrayPos = 0),
|
||||||
|
s.options.onLastStringBackspaced(),
|
||||||
|
s.shuffleStringsIfNeeded(),
|
||||||
|
s.begin())
|
||||||
|
: s.typewrite(s.strings[s.sequence[s.arrayPos]], e))
|
||||||
|
}, n)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'complete',
|
||||||
|
value: function () {
|
||||||
|
this.options.onComplete(this),
|
||||||
|
this.loop ? this.curLoop++ : (this.typingComplete = !0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'setPauseStatus',
|
||||||
|
value: function (t, e, s) {
|
||||||
|
;(this.pause.typewrite = s),
|
||||||
|
(this.pause.curString = t),
|
||||||
|
(this.pause.curStrPos = e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'toggleBlinking',
|
||||||
|
value: function (t) {
|
||||||
|
this.cursor &&
|
||||||
|
(this.pause.status ||
|
||||||
|
(this.cursorBlinking !== t &&
|
||||||
|
((this.cursorBlinking = t),
|
||||||
|
t
|
||||||
|
? this.cursor.classList.add('typed-cursor--blink')
|
||||||
|
: this.cursor.classList.remove(
|
||||||
|
'typed-cursor--blink'
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'humanizer',
|
||||||
|
value: function (t) {
|
||||||
|
return Math.round((Math.random() * t) / 2) + t
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'shuffleStringsIfNeeded',
|
||||||
|
value: function () {
|
||||||
|
this.shuffle &&
|
||||||
|
(this.sequence = this.sequence.sort(function () {
|
||||||
|
return Math.random() - 0.5
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'initFadeOut',
|
||||||
|
value: function () {
|
||||||
|
var t = this
|
||||||
|
return (
|
||||||
|
(this.el.className += ' ' + this.fadeOutClass),
|
||||||
|
this.cursor &&
|
||||||
|
(this.cursor.className += ' ' + this.fadeOutClass),
|
||||||
|
setTimeout(function () {
|
||||||
|
t.arrayPos++,
|
||||||
|
t.replaceText(''),
|
||||||
|
t.strings.length > t.arrayPos
|
||||||
|
? t.typewrite(t.strings[t.sequence[t.arrayPos]], 0)
|
||||||
|
: (t.typewrite(t.strings[0], 0), (t.arrayPos = 0))
|
||||||
|
}, this.fadeOutDelay)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'replaceText',
|
||||||
|
value: function (t) {
|
||||||
|
this.attr
|
||||||
|
? this.el.setAttribute(this.attr, t)
|
||||||
|
: this.isInput
|
||||||
|
? (this.el.value = t)
|
||||||
|
: 'html' === this.contentType
|
||||||
|
? (this.el.innerHTML = t)
|
||||||
|
: (this.el.textContent = t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'bindFocusEvents',
|
||||||
|
value: function () {
|
||||||
|
var t = this
|
||||||
|
this.isInput &&
|
||||||
|
(this.el.addEventListener('focus', function (e) {
|
||||||
|
t.stop()
|
||||||
|
}),
|
||||||
|
this.el.addEventListener('blur', function (e) {
|
||||||
|
;(t.el.value && 0 !== t.el.value.length) || t.start()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'insertCursor',
|
||||||
|
value: function () {
|
||||||
|
this.showCursor &&
|
||||||
|
(this.cursor ||
|
||||||
|
((this.cursor = document.createElement('span')),
|
||||||
|
(this.cursor.className = 'typed-cursor'),
|
||||||
|
this.cursor.setAttribute('aria-hidden', !0),
|
||||||
|
(this.cursor.innerHTML = this.cursorChar),
|
||||||
|
this.el.parentNode &&
|
||||||
|
this.el.parentNode.insertBefore(
|
||||||
|
this.cursor,
|
||||||
|
this.el.nextSibling
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
t
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
;(e['default'] = a), (t.exports = e['default'])
|
||||||
|
},
|
||||||
|
function (t, e, s) {
|
||||||
|
'use strict'
|
||||||
|
function n(t) {
|
||||||
|
return t && t.__esModule ? t : { default: t }
|
||||||
|
}
|
||||||
|
function i(t, e) {
|
||||||
|
if (!(t instanceof e))
|
||||||
|
throw new TypeError('Cannot call a class as a function')
|
||||||
|
}
|
||||||
|
Object.defineProperty(e, '__esModule', { value: !0 })
|
||||||
|
var r =
|
||||||
|
Object.assign ||
|
||||||
|
function (t) {
|
||||||
|
for (var e = 1; e < arguments.length; e++) {
|
||||||
|
var s = arguments[e]
|
||||||
|
for (var n in s)
|
||||||
|
Object.prototype.hasOwnProperty.call(s, n) && (t[n] = s[n])
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
},
|
||||||
|
o = (function () {
|
||||||
|
function t(t, e) {
|
||||||
|
for (var s = 0; s < e.length; s++) {
|
||||||
|
var n = e[s]
|
||||||
|
;(n.enumerable = n.enumerable || !1),
|
||||||
|
(n.configurable = !0),
|
||||||
|
'value' in n && (n.writable = !0),
|
||||||
|
Object.defineProperty(t, n.key, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return function (e, s, n) {
|
||||||
|
return s && t(e.prototype, s), n && t(e, n), e
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
a = s(2),
|
||||||
|
u = n(a),
|
||||||
|
l = (function () {
|
||||||
|
function t() {
|
||||||
|
i(this, t)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
o(t, [
|
||||||
|
{
|
||||||
|
key: 'load',
|
||||||
|
value: function (t, e, s) {
|
||||||
|
if (
|
||||||
|
('string' == typeof s
|
||||||
|
? (t.el = document.querySelector(s))
|
||||||
|
: (t.el = s),
|
||||||
|
(t.options = r({}, u['default'], e)),
|
||||||
|
(t.isInput = 'input' === t.el.tagName.toLowerCase()),
|
||||||
|
(t.attr = t.options.attr),
|
||||||
|
(t.bindInputFocusEvents = t.options.bindInputFocusEvents),
|
||||||
|
(t.showCursor = !t.isInput && t.options.showCursor),
|
||||||
|
(t.cursorChar = t.options.cursorChar),
|
||||||
|
(t.cursorBlinking = !0),
|
||||||
|
(t.elContent = t.attr
|
||||||
|
? t.el.getAttribute(t.attr)
|
||||||
|
: t.el.textContent),
|
||||||
|
(t.contentType = t.options.contentType),
|
||||||
|
(t.typeSpeed = t.options.typeSpeed),
|
||||||
|
(t.startDelay = t.options.startDelay),
|
||||||
|
(t.backSpeed = t.options.backSpeed),
|
||||||
|
(t.smartBackspace = t.options.smartBackspace),
|
||||||
|
(t.backDelay = t.options.backDelay),
|
||||||
|
(t.fadeOut = t.options.fadeOut),
|
||||||
|
(t.fadeOutClass = t.options.fadeOutClass),
|
||||||
|
(t.fadeOutDelay = t.options.fadeOutDelay),
|
||||||
|
(t.isPaused = !1),
|
||||||
|
(t.strings = t.options.strings.map(function (t) {
|
||||||
|
return t.trim()
|
||||||
|
})),
|
||||||
|
'string' == typeof t.options.stringsElement
|
||||||
|
? (t.stringsElement = document.querySelector(
|
||||||
|
t.options.stringsElement
|
||||||
|
))
|
||||||
|
: (t.stringsElement = t.options.stringsElement),
|
||||||
|
t.stringsElement)
|
||||||
|
) {
|
||||||
|
;(t.strings = []), (t.stringsElement.style.display = 'none')
|
||||||
|
var n = Array.prototype.slice.apply(
|
||||||
|
t.stringsElement.children
|
||||||
|
),
|
||||||
|
i = n.length
|
||||||
|
if (i)
|
||||||
|
for (var o = 0; o < i; o += 1) {
|
||||||
|
var a = n[o]
|
||||||
|
t.strings.push(a.innerHTML.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;(t.strPos = 0),
|
||||||
|
(t.arrayPos = 0),
|
||||||
|
(t.stopNum = 0),
|
||||||
|
(t.loop = t.options.loop),
|
||||||
|
(t.loopCount = t.options.loopCount),
|
||||||
|
(t.curLoop = 0),
|
||||||
|
(t.shuffle = t.options.shuffle),
|
||||||
|
(t.sequence = []),
|
||||||
|
(t.pause = {
|
||||||
|
status: !1,
|
||||||
|
typewrite: !0,
|
||||||
|
curString: '',
|
||||||
|
curStrPos: 0
|
||||||
|
}),
|
||||||
|
(t.typingComplete = !1)
|
||||||
|
for (var o in t.strings) t.sequence[o] = o
|
||||||
|
;(t.currentElContent = this.getCurrentElContent(t)),
|
||||||
|
(t.autoInsertCss = t.options.autoInsertCss),
|
||||||
|
this.appendAnimationCss(t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'getCurrentElContent',
|
||||||
|
value: function (t) {
|
||||||
|
var e = ''
|
||||||
|
return (e = t.attr
|
||||||
|
? t.el.getAttribute(t.attr)
|
||||||
|
: t.isInput
|
||||||
|
? t.el.value
|
||||||
|
: 'html' === t.contentType
|
||||||
|
? t.el.innerHTML
|
||||||
|
: t.el.textContent)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'appendAnimationCss',
|
||||||
|
value: function (t) {
|
||||||
|
var e = 'data-typed-js-css'
|
||||||
|
if (
|
||||||
|
t.autoInsertCss &&
|
||||||
|
(t.showCursor || t.fadeOut) &&
|
||||||
|
!document.querySelector('[' + e + ']')
|
||||||
|
) {
|
||||||
|
var s = document.createElement('style')
|
||||||
|
;(s.type = 'text/css'), s.setAttribute(e, !0)
|
||||||
|
var n = ''
|
||||||
|
t.showCursor &&
|
||||||
|
(n +=
|
||||||
|
'\n .typed-cursor{\n opacity: 1;\n }\n .typed-cursor.typed-cursor--blink{\n animation: typedjsBlink 0.7s infinite;\n -webkit-animation: typedjsBlink 0.7s infinite;\n animation: typedjsBlink 0.7s infinite;\n }\n @keyframes typedjsBlink{\n 50% { opacity: 0.0; }\n }\n @-webkit-keyframes typedjsBlink{\n 0% { opacity: 1; }\n 50% { opacity: 0.0; }\n 100% { opacity: 1; }\n }\n '),
|
||||||
|
t.fadeOut &&
|
||||||
|
(n +=
|
||||||
|
'\n .typed-fade-out{\n opacity: 0;\n transition: opacity .25s;\n }\n .typed-cursor.typed-cursor--blink.typed-fade-out{\n -webkit-animation: 0;\n animation: 0;\n }\n '),
|
||||||
|
0 !== s.length &&
|
||||||
|
((s.innerHTML = n), document.body.appendChild(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
t
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
e['default'] = l
|
||||||
|
var c = new l()
|
||||||
|
e.initializer = c
|
||||||
|
},
|
||||||
|
function (t, e) {
|
||||||
|
'use strict'
|
||||||
|
Object.defineProperty(e, '__esModule', { value: !0 })
|
||||||
|
var s = {
|
||||||
|
strings: [
|
||||||
|
'These are the default values...',
|
||||||
|
'You know what you should do?',
|
||||||
|
'Use your own!',
|
||||||
|
'Have a great day!'
|
||||||
|
],
|
||||||
|
stringsElement: null,
|
||||||
|
typeSpeed: 0,
|
||||||
|
startDelay: 0,
|
||||||
|
backSpeed: 0,
|
||||||
|
smartBackspace: !0,
|
||||||
|
shuffle: !1,
|
||||||
|
backDelay: 700,
|
||||||
|
fadeOut: !1,
|
||||||
|
fadeOutClass: 'typed-fade-out',
|
||||||
|
fadeOutDelay: 500,
|
||||||
|
loop: !1,
|
||||||
|
loopCount: 1 / 0,
|
||||||
|
showCursor: !0,
|
||||||
|
cursorChar: '|',
|
||||||
|
autoInsertCss: !0,
|
||||||
|
attr: null,
|
||||||
|
bindInputFocusEvents: !1,
|
||||||
|
contentType: 'html',
|
||||||
|
onBegin: function (t) {},
|
||||||
|
onComplete: function (t) {},
|
||||||
|
preStringTyped: function (t, e) {},
|
||||||
|
onStringTyped: function (t, e) {},
|
||||||
|
onLastStringBackspaced: function (t) {},
|
||||||
|
onTypingPaused: function (t, e) {},
|
||||||
|
onTypingResumed: function (t, e) {},
|
||||||
|
onReset: function (t) {},
|
||||||
|
onStop: function (t, e) {},
|
||||||
|
onStart: function (t, e) {},
|
||||||
|
onDestroy: function (t) {}
|
||||||
|
}
|
||||||
|
;(e['default'] = s), (t.exports = e['default'])
|
||||||
|
},
|
||||||
|
function (t, e) {
|
||||||
|
'use strict'
|
||||||
|
function s(t, e) {
|
||||||
|
if (!(t instanceof e))
|
||||||
|
throw new TypeError('Cannot call a class as a function')
|
||||||
|
}
|
||||||
|
Object.defineProperty(e, '__esModule', { value: !0 })
|
||||||
|
var n = (function () {
|
||||||
|
function t(t, e) {
|
||||||
|
for (var s = 0; s < e.length; s++) {
|
||||||
|
var n = e[s]
|
||||||
|
;(n.enumerable = n.enumerable || !1),
|
||||||
|
(n.configurable = !0),
|
||||||
|
'value' in n && (n.writable = !0),
|
||||||
|
Object.defineProperty(t, n.key, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return function (e, s, n) {
|
||||||
|
return s && t(e.prototype, s), n && t(e, n), e
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
i = (function () {
|
||||||
|
function t() {
|
||||||
|
s(this, t)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
n(t, [
|
||||||
|
{
|
||||||
|
key: 'typeHtmlChars',
|
||||||
|
value: function (t, e, s) {
|
||||||
|
if ('html' !== s.contentType) return e
|
||||||
|
var n = t.substr(e).charAt(0)
|
||||||
|
if ('<' === n || '&' === n) {
|
||||||
|
var i = ''
|
||||||
|
for (
|
||||||
|
i = '<' === n ? '>' : ';';
|
||||||
|
t.substr(e + 1).charAt(0) !== i &&
|
||||||
|
(e++, !(e + 1 > t.length));
|
||||||
|
|
||||||
|
);
|
||||||
|
e++
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'backSpaceHtmlChars',
|
||||||
|
value: function (t, e, s) {
|
||||||
|
if ('html' !== s.contentType) return e
|
||||||
|
var n = t.substr(e).charAt(0)
|
||||||
|
if ('>' === n || ';' === n) {
|
||||||
|
var i = ''
|
||||||
|
for (
|
||||||
|
i = '>' === n ? '<' : '&';
|
||||||
|
t.substr(e - 1).charAt(0) !== i && (e--, !(e < 0));
|
||||||
|
|
||||||
|
);
|
||||||
|
e--
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
t
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
e['default'] = i
|
||||||
|
var r = new i()
|
||||||
|
e.htmlParser = r
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
//# sourceMappingURL=typed.min.js.map
|
||||||
1
public/svg/xiaohongshu.svg
Normal file
1
public/svg/xiaohongshu.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1717576439356" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6277" width="200" height="200"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#333333" p-id="6278"></path><path d="M199.936 439.04H147.456c4.608 68.352-12.288 120.064-12.288 120.064l28.16 51.456c37.376-15.872 36.608-171.52 36.608-171.52m169.984 0H317.44s-0.768 155.648 36.352 171.52l28.16-51.456c0.256 0.256-16.64-51.712-12.032-120.064M232.96 595.712H199.936c0 38.656 32.512 41.984 32.512 41.984h16.64c33.536 0 34.816-38.4 34.816-38.4v-228.352H232.96v224.768z m176.128-109.568h34.048L417.28 539.392s-15.104 29.952 9.216 30.976h56.576l22.528-41.216h-28.416c-2.56 0-4.864-2.048-4.864-4.864 0-0.768 0.256-1.536 0.512-2.048l42.24-82.944H465.92l-2.816 5.888h-3.328c-2.56 0-4.864-2.048-4.864-4.864 0-0.768 0.256-1.536 0.512-2.048l35.328-67.328h-49.152l-41.984 84.48c0.512 0-14.592 29.696 9.472 30.72m7.68 100.608s-6.4 0.512-10.496-3.584l-25.344 48.896s4.096 5.632 12.8 5.632h62.976l26.88-50.944h-66.816z" fill="#FFFFFF" p-id="6279"></path><path d="M606.976 439.04h32.512v-52.224h-115.968v52.224h32.768v149.248h-48.64l-24.32 49.152h172.8v-49.152h-49.152v-149.248z m232.192 38.656h-5.888v-33.792c0-31.488-25.6-57.088-57.088-57.088h-19.712v-15.872h-50.176v15.872h-31.488v51.2h31.488v39.68h-50.176v51.2h50.176v109.056h50.176v-108.8h68.864c6.656 0 12.288 5.376 12.288 12.288v53.76h-47.872c0 23.808 19.2 43.008 43.008 43.008h13.056c23.808 0 43.008-19.2 43.008-43.008v-67.584c-0.256-27.904-22.272-49.92-49.664-49.92M756.48 437.76h17.92c4.352 0 7.68 3.584 7.68 7.68v32h-25.856V437.76z" fill="#FFFFFF" p-id="6280"></path><path d="M888.064 411.904c0-13.824-11.008-24.832-24.832-24.832-13.824 0-24.832 11.008-24.832 24.832v24.832h24.832c13.568 0 24.832-11.264 24.832-24.832" fill="#FFFFFF" p-id="6281"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
1
public/svg/zhishixingqiu.svg
Normal file
1
public/svg/zhishixingqiu.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1717576376677" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5034" width="200" height="200"><path d="M511.061333 801.92a291.157333 291.157333 0 0 1-290.858666-290.858667 291.157333 291.157333 0 0 1 338.517333-286.933333 26.752 26.752 0 0 1-8.704 52.736 237.653333 237.653333 0 0 0-276.309333 234.24 237.653333 237.653333 0 0 0 237.354666 237.354667 237.653333 237.653333 0 0 0 233.258667-281.770667 26.709333 26.709333 0 1 1 52.48-9.941333 291.157333 291.157333 0 0 1-285.781333 345.173333M694.613333 275.328a53.333333 53.333333 0 1 1 0 106.666667 53.333333 53.333333 0 0 1 0-106.666667M511.061333 0C228.821333 0 0 228.821333 0 511.061333c0 282.24 228.821333 511.061333 511.061333 511.061334 282.24 0 511.061333-228.821333 511.061334-511.061334C1022.122667 228.821333 793.301333 0 511.061333 0" fill="#556377" p-id="5035"></path></svg>
|
||||||
|
After Width: | Height: | Size: 889 B |
@@ -264,3 +264,11 @@ a.avatar-wrapper {
|
|||||||
writing-mode: vertical-rl; /* 竖向排列从右向左 */
|
writing-mode: vertical-rl; /* 竖向排列从右向左 */
|
||||||
text-orientation: upright; /* 文字方向正常 */
|
text-orientation: upright; /* 文字方向正常 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Chatbase 在移动端禁止遮挡 */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
button#chatbase-bubble-button {
|
||||||
|
margin-bottom: 42px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -523,8 +523,10 @@ summary > .notion-h {
|
|||||||
.notion-page {
|
.notion-page {
|
||||||
/* width: var(--notion-max-width); */
|
/* width: var(--notion-max-width); */
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
padding-left: calc(min(12px, 8vw));
|
padding-left: 0px !important;
|
||||||
padding-right: calc(min(12px, 8vw));
|
padding-right: 0px !important;
|
||||||
|
/* padding-left: calc(min(12px, 8vw)); */
|
||||||
|
/* padding-right: calc(min(12px, 8vw)); */
|
||||||
}
|
}
|
||||||
|
|
||||||
.notion-full-width {
|
.notion-full-width {
|
||||||
@@ -1449,6 +1451,7 @@ code[class*='language-'] {
|
|||||||
|
|
||||||
.notion-collection-card-property .notion-page-title-text {
|
.notion-collection-card-property .notion-page-title-text {
|
||||||
border-bottom: 0 none;
|
border-bottom: 0 none;
|
||||||
|
@apply dark:text-gray-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notion-collection-card-property
|
.notion-collection-card-property
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import ProductCard from './ProductCard'
|
|||||||
*/
|
*/
|
||||||
const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||||
const { NOTION_CONFIG } = useGlobal()
|
const { NOTION_CONFIG } = useGlobal()
|
||||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
const totalPage = Math.ceil(postCount / POSTS_PER_PAGE)
|
const totalPage = Math.ceil(postCount / POSTS_PER_PAGE)
|
||||||
const showPagination = postCount >= POSTS_PER_PAGE
|
const showPagination = postCount >= POSTS_PER_PAGE
|
||||||
if (!posts || posts.length === 0 || page > totalPage) {
|
if (!posts || posts.length === 0 || page > totalPage) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const BlogPostListScroll = ({
|
|||||||
siteInfo
|
siteInfo
|
||||||
}) => {
|
}) => {
|
||||||
const { NOTION_CONFIG } = useGlobal()
|
const { NOTION_CONFIG } = useGlobal()
|
||||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
|
|
||||||
const [page, updatePage] = useState(1)
|
const [page, updatePage] = useState(1)
|
||||||
const postsToShow = getListByPage(posts, page, POSTS_PER_PAGE)
|
const postsToShow = getListByPage(posts, page, POSTS_PER_PAGE)
|
||||||
|
|||||||
@@ -5,39 +5,101 @@ import { siteConfig } from '@/lib/config'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const SocialButton = () => {
|
const SocialButton = () => {
|
||||||
return <div className='w-full justify-center flex-wrap flex'>
|
return (
|
||||||
<div className='space-x-3 text-xl text-gray-600 dark:text-gray-300 '>
|
<div className='w-full justify-center flex-wrap flex'>
|
||||||
{siteConfig('CONTACT_GITHUB') && <a target='_blank' rel='noreferrer' title={'github'} href={siteConfig('CONTACT_GITHUB')} >
|
<div className='space-x-3 text-xl text-gray-600 dark:text-gray-300 '>
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-github dark:hover:text-red-400 hover:text-red-600'/>
|
{siteConfig('CONTACT_GITHUB') && (
|
||||||
</a>}
|
<a
|
||||||
{siteConfig('CONTACT_TWITTER') && <a target='_blank' rel='noreferrer' title={'twitter'} href={siteConfig('CONTACT_TWITTER')} >
|
target='_blank'
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-twitter dark:hover:text-red-400 hover:text-red-600'/>
|
rel='noreferrer'
|
||||||
</a>}
|
title={'github'}
|
||||||
{siteConfig('CONTACT_TELEGRAM') && <a target='_blank' rel='noreferrer' href={siteConfig('CONTACT_TELEGRAM')} title={'telegram'} >
|
href={siteConfig('CONTACT_GITHUB')}>
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-telegram dark:hover:text-red-400 hover:text-red-600'/>
|
<i className='transform hover:scale-125 duration-150 fab fa-github dark:hover:text-red-400 hover:text-red-600' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_LINKEDIN') && <a target='_blank' rel='noreferrer' href={siteConfig('CONTACT_LINKEDIN')} title={'linkIn'} >
|
)}
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-red-400 hover:text-red-600'/>
|
{siteConfig('CONTACT_TWITTER') && (
|
||||||
</a>}
|
<a
|
||||||
{siteConfig('CONTACT_WEIBO') && <a target='_blank' rel='noreferrer' title={'weibo'} href={siteConfig('CONTACT_WEIBO')} >
|
target='_blank'
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-weibo dark:hover:text-red-400 hover:text-red-600'/>
|
rel='noreferrer'
|
||||||
</a>}
|
title={'twitter'}
|
||||||
{siteConfig('CONTACT_INSTAGRAM') && <a target='_blank' rel='noreferrer' title={'instagram'} href={siteConfig('CONTACT_INSTAGRAM')} >
|
href={siteConfig('CONTACT_TWITTER')}>
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-instagram dark:hover:text-red-400 hover:text-red-600'/>
|
<i className='transform hover:scale-125 duration-150 fab fa-twitter dark:hover:text-red-400 hover:text-red-600' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_EMAIL') && <a target='_blank' rel='noreferrer' title={'email'} href={`mailto:${siteConfig('CONTACT_EMAIL')}`} >
|
)}
|
||||||
<i className='transform hover:scale-125 duration-150 fas fa-envelope dark:hover:text-red-400 hover:text-red-600'/>
|
{siteConfig('CONTACT_TELEGRAM') && (
|
||||||
</a>}
|
<a
|
||||||
{JSON.parse(siteConfig('ENABLE_RSS')) && <a target='_blank' rel='noreferrer' title={'RSS'} href={'/feed'} >
|
target='_blank'
|
||||||
<i className='transform hover:scale-125 duration-150 fas fa-rss dark:hover:text-red-400 hover:text-red-600'/>
|
rel='noreferrer'
|
||||||
</a>}
|
href={siteConfig('CONTACT_TELEGRAM')}
|
||||||
{siteConfig('CONTACT_BILIBILI') && <a target='_blank' rel='noreferrer' title={'bilibili'} href={siteConfig('CONTACT_BILIBILI')} >
|
title={'telegram'}>
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-bilibili dark:hover:text-red-400 hover:text-red-600'/>
|
<i className='transform hover:scale-125 duration-150 fab fa-telegram dark:hover:text-red-400 hover:text-red-600' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_YOUTUBE') && <a target='_blank' rel='noreferrer' title={'youtube'} href={siteConfig('CONTACT_YOUTUBE')} >
|
)}
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-youtube dark:hover:text-red-400 hover:text-red-600'/>
|
{siteConfig('CONTACT_LINKEDIN') && (
|
||||||
</a>}
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
href={siteConfig('CONTACT_LINKEDIN')}
|
||||||
|
title={'linkIn'}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_WEIBO') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'weibo'}
|
||||||
|
href={siteConfig('CONTACT_WEIBO')}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-weibo dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_INSTAGRAM') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'instagram'}
|
||||||
|
href={siteConfig('CONTACT_INSTAGRAM')}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-instagram dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_EMAIL') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'email'}
|
||||||
|
href={`mailto:${siteConfig('CONTACT_EMAIL')}`}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fas fa-envelope dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{JSON.parse(siteConfig('ENABLE_RSS')) && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'RSS'}
|
||||||
|
href={'/rss/feed.xml'}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fas fa-rss dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_BILIBILI') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'bilibili'}
|
||||||
|
href={siteConfig('CONTACT_BILIBILI')}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-bilibili dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_YOUTUBE') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'youtube'}
|
||||||
|
href={siteConfig('CONTACT_YOUTUBE')}>
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-youtube dark:hover:text-red-400 hover:text-red-600' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
}
|
}
|
||||||
export default SocialButton
|
export default SocialButton
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const BlogListPage = props => {
|
|||||||
const { locale, NOTION_CONFIG } = useGlobal()
|
const { locale, NOTION_CONFIG } = useGlobal()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const totalPage = Math.ceil(
|
const totalPage = Math.ceil(
|
||||||
postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
)
|
)
|
||||||
const currentPage = +page
|
const currentPage = +page
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ export const BlogListPage = props => {
|
|||||||
.split('?')[0]
|
.split('?')[0]
|
||||||
.replace(/\/page\/[1-9]\d*/, '')
|
.replace(/\/page\/[1-9]\d*/, '')
|
||||||
.replace(/\/$/, '')
|
.replace(/\/$/, '')
|
||||||
|
.replace('.html', '')
|
||||||
|
|
||||||
const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG)
|
const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG)
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ export const BlogListPage = props => {
|
|||||||
: `${pagePrefix}/page/${currentPage - 1}`,
|
: `${pagePrefix}/page/${currentPage - 1}`,
|
||||||
query: router.query.s ? { s: router.query.s } : {}
|
query: router.query.s ? { s: router.query.s } : {}
|
||||||
}}
|
}}
|
||||||
className={`${showPrev ? 'bg-black ' : 'bg-gray pointer-events-none '} text-white no-underline py-2 px-3 rounded`}>
|
className={`${showPrev ? 'bg-black dark:bg-hexo-black-gray' : 'bg-gray pointer-events-none invisible'} text-white no-underline py-2 px-3 rounded`}>
|
||||||
{locale.PAGINATION.PREV}
|
{locale.PAGINATION.PREV}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
@@ -52,7 +53,7 @@ export const BlogListPage = props => {
|
|||||||
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
||||||
query: router.query.s ? { s: router.query.s } : {}
|
query: router.query.s ? { s: router.query.s } : {}
|
||||||
}}
|
}}
|
||||||
className={`${showNext ? 'bg-black ' : 'bg-gray pointer-events-none '} text-white no-underline py-2 px-3 rounded`}>
|
className={`${showNext ? 'bg-black dark:bg-hexo-black-gray ' : 'bg-gray pointer-events-none invisible'} text-white no-underline py-2 px-3 rounded`}>
|
||||||
{locale.PAGINATION.NEXT}
|
{locale.PAGINATION.NEXT}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const BlogListScroll = props => {
|
|||||||
const { posts } = props
|
const { posts } = props
|
||||||
const { locale, NOTION_CONFIG } = useGlobal()
|
const { locale, NOTION_CONFIG } = useGlobal()
|
||||||
const [page, updatePage] = useState(1)
|
const [page, updatePage] = useState(1)
|
||||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
|
|
||||||
let hasMore = false
|
let hasMore = false
|
||||||
const postsToShow = posts
|
const postsToShow = posts
|
||||||
|
|||||||
94
themes/example/components/Catalog.js
Normal file
94
themes/example/components/Catalog.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import throttle from 'lodash.throttle'
|
||||||
|
import { uuidToId } from 'notion-utils'
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录导航组件
|
||||||
|
* @param toc
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const Catalog = ({ toc }) => {
|
||||||
|
// 监听滚动事件
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('scroll', actionSectionScrollSpy)
|
||||||
|
actionSectionScrollSpy()
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', actionSectionScrollSpy)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// 目录自动滚动
|
||||||
|
const tRef = useRef(null)
|
||||||
|
const tocIds = []
|
||||||
|
|
||||||
|
// 同步选中目录事件
|
||||||
|
const [activeSection, setActiveSection] = useState(null)
|
||||||
|
const throttleMs = 200
|
||||||
|
const actionSectionScrollSpy = useCallback(
|
||||||
|
throttle(() => {
|
||||||
|
const sections = document.getElementsByClassName('notion-h')
|
||||||
|
let prevBBox = null
|
||||||
|
let currentSectionId = activeSection
|
||||||
|
for (let i = 0; i < sections.length; ++i) {
|
||||||
|
const section = sections[i]
|
||||||
|
if (!section || !(section instanceof Element)) continue
|
||||||
|
if (!currentSectionId) {
|
||||||
|
currentSectionId = section.getAttribute('data-id')
|
||||||
|
}
|
||||||
|
const bbox = section.getBoundingClientRect()
|
||||||
|
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
|
||||||
|
const offset = Math.max(150, prevHeight / 4)
|
||||||
|
// GetBoundingClientRect returns values relative to viewport
|
||||||
|
if (bbox.top - offset < 0) {
|
||||||
|
currentSectionId = section.getAttribute('data-id')
|
||||||
|
prevBBox = bbox
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// No need to continue loop, if last element has been detected
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setActiveSection(currentSectionId)
|
||||||
|
const index = tocIds.indexOf(currentSectionId) || 0
|
||||||
|
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
|
||||||
|
}, throttleMs)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 无目录就直接返回空
|
||||||
|
if (!toc || toc.length < 1) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='px-3'>
|
||||||
|
<div
|
||||||
|
className='overflow-y-auto max-h-96 overscroll-none scroll-hidden'
|
||||||
|
ref={tRef}>
|
||||||
|
<nav className='h-full text-black dark:text-gray-300'>
|
||||||
|
{toc.map(tocItem => {
|
||||||
|
const id = uuidToId(tocItem.id)
|
||||||
|
tocIds.push(id)
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={id}
|
||||||
|
href={`#${id}`}
|
||||||
|
className={`notion-table-of-contents-item duration-300 transform font-light
|
||||||
|
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
marginLeft: tocItem.indentLevel * 16
|
||||||
|
}}
|
||||||
|
className={`truncate ${activeSection === id ? ' font-bold text-red-400 underline' : ''}`}>
|
||||||
|
{tocItem.text}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Catalog
|
||||||
@@ -4,12 +4,13 @@ import { MenuList } from './MenuList'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 网站顶部
|
* 网站顶部
|
||||||
|
* LOGO 和 菜单
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const Header = props => {
|
export const Header = props => {
|
||||||
return (
|
return (
|
||||||
<header className='w-full px-6 bg-white dark:bg-black relative z-20'>
|
<header className='w-full px-6 bg-white dark:bg-black relative z-20'>
|
||||||
<div className='container mx-auto max-w-4xl md:flex justify-between items-center'>
|
<div className='mx-auto max-w-4xl md:flex justify-between items-center'>
|
||||||
<Link
|
<Link
|
||||||
href='/'
|
href='/'
|
||||||
className='py-6 w-full text-center md:text-left md:w-auto text-gray-dark no-underline flex justify-center items-center'>
|
className='py-6 w-full text-center md:text-left md:w-auto text-gray-dark no-underline flex justify-center items-center'>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const MenuList = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className='w-full bg-white md:pt-0 px-6 relative z-20 border-t border-b border-gray-light dark:border-hexo-black-gray dark:bg-black'>
|
<nav className='w-full bg-white md:pt-0 px-6 relative z-20 border-t border-b border-gray-light dark:border-hexo-black-gray dark:bg-black'>
|
||||||
<div className='container mx-auto max-w-4xl md:flex justify-between items-center text-sm md:text-md md:justify-start'>
|
<div className='mx-auto max-w-4xl md:flex justify-between items-center text-sm md:text-md md:justify-start'>
|
||||||
<ul className='w-full text-center md:text-left flex flex-wrap justify-center items-stretch md:justify-start md:items-start'>
|
<ul className='w-full text-center md:text-left flex flex-wrap justify-center items-stretch md:justify-start md:items-start'>
|
||||||
{links.map((link, index) => (
|
{links.map((link, index) => (
|
||||||
<MenuItemDrop key={index} link={link} />
|
<MenuItemDrop key={index} link={link} />
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ const SearchInput = ({ currentTag, keyword, cRef }) => {
|
|||||||
const key = searchInputRef.current.value
|
const key = searchInputRef.current.value
|
||||||
if (key && key !== '') {
|
if (key && key !== '') {
|
||||||
router.push({ pathname: '/search/' + key }).then(r => {
|
router.push({ pathname: '/search/' + key }).then(r => {
|
||||||
console.log('搜索', key)
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
router.push({ pathname: '/' }).then(r => {
|
router.push({ pathname: '/' }).then(r => {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { siteConfig } from '@/lib/config'
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import CONFIG from '../config'
|
||||||
import Announcement from './Announcement'
|
import Announcement from './Announcement'
|
||||||
|
import Catalog from './Catalog'
|
||||||
const ExampleRecentComments = dynamic(
|
const ExampleRecentComments = dynamic(
|
||||||
() => import('./RecentCommentListForExample')
|
() => import('./RecentCommentListForExample')
|
||||||
)
|
)
|
||||||
@@ -13,10 +15,36 @@ const ExampleRecentComments = dynamic(
|
|||||||
*/
|
*/
|
||||||
export const SideBar = props => {
|
export const SideBar = props => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { latestPosts, categoryOptions, notice } = props
|
const { latestPosts, categoryOptions, notice, post } = props
|
||||||
|
// 评论相关
|
||||||
|
const COMMENT_WALINE_SERVER_URL = siteConfig(
|
||||||
|
'COMMENT_WALINE_SERVER_URL',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
const COMMENT_WALINE_RECENT = siteConfig('COMMENT_WALINE_RECENT', false)
|
||||||
|
|
||||||
|
// 文章详情页特殊布局
|
||||||
|
const HIDDEN_NOTIFICATION =
|
||||||
|
post && siteConfig('EXAMPLE_ARTICLE_HIDDEN_NOTIFICATION', false, CONFIG)
|
||||||
|
|
||||||
|
// 文章详情页左右布局改为上下布局
|
||||||
|
const LAYOUT_VERTICAL =
|
||||||
|
post && siteConfig('EXAMPLE_ARTICLE_LAYOUT_VERTICAL', false, CONFIG)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full md:w-64 sticky top-8'>
|
<>
|
||||||
<aside className='rounded shadow overflow-hidden mb-6'>
|
{/* 目录 */}
|
||||||
|
{post?.toc && post?.toc.length > 2 && (
|
||||||
|
<aside className='w-full rounded shadow overflow-hidden mb-6 pb-4'>
|
||||||
|
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
||||||
|
{locale.COMMON.TABLE_OF_CONTENTS}
|
||||||
|
</h3>
|
||||||
|
<Catalog toc={post?.toc} />
|
||||||
|
</aside>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 分类 */}
|
||||||
|
<aside className='w-full rounded shadow overflow-hidden mb-6'>
|
||||||
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
||||||
{locale.COMMON.CATEGORY}
|
{locale.COMMON.CATEGORY}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -34,7 +62,7 @@ export const SideBar = props => {
|
|||||||
{' '}
|
{' '}
|
||||||
<a
|
<a
|
||||||
href={`/category/${category.name}`}
|
href={`/category/${category.name}`}
|
||||||
className='text-gray-darkest text-sm'>
|
className='text-gray-darkest text-sm hover:underline'>
|
||||||
{category.name}({category.count})
|
{category.name}({category.count})
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -44,7 +72,9 @@ export const SideBar = props => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<aside className='rounded shadow overflow-hidden mb-6'>
|
|
||||||
|
{/* 最新文章 */}
|
||||||
|
<aside className='w-full rounded shadow overflow-hidden mb-6'>
|
||||||
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
||||||
{locale.COMMON.LATEST_POSTS}
|
{locale.COMMON.LATEST_POSTS}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -58,7 +88,7 @@ export const SideBar = props => {
|
|||||||
{' '}
|
{' '}
|
||||||
<a
|
<a
|
||||||
href={`/${p.slug}`}
|
href={`/${p.slug}`}
|
||||||
className='text-gray-darkest text-sm'>
|
className='text-gray-darkest text-sm hover:underline'>
|
||||||
{p.title}
|
{p.title}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -68,25 +98,29 @@ export const SideBar = props => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
{/* 公告 */}
|
||||||
{/* 公告栏 */}
|
{/* 公告栏 */}
|
||||||
<Announcement post={notice} />
|
{!HIDDEN_NOTIFICATION && <Announcement post={notice} />}
|
||||||
|
|
||||||
{/* 最近评论 */}
|
{/* 最近评论 */}
|
||||||
{siteConfig('COMMENT_WALINE_SERVER_URL') &&
|
{COMMENT_WALINE_SERVER_URL && COMMENT_WALINE_RECENT && (
|
||||||
siteConfig('COMMENT_WALINE_RECENT') && (
|
<aside className='w-full rounded shadow overflow-hidden mb-6'>
|
||||||
<aside className='rounded shadow overflow-hidden mb-6'>
|
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
||||||
<h3 className='text-sm bg-gray-100 text-gray-700 dark:bg-hexo-black-gray dark:text-gray-200 py-3 px-4 dark:border-hexo-black-gray border-b'>
|
{locale.COMMON.RECENT_COMMENTS}
|
||||||
{locale.COMMON.RECENT_COMMENTS}
|
</h3>
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div className='p-4'>
|
<div className='p-4'>
|
||||||
<ExampleRecentComments />
|
<ExampleRecentComments />
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
)}
|
)}
|
||||||
<aside className='rounded overflow-hidden mb-6'>
|
|
||||||
|
{/* 宠物挂件 */}
|
||||||
|
<aside
|
||||||
|
className={`rounded overflow-hidden mb-6 ${LAYOUT_VERTICAL ? 'hidden md:fixed right-4 bottom-20' : ''}`}>
|
||||||
<Live2D />
|
<Live2D />
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
48
themes/example/components/TitleBar.js
Normal file
48
themes/example/components/TitleBar.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import NotionIcon from '@/components/NotionIcon'
|
||||||
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import CONFIG from '../config'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题栏
|
||||||
|
*/
|
||||||
|
export default function TitleBar(props) {
|
||||||
|
const { post } = props
|
||||||
|
const { fullWidth, siteInfo } = useGlobal()
|
||||||
|
|
||||||
|
const title = post?.title || siteConfig('TITLE')
|
||||||
|
const description = post?.description || siteConfig('AUTHOR')
|
||||||
|
const headerImage = post?.pageCoverThumbnail
|
||||||
|
? post.pageCoverThumbnail
|
||||||
|
: siteInfo?.pageCover
|
||||||
|
|
||||||
|
const TITLE_BG = siteConfig('EXAMPLE_TITLE_IMAGE', false, CONFIG)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 标题栏 */}
|
||||||
|
{!fullWidth && (
|
||||||
|
<div className='relative overflow-hidden text-center px-6 py-12 mb-6 bg-gray-100 dark:bg-hexo-black-gray dark:border-hexo-black-gray border-b'>
|
||||||
|
<h1 className='title-1 relative text-xl md:text-4xl pb-4 z-10'>
|
||||||
|
{siteConfig('POST_TITLE_ICON') && (
|
||||||
|
<NotionIcon icon={post?.pageIcon} />
|
||||||
|
)}
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<p className='title-2 relative leading-loose text-gray-dark z-10'>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
{TITLE_BG && (
|
||||||
|
<>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={headerImage}
|
||||||
|
className='absolute object-cover top-0 left-0 w-full h-full select-none opacity-70 z-0'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,7 +8,12 @@ const CONFIG = {
|
|||||||
EXAMPLE_MENU_ARCHIVE: true, // 显示归档
|
EXAMPLE_MENU_ARCHIVE: true, // 显示归档
|
||||||
EXAMPLE_MENU_SEARCH: true, // 显示搜索
|
EXAMPLE_MENU_SEARCH: true, // 显示搜索
|
||||||
|
|
||||||
EXAMPLE_POST_LIST_COVER: true // 列表显示文章封面
|
EXAMPLE_POST_LIST_COVER: true, // 列表显示文章封面
|
||||||
|
|
||||||
|
EXAMPLE_TITLE_IMAGE: false, // 标题栏,是否背景图片
|
||||||
|
|
||||||
|
// 文章页面布局
|
||||||
|
EXAMPLE_ARTICLE_LAYOUT_VERTICAL: false, // 文章详情,左右布局改为上下布局
|
||||||
|
EXAMPLE_ARTICLE_HIDDEN_NOTIFICATION: false // 文章详情隐藏公告
|
||||||
}
|
}
|
||||||
export default CONFIG
|
export default CONFIG
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import Comment from '@/components/Comment'
|
import Comment from '@/components/Comment'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import NotionIcon from '@/components/NotionIcon'
|
|
||||||
import NotionPage from '@/components/NotionPage'
|
import NotionPage from '@/components/NotionPage'
|
||||||
import ShareBar from '@/components/ShareBar'
|
import ShareBar from '@/components/ShareBar'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
@@ -21,6 +20,7 @@ import { PostLock } from './components/PostLock'
|
|||||||
import { PostMeta } from './components/PostMeta'
|
import { PostMeta } from './components/PostMeta'
|
||||||
import SearchInput from './components/SearchInput'
|
import SearchInput from './components/SearchInput'
|
||||||
import { SideBar } from './components/SideBar'
|
import { SideBar } from './components/SideBar'
|
||||||
|
import TitleBar from './components/TitleBar'
|
||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
|
|
||||||
@@ -32,36 +32,15 @@ import { Style } from './style'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children } = props
|
const { children, post } = props
|
||||||
const { onLoading, fullWidth, locale } = useGlobal()
|
const { onLoading, fullWidth, locale } = useGlobal()
|
||||||
const router = useRouter()
|
|
||||||
const { post, category, tag } = props
|
|
||||||
|
|
||||||
const title = post?.title || siteConfig('TITLE')
|
// 文章详情页左右布局改为上下布局
|
||||||
const description = post?.description || siteConfig('AUTHOR')
|
const LAYOUT_VERTICAL =
|
||||||
|
post && siteConfig('EXAMPLE_ARTICLE_LAYOUT_VERTICAL', false, CONFIG)
|
||||||
|
|
||||||
// 顶部如果是按照分类或标签查看文章列表,列表顶部嵌入一个横幅
|
// 网站左右布局颠倒
|
||||||
// 如果是搜索,则列表顶部嵌入 搜索框
|
const LAYOUT_SIDEBAR_REVERSE = siteConfig('LAYOUT_SIDEBAR_REVERSE', false)
|
||||||
let slotTop = null
|
|
||||||
if (category) {
|
|
||||||
slotTop = (
|
|
||||||
<div className='pb-12'>
|
|
||||||
<i className='mr-1 fas fa-folder-open' />
|
|
||||||
{category}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else if (tag) {
|
|
||||||
slotTop = <div className='pb-12'>#{tag}</div>
|
|
||||||
} else if (props.slotTop) {
|
|
||||||
slotTop = props.slotTop
|
|
||||||
} else if (router.route === '/search') {
|
|
||||||
// 嵌入一个搜索框在顶部
|
|
||||||
slotTop = (
|
|
||||||
<div className='pb-12'>
|
|
||||||
<SearchInput {...props} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -71,33 +50,20 @@ const LayoutBase = props => {
|
|||||||
|
|
||||||
{/* 页头 */}
|
{/* 页头 */}
|
||||||
<Header {...props} />
|
<Header {...props} />
|
||||||
|
{/* 标题栏 */}
|
||||||
|
<TitleBar {...props} />
|
||||||
|
|
||||||
{/* 主体 */}
|
{/* 主体 */}
|
||||||
<div id='container-inner' className='w-full relative z-10'>
|
<div id='container-inner' className='w-full relative z-10'>
|
||||||
{/* 标题栏 */}
|
|
||||||
{!fullWidth && (
|
|
||||||
<div className='text-center px-6 py-12 mb-6 bg-gray-100 dark:bg-hexo-black-gray dark:border-hexo-black-gray border-b'>
|
|
||||||
<h1 className='text-xl md:text-4xl pb-4'>
|
|
||||||
{siteConfig('POST_TITLE_ICON') && (
|
|
||||||
<NotionIcon icon={post?.pageIcon} />
|
|
||||||
)}
|
|
||||||
{title}
|
|
||||||
</h1>
|
|
||||||
<p className='leading-loose text-gray-dark'>{description}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id='container-wrapper'
|
id='container-wrapper'
|
||||||
className={
|
className={`relative mx-auto justify-center md:flex py-8 px-2
|
||||||
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
${LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : ''}
|
||||||
? 'flex-row-reverse'
|
${LAYOUT_VERTICAL ? 'items-center flex-col' : 'items-start'}
|
||||||
: '') +
|
`}>
|
||||||
'relative container mx-auto justify-center md:flex items-start py-8 px-2'
|
|
||||||
}>
|
|
||||||
{/* 内容 */}
|
{/* 内容 */}
|
||||||
<div
|
<div
|
||||||
className={`w-full ${fullWidth ? '' : 'max-w-3xl'} xl:px-14 lg:px-4`}>
|
className={`${fullWidth ? '' : LAYOUT_VERTICAL ? 'max-w-5xl' : 'max-w-3xl'} w-full xl:px-14 lg:px-4`}>
|
||||||
<Transition
|
<Transition
|
||||||
show={!onLoading}
|
show={!onLoading}
|
||||||
appear={true}
|
appear={true}
|
||||||
@@ -109,13 +75,22 @@ const LayoutBase = props => {
|
|||||||
leaveTo='opacity-0 -translate-y-16'
|
leaveTo='opacity-0 -translate-y-16'
|
||||||
unmount={false}>
|
unmount={false}>
|
||||||
{/* 嵌入模块 */}
|
{/* 嵌入模块 */}
|
||||||
{slotTop}
|
{props.slotTop}
|
||||||
{children}
|
{children}
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 侧边栏 */}
|
{/* 侧边栏 */}
|
||||||
{!fullWidth && <SideBar {...props} />}
|
{!fullWidth && (
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
LAYOUT_VERTICAL
|
||||||
|
? 'flex space-x-0 md:space-x-2 md:flex-row flex-col w-full max-w-5xl justify-center xl:px-14 lg:px-4'
|
||||||
|
: 'md:w-64 sticky top-8'
|
||||||
|
}`}>
|
||||||
|
<SideBar {...props} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -150,8 +125,20 @@ const LayoutIndex = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutPostList = props => {
|
const LayoutPostList = props => {
|
||||||
|
const { category, tag } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* 显示分类 */}
|
||||||
|
{category && (
|
||||||
|
<div className='pb-12'>
|
||||||
|
<i className='mr-1 fas fa-folder-open' />
|
||||||
|
{category}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 显示标签 */}
|
||||||
|
{tag && <div className='pb-12'>#{tag}</div>}
|
||||||
|
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
<BlogListPage {...props} />
|
<BlogListPage {...props} />
|
||||||
) : (
|
) : (
|
||||||
@@ -192,7 +179,7 @@ const LayoutSlug = props => {
|
|||||||
{lock ? (
|
{lock ? (
|
||||||
<PostLock validPassword={validPassword} />
|
<PostLock validPassword={validPassword} />
|
||||||
) : (
|
) : (
|
||||||
<div id='article-wrapper' className='px-2'>
|
<div id='article-wrapper'>
|
||||||
<PostMeta post={post} />
|
<PostMeta post={post} />
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
<ShareBar post={post} />
|
<ShareBar post={post} />
|
||||||
@@ -237,7 +224,14 @@ const LayoutSearch = props => {
|
|||||||
}
|
}
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
return <LayoutPostList {...props} />
|
return (
|
||||||
|
<>
|
||||||
|
<div className='pb-12'>
|
||||||
|
<SearchInput {...props} />
|
||||||
|
</div>
|
||||||
|
<LayoutPostList {...props} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import Logo from './Logo'
|
|
||||||
import GroupCategory from './GroupCategory'
|
|
||||||
import { MenuList } from './MenuList'
|
|
||||||
import GroupTag from './GroupTag'
|
|
||||||
import SearchInput from './SearchInput'
|
|
||||||
import SiteInfo from './SiteInfo'
|
|
||||||
import Catalog from './Catalog'
|
|
||||||
import Announcement from './Announcement'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import DarkModeButton from '@/components/DarkModeButton'
|
import DarkModeButton from '@/components/DarkModeButton'
|
||||||
import SocialButton from './SocialButton'
|
|
||||||
import CONFIG from '@/themes/fukasawa/config'
|
|
||||||
import { AdSlot } from '@/components/GoogleAdsense'
|
import { AdSlot } from '@/components/GoogleAdsense'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import MailChimpForm from './MailChimpForm'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import CONFIG from '@/themes/fukasawa/config'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import Announcement from './Announcement'
|
||||||
|
import Catalog from './Catalog'
|
||||||
|
import GroupCategory from './GroupCategory'
|
||||||
|
import GroupTag from './GroupTag'
|
||||||
|
import Logo from './Logo'
|
||||||
|
import MailChimpForm from './MailChimpForm'
|
||||||
|
import { MenuList } from './MenuList'
|
||||||
|
import SearchInput from './SearchInput'
|
||||||
|
import SiteInfo from './SiteInfo'
|
||||||
|
import SocialButton from './SocialButton'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 侧边栏
|
* 侧边栏
|
||||||
@@ -24,16 +24,41 @@ import { debounce } from 'lodash'
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function AsideLeft(props) {
|
function AsideLeft(props) {
|
||||||
const { tagOptions, currentTag, categoryOptions, currentCategory, post, slot, notice } = props
|
const {
|
||||||
|
tagOptions,
|
||||||
|
currentTag,
|
||||||
|
categoryOptions,
|
||||||
|
currentCategory,
|
||||||
|
post,
|
||||||
|
slot,
|
||||||
|
notice
|
||||||
|
} = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { fullWidth } = useGlobal()
|
const { fullWidth } = useGlobal()
|
||||||
|
|
||||||
const FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT = fullWidth || siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT', null, CONFIG)
|
const FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT =
|
||||||
|
fullWidth ||
|
||||||
|
siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT', null, CONFIG)
|
||||||
|
|
||||||
|
const FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL = siteConfig(
|
||||||
|
'FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL',
|
||||||
|
false,
|
||||||
|
CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
const FUKASAWA_SIDEBAR_COLLAPSE_BUTTON = siteConfig(
|
||||||
|
'FUKASAWA_SIDEBAR_COLLAPSE_BUTTON',
|
||||||
|
null,
|
||||||
|
CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
// 侧边栏折叠从 本地存储中获取 open 状态的初始值
|
// 侧边栏折叠从 本地存储中获取 open 状态的初始值
|
||||||
const [isCollapsed, setIsCollapse] = useState(() => {
|
const [isCollapsed, setIsCollapse] = useState(() => {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
return localStorage.getItem('fukasawa-sidebar-collapse') === 'true' || FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT
|
return (
|
||||||
|
localStorage.getItem('fukasawa-sidebar-collapse') === 'true' ||
|
||||||
|
FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT
|
return FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT
|
||||||
})
|
})
|
||||||
@@ -45,8 +70,8 @@ function AsideLeft(props) {
|
|||||||
}
|
}
|
||||||
}, [isCollapsed])
|
}, [isCollapsed])
|
||||||
|
|
||||||
|
const isReverse = siteConfig('LAYOUT_SIDEBAR_REVERSE')
|
||||||
const position = useMemo(() => {
|
const position = useMemo(() => {
|
||||||
const isReverse = JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
if (isReverse) {
|
if (isReverse) {
|
||||||
return 'right-2'
|
return 'right-2'
|
||||||
@@ -69,7 +94,7 @@ function AsideLeft(props) {
|
|||||||
|
|
||||||
// 自动折叠侧边栏 onResize 窗口宽度小于1366 || 滚动条滚动至页面的300px时 ; 将open设置为false
|
// 自动折叠侧边栏 onResize 窗口宽度小于1366 || 滚动条滚动至页面的300px时 ; 将open设置为false
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL', false, CONFIG)) {
|
if (!FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const handleResize = debounce(() => {
|
const handleResize = debounce(() => {
|
||||||
@@ -93,72 +118,88 @@ function AsideLeft(props) {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
return <div className={`sideLeft relative ${isCollapsed ? 'w-0' : 'w-80'} duration-300 transition-all bg-white dark:bg-hexo-black-gray min-h-screen hidden lg:block z-20`}>
|
<div
|
||||||
{/* 折叠按钮 */}
|
className={`sideLeft relative ${isCollapsed ? 'w-0' : 'w-80'} duration-300 transition-all bg-white dark:bg-hexo-black-gray min-h-screen hidden lg:block z-20`}>
|
||||||
{siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_BUTTON', null, CONFIG) && <div className={`${position} hidden lg:block fixed top-0 cursor-pointer hover:scale-110 duration-300 px-3 py-2 dark:text-white`} onClick={toggleOpen}>
|
{/* 悬浮的折叠按钮 */}
|
||||||
{isCollapsed ? <i className="fa-solid fa-indent text-xl"></i> : <i className='fas fa-bars text-xl'></i>}
|
{FUKASAWA_SIDEBAR_COLLAPSE_BUTTON && (
|
||||||
</div>}
|
<div
|
||||||
|
className={`${position} hidden lg:block fixed top-0 cursor-pointer hover:scale-110 duration-300 px-3 py-2 dark:text-white`}
|
||||||
<div className={`h-full ${isCollapsed ? 'hidden' : 'p-8'}`}>
|
onClick={toggleOpen}>
|
||||||
|
{isCollapsed ? (
|
||||||
<Logo {...props} />
|
<i className='fa-solid fa-indent text-xl'></i>
|
||||||
|
) : (
|
||||||
<section className='siteInfo flex flex-col dark:text-gray-300 pt-8'>
|
<i className='fas fa-bars text-xl'></i>
|
||||||
{siteConfig('DESCRIPTION')}
|
)}
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className='flex flex-col text-gray-600'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<MenuList {...props} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className='flex flex-col text-gray-600'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<SearchInput {...props} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className='flex flex-col dark:text-gray-300'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<Announcement post={notice} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<MailChimpForm />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<AdSlot type='in-article' />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{router.asPath !== '/tag' && <section className='flex flex-col'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<GroupTag tags={tagOptions} currentTag={currentTag} />
|
|
||||||
</section>}
|
|
||||||
|
|
||||||
{router.asPath !== '/category' && <section className='flex flex-col'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<GroupCategory categories={categoryOptions} currentCategory={currentCategory} />
|
|
||||||
</section>}
|
|
||||||
|
|
||||||
<section className='flex flex-col'>
|
|
||||||
<div className='w-12 my-4' />
|
|
||||||
<SocialButton />
|
|
||||||
<SiteInfo />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className='flex justify-center dark:text-gray-200 pt-4'>
|
|
||||||
<DarkModeButton />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className='sticky top-0 pt-12 flex flex-col max-h-screen '>
|
|
||||||
<Catalog toc={post?.toc} />
|
|
||||||
<div className='flex justify-center'>
|
|
||||||
<div>{slot}</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={`h-full ${isCollapsed ? 'hidden' : 'p-8'}`}>
|
||||||
|
<Logo {...props} />
|
||||||
|
|
||||||
|
<section className='siteInfo flex flex-col dark:text-gray-300 pt-8'>
|
||||||
|
{siteConfig('DESCRIPTION')}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='flex flex-col text-gray-600'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<MenuList {...props} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='flex flex-col text-gray-600'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<SearchInput {...props} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='flex flex-col dark:text-gray-300'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<Announcement post={notice} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<MailChimpForm />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<AdSlot type='in-article' />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{router.asPath !== '/tag' && (
|
||||||
|
<section className='flex flex-col'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<GroupTag tags={tagOptions} currentTag={currentTag} />
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{router.asPath !== '/category' && (
|
||||||
|
<section className='flex flex-col'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<GroupCategory
|
||||||
|
categories={categoryOptions}
|
||||||
|
currentCategory={currentCategory}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<section className='flex flex-col'>
|
||||||
|
<div className='w-12 my-4' />
|
||||||
|
<SocialButton />
|
||||||
|
<SiteInfo />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='flex justify-center dark:text-gray-200 pt-4'>
|
||||||
|
<DarkModeButton />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='sticky top-0 pt-12 flex flex-col max-h-screen '>
|
||||||
|
<Catalog toc={post?.toc} />
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<div>{slot}</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AsideLeft
|
export default AsideLeft
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import LazyImage from '@/components/LazyImage'
|
import LazyImage from '@/components/LazyImage'
|
||||||
import NotionIcon from '@/components/NotionIcon'
|
import NotionIcon from '@/components/NotionIcon'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import CONFIG from '../config'
|
import CONFIG from '../config'
|
||||||
import TagItemMini from './TagItemMini'
|
import TagItemMini from './TagItemMini'
|
||||||
@@ -10,7 +11,8 @@ import TagItemMini from './TagItemMini'
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
const BlogCard = ({ showAnimate, post, showSummary }) => {
|
||||||
|
const {siteInfo} =useGlobal()
|
||||||
const showPreview =
|
const showPreview =
|
||||||
siteConfig('FUKASAWA_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
siteConfig('FUKASAWA_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||||
// fukasawa 强制显示图片
|
// fukasawa 强制显示图片
|
||||||
@@ -24,11 +26,12 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
|||||||
const showPageCover =
|
const showPageCover =
|
||||||
siteConfig('FUKASAWA_POST_LIST_COVER', null, CONFIG) &&
|
siteConfig('FUKASAWA_POST_LIST_COVER', null, CONFIG) &&
|
||||||
post?.pageCoverThumbnail
|
post?.pageCoverThumbnail
|
||||||
|
|
||||||
const FUKASAWA_POST_LIST_ANIMATION = siteConfig(
|
const FUKASAWA_POST_LIST_ANIMATION = siteConfig(
|
||||||
'FUKASAWA_POST_LIST_ANIMATION',
|
'FUKASAWA_POST_LIST_ANIMATION',
|
||||||
null,
|
null,
|
||||||
CONFIG
|
CONFIG
|
||||||
)
|
) || showAnimate
|
||||||
|
|
||||||
// 动画样式 首屏卡片不用,后面翻出来的加动画
|
// 动画样式 首屏卡片不用,后面翻出来的加动画
|
||||||
const aosProps = FUKASAWA_POST_LIST_ANIMATION
|
const aosProps = FUKASAWA_POST_LIST_ANIMATION
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ import PaginationSimple from './PaginationSimple'
|
|||||||
*/
|
*/
|
||||||
const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||||
const { NOTION_CONFIG } = useGlobal()
|
const { NOTION_CONFIG } = useGlobal()
|
||||||
|
const postsPerPage = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
const totalPage = Math.ceil(
|
const totalPage = Math.ceil(
|
||||||
postCount / parseInt(siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG))
|
postCount / postsPerPage
|
||||||
)
|
)
|
||||||
const showNext = page < totalPage
|
const showNext = page < totalPage
|
||||||
|
|
||||||
@@ -33,23 +34,27 @@ const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
|||||||
return () => window.removeEventListener('resize', handleResize)
|
return () => window.removeEventListener('resize', handleResize)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文章重新布局,使纵向排列看起来是横向排列
|
* 文章重新布局,使纵向排列看起来是横向排列
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const count = posts?.length || 0
|
const count = posts?.length || 0;
|
||||||
const rows = Math.ceil(count / columns)
|
const rows = Math.ceil(count / columns);
|
||||||
const newFilterPosts = []
|
const newFilterPosts = new Array(count);
|
||||||
for (let i = 0; i < columns; i++) {
|
|
||||||
for (let j = 0; j < rows; j++) {
|
let index = 0;
|
||||||
const index = j * columns + i
|
for (let col = 0; col < columns; col++) {
|
||||||
if (index < count) {
|
for (let row = 0; row < rows; row++) {
|
||||||
newFilterPosts.push(deepClone(posts[index]))
|
const sourceIndex = row * columns + col;
|
||||||
|
if (sourceIndex < count) {
|
||||||
|
newFilterPosts[index] = deepClone(posts[sourceIndex]);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setFilterPosts(newFilterPosts)
|
|
||||||
}, [columns, posts])
|
setFilterPosts(newFilterPosts);
|
||||||
|
}, [columns, posts]);
|
||||||
|
|
||||||
if (!filterPosts || filterPosts.length === 0) {
|
if (!filterPosts || filterPosts.length === 0) {
|
||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
|
|||||||
@@ -1,41 +1,36 @@
|
|||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config';
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global';
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import throttle from 'lodash.throttle';
|
||||||
import BlogCard from './BlogCard'
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import BlogPostListEmpty from './BlogListEmpty'
|
import BlogCard from './BlogCard';
|
||||||
|
import BlogPostListEmpty from './BlogListEmpty';
|
||||||
|
|
||||||
|
const BlogListScroll = ({ posts }) => {
|
||||||
|
const { locale, NOTION_CONFIG } = useGlobal();
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG);
|
||||||
|
const [filterPostsGroups, setFilterPostsGroups] = useState([]);
|
||||||
|
|
||||||
|
// 每页显示的文章数量
|
||||||
|
const postsPerPage = POSTS_PER_PAGE;
|
||||||
|
|
||||||
|
// 计算总页数
|
||||||
|
const totalPages = Math.ceil(posts.length / postsPerPage);
|
||||||
|
|
||||||
|
// 加载更多文章
|
||||||
|
const loadMorePosts = () => {
|
||||||
|
if (page < totalPages) {
|
||||||
|
setPage(page + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文章列表分页表格
|
|
||||||
* @param page 当前页
|
|
||||||
* @param posts 所有文章
|
|
||||||
* @param tags 所有标签
|
|
||||||
* @returns {JSX.Element}
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
const BlogListScroll = props => {
|
|
||||||
const { posts = [], siteInfo } = props
|
|
||||||
const { locale, NOTION_CONFIG } = useGlobal()
|
|
||||||
const targetRef = useRef(null)
|
const targetRef = useRef(null)
|
||||||
const [page, updatePage] = useState(1)
|
|
||||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
|
||||||
|
|
||||||
let hasMore = false
|
|
||||||
const postsToShow = posts
|
|
||||||
? Object.assign(posts).slice(0, POSTS_PER_PAGE * page)
|
|
||||||
: []
|
|
||||||
|
|
||||||
if (posts) {
|
|
||||||
const totalCount = posts.length
|
|
||||||
hasMore = page * POSTS_PER_PAGE < totalCount
|
|
||||||
}
|
|
||||||
const handleGetMore = () => {
|
|
||||||
if (!hasMore) return
|
|
||||||
updatePage(page + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听滚动自动分页加载
|
// 监听滚动自动分页加载
|
||||||
const scrollTrigger = () => {
|
const scrollTrigger = useCallback(
|
||||||
requestAnimationFrame(() => {
|
throttle(() => {
|
||||||
const scrollS = window.scrollY + window.outerHeight
|
const scrollS = window.scrollY + window.outerHeight
|
||||||
const clientHeight = targetRef
|
const clientHeight = targetRef
|
||||||
? targetRef.current
|
? targetRef.current
|
||||||
@@ -43,47 +38,64 @@ const BlogListScroll = props => {
|
|||||||
: 0
|
: 0
|
||||||
: 0
|
: 0
|
||||||
if (scrollS > clientHeight + 100) {
|
if (scrollS > clientHeight + 100) {
|
||||||
handleGetMore()
|
loadMorePosts()
|
||||||
}
|
}
|
||||||
})
|
}, 500)
|
||||||
}
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.addEventListener('scroll', scrollTrigger)
|
window.addEventListener('scroll', scrollTrigger)
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('scroll', scrollTrigger)
|
window.removeEventListener('scroll', scrollTrigger)
|
||||||
}
|
}
|
||||||
}, [])
|
})
|
||||||
|
|
||||||
|
// 根据当前页和每页文章数截取应该显示的文章
|
||||||
|
useEffect(() => {
|
||||||
|
const startIndex = (page - 1) * postsPerPage;
|
||||||
|
const endIndex = startIndex + postsPerPage;
|
||||||
|
const postsToShow = posts.slice(startIndex, endIndex);
|
||||||
|
const columns = 3; // 假设有3列
|
||||||
|
|
||||||
|
// 重新排列文章,保证列优先顺序
|
||||||
|
const newFilterPosts = [];
|
||||||
|
for (let col = 0; col < columns; col++) {
|
||||||
|
for (let i = col; i < postsToShow.length; i += columns) {
|
||||||
|
newFilterPosts.push(postsToShow[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilterPostsGroups((prev) => [...prev, newFilterPosts]);
|
||||||
|
}, [posts, page]);
|
||||||
|
|
||||||
if (!posts || posts.length === 0) {
|
if (!posts || posts.length === 0) {
|
||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id='posts-wrapper' ref={targetRef} className='grid-container'>
|
<div ref={targetRef}>
|
||||||
{/* 文章列表 */}
|
{filterPostsGroups.map((group, groupIndex) => (
|
||||||
{postsToShow?.map(post => (
|
<div key={groupIndex} id="posts-wrapper" className="grid-container mb-10">
|
||||||
<div
|
{group.map((post) => (
|
||||||
key={post.id}
|
<div
|
||||||
className='grid-item justify-center flex'
|
key={post.id}
|
||||||
style={{ breakInside: 'avoid' }}>
|
className="grid-item justify-center flex"
|
||||||
<BlogCard
|
style={{ breakInside: 'avoid' }}
|
||||||
index={posts.indexOf(post)}
|
>
|
||||||
key={post.id}
|
<BlogCard key={post.id} post={post} showAnimate={groupIndex > 0}/>
|
||||||
post={post}
|
</div>
|
||||||
siteInfo={siteInfo}
|
))}
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className='w-full my-4 py-4 text-center cursor-pointer '
|
className="w-full my-4 py-4 text-center cursor-pointer"
|
||||||
onClick={handleGetMore}>
|
onClick={loadMorePosts}
|
||||||
{' '}
|
>
|
||||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
{page < totalPages ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default BlogListScroll;
|
||||||
|
|
||||||
export default BlogListScroll
|
|
||||||
|
|||||||
64
themes/fukasawa/components/Header.js
Normal file
64
themes/fukasawa/components/Header.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import Collapse from '@/components/Collapse'
|
||||||
|
import { useRef, useState } from 'react'
|
||||||
|
import Logo from './Logo'
|
||||||
|
import { MenuList } from './MenuList'
|
||||||
|
import SearchInput from './SearchInput'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顶部导航
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const Header = props => {
|
||||||
|
const [isOpen, changeShow] = useState(false)
|
||||||
|
const collapseRef = useRef(null)
|
||||||
|
|
||||||
|
const toggleMenuOpen = () => {
|
||||||
|
changeShow(!isOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id='top-nav' className='z-40 block lg:hidden'>
|
||||||
|
{/* 导航栏 */}
|
||||||
|
<div
|
||||||
|
id='sticky-nav'
|
||||||
|
className={
|
||||||
|
'relative w-full top-0 z-20 transform duration-500 bg-white dark:bg-black'
|
||||||
|
}>
|
||||||
|
<Collapse type='vertical' isOpen={isOpen} collapseRef={collapseRef}>
|
||||||
|
<div className='py-1 px-5'>
|
||||||
|
<MenuList
|
||||||
|
{...props}
|
||||||
|
onHeightChange={param =>
|
||||||
|
collapseRef.current?.updateCollapseHeight(param)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchInput {...props} />
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
<div className='w-full flex justify-between items-center p-4 '>
|
||||||
|
{/* 左侧LOGO 标题 */}
|
||||||
|
<div className='flex flex-none flex-grow-0'>
|
||||||
|
<Logo {...props} />
|
||||||
|
</div>
|
||||||
|
<div className='flex'></div>
|
||||||
|
|
||||||
|
{/* 右侧功能 */}
|
||||||
|
<div className='mr-1 flex justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
||||||
|
<div
|
||||||
|
onClick={toggleMenuOpen}
|
||||||
|
className='cursor-pointer text-lg p-2'>
|
||||||
|
{isOpen ? (
|
||||||
|
<i className='fas fa-times' />
|
||||||
|
) : (
|
||||||
|
<i className='fas fa-bars' />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
@@ -7,9 +7,11 @@ export const MenuItemDrop = ({ link }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
onMouseOver={() => changeShow(true)}
|
onMouseOver={() => {
|
||||||
|
changeShow(true)
|
||||||
|
}}
|
||||||
onMouseOut={() => changeShow(false)}
|
onMouseOut={() => changeShow(false)}
|
||||||
className='relative py-1 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center '>
|
className='relative py-1 mb-1.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center '>
|
||||||
{!hasSubMenu && (
|
{!hasSubMenu && (
|
||||||
<Link
|
<Link
|
||||||
href={link?.href}
|
href={link?.href}
|
||||||
@@ -33,7 +35,7 @@ export const MenuItemDrop = ({ link }) => {
|
|||||||
{hasSubMenu && (
|
{hasSubMenu && (
|
||||||
<div className='text-right'>
|
<div className='text-right'>
|
||||||
<i
|
<i
|
||||||
className={`px-2 fas fa-chevron-right duration-500 transition-all ${show ? ' rotate-180' : ''}`}></i>
|
className={`px-2 fas fa-chevron-left duration-500 transition-all ${show ? ' rotate-180' : ''}`}></i>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +44,7 @@ export const MenuItemDrop = ({ link }) => {
|
|||||||
{/* 子菜单 */}
|
{/* 子菜单 */}
|
||||||
{hasSubMenu && (
|
{hasSubMenu && (
|
||||||
<ul
|
<ul
|
||||||
className={`${show ? 'visible opacity-100 left-52' : 'invisible opacity-0 left-40'} z-20 py-1 absolute right-0 top-0 w-full border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 drop-shadow-lg `}>
|
className={`${show ? 'visible opacity-100 left-72' : 'invisible opacity-0 left-80'} z-20 p-2 absolute right-0 top-0 w-full border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 drop-shadow-lg `}>
|
||||||
{link?.subMenus?.map((sLink, index) => {
|
{link?.subMenus?.map((sLink, index) => {
|
||||||
return (
|
return (
|
||||||
<li key={index}>
|
<li key={index}>
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ export const MenuList = props => {
|
|||||||
name: locale.NAV.ARCHIVE,
|
name: locale.NAV.ARCHIVE,
|
||||||
href: '/archive',
|
href: '/archive',
|
||||||
show: siteConfig('FUKASAWA_MENU_ARCHIVE', null, CONFIG)
|
show: siteConfig('FUKASAWA_MENU_ARCHIVE', null, CONFIG)
|
||||||
},
|
|
||||||
{
|
|
||||||
name: locale.NAV.SEARCH,
|
|
||||||
href: '/search',
|
|
||||||
show: siteConfig('FUKASAWA_MENU_SEARCH', null, CONFIG)
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -6,39 +6,101 @@ import { siteConfig } from '@/lib/config'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const SocialButton = () => {
|
const SocialButton = () => {
|
||||||
return <div className='w-52 flex-wrap flex'>
|
return (
|
||||||
<div className='space-x-3 text-lg text-gray-500 dark:text-gray-400'>
|
<div className='w-52 flex-wrap flex'>
|
||||||
{siteConfig('CONTACT_GITHUB') && <a target='_blank' rel='noreferrer' title={'github'} href={siteConfig('CONTACT_GITHUB')} >
|
<div className='space-x-3 text-lg text-gray-500 dark:text-gray-400'>
|
||||||
<i className='fab fa-github transform hover:scale-125 duration-150'/>
|
{siteConfig('CONTACT_GITHUB') && (
|
||||||
</a>}
|
<a
|
||||||
{siteConfig('CONTACT_TWITTER') && <a target='_blank' rel='noreferrer' title={'twitter'} href={siteConfig('CONTACT_TWITTER')} >
|
target='_blank'
|
||||||
<i className='fab fa-twitter transform hover:scale-125 duration-150'/>
|
rel='noreferrer'
|
||||||
</a>}
|
title={'github'}
|
||||||
{siteConfig('CONTACT_TELEGRAM') && <a target='_blank' rel='noreferrer' href={siteConfig('CONTACT_TELEGRAM')} title={'telegram'} >
|
href={siteConfig('CONTACT_GITHUB')}>
|
||||||
<i className='fab fa-telegram transform hover:scale-125 duration-150'/>
|
<i className='fab fa-github transform hover:scale-125 duration-150' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_LINKEDIN') && <a target='_blank' rel='noreferrer' href={siteConfig('CONTACT_LINKEDIN')} title={'linkedIn'} >
|
)}
|
||||||
<i className='fab fa-linkedin transform hover:scale-125 duration-150'/>
|
{siteConfig('CONTACT_TWITTER') && (
|
||||||
</a>}
|
<a
|
||||||
{siteConfig('CONTACT_WEIBO') && <a target='_blank' rel='noreferrer' title={'weibo'} href={siteConfig('CONTACT_WEIBO')} >
|
target='_blank'
|
||||||
<i className='fab fa-weibo transform hover:scale-125 duration-150'/>
|
rel='noreferrer'
|
||||||
</a>}
|
title={'twitter'}
|
||||||
{siteConfig('CONTACT_INSTAGRAM') && <a target='_blank' rel='noreferrer' title={'instagram'} href={siteConfig('CONTACT_INSTAGRAM')} >
|
href={siteConfig('CONTACT_TWITTER')}>
|
||||||
<i className='fab fa-instagram transform hover:scale-125 duration-150'/>
|
<i className='fab fa-twitter transform hover:scale-125 duration-150' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_EMAIL') && <a target='_blank' rel='noreferrer' title={'email'} href={`mailto:${siteConfig('CONTACT_EMAIL')}`} >
|
)}
|
||||||
<i className='fas fa-envelope transform hover:scale-125 duration-150'/>
|
{siteConfig('CONTACT_TELEGRAM') && (
|
||||||
</a>}
|
<a
|
||||||
{JSON.parse(siteConfig('ENABLE_RSS')) && <a target='_blank' rel='noreferrer' title={'RSS'} href={'/feed'} >
|
target='_blank'
|
||||||
<i className='fas fa-rss transform hover:scale-125 duration-150'/>
|
rel='noreferrer'
|
||||||
</a>}
|
href={siteConfig('CONTACT_TELEGRAM')}
|
||||||
{siteConfig('CONTACT_BILIBILI') && <a target='_blank' rel='noreferrer' title={'bilibili'} href={siteConfig('CONTACT_BILIBILI')} >
|
title={'telegram'}>
|
||||||
<i className='fab fa-bilibili transform hover:scale-125 duration-150'/>
|
<i className='fab fa-telegram transform hover:scale-125 duration-150' />
|
||||||
</a>}
|
</a>
|
||||||
{siteConfig('CONTACT_YOUTUBE') && <a target='_blank' rel='noreferrer' title={'youtube'} href={siteConfig('CONTACT_YOUTUBE')} >
|
)}
|
||||||
<i className='fab fa-youtube transform hover:scale-125 duration-150'/>
|
{siteConfig('CONTACT_LINKEDIN') && (
|
||||||
</a>}
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
href={siteConfig('CONTACT_LINKEDIN')}
|
||||||
|
title={'linkedIn'}>
|
||||||
|
<i className='fab fa-linkedin transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_WEIBO') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'weibo'}
|
||||||
|
href={siteConfig('CONTACT_WEIBO')}>
|
||||||
|
<i className='fab fa-weibo transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_INSTAGRAM') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'instagram'}
|
||||||
|
href={siteConfig('CONTACT_INSTAGRAM')}>
|
||||||
|
<i className='fab fa-instagram transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_EMAIL') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'email'}
|
||||||
|
href={`mailto:${siteConfig('CONTACT_EMAIL')}`}>
|
||||||
|
<i className='fas fa-envelope transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{JSON.parse(siteConfig('ENABLE_RSS')) && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'RSS'}
|
||||||
|
href={'/rss/feed.xml'}>
|
||||||
|
<i className='fas fa-rss transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_BILIBILI') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'bilibili'}
|
||||||
|
href={siteConfig('CONTACT_BILIBILI')}>
|
||||||
|
<i className='fab fa-bilibili transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{siteConfig('CONTACT_YOUTUBE') && (
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
title={'youtube'}
|
||||||
|
href={siteConfig('CONTACT_YOUTUBE')}>
|
||||||
|
<i className='fab fa-youtube transform hover:scale-125 duration-150' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
}
|
}
|
||||||
export default SocialButton
|
export default SocialButton
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { useState, useRef } from 'react'
|
|
||||||
import Collapse from '@/components/Collapse'
|
|
||||||
import { MenuList } from './MenuList'
|
|
||||||
import Logo from './Logo'
|
|
||||||
import SearchInput from './SearchInput'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 顶部导航
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const TopNav = props => {
|
|
||||||
const [isOpen, changeShow] = useState(false)
|
|
||||||
const collapseRef = useRef(null)
|
|
||||||
|
|
||||||
const toggleMenuOpen = () => {
|
|
||||||
changeShow(!isOpen)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (<div id='top-nav' className='z-40 block lg:hidden'>
|
|
||||||
|
|
||||||
{/* 导航栏 */}
|
|
||||||
<div id='sticky-nav' className={'relative w-full top-0 z-20 transform duration-500 bg-white dark:bg-black'}>
|
|
||||||
<Collapse type='vertical' isOpen={isOpen} collapseRef={collapseRef}>
|
|
||||||
<div className='py-1 px-5'>
|
|
||||||
<MenuList {...props} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />
|
|
||||||
<SearchInput {...props} />
|
|
||||||
</div>
|
|
||||||
</Collapse>
|
|
||||||
<div className='w-full flex justify-between items-center p-4 '>
|
|
||||||
{/* 左侧LOGO 标题 */}
|
|
||||||
<div className='flex flex-none flex-grow-0'>
|
|
||||||
<Logo {...props} />
|
|
||||||
</div>
|
|
||||||
<div className='flex'>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 右侧功能 */}
|
|
||||||
<div className='mr-1 flex justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
|
||||||
<div onClick={toggleMenuOpen} className='cursor-pointer text-lg p-2'>
|
|
||||||
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TopNav
|
|
||||||
@@ -10,11 +10,9 @@ const CONFIG = {
|
|||||||
FUKASAWA_MENU_CATEGORY: true, // 显示分类
|
FUKASAWA_MENU_CATEGORY: true, // 显示分类
|
||||||
FUKASAWA_MENU_TAG: true, // 显示标签
|
FUKASAWA_MENU_TAG: true, // 显示标签
|
||||||
FUKASAWA_MENU_ARCHIVE: true, // 显示归档
|
FUKASAWA_MENU_ARCHIVE: true, // 显示归档
|
||||||
FUKASAWA_MENU_SEARCH: false, // 显示搜索
|
|
||||||
|
|
||||||
FUKASAWA_SIDEBAR_COLLAPSE_BUTTON: true, // 侧边栏折叠按钮
|
FUKASAWA_SIDEBAR_COLLAPSE_BUTTON: true, // 侧边栏折叠按钮
|
||||||
FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT: false, // 侧边栏默认折叠收起
|
FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT: false, // 侧边栏默认折叠收起
|
||||||
FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL: false // 侧边栏滚动时折叠 仅文章阅读页有效
|
FUKASAWA_SIDEBAR_COLLAPSE_ON_SCROLL: false // 侧边栏滚动时折叠 仅文章阅读页有效
|
||||||
|
|
||||||
}
|
}
|
||||||
export default CONFIG
|
export default CONFIG
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import CONFIG from './config'
|
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||||
import TopNav from './components/TopNav'
|
import { AdSlot } from '@/components/GoogleAdsense'
|
||||||
import AsideLeft from './components/AsideLeft'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import WWAds from '@/components/WWAds'
|
||||||
|
import { siteConfig } from '@/lib/config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { createContext, useContext, useEffect, useRef } from 'react'
|
||||||
|
import ArticleDetail from './components/ArticleDetail'
|
||||||
|
import ArticleLock from './components/ArticleLock'
|
||||||
|
import AsideLeft from './components/AsideLeft'
|
||||||
import BlogListPage from './components/BlogListPage'
|
import BlogListPage from './components/BlogListPage'
|
||||||
import BlogListScroll from './components/BlogListScroll'
|
import BlogListScroll from './components/BlogListScroll'
|
||||||
import BlogArchiveItem from './components/BlogPostArchive'
|
import BlogArchiveItem from './components/BlogPostArchive'
|
||||||
import ArticleDetail from './components/ArticleDetail'
|
import Header from './components/Header'
|
||||||
import ArticleLock from './components/ArticleLock'
|
|
||||||
import TagItemMini from './components/TagItemMini'
|
import TagItemMini from './components/TagItemMini'
|
||||||
import { useRouter } from 'next/router'
|
import CONFIG from './config'
|
||||||
import { createContext, useContext, useEffect, useRef } from 'react'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { Transition } from '@headlessui/react'
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import { AdSlot } from '@/components/GoogleAdsense'
|
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
|
||||||
import WWAds from '@/components/WWAds'
|
|
||||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
|
||||||
|
|
||||||
const Live2D = dynamic(() => import('@/components/Live2D'))
|
const Live2D = dynamic(() => import('@/components/Live2D'))
|
||||||
|
|
||||||
@@ -43,51 +43,58 @@ export const useFukasawaGlobal = () => useContext(ThemeGlobalFukasawa)
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = props => {
|
||||||
const { children, headerSlot } = props
|
const { children, headerSlot } = props
|
||||||
const leftAreaSlot = <Live2D />
|
const leftAreaSlot = <Live2D />
|
||||||
const { onLoading, fullWidth } = useGlobal()
|
const { onLoading, fullWidth } = useGlobal()
|
||||||
const searchModal = useRef(null)
|
const searchModal = useRef(null)
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalFukasawa.Provider value={{ searchModal }}>
|
<ThemeGlobalFukasawa.Provider value={{ searchModal }}>
|
||||||
<div id='theme-fukasawa' className={`${siteConfig('FONT_STYLE')} dark:bg-black scroll-smooth`}>
|
<div
|
||||||
<AlgoliaSearchModal cRef={searchModal} {...props} />
|
id='theme-fukasawa'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} dark:bg-black scroll-smooth`}>
|
||||||
<Style />
|
<Style />
|
||||||
|
{/* 页头导航,此主题只在移动端生效 */}
|
||||||
|
<Header {...props} />
|
||||||
|
|
||||||
<TopNav {...props} />
|
<div
|
||||||
|
className={
|
||||||
<div className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + ' flex'}>
|
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
||||||
|
? 'flex-row-reverse'
|
||||||
|
: '') + ' flex'
|
||||||
|
}>
|
||||||
{/* 侧边抽屉 */}
|
{/* 侧边抽屉 */}
|
||||||
<AsideLeft {...props} slot={leftAreaSlot} />
|
<AsideLeft {...props} slot={leftAreaSlot} />
|
||||||
|
|
||||||
<main id='wrapper' className='relative flex w-full py-8 justify-center bg-day dark:bg-night'>
|
<main
|
||||||
<div id='container-inner' className={`${fullWidth ? '' : '2xl:max-w-6xl md:max-w-4xl'} w-full relative z-10`}>
|
id='wrapper'
|
||||||
|
className='relative flex w-full py-8 justify-center bg-day dark:bg-night'>
|
||||||
|
<div
|
||||||
|
id='container-inner'
|
||||||
|
className={`${fullWidth ? '' : '2xl:max-w-6xl md:max-w-4xl'} w-full relative z-10`}>
|
||||||
<Transition
|
<Transition
|
||||||
show={!onLoading}
|
show={!onLoading}
|
||||||
appear={true}
|
appear={true}
|
||||||
className="w-full"
|
className='w-full'
|
||||||
enter="transition ease-in-out duration-700 transform order-first"
|
enter='transition ease-in-out duration-700 transform order-first'
|
||||||
enterFrom="opacity-0 translate-y-16"
|
enterFrom='opacity-0 translate-y-16'
|
||||||
enterTo="opacity-100"
|
enterTo='opacity-100'
|
||||||
leave="transition ease-in-out duration-300 transform"
|
leave='transition ease-in-out duration-300 transform'
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
leaveFrom='opacity-100 translate-y-0'
|
||||||
leaveTo="opacity-0 -translate-y-16"
|
leaveTo='opacity-0 -translate-y-16'
|
||||||
unmount={false}
|
unmount={false}>
|
||||||
>
|
|
||||||
<div> {headerSlot} </div>
|
<div> {headerSlot} </div>
|
||||||
<div> {children} </div>
|
<div> {children} </div>
|
||||||
|
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<div className='mt-2'>
|
<div className='mt-2'>
|
||||||
<AdSlot type='native' />
|
<AdSlot type='native' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AlgoliaSearchModal cRef={searchModal} {...props} />
|
||||||
</div>
|
</div>
|
||||||
</ThemeGlobalFukasawa.Provider>
|
</ThemeGlobalFukasawa.Provider>
|
||||||
)
|
)
|
||||||
@@ -96,49 +103,65 @@ const LayoutBase = (props) => {
|
|||||||
/**
|
/**
|
||||||
* 首页
|
* 首页
|
||||||
* @param {*} props notion数据
|
* @param {*} props notion数据
|
||||||
* @returns 首页就是一个博客列表
|
* @returns 首页就是一个博客列表
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = props => {
|
||||||
return <LayoutPostList {...props} />
|
return <LayoutPostList {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 博客列表
|
* 博客列表
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
*/
|
*/
|
||||||
const LayoutPostList = (props) => {
|
const LayoutPostList = props => {
|
||||||
return <>
|
const POST_LIST_STYLE = siteConfig('POST_LIST_STYLE')
|
||||||
<div className='w-full p-2'><WWAds className='w-full' orientation='horizontal' /></div>
|
return (
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}
|
<>
|
||||||
</>
|
<div className='w-full p-2'>
|
||||||
|
<WWAds className='w-full' orientation='horizontal' />
|
||||||
|
</div>
|
||||||
|
{ POST_LIST_STYLE=== 'page' ? (
|
||||||
|
<BlogListPage {...props} />
|
||||||
|
) : (
|
||||||
|
<BlogListScroll {...props} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文章详情
|
* 文章详情
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSlug = (props) => {
|
const LayoutSlug = props => {
|
||||||
const { post, lock, validPassword } = props
|
const { post, lock, validPassword } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{lock ? <ArticleLock validPassword={validPassword} /> : <ArticleDetail {...props} />}
|
{lock ? (
|
||||||
|
<ArticleLock validPassword={validPassword} />
|
||||||
|
) : (
|
||||||
|
<ArticleDetail {...props} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -167,26 +190,28 @@ const LayoutSearch = props => {
|
|||||||
/**
|
/**
|
||||||
* 归档页面
|
* 归档页面
|
||||||
*/
|
*/
|
||||||
const LayoutArchive = (props) => {
|
const LayoutArchive = props => {
|
||||||
const { archivePosts } = props
|
const { archivePosts } = props
|
||||||
return <>
|
return (
|
||||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 shadow-md min-h-full">
|
<>
|
||||||
{Object.keys(archivePosts).map(archiveTitle => (
|
<div className='mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 shadow-md min-h-full'>
|
||||||
<BlogArchiveItem
|
{Object.keys(archivePosts).map(archiveTitle => (
|
||||||
key={archiveTitle}
|
<BlogArchiveItem
|
||||||
posts={archivePosts[archiveTitle]}
|
key={archiveTitle}
|
||||||
archiveTitle={archiveTitle}
|
posts={archivePosts[archiveTitle]}
|
||||||
/>
|
archiveTitle={archiveTitle}
|
||||||
))}
|
/>
|
||||||
</div>
|
))}
|
||||||
</>
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 404
|
* 404
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Layout404 = props => {
|
const Layout404 = props => {
|
||||||
return <>404</>
|
return <>404</>
|
||||||
}
|
}
|
||||||
@@ -194,16 +219,17 @@ const Layout404 = props => {
|
|||||||
/**
|
/**
|
||||||
* 分类列表
|
* 分类列表
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutCategoryIndex = (props) => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { categoryOptions } = props
|
const { categoryOptions } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
||||||
<div className='dark:text-gray-200 mb-5'>
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
<i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}:
|
<i className='mr-4 fas fa-th' />
|
||||||
|
{locale.COMMON.CATEGORY}:
|
||||||
</div>
|
</div>
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
{categoryOptions?.map(category => {
|
{categoryOptions?.map(category => {
|
||||||
@@ -214,8 +240,11 @@ const LayoutCategoryIndex = (props) => {
|
|||||||
passHref
|
passHref
|
||||||
legacyBehavior>
|
legacyBehavior>
|
||||||
<div
|
<div
|
||||||
className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
|
className={
|
||||||
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
|
'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'
|
||||||
|
}>
|
||||||
|
<i className='mr-4 fas fa-folder' />
|
||||||
|
{category.name}({category.count})
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
@@ -229,36 +258,41 @@ const LayoutCategoryIndex = (props) => {
|
|||||||
/**
|
/**
|
||||||
* 标签列表
|
* 标签列表
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutTagIndex = (props) => {
|
const LayoutTagIndex = props => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { tagOptions } = props
|
const { tagOptions } = props
|
||||||
return <>
|
return (
|
||||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
<>
|
||||||
<div className='dark:text-gray-200 mb-5'><i className='mr-4 fas fa-tag' />{locale.COMMON.TAGS}:</div>
|
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
||||||
<div id="tags-list" className="duration-200 flex flex-wrap ml-8">
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
{tagOptions.map(tag => {
|
<i className='mr-4 fas fa-tag' />
|
||||||
return (
|
{locale.COMMON.TAGS}:
|
||||||
<div key={tag.name} className="p-2">
|
</div>
|
||||||
<TagItemMini key={tag.name} tag={tag} />
|
<div id='tags-list' className='duration-200 flex flex-wrap ml-8'>
|
||||||
</div>
|
{tagOptions.map(tag => {
|
||||||
)
|
return (
|
||||||
})}
|
<div key={tag.name} className='p-2'>
|
||||||
|
<TagItemMini key={tag.name} tag={tag} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
</>
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
|
||||||
LayoutBase,
|
|
||||||
LayoutIndex,
|
|
||||||
LayoutSearch,
|
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
|
||||||
Layout404,
|
Layout404,
|
||||||
LayoutPostList,
|
LayoutArchive,
|
||||||
|
LayoutBase,
|
||||||
LayoutCategoryIndex,
|
LayoutCategoryIndex,
|
||||||
LayoutTagIndex
|
LayoutIndex,
|
||||||
|
LayoutPostList,
|
||||||
|
LayoutSearch,
|
||||||
|
LayoutSlug,
|
||||||
|
LayoutTagIndex,
|
||||||
|
CONFIG as THEME_CONFIG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,16 @@ const Style = () => {
|
|||||||
column-gap: .5rem;
|
column-gap: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
`}</style>
|
`}</style>
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Style }
|
export { Style }
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { siteConfig } from '@/lib/config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { GameListIndexCombine } from './GameListIndexCombine'
|
import { GameListIndexCombine } from './GameListIndexCombine'
|
||||||
import PaginationSimple from './PaginationSimple'
|
import PaginationSimple from './PaginationSimple'
|
||||||
@@ -9,7 +10,8 @@ import PaginationSimple from './PaginationSimple'
|
|||||||
export const BlogListPage = props => {
|
export const BlogListPage = props => {
|
||||||
const { page = 1, postCount } = props
|
const { page = 1, postCount } = props
|
||||||
const { NOTION_CONFIG } = useGlobal()
|
const { NOTION_CONFIG } = useGlobal()
|
||||||
const totalPage = Math.ceil(postCount / NOTION_CONFIG)
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
|
const totalPage = Math.ceil(postCount / POSTS_PER_PAGE)
|
||||||
const showNext = page < totalPage
|
const showNext = page < totalPage
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const BlogListScroll = props => {
|
|||||||
const { posts } = props
|
const { posts } = props
|
||||||
const { locale, NOTION_CONFIG } = useGlobal()
|
const { locale, NOTION_CONFIG } = useGlobal()
|
||||||
const [page, updatePage] = useState(1)
|
const [page, updatePage] = useState(1)
|
||||||
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG)
|
const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG)
|
||||||
|
|
||||||
let hasMore = false
|
let hasMore = false
|
||||||
const postsToShow =
|
const postsToShow =
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const PaginationSimple = ({ page, showNext }) => {
|
|||||||
.split('?')[0]
|
.split('?')[0]
|
||||||
.replace(/\/page\/[1-9]\d*/, '')
|
.replace(/\/page\/[1-9]\d*/, '')
|
||||||
.replace(/\/$/, '')
|
.replace(/\/$/, '')
|
||||||
|
.replace('.html', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='my-10 flex justify-between font-medium text-black dark:text-gray-100 space-x-2'>
|
<div className='my-10 flex justify-between font-medium text-black dark:text-gray-100 space-x-2'>
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
import { useGitBookGlobal } from '@/themes/gitbook'
|
import MobileButtonCatalog from './MobileButtonCatalog'
|
||||||
import JumpToTopButton from './JumpToTopButton'
|
import MobileButtonPageNav from './MobileButtonPageNav'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端底部导航
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export default function BottomMenuBar({ post, className }) {
|
export default function BottomMenuBar({ post, className }) {
|
||||||
const { pageNavVisible, changePageNavVisible } = useGitBookGlobal()
|
const showTocButton = post?.toc?.length > 1
|
||||||
|
|
||||||
const togglePageNavVisible = () => {
|
|
||||||
changePageNavVisible(!pageNavVisible)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'sticky z-10 bottom-0 w-full h-12 bg-white dark:bg-hexo-black-gray ' + className}>
|
<>
|
||||||
<div className='flex justify-between h-full shadow-card'>
|
{/* 移动端底部导航按钮 */}
|
||||||
<div onClick={togglePageNavVisible} className='flex w-full items-center justify-center cursor-pointer'>
|
<div className='bottom-button-group md:hidden w-screen h-14 px-4 fixed flex items-center justify-between right-left bottom-0 z-30 bg-white border-t dark:border-gray-800'>
|
||||||
<i className="fa-solid fa-book"></i>
|
<div className='w-full'>
|
||||||
</div>
|
<MobileButtonPageNav />
|
||||||
<div className='flex w-full items-center justify-center cursor-pointer'>
|
|
||||||
<JumpToTopButton />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{showTocButton && (
|
||||||
|
<div className='w-full'>
|
||||||
|
<MobileButtonCatalog />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import { uuidToId } from 'notion-utils'
|
import { uuidToId } from 'notion-utils'
|
||||||
@@ -13,6 +14,7 @@ const Catalog = ({ post }) => {
|
|||||||
const toc = post?.toc
|
const toc = post?.toc
|
||||||
// 同步选中目录事件
|
// 同步选中目录事件
|
||||||
const [activeSection, setActiveSection] = useState(null)
|
const [activeSection, setActiveSection] = useState(null)
|
||||||
|
const {locale}= useGlobal()
|
||||||
|
|
||||||
// 监听滚动事件
|
// 监听滚动事件
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -67,6 +69,7 @@ const Catalog = ({ post }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div className='w-full hidden md:block'><i className='mr-1 fas fa-stream' />{locale.COMMON.TABLE_OF_CONTENTS}</div>
|
||||||
<div
|
<div
|
||||||
id='toc-wrapper'
|
id='toc-wrapper'
|
||||||
className='toc-wrapper overflow-y-auto my-2 max-h-80 overscroll-none scroll-hidden'>
|
className='toc-wrapper overflow-y-auto my-2 max-h-80 overscroll-none scroll-hidden'>
|
||||||
|
|||||||
66
themes/gitbook/components/CatalogDrawerWrapper.js
Normal file
66
themes/gitbook/components/CatalogDrawerWrapper.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { useGitBookGlobal } from '@/themes/gitbook'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import Catalog from './Catalog'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 悬浮抽屉目录
|
||||||
|
* @param toc
|
||||||
|
* @param post
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const CatalogDrawerWrapper = ({ post, cRef }) => {
|
||||||
|
const { tocVisible, changeTocVisible } = useGitBookGlobal()
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const router = useRouter()
|
||||||
|
const switchVisible = () => {
|
||||||
|
changeTocVisible(!tocVisible)
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
changeTocVisible(false)
|
||||||
|
}, [router])
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
id='gitbook-toc-float'
|
||||||
|
className='fixed top-0 right-0 z-40 md:hidden'>
|
||||||
|
{/* 侧边菜单 */}
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
(tocVisible
|
||||||
|
? 'animate__slideInRight '
|
||||||
|
: ' -mr-72 animate__slideOutRight') +
|
||||||
|
' overflow-y-hidden shadow-card w-60 duration-200 fixed right-1 bottom-16 rounded py-2 bg-white dark:bg-hexo-black-gray'
|
||||||
|
}>
|
||||||
|
{post && (
|
||||||
|
<>
|
||||||
|
<div className='px-4 pb-2 flex justify-between items-center border-b font-bold'>
|
||||||
|
<span>{locale.COMMON.TABLE_OF_CONTENTS}</span>
|
||||||
|
<i
|
||||||
|
className='fas fa-times p-1 cursor-pointer'
|
||||||
|
onClick={() => {
|
||||||
|
changeTocVisible(false)
|
||||||
|
}}></i>
|
||||||
|
</div>
|
||||||
|
<div className='dark:text-gray-400 text-gray-600 px-3'>
|
||||||
|
<Catalog post={post} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 背景蒙版 */}
|
||||||
|
<div
|
||||||
|
id='right-drawer-background'
|
||||||
|
className={
|
||||||
|
(tocVisible ? 'block' : 'hidden') +
|
||||||
|
' fixed top-0 left-0 z-30 w-full h-full'
|
||||||
|
}
|
||||||
|
onClick={switchVisible}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default CatalogDrawerWrapper
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { useGitBookGlobal } from '@/themes/gitbook'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移动端悬浮目录按钮
|
|
||||||
*/
|
|
||||||
export default function FloatTocButton () {
|
|
||||||
const { tocVisible, changeTocVisible } = useGitBookGlobal()
|
|
||||||
|
|
||||||
const toggleToc = () => {
|
|
||||||
changeTocVisible(!tocVisible)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onClick={toggleToc}
|
|
||||||
className={ 'text-black flex justify-center items-center dark:text-gray-200 dark:bg-hexo-black-gray py-2 px-2'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
id="toc-button"
|
|
||||||
className={'fa-list-ol cursor-pointer fas hover:scale-150 transform duration-200'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,66 @@
|
|||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import SocialButton from './SocialButton'
|
||||||
|
/**
|
||||||
|
* 站点也叫
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
const Footer = ({ siteInfo }) => {
|
const Footer = ({ siteInfo }) => {
|
||||||
const d = new Date()
|
const d = new Date()
|
||||||
const currentYear = d.getFullYear()
|
const currentYear = d.getFullYear()
|
||||||
const since = siteConfig('SINCE')
|
const since = siteConfig('SINCE')
|
||||||
const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
const copyrightDate =
|
||||||
|
parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer className='z-20 bg:white dark:bg-hexo-black-gray justify-center text-center w-full text-sm relative'>
|
||||||
className='z-20 py-2 bg:white dark:bg-hexo-black-gray justify-center text-center w-full text-sm relative'
|
<hr className='pb-2' />
|
||||||
>
|
|
||||||
<hr className='pb-2' />
|
|
||||||
|
|
||||||
<div className='flex justify-center'>
|
<SocialButton />
|
||||||
<div><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 /></div>
|
|
||||||
© {`${copyrightDate}`}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div 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></div>
|
<div className='flex justify-center pt-1'>
|
||||||
|
<div>
|
||||||
|
<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 />
|
||||||
|
</div>
|
||||||
|
© {`${copyrightDate}`}
|
||||||
|
</div>
|
||||||
|
|
||||||
{siteConfig('BEI_AN') && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{siteConfig('BEI_AN')}</a><br /></>}
|
<div 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span className='hidden busuanzi_container_site_pv'>
|
{siteConfig('BEI_AN') && (
|
||||||
<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-shield-alt' />{' '}
|
||||||
<i className='fas fa-users' /> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
|
<a href='https://beian.miit.gov.cn/' className='mr-2'>
|
||||||
<h1 className='pt-1'>{siteConfig('TITLE')}</h1>
|
{siteConfig('BEI_AN')}
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
</footer>
|
<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>
|
||||||
|
{/* SEO title */}
|
||||||
|
<h1 className='pt-1 hidden'>{siteConfig('TITLE')}</h1>
|
||||||
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default function Header(props) {
|
|||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
<i className='fas fa-times' />
|
<i className='fas fa-times' />
|
||||||
) : (
|
) : (
|
||||||
<i className='fa-solid fa-ellipsis-vertical' />
|
<i className='fa-solid fa-bars' />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到网页顶部
|
* 跳转到网页顶部
|
||||||
* 当屏幕下滑500像素后会出现该控件
|
* 当屏幕下滑500像素后会出现该控件
|
||||||
@@ -9,15 +8,20 @@
|
|||||||
*/
|
*/
|
||||||
const JumpToTopButton = ({ showPercent = false, percent, className }) => {
|
const JumpToTopButton = ({ showPercent = false, percent, className }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="jump-to-top"
|
id='jump-to-top'
|
||||||
data-aos="fade-up"
|
data-aos='fade-up'
|
||||||
data-aos-duration="300"
|
data-aos-duration='300'
|
||||||
data-aos-once="false"
|
data-aos-once='false'
|
||||||
data-aos-anchor-placement="top-center"
|
data-aos-anchor-placement='top-center'
|
||||||
className='fixed xl:right-80 right-2 mr-10 bottom-24 z-20'>
|
className='fixed xl:right-80 right-2 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' }) }} />
|
<i
|
||||||
</div>
|
className='shadow 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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user