td-plots 1.10.0 → 1.10.2

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
@@ -1515,31 +1515,17 @@ const BoxPlot = (props) => {
1515
1515
  }; // Values map to x because the boxplot is horizontal
1516
1516
  return Object.assign({ type: "box", orientation: "h", name: trace.label, fillcolor: trace.fill === "none" ? "rgba(255, 255, 255, 0)" : undefined, line: {
1517
1517
  color: trace.color || "#1f77b4", // Default to Plotly's default blue if no color provided
1518
- }, boxpoints: false, hoverinfo: "none" }, boxDefinition);
1518
+ }, boxpoints: false,
1519
+ // hoverinfo: "none", // Disable default hover
1520
+ width: trace.width }, boxDefinition);
1519
1521
  });
1520
1522
  const layout = Object.assign(Object.assign({}, extraLayoutConfig), { title: {
1521
1523
  text: title,
1522
- }, showlegend: false, autosize: true, width: undefined, height: undefined, margin: Object.assign({ l: 50, r: 35, t: 50, b: 50, pad: 4 }, extraLayoutConfig.margin), xaxis: {
1523
- title: {
1524
+ }, showlegend: false, autosize: true, width: undefined, height: undefined, margin: Object.assign({ l: 50, r: 35, t: 50, b: 50, pad: 4 }, extraLayoutConfig.margin), xaxis: Object.assign({ title: {
1524
1525
  text: xAxisTitle,
1525
- },
1526
+ },
1526
1527
  // range: displayXAxis, // Fixed range prevents axis shifting during interaction or data updates
1527
- showgrid: true,
1528
- zeroline: false,
1529
- showline: true,
1530
- mirror: "ticks",
1531
- gridcolor: "#efefef",
1532
- gridwidth: 0.2,
1533
- zerolinecolor: "#969696",
1534
- zerolinewidth: 1,
1535
- linecolor: "#bababa",
1536
- linewidth: 1,
1537
- fixedrange: true, // Disable zooming
1538
- ticklabelposition: "outside",
1539
- // tickformat: isDateArray(data) ? dateTickFormat : d3FormatValueString, // Format ticks for dates
1540
- // automargin: true, // Adjust margin if tick labels rotate
1541
- // hoverformat: isNumberArray(allData) ? d3FormatValueString : undefined,
1542
- }, yaxis: Object.assign({ title: {
1528
+ showgrid: true, zeroline: false, showline: true, mirror: "ticks", gridcolor: "#efefef", gridwidth: 0.2, zerolinecolor: "#969696", zerolinewidth: 1, linecolor: "#bababa", linewidth: 1, fixedrange: true, ticklabelposition: "outside" }, extraLayoutConfig.xaxis), yaxis: Object.assign({ title: {
1543
1529
  text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
1544
1530
  standoff: 12, // Add space between title and axis
1545
1531
  }, automargin: true, showgrid: true, zeroline: false, showline: true, mirror: "ticks", gridcolor: "#efefef", gridwidth: 0.2, zerolinecolor: "#969696", zerolinewidth: 1, linecolor: "#bababa", linewidth: 1, fixedrange: true, tickcolor: "white", ticklen: 10, ticksuffix: " " }, extraLayoutConfig.yaxis) });
@@ -1594,6 +1580,7 @@ function isBoxPlotDataSummary(value) {
1594
1580
  (value.mean === undefined || typeof value.mean === "number"));
1595
1581
  }
1596
1582
 
1583
+ const BOX_SIZE = 15;
1597
1584
  const SVGBoxWithLine = (props) => {
1598
1585
  const boxWidth = 35;
1599
1586
  const boxHeight = 15;
@@ -1602,12 +1589,45 @@ const SVGBoxWithLine = (props) => {
1602
1589
  return (jsxs("svg", { width: lineWidth, height: boxHeight, viewBox: `0 0 ${lineWidth + 3} ${boxHeight + 3}`, children: [jsx("line", { x1: 0, y1: lineY, x2: (lineWidth - boxWidth) / 2, y2: lineY, stroke: props.color, strokeWidth: "2" }), jsx("line", { x1: (lineWidth + boxWidth) / 2, y1: lineY, x2: lineWidth, y2: lineY, stroke: props.color, strokeWidth: "2" }), jsx("line", { x1: 0, y1: lineY - 5, x2: 0, y2: lineY + 5, stroke: props.color, strokeWidth: "2" }), jsx("line", { x1: lineWidth, y1: lineY - 5, x2: lineWidth, y2: lineY + 5, stroke: props.color, strokeWidth: "2" }), jsx("rect", { x: (lineWidth - boxWidth) / 2, y: "0", width: boxWidth, height: boxHeight, fill: props.boxStyle !== "outlined" ? props.color : "none", opacity: props.boxStyle === "shaded" ? 0.3 : 1 }), jsx("rect", { x: (lineWidth - boxWidth) / 2, y: "0", width: boxWidth, height: boxHeight, stroke: props.color, strokeWidth: "2", fill: "none" }), jsx("line", { x1: (lineWidth - boxWidth) / 2 + boxWidth * 0.5, y1: 0, x2: (lineWidth - boxWidth) / 2 + boxWidth * 0.5, y2: boxHeight, stroke: props.color, strokeWidth: "2" })] }));
1603
1590
  };
1604
1591
  const LegendBoxPlotItem = (props) => {
1592
+ const labelKey = props.label == null
1593
+ ? ""
1594
+ : typeof props.label === "string"
1595
+ ? props.label
1596
+ : props.label.toString();
1597
+ return (jsxs(Box, { sx: {
1598
+ display: "flex",
1599
+ alignItems: "center",
1600
+ mr: 4,
1601
+ fontFamily: "Open Sans, verdana, arial, sans-serif",
1602
+ }, children: [jsx(SVGBoxWithLine, { color: props.color, boxStyle: props.boxStyle }), props.label] }, labelKey));
1603
+ };
1604
+ const LegendColorItem = (props) => {
1605
+ const labelKey = props.label == null
1606
+ ? ""
1607
+ : typeof props.label === "string"
1608
+ ? props.label
1609
+ : props.label.toString();
1610
+ return (jsxs(Box, { sx: {
1611
+ display: "flex",
1612
+ alignItems: "center",
1613
+ mr: 4,
1614
+ fontFamily: "Open Sans, verdana, arial, sans-serif",
1615
+ }, children: [jsx(Box, { sx: Object.assign({ width: props.width || BOX_SIZE, height: props.height || BOX_SIZE, backgroundColor: props.color, marginRight: 1 }, props.styleOverrides) }), props.label] }, labelKey));
1616
+ };
1617
+ const LegendLineItem = (props) => {
1618
+ const lineWidth = props.width || 40;
1619
+ const lineHeight = props.height || 4;
1620
+ const labelKey = props.label == null
1621
+ ? ""
1622
+ : typeof props.label === "string"
1623
+ ? props.label
1624
+ : props.label.toString();
1605
1625
  return (jsxs(Box, { sx: {
1606
1626
  display: "flex",
1607
1627
  alignItems: "center",
1608
1628
  mr: 4,
1609
1629
  fontFamily: "Open Sans, verdana, arial, sans-serif",
1610
- }, children: [jsx(SVGBoxWithLine, { color: props.color, boxStyle: props.boxStyle }), props.label] }, props.label));
1630
+ }, children: [jsx("svg", { width: lineWidth, height: lineHeight, style: { marginRight: 8 }, children: jsx("line", { x1: 0, y1: lineHeight / 2, x2: lineWidth, y2: lineHeight / 2, stroke: props.color, strokeWidth: lineHeight }) }), props.label] }, labelKey));
1611
1631
  };
