This commit is contained in:
GH Action - Upstream Sync
2024-05-28 01:09:00 +00:00
14 changed files with 1002 additions and 822 deletions

View File

@@ -1,11 +1,18 @@
import { useState, useImperativeHandle, useRef, useEffect, Fragment } from 'react'
import algoliasearch from 'algoliasearch'
import replaceSearchResult from '@/components/Mark'
import Link from 'next/link'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash/throttle'
import { siteConfig } from '@/lib/config'
import { useHotkeys } from 'react-hotkeys-hook';
import { useGlobal } from '@/lib/global'
import algoliasearch from 'algoliasearch'
import throttle from 'lodash/throttle'
import Link from 'next/link'
import { useRouter } from 'next/router'
import {
Fragment,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
const ShortCutActions = [
{
@@ -20,7 +27,6 @@ const ShortCutActions = [
key: 'Esc',
action: '关闭'
}
]
/**
@@ -36,31 +42,49 @@ export default function AlgoliaSearchModal({ cRef }) {
const [totalPage, setTotalPage] = useState(0)
const [totalHit, setTotalHit] = useState(0)
const [useTime, setUseTime] = useState(0)
const inputRef = useRef(null)
const [activeIndex, setActiveIndex] = useState(0)
const [isLoading, setIsLoading] = useState(false)
useHotkeys('ctrl+k', (e) => {
const inputRef = useRef(null)
const router = useRouter()
/**
* 快捷键设置
*/
useHotkeys('ctrl+k', e => {
e.preventDefault()
setIsModalOpen(true)
})
// 方向键调整选中
useHotkeys('down', (e) => {
e.preventDefault()
if (activeIndex < searchResults.length - 1) {
setActiveIndex(activeIndex + 1)
}
}, { enableOnFormTags: true })
useHotkeys('up', (e) => {
e.preventDefault()
if (activeIndex > 0) {
setActiveIndex(activeIndex - 1)
}
}, { enableOnFormTags: true })
useHotkeys(
'down',
e => {
e.preventDefault()
if (activeIndex < searchResults.length - 1) {
setActiveIndex(activeIndex + 1)
}
},
{ enableOnFormTags: true }
)
useHotkeys(
'up',
e => {
e.preventDefault()
if (activeIndex > 0) {
setActiveIndex(activeIndex - 1)
}
},
{ enableOnFormTags: true }
)
// esc关闭
useHotkeys('esc', (e) => {
e.preventDefault()
setIsModalOpen(false)
}, { enableOnFormTags: true })
useHotkeys(
'esc',
e => {
e.preventDefault()
setIsModalOpen(false)
},
{ enableOnFormTags: true }
)
// 跳转Search结果
const onJumpSearchResult = () => {
if (searchResults.length > 0) {
@@ -68,11 +92,15 @@ export default function AlgoliaSearchModal({ cRef }) {
}
}
// enter跳转
useHotkeys('enter', (e) => {
if (searchResults.length > 0) {
onJumpSearchResult(index)
}
}, { enableOnFormTags: true })
useHotkeys(
'enter',
e => {
if (searchResults.length > 0) {
onJumpSearchResult(index)
}
},
{ enableOnFormTags: true }
)
const resetSearch = () => {
setActiveIndex(0)
@@ -84,6 +112,16 @@ export default function AlgoliaSearchModal({ cRef }) {
if (inputRef.current) inputRef.current.value = ''
}
/**
* 页面路径变化后自动关闭此modal
*/
useEffect(() => {
setIsModalOpen(false)
}, [router])
/**
* 自动聚焦搜索框
*/
useEffect(() => {
if (isModalOpen) {
setTimeout(() => {
@@ -93,9 +131,10 @@ export default function AlgoliaSearchModal({ cRef }) {
resetSearch()
}
}, [isModalOpen])
/**
* 对外暴露方法
*/
* 对外暴露方法
**/
useImperativeHandle(cRef, () => {
return {
openSearch: () => {
@@ -104,7 +143,10 @@ export default function AlgoliaSearchModal({ cRef }) {
}
})
const client = algoliasearch(siteConfig('ALGOLIA_APP_ID'), siteConfig('ALGOLIA_SEARCH_ONLY_APP_KEY'))
const client = algoliasearch(
siteConfig('ALGOLIA_APP_ID'),
siteConfig('ALGOLIA_SEARCH_ONLY_APP_KEY')
)
const index = client.initIndex(siteConfig('ALGOLIA_INDEX'))
/**
@@ -131,7 +173,9 @@ export default function AlgoliaSearchModal({ cRef }) {
setTotalHit(nbHits)
setSearchResults(hits)
setIsLoading(false)
const doms = document.getElementById('search-wrapper').getElementsByClassName('replace')
const doms = document
.getElementById('search-wrapper')
.getElementsByClassName('replace')
setTimeout(() => {
replaceSearchResult({
@@ -149,33 +193,35 @@ export default function AlgoliaSearchModal({ cRef }) {
}
// 定义节流函数,确保在用户停止输入一段时间后才会调用处理搜索的方法
const throttledHandleInputChange = useRef(throttle((query, page = 0) => {
handleSearch(query, page);
}, 1000));
const throttledHandleInputChange = useRef(
throttle((query, page = 0) => {
handleSearch(query, page)
}, 1000)
)
// 用于存储搜索延迟的计时器
const searchTimer = useRef(null);
const searchTimer = useRef(null)
// 修改input的onChange事件处理函数
const handleInputChange = (e) => {
const query = e.target.value;
const handleInputChange = e => {
const query = e.target.value
// 如果已经有计时器在等待搜索,先清除之前的计时器
if (searchTimer.current) {
clearTimeout(searchTimer.current);
clearTimeout(searchTimer.current)
}
// 设置新的计时器,在用户停止输入一段时间后触发搜索
searchTimer.current = setTimeout(() => {
throttledHandleInputChange.current(query);
}, 800);
};
throttledHandleInputChange.current(query)
}, 800)
}
/**
* 切换页码
* @param {*} page
*/
const switchPage = (page) => {
const switchPage = page => {
throttledHandleInputChange.current(keyword, page)
}
@@ -191,58 +237,58 @@ export default function AlgoliaSearchModal({ cRef }) {
}
return (
<div
id="search-wrapper"
className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-12 flex items-start justify-center mt-0`}
>
id='search-wrapper'
className={`${
isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-12 flex items-start justify-center mt-0`}>
{/* 模态框 */}
<div
className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'
} flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}
>
<div className="flex justify-between items-center">
<div className="text-2xl text-blue-600 dark:text-yellow-600 font-bold">搜索</div>
className={`${
isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'
} flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
<div className='flex justify-between items-center'>
<div className='text-2xl text-blue-600 dark:text-yellow-600 font-bold'>
搜索
</div>
<div>
<i
className="text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600"
onClick={closeModal}
></i>
className='text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600'
onClick={closeModal}></i>
</div>
</div>
<input
type="text"
placeholder="在这里输入搜索关键词..."
type='text'
placeholder='在这里输入搜索关键词...'
onChange={e => handleInputChange(e)}
className="text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md"
className='text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md'
ref={inputRef}
/>
{/* 标签组 */}
<div className="mb-4">
<div className='mb-4'>
<TagGroups />
</div>
{
searchResults.length === 0 && keyword && !isLoading && (
<div>
<p className=" text-slate-600 text-center my-4 text-base"> 无法找到相关结果
<span
className='font-semibold'
>&quot;{keyword}&quot;</span></p>
</div>
)
}
{searchResults.length === 0 && keyword && !isLoading && (
<div>
<p className=' text-slate-600 text-center my-4 text-base'>
{' '}
无法找到相关结果
<span className='font-semibold'>&quot;{keyword}&quot;</span>
</p>
</div>
)}
<ul className='flex-1 overflow-auto'>
{searchResults.map((result, index) => (
<li key={result.objectID}
<li
key={result.objectID}
onMouseEnter={() => setActiveIndex(index)}
onClick={() => onJumpSearchResult(index)}
className={`cursor-pointer replace my-2 p-2 duration-100
rounded-lg
${activeIndex === index ? 'bg-blue-600 dark:bg-yellow-600' : ''}`}>
<a
className={`${activeIndex === index ? ' text-white' : ' text-black dark:text-gray-300 '}`}
>
className={`${activeIndex === index ? ' text-white' : ' text-black dark:text-gray-300 '}`}>
{result.title}
</a>
</li>
@@ -250,15 +296,22 @@ export default function AlgoliaSearchModal({ cRef }) {
</ul>
<Pagination totalPage={totalPage} page={page} switchPage={switchPage} />
<div className='flex items-center justify-between mt-2 sm:text-sm text-xs dark:text-gray-300'>
{totalHit === 0 && (<div className='flex items-center'>
{
ShortCutActions.map((action, index) => {
return <Fragment key={index}><div className='border-gray-300 dark:text-gray-300 text-gray-600 px-2 rounded border inline-block'>{action.key}</div>
<span className='ml-2 mr-4 text-gray-600 dark:text-gray-300'>{action.action}</span></Fragment>
})
}
</div>)
}
{totalHit === 0 && (
<div className='flex items-center'>
{ShortCutActions.map((action, index) => {
return (
<Fragment key={index}>
<div className='border-gray-300 dark:text-gray-300 text-gray-600 px-2 rounded border inline-block'>
{action.key}
</div>
<span className='ml-2 mr-4 text-gray-600 dark:text-gray-300'>
{action.action}
</span>
</Fragment>
)
})}
</div>
)}
<div>
{totalHit > 0 && (
<p>
@@ -266,19 +319,18 @@ export default function AlgoliaSearchModal({ cRef }) {
</p>
)}
</div>
<div className="text-gray-600 dark:text-gray-300 text-right">
<span >
<i className="fa-brands fa-algolia"></i> Algolia
<div className='text-gray-600 dark:text-gray-300 text-right'>
<span>
<i className='fa-brands fa-algolia'></i> Algolia
</span>
</div>
</div>
</div>
{/* 遮罩 */}
<div
onClick={closeModal}
className="z-30 fixed top-0 left-0 w-full h-full flex items-center justify-center glassmorphism"
className='z-30 fixed top-0 left-0 w-full h-full flex items-center justify-center glassmorphism'
/>
</div>
)
@@ -292,21 +344,31 @@ function TagGroups() {
// 获取tagOptions数组前十个
const firstTenTags = tagOptions?.slice(0, 10)
return <div id='tags-group' className='dark:border-gray-700 space-y-2'>
{
firstTenTags?.map((tag, index) => {
return <Link passHref
key={index}
href={`/tag/${encodeURIComponent(tag.name)}`}
className={'cursor-pointer inline-block whitespace-nowrap'}>
<div className={'flex items-center text-black dark:text-gray-300 hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'}>
<div className='text-lg'>{tag.name} </div>{tag.count ? <sup className='relative ml-1'>{tag.count}</sup> : <></>}
</div>
</Link>
})
}
</div>
return (
<div id='tags-group' className='dark:border-gray-700 space-y-2'>
{firstTenTags?.map((tag, index) => {
return (
<Link
passHref
key={index}
href={`/tag/${encodeURIComponent(tag.name)}`}
className={'cursor-pointer inline-block whitespace-nowrap'}>
<div
className={
'flex items-center text-black dark:text-gray-300 hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'
}>
<div className='text-lg'>{tag.name} </div>
{tag.count ? (
<sup className='relative ml-1'>{tag.count}</sup>
) : (
<></>
)}
</div>
</Link>
)
})}
</div>
)
}
/**
@@ -321,16 +383,16 @@ function Pagination(props) {
return (
<div className='flex space-x-1 w-full justify-center py-1'>
{Array.from({ length: totalPage }, (_, i) => {
const classNames = page === i
? 'font-bold text-white bg-blue-600 dark:bg-yellow-600 rounded'
: 'hover:text-blue-600 hover:font-bold dark:text-gray-300'
const classNames =
page === i
? 'font-bold text-white bg-blue-600 dark:bg-yellow-600 rounded'
: 'hover:text-blue-600 hover:font-bold dark:text-gray-300'
return (
<div
onClick={() => switchPage(i)}
className={`text-center cursor-pointer w-6 h-6 ${classNames}`}
key={i}
>
key={i}>
{i + 1}
</div>
)

View File

@@ -1,10 +1,10 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState, useRef, useLayoutEffect } from 'react'
import { useGlobal } from '@/lib/global'
import { saveDarkModeToLocalStorage, THEMES } from '@/themes/theme'
import useWindowSize from '@/hooks/useWindowSize'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { THEMES, saveDarkModeToLocalStorage } from '@/themes/theme'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
/**
* 自定义右键菜单
@@ -20,14 +20,14 @@ export default function CustomContextMenu(props) {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
const { latestPosts } = props
const { allNavPages } = props
const router = useRouter()
/**
* 随机跳转文章
*/
function handleJumpToRandomPost() {
const randomIndex = Math.floor(Math.random() * latestPosts.length)
const randomPost = latestPosts[randomIndex]
const randomIndex = Math.floor(Math.random() * allNavPages.length)
const randomPost = allNavPages[randomIndex]
router.push(`${siteConfig('SUB_PATH', '')}/${randomPost?.slug}`)
}
@@ -37,16 +37,26 @@ export default function CustomContextMenu(props) {
}, [])
useEffect(() => {
const handleContextMenu = (event) => {
setShow(false)
}, [router])
useEffect(() => {
const handleContextMenu = event => {
event.preventDefault()
// 计算点击位置加菜单宽高是否超出屏幕,如果超出则贴边弹出
const x = (event.clientX < windowSize.width - width) ? event.clientX : windowSize.width - width
const y = (event.clientY < windowSize.height - height) ? event.clientY : windowSize.height - height
const x =
event.clientX < windowSize.width - width
? event.clientX
: windowSize.width - width
const y =
event.clientY < windowSize.height - height
? event.clientY
: windowSize.height - height
setPosition({ y: `${y}px`, x: `${x}px` })
setShow(true)
}
const handleClick = (event) => {
const handleClick = event => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setShow(false)
}
@@ -80,19 +90,20 @@ export default function CustomContextMenu(props) {
function handleCopyLink() {
const url = window.location.href
navigator.clipboard.writeText(url)
navigator.clipboard
.writeText(url)
.then(() => {
console.log('页面地址已复制')
})
.catch((error) => {
.catch(error => {
console.error('复制页面地址失败:', error)
})
setShow(false)
}
/**
* 切换主题
*/
* 切换主题
*/
function handleChangeTheme() {
const randomTheme = THEMES[Math.floor(Math.random() * THEMES.length)] // 从THEMES数组中 随机取一个主题
const query = router.query
@@ -104,14 +115,14 @@ export default function CustomContextMenu(props) {
* 复制内容
*/
function handleCopy() {
const selectedText = document.getSelection().toString();
const selectedText = document.getSelection().toString()
if (selectedText) {
const tempInput = document.createElement('input');
tempInput.value = selectedText;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand('copy');
document.body.removeChild(tempInput);
const tempInput = document.createElement('input')
tempInput.value = selectedText
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand('copy')
document.body.removeChild(tempInput)
// alert("Text copied: " + selectedText);
} else {
// alert("Please select some text first.");
@@ -130,76 +141,119 @@ export default function CustomContextMenu(props) {
}
return (
<div
ref={menuRef}
style={{ top: position.y, left: position.x }}
className={`${show ? '' : 'invisible opacity-0'} select-none transition-opacity duration-200 fixed z-50`}
>
<div
ref={menuRef}
style={{ top: position.y, left: position.x }}
className={`${show ? '' : 'invisible opacity-0'} select-none transition-opacity duration-200 fixed z-50`}>
{/* 菜单内容 */}
<div className='rounded-xl w-52 dark:hover:border-yellow-600 bg-white dark:bg-[#040404] dark:text-gray-200 dark:border-gray-600 p-3 border drop-shadow-lg flex-col duration-300 transition-colors'>
{/* 顶部导航按钮 */}
<div className='flex justify-between'>
<i
onClick={handleBack}
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-left'></i>
<i
onClick={handleForward}
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-right'></i>
<i
onClick={handleRefresh}
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-rotate-right'></i>
<i
onClick={handleScrollTop}
className='hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-up'></i>
</div>
{/* 菜单内容 */}
<div className='rounded-xl w-52 dark:hover:border-yellow-600 bg-white dark:bg-[#040404] dark:text-gray-200 dark:border-gray-600 p-3 border drop-shadow-lg flex-col duration-300 transition-colors'>
{/* 顶部导航按钮 */}
<div className='flex justify-between'>
<i onClick={handleBack} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-left"></i>
<i onClick={handleForward} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-right"></i>
<i onClick={handleRefresh} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-rotate-right"></i>
<i onClick={handleScrollTop} className="hover:bg-blue-600 hover:text-white px-2 py-2 text-center w-8 rounded cursor-pointer fa-solid fa-arrow-up"></i>
</div>
<hr className='my-2 border-dashed' />
{/* 跳转导航按钮 */}
<div className='w-full px-2'>
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST') && <div onClick={handleJumpToRandomPost} title={locale.MENU.WALK_AROUND} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-podcast mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.WALK_AROUND}</div>
</div>}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY') && <Link href='/category' title={locale.MENU.CATEGORY} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-square-minus mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
</Link>}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG') && <Link href='/tag' title={locale.MENU.TAGS} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-tag mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
</Link>}
</div>
<hr className='my-2 border-dashed' />
{/* 功能按钮 */}
<div className='w-full px-2'>
{siteConfig('CAN_COPY') && (
<div onClick={handleCopy} title={locale.MENU.COPY} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-copy mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.COPY}</div>
</div>
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK') && <div onClick={handleCopyLink} title={locale.MENU.SHARE_URL} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-arrow-up-right-from-square mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.SHARE_URL}</div>
</div>}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE') && <div onClick={handleChangeDarkMode} title={isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
{isDarkMode ? <i className="fa-regular fa-sun mr-2" /> : <i className="fa-regular fa-moon mr-2" />}
<div className='whitespace-nowrap'> {isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}</div>
</div>}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH') && (
<div onClick={handleChangeTheme} title={locale.MENU.THEME_SWITCH} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className="fa-solid fa-palette mr-2" />
<div className='whitespace-nowrap'>{locale.MENU.THEME_SWITCH}</div>
</div>
)}
</div>
<hr className='my-2 border-dashed' />
{/* 跳转导航按钮 */}
<div className='w-full px-2'>
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_RANDOM_POST') && (
<div
onClick={handleJumpToRandomPost}
title={locale.MENU.WALK_AROUND}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-podcast mr-2' />
<div className='whitespace-nowrap'>{locale.MENU.WALK_AROUND}</div>
</div>
</div >
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY') && (
<Link
href='/category'
title={locale.MENU.CATEGORY}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-square-minus mr-2' />
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
</Link>
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG') && (
<Link
href='/tag'
title={locale.MENU.TAGS}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-tag mr-2' />
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
</Link>
)}
</div>
<hr className='my-2 border-dashed' />
{/* 功能按钮 */}
<div className='w-full px-2'>
{siteConfig('CAN_COPY') && (
<div
onClick={handleCopy}
title={locale.MENU.COPY}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-copy mr-2' />
<div className='whitespace-nowrap'>{locale.MENU.COPY}</div>
</div>
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_SHARE_LINK') && (
<div
onClick={handleCopyLink}
title={locale.MENU.SHARE_URL}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-arrow-up-right-from-square mr-2' />
<div className='whitespace-nowrap'>{locale.MENU.SHARE_URL}</div>
</div>
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_DARK_MODE') && (
<div
onClick={handleChangeDarkMode}
title={
isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE
}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
{isDarkMode ? (
<i className='fa-regular fa-sun mr-2' />
) : (
<i className='fa-regular fa-moon mr-2' />
)}
<div className='whitespace-nowrap'>
{' '}
{isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}
</div>
</div>
)}
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH') && (
<div
onClick={handleChangeTheme}
title={locale.MENU.THEME_SWITCH}
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
<i className='fa-solid fa-palette mr-2' />
<div className='whitespace-nowrap'>
{locale.MENU.THEME_SWITCH}
</div>
</div>
)}
</div>
</div>
</div>
)
}