@coffic/cosy-ui 0.3.39 → 0.3.45

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.
@@ -1,4 +1,59 @@
1
1
  ---
2
+ /**
3
+ * @component Header
4
+ *
5
+ * @description
6
+ * Header 组件是一个用于网站顶部导航的布局组件,提供了logo、导航菜单、语言切换等功能。
7
+ * 组件支持响应式设计,在不同屏幕尺寸下有良好的显示效果,并可选择固定在页面顶部。
8
+ *
9
+ * @design
10
+ * 设计理念:
11
+ * 1. 简洁实用 - 提供清晰的导航和品牌展示,不过度占用视觉空间
12
+ * 2. 响应式适配 - 在移动端和桌面端都有合适的展现形式
13
+ * 3. 可定制性 - 支持多种配置选项,适应不同网站的风格和需求
14
+ * 4. 多语言支持 - 内置语言切换功能,便于构建国际化网站
15
+ *
16
+ * @usage
17
+ * 基本用法:
18
+ * ```astro
19
+ * <Header
20
+ * logo={import("../assets/logo.png")}
21
+ * logoHref="/"
22
+ * navItems={[
23
+ * { href: "/docs", label: "文档", match: (path) => path.startsWith("/docs") },
24
+ * { href: "/components", label: "组件", match: (path) => path.startsWith("/components") }
25
+ * ]}
26
+ * />
27
+ * ```
28
+ *
29
+ * 自定义语言选项:
30
+ * ```astro
31
+ * <Header
32
+ * logo={import("../assets/logo.png")}
33
+ * languages={[
34
+ * { code: "zh-cn", name: "简体中文" },
35
+ * { code: "en", name: "English" },
36
+ * { code: "ja", name: "日本語" }
37
+ * ]}
38
+ * currentLocale="zh-cn"
39
+ * />
40
+ * ```
41
+ *
42
+ * 带有基础路径:
43
+ * ```astro
44
+ * <Header
45
+ * logo={import("../assets/logo.png")}
46
+ * basePath="/my-site"
47
+ * />
48
+ * ```
49
+ *
50
+ * 自定义高度:
51
+ * ```astro
52
+ * <Header
53
+ * logo={import("../assets/logo.png")}
54
+ * height="lg"
55
+ * />
56
+ */
2
57
  import Link from '../base/Link.astro';
3
58
  import Image from '../base/Image.astro';
4
59
  import '../../app.css';
@@ -50,19 +105,31 @@ interface Props {
50
105
  * @default false
51
106
  */
52
107
  defaultSidebarOpen?: boolean;
108
+ /**
109
+ * 导航栏高度
110
+ * @default "md"
111
+ */
112
+ height?: '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
113
+ /**
114
+ * 是否显示高度设置菜单
115
+ * @default true
116
+ */
117
+ showHeightSetting?: boolean;
53
118
  }
54
119
 
55
120
  const {
56
121
  logo,
57
- logoHref = "/",
122
+ logoHref = '/',
58
123
  navItems = [],
59
124
  sticky = true,
60
125
  languages = [
61
126
  { code: 'zh-cn', name: '中文' },
62
- { code: 'en', name: 'English' }
127
+ { code: 'en', name: 'English' },
63
128
  ],
64
129
  currentLocale = 'zh-cn',
65
130
  basePath = '',
131
+ height = 'md',
132
+ showHeightSetting = true,
66
133
  } = Astro.props;
67
134
 
68
135
  type NavItem = { href: string; label: string; match: (path: string) => boolean };
@@ -82,66 +149,362 @@ function getLanguageUrl(langCode: string) {
82
149
  // 如果有基础路径,需要加上
83
150
  return `${basePath}/${langCode}${pathWithoutLocale}`;
84
151
  }
