@coffic/cosy-ui 0.1.27 → 0.1.29

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/index.ts CHANGED
@@ -9,6 +9,7 @@ export { default as CodeBlock } from './src/components/CodeBlock.astro';
9
9
  export { default as Footer } from './src/components/Footer.astro';
10
10
  export { default as Header } from './src/components/Header.astro';
11
11
  export { default as Hero } from './src/components/Hero.astro';
12
+ export { default as Image } from './src/components/Image.astro';
12
13
  export { default as Link } from './src/components/Link.astro';
13
14
  export { default as Modal } from './src/components/Modal.astro';
14
15
  export { default as SocialIcon } from './src/components/SocialIcon.astro';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffic/cosy-ui",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "A astro component library",
5
5
  "repository": {
6
6
  "url": "https://github.com/CofficLab/cosy-ui"
@@ -40,7 +40,7 @@ const icons = {
40
40
 
41
41
  <div class={`alert ${alertClass} ${className}`} role="alert">
42
42
  <div class="flex items-center gap-2">
43
- <Fragment set:html={icons[type]} />
43
+ <div set:html={icons[type]} />
44
44
  <div class="flex-1">
45
45
  {title && <h3 class="font-bold">{title}</h3>}
46
46
  <div><slot /></div>
@@ -3,6 +3,9 @@ import { processSocialLink } from '../utils/social';
3
3
  import Link from './Link.astro';
4
4
  import Banner from './Banner.astro';
5
5
  import SocialIcon from './SocialIcon.astro';
6
+ import type { FooterProps } from '../types/footer';
7
+
8
+ interface Props extends FooterProps {}
6
9
 
7
10
  const {
8
11
  siteName,
@@ -19,12 +22,21 @@ const {
19
22
  termsLink,
20
23
  privacyLink,
21
24
  socialLinks = [],
22
- } = Astro.props;
25
+ teamLink,
26
+ careersLink,
27
+ newsLink,
28
+ historyLink,
29
+ partnersLink,
30
+ blogLink,
31
+ faqLink,
32
+ mediaLink,
33
+ techStackLink,
34
+ }: Props = Astro.props;
23
35
 
24
36
  const currentYear = new Date().getFullYear();
25
37
 
26
38
  // 处理社交链接
27
- const processedSocialLinks = socialLinks.map((link) => processSocialLink(link));
39
+ const processedSocialLinks = socialLinks.map((url) => processSocialLink(url));
28
40
  ---
29
41
 
30
42
  <footer class="bg-base-200/50 dark:bg-base-300/50 z-50 backdrop-blur-md">
@@ -66,11 +78,11 @@ const processedSocialLinks = socialLinks.map((link) => processSocialLink(link));
66
78
 
67
79
  {
68
80
  products.length > 0 && (
69
- <nav class="place-self-center text-center flex flex-col items-center min-h-[200px]">
81
+ <nav class="place-self-start text-center flex flex-col items-center min-h-[200px]">
70
82
  <h6 class="footer-title">产品</h6>
71
- <div class="flex flex-col gap-2">
83
+ <div class="flex flex-col">
72
84
  {products.map((product) => (
73
- <Link href={product.href} external={product.external}>
85
+ <Link href={product.href} external={product.external} size="sm">
74
86
  {product.name}
75
87
  </Link>
76
88
  ))}
@@ -80,12 +92,21 @@ const processedSocialLinks = socialLinks.map((link) => processSocialLink(link));
80
92
  }
81
93
 
82
94
  {
83
- (aboutLink || contactLink) && (
84
- <nav class="place-self-center text-center flex flex-col items-center min-h-[200px]">
95
+ (aboutLink || contactLink || teamLink || careersLink || newsLink || historyLink || partnersLink || blogLink || faqLink || mediaLink || techStackLink) && (
96
+ <nav class="place-self-start text-center flex flex-col items-center min-h-[200px]">
85
97
  <h6 class="footer-title">关于</h6>
86
- <div class="flex flex-col gap-2">
87
- {aboutLink && <Link href={aboutLink}>关于我们</Link>}
88
- {contactLink && <Link href={contactLink}>联系我们</Link>}
98
+ <div class="flex flex-col">
99
+ {aboutLink && <Link href={aboutLink} size="sm">关于我们</Link>}
100
+ {teamLink && <Link href={teamLink} size="sm">团队介绍</Link>}
101
+ {careersLink && <Link href={careersLink} size="sm">加入我们</Link>}
102
+ {newsLink && <Link href={newsLink} size="sm">新闻动态</Link>}
103
+ {historyLink && <Link href={historyLink} size="sm">发展历程</Link>}
104
+ {partnersLink && <Link href={partnersLink} size="sm">合作伙伴</Link>}
105
+ {blogLink && <Link href={blogLink} size="sm">技术博客</Link>}
106
+ {techStackLink && <Link href={techStackLink} size="sm">技术栈</Link>}
107
+ {faqLink && <Link href={faqLink} size="sm">常见问题</Link>}
108
+ {mediaLink && <Link href={mediaLink} size="sm">媒体报道</Link>}
109
+ {contactLink && <Link href={contactLink} size="sm">联系我们</Link>}
89
110
  </div>
90
111
  </nav>
91
112
  )
@@ -93,11 +114,11 @@ const processedSocialLinks = socialLinks.map((link) => processSocialLink(link));
93
114
 
94
115
  {
95
116
  (termsLink || privacyLink) && (
96
- <nav class="place-self-center text-center flex flex-col items-center min-h-[200px]">
117
+ <nav class="place-self-start text-center flex flex-col items-center min-h-[200px]">
97
118
  <h6 class="footer-title">法律</h6>
98
- <div class="flex flex-col gap-2">
99
- {termsLink && <Link href={termsLink}>服务条款</Link>}
100
- {privacyLink && <Link href={privacyLink}>隐私政策</Link>}
119
+ <div class="flex flex-col">
120
+ {termsLink && <Link href={termsLink} size="sm">服务条款</Link>}
121
+ {privacyLink && <Link href={privacyLink} size="sm">隐私政策</Link>}
101
122
  </div>
102
123
  </nav>
103
124
  )
@@ -0,0 +1,251 @@
1
+ ---
2
+ import { Image as AstroImage } from 'astro:assets';
3
+ import type { ImageMetadata } from 'astro';
4
+
5
+ interface Props {
6
+ /**
7
+ * 图片源,可以是本地图片或远程URL
8
+ */
9
+ src: ImageMetadata | string;
10
+ /**
11
+ * 图片的替代文本
12
+ */
13
+ alt: string;
14
+ /**
15
+ * 图片的宽度
16
+ */
17
+ width?: number;
18
+ /**
19
+ * 图片的高度
20
+ */
21
+ height?: number;
22
+ /**
23
+ * 图片的加载方式
24
+ * @default "lazy"
25
+ */
26
+ loading?: 'lazy' | 'eager';
27
+ /**
28
+ * 图片的填充方式
29
+ * @default "cover"
30
+ */
31
+ objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
32
+ /**
33
+ * 图片的位置
34
+ * @default "center"
35
+ */
36
+ objectPosition?: string;
37
+ /**
38
+ * 是否显示加载中的占位图
39
+ * @default true
40
+ */
41
+ showPlaceholder?: boolean;
42
+ /**
43
+ * 是否显示加载失败的错误图
44
+ * @default true
45
+ */
46
+ showError?: boolean;
47
+ /**
48
+ * 自定义类名
49
+ */
50
+ class?: string;
51
+ /**
52
+ * 是否启用图片懒加载
53
+ * @default true
54
+ */
55
+ lazy?: boolean;
56
+ /**
57
+ * 图片的圆角大小
58
+ * @default "none"
59
+ */
60
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full';
61
+ /**
62
+ * 图片的阴影效果
63
+ * @default "none"
64
+ */
65
+ shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
66
+ /**
67
+ * 图片的悬停效果
68
+ * @default "none"
69
+ */
70
+ hover?: 'none' | 'scale' | 'brightness' | 'blur';
71
+ /**
72
+ * 图片的过渡动画
73
+ * @default "none"
74
+ */
75
+ transition?: 'none' | 'fade' | 'slide' | 'zoom';
76
+ }
77
+
78
+ const {
79
+ src,
80
+ alt,
81
+ width,
82
+ height,
83
+ loading = 'lazy',
84
+ objectFit = 'cover',
85
+ objectPosition = 'center',
86
+ showPlaceholder = true,
87
+ showError = true,
88
+ class: className = '',
89
+ lazy = true,
90
+ rounded = 'none',
91
+ shadow = 'none',
92
+ hover = 'none',
93
+ transition = 'none',
94
+ } = Astro.props;
95
+
96
+ // 处理类名
97
+ const classes = [
98
+ 'relative',
99
+ // 圆角
100
+ rounded !== 'none' && `rounded-${rounded}`,
101
+ // 阴影
102
+ shadow !== 'none' && `shadow-${shadow}`,
103
+ // 悬停效果
104
+ hover !== 'none' && {
105
+ 'scale': 'hover:scale-105',
106
+ 'brightness': 'hover:brightness-110',
107
+ 'blur': 'hover:blur-sm',
108
+ }[hover],
109
+ // 过渡动画
110
+ transition !== 'none' && {
111
+ 'fade': 'transition-opacity duration-300',
112
+ 'slide': 'transition-transform duration-300',
113
+ 'zoom': 'transition-all duration-300',
114
+ }[transition],
115
+ className,
116
+ ].filter(Boolean).join(' ');
117
+
118
+ // 处理图片样式
119
+ const imageStyles = {
120
+ objectFit,
121
+ objectPosition,
122
+ };
123
+
124
+ // 判断是否为本地图片
125
+ const isLocalImage = typeof src !== 'string' && 'src' in src;
126
+ ---
127
+
128
+ <div class={classes}>
129
+ <div class="relative w-full h-full">
130
+ {isLocalImage ? (
131
+ <AstroImage
132
+ src={src}
133
+ alt={alt}
134
+ width={width}
135
+ height={height}
136
+ loading={loading}
137
+ class="w-full h-full opacity-0 transition-opacity duration-300"
138
+ style={imageStyles}
139
+ />
140
+ ) : (
141
+ <img
142
+ src={src}
143
+ alt={alt}
144
+ width={width}
145
+ height={height}
146
+ loading={loading}
147
+ class="w-full h-full opacity-0 transition-opacity duration-300"
148
+ style={imageStyles}
149
+ />
150
+ )}
151
+
152
+ {/* 加载占位图 */}
153
+ {showPlaceholder && (
154
+ <div class="absolute inset-0 bg-base-200 animate-pulse" />
155
+ )}
156
+
157
+ {/* 错误占位图 */}
158
+ {showError && (
159
+ <div class="absolute inset-0 bg-error/10 flex items-center justify-center hidden">
160
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-error" fill="none" viewBox="0 0 24 24" stroke="currentColor">
161
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
162
+ </svg>
163
+ </div>
164
+ )}
165
+ </div>
166
+ </div>
167
+
168
+ <script>
169
+ // 处理图片加载状态
170
+ function handleImageLoad(img) {
171
+ // 移除加载占位图
172
+ const placeholder = img.parentElement?.querySelector('.animate-pulse');
173
+ if (placeholder) {
174
+ placeholder.classList.add('opacity-0');
175
+ setTimeout(() => {
176
+ placeholder.remove();
177
+ }, 300);
178
+ }
179
+
180
+ // 添加加载完成动画
181
+ img.classList.add('opacity-100');
182
+ }
183
+
184
+ // 处理图片加载错误
185
+ function handleImageError(img) {
186
+ // 移除加载占位图
187
+ const placeholder = img.parentElement?.querySelector('.animate-pulse');
188
+ if (placeholder) {
189
+ placeholder.classList.add('opacity-0');
190
+ setTimeout(() => {
191
+ placeholder.remove();
192
+ }, 300);
193
+ }
194
+
195
+ // 显示错误占位图
196
+ const errorPlaceholder = img.parentElement?.querySelector('.bg-error\\/10');
197
+ if (errorPlaceholder) {
198
+ errorPlaceholder.classList.remove('hidden');
199
+ }
200
+ }
201
+
202
+ // 初始化图片加载处理
203
+ function initializeImageHandlers() {
204
+ const images = document.querySelectorAll('img[loading="lazy"]');
205
+ images.forEach(img => {
206
+ if (img instanceof HTMLImageElement) {
207
+ // 如果图片已经加载完成
208
+ if (img.complete) {
209
+ handleImageLoad(img);
210
+ } else {
211
+ // 添加加载事件监听
212
+ img.addEventListener('load', () => handleImageLoad(img));
213
+ img.addEventListener('error', () => handleImageError(img));
214
+ }
215
+ }
216
+ });
217
+ }
218
+
219
+ // 页面加载时初始化
220
+ document.addEventListener('astro:page-load', initializeImageHandlers);
221
+ // 初始加载时也初始化
222
+ initializeImageHandlers();
223
+ </script>
224
+
225
+ <style>
226
+ /* 过渡动画 */
227
+ .transition-opacity {
228
+ transition-property: opacity;
229
+ }
230
+
231
+ .transition-transform {
232
+ transition-property: transform;
233
+ }
234
+
235
+ .transition-all {
236
+ transition-property: all;
237
+ }
238
+
239
+ /* 悬停效果 */
240
+ .hover\:scale-105:hover {
241
+ transform: scale(1.05);
242
+ }
243
+
244
+ .hover\:brightness-110:hover {
245
+ filter: brightness(1.1);
246
+ }
247
+
248
+ .hover\:blur-sm:hover {
249
+ filter: blur(4px);
250
+ }
251
+ </style>
package/src/env.d.ts CHANGED
@@ -1 +0,0 @@
1
- /// <reference types="astro/client" />
@@ -0,0 +1,36 @@
1
+ export interface Logo {
2
+ src: string;
3
+ alt: string;
4
+ }
5
+
6
+ export interface Product {
7
+ name: string;
8
+ href: string;
9
+ external?: boolean;
10
+ }
11
+
12
+ export interface FooterProps {
13
+ siteName: string;
14
+ homeLink: string;
15
+ slogan: string;
16
+ company: string;
17
+ copyright: string;
18
+ inspirationalSlogan: string;
19
+ icp?: string;
20
+ logo?: Logo;
21
+ products?: Product[];
22
+ aboutLink?: string;
23
+ contactLink?: string;
24
+ termsLink?: string;
25
+ privacyLink?: string;
26
+ socialLinks?: string[];
27
+ teamLink?: string;
28
+ careersLink?: string;
29
+ newsLink?: string;
30
+ historyLink?: string;
31
+ partnersLink?: string;
32
+ blogLink?: string;
33
+ faqLink?: string;
34
+ mediaLink?: string;
35
+ techStackLink?: string;
36
+ }
@@ -75,15 +75,15 @@ export function processSocialLink(url: string): ProcessedSocialLink {
75
75
  if (!platformInfo) {
76
76
  // 如果无法识别平台,返回默认值
77
77
  return {
78
- url,
79
- name: new URL(url).hostname,
78
+ url: url,
79
+ name: '社交链接',
80
80
  platform: 'default',
81
81
  };
82
82
  }
83
83
 
84
84
  const [platform, config] = platformInfo;
85
85
  return {
86
- url,
86
+ url: url,
87
87
  name: config.name,
88
88
  platform,
89
89
  };