td-plots 1.11.6 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -355,6 +355,7 @@ function defaultLocale$2(definition) {
355
355
 
356
356
  // Utility functions for our components
357
357
  const ONEAVGMONTH = 2629800000; // Average month length in ms, copied from plotly constants in plotly.js/src/constants/numerical.js
358
+ const ONEWEEK = 604800000; // One week in milliseconds
358
359
  const ONEDAY = 86400000; // One day in milliseconds
359
360
  const ONEHOUR = 3600000; // One hour in milliseconds
360
361
  const ONEMINUTE = 60000; // One minute in milliseconds
@@ -402,12 +403,12 @@ function calculateStandardDeviation(arr) {
402
403
  // Helpful for making nice bins
403
404
  const roundToNextDay = (timestamp) => {
404
405
  const date = new Date(timestamp);
405
- date.setHours(23, 59, 59, 999); // End of day
406
+ date.setUTCHours(23, 59, 59, 999); // End of day
406
407
  return date.toISOString();
407
408
  };
408
409
  const roundToPrevDay = (timestamp) => {
409
410
  const date = new Date(timestamp);
410
- date.setHours(0, 0, 0, 0); // Start of day
411
+ date.setUTCHours(0, 0, 0, 0); // Start of day
411
412
  return date.toISOString();
412
413
  };
413
414
  // Convert plotly M# string to number of milliseconds
@@ -540,7 +541,7 @@ var SettingsIcon = createSvgIcon(/*#__PURE__*/jsx("path", {
540
541
  d: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6"
541
542
  }), 'Settings');
542
543
 
543
- const Plot$6 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
544
+ const Plot$8 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
544
545
  const HistogramPlot = (props) => {
545
546
  var _a, _b, _c, _d;
546
547
  const { data, title, xAxisTitle, barColor = "rgb(72, 72, 74)", unselectedBarColor = "rgba(203, 195, 195, 0.88)", selectorsColor = "black", containerStyleOverrides, unselectedData = [], handleClickOrSelection = () => { }, onDeselect = () => { }, plotId, selectByBin = false, dateTickFormat, binSizeOverride, statsAnnotations = ["mean"], emptySelectedRange = false, d3FormatValueString = ".1f", showBinSizeControls = "always", onBinSizeCalculated, onBinSizeChange, showBinSizeControlValue = true, isMobile = false, settingsTitleStylingOverrides = {}, } = props;
@@ -1292,7 +1293,7 @@ const HistogramPlot = (props) => {
1292
1293
  const newBinSize = newValue;
1293
1294
  setBinSize(newBinSize);
1294
1295
  onBinSizeChange === null || onBinSizeChange === void 0 ? void 0 : onBinSizeChange(newBinSize);
1295
- }, colorOverride: barColor, valueLabelFormat: valueLabelFormat, titleStylingOverrides: settingsTitleStylingOverrides, disabledTooltipText: "Requires 10 or more data points" }) }) })] })), jsx(Plot$6, { data: plotlyData, layout: layout, config: config, onSelected: handleSelection, onClick: handleClick, onDeselect: () => {
1296
+ }, colorOverride: barColor, valueLabelFormat: valueLabelFormat, titleStylingOverrides: settingsTitleStylingOverrides, disabledTooltipText: "Requires 10 or more data points" }) }) })] })), jsx(Plot$8, { data: plotlyData, layout: layout, config: config, onSelected: handleSelection, onClick: handleClick, onDeselect: () => {
1296
1297
  onDeselect();
1297
1298
  setSelectedRange(null); // Remove selected box
1298
1299
  }, onUpdate: handlePlotUpdate, useResizeHandler: true, style: {
@@ -1303,7 +1304,7 @@ const HistogramPlot = (props) => {
1303
1304
  } }, `histogram-${plotId || "default"}`)] }) }) }));
1304
1305
  };
1305
1306
 
1306
- const Plot$5 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1307
+ const Plot$7 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1307
1308
  const RadialHistogramPlot = (props) => {
1308
1309
  const { data, barColor = "rgb(72, 72, 74)", unselectedBarColor = "rgba(203, 195, 195, 0.88)", selectorsColor = "black", onSelected, onClick, containerStyleOverrides, barWidth = 20, // Default bar width in degrees
1309
1310
  } = props;
@@ -1403,14 +1404,14 @@ const RadialHistogramPlot = (props) => {
1403
1404
  staticPlot: false,
1404
1405
  };
1405
1406
  const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
1406
- return (jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ "--selection-color": selectorsColor }, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$5, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1407
+ return (jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ "--selection-color": selectorsColor }, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$7, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1407
1408
  width: "100%",
1408
1409
  height: "100%",
1409
1410
  display: "block",
1410
1411
  } }) }) }));
