@alphacifer/slidev-addon-theme 0.0.5 → 0.0.7

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 (93) hide show
  1. package/README.md +163 -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/comparison-table/ComparisonTable.vue +244 -0
  27. package/components/comparison-table/ComparisonTableCell.vue +88 -0
  28. package/components/comparison-table/ComparisonTableCol.vue +97 -0
  29. package/components/comparison-table/ComparisonTableCols.vue +74 -0
  30. package/components/comparison-table/ComparisonTableRow.vue +92 -0
  31. package/components/comparison-table/ComparisonTableRows.vue +21 -0
  32. package/components/hex-triad/HexTriad.vue +438 -0
  33. package/components/hex-triad/HexTriadBadge.vue +110 -0
  34. package/components/hex-triad/HexTriadCallout.vue +200 -0
  35. package/components/hex-triad/HexTriadCenterBadge.vue +51 -0
  36. package/components/hex-triad/HexTriadContent.vue +20 -0
  37. package/components/hex-triad/HexTriadHeading.vue +28 -0
  38. package/components/hex-triad/HexTriadLayer.vue +28 -0
  39. package/components/horiz-card/HorizCard.vue +318 -0
  40. package/components/horiz-card/HorizCardBadge.vue +75 -0
  41. package/components/horiz-card/HorizCardContent.vue +46 -0
  42. package/components/horiz-card/HorizCardHeading.vue +47 -0
  43. package/components/horiz-card/HorizCardIcon.vue +118 -0
  44. package/components/quad-hub/QuadHub.vue +642 -0
  45. package/components/quad-hub/QuadHubCallout.vue +267 -0
  46. package/components/quad-hub/QuadHubCenter.vue +53 -0
  47. package/components/quad-hub/QuadHubContent.vue +39 -0
  48. package/components/quad-hub/QuadHubHeading.vue +44 -0
  49. package/components/quad-hub/QuadHubIcon.vue +112 -0
  50. package/components/shifting-heading/TransitionHeading.vue +2 -2
  51. package/components/vert-card/VertCard.vue +389 -0
  52. package/components/vert-card/VertCardBadge.vue +72 -0
  53. package/components/vert-card/VertCardContent.vue +45 -0
  54. package/components/vert-card/VertCardHeading.vue +68 -0
  55. package/components/vert-card/VertCardIcon.vue +126 -0
  56. package/components/vert-card/VertCardTitle.vue +76 -0
  57. package/layouts/core/arc-toc.vue +76 -113
  58. package/layouts/core/bg-center.vue +32 -4
  59. package/layouts/shifting-heading/shifting-intro.vue +35 -7
  60. package/package.json +3 -2
  61. package/utils/arcArrowProcess/context.ts +28 -0
  62. package/utils/arcArrowProcess/createArcArrowProcessGeometry.ts +137 -0
  63. package/utils/arcArrowProcess/index.ts +3 -0
  64. package/utils/arcArrowProcess/resolveArcArrowProcessItems.ts +189 -0
  65. package/utils/arcCompare/context.ts +36 -0
  66. package/utils/arcCompare/createArcCompareGeometry.ts +254 -0
  67. package/utils/arcCompare/index.ts +3 -0
  68. package/utils/arcCompare/resolveArcCompareItems.ts +123 -0
  69. package/utils/arrowTriad/context.ts +38 -0
  70. package/utils/arrowTriad/createArrowTriadGeometry.ts +63 -0
  71. package/utils/arrowTriad/index.ts +3 -0
  72. package/utils/arrowTriad/resolveArrowTriadItems.ts +121 -0
  73. package/utils/chevronCard/context.ts +12 -0
  74. package/utils/chevronCard/createChevronCardGeometry.ts +48 -0
  75. package/utils/chevronCard/index.ts +2 -0
  76. package/utils/comparisonTable/constants.ts +72 -0
  77. package/utils/comparisonTable/context.ts +24 -0
  78. package/utils/comparisonTable/index.ts +2 -0
  79. package/utils/hexTriad/context.ts +25 -0
  80. package/utils/hexTriad/createHexTriadGeometry.ts +182 -0
  81. package/utils/hexTriad/index.ts +3 -0
  82. package/utils/hexTriad/resolveHexTriadItems.ts +63 -0
  83. package/utils/horizCard/context.ts +13 -0
  84. package/utils/horizCard/createHorizCardGeometry.ts +243 -0
  85. package/utils/horizCard/index.ts +2 -0
  86. package/utils/quadHub/context.ts +26 -0
  87. package/utils/quadHub/createQuadHubGeometry.ts +331 -0
  88. package/utils/quadHub/index.ts +3 -0
  89. package/utils/quadHub/resolveQuadHubItems.ts +214 -0
  90. package/utils/useDiagramAutoScale.ts +300 -0
  91. package/utils/vertCard/context.ts +18 -0
  92. package/utils/vertCard/createVertCardGeometry.ts +242 -0
  93. package/utils/vertCard/index.ts +2 -0
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Shared Slidev components and layouts for Alpha presentations.
4
4
 
