@coffic/cosy-ui 0.3.39 → 0.3.48

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,163 +1,160 @@
1
1
  ---
2
2
  /**
3
3
  * @component CodeExample
4
- *
4
+ *
5
5
  * @description
6
6
  * 用于展示组件示例及其源代码的组件。
7
7
  * 采用标签形式,默认显示预览,用户可以点击标签切换到源代码视图。
8
8
  * 支持亮色和暗黑模式,会根据系统设置自动切换主题。
9
- *
9
+ *
10
10
  * @usage
11
11
  * ```astro
12
- * <CodeExample
13
- * title="示例标题"
12
+ * <CodeExample
13
+ * title="示例标题"
14
14
  * description="示例描述"
15
15
  * code={`<Alert type="info">这是一个示例</Alert>`}
16
16
  * >
17
17
  * <Alert type="info">这是一个示例</Alert>
18
18
  * </CodeExample>
19
19
  * ```
20
- *
20
+ *
21
21
  * @props
22
22
  * @prop {string} [title] - 示例标题
23
23
  * @prop {string} [description] - 示例描述
24
24
  * @prop {string} code - 要展示的源代码
25
- *
25
+ *
26
26
  * @slots
27
27
  * @slot default - 组件的实际渲染内容
28
28
  */
29
29
 
30
30
  // 导入图标
31
- import { ClipboardIcon, CheckIcon } from '../../index'
32
- import "../../app.css"
31
+ import { ClipboardIcon, CheckIcon } from '../../index';
32
+ import '../../app.css';
33
33
 
34
34
  interface Props {
35
- title?: string;
36
- description?: string;
37
- code: string;
35
+ title?: string;
36
+ description?: string;
37
+ code: string;
38
38
  }
39
39
 
40
40
  const { title, description, code } = Astro.props;
41
41
  ---
42
42
 
43
- <div class="cosy:card cosy:bg-base-100 cosy:shadow-lg cosy:mb-8 cosy:rounded-none code-example">
44
- {title && <h3 class="cosy:card-title cosy:p-4 cosy:border-b cosy:border-base-300">{title}</h3>}
45
- {description && <p class="cosy:px-4 cosy:pt-4 cosy:text-base-content/70">{description}</p>}
46
-
47
- <div class="cosy:flex cosy:justify-between cosy:items-center cosy:px-4 cosy:bg-base-200 cosy:rounded-none">
48
- <div role="tablist" class="cosy:tabs cosy:tabs-box">
49
- <button role="tab" class="cosy:tab cosy:tab-active" data-tab="preview">预览</button>
50
- <button role="tab" class="cosy:tab" data-tab="code">代码</button>
51
- </div>
52
- <div class="cosy:flex cosy:items-center cosy:gap-2">
53
- <button class="cosy:btn cosy:btn-ghost cosy:btn-sm cosy:gap-2" aria-label="复制代码">
54
- <span class="copy-icon"><ClipboardIcon /></span>
55
- <span class="check-icon cosy:hidden cosy:text-success"><CheckIcon /></span>
56
- <span class="copy-text">复制代码</span>
57
- </button>
58
- </div>
59
- </div>
60
-
61
- <div class="cosy:relative cosy:p-0">
62
- <div class="code-example-panel active" data-panel="preview">
63
- <div class="cosy:p-6 cosy:bg-base-100">
64
- <slot />
65
- </div>
66
- </div>
67
-
68
- <div class="code-example-panel" data-panel="code">
69
- <div class="cosy:bg-base-300 cosy:px-6 cosy:py-2">
70
- <pre class="cosy:overflow-x-auto"><code class="language-astro">{code}</code></pre>
71
- </div>
72
- </div>
73
- </div>
74
- </div>
43
+ <div
44
+ class="cosy:bg-base-100 cosy:shadow-lg cosy:mb-8 cosy:rounded-none cosy:card cosy:code-example">
45
+ {title && <h3 class="cosy:p-4 cosy:border-b cosy:border-base-300 cosy:card-title">{title}</h3>}
46
+ {description && <p class="cosy:px-4 cosy:pt-4 cosy:text-base-content/70">{description}</p>}
47
+
48
+ <div
49
+ class="cosy:flex cosy:justify-between cosy:items-center cosy:bg-base-200 cosy:px-4 cosy:rounded-none">
50
+ <div role="tablist" class="cosy:tabs cosy:tabs-box">
51
+ <button role="tab" class="cosy:tab cosy:tab-active" data-tab="preview">预览</button>
52
+ <button role="tab" class="cosy:tab" data-tab="code">代码</button>
53
+ </div>
54
+ <div class="cosy:flex cosy:items-center cosy:gap-2">
55
+ <button class="cosy:gap-2 cosy:btn cosy:btn-ghost cosy:btn-sm" aria-label="复制代码">
56
+ <span class="cosy:copy-icon"><ClipboardIcon /></span>
57
+ <span class="cosy:hidden cosy:text-success cosy:check-icon"><CheckIcon /></span>
58
+ <span class="cosy:copy-text">复制代码</span>
59
+ </button>
60
+ </div>
61
+ </div>
75
62
 
