@coffic/cosy-ui 0.3.39 → 0.3.45

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,40 +1,40 @@
1
1
  ---
2
2
  /**
3
3
  * @component Banner
4
- *
4
+ *
5
5
  * @description
6
6
  * Banner 组件用于在页面中展示醒目的横幅信息,通常用于展示重要的标语、公告或营销信息。
7
7
  * 组件设计简洁大方,具有平滑的动画效果,能够吸引用户注意力。
8
- *
8
+ *
9
9
  * @design
10
10
  * 设计理念:
11
11
  * 1. 视觉冲击力 - 使用大号字体和醒目的背景,确保信息能够被用户注意到
12
12
  * 2. 简洁明了 - 简单的布局和充足的留白,让信息更容易被理解
13
13
  * 3. 动态效果 - 使用淡入和上滑动画,增强用户体验
14
14
  * 4. 响应式设计 - 在不同屏幕尺寸下保持良好的可读性
15
- *
15
+ *
16
16
  * 视觉特点:
17
17
  * - 圆角设计:柔和的视觉效果
18
18
  * - 悬停效果:轻微放大和阴影增强,提供交互反馈
19
19
  * - 动画效果:淡入和上滑动画,增强视觉吸引力
20
20
  * - 模糊背景:半透明背景配合模糊效果,现代感强
21
- *
21
+ *
22
22
  * @usage
23
23
  * 基本用法:
24
24
  * ```astro
25
25
  * <Banner>欢迎使用我们的服务</Banner>
26
26
  * ```
27
- *
27
+ *
28
28
  * 在页脚中使用:
29
29
  * ```astro
30
30
  * <footer>
31
31
  * <Banner>构建美好的数字体验</Banner>
32
32
  * </footer>
33
33
  * ```
34
- *
34
+ *
35
35
  * @slots
36
36
  * @slot default - Banner 中显示的文本内容
37
- *
37
+ *
38
38
  * @accessibility
39
39
  * - 使用适当的字体大小和行高,确保文本可读性
40
40
  * - 动画遵循 prefers-reduced-motion 媒体查询,尊重用户的动画偏好设置
@@ -42,16 +42,104 @@
42
42
 
43
43
  // 导入样式
44
44
  import '../../app.css';
45
+
46
+ interface Props {
47
+ /**
48
+ * 自定义CSS类,可用于覆盖默认样式
49
+ */
50
+ class?: string;
51
+
52
+ /**
53
+ * 背景颜色,默认为primary
54
+ */
55
+ bgColor?: 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error';
56
+
57
+ /**
58
+ * 文本颜色,默认根据背景色自动设置
59
+ */
60
+ textColor?: string;
61
+
62
+ /**
63
+ * 是否启用动画效果,默认为true
64
+ */
65
+ animated?: boolean;
66
+ }
67
+
68
+ const { class: className = '', bgColor = 'primary', textColor = '', animated = true } = Astro.props;
69
+
70
+ // 根据背景色自动设置文本颜色
71
+ const getTextColorClass = () => {
72
+ if (textColor) return `cosy:text-${textColor}`;
73
+
74
+ // 对于深色背景,使用白色文本
75
+ const darkBgColors = ['primary', 'secondary', 'accent', 'error'];
76
+ return darkBgColors.includes(bgColor) ? 'cosy:text-white' : 'cosy:text-gray-800';
77
+ };
78
+
79
+ // 组合CSS类
80
+ const bannerClasses = [
81
+ 'cosy:w-full',
82
+ 'cosy:py-8',
83
+ 'cosy:px-4',
84
+ 'cosy:rounded-lg',
85
+ 'cosy:text-center',
86
+ 'cosy:text-2xl',
87
+ 'cosy:md:text-3xl',
88
+ 'cosy:font-bold',
89
+ 'cosy:transition-all',
90
+ 'cosy:duration-300',
91
+ 'cosy:ease-in-out',
92
+ 'cosy:backdrop-blur-sm',
93
+ 'cosy:bg-opacity-90',
94
+ 'cosy:shadow-md',
95
+ 'cosy:hover:shadow-lg',
96
+ 'cosy:hover:scale-[1.01]',
97
+ `cosy:bg-${bgColor}`,
98
+ getTextColorClass(),
99
+ className,
100
+ ].join(' ');
101
+
102
+ // 动画类
103
+ const animationClasses = animated ? 'cosy:animate-fadeIn cosy:animate-slideUp' : '';
45
104
  ---