1411
1412
  };
1412
1413
 
1413
- const Plot$4 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1414
+ const Plot$6 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1414
1415
  const StatsDonut = (props) => {
1415
1416
  const { withCenterLabel = false, centerLabel, centerValue, showLegend = true, legendPosition = "right", containerStyle = {}, } = props;
1416
1417
  // Configure legend position based on prop
@@ -1458,7 +1459,7 @@ const StatsDonut = (props) => {
1458
1459
  },
1459
1460
  ]
1460
1461
  : [];
1461
- return (jsx("div", { style: Object.assign({ width: "100%", height: "100%", minHeight: "300px", display: "flex", justifyContent: "center", alignItems: "center" }, containerStyle), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$4, { data: [
1462
+ return (jsx("div", { style: Object.assign({ width: "100%", height: "100%", minHeight: "300px", display: "flex", justifyContent: "center", alignItems: "center" }, containerStyle), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$6, { data: [
1462
1463
  {
1463
1464
  type: "pie",
1464
1465
  values: props.values,
@@ -1491,7 +1492,7 @@ const StatsDonut = (props) => {
1491
1492
  }, useResizeHandler: true }) }) }));
1492
1493
  };
1493
1494
 
1494
- const Plot$3 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1495
+ const Plot$5 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1495
1496
  const BoxPlot = (props) => {
1496
1497
  const { data, width = 600, height = 400, title = "Box Plot", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "boxplot", extraLayoutConfig = {}, } = props;
1497
1498
  // Ref for plot container
@@ -1544,7 +1545,7 @@ const BoxPlot = (props) => {
1544
1545
  position: "relative",
1545
1546
  width: "100%",
1546
1547
  height: "100%",
1547
- }, children: [jsx(Plot$3, { data: plotlyData, layout: layout, config: config, useResizeHandler: true,
1548
+ }, children: [jsx(Plot$5, { data: plotlyData, layout: layout, config: config, useResizeHandler: true,
1548
1549
  // onHover={handleHover}
1549
1550
  // onUnhover={handleUnhover}
1550
1551
  style: {
@@ -2074,7 +2075,7 @@ const SplitBoxPlot = (props) => {
2074
2075
  return (jsx("div", { style: Object.assign({}, containerStyles), children: jsx(BoxPlot, { data: boxPlotData, width: width, height: height, title: title, xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, extraLayoutConfig: extraLayoutConfig, containerStyleOverrides: containerStyleOverrides, plotId: `${plotId}-boxplot` }) }));
2075
2076
  };
2076
2077
 
2077
- const Plot$2 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2078
+ const Plot$4 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2078
2079
  const SummaryComparisonPlot = (props) => {
2079
2080
  const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, unit = "", xAnnotations = [], tooltipLabels = {}, d3FormatValueString = ".1f", } = props;
2080
2081
  // Ref for plot container
@@ -2349,7 +2350,7 @@ const SummaryComparisonPlot = (props) => {
2349
2350
  position: "relative",
2350
2351
  width: "100%",
2351
2352
  height: "100%",
2352
- }, children: [jsx(Plot$2, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, onHover: handleHover, onUnhover: handleUnhover, style: {
2353
+ }, children: [jsx(Plot$4, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, onHover: handleHover, onUnhover: handleUnhover, style: {
2353
2354
  width: "100%",
2354
2355
  height: `${height}px`,
2355
2356
  display: "block",
@@ -2404,7 +2405,7 @@ const SummaryComparisonPlotLegend = ({ comparedDataLabel, summarizedDataLabel, c
2404
2405
  } })] }), jsx("span", { children: comparedDataLabel })] }), jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsx(Box, { sx: { width: 30, height: 5, backgroundColor: color } }), jsx("span", { children: summarizedDataLabel })] })] }));
2405
2406
  };
2406
2407
 
2407
- const Plot$1 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2408
+ const Plot$3 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2408
2409
  // This component plots a line chart in the foreground with a histogram in the backgroun. The histogram
2409
2410
  // is used only for context, while the line is intended to have the focus. To that end, the line plot has hover interactions
2410
2411
  // while the histogram does not.
