Merge branch 'tangly1024:main' into main

This commit is contained in:
Sebastian
2023-08-11 01:02:16 +08:00
committed by GitHub
45 changed files with 9853 additions and 191 deletions

View File

@@ -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

View File

@@ -177,8 +177,13 @@ const BLOG = {
STARRY_SKY: process.env.NEXT_PUBLIC_STARRY_SKY || false, // 开关
// ********挂件组件相关********
// Chatbase
CHATBASE_ID: process.env.NEXT_PUBLIC_CHATBASE_ID || null, // 是否显示chatbase机器人 https://www.chatbase.co/
// Chatbase 是否显示chatbase机器人 https://www.chatbase.co/
CHATBASE_ID: process.env.NEXT_PUBLIC_CHATBASE_ID || null,
// WebwhizAI 机器人 @see https://github.com/webwhiz-ai/webwhiz
WEB_WHIZ_ENABLED: process.env.NEXT_PUBLIC_WEB_WHIZ_ENABLED || false, // 是否显示
WEB_WHIZ_BASE_URL: process.env.NEXT_PUBLIC_WEB_WHIZ_BASE_URL || 'https://api.webwhiz.ai', // 可以自建服务器
WEB_WHIZ_CHAT_BOT_ID: process.env.NEXT_PUBLIC_WEB_WHIZ_CHAT_BOT_ID || null, // 在后台获取ID
// 悬浮挂件
WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件
WIDGET_PET_LINK:
@@ -224,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后端地址 腾讯云环境填envIdVercel环境填域名教程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:
@@ -257,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'

View File

@@ -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, {

View File

@@ -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
View 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

View File

@@ -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(
() => {
@@ -54,12 +55,19 @@ const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
ssr: false
})
/**
* 是否有评论
*/
export const commentEnable = BLOG.COMMENT_TWIKOO_ENV_ID || BLOG.COMMENT_WALINE_SERVER_URL || BLOG.COMMENT_VALINE_APP_ID ||
BLOG.COMMENT_GISCUS_REPO || BLOG.COMMENT_CUSDIS_APP_ID || BLOG.COMMENT_UTTERRANCES_REPO ||
BLOG.COMMENT_GITALK_CLIENT_ID || BLOG.COMMENT_WEBMENTION.ENABLE
/**
* 评论组件
* @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')) {
@@ -77,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 />

View File

@@ -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`} />}

View File

@@ -13,7 +13,7 @@ const CusdisComponent = ({ frontMatter }) => {
useEffect(() => {
loadExternalResource(BLOG.COMMENT_CUSDIS_SCRIPT_SRC, 'js').then(url => {
const CUSDIS = window.CUSDIS
CUSDIS.initial()
CUSDIS?.initial()
})
}, [isDarkMode])

View File

@@ -1,5 +1,6 @@
import BLOG from 'blog.config'
import dynamic from 'next/dynamic'
import WebWhiz from './Webwhiz'
// import TwikooCommentCounter from '@/components/TwikooCommentCounter'
// import { DebugPanel } from '@/components/DebugPanel'
@@ -57,6 +58,7 @@ const ExternalPlugin = (props) => {
{JSON.parse(BLOG.RIBBON) && <Ribbon />}
{JSON.parse(BLOG.CUSTOM_RIGHT_CLICK_CONTEXT_MENU) && <CustomContextMenu {...props} />}
{!JSON.parse(BLOG.CAN_COPY) && <DisableCopy/>}
{JSON.parse(BLOG.WEB_WHIZ_ENABLED) && <WebWhiz/>}
<VConsole/>
</>
}

View File

@@ -1,34 +1,28 @@
'use client'
import BLOG from '@/blog.config'
import { isBrowser, loadExternalResource } from '@/lib/utils'
import { isBrowser } from '@/lib/utils'
/**
* 自定义引入外部JS 和 CSS
* 自定义外部 script
* 传入参数将转为 <script>标签。
* @returns
*/
const ExternalScript = () => {
if (isBrowser) {
// 静态导入本地自定义样式
loadExternalResource('/css/custom.css', 'css')
loadExternalResource('/js/custom.js', 'js')
// 自动添加图片阴影
if (BLOG.IMG_SHADOW) {
loadExternalResource('/css/img-shadow.css', 'css')
}
if (BLOG.CUSTOM_EXTERNAL_JS && BLOG.CUSTOM_EXTERNAL_JS.length > 0) {
for (const url of BLOG.CUSTOM_EXTERNAL_JS) {
loadExternalResource(url, 'js')
}
}
if (BLOG.CUSTOM_EXTERNAL_CSS && BLOG.CUSTOM_EXTERNAL_CSS.length > 0) {
for (const url of BLOG.CUSTOM_EXTERNAL_CSS) {
loadExternalResource(url, 'css')
}
}
const ExternalScript = (props) => {
const { src } = props
if (!isBrowser || !src) {
return null
}
const element = document.querySelector(`script[src="${src}"]`)
if (element) {
return null
}
const script = document.createElement('script')
Object.entries(props).forEach(([key, value]) => {
script.setAttribute(key, value)
})
document.head.appendChild(script)
console.log('加载外部脚本', props, script)
return null
}

View File

@@ -3,7 +3,6 @@ import { Component } from 'react'
import PropTypes from 'prop-types'
export default function Messenger() {
console.log('facebook', BLOG.FACEBOOK_PAGE_ID, BLOG.FACEBOOK_APP_ID)
return <MessengerCustomerChat
pageId={BLOG.FACEBOOK_PAGE_ID}
appId={BLOG.FACEBOOK_APP_ID}

View File

@@ -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)
})
}
}

