@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.
- package/dist/app.css +1 -1
- package/dist/components/base/Image.astro +260 -248
- package/dist/components/data-display/Blog.astro +232 -179
- package/dist/components/data-display/TeamMember.astro +19 -16
- package/dist/components/data-display/TeamMembers.astro +52 -48
- package/dist/components/display/Banner.astro +105 -17
- package/dist/components/display/Card.astro +106 -75
- package/dist/components/display/CodeBlock.astro +90 -83
- package/dist/components/display/CodeExample.astro +126 -129
- package/dist/components/display/Hero.astro +112 -32
- package/dist/components/layouts/DocumentationLayout.astro +24 -3
- package/dist/components/layouts/Header.astro +404 -41
- package/dist/components/layouts/Sidebar.astro +1 -1
- package/dist/components/navigation/LanguageSwitcher.astro +88 -58
- package/dist/components/navigation/TableOfContents.astro +333 -320
- package/dist/components/navigation/ThemeSwitcher.astro +109 -68
- package/dist/components/typography/Heading.astro +163 -130
- package/dist/components/typography/Text.astro +73 -71
- package/dist/utils/theme.ts +74 -15
- package/package.json +75 -70
@@ -1,89 +1,130 @@
|
|
1
1
|
---
|
2
|
+
/**
|
3
|
+
* @component ThemeSwitcher
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* ThemeSwitcher 组件提供一个主题切换下拉菜单,允许用户在多个预定义主题之间切换。
|
7
|
+
* 组件会记住用户的主题选择,并在页面重新加载或访问网站的其他页面时保持该主题。
|
8
|
+
*
|
9
|
+
* @design
|
10
|
+
* 设计理念:
|
11
|
+
* 1. 简洁直观 - 使用图标按钮和下拉菜单提供清晰的交互方式
|
12
|
+
* 2. 主题持久化 - 使用本地存储记住用户的主题选择
|
13
|
+
* 3. 无缝集成 - 与 DaisyUI 主题系统无缝集成
|
14
|
+
* 4. 广泛兼容 - 支持多种主题选项,适应不同的视觉偏好
|
15
|
+
*
|
16
|
+
* @usage
|
17
|
+
* 基本用法:
|
18
|
+
* ```astro
|
19
|
+
* <ThemeSwitcher />
|
20
|
+
* ```
|
21
|
+
*
|
22
|
+
* 自定义样式:
|
23
|
+
* ```astro
|
24
|
+
* <ThemeSwitcher class="cosy:top-4 cosy:right-4 cosy:fixed" />
|
25
|
+
* ```
|
26
|
+
*/
|
27
|
+
|
2
28
|
import Button from '../base/Button.astro';
|
3
29
|
import SunCloudyIcon from '../icons/SunCloudyIcon.astro';
|
30
|
+
import '../../app.css';
|
4
31
|
|
5
32
|
interface Props {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
33
|
+
/**
|
34
|
+
* 自定义类名
|
35
|
+
*/
|
36
|
+
class?: string;
|
10
37
|
}
|
11
38
|
|
12
39
|
const { class: className } = Astro.props;
|
13
40
|
|
14
41
|
const themes = [
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
42
|
+
{ id: 'default', name: 'Default' },
|
43
|
+
{ id: 'light', name: 'Light' },
|
44
|
+
{ id: 'dark', name: 'Dark' },
|
45
|
+
{ id: 'pastel', name: 'Pastel' },
|
46
|
+
{ id: 'lemonade', name: 'Lemonade' },
|
47
|
+
{ id: 'cupcake', name: 'Cupcake' },
|
48
|
+
{ id: 'nord', name: 'Nord' },
|
49
|
+
{ id: 'business', name: 'Business' },
|
50
|
+
{ id: 'luxury', name: 'Luxury' },
|
24
51
|
];
|
25
52
|
---
|
26
53
|
|
27
|
-
<div class:list={[
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
<div class:list={['cosy:dropdown-end cosy:dropdown', className]}>
|
55
|
+
<Button variant="ghost" size="sm" class="cosy:p-1">
|
56
|
+
<SunCloudyIcon class="cosy:w-5 cosy:h-5" slot="icon-left" />
|
57
|
+
</Button>
|
58
|
+
<ul
|
59
|
+
tabindex="0"
|
60
|
+
class="cosy:bg-slate-900 cosy:dark:bg-slate-800 cosy:shadow-lg cosy:p-2 cosy:rounded-box cosy:w-56 cosy:dropdown-content cosy:menu">
|
61
|
+
{
|
62
|
+
themes.map((theme) => (
|
63
|
+
<li>
|
64
|
+
<button
|
65
|
+
class="cosy:flex cosy:justify-between cosy:items-center cosy:w-full cosy:transition-colors cosy:theme-item"
|
66
|
+
data-theme={theme.id}
|
67
|
+
data-active={false}>
|
68
|
+
<span>{theme.name}</span>
|
69
|
+
<span class="cosy:hidden cosy:text-primary cosy:theme-check">✓</span>
|
70
|
+
</button>
|
71
|
+
</li>
|
72
|
+
))
|
73
|
+
}
|
74
|
+
</ul>
|
47
75
|
</div>
|
48
76
|
|
49
77
|
<script>
|
50
|
-
|
78
|
+
import { createThemeManager } from '../../utils/theme';
|
79
|
+
|
80
|
+
const themeManager = createThemeManager();
|
81
|
+
|
82
|
+
function updateActiveTheme() {
|
83
|
+
const currentTheme = document.documentElement.getAttribute('data-theme') || 'default';
|
84
|
+
document.querySelectorAll('.cosy\\:theme-item').forEach((item) => {
|
85
|
+
const isActive = item.getAttribute('data-theme') === currentTheme;
|
86
|
+
item.setAttribute('data-active', String(isActive));
|
87
|
+
|
88
|
+
// 更新视觉状态
|
89
|
+
const checkmark = item.querySelector('.cosy\\:theme-check');
|
90
|
+
if (checkmark) {
|
91
|
+
if (isActive) {
|
92
|
+
checkmark.classList.remove('cosy:hidden');
|
93
|
+
item.classList.add('cosy:bg-base-200', 'cosy:font-medium');
|
94
|
+
} else {
|
95
|
+
checkmark.classList.add('cosy:hidden');
|
96
|
+
item.classList.remove('cosy:bg-base-200', 'cosy:font-medium');
|
97
|
+
}
|
98
|
+
}
|
99
|
+
});
|
100
|
+
}
|
51
101
|
|
52
|
-
|
102
|
+
// 初始化主题切换按钮
|
103
|
+
function initThemeSwitcher() {
|
104
|
+
document.querySelectorAll('.cosy\\:theme-item').forEach((item) => {
|
105
|
+
item.addEventListener('click', () => {
|
106
|
+
const theme = item.getAttribute('data-theme');
|
107
|
+
if (theme) {
|
108
|
+
themeManager.setTheme(theme);
|
109
|
+
updateActiveTheme();
|
110
|
+
}
|
111
|
+
});
|
112
|
+
});
|
113
|
+
}
|
53
114
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
item.classList.add('active');
|
61
|
-
} else {
|
62
|
-
item.classList.remove('active');
|
63
|
-
}
|
64
|
-
});
|
65
|
-
}
|
115
|
+
// 初始加载时初始化
|
116
|
+
function initialize() {
|
117
|
+
themeManager.initialize();
|
118
|
+
initThemeSwitcher();
|
119
|
+
updateActiveTheme();
|
120
|
+
}
|
66
121
|
|
67
|
-
|
68
|
-
|
69
|
-
item.addEventListener('click', () => {
|
70
|
-
const theme = item.getAttribute('data-theme');
|
71
|
-
if (theme) {
|
72
|
-
themeManager.setTheme(theme);
|
73
|
-
updateActiveTheme();
|
74
|
-
}
|
75
|
-
});
|
76
|
-
});
|
122
|
+
// 初始化
|
123
|
+
document.addEventListener('DOMContentLoaded', initialize);
|
77
124
|
|
78
|
-
|
79
|
-
|
80
|
-
themeManager.initialize();
|
81
|
-
updateActiveTheme();
|
82
|
-
});
|
125
|
+
// Astro view transitions 后重新初始化
|
126
|
+
document.addEventListener('astro:after-swap', initialize);
|
83
127
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
updateActiveTheme();
|
88
|
-
});
|
89
|
-
</script>
|
128
|
+
// 确保脚本加载后立即初始化
|
129
|
+
initialize();
|
130
|
+
</script>
|
@@ -1,23 +1,23 @@
|
|
1
1
|
---
|
2
2
|
/**
|
3
3
|
* @component Heading
|
4
|
-
*
|
4
|
+
*
|
5
5
|
* @description
|
6
6
|
* Heading 组件用于创建各级标题,提供一致的排版样式和灵活的定制选项。
|
7
|
-
*
|
7
|
+
*
|
8
8
|
* @design
|
9
9
|
* 设计理念:
|
10
10
|
* 1. 层次清晰 - 通过不同级别的标题建立内容的视觉层次结构
|
11
11
|
* 2. 一致性 - 确保整个应用中标题样式的一致性
|
12
12
|
* 3. 可定制性 - 支持多种配置选项,适应不同场景需求
|
13
13
|
* 4. 无障碍性 - 遵循语义化HTML标准,确保屏幕阅读器可以正确解析内容结构
|
14
|
-
*
|
14
|
+
*
|
15
15
|
* 视觉特点:
|
16
16
|
* - 字体大小和粗细随级别变化
|
17
17
|
* - 可选的下划线或底部边框
|
18
18
|
* - 可定制的颜色和间距
|
19
19
|
* - 响应式设计,在不同屏幕尺寸下保持良好的可读性
|
20
|
-
*
|
20
|
+
*
|
21
21
|
* @usage
|
22
22
|
* 基本用法:
|
23
23
|
* ```astro
|
@@ -25,22 +25,22 @@
|
|
25
25
|
* <Heading level={2}>这是一个二级标题</Heading>
|
26
26
|
* <Heading level={3}>这是一个三级标题</Heading>
|
27
27
|
* ```
|
28
|
-
*
|
28
|
+
*
|
29
29
|
* 自定义样式:
|
30
30
|
* ```astro
|
31
31
|
* <Heading level={2} color="primary" underline>带下划线的二级标题</Heading>
|
32
32
|
* ```
|
33
|
-
*
|
33
|
+
*
|
34
34
|
* 带锚点链接:
|
35
35
|
* ```astro
|
36
36
|
* <Heading level={2} id="section-1" anchor>带锚点的标题</Heading>
|
37
37
|
* ```
|
38
|
-
*
|
38
|
+
*
|
39
39
|
* 自定义间距:
|
40
40
|
* ```astro
|
41
|
-
* <Heading level={1} class="mb-8">自定义底部间距的标题</Heading>
|
41
|
+
* <Heading level={1} class="cosy:mb-8">自定义底部间距的标题</Heading>
|
42
42
|
* ```
|
43
|
-
*
|
43
|
+
*
|
44
44
|
* @props
|
45
45
|
* @prop {1|2|3|4|5|6} [level=2] - 标题级别,对应 h1-h6 标签
|
46
46
|
* @prop {string} [id] - 标题的 ID,用于锚点链接
|
@@ -49,157 +49,190 @@
|
|
49
49
|
* @prop {string} [align='left'] - 文本对齐方式:'left', 'center', 'right'
|
50
50
|
* @prop {'default'|'primary'|'secondary'|'accent'|'muted'} [color='default'] - 标题颜色
|
51
51
|
* @prop {string} [class] - 自定义 CSS 类名
|
52
|
-
*
|
52
|
+
*
|
53
53
|
* @slots
|
54
54
|
* @slot default - 标题内容
|
55
|
-
*
|
55
|
+
*
|
56
56
|
* @accessibility
|
57
57
|
* - 使用语义化的 h1-h6 标签
|
58
58
|
* - 锚点链接带有描述性 aria-label
|
59
59
|
* - 遵循标题层次结构的最佳实践
|
60
|
-
*
|
60
|
+
*
|
61
61
|
* @dependencies
|
62
62
|
* 依赖于以下图标组件:
|
63
63
|
* - LinkIcon (用于锚点链接)
|
64
64
|
*/
|
65
65
|
|
66
66
|
import LinkIcon from '../icons/LinkIcon.astro';
|
67
|
+
import '../../app.css';
|
67
68
|
|
68
69
|
interface Props {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
70
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
71
|
+
id?: string;
|
72
|
+
anchor?: boolean;
|
73
|
+
underline?: boolean;
|
74
|
+
align?: 'left' | 'center' | 'right';
|
75
|
+
color?: 'default' | 'primary' | 'secondary' | 'accent' | 'muted';
|
76
|
+
class?: string;
|
76
77
|
}
|
77
78
|
|
78
79
|
const {
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
level = 2,
|
81
|
+
id,
|
82
|
+
anchor = false,
|
83
|
+
underline = false,
|
84
|
+
align = 'left',
|
85
|
+
color = 'default',
|
86
|
+
class: className = '',
|
86
87
|
} = Astro.props;
|
87
88
|
|
88
89
|
// 根据级别和颜色设置样式
|
89
90
|
const headingClass = {
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
}[(level ?? 2) as 1|2|3|4|5|6];
|
91
|
+
1: 'cosy:text-4xl cosy:font-bold',
|
92
|
+
2: 'cosy:text-3xl cosy:font-semibold',
|
93
|
+
3: 'cosy:text-2xl cosy:font-semibold',
|
94
|
+
4: 'cosy:text-xl cosy:font-medium',
|
95
|
+
5: 'cosy:text-lg cosy:font-medium',
|
96
|
+
6: 'cosy:text-base cosy:font-medium',
|
97
|
+
}[(level ?? 2) as 1 | 2 | 3 | 4 | 5 | 6];
|
97
98
|
|
98
99
|
const colorClass = {
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
}[(color ?? 'default') as 'default'|'primary'|'secondary'|'accent'|'muted'];
|
100
|
+
default: 'cosy:text-gray-900 cosy:dark:text-gray-100',
|
101
|
+
primary: 'cosy:text-primary-600 cosy:dark:text-primary-400',
|
102
|
+
secondary: 'cosy:text-secondary-600 cosy:dark:text-secondary-400',
|
103
|
+
accent: 'cosy:text-accent-600 cosy:dark:text-accent-400',
|
104
|
+
muted: 'cosy:text-gray-600 cosy:dark:text-gray-400',
|
105
|
+
}[(color ?? 'default') as 'default' | 'primary' | 'secondary' | 'accent' | 'muted'];
|
105
106
|
|
106
107
|
const alignClass = {
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
}[(align ?? 'left') as 'left'|'center'|'right'];
|
108
|
+
left: 'cosy:text-left',
|
109
|
+
center: 'cosy:text-center',
|
110
|
+
right: 'cosy:text-right',
|
111
|
+
}[(align ?? 'left') as 'left' | 'center' | 'right'];
|
111
112
|
|
112
|
-
const underlineClass = underline
|
113
|
+
const underlineClass = underline
|
114
|
+
? 'cosy:border-b cosy:pb-2 cosy:border-gray-200 cosy:dark:border-gray-700'
|
115
|
+
: '';
|
113
116
|
|
114
117
|
// 组合所有类名
|
115
118
|
const combinedClass = `heading ${headingClass} ${colorClass} ${alignClass} ${underlineClass} ${className}`;
|
116
119
|
---
|
117
120
|
|
118
|
-
{
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
{
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
121
|
+
{
|
122
|
+
level === 1 && (
|
123
|
+
<h1 id={id} class={combinedClass}>
|
124
|
+
<slot />
|
125
|
+
{anchor && id && (
|
126
|
+
<a
|
127
|
+
href={`#${id}`}
|
128
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
129
|
+
aria-label={`链接到 ${id} 部分`}>
|
130
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
131
|
+
</a>
|
132
|
+
)}
|
133
|
+
</h1>
|
134
|
+
)
|
135
|
+
}
|
136
|
+
|
137
|
+
{
|
138
|
+
level === 2 && (
|
139
|
+
<h2 id={id} class={combinedClass}>
|
140
|
+
<slot />
|
141
|
+
{anchor && id && (
|
142
|
+
<a
|
143
|
+
href={`#${id}`}
|
144
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
145
|
+
aria-label={`链接到 ${id} 部分`}>
|
146
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
147
|
+
</a>
|
148
|
+
)}
|
149
|
+
</h2>
|
150
|
+
)
|
151
|
+
}
|
152
|
+
|
153
|
+
{
|
154
|
+
level === 3 && (
|
155
|
+
<h3 id={id} class={combinedClass}>
|
156
|
+
<slot />
|
157
|
+
{anchor && id && (
|
158
|
+
<a
|
159
|
+
href={`#${id}`}
|
160
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
161
|
+
aria-label={`链接到 ${id} 部分`}>
|
162
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
163
|
+
</a>
|
164
|
+
)}
|
165
|
+
</h3>
|
166
|
+
)
|
167
|
+
}
|
168
|
+
|
169
|
+
{
|
170
|
+
level === 4 && (
|
171
|
+
<h4 id={id} class={combinedClass}>
|
172
|
+
<slot />
|
173
|
+
{anchor && id && (
|
174
|
+
<a
|
175
|
+
href={`#${id}`}
|
176
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
177
|
+
aria-label={`链接到 ${id} 部分`}>
|
178
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
179
|
+
</a>
|
180
|
+
)}
|
181
|
+
</h4>
|
182
|
+
)
|
183
|
+
}
|
184
|
+
|
185
|
+
{
|
186
|
+
level === 5 && (
|
187
|
+
<h5 id={id} class={combinedClass}>
|
188
|
+
<slot />
|
189
|
+
{anchor && id && (
|
190
|
+
<a
|
191
|
+
href={`#${id}`}
|
192
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
193
|
+
aria-label={`链接到 ${id} 部分`}>
|
194
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
195
|
+
</a>
|
196
|
+
)}
|
197
|
+
</h5>
|
198
|
+
)
|
199
|
+
}
|
200
|
+
|
201
|
+
{
|
202
|
+
level === 6 && (
|
203
|
+
<h6 id={id} class={combinedClass}>
|
204
|
+
<slot />
|
205
|
+
{anchor && id && (
|
206
|
+
<a
|
207
|
+
href={`#${id}`}
|
208
|
+
class="cosy:hover:opacity-100 cosy:opacity-0 cosy:ml-2 cosy:transition-opacity heading-anchor"
|
209
|
+
aria-label={`链接到 ${id} 部分`}>
|
210
|
+
<LinkIcon class="cosy:inline-block cosy:w-4 cosy:h-4" />
|
211
|
+
</a>
|
212
|
+
)}
|
213
|
+
</h6>
|
214
|
+
)
|
215
|
+
}
|
183
216
|
|
184
217
|
<style>
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
</style>
|
218
|
+
.heading {
|
219
|
+
margin-bottom: 0.5em;
|
220
|
+
line-height: 1.2;
|
221
|
+
scroll-margin-top: 100px;
|
222
|
+
}
|
223
|
+
|
224
|
+
.heading-anchor {
|
225
|
+
color: inherit;
|
226
|
+
text-decoration: none;
|
227
|
+
vertical-align: middle;
|
228
|
+
}
|
229
|
+
|
230
|
+
/* 悬停效果 */
|
231
|
+
.heading:hover .heading-anchor {
|
232
|
+
opacity: 0.7 !important;
|
233
|
+
}
|
234
|
+
|
235
|
+
.heading-anchor:hover {
|
236
|
+
opacity: 1 !important;
|
237
|
+
}
|
238
|
+
</style>
|