@coffic/cosy-ui 1.0.11 → 1.0.12
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 +1 -0
- package/dist/index-vue.ts +1 -0
- package/dist/src/assets/iconData.js +3 -0
- package/dist/src/assets/icons-data/arrowDownS.d.ts +2 -0
- package/dist/src/assets/icons-data/arrowDownS.js +3 -0
- package/dist/src/assets/icons-data/arrowUpS.d.ts +2 -0
- package/dist/src/assets/icons-data/arrowUpS.js +3 -0
- package/dist/src/assets/icons-data/function.d.ts +2 -0
- package/dist/src/assets/icons-data/function.js +3 -0
- package/dist/src/assets/icons-data/index.d.ts +3 -0
- package/dist/src/assets/icons-data/index.js +3 -0
- package/dist/src-astro/icons/ArrowDownSIcon.astro +28 -0
- package/dist/src-astro/icons/ArrowUpSIcon.astro +28 -0
- package/dist/src-astro/icons/FunctionIcon.astro +28 -0
- package/dist/src-astro/icons/index.ts +3 -0
- package/dist/src-astro/mac-window/MacWindow.astro +7 -34
- package/dist/src-astro/math-formula/MathFormula.astro +463 -0
- package/dist/src-astro/math-formula/index.ts +3 -0
- package/dist/src-astro/math-formula/props.ts +11 -0
- package/dist/src-astro/math-formula/types.ts +46 -0
- package/dist/src-astro/products/ProductCard.astro +0 -4
- package/dist/src-astro/products/ProductHero.astro +0 -3
- package/dist/src-astro/products/ProductHeroContent.astro +0 -3
- package/dist/src-astro/products/ProductHeroImage.astro +0 -3
- package/dist/src-astro/products/Products.astro +0 -3
- package/dist/src-vue/math-formula/MathFormula.vue +198 -0
- package/dist/src-vue/math-formula/index.ts +2 -0
- package/dist/src-vue/math-formula/types.ts +18 -0
- package/package.json +1 -1
- /package/dist/{src-astro/products → src/styles}/product-card.css +0 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* @component MathFormula
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* MathFormula 公式展示组件用于在文档或页面中美观、统一地展示数学公式,支持标题、编号和说明。
|
|
7
|
+
*
|
|
8
|
+
* @usage
|
|
9
|
+
* 基本用法:
|
|
10
|
+
* ```astro
|
|
11
|
+
* <MathFormula title="换底公式" number="1" variant="gradient">
|
|
12
|
+
* $$\log_a c = \frac{\log_b c}{\log_b a}$$
|
|
13
|
+
* </MathFormula>
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* 带说明和符号表:
|
|
17
|
+
* ```astro
|
|
18
|
+
* <MathFormula title="换底公式" number="1">
|
|
19
|
+
* $$\log_a c = \frac{\log_b c}{\log_b a}$$
|
|
20
|
+
* <div slot="desc">
|
|
21
|
+
* 这是对数函数的重要性质之一。
|
|
22
|
+
* </div>
|
|
23
|
+
* <div slot="symbols">
|
|
24
|
+
* | 符号 | 含义 |
|
|
25
|
+
* | :--- | :--- |
|
|
26
|
+
* | $\log_a$ | 以 a 为底的对数 |
|
|
27
|
+
* </div>
|
|
28
|
+
* </MathFormula>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @props
|
|
32
|
+
* @prop {string} [title] - 公式标题,可选
|
|
33
|
+
* @prop {string|number} [number] - 公式编号,可选
|
|
34
|
+
* @prop {('default'|'gradient'|'glass'|'neon'|'card'|'spotlight')} [variant='gradient'] - 视觉风格
|
|
35
|
+
* @prop {boolean} [symbolsCollapsed=true] - 符号说明是否默认折叠
|
|
36
|
+
* @prop {string} [class] - 自定义 CSS 类名,用于覆盖默认样式
|
|
37
|
+
*
|
|
38
|
+
* @slots
|
|
39
|
+
* @slot default - 公式内容(支持 LaTeX)
|
|
40
|
+
* @slot desc - 说明文字(可选)
|
|
41
|
+
* @slot symbols - 符号说明(可折叠,默认折叠)
|
|
42
|
+
* @slot details - 详细说明(可折叠,默认折叠)
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { cn } from '../../src/class';
|
|
46
|
+
import { ArrowDownSIcon, ArrowUpSIcon, FunctionIcon, InfoIcon } from '../icons';
|
|
47
|
+
import type { IMathFormulaProps } from './props';
|
|
48
|
+
|
|
49
|
+
const props = Astro.props as IMathFormulaProps;
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
title = '',
|
|
53
|
+
number = '',
|
|
54
|
+
variant = 'gradient',
|
|
55
|
+
symbolsCollapsed = true,
|
|
56
|
+
class: className = '',
|
|
57
|
+
} = props;
|
|
58
|
+
|
|
59
|
+
const hasHeader = title || number;
|
|
60
|
+
const showDesc = Astro.slots.has('desc');
|
|
61
|
+
const showSymbols = Astro.slots.has('symbols');
|
|
62
|
+
const showDetails = Astro.slots.has('details');
|
|
63
|
+
|
|
64
|
+
// 生成唯一 ID 用于多个实例
|
|
65
|
+
const componentId = `math-formula-${Math.random().toString(36).substring(2, 9)}`;
|
|
66
|
+
|
|
67
|
+
// 构建容器样式类
|
|
68
|
+
const baseClasses = cn()
|
|
69
|
+
.add('cosy:my-6')
|
|
70
|
+
.add('cosy:p-4')
|
|
71
|
+
.add('cosy:rounded-lg')
|
|
72
|
+
.add('cosy:relative')
|
|
73
|
+
.add('cosy:transition-all')
|
|
74
|
+
.add('cosy:duration-300')
|
|
75
|
+
.build();
|
|
76
|
+
|
|
77
|
+
// 变体样式映射
|
|
78
|
+
const variantClassMap: Record<string, string> = {
|
|
79
|
+
default:
|
|
80
|
+
'cosy:bg-base-200/80 cosy:border-l-2 cosy:border-accent cosy:rounded-r-lg cosy:shadow cosy:hover:shadow-lg',
|
|
81
|
+
gradient:
|
|
82
|
+
'cosy:bg-linear-to-br cosy:from-accent/10 cosy:via-base-200/80 cosy:to-primary/10 cosy:border cosy:border-accent/30 cosy:shadow-inner cosy:hover:border-accent/50 cosy:backdrop-blur-sm',
|
|
83
|
+
glass: 'cosy:bg-base-100/40 cosy:backdrop-blur-md cosy:border cosy:border-base-300/50 cosy:shadow-xl cosy:hover:bg-base-100/50',
|
|
84
|
+
neon: 'cosy:bg-base-200/50 cosy:border-2 cosy:border-accent cosy:shadow-[0_0_15px_rgba(var(--a),0.3)] cosy:hover:shadow-[0_0_25px_rgba(var(--a),0.5)] cosy:backdrop-blur-sm',
|
|
85
|
+
card: 'cosy:bg-base-100 cosy:border-2 cosy:border-l-8 cosy:border-accent cosy:shadow-lg cosy:hover:shadow-2xl cosy:hover:-translate-y-1',
|
|
86
|
+
spotlight:
|
|
87
|
+
'cosy:bg-base-200/80 cosy:border cosy:border-accent/20 cosy:shadow-lg cosy:hover:shadow-xl cosy:overflow-hidden',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const containerClasses = cn()
|
|
91
|
+
.add(baseClasses)
|
|
92
|
+
.add(variantClassMap[variant] || variantClassMap.gradient)
|
|
93
|
+
.add(className)
|
|
94
|
+
.build();
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
class={`formula-container ${containerClasses}`}
|
|
99
|
+
data-formula-id={componentId}
|
|
100
|
+
data-variant={variant}>
|
|
101
|
+
<!-- Spotlight 效果的光斑(仅在 spotlight variant 时显示) -->
|
|
102
|
+
{
|
|
103
|
+
variant === 'spotlight' && (
|
|
104
|
+
<div class="spotlight-effect cosy:absolute cosy:inset-0 cosy:pointer-events-none" />
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
<!-- 右上角 info 图标 -->
|
|
109
|
+
<div class="cosy:absolute cosy:top-2 cosy:right-2 cosy:z-10">
|
|
110
|
+
<button
|
|
111
|
+
class="formula-info-btn cosy:w-5 cosy:h-5 cosy:text-accent cosy:hover:text-accent-focus cosy:hover:scale-110 cosy:transition-all cosy:duration-200"
|
|
112
|
+
title="什么是公式?"
|
|
113
|
+
type="button"
|
|
114
|
+
data-formula-id={componentId}>
|
|
115
|
+
<InfoIcon class="cosy:w-4 cosy:h-4" />
|
|
116
|
+
</button>
|
|
117
|
+
<!-- 提示框 -->
|
|
118
|
+
<div
|
|
119
|
+
class="formula-tooltip cosy:absolute cosy:top-6 cosy:right-0 cosy:w-64 cosy:p-3 cosy:bg-base-100 cosy:border cosy:border-base-300 cosy:rounded-lg cosy:shadow-xl cosy:z-20 cosy:text-sm cosy:animate-fade-in cosy:hidden"
|
|
120
|
+
data-formula-id={componentId}>
|
|
121
|
+
<div class="cosy:flex cosy:items-start cosy:gap-2">
|
|
122
|
+
<InfoIcon
|
|
123
|
+
class="cosy:w-4 cosy:h-4 cosy:text-accent cosy:mt-0.5 cosy:shrink-0"
|
|
124
|
+
/>
|
|
125
|
+
<div class="cosy:text-base-content">
|
|
126
|
+
<p class="cosy:font-medium cosy:mb-1 not-prose">数学公式</p>
|
|
127
|
+
<p
|
|
128
|
+
class="cosy:text-xs cosy:leading-relaxed not-prose cosy:opacity-70">
|
|
129
|
+
公式是数学中表达数量关系、结构规律的符号表达式,是解决数学问题的重要工具。重要结论公式应熟练掌握和灵活应用。
|
|
130
|
+
</p>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<!-- 小三角 -->
|
|
134
|
+
<div
|
|
135
|
+
class="cosy:absolute cosy:-top-1 cosy:right-3 cosy:w-2 cosy:h-2 cosy:bg-base-100 cosy:border-l cosy:border-t cosy:border-base-300 cosy:transform cosy:rotate-45">
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
hasHeader && (
|
|
142
|
+
<div
|
|
143
|
+
class={cn()
|
|
144
|
+
.flex()
|
|
145
|
+
.items('center')
|
|
146
|
+
.mb(2)
|
|
147
|
+
.relative()
|
|
148
|
+
.z(1)
|
|
149
|
+
.build()}>
|
|
150
|
+
<FunctionIcon
|
|
151
|
+
class={`cosy:text-accent cosy:mr-2 cosy:text-lg ${
|
|
152
|
+
variant === 'neon'
|
|
153
|
+
? 'cosy:drop-shadow-[0_0_8px_rgba(var(--a),0.8)]'
|
|
154
|
+
: ''
|
|
155
|
+
}`}
|
|
156
|
+
/>
|
|
157
|
+
{title && (
|
|
158
|
+
<span
|
|
159
|
+
class={`cosy:font-bold cosy:text-base-content ${
|
|
160
|
+
variant === 'neon'
|
|
161
|
+
? 'cosy:drop-shadow-[0_0_4px_rgba(var(--a),0.3)]'
|
|
162
|
+
: ''
|
|
163
|
+
}`}>
|
|
164
|
+
{title}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
{number && (
|
|
168
|
+
<span
|
|
169
|
+
class={`cosy:ml-2 cosy:text-xs cosy:text-base-content/70 cosy:px-2 cosy:py-0.5 cosy:rounded-full ${
|
|
170
|
+
variant !== 'default'
|
|
171
|
+
? 'cosy:bg-accent/20'
|
|
172
|
+
: 'cosy:bg-base-300'
|
|
173
|
+
}`}>
|
|
174
|
+
公式{number}
|
|
175
|
+
</span>
|
|
176
|
+
)}
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
<div
|
|
182
|
+
class={cn()
|
|
183
|
+
.add('cosy:overflow-x-auto')
|
|
184
|
+
.text('lg')
|
|
185
|
+
.add('cosy:text-base-content', 'formula-block')
|
|
186
|
+
.relative()
|
|
187
|
+
.z(1)
|
|
188
|
+
.build()}>
|
|
189
|
+
<slot />
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
{
|
|
193
|
+
showDesc && (
|
|
194
|
+
<div
|
|
195
|
+
class={cn()
|
|
196
|
+
.mt(2)
|
|
197
|
+
.text('sm')
|
|
198
|
+
.add(
|
|
199
|
+
'cosy:text-base-content/70',
|
|
200
|
+
'cosy:border-t',
|
|
201
|
+
'cosy:border-base-content/10'
|
|
202
|
+
)
|
|
203
|
+
.pt(2)
|
|
204
|
+
.relative()
|
|
205
|
+
.z(1)
|
|
206
|
+
.build()}>
|
|
207
|
+
<slot name="desc" />
|
|
208
|
+
</div>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
<!-- 可折叠的符号说明 -->
|
|
213
|
+
{
|
|
214
|
+
showSymbols && (
|
|
215
|
+
<div
|
|
216
|
+
class={`cosy:mt-2 cosy:pt-3 cosy:border-t cosy:border-base-content/10 cosy:relative cosy:z-1 ${
|
|
217
|
+
variant === 'default'
|
|
218
|
+
? 'cosy:border-t cosy:border-base-300'
|
|
219
|
+
: 'cosy:border-t cosy:border-accent/20'
|
|
220
|
+
}`}>
|
|
221
|
+
<details class="group" open={!symbolsCollapsed}>
|
|
222
|
+
<summary
|
|
223
|
+
class={`cosy:cursor-pointer cosy:text-sm cosy:font-medium cosy:flex cosy:items-center cosy:gap-1 cosy:transition-colors ${
|
|
224
|
+
variant === 'neon'
|
|
225
|
+
? 'cosy:text-accent cosy:hover:text-accent cosy:drop-shadow-[0_0_4px_rgba(var(--a),0.5)]'
|
|
226
|
+
: 'cosy:text-accent cosy:hover:text-accent-focus'
|
|
227
|
+
}`}>
|
|
228
|
+
<InfoIcon class="cosy:w-4 cosy:h-4 group-open:cosy:rotate-180 cosy:transition-transform cosy:duration-200" />
|
|
229
|
+
符号说明
|
|
230
|
+
</summary>
|
|
231
|
+
<div class="cosy:mt-2 cosy:text-sm cosy:text-base-content/70">
|
|
232
|
+
<slot name="symbols" />
|
|
233
|
+
</div>
|
|
234
|
+
</details>
|
|
235
|
+
</div>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
<!-- 可折叠的详细说明 -->
|
|
240
|
+
{
|
|
241
|
+
showDetails && (
|
|
242
|
+
<div class="cosy:mt-2 cosy:border-t cosy:border-base-content/10 cosy:pt-2 cosy:relative cosy:z-1">
|
|
243
|
+
<div class="cosy:flex cosy:justify-end">
|
|
244
|
+
<button
|
|
245
|
+
class="formula-details-toggle cosy:flex cosy:items-center cosy:gap-2 cosy:text-sm cosy:font-medium cosy:text-accent cosy:hover:text-accent-focus cosy:transition-colors cosy:duration-200 group"
|
|
246
|
+
type="button"
|
|
247
|
+
data-formula-id={componentId}>
|
|
248
|
+
<ArrowDownSIcon class="cosy:w-4 cosy:h-4 cosy:transition-transform cosy:duration-200 formula-details-icon-down" />
|
|
249
|
+
<ArrowUpSIcon class="cosy:w-4 cosy:h-4 cosy:transition-transform cosy:duration-200 formula-details-icon-up cosy:hidden" />
|
|
250
|
+
</button>
|
|
251
|
+
</div>
|
|
252
|
+
<div
|
|
253
|
+
class="formula-details-content cosy:mt-2 cosy:text-sm cosy:text-base-content/80 cosy:animate-fade-in cosy:hidden"
|
|
254
|
+
data-formula-id={componentId}>
|
|
255
|
+
<slot name="details" />
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<style>
|
|
263
|
+
.formula-block {
|
|
264
|
+
/* 让公式在小屏下可横向滚动 */
|
|
265
|
+
font-family: 'KaTeX_Main', 'Times New Roman', Times, serif;
|
|
266
|
+
word-break: break-word;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Spotlight 效果 */
|
|
270
|
+
.spotlight-effect {
|
|
271
|
+
background: radial-gradient(
|
|
272
|
+
circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
|
|
273
|
+
rgba(var(--a), 0.15) 0%,
|
|
274
|
+
transparent 50%
|
|
275
|
+
);
|
|
276
|
+
opacity: 0;
|
|
277
|
+
transition: opacity 0.3s ease;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.formula-container:hover .spotlight-effect {
|
|
281
|
+
opacity: 1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* 淡入动画 */
|
|
285
|
+
@keyframes fade-in {
|
|
286
|
+
from {
|
|
287
|
+
opacity: 0;
|
|
288
|
+
transform: translateY(-10px);
|
|
289
|
+
}
|
|
290
|
+
to {
|
|
291
|
+
opacity: 1;
|
|
292
|
+
transform: translateY(0);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.animate-fade-in {
|
|
297
|
+
animation: fade-in 0.2s ease-out;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Neon 效果脉冲 */
|
|
301
|
+
@keyframes neon-pulse {
|
|
302
|
+
0%,
|
|
303
|
+
100% {
|
|
304
|
+
box-shadow: 0 0 15px rgba(var(--a), 0.3);
|
|
305
|
+
}
|
|
306
|
+
50% {
|
|
307
|
+
box-shadow: 0 0 25px rgba(var(--a), 0.5);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
</style>
|
|
311
|
+
|
|
312
|
+
<script is:inline define:vars={{ componentId }}>
|
|
313
|
+
// 全局点击外部处理函数(使用事件委托,避免重复绑定)
|
|
314
|
+
let globalClickHandler = null;
|
|
315
|
+
|
|
316
|
+
function setupGlobalClickHandler() {
|
|
317
|
+
if (globalClickHandler) return; // 已经设置过
|
|
318
|
+
|
|
319
|
+
globalClickHandler = (event) => {
|
|
320
|
+
const target = event.target;
|
|
321
|
+
// 检查是否点击在 tooltip 或按钮外部
|
|
322
|
+
const allTooltips = document.querySelectorAll(
|
|
323
|
+
'.formula-tooltip:not(.cosy\\:hidden)'
|
|
324
|
+
);
|
|
325
|
+
allTooltips.forEach((tooltip) => {
|
|
326
|
+
const formulaId = tooltip.getAttribute('data-formula-id');
|
|
327
|
+
if (!formulaId) return;
|
|
328
|
+
|
|
329
|
+
const button = document.querySelector(
|
|
330
|
+
`.formula-info-btn[data-formula-id="${formulaId}"]`
|
|
331
|
+
);
|
|
332
|
+
if (
|
|
333
|
+
button &&
|
|
334
|
+
!button.contains(target) &&
|
|
335
|
+
!tooltip.contains(target)
|
|
336
|
+
) {
|
|
337
|
+
tooltip.classList.add('cosy:hidden');
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
document.addEventListener('click', globalClickHandler);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 处理 tooltip 显示/隐藏
|
|
346
|
+
function initTooltip(formulaId) {
|
|
347
|
+
const button = document.querySelector(
|
|
348
|
+
`.formula-info-btn[data-formula-id="${formulaId}"]`
|
|
349
|
+
);
|
|
350
|
+
const tooltip = document.querySelector(
|
|
351
|
+
`.formula-tooltip[data-formula-id="${formulaId}"]`
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (!button || !tooltip) return;
|
|
355
|
+
|
|
356
|
+
// 检查是否已经初始化过
|
|
357
|
+
if (button.hasAttribute('data-tooltip-initialized')) return;
|
|
358
|
+
|
|
359
|
+
const toggleTooltip = () => {
|
|
360
|
+
const isVisible = !tooltip.classList.contains('cosy:hidden');
|
|
361
|
+
// 关闭所有其他 tooltip
|
|
362
|
+
document
|
|
363
|
+
.querySelectorAll('.formula-tooltip')
|
|
364
|
+
.forEach((t) => t.classList.add('cosy:hidden'));
|
|
365
|
+
if (!isVisible) {
|
|
366
|
+
tooltip.classList.remove('cosy:hidden');
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
button.addEventListener('click', toggleTooltip);
|
|
371
|
+
button.setAttribute('data-tooltip-initialized', 'true');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 处理 details 折叠/展开
|
|
375
|
+
function initDetails(formulaId) {
|
|
376
|
+
const toggle = document.querySelector(
|
|
377
|
+
`.formula-details-toggle[data-formula-id="${formulaId}"]`
|
|
378
|
+
);
|
|
379
|
+
const content = document.querySelector(
|
|
380
|
+
`.formula-details-content[data-formula-id="${formulaId}"]`
|
|
381
|
+
);
|
|
382
|
+
const iconDown = toggle?.querySelector('.formula-details-icon-down');
|
|
383
|
+
const iconUp = toggle?.querySelector('.formula-details-icon-up');
|
|
384
|
+
|
|
385
|
+
if (!toggle || !content || !iconDown || !iconUp) return;
|
|
386
|
+
|
|
387
|
+
// 检查是否已经初始化过
|
|
388
|
+
if (toggle.hasAttribute('data-details-initialized')) return;
|
|
389
|
+
|
|
390
|
+
toggle.addEventListener('click', () => {
|
|
391
|
+
const isExpanded = !content.classList.contains('cosy:hidden');
|
|
392
|
+
if (isExpanded) {
|
|
393
|
+
content.classList.add('cosy:hidden');
|
|
394
|
+
// 切换图标:显示 down,隐藏 up
|
|
395
|
+
iconDown.classList.remove('cosy:hidden');
|
|
396
|
+
iconUp.classList.add('cosy:hidden');
|
|
397
|
+
} else {
|
|
398
|
+
content.classList.remove('cosy:hidden');
|
|
399
|
+
// 切换图标:隐藏 down,显示 up
|
|
400
|
+
iconDown.classList.add('cosy:hidden');
|
|
401
|
+
iconUp.classList.remove('cosy:hidden');
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
toggle.setAttribute('data-details-initialized', 'true');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// 处理 spotlight 鼠标跟踪效果
|
|
409
|
+
function initSpotlight(formulaId) {
|
|
410
|
+
const container = document.querySelector(
|
|
411
|
+
`.formula-container[data-formula-id="${formulaId}"][data-variant="spotlight"]`
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (!container) return;
|
|
415
|
+
|
|
416
|
+
// 检查是否已经初始化过
|
|
417
|
+
if (container.hasAttribute('data-spotlight-initialized')) return;
|
|
418
|
+
|
|
419
|
+
const handleMouseMove = (e) => {
|
|
420
|
+
const rect = container.getBoundingClientRect();
|
|
421
|
+
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
|
422
|
+
const y = ((e.clientY - rect.top) / rect.height) * 100;
|
|
423
|
+
container.style.setProperty('--mouse-x', `${x}%`);
|
|
424
|
+
container.style.setProperty('--mouse-y', `${y}%`);
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
container.addEventListener('mousemove', handleMouseMove);
|
|
428
|
+
// 初始化位置
|
|
429
|
+
container.style.setProperty('--mouse-x', '50%');
|
|
430
|
+
container.style.setProperty('--mouse-y', '50%');
|
|
431
|
+
container.setAttribute('data-spotlight-initialized', 'true');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// 初始化所有 MathFormula 组件
|
|
435
|
+
function initAllFormulas() {
|
|
436
|
+
// 设置全局点击处理(只需要设置一次)
|
|
437
|
+
setupGlobalClickHandler();
|
|
438
|
+
|
|
439
|
+
const containers = document.querySelectorAll(
|
|
440
|
+
'.formula-container[data-formula-id]'
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
containers.forEach((container) => {
|
|
444
|
+
const formulaId = container.getAttribute('data-formula-id');
|
|
445
|
+
if (!formulaId) return;
|
|
446
|
+
|
|
447
|
+
initTooltip(formulaId);
|
|
448
|
+
initDetails(formulaId);
|
|
449
|
+
initSpotlight(formulaId);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 首次加载时初始化
|
|
454
|
+
if (document.readyState === 'loading') {
|
|
455
|
+
document.addEventListener('DOMContentLoaded', initAllFormulas);
|
|
456
|
+
} else {
|
|
457
|
+
// 如果 DOM 已经加载完成,立即初始化
|
|
458
|
+
initAllFormulas();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// 客户端路由切换时重新初始化
|
|
462
|
+
document.addEventListener('astro:page-load', initAllFormulas);
|
|
463
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "astro/types";
|
|
2
|
+
import type { IMathFormulaPropsBase } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MathFormula 组件的 Astro 版本属性接口(继承基础接口并扩展 Astro 特定属性)
|
|
6
|
+
*/
|
|
7
|
+
export interface IMathFormulaProps
|
|
8
|
+
extends IMathFormulaPropsBase,
|
|
9
|
+
Omit<HTMLAttributes<"div">, keyof IMathFormulaPropsBase> {
|
|
10
|
+
// 继承基础属性和 HTML div 属性,但避免冲突
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MathFormula 组件的类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 视觉风格变体
|
|
7
|
+
*/
|
|
8
|
+
export type MathFormulaVariant =
|
|
9
|
+
| "default"
|
|
10
|
+
| "gradient"
|
|
11
|
+
| "glass"
|
|
12
|
+
| "neon"
|
|
13
|
+
| "card"
|
|
14
|
+
| "spotlight";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* MathFormula 组件的基础属性接口(与框架无关)
|
|
18
|
+
*/
|
|
19
|
+
export interface IMathFormulaPropsBase {
|
|
20
|
+
/**
|
|
21
|
+
* 公式标题,可选
|
|
22
|
+
*/
|
|
23
|
+
title?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 公式编号,可选
|
|
27
|
+
*/
|
|
28
|
+
number?: string | number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 视觉风格,支持多种预设样式
|
|
32
|
+
* @default "gradient"
|
|
33
|
+
*/
|
|
34
|
+
variant?: MathFormulaVariant;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 符号说明是否默认折叠
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
symbolsCollapsed?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 自定义 CSS 类名,用于覆盖默认样式
|
|
44
|
+
*/
|
|
45
|
+
class?: string;
|
|
46
|
+
}
|