@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.
@@ -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
- * @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;
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
- size = 'md',
105
- padding = 'md',
106
- verticalPadding = "md",
107
- centered = true,
108
- backgroundColor,
109
- id,
110
- class: className,
111
- 'class:list': classList,
112
- ...rest
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
- const verticalPaddingClass =
117
- verticalPadding === "none" || verticalPadding === "sm" ||
118
- verticalPadding === "md" || verticalPadding === "lg" ||
119
- verticalPadding === "xl"
120
- ? `main-padding-${verticalPadding}`
121
- : "main-padding-custom";
122
-
123
- // 处理背景颜色
124
- const bgColorClass = backgroundColor &&
125
- (backgroundColor === "primary" || backgroundColor === "secondary" ||
126
- backgroundColor === "tertiary" || backgroundColor === "light" ||
127
- backgroundColor === "dark")
128
- ? `main-bg-${backgroundColor}`
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
- (verticalPadding && verticalPaddingClass === "main-padding-custom")
134
- ? `padding-top: ${verticalPadding}; padding-bottom: ${verticalPadding};`
135
- : "";
136
-
137
- const customBgColor =
138
- (backgroundColor && !bgColorClass)
139
- ? `background-color: ${backgroundColor};`
140
- : "";
141
-
142
- const inlineStyle = (customStyle || customBgColor)
143
- ? `${customStyle} ${customBgColor}`.trim()
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
- id={id}
149
- class:list={[
150
- "main-content",
151
- verticalPaddingClass,
152
- bgColorClass,
153
- className,
154
- classList
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>
@@ -416,6 +416,7 @@ const {
416
416
  ...rest
417
417
  } = Astro.props;
418
418
 
419
+ // 获取当前路径,并处理基础路径
419
420
  const currentPath = Astro.url.pathname;
420
421
 
421
422
  // 获取有效的语言代码