@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.148.1 → 1.0.0-beta.150.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":"plotBuilder.cjs","sources":["../../../../src/components/charts/Chromatogram/plotBuilder.ts"],"sourcesContent":["import { CHART_COLORS } from \"../../../utils/colors\";\nimport { getLoadedPlotly } from \"../plotly-loader\";\n\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT, CHROMATOGRAM_TRACE } from \"./constants\";\nimport { buildHoverExtraContent, collectPeaksWithBoundaryData } from \"./dataProcessing\";\nimport { createRegionOverlayTraces } from \"./regionOverlays\";\n\nimport type { ChromatogramSeries, PeakAnnotation, BoundaryMarkerStyle, PeakSelectEvent } from \"./types\";\nimport type Plotly from \"plotly.js-dist\";\n\nimport { CHART_FONT_FAMILY, type PlotlyThemeColors } from \"@/hooks/use-plotly-theme\";\n\ntype PeakForInteraction = {\n peak: PeakAnnotation & { id: string };\n seriesIndex: number;\n seriesName: string;\n isAutoDetected: boolean;\n};\n\ntype BuildTraceDataParams = {\n processedSeries: ChromatogramSeries[];\n processedAnnotations: PeakAnnotation[];\n allDetectedPeaks: { peaks: PeakAnnotation[]; seriesIndex: number }[];\n allPeaksForInteraction: PeakForInteraction[];\n showMarkers: boolean;\n markerSize: number;\n xAxisTitle: string;\n yAxisTitle: string;\n boundaryMarkers: BoundaryMarkerStyle;\n};\n\ntype BuildLayoutParams = {\n title?: string;\n titleFontSize: number;\n titleTopMargin?: number;\n width: number;\n height: number;\n xAxisTitle: string;\n yAxisTitle: string;\n xRange?: [number, number];\n yRange?: [number, number];\n showLegend: boolean;\n seriesCount: number;\n showGridX: boolean;\n showGridY: boolean;\n showCrosshairs: boolean;\n theme: PlotlyThemeColors;\n peakAnnotations: Partial<Plotly.Annotations>[];\n};\n\ntype BuildConfigParams = {\n showExportButton: boolean;\n width: number;\n height: number;\n};\n\nexport type { PeakForInteraction, BuildTraceDataParams, BuildLayoutParams, BuildConfigParams };\n\nexport function buildTraceData(params: BuildTraceDataParams): Plotly.Data[] {\n const {\n processedSeries,\n processedAnnotations,\n allDetectedPeaks,\n allPeaksForInteraction,\n showMarkers,\n markerSize,\n xAxisTitle,\n yAxisTitle,\n boundaryMarkers,\n } = params;\n\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: CHROMATOGRAM_TRACE.BASE_LINE_WIDTH,\n },\n hovertemplate: `%{x:.2f} ${xAxisTitle}<br>%{y:.2f} ${yAxisTitle}<extra>${extraContent}</extra>`,\n };\n if (showMarkers) {\n trace.marker = { size: markerSize, color: traceColor };\n }\n return trace;\n });\n\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(\n allDetectedPeaks,\n processedAnnotations,\n processedSeries\n );\n if (peaksWithData.length > 0) {\n plotData.push(...createBoundaryMarkerTraces(peaksWithData));\n }\n }\n\n processedAnnotations.forEach((ann) => {\n if (ann.regionOverlay && processedSeries[0]) {\n plotData.push(...createRegionOverlayTraces([ann], 0, processedSeries[0]));\n }\n });\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n if (peaks.some((p) => p.regionOverlay) && processedSeries[seriesIndex]) {\n plotData.push(...createRegionOverlayTraces(peaks, seriesIndex, processedSeries[seriesIndex]));\n }\n });\n\n if (allPeaksForInteraction.length > 0) {\n const anyHoverText = allPeaksForInteraction.some((p) => p.peak.hoverText);\n const hitAreaTrace: Plotly.Data = {\n x: allPeaksForInteraction.map((p) => p.peak.x),\n y: allPeaksForInteraction.map((p) => p.peak.y),\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n marker: { size: 14, opacity: 0 },\n showlegend: false,\n name: \"\",\n customdata: allPeaksForInteraction.map((p) => ({\n id: p.peak.id,\n peak: p.peak,\n seriesIndex: p.seriesIndex,\n seriesName: p.seriesName,\n isAutoDetected: p.isAutoDetected,\n })) as unknown as Plotly.Datum[],\n ...(anyHoverText\n ? {\n hovertemplate: allPeaksForInteraction.map((p) =>\n p.peak.hoverText ? `${p.peak.hoverText}<extra></extra>` : \"<extra></extra>\"\n ),\n }\n : { hovertemplate: \"<extra></extra>\" }),\n };\n plotData.push(hitAreaTrace);\n }\n\n return plotData;\n}\n\nexport function buildLayout(params: BuildLayoutParams): Partial<Plotly.Layout> {\n const {\n title,\n titleFontSize,\n titleTopMargin,\n width,\n height,\n xAxisTitle,\n yAxisTitle,\n xRange,\n yRange,\n showLegend,\n seriesCount,\n showGridX,\n showGridY,\n showCrosshairs,\n theme,\n peakAnnotations,\n } = params;\n\n return {\n title: title\n ? {\n text: title,\n font: { size: titleFontSize, family: CHART_FONT_FAMILY, color: theme.textColor },\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\n ? (titleTopMargin ?? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE)\n : 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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\n },\n showlegend: showLegend && seriesCount > 1,\n annotations: peakAnnotations,\n };\n}\n\nexport function buildConfig(params: BuildConfigParams): Partial<Plotly.Config> {\n const { showExportButton, width, height } = params;\n return {\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,\n height,\n },\n }),\n };\n}\n\ntype MutableRef<T> = { current: T };\n\nexport function createHoverHandler(\n domElement: HTMLElement,\n processedSeriesLength: number,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>,\n hoverLineWidthMultiplier: number\n): (eventData: Plotly.PlotHoverEvent) => void {\n return (eventData) => {\n const pt = eventData.points[0];\n if (pt && pt.curveNumber < processedSeriesLength) {\n const targetIdx = pt.curveNumber;\n if (thickenedSeriesRef.current !== targetIdx) {\n // Hover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n if (thickenedSeriesRef.current !== null) {\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n }\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH * hoverLineWidthMultiplier } as Plotly.Data, [targetIdx]);\n thickenedSeriesRef.current = targetIdx;\n }\n }\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (peakPoint) {\n onPeakHoverRef.current?.(peakPoint.customdata as unknown as PeakSelectEvent);\n }\n };\n}\n\nexport function createClickHandler(\n onPeakClickRef: MutableRef<((event: PeakSelectEvent) => void) | undefined>\n): (eventData: Plotly.PlotMouseEvent) => void {\n return (eventData) => {\n if (!onPeakClickRef.current) return;\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (!peakPoint) return;\n onPeakClickRef.current(peakPoint.customdata as unknown as PeakSelectEvent);\n };\n}\n\nexport function createUnhoverHandler(\n domElement: HTMLElement,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>\n): () => void {\n return () => {\n onPeakHoverRef.current?.(null);\n if (thickenedSeriesRef.current !== null) {\n // Unhover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n thickenedSeriesRef.current = null;\n }\n };\n}\n"],"names":["buildTraceData","params","processedSeries","processedAnnotations","allDetectedPeaks","allPeaksForInteraction","showMarkers","markerSize","xAxisTitle","yAxisTitle","boundaryMarkers","plotData","s","index","traceColor","CHART_COLORS","extraContent","buildHoverExtraContent","trace","CHROMATOGRAM_TRACE","peaksWithData","collectPeaksWithBoundaryData","createBoundaryMarkerTraces","ann","createRegionOverlayTraces","peaks","seriesIndex","p","anyHoverText","hitAreaTrace","buildLayout","title","titleFontSize","titleTopMargin","width","height","xRange","yRange","showLegend","seriesCount","showGridX","showGridY","showCrosshairs","theme","peakAnnotations","CHART_FONT_FAMILY","CHROMATOGRAM_LAYOUT","buildConfig","showExportButton","createHoverHandler","domElement","processedSeriesLength","thickenedSeriesRef","onPeakHoverRef","hoverLineWidthMultiplier","eventData","pt","targetIdx","getLoadedPlotly","peakPoint","createClickHandler","onPeakClickRef","createUnhoverHandler"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plotBuilder.cjs","sources":["../../../../src/components/charts/Chromatogram/plotBuilder.ts"],"sourcesContent":["import { CHART_COLORS } from \"../../../utils/colors\";\nimport { getLoadedPlotly } from \"../plotly-loader\";\n\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT, CHROMATOGRAM_TRACE } from \"./constants\";\nimport { buildHoverExtraContent, collectPeaksWithBoundaryData } from \"./dataProcessing\";\nimport { createRegionOverlayTraces } from \"./regionOverlays\";\n\nimport type { ChromatogramSeries, PeakAnnotation, BoundaryMarkerStyle, PeakSelectEvent } from \"./types\";\nimport type { ChartTooltipHoverPoint } from \"../ChartTooltip\";\nimport type Plotly from \"plotly.js-dist\";\n\nimport { CHART_FONT_FAMILY, type PlotlyThemeColors } from \"@/hooks/use-plotly-theme\";\n\ntype PeakForInteraction = {\n peak: PeakAnnotation & { id: string };\n seriesIndex: number;\n seriesName: string;\n isAutoDetected: boolean;\n};\n\ntype BuildTraceDataParams = {\n processedSeries: ChromatogramSeries[];\n processedAnnotations: PeakAnnotation[];\n allDetectedPeaks: { peaks: PeakAnnotation[]; seriesIndex: number }[];\n allPeaksForInteraction: PeakForInteraction[];\n showMarkers: boolean;\n markerSize: number;\n boundaryMarkers: BoundaryMarkerStyle;\n};\n\ntype BuildLayoutParams = {\n title?: string;\n titleFontSize: number;\n titleTopMargin?: number;\n width: number;\n height: number;\n xAxisTitle: string;\n yAxisTitle: string;\n xRange?: [number, number];\n yRange?: [number, number];\n showLegend: boolean;\n seriesCount: number;\n showGridX: boolean;\n showGridY: boolean;\n showCrosshairs: boolean;\n theme: PlotlyThemeColors;\n peakAnnotations: Partial<Plotly.Annotations>[];\n};\n\ntype BuildConfigParams = {\n showExportButton: boolean;\n width: number;\n height: number;\n};\n\nexport type { PeakForInteraction, BuildTraceDataParams, BuildLayoutParams, BuildConfigParams };\n\nexport function buildTraceData(params: BuildTraceDataParams): Plotly.Data[] {\n const {\n processedSeries,\n processedAnnotations,\n allDetectedPeaks,\n allPeaksForInteraction,\n showMarkers,\n markerSize,\n boundaryMarkers,\n } = params;\n\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = s.color || CHART_COLORS[index % CHART_COLORS.length];\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: CHROMATOGRAM_TRACE.BASE_LINE_WIDTH,\n },\n // Hover is rendered by the shared ChartTooltip (bound in Chromatogram);\n // \"none\" keeps plotly_hover firing without also drawing Plotly's own\n // label on top of it (SW-2298).\n hoverinfo: \"none\" as const,\n };\n if (showMarkers) {\n trace.marker = { size: markerSize, color: traceColor };\n }\n return trace;\n });\n\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(\n allDetectedPeaks,\n processedAnnotations,\n processedSeries\n );\n if (peaksWithData.length > 0) {\n plotData.push(...createBoundaryMarkerTraces(peaksWithData));\n }\n }\n\n processedAnnotations.forEach((ann) => {\n if (ann.regionOverlay && processedSeries[0]) {\n plotData.push(...createRegionOverlayTraces([ann], 0, processedSeries[0]));\n }\n });\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n if (peaks.some((p) => p.regionOverlay) && processedSeries[seriesIndex]) {\n plotData.push(...createRegionOverlayTraces(peaks, seriesIndex, processedSeries[seriesIndex]));\n }\n });\n\n if (allPeaksForInteraction.length > 0) {\n const hitAreaTrace: Plotly.Data = {\n x: allPeaksForInteraction.map((p) => p.peak.x),\n y: allPeaksForInteraction.map((p) => p.peak.y),\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n marker: { size: 14, opacity: 0 },\n showlegend: false,\n name: \"\",\n customdata: allPeaksForInteraction.map((p) => ({\n id: p.peak.id,\n peak: p.peak,\n seriesIndex: p.seriesIndex,\n seriesName: p.seriesName,\n isAutoDetected: p.isAutoDetected,\n })) as unknown as Plotly.Datum[],\n // Peak hoverText reaches the shared ChartTooltip through customdata\n // (see buildChromatogramTooltipLines), not Plotly's native label.\n hoverinfo: \"none\" as const,\n };\n plotData.push(hitAreaTrace);\n }\n\n return plotData;\n}\n\ntype ChromatogramTooltipPoint = ChartTooltipHoverPoint;\n\ntype ChromatogramTooltipParams = {\n series: ChromatogramSeries[];\n xAxisTitle: string;\n yAxisTitle: string;\n};\n\nconst formatTooltipNumber = (value: number | string): string =>\n typeof value === \"number\" ? value.toFixed(2) : String(value);\n\n/**\n * The ChartTooltip renders plain text, so `hoverText` written for Plotly's\n * hovertemplate is normalised for display: `<br>` splits lines and the rest is\n * reduced to its text content via the HTML parser (entities decoded, tags\n * dropped). This is presentation clean-up, not security sanitisation — the\n * lines are rendered as React text nodes, never as markup.\n */\nexport function htmlToTooltipLines(text?: string): string[] {\n if (!text) return [];\n const withBreaks = text.replace(/<br\\s*\\/?>/gi, \"\\n\");\n const plain =\n typeof DOMParser === \"undefined\"\n ? withBreaks\n : (new DOMParser().parseFromString(withBreaks, \"text/html\").body.textContent ?? \"\");\n return plain\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n}\n\n/**\n * Split \"Signal (mAU)\" into { label: \"Signal\", unit: \"mAU\" } so values read\n * \"Signal: 12.00 mAU\"; a title without a trailing parenthesised unit keeps\n * its full text as the label and an empty unit.\n */\nexport function splitAxisTitle(axisTitle: string): { label: string; unit: string } {\n // Plain string scanning rather than a regex: the title is consumer input and\n // a backtracking pattern over its whitespace was flagged by CodeQL.\n const trimmed = axisTitle.trim();\n const fallback = { label: trimmed, unit: \"\" };\n if (!trimmed.endsWith(\")\")) return fallback;\n const open = trimmed.lastIndexOf(\"(\");\n if (open <= 0) return fallback;\n const unit = trimmed.slice(open + 1, -1).trim();\n const label = trimmed.slice(0, open).trim();\n if (!unit || !label || unit.includes(\"(\") || unit.includes(\")\")) return fallback;\n return { label, unit };\n}\n\nconst withUnit = (value: string, unit: string): string => (unit ? `${value} ${unit}` : value);\n\n/** \"<name>: <y> <unit>\" followed by the series metadata lines */\nfunction seriesTooltipLines(\n seriesEntry: ChromatogramSeries,\n y: number | string,\n unit: string\n): string[] {\n // buildHoverExtraContent yields \"<name><br>Key: value…\"; keep only the metadata\n const metadata = buildHoverExtraContent(seriesEntry.name, seriesEntry.metadata).split(\"<br>\").slice(1);\n return [`${seriesEntry.name}: ${withUnit(formatTooltipNumber(y), unit)}`, ...metadata];\n}\n\n/** Peak text for a hit-area point (customdata) or a region overlay (trace text) */\nfunction peakTooltipLines(point: ChromatogramTooltipPoint): string[] {\n const peak = (point.customdata as { peak?: PeakAnnotation } | null | undefined)?.peak;\n if (peak) return htmlToTooltipLines(peak.hoverText ?? peak.text);\n return typeof point.text === \"string\" ? htmlToTooltipLines(point.text) : [];\n}\n\n/**\n * Lines for the shared ChartTooltip: the shared x value, one line per hovered\n * series (plus its metadata), then any peak text/hoverText the cursor is on —\n * peaks arrive as the invisible hit-area trace's customdata, overlays carry\n * their text on the trace. The same peak can be reported by both in one\n * \"x unified\" hover, so identical peak blocks are emitted once.\n */\nexport function buildChromatogramTooltipLines(\n points: ChromatogramTooltipPoint[],\n params: ChromatogramTooltipParams\n): string[] {\n const { series, xAxisTitle, yAxisTitle } = params;\n // Both axes are split the same way so the x and y lines read alike:\n // \"Retention Time: 5.80 min\" over \"Sample A: 420.00 mAU\".\n const xAxis = splitAxisTitle(xAxisTitle);\n const { unit } = splitAxisTitle(yAxisTitle);\n const lines: string[] = [];\n const first = points.find((p) => p.x !== undefined);\n if (first?.x !== undefined) {\n lines.push(`${xAxis.label}: ${withUnit(formatTooltipNumber(first.x), xAxis.unit)}`);\n }\n\n const seenPeakBlocks = new Set<string>();\n for (const point of points) {\n const seriesEntry = point.curveNumber === undefined ? undefined : series[point.curveNumber];\n if (seriesEntry) {\n if (point.y !== undefined) lines.push(...seriesTooltipLines(seriesEntry, point.y, unit));\n continue;\n }\n const peakLines = peakTooltipLines(point);\n const key = peakLines.join(\"\\n\");\n if (peakLines.length > 0 && !seenPeakBlocks.has(key)) {\n seenPeakBlocks.add(key);\n lines.push(...peakLines);\n }\n }\n return lines;\n}\n\nexport function buildLayout(params: BuildLayoutParams): Partial<Plotly.Layout> {\n const {\n title,\n titleFontSize,\n titleTopMargin,\n width,\n height,\n xAxisTitle,\n yAxisTitle,\n xRange,\n yRange,\n showLegend,\n seriesCount,\n showGridX,\n showGridY,\n showCrosshairs,\n theme,\n peakAnnotations,\n } = params;\n\n return {\n title: title\n ? {\n text: title,\n font: { size: titleFontSize, family: CHART_FONT_FAMILY, color: theme.textColor },\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\n ? (titleTopMargin ?? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE)\n : 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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\n },\n showlegend: showLegend && seriesCount > 1,\n annotations: peakAnnotations,\n };\n}\n\nexport function buildConfig(params: BuildConfigParams): Partial<Plotly.Config> {\n const { showExportButton, width, height } = params;\n return {\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,\n height,\n },\n }),\n };\n}\n\ntype MutableRef<T> = { current: T };\n\nexport function createHoverHandler(\n domElement: HTMLElement,\n processedSeriesLength: number,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>,\n hoverLineWidthMultiplier: number\n): (eventData: Plotly.PlotHoverEvent) => void {\n return (eventData) => {\n const pt = eventData.points[0];\n if (pt && pt.curveNumber < processedSeriesLength) {\n const targetIdx = pt.curveNumber;\n if (thickenedSeriesRef.current !== targetIdx) {\n // Hover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n if (thickenedSeriesRef.current !== null) {\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n }\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH * hoverLineWidthMultiplier } as Plotly.Data, [targetIdx]);\n thickenedSeriesRef.current = targetIdx;\n }\n }\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (peakPoint) {\n onPeakHoverRef.current?.(peakPoint.customdata as unknown as PeakSelectEvent);\n }\n };\n}\n\nexport function createClickHandler(\n onPeakClickRef: MutableRef<((event: PeakSelectEvent) => void) | undefined>\n): (eventData: Plotly.PlotMouseEvent) => void {\n return (eventData) => {\n if (!onPeakClickRef.current) return;\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (!peakPoint) return;\n onPeakClickRef.current(peakPoint.customdata as unknown as PeakSelectEvent);\n };\n}\n\nexport function createUnhoverHandler(\n domElement: HTMLElement,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>\n): () => void {\n return () => {\n onPeakHoverRef.current?.(null);\n if (thickenedSeriesRef.current !== null) {\n // Unhover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n thickenedSeriesRef.current = null;\n }\n };\n}\n"],"names":["buildTraceData","params","processedSeries","processedAnnotations","allDetectedPeaks","allPeaksForInteraction","showMarkers","markerSize","boundaryMarkers","plotData","s","index","traceColor","CHART_COLORS","trace","CHROMATOGRAM_TRACE","peaksWithData","collectPeaksWithBoundaryData","createBoundaryMarkerTraces","ann","createRegionOverlayTraces","peaks","seriesIndex","p","hitAreaTrace","formatTooltipNumber","value","htmlToTooltipLines","text","withBreaks","line","splitAxisTitle","axisTitle","trimmed","fallback","open","unit","label","withUnit","seriesTooltipLines","seriesEntry","y","metadata","buildHoverExtraContent","peakTooltipLines","point","peak","buildChromatogramTooltipLines","points","series","xAxisTitle","yAxisTitle","xAxis","lines","first","seenPeakBlocks","peakLines","key","buildLayout","title","titleFontSize","titleTopMargin","width","height","xRange","yRange","showLegend","seriesCount","showGridX","showGridY","showCrosshairs","theme","peakAnnotations","CHART_FONT_FAMILY","CHROMATOGRAM_LAYOUT","buildConfig","showExportButton","createHoverHandler","domElement","processedSeriesLength","thickenedSeriesRef","onPeakHoverRef","hoverLineWidthMultiplier","eventData","pt","targetIdx","getLoadedPlotly","peakPoint","createClickHandler","onPeakClickRef","createUnhoverHandler"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChromatogramSeries, PeakAnnotation, BoundaryMarkerStyle, PeakSelectEvent } from './types';
|
|
2
|
+
import { ChartTooltipHoverPoint } from '../ChartTooltip';
|
|
2
3
|
import { default as Plotly } from 'plotly.js-dist';
|
|
3
4
|
import { PlotlyThemeColors } from '../../../hooks/use-plotly-theme';
|
|
4
5
|
type PeakForInteraction = {
|
|
@@ -19,8 +20,6 @@ type BuildTraceDataParams = {
|
|
|
19
20
|
allPeaksForInteraction: PeakForInteraction[];
|
|
20
21
|
showMarkers: boolean;
|
|
21
22
|
markerSize: number;
|
|
22
|
-
xAxisTitle: string;
|
|
23
|
-
yAxisTitle: string;
|
|
24
23
|
boundaryMarkers: BoundaryMarkerStyle;
|
|
25
24
|
};
|
|
26
25
|
type BuildLayoutParams = {
|
|
@@ -48,6 +47,37 @@ type BuildConfigParams = {
|
|
|
48
47
|
};
|
|
49
48
|
export type { PeakForInteraction, BuildTraceDataParams, BuildLayoutParams, BuildConfigParams };
|
|
50
49
|
export declare function buildTraceData(params: BuildTraceDataParams): Plotly.Data[];
|
|
50
|
+
type ChromatogramTooltipPoint = ChartTooltipHoverPoint;
|
|
51
|
+
type ChromatogramTooltipParams = {
|
|
52
|
+
series: ChromatogramSeries[];
|
|
53
|
+
xAxisTitle: string;
|
|
54
|
+
yAxisTitle: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The ChartTooltip renders plain text, so `hoverText` written for Plotly's
|
|
58
|
+
* hovertemplate is normalised for display: `<br>` splits lines and the rest is
|
|
59
|
+
* reduced to its text content via the HTML parser (entities decoded, tags
|
|
60
|
+
* dropped). This is presentation clean-up, not security sanitisation — the
|
|
61
|
+
* lines are rendered as React text nodes, never as markup.
|
|
62
|
+
*/
|
|
63
|
+
export declare function htmlToTooltipLines(text?: string): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Split "Signal (mAU)" into { label: "Signal", unit: "mAU" } so values read
|
|
66
|
+
* "Signal: 12.00 mAU"; a title without a trailing parenthesised unit keeps
|
|
67
|
+
* its full text as the label and an empty unit.
|
|
68
|
+
*/
|
|
69
|
+
export declare function splitAxisTitle(axisTitle: string): {
|
|
70
|
+
label: string;
|
|
71
|
+
unit: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Lines for the shared ChartTooltip: the shared x value, one line per hovered
|
|
75
|
+
* series (plus its metadata), then any peak text/hoverText the cursor is on —
|
|
76
|
+
* peaks arrive as the invisible hit-area trace's customdata, overlays carry
|
|
77
|
+
* their text on the trace. The same peak can be reported by both in one
|
|
78
|
+
* "x unified" hover, so identical peak blocks are emitted once.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildChromatogramTooltipLines(points: ChromatogramTooltipPoint[], params: ChromatogramTooltipParams): string[];
|
|
51
81
|
export declare function buildLayout(params: BuildLayoutParams): Partial<Plotly.Layout>;
|
|
52
82
|
export declare function buildConfig(params: BuildConfigParams): Partial<Plotly.Config>;
|
|
53
83
|
type MutableRef<T> = {
|
|
@@ -1,151 +1,191 @@
|
|
|
1
|
-
import { CHART_COLORS as
|
|
2
|
-
import { getLoadedPlotly as
|
|
3
|
-
import { createBoundaryMarkerTraces as
|
|
4
|
-
import { CHROMATOGRAM_TRACE as
|
|
5
|
-
import {
|
|
1
|
+
import { CHART_COLORS as T } from "../../../utils/colors.js";
|
|
2
|
+
import { getLoadedPlotly as k } from "../plotly-loader.js";
|
|
3
|
+
import { createBoundaryMarkerTraces as O } from "./boundaryMarkers.js";
|
|
4
|
+
import { CHROMATOGRAM_TRACE as x, CHROMATOGRAM_LAYOUT as h } from "./constants.js";
|
|
5
|
+
import { collectPeaksWithBoundaryData as M, buildHoverExtraContent as N } from "./dataProcessing.js";
|
|
6
6
|
import { createRegionOverlayTraces as A } from "./regionOverlays.js";
|
|
7
|
-
import { CHART_FONT_FAMILY as
|
|
8
|
-
function
|
|
7
|
+
import { CHART_FONT_FAMILY as f } from "../../../hooks/use-plotly-theme.js";
|
|
8
|
+
function P(e) {
|
|
9
9
|
const {
|
|
10
|
-
processedSeries:
|
|
11
|
-
processedAnnotations:
|
|
12
|
-
allDetectedPeaks:
|
|
13
|
-
allPeaksForInteraction:
|
|
14
|
-
showMarkers:
|
|
10
|
+
processedSeries: t,
|
|
11
|
+
processedAnnotations: o,
|
|
12
|
+
allDetectedPeaks: i,
|
|
13
|
+
allPeaksForInteraction: a,
|
|
14
|
+
showMarkers: c,
|
|
15
15
|
markerSize: d,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
x: o.x,
|
|
22
|
-
y: o.y,
|
|
16
|
+
boundaryMarkers: u
|
|
17
|
+
} = e, s = t.map((n, r) => {
|
|
18
|
+
const p = n.color || T[r % T.length], m = {
|
|
19
|
+
x: n.x,
|
|
20
|
+
y: n.y,
|
|
23
21
|
type: "scatter",
|
|
24
|
-
mode:
|
|
25
|
-
name:
|
|
22
|
+
mode: c ? "lines+markers" : "lines",
|
|
23
|
+
name: n.name,
|
|
26
24
|
line: {
|
|
27
|
-
color:
|
|
28
|
-
width:
|
|
25
|
+
color: p,
|
|
26
|
+
width: x.BASE_LINE_WIDTH
|
|
29
27
|
},
|
|
30
|
-
|
|
28
|
+
// Hover is rendered by the shared ChartTooltip (bound in Chromatogram);
|
|
29
|
+
// "none" keeps plotly_hover firing without also drawing Plotly's own
|
|
30
|
+
// label on top of it (SW-2298).
|
|
31
|
+
hoverinfo: "none"
|
|
31
32
|
};
|
|
32
|
-
return
|
|
33
|
+
return c && (m.marker = { size: d, color: p }), m;
|
|
33
34
|
});
|
|
34
|
-
if (
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
if (u !== "none") {
|
|
36
|
+
const n = M(
|
|
37
|
+
i,
|
|
38
|
+
o,
|
|
39
|
+
t
|
|
39
40
|
);
|
|
40
|
-
|
|
41
|
+
n.length > 0 && s.push(...O(n));
|
|
41
42
|
}
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
}),
|
|
45
|
-
|
|
46
|
-
}),
|
|
47
|
-
const
|
|
48
|
-
x:
|
|
49
|
-
y:
|
|
43
|
+
if (o.forEach((n) => {
|
|
44
|
+
n.regionOverlay && t[0] && s.push(...A([n], 0, t[0]));
|
|
45
|
+
}), i.forEach(({ peaks: n, seriesIndex: r }) => {
|
|
46
|
+
n.some((p) => p.regionOverlay) && t[r] && s.push(...A(n, r, t[r]));
|
|
47
|
+
}), a.length > 0) {
|
|
48
|
+
const n = {
|
|
49
|
+
x: a.map((r) => r.peak.x),
|
|
50
|
+
y: a.map((r) => r.peak.y),
|
|
50
51
|
type: "scatter",
|
|
51
52
|
mode: "markers",
|
|
52
53
|
marker: { size: 14, opacity: 0 },
|
|
53
54
|
showlegend: !1,
|
|
54
55
|
name: "",
|
|
55
|
-
customdata:
|
|
56
|
-
id:
|
|
57
|
-
peak:
|
|
58
|
-
seriesIndex:
|
|
59
|
-
seriesName:
|
|
60
|
-
isAutoDetected:
|
|
56
|
+
customdata: a.map((r) => ({
|
|
57
|
+
id: r.peak.id,
|
|
58
|
+
peak: r.peak,
|
|
59
|
+
seriesIndex: r.seriesIndex,
|
|
60
|
+
seriesName: r.seriesName,
|
|
61
|
+
isAutoDetected: r.isAutoDetected
|
|
61
62
|
})),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
} : { hovertemplate: "<extra></extra>" }
|
|
63
|
+
// Peak hoverText reaches the shared ChartTooltip through customdata
|
|
64
|
+
// (see buildChromatogramTooltipLines), not Plotly's native label.
|
|
65
|
+
hoverinfo: "none"
|
|
67
66
|
};
|
|
68
|
-
|
|
67
|
+
s.push(n);
|
|
68
|
+
}
|
|
69
|
+
return s;
|
|
70
|
+
}
|
|
71
|
+
const I = (e) => typeof e == "number" ? e.toFixed(2) : String(e);
|
|
72
|
+
function _(e) {
|
|
73
|
+
if (!e) return [];
|
|
74
|
+
const t = e.replace(/<br\s*\/?>/gi, `
|
|
75
|
+
`);
|
|
76
|
+
return (typeof DOMParser > "u" ? t : new DOMParser().parseFromString(t, "text/html").body.textContent ?? "").split(`
|
|
77
|
+
`).map((i) => i.trim()).filter(Boolean);
|
|
78
|
+
}
|
|
79
|
+
function w(e) {
|
|
80
|
+
const t = e.trim(), o = { label: t, unit: "" };
|
|
81
|
+
if (!t.endsWith(")")) return o;
|
|
82
|
+
const i = t.lastIndexOf("(");
|
|
83
|
+
if (i <= 0) return o;
|
|
84
|
+
const a = t.slice(i + 1, -1).trim(), c = t.slice(0, i).trim();
|
|
85
|
+
return !a || !c || a.includes("(") || a.includes(")") ? o : { label: c, unit: a };
|
|
86
|
+
}
|
|
87
|
+
const C = (e, t) => t ? `${e} ${t}` : e;
|
|
88
|
+
function v(e, t, o) {
|
|
89
|
+
const i = N(e.name, e.metadata).split("<br>").slice(1);
|
|
90
|
+
return [`${e.name}: ${C(I(t), o)}`, ...i];
|
|
91
|
+
}
|
|
92
|
+
function L(e) {
|
|
93
|
+
const t = e.customdata?.peak;
|
|
94
|
+
return t ? _(t.hoverText ?? t.text) : typeof e.text == "string" ? _(e.text) : [];
|
|
95
|
+
}
|
|
96
|
+
function S(e, t) {
|
|
97
|
+
const { series: o, xAxisTitle: i, yAxisTitle: a } = t, c = w(i), { unit: d } = w(a), u = [], s = e.find((r) => r.x !== void 0);
|
|
98
|
+
s?.x !== void 0 && u.push(`${c.label}: ${C(I(s.x), c.unit)}`);
|
|
99
|
+
const n = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const r of e) {
|
|
101
|
+
const p = r.curveNumber === void 0 ? void 0 : o[r.curveNumber];
|
|
102
|
+
if (p) {
|
|
103
|
+
r.y !== void 0 && u.push(...v(p, r.y, d));
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const m = L(r), g = m.join(`
|
|
107
|
+
`);
|
|
108
|
+
m.length > 0 && !n.has(g) && (n.add(g), u.push(...m));
|
|
69
109
|
}
|
|
70
110
|
return u;
|
|
71
111
|
}
|
|
72
|
-
function
|
|
112
|
+
function W(e) {
|
|
73
113
|
const {
|
|
74
|
-
title:
|
|
75
|
-
titleFontSize:
|
|
76
|
-
titleTopMargin:
|
|
77
|
-
width:
|
|
78
|
-
height:
|
|
114
|
+
title: t,
|
|
115
|
+
titleFontSize: o,
|
|
116
|
+
titleTopMargin: i,
|
|
117
|
+
width: a,
|
|
118
|
+
height: c,
|
|
79
119
|
xAxisTitle: d,
|
|
80
|
-
yAxisTitle:
|
|
81
|
-
xRange:
|
|
82
|
-
yRange:
|
|
83
|
-
showLegend:
|
|
84
|
-
seriesCount:
|
|
85
|
-
showGridX:
|
|
86
|
-
showGridY:
|
|
87
|
-
showCrosshairs:
|
|
88
|
-
theme:
|
|
89
|
-
peakAnnotations:
|
|
90
|
-
} =
|
|
120
|
+
yAxisTitle: u,
|
|
121
|
+
xRange: s,
|
|
122
|
+
yRange: n,
|
|
123
|
+
showLegend: r,
|
|
124
|
+
seriesCount: p,
|
|
125
|
+
showGridX: m,
|
|
126
|
+
showGridY: g,
|
|
127
|
+
showCrosshairs: y,
|
|
128
|
+
theme: l,
|
|
129
|
+
peakAnnotations: b
|
|
130
|
+
} = e;
|
|
91
131
|
return {
|
|
92
|
-
title:
|
|
93
|
-
text:
|
|
94
|
-
font: { size:
|
|
132
|
+
title: t ? {
|
|
133
|
+
text: t,
|
|
134
|
+
font: { size: o, family: f, color: l.textColor }
|
|
95
135
|
} : void 0,
|
|
96
|
-
width:
|
|
97
|
-
height:
|
|
136
|
+
width: a,
|
|
137
|
+
height: c,
|
|
98
138
|
margin: {
|
|
99
139
|
l: h.MARGIN_LEFT,
|
|
100
140
|
r: h.MARGIN_RIGHT,
|
|
101
141
|
b: h.MARGIN_BOTTOM,
|
|
102
|
-
t:
|
|
142
|
+
t: t ? i ?? h.MARGIN_TOP_WITH_TITLE : h.MARGIN_TOP_NO_TITLE,
|
|
103
143
|
pad: h.MARGIN_PAD
|
|
104
144
|
},
|
|
105
|
-
paper_bgcolor:
|
|
106
|
-
plot_bgcolor:
|
|
107
|
-
font: { family:
|
|
108
|
-
hovermode:
|
|
145
|
+
paper_bgcolor: l.paperBg,
|
|
146
|
+
plot_bgcolor: l.plotBg,
|
|
147
|
+
font: { family: f },
|
|
148
|
+
hovermode: y ? "x" : "x unified",
|
|
109
149
|
dragmode: "zoom",
|
|
110
150
|
xaxis: {
|
|
111
151
|
title: {
|
|
112
152
|
text: d,
|
|
113
|
-
font: { size: 14, color:
|
|
153
|
+
font: { size: 14, color: l.textSecondary, family: f },
|
|
114
154
|
standoff: 15
|
|
115
155
|
},
|
|
116
|
-
showgrid:
|
|
117
|
-
gridcolor:
|
|
118
|
-
linecolor:
|
|
156
|
+
showgrid: m,
|
|
157
|
+
gridcolor: l.gridColor,
|
|
158
|
+
linecolor: l.lineColor,
|
|
119
159
|
linewidth: 1,
|
|
120
|
-
range:
|
|
121
|
-
autorange: !
|
|
160
|
+
range: s,
|
|
161
|
+
autorange: !s,
|
|
122
162
|
zeroline: !1,
|
|
123
|
-
tickfont: { size: 12, color:
|
|
124
|
-
showspikes:
|
|
163
|
+
tickfont: { size: 12, color: l.textColor, family: f },
|
|
164
|
+
showspikes: y,
|
|
125
165
|
spikemode: "across",
|
|
126
166
|
spikesnap: "cursor",
|
|
127
|
-
spikecolor:
|
|
167
|
+
spikecolor: l.spikeColor,
|
|
128
168
|
spikethickness: 1,
|
|
129
169
|
spikedash: "dot"
|
|
130
170
|
},
|
|
131
171
|
yaxis: {
|
|
132
172
|
title: {
|
|
133
|
-
text:
|
|
134
|
-
font: { size: 14, color:
|
|
173
|
+
text: u,
|
|
174
|
+
font: { size: 14, color: l.textSecondary, family: f },
|
|
135
175
|
standoff: 10
|
|
136
176
|
},
|
|
137
|
-
showgrid:
|
|
138
|
-
gridcolor:
|
|
139
|
-
linecolor:
|
|
177
|
+
showgrid: g,
|
|
178
|
+
gridcolor: l.gridColor,
|
|
179
|
+
linecolor: l.lineColor,
|
|
140
180
|
linewidth: 1,
|
|
141
|
-
range:
|
|
142
|
-
autorange: !
|
|
181
|
+
range: n,
|
|
182
|
+
autorange: !n,
|
|
143
183
|
zeroline: !1,
|
|
144
|
-
tickfont: { size: 12, color:
|
|
145
|
-
showspikes:
|
|
184
|
+
tickfont: { size: 12, color: l.textColor, family: f },
|
|
185
|
+
showspikes: y,
|
|
146
186
|
spikemode: "across",
|
|
147
187
|
spikesnap: "cursor",
|
|
148
|
-
spikecolor:
|
|
188
|
+
spikecolor: l.spikeColor,
|
|
149
189
|
spikethickness: 1,
|
|
150
190
|
spikedash: "dot"
|
|
151
191
|
},
|
|
@@ -155,14 +195,14 @@ function L(i) {
|
|
|
155
195
|
xanchor: "center",
|
|
156
196
|
yanchor: "top",
|
|
157
197
|
orientation: "h",
|
|
158
|
-
font: { size: 12, color:
|
|
198
|
+
font: { size: 12, color: l.textColor, family: f }
|
|
159
199
|
},
|
|
160
|
-
showlegend:
|
|
161
|
-
annotations:
|
|
200
|
+
showlegend: r && p > 1,
|
|
201
|
+
annotations: b
|
|
162
202
|
};
|
|
163
203
|
}
|
|
164
|
-
function
|
|
165
|
-
const { showExportButton:
|
|
204
|
+
function F(e) {
|
|
205
|
+
const { showExportButton: t, width: o, height: i } = e;
|
|
166
206
|
return {
|
|
167
207
|
responsive: !0,
|
|
168
208
|
displayModeBar: !0,
|
|
@@ -170,47 +210,50 @@ function R(i) {
|
|
|
170
210
|
modeBarButtonsToRemove: [
|
|
171
211
|
"lasso2d",
|
|
172
212
|
"select2d",
|
|
173
|
-
...
|
|
213
|
+
...t ? [] : ["toImage"]
|
|
174
214
|
],
|
|
175
|
-
...
|
|
215
|
+
...t && {
|
|
176
216
|
toImageButtonOptions: {
|
|
177
217
|
format: "png",
|
|
178
218
|
filename: "chromatogram",
|
|
179
|
-
width:
|
|
180
|
-
height:
|
|
219
|
+
width: o,
|
|
220
|
+
height: i
|
|
181
221
|
}
|
|
182
222
|
}
|
|
183
223
|
};
|
|
184
224
|
}
|
|
185
|
-
function
|
|
186
|
-
return (
|
|
187
|
-
const d =
|
|
188
|
-
if (d && d.curveNumber <
|
|
189
|
-
const
|
|
190
|
-
|
|
225
|
+
function $(e, t, o, i, a) {
|
|
226
|
+
return (c) => {
|
|
227
|
+
const d = c.points[0];
|
|
228
|
+
if (d && d.curveNumber < t) {
|
|
229
|
+
const s = d.curveNumber;
|
|
230
|
+
o.current !== s && (o.current !== null && k().restyle(e, { "line.width": x.BASE_LINE_WIDTH }, [o.current]), k().restyle(e, { "line.width": x.BASE_LINE_WIDTH * a }, [s]), o.current = s);
|
|
191
231
|
}
|
|
192
|
-
const
|
|
193
|
-
|
|
232
|
+
const u = c.points.find((s) => s.customdata != null);
|
|
233
|
+
u && i.current?.(u.customdata);
|
|
194
234
|
};
|
|
195
235
|
}
|
|
196
|
-
function
|
|
197
|
-
return (
|
|
198
|
-
if (!
|
|
199
|
-
const
|
|
200
|
-
|
|
236
|
+
function U(e) {
|
|
237
|
+
return (t) => {
|
|
238
|
+
if (!e.current) return;
|
|
239
|
+
const o = t.points.find((i) => i.customdata != null);
|
|
240
|
+
o && e.current(o.customdata);
|
|
201
241
|
};
|
|
202
242
|
}
|
|
203
|
-
function
|
|
243
|
+
function Y(e, t, o) {
|
|
204
244
|
return () => {
|
|
205
|
-
|
|
245
|
+
o.current?.(null), t.current !== null && (k().restyle(e, { "line.width": x.BASE_LINE_WIDTH }, [t.current]), t.current = null);
|
|
206
246
|
};
|
|
207
247
|
}
|
|
208
248
|
export {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
249
|
+
S as buildChromatogramTooltipLines,
|
|
250
|
+
F as buildConfig,
|
|
251
|
+
W as buildLayout,
|
|
252
|
+
P as buildTraceData,
|
|
253
|
+
U as createClickHandler,
|
|
254
|
+
$ as createHoverHandler,
|
|
255
|
+
Y as createUnhoverHandler,
|
|
256
|
+
_ as htmlToTooltipLines,
|
|
257
|
+
w as splitAxisTitle
|
|
215
258
|
};
|
|
216
259
|
//# sourceMappingURL=plotBuilder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plotBuilder.js","sources":["../../../../src/components/charts/Chromatogram/plotBuilder.ts"],"sourcesContent":["import { CHART_COLORS } from \"../../../utils/colors\";\nimport { getLoadedPlotly } from \"../plotly-loader\";\n\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT, CHROMATOGRAM_TRACE } from \"./constants\";\nimport { buildHoverExtraContent, collectPeaksWithBoundaryData } from \"./dataProcessing\";\nimport { createRegionOverlayTraces } from \"./regionOverlays\";\n\nimport type { ChromatogramSeries, PeakAnnotation, BoundaryMarkerStyle, PeakSelectEvent } from \"./types\";\nimport type Plotly from \"plotly.js-dist\";\n\nimport { CHART_FONT_FAMILY, type PlotlyThemeColors } from \"@/hooks/use-plotly-theme\";\n\ntype PeakForInteraction = {\n peak: PeakAnnotation & { id: string };\n seriesIndex: number;\n seriesName: string;\n isAutoDetected: boolean;\n};\n\ntype BuildTraceDataParams = {\n processedSeries: ChromatogramSeries[];\n processedAnnotations: PeakAnnotation[];\n allDetectedPeaks: { peaks: PeakAnnotation[]; seriesIndex: number }[];\n allPeaksForInteraction: PeakForInteraction[];\n showMarkers: boolean;\n markerSize: number;\n xAxisTitle: string;\n yAxisTitle: string;\n boundaryMarkers: BoundaryMarkerStyle;\n};\n\ntype BuildLayoutParams = {\n title?: string;\n titleFontSize: number;\n titleTopMargin?: number;\n width: number;\n height: number;\n xAxisTitle: string;\n yAxisTitle: string;\n xRange?: [number, number];\n yRange?: [number, number];\n showLegend: boolean;\n seriesCount: number;\n showGridX: boolean;\n showGridY: boolean;\n showCrosshairs: boolean;\n theme: PlotlyThemeColors;\n peakAnnotations: Partial<Plotly.Annotations>[];\n};\n\ntype BuildConfigParams = {\n showExportButton: boolean;\n width: number;\n height: number;\n};\n\nexport type { PeakForInteraction, BuildTraceDataParams, BuildLayoutParams, BuildConfigParams };\n\nexport function buildTraceData(params: BuildTraceDataParams): Plotly.Data[] {\n const {\n processedSeries,\n processedAnnotations,\n allDetectedPeaks,\n allPeaksForInteraction,\n showMarkers,\n markerSize,\n xAxisTitle,\n yAxisTitle,\n boundaryMarkers,\n } = params;\n\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: CHROMATOGRAM_TRACE.BASE_LINE_WIDTH,\n },\n hovertemplate: `%{x:.2f} ${xAxisTitle}<br>%{y:.2f} ${yAxisTitle}<extra>${extraContent}</extra>`,\n };\n if (showMarkers) {\n trace.marker = { size: markerSize, color: traceColor };\n }\n return trace;\n });\n\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(\n allDetectedPeaks,\n processedAnnotations,\n processedSeries\n );\n if (peaksWithData.length > 0) {\n plotData.push(...createBoundaryMarkerTraces(peaksWithData));\n }\n }\n\n processedAnnotations.forEach((ann) => {\n if (ann.regionOverlay && processedSeries[0]) {\n plotData.push(...createRegionOverlayTraces([ann], 0, processedSeries[0]));\n }\n });\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n if (peaks.some((p) => p.regionOverlay) && processedSeries[seriesIndex]) {\n plotData.push(...createRegionOverlayTraces(peaks, seriesIndex, processedSeries[seriesIndex]));\n }\n });\n\n if (allPeaksForInteraction.length > 0) {\n const anyHoverText = allPeaksForInteraction.some((p) => p.peak.hoverText);\n const hitAreaTrace: Plotly.Data = {\n x: allPeaksForInteraction.map((p) => p.peak.x),\n y: allPeaksForInteraction.map((p) => p.peak.y),\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n marker: { size: 14, opacity: 0 },\n showlegend: false,\n name: \"\",\n customdata: allPeaksForInteraction.map((p) => ({\n id: p.peak.id,\n peak: p.peak,\n seriesIndex: p.seriesIndex,\n seriesName: p.seriesName,\n isAutoDetected: p.isAutoDetected,\n })) as unknown as Plotly.Datum[],\n ...(anyHoverText\n ? {\n hovertemplate: allPeaksForInteraction.map((p) =>\n p.peak.hoverText ? `${p.peak.hoverText}<extra></extra>` : \"<extra></extra>\"\n ),\n }\n : { hovertemplate: \"<extra></extra>\" }),\n };\n plotData.push(hitAreaTrace);\n }\n\n return plotData;\n}\n\nexport function buildLayout(params: BuildLayoutParams): Partial<Plotly.Layout> {\n const {\n title,\n titleFontSize,\n titleTopMargin,\n width,\n height,\n xAxisTitle,\n yAxisTitle,\n xRange,\n yRange,\n showLegend,\n seriesCount,\n showGridX,\n showGridY,\n showCrosshairs,\n theme,\n peakAnnotations,\n } = params;\n\n return {\n title: title\n ? {\n text: title,\n font: { size: titleFontSize, family: CHART_FONT_FAMILY, color: theme.textColor },\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\n ? (titleTopMargin ?? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE)\n : 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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\n },\n showlegend: showLegend && seriesCount > 1,\n annotations: peakAnnotations,\n };\n}\n\nexport function buildConfig(params: BuildConfigParams): Partial<Plotly.Config> {\n const { showExportButton, width, height } = params;\n return {\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,\n height,\n },\n }),\n };\n}\n\ntype MutableRef<T> = { current: T };\n\nexport function createHoverHandler(\n domElement: HTMLElement,\n processedSeriesLength: number,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>,\n hoverLineWidthMultiplier: number\n): (eventData: Plotly.PlotHoverEvent) => void {\n return (eventData) => {\n const pt = eventData.points[0];\n if (pt && pt.curveNumber < processedSeriesLength) {\n const targetIdx = pt.curveNumber;\n if (thickenedSeriesRef.current !== targetIdx) {\n // Hover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n if (thickenedSeriesRef.current !== null) {\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n }\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH * hoverLineWidthMultiplier } as Plotly.Data, [targetIdx]);\n thickenedSeriesRef.current = targetIdx;\n }\n }\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (peakPoint) {\n onPeakHoverRef.current?.(peakPoint.customdata as unknown as PeakSelectEvent);\n }\n };\n}\n\nexport function createClickHandler(\n onPeakClickRef: MutableRef<((event: PeakSelectEvent) => void) | undefined>\n): (eventData: Plotly.PlotMouseEvent) => void {\n return (eventData) => {\n if (!onPeakClickRef.current) return;\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (!peakPoint) return;\n onPeakClickRef.current(peakPoint.customdata as unknown as PeakSelectEvent);\n };\n}\n\nexport function createUnhoverHandler(\n domElement: HTMLElement,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>\n): () => void {\n return () => {\n onPeakHoverRef.current?.(null);\n if (thickenedSeriesRef.current !== null) {\n // Unhover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n thickenedSeriesRef.current = null;\n }\n };\n}\n"],"names":["buildTraceData","params","processedSeries","processedAnnotations","allDetectedPeaks","allPeaksForInteraction","showMarkers","markerSize","xAxisTitle","yAxisTitle","boundaryMarkers","plotData","s","index","traceColor","CHART_COLORS","extraContent","buildHoverExtraContent","trace","CHROMATOGRAM_TRACE","peaksWithData","collectPeaksWithBoundaryData","createBoundaryMarkerTraces","ann","createRegionOverlayTraces","peaks","seriesIndex","p","anyHoverText","hitAreaTrace","buildLayout","title","titleFontSize","titleTopMargin","width","height","xRange","yRange","showLegend","seriesCount","showGridX","showGridY","showCrosshairs","theme","peakAnnotations","CHART_FONT_FAMILY","CHROMATOGRAM_LAYOUT","buildConfig","showExportButton","createHoverHandler","domElement","processedSeriesLength","thickenedSeriesRef","onPeakHoverRef","hoverLineWidthMultiplier","eventData","pt","targetIdx","getLoadedPlotly","peakPoint","createClickHandler","onPeakClickRef","createUnhoverHandler"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plotBuilder.js","sources":["../../../../src/components/charts/Chromatogram/plotBuilder.ts"],"sourcesContent":["import { CHART_COLORS } from \"../../../utils/colors\";\nimport { getLoadedPlotly } from \"../plotly-loader\";\n\nimport { createBoundaryMarkerTraces } from \"./boundaryMarkers\";\nimport { CHROMATOGRAM_LAYOUT, CHROMATOGRAM_TRACE } from \"./constants\";\nimport { buildHoverExtraContent, collectPeaksWithBoundaryData } from \"./dataProcessing\";\nimport { createRegionOverlayTraces } from \"./regionOverlays\";\n\nimport type { ChromatogramSeries, PeakAnnotation, BoundaryMarkerStyle, PeakSelectEvent } from \"./types\";\nimport type { ChartTooltipHoverPoint } from \"../ChartTooltip\";\nimport type Plotly from \"plotly.js-dist\";\n\nimport { CHART_FONT_FAMILY, type PlotlyThemeColors } from \"@/hooks/use-plotly-theme\";\n\ntype PeakForInteraction = {\n peak: PeakAnnotation & { id: string };\n seriesIndex: number;\n seriesName: string;\n isAutoDetected: boolean;\n};\n\ntype BuildTraceDataParams = {\n processedSeries: ChromatogramSeries[];\n processedAnnotations: PeakAnnotation[];\n allDetectedPeaks: { peaks: PeakAnnotation[]; seriesIndex: number }[];\n allPeaksForInteraction: PeakForInteraction[];\n showMarkers: boolean;\n markerSize: number;\n boundaryMarkers: BoundaryMarkerStyle;\n};\n\ntype BuildLayoutParams = {\n title?: string;\n titleFontSize: number;\n titleTopMargin?: number;\n width: number;\n height: number;\n xAxisTitle: string;\n yAxisTitle: string;\n xRange?: [number, number];\n yRange?: [number, number];\n showLegend: boolean;\n seriesCount: number;\n showGridX: boolean;\n showGridY: boolean;\n showCrosshairs: boolean;\n theme: PlotlyThemeColors;\n peakAnnotations: Partial<Plotly.Annotations>[];\n};\n\ntype BuildConfigParams = {\n showExportButton: boolean;\n width: number;\n height: number;\n};\n\nexport type { PeakForInteraction, BuildTraceDataParams, BuildLayoutParams, BuildConfigParams };\n\nexport function buildTraceData(params: BuildTraceDataParams): Plotly.Data[] {\n const {\n processedSeries,\n processedAnnotations,\n allDetectedPeaks,\n allPeaksForInteraction,\n showMarkers,\n markerSize,\n boundaryMarkers,\n } = params;\n\n const plotData: Plotly.Data[] = processedSeries.map((s, index) => {\n const traceColor = s.color || CHART_COLORS[index % CHART_COLORS.length];\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: CHROMATOGRAM_TRACE.BASE_LINE_WIDTH,\n },\n // Hover is rendered by the shared ChartTooltip (bound in Chromatogram);\n // \"none\" keeps plotly_hover firing without also drawing Plotly's own\n // label on top of it (SW-2298).\n hoverinfo: \"none\" as const,\n };\n if (showMarkers) {\n trace.marker = { size: markerSize, color: traceColor };\n }\n return trace;\n });\n\n if (boundaryMarkers !== \"none\") {\n const peaksWithData = collectPeaksWithBoundaryData(\n allDetectedPeaks,\n processedAnnotations,\n processedSeries\n );\n if (peaksWithData.length > 0) {\n plotData.push(...createBoundaryMarkerTraces(peaksWithData));\n }\n }\n\n processedAnnotations.forEach((ann) => {\n if (ann.regionOverlay && processedSeries[0]) {\n plotData.push(...createRegionOverlayTraces([ann], 0, processedSeries[0]));\n }\n });\n allDetectedPeaks.forEach(({ peaks, seriesIndex }) => {\n if (peaks.some((p) => p.regionOverlay) && processedSeries[seriesIndex]) {\n plotData.push(...createRegionOverlayTraces(peaks, seriesIndex, processedSeries[seriesIndex]));\n }\n });\n\n if (allPeaksForInteraction.length > 0) {\n const hitAreaTrace: Plotly.Data = {\n x: allPeaksForInteraction.map((p) => p.peak.x),\n y: allPeaksForInteraction.map((p) => p.peak.y),\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n marker: { size: 14, opacity: 0 },\n showlegend: false,\n name: \"\",\n customdata: allPeaksForInteraction.map((p) => ({\n id: p.peak.id,\n peak: p.peak,\n seriesIndex: p.seriesIndex,\n seriesName: p.seriesName,\n isAutoDetected: p.isAutoDetected,\n })) as unknown as Plotly.Datum[],\n // Peak hoverText reaches the shared ChartTooltip through customdata\n // (see buildChromatogramTooltipLines), not Plotly's native label.\n hoverinfo: \"none\" as const,\n };\n plotData.push(hitAreaTrace);\n }\n\n return plotData;\n}\n\ntype ChromatogramTooltipPoint = ChartTooltipHoverPoint;\n\ntype ChromatogramTooltipParams = {\n series: ChromatogramSeries[];\n xAxisTitle: string;\n yAxisTitle: string;\n};\n\nconst formatTooltipNumber = (value: number | string): string =>\n typeof value === \"number\" ? value.toFixed(2) : String(value);\n\n/**\n * The ChartTooltip renders plain text, so `hoverText` written for Plotly's\n * hovertemplate is normalised for display: `<br>` splits lines and the rest is\n * reduced to its text content via the HTML parser (entities decoded, tags\n * dropped). This is presentation clean-up, not security sanitisation — the\n * lines are rendered as React text nodes, never as markup.\n */\nexport function htmlToTooltipLines(text?: string): string[] {\n if (!text) return [];\n const withBreaks = text.replace(/<br\\s*\\/?>/gi, \"\\n\");\n const plain =\n typeof DOMParser === \"undefined\"\n ? withBreaks\n : (new DOMParser().parseFromString(withBreaks, \"text/html\").body.textContent ?? \"\");\n return plain\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n}\n\n/**\n * Split \"Signal (mAU)\" into { label: \"Signal\", unit: \"mAU\" } so values read\n * \"Signal: 12.00 mAU\"; a title without a trailing parenthesised unit keeps\n * its full text as the label and an empty unit.\n */\nexport function splitAxisTitle(axisTitle: string): { label: string; unit: string } {\n // Plain string scanning rather than a regex: the title is consumer input and\n // a backtracking pattern over its whitespace was flagged by CodeQL.\n const trimmed = axisTitle.trim();\n const fallback = { label: trimmed, unit: \"\" };\n if (!trimmed.endsWith(\")\")) return fallback;\n const open = trimmed.lastIndexOf(\"(\");\n if (open <= 0) return fallback;\n const unit = trimmed.slice(open + 1, -1).trim();\n const label = trimmed.slice(0, open).trim();\n if (!unit || !label || unit.includes(\"(\") || unit.includes(\")\")) return fallback;\n return { label, unit };\n}\n\nconst withUnit = (value: string, unit: string): string => (unit ? `${value} ${unit}` : value);\n\n/** \"<name>: <y> <unit>\" followed by the series metadata lines */\nfunction seriesTooltipLines(\n seriesEntry: ChromatogramSeries,\n y: number | string,\n unit: string\n): string[] {\n // buildHoverExtraContent yields \"<name><br>Key: value…\"; keep only the metadata\n const metadata = buildHoverExtraContent(seriesEntry.name, seriesEntry.metadata).split(\"<br>\").slice(1);\n return [`${seriesEntry.name}: ${withUnit(formatTooltipNumber(y), unit)}`, ...metadata];\n}\n\n/** Peak text for a hit-area point (customdata) or a region overlay (trace text) */\nfunction peakTooltipLines(point: ChromatogramTooltipPoint): string[] {\n const peak = (point.customdata as { peak?: PeakAnnotation } | null | undefined)?.peak;\n if (peak) return htmlToTooltipLines(peak.hoverText ?? peak.text);\n return typeof point.text === \"string\" ? htmlToTooltipLines(point.text) : [];\n}\n\n/**\n * Lines for the shared ChartTooltip: the shared x value, one line per hovered\n * series (plus its metadata), then any peak text/hoverText the cursor is on —\n * peaks arrive as the invisible hit-area trace's customdata, overlays carry\n * their text on the trace. The same peak can be reported by both in one\n * \"x unified\" hover, so identical peak blocks are emitted once.\n */\nexport function buildChromatogramTooltipLines(\n points: ChromatogramTooltipPoint[],\n params: ChromatogramTooltipParams\n): string[] {\n const { series, xAxisTitle, yAxisTitle } = params;\n // Both axes are split the same way so the x and y lines read alike:\n // \"Retention Time: 5.80 min\" over \"Sample A: 420.00 mAU\".\n const xAxis = splitAxisTitle(xAxisTitle);\n const { unit } = splitAxisTitle(yAxisTitle);\n const lines: string[] = [];\n const first = points.find((p) => p.x !== undefined);\n if (first?.x !== undefined) {\n lines.push(`${xAxis.label}: ${withUnit(formatTooltipNumber(first.x), xAxis.unit)}`);\n }\n\n const seenPeakBlocks = new Set<string>();\n for (const point of points) {\n const seriesEntry = point.curveNumber === undefined ? undefined : series[point.curveNumber];\n if (seriesEntry) {\n if (point.y !== undefined) lines.push(...seriesTooltipLines(seriesEntry, point.y, unit));\n continue;\n }\n const peakLines = peakTooltipLines(point);\n const key = peakLines.join(\"\\n\");\n if (peakLines.length > 0 && !seenPeakBlocks.has(key)) {\n seenPeakBlocks.add(key);\n lines.push(...peakLines);\n }\n }\n return lines;\n}\n\nexport function buildLayout(params: BuildLayoutParams): Partial<Plotly.Layout> {\n const {\n title,\n titleFontSize,\n titleTopMargin,\n width,\n height,\n xAxisTitle,\n yAxisTitle,\n xRange,\n yRange,\n showLegend,\n seriesCount,\n showGridX,\n showGridY,\n showCrosshairs,\n theme,\n peakAnnotations,\n } = params;\n\n return {\n title: title\n ? {\n text: title,\n font: { size: titleFontSize, family: CHART_FONT_FAMILY, color: theme.textColor },\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\n ? (titleTopMargin ?? CHROMATOGRAM_LAYOUT.MARGIN_TOP_WITH_TITLE)\n : 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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\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: CHART_FONT_FAMILY },\n },\n showlegend: showLegend && seriesCount > 1,\n annotations: peakAnnotations,\n };\n}\n\nexport function buildConfig(params: BuildConfigParams): Partial<Plotly.Config> {\n const { showExportButton, width, height } = params;\n return {\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,\n height,\n },\n }),\n };\n}\n\ntype MutableRef<T> = { current: T };\n\nexport function createHoverHandler(\n domElement: HTMLElement,\n processedSeriesLength: number,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>,\n hoverLineWidthMultiplier: number\n): (eventData: Plotly.PlotHoverEvent) => void {\n return (eventData) => {\n const pt = eventData.points[0];\n if (pt && pt.curveNumber < processedSeriesLength) {\n const targetIdx = pt.curveNumber;\n if (thickenedSeriesRef.current !== targetIdx) {\n // Hover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n if (thickenedSeriesRef.current !== null) {\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n }\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH * hoverLineWidthMultiplier } as Plotly.Data, [targetIdx]);\n thickenedSeriesRef.current = targetIdx;\n }\n }\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (peakPoint) {\n onPeakHoverRef.current?.(peakPoint.customdata as unknown as PeakSelectEvent);\n }\n };\n}\n\nexport function createClickHandler(\n onPeakClickRef: MutableRef<((event: PeakSelectEvent) => void) | undefined>\n): (eventData: Plotly.PlotMouseEvent) => void {\n return (eventData) => {\n if (!onPeakClickRef.current) return;\n const peakPoint = eventData.points.find((p) => p.customdata != null);\n if (!peakPoint) return;\n onPeakClickRef.current(peakPoint.customdata as unknown as PeakSelectEvent);\n };\n}\n\nexport function createUnhoverHandler(\n domElement: HTMLElement,\n thickenedSeriesRef: MutableRef<number | null>,\n onPeakHoverRef: MutableRef<((event: PeakSelectEvent | null) => void) | undefined>\n): () => void {\n return () => {\n onPeakHoverRef.current?.(null);\n if (thickenedSeriesRef.current !== null) {\n // Unhover events can only fire once the plot is drawn, so the\n // lazily-loaded Plotly module is guaranteed to be available here.\n getLoadedPlotly().restyle(domElement, { \"line.width\": CHROMATOGRAM_TRACE.BASE_LINE_WIDTH } as Plotly.Data, [thickenedSeriesRef.current]);\n thickenedSeriesRef.current = null;\n }\n };\n}\n"],"names":["buildTraceData","params","processedSeries","processedAnnotations","allDetectedPeaks","allPeaksForInteraction","showMarkers","markerSize","boundaryMarkers","plotData","s","index","traceColor","CHART_COLORS","trace","CHROMATOGRAM_TRACE","peaksWithData","collectPeaksWithBoundaryData","createBoundaryMarkerTraces","ann","createRegionOverlayTraces","peaks","seriesIndex","hitAreaTrace","p","formatTooltipNumber","value","htmlToTooltipLines","text","withBreaks","line","splitAxisTitle","axisTitle","trimmed","fallback","open","unit","label","withUnit","seriesTooltipLines","seriesEntry","y","metadata","buildHoverExtraContent","peakTooltipLines","point","peak","buildChromatogramTooltipLines","points","series","xAxisTitle","yAxisTitle","xAxis","lines","first","seenPeakBlocks","peakLines","key","buildLayout","title","titleFontSize","titleTopMargin","width","height","xRange","yRange","showLegend","seriesCount","showGridX","showGridY","showCrosshairs","theme","peakAnnotations","CHART_FONT_FAMILY","CHROMATOGRAM_LAYOUT","buildConfig","showExportButton","createHoverHandler","domElement","processedSeriesLength","thickenedSeriesRef","onPeakHoverRef","hoverLineWidthMultiplier","eventData","pt","targetIdx","getLoadedPlotly","peakPoint","createClickHandler","onPeakClickRef","createUnhoverHandler"],"mappings":"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"}
|