@coffic/cosy-ui 0.1.29 → 0.2.2
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 +43 -22
- 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/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/TeamMember.astro +135 -0
- package/dist/components/data-display/TeamMembers.astro +101 -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/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/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/Stack.astro +149 -0
- package/dist/components/navigation/LanguageSwitcher.astro +81 -0
- package/dist/components/navigation/TableOfContents.astro +352 -0
- package/dist/components/navigation/ThemeSwitcher.astro +89 -0
- package/dist/components/typography/Article.astro +144 -0
- package/dist/components/typography/Heading.astro +205 -0
- package/dist/components/typography/Text.astro +187 -0
- package/dist/index.ts +70 -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 +67 -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,135 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Card
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* Card 组件用于在页面中展示相关内容的容器,通常包含标题、描述和可选的图片。
|
7
|
+
* 组件设计简洁大方,具有平滑的动画效果和交互反馈,适合展示产品、文章或功能介绍等内容。
|
8
|
+
*
|
9
|
+
* @design
|
10
|
+
* 设计理念:
|
11
|
+
* 1. 视觉层次感 - 通过阴影、悬停效果和微妙的动画创造深度感
|
12
|
+
* 2. 内容聚焦 - 简洁的布局让用户关注卡片内容
|
13
|
+
* 3. 交互反馈 - 悬停和点击时提供明确的视觉反馈
|
14
|
+
* 4. 适应性强 - 响应式设计,适应不同屏幕尺寸
|
15
|
+
*
|
16
|
+
* 视觉特点:
|
17
|
+
* - 半透明背景:现代感强,适合叠加在各种背景上
|
18
|
+
* - 模糊效果:背景模糊增强内容可读性
|
19
|
+
* - 光效设计:顶部有微妙的光晕效果,增强立体感
|
20
|
+
* - 悬停动画:轻微放大和阴影增强,提供交互反馈
|
21
|
+
* - 图片缩放:图片在悬停时轻微放大,增强视觉吸引力
|
22
|
+
*
|
23
|
+
* @usage
|
24
|
+
* 基本用法:
|
25
|
+
* ```astro
|
26
|
+
* <Card
|
27
|
+
* title="卡片标题"
|
28
|
+
* subtitle="卡片描述文本"
|
29
|
+
* >
|
30
|
+
* 卡片内容
|
31
|
+
* </Card>
|
32
|
+
* ```
|
33
|
+
*
|
34
|
+
* 带图片的卡片:
|
35
|
+
* ```astro
|
36
|
+
* <Card
|
37
|
+
* title="产品名称"
|
38
|
+
* subtitle="产品描述"
|
39
|
+
* imageUrl="/path/to/image.jpg"
|
40
|
+
* >
|
41
|
+
* 其他产品信息
|
42
|
+
* </Card>
|
43
|
+
* ```
|
44
|
+
*
|
45
|
+
* 可点击的卡片(链接):
|
46
|
+
* ```astro
|
47
|
+
* <Card
|
48
|
+
* title="文章标题"
|
49
|
+
* subtitle="文章摘要"
|
50
|
+
* imageUrl="/path/to/image.jpg"
|
51
|
+
* href="/articles/article-slug"
|
52
|
+
* />
|
53
|
+
* ```
|
54
|
+
*
|
55
|
+
* @props
|
56
|
+
* @prop {string} [title] - 卡片标题
|
57
|
+
* @prop {string} [subtitle] - 卡片副标题或描述
|
58
|
+
* @prop {string} [imageUrl] - 卡片顶部图片的URL
|
59
|
+
* @prop {string} [href] - 如果提供,卡片将变成可点击的链接
|
60
|
+
*
|
61
|
+
* @slots
|
62
|
+
* @slot default - 卡片的主要内容区域
|
63
|
+
*
|
64
|
+
* @accessibility
|
65
|
+
* - 使用语义化HTML结构
|
66
|
+
* - 当作为链接使用时,确保提供有意义的文本内容
|
67
|
+
* - 动画遵循 prefers-reduced-motion 媒体查询,尊重用户的动画偏好设置
|
68
|
+
*/
|
69
|
+
|
70
|
+
// 导入样式
|
71
|
+
import '../../app.css';
|
72
|
+
|
73
|
+
interface Props {
|
74
|
+
title?: string;
|
75
|
+
subtitle?: string;
|
76
|
+
imageUrl?: string;
|
77
|
+
href?: string;
|
78
|
+
}
|
79
|
+
|
80
|
+
const { title, subtitle, imageUrl, href } = Astro.props;
|
81
|
+
---
|
82
|
+
|
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>
|
@@ -0,0 +1,147 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component CodeBlock
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* CodeBlock 组件用于在页面中展示格式化的代码片段,支持语法高亮、行号显示和代码复制功能。
|
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
|
+
* <CodeBlock code={`console.log('Hello, world!');`} lang="js" />
|
26
|
+
* ```
|
27
|
+
*
|
28
|
+
* 带标题和行号:
|
29
|
+
* ```astro
|
30
|
+
* <CodeBlock
|
31
|
+
* code={`function greet(name) {\n return 'Hello, ' + name + '!';\n}`}
|
32
|
+
* lang="js"
|
33
|
+
* title="greeting.js"
|
34
|
+
* showLineNumbers={true}
|
35
|
+
* />
|
36
|
+
* ```
|
37
|
+
*
|
38
|
+
* @props
|
39
|
+
* @prop {string} code - 要显示的代码字符串
|
40
|
+
* @prop {string} [lang="plaintext"] - 代码语言,用于语法高亮
|
41
|
+
* @prop {string} [title] - 代码块的标题,通常是文件名
|
42
|
+
* @prop {boolean} [showLineNumbers=true] - 是否显示行号
|
43
|
+
*
|
44
|
+
* @accessibility
|
45
|
+
* - 使用语义化HTML结构
|
46
|
+
* - 复制按钮提供清晰的视觉反馈
|
47
|
+
* - 代码使用等宽字体,确保对齐和可读性
|
48
|
+
*/
|
49
|
+
|
50
|
+
// 导入样式
|
51
|
+
import '../../app.css';
|
52
|
+
|
53
|
+
interface Props {
|
54
|
+
code: string;
|
55
|
+
lang?: string;
|
56
|
+
title?: string;
|
57
|
+
showLineNumbers?: boolean;
|
58
|
+
}
|
59
|
+
|
60
|
+
const {
|
61
|
+
code,
|
62
|
+
lang = 'plaintext',
|
63
|
+
title,
|
64
|
+
showLineNumbers = true,
|
65
|
+
} = Astro.props;
|
66
|
+
|
67
|
+
// 移除代码字符串开头和结尾的空行
|
68
|
+
const trimmedCode = code.trim();
|
69
|
+
|
70
|
+
// 生成行号
|
71
|
+
const lines = trimmedCode.split('\n');
|
72
|
+
const lineNumbers = Array.from({ length: lines.length }, (_, i) => i + 1);
|
73
|
+
---
|
74
|
+
|
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
|
+
)}
|
93
|
+
|
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
|
+
)}
|
104
|
+
|
105
|
+
{/* 代码内容 */}
|
106
|
+
<pre class:list={["language-" + lang, { "with-line-numbers": showLineNumbers }]}>
|
107
|
+
<code class={"language-" + lang} set:html={trimmedCode} />
|
108
|
+
</pre>
|
109
|
+
</div>
|
110
|
+
</div>
|
111
|
+
|
112
|
+
<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
|
+
}
|
141
|
+
|
142
|
+
// 在页面加载时初始化
|
143
|
+
initializeCopyButtons();
|
144
|
+
|
145
|
+
// 在 Astro 页面切换时重新初始化
|
146
|
+
document.addEventListener('astro:page-load', initializeCopyButtons);
|
147
|
+
</script>
|
@@ -0,0 +1,330 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component CodeExample
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* 用于展示组件示例及其源代码的组件。
|
7
|
+
* 采用标签形式,默认显示预览,用户可以点击标签切换到源代码视图。
|
8
|
+
* 支持亮色和暗黑模式,会根据系统设置自动切换主题。
|
9
|
+
*
|
10
|
+
* @usage
|
11
|
+
* ```astro
|
12
|
+
* <CodeExample
|
13
|
+
* title="示例标题"
|
14
|
+
* description="示例描述"
|
15
|
+
* code={`<Alert type="info">这是一个示例</Alert>`}
|
16
|
+
* >
|
17
|
+
* <Alert type="info">这是一个示例</Alert>
|
18
|
+
* </CodeExample>
|
19
|
+
* ```
|
20
|
+
*
|
21
|
+
* @props
|
22
|
+
* @prop {string} [title] - 示例标题
|
23
|
+
* @prop {string} [description] - 示例描述
|
24
|
+
* @prop {string} code - 要展示的源代码
|
25
|
+
*
|
26
|
+
* @slots
|
27
|
+
* @slot default - 组件的实际渲染内容
|
28
|
+
*/
|
29
|
+
|
30
|
+
// 导入图标
|
31
|
+
import { ClipboardIcon, CheckIcon } from '../../index';
|
32
|
+
|
33
|
+
interface Props {
|
34
|
+
title?: string;
|
35
|
+
description?: string;
|
36
|
+
code: string;
|
37
|
+
}
|
38
|
+
|
39
|
+
const { title, description, code } = Astro.props;
|
40
|
+
---
|
41
|
+
|
42
|
+
<div class="code-example">
|
43
|
+
{title && <h3 class="code-example-title">{title}</h3>}
|
44
|
+
{description && <p class="code-example-description">{description}</p>}
|
45
|
+
|
46
|
+
<div class="code-example-tabs">
|
47
|
+
<button class="code-example-tab active" data-tab="preview">预览</button>
|
48
|
+
<button class="code-example-tab" data-tab="code">代码</button>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
<div class="code-example-content">
|
52
|
+
<div class="code-example-panel active" data-panel="preview">
|
53
|
+
<div class="code-example-preview">
|
54
|
+
<slot />
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
|
58
|
+
<div class="code-example-panel" data-panel="code">
|
59
|
+
<div class="code-example-code">
|
60
|
+
<div class="code-example-header">
|
61
|
+
<button class="copy-button" aria-label="复制代码">
|
62
|
+
<span class="copy-icon"><ClipboardIcon /></span>
|
63
|
+
<span class="check-icon"><CheckIcon /></span>
|
64
|
+
<span class="copy-text">复制代码</span>
|
65
|
+
</button>
|
66
|
+
</div>
|
67
|
+
<pre><code class="language-astro">{code}</code></pre>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<style>
|
74
|
+
/* 颜色变量 - 支持亮色和暗黑模式 */
|
75
|
+
:root {
|
76
|
+
/* 亮色模式变量 */
|
77
|
+
--code-example-border: #e2e8f0;
|
78
|
+
--code-example-bg: #ffffff;
|
79
|
+
--code-example-text: #333333;
|
80
|
+
--code-example-title-border: #e2e8f0;
|
81
|
+
--code-example-description-color: #64748b;
|
82
|
+
--code-example-tabs-bg: #f8fafc;
|
83
|
+
--code-example-tab-color: #64748b;
|
84
|
+
--code-example-tab-hover-color: #0f172a;
|
85
|
+
--code-example-tab-active-color: #3b82f6;
|
86
|
+
--code-example-tab-active-border: #3b82f6;
|
87
|
+
--code-example-preview-bg: #ffffff;
|
88
|
+
--code-example-code-bg: #1e293b;
|
89
|
+
--code-example-code-text: #e2e8f0;
|
90
|
+
--code-example-header-bg: #334155;
|
91
|
+
--code-example-copy-button-color: #e2e8f0;
|
92
|
+
--code-example-copy-button-hover-bg: rgba(255, 255, 255, 0.1);
|
93
|
+
--code-example-copy-success-color: #4ade80;
|
94
|
+
}
|
95
|
+
|
96
|
+
@media (prefers-color-scheme: dark) {
|
97
|
+
:root {
|
98
|
+
/* 暗黑模式变量 */
|
99
|
+
--code-example-border: #2d3748;
|
100
|
+
--code-example-bg: #1a202c;
|
101
|
+
--code-example-text: #e2e8f0;
|
102
|
+
--code-example-title-border: #2d3748;
|
103
|
+
--code-example-description-color: #a0aec0;
|
104
|
+
--code-example-tabs-bg: #171e2e;
|
105
|
+
--code-example-tab-color: #a0aec0;
|
106
|
+
--code-example-tab-hover-color: #e2e8f0;
|
107
|
+
--code-example-tab-active-color: #60a5fa;
|
108
|
+
--code-example-tab-active-border: #60a5fa;
|
109
|
+
--code-example-preview-bg: #1a202c;
|
110
|
+
--code-example-code-bg: #0f172a;
|
111
|
+
--code-example-code-text: #e2e8f0;
|
112
|
+
--code-example-header-bg: #1e293b;
|
113
|
+
--code-example-copy-button-color: #e2e8f0;
|
114
|
+
--code-example-copy-button-hover-bg: rgba(255, 255, 255, 0.1);
|
115
|
+
--code-example-copy-success-color: #4ade80;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
.code-example {
|
120
|
+
margin-bottom: 2rem;
|
121
|
+
border: 1px solid var(--code-example-border);
|
122
|
+
border-radius: 0.5rem;
|
123
|
+
overflow: hidden;
|
124
|
+
background-color: var(--code-example-bg);
|
125
|
+
color: var(--code-example-text);
|
126
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
127
|
+
}
|
128
|
+
|
129
|
+
.code-example-title {
|
130
|
+
font-size: 1.25rem;
|
131
|
+
font-weight: bold;
|
132
|
+
margin: 0;
|
133
|
+
padding: 1rem;
|
134
|
+
border-bottom: 1px solid var(--code-example-title-border);
|
135
|
+
}
|
136
|
+
|
137
|
+
.code-example-description {
|
138
|
+
padding: 0 1rem;
|
139
|
+
color: var(--code-example-description-color);
|
140
|
+
margin-top: 0.5rem;
|
141
|
+
margin-bottom: 1rem;
|
142
|
+
}
|
143
|
+
|
144
|
+
.code-example-tabs {
|
145
|
+
display: flex;
|
146
|
+
border-bottom: 1px solid var(--code-example-title-border);
|
147
|
+
background-color: var(--code-example-tabs-bg);
|
148
|
+
}
|
149
|
+
|
150
|
+
.code-example-tab {
|
151
|
+
padding: 0.75rem 1.5rem;
|
152
|
+
background: transparent;
|
153
|
+
border: none;
|
154
|
+
border-bottom: 2px solid transparent;
|
155
|
+
cursor: pointer;
|
156
|
+
font-weight: 500;
|
157
|
+
color: var(--code-example-tab-color);
|
158
|
+
transition: all 0.2s ease;
|
159
|
+
}
|
160
|
+
|
161
|
+
.code-example-tab:hover {
|
162
|
+
color: var(--code-example-tab-hover-color);
|
163
|
+
}
|
164
|
+
|
165
|
+
.code-example-tab.active {
|
166
|
+
color: var(--code-example-tab-active-color);
|
167
|
+
border-bottom-color: var(--code-example-tab-active-border);
|
168
|
+
}
|
169
|
+
|
170
|
+
.code-example-content {
|
171
|
+
position: relative;
|
172
|
+
}
|
173
|
+
|
174
|
+
.code-example-panel {
|
175
|
+
display: none;
|
176
|
+
}
|
177
|
+
|
178
|
+
.code-example-panel.active {
|
179
|
+
display: block;
|
180
|
+
}
|
181
|
+
|
182
|
+
.code-example-preview {
|
183
|
+
padding: 1.5rem;
|
184
|
+
background-color: var(--code-example-preview-bg);
|
185
|
+
}
|
186
|
+
|
187
|
+
.code-example-code {
|
188
|
+
position: relative;
|
189
|
+
background-color: var(--code-example-code-bg);
|
190
|
+
color: var(--code-example-code-text);
|
191
|
+
overflow: auto;
|
192
|
+
}
|
193
|
+
|
194
|
+
.code-example-header {
|
195
|
+
display: flex;
|
196
|
+
justify-content: flex-end;
|
197
|
+
align-items: center;
|
198
|
+
padding: 0.5rem 1rem;
|
199
|
+
background-color: var(--code-example-header-bg);
|
200
|
+
}
|
201
|
+
|
202
|
+
.copy-button {
|
203
|
+
background: transparent;
|
204
|
+
border: none;
|
205
|
+
color: var(--code-example-copy-button-color);
|
206
|
+
cursor: pointer;
|
207
|
+
display: flex;
|
208
|
+
align-items: center;
|
209
|
+
gap: 0.5rem;
|
210
|
+
padding: 0.5rem 0.75rem;
|
211
|
+
border-radius: 0.25rem;
|
212
|
+
transition: background-color 0.2s;
|
213
|
+
font-size: 0.875rem;
|
214
|
+
}
|
215
|
+
|
216
|
+
.copy-button:hover {
|
217
|
+
background-color: var(--code-example-copy-button-hover-bg);
|
218
|
+
}
|
219
|
+
|
220
|
+
.copy-icon, .check-icon {
|
221
|
+
display: flex;
|
222
|
+
align-items: center;
|
223
|
+
justify-content: center;
|
224
|
+
width: 1rem;
|
225
|
+
height: 1rem;
|
226
|
+
}
|
227
|
+
|
228
|
+
.check-icon {
|
229
|
+
display: none;
|
230
|
+
color: var(--code-example-copy-success-color);
|
231
|
+
}
|
232
|
+
|
233
|
+
.copy-button.copied .copy-icon {
|
234
|
+
display: none;
|
235
|
+
}
|
236
|
+
|
237
|
+
.copy-button.copied .check-icon {
|
238
|
+
display: flex;
|
239
|
+
}
|
240
|
+
|
241
|
+
.copy-button.copied .copy-text {
|
242
|
+
color: var(--code-example-copy-success-color);
|
243
|
+
}
|
244
|
+
|
245
|
+
pre {
|
246
|
+
margin: 0;
|
247
|
+
padding: 1rem;
|
248
|
+
overflow-x: auto;
|
249
|
+
}
|
250
|
+
|
251
|
+
code {
|
252
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
253
|
+
white-space: pre;
|
254
|
+
font-size: 0.9rem;
|
255
|
+
line-height: 1.5;
|
256
|
+
}
|
257
|
+
</style>
|
258
|
+
|
259
|
+
<script>
|
260
|
+
function initializeCodeExample() {
|
261
|
+
// 标签切换功能
|
262
|
+
const tabs = document.querySelectorAll('.code-example-tab');
|
263
|
+
|
264
|
+
tabs.forEach(tab => {
|
265
|
+
tab.addEventListener('click', () => {
|
266
|
+
// 获取当前标签组
|
267
|
+
const tabGroup = tab.closest('.code-example-tabs');
|
268
|
+
if (!tabGroup) return;
|
269
|
+
|
270
|
+
const codeExample = tab.closest('.code-example');
|
271
|
+
if (!codeExample) return;
|
272
|
+
|
273
|
+
const contentContainer = codeExample.querySelector('.code-example-content');
|
274
|
+
if (!contentContainer) return;
|
275
|
+
|
276
|
+
// 获取目标面板
|
277
|
+
const targetTab = tab.getAttribute('data-tab');
|
278
|
+
if (!targetTab) return;
|
279
|
+
|
280
|
+
// 更新标签状态
|
281
|
+
tabGroup.querySelectorAll('.code-example-tab').forEach(t => {
|
282
|
+
t.classList.remove('active');
|
283
|
+
});
|
284
|
+
tab.classList.add('active');
|
285
|
+
|
286
|
+
// 更新面板状态
|
287
|
+
contentContainer.querySelectorAll('.code-example-panel').forEach(panel => {
|
288
|
+
panel.classList.remove('active');
|
289
|
+
});
|
290
|
+
|
291
|
+
const targetPanel = contentContainer.querySelector(`[data-panel="${targetTab}"]`);
|
292
|
+
if (targetPanel) {
|
293
|
+
targetPanel.classList.add('active');
|
294
|
+
}
|
295
|
+
});
|
296
|
+
});
|
297
|
+
|
298
|
+
// 复制代码功能
|
299
|
+
const copyButtons = document.querySelectorAll('.copy-button');
|
300
|
+
|
301
|
+
copyButtons.forEach(button => {
|
302
|
+
button.addEventListener('click', () => {
|
303
|
+
const codeBlock = button.closest('.code-example-code');
|
304
|
+
if (!codeBlock) return;
|
305
|
+
|
306
|
+
const codeElement = codeBlock.querySelector('code');
|
307
|
+
if (!codeElement) return;
|
308
|
+
|
309
|
+
const code = codeElement.textContent || '';
|
310
|
+
|
311
|
+
// 复制到剪贴板
|
312
|
+
navigator.clipboard.writeText(code).then(() => {
|
313
|
+
// 显示复制成功状态
|
314
|
+
button.classList.add('copied');
|
315
|
+
|
316
|
+
// 3秒后恢复原状
|
317
|
+
setTimeout(() => {
|
318
|
+
button.classList.remove('copied');
|
319
|
+
}, 3000);
|
320
|
+
});
|
321
|
+
});
|
322
|
+
});
|
323
|
+
}
|
324
|
+
|
325
|
+
// 初始化
|
326
|
+
initializeCodeExample();
|
327
|
+
|
328
|
+
// Astro 页面切换时重新初始化
|
329
|
+
document.addEventListener('astro:page-load', initializeCodeExample);
|
330
|
+
</script>
|