76
- <style>
77
- .code-example-panel {
78
- display: none;
79
- }
80
-
81
- .code-example-panel.active {
82
- display: block;
83
- }
84
- </style>
63
+ <div class="cosy:relative cosy:p-0">
64
+ <div class="cosy:block cosy:code-example-panel" data-panel="preview">
65
+ <div class="cosy:bg-base-100 cosy:p-6">
66
+ <slot />
67
+ </div>
68
+ </div>
69
+
70
+ <div class="cosy:hidden cosy:code-example-panel" data-panel="code">
71
+ <div class="cosy:bg-base-300 cosy:px-6 cosy:py-2">
72
+ <pre class="cosy:overflow-x-auto"><code class="cosy:language-astro">{code}</code></pre>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
85
77
 
86
78
  <script>
87
- function initializeCodeExample() {
88
- // 标签切换功能
89
- const tabs = document.querySelectorAll('.cosy\\:tab');
90
-
91
- tabs.forEach(tab => {
92
- tab.addEventListener('click', () => {
93
- // 获取当前标签组
94
- const tabGroup = tab.closest('.cosy\\:tabs');
95
- if (!tabGroup) return;
96
-
97
- const codeExample = tab.closest('.cosy\\:card');
98
- if (!codeExample) return;
99
-
100
- const contentContainer = codeExample.querySelector('.cosy\\:relative');
101
- if (!contentContainer) return;
102
-
103
- // 获取目标面板
104
- const targetTab = tab.getAttribute('data-tab');
105
- if (!targetTab) return;
106
-
107
- // 更新标签状态
108
- tabGroup.querySelectorAll('.cosy\\:tab').forEach(t => {
109
- t.classList.remove('cosy:tab-active');
110
- });
111
- tab.classList.add('cosy:tab-active');
112
-
113
- // 更新面板状态
114
- contentContainer.querySelectorAll('.code-example-panel').forEach(panel => {
115
- panel.classList.remove('active');
116
- });
117
-
118
- const targetPanel = contentContainer.querySelector(`[data-panel="${targetTab}"]`);
119
- if (targetPanel) {
120
- targetPanel.classList.add('active');
121
- }
122
- });
123
- });
124
-
125
- // 复制代码功能
126
- const copyButtons = document.querySelectorAll('.cosy\\:btn-ghost');
127
-
128
- copyButtons.forEach(button => {
129
- button.addEventListener('click', () => {
130
- const codeBlock = button.closest('.cosy\\:bg-base-300');
131
- if (!codeBlock) return;
132
-
133
- const codeElement = codeBlock.querySelector('code');
134
- if (!codeElement) return;
135
-
136
- const code = codeElement.textContent || '';
137
-
138
- // 复制到剪贴板
139
- navigator.clipboard.writeText(code).then(() => {
140
- // 显示复制成功状态
141
- button.classList.add('cosy:btn-success');
142
- button.querySelector('.copy-icon')?.classList.add('cosy:hidden');
143
- button.querySelector('.check-icon')?.classList.remove('cosy:hidden');
144
- button.querySelector('.copy-text')?.classList.add('cosy:text-success');
145
-
146
- // 3秒后恢复原状
147
- setTimeout(() => {
148
- button.classList.remove('cosy:btn-success');
149
- button.querySelector('.copy-icon')?.classList.remove('cosy:hidden');
150
- button.querySelector('.check-icon')?.classList.add('cosy:hidden');
151
- button.querySelector('.copy-text')?.classList.remove('cosy:text-success');
152
- }, 3000);
153
- });
154
- });
155
- });
156
- }
157
-
158
- // 初始化
159
- initializeCodeExample();
160
-
161
- // Astro 页面切换时重新初始化
162
- document.addEventListener('astro:page-load', initializeCodeExample);
163
- </script>
79
+ function initializeCodeExample() {
80
+ // 标签切换功能
81
+ const tabs = document.querySelectorAll('.cosy\\:tab');
82
+
83
+ tabs.forEach((tab) => {
84
+ tab.addEventListener('click', () => {
85
+ // 获取当前标签组
86
+ const tabGroup = tab.closest('.cosy\\:tabs');
87
+ if (!tabGroup) return;
88
+
89
+ const codeExample = tab.closest('.cosy\\:card');
90
+ if (!codeExample) return;
91
+
92
+ const contentContainer = codeExample.querySelector('.cosy\\:relative');
93
+ if (!contentContainer) return;
94
+
95
+ // 获取目标面板
96
+ const targetTab = tab.getAttribute('data-tab');
97
+ if (!targetTab) return;
98
+
99
+ // 更新标签状态
100
+ tabGroup.querySelectorAll('.cosy\\:tab').forEach((t) => {
101
+ t.classList.remove('cosy:tab-active');
102
+ });
103
+ tab.classList.add('cosy:tab-active');
104
+
105
+ // 更新面板状态
106
+ const panels = contentContainer.querySelectorAll('[data-panel]');
107
+ panels.forEach((panel) => {
108
+ if (panel.getAttribute('data-panel') === targetTab) {
109
+ panel.classList.remove('cosy:hidden');
110
+ panel.classList.add('cosy:block');
111
+ } else {
112
+ panel.classList.add('cosy:hidden');
113
+ panel.classList.remove('cosy:block');
114
+ }
115
+ });
116
+ });
117
+ });
118
+
119
+ // 复制代码功能
120
+ const copyButtons = document.querySelectorAll('.cosy\\:btn-ghost');
121
+
122
+ copyButtons.forEach((button) => {
123
+ button.addEventListener('click', () => {
124
+ const codeExample = button.closest('.cosy\\:card');
125
+ if (!codeExample) return;
126
+
127
+ const codePanel = codeExample.querySelector('[data-panel="code"]');
128
+ if (!codePanel) return;
129
+
130
+ const codeElement = codePanel.querySelector('code');
131
+ if (!codeElement) return;
132
+
133
+ const code = codeElement.textContent || '';
134
+
135
+ // 复制到剪贴板
136
+ navigator.clipboard.writeText(code).then(() => {
137
+ // 显示复制成功状态
138
+ button.classList.add('cosy:btn-success');
139
+ button.querySelector('.cosy\\:copy-icon')?.classList.add('cosy:hidden');
140
+ button.querySelector('.cosy\\:check-icon')?.classList.remove('cosy:hidden');
141
+ button.querySelector('.cosy\\:copy-text')?.classList.add('cosy:text-success');
142
+
143
+ // 3秒后恢复原状
144
+ setTimeout(() => {
145
+ button.classList.remove('cosy:btn-success');
146
+ button.querySelector('.cosy\\:copy-icon')?.classList.remove('cosy:hidden');
147
+ button.querySelector('.cosy\\:check-icon')?.classList.add('cosy:hidden');
148
+ button.querySelector('.cosy\\:copy-text')?.classList.remove('cosy:text-success');
149
+ }, 3000);
150
+ });
151
+ });
152
+ });
153
+ }
154
+
155
+ // 初始化
156
+ initializeCodeExample();
157
+
158
+ // Astro 页面切换时重新初始化
159
+ document.addEventListener('astro:page-load', initializeCodeExample);
160
+ </script>
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  /**
3
3
  * @component Hero
4
- *
4
+ *
5
5
  * @description
6
6
  * Hero 组件是一个全屏的展示区域,通常用于网站的首页或重要页面的顶部。
7
7
  * 它提供了一个引人注目的视觉区域,可以包含标题、描述、图片和行动按钮。
8
- *
8
+ *
9
9
  * @design
10
10
  * 设计理念:
11
11
  * 1. 视觉冲击力 - 全屏展示,吸引用户注意
12
12
  * 2. 内容聚焦 - 清晰地传达核心信息
13
13
  * 3. 引导行动 - 通过链接按钮引导用户进行下一步操作
14
14
  * 4. 灵活布局 - 支持图片、标题、描述和自定义内容
15
- *
15
+ *
16
16
  * @usage
17
17
  * 基本用法:
18
18
  * ```astro
@@ -25,7 +25,7 @@
25
25
  * ]}
26
26
  * />
27
27
  * ```
28
- *
28
+ *
29
29
  * 带图片的用法:
30
30
  * ```astro
31
31
  * <Hero
@@ -37,7 +37,7 @@
37
37
  * ]}
38
38
  * />
39
39
  * ```
40
- *
40
+ *
41
41
  * 带自定义内容的用法:
42
42
  * ```astro
43
43
  * <Hero
@@ -52,7 +52,7 @@
52
52
  * </div>
53
53
  * </Hero>
54
54
  * ```
55
- *
55
+ *
56
56
  * @props
57
57
  * @prop {string} title - Hero 区域的主标题
58
58
  * @prop {string} description - 标题下方的描述文本
@@ -62,11 +62,11 @@
62
62
  * @prop {Array<Link>} links - 链接按钮数组
63
63
  * @prop {string} links[].text - 链接按钮的文本
64
64
  * @prop {string} links[].href - 链接按钮的目标地址
65
+ * @prop {string} [background="gradient"] - 背景样式,可选值:"gradient", "plain"
66
+ * @prop {string} [align="center"] - 内容对齐方式,可选值:"center", "left", "right"
65
67
  */
