@alphacifer/slidev-addon-theme 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +127 -2
  2. package/components/arc-arrow-process/ArcArrowProcess.vue +361 -0
  3. package/components/arc-arrow-process/ArcArrowProcessArrow.vue +143 -0
  4. package/components/arc-arrow-process/ArcArrowProcessCallout.vue +361 -0
  5. package/components/arc-arrow-process/ArcArrowProcessContent.vue +38 -0
  6. package/components/arc-arrow-process/ArcArrowProcessHeading.vue +43 -0
  7. package/components/arc-arrow-process/ArcArrowProcessIcon.vue +154 -0
  8. package/components/arc-compare/ArcCompare.vue +443 -0
  9. package/components/arc-compare/ArcCompareBadge.vue +63 -0
  10. package/components/arc-compare/ArcCompareCallout.vue +386 -0
  11. package/components/arc-compare/ArcCompareContent.vue +52 -0
  12. package/components/arc-compare/ArcCompareContents.vue +18 -0
  13. package/components/arc-compare/ArcCompareHeading.vue +52 -0
  14. package/components/arc-compare/ArcCompareLeft.vue +183 -0
  15. package/components/arc-compare/ArcCompareRight.vue +183 -0
  16. package/components/arc-compare/ArcCompareTitle.vue +47 -0
  17. package/components/arrow-triad/ArrowTriad.vue +368 -0
  18. package/components/arrow-triad/ArrowTriadCallout.vue +300 -0
  19. package/components/arrow-triad/ArrowTriadContent.vue +38 -0
  20. package/components/arrow-triad/ArrowTriadHeading.vue +38 -0
  21. package/components/arrow-triad/ArrowTriadIcon.vue +96 -0
  22. package/components/chevron-card/ChevronCard.vue +237 -0
  23. package/components/chevron-card/ChevronCardBadge.vue +74 -0
  24. package/components/chevron-card/ChevronCardContent.vue +39 -0
  25. package/components/chevron-card/ChevronCardHeading.vue +39 -0
  26. package/components/hex-triad/HexTriad.vue +438 -0
  27. package/components/hex-triad/HexTriadBadge.vue +110 -0
  28. package/components/hex-triad/HexTriadCallout.vue +200 -0
  29. package/components/hex-triad/HexTriadCenterBadge.vue +51 -0
  30. package/components/hex-triad/HexTriadContent.vue +20 -0
  31. package/components/hex-triad/HexTriadHeading.vue +28 -0
  32. package/components/hex-triad/HexTriadLayer.vue +28 -0
  33. package/components/horiz-card/HorizCard.vue +318 -0
  34. package/components/horiz-card/HorizCardBadge.vue +75 -0
  35. package/components/horiz-card/HorizCardContent.vue +46 -0
  36. package/components/horiz-card/HorizCardHeading.vue +47 -0
  37. package/components/horiz-card/HorizCardIcon.vue +118 -0
  38. package/components/quad-hub/QuadHub.vue +642 -0
  39. package/components/quad-hub/QuadHubCallout.vue +267 -0
  40. package/components/quad-hub/QuadHubCenter.vue +53 -0
  41. package/components/quad-hub/QuadHubContent.vue +39 -0
  42. package/components/quad-hub/QuadHubHeading.vue +44 -0
  43. package/components/quad-hub/QuadHubIcon.vue +112 -0
  44. package/components/shifting-heading/TransitionHeading.vue +3 -3
  45. package/components/vert-card/VertCard.vue +389 -0
  46. package/components/vert-card/VertCardBadge.vue +72 -0
  47. package/components/vert-card/VertCardContent.vue +45 -0
  48. package/components/vert-card/VertCardHeading.vue +68 -0
  49. package/components/vert-card/VertCardIcon.vue +126 -0
  50. package/components/vert-card/VertCardTitle.vue +76 -0
  51. package/layouts/core/arc-toc.vue +76 -113
  52. package/layouts/core/bg-center.vue +32 -4
  53. package/layouts/shifting-heading/shifting-intro.vue +35 -7
  54. package/package.json +3 -2
  55. package/utils/arcArrowProcess/context.ts +28 -0
  56. package/utils/arcArrowProcess/createArcArrowProcessGeometry.ts +137 -0
  57. package/utils/arcArrowProcess/index.ts +3 -0
  58. package/utils/arcArrowProcess/resolveArcArrowProcessItems.ts +189 -0
  59. package/utils/arcCompare/context.ts +36 -0
  60. package/utils/arcCompare/createArcCompareGeometry.ts +254 -0
  61. package/utils/arcCompare/index.ts +3 -0
  62. package/utils/arcCompare/resolveArcCompareItems.ts +123 -0
  63. package/utils/arrowTriad/context.ts +38 -0
  64. package/utils/arrowTriad/createArrowTriadGeometry.ts +63 -0
  65. package/utils/arrowTriad/index.ts +3 -0
  66. package/utils/arrowTriad/resolveArrowTriadItems.ts +121 -0
  67. package/utils/chevronCard/context.ts +12 -0
  68. package/utils/chevronCard/createChevronCardGeometry.ts +48 -0
  69. package/utils/chevronCard/index.ts +2 -0
  70. package/utils/hexTriad/context.ts +25 -0
  71. package/utils/hexTriad/createHexTriadGeometry.ts +182 -0
  72. package/utils/hexTriad/index.ts +3 -0
  73. package/utils/hexTriad/resolveHexTriadItems.ts +63 -0
  74. package/utils/horizCard/context.ts +13 -0
  75. package/utils/horizCard/createHorizCardGeometry.ts +243 -0
  76. package/utils/horizCard/index.ts +2 -0
  77. package/utils/quadHub/context.ts +26 -0
  78. package/utils/quadHub/createQuadHubGeometry.ts +331 -0
  79. package/utils/quadHub/index.ts +3 -0
  80. package/utils/quadHub/resolveQuadHubItems.ts +214 -0
  81. package/utils/useDiagramAutoScale.ts +300 -0
  82. package/utils/vertCard/context.ts +18 -0
  83. package/utils/vertCard/createVertCardGeometry.ts +242 -0
  84. package/utils/vertCard/index.ts +2 -0
