@alphacifer/slidev-addon-theme 0.0.4 → 0.0.6

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 (84) hide show
  1. package/README.md +127 -2
  2. package/components/arc-arrow-process/ArcArrowProcess.vue +361 -0
  3. package/components/arc-arrow-process/ArcArrowProcessArrow.vue +143 -0
  4. package/components/arc-arrow-process/ArcArrowProcessCallout.vue +361 -0
  5. package/components/arc-arrow-process/ArcArrowProcessContent.vue +38 -0
  6. package/components/arc-arrow-process/ArcArrowProcessHeading.vue +43 -0
  7. package/components/arc-arrow-process/ArcArrowProcessIcon.vue +154 -0
  8. package/components/arc-compare/ArcCompare.vue +443 -0
  9. package/components/arc-compare/ArcCompareBadge.vue +63 -0
  10. package/components/arc-compare/ArcCompareCallout.vue +386 -0
  11. package/components/arc-compare/ArcCompareContent.vue +52 -0
  12. package/components/arc-compare/ArcCompareContents.vue +18 -0
  13. package/components/arc-compare/ArcCompareHeading.vue +52 -0
  14. package/components/arc-compare/ArcCompareLeft.vue +183 -0
  15. package/components/arc-compare/ArcCompareRight.vue +183 -0
  16. package/components/arc-compare/ArcCompareTitle.vue +47 -0
  17. package/components/arrow-triad/ArrowTriad.vue +368 -0
  18. package/components/arrow-triad/ArrowTriadCallout.vue +300 -0
  19. package/components/arrow-triad/ArrowTriadContent.vue +38 -0
  20. package/components/arrow-triad/ArrowTriadHeading.vue +38 -0
  21. package/components/arrow-triad/ArrowTriadIcon.vue +96 -0
  22. package/components/chevron-card/ChevronCard.vue +237 -0
  23. package/components/chevron-card/ChevronCardBadge.vue +74 -0
  24. package/components/chevron-card/ChevronCardContent.vue +39 -0
  25. package/components/chevron-card/ChevronCardHeading.vue +39 -0
  26. package/components/hex-triad/HexTriad.vue +438 -0
  27. package/components/hex-triad/HexTriadBadge.vue +110 -0
  28. package/components/hex-triad/HexTriadCallout.vue +200 -0
  29. package/components/hex-triad/HexTriadCenterBadge.vue +51 -0
  30. package/components/hex-triad/HexTriadContent.vue +20 -0
  31. package/components/hex-triad/HexTriadHeading.vue +28 -0
  32. package/components/hex-triad/HexTriadLayer.vue +28 -0
  33. package/components/horiz-card/HorizCard.vue +318 -0
  34. package/components/horiz-card/HorizCardBadge.vue +75 -0
  35. package/components/horiz-card/HorizCardContent.vue +46 -0
  36. package/components/horiz-card/HorizCardHeading.vue +47 -0
  37. package/components/horiz-card/HorizCardIcon.vue +118 -0
  38. package/components/quad-hub/QuadHub.vue +642 -0
  39. package/components/quad-hub/QuadHubCallout.vue +267 -0
  40. package/components/quad-hub/QuadHubCenter.vue +53 -0
  41. package/components/quad-hub/QuadHubContent.vue +39 -0
  42. package/components/quad-hub/QuadHubHeading.vue +44 -0
  43. package/components/quad-hub/QuadHubIcon.vue +112 -0
  44. package/components/shifting-heading/TransitionHeading.vue +3 -3
  45. package/components/vert-card/VertCard.vue +389 -0
  46. package/components/vert-card/VertCardBadge.vue +72 -0
  47. package/components/vert-card/VertCardContent.vue +45 -0
  48. package/components/vert-card/VertCardHeading.vue +68 -0
  49. package/components/vert-card/VertCardIcon.vue +126 -0
  50. package/components/vert-card/VertCardTitle.vue +76 -0
  51. package/layouts/core/arc-toc.vue +76 -113
  52. package/layouts/core/bg-center.vue +32 -4
  53. package/layouts/shifting-heading/shifting-intro.vue +35 -7
  54. package/package.json +3 -2
  55. package/utils/arcArrowProcess/context.ts +28 -0
  56. package/utils/arcArrowProcess/createArcArrowProcessGeometry.ts +137 -0
  57. package/utils/arcArrowProcess/index.ts +3 -0
  58. package/utils/arcArrowProcess/resolveArcArrowProcessItems.ts +189 -0
  59. package/utils/arcCompare/context.ts +36 -0
  60. package/utils/arcCompare/createArcCompareGeometry.ts +254 -0
  61. package/utils/arcCompare/index.ts +3 -0
  62. package/utils/arcCompare/resolveArcCompareItems.ts +123 -0
  63. package/utils/arrowTriad/context.ts +38 -0
  64. package/utils/arrowTriad/createArrowTriadGeometry.ts +63 -0
  65. package/utils/arrowTriad/index.ts +3 -0
  66. package/utils/arrowTriad/resolveArrowTriadItems.ts +121 -0
  67. package/utils/chevronCard/context.ts +12 -0
  68. package/utils/chevronCard/createChevronCardGeometry.ts +48 -0
  69. package/utils/chevronCard/index.ts +2 -0
  70. package/utils/hexTriad/context.ts +25 -0
  71. package/utils/hexTriad/createHexTriadGeometry.ts +182 -0
  72. package/utils/hexTriad/index.ts +3 -0
  73. package/utils/hexTriad/resolveHexTriadItems.ts +63 -0
  74. package/utils/horizCard/context.ts +13 -0
  75. package/utils/horizCard/createHorizCardGeometry.ts +243 -0
  76. package/utils/horizCard/index.ts +2 -0
  77. package/utils/quadHub/context.ts +26 -0
  78. package/utils/quadHub/createQuadHubGeometry.ts +331 -0
  79. package/utils/quadHub/index.ts +3 -0
  80. package/utils/quadHub/resolveQuadHubItems.ts +214 -0
  81. package/utils/useDiagramAutoScale.ts +300 -0
  82. package/utils/vertCard/context.ts +18 -0
  83. package/utils/vertCard/createVertCardGeometry.ts +242 -0
  84. package/utils/vertCard/index.ts +2 -0
