@coffic/cosy-ui 0.9.18 → 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/app.css +1 -1
  2. package/dist/src-astro/code-container/ButtonCodeToggle.astro +0 -1
  3. package/dist/src-astro/code-container/ButtonCopyCode.astro +0 -2
  4. package/dist/src-astro/code-container/CodeContainer.astro +0 -3
  5. package/dist/src-astro/code-container/CodeToolbar.astro +0 -2
  6. package/dist/src-astro/footer/Footer.astro +1 -0
  7. package/dist/src-astro/header/Header.astro +3 -44
  8. package/dist/src-astro/header/LogoLink.astro +87 -0
  9. package/dist/src-astro/language-switcher/LanguageSwitcher.astro +3 -1
  10. package/dist/src-astro/layout-app/AppLayout.astro +12 -12
  11. package/dist/src-astro/layout-app/SkeletonLoader.astro +191 -0
  12. package/dist/src-astro/layout-app/loading-classes.ts +55 -0
  13. package/dist/src-astro/layout-basic/BaseLayout.astro +6 -6
  14. package/dist/src-astro/layout-basic/index.ts +1 -9
  15. package/dist/src-astro/loading-overlay/LoadingOverlay.astro +147 -75
  16. package/dist/src-astro/loading-overlay/index.ts +1 -1
  17. package/dist/src-astro/sidebar/MobileNav.astro +55 -0
  18. package/dist/src-astro/sidebar/NavItem.astro +100 -0
  19. package/dist/src-astro/sidebar/Sidebar.astro +37 -98
  20. package/dist/src-astro/sidebar/SidebarNav.astro +10 -87
  21. package/dist/src-astro/sidebar/index.ts +3 -1
  22. package/dist/src-astro/sidebar/utils.ts +64 -0
  23. package/dist/src-astro/theme-switcher/ThemeSwitcher.astro +3 -1
  24. package/dist/src-astro/types/layout.ts +5 -0
  25. package/dist/src-astro/types/meta.ts +0 -6
  26. package/package.json +1 -1
  27. package/dist/src-astro/layout-basic/BaseLayoutBasic.astro +0 -17
  28. package/dist/src-astro/loading-overlay/types.ts +0 -12
@@ -3,17 +3,15 @@
3
3
  * @component LoadingOverlay
4
4
  *
5
5
  * @description
6
- * LoadingOverlay 组件是一个全屏加载弹出层,用于在页面跳转时显示加载状态。
7
- * 它监听 Astro 的页面过渡事件,在跳转开始时延迟显示,跳转完成后隐藏。
8
- * 为了避免频繁的视觉干扰,只有在加载时间超过指定延迟时间(默认1秒)时才显示。
6
+ * LoadingOverlay 组件是一个全屏加载遮罩层,用于在页面加载或路由切换时显示加载状态。
7
+ * 它提供了多种加载动画类型,支持延迟显示和背景模糊效果,确保良好的用户体验。
9
8
  *
10
9
  * @design
11
10
  * 设计理念:
12
- * 1. 全屏覆盖 - 使用固定定位覆盖整个视口
13
- * 2. 居中显示 - 加载指示器在屏幕中央显示
14
- * 3. 平滑动画 - 使用淡入淡出动画效果
15
- * 4. 延迟显示 - 避免短时间加载的视觉干扰
16
- * 5. 可定制性 - 支持自定义加载文本、样式和延迟时间
11
+ * 1. 用户体验优先 - 通过延迟显示避免闪烁,确保最小显示时间
12
+ * 2. 视觉反馈 - 提供多种动画类型,满足不同场景需求
13
+ * 3. 无障碍设计 - 支持键盘导航和屏幕阅读器
14
+ * 4. 性能优化 - 使用 CSS 动画和事件监听,避免阻塞主线程
17
15
  *
18
16
  * @usage
19
17
  * 基本用法:
@@ -22,22 +20,53 @@
22
20
  * import { LoadingOverlay } from '@coffic/cosy-ui';
23
21
  * ---
24
22
  *
