mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
@@ -5,7 +5,7 @@ import { loadExternalResource } from '@/lib/utils'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function Live2D() {
|
||||
if (!BLOG.WIDGET_PET) {
|
||||
if (!BLOG.WIDGET_PET || !JSON.parse(BLOG.WIDGET_PET)) {
|
||||
return <></>
|
||||
}
|
||||
const { switchTheme } = useGlobal()
|
||||
@@ -29,7 +29,7 @@ function initLive2D() {
|
||||
Promise.all([
|
||||
// loadExternalResource('https://cdn.zhangxinxu.com/sp/demo/live2d/live2d/js/live2d.js', 'js')
|
||||
loadExternalResource('https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/live2d.min.js', 'js')
|
||||
]).then(() => {
|
||||
]).then((e) => {
|
||||
// https://github.com/xiazeyu/live2d-widget-models
|
||||
loadlive2d('live2d', BLOG.WIDGET_PET_LINK)
|
||||
})
|
||||
|
||||
@@ -1,65 +1,51 @@
|
||||
|
||||
import 'prismjs'
|
||||
import 'prismjs/components/prism-bash'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import 'prismjs/components/prism-java'
|
||||
|
||||
import { NotionRenderer } from 'react-notion-x'
|
||||
import mediumZoom from 'medium-zoom'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const Code = dynamic(() =>
|
||||
import('react-notion-x/build/third-party/code').then((m) => m.Code)
|
||||
import('react-notion-x/build/third-party/code').then((m) => m.Code), { ssr: false }
|
||||
)
|
||||
const Collection = dynamic(() =>
|
||||
import('react-notion-x/build/third-party/collection').then(
|
||||
(m) => m.Collection
|
||||
)
|
||||
import('react-notion-x/build/third-party/collection').then((m) => m.Collection), { ssr: false }
|
||||
)
|
||||
|
||||
const Equation = dynamic(() =>
|
||||
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
|
||||
import('react-notion-x/build/third-party/equation').then((m) => m.Equation), { ssr: false }
|
||||
)
|
||||
|
||||
const Pdf = dynamic(
|
||||
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
|
||||
{
|
||||
ssr: false
|
||||
}
|
||||
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf), { ssr: false }
|
||||
)
|
||||
|
||||
const Modal = dynamic(
|
||||
() => import('react-notion-x/build/third-party/modal').then((m) => m.Modal),
|
||||
{
|
||||
ssr: false
|
||||
}
|
||||
() => import('react-notion-x/build/third-party/modal').then((m) => m.Modal), { ssr: false }
|
||||
)
|
||||
const NotionPage = ({ post }) => {
|
||||
if (!post || !post.blockMap) {
|
||||
return <>{post?.summary || ''}</>
|
||||
}
|
||||
|
||||
const zoom = typeof window !== 'undefined' && mediumZoom({
|
||||
container: '.notion-viewport',
|
||||
background: 'rgba(0, 0, 0, 0.2)',
|
||||
margin: getMediumZoomMargin()
|
||||
})
|
||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
// 将所有container下的所有图片添加medium-zoom
|
||||
const container = document?.getElementById('container')
|
||||
const imgList = container?.getElementsByTagName('img')
|
||||
if (imgList && zoomRef.current) {
|
||||
for (let i = 0; i < imgList.length; i++) {
|
||||
(zoomRef.current).attach(imgList[i])
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (typeof document === 'undefined') {
|
||||
return
|
||||
}
|
||||
}, [router?.events])
|
||||
const buttons = document.getElementsByClassName('notion-code-copy')
|
||||
for (const e of buttons) {
|
||||
e.addEventListener('click', fixCopy)
|
||||
}
|
||||
}, 500)
|
||||
|
||||
/**
|
||||
* 复制代码后,会重复 @see https://github.com/tangly1024/NotionNext/issues/165
|
||||
* @param {*} e
|
||||
*/
|
||||
function fixCopy(e) {
|
||||
const codeE = e.target.parentElement.parentElement.lastElementChild
|
||||
console.log(codeE)
|
||||
const codeEnd = codeE.lastChild
|
||||
if (codeEnd.nodeName === '#text' && codeE.childNodes.length > 1) {
|
||||
codeEnd.nodeValue = null
|
||||
}
|
||||
}
|
||||
|
||||
return <div id='container'>
|
||||
<NotionRenderer
|
||||
@@ -85,22 +71,4 @@ const mapPageUrl = id => {
|
||||
return '/article/' + id.replace(/-/g, '')
|
||||
}
|
||||
|
||||
function getMediumZoomMargin() {
|
||||
const width = window.innerWidth
|
||||
|
||||
if (width < 500) {
|
||||
return 8
|
||||
} else if (width < 800) {
|
||||
return 20
|
||||
} else if (width < 1280) {
|
||||
return 30
|
||||
} else if (width < 1600) {
|
||||
return 40
|
||||
} else if (width < 1920) {
|
||||
return 48
|
||||
} else {
|
||||
return 72
|
||||
}
|
||||
}
|
||||
|
||||
export default NotionPage
|
||||
|
||||
@@ -22,7 +22,7 @@ const Slug = props => {
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (window) {
|
||||
const article = document.getElementById('container')
|
||||
const article = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (!article) {
|
||||
router.push('/404').then(() => {
|
||||
console.warn('找不到页面', router.asPath)
|
||||
|
||||
@@ -113,6 +113,7 @@ nav {
|
||||
.scroll-hidden{
|
||||
-ms-overflow-style: none;
|
||||
overflow: -moz-scrollbars-none;
|
||||
scrollbar-width: none; /* firefox */
|
||||
}
|
||||
|
||||
.scroll-hidden::-webkit-scrollbar { width: 0 !important }
|
||||
@@ -141,3 +142,7 @@ nav {
|
||||
.shadow-text{
|
||||
text-shadow: 0.1em 0.1em 0.2em black;
|
||||
}
|
||||
|
||||
.notion-code-copy-button > svg{
|
||||
pointer-events:none
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export const LayoutSearch = props => {
|
||||
const { keyword, posts } = props
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const container = document.getElementById('container')
|
||||
const container = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (container && container.innerHTML) {
|
||||
const re = new RegExp(`${keyword}`, 'gim')
|
||||
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`)
|
||||
|
||||
@@ -9,7 +9,7 @@ export const LayoutSearch = (props) => {
|
||||
const currentSearch = keyword || router?.query?.s
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const container = document.getElementById('container')
|
||||
const container = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (container && container.innerHTML) {
|
||||
const re = new RegExp(`${currentSearch}`, 'gim')
|
||||
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${currentSearch}</span>`)
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import 'prismjs'
|
||||
import 'prismjs/components/prism-bash'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
@@ -18,8 +12,8 @@ export const LayoutSlug = (props) => {
|
||||
|
||||
return (
|
||||
<LayoutBase {...props} >
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const Layout404 = props => {
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
if (window) {
|
||||
const article = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (!article) {
|
||||
router.push('/').then(() => {
|
||||
console.log('找不到页面', router.asPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 3000)
|
||||
})
|
||||
return (
|
||||
<LayoutBase {...props}>
|
||||
<div className="text-black w-full h-screen text-center justify-center content-center items-center flex flex-col">
|
||||
|
||||
@@ -9,7 +9,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || document.getElementById('container')
|
||||
const target = currentRef || (typeof document !== 'undefined' && document.getElementById('container'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -11,23 +11,23 @@ export const LayoutSearch = (props) => {
|
||||
const { keyword } = props
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const container = document.getElementById('container')
|
||||
const container = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (container && container.innerHTML) {
|
||||
const re = new RegExp(`${keyword}`, 'gim')
|
||||
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`)
|
||||
}
|
||||
},
|
||||
100)
|
||||
100)
|
||||
})
|
||||
return <LayoutBase {...props}>
|
||||
<div className='py-12'>
|
||||
<div className='pb-4 w-full'>{locale.NAV.SEARCH}</div>
|
||||
<SearchInput currentSearch={keyword} {...props}/>
|
||||
<TagGroups {...props}/>
|
||||
<CategoryGroup {...props}/>
|
||||
<SearchInput currentSearch={keyword} {...props} />
|
||||
<TagGroups {...props} />
|
||||
<CategoryGroup {...props} />
|
||||
</div>
|
||||
<div id='container'>
|
||||
<BlogPostListScroll {...props}/>
|
||||
<BlogPostListScroll {...props} />
|
||||
</div>
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || document.getElementById('container')
|
||||
const target = currentRef || (typeof document !== 'undefined' && document.getElementById('container'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -8,7 +8,7 @@ export const Layout404 = props => {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
if (window) {
|
||||
const article = document.getElementById('container')
|
||||
const article = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (!article) {
|
||||
router.push('/').then(() => {
|
||||
console.log('找不到页面', router.asPath)
|
||||
@@ -16,13 +16,13 @@ export const Layout404 = props => {
|
||||
}
|
||||
}
|
||||
}, 3000)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return <LayoutBase {...props}>
|
||||
<div
|
||||
className='md:-mt-20 text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
|
||||
<div className='dark:text-gray-200'>
|
||||
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><i className='mr-2 fas fa-spinner animate-spin'/>404</h2>
|
||||
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><i className='mr-2 fas fa-spinner animate-spin' />404</h2>
|
||||
<div className='inline-block text-left h-32 leading-10 items-center'>
|
||||
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
||||
</div>
|
||||
|
||||
@@ -2,21 +2,17 @@ import LayoutBase from './LayoutBase'
|
||||
import StickyBar from './components/StickyBar'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const LayoutSearch = (props) => {
|
||||
const { locale } = useGlobal()
|
||||
const { posts, keyword } = props
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const container = document.getElementById('container')
|
||||
if (container && container.innerHTML) {
|
||||
const re = new RegExp(`${keyword}`, 'gim')
|
||||
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`)
|
||||
}
|
||||
},
|
||||
100)
|
||||
})
|
||||
setTimeout(() => {
|
||||
const container = typeof document !== 'undefined' && document.getElementById('container')
|
||||
if (container && container.innerHTML) {
|
||||
const re = new RegExp(`${keyword}`, 'gim')
|
||||
container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`)
|
||||
}
|
||||
}, 200)
|
||||
return (
|
||||
<LayoutBase {...props} >
|
||||
<StickyBar>
|
||||
@@ -26,7 +22,7 @@ export const LayoutSearch = (props) => {
|
||||
</div>
|
||||
</StickyBar>
|
||||
<div className="md:mt-5">
|
||||
<BlogPostListScroll {...props} showSummary={true}/>
|
||||
<BlogPostListScroll {...props} showSummary={true} />
|
||||
</div>
|
||||
</LayoutBase>
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
||||
return () => {
|
||||
window.removeEventListener('scroll', scrollTrigger)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const targetRef = useRef(null)
|
||||
const { locale } = useGlobal()
|
||||
@@ -57,7 +57,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
||||
{/* 文章列表 */}
|
||||
<div className='flex flex-wrap space-y-1 lg:space-y-4'>
|
||||
{postsToShow.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary}/>
|
||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
||||
<div onClick={() => {
|
||||
handleGetMore()
|
||||
}}
|
||||
className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow hover:shadow-xl duration-200 dark:text-gray-200'
|
||||
className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow hover:shadow-xl duration-200 dark:text-gray-200'
|
||||
> {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`} </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function Header(props) {
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
const { isDarkMode } = useGlobal()
|
||||
|
||||
const autoScrollEnd = () => {
|
||||
@@ -90,7 +90,7 @@ export default function Header(props) {
|
||||
window.removeEventListener('scroll', scrollTrigger)
|
||||
window.removeEventListener('resize', updateHeaderHeight)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<header
|
||||
|
||||
@@ -2,18 +2,18 @@ import Head from 'next/head'
|
||||
import { useEffect } from 'react'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
|
||||
export default function Live2DWife () {
|
||||
export default function Live2DWife() {
|
||||
useEffect(() => {
|
||||
if (window) {
|
||||
initLive2DWife()
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
return <>
|
||||
<Head><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css"/></Head>
|
||||
<Head><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css" /></Head>
|
||||
</>
|
||||
}
|
||||
|
||||
function initLive2DWife () {
|
||||
function initLive2DWife() {
|
||||
// 注意:live2d_path 参数应使用绝对路径
|
||||
const live2dPath = 'https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/'
|
||||
// const live2d_path = "/live2d-widget/";
|
||||
|
||||
@@ -9,7 +9,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || document.getElementById('container')
|
||||
const target = currentRef || (typeof document !== 'undefined' && document.getElementById('container'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -18,7 +18,7 @@ const SideBarDrawer = ({ post, cRef, tags, slot, categories, currentCategory })
|
||||
useEffect(() => {
|
||||
const sideBarWrapperElement = document.getElementById('sidebar-wrapper')
|
||||
sideBarWrapperElement?.classList?.remove('hidden')
|
||||
})
|
||||
}, [])
|
||||
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
@@ -52,7 +52,7 @@ const SideBarDrawer = ({ post, cRef, tags, slot, categories, currentCategory })
|
||||
<SideBar tags={tags} post={post} slot={slot} categories={categories} currentCategory={currentCategory} />
|
||||
</div>
|
||||
{/* 背景蒙版 */}
|
||||
<div id='sidebar-drawer-background' onClick={() => { switchSideDrawerVisible(false) }} className='hidden animate__animated animate__fadeIn fixed top-0 duration-300 left-0 z-30 w-full h-full glassmorphism'/>
|
||||
<div id='sidebar-drawer-background' onClick={() => { switchSideDrawerVisible(false) }} className='hidden animate__animated animate__fadeIn fixed top-0 duration-300 left-0 z-30 w-full h-full glassmorphism' />
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ const StickyBar = ({ children }) => {
|
||||
|
||||
return (
|
||||
<div id='sticky-bar' className='sticky flex-grow justify-center top-0 duration-500 z-10 pb-16'>
|
||||
<div className='glassmorphism dark:border-gray-600 px-5 absolute shadow-md border w-full hidden-scroll'>
|
||||
<div id='tag-container' className="md:pl-3 overflow-x-auto">
|
||||
{ children }
|
||||
</div>
|
||||
<div className='glassmorphism dark:border-gray-600 px-5 absolute shadow-md border w-full scroll-hidden'>
|
||||
<div id='tag-container' className="md:pl-3 overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -4,20 +4,20 @@ import { useEffect } from 'react'
|
||||
* 字数统计
|
||||
* @returns
|
||||
*/
|
||||
export default function WordCount () {
|
||||
export default function WordCount() {
|
||||
useEffect(() => {
|
||||
countWords()
|
||||
})
|
||||
}, [])
|
||||
|
||||
return <div id='wordCountWrapper' className='hidden'>
|
||||
<i className='mr-1 fas fa-file-word'/> 本文字数 <strong id='wordCount'>0</strong> | <i className='mr-1 fas fa-clock' /> 阅读时长 ≈ <strong id='readTime'>0</strong> 分钟
|
||||
</div>
|
||||
<i className='mr-1 fas fa-file-word' /> 本文字数 <strong id='wordCount'>0</strong> | <i className='mr-1 fas fa-clock' /> 阅读时长 ≈ <strong id='readTime'>0</strong> 分钟
|
||||
</div>
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字数统计和阅读时间
|
||||
*/
|
||||
function countWords () {
|
||||
function countWords() {
|
||||
if (window) {
|
||||
const articleElement = document.getElementById('notion-article')
|
||||
if (articleElement) {
|
||||
@@ -33,13 +33,13 @@ function countWords () {
|
||||
}
|
||||
|
||||
// 去除html标签
|
||||
function deleteHtmlTag (str) {
|
||||
function deleteHtmlTag(str) {
|
||||
str = str.replace(/<[^>]+>|&[^>]+;/g, '').trim()// 去掉所有的html标签和 之类的特殊符合
|
||||
return str
|
||||
}
|
||||
|
||||
// 用word方式计算正文字数
|
||||
function fnGetCpmisWords (str) {
|
||||
function fnGetCpmisWords(str) {
|
||||
let sLen = 0
|
||||
try {
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
|
||||
Reference in New Issue
Block a user