View File

@@ -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, // 腾讯云环境填 envIdVercel 环境填地址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-guangzhouVercel 环境不填
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname需传此参数
})
} catch (error) {
console.error('twikoo 加载失败', error)
}
}
useEffect(() => {
loadTwikoo()
}, [])
window?.twikoo?.init({
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envIdVercel 环境填地址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-guangzhouVercel 环境不填
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname需传此参数
})
}, [isDarkMode])
return (
<div id="twikoo"></div>
)

17
components/Webwhiz.js Normal file
View File

@@ -0,0 +1,17 @@
import BLOG from '@/blog.config'
import ExternalScript from './ExternalScript'
/**
* 一个开源ai组件
* @see https://github.com/webwhiz-ai/webwhiz
* @returns
*/
export default function WebWhiz() {
const props = {
id: '__webwhizSdk__',
src: 'https://www.unpkg.com/webwhiz@1.0.0/dist/sdk.js',
baseUrl: BLOG.WEB_WHIZ_BASE_URL,
chatbotId: BLOG.WEB_WHIZ_CHAT_BOT_ID
}
return <ExternalScript {...props}/>
}

View File

@@ -3,16 +3,21 @@ export default function getAllPageIds (collectionQuery, collectionId, collection
if (!collectionQuery && !collectionView) {
return []
}
let pageIds = []
// 优先按照第一个视图排序
if (viewIds && viewIds.length > 0) {
const ids = collectionView[viewIds[0]].value.page_sort
// console.log('PageIds: 从viewId获取', viewIds)
for (const id of ids) {
pageIds.push(id)
let pageIds = []
try {
if (viewIds && viewIds.length > 0) {
const ids = collectionQuery[collectionId][viewIds[0]]?.collection_group_results?.blockIds
for (const id of ids) {
pageIds.push(id)
}
}
// 否则按照数据库原始排序
} else if (collectionQuery && Object.values(collectionQuery).length > 0) {
} catch (error) {
}
// 否则按照数据库原始排序
if (pageIds.length === 0 && collectionQuery && Object.values(collectionQuery).length > 0) {
const pageSet = new Set()
Object.values(collectionQuery[collectionId]).forEach(view => {
view?.blockIds?.forEach(id => pageSet.add(id)) // group视图

View File

@@ -98,7 +98,7 @@ export default async function getPageProperties(id, block, schema, authToken, ta
properties.slug = properties.slug ?? properties.id
} else if (properties.type === BLOG.NOTION_PROPERTY_NAME.type_menu || properties.type === BLOG.NOTION_PROPERTY_NAME.type_sub_menu) {
// 菜单路径为空、作为可展开菜单使用
properties.to = properties.slug ?? null
properties.to = properties.slug ?? '#'
properties.name = properties.title ?? ''
}

View File

@@ -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)

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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": {

View File

@@ -1,5 +1,3 @@
import { useEffect } from 'react'
import '@/styles/animate.css' // @see https://animate.style/
import '@/styles/globals.css'
import '@/styles/nprogress.css'
@@ -14,20 +12,43 @@ import { GlobalContextProvider } from '@/lib/global'
import AOS from 'aos'
import 'aos/dist/aos.css' // You can also use <link> for styles
import dynamic from 'next/dynamic'
import { isBrowser, loadExternalResource } from '@/lib/utils'
import BLOG from '@/blog.config'
// 自定义样式css和js引入
import ExternalScript from '@/components/ExternalScript'
// 各种扩展插件 动画等
const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
const MyApp = ({ Component, pageProps }) => {
useEffect(() => {
// 自定义样式css和js引入
if (isBrowser) {
// 初始化AOS动画
AOS.init()
}, [])
// 静态导入本地自定义样式
loadExternalResource('/css/custom.css', 'css')
loadExternalResource('/js/custom.js', 'js')
// 自动添加图片阴影
if (BLOG.IMG_SHADOW) {
loadExternalResource('/css/img-shadow.css', 'css')
}
// 导入外部自定义脚本
if (BLOG.CUSTOM_EXTERNAL_JS && BLOG.CUSTOM_EXTERNAL_JS.length > 0) {
for (const url of BLOG.CUSTOM_EXTERNAL_JS) {
loadExternalResource(url, 'js')
}
}
// 导入外部自定义样式
if (BLOG.CUSTOM_EXTERNAL_CSS && BLOG.CUSTOM_EXTERNAL_CSS.length > 0) {
for (const url of BLOG.CUSTOM_EXTERNAL_CSS) {
loadExternalResource(url, 'css')
}
}
}
return (
<GlobalContextProvider {...pageProps}>
<ExternalScript />
<Component {...pageProps} />
<ExternalPlugins {...pageProps} />
</GlobalContextProvider>

107
public/js/cusdis.es.js Normal file
View 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()

View File

@@ -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

View File

@@ -1419,6 +1419,10 @@ code[class*='language-'] {
margin-right: 6px;
}
.notion-collection {
@apply max-w-0;
}
.notion-collection-card{
/* cursor: default !important; */
}

View File

@@ -76,7 +76,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"
@@ -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}>

View File

@@ -84,7 +84,7 @@ const LayoutBase = (props) => {
className="w-full"
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -98,7 +98,7 @@ const LayoutBase = (props) => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -70,10 +70,10 @@ export default function ArticleAdjacent({ prev, next }) {
{/* 桌面端 */}
<div id='pc-next-post' className={`hidden md:block fixed z-40 right-16 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
<div id='pc-next-post' className={`hidden md:block fixed z-40 right-24 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
<Link
href={`/${next.slug}`}
className='cursor-pointer duration transition-all h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white dark:text-gray-300 dark:hover:text-yellow-600 hover:text-white hover:font-bold hover:bg-gray-400 rounded-lg flex flex-col justify-between'
className='cursor-pointer drop-shadow-xl duration transition-all h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white dark:text-gray-300 dark:hover:text-yellow-600 hover:text-white hover:font-bold hover:bg-blue-600 rounded-lg flex flex-col justify-between'
>
<div className='text-xs'>下一篇</div>
<hr />

View File

@@ -50,21 +50,12 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
</div>
{/* 摘要 */}
{(!showPreview || showSummary) && !post.results && (
{(!showPreview || showSummary) && (
<p className="line-clamp-2 replace my-3 2xl:my-1 text-gray-700 dark:text-gray-300 text-sm font-light leading-tight">
{post.summary}
</p>
)}
{/* 搜索结果 */}
{post.results && (
<p className="line-clamp-2 mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
{post.results.map((r, index) => (
<span key={index}>{r}</span>
))}
</p>
)}
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
<div>
{' '}

View File

@@ -29,12 +29,12 @@ export const MenuItemCollapse = ({ link }) => {
<div className='select-none w-full px-2 py-2 border rounded-xl text-left dark:bg-hexo-black-gray' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className="font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1">
className="font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest">
<span className=' transition-all items-center duration-200'>{link?.icon && <i className={link.icon + ' mr-4'} />}{link?.name}</span>
</Link>}
{hasSubMenu && <div
onClick={hasSubMenu ? toggleOpenSubMenu : null}
className="font-extralight flex items-center justify-between pl-2 pr-4 cursor-pointer dark:text-gray-200 no-underline tracking-widest pb-1">
className="font-extralight flex items-center justify-between pl-2 pr-4 cursor-pointer dark:text-gray-200 no-underline tracking-widest">
<span className='transition-all items-center duration-200'>{link?.icon && <i className={link.icon + ' mr-4'} />}{link?.name}</span>
<i className={`select-none px-2 fas fa-chevron-left transition-all duration-200 ${isOpen ? '-rotate-90' : ''} text-gray-400`}></i>
</div>}

View File

@@ -15,7 +15,7 @@ import SearchNav from './components/SearchNav'
import BlogPostArchive from './components/BlogPostArchive'
import { ArticleLock } from './components/ArticleLock'
import PostHeader from './components/PostHeader'
import Comment from '@/components/Comment'
import Comment, { commentEnable } from '@/components/Comment'
import NotionPage from '@/components/NotionPage'
import ArticleAdjacent from './components/ArticleAdjacent'
import ArticleCopyright from './components/ArticleCopyright'
@@ -44,7 +44,7 @@ const LayoutBase = props => {
return (
<div id='theme-heo' className='bg-[#f7f9fe] dark:bg-[#18171d] h-full min-h-screen flex flex-col'>
{/* SEO信息 */}
<CommonHead meta={meta}/>
<CommonHead meta={meta} />
<Style />
{/* 顶部嵌入 导航栏首页放hero文章页放文章详情 */}
@@ -158,7 +158,9 @@ const LayoutSearch = props => {
<div id='post-outer-wrapper' className='px-5 md:px-0'>
{!currentSearch
? <SearchNav {...props} />
: <div id="posts-wrapper"> {BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />} </div>}
: <div id="posts-wrapper">
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
</div>}
</div>
</LayoutBase>
)
@@ -223,7 +225,7 @@ const LayoutSlug = props => {
return (
<LayoutBase {...props} headerSlot={headerSlot} showCategory={false} showTag={false} slotRight={slotRight}>
<div className="w-full max-w-5xl lg:hover:shadow lg:border rounded-t-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article">
<div className="w-full max-w-5xl lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article">
{lock && <ArticleLock validPassword={validPassword} />}
{!lock && <div id="article-wrapper" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
@@ -233,7 +235,7 @@ const LayoutSlug = props => {
data-aos-duration="300"
data-aos-once="false"
data-aos-anchor-placement="top-bottom"
itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased overflow-y-hidden" >
itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased overflow-y-hidden" >
{/* Notion文章主体 */}
<section className='px-5 justify-center mx-auto'>
{post && <NotionPage post={post} />}
@@ -253,13 +255,17 @@ const LayoutSlug = props => {
</article>
<hr className='my-4 border-dashed' />
<div className={`${commentEnable && post ? '' : 'hidden'}`}>
<hr className='my-4 border-dashed' />
{/* 评论互动 */}
<div className="duration-200 overflow-x-auto px-5">
<div className='text-2xl dark:text-white'><i className='fas fa-comment mr-1' />{locale.COMMON.COMMENTS}</div>
<Comment frontMatter={post} className='' />
</div>
{/* 评论互动 */}
<div className="duration-200 overflow-x-auto px-5">
<div className='text-2xl dark:text-white'><i className='fas fa-comment mr-1' />{locale.COMMON.COMMENTS}</div>
<Comment frontMatter={post} className='' />
</div>
</div>}
</div>
<FloatTocButton {...props} />
@@ -298,7 +304,7 @@ const Layout404 = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -78,7 +78,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -106,7 +106,7 @@ export default function Features() {
className="w-full"
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform absolute"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"
@@ -125,7 +125,7 @@ export default function Features() {
className="w-full"
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform absolute"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"
@@ -145,7 +145,7 @@ export default function Features() {
className="w-full"
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform absolute"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -59,7 +59,7 @@ export default function MobileMenu() {
className="absolute top-full h-screen pb-16 z-20 left-0 w-full overflow-scroll bg-white"
enter="transition ease-out duration-200 transform"
enterFrom="opacity-0 -translate-y-2"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"

View File

@@ -57,6 +57,9 @@ const CONFIG = {
TESTIMONIALS_SOCIAL_URL: 'https://blog.gaoran.xyz/',
TESTIMONIALS_WORD: '“ 感谢大佬的方法。之前尝试过Super、Potion等国外的第三方平台实现效果一般个性化程度远不如这个方法已经用起来了 “',
POST_REDIRECT_ENABLE: process.env.NEXT_PUBLIC_POST_REDIRECT_ENABLE || false, // 是否开启文章地址重定向 用于迁移旧网站域名
POST_REDIRECT_URL: process.env.NEXT_PUBLIC_POST_REDIRECT_URL || 'https://blog.tangly1024.com', // 重定向网站地址
NEWSLETTER: process.env.NEXT_PUBLIC_THEME_LANDING_NEWSLETTER || false // 是否开启邮件订阅 请先配置mailchimp功能 https://docs.tangly1024.com/article/notion-next-mailchimp
}
export default CONFIG

View File

@@ -1,4 +1,6 @@
'use client'
/**
* 这是一个空白主题,方便您用作创建新主题时的模板,从而开发出您自己喜欢的主题
* 1. 禁用了代码质量检查功能提高了代码的宽容度您可以使用标准的html写法
@@ -15,6 +17,10 @@ import FeaturesBlocks from './components/FeaturesBlocks'
import Testimonials from './components/Testimonials'
import Newsletter from './components/Newsletter'
import CommonHead from '@/components/CommonHead'
import { useRouter } from 'next/router'
import CONFIG from './config'
import Loading from '@/components/Loading'
import { isBrowser } from '@/lib/utils'
/**
* 这是个配置文件,可以方便在此统一配置信息
@@ -31,8 +37,8 @@ const THEME_CONFIG = { THEME: 'landing' }
const LayoutBase = (props) => {
const { meta, siteInfo, children } = props
return <div id='theme-blank' className="overflow-hidden flex flex-col justify-between bg-white">
return <div id='theme-landing' className="overflow-hidden flex flex-col justify-between bg-white">
{/* 网页SEO */}
<CommonHead meta={meta} siteInfo={siteInfo} />
@@ -72,11 +78,24 @@ const LayoutIndex = (props) => {
* @param {*} props
* @returns
*/
const LayoutSlug = (props) => <LayoutBase {...props}>
<div id='container-inner' className='mx-auto max-w-screen-lg p-12'>
<NotionPage {...props} />
</div>
</LayoutBase>
const LayoutSlug = (props) => {
// 如果 是 /article/[slug] 的文章路径则进行重定向到另一个域名
const router = useRouter()
if (JSON.parse(CONFIG.POST_REDIRECT_ENABLE) && isBrowser && router.route == '/[prefix]/[slug]') {
const redirectUrl = CONFIG.POST_REDIRECT_URL + router.asPath.replace('?theme=landing', '')
router.push(redirectUrl)
return <div id='theme-landing'><Loading /></div>
}
return <LayoutBase {...props}>
<div id='container-inner' className='mx-auto max-w-screen-lg p-12'>
<NotionPage {...props} />
</div>
</LayoutBase>
}
// 其他布局暂时留空
const LayoutSearch = (props) => <LayoutBase {...props}><Hero /></LayoutBase>

View File

@@ -58,7 +58,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 -translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-16"
@@ -79,7 +79,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -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">

View File

@@ -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 />
*/

View 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>
)
}

View 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>
</>
}

View File

@@ -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

View File

@@ -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 translate-y-0"
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>
)
}

View File

@@ -74,7 +74,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"

View File

@@ -61,7 +61,7 @@ const LayoutBase = props => {
appear={true}
enter="transition ease-in-out duration-700 transform order-first"
enterFrom="opacity-0 translate-y-16"
enterTo="opacity-100 translate-y-0"
enterTo="opacity-100"
leave="transition ease-in-out duration-300 transform"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-16"