@@ -2528,7 +2529,7 @@ const LineWithHistogram = (props) => {
2528
2529
  y1: bin.y, // Height matches bin count on y2 axis
2529
2530
  fillcolor: histogramData.color || "rgb(211, 211, 212)",
2530
2531
  line: {
2531
- color: histogramData.color || "rgb(211, 211, 212)", // Match fill color
2532
+ color: "white", // Match fill color
2532
2533
  width: 0, // No border
2533
2534
  },
2534
2535
  layer: "below", // Ensure shapes are drawn below traces
@@ -2552,7 +2553,7 @@ const LineWithHistogram = (props) => {
2552
2553
  zeroline: false,
2553
2554
  showline: true,
2554
2555
  mirror: "ticks",
2555
- gridcolor: "#efefef",
2556
+ gridcolor: "rgba(239, 239, 239, 0.5)",
2556
2557
  gridwidth: 0.2,
2557
2558
  zerolinecolor: "#969696",
2558
2559
  zerolinewidth: 1,
@@ -2620,15 +2621,145 @@ const LineWithHistogram = (props) => {
2620
2621
  position: "relative",
2621
2622
  width: "100%",
2622
2623
  height: "100%",
2623
- }, children: jsx(Plot$1, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, style: {
2624
+ }, children: jsx(Plot$3, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, style: {
2624
2625
  width: "100%",
2625
2626
  height: "100%",
2626
2627
  display: "block",
2627
2628
  transition: "opacity 0.15s ease-in-out",
2628
- }, revision: revision }, `lineplot-${plotId || "default"}`) }), jsx("div", { style: { display: "none" }, children: jsx(Plot$1, { data: histogramPlotlyData, layout: { width: 400, height: 300 }, onInitialized: handlePlotInitialized }, `hidden-histogram-${plotId || "default"}-${hiddenPlotKey}`) })] }) }));
2629
+ }, revision: revision }, `lineplot-${plotId || "default"}`) }), jsx("div", { style: { display: "none" }, children: jsx(Plot$3, { data: histogramPlotlyData, layout: { width: 400, height: 300 }, onInitialized: handlePlotInitialized }, `hidden-histogram-${plotId || "default"}-${hiddenPlotKey}`) })] }) }));
2629
2630
  };
2630
2631
 