46
105
 
47
- <div class="banner-container">
48
- <div class="banner">
49
- <div class="banner-content">
50
- <div class="banner-text">
51
- <p class="banner-message">
52
- <slot />
53
- </p>
54
- </div>
55
- </div>
56
- </div>
106
+ <div class={`${bannerClasses} ${animationClasses}`}>
107
+ <slot />
57
108
  </div>
109
+
110
+ <style>
111
+ @keyframes fadeIn {
112
+ from {
113
+ opacity: 0;
114
+ }
115
+ to {
116
+ opacity: 1;
117
+ }
118
+ }
119
+
120
+ @keyframes slideUp {
121
+ from {
122
+ transform: translateY(20px);
123
+ }
124
+ to {
125
+ transform: translateY(0);
126
+ }
127
+ }
128
+
129
+ /* 动画类 */
130
+ .cosy\:animate-fadeIn {
131
+ animation: fadeIn 0.8s ease-in-out;
132
+ }
133
+
134
+ .cosy\:animate-slideUp {
135
+ animation: slideUp 0.5s ease-out;
136
+ }
137
+
138
+ /* 尊重用户的减少动画偏好 */
139
+ @media (prefers-reduced-motion: reduce) {
140
+ .cosy\:animate-fadeIn,
141
+ .cosy\:animate-slideUp {
142
+ animation: none;
143
+ }
144
+ }
145
+ </style>
@@ -1,66 +1,67 @@
1
1
  ---
