@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
@@ -0,0 +1,200 @@
1
+ <script setup lang="ts">
2
+ import { computed, Fragment, inject, provide, useSlots, type VNode } from 'vue';
3
+
4
+ import {
5
+ DEFAULT_HEX_TRIAD_ITEMS,
6
+ HEX_TRIAD_CALLOUT_KEY,
7
+ HEX_TRIAD_ROOT_KEY,
8
+ type IHexTriadItem,
9
+ } from '../../utils/hexTriad';
10
+ import HexTriadBadge from './HexTriadBadge.vue';
11
+ import HexTriadContent from './HexTriadContent.vue';
12
+ import HexTriadHeading from './HexTriadHeading.vue';
13
+
14
+ defineOptions({
15
+ name: 'HexTriadCallout',
16
+ });
17
+
18
+ interface INamedComponent {
19
+ readonly name?: string;
20
+ }
21
+
22
+ function hasComponentName(type: unknown, name: string): boolean {
23
+ if (typeof type === 'object' && type !== null && 'name' in type) {
24
+ return (type as INamedComponent).name === name;
25
+ }
26
+
27
+ return false;
28
+ }
29
+
30
+ const rootContext = inject(HEX_TRIAD_ROOT_KEY, undefined);
31
+
32
+ if (!rootContext) {
33
+ console.warn('[HexTriadCallout] Must be used inside <HexTriad>');
34
+ }
35
+
36
+ const registration = rootContext?.registerCallout();
37
+ const index = registration?.index ?? 0;
38
+
39
+ const fallbackItem = computed<IHexTriadItem>(() => {
40
+ return DEFAULT_HEX_TRIAD_ITEMS[0];
41
+ });
42
+
43
+ const item = registration?.item ?? fallbackItem;
44
+
45
+ const setBadgeText = (text: string | number): void => {
46
+ registration?.setBadgeText(text);
47
+ };
48
+
49
+ provide(HEX_TRIAD_CALLOUT_KEY, {
50
+ item,
51
+ index,
52
+ setBadgeText,
53
+ });
54
+
55
+ const slots = useSlots();
56
+
57
+ function flattenVNodes(nodes: VNode[]): VNode[] {
58
+ const result: VNode[] = [];
59
+
60
+ for (const node of nodes) {
61
+ if (node.type === Fragment && Array.isArray(node.children)) {
62
+ result.push(...flattenVNodes(node.children as VNode[]));
63
+ } else {
64
+ result.push(node);
65
+ }
66
+ }
67
+
68
+ return result;
69
+ }
70
+
71
+ function isBadgeNode(node: VNode): boolean {
72
+ return (
73
+ hasComponentName(node.type, 'HexTriadBadge') || node.type === HexTriadBadge
74
+ );
75
+ }
76
+
77
+ function isHeadingNode(node: VNode): boolean {
78
+ return (
79
+ hasComponentName(node.type, 'HexTriadHeading') ||
80
+ node.type === HexTriadHeading
81
+ );
82
+ }
83
+
84
+ function isContentNode(node: VNode): boolean {
85
+ return (
86
+ hasComponentName(node.type, 'HexTriadContent') ||
87
+ node.type === HexTriadContent
88
+ );
89
+ }
90
+
91
+ const defaultNodes = computed(() => {
92
+ if (!slots.default) {
93
+ return [];
94
+ }
95
+
96
+ return flattenVNodes(slots.default());
97
+ });
98
+
99
+ const badgeNode = computed(() => {
100
+ return defaultNodes.value.find(isBadgeNode);
101
+ });
102
+
103
+ const headingNode = computed(() => {
104
+ return defaultNodes.value.find(isHeadingNode);
105
+ });
106
+
107
+ const contentNode = computed(() => {
108
+ return defaultNodes.value.find(isContentNode);
109
+ });
110
+
111
+ const otherNodes = computed(() => {
112
+ return defaultNodes.value.filter((node) => {
113
+ return !isBadgeNode(node) && !isHeadingNode(node) && !isContentNode(node);
114
+ });
115
+ });
116
+
117
+ // Positions matching the design exactly:
118
+ // Index 0: upper-right pocket above orange hex
119
+ // Index 1: lower-right, right of orange hex
120
+ // Index 2: lower-left, left of blue hex
121
+ const positionStyle = computed(() => {
122
+ if (index === 0) {
123
+ return 'left: calc(50% + 90px); top: 20px;';
124
+ }
125
+
126
+ if (index === 1) {
127
+ return 'left: calc(50% + 185px); top: 210px;';
128
+ }
129
+
130
+ return 'right: calc(50% + 185px); top: 210px;';
131
+ });
132
+
133
+ const animation = computed(() => rootContext?.animation?.value ?? true);
134
+
135
+ const containerClass = computed(() => {
136
+ if (index === 2) {
137
+ return `alpha-hex-triad-callout alpha-hex-triad-callout--${index} absolute flex items-start gap-3 justify-end text-right z-10 pointer-events-auto`;
138
+ }
139
+
140
+ return `alpha-hex-triad-callout alpha-hex-triad-callout--${index} absolute flex items-start gap-3 justify-start text-left z-10 pointer-events-auto`;
141
+ });
142
+ </script>
143
+
144
+ <template>
145
+ <div
146
+ v-if="index < 3"
147
+ :class="[containerClass, { 'alpha-hex-triad-callout--animated': animation }]"
148
+ :style="positionStyle"
149
+ >
150
+ <!-- For Index 0 and 1: Badge on left, Text on right -->
151
+ <template v-if="index !== 2">
152
+ <component :is="badgeNode" v-if="badgeNode" />
153
+ <HexTriadBadge v-else />
154
+ <div class="flex flex-col items-start text-left">
155
+ <component :is="headingNode" v-if="headingNode" />
156
+ <HexTriadHeading v-else />
157
+ <component :is="contentNode" v-if="contentNode" />
158
+ <HexTriadContent v-else />
159
+ <component :is="node" v-for="(node, i) in otherNodes" :key="i" />
160
+ </div>
161
+ </template>
162
+ <!-- For Index 2 (Blue / Left): Text on left, Badge on right -->
163
+ <template v-else>
164
+ <div class="flex flex-col items-end text-right">
165
+ <component :is="headingNode" v-if="headingNode" />
166
+ <HexTriadHeading v-else />
167
+ <component :is="contentNode" v-if="contentNode" />
168
+ <HexTriadContent v-else />
169
+ <component :is="node" v-for="(node, i) in otherNodes" :key="i" />
170
+ </div>
171
+ <component :is="badgeNode" v-if="badgeNode" />
172
+ <HexTriadBadge v-else />
173
+ </template>
174
+ </div>
175
+ </template>
176
+
177
+ <style scoped>
178
+ .alpha-hex-triad-callout--animated.alpha-hex-triad-callout--0 {
179
+ animation: hex-callout-display 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--hex-start-delay, 0ms) + 250ms) both;
180
+ }
181
+
182
+ .alpha-hex-triad-callout--animated.alpha-hex-triad-callout--1 {
183
+ animation: hex-callout-display 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--hex-start-delay, 0ms) + 450ms) both;
184
+ }
185
+
186
+ .alpha-hex-triad-callout--animated.alpha-hex-triad-callout--2 {
187
+ animation: hex-callout-display 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--hex-start-delay, 0ms) + 650ms) both;
188
+ }
189
+
190
+ @keyframes hex-callout-display {
191
+ 0% {
192
+ opacity: 0;
193
+ transform: translateY(12px);
194
+ }
195
+ 100% {
196
+ opacity: 1;
197
+ transform: translateY(0);
198
+ }
199
+ }
200
+ </style>
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ defineOptions({
5
+ name: 'HexTriadCenterBadge',
6
+ });
7
+
8
+ export interface IHexTriadCenterBadgeProps {
9
+ readonly x: number;
10
+ readonly y: number;
11
+ readonly text: string | number;
12
+ readonly color: string;
13
+ readonly filter?: string;
14
+ readonly radius: number;
15
+ }
16
+
17
+ const props = withDefaults(defineProps<IHexTriadCenterBadgeProps>(), {
18
+ filter: 'url(#hex-badge-shadow)',
19
+ });
20
+
21
+ const transform = computed(() => {
22
+ return `translate(${props.x}, ${props.y})`;
23
+ });
24
+ </script>
25
+
26
+ <template>
27
+ <g class="alpha-hex-triad-center-badge" :transform="transform" :filter="filter">
28
+ <circle class="alpha-hex-triad-center-badge__outer" cx="0" cy="0" :r="radius + 4" fill="#ffffff" />
29
+ <circle class="alpha-hex-triad-center-badge__circle" cx="0" cy="0" :r="radius" :fill="color" />
30
+ <circle
31
+ class="alpha-hex-triad-center-badge__ring"
32
+ cx="0"
33
+ cy="0"
34
+ :r="radius - 4"
35
+ fill="none"
36
+ stroke="#ffffff"
37
+ stroke-width="1.2"
38
+ :style="{ opacity: 0.65 }"
39
+ />
40
+ <text
41
+ class="alpha-hex-triad-center-badge__text font-black text-lg select-none"
42
+ x="0"
43
+ y="6.5"
44
+ text-anchor="middle"
45
+ fill="#ffffff"
46
+ font-family="system-ui, -apple-system, sans-serif"
47
+ >
48
+ {{ text }}
49
+ </text>
50
+ </g>
51
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ name: 'HexTriadContent',
4
+ });
5
+ </script>
6
+
7
+ <template>
8
+ <p
9
+ class="alpha-hex-triad-content mt-0 mb-0 text-[1rem] leading-relaxed max-w-[240px]"
10
+ :style="{
11
+ margin: 0,
12
+ fontSize: '1rem',
13
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
14
+ }"
15
+ >
16
+ <slot>
17
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod sed diam nonummy nibh
18
+ </slot>
19
+ </p>
20
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject } from 'vue';
3
+
4
+ import { HEX_TRIAD_CALLOUT_KEY } from '../../utils/hexTriad';
5
+
6
+ defineOptions({
7
+ name: 'HexTriadHeading',
8
+ });
9
+
10
+ const calloutContext = inject(HEX_TRIAD_CALLOUT_KEY, undefined);
11
+ const headingColor = computed(() => {
12
+ return calloutContext?.item.value.color ?? '#e11d48';
13
+ });
14
+ </script>
15
+
16
+ <template>
17
+ <h3
18
+ class="alpha-hex-triad-heading font-bold text-[1.25rem] tracking-tight mb-[0.5rem]"
19
+ :style="{
20
+ color: headingColor,
21
+ fontSize: '1.25rem',
22
+ fontFamily:
23
+ 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
24
+ }"
25
+ >
26
+ <slot>Text Here</slot>
27
+ </h3>
28
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ name: 'HexTriadLayer',
4
+ });
5
+
6
+ export interface IHexTriadLayerProps {
7
+ readonly d: string;
8
+ readonly fill?: string;
9
+ readonly filter?: string;
10
+ readonly opacity?: number | string;
11
+ }
12
+
13
+ withDefaults(defineProps<IHexTriadLayerProps>(), {
14
+ opacity: 1,
15
+ fill: undefined,
16
+ filter: undefined,
17
+ });
18
+ </script>
19
+
20
+ <template>
21
+ <path
22
+ class="alpha-hex-triad-layer"
23
+ :d="d"
24
+ :fill="fill"
25
+ :filter="filter"
26
+ :style="opacity !== undefined ? { opacity } : undefined"
27
+ />
28
+ </template>
@@ -0,0 +1,318 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ Fragment,
6
+ provide,
7
+ useSlots,
8
+ type VNode,
9
+ } from 'vue';
10
+
11
+ import {
12
+ createHorizCardAccentPath,
13
+ createHorizCardPath,
14
+ HORIZ_CARD_KEY,
15
+ hexToRgba,
16
+ } from '../../utils/horizCard';
17
+ import { useMergedUnoAttrs } from '../../utils/useMergedUnoAttrs';
18
+
19
+ defineOptions({
20
+ name: 'HorizCard',
21
+ inheritAttrs: false,
22
+ });
23
+
24
+ export interface IHorizCardProps {
25
+ readonly color?: string;
26
+ readonly dots?: boolean;
27
+ readonly title?: string;
28
+ readonly cardBg?: string;
29
+ readonly textColor?: string;
30
+ readonly titleColor?: string;
31
+ readonly lightColor?: string;
32
+ readonly description?: string;
33
+ readonly step?: string | number;
34
+ readonly icon?: string | object;
35
+ readonly hasIconCircle?: boolean;
36
+ }
37
+
38
+ const props = withDefaults(defineProps<IHorizCardProps>(), {
39
+ dots: true,
40
+ step: '01',
41
+ icon: undefined,
42
+ color: '#ea583a',
43
+ cardBg: '#ffffff',
44
+ title: 'Lorem Ipsum',
45
+ textColor: undefined,
46
+ lightColor: undefined,
47
+ titleColor: undefined,
48
+ description: undefined,
49
+ hasIconCircle: undefined,
50
+ });
51
+
52
+ const slots = useSlots();
53
+
54
+ interface INamedComponent {
55
+ readonly name?: string;
56
+ }
57
+
58
+ function hasComponentName(type: unknown, name: string): boolean {
59
+ if (typeof type === 'object' && type !== null && 'name' in type) {
60
+ return (type as INamedComponent).name === name;
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ function flattenVNodes(nodes: VNode[]): VNode[] {
67
+ const result: VNode[] = [];
68
+
69
+ for (const node of nodes) {
70
+ if (node.type === Fragment && Array.isArray(node.children)) {
71
+ result.push(...flattenVNodes(node.children as VNode[]));
72
+ } else {
73
+ result.push(node);
74
+ }
75
+ }
76
+
77
+ return result;
78
+ }
79
+
80
+ function isBadgeNode(node: VNode): boolean {
81
+ return hasComponentName(node.type, 'HorizCardBadge');
82
+ }
83
+
84
+ function isIconNode(node: VNode): boolean {
85
+ return hasComponentName(node.type, 'HorizCardIcon');
86
+ }
87
+
88
+ function isHeadingNode(node: VNode): boolean {
89
+ return hasComponentName(node.type, 'HorizCardHeading');
90
+ }
91
+
92
+ function isContentNode(node: VNode): boolean {
93
+ return hasComponentName(node.type, 'HorizCardContent');
94
+ }
95
+
96
+ const defaultNodes = computed(() => {
97
+ if (!slots.default) {
98
+ return [];
99
+ }
100
+
101
+ return flattenVNodes(slots.default());
102
+ });
103
+
104
+ const slottedBadge = computed(() => {
105
+ return defaultNodes.value.find(isBadgeNode);
106
+ });
107
+
108
+ const slottedIcon = computed(() => {
109
+ return defaultNodes.value.find(isIconNode);
110
+ });
111
+
112
+ const slottedHeading = computed(() => {
113
+ return defaultNodes.value.find(isHeadingNode);
114
+ });
115
+
116
+ const slottedContent = computed(() => {
117
+ return defaultNodes.value.find(isContentNode);
118
+ });
119
+
120
+ const otherSlottedNodes = computed(() => {
121
+ return defaultNodes.value.filter((node) => {
122
+ return (
123
+ !isBadgeNode(node) &&
124
+ !isIconNode(node) &&
125
+ !isHeadingNode(node) &&
126
+ !isContentNode(node)
127
+ );
128
+ });
129
+ });
130
+
131
+ const resolvedColor = computed(() => {
132
+ return props.color;
133
+ });
134
+
135
+ const resolvedLightColor = computed(() => {
136
+ return (
137
+ props.lightColor ??
138
+ hexToRgba({
139
+ alpha: 0.16,
140
+ hex: props.color,
141
+ })
142
+ );
143
+ });
144
+
145
+ const resolvedCardBg = computed(() => {
146
+ return props.cardBg;
147
+ });
148
+
149
+ const resolvedTitleColor = computed(() => {
150
+ return props.titleColor ?? props.color;
151
+ });
152
+
153
+ const resolvedTextColor = computed(() => {
154
+ return props.textColor;
155
+ });
156
+
157
+ const resolvedStep = computed(() => {
158
+ return props.step;
159
+ });
160
+
161
+ provide(HORIZ_CARD_KEY, {
162
+ step: resolvedStep,
163
+ color: resolvedColor,
164
+ cardBg: resolvedCardBg,
165
+ lightColor: resolvedLightColor,
166
+ titleColor: resolvedTitleColor,
167
+ textColor: resolvedTextColor as ComputedRef<string | undefined>,
168
+ });
169
+
170
+ // Fixed coordinate grid for crisp SVG vector drawing
171
+ const CARD_WIDTH = 360;
172
+ const CARD_HEIGHT = 260;
173
+
174
+ const cardPath = computed(() => {
175
+ return createHorizCardPath({
176
+ chamferX: 60,
177
+ chamferY: 76,
178
+ borderRadius: 24,
179
+ chamferFillet: 22,
180
+ width: CARD_WIDTH,
181
+ height: CARD_HEIGHT,
182
+ });
183
+ });
184
+
185
+ const accentPath = computed(() => {
186
+ return createHorizCardAccentPath({
187
+ chamferX: 60,
188
+ chamferY: 76,
189
+ chamferFillet: 22,
190
+ width: CARD_WIDTH,
191
+ height: CARD_HEIGHT,
192
+ accentThickness: 16,
193
+ accentTopFillet: 22,
194
+ accentBottomFillet: 22,
195
+ });
196
+ });
197
+
198
+ // Determine whether icon circle is active
199
+ const isIconActive = computed(() => {
200
+ if (props.hasIconCircle !== undefined) {
201
+ return props.hasIconCircle;
202
+ }
203
+
204
+ return Boolean(props.icon || slots.icon || slottedIcon.value !== undefined);
205
+ });
206
+
207
+ const { className, forwardedAttrs } = useMergedUnoAttrs(
208
+ 'alpha-horiz-card relative inline-block select-none w-[360px] h-[260px] font-sans',
209
+ );
210
+ </script>
211
+
212
+ <template>
213
+ <div v-bind="forwardedAttrs()" :class="className()">
214
+ <!-- SVG Vector Layer: Bottom-left accent blob + filleted chamfered card outline -->
215
+ <svg
216
+ class="absolute -left-[28px] -top-[16px] w-[416px] h-[292px] pointer-events-none overflow-visible"
217
+ viewBox="-28 -16 416 292"
218
+ xmlns="http://www.w3.org/2000/svg"
219
+ >
220
+ <!-- Layer 1: Solid accent shape behind the card & chamfer -->
221
+ <path
222
+ class="alpha-horiz-card__accent"
223
+ :d="accentPath"
224
+ :fill="resolvedColor"
225
+ />
226
+ <!-- Layer 2: Main card body with stroke border and filleted chamfered cut -->
227
+ <path
228
+ class="alpha-horiz-card__body"
229
+ :d="cardPath"
230
+ :fill="resolvedCardBg"
231
+ :stroke="resolvedColor"
232
+ stroke-width="2.75"
233
+ stroke-linejoin="round"
234
+ />
235
+ </svg>
236
+ <!-- Ribbon Step Badge at top left (hangs over the top border) -->
237
+ <div class="absolute left-[20px] -top-[20px] z-10">
238
+ <slot name="badge">
239
+ <component :is="slottedBadge" v-if="slottedBadge" />
240
+ <HorizCardBadge v-else :step="resolvedStep" :color="resolvedColor" />
241
+ </slot>
242
+ </div>
243
+ <!-- Card Content Layer -->
244
+ <div
245
+ class="relative z-10 w-full h-full pt-10 pb-6 px-6 flex flex-col justify-between"
246
+ >
247
+ <!-- Middle Row: Optional Icon Circle + Text Content -->
248
+ <div
249
+ class="flex items-center gap-4 flex-1"
250
+ :class="{ 'pl-1': isIconActive }"
251
+ >
252
+ <!-- Icon Circle (rendered when icon/slot is provided) -->
253
+ <template v-if="isIconActive">
254
+ <slot name="icon">
255
+ <component :is="slottedIcon" v-if="slottedIcon" />
256
+ <HorizCardIcon
257
+ v-else
258
+ :icon="props.icon"
259
+ :color="resolvedColor"
260
+ :light-color="resolvedLightColor"
261
+ :size="100"
262
+ />
263
+ </slot>
264
+ </template>
265
+ <!-- Heading + Body Description -->
266
+ <div class="flex-1 flex flex-col justify-center min-w-0 pr-1">
267
+ <slot name="title">
268
+ <component :is="slottedHeading" v-if="slottedHeading" />
269
+ <HorizCardHeading
270
+ v-else
271
+ :color="resolvedTitleColor"
272
+ class="mb-2 text-lg"
273
+ >
274
+ {{ props.title }}
275
+ </HorizCardHeading>
276
+ </slot>
277
+ <slot name="description">
278
+ <component :is="slottedContent" v-if="slottedContent" />
279
+ <HorizCardContent
280
+ v-else
281
+ :color="resolvedTextColor"
282
+ class="text-[11px] leading-[1.45]"
283
+ >
284
+ <template v-if="props.description">
285
+ {{ props.description }}
286
+ </template>
287
+ </HorizCardContent>
288
+ </slot>
289
+ <!-- Any other custom slotted components -->
290
+ <component
291
+ :is="node"
292
+ v-for="(node, index) in otherSlottedNodes"
293
+ :key="index"
294
+ />
295
+ </div>
296
+ </div>
297
+ <!-- Bottom Row: Decorative Indicator Dots -->
298
+ <div v-if="props.dots" class="flex justify-end items-center pr-2 pb-1">
299
+ <slot name="dots">
300
+ <div class="inline-flex items-center gap-1.5 opacity-90">
301
+ <span
302
+ class="w-2 h-2 rounded-full inline-block"
303
+ :style="{ backgroundColor: resolvedColor }"
304
+ />
305
+ <span
306
+ class="w-2 h-2 rounded-full inline-block"
307
+ :style="{ backgroundColor: resolvedColor }"
308
+ />
309
+ <span
310
+ class="w-2 h-2 rounded-full inline-block"
311
+ :style="{ backgroundColor: resolvedColor }"
312
+ />
313
+ </div>
314
+ </slot>
315
+ </div>
316
+ </div>
317
+ </div>
318
+ </template>