mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-16 07:26:47 +00:00
Merge branch 'tangly1024:main' into main
This commit is contained in:
@@ -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
|
||||
@@ -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) |
|
||||
|
||||
## 我要如何开始?
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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
21
components/DisableCopy.js
Normal 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
|
||||
}
|
||||
@@ -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/>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -9,7 +9,7 @@ const FullScreenButton = () => {
|
||||
const [isFullScreen, setIsFullScreen] = useState(false)
|
||||
|
||||
const handleFullScreenClick = () => {
|
||||
if (!isBrowser()) {
|
||||
if (!isBrowser) {
|
||||
return
|
||||
}
|
||||
const element = document.documentElement
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
34
components/QrCode.js
Normal 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>
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
52
lib/utils.js
52
lib/utils.js
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,7 +15,7 @@ const ArchiveIndex = props => {
|
||||
const Layout = getLayoutByTheme(useRouter())
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const anchor = window.location.hash
|
||||
if (anchor) {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
36
themes/gitbook/components/BlogArchiveItem.js
Normal file
36
themes/gitbook/components/BlogArchiveItem.js
Normal 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>{' '}
|
||||
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
@@ -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' })
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -84,12 +84,12 @@ const NavBar = props => {
|
||||
}
|
||||
}
|
||||
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function Swipe({ items }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
const handleClick = (item) => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.open(item?.url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -132,7 +132,7 @@ const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} > </i>}{sLink.title}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; */
|
||||
|
||||
Reference in New Issue
Block a user