2631
- const Plot = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2632
+ const Plot$2 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2633
+ const SPLIT = 0.33;
2634
+ const GAP = 0.04;
2635
+ // Stacked subplots sharing a single x-axis: histogram on top (yaxis2), line on bottom (yaxis).
2636
+ // Because both traces use xaxis "x", hovermode "x unified" spans both panels simultaneously.
2637
+ // histogramData.x must be in the same domain as lineData.x for hover to be meaningful.
2638
+ const LineOnHistogram = (props) => {
2639
+ const { lineData, histogramData, title, xAxisTitle, yAxisTitle, y2AxisTitle, containerStyleOverrides, plotId = "lineplot", extraLayoutConfig = {}, revision = 0, } = props;
2640
+ const sharedAxisStyle = {
2641
+ showgrid: true,
2642
+ zeroline: false,
2643
+ showline: true,
2644
+ mirror: "ticks",
2645
+ gridcolor: "rgba(239, 239, 239, 0.5)",
2646
+ gridwidth: 0.2,
2647
+ linecolor: "#bababa",
2648
+ linewidth: 1,
2649
+ fixedrange: true,
2650
+ };
2651
+ const lineDataTimes = lineData.x.map((d) => d.getTime());
2652
+ const adjustedBinSize = lineDataTimes.length > 1 &&
2653
+ Math.max(...lineDataTimes) - Math.min(...lineDataTimes) > ONEAVGMONTH * 6
2654
+ ? ONEWEEK
2655
+ : ONEDAY;
2656
+ const lineMarkerSymbols = lineData.x.map((d, i) => {
2657
+ if (i === 0) {
2658
+ return "arrow-up";
2659
+ }
2660
+ if (i === lineData.x.length - 1) {
2661
+ return "arrow-up";
2662
+ }
2663
+ return "circle";
2664
+ });
2665
+ const lineMarkerSymbolStandoff = lineData.x.map((d, i) => {
2666
+ if (i === 0) {
2667
+ return -6;
2668
+ }
2669
+ if (i === lineData.x.length - 1) {
2670
+ return -6;
2671
+ }
2672
+ return 0;
2673
+ });
2674
+ const lineMarkerSymbolAngles = lineData.x.map((d, i) => {
2675
+ if (i === 0) {
2676
+ return -90;
2677
+ }
2678
+ if (i === lineData.x.length - 1) {
2679
+ return 90;
2680
+ }
2681
+ return 0;
2682
+ });
2683
+ const plotlyData = [
2684
+ {
2685
+ type: "histogram",
2686
+ x: histogramData.x,
2687
+ xaxis: "x",
2688
+ yaxis: "y",
2689
+ xbins: histogramData.x.length > 0
2690
+ ? {
2691
+ size: adjustedBinSize,
2692
+ start: roundToPrevDay(Math.min(...histogramData.x.map((d) => new Date(d).getTime()))),
2693
+ end: new Date(Math.max(...histogramData.x.map((d) => new Date(d).getTime())) +
2694
+ adjustedBinSize).toISOString(),
2695
+ }
2696
+ : undefined,
2697
+ name: histogramData.name || "histogram",
2698
+ marker: {
2699
+ color: histogramData.color || "rgb(211, 211, 212)",
2700
+ line: { color: "white", width: 0.5 },
2701
+ },
2702
+ hovertemplate: histogramData.hovertemplate || "Count: %{y}<extra></extra>",
2703
+ },
2704
+ {
2705
+ // Invisible dummy trace — forces xaxis2 (top x-axis) to render
2706
+ type: "scatter",
2707
+ x: histogramData.x.slice(0, 1),
2708
+ y: [null],
2709
+ xaxis: "x2",
2710
+ yaxis: "y2",
2711
+ mode: "markers",
2712
+ marker: { opacity: 0, size: 0.1 },
2713
+ hoverinfo: "skip",
2714
+ showlegend: false,
2715
+ },
2716
+ {
2717
+ type: "scatter",
2718
+ mode: "lines+markers",
2719
+ x: lineData.x,
2720
+ y: lineData.y,
2721
+ xaxis: "x",
2722
+ yaxis: "y2",
2723
+ name: lineData.name,
2724
+ line: { color: lineData.color || "#1f77b4", shape: "hv" },
2725
+ marker: {
2726
+ color: lineData.color || "#1f77b4",
2727
+ size: 6,
2728
+ symbol: lineMarkerSymbols,
2729
+ // @ts-ignore
2730
+ standoff: lineMarkerSymbolStandoff,
2731
+ angle: lineMarkerSymbolAngles,
2732
+ },
2733
+ hovertemplate: lineData.hovertemplate || "%{y}<extra></extra>",
2734
+ },
2735
+ ];
2736
+ const layout = Object.assign(Object.assign({}, extraLayoutConfig), { title: { text: title }, showlegend: false, autosize: true, width: undefined, height: undefined, hovermode: "x unified", margin: { l: 50, r: 20, t: title ? 50 : 40, b: 50, pad: 4 }, xaxis: Object.assign(Object.assign({}, sharedAxisStyle), { title: { text: xAxisTitle }, domain: [0, 1], anchor: "y", ticklen: 4, tickcolor: "white",
2737
+ //@ts-ignore
2738
+ unifiedhovertitle: {
2739
+ text: "%{x|%B %d, %Y}",
2740
+ } }), xaxis2: Object.assign(Object.assign({}, sharedAxisStyle), { overlaying: "x", matches: "x", side: "top", anchor: "y2", ticklen: 4, tickcolor: "white" }), yaxis: Object.assign(Object.assign({}, sharedAxisStyle), { domain: [0, SPLIT - GAP / 2], anchor: "x", title: {
2741
+ text: yAxisTitle || "",
2742
+ standoff: 12,
2743
+ }, automargin: true, gridcolor: "#efefef", tickcolor: "white", ticklen: 10 }), yaxis2: Object.assign(Object.assign({}, sharedAxisStyle), { domain: [SPLIT + GAP / 2, 1], anchor: "x", showgrid: false, zeroline: false, showline: true, linecolor: "#bababa", linewidth: 1, fixedrange: true, title: {
2744
+ text: y2AxisTitle || "Count",
2745
+ standoff: 12,
2746
+ }, nticks: new Set(lineData.y).size === 1 ? 1 : undefined, tickvals: new Set(lineData.y).size === 1 ? [lineData.y[0]] : undefined }) });
2747
+ const config = {
2748
+ responsive: true,
2749
+ displayModeBar: false,
2750
+ displaylogo: false,
2751
+ scrollZoom: false,
2752
+ staticPlot: false,
2753
+ };
2754
+ return (jsx("div", { style: Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$2, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, style: {
2755
+ width: "100%",
2756
+ height: "100%",
2757
+ display: "block",
2758
+ transition: "opacity 0.15s ease-in-out",
2759
+ }, revision: revision }, `lineplot-${plotId || "default"}`) }) }));
2760
+ };
2761
+
2762
+ const Plot$1 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2632
2763
  const BarPlot = (props) => {
2633
2764
  var _a, _b;
2634
2765
  const { data, data2 = null, data2Selected = [], barWidth = 2, barColor = "rgb(205, 26, 154)", // Default bar color (red)
@@ -3043,7 +3174,7 @@ const BarPlot = (props) => {
3043
3174
  position: "relative",
3044
3175
  width: "100%",
3045
3176
  height: "100%",
3046
- }, children: jsx(Plot, { data: [...plotlyData, ...histogramSubplotData],
3177
+ }, children: jsx(Plot$1, { data: [...plotlyData, ...histogramSubplotData],
3047
3178
  // Type doesn't contain "mirror" prop but it works.
3048
3179
  //@ts-ignore
3049
3180
  layout: layout, config: config, useResizeHandler: true, style: {
@@ -3074,6 +3205,388 @@ const BarPlot = (props) => {
3074
3205
  }, children: [barGroupTooltipTitle, ": ", jsx("b", { children: barGroupTooltip.label })] }))] }));