2
2
  /**
3
3
  * @component Card
4
- *
4
+ *
5
5
  * @description
6
6
  * Card 组件用于在页面中展示相关内容的容器,通常包含标题、描述和可选的图片。
7
7
  * 组件设计简洁大方,具有平滑的动画效果和交互反馈,适合展示产品、文章或功能介绍等内容。
8
- *
8
+ *
9
9
  * @design
10
10
  * 设计理念:
11
11
  * 1. 视觉层次感 - 通过阴影、悬停效果和微妙的动画创造深度感
12
12
  * 2. 内容聚焦 - 简洁的布局让用户关注卡片内容
13
13
  * 3. 交互反馈 - 悬停和点击时提供明确的视觉反馈
14
14
  * 4. 适应性强 - 响应式设计,适应不同屏幕尺寸
15
- *
15
+ *
16
16
  * 视觉特点:
17
17
  * - 半透明背景:现代感强,适合叠加在各种背景上
18
18
  * - 模糊效果:背景模糊增强内容可读性
19
19
  * - 光效设计:顶部有微妙的光晕效果,增强立体感
20
20
  * - 悬停动画:轻微放大和阴影增强,提供交互反馈
21
21
  * - 图片缩放:图片在悬停时轻微放大,增强视觉吸引力
22
- *
22
+ *
23
23
  * @usage
24
24
  * 基本用法:
25
25
  * ```astro
26
- * <Card
27
- * title="卡片标题"
26
+ * <Card
27
+ * title="卡片标题"
28
28
  * subtitle="卡片描述文本"
29
29
  * >
30
30
  * 卡片内容
31
31
  * </Card>
32
32
  * ```
33
- *
33
+ *
34
34
  * 带图片的卡片:
35
35
  * ```astro
36
- * <Card
37
- * title="产品名称"
38
- * subtitle="产品描述"
36
+ * <Card
37
+ * title="产品名称"
38
+ * subtitle="产品描述"
39
39
  * imageUrl="/path/to/image.jpg"
40
40
  * >
41
41
  * 其他产品信息
42
42
  * </Card>
43
43
  * ```
44
- *
44
+ *
45
45
  * 可点击的卡片(链接):
46
46
  * ```astro
47
- * <Card
48
- * title="文章标题"
49
- * subtitle="文章摘要"
47
+ * <Card
48
+ * title="文章标题"
49
+ * subtitle="文章摘要"
50
50
  * imageUrl="/path/to/image.jpg"
51
51
  * href="/articles/article-slug"
52
52
  * />
53
53
  * ```
54
- *
54
+ *
55
55
  * @props
56
56
  * @prop {string} [title] - 卡片标题
57
57
  * @prop {string} [subtitle] - 卡片副标题或描述
58
58
  * @prop {string} [imageUrl] - 卡片顶部图片的URL
59
59
  * @prop {string} [href] - 如果提供,卡片将变成可点击的链接
60
- *
60
+ * @prop {string} [class] - 自定义CSS类,可用于覆盖默认样式
61
+ *
61
62
  * @slots
62
63
  * @slot default - 卡片的主要内容区域
63
- *
64
+ *
64
65
  * @accessibility
65
66
  * - 使用语义化HTML结构
66
67
  * - 当作为链接使用时,确保提供有意义的文本内容
@@ -71,65 +72,95 @@
71
72
  import '../../app.css';
72
73
 
73
74
  interface Props {
74
- title?: string;
75
- subtitle?: string;
76
- imageUrl?: string;
77
- href?: string;
75
+ title?: string;
76
+ subtitle?: string;
77
+ imageUrl?: string;
78
+ href?: string;
79
+ class?: string;
78
80
  }
79
81
 
80
- const { title, subtitle, imageUrl, href } = Astro.props;
82
+ const { title, subtitle, imageUrl, href, class: className = '' } = Astro.props;
83
+
84
+ // 定义动画的CSS类
85
+ const fadeInAnimation = 'cosy:animate-fadeIn';
86
+
87
+ // 定义卡片的CSS类
88
+ const cardClasses = [
89
+ 'cosy:bg-base-100',
90
+ 'cosy:bg-opacity-70',
91
+ 'cosy:backdrop-blur-sm',
92
+ 'cosy:rounded-xl',
93
+ 'cosy:shadow-md',
94
+ 'cosy:overflow-hidden',
95
+ 'cosy:transition-all',
96
+ 'cosy:duration-300',
97
+ 'cosy:ease-in-out',
98
+ 'cosy:hover:shadow-lg',
99
+ 'cosy:hover:scale-[1.02]',
100
+ className,
101
+ ].join(' ');
102
+
103
+ // 定义卡片内容的CSS类
104
+ const cardBodyClasses = [
105
+ 'cosy:p-6',
106
+ 'cosy:flex',
107
+ 'cosy:flex-col',
108
+ 'cosy:gap-4',
109
+ href && 'cosy:cursor-pointer',
110
+ ]
111
+ .filter(Boolean)
112
+ .join(' ');
113
+
114
+ // 定义图片容器的CSS类
115
+ const figureClasses = 'cosy:overflow-hidden cosy:mb-4 cosy:-mx-6 cosy:-mt-6';
116
+
117
+ // 定义图片的CSS类
118
+ const imageClasses =
119
+ 'cosy:w-full cosy:h-auto cosy:object-cover cosy:transition-transform cosy:duration-300 cosy:group-hover:scale-105';
120
+
121
+ // 定义标题的CSS类
122
+ const titleClasses = 'cosy:text-xl cosy:font-bold cosy:mb-2';
123
+
124
+ // 定义描述的CSS类
125
+ const subtitleClasses = 'cosy:text-base cosy:text-gray-600 cosy:dark:text-gray-300';
81
126
  ---
82
127
 
83
- <div class="group perspective-1000">
84
- <div class:list={[
85
- "card",
86
- href && "cursor-pointer",
87
- "motion-safe:animate-fadeIn"
88
- ]}>
89
- {href ? (
90
- <a href={href} class="card-body">
91
- {imageUrl && (
92
- <figure>
93
- <img
94
- src={imageUrl}
95
- alt={title}
96
- />
97
- </figure>
98
- )}
99
- {title && (
100
- <h2 class="card-title">
101
- {title}
102
- </h2>
103
- )}
104
- {subtitle && (
105
- <p>
106
- {subtitle}
107
- </p>
108
- )}
109
- <slot />
110
- </a>
111
- ) : (
112
- <div class="card-body">
113
- {imageUrl && (
114
- <figure>
115
- <img
116
- src={imageUrl}
117
- alt={title}
118
- />
119
- </figure>
120
- )}
121
- {title && (
122
- <h2 class="card-title">
123
- {title}
124
- </h2>
125
- )}
126
- {subtitle && (
127
- <p>
128
- {subtitle}
129
- </p>
130
- )}
131
- <slot />
132
- </div>
133
- )}
134
- </div>
135
- </div>
128
+ <div class="cosy:group cosy:perspective-1000">
129
+ <div class={cardClasses}>
130
+ {
131
+ href ? (
132
+ <a href={href} class={cardBodyClasses}>
133
+ {imageUrl && (
134
+ <figure class={figureClasses}>
135
+ <img src={imageUrl} alt={title} class={imageClasses} />
136
+ </figure>
137
+ )}
138
+ {title && <h2 class={titleClasses}>{title}</h2>}
139
+ {subtitle && <p class={subtitleClasses}>{subtitle}</p>}
140
+ <slot />
141
+ </a>
142
+ ) : (
143
+ <div class={cardBodyClasses}>
144
+ {imageUrl && (
145
+ <figure class={figureClasses}>
146
+ <img src={imageUrl} alt={title} class={imageClasses} />
147
+ </figure>
148
+ )}
149
+ {title && <h2 class={titleClasses}>{title}</h2>}
150
+ {subtitle && <p class={subtitleClasses}>{subtitle}</p>}
151
+ <slot />
152
+ </div>
153
+ )
154
+ }
155
+ </div>
156
+ </div>
157
+
158
+ <style>
159
+ /* 为了确保prefers-reduced-motion生效 */
160
+ @media (prefers-reduced-motion: reduce) {
161
+ .cosy\:hover\:scale-\[1\.02\]:hover,
162
+ .cosy\:group-hover\:scale-105 {
163
+ transform: none !important;
164
+ }
165
+ }
166
+ </style>
@@ -1,46 +1,46 @@
1
1
  ---