1612
1632
 
1613
1633
  // This component takes grouped data and transforms it for displaying with the BoxPlot component.
@@ -1618,10 +1638,12 @@ const PairedComparisonsBoxPlot = (props) => {
1618
1638
  // Transform the grouped data into an array for BoxPlot
1619
1639
  const boxPlotData = groups.flatMap((group, groupIndex) => {
1620
1640
  const groupYPosition = groupIndex; // Position the group on the y-axis based on its index
1621
- group.color || "orange";
1622
- return group.boxes.map((box, boxIndex) => (Object.assign(Object.assign({}, box), {
1623
- // color: box.color || groupColor, // Use box color if provided, otherwise use group color
1624
- color: "#75757f", fill: boxIndex % 2 === 0 ? "none" : "auto", y: groupYPosition + 0.15 + (boxIndex - 1) * 0.3 })));
1641
+ const groupColor = group.color || "orange";
1642
+ return group.boxes.map((box, boxIndex) => (Object.assign(Object.assign({}, box), { color: box.color || groupColor, width: 0.28, fill: boxIndex % 2 === 0 ? "none" : "auto", y: boxIndex % 2 === 0 &&
1643
+ Array.isArray(group.boxes[1].values) &&
1644
+ group.boxes[1].values.length === 0
1645
+ ? groupYPosition
1646
+ : groupYPosition + 0.25 + (boxIndex - 1) * 0.5 })));
1625
1647
  });
1626
1648
  // We have to construct nice ticks. We can position them with the group indices.
1627
1649
  const tickvals = groups.map((_, index) => index);
@@ -1636,15 +1658,15 @@ const PairedComparisonsBoxPlot = (props) => {
1636
1658
  y0: groupIndex - 0.5,
1637
1659
  y1: groupIndex + 0.5,
1638
1660
  yref: "y",
1639
- fillcolor: groupIndex % 2 === 0 ? "#ffffff" : "#f8f8f8",
1640
- opacity: 0.05,
1661
+ fillcolor: groupIndex % 2 === 0 ? "#ffffff" : "#e7e5e5",
1662
+ opacity: 0.2,
1641
1663
  layer: "below",
1642
1664
  line: {
1643
1665
  width: 0,
1644
1666
  },
1645
1667
  };
1646
1668
  });
