@coffic/cosy-ui 0.9.24 → 0.9.25

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 (40) hide show
  1. package/dist/app.css +1 -1
  2. package/dist/index-astro.ts +0 -1
  3. package/dist/src/assets/iconData.ts +5 -0
  4. package/dist/src/utils/link.ts +1 -1
  5. package/dist/src/utils/theme.ts +99 -96
  6. package/dist/src-astro/article/Article.astro +1 -0
  7. package/dist/src-astro/container/Container.astro +9 -0
  8. package/dist/src-astro/footer/Footer.astro +16 -19
  9. package/dist/src-astro/footer/FooterCopyright.astro +8 -17
  10. package/dist/src-astro/footer/FooterICP.astro +18 -0
  11. package/dist/src-astro/footer/index.ts +1 -0
  12. package/dist/src-astro/footer/types.ts +27 -0
  13. package/dist/src-astro/header/Header.astro +53 -54
  14. package/dist/src-astro/header/HeaderCenter.astro +59 -0
  15. package/dist/src-astro/header/HeaderEnd.astro +90 -0
  16. package/dist/src-astro/header/HeaderStart.astro +78 -0
  17. package/dist/src-astro/header/MobileNav.astro +44 -0
  18. package/dist/src-astro/header/NavItems.astro +82 -0
  19. package/dist/src-astro/header/index.ts +6 -0
  20. package/dist/src-astro/icons/GlobeIcon.astro +28 -0
  21. package/dist/src-astro/icons/index.ts +1 -0
  22. package/dist/src-astro/language-switcher/LanguageSwitcher.astro +5 -2
  23. package/dist/src-astro/layout-app/AppHeader.astro +95 -0
  24. package/dist/src-astro/layout-app/AppLayout.astro +18 -26
  25. package/dist/src-astro/modal/Modal.astro +29 -4
  26. package/dist/src-astro/sidebar/DesktopSidebar.astro +123 -0
  27. package/dist/src-astro/sidebar/MobileSidebar.astro +91 -0
  28. package/dist/src-astro/sidebar/Sidebar.astro +45 -86
  29. package/dist/src-astro/sidebar/SidebarNav.astro +12 -3
  30. package/dist/src-astro/sidebar/index.ts +1 -2
  31. package/dist/src-astro/theme-switcher/ThemeItem.astro +43 -3
  32. package/dist/src-astro/theme-switcher/ThemeSwitcher.astro +6 -67
  33. package/dist/src-astro/theme-switcher/index.ts +1 -9
  34. package/dist/src-astro/types/footer.ts +5 -0
  35. package/dist/src-astro/types/header.ts +6 -0
  36. package/package.json +1 -1
  37. package/dist/src-astro/nav-item/NavItems.astro +0 -44
  38. package/dist/src-astro/nav-item/index.ts +0 -3
  39. package/dist/src-astro/sidebar/MobileNav.astro +0 -55
  40. package/dist/src-astro/theme-switcher/ThemeSwitcherBasic.astro +0 -7
@@ -35,7 +35,6 @@ export * from './src-astro/logout';
35
35
  export * from './src-astro/main';
36
36
  export * from './src-astro/modal';
37
37
  export * from './src-astro/module';
38
- export * from './src-astro/nav-item';
39
38
  export * from './src-astro/nav-section';
40
39
  export * from './src-astro/placeholder';
41
40
  export * from './src-astro/products';
@@ -239,4 +239,9 @@ export const iconData: Record<string, IconData> = {
239
239
  website: {
240
240
  path: 'M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
241
241
  },
242
+
243
+ // 地球图标
244
+ globe: {
245
+ path: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z',
246
+ },
242
247
  };
@@ -32,7 +32,7 @@ export class LinkUtil {
32
32
  }
33
33
 
34
34
  static getHomeLink(lang: string): string {
35
- return `${this.getBaseUrl}/${lang}`;
35
+ return `${this.getBaseUrl()}/${lang}`;
36
36
  }