2
2
  /**
3
3
  * @component CodeBlock
4
- *
4
+ *
5
5
  * @description
6
6
  * CodeBlock 组件用于在页面中展示格式化的代码片段,支持语法高亮、行号显示和代码复制功能。
7
7
  * 组件设计简洁美观,提供良好的代码可读性和用户交互体验。
8
- *
8
+ *
9
9
  * @design
10
10
  * 设计理念:
11
11
  * 1. 可读性优先 - 清晰的字体、合适的间距和语法高亮,确保代码易于阅读
12
12
  * 2. 功能完备 - 支持行号显示、代码复制等实用功能
13
13
  * 3. 视觉一致性 - 与整体设计系统保持一致的视觉风格
14
14
  * 4. 响应式设计 - 在不同屏幕尺寸下保持良好的可用性
15
- *
15
+ *
16
16
  * 视觉特点:
17
17
  * - 语法高亮:使用北欧风格的配色方案,柔和且易于阅读
18
18
  * - 行号显示:可选的行号显示,帮助定位代码位置
19
19
  * - 标题栏:可选的标题栏,显示文件名或代码类型
20
20
  * - 复制按钮:便捷的代码复制功能,带有状态反馈
21
- *
21
+ *
22
22
  * @usage
23
23
  * 基本用法:
24
24
  * ```astro
25
25
  * <CodeBlock code={`console.log('Hello, world!');`} lang="js" />
26
26
  * ```
27
- *
27
+ *
28
28
  * 带标题和行号:
29
29
  * ```astro
30
- * <CodeBlock
30
+ * <CodeBlock
31
31
  * code={`function greet(name) {\n return 'Hello, ' + name + '!';\n}`}
32
32
  * lang="js"
33
33
  * title="greeting.js"
34
34
  * showLineNumbers={true}
35
35
  * />
36
36
  * ```
37
- *
37
+ *
38
38
  * @props
39
39
  * @prop {string} code - 要显示的代码字符串
40
40
  * @prop {string} [lang="plaintext"] - 代码语言,用于语法高亮
41
41
  * @prop {string} [title] - 代码块的标题,通常是文件名
42
42
  * @prop {boolean} [showLineNumbers=true] - 是否显示行号
43
- *
43
+ *
44
44
  * @accessibility
45
45
  * - 使用语义化HTML结构
46
46
  * - 复制按钮提供清晰的视觉反馈
@@ -51,18 +51,13 @@
51
51
  import '../../app.css';
52
52
 
53
53
  interface Props {
54
- code: string;
55
- lang?: string;
56
- title?: string;
57
- showLineNumbers?: boolean;
54
+ code: string;
55
+ lang?: string;
56
+ title?: string;
57
+ showLineNumbers?: boolean;
58
58
  }
59
59
 
60
- const {
61
- code,
62
- lang = 'plaintext',
63
- title,
64
- showLineNumbers = true,
65
- } = Astro.props;
60
+ const { code, lang = 'plaintext', title, showLineNumbers = true } = Astro.props;
66
61
 
67
62
  // 移除代码字符串开头和结尾的空行
68
63
  const trimmedCode = code.trim();
@@ -72,76 +67,88 @@ const lines = trimmedCode.split('\n');
72
67
  const lineNumbers = Array.from({ length: lines.length }, (_, i) => i + 1);
73
68
  ---
74
69
 
75
- <div class="code-block not-prose">
76
- {/* 标题栏 */}
77
- {title && (
78
- <div class="code-block-header">
79
- <span class="code-block-title">{title}</span>
80
- <button
81
- class="copy-button"
82
- data-code={trimmedCode}
83
- title="复制代码"
84
- >
85
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">
86
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
87
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
88
- </svg>
89
- <span class="copy-text">复制</span>
90
- </button>
91
- </div>
92
- )}
70
+ <div class="cosy:border cosy:border-base-300 cosy:rounded-lg cosy:overflow-hidden cosy:not-prose">
71
+ {/* 标题栏 */}
72
+ {
73
+ title && (
74
+ <div class="cosy:flex cosy:justify-between cosy:items-center cosy:bg-base-200 cosy:px-4 cosy:py-2 cosy:border-b cosy:border-base-300">
75
+ <span class="cosy:font-medium cosy:text-base-content">{title}</span>
76
+ <button
77
+ class="cosy:flex cosy:items-center cosy:gap-1 cosy:hover:bg-base-300 cosy:px-2 cosy:py-1 cosy:rounded-md cosy:text-sm cosy:text-base-content cosy:transition-colors"
78
+ data-code={trimmedCode}
79
+ title="复制代码">
80
+ <svg
81
+ class="cosy:w-4 cosy:h-4"
82
+ xmlns="http://www.w3.org/2000/svg"
83
+ viewBox="0 0 24 24"
84
+ fill="none"
85
+ stroke="currentColor">
86
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
87
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
88
+ </svg>
89
+ <span class="cosy:copy-text">复制</span>
90
+ </button>
91
+ </div>
92
+ )
93
+ }
93
94
 
