@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.
- package/README.md +67 -23
- package/dist/app.css +1 -0
- package/dist/assets/logo-rounded.png +0 -0
- package/dist/assets/logo.png +0 -0
- package/dist/components/base/Alert.astro +186 -0
- package/dist/components/base/Button.astro +103 -0
- package/dist/components/base/Image.astro +291 -0
- package/dist/components/base/Link.astro +131 -0
- package/dist/components/base/README.md +53 -0
- package/dist/components/base/index.ts +6 -0
- package/dist/components/containers/Container.astro +103 -0
- package/dist/components/containers/Main.astro +167 -0
- package/dist/components/containers/Section.astro +145 -0
- package/dist/components/containers/index.ts +3 -0
- package/dist/components/data-display/Blog.astro +195 -0
- package/dist/components/data-display/README.md +37 -0
- package/dist/components/data-display/TeamMember.astro +135 -0
- package/dist/components/data-display/TeamMembers.astro +101 -0
- package/dist/components/data-display/index.ts +3 -0
- package/dist/components/display/Banner.astro +57 -0
- package/dist/components/display/Card.astro +135 -0
- package/dist/components/display/CodeBlock.astro +147 -0
- package/dist/components/display/CodeExample.astro +330 -0
- package/dist/components/display/Hero.astro +119 -0
- package/dist/components/display/Modal.astro +115 -0
- package/dist/components/display/README.md +32 -0
- package/dist/components/display/index.ts +6 -0
- package/dist/components/icons/AlertTriangle.astro +35 -0
- package/dist/components/icons/CalendarIcon.astro +38 -0
- package/dist/components/icons/CheckCircle.astro +36 -0
- package/dist/components/icons/CheckIcon.astro +38 -0
- package/dist/components/icons/ClipboardIcon.astro +39 -0
- package/dist/components/icons/CloseIcon.astro +38 -0
- package/dist/components/icons/ErrorIcon.astro +35 -0
- package/dist/components/icons/GithubIcon.astro +31 -0
- package/dist/components/icons/InfoCircle.astro +37 -0
- package/dist/components/icons/InfoIcon.astro +38 -0
- package/dist/components/icons/LinkIcon.astro +39 -0
- package/dist/components/icons/LinkedinIcon.astro +31 -0
- package/dist/components/icons/MenuIcon.astro +41 -0
- package/dist/components/icons/SearchIcon.astro +40 -0
- package/dist/components/icons/SocialIcon.astro +100 -0
- package/dist/components/icons/SuccessIcon.astro +35 -0
- package/dist/components/icons/SunCloudyIcon.astro +45 -0
- package/dist/components/icons/TwitterIcon.astro +31 -0
- package/dist/components/icons/UserIcon.astro +35 -0
- package/dist/components/icons/WarningIcon.astro +38 -0
- package/dist/components/icons/XCircle.astro +37 -0
- package/dist/components/icons/index.ts +23 -0
- package/dist/components/layouts/BaseLayout.astro +144 -0
- package/dist/components/layouts/DashboardLayout.astro +660 -0
- package/dist/components/layouts/DefaultLayout.astro +170 -0
- package/dist/components/layouts/DocumentationLayout.astro +469 -0
- package/dist/components/layouts/Flex.astro +138 -0
- package/dist/components/layouts/Footer.astro +284 -0
- package/dist/components/layouts/Grid.astro +182 -0
- package/dist/components/layouts/Header.astro +114 -0
- package/dist/components/layouts/LandingLayout.astro +388 -0
- package/dist/components/layouts/README.md +37 -0
- package/dist/components/layouts/Stack.astro +149 -0
- package/dist/components/layouts/index.ts +6 -0
- package/dist/components/navigation/LanguageSwitcher.astro +81 -0
- package/dist/components/navigation/README.md +24 -0
- package/dist/components/navigation/TableOfContents.astro +352 -0
- package/dist/components/navigation/ThemeSwitcher.astro +89 -0
- package/dist/components/navigation/index.ts +3 -0
- package/dist/components/typography/Article.astro +144 -0
- package/dist/components/typography/Heading.astro +205 -0
- package/dist/components/typography/README.md +29 -0
- package/dist/components/typography/Text.astro +187 -0
- package/dist/components/typography/index.ts +3 -0
- package/dist/index.ts +9 -0
- package/dist/integration.ts +14 -0
- package/dist/style.ts +1 -0
- package/{src → dist}/types/footer.ts +1 -0
- package/dist/utils/theme.ts +55 -0
- package/package.json +65 -59
- package/index.ts +0 -18
- package/src/components/Alert.astro +0 -78
- package/src/components/Article.astro +0 -11
- package/src/components/Banner.astro +0 -49
- package/src/components/Blog.astro +0 -115
- package/src/components/Button.astro +0 -49
- package/src/components/Card.astro +0 -113
- package/src/components/CodeBlock.astro +0 -186
- package/src/components/Footer.astro +0 -148
- package/src/components/Header.astro +0 -305
- package/src/components/Hero.astro +0 -69
- package/src/components/Image.astro +0 -251
- package/src/components/Link.astro +0 -82
- package/src/components/Modal.astro +0 -67
- package/src/components/SocialIcon.astro +0 -36
- package/src/components/TeamMember.astro +0 -68
- package/src/components/TeamMembers.astro +0 -43
- package/src/env.d.ts +0 -0
- /package/{src/components → dist/components/base}/ThemeItem.astro +0 -0
- /package/{src → dist}/utils/social.ts +0 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Flex
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* Flex 组件是一个灵活的弹性布局组件,用于创建各种排列和对齐方式的布局。
|
7
|
+
* 它封装了 CSS Flexbox 的常用功能,提供简单易用的接口来控制子元素的排列方式。
|
8
|
+
*
|
9
|
+
* @note
|
10
|
+
* 此组件需要在 Root 组件内使用以确保样式正确加载。
|
11
|
+
*
|
12
|
+
* @usage
|
13
|
+
* ```astro
|
14
|
+
* ---
|
15
|
+
* import { Root, Flex } from '@coffic/cosy-ui';
|
16
|
+
* ---
|
17
|
+
*
|
18
|
+
* <Root>
|
19
|
+
* <Flex>
|
20
|
+
* <div>默认水平排列</div>
|
21
|
+
* <div>居中对齐</div>
|
22
|
+
* </Flex>
|
23
|
+
* </Root>
|
24
|
+
* ```
|
25
|
+
*/
|
26
|
+
|
27
|
+
import type { HTMLAttributes } from 'astro/types';
|
28
|
+
|
29
|
+
type Direction = 'row' | 'row-reverse' | 'column' | 'column-reverse';
|
30
|
+
type Justify = 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly';
|
31
|
+
type Align = 'start' | 'end' | 'center' | 'stretch' | 'baseline';
|
32
|
+
type Gap = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
33
|
+
|
34
|
+
interface Props extends HTMLAttributes<'div'> {
|
35
|
+
/**
|
36
|
+
* 排列方向
|
37
|
+
* @default "row"
|
38
|
+
*/
|
39
|
+
direction?: Direction;
|
40
|
+
|
41
|
+
/**
|
42
|
+
* 水平对齐方式
|
43
|
+
* @default "center"
|
44
|
+
*/
|
45
|
+
justify?: Justify;
|
46
|
+
|
47
|
+
/**
|
48
|
+
* 垂直对齐方式
|
49
|
+
* @default "center"
|
50
|
+
*/
|
51
|
+
align?: Align;
|
52
|
+
|
53
|
+
/**
|
54
|
+
* 是否允许换行
|
55
|
+
* @default false
|
56
|
+
*/
|
57
|
+
wrap?: boolean;
|
58
|
+
|
59
|
+
/**
|
60
|
+
* 元素间距
|
61
|
+
* @default "md"
|
62
|
+
*/
|
63
|
+
gap?: Gap;
|
64
|
+
|
65
|
+
/**
|
66
|
+
* 自定义类名
|
67
|
+
*/
|
68
|
+
class?: string;
|
69
|
+
|
70
|
+
/**
|
71
|
+
* 类名列表
|
72
|
+
*/
|
73
|
+
'class:list'?: any;
|
74
|
+
}
|
75
|
+
|
76
|
+
const {
|
77
|
+
direction = 'row',
|
78
|
+
justify = 'center',
|
79
|
+
align = 'center',
|
80
|
+
wrap = false,
|
81
|
+
gap = 'md',
|
82
|
+
class: className = '',
|
83
|
+
'class:list': classList,
|
84
|
+
...rest
|
85
|
+
} = Astro.props;
|
86
|
+
|
87
|
+
// 方向映射
|
88
|
+
const directionClasses: Record<Direction, string> = {
|
89
|
+
'row': 'flex-row',
|
90
|
+
'row-reverse': 'flex-row-reverse',
|
91
|
+
'column': 'flex-col',
|
92
|
+
'column-reverse': 'flex-col-reverse'
|
93
|
+
};
|
94
|
+
|
95
|
+
// 水平对齐映射
|
96
|
+
const justifyClasses: Record<Justify, string> = {
|
97
|
+
'start': 'justify-start',
|
98
|
+
'end': 'justify-end',
|
99
|
+
'center': 'justify-center',
|
100
|
+
'between': 'justify-between',
|
101
|
+
'around': 'justify-around',
|
102
|
+
'evenly': 'justify-evenly'
|
103
|
+
};
|
104
|
+
|
105
|
+
// 垂直对齐映射
|
106
|
+
const alignClasses: Record<Align, string> = {
|
107
|
+
'start': 'items-start',
|
108
|
+
'end': 'items-end',
|
109
|
+
'center': 'items-center',
|
110
|
+
'stretch': 'items-stretch',
|
111
|
+
'baseline': 'items-baseline'
|
112
|
+
};
|
113
|
+
|
114
|
+
// 间距映射
|
115
|
+
const gapClasses: Record<Gap, string> = {
|
116
|
+
'none': 'gap-0',
|
117
|
+
'xs': 'gap-2',
|
118
|
+
'sm': 'gap-4',
|
119
|
+
'md': 'gap-6',
|
120
|
+
'lg': 'gap-8',
|
121
|
+
'xl': 'gap-12'
|
122
|
+
};
|
123
|
+
|
124
|
+
// 构建最终类名
|
125
|
+
const flexClasses = [
|
126
|
+
'flex',
|
127
|
+
directionClasses[direction as Direction],
|
128
|
+
justifyClasses[justify as Justify],
|
129
|
+
alignClasses[align as Align],
|
130
|
+
wrap ? 'flex-wrap' : 'flex-nowrap',
|
131
|
+
gapClasses[gap as Gap],
|
132
|
+
className
|
133
|
+
].join(' ');
|
134
|
+
---
|
135
|
+
|
136
|
+
<div class:list={[flexClasses, classList]} {...rest}>
|
137
|
+
<slot />
|
138
|
+
</div>
|
@@ -0,0 +1,284 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Footer
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* Footer 组件用于创建网站的页脚部分,提供版权信息、导航链接和社交媒体链接等内容。
|
7
|
+
* 该组件支持高度自定义,可以根据需要显示或隐藏不同的部分。
|
8
|
+
*
|
9
|
+
* @design
|
10
|
+
* 设计理念:
|
11
|
+
* 1. 清晰的信息层次 - 通过视觉层次和分组展示不同类别的页脚信息
|
12
|
+
* 2. 响应式布局 - 在不同屏幕尺寸下保持良好的可用性和美观性
|
13
|
+
* 3. 可定制性 - 支持多种配置选项,适应不同网站的需求
|
14
|
+
* 4. 品牌一致性 - 与网站整体设计风格保持一致
|
15
|
+
*
|
16
|
+
* 视觉特点:
|
17
|
+
* - 分区布局:清晰区分不同类别的信息
|
18
|
+
* - 简洁设计:使用适当的间距和字体大小确保可读性
|
19
|
+
* - 品牌展示:突出显示网站名称和标语
|
20
|
+
* - 社交媒体整合:提供社交媒体链接的展示
|
21
|
+
*
|
22
|
+
* @usage
|
23
|
+
* 基本用法:
|
24
|
+
* ```astro
|
25
|
+
* <Footer
|
26
|
+
* siteName="我的网站"
|
27
|
+
* homeLink="/"
|
28
|
+
* slogan="简单而强大"
|
29
|
+
* company="我的公司"
|
30
|
+
* copyright="保留所有权利"
|
31
|
+
* inspirationalSlogan="构建美好的数字体验"
|
32
|
+
* />
|
33
|
+
* ```
|
34
|
+
*
|
35
|
+
* 添加社交媒体链接:
|
36
|
+
* ```astro
|
37
|
+
* <Footer
|
38
|
+
* siteName="我的网站"
|
39
|
+
* homeLink="/"
|
40
|
+
* slogan="简单而强大"
|
41
|
+
* company="我的公司"
|
42
|
+
* copyright="保留所有权利"
|
43
|
+
* inspirationalSlogan="构建美好的数字体验"
|
44
|
+
* socialLinks={[
|
45
|
+
* "https://github.com/myusername",
|
46
|
+
* "https://twitter.com/myusername"
|
47
|
+
* ]}
|
48
|
+
* />
|
49
|
+
* ```
|
50
|
+
*
|
51
|
+
* 添加产品链接:
|
52
|
+
* ```astro
|
53
|
+
* <Footer
|
54
|
+
* siteName="我的网站"
|
55
|
+
* homeLink="/"
|
56
|
+
* slogan="简单而强大"
|
57
|
+
* company="我的公司"
|
58
|
+
* copyright="保留所有权利"
|
59
|
+
* inspirationalSlogan="构建美好的数字体验"
|
60
|
+
* products={[
|
61
|
+
* { name: "产品A", href: "/products/a" },
|
62
|
+
* { name: "产品B", href: "/products/b" }
|
63
|
+
* ]}
|
64
|
+
* />
|
65
|
+
* ```
|
66
|
+
*
|
67
|
+
* 完整示例:
|
68
|
+
* ```astro
|
69
|
+
* <Footer
|
70
|
+
* siteName="我的网站"
|
71
|
+
* homeLink="/"
|
72
|
+
* slogan="简单而强大"
|
73
|
+
* company="我的公司"
|
74
|
+
* copyright="保留所有权利"
|
75
|
+
* inspirationalSlogan="构建美好的数字体验"
|
76
|
+
* logo={{ src: "/logo.png", alt: "网站Logo" }}
|
77
|
+
* products={[
|
78
|
+
* { name: "产品A", href: "/products/a" },
|
79
|
+
* { name: "产品B", href: "/products/b" }
|
80
|
+
* ]}
|
81
|
+
* socialLinks={[
|
82
|
+
* "https://github.com/myusername",
|
83
|
+
* "https://twitter.com/myusername"
|
84
|
+
* ]}
|
85
|
+
* aboutLink="/about"
|
86
|
+
* contactLink="/contact"
|
87
|
+
* termsLink="/terms"
|
88
|
+
* privacyLink="/privacy"
|
89
|
+
* />
|
90
|
+
* ```
|
91
|
+
*
|
92
|
+
* @props
|
93
|
+
* @prop {string} siteName - 网站名称
|
94
|
+
* @prop {string} homeLink - 首页链接
|
95
|
+
* @prop {string} slogan - 网站标语
|
96
|
+
* @prop {string} company - 公司名称
|
97
|
+
* @prop {string} copyright - 版权信息
|
98
|
+
* @prop {string} inspirationalSlogan - 鼓舞人心的标语,显示在横幅中
|
99
|
+
* @prop {string} [icp] - ICP备案号(中国网站需要)
|
100
|
+
* @prop {Object} [logo] - 网站Logo对象,包含src和alt属性
|
101
|
+
* @prop {Array<Object>} [products=[]] - 产品链接数组,每个对象包含name、href和可选的external属性
|
102
|
+
* @prop {string} [aboutLink] - "关于我们"页面链接
|
103
|
+
* @prop {string} [contactLink] - "联系我们"页面链接
|
104
|
+
* @prop {string} [termsLink] - "服务条款"页面链接
|
105
|
+
* @prop {string} [privacyLink] - "隐私政策"页面链接
|
106
|
+
* @prop {Array<string>} [socialLinks=[]] - 社交媒体链接数组
|
107
|
+
* @prop {string} [teamLink] - "团队介绍"页面链接
|
108
|
+
* @prop {string} [careersLink] - "加入我们"页面链接
|
109
|
+
* @prop {string} [newsLink] - "新闻动态"页面链接
|
110
|
+
* @prop {string} [historyLink] - "发展历程"页面链接
|
111
|
+
* @prop {string} [partnersLink] - "合作伙伴"页面链接
|
112
|
+
* @prop {string} [blogLink] - "技术博客"页面链接
|
113
|
+
* @prop {string} [faqLink] - "常见问题"页面链接
|
114
|
+
* @prop {string} [mediaLink] - "媒体报道"页面链接
|
115
|
+
* @prop {string} [techStackLink] - "技术栈"页面链接
|
116
|
+
* @prop {boolean} [debug=false] - 调试模式
|
117
|
+
*
|
118
|
+
* @accessibility
|
119
|
+
* - 使用语义化HTML结构,确保屏幕阅读器可以正确解析
|
120
|
+
* - 所有链接都有明确的文本描述
|
121
|
+
* - 社交媒体图标包含屏幕阅读器专用的文本说明
|
122
|
+
*/
|
123
|
+
|
124
|
+
import { processSocialLink } from '../../utils/social';
|
125
|
+
import Link from '../base/Link.astro';
|
126
|
+
import SocialIcon from '../icons/SocialIcon.astro';
|
127
|
+
import type { FooterProps } from '../../types/footer';
|
128
|
+
|
129
|
+
// 导入样式
|
130
|
+
import '../../app.css';
|
131
|
+
|
132
|
+
interface Props extends FooterProps {}
|
133
|
+
|
134
|
+
const {
|
135
|
+
siteName,
|
136
|
+
homeLink,
|
137
|
+
slogan,
|
138
|
+
company,
|
139
|
+
copyright,
|
140
|
+
inspirationalSlogan,
|
141
|
+
icp,
|
142
|
+
logo,
|
143
|
+
products = [],
|
144
|
+
aboutLink,
|
145
|
+
contactLink,
|
146
|
+
termsLink,
|
147
|
+
privacyLink,
|
148
|
+
socialLinks = [],
|
149
|
+
teamLink,
|
150
|
+
careersLink,
|
151
|
+
newsLink,
|
152
|
+
historyLink,
|
153
|
+
partnersLink,
|
154
|
+
blogLink,
|
155
|
+
faqLink,
|
156
|
+
mediaLink,
|
157
|
+
techStackLink,
|
158
|
+
debug = false,
|
159
|
+
}: Props = Astro.props;
|
160
|
+
|
161
|
+
const currentYear = new Date().getFullYear();
|
162
|
+
|
163
|
+
// 处理社交链接
|
164
|
+
const processedSocialLinks = socialLinks.map((url) => processSocialLink(url));
|
165
|
+
|
166
|
+
// 调试模式的样式类
|
167
|
+
const debugClasses = debug ? {
|
168
|
+
footer: "border-2 border-blue-500",
|
169
|
+
section: "border-2 border-green-500",
|
170
|
+
nav: "border-2 border-yellow-500",
|
171
|
+
aside: "border-2 border-purple-500"
|
172
|
+
} : {
|
173
|
+
footer: "",
|
174
|
+
section: "",
|
175
|
+
nav: "",
|
176
|
+
aside: ""
|
177
|
+
};
|
178
|
+
---
|
179
|
+
|
180
|
+
<footer class:list={["footer sm:footer-horizontal bg-base-200 text-base-content p-10", debugClasses.footer]}>
|
181
|
+
<div class:list={["flex container mx-auto sm:flex-col md:flex-row justify-between gap-8", debugClasses.section]}>
|
182
|
+
{/* 品牌区域 */}
|
183
|
+
<aside class:list={["max-w-xs", debugClasses.aside]}>
|
184
|
+
<a href={homeLink} class="flex items-center gap-2 mb-4">
|
185
|
+
{logo && (
|
186
|
+
<img
|
187
|
+
src={logo.src}
|
188
|
+
alt={logo.alt}
|
189
|
+
class="mask mask-squircle w-12 h-12"
|
190
|
+
width="48"
|
191
|
+
height="48"
|
192
|
+
/>
|
193
|
+
)}
|
194
|
+
<div>
|
195
|
+
<h2 class="text-xl font-bold">{siteName}</h2>
|
196
|
+
<p class="text-base-content/70">{slogan}</p>
|
197
|
+
</div>
|
198
|
+
</a>
|
199
|
+
|
200
|
+
{/* 社交媒体链接 */}
|
201
|
+
{processedSocialLinks.length > 0 && (
|
202
|
+
<div class="flex gap-2 mt-4">
|
203
|
+
{processedSocialLinks.map((link) => (
|
204
|
+
<a
|
205
|
+
href={link.url}
|
206
|
+
class="btn btn-circle btn-ghost btn-sm"
|
207
|
+
target="_blank"
|
208
|
+
rel="noopener noreferrer"
|
209
|
+
aria-label={link.name}
|
210
|
+
>
|
211
|
+
<SocialIcon platform={link.platform} />
|
212
|
+
</a>
|
213
|
+
))}
|
214
|
+
</div>
|
215
|
+
)}
|
216
|
+
</aside>
|
217
|
+
|
218
|
+
<div class:list={["grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-10 sm:justify-end", debugClasses.section]}>
|
219
|
+
{/* 产品导航 */}
|
220
|
+
{products.length > 0 && (
|
221
|
+
<nav class:list={["", debugClasses.nav]}>
|
222
|
+
<h6 class="footer-title">产品</h6>
|
223
|
+
{products.map((product) => (
|
224
|
+
<Link href={product.href} external={product.external} block animation='hover-lift'>
|
225
|
+
{product.name}
|
226
|
+
</Link>
|
227
|
+
))}
|
228
|
+
</nav>
|
229
|
+
)}
|
230
|
+
|
231
|
+
{/* 关于导航 */}
|
232
|
+
{(aboutLink || contactLink || teamLink || careersLink) && (
|
233
|
+
<nav class:list={["", debugClasses.nav]}>
|
234
|
+
<h6 class="footer-title">关于我们</h6>
|
235
|
+
{aboutLink && <Link href={aboutLink} block animation='hover-lift'>关于我们</Link>}
|
236
|
+
{teamLink && <Link href={teamLink} block animation='hover-lift'>团队介绍</Link>}
|
237
|
+
{careersLink && <Link href={careersLink} block animation='hover-lift'>加入我们</Link>}
|
238
|
+
{contactLink && <Link href={contactLink} block animation='hover-lift'>联系我们</Link>}
|
239
|
+
</nav>
|
240
|
+
)}
|
241
|
+
|
242
|
+
{/* 资源导航 */}
|
243
|
+
{(newsLink || historyLink || partnersLink || blogLink || faqLink || mediaLink || techStackLink) && (
|
244
|
+
<nav class:list={["", debugClasses.nav]}>
|
245
|
+
<h6 class="footer-title">资源</h6>
|
246
|
+
{newsLink && <Link href={newsLink} block animation='hover-lift'>新闻动态</Link>}
|
247
|
+
{blogLink && <Link href={blogLink} block animation='hover-lift'>技术博客</Link>}
|
248
|
+
{faqLink && <Link href={faqLink} block animation='hover-lift'>常见问题</Link>}
|
249
|
+
{techStackLink && <Link href={techStackLink} block animation='hover-lift'>技术栈</Link>}
|
250
|
+
</nav>
|
251
|
+
)}
|
252
|
+
|
253
|
+
{/* 法律导航 */}
|
254
|
+
{(termsLink || privacyLink) && (
|
255
|
+
<nav class:list={["", debugClasses.nav]}>
|
256
|
+
<h6 class="footer-title">法律</h6>
|
257
|
+
{termsLink && <Link href={termsLink} block animation='hover-lift'>服务条款</Link>}
|
258
|
+
{privacyLink && <Link href={privacyLink} block animation='hover-lift'>隐私政策</Link>}
|
259
|
+
</nav>
|
260
|
+
)}
|
261
|
+
</div>
|
262
|
+
</div>
|
263
|
+
</footer>
|
264
|
+
|
265
|
+
{/* 横幅 */}
|
266
|
+
{inspirationalSlogan && (
|
267
|
+
<aside class:list={["p-4 bg-primary text-primary-content w-full", debugClasses.aside]}>
|
268
|
+
<p class="text-center">{inspirationalSlogan}</p>
|
269
|
+
</aside>
|
270
|
+
)}
|
271
|
+
|
272
|
+
{/* 底部版权信息 */}
|
273
|
+
<div class:list={["footer footer-center p-4 bg-base-300 text-base-content", debugClasses.footer]}>
|
274
|
+
<aside class:list={["items-center grid-flow-col", debugClasses.aside]}>
|
275
|
+
<p class="text-sm opacity-70">
|
276
|
+
© {currentYear} {company} - {copyright}
|
277
|
+
</p>
|
278
|
+
{icp && (
|
279
|
+
<p class="text-sm opacity-70 ml-4">
|
280
|
+
{icp}
|
281
|
+
</p>
|
282
|
+
)}
|
283
|
+
</aside>
|
284
|
+
</div>
|
@@ -0,0 +1,182 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Grid
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* Grid 组件是一个灵活的网格布局组件,用于创建响应式的多列布局。
|
7
|
+
* 它封装了 CSS Grid 的常用功能,提供简单易用的接口来控制网格的列数和间距。
|
8
|
+
*
|
9
|
+
* @design
|
10
|
+
* 设计理念:
|
11
|
+
* 1. 简单易用 - 通过简洁的 API 控制复杂的 Grid 布局
|
12
|
+
* 2. 响应式优先 - 内置响应式断点,轻松创建适应不同屏幕尺寸的布局
|
13
|
+
* 3. 灵活可配置 - 支持自定义列数和间距
|
14
|
+
* 4. 语义化属性 - 使用直观的属性名称,降低学习成本
|
15
|
+
*
|
16
|
+
* @usage
|
17
|
+
* 基本用法:
|
18
|
+
* ```astro
|
19
|
+
* <Grid cols={3} gap="md">
|
20
|
+
* <div>第一列</div>
|
21
|
+
* <div>第二列</div>
|
22
|
+
* <div>第三列</div>
|
23
|
+
* </Grid>
|
24
|
+
* ```
|
25
|
+
*
|
26
|
+
* 响应式网格:
|
27
|
+
* ```astro
|
28
|
+
* <Grid cols={{base: 1, md: 2, lg: 3}} gap="lg">
|
29
|
+
* <div>响应式网格</div>
|
30
|
+
* <div>在不同屏幕尺寸下</div>
|
31
|
+
* <div>显示不同的列数</div>
|
32
|
+
* </Grid>
|
33
|
+
* ```
|
34
|
+
*
|
35
|
+
* 自定义行列间距:
|
36
|
+
* ```astro
|
37
|
+
* <Grid cols={2} rowGap="lg" colGap="sm">
|
38
|
+
* <div>行间距大</div>
|
39
|
+
* <div>列间距小</div>
|
40
|
+
* <div>第三项</div>
|
41
|
+
* <div>第四项</div>
|
42
|
+
* </Grid>
|
43
|
+
* ```
|
44
|
+
*
|
45
|
+
* @props
|
46
|
+
* @prop {number | Object} [cols=1] - 网格列数,可以是固定值或响应式对象
|
47
|
+
* @prop {string} [gap="md"] - 网格间距,可选值:none, xs, sm, md, lg, xl
|
48
|
+
* @prop {string} [rowGap] - 行间距,默认与 gap 相同
|
49
|
+
* @prop {string} [colGap] - 列间距,默认与 gap 相同
|
50
|
+
* @prop {string} [class] - 自定义类名
|
51
|
+
* @prop {any} [class:list] - 类名列表
|
52
|
+
*/
|
53
|
+
|
54
|
+
// 导入样式
|
55
|
+
import '../../app.css';
|
56
|
+
|
57
|
+
import type { HTMLAttributes } from 'astro/types';
|
58
|
+
|
59
|
+
type GapSize = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
60
|
+
type Breakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
61
|
+
|
62
|
+
type ResponsiveValue<T> = T | {
|
63
|
+
base?: T;
|
64
|
+
sm?: T;
|
65
|
+
md?: T;
|
66
|
+
lg?: T;
|
67
|
+
xl?: T;
|
68
|
+
'2xl'?: T;
|
69
|
+
};
|
70
|
+
|
71
|
+
interface Props extends HTMLAttributes<'div'> {
|
72
|
+
/**
|
73
|
+
* 网格列数,可以是固定值或响应式对象
|
74
|
+
* @default 1
|
75
|
+
*/
|
76
|
+
cols?: ResponsiveValue<number>;
|
77
|
+
|
78
|
+
/**
|
79
|
+
* 网格间距
|
80
|
+
* @default "md"
|
81
|
+
*/
|
82
|
+
gap?: GapSize;
|
83
|
+
|
84
|
+
/**
|
85
|
+
* 行间距,默认与gap相同
|
86
|
+
*/
|
87
|
+
rowGap?: GapSize;
|
88
|
+
|
89
|
+
/**
|
90
|
+
* 列间距,默认与gap相同
|
91
|
+
*/
|
92
|
+
colGap?: GapSize;
|
93
|
+
|
94
|
+
/**
|
95
|
+
* 自定义类名
|
96
|
+
*/
|
97
|
+
class?: string;
|
98
|
+
|
99
|
+
/**
|
100
|
+
* 类名列表
|
101
|
+
*/
|
102
|
+
'class:list'?: any;
|
103
|
+
}
|
104
|
+
|
105
|
+
const {
|
106
|
+
cols = 1,
|
107
|
+
gap = 'md',
|
108
|
+
rowGap,
|
109
|
+
colGap,
|
110
|
+
class: className = '',
|
111
|
+
'class:list': classList,
|
112
|
+
...rest
|
113
|
+
} = Astro.props;
|
114
|
+
|
115
|
+
// 处理响应式列数
|
116
|
+
const getColsClasses = (cols: ResponsiveValue<number>) => {
|
117
|
+
if (typeof cols === 'number') {
|
118
|
+
return `grid-cols-${cols}`;
|
119
|
+
}
|
120
|
+
|
121
|
+
const breakpoints: Record<Breakpoint, string> = {
|
122
|
+
base: '',
|
123
|
+
sm: 'sm:',
|
124
|
+
md: 'md:',
|
125
|
+
lg: 'lg:',
|
126
|
+
xl: 'xl:',
|
127
|
+
'2xl': '2xl:'
|
128
|
+
};
|
129
|
+
|
130
|
+
return Object.entries(cols)
|
131
|
+
.map(([breakpoint, value]) => {
|
132
|
+
if (breakpoint === 'base') {
|
133
|
+
return `grid-cols-${value}`;
|
134
|
+
}
|
135
|
+
return `${breakpoints[breakpoint as Breakpoint]}grid-cols-${value}`;
|
136
|
+
})
|
137
|
+
.join(' ');
|
138
|
+
};
|
139
|
+
|
140
|
+
// 间距映射
|
141
|
+
const gapClasses: Record<GapSize, string> = {
|
142
|
+
none: 'gap-0',
|
143
|
+
xs: 'gap-2',
|
144
|
+
sm: 'gap-4',
|
145
|
+
md: 'gap-6',
|
146
|
+
lg: 'gap-8',
|
147
|
+
xl: 'gap-12'
|
148
|
+
};
|
149
|
+
|
150
|
+
// 行间距映射
|
151
|
+
const rowGapClasses: Record<GapSize, string> = {
|
152
|
+
none: 'row-gap-0',
|
153
|
+
xs: 'row-gap-2',
|
154
|
+
sm: 'row-gap-4',
|
155
|
+
md: 'row-gap-6',
|
156
|
+
lg: 'row-gap-8',
|
157
|
+
xl: 'row-gap-12'
|
158
|
+
};
|
159
|
+
|
160
|
+
// 列间距映射
|
161
|
+
const colGapClasses: Record<GapSize, string> = {
|
162
|
+
none: 'col-gap-0',
|
163
|
+
xs: 'col-gap-2',
|
164
|
+
sm: 'col-gap-4',
|
165
|
+
md: 'col-gap-6',
|
166
|
+
lg: 'col-gap-8',
|
167
|
+
xl: 'col-gap-12'
|
168
|
+
};
|
169
|
+
|
170
|
+
// 构建最终类名
|
171
|
+
const gridClasses = [
|
172
|
+
'grid',
|
173
|
+
getColsClasses(cols),
|
174
|
+
rowGap ? rowGapClasses[rowGap as GapSize] : colGap ? gapClasses[gap as GapSize] : gapClasses[gap as GapSize],
|
175
|
+
colGap ? colGapClasses[colGap as GapSize] : null,
|
176
|
+
className
|
177
|
+
].filter(Boolean).join(' ');
|
178
|
+
---
|
179
|
+
|
180
|
+
<div class:list={[gridClasses, classList]} {...rest}>
|
181
|
+
<slot />
|
182
|
+
</div>
|