152
+
153
+ // 定义可用的高度选项
154
+ const heightOptions = [
155
+ { value: '3xs', label: '超超小' },
156
+ { value: '2xs', label: '超小' },
157
+ { value: 'xs', label: '小' },
158
+ { value: 'sm', label: '较小' },
159
+ { value: 'md', label: '中等' },
160
+ { value: 'lg', label: '大' },
161
+ { value: 'xl', label: '超大' },
162
+ ];
163
+
164
+ // 获取初始高度(优先使用localStorage中的值)
165
+ type HeightType = '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
166
+
167
+ let initialHeight: HeightType;
168
+ if (typeof window !== 'undefined') {
169
+ const storedHeight = localStorage.getItem('cosy-header-height');
170
+ initialHeight = (storedHeight as HeightType) || height;
171
+ } else {
172
+ initialHeight = height;
173
+ }
174
+
175
+ // 根据高度设置样式
176
+ const headerHeightClasses = {
177
+ '3xs': 'cosy:h-4',
178
+ '2xs': 'cosy:h-6',
179
+ xs: 'cosy:h-8',
180
+ sm: 'cosy:h-10',
181
+ md: 'cosy:h-12',
182
+ lg: 'cosy:h-16',
183
+ xl: 'cosy:h-20',
184
+ };
185
+
186
+ const headerHeightClass = headerHeightClasses[initialHeight];
187
+
188
+ // 设置logo大小
189
+ const logoSizeClasses = {
190
+ '3xs': 'cosy:w-3 cosy:h-3',
191
+ '2xs': 'cosy:w-4 cosy:h-4',
192
+ xs: 'cosy:w-5 cosy:h-5',
193
+ sm: 'cosy:w-6 cosy:h-6',
194
+ md: 'cosy:w-8 cosy:h-8',
195
+ lg: 'cosy:w-10 cosy:h-10',
196
+ xl: 'cosy:w-12 cosy:h-12',
197
+ };
198
+
199
+ const logoSizeClass = logoSizeClasses[initialHeight];
200
+
201
+ // 设置占位空间高度
202
+ const spacerHeightClasses = {
203
+ '3xs': 'cosy:h-4',
204
+ '2xs': 'cosy:h-6',
205
+ xs: 'cosy:h-8',
206
+ sm: 'cosy:h-10',
207
+ md: 'cosy:h-12',
208
+ lg: 'cosy:h-16',
209
+ xl: 'cosy:h-20',
210
+ };
211
+
212
+ const spacerHeightClass = spacerHeightClasses[initialHeight];
85
213
  ---
86
214
 
87
- <header class:list={[
88
- "cosy:navbar cosy:bg-accent/70 cosy:backdrop-blur cosy:border-base-200 cosy:z-50 cosy:w-full",
89
- { "cosy:fixed cosy:top-0": sticky }
90
- ]}>
215
+ <header
216
+ class:list={[
217
+ 'cosy:navbar cosy:bg-accent/70 cosy:backdrop-blur cosy:border-base-200 cosy:z-50 cosy:w-full cosy:m-0 cosy:p-0 cosy:min-h-1',
218
+ headerHeightClass,
219
+ { 'cosy:fixed cosy:top-0': sticky },
220
+ ]}>
91
221
  <div class="cosy:navbar-start">
92
- <Link href={logoHref} class="cosy:btn cosy:btn-ghost">
93
- <Image src={logo} alt="logo" class="cosy:w-10 cosy:h-10" />
222
+ <Link
223
+ href={logoHref}
224
+ class:list={[
225
+ 'cosy:btn cosy:btn-ghost',
226
+ initialHeight === 'xs' || initialHeight === '2xs' || initialHeight === '3xs'
227
+ ? 'cosy:btn-xs cosy:h-auto cosy:min-h-0 cosy:p-1'
228
+ : initialHeight === 'sm'
229
+ ? 'cosy:btn-sm'
230
+ : '',
231
+ ]}>
232
+ <Image src={logo} alt="logo" class={logoSizeClass} />
94
233
  </Link>
95
234
  </div>
96
235
 
97
236
  <!-- 导航 -->
98
237
  <div class="cosy:hidden cosy:lg:flex cosy:navbar-center">