66
68
 
67
- // 导入样式
68
69
  import '../../app.css';
69
-
70
70
  import Link from '../base/Link.astro';
71
71
 
72
72
  interface Link {
@@ -82,38 +82,118 @@ interface Props {
82
82
  alt: string;
83
83
  };
84
84
  links: Link[];
85
+ background?: 'gradient' | 'plain';
86
+ align?: 'center' | 'left' | 'right';
85
87
  }
86
88
 
87
- const { title, description, image, links = [] } = Astro.props;
88
- ---
89
+ const {
90
+ title,
91
+ description,
92
+ image,
93
+ links = [],
94
+ background = 'gradient',
95
+ align = 'center',
96
+ } = Astro.props;
89
97
 
90
- <div class="hero-container">
91
- <div class="hero-content">
92
- {image && <img src={image.src} alt={image.alt} class="hero-image" />}
98
+ const containerClasses = [
99
+ 'hero',
100
+ 'cosy:min-h-screen',
101
+ 'cosy:w-full',
102
+ background === 'gradient'
103
+ ? 'cosy:bg-gradient-to-br cosy:from-primary/10 cosy:to-secondary/20'
104
+ : 'cosy:bg-base-100',
105
+ 'cosy:py-16',
106
+ 'cosy:px-4',
107
+ 'cosy:sm:px-6',
108
+ 'cosy:lg:px-8',
109
+ ].join(' ');
93
110
 
