262 lines
10 KiB
Plaintext
262 lines
10 KiB
Plaintext
---
|
||
import { Icon } from "astro-icon/components";
|
||
import DisplaySettings from "@/components/interactive/DisplaySettings.svelte";
|
||
import LayoutSwitchButton from "@/components/interactive/LayoutSwitchButton.svelte";
|
||
import LightDarkSwitch from "@/components/interactive/LightDarkSwitch.svelte";
|
||
import Search from "@/components/interactive/Search.svelte";
|
||
import WallpaperSwitch from "@/components/interactive/WallpaperSwitch.svelte";
|
||
import {
|
||
backgroundWallpaper,
|
||
navBarConfig,
|
||
navBarSearchConfig,
|
||
siteConfig,
|
||
} from "@/config";
|
||
import { LinkPresets } from "@/constants/link-presets";
|
||
import {
|
||
LinkPreset,
|
||
type NavBarLink,
|
||
NavBarSearchMethod,
|
||
} from "@/types/config";
|
||
import { isHomePage } from "@/utils/layout-utils";
|
||
import { url } from "@/utils/url-utils";
|
||
import DropdownMenu from "./DropdownMenu.astro";
|
||
import NavMenuPanel from "./NavMenuPanel.astro";
|
||
|
||
const className = Astro.props.class;
|
||
|
||
// 检查是否允许切换壁纸模式
|
||
const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true;
|
||
|
||
// 检查是否允许切换布局(双侧栏模式下也允许切换,但切换到网格时会自动隐藏右侧边栏)
|
||
const allowLayoutSwitch = siteConfig.postListLayout.allowSwitch;
|
||
|
||
// 获取导航栏透明模式配置
|
||
const navbarTransparentMode =
|
||
backgroundWallpaper.banner?.navbar?.transparentMode || "semi";
|
||
|
||
// 获取导航栏标题,如果没有设置则使用 siteConfig.title
|
||
const navbarTitle = siteConfig.navbar.title || siteConfig.title;
|
||
|
||
// 获取导航栏宽度配置
|
||
const navbarWidthFull = siteConfig.navbar.widthFull ?? false;
|
||
|
||
// 检查是否为首页
|
||
const isHomePageCheck = isHomePage(Astro.url.pathname);
|
||
|
||
let links: NavBarLink[] = navBarConfig.links.map(
|
||
(item: NavBarLink | LinkPreset): NavBarLink => {
|
||
if (typeof item === "number") {
|
||
return LinkPresets[item];
|
||
}
|
||
return item;
|
||
},
|
||
);
|
||
---
|
||
<div id="navbar" class="z-50 onload-animation" data-transparent-mode={navbarTransparentMode} data-is-home={isHomePageCheck} data-full-width={navbarWidthFull}>
|
||
<div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--card-bg)] transition"></div> <!-- used for onload animation -->
|
||
<div class:list={[
|
||
className,
|
||
"!overflow-visible h-[4.5rem] mx-auto flex items-center px-4",
|
||
navbarWidthFull ? "" : "justify-between max-w-[var(--page-width)]"
|
||
]}>
|
||
<a href={url('/')} class="btn-plain scale-animation rounded-lg h-[3.25rem] px-5 font-bold active:scale-95">
|
||
<div class:list={[
|
||
"flex flex-row items-center text-md",
|
||
siteConfig.navbar.followTheme ? "text-[var(--primary)]" : "dark:text-white text-black"
|
||
]}>
|
||
{siteConfig.navbar.logo?.type === "icon" ? (
|
||
<Icon name={siteConfig.navbar.logo.value || "material-symbols:home-pin-outline"} class="text-[1.75rem] mb-1 mr-2" />
|
||
) : siteConfig.navbar.logo?.type === "image" ? (
|
||
<img src={url(siteConfig.navbar.logo.value)} alt={siteConfig.navbar.logo.alt || siteConfig.title} class="h-[1.75rem] w-[1.75rem] mb-1 mr-2 object-contain" loading="lazy" />
|
||
) : (
|
||
<Icon name="material-symbols:home-pin-outline" class="text-[1.75rem] mb-1 mr-2" />
|
||
)}
|
||
{navbarTitle}
|
||
</div>
|
||
</a>
|
||
<div class="hidden md:flex items-center space-x-1">
|
||
{links.map((l) => {
|
||
return <DropdownMenu link={l} />;
|
||
})}
|
||
</div>
|
||
<div class:list={["flex", navbarWidthFull ? "ml-auto" : ""]}>
|
||
<!--<SearchPanel client:load>-->
|
||
<Search
|
||
client:load
|
||
searchMethod={navBarSearchConfig.method}
|
||
meiliSearchConfig={navBarSearchConfig.meiliSearchConfig}
|
||
/>
|
||
{!siteConfig.themeColor.fixed && (
|
||
<button aria-label="Display Settings" class="btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="display-settings-switch">
|
||
<Icon name="material-symbols:palette-outline" class="text-[1.25rem]"></Icon>
|
||
</button>
|
||
)}
|
||
{/* @ts-ignore */}
|
||
{isWallpaperSwitchable && <WallpaperSwitch client:load></WallpaperSwitch>}
|
||
{allowLayoutSwitch && <LayoutSwitchButton client:load currentLayout={siteConfig.postListLayout.defaultMode}></LayoutSwitchButton>}
|
||
{/* @ts-ignore */}
|
||
<LightDarkSwitch client:load></LightDarkSwitch>
|
||
<button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:!hidden" id="nav-menu-switch">
|
||
<Icon name="material-symbols:menu-rounded" class="text-[1.25rem]"></Icon>
|
||
</button>
|
||
</div>
|
||
<NavMenuPanel links={links}></NavMenuPanel>
|
||
<DisplaySettings client:load></DisplaySettings>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function loadButtonScript() {
|
||
let settingBtn = document.getElementById("display-settings-switch");
|
||
if (settingBtn) {
|
||
settingBtn.onclick = function () {
|
||
let settingPanel = document.getElementById("display-setting");
|
||
if (settingPanel) {
|
||
settingPanel.classList.toggle("float-panel-closed");
|
||
}
|
||
};
|
||
}
|
||
|
||
let menuBtn = document.getElementById("nav-menu-switch");
|
||
if (menuBtn) {
|
||
menuBtn.onclick = function () {
|
||
let menuPanel = document.getElementById("nav-menu-panel");
|
||
if (menuPanel) {
|
||
menuPanel.classList.toggle("float-panel-closed");
|
||
}
|
||
};
|
||
}
|
||
|
||
let wallpaperSwitchBtn = document.getElementById("wallpaper-mode-switch");
|
||
if (wallpaperSwitchBtn) {
|
||
wallpaperSwitchBtn.onclick = function () {
|
||
let wallpaperPanel = document.getElementById("wallpaper-mode-panel");
|
||
if (wallpaperPanel) {
|
||
wallpaperPanel.classList.toggle("float-panel-closed");
|
||
}
|
||
};
|
||
}
|
||
|
||
let themeSwitchBtn = document.getElementById("scheme-switch");
|
||
if (themeSwitchBtn) {
|
||
themeSwitchBtn.onclick = function () {
|
||
let themePanel = document.getElementById("theme-mode-panel");
|
||
if (themePanel) {
|
||
themePanel.classList.toggle("float-panel-closed");
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
loadButtonScript();
|
||
|
||
// 为semifull模式添加滚动检测逻辑
|
||
function initSemifullScrollDetection() {
|
||
const navbar = document.getElementById('navbar');
|
||
if (!navbar) return;
|
||
|
||
const transparentMode = navbar.getAttribute('data-transparent-mode');
|
||
if (transparentMode !== 'semifull') return;
|
||
|
||
const isHomePage = navbar.getAttribute('data-is-home') === 'true';
|
||
|
||
// 如果不是首页,移除滚动事件监听器并设置为半透明状态
|
||
if (!isHomePage) {
|
||
// 移除之前的滚动事件监听器(如果存在)
|
||
if (window.semifullScrollHandler) {
|
||
window.removeEventListener('scroll', window.semifullScrollHandler as () => void);
|
||
window.semifullScrollHandler = undefined;
|
||
}
|
||
// 设置为半透明状态
|
||
navbar.classList.add('scrolled');
|
||
return;
|
||
}
|
||
|
||
// 移除现有的scrolled类,重置状态
|
||
navbar.classList.remove('scrolled');
|
||
|
||
let ticking = false;
|
||
|
||
function updateNavbarState() {
|
||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||
const threshold = 50; // 滚动阈值,可以根据需要调整
|
||
// 使用批量DOM操作优化性能
|
||
if (scrollTop > threshold) {
|
||
navbar!.classList.add('scrolled');
|
||
} else {
|
||
navbar!.classList.remove('scrolled');
|
||
}
|
||
|
||
ticking = false;
|
||
}
|
||
|
||
function requestTick() {
|
||
if (!ticking) {
|
||
requestAnimationFrame(updateNavbarState);
|
||
ticking = true;
|
||
}
|
||
}
|
||
|
||
// 移除之前的滚动事件监听器(如果存在)
|
||
if (window.semifullScrollHandler) {
|
||
window.removeEventListener('scroll', window.semifullScrollHandler as () => void);
|
||
}
|
||
|
||
// 保存新的事件处理器引用
|
||
window.semifullScrollHandler = requestTick as unknown as (() => void);
|
||
|
||
// 监听滚动事件
|
||
window.addEventListener('scroll', requestTick, { passive: true });
|
||
|
||
// 初始化状态
|
||
updateNavbarState();
|
||
}
|
||
|
||
// 将函数暴露到全局对象,供页面切换时调用
|
||
window.initSemifullScrollDetection = initSemifullScrollDetection;
|
||
|
||
// 页面加载完成后初始化滚动检测
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initSemifullScrollDetection);
|
||
} else {
|
||
initSemifullScrollDetection();
|
||
}
|
||
</script>
|
||
|
||
{
|
||
navBarSearchConfig.method === NavBarSearchMethod.PageFind && import.meta.env.PROD && (
|
||
<script is:inline define:vars={{ scriptUrl: url('/pagefind/pagefind.js') }}>
|
||
{/* 你的 loadPagefind 函数的完整内容放在这里 */}
|
||
async function loadPagefind() {
|
||
try {
|
||
const response = await fetch(scriptUrl, { method: 'HEAD' });
|
||
if (!response.ok) {
|
||
throw new Error(`Pagefind script not found: ${response.status}`);
|
||
}
|
||
const pagefind = await import(scriptUrl);
|
||
await pagefind.options({
|
||
excerptLength: 20
|
||
});
|
||
window.pagefind = pagefind;
|
||
document.dispatchEvent(new CustomEvent('pagefindready'));
|
||
console.log('Pagefind loaded and initialized successfully, event dispatched.');
|
||
} catch (error) {
|
||
console.error('Failed to load Pagefind:', error);
|
||
window.pagefind = {
|
||
search: () => Promise.resolve({ results: [] }),
|
||
options: () => Promise.resolve(),
|
||
};
|
||
document.dispatchEvent(new CustomEvent('pagefindloaderror'));
|
||
console.log('Pagefind load error, event dispatched.');
|
||
}
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', loadPagefind);
|
||
} else {
|
||
loadPagefind();
|
||
}
|
||
</script>
|
||
)
|
||
}
|