feat(sidebar): 实现响应式侧边栏状态管理
- 添加媒体查询监听以自动切换侧边栏显示状态 - 引入sidebarStore统一管理侧边栏开闭逻辑 - 移除页面内联侧边栏展开控制逻辑 - 添加Sprite图标组件客户端渲染条件判断 - 更新侧边栏样式类名以支持过渡动画效果 - 移除用户信息展示相关冗余代码块
This commit is contained in:
13
package-lock.json
generated
13
package-lock.json
generated
@@ -1141,7 +1141,6 @@
|
||||
"integrity": "sha512-/rnwfSWS3qwUSzvHynUTORF9xSJi7PCR9yXkxUOnRrNqyKmCmh3FPHH+E9BbgqxXfTevGXBqgnlh9kMb+9T5XA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@sveltejs/acorn-typescript": "^1.0.5",
|
||||
@@ -1181,7 +1180,6 @@
|
||||
"integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
|
||||
"debug": "^4.4.1",
|
||||
@@ -1514,7 +1512,6 @@
|
||||
"integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -1565,7 +1562,6 @@
|
||||
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.47.0",
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
@@ -1784,7 +1780,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -2147,7 +2142,6 @@
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -3253,7 +3247,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -3281,7 +3274,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -3415,7 +3407,6 @@
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -3432,7 +3423,6 @@
|
||||
"integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.0.0",
|
||||
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
|
||||
@@ -3762,7 +3752,6 @@
|
||||
"integrity": "sha512-d1R+3pFa39LXoHCsxHmV//D2pSFZlEMlnxCVQ54TlrQv+4o5pewJO0/Pc5MUp+j71PJrOrPJHTvREZJHn+ymDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
@@ -3930,7 +3919,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -3993,7 +3981,6 @@
|
||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
||||
8
src/lib/assets/sprite.svg
Normal file
8
src/lib/assets/sprite.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg>
|
||||
<symbol id="panel-right-close" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
|
||||
<path d="M15 3.5v17M8 9l3 3l-3 3" />
|
||||
<path d="M3 9.4c0-2.24 0-3.36.436-4.216a4 4 0 0 1 1.748-1.748C6.04 3 7.16 3 9.4 3h5.2c2.24 0 3.36 0 4.216.436a4 4 0 0 1 1.748 1.748C21 6.04 21 7.16 21 9.4v5.2c0 2.24 0 3.36-.436 4.216a4 4 0 0 1-1.748 1.748C17.96 21 16.84 21 14.6 21H9.4c-2.24 0-3.36 0-4.216-.436a4 4 0 0 1-1.748-1.748C3 17.96 3 16.84 3 14.6z" />
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
36
src/lib/components/icon/Icon.svelte
Normal file
36
src/lib/components/icon/Icon.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { IconId } from '$lib/types/icon-ids.ts';
|
||||
|
||||
export let id: IconId;
|
||||
export let size: number | string ;
|
||||
export let className: string = '';
|
||||
$: dimensions = typeof size === 'number' ? `${size}px` : size;
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<svg {...$$restProps}
|
||||
role="img"
|
||||
class={className}
|
||||
aria-hidden="true"
|
||||
width={dimensions?dimensions:24}
|
||||
height={dimensions?dimensions:24}
|
||||
>
|
||||
<use href={`#${id}`} />
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.app-icon {
|
||||
/* 确保图标与文本基线对齐 */
|
||||
vertical-align: middle;
|
||||
/* 防止用户选择图标,提高用户体验 */
|
||||
user-select: none;
|
||||
/* 默认显示为行内块级元素 */
|
||||
display: inline-block;
|
||||
/* 确保它能响应 CSS 动画 */
|
||||
transition: color 0.2s, transform 0.2s;
|
||||
}
|
||||
</style>
|
||||
|
||||
9
src/lib/components/icon/Sprite.svelte
Normal file
9
src/lib/components/icon/Sprite.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import SpriteSvg from '$lib/assets/sprite.svg'
|
||||
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;" >
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html SpriteSvg}
|
||||
</svg>
|
||||
33
src/lib/stores/sidebarStore.ts
Normal file
33
src/lib/stores/sidebarStore.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
|
||||
interface SidebarState {
|
||||
isOpen: boolean;
|
||||
isExpanded: boolean;
|
||||
}
|
||||
|
||||
|
||||
export const sidebarStore = writable<SidebarState>({
|
||||
isOpen: false,
|
||||
isExpanded: false,
|
||||
})
|
||||
|
||||
/**
|
||||
* 切换侧边栏打开、隐藏(偏移隐藏)状态
|
||||
*/
|
||||
export const toggleSidebar = () => {
|
||||
sidebarStore.update(state => ({
|
||||
...state,
|
||||
isOpen: !state.isOpen,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换侧边栏展开状态
|
||||
*/
|
||||
export const toggleSidebarOpen = () => {
|
||||
sidebarStore.update(state => ({
|
||||
...state,
|
||||
isExpanded: !state.isExpanded,
|
||||
}));
|
||||
}
|
||||
3
src/lib/types/icon-ids.ts
Normal file
3
src/lib/types/icon-ids.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type IconId = {
|
||||
[key: string]: string;
|
||||
};
|
||||
@@ -9,7 +9,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="dropdown dropdown-end">
|
||||
<div class="dropdown dropdown-center md:dropdown-end ">
|
||||
<div tabindex="0" role="button" class="rounded hover:bg-base-100 active:bg-base-200 p-2 overflow-hidden flex items-center gap-2">
|
||||
<ThemePreview themeId={$themeStore} />
|
||||
<svg width="12px" height="12px" class="mt-px text-base-content size-2 fill-current opacity-60 sm:inline-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048"><path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"></path></svg>
|
||||
|
||||
@@ -1,12 +1,53 @@
|
||||
<script lang="ts">
|
||||
import './layout.css';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import favicon from '$lib/assets/favicon.svg?url';
|
||||
|
||||
import { themeStore } from '$lib/stores/themeStore.ts';
|
||||
let { children } = $props();
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { sidebarStore } from '$lib/stores/sidebarStore.ts';
|
||||
import Sprite from '$lib/components/icon/Sprite.svelte';
|
||||
|
||||
const MD_BREAKPOINT = '(min-width: 768px)';
|
||||
|
||||
|
||||
|
||||
const handleMediaQueryChange = (event: MediaQueryListEvent) => {
|
||||
const isCurrentlyDesktop = event.matches;
|
||||
console.log(isCurrentlyDesktop);
|
||||
sidebarStore.update((store) => ({
|
||||
...store,
|
||||
isOpen: isCurrentlyDesktop
|
||||
}));
|
||||
}
|
||||
|
||||
let isMounted = $state(false); // 客户端渲染标志
|
||||
onMount(()=>{
|
||||
isMounted = true;
|
||||
const isDesktop = window.matchMedia(MD_BREAKPOINT).matches;
|
||||
console.log(isDesktop);
|
||||
sidebarStore.update((store) => ({
|
||||
...store,
|
||||
isOpen: isDesktop,
|
||||
}));
|
||||
|
||||
|
||||
const mediaQuery = window.matchMedia(MD_BREAKPOINT);
|
||||
|
||||
mediaQuery.addEventListener('change', handleMediaQueryChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handleMediaQueryChange);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
{#if isMounted}
|
||||
<Sprite />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<div data-theme={$themeStore} class="text-base-content">
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
|
||||
redirect(302, `${resolve}/app/dashboard`);
|
||||
</script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,10 +11,6 @@
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -3,5 +3,8 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()]
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
sveltekit(),
|
||||
]
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user