@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,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>
@@ -230,6 +230,12 @@ export interface Props {
230
230
  */
231
231
  basePath?: string;
232
232
 
233
+ /**
234
+ * Header 组件的高度
235
+ * @default "md"
236
+ */
237
+ headerHeight?: '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
238
+
233
239
  /**
234
240
  * 页脚相关配置
235
241
  */
@@ -376,6 +382,7 @@ const {
376
382
  languages,
377
383
  currentLocale,
378
384
  basePath = '',
385
+ headerHeight = 'md',
379
386
  footerHomeLink,
380
387
  footerSlogan,
381
388
  footerCompany,
@@ -406,6 +413,18 @@ const currentPath = Astro.url.pathname;
406
413
 
407
414
  // 获取有效的语言代码
408
415
  const validLang = getValidLanguage(currentLocale);
416
+
417
+ // 根据header高度设置侧边栏top值
418
+ const sidebarTopClasses = {
419
+ '3xs': 'cosy:top-4',
420
+ '2xs': 'cosy:top-6',
421
+ xs: 'cosy:top-8',
422
+ sm: 'cosy:top-10',
423
+ md: 'cosy:top-12',
424
+ lg: 'cosy:top-16',
425
+ xl: 'cosy:top-20',
426
+ };
427
+ const sidebarTopClass = sidebarTopClasses[headerHeight];
409
428
  ---
410
429
 
411
430
  <BaseLayout
@@ -430,14 +449,15 @@ const validLang = getValidLanguage(currentLocale);
430
449
  basePath={basePath}
431
450
  showSidebarToggle={true}
432
451
  defaultSidebarOpen={defaultSidebarOpen}
452
+ height={headerHeight}
433
453
  />
434
454
  )
435
455
  }
436
456
 
437
457
  <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">
458
+ <div class="cosy:flex cosy:flex-row cosy:flex-1 cosy:pb-0 cosy:min-h-screen">
439
459
  <!-- 侧边栏容器 -->
440
- <div class="cosy:top-16 cosy:z-10 cosy:sticky cosy:bg-base-100 cosy:h-screen">
460
+ <div class={`${sidebarTopClass} cosy:z-10 cosy:sticky cosy:bg-base-100 cosy:h-screen`}>
441
461
  <Sidebar
442
462
  sidebarItems={sidebarItems}
443
463
  currentPath={currentPath}
@@ -449,7 +469,8 @@ const validLang = getValidLanguage(currentLocale);
449
469
  <!-- 主内容区域 -->
450
470
  <Main class="cosy:lg:py-8 cosy:py-4 cosy:w-full">
451
471
  <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">
472
+ <div
473
+ class="cosy:flex cosy:lg:flex-row cosy:flex-col cosy:justify-center cosy:gap-8 cosy:pb-96">
453
474
  <Article class="cosy:flex-1 xl:cosy:w-[calc(100%-16rem)]">
454
475
  <slot />
455
476
  </Article>