@coffic/cosy-ui 0.3.48 → 0.3.53
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/dist/app.css +1 -1
- package/dist/components/containers/Main.astro +103 -108
- package/dist/components/data-display/ProductCard.astro +318 -0
- package/dist/components/layouts/DocumentationLayout.astro +1 -0
- package/dist/components/layouts/SidebarNav.astro +96 -100
- package/dist/index.ts +1 -0
- package/dist/utils/path.ts +33 -5
- package/package.json +3 -2
@@ -1,45 +1,45 @@
|
|
1
1
|
---
|
2
2
|
/**
|
3
3
|
* @component Main
|
4
|
-
*
|
4
|
+
*
|
5
5
|
* @description
|
6
6
|
* Main 组件是页面的主要内容区域,使用 Container 组件提供响应式布局。
|
7
7
|
* 它适合作为页面的主体部分,自动处理不同屏幕尺寸下的布局和内边距。
|
8
|
-
*
|
8
|
+
*
|
9
9
|
* @design
|
10
10
|
* 设计理念:
|
11
11
|
* 1. 结构清晰 - 明确表示页面的主要内容区域,提高语义化
|
12
12
|
* 2. 响应式布局 - 在不同屏幕尺寸下自动调整内边距和宽度
|
13
13
|
* 3. 灵活配置 - 支持自定义容器尺寸、内边距和背景颜色
|
14
14
|
* 4. 与容器集成 - 无缝集成 Container 组件,保持布局一致性
|
15
|
-
*
|
15
|
+
*
|
16
16
|
* @usage
|
17
17
|
* 基本用法:
|
18
18
|
* ```astro
|
19
19
|
* ---
|
20
20
|
* import { Main } from '@coffic/cosy-ui';
|
21
21
|
* ---
|
22
|
-
*
|
22
|
+
*
|
23
23
|
* <Main>
|
24
24
|
* <h1>页面标题</h1>
|
25
25
|
* <p>页面内容</p>
|
26
26
|
* </Main>
|
27
27
|
* ```
|
28
|
-
*
|
28
|
+
*
|
29
29
|
* 自定义容器尺寸:
|
30
30
|
* ```astro
|
31
31
|
* <Main size="lg">
|
32
32
|
* <p>这是一个较宽的主内容区域</p>
|
33
33
|
* </Main>
|
34
34
|
* ```
|
35
|
-
*
|
35
|
+
*
|
36
36
|
* 自定义内边距:
|
37
37
|
* ```astro
|
38
38
|
* <Main verticalPadding="lg" padding="xl">
|
39
39
|
* <p>这个主内容区域有更大的内边距</p>
|
40
40
|
* </Main>
|
41
41
|
* ```
|
42
|
-
*
|
42
|
+
*
|
43
43
|
* 自定义背景颜色:
|
44
44
|
* ```astro
|
45
45
|
* <Main backgroundColor="primary">
|
@@ -54,114 +54,109 @@ import Container from './Container.astro';
|
|
54
54
|
import '../../app.css';
|
55
55
|
|
56
56
|
export interface Props {
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
57
|
+
/**
|
58
|
+
* 容器大小
|
59
|
+
* @default "md"
|
60
|
+
*/
|
61
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
62
|
+
|
63
|
+
/**
|
64
|
+
* 水平内边距(通过 Container 组件的 padding 属性设置)
|
65
|
+
* @default "md"
|
66
|
+
*/
|
67
|
+
padding?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
68
|
+
|
69
|
+
/**
|
70
|
+
* 垂直内边距
|
71
|
+
* @default "md"
|
72
|
+
*/
|
73
|
+
verticalPadding?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | string;
|
74
|
+
|
75
|
+
/**
|
76
|
+
* 是否居中显示内容
|
77
|
+
* @default true
|
78
|
+
*/
|
79
|
+
centered?: boolean;
|
80
|
+
|
81
|
+
/**
|
82
|
+
* 背景颜色
|
83
|
+
* @default undefined
|
84
|
+
*/
|
85
|
+
backgroundColor?: 'primary' | 'secondary' | 'tertiary' | 'light' | 'dark' | string;
|
86
|
+
|
87
|
+
/**
|
88
|
+
* HTML id 属性
|
89
|
+
*/
|
90
|
+
id?: string;
|
91
|
+
|
92
|
+
/**
|
93
|
+
* 类名
|
94
|
+
*/
|
95
|
+
class?: string;
|
96
|
+
|
97
|
+
/**
|
98
|
+
* 类名列表
|
99
|
+
*/
|
100
|
+
'class:list'?: any;
|
101
101
|
}
|
102
102
|
|
103
103
|
const {
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
104
|
+
size = 'md',
|
105
|
+
padding = 'md',
|
106
|
+
verticalPadding = 'md',
|
107
|
+
centered = true,
|
108
|
+
backgroundColor,
|
109
|
+
id,
|
110
|
+
class: className,
|
111
|
+
'class:list': classList,
|
112
|
+
...rest
|
113
113
|
} = Astro.props;
|
114
114
|
|
115
|
-
//
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
115
|
+
// 获取垂直内边距的Tailwind类
|
116
|
+
function getVerticalPaddingClasses(padding: string) {
|
117
|
+
if (padding === 'none') return 'cosy:py-0';
|
118
|
+
if (padding === 'sm') return 'cosy:py-2';
|
119
|
+
if (padding === 'md') return 'cosy:py-4';
|
120
|
+
if (padding === 'lg') return 'cosy:py-6';
|
121
|
+
if (padding === 'xl') return 'cosy:py-8';
|
122
|
+
return ''; // 对于自定义padding,使用内联样式
|
123
|
+
}
|
124
|
+
|
125
|
+
// 获取背景颜色的Tailwind类
|
126
|
+
function getBgColorClasses(color: string | undefined) {
|
127
|
+
if (!color) return '';
|
128
|
+
if (color === 'primary') return 'cosy:bg-primary';
|
129
|
+
if (color === 'secondary') return 'cosy:bg-secondary';
|
130
|
+
if (color === 'tertiary') return 'cosy:bg-accent';
|
131
|
+
if (color === 'light') return 'cosy:bg-base-100';
|
132
|
+
if (color === 'dark') return 'cosy:bg-base-300';
|
133
|
+
return ''; // 对于自定义颜色,使用内联样式
|
134
|
+
}
|
135
|
+
|
136
|
+
const verticalPaddingClass = getVerticalPaddingClasses(verticalPadding as string);
|
137
|
+
const bgColorClass = getBgColorClasses(backgroundColor);
|
130
138
|
|
131
139
|
// 构建自定义样式
|
132
|
-
const customStyle =
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
: undefined;
|
140
|
+
const customStyle = [];
|
141
|
+
|
142
|
+
// 只有当没有对应的Tailwind类时,才使用内联样式
|
143
|
+
if (!verticalPaddingClass && verticalPadding && verticalPadding !== 'none') {
|
144
|
+
customStyle.push(`padding-top: ${verticalPadding}; padding-bottom: ${verticalPadding};`);
|
145
|
+
}
|
146
|
+
|
147
|
+
if (!bgColorClass && backgroundColor) {
|
148
|
+
customStyle.push(`background-color: ${backgroundColor};`);
|
149
|
+
}
|
150
|
+
|
151
|
+
const inlineStyle = customStyle.length > 0 ? customStyle.join(' ') : undefined;
|
145
152
|
---
|
146
153
|
|
147
154
|
<main
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
style={inlineStyle}
|
157
|
-
{...rest}
|
158
|
-
>
|
159
|
-
<Container
|
160
|
-
size={size}
|
161
|
-
padding={padding}
|
162
|
-
centered={centered}
|
163
|
-
class="main-container"
|
164
|
-
>
|
165
|
-
<slot />
|
166
|
-
</Container>
|
167
|
-
</main>
|
155
|
+
id={id}
|
156
|
+
class:list={['cosy:w-full', verticalPaddingClass, bgColorClass, className, classList]}
|
157
|
+
style={inlineStyle}
|
158
|
+
{...rest}>
|
159
|
+
<Container size={size} padding={padding} centered={centered}>
|
160
|
+
<slot />
|
161
|
+
</Container>
|
162
|
+
</main>
|
@@ -0,0 +1,318 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component ProductCard
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* ProductCard 组件用于展示产品信息,包括产品名称、图片、描述、App Store链接和产品官网链接。
|
7
|
+
* 组件采用卡片式设计,支持悬停效果,并提供链接到产品相关页面的功能。
|
8
|
+
* 支持多种尺寸选项:xs, sm, md, lg, xl
|
9
|
+
*
|
10
|
+
* @design
|
11
|
+
* 设计理念:
|
12
|
+
* 1. 产品展示 - 突出展示产品的关键信息和图片
|
13
|
+
* 2. 链接跳转 - 提供App Store和产品官网链接
|
14
|
+
* 3. 视觉一致性 - 使用卡片组件确保与整体设计风格一致
|
15
|
+
* 4. 交互反馈 - 悬停时提供视觉反馈,增强用户体验
|
16
|
+
* 5. 尺寸灵活 - 提供多种预设尺寸,适应不同场景需求
|
17
|
+
*
|
18
|
+
* @usage
|
19
|
+
* 基本用法:
|
20
|
+
* ```astro
|
21
|
+
* <ProductCard
|
22
|
+
* name="产品名称"
|
23
|
+
* image="/images/products/product1.jpg"
|
24
|
+
* description="产品简短描述文本"
|
25
|
+
* productUrl="https://product-website.com"
|
26
|
+
* />
|
27
|
+
* ```
|
28
|
+
*
|
29
|
+
* 包含App Store链接:
|
30
|
+
* ```astro
|
31
|
+
* <ProductCard
|
32
|
+
* name="产品名称"
|
33
|
+
* image="/images/products/product1.jpg"
|
34
|
+
* description="产品简短描述文本"
|
35
|
+
* appStoreUrl="https://apps.apple.com/app/product"
|
36
|
+
* productUrl="https://product-website.com"
|
37
|
+
* />
|
38
|
+
* ```
|
39
|
+
*
|
40
|
+
* 使用不同尺寸:
|
41
|
+
* ```astro
|
42
|
+
* <ProductCard
|
43
|
+
* size="sm"
|
44
|
+
* name="小尺寸产品卡片"
|
45
|
+
* image="/images/products/product1.jpg"
|
46
|
+
* description="产品简短描述文本"
|
47
|
+
* />
|
48
|
+
* ```
|
49
|
+
*/
|
50
|
+
|
51
|
+
import Link from '../base/Link.astro';
|
52
|
+
import Image from '../base/Image.astro';
|
53
|
+
import SocialIcon from '../icons/SocialIcon.astro';
|
54
|
+
|
55
|
+
// 导入样式
|
56
|
+
import '../../app.css';
|
57
|
+
|
58
|
+
// 自定义图片元数据接口,替代 astro 的 ImageMetadata
|
59
|
+
interface CustomImageMetadata {
|
60
|
+
src: string;
|
61
|
+
width: number;
|
62
|
+
height: number;
|
63
|
+
format: string;
|
64
|
+
}
|
65
|
+
|
66
|
+
type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
67
|
+
|
68
|
+
export interface Props {
|
69
|
+
/**
|
70
|
+
* 产品名称
|
71
|
+
*/
|
72
|
+
name: string;
|
73
|
+
/**
|
74
|
+
* 产品图片
|
75
|
+
*/
|
76
|
+
image: CustomImageMetadata | string;
|
77
|
+
/**
|
78
|
+
* 产品描述
|
79
|
+
*/
|
80
|
+
description: string;
|
81
|
+
/**
|
82
|
+
* App Store链接
|
83
|
+
*/
|
84
|
+
appStoreUrl?: string;
|
85
|
+
/**
|
86
|
+
* 产品官网链接
|
87
|
+
*/
|
88
|
+
productUrl?: string;
|
89
|
+
/**
|
90
|
+
* GitHub仓库链接
|
91
|
+
*/
|
92
|
+
githubUrl?: string;
|
93
|
+
/**
|
94
|
+
* 卡片尺寸
|
95
|
+
* - xs: 超小尺寸,适合密集布局
|
96
|
+
* - sm: 小尺寸,适合列表
|
97
|
+
* - md: 中等尺寸,默认
|
98
|
+
* - lg: 大尺寸,适合突出显示
|
99
|
+
* - xl: 超大尺寸,适合特色产品
|
100
|
+
*/
|
101
|
+
size?: Size;
|
102
|
+
/**
|
103
|
+
* 主按钮文本(产品官网按钮)
|
104
|
+
*/
|
105
|
+
primaryButtonText?: string;
|
106
|
+
/**
|
107
|
+
* 次按钮文本(App Store按钮)
|
108
|
+
*/
|
109
|
+
secondaryButtonText?: string;
|
110
|
+
/**
|
111
|
+
* GitHub按钮文本
|
112
|
+
*/
|
113
|
+
githubButtonText?: string;
|
114
|
+
/**
|
115
|
+
* 按钮布局方向
|
116
|
+
* - row: 水平布局(默认)
|
117
|
+
* - column: 垂直布局
|
118
|
+
*/
|
119
|
+
buttonLayout?: 'row' | 'column';
|
120
|
+
/**
|
121
|
+
* 自定义类名
|
122
|
+
*/
|
123
|
+
class?: string;
|
124
|
+
}
|
125
|
+
|
126
|
+
const {
|
127
|
+
name,
|
128
|
+
image,
|
129
|
+
description,
|
130
|
+
appStoreUrl,
|
131
|
+
productUrl,
|
132
|
+
githubUrl,
|
133
|
+
size = 'md',
|
134
|
+
primaryButtonText = '访问官网',
|
135
|
+
secondaryButtonText = 'App Store',
|
136
|
+
githubButtonText = 'GitHub',
|
137
|
+
buttonLayout = 'row',
|
138
|
+
class: className = '',
|
139
|
+
} = Astro.props;
|
140
|
+
|
141
|
+
// 尺寸样式映射
|
142
|
+
const sizeStyles = {
|
143
|
+
xs: {
|
144
|
+
card: 'cosy:max-w-[200px]',
|
145
|
+
figure: 'cosy:p-2',
|
146
|
+
image: {
|
147
|
+
width: 150,
|
148
|
+
height: 100,
|
149
|
+
},
|
150
|
+
title: 'cosy:text-sm',
|
151
|
+
description: 'cosy:text-xs cosy:line-clamp-2',
|
152
|
+
buttons: 'cosy:text-xs cosy:btn-xs',
|
153
|
+
padding: 'cosy:px-3 cosy:py-2',
|
154
|
+
},
|
155
|
+
sm: {
|
156
|
+
card: 'cosy:max-w-[250px]',
|
157
|
+
figure: 'cosy:p-3',
|
158
|
+
image: {
|
159
|
+
width: 200,
|
160
|
+
height: 133,
|
161
|
+
},
|
162
|
+
title: 'cosy:text-base',
|
163
|
+
description: 'cosy:text-sm cosy:line-clamp-3',
|
164
|
+
buttons: 'cosy:text-sm cosy:btn-sm',
|
165
|
+
padding: 'cosy:px-4 cosy:py-3',
|
166
|
+
},
|
167
|
+
md: {
|
168
|
+
card: 'cosy:max-w-[320px]',
|
169
|
+
figure: 'cosy:p-4',
|
170
|
+
image: {
|
171
|
+
width: 300,
|
172
|
+
height: 200,
|
173
|
+
},
|
174
|
+
title: 'cosy:text-xl',
|
175
|
+
description: 'cosy:text-base',
|
176
|
+
buttons: '',
|
177
|
+
padding: 'cosy:px-6 cosy:py-4',
|
178
|
+
},
|
179
|
+
lg: {
|
180
|
+
card: 'cosy:max-w-[400px]',
|
181
|
+
figure: 'cosy:p-5',
|
182
|
+
image: {
|
183
|
+
width: 380,
|
184
|
+
height: 253,
|
185
|
+
},
|
186
|
+
title: 'cosy:text-2xl',
|
187
|
+
description: 'cosy:text-lg',
|
188
|
+
buttons: 'cosy:text-base cosy:btn-lg',
|
189
|
+
padding: 'cosy:px-7 cosy:py-5',
|
190
|
+
},
|
191
|
+
xl: {
|
192
|
+
card: 'cosy:max-w-[500px]',
|
193
|
+
figure: 'cosy:p-6',
|
194
|
+
image: {
|
195
|
+
width: 480,
|
196
|
+
height: 320,
|
197
|
+
},
|
198
|
+
title: 'cosy:text-3xl',
|
199
|
+
description: 'cosy:text-xl',
|
200
|
+
buttons: 'cosy:text-lg cosy:btn-lg',
|
201
|
+
padding: 'cosy:px-8 cosy:py-6',
|
202
|
+
},
|
203
|
+
};
|
204
|
+
|
205
|
+
const currentSize = sizeStyles[size];
|
206
|
+
|
207
|
+
// 计算按钮布局类名,根据按钮数量和布局方向调整
|
208
|
+
const getButtonLayoutClass = () => {
|
209
|
+
let count = 0;
|
210
|
+
if (productUrl) count++;
|
211
|
+
if (appStoreUrl) count++;
|
212
|
+
if (githubUrl) count++;
|
213
|
+
|
214
|
+
// 垂直布局时所有按钮都是全宽
|
215
|
+
if (buttonLayout === 'column') {
|
216
|
+
return 'cosy:w-full';
|
217
|
+
}
|
218
|
+
|
219
|
+
// 水平布局时根据按钮数量选择合适的布局类
|
220
|
+
switch (count) {
|
221
|
+
case 1:
|
222
|
+
return 'cosy:w-full'; // 单个按钮占满一行
|
223
|
+
case 2:
|
224
|
+
return 'cosy:flex-1'; // 两个按钮平分
|
225
|
+
case 3:
|
226
|
+
return 'cosy:flex-1'; // 三个按钮平分
|
227
|
+
default:
|
228
|
+
return '';
|
229
|
+
}
|
230
|
+
};
|
231
|
+
|
232
|
+
const buttonLayoutClass = getButtonLayoutClass();
|
233
|
+
const buttonsContainerClass =
|
234
|
+
buttonLayout === 'column'
|
235
|
+
? 'cosy:flex cosy:flex-col cosy:gap-2 cosy:mt-auto'
|
236
|
+
: 'cosy:flex cosy:flex-wrap cosy:gap-2 cosy:mt-auto';
|
237
|
+
---
|
238
|
+
|
239
|
+
<div
|
240
|
+
class:list={[
|
241
|
+
'cosy:card cosy:bg-base-100 cosy:shadow-md cosy:hover:shadow-lg cosy:transition-shadow cosy:duration-300',
|
242
|
+
currentSize.card,
|
243
|
+
className,
|
244
|
+
]}>
|
245
|
+
<figure class:list={[currentSize.figure]}>
|
246
|
+
<Image
|
247
|
+
src={image}
|
248
|
+
alt={`${name} product image`}
|
249
|
+
width={currentSize.image.width}
|
250
|
+
height={currentSize.image.height}
|
251
|
+
rounded="lg"
|
252
|
+
transition="fade"
|
253
|
+
hover="brightness"
|
254
|
+
class="cosy:object-cover cosy:w-full"
|
255
|
+
loading="lazy"
|
256
|
+
showError={true}
|
257
|
+
/>
|
258
|
+
</figure>
|
259
|
+
<div class:list={[currentSize.padding, 'cosy:card-body']}>
|
260
|
+
<h2 class:list={['cosy:mb-2 cosy:font-bold cosy:card-title', currentSize.title]}>{name}</h2>
|
261
|
+
<p class:list={['cosy:mb-4', currentSize.description]}>{description}</p>
|
262
|
+
|
263
|
+
<div class={buttonsContainerClass}>
|
264
|
+
{
|
265
|
+
productUrl && (
|
266
|
+
<Link
|
267
|
+
href={productUrl}
|
268
|
+
external
|
269
|
+
variant="primary"
|
270
|
+
class:list={[
|
271
|
+
'cosy:btn cosy:bg-[#4468e3] cosy:hover:bg-[#3857cc] cosy:border-0',
|
272
|
+
'cosy:text-white cosy:font-bold cosy:shadow-sm',
|
273
|
+
buttonLayoutClass,
|
274
|
+
currentSize.buttons,
|
275
|
+
]}
|
276
|
+
aria-label={`Visit ${name}'s website`}>
|
277
|
+
{primaryButtonText}
|
278
|
+
</Link>
|
279
|
+
)
|
280
|
+
}
|
281
|
+
{
|
282
|
+
appStoreUrl && (
|
283
|
+
<Link
|
284
|
+
href={appStoreUrl}
|
285
|
+
external
|
286
|
+
variant="secondary"
|
287
|
+
class:list={[
|
288
|
+
'cosy:btn cosy:bg-[#161616] cosy:hover:bg-black cosy:border-0',
|
289
|
+
'cosy:text-white cosy:font-bold cosy:shadow-sm',
|
290
|
+
buttonLayoutClass,
|
291
|
+
currentSize.buttons,
|
292
|
+
]}
|
293
|
+
aria-label={`Download ${name} on App Store`}>
|
294
|
+
{secondaryButtonText}
|
295
|
+
</Link>
|
296
|
+
)
|
297
|
+
}
|
298
|
+
{
|
299
|
+
githubUrl && (
|
300
|
+
<Link
|
301
|
+
href={githubUrl}
|
302
|
+
external
|
303
|
+
variant="ghost"
|
304
|
+
class:list={[
|
305
|
+
'cosy:btn cosy:bg-[#f0f0f0] cosy:hover:bg-[#e0e0e0] cosy:border-0',
|
306
|
+
'cosy:text-[#24292f] cosy:font-bold cosy:shadow-sm',
|
307
|
+
buttonLayoutClass,
|
308
|
+
currentSize.buttons,
|
309
|
+
]}
|
310
|
+
aria-label={`View ${name}'s GitHub repository`}>
|
311
|
+
<SocialIcon platform="github" class="cosy:w-4 cosy:h-4 cosy:mr-1.5 cosy:inline-block" />
|
312
|
+
<span class="cosy:inline-block">{githubButtonText}</span>
|
313
|
+
</Link>
|
314
|
+
)
|
315
|
+
}
|
316
|
+
</div>
|
317
|
+
</div>
|
318
|
+
</div>
|