Merge branch 'tangly1024:main' into main

This commit is contained in:
Sebastian
2023-08-03 10:47:00 +08:00
committed by GitHub
72 changed files with 669 additions and 194 deletions

View File

@@ -1,2 +1,2 @@
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
NEXT_PUBLIC_VERSION=4.0.8
NEXT_PUBLIC_VERSION=4.0.11

View File

@@ -29,7 +29,7 @@
| Next | Medium | Hexo | Fukasawa |
|--|--|--|--|
| <img src='./docs/theme-next.png' width='300'/> [预览NEXT](https://tangly1024.com/?theme=next) | <img src='./docs/theme-medium.png' width='300'/> [预览MEDIUM](https://tangly1024.com/?theme=medium) | <img src='./docs/theme-hexo.png' width='300'/> [预览HEXO](https://tangly1024.com/?theme=hexo) | <img src='./docs/theme-fukasawa.png' width='300'/> [预览FUKASAWA](https://tangly1024.com/?theme=fukasawa) |
| <img src='./docs/theme-next.png' width='300'/> [预览NEXT](https://preview.tangly1024.com/?theme=next) | <img src='./docs/theme-medium.png' width='300'/> [预览MEDIUM](https://preview.tangly1024.com/?theme=medium) | <img src='./docs/theme-hexo.png' width='300'/> [预览HEXO](https://preview.tangly1024.com/?theme=hexo) | <img src='./docs/theme-fukasawa.png' width='300'/> [预览FUKASAWA](https://preview.tangly1024.com/?theme=fukasawa) |
## 我要如何开始?

View File

@@ -83,7 +83,7 @@ const BLOG = {
FONT_AWESOME: process.env.NEXT_PUBLIC_FONT_AWESOME_PATH || 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', // font-awesome 字体图标地址; 可选 /css/all.min.css https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/6.0.0/css/all.min.css
// END ************网站字体*****************
CAN_COPY: process.env.NEXT_PUBLIC_CAN_COPY || true, // 是否允许复制页面内容 默认允许如果设置为false、则全栈禁止复制内容。
CUSTOM_RIGHT_CLICK_CONTEXT_MENU: process.env.NEXT_PUBLIC_CUSTOM_RIGHT_CLICK_CONTEXT_MENU || true, // 自定义右键菜单,覆盖系统菜单
// 自定义外部脚本,外部样式
@@ -119,6 +119,8 @@ const BLOG = {
// Mermaid 图表CDN
MERMAID_CDN: process.env.NEXT_PUBLIC_MERMAID_CDN || 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.2.4/mermaid.min.js', // CDN
// QRCodeCDN
QR_CODE_CDN: process.env.NEXT_PUBLIC_QR_CODE_CDN || 'https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js',
BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc
BACKGROUND_DARK: '#000000', // use hex value, don't forget '#'
@@ -307,12 +309,10 @@ const BLOG = {
ANALYTICS_CNZZ_ID: process.env.NEXT_PUBLIC_ANALYTICS_CNZZ_ID || '', // 只需要填写站长统计的id, [cnzz_id] -> https://s9.cnzz.com/z_stat.php?id=[cnzz_id]&web_id=[cnzz_id]
ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX
ANALYTICS_ACKEE_TRACKER:
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js'
ANALYTICS_ACKEE_DATA_SERVER:
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash
ANALYTICS_ACKEE_DOMAIN_ID:
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
// ACKEE网站访客统计工具
ANALYTICS_ACKEE_TRACKER: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.com/tracker.js'
ANALYTICS_ACKEE_DATA_SERVER: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.com , don't end with a slash
ANALYTICS_ACKEE_DOMAIN_ID: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '82e51db6-dec2-423a-b7c9-b4ff7ebb3302'
SEO_GOOGLE_SITE_VERIFICATION:
process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code

View File

@@ -1,21 +1,83 @@
import { useRouter } from 'next/router'
import useAckee from 'use-ackee'
import BLOG from '@/blog.config'
'use strict'
import { useEffect } from 'react'
import BLOG from '@/blog.config'
import { loadExternalResource } from '@/lib/utils'
import { useRouter } from 'next/router'
const Ackee = () => {
const router = useRouter()
useAckee(
router.asPath,
{
server: BLOG.ANALYTICS_ACKEE_DATA_SERVER,
domainId: BLOG.ANALYTICS_ACKEE_DOMAIN_ID
},
{
detailed: false,
ignoreLocalhost: true
}
)
// handleAckee 函数
const handleAckeeCallback = () => {
handleAckee(
router.asPath,
{
server: BLOG.ANALYTICS_ACKEE_DATA_SERVER,
domainId: BLOG.ANALYTICS_ACKEE_DOMAIN_ID
},
{
/*
* Enable or disable tracking of personal data.
* We recommend to ask the user for permission before turning this option on.
*/
detailed: true,
/*
* Enable or disable tracking when on localhost.
*/
ignoreLocalhost: false,
/*
* Enable or disable the tracking of your own visits.
* This is enabled by default, but should be turned off when using a wildcard Access-Control-Allow-Origin header.
* Some browsers strictly block third-party cookies. The option won't have an impact when this is the case.
*/
ignoreOwnVisits: false
}
)
}
// 或者使用其他依赖数组,根据需要执行 handleAckee
useEffect(() => {
handleAckeeCallback()
}, [router])
return null
}
export default Ackee
/**
* Function to use Ackee.
* Creates an instance once and a new record every time the pathname changes.
* Safely no-ops during server-side rendering.
* @param {?String} pathname - Current path.
* @param {Object} environment - Object containing the URL of the Ackee server and the domain id.
* @param {?Object} options - Ackee options.
*/
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)
if (instance == null) {
console.warn('Skipped record creation because useAckee has been called in a non-browser environment')
return
}
const hasPathname = (
pathname != null && pathname !== ''
)
if (hasPathname === false) {
console.warn('Skipped record creation because useAckee has been called without pathname')
return
}
const attributes = ackeeTracker.attributes(options.detailed)
const url = new URL(pathname, location)
return instance.record(environment.domainId, {
...attributes,
siteLocation: url.href
}).stop
}

View File

@@ -62,7 +62,7 @@ const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
const Comment = ({ frontMatter, className }) => {
const router = useRouter()
if (isBrowser() && ('giscus' in router.query || router.query.target === 'comment')) {
if (isBrowser && ('giscus' in router.query || router.query.target === 'comment')) {
setTimeout(() => {
const url = router.asPath.replace('?target=comment', '')
history.replaceState({}, '', url)

View File

@@ -56,12 +56,12 @@ const CommonScript = () => {
{/* 代码统计 */}
{/* ackee统计脚本 */}
{BLOG.ANALYTICS_ACKEE_TRACKER && (
{/* {BLOG.ANALYTICS_ACKEE_TRACKER && (
<script async src={BLOG.ANALYTICS_ACKEE_TRACKER}
data-ackee-server={BLOG.ANALYTICS_ACKEE_DATA_SERVER}
data-ackee-domain-id={BLOG.ANALYTICS_ACKEE_DOMAIN_ID}
/>
)}
)} */}
{/* 百度统计 */}
{BLOG.ANALYTICS_BAIDU_ID && (

View File

@@ -1,8 +1,8 @@
import { useGlobal } from '@/lib/global'
import { ReactCusdis } from 'react-cusdis'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { loadExternalResource } from '@/lib/utils'
const CusdisComponent = ({ frontMatter }) => {
const { locale } = useGlobal()
@@ -11,33 +11,21 @@ const CusdisComponent = ({ frontMatter }) => {
// 处理cusdis主题
useEffect(() => {
const cusdisThread = document?.getElementById('cusdis_thread')
const cusdisIframe = cusdisThread?.getElementsByTagName('iframe')
if (cusdisIframe) {
const cusdisWrapper = cusdisIframe[0]?.contentDocument?.getElementById('root')
if (isDarkMode) {
cusdisWrapper?.classList?.remove('light')
cusdisWrapper?.classList?.add('dark')
} else {
cusdisWrapper?.classList?.remove('dark')
cusdisWrapper?.classList?.add('light')
}
if (!cusdisWrapper?.firstElementChild?.classList?.contains('dark:text-gray-100')) {
cusdisWrapper?.firstElementChild?.classList?.add('dark:text-gray-100')
}
}
})
loadExternalResource(BLOG.COMMENT_CUSDIS_SCRIPT_SRC, 'js').then(url => {
const CUSDIS = window.CUSDIS
CUSDIS.initial()
})
}, [isDarkMode])
return <ReactCusdis
lang={locale.LOCALE.toLowerCase()}
attrs={{
host: BLOG.COMMENT_CUSDIS_HOST,
appId: BLOG.COMMENT_CUSDIS_APP_ID,
pageId: frontMatter.id,
pageTitle: frontMatter.title,
pageUrl: BLOG.LINK + router.asPath
}}
/>
return <div id="cusdis_thread"
lang={locale.LOCALE.toLowerCase()}
data-host={BLOG.COMMENT_CUSDIS_HOST}
data-app-id={BLOG.COMMENT_CUSDIS_APP_ID}
data-page-id={frontMatter.id}
data-page-url={BLOG.LINK + router.asPath}
data-page-title={frontMatter.title}
data-theme={isDarkMode ? 'dark' : 'light'}
></div>
}
export default CusdisComponent

21
components/DisableCopy.js Normal file
View File

@@ -0,0 +1,21 @@
import BLOG from '@/blog.config'
import { useEffect } from 'react'
/**
* 禁止用户拷贝文章的插件
*/
export default function DisableCopy() {
useEffect(() => {
if (!JSON.parse(BLOG.CAN_COPY)) {
// 全栈添加禁止复制的样式
document.getElementsByTagName('html')[0].classList.add('forbid-copy')
// 监听复制事件
document.addEventListener('copy', function (event) {
event.preventDefault() // 阻止默认复制行为
alert('抱歉,本网页内容不可复制!')
})
}
}, [])
return null
}

View File

@@ -30,6 +30,7 @@ const GoogleAdsense = dynamic(() => import('@/components/GoogleAdsense'), { ssr:
const Messenger = dynamic(() => import('@/components/FacebookMessenger'), { ssr: false })
const VConsole = dynamic(() => import('@/components/VConsole'), { ssr: false })
const CustomContextMenu = dynamic(() => import('@/components/CustomContextMenu'), { ssr: false })
const DisableCopy = dynamic(() => import('@/components/DisableCopy'), { ssr: false })
/**
* 各种第三方组件
@@ -55,6 +56,7 @@ const ExternalPlugin = (props) => {
{JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE) && <TwikooCommentCounter {...props}/>}
{JSON.parse(BLOG.RIBBON) && <Ribbon />}
{JSON.parse(BLOG.CUSTOM_RIGHT_CLICK_CONTEXT_MENU) && <CustomContextMenu {...props} />}
{!JSON.parse(BLOG.CAN_COPY) && <DisableCopy/>}
<VConsole/>
</>
}

View File

@@ -8,7 +8,7 @@ import { isBrowser, loadExternalResource } from '@/lib/utils'
* @returns
*/
const ExternalScript = () => {
if (isBrowser()) {
if (isBrowser) {
// 静态导入本地自定义样式
loadExternalResource('/css/custom.css', 'css')
loadExternalResource('/js/custom.js', 'js')

View File

@@ -1,11 +1,256 @@
import BLOG from '@/blog.config'
import MessengerCustomerChat from 'react-messenger-customer-chat'
import { Component } from 'react'
import PropTypes from 'prop-types'
const Messenger = () => (
<MessengerCustomerChat
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}
language={BLOG.LANG.replace('-', '_')}
shouldShowDialog={true}
/>
)
export default Messenger
}
/**
* @see https://github.com/Yoctol/react-messenger-customer-chat
*/
class MessengerCustomerChat extends Component {
constructor(props) {
super(props)
this.state = {
fbLoaded: false,
shouldShowDialog: undefined
}
}
/**
* 初始化
*/
componentDidMount() {
this.setFbAsyncInit()
this.reloadSDKAsynchronously()
}
componentDidUpdate(prevProps) {
if (
prevProps.pageId !== this.props.pageId ||
prevProps.appId !== this.props.appId ||
prevProps.shouldShowDialog !== this.props.shouldShowDialog ||
prevProps.htmlRef !== this.props.htmlRef ||
prevProps.minimized !== this.props.minimized ||
prevProps.themeColor !== this.props.themeColor ||
prevProps.loggedInGreeting !== this.props.loggedInGreeting ||
prevProps.loggedOutGreeting !== this.props.loggedOutGreeting ||
prevProps.greetingDialogDisplay !== this.props.greetingDialogDisplay ||
prevProps.greetingDialogDelay !== this.props.greetingDialogDelay ||
prevProps.autoLogAppEvents !== this.props.autoLogAppEvents ||
prevProps.xfbml !== this.props.xfbml ||
prevProps.version !== this.props.version ||
prevProps.language !== this.props.language
) {
this.setFbAsyncInit()
this.reloadSDKAsynchronously()
}
}
componentWillUnmount() {
if (window.FB !== undefined) {
window.FB.CustomerChat.hide()
}
}
/**
* 初始化
*/
setFbAsyncInit() {
const { appId, autoLogAppEvents, xfbml, version } = this.props
window.fbAsyncInit = () => {
window.FB.init({
appId,
autoLogAppEvents,
xfbml,
version: `v${version}`
})
this.setState({ fbLoaded: true })
}
}
loadSDKAsynchronously() {
const { language } = this.props;
/* eslint-disable */
(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = `https://connect.facebook.net/${language}/sdk/xfbml.customerchat.js`;
fjs.parentNode.insertBefore(js, fjs);
})(document, 'script', 'facebook-jssdk');
/* eslint-enable */
}
removeFacebookSDK() {
removeElementByIds(['facebook-jssdk', 'fb-root'])
delete window.FB
}
reloadSDKAsynchronously() {
this.removeFacebookSDK()
this.loadSDKAsynchronously()
}
controlPlugin() {
const { shouldShowDialog } = this.props
if (shouldShowDialog) {
window.FB.CustomerChat.showDialog()
} else {
window.FB.CustomerChat.hideDialog()
}
}
subscribeEvents() {
const { onCustomerChatDialogShow, onCustomerChatDialogHide } = this.props
if (onCustomerChatDialogShow) {
window.FB.Event.subscribe(
'customerchat.dialogShow',
onCustomerChatDialogShow
)
}
if (onCustomerChatDialogHide) {
window.FB.Event.subscribe(
'customerchat.dialogHide',
onCustomerChatDialogHide
)
}
}
createMarkup() {
const {
pageId,
htmlRef,
minimized,
themeColor,
loggedInGreeting,
loggedOutGreeting,
greetingDialogDisplay,
greetingDialogDelay
} = this.props
const refAttribute = htmlRef !== undefined ? `ref="${htmlRef}"` : ''
const minimizedAttribute =
minimized !== undefined ? `minimized="${minimized}"` : ''
const themeColorAttribute =
themeColor !== undefined ? `theme_color="${themeColor}"` : ''
const loggedInGreetingAttribute =
loggedInGreeting !== undefined
? `logged_in_greeting="${loggedInGreeting}"`
: ''
const loggedOutGreetingAttribute =
loggedOutGreeting !== undefined
? `logged_out_greeting="${loggedOutGreeting}"`
: ''
const greetingDialogDisplayAttribute =
greetingDialogDisplay !== undefined
? `greeting_dialog_display="${greetingDialogDisplay}"`
: ''
const greetingDialogDelayAttribute =
greetingDialogDelay !== undefined
? `greeting_dialog_delay="${greetingDialogDelay}"`
: ''
return {
__html: `<div
class="fb-customerchat"
page_id="${pageId}"
${refAttribute}
${minimizedAttribute}
${themeColorAttribute}
${loggedInGreetingAttribute}
${loggedOutGreetingAttribute}
${greetingDialogDisplayAttribute}
${greetingDialogDelayAttribute}
></div>`
}
}
render() {
const { fbLoaded, shouldShowDialog } = this.state
if (fbLoaded && shouldShowDialog !== this.props.shouldShowDialog) {
document.addEventListener(
'DOMNodeInserted',
(event) => {
const element = event.target
if (
element.className &&
typeof element.className === 'string' &&
element.className.includes('fb_dialog')
) {
this.controlPlugin()
}
},
false
)
this.subscribeEvents()
}
// Add a random key to rerender. Reference:
// https://stackoverflow.com/questions/30242530/dangerouslysetinnerhtml-doesnt-update-during-render
return <div key={Date()} dangerouslySetInnerHTML={this.createMarkup()} />
}
}
const removeElementByIds = (ids) => {
ids.forEach((id) => {
const element = document.getElementById(id)
if (element && element.parentNode) {
element.parentNode.removeChild(element)
}
})
}
MessengerCustomerChat.propTypes = {
pageId: PropTypes.string.isRequired,
appId: PropTypes.string,
shouldShowDialog: PropTypes.bool,
htmlRef: PropTypes.string,
minimized: PropTypes.bool,
themeColor: PropTypes.string,
loggedInGreeting: PropTypes.string,
loggedOutGreeting: PropTypes.string,
greetingDialogDisplay: PropTypes.oneOf(['show', 'hide', 'fade']),
greetingDialogDelay: PropTypes.number,
autoLogAppEvents: PropTypes.bool,
xfbml: PropTypes.bool,
version: PropTypes.string,
language: PropTypes.string,
onCustomerChatDialogShow: PropTypes.func,
onCustomerChatDialogHide: PropTypes.func
}
MessengerCustomerChat.defaultProps = {
appId: null,
shouldShowDialog: false,
htmlRef: undefined,
minimized: undefined,
themeColor: undefined,
loggedInGreeting: undefined,
loggedOutGreeting: undefined,
greetingDialogDisplay: undefined,
greetingDialogDelay: undefined,
autoLogAppEvents: true,
xfbml: true,
version: '11.0',
language: 'en_US',
onCustomerChatDialogShow: undefined,
onCustomerChatDialogHide: undefined
}

View File

@@ -2,13 +2,17 @@ import BLOG from '@/blog.config'
import { FacebookProvider, Page } from 'react-facebook'
import { FacebookIcon } from 'react-share'
/**
* facebook个人主页
* @returns
*/
const FacebookPage = () => {
if (!BLOG.FACEBOOK_APP_ID && !BLOG.FACEBOOK_PAGE) {
if (!BLOG.FACEBOOK_APP_ID || !BLOG.FACEBOOK_PAGE) {
return <></>
}
return <div className="shadow-md hover:shadow-xl dark:text-gray-300 border dark:border-black rounded-xl px-2 py-4 bg-white dark:bg-hexo-black-gray lg:duration-100 justify-center">
{BLOG.FACEBOOK_PAGE && (
<div className="flex items-center pb-2">
<div className="flex items-center pb-2">
<a
href={BLOG.FACEBOOK_PAGE}
target="_blank"

View File

@@ -9,7 +9,7 @@ const FullScreenButton = () => {
const [isFullScreen, setIsFullScreen] = useState(false)
const handleFullScreenClick = () => {
if (!isBrowser()) {
if (!isBrowser) {
return
}
const element = document.documentElement

View File

@@ -21,7 +21,7 @@ export default function GoogleAdsense() {
for (let i = 0; i <= ads.length; i++) {
try {
adsbygoogle.push(ads[i])
console.log('adsbygoogle', i, ads[i], adsbygoogle)
// console.log('adsbygoogle', i, ads[i], adsbygoogle)
} catch (e) {
}

View File

@@ -65,7 +65,7 @@ const NotionPage = ({ post, className }) => {
// 将相册gallery下的图片加入放大功能
if (JSON.parse(BLOG.POST_DISABLE_GALLERY_CLICK)) {
setTimeout(() => {
if (isBrowser()) {
if (isBrowser) {
const imgList = document?.querySelectorAll('.notion-collection-card-cover img')
if (imgList && zoomRef.current) {
for (let i = 0; i < imgList.length; i++) {
@@ -80,6 +80,25 @@ const NotionPage = ({ post, className }) => {
}
}, 800)
}
/**
* 处理页面内连接跳转
* 如果链接就是当前网站,则不打开新窗口访问
*/
if (isBrowser) {
const currentURL = window.location.origin + window.location.pathname
const allAnchorTags = document.getElementsByTagName('a') // 或者使用 document.querySelectorAll('a') 获取 NodeList
for (const anchorTag of allAnchorTags) {
if (anchorTag?.target === '_blank') {
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
const hrefWithRelativeHash = currentURL.split('#')[0] + anchorTag.href.split('#')[1]
if (currentURL === hrefWithoutQueryHash || currentURL === hrefWithRelativeHash) {
anchorTag.target = '_self'
}
}
}
}
}, [])
if (!post || !post.blockMap) {

View File

@@ -141,7 +141,6 @@ const renderMermaid = async() => {
if (needLoad) {
loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => {
const mermaid = window.mermaid
console.log('mermaid加载成功', url, mermaid)
mermaid.contentLoaded()
})
}

34
components/QrCode.js Normal file
View File

@@ -0,0 +1,34 @@
import BLOG from '@/blog.config'
import { loadExternalResource } from '@/lib/utils'
import { useEffect } from 'react'
/**
* 二维码生成
*/
export default function QrCode({ value }) {
useEffect(() => {
let qrcode
if (!value) {
return
}
loadExternalResource(BLOG.QR_CODE_CDN, 'js').then(url => {
const QRCode = window.QRCode
qrcode = new QRCode(document.getElementById('qrcode'), {
text: value,
width: 256,
height: 256,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
})
console.log('二维码', qrcode, value)
})
return () => {
if (qrcode) {
qrcode.clear() // clear the code.
}
}
}, [])
return <div id="qrcode"></div>
}

View File

@@ -49,12 +49,7 @@ import {
HatenaIcon
} from 'react-share'
const QRCode = dynamic(
() => {
return import('qrcode.react')
},
{ ssr: false }
)
const QrCode = dynamic(() => import('@/components/QrCode'), { ssr: false })
/**
* @author https://github.com/txs
@@ -353,8 +348,8 @@ const ShareButtons = ({ shareUrl, title, body, image }) => {
</div>
<div className='absolute'>
<div id='pop' className={(qrCodeShow ? 'opacity-100 ' : ' invisible opacity-0') + ' z-40 absolute bottom-10 -left-10 bg-white shadow-xl transition-all duration-200 text-center'}>
<div className='p-2 mt-1'>
<QRCode value={shareUrl} fgColor='#000000' />
<div className='p-2 mt-1 w-28 h-28'>
<QrCode value={shareUrl}/>
</div>
<span className='text-black font-semibold p-1 rounded-t-lg text-sm mx-auto mb-1'>
{locale.COMMON.SCAN_QR_CODE}

View File

@@ -13,8 +13,7 @@ import { useEffect } from 'react'
const Twikoo = ({ isDarkMode }) => {
const loadTwikoo = async () => {
try {
const url = await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
console.log('twikoo 加载成功', url)
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

View File

@@ -6,7 +6,6 @@ const ValineComponent = ({ path }) => {
const loadValine = async () => {
try {
await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js')
// console.log('valine 加载成功', url)
const Valine = window.Valine
// eslint-disable-next-line no-unused-vars
const valine = new Valine({

View File

@@ -91,7 +91,7 @@ export function GlobalContextProvider(props) {
* @param {*} setTheme
*/
const initTheme = () => {
if (isBrowser()) {
if (isBrowser) {
setTimeout(() => {
const elements = document.querySelectorAll('[id^="theme-"]')
if (elements?.length > 1) {

View File

@@ -64,7 +64,7 @@ export function generateLocaleDict(langString) {
* 根据用户当前浏览器语言进行切换
*/
export function initLocale(lang, locale, changeLang, changeLocale) {
if (isBrowser()) {
if (isBrowser) {
const queryLang = getQueryVariable('lang') || loadLangFromCookies() || window.navigator.language
let currentLang = lang
if (queryLang !== lang) {

View File

@@ -1,6 +1,12 @@
// 封装异步加载资源的方法
import { memo } from 'react'
/**
* 判断是否客户端
* @returns {boolean}
*/
export const isBrowser = typeof window !== 'undefined'
/**
* 组件持久化
*/
@@ -39,8 +45,14 @@ export function loadExternalResource(url, type) {
tag.src = url
}
if (tag) {
tag.onload = () => resolve(url)
tag.onerror = () => reject(url)
tag.onload = () => {
console.log('Load Success', url)
resolve(url)
}
tag.onerror = () => {
console.log('Load Error', url)
reject(url)
}
document.head.appendChild(tag)
}
})
@@ -52,7 +64,7 @@ export function loadExternalResource(url, type) {
* @returns
*/
export function getQueryVariable(key) {
const query = isBrowser() ? window.location.search.substring(1) : ''
const query = isBrowser ? window.location.search.substring(1) : ''
const vars = query.split('&')
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split('=')
@@ -112,24 +124,32 @@ export function isIterable(obj) {
return obj != null && typeof obj[Symbol.iterator] === 'function'
}
/**
* 深拷贝对象
* 根据源对象类型深度复制支持object和array
* @param {*} obj
* @returns
*/
export function deepClone(obj) {
const newObj = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === 'object') {
if (Array.isArray(obj)) {
// If obj is an array, create a new array and deep clone each element
return obj.map(item => deepClone(item))
} else if (obj && typeof obj === 'object') {
const newObj = {}
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (obj[key] instanceof Date) { // 判断属性值是否为 Date 对象
newObj[key] = new Date(obj[key].getTime()).toISOString() // 直接拷贝引用
} else if (obj[key] && typeof obj[key] === 'object') {
newObj[key] = deepClone(obj[key])
if (obj[key] instanceof Date) {
newObj[key] = new Date(obj[key].getTime()).toISOString()
} else {
newObj[key] = obj[key]
newObj[key] = deepClone(obj[key])
}
}
}
return newObj
} else {
return obj
}
return newObj
}
/**
* 延时
* @param {*} ms
@@ -137,12 +157,6 @@ export function deepClone(obj) {
*/
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* 判断是否客户端
* @returns {boolean}
*/
export const isBrowser = () => typeof window !== 'undefined'
/**
* 获取从第1页到指定页码的文章
* @param pageIndex 第几页
@@ -162,7 +176,7 @@ export const getListByPage = function (list, pageIndex, pageSize) {
*/
export const isMobile = () => {
let isMobile = false
if (!isBrowser()) {
if (!isBrowser) {
return isMobile
}

View File

@@ -1,6 +1,6 @@
{
"name": "notion-next",
"version": "4.0.8",
"version": "4.0.11",
"homepage": "https://github.com/tangly1024/NotionNext.git",
"license": "MIT",
"repository": {
@@ -43,18 +43,14 @@
"nprogress": "^0.2.0",
"preact": "^10.5.15",
"prism-themes": "1.9.0",
"qrcode.react": "^1.0.1",
"react": "^18.2.0",
"react-cookies": "^0.1.1",
"react-cusdis": "^2.1.3",
"react-dom": "^18.2.0",
"react-facebook": "^8.1.4",
"react-messenger-customer-chat": "^0.8.0",
"react-notion-x": "6.16.0",
"react-share": "^4.4.1",
"react-tweet-embed": "~2.0.0",
"typed.js": "^2.0.12",
"use-ackee": "^3.0.0"
"typed.js": "^2.0.12"
},
"devDependencies": {
"@waline/client": "^2.5.1",

View File

@@ -41,7 +41,7 @@ const Slug = props => {
// 404
if (!post) {
setTimeout(() => {
if (isBrowser()) {
if (isBrowser) {
const article = document.getElementById('notion-article')
if (!article) {
router.push('/404').then(() => {
@@ -111,13 +111,12 @@ export async function getStaticProps({ params: { prefix } }) {
// 处理非列表内文章的内信息
if (!props?.post) {
const pageId = prefix.slice(-1)[0]
const pageId = prefix
if (pageId.length >= 32) {
const post = await getNotion(pageId)
props.post = post
}
}
// 无法获取文章
if (!props?.post) {
props.post = null

View File

@@ -15,7 +15,7 @@ const ArchiveIndex = props => {
const Layout = getLayoutByTheme(useRouter())
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
const anchor = window.location.hash
if (anchor) {
setTimeout(() => {

View File

@@ -263,3 +263,9 @@ a.avatar-wrapper {
.reply-author-name {
font-weight: 500;
}
.forbid-copy {
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}

View File

@@ -9,7 +9,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<div className="rounded px-2 md:pl-0 md:mr-3 my-4 md:pr-3 text-gray-700 dark:text-gray-200 no-underline md:border-r border-gray-light">
<Link href={link?.to} >
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
{link?.icon && <i className={link?.icon} />} {link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
</Link>
@@ -27,7 +27,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 top-12' : 'invisible opacity-0 top-10'} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link.subMenus.map((sLink, index) => {
return <li key={index} className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -177,7 +177,7 @@ const LayoutSearch = props => {
const slotTop = <div className='pb-12'><SearchInput {...props} /></div>
const router = useRouter()
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
// 高亮搜索到的结果
const container = document.getElementById('posts-wrapper')
if (keyword && container) {

View File

@@ -77,7 +77,7 @@ const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
* @returns
*/
const calculateColumns = () => {
if (!isBrowser()) {
if (!isBrowser) {
return 3
} else {
if (window.innerWidth >= 1024) {

View File

@@ -34,7 +34,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className={ (selected ? 'bg-gray-600 text-white hover:text-white' : 'hover:text-gray-600') + ' px-5 w-full text-left duration-200 dark:bg-hexo-black-gray dark:border-black'} onClick={toggleShow} >
{!hasSubMenu && <Link href={link?.to} className='dark:text-gray-200 py-2 w-full my-auto items-center justify-between flex '>
{!hasSubMenu && <Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='dark:text-gray-200 py-2 w-full my-auto items-center justify-between flex '>
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
</Link>}
@@ -52,7 +52,7 @@ export const MenuItemCollapse = (props) => {
return <div key={sLink.id} className='whitespace-nowrap dark:text-gray-200
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<div><div className={`${sLink.icon} text-center w-3 mr-3 text-xs`} />{sLink.title}</div>
</Link>
</div>

View File

@@ -9,7 +9,7 @@ export const MenuItemDrop = ({ link }) => {
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 '>
{!hasSubMenu &&
<Link href={link?.to} className='w-full my-auto items-center justify-between flex ' >
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='w-full my-auto items-center justify-between flex ' >
<div><div className={`${link.icon} text-center w-4 mr-2`} />{link.name}</div>
{link.slot}
</Link>}
@@ -25,7 +25,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <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 `}>
{link?.subMenus?.map((sLink, index) => {
return <li key={index}>
<Link href={sLink.to} className='my-auto py-1 px-2 items-center justify-start flex text-gray-500 dark:text-gray-300 hover:text-black hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 '>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='my-auto py-1 px-2 items-center justify-start flex text-gray-500 dark:text-gray-300 hover:text-black hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 '>
{sLink.icon && <i className={`${sLink.icon} w-4 text-center `} />}
<div className={'ml-2 whitespace-nowrap'}>{sLink.name}</div>
{sLink.slot}

View File

@@ -57,7 +57,7 @@ const LayoutBase = (props) => {
// 在组件卸载时保存 open 状态到本地存储中
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
localStorage.setItem('fukasawa-sidebar-collapse', isCollapsed)
}
}, [isCollapsed])
@@ -149,7 +149,7 @@ const LayoutSearch = props => {
const { keyword } = props
const router = useRouter()
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,

View File

@@ -0,0 +1,36 @@
import BLOG from '@/blog.config'
import Link from 'next/link'
/**
* 归档分组
* @param {*} param0
* @returns
*/
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
return (
<div key={archiveTitle}>
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
{archiveTitle}
</div>
<ul>
{archivePosts[archiveTitle]?.map(post => (
<li key={post.id}
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
>
<div id={post?.publishDay}>
<span className="text-gray-400">
{post.date?.start_date}
</span>{' '}
&nbsp;
<Link passHref href={`${BLOG.SUB_PATH}/${post.slug}`}
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
{post.title}
</Link>
</div>
</li>
))}
</ul>
</div>
)
}

View File

@@ -49,7 +49,7 @@ const Catalog = ({ post }) => {
}
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
if (isBrowser() && tocIds?.length > 0) {
if (isBrowser && tocIds?.length > 0) {
for (const tocWrapper of document?.getElementsByClassName('toc-wrapper')) {
tocWrapper?.scrollTo({ top: 28 * index, behavior: 'smooth' })
}

View File

@@ -34,12 +34,11 @@ export const MenuItemCollapse = (props) => {
return <>
<div className={ (selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600') + ' px-7 w-full text-left duration-200 dark:bg-hexo-black-gray dark:border-black'} onClick={toggleShow} >
{!hasSubMenu && <Link href={link?.to} className='py-2 w-full my-auto items-center justify-between flex '>
{!hasSubMenu && <Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='py-2 w-full my-auto items-center justify-between flex '>
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
</Link>}
{hasSubMenu && <div
onClick={hasSubMenu ? toggleOpenSubMenu : null}
{hasSubMenu && <div onClick={hasSubMenu ? toggleOpenSubMenu : null}
className="py-2 font-extralight flex justify-between cursor-pointer dark:text-gray-200 no-underline tracking-widest">
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
<div className='inline-flex items-center '><i className={`px-2 fas fa-chevron-right transition-all duration-200 ${isOpen ? 'rotate-90' : ''}`}></i></div>
@@ -52,7 +51,7 @@ export const MenuItemCollapse = (props) => {
return <div key={sLink.id} className='
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-200
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<div><div className={`${sLink.icon} text-center w-3 mr-3 text-xs`} />{sLink.title}</div>
</Link>
</div>

View File

@@ -29,7 +29,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<div className={'px-2 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<Link href={link?.to}>
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
{link?.icon && <i className={link?.icon} />} {link?.name}
</Link>
</div>
@@ -39,7 +39,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 top-12 ' : 'invisible opacity-0 top-10 '} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link?.subMenus?.map((sLink, index) => {
return <li key={index} className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-xs font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
const currentRef = targetRef?.current || targetRef
const [percent, changePercent] = useState(0)
const scrollListener = () => {
const target = currentRef || (isBrowser() && document.getElementById('posts-wrapper'))
const target = currentRef || (isBrowser && document.getElementById('posts-wrapper'))
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -31,6 +31,9 @@ import { ArticleLock } from './components/ArticleLock'
import { Transition } from '@headlessui/react'
import { Style } from './style'
import CommonHead from '@/components/CommonHead'
import BlogArchiveItem from './components/BlogArchiveItem'
import BlogPostListPage from './components/BlogPostListPage'
import Link from 'next/link'
// 主题全局变量
const ThemeGlobalGitbook = createContext()
@@ -53,7 +56,6 @@ const LayoutBase = (props) => {
const showTocButton = post?.toc?.length > 1
useEffect(() => {
console.log('更新导航', allNavPages)
setFilteredNavPages(allNavPages)
}, [post])
@@ -171,7 +173,7 @@ const LayoutIndex = (props) => {
router.push(CONFIG.INDEX_PAGE).then(() => {
// console.log('跳转到指定首页', CONFIG.INDEX_PAGE)
setTimeout(() => {
if (isBrowser()) {
if (isBrowser) {
const article = document.getElementById('notion-article')
if (!article) {
console.log('请检查您的Notion数据库中是否包含此slug页面 ', CONFIG.INDEX_PAGE)
@@ -194,7 +196,9 @@ const LayoutIndex = (props) => {
* @returns
*/
const LayoutPostList = (props) => {
return <LayoutBase {...props} />
return <LayoutBase {...props} >
<div className='mt-10'><BlogPostListPage {...props} /></div>
</LayoutBase>
}
/**
@@ -253,13 +257,19 @@ const LayoutSearch = (props) => {
}
/**
* 没有归档
* 归档页面基本不会用到
* 全靠页面导航
* @param {*} props
* @returns
*/
const LayoutArchive = (props) => {
return <LayoutBase {...props}></LayoutBase>
const { archivePosts } = props
return <LayoutBase {...props}>
<div className="mb-10 pb-20 md:py-12 py-3 min-h-full">
{Object.keys(archivePosts)?.map(archiveTitle => <BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />)}
</div>
</LayoutBase>
}
/**
@@ -275,14 +285,57 @@ const Layout404 = props => {
* 分类列表
*/
const LayoutCategoryIndex = (props) => {
return <LayoutBase {...props}></LayoutBase>
const { categoryOptions } = props
const { locale } = useGlobal()
return <LayoutBase {...props}>
<div className='bg-white dark:bg-gray-700 py-10'>
<div className='dark:text-gray-200 mb-5'>
<i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}:
</div>
<div id='category-list' className='duration-200 flex flex-wrap'>
{categoryOptions?.map(category => {
return (
<Link
key={category.name}
href={`/category/${category.name}`}
passHref
legacyBehavior>
<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'}>
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
</div>
</Link>
)
})}
</div>
</div>
</LayoutBase>
}
/**
* 标签列表
*/
const LayoutTagIndex = (props) => {
return <LayoutBase {...props}></LayoutBase>
const { tagOptions } = props
const { locale } = useGlobal()
return <LayoutBase {...props}>
<div className="bg-white dark:bg-gray-700 py-10">
<div className="dark:text-gray-200 mb-5">
<i className="mr-4 fas fa-tag" />
{locale.COMMON.TAGS}:
</div>
<div id="tags-list" className="duration-200 flex flex-wrap">
{tagOptions?.map(tag => {
return (
<div key={tag.name} className="p-2">
<TagItemMini key={tag.name} tag={tag} />
</div>
)
})}
</div>
</div>
</LayoutBase>
}
export {

View File

@@ -24,7 +24,7 @@ const MenuGroupCard = (props) => {
<div key={index} className=''>
<Link title={link.to}
href={link.to}
target={link.to.indexOf('http') === 0 ? '_blank' : '_self'}
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className={'w-full flex items-center justify-between py-1 hover:scale-105 duration-200 transform dark:hover:text-indigo-400 hover:text-indigo-600 px-2 cursor-pointer'}>
<>
<div>{link.name} :</div>

View File

@@ -28,7 +28,7 @@ export const MenuItemCollapse = ({ link }) => {
return <>
<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}
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">
<span className=' transition-all items-center duration-200'>{link?.icon && <i className={link.icon + ' mr-4'} />}{link?.name}</span>
</Link>}
@@ -44,7 +44,7 @@ export const MenuItemCollapse = ({ link }) => {
{hasSubMenu && <Collapse isOpen={isOpen} className='rounded-xl'>
{link.subMenus.map(sLink => {
return <div key={sLink.id} className='dark:bg-black dark:text-gray-200 text-left px-3 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm ml-4 whitespace-nowrap'>{link?.icon && <i className={sLink.icon + ' mr-2'} />} {sLink.title}</span>
</Link>
</div>

View File

@@ -14,6 +14,7 @@ export const MenuItemDrop = ({ link }) => {
{/* 不含子菜单 */}
{!hasSubMenu &&
<Link
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
href={link?.to}
className="font-sans hover:bg-black hover:bg-opacity-10 rounded-2xl flex justify-center items-center px-3 py-1 no-underline tracking-widest">
{link?.icon && <i className={link?.icon} />} {link?.name}
@@ -30,7 +31,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul style={{ backdropFilter: 'blur(3px)' }} className={`${show ? 'visible opacity-100 top-14' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-xl bg-white transition-all duration-300 z-20 absolute`}>
{link.subMenus.map((sLink, index) => {
return <li key={index} className='cursor-pointer hover:bg-blue-600 hover:text-white text-gray-900 tracking-widest transition-all duration-200 dark:border-gray-700 py-1 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -84,12 +84,12 @@ const NavBar = props => {
}
}
if (isBrowser()) {
if (isBrowser) {
window.addEventListener('scroll', handleScroll)
}
return () => {
if (isBrowser()) {
if (isBrowser) {
window.removeEventListener('scroll', handleScroll)
}
}

View File

@@ -11,7 +11,7 @@ export function Swipe({ items }) {
const [activeIndex, setActiveIndex] = useState(0)
const handleClick = (item) => {
if (isBrowser()) {
if (isBrowser) {
window.open(item?.url)
}
}

View File

@@ -25,7 +25,7 @@ const MenuGroupCard = (props) => {
key={`${link.to}`}
title={link.to}
href={link.to}
target={link.to.indexOf('http') === 0 ? '_blank' : '_self'}
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className={'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'}>
<div className='w-full items-center justify-center hover:scale-105 duration-200 transform dark:hover:text-indigo-400 hover:text-indigo-600'>

View File

@@ -28,7 +28,7 @@ export const MenuItemCollapse = ({ link }) => {
return <>
<div className='w-full px-8 py-3 text-left dark:bg-hexo-black-gray' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to}
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">
<span className=' transition-all items-center duration-200'>{link?.icon && <i className={link.icon + ' mr-4'} />}{link?.name}</span>
</Link>}
@@ -44,7 +44,7 @@ export const MenuItemCollapse = ({ link }) => {
{hasSubMenu && <Collapse isOpen={isOpen}>
{link.subMenus.map((sLink, index) => {
return <div key={index} className='dark:bg-black dark:text-gray-200 text-left px-10 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm ml-4 whitespace-nowrap'>{link?.icon && <i className={sLink.icon + ' mr-2'} />} {sLink.title}</span>
</Link>
</div>

View File

@@ -13,7 +13,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<Link
href={link?.to}
href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className="font-sans menu-link pl-2 pr-4 text-gray-700 dark:text-gray-200 no-underline tracking-widest pb-1">
{link?.icon && <i className={link?.icon}/>} {link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
@@ -30,7 +30,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul style={{ backdropFilter: 'blur(3px)' }} className={`${show ? 'visible opacity-100 top-12' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-md bg-white transition-all duration-300 z-20 absolute block `}>
{link.subMenus.map((sLink, index) => {
return <li key={index} className='cursor-pointer hover:bg-indigo-300 text-gray-900 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
const currentRef = targetRef?.current || targetRef
const [percent, changePercent] = useState(0)
const scrollListener = () => {
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -71,7 +71,7 @@ const LayoutBase = props => {
{/* 主区块 */}
<main id="wrapper" className={`${CONFIG.HOME_BANNER_ENABLE ? '' : 'pt-16'} bg-hexo-background-gray dark:bg-black w-full py-8 md:px-8 lg:px-24 min-h-screen relative`}>
<div id="container-inner" className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + ' w-full mx-auto lg:flex lg:space-x-4 justify-center relative z-10'} >
<div className={`${className || ''} w-full max-w-4xl h-full overflow-x-hidden`}>
<div className={`${className || ''} w-full max-w-4xl h-full overflow-hidden`}>
<Transition
show={!onLoading}
@@ -191,7 +191,7 @@ const LayoutSlug = props => {
const { post, lock, validPassword } = props
const drawerRight = useRef(null)
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
const targetRef = isBrowser ? document.getElementById('article-wrapper') : null
const floatSlot = <>
{post?.toc?.length > 1 && <div className="block lg:hidden">
@@ -254,7 +254,7 @@ const Layout404 = props => {
useEffect(() => {
// 延时3秒如果加载失败就返回首页
setTimeout(() => {
if (isBrowser()) {
if (isBrowser) {
const article = document.getElementById('notion-article')
if (!article) {
router.push('/').then(() => {

View File

@@ -25,7 +25,7 @@ const MenuGroupCard = (props) => {
key={`${link.to}`}
title={link.to}
href={link.to}
target={link.to.indexOf('http') === 0 ? '_blank' : '_self'}
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className={'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'}>
<div className='w-full items-center justify-center hover:scale-105 duration-200 transform dark:hover:text-indigo-400 hover:text-indigo-600'>

View File

@@ -33,7 +33,7 @@ export const MenuItemCollapse = ({ link }) => {
<div onClick={toggleShow} className={'py-2 px-5 duration-300 text-base justify-between hover:bg-indigo-700 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
(selected ? 'bg-indigo-500 text-white ' : ' text-black dark:text-white ')}>
{!hasSubMenu && <Link href={link?.to}>
{!hasSubMenu && <Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<div className='my-auto items-center justify-between flex '>
{link.icon && <i className={`${link.icon} w-4 mr-6 text-center`} />}
<div >{link.name}</div>
@@ -51,7 +51,7 @@ export const MenuItemCollapse = ({ link }) => {
{hasSubMenu && <Collapse isOpen={isOpen}>
{link.subMenus.map(sLink => {
return <div key={sLink.id} className='cursor-pointer whitespace-nowrap dark:text-gray-200 w-full font-extralight dark:bg-black text-left px-5 justify-start bg-gray-100 hover:bg-indigo-700 hover:text-white dark:hover:bg-gray-900 tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm'><i className={`${sLink.icon} w-4 mr-3 text-center`} />{sLink.title}</span>
</Link>
</div>

View File

@@ -13,7 +13,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<Link
href={link?.to}
href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className="font-sans menu-link pl-2 pr-4 no-underline tracking-widest pb-1">
{link?.icon && <i className={link?.icon} />} {link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
@@ -30,7 +30,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul style={{ backdropFilter: 'blur(3px)' }} className={`${show ? 'visible opacity-100 top-12' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-md bg-white transition-all duration-300 z-20 absolute block `}>
{link.subMenus.map(sLink => {
return <li key={sLink.id} className='cursor-pointer hover:bg-indigo-300 text-gray-900 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -11,7 +11,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
const [percent, changePercent] = useState(0)
const scrollListener = () => {
requestAnimationFrame(() => {
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -34,7 +34,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className={ (selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600') + ' px-5 w-full text-left duration-200 dark:bg-hexo-black-gray dark:border-black'} onClick={toggleShow} >
{!hasSubMenu && <Link href={link?.to} className='py-2 w-full my-auto items-center justify-between flex '>
{!hasSubMenu && <Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='py-2 w-full my-auto items-center justify-between flex '>
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
</Link>}
@@ -52,7 +52,7 @@ export const MenuItemCollapse = (props) => {
return <div key={sLink.id} className='
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-200
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<div><div className={`${sLink.icon} text-center w-3 mr-3 text-xs`} />{sLink.title}</div>
</Link>
</div>

View File

@@ -29,7 +29,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<div className={'px-3 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<Link href={link?.to}>
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
{link?.icon && <i className={link?.icon} />} {link?.name}
</Link>
</div>
@@ -39,7 +39,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 top-12 ' : 'invisible opacity-0 top-10 '} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link?.subMenus?.map(sLink => {
return <li key={sLink.id} className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-xs font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
const currentRef = targetRef?.current || targetRef
const [percent, changePercent] = useState(0)
const scrollListener = () => {
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -206,7 +206,7 @@ const LayoutSearch = (props) => {
const currentSearch = keyword || router?.query?.s
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,

View File

@@ -24,8 +24,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className='px-5 py-2 w-full text-left duration-200 hover:bg-gray-700 hover:text-white not:last-child:border-b-0 border-b dark:bg-hexo-black-gray dark:border-black' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to}
{!hasSubMenu && <Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className='w-full my-auto items-center justify-between flex dark:text-gray-200 '>
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
</Link>}
@@ -44,7 +43,7 @@ export const MenuItemCollapse = (props) => {
return <div key={sLink.id} className='whitespace-nowrap dark:text-gray-200
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<div>{sLink.icon && <div className={`${sLink.icon} text-center w-3 mr-3 text-xs`} />}{sLink.title}</div>
</Link>
</div>

View File

@@ -9,7 +9,7 @@ export const MenuItemDrop = ({ link }) => {
className='relative py-1.5 px-5 duration-300 text-base justify-between hover:bg-gray-700 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center '>
{!hasSubMenu &&
<Link href={link?.to} className='w-full my-auto items-center justify-between flex ' >
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='w-full my-auto items-center justify-between flex ' >
<div><div className={`${link.icon} text-center w-4 mr-4`} />{link.name}</div>
{link.slot}
</Link>}
@@ -25,7 +25,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 left-56' : 'invisible opacity-0 left-40'} whitespace-nowrap 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 => {
return <li key={sLink.id} >
<Link href={sLink.to} className='my-auto h-9 px-2 items-center justify-start flex not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 '>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} className='my-auto h-9 px-2 items-center justify-start flex not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 '>
{sLink.icon && <i className={`${sLink.icon} w-4 text-center`} />}
<div className={'ml-4'}>{sLink.name}</div>
{sLink.slot}

View File

@@ -11,7 +11,7 @@ import { useRouter } from 'next/router'
const PaginationNumber = ({ page, totalPage }) => {
const router = useRouter()
const currentPage = +page
const showNext = page !== totalPage
const showNext = currentPage !== totalPage
const pagePrefix = router.asPath.split('?')[0].replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
const pages = generatePages(pagePrefix, page, currentPage, totalPage)
@@ -21,7 +21,7 @@ const PaginationNumber = ({ page, totalPage }) => {
data-aos-duration="300"
data-aos-once="false"
data-aos-anchor-placement="top-bottom"
className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-hexo-black-gray dark:text-gray-300 py-3 shadow space-x-2">
className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-200 transition-all bg-white dark:bg-hexo-black-gray dark:text-gray-300 py-3 shadow space-x-2">
{/* 上一页 */}
<Link
href={{
@@ -36,7 +36,7 @@ const PaginationNumber = ({ page, totalPage }) => {
<div
rel="prev"
className={`${currentPage === 1 ? 'invisible' : 'block'
} border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
} hover:border-t-2 border-white hover:border-gray-400 dark:hover:border-gray-400 w-8 h-8 justify-center flex items-center cursor-pointer duration-200 transition-all hover:font-bold`}
>
<i className="fas fa-angle-left" />
</div>
@@ -55,7 +55,7 @@ const PaginationNumber = ({ page, totalPage }) => {
<div
rel="next"
className={`${+showNext ? 'block' : 'invisible'
} border-t-2 border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
} hover:border-t-2 border-white hover:border-gray-400 dark:hover:border-gray-400 w-8 h-8 justify-center flex items-center cursor-pointer duration-200 transition-all hover:font-bold`}
>
<i className="fas fa-angle-right" />
</div>
@@ -73,8 +73,8 @@ function getPageElement(pagePrefix, page, currentPage) {
className={
(page + '' === currentPage + ''
? 'font-bold bg-gray-500 dark:bg-gray-400 text-white '
: 'border-t-2 duration-500 border-white hover:border-gray-400 ') +
' border-white dark:border-gray-700 dark:hover:border-gray-400 cursor-pointer w-6 text-center font-light hover:font-bold'
: 'hover:border-t-2 duration-200 transition-all border-white hover:border-gray-400 ') +
' border-white dark:hover:border-gray-400 cursor-pointer w-8 h-8 justify-center flex items-center font-light hover:font-bold'
}>
{page}
@@ -100,7 +100,7 @@ function generatePages(pagePrefix, page, currentPage, totalPage) {
startPage = totalPage - dynamicGroupCount
}
if (startPage > 2) {
pages.push(<div key={-1}>... </div>)
pages.push(<div key={-1} className='select-none'>... </div>)
}
for (let i = 0; i < dynamicGroupCount; i++) {
@@ -110,7 +110,7 @@ function generatePages(pagePrefix, page, currentPage, totalPage) {
}
if (startPage + dynamicGroupCount < totalPage) {
pages.push(<div key={-2}>... </div>)
pages.push(<div key={-2} className='select-none'>... </div>)
}
pages.push(getPageElement(pagePrefix, totalPage, page))

View File

@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
const currentRef = targetRef?.current || targetRef
const [percent, changePercent] = useState(0)
const scrollListener = () => {
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -161,7 +161,7 @@ const LayoutSearch = (props) => {
const { posts, keyword } = props
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,
@@ -201,7 +201,7 @@ const Layout404 = props => {
useEffect(() => {
// 延时3秒如果加载失败就返回首页
setTimeout(() => {
const article = isBrowser() && document.getElementById('article-wrapper')
const article = isBrowser && document.getElementById('article-wrapper')
if (!article) {
router.push('/').then(() => {
// console.log('找不到页面', router.asPath)
@@ -253,7 +253,7 @@ const LayoutArchive = (props) => {
const LayoutSlug = (props) => {
const { post, lock, validPassword } = props
const drawerRight = useRef(null)
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
const targetRef = isBrowser ? document.getElementById('article-wrapper') : null
const floatSlot = <div className='block lg:hidden'>
<TocDrawerButton onClick={() => {
drawerRight?.current?.handleSwitchVisible()

View File

@@ -29,7 +29,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className='w-full px-4 py-2 text-left dark:bg-hexo-black-gray dark:border-black' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to}
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">
<span className=' hover:text-red-400 transition-all items-center duration-200'>{link?.icon && <span className='mr-2'><i className={link.icon}/></span>}{link?.name}</span>
</Link>}
@@ -45,7 +45,7 @@ export const MenuItemCollapse = (props) => {
{hasSubMenu && <Collapse isOpen={isOpen} onHeightChange={props.onHeightChange}>
{link.subMenus.map(sLink => {
return <div key={sLink.id} className='font-extralight dark:bg-black text-left px-10 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-xs'>{sLink.title}</span>
</Link>
</div>

View File

@@ -15,7 +15,7 @@ export const MenuItemDrop = ({ link }) => {
<div className='cursor-pointer ' onMouseOver={() => changeShow(true)} onMouseOut={() => changeShow(false)}>
{!hasSubMenu &&
<div className="block text-black dark:text-gray-50 nav" >
<Link href={link?.to} >
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} >
{link?.icon && <i className={link?.icon} />} {link?.name}
</Link>
</div>
@@ -32,7 +32,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 top-12 ' : 'invisible opacity-0 top-10 '} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link.subMenus.map(sLink => {
return <li key={sLink.id} className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} >
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -134,7 +134,7 @@ const LayoutPostList = props => {
const LayoutSearch = props => {
const { keyword } = props
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,

View File

@@ -29,7 +29,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className='w-full px-4 py-2 text-left dark:bg-hexo-black-gray dark:border-black' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to}
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">
<span className=' hover:text-red-400 transition-all items-center duration-200'>{link?.icon && <span className='mr-2'><i className={link.icon}/></span>}{link?.name}</span>
</Link>}
@@ -45,7 +45,7 @@ export const MenuItemCollapse = (props) => {
{hasSubMenu && <Collapse isOpen={isOpen} onHeightChange={props.onHeightChange}>
{link.subMenus.map((sLink, index) => {
return <div key={index} className='font-extralight dark:bg-black text-left px-10 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-xs'>{sLink.title}</span>
</Link>
</div>

View File

@@ -12,7 +12,7 @@ export const MenuItemDrop = ({ link }) => {
return <li className='cursor-pointer py-2 px-3' onMouseEnter={() => changeShow(true)} onMouseLeave={() => changeShow(false)}>
{!hasSubMenu &&
<div className="block text-black dark:text-gray-50 nav" >
<Link href={link?.to} >
<Link href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'} >
{link?.icon && <i className={link?.icon} />} {link?.name}
</Link>
</div>
@@ -29,7 +29,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 bottom-16 ' : 'invisible opacity-0 bottom-14'} border-gray-100 bg-white rounded-lg overflow-hidden dark:bg-black dark:border-gray-800 bg-opacity-60 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link.subMenus.map((sLink, index) => {
return <li key={index} className='text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{link?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -132,7 +132,7 @@ const LayoutSearch = props => {
const { keyword } = props
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,

View File

@@ -29,7 +29,7 @@ export const MenuItemCollapse = (props) => {
return <>
<div className='w-full px-8 py-3 text-left border-b dark:bg-hexo-black-gray dark:border-black' onClick={toggleShow} >
{!hasSubMenu && <Link
href={link?.to}
href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className="font-extralight items-center flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1">
<span className='text-blue-400 hover:text-red-400 transition-all items-center duration-200'>{link?.icon && <span className='mr-2'><i className={link.icon}/></span>}{link?.name}</span>
</Link>}
@@ -45,7 +45,7 @@ export const MenuItemCollapse = (props) => {
{hasSubMenu && <Collapse isOpen={isOpen} onHeightChange={props.onHeightChange}>
{link.subMenus.map(sLink => {
return <div key={sLink.id} className='font-extralight dark:bg-black text-left px-10 justify-start text-blue-400 bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
<Link href={sLink.to}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='ml-4 text-xs'>{sLink?.icon && <span className='mr-2 w-4'><i className={sLink.icon}/></span>}{sLink.title}</span>
</Link>
</div>

View File

@@ -13,8 +13,7 @@ export const MenuItemDrop = ({ link }) => {
{!hasSubMenu &&
<Link
href={link?.to}
target={link?.target || '_self'}
href={link?.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
className="font-sans menu-link pl-2 pr-4 text-gray-700 dark:text-gray-200 no-underline tracking-widest pb-1">
<>{link?.icon && <span className='mr-2'><i className={link.icon} /></span>}{link?.name}
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}</>
@@ -31,7 +30,7 @@ export const MenuItemDrop = ({ link }) => {
{hasSubMenu && <ul className={`${show ? 'visible opacity-100 top-12' : 'invisible opacity-0 top-10'} border-gray-100 bg-white dark:bg-black dark:border-gray-800 transition-all duration-300 z-20 absolute block drop-shadow-lg `}>
{link.subMenus.map(sLink => {
return <li key={sLink.id} className='not:last-child:border-b-0 border-b text-blue-500 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-2'>
<Link href={sLink.to} target={sLink?.target || '_self'}>
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
<span className='text-sm text-nowrap font-extralight'>{sLink?.icon && <i className={sLink?.icon} > &nbsp; </i>}{sLink.title}</span>
</Link>
</li>

View File

@@ -36,7 +36,7 @@ const LayoutBase = props => {
const { children, slotTop, meta } = props
const { onLoading } = useGlobal()
if (isBrowser()) {
if (isBrowser) {
loadExternalResource('/css/theme-simple.css', 'css')
}
return (
@@ -122,7 +122,7 @@ const LayoutSearch = props => {
const { keyword } = props
useEffect(() => {
if (isBrowser()) {
if (isBrowser) {
replaceSearchResult({
doms: document.getElementById('posts-wrapper'),
search: keyword,

View File

@@ -11,6 +11,12 @@ const Style = () => {
.dark body{
background-color: black;
}
// 文本不可选取
.forbid-copy {
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#theme-simple #announcement-content {
/* background-color: #f6f6f6; */