mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Merge branch 'main' into baidupush
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=4.0.11
|
||||
NEXT_PUBLIC_VERSION=4.0.12
|
||||
@@ -229,10 +229,15 @@ const BLOG = {
|
||||
// ********挂件组件相关********
|
||||
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
|
||||
|
||||
// artalk 评论插件
|
||||
COMMENT_ARTALK_SERVER: process.env.NEXT_PUBLIC_COMMENT_ARTALK_SERVER || '', // ArtalkServert后端地址 https://artalk.js.org/guide/deploy.html
|
||||
COMMENT_ARTALK_JS: process.env.NEXT_PUBLIC_COMMENT_ARTALK_JS || 'https://cdnjs.cloudflare.com/ajax/libs/artalk/2.5.5/Artalk.js', // ArtalkServert js cdn
|
||||
COMMENT_ARTALK_CSS: process.env.NEXT_PUBLIC_COMMENT_ARTALK_CSS || 'https://cdnjs.cloudflare.com/ajax/libs/artalk/2.5.5/Artalk.css', // ArtalkServert css cdn
|
||||
|
||||
// twikoo
|
||||
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO后端地址 腾讯云环境填envId;Vercel环境填域名,教程:https://tangly1024.com/article/notionnext-twikoo
|
||||
COMMENT_TWIKOO_COUNT_ENABLE: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_COUNT_ENABLE || false, // 博客列表是否显示评论数
|
||||
COMMENT_TWIKOO_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_CDN_URL || 'https://cdn.staticfile.org/twikoo/1.6.16/twikoo.all.min.js', // twikoo客户端cdn
|
||||
COMMENT_TWIKOO_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_CDN_URL || 'https://cdn.staticfile.org/twikoo/1.6.16/twikoo.min.js', // twikoo客户端cdn
|
||||
|
||||
// utterance
|
||||
COMMENT_UTTERRANCES_REPO:
|
||||
@@ -262,7 +267,7 @@ const BLOG = {
|
||||
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version
|
||||
COMMENT_CUSDIS_SCRIPT_SRC:
|
||||
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC ||
|
||||
'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version
|
||||
'/js/cusdis.es.js', // change this if you're using self-hosted version
|
||||
|
||||
// gitalk评论插件 更多参考 https://gitalk.github.io/
|
||||
COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext'
|
||||
|
||||
@@ -57,7 +57,7 @@ const handleAckee = async function(pathname, environment, options = {}) {
|
||||
await loadExternalResource(BLOG.ANALYTICS_ACKEE_TRACKER, 'js')
|
||||
const ackeeTracker = window.ackeeTracker
|
||||
|
||||
const instance = ackeeTracker.create(environment.server, options)
|
||||
const instance = ackeeTracker?.create(environment.server, options)
|
||||
|
||||
if (instance == null) {
|
||||
console.warn('Skipped record creation because useAckee has been called in a non-browser environment')
|
||||
@@ -73,7 +73,7 @@ const handleAckee = async function(pathname, environment, options = {}) {
|
||||
return
|
||||
}
|
||||
|
||||
const attributes = ackeeTracker.attributes(options.detailed)
|
||||
const attributes = ackeeTracker?.attributes(options.detailed)
|
||||
const url = new URL(pathname, location)
|
||||
|
||||
return instance.record(environment.domainId, {
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function AlgoliaSearchModal({ cRef }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='search-wrapper' className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'} fixed h-screen w-screen left-0 top-0 mt-12 flex items-start justify-center`}>
|
||||
<div id='search-wrapper' className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'} z-30 fixed h-screen w-screen left-0 top-0 mt-12 flex items-start justify-center`}>
|
||||
|
||||
{/* 模态框 */}
|
||||
<div className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'} flex flex-col justify-between w-full min-h-[10rem] 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 `}>
|
||||
|
||||
30
components/Artalk.js
Normal file
30
components/Artalk.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
// import { loadExternalResource } from '@/lib/utils'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* Giscus评论 @see https://giscus.app/zh-CN
|
||||
* Contribute by @txs https://github.com/txs/NotionNext/commit/1bf7179d0af21fb433e4c7773504f244998678cb
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const Artalk = ({ siteInfo }) => {
|
||||
useEffect(() => {
|
||||
loadExternalResource(BLOG.COMMENT_ARTALK_CSS, 'css')
|
||||
window?.Artalk?.init({
|
||||
server: BLOG.COMMENT_ARTALK_SERVER, // 后端地址
|
||||
el: '#artalk', // 容器元素
|
||||
locale: BLOG.LANG,
|
||||
// pageKey: '/post/1', // 固定链接 (留空自动获取)
|
||||
// pageTitle: '关于引入 Artalk 的这档子事', // 页面标题 (留空自动获取)
|
||||
site: siteInfo?.title // 你的站点名
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
<div id="artalk"></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Artalk
|
||||
@@ -3,6 +3,7 @@ import dynamic from 'next/dynamic'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import Artalk from './Artalk'
|
||||
|
||||
const WalineComponent = dynamic(
|
||||
() => {
|
||||
@@ -66,7 +67,7 @@ BLOG.COMMENT_GISCUS_REPO || BLOG.COMMENT_CUSDIS_APP_ID || BLOG.COMMENT_UTTERRANC
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const Comment = ({ frontMatter, className }) => {
|
||||
const Comment = ({ siteInfo, frontMatter, className }) => {
|
||||
const router = useRouter()
|
||||
|
||||
if (isBrowser && ('giscus' in router.query || router.query.target === 'comment')) {
|
||||
@@ -84,6 +85,9 @@ const Comment = ({ frontMatter, className }) => {
|
||||
return (
|
||||
<div key={frontMatter?.id} id='comment' className={`comment mt-5 text-gray-800 dark:text-gray-300 ${className || ''}`}>
|
||||
<Tabs>
|
||||
{BLOG.COMMENT_ARTALK_SERVER && (<div key='Artalk'>
|
||||
<Artalk siteInfo={siteInfo} />
|
||||
</div>)}
|
||||
|
||||
{BLOG.COMMENT_TWIKOO_ENV_ID && (<div key='Twikoo'>
|
||||
<TwikooCompenent />
|
||||
|
||||
@@ -38,7 +38,11 @@ const CommonScript = () => {
|
||||
/>
|
||||
</>)}
|
||||
|
||||
{BLOG.COMMENT_CUSDIS_APP_ID && <script defer src='https://cusdis.com/js/widget/lang/zh-cn.js' />}
|
||||
{BLOG.COMMENT_CUSDIS_APP_ID && <script defer src={`https://cusdis.com/js/widget/lang/${BLOG.LANG.toLowerCase()}.js`} />}
|
||||
|
||||
{BLOG.COMMENT_TWIKOO_ENV_ID && <script defer src={BLOG.COMMENT_TWIKOO_CDN_URL}/> }
|
||||
|
||||
{BLOG.COMMENT_ARTALK_SERVER && <script defer src={BLOG.COMMENT_ARTALK_JS}/> }
|
||||
|
||||
{BLOG.COMMENT_TIDIO_ID && <script async src={`//code.tidio.co/${BLOG.COMMENT_TIDIO_ID}.js`} />}
|
||||
|
||||
|
||||
@@ -140,8 +140,10 @@ const renderMermaid = async() => {
|
||||
}
|
||||
if (needLoad) {
|
||||
loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => {
|
||||
const mermaid = window.mermaid
|
||||
mermaid.contentLoaded()
|
||||
setTimeout(() => {
|
||||
const mermaid = window.mermaid
|
||||
mermaid?.contentLoaded()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
// import { loadExternalResource } from '@/lib/utils'
|
||||
import { useEffect } from 'react'
|
||||
// import twikoo from 'twikoo'
|
||||
|
||||
/**
|
||||
* Giscus评论 @see https://giscus.app/zh-CN
|
||||
@@ -11,25 +10,15 @@ import { useEffect } from 'react'
|
||||
*/
|
||||
|
||||
const Twikoo = ({ isDarkMode }) => {
|
||||
const loadTwikoo = async () => {
|
||||
try {
|
||||
await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
|
||||
const twikoo = window.twikoo
|
||||
twikoo?.init({
|
||||
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
|
||||
el: '#twikoo', // 容器元素
|
||||
lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
|
||||
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
|
||||
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('twikoo 加载失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadTwikoo()
|
||||
}, [])
|
||||
window?.twikoo?.init({
|
||||
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
|
||||
el: '#twikoo', // 容器元素
|
||||
lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
|
||||
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
|
||||
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
|
||||
})
|
||||
}, [isDarkMode])
|
||||
return (
|
||||
<div id="twikoo"></div>
|
||||
)
|
||||
|
||||
@@ -65,6 +65,14 @@ const mapImgUrl = (img, block, type = 'block', from) => {
|
||||
ret = BLOG.NOTION_HOST + '/image/' + encodeURIComponent(ret) + '?table=' + type + '&id=' + block.id
|
||||
}
|
||||
|
||||
// UnSplash 随机图片接口优化
|
||||
if (ret.includes('source.unsplash.com/random')) {
|
||||
// 检查原始URL是否已经包含参数
|
||||
const separator = ret.includes('?') ? '&' : '?'
|
||||
// 拼接唯一识别参数,防止请求的图片被缓存
|
||||
ret = `${ret}${separator}random=${block.id}`
|
||||
}
|
||||
|
||||
// 文章封面
|
||||
if (from === 'pageCoverThumbnail') {
|
||||
ret = compressImage(ret)
|
||||
|
||||
11
lib/utils.js
11
lib/utils.js
@@ -24,11 +24,14 @@ export const memorize = (Component) => {
|
||||
*/
|
||||
export function loadExternalResource(url, type) {
|
||||
// 检查是否已存在
|
||||
const elements = document.querySelectorAll(`[href='${url}']`)
|
||||
if (elements.length > 0 || !url) {
|
||||
return
|
||||
}
|
||||
const elements = type === 'js' ? document.querySelectorAll(`[src='${url}']`) : document.querySelectorAll(`[href='${url}']`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (elements.length > 0 || !url) {
|
||||
resolve(url)
|
||||
return url
|
||||
}
|
||||
|
||||
let tag
|
||||
|
||||
if (type === 'css') {
|
||||
|
||||
9371
package-lock.json
generated
Normal file
9371
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "4.0.11",
|
||||
"version": "4.0.12",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
107
public/js/cusdis.es.js
Normal file
107
public/js/cusdis.es.js
Normal file
@@ -0,0 +1,107 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
window.CUSDIS = {}
|
||||
let cusdisIframe
|
||||
|
||||
function createIframe(targetElement) {
|
||||
if (!cusdisIframe) {
|
||||
cusdisIframe = document.createElement('iframe')
|
||||
setupIframe(cusdisIframe, targetElement)
|
||||
}
|
||||
cusdisIframe.srcdoc = generateIframeContent(targetElement)
|
||||
cusdisIframe.style.width = '100%'
|
||||
cusdisIframe.style.border = '0'
|
||||
}
|
||||
|
||||
function setupIframe(iframe, targetElement) {
|
||||
const colorSchemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const messageHandler = event => {
|
||||
try {
|
||||
const message = JSON.parse(event.data)
|
||||
if (message.from === 'cusdis') {
|
||||
switch (message.event) {
|
||||
case 'onload':
|
||||
if (targetElement.dataset.theme === 'auto') {
|
||||
setTheme(colorSchemeMediaQuery.matches ? 'dark' : 'light')
|
||||
}
|
||||
break
|
||||
case 'resize':
|
||||
iframe.style.height = message.data + 'px'
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
const colorSchemeChangeHandler = e => {
|
||||
const isDarkMode = e.matches
|
||||
if (targetElement.dataset.theme === 'auto') {
|
||||
setTheme(isDarkMode ? 'dark' : 'light')
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', messageHandler)
|
||||
colorSchemeMediaQuery.addEventListener('change', colorSchemeChangeHandler)
|
||||
}
|
||||
|
||||
function generateIframeContent(element) {
|
||||
const cusdisHost = element.dataset.host || 'https://cusdis.com'
|
||||
const iframeSrc = element.dataset.iframe || `${cusdisHost}/js/iframe.umd.js`
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="${cusdisHost}/js/style.css">
|
||||
<base target="_parent" />
|
||||
<link>
|
||||
<script>
|
||||
window.CUSDIS_LOCALE = ${JSON.stringify(window.CUSDIS_LOCALE)}
|
||||
window.__DATA__ = ${JSON.stringify(element.dataset)}
|
||||
<\/script>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="${iframeSrc}" type="module">
|
||||
|
||||
<\/script>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
function setTheme(theme, data) {
|
||||
if (cusdisIframe) {
|
||||
cusdisIframe.contentWindow.postMessage(JSON.stringify({ from: 'cusdis', event: theme, data: data }))
|
||||
}
|
||||
}
|
||||
|
||||
function renderTo(element) {
|
||||
if (element) {
|
||||
element.innerHTML = ''
|
||||
createIframe(element)
|
||||
element.appendChild(cusdisIframe)
|
||||
}
|
||||
}
|
||||
|
||||
function initialRender() {
|
||||
let element
|
||||
if (window.cusdisElementId) {
|
||||
element = document.querySelector(`#${window.cusdisElementId}`)
|
||||
} else if (document.querySelector('#cusdis_thread')) {
|
||||
element = document.querySelector('#cusdis_thread')
|
||||
} else if (document.querySelector('#cusdis')) {
|
||||
console.warn('id `cusdis` is deprecated. Please use `cusdis_thread` instead')
|
||||
element = document.querySelector('#cusdis')
|
||||
}
|
||||
|
||||
if (!window.CUSDIS_PREVENT_INITIAL_RENDER && element) {
|
||||
renderTo(element)
|
||||
}
|
||||
}
|
||||
|
||||
window.renderCusdis = renderTo
|
||||
window.CUSDIS.renderTo = renderTo
|
||||
window.CUSDIS.setTheme = setTheme
|
||||
window.CUSDIS.initial = initialRender
|
||||
initialRender()
|
||||
@@ -100,10 +100,6 @@ nav {
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
.notion-collection {
|
||||
@apply max-w-0;
|
||||
}
|
||||
|
||||
.glassmorphism {
|
||||
background: hsla(0, 0%, 100%, 0.05);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
@@ -159,20 +155,6 @@ nav {
|
||||
margin: -0.125em 0.25em;
|
||||
}
|
||||
|
||||
|
||||
.nobelium{
|
||||
@apply flex flex-col justify-between
|
||||
}
|
||||
|
||||
.nobelium .notion-code{
|
||||
/* @apply max-w-2xl; */
|
||||
}
|
||||
|
||||
.next #announcement-content *{
|
||||
font-size:13px !important;
|
||||
line-height:1.7 !important;
|
||||
}
|
||||
|
||||
/* twikoo 评论区超链接样式 */
|
||||
.tk-main a {
|
||||
@apply text-blue-700
|
||||
|
||||
@@ -1419,6 +1419,10 @@ code[class*='language-'] {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.notion-collection {
|
||||
@apply max-w-0;
|
||||
}
|
||||
|
||||
.notion-collection-card{
|
||||
/* cursor: default !important; */
|
||||
}
|
||||
|
||||
@@ -123,11 +123,14 @@ const LayoutIndex = props => {
|
||||
const LayoutPostList = props => {
|
||||
const { category, tag } = props
|
||||
// 顶部如果是按照分类或标签查看文章列表,列表顶部嵌入一个横幅
|
||||
// 如果是搜索,则列表顶部嵌入 搜索框
|
||||
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
|
||||
}
|
||||
return (
|
||||
<LayoutBase {...props} slotTop={slotTop}>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Footer = (props) => {
|
||||
!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'
|
||||
}`}
|
||||
>
|
||||
<DarkModeButton className='text-center pt-4'/>
|
||||
<DarkModeButton className='text-center py-4'/>
|
||||
<hr className="border-gray-200 dark:border-gray-600" />
|
||||
<div className="my-4 text-sm leading-6">
|
||||
<div className="flex align-baseline justify-between flex-wrap">
|
||||
|
||||
@@ -8,6 +8,8 @@ import { MenuItemDrop } from './MenuItemDrop'
|
||||
import Collapse from '@/components/Collapse'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import RandomPostButton from './RandomPostButton'
|
||||
import SearchButton from './SearchButton'
|
||||
|
||||
const Nav = props => {
|
||||
const { navBarTitle, fullWidth, siteInfo } = props
|
||||
@@ -28,11 +30,9 @@ const Nav = props => {
|
||||
useEffect(() => {
|
||||
const obvserver = new window.IntersectionObserver(handler)
|
||||
obvserver.observe(sentinalRef.current)
|
||||
// Don't touch this, I have no idea how it works XD
|
||||
// return () => {
|
||||
// if (sentinalRef.current) obvserver.unobserve(sentinalRef.current)
|
||||
// }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return () => {
|
||||
if (sentinalRef.current) obvserver.unobserve(sentinalRef.current)
|
||||
}
|
||||
}, [sentinalRef])
|
||||
return <>
|
||||
<div className="observer-element h-4 md:h-12" ref={sentinalRef}></div>
|
||||
@@ -102,19 +102,30 @@ const NavBar = props => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-shrink-0">
|
||||
<ul className=" hidden md:flex flex-row">
|
||||
<div className="flex-shrink-0 flex">
|
||||
<ul className="hidden md:flex flex-row">
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
</ul>
|
||||
<div className='md:hidden'><i onClick={toggleOpen} className='fas fa-bars cursor-pointer px-5 block md:hidden'></i>
|
||||
<div className='md:hidden'>
|
||||
<Collapse collapseRef={collapseRef} isOpen={isOpen} type='vertical' className='fixed top-16 right-6'>
|
||||
<div className='dark:border-black bg-white dark:bg-black rounded border p-2 text-sm'>
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)}/>)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
{JSON.parse(CONFIG.MENU_RANDOM_POST) && <RandomPostButton {...props} />}
|
||||
{JSON.parse(CONFIG.MENU_SEARCH_BUTTON) && <SearchButton {...props}/>}
|
||||
<i onClick={toggleOpen} className='fas fa-bars cursor-pointer px-5 flex justify-center items-center md:hidden'></i>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Nav
|
||||
|
||||
/**
|
||||
*
|
||||
{!JSON.parse(BLOG.THEME_SWITCH) && <div className='hidden md:block'><DarkModeButton {...props} /></div>}
|
||||
<ReadingProgress />
|
||||
|
||||
*/
|
||||
|
||||
26
themes/nobelium/components/RandomPostButton.js
Normal file
26
themes/nobelium/components/RandomPostButton.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
/**
|
||||
* 随机跳转到一个文章
|
||||
*/
|
||||
export default function RandomPostButton(props) {
|
||||
const { latestPosts } = props
|
||||
const router = useRouter()
|
||||
const { locale } = useGlobal()
|
||||
/**
|
||||
* 随机跳转文章
|
||||
*/
|
||||
function handleClick() {
|
||||
const randomIndex = Math.floor(Math.random() * latestPosts.length)
|
||||
const randomPost = latestPosts[randomIndex]
|
||||
router.push(`${BLOG.SUB_PATH}/${randomPost?.slug}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div title={locale.MENU.WALK_AROUND} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all' onClick={handleClick}>
|
||||
<i className="fa-solid fa-podcast"></i>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
28
themes/nobelium/components/SearchButton.js
Normal file
28
themes/nobelium/components/SearchButton.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useNobeliumGlobal } from '..'
|
||||
|
||||
/**
|
||||
* 搜索按钮
|
||||
* @returns
|
||||
*/
|
||||
export default function SearchButton(props) {
|
||||
const { locale } = useGlobal()
|
||||
const { searchModal } = useNobeliumGlobal()
|
||||
const router = useRouter()
|
||||
|
||||
function handleSearch() {
|
||||
if (BLOG.ALGOLIA_APP_ID) {
|
||||
searchModal.current.openSearch()
|
||||
} else {
|
||||
router.push('/search')
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<div onClick={handleSearch} title={locale.NAV.SEARCH} alt={locale.NAV.SEARCH} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
|
||||
<i title={locale.NAV.SEARCH} className="fa-solid fa-magnifying-glass" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
const CONFIG = {
|
||||
|
||||
// 菜单配置
|
||||
NAV_NOTION_ICON: true, // 是否读取Notion图标作为站点头像 ; 否则默认显示黑色SVG方块
|
||||
|
||||
// 特殊菜单
|
||||
MENU_RANDOM_POST: true, // 是否显示随机跳转文章按钮
|
||||
MENU_SEARCH_BUTTON: true, // 是否显示搜索按钮,该按钮支持Algolia搜索
|
||||
|
||||
// 默认菜单配置 (开启自定义菜单后,以下配置则失效,请在Notion中自行配置菜单)
|
||||
MENU_CATEGORY: false, // 显示分类
|
||||
MENU_TAG: true, // 显示标签
|
||||
MENU_ARCHIVE: false, // 显示归档
|
||||
MENU_SEARCH: true, // 显示搜索
|
||||
MENU_RSS: false, // 显示订阅
|
||||
|
||||
NAV_NOTION_ICON: true // 是否读取Notion图标作为站点头像
|
||||
MENU_RSS: false // 显示订阅
|
||||
|
||||
}
|
||||
export default CONFIG
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import CONFIG from './config'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { createContext, useEffect, useState, useContext, useRef } from 'react'
|
||||
import Nav from './components/Nav'
|
||||
import { Footer } from './components/Footer'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
@@ -24,6 +24,11 @@ import { Transition } from '@headlessui/react'
|
||||
import { Style } from './style'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||
|
||||
// 主题全局状态
|
||||
const ThemeGlobalNobelium = createContext()
|
||||
export const useNobeliumGlobal = () => useContext(ThemeGlobalNobelium)
|
||||
|
||||
/**
|
||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||
@@ -36,51 +41,58 @@ const LayoutBase = props => {
|
||||
|
||||
const fullWidth = post?.fullWidth ?? false
|
||||
const { onLoading } = useGlobal()
|
||||
const searchModal = useRef(null)
|
||||
|
||||
return (
|
||||
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen'>
|
||||
{/* SEO相关 */}
|
||||
<CommonHead meta={meta}/>
|
||||
{/* SEO相关 */}
|
||||
<Style/>
|
||||
<ThemeGlobalNobelium.Provider value={{ searchModal }}>
|
||||
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen'>
|
||||
{/* SEO相关 */}
|
||||
<CommonHead meta={meta} />
|
||||
{/* SEO相关 */}
|
||||
<Style />
|
||||
|
||||
{/* 顶部导航栏 */}
|
||||
<Nav {...props} />
|
||||
{/* 顶部导航栏 */}
|
||||
<Nav {...props} />
|
||||
|
||||
{/* 主区 */}
|
||||
<main id='out-wrapper' className={`relative m-auto flex-grow w-full transition-all ${!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'}`}>
|
||||
{/* 主区 */}
|
||||
<main id='out-wrapper' className={`relative m-auto flex-grow w-full transition-all ${!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'}`}>
|
||||
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
enter="transition ease-in-out duration-700 transform order-first"
|
||||
enterFrom="opacity-0 translate-y-16"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 -translate-y-16"
|
||||
unmount={false}
|
||||
>
|
||||
{/* 顶部插槽 */}
|
||||
{topSlot}
|
||||
{children}
|
||||
</Transition>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
enter="transition ease-in-out duration-700 transform order-first"
|
||||
enterFrom="opacity-0 translate-y-16"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 -translate-y-16"
|
||||
unmount={false}
|
||||
>
|
||||
{/* 顶部插槽 */}
|
||||
{topSlot}
|
||||
{children}
|
||||
</Transition>
|
||||
|
||||
</main>
|
||||
</main>
|
||||
|
||||
{/* 页脚 */}
|
||||
<Footer {...props} />
|
||||
{/* 页脚 */}
|
||||
<Footer {...props} />
|
||||
|
||||
{/* 右下悬浮 */}
|
||||
<div className='fixed right-4 bottom-4'>
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
|
||||
{/* 左下悬浮 */}
|
||||
<div className="bottom-4 -left-14 fixed justify-end z-40">
|
||||
<Live2D />
|
||||
</div>
|
||||
|
||||
{/* 搜索框 */}
|
||||
<AlgoliaSearchModal cRef={searchModal} {...props}/>
|
||||
|
||||
{/* 右下悬浮 */}
|
||||
<div className='fixed right-4 bottom-4'>
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
|
||||
{/* 左下悬浮 */}
|
||||
<div className="bottom-4 -left-14 fixed justify-end z-40">
|
||||
<Live2D />
|
||||
</div>
|
||||
</div>
|
||||
</ThemeGlobalNobelium.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user