94
- <h2 class="hero-title">{title}</h2>
95
- <p class="hero-description">
96
- {description}
97
- </p>
111
+ const contentClasses = [
112
+ 'hero-content',
113
+ 'cosy:flex',
114
+ 'cosy:flex-col',
115
+ align === 'center' ? 'cosy:text-center cosy:items-center' : '',
116
+ align === 'left' ? 'cosy:text-left cosy:items-start' : '',
117
+ align === 'right' ? 'cosy:text-right cosy:items-end' : '',
118
+ 'cosy:max-w-7xl',
119
+ 'cosy:mx-auto',
120
+ 'cosy:gap-10',
121
+ 'cosy:lg:flex-row',
122
+ 'cosy:lg:gap-16',
123
+ ].join(' ');
98
124
 
99
- <div class="hero-app">
100
- <slot name="app" />
101
- </div>
125
+ const titleClasses = [
126
+ 'cosy:text-5xl',
127
+ 'cosy:font-bold',
128
+ 'cosy:mb-4',
129
+ 'cosy:sm:text-6xl',
130
+ 'cosy:lg:text-7xl',
131
+ 'cosy:text-primary',
132
+ ].join(' ');
133
+
134
+ const descriptionClasses = ['cosy:text-xl', 'cosy:opacity-80', 'cosy:max-w-3xl', 'cosy:mb-8'].join(
135
+ ' '
136
+ );
137
+
138
+ const imageClasses = [
139
+ 'cosy:max-w-sm',
140
+ 'cosy:rounded-lg',
141
+ 'cosy:shadow-2xl',
142
+ 'cosy:lg:max-w-md',
143
+ 'cosy:transition-all',
144
+ 'cosy:duration-300',
145
+ 'cosy:hover:scale-105',
146
+ 'cosy:hover:shadow-primary/20',
147
+ ].join(' ');
148
+
149
+ const linksContainerClasses = [
150
+ 'cosy:flex',
151
+ 'cosy:flex-wrap',
152
+ 'cosy:gap-4',
153
+ align === 'center' ? 'cosy:justify-center' : '',
154
+ align === 'left' ? 'cosy:justify-start' : '',
155
+ align === 'right' ? 'cosy:justify-end' : '',
156
+ 'cosy:mt-4',
157
+ ].join(' ');
158
+
159
+ const appContainerClasses = ['cosy:w-full', 'cosy:max-w-3xl', 'cosy:mx-auto', 'cosy:my-8'].join(
160
+ ' '
161
+ );
162
+ ---
163
+
164
+ <div class={containerClasses}>
165
+ <div class={contentClasses}>
166
+ {
167
+ image && (
168
+ <div class="cosy:order-1 cosy:lg:order-2">
169
+ <img class={imageClasses} src={image.src} alt={image.alt} />
170
+ </div>
171
+ )
172
+ }
173
+
174
+ <div class="cosy:order-2 cosy:lg:order-1 cosy:flex cosy:flex-col">
175
+ <h2 class={titleClasses}>{title}</h2>
176
+ <p class={descriptionClasses}>
177
+ {description}
178
+ </p>
102
179
 