99
- <ul class="cosy:px-1 cosy:menu cosy:menu-horizontal">
100
- {navItems.map((item: NavItem) => (
101
- <li>
102
- <Link href={item.href}>
103
- {item.label}
104
- </Link>
105
- </li>
106
- ))}
238
+ <ul
239
+ class:list={[
240
+ 'cosy:px-1 cosy:menu cosy:menu-horizontal',
241
+ initialHeight === 'xs' || initialHeight === '2xs' || initialHeight === '3xs'
242
+ ? 'cosy:menu-xs'
243
+ : initialHeight === 'sm'
244
+ ? 'cosy:menu-sm'
245
+ : '',
246
+ ]}>
247
+ {
248
+ navItems.map((item: NavItem) => (
249
+ <li>
250
+ <Link
251
+ href={item.href}
252
+ class:list={[
253
+ initialHeight === 'xs' || initialHeight === '2xs' || initialHeight === '3xs'
254
+ ? 'cosy:py-0 cosy:px-2 cosy:min-h-0'
255
+ : initialHeight === 'sm'
256
+ ? 'cosy:py-1 cosy:min-h-6'
257
+ : '',
258
+ ]}>
259
+ {item.label}
260
+ </Link>
261
+ </li>
262
+ ))
263
+ }
107
264
  </ul>
108
265
  </div>
109
266
 
110
267
  <div class="cosy:navbar-end">
268
+ {
269
+ showHeightSetting && (
270
+ <div class="cosy:mr-2 cosy:dropdown cosy:dropdown-end">
271
+ <div
272
+ tabindex="0"
273
+ role="button"
274
+ class:list={[
275
+ 'cosy:btn cosy:btn-ghost',
276
+ initialHeight === 'xs' || initialHeight === '2xs' || initialHeight === '3xs'
277
+ ? 'cosy:btn-xs cosy:h-auto cosy:min-h-0 cosy:p-1'
278
+ : initialHeight === 'sm'
279
+ ? 'cosy:btn-sm'
280
+ : 'cosy:btn-sm',
281
+ ]}
282
+ id="height-dropdown-btn">
283
+ <span class="cosy:mr-1">高度</span>
284
+ <svg
285
+ xmlns="http://www.w3.org/2000/svg"
286
+ class="cosy:w-4 cosy:h-4"
287
+ fill="none"
288
+ viewBox="0 0 24 24"
289
+ stroke="currentColor">
290
+ <path
291
+ stroke-linecap="round"
292
+ stroke-linejoin="round"
293
+ stroke-width="2"
294
+ d="M19 9l-7 7-7-7"
295
+ />
296
+ </svg>
297
+ </div>
298
+ <ul
299
+ tabindex="0"
300
+ class="cosy:z-[1] cosy:bg-base-100 cosy:shadow cosy:p-2 cosy:rounded-box cosy:w-40 cosy:dropdown-content cosy:menu"
301
+ id="height-dropdown-content">
302
+ {heightOptions.map((option) => (
303
+ <li data-height={option.value} class="height-option" id={`height-${option.value}`}>
304
+ <a class={initialHeight === option.value ? 'cosy:active' : ''}>{option.label}</a>
305
+ </li>
306
+ ))}
307
+ </ul>
308
+ </div>
309
+ )
310
+ }
311
+
111
312
  <!-- 语言切换按钮 -->
112
313
  <div class="cosy:dropdown cosy:dropdown-end">
113
- <div tabindex="0" role="button" class="cosy:btn cosy:btn-ghost cosy:btn-sm">
314
+ <div
315
+ tabindex="0"
316
+ role="button"
317
+ class:list={[
318
+ 'cosy:btn cosy:btn-ghost',
319
+ initialHeight === 'xs' || initialHeight === '2xs' || initialHeight === '3xs'
320
+ ? 'cosy:btn-xs cosy:h-auto cosy:min-h-0 cosy:p-1'
321
+ : initialHeight === 'sm'
322
+ ? 'cosy:btn-sm'
323
+ : 'cosy:btn-sm',
324
+ ]}>
114
325
  <span class="cosy:mr-1">{currentLocale === 'zh-cn' ? '中文' : 'English'}</span>