@@ -0,0 +1,137 @@
1
+ export interface IPoint {
2
+ readonly x: number;
3
+ readonly y: number;
4
+ }
5
+
6
+ export interface IArcArrowProcessArrowPathData {
7
+ readonly id: string;
8
+ readonly path: string;
9
+ readonly rotation: number;
10
+ readonly tip: IPoint;
11
+ readonly notch: IPoint;
12
+ readonly center: IPoint;
13
+ readonly gradientStart: IPoint;
14
+ readonly gradientEnd: IPoint;
15
+ }
16
+
17
+ export interface IArcArrowProcessGeometry {
18
+ readonly viewBoxWidth: number;
19
+ readonly viewBoxHeight: number;
20
+ readonly viewBoxMinY: number;
21
+ readonly cx: number;
22
+ readonly cy: number;
23
+ readonly arrows: readonly IArcArrowProcessArrowPathData[];
24
+ readonly count: number;
25
+ }
26
+
27
+ export const DEFAULT_ARC_ARROW_PATH = [
28
+ 'M -64.99 416.33',
29
+ 'L -92.91 387',
30
+ 'A 398 398 0 0 0 92.91 387',
31
+ 'L 82.88 370.9',
32
+ 'L 152.5 395.32',
33
+ 'L 101.55 448.69',
34
+ 'L 103.18 429.79',
35
+ 'A 442 442 0 0 1 -103.18 429.79',
36
+ 'Z',
37
+ ].join(' ');
38
+
39
+ export const LONGER_MIDDLE_ARC_ARROW_PATH = [
40
+ 'M -86.69 412.36',
41
+ 'L -113.04 381.61',
42
+ 'A 398 398 0 0 0 113.04 381.61',
43
+ 'L 102.17 366.06',
44
+ 'L 172.98 386.8',
45
+ 'L 124.89 442.76',
46
+ 'L 125.53 423.8',
47
+ 'A 442 442 0 0 1 -125.53 423.8',
48
+ 'Z',
49
+ ].join(' ');
50
+
51
+ export function createArcArrowProcessGeometry(
52
+ count = 4,
53
+ rotationOffset?: number,
54
+ ): IArcArrowProcessGeometry {
55
+ const viewBoxWidth = 1000;
56
+ const viewBoxHeight = 370;
57
+ const viewBoxMinY = -30;
58
+ const cx = 500;
59
+ const cy = -220;
60
+ const step = 39;
61
+ const defaultOffset = count === 3 ? 5 : 0;
62
+ const offset = rotationOffset ?? defaultOffset;
63
+
64
+ const arrows: IArcArrowProcessArrowPathData[] = [];
65
+
66
+ for (let i = 0; i < count; i++) {
67
+ let rotation: number;
68
+ let path = DEFAULT_ARC_ARROW_PATH;
69
+ let tipOffset = 0;
70
+ let notchOffset = 0;
71
+
72
+ if (count === 3) {
73
+ const actualStep = 42; // step (39) + extra span (3) for equal visual width
74
+
75
+ if (i === 0) {
76
+ rotation = Number((actualStep + offset).toFixed(2));
77
+ } else if (i === 1) {
78
+ rotation = Number(offset.toFixed(2));
79
+ path = LONGER_MIDDLE_ARC_ARROW_PATH;
80
+ tipOffset = -3;
81
+ notchOffset = 3;
82
+ } else {
83
+ rotation = Number((-actualStep + offset).toFixed(2));
84
+ }
85
+ } else {
86
+ rotation = Number(
87
+ ((count - 1) * (step / 2) - i * step + offset).toFixed(2),
88
+ );
89
+ }
90
+
91
+ const thetaTip = ((68.91 + tipOffset + rotation) * Math.PI) / 180;
92
+ const thetaNotch = ((98.87 + notchOffset + rotation) * Math.PI) / 180;
93
+ const thetaCenter = ((83.89 + rotation) * Math.PI) / 180;
94
+
95
+ const tip: IPoint = {
96
+ x: Number((cx + 424 * Math.cos(thetaTip)).toFixed(2)),
97
+ y: Number((cy + 424 * Math.sin(thetaTip)).toFixed(2)),
98
+ };
99
+
100
+ const notch: IPoint = {
101
+ x: Number((cx + 421 * Math.cos(thetaNotch)).toFixed(2)),
102
+ y: Number((cy + 421 * Math.sin(thetaNotch)).toFixed(2)),
103
+ };
104
+
105
+ const center: IPoint = {
106
+ x: Number((cx + 420 * Math.cos(thetaCenter)).toFixed(2)),
107
+ y: Number((cy + 420 * Math.sin(thetaCenter)).toFixed(2)),
108
+ };
109
+
110
+ arrows.push({
111
+ id: `arrow-${count}-${i}`,
112
+ path,
113
+ rotation,
114
+ tip,
115
+ notch,
116
+ center,
117
+ gradientStart: {
118
+ x: notch.x,
119
+ y: notch.y,
120
+ },
121
+ gradientEnd: {
122
+ x: tip.x,
123
+ y: tip.y,
124
+ },
125
+ });
126
+ }
127
+
128
+ return {
129
+ viewBoxWidth,
130
+ viewBoxHeight,
131
+ viewBoxMinY,
132
+ cx,
133
+ cy,
134
+ arrows,
135
+ count,
136
+ };
137
+ }
@@ -0,0 +1,3 @@
1
+ export * from './context';
2
+ export * from './createArcArrowProcessGeometry';
3
+ export * from './resolveArcArrowProcessItems';
@@ -0,0 +1,189 @@
1
+ export interface IArcArrowProcessItem {
2
+ readonly id?: string | number;
3
+ readonly color?: string;
4
+ readonly gradientEnd?: string;
5
+ readonly title?: string;
6
+ readonly description?: string;
7
+ readonly icon?: string | object | false;
8
+ readonly titleColor?: string;
9
+ readonly textColor?: string;
10
+ }
11
+
12
+ export interface IResolvedArcArrowProcessItem {
13
+ readonly id: string | number;
14
+ readonly color: string;
15
+ readonly gradientEnd: string;
16
+ readonly title: string;
17
+ readonly description: string;
18
+ readonly icon?: string | object | false;
19
+ readonly titleColor: string;
20
+ readonly textColor: string;
21
+ }
22
+
23
+ export const DEFAULT_ARC_ARROW_PROCESS_ITEMS_4: readonly [
24
+ IResolvedArcArrowProcessItem,
25
+ IResolvedArcArrowProcessItem,
26
+ IResolvedArcArrowProcessItem,
27
+ IResolvedArcArrowProcessItem,
28
+ ] = [
29
+ {
30
+ id: '01',
31
+ color: '#a0a5aa',
32
+ gradientEnd: '#8e959f',
33
+ title: 'Heading Here',
34
+ description:
35
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut.',
36
+ titleColor: '#888d94',
37
+ textColor: '#8e959f',
38
+ },
39
+ {
40
+ id: '02',
41
+ color: '#2c485d',
42
+ gradientEnd: '#334e68',
43
+ title: 'Heading Here',
44
+ description:
45
+ 'Sed do eiusmod tempor incididunt ut labore et lorem ipsum dolor sit amet, consectetur adipiscing elit.',
46
+ titleColor: '#2c485d',
47
+ textColor: '#8e959f',
48
+ },
49
+ {
50
+ id: '03',
51
+ color: '#6fa3b5',
52
+ gradientEnd: '#7fb0c0',
53
+ title: 'Heading Here',
54
+ description:
55
+ 'Tempor incididunt ut laborelorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod et.',
56
+ titleColor: '#6fa3b5',
57
+ textColor: '#8e959f',
58
+ },
59
+ {
60
+ id: '04',
61
+ color: '#d6284d',
62
+ gradientEnd: '#e63964',
63
+ title: 'Heading Here',
64
+ description:
65
+ 'Eiusmod tempor incididunt ut labore lorem ipsum dolor sit amet, consectetur adipiscing elit, sed.',
66
+ titleColor: '#d6284d',
67
+ textColor: '#8e959f',
68
+ },
69
+ ];
70
+
71
+ export const DEFAULT_ARC_ARROW_PROCESS_ITEMS_3: readonly [
72
+ IResolvedArcArrowProcessItem,
73
+ IResolvedArcArrowProcessItem,
74
+ IResolvedArcArrowProcessItem,
75
+ ] = [
76
+ {
77
+ id: '01',
78
+ color: '#a0a5aa',
79
+ gradientEnd: '#8e959f',
80
+ title: 'Heading Here',
81
+ description:
82
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.',
83
+ titleColor: '#888d94',
84
+ textColor: '#8e959f',
85
+ },
86
+ {
87
+ id: '02',
88
+ color: '#2c485d',
89
+ gradientEnd: '#334e68',
90
+ title: 'Heading Here',
91
+ description:
92
+ 'Sed do eiusmod tempor incididunt ut labore et lorem ipsum dolor sit amet, consectetur adipiscing elit.',
93
+ titleColor: '#2c485d',
94
+ textColor: '#8e959f',
95
+ },
96
+ {
97
+ id: '03',
98
+ color: '#d6284d',
99
+ gradientEnd: '#e63964',
100
+ title: 'Heading Here',
101
+ description:
102
+ 'Eiusmod tempor incididunt ut labore lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.',
103
+ titleColor: '#d6284d',
104
+ textColor: '#8e959f',
105
+ },
106
+ ];
107
+
108
+ export const DEFAULT_ARC_ARROW_PROCESS_ITEMS_2: readonly [
109
+ IResolvedArcArrowProcessItem,
110
+ IResolvedArcArrowProcessItem,
111
+ ] = [
112
+ {
113
+ id: '01',
114
+ color: '#2c485d',
115
+ gradientEnd: '#334e68',
116
+ title: 'Heading Here',
117
+ description:
118
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.',
119
+ titleColor: '#2c485d',
120
+ textColor: '#8e959f',
121
+ },
122
+ {
123
+ id: '02',
124
+ color: '#6fa3b5',
125
+ gradientEnd: '#7fb0c0',
126
+ title: 'Heading Here',
127
+ description:
128
+ 'Sed do eiusmod tempor incididunt ut labore et lorem ipsum dolor sit amet, consectetur adipiscing elit.',
129
+ titleColor: '#6fa3b5',
130
+ textColor: '#8e959f',
131
+ },
132
+ ];
133
+
134
+ export function getDefaultArcArrowProcessItem(
135
+ index: number,
136
+ count: number = 4,
137
+ ): IResolvedArcArrowProcessItem {
138
+ const list =
139
+ count === 2
140
+ ? DEFAULT_ARC_ARROW_PROCESS_ITEMS_2
141
+ : count === 3
142
+ ? DEFAULT_ARC_ARROW_PROCESS_ITEMS_3
143
+ : DEFAULT_ARC_ARROW_PROCESS_ITEMS_4;
144
+ const safe = Math.abs(index) % list.length;
145
+ const item = list[safe];
146
+
147
+ if (item) {
148
+ return item;
149
+ }
150
+
151
+ return DEFAULT_ARC_ARROW_PROCESS_ITEMS_4[0];
152
+ }
153
+
154
+ export function resolveSingleArcArrowProcessItem(
155
+ item: IArcArrowProcessItem | undefined,
156
+ fallbackIndex = 0,
157
+ count: number = 4,
158
+ ): IResolvedArcArrowProcessItem {
159
+ const fallback = getDefaultArcArrowProcessItem(fallbackIndex, count);
160
+
161
+ if (!item) {
162
+ return fallback;
163
+ }
164
+
165
+ return {
166
+ id: item.id ?? fallback.id,
167
+ color: item.color ?? fallback.color,
168
+ gradientEnd: item.gradientEnd ?? fallback.gradientEnd,
169
+ title: item.title ?? fallback.title,
170
+ description: item.description ?? fallback.description,
171
+ icon: item.icon !== undefined ? item.icon : fallback.icon,
172
+ titleColor: item.titleColor ?? item.color ?? fallback.titleColor,
173
+ textColor: item.textColor ?? fallback.textColor,
174
+ };
175
+ }
176
+
177
+ export function resolveArcArrowProcessItems(
178
+ items: readonly IArcArrowProcessItem[] | undefined,
179
+ count: number = 4,
180
+ ): readonly IResolvedArcArrowProcessItem[] {
181
+ const targetCount = count >= 2 ? count : 4;
182
+ const result: IResolvedArcArrowProcessItem[] = [];
183
+
184
+ for (let i = 0; i < targetCount; i++) {
185
+ result.push(resolveSingleArcArrowProcessItem(items?.[i], i, targetCount));
186
+ }
187
+
188
+ return result;
189
+ }
@@ -0,0 +1,36 @@
1
+ import type { ComputedRef, InjectionKey } from 'vue';
2
+
3
+ import type { IArcCompareGeometry } from './createArcCompareGeometry';
4
+ import type { IResolvedArcCompareItem } from './resolveArcCompareItems';
5
+
6
+ export interface IArcCompareRootContext {
7
+ readonly animation: ComputedRef<boolean>;
8
+ readonly startDelay: ComputedRef<number>;
9
+ readonly pointsCount: ComputedRef<number>;
10
+ readonly geo: ComputedRef<IArcCompareGeometry>;
11
+ }
12
+
13
+ export interface IArcCompareSideContext {
14
+ readonly side: 'left' | 'right';
15
+ readonly color: ComputedRef<string>;
16
+ readonly title: ComputedRef<string>;
17
+ readonly setTitle: (title: string) => void;
18
+ readonly registerCallout: () => number;
19
+ readonly count: ComputedRef<number>;
20
+ }
21
+
22
+ export interface IArcCompareCalloutContext {
23
+ readonly side: 'left' | 'right';
24
+ readonly index: number;
25
+ readonly color: ComputedRef<string>;
26
+ readonly item: ComputedRef<IResolvedArcCompareItem>;
27
+ }
28
+
29
+ export const ARC_COMPARE_ROOT_KEY: InjectionKey<IArcCompareRootContext> =
30
+ Symbol('ARC_COMPARE_ROOT_KEY');
31
+
32
+ export const ARC_COMPARE_SIDE_KEY: InjectionKey<IArcCompareSideContext> =
33
+ Symbol('ARC_COMPARE_SIDE_KEY');
34
+
35
+ export const ARC_COMPARE_CALLOUT_KEY: InjectionKey<IArcCompareCalloutContext> =
36
+ Symbol('ARC_COMPARE_CALLOUT_KEY');
@@ -0,0 +1,254 @@
1
+ export interface IArcComparePoint {
2
+ readonly x: number;
3
+ readonly y: number;
4
+ }
5
+
6
+ export interface IArcCompareNodeGeo {
7
+ readonly index: number;
8
+ readonly center: IArcComparePoint;
9
+ readonly radius: number;
10
+ }
11
+
12
+ export interface IArcCompareCalloutBox {
13
+ readonly index: number;
14
+ readonly x: number;
15
+ readonly y: number;
16
+ readonly width: number;
17
+ readonly badgeOffset: number;
18
+ readonly top: number;
19
+ readonly textGap?: number;
20
+ readonly textWidth?: number;
21
+ }
22
+
23
+ export interface IArcCompareSideGeometry {
24
+ readonly hubPath: string;
25
+ readonly hubCenter: IArcComparePoint;
26
+ readonly hubRadius: number;
27
+ readonly arcPath: string;
28
+ readonly topDot: IArcComparePoint;
29
+ readonly bottomDot: IArcComparePoint;
30
+ readonly dotRadius: number;
31
+ readonly nodes: readonly IArcCompareNodeGeo[];
32
+ readonly callouts: readonly IArcCompareCalloutBox[];
33
+ }
34
+
35
+ export interface IArcCompareGeometry {
36
+ readonly viewBoxWidth: number;
37
+ readonly viewBoxHeight: number;
38
+ readonly centerX: number;
39
+ readonly centerY: number;
40
+ readonly vsCenter: IArcComparePoint;
41
+ readonly vsRadius: number;
42
+ readonly pointsCount: number;
43
+ readonly dividerLine: {
44
+ readonly x: number;
45
+ readonly y1: number;
46
+ readonly y2: number;
47
+ };
48
+ readonly left: IArcCompareSideGeometry;
49
+ readonly right: IArcCompareSideGeometry;
50
+ }
51
+
52
+ export interface ICreateArcCompareGeometryOptions {
53
+ readonly viewBoxWidth?: number;
54
+ readonly viewBoxHeight?: number;
55
+ readonly hubRadius?: number;
56
+ readonly arcRadius?: number;
57
+ readonly arcOffset?: number;
58
+ readonly nodeRadius?: number;
59
+ readonly nodeAngleDeg?: number;
60
+ readonly arcExtentDeg?: number;
61
+ readonly vsRadius?: number;
62
+ readonly pointsCount?: number;
63
+ }
64
+
65
+ export function createArcCompareGeometry(
66
+ options: ICreateArcCompareGeometryOptions = {},
67
+ ): IArcCompareGeometry {
68
+ const viewBoxWidth = options.viewBoxWidth ?? 1000;
69
+ const viewBoxHeight = options.viewBoxHeight ?? 480;
70
+ const hubRadius = options.hubRadius ?? 135;
71
+ const arcRadius = options.arcRadius ?? 235;
72
+ const arcOffset = options.arcOffset ?? 25;
73
+ const nodeRadius = options.nodeRadius ?? 26;
74
+ const vsRadius = options.vsRadius ?? 26;
75
+ const dotRadius = 3.5;
76
+ const pointsCount = options.pointsCount === 4 ? 4 : 3;
77
+
78
+ const defaultArcExtent = pointsCount === 4 ? 66 : 58;
79
+ const arcExtentDeg = options.arcExtentDeg ?? defaultArcExtent;
80
+
81
+ const centerX = viewBoxWidth / 2;
82
+ const centerY = viewBoxHeight / 2;
83
+ const leftArcCenterX = -arcOffset;
84
+
85
+ // Angular math for left side:
86
+ const extentRad = (arcExtentDeg * Math.PI) / 180;
87
+
88
+ // Left arc start (top) and end (bottom)
89
+ const leftStartCos = Math.cos(extentRad);
90
+ const leftStartSin = Math.sin(extentRad);
91
+ const leftTopX = Number(
92
+ (leftArcCenterX + arcRadius * leftStartCos).toFixed(2),
93
+ );
94
+ const leftTopY = Number((centerY - arcRadius * leftStartSin).toFixed(2));
95
+ const leftBottomX = leftTopX;
96
+ const leftBottomY = Number((centerY + arcRadius * leftStartSin).toFixed(2));
97
+
98
+ // Left arc path
99
+ const leftArcPath = `M ${leftTopX} ${leftTopY} A ${arcRadius} ${arcRadius} 0 0 1 ${leftBottomX} ${leftBottomY}`;
100
+
101
+ // Left hub path (semicircle centered at 0, centerY)
102
+ const leftHubPath = `M 0 ${centerY - hubRadius} A ${hubRadius} ${hubRadius} 0 0 1 0 ${centerY + hubRadius} Z`;
103
+
104
+ // Determine node angles for 3 or 4 points
105
+ const nodeAnglesDeg: readonly number[] =
106
+ pointsCount === 4
107
+ ? [50, 16.5, -16.5, -50]
108
+ : [options.nodeAngleDeg ?? 38, 0, -(options.nodeAngleDeg ?? 38)];
109
+
110
+ // Left nodes and callouts
111
+ const leftCalloutGap = 26;
112
+ const leftNodes: IArcCompareNodeGeo[] = [];
113
+ const leftCallouts: IArcCompareCalloutBox[] = [];
114
+ const defaultTextGap = 28;
115
+ const textWidth = pointsCount === 4 ? 215 : 205;
116
+
117
+ for (let i = 0; i < pointsCount; i++) {
118
+ const angleDeg = nodeAnglesDeg[i] ?? 0;
119
+ const angleRad = (angleDeg * Math.PI) / 180;
120
+ const nodeX = Number(
121
+ (leftArcCenterX + arcRadius * Math.cos(angleRad)).toFixed(2),
122
+ );
123
+ const nodeY = Number((centerY - arcRadius * Math.sin(angleRad)).toFixed(2));
124
+
125
+ leftNodes.push({
126
+ index: i,
127
+ center: {
128
+ x: nodeX,
129
+ y: nodeY,
130
+ },
131
+ radius: nodeRadius,
132
+ });
133
+
134
+ const badgeOffset = Math.round(nodeX - nodeRadius);
135
+ const top = Math.round(nodeY - nodeRadius);
136
+ // Only adjust position for 01 (index 0) so its text does not overlay the arc curve.
137
+ // 02, 03, and 04 keep their standard natural gap.
138
+ const textGap = i === 0 ? (pointsCount === 4 ? 46 : 36) : defaultTextGap;
139
+ const width = Math.round(nodeRadius * 2 + textGap + textWidth);
140
+
141
+ leftCallouts.push({
142
+ index: i,
143
+ x: nodeX + leftCalloutGap,
144
+ y: nodeY - 32,
145
+ width,
146
+ badgeOffset,
147
+ top,
148
+ textGap,
149
+ textWidth,
150
+ });
151
+ }
152
+
153
+ // Right side (symmetrical reflection across centerX)
154
+ const rightTopX = Number((viewBoxWidth - leftTopX).toFixed(2));
155
+ const rightTopY = leftTopY;
156
+ const rightBottomX = rightTopX;
157
+ const rightBottomY = leftBottomY;
158
+
159
+ const rightArcPath = `M ${rightTopX} ${rightTopY} A ${arcRadius} ${arcRadius} 0 0 0 ${rightBottomX} ${rightBottomY}`;
160
+ const rightHubPath = `M ${viewBoxWidth} ${centerY - hubRadius} A ${hubRadius} ${hubRadius} 0 0 0 ${viewBoxWidth} ${centerY + hubRadius} Z`;
161
+
162
+ const rightNodes: IArcCompareNodeGeo[] = [];
163
+ const rightCallouts: IArcCompareCalloutBox[] = [];
164
+
165
+ for (let i = 0; i < pointsCount; i++) {
166
+ const leftNode = leftNodes[i];
167
+ const leftCallout = leftCallouts[i];
168
+
169
+ if (!leftNode || !leftCallout) {
170
+ continue;
171
+ }
172
+
173
+ const rightNodeX = Number((viewBoxWidth - leftNode.center.x).toFixed(2));
174
+ const rightNodeY = leftNode.center.y;
175
+
176
+ rightNodes.push({
177
+ index: i,
178
+ center: {
179
+ x: rightNodeX,
180
+ y: rightNodeY,
181
+ },
182
+ radius: nodeRadius,
183
+ });
184
+
185
+ rightCallouts.push({
186
+ index: i,
187
+ x: centerX + 20,
188
+ y: rightNodeY - 32,
189
+ width: leftCallout.width,
190
+ badgeOffset: leftCallout.badgeOffset,
191
+ top: leftCallout.top,
192
+ textGap: leftCallout.textGap,
193
+ textWidth: leftCallout.textWidth,
194
+ });
195
+ }
196
+
197
+ return {
198
+ viewBoxWidth,
199
+ viewBoxHeight,
200
+ centerX,
201
+ centerY,
202
+ vsCenter: {
203
+ x: centerX,
204
+ y: centerY,
205
+ },
206
+ vsRadius,
207
+ pointsCount,
208
+ dividerLine: {
209
+ x: centerX,
210
+ y1: 20,
211
+ y2: viewBoxHeight - 20,
212
+ },
213
+ left: {
214
+ hubPath: leftHubPath,
215
+ hubCenter: {
216
+ x: 55,
217
+ y: centerY,
218
+ },
219
+ hubRadius,
220
+ arcPath: leftArcPath,
221
+ topDot: {
222
+ x: leftTopX,
223
+ y: leftTopY,
224
+ },
225
+ bottomDot: {
226
+ x: leftBottomX,
227
+ y: leftBottomY,
228
+ },
229
+ dotRadius,
230
+ nodes: leftNodes,
231
+ callouts: leftCallouts,
232
+ },
233
+ right: {
234
+ hubPath: rightHubPath,
235
+ hubCenter: {
236
+ x: viewBoxWidth - 55,
237
+ y: centerY,
238
+ },
239
+ hubRadius,
240
+ arcPath: rightArcPath,
241
+ topDot: {
242
+ x: rightTopX,
243
+ y: rightTopY,
244
+ },
245
+ bottomDot: {
246
+ x: rightBottomX,
247
+ y: rightBottomY,
248
+ },
249
+ dotRadius,
250
+ nodes: rightNodes,
251
+ callouts: rightCallouts,
252
+ },
253
+ };
254
+ }
@@ -0,0 +1,3 @@
1
+ export * from './context';
2
+ export * from './createArcCompareGeometry';
3
+ export * from './resolveArcCompareItems';