@@ -0,0 +1,74 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject } from 'vue';
3
+
4
+ import { CHEVRON_CARD_KEY } from '../../utils/chevronCard';
5
+
6
+ defineOptions({
7
+ name: 'ChevronCardBadge',
8
+ });
9
+
10
+ export interface IChevronCardBadgeProps {
11
+ readonly step?: string | number;
12
+ readonly icon?: string | object;
13
+ readonly color?: string;
14
+ readonly width?: number;
15
+ readonly height?: number | string;
16
+ readonly notchDepth?: number;
17
+ }
18
+
19
+ const props = withDefaults(defineProps<IChevronCardBadgeProps>(), {
20
+ step: undefined,
21
+ icon: undefined,
22
+ color: undefined,
23
+ width: 130,
24
+ height: undefined,
25
+ notchDepth: 26,
26
+ });
27
+
28
+ const cardContext = inject(CHEVRON_CARD_KEY, undefined);
29
+
30
+ const resolvedColor = computed(() => {
31
+ return props.color ?? cardContext?.color.value ?? '#f59e0b';
32
+ });
33
+
34
+ const resolvedStep = computed(() => {
35
+ return props.step ?? cardContext?.step.value ?? '01';
36
+ });
37
+ </script>
38
+
39
+ <template>
40
+ <div
41
+ class="alpha-chevron-card-badge relative select-none flex-shrink-0 flex items-center justify-center text-white"
42
+ :style="{
43
+ width: `${props.width}px`,
44
+ height: props.height
45
+ ? typeof props.height === 'number'
46
+ ? `${props.height}px`
47
+ : props.height
48
+ : '100%',
49
+ backgroundColor: resolvedColor,
50
+ clipPath: `polygon(0% 0%, calc(100% - ${props.notchDepth}px) 0%, 100% 50%, calc(100% - ${props.notchDepth}px) 100%, 0% 100%, ${props.notchDepth}px 50%)`,
51
+ }"
52
+ >
53
+ <slot name="icon">
54
+ <slot>
55
+ <component
56
+ :is="props.icon"
57
+ v-if="typeof props.icon === 'object'"
58
+ class="w-8 h-8 text-white"
59
+ />
60
+ <span
61
+ v-else-if="typeof props.icon === 'string'"
62
+ :class="[props.icon, 'text-3xl text-white inline-block']"
63
+ />
64
+ <span
65
+ v-else
66
+ class="font-extrabold text-[32px] tracking-tight font-sans select-none"
67
+ style="font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"
68
+ >
69
+ {{ resolvedStep }}
70
+ </span>
71
+ </slot>
72
+ </slot>
73
+ </div>
74
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject } from 'vue';
3
+
4
+ import { CHEVRON_CARD_KEY } from '../../utils/chevronCard';
5
+
6
+ defineOptions({
7
+ name: 'ChevronCardContent',
8
+ });
9
+
10
+ export interface IChevronCardContentProps {
11
+ readonly color?: string;
12
+ }
13
+
14
+ const props = withDefaults(defineProps<IChevronCardContentProps>(), {
15
+ color: undefined,
16
+ });
17
+
18
+ const cardContext = inject(CHEVRON_CARD_KEY, undefined);
19
+
20
+ const contentColor = computed(() => {
21
+ return props.color ?? cardContext?.textColor.value ?? '#6b7280';
22
+ });
23
+ </script>
24
+
25
+ <template>
26
+ <div
27
+ class="alpha-chevron-card-content text-[1rem] leading-[1.45] font-normal tracking-tight select-none font-sans"
28
+ :style="{
29
+ color: contentColor,
30
+ margin: 0,
31
+ padding: 0,
32
+ fontSize: '1rem',
33
+ fontFamily:
34
+ 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
35
+ }"
36
+ >
37
+ <slot />
38
+ </div>
39
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject } from 'vue';
3
+
4
+ import { CHEVRON_CARD_KEY } from '../../utils/chevronCard';
5
+
6
+ defineOptions({
7
+ name: 'ChevronCardHeading',
8
+ });
9
+
10
+ export interface IChevronCardHeadingProps {
11
+ readonly color?: string;
12
+ }
13
+
14
+ const props = withDefaults(defineProps<IChevronCardHeadingProps>(), {
15
+ color: undefined,
16
+ });
17
+
18
+ const cardContext = inject(CHEVRON_CARD_KEY, undefined);
19
+
20
+ const headingColor = computed(() => {
21
+ return props.color ?? cardContext?.titleColor.value ?? '#4b5563';
22
+ });
23
+ </script>
24
+
25
+ <template>
26
+ <div
27
+ class="alpha-chevron-card-heading font-bold text-[1.25rem] uppercase tracking-wider leading-tight select-none font-sans"
28
+ :style="{
29
+ color: headingColor,
30
+ padding: 0,
31
+ fontSize: '1.25rem',
32
+ fontFamily:
33
+ 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
34
+ marginBottom: '0.25rem',
35
+ }"
36
+ >
37
+ <slot />
38
+ </div>
39
+ </template>
@@ -0,0 +1,438 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ inject,
6
+ onBeforeUpdate,
7
+ provide,
8
+ ref,
9
+ } from 'vue';
10
+
11
+ import {
12
+ calculateHexTriadCoordinates,
13
+ createRoundedHexagonPath,
14
+ HEX_TRIAD_ROOT_KEY,
15
+ type IHexTriadItem,
16
+ resolveHexTriadItems,
17
+ } from '../../utils/hexTriad';
18
+ import { useMergedUnoAttrs } from '../../utils/useMergedUnoAttrs';
19
+
20
+ defineOptions({
21
+ inheritAttrs: false,
22
+ });
23
+
24
+ export interface IHexTriadProps {
25
+ readonly title?: string;
26
+ readonly subtitle?: string;
27
+ readonly animation?: boolean;
28
+ readonly items?: readonly IHexTriadItem[];
29
+ /**
30
+ * Additional delay (ms) before animation starts.
31
+ * Defaults to 600ms inside `shifting-intro` layout, 0ms otherwise.
32
+ */
33
+ readonly startDelay?: number;
34
+ /**
35
+ * Manually control whether animation runs.
36
+ * Defaults to true, or automatically tracks `$clicks > 0` inside `shifting-intro`.
37
+ */
38
+ readonly active?: boolean;
39
+ }
40
+
41
+ const props = withDefaults(defineProps<IHexTriadProps>(), {
42
+ animation: true,
43
+ title: undefined,
44
+ items: undefined,
45
+ active: undefined,
46
+ subtitle: undefined,
47
+ startDelay: undefined,
48
+ });
49
+
50
+ const shiftingIntro = inject<
51
+ | {
52
+ active: ComputedRef<boolean>;
53
+ isShifting?: ComputedRef<boolean>;
54
+ }
55
+ | undefined
56
+ >('SLIDEV_LAYOUT_SHIFTING_INTRO', undefined);
57
+
58
+ const isAnimated = computed(() => {
59
+ if (!props.animation) {
60
+ return false;
61
+ }
62
+
63
+ if (props.active !== undefined) {
64
+ return props.active;
65
+ }
66
+
67
+ if (shiftingIntro) {
68
+ return shiftingIntro.active.value;
69
+ }
70
+
71
+ return true;
72
+ });
73
+
74
+ const resolvedStartDelay = computed(() => {
75
+ if (props.startDelay !== undefined) {
76
+ return props.startDelay;
77
+ }
78
+
79
+ if (shiftingIntro) {
80
+ // Only delay 600ms when the heading is actively shifting up from click 0.
81
+ // On reload at click >= 1, heading is already at the top -> 0ms delay!
82
+ return shiftingIntro.isShifting?.value ? 600 : 0;
83
+ }
84
+
85
+ return 0;
86
+ });
87
+
88
+ const resolvedItems = computed(() => resolveHexTriadItems(props.items));
89
+
90
+ let calloutCounter = 0;
91
+ const customBadgeTexts = ref<(string | number)[]>([]);
92
+
93
+ const registerCallout = () => {
94
+ const idx = calloutCounter++;
95
+ return {
96
+ index: idx,
97
+ item: computed(() => resolvedItems.value[idx] ?? resolvedItems.value[0]),
98
+ setBadgeText: (text: string | number) => {
99
+ customBadgeTexts.value[idx] = text;
100
+ },
101
+ };
102
+ };
103
+
104
+ onBeforeUpdate(() => {
105
+ calloutCounter = 0;
106
+ });
107
+
108
+ provide(HEX_TRIAD_ROOT_KEY, {
109
+ registerCallout,
110
+ items: resolvedItems,
111
+ animation: isAnimated,
112
+ });
113
+
114
+ const item1 = computed(() => {
115
+ return resolvedItems.value[0];
116
+ });
117
+ const item2 = computed(() => {
118
+ return resolvedItems.value[1];
119
+ });
120
+ const item3 = computed(() => {
121
+ return resolvedItems.value[2];
122
+ });
123
+
124
+ // Central diagram coordinates
125
+ const coords = calculateHexTriadCoordinates();
126
+ const {
127
+ hex1,
128
+ hex2,
129
+ hex3,
130
+ badge1,
131
+ badge2,
132
+ badge3,
133
+ centerX,
134
+ centerY,
135
+ hexRadius,
136
+ triadRadius,
137
+ badgeRadius,
138
+ } = coords;
139
+
140
+ const hex1X = hex1.x;
141
+ const hex1Y = hex1.y;
142
+ const hex2X = hex2.x;
143
+ const hex2Y = hex2.y;
144
+ const hex3X = hex3.x;
145
+ const hex3Y = hex3.y;
146
+
147
+ const badge1X = badge1.x;
148
+ const badge1Y = badge1.y;
149
+ const badge2X = badge2.x;
150
+ const badge2Y = badge2.y;
151
+ const badge3X = badge3.x;
152
+ const badge3Y = badge3.y;
153
+
154
+ const HEX_CORNER_RADIUS = 16;
155
+
156
+ // Second layer (background pastel hexagons):
157
+ // Aligned to first layer, then moved 1/4 block outward:
158
+ // - Red: top (-32px)
159
+ // - Orange: right-bottom (+27.71px, +16px)
160
+ // - Blue: left-bottom (-27.71px, +16px)
161
+ const QUARTER_SHIFT = 32;
162
+ const shiftCos30 = Number(
163
+ (QUARTER_SHIFT * Math.cos((30 * Math.PI) / 180)).toFixed(2),
164
+ );
165
+ const shiftSin30 = Number(
166
+ (QUARTER_SHIFT * Math.sin((30 * Math.PI) / 180)).toFixed(2),
167
+ );
168
+
169
+ const hex1BgPath = createRoundedHexagonPath(
170
+ hex1X,
171
+ hex1Y - QUARTER_SHIFT,
172
+ hexRadius,
173
+ HEX_CORNER_RADIUS,
174
+ );
175
+ const hex2BgPath = createRoundedHexagonPath(
176
+ hex2X + shiftCos30,
177
+ hex2Y + shiftSin30,
178
+ hexRadius,
179
+ HEX_CORNER_RADIUS,
180
+ );
181
+ const hex3BgPath = createRoundedHexagonPath(
182
+ hex3X - shiftCos30,
183
+ hex3Y + shiftSin30,
184
+ hexRadius,
185
+ HEX_CORNER_RADIUS,
186
+ );
187
+
188
+ // Middle layer (solid base at the same position as front layer)
189
+ const hex1BasePath = createRoundedHexagonPath(
190
+ hex1X,
191
+ hex1Y,
192
+ hexRadius,
193
+ HEX_CORNER_RADIUS,
194
+ );
195
+ const hex2BasePath = createRoundedHexagonPath(
196
+ hex2X,
197
+ hex2Y,
198
+ hexRadius,
199
+ HEX_CORNER_RADIUS,
200
+ );
201
+ const hex3BasePath = createRoundedHexagonPath(
202
+ hex3X,
203
+ hex3Y,
204
+ hexRadius,
205
+ HEX_CORNER_RADIUS,
206
+ );
207
+
208
+ // Foreground hexagons with rounded borders
209
+ const hex1TopPath = createRoundedHexagonPath(
210
+ hex1X,
211
+ hex1Y,
212
+ hexRadius,
213
+ HEX_CORNER_RADIUS,
214
+ );
215
+ const hex2TopPath = createRoundedHexagonPath(
216
+ hex2X,
217
+ hex2Y,
218
+ hexRadius,
219
+ HEX_CORNER_RADIUS,
220
+ );
221
+ const hex3TopPath = createRoundedHexagonPath(
222
+ hex3X,
223
+ hex3Y,
224
+ hexRadius,
225
+ HEX_CORNER_RADIUS,
226
+ );
227
+
228
+ const { className, forwardedAttrs } = useMergedUnoAttrs(
229
+ 'alpha-hex-triad w-full flex flex-col items-center justify-center select-none font-sans',
230
+ );
231
+ </script>
232
+
233
+ <template>
234
+ <div
235
+ v-bind="forwardedAttrs()"
236
+ :class="className()"
237
+ :style="{ '--hex-start-delay': `${resolvedStartDelay}ms` }"
238
+ >
239
+ <header v-if="title || $slots.title" class="alpha-hex-triad__header mb-4 text-center">
240
+ <slot name="title">
241
+ <h2 class="alpha-hex-triad__title text-2xl font-bold font-sans tracking-tight">
242
+ {{ title }}
243
+ </h2>
244
+ </slot>
245
+ <p v-if="subtitle || $slots.subtitle" class="alpha-hex-triad__subtitle text-sm font-sans">
246
+ <slot name="subtitle">{{ subtitle }}</slot>
247
+ </p>
248
+ </header>
249
+ <div class="alpha-hex-triad__stage relative w-full max-w-5xl h-[440px] flex items-center justify-center">
250
+ <!-- Center Column: Overlapping 3D Rounded Hexagon SVG -->
251
+ <div class="alpha-hex-triad__center relative flex items-center justify-center">
252
+ <svg
253
+ class="alpha-hex-triad__svg w-[450px] h-[420px] overflow-visible"
254
+ viewBox="0 0 540 440"
255
+ >
256
+ <defs>
257
+ <!-- Drop shadow for badges -->
258
+ <filter id="hex-badge-shadow" x="-30%" y="-30%" width="160%" height="160%">
259
+ <feDropShadow dx="0" dy="3" stdDeviation="4" flood-opacity="0.35" />
260
+ </filter>
261
+ <!-- 3D extrusion shadow for base layer -->
262
+ <filter id="hex-triad-depth" x="-20%" y="-20%" width="140%" height="140%">
263
+ <feDropShadow dx="0" dy="6" stdDeviation="6" flood-opacity="0.3" />
264
+ </filter>
265
+ <!-- Gradients for Hex 01 -->
266
+ <linearGradient id="grad-hex-1" x1="0%" y1="0%" x2="0%" y2="100%">
267
+ <stop offset="0%" :stop-color="item1.color" stop-opacity="0.95" />
268
+ <stop offset="100%" :stop-color="item1.depthColor" stop-opacity="0.9" />
269
+ </linearGradient>
270
+ <!-- Gradients for Hex 02 -->
271
+ <linearGradient id="grad-hex-2" x1="0%" y1="0%" x2="100%" y2="100%">
272
+ <stop offset="0%" :stop-color="item2.color" stop-opacity="0.95" />
273
+ <stop offset="100%" :stop-color="item2.depthColor" stop-opacity="0.9" />
274
+ </linearGradient>
275
+ <!-- Gradients for Hex 03 -->
276
+ <linearGradient id="grad-hex-3" x1="0%" y1="0%" x2="100%" y2="100%">
277
+ <stop offset="0%" :stop-color="item3.color" stop-opacity="0.95" />
278
+ <stop offset="100%" :stop-color="item3.depthColor" stop-opacity="0.9" />
279
+ </linearGradient>
280
+ </defs>
281
+ <!-- LAYER 1: Background Pastel Offset Hexagons (Animated: slides out from behind solid hexagon to offset position) -->
282
+ <g
283
+ class="alpha-hex-layer1 alpha-hex-layer1--3"
284
+ :class="{ 'alpha-hex-layer1--animated': isAnimated }"
285
+ >
286
+ <HexTriadLayer
287
+ :d="hex3BgPath"
288
+ :fill="item3.lightColor ?? item3.color"
289
+ :opacity="0.38"
290
+ />
291
+ </g>
292
+ <g
293
+ class="alpha-hex-layer1 alpha-hex-layer1--2"
294
+ :class="{ 'alpha-hex-layer1--animated': isAnimated }"
295
+ >
296
+ <HexTriadLayer
297
+ :d="hex2BgPath"
298
+ :fill="item2.lightColor ?? item2.color"
299
+ :opacity="0.38"
300
+ />
301
+ </g>
302
+ <g
303
+ class="alpha-hex-layer1 alpha-hex-layer1--1"
304
+ :class="{ 'alpha-hex-layer1--animated': isAnimated }"
305
+ >
306
+ <HexTriadLayer
307
+ :d="hex1BgPath"
308
+ :fill="item1.lightColor ?? item1.color"
309
+ :opacity="0.38"
310
+ />
311
+ </g>
312
+ <!-- LAYER 2: Middle 3D Depth Base Hexagons (3 hexa blocks) -->
313
+ <HexTriadLayer
314
+ :d="hex3BasePath"
315
+ :fill="item3.depthColor"
316
+ filter="url(#hex-triad-depth)"
317
+ :opacity="0.85"
318
+ />
319
+ <HexTriadLayer
320
+ :d="hex2BasePath"
321
+ :fill="item2.depthColor"
322
+ filter="url(#hex-triad-depth)"
323
+ :opacity="0.85"
324
+ />
325
+ <HexTriadLayer
326
+ :d="hex1BasePath"
327
+ :fill="item1.depthColor"
328
+ filter="url(#hex-triad-depth)"
329
+ :opacity="0.85"
330
+ />
331
+ <!-- LAYER 3: Foreground Hexagons with Gradients (3 hexa blocks) -->
332
+ <HexTriadLayer
333
+ :d="hex3TopPath"
334
+ fill="url(#grad-hex-3)"
335
+ :opacity="0.9"
336
+ />
337
+ <HexTriadLayer
338
+ :d="hex2TopPath"
339
+ fill="url(#grad-hex-2)"
340
+ :opacity="0.9"
341
+ />
342
+ <HexTriadLayer
343
+ :d="hex1TopPath"
344
+ fill="url(#grad-hex-1)"
345
+ :opacity="0.9"
346
+ />
347
+ <!-- LAYER 4: White Interconnecting Ring (Circumcircle) -->
348
+ <circle
349
+ class="alpha-hex-triad__circumcircle"
350
+ :cx="centerX"
351
+ :cy="centerY"
352
+ :r="triadRadius"
353
+ fill="none"
354
+ stroke="#ffffff"
355
+ stroke-width="2.5"
356
+ stroke-linecap="round"
357
+ :style="{ opacity: 0.95 }"
358
+ />
359
+ <!-- LAYER 5: Center Circular Badges (Centered Directly on the White Circle) -->
360
+ <!-- Badge 03 (Bottom-Left) -->
361
+ <HexTriadCenterBadge
362
+ :x="badge3X"
363
+ :y="badge3Y"
364
+ :radius="badgeRadius"
365
+ :color="item3.color"
366
+ :text="customBadgeTexts[2] || item3.id"
367
+ />
368
+ <!-- Badge 02 (Bottom-Right) -->
369
+ <HexTriadCenterBadge
370
+ :x="badge2X"
371
+ :y="badge2Y"
372
+ :radius="badgeRadius"
373
+ :color="item2.color"
374
+ :text="customBadgeTexts[1] || item2.id"
375
+ />
376
+ <!-- Badge 01 (Top) -->
377
+ <HexTriadCenterBadge
378
+ :x="badge1X"
379
+ :y="badge1Y"
380
+ :radius="badgeRadius"
381
+ :color="item1.color"
382
+ :text="customBadgeTexts[0] || item1.id"
383
+ />
384
+ </svg>
385
+ </div>
386
+ <!-- If default slot provided, render user's <HexTriadCallout> components -->
387
+ <slot v-if="$slots.default" />
388
+ <!-- Fallback: render 3 default callouts if no slots provided -->
389
+ <template v-else>
390
+ <HexTriadCallout />
391
+ <HexTriadCallout />
392
+ <HexTriadCallout />
393
+ </template>
394
+ </div>
395
+ </div>
396
+ </template>
397
+
398
+ <style scoped>
399
+ /* Layer 1 (Pastel Hexagons) slides out with overlapping cascading stagger */
400
+ .alpha-hex-layer1--1.alpha-hex-layer1--animated {
401
+ animation: hex-layer1-slide-1 500ms cubic-bezier(0.25, 1, 0.5, 1) calc(var(--hex-start-delay, 0ms) + 100ms) both;
402
+ }
403
+
404
+ .alpha-hex-layer1--2.alpha-hex-layer1--animated {
405
+ animation: hex-layer1-slide-2 500ms cubic-bezier(0.25, 1, 0.5, 1) calc(var(--hex-start-delay, 0ms) + 300ms) both;
406
+ }
407
+
408
+ .alpha-hex-layer1--3.alpha-hex-layer1--animated {
409
+ animation: hex-layer1-slide-3 500ms cubic-bezier(0.25, 1, 0.5, 1) calc(var(--hex-start-delay, 0ms) + 500ms) both;
410
+ }
411
+
412
+ @keyframes hex-layer1-slide-1 {
413
+ 0% {
414
+ transform: translate(0px, 32px);
415
+ }
416
+ 100% {
417
+ transform: translate(0px, 0px);
418
+ }
419
+ }
420
+
421
+ @keyframes hex-layer1-slide-2 {
422
+ 0% {
423
+ transform: translate(-27.71px, -16px);
424
+ }
425
+ 100% {
426
+ transform: translate(0px, 0px);
427
+ }
428
+ }
429
+
430
+ @keyframes hex-layer1-slide-3 {
431
+ 0% {
432
+ transform: translate(27.71px, -16px);
433
+ }
434
+ 100% {
435
+ transform: translate(0px, 0px);
436
+ }
437
+ }
438
+ </style>
@@ -0,0 +1,110 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject, useSlots, watchEffect } from 'vue';
3
+
4
+ import {
5
+ createRoundedHexagonPath,
6
+ HEX_TRIAD_CALLOUT_KEY,
7
+ } from '../../utils/hexTriad';
8
+
9
+ defineOptions({
10
+ name: 'HexTriadBadge',
11
+ });
12
+
13
+ export interface IHexTriadBadgeProps {
14
+ readonly id?: string | number;
15
+ readonly color?: string;
16
+ readonly lightColor?: string;
17
+ }
18
+
19
+ const props = withDefaults(defineProps<IHexTriadBadgeProps>(), {
20
+ id: undefined,
21
+ color: undefined,
22
+ lightColor: undefined,
23
+ });
24
+
25
+ defineSlots<{
26
+ default?: () => unknown;
27
+ }>();
28
+
29
+ const calloutContext = inject(HEX_TRIAD_CALLOUT_KEY, undefined);
30
+ const slots = useSlots();
31
+
32
+ const miniHexPath = createRoundedHexagonPath(18, 18, 17, 4.5);
33
+
34
+ const badgeColor = computed(() => {
35
+ return props.color ?? calloutContext?.item.value.color ?? '#e11d48';
36
+ });
37
+
38
+ const badgeLightColor = computed(() => {
39
+ return (
40
+ props.lightColor ??
41
+ calloutContext?.item.value.lightColor ??
42
+ calloutContext?.item.value.color ??
43
+ '#fb7185'
44
+ );
45
+ });
46
+
47
+ const fallbackId = computed(() => {
48
+ return props.id ?? calloutContext?.item.value.id ?? '01';
49
+ });
50
+
51
+ // Extract text from default slot or prop if available
52
+ const badgeText = computed<string | number>(() => {
53
+ if (props.id !== undefined) {
54
+ return props.id;
55
+ }
56
+
57
+ if (!slots.default) {
58
+ return fallbackId.value;
59
+ }
60
+
61
+ const nodes = slots.default();
62
+ const text = nodes
63
+ .map((node) => {
64
+ if (typeof node.children === 'string') {
65
+ return node.children;
66
+ }
67
+
68
+ if (typeof node.children === 'number') {
69
+ return String(node.children);
70
+ }
71
+
72
+ return '';
73
+ })
74
+ .join('')
75
+ .trim();
76
+ return text || fallbackId.value;
77
+ });
78
+
79
+ watchEffect(() => {
80
+ if (calloutContext?.setBadgeText) {
81
+ calloutContext.setBadgeText(badgeText.value);
82
+ }
83
+ });
84
+ </script>
85
+
86
+ <template>
87
+ <svg class="alpha-hex-triad-badge w-10 h-10 drop-shadow flex-shrink-0" viewBox="0 0 36 36">
88
+ <path class="alpha-hex-triad-badge__bg" :d="miniHexPath" :fill="badgeLightColor" :style="{ opacity: 0.45 }" />
89
+ <path
90
+ class="alpha-hex-triad-badge__border"
91
+ :d="miniHexPath"
92
+ fill="none"
93
+ :stroke="badgeLightColor"
94
+ stroke-width="1"
95
+ :style="{ opacity: 0.8 }"
96
+ />
97
+ <circle class="alpha-hex-triad-badge__circle" cx="18" cy="18" r="11" :fill="badgeColor" />
98
+ <text
99
+ class="alpha-hex-triad-badge__text font-black text-[10px] font-sans select-none"
100
+ x="18"
101
+ y="21.5"
102
+ text-anchor="middle"
103
+ fill="#ffffff"
104
+ >
105
+ <tspan>
106
+ <slot>{{ fallbackId }}</slot>
107
+ </tspan>
108
+ </text>
109
+ </svg>
110
+ </template>