25
- * <LoadingOverlay />
23
+ * <LoadingOverlay text="正在加载..." />
26
24
  * ```
27
25
  *
28
- * 自定义延迟时间:
26
+ * 自定义动画类型:
29
27
  * ```astro
30
- * <LoadingOverlay delay={2000} text="页面加载中..." />
28
+ * <LoadingOverlay
29
+ * text="处理中..."
30
+ * spinnerType="spinner"
31
+ * loadingDelay={500}
32
+ * />
33
+ * ```
34
+ *
35
+ * 无背景遮罩:
36
+ * ```astro
37
+ * <LoadingOverlay
38
+ * text="加载中..."
39
+ * showBackdrop={false}
40
+ * class="cosy:bg-transparent"
41
+ * />
31
42
  * ```
32
43
  *
33
44
  * 自定义样式:
34
45
  * ```astro
35
46
  * <LoadingOverlay
36
- * text="正在跳转..."
37
- * class="custom-loading-overlay"
38
- * delay={1500}
47
+ * text="请稍候..."
48
+ * spinnerType="pulse"
49
+ * class="cosy:bg-blue-500/20"
39
50
  * />
40
51
  * ```
52
+ *
53
+ * @props
54
+ * - text?: string - 加载文本,默认为 "Loading..."
55
+ * - class?: string - 自定义 CSS 类名
56
+ * - showSpinner?: boolean - 是否显示加载动画,默认为 true
57
+ * - spinnerType?: 'dots' | 'spinner' | 'pulse' - 加载动画类型,默认为 'dots'
58
+ * - loadingDelay?: number - 延迟显示时间(毫秒),默认为 1000ms
59
+ * - showBackdrop?: boolean - 是否显示背景模糊遮罩,默认为 true
60
+ *
61
+ * @slots
62
+ * 此组件没有插槽,所有内容通过 props 配置
63
+ *
64
+ * @events
65
+ * 组件会自动监听以下 Astro 事件:
66
+ * - astro:page-load - 页面加载完成时隐藏
67
+ * - astro:before-preparation - 页面准备前显示
68
+ * - astro:before-swap - 路由切换前显示
69
+ * - astro:after-swap - 路由切换后隐藏
41
70
  */
42
71
 
43
72
  import '../../style.ts';
@@ -53,6 +82,8 @@ export interface Props {
53
82
  spinnerType?: 'dots' | 'spinner' | 'pulse';
54
83
  /** 延迟显示时间(毫秒),默认1000ms */
55
84
  loadingDelay?: number;
85
+ /** 是否显示背景模糊遮罩,默认true */
86
+ showBackdrop?: boolean;
56
87
  }
57
88
 
58
89
  const {
@@ -61,23 +92,26 @@ const {
61
92
  showSpinner = true,
62
93
  spinnerType = 'dots',
63
94
  loadingDelay = 1000,
95
+ showBackdrop = true,
64
96
  } = Astro.props;
65
97
 
66
98
  // 生成唯一的 ID
67
- const overlayId = `loading-overlay-${Math.random().toString(36).substr(2, 9)}`;
99
+ const overlayId = `loading-overlay`;
68
100
  ---
69
101
 
70
102
  <div
71
103
  id={overlayId}
104
+ transition:persist
72
105
  class:list={[
73
- 'cosy:fixed cosy:inset-0 cosy:z-[9999] cosy:bg-black/50 cosy:backdrop-blur-sm',
106
+ 'cosy:fixed cosy:inset-0 cosy:z-[9999]',
107
+ showBackdrop ? 'cosy:bg-black/50 cosy:backdrop-blur-sm' : '',
74
108
  'cosy:flex cosy:items-center cosy:justify-center',
75
109
  'cosy:opacity-0 cosy:pointer-events-none cosy:transition-opacity cosy:duration-300',
76
110
  className,
77
111
  ]}
78
112
  data-loading-overlay>
79
113
  <div
80
- class="cosy:bg-white cosy:dark:bg-gray-800 cosy:rounded-lg cosy:shadow-xl cosy:p-6 cosy:max-w-sm cosy:w-full cosy:mx-4">
114
+ class="cosy:bg-accent cosy:rounded-lg cosy:shadow-xl cosy:p-6 cosy:max-w-sm cosy:w-full cosy:mx-4">
81
115
  <div class="cosy:flex cosy:flex-col cosy:items-center cosy:space-y-4">
82
116
  {
83
117
  showSpinner && (
@@ -112,67 +146,105 @@ const overlayId = `loading-overlay-${Math.random().toString(36).substr(2, 9)}`;
112
146
  </div>
113
147
  </div>
114
148
 
115
- <script define:vars={{ overlayId, loadingDelay }}>
116
- // 获取加载弹出层元素
117
- const overlay = document.getElementById(overlayId);
118
-
119
- if (overlay) {
120
- let loadingStartTime = null;
121
- let showTimeout = null;
122
- let isLoading = false;
123
-
124
- // 显示加载弹出层的函数
125
- const showOverlay = () => {
126
- if (isLoading) return;
127
-
128
- isLoading = true;
129
- loadingStartTime = Date.now();
130
-
131
- // 延迟显示,避免短时间加载的视觉干扰
132
- showTimeout = window.setTimeout(() => {
133
- overlay.style.opacity = '1';
134
- overlay.style.pointerEvents = 'auto';
135
- }, loadingDelay);
136
- };
137
-
138
- // 隐藏加载弹出层的函数
139
- const hideOverlay = () => {
140
- if (showTimeout) {
141
- clearTimeout(showTimeout);
142
- showTimeout = null;
149
+ <script is:inline define:vars={{ overlayId, loadingDelay }}>
150
+ let showTimeout = null;
151
+ let hideTimeout = null;
152
+ let displayStartTime = null;
153
+ let loaded = false;
154
+ const minDisplayTime = 1000;
155
+
156
+ const reset = () => {
157
+ displayStartTime = null;
158
+ };
159
+
160
+ const isDomDisplayed = () => {
161
+ const overlay = document.getElementById(overlayId);
162
+ if (!overlay) {
163
+ return false;
164
+ }
165
+ return (
166
+ overlay.style.opacity === '1' && overlay.style.pointerEvents === 'auto'
167
+ );
168
+ };
169
+
170
+ // 显示加载弹出层的函数
171
+ const showOverlay = () => {
172
+ if (isDomDisplayed() || loaded) return;
173
+
174
+ // 设置延迟显示
175
+ showTimeout = setTimeout(() => {
176
+ const overlay = document.getElementById(overlayId);
177
+ if (!overlay) {
178
+ return;
143
179
  }
144
180
 
145
- isLoading = false;
146
- loadingStartTime = null;
181
+ if (loaded) {
182
+ return;
183
+ }
184
+
185
+ overlay.style.opacity = '1';
186
+ overlay.style.pointerEvents = 'auto';
187
+ displayStartTime = Date.now();
188
+ }, loadingDelay);
189
+ };
190
+
191
+ // 隐藏加载弹出层的函数
192
+ const hideOverlay = (reason) => {
193
+ console.log('LoadingOverlay: hideOverlay', reason);
194
+
195
+ // 清除显示延迟
196
+ if (showTimeout) {
197
+ clearTimeout(showTimeout);
198
+ showTimeout = null;
199
+ }
200
+
201
+ // 如果没有显示
202
+ if (!isDomDisplayed()) {
203
+ reset();
204
+ return;
205
+ }
206
+
207
+ hideTimeout = setTimeout(() => {
208
+ const overlay = document.getElementById(overlayId);
209
+ if (!overlay) {
210
+ return;
211
+ }
147
212
 
148
213
  overlay.style.opacity = '0';
149
214
  overlay.style.pointerEvents = 'none';
150
- };
151
-
152
- // 监听 Astro 页面过渡事件
153
- document.addEventListener('astro:page-load', () => {
154
- hideOverlay();
155
- });
156
-
157
- document.addEventListener('astro:before-preparation', () => {
158
- showOverlay();
159
- });
160
-
161
- // 监听路由变化
162
- document.addEventListener('astro:before-swap', () => {
163
- showOverlay();
164
- });
165
-
166
- document.addEventListener('astro:after-swap', () => {
167
- // 如果加载时间很短,立即隐藏
168
- if (loadingStartTime && Date.now() - loadingStartTime < loadingDelay) {
169
- hideOverlay();
170
- } else {
171
- // 否则延迟隐藏,给用户一个视觉反馈
172
- setTimeout(() => {
173
- hideOverlay();
174
- }, 100);
175
- }
176
- });
177
- }
215
+ reset();
216
+ }, minDisplayTime);
217
+ };
218
+
219
+ // 监听 Astro 页面过渡事件
220
+ document.addEventListener('astro:page-load', () => {
221
+ loaded = true;
222
+ hideOverlay('astro:page-load');
223
+ });
224
+
225
+ document.addEventListener('astro:before-preparation', () => {
226
+ loaded = false;
227
+ showOverlay('astro:before-preparation');
228
+ });
229
+
230
+ // 监听路由变化
231
+ document.addEventListener('astro:before-swap', () => {
232
+ loaded = false;
233
+ showOverlay('astro:before-swap');
234
+ });
235
+
236
+ document.addEventListener('astro:after-swap', () => {
237
+ loaded = true;
238
+ hideOverlay('astro:after-swap');
239
+ });
240
+
241
+ // 页面卸载时清理定时器
242
+ window.addEventListener('beforeunload', () => {
243
+ if (showTimeout) {
244
+ clearTimeout(showTimeout);
245
+ }
246
+ if (hideTimeout) {
247
+ clearTimeout(hideTimeout);
248
+ }
249
+ });
178
250
  </script>
@@ -1,2 +1,2 @@
1
1
  export { default as LoadingOverlay } from './LoadingOverlay.astro';
2
- export type { LoadingOverlayProps } from './types';
2
+ export type { Props as LoadingOverlayProps } from './LoadingOverlay.astro';
@@ -0,0 +1,55 @@
1
+ ---
2
+ /**
3
+ * MobileNav组件
4
+ *
5
+ * 移动端导航栏组件
6
+ */
7
+
8
+ import { MenuIcon } from '../../index-astro';
9
+ import { isPathMatch } from '../../src/utils/path.ts';
10
+ import type { ISidebarItem } from '../types/sidebar.ts';
11
+
12
+ interface Props {
13
+ /**
14
+ * 侧边栏项目
15
+ */
16
+ sidebarItems: ISidebarItem[];
17
+
18
+ /**
19
+ * 当前路径
20
+ */
21
+ currentPath: string;
22
+
23
+ /**
24
+ * 是否开启调试模式
25
+ */
26
+ debug?: boolean;
27
+ }
28
+
29
+ const { sidebarItems, currentPath, debug = false } = Astro.props;
30
+
31
+ const debugClass = debug ? 'cosy:border cosy:border-red-500' : '';
32
+
33
+ // 获取当前活动的一级导航项
34
+ const currentSection = sidebarItems.find((section) =>
35
+ section.items?.some((item) => isPathMatch(currentPath, item.href))
36
+ );
37
+ ---
38
+
39
+ <div
40
+ class:list={[
41
+ 'cosy:flex cosy:lg:hidden cosy:items-center cosy:justify-between cosy:px-4 cosy:py-2 cosy:border-b cosy:border-base-300 cosy:bg-base-100 cosy:relative cosy:z-10',
42
+ debugClass,
43
+ ]}>
44
+ <div class="cosy:flex cosy:items-center cosy:gap-2">
45
+ <button
46
+ type="button"
47
+ class="cosy:p-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
48
+ data-modal-target="mobile-sidebar">
49
+ <MenuIcon class="cosy:w-5 cosy:h-5" />
50
+ </button>
51
+ <span class="cosy:font-medium cosy:text-sm">
52
+ {currentSection?.text || '导航'}
53
+ </span>
54
+ </div>
55
+ </div>
@@ -0,0 +1,100 @@
1
+ ---
2
+ /**
3
+ * NavItem组件
4
+ *
5
+ * 递归渲染导航项,支持无限层级
6
+ */
7
+
8
+ import { isPathMatch } from '../../src/utils/path.ts';
9
+ import '../../style.ts';
10
+ import type { ISidebarItem } from '../types/sidebar.ts';
11
+
12
+ interface Props {
13
+ /**
14
+ * 导航项
15
+ */
16
+ item: ISidebarItem;
17
+
18
+ /**
19
+ * 当前路径
20
+ */
21
+ currentPath: string;
22
+
23
+ /**
24
+ * 嵌套层级
25
+ * @default 0
26
+ */
27
+ level?: number;
28
+
29
+ /**
30
+ * 是否开启调试模式
31
+ */
32
+ debug?: boolean;
33
+ }
34
+
35
+ const { item, currentPath, level = 0, debug = false } = Astro.props;
36
+
37
+ const debugClass = debug ? 'cosy:border cosy:border-red-500' : '';
38
+ const isActive = isPathMatch(currentPath, item.href);
39
+ const badgeSize = level === 0 ? 'cosy:badge-sm' : 'cosy:badge-xs';
40
+ ---
41
+
42
+ <li class:list={[debugClass, level === 0 ? 'cosy:no-underline' : '']}>
43
+ <a
44
+ data-sidebar-item
45
+ data-current-path={currentPath}
46
+ data-href={item.href}
47
+ href={item.href}
48
+ class:list={[
49
+ 'cosy:hover:bg-base-300',
50
+ level === 0 ? 'cosy:no-underline' : '',
51
+ { 'cosy:menu-active': isActive },
52
+ debugClass,
53
+ ]}>
54
+ {item.text}
55
+ {
56
+ item.badge !== undefined && item.badge !== null && (
57
+ <span class={`cosy:badge ${badgeSize} cosy:ml-2`}>{item.badge}</span>
58
+ )
59
+ }
60
+ </a>
61
+ {
62
+ item.items && item.items.length > 0 && (
63
+ <ul class:list={[debugClass]}>
64
+ {item.items.map((subitem) => (
65
+ <Astro.self
66
+ item={subitem}
67
+ currentPath={currentPath}
68
+ level={level + 1}
69
+ debug={debug}
70
+ />
71
+ ))}
72
+ </ul>
73
+ )
74
+ }
75
+ </li>
76
+
77
+ <script>
78
+ // 立即处理导航项点击,更新高亮状态
79
+ document.addEventListener('click', (event) => {
80
+ const target = event.target as HTMLElement;
81
+ const navItem = target.closest('[data-sidebar-item]') as HTMLAnchorElement;
82
+
83
+ if (navItem) {
84
+ // 立即移除所有导航项的高亮状态
85
+ const allNavItems = document.querySelectorAll('[data-sidebar-item]');
86
+ allNavItems.forEach((item) => {
87
+ item.classList.remove('cosy:menu-active');
88
+ });
89
+
90
+ // 立即为当前点击的导航项添加高亮状态
91
+ navItem.classList.add('cosy:menu-active');
92
+
93
+ // 可选:添加一个短暂的视觉反馈
94
+ navItem.style.transition = 'background-color 0.2s ease';
95
+ setTimeout(() => {
96
+ navItem.style.transition = '';
97
+ }, 200);
98
+ }
99
+ });
100
+ </script>
@@ -22,8 +22,9 @@
22
22
  */
23
23
 
24
24
  import '../../style.ts';
25
- import { isPathMatch } from '../../src/utils/path.ts';
26
- import { MenuIcon, SidebarNav, Modal } from '../../index-astro';
25
+ import { SidebarNav, Modal } from '../../index-astro';
26
+ import { getMarginTopClass, getMarginBottomClass } from './utils.ts';
27
+ import MobileNav from './MobileNav.astro';
27
28
  import type { ISidebarProps } from '../../index-astro';
28
29
 
29
30
  export interface Props extends ISidebarProps {}
@@ -40,65 +41,22 @@ const {
40
41
  const currentPath = Astro.url.pathname;
41
42
 
42
43
  const debugClass = debug ? 'cosy:border cosy:border-red-500' : '';
43
-
44
- function getVerticalMarginTopClasses(marginTop: string) {
45
- if (marginTop === 'none') return 'cosy:mt-0';
46
- if (marginTop === 'xs') return 'cosy:mt-1';
47
- if (marginTop === 'sm') return 'cosy:mt-2';
48
- if (marginTop === 'md') return 'cosy:mt-4';
49
- if (marginTop === 'lg') return 'cosy:mt-6';
50
- if (marginTop === 'xl') return 'cosy:mt-8';
51
- if (marginTop === '2xl') return 'cosy:mt-10';
52
- if (marginTop === '3xl') return 'cosy:mt-12';
53
- if (marginTop === '4xl') return 'cosy:mt-16';
54
- if (marginTop === '5xl') return 'cosy:mt-20';
55
- return '';
56
- }
57
-
58
- function getVerticalMarginBottomClasses(marginBottom: string) {
59
- if (marginBottom === 'none') return 'cosy:mb-0';
60
- if (marginBottom === 'xs') return 'cosy:mb-1';
61
- if (marginBottom === 'sm') return 'cosy:mb-2';
62
- if (marginBottom === 'md') return 'cosy:mb-4';
63
- if (marginBottom === 'lg') return 'cosy:mb-6';
64
- if (marginBottom === 'xl') return 'cosy:mb-8';
65
- if (marginBottom === '2xl') return 'cosy:mb-10';
66
- if (marginBottom === '3xl') return 'cosy:mb-12';
67
- if (marginBottom === '4xl') return 'cosy:mb-16';
68
- if (marginBottom === '5xl') return 'cosy:mb-20';
69
- return '';
70
- }
71
-
72
- // 获取当前活动的一级导航项
73
- const currentSection = sidebarItems.find((section) =>
74
- section.items?.some((item) => isPathMatch(currentPath, item.href))
75
- );
76
44
  ---
77
45
 
78
46
  {/* 移动端导航栏 */}
79
- <div
80
- class:list={[
81
- 'cosy:flex cosy:lg:hidden cosy:items-center cosy:justify-between cosy:px-4 cosy:py-2 cosy:border-b cosy:border-base-300 cosy:bg-base-100 cosy:relative cosy:z-10',
82
- debugClass,
83
- ]}>
84
- <div class="cosy:flex cosy:items-center cosy:gap-2">
85
- <button
86
- type="button"
87
- class="cosy:p-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
88
- data-modal-target="mobile-sidebar">
89
- <MenuIcon class="cosy:w-5 cosy:h-5" />
90
- </button>
91
- <span class="cosy:font-medium cosy:text-sm"
92
- >{currentSection?.text || '导航'}</span
93
- >
94
- </div>
95
- </div>
47
+ <MobileNav
48
+ sidebarItems={sidebarItems}
49
+ currentPath={currentPath}
50
+ debug={debug}
51
+ />
96
52
 
97
53
  {/* 移动端侧边栏弹出层 */}
98
54
  <Modal
99
55
  id="mobile-sidebar"
100
56
  class="cosy:mx-4 cosy:lg:w-80 cosy:w-[calc(100vw-2rem)] cosy:max-w-full">
101
- <div class="cosy:h-[calc(100vh-8rem)] cosy:overflow-y-auto">
57
+ <div
58
+ class="cosy:h-[calc(100vh-8rem)] cosy:overflow-y-auto"
59
+ data-mobile-sidebar-content>
102
60
  <SidebarNav
103
61
  sidebarItems={sidebarItems}
104
62
  currentPath={currentPath}
@@ -117,11 +75,12 @@ const currentSection = sidebarItems.find((section) =>
117
75
  className,
118
76
  debugClass,
119
77
  'cosy:hidden cosy:lg:block',
120
- getVerticalMarginTopClasses(marginTop),
121
- getVerticalMarginBottomClasses(marginBottom),
78
+ getMarginTopClass(marginTop),
79
+ getMarginBottomClass(marginBottom),
122
80
  ]}>
123
81
  <div
124
- class="cosy:top-16 cosy:sticky cosy:pb-48 cosy:h-[calc(100vh-0rem)] cosy:overflow-y-auto">
82
+ class="cosy:top-16 cosy:sticky cosy:pb-48 cosy:h-[calc(100vh-0rem)] cosy:overflow-y-auto"
83
+ data-desktop-sidebar-content>
125
84
  <SidebarNav
126
85
  sidebarItems={sidebarItems}
127
86
  currentPath={currentPath}
@@ -131,62 +90,42 @@ const currentSection = sidebarItems.find((section) =>
131
90
  </aside>
132
91
 
133
92
  <script>
93
+ import { saveScrollPosition, restoreScrollPosition } from './utils.ts';
94
+
134
95
  // 处理侧边栏滚动位置保存和恢复
135
96
  document.addEventListener('astro:before-preparation', () => {
136
- // 获取桌面侧边栏滚动容器
137
- const desktopSidebarContent = document.querySelector(
138
- 'aside[data-sidebar] .cosy\\:overflow-y-auto'
97
+ // 保存桌面端滚动位置
98
+ const desktopContent = document.querySelector(
99
+ '[data-desktop-sidebar-content]'
139
100
  );
140
-
141
- // 保存滚动位置到localStorage
142
- if (desktopSidebarContent) {
143
- localStorage.setItem(
144
- 'sidebarScrollPosition',
145
- desktopSidebarContent.scrollTop.toString()
146
- );
101
+ if (desktopContent) {
102
+ saveScrollPosition(desktopContent, 'sidebarScrollPosition');
147
103
  }
148
104
 
149
- // 获取移动端侧边栏滚动容器
150
- const mobileSidebarContent = document.querySelector(
151
- '.cosy\\:h-\\[calc\\(100vh-8rem\\)\\].cosy\\:overflow-y-auto'
152
- );
153
-
154
105
  // 保存移动端滚动位置
155
- if (mobileSidebarContent) {
156
- localStorage.setItem(
157
- 'mobileSidebarScrollPosition',
158
- mobileSidebarContent.scrollTop.toString()
159
- );
106
+ const mobileContent = document.querySelector(
107
+ '[data-mobile-sidebar-content]'
108
+ );
109
+ if (mobileContent) {
110
+ saveScrollPosition(mobileContent, 'mobileSidebarScrollPosition');
160
111
  }
161
112
  });
162
113
 
163
114
  document.addEventListener('astro:page-load', () => {
164
- // 获取桌面侧边栏滚动容器
165
- const desktopSidebarContent = document.querySelector(
166
- 'aside[data-sidebar] .cosy\\:overflow-y-auto'
115
+ // 恢复桌面端滚动位置
116
+ const desktopContent = document.querySelector(
117
+ '[data-desktop-sidebar-content]'
167
118
  );
168
-
169
- // 恢复滚动位置
170
- if (desktopSidebarContent) {
171
- const savedPosition = localStorage.getItem('sidebarScrollPosition');
172
- if (savedPosition) {
173
- desktopSidebarContent.scrollTop = parseInt(savedPosition, 10);
174
- }
119
+ if (desktopContent) {
120
+ restoreScrollPosition(desktopContent, 'sidebarScrollPosition');
175
121
  }
176
122
 
177
- // 获取移动端侧边栏滚动容器
178
- const mobileSidebarContent = document.querySelector(
179
- '.cosy\\:h-\\[calc\\(100vh-8rem\\)\\].cosy\\:overflow-y-auto'
180
- );
181
-
182
123
  // 恢复移动端滚动位置
183
- if (mobileSidebarContent) {
184
- const savedMobilePosition = localStorage.getItem(
185
- 'mobileSidebarScrollPosition'
186
- );
187
- if (savedMobilePosition) {
188
- mobileSidebarContent.scrollTop = parseInt(savedMobilePosition, 10);
189
- }
124
+ const mobileContent = document.querySelector(
125
+ '[data-mobile-sidebar-content]'
126
+ );
127
+ if (mobileContent) {
128
+ restoreScrollPosition(mobileContent, 'mobileSidebarScrollPosition');
190
129
  }
191
130
  });
192
131
  </script>