115
- <svg xmlns="http://www.w3.org/2000/svg" class="cosy:w-4 cosy:h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
116
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
326
+ <svg
327
+ xmlns="http://www.w3.org/2000/svg"
328
+ class="cosy:w-4 cosy:h-4"
329
+ fill="none"
330
+ viewBox="0 0 24 24"
331
+ stroke="currentColor">
332
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"
333
+ ></path>
117
334
  </svg>
118
335
  </div>
119
- <ul tabindex="0" class="cosy:z-[1] cosy:bg-base-100 cosy:shadow cosy:p-2 cosy:rounded-box cosy:w-32 cosy:dropdown-content cosy:menu">
120
- {languages.map((lang) => (
121
- <li class={currentLocale === lang.code ? "cosy:disabled" : ""}>
122
- <a href={getLanguageUrl(lang.code)} class={currentLocale === lang.code ? "cosy:active" : ""}>
123
- {lang.name}
124
- </a>
125
- </li>
126
- ))}
336
+ <ul
337
+ tabindex="0"
338
+ class="cosy:z-[1] cosy:bg-base-100 cosy:shadow cosy:p-2 cosy:rounded-box cosy:w-32 cosy:dropdown-content cosy:menu">
339
+ {
340
+ languages.map((lang) => (
341
+ <li class={currentLocale === lang.code ? 'cosy:disabled' : ''}>
342
+ <a
343
+ href={getLanguageUrl(lang.code)}
344
+ class={currentLocale === lang.code ? 'cosy:active' : ''}>
345
+ {lang.name}
346
+ </a>
347
+ </li>
348
+ ))
349
+ }
127
350
  </ul>
128
351
  </div>
129
352
  </div>
130
353
  </header>
131
354
 
