chartifypdf 0.1.0 → 0.2.0
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/ContextMenu.d.ts +15 -0
- package/dist/components/LinePath.d.ts +3 -1
- package/dist/components/PeakMarkers.d.ts +11 -0
- package/dist/hooks/useDataDomain.d.ts +6 -0
- package/dist/hooks/useScales.d.ts +1 -1
- package/dist/hooks/useTooltip.d.ts +1 -0
- package/dist/hooks/useZoom.d.ts +4 -3
- package/dist/index.d.ts +32 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/chart.types.d.ts +18 -0
- package/dist/utils/math.d.ts +1 -0
- package/dist/utils/path.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { DataSeries, ContextMenuConfig } from "../types/chart.types";
|
|
3
|
+
interface ContextMenuProps {
|
|
4
|
+
visible: boolean;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
nearestX: number;
|
|
8
|
+
data: DataSeries[];
|
|
9
|
+
config?: ContextMenuConfig;
|
|
10
|
+
colorPalette?: string[];
|
|
11
|
+
getSeriesColor: (index: number) => string;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
}
|
|
14
|
+
export declare const ContextMenu: React.FC<ContextMenuProps>;
|
|
15
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type { DataSeries, DataPoint, AnimationConfig } from "../types/chart.types";
|
|
2
|
+
import type { DataSeries, DataPoint, AnimationConfig, CurveType } from "../types/chart.types";
|
|
3
3
|
interface LinePathProps {
|
|
4
4
|
series: DataSeries;
|
|
5
5
|
xScale: (v: number) => number;
|
|
@@ -7,6 +7,8 @@ interface LinePathProps {
|
|
|
7
7
|
color: string;
|
|
8
8
|
animation?: AnimationConfig;
|
|
9
9
|
onPointClick?: (point: DataPoint, series: DataSeries) => void;
|
|
10
|
+
curveType?: CurveType;
|
|
11
|
+
highlightOpacity?: number;
|
|
10
12
|
}
|
|
11
13
|
export declare const LinePath: React.FC<LinePathProps>;
|
|
12
14
|
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { PeakConfig, DataSeries } from "../types/chart.types";
|
|
3
|
+
interface PeakMarkersProps {
|
|
4
|
+
peaks: PeakConfig[];
|
|
5
|
+
data: DataSeries[];
|
|
6
|
+
xScale: (v: number) => number;
|
|
7
|
+
yScale: (v: number) => number;
|
|
8
|
+
colorPalette?: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const PeakMarkers: React.FC<PeakMarkersProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DataSeries, AxisConfig } from "../types/chart.types";
|
|
2
|
+
export interface DataDomain {
|
|
3
|
+
fullXDomain: [number, number];
|
|
4
|
+
fullYDomain: [number, number];
|
|
5
|
+
}
|
|
6
|
+
export declare function useDataDomain(data: DataSeries[], xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig): DataDomain;
|
|
@@ -7,4 +7,4 @@ export interface ScalesResult {
|
|
|
7
7
|
xDomain: [number, number];
|
|
8
8
|
yDomain: [number, number];
|
|
9
9
|
}
|
|
10
|
-
export declare function useScales(data: DataSeries[], plotWidth: number, plotHeight: number, xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig): ScalesResult;
|
|
10
|
+
export declare function useScales(data: DataSeries[], plotWidth: number, plotHeight: number, xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig, viewXDomain?: [number, number], viewYDomain?: [number, number]): ScalesResult;
|
package/dist/hooks/useZoom.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ZoomConfig } from "../types/chart.types";
|
|
2
2
|
export interface ZoomState {
|
|
3
|
+
viewXDomain: [number, number];
|
|
4
|
+
viewYDomain: [number, number];
|
|
3
5
|
scale: number;
|
|
4
|
-
offsetX: number;
|
|
5
|
-
offsetY: number;
|
|
6
6
|
isPanning: boolean;
|
|
7
|
+
showZoomHint: boolean;
|
|
7
8
|
zoomIn: () => void;
|
|
8
9
|
zoomOut: () => void;
|
|
9
10
|
resetZoom: () => void;
|
|
@@ -12,4 +13,4 @@ export interface ZoomState {
|
|
|
12
13
|
handlePanMove: (e: React.MouseEvent) => void;
|
|
13
14
|
handlePanEnd: () => void;
|
|
14
15
|
}
|
|
15
|
-
export declare function useZoom(config: ZoomConfig | undefined, plotWidth: number, plotHeight: number): ZoomState;
|
|
16
|
+
export declare function useZoom(config: ZoomConfig | undefined, fullXDomain: [number, number], fullYDomain: [number, number], plotWidth: number, plotHeight: number): ZoomState;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface DataPoint {
|
|
|
5
5
|
y: number;
|
|
6
6
|
label?: string;
|
|
7
7
|
}
|
|
8
|
+
type CurveType = "linear" | "monotone" | "natural";
|
|
8
9
|
interface DataSeries {
|
|
9
10
|
id: string;
|
|
10
11
|
name: string;
|
|
@@ -14,6 +15,7 @@ interface DataSeries {
|
|
|
14
15
|
strokeDasharray?: string;
|
|
15
16
|
showDots?: boolean;
|
|
16
17
|
dotRadius?: number;
|
|
18
|
+
curveType?: CurveType;
|
|
17
19
|
}
|
|
18
20
|
interface AxisConfig {
|
|
19
21
|
label?: string;
|
|
@@ -40,6 +42,7 @@ interface ZoomConfig {
|
|
|
40
42
|
controlsPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
41
43
|
enableWheel?: boolean;
|
|
42
44
|
enablePan?: boolean;
|
|
45
|
+
requireCtrlKey?: boolean;
|
|
43
46
|
}
|
|
44
47
|
interface ChartMargin {
|
|
45
48
|
top: number;
|
|
@@ -59,6 +62,18 @@ interface AnimationConfig {
|
|
|
59
62
|
duration?: number;
|
|
60
63
|
easing?: string;
|
|
61
64
|
}
|
|
65
|
+
interface PeakConfig {
|
|
66
|
+
x: number;
|
|
67
|
+
label?: string;
|
|
68
|
+
color?: string;
|
|
69
|
+
icon?: "arrow" | "diamond" | "star";
|
|
70
|
+
}
|
|
71
|
+
interface ContextMenuConfig {
|
|
72
|
+
enabled?: boolean;
|
|
73
|
+
backgroundColor?: string;
|
|
74
|
+
textColor?: string;
|
|
75
|
+
borderColor?: string;
|
|
76
|
+
}
|
|
62
77
|
interface LineChartProps {
|
|
63
78
|
data: DataSeries[];
|
|
64
79
|
width?: number;
|
|
@@ -74,6 +89,9 @@ interface LineChartProps {
|
|
|
74
89
|
className?: string;
|
|
75
90
|
onPointClick?: (point: DataPoint, series: DataSeries) => void;
|
|
76
91
|
ariaLabel?: string;
|
|
92
|
+
curveType?: CurveType;
|
|
93
|
+
peaks?: PeakConfig[];
|
|
94
|
+
contextMenu?: ContextMenuConfig;
|
|
77
95
|
}
|
|
78
96
|
|
|
79
97
|
declare const LineChart: React$1.FC<LineChartProps>;
|
|
@@ -83,6 +101,12 @@ declare function useChartDimensions(containerRef: RefObject<HTMLDivElement | nul
|
|
|
83
101
|
height: number;
|
|
84
102
|
};
|
|
85
103
|
|
|
104
|
+
interface DataDomain {
|
|
105
|
+
fullXDomain: [number, number];
|
|
106
|
+
fullYDomain: [number, number];
|
|
107
|
+
}
|
|
108
|
+
declare function useDataDomain(data: DataSeries[], xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig): DataDomain;
|
|
109
|
+
|
|
86
110
|
interface ScalesResult {
|
|
87
111
|
xScale: (value: number) => number;
|
|
88
112
|
yScale: (value: number) => number;
|
|
@@ -91,13 +115,14 @@ interface ScalesResult {
|
|
|
91
115
|
xDomain: [number, number];
|
|
92
116
|
yDomain: [number, number];
|
|
93
117
|
}
|
|
94
|
-
declare function useScales(data: DataSeries[], plotWidth: number, plotHeight: number, xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig): ScalesResult;
|
|
118
|
+
declare function useScales(data: DataSeries[], plotWidth: number, plotHeight: number, xAxisConfig?: AxisConfig, yAxisConfig?: AxisConfig, viewXDomain?: [number, number], viewYDomain?: [number, number]): ScalesResult;
|
|
95
119
|
|
|
96
120
|
interface ZoomState {
|
|
121
|
+
viewXDomain: [number, number];
|
|
122
|
+
viewYDomain: [number, number];
|
|
97
123
|
scale: number;
|
|
98
|
-
offsetX: number;
|
|
99
|
-
offsetY: number;
|
|
100
124
|
isPanning: boolean;
|
|
125
|
+
showZoomHint: boolean;
|
|
101
126
|
zoomIn: () => void;
|
|
102
127
|
zoomOut: () => void;
|
|
103
128
|
resetZoom: () => void;
|
|
@@ -106,7 +131,7 @@ interface ZoomState {
|
|
|
106
131
|
handlePanMove: (e: React.MouseEvent) => void;
|
|
107
132
|
handlePanEnd: () => void;
|
|
108
133
|
}
|
|
109
|
-
declare function useZoom(config: ZoomConfig | undefined, plotWidth: number, plotHeight: number): ZoomState;
|
|
134
|
+
declare function useZoom(config: ZoomConfig | undefined, fullXDomain: [number, number], fullYDomain: [number, number], plotWidth: number, plotHeight: number): ZoomState;
|
|
110
135
|
|
|
111
136
|
interface TooltipState {
|
|
112
137
|
tooltipVisible: boolean;
|
|
@@ -114,10 +139,11 @@ interface TooltipState {
|
|
|
114
139
|
tooltipY: number;
|
|
115
140
|
activePoint: DataPoint | null;
|
|
116
141
|
activeSeries: DataSeries | null;
|
|
142
|
+
activeSeriesId: string | null;
|
|
117
143
|
handleMouseMove: (e: React.MouseEvent) => void;
|
|
118
144
|
handleMouseLeave: () => void;
|
|
119
145
|
}
|
|
120
146
|
declare function useTooltip(svgRef: RefObject<SVGSVGElement | null>, data: DataSeries[], xScale: (v: number) => number, yScale: (v: number) => number, marginLeft: number, marginTop: number, plotWidth: number, _config?: TooltipConfig): TooltipState;
|
|
121
147
|
|
|
122
|
-
export { LineChart, useChartDimensions, useScales, useTooltip, useZoom };
|
|
123
|
-
export type { AnimationConfig, AxisConfig, ChartMargin, ChartStyle, DataPoint, DataSeries, LineChartProps, TooltipConfig, ZoomConfig };
|
|
148
|
+
export { LineChart, useChartDimensions, useDataDomain, useScales, useTooltip, useZoom };
|
|
149
|
+
export type { AnimationConfig, AxisConfig, ChartMargin, ChartStyle, ContextMenuConfig, CurveType, DataPoint, DataSeries, LineChartProps, PeakConfig, TooltipConfig, ZoomConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react/jsx-runtime"),t=require("react");const o=["#4e79a7","#f28e2b","#e15759","#76b7b2","#59a14f","#edc948","#b07aa1","#ff9da7","#9c755f","#bab0ac"];function n(e,t){const n=t??o;return n[e%n.length]}function i(e,o,n){const[i,s]=t.useState({width:o??0,height:n??0}),r=void 0!==o&&void 0!==n;return t.useLayoutEffect(()=>{if(r)return void s({width:o,height:n});const t=e.current;if(!t)return;const i=()=>{const e=t.getBoundingClientRect();s({width:e.width,height:e.height})};i();const a=new ResizeObserver(()=>{i()});return a.observe(t),()=>a.disconnect()},[e,r,o,n]),i}function s(e,t,o){return Math.min(Math.max(e,t),o)}function r(e,t){const o=Math.floor(Math.log10(e)),n=e/Math.pow(10,o);let i;return i=t?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10,i*Math.pow(10,o)}function a(e,t,o){if(e===t){const o=0===e?1:.1*Math.abs(e);e-=o,t+=o}const n=r(t-e,!1),i=r(n/(o-1),!0);return{niceMin:Math.floor(e/i)*i,niceMax:Math.ceil(t/i)*i,tickStep:i}}function l(e,t,o,n){return i=>{if(t===e)return(o+n)/2;const s=function(e,t,o){return e===t?0:(o-e)/(t-e)}(e,t,i);return function(e,t,o){return e+(t-e)*o}(o,n,s)}}function c(e,t,o){const n=[];for(let i=e;i<=t+.5*o;i+=o)n.push(parseFloat(i.toPrecision(12)));return n}function d(e,o,n,i,s){return t.useMemo(()=>{let t=1/0,r=-1/0,d=1/0,h=-1/0;for(const o of e)for(const e of o.data)e.x<t&&(t=e.x),e.x>r&&(r=e.x),e.y<d&&(d=e.y),e.y>h&&(h=e.y);isFinite(t)||(t=0,r=1,d=0,h=1),void 0!==i?.min&&(t=i.min),void 0!==i?.max&&(r=i.max),void 0!==s?.min&&(d=s.min),void 0!==s?.max&&(h=s.max);const u=i?.tickCount??6,f=s?.tickCount??6,x=a(t,r,u),m=a(d,h,f);return{xScale:l(x.niceMin,x.niceMax,0,o),yScale:l(m.niceMin,m.niceMax,n,0),xTicks:c(x.niceMin,x.niceMax,x.tickStep),yTicks:c(m.niceMin,m.niceMax,m.tickStep),xDomain:[x.niceMin,x.niceMax],yDomain:[m.niceMin,m.niceMax]}},[e,o,n,i,s])}function h(e,o,n){const i=e?.enabled??!1,r=e?.minScale??1,a=e?.maxScale??10,l=e?.step??.5,c=e?.enableWheel??!0,d=e?.enablePan??!0,[h,u]=t.useState(1),[f,x]=t.useState(0),[m,y]=t.useState(0),[g,p]=t.useState(!1),b=t.useRef({x:0,y:0,offsetX:0,offsetY:0}),k=t.useCallback((e,t,i)=>{const r=n*(i-1);return{x:s(e,-(o*(i-1)),0),y:s(t,-r,0)}},[o,n]),C=t.useCallback(()=>{i&&u(e=>{const t=s(e+l,r,a);return x(e=>k(e-o*l/2,0,t).x),y(e=>k(0,e-n*l/2,t).y),t})},[i,l,r,a,o,n,k]),S=t.useCallback(()=>{i&&u(e=>{const t=s(e-l,r,a);return x(e=>k(e+o*l/2,0,t).x),y(e=>k(0,e+n*l/2,t).y),t})},[i,l,r,a,o,n,k]),v=t.useCallback(()=>{u(1),x(0),y(0)},[]),M=t.useCallback(e=>{if(!i||!c)return;e.preventDefault();const t=e.currentTarget.getBoundingClientRect(),o=e.clientX-t.left,n=e.clientY-t.top,d=e.deltaY<0?l:-l;u(e=>{const t=s(e+d,r,a),i=t/e;return x(e=>k(o-i*(o-e),0,t).x),y(e=>k(0,n-i*(n-e),t).y),t})},[i,c,l,r,a,k]),j=t.useCallback(e=>{!i||!d||h<=1||0===e.button&&(p(!0),b.current={x:e.clientX,y:e.clientY,offsetX:f,offsetY:m})},[i,d,h,f,m]),w=t.useCallback(e=>{if(!g)return;const t=e.clientX-b.current.x,o=e.clientY-b.current.y,n=k(b.current.offsetX+t,b.current.offsetY+o,h);x(n.x),y(n.y)},[g,h,k]),$=t.useCallback(()=>{p(!1)},[]);return{scale:h,offsetX:f,offsetY:m,isPanning:g,zoomIn:C,zoomOut:S,resetZoom:v,handleWheel:M,handlePanStart:j,handlePanMove:w,handlePanEnd:$}}function u(e,t,o){if(0===e.length)return null;let n=0,i=e.length-1;for(;n<i;){const s=Math.floor((n+i)/2);o(e[s].x)<t?n=s+1:i=s}let s=e[n],r=Math.abs(o(s.x)-t);if(n>0){const i=Math.abs(o(e[n-1].x)-t);i<r&&(r=i,s=e[n-1])}return s}function f(e,o,n,i,s,r,a,l){const[c,d]=t.useState(!1),[h,f]=t.useState(0),[x,m]=t.useState(0),[y,g]=t.useState(null),[p,b]=t.useState(null);return{tooltipVisible:c,tooltipX:h,tooltipY:x,activePoint:y,activeSeries:p,handleMouseMove:t.useCallback(t=>{const r=e.current;if(!r)return;const l=r.getBoundingClientRect(),c=t.clientX-l.left-s;if(c<0||c>a)return void d(!1);let h=null,x=null,y=1/0;for(const e of o){const t=u(e.data,c,n);if(!t)continue;const o=Math.abs(n(t.x)-c);o<y&&(y=o,h=t,x=e)}h&&x&&(f(n(h.x)),m(i(h.y)),g(h),b(x),d(!0))},[e,o,n,i,s,a]),handleMouseLeave:t.useCallback(()=>{d(!1),g(null),b(null)},[])}}const x=e=>Math.abs(e)>=1e6?`${(e/1e6).toFixed(1)}M`:Math.abs(e)>=1e3?`${(e/1e3).toFixed(1)}K`:Number.isInteger(e)?e.toString():e.toFixed(1),m=t.memo(({xTicks:t,yTicks:o,xScale:n,yScale:i,plotWidth:s,plotHeight:r,xAxisConfig:a,yAxisConfig:l,style:c})=>{const d=c?.axisColor??"#333",h=c?.tickColor??"#666",u=c?.fontFamily??"sans-serif",f=c?.fontSize??11,m=a?.tickFormat??x,y=l?.tickFormat??x;return e.jsxs("g",{className:"chartifypdf-axes",children:[e.jsx("line",{x1:0,y1:r,x2:s,y2:r,stroke:d,strokeWidth:1}),t.map(t=>{const o=n(t);return e.jsxs("g",{children:[e.jsx("line",{x1:o,y1:r,x2:o,y2:r+6,stroke:d,strokeWidth:1}),e.jsx("text",{x:o,y:r+18,textAnchor:"middle",fill:h,fontFamily:u,fontSize:f,children:m(t)})]},`x-tick-${t}`)}),a?.label&&e.jsx("text",{x:s/2,y:r+38,textAnchor:"middle",fill:d,fontFamily:u,fontSize:f+1,fontWeight:"bold",children:a.label}),e.jsx("line",{x1:0,y1:0,x2:0,y2:r,stroke:d,strokeWidth:1}),o.map(t=>{const o=i(t);return e.jsxs("g",{children:[e.jsx("line",{x1:-6,y1:o,x2:0,y2:o,stroke:d,strokeWidth:1}),e.jsx("text",{x:-10,y:o,textAnchor:"end",dominantBaseline:"middle",fill:h,fontFamily:u,fontSize:f,children:y(t)})]},`y-tick-${t}`)}),l?.label&&e.jsx("text",{x:0,y:0,textAnchor:"middle",fill:d,fontFamily:u,fontSize:f+1,fontWeight:"bold",transform:`translate(-40, ${r/2}) rotate(-90)`,children:l.label})]})});m.displayName="Axes";const y=t.memo(({xTicks:t,yTicks:o,xScale:n,yScale:i,plotWidth:s,plotHeight:r,showXGrid:a,showYGrid:l,xGridColor:c="#e0e0e0",yGridColor:d="#e0e0e0"})=>e.jsxs("g",{className:"chartifypdf-grid",children:[l&&o.map(t=>{const o=i(t);return e.jsx("line",{x1:0,y1:o,x2:s,y2:o,stroke:d,strokeDasharray:"4 4",strokeOpacity:.5},`y-grid-${t}`)}),a&&t.map(t=>{const o=n(t);return e.jsx("line",{x1:o,y1:0,x2:o,y2:r,stroke:c,strokeDasharray:"4 4",strokeOpacity:.5},`x-grid-${t}`)})]}));y.displayName="GridLines";const g=t.memo(({series:o,xScale:n,yScale:i,color:s,animation:r,onPointClick:a})=>{const l=t.useRef(null),[c,d]=t.useState(0),[h,u]=t.useState(!r?.enabled),f=o.strokeWidth??2,x=o.showDots??!1,m=o.dotRadius??3.5,y=function(e){if(0===e.length)return"";const[t,...o]=e;return`M${t.x},${t.y}`+o.map(e=>`L${e.x},${e.y}`).join("")}(o.data.map(e=>({x:n(e.x),y:i(e.y)})));t.useEffect(()=>{if(r?.enabled&&l.current){const e=l.current.getTotalLength();d(e),u(!1);const t=setTimeout(()=>{u(!0)},r.duration??800);return()=>clearTimeout(t)}},[y,r?.enabled,r?.duration]);const g=r?.enabled&&c>0?{strokeDasharray:c,strokeDashoffset:h?0:c,transition:`stroke-dashoffset ${r.duration??800}ms ${r.easing??"ease-in-out"}`}:{};return e.jsxs("g",{className:"chartifypdf-line-path",children:[e.jsx("path",{ref:l,d:y,fill:"none",stroke:s,strokeWidth:f,strokeDasharray:o.strokeDasharray,strokeLinecap:"round",strokeLinejoin:"round",style:g}),x&&o.data.map((t,r)=>e.jsx("circle",{cx:n(t.x),cy:i(t.y),r:m,fill:s,stroke:"#fff",strokeWidth:1.5,style:{cursor:a?"pointer":"default"},onClick:a?()=>a(t,o):void 0},`dot-${o.id}-${r}`))]})});g.displayName="LinePath";const p=t.memo(({visible:t,x:o,y:n,point:i,series:s,plotHeight:r,plotWidth:a,config:l,seriesColor:c})=>{if(!t||!i||!s)return null;const d=l?.backgroundColor??"rgba(0, 0, 0, 0.8)",h=l?.textColor??"#fff";let u;if(l?.renderCustom)u=l.renderCustom(i,s);else{u=l?.formatter?l.formatter(i,s):`${s.name}: (${i.x}, ${i.y})`}let f=o+12,x=n-40-8;return f+140>a&&(f=o-140-12),x<0&&(x=n+12),e.jsxs("g",{className:"chartifypdf-tooltip",pointerEvents:"none",children:[e.jsx("line",{x1:o,y1:0,x2:o,y2:r,stroke:"#999",strokeWidth:1,strokeDasharray:"4 4",opacity:.6}),e.jsx("circle",{cx:o,cy:n,r:5,fill:c,stroke:"#fff",strokeWidth:2}),e.jsx("foreignObject",{x:f,y:x,width:140,height:60,overflow:"visible",children:e.jsx("div",{style:{backgroundColor:d,color:h,padding:"6px 10px",borderRadius:"4px",fontSize:"12px",fontFamily:"sans-serif",whiteSpace:"nowrap",lineHeight:1.4,boxShadow:"0 2px 6px rgba(0,0,0,0.2)"},children:u})})]})});p.displayName="Tooltip";const b=t.memo(({config:t,svgWidth:o,svgHeight:n,margin:i,scale:s,onZoomIn:r,onZoomOut:a,onReset:l})=>{if(!t?.enabled||!1===t.showControls)return null;const c=t.controlsPosition??"top-right",d=24;let h,u;switch(c){case"top-left":h=i.left+8,u=i.top+8;break;case"bottom-left":h=i.left+8,u=n-i.bottom-24-8;break;case"bottom-right":h=o-i.right-80-8,u=n-i.bottom-24-8;break;default:h=o-i.right-80-8,u=i.top+8}const f=s>1?[{label:"+",onClick:r},{label:"−",onClick:a},{label:"↺",onClick:l}]:[{label:"+",onClick:r},{label:"−",onClick:a}];return e.jsx("g",{className:"chartifypdf-zoom-controls",transform:`translate(${h}, ${u})`,children:f.map((t,o)=>e.jsxs("g",{transform:`translate(${28*o}, 0)`,onClick:t.onClick,style:{cursor:"pointer"},children:[e.jsx("rect",{width:d,height:d,rx:4,fill:"rgba(255,255,255,0.9)",stroke:"#ccc",strokeWidth:1}),e.jsx("text",{x:12,y:12,textAnchor:"middle",dominantBaseline:"central",fontSize:14,fontFamily:"sans-serif",fill:"#333",fontWeight:"bold",children:t.label})]},t.label))})});b.displayName="ZoomControls";const k={top:20,right:20,bottom:50,left:60},C=({data:o,width:s,height:r,margin:a,xAxis:l,yAxis:c,tooltip:u,zoom:x,style:C,animation:S,colorPalette:v,className:M,onPointClick:j,ariaLabel:w})=>{const $=t.useRef(null),W=t.useRef(null),P=t.useId(),L={...k,...a},T=i($,s,r??400),{width:N,height:z}=T,F=Math.max(0,N-L.left-L.right),D=Math.max(0,z-L.top-L.bottom),{xScale:X,yScale:Y,xTicks:A,yTicks:R}=d(o,F,D,l,c),G=h(x,F,D),H=!1!==u?.enabled,O=f(W,o,X,Y,L.left,L.top,F),Z=l?.gridLines??!1,I=c?.gridLines??!0;return 0===N||0===z?e.jsx("div",{ref:$,className:M,style:{width:s??"100%",height:r??400,backgroundColor:C?.backgroundColor}}):e.jsx("div",{ref:$,className:M,style:{width:s??"100%",height:r??400,backgroundColor:C?.backgroundColor},children:e.jsxs("svg",{ref:W,width:N,height:z,role:"img","aria-label":w??"Line chart",onMouseMove:H&&!G.isPanning?O.handleMouseMove:void 0,onMouseLeave:H?O.handleMouseLeave:void 0,onWheel:x?.enabled?G.handleWheel:void 0,onMouseDown:x?.enabled?G.handlePanStart:void 0,onMouseUp:x?.enabled?G.handlePanEnd:void 0,style:{userSelect:G.isPanning?"none":void 0,cursor:G.isPanning?"grabbing":x?.enabled&&G.scale>1?"grab":void 0},children:[e.jsx("defs",{children:e.jsx("clipPath",{id:P,children:e.jsx("rect",{width:F,height:D})})}),e.jsxs("g",{transform:`translate(${L.left}, ${L.top})`,children:[e.jsx(m,{xTicks:A,yTicks:R,xScale:X,yScale:Y,plotWidth:F,plotHeight:D,xAxisConfig:l,yAxisConfig:c,style:C}),e.jsx(y,{xTicks:A,yTicks:R,xScale:X,yScale:Y,plotWidth:F,plotHeight:D,showXGrid:Z,showYGrid:I,xGridColor:l?.gridLineColor,yGridColor:c?.gridLineColor}),e.jsx("g",{clipPath:`url(#${P})`,children:e.jsx("g",{transform:`translate(${G.offsetX}, ${G.offsetY}) scale(${G.scale})`,onMouseMove:x?.enabled?G.handlePanMove:void 0,children:o.map((t,o)=>e.jsx(g,{series:t,xScale:X,yScale:Y,color:t.color??n(o,v),animation:S,onPointClick:j},t.id))})}),H&&e.jsx(p,{visible:O.tooltipVisible,x:O.tooltipX,y:O.tooltipY,point:O.activePoint,series:O.activeSeries,plotHeight:D,plotWidth:F,config:u,seriesColor:O.activeSeries?.color??n(o.findIndex(e=>e.id===O.activeSeries?.id),v)})]}),e.jsx(b,{config:x,svgWidth:N,svgHeight:z,margin:L,scale:G.scale,onZoomIn:G.zoomIn,onZoomOut:G.zoomOut,onReset:G.resetZoom})]})})};C.displayName="LineChart",exports.LineChart=C,exports.useChartDimensions=i,exports.useScales=d,exports.useTooltip=f,exports.useZoom=h;
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),t=require("react");const n=["#4e79a7","#f28e2b","#e15759","#76b7b2","#59a14f","#edc948","#b07aa1","#ff9da7","#9c755f","#bab0ac"];function o(e,n,o){const[i,r]=t.useState({width:n??0,height:o??0}),s=void 0!==n&&void 0!==o;return t.useLayoutEffect(()=>{if(s)return void r({width:n,height:o});const t=e.current;if(!t)return;const i=()=>{const e=t.getBoundingClientRect();r({width:e.width,height:e.height})};i();const l=new ResizeObserver(()=>{i()});return l.observe(t),()=>l.disconnect()},[e,s,n,o]),i}function i(e,t,n){return Math.min(Math.max(e,t),n)}function r(e,t,n){return e+(t-e)*n}function s(e,t){const n=Math.floor(Math.log10(e)),o=e/Math.pow(10,n);let i;return i=t?o<1.5?1:o<3?2:o<7?5:10:o<=1?1:o<=2?2:o<=5?5:10,i*Math.pow(10,n)}function l(e,t,n){if(e===t){const n=0===e?1:.1*Math.abs(e);e-=n,t+=n}const o=s(t-e,!1),i=s(o/(n-1),!0);return{niceMin:Math.floor(e/i)*i,niceMax:Math.ceil(t/i)*i,tickStep:i}}function a(e,t,n,o){return i=>{if(t===e)return(n+o)/2;const s=function(e,t,n){return e===t?0:(n-e)/(t-e)}(e,t,i);return r(n,o,s)}}function c(e,t,n){const o=[];for(let i=e;i<=t+.5*n;i+=n)o.push(parseFloat(i.toPrecision(12)));return o}function d(e,n,o){return t.useMemo(()=>{let t=1/0,i=-1/0,r=1/0,s=-1/0;for(const n of e)for(const e of n.data)e.x<t&&(t=e.x),e.x>i&&(i=e.x),e.y<r&&(r=e.y),e.y>s&&(s=e.y);isFinite(t)||(t=0,i=1,r=0,s=1),void 0!==n?.min&&(t=n.min),void 0!==n?.max&&(i=n.max),void 0!==o?.min&&(r=o.min),void 0!==o?.max&&(s=o.max);const a=n?.tickCount??6,c=o?.tickCount??6,d=l(t,i,a),u=l(r,s,c);return{fullXDomain:[d.niceMin,d.niceMax],fullYDomain:[u.niceMin,u.niceMax]}},[e,n,o])}function u(e,n,o,i,r,s,d){return t.useMemo(()=>{let t=1/0,u=-1/0,x=1/0,f=-1/0;for(const n of e)for(const e of n.data)e.x<t&&(t=e.x),e.x>u&&(u=e.x),e.y<x&&(x=e.y),e.y>f&&(f=e.y);isFinite(t)||(t=0,u=1,x=0,f=1),void 0!==i?.min&&(t=i.min),void 0!==i?.max&&(u=i.max),void 0!==r?.min&&(x=r.min),void 0!==r?.max&&(f=r.max);const h=i?.tickCount??6,y=r?.tickCount??6;let m,p,g,b;if(s)m=s[0],p=s[1];else{const e=l(t,u,h);m=e.niceMin,p=e.niceMax}if(d)g=d[0],b=d[1];else{const e=l(x,f,y);g=e.niceMin,b=e.niceMax}const k=l(m,p,h),v=l(g,b,y);return{xScale:a(m,p,0,n),yScale:a(g,b,o,0),xTicks:c(k.niceMin,k.niceMax,k.tickStep).filter(e=>e>=m&&e<=p),yTicks:c(v.niceMin,v.niceMax,v.tickStep).filter(e=>e>=g&&e<=b),xDomain:[m,p],yDomain:[g,b]}},[e,n,o,i,r,s,d])}function x(e,n,o,s,l){const a=e?.enabled??!1,c=e?.maxScale??10,d=e?.step??.5,u=e?.enableWheel??!0,x=e?.enablePan??!0,f=e?.requireCtrlKey??!0,[h,y]=t.useState(n),[m,p]=t.useState(o),[g,b]=t.useState(!1),[k,v]=t.useState(!1),C=t.useRef({x:0,y:0,xDomain:n,yDomain:o}),S=t.useRef(null),j=t.useRef({x:n,y:o});n[0]===j.current.x[0]&&n[1]===j.current.x[1]&&o[0]===j.current.y[0]&&o[1]===j.current.y[1]||(j.current={x:n,y:o},y(n),p(o));const M=n[1]-n[0],$=h[1]-h[0],w=M>0&&$>0?M/$:1,D=t.useCallback((e,t)=>{const n=e[1]-e[0];if(n>=t[1]-t[0])return t;let o=e[0],i=e[1];return o<t[0]&&(o=t[0],i=o+n),i>t[1]&&(i=t[1],o=i-n),[o,i]},[]),W=t.useCallback(()=>{S.current&&clearTimeout(S.current),v(!0),S.current=setTimeout(()=>v(!1),2e3)},[]),T=t.useCallback((e,t,i)=>{if(!a)return;const s=t??.5,l=i??.5;y(t=>{const o=(t[1]-t[0])/e,i=M/c,l=Math.max(o,i),a=Math.min(l,M),d=r(t[0],t[1],s)-a*s;return D([d,d+a],n)}),p(t=>{const n=t[1]-t[0],i=o[1]-o[0],s=n/e,a=i/c,d=Math.max(s,a),u=Math.min(d,i),x=r(t[0],t[1],l)-u*l;return D([x,x+u],o)})},[a,n,o,M,c,D]),P=t.useCallback(()=>{T(1+d)},[d,T]),L=t.useCallback(()=>{T(1/(1+d))},[d,T]),X=t.useCallback(()=>{y(n),p(o)},[n,o]),A=t.useCallback(e=>{if(!a||!u)return;if(f&&!e.ctrlKey&&!e.metaKey)return void W();e.preventDefault();const t=e.currentTarget.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top,r=i(n/(t.width||1),0,1),s=i(o/(t.height||1),0,1),l=e.deltaY<0?1+d:1/(1+d);T(l,r,s)},[a,u,f,d,W,T]),z=t.useCallback(e=>{!a||!x||w<=1||0===e.button&&(b(!0),C.current={x:e.clientX,y:e.clientY,xDomain:h,yDomain:m})},[a,x,w,h,m]),N=t.useCallback(e=>{if(!g)return;const t=e.clientX-C.current.x,i=e.clientY-C.current.y,r=C.current.xDomain,a=C.current.yDomain,c=r[1]-r[0],d=a[1]-a[0],u=-t/s*c,x=i/l*d,f=[r[0]+u,r[1]+u],h=[a[0]+x,a[1]+x];y(D(f,n)),p(D(h,o))},[g,s,l,n,o,D]),R=t.useCallback(()=>{b(!1)},[]);return{viewXDomain:h,viewYDomain:m,scale:w,isPanning:g,showZoomHint:k,zoomIn:P,zoomOut:L,resetZoom:X,handleWheel:A,handlePanStart:z,handlePanMove:N,handlePanEnd:R}}function f(e,t,n){if(0===e.length)return null;let o=0,i=e.length-1;for(;o<i;){const r=Math.floor((o+i)/2);n(e[r].x)<t?o=r+1:i=r}let r=e[o],s=Math.abs(n(r.x)-t);if(o>0){const i=Math.abs(n(e[o-1].x)-t);i<s&&(s=i,r=e[o-1])}return r}function h(e,n,o,i,r,s,l,a){const[c,d]=t.useState(!1),[u,x]=t.useState(0),[h,y]=t.useState(0),[m,p]=t.useState(null),[g,b]=t.useState(null),[k,v]=t.useState(null);return{tooltipVisible:c,tooltipX:u,tooltipY:h,activePoint:m,activeSeries:g,activeSeriesId:k,handleMouseMove:t.useCallback(t=>{const s=e.current;if(!s)return;const a=s.getBoundingClientRect(),c=t.clientX-a.left-r;if(c<0||c>l)return d(!1),void v(null);let u=null,h=null,m=1/0;for(const e of n){const t=f(e.data,c,o);if(!t)continue;const n=Math.abs(o(t.x)-c);n<m&&(m=n,u=t,h=e)}u&&h&&(x(o(u.x)),y(i(u.y)),p(u),b(h),v(h.id),d(!0))},[e,n,o,i,r,l]),handleMouseLeave:t.useCallback(()=>{d(!1),p(null),b(null),v(null)},[])}}const y=e=>Math.abs(e)>=1e6?`${(e/1e6).toFixed(1)}M`:Math.abs(e)>=1e3?`${(e/1e3).toFixed(1)}K`:Number.isInteger(e)?e.toString():e.toFixed(1),m=t.memo(({xTicks:t,yTicks:n,xScale:o,yScale:i,plotWidth:r,plotHeight:s,xAxisConfig:l,yAxisConfig:a,style:c})=>{const d=c?.axisColor??"#333",u=c?.tickColor??"#666",x=c?.fontFamily??"sans-serif",f=c?.fontSize??11,h=l?.tickFormat??y,m=a?.tickFormat??y;return e.jsxs("g",{className:"chartifypdf-axes",children:[e.jsx("line",{x1:0,y1:s,x2:r,y2:s,stroke:d,strokeWidth:1}),t.map(t=>{const n=o(t);return e.jsxs("g",{children:[e.jsx("line",{x1:n,y1:s,x2:n,y2:s+6,stroke:d,strokeWidth:1}),e.jsx("text",{x:n,y:s+18,textAnchor:"middle",fill:u,fontFamily:x,fontSize:f,children:h(t)})]},`x-tick-${t}`)}),l?.label&&e.jsx("text",{x:r/2,y:s+38,textAnchor:"middle",fill:d,fontFamily:x,fontSize:f+1,fontWeight:"bold",children:l.label}),e.jsx("line",{x1:0,y1:0,x2:0,y2:s,stroke:d,strokeWidth:1}),n.map(t=>{const n=i(t);return e.jsxs("g",{children:[e.jsx("line",{x1:-6,y1:n,x2:0,y2:n,stroke:d,strokeWidth:1}),e.jsx("text",{x:-10,y:n,textAnchor:"end",dominantBaseline:"middle",fill:u,fontFamily:x,fontSize:f,children:m(t)})]},`y-tick-${t}`)}),a?.label&&e.jsx("text",{x:0,y:0,textAnchor:"middle",fill:d,fontFamily:x,fontSize:f+1,fontWeight:"bold",transform:`translate(-40, ${s/2}) rotate(-90)`,children:a.label})]})});m.displayName="Axes";const p=t.memo(({xTicks:t,yTicks:n,xScale:o,yScale:i,plotWidth:r,plotHeight:s,showXGrid:l,showYGrid:a,xGridColor:c="#e0e0e0",yGridColor:d="#e0e0e0"})=>e.jsxs("g",{className:"chartifypdf-grid",children:[a&&n.map(t=>{const n=i(t);return e.jsx("line",{x1:0,y1:n,x2:r,y2:n,stroke:d,strokeDasharray:"4 4",strokeOpacity:.5},`y-grid-${t}`)}),l&&t.map(t=>{const n=o(t);return e.jsx("line",{x1:n,y1:0,x2:n,y2:s,stroke:c,strokeDasharray:"4 4",strokeOpacity:.5},`x-grid-${t}`)})]}));function g(e){if(0===e.length)return"";const[t,...n]=e;return`M${t.x},${t.y}`+n.map(e=>`L${e.x},${e.y}`).join("")}function b(e,t){switch(t){case"monotone":return function(e){const t=e.length;if(t<2)return g(e);if(2===t)return g(e);const n=[],o=[],i=[];for(let r=0;r<t-1;r++)n.push(e[r+1].x-e[r].x),o.push(e[r+1].y-e[r].y),i.push(0===n[r]?0:o[r]/n[r]);const r=new Array(t);r[0]=i[0],r[t-1]=i[t-2];for(let e=1;e<t-1;e++)i[e-1]*i[e]<=0?r[e]=0:r[e]=2/(1/i[e-1]+1/i[e]);for(let e=0;e<t-1;e++)if(0===i[e])r[e]=0,r[e+1]=0;else{const t=r[e]/i[e],n=r[e+1]/i[e],o=t*t+n*n;if(o>9){const s=3/Math.sqrt(o);r[e]=s*t*i[e],r[e+1]=s*n*i[e]}}let s=`M${e[0].x},${e[0].y}`;for(let o=0;o<t-1;o++){const t=n[o]/3;s+=`C${e[o].x+t},${e[o].y+r[o]*t},${e[o+1].x-t},${e[o+1].y-r[o+1]*t},${e[o+1].x},${e[o+1].y}`}return s}(e);case"natural":return function(e){const t=e.length;if(t<2)return g(e);if(2===t)return g(e);function n(e){const t=e.length-1,n=new Array(t),o=new Array(t),i=new Array(t);n[0]=0,o[0]=2,i[0]=e[0]+2*e[1];for(let r=1;r<t-1;r++)n[r]=1,o[r]=4,i[r]=4*e[r]+2*e[r+1];n[t-1]=2,o[t-1]=7,i[t-1]=8*e[t-1]+e[t];for(let e=1;e<t;e++){const t=n[e]/o[e-1];o[e]-=t,i[e]-=t*i[e-1]}const r=new Array(t);r[t-1]=i[t-1]/o[t-1];for(let e=t-2;e>=0;e--)r[e]=(i[e]-r[e+1])/o[e];const s=new Array(t);for(let n=0;n<t-1;n++)s[n]=2*e[n+1]-r[n+1];s[t-1]=(e[t]+r[t-1])/2;const l=[];for(let e=0;e<t;e++)l.push({cp1:r[e],cp2:s[e]});return l}const o=e.map(e=>e.x),i=e.map(e=>e.y),r=n(o),s=n(i);let l=`M${e[0].x},${e[0].y}`;for(let n=0;n<t-1;n++)l+=`C${r[n].cp1},${s[n].cp1},${r[n].cp2},${s[n].cp2},${e[n+1].x},${e[n+1].y}`;return l}(e);default:return g(e)}}p.displayName="GridLines";const k=t.memo(({series:n,xScale:o,yScale:i,color:r,animation:s,onPointClick:l,curveType:a,highlightOpacity:c})=>{const d=t.useRef(null),[u,x]=t.useState(0),[f,h]=t.useState(!s?.enabled),y=n.strokeWidth??2,m=n.showDots??!1,p=n.dotRadius??3.5,k=n.curveType??a??"linear",v=c??1,C=n.data.map(e=>({x:o(e.x),y:i(e.y)})),S="linear"===k?g(C):b(C,k);t.useEffect(()=>{if(s?.enabled&&d.current){const e=d.current.getTotalLength();x(e),h(!1);const t=setTimeout(()=>{h(!0)},s.duration??800);return()=>clearTimeout(t)}},[S,s?.enabled,s?.duration]);const j=s?.enabled&&u>0?{strokeDasharray:u,strokeDashoffset:f?0:u,transition:`stroke-dashoffset ${s.duration??800}ms ${s.easing??"ease-in-out"}`}:{};return e.jsxs("g",{className:"chartifypdf-line-path",style:{opacity:v,transition:"opacity 150ms ease"},children:[e.jsx("path",{ref:d,d:S,fill:"none",stroke:r,strokeWidth:y,strokeDasharray:n.strokeDasharray,strokeLinecap:"round",strokeLinejoin:"round",style:j}),m&&n.data.map((t,s)=>e.jsx("circle",{cx:o(t.x),cy:i(t.y),r:p,fill:r,stroke:"#fff",strokeWidth:1.5,style:{cursor:l?"pointer":"default"},onClick:l?()=>l(t,n):void 0},`dot-${n.id}-${s}`))]})});k.displayName="LinePath";const v=t.memo(({visible:t,x:n,y:o,point:i,series:r,plotHeight:s,plotWidth:l,config:a,seriesColor:c})=>{if(!t||!i||!r)return null;const d=a?.backgroundColor??"rgba(0, 0, 0, 0.8)",u=a?.textColor??"#fff";let x;if(a?.renderCustom)x=a.renderCustom(i,r);else{x=a?.formatter?a.formatter(i,r):`${r.name}: (${i.x}, ${i.y})`}let f=n+12,h=o-40-8;return f+140>l&&(f=n-140-12),h<0&&(h=o+12),e.jsxs("g",{className:"chartifypdf-tooltip",pointerEvents:"none",children:[e.jsx("line",{x1:n,y1:0,x2:n,y2:s,stroke:"#999",strokeWidth:1,strokeDasharray:"4 4",opacity:.6}),e.jsx("circle",{cx:n,cy:o,r:5,fill:c,stroke:"#fff",strokeWidth:2}),e.jsx("foreignObject",{x:f,y:h,width:140,height:60,overflow:"visible",children:e.jsx("div",{style:{backgroundColor:d,color:u,padding:"6px 10px",borderRadius:"4px",fontSize:"12px",fontFamily:"sans-serif",whiteSpace:"nowrap",lineHeight:1.4,boxShadow:"0 2px 6px rgba(0,0,0,0.2)"},children:x})})]})});v.displayName="Tooltip";const C=t.memo(({config:t,svgWidth:n,svgHeight:o,margin:i,scale:r,onZoomIn:s,onZoomOut:l,onReset:a})=>{if(!t?.enabled||!1===t.showControls)return null;const c=t.controlsPosition??"top-right",d=24;let u,x;switch(c){case"top-left":u=i.left+8,x=i.top+8;break;case"bottom-left":u=i.left+8,x=o-i.bottom-24-8;break;case"bottom-right":u=n-i.right-80-8,x=o-i.bottom-24-8;break;default:u=n-i.right-80-8,x=i.top+8}const f=r>1?[{label:"+",onClick:s},{label:"−",onClick:l},{label:"↺",onClick:a}]:[{label:"+",onClick:s},{label:"−",onClick:l}];return e.jsx("g",{className:"chartifypdf-zoom-controls",transform:`translate(${u}, ${x})`,children:f.map((t,n)=>e.jsxs("g",{transform:`translate(${28*n}, 0)`,onClick:t.onClick,style:{cursor:"pointer"},children:[e.jsx("rect",{width:d,height:d,rx:4,fill:"rgba(255,255,255,0.9)",stroke:"#ccc",strokeWidth:1}),e.jsx("text",{x:12,y:12,textAnchor:"middle",dominantBaseline:"central",fontSize:14,fontFamily:"sans-serif",fill:"#333",fontWeight:"bold",children:t.label})]},t.label))})});function S({x:t,y:n,color:o}){return e.jsx("polygon",{points:`${t},${n} ${t-5},${n-10} ${t+5},${n-10}`,fill:o})}function j({x:t,y:n,color:o}){return e.jsx("polygon",{points:`${t},${n-6} ${t+5},${n} ${t},${n+6} ${t-5},${n}`,fill:o})}function M({x:t,y:n,color:o}){const i=[];for(let e=0;e<5;e++){const o=(72*e-90)*(Math.PI/180),r=(72*e+36-90)*(Math.PI/180);i.push(`${t+6*Math.cos(o)},${n+6*Math.sin(o)}`),i.push(`${t+3*Math.cos(r)},${n+3*Math.sin(r)}`)}return e.jsx("polygon",{points:i.join(" "),fill:o})}C.displayName="ZoomControls";const $=t.memo(({peaks:t,data:n,xScale:o,yScale:i})=>e.jsx("g",{className:"chartifypdf-peak-markers",children:t.map((t,r)=>{const s=function(e,t){let n=-1/0,o=-1;for(let i=0;i<e.length;i++){const r=e[i];let s=r.data[0],l=1/0;for(const e of r.data){const n=Math.abs(e.x-t);n<l&&(l=n,s=e)}s&&s.y>n&&(n=s.y,o=i)}return-1===o?null:{y:n,seriesIndex:o}}(n,t.x);if(!s)return null;const l=o(t.x),a=i(s.y),c=t.color??"#ef4444",d=t.icon??"arrow",u=t.label,x=a-14;return e.jsxs("g",{children:[e.jsx("line",{x1:l,y1:a,x2:l,y2:x+6,stroke:c,strokeWidth:1,strokeDasharray:"3 2",opacity:.6}),"arrow"===d&&e.jsx(S,{x:l,y:x,color:c}),"diamond"===d&&e.jsx(j,{x:l,y:x,color:c}),"star"===d&&e.jsx(M,{x:l,y:x,color:c}),u&&e.jsx("text",{x:l,y:x-8,textAnchor:"middle",fill:c,fontSize:11,fontWeight:600,children:u})]},`peak-${r}`)})}));$.displayName="PeakMarkers";const w=({visible:n,x:o,y:i,nearestX:r,data:s,config:l,getSeriesColor:a,onClose:c})=>{const d=t.useRef(null);if(t.useEffect(()=>{if(!n)return;const e=e=>{d.current&&!d.current.contains(e.target)&&c()},t=e=>{"Escape"===e.key&&c()},o=setTimeout(()=>{document.addEventListener("mousedown",e),document.addEventListener("keydown",t)},0);return()=>{clearTimeout(o),document.removeEventListener("mousedown",e),document.removeEventListener("keydown",t)}},[n,c]),!n)return null;const u=l?.backgroundColor??"#ffffff",x=l?.textColor??"#1f2937",f=l?.borderColor??"#e5e7eb",h=s.map((e,t)=>{const n=function(e,t){if(0===e.data.length)return null;let n=e.data[0],o=Math.abs(n.x-t);for(const i of e.data){const e=Math.abs(i.x-t);e<o&&(o=e,n=i)}return n.y}(e,r);return{name:e.name,color:e.color??a(t),x:r,y:n}});return e.jsxs("div",{ref:d,style:{position:"fixed",left:o,top:i,zIndex:9999,backgroundColor:u,color:x,border:`1px solid ${f}`,borderRadius:6,boxShadow:"0 4px 12px rgba(0,0,0,0.15)",padding:"8px 0",minWidth:200,fontSize:12,fontFamily:"system-ui, -apple-system, sans-serif"},children:[e.jsxs("div",{style:{padding:"4px 12px 8px",fontWeight:600,fontSize:11,color:x,opacity:.6,borderBottom:`1px solid ${f}`,marginBottom:4},children:["Data at X = ","number"==typeof r?r.toLocaleString():r]}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:"4px 12px",fontWeight:600,opacity:.7},children:"Series"}),e.jsx("th",{style:{textAlign:"right",padding:"4px 12px",fontWeight:600,opacity:.7},children:"Y"})]})}),e.jsx("tbody",{children:h.map((t,n)=>e.jsxs("tr",{style:{borderTop:n>0?`1px solid ${f}`:void 0},children:[e.jsxs("td",{style:{padding:"4px 12px",display:"flex",alignItems:"center",gap:6},children:[e.jsx("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",backgroundColor:t.color,flexShrink:0}}),t.name]}),e.jsx("td",{style:{padding:"4px 12px",textAlign:"right",fontVariantNumeric:"tabular-nums"},children:null!==t.y?t.y.toLocaleString():"—"})]},n))})]})]})};w.displayName="ContextMenu";const D={top:20,right:20,bottom:50,left:60},W=({data:i,width:r,height:s,margin:l,xAxis:a,yAxis:c,tooltip:f,zoom:y,style:g,animation:b,colorPalette:S,className:j,onPointClick:M,ariaLabel:W,curveType:T,peaks:P,contextMenu:L})=>{const X=t.useRef(null),A=t.useRef(null),z=t.useId(),N={...D,...l},R=o(X,r,s??400),{width:F,height:Y}=R,I=Math.max(0,F-N.left-N.right),E=Math.max(0,Y-N.top-N.bottom),{fullXDomain:H,fullYDomain:O}=d(i,a,c),Z=x(y,H,O,I,E),G=y?.enabled??!1,{xScale:B,yScale:q,xTicks:K,yTicks:V}=u(i,I,E,a,c,G?Z.viewXDomain:void 0,G?Z.viewYDomain:void 0),U=!1!==f?.enabled,J=h(A,i,B,q,N.left,N.top,I),Q=J.activeSeriesId,_=J.tooltipVisible&&null!==Q,ee=e=>_?e===Q?1:.2:1,[te,ne]=t.useState({visible:!1,clientX:0,clientY:0,nearestX:0}),oe=t.useCallback(e=>{if(!L?.enabled)return;e.preventDefault();const t=A.current;if(!t)return;const n=t.getBoundingClientRect(),o=e.clientX-n.left-N.left;if(o<0||o>I)return;let r=0,s=1/0;for(const e of i)for(const t of e.data){const e=B(t.x),n=Math.abs(e-o);n<s&&(s=n,r=t.x)}ne({visible:!0,clientX:e.clientX,clientY:e.clientY,nearestX:r})},[L?.enabled,i,B,N.left,I]),ie=t.useCallback(()=>{ne(e=>({...e,visible:!1}))},[]),re=a?.gridLines??!1,se=c?.gridLines??!0,le=e=>function(e,t){const o=t??n;return o[e%o.length]}(e,S);return 0===F||0===Y?e.jsx("div",{ref:X,className:j,style:{width:r??"100%",height:s??400,backgroundColor:g?.backgroundColor}}):e.jsxs("div",{ref:X,className:j,style:{width:r??"100%",height:s??400,backgroundColor:g?.backgroundColor,position:"relative"},children:[e.jsxs("svg",{ref:A,width:F,height:Y,role:"img","aria-label":W??"Line chart",onMouseMove:U&&!Z.isPanning?J.handleMouseMove:void 0,onMouseLeave:U?J.handleMouseLeave:void 0,onWheel:G?Z.handleWheel:void 0,onMouseDown:G?Z.handlePanStart:void 0,onMouseUp:G?Z.handlePanEnd:void 0,onContextMenu:oe,style:{userSelect:Z.isPanning?"none":void 0,cursor:Z.isPanning?"grabbing":G&&Z.scale>1?"grab":void 0},children:[e.jsx("defs",{children:e.jsx("clipPath",{id:z,children:e.jsx("rect",{width:I,height:E})})}),e.jsxs("g",{transform:`translate(${N.left}, ${N.top})`,children:[e.jsx(m,{xTicks:K,yTicks:V,xScale:B,yScale:q,plotWidth:I,plotHeight:E,xAxisConfig:a,yAxisConfig:c,style:g}),e.jsx(p,{xTicks:K,yTicks:V,xScale:B,yScale:q,plotWidth:I,plotHeight:E,showXGrid:re,showYGrid:se,xGridColor:a?.gridLineColor,yGridColor:c?.gridLineColor}),e.jsxs("g",{clipPath:`url(#${z})`,children:[G?e.jsx("g",{onMouseMove:Z.handlePanMove,children:i.map((t,n)=>e.jsx(k,{series:t,xScale:B,yScale:q,color:t.color??le(n),animation:b,onPointClick:M,curveType:T,highlightOpacity:ee(t.id)},t.id))}):i.map((t,n)=>e.jsx(k,{series:t,xScale:B,yScale:q,color:t.color??le(n),animation:b,onPointClick:M,curveType:T,highlightOpacity:ee(t.id)},t.id)),P&&P.length>0&&e.jsx($,{peaks:P,data:i,xScale:B,yScale:q,colorPalette:S})]}),U&&e.jsx(v,{visible:J.tooltipVisible,x:J.tooltipX,y:J.tooltipY,point:J.activePoint,series:J.activeSeries,plotHeight:E,plotWidth:I,config:f,seriesColor:J.activeSeries?.color??le(i.findIndex(e=>e.id===J.activeSeries?.id))})]}),e.jsx(C,{config:y,svgWidth:F,svgHeight:Y,margin:N,scale:Z.scale,onZoomIn:Z.zoomIn,onZoomOut:Z.zoomOut,onReset:Z.resetZoom})]}),Z.showZoomHint&&e.jsx("div",{style:{position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",background:"rgba(0, 0, 0, 0.7)",color:"#fff",padding:"8px 16px",borderRadius:6,fontSize:13,fontFamily:"system-ui, -apple-system, sans-serif",pointerEvents:"none",whiteSpace:"nowrap",zIndex:10},children:"Hold Ctrl + scroll to zoom"}),L?.enabled&&e.jsx(w,{visible:te.visible,x:te.clientX,y:te.clientY,nearestX:te.nearestX,data:i,config:L,getSeriesColor:le,onClose:ie})]})};W.displayName="LineChart",exports.LineChart=W,exports.useChartDimensions=o,exports.useDataDomain=d,exports.useScales=u,exports.useTooltip=h,exports.useZoom=x;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/utils/color.ts","../src/hooks/useChartDimensions.ts","../src/utils/math.ts","../src/hooks/useScales.ts","../src/hooks/useZoom.ts","../src/hooks/useTooltip.ts","../src/components/Axes.tsx","../src/components/GridLines.tsx","../src/components/LinePath.tsx","../src/utils/path.ts","../src/components/Tooltip.tsx","../src/components/ZoomControls.tsx","../src/components/LineChart.tsx"],"sourcesContent":["export const DEFAULT_PALETTE = [\n \"#4e79a7\",\n \"#f28e2b\",\n \"#e15759\",\n \"#76b7b2\",\n \"#59a14f\",\n \"#edc948\",\n \"#b07aa1\",\n \"#ff9da7\",\n \"#9c755f\",\n \"#bab0ac\",\n];\n\nexport function getSeriesColor(index: number, palette?: string[]): string {\n const colors = palette ?? DEFAULT_PALETTE;\n return colors[index % colors.length];\n}\n","import { useLayoutEffect, useState, type RefObject } from \"react\";\n\nexport function useChartDimensions(\n containerRef: RefObject<HTMLDivElement | null>,\n providedWidth?: number,\n providedHeight?: number\n): { width: number; height: number } {\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n }>({\n width: providedWidth ?? 0,\n height: providedHeight ?? 0,\n });\n\n const isFixed =\n providedWidth !== undefined && providedHeight !== undefined;\n\n useLayoutEffect(() => {\n if (isFixed) {\n setDimensions({ width: providedWidth!, height: providedHeight! });\n return;\n }\n\n const node = containerRef.current;\n if (!node) return;\n\n const measure = () => {\n const rect = node.getBoundingClientRect();\n setDimensions({ width: rect.width, height: rect.height });\n };\n\n measure();\n\n const observer = new ResizeObserver(() => {\n measure();\n });\n observer.observe(node);\n\n return () => observer.disconnect();\n }, [containerRef, isFixed, providedWidth, providedHeight]);\n\n return dimensions;\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function niceNumber(range: number, round: boolean): number {\n const exponent = Math.floor(Math.log10(range));\n const fraction = range / Math.pow(10, exponent);\n\n let niceFraction: number;\n if (round) {\n if (fraction < 1.5) niceFraction = 1;\n else if (fraction < 3) niceFraction = 2;\n else if (fraction < 7) niceFraction = 5;\n else niceFraction = 10;\n } else {\n if (fraction <= 1) niceFraction = 1;\n else if (fraction <= 2) niceFraction = 2;\n else if (fraction <= 5) niceFraction = 5;\n else niceFraction = 10;\n }\n\n return niceFraction * Math.pow(10, exponent);\n}\n\nexport function niceRange(\n min: number,\n max: number,\n tickCount: number\n): { niceMin: number; niceMax: number; tickStep: number } {\n if (min === max) {\n const offset = min === 0 ? 1 : Math.abs(min) * 0.1;\n min = min - offset;\n max = max + offset;\n }\n\n const range = niceNumber(max - min, false);\n const tickStep = niceNumber(range / (tickCount - 1), true);\n const niceMin = Math.floor(min / tickStep) * tickStep;\n const niceMax = Math.ceil(max / tickStep) * tickStep;\n\n return { niceMin, niceMax, tickStep };\n}\n\nexport function linearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (value: number) => number {\n return (value: number) => {\n if (domainMax === domainMin) return (rangeMin + rangeMax) / 2;\n const t = inverseLerp(domainMin, domainMax, value);\n return lerp(rangeMin, rangeMax, t);\n };\n}\n\nexport function generateTicks(\n niceMin: number,\n niceMax: number,\n tickStep: number\n): number[] {\n const ticks: number[] = [];\n for (let v = niceMin; v <= niceMax + tickStep * 0.5; v += tickStep) {\n ticks.push(parseFloat(v.toPrecision(12)));\n }\n return ticks;\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange, linearScale, generateTicks } from \"../utils/math\";\n\nexport interface ScalesResult {\n xScale: (value: number) => number;\n yScale: (value: number) => number;\n xTicks: number[];\n yTicks: number[];\n xDomain: [number, number];\n yDomain: [number, number];\n}\n\nexport function useScales(\n data: DataSeries[],\n plotWidth: number,\n plotHeight: number,\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig\n): ScalesResult {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n const xNice = niceRange(xMin, xMax, xTickCount);\n const yNice = niceRange(yMin, yMax, yTickCount);\n\n const xScale = linearScale(xNice.niceMin, xNice.niceMax, 0, plotWidth);\n // Y inverted: SVG Y increases downward\n const yScale = linearScale(yNice.niceMin, yNice.niceMax, plotHeight, 0);\n\n const xTicks = generateTicks(xNice.niceMin, xNice.niceMax, xNice.tickStep);\n const yTicks = generateTicks(yNice.niceMin, yNice.niceMax, yNice.tickStep);\n\n return {\n xScale,\n yScale,\n xTicks,\n yTicks,\n xDomain: [xNice.niceMin, xNice.niceMax] as [number, number],\n yDomain: [yNice.niceMin, yNice.niceMax] as [number, number],\n };\n }, [data, plotWidth, plotHeight, xAxisConfig, yAxisConfig]);\n}\n","import { useState, useCallback, useRef } from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\nimport { clamp } from \"../utils/math\";\n\nexport interface ZoomState {\n scale: number;\n offsetX: number;\n offsetY: number;\n isPanning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n resetZoom: () => void;\n handleWheel: (e: React.WheelEvent) => void;\n handlePanStart: (e: React.MouseEvent) => void;\n handlePanMove: (e: React.MouseEvent) => void;\n handlePanEnd: () => void;\n}\n\nexport function useZoom(\n config: ZoomConfig | undefined,\n plotWidth: number,\n plotHeight: number\n): ZoomState {\n const enabled = config?.enabled ?? false;\n const minScale = config?.minScale ?? 1;\n const maxScale = config?.maxScale ?? 10;\n const step = config?.step ?? 0.5;\n const enableWheel = config?.enableWheel ?? true;\n const enablePan = config?.enablePan ?? true;\n\n const [scale, setScale] = useState(1);\n const [offsetX, setOffsetX] = useState(0);\n const [offsetY, setOffsetY] = useState(0);\n const [isPanning, setIsPanning] = useState(false);\n const panStart = useRef({ x: 0, y: 0, offsetX: 0, offsetY: 0 });\n\n const clampOffset = useCallback(\n (ox: number, oy: number, s: number) => {\n const maxOffsetX = plotWidth * (s - 1);\n const maxOffsetY = plotHeight * (s - 1);\n return {\n x: clamp(ox, -maxOffsetX, 0),\n y: clamp(oy, -maxOffsetY, 0),\n };\n },\n [plotWidth, plotHeight]\n );\n\n const zoomIn = useCallback(() => {\n if (!enabled) return;\n setScale((prev) => {\n const next = clamp(prev + step, minScale, maxScale);\n // Center zoom\n setOffsetX((ox) => {\n const newOx = ox - (plotWidth * step) / 2;\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = oy - (plotHeight * step) / 2;\n return clampOffset(0, newOy, next).y;\n });\n return next;\n });\n }, [enabled, step, minScale, maxScale, plotWidth, plotHeight, clampOffset]);\n\n const zoomOut = useCallback(() => {\n if (!enabled) return;\n setScale((prev) => {\n const next = clamp(prev - step, minScale, maxScale);\n setOffsetX((ox) => {\n const newOx = ox + (plotWidth * step) / 2;\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = oy + (plotHeight * step) / 2;\n return clampOffset(0, newOy, next).y;\n });\n return next;\n });\n }, [enabled, step, minScale, maxScale, plotWidth, plotHeight, clampOffset]);\n\n const resetZoom = useCallback(() => {\n setScale(1);\n setOffsetX(0);\n setOffsetY(0);\n }, []);\n\n const handleWheel = useCallback(\n (e: React.WheelEvent) => {\n if (!enabled || !enableWheel) return;\n e.preventDefault();\n\n const rect = (e.currentTarget as SVGElement).getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY < 0 ? step : -step;\n\n setScale((prev) => {\n const next = clamp(prev + delta, minScale, maxScale);\n const ratio = next / prev;\n\n setOffsetX((ox) => {\n const newOx = mouseX - ratio * (mouseX - ox);\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = mouseY - ratio * (mouseY - oy);\n return clampOffset(0, newOy, next).y;\n });\n\n return next;\n });\n },\n [enabled, enableWheel, step, minScale, maxScale, clampOffset]\n );\n\n const handlePanStart = useCallback(\n (e: React.MouseEvent) => {\n if (!enabled || !enablePan || scale <= 1) return;\n if (e.button !== 0) return; // left-click only\n setIsPanning(true);\n panStart.current = {\n x: e.clientX,\n y: e.clientY,\n offsetX,\n offsetY,\n };\n },\n [enabled, enablePan, scale, offsetX, offsetY]\n );\n\n const handlePanMove = useCallback(\n (e: React.MouseEvent) => {\n if (!isPanning) return;\n const dx = e.clientX - panStart.current.x;\n const dy = e.clientY - panStart.current.y;\n const clamped = clampOffset(\n panStart.current.offsetX + dx,\n panStart.current.offsetY + dy,\n scale\n );\n setOffsetX(clamped.x);\n setOffsetY(clamped.y);\n },\n [isPanning, scale, clampOffset]\n );\n\n const handlePanEnd = useCallback(() => {\n setIsPanning(false);\n }, []);\n\n return {\n scale,\n offsetX,\n offsetY,\n isPanning,\n zoomIn,\n zoomOut,\n resetZoom,\n handleWheel,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n };\n}\n","import { useState, useCallback, type RefObject } from \"react\";\nimport type { DataSeries, DataPoint, TooltipConfig } from \"../types/chart.types\";\n\nexport interface TooltipState {\n tooltipVisible: boolean;\n tooltipX: number;\n tooltipY: number;\n activePoint: DataPoint | null;\n activeSeries: DataSeries | null;\n handleMouseMove: (e: React.MouseEvent) => void;\n handleMouseLeave: () => void;\n}\n\nfunction findNearestByPixelX(\n sorted: DataPoint[],\n mouseX: number,\n xScale: (v: number) => number\n): DataPoint | null {\n if (sorted.length === 0) return null;\n\n let low = 0;\n let high = sorted.length - 1;\n\n // Binary search for nearest pixel X\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n if (xScale(sorted[mid].x) < mouseX) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n let nearest = sorted[low];\n let minDist = Math.abs(xScale(nearest.x) - mouseX);\n\n if (low > 0) {\n const dist = Math.abs(xScale(sorted[low - 1].x) - mouseX);\n if (dist < minDist) {\n minDist = dist;\n nearest = sorted[low - 1];\n }\n }\n\n return nearest;\n}\n\nexport function useTooltip(\n svgRef: RefObject<SVGSVGElement | null>,\n data: DataSeries[],\n xScale: (v: number) => number,\n yScale: (v: number) => number,\n marginLeft: number,\n marginTop: number,\n plotWidth: number,\n _config?: TooltipConfig\n): TooltipState {\n const [tooltipVisible, setTooltipVisible] = useState(false);\n const [tooltipX, setTooltipX] = useState(0);\n const [tooltipY, setTooltipY] = useState(0);\n const [activePoint, setActivePoint] = useState<DataPoint | null>(null);\n const [activeSeries, setActiveSeries] = useState<DataSeries | null>(null);\n\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - marginLeft;\n\n if (mouseX < 0 || mouseX > plotWidth) {\n setTooltipVisible(false);\n return;\n }\n\n let closestPoint: DataPoint | null = null;\n let closestSeries: DataSeries | null = null;\n let closestDist = Infinity;\n\n for (const series of data) {\n const nearest = findNearestByPixelX(series.data, mouseX, xScale);\n if (!nearest) continue;\n\n const dist = Math.abs(xScale(nearest.x) - mouseX);\n if (dist < closestDist) {\n closestDist = dist;\n closestPoint = nearest;\n closestSeries = series;\n }\n }\n\n if (closestPoint && closestSeries) {\n setTooltipX(xScale(closestPoint.x));\n setTooltipY(yScale(closestPoint.y));\n setActivePoint(closestPoint);\n setActiveSeries(closestSeries);\n setTooltipVisible(true);\n }\n },\n [svgRef, data, xScale, yScale, marginLeft, plotWidth]\n );\n\n const handleMouseLeave = useCallback(() => {\n setTooltipVisible(false);\n setActivePoint(null);\n setActiveSeries(null);\n }, []);\n\n return {\n tooltipVisible,\n tooltipX,\n tooltipY,\n activePoint,\n activeSeries,\n handleMouseMove,\n handleMouseLeave,\n };\n}\n","import React from \"react\";\nimport type { AxisConfig, ChartStyle } from \"../types/chart.types\";\n\ninterface AxesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n xAxisConfig?: AxisConfig;\n yAxisConfig?: AxisConfig;\n style?: ChartStyle;\n}\n\nconst defaultTickFormat = (v: number): string => {\n if (Math.abs(v) >= 1e6) return `${(v / 1e6).toFixed(1)}M`;\n if (Math.abs(v) >= 1e3) return `${(v / 1e3).toFixed(1)}K`;\n return Number.isInteger(v) ? v.toString() : v.toFixed(1);\n};\n\nexport const Axes: React.FC<AxesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n style,\n }) => {\n const axisColor = style?.axisColor ?? \"#333\";\n const tickColor = style?.tickColor ?? \"#666\";\n const fontFamily = style?.fontFamily ?? \"sans-serif\";\n const fontSize = style?.fontSize ?? 11;\n const xTickFormat = xAxisConfig?.tickFormat ?? defaultTickFormat;\n const yTickFormat = yAxisConfig?.tickFormat ?? defaultTickFormat;\n\n return (\n <g className=\"chartifypdf-axes\">\n {/* X Axis */}\n <line\n x1={0}\n y1={plotHeight}\n x2={plotWidth}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <g key={`x-tick-${tick}`}>\n <line\n x1={x}\n y1={plotHeight}\n x2={x}\n y2={plotHeight + 6}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={x}\n y={plotHeight + 18}\n textAnchor=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {xTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {xAxisConfig?.label && (\n <text\n x={plotWidth / 2}\n y={plotHeight + 38}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n >\n {xAxisConfig.label}\n </text>\n )}\n\n {/* Y Axis */}\n <line\n x1={0}\n y1={0}\n x2={0}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={-6}\n y1={y}\n x2={0}\n y2={y}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={-10}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {yTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {yAxisConfig?.label && (\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n transform={`translate(${-40}, ${plotHeight / 2}) rotate(-90)`}\n >\n {yAxisConfig.label}\n </text>\n )}\n </g>\n );\n }\n);\n\nAxes.displayName = \"Axes\";\n","import React from \"react\";\n\ninterface GridLinesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n showXGrid: boolean;\n showYGrid: boolean;\n xGridColor?: string;\n yGridColor?: string;\n}\n\nexport const GridLines: React.FC<GridLinesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n showXGrid,\n showYGrid,\n xGridColor = \"#e0e0e0\",\n yGridColor = \"#e0e0e0\",\n }) => {\n return (\n <g className=\"chartifypdf-grid\">\n {showYGrid &&\n yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <line\n key={`y-grid-${tick}`}\n x1={0}\n y1={y}\n x2={plotWidth}\n y2={y}\n stroke={yGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n {showXGrid &&\n xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <line\n key={`x-grid-${tick}`}\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke={xGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n </g>\n );\n }\n);\n\nGridLines.displayName = \"GridLines\";\n","import React, { useRef, useEffect, useState } from \"react\";\nimport type { DataSeries, DataPoint, AnimationConfig } from \"../types/chart.types\";\nimport { buildLinePath } from \"../utils/path\";\n\ninterface LinePathProps {\n series: DataSeries;\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n color: string;\n animation?: AnimationConfig;\n onPointClick?: (point: DataPoint, series: DataSeries) => void;\n}\n\nexport const LinePath: React.FC<LinePathProps> = React.memo(\n ({ series, xScale, yScale, color, animation, onPointClick }) => {\n const pathRef = useRef<SVGPathElement>(null);\n const [pathLength, setPathLength] = useState(0);\n const [animationDone, setAnimationDone] = useState(!animation?.enabled);\n\n const strokeWidth = series.strokeWidth ?? 2;\n const showDots = series.showDots ?? false;\n const dotRadius = series.dotRadius ?? 3.5;\n\n const points = series.data.map((pt) => ({\n x: xScale(pt.x),\n y: yScale(pt.y),\n }));\n\n const d = buildLinePath(points);\n\n useEffect(() => {\n if (animation?.enabled && pathRef.current) {\n const length = pathRef.current.getTotalLength();\n setPathLength(length);\n setAnimationDone(false);\n\n const timer = setTimeout(() => {\n setAnimationDone(true);\n }, animation.duration ?? 800);\n\n return () => clearTimeout(timer);\n }\n }, [d, animation?.enabled, animation?.duration]);\n\n const animStyle: React.CSSProperties =\n animation?.enabled && pathLength > 0\n ? {\n strokeDasharray: pathLength,\n strokeDashoffset: animationDone ? 0 : pathLength,\n transition: `stroke-dashoffset ${animation.duration ?? 800}ms ${animation.easing ?? \"ease-in-out\"}`,\n }\n : {};\n\n return (\n <g className=\"chartifypdf-line-path\">\n <path\n ref={pathRef}\n d={d}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={series.strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={animStyle}\n />\n {showDots &&\n series.data.map((pt, i) => (\n <circle\n key={`dot-${series.id}-${i}`}\n cx={xScale(pt.x)}\n cy={yScale(pt.y)}\n r={dotRadius}\n fill={color}\n stroke=\"#fff\"\n strokeWidth={1.5}\n style={{ cursor: onPointClick ? \"pointer\" : \"default\" }}\n onClick={\n onPointClick\n ? () => onPointClick(pt, series)\n : undefined\n }\n />\n ))}\n </g>\n );\n }\n);\n\nLinePath.displayName = \"LinePath\";\n","export function buildLinePath(\n points: { x: number; y: number }[]\n): string {\n if (points.length === 0) return \"\";\n const [first, ...rest] = points;\n return `M${first.x},${first.y}` + rest.map((p) => `L${p.x},${p.y}`).join(\"\");\n}\n","import React from \"react\";\nimport type { DataPoint, DataSeries, TooltipConfig } from \"../types/chart.types\";\n\ninterface TooltipProps {\n visible: boolean;\n x: number;\n y: number;\n point: DataPoint | null;\n series: DataSeries | null;\n plotHeight: number;\n plotWidth: number;\n config?: TooltipConfig;\n seriesColor: string;\n}\n\nexport const Tooltip: React.FC<TooltipProps> = React.memo(\n ({ visible, x, y, point, series, plotHeight, plotWidth, config, seriesColor }) => {\n if (!visible || !point || !series) return null;\n\n const bgColor = config?.backgroundColor ?? \"rgba(0, 0, 0, 0.8)\";\n const textColor = config?.textColor ?? \"#fff\";\n\n // Determine tooltip text\n let content: React.ReactNode;\n if (config?.renderCustom) {\n content = config.renderCustom(point, series);\n } else {\n const text = config?.formatter\n ? config.formatter(point, series)\n : `${series.name}: (${point.x}, ${point.y})`;\n content = text;\n }\n\n // Smart positioning: flip when near edges\n const tooltipWidth = 140;\n const tooltipHeight = 40;\n let tx = x + 12;\n let ty = y - tooltipHeight - 8;\n\n if (tx + tooltipWidth > plotWidth) {\n tx = x - tooltipWidth - 12;\n }\n if (ty < 0) {\n ty = y + 12;\n }\n\n return (\n <g className=\"chartifypdf-tooltip\" pointerEvents=\"none\">\n {/* Vertical indicator line */}\n <line\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke=\"#999\"\n strokeWidth={1}\n strokeDasharray=\"4 4\"\n opacity={0.6}\n />\n {/* Highlight circle */}\n <circle\n cx={x}\n cy={y}\n r={5}\n fill={seriesColor}\n stroke=\"#fff\"\n strokeWidth={2}\n />\n {/* Tooltip box using foreignObject */}\n <foreignObject\n x={tx}\n y={ty}\n width={tooltipWidth}\n height={tooltipHeight + 20}\n overflow=\"visible\"\n >\n <div\n style={{\n backgroundColor: bgColor,\n color: textColor,\n padding: \"6px 10px\",\n borderRadius: \"4px\",\n fontSize: \"12px\",\n fontFamily: \"sans-serif\",\n whiteSpace: \"nowrap\",\n lineHeight: 1.4,\n boxShadow: \"0 2px 6px rgba(0,0,0,0.2)\",\n }}\n >\n {content}\n </div>\n </foreignObject>\n </g>\n );\n }\n);\n\nTooltip.displayName = \"Tooltip\";\n","import React from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\n\ninterface ZoomControlsProps {\n config?: ZoomConfig;\n svgWidth: number;\n svgHeight: number;\n margin: { top: number; right: number; bottom: number; left: number };\n scale: number;\n onZoomIn: () => void;\n onZoomOut: () => void;\n onReset: () => void;\n}\n\nexport const ZoomControls: React.FC<ZoomControlsProps> = React.memo(\n ({ config, svgWidth, svgHeight, margin, scale, onZoomIn, onZoomOut, onReset }) => {\n if (!config?.enabled || config.showControls === false) return null;\n\n const position = config.controlsPosition ?? \"top-right\";\n const btnSize = 24;\n const gap = 4;\n const groupWidth = btnSize * 3 + gap * 2;\n const groupHeight = btnSize;\n const padding = 8;\n\n let gx: number;\n let gy: number;\n\n switch (position) {\n case \"top-left\":\n gx = margin.left + padding;\n gy = margin.top + padding;\n break;\n case \"bottom-left\":\n gx = margin.left + padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"bottom-right\":\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"top-right\":\n default:\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = margin.top + padding;\n break;\n }\n\n const buttons = [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2013\", onClick: onReset }, // en-dash for reset\n { label: \"\\u2212\", onClick: onZoomOut }, // minus sign\n ];\n\n // Replace middle button with reset when zoomed, or show as \"-\"\n const displayButtons =\n scale > 1\n ? [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n { label: \"\\u21BA\", onClick: onReset },\n ]\n : [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n ];\n\n return (\n <g\n className=\"chartifypdf-zoom-controls\"\n transform={`translate(${gx}, ${gy})`}\n >\n {displayButtons.map((btn, i) => (\n <g\n key={btn.label}\n transform={`translate(${i * (btnSize + gap)}, 0)`}\n onClick={btn.onClick}\n style={{ cursor: \"pointer\" }}\n >\n <rect\n width={btnSize}\n height={btnSize}\n rx={4}\n fill=\"rgba(255,255,255,0.9)\"\n stroke=\"#ccc\"\n strokeWidth={1}\n />\n <text\n x={btnSize / 2}\n y={btnSize / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={14}\n fontFamily=\"sans-serif\"\n fill=\"#333\"\n fontWeight=\"bold\"\n >\n {btn.label}\n </text>\n </g>\n ))}\n </g>\n );\n }\n);\n\nZoomControls.displayName = \"ZoomControls\";\n","import React, { useRef, useId } from \"react\";\nimport type {\n LineChartProps,\n ChartMargin,\n} from \"../types/chart.types\";\nimport { getSeriesColor } from \"../utils/color\";\nimport { useChartDimensions } from \"../hooks/useChartDimensions\";\nimport { useScales } from \"../hooks/useScales\";\nimport { useZoom } from \"../hooks/useZoom\";\nimport { useTooltip } from \"../hooks/useTooltip\";\nimport { Axes } from \"./Axes\";\nimport { GridLines } from \"./GridLines\";\nimport { LinePath } from \"./LinePath\";\nimport { Tooltip } from \"./Tooltip\";\nimport { ZoomControls } from \"./ZoomControls\";\n\nconst DEFAULT_MARGIN: ChartMargin = {\n top: 20,\n right: 20,\n bottom: 50,\n left: 60,\n};\n\nconst DEFAULT_HEIGHT = 400;\n\nexport const LineChart: React.FC<LineChartProps> = ({\n data,\n width: providedWidth,\n height: providedHeight,\n margin: marginOverride,\n xAxis: xAxisConfig,\n yAxis: yAxisConfig,\n tooltip: tooltipConfig,\n zoom: zoomConfig,\n style: chartStyle,\n animation,\n colorPalette,\n className,\n onPointClick,\n ariaLabel,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const clipId = useId();\n\n const margin: ChartMargin = {\n ...DEFAULT_MARGIN,\n ...marginOverride,\n };\n\n const dimensions = useChartDimensions(\n containerRef,\n providedWidth,\n providedHeight ?? DEFAULT_HEIGHT\n );\n const { width: svgWidth, height: svgHeight } = dimensions;\n\n const plotWidth = Math.max(0, svgWidth - margin.left - margin.right);\n const plotHeight = Math.max(0, svgHeight - margin.top - margin.bottom);\n\n const { xScale, yScale, xTicks, yTicks } = useScales(\n data,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig\n );\n\n const zoomState = useZoom(zoomConfig, plotWidth, plotHeight);\n\n const tooltipEnabled = tooltipConfig?.enabled !== false;\n const tooltipState = useTooltip(\n svgRef,\n data,\n xScale,\n yScale,\n margin.left,\n margin.top,\n plotWidth,\n tooltipConfig\n );\n\n const showXGrid = xAxisConfig?.gridLines ?? false;\n const showYGrid = yAxisConfig?.gridLines ?? true;\n\n // Don't render until we have dimensions\n if (svgWidth === 0 || svgHeight === 0) {\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n />\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n >\n <svg\n ref={svgRef}\n width={svgWidth}\n height={svgHeight}\n role=\"img\"\n aria-label={ariaLabel ?? \"Line chart\"}\n onMouseMove={\n tooltipEnabled && !zoomState.isPanning\n ? tooltipState.handleMouseMove\n : undefined\n }\n onMouseLeave={tooltipEnabled ? tooltipState.handleMouseLeave : undefined}\n onWheel={zoomConfig?.enabled ? zoomState.handleWheel : undefined}\n onMouseDown={zoomConfig?.enabled ? zoomState.handlePanStart : undefined}\n onMouseUp={zoomConfig?.enabled ? zoomState.handlePanEnd : undefined}\n style={{\n userSelect: zoomState.isPanning ? \"none\" : undefined,\n cursor: zoomState.isPanning\n ? \"grabbing\"\n : zoomConfig?.enabled && zoomState.scale > 1\n ? \"grab\"\n : undefined,\n }}\n >\n <defs>\n <clipPath id={clipId}>\n <rect width={plotWidth} height={plotHeight} />\n </clipPath>\n </defs>\n\n <g transform={`translate(${margin.left}, ${margin.top})`}>\n {/* Axes render outside zoom transform — always crisp */}\n <Axes\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n xAxisConfig={xAxisConfig}\n yAxisConfig={yAxisConfig}\n style={chartStyle}\n />\n\n {/* Grid lines */}\n <GridLines\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n showXGrid={showXGrid}\n showYGrid={showYGrid}\n xGridColor={xAxisConfig?.gridLineColor}\n yGridColor={yAxisConfig?.gridLineColor}\n />\n\n {/* Clipped + zoomed data area */}\n <g clipPath={`url(#${clipId})`}>\n <g\n transform={`translate(${zoomState.offsetX}, ${zoomState.offsetY}) scale(${zoomState.scale})`}\n onMouseMove={\n zoomConfig?.enabled ? zoomState.handlePanMove : undefined\n }\n >\n {data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={\n series.color ?? getSeriesColor(i, colorPalette)\n }\n animation={animation}\n onPointClick={onPointClick}\n />\n ))}\n </g>\n </g>\n\n {/* Tooltip renders above everything in plot area */}\n {tooltipEnabled && (\n <Tooltip\n visible={tooltipState.tooltipVisible}\n x={tooltipState.tooltipX}\n y={tooltipState.tooltipY}\n point={tooltipState.activePoint}\n series={tooltipState.activeSeries}\n plotHeight={plotHeight}\n plotWidth={plotWidth}\n config={tooltipConfig}\n seriesColor={\n tooltipState.activeSeries?.color ??\n getSeriesColor(\n data.findIndex(\n (s) => s.id === tooltipState.activeSeries?.id\n ),\n colorPalette\n )\n }\n />\n )}\n </g>\n\n {/* Zoom controls render at SVG root level */}\n <ZoomControls\n config={zoomConfig}\n svgWidth={svgWidth}\n svgHeight={svgHeight}\n margin={margin}\n scale={zoomState.scale}\n onZoomIn={zoomState.zoomIn}\n onZoomOut={zoomState.zoomOut}\n onReset={zoomState.resetZoom}\n />\n </svg>\n </div>\n );\n};\n\nLineChart.displayName = \"LineChart\";\n"],"names":["DEFAULT_PALETTE","getSeriesColor","index","palette","colors","length","useChartDimensions","containerRef","providedWidth","providedHeight","dimensions","setDimensions","useState","width","height","isFixed","undefined","useLayoutEffect","node","current","measure","rect","getBoundingClientRect","observer","ResizeObserver","observe","disconnect","clamp","value","min","max","Math","niceNumber","range","round","exponent","floor","log10","fraction","pow","niceFraction","niceRange","tickCount","offset","abs","tickStep","niceMin","niceMax","ceil","linearScale","domainMin","domainMax","rangeMin","rangeMax","t","a","b","inverseLerp","lerp","generateTicks","ticks","v","push","parseFloat","toPrecision","useScales","data","plotWidth","plotHeight","xAxisConfig","yAxisConfig","useMemo","xMin","Infinity","xMax","yMin","yMax","series","point","x","y","isFinite","xTickCount","yTickCount","xNice","yNice","xScale","yScale","xTicks","yTicks","xDomain","yDomain","useZoom","config","enabled","minScale","maxScale","step","enableWheel","enablePan","scale","setScale","offsetX","setOffsetX","offsetY","setOffsetY","isPanning","setIsPanning","panStart","useRef","clampOffset","useCallback","ox","oy","s","maxOffsetY","zoomIn","prev","next","zoomOut","resetZoom","handleWheel","e","preventDefault","currentTarget","mouseX","clientX","left","mouseY","clientY","top","delta","deltaY","ratio","handlePanStart","button","handlePanMove","dx","dy","clamped","handlePanEnd","findNearestByPixelX","sorted","low","high","mid","nearest","minDist","dist","useTooltip","svgRef","marginLeft","marginTop","_config","tooltipVisible","setTooltipVisible","tooltipX","setTooltipX","tooltipY","setTooltipY","activePoint","setActivePoint","activeSeries","setActiveSeries","handleMouseMove","svg","closestPoint","closestSeries","closestDist","handleMouseLeave","defaultTickFormat","toFixed","Number","isInteger","toString","Axes","React","memo","style","axisColor","tickColor","fontFamily","fontSize","xTickFormat","tickFormat","yTickFormat","_jsxs","className","_jsx","x1","y1","x2","y2","stroke","strokeWidth","map","tick","children","textAnchor","fill","label","fontWeight","dominantBaseline","transform","displayName","GridLines","showXGrid","showYGrid","xGridColor","yGridColor","strokeDasharray","strokeOpacity","LinePath","color","animation","onPointClick","pathRef","pathLength","setPathLength","animationDone","setAnimationDone","showDots","dotRadius","d","points","first","rest","p","join","buildLinePath","pt","useEffect","getTotalLength","timer","setTimeout","duration","clearTimeout","animStyle","strokeDashoffset","transition","easing","ref","strokeLinecap","strokeLinejoin","i","cx","cy","r","cursor","onClick","id","Tooltip","visible","seriesColor","bgColor","backgroundColor","textColor","content","renderCustom","formatter","name","tx","ty","pointerEvents","opacity","tooltipHeight","overflow","padding","borderRadius","whiteSpace","lineHeight","boxShadow","ZoomControls","svgWidth","svgHeight","margin","onZoomIn","onZoomOut","onReset","showControls","position","controlsPosition","btnSize","gx","gy","bottom","right","displayButtons","btn","rx","DEFAULT_MARGIN","LineChart","marginOverride","xAxis","yAxis","tooltip","tooltipConfig","zoom","zoomConfig","chartStyle","colorPalette","ariaLabel","clipId","useId","zoomState","tooltipEnabled","tooltipState","gridLines","role","onMouseMove","onMouseLeave","onWheel","onMouseDown","onMouseUp","userSelect","gridLineColor","clipPath","findIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils/color.ts","../src/hooks/useChartDimensions.ts","../src/utils/math.ts","../src/hooks/useDataDomain.ts","../src/hooks/useScales.ts","../src/hooks/useZoom.ts","../src/hooks/useTooltip.ts","../src/components/Axes.tsx","../src/components/GridLines.tsx","../src/utils/path.ts","../src/components/LinePath.tsx","../src/components/Tooltip.tsx","../src/components/ZoomControls.tsx","../src/components/PeakMarkers.tsx","../src/components/ContextMenu.tsx","../src/components/LineChart.tsx"],"sourcesContent":["export const DEFAULT_PALETTE = [\n \"#4e79a7\",\n \"#f28e2b\",\n \"#e15759\",\n \"#76b7b2\",\n \"#59a14f\",\n \"#edc948\",\n \"#b07aa1\",\n \"#ff9da7\",\n \"#9c755f\",\n \"#bab0ac\",\n];\n\nexport function getSeriesColor(index: number, palette?: string[]): string {\n const colors = palette ?? DEFAULT_PALETTE;\n return colors[index % colors.length];\n}\n","import { useLayoutEffect, useState, type RefObject } from \"react\";\n\nexport function useChartDimensions(\n containerRef: RefObject<HTMLDivElement | null>,\n providedWidth?: number,\n providedHeight?: number\n): { width: number; height: number } {\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n }>({\n width: providedWidth ?? 0,\n height: providedHeight ?? 0,\n });\n\n const isFixed =\n providedWidth !== undefined && providedHeight !== undefined;\n\n useLayoutEffect(() => {\n if (isFixed) {\n setDimensions({ width: providedWidth!, height: providedHeight! });\n return;\n }\n\n const node = containerRef.current;\n if (!node) return;\n\n const measure = () => {\n const rect = node.getBoundingClientRect();\n setDimensions({ width: rect.width, height: rect.height });\n };\n\n measure();\n\n const observer = new ResizeObserver(() => {\n measure();\n });\n observer.observe(node);\n\n return () => observer.disconnect();\n }, [containerRef, isFixed, providedWidth, providedHeight]);\n\n return dimensions;\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function niceNumber(range: number, round: boolean): number {\n const exponent = Math.floor(Math.log10(range));\n const fraction = range / Math.pow(10, exponent);\n\n let niceFraction: number;\n if (round) {\n if (fraction < 1.5) niceFraction = 1;\n else if (fraction < 3) niceFraction = 2;\n else if (fraction < 7) niceFraction = 5;\n else niceFraction = 10;\n } else {\n if (fraction <= 1) niceFraction = 1;\n else if (fraction <= 2) niceFraction = 2;\n else if (fraction <= 5) niceFraction = 5;\n else niceFraction = 10;\n }\n\n return niceFraction * Math.pow(10, exponent);\n}\n\nexport function niceRange(\n min: number,\n max: number,\n tickCount: number\n): { niceMin: number; niceMax: number; tickStep: number } {\n if (min === max) {\n const offset = min === 0 ? 1 : Math.abs(min) * 0.1;\n min = min - offset;\n max = max + offset;\n }\n\n const range = niceNumber(max - min, false);\n const tickStep = niceNumber(range / (tickCount - 1), true);\n const niceMin = Math.floor(min / tickStep) * tickStep;\n const niceMax = Math.ceil(max / tickStep) * tickStep;\n\n return { niceMin, niceMax, tickStep };\n}\n\nexport function linearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (value: number) => number {\n return (value: number) => {\n if (domainMax === domainMin) return (rangeMin + rangeMax) / 2;\n const t = inverseLerp(domainMin, domainMax, value);\n return lerp(rangeMin, rangeMax, t);\n };\n}\n\nexport function generateTicks(\n niceMin: number,\n niceMax: number,\n tickStep: number\n): number[] {\n const ticks: number[] = [];\n for (let v = niceMin; v <= niceMax + tickStep * 0.5; v += tickStep) {\n ticks.push(parseFloat(v.toPrecision(12)));\n }\n return ticks;\n}\n\nexport function inverseLinearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (pixel: number) => number {\n return (pixel: number) => {\n if (rangeMax === rangeMin) return (domainMin + domainMax) / 2;\n const t = inverseLerp(rangeMin, rangeMax, pixel);\n return lerp(domainMin, domainMax, t);\n };\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange } from \"../utils/math\";\n\nexport interface DataDomain {\n fullXDomain: [number, number];\n fullYDomain: [number, number];\n}\n\nexport function useDataDomain(\n data: DataSeries[],\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig\n): DataDomain {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n const xNice = niceRange(xMin, xMax, xTickCount);\n const yNice = niceRange(yMin, yMax, yTickCount);\n\n return {\n fullXDomain: [xNice.niceMin, xNice.niceMax] as [number, number],\n fullYDomain: [yNice.niceMin, yNice.niceMax] as [number, number],\n };\n }, [data, xAxisConfig, yAxisConfig]);\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange, linearScale, generateTicks } from \"../utils/math\";\n\nexport interface ScalesResult {\n xScale: (value: number) => number;\n yScale: (value: number) => number;\n xTicks: number[];\n yTicks: number[];\n xDomain: [number, number];\n yDomain: [number, number];\n}\n\nexport function useScales(\n data: DataSeries[],\n plotWidth: number,\n plotHeight: number,\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig,\n viewXDomain?: [number, number],\n viewYDomain?: [number, number]\n): ScalesResult {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n // Use viewDomain if provided (semantic zoom), otherwise compute from data\n let xDomainMin: number, xDomainMax: number;\n let yDomainMin: number, yDomainMax: number;\n\n if (viewXDomain) {\n xDomainMin = viewXDomain[0];\n xDomainMax = viewXDomain[1];\n } else {\n const xNice = niceRange(xMin, xMax, xTickCount);\n xDomainMin = xNice.niceMin;\n xDomainMax = xNice.niceMax;\n }\n\n if (viewYDomain) {\n yDomainMin = viewYDomain[0];\n yDomainMax = viewYDomain[1];\n } else {\n const yNice = niceRange(yMin, yMax, yTickCount);\n yDomainMin = yNice.niceMin;\n yDomainMax = yNice.niceMax;\n }\n\n // Compute nice ticks for the current view range\n const xViewNice = niceRange(xDomainMin, xDomainMax, xTickCount);\n const yViewNice = niceRange(yDomainMin, yDomainMax, yTickCount);\n\n const xScale = linearScale(xDomainMin, xDomainMax, 0, plotWidth);\n // Y inverted: SVG Y increases downward\n const yScale = linearScale(yDomainMin, yDomainMax, plotHeight, 0);\n\n const xTicks = generateTicks(xViewNice.niceMin, xViewNice.niceMax, xViewNice.tickStep)\n .filter((t) => t >= xDomainMin && t <= xDomainMax);\n const yTicks = generateTicks(yViewNice.niceMin, yViewNice.niceMax, yViewNice.tickStep)\n .filter((t) => t >= yDomainMin && t <= yDomainMax);\n\n return {\n xScale,\n yScale,\n xTicks,\n yTicks,\n xDomain: [xDomainMin, xDomainMax] as [number, number],\n yDomain: [yDomainMin, yDomainMax] as [number, number],\n };\n }, [data, plotWidth, plotHeight, xAxisConfig, yAxisConfig, viewXDomain, viewYDomain]);\n}\n","import { useState, useCallback, useRef } from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\nimport { clamp, lerp } from \"../utils/math\";\n\nexport interface ZoomState {\n viewXDomain: [number, number];\n viewYDomain: [number, number];\n scale: number;\n isPanning: boolean;\n showZoomHint: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n resetZoom: () => void;\n handleWheel: (e: React.WheelEvent) => void;\n handlePanStart: (e: React.MouseEvent) => void;\n handlePanMove: (e: React.MouseEvent) => void;\n handlePanEnd: () => void;\n}\n\nexport function useZoom(\n config: ZoomConfig | undefined,\n fullXDomain: [number, number],\n fullYDomain: [number, number],\n plotWidth: number,\n plotHeight: number\n): ZoomState {\n const enabled = config?.enabled ?? false;\n const minScale = config?.minScale ?? 1;\n const maxScale = config?.maxScale ?? 10;\n const step = config?.step ?? 0.5;\n const enableWheel = config?.enableWheel ?? true;\n const enablePan = config?.enablePan ?? true;\n const requireCtrlKey = config?.requireCtrlKey ?? true;\n\n const [viewXDomain, setViewXDomain] = useState<[number, number]>(fullXDomain);\n const [viewYDomain, setViewYDomain] = useState<[number, number]>(fullYDomain);\n const [isPanning, setIsPanning] = useState(false);\n const [showZoomHint, setShowZoomHint] = useState(false);\n\n const panStart = useRef({ x: 0, y: 0, xDomain: fullXDomain, yDomain: fullYDomain });\n const hintTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastFullDomain = useRef({ x: fullXDomain, y: fullYDomain });\n\n // Sync view domain when full domain changes (new data)\n if (\n fullXDomain[0] !== lastFullDomain.current.x[0] ||\n fullXDomain[1] !== lastFullDomain.current.x[1] ||\n fullYDomain[0] !== lastFullDomain.current.y[0] ||\n fullYDomain[1] !== lastFullDomain.current.y[1]\n ) {\n lastFullDomain.current = { x: fullXDomain, y: fullYDomain };\n setViewXDomain(fullXDomain);\n setViewYDomain(fullYDomain);\n }\n\n // Derived scale for UI (ratio of full range to view range)\n const fullXRange = fullXDomain[1] - fullXDomain[0];\n const viewXRange = viewXDomain[1] - viewXDomain[0];\n const scale = fullXRange > 0 && viewXRange > 0 ? fullXRange / viewXRange : 1;\n\n const clampDomain = useCallback(\n (\n domain: [number, number],\n fullDomain: [number, number]\n ): [number, number] => {\n const range = domain[1] - domain[0];\n const fullRange = fullDomain[1] - fullDomain[0];\n // Don't allow zooming out beyond full domain\n if (range >= fullRange) return fullDomain;\n // Clamp to bounds\n let min = domain[0];\n let max = domain[1];\n if (min < fullDomain[0]) {\n min = fullDomain[0];\n max = min + range;\n }\n if (max > fullDomain[1]) {\n max = fullDomain[1];\n min = max - range;\n }\n return [min, max];\n },\n []\n );\n\n const showHint = useCallback(() => {\n if (hintTimer.current) clearTimeout(hintTimer.current);\n setShowZoomHint(true);\n hintTimer.current = setTimeout(() => setShowZoomHint(false), 2000);\n }, []);\n\n const zoomByFactor = useCallback(\n (factor: number, centerXFrac?: number, centerYFrac?: number) => {\n if (!enabled) return;\n\n const cxFrac = centerXFrac ?? 0.5;\n const cyFrac = centerYFrac ?? 0.5;\n\n setViewXDomain((prev) => {\n const range = prev[1] - prev[0];\n const newRange = range / factor;\n const minRange = fullXRange / maxScale;\n const clampedRange = Math.max(newRange, minRange);\n const finalRange = Math.min(clampedRange, fullXRange);\n\n const center = lerp(prev[0], prev[1], cxFrac);\n const newMin = center - finalRange * cxFrac;\n const newMax = newMin + finalRange;\n return clampDomain([newMin, newMax], fullXDomain);\n });\n\n setViewYDomain((prev) => {\n const range = prev[1] - prev[0];\n const fullYRange = fullYDomain[1] - fullYDomain[0];\n const newRange = range / factor;\n const minRange = fullYRange / maxScale;\n const clampedRange = Math.max(newRange, minRange);\n const finalRange = Math.min(clampedRange, fullYRange);\n\n const center = lerp(prev[0], prev[1], cyFrac);\n const newMin = center - finalRange * cyFrac;\n const newMax = newMin + finalRange;\n return clampDomain([newMin, newMax], fullYDomain);\n });\n },\n [enabled, fullXDomain, fullYDomain, fullXRange, maxScale, clampDomain]\n );\n\n const zoomIn = useCallback(() => {\n const factor = 1 + step;\n zoomByFactor(factor);\n }, [step, zoomByFactor]);\n\n const zoomOut = useCallback(() => {\n const factor = 1 / (1 + step);\n zoomByFactor(factor);\n }, [step, zoomByFactor]);\n\n const resetZoom = useCallback(() => {\n setViewXDomain(fullXDomain);\n setViewYDomain(fullYDomain);\n }, [fullXDomain, fullYDomain]);\n\n const handleWheel = useCallback(\n (e: React.WheelEvent) => {\n if (!enabled || !enableWheel) return;\n\n // Ctrl+wheel guard\n if (requireCtrlKey && !e.ctrlKey && !e.metaKey) {\n showHint();\n return; // Let page scroll normally\n }\n\n e.preventDefault();\n\n const rect = (e.currentTarget as SVGElement).getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n // Compute mouse position as fraction of plot area\n // Account for margins by treating the full SVG rect\n const cxFrac = clamp(mouseX / (rect.width || 1), 0, 1);\n const cyFrac = clamp(mouseY / (rect.height || 1), 0, 1);\n\n const delta = e.deltaY < 0 ? 1 + step : 1 / (1 + step);\n zoomByFactor(delta, cxFrac, cyFrac);\n },\n [enabled, enableWheel, requireCtrlKey, step, showHint, zoomByFactor]\n );\n\n const handlePanStart = useCallback(\n (e: React.MouseEvent) => {\n if (!enabled || !enablePan || scale <= 1) return;\n if (e.button !== 0) return; // left-click only\n setIsPanning(true);\n panStart.current = {\n x: e.clientX,\n y: e.clientY,\n xDomain: viewXDomain,\n yDomain: viewYDomain,\n };\n },\n [enabled, enablePan, scale, viewXDomain, viewYDomain]\n );\n\n const handlePanMove = useCallback(\n (e: React.MouseEvent) => {\n if (!isPanning) return;\n\n const dx = e.clientX - panStart.current.x;\n const dy = e.clientY - panStart.current.y;\n\n // Convert pixel delta to domain delta\n const prevXDomain = panStart.current.xDomain;\n const prevYDomain = panStart.current.yDomain;\n const xRange = prevXDomain[1] - prevXDomain[0];\n const yRange = prevYDomain[1] - prevYDomain[0];\n\n const domainDx = -(dx / plotWidth) * xRange;\n const domainDy = (dy / plotHeight) * yRange; // Y inverted\n\n const newXDomain: [number, number] = [\n prevXDomain[0] + domainDx,\n prevXDomain[1] + domainDx,\n ];\n const newYDomain: [number, number] = [\n prevYDomain[0] + domainDy,\n prevYDomain[1] + domainDy,\n ];\n\n setViewXDomain(clampDomain(newXDomain, fullXDomain));\n setViewYDomain(clampDomain(newYDomain, fullYDomain));\n },\n [isPanning, plotWidth, plotHeight, fullXDomain, fullYDomain, clampDomain]\n );\n\n const handlePanEnd = useCallback(() => {\n setIsPanning(false);\n }, []);\n\n return {\n viewXDomain,\n viewYDomain,\n scale,\n isPanning,\n showZoomHint,\n zoomIn,\n zoomOut,\n resetZoom,\n handleWheel,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n };\n}\n","import { useState, useCallback, type RefObject } from \"react\";\nimport type { DataSeries, DataPoint, TooltipConfig } from \"../types/chart.types\";\n\nexport interface TooltipState {\n tooltipVisible: boolean;\n tooltipX: number;\n tooltipY: number;\n activePoint: DataPoint | null;\n activeSeries: DataSeries | null;\n activeSeriesId: string | null;\n handleMouseMove: (e: React.MouseEvent) => void;\n handleMouseLeave: () => void;\n}\n\nfunction findNearestByPixelX(\n sorted: DataPoint[],\n mouseX: number,\n xScale: (v: number) => number\n): DataPoint | null {\n if (sorted.length === 0) return null;\n\n let low = 0;\n let high = sorted.length - 1;\n\n // Binary search for nearest pixel X\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n if (xScale(sorted[mid].x) < mouseX) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n let nearest = sorted[low];\n let minDist = Math.abs(xScale(nearest.x) - mouseX);\n\n if (low > 0) {\n const dist = Math.abs(xScale(sorted[low - 1].x) - mouseX);\n if (dist < minDist) {\n minDist = dist;\n nearest = sorted[low - 1];\n }\n }\n\n return nearest;\n}\n\nexport function useTooltip(\n svgRef: RefObject<SVGSVGElement | null>,\n data: DataSeries[],\n xScale: (v: number) => number,\n yScale: (v: number) => number,\n marginLeft: number,\n marginTop: number,\n plotWidth: number,\n _config?: TooltipConfig\n): TooltipState {\n const [tooltipVisible, setTooltipVisible] = useState(false);\n const [tooltipX, setTooltipX] = useState(0);\n const [tooltipY, setTooltipY] = useState(0);\n const [activePoint, setActivePoint] = useState<DataPoint | null>(null);\n const [activeSeries, setActiveSeries] = useState<DataSeries | null>(null);\n const [activeSeriesId, setActiveSeriesId] = useState<string | null>(null);\n\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - marginLeft;\n\n if (mouseX < 0 || mouseX > plotWidth) {\n setTooltipVisible(false);\n setActiveSeriesId(null);\n return;\n }\n\n let closestPoint: DataPoint | null = null;\n let closestSeries: DataSeries | null = null;\n let closestDist = Infinity;\n\n for (const series of data) {\n const nearest = findNearestByPixelX(series.data, mouseX, xScale);\n if (!nearest) continue;\n\n const dist = Math.abs(xScale(nearest.x) - mouseX);\n if (dist < closestDist) {\n closestDist = dist;\n closestPoint = nearest;\n closestSeries = series;\n }\n }\n\n if (closestPoint && closestSeries) {\n setTooltipX(xScale(closestPoint.x));\n setTooltipY(yScale(closestPoint.y));\n setActivePoint(closestPoint);\n setActiveSeries(closestSeries);\n setActiveSeriesId(closestSeries.id);\n setTooltipVisible(true);\n }\n },\n [svgRef, data, xScale, yScale, marginLeft, plotWidth]\n );\n\n const handleMouseLeave = useCallback(() => {\n setTooltipVisible(false);\n setActivePoint(null);\n setActiveSeries(null);\n setActiveSeriesId(null);\n }, []);\n\n return {\n tooltipVisible,\n tooltipX,\n tooltipY,\n activePoint,\n activeSeries,\n activeSeriesId,\n handleMouseMove,\n handleMouseLeave,\n };\n}\n","import React from \"react\";\nimport type { AxisConfig, ChartStyle } from \"../types/chart.types\";\n\ninterface AxesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n xAxisConfig?: AxisConfig;\n yAxisConfig?: AxisConfig;\n style?: ChartStyle;\n}\n\nconst defaultTickFormat = (v: number): string => {\n if (Math.abs(v) >= 1e6) return `${(v / 1e6).toFixed(1)}M`;\n if (Math.abs(v) >= 1e3) return `${(v / 1e3).toFixed(1)}K`;\n return Number.isInteger(v) ? v.toString() : v.toFixed(1);\n};\n\nexport const Axes: React.FC<AxesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n style,\n }) => {\n const axisColor = style?.axisColor ?? \"#333\";\n const tickColor = style?.tickColor ?? \"#666\";\n const fontFamily = style?.fontFamily ?? \"sans-serif\";\n const fontSize = style?.fontSize ?? 11;\n const xTickFormat = xAxisConfig?.tickFormat ?? defaultTickFormat;\n const yTickFormat = yAxisConfig?.tickFormat ?? defaultTickFormat;\n\n return (\n <g className=\"chartifypdf-axes\">\n {/* X Axis */}\n <line\n x1={0}\n y1={plotHeight}\n x2={plotWidth}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <g key={`x-tick-${tick}`}>\n <line\n x1={x}\n y1={plotHeight}\n x2={x}\n y2={plotHeight + 6}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={x}\n y={plotHeight + 18}\n textAnchor=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {xTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {xAxisConfig?.label && (\n <text\n x={plotWidth / 2}\n y={plotHeight + 38}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n >\n {xAxisConfig.label}\n </text>\n )}\n\n {/* Y Axis */}\n <line\n x1={0}\n y1={0}\n x2={0}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={-6}\n y1={y}\n x2={0}\n y2={y}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={-10}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {yTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {yAxisConfig?.label && (\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n transform={`translate(${-40}, ${plotHeight / 2}) rotate(-90)`}\n >\n {yAxisConfig.label}\n </text>\n )}\n </g>\n );\n }\n);\n\nAxes.displayName = \"Axes\";\n","import React from \"react\";\n\ninterface GridLinesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n showXGrid: boolean;\n showYGrid: boolean;\n xGridColor?: string;\n yGridColor?: string;\n}\n\nexport const GridLines: React.FC<GridLinesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n showXGrid,\n showYGrid,\n xGridColor = \"#e0e0e0\",\n yGridColor = \"#e0e0e0\",\n }) => {\n return (\n <g className=\"chartifypdf-grid\">\n {showYGrid &&\n yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <line\n key={`y-grid-${tick}`}\n x1={0}\n y1={y}\n x2={plotWidth}\n y2={y}\n stroke={yGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n {showXGrid &&\n xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <line\n key={`x-grid-${tick}`}\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke={xGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n </g>\n );\n }\n);\n\nGridLines.displayName = \"GridLines\";\n","import type { CurveType } from \"../types/chart.types\";\n\nexport function buildLinePath(\n points: { x: number; y: number }[]\n): string {\n if (points.length === 0) return \"\";\n const [first, ...rest] = points;\n return `M${first.x},${first.y}` + rest.map((p) => `L${p.x},${p.y}`).join(\"\");\n}\n\n/**\n * Fritsch-Carlson monotone cubic interpolation.\n * Guarantees no overshoot — ideal for data visualization.\n */\nfunction buildMonotonePath(points: { x: number; y: number }[]): string {\n const n = points.length;\n if (n < 2) return buildLinePath(points);\n if (n === 2) return buildLinePath(points);\n\n // Compute slopes of secant lines\n const dx: number[] = [];\n const dy: number[] = [];\n const m: number[] = []; // slopes of secants\n for (let i = 0; i < n - 1; i++) {\n dx.push(points[i + 1].x - points[i].x);\n dy.push(points[i + 1].y - points[i].y);\n m.push(dx[i] === 0 ? 0 : dy[i] / dx[i]);\n }\n\n // Compute tangent slopes using Fritsch-Carlson method\n const tangents: number[] = new Array(n);\n tangents[0] = m[0];\n tangents[n - 1] = m[n - 2];\n\n for (let i = 1; i < n - 1; i++) {\n if (m[i - 1] * m[i] <= 0) {\n // Local extremum or flat — zero tangent\n tangents[i] = 0;\n } else {\n // Harmonic mean of adjacent slopes\n tangents[i] = 2 / (1 / m[i - 1] + 1 / m[i]);\n }\n }\n\n // Clamp tangents to prevent overshoot (Fritsch-Carlson)\n for (let i = 0; i < n - 1; i++) {\n if (m[i] === 0) {\n tangents[i] = 0;\n tangents[i + 1] = 0;\n } else {\n const alpha = tangents[i] / m[i];\n const beta = tangents[i + 1] / m[i];\n // Ensure within monotone bounds\n const s = alpha * alpha + beta * beta;\n if (s > 9) {\n const tau = 3 / Math.sqrt(s);\n tangents[i] = tau * alpha * m[i];\n tangents[i + 1] = tau * beta * m[i];\n }\n }\n }\n\n // Build SVG path with cubic bezier segments\n let d = `M${points[0].x},${points[0].y}`;\n for (let i = 0; i < n - 1; i++) {\n const segLen = dx[i] / 3;\n const cp1x = points[i].x + segLen;\n const cp1y = points[i].y + tangents[i] * segLen;\n const cp2x = points[i + 1].x - segLen;\n const cp2y = points[i + 1].y - tangents[i + 1] * segLen;\n d += `C${cp1x},${cp1y},${cp2x},${cp2y},${points[i + 1].x},${points[i + 1].y}`;\n }\n\n return d;\n}\n\n/**\n * Natural cubic spline interpolation.\n * Smoother than monotone but may overshoot.\n */\nfunction buildNaturalPath(points: { x: number; y: number }[]): string {\n const n = points.length;\n if (n < 2) return buildLinePath(points);\n if (n === 2) return buildLinePath(points);\n\n // Solve tridiagonal system for natural cubic spline\n // For each coordinate axis independently\n function solveSpline(vals: number[]): { cp1: number; cp2: number }[] {\n const len = vals.length - 1;\n\n // Thomas algorithm for tridiagonal system\n const a: number[] = new Array(len);\n const b: number[] = new Array(len);\n const r: number[] = new Array(len);\n\n // Natural spline boundary: second derivative = 0 at endpoints\n a[0] = 0;\n b[0] = 2;\n r[0] = vals[0] + 2 * vals[1];\n\n for (let i = 1; i < len - 1; i++) {\n a[i] = 1;\n b[i] = 4;\n r[i] = 4 * vals[i] + 2 * vals[i + 1];\n }\n\n a[len - 1] = 2;\n b[len - 1] = 7;\n r[len - 1] = 8 * vals[len - 1] + vals[len];\n\n // Forward sweep\n for (let i = 1; i < len; i++) {\n const m = a[i] / b[i - 1];\n b[i] -= m;\n r[i] -= m * r[i - 1];\n }\n\n // Back substitution — first control points\n const cp1: number[] = new Array(len);\n cp1[len - 1] = r[len - 1] / b[len - 1];\n for (let i = len - 2; i >= 0; i--) {\n cp1[i] = (r[i] - cp1[i + 1]) / b[i];\n }\n\n // Second control points\n const cp2: number[] = new Array(len);\n for (let i = 0; i < len - 1; i++) {\n cp2[i] = 2 * vals[i + 1] - cp1[i + 1];\n }\n cp2[len - 1] = (vals[len] + cp1[len - 1]) / 2;\n\n const result: { cp1: number; cp2: number }[] = [];\n for (let i = 0; i < len; i++) {\n result.push({ cp1: cp1[i], cp2: cp2[i] });\n }\n return result;\n }\n\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n\n const xCPs = solveSpline(xs);\n const yCPs = solveSpline(ys);\n\n let d = `M${points[0].x},${points[0].y}`;\n for (let i = 0; i < n - 1; i++) {\n d += `C${xCPs[i].cp1},${yCPs[i].cp1},${xCPs[i].cp2},${yCPs[i].cp2},${points[i + 1].x},${points[i + 1].y}`;\n }\n\n return d;\n}\n\nexport function buildSmoothPath(\n points: { x: number; y: number }[],\n curveType: CurveType\n): string {\n switch (curveType) {\n case \"monotone\":\n return buildMonotonePath(points);\n case \"natural\":\n return buildNaturalPath(points);\n case \"linear\":\n default:\n return buildLinePath(points);\n }\n}\n","import React, { useRef, useEffect, useState } from \"react\";\nimport type { DataSeries, DataPoint, AnimationConfig, CurveType } from \"../types/chart.types\";\nimport { buildLinePath, buildSmoothPath } from \"../utils/path\";\n\ninterface LinePathProps {\n series: DataSeries;\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n color: string;\n animation?: AnimationConfig;\n onPointClick?: (point: DataPoint, series: DataSeries) => void;\n curveType?: CurveType;\n highlightOpacity?: number;\n}\n\nexport const LinePath: React.FC<LinePathProps> = React.memo(\n ({ series, xScale, yScale, color, animation, onPointClick, curveType, highlightOpacity }) => {\n const pathRef = useRef<SVGPathElement>(null);\n const [pathLength, setPathLength] = useState(0);\n const [animationDone, setAnimationDone] = useState(!animation?.enabled);\n\n const strokeWidth = series.strokeWidth ?? 2;\n const showDots = series.showDots ?? false;\n const dotRadius = series.dotRadius ?? 3.5;\n const effectiveCurve = series.curveType ?? curveType ?? \"linear\";\n const opacity = highlightOpacity ?? 1;\n\n const points = series.data.map((pt) => ({\n x: xScale(pt.x),\n y: yScale(pt.y),\n }));\n\n const d =\n effectiveCurve === \"linear\"\n ? buildLinePath(points)\n : buildSmoothPath(points, effectiveCurve);\n\n useEffect(() => {\n if (animation?.enabled && pathRef.current) {\n const length = pathRef.current.getTotalLength();\n setPathLength(length);\n setAnimationDone(false);\n\n const timer = setTimeout(() => {\n setAnimationDone(true);\n }, animation.duration ?? 800);\n\n return () => clearTimeout(timer);\n }\n }, [d, animation?.enabled, animation?.duration]);\n\n const animStyle: React.CSSProperties =\n animation?.enabled && pathLength > 0\n ? {\n strokeDasharray: pathLength,\n strokeDashoffset: animationDone ? 0 : pathLength,\n transition: `stroke-dashoffset ${animation.duration ?? 800}ms ${animation.easing ?? \"ease-in-out\"}`,\n }\n : {};\n\n return (\n <g\n className=\"chartifypdf-line-path\"\n style={{\n opacity,\n transition: \"opacity 150ms ease\",\n }}\n >\n <path\n ref={pathRef}\n d={d}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={series.strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={animStyle}\n />\n {showDots &&\n series.data.map((pt, i) => (\n <circle\n key={`dot-${series.id}-${i}`}\n cx={xScale(pt.x)}\n cy={yScale(pt.y)}\n r={dotRadius}\n fill={color}\n stroke=\"#fff\"\n strokeWidth={1.5}\n style={{ cursor: onPointClick ? \"pointer\" : \"default\" }}\n onClick={\n onPointClick\n ? () => onPointClick(pt, series)\n : undefined\n }\n />\n ))}\n </g>\n );\n }\n);\n\nLinePath.displayName = \"LinePath\";\n","import React from \"react\";\nimport type { DataPoint, DataSeries, TooltipConfig } from \"../types/chart.types\";\n\ninterface TooltipProps {\n visible: boolean;\n x: number;\n y: number;\n point: DataPoint | null;\n series: DataSeries | null;\n plotHeight: number;\n plotWidth: number;\n config?: TooltipConfig;\n seriesColor: string;\n}\n\nexport const Tooltip: React.FC<TooltipProps> = React.memo(\n ({ visible, x, y, point, series, plotHeight, plotWidth, config, seriesColor }) => {\n if (!visible || !point || !series) return null;\n\n const bgColor = config?.backgroundColor ?? \"rgba(0, 0, 0, 0.8)\";\n const textColor = config?.textColor ?? \"#fff\";\n\n // Determine tooltip text\n let content: React.ReactNode;\n if (config?.renderCustom) {\n content = config.renderCustom(point, series);\n } else {\n const text = config?.formatter\n ? config.formatter(point, series)\n : `${series.name}: (${point.x}, ${point.y})`;\n content = text;\n }\n\n // Smart positioning: flip when near edges\n const tooltipWidth = 140;\n const tooltipHeight = 40;\n let tx = x + 12;\n let ty = y - tooltipHeight - 8;\n\n if (tx + tooltipWidth > plotWidth) {\n tx = x - tooltipWidth - 12;\n }\n if (ty < 0) {\n ty = y + 12;\n }\n\n return (\n <g className=\"chartifypdf-tooltip\" pointerEvents=\"none\">\n {/* Vertical indicator line */}\n <line\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke=\"#999\"\n strokeWidth={1}\n strokeDasharray=\"4 4\"\n opacity={0.6}\n />\n {/* Highlight circle */}\n <circle\n cx={x}\n cy={y}\n r={5}\n fill={seriesColor}\n stroke=\"#fff\"\n strokeWidth={2}\n />\n {/* Tooltip box using foreignObject */}\n <foreignObject\n x={tx}\n y={ty}\n width={tooltipWidth}\n height={tooltipHeight + 20}\n overflow=\"visible\"\n >\n <div\n style={{\n backgroundColor: bgColor,\n color: textColor,\n padding: \"6px 10px\",\n borderRadius: \"4px\",\n fontSize: \"12px\",\n fontFamily: \"sans-serif\",\n whiteSpace: \"nowrap\",\n lineHeight: 1.4,\n boxShadow: \"0 2px 6px rgba(0,0,0,0.2)\",\n }}\n >\n {content}\n </div>\n </foreignObject>\n </g>\n );\n }\n);\n\nTooltip.displayName = \"Tooltip\";\n","import React from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\n\ninterface ZoomControlsProps {\n config?: ZoomConfig;\n svgWidth: number;\n svgHeight: number;\n margin: { top: number; right: number; bottom: number; left: number };\n scale: number;\n onZoomIn: () => void;\n onZoomOut: () => void;\n onReset: () => void;\n}\n\nexport const ZoomControls: React.FC<ZoomControlsProps> = React.memo(\n ({ config, svgWidth, svgHeight, margin, scale, onZoomIn, onZoomOut, onReset }) => {\n if (!config?.enabled || config.showControls === false) return null;\n\n const position = config.controlsPosition ?? \"top-right\";\n const btnSize = 24;\n const gap = 4;\n const groupWidth = btnSize * 3 + gap * 2;\n const groupHeight = btnSize;\n const padding = 8;\n\n let gx: number;\n let gy: number;\n\n switch (position) {\n case \"top-left\":\n gx = margin.left + padding;\n gy = margin.top + padding;\n break;\n case \"bottom-left\":\n gx = margin.left + padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"bottom-right\":\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"top-right\":\n default:\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = margin.top + padding;\n break;\n }\n\n const buttons = [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2013\", onClick: onReset }, // en-dash for reset\n { label: \"\\u2212\", onClick: onZoomOut }, // minus sign\n ];\n\n // Replace middle button with reset when zoomed, or show as \"-\"\n const displayButtons =\n scale > 1\n ? [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n { label: \"\\u21BA\", onClick: onReset },\n ]\n : [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n ];\n\n return (\n <g\n className=\"chartifypdf-zoom-controls\"\n transform={`translate(${gx}, ${gy})`}\n >\n {displayButtons.map((btn, i) => (\n <g\n key={btn.label}\n transform={`translate(${i * (btnSize + gap)}, 0)`}\n onClick={btn.onClick}\n style={{ cursor: \"pointer\" }}\n >\n <rect\n width={btnSize}\n height={btnSize}\n rx={4}\n fill=\"rgba(255,255,255,0.9)\"\n stroke=\"#ccc\"\n strokeWidth={1}\n />\n <text\n x={btnSize / 2}\n y={btnSize / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={14}\n fontFamily=\"sans-serif\"\n fill=\"#333\"\n fontWeight=\"bold\"\n >\n {btn.label}\n </text>\n </g>\n ))}\n </g>\n );\n }\n);\n\nZoomControls.displayName = \"ZoomControls\";\n","import React from \"react\";\nimport type { PeakConfig, DataSeries } from \"../types/chart.types\";\n\ninterface PeakMarkersProps {\n peaks: PeakConfig[];\n data: DataSeries[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n colorPalette?: string[];\n}\n\nfunction findYAtX(data: DataSeries[], targetX: number): { y: number; seriesIndex: number } | null {\n let maxY = -Infinity;\n let bestSeriesIdx = -1;\n\n for (let si = 0; si < data.length; si++) {\n const series = data[si];\n // Find closest data point to target X\n let closest = series.data[0];\n let closestDist = Infinity;\n for (const pt of series.data) {\n const dist = Math.abs(pt.x - targetX);\n if (dist < closestDist) {\n closestDist = dist;\n closest = pt;\n }\n }\n if (closest && closest.y > maxY) {\n maxY = closest.y;\n bestSeriesIdx = si;\n }\n }\n\n if (bestSeriesIdx === -1) return null;\n return { y: maxY, seriesIndex: bestSeriesIdx };\n}\n\nfunction ArrowIcon({ x, y, color }: { x: number; y: number; color: string }) {\n return (\n <polygon\n points={`${x},${y} ${x - 5},${y - 10} ${x + 5},${y - 10}`}\n fill={color}\n />\n );\n}\n\nfunction DiamondIcon({ x, y, color }: { x: number; y: number; color: string }) {\n return (\n <polygon\n points={`${x},${y - 6} ${x + 5},${y} ${x},${y + 6} ${x - 5},${y}`}\n fill={color}\n />\n );\n}\n\nfunction StarIcon({ x, y, color }: { x: number; y: number; color: string }) {\n // 5-point star\n const r1 = 6;\n const r2 = 3;\n const pts: string[] = [];\n for (let i = 0; i < 5; i++) {\n const outerAngle = (i * 72 - 90) * (Math.PI / 180);\n const innerAngle = ((i * 72 + 36) - 90) * (Math.PI / 180);\n pts.push(`${x + r1 * Math.cos(outerAngle)},${y + r1 * Math.sin(outerAngle)}`);\n pts.push(`${x + r2 * Math.cos(innerAngle)},${y + r2 * Math.sin(innerAngle)}`);\n }\n return <polygon points={pts.join(\" \")} fill={color} />;\n}\n\nexport const PeakMarkers: React.FC<PeakMarkersProps> = React.memo(\n ({ peaks, data, xScale, yScale }) => {\n return (\n <g className=\"chartifypdf-peak-markers\">\n {peaks.map((peak, i) => {\n const result = findYAtX(data, peak.x);\n if (!result) return null;\n\n const px = xScale(peak.x);\n const py = yScale(result.y);\n const color = peak.color ?? \"#ef4444\";\n const icon = peak.icon ?? \"arrow\";\n const label = peak.label;\n\n // Position icon above the data point\n const iconY = py - 14;\n\n return (\n <g key={`peak-${i}`}>\n {/* Vertical dashed line from data point up */}\n <line\n x1={px}\n y1={py}\n x2={px}\n y2={iconY + 6}\n stroke={color}\n strokeWidth={1}\n strokeDasharray=\"3 2\"\n opacity={0.6}\n />\n {/* Icon */}\n {icon === \"arrow\" && <ArrowIcon x={px} y={iconY} color={color} />}\n {icon === \"diamond\" && <DiamondIcon x={px} y={iconY} color={color} />}\n {icon === \"star\" && <StarIcon x={px} y={iconY} color={color} />}\n {/* Label */}\n {label && (\n <text\n x={px}\n y={iconY - 8}\n textAnchor=\"middle\"\n fill={color}\n fontSize={11}\n fontWeight={600}\n >\n {label}\n </text>\n )}\n </g>\n );\n })}\n </g>\n );\n }\n);\n\nPeakMarkers.displayName = \"PeakMarkers\";\n","import React, { useEffect, useRef } from \"react\";\nimport type { DataSeries, ContextMenuConfig } from \"../types/chart.types\";\n\ninterface ContextMenuProps {\n visible: boolean;\n x: number;\n y: number;\n nearestX: number;\n data: DataSeries[];\n config?: ContextMenuConfig;\n colorPalette?: string[];\n getSeriesColor: (index: number) => string;\n onClose: () => void;\n}\n\nfunction findNearestY(series: DataSeries, targetX: number): number | null {\n if (series.data.length === 0) return null;\n let closest = series.data[0];\n let closestDist = Math.abs(closest.x - targetX);\n for (const pt of series.data) {\n const dist = Math.abs(pt.x - targetX);\n if (dist < closestDist) {\n closestDist = dist;\n closest = pt;\n }\n }\n return closest.y;\n}\n\nexport const ContextMenu: React.FC<ContextMenuProps> = ({\n visible,\n x,\n y,\n nearestX,\n data,\n config,\n getSeriesColor,\n onClose,\n}) => {\n const menuRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!visible) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n\n // Delay adding listeners to avoid immediate close from the contextmenu event\n const timer = setTimeout(() => {\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleKeyDown);\n }, 0);\n\n return () => {\n clearTimeout(timer);\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [visible, onClose]);\n\n if (!visible) return null;\n\n const bgColor = config?.backgroundColor ?? \"#ffffff\";\n const textColor = config?.textColor ?? \"#1f2937\";\n const borderColor = config?.borderColor ?? \"#e5e7eb\";\n\n const rows = data.map((series, i) => {\n const yVal = findNearestY(series, nearestX);\n return {\n name: series.name,\n color: series.color ?? getSeriesColor(i),\n x: nearestX,\n y: yVal,\n };\n });\n\n return (\n <div\n ref={menuRef}\n style={{\n position: \"fixed\",\n left: x,\n top: y,\n zIndex: 9999,\n backgroundColor: bgColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: 6,\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n padding: \"8px 0\",\n minWidth: 200,\n fontSize: 12,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n }}\n >\n <div\n style={{\n padding: \"4px 12px 8px\",\n fontWeight: 600,\n fontSize: 11,\n color: textColor,\n opacity: 0.6,\n borderBottom: `1px solid ${borderColor}`,\n marginBottom: 4,\n }}\n >\n Data at X = {typeof nearestX === \"number\" ? nearestX.toLocaleString() : nearestX}\n </div>\n <table style={{ width: \"100%\", borderCollapse: \"collapse\" }}>\n <thead>\n <tr>\n <th style={{ textAlign: \"left\", padding: \"4px 12px\", fontWeight: 600, opacity: 0.7 }}>\n Series\n </th>\n <th style={{ textAlign: \"right\", padding: \"4px 12px\", fontWeight: 600, opacity: 0.7 }}>\n Y\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr\n key={i}\n style={{\n borderTop: i > 0 ? `1px solid ${borderColor}` : undefined,\n }}\n >\n <td style={{ padding: \"4px 12px\", display: \"flex\", alignItems: \"center\", gap: 6 }}>\n <span\n style={{\n display: \"inline-block\",\n width: 8,\n height: 8,\n borderRadius: \"50%\",\n backgroundColor: row.color,\n flexShrink: 0,\n }}\n />\n {row.name}\n </td>\n <td style={{ padding: \"4px 12px\", textAlign: \"right\", fontVariantNumeric: \"tabular-nums\" }}>\n {row.y !== null ? row.y.toLocaleString() : \"—\"}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\nContextMenu.displayName = \"ContextMenu\";\n","import React, { useRef, useId, useState, useCallback } from \"react\";\nimport type {\n LineChartProps,\n ChartMargin,\n DataPoint,\n} from \"../types/chart.types\";\nimport { getSeriesColor } from \"../utils/color\";\nimport { useChartDimensions } from \"../hooks/useChartDimensions\";\nimport { useDataDomain } from \"../hooks/useDataDomain\";\nimport { useScales } from \"../hooks/useScales\";\nimport { useZoom } from \"../hooks/useZoom\";\nimport { useTooltip } from \"../hooks/useTooltip\";\nimport { Axes } from \"./Axes\";\nimport { GridLines } from \"./GridLines\";\nimport { LinePath } from \"./LinePath\";\nimport { Tooltip } from \"./Tooltip\";\nimport { ZoomControls } from \"./ZoomControls\";\nimport { PeakMarkers } from \"./PeakMarkers\";\nimport { ContextMenu } from \"./ContextMenu\";\n\nconst DEFAULT_MARGIN: ChartMargin = {\n top: 20,\n right: 20,\n bottom: 50,\n left: 60,\n};\n\nconst DEFAULT_HEIGHT = 400;\n\nexport const LineChart: React.FC<LineChartProps> = ({\n data,\n width: providedWidth,\n height: providedHeight,\n margin: marginOverride,\n xAxis: xAxisConfig,\n yAxis: yAxisConfig,\n tooltip: tooltipConfig,\n zoom: zoomConfig,\n style: chartStyle,\n animation,\n colorPalette,\n className,\n onPointClick,\n ariaLabel,\n curveType: globalCurveType,\n peaks,\n contextMenu: contextMenuConfig,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const clipId = useId();\n\n const margin: ChartMargin = {\n ...DEFAULT_MARGIN,\n ...marginOverride,\n };\n\n const dimensions = useChartDimensions(\n containerRef,\n providedWidth,\n providedHeight ?? DEFAULT_HEIGHT\n );\n const { width: svgWidth, height: svgHeight } = dimensions;\n\n const plotWidth = Math.max(0, svgWidth - margin.left - margin.right);\n const plotHeight = Math.max(0, svgHeight - margin.top - margin.bottom);\n\n // 1. Extract full domain from data\n const { fullXDomain, fullYDomain } = useDataDomain(data, xAxisConfig, yAxisConfig);\n\n // 2. Domain-based zoom\n const zoomState = useZoom(zoomConfig, fullXDomain, fullYDomain, plotWidth, plotHeight);\n\n // 3. Scales from view domain (semantic zoom — ticks update automatically)\n const zoomEnabled = zoomConfig?.enabled ?? false;\n const { xScale, yScale, xTicks, yTicks } = useScales(\n data,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n zoomEnabled ? zoomState.viewXDomain : undefined,\n zoomEnabled ? zoomState.viewYDomain : undefined\n );\n\n // Tooltip\n const tooltipEnabled = tooltipConfig?.enabled !== false;\n const tooltipState = useTooltip(\n svgRef,\n data,\n xScale,\n yScale,\n margin.left,\n margin.top,\n plotWidth,\n tooltipConfig\n );\n\n // Hover highlight: compute opacity per series\n const activeSeriesId = tooltipState.activeSeriesId;\n const hasHover = tooltipState.tooltipVisible && activeSeriesId !== null;\n\n const getHighlightOpacity = (seriesId: string): number => {\n if (!hasHover) return 1;\n return seriesId === activeSeriesId ? 1 : 0.2;\n };\n\n // Context menu state\n const [ctxMenu, setCtxMenu] = useState<{\n visible: boolean;\n clientX: number;\n clientY: number;\n nearestX: number;\n }>({ visible: false, clientX: 0, clientY: 0, nearestX: 0 });\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (!contextMenuConfig?.enabled) return;\n e.preventDefault();\n\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - margin.left;\n\n if (mouseX < 0 || mouseX > plotWidth) return;\n\n // Find nearest X value across all series\n let nearestX = 0;\n let nearestDist = Infinity;\n for (const series of data) {\n for (const pt of series.data) {\n const px = xScale(pt.x);\n const dist = Math.abs(px - mouseX);\n if (dist < nearestDist) {\n nearestDist = dist;\n nearestX = pt.x;\n }\n }\n }\n\n setCtxMenu({\n visible: true,\n clientX: e.clientX,\n clientY: e.clientY,\n nearestX,\n });\n },\n [contextMenuConfig?.enabled, data, xScale, margin.left, plotWidth]\n );\n\n const closeContextMenu = useCallback(() => {\n setCtxMenu((prev) => ({ ...prev, visible: false }));\n }, []);\n\n const showXGrid = xAxisConfig?.gridLines ?? false;\n const showYGrid = yAxisConfig?.gridLines ?? true;\n\n const getColor = (index: number) => getSeriesColor(index, colorPalette);\n\n // Don't render until we have dimensions\n if (svgWidth === 0 || svgHeight === 0) {\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n />\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n position: \"relative\",\n }}\n >\n <svg\n ref={svgRef}\n width={svgWidth}\n height={svgHeight}\n role=\"img\"\n aria-label={ariaLabel ?? \"Line chart\"}\n onMouseMove={\n tooltipEnabled && !zoomState.isPanning\n ? tooltipState.handleMouseMove\n : undefined\n }\n onMouseLeave={tooltipEnabled ? tooltipState.handleMouseLeave : undefined}\n onWheel={zoomEnabled ? zoomState.handleWheel : undefined}\n onMouseDown={zoomEnabled ? zoomState.handlePanStart : undefined}\n onMouseUp={zoomEnabled ? zoomState.handlePanEnd : undefined}\n onContextMenu={handleContextMenu}\n style={{\n userSelect: zoomState.isPanning ? \"none\" : undefined,\n cursor: zoomState.isPanning\n ? \"grabbing\"\n : zoomEnabled && zoomState.scale > 1\n ? \"grab\"\n : undefined,\n }}\n >\n <defs>\n <clipPath id={clipId}>\n <rect width={plotWidth} height={plotHeight} />\n </clipPath>\n </defs>\n\n <g transform={`translate(${margin.left}, ${margin.top})`}>\n {/* Axes — always crisp, ticks update with zoom */}\n <Axes\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n xAxisConfig={xAxisConfig}\n yAxisConfig={yAxisConfig}\n style={chartStyle}\n />\n\n {/* Grid lines */}\n <GridLines\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n showXGrid={showXGrid}\n showYGrid={showYGrid}\n xGridColor={xAxisConfig?.gridLineColor}\n yGridColor={yAxisConfig?.gridLineColor}\n />\n\n {/* Clipped data area — no CSS transform, scales handle zoom */}\n <g clipPath={`url(#${clipId})`}>\n {zoomEnabled ? (\n <g\n onMouseMove={zoomState.handlePanMove}\n >\n {data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={series.color ?? getColor(i)}\n animation={animation}\n onPointClick={onPointClick}\n curveType={globalCurveType}\n highlightOpacity={getHighlightOpacity(series.id)}\n />\n ))}\n </g>\n ) : (\n data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={series.color ?? getColor(i)}\n animation={animation}\n onPointClick={onPointClick}\n curveType={globalCurveType}\n highlightOpacity={getHighlightOpacity(series.id)}\n />\n ))\n )}\n\n {/* Peak markers */}\n {peaks && peaks.length > 0 && (\n <PeakMarkers\n peaks={peaks}\n data={data}\n xScale={xScale}\n yScale={yScale}\n colorPalette={colorPalette}\n />\n )}\n </g>\n\n {/* Tooltip renders above everything in plot area */}\n {tooltipEnabled && (\n <Tooltip\n visible={tooltipState.tooltipVisible}\n x={tooltipState.tooltipX}\n y={tooltipState.tooltipY}\n point={tooltipState.activePoint}\n series={tooltipState.activeSeries}\n plotHeight={plotHeight}\n plotWidth={plotWidth}\n config={tooltipConfig}\n seriesColor={\n tooltipState.activeSeries?.color ??\n getColor(\n data.findIndex(\n (s) => s.id === tooltipState.activeSeries?.id\n )\n )\n }\n />\n )}\n </g>\n\n {/* Zoom controls render at SVG root level */}\n <ZoomControls\n config={zoomConfig}\n svgWidth={svgWidth}\n svgHeight={svgHeight}\n margin={margin}\n scale={zoomState.scale}\n onZoomIn={zoomState.zoomIn}\n onZoomOut={zoomState.zoomOut}\n onReset={zoomState.resetZoom}\n />\n </svg>\n\n {/* Zoom hint overlay */}\n {zoomState.showZoomHint && (\n <div\n style={{\n position: \"absolute\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n background: \"rgba(0, 0, 0, 0.7)\",\n color: \"#fff\",\n padding: \"8px 16px\",\n borderRadius: 6,\n fontSize: 13,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n pointerEvents: \"none\",\n whiteSpace: \"nowrap\",\n zIndex: 10,\n }}\n >\n Hold Ctrl + scroll to zoom\n </div>\n )}\n\n {/* Right-click context menu */}\n {contextMenuConfig?.enabled && (\n <ContextMenu\n visible={ctxMenu.visible}\n x={ctxMenu.clientX}\n y={ctxMenu.clientY}\n nearestX={ctxMenu.nearestX}\n data={data}\n config={contextMenuConfig}\n getSeriesColor={getColor}\n onClose={closeContextMenu}\n />\n )}\n </div>\n );\n};\n\nLineChart.displayName = \"LineChart\";\n"],"names":["DEFAULT_PALETTE","useChartDimensions","containerRef","providedWidth","providedHeight","dimensions","setDimensions","useState","width","height","isFixed","undefined","useLayoutEffect","node","current","measure","rect","getBoundingClientRect","observer","ResizeObserver","observe","disconnect","clamp","value","min","max","Math","lerp","a","b","t","niceNumber","range","round","exponent","floor","log10","fraction","pow","niceFraction","niceRange","tickCount","offset","abs","tickStep","niceMin","niceMax","ceil","linearScale","domainMin","domainMax","rangeMin","rangeMax","inverseLerp","generateTicks","ticks","v","push","parseFloat","toPrecision","useDataDomain","data","xAxisConfig","yAxisConfig","useMemo","xMin","Infinity","xMax","yMin","yMax","series","point","x","y","isFinite","xTickCount","yTickCount","xNice","yNice","fullXDomain","fullYDomain","useScales","plotWidth","plotHeight","viewXDomain","viewYDomain","xDomainMin","xDomainMax","yDomainMin","yDomainMax","xViewNice","yViewNice","xScale","yScale","xTicks","filter","yTicks","xDomain","yDomain","useZoom","config","enabled","maxScale","step","enableWheel","enablePan","requireCtrlKey","setViewXDomain","setViewYDomain","isPanning","setIsPanning","showZoomHint","setShowZoomHint","panStart","useRef","hintTimer","lastFullDomain","fullXRange","viewXRange","scale","clampDomain","useCallback","domain","fullDomain","showHint","clearTimeout","setTimeout","zoomByFactor","factor","centerXFrac","centerYFrac","cxFrac","cyFrac","prev","newRange","minRange","clampedRange","finalRange","newMin","fullYRange","zoomIn","zoomOut","resetZoom","handleWheel","e","ctrlKey","metaKey","preventDefault","currentTarget","mouseX","clientX","left","mouseY","clientY","top","delta","deltaY","handlePanStart","button","handlePanMove","dx","dy","prevXDomain","prevYDomain","xRange","yRange","domainDx","domainDy","newXDomain","newYDomain","handlePanEnd","findNearestByPixelX","sorted","length","low","high","mid","nearest","minDist","dist","useTooltip","svgRef","marginLeft","marginTop","_config","tooltipVisible","setTooltipVisible","tooltipX","setTooltipX","tooltipY","setTooltipY","activePoint","setActivePoint","activeSeries","setActiveSeries","activeSeriesId","setActiveSeriesId","handleMouseMove","svg","closestPoint","closestSeries","closestDist","id","handleMouseLeave","defaultTickFormat","toFixed","Number","isInteger","toString","Axes","React","memo","style","axisColor","tickColor","fontFamily","fontSize","xTickFormat","tickFormat","yTickFormat","_jsxs","className","_jsx","x1","y1","x2","y2","stroke","strokeWidth","map","tick","children","textAnchor","fill","label","fontWeight","dominantBaseline","transform","displayName","GridLines","showXGrid","showYGrid","xGridColor","yGridColor","strokeDasharray","strokeOpacity","buildLinePath","points","first","rest","p","join","buildSmoothPath","curveType","n","m","i","tangents","Array","alpha","beta","s","tau","sqrt","d","segLen","buildMonotonePath","solveSpline","vals","len","r","cp1","cp2","result","xs","ys","xCPs","yCPs","buildNaturalPath","LinePath","color","animation","onPointClick","highlightOpacity","pathRef","pathLength","setPathLength","animationDone","setAnimationDone","showDots","dotRadius","effectiveCurve","opacity","pt","useEffect","getTotalLength","timer","duration","animStyle","strokeDashoffset","transition","easing","ref","strokeLinecap","strokeLinejoin","cx","cy","cursor","onClick","Tooltip","visible","seriesColor","bgColor","backgroundColor","textColor","content","renderCustom","formatter","name","tx","ty","pointerEvents","tooltipHeight","overflow","padding","borderRadius","whiteSpace","lineHeight","boxShadow","ZoomControls","svgWidth","svgHeight","margin","onZoomIn","onZoomOut","onReset","showControls","position","controlsPosition","btnSize","gx","gy","bottom","right","displayButtons","btn","rx","ArrowIcon","DiamondIcon","StarIcon","pts","outerAngle","PI","innerAngle","cos","sin","PeakMarkers","peaks","peak","targetX","maxY","bestSeriesIdx","si","closest","seriesIndex","findYAtX","px","py","icon","iconY","ContextMenu","nearestX","getSeriesColor","onClose","menuRef","handleClickOutside","contains","target","handleKeyDown","key","document","addEventListener","removeEventListener","borderColor","rows","yVal","findNearestY","zIndex","border","minWidth","borderBottom","marginBottom","toLocaleString","borderCollapse","textAlign","row","borderTop","display","alignItems","gap","flexShrink","fontVariantNumeric","DEFAULT_MARGIN","LineChart","marginOverride","xAxis","yAxis","tooltip","tooltipConfig","zoom","zoomConfig","chartStyle","colorPalette","ariaLabel","globalCurveType","contextMenu","contextMenuConfig","clipId","useId","zoomState","zoomEnabled","tooltipEnabled","tooltipState","hasHover","getHighlightOpacity","seriesId","ctxMenu","setCtxMenu","handleContextMenu","nearestDist","closeContextMenu","gridLines","getColor","index","palette","colors","role","onMouseMove","onMouseLeave","onWheel","onMouseDown","onMouseUp","onContextMenu","userSelect","gridLineColor","clipPath","findIndex","background"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsxs as e,jsx as t}from"react/jsx-runtime";import o,{useState as n,useLayoutEffect as i,useMemo as r,useRef as l,useCallback as a,useEffect as s,useId as c}from"react";const d=["#4e79a7","#f28e2b","#e15759","#76b7b2","#59a14f","#edc948","#b07aa1","#ff9da7","#9c755f","#bab0ac"];function h(e,t){const o=t??d;return o[e%o.length]}function f(e,t,o){const[r,l]=n({width:t??0,height:o??0}),a=void 0!==t&&void 0!==o;return i(()=>{if(a)return void l({width:t,height:o});const n=e.current;if(!n)return;const i=()=>{const e=n.getBoundingClientRect();l({width:e.width,height:e.height})};i();const r=new ResizeObserver(()=>{i()});return r.observe(n),()=>r.disconnect()},[e,a,t,o]),r}function u(e,t,o){return Math.min(Math.max(e,t),o)}function x(e,t){const o=Math.floor(Math.log10(e)),n=e/Math.pow(10,o);let i;return i=t?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10,i*Math.pow(10,o)}function m(e,t,o){if(e===t){const o=0===e?1:.1*Math.abs(e);e-=o,t+=o}const n=x(t-e,!1),i=x(n/(o-1),!0);return{niceMin:Math.floor(e/i)*i,niceMax:Math.ceil(t/i)*i,tickStep:i}}function y(e,t,o,n){return i=>{if(t===e)return(o+n)/2;const r=function(e,t,o){return e===t?0:(o-e)/(t-e)}(e,t,i);return function(e,t,o){return e+(t-e)*o}(o,n,r)}}function g(e,t,o){const n=[];for(let i=e;i<=t+.5*o;i+=o)n.push(parseFloat(i.toPrecision(12)));return n}function p(e,t,o,n,i){return r(()=>{let r=1/0,l=-1/0,a=1/0,s=-1/0;for(const t of e)for(const e of t.data)e.x<r&&(r=e.x),e.x>l&&(l=e.x),e.y<a&&(a=e.y),e.y>s&&(s=e.y);isFinite(r)||(r=0,l=1,a=0,s=1),void 0!==n?.min&&(r=n.min),void 0!==n?.max&&(l=n.max),void 0!==i?.min&&(a=i.min),void 0!==i?.max&&(s=i.max);const c=n?.tickCount??6,d=i?.tickCount??6,h=m(r,l,c),f=m(a,s,d);return{xScale:y(h.niceMin,h.niceMax,0,t),yScale:y(f.niceMin,f.niceMax,o,0),xTicks:g(h.niceMin,h.niceMax,h.tickStep),yTicks:g(f.niceMin,f.niceMax,f.tickStep),xDomain:[h.niceMin,h.niceMax],yDomain:[f.niceMin,f.niceMax]}},[e,t,o,n,i])}function b(e,t,o){const i=e?.enabled??!1,r=e?.minScale??1,s=e?.maxScale??10,c=e?.step??.5,d=e?.enableWheel??!0,h=e?.enablePan??!0,[f,x]=n(1),[m,y]=n(0),[g,p]=n(0),[b,k]=n(!1),M=l({x:0,y:0,offsetX:0,offsetY:0}),v=a((e,n,i)=>{const r=o*(i-1);return{x:u(e,-(t*(i-1)),0),y:u(n,-r,0)}},[t,o]),C=a(()=>{i&&x(e=>{const n=u(e+c,r,s);return y(e=>v(e-t*c/2,0,n).x),p(e=>v(0,e-o*c/2,n).y),n})},[i,c,r,s,t,o,v]),S=a(()=>{i&&x(e=>{const n=u(e-c,r,s);return y(e=>v(e+t*c/2,0,n).x),p(e=>v(0,e+o*c/2,n).y),n})},[i,c,r,s,t,o,v]),w=a(()=>{x(1),y(0),p(0)},[]),$=a(e=>{if(!i||!d)return;e.preventDefault();const t=e.currentTarget.getBoundingClientRect(),o=e.clientX-t.left,n=e.clientY-t.top,l=e.deltaY<0?c:-c;x(e=>{const t=u(e+l,r,s),i=t/e;return y(e=>v(o-i*(o-e),0,t).x),p(e=>v(0,n-i*(n-e),t).y),t})},[i,d,c,r,s,v]),W=a(e=>{!i||!h||f<=1||0===e.button&&(k(!0),M.current={x:e.clientX,y:e.clientY,offsetX:m,offsetY:g})},[i,h,f,m,g]),P=a(e=>{if(!b)return;const t=e.clientX-M.current.x,o=e.clientY-M.current.y,n=v(M.current.offsetX+t,M.current.offsetY+o,f);y(n.x),p(n.y)},[b,f,v]),T=a(()=>{k(!1)},[]);return{scale:f,offsetX:m,offsetY:g,isPanning:b,zoomIn:C,zoomOut:S,resetZoom:w,handleWheel:$,handlePanStart:W,handlePanMove:P,handlePanEnd:T}}function k(e,t,o){if(0===e.length)return null;let n=0,i=e.length-1;for(;n<i;){const r=Math.floor((n+i)/2);o(e[r].x)<t?n=r+1:i=r}let r=e[n],l=Math.abs(o(r.x)-t);if(n>0){const i=Math.abs(o(e[n-1].x)-t);i<l&&(l=i,r=e[n-1])}return r}function M(e,t,o,i,r,l,s,c){const[d,h]=n(!1),[f,u]=n(0),[x,m]=n(0),[y,g]=n(null),[p,b]=n(null);return{tooltipVisible:d,tooltipX:f,tooltipY:x,activePoint:y,activeSeries:p,handleMouseMove:a(n=>{const l=e.current;if(!l)return;const a=l.getBoundingClientRect(),c=n.clientX-a.left-r;if(c<0||c>s)return void h(!1);let d=null,f=null,x=1/0;for(const e of t){const t=k(e.data,c,o);if(!t)continue;const n=Math.abs(o(t.x)-c);n<x&&(x=n,d=t,f=e)}d&&f&&(u(o(d.x)),m(i(d.y)),g(d),b(f),h(!0))},[e,t,o,i,r,s]),handleMouseLeave:a(()=>{h(!1),g(null),b(null)},[])}}const v=e=>Math.abs(e)>=1e6?`${(e/1e6).toFixed(1)}M`:Math.abs(e)>=1e3?`${(e/1e3).toFixed(1)}K`:Number.isInteger(e)?e.toString():e.toFixed(1),C=o.memo(({xTicks:o,yTicks:n,xScale:i,yScale:r,plotWidth:l,plotHeight:a,xAxisConfig:s,yAxisConfig:c,style:d})=>{const h=d?.axisColor??"#333",f=d?.tickColor??"#666",u=d?.fontFamily??"sans-serif",x=d?.fontSize??11,m=s?.tickFormat??v,y=c?.tickFormat??v;return e("g",{className:"chartifypdf-axes",children:[t("line",{x1:0,y1:a,x2:l,y2:a,stroke:h,strokeWidth:1}),o.map(o=>{const n=i(o);return e("g",{children:[t("line",{x1:n,y1:a,x2:n,y2:a+6,stroke:h,strokeWidth:1}),t("text",{x:n,y:a+18,textAnchor:"middle",fill:f,fontFamily:u,fontSize:x,children:m(o)})]},`x-tick-${o}`)}),s?.label&&t("text",{x:l/2,y:a+38,textAnchor:"middle",fill:h,fontFamily:u,fontSize:x+1,fontWeight:"bold",children:s.label}),t("line",{x1:0,y1:0,x2:0,y2:a,stroke:h,strokeWidth:1}),n.map(o=>{const n=r(o);return e("g",{children:[t("line",{x1:-6,y1:n,x2:0,y2:n,stroke:h,strokeWidth:1}),t("text",{x:-10,y:n,textAnchor:"end",dominantBaseline:"middle",fill:f,fontFamily:u,fontSize:x,children:y(o)})]},`y-tick-${o}`)}),c?.label&&t("text",{x:0,y:0,textAnchor:"middle",fill:h,fontFamily:u,fontSize:x+1,fontWeight:"bold",transform:`translate(-40, ${a/2}) rotate(-90)`,children:c.label})]})});C.displayName="Axes";const S=o.memo(({xTicks:o,yTicks:n,xScale:i,yScale:r,plotWidth:l,plotHeight:a,showXGrid:s,showYGrid:c,xGridColor:d="#e0e0e0",yGridColor:h="#e0e0e0"})=>e("g",{className:"chartifypdf-grid",children:[c&&n.map(e=>{const o=r(e);return t("line",{x1:0,y1:o,x2:l,y2:o,stroke:h,strokeDasharray:"4 4",strokeOpacity:.5},`y-grid-${e}`)}),s&&o.map(e=>{const o=i(e);return t("line",{x1:o,y1:0,x2:o,y2:a,stroke:d,strokeDasharray:"4 4",strokeOpacity:.5},`x-grid-${e}`)})]}));S.displayName="GridLines";const w=o.memo(({series:o,xScale:i,yScale:r,color:a,animation:c,onPointClick:d})=>{const h=l(null),[f,u]=n(0),[x,m]=n(!c?.enabled),y=o.strokeWidth??2,g=o.showDots??!1,p=o.dotRadius??3.5,b=function(e){if(0===e.length)return"";const[t,...o]=e;return`M${t.x},${t.y}`+o.map(e=>`L${e.x},${e.y}`).join("")}(o.data.map(e=>({x:i(e.x),y:r(e.y)})));s(()=>{if(c?.enabled&&h.current){const e=h.current.getTotalLength();u(e),m(!1);const t=setTimeout(()=>{m(!0)},c.duration??800);return()=>clearTimeout(t)}},[b,c?.enabled,c?.duration]);const k=c?.enabled&&f>0?{strokeDasharray:f,strokeDashoffset:x?0:f,transition:`stroke-dashoffset ${c.duration??800}ms ${c.easing??"ease-in-out"}`}:{};return e("g",{className:"chartifypdf-line-path",children:[t("path",{ref:h,d:b,fill:"none",stroke:a,strokeWidth:y,strokeDasharray:o.strokeDasharray,strokeLinecap:"round",strokeLinejoin:"round",style:k}),g&&o.data.map((e,n)=>t("circle",{cx:i(e.x),cy:r(e.y),r:p,fill:a,stroke:"#fff",strokeWidth:1.5,style:{cursor:d?"pointer":"default"},onClick:d?()=>d(e,o):void 0},`dot-${o.id}-${n}`))]})});w.displayName="LinePath";const $=o.memo(({visible:o,x:n,y:i,point:r,series:l,plotHeight:a,plotWidth:s,config:c,seriesColor:d})=>{if(!o||!r||!l)return null;const h=c?.backgroundColor??"rgba(0, 0, 0, 0.8)",f=c?.textColor??"#fff";let u;if(c?.renderCustom)u=c.renderCustom(r,l);else{u=c?.formatter?c.formatter(r,l):`${l.name}: (${r.x}, ${r.y})`}let x=n+12,m=i-40-8;return x+140>s&&(x=n-140-12),m<0&&(m=i+12),e("g",{className:"chartifypdf-tooltip",pointerEvents:"none",children:[t("line",{x1:n,y1:0,x2:n,y2:a,stroke:"#999",strokeWidth:1,strokeDasharray:"4 4",opacity:.6}),t("circle",{cx:n,cy:i,r:5,fill:d,stroke:"#fff",strokeWidth:2}),t("foreignObject",{x:x,y:m,width:140,height:60,overflow:"visible",children:t("div",{style:{backgroundColor:h,color:f,padding:"6px 10px",borderRadius:"4px",fontSize:"12px",fontFamily:"sans-serif",whiteSpace:"nowrap",lineHeight:1.4,boxShadow:"0 2px 6px rgba(0,0,0,0.2)"},children:u})})]})});$.displayName="Tooltip";const W=o.memo(({config:o,svgWidth:n,svgHeight:i,margin:r,scale:l,onZoomIn:a,onZoomOut:s,onReset:c})=>{if(!o?.enabled||!1===o.showControls)return null;const d=o.controlsPosition??"top-right",h=24;let f,u;switch(d){case"top-left":f=r.left+8,u=r.top+8;break;case"bottom-left":f=r.left+8,u=i-r.bottom-24-8;break;case"bottom-right":f=n-r.right-80-8,u=i-r.bottom-24-8;break;default:f=n-r.right-80-8,u=r.top+8}return t("g",{className:"chartifypdf-zoom-controls",transform:`translate(${f}, ${u})`,children:(l>1?[{label:"+",onClick:a},{label:"−",onClick:s},{label:"↺",onClick:c}]:[{label:"+",onClick:a},{label:"−",onClick:s}]).map((o,n)=>e("g",{transform:`translate(${28*n}, 0)`,onClick:o.onClick,style:{cursor:"pointer"},children:[t("rect",{width:h,height:h,rx:4,fill:"rgba(255,255,255,0.9)",stroke:"#ccc",strokeWidth:1}),t("text",{x:12,y:12,textAnchor:"middle",dominantBaseline:"central",fontSize:14,fontFamily:"sans-serif",fill:"#333",fontWeight:"bold",children:o.label})]},o.label))})});W.displayName="ZoomControls";const P={top:20,right:20,bottom:50,left:60},T=({data:o,width:n,height:i,margin:r,xAxis:a,yAxis:s,tooltip:d,zoom:u,style:x,animation:m,colorPalette:y,className:g,onPointClick:k,ariaLabel:v})=>{const T=l(null),L=l(null),N=c(),z={...P,...r},F=f(T,n,i??400),{width:X,height:Y}=F,A=Math.max(0,X-z.left-z.right),D=Math.max(0,Y-z.top-z.bottom),{xScale:G,yScale:H,xTicks:O,yTicks:R}=p(o,A,D,a,s),Z=b(u,A,D),I=!1!==d?.enabled,B=M(L,o,G,H,z.left,z.top,A),j=a?.gridLines??!1,E=s?.gridLines??!0;return t("div",0===X||0===Y?{ref:T,className:g,style:{width:n??"100%",height:i??400,backgroundColor:x?.backgroundColor}}:{ref:T,className:g,style:{width:n??"100%",height:i??400,backgroundColor:x?.backgroundColor},children:e("svg",{ref:L,width:X,height:Y,role:"img","aria-label":v??"Line chart",onMouseMove:I&&!Z.isPanning?B.handleMouseMove:void 0,onMouseLeave:I?B.handleMouseLeave:void 0,onWheel:u?.enabled?Z.handleWheel:void 0,onMouseDown:u?.enabled?Z.handlePanStart:void 0,onMouseUp:u?.enabled?Z.handlePanEnd:void 0,style:{userSelect:Z.isPanning?"none":void 0,cursor:Z.isPanning?"grabbing":u?.enabled&&Z.scale>1?"grab":void 0},children:[t("defs",{children:t("clipPath",{id:N,children:t("rect",{width:A,height:D})})}),e("g",{transform:`translate(${z.left}, ${z.top})`,children:[t(C,{xTicks:O,yTicks:R,xScale:G,yScale:H,plotWidth:A,plotHeight:D,xAxisConfig:a,yAxisConfig:s,style:x}),t(S,{xTicks:O,yTicks:R,xScale:G,yScale:H,plotWidth:A,plotHeight:D,showXGrid:j,showYGrid:E,xGridColor:a?.gridLineColor,yGridColor:s?.gridLineColor}),t("g",{clipPath:`url(#${N})`,children:t("g",{transform:`translate(${Z.offsetX}, ${Z.offsetY}) scale(${Z.scale})`,onMouseMove:u?.enabled?Z.handlePanMove:void 0,children:o.map((e,o)=>t(w,{series:e,xScale:G,yScale:H,color:e.color??h(o,y),animation:m,onPointClick:k},e.id))})}),I&&t($,{visible:B.tooltipVisible,x:B.tooltipX,y:B.tooltipY,point:B.activePoint,series:B.activeSeries,plotHeight:D,plotWidth:A,config:d,seriesColor:B.activeSeries?.color??h(o.findIndex(e=>e.id===B.activeSeries?.id),y)})]}),t(W,{config:u,svgWidth:X,svgHeight:Y,margin:z,scale:Z.scale,onZoomIn:Z.zoomIn,onZoomOut:Z.zoomOut,onReset:Z.resetZoom})]})})};T.displayName="LineChart";export{T as LineChart,f as useChartDimensions,p as useScales,M as useTooltip,b as useZoom};
|
|
1
|
+
import{jsxs as e,jsx as t}from"react/jsx-runtime";import n,{useState as o,useLayoutEffect as i,useMemo as r,useRef as l,useCallback as a,useEffect as s,useId as c}from"react";const d=["#4e79a7","#f28e2b","#e15759","#76b7b2","#59a14f","#edc948","#b07aa1","#ff9da7","#9c755f","#bab0ac"];function h(e,t,n){const[r,l]=o({width:t??0,height:n??0}),a=void 0!==t&&void 0!==n;return i(()=>{if(a)return void l({width:t,height:n});const o=e.current;if(!o)return;const i=()=>{const e=o.getBoundingClientRect();l({width:e.width,height:e.height})};i();const r=new ResizeObserver(()=>{i()});return r.observe(o),()=>r.disconnect()},[e,a,t,n]),r}function f(e,t,n){return Math.min(Math.max(e,t),n)}function u(e,t,n){return e+(t-e)*n}function x(e,t){const n=Math.floor(Math.log10(e)),o=e/Math.pow(10,n);let i;return i=t?o<1.5?1:o<3?2:o<7?5:10:o<=1?1:o<=2?2:o<=5?5:10,i*Math.pow(10,n)}function y(e,t,n){if(e===t){const n=0===e?1:.1*Math.abs(e);e-=n,t+=n}const o=x(t-e,!1),i=x(o/(n-1),!0);return{niceMin:Math.floor(e/i)*i,niceMax:Math.ceil(t/i)*i,tickStep:i}}function m(e,t,n,o){return i=>{if(t===e)return(n+o)/2;const r=function(e,t,n){return e===t?0:(n-e)/(t-e)}(e,t,i);return u(n,o,r)}}function p(e,t,n){const o=[];for(let i=e;i<=t+.5*n;i+=n)o.push(parseFloat(i.toPrecision(12)));return o}function g(e,t,n){return r(()=>{let o=1/0,i=-1/0,r=1/0,l=-1/0;for(const t of e)for(const e of t.data)e.x<o&&(o=e.x),e.x>i&&(i=e.x),e.y<r&&(r=e.y),e.y>l&&(l=e.y);isFinite(o)||(o=0,i=1,r=0,l=1),void 0!==t?.min&&(o=t.min),void 0!==t?.max&&(i=t.max),void 0!==n?.min&&(r=n.min),void 0!==n?.max&&(l=n.max);const a=t?.tickCount??6,s=n?.tickCount??6,c=y(o,i,a),d=y(r,l,s);return{fullXDomain:[c.niceMin,c.niceMax],fullYDomain:[d.niceMin,d.niceMax]}},[e,t,n])}function b(e,t,n,o,i,l,a){return r(()=>{let r=1/0,s=-1/0,c=1/0,d=-1/0;for(const t of e)for(const e of t.data)e.x<r&&(r=e.x),e.x>s&&(s=e.x),e.y<c&&(c=e.y),e.y>d&&(d=e.y);isFinite(r)||(r=0,s=1,c=0,d=1),void 0!==o?.min&&(r=o.min),void 0!==o?.max&&(s=o.max),void 0!==i?.min&&(c=i.min),void 0!==i?.max&&(d=i.max);const h=o?.tickCount??6,f=i?.tickCount??6;let u,x,g,b;if(l)u=l[0],x=l[1];else{const e=y(r,s,h);u=e.niceMin,x=e.niceMax}if(a)g=a[0],b=a[1];else{const e=y(c,d,f);g=e.niceMin,b=e.niceMax}const k=y(u,x,h),v=y(g,b,f);return{xScale:m(u,x,0,t),yScale:m(g,b,n,0),xTicks:p(k.niceMin,k.niceMax,k.tickStep).filter(e=>e>=u&&e<=x),yTicks:p(v.niceMin,v.niceMax,v.tickStep).filter(e=>e>=g&&e<=b),xDomain:[u,x],yDomain:[g,b]}},[e,t,n,o,i,l,a])}function k(e,t,n,i,r){const s=e?.enabled??!1,c=e?.maxScale??10,d=e?.step??.5,h=e?.enableWheel??!0,x=e?.enablePan??!0,y=e?.requireCtrlKey??!0,[m,p]=o(t),[g,b]=o(n),[k,v]=o(!1),[M,C]=o(!1),$=l({x:0,y:0,xDomain:t,yDomain:n}),S=l(null),w=l({x:t,y:n});t[0]===w.current.x[0]&&t[1]===w.current.x[1]&&n[0]===w.current.y[0]&&n[1]===w.current.y[1]||(w.current={x:t,y:n},p(t),b(n));const W=t[1]-t[0],D=m[1]-m[0],P=W>0&&D>0?W/D:1,T=a((e,t)=>{const n=e[1]-e[0];if(n>=t[1]-t[0])return t;let o=e[0],i=e[1];return o<t[0]&&(o=t[0],i=o+n),i>t[1]&&(i=t[1],o=i-n),[o,i]},[]),X=a(()=>{S.current&&clearTimeout(S.current),C(!0),S.current=setTimeout(()=>C(!1),2e3)},[]),A=a((e,o,i)=>{if(!s)return;const r=o??.5,l=i??.5;p(n=>{const o=(n[1]-n[0])/e,i=W/c,l=Math.max(o,i),a=Math.min(l,W),s=u(n[0],n[1],r)-a*r;return T([s,s+a],t)}),b(t=>{const o=t[1]-t[0],i=n[1]-n[0],r=o/e,a=i/c,s=Math.max(r,a),d=Math.min(s,i),h=u(t[0],t[1],l)-d*l;return T([h,h+d],n)})},[s,t,n,W,c,T]),L=a(()=>{A(1+d)},[d,A]),z=a(()=>{A(1/(1+d))},[d,A]),N=a(()=>{p(t),b(n)},[t,n]),F=a(e=>{if(!s||!h)return;if(y&&!e.ctrlKey&&!e.metaKey)return void X();e.preventDefault();const t=e.currentTarget.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top,i=f(n/(t.width||1),0,1),r=f(o/(t.height||1),0,1),l=e.deltaY<0?1+d:1/(1+d);A(l,i,r)},[s,h,y,d,X,A]),Y=a(e=>{!s||!x||P<=1||0===e.button&&(v(!0),$.current={x:e.clientX,y:e.clientY,xDomain:m,yDomain:g})},[s,x,P,m,g]),I=a(e=>{if(!k)return;const o=e.clientX-$.current.x,l=e.clientY-$.current.y,a=$.current.xDomain,s=$.current.yDomain,c=a[1]-a[0],d=s[1]-s[0],h=-o/i*c,f=l/r*d,u=[a[0]+h,a[1]+h],x=[s[0]+f,s[1]+f];p(T(u,t)),b(T(x,n))},[k,i,r,t,n,T]),H=a(()=>{v(!1)},[]);return{viewXDomain:m,viewYDomain:g,scale:P,isPanning:k,showZoomHint:M,zoomIn:L,zoomOut:z,resetZoom:N,handleWheel:F,handlePanStart:Y,handlePanMove:I,handlePanEnd:H}}function v(e,t,n){if(0===e.length)return null;let o=0,i=e.length-1;for(;o<i;){const r=Math.floor((o+i)/2);n(e[r].x)<t?o=r+1:i=r}let r=e[o],l=Math.abs(n(r.x)-t);if(o>0){const i=Math.abs(n(e[o-1].x)-t);i<l&&(l=i,r=e[o-1])}return r}function M(e,t,n,i,r,l,s,c){const[d,h]=o(!1),[f,u]=o(0),[x,y]=o(0),[m,p]=o(null),[g,b]=o(null),[k,M]=o(null);return{tooltipVisible:d,tooltipX:f,tooltipY:x,activePoint:m,activeSeries:g,activeSeriesId:k,handleMouseMove:a(o=>{const l=e.current;if(!l)return;const a=l.getBoundingClientRect(),c=o.clientX-a.left-r;if(c<0||c>s)return h(!1),void M(null);let d=null,f=null,x=1/0;for(const e of t){const t=v(e.data,c,n);if(!t)continue;const o=Math.abs(n(t.x)-c);o<x&&(x=o,d=t,f=e)}d&&f&&(u(n(d.x)),y(i(d.y)),p(d),b(f),M(f.id),h(!0))},[e,t,n,i,r,s]),handleMouseLeave:a(()=>{h(!1),p(null),b(null),M(null)},[])}}const C=e=>Math.abs(e)>=1e6?`${(e/1e6).toFixed(1)}M`:Math.abs(e)>=1e3?`${(e/1e3).toFixed(1)}K`:Number.isInteger(e)?e.toString():e.toFixed(1),$=n.memo(({xTicks:n,yTicks:o,xScale:i,yScale:r,plotWidth:l,plotHeight:a,xAxisConfig:s,yAxisConfig:c,style:d})=>{const h=d?.axisColor??"#333",f=d?.tickColor??"#666",u=d?.fontFamily??"sans-serif",x=d?.fontSize??11,y=s?.tickFormat??C,m=c?.tickFormat??C;return e("g",{className:"chartifypdf-axes",children:[t("line",{x1:0,y1:a,x2:l,y2:a,stroke:h,strokeWidth:1}),n.map(n=>{const o=i(n);return e("g",{children:[t("line",{x1:o,y1:a,x2:o,y2:a+6,stroke:h,strokeWidth:1}),t("text",{x:o,y:a+18,textAnchor:"middle",fill:f,fontFamily:u,fontSize:x,children:y(n)})]},`x-tick-${n}`)}),s?.label&&t("text",{x:l/2,y:a+38,textAnchor:"middle",fill:h,fontFamily:u,fontSize:x+1,fontWeight:"bold",children:s.label}),t("line",{x1:0,y1:0,x2:0,y2:a,stroke:h,strokeWidth:1}),o.map(n=>{const o=r(n);return e("g",{children:[t("line",{x1:-6,y1:o,x2:0,y2:o,stroke:h,strokeWidth:1}),t("text",{x:-10,y:o,textAnchor:"end",dominantBaseline:"middle",fill:f,fontFamily:u,fontSize:x,children:m(n)})]},`y-tick-${n}`)}),c?.label&&t("text",{x:0,y:0,textAnchor:"middle",fill:h,fontFamily:u,fontSize:x+1,fontWeight:"bold",transform:`translate(-40, ${a/2}) rotate(-90)`,children:c.label})]})});$.displayName="Axes";const S=n.memo(({xTicks:n,yTicks:o,xScale:i,yScale:r,plotWidth:l,plotHeight:a,showXGrid:s,showYGrid:c,xGridColor:d="#e0e0e0",yGridColor:h="#e0e0e0"})=>e("g",{className:"chartifypdf-grid",children:[c&&o.map(e=>{const n=r(e);return t("line",{x1:0,y1:n,x2:l,y2:n,stroke:h,strokeDasharray:"4 4",strokeOpacity:.5},`y-grid-${e}`)}),s&&n.map(e=>{const n=i(e);return t("line",{x1:n,y1:0,x2:n,y2:a,stroke:d,strokeDasharray:"4 4",strokeOpacity:.5},`x-grid-${e}`)})]}));function w(e){if(0===e.length)return"";const[t,...n]=e;return`M${t.x},${t.y}`+n.map(e=>`L${e.x},${e.y}`).join("")}function W(e,t){switch(t){case"monotone":return function(e){const t=e.length;if(t<2)return w(e);if(2===t)return w(e);const n=[],o=[],i=[];for(let r=0;r<t-1;r++)n.push(e[r+1].x-e[r].x),o.push(e[r+1].y-e[r].y),i.push(0===n[r]?0:o[r]/n[r]);const r=new Array(t);r[0]=i[0],r[t-1]=i[t-2];for(let e=1;e<t-1;e++)i[e-1]*i[e]<=0?r[e]=0:r[e]=2/(1/i[e-1]+1/i[e]);for(let e=0;e<t-1;e++)if(0===i[e])r[e]=0,r[e+1]=0;else{const t=r[e]/i[e],n=r[e+1]/i[e],o=t*t+n*n;if(o>9){const l=3/Math.sqrt(o);r[e]=l*t*i[e],r[e+1]=l*n*i[e]}}let l=`M${e[0].x},${e[0].y}`;for(let o=0;o<t-1;o++){const t=n[o]/3;l+=`C${e[o].x+t},${e[o].y+r[o]*t},${e[o+1].x-t},${e[o+1].y-r[o+1]*t},${e[o+1].x},${e[o+1].y}`}return l}(e);case"natural":return function(e){const t=e.length;if(t<2)return w(e);if(2===t)return w(e);function n(e){const t=e.length-1,n=new Array(t),o=new Array(t),i=new Array(t);n[0]=0,o[0]=2,i[0]=e[0]+2*e[1];for(let r=1;r<t-1;r++)n[r]=1,o[r]=4,i[r]=4*e[r]+2*e[r+1];n[t-1]=2,o[t-1]=7,i[t-1]=8*e[t-1]+e[t];for(let e=1;e<t;e++){const t=n[e]/o[e-1];o[e]-=t,i[e]-=t*i[e-1]}const r=new Array(t);r[t-1]=i[t-1]/o[t-1];for(let e=t-2;e>=0;e--)r[e]=(i[e]-r[e+1])/o[e];const l=new Array(t);for(let n=0;n<t-1;n++)l[n]=2*e[n+1]-r[n+1];l[t-1]=(e[t]+r[t-1])/2;const a=[];for(let e=0;e<t;e++)a.push({cp1:r[e],cp2:l[e]});return a}const o=e.map(e=>e.x),i=e.map(e=>e.y),r=n(o),l=n(i);let a=`M${e[0].x},${e[0].y}`;for(let n=0;n<t-1;n++)a+=`C${r[n].cp1},${l[n].cp1},${r[n].cp2},${l[n].cp2},${e[n+1].x},${e[n+1].y}`;return a}(e);default:return w(e)}}S.displayName="GridLines";const D=n.memo(({series:n,xScale:i,yScale:r,color:a,animation:c,onPointClick:d,curveType:h,highlightOpacity:f})=>{const u=l(null),[x,y]=o(0),[m,p]=o(!c?.enabled),g=n.strokeWidth??2,b=n.showDots??!1,k=n.dotRadius??3.5,v=n.curveType??h??"linear",M=f??1,C=n.data.map(e=>({x:i(e.x),y:r(e.y)})),$="linear"===v?w(C):W(C,v);s(()=>{if(c?.enabled&&u.current){const e=u.current.getTotalLength();y(e),p(!1);const t=setTimeout(()=>{p(!0)},c.duration??800);return()=>clearTimeout(t)}},[$,c?.enabled,c?.duration]);const S=c?.enabled&&x>0?{strokeDasharray:x,strokeDashoffset:m?0:x,transition:`stroke-dashoffset ${c.duration??800}ms ${c.easing??"ease-in-out"}`}:{};return e("g",{className:"chartifypdf-line-path",style:{opacity:M,transition:"opacity 150ms ease"},children:[t("path",{ref:u,d:$,fill:"none",stroke:a,strokeWidth:g,strokeDasharray:n.strokeDasharray,strokeLinecap:"round",strokeLinejoin:"round",style:S}),b&&n.data.map((e,o)=>t("circle",{cx:i(e.x),cy:r(e.y),r:k,fill:a,stroke:"#fff",strokeWidth:1.5,style:{cursor:d?"pointer":"default"},onClick:d?()=>d(e,n):void 0},`dot-${n.id}-${o}`))]})});D.displayName="LinePath";const P=n.memo(({visible:n,x:o,y:i,point:r,series:l,plotHeight:a,plotWidth:s,config:c,seriesColor:d})=>{if(!n||!r||!l)return null;const h=c?.backgroundColor??"rgba(0, 0, 0, 0.8)",f=c?.textColor??"#fff";let u;if(c?.renderCustom)u=c.renderCustom(r,l);else{u=c?.formatter?c.formatter(r,l):`${l.name}: (${r.x}, ${r.y})`}let x=o+12,y=i-40-8;return x+140>s&&(x=o-140-12),y<0&&(y=i+12),e("g",{className:"chartifypdf-tooltip",pointerEvents:"none",children:[t("line",{x1:o,y1:0,x2:o,y2:a,stroke:"#999",strokeWidth:1,strokeDasharray:"4 4",opacity:.6}),t("circle",{cx:o,cy:i,r:5,fill:d,stroke:"#fff",strokeWidth:2}),t("foreignObject",{x:x,y:y,width:140,height:60,overflow:"visible",children:t("div",{style:{backgroundColor:h,color:f,padding:"6px 10px",borderRadius:"4px",fontSize:"12px",fontFamily:"sans-serif",whiteSpace:"nowrap",lineHeight:1.4,boxShadow:"0 2px 6px rgba(0,0,0,0.2)"},children:u})})]})});P.displayName="Tooltip";const T=n.memo(({config:n,svgWidth:o,svgHeight:i,margin:r,scale:l,onZoomIn:a,onZoomOut:s,onReset:c})=>{if(!n?.enabled||!1===n.showControls)return null;const d=n.controlsPosition??"top-right",h=24;let f,u;switch(d){case"top-left":f=r.left+8,u=r.top+8;break;case"bottom-left":f=r.left+8,u=i-r.bottom-24-8;break;case"bottom-right":f=o-r.right-80-8,u=i-r.bottom-24-8;break;default:f=o-r.right-80-8,u=r.top+8}return t("g",{className:"chartifypdf-zoom-controls",transform:`translate(${f}, ${u})`,children:(l>1?[{label:"+",onClick:a},{label:"−",onClick:s},{label:"↺",onClick:c}]:[{label:"+",onClick:a},{label:"−",onClick:s}]).map((n,o)=>e("g",{transform:`translate(${28*o}, 0)`,onClick:n.onClick,style:{cursor:"pointer"},children:[t("rect",{width:h,height:h,rx:4,fill:"rgba(255,255,255,0.9)",stroke:"#ccc",strokeWidth:1}),t("text",{x:12,y:12,textAnchor:"middle",dominantBaseline:"central",fontSize:14,fontFamily:"sans-serif",fill:"#333",fontWeight:"bold",children:n.label})]},n.label))})});function X({x:e,y:n,color:o}){return t("polygon",{points:`${e},${n} ${e-5},${n-10} ${e+5},${n-10}`,fill:o})}function A({x:e,y:n,color:o}){return t("polygon",{points:`${e},${n-6} ${e+5},${n} ${e},${n+6} ${e-5},${n}`,fill:o})}function L({x:e,y:n,color:o}){const i=[];for(let t=0;t<5;t++){const o=(72*t-90)*(Math.PI/180),r=(72*t+36-90)*(Math.PI/180);i.push(`${e+6*Math.cos(o)},${n+6*Math.sin(o)}`),i.push(`${e+3*Math.cos(r)},${n+3*Math.sin(r)}`)}return t("polygon",{points:i.join(" "),fill:o})}T.displayName="ZoomControls";const z=n.memo(({peaks:n,data:o,xScale:i,yScale:r})=>t("g",{className:"chartifypdf-peak-markers",children:n.map((n,l)=>{const a=function(e,t){let n=-1/0,o=-1;for(let i=0;i<e.length;i++){const r=e[i];let l=r.data[0],a=1/0;for(const e of r.data){const n=Math.abs(e.x-t);n<a&&(a=n,l=e)}l&&l.y>n&&(n=l.y,o=i)}return-1===o?null:{y:n,seriesIndex:o}}(o,n.x);if(!a)return null;const s=i(n.x),c=r(a.y),d=n.color??"#ef4444",h=n.icon??"arrow",f=n.label,u=c-14;return e("g",{children:[t("line",{x1:s,y1:c,x2:s,y2:u+6,stroke:d,strokeWidth:1,strokeDasharray:"3 2",opacity:.6}),"arrow"===h&&t(X,{x:s,y:u,color:d}),"diamond"===h&&t(A,{x:s,y:u,color:d}),"star"===h&&t(L,{x:s,y:u,color:d}),f&&t("text",{x:s,y:u-8,textAnchor:"middle",fill:d,fontSize:11,fontWeight:600,children:f})]},`peak-${l}`)})}));z.displayName="PeakMarkers";const N=({visible:n,x:o,y:i,nearestX:r,data:a,config:c,getSeriesColor:d,onClose:h})=>{const f=l(null);if(s(()=>{if(!n)return;const e=e=>{f.current&&!f.current.contains(e.target)&&h()},t=e=>{"Escape"===e.key&&h()},o=setTimeout(()=>{document.addEventListener("mousedown",e),document.addEventListener("keydown",t)},0);return()=>{clearTimeout(o),document.removeEventListener("mousedown",e),document.removeEventListener("keydown",t)}},[n,h]),!n)return null;const u=c?.backgroundColor??"#ffffff",x=c?.textColor??"#1f2937",y=c?.borderColor??"#e5e7eb",m=a.map((e,t)=>{const n=function(e,t){if(0===e.data.length)return null;let n=e.data[0],o=Math.abs(n.x-t);for(const i of e.data){const e=Math.abs(i.x-t);e<o&&(o=e,n=i)}return n.y}(e,r);return{name:e.name,color:e.color??d(t),x:r,y:n}});return e("div",{ref:f,style:{position:"fixed",left:o,top:i,zIndex:9999,backgroundColor:u,color:x,border:`1px solid ${y}`,borderRadius:6,boxShadow:"0 4px 12px rgba(0,0,0,0.15)",padding:"8px 0",minWidth:200,fontSize:12,fontFamily:"system-ui, -apple-system, sans-serif"},children:[e("div",{style:{padding:"4px 12px 8px",fontWeight:600,fontSize:11,color:x,opacity:.6,borderBottom:`1px solid ${y}`,marginBottom:4},children:["Data at X = ","number"==typeof r?r.toLocaleString():r]}),e("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t("thead",{children:e("tr",{children:[t("th",{style:{textAlign:"left",padding:"4px 12px",fontWeight:600,opacity:.7},children:"Series"}),t("th",{style:{textAlign:"right",padding:"4px 12px",fontWeight:600,opacity:.7},children:"Y"})]})}),t("tbody",{children:m.map((n,o)=>e("tr",{style:{borderTop:o>0?`1px solid ${y}`:void 0},children:[e("td",{style:{padding:"4px 12px",display:"flex",alignItems:"center",gap:6},children:[t("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",backgroundColor:n.color,flexShrink:0}}),n.name]}),t("td",{style:{padding:"4px 12px",textAlign:"right",fontVariantNumeric:"tabular-nums"},children:null!==n.y?n.y.toLocaleString():"—"})]},o))})]})]})};N.displayName="ContextMenu";const F={top:20,right:20,bottom:50,left:60},Y=({data:n,width:i,height:r,margin:s,xAxis:f,yAxis:u,tooltip:x,zoom:y,style:m,animation:p,colorPalette:v,className:C,onPointClick:w,ariaLabel:W,curveType:X,peaks:A,contextMenu:L})=>{const Y=l(null),I=l(null),H=c(),R={...F,...s},O=h(Y,i,r??400),{width:E,height:G}=O,Z=Math.max(0,E-R.left-R.right),B=Math.max(0,G-R.top-R.bottom),{fullXDomain:j,fullYDomain:K}=g(n,f,u),V=k(y,j,K,Z,B),q=y?.enabled??!1,{xScale:U,yScale:J,xTicks:Q,yTicks:_}=b(n,Z,B,f,u,q?V.viewXDomain:void 0,q?V.viewYDomain:void 0),ee=!1!==x?.enabled,te=M(I,n,U,J,R.left,R.top,Z),ne=te.activeSeriesId,oe=te.tooltipVisible&&null!==ne,ie=e=>oe?e===ne?1:.2:1,[re,le]=o({visible:!1,clientX:0,clientY:0,nearestX:0}),ae=a(e=>{if(!L?.enabled)return;e.preventDefault();const t=I.current;if(!t)return;const o=t.getBoundingClientRect(),i=e.clientX-o.left-R.left;if(i<0||i>Z)return;let r=0,l=1/0;for(const e of n)for(const t of e.data){const e=U(t.x),n=Math.abs(e-i);n<l&&(l=n,r=t.x)}le({visible:!0,clientX:e.clientX,clientY:e.clientY,nearestX:r})},[L?.enabled,n,U,R.left,Z]),se=a(()=>{le(e=>({...e,visible:!1}))},[]),ce=f?.gridLines??!1,de=u?.gridLines??!0,he=e=>function(e,t){const n=t??d;return n[e%n.length]}(e,v);return 0===E||0===G?t("div",{ref:Y,className:C,style:{width:i??"100%",height:r??400,backgroundColor:m?.backgroundColor}}):e("div",{ref:Y,className:C,style:{width:i??"100%",height:r??400,backgroundColor:m?.backgroundColor,position:"relative"},children:[e("svg",{ref:I,width:E,height:G,role:"img","aria-label":W??"Line chart",onMouseMove:ee&&!V.isPanning?te.handleMouseMove:void 0,onMouseLeave:ee?te.handleMouseLeave:void 0,onWheel:q?V.handleWheel:void 0,onMouseDown:q?V.handlePanStart:void 0,onMouseUp:q?V.handlePanEnd:void 0,onContextMenu:ae,style:{userSelect:V.isPanning?"none":void 0,cursor:V.isPanning?"grabbing":q&&V.scale>1?"grab":void 0},children:[t("defs",{children:t("clipPath",{id:H,children:t("rect",{width:Z,height:B})})}),e("g",{transform:`translate(${R.left}, ${R.top})`,children:[t($,{xTicks:Q,yTicks:_,xScale:U,yScale:J,plotWidth:Z,plotHeight:B,xAxisConfig:f,yAxisConfig:u,style:m}),t(S,{xTicks:Q,yTicks:_,xScale:U,yScale:J,plotWidth:Z,plotHeight:B,showXGrid:ce,showYGrid:de,xGridColor:f?.gridLineColor,yGridColor:u?.gridLineColor}),e("g",{clipPath:`url(#${H})`,children:[q?t("g",{onMouseMove:V.handlePanMove,children:n.map((e,n)=>t(D,{series:e,xScale:U,yScale:J,color:e.color??he(n),animation:p,onPointClick:w,curveType:X,highlightOpacity:ie(e.id)},e.id))}):n.map((e,n)=>t(D,{series:e,xScale:U,yScale:J,color:e.color??he(n),animation:p,onPointClick:w,curveType:X,highlightOpacity:ie(e.id)},e.id)),A&&A.length>0&&t(z,{peaks:A,data:n,xScale:U,yScale:J,colorPalette:v})]}),ee&&t(P,{visible:te.tooltipVisible,x:te.tooltipX,y:te.tooltipY,point:te.activePoint,series:te.activeSeries,plotHeight:B,plotWidth:Z,config:x,seriesColor:te.activeSeries?.color??he(n.findIndex(e=>e.id===te.activeSeries?.id))})]}),t(T,{config:y,svgWidth:E,svgHeight:G,margin:R,scale:V.scale,onZoomIn:V.zoomIn,onZoomOut:V.zoomOut,onReset:V.resetZoom})]}),V.showZoomHint&&t("div",{style:{position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",background:"rgba(0, 0, 0, 0.7)",color:"#fff",padding:"8px 16px",borderRadius:6,fontSize:13,fontFamily:"system-ui, -apple-system, sans-serif",pointerEvents:"none",whiteSpace:"nowrap",zIndex:10},children:"Hold Ctrl + scroll to zoom"}),L?.enabled&&t(N,{visible:re.visible,x:re.clientX,y:re.clientY,nearestX:re.nearestX,data:n,config:L,getSeriesColor:he,onClose:se})]})};Y.displayName="LineChart";export{Y as LineChart,h as useChartDimensions,g as useDataDomain,b as useScales,M as useTooltip,k as useZoom};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/color.ts","../src/hooks/useChartDimensions.ts","../src/utils/math.ts","../src/hooks/useScales.ts","../src/hooks/useZoom.ts","../src/hooks/useTooltip.ts","../src/components/Axes.tsx","../src/components/GridLines.tsx","../src/components/LinePath.tsx","../src/utils/path.ts","../src/components/Tooltip.tsx","../src/components/ZoomControls.tsx","../src/components/LineChart.tsx"],"sourcesContent":["export const DEFAULT_PALETTE = [\n \"#4e79a7\",\n \"#f28e2b\",\n \"#e15759\",\n \"#76b7b2\",\n \"#59a14f\",\n \"#edc948\",\n \"#b07aa1\",\n \"#ff9da7\",\n \"#9c755f\",\n \"#bab0ac\",\n];\n\nexport function getSeriesColor(index: number, palette?: string[]): string {\n const colors = palette ?? DEFAULT_PALETTE;\n return colors[index % colors.length];\n}\n","import { useLayoutEffect, useState, type RefObject } from \"react\";\n\nexport function useChartDimensions(\n containerRef: RefObject<HTMLDivElement | null>,\n providedWidth?: number,\n providedHeight?: number\n): { width: number; height: number } {\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n }>({\n width: providedWidth ?? 0,\n height: providedHeight ?? 0,\n });\n\n const isFixed =\n providedWidth !== undefined && providedHeight !== undefined;\n\n useLayoutEffect(() => {\n if (isFixed) {\n setDimensions({ width: providedWidth!, height: providedHeight! });\n return;\n }\n\n const node = containerRef.current;\n if (!node) return;\n\n const measure = () => {\n const rect = node.getBoundingClientRect();\n setDimensions({ width: rect.width, height: rect.height });\n };\n\n measure();\n\n const observer = new ResizeObserver(() => {\n measure();\n });\n observer.observe(node);\n\n return () => observer.disconnect();\n }, [containerRef, isFixed, providedWidth, providedHeight]);\n\n return dimensions;\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function niceNumber(range: number, round: boolean): number {\n const exponent = Math.floor(Math.log10(range));\n const fraction = range / Math.pow(10, exponent);\n\n let niceFraction: number;\n if (round) {\n if (fraction < 1.5) niceFraction = 1;\n else if (fraction < 3) niceFraction = 2;\n else if (fraction < 7) niceFraction = 5;\n else niceFraction = 10;\n } else {\n if (fraction <= 1) niceFraction = 1;\n else if (fraction <= 2) niceFraction = 2;\n else if (fraction <= 5) niceFraction = 5;\n else niceFraction = 10;\n }\n\n return niceFraction * Math.pow(10, exponent);\n}\n\nexport function niceRange(\n min: number,\n max: number,\n tickCount: number\n): { niceMin: number; niceMax: number; tickStep: number } {\n if (min === max) {\n const offset = min === 0 ? 1 : Math.abs(min) * 0.1;\n min = min - offset;\n max = max + offset;\n }\n\n const range = niceNumber(max - min, false);\n const tickStep = niceNumber(range / (tickCount - 1), true);\n const niceMin = Math.floor(min / tickStep) * tickStep;\n const niceMax = Math.ceil(max / tickStep) * tickStep;\n\n return { niceMin, niceMax, tickStep };\n}\n\nexport function linearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (value: number) => number {\n return (value: number) => {\n if (domainMax === domainMin) return (rangeMin + rangeMax) / 2;\n const t = inverseLerp(domainMin, domainMax, value);\n return lerp(rangeMin, rangeMax, t);\n };\n}\n\nexport function generateTicks(\n niceMin: number,\n niceMax: number,\n tickStep: number\n): number[] {\n const ticks: number[] = [];\n for (let v = niceMin; v <= niceMax + tickStep * 0.5; v += tickStep) {\n ticks.push(parseFloat(v.toPrecision(12)));\n }\n return ticks;\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange, linearScale, generateTicks } from \"../utils/math\";\n\nexport interface ScalesResult {\n xScale: (value: number) => number;\n yScale: (value: number) => number;\n xTicks: number[];\n yTicks: number[];\n xDomain: [number, number];\n yDomain: [number, number];\n}\n\nexport function useScales(\n data: DataSeries[],\n plotWidth: number,\n plotHeight: number,\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig\n): ScalesResult {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n const xNice = niceRange(xMin, xMax, xTickCount);\n const yNice = niceRange(yMin, yMax, yTickCount);\n\n const xScale = linearScale(xNice.niceMin, xNice.niceMax, 0, plotWidth);\n // Y inverted: SVG Y increases downward\n const yScale = linearScale(yNice.niceMin, yNice.niceMax, plotHeight, 0);\n\n const xTicks = generateTicks(xNice.niceMin, xNice.niceMax, xNice.tickStep);\n const yTicks = generateTicks(yNice.niceMin, yNice.niceMax, yNice.tickStep);\n\n return {\n xScale,\n yScale,\n xTicks,\n yTicks,\n xDomain: [xNice.niceMin, xNice.niceMax] as [number, number],\n yDomain: [yNice.niceMin, yNice.niceMax] as [number, number],\n };\n }, [data, plotWidth, plotHeight, xAxisConfig, yAxisConfig]);\n}\n","import { useState, useCallback, useRef } from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\nimport { clamp } from \"../utils/math\";\n\nexport interface ZoomState {\n scale: number;\n offsetX: number;\n offsetY: number;\n isPanning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n resetZoom: () => void;\n handleWheel: (e: React.WheelEvent) => void;\n handlePanStart: (e: React.MouseEvent) => void;\n handlePanMove: (e: React.MouseEvent) => void;\n handlePanEnd: () => void;\n}\n\nexport function useZoom(\n config: ZoomConfig | undefined,\n plotWidth: number,\n plotHeight: number\n): ZoomState {\n const enabled = config?.enabled ?? false;\n const minScale = config?.minScale ?? 1;\n const maxScale = config?.maxScale ?? 10;\n const step = config?.step ?? 0.5;\n const enableWheel = config?.enableWheel ?? true;\n const enablePan = config?.enablePan ?? true;\n\n const [scale, setScale] = useState(1);\n const [offsetX, setOffsetX] = useState(0);\n const [offsetY, setOffsetY] = useState(0);\n const [isPanning, setIsPanning] = useState(false);\n const panStart = useRef({ x: 0, y: 0, offsetX: 0, offsetY: 0 });\n\n const clampOffset = useCallback(\n (ox: number, oy: number, s: number) => {\n const maxOffsetX = plotWidth * (s - 1);\n const maxOffsetY = plotHeight * (s - 1);\n return {\n x: clamp(ox, -maxOffsetX, 0),\n y: clamp(oy, -maxOffsetY, 0),\n };\n },\n [plotWidth, plotHeight]\n );\n\n const zoomIn = useCallback(() => {\n if (!enabled) return;\n setScale((prev) => {\n const next = clamp(prev + step, minScale, maxScale);\n // Center zoom\n setOffsetX((ox) => {\n const newOx = ox - (plotWidth * step) / 2;\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = oy - (plotHeight * step) / 2;\n return clampOffset(0, newOy, next).y;\n });\n return next;\n });\n }, [enabled, step, minScale, maxScale, plotWidth, plotHeight, clampOffset]);\n\n const zoomOut = useCallback(() => {\n if (!enabled) return;\n setScale((prev) => {\n const next = clamp(prev - step, minScale, maxScale);\n setOffsetX((ox) => {\n const newOx = ox + (plotWidth * step) / 2;\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = oy + (plotHeight * step) / 2;\n return clampOffset(0, newOy, next).y;\n });\n return next;\n });\n }, [enabled, step, minScale, maxScale, plotWidth, plotHeight, clampOffset]);\n\n const resetZoom = useCallback(() => {\n setScale(1);\n setOffsetX(0);\n setOffsetY(0);\n }, []);\n\n const handleWheel = useCallback(\n (e: React.WheelEvent) => {\n if (!enabled || !enableWheel) return;\n e.preventDefault();\n\n const rect = (e.currentTarget as SVGElement).getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY < 0 ? step : -step;\n\n setScale((prev) => {\n const next = clamp(prev + delta, minScale, maxScale);\n const ratio = next / prev;\n\n setOffsetX((ox) => {\n const newOx = mouseX - ratio * (mouseX - ox);\n return clampOffset(newOx, 0, next).x;\n });\n setOffsetY((oy) => {\n const newOy = mouseY - ratio * (mouseY - oy);\n return clampOffset(0, newOy, next).y;\n });\n\n return next;\n });\n },\n [enabled, enableWheel, step, minScale, maxScale, clampOffset]\n );\n\n const handlePanStart = useCallback(\n (e: React.MouseEvent) => {\n if (!enabled || !enablePan || scale <= 1) return;\n if (e.button !== 0) return; // left-click only\n setIsPanning(true);\n panStart.current = {\n x: e.clientX,\n y: e.clientY,\n offsetX,\n offsetY,\n };\n },\n [enabled, enablePan, scale, offsetX, offsetY]\n );\n\n const handlePanMove = useCallback(\n (e: React.MouseEvent) => {\n if (!isPanning) return;\n const dx = e.clientX - panStart.current.x;\n const dy = e.clientY - panStart.current.y;\n const clamped = clampOffset(\n panStart.current.offsetX + dx,\n panStart.current.offsetY + dy,\n scale\n );\n setOffsetX(clamped.x);\n setOffsetY(clamped.y);\n },\n [isPanning, scale, clampOffset]\n );\n\n const handlePanEnd = useCallback(() => {\n setIsPanning(false);\n }, []);\n\n return {\n scale,\n offsetX,\n offsetY,\n isPanning,\n zoomIn,\n zoomOut,\n resetZoom,\n handleWheel,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n };\n}\n","import { useState, useCallback, type RefObject } from \"react\";\nimport type { DataSeries, DataPoint, TooltipConfig } from \"../types/chart.types\";\n\nexport interface TooltipState {\n tooltipVisible: boolean;\n tooltipX: number;\n tooltipY: number;\n activePoint: DataPoint | null;\n activeSeries: DataSeries | null;\n handleMouseMove: (e: React.MouseEvent) => void;\n handleMouseLeave: () => void;\n}\n\nfunction findNearestByPixelX(\n sorted: DataPoint[],\n mouseX: number,\n xScale: (v: number) => number\n): DataPoint | null {\n if (sorted.length === 0) return null;\n\n let low = 0;\n let high = sorted.length - 1;\n\n // Binary search for nearest pixel X\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n if (xScale(sorted[mid].x) < mouseX) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n let nearest = sorted[low];\n let minDist = Math.abs(xScale(nearest.x) - mouseX);\n\n if (low > 0) {\n const dist = Math.abs(xScale(sorted[low - 1].x) - mouseX);\n if (dist < minDist) {\n minDist = dist;\n nearest = sorted[low - 1];\n }\n }\n\n return nearest;\n}\n\nexport function useTooltip(\n svgRef: RefObject<SVGSVGElement | null>,\n data: DataSeries[],\n xScale: (v: number) => number,\n yScale: (v: number) => number,\n marginLeft: number,\n marginTop: number,\n plotWidth: number,\n _config?: TooltipConfig\n): TooltipState {\n const [tooltipVisible, setTooltipVisible] = useState(false);\n const [tooltipX, setTooltipX] = useState(0);\n const [tooltipY, setTooltipY] = useState(0);\n const [activePoint, setActivePoint] = useState<DataPoint | null>(null);\n const [activeSeries, setActiveSeries] = useState<DataSeries | null>(null);\n\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - marginLeft;\n\n if (mouseX < 0 || mouseX > plotWidth) {\n setTooltipVisible(false);\n return;\n }\n\n let closestPoint: DataPoint | null = null;\n let closestSeries: DataSeries | null = null;\n let closestDist = Infinity;\n\n for (const series of data) {\n const nearest = findNearestByPixelX(series.data, mouseX, xScale);\n if (!nearest) continue;\n\n const dist = Math.abs(xScale(nearest.x) - mouseX);\n if (dist < closestDist) {\n closestDist = dist;\n closestPoint = nearest;\n closestSeries = series;\n }\n }\n\n if (closestPoint && closestSeries) {\n setTooltipX(xScale(closestPoint.x));\n setTooltipY(yScale(closestPoint.y));\n setActivePoint(closestPoint);\n setActiveSeries(closestSeries);\n setTooltipVisible(true);\n }\n },\n [svgRef, data, xScale, yScale, marginLeft, plotWidth]\n );\n\n const handleMouseLeave = useCallback(() => {\n setTooltipVisible(false);\n setActivePoint(null);\n setActiveSeries(null);\n }, []);\n\n return {\n tooltipVisible,\n tooltipX,\n tooltipY,\n activePoint,\n activeSeries,\n handleMouseMove,\n handleMouseLeave,\n };\n}\n","import React from \"react\";\nimport type { AxisConfig, ChartStyle } from \"../types/chart.types\";\n\ninterface AxesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n xAxisConfig?: AxisConfig;\n yAxisConfig?: AxisConfig;\n style?: ChartStyle;\n}\n\nconst defaultTickFormat = (v: number): string => {\n if (Math.abs(v) >= 1e6) return `${(v / 1e6).toFixed(1)}M`;\n if (Math.abs(v) >= 1e3) return `${(v / 1e3).toFixed(1)}K`;\n return Number.isInteger(v) ? v.toString() : v.toFixed(1);\n};\n\nexport const Axes: React.FC<AxesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n style,\n }) => {\n const axisColor = style?.axisColor ?? \"#333\";\n const tickColor = style?.tickColor ?? \"#666\";\n const fontFamily = style?.fontFamily ?? \"sans-serif\";\n const fontSize = style?.fontSize ?? 11;\n const xTickFormat = xAxisConfig?.tickFormat ?? defaultTickFormat;\n const yTickFormat = yAxisConfig?.tickFormat ?? defaultTickFormat;\n\n return (\n <g className=\"chartifypdf-axes\">\n {/* X Axis */}\n <line\n x1={0}\n y1={plotHeight}\n x2={plotWidth}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <g key={`x-tick-${tick}`}>\n <line\n x1={x}\n y1={plotHeight}\n x2={x}\n y2={plotHeight + 6}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={x}\n y={plotHeight + 18}\n textAnchor=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {xTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {xAxisConfig?.label && (\n <text\n x={plotWidth / 2}\n y={plotHeight + 38}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n >\n {xAxisConfig.label}\n </text>\n )}\n\n {/* Y Axis */}\n <line\n x1={0}\n y1={0}\n x2={0}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={-6}\n y1={y}\n x2={0}\n y2={y}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={-10}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {yTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {yAxisConfig?.label && (\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n transform={`translate(${-40}, ${plotHeight / 2}) rotate(-90)`}\n >\n {yAxisConfig.label}\n </text>\n )}\n </g>\n );\n }\n);\n\nAxes.displayName = \"Axes\";\n","import React from \"react\";\n\ninterface GridLinesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n showXGrid: boolean;\n showYGrid: boolean;\n xGridColor?: string;\n yGridColor?: string;\n}\n\nexport const GridLines: React.FC<GridLinesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n showXGrid,\n showYGrid,\n xGridColor = \"#e0e0e0\",\n yGridColor = \"#e0e0e0\",\n }) => {\n return (\n <g className=\"chartifypdf-grid\">\n {showYGrid &&\n yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <line\n key={`y-grid-${tick}`}\n x1={0}\n y1={y}\n x2={plotWidth}\n y2={y}\n stroke={yGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n {showXGrid &&\n xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <line\n key={`x-grid-${tick}`}\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke={xGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n </g>\n );\n }\n);\n\nGridLines.displayName = \"GridLines\";\n","import React, { useRef, useEffect, useState } from \"react\";\nimport type { DataSeries, DataPoint, AnimationConfig } from \"../types/chart.types\";\nimport { buildLinePath } from \"../utils/path\";\n\ninterface LinePathProps {\n series: DataSeries;\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n color: string;\n animation?: AnimationConfig;\n onPointClick?: (point: DataPoint, series: DataSeries) => void;\n}\n\nexport const LinePath: React.FC<LinePathProps> = React.memo(\n ({ series, xScale, yScale, color, animation, onPointClick }) => {\n const pathRef = useRef<SVGPathElement>(null);\n const [pathLength, setPathLength] = useState(0);\n const [animationDone, setAnimationDone] = useState(!animation?.enabled);\n\n const strokeWidth = series.strokeWidth ?? 2;\n const showDots = series.showDots ?? false;\n const dotRadius = series.dotRadius ?? 3.5;\n\n const points = series.data.map((pt) => ({\n x: xScale(pt.x),\n y: yScale(pt.y),\n }));\n\n const d = buildLinePath(points);\n\n useEffect(() => {\n if (animation?.enabled && pathRef.current) {\n const length = pathRef.current.getTotalLength();\n setPathLength(length);\n setAnimationDone(false);\n\n const timer = setTimeout(() => {\n setAnimationDone(true);\n }, animation.duration ?? 800);\n\n return () => clearTimeout(timer);\n }\n }, [d, animation?.enabled, animation?.duration]);\n\n const animStyle: React.CSSProperties =\n animation?.enabled && pathLength > 0\n ? {\n strokeDasharray: pathLength,\n strokeDashoffset: animationDone ? 0 : pathLength,\n transition: `stroke-dashoffset ${animation.duration ?? 800}ms ${animation.easing ?? \"ease-in-out\"}`,\n }\n : {};\n\n return (\n <g className=\"chartifypdf-line-path\">\n <path\n ref={pathRef}\n d={d}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={series.strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={animStyle}\n />\n {showDots &&\n series.data.map((pt, i) => (\n <circle\n key={`dot-${series.id}-${i}`}\n cx={xScale(pt.x)}\n cy={yScale(pt.y)}\n r={dotRadius}\n fill={color}\n stroke=\"#fff\"\n strokeWidth={1.5}\n style={{ cursor: onPointClick ? \"pointer\" : \"default\" }}\n onClick={\n onPointClick\n ? () => onPointClick(pt, series)\n : undefined\n }\n />\n ))}\n </g>\n );\n }\n);\n\nLinePath.displayName = \"LinePath\";\n","export function buildLinePath(\n points: { x: number; y: number }[]\n): string {\n if (points.length === 0) return \"\";\n const [first, ...rest] = points;\n return `M${first.x},${first.y}` + rest.map((p) => `L${p.x},${p.y}`).join(\"\");\n}\n","import React from \"react\";\nimport type { DataPoint, DataSeries, TooltipConfig } from \"../types/chart.types\";\n\ninterface TooltipProps {\n visible: boolean;\n x: number;\n y: number;\n point: DataPoint | null;\n series: DataSeries | null;\n plotHeight: number;\n plotWidth: number;\n config?: TooltipConfig;\n seriesColor: string;\n}\n\nexport const Tooltip: React.FC<TooltipProps> = React.memo(\n ({ visible, x, y, point, series, plotHeight, plotWidth, config, seriesColor }) => {\n if (!visible || !point || !series) return null;\n\n const bgColor = config?.backgroundColor ?? \"rgba(0, 0, 0, 0.8)\";\n const textColor = config?.textColor ?? \"#fff\";\n\n // Determine tooltip text\n let content: React.ReactNode;\n if (config?.renderCustom) {\n content = config.renderCustom(point, series);\n } else {\n const text = config?.formatter\n ? config.formatter(point, series)\n : `${series.name}: (${point.x}, ${point.y})`;\n content = text;\n }\n\n // Smart positioning: flip when near edges\n const tooltipWidth = 140;\n const tooltipHeight = 40;\n let tx = x + 12;\n let ty = y - tooltipHeight - 8;\n\n if (tx + tooltipWidth > plotWidth) {\n tx = x - tooltipWidth - 12;\n }\n if (ty < 0) {\n ty = y + 12;\n }\n\n return (\n <g className=\"chartifypdf-tooltip\" pointerEvents=\"none\">\n {/* Vertical indicator line */}\n <line\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke=\"#999\"\n strokeWidth={1}\n strokeDasharray=\"4 4\"\n opacity={0.6}\n />\n {/* Highlight circle */}\n <circle\n cx={x}\n cy={y}\n r={5}\n fill={seriesColor}\n stroke=\"#fff\"\n strokeWidth={2}\n />\n {/* Tooltip box using foreignObject */}\n <foreignObject\n x={tx}\n y={ty}\n width={tooltipWidth}\n height={tooltipHeight + 20}\n overflow=\"visible\"\n >\n <div\n style={{\n backgroundColor: bgColor,\n color: textColor,\n padding: \"6px 10px\",\n borderRadius: \"4px\",\n fontSize: \"12px\",\n fontFamily: \"sans-serif\",\n whiteSpace: \"nowrap\",\n lineHeight: 1.4,\n boxShadow: \"0 2px 6px rgba(0,0,0,0.2)\",\n }}\n >\n {content}\n </div>\n </foreignObject>\n </g>\n );\n }\n);\n\nTooltip.displayName = \"Tooltip\";\n","import React from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\n\ninterface ZoomControlsProps {\n config?: ZoomConfig;\n svgWidth: number;\n svgHeight: number;\n margin: { top: number; right: number; bottom: number; left: number };\n scale: number;\n onZoomIn: () => void;\n onZoomOut: () => void;\n onReset: () => void;\n}\n\nexport const ZoomControls: React.FC<ZoomControlsProps> = React.memo(\n ({ config, svgWidth, svgHeight, margin, scale, onZoomIn, onZoomOut, onReset }) => {\n if (!config?.enabled || config.showControls === false) return null;\n\n const position = config.controlsPosition ?? \"top-right\";\n const btnSize = 24;\n const gap = 4;\n const groupWidth = btnSize * 3 + gap * 2;\n const groupHeight = btnSize;\n const padding = 8;\n\n let gx: number;\n let gy: number;\n\n switch (position) {\n case \"top-left\":\n gx = margin.left + padding;\n gy = margin.top + padding;\n break;\n case \"bottom-left\":\n gx = margin.left + padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"bottom-right\":\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"top-right\":\n default:\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = margin.top + padding;\n break;\n }\n\n const buttons = [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2013\", onClick: onReset }, // en-dash for reset\n { label: \"\\u2212\", onClick: onZoomOut }, // minus sign\n ];\n\n // Replace middle button with reset when zoomed, or show as \"-\"\n const displayButtons =\n scale > 1\n ? [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n { label: \"\\u21BA\", onClick: onReset },\n ]\n : [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n ];\n\n return (\n <g\n className=\"chartifypdf-zoom-controls\"\n transform={`translate(${gx}, ${gy})`}\n >\n {displayButtons.map((btn, i) => (\n <g\n key={btn.label}\n transform={`translate(${i * (btnSize + gap)}, 0)`}\n onClick={btn.onClick}\n style={{ cursor: \"pointer\" }}\n >\n <rect\n width={btnSize}\n height={btnSize}\n rx={4}\n fill=\"rgba(255,255,255,0.9)\"\n stroke=\"#ccc\"\n strokeWidth={1}\n />\n <text\n x={btnSize / 2}\n y={btnSize / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={14}\n fontFamily=\"sans-serif\"\n fill=\"#333\"\n fontWeight=\"bold\"\n >\n {btn.label}\n </text>\n </g>\n ))}\n </g>\n );\n }\n);\n\nZoomControls.displayName = \"ZoomControls\";\n","import React, { useRef, useId } from \"react\";\nimport type {\n LineChartProps,\n ChartMargin,\n} from \"../types/chart.types\";\nimport { getSeriesColor } from \"../utils/color\";\nimport { useChartDimensions } from \"../hooks/useChartDimensions\";\nimport { useScales } from \"../hooks/useScales\";\nimport { useZoom } from \"../hooks/useZoom\";\nimport { useTooltip } from \"../hooks/useTooltip\";\nimport { Axes } from \"./Axes\";\nimport { GridLines } from \"./GridLines\";\nimport { LinePath } from \"./LinePath\";\nimport { Tooltip } from \"./Tooltip\";\nimport { ZoomControls } from \"./ZoomControls\";\n\nconst DEFAULT_MARGIN: ChartMargin = {\n top: 20,\n right: 20,\n bottom: 50,\n left: 60,\n};\n\nconst DEFAULT_HEIGHT = 400;\n\nexport const LineChart: React.FC<LineChartProps> = ({\n data,\n width: providedWidth,\n height: providedHeight,\n margin: marginOverride,\n xAxis: xAxisConfig,\n yAxis: yAxisConfig,\n tooltip: tooltipConfig,\n zoom: zoomConfig,\n style: chartStyle,\n animation,\n colorPalette,\n className,\n onPointClick,\n ariaLabel,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const clipId = useId();\n\n const margin: ChartMargin = {\n ...DEFAULT_MARGIN,\n ...marginOverride,\n };\n\n const dimensions = useChartDimensions(\n containerRef,\n providedWidth,\n providedHeight ?? DEFAULT_HEIGHT\n );\n const { width: svgWidth, height: svgHeight } = dimensions;\n\n const plotWidth = Math.max(0, svgWidth - margin.left - margin.right);\n const plotHeight = Math.max(0, svgHeight - margin.top - margin.bottom);\n\n const { xScale, yScale, xTicks, yTicks } = useScales(\n data,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig\n );\n\n const zoomState = useZoom(zoomConfig, plotWidth, plotHeight);\n\n const tooltipEnabled = tooltipConfig?.enabled !== false;\n const tooltipState = useTooltip(\n svgRef,\n data,\n xScale,\n yScale,\n margin.left,\n margin.top,\n plotWidth,\n tooltipConfig\n );\n\n const showXGrid = xAxisConfig?.gridLines ?? false;\n const showYGrid = yAxisConfig?.gridLines ?? true;\n\n // Don't render until we have dimensions\n if (svgWidth === 0 || svgHeight === 0) {\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n />\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n >\n <svg\n ref={svgRef}\n width={svgWidth}\n height={svgHeight}\n role=\"img\"\n aria-label={ariaLabel ?? \"Line chart\"}\n onMouseMove={\n tooltipEnabled && !zoomState.isPanning\n ? tooltipState.handleMouseMove\n : undefined\n }\n onMouseLeave={tooltipEnabled ? tooltipState.handleMouseLeave : undefined}\n onWheel={zoomConfig?.enabled ? zoomState.handleWheel : undefined}\n onMouseDown={zoomConfig?.enabled ? zoomState.handlePanStart : undefined}\n onMouseUp={zoomConfig?.enabled ? zoomState.handlePanEnd : undefined}\n style={{\n userSelect: zoomState.isPanning ? \"none\" : undefined,\n cursor: zoomState.isPanning\n ? \"grabbing\"\n : zoomConfig?.enabled && zoomState.scale > 1\n ? \"grab\"\n : undefined,\n }}\n >\n <defs>\n <clipPath id={clipId}>\n <rect width={plotWidth} height={plotHeight} />\n </clipPath>\n </defs>\n\n <g transform={`translate(${margin.left}, ${margin.top})`}>\n {/* Axes render outside zoom transform — always crisp */}\n <Axes\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n xAxisConfig={xAxisConfig}\n yAxisConfig={yAxisConfig}\n style={chartStyle}\n />\n\n {/* Grid lines */}\n <GridLines\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n showXGrid={showXGrid}\n showYGrid={showYGrid}\n xGridColor={xAxisConfig?.gridLineColor}\n yGridColor={yAxisConfig?.gridLineColor}\n />\n\n {/* Clipped + zoomed data area */}\n <g clipPath={`url(#${clipId})`}>\n <g\n transform={`translate(${zoomState.offsetX}, ${zoomState.offsetY}) scale(${zoomState.scale})`}\n onMouseMove={\n zoomConfig?.enabled ? zoomState.handlePanMove : undefined\n }\n >\n {data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={\n series.color ?? getSeriesColor(i, colorPalette)\n }\n animation={animation}\n onPointClick={onPointClick}\n />\n ))}\n </g>\n </g>\n\n {/* Tooltip renders above everything in plot area */}\n {tooltipEnabled && (\n <Tooltip\n visible={tooltipState.tooltipVisible}\n x={tooltipState.tooltipX}\n y={tooltipState.tooltipY}\n point={tooltipState.activePoint}\n series={tooltipState.activeSeries}\n plotHeight={plotHeight}\n plotWidth={plotWidth}\n config={tooltipConfig}\n seriesColor={\n tooltipState.activeSeries?.color ??\n getSeriesColor(\n data.findIndex(\n (s) => s.id === tooltipState.activeSeries?.id\n ),\n colorPalette\n )\n }\n />\n )}\n </g>\n\n {/* Zoom controls render at SVG root level */}\n <ZoomControls\n config={zoomConfig}\n svgWidth={svgWidth}\n svgHeight={svgHeight}\n margin={margin}\n scale={zoomState.scale}\n onZoomIn={zoomState.zoomIn}\n onZoomOut={zoomState.zoomOut}\n onReset={zoomState.resetZoom}\n />\n </svg>\n </div>\n );\n};\n\nLineChart.displayName = \"LineChart\";\n"],"names":["DEFAULT_PALETTE","getSeriesColor","index","palette","colors","length","useChartDimensions","containerRef","providedWidth","providedHeight","dimensions","setDimensions","useState","width","height","isFixed","undefined","useLayoutEffect","node","current","measure","rect","getBoundingClientRect","observer","ResizeObserver","observe","disconnect","clamp","value","min","max","Math","niceNumber","range","round","exponent","floor","log10","fraction","pow","niceFraction","niceRange","tickCount","offset","abs","tickStep","niceMin","niceMax","ceil","linearScale","domainMin","domainMax","rangeMin","rangeMax","t","a","b","inverseLerp","lerp","generateTicks","ticks","v","push","parseFloat","toPrecision","useScales","data","plotWidth","plotHeight","xAxisConfig","yAxisConfig","useMemo","xMin","Infinity","xMax","yMin","yMax","series","point","x","y","isFinite","xTickCount","yTickCount","xNice","yNice","xScale","yScale","xTicks","yTicks","xDomain","yDomain","useZoom","config","enabled","minScale","maxScale","step","enableWheel","enablePan","scale","setScale","offsetX","setOffsetX","offsetY","setOffsetY","isPanning","setIsPanning","panStart","useRef","clampOffset","useCallback","ox","oy","s","maxOffsetY","zoomIn","prev","next","zoomOut","resetZoom","handleWheel","e","preventDefault","currentTarget","mouseX","clientX","left","mouseY","clientY","top","delta","deltaY","ratio","handlePanStart","button","handlePanMove","dx","dy","clamped","handlePanEnd","findNearestByPixelX","sorted","low","high","mid","nearest","minDist","dist","useTooltip","svgRef","marginLeft","marginTop","_config","tooltipVisible","setTooltipVisible","tooltipX","setTooltipX","tooltipY","setTooltipY","activePoint","setActivePoint","activeSeries","setActiveSeries","handleMouseMove","svg","closestPoint","closestSeries","closestDist","handleMouseLeave","defaultTickFormat","toFixed","Number","isInteger","toString","Axes","React","memo","style","axisColor","tickColor","fontFamily","fontSize","xTickFormat","tickFormat","yTickFormat","_jsxs","className","_jsx","x1","y1","x2","y2","stroke","strokeWidth","map","tick","children","textAnchor","fill","label","fontWeight","dominantBaseline","transform","displayName","GridLines","showXGrid","showYGrid","xGridColor","yGridColor","strokeDasharray","strokeOpacity","LinePath","color","animation","onPointClick","pathRef","pathLength","setPathLength","animationDone","setAnimationDone","showDots","dotRadius","d","points","first","rest","p","join","buildLinePath","pt","useEffect","getTotalLength","timer","setTimeout","duration","clearTimeout","animStyle","strokeDashoffset","transition","easing","ref","strokeLinecap","strokeLinejoin","i","cx","cy","r","cursor","onClick","id","Tooltip","visible","seriesColor","bgColor","backgroundColor","textColor","content","renderCustom","formatter","name","tx","ty","pointerEvents","opacity","tooltipHeight","overflow","padding","borderRadius","whiteSpace","lineHeight","boxShadow","ZoomControls","svgWidth","svgHeight","margin","onZoomIn","onZoomOut","onReset","showControls","position","controlsPosition","btnSize","gx","gy","bottom","right","btn","rx","DEFAULT_MARGIN","LineChart","marginOverride","xAxis","yAxis","tooltip","tooltipConfig","zoom","zoomConfig","chartStyle","colorPalette","ariaLabel","clipId","useId","zoomState","tooltipEnabled","tooltipState","gridLines","role","onMouseMove","onMouseLeave","onWheel","onMouseDown","onMouseUp","userSelect","gridLineColor","clipPath","findIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/utils/color.ts","../src/hooks/useChartDimensions.ts","../src/utils/math.ts","../src/hooks/useDataDomain.ts","../src/hooks/useScales.ts","../src/hooks/useZoom.ts","../src/hooks/useTooltip.ts","../src/components/Axes.tsx","../src/components/GridLines.tsx","../src/utils/path.ts","../src/components/LinePath.tsx","../src/components/Tooltip.tsx","../src/components/ZoomControls.tsx","../src/components/PeakMarkers.tsx","../src/components/ContextMenu.tsx","../src/components/LineChart.tsx"],"sourcesContent":["export const DEFAULT_PALETTE = [\n \"#4e79a7\",\n \"#f28e2b\",\n \"#e15759\",\n \"#76b7b2\",\n \"#59a14f\",\n \"#edc948\",\n \"#b07aa1\",\n \"#ff9da7\",\n \"#9c755f\",\n \"#bab0ac\",\n];\n\nexport function getSeriesColor(index: number, palette?: string[]): string {\n const colors = palette ?? DEFAULT_PALETTE;\n return colors[index % colors.length];\n}\n","import { useLayoutEffect, useState, type RefObject } from \"react\";\n\nexport function useChartDimensions(\n containerRef: RefObject<HTMLDivElement | null>,\n providedWidth?: number,\n providedHeight?: number\n): { width: number; height: number } {\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n }>({\n width: providedWidth ?? 0,\n height: providedHeight ?? 0,\n });\n\n const isFixed =\n providedWidth !== undefined && providedHeight !== undefined;\n\n useLayoutEffect(() => {\n if (isFixed) {\n setDimensions({ width: providedWidth!, height: providedHeight! });\n return;\n }\n\n const node = containerRef.current;\n if (!node) return;\n\n const measure = () => {\n const rect = node.getBoundingClientRect();\n setDimensions({ width: rect.width, height: rect.height });\n };\n\n measure();\n\n const observer = new ResizeObserver(() => {\n measure();\n });\n observer.observe(node);\n\n return () => observer.disconnect();\n }, [containerRef, isFixed, providedWidth, providedHeight]);\n\n return dimensions;\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function niceNumber(range: number, round: boolean): number {\n const exponent = Math.floor(Math.log10(range));\n const fraction = range / Math.pow(10, exponent);\n\n let niceFraction: number;\n if (round) {\n if (fraction < 1.5) niceFraction = 1;\n else if (fraction < 3) niceFraction = 2;\n else if (fraction < 7) niceFraction = 5;\n else niceFraction = 10;\n } else {\n if (fraction <= 1) niceFraction = 1;\n else if (fraction <= 2) niceFraction = 2;\n else if (fraction <= 5) niceFraction = 5;\n else niceFraction = 10;\n }\n\n return niceFraction * Math.pow(10, exponent);\n}\n\nexport function niceRange(\n min: number,\n max: number,\n tickCount: number\n): { niceMin: number; niceMax: number; tickStep: number } {\n if (min === max) {\n const offset = min === 0 ? 1 : Math.abs(min) * 0.1;\n min = min - offset;\n max = max + offset;\n }\n\n const range = niceNumber(max - min, false);\n const tickStep = niceNumber(range / (tickCount - 1), true);\n const niceMin = Math.floor(min / tickStep) * tickStep;\n const niceMax = Math.ceil(max / tickStep) * tickStep;\n\n return { niceMin, niceMax, tickStep };\n}\n\nexport function linearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (value: number) => number {\n return (value: number) => {\n if (domainMax === domainMin) return (rangeMin + rangeMax) / 2;\n const t = inverseLerp(domainMin, domainMax, value);\n return lerp(rangeMin, rangeMax, t);\n };\n}\n\nexport function generateTicks(\n niceMin: number,\n niceMax: number,\n tickStep: number\n): number[] {\n const ticks: number[] = [];\n for (let v = niceMin; v <= niceMax + tickStep * 0.5; v += tickStep) {\n ticks.push(parseFloat(v.toPrecision(12)));\n }\n return ticks;\n}\n\nexport function inverseLinearScale(\n domainMin: number,\n domainMax: number,\n rangeMin: number,\n rangeMax: number\n): (pixel: number) => number {\n return (pixel: number) => {\n if (rangeMax === rangeMin) return (domainMin + domainMax) / 2;\n const t = inverseLerp(rangeMin, rangeMax, pixel);\n return lerp(domainMin, domainMax, t);\n };\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange } from \"../utils/math\";\n\nexport interface DataDomain {\n fullXDomain: [number, number];\n fullYDomain: [number, number];\n}\n\nexport function useDataDomain(\n data: DataSeries[],\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig\n): DataDomain {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n const xNice = niceRange(xMin, xMax, xTickCount);\n const yNice = niceRange(yMin, yMax, yTickCount);\n\n return {\n fullXDomain: [xNice.niceMin, xNice.niceMax] as [number, number],\n fullYDomain: [yNice.niceMin, yNice.niceMax] as [number, number],\n };\n }, [data, xAxisConfig, yAxisConfig]);\n}\n","import { useMemo } from \"react\";\nimport type { DataSeries, AxisConfig } from \"../types/chart.types\";\nimport { niceRange, linearScale, generateTicks } from \"../utils/math\";\n\nexport interface ScalesResult {\n xScale: (value: number) => number;\n yScale: (value: number) => number;\n xTicks: number[];\n yTicks: number[];\n xDomain: [number, number];\n yDomain: [number, number];\n}\n\nexport function useScales(\n data: DataSeries[],\n plotWidth: number,\n plotHeight: number,\n xAxisConfig?: AxisConfig,\n yAxisConfig?: AxisConfig,\n viewXDomain?: [number, number],\n viewYDomain?: [number, number]\n): ScalesResult {\n return useMemo(() => {\n let xMin = Infinity;\n let xMax = -Infinity;\n let yMin = Infinity;\n let yMax = -Infinity;\n\n for (const series of data) {\n for (const point of series.data) {\n if (point.x < xMin) xMin = point.x;\n if (point.x > xMax) xMax = point.x;\n if (point.y < yMin) yMin = point.y;\n if (point.y > yMax) yMax = point.y;\n }\n }\n\n if (!isFinite(xMin)) {\n xMin = 0;\n xMax = 1;\n yMin = 0;\n yMax = 1;\n }\n\n if (xAxisConfig?.min !== undefined) xMin = xAxisConfig.min;\n if (xAxisConfig?.max !== undefined) xMax = xAxisConfig.max;\n if (yAxisConfig?.min !== undefined) yMin = yAxisConfig.min;\n if (yAxisConfig?.max !== undefined) yMax = yAxisConfig.max;\n\n const xTickCount = xAxisConfig?.tickCount ?? 6;\n const yTickCount = yAxisConfig?.tickCount ?? 6;\n\n // Use viewDomain if provided (semantic zoom), otherwise compute from data\n let xDomainMin: number, xDomainMax: number;\n let yDomainMin: number, yDomainMax: number;\n\n if (viewXDomain) {\n xDomainMin = viewXDomain[0];\n xDomainMax = viewXDomain[1];\n } else {\n const xNice = niceRange(xMin, xMax, xTickCount);\n xDomainMin = xNice.niceMin;\n xDomainMax = xNice.niceMax;\n }\n\n if (viewYDomain) {\n yDomainMin = viewYDomain[0];\n yDomainMax = viewYDomain[1];\n } else {\n const yNice = niceRange(yMin, yMax, yTickCount);\n yDomainMin = yNice.niceMin;\n yDomainMax = yNice.niceMax;\n }\n\n // Compute nice ticks for the current view range\n const xViewNice = niceRange(xDomainMin, xDomainMax, xTickCount);\n const yViewNice = niceRange(yDomainMin, yDomainMax, yTickCount);\n\n const xScale = linearScale(xDomainMin, xDomainMax, 0, plotWidth);\n // Y inverted: SVG Y increases downward\n const yScale = linearScale(yDomainMin, yDomainMax, plotHeight, 0);\n\n const xTicks = generateTicks(xViewNice.niceMin, xViewNice.niceMax, xViewNice.tickStep)\n .filter((t) => t >= xDomainMin && t <= xDomainMax);\n const yTicks = generateTicks(yViewNice.niceMin, yViewNice.niceMax, yViewNice.tickStep)\n .filter((t) => t >= yDomainMin && t <= yDomainMax);\n\n return {\n xScale,\n yScale,\n xTicks,\n yTicks,\n xDomain: [xDomainMin, xDomainMax] as [number, number],\n yDomain: [yDomainMin, yDomainMax] as [number, number],\n };\n }, [data, plotWidth, plotHeight, xAxisConfig, yAxisConfig, viewXDomain, viewYDomain]);\n}\n","import { useState, useCallback, useRef } from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\nimport { clamp, lerp } from \"../utils/math\";\n\nexport interface ZoomState {\n viewXDomain: [number, number];\n viewYDomain: [number, number];\n scale: number;\n isPanning: boolean;\n showZoomHint: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n resetZoom: () => void;\n handleWheel: (e: React.WheelEvent) => void;\n handlePanStart: (e: React.MouseEvent) => void;\n handlePanMove: (e: React.MouseEvent) => void;\n handlePanEnd: () => void;\n}\n\nexport function useZoom(\n config: ZoomConfig | undefined,\n fullXDomain: [number, number],\n fullYDomain: [number, number],\n plotWidth: number,\n plotHeight: number\n): ZoomState {\n const enabled = config?.enabled ?? false;\n const minScale = config?.minScale ?? 1;\n const maxScale = config?.maxScale ?? 10;\n const step = config?.step ?? 0.5;\n const enableWheel = config?.enableWheel ?? true;\n const enablePan = config?.enablePan ?? true;\n const requireCtrlKey = config?.requireCtrlKey ?? true;\n\n const [viewXDomain, setViewXDomain] = useState<[number, number]>(fullXDomain);\n const [viewYDomain, setViewYDomain] = useState<[number, number]>(fullYDomain);\n const [isPanning, setIsPanning] = useState(false);\n const [showZoomHint, setShowZoomHint] = useState(false);\n\n const panStart = useRef({ x: 0, y: 0, xDomain: fullXDomain, yDomain: fullYDomain });\n const hintTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastFullDomain = useRef({ x: fullXDomain, y: fullYDomain });\n\n // Sync view domain when full domain changes (new data)\n if (\n fullXDomain[0] !== lastFullDomain.current.x[0] ||\n fullXDomain[1] !== lastFullDomain.current.x[1] ||\n fullYDomain[0] !== lastFullDomain.current.y[0] ||\n fullYDomain[1] !== lastFullDomain.current.y[1]\n ) {\n lastFullDomain.current = { x: fullXDomain, y: fullYDomain };\n setViewXDomain(fullXDomain);\n setViewYDomain(fullYDomain);\n }\n\n // Derived scale for UI (ratio of full range to view range)\n const fullXRange = fullXDomain[1] - fullXDomain[0];\n const viewXRange = viewXDomain[1] - viewXDomain[0];\n const scale = fullXRange > 0 && viewXRange > 0 ? fullXRange / viewXRange : 1;\n\n const clampDomain = useCallback(\n (\n domain: [number, number],\n fullDomain: [number, number]\n ): [number, number] => {\n const range = domain[1] - domain[0];\n const fullRange = fullDomain[1] - fullDomain[0];\n // Don't allow zooming out beyond full domain\n if (range >= fullRange) return fullDomain;\n // Clamp to bounds\n let min = domain[0];\n let max = domain[1];\n if (min < fullDomain[0]) {\n min = fullDomain[0];\n max = min + range;\n }\n if (max > fullDomain[1]) {\n max = fullDomain[1];\n min = max - range;\n }\n return [min, max];\n },\n []\n );\n\n const showHint = useCallback(() => {\n if (hintTimer.current) clearTimeout(hintTimer.current);\n setShowZoomHint(true);\n hintTimer.current = setTimeout(() => setShowZoomHint(false), 2000);\n }, []);\n\n const zoomByFactor = useCallback(\n (factor: number, centerXFrac?: number, centerYFrac?: number) => {\n if (!enabled) return;\n\n const cxFrac = centerXFrac ?? 0.5;\n const cyFrac = centerYFrac ?? 0.5;\n\n setViewXDomain((prev) => {\n const range = prev[1] - prev[0];\n const newRange = range / factor;\n const minRange = fullXRange / maxScale;\n const clampedRange = Math.max(newRange, minRange);\n const finalRange = Math.min(clampedRange, fullXRange);\n\n const center = lerp(prev[0], prev[1], cxFrac);\n const newMin = center - finalRange * cxFrac;\n const newMax = newMin + finalRange;\n return clampDomain([newMin, newMax], fullXDomain);\n });\n\n setViewYDomain((prev) => {\n const range = prev[1] - prev[0];\n const fullYRange = fullYDomain[1] - fullYDomain[0];\n const newRange = range / factor;\n const minRange = fullYRange / maxScale;\n const clampedRange = Math.max(newRange, minRange);\n const finalRange = Math.min(clampedRange, fullYRange);\n\n const center = lerp(prev[0], prev[1], cyFrac);\n const newMin = center - finalRange * cyFrac;\n const newMax = newMin + finalRange;\n return clampDomain([newMin, newMax], fullYDomain);\n });\n },\n [enabled, fullXDomain, fullYDomain, fullXRange, maxScale, clampDomain]\n );\n\n const zoomIn = useCallback(() => {\n const factor = 1 + step;\n zoomByFactor(factor);\n }, [step, zoomByFactor]);\n\n const zoomOut = useCallback(() => {\n const factor = 1 / (1 + step);\n zoomByFactor(factor);\n }, [step, zoomByFactor]);\n\n const resetZoom = useCallback(() => {\n setViewXDomain(fullXDomain);\n setViewYDomain(fullYDomain);\n }, [fullXDomain, fullYDomain]);\n\n const handleWheel = useCallback(\n (e: React.WheelEvent) => {\n if (!enabled || !enableWheel) return;\n\n // Ctrl+wheel guard\n if (requireCtrlKey && !e.ctrlKey && !e.metaKey) {\n showHint();\n return; // Let page scroll normally\n }\n\n e.preventDefault();\n\n const rect = (e.currentTarget as SVGElement).getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n // Compute mouse position as fraction of plot area\n // Account for margins by treating the full SVG rect\n const cxFrac = clamp(mouseX / (rect.width || 1), 0, 1);\n const cyFrac = clamp(mouseY / (rect.height || 1), 0, 1);\n\n const delta = e.deltaY < 0 ? 1 + step : 1 / (1 + step);\n zoomByFactor(delta, cxFrac, cyFrac);\n },\n [enabled, enableWheel, requireCtrlKey, step, showHint, zoomByFactor]\n );\n\n const handlePanStart = useCallback(\n (e: React.MouseEvent) => {\n if (!enabled || !enablePan || scale <= 1) return;\n if (e.button !== 0) return; // left-click only\n setIsPanning(true);\n panStart.current = {\n x: e.clientX,\n y: e.clientY,\n xDomain: viewXDomain,\n yDomain: viewYDomain,\n };\n },\n [enabled, enablePan, scale, viewXDomain, viewYDomain]\n );\n\n const handlePanMove = useCallback(\n (e: React.MouseEvent) => {\n if (!isPanning) return;\n\n const dx = e.clientX - panStart.current.x;\n const dy = e.clientY - panStart.current.y;\n\n // Convert pixel delta to domain delta\n const prevXDomain = panStart.current.xDomain;\n const prevYDomain = panStart.current.yDomain;\n const xRange = prevXDomain[1] - prevXDomain[0];\n const yRange = prevYDomain[1] - prevYDomain[0];\n\n const domainDx = -(dx / plotWidth) * xRange;\n const domainDy = (dy / plotHeight) * yRange; // Y inverted\n\n const newXDomain: [number, number] = [\n prevXDomain[0] + domainDx,\n prevXDomain[1] + domainDx,\n ];\n const newYDomain: [number, number] = [\n prevYDomain[0] + domainDy,\n prevYDomain[1] + domainDy,\n ];\n\n setViewXDomain(clampDomain(newXDomain, fullXDomain));\n setViewYDomain(clampDomain(newYDomain, fullYDomain));\n },\n [isPanning, plotWidth, plotHeight, fullXDomain, fullYDomain, clampDomain]\n );\n\n const handlePanEnd = useCallback(() => {\n setIsPanning(false);\n }, []);\n\n return {\n viewXDomain,\n viewYDomain,\n scale,\n isPanning,\n showZoomHint,\n zoomIn,\n zoomOut,\n resetZoom,\n handleWheel,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n };\n}\n","import { useState, useCallback, type RefObject } from \"react\";\nimport type { DataSeries, DataPoint, TooltipConfig } from \"../types/chart.types\";\n\nexport interface TooltipState {\n tooltipVisible: boolean;\n tooltipX: number;\n tooltipY: number;\n activePoint: DataPoint | null;\n activeSeries: DataSeries | null;\n activeSeriesId: string | null;\n handleMouseMove: (e: React.MouseEvent) => void;\n handleMouseLeave: () => void;\n}\n\nfunction findNearestByPixelX(\n sorted: DataPoint[],\n mouseX: number,\n xScale: (v: number) => number\n): DataPoint | null {\n if (sorted.length === 0) return null;\n\n let low = 0;\n let high = sorted.length - 1;\n\n // Binary search for nearest pixel X\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n if (xScale(sorted[mid].x) < mouseX) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n let nearest = sorted[low];\n let minDist = Math.abs(xScale(nearest.x) - mouseX);\n\n if (low > 0) {\n const dist = Math.abs(xScale(sorted[low - 1].x) - mouseX);\n if (dist < minDist) {\n minDist = dist;\n nearest = sorted[low - 1];\n }\n }\n\n return nearest;\n}\n\nexport function useTooltip(\n svgRef: RefObject<SVGSVGElement | null>,\n data: DataSeries[],\n xScale: (v: number) => number,\n yScale: (v: number) => number,\n marginLeft: number,\n marginTop: number,\n plotWidth: number,\n _config?: TooltipConfig\n): TooltipState {\n const [tooltipVisible, setTooltipVisible] = useState(false);\n const [tooltipX, setTooltipX] = useState(0);\n const [tooltipY, setTooltipY] = useState(0);\n const [activePoint, setActivePoint] = useState<DataPoint | null>(null);\n const [activeSeries, setActiveSeries] = useState<DataSeries | null>(null);\n const [activeSeriesId, setActiveSeriesId] = useState<string | null>(null);\n\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - marginLeft;\n\n if (mouseX < 0 || mouseX > plotWidth) {\n setTooltipVisible(false);\n setActiveSeriesId(null);\n return;\n }\n\n let closestPoint: DataPoint | null = null;\n let closestSeries: DataSeries | null = null;\n let closestDist = Infinity;\n\n for (const series of data) {\n const nearest = findNearestByPixelX(series.data, mouseX, xScale);\n if (!nearest) continue;\n\n const dist = Math.abs(xScale(nearest.x) - mouseX);\n if (dist < closestDist) {\n closestDist = dist;\n closestPoint = nearest;\n closestSeries = series;\n }\n }\n\n if (closestPoint && closestSeries) {\n setTooltipX(xScale(closestPoint.x));\n setTooltipY(yScale(closestPoint.y));\n setActivePoint(closestPoint);\n setActiveSeries(closestSeries);\n setActiveSeriesId(closestSeries.id);\n setTooltipVisible(true);\n }\n },\n [svgRef, data, xScale, yScale, marginLeft, plotWidth]\n );\n\n const handleMouseLeave = useCallback(() => {\n setTooltipVisible(false);\n setActivePoint(null);\n setActiveSeries(null);\n setActiveSeriesId(null);\n }, []);\n\n return {\n tooltipVisible,\n tooltipX,\n tooltipY,\n activePoint,\n activeSeries,\n activeSeriesId,\n handleMouseMove,\n handleMouseLeave,\n };\n}\n","import React from \"react\";\nimport type { AxisConfig, ChartStyle } from \"../types/chart.types\";\n\ninterface AxesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n xAxisConfig?: AxisConfig;\n yAxisConfig?: AxisConfig;\n style?: ChartStyle;\n}\n\nconst defaultTickFormat = (v: number): string => {\n if (Math.abs(v) >= 1e6) return `${(v / 1e6).toFixed(1)}M`;\n if (Math.abs(v) >= 1e3) return `${(v / 1e3).toFixed(1)}K`;\n return Number.isInteger(v) ? v.toString() : v.toFixed(1);\n};\n\nexport const Axes: React.FC<AxesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n style,\n }) => {\n const axisColor = style?.axisColor ?? \"#333\";\n const tickColor = style?.tickColor ?? \"#666\";\n const fontFamily = style?.fontFamily ?? \"sans-serif\";\n const fontSize = style?.fontSize ?? 11;\n const xTickFormat = xAxisConfig?.tickFormat ?? defaultTickFormat;\n const yTickFormat = yAxisConfig?.tickFormat ?? defaultTickFormat;\n\n return (\n <g className=\"chartifypdf-axes\">\n {/* X Axis */}\n <line\n x1={0}\n y1={plotHeight}\n x2={plotWidth}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <g key={`x-tick-${tick}`}>\n <line\n x1={x}\n y1={plotHeight}\n x2={x}\n y2={plotHeight + 6}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={x}\n y={plotHeight + 18}\n textAnchor=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {xTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {xAxisConfig?.label && (\n <text\n x={plotWidth / 2}\n y={plotHeight + 38}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n >\n {xAxisConfig.label}\n </text>\n )}\n\n {/* Y Axis */}\n <line\n x1={0}\n y1={0}\n x2={0}\n y2={plotHeight}\n stroke={axisColor}\n strokeWidth={1}\n />\n {yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={-6}\n y1={y}\n x2={0}\n y2={y}\n stroke={axisColor}\n strokeWidth={1}\n />\n <text\n x={-10}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"middle\"\n fill={tickColor}\n fontFamily={fontFamily}\n fontSize={fontSize}\n >\n {yTickFormat(tick)}\n </text>\n </g>\n );\n })}\n {yAxisConfig?.label && (\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n fill={axisColor}\n fontFamily={fontFamily}\n fontSize={fontSize + 1}\n fontWeight=\"bold\"\n transform={`translate(${-40}, ${plotHeight / 2}) rotate(-90)`}\n >\n {yAxisConfig.label}\n </text>\n )}\n </g>\n );\n }\n);\n\nAxes.displayName = \"Axes\";\n","import React from \"react\";\n\ninterface GridLinesProps {\n xTicks: number[];\n yTicks: number[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n plotWidth: number;\n plotHeight: number;\n showXGrid: boolean;\n showYGrid: boolean;\n xGridColor?: string;\n yGridColor?: string;\n}\n\nexport const GridLines: React.FC<GridLinesProps> = React.memo(\n ({\n xTicks,\n yTicks,\n xScale,\n yScale,\n plotWidth,\n plotHeight,\n showXGrid,\n showYGrid,\n xGridColor = \"#e0e0e0\",\n yGridColor = \"#e0e0e0\",\n }) => {\n return (\n <g className=\"chartifypdf-grid\">\n {showYGrid &&\n yTicks.map((tick) => {\n const y = yScale(tick);\n return (\n <line\n key={`y-grid-${tick}`}\n x1={0}\n y1={y}\n x2={plotWidth}\n y2={y}\n stroke={yGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n {showXGrid &&\n xTicks.map((tick) => {\n const x = xScale(tick);\n return (\n <line\n key={`x-grid-${tick}`}\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke={xGridColor}\n strokeDasharray=\"4 4\"\n strokeOpacity={0.5}\n />\n );\n })}\n </g>\n );\n }\n);\n\nGridLines.displayName = \"GridLines\";\n","import type { CurveType } from \"../types/chart.types\";\n\nexport function buildLinePath(\n points: { x: number; y: number }[]\n): string {\n if (points.length === 0) return \"\";\n const [first, ...rest] = points;\n return `M${first.x},${first.y}` + rest.map((p) => `L${p.x},${p.y}`).join(\"\");\n}\n\n/**\n * Fritsch-Carlson monotone cubic interpolation.\n * Guarantees no overshoot — ideal for data visualization.\n */\nfunction buildMonotonePath(points: { x: number; y: number }[]): string {\n const n = points.length;\n if (n < 2) return buildLinePath(points);\n if (n === 2) return buildLinePath(points);\n\n // Compute slopes of secant lines\n const dx: number[] = [];\n const dy: number[] = [];\n const m: number[] = []; // slopes of secants\n for (let i = 0; i < n - 1; i++) {\n dx.push(points[i + 1].x - points[i].x);\n dy.push(points[i + 1].y - points[i].y);\n m.push(dx[i] === 0 ? 0 : dy[i] / dx[i]);\n }\n\n // Compute tangent slopes using Fritsch-Carlson method\n const tangents: number[] = new Array(n);\n tangents[0] = m[0];\n tangents[n - 1] = m[n - 2];\n\n for (let i = 1; i < n - 1; i++) {\n if (m[i - 1] * m[i] <= 0) {\n // Local extremum or flat — zero tangent\n tangents[i] = 0;\n } else {\n // Harmonic mean of adjacent slopes\n tangents[i] = 2 / (1 / m[i - 1] + 1 / m[i]);\n }\n }\n\n // Clamp tangents to prevent overshoot (Fritsch-Carlson)\n for (let i = 0; i < n - 1; i++) {\n if (m[i] === 0) {\n tangents[i] = 0;\n tangents[i + 1] = 0;\n } else {\n const alpha = tangents[i] / m[i];\n const beta = tangents[i + 1] / m[i];\n // Ensure within monotone bounds\n const s = alpha * alpha + beta * beta;\n if (s > 9) {\n const tau = 3 / Math.sqrt(s);\n tangents[i] = tau * alpha * m[i];\n tangents[i + 1] = tau * beta * m[i];\n }\n }\n }\n\n // Build SVG path with cubic bezier segments\n let d = `M${points[0].x},${points[0].y}`;\n for (let i = 0; i < n - 1; i++) {\n const segLen = dx[i] / 3;\n const cp1x = points[i].x + segLen;\n const cp1y = points[i].y + tangents[i] * segLen;\n const cp2x = points[i + 1].x - segLen;\n const cp2y = points[i + 1].y - tangents[i + 1] * segLen;\n d += `C${cp1x},${cp1y},${cp2x},${cp2y},${points[i + 1].x},${points[i + 1].y}`;\n }\n\n return d;\n}\n\n/**\n * Natural cubic spline interpolation.\n * Smoother than monotone but may overshoot.\n */\nfunction buildNaturalPath(points: { x: number; y: number }[]): string {\n const n = points.length;\n if (n < 2) return buildLinePath(points);\n if (n === 2) return buildLinePath(points);\n\n // Solve tridiagonal system for natural cubic spline\n // For each coordinate axis independently\n function solveSpline(vals: number[]): { cp1: number; cp2: number }[] {\n const len = vals.length - 1;\n\n // Thomas algorithm for tridiagonal system\n const a: number[] = new Array(len);\n const b: number[] = new Array(len);\n const r: number[] = new Array(len);\n\n // Natural spline boundary: second derivative = 0 at endpoints\n a[0] = 0;\n b[0] = 2;\n r[0] = vals[0] + 2 * vals[1];\n\n for (let i = 1; i < len - 1; i++) {\n a[i] = 1;\n b[i] = 4;\n r[i] = 4 * vals[i] + 2 * vals[i + 1];\n }\n\n a[len - 1] = 2;\n b[len - 1] = 7;\n r[len - 1] = 8 * vals[len - 1] + vals[len];\n\n // Forward sweep\n for (let i = 1; i < len; i++) {\n const m = a[i] / b[i - 1];\n b[i] -= m;\n r[i] -= m * r[i - 1];\n }\n\n // Back substitution — first control points\n const cp1: number[] = new Array(len);\n cp1[len - 1] = r[len - 1] / b[len - 1];\n for (let i = len - 2; i >= 0; i--) {\n cp1[i] = (r[i] - cp1[i + 1]) / b[i];\n }\n\n // Second control points\n const cp2: number[] = new Array(len);\n for (let i = 0; i < len - 1; i++) {\n cp2[i] = 2 * vals[i + 1] - cp1[i + 1];\n }\n cp2[len - 1] = (vals[len] + cp1[len - 1]) / 2;\n\n const result: { cp1: number; cp2: number }[] = [];\n for (let i = 0; i < len; i++) {\n result.push({ cp1: cp1[i], cp2: cp2[i] });\n }\n return result;\n }\n\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n\n const xCPs = solveSpline(xs);\n const yCPs = solveSpline(ys);\n\n let d = `M${points[0].x},${points[0].y}`;\n for (let i = 0; i < n - 1; i++) {\n d += `C${xCPs[i].cp1},${yCPs[i].cp1},${xCPs[i].cp2},${yCPs[i].cp2},${points[i + 1].x},${points[i + 1].y}`;\n }\n\n return d;\n}\n\nexport function buildSmoothPath(\n points: { x: number; y: number }[],\n curveType: CurveType\n): string {\n switch (curveType) {\n case \"monotone\":\n return buildMonotonePath(points);\n case \"natural\":\n return buildNaturalPath(points);\n case \"linear\":\n default:\n return buildLinePath(points);\n }\n}\n","import React, { useRef, useEffect, useState } from \"react\";\nimport type { DataSeries, DataPoint, AnimationConfig, CurveType } from \"../types/chart.types\";\nimport { buildLinePath, buildSmoothPath } from \"../utils/path\";\n\ninterface LinePathProps {\n series: DataSeries;\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n color: string;\n animation?: AnimationConfig;\n onPointClick?: (point: DataPoint, series: DataSeries) => void;\n curveType?: CurveType;\n highlightOpacity?: number;\n}\n\nexport const LinePath: React.FC<LinePathProps> = React.memo(\n ({ series, xScale, yScale, color, animation, onPointClick, curveType, highlightOpacity }) => {\n const pathRef = useRef<SVGPathElement>(null);\n const [pathLength, setPathLength] = useState(0);\n const [animationDone, setAnimationDone] = useState(!animation?.enabled);\n\n const strokeWidth = series.strokeWidth ?? 2;\n const showDots = series.showDots ?? false;\n const dotRadius = series.dotRadius ?? 3.5;\n const effectiveCurve = series.curveType ?? curveType ?? \"linear\";\n const opacity = highlightOpacity ?? 1;\n\n const points = series.data.map((pt) => ({\n x: xScale(pt.x),\n y: yScale(pt.y),\n }));\n\n const d =\n effectiveCurve === \"linear\"\n ? buildLinePath(points)\n : buildSmoothPath(points, effectiveCurve);\n\n useEffect(() => {\n if (animation?.enabled && pathRef.current) {\n const length = pathRef.current.getTotalLength();\n setPathLength(length);\n setAnimationDone(false);\n\n const timer = setTimeout(() => {\n setAnimationDone(true);\n }, animation.duration ?? 800);\n\n return () => clearTimeout(timer);\n }\n }, [d, animation?.enabled, animation?.duration]);\n\n const animStyle: React.CSSProperties =\n animation?.enabled && pathLength > 0\n ? {\n strokeDasharray: pathLength,\n strokeDashoffset: animationDone ? 0 : pathLength,\n transition: `stroke-dashoffset ${animation.duration ?? 800}ms ${animation.easing ?? \"ease-in-out\"}`,\n }\n : {};\n\n return (\n <g\n className=\"chartifypdf-line-path\"\n style={{\n opacity,\n transition: \"opacity 150ms ease\",\n }}\n >\n <path\n ref={pathRef}\n d={d}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={series.strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={animStyle}\n />\n {showDots &&\n series.data.map((pt, i) => (\n <circle\n key={`dot-${series.id}-${i}`}\n cx={xScale(pt.x)}\n cy={yScale(pt.y)}\n r={dotRadius}\n fill={color}\n stroke=\"#fff\"\n strokeWidth={1.5}\n style={{ cursor: onPointClick ? \"pointer\" : \"default\" }}\n onClick={\n onPointClick\n ? () => onPointClick(pt, series)\n : undefined\n }\n />\n ))}\n </g>\n );\n }\n);\n\nLinePath.displayName = \"LinePath\";\n","import React from \"react\";\nimport type { DataPoint, DataSeries, TooltipConfig } from \"../types/chart.types\";\n\ninterface TooltipProps {\n visible: boolean;\n x: number;\n y: number;\n point: DataPoint | null;\n series: DataSeries | null;\n plotHeight: number;\n plotWidth: number;\n config?: TooltipConfig;\n seriesColor: string;\n}\n\nexport const Tooltip: React.FC<TooltipProps> = React.memo(\n ({ visible, x, y, point, series, plotHeight, plotWidth, config, seriesColor }) => {\n if (!visible || !point || !series) return null;\n\n const bgColor = config?.backgroundColor ?? \"rgba(0, 0, 0, 0.8)\";\n const textColor = config?.textColor ?? \"#fff\";\n\n // Determine tooltip text\n let content: React.ReactNode;\n if (config?.renderCustom) {\n content = config.renderCustom(point, series);\n } else {\n const text = config?.formatter\n ? config.formatter(point, series)\n : `${series.name}: (${point.x}, ${point.y})`;\n content = text;\n }\n\n // Smart positioning: flip when near edges\n const tooltipWidth = 140;\n const tooltipHeight = 40;\n let tx = x + 12;\n let ty = y - tooltipHeight - 8;\n\n if (tx + tooltipWidth > plotWidth) {\n tx = x - tooltipWidth - 12;\n }\n if (ty < 0) {\n ty = y + 12;\n }\n\n return (\n <g className=\"chartifypdf-tooltip\" pointerEvents=\"none\">\n {/* Vertical indicator line */}\n <line\n x1={x}\n y1={0}\n x2={x}\n y2={plotHeight}\n stroke=\"#999\"\n strokeWidth={1}\n strokeDasharray=\"4 4\"\n opacity={0.6}\n />\n {/* Highlight circle */}\n <circle\n cx={x}\n cy={y}\n r={5}\n fill={seriesColor}\n stroke=\"#fff\"\n strokeWidth={2}\n />\n {/* Tooltip box using foreignObject */}\n <foreignObject\n x={tx}\n y={ty}\n width={tooltipWidth}\n height={tooltipHeight + 20}\n overflow=\"visible\"\n >\n <div\n style={{\n backgroundColor: bgColor,\n color: textColor,\n padding: \"6px 10px\",\n borderRadius: \"4px\",\n fontSize: \"12px\",\n fontFamily: \"sans-serif\",\n whiteSpace: \"nowrap\",\n lineHeight: 1.4,\n boxShadow: \"0 2px 6px rgba(0,0,0,0.2)\",\n }}\n >\n {content}\n </div>\n </foreignObject>\n </g>\n );\n }\n);\n\nTooltip.displayName = \"Tooltip\";\n","import React from \"react\";\nimport type { ZoomConfig } from \"../types/chart.types\";\n\ninterface ZoomControlsProps {\n config?: ZoomConfig;\n svgWidth: number;\n svgHeight: number;\n margin: { top: number; right: number; bottom: number; left: number };\n scale: number;\n onZoomIn: () => void;\n onZoomOut: () => void;\n onReset: () => void;\n}\n\nexport const ZoomControls: React.FC<ZoomControlsProps> = React.memo(\n ({ config, svgWidth, svgHeight, margin, scale, onZoomIn, onZoomOut, onReset }) => {\n if (!config?.enabled || config.showControls === false) return null;\n\n const position = config.controlsPosition ?? \"top-right\";\n const btnSize = 24;\n const gap = 4;\n const groupWidth = btnSize * 3 + gap * 2;\n const groupHeight = btnSize;\n const padding = 8;\n\n let gx: number;\n let gy: number;\n\n switch (position) {\n case \"top-left\":\n gx = margin.left + padding;\n gy = margin.top + padding;\n break;\n case \"bottom-left\":\n gx = margin.left + padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"bottom-right\":\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = svgHeight - margin.bottom - groupHeight - padding;\n break;\n case \"top-right\":\n default:\n gx = svgWidth - margin.right - groupWidth - padding;\n gy = margin.top + padding;\n break;\n }\n\n const buttons = [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2013\", onClick: onReset }, // en-dash for reset\n { label: \"\\u2212\", onClick: onZoomOut }, // minus sign\n ];\n\n // Replace middle button with reset when zoomed, or show as \"-\"\n const displayButtons =\n scale > 1\n ? [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n { label: \"\\u21BA\", onClick: onReset },\n ]\n : [\n { label: \"+\", onClick: onZoomIn },\n { label: \"\\u2212\", onClick: onZoomOut },\n ];\n\n return (\n <g\n className=\"chartifypdf-zoom-controls\"\n transform={`translate(${gx}, ${gy})`}\n >\n {displayButtons.map((btn, i) => (\n <g\n key={btn.label}\n transform={`translate(${i * (btnSize + gap)}, 0)`}\n onClick={btn.onClick}\n style={{ cursor: \"pointer\" }}\n >\n <rect\n width={btnSize}\n height={btnSize}\n rx={4}\n fill=\"rgba(255,255,255,0.9)\"\n stroke=\"#ccc\"\n strokeWidth={1}\n />\n <text\n x={btnSize / 2}\n y={btnSize / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={14}\n fontFamily=\"sans-serif\"\n fill=\"#333\"\n fontWeight=\"bold\"\n >\n {btn.label}\n </text>\n </g>\n ))}\n </g>\n );\n }\n);\n\nZoomControls.displayName = \"ZoomControls\";\n","import React from \"react\";\nimport type { PeakConfig, DataSeries } from \"../types/chart.types\";\n\ninterface PeakMarkersProps {\n peaks: PeakConfig[];\n data: DataSeries[];\n xScale: (v: number) => number;\n yScale: (v: number) => number;\n colorPalette?: string[];\n}\n\nfunction findYAtX(data: DataSeries[], targetX: number): { y: number; seriesIndex: number } | null {\n let maxY = -Infinity;\n let bestSeriesIdx = -1;\n\n for (let si = 0; si < data.length; si++) {\n const series = data[si];\n // Find closest data point to target X\n let closest = series.data[0];\n let closestDist = Infinity;\n for (const pt of series.data) {\n const dist = Math.abs(pt.x - targetX);\n if (dist < closestDist) {\n closestDist = dist;\n closest = pt;\n }\n }\n if (closest && closest.y > maxY) {\n maxY = closest.y;\n bestSeriesIdx = si;\n }\n }\n\n if (bestSeriesIdx === -1) return null;\n return { y: maxY, seriesIndex: bestSeriesIdx };\n}\n\nfunction ArrowIcon({ x, y, color }: { x: number; y: number; color: string }) {\n return (\n <polygon\n points={`${x},${y} ${x - 5},${y - 10} ${x + 5},${y - 10}`}\n fill={color}\n />\n );\n}\n\nfunction DiamondIcon({ x, y, color }: { x: number; y: number; color: string }) {\n return (\n <polygon\n points={`${x},${y - 6} ${x + 5},${y} ${x},${y + 6} ${x - 5},${y}`}\n fill={color}\n />\n );\n}\n\nfunction StarIcon({ x, y, color }: { x: number; y: number; color: string }) {\n // 5-point star\n const r1 = 6;\n const r2 = 3;\n const pts: string[] = [];\n for (let i = 0; i < 5; i++) {\n const outerAngle = (i * 72 - 90) * (Math.PI / 180);\n const innerAngle = ((i * 72 + 36) - 90) * (Math.PI / 180);\n pts.push(`${x + r1 * Math.cos(outerAngle)},${y + r1 * Math.sin(outerAngle)}`);\n pts.push(`${x + r2 * Math.cos(innerAngle)},${y + r2 * Math.sin(innerAngle)}`);\n }\n return <polygon points={pts.join(\" \")} fill={color} />;\n}\n\nexport const PeakMarkers: React.FC<PeakMarkersProps> = React.memo(\n ({ peaks, data, xScale, yScale }) => {\n return (\n <g className=\"chartifypdf-peak-markers\">\n {peaks.map((peak, i) => {\n const result = findYAtX(data, peak.x);\n if (!result) return null;\n\n const px = xScale(peak.x);\n const py = yScale(result.y);\n const color = peak.color ?? \"#ef4444\";\n const icon = peak.icon ?? \"arrow\";\n const label = peak.label;\n\n // Position icon above the data point\n const iconY = py - 14;\n\n return (\n <g key={`peak-${i}`}>\n {/* Vertical dashed line from data point up */}\n <line\n x1={px}\n y1={py}\n x2={px}\n y2={iconY + 6}\n stroke={color}\n strokeWidth={1}\n strokeDasharray=\"3 2\"\n opacity={0.6}\n />\n {/* Icon */}\n {icon === \"arrow\" && <ArrowIcon x={px} y={iconY} color={color} />}\n {icon === \"diamond\" && <DiamondIcon x={px} y={iconY} color={color} />}\n {icon === \"star\" && <StarIcon x={px} y={iconY} color={color} />}\n {/* Label */}\n {label && (\n <text\n x={px}\n y={iconY - 8}\n textAnchor=\"middle\"\n fill={color}\n fontSize={11}\n fontWeight={600}\n >\n {label}\n </text>\n )}\n </g>\n );\n })}\n </g>\n );\n }\n);\n\nPeakMarkers.displayName = \"PeakMarkers\";\n","import React, { useEffect, useRef } from \"react\";\nimport type { DataSeries, ContextMenuConfig } from \"../types/chart.types\";\n\ninterface ContextMenuProps {\n visible: boolean;\n x: number;\n y: number;\n nearestX: number;\n data: DataSeries[];\n config?: ContextMenuConfig;\n colorPalette?: string[];\n getSeriesColor: (index: number) => string;\n onClose: () => void;\n}\n\nfunction findNearestY(series: DataSeries, targetX: number): number | null {\n if (series.data.length === 0) return null;\n let closest = series.data[0];\n let closestDist = Math.abs(closest.x - targetX);\n for (const pt of series.data) {\n const dist = Math.abs(pt.x - targetX);\n if (dist < closestDist) {\n closestDist = dist;\n closest = pt;\n }\n }\n return closest.y;\n}\n\nexport const ContextMenu: React.FC<ContextMenuProps> = ({\n visible,\n x,\n y,\n nearestX,\n data,\n config,\n getSeriesColor,\n onClose,\n}) => {\n const menuRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!visible) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n\n // Delay adding listeners to avoid immediate close from the contextmenu event\n const timer = setTimeout(() => {\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleKeyDown);\n }, 0);\n\n return () => {\n clearTimeout(timer);\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [visible, onClose]);\n\n if (!visible) return null;\n\n const bgColor = config?.backgroundColor ?? \"#ffffff\";\n const textColor = config?.textColor ?? \"#1f2937\";\n const borderColor = config?.borderColor ?? \"#e5e7eb\";\n\n const rows = data.map((series, i) => {\n const yVal = findNearestY(series, nearestX);\n return {\n name: series.name,\n color: series.color ?? getSeriesColor(i),\n x: nearestX,\n y: yVal,\n };\n });\n\n return (\n <div\n ref={menuRef}\n style={{\n position: \"fixed\",\n left: x,\n top: y,\n zIndex: 9999,\n backgroundColor: bgColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: 6,\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n padding: \"8px 0\",\n minWidth: 200,\n fontSize: 12,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n }}\n >\n <div\n style={{\n padding: \"4px 12px 8px\",\n fontWeight: 600,\n fontSize: 11,\n color: textColor,\n opacity: 0.6,\n borderBottom: `1px solid ${borderColor}`,\n marginBottom: 4,\n }}\n >\n Data at X = {typeof nearestX === \"number\" ? nearestX.toLocaleString() : nearestX}\n </div>\n <table style={{ width: \"100%\", borderCollapse: \"collapse\" }}>\n <thead>\n <tr>\n <th style={{ textAlign: \"left\", padding: \"4px 12px\", fontWeight: 600, opacity: 0.7 }}>\n Series\n </th>\n <th style={{ textAlign: \"right\", padding: \"4px 12px\", fontWeight: 600, opacity: 0.7 }}>\n Y\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr\n key={i}\n style={{\n borderTop: i > 0 ? `1px solid ${borderColor}` : undefined,\n }}\n >\n <td style={{ padding: \"4px 12px\", display: \"flex\", alignItems: \"center\", gap: 6 }}>\n <span\n style={{\n display: \"inline-block\",\n width: 8,\n height: 8,\n borderRadius: \"50%\",\n backgroundColor: row.color,\n flexShrink: 0,\n }}\n />\n {row.name}\n </td>\n <td style={{ padding: \"4px 12px\", textAlign: \"right\", fontVariantNumeric: \"tabular-nums\" }}>\n {row.y !== null ? row.y.toLocaleString() : \"—\"}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\nContextMenu.displayName = \"ContextMenu\";\n","import React, { useRef, useId, useState, useCallback } from \"react\";\nimport type {\n LineChartProps,\n ChartMargin,\n DataPoint,\n} from \"../types/chart.types\";\nimport { getSeriesColor } from \"../utils/color\";\nimport { useChartDimensions } from \"../hooks/useChartDimensions\";\nimport { useDataDomain } from \"../hooks/useDataDomain\";\nimport { useScales } from \"../hooks/useScales\";\nimport { useZoom } from \"../hooks/useZoom\";\nimport { useTooltip } from \"../hooks/useTooltip\";\nimport { Axes } from \"./Axes\";\nimport { GridLines } from \"./GridLines\";\nimport { LinePath } from \"./LinePath\";\nimport { Tooltip } from \"./Tooltip\";\nimport { ZoomControls } from \"./ZoomControls\";\nimport { PeakMarkers } from \"./PeakMarkers\";\nimport { ContextMenu } from \"./ContextMenu\";\n\nconst DEFAULT_MARGIN: ChartMargin = {\n top: 20,\n right: 20,\n bottom: 50,\n left: 60,\n};\n\nconst DEFAULT_HEIGHT = 400;\n\nexport const LineChart: React.FC<LineChartProps> = ({\n data,\n width: providedWidth,\n height: providedHeight,\n margin: marginOverride,\n xAxis: xAxisConfig,\n yAxis: yAxisConfig,\n tooltip: tooltipConfig,\n zoom: zoomConfig,\n style: chartStyle,\n animation,\n colorPalette,\n className,\n onPointClick,\n ariaLabel,\n curveType: globalCurveType,\n peaks,\n contextMenu: contextMenuConfig,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const clipId = useId();\n\n const margin: ChartMargin = {\n ...DEFAULT_MARGIN,\n ...marginOverride,\n };\n\n const dimensions = useChartDimensions(\n containerRef,\n providedWidth,\n providedHeight ?? DEFAULT_HEIGHT\n );\n const { width: svgWidth, height: svgHeight } = dimensions;\n\n const plotWidth = Math.max(0, svgWidth - margin.left - margin.right);\n const plotHeight = Math.max(0, svgHeight - margin.top - margin.bottom);\n\n // 1. Extract full domain from data\n const { fullXDomain, fullYDomain } = useDataDomain(data, xAxisConfig, yAxisConfig);\n\n // 2. Domain-based zoom\n const zoomState = useZoom(zoomConfig, fullXDomain, fullYDomain, plotWidth, plotHeight);\n\n // 3. Scales from view domain (semantic zoom — ticks update automatically)\n const zoomEnabled = zoomConfig?.enabled ?? false;\n const { xScale, yScale, xTicks, yTicks } = useScales(\n data,\n plotWidth,\n plotHeight,\n xAxisConfig,\n yAxisConfig,\n zoomEnabled ? zoomState.viewXDomain : undefined,\n zoomEnabled ? zoomState.viewYDomain : undefined\n );\n\n // Tooltip\n const tooltipEnabled = tooltipConfig?.enabled !== false;\n const tooltipState = useTooltip(\n svgRef,\n data,\n xScale,\n yScale,\n margin.left,\n margin.top,\n plotWidth,\n tooltipConfig\n );\n\n // Hover highlight: compute opacity per series\n const activeSeriesId = tooltipState.activeSeriesId;\n const hasHover = tooltipState.tooltipVisible && activeSeriesId !== null;\n\n const getHighlightOpacity = (seriesId: string): number => {\n if (!hasHover) return 1;\n return seriesId === activeSeriesId ? 1 : 0.2;\n };\n\n // Context menu state\n const [ctxMenu, setCtxMenu] = useState<{\n visible: boolean;\n clientX: number;\n clientY: number;\n nearestX: number;\n }>({ visible: false, clientX: 0, clientY: 0, nearestX: 0 });\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (!contextMenuConfig?.enabled) return;\n e.preventDefault();\n\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - margin.left;\n\n if (mouseX < 0 || mouseX > plotWidth) return;\n\n // Find nearest X value across all series\n let nearestX = 0;\n let nearestDist = Infinity;\n for (const series of data) {\n for (const pt of series.data) {\n const px = xScale(pt.x);\n const dist = Math.abs(px - mouseX);\n if (dist < nearestDist) {\n nearestDist = dist;\n nearestX = pt.x;\n }\n }\n }\n\n setCtxMenu({\n visible: true,\n clientX: e.clientX,\n clientY: e.clientY,\n nearestX,\n });\n },\n [contextMenuConfig?.enabled, data, xScale, margin.left, plotWidth]\n );\n\n const closeContextMenu = useCallback(() => {\n setCtxMenu((prev) => ({ ...prev, visible: false }));\n }, []);\n\n const showXGrid = xAxisConfig?.gridLines ?? false;\n const showYGrid = yAxisConfig?.gridLines ?? true;\n\n const getColor = (index: number) => getSeriesColor(index, colorPalette);\n\n // Don't render until we have dimensions\n if (svgWidth === 0 || svgHeight === 0) {\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n }}\n />\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n width: providedWidth ?? \"100%\",\n height: providedHeight ?? DEFAULT_HEIGHT,\n backgroundColor: chartStyle?.backgroundColor,\n position: \"relative\",\n }}\n >\n <svg\n ref={svgRef}\n width={svgWidth}\n height={svgHeight}\n role=\"img\"\n aria-label={ariaLabel ?? \"Line chart\"}\n onMouseMove={\n tooltipEnabled && !zoomState.isPanning\n ? tooltipState.handleMouseMove\n : undefined\n }\n onMouseLeave={tooltipEnabled ? tooltipState.handleMouseLeave : undefined}\n onWheel={zoomEnabled ? zoomState.handleWheel : undefined}\n onMouseDown={zoomEnabled ? zoomState.handlePanStart : undefined}\n onMouseUp={zoomEnabled ? zoomState.handlePanEnd : undefined}\n onContextMenu={handleContextMenu}\n style={{\n userSelect: zoomState.isPanning ? \"none\" : undefined,\n cursor: zoomState.isPanning\n ? \"grabbing\"\n : zoomEnabled && zoomState.scale > 1\n ? \"grab\"\n : undefined,\n }}\n >\n <defs>\n <clipPath id={clipId}>\n <rect width={plotWidth} height={plotHeight} />\n </clipPath>\n </defs>\n\n <g transform={`translate(${margin.left}, ${margin.top})`}>\n {/* Axes — always crisp, ticks update with zoom */}\n <Axes\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n xAxisConfig={xAxisConfig}\n yAxisConfig={yAxisConfig}\n style={chartStyle}\n />\n\n {/* Grid lines */}\n <GridLines\n xTicks={xTicks}\n yTicks={yTicks}\n xScale={xScale}\n yScale={yScale}\n plotWidth={plotWidth}\n plotHeight={plotHeight}\n showXGrid={showXGrid}\n showYGrid={showYGrid}\n xGridColor={xAxisConfig?.gridLineColor}\n yGridColor={yAxisConfig?.gridLineColor}\n />\n\n {/* Clipped data area — no CSS transform, scales handle zoom */}\n <g clipPath={`url(#${clipId})`}>\n {zoomEnabled ? (\n <g\n onMouseMove={zoomState.handlePanMove}\n >\n {data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={series.color ?? getColor(i)}\n animation={animation}\n onPointClick={onPointClick}\n curveType={globalCurveType}\n highlightOpacity={getHighlightOpacity(series.id)}\n />\n ))}\n </g>\n ) : (\n data.map((series, i) => (\n <LinePath\n key={series.id}\n series={series}\n xScale={xScale}\n yScale={yScale}\n color={series.color ?? getColor(i)}\n animation={animation}\n onPointClick={onPointClick}\n curveType={globalCurveType}\n highlightOpacity={getHighlightOpacity(series.id)}\n />\n ))\n )}\n\n {/* Peak markers */}\n {peaks && peaks.length > 0 && (\n <PeakMarkers\n peaks={peaks}\n data={data}\n xScale={xScale}\n yScale={yScale}\n colorPalette={colorPalette}\n />\n )}\n </g>\n\n {/* Tooltip renders above everything in plot area */}\n {tooltipEnabled && (\n <Tooltip\n visible={tooltipState.tooltipVisible}\n x={tooltipState.tooltipX}\n y={tooltipState.tooltipY}\n point={tooltipState.activePoint}\n series={tooltipState.activeSeries}\n plotHeight={plotHeight}\n plotWidth={plotWidth}\n config={tooltipConfig}\n seriesColor={\n tooltipState.activeSeries?.color ??\n getColor(\n data.findIndex(\n (s) => s.id === tooltipState.activeSeries?.id\n )\n )\n }\n />\n )}\n </g>\n\n {/* Zoom controls render at SVG root level */}\n <ZoomControls\n config={zoomConfig}\n svgWidth={svgWidth}\n svgHeight={svgHeight}\n margin={margin}\n scale={zoomState.scale}\n onZoomIn={zoomState.zoomIn}\n onZoomOut={zoomState.zoomOut}\n onReset={zoomState.resetZoom}\n />\n </svg>\n\n {/* Zoom hint overlay */}\n {zoomState.showZoomHint && (\n <div\n style={{\n position: \"absolute\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n background: \"rgba(0, 0, 0, 0.7)\",\n color: \"#fff\",\n padding: \"8px 16px\",\n borderRadius: 6,\n fontSize: 13,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n pointerEvents: \"none\",\n whiteSpace: \"nowrap\",\n zIndex: 10,\n }}\n >\n Hold Ctrl + scroll to zoom\n </div>\n )}\n\n {/* Right-click context menu */}\n {contextMenuConfig?.enabled && (\n <ContextMenu\n visible={ctxMenu.visible}\n x={ctxMenu.clientX}\n y={ctxMenu.clientY}\n nearestX={ctxMenu.nearestX}\n data={data}\n config={contextMenuConfig}\n getSeriesColor={getColor}\n onClose={closeContextMenu}\n />\n )}\n </div>\n );\n};\n\nLineChart.displayName = \"LineChart\";\n"],"names":["DEFAULT_PALETTE","useChartDimensions","containerRef","providedWidth","providedHeight","dimensions","setDimensions","useState","width","height","isFixed","undefined","useLayoutEffect","node","current","measure","rect","getBoundingClientRect","observer","ResizeObserver","observe","disconnect","clamp","value","min","max","Math","lerp","a","b","t","niceNumber","range","round","exponent","floor","log10","fraction","pow","niceFraction","niceRange","tickCount","offset","abs","tickStep","niceMin","niceMax","ceil","linearScale","domainMin","domainMax","rangeMin","rangeMax","inverseLerp","generateTicks","ticks","v","push","parseFloat","toPrecision","useDataDomain","data","xAxisConfig","yAxisConfig","useMemo","xMin","Infinity","xMax","yMin","yMax","series","point","x","y","isFinite","xTickCount","yTickCount","xNice","yNice","fullXDomain","fullYDomain","useScales","plotWidth","plotHeight","viewXDomain","viewYDomain","xDomainMin","xDomainMax","yDomainMin","yDomainMax","xViewNice","yViewNice","xScale","yScale","xTicks","filter","yTicks","xDomain","yDomain","useZoom","config","enabled","maxScale","step","enableWheel","enablePan","requireCtrlKey","setViewXDomain","setViewYDomain","isPanning","setIsPanning","showZoomHint","setShowZoomHint","panStart","useRef","hintTimer","lastFullDomain","fullXRange","viewXRange","scale","clampDomain","useCallback","domain","fullDomain","showHint","clearTimeout","setTimeout","zoomByFactor","factor","centerXFrac","centerYFrac","cxFrac","cyFrac","prev","newRange","minRange","clampedRange","finalRange","newMin","fullYRange","zoomIn","zoomOut","resetZoom","handleWheel","e","ctrlKey","metaKey","preventDefault","currentTarget","mouseX","clientX","left","mouseY","clientY","top","delta","deltaY","handlePanStart","button","handlePanMove","dx","dy","prevXDomain","prevYDomain","xRange","yRange","domainDx","domainDy","newXDomain","newYDomain","handlePanEnd","findNearestByPixelX","sorted","length","low","high","mid","nearest","minDist","dist","useTooltip","svgRef","marginLeft","marginTop","_config","tooltipVisible","setTooltipVisible","tooltipX","setTooltipX","tooltipY","setTooltipY","activePoint","setActivePoint","activeSeries","setActiveSeries","activeSeriesId","setActiveSeriesId","handleMouseMove","svg","closestPoint","closestSeries","closestDist","id","handleMouseLeave","defaultTickFormat","toFixed","Number","isInteger","toString","Axes","React","memo","style","axisColor","tickColor","fontFamily","fontSize","xTickFormat","tickFormat","yTickFormat","_jsxs","className","_jsx","x1","y1","x2","y2","stroke","strokeWidth","map","tick","children","textAnchor","fill","label","fontWeight","dominantBaseline","transform","displayName","GridLines","showXGrid","showYGrid","xGridColor","yGridColor","strokeDasharray","strokeOpacity","buildLinePath","points","first","rest","p","join","buildSmoothPath","curveType","n","m","i","tangents","Array","alpha","beta","s","tau","sqrt","d","segLen","buildMonotonePath","solveSpline","vals","len","r","cp1","cp2","result","xs","ys","xCPs","yCPs","buildNaturalPath","LinePath","color","animation","onPointClick","highlightOpacity","pathRef","pathLength","setPathLength","animationDone","setAnimationDone","showDots","dotRadius","effectiveCurve","opacity","pt","useEffect","getTotalLength","timer","duration","animStyle","strokeDashoffset","transition","easing","ref","strokeLinecap","strokeLinejoin","cx","cy","cursor","onClick","Tooltip","visible","seriesColor","bgColor","backgroundColor","textColor","content","renderCustom","formatter","name","tx","ty","pointerEvents","tooltipHeight","overflow","padding","borderRadius","whiteSpace","lineHeight","boxShadow","ZoomControls","svgWidth","svgHeight","margin","onZoomIn","onZoomOut","onReset","showControls","position","controlsPosition","btnSize","gx","gy","bottom","right","btn","rx","ArrowIcon","DiamondIcon","StarIcon","pts","outerAngle","PI","innerAngle","cos","sin","PeakMarkers","peaks","peak","targetX","maxY","bestSeriesIdx","si","closest","seriesIndex","findYAtX","px","py","icon","iconY","ContextMenu","nearestX","getSeriesColor","onClose","menuRef","handleClickOutside","contains","target","handleKeyDown","key","document","addEventListener","removeEventListener","borderColor","rows","yVal","findNearestY","zIndex","border","minWidth","borderBottom","marginBottom","toLocaleString","borderCollapse","textAlign","row","borderTop","display","alignItems","gap","flexShrink","fontVariantNumeric","DEFAULT_MARGIN","LineChart","marginOverride","xAxis","yAxis","tooltip","tooltipConfig","zoom","zoomConfig","chartStyle","colorPalette","ariaLabel","globalCurveType","contextMenu","contextMenuConfig","clipId","useId","zoomState","zoomEnabled","tooltipEnabled","tooltipState","hasHover","getHighlightOpacity","seriesId","ctxMenu","setCtxMenu","handleContextMenu","nearestDist","closeContextMenu","gridLines","getColor","index","palette","colors","role","onMouseMove","onMouseLeave","onWheel","onMouseDown","onMouseUp","onContextMenu","userSelect","gridLineColor","clipPath","findIndex","background"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/index.ts","../src/components/axes.tsx","../src/components/gridlines.tsx","../src/components/linechart.tsx","../src/components/linepath.tsx","../src/components/tooltip.tsx","../src/components/zoomcontrols.tsx","../src/hooks/usechartdimensions.ts","../src/hooks/usescales.ts","../src/hooks/usetooltip.ts","../src/hooks/usezoom.ts","../src/types/chart.types.ts","../src/utils/color.ts","../src/utils/math.ts","../src/utils/path.ts"],"version":"5.9.2"}
|
|
@@ -4,6 +4,7 @@ export interface DataPoint {
|
|
|
4
4
|
y: number;
|
|
5
5
|
label?: string;
|
|
6
6
|
}
|
|
7
|
+
export type CurveType = "linear" | "monotone" | "natural";
|
|
7
8
|
export interface DataSeries {
|
|
8
9
|
id: string;
|
|
9
10
|
name: string;
|
|
@@ -13,6 +14,7 @@ export interface DataSeries {
|
|
|
13
14
|
strokeDasharray?: string;
|
|
14
15
|
showDots?: boolean;
|
|
15
16
|
dotRadius?: number;
|
|
17
|
+
curveType?: CurveType;
|
|
16
18
|
}
|
|
17
19
|
export interface AxisConfig {
|
|
18
20
|
label?: string;
|
|
@@ -39,6 +41,7 @@ export interface ZoomConfig {
|
|
|
39
41
|
controlsPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
40
42
|
enableWheel?: boolean;
|
|
41
43
|
enablePan?: boolean;
|
|
44
|
+
requireCtrlKey?: boolean;
|
|
42
45
|
}
|
|
43
46
|
export interface ChartMargin {
|
|
44
47
|
top: number;
|
|
@@ -58,6 +61,18 @@ export interface AnimationConfig {
|
|
|
58
61
|
duration?: number;
|
|
59
62
|
easing?: string;
|
|
60
63
|
}
|
|
64
|
+
export interface PeakConfig {
|
|
65
|
+
x: number;
|
|
66
|
+
label?: string;
|
|
67
|
+
color?: string;
|
|
68
|
+
icon?: "arrow" | "diamond" | "star";
|
|
69
|
+
}
|
|
70
|
+
export interface ContextMenuConfig {
|
|
71
|
+
enabled?: boolean;
|
|
72
|
+
backgroundColor?: string;
|
|
73
|
+
textColor?: string;
|
|
74
|
+
borderColor?: string;
|
|
75
|
+
}
|
|
61
76
|
export interface LineChartProps {
|
|
62
77
|
data: DataSeries[];
|
|
63
78
|
width?: number;
|
|
@@ -73,4 +88,7 @@ export interface LineChartProps {
|
|
|
73
88
|
className?: string;
|
|
74
89
|
onPointClick?: (point: DataPoint, series: DataSeries) => void;
|
|
75
90
|
ariaLabel?: string;
|
|
91
|
+
curveType?: CurveType;
|
|
92
|
+
peaks?: PeakConfig[];
|
|
93
|
+
contextMenu?: ContextMenuConfig;
|
|
76
94
|
}
|
package/dist/utils/math.d.ts
CHANGED
|
@@ -9,3 +9,4 @@ export declare function niceRange(min: number, max: number, tickCount: number):
|
|
|
9
9
|
};
|
|
10
10
|
export declare function linearScale(domainMin: number, domainMax: number, rangeMin: number, rangeMax: number): (value: number) => number;
|
|
11
11
|
export declare function generateTicks(niceMin: number, niceMax: number, tickStep: number): number[];
|
|
12
|
+
export declare function inverseLinearScale(domainMin: number, domainMax: number, rangeMin: number, rangeMax: number): (pixel: number) => number;
|
package/dist/utils/path.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chartifypdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A zero-dependency React line chart component using pure SVG. Supports zoom, tooltip, responsive sizing, and customizable styling.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|