mirror of
https://github.com/sendevia/website.git
synced 2026-03-05 23:32:45 +08:00
feat: refactor screen width handling to use Pinia store
This commit is contained in:
@@ -4,20 +4,19 @@ import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { useGlobalScroll } from "../composables/useGlobalScroll";
|
||||
import { useAllPosts, type Post } from "../composables/useAllPosts";
|
||||
import { useSearchState } from "../composables/useSearchState";
|
||||
import { useScreenWidth } from "../composables/useScreenWidth";
|
||||
import { useScreenWidthStore } from "../stores/screenWidth";
|
||||
import { handleTabNavigation } from "../utils/tabNavigation";
|
||||
|
||||
const { frontmatter } = useGlobalData();
|
||||
const { isScrolled } = useGlobalScroll({ threshold: 100 });
|
||||
const { isSearchActive, isSearchTyping, deactivateSearch, setSearchFocus, setSearchTyping } = useSearchState();
|
||||
const { isAboveBreakpoint } = useScreenWidth(840);
|
||||
|
||||
const screenWidthStore = useScreenWidthStore();
|
||||
const isHome = computed(() => frontmatter.value.home === true);
|
||||
const articlesRef = useAllPosts(true);
|
||||
const query = ref("");
|
||||
const appbar = ref<HTMLElement | null>(null);
|
||||
const searchInput = ref<HTMLInputElement | null>(null);
|
||||
const isTabFocusable = computed(() => !isAboveBreakpoint.value);
|
||||
const isTabFocusable = computed(() => !screenWidthStore.isAboveBreakpoint);
|
||||
|
||||
// 计算过滤后的文章
|
||||
const filteredPosts = computed<Post[]>(() => {
|
||||
@@ -120,20 +119,14 @@ const handleKeydown = (event: KeyboardEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePopState = () => {
|
||||
if (isSearchActive.value) clearSearchState();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleDocumentClick);
|
||||
document.addEventListener("keydown", handleKeydown);
|
||||
window.addEventListener("popstate", handlePopState);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener("click", handleDocumentClick);
|
||||
document.removeEventListener("keydown", handleKeydown);
|
||||
window.removeEventListener("popstate", handlePopState);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { useScreenWidth } from "../composables/useScreenWidth";
|
||||
import { useScreenWidthStore } from "../stores/screenWidth";
|
||||
import { useSearchState } from "../composables/useSearchState";
|
||||
|
||||
const { page, theme } = useGlobalData();
|
||||
const { isAboveBreakpoint } = useScreenWidth(840);
|
||||
const screenWidthStore = useScreenWidthStore();
|
||||
const { isSearchActive, activateSearch, deactivateSearch } = useSearchState();
|
||||
|
||||
// 计算导航段落
|
||||
@@ -40,7 +40,7 @@ function toggleSearch(event: MouseEvent) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav :class="isAboveBreakpoint ? 'rail' : 'bar'">
|
||||
<nav :class="screenWidthStore.isAboveBreakpoint ? 'rail' : 'bar'">
|
||||
<button class="fab" @mousedown.prevent @click.stop="toggleSearch">
|
||||
<span>{{ isSearchActive ? "close" : "search" }}</span>
|
||||
</button>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from "vue";
|
||||
import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { useScreenWidth } from "../composables/useScreenWidth";
|
||||
import { useScreenWidthStore } from "../stores/screenWidth";
|
||||
|
||||
const { page, frontmatter } = useGlobalData();
|
||||
const { isAboveBreakpoint: isMonitoring } = useScreenWidth(840);
|
||||
const screenWidthStore = useScreenWidthStore();
|
||||
const navRef = ref<HTMLElement | null>(null);
|
||||
const indicator = ref({ top: "0px", left: "0px", width: "100%", height: "0px", opacity: 0 });
|
||||
const headings = ref<Array<{ id: string; text: string; level: number }>>([]);
|
||||
@@ -165,7 +165,7 @@ function toggleMonitoring(shouldMonitor: boolean) {
|
||||
}
|
||||
|
||||
const resizeHandler = () => {
|
||||
if (isMonitoring.value) {
|
||||
if (screenWidthStore.isAboveBreakpoint) {
|
||||
collectHeadings();
|
||||
createObserver();
|
||||
}
|
||||
@@ -173,18 +173,18 @@ const resizeHandler = () => {
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
onMounted(() => {
|
||||
toggleMonitoring(isMonitoring.value);
|
||||
toggleMonitoring(screenWidthStore.isAboveBreakpoint);
|
||||
|
||||
window.addEventListener("resize", resizeHandler);
|
||||
window.addEventListener("resize", updateIndicator, { passive: true });
|
||||
window.addEventListener("hashchange", () => {
|
||||
if (isMonitoring.value) {
|
||||
if (screenWidthStore.isAboveBreakpoint) {
|
||||
collectHeadings();
|
||||
createObserver();
|
||||
}
|
||||
});
|
||||
window.addEventListener("popstate", () => {
|
||||
if (isMonitoring.value) {
|
||||
if (screenWidthStore.isAboveBreakpoint) {
|
||||
collectHeadings();
|
||||
createObserver();
|
||||
}
|
||||
@@ -224,14 +224,17 @@ if (typeof window !== "undefined") {
|
||||
}
|
||||
});
|
||||
|
||||
watch(isMonitoring, (newValue) => {
|
||||
toggleMonitoring(newValue);
|
||||
});
|
||||
watch(
|
||||
() => screenWidthStore.isAboveBreakpoint,
|
||||
(newValue) => {
|
||||
toggleMonitoring(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => headingsActiveId.value,
|
||||
() => {
|
||||
if (isMonitoring.value) {
|
||||
if (screenWidthStore.isAboveBreakpoint) {
|
||||
nextTick(() => updateIndicator());
|
||||
}
|
||||
}
|
||||
@@ -240,7 +243,7 @@ if (typeof window !== "undefined") {
|
||||
watch(
|
||||
() => grouped.value,
|
||||
() => {
|
||||
if (isMonitoring.value) {
|
||||
if (screenWidthStore.isAboveBreakpoint) {
|
||||
nextTick(() => updateIndicator());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
/**
|
||||
* 响应式屏幕宽度检测组合式函数
|
||||
* @param breakpoint 断点值,默认为840px
|
||||
* @returns 包含屏幕宽度和是否超过断点的响应式对象
|
||||
*/
|
||||
export function useScreenWidth(breakpoint = 840) {
|
||||
const screenWidth = ref<number>(0);
|
||||
const isAboveBreakpoint = ref<boolean>(true);
|
||||
|
||||
function updateScreenWidth() {
|
||||
if (typeof window !== "undefined") {
|
||||
screenWidth.value = window.innerWidth;
|
||||
isAboveBreakpoint.value = screenWidth.value > breakpoint;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
onMounted(() => {
|
||||
updateScreenWidth();
|
||||
window.addEventListener("resize", updateScreenWidth);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("resize", updateScreenWidth);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
screenWidth,
|
||||
isAboveBreakpoint,
|
||||
updateScreenWidth,
|
||||
};
|
||||
}
|
||||
81
.vitepress/theme/stores/screenWidth.ts
Normal file
81
.vitepress/theme/stores/screenWidth.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
/**
|
||||
* 屏幕宽度响应式状态管理
|
||||
*/
|
||||
export const useScreenWidthStore = defineStore("screenWidth", () => {
|
||||
// 响应式状态
|
||||
const screenWidth = ref<number>(0);
|
||||
const isAboveBreakpoint = ref<boolean>(true);
|
||||
const breakpoint = ref<number>(840);
|
||||
|
||||
// 内部状态
|
||||
let resizeHandler: (() => void) | null = null;
|
||||
let isInitialized = false;
|
||||
|
||||
/**
|
||||
* 更新屏幕宽度状态
|
||||
*/
|
||||
function update() {
|
||||
if (typeof window !== "undefined") {
|
||||
screenWidth.value = window.innerWidth;
|
||||
isAboveBreakpoint.value = screenWidth.value > breakpoint.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化监听器
|
||||
*/
|
||||
function init() {
|
||||
if (typeof window !== "undefined" && !isInitialized) {
|
||||
update();
|
||||
resizeHandler = () => update();
|
||||
window.addEventListener("resize", resizeHandler);
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理监听器
|
||||
*/
|
||||
function cleanup() {
|
||||
if (typeof window !== "undefined" && resizeHandler) {
|
||||
window.removeEventListener("resize", resizeHandler);
|
||||
resizeHandler = null;
|
||||
isInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置断点阈值
|
||||
* @param value - 新的断点阈值
|
||||
*/
|
||||
function setBreakpoint(value: number) {
|
||||
breakpoint.value = value;
|
||||
update();
|
||||
}
|
||||
|
||||
// 监听断点变化,自动更新状态
|
||||
watch(breakpoint, () => {
|
||||
update();
|
||||
});
|
||||
|
||||
// 自动初始化
|
||||
if (typeof window !== "undefined") {
|
||||
init();
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
screenWidth,
|
||||
isAboveBreakpoint,
|
||||
breakpoint,
|
||||
|
||||
// 方法
|
||||
update,
|
||||
init,
|
||||
cleanup,
|
||||
setBreakpoint,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user