@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.148.1 → 1.0.0-beta.149.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/charts/AreaPlot/AreaPlot.cjs +1 -1
- package/dist/components/charts/AreaPlot/AreaPlot.cjs.map +1 -1
- package/dist/components/charts/AreaPlot/AreaPlot.js +166 -148
- package/dist/components/charts/AreaPlot/AreaPlot.js.map +1 -1
- package/dist/components/charts/BarChart/BarChart.cjs +1 -1
- package/dist/components/charts/BarChart/BarChart.cjs.map +1 -1
- package/dist/components/charts/BarChart/BarChart.d.ts +7 -0
- package/dist/components/charts/BarChart/BarChart.js +151 -120
- package/dist/components/charts/BarChart/BarChart.js.map +1 -1
- package/dist/components/charts/BoxPlot/BoxPlot.cjs +1 -1
- package/dist/components/charts/BoxPlot/BoxPlot.cjs.map +1 -1
- package/dist/components/charts/BoxPlot/BoxPlot.d.ts +4 -0
- package/dist/components/charts/BoxPlot/BoxPlot.js +133 -102
- package/dist/components/charts/BoxPlot/BoxPlot.js.map +1 -1
- package/dist/components/charts/ChartTooltip/lines.cjs.map +1 -1
- package/dist/components/charts/ChartTooltip/lines.d.ts +2 -0
- package/dist/components/charts/ChartTooltip/lines.js.map +1 -1
- package/dist/components/charts/Chromatogram/Chromatogram.cjs +1 -1
- package/dist/components/charts/Chromatogram/Chromatogram.cjs.map +1 -1
- package/dist/components/charts/Chromatogram/Chromatogram.js +111 -114
- package/dist/components/charts/Chromatogram/Chromatogram.js.map +1 -1
- package/dist/components/charts/Chromatogram/plotBuilder.cjs +4 -1
- package/dist/components/charts/Chromatogram/plotBuilder.cjs.map +1 -1
- package/dist/components/charts/Chromatogram/plotBuilder.d.ts +32 -2
- package/dist/components/charts/Chromatogram/plotBuilder.js +170 -127
- package/dist/components/charts/Chromatogram/plotBuilder.js.map +1 -1
- package/dist/components/charts/Chromatogram/regionOverlays.cjs +1 -1
- package/dist/components/charts/Chromatogram/regionOverlays.cjs.map +1 -1
- package/dist/components/charts/Chromatogram/regionOverlays.js +11 -11
- package/dist/components/charts/Chromatogram/regionOverlays.js.map +1 -1
- package/dist/components/charts/Chromatogram/types.d.ts +4 -2
- package/dist/components/charts/Electropherogram/Electropherogram.cjs +1 -1
- package/dist/components/charts/Electropherogram/Electropherogram.cjs.map +1 -1
- package/dist/components/charts/Electropherogram/Electropherogram.js +89 -88
- package/dist/components/charts/Electropherogram/Electropherogram.js.map +1 -1
- package/dist/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.cjs +1 -1
- package/dist/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.cjs.map +1 -1
- package/dist/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.js +4 -5
- package/dist/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.js.map +1 -1
- package/dist/components/charts/chart-scale.cjs +2 -0
- package/dist/components/charts/chart-scale.cjs.map +1 -0
- package/dist/components/charts/chart-scale.d.ts +61 -0
- package/dist/components/charts/chart-scale.js +61 -0
- package/dist/components/charts/chart-scale.js.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.scoped.css +1 -1
- package/dist/index.tailwind.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AreaPlot.js","sources":["../../../../src/components/charts/AreaPlot/AreaPlot.tsx"],"sourcesContent":["import React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { chartTooltipLines, useChartTooltip } from \"../ChartTooltip\";\nimport { getLoadedPlotly, loadPlotly } from \"../plotly-loader\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { CHART_FONT_FAMILY, usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\ninterface AreaDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n fill?: \"tozeroy\" | \"tonexty\" | \"toself\";\n}\n\ntype AreaPlotVariant = \"normal\" | \"stacked\";\n\n/** Top margin reserving room for the 32px title; reduced when no title is set */\nconst TITLE_MARGIN_TOP = 80;\nconst NO_TITLE_MARGIN_TOP = 40;\n\ninterface AreaPlotProps {\n dataSeries: AreaDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n variant?: AreaPlotVariant;\n xTitle?: string;\n yTitle?: string;\n title?: string;\n /**\n * Categorical labels for the x-axis ticks. When provided, the x data values\n * still drive area positioning but the displayed tick labels match these\n * strings in order (e.g. [\"Mon\", \"Tue\", …]). Should align 1:1 with the\n * unique, ordered x values across all series.\n */\n xTickText?: string[];\n}\n\nconst AreaPlot: React.FC<AreaPlotProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n variant = \"normal\",\n xTitle,\n yTitle,\n title,\n xTickText,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n\n // When width/height are omitted the chart fills its container; otherwise the\n // explicit pixel size wins. The container is measured either way (cheap) but\n // only used as the resolved size for the omitted dimension(s).\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n\n // Hold the latest resolved size in a ref so the newPlot effect can read it\n // without listing it as a dependency — size changes are handled by a\n // separate relayout effect rather than by tearing down and recreating the plot.\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n const { bindTooltip, tooltipElement } = useChartTooltip({\n // Stacked traces carry the original series values as customdata;\n // display those instead of the cumulative stack heights\n getLines: (points) =>\n chartTooltipLines(\n points.map((point) =>\n typeof point.customdata === \"number\"\n ? { ...point, y: point.customdata }\n : point,\n ),\n { xLabel: xTitle, yLabel: yTitle },\n ),\n });\n\n const { xMin, xMax, yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: variant === \"stacked\" ? 0 : minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries, variant]);\n\n const effectiveXRange = useMemo(\n () => xRange || [xMin, xMax],\n [xRange, xMin, xMax],\n );\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(() => {\n const range = effectiveXRange[1] - effectiveXRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveXRange[0] / step) * step;\n while (current <= effectiveXRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveXRange]);\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n // When categorical labels are supplied, ticks must sit on the actual data\n // x-positions rather than the computed nice-step values above. Sorted\n // ascending so labels map deterministically to x regardless of series order.\n const xDataValues = useMemo(\n () => [...new Set(dataSeries.flatMap((s) => s.x))].sort((a, b) => a - b),\n [dataSeries],\n );\n\n // Only apply categorical labels when they align 1:1 with the tick positions;\n // a mismatch would silently mis-label ticks, so fall back to numeric ticks.\n const useCategoricalX = !!xTickText && xTickText.length === xDataValues.length;\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () =>\n title\n ? {\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: CHART_FONT_FAMILY,\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }\n : undefined,\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n let data;\n\n if (variant === \"stacked\") {\n // For stacked mode, we need to calculate cumulative values\n const cumulativeY = new Array(dataSeries[0]?.x.length || 0).fill(0);\n\n data = dataSeries.map((series, index) => {\n // Calculate cumulative values for this series\n const stackedY = series.y.map((value, i) => {\n const result = cumulativeY[i] + value;\n cumulativeY[i] = result;\n return result;\n });\n\n const color = seriesColor(index, series.color);\n return {\n x: series.x,\n y: stackedY,\n // Tooltips report the series' own values, not the stacked sums\n customdata: series.y,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n fill: index === 0 ? (\"tozeroy\" as const) : (\"tonexty\" as const),\n fillcolor: color,\n line: {\n color,\n width: 2,\n },\n };\n });\n } else {\n // Normal mode - each area fills independently from zero\n data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n x: series.x,\n y: series.y,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n fill: series.fill || (\"tozeroy\" as const),\n fillcolor: color,\n line: {\n color,\n width: 2,\n },\n };\n });\n }\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n ...(titleOptions ? { title: titleOptions } : {}),\n margin: {\n l: 80,\n r: 40,\n // Reserve room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n b: 96,\n t: title ? TITLE_MARGIN_TOP : NO_TITLE_MARGIN_TOP,\n pad: 0,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: CHART_FONT_FAMILY,\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: useCategoricalX ? xDataValues : xTicks,\n ...(useCategoricalX ? { ticktext: xTickText } : {}),\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: CHART_FONT_FAMILY,\n weight: 500,\n lineheight: 18,\n },\n },\n showlegend: true,\n };\n\n const config = {\n // We drive sizing explicitly from the measured container, so Plotly's own\n // window-resize responsiveness is disabled (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n // Plotly is loaded lazily so it stays out of the consumer's main chunk\n // (SW-2007); the draw is skipped if the effect re-runs or unmounts first.\n let cancelled = false;\n let plotElement: HTMLDivElement | null = null;\n void loadPlotly().then((plotly) => {\n if (cancelled || !plotRef.current) return;\n plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n });\n\n return () => {\n cancelled = true;\n if (plotElement) {\n getLoadedPlotly().purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, effectiveXRange, effectiveYRange, variant, xTitle, yTitle, title, titleOptions, tickOptions, xTicks, yTicks, xDataValues, useCategoricalX, xTickText, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — far cheaper\n // than recreating the plot (and it preserves tooltip/event bindings).\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n // plotInitedRef guarantees Plotly finished loading, so sync access is safe.\n void getLoadedPlotly()\n .relayout(plotElement, { width: resolvedWidth, height: resolvedHeight })\n .catch(() => {});\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"area-graph-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { AreaPlot };\nexport type { AreaDataSeries, AreaPlotVariant, AreaPlotProps };\n"],"names":["TITLE_MARGIN_TOP","NO_TITLE_MARGIN_TOP","AreaPlot","dataSeries","width","height","xRange","yRange","variant","xTitle","yTitle","title","xTickText","plotRef","useRef","theme","usePlotlyTheme","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","bindTooltip","tooltipElement","useChartTooltip","points","chartTooltipLines","point","xMin","xMax","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveXRange","effectiveYRange","xTicks","range","step","ticks","current","yTicks","xDataValues","s","a","b","useCategoricalX","tickOptions","CHART_FONT_FAMILY","titleOptions","useEffect","data","cumulativeY","index","stackedY","value","i","result","color","seriesColor","layout","config","cancelled","plotElement","loadPlotly","plotly","getLoadedPlotly","jsxs","cn","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AreaPlot.js","sources":["../../../../src/components/charts/AreaPlot/AreaPlot.tsx"],"sourcesContent":["import React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { Y_TICK_LABEL_SPACING, maxTickCount, resolveChartScale, thinTicks } from \"../chart-scale\";\nimport { chartTooltipLines, useChartTooltip } from \"../ChartTooltip\";\nimport { getLoadedPlotly, loadPlotly } from \"../plotly-loader\";\n\nimport type Plotly from \"plotly.js-dist\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { CHART_FONT_FAMILY, usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\ninterface AreaDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n fill?: \"tozeroy\" | \"tonexty\" | \"toself\";\n}\n\ntype AreaPlotVariant = \"normal\" | \"stacked\";\n\ninterface AreaPlotProps {\n dataSeries: AreaDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n variant?: AreaPlotVariant;\n xTitle?: string;\n yTitle?: string;\n title?: string;\n /**\n * Categorical labels for the x-axis ticks. When provided, the x data values\n * still drive area positioning but the displayed tick labels match these\n * strings in order (e.g. [\"Mon\", \"Tue\", …]). Should align 1:1 with the\n * unique, ordered x values across all series.\n */\n xTickText?: string[];\n}\n\nconst AreaPlot: React.FC<AreaPlotProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n variant = \"normal\",\n xTitle,\n yTitle,\n title,\n xTickText,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n\n // When width/height are omitted the chart fills its container; otherwise the\n // explicit pixel size wins. The container is measured either way (cheap) but\n // only used as the resolved size for the omitted dimension(s).\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n // Fonts, tick length and margins step down on small canvases so the plot\n // area (not the chrome) gets the pixels (SW-2298).\n const scale = resolveChartScale(resolvedWidth, resolvedHeight);\n const marginTop = title ? scale.margin.tTitle : scale.margin.tNoTitle;\n\n // Hold the latest resolved size in a ref so the newPlot effect can read it\n // without listing it as a dependency — size changes are handled by a\n // separate relayout effect rather than by tearing down and recreating the plot.\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n const { bindTooltip, tooltipElement } = useChartTooltip({\n // Stacked traces carry the original series values as customdata;\n // display those instead of the cumulative stack heights\n getLines: (points) =>\n chartTooltipLines(\n points.map((point) =>\n typeof point.customdata === \"number\"\n ? { ...point, y: point.customdata }\n : point,\n ),\n { xLabel: xTitle, yLabel: yTitle },\n ),\n });\n\n const { xMin, xMax, yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: variant === \"stacked\" ? 0 : minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries, variant]);\n\n const effectiveXRange = useMemo(\n () => xRange || [xMin, xMax],\n [xRange, xMin, xMax],\n );\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(() => {\n const range = effectiveXRange[1] - effectiveXRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveXRange[0] / step) * step;\n while (current <= effectiveXRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveXRange]);\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n // When categorical labels are supplied, ticks must sit on the actual data\n // x-positions rather than the computed nice-step values above. Sorted\n // ascending so labels map deterministically to x regardless of series order.\n const xDataValues = useMemo(\n () => [...new Set(dataSeries.flatMap((s) => s.x))].sort((a, b) => a - b),\n [dataSeries],\n );\n\n // Only apply categorical labels when they align 1:1 with the tick positions;\n // a mismatch would silently mis-label ticks, so fall back to numeric ticks.\n const useCategoricalX = !!xTickText && xTickText.length === xDataValues.length;\n\n // Y ticks thinned to what fits the plot height. Derived from the resolved\n // (not last-plotted) height so an in-place resize also updates them via the\n // relayout effect below, instead of only when the scale bucket flips.\n const yTickVals = useMemo(\n () =>\n thinTicks(\n yTicks,\n maxTickCount(\n resolvedHeight - marginTop - scale.margin.b,\n scale.tickFontSize * Y_TICK_LABEL_SPACING,\n ),\n ),\n [yTicks, resolvedHeight, marginTop, scale],\n );\n const yTickKey = yTickVals.join(\",\");\n const yTickValsRef = useRef(yTickVals);\n yTickValsRef.current = yTickVals;\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: scale.ticklen,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: scale.tickFontSize,\n color: theme.textColor,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme, scale],\n );\n\n const titleOptions = useMemo(\n () =>\n title\n ? {\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: scale.titleFontSize,\n weight: 600,\n family: CHART_FONT_FAMILY,\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }\n : undefined,\n [title, theme, scale],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n let data;\n\n if (variant === \"stacked\") {\n // For stacked mode, we need to calculate cumulative values\n const cumulativeY = new Array(dataSeries[0]?.x.length || 0).fill(0);\n\n data = dataSeries.map((series, index) => {\n // Calculate cumulative values for this series\n const stackedY = series.y.map((value, i) => {\n const result = cumulativeY[i] + value;\n cumulativeY[i] = result;\n return result;\n });\n\n const color = seriesColor(index, series.color);\n return {\n x: series.x,\n y: stackedY,\n // Tooltips report the series' own values, not the stacked sums\n customdata: series.y,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n fill: index === 0 ? (\"tozeroy\" as const) : (\"tonexty\" as const),\n fillcolor: color,\n line: {\n color,\n width: 2,\n },\n };\n });\n } else {\n // Normal mode - each area fills independently from zero\n data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n x: series.x,\n y: series.y,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n fill: series.fill || (\"tozeroy\" as const),\n fillcolor: color,\n line: {\n color,\n width: 2,\n },\n };\n });\n }\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n ...(titleOptions ? { title: titleOptions } : {}),\n margin: {\n l: scale.margin.l,\n r: scale.margin.r,\n // Reserve room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n b: scale.margin.b,\n t: marginTop,\n pad: 0,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: CHART_FONT_FAMILY,\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: scale.axisTitleFontSize,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: scale.axisTitleStandoff,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: useCategoricalX ? xDataValues : xTicks,\n ...(useCategoricalX ? { ticktext: xTickText } : {}),\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: scale.axisTitleFontSize,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: scale.axisTitleStandoff,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTickValsRef.current,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: scale.legendFontSize,\n color: theme.legendColor,\n family: CHART_FONT_FAMILY,\n weight: 500,\n lineheight: scale.legendLineHeight,\n },\n },\n showlegend: true,\n };\n\n const config = {\n // We drive sizing explicitly from the measured container, so Plotly's own\n // window-resize responsiveness is disabled (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n // Plotly is loaded lazily so it stays out of the consumer's main chunk\n // (SW-2007); the draw is skipped if the effect re-runs or unmounts first.\n let cancelled = false;\n let plotElement: HTMLDivElement | null = null;\n void loadPlotly().then((plotly) => {\n if (cancelled || !plotRef.current) return;\n plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n });\n\n return () => {\n cancelled = true;\n if (plotElement) {\n getLoadedPlotly().purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, effectiveXRange, effectiveYRange, variant, xTitle, yTitle, title, titleOptions, tickOptions, xTicks, yTicks, xDataValues, useCategoricalX, xTickText, theme, scale, marginTop, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — far cheaper\n // than recreating the plot (and it preserves tooltip/event bindings).\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n // plotInitedRef guarantees Plotly finished loading, so sync access is safe.\n void getLoadedPlotly()\n .relayout(plotElement, {\n width: resolvedWidth,\n height: resolvedHeight,\n // Re-thin the y ticks for the new plot height (SW-2298). The flattened\n // key is valid relayout input but not part of the Layout type.\n \"yaxis.tickvals\": yTickValsRef.current,\n } as Partial<Plotly.Layout>)\n .catch(() => {});\n }, [resolvedWidth, resolvedHeight, yTickKey]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"area-graph-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { AreaPlot };\nexport type { AreaDataSeries, AreaPlotVariant, AreaPlotProps };\n"],"names":["AreaPlot","dataSeries","width","height","xRange","yRange","variant","xTitle","yTitle","title","xTickText","plotRef","useRef","theme","usePlotlyTheme","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","scale","resolveChartScale","marginTop","sizeRef","plotInitedRef","appliedSizeRef","bindTooltip","tooltipElement","useChartTooltip","points","chartTooltipLines","point","xMin","xMax","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveXRange","effectiveYRange","xTicks","range","step","ticks","current","yTicks","xDataValues","s","a","b","useCategoricalX","yTickVals","thinTicks","maxTickCount","Y_TICK_LABEL_SPACING","yTickKey","yTickValsRef","tickOptions","CHART_FONT_FAMILY","titleOptions","useEffect","data","cumulativeY","index","stackedY","value","i","result","color","seriesColor","layout","config","cancelled","plotElement","loadPlotly","plotly","getLoadedPlotly","jsxs","cn","jsx"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("react/jsx-runtime"),r=require("react"),y=require("../chart-scale.cjs"),Z=require("../ChartTooltip/ChartTooltip.cjs"),H=require("../plotly-loader.cjs"),$=require("../../../hooks/use-element-size.cjs"),d=require("../../../hooks/use-plotly-theme.cjs"),ee=require("../../../lib/utils.cjs"),te=require("../../../utils/colors.cjs"),oe={...y.REGULAR_SCALE,axisTitleStandoff:30,legendFontSize:16,legendLineHeight:21,margin:{...y.REGULAR_SCALE.margin,r:30,tTitle:60,tNoTitle:30}},re=.24,ne=.8,ie=({dataSeries:a,width:O,height:P,xRange:w,yRange:M,variant:m="group",xTitle:R,yTitle:E,title:T,barWidth:F,xTickText:x})=>{const f=r.useRef(null),i=d.usePlotlyTheme(),{bindTooltip:Y,tooltipElement:D}=Z.useChartTooltip({xLabel:R,yLabel:E}),[W,q]=$.useElementSize(),u=O??q.width,c=P??q.height,B=u>0&&c>0,K=O===void 0,J=P===void 0,o=y.resolveChartScale(u,c,oe),C=T?o.margin.tTitle:o.margin.tNoTitle,L=r.useRef({width:u,height:c});L.current={width:u,height:c};const b=r.useRef(!1),k=r.useRef({width:0,height:0}),{yMin:U,yMax:G}=r.useMemo(()=>{let e=Number.MAX_VALUE,t=Number.MIN_VALUE,s=Number.MAX_VALUE,l=Number.MIN_VALUE;a.forEach(g=>{g.x.forEach(A=>{e=Math.min(e,A),t=Math.max(t,A)}),g.y.forEach(A=>{s=Math.min(s,A),l=Math.max(l,A)})});const h=(t-e)*.1,n=(l-s)*.1;return{xMin:e-h,xMax:t+h,yMin:m==="stack"?0:s-n,yMax:l+n}},[a,m]),_=r.useMemo(()=>M||[U,G],[M,U,G]),p=r.useMemo(()=>[...new Set(a.flatMap(e=>e.x))].sort((e,t)=>e-t),[a]),V=!!x&&x.length===p.length,z=r.useMemo(()=>{if(F!==void 0)return F;const e=p.slice(1).map((n,g)=>n-p[g]).sort((n,g)=>n-g);if(e.length===0)return;const t=Math.floor(e.length/2),s=e.length%2===1?e[t]:(e[t-1]+e[t])/2,l=m==="group"?Math.max(1,a.length):1,h=Math.min(re,ne/l);return s*h},[F,p,m,a.length]),N=r.useMemo(()=>{const e=_[1]-_[0];let t=Math.pow(10,Math.floor(Math.log10(e)));e/t>10&&(t=t*2),e/t<4&&(t=t/2);const s=[];let l=Math.ceil(_[0]/t)*t;for(;l<=_[1];)s.push(l),l+=t;return s},[_]),X=r.useMemo(()=>{switch(m){case"stack":return"stack";case"overlay":return"overlay";default:return"group"}},[m]),S=r.useMemo(()=>y.thinTicks(N,y.maxTickCount(c-C-o.margin.b,o.tickFontSize*y.Y_TICK_LABEL_SPACING)),[N,c,C,o]),Q=S.join(","),v=r.useRef(S);v.current=S;const I=r.useMemo(()=>({tickcolor:i.tickColor,ticklen:o.ticklen,tickwidth:1,ticks:"outside",tickfont:{size:o.tickFontSize,color:i.textColor,family:d.CHART_FONT_FAMILY,weight:400},linecolor:i.lineColor,linewidth:1,position:0,zeroline:!1}),[i,o]);return r.useEffect(()=>{if(!f.current||!B)return;const e=a.map((n,g)=>({x:n.x,y:n.y,type:"bar",name:n.name,hoverinfo:"none",marker:{color:te.seriesColor(g,n.color)},...z===void 0?{}:{width:z},error_y:n.error_y})),t={...T?{title:{text:T,font:{size:o.titleFontSize,family:d.CHART_FONT_FAMILY,color:i.textColor}}}:{},width:L.current.width,height:L.current.height,margin:{l:o.margin.l,r:o.margin.r,b:o.margin.b,t:C,pad:0},paper_bgcolor:i.paperBg,plot_bgcolor:i.plotBg,font:{family:d.CHART_FONT_FAMILY},barmode:X,bargap:.15,dragmode:!1,xaxis:{title:{text:R,font:{size:o.axisTitleFontSize,color:i.textSecondary,family:d.CHART_FONT_FAMILY,weight:400},standoff:o.axisTitleStandoff},gridcolor:i.gridColor,range:w,autorange:!w,tickmode:"array",tickvals:p,...V?{ticktext:x}:{},showgrid:!0,automargin:!0,...I},yaxis:{title:{text:E,font:{size:o.axisTitleFontSize,color:i.textSecondary,family:d.CHART_FONT_FAMILY,weight:400},standoff:o.axisTitleStandoff},gridcolor:i.gridColor,range:M,autorange:!M,tickmode:"array",tickvals:v.current,showgrid:!0,automargin:!0,...I},legend:{x:.5,y:0,xanchor:"center",yanchor:"bottom",yref:"container",orientation:"h",font:{size:o.legendFontSize,color:i.legendColor,family:d.CHART_FONT_FAMILY,weight:500,lineheight:o.legendLineHeight}},showlegend:a.length>1},s={responsive:!1,displayModeBar:!1,displaylogo:!1};let l=!1,h=null;return H.loadPlotly().then(n=>{l||!f.current||(n.newPlot(f.current,e,t,s),Y(f.current),h=f.current,b.current=!0,k.current={...L.current})}),()=>{l=!0,h&&(H.getLoadedPlotly().purge(h),b.current=!1)}},[a,B,w,M,R,E,T,z,X,I,p,N,V,x,i,o,C,Y]),r.useEffect(()=>{const e=f.current;!e||!b.current||u<=0||c<=0||k.current.width===u&&k.current.height===c||(k.current={width:u,height:c},H.getLoadedPlotly().relayout(e,{width:u,height:c,"yaxis.tickvals":v.current}).catch(()=>{}))},[u,c,Q]),j.jsxs("div",{ref:W,className:ee.cn("bar-graph-container relative",K&&"w-full",J&&"h-full"),children:[j.jsx("div",{ref:f,style:{width:"100%",height:"100%"}}),D]})};exports.BarChart=ie;
|
|
2
2
|
//# sourceMappingURL=BarChart.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.cjs","sources":["../../../../src/components/charts/BarChart/BarChart.tsx"],"sourcesContent":["import React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\nimport { getLoadedPlotly, loadPlotly } from \"../plotly-loader\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { CHART_FONT_FAMILY, usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\ninterface BarDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n error_y?: {\n type: \"data\";\n array: number[];\n visible: boolean;\n };\n}\n\ntype BarChartVariant = \"group\" | \"stack\" | \"overlay\";\n\n/** Top margin reserving room for the 32px title; reduced when no title is set */\nconst TITLE_MARGIN_TOP = 60;\nconst NO_TITLE_MARGIN_TOP = 30;\n\ninterface BarChartProps {\n dataSeries: BarDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n variant?: BarChartVariant;\n xTitle?: string;\n yTitle?: string;\n title?: string;\n barWidth?: number;\n /**\n * Categorical labels for the x-axis ticks. When provided, the x data values\n * still drive bar positioning but the displayed tick labels match these\n * strings in order (e.g. [\"Mon\", \"Tue\", …]). Should align 1:1 with the\n * unique, ordered x values across all series.\n */\n xTickText?: string[];\n}\n\nconst BarChart: React.FC<BarChartProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n variant = \"group\",\n xTitle,\n yTitle,\n title,\n barWidth = 24,\n xTickText,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaPlot for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n\n const { yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: variant === \"stack\" ? 0 : minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries, variant]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(\n () => [...new Set(dataSeries.flatMap((s) => s.x))].sort((a, b) => a - b),\n [dataSeries],\n );\n\n // Only apply categorical labels when they align 1:1 with the tick positions;\n // a mismatch would silently mis-label ticks, so fall back to numeric ticks.\n const useCategoricalX = !!xTickText && xTickText.length === xTicks.length;\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const barMode = useMemo((): \"group\" | \"stack\" | \"overlay\" => {\n switch (variant) {\n case \"stack\":\n return \"stack\";\n case \"overlay\":\n return \"overlay\";\n case \"group\":\n default:\n return \"group\";\n }\n }, [variant]);\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const data = dataSeries.map((series, index) => ({\n x: series.x,\n y: series.y,\n type: \"bar\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color: seriesColor(index, series.color),\n },\n width: barWidth,\n error_y: series.error_y,\n }));\n\n const layout = {\n ...(title\n ? {\n title: {\n text: title,\n font: {\n size: 32,\n family: CHART_FONT_FAMILY,\n color: theme.textColor,\n },\n },\n }\n : {}),\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n margin: {\n l: 80,\n r: 30,\n // Reserve room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n b: 96,\n t: title ? TITLE_MARGIN_TOP : NO_TITLE_MARGIN_TOP,\n pad: 0,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: CHART_FONT_FAMILY,\n },\n barmode: barMode,\n bargap: 0.15,\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: 32,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: xTicks,\n ...(useCategoricalX ? { ticktext: xTickText } : {}),\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: 30,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 16,\n color: theme.legendColor,\n family: CHART_FONT_FAMILY,\n weight: 500,\n },\n },\n showlegend: dataSeries.length > 1,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n // Plotly is loaded lazily so it stays out of the consumer's main chunk\n // (SW-2007); the draw is skipped if the effect re-runs or unmounts first.\n let cancelled = false;\n let plotElement: HTMLDivElement | null = null;\n void loadPlotly().then((plotly) => {\n if (cancelled || !plotRef.current) return;\n plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n });\n\n return () => {\n cancelled = true;\n if (plotElement) {\n getLoadedPlotly().purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, xTitle, yTitle, title, barWidth, barMode, tickOptions, xTicks, yTicks, useCategoricalX, xTickText, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n // plotInitedRef guarantees Plotly finished loading, so sync access is safe.\n void getLoadedPlotly()\n .relayout(plotElement, { width: resolvedWidth, height: resolvedHeight })\n .catch(() => {});\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"bar-graph-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { BarChart };\nexport type { BarDataSeries, BarChartVariant, BarChartProps };\n"],"names":["TITLE_MARGIN_TOP","NO_TITLE_MARGIN_TOP","BarChart","dataSeries","width","height","xRange","yRange","variant","xTitle","yTitle","title","barWidth","xTickText","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveYRange","xTicks","s","a","b","useCategoricalX","yTicks","range","step","ticks","current","barMode","tickOptions","CHART_FONT_FAMILY","useEffect","data","index","seriesColor","layout","config","cancelled","plotElement","loadPlotly","plotly","getLoadedPlotly","jsxs","cn","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BarChart.cjs","sources":["../../../../src/components/charts/BarChart/BarChart.tsx"],"sourcesContent":["import React, { useEffect, useRef, useMemo } from \"react\";\n\nimport {\n REGULAR_SCALE,\n Y_TICK_LABEL_SPACING,\n maxTickCount,\n resolveChartScale,\n thinTicks,\n} from \"../chart-scale\";\nimport { useChartTooltip } from \"../ChartTooltip\";\nimport { getLoadedPlotly, loadPlotly } from \"../plotly-loader\";\n\nimport type Plotly from \"plotly.js-dist\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { CHART_FONT_FAMILY, usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\ninterface BarDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n error_y?: {\n type: \"data\";\n array: number[];\n visible: boolean;\n };\n}\n\ntype BarChartVariant = \"group\" | \"stack\" | \"overlay\";\n\n/** Full-size chrome: the bar chart runs a larger legend and tighter top margin */\nconst BAR_REGULAR_SCALE = {\n ...REGULAR_SCALE,\n axisTitleStandoff: 30,\n legendFontSize: 16,\n legendLineHeight: 21,\n margin: { ...REGULAR_SCALE.margin, r: 30, tTitle: 60, tNoTitle: 30 },\n};\n\n/**\n * Default bar width as a fraction of the median gap between x positions. A\n * fixed data-unit width (formerly 24) only suited x spaced by ~100 and swamped\n * the axis for index-style x such as 0..6 (SW-2298). The median (not the\n * minimum) keeps one tight pair in otherwise wide-spaced data from shrinking\n * every bar to a hairline.\n */\nconst DEFAULT_BAR_WIDTH_FRACTION = 0.24;\n/**\n * Grouped bars share one slot per x position; cap the group at this fraction\n * of the gap so four or more series no longer overflow into the neighbour.\n */\nconst MAX_GROUP_WIDTH_FRACTION = 0.8;\n\ninterface BarChartProps {\n dataSeries: BarDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n variant?: BarChartVariant;\n xTitle?: string;\n yTitle?: string;\n title?: string;\n /**\n * Bar width in x-axis data units. Defaults to about a quarter of the median\n * gap between x positions (narrower when four or more grouped series must\n * share a slot), so bars keep the same visual weight whether x runs 0..6 or\n * 200..1000. With very uneven spacing the closest bars can still overlap or\n * the widest gaps look sparse — pass an explicit width in that case.\n */\n barWidth?: number;\n /**\n * Categorical labels for the x-axis ticks. When provided, the x data values\n * still drive bar positioning but the displayed tick labels match these\n * strings in order (e.g. [\"Mon\", \"Tue\", …]). Should align 1:1 with the\n * unique, ordered x values across all series.\n */\n xTickText?: string[];\n}\n\nconst BarChart: React.FC<BarChartProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n variant = \"group\",\n xTitle,\n yTitle,\n title,\n barWidth,\n xTickText,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaPlot for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n // Fonts, tick length and margins step down on small canvases so the plot\n // area (not the chrome) gets the pixels (SW-2298).\n const scale = resolveChartScale(resolvedWidth, resolvedHeight, BAR_REGULAR_SCALE);\n const marginTop = title ? scale.margin.tTitle : scale.margin.tNoTitle;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n\n const { yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: variant === \"stack\" ? 0 : minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries, variant]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(\n () => [...new Set(dataSeries.flatMap((s) => s.x))].sort((a, b) => a - b),\n [dataSeries],\n );\n\n // Only apply categorical labels when they align 1:1 with the tick positions;\n // a mismatch would silently mis-label ticks, so fall back to numeric ticks.\n const useCategoricalX = !!xTickText && xTickText.length === xTicks.length;\n\n const resolvedBarWidth = useMemo(() => {\n if (barWidth !== undefined) return barWidth;\n // xTicks is the sorted, de-duplicated data x, so every gap is > 0\n const gaps = xTicks.slice(1).map((x, i) => x - xTicks[i]).sort((a, b) => a - b);\n // A single x position has no gap to scale from; let Plotly size the bar.\n if (gaps.length === 0) return;\n const mid = Math.floor(gaps.length / 2);\n const medianGap = gaps.length % 2 === 1 ? gaps[mid] : (gaps[mid - 1] + gaps[mid]) / 2;\n const groupedSeries = variant === \"group\" ? Math.max(1, dataSeries.length) : 1;\n const fraction = Math.min(DEFAULT_BAR_WIDTH_FRACTION, MAX_GROUP_WIDTH_FRACTION / groupedSeries);\n return medianGap * fraction;\n }, [barWidth, xTicks, variant, dataSeries.length]);\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const barMode = useMemo((): \"group\" | \"stack\" | \"overlay\" => {\n switch (variant) {\n case \"stack\":\n return \"stack\";\n case \"overlay\":\n return \"overlay\";\n case \"group\":\n default:\n return \"group\";\n }\n }, [variant]);\n\n // Y ticks thinned to what fits the plot height. Derived from the resolved\n // (not last-plotted) height so an in-place resize also updates them via the\n // relayout effect below, instead of only when the scale bucket flips.\n const yTickVals = useMemo(\n () =>\n thinTicks(\n yTicks,\n maxTickCount(\n resolvedHeight - marginTop - scale.margin.b,\n scale.tickFontSize * Y_TICK_LABEL_SPACING,\n ),\n ),\n [yTicks, resolvedHeight, marginTop, scale],\n );\n const yTickKey = yTickVals.join(\",\");\n const yTickValsRef = useRef(yTickVals);\n yTickValsRef.current = yTickVals;\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: scale.ticklen,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: scale.tickFontSize,\n color: theme.textColor,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme, scale],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const data = dataSeries.map((series, index) => ({\n x: series.x,\n y: series.y,\n type: \"bar\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color: seriesColor(index, series.color),\n },\n ...(resolvedBarWidth === undefined ? {} : { width: resolvedBarWidth }),\n error_y: series.error_y,\n }));\n\n const layout = {\n ...(title\n ? {\n title: {\n text: title,\n font: {\n size: scale.titleFontSize,\n family: CHART_FONT_FAMILY,\n color: theme.textColor,\n },\n },\n }\n : {}),\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n margin: {\n l: scale.margin.l,\n r: scale.margin.r,\n // Reserve room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n b: scale.margin.b,\n t: marginTop,\n pad: 0,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: CHART_FONT_FAMILY,\n },\n barmode: barMode,\n bargap: 0.15,\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: scale.axisTitleFontSize,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: scale.axisTitleStandoff,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: xTicks,\n ...(useCategoricalX ? { ticktext: xTickText } : {}),\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: scale.axisTitleFontSize,\n color: theme.textSecondary,\n family: CHART_FONT_FAMILY,\n weight: 400,\n },\n standoff: scale.axisTitleStandoff,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTickValsRef.current,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: scale.legendFontSize,\n color: theme.legendColor,\n family: CHART_FONT_FAMILY,\n weight: 500,\n lineheight: scale.legendLineHeight,\n },\n },\n showlegend: dataSeries.length > 1,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n // Plotly is loaded lazily so it stays out of the consumer's main chunk\n // (SW-2007); the draw is skipped if the effect re-runs or unmounts first.\n let cancelled = false;\n let plotElement: HTMLDivElement | null = null;\n void loadPlotly().then((plotly) => {\n if (cancelled || !plotRef.current) return;\n plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n });\n\n return () => {\n cancelled = true;\n if (plotElement) {\n getLoadedPlotly().purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, xTitle, yTitle, title, resolvedBarWidth, barMode, tickOptions, xTicks, yTicks, useCategoricalX, xTickText, theme, scale, marginTop, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n // plotInitedRef guarantees Plotly finished loading, so sync access is safe.\n void getLoadedPlotly()\n .relayout(plotElement, {\n width: resolvedWidth,\n height: resolvedHeight,\n // Re-thin the y ticks for the new plot height (SW-2298). The flattened\n // key is valid relayout input but not part of the Layout type.\n \"yaxis.tickvals\": yTickValsRef.current,\n } as Partial<Plotly.Layout>)\n .catch(() => {});\n }, [resolvedWidth, resolvedHeight, yTickKey]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"bar-graph-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { BarChart };\nexport type { BarDataSeries, BarChartVariant, BarChartProps };\n"],"names":["BAR_REGULAR_SCALE","REGULAR_SCALE","DEFAULT_BAR_WIDTH_FRACTION","MAX_GROUP_WIDTH_FRACTION","BarChart","dataSeries","width","height","xRange","yRange","variant","xTitle","yTitle","title","barWidth","xTickText","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","scale","resolveChartScale","marginTop","sizeRef","plotInitedRef","appliedSizeRef","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveYRange","xTicks","s","a","b","useCategoricalX","resolvedBarWidth","gaps","i","mid","medianGap","groupedSeries","fraction","yTicks","range","step","ticks","current","barMode","yTickVals","thinTicks","maxTickCount","Y_TICK_LABEL_SPACING","yTickKey","yTickValsRef","tickOptions","CHART_FONT_FAMILY","useEffect","data","index","seriesColor","layout","config","cancelled","plotElement","loadPlotly","plotly","getLoadedPlotly","jsxs","cn","jsx"],"mappings":"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"}
|
|
@@ -30,6 +30,13 @@ interface BarChartProps {
|
|
|
30
30
|
xTitle?: string;
|
|
31
31
|
yTitle?: string;
|
|
32
32
|
title?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Bar width in x-axis data units. Defaults to about a quarter of the median
|
|
35
|
+
* gap between x positions (narrower when four or more grouped series must
|
|
36
|
+
* share a slot), so bars keep the same visual weight whether x runs 0..6 or
|
|
37
|
+
* 200..1000. With very uneven spacing the closest bars can still overlap or
|
|
38
|
+
* the widest gaps look sparse — pass an explicit width in that case.
|
|
39
|
+
*/
|
|
33
40
|
barWidth?: number;
|
|
34
41
|
/**
|
|
35
42
|
* Categorical labels for the x-axis ticks. When provided, the x data values
|