1647
- const groupAnnotations = groups.map((group, groupIndex) => ({
1669
+ groups.map((group, groupIndex) => ({
1648
1670
  type: "line",
1649
1671
  yref: "y",
1650
1672
  xref: "paper",
@@ -1659,8 +1681,23 @@ const PairedComparisonsBoxPlot = (props) => {
1659
1681
  }));
1660
1682
  const differenceAnnotations = [];
1661
1683
  const differenceBetweenMediansLines = [];
1684
+ // Find reasonable max and min for the x axis.
1685
+ let globalMin = Infinity;
1686
+ let globalMax = -Infinity;
1662
1687
  groups.forEach((group, groupIndex) => {
1663
- // console.log(group.boxes);
1688
+ // Update global min and max
1689
+ group.boxes.forEach((box) => {
1690
+ if (isBoxPlotDataSummary(box.values)) {
1691
+ globalMin = Math.min(globalMin, box.values.lowerWhisker);
1692
+ globalMax = Math.max(globalMax, box.values.upperWhisker);
1693
+ }
1694
+ else if (Array.isArray(box.values)) {
1695
+ const boxMin = Math.min(...box.values);
1696
+ const boxMax = Math.max(...box.values);
1697
+ globalMin = Math.min(globalMin, boxMin);
1698
+ globalMax = Math.max(globalMax, boxMax);
1699
+ }
1700
+ });
1664
1701
  if ((!isBoxPlotDataSummary(group.boxes[0].values) &&
1665
1702
  group.boxes[0].values.length == 0) ||
1666
1703
  (!isBoxPlotDataSummary(group.boxes[1].values) &&
@@ -1702,14 +1739,15 @@ const PairedComparisonsBoxPlot = (props) => {
1702
1739
  xref: "x",
1703
1740
  x: medianB > medianA ? medianB : medianA, // Position the annotation at the larger median
1704
1741
  y: groupIndex, // Align with the center of the group
1705
- text: ` ${differenceBetweenMedians.toFixed(1)}`,
1742
+ text: ` ${differenceBetweenMedians.toFixed(1)}${props.unit ? ` ${props.unit}` : ""} `, // Show the difference between the medians with optional unit
1706
1743
  showarrow: false,
1707
1744
  xanchor: "left",
1708
1745
  align: "left",
1709
1746
  font: {
1710
- size: 12,
1747
+ size: 16,
1711
1748
  color: annotationColor,
1712
1749
  style: "italic",
1750
+ weight: "bold",
1713
1751
  },
1714
1752
  });
1715
1753
  differenceBetweenMediansLines.push({
@@ -1761,6 +1799,20 @@ const PairedComparisonsBoxPlot = (props) => {
1761
1799
  range: [-0.5, groups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
1762
1800
  tickcolor: "#ffffff",
1763
1801
  showgrid: false,
1802
+ ticklabelposition: "inside",
1803
+ //@ts-ignore
1804
+ ticklabelstandoff: 10,
1805
+ ticksuffix: " ", // Add space between ticks and data
1806
+ title: {
1807
+ text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
1808
+ standoff: 45,
1809
+ },
1810
+ },
1811
+ xaxis: {
1812
+ insiderange: [
1813
+ globalMin - (globalMax - globalMin) * 0.1,
1814
+ globalMax + (globalMax - globalMin) * 0.1,
1815
+ ], // Set the x-axis range based on the global min and max values with some padding
1764
1816
  },
1765
1817
  margin: {
1766
1818
  t: 5,
@@ -1769,7 +1821,7 @@ const PairedComparisonsBoxPlot = (props) => {
1769
1821
  shapes: [
1770
1822
  ...separatorShapes,
1771
1823
  ...differenceBetweenMediansLines,
1772
- ...groupAnnotations,
1824
+ // ...groupAnnotations,
1773
1825
  ],
1774
1826
  annotations: differenceAnnotations,
1775
1827
  };
@@ -1780,7 +1832,7 @@ const PairedComparisonsBoxPlot = (props) => {
1780
1832
 
1781
1833
  const Plot$2 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
1782
1834
  const SummaryComparisonPlot = (props) => {
1783
- const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, } = props;
1835
+ const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, unit = "", } = props;
1784
1836
  // Ref for plot container
1785
1837
  const containerRef = useRef(null);
1786
1838
  // State for custom tooltip
@@ -1862,6 +1914,8 @@ const SummaryComparisonPlot = (props) => {
1862
1914
  const handleUnhover = () => {
1863
1915
  setTooltip((prev) => (Object.assign(Object.assign({}, prev), { visible: false })));
1864
1916
  };
1917
+ const comparedLines = [];
1918
+ const comparedAnnotations = [];
1865
1919
  // Transform the data into a format suitable for a plotly scatterplot
1866
1920
  const plotlyData = groups.flatMap((group, groupIndex) => {
1867
1921
  const traces = [];
@@ -1872,10 +1926,10 @@ const SummaryComparisonPlot = (props) => {
1872
1926
  mode: "lines",
1873
1927
  name: group.groupLabel,
1874
1928
  x: [group.data.summarizedMin, group.data.summarizedMax],
1875
- y: [0 + groupIndex * 0.1, 0 + groupIndex * 0.1],
1929
+ y: [groupIndex * 0.1, groupIndex * 0.1],
1876
1930
  line: {
1877
1931
  color: colorToRGBA(group.color || "orange", 1),
1878
- width: 2,
1932
+ width: 6,
1879
1933
  },
1880
1934
  showlegend: false,
1881
1935
  hoverinfo: "none",
@@ -1887,9 +1941,9 @@ const SummaryComparisonPlot = (props) => {
1887
1941
  mode: "markers",
1888
1942
  name: group.groupLabel,
1889
1943
  x: [group.data.comparedMedian],
1890
- y: [0 + groupIndex * 0.1],
1944
+ y: [groupIndex * 0.1],
1891
1945
  marker: {
1892
- color: group.color || "orange",
1946
+ color: colorToRGBA(group.color || "orange", 1),
1893
1947
  size: 10,
1894
1948
  symbol: "circle",
1895
1949
  line: {
@@ -1900,9 +1954,45 @@ const SummaryComparisonPlot = (props) => {
1900
1954
  showlegend: false,
1901
1955
  hoverinfo: "none",
1902
1956
  });
1957
+ comparedLines.push({
1958
+ type: "line",
1959
+ name: group.groupLabel,
1960
+ x0: group.data.comparedMedian,
1961
+ x1: group.data.comparedMedian,
1962
+ y0: 0,
1963
+ y1: 1.1,
1964
+ yref: "paper",
1965
+ line: {
1966
+ color: group.color || "orange",
1967
+ width: 2,
1968
+ },
1969
+ });
1970
+ comparedAnnotations.push({
1971
+ x: group.data.comparedMedian,
1972
+ y: 1.08,
1973
+ xref: "x",
1974
+ yref: "paper",
1975
+ text: `${group.data.comparedMedian.toFixed(2)} ${unit}`,
1976
+ showarrow: false,
1977
+ xanchor: "center",
1978
+ yanchor: "bottom",
1979
+ });
1903
1980
  }
1904
1981
  return traces;
1905
1982
  });
1983
+ // Add tick label for the compared annotation value
1984
+ if (comparedAnnotations.length > 0) {
1985
+ comparedAnnotations.push({
1986
+ x: -0.03, // Position to the left of the y-axis
1987
+ y: 1.03,
1988
+ xref: "paper",
1989
+ yref: "paper",
1990
+ text: "My AVG",
1991
+ showarrow: false,
1992
+ xanchor: "right",
1993
+ yanchor: "bottom",
1994
+ });
1995
+ }
1906
1996
  const xRangeMin = groups.reduce((min, group) => {
1907
1997
  var _a, _b;
1908
1998
  return Math.min(min, (_a = group.data.summarizedMin) !== null && _a !== void 0 ? _a : Infinity, (_b = group.data.comparedMedian) !== null && _b !== void 0 ? _b : Infinity);
@@ -1918,10 +2008,9 @@ const SummaryComparisonPlot = (props) => {
1918
2008
  margin: {
1919
2009
  l: 130,
1920
2010
  r: 35, // Balance between ensuring the mean annotation doesn't get cut off and having too much margin.
1921
- t: title ? 50 : 5,
2011
+ t: title ? 70 : 40,
1922
2012
  b: 50,
1923
2013
  pad: 4,
1924
- // ...extraLayoutConfig.margin, // Merge in any extra margin config provided via props
1925
2014
  },
1926
2015
  title: {
1927
2016
  text: title,
@@ -1929,8 +2018,12 @@ const SummaryComparisonPlot = (props) => {
1929
2018
  size: 16,
1930
2019
  },
1931
2020
  xref: "paper",
1932
- x: 0.5,
1933
- xanchor: "center",
2021
+ x: 0,
2022
+ xanchor: "left",
2023
+ yanchor: "bottom",
2024
+ pad: {
2025
+ b: 30,
2026
+ },
1934
2027
  },
1935
2028
  xaxis: {
1936
2029
  title: {
@@ -1975,8 +2068,12 @@ const SummaryComparisonPlot = (props) => {
1975
2068
  range: [-0.08, 0.08 + (groups.length - 1) * 0.1], // Add padding around the groups
1976
2069
  automargin: true,
1977
2070
  tickcolor: "white", // Hide default ticks since we're using them for group labels in the paired comparisons plot
2071
+ //@ts-ignore
2072
+ ticklabelstandoff: 5,
1978
2073
  },
1979
2074
  hovermode: "y",
2075
+ shapes: comparedLines,
2076
+ annotations: comparedAnnotations,
1980
2077
  };
1981
2078
  const containerStyles = Object.assign({ width: "100%", height: height, position: "relative", display: "flex", flexDirection: "column", gap: 0 }, containerStyleOverrides);
1982
2079
  const config = {
@@ -2259,30 +2356,88 @@ const LineWithHistogram = (props) => {
2259
2356
 
2260
2357
  const Plot = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2261
2358
  const BarPlot = (props) => {
2262
- const { data, data2 = null, data2Selected = [], barWidth = 0.8, barColor = "rgb(205, 26, 154)", // Default bar color (red)
2359
+ var _a, _b;
2360
+ const { data, data2 = null, data2Selected = [], barDataWidth = 2, barWidth = 0.8, barColor = "rgb(205, 26, 154)", // Default bar color (red)
2263
2361
  barColor2 = "rgba(100, 200, 255, 0.7)", // Default histogram bar color (blue)
2362
+ data2SelectedColor = "rgba(255, 0, 0, 0.5)", // Default color for the histogram selection box (red)
2264
2363
  width = 600, height = 400, title = "", title2 = "", // Default title for histogram subplot
2265
2364
  xAxisTitle = "X Axis", yAxisTitle = "Y Axis", xAxis2Title = "", yAxis2Title = "", xAccessor = (d) => d.x, yAccessor = (d) => d.y, containerStyleOverrides = {}, plotId = "bar-plot", xAnnotations = [], barHoverTemplate = "", // Default hover template for bars
2365
+ barGroups = [], // Optional array of bar groups for grouping bars by color
2366
+ barGroupTooltipTitle = "Group", // Default title for the bar group tooltip
2266
2367
  } = props;
2267
2368
  // Ref for plot container
2268
2369
  const containerRef = useRef(null);
2370
+ const plotMetaRef = useRef(null);
2371
+ const [barGroupTooltip, setBarGroupTooltip] = useState(null);
2269
2372
  const y2AxisPosition = 0.72; // Position of the secondary y-axis (histogram) as a fraction of the plot height
2373
+ const capturePlotMeta = (_figure, graphDiv) => {
2374
+ var _a, _b, _c;
2375
+ try {
2376
+ const m = (_a = graphDiv._fullLayout) === null || _a === void 0 ? void 0 : _a.margin;
2377
+ const r = (_c = (_b = graphDiv._fullLayout) === null || _b === void 0 ? void 0 : _b.xaxis) === null || _c === void 0 ? void 0 : _c.range;
2378
+ if (m && r) {
2379
+ plotMetaRef.current = {
2380
+ xRange: r,
2381
+ margin: { l: m.l, r: m.r, t: m.t, b: m.b },
2382
+ };
2383
+ }
2384
+ }
2385
+ catch (_d) { }
2386
+ };
2387
+ const handleBarGroupMouseMove = (e) => {
2388
+ var _a;
2389
+ if (!containerRef.current ||
2390
+ barGroups.length === 0 ||
2391
+ !plotMetaRef.current) {
2392
+ setBarGroupTooltip(null);
2393
+ return;
2394
+ }
2395
+ const { xRange, margin } = plotMetaRef.current;
2396
+ const rect = containerRef.current.getBoundingClientRect();
2397
+ const mouseX = e.clientX - rect.left;
2398
+ const mouseY = e.clientY - rect.top;
2399
+ const containerHeight = containerRef.current.clientHeight;
2400
+ const containerWidth = containerRef.current.clientWidth;
2401
+ const plotW = containerWidth - margin.l - margin.r;
2402
+ containerHeight - margin.t - margin.b;
2403
+ const plotBottom = containerHeight - margin.b;
2404
+ // barGroupShapes sit at y0=-0.02, y1=-0.04 in paper coords (just below the x-axis line)
2405
+ const stripTop = plotBottom - 10; // Plot bottom extends further than the x axis. Value here adjusted manually.
2406
+ const stripBottom = plotBottom + 20; // approximate. Doesn't have to be perfect.
2407
+ if (mouseY < stripTop || mouseY > stripBottom) {
2408
+ setBarGroupTooltip(null);
2409
+ return;
2410
+ }
2411
+ const dataX = xRange[0] + ((mouseX - margin.l) / plotW) * (xRange[1] - xRange[0]);
2412
+ const hovered = barGroups.find((g) => dataX >= g.min && dataX <= g.max);
2413
+ setBarGroupTooltip(hovered
2414
+ ? {
2415
+ x: mouseX,
2416
+ y: plotBottom,
2417
+ name: hovered.name,
2418
+ label: (_a = hovered.label) !== null && _a !== void 0 ? _a : hovered.name,
2419
+ color: hovered.color,
2420
+ min: hovered.min,
2421
+ max: hovered.max,
2422
+ }
2423
+ : null);
2424
+ };
2270
2425
  const plotlyData = [
2271
2426
  {
2272
2427
  x: data.map((point) => xAccessor(point)),
2273
2428
  y: data.map((point) => yAccessor(point)),
2274
- // customdata: data.map((point) => [xAccessor(point) + 2]),
2429
+ customdata: data.map((point) => [xAccessor(point) + barDataWidth]),
2275
2430
  yaxis: "y",
2276
2431
  type: "bar",
2277
2432
  marker: {
2278
- color: barColor, // Use the provided bar color
2433
+ color: "white", // Use the provided bar color
2279
2434
  line: {
2280
- color: "white",
2281
- width: 0.5,
2435
+ color: barColor,
2436
+ width: 2,
2282
2437
  },
2283
2438
  },
2284
2439
  width: barWidth, // width centered on the x value.
2285
- // hovertemplate: barHoverTemplate || "%{y}", // Use provided hover template or default to showing y value
2440
+ hovertemplate: barHoverTemplate || "%{y}", // Use provided hover template or default to showing y value
2286
2441
  },
2287
2442
  ];
2288
2443
  const histogramSubplotData = data2
@@ -2299,11 +2454,10 @@ const BarPlot = (props) => {
2299
2454
  },
2300
2455
  xaxis: "x2", // Use secondary x-axis for histogram
2301
2456
  yaxis: "y2", // Place histogram on secondary y-axis
2457
+ hovertemplate: "[%{x})<br>Count: %{y}<extra></extra>", // Custom hover text
2302
2458
  },
2303
2459
  ]
2304
2460
  : [];
2305
- // Create a multiply-like effect by using a semi-transparent dark overlay
2306
- const multiplyColor = "rgba(29, 104, 185, 0.1)";
2307
2461
  const selectedHistogramShape = data2Selected.length === 2
2308
2462
  ? [
2309
2463
  {
@@ -2313,10 +2467,10 @@ const BarPlot = (props) => {
2313
2467
  y0: y2AxisPosition, // Start at the top of the histogram subplot
2314
2468
  y1: 1,
2315
2469
  yref: "paper",
2316
- fillcolor: multiplyColor,
2317
2470
  line: {
2318
- width: 1,
2319
- color: multiplyColor,
2471
+ width: 2,
2472
+ color: data2SelectedColor,
2473
+ dash: "dot",
2320
2474
  },
2321
2475
  layer: "above", // Ensure the selection box is above the bars
2322
2476
  },
@@ -2392,6 +2546,59 @@ const BarPlot = (props) => {
2392
2546
  },
2393
2547
  ]
2394
2548
  : [];
2549
+ const xMax = data.length > 0 ? Math.max(...data.map((d) => xAccessor(d))) : 0;
2550
+ const barGroupShapes = barGroups.flatMap((group) => [
2551
+ {
2552
+ type: "rect",
2553
+ xref: "x",
2554
+ yref: "paper",
2555
+ x0: group.min,
2556
+ x1: group.max > xMax ? xMax + barWidth : group.max,
2557
+ y0: -0.02,
2558
+ y1: -0.07,
2559
+ fillcolor: group.color,
2560
+ line: {
2561
+ width: 1,
2562
+ color: "white",
2563
+ },
2564
+ },
2565
+ ]);
2566
+ const barGroupShapeAnnotation = barGroups.length > 0
2567
+ ? {
2568
+ xref: "x",
2569
+ yref: "paper",
2570
+ x: barGroups[0].min + barDataWidth / 2,
2571
+ y: -0.043,
2572
+ text: barGroupTooltipTitle,
2573
+ showarrow: false,
2574
+ yanchor: "middle",
2575
+ xanchor: "left",
2576
+ font: {
2577
+ size: 14,
2578
+ color: "#fff",
2579
+ weight: 500,
2580
+ },
2581
+ }
2582
+ : null;
2583
+ const barGroupHighlightBox = barGroupTooltip
2584
+ ? [
2585
+ {
2586
+ type: "rect",
2587
+ xref: "x",
2588
+ yref: "paper",
2589
+ x0: barGroupTooltip.min,
2590
+ x1: barGroupTooltip.max > xMax ? xMax + barWidth : barGroupTooltip.max,
2591
+ y0: 0,
2592
+ y1: data2 ? y2AxisPosition - 0.02 : 1,
2593
+ fillcolor: colorToRGBA(barGroupTooltip.color, 0.2),
2594
+ line: {
2595
+ width: 2,
2596
+ color: barGroupTooltip.color,
2597
+ },
2598
+ layer: "above",
2599
+ },
2600
+ ]
2601
+ : [];
2395
2602
  const layout = Object.assign(Object.assign(Object.assign(Object.assign({ title: data2
2396
2603
  ? undefined
2397
2604
  : {
@@ -2418,8 +2625,9 @@ const BarPlot = (props) => {
2418
2625
  linecolor: "#bababa",
2419
2626
  linewidth: 1,
2420
2627
  fixedrange: true, // Disable zooming
2421
- // tickformat: isDateArray(data) ? dateTickFormat : d3FormatValueString, // Format ticks for dates
2422
2628
  automargin: true, // Adjust margin if tick labels rotate
2629
+ ticklen: 21,
2630
+ tickcolor: "white",
2423
2631
  } }, (data2 && {
2424
2632
  xaxis2: {
2425
2633
  title: {
@@ -2484,12 +2692,14 @@ const BarPlot = (props) => {
2484
2692
  })), { bargap: 0.03, shapes: [
2485
2693
  ...titleBackgroundShapes,
2486
2694
  ...selectedHistogramShape,
2695
+ ...barGroupShapes,
2696
+ ...barGroupHighlightBox,
2487
2697
  ...xAnnotations.map((annotation) => ({
2488
2698
  type: "line",
2489
2699
  x0: annotation.x,
2490
2700
  x1: annotation.x,
2491
2701
  y0: 0,
2492
- y1: 1.1,
2702
+ y1: 1.05,
2493
2703
  xref: "x",
2494
2704
  yref: "paper",
2495
2705
  line: {
@@ -2499,9 +2709,10 @@ const BarPlot = (props) => {
2499
2709
  })),
2500
2710
  ], annotations: [
2501
2711
  ...titleAnnotations,
2712
+ ...(barGroupShapeAnnotation ? [barGroupShapeAnnotation] : []),
2502
2713
  ...xAnnotations.map((annotation) => ({
2503
2714
  x: annotation.x,
2504
- y: 1.11,
2715
+ y: 1.06,
2505
2716
  xref: "x",
2506
2717
  yref: "paper",
2507
2718
  text: annotation.text || "",
@@ -2513,7 +2724,6 @@ const BarPlot = (props) => {
2513
2724
  },
2514
2725
  })),
2515
2726
  ] });
2516
- console.log("BarPlot layout:", layout);
2517
2727
  const config = {
2518
2728
  responsive: true, // Make the plot responsive
2519
2729
  displayModeBar: false, // Hide the mode bar
@@ -2522,20 +2732,39 @@ const BarPlot = (props) => {
2522
2732
  staticPlot: false, // Enable interactivity
2523
2733
  };
2524
2734
  const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
2525
- console.log([...plotlyData, ...histogramSubplotData]);
2526
- return (jsx("div", { ref: containerRef, className: `plot-container histogram ${plotId}`, style: Object.assign({}, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx("div", { style: {
2527
- position: "relative",
2528
- width: "100%",
2529
- height: "100%",
2530
- }, children: jsx(Plot, { data: [...plotlyData, ...histogramSubplotData],
2531
- // Type doesn't contain "mirror" prop but it works.
2532
- //@ts-ignore
2533
- layout: layout, config: config, useResizeHandler: true, style: {
2735
+ return (jsxs("div", { ref: containerRef, className: `plot-container histogram ${plotId}`, style: Object.assign(Object.assign({}, containerStyles), { cursor: barGroupTooltip ? "pointer" : "default" }), onMouseMove: handleBarGroupMouseMove, onMouseLeave: () => setBarGroupTooltip(null), children: [jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx("div", { style: {
2736
+ position: "relative",
2534
2737
  width: "100%",
2535
2738
  height: "100%",
2536
- display: "block",
2537
- transition: "opacity 0.15s ease-in-out",
2538
- } }, `bar-${plotId || "default"}`) }) }) }));
2739
+ }, children: jsx(Plot, { data: [...plotlyData, ...histogramSubplotData],
2740
+ // Type doesn't contain "mirror" prop but it works.
2741
+ //@ts-ignore
2742
+ layout: layout, config: config, useResizeHandler: true, style: {
2743
+ width: "100%",
2744
+ height: "100%",
2745
+ display: "block",
2746
+ transition: "opacity 0.15s ease-in-out",
2747
+ }, onInitialized: capturePlotMeta, onUpdate: capturePlotMeta }, `bar-${plotId || "default"}`) }) }), barGroupTooltip && (jsxs("div", { style: {
2748
+ position: "absolute",
2749
+ left: barGroupTooltip.x + 250 >
2750
+ ((_b = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : Infinity)
2751
+ ? barGroupTooltip.x - 250
2752
+ : barGroupTooltip.x + 12,
2753
+ top: barGroupTooltip.y + 10,
2754
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
2755
+ borderRadius: "4px",
2756
+ color: "#333",
2757
+ padding: "8px 12px",
2758
+ fontSize: "13px",
2759
+ lineHeight: "1.5",
2760
+ pointerEvents: "none",
2761
+ zIndex: 1000,
2762
+ whiteSpace: "nowrap",
2763
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
2764
+ borderLeft: `7px solid ${barGroupTooltip.color}`,
2765
+ fontFamily: '"Open Sans", verdana, arial, sans-serif',
2766
+ maxWidth: "250px",
2767
+ }, children: [barGroupTooltipTitle, ": ", jsx("b", { children: barGroupTooltip.label })] }))] }));
2539
2768
  };
2540
2769
 
2541
2770
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -100019,5 +100248,5 @@ var reactPlotlyWrapper$1 = /*#__PURE__*/Object.freeze({
100019
100248
  default: reactPlotlyWrapper
100020
100249
  });
100021
100250
 
100022
- export { BarPlot, BoxPlot, HistogramPlot, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
100251
+ export { BarPlot, BoxPlot, HistogramPlot, LegendColorItem, LegendLineItem, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
100023
100252
  //# sourceMappingURL=index.esm.js.map