@coffic/cosy-ui 0.3.67 → 0.4.3
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/Container.astro +2 -2
- package/dist/components/containers/Main.astro +41 -54
- package/dist/components/data-display/Products.astro +197 -0
- package/dist/components/layouts/AppLayout.astro +254 -0
- package/dist/components/layouts/BaseLayout.astro +3 -52
- package/dist/components/layouts/Footer.astro +141 -113
- package/dist/components/layouts/Header.astro +10 -292
- package/dist/components/layouts/Sidebar.astro +55 -31
- package/dist/components/layouts/SidebarNav.astro +1 -11
- package/dist/components/typography/Article.astro +8 -30
- package/dist/index.ts +4 -2
- package/dist/types/article.ts +22 -0
- package/dist/types/footer.ts +119 -22
- package/dist/types/header.ts +70 -0
- package/dist/types/layout.ts +71 -10
- package/dist/types/main.ts +69 -0
- package/dist/types/meta.ts +50 -0
- package/dist/types/sidebar.ts +38 -0
- package/dist/utils/language.ts +23 -8
- package/package.json +2 -2
- package/dist/components/layouts/DocumentationLayout.astro +0 -624
@@ -0,0 +1,197 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Products
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* Products 组件是ProductCard的容器集合,用于展示多个产品卡片。
|
7
|
+
* 组件会自动根据父容器大小调整布局,确保在任何视口大小下都能整齐排列产品卡片。
|
8
|
+
* 支持网格和列表两种布局模式,并提供多种配置选项以适应不同场景需求。
|
9
|
+
*
|
10
|
+
* @design
|
11
|
+
* 设计理念:
|
12
|
+
* 1. 响应式布局 - 自动适应父容器大小,在不同设备上呈现最佳效果
|
13
|
+
* 2. 灵活配置 - 提供多种布局选项、间距设置和对齐方式
|
14
|
+
* 3. 统一展示 - 确保所有产品卡片视觉一致,提供等高选项
|
15
|
+
* 4. 性能优化 - 支持懒加载和分页显示大量产品
|
16
|
+
*
|
17
|
+
* @usage
|
18
|
+
* 基本用法:
|
19
|
+
* ```astro
|
20
|
+
* <Products
|
21
|
+
* products={[
|
22
|
+
* {
|
23
|
+
* name: "产品1",
|
24
|
+
* image: "/images/products/product1.jpg",
|
25
|
+
* description: "产品1描述",
|
26
|
+
* productUrl: "https://product1.com"
|
27
|
+
* },
|
28
|
+
* {
|
29
|
+
* name: "产品2",
|
30
|
+
* image: "/images/products/product2.jpg",
|
31
|
+
* description: "产品2描述",
|
32
|
+
* appStoreUrl: "https://apps.apple.com/app/product2"
|
33
|
+
* }
|
34
|
+
* ]}
|
35
|
+
* />
|
36
|
+
* ```
|
37
|
+
*
|
38
|
+
* 使用网格布局并设置列数:
|
39
|
+
* ```astro
|
40
|
+
* <Products
|
41
|
+
* layout="grid"
|
42
|
+
* columns={{ base: 1, sm: 2, md: 3, lg: 4 }}
|
43
|
+
* products={products}
|
44
|
+
* />
|
45
|
+
* ```
|
46
|
+
*
|
47
|
+
* 使用列表布局:
|
48
|
+
* ```astro
|
49
|
+
* <Products
|
50
|
+
* layout="list"
|
51
|
+
* products={products}
|
52
|
+
* />
|
53
|
+
* ```
|
54
|
+
*
|
55
|
+
* 自定义卡片尺寸和间距:
|
56
|
+
* ```astro
|
57
|
+
* <Products
|
58
|
+
* cardSize="sm"
|
59
|
+
* gap="lg"
|
60
|
+
* products={products}
|
61
|
+
* />
|
62
|
+
* ```
|
63
|
+
*/
|
64
|
+
|
65
|
+
import ProductCard from './ProductCard.astro';
|
66
|
+
import '../../app.css';
|
67
|
+
|
68
|
+
// 导入ProductCard的类型定义
|
69
|
+
import type { Props as ProductCardProps } from './ProductCard.astro';
|
70
|
+
|
71
|
+
// 定义产品项类型
|
72
|
+
type ProductItem = Omit<ProductCardProps, 'class'>;
|
73
|
+
|
74
|
+
// 定义响应式列数配置
|
75
|
+
type ResponsiveColumns = {
|
76
|
+
base?: number; // 默认列数
|
77
|
+
sm?: number; // 小屏幕列数
|
78
|
+
md?: number; // 中等屏幕列数
|
79
|
+
lg?: number; // 大屏幕列数
|
80
|
+
xl?: number; // 超大屏幕列数
|
81
|
+
};
|
82
|
+
|
83
|
+
// 定义间距大小
|
84
|
+
type GapSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
85
|
+
|
86
|
+
// 定义布局类型
|
87
|
+
type LayoutType = 'grid' | 'list';
|
88
|
+
|
89
|
+
export interface Props {
|
90
|
+
/**
|
91
|
+
* 产品数据数组
|
92
|
+
*/
|
93
|
+
products: ProductItem[];
|
94
|
+
/**
|
95
|
+
* 布局类型
|
96
|
+
* - grid: 网格布局(默认)
|
97
|
+
* - list: 列表布局
|
98
|
+
*/
|
99
|
+
layout?: LayoutType;
|
100
|
+
/**
|
101
|
+
* 列数配置
|
102
|
+
* 可以为固定数字或响应式对象
|
103
|
+
* 例如: { base: 1, sm: 2, md: 3, lg: 4 }
|
104
|
+
*/
|
105
|
+
columns?: number | ResponsiveColumns;
|
106
|
+
/**
|
107
|
+
* 卡片尺寸
|
108
|
+
* 应用于所有ProductCard
|
109
|
+
*/
|
110
|
+
cardSize?: ProductCardProps['size'];
|
111
|
+
/**
|
112
|
+
* 卡片间距
|
113
|
+
* - xs: 超小间距
|
114
|
+
* - sm: 小间距
|
115
|
+
* - md: 中等间距(默认)
|
116
|
+
* - lg: 大间距
|
117
|
+
* - xl: 超大间距
|
118
|
+
*/
|
119
|
+
gap?: GapSize;
|
120
|
+
/**
|
121
|
+
* 是否启用等高卡片
|
122
|
+
* 确保所有卡片高度一致
|
123
|
+
*/
|
124
|
+
equalHeight?: boolean;
|
125
|
+
/**
|
126
|
+
* 描述文本的最大行数
|
127
|
+
* 应用于所有ProductCard
|
128
|
+
*/
|
129
|
+
descriptionLines?: number;
|
130
|
+
/**
|
131
|
+
* 自定义类名
|
132
|
+
*/
|
133
|
+
class?: string;
|
134
|
+
}
|
135
|
+
|
136
|
+
const {
|
137
|
+
products,
|
138
|
+
layout = 'grid',
|
139
|
+
columns = { base: 1, sm: 2, md: 3, lg: 4 },
|
140
|
+
cardSize = 'md',
|
141
|
+
gap = 'md',
|
142
|
+
equalHeight = true,
|
143
|
+
descriptionLines,
|
144
|
+
class: className = '',
|
145
|
+
} = Astro.props;
|
146
|
+
|
147
|
+
// 间距映射
|
148
|
+
const gapMap = {
|
149
|
+
xs: 'cosy:gap-1',
|
150
|
+
sm: 'cosy:gap-2',
|
151
|
+
md: 'cosy:gap-4',
|
152
|
+
lg: 'cosy:gap-6',
|
153
|
+
xl: 'cosy:gap-8',
|
154
|
+
};
|
155
|
+
|
156
|
+
// 获取响应式列数类名
|
157
|
+
const getColumnsClasses = () => {
|
158
|
+
if (typeof columns === 'number') {
|
159
|
+
return `cosy:grid-cols-${columns}`;
|
160
|
+
}
|
161
|
+
|
162
|
+
const { base = 1, sm, md, lg, xl } = columns;
|
163
|
+
const classes = [`cosy:grid-cols-${base}`];
|
164
|
+
|
165
|
+
if (sm) classes.push(`cosy:sm:grid-cols-${sm}`);
|
166
|
+
if (md) classes.push(`cosy:md:grid-cols-${md}`);
|
167
|
+
if (lg) classes.push(`cosy:lg:grid-cols-${lg}`);
|
168
|
+
if (xl) classes.push(`cosy:xl:grid-cols-${xl}`);
|
169
|
+
|
170
|
+
return classes.join(' ');
|
171
|
+
};
|
172
|
+
|
173
|
+
// 获取布局类名
|
174
|
+
const getLayoutClasses = () => {
|
175
|
+
if (layout === 'grid') {
|
176
|
+
return ['cosy:grid', getColumnsClasses()];
|
177
|
+
} else {
|
178
|
+
return ['cosy:flex', 'cosy:flex-col'];
|
179
|
+
}
|
180
|
+
};
|
181
|
+
|
182
|
+
// 构建容器类名
|
183
|
+
const containerClasses = [...getLayoutClasses(), gapMap[gap], 'cosy:w-full', className];
|
184
|
+
---
|
185
|
+
|
186
|
+
<div class:list={containerClasses}>
|
187
|
+
{
|
188
|
+
products.map((product) => (
|
189
|
+
<ProductCard
|
190
|
+
{...product}
|
191
|
+
size={cardSize}
|
192
|
+
equalHeight={equalHeight}
|
193
|
+
descriptionLines={descriptionLines}
|
194
|
+
/>
|
195
|
+
))
|
196
|
+
}
|
197
|
+
</div>
|
@@ -0,0 +1,254 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* AppLayout组件
|
4
|
+
*
|
5
|
+
* 适用于页面布局,包含侧边栏导航和目录
|
6
|
+
*
|
7
|
+
* 布局效果:
|
8
|
+
*
|
9
|
+
* 移动端:
|
10
|
+
* ```
|
11
|
+
* +------------------+
|
12
|
+
* | Header |
|
13
|
+
* +------------------+
|
14
|
+
* | Sidebar (1 line) |
|
15
|
+
* +------------------+
|
16
|
+
* | |
|
17
|
+
* | Main Content |
|
18
|
+
* | |
|
19
|
+
* | |
|
20
|
+
* +------------------+
|
21
|
+
* | Footer |
|
22
|
+
* +------------------+
|
23
|
+
* ```
|
24
|
+
*
|
25
|
+
* 桌面端:
|
26
|
+
* ```
|
27
|
+
* +------------------+
|
28
|
+
* | Header |
|
29
|
+
* +--------+---------+
|
30
|
+
* | | |
|
31
|
+
* |Sidebar | Content |
|
32
|
+
* | | |
|
33
|
+
* | | |
|
34
|
+
* +--------+---------+
|
35
|
+
* | Footer |
|
36
|
+
* +------------------+
|
37
|
+
* ```
|
38
|
+
*
|
39
|
+
* @param {Object} sidebarConfig - 侧边栏配置
|
40
|
+
* @param {boolean} [showHeader=true] - 是否显示头部
|
41
|
+
* @param {boolean} [showFooter=true] - 是否显示页脚
|
42
|
+
* @param {boolean} [showSidebar=true] - 是否显示侧边栏
|
43
|
+
* @param {string} [className] - 自定义类名
|
44
|
+
* @param {Array} [classList] - 自定义类名列表
|
45
|
+
* @param {boolean} [debug=true] - 是否启用调试模式
|
46
|
+
* @param {Object} mainContentConfig - 主内容区域配置
|
47
|
+
* @param {Object} footerConfig - 页脚配置
|
48
|
+
* @param {Object} headerConfig - 头部配置
|
49
|
+
* @param {Object} metaConfig - 元数据配置
|
50
|
+
* @param {Object} rest - 其他属性
|
51
|
+
*
|
52
|
+
* @example
|
53
|
+
* ```astro
|
54
|
+
* ---
|
55
|
+
* import AppLayout from '../layouts/AppLayout.astro';
|
56
|
+
*
|
57
|
+
* const sidebarItems = [
|
58
|
+
* { title: "入门", items: [
|
59
|
+
* { href: "/docs/getting-started", text: "快速开始" },
|
60
|
+
* { href: "/docs/installation", text: "安装" }
|
61
|
+
* ]},
|
62
|
+
* { title: "组件", items: [
|
63
|
+
* { href: "/docs/components/button", text: "Button 按钮" },
|
64
|
+
* { href: "/docs/components/card", text: "Card 卡片" }
|
65
|
+
* ]}
|
66
|
+
* ];
|
67
|
+
* ---
|
68
|
+
*
|
69
|
+
* <AppLayout
|
70
|
+
* metaConfig={{
|
71
|
+
* title: "文档标题",
|
72
|
+
* description: "文档描述"
|
73
|
+
* }}
|
74
|
+
* sidebarConfig={{
|
75
|
+
* sidebarItems: sidebarItems
|
76
|
+
* }}
|
77
|
+
* >
|
78
|
+
* <h1>文档内容</h1>
|
79
|
+
* <p>这是文档的主要内容</p>
|
80
|
+
* </AppLayout>
|
81
|
+
* ```
|
82
|
+
*
|
83
|
+
* 自定义页脚示例:
|
84
|
+
* ```astro
|
85
|
+
* <AppLayout
|
86
|
+
* metaConfig={{
|
87
|
+
* title: "文档标题",
|
88
|
+
* description: "文档描述"
|
89
|
+
* }}
|
90
|
+
* sidebarConfig={{
|
91
|
+
* sidebarItems: sidebarItems
|
92
|
+
* }}
|
93
|
+
* footerConfig={{
|
94
|
+
* slogan: "简单而强大的组件库",
|
95
|
+
* inspirationalSlogan: "让开发更加愉悦",
|
96
|
+
* socialLinks: [
|
97
|
+
* "https://github.com/myorg/myrepo",
|
98
|
+
* "https://twitter.com/myorg"
|
99
|
+
* ],
|
100
|
+
* products: [
|
101
|
+
* { name: "组件库", href: "/components" },
|
102
|
+
* { name: "模板", href: "/templates" }
|
103
|
+
* ]
|
104
|
+
* }}
|
105
|
+
* >
|
106
|
+
* <h1>文档内容</h1>
|
107
|
+
* <p>这是文档的主要内容</p>
|
108
|
+
* </AppLayout>
|
109
|
+
* ```
|
110
|
+
*
|
111
|
+
* 组件支持多种页脚相关的配置参数,可以通过以 `footer` 为前缀的属性来自定义页脚的内容和链接。
|
112
|
+
* 所有这些参数都是可选的,组件会为常用参数提供默认值。
|
113
|
+
*
|
114
|
+
* 全宽内容区域示例:
|
115
|
+
* ```astro
|
116
|
+
* <AppLayout
|
117
|
+
* metaConfig={{
|
118
|
+
* title: "文档标题",
|
119
|
+
* description: "文档描述"
|
120
|
+
* }}
|
121
|
+
* sidebarConfig={{
|
122
|
+
* sidebarItems: sidebarItems
|
123
|
+
* }}
|
124
|
+
* mainContentConfig={{
|
125
|
+
* fullWidth: true
|
126
|
+
* }}
|
127
|
+
* >
|
128
|
+
* <!-- 全宽Hero部分,无需容器限制 -->
|
129
|
+
* <div class="cosy:bg-primary cosy:p-10 cosy:text-white cosy:text-center">
|
130
|
+
* <h1 class="cosy:text-3xl">全宽Hero部分</h1>
|
131
|
+
* <p class="cosy:mt-4">没有容器限制,宽度可以100%占满</p>
|
132
|
+
* </div>
|
133
|
+
*
|
134
|
+
* <!-- 自定义容器部分 -->
|
135
|
+
* <div class="cosy:mx-auto cosy:p-6 cosy:container">
|
136
|
+
* <p>在全宽模式下,您可以自行控制内容的容器和间距</p>
|
137
|
+
* <p>这使得创建全宽背景的同时,保持内容在合适的宽度内</p>
|
138
|
+
* </div>
|
139
|
+
*
|
140
|
+
* <!-- 另一个全宽部分 -->
|
141
|
+
* <div class="cosy:bg-accent cosy:mt-8 cosy:p-10">
|
142
|
+
* <div class="cosy:mx-auto cosy:container">
|
143
|
+
* <h2 class="cosy:text-2xl">灵活的布局</h2>
|
144
|
+
* <p>您可以自由组合全宽区域和容器限制区域</p>
|
145
|
+
* </div>
|
146
|
+
* </div>
|
147
|
+
* </AppLayout>
|
148
|
+
* ```
|
149
|
+
*
|
150
|
+
* 调试模式示例:
|
151
|
+
* ```astro
|
152
|
+
* <AppLayout
|
153
|
+
* metaConfig={{
|
154
|
+
* title: "文档标题",
|
155
|
+
* description: "文档描述"
|
156
|
+
* }}
|
157
|
+
* sidebarConfig={{
|
158
|
+
* sidebarItems: sidebarItems
|
159
|
+
* }}
|
160
|
+
* debug={true}
|
161
|
+
* >
|
162
|
+
* <h1>文档内容</h1>
|
163
|
+
* <p>这是文档的主要内容</p>
|
164
|
+
* </AppLayout>
|
165
|
+
* ```
|
166
|
+
*/
|
167
|
+
|
168
|
+
import '../../app.css';
|
169
|
+
import BaseLayout from './BaseLayout.astro';
|
170
|
+
import Footer from './Footer.astro';
|
171
|
+
import Main from '../containers/Main.astro';
|
172
|
+
import Header from './Header.astro';
|
173
|
+
import Sidebar from './Sidebar.astro';
|
174
|
+
import { ClientRouter } from 'astro:transitions';
|
175
|
+
import type { AppLayoutProps } from '@/types/layout';
|
176
|
+
|
177
|
+
interface Props extends AppLayoutProps {}
|
178
|
+
|
179
|
+
const {
|
180
|
+
sidebarConfig,
|
181
|
+
showHeader = true,
|
182
|
+
showFooter = true,
|
183
|
+
showSidebar = true,
|
184
|
+
class: className,
|
185
|
+
'class:list': classList,
|
186
|
+
debug = true,
|
187
|
+
mainContentConfig,
|
188
|
+
footerConfig,
|
189
|
+
headerConfig,
|
190
|
+
metaConfig,
|
191
|
+
...rest
|
192
|
+
}: Props = Astro.props;
|
193
|
+
---
|
194
|
+
|
195
|
+
<BaseLayout
|
196
|
+
title={metaConfig.title}
|
197
|
+
description={metaConfig.description}
|
198
|
+
keywords={metaConfig.keywords}
|
199
|
+
author={metaConfig.author}
|
200
|
+
robots={metaConfig.robots}
|
201
|
+
head={metaConfig.head}
|
202
|
+
class:list={[
|
203
|
+
'cosy:min-h-screen cosy:flex cosy:flex-col',
|
204
|
+
{ 'cosy:border cosy:border-base-300': debug },
|
205
|
+
]}
|
206
|
+
{...rest}>
|
207
|
+
{
|
208
|
+
showHeader && (
|
209
|
+
<Header
|
210
|
+
logo={headerConfig.logo}
|
211
|
+
logoHref={headerConfig.logoHref}
|
212
|
+
navItems={headerConfig.navItems}
|
213
|
+
languages={headerConfig.languages}
|
214
|
+
currentLocale={headerConfig.currentLocale}
|
215
|
+
sticky={headerConfig.sticky}
|
216
|
+
basePath={headerConfig.basePath}
|
217
|
+
showSidebarToggle={headerConfig.showSidebarToggle}
|
218
|
+
defaultSidebarOpen={headerConfig.defaultSidebarOpen}
|
219
|
+
height={headerConfig.height}
|
220
|
+
transition:persist
|
221
|
+
/>
|
222
|
+
)
|
223
|
+
}
|
224
|
+
|
225
|
+
<div class="cosy:flex cosy:flex-row cosy:flex-1 cosy:pb-0 cosy:min-h-screen">
|
226
|
+
<!-- 侧边栏容器 -->
|
227
|
+
{showSidebar && <Sidebar {...sidebarConfig} />}
|
228
|
+
|
229
|
+
<!-- 主内容区域 -->
|
230
|
+
<Main {...mainContentConfig}>
|
231
|
+
<slot />
|
232
|
+
<ClientRouter />
|
233
|
+
</Main>
|
234
|
+
</div>
|
235
|
+
|
236
|
+
<!-- Footer -->
|
237
|
+
{showFooter && <Footer {...footerConfig} />}
|
238
|
+
|
239
|
+
<script>
|
240
|
+
// Handle sidebar toggle
|
241
|
+
const sidebarToggle = document.getElementById('sidebar-toggle');
|
242
|
+
const sidebar = document.getElementById('sidebar-mobile');
|
243
|
+
const sidebarOverlay = document.getElementById('sidebar-overlay');
|
244
|
+
|
245
|
+
function toggleSidebar() {
|
246
|
+
sidebar?.classList.toggle('cosy:hidden');
|
247
|
+
sidebarOverlay?.classList.toggle('cosy:hidden');
|
248
|
+
document.body.classList.toggle('cosy:overflow-hidden');
|
249
|
+
}
|
250
|
+
|
251
|
+
sidebarToggle?.addEventListener('click', toggleSidebar);
|
252
|
+
sidebarOverlay?.addEventListener('click', toggleSidebar);
|
253
|
+
</script>
|
254
|
+
</BaseLayout>
|
@@ -49,59 +49,10 @@
|
|
49
49
|
* ```
|
50
50
|
*/
|
51
51
|
|
52
|
-
// 导入样式
|
53
52
|
import '../../app.css';
|
53
|
+
import type { MetaProps } from '@/types/meta';
|
54
54
|
|
55
|
-
export interface Props {
|
56
|
-
/**
|
57
|
-
* 页面标题
|
58
|
-
*/
|
59
|
-
title: string;
|
60
|
-
|
61
|
-
/**
|
62
|
-
* 页面描述
|
63
|
-
* @default ""
|
64
|
-
*/
|
65
|
-
description?: string;
|
66
|
-
|
67
|
-
/**
|
68
|
-
* 页面关键词
|
69
|
-
* @default ""
|
70
|
-
*/
|
71
|
-
keywords?: string;
|
72
|
-
|
73
|
-
/**
|
74
|
-
* 页面语言
|
75
|
-
* @default "zh-CN"
|
76
|
-
*/
|
77
|
-
lang?: string;
|
78
|
-
|
79
|
-
/**
|
80
|
-
* 是否包含视口元标签
|
81
|
-
* @default true
|
82
|
-
*/
|
83
|
-
viewport?: boolean;
|
84
|
-
|
85
|
-
/**
|
86
|
-
* 自定义CSS
|
87
|
-
*/
|
88
|
-
customStyles?: string;
|
89
|
-
|
90
|
-
/**
|
91
|
-
* 自定义头部内容
|
92
|
-
*/
|
93
|
-
head?: astroHTML.JSX.Element;
|
94
|
-
|
95
|
-
/**
|
96
|
-
* 页面类名
|
97
|
-
*/
|
98
|
-
class?: string;
|
99
|
-
|
100
|
-
/**
|
101
|
-
* 类名列表
|
102
|
-
*/
|
103
|
-
'class:list'?: any;
|
104
|
-
}
|
55
|
+
export interface Props extends MetaProps {}
|
105
56
|
|
106
57
|
const {
|
107
58
|
title,
|
@@ -141,4 +92,4 @@ let bodyClasses = className || "";
|
|
141
92
|
<body class:list={[bodyClasses, classList]}>
|
142
93
|
<slot />
|
143
94
|
</body>
|
144
|
-
</html>
|
95
|
+
</html>
|