132
- {sticky && (
133
- <div class="cosy:h-16"></div>
134
- )}
135
-
136
- <script define:vars={{ sticky }}>
137
- if (sticky) {
138
- const header = document.querySelector('header');
139
- window.addEventListener('scroll', () => {
140
- if (window.pageYOffset > 0) {
141
- header?.classList.add('cosy:shadow-lg');
142
- } else {
143
- header?.classList.remove('cosy:shadow-lg');
355
+ {sticky && <div class={spacerHeightClass} id="header-spacer" />}
356
+
357
+ <script define:vars={{ sticky, height, headerHeightClasses, spacerHeightClasses }}>
358
+ // 等待DOM完全加载
359
+ document.addEventListener('DOMContentLoaded', () => {
360
+ // 获取存储的高度设置,如果没有则使用传入的默认高度
361
+ const storedHeight = localStorage.getItem('cosy-header-height');
362
+ let currentHeight = storedHeight || height;
363
+
364
+ // 更新DOM元素的高度类
365
+ function updateHeaderHeight(newHeight) {
366
+ const header = document.querySelector('header');
367
+ const spacer = document.getElementById('header-spacer');
368
+
369
+ if (!header) return;
370
+
371
+ // 先获取header当前的所有类名
372
+ const headerClasses = [...header.classList];
373
+
374
+ // 找出并移除当前的高度类
375
+ Object.values(headerHeightClasses).forEach((cls) => {
376
+ if (headerClasses.includes(cls)) {
377
+ header.classList.remove(cls);
378
+ }
379
+ });
380
+
381
+ // 添加新的高度类
382
+ const newHeaderClass = headerHeightClasses[newHeight];
383
+ header.classList.add(newHeaderClass);
384
+
385
+ // 更新占位元素高度
386
+ if (spacer) {
387
+ const spacerClasses = [...spacer.classList];
388
+
389
+ Object.values(spacerHeightClasses).forEach((cls) => {
390
+ const className = cls.replace('cosy:', '');
391
+ if (spacerClasses.includes(className)) {
392
+ spacer.classList.remove(className);
393
+ }
394
+ });
395
+
396
+ const newSpacerClass = spacerHeightClasses[newHeight].replace('cosy:', '');
397
+ spacer.classList.add(newSpacerClass);
398
+ }
399
+
400
+ // 更新样式类,需要处理:list中的类
401
+ // 1. 处理logo按钮
402
+ const logoBtn = document.querySelector('.cosy\\:navbar-start .cosy\\:btn');
403
+ if (logoBtn) {
404
+ // 清理之前的按钮大小类
405
+ ['cosy:btn-xs', 'cosy:btn-sm', 'cosy:h-auto', 'cosy:min-h-0', 'cosy:p-1'].forEach((cls) => {
406
+ logoBtn.classList.remove(cls.replace('cosy:', ''));
407
+ });
408
+
409
+ // 根据新高度添加适当的类
410
+ if (newHeight === 'xs' || newHeight === '2xs' || newHeight === '3xs') {
411
+ ['cosy:btn-xs', 'cosy:h-auto', 'cosy:min-h-0', 'cosy:p-1'].forEach((cls) => {
412
+ logoBtn.classList.add(cls.replace('cosy:', ''));
413
+ });
414
+ } else if (newHeight === 'sm') {
415
+ logoBtn.classList.add('btn-sm');
416
+ }
417
+ }
418
+
419
+ // 2. 处理导航菜单
420
+ const navMenu = document.querySelector('.cosy\\:navbar-center .cosy\\:menu');
421
+ if (navMenu) {
422
+ navMenu.classList.remove('menu-xs', 'menu-sm');
423
+
424
+ if (newHeight === 'xs' || newHeight === '2xs' || newHeight === '3xs') {
425
+ navMenu.classList.add('menu-xs');
426
+ } else if (newHeight === 'sm') {
427
+ navMenu.classList.add('menu-sm');
428
+ }
144
429
  }
430
+
431
+ // 3. 处理菜单项
432
+ const navLinks = document.querySelectorAll('.cosy\\:navbar-center .cosy\\:menu li a');
433
+ navLinks.forEach((link) => {
434
+ ['cosy:py-0', 'cosy:px-2', 'cosy:min-h-0', 'cosy:py-1', 'cosy:min-h-6'].forEach((cls) => {
435
+ link.classList.remove(cls.replace('cosy:', ''));
436
+ });
437
+
438
+ if (newHeight === 'xs' || newHeight === '2xs' || newHeight === '3xs') {
439
+ ['cosy:py-0', 'cosy:px-2', 'cosy:min-h-0'].forEach((cls) => {
440
+ link.classList.add(cls.replace('cosy:', ''));
441
+ });
442
+ } else if (newHeight === 'sm') {
443
+ ['cosy:py-1', 'cosy:min-h-6'].forEach((cls) => {
444
+ link.classList.add(cls.replace('cosy:', ''));
445
+ });
446
+ }
447
+ });
448
+
449
+ // 4. 处理下拉按钮
450
+ const dropdownBtns = document.querySelectorAll(
451
+ '.cosy\\:navbar-end .cosy\\:dropdown .cosy\\:btn'
452
+ );
453
+ dropdownBtns.forEach((btn) => {
454
+ ['cosy:btn-xs', 'cosy:btn-sm', 'cosy:h-auto', 'cosy:min-h-0', 'cosy:p-1'].forEach((cls) => {
455
+ btn.classList.remove(cls.replace('cosy:', ''));
456
+ });
457
+
458
+ if (newHeight === 'xs' || newHeight === '2xs' || newHeight === '3xs') {
459
+ ['cosy:btn-xs', 'cosy:h-auto', 'cosy:min-h-0', 'cosy:p-1'].forEach((cls) => {
460
+ btn.classList.add(cls.replace('cosy:', ''));
461
+ });
462
+ } else if (newHeight === 'sm' || newHeight === 'md') {
463
+ btn.classList.add('btn-sm');
464
+ }
465
+ });
466
+
467
+ // 标记当前选中的选项
468
+ document.querySelectorAll('.height-option a').forEach((item) => {
469
+ item.classList.remove('active');
470
+ });
471
+
472
+ const activeItem = document.querySelector(`#height-${newHeight} a`);
473
+ if (activeItem) {
474
+ activeItem.classList.add('active');
475
+ }
476
+
477
+ // 保存设置到本地存储
478
+ localStorage.setItem('cosy-header-height', newHeight);
479
+ currentHeight = newHeight;
480
+ }
481
+
482
+ // 如果有存储的高度设置,应用它
483
+ if (storedHeight) {
484
+ updateHeaderHeight(storedHeight);
485
+ }
486
+
487
+ // 绑定点击事件到高度选项
488
+ document.querySelectorAll('.height-option').forEach((item) => {
489
+ item.addEventListener('click', function () {
490
+ const newHeight = this.getAttribute('data-height');
491
+ updateHeaderHeight(newHeight);
492
+
493
+ // 关闭下拉菜单
494
+ document.activeElement.blur();
495
+ });
145
496
  });
146
- }
147
- </script>
497
+
498
+ // 滚动时添加阴影
499
+ if (sticky) {
500
+ const header = document.querySelector('header');
501
+ window.addEventListener('scroll', () => {
502
+ if (window.pageYOffset > 0) {
503
+ header?.classList.add('cosy:shadow-lg'.replace('cosy:', ''));
504
+ } else {
505
+ header?.classList.remove('cosy:shadow-lg'.replace('cosy:', ''));
506
+ }
507
+ });
508
+ }
509
+ });
510
+ </script>
@@ -90,7 +90,7 @@ const currentSection = sidebarItems.find((section) =>
90
90
 
91
91
  {/* 桌面端侧边栏 */}
92
92
  <aside class:list={[className, debugClass, 'cosy:hidden cosy:lg:block']}>
93
- <div class="cosy:top-16 cosy:sticky cosy:h-[calc(100vh-4rem)]">
93
+ <div class="cosy:top-16 cosy:sticky cosy:pb-48 cosy:h-[calc(100vh-0rem)] cosy:overflow-y-auto">
94
94
  <SidebarNav sidebarItems={sidebarItems} currentPath={currentPath} debug={debug} />
95
95
  </div>
96
96
  </aside>
@@ -1,81 +1,111 @@
1
1
  ---
2
+ /**
3
+ * @component LanguageSwitcher
4
+ *
5
+ * @description
6
+ * LanguageSwitcher 组件提供一个语言切换下拉菜单,支持多语言网站的语言切换功能。
7
+ * 组件会自动处理 URL 路径,确保在切换语言时保留当前页面路径。
8
+ *
9
+ * @design
10
+ * 设计理念:
11
+ * 1. 简洁直观 - 清晰显示当前语言和可选语言
12
+ * 2. 无缝集成 - 自动处理 URL 路由,保持用户浏览上下文
13
+ * 3. 可定制性 - 支持自定义语言列表和当前语言设置
14
+ * 4. 一致的视觉风格 - 使用与整体设计系统一致的下拉菜单样式
15
+ *
16
+ * @usage
17
+ * 基本用法:
18
+ * ```astro
19
+ * <LanguageSwitcher />
20
+ * ```
21
+ *
22
+ * 自定义语言列表:
23
+ * ```astro
24
+ * <LanguageSwitcher
25
+ * languages={[
26
+ * { code: 'zh-cn', name: '简体中文' },
27
+ * { code: 'en', name: 'English' },
28
+ * { code: 'ja', name: '日本語' }
29
+ * ]}
30
+ * currentLocale="en"
31
+ * />
32
+ * ```
33
+ */
34
+
2
35
  import Button from '../base/Button.astro';
3
36
  import Link from '../base/Link.astro';
37
+ import '../../app.css';
4
38
 
5
39
  interface Language {
6
- code: string;
7
- name: string;
40
+ code: string;
41
+ name: string;
8
42
  }
9
43
 
10
44
  interface Props {
11
- /**
12
- * 自定义类名
13
- */
14
- class?: string;
45
+ /**
46
+ * 自定义类名
47
+ */
48
+ class?: string;
15
49
 
16
- /**
17
- * 语言列表
18
- * @default [{ code: 'zh-cn', name: '简体中文' }, { code: 'en', name: 'English' }]
19
- */
20
- languages?: Language[];
50
+ /**
51
+ * 语言列表
52
+ * @default [{ code: 'zh-cn', name: '简体中文' }, { code: 'en', name: 'English' }]
53
+ */
54
+ languages?: Language[];
21
55
 
22
- /**
23
- * 当前语言
24
- * @default 'zh-cn'
25
- */
26
- currentLocale?: string;
56
+ /**
57
+ * 当前语言
58
+ * @default 'zh-cn'
59
+ */
60
+ currentLocale?: string;
27
61
  }
28
62
 
29
63
  const {
30
- class: className,
31
- languages = [
32
- { code: 'zh-cn', name: '简体中文' },
33
- { code: 'en', name: 'English' },
34
- ],
35
- currentLocale = 'zh-cn',
64
+ class: className,
65
+ languages = [
66
+ { code: 'zh-cn', name: '简体中文' },
67
+ { code: 'en', name: 'English' },
68
+ ],
69
+ currentLocale = 'zh-cn',
36
70
  } = Astro.props;
37
71
 
38
72
  const currentLanguageName =
39
- languages.find((lang: Language) => lang.code === currentLocale)?.name || '简体中文';
73
+ languages.find((lang: Language) => lang.code === currentLocale)?.name || '简体中文';
40
74
 
41
75
  // 为每个语言生成对应的URL
42
76
  function generateLanguageUrl(langCode: string): string {
43
- const currentPath = Astro.url.pathname;
44
- const pathParts = currentPath.split('/').filter(Boolean);
45
- const firstPathPart = pathParts[0];
46
- const supportedLanguages = languages.map((lang: Language) => lang.code);
47
- const isFirstPartLang = supportedLanguages.includes(firstPathPart);
77
+ const currentPath = Astro.url.pathname;
78
+ const pathParts = currentPath.split('/').filter(Boolean);
79
+ const firstPathPart = pathParts[0];
80
+ const supportedLanguages = languages.map((lang: Language) => lang.code);
81
+ const isFirstPartLang = supportedLanguages.includes(firstPathPart);
48
82
 
49
- if (isFirstPartLang) {
50
- pathParts[0] = langCode;
51
- return '/' + pathParts.join('/');
52
- } else {
53
- return '/' + langCode + (currentPath === '/' ? '' : currentPath);
54
- }
83
+ if (isFirstPartLang) {
84
+ pathParts[0] = langCode;
85
+ return '/' + pathParts.join('/');
86
+ } else {
87
+ return '/' + langCode + (currentPath === '/' ? '' : currentPath);
88
+ }
55
89
  }
56
90
  ---
57
91
 
58
- <div class:list={["dropdown-end dropdown", className]}>
59
- <Button variant="ghost" size="sm" class="p-1">
60
- {currentLanguageName}
61
- </Button>
62
- <ul
63
- tabindex={0}
64
- class="dropdown-content menu w-40 rounded-box bg-slate-900 p-2 shadow-lg dark:bg-slate-800"
65
- >
66
- {
67
- languages.map((lang: Language) => (
68
- <li>
69
- <Link
70
- href={generateLanguageUrl(lang.code)}
71
- class:list={[
72
- { active: lang.code === currentLocale }
73
- ]}
74
- >
75
- {lang.name}
76
- </Link>
77
- </li>
78
- ))
79
- }
80
- </ul>
81
- </div>
92
+ <div class:list={['cosy:dropdown cosy:dropdown-end', className]}>
93
+ <Button variant="ghost" size="sm" class="cosy:p-1">
94
+ {currentLanguageName}
95
+ </Button>
96
+ <ul
97
+ tabindex={0}
98
+ class="cosy:bg-slate-900 cosy:dark:bg-slate-800 cosy:shadow-lg cosy:p-2 cosy:rounded-box cosy:w-40 cosy:dropdown-content cosy:menu">
99
+ {
100
+ languages.map((lang: Language) => (
101
+ <li>
102
+ <Link
103
+ href={generateLanguageUrl(lang.code)}
104
+ class:list={[{ 'cosy:active': lang.code === currentLocale }]}>
105
+ {lang.name}
106
+ </Link>
107
+ </li>
108
+ ))
109
+ }
110
+ </ul>
111
+ </div>