94
- {/* 代码区域 */}
95
- <div class="code-content">
96
- {/* 行号 */}
97
- {showLineNumbers && (
98
- <div class="line-numbers">
99
- {lineNumbers.map(num => (
100
- <div class="line-number">{num}</div>
101
- ))}
102
- </div>
103
- )}
95
+ {/* 代码区域 */}
96
+ <div class="cosy:flex cosy:overflow-auto">
97
+ {/* 行号 */}
98
+ {
99
+ showLineNumbers && (
100
+ <div class="cosy:flex cosy:flex-col cosy:bg-base-100 cosy:py-2 cosy:pr-2 cosy:pl-3 cosy:border-r cosy:border-base-300 cosy:text-base-content cosy:text-right cosy:text-opacity-50 cosy:select-none">
101
+ {lineNumbers.map((num) => (
102
+ <div class="cosy:font-mono cosy:text-xs cosy:leading-5">{num}</div>
103
+ ))}
104
+ </div>
105
+ )
106
+ }
104
107
 
105
- {/* 代码内容 */}
106
- <pre class:list={["language-" + lang, { "with-line-numbers": showLineNumbers }]}>
107
- <code class={"language-" + lang} set:html={trimmedCode} />
108
+ {/* 代码内容 */}
109
+ <pre
110
+ class:list={[
111
+ 'cosy:p-2 cosy:overflow-auto cosy:font-mono cosy:text-sm cosy:bg-base-100 cosy:text-base-content cosy:w-full',
112
+ { 'cosy:pl-0': showLineNumbers },
113
+ ]}>
114
+ <code class="cosy:block cosy:font-mono" set:html={trimmedCode} />
108
115
  </pre>
