@coffic/cosy-ui 0.8.21 → 0.8.22
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/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/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/language-switcher/LanguageSwitcher.astro +6 -2
- package/dist/src-astro/link/Link.astro +50 -3
- package/package.json +1 -1
- 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
@@ -230,4 +230,8 @@ export const iconData: Record<string, IconData> = {
|
|
230
230
|
path: 'M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM8.82258 15.3427L8.03226 16.7137C7.80645 17.1089 7.30242 17.2419 6.90726 17.0161C6.5121 16.7903 6.37903 16.2863 6.60484 15.8911L7.19355 14.875C7.85484 14.6694 8.39516 14.8266 8.82258 15.3427ZM13.2097 8.66129L15.6331 12.8548H17.7742C18.2298 12.8548 18.5968 13.2218 18.5968 13.6774C18.5968 14.1331 18.2298 14.5 17.7742 14.5H16.5847L17.3871 15.8911C17.6129 16.2863 17.4798 16.7863 17.0847 17.0161C16.6895 17.2419 16.1895 17.1089 15.9597 16.7137L12.9194 11.4476C12.2298 10.2581 12.7218 9.06452 13.2097 8.66129ZM13.4879 5.61694C13.8831 5.84274 14.0161 6.34677 13.7903 6.74194L10.2621 12.8508H12.8145C13.6411 12.8508 14.1048 13.8226 13.746 14.496H6.26613C5.81048 14.496 5.44355 14.129 5.44355 13.6734C5.44355 13.2177 5.81048 12.8508 6.26613 12.8508H8.3629L11.0484 8.19758L10.2097 6.74194C9.98387 6.34677 10.1169 5.84677 10.5121 5.61694C10.9073 5.39113 11.4073 5.52419 11.6371 5.91935L11.996 6.55242L12.3629 5.91935C12.5887 5.52419 13.0927 5.39113 13.4879 5.61694Z',
|
231
231
|
viewBox: '0 0 24 24',
|
232
232
|
},
|
233
|
-
|
233
|
+
|
234
|
+
code: {
|
235
|
+
path: 'M8 6L2 12L8 18M16 6L22 12L16 18M10 16L14 8',
|
236
|
+
},
|
237
|
+
};
|
@@ -56,7 +56,23 @@ interface Props {
|
|
56
56
|
| 'info'
|
57
57
|
| 'success'
|
58
58
|
| 'warning'
|
59
|
-
| 'error'
|
59
|
+
| 'error'
|
60
|
+
// 大自然灵感渐变
|
61
|
+
| 'gradient-sky'
|
62
|
+
| 'gradient-sunset'
|
63
|
+
| 'gradient-forest'
|
64
|
+
| 'gradient-ocean'
|
65
|
+
| 'gradient-mountain'
|
66
|
+
| 'gradient-flower'
|
67
|
+
// 水果灵感渐变
|
68
|
+
| 'gradient-watermelon'
|
69
|
+
| 'gradient-lemon'
|
70
|
+
| 'gradient-grape'
|
71
|
+
| 'gradient-blueberry'
|
72
|
+
| 'gradient-mango'
|
73
|
+
| 'gradient-kiwi'
|
74
|
+
| 'gradient-pitaya'
|
75
|
+
| 'gradient-banana';
|
60
76
|
|
61
77
|
/**
|
62
78
|
* 文本颜色,默认根据背景色自动设置
|
@@ -101,7 +117,43 @@ const getTextColorClass = () => {
|
|
101
117
|
: 'cosy:text-gray-800';
|
102
118
|
};
|
103
119
|
|
104
|
-
//
|
120
|
+
// 渐变色背景映射
|
121
|
+
const gradientBgClassMap = {
|
122
|
+
'gradient-sky': 'cosy:bg-gradient-to-r cosy:from-sky-400 cosy:to-indigo-500',
|
123
|
+
'gradient-watermelon':
|
124
|
+
'cosy:bg-gradient-to-r cosy:from-green-300 cosy:via-pink-400 cosy:to-red-500',
|
125
|
+
'gradient-lemon':
|
126
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-200 cosy:via-yellow-400 cosy:to-yellow-600',
|
127
|
+
'gradient-grape':
|
128
|
+
'cosy:bg-gradient-to-r cosy:from-purple-400 cosy:via-indigo-500 cosy:to-purple-700',
|
129
|
+
'gradient-mango':
|
130
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-300 cosy:via-orange-400 cosy:to-orange-600',
|
131
|
+
'gradient-forest':
|
132
|
+
'cosy:bg-gradient-to-r cosy:from-green-700 cosy:to-lime-300',
|
133
|
+
'gradient-ocean': 'cosy:bg-gradient-to-r cosy:from-cyan-400 cosy:to-blue-700',
|
134
|
+
'gradient-sunset':
|
135
|
+
'cosy:bg-gradient-to-r cosy:from-orange-400 cosy:via-pink-500 cosy:to-red-500',
|
136
|
+
'gradient-flower':
|
137
|
+
'cosy:bg-gradient-to-r cosy:from-pink-300 cosy:via-purple-400 cosy:to-fuchsia-500',
|
138
|
+
'gradient-pitaya':
|
139
|
+
'cosy:bg-gradient-to-r cosy:from-pink-200 cosy:via-fuchsia-400 cosy:to-lime-300',
|
140
|
+
'gradient-banana':
|
141
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-100 cosy:via-yellow-300 cosy:to-yellow-500',
|
142
|
+
'gradient-blueberry':
|
143
|
+
'cosy:bg-gradient-to-r cosy:from-blue-400 cosy:via-blue-600 cosy:to-indigo-700',
|
144
|
+
'gradient-kiwi':
|
145
|
+
'cosy:bg-gradient-to-r cosy:from-lime-200 cosy:via-green-400 cosy:to-green-700',
|
146
|
+
};
|
147
|
+
|
148
|
+
const isGradient =
|
149
|
+
typeof bgColor === 'string' && bgColor.startsWith('gradient-');
|
150
|
+
let bgClass = '';
|
151
|
+
if (isGradient) {
|
152
|
+
bgClass =
|
153
|
+
gradientBgClassMap[bgColor as keyof typeof gradientBgClassMap] ?? '';
|
154
|
+
} else {
|
155
|
+
bgClass = `cosy:bg-${bgColor}`;
|
156
|
+
}
|
105
157
|
const bannerClasses = [
|
106
158
|
'cosy:w-full',
|
107
159
|
'cosy:py-8',
|
@@ -119,7 +171,7 @@ const bannerClasses = [
|
|
119
171
|
'cosy:shadow-md',
|
120
172
|
'cosy:hover:shadow-lg',
|
121
173
|
'cosy:hover:scale-[1.01]',
|
122
|
-
|
174
|
+
bgClass,
|
123
175
|
getTextColorClass(),
|
124
176
|
className,
|
125
177
|
].join(' ');
|
@@ -1,54 +1,3 @@
|
|
1
1
|
import Banner from './Banner.astro';
|
2
|
-
import BannerBasic from './BannerBasic.astro';
|
3
|
-
import BannerPrimary from './BannerPrimary.astro';
|
4
|
-
import BannerSecondary from './BannerSecondary.astro';
|
5
|
-
import BannerSuccess from './BannerSuccess.astro';
|
6
|
-
import BannerWarning from './BannerWarning.astro';
|
7
|
-
import BannerDanger from './BannerDanger.astro';
|
8
|
-
import BannerInfo from './BannerInfo.astro';
|
9
|
-
import BannerCustomStyle from './BannerCustomStyle.astro';
|
10
|
-
import BannerFadeIn from './BannerFadeIn.astro';
|
11
|
-
import BannerSlideUp from './BannerSlideUp.astro';
|
12
|
-
import BannerAllAnimations from './BannerAllAnimations.astro';
|
13
|
-
import BasicSourceCode from './BannerBasic.astro?raw';
|
14
|
-
import PrimarySourceCode from './BannerPrimary.astro?raw';
|
15
|
-
import SecondarySourceCode from './BannerSecondary.astro?raw';
|
16
|
-
import SuccessSourceCode from './BannerSuccess.astro?raw';
|
17
|
-
import WarningSourceCode from './BannerWarning.astro?raw';
|
18
|
-
import DangerSourceCode from './BannerDanger.astro?raw';
|
19
|
-
import InfoSourceCode from './BannerInfo.astro?raw';
|
20
|
-
import CustomStyleSourceCode from './BannerCustomStyle.astro?raw';
|
21
|
-
import FadeInSourceCode from './BannerFadeIn.astro?raw';
|
22
|
-
import SlideUpSourceCode from './BannerSlideUp.astro?raw';
|
23
|
-
import AllAnimationsSourceCode from './BannerAllAnimations.astro?raw';
|
24
|
-
import { extractSimpleExample } from '../../src/utils/component';
|
25
2
|
|
26
|
-
export {
|
27
|
-
Banner,
|
28
|
-
BannerBasic,
|
29
|
-
BannerPrimary,
|
30
|
-
BannerSecondary,
|
31
|
-
BannerSuccess,
|
32
|
-
BannerWarning,
|
33
|
-
BannerDanger,
|
34
|
-
BannerInfo,
|
35
|
-
BannerCustomStyle,
|
36
|
-
BannerFadeIn,
|
37
|
-
BannerSlideUp,
|
38
|
-
BannerAllAnimations,
|
39
|
-
};
|
40
|
-
|
41
|
-
// 导出示例源代码
|
42
|
-
export const BannerExampleCodes = {
|
43
|
-
Basic: extractSimpleExample(BasicSourceCode, 'Banner'),
|
44
|
-
Primary: extractSimpleExample(PrimarySourceCode, 'Banner'),
|
45
|
-
Secondary: extractSimpleExample(SecondarySourceCode, 'Banner'),
|
46
|
-
Success: extractSimpleExample(SuccessSourceCode, 'Banner'),
|
47
|
-
Warning: extractSimpleExample(WarningSourceCode, 'Banner'),
|
48
|
-
Danger: extractSimpleExample(DangerSourceCode, 'Banner'),
|
49
|
-
Info: extractSimpleExample(InfoSourceCode, 'Banner'),
|
50
|
-
CustomStyle: extractSimpleExample(CustomStyleSourceCode, 'Banner'),
|
51
|
-
FadeIn: extractSimpleExample(FadeInSourceCode, 'Banner'),
|
52
|
-
SlideUp: extractSimpleExample(SlideUpSourceCode, 'Banner'),
|
53
|
-
AllAnimations: extractSimpleExample(AllAnimationsSourceCode, 'Banner'),
|
54
|
-
};
|
3
|
+
export { Banner };
|
@@ -69,7 +69,20 @@ interface Props {
|
|
69
69
|
| 'ghost'
|
70
70
|
| 'link'
|
71
71
|
| 'outline'
|
72
|
-
| 'neutral'
|
72
|
+
| 'neutral'
|
73
|
+
| 'gradient-sky'
|
74
|
+
| 'gradient-watermelon'
|
75
|
+
| 'gradient-lemon'
|
76
|
+
| 'gradient-grape'
|
77
|
+
| 'gradient-mango'
|
78
|
+
| 'gradient-forest'
|
79
|
+
| 'gradient-ocean'
|
80
|
+
| 'gradient-sunset'
|
81
|
+
| 'gradient-flower'
|
82
|
+
| 'gradient-pitaya'
|
83
|
+
| 'gradient-banana'
|
84
|
+
| 'gradient-blueberry'
|
85
|
+
| 'gradient-kiwi';
|
73
86
|
size?: 'lg' | 'md' | 'sm' | 'xs';
|
74
87
|
shape?: 'circle' | 'square';
|
75
88
|
wide?: boolean;
|
@@ -119,6 +132,32 @@ const getButtonClasses = () => {
|
|
119
132
|
link: 'cosy:btn-link',
|
120
133
|
outline: 'cosy:btn-outline',
|
121
134
|
neutral: 'cosy:btn-neutral',
|
135
|
+
'gradient-sky':
|
136
|
+
'cosy:bg-gradient-to-r cosy:from-sky-400 cosy:to-indigo-500 cosy:text-white hover:cosy:from-sky-500 hover:cosy:to-indigo-600',
|
137
|
+
'gradient-watermelon':
|
138
|
+
'cosy:bg-gradient-to-r cosy:from-green-300 cosy:via-pink-400 cosy:to-red-500 cosy:text-white hover:cosy:from-green-400 hover:cosy:to-red-600',
|
139
|
+
'gradient-lemon':
|
140
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-200 cosy:via-yellow-400 cosy:to-yellow-600 cosy:text-gray-900 hover:cosy:from-yellow-300 hover:cosy:to-yellow-700',
|
141
|
+
'gradient-grape':
|
142
|
+
'cosy:bg-gradient-to-r cosy:from-purple-400 cosy:via-indigo-500 cosy:to-purple-700 cosy:text-white hover:cosy:from-purple-500 hover:cosy:to-purple-800',
|
143
|
+
'gradient-mango':
|
144
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-300 cosy:via-orange-400 cosy:to-orange-600 cosy:text-white hover:cosy:from-yellow-400 hover:cosy:to-orange-700',
|
145
|
+
'gradient-forest':
|
146
|
+
'cosy:bg-gradient-to-r cosy:from-green-700 cosy:to-lime-300 cosy:text-white hover:cosy:from-green-800 hover:cosy:to-lime-400',
|
147
|
+
'gradient-ocean':
|
148
|
+
'cosy:bg-gradient-to-r cosy:from-cyan-400 cosy:to-blue-700 cosy:text-white hover:cosy:from-cyan-500 hover:cosy:to-blue-800',
|
149
|
+
'gradient-sunset':
|
150
|
+
'cosy:bg-gradient-to-r cosy:from-orange-400 cosy:via-pink-500 cosy:to-red-500 cosy:text-white hover:cosy:from-orange-500 hover:cosy:to-red-600',
|
151
|
+
'gradient-flower':
|
152
|
+
'cosy:bg-gradient-to-r cosy:from-pink-300 cosy:via-purple-400 cosy:to-fuchsia-500 cosy:text-white hover:cosy:from-pink-400 hover:cosy:to-fuchsia-600',
|
153
|
+
'gradient-pitaya':
|
154
|
+
'cosy:bg-gradient-to-r cosy:from-pink-200 cosy:via-fuchsia-400 cosy:to-lime-300 cosy:text-white hover:cosy:from-pink-300 hover:cosy:to-lime-400',
|
155
|
+
'gradient-banana':
|
156
|
+
'cosy:bg-gradient-to-r cosy:from-yellow-100 cosy:via-yellow-300 cosy:to-yellow-500 cosy:text-gray-900 hover:cosy:from-yellow-200 hover:cosy:to-yellow-600',
|
157
|
+
'gradient-blueberry':
|
158
|
+
'cosy:bg-gradient-to-r cosy:from-blue-400 cosy:via-blue-600 cosy:to-indigo-700 cosy:text-white hover:cosy:from-blue-500 hover:cosy:to-indigo-800',
|
159
|
+
'gradient-kiwi':
|
160
|
+
'cosy:bg-gradient-to-r cosy:from-lime-200 cosy:via-green-400 cosy:to-green-700 cosy:text-white hover:cosy:from-lime-300 hover:cosy:to-green-800',
|
122
161
|
};
|
123
162
|
|
124
163
|
// Size classes
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
import { CodeIcon } from '../icons';
|
3
|
+
const { isCodeView = false } = Astro.props;
|
4
|
+
---
|
5
|
+
|
6
|
+
<button
|
7
|
+
role="switch"
|
8
|
+
class={`cosy:btn cosy:btn-ghost cosy:btn-sm ${isCodeView ? 'cosy:btn-primary' : ''}`}
|
9
|
+
aria-checked={isCodeView ? 'true' : 'false'}
|
10
|
+
aria-label="切换代码/预览"
|
11
|
+
data-toggle="code">
|
12
|
+
<span class="cosy:code-icon"><CodeIcon /></span>
|
13
|
+
</button>
|
14
|
+
|
15
|
+
<script>
|
16
|
+
function initializeCodeToggle() {
|
17
|
+
console.log('CodeContainer: 初始化代码/预览切换按钮');
|
18
|
+
const codeToggles = document.querySelectorAll('[data-toggle="code"]');
|
19
|
+
codeToggles.forEach((toggle) => {
|
20
|
+
toggle.addEventListener('click', () => {
|
21
|
+
const container = toggle.closest('[data-role="code-container"]');
|
22
|
+
if (!container) {
|
23
|
+
console.error('CodeContainer: 无法找到父容器');
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
const isChecked = toggle.getAttribute('aria-checked') === 'true';
|
27
|
+
toggle.setAttribute('aria-checked', !isChecked ? 'true' : 'false');
|
28
|
+
toggle.classList.toggle('cosy:btn-primary', !isChecked);
|
29
|
+
toggle.classList.toggle('cosy:btn-ghost', isChecked);
|
30
|
+
const activeExample = container.querySelector(
|
31
|
+
'.cosy\\:example-container:not(.cosy\\:hidden)'
|
32
|
+
);
|
33
|
+
if (!activeExample) {
|
34
|
+
console.error('CodeContainer: 无法找到活动示例');
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
activeExample
|
38
|
+
.querySelectorAll('.cosy\\:code-example-panel')
|
39
|
+
.forEach((panel) => {
|
40
|
+
if (panel.getAttribute('data-panel') === 'code') {
|
41
|
+
panel.classList.toggle('cosy:hidden', isChecked);
|
42
|
+
panel.classList.toggle('cosy:block', !isChecked);
|
43
|
+
} else {
|
44
|
+
panel.classList.toggle('cosy:hidden', !isChecked);
|
45
|
+
panel.classList.toggle('cosy:block', isChecked);
|
46
|
+
}
|
47
|
+
});
|
48
|
+
});
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
52
|
+
document.addEventListener('astro:page-load', () => {
|
53
|
+
initializeCodeToggle();
|
54
|
+
});
|
55
|
+
</script>
|
@@ -0,0 +1,74 @@
|
|
1
|
+
---
|
2
|
+
import { ClipboardIcon } from '../icons';
|
3
|
+
---
|
4
|
+
|
5
|
+
<button
|
6
|
+
class="cosy:gap-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
|
7
|
+
aria-label="复制代码"
|
8
|
+
id="cosy-copy-btn"
|
9
|
+
type="button"
|
10
|
+
style="position: relative;">
|
11
|
+
<span class="cosy:copy-icon"><ClipboardIcon /></span>
|
12
|
+
<span
|
13
|
+
id="copy-toast"
|
14
|
+
style="
|
15
|
+
display: none;
|
16
|
+
position: absolute;
|
17
|
+
top: -2.5rem;
|
18
|
+
left: 50%;
|
19
|
+
transform: translateX(-50%);
|
20
|
+
background: #22c55e;
|
21
|
+
color: #fff;
|
22
|
+
padding: 0.25rem 0.75rem;
|
23
|
+
border-radius: 0.5rem;
|
24
|
+
font-size: 0.875rem;
|
25
|
+
white-space: nowrap;
|
26
|
+
z-index: 10;
|
27
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
28
|
+
pointer-events: none;
|
29
|
+
transition: opacity 0.2s;
|
30
|
+
"
|
31
|
+
>复制成功</span
|
32
|
+
>
|
33
|
+
</button>
|
34
|
+
|
35
|
+
<script>
|
36
|
+
function initializeCopyCode() {
|
37
|
+
console.log('CodeContainer: 初始化复制代码按钮');
|
38
|
+
|
39
|
+
const copyButtons = document.querySelectorAll('[aria-label="复制代码"]');
|
40
|
+
copyButtons.forEach((button) => {
|
41
|
+
button.addEventListener('click', () => {
|
42
|
+
const container = button.closest('[data-role="code-container"]');
|
43
|
+
if (!container) return;
|
44
|
+
const activeExample = container.querySelector(
|
45
|
+
'.cosy\\:example-container:not(.cosy\\:hidden)'
|
46
|
+
);
|
47
|
+
if (!activeExample) return;
|
48
|
+
const codePanel = activeExample.querySelector('[data-panel="code"]');
|
49
|
+
if (!codePanel) return;
|
50
|
+
const codeElement = codePanel.querySelector('code');
|
51
|
+
if (!codeElement) return;
|
52
|
+
const code = codeElement.textContent || '';
|
53
|
+
navigator.clipboard.writeText(code).then(() => {
|
54
|
+
// 气泡提示
|
55
|
+
const toast = button.querySelector('#copy-toast');
|
56
|
+
if (toast && toast instanceof HTMLElement) {
|
57
|
+
toast.style.display = 'block';
|
58
|
+
toast.style.opacity = '1';
|
59
|
+
setTimeout(() => {
|
60
|
+
toast.style.opacity = '0';
|
61
|
+
setTimeout(() => {
|
62
|
+
toast.style.display = 'none';
|
63
|
+
}, 200);
|
64
|
+
}, 2000);
|
65
|
+
}
|
66
|
+
});
|
67
|
+
});
|
68
|
+
});
|
69
|
+
}
|
70
|
+
|
71
|
+
document.addEventListener('astro:page-load', () => {
|
72
|
+
initializeCopyCode();
|
73
|
+
});
|
74
|
+
</script>
|
@@ -34,7 +34,10 @@
|
|
34
34
|
* ```
|
35
35
|
*/
|
36
36
|
|
37
|
-
import
|
37
|
+
import CodeToolbar from './CodeToolbar.astro';
|
38
|
+
import Description from './Description.astro';
|
39
|
+
import Preview from './Preview.astro';
|
40
|
+
import CodePanel from './CodePanel.astro';
|
38
41
|
import '../../style.ts';
|
39
42
|
|
40
43
|
interface Props {
|
@@ -66,318 +69,40 @@ const {
|
|
66
69
|
---
|
67
70
|
|
68
71
|
<div data-role="code-container" class="cosy:mb-8 cosy:card" ignore-heading>
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
{
|
76
|
-
codes.map((_, index) => (
|
77
|
-
<button
|
78
|
-
role="tab"
|
79
|
-
class={`cosy:tab ${index === 0 ? 'cosy:tab-active' : ''}`}
|
80
|
-
data-tab={`tab-${index + 1}`}>
|
81
|
-
{titles[index] || `示例 ${index + 1}`}
|
82
|
-
</button>
|
83
|
-
))
|
84
|
-
}
|
85
|
-
</div>
|
86
|
-
</div>
|
87
|
-
|
88
|
-
<!-- 工具按钮 -->
|
89
|
-
<div class="cosy:flex cosy:items-center cosy:gap-2">
|
90
|
-
<button
|
91
|
-
role="switch"
|
92
|
-
class="cosy:btn cosy:btn-ghost cosy:btn-sm"
|
93
|
-
aria-checked="false"
|
94
|
-
aria-label="切换代码/预览"
|
95
|
-
data-toggle="code">
|
96
|
-
<span class="cosy:code-icon"><InfoIcon /></span>
|
97
|
-
</button>
|
98
|
-
<button
|
99
|
-
class="cosy:gap-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
|
100
|
-
aria-label="复制代码">
|
101
|
-
<span class="cosy:copy-icon"><ClipboardIcon /></span>
|
102
|
-
<span class="cosy:hidden cosy:check-icon"><CheckIcon /></span>
|
103
|
-
</button>
|
104
|
-
</div>
|
105
|
-
</div>
|
72
|
+
<CodeToolbar
|
73
|
+
titles={titles.length ? titles : codes.map((_, i) => `示例 ${i + 1}`)}
|
74
|
+
activeTab={0}
|
75
|
+
isCodeView={false}
|
76
|
+
code={codes[0]}
|
77
|
+
/>
|
106
78
|
|
107
79
|
{
|
108
80
|
codes.map((code, index) => (
|
109
81
|
<div
|
110
82
|
class={`cosy:example-container ${index === 0 ? 'cosy:block' : 'cosy:hidden'}`}
|
111
83
|
data-example={`tab-${index + 1}`}>
|
112
|
-
{
|
113
|
-
{descriptions[index] && (
|
114
|
-
<p
|
115
|
-
class="cosy:px-6 cosy:py-2 cosy:text-sm not-prose
|
116
|
-
cosy:bg-gradient-to-b cosy:from-blue-100/50 cosy:to-blue-100/90
|
117
|
-
cosy:dark:from-blue-900/20 cosy:dark:to-blue-900/20">
|
118
|
-
{descriptions[index]}
|
119
|
-
</p>
|
120
|
-
)}
|
84
|
+
<Description text={descriptions[index]} />
|
121
85
|
|
122
86
|
<div class="cosy:relative cosy:p-4 cosy:bg-gradient-to-br cosy:from-red-100 cosy:to-green-300 cosy:dark:from-red-900/20 cosy:dark:to-green-900/20 cosy:rounded-b">
|
123
|
-
<
|
87
|
+
<Preview>
|
124
88
|
<div
|
125
89
|
class={`cosy:code-preview-area ${resetStyles ? 'cosy:reset-styles' : ''}`}
|
126
90
|
data-preview-area={`tab-${index + 1}`}>
|
127
91
|
<slot />
|
128
92
|
</div>
|
129
|
-
</
|
93
|
+
</Preview>
|
130
94
|
|
131
|
-
<
|
132
|
-
<pre class="cosy:overflow-x-auto" style="margin:0">
|
133
|
-
<code class="cosy:language-astro">{code}</code>
|
134
|
-
</pre>
|
135
|
-
</div>
|
95
|
+
<CodePanel code={code} />
|
136
96
|
</div>
|
137
97
|
</div>
|
138
98
|
))
|
139
99
|
}
|
140
100
|
</div>
|
141
101
|
|
142
|
-
<!-- 样式隔离和重置 -->
|
143
|
-
<style>
|
144
|
-
/* 预览区域样式重置,防止外部样式影响内部组件 */
|
145
|
-
[data-role='code-container'] .cosy\:reset-styles {
|
146
|
-
/* 重置所有可能影响布局的样式 */
|
147
|
-
all: initial;
|
148
|
-
font-family: inherit;
|
149
|
-
color: inherit;
|
150
|
-
line-height: 1.5;
|
151
|
-
|
152
|
-
/* 恢复必要的基础样式 */
|
153
|
-
display: block;
|
154
|
-
box-sizing: border-box;
|
155
|
-
|
156
|
-
/* 隔离样式作用域 */
|
157
|
-
isolation: isolate;
|
158
|
-
contain: layout style;
|
159
|
-
}
|
160
|
-
|
161
|
-
/* 重置内部所有元素的样式 */
|
162
|
-
[data-role='code-container'] .cosy\:reset-styles * {
|
163
|
-
all: unset;
|
164
|
-
display: revert;
|
165
|
-
box-sizing: border-box;
|
166
|
-
}
|
167
|
-
|
168
|
-
/* 恢复必要的文本样式 */
|
169
|
-
[data-role='code-container'] .cosy\:reset-styles {
|
170
|
-
font-family:
|
171
|
-
system-ui,
|
172
|
-
-apple-system,
|
173
|
-
BlinkMacSystemFont,
|
174
|
-
'Segoe UI',
|
175
|
-
Roboto,
|
176
|
-
'Helvetica Neue',
|
177
|
-
Arial,
|
178
|
-
sans-serif;
|
179
|
-
font-size: 14px;
|
180
|
-
line-height: 1.5;
|
181
|
-
color: #374151;
|
182
|
-
}
|
183
|
-
|
184
|
-
/* 恢复基本的HTML元素样式 */
|
185
|
-
[data-role='code-container'] .cosy\:reset-styles h1,
|
186
|
-
[data-role='code-container'] .cosy\:reset-styles h2,
|
187
|
-
[data-role='code-container'] .cosy\:reset-styles h3,
|
188
|
-
[data-role='code-container'] .cosy\:reset-styles h4,
|
189
|
-
[data-role='code-container'] .cosy\:reset-styles h5,
|
190
|
-
[data-role='code-container'] .cosy\:reset-styles h6 {
|
191
|
-
font-weight: bold;
|
192
|
-
margin-bottom: 0.5em;
|
193
|
-
}
|
194
|
-
|
195
|
-
[data-role='code-container'] .cosy\:reset-styles p {
|
196
|
-
margin-bottom: 1em;
|
197
|
-
}
|
198
|
-
|
199
|
-
[data-role='code-container'] .cosy\:reset-styles ul,
|
200
|
-
[data-role='code-container'] .cosy\:reset-styles ol {
|
201
|
-
padding-left: 1.5em;
|
202
|
-
margin-bottom: 1em;
|
203
|
-
}
|
204
|
-
|
205
|
-
[data-role='code-container'] .cosy\:reset-styles li {
|
206
|
-
margin-bottom: 0.25em;
|
207
|
-
}
|
208
|
-
|
209
|
-
/* 防止内部组件的样式泄漏到外部 */
|
210
|
-
[data-role='code-container'] .cosy\:code-preview-area {
|
211
|
-
position: relative;
|
212
|
-
z-index: 0;
|
213
|
-
}
|
214
|
-
|
215
|
-
/* 确保预览区域的样式隔离 */
|
216
|
-
[data-role='code-container'] .cosy\:code-preview-area::before {
|
217
|
-
content: '';
|
218
|
-
position: absolute;
|
219
|
-
top: -1px;
|
220
|
-
left: -1px;
|
221
|
-
right: -1px;
|
222
|
-
bottom: -1px;
|
223
|
-
pointer-events: none;
|
224
|
-
z-index: -1;
|
225
|
-
}
|
226
|
-
</style>
|
227
|
-
|
228
102
|
<script>
|
229
103
|
function initializeCodeContainer() {
|
230
104
|
console.log('CodeContainer: 初始化代码容器');
|
231
105
|
|
232
|
-
// 示例切换功能
|
233
|
-
const exampleTabs = document.querySelectorAll(
|
234
|
-
'[role="tab"][data-tab^="tab-"]'
|
235
|
-
);
|
236
|
-
exampleTabs.forEach((tab) => {
|
237
|
-
tab.addEventListener('click', () => {
|
238
|
-
console.log('CodeContainer: 切换示例', tab.getAttribute('data-tab'));
|
239
|
-
const container = tab.closest('[data-role="code-container"]');
|
240
|
-
if (!container) return;
|
241
|
-
|
242
|
-
// 更新标签状态
|
243
|
-
container
|
244
|
-
.querySelectorAll('[role="tab"][data-tab^="tab-"]')
|
245
|
-
.forEach((t) => {
|
246
|
-
t.classList.remove('cosy:tab-active');
|
247
|
-
});
|
248
|
-
tab.classList.add('cosy:tab-active');
|
249
|
-
|
250
|
-
// 获取当前标签对应的内容 id
|
251
|
-
const targetId = tab.getAttribute('data-tab');
|
252
|
-
if (!targetId) return;
|
253
|
-
|
254
|
-
// 切换示例容器
|
255
|
-
container
|
256
|
-
.querySelectorAll('.cosy\\:example-container')
|
257
|
-
.forEach((example) => {
|
258
|
-
if (example.getAttribute('data-example') === targetId) {
|
259
|
-
example.classList.remove('cosy:hidden');
|
260
|
-
example.classList.add('cosy:block');
|
261
|
-
} else {
|
262
|
-
example.classList.add('cosy:hidden');
|
263
|
-
example.classList.remove('cosy:block');
|
264
|
-
}
|
265
|
-
});
|
266
|
-
|
267
|
-
// 更新对应内容的可见性
|
268
|
-
const allContent = container.querySelectorAll('[id^="tab-"]');
|
269
|
-
allContent.forEach((content) => {
|
270
|
-
if (content.id === targetId) {
|
271
|
-
content.classList.remove('cosy:hidden');
|
272
|
-
content.classList.add('cosy:block');
|
273
|
-
} else {
|
274
|
-
content.classList.add('cosy:hidden');
|
275
|
-
content.classList.remove('cosy:block');
|
276
|
-
}
|
277
|
-
});
|
278
|
-
});
|
279
|
-
});
|
280
|
-
|
281
|
-
// 代码开关功能
|
282
|
-
const codeToggles = document.querySelectorAll('[data-toggle="code"]');
|
283
|
-
codeToggles.forEach((toggle) => {
|
284
|
-
toggle.addEventListener('click', () => {
|
285
|
-
console.log('code toggle clicked', toggle);
|
286
|
-
const container = toggle.closest('[data-role="code-container"]');
|
287
|
-
if (!container) {
|
288
|
-
console.error('CodeContainer: 无法找到父容器');
|
289
|
-
return;
|
290
|
-
}
|
291
|
-
|
292
|
-
// 切换开关状态
|
293
|
-
const isChecked = toggle.getAttribute('aria-checked') === 'true';
|
294
|
-
console.log('代码按钮 isChecked:', isChecked);
|
295
|
-
toggle.setAttribute('aria-checked', !isChecked ? 'true' : 'false');
|
296
|
-
toggle.classList.toggle('cosy:btn-primary', !isChecked);
|
297
|
-
toggle.classList.toggle('cosy:btn-ghost', isChecked);
|
298
|
-
|
299
|
-
// 找到当前显示的示例
|
300
|
-
const activeExample = container.querySelector(
|
301
|
-
'.cosy\\:example-container:not(.cosy\\:hidden)'
|
302
|
-
);
|
303
|
-
if (!activeExample) {
|
304
|
-
console.error('CodeContainer: 无法找到活动示例');
|
305
|
-
return;
|
306
|
-
}
|
307
|
-
|
308
|
-
// 切换面板
|
309
|
-
activeExample
|
310
|
-
.querySelectorAll('.cosy\\:code-example-panel')
|
311
|
-
.forEach((panel) => {
|
312
|
-
if (panel.getAttribute('data-panel') === 'code') {
|
313
|
-
console.log(
|
314
|
-
'处理代码面板的展示/隐藏,此时的isChecked:',
|
315
|
-
isChecked
|
316
|
-
);
|
317
|
-
panel.classList.toggle('cosy:hidden', isChecked);
|
318
|
-
panel.classList.toggle('cosy:block', !isChecked);
|
319
|
-
} else {
|
320
|
-
console.log(
|
321
|
-
'处理预览面板的展示/隐藏,此时的isChecked:',
|
322
|
-
isChecked
|
323
|
-
);
|
324
|
-
panel.classList.toggle('cosy:hidden', !isChecked);
|
325
|
-
panel.classList.toggle('cosy:block', isChecked);
|
326
|
-
}
|
327
|
-
});
|
328
|
-
});
|
329
|
-
});
|
330
|
-
|
331
|
-
// 复制代码功能
|
332
|
-
const copyButtons = document.querySelectorAll('[aria-label="复制代码"]');
|
333
|
-
copyButtons.forEach((button) => {
|
334
|
-
button.addEventListener('click', () => {
|
335
|
-
const container = button.closest('[data-role="code-container"]');
|
336
|
-
if (!container) return;
|
337
|
-
|
338
|
-
// 确保代码面板是显示的
|
339
|
-
const codeToggle = container.querySelector('[data-toggle="code"]');
|
340
|
-
if (codeToggle?.getAttribute('aria-checked') !== 'true') {
|
341
|
-
(codeToggle as HTMLElement)?.click();
|
342
|
-
}
|
343
|
-
|
344
|
-
// 找到当前显示的示例
|
345
|
-
const activeExample = container.querySelector(
|
346
|
-
'.cosy\\:example-container:not(.cosy\\:hidden)'
|
347
|
-
);
|
348
|
-
if (!activeExample) return;
|
349
|
-
|
350
|
-
const codePanel = activeExample.querySelector('[data-panel="code"]');
|
351
|
-
if (!codePanel) return;
|
352
|
-
|
353
|
-
const codeElement = codePanel.querySelector('code');
|
354
|
-
if (!codeElement) return;
|
355
|
-
|
356
|
-
const code = codeElement.textContent || '';
|
357
|
-
|
358
|
-
// 复制到剪贴板
|
359
|
-
navigator.clipboard.writeText(code).then(() => {
|
360
|
-
button.classList.add('cosy:btn-success');
|
361
|
-
button
|
362
|
-
.querySelector('.cosy\\:copy-icon')
|
363
|
-
?.classList.add('cosy:hidden');
|
364
|
-
button
|
365
|
-
.querySelector('.cosy\\:check-icon')
|
366
|
-
?.classList.remove('cosy:hidden');
|
367
|
-
|
368
|
-
setTimeout(() => {
|
369
|
-
button.classList.remove('cosy:btn-success');
|
370
|
-
button
|
371
|
-
.querySelector('.cosy\\:copy-icon')
|
372
|
-
?.classList.remove('cosy:hidden');
|
373
|
-
button
|
374
|
-
.querySelector('.cosy\\:check-icon')
|
375
|
-
?.classList.add('cosy:hidden');
|
376
|
-
}, 3000);
|
377
|
-
});
|
378
|
-
});
|
379
|
-
});
|
380
|
-
|
381
106
|
// 初始化时隐藏除第一个以外的所有内容
|
382
107
|
document
|
383
108
|
.querySelectorAll('[data-role="code-container"]')
|
@@ -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>
|