@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.
- package/README.md +163 -2
- package/components/arc-arrow-process/ArcArrowProcess.vue +361 -0
- package/components/arc-arrow-process/ArcArrowProcessArrow.vue +143 -0
- package/components/arc-arrow-process/ArcArrowProcessCallout.vue +361 -0
- package/components/arc-arrow-process/ArcArrowProcessContent.vue +38 -0
- package/components/arc-arrow-process/ArcArrowProcessHeading.vue +43 -0
- package/components/arc-arrow-process/ArcArrowProcessIcon.vue +154 -0
- package/components/arc-compare/ArcCompare.vue +443 -0
- package/components/arc-compare/ArcCompareBadge.vue +63 -0
- package/components/arc-compare/ArcCompareCallout.vue +386 -0
- package/components/arc-compare/ArcCompareContent.vue +52 -0
- package/components/arc-compare/ArcCompareContents.vue +18 -0
- package/components/arc-compare/ArcCompareHeading.vue +52 -0
- package/components/arc-compare/ArcCompareLeft.vue +183 -0
- package/components/arc-compare/ArcCompareRight.vue +183 -0
- package/components/arc-compare/ArcCompareTitle.vue +47 -0
- package/components/arrow-triad/ArrowTriad.vue +368 -0
- package/components/arrow-triad/ArrowTriadCallout.vue +300 -0
- package/components/arrow-triad/ArrowTriadContent.vue +38 -0
- package/components/arrow-triad/ArrowTriadHeading.vue +38 -0
- package/components/arrow-triad/ArrowTriadIcon.vue +96 -0
- package/components/chevron-card/ChevronCard.vue +237 -0
- package/components/chevron-card/ChevronCardBadge.vue +74 -0
- package/components/chevron-card/ChevronCardContent.vue +39 -0
- package/components/chevron-card/ChevronCardHeading.vue +39 -0
- package/components/comparison-table/ComparisonTable.vue +244 -0
- package/components/comparison-table/ComparisonTableCell.vue +88 -0
- package/components/comparison-table/ComparisonTableCol.vue +97 -0
- package/components/comparison-table/ComparisonTableCols.vue +74 -0
- package/components/comparison-table/ComparisonTableRow.vue +92 -0
- package/components/comparison-table/ComparisonTableRows.vue +21 -0
- package/components/hex-triad/HexTriad.vue +438 -0
- package/components/hex-triad/HexTriadBadge.vue +110 -0
- package/components/hex-triad/HexTriadCallout.vue +200 -0
- package/components/hex-triad/HexTriadCenterBadge.vue +51 -0
- package/components/hex-triad/HexTriadContent.vue +20 -0
- package/components/hex-triad/HexTriadHeading.vue +28 -0
- package/components/hex-triad/HexTriadLayer.vue +28 -0
- package/components/horiz-card/HorizCard.vue +318 -0
- package/components/horiz-card/HorizCardBadge.vue +75 -0
- package/components/horiz-card/HorizCardContent.vue +46 -0
- package/components/horiz-card/HorizCardHeading.vue +47 -0
- package/components/horiz-card/HorizCardIcon.vue +118 -0
- package/components/quad-hub/QuadHub.vue +642 -0
- package/components/quad-hub/QuadHubCallout.vue +267 -0
- package/components/quad-hub/QuadHubCenter.vue +53 -0
- package/components/quad-hub/QuadHubContent.vue +39 -0
- package/components/quad-hub/QuadHubHeading.vue +44 -0
- package/components/quad-hub/QuadHubIcon.vue +112 -0
- package/components/shifting-heading/TransitionHeading.vue +2 -2
- package/components/vert-card/VertCard.vue +389 -0
- package/components/vert-card/VertCardBadge.vue +72 -0
- package/components/vert-card/VertCardContent.vue +45 -0
- package/components/vert-card/VertCardHeading.vue +68 -0
- package/components/vert-card/VertCardIcon.vue +126 -0
- package/components/vert-card/VertCardTitle.vue +76 -0
- package/layouts/core/arc-toc.vue +76 -113
- package/layouts/core/bg-center.vue +32 -4
- package/layouts/shifting-heading/shifting-intro.vue +35 -7
- package/package.json +3 -2
- package/utils/arcArrowProcess/context.ts +28 -0
- package/utils/arcArrowProcess/createArcArrowProcessGeometry.ts +137 -0
- package/utils/arcArrowProcess/index.ts +3 -0
- package/utils/arcArrowProcess/resolveArcArrowProcessItems.ts +189 -0
- package/utils/arcCompare/context.ts +36 -0
- package/utils/arcCompare/createArcCompareGeometry.ts +254 -0
- package/utils/arcCompare/index.ts +3 -0
- package/utils/arcCompare/resolveArcCompareItems.ts +123 -0
- package/utils/arrowTriad/context.ts +38 -0
- package/utils/arrowTriad/createArrowTriadGeometry.ts +63 -0
- package/utils/arrowTriad/index.ts +3 -0
- package/utils/arrowTriad/resolveArrowTriadItems.ts +121 -0
- package/utils/chevronCard/context.ts +12 -0
- package/utils/chevronCard/createChevronCardGeometry.ts +48 -0
- package/utils/chevronCard/index.ts +2 -0
- package/utils/comparisonTable/constants.ts +72 -0
- package/utils/comparisonTable/context.ts +24 -0
- package/utils/comparisonTable/index.ts +2 -0
- package/utils/hexTriad/context.ts +25 -0
- package/utils/hexTriad/createHexTriadGeometry.ts +182 -0
- package/utils/hexTriad/index.ts +3 -0
- package/utils/hexTriad/resolveHexTriadItems.ts +63 -0
- package/utils/horizCard/context.ts +13 -0
- package/utils/horizCard/createHorizCardGeometry.ts +243 -0
- package/utils/horizCard/index.ts +2 -0
- package/utils/quadHub/context.ts +26 -0
- package/utils/quadHub/createQuadHubGeometry.ts +331 -0
- package/utils/quadHub/index.ts +3 -0
- package/utils/quadHub/resolveQuadHubItems.ts +214 -0
- package/utils/useDiagramAutoScale.ts +300 -0
- package/utils/vertCard/context.ts +18 -0
- package/utils/vertCard/createVertCardGeometry.ts +242 -0
- package/utils/vertCard/index.ts +2 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, Fragment, inject, provide, useSlots, type VNode } from 'vue';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ARROW_TRIAD_CALLOUT_KEY,
|
|
6
|
+
ARROW_TRIAD_ROOT_KEY,
|
|
7
|
+
getDefaultArrowTriadItem,
|
|
8
|
+
type IArrowTriadItem,
|
|
9
|
+
type IResolvedArrowTriadItem,
|
|
10
|
+
resolveSingleArrowTriadItem,
|
|
11
|
+
} from '../../utils/arrowTriad';
|
|
12
|
+
import ArrowTriadContent from './ArrowTriadContent.vue';
|
|
13
|
+
import ArrowTriadHeading from './ArrowTriadHeading.vue';
|
|
14
|
+
import ArrowTriadIcon from './ArrowTriadIcon.vue';
|
|
15
|
+
|
|
16
|
+
defineOptions({
|
|
17
|
+
name: 'ArrowTriadCallout',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export interface IArrowTriadCalloutProps {
|
|
21
|
+
readonly id?: string | number;
|
|
22
|
+
readonly index?: number;
|
|
23
|
+
readonly icon?: string | object | false;
|
|
24
|
+
readonly color?: string;
|
|
25
|
+
readonly cardBg?: string;
|
|
26
|
+
readonly textColor?: string;
|
|
27
|
+
readonly titleColor?: string;
|
|
28
|
+
readonly title?: string;
|
|
29
|
+
readonly description?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const props = withDefaults(defineProps<IArrowTriadCalloutProps>(), {
|
|
33
|
+
id: undefined,
|
|
34
|
+
index: undefined,
|
|
35
|
+
icon: undefined,
|
|
36
|
+
color: undefined,
|
|
37
|
+
cardBg: undefined,
|
|
38
|
+
textColor: undefined,
|
|
39
|
+
titleColor: undefined,
|
|
40
|
+
title: undefined,
|
|
41
|
+
description: undefined,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
interface INamedComponent {
|
|
45
|
+
readonly name?: string;
|
|
46
|
+
readonly __name?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hasComponentName(type: unknown, name: string): boolean {
|
|
50
|
+
if (typeof type === 'object' && type !== null) {
|
|
51
|
+
const comp = type as INamedComponent;
|
|
52
|
+
|
|
53
|
+
return comp.name === name || comp.__name === name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rootContext = inject(ARROW_TRIAD_ROOT_KEY, undefined);
|
|
60
|
+
|
|
61
|
+
if (!rootContext) {
|
|
62
|
+
console.warn('[ArrowTriadCallout] Must be used inside <ArrowTriad>');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const registration =
|
|
66
|
+
props.index === undefined ? rootContext?.registerCallout() : undefined;
|
|
67
|
+
const resolvedIndex = computed(() => {
|
|
68
|
+
if (props.index !== undefined) {
|
|
69
|
+
return props.index;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return registration?.index ?? 0;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const activeItem = computed<IResolvedArrowTriadItem>(() => {
|
|
76
|
+
const baseItem =
|
|
77
|
+
rootContext?.items.value[resolvedIndex.value] ??
|
|
78
|
+
getDefaultArrowTriadItem(resolvedIndex.value);
|
|
79
|
+
|
|
80
|
+
const override: IArrowTriadItem = {
|
|
81
|
+
id: props.id,
|
|
82
|
+
color: props.color,
|
|
83
|
+
icon: props.icon,
|
|
84
|
+
cardBg: props.cardBg,
|
|
85
|
+
textColor: props.textColor,
|
|
86
|
+
titleColor: props.titleColor,
|
|
87
|
+
title: props.title,
|
|
88
|
+
description: props.description,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return resolveSingleArrowTriadItem(
|
|
92
|
+
{
|
|
93
|
+
...baseItem,
|
|
94
|
+
...Object.fromEntries(
|
|
95
|
+
Object.entries(override).filter(([_, v]) => {
|
|
96
|
+
return v !== undefined;
|
|
97
|
+
}),
|
|
98
|
+
),
|
|
99
|
+
},
|
|
100
|
+
resolvedIndex.value,
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
provide(ARROW_TRIAD_CALLOUT_KEY, {
|
|
105
|
+
item: activeItem,
|
|
106
|
+
index: resolvedIndex.value,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const slots = useSlots();
|
|
110
|
+
|
|
111
|
+
function flattenVNodes(nodes: VNode[]): VNode[] {
|
|
112
|
+
const result: VNode[] = [];
|
|
113
|
+
|
|
114
|
+
for (const node of nodes) {
|
|
115
|
+
if (node.type === Fragment && Array.isArray(node.children)) {
|
|
116
|
+
result.push(...flattenVNodes(node.children as VNode[]));
|
|
117
|
+
} else {
|
|
118
|
+
result.push(node);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const defaultNodes = computed(() => {
|
|
126
|
+
if (!slots.default) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return flattenVNodes(slots.default());
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const headingNode = computed(() => {
|
|
134
|
+
return defaultNodes.value.find((node) => {
|
|
135
|
+
return hasComponentName(node.type, 'ArrowTriadHeading');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const contentNode = computed(() => {
|
|
140
|
+
return defaultNodes.value.find((node) => {
|
|
141
|
+
return hasComponentName(node.type, 'ArrowTriadContent');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function isValidSlottedNode(node: VNode): boolean {
|
|
146
|
+
if (typeof node.type === 'symbol') {
|
|
147
|
+
return typeof node.children === 'string' && node.children.trim().length > 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const otherSlottedNodes = computed(() => {
|
|
154
|
+
return defaultNodes.value.filter((node) => {
|
|
155
|
+
return (
|
|
156
|
+
isValidSlottedNode(node) &&
|
|
157
|
+
!hasComponentName(node.type, 'ArrowTriadHeading') &&
|
|
158
|
+
!hasComponentName(node.type, 'ArrowTriadContent')
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const positionStyle = computed(() => {
|
|
164
|
+
const is2Left =
|
|
165
|
+
rootContext?.layout?.value === '2-left-1-right' ||
|
|
166
|
+
rootContext?.layout?.value === '2-left';
|
|
167
|
+
|
|
168
|
+
if (is2Left) {
|
|
169
|
+
switch (resolvedIndex.value) {
|
|
170
|
+
case 0: {
|
|
171
|
+
// Top-Left (aligned with Yellow arrow tip pointing North-West)
|
|
172
|
+
return 'right: calc(50% + 150px); top: calc(50% - 170px);';
|
|
173
|
+
}
|
|
174
|
+
case 1: {
|
|
175
|
+
// Bottom-Left (aligned with Green arrow tip pointing South-West)
|
|
176
|
+
return 'right: calc(50% + 150px); top: calc(50% + 15px);';
|
|
177
|
+
}
|
|
178
|
+
case 2: {
|
|
179
|
+
// Middle-Right (aligned with Blue arrow tip pointing East)
|
|
180
|
+
return 'left: calc(50% + 166px); top: calc(50% - 78px);';
|
|
181
|
+
}
|
|
182
|
+
default: {
|
|
183
|
+
return '';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 1-left-2-right
|
|
189
|
+
switch (resolvedIndex.value) {
|
|
190
|
+
case 0: {
|
|
191
|
+
// Top-Right (aligned with Yellow arrow tip pointing North-East)
|
|
192
|
+
return 'left: calc(50% + 150px); top: calc(50% - 170px);';
|
|
193
|
+
}
|
|
194
|
+
case 1: {
|
|
195
|
+
// Bottom-Right (aligned with Green arrow tip pointing South-East)
|
|
196
|
+
return 'left: calc(50% + 150px); top: calc(50% + 15px);';
|
|
197
|
+
}
|
|
198
|
+
case 2: {
|
|
199
|
+
// Middle-Left (aligned with Blue arrow tip pointing West)
|
|
200
|
+
return 'right: calc(50% + 166px); top: calc(50% - 78px);';
|
|
201
|
+
}
|
|
202
|
+
default: {
|
|
203
|
+
return '';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const animation = computed(() => {
|
|
209
|
+
return rootContext?.animation?.value ?? true;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const hasIcon = computed(() => {
|
|
213
|
+
if (slots.icon) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const iconVal = props.icon !== undefined ? props.icon : activeItem.value.icon;
|
|
218
|
+
|
|
219
|
+
if (!iconVal || iconVal === 'false') {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (typeof iconVal === 'string' && iconVal.trim() === '') {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return true;
|
|
228
|
+
});
|
|
229
|
+
</script>
|
|
230
|
+
|
|
231
|
+
<template>
|
|
232
|
+
<div
|
|
233
|
+
v-if="resolvedIndex < 3"
|
|
234
|
+
:class="[
|
|
235
|
+
'alpha-arrow-triad-callout absolute w-[280px] h-[155px] rounded-[20px] px-4 py-3 flex flex-col items-center justify-center text-center z-10 transition-all duration-300 pointer-events-auto select-none box-border shadow-[0_10px_30px_-5px_rgba(0,0,0,0.06),0_4px_6px_-4px_rgba(0,0,0,0.03)]',
|
|
236
|
+
`alpha-arrow-triad-callout--${resolvedIndex}`,
|
|
237
|
+
{ 'alpha-arrow-triad-callout--animated': animation },
|
|
238
|
+
]"
|
|
239
|
+
:style="[
|
|
240
|
+
positionStyle,
|
|
241
|
+
{
|
|
242
|
+
backgroundColor: activeItem.cardBg,
|
|
243
|
+
fontFamily:
|
|
244
|
+
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
|
|
245
|
+
},
|
|
246
|
+
]"
|
|
247
|
+
>
|
|
248
|
+
<!-- Card Icon Header -->
|
|
249
|
+
<div v-if="hasIcon" class="mb-2 flex items-center justify-center">
|
|
250
|
+
<slot name="icon">
|
|
251
|
+
<ArrowTriadIcon :icon="activeItem.icon" :size="32" />
|
|
252
|
+
</slot>
|
|
253
|
+
</div>
|
|
254
|
+
<!-- Title -->
|
|
255
|
+
<slot name="title">
|
|
256
|
+
<component :is="headingNode" v-if="headingNode" />
|
|
257
|
+
<ArrowTriadHeading v-else :color="activeItem.titleColor">
|
|
258
|
+
{{ activeItem.title }}
|
|
259
|
+
</ArrowTriadHeading>
|
|
260
|
+
</slot>
|
|
261
|
+
<!-- Description -->
|
|
262
|
+
<slot name="description">
|
|
263
|
+
<component :is="contentNode" v-if="contentNode" />
|
|
264
|
+
<ArrowTriadContent v-else :color="activeItem.textColor">
|
|
265
|
+
{{ activeItem.description }}
|
|
266
|
+
</ArrowTriadContent>
|
|
267
|
+
</slot>
|
|
268
|
+
<!-- Other slotted nodes -->
|
|
269
|
+
<component
|
|
270
|
+
:is="node"
|
|
271
|
+
v-for="(node, idx) in otherSlottedNodes"
|
|
272
|
+
:key="idx"
|
|
273
|
+
/>
|
|
274
|
+
</div>
|
|
275
|
+
</template>
|
|
276
|
+
|
|
277
|
+
<style scoped>
|
|
278
|
+
.alpha-arrow-triad-callout--animated.alpha-arrow-triad-callout--0 {
|
|
279
|
+
animation: arrow-triad-callout-fade 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arrow-triad-start-delay, 0ms) + 150ms) both;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.alpha-arrow-triad-callout--animated.alpha-arrow-triad-callout--1 {
|
|
283
|
+
animation: arrow-triad-callout-fade 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arrow-triad-start-delay, 0ms) + 250ms) both;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.alpha-arrow-triad-callout--animated.alpha-arrow-triad-callout--2 {
|
|
287
|
+
animation: arrow-triad-callout-fade 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--arrow-triad-start-delay, 0ms) + 350ms) both;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@keyframes arrow-triad-callout-fade {
|
|
291
|
+
0% {
|
|
292
|
+
opacity: 0;
|
|
293
|
+
transform: translateY(12px) scale(0.96);
|
|
294
|
+
}
|
|
295
|
+
100% {
|
|
296
|
+
opacity: 1;
|
|
297
|
+
transform: translateY(0) scale(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from 'vue';
|
|
3
|
+
|
|
4
|
+
import { ARROW_TRIAD_CALLOUT_KEY } from '../../utils/arrowTriad';
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
name: 'ArrowTriadContent',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export interface IArrowTriadContentProps {
|
|
11
|
+
readonly color?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<IArrowTriadContentProps>(), {
|
|
15
|
+
color: undefined,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const calloutContext = inject(ARROW_TRIAD_CALLOUT_KEY, undefined);
|
|
19
|
+
|
|
20
|
+
const contentColor = computed(() => {
|
|
21
|
+
return props.color ?? calloutContext?.item.value.textColor ?? '#64748b';
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div
|
|
27
|
+
class="alpha-arrow-triad-content text-[1rem] leading-[1.5] text-center font-normal select-none"
|
|
28
|
+
:style="{
|
|
29
|
+
color: contentColor,
|
|
30
|
+
fontFamily:
|
|
31
|
+
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
|
|
32
|
+
}"
|
|
33
|
+
>
|
|
34
|
+
<slot>
|
|
35
|
+
{{ calloutContext?.item.value.description }}
|
|
36
|
+
</slot>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from 'vue';
|
|
3
|
+
|
|
4
|
+
import { ARROW_TRIAD_CALLOUT_KEY } from '../../utils/arrowTriad';
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
name: 'ArrowTriadHeading',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export interface IArrowTriadHeadingProps {
|
|
11
|
+
readonly color?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<IArrowTriadHeadingProps>(), {
|
|
15
|
+
color: undefined,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const calloutContext = inject(ARROW_TRIAD_CALLOUT_KEY, undefined);
|
|
19
|
+
|
|
20
|
+
const headingColor = computed(() => {
|
|
21
|
+
return props.color ?? calloutContext?.item.value.titleColor ?? '#1e293b';
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div
|
|
27
|
+
class="alpha-arrow-triad-heading font-bold text-[1.25rem] uppercase tracking-wider text-center leading-tight mb-2 select-none"
|
|
28
|
+
:style="{
|
|
29
|
+
color: headingColor,
|
|
30
|
+
fontFamily:
|
|
31
|
+
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
|
|
32
|
+
}"
|
|
33
|
+
>
|
|
34
|
+
<slot>
|
|
35
|
+
{{ calloutContext?.item.value.title }}
|
|
36
|
+
</slot>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
name: 'ArrowTriadIcon',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export interface IArrowTriadIconProps {
|
|
9
|
+
readonly size?: number;
|
|
10
|
+
readonly color?: string;
|
|
11
|
+
readonly icon?: string | object | false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<IArrowTriadIconProps>(), {
|
|
15
|
+
size: 32,
|
|
16
|
+
icon: undefined,
|
|
17
|
+
color: '#334155',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const isStringIcon = computed((): boolean => {
|
|
21
|
+
return typeof props.icon === 'string';
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const isComponentIcon = computed((): boolean => {
|
|
25
|
+
return typeof props.icon === 'object' && props.icon !== null;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const hasIcon = computed((): boolean => {
|
|
29
|
+
return Boolean(props.icon) && props.icon !== 'false' && props.icon !== '';
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const resolvedIconClass = computed((): string => {
|
|
33
|
+
if (typeof props.icon !== 'string') {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const str = props.icon.trim();
|
|
38
|
+
|
|
39
|
+
if (str.startsWith('i-')) {
|
|
40
|
+
return str;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (str.includes(':')) {
|
|
44
|
+
return `i-${str.replace(':', '-')}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (str.includes('-')) {
|
|
48
|
+
return `i-${str}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return str;
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<template>
|
|
56
|
+
<div
|
|
57
|
+
v-if="hasIcon"
|
|
58
|
+
class="alpha-arrow-triad-icon flex items-center justify-center pointer-events-none select-none"
|
|
59
|
+
:style="{
|
|
60
|
+
color: props.color,
|
|
61
|
+
width: `${props.size}px`,
|
|
62
|
+
height: `${props.size}px`,
|
|
63
|
+
}"
|
|
64
|
+
>
|
|
65
|
+
<slot>
|
|
66
|
+
<component
|
|
67
|
+
:is="props.icon"
|
|
68
|
+
v-if="isComponentIcon"
|
|
69
|
+
class="w-full h-full"
|
|
70
|
+
:style="{ color: props.color }"
|
|
71
|
+
/>
|
|
72
|
+
<!-- UnoCSS / Iconify class name (e.g. i-carbon-shield-check or carbon:shield-check) -->
|
|
73
|
+
<span
|
|
74
|
+
v-else-if="isStringIcon && resolvedIconClass"
|
|
75
|
+
:class="resolvedIconClass"
|
|
76
|
+
class="inline-block"
|
|
77
|
+
:style="{
|
|
78
|
+
color: props.color,
|
|
79
|
+
width: `${props.size}px`,
|
|
80
|
+
height: `${props.size}px`,
|
|
81
|
+
fontSize: `${props.size}px`,
|
|
82
|
+
}"
|
|
83
|
+
/>
|
|
84
|
+
<span
|
|
85
|
+
v-else-if="isStringIcon"
|
|
86
|
+
class="inline-block font-semibold"
|
|
87
|
+
:style="{
|
|
88
|
+
color: props.color,
|
|
89
|
+
fontSize: `${props.size * 0.75}px`,
|
|
90
|
+
}"
|
|
91
|
+
>
|
|
92
|
+
{{ props.icon }}
|
|
93
|
+
</span>
|
|
94
|
+
</slot>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, Fragment, provide, useSlots, type VNode } from 'vue';
|
|
3
|
+
|
|
4
|
+
import { CHEVRON_CARD_KEY } from '../../utils/chevronCard';
|
|
5
|
+
import { useMergedUnoAttrs } from '../../utils/useMergedUnoAttrs';
|
|
6
|
+
import ChevronCardBadge from './ChevronCardBadge.vue';
|
|
7
|
+
import ChevronCardContent from './ChevronCardContent.vue';
|
|
8
|
+
import ChevronCardHeading from './ChevronCardHeading.vue';
|
|
9
|
+
|
|
10
|
+
defineOptions({
|
|
11
|
+
name: 'ChevronCard',
|
|
12
|
+
inheritAttrs: false,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export interface IChevronCardProps {
|
|
16
|
+
readonly step?: string | number;
|
|
17
|
+
readonly icon?: string | object;
|
|
18
|
+
readonly color?: string;
|
|
19
|
+
readonly width?: number | string;
|
|
20
|
+
readonly cardBg?: string;
|
|
21
|
+
readonly height?: number | string;
|
|
22
|
+
readonly textColor?: string;
|
|
23
|
+
readonly animation?: boolean;
|
|
24
|
+
readonly notchDepth?: number;
|
|
25
|
+
readonly titleColor?: string;
|
|
26
|
+
readonly chevronWidth?: number;
|
|
27
|
+
readonly borderRadius?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<IChevronCardProps>(), {
|
|
31
|
+
step: '01',
|
|
32
|
+
icon: undefined,
|
|
33
|
+
color: '#f59e0b',
|
|
34
|
+
width: undefined,
|
|
35
|
+
cardBg: '#f5f5f7',
|
|
36
|
+
height: undefined,
|
|
37
|
+
textColor: undefined,
|
|
38
|
+
animation: true,
|
|
39
|
+
notchDepth: 26,
|
|
40
|
+
titleColor: undefined,
|
|
41
|
+
chevronWidth: 130,
|
|
42
|
+
borderRadius: 18,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const slots = useSlots();
|
|
46
|
+
|
|
47
|
+
interface INamedComponent {
|
|
48
|
+
readonly name?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasComponentName(type: unknown, name: string): boolean {
|
|
52
|
+
if (typeof type === 'object' && type !== null && 'name' in type) {
|
|
53
|
+
return (type as INamedComponent).name === name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function flattenVNodes(nodes: VNode[]): VNode[] {
|
|
60
|
+
const result: VNode[] = [];
|
|
61
|
+
|
|
62
|
+
for (const node of nodes) {
|
|
63
|
+
if (node.type === Fragment && Array.isArray(node.children)) {
|
|
64
|
+
result.push(...flattenVNodes(node.children as VNode[]));
|
|
65
|
+
} else {
|
|
66
|
+
result.push(node);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isBadgeNode(node: VNode): boolean {
|
|
74
|
+
return (
|
|
75
|
+
hasComponentName(node.type, 'ChevronCardBadge') ||
|
|
76
|
+
node.type === ChevronCardBadge
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isHeadingNode(node: VNode): boolean {
|
|
81
|
+
return (
|
|
82
|
+
hasComponentName(node.type, 'ChevronCardHeading') ||
|
|
83
|
+
node.type === ChevronCardHeading
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isContentNode(node: VNode): boolean {
|
|
88
|
+
return (
|
|
89
|
+
hasComponentName(node.type, 'ChevronCardContent') ||
|
|
90
|
+
node.type === ChevronCardContent
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const defaultNodes = computed(() => {
|
|
95
|
+
if (!slots.default) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return flattenVNodes(slots.default());
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const slottedBadge = computed(() => {
|
|
103
|
+
return defaultNodes.value.find(isBadgeNode);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const slottedHeading = computed(() => {
|
|
107
|
+
return defaultNodes.value.find(isHeadingNode);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const slottedContent = computed(() => {
|
|
111
|
+
return defaultNodes.value.find(isContentNode);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const otherSlottedNodes = computed(() => {
|
|
115
|
+
return defaultNodes.value.filter((node) => {
|
|
116
|
+
return !isBadgeNode(node) && !isHeadingNode(node) && !isContentNode(node);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const resolvedStep = computed(() => {
|
|
121
|
+
return props.step;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const resolvedColor = computed(() => {
|
|
125
|
+
return props.color;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const resolvedCardBg = computed(() => {
|
|
129
|
+
return props.cardBg;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const resolvedTextColor = computed(() => {
|
|
133
|
+
return props.textColor;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const resolvedTitleColor = computed(() => {
|
|
137
|
+
return props.titleColor;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
provide(CHEVRON_CARD_KEY, {
|
|
141
|
+
step: resolvedStep,
|
|
142
|
+
color: resolvedColor,
|
|
143
|
+
cardBg: resolvedCardBg,
|
|
144
|
+
textColor: resolvedTextColor,
|
|
145
|
+
titleColor: resolvedTitleColor,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const resolvedWidth = computed(() => {
|
|
149
|
+
if (props.width === undefined) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return typeof props.width === 'number' ? `${props.width}px` : props.width;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const resolvedHeight = computed(() => {
|
|
157
|
+
if (props.height === undefined) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return typeof props.height === 'number' ? `${props.height}px` : props.height;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const { className, forwardedAttrs } = useMergedUnoAttrs(
|
|
165
|
+
'alpha-chevron-card flex items-stretch w-full select-none font-sans',
|
|
166
|
+
);
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<div
|
|
171
|
+
v-bind="forwardedAttrs()"
|
|
172
|
+
:class="[
|
|
173
|
+
className(),
|
|
174
|
+
{ 'alpha-chevron-card--animated': props.animation },
|
|
175
|
+
]"
|
|
176
|
+
:style="{
|
|
177
|
+
width: resolvedWidth,
|
|
178
|
+
minHeight: resolvedHeight ?? '112px',
|
|
179
|
+
fontFamily:
|
|
180
|
+
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
|
|
181
|
+
}"
|
|
182
|
+
>
|
|
183
|
+
<!-- Chevron Step Badge Layer -->
|
|
184
|
+
<div class="flex-shrink-0 z-10 flex">
|
|
185
|
+
<slot name="badge">
|
|
186
|
+
<component :is="slottedBadge" v-if="slottedBadge" />
|
|
187
|
+
<ChevronCardBadge
|
|
188
|
+
v-else
|
|
189
|
+
:step="resolvedStep"
|
|
190
|
+
:icon="props.icon"
|
|
191
|
+
:color="resolvedColor"
|
|
192
|
+
:width="props.chevronWidth"
|
|
193
|
+
:notch-depth="props.notchDepth"
|
|
194
|
+
/>
|
|
195
|
+
</slot>
|
|
196
|
+
</div>
|
|
197
|
+
<!-- Content Body Layer -->
|
|
198
|
+
<div
|
|
199
|
+
class="flex-1 min-w-0 flex flex-col justify-center py-4 pr-6"
|
|
200
|
+
:style="{
|
|
201
|
+
backgroundColor: resolvedCardBg,
|
|
202
|
+
borderRadius: `0 ${props.borderRadius}px ${props.borderRadius}px 0`,
|
|
203
|
+
marginLeft: `-${props.notchDepth}px`,
|
|
204
|
+
paddingLeft: `${props.notchDepth + 20}px`,
|
|
205
|
+
}"
|
|
206
|
+
>
|
|
207
|
+
<slot name="title">
|
|
208
|
+
<component :is="slottedHeading" v-if="slottedHeading" />
|
|
209
|
+
</slot>
|
|
210
|
+
<slot name="description">
|
|
211
|
+
<component :is="slottedContent" v-if="slottedContent" />
|
|
212
|
+
</slot>
|
|
213
|
+
<component
|
|
214
|
+
:is="node"
|
|
215
|
+
v-for="(node, index) in otherSlottedNodes"
|
|
216
|
+
:key="index"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</template>
|
|
221
|
+
|
|
222
|
+
<style scoped>
|
|
223
|
+
.alpha-chevron-card--animated {
|
|
224
|
+
animation: chevron-card-fade-in 400ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@keyframes chevron-card-fade-in {
|
|
228
|
+
0% {
|
|
229
|
+
opacity: 0;
|
|
230
|
+
transform: translateX(-16px);
|
|
231
|
+
}
|
|
232
|
+
100% {
|
|
233
|
+
opacity: 1;
|
|
234
|
+
transform: translateX(0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
</style>
|