@tetrascience-npm/tetrascience-react-ui 0.6.0 → 0.7.0-beta.103.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/README.md +55 -0
- package/dist/components/ai/chain-of-thought.cjs +1 -1
- package/dist/components/ai/chain-of-thought.cjs.map +1 -1
- package/dist/components/ai/chain-of-thought.js +5 -5
- package/dist/components/ai/chain-of-thought.js.map +1 -1
- package/dist/components/ai/conversation.cjs +2 -2
- package/dist/components/ai/conversation.cjs.map +1 -1
- package/dist/components/ai/conversation.js +2 -2
- package/dist/components/ai/conversation.js.map +1 -1
- package/dist/components/ai/queue.cjs +1 -1
- package/dist/components/ai/queue.cjs.map +1 -1
- package/dist/components/ai/queue.js +9 -9
- package/dist/components/ai/queue.js.map +1 -1
- package/dist/components/charts/AreaGraph/AreaGraph.cjs +1 -1
- package/dist/components/charts/AreaGraph/AreaGraph.cjs.map +1 -1
- package/dist/components/charts/AreaGraph/AreaGraph.js +165 -103
- package/dist/components/charts/AreaGraph/AreaGraph.js.map +1 -1
- package/dist/components/charts/BarGraph/BarGraph.cjs +1 -1
- package/dist/components/charts/BarGraph/BarGraph.cjs.map +1 -1
- package/dist/components/charts/BarGraph/BarGraph.js +135 -90
- package/dist/components/charts/BarGraph/BarGraph.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.js +130 -92
- package/dist/components/charts/Boxplot/Boxplot.js.map +1 -1
- package/dist/components/charts/ChartTooltip/ChartTooltip.cjs +2 -0
- package/dist/components/charts/ChartTooltip/ChartTooltip.cjs.map +1 -0
- package/dist/components/charts/ChartTooltip/ChartTooltip.js +163 -0
- package/dist/components/charts/ChartTooltip/ChartTooltip.js.map +1 -0
- package/dist/components/charts/ChartTooltip/lines.cjs +2 -0
- package/dist/components/charts/ChartTooltip/lines.cjs.map +1 -0
- package/dist/components/charts/ChartTooltip/lines.js +29 -0
- package/dist/components/charts/ChartTooltip/lines.js.map +1 -0
- 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 +75 -61
- package/dist/components/charts/Chromatogram/Chromatogram.js.map +1 -1
- package/dist/components/charts/ChromatogramChart/ChromatogramChart.cjs +1 -1
- package/dist/components/charts/ChromatogramChart/ChromatogramChart.cjs.map +1 -1
- package/dist/components/charts/ChromatogramChart/ChromatogramChart.js +116 -108
- package/dist/components/charts/ChromatogramChart/ChromatogramChart.js.map +1 -1
- package/dist/components/charts/ChromatogramChart/annotations.cjs +1 -1
- package/dist/components/charts/ChromatogramChart/annotations.cjs.map +1 -1
- package/dist/components/charts/ChromatogramChart/annotations.js +32 -32
- package/dist/components/charts/ChromatogramChart/annotations.js.map +1 -1
- package/dist/components/charts/ChromatogramChart/boundaryMarkers.cjs +1 -1
- package/dist/components/charts/ChromatogramChart/boundaryMarkers.cjs.map +1 -1
- package/dist/components/charts/ChromatogramChart/boundaryMarkers.js +6 -6
- package/dist/components/charts/ChromatogramChart/boundaryMarkers.js.map +1 -1
- package/dist/components/charts/ChromatogramChart/constants.cjs +1 -1
- package/dist/components/charts/ChromatogramChart/constants.cjs.map +1 -1
- package/dist/components/charts/ChromatogramChart/constants.js +11 -5
- package/dist/components/charts/ChromatogramChart/constants.js.map +1 -1
- package/dist/components/charts/ChromatogramChart/dataProcessing.cjs +1 -1
- package/dist/components/charts/ChromatogramChart/dataProcessing.cjs.map +1 -1
- package/dist/components/charts/ChromatogramChart/dataProcessing.js +38 -49
- package/dist/components/charts/ChromatogramChart/dataProcessing.js.map +1 -1
- package/dist/components/charts/DotPlot/DotPlot.cjs +1 -1
- package/dist/components/charts/DotPlot/DotPlot.cjs.map +1 -1
- package/dist/components/charts/DotPlot/DotPlot.js +112 -88
- package/dist/components/charts/DotPlot/DotPlot.js.map +1 -1
- package/dist/components/charts/Histogram/Histogram.cjs +1 -1
- package/dist/components/charts/Histogram/Histogram.cjs.map +1 -1
- package/dist/components/charts/Histogram/Histogram.js +132 -115
- package/dist/components/charts/Histogram/Histogram.js.map +1 -1
- package/dist/components/charts/LineGraph/LineGraph.cjs +1 -1
- package/dist/components/charts/LineGraph/LineGraph.cjs.map +1 -1
- package/dist/components/charts/LineGraph/LineGraph.js +151 -111
- package/dist/components/charts/LineGraph/LineGraph.js.map +1 -1
- package/dist/components/charts/PieChart/PieChart.cjs +1 -1
- package/dist/components/charts/PieChart/PieChart.cjs.map +1 -1
- package/dist/components/charts/PieChart/PieChart.js +81 -70
- package/dist/components/charts/PieChart/PieChart.js.map +1 -1
- package/dist/components/charts/PlateMap/PlateMap.cjs +1 -1
- package/dist/components/charts/PlateMap/PlateMap.cjs.map +1 -1
- package/dist/components/charts/PlateMap/PlateMap.js +271 -256
- package/dist/components/charts/PlateMap/PlateMap.js.map +1 -1
- package/dist/components/charts/PlateMap/constants.cjs +1 -1
- package/dist/components/charts/PlateMap/constants.cjs.map +1 -1
- package/dist/components/charts/PlateMap/constants.js +20 -29
- package/dist/components/charts/PlateMap/constants.js.map +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.cjs +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.cjs.map +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.js +154 -106
- package/dist/components/charts/ScatterGraph/ScatterGraph.js.map +1 -1
- package/dist/components/composed/DataAppShell/DataAppShell.cjs +1 -1
- package/dist/components/composed/DataAppShell/DataAppShell.cjs.map +1 -1
- package/dist/components/composed/DataAppShell/DataAppShell.js +118 -111
- package/dist/components/composed/DataAppShell/DataAppShell.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.js +8 -8
- package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -1
- package/dist/components/composed/RichListItem/RichListItem.cjs +1 -1
- package/dist/components/composed/RichListItem/RichListItem.cjs.map +1 -1
- package/dist/components/composed/RichListItem/RichListItem.js +16 -16
- package/dist/components/composed/RichListItem/RichListItem.js.map +1 -1
- package/dist/components/ui/accordion.cjs +1 -1
- package/dist/components/ui/accordion.cjs.map +1 -1
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/accordion.js.map +1 -1
- package/dist/components/ui/badge.cjs +1 -1
- package/dist/components/ui/badge.cjs.map +1 -1
- package/dist/components/ui/badge.js +8 -8
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.cjs +1 -1
- package/dist/components/ui/button.cjs.map +1 -1
- package/dist/components/ui/button.js +11 -11
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/checkbox.cjs +1 -1
- package/dist/components/ui/checkbox.cjs.map +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/combobox.cjs +1 -1
- package/dist/components/ui/combobox.cjs.map +1 -1
- package/dist/components/ui/combobox.js +12 -12
- package/dist/components/ui/combobox.js.map +1 -1
- package/dist/components/ui/input-group.cjs +1 -1
- package/dist/components/ui/input-group.cjs.map +1 -1
- package/dist/components/ui/input-group.js +19 -19
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/item.cjs +1 -1
- package/dist/components/ui/item.cjs.map +1 -1
- package/dist/components/ui/item.js +1 -1
- package/dist/components/ui/item.js.map +1 -1
- package/dist/components/ui/menubar.cjs +1 -1
- package/dist/components/ui/menubar.cjs.map +1 -1
- package/dist/components/ui/menubar.js +1 -1
- package/dist/components/ui/menubar.js.map +1 -1
- package/dist/components/ui/navigation-menu.cjs +1 -1
- package/dist/components/ui/navigation-menu.cjs.map +1 -1
- package/dist/components/ui/navigation-menu.js +54 -54
- package/dist/components/ui/navigation-menu.js.map +1 -1
- package/dist/components/ui/radio-group.cjs +1 -1
- package/dist/components/ui/radio-group.cjs.map +1 -1
- package/dist/components/ui/radio-group.js +1 -1
- package/dist/components/ui/radio-group.js.map +1 -1
- package/dist/components/ui/resizable.cjs +1 -1
- package/dist/components/ui/resizable.cjs.map +1 -1
- package/dist/components/ui/resizable.js +27 -15
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/components/ui/select.cjs +1 -1
- package/dist/components/ui/select.cjs.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/slider.cjs +1 -1
- package/dist/components/ui/slider.cjs.map +1 -1
- package/dist/components/ui/slider.js +9 -9
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/table.cjs +1 -1
- package/dist/components/ui/table.cjs.map +1 -1
- package/dist/components/ui/table.js +13 -13
- package/dist/components/ui/table.js.map +1 -1
- package/dist/components/ui/tabs.cjs +1 -1
- package/dist/components/ui/tabs.cjs.map +1 -1
- package/dist/components/ui/tabs.js +7 -7
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/components/ui/toggle.cjs +1 -1
- package/dist/components/ui/toggle.cjs.map +1 -1
- package/dist/components/ui/toggle.js +12 -12
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/hooks/use-element-size.cjs +2 -0
- package/dist/hooks/use-element-size.cjs.map +1 -0
- package/dist/hooks/use-element-size.js +21 -0
- package/dist/hooks/use-element-size.js.map +1 -0
- package/dist/hooks/use-plotly-theme.cjs +1 -1
- package/dist/hooks/use-plotly-theme.cjs.map +1 -1
- package/dist/hooks/use-plotly-theme.js +4 -2
- package/dist/hooks/use-plotly-theme.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +254 -77
- package/dist/index.js +587 -581
- package/dist/index.js.map +1 -1
- package/dist/index.tailwind.css +1 -1
- package/dist/utils/colors.cjs +1 -1
- package/dist/utils/colors.cjs.map +1 -1
- package/dist/utils/colors.js +38 -93
- package/dist/utils/colors.js.map +1 -1
- package/package.json +18 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chromatogram.js","sources":["../../../../src/components/charts/Chromatogram/Chromatogram.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport \"./Chromatogram.scss\";\n\n/** Height offset for the plot area in pixels */\nconst PLOT_HEIGHT_OFFSET = 75;\n/** Scale factor for y-axis range to add padding above max value */\nconst Y_AXIS_PADDING_FACTOR = 1.05;\n\ninterface PeakData {\n position: number;\n base?: string;\n peakA: number;\n peakT: number;\n peakG: number;\n peakC: number;\n}\n\ninterface ChromatogramProps {\n data?: PeakData[];\n width?: number;\n height?: number;\n positionInterval?: number;\n colorA?: string;\n colorT?: string;\n colorG?: string;\n colorC?: string;\n}\n\nconst determineBase = (item: PeakData): string => {\n const peakValues = {\n A: item.peakA,\n T: item.peakT,\n G: item.peakG,\n C: item.peakC,\n };\n\n const values = Object.values(peakValues);\n const allEqual = values.every((val) => val === values[0]);\n\n if (allEqual) {\n return \"\";\n }\n\n let highestBase = \"\";\n let highestValue = 0;\n\n Object.entries(peakValues).forEach(([base, value]) => {\n if (value > highestValue) {\n highestBase = base;\n highestValue = value;\n }\n });\n\n return highestBase;\n};\n\nconst Chromatogram: React.FC<ChromatogramProps> = ({\n data = [],\n width = 900,\n height = 600,\n positionInterval = 10,\n colorA = \"#2D9CDB\",\n colorT = \"#A1C63C\",\n colorG = \"#FF5C64\",\n colorC = \"#FFA62E\",\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n\n const positions = useMemo(() => data.map((item) => item.position), [data]);\n const sequence = useMemo(() => data.map((item) => determineBase(item)), [data]);\n const peakA = useMemo(() => data.map((item) => item.peakA), [data]);\n const peakT = useMemo(() => data.map((item) => item.peakT), [data]);\n const peakG = useMemo(() => data.map((item) => item.peakG), [data]);\n const peakC = useMemo(() => data.map((item) => item.peakC), [data]);\n\n const aTrace = useMemo(\n () => ({\n x: positions,\n y: peakA,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: \"A\",\n line: { color: colorA, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakA, colorA],\n );\n\n const tTrace = useMemo(\n () => ({\n x: positions,\n y: peakT,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: \"T\",\n line: { color: colorT, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakT, colorT],\n );\n\n const gTrace = useMemo(\n () => ({\n x: positions,\n y: peakG,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: \"G\",\n line: { color: colorG, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakG, colorG],\n );\n\n const cTrace = useMemo(\n () => ({\n x: positions,\n y: peakC,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n name: \"C\",\n line: { color: colorC, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakC, colorC],\n );\n\n const maxValue = useMemo(\n () => Math.max(...peakA, ...peakT, ...peakG, ...peakC),\n [peakA, peakT, peakG, peakC],\n );\n\n useEffect(() => {\n if (!plotRef.current || data.length === 0) return;\n\n const plotData = [aTrace, tTrace, gTrace, cTrace];\n\n const layout: Partial<Plotly.Layout> = {\n width: width,\n height: height - PLOT_HEIGHT_OFFSET,\n margin: { l: 0, r: 0, b: 20, t: 10, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n showlegend: false,\n xaxis: {\n showgrid: false,\n zeroline: false,\n showticklabels: false,\n showline: false,\n range: [Math.min(...positions), Math.max(...positions)],\n fixedrange: true,\n },\n yaxis: {\n showgrid: false,\n zeroline: false,\n showticklabels: false,\n showline: false,\n range: [0, maxValue * Y_AXIS_PADDING_FACTOR],\n fixedrange: true,\n },\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n fillFrame: true,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [data, width, height, aTrace, tTrace, gTrace, cTrace, maxValue, positions, theme]);\n\n if (data.length === 0) {\n return <div className=\"chart-container\">No data available</div>;\n }\n\n const renderSequence = () => {\n const renderSequenceLetters = () => {\n const minPosition = Math.min(...positions);\n const maxPosition = Math.max(...positions);\n const chartWidth = width;\n\n return (\n <div className=\"sequence-letters-container\">\n {sequence.map((base, index) => {\n const position = positions[index];\n const color =\n base === \"A\"\n ? colorA\n : base === \"T\"\n ? colorT\n : base === \"G\"\n ? colorG\n : base === \"C\"\n ? colorC\n : theme.textColor;\n\n const percentage =\n (position - minPosition) / (maxPosition - minPosition);\n const leftPosition = percentage * chartWidth;\n\n return (\n <span\n key={`base-${index}`}\n className=\"sequence-letter\"\n style={{\n left: `${leftPosition}px`,\n color,\n }}\n >\n {base}\n </span>\n );\n })}\n </div>\n );\n };\n\n const renderPositionNumbers = () => {\n const minPosition = Math.min(...positions);\n const maxPosition = Math.max(...positions);\n const chartWidth = width;\n\n const startPos =\n Math.ceil(minPosition / positionInterval) * positionInterval;\n\n const regularPositionLabels: Array<{ position: number; label: string }> =\n [];\n\n for (let pos = startPos; pos <= maxPosition; pos += positionInterval) {\n regularPositionLabels.push({\n position: pos,\n label: pos.toString(),\n });\n }\n\n return (\n <div className=\"position-numbers-container\">\n {regularPositionLabels.map((label) => {\n const percentage =\n (label.position - minPosition) / (maxPosition - minPosition);\n const leftPosition = percentage * chartWidth;\n\n return (\n <span\n key={`pos-${label.position}`}\n className=\"position-number\"\n style={{\n left: `${leftPosition}px`,\n }}\n >\n {label.label}\n </span>\n );\n })}\n </div>\n );\n };\n\n return (\n <div className=\"sequence-header\">\n {renderSequenceLetters()}\n {renderPositionNumbers()}\n </div>\n );\n };\n\n return (\n <div className=\"chromatogram-container\" style={{ width, height }}>\n {renderSequence()}\n <div className=\"chromatogram-chart\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n </div>\n </div>\n );\n};\n\nexport { Chromatogram };\nexport type { PeakData, ChromatogramProps };\n"],"names":["PLOT_HEIGHT_OFFSET","Y_AXIS_PADDING_FACTOR","determineBase","item","peakValues","values","val","highestBase","highestValue","base","value","Chromatogram","data","width","height","positionInterval","colorA","colorT","colorG","colorC","plotRef","useRef","theme","usePlotlyTheme","positions","useMemo","sequence","peakA","peakT","peakG","peakC","aTrace","tTrace","gTrace","cTrace","maxValue","useEffect","plotData","layout","config","Plotly","plotElement","jsx","jsxs","renderSequenceLetters","minPosition","maxPosition","chartWidth","index","position","color","leftPosition","renderPositionNumbers","startPos","regularPositionLabels","pos","label"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Chromatogram.js","sources":["../../../../src/components/charts/Chromatogram/Chromatogram.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { CHART_COLORS } from \"@/utils/colors\";\nimport \"./Chromatogram.scss\";\n\n/** Height offset for the plot area in pixels */\nconst PLOT_HEIGHT_OFFSET = 75;\n/** Scale factor for y-axis range to add padding above max value */\nconst Y_AXIS_PADDING_FACTOR = 1.05;\n\ninterface PeakData {\n position: number;\n base?: string;\n peakA: number;\n peakT: number;\n peakG: number;\n peakC: number;\n}\n\ninterface ChromatogramProps {\n data?: PeakData[];\n width?: number;\n height?: number;\n positionInterval?: number;\n colorA?: string;\n colorT?: string;\n colorG?: string;\n colorC?: string;\n}\n\nconst determineBase = (item: PeakData): string => {\n const peakValues = {\n A: item.peakA,\n T: item.peakT,\n G: item.peakG,\n C: item.peakC,\n };\n\n const values = Object.values(peakValues);\n const allEqual = values.every((val) => val === values[0]);\n\n if (allEqual) {\n return \"\";\n }\n\n let highestBase = \"\";\n let highestValue = 0;\n\n Object.entries(peakValues).forEach(([base, value]) => {\n if (value > highestValue) {\n highestBase = base;\n highestValue = value;\n }\n });\n\n return highestBase;\n};\n\nconst Chromatogram: React.FC<ChromatogramProps> = ({\n data = [],\n width = 900,\n height = 600,\n positionInterval = 10,\n colorA = CHART_COLORS[0], // blue\n colorT = CHART_COLORS[2], // teal/green\n colorG = CHART_COLORS[3], // red\n colorC = CHART_COLORS[1], // orange\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({\n xLabel: \"Position\",\n yLabel: \"Intensity\",\n });\n\n const positions = useMemo(() => data.map((item) => item.position), [data]);\n const sequence = useMemo(() => data.map((item) => determineBase(item)), [data]);\n const peakA = useMemo(() => data.map((item) => item.peakA), [data]);\n const peakT = useMemo(() => data.map((item) => item.peakT), [data]);\n const peakG = useMemo(() => data.map((item) => item.peakG), [data]);\n const peakC = useMemo(() => data.map((item) => item.peakC), [data]);\n\n const aTrace = useMemo(\n () => ({\n x: positions,\n y: peakA,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n hoverinfo: \"none\" as const,\n name: \"A\",\n line: { color: colorA, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakA, colorA],\n );\n\n const tTrace = useMemo(\n () => ({\n x: positions,\n y: peakT,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n hoverinfo: \"none\" as const,\n name: \"T\",\n line: { color: colorT, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakT, colorT],\n );\n\n const gTrace = useMemo(\n () => ({\n x: positions,\n y: peakG,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n hoverinfo: \"none\" as const,\n name: \"G\",\n line: { color: colorG, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakG, colorG],\n );\n\n const cTrace = useMemo(\n () => ({\n x: positions,\n y: peakC,\n type: \"scatter\" as const,\n mode: \"lines\" as const,\n hoverinfo: \"none\" as const,\n name: \"C\",\n line: { color: colorC, width: 2, shape: \"spline\" as const },\n }),\n [positions, peakC, colorC],\n );\n\n const maxValue = useMemo(\n () => Math.max(...peakA, ...peakT, ...peakG, ...peakC),\n [peakA, peakT, peakG, peakC],\n );\n\n useEffect(() => {\n if (!plotRef.current || data.length === 0) return;\n\n const plotData = [aTrace, tTrace, gTrace, cTrace];\n\n const layout: Partial<Plotly.Layout> = {\n width: width,\n height: height - PLOT_HEIGHT_OFFSET,\n margin: { l: 0, r: 0, b: 20, t: 10, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n showlegend: false,\n xaxis: {\n showgrid: false,\n zeroline: false,\n showticklabels: false,\n showline: false,\n range: [Math.min(...positions), Math.max(...positions)],\n fixedrange: true,\n },\n yaxis: {\n showgrid: false,\n zeroline: false,\n showticklabels: false,\n showline: false,\n range: [0, maxValue * Y_AXIS_PADDING_FACTOR],\n fixedrange: true,\n },\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n fillFrame: true,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [data, width, height, aTrace, tTrace, gTrace, cTrace, maxValue, positions, theme, bindTooltip]);\n\n if (data.length === 0) {\n return <div className=\"chart-container\">No data available</div>;\n }\n\n const renderSequence = () => {\n const renderSequenceLetters = () => {\n const minPosition = Math.min(...positions);\n const maxPosition = Math.max(...positions);\n const chartWidth = width;\n\n return (\n <div className=\"sequence-letters-container\">\n {sequence.map((base, index) => {\n const position = positions[index];\n const color =\n base === \"A\"\n ? colorA\n : base === \"T\"\n ? colorT\n : base === \"G\"\n ? colorG\n : base === \"C\"\n ? colorC\n : theme.textColor;\n\n const percentage =\n (position - minPosition) / (maxPosition - minPosition);\n const leftPosition = percentage * chartWidth;\n\n return (\n <span\n key={`base-${index}`}\n className=\"sequence-letter\"\n style={{\n left: `${leftPosition}px`,\n color,\n }}\n >\n {base}\n </span>\n );\n })}\n </div>\n );\n };\n\n const renderPositionNumbers = () => {\n const minPosition = Math.min(...positions);\n const maxPosition = Math.max(...positions);\n const chartWidth = width;\n\n const startPos =\n Math.ceil(minPosition / positionInterval) * positionInterval;\n\n const regularPositionLabels: Array<{ position: number; label: string }> =\n [];\n\n for (let pos = startPos; pos <= maxPosition; pos += positionInterval) {\n regularPositionLabels.push({\n position: pos,\n label: pos.toString(),\n });\n }\n\n return (\n <div className=\"position-numbers-container\">\n {regularPositionLabels.map((label) => {\n const percentage =\n (label.position - minPosition) / (maxPosition - minPosition);\n const leftPosition = percentage * chartWidth;\n\n return (\n <span\n key={`pos-${label.position}`}\n className=\"position-number\"\n style={{\n left: `${leftPosition}px`,\n }}\n >\n {label.label}\n </span>\n );\n })}\n </div>\n );\n };\n\n return (\n <div className=\"sequence-header\">\n {renderSequenceLetters()}\n {renderPositionNumbers()}\n </div>\n );\n };\n\n return (\n <div className=\"chromatogram-container relative\" style={{ width, height }}>\n {renderSequence()}\n <div className=\"chromatogram-chart\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n </div>\n {tooltipElement}\n </div>\n );\n};\n\nexport { Chromatogram };\nexport type { PeakData, ChromatogramProps };\n"],"names":["PLOT_HEIGHT_OFFSET","Y_AXIS_PADDING_FACTOR","determineBase","item","peakValues","values","val","highestBase","highestValue","base","value","Chromatogram","data","width","height","positionInterval","colorA","CHART_COLORS","colorT","colorG","colorC","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","positions","useMemo","sequence","peakA","peakT","peakG","peakC","aTrace","tTrace","gTrace","cTrace","maxValue","useEffect","plotData","layout","config","Plotly","plotElement","jsx","jsxs","renderSequenceLetters","minPosition","maxPosition","chartWidth","index","position","color","leftPosition","renderPositionNumbers","startPos","regularPositionLabels","pos","label"],"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 Y=require("react/jsx-runtime"),j=require("plotly.js-dist"),u=require("react"),K=require("../../../utils/colors.cjs"),Q=require("../ChartTooltip/ChartTooltip.cjs"),D=require("./annotations.cjs"),V=require("./boundaryMarkers.cjs"),s=require("./constants.cjs"),T=require("./dataProcessing.cjs"),Z=require("./peakDetection.cjs"),X=require("../../../hooks/use-plotly-theme.cjs"),w=[],$=({series:a,width:h=900,height:A=500,title:f,xAxisTitle:M="Retention Time (min)",yAxisTitle:k="Signal (mAU)",annotations:d=w,xRange:O,yRange:I,showLegend:P=!0,showGridX:v=!0,showGridY:G=!0,showMarkers:R=!1,markerSize:q=4,showCrosshairs:p=!1,baselineCorrection:N="none",baselineWindowSize:L=50,peakDetectionOptions:l,showPeakAreas:b=!1,boundaryMarkers:z="none",annotationOverlapThreshold:B=.4,showExportButton:C=!0})=>{const m=l!==void 0,E=u.useRef(null),e=X.usePlotlyTheme(),{bindTooltip:H,tooltipElement:W}=Q.useChartTooltip({xLabel:M,yLabel:k}),n=u.useMemo(()=>a.map(o=>{const r=T.validateSeriesData(o.x,o.y);return{...o,x:r.x,y:T.applyBaselineCorrection(r.y,N,L)}}),[a,N,L]),x=u.useMemo(()=>{if(d.length===0||n.length===0)return d;const{x:o,y:r}=n[0];return T.processUserAnnotations(d,o,r)},[d,n]),_=u.useMemo(()=>{const o=[];return m&&l&&n.forEach((r,c)=>{const g=Z.detectPeaks(r.x,r.y,l);g.length>0&&o.push({peaks:g,seriesIndex:c})}),o},[n,m,l]);return u.useEffect(()=>{const o=E.current;if(!o||a.length===0)return;const r=n.map((t,i)=>{const y=K.seriesColor(i,t.color),S={x:t.x,y:t.y,type:"scatter",mode:R?"lines+markers":"lines",name:t.name,line:{color:y,width:1.5},hoverinfo:"none"};return R&&(S.marker={size:q,color:y}),S});if(z!=="none"){const t=T.collectPeaksWithBoundaryData(_,x,n);if(t.length>0){const i=V.createBoundaryMarkerTraces(t);r.push(...i)}}const c=[];x.forEach(t=>{c.push({peak:t,seriesIndex:-1})}),b&&m&&_.forEach(({peaks:t,seriesIndex:i})=>{t.forEach(y=>{c.push({peak:y,seriesIndex:i})})});const g=D.groupOverlappingPeaks(c,B),U=[];for(const t of g)U.push(...D.createGroupAnnotations(t));const F={title:f?{text:f,font:{size:20,family:"Inter, sans-serif",color:e.textColor}}:void 0,width:h,height:A,margin:{l:s.CHROMATOGRAM_LAYOUT.MARGIN_LEFT,r:s.CHROMATOGRAM_LAYOUT.MARGIN_RIGHT,b:s.CHROMATOGRAM_LAYOUT.MARGIN_BOTTOM,t:f?s.CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE:s.CHROMATOGRAM_LAYOUT.MARGIN_TOP_NO_TITLE,pad:s.CHROMATOGRAM_LAYOUT.MARGIN_PAD},paper_bgcolor:e.paperBg,plot_bgcolor:e.plotBg,font:{family:"Inter, sans-serif"},hovermode:p?"x":"x unified",dragmode:"zoom",xaxis:{title:{text:M,font:{size:14,color:e.textSecondary,family:"Inter, sans-serif"},standoff:15},showgrid:v,gridcolor:e.gridColor,linecolor:e.lineColor,linewidth:1,range:O,autorange:!O,zeroline:!1,tickfont:{size:12,color:e.textColor,family:"Inter, sans-serif"},showspikes:p,spikemode:"across",spikesnap:"cursor",spikecolor:e.spikeColor,spikethickness:1,spikedash:"dot"},yaxis:{title:{text:k,font:{size:14,color:e.textSecondary,family:"Inter, sans-serif"},standoff:10},showgrid:G,gridcolor:e.gridColor,linecolor:e.lineColor,linewidth:1,range:I,autorange:!I,zeroline:!1,tickfont:{size:12,color:e.textColor,family:"Inter, sans-serif"},showspikes:p,spikemode:"across",spikesnap:"cursor",spikecolor:e.spikeColor,spikethickness:1,spikedash:"dot"},legend:{x:.5,y:-.15,xanchor:"center",yanchor:"top",orientation:"h",font:{size:12,color:e.textColor,family:"Inter, sans-serif"}},showlegend:P&&a.length>1,annotations:U},J={responsive:!0,displayModeBar:!0,displaylogo:!1,modeBarButtonsToRemove:["lasso2d","select2d",...C?[]:["toImage"]],...C&&{toImageButtonOptions:{format:"png",filename:"chromatogram",width:h,height:A}}};return j.newPlot(o,r,F,J),H(o),()=>{o&&j.purge(o)}},[n,_,a.length,h,A,f,M,k,x,O,I,P,v,G,R,q,p,m,l,b,z,B,C,e,H]),Y.jsxs("div",{className:"chromatogram-chart-container relative",children:[Y.jsx("div",{ref:E,style:{width:"100%",height:"100%"}}),W]})};exports.ChromatogramChart=$;
|
|
2
2
|
//# sourceMappingURL=ChromatogramChart.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChromatogramChart.cjs","sources":["../../../../src/components/charts/ChromatogramChart/ChromatogramChart.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\n\nimport { CHART_COLORS } from \"../../../utils/colors\";\n\n\nimport {\n groupOverlappingPeaks,\n createGroupAnnotations,\n} from \"./annotations\";\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT } from \"./constants\";\nimport {\n validateSeriesData,\n applyBaselineCorrection,\n buildHoverExtraContent,\n collectPeaksWithBoundaryData,\n processUserAnnotations,\n} from \"./dataProcessing\";\nimport { detectPeaks } from \"./peakDetection\";\n\nimport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n PeakWithMeta,\n} from \"./types\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\n\n// Re-export types for external use\nexport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n};\n\n\nconst ChromatogramChart: React.FC<ChromatogramChartProps> = ({\n series,\n width = 900,\n height = 500,\n title,\n xAxisTitle = \"Retention Time (min)\",\n yAxisTitle = \"Signal (mAU)\",\n annotations = [],\n xRange,\n yRange,\n showLegend = true,\n showGridX = true,\n showGridY = true,\n showMarkers = false,\n markerSize = 4,\n showCrosshairs = false,\n baselineCorrection = \"none\",\n baselineWindowSize = 50,\n peakDetectionOptions,\n showPeakAreas = false,\n boundaryMarkers = \"none\",\n annotationOverlapThreshold = 0.4,\n showExportButton = true,\n}) => {\n // Derive peak detection state from options\n const enablePeakDetection = peakDetectionOptions !== undefined;\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n\n // Memoize processed series with baseline correction\n const processedSeries = useMemo(() => {\n return series.map((s) => {\n const validated = validateSeriesData(s.x, s.y);\n return {\n ...s,\n x: validated.x,\n y: applyBaselineCorrection(validated.y, baselineCorrection, baselineWindowSize),\n };\n });\n }, [series, baselineCorrection, baselineWindowSize]);\n\n // Process user annotations to convert startX/endX to indices and compute areas\n const processedAnnotations = useMemo(() => {\n if (annotations.length === 0 || processedSeries.length === 0) {\n return annotations;\n }\n // Use first series data for index lookup (user annotations apply to first series)\n const { x, y } = processedSeries[0];\n return processUserAnnotations(annotations, x, y);\n }, [annotations, processedSeries]);\n\n // Memoize peak detection results\n const allDetectedPeaks = useMemo(() => {\n const peaks: { peaks: PeakAnnotation[]; seriesIndex: number }[] = [];\n if (enablePeakDetection && peakDetectionOptions) {\n processedSeries.forEach((s, index) => {\n const detected = detectPeaks(s.x, s.y, peakDetectionOptions);\n if (detected.length > 0) {\n peaks.push({ peaks: detected, seriesIndex: index });\n }\n });\n }\n return peaks;\n }, [processedSeries, enablePeakDetection, peakDetectionOptions]);\n\n useEffect(() => {\n const currentRef = plotRef.current;\n if (!currentRef || series.length === 0) return;\n\n // Build trace data with auto-assigned colors\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = s.color || CHART_COLORS[index % CHART_COLORS.length];\n const extraContent = buildHoverExtraContent(s.name, s.metadata);\n\n const trace: Plotly.Data = {\n x: s.x,\n y: s.y,\n type: \"scatter\" as const,\n mode: showMarkers ? \"lines+markers\" as const : \"lines\" as const,\n name: s.name,\n line: {\n color: traceColor,\n width: 1.5,\n },\n hovertemplate: `%{x:.2f} ${xAxisTitle}<br>%{y:.2f} ${yAxisTitle}<extra>${extraContent}</extra>`,\n };\n if (showMarkers) {\n trace.marker = {\n size: markerSize,\n color: traceColor,\n };\n }\n return trace;\n });\n\n // Add peak boundary markers if enabled\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(allDetectedPeaks, processedAnnotations, processedSeries);\n if (peaksWithData.length > 0) {\n const boundaryTraces = createBoundaryMarkerTraces(peaksWithData);\n plotData.push(...boundaryTraces);\n }\n }\n\n // Collect all peaks for unified staggering logic\n const allPeaksWithMeta: PeakWithMeta[] = [];\n\n // Add user-defined annotations (seriesIndex -1 indicates user-defined)\n processedAnnotations.forEach((ann) => {\n allPeaksWithMeta.push({ peak: ann, seriesIndex: -1 });\n });\n\n // Add auto-detected peaks if enabled\n if (showPeakAreas && enablePeakDetection) {\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n peaks.forEach((peak) => {\n allPeaksWithMeta.push({ peak, seriesIndex });\n });\n });\n }\n\n // Group all overlapping peaks and create annotations with staggering\n const groups = groupOverlappingPeaks(allPeaksWithMeta, annotationOverlapThreshold);\n const plotlyAnnotations: Partial<Plotly.Annotations>[] = [];\n\n for (const group of groups) {\n plotlyAnnotations.push(...createGroupAnnotations(group));\n }\n\n const layout: Partial<Plotly.Layout> = {\n title: title\n ? {\n text: title,\n font: {\n size: 20,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n }\n : undefined,\n width,\n height,\n margin: {\n l: CHROMATOGRAM_LAYOUT.MARGIN_LEFT,\n r: CHROMATOGRAM_LAYOUT.MARGIN_RIGHT,\n b: CHROMATOGRAM_LAYOUT.MARGIN_BOTTOM,\n t: title ? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE : CHROMATOGRAM_LAYOUT.MARGIN_TOP_NO_TITLE,\n pad: CHROMATOGRAM_LAYOUT.MARGIN_PAD,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: { family: \"Inter, sans-serif\" },\n hovermode: showCrosshairs ? \"x\" as const : \"x unified\" as const,\n dragmode: \"zoom\" as const,\n xaxis: {\n title: {\n text: xAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 15,\n },\n showgrid: showGridX,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: xRange,\n autorange: !xRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n yaxis: {\n title: {\n text: yAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 10,\n },\n showgrid: showGridY,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: yRange,\n autorange: !yRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n legend: {\n x: 0.5,\n y: -0.15,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n },\n showlegend: showLegend && series.length > 1,\n annotations: plotlyAnnotations,\n };\n\n const config: Partial<Plotly.Config> = {\n responsive: true,\n displayModeBar: true,\n displaylogo: false,\n modeBarButtonsToRemove: [\n \"lasso2d\",\n \"select2d\",\n ...(showExportButton ? [] : [\"toImage\"] as Plotly.ModeBarDefaultButtons[]),\n ] as Plotly.ModeBarDefaultButtons[],\n ...(showExportButton && {\n toImageButtonOptions: {\n format: \"png\",\n filename: \"chromatogram\",\n width: width,\n height: height,\n },\n }),\n };\n\n Plotly.newPlot(currentRef, plotData, layout, config);\n\n return () => {\n if (currentRef) {\n Plotly.purge(currentRef);\n }\n };\n }, [\n processedSeries, allDetectedPeaks, series.length, width, height, title, xAxisTitle, yAxisTitle,\n processedAnnotations, xRange, yRange, showLegend, showGridX, showGridY, showMarkers, markerSize,\n showCrosshairs, enablePeakDetection, peakDetectionOptions, showPeakAreas, boundaryMarkers,\n annotationOverlapThreshold, showExportButton, theme,\n ]);\n\n return (\n <div className=\"chromatogram-chart-container\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n </div>\n );\n};\n\nexport { ChromatogramChart };\n\n"],"names":["ChromatogramChart","series","width","height","title","xAxisTitle","yAxisTitle","annotations","xRange","yRange","showLegend","showGridX","showGridY","showMarkers","markerSize","showCrosshairs","baselineCorrection","baselineWindowSize","peakDetectionOptions","showPeakAreas","boundaryMarkers","annotationOverlapThreshold","showExportButton","enablePeakDetection","plotRef","useRef","theme","usePlotlyTheme","processedSeries","useMemo","s","validated","validateSeriesData","applyBaselineCorrection","processedAnnotations","x","y","processUserAnnotations","allDetectedPeaks","peaks","index","detected","detectPeaks","useEffect","currentRef","plotData","traceColor","CHART_COLORS","extraContent","buildHoverExtraContent","trace","peaksWithData","collectPeaksWithBoundaryData","boundaryTraces","createBoundaryMarkerTraces","allPeaksWithMeta","ann","seriesIndex","peak","groups","groupOverlappingPeaks","plotlyAnnotations","group","createGroupAnnotations","layout","CHROMATOGRAM_LAYOUT","config","Plotly","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChromatogramChart.cjs","sources":["../../../../src/components/charts/ChromatogramChart/ChromatogramChart.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\n\nimport { seriesColor } from \"../../../utils/colors\";\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport {\n groupOverlappingPeaks,\n createGroupAnnotations,\n} from \"./annotations\";\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT } from \"./constants\";\nimport {\n validateSeriesData,\n applyBaselineCorrection,\n collectPeaksWithBoundaryData,\n processUserAnnotations,\n} from \"./dataProcessing\";\nimport { detectPeaks } from \"./peakDetection\";\n\nimport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n PeakWithMeta,\n} from \"./types\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\n\n// Re-export types for external use\nexport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n};\n\n\n// Stable default so the no-annotations case keeps a constant identity; an\n// inline `[]` default would change every render, re-running the plot effect\n// (and tearing down the hover tooltip) on each re-render.\nconst EMPTY_ANNOTATIONS: PeakAnnotation[] = [];\n\nconst ChromatogramChart: React.FC<ChromatogramChartProps> = ({\n series,\n width = 900,\n height = 500,\n title,\n xAxisTitle = \"Retention Time (min)\",\n yAxisTitle = \"Signal (mAU)\",\n annotations = EMPTY_ANNOTATIONS,\n xRange,\n yRange,\n showLegend = true,\n showGridX = true,\n showGridY = true,\n showMarkers = false,\n markerSize = 4,\n showCrosshairs = false,\n baselineCorrection = \"none\",\n baselineWindowSize = 50,\n peakDetectionOptions,\n showPeakAreas = false,\n boundaryMarkers = \"none\",\n annotationOverlapThreshold = 0.4,\n showExportButton = true,\n}) => {\n // Derive peak detection state from options\n const enablePeakDetection = peakDetectionOptions !== undefined;\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({\n xLabel: xAxisTitle,\n yLabel: yAxisTitle,\n });\n\n // Memoize processed series with baseline correction\n const processedSeries = useMemo(() => {\n return series.map((s) => {\n const validated = validateSeriesData(s.x, s.y);\n return {\n ...s,\n x: validated.x,\n y: applyBaselineCorrection(validated.y, baselineCorrection, baselineWindowSize),\n };\n });\n }, [series, baselineCorrection, baselineWindowSize]);\n\n // Process user annotations to convert startX/endX to indices and compute areas\n const processedAnnotations = useMemo(() => {\n if (annotations.length === 0 || processedSeries.length === 0) {\n return annotations;\n }\n // Use first series data for index lookup (user annotations apply to first series)\n const { x, y } = processedSeries[0];\n return processUserAnnotations(annotations, x, y);\n }, [annotations, processedSeries]);\n\n // Memoize peak detection results\n const allDetectedPeaks = useMemo(() => {\n const peaks: { peaks: PeakAnnotation[]; seriesIndex: number }[] = [];\n if (enablePeakDetection && peakDetectionOptions) {\n processedSeries.forEach((s, index) => {\n const detected = detectPeaks(s.x, s.y, peakDetectionOptions);\n if (detected.length > 0) {\n peaks.push({ peaks: detected, seriesIndex: index });\n }\n });\n }\n return peaks;\n }, [processedSeries, enablePeakDetection, peakDetectionOptions]);\n\n useEffect(() => {\n const currentRef = plotRef.current;\n if (!currentRef || series.length === 0) return;\n\n // Build trace data with auto-assigned colors\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = seriesColor(index, s.color);\n\n const trace: Plotly.Data = {\n x: s.x,\n y: s.y,\n type: \"scatter\" as const,\n mode: showMarkers ? \"lines+markers\" as const : \"lines\" as const,\n name: s.name,\n line: {\n color: traceColor,\n width: 1.5,\n },\n hoverinfo: \"none\" as const,\n };\n if (showMarkers) {\n trace.marker = {\n size: markerSize,\n color: traceColor,\n };\n }\n return trace;\n });\n\n // Add peak boundary markers if enabled\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(allDetectedPeaks, processedAnnotations, processedSeries);\n if (peaksWithData.length > 0) {\n const boundaryTraces = createBoundaryMarkerTraces(peaksWithData);\n plotData.push(...boundaryTraces);\n }\n }\n\n // Collect all peaks for unified staggering logic\n const allPeaksWithMeta: PeakWithMeta[] = [];\n\n // Add user-defined annotations (seriesIndex -1 indicates user-defined)\n processedAnnotations.forEach((ann) => {\n allPeaksWithMeta.push({ peak: ann, seriesIndex: -1 });\n });\n\n // Add auto-detected peaks if enabled\n if (showPeakAreas && enablePeakDetection) {\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n peaks.forEach((peak) => {\n allPeaksWithMeta.push({ peak, seriesIndex });\n });\n });\n }\n\n // Group all overlapping peaks and create annotations with staggering\n const groups = groupOverlappingPeaks(allPeaksWithMeta, annotationOverlapThreshold);\n const plotlyAnnotations: Partial<Plotly.Annotations>[] = [];\n\n for (const group of groups) {\n plotlyAnnotations.push(...createGroupAnnotations(group));\n }\n\n const layout: Partial<Plotly.Layout> = {\n title: title\n ? {\n text: title,\n font: {\n size: 20,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n }\n : undefined,\n width,\n height,\n margin: {\n l: CHROMATOGRAM_LAYOUT.MARGIN_LEFT,\n r: CHROMATOGRAM_LAYOUT.MARGIN_RIGHT,\n b: CHROMATOGRAM_LAYOUT.MARGIN_BOTTOM,\n t: title ? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE : CHROMATOGRAM_LAYOUT.MARGIN_TOP_NO_TITLE,\n pad: CHROMATOGRAM_LAYOUT.MARGIN_PAD,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: { family: \"Inter, sans-serif\" },\n hovermode: showCrosshairs ? \"x\" as const : \"x unified\" as const,\n dragmode: \"zoom\" as const,\n xaxis: {\n title: {\n text: xAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 15,\n },\n showgrid: showGridX,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: xRange,\n autorange: !xRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n yaxis: {\n title: {\n text: yAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 10,\n },\n showgrid: showGridY,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: yRange,\n autorange: !yRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n legend: {\n x: 0.5,\n y: -0.15,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n },\n showlegend: showLegend && series.length > 1,\n annotations: plotlyAnnotations,\n };\n\n const config: Partial<Plotly.Config> = {\n responsive: true,\n displayModeBar: true,\n displaylogo: false,\n modeBarButtonsToRemove: [\n \"lasso2d\",\n \"select2d\",\n ...(showExportButton ? [] : [\"toImage\"] as Plotly.ModeBarDefaultButtons[]),\n ] as Plotly.ModeBarDefaultButtons[],\n ...(showExportButton && {\n toImageButtonOptions: {\n format: \"png\",\n filename: \"chromatogram\",\n width: width,\n height: height,\n },\n }),\n };\n\n Plotly.newPlot(currentRef, plotData, layout, config);\n bindTooltip(currentRef);\n\n return () => {\n if (currentRef) {\n Plotly.purge(currentRef);\n }\n };\n }, [\n processedSeries, allDetectedPeaks, series.length, width, height, title, xAxisTitle, yAxisTitle,\n processedAnnotations, xRange, yRange, showLegend, showGridX, showGridY, showMarkers, markerSize,\n showCrosshairs, enablePeakDetection, peakDetectionOptions, showPeakAreas, boundaryMarkers,\n annotationOverlapThreshold, showExportButton, theme, bindTooltip,\n ]);\n\n return (\n <div className=\"chromatogram-chart-container relative\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { ChromatogramChart };\n\n"],"names":["EMPTY_ANNOTATIONS","ChromatogramChart","series","width","height","title","xAxisTitle","yAxisTitle","annotations","xRange","yRange","showLegend","showGridX","showGridY","showMarkers","markerSize","showCrosshairs","baselineCorrection","baselineWindowSize","peakDetectionOptions","showPeakAreas","boundaryMarkers","annotationOverlapThreshold","showExportButton","enablePeakDetection","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","processedSeries","useMemo","s","validated","validateSeriesData","applyBaselineCorrection","processedAnnotations","x","y","processUserAnnotations","allDetectedPeaks","peaks","index","detected","detectPeaks","useEffect","currentRef","plotData","traceColor","seriesColor","trace","peaksWithData","collectPeaksWithBoundaryData","boundaryTraces","createBoundaryMarkerTraces","allPeaksWithMeta","ann","seriesIndex","peak","groups","groupOverlappingPeaks","plotlyAnnotations","group","createGroupAnnotations","layout","CHROMATOGRAM_LAYOUT","config","Plotly","jsxs","jsx"],"mappings":"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"}
|
|
@@ -1,157 +1,161 @@
|
|
|
1
|
-
import { jsx as
|
|
1
|
+
import { jsxs as Y, jsx as q } from "react/jsx-runtime";
|
|
2
2
|
import U from "plotly.js-dist";
|
|
3
|
-
import { useRef as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import { useRef as J, useMemo as v, useEffect as K } from "react";
|
|
4
|
+
import { seriesColor as Q } from "../../../utils/colors.js";
|
|
5
|
+
import { useChartTooltip as V } from "../ChartTooltip/ChartTooltip.js";
|
|
6
|
+
import { groupOverlappingPeaks as Z, createGroupAnnotations as $ } from "./annotations.js";
|
|
7
|
+
import { createBoundaryMarkerTraces as X } from "./boundaryMarkers.js";
|
|
8
|
+
import { CHROMATOGRAM_LAYOUT as s } from "./constants.js";
|
|
9
|
+
import { validateSeriesData as w, applyBaselineCorrection as oo, processUserAnnotations as eo, collectPeaksWithBoundaryData as ro } from "./dataProcessing.js";
|
|
10
|
+
import { detectPeaks as to } from "./peakDetection.js";
|
|
11
|
+
import { usePlotlyTheme as no } from "../../../hooks/use-plotly-theme.js";
|
|
12
|
+
const so = [], ko = ({
|
|
13
|
+
series: a,
|
|
13
14
|
width: y = 900,
|
|
14
15
|
height: h = 500,
|
|
15
16
|
title: f,
|
|
16
17
|
xAxisTitle: k = "Retention Time (min)",
|
|
17
|
-
yAxisTitle:
|
|
18
|
-
annotations: m =
|
|
19
|
-
xRange:
|
|
20
|
-
yRange:
|
|
21
|
-
showLegend:
|
|
22
|
-
showGridX:
|
|
23
|
-
showGridY:
|
|
24
|
-
showMarkers:
|
|
25
|
-
markerSize:
|
|
18
|
+
yAxisTitle: I = "Signal (mAU)",
|
|
19
|
+
annotations: m = so,
|
|
20
|
+
xRange: x,
|
|
21
|
+
yRange: T,
|
|
22
|
+
showLegend: M = !0,
|
|
23
|
+
showGridX: N = !0,
|
|
24
|
+
showGridY: R = !0,
|
|
25
|
+
showMarkers: _ = !1,
|
|
26
|
+
markerSize: z = 4,
|
|
26
27
|
showCrosshairs: p = !1,
|
|
27
|
-
baselineCorrection:
|
|
28
|
+
baselineCorrection: B = "none",
|
|
28
29
|
baselineWindowSize: E = 50,
|
|
29
30
|
peakDetectionOptions: l,
|
|
30
31
|
showPeakAreas: G = !1,
|
|
31
32
|
boundaryMarkers: O = "none",
|
|
32
|
-
annotationOverlapThreshold:
|
|
33
|
-
showExportButton:
|
|
33
|
+
annotationOverlapThreshold: b = 0.4,
|
|
34
|
+
showExportButton: C = !0
|
|
34
35
|
}) => {
|
|
35
|
-
const d = l !== void 0,
|
|
36
|
-
|
|
36
|
+
const d = l !== void 0, L = J(null), o = no(), { bindTooltip: S, tooltipElement: W } = V({
|
|
37
|
+
xLabel: k,
|
|
38
|
+
yLabel: I
|
|
39
|
+
}), n = v(() => a.map((e) => {
|
|
40
|
+
const t = w(e.x, e.y);
|
|
37
41
|
return {
|
|
38
|
-
...
|
|
39
|
-
x:
|
|
40
|
-
y:
|
|
42
|
+
...e,
|
|
43
|
+
x: t.x,
|
|
44
|
+
y: oo(t.y, B, E)
|
|
41
45
|
};
|
|
42
|
-
}), [
|
|
46
|
+
}), [a, B, E]), A = v(() => {
|
|
43
47
|
if (m.length === 0 || n.length === 0)
|
|
44
48
|
return m;
|
|
45
|
-
const { x:
|
|
46
|
-
return
|
|
47
|
-
}, [m, n]),
|
|
48
|
-
const
|
|
49
|
-
return d && l && n.forEach((
|
|
50
|
-
const u =
|
|
51
|
-
u.length > 0 &&
|
|
52
|
-
}),
|
|
49
|
+
const { x: e, y: t } = n[0];
|
|
50
|
+
return eo(m, e, t);
|
|
51
|
+
}, [m, n]), P = v(() => {
|
|
52
|
+
const e = [];
|
|
53
|
+
return d && l && n.forEach((t, i) => {
|
|
54
|
+
const u = to(t.x, t.y, l);
|
|
55
|
+
u.length > 0 && e.push({ peaks: u, seriesIndex: i });
|
|
56
|
+
}), e;
|
|
53
57
|
}, [n, d, l]);
|
|
54
|
-
return
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
57
|
-
const
|
|
58
|
-
const g =
|
|
59
|
-
x:
|
|
60
|
-
y:
|
|
58
|
+
return K(() => {
|
|
59
|
+
const e = L.current;
|
|
60
|
+
if (!e || a.length === 0) return;
|
|
61
|
+
const t = n.map((r, c) => {
|
|
62
|
+
const g = Q(c, r.color), H = {
|
|
63
|
+
x: r.x,
|
|
64
|
+
y: r.y,
|
|
61
65
|
type: "scatter",
|
|
62
|
-
mode:
|
|
63
|
-
name:
|
|
66
|
+
mode: _ ? "lines+markers" : "lines",
|
|
67
|
+
name: r.name,
|
|
64
68
|
line: {
|
|
65
69
|
color: g,
|
|
66
70
|
width: 1.5
|
|
67
71
|
},
|
|
68
|
-
|
|
72
|
+
hoverinfo: "none"
|
|
69
73
|
};
|
|
70
|
-
return
|
|
71
|
-
size:
|
|
74
|
+
return _ && (H.marker = {
|
|
75
|
+
size: z,
|
|
72
76
|
color: g
|
|
73
|
-
}),
|
|
77
|
+
}), H;
|
|
74
78
|
});
|
|
75
79
|
if (O !== "none") {
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
const c =
|
|
79
|
-
|
|
80
|
+
const r = ro(P, A, n);
|
|
81
|
+
if (r.length > 0) {
|
|
82
|
+
const c = X(r);
|
|
83
|
+
t.push(...c);
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
const i = [];
|
|
83
|
-
|
|
84
|
-
i.push({ peak:
|
|
85
|
-
}), G && d &&
|
|
86
|
-
|
|
87
|
+
A.forEach((r) => {
|
|
88
|
+
i.push({ peak: r, seriesIndex: -1 });
|
|
89
|
+
}), G && d && P.forEach(({ peaks: r, seriesIndex: c }) => {
|
|
90
|
+
r.forEach((g) => {
|
|
87
91
|
i.push({ peak: g, seriesIndex: c });
|
|
88
92
|
});
|
|
89
93
|
});
|
|
90
|
-
const u =
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
+
const u = Z(i, b), D = [];
|
|
95
|
+
for (const r of u)
|
|
96
|
+
D.push(...$(r));
|
|
97
|
+
const j = {
|
|
94
98
|
title: f ? {
|
|
95
99
|
text: f,
|
|
96
100
|
font: {
|
|
97
101
|
size: 20,
|
|
98
102
|
family: "Inter, sans-serif",
|
|
99
|
-
color:
|
|
103
|
+
color: o.textColor
|
|
100
104
|
}
|
|
101
105
|
} : void 0,
|
|
102
106
|
width: y,
|
|
103
107
|
height: h,
|
|
104
108
|
margin: {
|
|
105
|
-
l:
|
|
106
|
-
r:
|
|
107
|
-
b:
|
|
108
|
-
t: f ?
|
|
109
|
-
pad:
|
|
109
|
+
l: s.MARGIN_LEFT,
|
|
110
|
+
r: s.MARGIN_RIGHT,
|
|
111
|
+
b: s.MARGIN_BOTTOM,
|
|
112
|
+
t: f ? s.MARGIN_TOP_WITH_TITLE : s.MARGIN_TOP_NO_TITLE,
|
|
113
|
+
pad: s.MARGIN_PAD
|
|
110
114
|
},
|
|
111
|
-
paper_bgcolor:
|
|
112
|
-
plot_bgcolor:
|
|
115
|
+
paper_bgcolor: o.paperBg,
|
|
116
|
+
plot_bgcolor: o.plotBg,
|
|
113
117
|
font: { family: "Inter, sans-serif" },
|
|
114
118
|
hovermode: p ? "x" : "x unified",
|
|
115
119
|
dragmode: "zoom",
|
|
116
120
|
xaxis: {
|
|
117
121
|
title: {
|
|
118
122
|
text: k,
|
|
119
|
-
font: { size: 14, color:
|
|
123
|
+
font: { size: 14, color: o.textSecondary, family: "Inter, sans-serif" },
|
|
120
124
|
standoff: 15
|
|
121
125
|
},
|
|
122
|
-
showgrid:
|
|
123
|
-
gridcolor:
|
|
124
|
-
linecolor:
|
|
126
|
+
showgrid: N,
|
|
127
|
+
gridcolor: o.gridColor,
|
|
128
|
+
linecolor: o.lineColor,
|
|
125
129
|
linewidth: 1,
|
|
126
|
-
range:
|
|
127
|
-
autorange: !
|
|
130
|
+
range: x,
|
|
131
|
+
autorange: !x,
|
|
128
132
|
zeroline: !1,
|
|
129
|
-
tickfont: { size: 12, color:
|
|
133
|
+
tickfont: { size: 12, color: o.textColor, family: "Inter, sans-serif" },
|
|
130
134
|
showspikes: p,
|
|
131
135
|
spikemode: "across",
|
|
132
136
|
spikesnap: "cursor",
|
|
133
|
-
spikecolor:
|
|
137
|
+
spikecolor: o.spikeColor,
|
|
134
138
|
spikethickness: 1,
|
|
135
139
|
spikedash: "dot"
|
|
136
140
|
},
|
|
137
141
|
yaxis: {
|
|
138
142
|
title: {
|
|
139
|
-
text:
|
|
140
|
-
font: { size: 14, color:
|
|
143
|
+
text: I,
|
|
144
|
+
font: { size: 14, color: o.textSecondary, family: "Inter, sans-serif" },
|
|
141
145
|
standoff: 10
|
|
142
146
|
},
|
|
143
|
-
showgrid:
|
|
144
|
-
gridcolor:
|
|
145
|
-
linecolor:
|
|
147
|
+
showgrid: R,
|
|
148
|
+
gridcolor: o.gridColor,
|
|
149
|
+
linecolor: o.lineColor,
|
|
146
150
|
linewidth: 1,
|
|
147
|
-
range:
|
|
148
|
-
autorange: !
|
|
151
|
+
range: T,
|
|
152
|
+
autorange: !T,
|
|
149
153
|
zeroline: !1,
|
|
150
|
-
tickfont: { size: 12, color:
|
|
154
|
+
tickfont: { size: 12, color: o.textColor, family: "Inter, sans-serif" },
|
|
151
155
|
showspikes: p,
|
|
152
156
|
spikemode: "across",
|
|
153
157
|
spikesnap: "cursor",
|
|
154
|
-
spikecolor:
|
|
158
|
+
spikecolor: o.spikeColor,
|
|
155
159
|
spikethickness: 1,
|
|
156
160
|
spikedash: "dot"
|
|
157
161
|
},
|
|
@@ -161,20 +165,20 @@ const de = ({
|
|
|
161
165
|
xanchor: "center",
|
|
162
166
|
yanchor: "top",
|
|
163
167
|
orientation: "h",
|
|
164
|
-
font: { size: 12, color:
|
|
168
|
+
font: { size: 12, color: o.textColor, family: "Inter, sans-serif" }
|
|
165
169
|
},
|
|
166
|
-
showlegend:
|
|
167
|
-
annotations:
|
|
168
|
-
},
|
|
170
|
+
showlegend: M && a.length > 1,
|
|
171
|
+
annotations: D
|
|
172
|
+
}, F = {
|
|
169
173
|
responsive: !0,
|
|
170
174
|
displayModeBar: !0,
|
|
171
175
|
displaylogo: !1,
|
|
172
176
|
modeBarButtonsToRemove: [
|
|
173
177
|
"lasso2d",
|
|
174
178
|
"select2d",
|
|
175
|
-
...
|
|
179
|
+
...C ? [] : ["toImage"]
|
|
176
180
|
],
|
|
177
|
-
...
|
|
181
|
+
...C && {
|
|
178
182
|
toImageButtonOptions: {
|
|
179
183
|
format: "png",
|
|
180
184
|
filename: "chromatogram",
|
|
@@ -183,37 +187,41 @@ const de = ({
|
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
};
|
|
186
|
-
return U.newPlot(t,
|
|
187
|
-
|
|
190
|
+
return U.newPlot(e, t, j, F), S(e), () => {
|
|
191
|
+
e && U.purge(e);
|
|
188
192
|
};
|
|
189
193
|
}, [
|
|
190
194
|
n,
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
P,
|
|
196
|
+
a.length,
|
|
193
197
|
y,
|
|
194
198
|
h,
|
|
195
199
|
f,
|
|
196
200
|
k,
|
|
197
|
-
x,
|
|
198
|
-
R,
|
|
199
201
|
I,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
A,
|
|
203
|
+
x,
|
|
204
|
+
T,
|
|
202
205
|
M,
|
|
206
|
+
N,
|
|
207
|
+
R,
|
|
208
|
+
_,
|
|
203
209
|
z,
|
|
204
|
-
T,
|
|
205
|
-
B,
|
|
206
210
|
p,
|
|
207
211
|
d,
|
|
208
212
|
l,
|
|
209
213
|
G,
|
|
210
214
|
O,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
b,
|
|
216
|
+
C,
|
|
217
|
+
o,
|
|
218
|
+
S
|
|
219
|
+
]), /* @__PURE__ */ Y("div", { className: "chromatogram-chart-container relative", children: [
|
|
220
|
+
/* @__PURE__ */ q("div", { ref: L, style: { width: "100%", height: "100%" } }),
|
|
221
|
+
W
|
|
222
|
+
] });
|
|
215
223
|
};
|
|
216
224
|
export {
|
|
217
|
-
|
|
225
|
+
ko as ChromatogramChart
|
|
218
226
|
};
|
|
219
227
|
//# sourceMappingURL=ChromatogramChart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChromatogramChart.js","sources":["../../../../src/components/charts/ChromatogramChart/ChromatogramChart.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\n\nimport { CHART_COLORS } from \"../../../utils/colors\";\n\n\nimport {\n groupOverlappingPeaks,\n createGroupAnnotations,\n} from \"./annotations\";\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT } from \"./constants\";\nimport {\n validateSeriesData,\n applyBaselineCorrection,\n buildHoverExtraContent,\n collectPeaksWithBoundaryData,\n processUserAnnotations,\n} from \"./dataProcessing\";\nimport { detectPeaks } from \"./peakDetection\";\n\nimport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n PeakWithMeta,\n} from \"./types\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\n\n// Re-export types for external use\nexport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n};\n\n\nconst ChromatogramChart: React.FC<ChromatogramChartProps> = ({\n series,\n width = 900,\n height = 500,\n title,\n xAxisTitle = \"Retention Time (min)\",\n yAxisTitle = \"Signal (mAU)\",\n annotations = [],\n xRange,\n yRange,\n showLegend = true,\n showGridX = true,\n showGridY = true,\n showMarkers = false,\n markerSize = 4,\n showCrosshairs = false,\n baselineCorrection = \"none\",\n baselineWindowSize = 50,\n peakDetectionOptions,\n showPeakAreas = false,\n boundaryMarkers = \"none\",\n annotationOverlapThreshold = 0.4,\n showExportButton = true,\n}) => {\n // Derive peak detection state from options\n const enablePeakDetection = peakDetectionOptions !== undefined;\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n\n // Memoize processed series with baseline correction\n const processedSeries = useMemo(() => {\n return series.map((s) => {\n const validated = validateSeriesData(s.x, s.y);\n return {\n ...s,\n x: validated.x,\n y: applyBaselineCorrection(validated.y, baselineCorrection, baselineWindowSize),\n };\n });\n }, [series, baselineCorrection, baselineWindowSize]);\n\n // Process user annotations to convert startX/endX to indices and compute areas\n const processedAnnotations = useMemo(() => {\n if (annotations.length === 0 || processedSeries.length === 0) {\n return annotations;\n }\n // Use first series data for index lookup (user annotations apply to first series)\n const { x, y } = processedSeries[0];\n return processUserAnnotations(annotations, x, y);\n }, [annotations, processedSeries]);\n\n // Memoize peak detection results\n const allDetectedPeaks = useMemo(() => {\n const peaks: { peaks: PeakAnnotation[]; seriesIndex: number }[] = [];\n if (enablePeakDetection && peakDetectionOptions) {\n processedSeries.forEach((s, index) => {\n const detected = detectPeaks(s.x, s.y, peakDetectionOptions);\n if (detected.length > 0) {\n peaks.push({ peaks: detected, seriesIndex: index });\n }\n });\n }\n return peaks;\n }, [processedSeries, enablePeakDetection, peakDetectionOptions]);\n\n useEffect(() => {\n const currentRef = plotRef.current;\n if (!currentRef || series.length === 0) return;\n\n // Build trace data with auto-assigned colors\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = s.color || CHART_COLORS[index % CHART_COLORS.length];\n const extraContent = buildHoverExtraContent(s.name, s.metadata);\n\n const trace: Plotly.Data = {\n x: s.x,\n y: s.y,\n type: \"scatter\" as const,\n mode: showMarkers ? \"lines+markers\" as const : \"lines\" as const,\n name: s.name,\n line: {\n color: traceColor,\n width: 1.5,\n },\n hovertemplate: `%{x:.2f} ${xAxisTitle}<br>%{y:.2f} ${yAxisTitle}<extra>${extraContent}</extra>`,\n };\n if (showMarkers) {\n trace.marker = {\n size: markerSize,\n color: traceColor,\n };\n }\n return trace;\n });\n\n // Add peak boundary markers if enabled\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(allDetectedPeaks, processedAnnotations, processedSeries);\n if (peaksWithData.length > 0) {\n const boundaryTraces = createBoundaryMarkerTraces(peaksWithData);\n plotData.push(...boundaryTraces);\n }\n }\n\n // Collect all peaks for unified staggering logic\n const allPeaksWithMeta: PeakWithMeta[] = [];\n\n // Add user-defined annotations (seriesIndex -1 indicates user-defined)\n processedAnnotations.forEach((ann) => {\n allPeaksWithMeta.push({ peak: ann, seriesIndex: -1 });\n });\n\n // Add auto-detected peaks if enabled\n if (showPeakAreas && enablePeakDetection) {\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n peaks.forEach((peak) => {\n allPeaksWithMeta.push({ peak, seriesIndex });\n });\n });\n }\n\n // Group all overlapping peaks and create annotations with staggering\n const groups = groupOverlappingPeaks(allPeaksWithMeta, annotationOverlapThreshold);\n const plotlyAnnotations: Partial<Plotly.Annotations>[] = [];\n\n for (const group of groups) {\n plotlyAnnotations.push(...createGroupAnnotations(group));\n }\n\n const layout: Partial<Plotly.Layout> = {\n title: title\n ? {\n text: title,\n font: {\n size: 20,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n }\n : undefined,\n width,\n height,\n margin: {\n l: CHROMATOGRAM_LAYOUT.MARGIN_LEFT,\n r: CHROMATOGRAM_LAYOUT.MARGIN_RIGHT,\n b: CHROMATOGRAM_LAYOUT.MARGIN_BOTTOM,\n t: title ? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE : CHROMATOGRAM_LAYOUT.MARGIN_TOP_NO_TITLE,\n pad: CHROMATOGRAM_LAYOUT.MARGIN_PAD,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: { family: \"Inter, sans-serif\" },\n hovermode: showCrosshairs ? \"x\" as const : \"x unified\" as const,\n dragmode: \"zoom\" as const,\n xaxis: {\n title: {\n text: xAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 15,\n },\n showgrid: showGridX,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: xRange,\n autorange: !xRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n yaxis: {\n title: {\n text: yAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 10,\n },\n showgrid: showGridY,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: yRange,\n autorange: !yRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n legend: {\n x: 0.5,\n y: -0.15,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n },\n showlegend: showLegend && series.length > 1,\n annotations: plotlyAnnotations,\n };\n\n const config: Partial<Plotly.Config> = {\n responsive: true,\n displayModeBar: true,\n displaylogo: false,\n modeBarButtonsToRemove: [\n \"lasso2d\",\n \"select2d\",\n ...(showExportButton ? [] : [\"toImage\"] as Plotly.ModeBarDefaultButtons[]),\n ] as Plotly.ModeBarDefaultButtons[],\n ...(showExportButton && {\n toImageButtonOptions: {\n format: \"png\",\n filename: \"chromatogram\",\n width: width,\n height: height,\n },\n }),\n };\n\n Plotly.newPlot(currentRef, plotData, layout, config);\n\n return () => {\n if (currentRef) {\n Plotly.purge(currentRef);\n }\n };\n }, [\n processedSeries, allDetectedPeaks, series.length, width, height, title, xAxisTitle, yAxisTitle,\n processedAnnotations, xRange, yRange, showLegend, showGridX, showGridY, showMarkers, markerSize,\n showCrosshairs, enablePeakDetection, peakDetectionOptions, showPeakAreas, boundaryMarkers,\n annotationOverlapThreshold, showExportButton, theme,\n ]);\n\n return (\n <div className=\"chromatogram-chart-container\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n </div>\n );\n};\n\nexport { ChromatogramChart };\n\n"],"names":["ChromatogramChart","series","width","height","title","xAxisTitle","yAxisTitle","annotations","xRange","yRange","showLegend","showGridX","showGridY","showMarkers","markerSize","showCrosshairs","baselineCorrection","baselineWindowSize","peakDetectionOptions","showPeakAreas","boundaryMarkers","annotationOverlapThreshold","showExportButton","enablePeakDetection","plotRef","useRef","theme","usePlotlyTheme","processedSeries","useMemo","s","validated","validateSeriesData","applyBaselineCorrection","processedAnnotations","x","y","processUserAnnotations","allDetectedPeaks","peaks","index","detected","detectPeaks","useEffect","currentRef","plotData","traceColor","CHART_COLORS","extraContent","buildHoverExtraContent","trace","peaksWithData","collectPeaksWithBoundaryData","boundaryTraces","createBoundaryMarkerTraces","allPeaksWithMeta","ann","seriesIndex","peak","groups","groupOverlappingPeaks","plotlyAnnotations","group","createGroupAnnotations","layout","CHROMATOGRAM_LAYOUT","config","Plotly","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChromatogramChart.js","sources":["../../../../src/components/charts/ChromatogramChart/ChromatogramChart.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\n\nimport { seriesColor } from \"../../../utils/colors\";\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport {\n groupOverlappingPeaks,\n createGroupAnnotations,\n} from \"./annotations\";\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT } from \"./constants\";\nimport {\n validateSeriesData,\n applyBaselineCorrection,\n collectPeaksWithBoundaryData,\n processUserAnnotations,\n} from \"./dataProcessing\";\nimport { detectPeaks } from \"./peakDetection\";\n\nimport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n PeakWithMeta,\n} from \"./types\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\n\n// Re-export types for external use\nexport type {\n ChromatogramSeries,\n PeakAnnotation,\n BaselineCorrectionMethod,\n BoundaryMarkerStyle,\n BoundaryMarkerType,\n PeakDetectionOptions,\n ChromatogramChartProps,\n};\n\n\n// Stable default so the no-annotations case keeps a constant identity; an\n// inline `[]` default would change every render, re-running the plot effect\n// (and tearing down the hover tooltip) on each re-render.\nconst EMPTY_ANNOTATIONS: PeakAnnotation[] = [];\n\nconst ChromatogramChart: React.FC<ChromatogramChartProps> = ({\n series,\n width = 900,\n height = 500,\n title,\n xAxisTitle = \"Retention Time (min)\",\n yAxisTitle = \"Signal (mAU)\",\n annotations = EMPTY_ANNOTATIONS,\n xRange,\n yRange,\n showLegend = true,\n showGridX = true,\n showGridY = true,\n showMarkers = false,\n markerSize = 4,\n showCrosshairs = false,\n baselineCorrection = \"none\",\n baselineWindowSize = 50,\n peakDetectionOptions,\n showPeakAreas = false,\n boundaryMarkers = \"none\",\n annotationOverlapThreshold = 0.4,\n showExportButton = true,\n}) => {\n // Derive peak detection state from options\n const enablePeakDetection = peakDetectionOptions !== undefined;\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({\n xLabel: xAxisTitle,\n yLabel: yAxisTitle,\n });\n\n // Memoize processed series with baseline correction\n const processedSeries = useMemo(() => {\n return series.map((s) => {\n const validated = validateSeriesData(s.x, s.y);\n return {\n ...s,\n x: validated.x,\n y: applyBaselineCorrection(validated.y, baselineCorrection, baselineWindowSize),\n };\n });\n }, [series, baselineCorrection, baselineWindowSize]);\n\n // Process user annotations to convert startX/endX to indices and compute areas\n const processedAnnotations = useMemo(() => {\n if (annotations.length === 0 || processedSeries.length === 0) {\n return annotations;\n }\n // Use first series data for index lookup (user annotations apply to first series)\n const { x, y } = processedSeries[0];\n return processUserAnnotations(annotations, x, y);\n }, [annotations, processedSeries]);\n\n // Memoize peak detection results\n const allDetectedPeaks = useMemo(() => {\n const peaks: { peaks: PeakAnnotation[]; seriesIndex: number }[] = [];\n if (enablePeakDetection && peakDetectionOptions) {\n processedSeries.forEach((s, index) => {\n const detected = detectPeaks(s.x, s.y, peakDetectionOptions);\n if (detected.length > 0) {\n peaks.push({ peaks: detected, seriesIndex: index });\n }\n });\n }\n return peaks;\n }, [processedSeries, enablePeakDetection, peakDetectionOptions]);\n\n useEffect(() => {\n const currentRef = plotRef.current;\n if (!currentRef || series.length === 0) return;\n\n // Build trace data with auto-assigned colors\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = seriesColor(index, s.color);\n\n const trace: Plotly.Data = {\n x: s.x,\n y: s.y,\n type: \"scatter\" as const,\n mode: showMarkers ? \"lines+markers\" as const : \"lines\" as const,\n name: s.name,\n line: {\n color: traceColor,\n width: 1.5,\n },\n hoverinfo: \"none\" as const,\n };\n if (showMarkers) {\n trace.marker = {\n size: markerSize,\n color: traceColor,\n };\n }\n return trace;\n });\n\n // Add peak boundary markers if enabled\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(allDetectedPeaks, processedAnnotations, processedSeries);\n if (peaksWithData.length > 0) {\n const boundaryTraces = createBoundaryMarkerTraces(peaksWithData);\n plotData.push(...boundaryTraces);\n }\n }\n\n // Collect all peaks for unified staggering logic\n const allPeaksWithMeta: PeakWithMeta[] = [];\n\n // Add user-defined annotations (seriesIndex -1 indicates user-defined)\n processedAnnotations.forEach((ann) => {\n allPeaksWithMeta.push({ peak: ann, seriesIndex: -1 });\n });\n\n // Add auto-detected peaks if enabled\n if (showPeakAreas && enablePeakDetection) {\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n peaks.forEach((peak) => {\n allPeaksWithMeta.push({ peak, seriesIndex });\n });\n });\n }\n\n // Group all overlapping peaks and create annotations with staggering\n const groups = groupOverlappingPeaks(allPeaksWithMeta, annotationOverlapThreshold);\n const plotlyAnnotations: Partial<Plotly.Annotations>[] = [];\n\n for (const group of groups) {\n plotlyAnnotations.push(...createGroupAnnotations(group));\n }\n\n const layout: Partial<Plotly.Layout> = {\n title: title\n ? {\n text: title,\n font: {\n size: 20,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n }\n : undefined,\n width,\n height,\n margin: {\n l: CHROMATOGRAM_LAYOUT.MARGIN_LEFT,\n r: CHROMATOGRAM_LAYOUT.MARGIN_RIGHT,\n b: CHROMATOGRAM_LAYOUT.MARGIN_BOTTOM,\n t: title ? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE : CHROMATOGRAM_LAYOUT.MARGIN_TOP_NO_TITLE,\n pad: CHROMATOGRAM_LAYOUT.MARGIN_PAD,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: { family: \"Inter, sans-serif\" },\n hovermode: showCrosshairs ? \"x\" as const : \"x unified\" as const,\n dragmode: \"zoom\" as const,\n xaxis: {\n title: {\n text: xAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 15,\n },\n showgrid: showGridX,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: xRange,\n autorange: !xRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n yaxis: {\n title: {\n text: yAxisTitle,\n font: { size: 14, color: theme.textSecondary, family: \"Inter, sans-serif\" },\n standoff: 10,\n },\n showgrid: showGridY,\n gridcolor: theme.gridColor,\n linecolor: theme.lineColor,\n linewidth: 1,\n range: yRange,\n autorange: !yRange,\n zeroline: false,\n tickfont: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n showspikes: showCrosshairs,\n spikemode: \"across\" as const,\n spikesnap: \"cursor\" as const,\n spikecolor: theme.spikeColor,\n spikethickness: 1,\n spikedash: \"dot\" as const,\n },\n legend: {\n x: 0.5,\n y: -0.15,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: { size: 12, color: theme.textColor, family: \"Inter, sans-serif\" },\n },\n showlegend: showLegend && series.length > 1,\n annotations: plotlyAnnotations,\n };\n\n const config: Partial<Plotly.Config> = {\n responsive: true,\n displayModeBar: true,\n displaylogo: false,\n modeBarButtonsToRemove: [\n \"lasso2d\",\n \"select2d\",\n ...(showExportButton ? [] : [\"toImage\"] as Plotly.ModeBarDefaultButtons[]),\n ] as Plotly.ModeBarDefaultButtons[],\n ...(showExportButton && {\n toImageButtonOptions: {\n format: \"png\",\n filename: \"chromatogram\",\n width: width,\n height: height,\n },\n }),\n };\n\n Plotly.newPlot(currentRef, plotData, layout, config);\n bindTooltip(currentRef);\n\n return () => {\n if (currentRef) {\n Plotly.purge(currentRef);\n }\n };\n }, [\n processedSeries, allDetectedPeaks, series.length, width, height, title, xAxisTitle, yAxisTitle,\n processedAnnotations, xRange, yRange, showLegend, showGridX, showGridY, showMarkers, markerSize,\n showCrosshairs, enablePeakDetection, peakDetectionOptions, showPeakAreas, boundaryMarkers,\n annotationOverlapThreshold, showExportButton, theme, bindTooltip,\n ]);\n\n return (\n <div className=\"chromatogram-chart-container relative\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { ChromatogramChart };\n\n"],"names":["EMPTY_ANNOTATIONS","ChromatogramChart","series","width","height","title","xAxisTitle","yAxisTitle","annotations","xRange","yRange","showLegend","showGridX","showGridY","showMarkers","markerSize","showCrosshairs","baselineCorrection","baselineWindowSize","peakDetectionOptions","showPeakAreas","boundaryMarkers","annotationOverlapThreshold","showExportButton","enablePeakDetection","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","processedSeries","useMemo","s","validated","validateSeriesData","applyBaselineCorrection","processedAnnotations","x","y","processUserAnnotations","allDetectedPeaks","peaks","index","detected","detectPeaks","useEffect","currentRef","plotData","traceColor","seriesColor","trace","peaksWithData","collectPeaksWithBoundaryData","boundaryTraces","createBoundaryMarkerTraces","allPeaksWithMeta","ann","seriesIndex","peak","groups","groupOverlappingPeaks","plotlyAnnotations","group","createGroupAnnotations","layout","CHROMATOGRAM_LAYOUT","config","Plotly","jsxs","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 T=require("../../../utils/colors.cjs"),O=require("./constants.cjs"),A={default:{ax:0,ay:-35},overlap:[{ax:50,ay:-35},{ax:-60,ay:-35},{ax:70,ay:-55},{ax:-80,ay:-55},{ax:50,ay:-75},{ax:-60,ay:-75}]};function u(t,n){const e=[...t].sort((a,s)=>a.peak.x-s.peak.x),o=[];let r=[];for(const a of e){if(r.length===0){r.push(a);continue}const s=r[r.length-1];Math.abs(a.peak.x-s.peak.x)<n?r.push(a):(o.push(r),r=[a])}return r.length>0&&o.push(r),o}function c(t,n,e){const o=n===-1,r=o?O.CHROMATOGRAM_ANNOTATION.USER_ANNOTATION_COLOR:T.seriesColor(n),a=o?O.CHROMATOGRAM_ANNOTATION.USER_ANNOTATION_TEXT_COLOR:r,s=t.text??(t._computed?.area===void 0?"":`Area: ${t._computed.area.toFixed(2)}`),i=o&&t.ax!==void 0?t.ax:e.ax,N=o&&t.ay!==void 0?t.ay:e.ay;return{x:t.x,y:t.y,text:s,showarrow:!0,arrowhead:2,arrowsize:1,arrowwidth:1,arrowcolor:r,ax:i,ay:N,font:{size:o?O.CHROMATOGRAM_ANNOTATION.USER_ANNOTATION_FONT_SIZE:O.CHROMATOGRAM_ANNOTATION.AUTO_ANNOTATION_FONT_SIZE,color:a,family:"Inter, sans-serif"},bgcolor:O.CHROMATOGRAM_ANNOTATION.BACKGROUND_COLOR,borderpad:2,bordercolor:o?void 0:r,borderwidth:o?0:1}}function l(t){if(t.length===1){const{peak:e,seriesIndex:o}=t[0];return[c(e,o,A.default)]}return[...t].sort((e,o)=>e.peak.y-o.peak.y).map(({peak:e,seriesIndex:o},r)=>{const a=A.overlap[r%A.overlap.length];return c(e,o,a)})}exports.ANNOTATION_SLOTS=A;exports.createGroupAnnotations=l;exports.createPeakAnnotation=c;exports.groupOverlappingPeaks=u;
|
|
2
2
|
//# sourceMappingURL=annotations.cjs.map
|