103
- <div class="hero-links">
104
180
  {
105
- links.map((link: Link) => (
106
- <Link
107
- href={link.href}
108
- external
109
- variant="cta"
110
- animation="hover-lift"
111
- size="lg"
112
- >
113
- {link.text}
114
- </Link>
115
- ))
181
+ Astro.slots.has('app') && (
182
+ <div class={appContainerClasses}>
183
+ <slot name="app" />
184
+ </div>
185
+ )
116
186
  }
187
+
188
+ <div class={linksContainerClasses}>
189
+ {
190
+ links.map((link: Link) => (
191
+ <Link href={link.href} external variant="cta" animation="hover-lift" size="lg">
192
+ {link.text}
193
+ </Link>
194
+ ))
195
+ }
196
+ </div>
117
197
  </div>
118
198
  </div>
119
199
  </div>
@@ -166,6 +166,12 @@ export interface Props {
166
166
  */
167
167
  sidebarItems: SidebarSection[];
168
168
 
169
+ /**
170
+ * 是否显示侧边栏
171
+ * @default true
172
+ */
173
+ showSidebar?: boolean;
174
+
169
175
  /**
170
176
  * 是否显示目录
171
177
  * @default true
@@ -230,6 +236,12 @@ export interface Props {
230
236
  */
231
237
  basePath?: string;
232
238
 
239
+ /**
240
+ * Header 组件的高度
241
+ * @default "md"
242
+ */
243
+ headerHeight?: '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
244
+
233
245
  /**
234
246
  * 页脚相关配置
235
247
  */
@@ -365,6 +377,7 @@ const {
365
377
  logo = DefaultLogo,
366
378
  logoHref = '/',
367
379
  sidebarItems,
380
+ showSidebar = true,
368
381
  showTableOfContents = true,
369
382
  showHeader = true,
370
383
  showFooter = true,
@@ -376,6 +389,7 @@ const {
376
389
  languages,
377
390
  currentLocale,
378
391
  basePath = '',
392
+ headerHeight = 'md',
379
393
  footerHomeLink,
380
394
  footerSlogan,
381
395
  footerCompany,
@@ -406,6 +420,18 @@ const currentPath = Astro.url.pathname;
406
420
 
407
421
  // 获取有效的语言代码
408
422
  const validLang = getValidLanguage(currentLocale);
423
+
424
+ // 根据header高度设置侧边栏top值
425
+ const sidebarTopClasses = {
426
+ '3xs': 'cosy:top-4',
427
+ '2xs': 'cosy:top-6',
428
+ xs: 'cosy:top-8',
429
+ sm: 'cosy:top-10',
430
+ md: 'cosy:top-12',
431
+ lg: 'cosy:top-16',
432
+ xl: 'cosy:top-20',
433
+ };
434
+ const sidebarTopClass = sidebarTopClasses[headerHeight];
409
435
  ---
410
436
 
411
437
  <BaseLayout
@@ -428,28 +454,35 @@ const validLang = getValidLanguage(currentLocale);
428
454
  currentLocale={currentLocale}
429
455
  sticky={true}
430
456
  basePath={basePath}
431
- showSidebarToggle={true}
457
+ showSidebarToggle={showSidebar && true}
432
458
  defaultSidebarOpen={defaultSidebarOpen}
459
+ height={headerHeight}
460
+ transition:persist
433
461
  />
434
462
  )
435
463
  }
436
464
 
437
465
  <div class="cosy:flex cosy:lg:flex-row cosy:flex-col cosy:flex-1">
438
- <div class="cosy:flex cosy:flex-row cosy:flex-1 cosy:min-h-screen cosy:pb-96">
466
+ <div class="cosy:flex cosy:flex-row cosy:flex-1 cosy:pb-0 cosy:min-h-screen">
439
467
  <!-- 侧边栏容器 -->
440
- <div class="cosy:top-16 cosy:z-10 cosy:sticky cosy:bg-base-100 cosy:h-screen">
441
- <Sidebar
442
- sidebarItems={sidebarItems}
443
- currentPath={currentPath}
444
- debug={false}
445
- class="cosy:lg:border-r cosy:border-b cosy:border-base-300 cosy:lg:border-b-0 cosy:lg:w-64 cosy:lg:shrink-0"
446
- />
447
- </div>
468
+ {
469
+ showSidebar && (
470
+ <div class={`${sidebarTopClass} cosy:z-10 cosy:sticky cosy:bg-base-100 cosy:h-screen`}>
471
+ <Sidebar
472
+ sidebarItems={sidebarItems}
473
+ currentPath={currentPath}
474
+ debug={false}
475
+ class="cosy:lg:border-r cosy:border-b cosy:border-base-300 cosy:lg:border-b-0 cosy:lg:w-64 cosy:lg:shrink-0"
476
+ />
477
+ </div>
478
+ )
479
+ }
448
480
 
449
481
  <!-- 主内容区域 -->
450
482
  <Main class="cosy:lg:py-8 cosy:py-4 cosy:w-full">
451
483
  <div class="cosy:mx-auto cosy:px-4 lg:cosy:px-8 cosy:container">
452
- <div class="cosy:flex cosy:lg:flex-row cosy:flex-col cosy:justify-center cosy:gap-8">
484
+ <div
485
+ class="cosy:flex cosy:lg:flex-row cosy:flex-col cosy:justify-center cosy:gap-8 cosy:pb-96">
453
486
  <Article class="cosy:flex-1 xl:cosy:w-[calc(100%-16rem)]">
454
487
  <slot />
455
488
  </Article>