37
37
 
38
38
  static getLessonsLink(lang: string): string {
@@ -1,9 +1,9 @@
1
1
  interface ThemeManager {
2
- updateActiveTheme: (currentTheme: string) => void;
3
- handleThemeClick: (this: HTMLElement) => void;
4
- initialize: () => void;
5
- setTheme: (theme: string) => void;
6
- detectSystemTheme: () => string;
2
+ updateActiveTheme: (currentTheme: string) => void;
3
+ handleThemeClick: (this: HTMLElement) => void;
4
+ initialize: () => void;
5
+ setTheme: (theme: string) => void;
6
+ detectSystemTheme: () => string;
7
7
  }
8
8
 
9
9
  /**
@@ -14,109 +14,112 @@ interface ThemeManager {
14
14
  * @returns ThemeManager 主题管理器对象
15
15
  */
16
16
  export function createThemeManager(): ThemeManager {
17
- const getThemeItems = () =>
18
- document.querySelectorAll<HTMLElement>('[data-theme]');
17
+ const getThemeItems = () =>
18
+ document.querySelectorAll<HTMLElement>('[data-theme-id], .cosy\\:theme-item');
19
19
 
20
- const updateActiveTheme = (currentTheme: string) => {
21
- const themeItems = getThemeItems();
22
- themeItems.forEach((item) => {
23
- const themeId = item.getAttribute('data-theme');
24
- const isActive = themeId === currentTheme;
20
+ const updateActiveTheme = (currentTheme: string) => {
21
+ const themeItems = getThemeItems();
22
+ themeItems.forEach((item) => {
23
+ const themeId = item.getAttribute('data-theme-id');
24
+ const isActive = themeId === currentTheme;
25
25
 
26
- // 设置元素的 data-active 属性
27
- item.setAttribute('data-active', String(isActive));
26
+ // 设置元素的 data-active 属性
27
+ item.setAttribute('data-active', String(isActive));
28
28
 
29
- // 仅对于旧式主题按钮(data-set-theme 属性),保留旧的类切换行为
30
- if (item.hasAttribute('data-set-theme')) {
31
- item.classList.toggle('cosy:bg-primary', isActive);
32
- item.classList.toggle('cosy:text-primary-content', isActive);
33
- }
34
- });
35
- };
29
+ // 更新 CheckIcon 显示状态
30
+ if (item.classList.contains('cosy:theme-item')) {
31
+ const checkmark = item.querySelector('.cosy\\:theme-check');
32
+ if (checkmark) {
33
+ if (isActive) {
34
+ checkmark.classList.remove('cosy:hidden');
35
+ item.classList.add('cosy:bg-base-200', 'cosy:font-medium');
36
+ } else {
37
+ checkmark.classList.add('cosy:hidden');
38
+ item.classList.remove('cosy:bg-base-200', 'cosy:font-medium');
39
+ }
40
+ }
41
+ }
42
+ });
43
+ };
36
44
 
37
- function handleThemeClick(this: HTMLElement) {
38
- const theme =
39
- this.getAttribute('data-set-theme') || this.getAttribute('data-theme');
40
- if (theme) {
41
- setTheme(theme);
45
+ function handleThemeClick(this: HTMLElement) {
46
+ const theme = this.getAttribute('data-theme-id');
47
+ if (theme) {
48
+ setTheme(theme);
49
+ }
42
50
  }
43
- }
44
51
 
45
- /**
46
- * 设置主题
47
- *
48
- * @param theme 主题ID
49
- */
50
- const setTheme = (theme: string) => {
51
- document.documentElement.setAttribute('data-theme', theme);
52
- localStorage.setItem('theme', theme);
53
- updateActiveTheme(theme);
54
- };
52
+ /**
53
+ * 设置主题
54
+ *
55
+ * @param theme 主题ID
56
+ */
57
+ const setTheme = (theme: string) => {
58
+ document.documentElement.setAttribute('data-theme', theme);
59
+ localStorage.setItem('theme', theme);
60
+ updateActiveTheme(theme);
61
+ };
55
62
 
56
- /**
57
- * 检测系统主题偏好(亮色/暗色)
58
- *
59
- * @returns 'dark' 或 'light'
60
- */
61
- const detectSystemTheme = (): string => {
62
- return window.matchMedia('(prefers-color-scheme: dark)').matches
63
- ? 'dark'
64
- : 'light';
65
- };
63
+ /**
64
+ * 检测系统主题偏好(亮色/暗色)
65
+ *
66
+ * @returns 'dark' 或 'light'
67
+ */
68
+ const detectSystemTheme = (): string => {
69
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
70
+ ? 'dark'
71
+ : 'light';
72
+ };
66
73
 
67
- /**
68
- * 初始化主题管理器
69
- *
70
- * 从本地存储中获取主题设置,如果没有则尝试使用系统主题,最后使用默认主题
71
- */
72
- const initialize = () => {
73
- // 从本地存储中获取主题
74
- let savedTheme = localStorage.getItem('theme');
74
+ /**
75
+ * 初始化主题管理器
76
+ *
77
+ * 从本地存储中获取主题设置,如果没有则尝试使用系统主题,最后使用默认主题
78
+ */
79
+ const initialize = () => {
80
+ // 从本地存储中获取主题
81
+ let savedTheme = localStorage.getItem('theme');
75
82
 
76
- // 如果没有保存的主题,则尝试使用系统主题
77
- if (!savedTheme) {
78
- const systemTheme = detectSystemTheme();
79
- if (systemTheme === 'dark') {
80
- savedTheme = 'dark';
81
- } else {
82
- savedTheme = 'default'; // 默认使用默认主题
83
- }
84
- }
83
+ // 如果没有保存的主题,则尝试使用系统主题
84
+ if (!savedTheme) {
85
+ const systemTheme = detectSystemTheme();
86
+ if (systemTheme === 'dark') {
87
+ savedTheme = 'dark';
88
+ } else {
89
+ savedTheme = 'default'; // 默认使用默认主题
90
+ }
91
+ }
85
92
 
86
- // 设置主题
87
- document.documentElement.setAttribute('data-theme', savedTheme);
88
- updateActiveTheme(savedTheme);
93
+ // 设置主题
94
+ document.documentElement.setAttribute('data-theme', savedTheme);
95
+ updateActiveTheme(savedTheme);
89
96
 
90
- // 添加主题切换事件监听器
91
- // 支持新旧两种类型的主题切换按钮
92
- const themeItems = [
93
- ...Array.from(document.querySelectorAll<HTMLElement>('[data-set-theme]')),
94
- ...Array.from(
95
- document.querySelectorAll<HTMLElement>('.cosy\\:theme-item')
96
- ),
97
- ];
97
+ // 添加主题切换事件监听器
98
+ const themeItems = Array.from(
99
+ document.querySelectorAll<HTMLElement>('[data-theme-id]')
100
+ );
98
101
 
99
- themeItems.forEach((item) => {
100
- item.removeEventListener('click', handleThemeClick);
101
- item.addEventListener('click', handleThemeClick);
102
- });
102
+ themeItems.forEach((item) => {
103
+ item.removeEventListener('click', handleThemeClick);
104
+ item.addEventListener('click', handleThemeClick);
105
+ });
103
106
 
104
- // 监听系统主题变化
105
- window
106
- .matchMedia('(prefers-color-scheme: dark)')
107
- .addEventListener('change', (e) => {
108
- // 只有当用户没有手动设置过主题时,才跟随系统主题变化
109
- if (!localStorage.getItem('theme')) {
110
- setTheme(e.matches ? 'dark' : 'default');
111
- }
112
- });
113
- };
107
+ // 监听系统主题变化
108
+ window
109
+ .matchMedia('(prefers-color-scheme: dark)')
110
+ .addEventListener('change', (e) => {
111
+ // 只有当用户没有手动设置过主题时,才跟随系统主题变化
112
+ if (!localStorage.getItem('theme')) {
113
+ setTheme(e.matches ? 'dark' : 'default');
114
+ }
115
+ });
116
+ };
114
117
 
115
- return {
116
- updateActiveTheme,
117
- handleThemeClick,
118
- initialize,
119
- setTheme,
120
- detectSystemTheme,
121
- };
118
+ return {
119
+ updateActiveTheme,
120
+ handleThemeClick,
121
+ initialize,
122
+ setTheme,
123
+ detectSystemTheme,
124
+ };
122
125
  }
@@ -127,6 +127,7 @@ const baseClasses = [
127
127
  'cosy:mx-auto',
128
128
  'cosy:dark:prose-invert', // 暗黑模式支持,
129
129
  'cosy:m-0',
130
+ 'cosy:px-2 cosy:lg:px-4',
130
131
  'cosy:min-h-screen',
131
132
  'cosy:pb-96',
132
133
  widthClasses,
@@ -71,6 +71,15 @@
71
71
  * <div>第二项</div>
72
72
  * </Container>
73
73
  * ```
74
+ *
75
+ * @props
76
+ * @prop {string} size - 容器尺寸
77
+ * @prop {string} padding - 内边距大小
78
+ * @prop {string} margin - 外边距大小
79
+ * @prop {boolean} centered - 是否居中显示
80
+ * @prop {boolean} border - 是否显示边框
81
+ * @prop {string} flex - flex布局方向
82
+ * @prop {string} gap - flex项目间距
74
83
  */
75
84
 
76
85
  import type { HTMLAttributes } from 'astro/types';
@@ -139,6 +139,7 @@
139
139
  * @prop {string} copyright - 版权信息
140
140
  * @prop {string} inspirationalSlogan - 鼓舞人心的标语,显示在横幅中
141
141
  * @prop {string} [icp] - ICP备案号(中国网站需要)
142
+ * @prop {string} [icpLink] - ICP备案链接(可选)
142
143
  * @prop {Object} [logo] - 网站Logo对象,包含src和alt属性
143
144
  * @prop {Array<Object>} [products=[]] - 产品链接数组,每个对象包含name、href和可选的external属性
144
145
  * @prop {Array<Object>} [friendlyLinks=[]] - 友情链接数组,每个对象包含name、href和可选的external属性
@@ -173,10 +174,12 @@ import {
173
174
  type IFooterProps,
174
175
  createTextGetter,
175
176
  Link,
177
+ Container,
176
178
  } from '../../index-astro';
177
179
  import FooterSection from './FooterSection.astro';
178
180
  import '../../style.ts';
179
181
  import FooterCopyright from './FooterCopyright.astro';
182
+ import FooterICP from './FooterICP.astro';
180
183
 
181
184
  const {
182
185
  siteName,
@@ -186,6 +189,7 @@ const {
186
189
  copyright,
187
190
  inspirationalSlogan,
188
191
  icp,
192
+ icpLink,
189
193
  logo,
190
194
  products = [],
191
195
  friendlyLinks = [],
@@ -234,7 +238,7 @@ const debugClasses = debug
234
238
 
235
239
  <footer
236
240
  class:list={[
237
- 'cosy:footer cosy:z-50 cosy:sm:footer-horizontal cosy:bg-base-200 cosy:text-base-content cosy:p-10',
241
+ 'cosy:footer cosy:flex cosy:flex-col cosy:z-50 cosy:sm:footer-horizontal cosy:bg-base-200 cosy:text-base-content cosy:p-10',
238
242
  debugClasses.footer,
239
243
  ]}>
240
244
  <div
@@ -356,23 +360,16 @@ const debugClasses = debug
356
360
  }
357
361
  </div>
358
362
  </div>
359
- </footer>
360
363
 
361
- {/* 横幅 */}
362
- {
363
- inspirationalSlogan && (
364
- <aside class:list={['cosy:p-4 cosy:w-full', debugClasses.aside]}>
365
- <p class="cosy:text-center">{inspirationalSlogan}</p>
366
- </aside>
367
- )
368
- }
364
+ <div
365
+ class="cosy:flex cosy:flex-col cosy:items-center cosy:justify-center cosy:w-full cosy:opacity-70 cosy:lg:flex-row cosy:gap-4">
366
+ <FooterCopyright
367
+ company={company}
368
+ copyright={copyright}
369
+ currentYear={currentYear}
370
+ t={t}
371
+ />
369
372
 
370
- {/* 底部版权信息 */}
371
- <FooterCopyright
372
- company={company}
373
- copyright={copyright}
374
- icp={icp}
375
- currentYear={currentYear}
376
- t={t}
377
- debugClasses={debugClasses}
378
- />
373
+ {icp && <FooterICP icp={icp} icpLink={icpLink} />}
374
+ </div>
375
+ </footer>
@@ -5,25 +5,16 @@
5
5
  * @props
6
6
  * @prop {string} company - 公司名称
7
7
  * @prop {string} copyright - 版权信息
8
- * @prop {string} icp - ICP备案号(可选)
9
8
  * @prop {number} currentYear - 当前年份
10
9
  * @prop {Function} t - 文本获取函数
11
- * @prop {object} debugClasses - 调试样式类
12
10
  */
13
- const { company, copyright, icp, currentYear, t, debugClasses } = Astro.props;
11
+
12
+ import '../../style.ts';
13
+
14
+ const { company, copyright, currentYear, t } = Astro.props;
14
15
  ---
15
16
 
16
- <div
17
- class:list={[
18
- 'cosy:footer cosy:footer-center cosy:p-4 cosy:bg-base-300',
19
- debugClasses?.footer,
20
- ]}>
21
- <aside
22
- class:list={['cosy:items-center cosy:grid-flow-col', debugClasses?.aside]}>
23
- <p class="cosy:opacity-70 cosy:text-sm">
24
- © {currentYear}
25
- {company} - {copyright || (t && t('allRightsReserved'))}
26
- </p>
27
- {icp && <p class="cosy:opacity-70 cosy:ml-4 cosy:text-sm">{icp}</p>}
28
- </aside>
29
- </div>
17
+ <p class="cosy:text-sm cosy:text-center">
18
+ © {currentYear}
19
+ {company} - {copyright || (t && t('allRightsReserved'))}
20
+ </p>
@@ -0,0 +1,18 @@
1
+ ---
2
+ /**
3
+ * @component FooterICP
4
+ * @description 页脚ICP备案信息组件。
5
+ * @props
6
+ * @prop {string} icp - ICP备案号
7
+ * @prop {string} icpLink - ICP备案链接(可选,默认为工信部备案查询网站)
8
+ */
9
+
10
+ import '../../style.ts';
11
+ import { Link } from '../../index-astro';
12
+
13
+ const { icp, icpLink = 'https://beian.miit.gov.cn/' } = Astro.props;
14
+ ---
15
+
16
+ <Link href={icpLink} external variant="text" size="sm" noUnderline={true}>
17
+ {icp}
18
+ </Link>
@@ -1,2 +1,3 @@
1
1
  export { default as Footer } from './Footer.astro';
2
2
  export { default as FooterSection } from './FooterSection.astro';
3
+ export { default as FooterICP } from './FooterICP.astro';
@@ -0,0 +1,27 @@
1
+ export interface FooterICPProps {
2
+ icp: string;
3
+ icpLink?: string;
4
+ debugClasses?: {
5
+ icp?: string;
6
+ };
7
+ }
8
+
9
+ export interface FooterCopyrightProps {
10
+ company: string;
11
+ copyright?: string;
12
+ currentYear: number;
13
+ t?: (key: string) => string;
14
+ debugClasses?: {
15
+ footer?: string;
16
+ aside?: string;
17
+ };
18
+ }
19
+
20
+ export interface FooterSectionProps {
21
+ title: string;
22
+ links: Array<{
23
+ name: string;
24
+ href?: string;
25
+ external?: boolean;
26
+ }>;
27
+ }
@@ -27,19 +27,26 @@
27
27
  * <Header astroI18n={astroI18n} />
28
28
  * ```
29
29
  *
30
+ * 自定义导航项间距:
31
+ * ```astro
32
+ * <Header gap={4} />
33
+ * ```
34
+ *
30
35
  * @props
31
36
  * - astroI18n - 完整的 astro:i18n 模块(启用语言切换时需要)
37
+ * - gap - 导航项之间的间距,可选值:0, 1, 2, 3, 4, 6, 8, 10, 12,默认 2
32
38
  */
33
39
  import {
34
- LanguageSwitcher,
35
40
  LinkUtil,
36
41
  type IHeaderProps,
37
42
  type INavItem,
38
- NavItems,
39
- ThemeSwitcher,
43
+ Modal,
40
44
  } from '../../index-astro';
41
45
  import Logo from '../../src/assets/logo-rounded.png';
42
- import LogoLink from './LogoLink.astro';
46
+ import HeaderStart from './HeaderStart.astro';
47
+ import HeaderCenter from './HeaderCenter.astro';
48
+ import HeaderEnd from './HeaderEnd.astro';
49
+ import MobileNav from './MobileNav.astro';
43
50
 
44
51
  export interface Props extends IHeaderProps {
45
52
  debug?: boolean;
@@ -57,6 +64,7 @@ const {
57
64
  paddingVertical = 'none',
58
65
  navPosition = 'center',
59
66
  showThemeSwitcher = true,
67
+ gap = 2,
60
68
  astroI18n,
61
69
  } = Astro.props;
62
70
 
@@ -121,7 +129,7 @@ const activeLink = LinkUtil.getActiveLink(
121
129
  ]}>
122
130
  <div
123
131
  class:list={[
124
- 'cosy:bg-accent/70 cosy:flex cosy:flex-grow cosy:backdrop-blur not-prose cosy:border-base-200',
132
+ 'cosy:bg-base-300/90 cosy:text-base-content cosy:flex cosy:flex-grow cosy:backdrop-blur not-prose cosy:shadow-md',
125
133
  {
126
134
  'cosy:rounded-none': rounded === 'none',
127
135
  'cosy:rounded-sm': rounded === 'sm',
@@ -133,56 +141,47 @@ const activeLink = LinkUtil.getActiveLink(
133
141
  headerHeightClass,
134
142
  { 'cosy:border cosy:border-dashed cosy:border-red-500': debug },
135
143
  ]}>
136
- <div class="cosy:navbar-start cosy:pl-1">
137
- {
138
- navPosition === 'start' ? (
139
- <div class="cosy:flex cosy:items-center cosy:gap-4">
140
- <LogoLink src={logo} href={logoHref} size={height} debug={debug} />
141
- <div class="cosy:hidden cosy:lg:flex">
142
- <NavItems
143
- navItems={navItems}
144
- activeLink={activeLink}
145
- linkHeightClass={linkHeightClass}
146
- />
147
- </div>
148
- </div>
149
- ) : (
150
- <LogoLink src={logo} href={logoHref} size={height} debug={debug} />
151
- )
152
- }
153
- <slot name="navbar-start" />
154
- </div>
155
-
156
- <div class="cosy:hidden cosy:lg:flex cosy:navbar-center">
157
- {
158
- navPosition === 'center' && (
159
- <NavItems
160
- navItems={navItems}
161
- activeLink={activeLink}
162
- linkHeightClass={linkHeightClass}
163
- />
164
- )
165
- }
166
- <slot name="navbar-center" />
167
- </div>
168
-
169
- <div class="cosy:navbar-end cosy:pr-1">
170
- {
171
- navPosition === 'end' && (
172
- <div class="cosy:hidden cosy:lg:flex">
173
- <NavItems
174
- navItems={navItems}
175
- activeLink={activeLink}
176
- linkHeightClass={linkHeightClass}
177
- />
178
- </div>
179
- )
180
- }
144
+ <HeaderStart
145
+ logo={logo}
146
+ logoHref={logoHref}
147
+ height={height}
148
+ navPosition={navPosition}
149
+ navItems={navItems}
150
+ activeLink={activeLink}
151
+ linkHeightClass={linkHeightClass}
152
+ debug={debug}>
153
+ <slot name="navbar-start" slot="navbar-start" />
154
+ </HeaderStart>
181
155
 
182
- {showThemeSwitcher && <ThemeSwitcher />}
183
- {isI18nEnabled && <LanguageSwitcher astroI18n={astroI18n} />}
156
+ <HeaderCenter
157
+ navPosition={navPosition}
158
+ navItems={navItems}
159
+ activeLink={activeLink}
160
+ linkHeightClass={linkHeightClass}
161
+ gap={gap}
162
+ />
184
163
 
185
- <slot name="navbar-end" />
186
- </div>
164
+ <HeaderEnd
165
+ navPosition={navPosition}
166
+ navItems={navItems}
167
+ activeLink={activeLink}
168
+ linkHeightClass={linkHeightClass}
169
+ showThemeSwitcher={showThemeSwitcher}
170
+ isI18nEnabled={isI18nEnabled}
171
+ astroI18n={astroI18n}
172
+ />
187
173
  </div>
188
174
  </header>
175
+
176
+ <!-- 移动端导航 Modal -->
177
+ {
178
+ navItems.length > 0 && (
179
+ <Modal id="mobile-nav" title="导航菜单">
180
+ <MobileNav
181
+ navItems={navItems}
182
+ activeLink={activeLink}
183
+ linkHeightClass={linkHeightClass}
184
+ />
185
+ </Modal>
186
+ )
187
+ }
@@ -0,0 +1,59 @@
1
+ ---
2
+ /**
3
+ * @component HeaderCenter
4
+ *
5
+ * @description
6
+ * HeaderCenter 组件负责渲染 Header 的中央区域,包括中央导航项。
7
+ * 这是一个内部组件,通常由 Header 组件使用。
8
+ *
9
+ * @usage
10
+ * ```astro
11
+ * <HeaderCenter
12
+ * navPosition={navPosition}
13
+ * navItems={navItems}
14
+ * activeLink={activeLink}
15
+ * linkHeightClass={linkHeightClass}
16
+ * gap={2}
17
+ * />
18
+ * ```
19
+ *
20
+ * @props
21
+ * - navPosition - 导航位置
22
+ * - navItems - 导航项数组
23
+ * - activeLink - 当前激活的链接
24
+ * - linkHeightClass - 链接高度类名
25
+ * - gap - 导航项之间的间距,可选值:0, 1, 2, 3, 4, 6, 8, 10, 12,默认 2
26
+ */
27
+ import { type INavItem } from '../../index-astro';
28
+ import NavItems from './NavItems.astro';
29
+
30
+ export interface Props {
31
+ navPosition: string;
32
+ navItems: INavItem[];
33
+ activeLink: string;
34
+ linkHeightClass: string;
35
+ gap?: number;
36
+ }
37
+
38
+ const {
39
+ navPosition,
40
+ navItems,
41
+ activeLink,
42
+ linkHeightClass,
43
+ gap = 2,
44
+ } = Astro.props;
45
+ ---
46
+
47
+ <div class="cosy:hidden cosy:lg:flex cosy:navbar-center">
48
+ {
49
+ navPosition === 'center' && (
50
+ <NavItems
51
+ navItems={navItems}
52
+ activeLink={activeLink}
53
+ linkHeightClass={linkHeightClass}
54
+ gap={gap}
55
+ />
56
+ )
57
+ }
58
+ <slot name="navbar-center" />
59
+ </div>