@coffic/cosy-ui 0.8.21 → 0.8.23
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/index-astro.ts +2 -0
- package/dist/src/assets/iconData.ts +5 -1
- package/dist/src-astro/banner/Banner.astro +55 -3
- package/dist/src-astro/banner/index.ts +1 -52
- package/dist/src-astro/button/Button.astro +40 -1
- package/dist/src-astro/code-container/ButtonCodeToggle.astro +55 -0
- package/dist/src-astro/code-container/ButtonCopyCode.astro +74 -0
- package/dist/src-astro/code-container/CodeContainer.astro +14 -289
- package/dist/src-astro/code-container/CodePanel.astro +14 -0
- package/dist/src-astro/code-container/CodeToolbar.astro +100 -0
- package/dist/src-astro/code-container/Description.astro +17 -0
- package/dist/src-astro/code-container/Preview.astro +93 -0
- package/dist/src-astro/code-panel/CodePanel.astro +109 -0
- package/dist/src-astro/code-panel/index.ts +2 -0
- package/dist/src-astro/code-panel/types.ts +17 -0
- package/dist/src-astro/footer/Footer.astro +23 -20
- package/dist/src-astro/footer/FooterSection.astro +7 -9
- package/dist/src-astro/grid/index.ts +1 -9
- package/dist/src-astro/heading/index.ts +1 -9
- package/dist/src-astro/hero/Hero.astro +72 -8
- package/dist/src-astro/icons/CodeIcon.astro +22 -0
- package/dist/src-astro/icons/index.ts +2 -1
- package/dist/src-astro/install-tabs/ButtonCopyInstall.astro +102 -0
- package/dist/src-astro/install-tabs/InstallTabs.astro +174 -0
- package/dist/src-astro/install-tabs/index.ts +2 -0
- package/dist/src-astro/install-tabs/types.ts +8 -0
- package/dist/src-astro/language-switcher/LanguageSwitcher.astro +6 -2
- package/dist/src-astro/layout-app/AppLayout.astro +1 -10
- package/dist/src-astro/layout-basic/BaseLayout.astro +20 -4
- package/dist/src-astro/link/Link.astro +50 -3
- package/dist/src-astro/types/meta.ts +65 -53
- package/package.json +3 -2
- package/dist/src-astro/banner/BannerAllAnimations.astro +0 -10
- package/dist/src-astro/banner/BannerBasic.astro +0 -5
- package/dist/src-astro/banner/BannerCustomStyle.astro +0 -8
- package/dist/src-astro/banner/BannerDanger.astro +0 -5
- package/dist/src-astro/banner/BannerFadeIn.astro +0 -5
- package/dist/src-astro/banner/BannerInfo.astro +0 -5
- package/dist/src-astro/banner/BannerPrimary.astro +0 -5
- package/dist/src-astro/banner/BannerSecondary.astro +0 -5
- package/dist/src-astro/banner/BannerSlideUp.astro +0 -5
- package/dist/src-astro/banner/BannerSuccess.astro +0 -5
- package/dist/src-astro/banner/BannerWarning.astro +0 -5
- package/dist/src-astro/grid/GridBasic.astro +0 -21
- package/dist/src-astro/heading/HeadingBasic.astro +0 -10
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
interface Props {
|
3
|
+
code: string;
|
4
|
+
}
|
5
|
+
const { code = '' } = Astro.props;
|
6
|
+
---
|
7
|
+
|
8
|
+
<div class="cosy:hidden cosy:code-example-panel" data-panel="code">
|
9
|
+
<pre
|
10
|
+
class="cosy:overflow-x-auto"
|
11
|
+
style="margin:0">
|
12
|
+
<code class="cosy:language-astro">{code}</code>
|
13
|
+
</pre>
|
14
|
+
</div>
|
@@ -0,0 +1,100 @@
|
|
1
|
+
---
|
2
|
+
import CodeToggleButton from './ButtonCodeToggle.astro';
|
3
|
+
import CopyCodeButton from './ButtonCopyCode.astro';
|
4
|
+
|
5
|
+
interface Props {
|
6
|
+
titles: string[];
|
7
|
+
activeTab: number;
|
8
|
+
isCodeView: boolean;
|
9
|
+
code: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
const {
|
13
|
+
titles = [],
|
14
|
+
activeTab = 0,
|
15
|
+
isCodeView = false,
|
16
|
+
code = '',
|
17
|
+
} = Astro.props;
|
18
|
+
---
|
19
|
+
|
20
|
+
<div
|
21
|
+
class="cosy:flex cosy:justify-between cosy:items-center cosy:bg-base-200 cosy:px-4 cosy:rounded-t">
|
22
|
+
<!-- 标签 -->
|
23
|
+
<div class="cosy:flex cosy:items-center cosy:gap-4">
|
24
|
+
<div role="tablist" class="cosy:tabs cosy:tabs-box">
|
25
|
+
{
|
26
|
+
titles.map((title: string, index: number) => (
|
27
|
+
<button
|
28
|
+
role="tab"
|
29
|
+
class={`cosy:tab ${index === activeTab ? 'cosy:tab-active' : ''}`}
|
30
|
+
data-tab={`tab-${index + 1}`}>
|
31
|
+
{title || `示例 ${index + 1}`}
|
32
|
+
</button>
|
33
|
+
))
|
34
|
+
}
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
<!-- 工具按钮 -->
|
38
|
+
<div class="cosy:flex cosy:items-center cosy:gap-2">
|
39
|
+
<CodeToggleButton isCodeView={isCodeView} />
|
40
|
+
<CopyCodeButton code={code} />
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<script>
|
45
|
+
function initializeTab() {
|
46
|
+
console.log('CodeContainer: 初始化标签切换按钮');
|
47
|
+
|
48
|
+
const exampleTabs = document.querySelectorAll(
|
49
|
+
'[role="tab"][data-tab^="tab-"]'
|
50
|
+
);
|
51
|
+
exampleTabs.forEach((tab) => {
|
52
|
+
tab.addEventListener('click', () => {
|
53
|
+
console.log('CodeContainer: 切换示例', tab.getAttribute('data-tab'));
|
54
|
+
const container = tab.closest('[data-role="code-container"]');
|
55
|
+
if (!container) return;
|
56
|
+
|
57
|
+
// 更新标签状态
|
58
|
+
container
|
59
|
+
.querySelectorAll('[role="tab"][data-tab^="tab-"]')
|
60
|
+
.forEach((t) => {
|
61
|
+
t.classList.remove('cosy:tab-active');
|
62
|
+
});
|
63
|
+
tab.classList.add('cosy:tab-active');
|
64
|
+
|
65
|
+
// 获取当前标签对应的内容 id
|
66
|
+
const targetId = tab.getAttribute('data-tab');
|
67
|
+
if (!targetId) return;
|
68
|
+
|
69
|
+
// 切换示例容器
|
70
|
+
container
|
71
|
+
.querySelectorAll('.cosy\\:example-container')
|
72
|
+
.forEach((example) => {
|
73
|
+
if (example.getAttribute('data-example') === targetId) {
|
74
|
+
example.classList.remove('cosy:hidden');
|
75
|
+
example.classList.add('cosy:block');
|
76
|
+
} else {
|
77
|
+
example.classList.add('cosy:hidden');
|
78
|
+
example.classList.remove('cosy:block');
|
79
|
+
}
|
80
|
+
});
|
81
|
+
|
82
|
+
// 更新对应内容的可见性
|
83
|
+
const allContent = container.querySelectorAll('[id^="tab-"]');
|
84
|
+
allContent.forEach((content) => {
|
85
|
+
if (content.id === targetId) {
|
86
|
+
content.classList.remove('cosy:hidden');
|
87
|
+
content.classList.add('cosy:block');
|
88
|
+
} else {
|
89
|
+
content.classList.add('cosy:hidden');
|
90
|
+
content.classList.remove('cosy:block');
|
91
|
+
}
|
92
|
+
});
|
93
|
+
});
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
document.addEventListener('astro:page-load', () => {
|
98
|
+
initializeTab();
|
99
|
+
});
|
100
|
+
</script>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
interface Props {
|
3
|
+
text: string;
|
4
|
+
}
|
5
|
+
const { text = '' } = Astro.props;
|
6
|
+
---
|
7
|
+
|
8
|
+
{
|
9
|
+
text && (
|
10
|
+
<p
|
11
|
+
class="cosy:px-6 cosy:py-2 cosy:text-sm not-prose
|
12
|
+
cosy:bg-gradient-to-b cosy:from-blue-100/50 cosy:to-blue-100/90
|
13
|
+
cosy:dark:from-blue-900/20 cosy:dark:to-blue-900/20">
|
14
|
+
{text}
|
15
|
+
</p>
|
16
|
+
)
|
17
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
---
|
2
|
+
// 预览组件,渲染slot内容
|
3
|
+
---
|
4
|
+
|
5
|
+
<div class="cosy:code-example-panel cosy:block" data-panel="preview">
|
6
|
+
<slot />
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<!-- 样式隔离和重置 -->
|
10
|
+
<style>
|
11
|
+
/* 预览区域样式重置,防止外部样式影响内部组件 */
|
12
|
+
[data-role='code-container'] .cosy\:reset-styles {
|
13
|
+
/* 重置所有可能影响布局的样式 */
|
14
|
+
all: initial;
|
15
|
+
font-family: inherit;
|
16
|
+
color: inherit;
|
17
|
+
line-height: 1.5;
|
18
|
+
|
19
|
+
/* 恢复必要的基础样式 */
|
20
|
+
display: block;
|
21
|
+
box-sizing: border-box;
|
22
|
+
|
23
|
+
/* 隔离样式作用域 */
|
24
|
+
isolation: isolate;
|
25
|
+
contain: layout style;
|
26
|
+
}
|
27
|
+
|
28
|
+
/* 重置内部所有元素的样式 */
|
29
|
+
[data-role='code-container'] .cosy\:reset-styles * {
|
30
|
+
all: unset;
|
31
|
+
display: revert;
|
32
|
+
box-sizing: border-box;
|
33
|
+
}
|
34
|
+
|
35
|
+
/* 恢复必要的文本样式 */
|
36
|
+
[data-role='code-container'] .cosy\:reset-styles {
|
37
|
+
font-family:
|
38
|
+
system-ui,
|
39
|
+
-apple-system,
|
40
|
+
BlinkMacSystemFont,
|
41
|
+
'Segoe UI',
|
42
|
+
Roboto,
|
43
|
+
'Helvetica Neue',
|
44
|
+
Arial,
|
45
|
+
sans-serif;
|
46
|
+
font-size: 14px;
|
47
|
+
line-height: 1.5;
|
48
|
+
color: #374151;
|
49
|
+
}
|
50
|
+
|
51
|
+
/* 恢复基本的HTML元素样式 */
|
52
|
+
[data-role='code-container'] .cosy\:reset-styles h1,
|
53
|
+
[data-role='code-container'] .cosy\:reset-styles h2,
|
54
|
+
[data-role='code-container'] .cosy\:reset-styles h3,
|
55
|
+
[data-role='code-container'] .cosy\:reset-styles h4,
|
56
|
+
[data-role='code-container'] .cosy\:reset-styles h5,
|
57
|
+
[data-role='code-container'] .cosy\:reset-styles h6 {
|
58
|
+
font-weight: bold;
|
59
|
+
margin-bottom: 0.5em;
|
60
|
+
}
|
61
|
+
|
62
|
+
[data-role='code-container'] .cosy\:reset-styles p {
|
63
|
+
margin-bottom: 1em;
|
64
|
+
}
|
65
|
+
|
66
|
+
[data-role='code-container'] .cosy\:reset-styles ul,
|
67
|
+
[data-role='code-container'] .cosy\:reset-styles ol {
|
68
|
+
padding-left: 1.5em;
|
69
|
+
margin-bottom: 1em;
|
70
|
+
}
|
71
|
+
|
72
|
+
[data-role='code-container'] .cosy\:reset-styles li {
|
73
|
+
margin-bottom: 0.25em;
|
74
|
+
}
|
75
|
+
|
76
|
+
/* 防止内部组件的样式泄漏到外部 */
|
77
|
+
[data-role='code-container'] .cosy\:code-preview-area {
|
78
|
+
position: relative;
|
79
|
+
z-index: 0;
|
80
|
+
}
|
81
|
+
|
82
|
+
/* 确保预览区域的样式隔离 */
|
83
|
+
[data-role='code-container'] .cosy\:code-preview-area::before {
|
84
|
+
content: '';
|
85
|
+
position: absolute;
|
86
|
+
top: -1px;
|
87
|
+
left: -1px;
|
88
|
+
right: -1px;
|
89
|
+
bottom: -1px;
|
90
|
+
pointer-events: none;
|
91
|
+
z-index: -1;
|
92
|
+
}
|
93
|
+
</style>
|
@@ -0,0 +1,109 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component CodePanel
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* 代码展示面板组件,支持语法高亮、多主题切换和行号显示。
|
7
|
+
* 基于 Shiki 提供高质量的代码高亮效果,与 VS Code 保持一致的渲染效果。
|
8
|
+
* 支持服务端渲染,无需客户端 JavaScript 即可展示代码高亮。
|
9
|
+
*
|
10
|
+
* @features
|
11
|
+
* - 🎨 语法高亮:基于 Shiki,支持 180+ 编程语言
|
12
|
+
* - 🎭 多主题:支持 VS Code 主题(dark-plus、light-plus、github-dark 等)
|
13
|
+
* - 📏 行号显示:可选的行号显示功能
|
14
|
+
* - 🚀 性能优化:服务端渲染,避免客户端重复计算
|
15
|
+
* - 📱 响应式:自适应不同屏幕尺寸
|
16
|
+
* - 🎯 可访问性:语义化 HTML 结构,支持键盘导航
|
17
|
+
*
|
18
|
+
* @usage
|
19
|
+
* 基本用法:
|
20
|
+
* ```astro
|
21
|
+
* <CodePanel code="console.log('Hello, world!');" />
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* 指定语言和主题:
|
25
|
+
* ```astro
|
26
|
+
* <CodePanel
|
27
|
+
* code="const name = 'TypeScript';"
|
28
|
+
* language="typescript"
|
29
|
+
* theme="github-dark"
|
30
|
+
* showLineNumbers={true}
|
31
|
+
* />
|
32
|
+
* ```
|
33
|
+
*
|
34
|
+
* 在切换面板中使用:
|
35
|
+
* ```astro
|
36
|
+
* <CodePanel
|
37
|
+
* code={sourceCode}
|
38
|
+
* language="astro"
|
39
|
+
* visible={false}
|
40
|
+
* />
|
41
|
+
* ```
|
42
|
+
*
|
43
|
+
* @props
|
44
|
+
* @param {string} code - 要显示的代码字符串
|
45
|
+
* @param {string} [language="typescript"] - 代码语言,用于语法高亮
|
46
|
+
* @param {string} [theme="dark-plus"] - 主题名称
|
47
|
+
* @param {boolean} [showLineNumbers=false] - 是否显示行号
|
48
|
+
* @param {boolean} [visible=true] - 是否显示在面板中
|
49
|
+
* @param {string} [class] - 自定义类名
|
50
|
+
*
|
51
|
+
* @slots
|
52
|
+
* 此组件不包含插槽,通过 props 传入代码内容
|
53
|
+
*
|
54
|
+
* @accessibility
|
55
|
+
* - 使用语义化的 HTML 结构
|
56
|
+
* - 代码区域具有适当的 aria 标签
|
57
|
+
* - 支持键盘导航和屏幕阅读器
|
58
|
+
* - 高对比度主题支持
|
59
|
+
*/
|
60
|
+
import type { CodePanelProps } from './types';
|
61
|
+
import '../../style.ts';
|
62
|
+
|
63
|
+
interface Props extends CodePanelProps {}
|
64
|
+
|
65
|
+
const {
|
66
|
+
code = '',
|
67
|
+
language = 'typescript',
|
68
|
+
theme = 'dark-plus',
|
69
|
+
showLineNumbers = false,
|
70
|
+
visible = true,
|
71
|
+
class: className = '',
|
72
|
+
} = Astro.props;
|
73
|
+
|
74
|
+
// 优化 Shiki 加载 - 使用动态导入避免构建时创建多个实例
|
75
|
+
let highlightedCode = code;
|
76
|
+
try {
|
77
|
+
// 使用动态导入避免构建时的性能问题
|
78
|
+
const { createHighlighter } = await import('shiki');
|
79
|
+
|
80
|
+
const highlighter = await createHighlighter({
|
81
|
+
themes: [theme],
|
82
|
+
langs: [language],
|
83
|
+
});
|
84
|
+
|
85
|
+
highlightedCode = highlighter.codeToHtml(code, {
|
86
|
+
lang: language,
|
87
|
+
theme: theme,
|
88
|
+
transformers: [],
|
89
|
+
});
|
90
|
+
|
91
|
+
// 释放资源
|
92
|
+
highlighter.dispose?.();
|
93
|
+
} catch (error) {
|
94
|
+
console.warn(
|
95
|
+
'CodePanel: Shiki highlighting failed, falling back to plain text:',
|
96
|
+
error
|
97
|
+
);
|
98
|
+
// 降级到普通代码块
|
99
|
+
highlightedCode = `<pre><code>${code}</code></pre>`;
|
100
|
+
}
|
101
|
+
---
|
102
|
+
|
103
|
+
<div data-panel="code" role="region" aria-label="代码展示面板">
|
104
|
+
<!-- Shiki 渲染的代码 -->
|
105
|
+
<div
|
106
|
+
class="cosy:w-full not-prose cosy-shiki-container"
|
107
|
+
set:html={highlightedCode}
|
108
|
+
/>
|
109
|
+
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/**
|
2
|
+
* CodePanel 组件的属性接口
|
3
|
+
*/
|
4
|
+
export interface CodePanelProps {
|
5
|
+
/** 要显示的代码字符串 */
|
6
|
+
code: string;
|
7
|
+
/** 代码语言,用于语法高亮,默认 'typescript' */
|
8
|
+
language?: string;
|
9
|
+
/** 主题名称,默认 'dark-plus' */
|
10
|
+
theme?: 'dark-plus' | 'light-plus' | 'github-dark' | 'github-light' | 'nord' | 'dracula';
|
11
|
+
/** 是否显示行号,默认 false */
|
12
|
+
showLineNumbers?: boolean;
|
13
|
+
/** 是否显示在面板中(用于切换),默认 true */
|
14
|
+
visible?: boolean;
|
15
|
+
/** 自定义类名 */
|
16
|
+
class?: string;
|
17
|
+
}
|
@@ -172,6 +172,7 @@ import {
|
|
172
172
|
LanguageUtil,
|
173
173
|
type IFooterProps,
|
174
174
|
createTextGetter,
|
175
|
+
Link,
|
175
176
|
} from '../../index-astro';
|
176
177
|
import FooterSection from './FooterSection.astro';
|
177
178
|
import '../../style.ts';
|
@@ -242,9 +243,7 @@ const debugClasses = debug
|
|
242
243
|
]}>
|
243
244
|
{/* 品牌区域 */}
|
244
245
|
<aside class:list={['cosy:max-w-xs cosy:text-center', debugClasses.aside]}>
|
245
|
-
<
|
246
|
-
href={homeLink}
|
247
|
-
class="cosy:flex cosy:items-center cosy:gap-2 cosy:mb-4 cosy:no-underline">
|
246
|
+
<Link href={homeLink} align="center" noUnderline block class="cosy:mb-4">
|
248
247
|
{
|
249
248
|
logo && (
|
250
249
|
<img
|
@@ -260,21 +259,25 @@ const debugClasses = debug
|
|
260
259
|
<h2 class="cosy:font-bold cosy:text-xl">{siteName}</h2>
|
261
260
|
<p class="cosy:text-base-content/70">{slogan}</p>
|
262
261
|
</div>
|
263
|
-
</
|
262
|
+
</Link>
|
264
263
|
|
265
264
|
{/* 社交媒体链接 */}
|
266
265
|
{
|
267
266
|
processedSocialLinks.length > 0 && (
|
268
267
|
<div class="cosy:flex cosy:justify-center cosy:gap-2 cosy:mt-4">
|
269
268
|
{processedSocialLinks.map((link) => (
|
270
|
-
<
|
269
|
+
<Link
|
271
270
|
href={link.url}
|
272
|
-
|
273
|
-
|
271
|
+
btn
|
272
|
+
circle
|
273
|
+
ghost
|
274
|
+
size="sm"
|
275
|
+
color="primary"
|
276
|
+
external
|
274
277
|
rel="noopener noreferrer"
|
275
278
|
aria-label={link.name}>
|
276
279
|
<SocialIcon platform={link.platform} />
|
277
|
-
</
|
280
|
+
</Link>
|
278
281
|
))}
|
279
282
|
</div>
|
280
283
|
)
|
@@ -307,10 +310,10 @@ const debugClasses = debug
|
|
307
310
|
<FooterSection
|
308
311
|
title={t('about')}
|
309
312
|
links={[
|
310
|
-
{ name: t('aboutUs'), href: aboutLink },
|
311
|
-
{ name: t('team'), href: teamLink },
|
312
|
-
{ name: t('careers'), href: careersLink },
|
313
|
-
{ name: t('contactUs'), href: contactLink },
|
313
|
+
{ name: t('aboutUs'), href: aboutLink ?? '' },
|
314
|
+
{ name: t('team'), href: teamLink ?? '' },
|
315
|
+
{ name: t('careers'), href: careersLink ?? '' },
|
316
|
+
{ name: t('contactUs'), href: contactLink ?? '' },
|
314
317
|
].filter((link) => link.href)}
|
315
318
|
/>
|
316
319
|
)
|
@@ -328,11 +331,11 @@ const debugClasses = debug
|
|
328
331
|
<FooterSection
|
329
332
|
title={t('resources')}
|
330
333
|
links={[
|
331
|
-
{ name: t('news'), href: newsLink },
|
332
|
-
{ name: t('blog'), href: blogLink },
|
333
|
-
{ name: t('faq'), href: faqLink },
|
334
|
-
{ name: t('history'), href: historyLink },
|
335
|
-
{ name: t('techStack'), href: techStackLink },
|
334
|
+
{ name: t('news'), href: newsLink ?? '' },
|
335
|
+
{ name: t('blog'), href: blogLink ?? '' },
|
336
|
+
{ name: t('faq'), href: faqLink ?? '' },
|
337
|
+
{ name: t('history'), href: historyLink ?? '' },
|
338
|
+
{ name: t('techStack'), href: techStackLink ?? '' },
|
336
339
|
].filter((link) => link.href)}
|
337
340
|
/>
|
338
341
|
)
|
@@ -344,8 +347,8 @@ const debugClasses = debug
|
|
344
347
|
<FooterSection
|
345
348
|
title={t('legal')}
|
346
349
|
links={[
|
347
|
-
{ name: t('terms'), href: termsLink },
|
348
|
-
{ name: t('privacy'), href: privacyLink },
|
350
|
+
{ name: t('terms'), href: termsLink ?? '' },
|
351
|
+
{ name: t('privacy'), href: privacyLink ?? '' },
|
349
352
|
].filter((link) => link.href)}
|
350
353
|
/>
|
351
354
|
)
|
@@ -366,7 +369,7 @@ const debugClasses = debug
|
|
366
369
|
{/* 底部版权信息 */}
|
367
370
|
<div
|
368
371
|
class:list={[
|
369
|
-
'cosy:footer cosy:footer-center cosy:p-4 cosy:bg-base-300
|
372
|
+
'cosy:footer cosy:footer-center cosy:p-4 cosy:bg-base-300',
|
370
373
|
debugClasses.footer,
|
371
374
|
]}>
|
372
375
|
<aside
|
@@ -6,10 +6,7 @@
|
|
6
6
|
* FooterSection 组件用于在页脚中显示一组链接,通常用于导航、产品列表等。
|
7
7
|
*/
|
8
8
|
|
9
|
-
|
10
|
-
name: string;
|
11
|
-
href?: string;
|
12
|
-
}
|
9
|
+
import { Link, type IProduct } from '../../index-astro';
|
13
10
|
|
14
11
|
interface Props {
|
15
12
|
/**
|
@@ -20,7 +17,7 @@ interface Props {
|
|
20
17
|
/**
|
21
18
|
* 链接列表
|
22
19
|
*/
|
23
|
-
links:
|
20
|
+
links: IProduct[];
|
24
21
|
}
|
25
22
|
|
26
23
|
const { title, links } = Astro.props;
|
@@ -33,12 +30,13 @@ const { title, links } = Astro.props;
|
|
33
30
|
links.map((link) => (
|
34
31
|
<li class="cosy:list-none">
|
35
32
|
{link.href ? (
|
36
|
-
<
|
33
|
+
<Link
|
37
34
|
href={link.href}
|
38
|
-
|
39
|
-
|
35
|
+
size="sm"
|
36
|
+
target={link.external ? '_blank' : '_self'}
|
37
|
+
variant="text">
|
40
38
|
{link.name}
|
41
|
-
</
|
39
|
+
</Link>
|
42
40
|
) : (
|
43
41
|
<span class="cosy:opacity-50">{link.name}</span>
|
44
42
|
)}
|
@@ -1,11 +1,3 @@
|
|
1
1
|
import Grid from './Grid.astro';
|
2
|
-
import GridBasic from './GridBasic.astro';
|
3
|
-
import BasicSourceCode from './GridBasic.astro?raw';
|
4
|
-
import { extractSimpleExample } from '../../src/utils/component';
|
5
2
|
|
6
|
-
export { Grid
|
7
|
-
|
8
|
-
// 导出示例源代码
|
9
|
-
export const GridExampleCodes = {
|
10
|
-
Basic: extractSimpleExample(BasicSourceCode, 'Grid'),
|
11
|
-
};
|
3
|
+
export { Grid };
|
@@ -1,11 +1,3 @@
|
|
1
1
|
import Heading from './Heading.astro';
|
2
|
-
import HeadingBasic from './HeadingBasic.astro';
|
3
|
-
import BasicSourceCode from './HeadingBasic.astro?raw';
|
4
|
-
import { extractSimpleExample } from '../../src/utils/component';
|
5
2
|
|
6
|
-
export { Heading
|
7
|
-
|
8
|
-
// 导出示例源代码
|
9
|
-
export const HeadingExampleCodes = {
|
10
|
-
Basic: extractSimpleExample(BasicSourceCode, 'Heading'),
|
11
|
-
};
|
3
|
+
export { Heading };
|
@@ -143,7 +143,7 @@
|
|
143
143
|
* @prop {string} links[].text - 链接按钮的文本
|
144
144
|
* @prop {string} links[].href - 链接按钮的目标地址
|
145
145
|
* @prop {string} links[].variant - 链接按钮的变体,可选值:"primary", "secondary", "accent", "info", "success", "warning", "error"
|
146
|
-
* @prop {string} [background="gradient"] - 背景样式,可选值:"gradient", "plain"
|
146
|
+
* @prop {string} [background="gradient"] - 背景样式,可选值:"gradient", "plain", "gradient-primary", "gradient-secondary", "gradient-accent", "gradient-success", "gradient-warning", "gradient-info", "gradient-sky", "gradient-sunset", "gradient-forest", "gradient-ocean", "gradient-mountain", "gradient-flower", "gradient-watermelon", "gradient-lemon", "gradient-grape", "gradient-blueberry", "gradient-mango", "gradient-kiwi", "gradient-pitaya", "gradient-banana"
|
147
147
|
* @prop {string} [align="center"] - 内容对齐方式,可选值:"center", "left", "right"
|
148
148
|
* @prop {string} [imagePosition="right"] - 图片位置,可选值:"right", "left", "top", "bottom"
|
149
149
|
* @prop {string} [backgroundImage] - 背景图片的URL
|
@@ -176,7 +176,29 @@ interface Props {
|
|
176
176
|
alt: string;
|
177
177
|
};
|
178
178
|
links: Link[];
|
179
|
-
background?:
|
179
|
+
background?:
|
180
|
+
| 'gradient'
|
181
|
+
| 'plain'
|
182
|
+
| 'gradient-primary'
|
183
|
+
| 'gradient-secondary'
|
184
|
+
| 'gradient-accent'
|
185
|
+
| 'gradient-success'
|
186
|
+
| 'gradient-warning'
|
187
|
+
| 'gradient-info'
|
188
|
+
| 'gradient-sky'
|
189
|
+
| 'gradient-sunset'
|
190
|
+
| 'gradient-forest'
|
191
|
+
| 'gradient-ocean'
|
192
|
+
| 'gradient-mountain'
|
193
|
+
| 'gradient-flower'
|
194
|
+
| 'gradient-watermelon'
|
195
|
+
| 'gradient-lemon'
|
196
|
+
| 'gradient-grape'
|
197
|
+
| 'gradient-blueberry'
|
198
|
+
| 'gradient-mango'
|
199
|
+
| 'gradient-kiwi'
|
200
|
+
| 'gradient-pitaya'
|
201
|
+
| 'gradient-banana';
|
180
202
|
align?: 'center' | 'left' | 'right';
|
181
203
|
imagePosition?: 'right' | 'left' | 'top' | 'bottom';
|
182
204
|
backgroundImage?: string;
|
@@ -200,6 +222,49 @@ const {
|
|
200
222
|
// 确保不透明度在0-1之间
|
201
223
|
const safeOpacity = Math.max(0, Math.min(1, overlayOpacity));
|
202
224
|
|
225
|
+
// 渐变色背景映射
|
226
|
+
const gradientBgClassMap = {
|
227
|
+
gradient: 'cosy:bg-gradient-to-br cosy:from-primary/10 cosy:to-secondary/20',
|
228
|
+
'gradient-primary':
|
229
|
+
'cosy:bg-gradient-to-br cosy:from-primary-400 cosy:to-primary-700',
|
230
|
+
'gradient-secondary':
|
231
|
+
'cosy:bg-gradient-to-br cosy:from-secondary-400 cosy:to-secondary-700',
|
232
|
+
'gradient-accent':
|
233
|
+
'cosy:bg-gradient-to-br cosy:from-accent-400 cosy:to-accent-700',
|
234
|
+
'gradient-success':
|
235
|
+
'cosy:bg-gradient-to-br cosy:from-success-400 cosy:to-success-700',
|
236
|
+
'gradient-warning':
|
237
|
+
'cosy:bg-gradient-to-br cosy:from-warning-400 cosy:to-warning-700',
|
238
|
+
'gradient-info': 'cosy:bg-gradient-to-br cosy:from-info-400 cosy:to-info-700',
|
239
|
+
'gradient-sky': 'cosy:bg-gradient-to-br cosy:from-sky-400 cosy:to-indigo-500',
|
240
|
+
'gradient-sunset':
|
241
|
+
'cosy:bg-gradient-to-br cosy:from-orange-400 cosy:via-pink-500 cosy:to-red-500',
|
242
|
+
'gradient-forest':
|
243
|
+
'cosy:bg-gradient-to-br cosy:from-green-700 cosy:to-lime-300',
|
244
|
+
'gradient-ocean':
|
245
|
+
'cosy:bg-gradient-to-br cosy:from-cyan-400 cosy:to-blue-700',
|
246
|
+
'gradient-mountain':
|
247
|
+
'cosy:bg-gradient-to-br cosy:from-gray-400 cosy:to-blue-900',
|
248
|
+
'gradient-flower':
|
249
|
+
'cosy:bg-gradient-to-br cosy:from-pink-300 cosy:via-purple-400 cosy:to-fuchsia-500',
|
250
|
+
'gradient-watermelon':
|
251
|
+
'cosy:bg-gradient-to-br cosy:from-green-300 cosy:via-pink-400 cosy:to-red-500',
|
252
|
+
'gradient-lemon':
|
253
|
+
'cosy:bg-gradient-to-br cosy:from-yellow-200 cosy:via-yellow-400 cosy:to-yellow-600',
|
254
|
+
'gradient-grape':
|
255
|
+
'cosy:bg-gradient-to-br cosy:from-purple-400 cosy:via-indigo-500 cosy:to-purple-700',
|
256
|
+
'gradient-blueberry':
|
257
|
+
'cosy:bg-gradient-to-br cosy:from-blue-400 cosy:via-blue-600 cosy:to-indigo-700',
|
258
|
+
'gradient-mango':
|
259
|
+
'cosy:bg-gradient-to-br cosy:from-yellow-300 cosy:via-orange-400 cosy:to-orange-600',
|
260
|
+
'gradient-kiwi':
|
261
|
+
'cosy:bg-gradient-to-br cosy:from-lime-200 cosy:via-green-400 cosy:to-green-700',
|
262
|
+
'gradient-pitaya':
|
263
|
+
'cosy:bg-gradient-to-br cosy:from-pink-200 cosy:via-fuchsia-400 cosy:to-lime-300',
|
264
|
+
'gradient-banana':
|
265
|
+
'cosy:bg-gradient-to-br cosy:from-yellow-100 cosy:via-yellow-300 cosy:to-yellow-500',
|
266
|
+
};
|
267
|
+
|
203
268
|
// 确定容器类
|
204
269
|
const containerClasses = [
|
205
270
|
'cosy:hero',
|
@@ -207,8 +272,9 @@ const containerClasses = [
|
|
207
272
|
'cosy:w-full',
|
208
273
|
'cosy:relative',
|
209
274
|
backgroundImage ? 'cosy:bg-cover cosy:bg-center' : '',
|
210
|
-
!backgroundImage && background
|
211
|
-
?
|
275
|
+
!backgroundImage && background.startsWith('gradient')
|
276
|
+
? gradientBgClassMap[background as keyof typeof gradientBgClassMap] ||
|
277
|
+
gradientBgClassMap['gradient']
|
212
278
|
: '',
|
213
279
|
!backgroundImage && background === 'plain' ? 'cosy:bg-base-100' : '',
|
214
280
|
].join(' ');
|
@@ -330,7 +396,7 @@ const contentOrder = {
|
|
330
396
|
|
331
397
|
<div
|
332
398
|
class={containerClasses}
|
333
|
-
style={backgroundImage ? `background-image: url(${backgroundImage})` :
|
399
|
+
style={backgroundImage ? `background-image: url(${backgroundImage})` : {}}
|
334
400
|
ignore-heading>
|
335
401
|
{
|
336
402
|
backgroundImage && backgroundOverlay !== 'none' && (
|
@@ -354,9 +420,7 @@ const contentOrder = {
|
|
354
420
|
(align === 'left' ? 'cosy:text-left cosy:items-start' : '') +
|
355
421
|
(align === 'right' ? 'cosy:text-right cosy:items-end' : '')}>
|
356
422
|
<h2 class={titleClasses}>{title}</h2>
|
357
|
-
<p class={descriptionClasses}>
|
358
|
-
{description}
|
359
|
-
</p>
|
423
|
+
<p class={descriptionClasses}>{description}</p>
|
360
424
|
|
361
425
|
{
|
362
426
|
Astro.slots.has('app') && (
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
// @component CodeIcon
|
3
|
+
// @description 代码相关的图标,用于表示代码块、开发等场景
|
4
|
+
import AstroIcon from './AstroIcon.astro';
|
5
|
+
|
6
|
+
interface Props {
|
7
|
+
/** 图标的大小 @default "24px" */
|
8
|
+
size?: string;
|
9
|
+
/** 图标的颜色 @default "currentColor" */
|
10
|
+
color?: string;
|
11
|
+
/** 自定义类名 */
|
12
|
+
class?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
const {
|
16
|
+
size = '24px',
|
17
|
+
color = 'currentColor',
|
18
|
+
class: className = '',
|
19
|
+
} = Astro.props;
|
20
|
+
---
|
21
|
+
|
22
|
+
<AstroIcon name="code" size={size} stroke={color} class={className} />
|