5
+ For Alpha's light-only academic styling, combine this addon with
6
+ `@alphacifer/slidev-theme-academic`.
7
+
5
8
  ## Installation
6
9
 
7
10
  ```bash
@@ -31,14 +34,172 @@ addons:
31
34
  - Components: `ThanksContent`, `ThanksOutlineSquare`, `ThanksSquare`
32
35
  - Layout: `thanks`
33
36
 
37
+ ## Hex triad
38
+
39
+ - Components: `HexTriad`, `HexTriadCallout`, `HexTriadBadge`, `HexTriadHeading`, `HexTriadContent`
40
+
41
+ ```vue
42
+ <HexTriad>
43
+ <HexTriadCallout>
44
+ <HexTriadBadge>01</HexTriadBadge>
45
+ <HexTriadHeading>Performance</HexTriadHeading>
46
+ <HexTriadContent>Fast builds, instant feedback, and zero-overhead runtime.</HexTriadContent>
47
+ </HexTriadCallout>
48
+ <HexTriadCallout>
49
+ <HexTriadBadge>02</HexTriadBadge>
50
+ <HexTriadHeading>Reliability</HexTriadHeading>
51
+ <HexTriadContent>Strict type safety paired with deterministic test suites.</HexTriadContent>
52
+ </HexTriadCallout>
53
+ <HexTriadCallout>
54
+ <HexTriadBadge>03</HexTriadBadge>
55
+ <HexTriadHeading>Scalability</HexTriadHeading>
56
+ <HexTriadContent>Seamless growth across packages, tools, and workflows.</HexTriadContent>
57
+ </HexTriadCallout>
58
+ </HexTriad>
59
+ ```
60
+
61
+ ## Horiz card
62
+
63
+ - Components: `HorizCard`, `HorizCardBadge`, `HorizCardHeading`, `HorizCardContent`, `HorizCardIcon`
64
+
65
+ ```vue
66
+ <!-- Self-contained props usage with optional icon -->
67
+ <HorizCard
68
+ step="01"
69
+ title="Lorem Ipsum"
70
+ description="Lorem ipsum dolor sit amet, nibh est. A magna maecenas, quam magna nec quis."
71
+ />
72
+
73
+ <!-- With custom icon and color -->
74
+ <HorizCard
75
+ step="02"
76
+ title="Discovery"
77
+ description="Explore ideas and prototype user experiences."
78
+ color="#2563eb"
79
+ >
80
+ <template #icon>
81
+ <div class="i-carbon-search text-4xl" />
82
+ </template>
83
+ </HorizCard>
84
+ ```
85
+
86
+ ## Arc compare
87
+
88
+ - Components: `ArcCompare`, `ArcCompareLeft`, `ArcCompareRight`, `ArcCompareTitle`, `ArcCompareContents`, `ArcCompareCallout`, `ArcCompareBadge`, `ArcCompareHeading`, `ArcCompareContent`
89
+
90
+ ```vue
91
+ <!-- Self-contained mockup default (3 points) -->
92
+
93
+ <!-- 4 points mode -->
94
+ <ArcCompare>
95
+ <ArcCompareLeft color="#ea580c">
96
+ <ArcCompareTitle>Traditional<br />Monolith</ArcCompareTitle>
97
+ <ArcCompareContents>
98
+ <ArcCompareCallout>
99
+ <ArcCompareBadge>01</ArcCompareBadge>
100
+ <ArcCompareHeading>Coupled State</ArcCompareHeading>
101
+ <ArcCompareContent>
102
+ Shared database schemas create cross-team deployment bottlenecks.
103
+ </ArcCompareContent>
104
+ </ArcCompareCallout>
105
+ <ArcCompareCallout>
106
+ <ArcCompareBadge>02</ArcCompareBadge>
107
+ <ArcCompareHeading>Vertical Scale</ArcCompareHeading>
108
+ <ArcCompareContent>
109
+ Requires upgrading single instances with exponential cost increases.
110
+ </ArcCompareContent>
111
+ </ArcCompareCallout>
112
+ <ArcCompareCallout>
113
+ <ArcCompareBadge>03</ArcCompareBadge>
114
+ <ArcCompareHeading>Single Failure</ArcCompareHeading>
115
+ <ArcCompareContent>
116
+ Unchecked memory leaks or crashes can bring down the entire system.
117
+ </ArcCompareContent>
118
+ </ArcCompareCallout>
119
+ </ArcCompareContents>
120
+ </ArcCompareLeft>
121
+
122
+ <ArcCompareRight color="#0284c7">
123
+ <ArcCompareTitle>Distributed<br />Microservices</ArcCompareTitle>
124
+ <ArcCompareContents>
125
+ <ArcCompareCallout>
126
+ <ArcCompareBadge>01</ArcCompareBadge>
127
+ <ArcCompareHeading>Bounded Context</ArcCompareHeading>
128
+ <ArcCompareContent>
129
+ Autonomous services communicate over defined asynchronous events.
130
+ </ArcCompareContent>
131
+ </ArcCompareCallout>
132
+ <ArcCompareCallout>
133
+ <ArcCompareBadge>02</ArcCompareBadge>
134
+ <ArcCompareHeading>Elastic Scale</ArcCompareHeading>
135
+ <ArcCompareContent>
136
+ Horizontal autoscaling responds dynamically to spike traffic demand.
137
+ </ArcCompareContent>
138
+ </ArcCompareCallout>
139
+ <ArcCompareCallout>
140
+ <ArcCompareBadge>03</ArcCompareBadge>
141
+ <ArcCompareHeading>Isolated Faults</ArcCompareHeading>
142
+ <ArcCompareContent>
143
+ Failures remain localized without cascading across service domains.
144
+ </ArcCompareContent>
145
+ </ArcCompareCallout>
146
+ </ArcCompareContents>
147
+ </ArcCompareRight>
148
+ </ArcCompare>
149
+ ```
150
+
151
+ ## Comparison table
152
+
153
+ - Components: `ComparisonTable`, `ComparisonTableCols`, `ComparisonTableCol`, `ComparisonTableRows`, `ComparisonTableRow`, `ComparisonTableCell`
154
+
155
+ ```vue
156
+ <!-- Self-contained mockup default (5 columns x 6 rows) -->
157
+ <ComparisonTable />
158
+
159
+ <!-- Custom compound comparison structure -->
160
+ <ComparisonTable>
161
+ <ComparisonTableCols>
162
+ <ComparisonTableCol color="#ff0000">
163
+ Column 1
164
+ </ComparisonTableCol>
165
+ <ComparisonTableCol color="#00ff00">
166
+ Column 2
167
+ </ComparisonTableCol>
168
+ <ComparisonTableCol color="#0000ff">
169
+ Column 3
170
+ </ComparisonTableCol>
171
+ </ComparisonTableCols>
172
+ <ComparisonTableRows>
173
+ <ComparisonTableRow title="Row 1">
174
+ <ComparisonTableCell>Row 1, Cell 1</ComparisonTableCell>
175
+ <ComparisonTableCell>Row 1, Cell 2</ComparisonTableCell>
176
+ <ComparisonTableCell>Row 1, Cell 3</ComparisonTableCell>
177
+ </ComparisonTableRow>
178
+ <ComparisonTableRow title="Row 2">
179
+ <ComparisonTableCell>Row 2, Cell 1</ComparisonTableCell>
180
+ <ComparisonTableCell>Row 2, Cell 2</ComparisonTableCell>
181
+ <ComparisonTableCell>Row 2, Cell 3</ComparisonTableCell>
182
+ </ComparisonTableRow>
183
+ </ComparisonTableRows>
184
+ </ComparisonTable>
185
+ ```
186
+
34
187
  ## Preview
35
188
 
36
189
  ```bash