3075
3206
  };
3076
3207
 
3208
+ const Plot = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
3209
+ const StackedBarPlot = (props) => {
3210
+ const { data, histogramData = null, selectedHistogramData = [], barWidth = 2, barColor = "rgb(205, 26, 154)", // Default bar color (red)
3211
+ barColor2 = "rgba(100, 200, 255, 0.7)", // Default histogram bar color (blue)
3212
+ selectedHistogramDataColor = "rgba(255, 0, 0, 0.5)", // Default color for the histogram selection box (red)
3213
+ title = "", title2 = "", // Default title for histogram subplot
3214
+ xAxisTitle = "X Axis", yAxisTitle = "", xAxis2Title = "", yAxis2Title = "", xAccessor = (d) => d.x, yAccessor = (d) => d.y, containerStyleOverrides = {}, plotId = "bar-plot", xAnnotations = [], barHoverTemplate = "", // Default hover template for bars
3215
+ d3FormatValueString = ".1f", } = props;
3216
+ // Ref for plot container
3217
+ const containerRef = useRef(null);
3218
+ const plotMetaRef = useRef(null);
3219
+ // px above the top of the plot area — lands between x2 tick labels (~15px) and x2 title (~35px)
3220
+ const X_ANNOTATION_TOP_PX = 22;
3221
+ // Tracks the rendered plot area height so we can convert the shape's fixed pixel
3222
+ // center back into a paper y coordinate for the annotation.
3223
+ const [plotAreaHeight, setPlotAreaHeight] = useState(300);
3224
+ const capturePlotMeta = (_figure, graphDiv) => {
3225
+ var _a, _b, _c, _d, _e, _f;
3226
+ try {
3227
+ const m = (_a = graphDiv._fullLayout) === null || _a === void 0 ? void 0 : _a.margin;
3228
+ const r = (_c = (_b = graphDiv._fullLayout) === null || _b === void 0 ? void 0 : _b.xaxis) === null || _c === void 0 ? void 0 : _c.range;
3229
+ const h = (_d = graphDiv._fullLayout) === null || _d === void 0 ? void 0 : _d.height;
3230
+ if (m && r) {
3231
+ plotMetaRef.current = {
3232
+ xRange: r,
3233
+ margin: { l: m.l, r: m.r, t: m.t, b: m.b },
3234
+ height: (_f = h !== null && h !== void 0 ? h : (_e = containerRef.current) === null || _e === void 0 ? void 0 : _e.clientHeight) !== null && _f !== void 0 ? _f : 400,
3235
+ };
3236
+ if (h) {
3237
+ const newPlotH = h - m.t - m.b;
3238
+ if (newPlotH > 0)
3239
+ setPlotAreaHeight(newPlotH);
3240
+ }
3241
+ }
3242
+ }
3243
+ catch (_g) { }
3244
+ };
3245
+ const y2AxisPosition = 0.72; // Position of the secondary y-axis (histogram) as a fraction of the plot height
3246
+ const plotlyData = data.map((series, index) => {
3247
+ var _a;
3248
+ return ({
3249
+ x: series.data.map((point) => xAccessor(point)),
3250
+ y: series.data.map((point) => yAccessor(point)),
3251
+ customdata: series.data.map((point) => [xAccessor(point) + barWidth]),
3252
+ xaxis: index === 0 ? "x" : `x${index + 2}`, // x, x3, x4, ... (x2 reserved for histogram)
3253
+ yaxis: index === 0 ? "y" : `y${index + 1}`, // Assign y-axis for each series
3254
+ type: "bar",
3255
+ marker: {
3256
+ color: "white", // Use the provided bar color
3257
+ line: {
3258
+ color: (_a = series.color) !== null && _a !== void 0 ? _a : barColor,
3259
+ width: 2,
3260
+ },
3261
+ },
3262
+ width: barWidth, // width centered on the x value.
3263
+ hovertemplate: barHoverTemplate || "%{y}", // Use provided hover template or default to showing y value
3264
+ });
3265
+ });
3266
+ const histogramSubplotData = histogramData
3267
+ ? [
3268
+ {
3269
+ x: histogramData,
3270
+ type: "histogram",
3271
+ marker: {
3272
+ color: barColor2,
3273
+ line: {
3274
+ color: "white",
3275
+ width: 0.5,
3276
+ },
3277
+ },
3278
+ xaxis: "x2", // Use secondary x-axis for histogram
3279
+ yaxis: `y${data.length + 1}`, // Use secondary y-axis for histogram
3280
+ hovertemplate: "[%{x})<br>Count: %{y}<extra></extra>", // Custom hover text
3281
+ },
3282
+ ]
3283
+ : [];
3284
+ const selectedHistogramShape = selectedHistogramData.length === 2 &&
3285
+ selectedHistogramData[0] < selectedHistogramData[1]
3286
+ ? [
3287
+ {
3288
+ type: "rect",
3289
+ x0: selectedHistogramData[0],
3290
+ x1: selectedHistogramData[1],
3291
+ y0: y2AxisPosition, // Start at the top of the histogram subplot
3292
+ y1: 1,
3293
+ yref: "paper",
3294
+ line: {
3295
+ width: 2,
3296
+ color: selectedHistogramDataColor,
3297
+ dash: "dot",
3298
+ },
3299
+ layer: "above", // Ensure the selection box is above the bars
3300
+ },
3301
+ ]
3302
+ : [];
3303
+ // Bar subplots fill the full height when there's no histogram; otherwise they share the bottom portion.
3304
+ const barAreaTop = histogramData ? y2AxisPosition : 1;
3305
+ // Title background shape (when histogramData exists)
3306
+ const titleBackgroundShapes = data.flatMap((series, index) => {
3307
+ var _a, _b;
3308
+ return [
3309
+ {
3310
+ type: "rect",
3311
+ xref: "paper",
3312
+ yref: "paper",
3313
+ x0: 0,
3314
+ y0: (barAreaTop / data.length) * (index + 1) - 0.08, // Position behind the title of each series
3315
+ x1: series.name.length * 0.014, // Approximate width based on title2 length
3316
+ y1: (barAreaTop / data.length) * (index + 1) - 0.02,
3317
+ fillcolor: "rgba(255, 255, 255, 0.5)",
3318
+ line: {
3319
+ color: "rgba(255, 255, 255, 0.5)",
3320
+ width: 0,
3321
+ },
3322
+ layer: "above",
3323
+ },
3324
+ {
3325
+ type: "rect",
3326
+ xref: "paper",
3327
+ yref: "paper",
3328
+ x0: 0,
3329
+ y0: (barAreaTop / data.length) * (index + 1) - 0.08, // Position behind the title of each series
3330
+ x1: 0.01,
3331
+ y1: (barAreaTop / data.length) * (index + 1) - 0.02,
3332
+ fillcolor: (_a = series.color) !== null && _a !== void 0 ? _a : "rgba(255, 255, 255, 0.5)",
3333
+ line: {
3334
+ color: (_b = series.color) !== null && _b !== void 0 ? _b : "rgba(255, 255, 255, 0.5)",
3335
+ width: 0,
3336
+ },
3337
+ layer: "above",
3338
+ },
3339
+ ];
3340
+ });
3341
+ // Add title background shape for histogram
3342
+ titleBackgroundShapes.push({
3343
+ type: "rect",
3344
+ xref: "paper",
3345
+ yref: "paper",
3346
+ x0: 0,
3347
+ y0: 0.96, // Position behind the primary title
3348
+ x1: 0.02 + title.length * 0.01, // Approximate width based on title length 0.3/
3349
+ y1: 1,
3350
+ fillcolor: "rgba(255, 255, 255, 0.5)",
3351
+ line: {
3352
+ color: "rgba(200, 200, 200, 0.5)",
3353
+ width: 0,
3354
+ },
3355
+ layer: "above",
3356
+ });
3357
+ // Title annotation
3358
+ const titleAnnotations = data.map((series, index) => {
3359
+ return {
3360
+ xref: "paper",
3361
+ yref: "paper",
3362
+ x: 0.01,
3363
+ xshift: 2,
3364
+ y: (barAreaTop / data.length) * (index + 1) - 0.05,
3365
+ xanchor: "left",
3366
+ yanchor: "middle",
3367
+ text: series.name,
3368
+ showarrow: false,
3369
+ font: {
3370
+ size: 16,
3371
+ color: "#333",
3372
+ weight: 500,
3373
+ },
3374
+ };
3375
+ });
3376
+ titleAnnotations.push({
3377
+ xref: "paper",
3378
+ yref: "paper",
3379
+ x: 0.01,
3380
+ y: 0.96,
3381
+ xshift: 2,
3382
+ xanchor: "left",
3383
+ yanchor: "middle",
3384
+ text: title2,
3385
+ showarrow: false,
3386
+ font: {
3387
+ size: 16,
3388
+ color: "#333",
3389
+ weight: 500,
3390
+ },
3391
+ });
3392
+ const plotlyYaxes = data.map((_series, index) => ({
3393
+ // title: {
3394
+ // text: yAxisTitle ?? "Count",
3395
+ // standoff: 15,
3396
+ // },
3397
+ domain: [
3398
+ (barAreaTop / data.length) * index,
3399
+ (barAreaTop / data.length) * (index + 1) - 0.02,
3400
+ ],
3401
+ hoverformat: d3FormatValueString,
3402
+ nticks: 3,
3403
+ automargin: true,
3404
+ showgrid: true,
3405
+ zeroline: false,
3406
+ showline: true,
3407
+ mirror: "ticks",
3408
+ gridcolor: "#efefef",
3409
+ gridwidth: 0.2,
3410
+ zerolinecolor: "#969696",
3411
+ zerolinewidth: 1,
3412
+ linecolor: "#bababa",
3413
+ linewidth: 1,
3414
+ fixedrange: true,
3415
+ ticksuffix: " ",
3416
+ }));
3417
+ if (histogramData) {
3418
+ plotlyYaxes.push({
3419
+ title: {
3420
+ text: yAxis2Title,
3421
+ standoff: 12,
3422
+ },
3423
+ domain: [y2AxisPosition, 1],
3424
+ automargin: true,
3425
+ showgrid: true,
3426
+ zeroline: false,
3427
+ showline: true,
3428
+ mirror: "ticks",
3429
+ gridcolor: "#efefef",
3430
+ gridwidth: 0.2,
3431
+ zerolinecolor: "#969696",
3432
+ zerolinewidth: 1,
3433
+ linecolor: "#bababa",
3434
+ linewidth: 1,
3435
+ fixedrange: true,
3436
+ ticksuffix: " ",
3437
+ });
3438
+ }
3439
+ const layout = Object.assign(Object.assign(Object.assign(Object.assign({ showlegend: false, autosize: true, width: undefined, height: undefined, margin: {
3440
+ l: 50,
3441
+ r: 35, // Balance between ensuring the mean annotation doesn't get cut off and having too much margin.
3442
+ t: 40 + (title ? 50 : 0), // Add extra top margin if there is a title
3443
+ b: 70,
3444
+ pad: 4,
3445
+ }, xaxis: {
3446
+ title: {
3447
+ text: xAxisTitle,
3448
+ },
3449
+ anchor: "y", // Anchor to primary y-axis
3450
+ hoverformat: d3FormatValueString, // Format hover values using provided d3 format string
3451
+ showgrid: true,
3452
+ zeroline: false,
3453
+ showline: true,
3454
+ mirror: "ticks",
3455
+ gridcolor: "#efefef",
3456
+ gridwidth: 0.2,
3457
+ zerolinecolor: "#969696",
3458
+ zerolinewidth: 1,
3459
+ linecolor: "#bababa",
3460
+ linewidth: 1,
3461
+ fixedrange: true, // Disable zooming
3462
+ automargin: true, // Adjust margin if tick labels rotate
3463
+ ticklen: 8, // tick length in px.
3464
+ tickcolor: "white",
3465
+ tickformat: d3FormatValueString, // Format ticks using provided d3 format string
3466
+ rangemode: "tozero", // Ensure x-axis starts at zero
3467
+ } }, (histogramData && {
3468
+ xaxis2: {
3469
+ title: {
3470
+ text: xAxis2Title,
3471
+ standoff: 22,
3472
+ },
3473
+ side: "top",
3474
+ anchor: `y${data.length + 1}`, // Anchor to the secondary y-axis (histogram)
3475
+ showgrid: true,
3476
+ zeroline: false,
3477
+ showline: true,
3478
+ mirror: "ticks",
3479
+ showticklabels: true, // Show x-axis tick labels for histogram
3480
+ gridcolor: "#efefef",
3481
+ gridwidth: 0.2,
3482
+ zerolinecolor: "#969696",
3483
+ zerolinewidth: 1,
3484
+ linecolor: "#bababa",
3485
+ linewidth: 1,
3486
+ fixedrange: true,
3487
+ automargin: true,
3488
+ matches: "x", // Match the range of xaxis to keep them synchronized
3489
+ tickformat: d3FormatValueString, // Format ticks using provided d3 format string
3490
+ rangemode: "tozero",
3491
+ },
3492
+ })), plotlyYaxes.reduce((acc, yaxis, index) => {
3493
+ acc[`yaxis${index === 0 ? "" : index + 1}`] = yaxis;
3494
+ return acc;
3495
+ }, {})), data.slice(1).reduce((acc, _series, i) => {
3496
+ acc[`xaxis${i + 3}`] = {
3497
+ anchor: `y${i + 2}`,
3498
+ matches: "x",
3499
+ showticklabels: false,
3500
+ showgrid: true,
3501
+ zeroline: false,
3502
+ showline: true,
3503
+ mirror: "ticks",
3504
+ gridcolor: "#efefef",
3505
+ gridwidth: 0.2,
3506
+ zerolinecolor: "#969696",
3507
+ zerolinewidth: 1,
3508
+ linecolor: "#bababa",
3509
+ linewidth: 1,
3510
+ fixedrange: true,
3511
+ automargin: true,
3512
+ ticklen: 8,
3513
+ tickcolor: "white",
3514
+ tickformat: d3FormatValueString,
3515
+ rangemode: "tozero",
3516
+ };
3517
+ return acc;
3518
+ }, {})), { bargap: 0.03, shapes: [
3519
+ ...titleBackgroundShapes,
3520
+ ...selectedHistogramShape,
3521
+ ...xAnnotations.map((annotation) => ({
3522
+ type: "line",
3523
+ x0: annotation.x,
3524
+ x1: annotation.x,
3525
+ y0: 0,
3526
+ y1: 1 + (X_ANNOTATION_TOP_PX - 4) / plotAreaHeight,
3527
+ xref: "x",
3528
+ yref: "paper",
3529
+ line: {
3530
+ color: annotation.color || "rgba(255, 0, 0, 0.7)",
3531
+ width: 3,
3532
+ },
3533
+ })),
3534
+ ], annotations: [
3535
+ ...titleAnnotations,
3536
+ ...(yAxisTitle
3537
+ ? [
3538
+ {
3539
+ xref: "paper",
3540
+ yref: "paper",
3541
+ x: 0,
3542
+ xshift: -45,
3543
+ y: barAreaTop / 2,
3544
+ xanchor: "center",
3545
+ yanchor: "middle",
3546
+ text: yAxisTitle,
3547
+ showarrow: false,
3548
+ textangle: -90,
3549
+ font: { size: 13, color: "#444" },
3550
+ },
3551
+ ]
3552
+ : []),
3553
+ ...xAnnotations.map((annotation) => ({
3554
+ x: annotation.x,
3555
+ y: 1 + X_ANNOTATION_TOP_PX / plotAreaHeight,
3556
+ xref: "x",
3557
+ yref: "paper",
3558
+ text: annotation.text || "",
3559
+ showarrow: false,
3560
+ yanchor: "bottom",
3561
+ font: {
3562
+ color: annotation.color || "rgba(255, 0, 0, 0.7)",
3563
+ size: 12,
3564
+ },
3565
+ })),
3566
+ ] });
3567
+ const config = {
3568
+ responsive: true, // Make the plot responsive
3569
+ displayModeBar: false, // Hide the mode bar
3570
+ displaylogo: false, // Hide the Plotly logo
3571
+ scrollZoom: false, // Disable zooming with scroll
3572
+ staticPlot: false, // Enable interactivity
3573
+ };
3574
+ const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
3575
+ return (jsx("div", { ref: containerRef, className: `plot-container histogram ${plotId}`, style: Object.assign({}, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx("div", { style: {
3576
+ position: "relative",
3577
+ width: "100%",
3578
+ height: "100%",
3579
+ }, children: jsx(Plot, { data: [...plotlyData, ...histogramSubplotData],
3580
+ // Type doesn't contain "mirror" prop but it works.
3581
+ //@ts-ignore
3582
+ layout: layout, config: config, useResizeHandler: true, style: {
3583
+ width: "100%",
3584
+ height: "100%",
3585
+ display: "block",
3586
+ transition: "opacity 0.15s ease-in-out",
3587
+ }, onInitialized: capturePlotMeta, onUpdate: capturePlotMeta }, `bar-${plotId || "default"}`) }) }) }));
3588
+ };
3589
+
3077
3590
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3078
3591
 
3079
3592
  function getDefaultExportFromCjs (x) {
@@ -100555,5 +101068,5 @@ var reactPlotlyWrapper$1 = /*#__PURE__*/Object.freeze({
100555
101068
  default: reactPlotlyWrapper
100556
101069
  });
100557
101070
 
100558
- export { BarPlot, BoxPlot, HistogramPlot, LegendColorItem, LegendLineItem, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, SplitBoxPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
101071
+ export { BarPlot, BoxPlot, HistogramPlot, LegendColorItem, LegendLineItem, LineOnHistogram, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, SplitBoxPlot, StackedBarPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
100559
101072
  //# sourceMappingURL=index.esm.js.map