@coffic/cosy-ui 0.1.29 → 0.2.0

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.
Files changed (97) hide show
  1. package/README.md +67 -23
  2. package/dist/app.css +1 -0
  3. package/dist/assets/logo-rounded.png +0 -0
  4. package/dist/assets/logo.png +0 -0
  5. package/dist/components/base/Alert.astro +186 -0
  6. package/dist/components/base/Button.astro +103 -0
  7. package/dist/components/base/Image.astro +291 -0
  8. package/dist/components/base/Link.astro +131 -0
  9. package/dist/components/base/README.md +53 -0
  10. package/dist/components/base/index.ts +6 -0
  11. package/dist/components/containers/Container.astro +103 -0
  12. package/dist/components/containers/Main.astro +167 -0
  13. package/dist/components/containers/Section.astro +145 -0
  14. package/dist/components/containers/index.ts +3 -0
  15. package/dist/components/data-display/Blog.astro +195 -0
  16. package/dist/components/data-display/README.md +37 -0
  17. package/dist/components/data-display/TeamMember.astro +135 -0
  18. package/dist/components/data-display/TeamMembers.astro +101 -0
  19. package/dist/components/data-display/index.ts +3 -0
  20. package/dist/components/display/Banner.astro +57 -0
  21. package/dist/components/display/Card.astro +135 -0
  22. package/dist/components/display/CodeBlock.astro +147 -0
  23. package/dist/components/display/CodeExample.astro +330 -0
  24. package/dist/components/display/Hero.astro +119 -0
  25. package/dist/components/display/Modal.astro +115 -0
  26. package/dist/components/display/README.md +32 -0
  27. package/dist/components/display/index.ts +6 -0
  28. package/dist/components/icons/AlertTriangle.astro +35 -0
  29. package/dist/components/icons/CalendarIcon.astro +38 -0
  30. package/dist/components/icons/CheckCircle.astro +36 -0
  31. package/dist/components/icons/CheckIcon.astro +38 -0
  32. package/dist/components/icons/ClipboardIcon.astro +39 -0
  33. package/dist/components/icons/CloseIcon.astro +38 -0
  34. package/dist/components/icons/ErrorIcon.astro +35 -0
  35. package/dist/components/icons/GithubIcon.astro +31 -0
  36. package/dist/components/icons/InfoCircle.astro +37 -0
  37. package/dist/components/icons/InfoIcon.astro +38 -0
  38. package/dist/components/icons/LinkIcon.astro +39 -0
  39. package/dist/components/icons/LinkedinIcon.astro +31 -0
  40. package/dist/components/icons/MenuIcon.astro +41 -0
  41. package/dist/components/icons/SearchIcon.astro +40 -0
  42. package/dist/components/icons/SocialIcon.astro +100 -0
  43. package/dist/components/icons/SuccessIcon.astro +35 -0
  44. package/dist/components/icons/SunCloudyIcon.astro +45 -0
  45. package/dist/components/icons/TwitterIcon.astro +31 -0
  46. package/dist/components/icons/UserIcon.astro +35 -0
  47. package/dist/components/icons/WarningIcon.astro +38 -0
  48. package/dist/components/icons/XCircle.astro +37 -0
  49. package/dist/components/icons/index.ts +23 -0
  50. package/dist/components/layouts/BaseLayout.astro +144 -0
  51. package/dist/components/layouts/DashboardLayout.astro +660 -0
  52. package/dist/components/layouts/DefaultLayout.astro +170 -0
  53. package/dist/components/layouts/DocumentationLayout.astro +469 -0
  54. package/dist/components/layouts/Flex.astro +138 -0
  55. package/dist/components/layouts/Footer.astro +284 -0
  56. package/dist/components/layouts/Grid.astro +182 -0
  57. package/dist/components/layouts/Header.astro +114 -0
  58. package/dist/components/layouts/LandingLayout.astro +388 -0
  59. package/dist/components/layouts/README.md +37 -0
  60. package/dist/components/layouts/Stack.astro +149 -0
  61. package/dist/components/layouts/index.ts +6 -0
  62. package/dist/components/navigation/LanguageSwitcher.astro +81 -0
  63. package/dist/components/navigation/README.md +24 -0
  64. package/dist/components/navigation/TableOfContents.astro +352 -0
  65. package/dist/components/navigation/ThemeSwitcher.astro +89 -0
  66. package/dist/components/navigation/index.ts +3 -0
  67. package/dist/components/typography/Article.astro +144 -0
  68. package/dist/components/typography/Heading.astro +205 -0
  69. package/dist/components/typography/README.md +29 -0
  70. package/dist/components/typography/Text.astro +187 -0
  71. package/dist/components/typography/index.ts +3 -0
  72. package/dist/index.ts +9 -0
  73. package/dist/integration.ts +14 -0
  74. package/dist/style.ts +1 -0
  75. package/{src → dist}/types/footer.ts +1 -0
  76. package/dist/utils/theme.ts +55 -0
  77. package/package.json +65 -59
  78. package/index.ts +0 -18
  79. package/src/components/Alert.astro +0 -78
  80. package/src/components/Article.astro +0 -11
  81. package/src/components/Banner.astro +0 -49
  82. package/src/components/Blog.astro +0 -115
  83. package/src/components/Button.astro +0 -49
  84. package/src/components/Card.astro +0 -113
  85. package/src/components/CodeBlock.astro +0 -186
  86. package/src/components/Footer.astro +0 -148
  87. package/src/components/Header.astro +0 -305
  88. package/src/components/Hero.astro +0 -69
  89. package/src/components/Image.astro +0 -251
  90. package/src/components/Link.astro +0 -82
  91. package/src/components/Modal.astro +0 -67
  92. package/src/components/SocialIcon.astro +0 -36
  93. package/src/components/TeamMember.astro +0 -68
  94. package/src/components/TeamMembers.astro +0 -43
  95. package/src/env.d.ts +0 -0
  96. /package/{src/components → dist/components/base}/ThemeItem.astro +0 -0
  97. /package/{src → dist}/utils/social.ts +0 -0