109
- </div>
116
+ </div>
110
117
  </div>
111
118
 
112
119
  <script>
113
- function initializeCopyButtons() {
114
- const copyButtons = document.querySelectorAll('.copy-button');
115
-
116
- copyButtons.forEach(button => {
117
- button.addEventListener('click', async () => {
118
- const code = button.getAttribute('data-code') || '';
119
- const copyText = button.querySelector('.copy-text');
120
-
121
- try {
122
- await navigator.clipboard.writeText(code);
123
- if (copyText) {
124
- copyText.textContent = '已复制!';
125
- setTimeout(() => {
126
- copyText.textContent = '复制';
127
- }, 2000);
128
- }
129
- } catch (err) {
130
- console.error('复制失败:', err);
131
- if (copyText) {
132
- copyText.textContent = '复制失败';
133
- setTimeout(() => {
134
- copyText.textContent = '复制';
135
- }, 2000);
136
- }
137
- }
138
- });
139
- });
140
- }
120
+ function initializeCopyButtons() {
121
+ const copyButtons = document.querySelectorAll('.cosy\\:flex.cosy\\:items-center.cosy\\:gap-1');
141
122
 
142
- // 在页面加载时初始化
143
- initializeCopyButtons();
123
+ copyButtons.forEach((button) => {
124
+ button.addEventListener('click', async () => {
125
+ const code = button.getAttribute('data-code') || '';
126
+ const copyText = button.querySelector('.cosy\\:copy-text');
144
127
 
145
- // 在 Astro 页面切换时重新初始化
146
- document.addEventListener('astro:page-load', initializeCopyButtons);
147
- </script>
128
+ try {
129
+ await navigator.clipboard.writeText(code);
130
+ if (copyText) {
131
+ copyText.textContent = '已复制!';
132
+ setTimeout(() => {
133
+ copyText.textContent = '复制';
134
+ }, 2000);
135
+ }
136
+ } catch (err) {
137
+ console.error('复制失败:', err);
138
+ if (copyText) {
139
+ copyText.textContent = '复制失败';
140
+ setTimeout(() => {
141
+ copyText.textContent = '复制';
142
+ }, 2000);
143
+ }
144
+ }
145
+ });
146
+ });
147
+ }
148
+
149
+ // 在页面加载时初始化
150
+ initializeCopyButtons();
151
+
152
+ // 在 Astro 页面切换时重新初始化
153
+ document.addEventListener('astro:page-load', initializeCopyButtons);
154
+ </script>