chartifypdf 0.2.0 → 0.4.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/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/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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"}
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/utils/exportPdf.ts","../src/components/ExportButton.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 // Generate ticks, respecting integerTicks option\n let xTickStep = xViewNice.tickStep;\n let xTickMin = xViewNice.niceMin;\n let xTickMax = xViewNice.niceMax;\n if (xAxisConfig?.integerTicks) {\n xTickStep = Math.max(1, Math.ceil(xTickStep));\n xTickMin = Math.ceil(xDomainMin);\n xTickMax = Math.floor(xDomainMax);\n }\n\n let yTickStep = yViewNice.tickStep;\n let yTickMin = yViewNice.niceMin;\n let yTickMax = yViewNice.niceMax;\n if (yAxisConfig?.integerTicks) {\n yTickStep = Math.max(1, Math.ceil(yTickStep));\n yTickMin = Math.ceil(yDomainMin);\n yTickMax = Math.floor(yDomainMax);\n }\n\n const xTicks = generateTicks(xTickMin, xTickMax, xTickStep)\n .filter((t) => t >= xDomainMin && t <= xDomainMax)\n .filter((t) => !xAxisConfig?.integerTicks || Number.isInteger(t));\n const yTicks = generateTicks(yTickMin, yTickMax, yTickStep)\n .filter((t) => t >= yDomainMin && t <= yDomainMax)\n .filter((t) => !yAxisConfig?.integerTicks || Number.isInteger(t));\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 zoomToPoint: (dataX: number, dataY: number) => 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 zoomToPoint = useCallback(\n (dataX: number, dataY: number) => {\n if (!enabled) return;\n // Zoom 2x centered at the given data point\n const factor = 2;\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 newMin = dataX - finalRange / 2;\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 newMin = dataY - finalRange / 2;\n const newMax = newMin + finalRange;\n return clampDomain([newMin, newMax], fullYDomain);\n });\n },\n [enabled, fullXDomain, fullYDomain, fullXRange, maxScale, clampDomain]\n );\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 zoomToPoint,\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 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 const mouseY = e.clientY - rect.top - marginTop;\n\n if (mouseX < 0 || mouseX > plotWidth) {\n setTooltipVisible(false);\n setActiveSeriesId(null);\n return;\n }\n\n // Step 1: Find nearest X across all series\n let nearestX = 0;\n let nearestXDist = Infinity;\n for (const series of data) {\n const nearest = findNearestByPixelX(series.data, mouseX, xScale);\n if (!nearest) continue;\n const dist = Math.abs(xScale(nearest.x) - mouseX);\n if (dist < nearestXDist) {\n nearestXDist = dist;\n nearestX = nearest.x;\n }\n }\n\n // Step 2: At that X, find which series has Y closest to mouse Y (pixel)\n let closestPoint: DataPoint | null = null;\n let closestSeries: DataSeries | null = null;\n let closestYDist = Infinity;\n\n for (const series of data) {\n // Find point at or nearest to nearestX in this series\n let bestPt: DataPoint | null = null;\n let bestXDist = Infinity;\n for (const pt of series.data) {\n const xDist = Math.abs(pt.x - nearestX);\n if (xDist < bestXDist) {\n bestXDist = xDist;\n bestPt = pt;\n }\n }\n if (!bestPt) continue;\n\n const pyY = yScale(bestPt.y);\n const yDist = Math.abs(pyY - mouseY);\n\n if (yDist < closestYDist) {\n closestYDist = yDist;\n closestPoint = bestPt;\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, marginTop, 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","export type ExportFormat = \"pdf\" | \"png\" | \"svg\";\n\nfunction cloneSvgWithStyles(svgElement: SVGSVGElement): SVGSVGElement {\n const clone = svgElement.cloneNode(true) as SVGSVGElement;\n\n // Ensure the clone has explicit dimensions\n const rect = svgElement.getBoundingClientRect();\n clone.setAttribute(\"width\", String(rect.width));\n clone.setAttribute(\"height\", String(rect.height));\n clone.setAttribute(\"xmlns\", \"http://www.w3.org/2000/svg\");\n clone.setAttribute(\"xmlns:xlink\", \"http://www.w3.org/1999/xlink\");\n\n // Inline computed styles for reliable rendering\n const allOriginal = svgElement.querySelectorAll(\"*\");\n const allClone = clone.querySelectorAll(\"*\");\n for (let i = 0; i < allOriginal.length; i++) {\n const orig = allOriginal[i] as Element;\n const cloned = allClone[i] as SVGElement | HTMLElement;\n if (!cloned.style) continue;\n\n const computed = window.getComputedStyle(orig);\n const importantProps = [\n \"fill\", \"stroke\", \"stroke-width\", \"stroke-dasharray\",\n \"opacity\", \"font-family\", \"font-size\", \"font-weight\",\n \"text-anchor\", \"dominant-baseline\", \"visibility\", \"display\",\n ];\n for (const prop of importantProps) {\n const val = computed.getPropertyValue(prop);\n if (val) {\n cloned.style.setProperty(prop, val);\n }\n }\n }\n\n return clone;\n}\n\nfunction svgToDataUrl(svgElement: SVGSVGElement): string {\n const clone = cloneSvgWithStyles(svgElement);\n const serializer = new XMLSerializer();\n const svgString = serializer.serializeToString(clone);\n const blob = new Blob([svgString], { type: \"image/svg+xml;charset=utf-8\" });\n return URL.createObjectURL(blob);\n}\n\nfunction downloadBlob(blob: Blob, fileName: string) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = fileName;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nasync function svgToCanvas(\n svgElement: SVGSVGElement,\n scale: number = 2\n): Promise<HTMLCanvasElement> {\n const rect = svgElement.getBoundingClientRect();\n const width = rect.width * scale;\n const height = rect.height * scale;\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Cannot get canvas 2d context\");\n\n ctx.scale(scale, scale);\n\n const url = svgToDataUrl(svgElement);\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // Draw white background\n ctx.fillStyle = \"#ffffff\";\n ctx.fillRect(0, 0, rect.width, rect.height);\n ctx.drawImage(img, 0, 0, rect.width, rect.height);\n URL.revokeObjectURL(url);\n resolve(canvas);\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error(\"Failed to load SVG as image\"));\n };\n img.src = url;\n });\n}\n\nexport async function exportChartAsSvg(\n svgElement: SVGSVGElement,\n fileName: string = \"chart\"\n): Promise<void> {\n const clone = cloneSvgWithStyles(svgElement);\n const serializer = new XMLSerializer();\n const svgString = serializer.serializeToString(clone);\n const blob = new Blob([svgString], { type: \"image/svg+xml;charset=utf-8\" });\n downloadBlob(blob, `${fileName}.svg`);\n}\n\nexport async function exportChartAsPng(\n svgElement: SVGSVGElement,\n fileName: string = \"chart\"\n): Promise<void> {\n const canvas = await svgToCanvas(svgElement);\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error(\"Canvas toBlob failed\"))),\n \"image/png\"\n );\n });\n downloadBlob(blob, `${fileName}.png`);\n}\n\nexport async function exportChartAsPdf(\n svgElement: SVGSVGElement,\n fileName: string = \"chart\"\n): Promise<void> {\n const canvas = await svgToCanvas(svgElement);\n const dataUrl = canvas.toDataURL(\"image/png\");\n\n const rect = svgElement.getBoundingClientRect();\n\n // Open a new window with the chart image and trigger print (Save as PDF)\n const printWindow = window.open(\"\", \"_blank\");\n if (!printWindow) {\n throw new Error(\"Popup blocked. Please allow popups to export PDF.\");\n }\n\n printWindow.document.write(`\n <!DOCTYPE html>\n <html>\n <head>\n <title>${fileName}</title>\n <style>\n @page { size: landscape; margin: 10mm; }\n body { margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; }\n img { max-width: 100%; height: auto; }\n </style>\n </head>\n <body>\n <img src=\"${dataUrl}\" width=\"${rect.width}\" height=\"${rect.height}\" />\n <script>\n window.onload = function() {\n setTimeout(function() { window.print(); window.close(); }, 300);\n };\n </script>\n </body>\n </html>\n `);\n printWindow.document.close();\n}\n\nexport async function exportChart(\n svgElement: SVGSVGElement,\n format: ExportFormat = \"pdf\",\n fileName: string = \"chart\"\n): Promise<void> {\n switch (format) {\n case \"svg\":\n return exportChartAsSvg(svgElement, fileName);\n case \"png\":\n return exportChartAsPng(svgElement, fileName);\n case \"pdf\":\n return exportChartAsPdf(svgElement, fileName);\n }\n}\n","import React, { useCallback } from \"react\";\nimport type { ExportConfig } from \"../types/chart.types\";\nimport { exportChart } from \"../utils/exportPdf\";\n\ninterface ExportButtonProps {\n config?: ExportConfig;\n svgRef: React.RefObject<SVGSVGElement | null>;\n svgWidth: number;\n svgHeight: number;\n margin: { top: number; right: number; bottom: number; left: number };\n}\n\nexport const ExportButton: React.FC<ExportButtonProps> = React.memo(\n ({ config, svgRef, svgWidth, svgHeight, margin }) => {\n if (!config?.enabled) return null;\n\n const position = config.buttonPosition ?? \"top-right\";\n const btnWidth = 28;\n const btnHeight = 24;\n const padding = 8;\n\n // Offset from zoom controls — place export button next to them\n // Use a fixed offset from the edge\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 + btnHeight + 8;\n break;\n case \"bottom-left\":\n gx = margin.left + padding;\n gy = svgHeight - margin.bottom - btnHeight - padding;\n break;\n case \"bottom-right\":\n gx = svgWidth - margin.right - btnWidth - padding;\n gy = svgHeight - margin.bottom - btnHeight - padding;\n break;\n case \"top-right\":\n default:\n gx = svgWidth - margin.right - btnWidth - padding;\n gy = margin.top + padding + btnHeight + 8;\n break;\n }\n\n const handleExport = useCallback(() => {\n const svg = svgRef.current;\n if (!svg) return;\n\n const format = config?.format ?? \"pdf\";\n const fileName = config?.fileName ?? \"chart\";\n exportChart(svg, format, fileName);\n }, [svgRef, config?.format, config?.fileName]);\n\n // Download icon (simple arrow-down into tray)\n const iconPath = \"M7,2 L7,10 L4,7 M7,10 L10,7 M3,13 L11,13\";\n\n return (\n <g\n className=\"chartifypdf-export-button\"\n transform={`translate(${gx}, ${gy})`}\n onClick={handleExport}\n style={{ cursor: \"pointer\" }}\n >\n <rect\n width={btnWidth}\n height={btnHeight}\n rx={4}\n fill=\"rgba(255,255,255,0.9)\"\n stroke=\"#ccc\"\n strokeWidth={1}\n />\n <g transform=\"translate(7, 4)\">\n <path\n d={iconPath}\n fill=\"none\"\n stroke=\"#333\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </g>\n </g>\n );\n }\n);\n\nExportButton.displayName = \"ExportButton\";\n","import React, { useRef, useId, useState, useCallback, useEffect } from \"react\";\nimport type {\n LineChartProps,\n ChartMargin,\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\";\nimport { ExportButton } from \"./ExportButton\";\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 export: exportConfig,\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 const zoomEnabled = zoomConfig?.enabled ?? false;\n const enableClickZoom = zoomConfig?.enableClickZoom ?? false;\n\n // 3. Scales from view domain (semantic zoom — ticks update automatically)\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 // FIX #1: Native wheel listener to prevent page scroll during Ctrl+zoom\n const requireCtrlKey = zoomConfig?.requireCtrlKey ?? true;\n const enableWheel = zoomConfig?.enableWheel ?? true;\n\n useEffect(() => {\n const svg = svgRef.current;\n if (!svg || !zoomEnabled || !enableWheel) return;\n\n const handleNativeWheel = (e: WheelEvent) => {\n // When Ctrl is held and zoom is enabled, prevent page scroll\n if (requireCtrlKey) {\n if (e.ctrlKey || e.metaKey) {\n e.preventDefault();\n }\n } else {\n // If requireCtrlKey is false, always prevent scroll on chart\n e.preventDefault();\n }\n };\n\n svg.addEventListener(\"wheel\", handleNativeWheel, { passive: false });\n return () => svg.removeEventListener(\"wheel\", handleNativeWheel);\n }, [zoomEnabled, enableWheel, requireCtrlKey]);\n\n // FIX #4: Click to zoom at point + double-click to reset\n const handleChartClick = useCallback(\n (e: React.MouseEvent) => {\n if (!zoomEnabled || !enableClickZoom) return;\n if (zoomState.isPanning) return;\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 const mouseY = e.clientY - rect.top - margin.top;\n\n if (mouseX < 0 || mouseX > plotWidth || mouseY < 0 || mouseY > plotHeight) return;\n\n // Find nearest data point\n let nearestX = 0;\n let nearestY = 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 py = yScale(pt.y);\n const dist = Math.hypot(px - mouseX, py - mouseY);\n if (dist < nearestDist) {\n nearestDist = dist;\n nearestX = pt.x;\n nearestY = pt.y;\n }\n }\n }\n\n zoomState.zoomToPoint(nearestX, nearestY);\n },\n [zoomEnabled, enableClickZoom, zoomState, data, xScale, yScale, margin.left, margin.top, plotWidth, plotHeight]\n );\n\n const handleDoubleClick = useCallback(() => {\n if (!zoomEnabled || !enableClickZoom) return;\n zoomState.resetZoom();\n }, [zoomEnabled, enableClickZoom, zoomState]);\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 onClick={handleChartClick}\n onDoubleClick={handleDoubleClick}\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 onMouseMove={zoomState.handlePanMove}>\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\n {/* Export button */}\n <ExportButton\n config={exportConfig}\n svgRef={svgRef}\n svgWidth={svgWidth}\n svgHeight={svgHeight}\n margin={margin}\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","xTickStep","xTickMin","xTickMax","integerTicks","yTickStep","yTickMin","yTickMax","xTicks","filter","Number","isInteger","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","zoomToPoint","dataX","dataY","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","nearestX","nearestXDist","closestPoint","closestSeries","closestYDist","bestPt","bestXDist","pt","xDist","pyY","yDist","id","handleMouseLeave","defaultTickFormat","toFixed","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","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","closestDist","seriesIndex","findYAtX","px","py","icon","iconY","ContextMenu","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","cloneSvgWithStyles","svgElement","clone","cloneNode","setAttribute","String","allOriginal","querySelectorAll","allClone","orig","cloned","computed","window","getComputedStyle","importantProps","prop","val","getPropertyValue","setProperty","downloadBlob","blob","fileName","url","URL","createObjectURL","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","async","svgToCanvas","canvas","ctx","getContext","Error","svgString","XMLSerializer","serializeToString","Blob","type","svgToDataUrl","Promise","resolve","reject","img","Image","onload","fillStyle","fillRect","drawImage","onerror","src","exportChartAsSvg","exportChartAsPng","toBlob","exportChartAsPdf","dataUrl","toDataURL","printWindow","open","write","close","exportChart","format","ExportButton","buttonPosition","handleExport","DEFAULT_MARGIN","LineChart","marginOverride","xAxis","yAxis","tooltip","tooltipConfig","zoom","zoomConfig","chartStyle","colorPalette","ariaLabel","globalCurveType","contextMenu","contextMenuConfig","export","exportConfig","clipId","useId","zoomState","zoomEnabled","enableClickZoom","tooltipEnabled","tooltipState","hasHover","getHighlightOpacity","seriesId","handleNativeWheel","passive","handleChartClick","nearestY","nearestDist","hypot","handleDoubleClick","ctxMenu","setCtxMenu","handleContextMenu","closeContextMenu","gridLines","getColor","index","palette","colors","role","onMouseMove","onMouseLeave","onWheel","onMouseDown","onMouseUp","onDoubleClick","onContextMenu","userSelect","gridLineColor","clipPath","findIndex","background"],"mappings":"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"}