@alphacifer/slidev-addon-theme 0.0.8 → 0.0.9

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.
Files changed (26) hide show
  1. package/README.md +80 -0
  2. package/components/pyramid/circular-pyramid/CircularPyramid.vue +871 -0
  3. package/components/pyramid/circular-pyramid/CircularPyramidStack.vue +375 -0
  4. package/components/pyramid/circular-pyramid/CircularPyramidStackContent.vue +208 -0
  5. package/components/pyramid/circular-pyramid/CircularPyramidStackTitle.vue +138 -0
  6. package/components/pyramid/square-pyramid/SquarePyramid.vue +876 -0
  7. package/components/pyramid/square-pyramid/SquarePyramidStack.vue +366 -0
  8. package/components/pyramid/square-pyramid/SquarePyramidStackContent.vue +204 -0
  9. package/components/pyramid/square-pyramid/SquarePyramidStackTitle.vue +134 -0
  10. package/package.json +2 -1
  11. package/utils/pyramid/circular-pyramid/context.ts +46 -0
  12. package/utils/pyramid/circular-pyramid/createCircularPyramidGeometry.ts +143 -0
  13. package/utils/pyramid/circular-pyramid/index.ts +3 -0
  14. package/utils/pyramid/circular-pyramid/types.ts +34 -0
  15. package/utils/pyramid/index.ts +3 -0
  16. package/utils/pyramid/shared/colors.ts +92 -0
  17. package/utils/pyramid/shared/connector.ts +111 -0
  18. package/utils/pyramid/shared/constants.ts +8 -0
  19. package/utils/pyramid/shared/index.ts +5 -0
  20. package/utils/pyramid/shared/stacking.ts +76 -0
  21. package/utils/pyramid/shared/types.ts +36 -0
  22. package/utils/pyramid/square-pyramid/context.ts +46 -0
  23. package/utils/pyramid/square-pyramid/createSquarePyramidGeometry.ts +265 -0
  24. package/utils/pyramid/square-pyramid/index.ts +3 -0
  25. package/utils/pyramid/square-pyramid/types.ts +39 -0
  26. package/utils/useDiagramAutoScale.ts +6 -8