@@ -0,0 +1,167 @@
1
+ ---
2
+ /**
3
+ * @component Main
4
+ *
5
+ * @description
6
+ * Main 组件是页面的主要内容区域,使用 Container 组件提供响应式布局。
7
+ * 它适合作为页面的主体部分,自动处理不同屏幕尺寸下的布局和内边距。
8
+ *
9
+ * @design
10
+ * 设计理念:
11
+ * 1. 结构清晰 - 明确表示页面的主要内容区域,提高语义化
12
+ * 2. 响应式布局 - 在不同屏幕尺寸下自动调整内边距和宽度
13
+ * 3. 灵活配置 - 支持自定义容器尺寸、内边距和背景颜色
14
+ * 4. 与容器集成 - 无缝集成 Container 组件,保持布局一致性
15
+ *
16
+ * @usage
17
+ * 基本用法:
18
+ * ```astro
19
+ * ---
20
+ * import { Main } from '@coffic/cosy-ui';
21
+ * ---
22
+ *
23
+ * <Main>
24
+ * <h1>页面标题</h1>
25
+ * <p>页面内容</p>
26
+ * </Main>
27
+ * ```
28
+ *
29
+ * 自定义容器尺寸:
30
+ * ```astro
31
+ * <Main size="lg">
32
+ * <p>这是一个较宽的主内容区域</p>
33
+ * </Main>
34
+ * ```
35
+ *
36
+ * 自定义内边距:
37
+ * ```astro
38
+ * <Main verticalPadding="lg" padding="xl">
39
+ * <p>这个主内容区域有更大的内边距</p>
40
+ * </Main>
41
+ * ```
42
+ *
43
+ * 自定义背景颜色:
44
+ * ```astro
45
+ * <Main backgroundColor="primary">
46
+ * <p>这个主内容区域有主题色背景</p>
47
+ * </Main>
48
+ * ```
49
+ */
50
+
51
+ import Container from './Container.astro';
52
+
53
+ // 导入样式
54
+ import '../../app.css';
55
+
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;
101
+ }
102
+
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
113
+ } = Astro.props;
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
+ : "";
130
+
131
+ // 构建自定义样式
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;
145
+ ---
146
+
147
+ <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>
@@ -0,0 +1,145 @@
1
+ ---
2
+ /**
3
+ * Section组件
4
+ *
5
+ * 一个用于页面内容区块的组件,支持不同的内边距、背景和间距
6
+ *
7
+ * @example
8
+ * ```astro
9
+ * <Section>
10
+ * <h2>默认区块</h2>
11
+ * <p>内容将被包裹在一个合适的区块中</p>
12
+ * </Section>
13
+ *
14
+ * <Section padding="lg" background="gray" centered={true}>
15
+ * <h2>自定义区块</h2>
16
+ * <p>大内边距,灰色背景,内容居中</p>
17
+ * </Section>
18
+ * ```
19
+ */
20
+
21
+ import type { HTMLAttributes } from 'astro/types';
22
+ import Container from './Container.astro';
23
+
24
+ interface Props extends HTMLAttributes<'section'> {
25
+ /**
26
+ * 内边距大小
27
+ * @default "md"
28
+ */
29
+ padding?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
30
+
31
+ /**
32
+ * 背景颜色
33
+ * @default "transparent"
34
+ */
35
+ background?: 'transparent' | 'white' | 'gray' | 'primary' | 'secondary' | 'dark';
36
+
37
+ /**
38
+ * 是否使用容器包裹内容
39
+ * @default true
40
+ */
41
+ container?: boolean;
42
+
43
+ /**
44
+ * 容器尺寸,仅当container为true时有效
45
+ * @default "md"
46
+ */
47
+ containerSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
48
+
49
+ /**
50
+ * 内容是否居中
51
+ * @default false
52
+ */
53
+ centered?: boolean;
54
+
55
+ /**
56
+ * 自定义类名
57
+ */
58
+ class?: string;
59
+
60
+ /**
61
+ * 类名列表
62
+ */
63
+ 'class:list'?: any;
64
+
65
+ /**
66
+ * 自定义ID
67
+ */
68
+ id?: string;
69
+
70
+ /**
71
+ * 自定义内联样式
72
+ */
73
+ style?: string;
74
+ }
75
+
76
+ const {
77
+ padding = 'md',
78
+ background = 'transparent',
79
+ container = true,
80
+ containerSize = 'md',
81
+ centered = false,
82
+ class: className = '',
83
+ 'class:list': classList,
84
+ id,
85
+ style,
86
+ ...rest
87
+ } = Astro.props;
88
+
89
+ // 内边距映射
90
+ const paddingClasses = {
91
+ 'none': 'py-0',
92
+ 'sm': 'py-6',
93
+ 'md': 'py-12',
94
+ 'lg': 'py-16',
95
+ 'xl': 'py-24'
96
+ };
97
+
98
+ // 背景颜色映射
99
+ const backgroundClasses = {
100
+ 'transparent': 'bg-transparent',
101
+ 'white': 'bg-white',
102
+ 'gray': 'bg-gray-100',
103
+ 'primary': 'bg-blue-50',
104
+ 'secondary': 'bg-gray-50',
105
+ 'dark': 'bg-gray-900 text-white'
106
+ };
107
+
108
+ // 构建最终类名
109
+ const sectionClasses = [
110
+ paddingClasses[padding as keyof typeof paddingClasses],
111
+ backgroundClasses[background as keyof typeof backgroundClasses],
112
+ className
113
+ ].join(' ');
114
+
115
+ // 内容类名
116
+ const contentClasses = centered ? 'text-center' : '';
117
+ ---
118
+
119
+ <section id={id} class:list={[sectionClasses, classList]} style={style} {...rest}>
120
+ {container ? (
121
+ <Container size={containerSize} class={contentClasses}>
122
+ <slot />
123
+ </Container>
124
+ ) : (
125
+ <div class={contentClasses}>
126
+ <slot />
127
+ </div>
128
+ )}
129
+ </section>
130
+
131
+ <style>
132
+ /* 背景颜色 */
133
+ .bg-transparent { background-color: transparent; }
134
+ .bg-white { background-color: #ffffff; }
135
+ .bg-gray-50 { background-color: #f9fafb; }
136
+ .bg-gray-100 { background-color: #f3f4f6; }
137
+ .bg-blue-50 { background-color: #eff6ff; }
138
+ .bg-gray-900 { background-color: #111827; }
139
+
140
+ /* 文字颜色 */
141
+ .text-white { color: #ffffff; }
142
+
143
+ /* 文本对齐 */
144
+ .text-center { text-align: center; }
145
+ </style>
@@ -0,0 +1,3 @@
1
+ export { default as Container } from './Container.astro';
2
+ export { default as Section } from './Section.astro';
3
+ export { default as Main } from './Main.astro';
@@ -0,0 +1,195 @@
1
+ ---
2
+ import { UserIcon, CalendarIcon } from '../icons';
3
+
4
+ interface Props {
5
+ title: string;
6
+ subtitle?: string;
7
+ author?: string;
8
+ date?: Date;
9
+ cover?: {
10
+ src: string;
11
+ alt?: string;
12
+ };
13
+ tags?: string[];
14
+ }
15
+
16
+ const {
17
+ title,
18
+ subtitle,
19
+ author,
20
+ date,
21
+ cover,
22
+ tags = [],
23
+ } = Astro.props;
24
+
25
+ // 格式化日期
26
+ const formatDate = (date?: Date) => {
27
+ if (!date) return '';
28
+ return new Intl.DateTimeFormat('zh-CN', {
29
+ year: 'numeric',
30
+ month: 'long',
31
+ day: 'numeric'
32
+ }).format(date);
33
+ };
34
+ ---
35
+
36
+ <article class="article">
37
+ <header class="article-header">
38
+ {cover && (
39
+ <div class="cover-container">
40
+ <img
41
+ src={cover.src}
42
+ alt={cover.alt || title}
43
+ class="cover-image"
44
+ />
45
+ </div>
46
+ )}
47
+
48
+ <div class="header-content">
49
+ <h1 class="article-title">{title}</h1>
50
+ {subtitle && (
51
+ <p class="article-subtitle">{subtitle}</p>
52
+ )}
53
+
54
+ <div class="article-meta">
55
+ {author && (
56
+ <div class="meta-item">
57
+ <UserIcon size="16px" />
58
+ <span>{author}</span>
59
+ </div>
60
+ )}
61
+
62
+ {date && (
63
+ <div class="meta-item">
64
+ <CalendarIcon size="16px" />
65
+ <time datetime={date.toISOString()}>{formatDate(date)}</time>
66
+ </div>
67
+ )}
68
+ </div>
69
+
70
+ {tags.length > 0 && (
71
+ <div class="article-tags">
72
+ {tags.map((tag: string) => (
73
+ <span class="tag">{tag}</span>
74
+ ))}
75
+ </div>
76
+ )}
77
+ </div>
78
+ </header>
79
+
80
+ <div class="article-content">
81
+ <slot />
82
+ </div>
83
+ </article>
84
+
85
+ <style>
86
+ /* 基础样式 */
87
+ .article {
88
+ container-type: inline-size;
89
+ }
90
+
91
+ /* 文章头部 */
92
+ .article-header {
93
+ margin-bottom: 2rem;
94
+ }
95
+
96
+ /* 封面图片 */
97
+ .cover-container {
98
+ position: relative;
99
+ width: 100%;
100
+ height: 300px;
101
+ margin-bottom: 1.5rem;
102
+ border-radius: 0.5rem;
103
+ overflow: hidden;
104
+ }
105
+
106
+ .cover-image {
107
+ width: 100%;
108
+ height: 100%;
109
+ object-fit: cover;
110
+ }
111
+
112
+ /* 标题和副标题 */
113
+ .header-content {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: 1rem;
117
+ }
118
+
119
+ .article-title {
120
+ font-size: 2.25rem;
121
+ font-weight: 700;
122
+ line-height: 1.2;
123
+ letter-spacing: -0.025em;
124
+ }
125
+
126
+ .article-subtitle {
127
+ font-size: 1.25rem;
128
+ color: var(--text-secondary, rgba(0, 0, 0, 0.7));
129
+ }
130
+
131
+ /* 元数据 */
132
+ .article-meta {
133
+ display: flex;
134
+ flex-wrap: wrap;
135
+ align-items: center;
136
+ gap: 1rem;
137
+ font-size: 0.875rem;
138
+ color: var(--text-secondary, rgba(0, 0, 0, 0.6));
139
+ }
140
+
141
+ .meta-item {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 0.5rem;
145
+ }
146
+
147
+ /* 标签 */
148
+ .article-tags {
149
+ display: flex;
150
+ flex-wrap: wrap;
151
+ gap: 0.5rem;
152
+ }
153
+
154
+ .tag {
155
+ display: inline-block;
156
+ padding: 0.25rem 0.75rem;
157
+ border: 1px solid var(--border-color, #e2e8f0);
158
+ border-radius: 9999px;
159
+ font-size: 0.75rem;
160
+ line-height: 1.5;
161
+ }
162
+
163
+ /* 文章内容 */
164
+ .article-content {
165
+ max-width: none;
166
+ font-size: 1.125rem;
167
+ line-height: 1.75;
168
+ }
169
+
170
+ /* 响应式调整 */
171
+ @media (min-width: 768px) {
172
+ .article-title {
173
+ font-size: 3rem;
174
+ }
175
+
176
+ .cover-container {
177
+ height: 400px;
178
+ }
179
+ }
180
+
181
+ /* 暗色主题适配 */
182
+ @media (prefers-color-scheme: dark) {
183
+ .article-subtitle {
184
+ color: var(--text-secondary, rgba(255, 255, 255, 0.7));
185
+ }
186
+
187
+ .article-meta {
188
+ color: var(--text-secondary, rgba(255, 255, 255, 0.6));
189
+ }
190
+
191
+ .tag {
192
+ border-color: var(--border-color, #2d3748);
193
+ }
194
+ }
195
+ </style>
@@ -0,0 +1,37 @@
1
+ # 数据展示组件 (Data Display Components)
2
+
3
+ 这个目录包含了专门用于数据展示的组件,主要用于展示结构化的数据内容。
4
+
5
+ ## 设计原则
6
+
7
+ 1. **数据优先**:以数据的展示和可读性为首要考虑
8
+ 2. **结构清晰**:保持数据结构的清晰和层次感
9
+ 3. **交互友好**:提供必要的数据交互功能
10
+
11
+ ## 包含的组件
12
+
13
+ - `TeamMember.astro`: 团队成员展示组件
14
+ - `TeamMembers.astro`: 团队成员列表组件
15
+ - `Blog.astro`: 博客文章展示组件
16
+
17
+ ## 使用指南
18
+
19
+ 数据展示组件通常需要传入结构化的数据:
20
+
21
+ ```astro
22
+ <TeamMembers>
23
+ <TeamMember
24
+ name="张三"
25
+ role="开发者"
26
+ avatar="/avatars/zhangsan.jpg"
27
+ />
28
+ </TeamMembers>
29
+
30
+ <Blog
31
+ title="文章标题"
32
+ author="作者"
33
+ date="2024-03-16"
34
+ >
35
+ 文章内容
36
+ </Blog>
37
+ ```
@@ -0,0 +1,135 @@
1
+ ---
2
+ /**
3
+ * @component TeamMember
4
+ *
5
+ * @description
6
+ * TeamMember 组件用于展示单个团队成员的信息,包括姓名、职位、头像、简介和社交媒体链接。
7
+ * 组件采用卡片式设计,支持悬停效果,并可以链接到成员的社交媒体账号。
8
+ *
9
+ * @design
10
+ * 设计理念:
11
+ * 1. 个人展示 - 突出展示团队成员的个人信息和形象
12
+ * 2. 社交连接 - 提供社交媒体链接,方便与团队成员建立联系
13
+ * 3. 视觉一致性 - 使用卡片组件确保与整体设计风格一致
14
+ * 4. 交互反馈 - 悬停时提供视觉反馈,增强用户体验
15
+ *
16
+ * @usage
17
+ * 基本用法:
18
+ * ```astro
19
+ * <TeamMember
20
+ * name="张三"
21
+ * role="前端开发工程师"
22
+ * avatar="/images/avatars/zhangsan.jpg"
23
+ * bio="拥有5年前端开发经验,专注于React和Vue生态系统。"
24
+ * />
25
+ * ```
26
+ *
27
+ * 包含社交媒体链接:
28
+ * ```astro
29
+ * <TeamMember
30
+ * name="李四"
31
+ * role="UI设计师"
32
+ * avatar="/images/avatars/lisi.jpg"
33
+ * bio="专注于用户体验和界面设计,热爱创造直观易用的产品。"
34
+ * socialLinks={[
35
+ * { platform: 'github', url: 'https://github.com/lisi' },
36
+ * { platform: 'twitter', url: 'https://twitter.com/lisi' },
37
+ * { platform: 'linkedin', url: 'https://linkedin.com/in/lisi' }
38
+ * ]}
39
+ * />
40
+ * ```
41
+ */
42
+
43
+ import Link from '../base/Link.astro';
44
+ import SocialIcon from '../icons/SocialIcon.astro';
45
+ import Image from '../base/Image.astro';
46
+
47
+ // 导入样式
48
+ import '../../app.css';
49
+
50
+ interface SocialLink {
51
+ /**
52
+ * 社交媒体平台
53
+ */
54
+ platform: 'github' | 'twitter' | 'linkedin' | 'website' | 'email';
55
+ /**
56
+ * 社交媒体链接URL
57
+ */
58
+ url: string;
59
+ }
60
+
61
+ // 自定义图片元数据接口,替代 astro 的 ImageMetadata
62
+ interface CustomImageMetadata {
63
+ src: string;
64
+ width: number;
65
+ height: number;
66
+ format: string;
67
+ }
68
+
69
+ export interface Props {
70
+ /**
71
+ * 团队成员姓名
72
+ */
73
+ name: string;
74
+ /**
75
+ * 团队成员职位
76
+ */
77
+ role: string;
78
+ /**
79
+ * 团队成员头像
80
+ */
81
+ avatar: CustomImageMetadata | string;
82
+ /**
83
+ * 团队成员简介
84
+ */
85
+ bio: string;
86
+ /**
87
+ * 社交媒体链接
88
+ */
89
+ socialLinks?: SocialLink[];
90
+ /**
91
+ * 自定义类名
92
+ */
93
+ class?: string;
94
+ }
95
+
96
+ const { name, role, avatar, bio, socialLinks, class: className = '' } = Astro.props;
97
+ ---
98
+
99
+ <div class:list={['team-member-card', className]}>
100
+ <figure class="team-member-avatar-container">
101
+ <Image
102
+ src={avatar}
103
+ alt={`${name}'s avatar`}
104
+ width={192}
105
+ height={192}
106
+ rounded="xl"
107
+ transition="fade"
108
+ hover="brightness"
109
+ class="team-member-avatar"
110
+ />
111
+ </figure>
112
+ <div class="team-member-content">
113
+ <h2 class="team-member-name">{name}</h2>
114
+ <p class="team-member-role">{role}</p>
115
+ <p class="team-member-bio">{bio}</p>
116
+
117
+ {
118
+ socialLinks && socialLinks.length > 0 && (
119
+ <div class="team-member-social">
120
+ {socialLinks.map((link: SocialLink) => (
121
+ <Link
122
+ href={link.url}
123
+ external
124
+ variant="ghost"
125
+ class="team-member-social-link"
126
+ aria-label={`Visit ${name}'s ${link.platform} profile`}>
127
+ <SocialIcon platform={link.platform} class="team-member-social-icon" />
128
+ </Link>
129
+ ))}
130
+ </div>
131
+ )
132
+ }
133
+ </div>
134
+ </div>
135
+