@alphacifer/slidev-addon-theme 0.0.7 → 0.0.8

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 (105) hide show
  1. package/README.md +106 -76
  2. package/components/{chevron-card → card/chevron-card}/ChevronCard.vue +2 -2
  3. package/components/{chevron-card → card/chevron-card}/ChevronCardBadge.vue +1 -1
  4. package/components/{chevron-card → card/chevron-card}/ChevronCardContent.vue +1 -1
  5. package/components/{chevron-card → card/chevron-card}/ChevronCardHeading.vue +1 -1
  6. package/components/{horiz-card → card/horiz-card}/HorizCard.vue +2 -2
  7. package/components/{horiz-card → card/horiz-card}/HorizCardBadge.vue +2 -2
  8. package/components/{horiz-card → card/horiz-card}/HorizCardContent.vue +2 -2
  9. package/components/{horiz-card → card/horiz-card}/HorizCardHeading.vue +2 -2
  10. package/components/{horiz-card → card/horiz-card}/HorizCardIcon.vue +2 -2
  11. package/components/{vert-card → card/vert-card}/VertCard.vue +2 -2
  12. package/components/{vert-card → card/vert-card}/VertCardBadge.vue +5 -2
  13. package/components/{vert-card → card/vert-card}/VertCardContent.vue +2 -2
  14. package/components/{vert-card → card/vert-card}/VertCardHeading.vue +2 -2
  15. package/components/{vert-card → card/vert-card}/VertCardIcon.vue +2 -2
  16. package/components/{vert-card → card/vert-card}/VertCardTitle.vue +2 -2
  17. package/components/comparisons/arc-comparison/ArcComparison.vue +654 -0
  18. package/components/comparisons/arc-comparison/ArcComparisonBadge.vue +29 -0
  19. package/components/comparisons/arc-comparison/ArcComparisonContent.vue +29 -0
  20. package/components/comparisons/arc-comparison/ArcComparisonContents.vue +17 -0
  21. package/components/comparisons/arc-comparison/ArcComparisonHeading.vue +29 -0
  22. package/components/comparisons/arc-comparison/ArcComparisonLeft.vue +268 -0
  23. package/components/comparisons/arc-comparison/ArcComparisonOrbit.vue +47 -0
  24. package/components/comparisons/arc-comparison/ArcComparisonRight.vue +271 -0
  25. package/components/comparisons/arc-comparison/ArcComparisonTitle.vue +29 -0
  26. package/components/{comparison-table/ComparisonTable.vue → comparisons/table-comparison/TableComparison.vue} +37 -27
  27. package/components/{comparison-table/ComparisonTableCell.vue → comparisons/table-comparison/TableComparisonCell.vue} +8 -8
  28. package/components/{comparison-table/ComparisonTableCol.vue → comparisons/table-comparison/TableComparisonCol.vue} +10 -10
  29. package/components/{comparison-table/ComparisonTableCols.vue → comparisons/table-comparison/TableComparisonCols.vue} +11 -11
  30. package/components/{comparison-table/ComparisonTableRow.vue → comparisons/table-comparison/TableComparisonRow.vue} +9 -9
  31. package/components/{comparison-table/ComparisonTableRows.vue → comparisons/table-comparison/TableComparisonRows.vue} +3 -3
  32. package/components/core/ReflectedTitle.vue +2 -1
  33. package/components/core/arc-orbit/ArcOrbit.vue +468 -0
  34. package/components/{arc-compare/ArcCompareBadge.vue → core/arc-orbit/ArcOrbitBadge.vue} +11 -8
  35. package/components/core/arc-orbit/ArcOrbitCallout.vue +400 -0
  36. package/components/{arc-compare/ArcCompareContent.vue → core/arc-orbit/ArcOrbitContent.vue} +8 -8
  37. package/components/{arc-compare/ArcCompareContents.vue → core/arc-orbit/ArcOrbitContents.vue} +3 -3
  38. package/components/{arc-compare/ArcCompareHeading.vue → core/arc-orbit/ArcOrbitHeading.vue} +11 -8
  39. package/components/core/arc-orbit/ArcOrbitTitle.vue +50 -0
  40. package/components/{thanks → core/thanks}/ThanksOutlineSquare.vue +1 -1
  41. package/components/{thanks → core/thanks}/ThanksSquare.vue +1 -1
  42. package/components/{quad-hub/QuadHub.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetrad.vue} +71 -69
  43. package/components/{quad-hub/QuadHubCallout.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradCallout.vue} +45 -43
  44. package/components/{quad-hub/QuadHubCenter.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradCenter.vue} +4 -4
  45. package/components/{quad-hub/QuadHubContent.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradContent.vue} +6 -6
  46. package/components/{quad-hub/QuadHubHeading.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradHeading.vue} +6 -6
  47. package/components/{quad-hub/QuadHubIcon.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradIcon.vue} +6 -6
  48. package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriad.vue +3 -3
  49. package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadCallout.vue +1 -1
  50. package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadContent.vue +1 -1
  51. package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadHeading.vue +1 -1
  52. package/components/facets/triad/gear-triad/GearTriad.vue +591 -0
  53. package/components/facets/triad/gear-triad/GearTriadCallout.vue +285 -0
  54. package/components/facets/triad/gear-triad/GearTriadCenter.vue +41 -0
  55. package/components/facets/triad/gear-triad/GearTriadContent.vue +58 -0
  56. package/components/facets/triad/gear-triad/GearTriadContents.vue +111 -0
  57. package/components/facets/triad/gear-triad/GearTriadDescription.vue +38 -0
  58. package/components/facets/triad/gear-triad/GearTriadHeading.vue +38 -0
  59. package/components/facets/triad/gear-triad/GearTriadIcon.vue +157 -0
  60. package/components/{hex-triad → facets/triad/hex-triad}/HexTriad.vue +2 -2
  61. package/components/{hex-triad → facets/triad/hex-triad}/HexTriadBadge.vue +1 -1
  62. package/components/{hex-triad → facets/triad/hex-triad}/HexTriadCallout.vue +1 -1
  63. package/components/{hex-triad → facets/triad/hex-triad}/HexTriadHeading.vue +1 -1
  64. package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcess.vue +2 -2
  65. package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessArrow.vue +1 -1
  66. package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessCallout.vue +1 -1
  67. package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessContent.vue +1 -1
  68. package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessHeading.vue +1 -1
  69. package/package.json +1 -1
  70. package/utils/arcComparison/context.ts +40 -0
  71. package/utils/arcComparison/createArcComparisonGeometry.ts +175 -0
  72. package/utils/arcComparison/index.ts +3 -0
  73. package/utils/{arcCompare/resolveArcCompareItems.ts → arcComparison/resolveArcComparisonItems.ts} +24 -45
  74. package/utils/arcOrbit/context.ts +38 -0
  75. package/utils/{arcCompare/createArcCompareGeometry.ts → arcOrbit/createArcOrbitGeometry.ts} +77 -108
  76. package/utils/arcOrbit/index.ts +3 -0
  77. package/utils/arcOrbit/resolveArcOrbitItems.ts +92 -0
  78. package/utils/gearTriad/context.ts +51 -0
  79. package/utils/gearTriad/createGearTriadGeometry.ts +210 -0
  80. package/utils/gearTriad/index.ts +3 -0
  81. package/utils/gearTriad/resolveGearTriadItems.ts +159 -0
  82. package/utils/rectOrbitTetrad/context.ts +31 -0
  83. package/utils/{quadHub/createQuadHubGeometry.ts → rectOrbitTetrad/createRectOrbitTetradGeometry.ts} +33 -28
  84. package/utils/rectOrbitTetrad/index.ts +3 -0
  85. package/utils/{quadHub/resolveQuadHubItems.ts → rectOrbitTetrad/resolveRectOrbitTetradItems.ts} +25 -25
  86. package/utils/{comparisonTable → tableComparison}/context.ts +6 -6
  87. package/utils/useDiagramAutoScale.ts +1 -1
  88. package/components/arc-compare/ArcCompare.vue +0 -443
  89. package/components/arc-compare/ArcCompareCallout.vue +0 -386
  90. package/components/arc-compare/ArcCompareLeft.vue +0 -183
  91. package/components/arc-compare/ArcCompareRight.vue +0 -183
  92. package/components/arc-compare/ArcCompareTitle.vue +0 -47
  93. package/utils/arcCompare/context.ts +0 -36
  94. package/utils/arcCompare/index.ts +0 -3
  95. package/utils/quadHub/context.ts +0 -26
  96. package/utils/quadHub/index.ts +0 -3
  97. /package/components/{shifting-heading → core}/TransitionHeading.vue +0 -0
  98. /package/components/{thanks → core/thanks}/ThanksContent.vue +0 -0
  99. /package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadIcon.vue +0 -0
  100. /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadCenterBadge.vue +0 -0
  101. /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadContent.vue +0 -0
  102. /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadLayer.vue +0 -0
  103. /package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessIcon.vue +0 -0
  104. /package/utils/{comparisonTable → tableComparison}/constants.ts +0 -0
  105. /package/utils/{comparisonTable → tableComparison}/index.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, inject } from 'vue';