37
190
  pnpm --filter @alphacifer/slidev-addon-theme dev
38
191
  ```
39
192
 
40
- The preview deck is defined in `slides.md` and loads this package locally as a
41
- Slidev addon.
193
+ The default preview deck is defined in `slides.md` and loads this package
194
+ locally as a Slidev addon with the Seriph theme.
195
+
196
+ Run the same preview content with the sibling Alpha Academic theme:
197
+
198
+ ```bash
199
+ pnpm --filter @alphacifer/slidev-addon-theme dev:academic
200
+ ```
201
+
202
+ The academic preview deck is defined in `academic-slides.md`.
42
203
 
43
204
  The preview deck includes rendered arc TOC examples for every supported item
44
205
  count from one through seven.
@@ -0,0 +1,361 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ Fragment,
6
+ inject,
7
+ onBeforeUpdate,
8
+ provide,
9
+ ref,
10
+ useSlots,
11
+ type VNode,
12
+ } from 'vue';
13
+
14
+ import {
15
+ ARC_ARROW_PROCESS_ROOT_KEY,
16
+ createArcArrowProcessGeometry,
17
+ getDefaultArcArrowProcessItem,
18
+ type IArcArrowProcessItem,
19
+ type IArcArrowProcessRegistration,
20
+ type IResolvedArcArrowProcessItem,
21
+ resolveArcArrowProcessItems,
22
+ } from '../../utils/arcArrowProcess';
23
+ import { useMergedUnoAttrs } from '../../utils/useMergedUnoAttrs';
24
+ import ArcArrowProcessArrow from './ArcArrowProcessArrow.vue';
25
+ import ArcArrowProcessCallout from './ArcArrowProcessCallout.vue';
26
+
27
+ defineOptions({
28
+ name: 'ArcArrowProcess',
29
+ inheritAttrs: false,
30
+ });
31
+
32
+ export interface IArcArrowProcessProps {
33
+ readonly items?: readonly IArcArrowProcessItem[];
34
+ readonly count?: number;
35
+ readonly animation?: boolean;
36
+ readonly height?: number | string;
37
+ readonly startDelay?: number;
38
+ readonly active?: boolean;
39
+ readonly rotationOffset?: number;
40
+ }
41
+
42
+ const props = withDefaults(defineProps<IArcArrowProcessProps>(), {
43
+ items: undefined,
44
+ count: undefined,
45
+ animation: true,
46
+ height: 370,
47
+ startDelay: undefined,
48
+ active: undefined,
49
+ rotationOffset: undefined,
50
+ });
51
+
52
+ const slots = useSlots();
53
+
54
+ interface INamedComponent {
55
+ readonly name?: string;
56
+ readonly __name?: string;
57
+ }
58
+
59
+ function hasComponentName(type: unknown, name: string): boolean {
60
+ if (typeof type === 'object' && type !== null) {
61
+ const comp = type as INamedComponent;
62
+
63
+ return comp.name === name || comp.__name === name;
64
+ }
65
+
66
+ return false;
67
+ }
68
+
69
+ function flattenVNodes(nodes: VNode[]): VNode[] {
70
+ const result: VNode[] = [];
71
+
72
+ for (const node of nodes) {
73
+ if (node.type === Fragment && Array.isArray(node.children)) {
74
+ result.push(...flattenVNodes(node.children as VNode[]));
75
+ } else {
76
+ result.push(node);
77
+ }
78
+ }
79
+
80
+ return result;
81
+ }
82
+
83
+ function countCallouts(nodes: VNode[]): number {
84
+ let count = 0;
85
+
86
+ for (const node of flattenVNodes(nodes)) {
87
+ if (hasComponentName(node.type, 'ArcArrowProcessCallout')) {
88
+ count++;
89
+ }
90
+ }
91
+
92
+ return count;
93
+ }
94
+
95
+ const slottedCount = computed(() => {
96
+ if (!slots.default) {
97
+ return 0;
98
+ }
99
+
100
+ return countCallouts(slots.default());
101
+ });
102
+
103
+ const shiftingIntro = inject<
104
+ | {
105
+ active: ComputedRef<boolean>;
106
+ isShifting?: ComputedRef<boolean>;
107
+ }
108
+ | undefined
109
+ >('SLIDEV_LAYOUT_SHIFTING_INTRO', undefined);
110
+
111
+ const isAnimated = computed(() => {
112
+ if (!props.animation) {
113
+ return false;
114
+ }
115
+
116
+ if (props.active !== undefined) {
117
+ return props.active;
118
+ }
119
+
120
+ if (shiftingIntro) {
121
+ return shiftingIntro.active.value;
122
+ }
123
+
124
+ return true;
125
+ });
126
+
127
+ const resolvedStartDelay = computed(() => {
128
+ if (props.startDelay !== undefined) {
129
+ return props.startDelay;
130
+ }
131
+
132
+ if (shiftingIntro) {
133
+ return shiftingIntro.isShifting?.value ? 600 : 0;
134
+ }
135
+
136
+ return 0;
137
+ });
138
+
139
+ const resolvedCount = computed<number>(() => {
140
+ if (props.count !== undefined) {
141
+ return props.count;
142
+ }
143
+
144
+ if (slottedCount.value > 0) {
145
+ return slottedCount.value;
146
+ }
147
+
148
+ if (props.items && props.items.length > 0) {
149
+ return props.items.length;
150
+ }
151
+
152
+ return 4;
153
+ });
154
+
155
+ const resolvedItems = computed(() => {
156
+ return resolveArcArrowProcessItems(props.items, resolvedCount.value);
157
+ });
158
+
159
+ const registeredCalloutColors = ref<Record<number, string>>({});
160
+
161
+ const setCalloutColor = (index: number, color: string | undefined) => {
162
+ if (color === undefined) {
163
+ const updated = { ...registeredCalloutColors.value };
164
+ delete updated[index];
165
+ registeredCalloutColors.value = updated;
166
+ } else {
167
+ registeredCalloutColors.value = {
168
+ ...registeredCalloutColors.value,
169
+ [index]: color,
170
+ };
171
+ }
172
+ };
173
+
174
+ const slottedCalloutProps = computed(() => {
175
+ if (!slots.default) {
176
+ return [];
177
+ }
178
+
179
+ const nodes = flattenVNodes(slots.default()).filter((node) =>
180
+ hasComponentName(node.type, 'ArcArrowProcessCallout'),
181
+ );
182
+
183
+ return nodes.map((node, idx) => {
184
+ const rawProps = node.props;
185
+ const explicitIndex =
186
+ rawProps?.index !== undefined && rawProps.index !== null
187
+ ? Number(rawProps.index)
188
+ : idx;
189
+
190
+ return {
191
+ index: explicitIndex,
192
+ color: typeof rawProps?.color === 'string' ? rawProps.color : undefined,
193
+ };
194
+ });
195
+ });
196
+
197
+ const getArrowColor = (idx: number): string => {
198
+ if (registeredCalloutColors.value[idx]) {
199
+ return registeredCalloutColors.value[idx];
200
+ }
201
+
202
+ const slottedMatch = slottedCalloutProps.value.find((p) => p.index === idx);
203
+ if (slottedMatch?.color) {
204
+ return slottedMatch.color;
205
+ }
206
+
207
+ if (props.items?.[idx]?.color) {
208
+ return props.items[idx].color;
209
+ }
210
+
211
+ return getDefaultArcArrowProcessItem(idx, resolvedCount.value).color;
212
+ };
213
+
214
+ const getArrowGradientEnd = (idx: number): string | undefined => {
215
+ if (props.items?.[idx]?.gradientEnd) {
216
+ return props.items[idx].gradientEnd;
217
+ }
218
+
219
+ if (registeredCalloutColors.value[idx]) {
220
+ return registeredCalloutColors.value[idx];
221
+ }
222
+
223
+ const slottedMatch = slottedCalloutProps.value.find((p) => p.index === idx);
224
+ if (slottedMatch?.color) {
225
+ return slottedMatch.color;
226
+ }
227
+
228
+ return getDefaultArcArrowProcessItem(idx, resolvedCount.value).gradientEnd;
229
+ };
230
+
231
+ let calloutCounter = 0;
232
+ onBeforeUpdate(() => {
233
+ calloutCounter = 0;
234
+ });
235
+
236
+ const registerCallout = (): IArcArrowProcessRegistration => {
237
+ const idx = calloutCounter++;
238
+
239
+ return {
240
+ index: idx,
241
+ item: computed<IResolvedArcArrowProcessItem>(() => {
242
+ const item = resolvedItems.value[idx] ?? resolvedItems.value[0];
243
+
244
+ if (item) {
245
+ return item;
246
+ }
247
+
248
+ return getDefaultArcArrowProcessItem(idx, resolvedCount.value);
249
+ }),
250
+ };
251
+ };
252
+
253
+ provide(ARC_ARROW_PROCESS_ROOT_KEY, {
254
+ items: resolvedItems,
255
+ count: resolvedCount,
256
+ animation: isAnimated,
257
+ registerCallout,
258
+ setCalloutColor,
259
+ });
260
+
261
+ const geo = computed(() => {
262
+ return createArcArrowProcessGeometry(
263
+ resolvedCount.value,
264
+ props.rotationOffset,
265
+ );
266
+ });
267
+
268
+ const resolvedHeightStyle = computed(() => {
269
+ if (props.height === undefined) {
270
+ return '100%';
271
+ }
272
+
273
+ return typeof props.height === 'number' ? `${props.height}px` : props.height;
274
+ });
275
+
276
+ const { className, forwardedAttrs } = useMergedUnoAttrs(
277
+ 'alpha-arc-arrow-process w-full flex items-start justify-center select-none font-sans relative',
278
+ );
279
+ </script>
280
+
281
+ <template>
282
+ <div
283
+ v-bind="forwardedAttrs()"
284
+ :class="className()"
285
+ :style="{
286
+ height: resolvedHeightStyle,
287
+ minHeight: '340px',
288
+ fontFamily:
289
+ 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
290
+ '--arc-arrow-start-delay': `${resolvedStartDelay}ms`,
291
+ }"
292
+ >
293
+ <!-- Relative stage container -->
294
+ <div
295
+ class="alpha-arc-arrow-process__stage relative w-full h-full max-w-[1000px] flex items-start justify-center overflow-visible"
296
+ >
297
+ <!-- Central Graphic: Reusing ArcArrowProcessArrow -->
298
+ <div
299
+ class="alpha-arc-arrow-process__center absolute inset-0 w-full h-full pointer-events-none"
300
+ >
301
+ <ArcArrowProcessArrow
302
+ v-for="(arrow, idx) in geo.arrows"
303
+ :key="idx"
304
+ :class="[
305
+ 'alpha-arc-arrow-process__arrow absolute inset-0 w-full h-full',
306
+ `alpha-arc-arrow-process__arrow--${idx}`,
307
+ { 'alpha-arc-arrow-process--animated': isAnimated },
308
+ ]"
309
+ :color="getArrowColor(idx)"
310
+ :gradient-end="getArrowGradientEnd(idx)"
311
+ :origin-y="geo.cy"
312
+ :path="arrow.path"
313
+ :rotation="arrow.rotation"
314
+ :standalone="false"
315
+ />
316
+ </div>
317
+ <!-- Callout Cards -->
318
+ <slot v-if="slots.default" />
319
+ <template v-else>
320
+ <ArcArrowProcessCallout
321
+ v-for="idx in resolvedCount"
322
+ :key="idx - 1"
323
+ :index="idx - 1"
324
+ />
325
+ </template>
326
+ </div>
327
+ </div>
328
+ </template>
329
+
330
+ <style scoped>
331
+ .alpha-arc-arrow-process--animated.alpha-arc-arrow-process__arrow--0 {
332
+ animation: arc-arrow-pop 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arc-arrow-start-delay, 0ms) + 50ms) both;
333
+ transform-origin: 500px -220px;
334
+ }
335
+
336
+ .alpha-arc-arrow-process--animated.alpha-arc-arrow-process__arrow--1 {
337
+ animation: arc-arrow-pop 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arc-arrow-start-delay, 0ms) + 130ms) both;
338
+ transform-origin: 500px -220px;
339
+ }
340
+
341
+ .alpha-arc-arrow-process--animated.alpha-arc-arrow-process__arrow--2 {
342
+ animation: arc-arrow-pop 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arc-arrow-start-delay, 0ms) + 210ms) both;
343
+ transform-origin: 500px -220px;
344
+ }
345
+
346
+ .alpha-arc-arrow-process--animated.alpha-arc-arrow-process__arrow--3 {
347
+ animation: arc-arrow-pop 450ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arc-arrow-start-delay, 0ms) + 290ms) both;
348
+ transform-origin: 500px -220px;
349
+ }
350
+
351
+ @keyframes arc-arrow-pop {
352
+ 0% {
353
+ opacity: 0;
354
+ transform: scale(0.92);
355
+ }
356
+ 100% {
357
+ opacity: 1;
358
+ transform: scale(1);
359
+ }
360
+ }
361
+ </style>
@@ -0,0 +1,143 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ import { DEFAULT_ARC_ARROW_PATH } from '../../utils/arcArrowProcess';
5
+
6
+ defineOptions({
7
+ name: 'ArcArrowProcessArrow',
8
+ });
9
+
10
+ export interface IArcArrowProcessArrowProps {
11
+ readonly color?: string;
12
+ readonly gradientEnd?: string;
13
+ readonly stroke?: string;
14
+ readonly strokeWidth?: number;
15
+ readonly width?: number | string;
16
+ readonly height?: number | string;
17
+ readonly rotation?: number;
18
+ readonly standalone?: boolean;
19
+ readonly path?: string;
20
+ readonly viewBox?: string;
21
+ readonly originY?: number;
22
+ }
23
+
24
+ const props = withDefaults(defineProps<IArcArrowProcessArrowProps>(), {
25
+ color: '#6fa3b5',
26
+ gradientEnd: '#8cb8c8',
27
+ stroke: '#ffffff',
28
+ strokeWidth: 2.5,
29
+ width: undefined,
30
+ height: undefined,
31
+ rotation: 0,
32
+ standalone: undefined,
33
+ path: undefined,
34
+ viewBox: undefined,
35
+ originY: -220,
36
+ });
37
+
38
+ const uid = Math.random().toString(36).slice(2, 8);
39
+ const gradId = computed(() => `arc-arrow-grad-${uid}`);
40
+ const shadowId = computed(() => `arc-arrow-shadow-${uid}`);
41
+
42
+ const isStandalone = computed(() => {
43
+ if (props.standalone !== undefined) {
44
+ return props.standalone;
45
+ }
46
+
47
+ return props.rotation === 0;
48
+ });
49
+
50
+ const resolvedPath = computed(() => {
51
+ return props.path ?? DEFAULT_ARC_ARROW_PATH;
52
+ });
53
+
54
+ const resolvedViewBox = computed(() => {
55
+ if (props.viewBox !== undefined) {
56
+ return props.viewBox;
57
+ }
58
+
59
+ return isStandalone.value ? '-125 335 310 150' : '0 -30 1000 370';
60
+ });
61
+
62
+ const resolvedWidth = computed(() => {
63
+ if (props.width !== undefined) {
64
+ return typeof props.width === 'number' ? `${props.width}px` : props.width;
65
+ }
66
+
67
+ return isStandalone.value ? '380px' : '100%';
68
+ });
69
+
70
+ const resolvedHeight = computed(() => {
71
+ if (props.height !== undefined) {
72
+ return typeof props.height === 'number'
73
+ ? `${props.height}px`
74
+ : props.height;
75
+ }
76
+
77
+ return isStandalone.value ? '215px' : '100%';
78
+ });
79
+ </script>
80
+
81
+ <template>
82
+ <div
83
+ class="alpha-arc-arrow-process-arrow inline-flex items-center justify-center select-none"
84
+ :style="{
85
+ width: resolvedWidth,
86
+ height: resolvedHeight,
87
+ }"
88
+ >
89
+ <svg
90
+ class="w-full h-full overflow-visible"
91
+ :viewBox="resolvedViewBox"
92
+ preserveAspectRatio="xMidYMin meet"
93
+ xmlns="http://www.w3.org/2000/svg"
94
+ >
95
+ <defs>
96
+ <filter
97
+ :id="shadowId"
98
+ x="-30%"
99
+ y="-30%"
100
+ width="160%"
101
+ height="160%"
102
+ >
103
+ <feDropShadow
104
+ dx="0"
105
+ dy="5"
106
+ stdDeviation="5"
107
+ flood-opacity="0.22"
108
+ flood-color="#000000"
109
+ />
110
+ </filter>
111
+ <linearGradient
112
+ :id="gradId"
113
+ x1="0%"
114
+ y1="100%"
115
+ x2="100%"
116
+ y2="0%"
117
+ >
118
+ <stop offset="0%" :stop-color="props.color" />
119
+ <stop offset="100%" :stop-color="props.gradientEnd ?? props.color" />
120
+ </linearGradient>
121
+ </defs>
122
+ <g v-if="!isStandalone" :transform="`translate(500, ${props.originY}) rotate(${props.rotation})`">
123
+ <path
124
+ :d="resolvedPath"
125
+ :fill="`url(#${gradId})`"
126
+ :stroke="props.stroke"
127
+ :stroke-width="props.strokeWidth"
128
+ stroke-linejoin="round"
129
+ :filter="`url(#${shadowId})`"
130
+ />
131
+ </g>
132
+ <path
133
+ v-else
134
+ :d="resolvedPath"
135
+ :fill="`url(#${gradId})`"
136
+ :stroke="props.stroke"
137
+ :stroke-width="props.strokeWidth"
138
+ stroke-linejoin="round"
139
+ :filter="`url(#${shadowId})`"
140
+ />
141
+ </svg>
142
+ </div>
143
+ </template>