@@ -0,0 +1,375 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ computed,
4
+ Fragment,
5
+ inject,
6
+ onUnmounted,
7
+ provide,
8
+ useSlots,
9
+ type VNode,
10
+ } from 'vue';
11
+
12
+ import {
13
+ CIRCULAR_PYRAMID_ROOT_KEY,
14
+ CIRCULAR_PYRAMID_STACK_KEY,
15
+ DEFAULT_CIRCULAR_PYRAMID_COLOR,
16
+ DEFAULT_CIRCULAR_PYRAMID_STACK_COLORS,
17
+ } from '../../../utils/pyramid/circular-pyramid';
18
+ import CircularPyramidStackContent from './CircularPyramidStackContent.vue';
19
+ import CircularPyramidStackTitle from './CircularPyramidStackTitle.vue';
20
+
21
+ defineOptions({
22
+ name: 'CircularPyramidStack',
23
+ });
24
+
25
+ export interface ICircularPyramidStackProps {
26
+ readonly index?: number;
27
+ readonly step?: string | number;
28
+ readonly title?: string;
29
+ readonly content?: string;
30
+ readonly color?: string;
31
+ readonly active?: boolean;
32
+ readonly connectorWidth?: number;
33
+ }
34
+
35
+ const props = withDefaults(defineProps<ICircularPyramidStackProps>(), {
36
+ index: undefined,
37
+ step: undefined,
38
+ title: undefined,
39
+ content: undefined,
40
+ color: undefined,
41
+ active: undefined,
42
+ connectorWidth: undefined,
43
+ });
44
+
45
+ const slots = useSlots();
46
+ const rootContext = inject(CIRCULAR_PYRAMID_ROOT_KEY, undefined);
47
+
48
+ interface INamedComponent {
49
+ readonly type?: {
50
+ readonly name?: string;
51
+ readonly displayName?: string;
52
+ };
53
+ }
54
+
55
+ function hasComponentName(type: unknown, targetName: string): boolean {
56
+ if (typeof type === 'object' && type !== null) {
57
+ const comp = type as INamedComponent['type'];
58
+ return comp?.name === targetName || comp?.displayName === targetName;
59
+ }
60
+ return false;
61
+ }
62
+
63
+ function flattenVNodes(nodes: VNode[]): VNode[] {
64
+ const result: VNode[] = [];
65
+ for (const node of nodes) {
66
+ if (node.type === Fragment && Array.isArray(node.children)) {
67
+ result.push(...flattenVNodes(node.children as VNode[]));
68
+ } else {
69
+ result.push(node);
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+
75
+ const defaultNodes = computed(() => {
76
+ if (!slots.default) {
77
+ return [];
78
+ }
79
+ return flattenVNodes(slots.default());
80
+ });
81
+
82
+ const slottedTitle = computed(() => {
83
+ return defaultNodes.value.find((node) =>
84
+ hasComponentName(node.type, 'CircularPyramidStackTitle'),
85
+ );
86
+ });
87
+
88
+ const slottedContent = computed(() => {
89
+ return defaultNodes.value.find((node) =>
90
+ hasComponentName(node.type, 'CircularPyramidStackContent'),
91
+ );
92
+ });
93
+
94
+ const stackToken = Symbol();
95
+ const effectiveColor = computed(() => {
96
+ return (
97
+ props.color || (slottedTitle.value?.props?.color as string | undefined)
98
+ );
99
+ });
100
+ const registration = rootContext?.registerStack(stackToken, effectiveColor);
101
+
102
+ onUnmounted(() => {
103
+ registration?.unregister();
104
+ });
105
+
106
+ const resolvedIndex = computed<number>(() => {
107
+ if (props.index !== undefined) {
108
+ return Number(props.index);
109
+ }
110
+ return registration?.index.value ?? 0;
111
+ });
112
+
113
+ const isActive = computed<boolean>(() => {
114
+ if (props.active !== undefined) {
115
+ return props.active;
116
+ }
117
+ if (!rootContext) {
118
+ return false;
119
+ }
120
+ return rootContext.activeIndex.value === resolvedIndex.value;
121
+ });
122
+
123
+ const resolvedStep = computed(() => {
124
+ if (props.step !== undefined) {
125
+ return props.step;
126
+ }
127
+ return String(resolvedIndex.value + 1).padStart(2, '0');
128
+ });
129
+
130
+ const layerGeometry = computed(() => {
131
+ if (!rootContext) {
132
+ return undefined;
133
+ }
134
+ return rootContext.geo.value.layers[resolvedIndex.value];
135
+ });
136
+
137
+ const posY = computed(() => {
138
+ if (!layerGeometry.value) {
139
+ return 0;
140
+ }
141
+ const geo = layerGeometry.value;
142
+ return (
143
+ geo.slabCenterY ??
144
+ Number((geo.cy + (geo.cylinderHeight ?? 0) / 2).toFixed(2))
145
+ );
146
+ });
147
+
148
+ const discRadiusX = computed(() => {
149
+ if (!layerGeometry.value) {
150
+ return 140;
151
+ }
152
+ return layerGeometry.value.rx;
153
+ });
154
+
155
+ const DOT_POSITION_X = 210;
156
+
157
+ const leftWidth = computed(() => {
158
+ if (layerGeometry.value) {
159
+ const rx = layerGeometry.value.rx;
160
+ const cx = 465;
161
+ return Math.max(180, Math.round(cx - rx - 40));
162
+ }
163
+ return DOT_POSITION_X;
164
+ });
165
+
166
+ const rightWidth = computed(() => rootContext?.contentWidthPx.value ?? 240);
167
+
168
+ const rightWingWidth = computed(() => {
169
+ if (layerGeometry.value) {
170
+ const rx = layerGeometry.value.rx;
171
+ const cx = 465;
172
+ const dotRightX = cx + rx + 40;
173
+ const containerW = 960;
174
+ return Math.max(rightWidth.value + 20, Math.round(containerW - dotRightX));
175
+ }
176
+ return rightWidth.value + 40;
177
+ });
178
+
179
+ const rightOffset = computed(
180
+ () => rootContext?.contentRightOffsetPx.value ?? 0,
181
+ );
182
+
183
+ const resolvedColor = computed<string>(() => {
184
+ if (effectiveColor.value) {
185
+ return effectiveColor.value;
186
+ }
187
+ return (
188
+ rootContext?.getLayerColor(resolvedIndex.value) ??
189
+ DEFAULT_CIRCULAR_PYRAMID_STACK_COLORS[
190
+ Math.abs(resolvedIndex.value) %
191
+ DEFAULT_CIRCULAR_PYRAMID_STACK_COLORS.length
192
+ ] ??
193
+ DEFAULT_CIRCULAR_PYRAMID_COLOR
194
+ );
195
+ });
196
+
197
+ const hasActiveStack = computed(() => {
198
+ return (rootContext?.activeIndex.value ?? -1) >= 0;
199
+ });
200
+
201
+ function handleStackClick(): void {
202
+ if (rootContext?.interactive.value) {
203
+ if (rootContext.activeIndex.value === resolvedIndex.value) {
204
+ rootContext.setActiveIndex(-1);
205
+ } else {
206
+ rootContext.setActiveIndex(resolvedIndex.value);
207
+ }
208
+ }
209
+ }
210
+
211
+ const deltaY = computed(() => {
212
+ return rootContext?.getCardDeltaY?.(resolvedIndex.value) ?? 0;
213
+ });
214
+
215
+ const connectorWidth = computed(() => {
216
+ return Math.max(20, rightWingWidth.value - rightWidth.value);
217
+ });
218
+
219
+ provide(CIRCULAR_PYRAMID_STACK_KEY, {
220
+ index: resolvedIndex,
221
+ isActive,
222
+ step: resolvedStep,
223
+ layerGeo: layerGeometry,
224
+ color: resolvedColor,
225
+ deltaY,
226
+ connectorWidth,
227
+ setActive: handleStackClick,
228
+ });
229
+ </script>
230
+
231
+ <template>
232
+ <div
233
+ class="alpha-circular-pyramid-stack absolute inset-x-0 pointer-events-none select-none flex items-center justify-between"
234
+ :class="[
235
+ isActive
236
+ ? 'alpha-circular-pyramid-stack--active'
237
+ : 'alpha-circular-pyramid-stack--inactive',
238
+ `alpha-circular-pyramid-stack--${resolvedIndex}`,
239
+ ]"
240
+ :data-index="resolvedIndex"
241
+ :data-active="isActive"
242
+ :style="{
243
+ top: `${posY}px`,
244
+ transform: 'translateY(-50%)',
245
+ }"
246
+ >
247
+ <!-- Left Wing: Title Pill (150px) -->
248
+ <div
249
+ class="alpha-circular-pyramid-stack__left flex-shrink-0 flex items-center justify-start pointer-events-auto cursor-pointer"
250
+ :class="[
251
+ `alpha-circular-pyramid-stack__left--${resolvedIndex}`,
252
+ { 'alpha-circular-pyramid-stack--animated': rootContext?.animation.value },
253
+ ]"
254
+ :style="{
255
+ width: `${leftWidth}px`,
256
+ minWidth: `${leftWidth}px`,
257
+ maxWidth: `${leftWidth}px`,
258
+ opacity: !hasActiveStack || isActive ? 1 : 0.65,
259
+ }"
260
+ @click="handleStackClick"
261
+ >
262
+ <component :is="slottedTitle" v-if="slottedTitle" />
263
+ <CircularPyramidStackTitle
264
+ v-else
265
+ :step="resolvedStep"
266
+ :title="props.title || `Stage ${resolvedStep}`"
267
+ :connector-width="props.connectorWidth"
268
+ />
269
+ </div>
270
+ <!-- Right Wing: Dot + Connector + Content Card (40px from slab) -->
271
+ <div
272
+ class="alpha-circular-pyramid-stack__right flex-shrink-0 flex items-center justify-end pointer-events-auto cursor-pointer"
273
+ :class="[
274
+ `alpha-circular-pyramid-stack__right--${resolvedIndex}`,
275
+ { 'alpha-circular-pyramid-stack--animated': rootContext?.animation.value },
276
+ ]"
277
+ :style="{
278
+ width: `${rightWingWidth}px`,
279
+ minWidth: `${rightWidth}px`,
280
+ maxWidth: `${rightWingWidth}px`,
281
+ marginRight: rightOffset ? `${rightOffset}px` : undefined,
282
+ opacity: !hasActiveStack || isActive ? 1 : 0.65,
283
+ }"
284
+ @click="handleStackClick"
285
+ >
286
+ <component :is="slottedContent" v-if="slottedContent" />
287
+ <CircularPyramidStackContent
288
+ v-else
289
+ :content="props.content || 'Layer details and operational description.'"
290
+ />
291
+ </div>
292
+ </div>
293
+ </template>
294
+
295
+ <style scoped>
296
+ .alpha-circular-pyramid-stack {
297
+ height: 0;
298
+ overflow: visible;
299
+ }
300
+
301
+ .alpha-circular-pyramid-stack__left,
302
+ .alpha-circular-pyramid-stack__right {
303
+ transition: opacity 0.3s ease, transform 0.3s ease;
304
+ }
305
+
306
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--0 {
307
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 100ms) both;
308
+ }
309
+
310
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--1 {
311
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 180ms) both;
312
+ }
313
+
314
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--2 {
315
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 260ms) both;
316
+ }
317
+
318
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--3 {
319
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 340ms) both;
320
+ }
321
+
322
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--4 {
323
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 420ms) both;
324
+ }
325
+
326
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__left--5 {
327
+ animation: alpha-pyramid-left-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 500ms) both;
328
+ }
329
+
330
+ @keyframes alpha-pyramid-left-in {
331
+ 0% {
332
+ opacity: 0;
333
+ transform: translateX(-12px);
334
+ }
335
+ 100% {
336
+ opacity: 1;
337
+ transform: translateX(0);
338
+ }
339
+ }
340
+
341
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--0 {
342
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 120ms) both;
343
+ }
344
+
345
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--1 {
346
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 200ms) both;
347
+ }
348
+
349
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--2 {
350
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 280ms) both;
351
+ }
352
+
353
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--3 {
354
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 360ms) both;
355
+ }
356
+
357
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--4 {
358
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 440ms) both;
359
+ }
360
+
361
+ .alpha-circular-pyramid-stack--animated.alpha-circular-pyramid-stack__right--5 {
362
+ animation: alpha-pyramid-right-in 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--circular-pyramid-start-delay, 0ms) + 520ms) both;
363
+ }
364
+
365
+ @keyframes alpha-pyramid-right-in {
366
+ 0% {
367
+ opacity: 0;
368
+ transform: translateX(12px);
369
+ }
370
+ 100% {
371
+ opacity: 1;
372
+ transform: translateX(0);
373
+ }
374
+ }
375
+ </style>
@@ -0,0 +1,208 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject, onMounted, onUnmounted, ref } from 'vue';
3
+
4
+ import {
5
+ CIRCULAR_PYRAMID_ROOT_KEY,
6
+ CIRCULAR_PYRAMID_STACK_KEY,
7
+ } from '../../../utils/pyramid/circular-pyramid';
8
+ import { createPyramidConnectorPath } from '../../../utils/pyramid/shared';
9
+
10
+ defineOptions({
11
+ name: 'CircularPyramidStackContent',
12
+ });
13
+
14
+ export interface ICircularPyramidStackContentProps {
15
+ readonly content?: string;
16
+ readonly color?: string;
17
+ readonly connectorWidth?: number;
18
+ }
19
+
20
+ const props = withDefaults(defineProps<ICircularPyramidStackContentProps>(), {
21
+ content: undefined,
22
+ color: undefined,
23
+ connectorWidth: undefined,
24
+ });
25
+
26
+ const rootContext = inject(CIRCULAR_PYRAMID_ROOT_KEY, undefined);
27
+ const stackContext = inject(CIRCULAR_PYRAMID_STACK_KEY, undefined);
28
+
29
+ const connectorRef = ref<HTMLElement | null>(null);
30
+ const measuredConnectorWidth = ref<number>(0);
31
+
32
+ let resizeObserver: ResizeObserver | null = null;
33
+
34
+ function updateWidth(): void {
35
+ if (connectorRef.value && connectorRef.value.clientWidth > 0) {
36
+ measuredConnectorWidth.value = connectorRef.value.clientWidth;
37
+ }
38
+ }
39
+
40
+ onMounted(() => {
41
+ updateWidth();
42
+ if (typeof ResizeObserver !== 'undefined' && connectorRef.value) {
43
+ resizeObserver = new ResizeObserver(updateWidth);
44
+ resizeObserver.observe(connectorRef.value);
45
+ }
46
+ });
47
+
48
+ onUnmounted(() => {
49
+ resizeObserver?.disconnect();
50
+ });
51
+
52
+ const isActive = computed(() => {
53
+ return stackContext?.isActive.value ?? false;
54
+ });
55
+
56
+ const palette = computed(() => {
57
+ return rootContext?.palette.value;
58
+ });
59
+
60
+ const contentWidth = computed(() => rootContext?.contentWidthPx.value ?? 240);
61
+
62
+ const deltaY = computed(() => {
63
+ if (stackContext?.deltaY?.value !== undefined) {
64
+ return stackContext.deltaY.value;
65
+ }
66
+ const idx = stackContext?.index.value ?? 0;
67
+ return rootContext?.getCardDeltaY?.(idx) ?? 0;
68
+ });
69
+
70
+ const effectiveDeltaY = computed(() => deltaY.value ?? 0);
71
+
72
+ const connectorWidth = computed(() => {
73
+ if (props.connectorWidth !== undefined) {
74
+ return props.connectorWidth;
75
+ }
76
+ if (stackContext?.connectorWidth?.value !== undefined) {
77
+ return stackContext.connectorWidth.value;
78
+ }
79
+ return measuredConnectorWidth.value || 40;
80
+ });
81
+
82
+ const svgMargin = 4;
83
+ const svgHeight = computed(() => {
84
+ return Math.max(8, Math.abs(effectiveDeltaY.value) + svgMargin * 2);
85
+ });
86
+
87
+ const svgMinY = computed(() => {
88
+ return Math.min(0, effectiveDeltaY.value) - svgMargin;
89
+ });
90
+
91
+ const svgTop = computed(() => {
92
+ return Math.min(0, effectiveDeltaY.value) - svgMargin;
93
+ });
94
+
95
+ const connectorPath = computed(() => {
96
+ const w = connectorWidth.value;
97
+ return createPyramidConnectorPath(
98
+ { x: 0, y: 0 },
99
+ { x: w, y: effectiveDeltaY.value },
100
+ );
101
+ });
102
+
103
+ const stackColor = computed(() => {
104
+ return (
105
+ props.color ??
106
+ stackContext?.color?.value ??
107
+ rootContext?.color.value ??
108
+ '#3b82f6'
109
+ );
110
+ });
111
+ </script>
112
+
113
+ <template>
114
+ <div class="alpha-circular-pyramid-stack-content-wrapper relative w-full flex items-center select-none overflow-visible">
115
+ <!-- Scalable Line between Dot and Content Card (always at least 20px) -->
116
+ <div
117
+ ref="connectorRef"
118
+ class="alpha-circular-pyramid-stack-content__connector flex-1 flex items-center min-w-[20px] pointer-events-none relative overflow-visible h-0"
119
+ >
120
+ <div
121
+ class="alpha-circular-pyramid-stack-content__dot absolute left-0 top-0 -translate-x-1/2 -translate-y-1/2 w-1.5 h-1.5 rounded-full flex-shrink-0 transition-all duration-300 pointer-events-auto"
122
+ :style="{
123
+ backgroundColor: stackColor,
124
+ boxShadow: isActive
125
+ ? `0 0 6px ${stackColor}`
126
+ : `0 0 2px ${stackColor}80`,
127
+ }"
128
+ />
129
+ <!-- Straight horizontal line when deltaY is 0 -->
130
+ <div
131
+ v-if="!effectiveDeltaY"
132
+ class="alpha-circular-pyramid-stack-content__line absolute left-0 top-0 w-full h-[1px] -translate-y-1/2 pointer-events-none transition-colors duration-300"
133
+ :style="{
134
+ backgroundColor: stackColor,
135
+ opacity: isActive ? 1 : 0.65,
136
+ }"
137
+ />
138
+ <!-- Zigzag SVG connector when deltaY !== 0 -->
139
+ <svg
140
+ v-else
141
+ class="alpha-circular-pyramid-stack-content__line-svg absolute left-0 pointer-events-none overflow-visible"
142
+ :width="connectorWidth"
143
+ :height="svgHeight"
144
+ :viewBox="`0 ${svgMinY} ${connectorWidth} ${svgHeight}`"
145
+ :style="{
146
+ top: `${svgTop}px`,
147
+ width: `${connectorWidth}px`,
148
+ height: `${svgHeight}px`,
149
+ }"
150
+ >
151
+ <path
152
+ class="alpha-circular-pyramid-stack-content__line transition-all duration-300"
153
+ :d="connectorPath"
154
+ fill="none"
155
+ :stroke="stackColor"
156
+ stroke-width="1.25"
157
+ stroke-linecap="round"
158
+ stroke-linejoin="round"
159
+ :style="{
160
+ opacity: isActive ? 1 : 0.65,
161
+ }"
162
+ />
163
+ </svg>
164
+ </div>
165
+ <!-- Content Card -->
166
+ <div
167
+ class="alpha-circular-pyramid-stack-content rounded-xl p-2 px-2.5 transition-all duration-300 border select-none box-border flex-shrink-0"
168
+ :class="[
169
+ isActive
170
+ ? 'alpha-circular-pyramid-stack-content--active'
171
+ : 'alpha-circular-pyramid-stack-content--default',
172
+ ]"
173
+ :style="{
174
+ width: `${contentWidth}px`,
175
+ minWidth: `${contentWidth}px`,
176
+ maxWidth: `${contentWidth}px`,
177
+ boxSizing: 'border-box',
178
+ borderColor: stackColor,
179
+ backgroundColor: isActive
180
+ ? 'rgba(255, 255, 255, 0.95)'
181
+ : 'rgba(255, 255, 255, 0.85)',
182
+ boxShadow: isActive
183
+ ? `0 4px 14px ${stackColor}26`
184
+ : 'none',
185
+ transform: deltaY ? `translateY(${deltaY}px)` : undefined,
186
+ }"
187
+ >
188
+ <div
189
+ class="text-[10px] leading-snug transition-colors duration-300 break-words line-clamp-3"
190
+ :style="{
191
+ color: isActive ? '#0f172a' : '#475569',
192
+ }"
193
+ >
194
+ <slot>{{ props.content }}</slot>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </template>
199
+
200
+ <style scoped>
201
+ .alpha-circular-pyramid-stack-content {
202
+ box-sizing: border-box;
203
+ }
204
+
205
+ .alpha-circular-pyramid-stack-content--active {
206
+ backdrop-filter: blur(8px);
207
+ }
208
+ </style>