@moderneinc/react-charts 1.1.0 → 1.2.0-next.2f2ee2
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/dist/components/chrono-chart/chrono-chart.component.d.ts +3 -0
- package/dist/components/chrono-chart/chrono-chart.types.d.ts +58 -0
- package/dist/components/chrono-chart/components/category-table.component.d.ts +3 -0
- package/dist/components/chrono-chart/components/category-table.types.d.ts +17 -0
- package/dist/components/chrono-chart/utils/data-transformer.d.ts +9 -0
- package/dist/components/d3-stacked-area-chart/d3-stacked-area-chart.component.d.ts +7 -0
- package/dist/components/d3-stacked-area-chart/d3-stacked-area-chart.types.d.ts +89 -0
- package/dist/components/d3-stacked-area-chart/hooks/use-d3-stacked-area.hook.d.ts +34 -0
- package/dist/components/morph-chart/hooks/shared/computations.d.ts +27 -0
- package/dist/components/morph-chart/hooks/shared/types.d.ts +22 -0
- package/dist/components/morph-chart/hooks/use-morph-chart.hook.d.ts +85 -0
- package/dist/components/morph-chart/index.d.ts +2 -0
- package/dist/components/morph-chart/morph-chart.component.d.ts +14 -0
- package/dist/components/morph-chart/morph-chart.types.d.ts +154 -0
- package/dist/components/morph-chart/utils/animation-constants.d.ts +23 -0
- package/dist/components/morph-chart/utils/animation-utils.d.ts +44 -0
- package/dist/components/morph-chart/utils/arc-path-calculator.d.ts +53 -0
- package/dist/components/morph-chart/utils/area-renderer.d.ts +24 -0
- package/dist/components/morph-chart/utils/bar-renderer.d.ts +19 -0
- package/dist/components/morph-chart/utils/gsap-orchestrator.d.ts +252 -0
- package/dist/components/morph-chart/utils/morph-interpolator.d.ts +96 -0
- package/dist/components/morph-chart/utils/parliament-renderer.d.ts +23 -0
- package/dist/components/morph-chart/utils/parliament-seat-extractor.d.ts +33 -0
- package/dist/components/morph-chart/utils/position-mapper.d.ts +48 -0
- package/dist/components/morph-chart/utils/segment-transformer.d.ts +70 -0
- package/dist/components/morph-chart/utils/svg-patterns.d.ts +19 -0
- package/dist/components/parliament-chart/hooks/{useParliamentChart.d.ts → use-parliament-chart.hook.d.ts} +6 -2
- package/dist/components/parliament-chart/{ParliamentChart.d.ts → parliament-chart.component.d.ts} +1 -1
- package/dist/components/parliament-chart/{ParliamentChart.types.d.ts → parliament-chart.types.d.ts} +6 -1
- package/dist/components/parliament-chart/utils/parliament-animation.d.ts +13 -0
- package/dist/components/stacked-area-chart/hooks/use-stacked-area-chart.hook.d.ts +6 -0
- package/dist/components/stacked-area-chart/stacked-area-chart.component.d.ts +10 -0
- package/dist/components/stacked-area-chart/stacked-area-chart.constants.d.ts +115 -0
- package/dist/components/stacked-area-chart/stacked-area-chart.types.d.ts +186 -0
- package/dist/components/stacked-area-chart/utils/color.utils.d.ts +4 -0
- package/dist/components/timeline-chart/hooks/use-timeline-chart.hook.d.ts +2 -0
- package/dist/components/timeline-chart/timeline-chart.component.d.ts +9 -0
- package/dist/components/timeline-chart/timeline-chart.types.d.ts +133 -0
- package/dist/components/timeline-chart/timeline-selected-events.component.d.ts +9 -0
- package/dist/index.cjs +144 -11
- package/dist/index.d.ts +21 -8
- package/dist/index.js +30451 -4879
- package/dist/theme/{defaultColors.d.ts → default-colors.d.ts} +8 -4
- package/dist/theme/timeline-defaults.d.ts +50 -0
- package/package.json +32 -13
- /package/dist/components/parliament-chart/{ParliamentChart.constants.d.ts → parliament-chart.constants.d.ts} +0 -0
- /package/dist/components/parliament-chart/utils/{parliamentArcCalculator.d.ts → parliament-arc-calculator.d.ts} +0 -0
- /package/dist/components/parliament-chart/utils/{parliamentChartData.utils.d.ts → parliament-chart-data.utils.d.ts} +0 -0
- /package/dist/components/parliament-chart/utils/{parliamentSvgEnhanced.d.ts → parliament-svg-enhanced.d.ts} +0 -0
- /package/dist/components/parliament-chart/utils/{parliamentSvgPatterns.d.ts → parliament-svg-patterns.d.ts} +0 -0
- /package/dist/components/parliament-chart/utils/{parliamentSvg.d.ts → parliament-svg.d.ts} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TimelineEvent } from '../timeline-chart/timeline-chart.types';
|
|
2
|
+
export type ChronoMode = 'point-in-time' | 'historical';
|
|
3
|
+
export type ChronoCategory = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
color: string;
|
|
7
|
+
parliamentMapping?: {
|
|
8
|
+
isSpecialCategory: boolean;
|
|
9
|
+
hatchPattern?: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type ChronoData = {
|
|
13
|
+
timeSeries: Array<{
|
|
14
|
+
timestamp: number;
|
|
15
|
+
values: Record<string, number>;
|
|
16
|
+
}>;
|
|
17
|
+
categories: ChronoCategory[];
|
|
18
|
+
metadata: {
|
|
19
|
+
total: number;
|
|
20
|
+
specialCounts?: {
|
|
21
|
+
notApplicable: number;
|
|
22
|
+
noLst: number;
|
|
23
|
+
unavailable: number;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
events?: TimelineEvent[];
|
|
27
|
+
};
|
|
28
|
+
export type ChronoChartProps = {
|
|
29
|
+
data: ChronoData;
|
|
30
|
+
/**
|
|
31
|
+
* Index of the time series data point to display in point-in-time mode.
|
|
32
|
+
* - If set: Chart shows point-in-time view for data at this index
|
|
33
|
+
* - If undefined: Chart shows historical view (default)
|
|
34
|
+
* - If data.timeSeries.length === 1: Automatically shows point-in-time mode
|
|
35
|
+
*
|
|
36
|
+
* Note: Changing selectedIndex does NOT trigger animation. Animation only
|
|
37
|
+
* plays when toggling between point-in-time and historical modes.
|
|
38
|
+
*/
|
|
39
|
+
selectedIndex?: number;
|
|
40
|
+
title?: string;
|
|
41
|
+
subtitle?: string;
|
|
42
|
+
showHeader?: boolean;
|
|
43
|
+
width?: number;
|
|
44
|
+
height?: number;
|
|
45
|
+
timeRange?: [number, number];
|
|
46
|
+
onTimeRangeChange?: (range: [number, number]) => void;
|
|
47
|
+
enableBrush?: boolean;
|
|
48
|
+
showLegend?: boolean;
|
|
49
|
+
showTooltip?: boolean;
|
|
50
|
+
showGrid?: boolean;
|
|
51
|
+
enableAnimation?: boolean;
|
|
52
|
+
animationDuration?: number;
|
|
53
|
+
formatDate?: (timestamp: number) => string;
|
|
54
|
+
formatValue?: (value: number) => string;
|
|
55
|
+
onAnimationStateChange?: (isAnimating: boolean) => void;
|
|
56
|
+
onTimelineReady?: (timeline: unknown) => void;
|
|
57
|
+
onAnimationProgress?: (progress: number, stage: string) => void;
|
|
58
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type CategoryTableItem = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
percentage: number;
|
|
6
|
+
color: string;
|
|
7
|
+
tooltip?: string;
|
|
8
|
+
max?: number;
|
|
9
|
+
min?: number;
|
|
10
|
+
};
|
|
11
|
+
export type CategoryTableProps = {
|
|
12
|
+
categories: CategoryTableItem[];
|
|
13
|
+
activeCategory: string | null;
|
|
14
|
+
onCategoryHover: (categoryId: string | null) => void;
|
|
15
|
+
mode: 'point-in-time' | 'historical';
|
|
16
|
+
visible?: boolean;
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MorphChartCategory, MorphChartDataPoint } from '../../morph-chart/morph-chart.types';
|
|
2
|
+
import { ChronoData } from '../chrono-chart.types';
|
|
3
|
+
import { CategoryTableItem } from '../components/category-table.types';
|
|
4
|
+
export declare const extractLatestValues: (data: ChronoData, mode?: "point-in-time" | "historical") => CategoryTableItem[];
|
|
5
|
+
export declare const transformToMorphChart: (data: ChronoData) => {
|
|
6
|
+
data: MorphChartDataPoint[];
|
|
7
|
+
categories: MorphChartCategory[];
|
|
8
|
+
};
|
|
9
|
+
export declare const validateChronoData: (data: ChronoData) => void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
import { D3StackedAreaChartProps } from './d3-stacked-area-chart.types';
|
|
3
|
+
/**
|
|
4
|
+
* D3-based stacked area chart component
|
|
5
|
+
* Built for seamless morphing with parliament chart
|
|
6
|
+
*/
|
|
7
|
+
export declare const D3StackedAreaChart: FunctionComponent<D3StackedAreaChartProps>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D3-based stacked area chart types
|
|
3
|
+
* Built for seamless morphing with parliament chart
|
|
4
|
+
*/
|
|
5
|
+
export type D3StackedAreaDataPoint = {
|
|
6
|
+
timestamp: number;
|
|
7
|
+
[category: string]: number;
|
|
8
|
+
};
|
|
9
|
+
export type D3StackedAreaCategory = {
|
|
10
|
+
dataKey: string;
|
|
11
|
+
label: string;
|
|
12
|
+
color: string;
|
|
13
|
+
strokeWidth?: number;
|
|
14
|
+
strokeDasharray?: string;
|
|
15
|
+
fillOpacity?: number;
|
|
16
|
+
};
|
|
17
|
+
export type D3StackedAreaChartProps = {
|
|
18
|
+
/** Array of data points sorted by timestamp */
|
|
19
|
+
data: D3StackedAreaDataPoint[];
|
|
20
|
+
/** Array of category definitions */
|
|
21
|
+
categories: D3StackedAreaCategory[];
|
|
22
|
+
/** Chart title */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Chart subtitle */
|
|
25
|
+
subtitle?: string;
|
|
26
|
+
/** Show header (title and subtitle) */
|
|
27
|
+
showHeader?: boolean;
|
|
28
|
+
/** Show legend */
|
|
29
|
+
showLegend?: boolean;
|
|
30
|
+
/** Show tooltips on hover */
|
|
31
|
+
showTooltip?: boolean;
|
|
32
|
+
/** Chart dimensions */
|
|
33
|
+
width?: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
/** Margins for axes */
|
|
36
|
+
margin?: {
|
|
37
|
+
top: number;
|
|
38
|
+
right: number;
|
|
39
|
+
bottom: number;
|
|
40
|
+
left: number;
|
|
41
|
+
};
|
|
42
|
+
/** Time range for x-axis */
|
|
43
|
+
timeRange?: [number, number];
|
|
44
|
+
/** Show grid lines */
|
|
45
|
+
showGrid?: boolean;
|
|
46
|
+
/** Show axes (deprecated: use showXAxis and showYAxis instead) */
|
|
47
|
+
showAxes?: boolean;
|
|
48
|
+
/** Show X-axis */
|
|
49
|
+
showXAxis?: boolean;
|
|
50
|
+
/** Show Y-axis */
|
|
51
|
+
showYAxis?: boolean;
|
|
52
|
+
/** Enable animations */
|
|
53
|
+
enableAnimation?: boolean;
|
|
54
|
+
/** Animation duration */
|
|
55
|
+
animationDuration?: number;
|
|
56
|
+
/** Format date for axis */
|
|
57
|
+
formatDate?: (timestamp: number) => string;
|
|
58
|
+
/** Format value for axis */
|
|
59
|
+
formatValue?: (value: number) => string;
|
|
60
|
+
/** Callback when time range changes */
|
|
61
|
+
onTimeRangeChange?: (range: [number, number]) => void;
|
|
62
|
+
/** Enable brush selection */
|
|
63
|
+
enableBrush?: boolean;
|
|
64
|
+
/** Zoom to selection - when true, brush clears after selection and expects chart to zoom via timeRange update */
|
|
65
|
+
zoomToSelection?: boolean;
|
|
66
|
+
/** Markers (vertical lines for events) */
|
|
67
|
+
markers?: Array<{
|
|
68
|
+
timestamp: number;
|
|
69
|
+
label?: string;
|
|
70
|
+
color?: string;
|
|
71
|
+
strokeDasharray?: string;
|
|
72
|
+
}>;
|
|
73
|
+
/** Marker visibility mode: 'hover' shows only nearest marker on hover, 'always' shows all markers */
|
|
74
|
+
markerVisibilityMode?: 'always' | 'hover';
|
|
75
|
+
/** Mode for morphing animation */
|
|
76
|
+
morphMode?: 'area' | 'parliament' | 'morphing';
|
|
77
|
+
/** Parliament layout data (for morphing) */
|
|
78
|
+
parliamentData?: {
|
|
79
|
+
arcAngle: number;
|
|
80
|
+
radius: number;
|
|
81
|
+
seatPositions: Array<{
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
category: string;
|
|
85
|
+
}>;
|
|
86
|
+
};
|
|
87
|
+
/** Morph progress (0 = area, 1 = parliament) */
|
|
88
|
+
morphProgress?: number;
|
|
89
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { D3StackedAreaCategory, D3StackedAreaDataPoint } from '../d3-stacked-area-chart.types';
|
|
3
|
+
type UseD3StackedAreaProps = {
|
|
4
|
+
containerRef: RefObject<SVGSVGElement>;
|
|
5
|
+
data: D3StackedAreaDataPoint[];
|
|
6
|
+
categories: D3StackedAreaCategory[];
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
margin: {
|
|
10
|
+
top: number;
|
|
11
|
+
right: number;
|
|
12
|
+
bottom: number;
|
|
13
|
+
left: number;
|
|
14
|
+
};
|
|
15
|
+
timeRange?: [number, number];
|
|
16
|
+
showGrid?: boolean;
|
|
17
|
+
showXAxis?: boolean;
|
|
18
|
+
showYAxis?: boolean;
|
|
19
|
+
enableBrush?: boolean;
|
|
20
|
+
zoomToSelection?: boolean;
|
|
21
|
+
onTimeRangeChange?: (range: [number, number]) => void;
|
|
22
|
+
formatDate?: (timestamp: number) => string;
|
|
23
|
+
formatValue?: (value: number) => string;
|
|
24
|
+
markers?: Array<{
|
|
25
|
+
timestamp: number;
|
|
26
|
+
label?: string;
|
|
27
|
+
color?: string;
|
|
28
|
+
strokeDasharray?: string;
|
|
29
|
+
}>;
|
|
30
|
+
markerVisibilityMode?: 'always' | 'hover';
|
|
31
|
+
onMarkerHoverChange?: (isHovering: boolean) => void;
|
|
32
|
+
};
|
|
33
|
+
export declare const useD3StackedArea: ({ containerRef, data, categories, width, height, margin, timeRange, showGrid, showXAxis, showYAxis, enableBrush, zoomToSelection, onTimeRangeChange, formatDate, formatValue, markers, markerVisibilityMode, onMarkerHoverChange }: UseD3StackedAreaProps) => void;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StageDurations } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Calculate stage durations from total animation duration
|
|
4
|
+
*
|
|
5
|
+
* Splits the total duration into proportional stages for smooth timing:
|
|
6
|
+
* - 27% Seats to bar transformation
|
|
7
|
+
* - 3% Pause to appreciate seat-to-bar transition
|
|
8
|
+
* - 25% Bar slide to timeline + axes appear
|
|
9
|
+
* - 25% Other bars grow from baseline
|
|
10
|
+
* - 20% Bars morph to curved areas
|
|
11
|
+
*
|
|
12
|
+
* @param totalDuration - Total animation duration in milliseconds
|
|
13
|
+
* @returns Object with duration for each stage in milliseconds
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const durations = calculateStageDurations(2000);
|
|
18
|
+
* // {
|
|
19
|
+
* // seatsToBar: 540, // 27%
|
|
20
|
+
* // crossFadePause: 60, // 3%
|
|
21
|
+
* // barSlideToTimeline: 500, // 25%
|
|
22
|
+
* // axesAndBarsGrow: 500, // 25%
|
|
23
|
+
* // barsToArea: 400 // 20%
|
|
24
|
+
* // }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function calculateStageDurations(totalDuration: number): StageDurations;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for morph animation stages
|
|
3
|
+
*
|
|
4
|
+
* These types are used across multiple animation stages for consistent
|
|
5
|
+
* timing and duration calculations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Animation stage durations for parliament → area morph (4 stages)
|
|
9
|
+
* Calculated as fractions of total duration for smooth timing
|
|
10
|
+
*/
|
|
11
|
+
export type StageDurations = {
|
|
12
|
+
/** Frame 1→2: Seats directly transform to vertical bar (27% of total) */
|
|
13
|
+
seatsToBar: number;
|
|
14
|
+
/** Pause after cross-fade to appreciate seat-to-bar transition (3% of total) */
|
|
15
|
+
crossFadePause: number;
|
|
16
|
+
/** Frame 2→3: Bar slides to timeline + axes/grid appear (25% of total) */
|
|
17
|
+
barSlideToTimeline: number;
|
|
18
|
+
/** Frame 3→4: Other bars grow from baseline (25% of total) */
|
|
19
|
+
axesAndBarsGrow: number;
|
|
20
|
+
/** Frame 4→5: Bars morph to curved area (20% of total) */
|
|
21
|
+
barsToArea: number;
|
|
22
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { HoveredData, MorphChartCategory, MorphChartDataPoint, MorphMode } from '../morph-chart.types';
|
|
3
|
+
type UseMorphChartProps = {
|
|
4
|
+
containerRef: RefObject<SVGSVGElement>;
|
|
5
|
+
data: MorphChartDataPoint[];
|
|
6
|
+
categories: MorphChartCategory[];
|
|
7
|
+
mode: 'parliament' | 'stacked-area';
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
margin: {
|
|
11
|
+
top: number;
|
|
12
|
+
right: number;
|
|
13
|
+
bottom: number;
|
|
14
|
+
left: number;
|
|
15
|
+
};
|
|
16
|
+
timeRange?: [number, number];
|
|
17
|
+
showGrid?: boolean;
|
|
18
|
+
showAxes?: boolean;
|
|
19
|
+
formatDate?: (timestamp: number) => string;
|
|
20
|
+
formatValue?: (value: number) => string;
|
|
21
|
+
markers?: Array<{
|
|
22
|
+
timestamp: number;
|
|
23
|
+
label?: string;
|
|
24
|
+
color?: string;
|
|
25
|
+
}>;
|
|
26
|
+
arcAngle?: number;
|
|
27
|
+
parliamentRadius?: number;
|
|
28
|
+
seatSize?: number;
|
|
29
|
+
animationDuration?: number;
|
|
30
|
+
onMorphComplete?: () => void;
|
|
31
|
+
onAnimationStateChange?: (isAnimating: boolean) => void;
|
|
32
|
+
onTimelineReady?: (timeline: gsap.core.Timeline) => void;
|
|
33
|
+
onAnimationProgress?: (progress: number, stage: string) => void;
|
|
34
|
+
onHoveredDataChange?: (data: HoveredData) => void;
|
|
35
|
+
hoveredCategory?: string | null;
|
|
36
|
+
maxSeats?: number;
|
|
37
|
+
parliamentTimestamp?: number;
|
|
38
|
+
enableBrush?: boolean;
|
|
39
|
+
onTimeRangeChange?: (range: [number, number]) => void;
|
|
40
|
+
showScaleIndicator?: boolean;
|
|
41
|
+
reposPerSeat?: number;
|
|
42
|
+
timelineEvents?: Array<{
|
|
43
|
+
timestamp: number;
|
|
44
|
+
eventName: string;
|
|
45
|
+
color?: string;
|
|
46
|
+
}>;
|
|
47
|
+
showTimeline?: boolean;
|
|
48
|
+
timelineHeight?: number;
|
|
49
|
+
timelineOffset?: number;
|
|
50
|
+
};
|
|
51
|
+
export declare const useMorphChart: ({ containerRef, data, categories, mode, width, height, margin, timeRange, showGrid, showAxes, formatDate, formatValue, markers, arcAngle, parliamentRadius, seatSize, animationDuration, onMorphComplete, onAnimationStateChange, onTimelineReady, onAnimationProgress, onHoveredDataChange, hoveredCategory: externalHoveredCategory, maxSeats, parliamentTimestamp, enableBrush, onTimeRangeChange, showScaleIndicator, reposPerSeat, timelineEvents, showTimeline, timelineHeight, timelineOffset }: UseMorphChartProps) => {
|
|
52
|
+
isMorphing: boolean;
|
|
53
|
+
currentMode: MorphMode;
|
|
54
|
+
hoveredCategory: string | null;
|
|
55
|
+
scaleIndicatorOpacity: number;
|
|
56
|
+
visibleMarkers: {
|
|
57
|
+
timestamp: number;
|
|
58
|
+
label?: string;
|
|
59
|
+
color?: string;
|
|
60
|
+
x: number;
|
|
61
|
+
}[];
|
|
62
|
+
hoveredMarker: {
|
|
63
|
+
timestamp: number;
|
|
64
|
+
label?: string;
|
|
65
|
+
color?: string;
|
|
66
|
+
x: number;
|
|
67
|
+
} | null;
|
|
68
|
+
setHoveredMarker: import('react').Dispatch<import('react').SetStateAction<{
|
|
69
|
+
timestamp: number;
|
|
70
|
+
label?: string;
|
|
71
|
+
color?: string;
|
|
72
|
+
x: number;
|
|
73
|
+
} | null>>;
|
|
74
|
+
hoveredTimelineEvent: {
|
|
75
|
+
timestamp: number;
|
|
76
|
+
eventName: string;
|
|
77
|
+
x: number;
|
|
78
|
+
} | null;
|
|
79
|
+
setHoveredTimelineEvent: import('react').Dispatch<import('react').SetStateAction<{
|
|
80
|
+
timestamp: number;
|
|
81
|
+
eventName: string;
|
|
82
|
+
x: number;
|
|
83
|
+
} | null>>;
|
|
84
|
+
};
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
import { MorphChartProps } from './morph-chart.types';
|
|
3
|
+
/**
|
|
4
|
+
* MorphChart - Seamlessly morphs between parliament and stacked area visualizations
|
|
5
|
+
*
|
|
6
|
+
* This component uses D3 to create smooth transitions between a parliament chart
|
|
7
|
+
* (point-in-time view) and a stacked area chart (historical view).
|
|
8
|
+
*
|
|
9
|
+
* Key insight: The parliament chart represents the rightmost edge (latest timestamp)
|
|
10
|
+
* of the stacked area chart. During morphing:
|
|
11
|
+
* - Parliament → Stacked Area: Seats slide to the right edge, then areas expand leftward
|
|
12
|
+
* - Stacked Area → Parliament: Areas collapse to the right edge, then seats expand to parliament layout
|
|
13
|
+
*/
|
|
14
|
+
export declare const MorphChart: FunctionComponent<MorphChartProps>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { D3StackedAreaCategory } from '../d3-stacked-area-chart/d3-stacked-area-chart.types';
|
|
2
|
+
export type MorphMode = 'area' | 'parliament' | 'morphing';
|
|
3
|
+
export type MorphChartDataPoint = {
|
|
4
|
+
timestamp: number;
|
|
5
|
+
values: Record<string, number>;
|
|
6
|
+
};
|
|
7
|
+
export type MorphChartCategory = D3StackedAreaCategory & {
|
|
8
|
+
/** Parliament chart mapping (for special categories) */
|
|
9
|
+
parliamentMapping?: {
|
|
10
|
+
isSpecialCategory?: boolean;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Hovered data state for tooltips and interactions
|
|
15
|
+
*/
|
|
16
|
+
export type HoveredData = {
|
|
17
|
+
label: string;
|
|
18
|
+
value: number;
|
|
19
|
+
color: string;
|
|
20
|
+
} | null;
|
|
21
|
+
export type MorphChartProps = {
|
|
22
|
+
/** Time series data for area chart */
|
|
23
|
+
data: MorphChartDataPoint[];
|
|
24
|
+
/** Category definitions */
|
|
25
|
+
categories: MorphChartCategory[];
|
|
26
|
+
/** Current visualization mode */
|
|
27
|
+
mode: 'parliament' | 'stacked-area';
|
|
28
|
+
/** Morph progress: 0 = fully area, 1 = fully parliament */
|
|
29
|
+
morphProgress?: number;
|
|
30
|
+
/** Chart dimensions */
|
|
31
|
+
width?: number;
|
|
32
|
+
height?: number;
|
|
33
|
+
/** Margins */
|
|
34
|
+
margin?: {
|
|
35
|
+
top: number;
|
|
36
|
+
right: number;
|
|
37
|
+
bottom: number;
|
|
38
|
+
left: number;
|
|
39
|
+
};
|
|
40
|
+
/** Time range for area chart */
|
|
41
|
+
timeRange?: [number, number];
|
|
42
|
+
/** Show grid lines */
|
|
43
|
+
showGrid?: boolean;
|
|
44
|
+
/** Show axes */
|
|
45
|
+
showAxes?: boolean;
|
|
46
|
+
/** Enable brush selection */
|
|
47
|
+
enableBrush?: boolean;
|
|
48
|
+
/** Callback when time range changes */
|
|
49
|
+
onTimeRangeChange?: (range: [number, number]) => void;
|
|
50
|
+
/** Format date for axis */
|
|
51
|
+
formatDate?: (timestamp: number) => string;
|
|
52
|
+
/** Format value for axis */
|
|
53
|
+
formatValue?: (value: number) => string;
|
|
54
|
+
/** Event markers */
|
|
55
|
+
markers?: Array<{
|
|
56
|
+
timestamp: number;
|
|
57
|
+
label?: string;
|
|
58
|
+
color?: string;
|
|
59
|
+
}>;
|
|
60
|
+
/** Animation duration in milliseconds */
|
|
61
|
+
animationDuration?: number;
|
|
62
|
+
/** Callback when morphing animation completes */
|
|
63
|
+
onMorphComplete?: () => void;
|
|
64
|
+
/** Callback when animation state changes */
|
|
65
|
+
onAnimationStateChange?: (isAnimating: boolean) => void;
|
|
66
|
+
/** Callback when GSAP timeline is ready for control */
|
|
67
|
+
onTimelineReady?: (timeline: unknown) => void;
|
|
68
|
+
/** Callback for animation progress updates */
|
|
69
|
+
onAnimationProgress?: (progress: number, stage: string) => void;
|
|
70
|
+
/** Parliament arc angle (degrees) */
|
|
71
|
+
arcAngle?: number;
|
|
72
|
+
/** Parliament radius */
|
|
73
|
+
parliamentRadius?: number;
|
|
74
|
+
/** Parliament seat size */
|
|
75
|
+
seatSize?: number;
|
|
76
|
+
/** Use enhanced parliament rendering */
|
|
77
|
+
useEnhancedParliament?: boolean;
|
|
78
|
+
/** Callback when hover state changes */
|
|
79
|
+
onHoveredDataChange?: (data: HoveredData) => void;
|
|
80
|
+
/** Currently hovered category (for external control of highlighting) */
|
|
81
|
+
hoveredCategory?: string | null;
|
|
82
|
+
/** Max seats before scaling (default: 500) */
|
|
83
|
+
maxSeats?: number;
|
|
84
|
+
/** Show tooltip overlay (default: true) */
|
|
85
|
+
showTooltip?: boolean;
|
|
86
|
+
/** Show category summary table (default: false for MorphChart) */
|
|
87
|
+
showTable?: boolean;
|
|
88
|
+
/** Show scaled indicator when seats > maxSeats (default: true) */
|
|
89
|
+
showScaledIndicator?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Explicit timestamp that the parliament represents.
|
|
92
|
+
* If provided, this overrides automatic timestamp detection and ensures
|
|
93
|
+
* the bar slides to the correct position on the timeline during animation.
|
|
94
|
+
* If not provided, defaults to the rightmost (most recent) timestamp.
|
|
95
|
+
*/
|
|
96
|
+
parliamentTimestamp?: number;
|
|
97
|
+
/** Timeline events to display below the x-axis (area mode only) */
|
|
98
|
+
timelineEvents?: Array<{
|
|
99
|
+
timestamp: number;
|
|
100
|
+
eventName: string;
|
|
101
|
+
color?: string;
|
|
102
|
+
}>;
|
|
103
|
+
/** Show timeline track (default: false) */
|
|
104
|
+
showTimeline?: boolean;
|
|
105
|
+
/** Timeline track height in pixels (default: 40) */
|
|
106
|
+
timelineHeight?: number;
|
|
107
|
+
/** Space between x-axis and timeline in pixels (default: 35) */
|
|
108
|
+
timelineOffset?: number;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Seat position in parliament layout
|
|
112
|
+
*/
|
|
113
|
+
export type SeatPosition = {
|
|
114
|
+
x: number;
|
|
115
|
+
y: number;
|
|
116
|
+
category: string;
|
|
117
|
+
categoryIndex: number;
|
|
118
|
+
seatIndex: number;
|
|
119
|
+
ring: number;
|
|
120
|
+
angle: number;
|
|
121
|
+
radius: number;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Area chart point in cartesian space
|
|
125
|
+
*/
|
|
126
|
+
export type AreaPoint = {
|
|
127
|
+
x: number;
|
|
128
|
+
y: number;
|
|
129
|
+
y0: number;
|
|
130
|
+
y1: number;
|
|
131
|
+
category: string;
|
|
132
|
+
timestamp: number;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Parliament layout metadata
|
|
136
|
+
*/
|
|
137
|
+
export type ParliamentLayout = {
|
|
138
|
+
arcAngle: number;
|
|
139
|
+
radius: number;
|
|
140
|
+
innerRadius: number;
|
|
141
|
+
centerX: number;
|
|
142
|
+
centerY: number;
|
|
143
|
+
seatSize: number;
|
|
144
|
+
seats: SeatPosition[];
|
|
145
|
+
totalSeats: number;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Area chart layout metadata
|
|
149
|
+
*/
|
|
150
|
+
export type AreaLayout = {
|
|
151
|
+
chartWidth: number;
|
|
152
|
+
chartHeight: number;
|
|
153
|
+
points: AreaPoint[];
|
|
154
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation constants for parliament to stacked area morphing
|
|
3
|
+
* Centralized configuration for timing, easing, and stage proportions
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* GSAP easing functions for smooth transitions
|
|
7
|
+
* Uses GSAP's power easing for professional motion
|
|
8
|
+
*/
|
|
9
|
+
export declare const EASING: {
|
|
10
|
+
/** Smooth acceleration and deceleration - used for transformations */
|
|
11
|
+
readonly IN_OUT: "power2.inOut";
|
|
12
|
+
/** Smooth deceleration only - used for sliding and growing */
|
|
13
|
+
readonly OUT: "power2.out";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Timing constants for staggered animations
|
|
17
|
+
*/
|
|
18
|
+
export declare const TIMING: {
|
|
19
|
+
/** No stagger - all elements animate simultaneously */
|
|
20
|
+
readonly NO_STAGGER: 0;
|
|
21
|
+
/** Ripple effect delay - each bar delayed by distance × this value (50ms per unit) */
|
|
22
|
+
readonly RIPPLE_STAGGER_DELAY: 0.05;
|
|
23
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for animation path manipulation
|
|
3
|
+
* Shared helpers for creating SVG paths from bar geometries
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a rectangular SVG path from bar dimensions
|
|
7
|
+
* Used as the initial state for path morphing animations
|
|
8
|
+
*
|
|
9
|
+
* @param x - Left edge x-coordinate of the rectangle
|
|
10
|
+
* @param y - Top edge y-coordinate of the rectangle
|
|
11
|
+
* @param width - Width of the rectangle
|
|
12
|
+
* @param height - Height of the rectangle
|
|
13
|
+
* @returns SVG path string representing a closed rectangle
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const path = createRectPath(100, 50, 20, 80);
|
|
18
|
+
* // Returns: "M 100 130 L 100 50 L 120 50 L 120 130 Z"
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRectPath(x: number, y: number, width: number, height: number): string;
|
|
22
|
+
/**
|
|
23
|
+
* Create a combined SVG path from multiple bar segments for a single category
|
|
24
|
+
* Connects all bars into a single continuous path suitable for morphing
|
|
25
|
+
*
|
|
26
|
+
* The path traces along the top edges of all bars (left to right),
|
|
27
|
+
* then closes along the bottom baseline, creating a unified shape.
|
|
28
|
+
*
|
|
29
|
+
* @param bars - Array of SVG rect elements to combine
|
|
30
|
+
* @returns SVG path string representing the combined shape
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const bars = svg.selectAll('.category-java rect').nodes();
|
|
35
|
+
* const combinedPath = createCombinedBarPath(bars);
|
|
36
|
+
* // Returns path that traces outline of all bars
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* - Bars are automatically sorted by x-position (left to right)
|
|
41
|
+
* - Empty array returns empty string
|
|
42
|
+
* - Path draws: bottom-left → tops (left-to-right) → bottom-right → close
|
|
43
|
+
*/
|
|
44
|
+
export declare function createCombinedBarPath(bars: SVGRectElement[]): string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arc path calculator for slinky-style seat → bar animations
|
|
3
|
+
* Generates pronounced bezier arc paths for smooth cascading motion
|
|
4
|
+
*/
|
|
5
|
+
export interface Point {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ArcPathData {
|
|
10
|
+
startX: number;
|
|
11
|
+
startY: number;
|
|
12
|
+
controlX: number;
|
|
13
|
+
controlY: number;
|
|
14
|
+
endX: number;
|
|
15
|
+
endY: number;
|
|
16
|
+
distance: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Calculate the control point for a quadratic bezier curve
|
|
20
|
+
* Creates a pronounced arc by offsetting perpendicular to the direct path
|
|
21
|
+
*
|
|
22
|
+
* @param start - Starting point (seat position)
|
|
23
|
+
* @param end - Ending point (bar segment position)
|
|
24
|
+
* @param arcIntensity - How pronounced the arc should be (0-1, default 0.35 for 35% offset)
|
|
25
|
+
* @returns Control point for the bezier curve
|
|
26
|
+
*/
|
|
27
|
+
export declare function calculateArcControlPoint(start: Point, end: Point, arcIntensity?: number): Point;
|
|
28
|
+
/**
|
|
29
|
+
* Generate complete arc path data for animating a seat to a bar position
|
|
30
|
+
*
|
|
31
|
+
* @param seatPosition - Current seat position in parliament
|
|
32
|
+
* @param barPosition - Target bar segment position
|
|
33
|
+
* @param arcIntensity - How pronounced the arc should be (default 0.35)
|
|
34
|
+
* @returns Complete path data for GSAP animation
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateArcPath(seatPosition: Point, barPosition: Point, arcIntensity?: number): ArcPathData;
|
|
37
|
+
/**
|
|
38
|
+
* Generate SVG path string for a quadratic bezier arc
|
|
39
|
+
* Useful for debugging or visual representation
|
|
40
|
+
*
|
|
41
|
+
* @param pathData - Arc path data
|
|
42
|
+
* @returns SVG path string
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateSVGPath(pathData: ArcPathData): string;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate points along a quadratic bezier curve
|
|
47
|
+
* Useful for visualizing or debugging the arc path
|
|
48
|
+
*
|
|
49
|
+
* @param pathData - Arc path data
|
|
50
|
+
* @param numPoints - Number of points to generate along the curve (default 20)
|
|
51
|
+
* @returns Array of points along the curve
|
|
52
|
+
*/
|
|
53
|
+
export declare function getPointsAlongArc(pathData: ArcPathData, numPoints?: number): Point[];
|