3
3
 
4
- import { ARROW_TRIAD_CALLOUT_KEY } from '../../utils/arrowTriad';
4
+ import { ARROW_TRIAD_CALLOUT_KEY } from '../../../../utils/arrowTriad';
5
5
 
6
6
  defineOptions({
7
7
  name: 'ArrowTriadHeading',
@@ -0,0 +1,591 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ inject,
6
+ onBeforeUpdate,
7
+ provide,
8
+ type Ref,
9
+ ref,
10
+ useSlots,
11
+ type VNode,
12
+ watch,
13
+ } from 'vue';
14
+
15
+ import {
16
+ createGearTriadGeometry,
17
+ GEAR_TRIAD_BASE_COLOR,
18
+ GEAR_TRIAD_ROOT_KEY,
19
+ type IGearTriadItem,
20
+ type IResolvedGearTriadItem,
21
+ resolveGearTriadItems,
22
+ resolveSingleGearTriadItem,
23
+ type TGearTriadIcon,
24
+ } from '../../../../utils/gearTriad';
25
+ import { useDiagramAutoScale } from '../../../../utils/useDiagramAutoScale';
26
+ import { useMergedUnoAttrs } from '../../../../utils/useMergedUnoAttrs';
27
+ import GearTriadCallout from './GearTriadCallout.vue';
28
+ import GearTriadCenter from './GearTriadCenter.vue';
29
+ import GearTriadIcon from './GearTriadIcon.vue';
30
+
31
+ defineOptions({
32
+ name: 'GearTriad',
33
+ inheritAttrs: false,
34
+ });
35
+
36
+ export interface IGearTriadProps {
37
+ readonly title?: string;
38
+ readonly subtitle?: string;
39
+ readonly items?: readonly IGearTriadItem[];
40
+ readonly icons?: readonly (TGearTriadIcon | undefined)[];
41
+ readonly animation?: boolean;
42
+ readonly active?: boolean;
43
+ readonly startDelay?: number;
44
+ readonly scale?: number;
45
+ readonly autoScale?: boolean;
46
+ readonly maxScale?: number;
47
+ readonly height?: number | string;
48
+ readonly centerIcon?: string | object | false;
49
+ readonly step?: number;
50
+ readonly clicks?: number;
51
+ readonly moved?: boolean;
52
+ }
53
+
54
+ const props = withDefaults(defineProps<IGearTriadProps>(), {
55
+ title: undefined,
56
+ subtitle: undefined,
57
+ items: undefined,
58
+ icons: undefined,
59
+ animation: true,
60
+ active: undefined,
61
+ startDelay: undefined,
62
+ scale: undefined,
63
+ autoScale: true,
64
+ maxScale: undefined,
65
+ height: undefined,
66
+ centerIcon: 'gear',
67
+ step: undefined,
68
+ clicks: undefined,
69
+ moved: undefined,
70
+ });
71
+
72
+ const emit = defineEmits<{
73
+ (e: 'click', event: MouseEvent): void;
74
+ (e: 'update:step', step: number): void;
75
+ (e: 'update:moved', moved: boolean): void;
76
+ }>();
77
+
78
+ const slots = useSlots();
79
+
80
+ const slidevClicks = inject<
81
+ | {
82
+ current: Ref<number> | ComputedRef<number> | number;
83
+ total?: Ref<number> | ComputedRef<number> | number;
84
+ }
85
+ | undefined
86
+ >('$$slidev-clicks-context', undefined);
87
+
88
+ const shiftingIntro = inject<
89
+ | {
90
+ active: ComputedRef<boolean>;
91
+ clicks?: Ref<number> | ComputedRef<number>;
92
+ isShifting?: ComputedRef<boolean>;
93
+ }
94
+ | undefined
95
+ >('SLIDEV_LAYOUT_SHIFTING_INTRO', undefined);
96
+
97
+ const internalStep = ref(props.step ?? 0);
98
+
99
+ interface INamedComponent {
100
+ readonly name?: string;
101
+ readonly __name?: string;
102
+ }
103
+
104
+ const registeredContentsCount = ref(0);
105
+ const registerContents = () => {
106
+ registeredContentsCount.value++;
107
+ };
108
+
109
+ function checkHasContentsVNodes(nodes: readonly VNode[] | undefined): boolean {
110
+ if (!nodes) {
111
+ return false;
112
+ }
113
+ for (const node of nodes) {
114
+ if (!node) {
115
+ continue;
116
+ }
117
+ if (typeof node.type === 'object' && node.type !== null) {
118
+ const comp = node.type as INamedComponent;
119
+ if (
120
+ comp.name === 'GearTriadContents' ||
121
+ comp.__name === 'GearTriadContents'
122
+ ) {
123
+ return true;
124
+ }
125
+ }
126
+ if (Array.isArray(node.children)) {
127
+ if (checkHasContentsVNodes(node.children as readonly VNode[])) {
128
+ return true;
129
+ }
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+
135
+ const slotHasContents = computed(() => {
136
+ return slots.default ? checkHasContentsVNodes(slots.default()) : false;
137
+ });
138
+
139
+ const supportsClicks = computed(() => {
140
+ return registeredContentsCount.value > 0 || slotHasContents.value;
141
+ });
142
+
143
+ watch(
144
+ () => props.step,
145
+ (val) => {
146
+ if (val !== undefined) {
147
+ internalStep.value = val;
148
+ }
149
+ },
150
+ );
151
+
152
+ watch(
153
+ () => props.moved,
154
+ (val) => {
155
+ if (val !== undefined) {
156
+ internalStep.value = val ? 1 : 0;
157
+ }
158
+ },
159
+ );
160
+
161
+ const resolvedStep = computed(() => {
162
+ if (props.step !== undefined) {
163
+ return props.step;
164
+ }
165
+ if (props.moved !== undefined) {
166
+ return props.moved ? 1 : 0;
167
+ }
168
+ if (props.clicks !== undefined) {
169
+ return props.clicks;
170
+ }
171
+ if (shiftingIntro?.clicks && typeof shiftingIntro.clicks.value === 'number') {
172
+ return shiftingIntro.clicks.value;
173
+ }
174
+ if (slidevClicks) {
175
+ const curr =
176
+ typeof slidevClicks.current === 'object' &&
177
+ 'value' in slidevClicks.current
178
+ ? slidevClicks.current.value
179
+ : (slidevClicks.current as number);
180
+ if (typeof curr === 'number') {
181
+ return curr;
182
+ }
183
+ }
184
+ return internalStep.value;
185
+ });
186
+
187
+ const isShifted = computed(() => resolvedStep.value > 0);
188
+
189
+ const activeOptionIndex = computed(() => {
190
+ if (resolvedStep.value <= 0) {
191
+ return 0;
192
+ }
193
+ return (resolvedStep.value - 1) % 3;
194
+ });
195
+
196
+ function handleDiagramClick(event: MouseEvent): void {
197
+ emit('click', event);
198
+ if (!supportsClicks.value) {
199
+ return;
200
+ }
201
+ const nextStep = (internalStep.value + 1) % 4;
202
+ internalStep.value = nextStep;
203
+ emit('update:step', nextStep);
204
+ emit('update:moved', nextStep > 0);
205
+ }
206
+
207
+ const diagramTranslateX = computed(() => {
208
+ return isShifted.value ? -260 : 0;
209
+ });
210
+
211
+ const gearRotation = computed(() => {
212
+ if (!isShifted.value) {
213
+ return 0;
214
+ }
215
+ if (activeOptionIndex.value === 0) {
216
+ return 150;
217
+ }
218
+ if (activeOptionIndex.value === 1) {
219
+ return 30;
220
+ }
221
+ return -90;
222
+ });
223
+
224
+ const isAnimated = computed(() => {
225
+ if (!props.animation) {
226
+ return false;
227
+ }
228
+
229
+ if (props.active !== undefined) {
230
+ return props.active;
231
+ }
232
+
233
+ if (shiftingIntro) {
234
+ return shiftingIntro.active.value;
235
+ }
236
+
237
+ return true;
238
+ });
239
+
240
+ const resolvedStartDelay = computed(() => {
241
+ if (props.startDelay !== undefined) {
242
+ return props.startDelay;
243
+ }
244
+
245
+ if (shiftingIntro) {
246
+ return shiftingIntro.isShifting?.value ? 600 : 0;
247
+ }
248
+
249
+ return 0;
250
+ });
251
+
252
+ const calloutOverrides = ref<Record<number, IGearTriadItem>>({});
253
+
254
+ const resolvedItems = computed(() => {
255
+ const base = resolveGearTriadItems(props.items, props.icons);
256
+
257
+ return [
258
+ resolveSingleGearTriadItem(
259
+ {
260
+ ...base[0],
261
+ ...(calloutOverrides.value[0] ?? {}),
262
+ },
263
+ 0,
264
+ ),
265
+ resolveSingleGearTriadItem(
266
+ {
267
+ ...base[1],
268
+ ...(calloutOverrides.value[1] ?? {}),
269
+ },
270
+ 1,
271
+ ),
272
+ resolveSingleGearTriadItem(
273
+ {
274
+ ...base[2],
275
+ ...(calloutOverrides.value[2] ?? {}),
276
+ },
277
+ 2,
278
+ ),
279
+ ] as const;
280
+ });
281
+
282
+ let calloutCounter = 0;
283
+ onBeforeUpdate(() => {
284
+ calloutCounter = 0;
285
+ });
286
+
287
+ const registerCallout = (
288
+ override?: ComputedRef<IGearTriadItem> | (() => IGearTriadItem),
289
+ ) => {
290
+ const idx = calloutCounter++;
291
+ if (override) {
292
+ watch(
293
+ typeof override === 'function' ? override : () => override.value,
294
+ (val) => {
295
+ if (val) {
296
+ calloutOverrides.value = {
297
+ ...calloutOverrides.value,
298
+ [idx]: val,
299
+ };
300
+ }
301
+ },
302
+ { immediate: true, deep: true },
303
+ );
304
+ }
305
+
306
+ return {
307
+ index: idx,
308
+ item: computed(() => resolvedItems.value[idx] ?? resolvedItems.value[0]),
309
+ };
310
+ };
311
+
312
+ provide(GEAR_TRIAD_ROOT_KEY, {
313
+ registerCallout,
314
+ registerContents,
315
+ hasContents: supportsClicks,
316
+ items: resolvedItems,
317
+ animation: isAnimated,
318
+ isShifted,
319
+ currentStep: resolvedStep,
320
+ activeOptionIndex,
321
+ });
322
+
323
+ const geo = createGearTriadGeometry();
324
+
325
+ const item0 = computed(() => resolvedItems.value[0]);
326
+ const item1 = computed(() => resolvedItems.value[1]);
327
+ const item2 = computed(() => resolvedItems.value[2]);
328
+
329
+ const sectors = computed(() => [
330
+ {
331
+ name: 'pink',
332
+ rotation: 0,
333
+ item: item2.value,
334
+ iconPosition: geo.pinkSector.iconPosition,
335
+ },
336
+ {
337
+ name: 'cyan',
338
+ rotation: 120,
339
+ item: item0.value,
340
+ iconPosition: geo.cyanSector.iconPosition,
341
+ },
342
+ {
343
+ name: 'orange',
344
+ rotation: 240,
345
+ item: item1.value,
346
+ iconPosition: geo.orangeSector.iconPosition,
347
+ },
348
+ ]);
349
+
350
+ function getSectorFaceColor(sector: {
351
+ name: string;
352
+ item: IResolvedGearTriadItem;
353
+ }): string {
354
+ if (!isShifted.value) {
355
+ return sector.item.color;
356
+ }
357
+ const focusedSectorName =
358
+ activeOptionIndex.value === 0
359
+ ? 'cyan'
360
+ : activeOptionIndex.value === 1
361
+ ? 'orange'
362
+ : 'pink';
363
+ if (sector.name === focusedSectorName) {
364
+ return sector.item.color;
365
+ }
366
+ return '#d5d8dc';
367
+ }
368
+
369
+ function getSectorIconColor(sector: { name: string }): string {
370
+ if (!isShifted.value) {
371
+ return '#0f172a';
372
+ }
373
+ const focusedSectorName =
374
+ activeOptionIndex.value === 0
375
+ ? 'cyan'
376
+ : activeOptionIndex.value === 1
377
+ ? 'orange'
378
+ : 'pink';
379
+ if (sector.name === focusedSectorName) {
380
+ return '#0f172a';
381
+ }
382
+ return '#ffffff';
383
+ }
384
+
385
+ const rootRef = ref<HTMLElement | null>(null);
386
+
387
+ const { resolvedScale, containerHeight } = useDiagramAutoScale({
388
+ rootRef,
389
+ baseWidth: 960,
390
+ baseHeight: 480,
391
+ scale: () => props.scale,
392
+ autoScale: () => props.autoScale,
393
+ maxScale: () => props.maxScale,
394
+ height: () => props.height,
395
+ });
396
+
397
+ const resolvedHeightStyle = computed(() => {
398
+ if (props.height !== undefined) {
399
+ return typeof props.height === 'number'
400
+ ? `${props.height}px`
401
+ : props.height;
402
+ }
403
+
404
+ if (containerHeight.value > 0) {
405
+ return `${containerHeight.value}px`;
406
+ }
407
+
408
+ return '100%';
409
+ });
410
+
411
+ const { className, forwardedAttrs } = useMergedUnoAttrs(
412
+ 'alpha-gear-triad w-full flex flex-col items-center justify-center select-none font-sans',
413
+ );
414
+ </script>
415
+
416
+ <template>
417
+ <div
418
+ ref="rootRef"
419
+ v-bind="forwardedAttrs()"
420
+ :class="className()"
421
+ :style="{
422
+ '--gear-start-delay': `${resolvedStartDelay}ms`,
423
+ height: resolvedHeightStyle,
424
+ }"
425
+ >
426
+ <!-- Header (Title / Subtitle) -->
427
+ <header
428
+ v-if="title || $slots.title"
429
+ class="alpha-gear-triad__header mb-2 text-center"
430
+ >
431
+ <slot name="title">
432
+ <h2 class="alpha-gear-triad__title text-2xl font-bold font-sans tracking-tight text-slate-900">
433
+ {{ title }}
434
+ </h2>
435
+ </slot>
436
+ <p
437
+ v-if="subtitle || $slots.subtitle"
438
+ class="alpha-gear-triad__subtitle text-sm font-sans text-slate-500 mt-1"
439
+ >
440
+ <slot name="subtitle">{{ subtitle }}</slot>
441
+ </p>
442
+ </header>
443
+ <!-- Auto-Scaled Stage -->
444
+ <div
445
+ class="alpha-gear-triad__scaler relative flex items-center justify-center origin-center transition-transform duration-200"
446
+ :style="{
447
+ width: '960px',
448
+ height: '480px',
449
+ transform: `scale(${resolvedScale})`,
450
+ }"
451
+ >
452
+ <!-- Central Gear SVG Assembly -->
453
+ <div
454
+ class="alpha-gear-triad__diagram relative flex items-center justify-center w-[540px] h-[480px] transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]"
455
+ :class="[
456
+ { 'alpha-gear-triad__diagram--animated': isAnimated },
457
+ supportsClicks ? 'cursor-pointer' : 'cursor-default',
458
+ ]"
459
+ :style="{
460
+ transform: `translateX(${diagramTranslateX}px)`,
461
+ }"
462
+ @click="handleDiagramClick"
463
+ >
464
+ <!-- Rotating Wheel (simultaneously rotates while diagram translates) -->
465
+ <div
466
+ class="alpha-gear-triad__wheel relative w-[540px] h-[480px] transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]"
467
+ :style="{
468
+ transformOrigin: `${geo.centerX}px ${geo.centerY}px`,
469
+ transform: `rotate(${gearRotation}deg)`,
470
+ }"
471
+ >
472
+ <svg
473
+ class="alpha-gear-triad__svg w-[540px] h-[480px] overflow-visible"
474
+ :viewBox="`0 0 ${geo.viewBoxWidth} ${geo.viewBoxHeight}`"
475
+ >
476
+ <defs>
477
+ <!-- Elevation Drop Shadow for Gear Assembly -->
478
+ <filter id="alpha-gear-assembly-shadow" x="-20%" y="-20%" width="140%" height="140%">
479
+ <feDropShadow dx="0" dy="10" stdDeviation="14" flood-color="#0f172a" flood-opacity="0.22" />
480
+ <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#0f172a" flood-opacity="0.10" />
481
+ </filter>
482
+ </defs>
483
+ <!-- Group containing all gear elements with overall elevation drop shadow -->
484
+ <g
485
+ filter="url(#alpha-gear-assembly-shadow)"
486
+ class="alpha-gear-triad__assembly origin-center"
487
+ >
488
+ <!-- LAYER 1: Inter-Sector Valley Shapes Between the 3 Petals (Reused for all 3 Places) -->
489
+ <path
490
+ v-for="sector in sectors"
491
+ :key="`valley-${sector.name}`"
492
+ :d="geo.petal.valleyPath"
493
+ :transform="`rotate(${sector.rotation}, ${geo.centerX}, ${geo.centerY})`"
494
+ :fill="GEAR_TRIAD_BASE_COLOR"
495
+ class="alpha-gear-triad__valley"
496
+ />
497
+ <!-- LAYER 2: Single Reusable Petal Surface Face Rotated for the 3 Sectors -->
498
+ <g
499
+ v-for="sector in sectors"
500
+ :key="`petal-${sector.name}`"
501
+ :transform="`rotate(${sector.rotation}, ${geo.centerX}, ${geo.centerY})`"
502
+ >
503
+ <path
504
+ :d="geo.petal.facePath"
505
+ :fill="getSectorFaceColor(sector)"
506
+ :class="`alpha-gear-triad__petal alpha-gear-triad__petal--${sector.name}`"
507
+ class="transition-colors duration-500 ease-out"
508
+ />
509
+ </g>
510
+ <!-- LAYER 4: Outline Icons Stamped on the 3 Petals -->
511
+ <foreignObject
512
+ v-for="sector in sectors"
513
+ v-show="sector.item.icon"
514
+ :key="`icon-${sector.name}`"
515
+ :x="sector.iconPosition.x - 17"
516
+ :y="sector.iconPosition.y - 17"
517
+ width="34"
518
+ height="34"
519
+ class="overflow-visible pointer-events-none select-none"
520
+ >
521
+ <div
522
+ class="w-full h-full flex items-center justify-center origin-center transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]"
523
+ :style="{
524
+ transform: `rotate(${-gearRotation}deg)`,
525
+ }"
526
+ >
527
+ <GearTriadIcon
528
+ :icon="sector.item.icon"
529
+ :size="34"
530
+ :color="getSectorIconColor(sector)"
531
+ :stroke-width="2"
532
+ />
533
+ </div>
534
+ </foreignObject>
535
+ </g>
536
+ </svg>
537
+ </div>
538
+ <!-- LAYER 5: Center Floating Circular Hub -->
539
+ <slot name="center">
540
+ <GearTriadCenter
541
+ :radius="geo.hubRadius"
542
+ :icon="props.centerIcon"
543
+ :icon-size="44"
544
+ :class="{ 'alpha-gear-center--animated': isAnimated }"
545
+ :style="{
546
+ top: `${geo.centerY}px`,
547
+ left: `${geo.centerX}px`,
548
+ }"
549
+ />
550
+ </slot>
551
+ </div>
552
+ <!-- LAYER 6: Surrounding Callouts & Detail Contents -->
553
+ <slot v-if="$slots.default" />
554
+ <template v-else>
555
+ <GearTriadCallout />
556
+ <GearTriadCallout />
557
+ <GearTriadCallout />
558
+ </template>
559
+ </div>
560
+ </div>
561
+ </template>
562
+
563
+ <style scoped>
564
+ .alpha-gear-triad__diagram--animated {
565
+ animation: gear-triad-fade-in 700ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--gear-start-delay, 0ms)) backwards;
566
+ }
567
+
568
+ .alpha-gear-center--animated {
569
+ animation: gear-center-pop-in 600ms cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--gear-start-delay, 0ms) + 100ms) backwards;
570
+ }
571
+
572
+ @keyframes gear-triad-fade-in {
573
+ 0% {
574
+ opacity: 0;
575
+ }
576
+ 100% {
577
+ opacity: 1;
578
+ }
579
+ }
580
+
581
+ @keyframes gear-center-pop-in {
582
+ 0% {
583
+ opacity: 0;
584
+ transform: translate(-50%, -50%) scale(0.6);
585
+ }
586
+ 100% {
587
+ opacity: 1;
588
+ transform: translate(-50%, -50%) scale(1);
589
+ }
590
+ }
591
+ </style>