@moderneinc/react-charts 1.2.0-next.d2aaa2 → 1.2.0-next.e6432a
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 +7 -0
- package/dist/components/chrono-chart/chrono-chart.types.d.ts +48 -1
- package/dist/components/chrono-chart/components/category-table.component.d.ts +10 -0
- package/dist/components/chrono-chart/components/category-table.types.d.ts +25 -0
- package/dist/components/chrono-chart/utils/data-transformer.d.ts +32 -0
- package/dist/components/morph-chart/hooks/shared/computations.d.ts +12 -10
- package/dist/components/morph-chart/hooks/shared/types.d.ts +7 -5
- package/dist/components/morph-chart/utils/accordion-generator.d.ts +102 -0
- package/dist/components/morph-chart/utils/gsap-orchestrator.d.ts +55 -16
- package/dist/components/morph-chart/utils/slinky-3d-generator.d.ts +35 -0
- package/dist/components/morph-chart/utils/svg-slinky-generator.d.ts +25 -0
- package/dist/components/timeline-chart/timeline-chart.types.d.ts +16 -0
- package/dist/index.cjs +76 -76
- package/dist/index.js +11503 -10852
- package/dist/theme/timeline-defaults.d.ts +33 -1
- package/package.json +4 -1
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { FunctionComponent } from 'react';
|
|
2
2
|
import { ChronoChartProps } from './chrono-chart.types';
|
|
3
|
+
/**
|
|
4
|
+
* ChronoChart: A dual-mode time-series visualization component.
|
|
5
|
+
*
|
|
6
|
+
* Renders either a parliament diagram (point-in-time) or stacked area chart (historical)
|
|
7
|
+
* with smooth animated transitions between modes. Delegates rendering to MorphChart
|
|
8
|
+
* while managing mode logic, data transformation, and user interactions.
|
|
9
|
+
*/
|
|
3
10
|
export declare const ChronoChart: FunctionComponent<ChronoChartProps>;
|
|
@@ -1,33 +1,62 @@
|
|
|
1
1
|
import { TimelineEvent } from '../timeline-chart/timeline-chart.types';
|
|
2
|
+
/**
|
|
3
|
+
* Display mode for the ChronoChart.
|
|
4
|
+
* - 'point-in-time': Shows a snapshot at a specific moment
|
|
5
|
+
* - 'historical': Shows trends over time
|
|
6
|
+
*/
|
|
2
7
|
export type ChronoMode = 'point-in-time' | 'historical';
|
|
8
|
+
/**
|
|
9
|
+
* Represents a data category with visual styling and optional parliament-specific mappings.
|
|
10
|
+
*/
|
|
3
11
|
export type ChronoCategory = {
|
|
12
|
+
/** Unique identifier for the category */
|
|
4
13
|
id: string;
|
|
14
|
+
/** Display label for the category */
|
|
5
15
|
label: string;
|
|
16
|
+
/** Color hex code (e.g., '#FF5733') */
|
|
6
17
|
color: string;
|
|
7
|
-
/** Optional dash pattern for the stroke (e.g., '5 5' for dashed lines
|
|
18
|
+
/** Optional dash pattern for the stroke (e.g., '5 5' for dashed lines) */
|
|
8
19
|
strokeDasharray?: string;
|
|
20
|
+
/** Parliament-specific configuration for special category rendering */
|
|
9
21
|
parliamentMapping?: {
|
|
22
|
+
/** Whether this category receives special visual treatment in parliament view */
|
|
10
23
|
isSpecialCategory: boolean;
|
|
24
|
+
/** Hatch pattern identifier for special categories */
|
|
11
25
|
hatchPattern?: string;
|
|
12
26
|
};
|
|
13
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Complete dataset for the ChronoChart including time series, categories, and metadata.
|
|
30
|
+
*/
|
|
14
31
|
export type ChronoData = {
|
|
32
|
+
/** Array of time-series data points, each containing a timestamp and category values */
|
|
15
33
|
timeSeries: Array<{
|
|
34
|
+
/** Unix timestamp or date number */
|
|
16
35
|
timestamp: number;
|
|
36
|
+
/** Map of category IDs to their numeric values at this timestamp */
|
|
17
37
|
values: Record<string, number>;
|
|
18
38
|
}>;
|
|
39
|
+
/** Category definitions with styling and metadata */
|
|
19
40
|
categories: ChronoCategory[];
|
|
41
|
+
/** Aggregate information about the dataset */
|
|
20
42
|
metadata: {
|
|
43
|
+
/** Total count across all categories */
|
|
21
44
|
total: number;
|
|
45
|
+
/** Counts for special categories (optional) */
|
|
22
46
|
specialCounts?: {
|
|
23
47
|
notApplicable: number;
|
|
24
48
|
noLst: number;
|
|
25
49
|
unavailable: number;
|
|
26
50
|
};
|
|
27
51
|
};
|
|
52
|
+
/** Optional timeline events to display alongside the historical view */
|
|
28
53
|
events?: TimelineEvent[];
|
|
29
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Props for the ChronoChart component.
|
|
57
|
+
*/
|
|
30
58
|
export type ChronoChartProps = {
|
|
59
|
+
/** Complete dataset to visualize */
|
|
31
60
|
data: ChronoData;
|
|
32
61
|
/**
|
|
33
62
|
* Index of the time series data point to display in point-in-time mode.
|
|
@@ -39,18 +68,31 @@ export type ChronoChartProps = {
|
|
|
39
68
|
* plays when toggling between point-in-time and historical modes.
|
|
40
69
|
*/
|
|
41
70
|
selectedIndex?: number;
|
|
71
|
+
/** Chart title displayed at the top */
|
|
42
72
|
title?: string;
|
|
73
|
+
/** Chart subtitle displayed below the title */
|
|
43
74
|
subtitle?: string;
|
|
75
|
+
/** Whether to show the title/subtitle header section */
|
|
44
76
|
showHeader?: boolean;
|
|
77
|
+
/** Chart width in pixels (default: 900) */
|
|
45
78
|
width?: number;
|
|
79
|
+
/** Chart height in pixels (default: 600) */
|
|
46
80
|
height?: number;
|
|
81
|
+
/** Initial time range for the historical view [startTimestamp, endTimestamp] */
|
|
47
82
|
timeRange?: [number, number];
|
|
83
|
+
/** Callback when the time range changes (via brush or other interactions) */
|
|
48
84
|
onTimeRangeChange?: (range: [number, number]) => void;
|
|
85
|
+
/** Enable brush selection for time range in historical mode */
|
|
49
86
|
enableBrush?: boolean;
|
|
87
|
+
/** Show the legend/category table */
|
|
50
88
|
showLegend?: boolean;
|
|
89
|
+
/** Enable tooltips on hover */
|
|
51
90
|
showTooltip?: boolean;
|
|
91
|
+
/** Show background grid lines */
|
|
52
92
|
showGrid?: boolean;
|
|
93
|
+
/** Enable animations when switching between modes */
|
|
53
94
|
enableAnimation?: boolean;
|
|
95
|
+
/** Duration of animations in milliseconds */
|
|
54
96
|
animationDuration?: number;
|
|
55
97
|
/**
|
|
56
98
|
* Disable the timeline in historical mode.
|
|
@@ -59,9 +101,14 @@ export type ChronoChartProps = {
|
|
|
59
101
|
* @default false
|
|
60
102
|
*/
|
|
61
103
|
disableTimeline?: boolean;
|
|
104
|
+
/** Custom function to format timestamps for display */
|
|
62
105
|
formatDate?: (timestamp: number) => string;
|
|
106
|
+
/** Custom function to format numeric values for display */
|
|
63
107
|
formatValue?: (value: number) => string;
|
|
108
|
+
/** Callback when animation state changes (starting/stopping) */
|
|
64
109
|
onAnimationStateChange?: (isAnimating: boolean) => void;
|
|
110
|
+
/** Callback when the GSAP timeline is initialized */
|
|
65
111
|
onTimelineReady?: (timeline: unknown) => void;
|
|
112
|
+
/** Callback for animation progress updates */
|
|
66
113
|
onAnimationProgress?: (progress: number, stage: string) => void;
|
|
67
114
|
};
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import { FunctionComponent } from 'react';
|
|
2
2
|
import { CategoryTableProps } from './category-table.types';
|
|
3
|
+
/**
|
|
4
|
+
* CategoryTable displays a legend/data table for ChronoChart categories.
|
|
5
|
+
*
|
|
6
|
+
* The table adapts its columns based on the display mode:
|
|
7
|
+
* - Point-in-time: Shows current count and percentage for each category
|
|
8
|
+
* - Historical: Shows last value, maximum, and minimum across the time series
|
|
9
|
+
*
|
|
10
|
+
* Special categories (N/A, Data Missing, No LST) use hatch patterns instead of
|
|
11
|
+
* solid colors to distinguish them from regular data categories.
|
|
12
|
+
*/
|
|
3
13
|
export declare const CategoryTable: FunctionComponent<CategoryTableProps>;
|
|
@@ -1,17 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the CategoryTable component.
|
|
3
|
+
*
|
|
4
|
+
* The CategoryTable displays a legend/data table showing all categories with their
|
|
5
|
+
* values, percentages, and colors. It supports both point-in-time and historical modes.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Represents a single category row in the table.
|
|
9
|
+
*/
|
|
1
10
|
export type CategoryTableItem = {
|
|
11
|
+
/** Unique identifier for the category */
|
|
2
12
|
id: string;
|
|
13
|
+
/** Display label for the category */
|
|
3
14
|
label: string;
|
|
15
|
+
/** Current value: "#" column in point-in-time mode OR "Last" column in historical mode */
|
|
4
16
|
value: number;
|
|
17
|
+
/** Percentage of total (displayed in point-in-time mode only) */
|
|
5
18
|
percentage: number;
|
|
19
|
+
/** Color hex code for the category's visual representation */
|
|
6
20
|
color: string;
|
|
21
|
+
/** Optional tooltip text providing additional context */
|
|
7
22
|
tooltip?: string;
|
|
23
|
+
/** Maximum value across time series (historical mode only) */
|
|
8
24
|
max?: number;
|
|
25
|
+
/** Minimum value across time series (historical mode only) */
|
|
9
26
|
min?: number;
|
|
10
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Props for the CategoryTable component.
|
|
30
|
+
*/
|
|
11
31
|
export type CategoryTableProps = {
|
|
32
|
+
/** Array of category items to display in the table */
|
|
12
33
|
categories: CategoryTableItem[];
|
|
34
|
+
/** Currently hovered category ID for highlighting, or null if none */
|
|
13
35
|
activeCategory: string | null;
|
|
36
|
+
/** Callback when user hovers over or leaves a category row */
|
|
14
37
|
onCategoryHover: (categoryId: string | null) => void;
|
|
38
|
+
/** Display mode affecting which columns are shown */
|
|
15
39
|
mode: 'point-in-time' | 'historical';
|
|
40
|
+
/** Whether the table is visible (affects opacity/display) */
|
|
16
41
|
visible?: boolean;
|
|
17
42
|
};
|
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
import { MorphChartCategory, MorphChartDataPoint } from '../../morph-chart/morph-chart.types';
|
|
2
2
|
import { ChronoData } from '../chrono-chart.types';
|
|
3
3
|
import { CategoryTableItem } from '../components/category-table.types';
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the latest values from ChronoData and formats them for display in the category table.
|
|
6
|
+
*
|
|
7
|
+
* In point-in-time mode: Returns the latest value for each category
|
|
8
|
+
* In historical mode: Also includes min/max values across the time series
|
|
9
|
+
*
|
|
10
|
+
* @param data - The complete ChronoData dataset
|
|
11
|
+
* @param mode - Display mode affecting which values are included
|
|
12
|
+
* @returns Array of formatted category items with values, percentages, and metadata
|
|
13
|
+
*/
|
|
4
14
|
export declare const extractLatestValues: (data: ChronoData, mode?: "point-in-time" | "historical") => CategoryTableItem[];
|
|
15
|
+
/**
|
|
16
|
+
* Transforms ChronoData format to MorphChart format for visualization.
|
|
17
|
+
*
|
|
18
|
+
* This conversion is necessary because ChronoChart uses its own data structure
|
|
19
|
+
* but delegates rendering to the MorphChart component.
|
|
20
|
+
*
|
|
21
|
+
* @param data - The ChronoData to transform
|
|
22
|
+
* @returns Object containing MorphChart-compatible data points and categories
|
|
23
|
+
* @throws Error if no data points are available
|
|
24
|
+
*/
|
|
5
25
|
export declare const transformToMorphChart: (data: ChronoData) => {
|
|
6
26
|
data: MorphChartDataPoint[];
|
|
7
27
|
categories: MorphChartCategory[];
|
|
8
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Validates the structure and integrity of ChronoData.
|
|
31
|
+
*
|
|
32
|
+
* Checks for:
|
|
33
|
+
* - Non-empty time series and categories
|
|
34
|
+
* - Required metadata fields
|
|
35
|
+
* - Proper timestamp ordering (ascending)
|
|
36
|
+
* - Category presence in data points
|
|
37
|
+
*
|
|
38
|
+
* @param data - The ChronoData to validate
|
|
39
|
+
* @throws Error with descriptive message if validation fails
|
|
40
|
+
*/
|
|
9
41
|
export declare const validateChronoData: (data: ChronoData) => void;
|
|
@@ -3,11 +3,12 @@ import { StageDurations } from './types';
|
|
|
3
3
|
* Calculate stage durations from total animation duration
|
|
4
4
|
*
|
|
5
5
|
* Splits the total duration into proportional stages for smooth timing:
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
6
|
+
* - 20% Seats transform to zig-zag lines (accordion shape)
|
|
7
|
+
* - 7% Lines collapse into vertical bar
|
|
8
|
+
* - 0.3% Pause to appreciate seat-to-bar transition
|
|
9
|
+
* - 22% Bar slide to timeline + axes appear
|
|
10
|
+
* - 22% Other bars grow from baseline
|
|
11
|
+
* - 28.7% Bars morph to curved areas
|
|
11
12
|
*
|
|
12
13
|
* @param totalDuration - Total animation duration in milliseconds
|
|
13
14
|
* @returns Object with duration for each stage in milliseconds
|
|
@@ -16,11 +17,12 @@ import { StageDurations } from './types';
|
|
|
16
17
|
* ```ts
|
|
17
18
|
* const durations = calculateStageDurations(2000);
|
|
18
19
|
* // {
|
|
19
|
-
* //
|
|
20
|
-
* //
|
|
21
|
-
* //
|
|
22
|
-
* //
|
|
23
|
-
* //
|
|
20
|
+
* // seatsToLines: 400, // 20%
|
|
21
|
+
* // seatsToBar: 140, // 7%
|
|
22
|
+
* // crossFadePause: 6, // 0.3%
|
|
23
|
+
* // barSlideToTimeline: 440, // 22%
|
|
24
|
+
* // axesAndBarsGrow: 440, // 22%
|
|
25
|
+
* // barsToArea: 574 // 28.7%
|
|
24
26
|
* // }
|
|
25
27
|
* ```
|
|
26
28
|
*/
|
|
@@ -5,18 +5,20 @@
|
|
|
5
5
|
* timing and duration calculations.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
|
-
* Animation stage durations for parliament → area morph (
|
|
8
|
+
* Animation stage durations for parliament → area morph (5 stages)
|
|
9
9
|
* Calculated as fractions of total duration for smooth timing
|
|
10
10
|
*/
|
|
11
11
|
export type StageDurations = {
|
|
12
|
-
/** Frame 1
|
|
12
|
+
/** Frame 0→1: Seats transform to zig-zag lines forming accordion (20% of total) */
|
|
13
|
+
seatsToLines: number;
|
|
14
|
+
/** Frame 1→2: Lines collapse into vertical bar (15% of total) */
|
|
13
15
|
seatsToBar: number;
|
|
14
16
|
/** Pause after cross-fade to appreciate seat-to-bar transition (3% of total) */
|
|
15
17
|
crossFadePause: number;
|
|
16
|
-
/** Frame 2→3: Bar slides to timeline + axes/grid appear (
|
|
18
|
+
/** Frame 2→3: Bar slides to timeline + axes/grid appear (22% of total) */
|
|
17
19
|
barSlideToTimeline: number;
|
|
18
|
-
/** Frame 3→4: Other bars grow from baseline (
|
|
20
|
+
/** Frame 3→4: Other bars grow from baseline (22% of total) */
|
|
19
21
|
axesAndBarsGrow: number;
|
|
20
|
-
/** Frame 4→5: Bars morph to curved area (
|
|
22
|
+
/** Frame 4→5: Bars morph to curved area (18% of total) */
|
|
21
23
|
barsToArea: number;
|
|
22
24
|
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for generating accordion-style zig-zag lines
|
|
3
|
+
* Used in the seats-to-lines animation stage
|
|
4
|
+
*/
|
|
5
|
+
export interface AccordionLinePoint {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
category: string;
|
|
9
|
+
categoryIndex: number;
|
|
10
|
+
}
|
|
11
|
+
export interface AccordionLineConfig {
|
|
12
|
+
centerX: number;
|
|
13
|
+
centerY: number;
|
|
14
|
+
radius: number;
|
|
15
|
+
startAngle: number;
|
|
16
|
+
endAngle: number;
|
|
17
|
+
amplitude: number;
|
|
18
|
+
segments: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate zig-zag line points for a category's accordion section
|
|
22
|
+
*
|
|
23
|
+
* @param config - Configuration for the accordion line
|
|
24
|
+
* @returns Array of points forming the zig-zag pattern
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateAccordionLine(config: AccordionLineConfig): AccordionLinePoint[];
|
|
27
|
+
/**
|
|
28
|
+
* Generate accordion lines for all categories
|
|
29
|
+
*
|
|
30
|
+
* @param categories - Array of categories with their seat counts
|
|
31
|
+
* @param parliamentCenter - Center point of the parliament
|
|
32
|
+
* @param radius - Base radius of the parliament arc
|
|
33
|
+
* @param arcAngle - Total angle of the parliament arc (in radians)
|
|
34
|
+
* @returns Map of category to accordion line points
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateCategoryAccordionLines(categories: Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
seatCount: number;
|
|
39
|
+
color: string;
|
|
40
|
+
}>, parliamentCenter: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}, radius: number, arcAngle?: number): Map<string, AccordionLinePoint[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Generate a single continuous accordion line for all categories
|
|
46
|
+
* Creates one connected slinky that spans the entire parliament arc
|
|
47
|
+
*
|
|
48
|
+
* @param categories - Array of categories with their seat counts
|
|
49
|
+
* @param parliamentCenter - Center point of the parliament
|
|
50
|
+
* @param radius - Base radius of the parliament arc
|
|
51
|
+
* @param arcAngle - Total angle of the parliament arc (in radians)
|
|
52
|
+
* @returns Single array of accordion line points with category info
|
|
53
|
+
*/
|
|
54
|
+
export declare function generateContinuousAccordionLine(categories: Array<{
|
|
55
|
+
name: string;
|
|
56
|
+
seatCount: number;
|
|
57
|
+
color: string;
|
|
58
|
+
}>, parliamentCenter: {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
}, radius: number, arcAngle?: number): AccordionLinePoint[];
|
|
62
|
+
/**
|
|
63
|
+
* Create SVG path string from accordion line points
|
|
64
|
+
*
|
|
65
|
+
* @param points - Array of accordion line points
|
|
66
|
+
* @param curved - Whether to use curved lines (true) or sharp zig-zags (false)
|
|
67
|
+
* @returns SVG path string
|
|
68
|
+
*/
|
|
69
|
+
export declare function createAccordionPath(points: AccordionLinePoint[], curved?: boolean): string;
|
|
70
|
+
/**
|
|
71
|
+
* Calculate intermediate positions for accordion collapse animation with slinky physics
|
|
72
|
+
*
|
|
73
|
+
* @param linePoints - Original accordion line points
|
|
74
|
+
* @param targetX - Target X position (vertical bar location)
|
|
75
|
+
* @param targetY - Target Y position (vertical bar location)
|
|
76
|
+
* @param progress - Animation progress (0-1)
|
|
77
|
+
* @returns Interpolated points for current progress with wave motion
|
|
78
|
+
*/
|
|
79
|
+
export declare function interpolateAccordionCollapse(linePoints: AccordionLinePoint[], targetX: number, targetY: number, progress: number): AccordionLinePoint[];
|
|
80
|
+
/**
|
|
81
|
+
* Generate multiple depth layers for slinky 3D effect
|
|
82
|
+
*
|
|
83
|
+
* @param config - Base configuration for accordion line
|
|
84
|
+
* @param numLayers - Number of layers to create (default 3)
|
|
85
|
+
* @returns Array of accordion line configurations with depth properties
|
|
86
|
+
*/
|
|
87
|
+
export declare function generateLayeredAccordionLines(config: AccordionLineConfig, numLayers?: number): Array<{
|
|
88
|
+
points: AccordionLinePoint[];
|
|
89
|
+
depth: number;
|
|
90
|
+
opacity: number;
|
|
91
|
+
strokeWidth: number;
|
|
92
|
+
blur: number;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Generate a single vertical line path for collapsed state
|
|
96
|
+
*
|
|
97
|
+
* @param x - X position of the vertical line
|
|
98
|
+
* @param topY - Top Y position
|
|
99
|
+
* @param bottomY - Bottom Y position
|
|
100
|
+
* @returns SVG path string for vertical line
|
|
101
|
+
*/
|
|
102
|
+
export declare function createVerticalLinePath(x: number, topY: number, bottomY: number): string;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Selection } from 'd3-selection';
|
|
2
2
|
import { MorphChartCategory } from '../morph-chart.types';
|
|
3
3
|
interface AnimationDurations {
|
|
4
|
-
|
|
4
|
+
seatsToRings: number;
|
|
5
|
+
ringsToBar: number;
|
|
5
6
|
axesReveal: number;
|
|
6
7
|
barSlide: number;
|
|
7
8
|
barsGrow: number;
|
|
8
9
|
barsToArea: number;
|
|
10
|
+
ringsHold?: number;
|
|
9
11
|
}
|
|
10
12
|
interface AnimationCallbacks {
|
|
11
13
|
onStageComplete?: (stage: string) => void;
|
|
@@ -34,39 +36,76 @@ export declare class GSAPOrchestrator {
|
|
|
34
36
|
private durations;
|
|
35
37
|
constructor(durations: AnimationDurations, callbacks?: AnimationCallbacks);
|
|
36
38
|
/**
|
|
37
|
-
* Stage
|
|
39
|
+
* Stage 1a: Animate parliament seats transforming into slinky rings
|
|
38
40
|
*
|
|
39
|
-
* Transforms the circular parliament layout into a
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* Transforms the circular parliament layout into a slinky (spring coil) formation.
|
|
42
|
+
* This creates a whimsical intermediate state between the circular parliament
|
|
43
|
+
* and the stacked bar, making the transition more visually engaging.
|
|
42
44
|
*
|
|
43
|
-
* @param seats - D3 selection of parliament seat
|
|
44
|
-
* @param
|
|
45
|
+
* @param seats - D3 selection of parliament seat elements to animate
|
|
46
|
+
* @param baselineY - Y-coordinate of the chart baseline (reference for positioning)
|
|
45
47
|
* @returns this - Returns the orchestrator instance for method chaining
|
|
46
48
|
*
|
|
47
49
|
* @remarks
|
|
48
50
|
* **Visual transformation:**
|
|
49
|
-
* - Parliament seats (circular arc) →
|
|
50
|
-
* -
|
|
51
|
-
* -
|
|
51
|
+
* - Parliament seats (circular arc) → Slinky rings (elliptical coils)
|
|
52
|
+
* - Seats morph and fade into overlapping rings
|
|
53
|
+
* - Rings positioned along the parliament arc initially
|
|
54
|
+
* - Uses perspective effects for 3D slinky appearance
|
|
52
55
|
*
|
|
53
56
|
* **Data preservation:**
|
|
54
57
|
* - Original seat positions stored in data-orig-* attributes for potential reversal
|
|
55
|
-
* -
|
|
58
|
+
* - Current dimensions preserved before transformation
|
|
56
59
|
*
|
|
57
60
|
* **Timeline integration:**
|
|
58
|
-
* - Added at '
|
|
59
|
-
* - Duration:
|
|
61
|
+
* - Added at 'seatsToRings' label (first stage)
|
|
62
|
+
* - Duration: Set by durations.seatsToRings
|
|
60
63
|
* - Triggers onStageComplete callback when starting
|
|
61
64
|
*
|
|
62
65
|
* @example
|
|
63
66
|
* ```ts
|
|
64
67
|
* const seats = svg.selectAll('.parliament-seat');
|
|
65
|
-
* const
|
|
66
|
-
* orchestrator.
|
|
68
|
+
* const baselineY = height - margin.bottom;
|
|
69
|
+
* orchestrator.animateSeatsToRings(seats, baselineY);
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
animateSeatsToRings(seats: Selection<SVGRectElement, unknown, null, undefined>, baselineY: number): GSAPOrchestrator;
|
|
73
|
+
/**
|
|
74
|
+
* Stage 1b: Animate slinky rings collapsing into stacked bar
|
|
75
|
+
*
|
|
76
|
+
* Transforms the slinky rings created in Stage 1a into a stacked
|
|
77
|
+
* vertical bar. Rings compress and stack vertically to form rectangular
|
|
78
|
+
* segments of the final bar, maintaining visual continuity.
|
|
79
|
+
*
|
|
80
|
+
* @param seats - D3 selection of ring elements (formerly seats) to animate
|
|
81
|
+
* @param targetPositions - Array of target positions for each segment, where each position defines the final rectangle geometry (x, y, width, height)
|
|
82
|
+
* @returns this - Returns the orchestrator instance for method chaining
|
|
83
|
+
*
|
|
84
|
+
* @remarks
|
|
85
|
+
* **Visual transformation:**
|
|
86
|
+
* - Slinky rings → Stacked bar segments (rectangles)
|
|
87
|
+
* - Rings compress and flatten into bar segments
|
|
88
|
+
* - Reposition to stack vertically in correct order
|
|
89
|
+
* - All segments animate simultaneously (no stagger)
|
|
90
|
+
* - Uses power2.inOut easing for smooth acceleration/deceleration
|
|
91
|
+
*
|
|
92
|
+
* **Data requirements:**
|
|
93
|
+
* - Target positions must match ring count (indexed by array position)
|
|
94
|
+
* - Each position defines final x, y, width, height for the bar segment
|
|
95
|
+
*
|
|
96
|
+
* **Timeline integration:**
|
|
97
|
+
* - Added at 'ringsToBar' label (after seatsToRings)
|
|
98
|
+
* - Duration: Set by durations.ringsToBar
|
|
99
|
+
* - Triggers onStageComplete callback when starting
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* const rings = svg.selectAll('.slinky-ring');
|
|
104
|
+
* const targets = calculateBarPositions(rings);
|
|
105
|
+
* orchestrator.animateRingsToBar(rings, targets);
|
|
67
106
|
* ```
|
|
68
107
|
*/
|
|
69
|
-
|
|
108
|
+
animateRingsToBar(seats: Selection<SVGRectElement, unknown, null, undefined>, targetPositions: Array<{
|
|
70
109
|
x: number;
|
|
71
110
|
y: number;
|
|
72
111
|
width: number;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Selection } from 'd3-selection';
|
|
2
|
+
export interface Slinky3DConfig {
|
|
3
|
+
categories: Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
color: string;
|
|
6
|
+
seatCount: number;
|
|
7
|
+
}>;
|
|
8
|
+
centerX: number;
|
|
9
|
+
centerY: number;
|
|
10
|
+
radius: number;
|
|
11
|
+
arcAngle: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate HTML content for 3D slinky rings
|
|
15
|
+
*/
|
|
16
|
+
export declare function generate3DSlinkyHTML(categories: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
color: string;
|
|
19
|
+
seatCount: number;
|
|
20
|
+
}>): string;
|
|
21
|
+
/**
|
|
22
|
+
* Generate CSS styles for 3D slinky effect
|
|
23
|
+
*/
|
|
24
|
+
export declare function generate3DSlinkyStyles(instanceId: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Create 3D slinky elements in SVG using foreignObject
|
|
27
|
+
*/
|
|
28
|
+
export declare function create3DSlinky(container: Selection<SVGGElement, unknown, null, undefined>, config: Slinky3DConfig, instanceId: string): {
|
|
29
|
+
wrapper: Selection<SVGForeignObjectElement, unknown, null, undefined>;
|
|
30
|
+
cleanup: () => void;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Animate 3D slinky compression using GSAP
|
|
34
|
+
*/
|
|
35
|
+
export declare function animate3DSlinkyCompression(instanceId: string, timeline: gsap.core.Timeline, startTime: string, duration: number, targetX: number, targetY: number): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Selection } from 'd3-selection';
|
|
2
|
+
export interface SvgSlinkyConfig {
|
|
3
|
+
categories: Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
color: string;
|
|
6
|
+
seatCount: number;
|
|
7
|
+
}>;
|
|
8
|
+
centerX: number;
|
|
9
|
+
centerY: number;
|
|
10
|
+
radius: number;
|
|
11
|
+
innerRadius: number;
|
|
12
|
+
arcAngle: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create SVG slinky coils
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSvgSlinky(container: Selection<SVGGElement, unknown, null, undefined>, config: SvgSlinkyConfig, instanceId: string): {
|
|
18
|
+
slinkyGroup: Selection<SVGGElement, unknown, null, undefined>;
|
|
19
|
+
cleanup: () => void;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Animate SVG slinky closing - stacks rings vertically into a bar on the right
|
|
23
|
+
* Uses staggered animation phases to create realistic slinky walking effect
|
|
24
|
+
*/
|
|
25
|
+
export declare function animateSvgSlinkyCompression(slinkyGroup: Selection<SVGGElement, unknown, null, undefined>, timeline: gsap.core.Timeline, startTime: string, duration: number, targetX: number, targetY: number): void;
|
|
@@ -15,6 +15,8 @@ export type TimelineSelection = {
|
|
|
15
15
|
start: number | null;
|
|
16
16
|
end: number | null;
|
|
17
17
|
};
|
|
18
|
+
export type DragHandleType = 'start' | 'end' | 'body' | null;
|
|
19
|
+
export type DragHandleVariant = 'arrow' | 'grip';
|
|
18
20
|
export type TimelineChartSlotProps = {
|
|
19
21
|
/** Props for the header container (Stack) */
|
|
20
22
|
header?: StackProps;
|
|
@@ -32,6 +34,10 @@ export type TimelineChartSlotProps = {
|
|
|
32
34
|
event?: BoxProps;
|
|
33
35
|
/** Props for the selection highlight overlay (Box) */
|
|
34
36
|
selection?: BoxProps;
|
|
37
|
+
/** Props for the start drag handle (Box) */
|
|
38
|
+
startHandle?: BoxProps;
|
|
39
|
+
/** Props for the end drag handle (Box) */
|
|
40
|
+
endHandle?: BoxProps;
|
|
35
41
|
/** Props for the hover tooltip container (Box) */
|
|
36
42
|
tooltip?: BoxProps;
|
|
37
43
|
/** Props for the tooltip event name Typography component */
|
|
@@ -65,6 +71,10 @@ export type TimelineChartProps = {
|
|
|
65
71
|
monthLabelCount?: number;
|
|
66
72
|
/** Enable zoom with mouse wheel */
|
|
67
73
|
enableZoom?: boolean;
|
|
74
|
+
/** Drag handle style variant (default: 'arrow') */
|
|
75
|
+
dragHandleVariant?: DragHandleVariant;
|
|
76
|
+
/** Color for drag handles (default: '#4B5563' for arrow, '#9CA3AF' for grip) */
|
|
77
|
+
dragHandleColor?: string;
|
|
68
78
|
/** Show header title and instructions (default: true) */
|
|
69
79
|
showHeader?: boolean;
|
|
70
80
|
/** Custom title for the header */
|
|
@@ -108,12 +118,14 @@ export type UseTimelineChartProps = {
|
|
|
108
118
|
export type UseTimelineChartReturn = {
|
|
109
119
|
isDragging: boolean;
|
|
110
120
|
dragStart: number | null;
|
|
121
|
+
dragHandleType: DragHandleType;
|
|
111
122
|
internalSelectedRange: TimelineSelection;
|
|
112
123
|
internalVisibleRange: TimelineSelection;
|
|
113
124
|
hoveredEvent: TimelineEvent | null;
|
|
114
125
|
minDate: number;
|
|
115
126
|
maxDate: number;
|
|
116
127
|
timeSpan: number;
|
|
128
|
+
timelineContainerRef: React.RefObject<HTMLDivElement>;
|
|
117
129
|
getPositionFromTimestamp: (timestamp: number) => number;
|
|
118
130
|
getTimestampFromPosition: (x: number, containerWidth: number) => number;
|
|
119
131
|
handleMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
@@ -121,9 +133,13 @@ export type UseTimelineChartReturn = {
|
|
|
121
133
|
handleMouseUp: () => void;
|
|
122
134
|
handleWheel: (e: React.WheelEvent<HTMLDivElement>) => void;
|
|
123
135
|
handleDoubleClick: () => void;
|
|
136
|
+
handleStartHandleMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
137
|
+
handleEndHandleMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
138
|
+
handleSelectionBodyMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
124
139
|
setHoveredEvent: (event: TimelineEvent | null) => void;
|
|
125
140
|
getEventsInRange: () => TimelineEvent[];
|
|
126
141
|
resetZoom: () => void;
|
|
142
|
+
showHandles: boolean;
|
|
127
143
|
};
|
|
128
144
|
export type TimelineSelectedEventsProps = {
|
|
129
145
|
/** Array of selected events to display */
|