@coffic/cosy-ui 0.3.12 → 0.3.33
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.
- package/README.md +25 -25
- package/dist/app.css +1 -1
- package/dist/components/base/Alert.astro +45 -140
- package/dist/components/base/Button.astro +61 -13
- package/dist/components/base/Image.astro +55 -16
- package/dist/components/base/Link.astro +14 -0
- package/dist/components/containers/Container.astro +35 -2
- package/dist/components/containers/Section.astro +23 -30
- package/dist/components/display/Modal.astro +14 -7
- package/dist/components/icons/MenuIcon.astro +5 -8
- package/dist/components/layouts/DocumentationLayout.astro +425 -359
- package/dist/components/layouts/Footer.astro +31 -77
- package/dist/components/layouts/Header.astro +17 -27
- package/dist/components/layouts/NavSection.astro +32 -0
- package/dist/components/layouts/Sidebar.astro +69 -92
- package/dist/components/layouts/SidebarNav.astro +122 -0
- package/dist/components/navigation/TableOfContents.astro +18 -50
- package/dist/index.ts +2 -0
- package/dist/types/layout.ts +10 -0
- package/package.json +2 -5
- package/dist/components/containers/index.ts +0 -3
@@ -146,11 +146,11 @@
|
|
146
146
|
*/
|
147
147
|
|
148
148
|
import { processSocialLink } from '../../utils/social';
|
149
|
-
import Link from '../base/Link.astro';
|
150
149
|
import SocialIcon from '../icons/SocialIcon.astro';
|
151
150
|
import type { FooterProps } from '../../types/footer';
|
152
|
-
import { getCurrentLanguage
|
151
|
+
import { getCurrentLanguage } from '../../utils/language';
|
153
152
|
import { createTextGetter } from '../../utils/i18n';
|
153
|
+
import NavSection from './NavSection.astro';
|
154
154
|
|
155
155
|
// 导入样式
|
156
156
|
import '../../app.css';
|
@@ -194,7 +194,6 @@ const {
|
|
194
194
|
techStackLink,
|
195
195
|
debug = false,
|
196
196
|
lang: userLang,
|
197
|
-
enableLogging = false,
|
198
197
|
}: Props = Astro.props;
|
199
198
|
|
200
199
|
// 获取当前语言
|
@@ -202,22 +201,6 @@ const langInfo = getCurrentLanguage(userLang);
|
|
202
201
|
// 创建文本获取函数
|
203
202
|
const t = createTextGetter(langInfo, 'footer');
|
204
203
|
|
205
|
-
// 获取语言来源的描述
|
206
|
-
function getLanguageSourceDescription(source: LanguageSource): string {
|
207
|
-
switch (source) {
|
208
|
-
case LanguageSource.USER:
|
209
|
-
return '用户指定';
|
210
|
-
case LanguageSource.URL:
|
211
|
-
return 'URL参数';
|
212
|
-
case LanguageSource.BROWSER:
|
213
|
-
return '浏览器设置';
|
214
|
-
case LanguageSource.DEFAULT:
|
215
|
-
return '默认语言';
|
216
|
-
default:
|
217
|
-
return '未知来源';
|
218
|
-
}
|
219
|
-
}
|
220
|
-
|
221
204
|
const currentYear = new Date().getFullYear();
|
222
205
|
|
223
206
|
// 处理社交链接
|
@@ -237,29 +220,29 @@ const debugClasses = debug ? {
|
|
237
220
|
};
|
238
221
|
---
|
239
222
|
|
240
|
-
<footer class:list={["cosy:footer cosy:sm:footer-horizontal cosy:bg-base-200 cosy:text-base-content cosy:p-10", debugClasses.footer]}>
|
241
|
-
<div class:list={["cosy:flex cosy:flex-col cosy:gap-8 cosy:container cosy:mx-auto cosy:items-center cosy:w-full cosy:md:flex-row cosy:md:justify-between", debugClasses.section]}>
|
223
|
+
<footer class:list={["cosy:footer cosy:z-50 cosy:sm:footer-horizontal cosy:bg-base-200 cosy:text-base-content cosy:p-10", debugClasses.footer]}>
|
224
|
+
<div class:list={["cosy:flex cosy:flex-col cosy:md:h-56 cosy:gap-8 cosy:container cosy:mx-auto cosy:items-center cosy:w-full cosy:md:flex-row cosy:md:justify-between", debugClasses.section]}>
|
242
225
|
{/* 品牌区域 */}
|
243
|
-
<aside class:list={["cosy:max-w-xs", debugClasses.aside]}>
|
244
|
-
<a href={homeLink} class="cosy:flex cosy:items-center cosy:gap-2 cosy:mb-4">
|
226
|
+
<aside class:list={["cosy:max-w-xs cosy:text-center", debugClasses.aside]}>
|
227
|
+
<a href={homeLink} class="cosy:flex cosy:items-center cosy:gap-2 cosy:mb-4 cosy:no-underline">
|
245
228
|
{logo && (
|
246
229
|
<img
|
247
230
|
src={logo.src}
|
248
231
|
alt={logo.alt}
|
249
|
-
class="cosy:
|
232
|
+
class="cosy:w-12 cosy:h-12 cosy:mask cosy:mask-squircle"
|
250
233
|
width="48"
|
251
234
|
height="48"
|
252
235
|
/>
|
253
236
|
)}
|
254
237
|
<div>
|
255
|
-
<h2 class="cosy:
|
238
|
+
<h2 class="cosy:font-bold cosy:text-xl">{siteName}</h2>
|
256
239
|
<p class="cosy:text-base-content/70">{slogan}</p>
|
257
240
|
</div>
|
258
241
|
</a>
|
259
242
|
|
260
243
|
{/* 社交媒体链接 */}
|
261
244
|
{processedSocialLinks.length > 0 && (
|
262
|
-
<div class="cosy:flex cosy:gap-2 cosy:mt-4">
|
245
|
+
<div class="cosy:flex cosy:justify-center cosy:gap-2 cosy:mt-4">
|
263
246
|
{processedSocialLinks.map((link) => (
|
264
247
|
<a
|
265
248
|
href={link.url}
|
@@ -276,49 +259,39 @@ const debugClasses = debug ? {
|
|
276
259
|
</aside>
|
277
260
|
|
278
261
|
{/* 导航区域 */}
|
279
|
-
<div class:list={["cosy:flex cosy:flex-col cosy:gap-8 cosy:mx-auto cosy:max-w-xl cosy:text-center cosy:items-center cosy:w-full cosy:md:flex-row cosy:md:justify-between cosy:md:items-start", debugClasses.section]}>
|
262
|
+
<div class:list={["cosy:flex cosy:h-full cosy:flex-col cosy:justify-center cosy:gap-8 cosy:mx-auto cosy:max-w-xl cosy:text-center cosy:items-center cosy:w-full cosy:md:flex-row cosy:md:justify-between cosy:md:items-start", debugClasses.section]}>
|
280
263
|
{/* 产品导航 */}
|
281
264
|
{products.length > 0 && (
|
282
|
-
<
|
283
|
-
<h6 class="cosy:footer-title">{t('products')}</h6>
|
284
|
-
{products.map((product) => (
|
285
|
-
<Link href={product.href} external={product.external} size='sm' block animation='hover-lift'>
|
286
|
-
{product.name}
|
287
|
-
</Link>
|
288
|
-
))}
|
289
|
-
</nav>
|
265
|
+
<NavSection title={t('products')} links={products} />
|
290
266
|
)}
|
291
267
|
|
292
268
|
{/* 关于导航 */}
|
293
269
|
{(aboutLink || contactLink || teamLink || careersLink) && (
|
294
|
-
<
|
295
|
-
|
296
|
-
{
|
297
|
-
{
|
298
|
-
{
|
299
|
-
|
300
|
-
</nav>
|
270
|
+
<NavSection title={t('about')} links={[
|
271
|
+
{ name: t('aboutUs'), href: aboutLink },
|
272
|
+
{ name: t('team'), href: teamLink },
|
273
|
+
{ name: t('careers'), href: careersLink },
|
274
|
+
{ name: t('contactUs'), href: contactLink }
|
275
|
+
].filter(link => link.href)} />
|
301
276
|
)}
|
302
277
|
|
303
278
|
{/* 资源导航 */}
|
304
279
|
{(newsLink || historyLink || partnersLink || blogLink || faqLink || mediaLink || techStackLink) && (
|
305
|
-
<
|
306
|
-
|
307
|
-
{
|
308
|
-
{
|
309
|
-
{
|
310
|
-
{
|
311
|
-
|
312
|
-
</nav>
|
280
|
+
<NavSection title={t('resources')} links={[
|
281
|
+
{ name: t('news'), href: newsLink },
|
282
|
+
{ name: t('blog'), href: blogLink },
|
283
|
+
{ name: t('faq'), href: faqLink },
|
284
|
+
{ name: t('history'), href: historyLink },
|
285
|
+
{ name: t('techStack'), href: techStackLink }
|
286
|
+
].filter(link => link.href)} />
|
313
287
|
)}
|
314
288
|
|
315
289
|
{/* 法律导航 */}
|
316
290
|
{(termsLink || privacyLink) && (
|
317
|
-
<
|
318
|
-
|
319
|
-
{
|
320
|
-
|
321
|
-
</nav>
|
291
|
+
<NavSection title={t('legal')} links={[
|
292
|
+
{ name: t('terms'), href: termsLink },
|
293
|
+
{ name: t('privacy'), href: privacyLink }
|
294
|
+
].filter(link => link.href)} />
|
322
295
|
)}
|
323
296
|
</div>
|
324
297
|
</div>
|
@@ -334,32 +307,13 @@ const debugClasses = debug ? {
|
|
334
307
|
{/* 底部版权信息 */}
|
335
308
|
<div class:list={["cosy:footer cosy:footer-center cosy:p-4 cosy:bg-base-300 cosy:text-base-content", debugClasses.footer]}>
|
336
309
|
<aside class:list={["cosy:items-center cosy:grid-flow-col", debugClasses.aside]}>
|
337
|
-
<p class="cosy:
|
310
|
+
<p class="cosy:opacity-70 cosy:text-sm">
|
338
311
|
© {currentYear} {company} - {copyright || t('allRightsReserved')}
|
339
312
|
</p>
|
340
313
|
{icp && (
|
341
|
-
<p class="cosy:
|
314
|
+
<p class="cosy:opacity-70 cosy:ml-4 cosy:text-sm">
|
342
315
|
{icp}
|
343
316
|
</p>
|
344
317
|
)}
|
345
318
|
</aside>
|
346
|
-
</div>
|
347
|
-
|
348
|
-
<script define:vars={{ langInfo, enableLogging }}>
|
349
|
-
// 输出语言信息到控制台
|
350
|
-
if (enableLogging) {
|
351
|
-
console.log(`[Footer] 语言信息:`, {
|
352
|
-
语言代码: langInfo.code,
|
353
|
-
语言来源: langInfo.source,
|
354
|
-
来源描述: (() => {
|
355
|
-
switch (langInfo.source) {
|
356
|
-
case 'user': return '用户指定';
|
357
|
-
case 'url': return 'URL参数';
|
358
|
-
case 'browser': return '浏览器设置';
|
359
|
-
case 'default': return '默认语言';
|
360
|
-
default: return '未知来源';
|
361
|
-
}
|
362
|
-
})()
|
363
|
-
});
|
364
|
-
}
|
365
|
-
</script>
|
319
|
+
</div>
|
@@ -1,7 +1,5 @@
|
|
1
1
|
---
|
2
|
-
import SearchIcon from '../icons/SearchIcon.astro';
|
3
2
|
import Link from '../base/Link.astro';
|
4
|
-
import Button from '../base/Button.astro';
|
5
3
|
import Image from '../base/Image.astro';
|
6
4
|
import '../../app.css';
|
7
5
|
|
@@ -42,6 +40,16 @@ interface Props {
|
|
42
40
|
* @default ""
|
43
41
|
*/
|
44
42
|
basePath?: string;
|
43
|
+
/**
|
44
|
+
* 是否显示侧边栏切换按钮
|
45
|
+
* @default false
|
46
|
+
*/
|
47
|
+
showSidebarToggle?: boolean;
|
48
|
+
/**
|
49
|
+
* 侧边栏是否默认展开
|
50
|
+
* @default false
|
51
|
+
*/
|
52
|
+
defaultSidebarOpen?: boolean;
|
45
53
|
}
|
46
54
|
|
47
55
|
const {
|
@@ -54,7 +62,7 @@ const {
|
|
54
62
|
{ code: 'en', name: 'English' }
|
55
63
|
],
|
56
64
|
currentLocale = 'zh-cn',
|
57
|
-
basePath = ''
|
65
|
+
basePath = '',
|
58
66
|
} = Astro.props;
|
59
67
|
|
60
68
|
type NavItem = { href: string; label: string; match: (path: string) => boolean };
|
@@ -77,36 +85,18 @@ function getLanguageUrl(langCode: string) {
|
|
77
85
|
---
|
78
86
|
|
79
87
|
<header class:list={[
|
80
|
-
"cosy:navbar cosy:bg-accent/
|
88
|
+
"cosy:navbar cosy:bg-accent/70 cosy:backdrop-blur cosy:border-base-200 cosy:z-50 cosy:w-full",
|
81
89
|
{ "cosy:fixed cosy:top-0": sticky }
|
82
90
|
]}>
|
83
91
|
<div class="cosy:navbar-start">
|
84
|
-
<!-- 移动端菜单 -->
|
85
|
-
<div class="cosy:dropdown cosy:lg:hidden">
|
86
|
-
<div tabindex="0" role="button" class="cosy:btn cosy:btn-ghost cosy:btn-circle">
|
87
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="cosy:h-5 cosy:w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
88
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7" />
|
89
|
-
</svg>
|
90
|
-
</div>
|
91
|
-
<ul tabindex="0" class="cosy:menu cosy:menu-sm cosy:dropdown-content cosy:mt-3 cosy:z-[1] cosy:p-2 cosy:shadow cosy:bg-base-100 cosy:rounded-box cosy:w-52">
|
92
|
-
{navItems.map((item: NavItem) => (
|
93
|
-
<li>
|
94
|
-
<Link href={item.href}>
|
95
|
-
{item.label}
|
96
|
-
</Link>
|
97
|
-
</li>
|
98
|
-
))}
|
99
|
-
</ul>
|
100
|
-
</div>
|
101
|
-
|
102
92
|
<Link href={logoHref} class="cosy:btn cosy:btn-ghost">
|
103
93
|
<Image src={logo} alt="logo" class="cosy:w-10 cosy:h-10" />
|
104
94
|
</Link>
|
105
95
|
</div>
|
106
96
|
|
107
|
-
<!--
|
108
|
-
<div class="cosy:
|
109
|
-
<ul class="cosy:
|
97
|
+
<!-- 导航 -->
|
98
|
+
<div class="cosy:hidden cosy:lg:flex cosy:navbar-center">
|
99
|
+
<ul class="cosy:px-1 cosy:menu cosy:menu-horizontal">
|
110
100
|
{navItems.map((item: NavItem) => (
|
111
101
|
<li>
|
112
102
|
<Link href={item.href}>
|
@@ -122,11 +112,11 @@ function getLanguageUrl(langCode: string) {
|
|
122
112
|
<div class="cosy:dropdown cosy:dropdown-end">
|
123
113
|
<div tabindex="0" role="button" class="cosy:btn cosy:btn-ghost cosy:btn-sm">
|
124
114
|
<span class="cosy:mr-1">{currentLocale === 'zh-cn' ? '中文' : 'English'}</span>
|
125
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="cosy:
|
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">
|
126
116
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
127
117
|
</svg>
|
128
118
|
</div>
|
129
|
-
<ul tabindex="0" class="cosy:
|
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">
|
130
120
|
{languages.map((lang) => (
|
131
121
|
<li class={currentLocale === lang.code ? "cosy:disabled" : ""}>
|
132
122
|
<a href={getLanguageUrl(lang.code)} class={currentLocale === lang.code ? "cosy:active" : ""}>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component NavSection
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* NavSection 组件用于在页脚中创建一个导航部分。
|
7
|
+
*
|
8
|
+
* @props
|
9
|
+
* @prop {string} title - 导航部分的标题
|
10
|
+
* @prop {Array<Object>} links - 链接数组,每个对象包含name、href和可选的external属性
|
11
|
+
*
|
12
|
+
*/
|
13
|
+
|
14
|
+
import Link from '../base/Link.astro';
|
15
|
+
|
16
|
+
interface NavSectionProps {
|
17
|
+
title: string;
|
18
|
+
links: Array<{ name: string; href: string; external?: boolean }>;
|
19
|
+
}
|
20
|
+
|
21
|
+
const props = Astro.props as NavSectionProps;
|
22
|
+
const { title, links } = props;
|
23
|
+
---
|
24
|
+
|
25
|
+
<nav class="cosy:flex cosy:flex-col cosy:items-center cosy:gap-3 cosy:hover:bg-base-300 cosy:hover:shadow-lg cosy:p-4 cosy:rounded cosy:h-full! cosy:hover:scale-105 cosy:transition-transform cosy:duration-300">
|
26
|
+
<h6 class="cosy:footer-title">{title}</h6>
|
27
|
+
{links.map((link) => (
|
28
|
+
<Link href={link.href} external={link.external} size='sm' block animation='hover-lift' centerText>
|
29
|
+
{link.name}
|
30
|
+
</Link>
|
31
|
+
))}
|
32
|
+
</nav>
|
@@ -1,14 +1,14 @@
|
|
1
1
|
---
|
2
2
|
/**
|
3
3
|
* Sidebar组件
|
4
|
-
*
|
4
|
+
*
|
5
5
|
* 用于文档页面的侧边栏导航
|
6
|
-
*
|
6
|
+
*
|
7
7
|
* @example
|
8
8
|
* ```astro
|
9
9
|
* ---
|
10
10
|
* import Sidebar from './Sidebar.astro';
|
11
|
-
*
|
11
|
+
*
|
12
12
|
* const sidebarItems = [
|
13
13
|
* { title: "入门", items: [
|
14
14
|
* { href: "/docs/getting-started", text: "快速开始" },
|
@@ -16,104 +16,81 @@
|
|
16
16
|
* ]}
|
17
17
|
* ];
|
18
18
|
* ---
|
19
|
-
*
|
19
|
+
*
|
20
20
|
* <Sidebar sidebarItems={sidebarItems} currentPath="/docs/getting-started" />
|
21
21
|
* ```
|
22
22
|
*/
|
23
23
|
|
24
24
|
import { isPathMatch } from '../../utils/path';
|
25
|
-
import
|
25
|
+
import Modal from '../display/Modal.astro';
|
26
|
+
import SidebarNav from './SidebarNav.astro';
|
27
|
+
import MenuIcon from '../icons/MenuIcon.astro';
|
28
|
+
import '../../app.css';
|
26
29
|
|
27
|
-
|
28
|
-
href: string;
|
29
|
-
text: string;
|
30
|
-
items?: SidebarItem[];
|
31
|
-
}
|
32
|
-
|
33
|
-
export interface SidebarSection {
|
34
|
-
title: string;
|
35
|
-
items: SidebarItem[];
|
36
|
-
}
|
30
|
+
import type { SidebarSection } from '../../types/layout';
|
37
31
|
|
38
32
|
export interface Props {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
33
|
+
/**
|
34
|
+
* 侧边栏项目
|
35
|
+
*/
|
36
|
+
sidebarItems: SidebarSection[];
|
37
|
+
|
38
|
+
/**
|
39
|
+
* 当前路径
|
40
|
+
*/
|
41
|
+
currentPath: string;
|
42
|
+
|
43
|
+
/**
|
44
|
+
* 桌面端类名
|
45
|
+
*/
|
46
|
+
class?: string;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* 是否开启调试模式,显示边框
|
50
|
+
* @default false
|
51
|
+
*/
|
52
|
+
debug?: boolean;
|
48
53
|
}
|
49
54
|
|
50
|
-
const { sidebarItems, currentPath } = Astro.props;
|
55
|
+
const { sidebarItems, currentPath, class: className, debug = false } = Astro.props;
|
56
|
+
|
57
|
+
const debugClass = debug ? 'cosy:border cosy:border-red-500' : '';
|
58
|
+
|
59
|
+
// 获取当前活动的一级导航项
|
60
|
+
const currentSection = sidebarItems.find((section) =>
|
61
|
+
section.items.some((item) => isPathMatch(currentPath, item.href))
|
62
|
+
);
|
51
63
|
---
|
52
64
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
{subitem.text}
|
86
|
-
</a>
|
87
|
-
{subitem.items && (
|
88
|
-
<ul>
|
89
|
-
{subitem.items.map((subsubitem: SidebarItem) => {
|
90
|
-
const isSubSubActive = isPathMatch(currentPath, subsubitem.href);
|
91
|
-
return (
|
92
|
-
<li>
|
93
|
-
<a
|
94
|
-
href={subsubitem.href}
|
95
|
-
class:list={[
|
96
|
-
"cosy:hover:bg-base-300",
|
97
|
-
{ "cosy:active": isSubSubActive }
|
98
|
-
]}
|
99
|
-
>
|
100
|
-
{subsubitem.text}
|
101
|
-
</a>
|
102
|
-
</li>
|
103
|
-
);
|
104
|
-
})}
|
105
|
-
</ul>
|
106
|
-
)}
|
107
|
-
</li>
|
108
|
-
);
|
109
|
-
})}
|
110
|
-
</ul>
|
111
|
-
)}
|
112
|
-
</li>
|
113
|
-
);
|
114
|
-
})}
|
115
|
-
</ul>
|
116
|
-
</div>
|
117
|
-
))}
|
118
|
-
</nav>
|
119
|
-
</aside>
|
65
|
+
{/* 移动端导航栏 */}
|
66
|
+
<div
|
67
|
+
class:list={[
|
68
|
+
'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',
|
69
|
+
debugClass,
|
70
|
+
]}>
|
71
|
+
<div class="cosy:flex cosy:items-center cosy:gap-2">
|
72
|
+
<button
|
73
|
+
type="button"
|
74
|
+
class="cosy:p-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
|
75
|
+
data-modal-target="mobile-sidebar">
|
76
|
+
<MenuIcon class="cosy:w-5 cosy:h-5" />
|
77
|
+
</button>
|
78
|
+
<span class="cosy:font-medium cosy:text-sm">
|
79
|
+
{currentSection?.title || '导航'}
|
80
|
+
</span>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
{/* 移动端侧边栏弹出层 */}
|
85
|
+
<Modal id="mobile-sidebar" class="cosy:mx-4 cosy:lg:w-80 cosy:w-[calc(100vw-2rem)] cosy:max-w-full">
|
86
|
+
<div class="cosy:h-[calc(100vh-8rem)] cosy:overflow-y-auto">
|
87
|
+
<SidebarNav sidebarItems={sidebarItems} currentPath={currentPath} debug={debug} />
|
88
|
+
</div>
|
89
|
+
</Modal>
|
90
|
+
|
91
|
+
{/* 桌面端侧边栏 */}
|
92
|
+
<aside class:list={[className, debugClass, 'cosy:hidden cosy:lg:block']}>
|
93
|
+
<div class="cosy:top-16 cosy:sticky cosy:h-[calc(100vh-4rem)]">
|
94
|
+
<SidebarNav sidebarItems={sidebarItems} currentPath={currentPath} debug={debug} />
|
95
|
+
</div>
|
96
|
+
</aside>
|
@@ -0,0 +1,122 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* SidebarNav组件
|
4
|
+
*
|
5
|
+
* 用于渲染侧边栏的导航内容
|
6
|
+
*/
|
7
|
+
|
8
|
+
import { isPathMatch } from '../../utils/path';
|
9
|
+
import "../../app.css"
|
10
|
+
|
11
|
+
export interface SidebarItem {
|
12
|
+
href: string;
|
13
|
+
text: string;
|
14
|
+
items?: SidebarItem[];
|
15
|
+
}
|
16
|
+
|
17
|
+
export interface SidebarSection {
|
18
|
+
title: string;
|
19
|
+
items: SidebarItem[];
|
20
|
+
}
|
21
|
+
|
22
|
+
interface Props {
|
23
|
+
/**
|
24
|
+
* 侧边栏项目
|
25
|
+
*/
|
26
|
+
sidebarItems: SidebarSection[];
|
27
|
+
|
28
|
+
/**
|
29
|
+
* 当前路径
|
30
|
+
*/
|
31
|
+
currentPath: string;
|
32
|
+
|
33
|
+
/**
|
34
|
+
* 是否开启调试模式,显示边框
|
35
|
+
* @default false
|
36
|
+
*/
|
37
|
+
debug?: boolean;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* 自定义类名
|
41
|
+
*/
|
42
|
+
class?: string;
|
43
|
+
}
|
44
|
+
|
45
|
+
const {
|
46
|
+
sidebarItems,
|
47
|
+
currentPath,
|
48
|
+
debug = false,
|
49
|
+
class: className
|
50
|
+
} = Astro.props;
|
51
|
+
|
52
|
+
const debugClass = debug ? "cosy:border cosy:border-red-500" : "";
|
53
|
+
---
|
54
|
+
|
55
|
+
<nav class:list={["cosy:p-4", debugClass, className]}>
|
56
|
+
{sidebarItems.map((section: SidebarSection) => (
|
57
|
+
<div class:list={["cosy:mb-6", debugClass]}>
|
58
|
+
<h3 class:list={["cosy:font-bold cosy:mb-2 cosy:text-base-content/70", debugClass]}>{section.title}</h3>
|
59
|
+
<ul class:list={["cosy:menu cosy:bg-base-200 cosy:rounded-box cosy:w-56", debugClass]}>
|
60
|
+
{section.items.map((item: SidebarItem) => {
|
61
|
+
const isActive = isPathMatch(currentPath, item.href);
|
62
|
+
return (
|
63
|
+
<li class:list={[debugClass]}>
|
64
|
+
<a
|
65
|
+
href={item.href}
|
66
|
+
class:list={[
|
67
|
+
"cosy:hover:bg-base-300",
|
68
|
+
{ "cosy:menu-active": isActive },
|
69
|
+
debugClass
|
70
|
+
]}
|
71
|
+
>
|
72
|
+
{item.text}
|
73
|
+
</a>
|
74
|
+
{item.items && (
|
75
|
+
<ul class:list={[debugClass]}>
|
76
|
+
{item.items.map((subitem: SidebarItem) => {
|
77
|
+
const isSubActive = isPathMatch(currentPath, subitem.href);
|
78
|
+
return (
|
79
|
+
<li class:list={[debugClass]}>
|
80
|
+
<a
|
81
|
+
href={subitem.href}
|
82
|
+
class:list={[
|
83
|
+
"cosy:hover:bg-base-300",
|
84
|
+
{ "cosy:active": isSubActive },
|
85
|
+
debugClass
|
86
|
+
]}
|
87
|
+
>
|
88
|
+
{subitem.text}
|
89
|
+
</a>
|
90
|
+
{subitem.items && (
|
91
|
+
<ul class:list={[debugClass]}>
|
92
|
+
{subitem.items.map((subsubitem: SidebarItem) => {
|
93
|
+
const isSubSubActive = isPathMatch(currentPath, subsubitem.href);
|
94
|
+
return (
|
95
|
+
<li class:list={[debugClass]}>
|
96
|
+
<a
|
97
|
+
href={subsubitem.href}
|
98
|
+
class:list={[
|
99
|
+
"cosy:hover:bg-base-300",
|
100
|
+
{ "cosy:active": isSubSubActive },
|
101
|
+
debugClass
|
102
|
+
]}
|
103
|
+
>
|
104
|
+
{subsubitem.text}
|
105
|
+
</a>
|
106
|
+
</li>
|
107
|
+
);
|
108
|
+
})}
|
109
|
+
</ul>
|
110
|
+
)}
|
111
|
+
</li>
|
112
|
+
);
|
113
|
+
})}
|
114
|
+
</ul>
|
115
|
+
)}
|
116
|
+
</li>
|
117
|
+
);
|
118
|
+
})}
|
119
|
+
</ul>
|
120
|
+
</div>
|
121
|
+
))}
|
122
|
+
</nav>
|