td-plots 1.7.2 → 1.8.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.js CHANGED
@@ -8,6 +8,8 @@ var Button = require('@mui/material/Button');
8
8
  var Tooltip = require('@mui/material/Tooltip');
9
9
  var utils = require('@mui/material/utils');
10
10
  var IconButton = require('@mui/material/IconButton');
11
+ var material = require('@mui/material');
12
+ var Box = require('@mui/material/Box');
11
13
 
12
14
  function styleInject(css, ref) {
13
15
  if ( ref === void 0 ) ref = {};
@@ -36,7 +38,7 @@ function styleInject(css, ref) {
36
38
  }
37
39
  }
38
40
 
39
- var css_248z = ".plot-container{height:100%;max-width:100%;min-height:300px;overflow:hidden!important;position:relative;width:100%}.plot-container>div{flex:1;height:100%!important;width:100%!important}.plot-container .main-svg{max-height:100%!important;max-width:100%!important}.plot-container .main-svg,.plot-container .plotly-graph-div,.plot-container svg.main-svg[height],.plot-container svg.main-svg[width]{height:100%!important;width:100%!important}.plot-container .point{border-radius:5px!important;overflow:hidden!important}.plot-container .cursor-ns-resize{height:0;width:0}.plot-container .cursor-ew-resize{fill:var(--selection-color,blue)!important;stroke:var(--selection-color,blue)!important}.plot-container .selectionlayer>path{stroke:var(--selection-color,blue)!important;stroke-dasharray:0!important;stroke-width:1px!important;opacity:.5!important}.plot-container .zoomlayer>path{stroke-dasharray:0!important;stroke:var(--selection-color,blue)!important;fill:var(--selection-color,blue)!important}.radial-histogram-container{aspect-ratio:1}.loading-overlay{align-items:center;background-color:hsla(0,0%,100%,.8);display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%;z-index:300}.histogram-controls{pointer-events:auto}.histogram-controls.show-always{opacity:1;visibility:visible}.histogram-controls.show-on-hover{opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;visibility:hidden}.plot-container:hover .histogram-controls.show-on-hover{opacity:1;visibility:visible}";
41
+ var css_248z = ".plot-container{height:100%;max-width:100%;min-height:300px;overflow:hidden!important;position:relative;width:100%}.plot-container>div{flex:1;width:100%!important}.plot-container .main-svg{max-height:100%!important;max-width:100%!important}.plot-container .main-svg,.plot-container .plotly-graph-div,.plot-container svg.main-svg[height],.plot-container svg.main-svg[width]{height:100%!important;width:100%!important}.plot-container .point{border-radius:5px!important;overflow:hidden!important}.plot-container .cursor-ns-resize{height:0;width:0}.plot-container .cursor-ew-resize{fill:var(--selection-color,blue)!important;stroke:var(--selection-color,blue)!important}.plot-container .selectionlayer>path{stroke:var(--selection-color,blue)!important;stroke-dasharray:0!important;stroke-width:1px!important;opacity:.5!important}.plot-container .zoomlayer>path{stroke-dasharray:0!important;stroke:var(--selection-color,blue)!important;fill:var(--selection-color,blue)!important}.radial-histogram-container{aspect-ratio:1}.loading-overlay{align-items:center;background-color:hsla(0,0%,100%,.8);display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%;z-index:300}.histogram-controls{pointer-events:auto}.histogram-controls.show-always{opacity:1;visibility:visible}.histogram-controls.show-on-hover{opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;visibility:hidden}.histogram .plot-container:hover .histogram-controls.show-on-hover{opacity:1;visibility:visible}";
40
42
  styleInject(css_248z);
41
43
 
42
44
  function formatDecimal(x) {
@@ -424,6 +426,47 @@ const plotlyMToMilliseconds = (mString) => {
424
426
  }
425
427
  return 0;
426
428
  };
429
+ // Helper function to convert color to rgba with specified opacity
430
+ const colorToRGBA = (color, opacity) => {
431
+ // For named colors, create a temporary element to get computed rgb
432
+ // This is a fallback that works in browser environments
433
+ const temp = document.createElement("div");
434
+ temp.style.color = color;
435
+ document.body.appendChild(temp);
436
+ const computed = window.getComputedStyle(temp).color;
437
+ document.body.removeChild(temp);
438
+ const rgbMatch = computed.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
439
+ if (rgbMatch) {
440
+ return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${opacity})`;
441
+ }
442
+ // Fallback: return original color
443
+ return color;
444
+ };
445
+ const computeMedian = (data) => {
446
+ if (data.length === 0) {
447
+ return 0;
448
+ }
449
+ const values = data.sort((a, b) => a - b);
450
+ const mid = Math.floor(values.length / 2);
451
+ return values.length % 2 !== 0
452
+ ? values[mid]
453
+ : (values[mid - 1] + values[mid]) / 2;
454
+ };
455
+ const computeQuartile = (data, quartile) => {
456
+ if (data.length === 0) {
457
+ return 0;
458
+ }
459
+ const values = data.sort((a, b) => a - b);
460
+ const pos = (values.length - 1) * (quartile / 4);
461
+ const base = Math.floor(pos);
462
+ const rest = pos - base;
463
+ if (values[base + 1] !== undefined) {
464
+ return values[base] + rest * (values[base + 1] - values[base]);
465
+ }
466
+ else {
467
+ return values[base];
468
+ }
469
+ };
427
470
 
428
471
  // Loading component renders a circular spinner above an element (usually a plot)
429
472
  const Loading = () => {
@@ -498,7 +541,7 @@ var SettingsIcon = utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
498
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"
499
542
  }), 'Settings');
500
543
 
501
- const Plot$3 = React.lazy(() => import('react-plotly.js'));
544
+ const Plot$6 = React.lazy(() => import('react-plotly.js'));
502
545
  const HistogramPlot = (props) => {
503
546
  var _a, _b, _c, _d;
504
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;
@@ -1133,7 +1176,9 @@ const HistogramPlot = (props) => {
1133
1176
  .map((n) => n * ONEAVGMONTH)
1134
1177
  .concat([defaultBinSize])
1135
1178
  : unitOfTime === "day"
1136
- ? [1, 2, 3, 5, 7, 30].map((n) => n * ONEDAY).concat([defaultBinSize])
1179
+ ? [1, 2, 3, 5, 7, 30]
1180
+ .map((n) => n * ONEDAY)
1181
+ .concat([defaultBinSize])
1137
1182
  : unitOfTime === "hr"
1138
1183
  ? [1, 2, 3, 6, 12, 24]
1139
1184
  .map((n) => n * ONEHOUR)
@@ -1195,7 +1240,7 @@ const HistogramPlot = (props) => {
1195
1240
  return valueLabel;
1196
1241
  };
1197
1242
  }
1198
- return (jsxRuntime.jsx("div", { ref: containerRef, className: `plot-container ${plotId}`, style: Object.assign({ "--selection-color": selectorsColor }, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsxs("div", { style: {
1243
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: `plot-container histogram ${plotId}`, style: Object.assign({ "--selection-color": selectorsColor }, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsxs("div", { style: {
1199
1244
  position: "relative",
1200
1245
  width: "100%",
1201
1246
  height: "100%",
@@ -1248,7 +1293,7 @@ const HistogramPlot = (props) => {
1248
1293
  const newBinSize = newValue;
1249
1294
  setBinSize(newBinSize);
1250
1295
  onBinSizeChange === null || onBinSizeChange === void 0 ? void 0 : onBinSizeChange(newBinSize);
1251
- }, colorOverride: barColor, valueLabelFormat: valueLabelFormat, titleStylingOverrides: settingsTitleStylingOverrides, disabledTooltipText: "Requires 10 or more data points" }) }) })] })), jsxRuntime.jsx(Plot$3, { 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" }) }) })] })), jsxRuntime.jsx(Plot$6, { data: plotlyData, layout: layout, config: config, onSelected: handleSelection, onClick: handleClick, onDeselect: () => {
1252
1297
  onDeselect();
1253
1298
  setSelectedRange(null); // Remove selected box
1254
1299
  }, onUpdate: handlePlotUpdate, useResizeHandler: true, style: {
@@ -1259,7 +1304,7 @@ const HistogramPlot = (props) => {
1259
1304
  } }, `histogram-${plotId || "default"}`)] }) }) }));
1260
1305
  };
1261
1306
 
1262
- const Plot$2 = React.lazy(() => import('react-plotly.js'));
1307
+ const Plot$5 = React.lazy(() => import('react-plotly.js'));
1263
1308
  const RadialHistogramPlot = (props) => {
1264
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
1265
1310
  } = props;
@@ -1359,14 +1404,14 @@ const RadialHistogramPlot = (props) => {
1359
1404
  staticPlot: false,
1360
1405
  };
1361
1406
  const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
1362
- return (jsxRuntime.jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ '--selection-color': selectorsColor }, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsx(Plot$2, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1407
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ '--selection-color': selectorsColor }, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsx(Plot$5, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1363
1408
  width: "100%",
1364
1409
  height: "100%",
1365
1410
  display: "block"
1366
1411
  } }) }) }));
1367
1412
  };
1368
1413
 
1369
- const Plot$1 = React.lazy(() => import('react-plotly.js'));
1414
+ const Plot$4 = React.lazy(() => import('react-plotly.js'));
1370
1415
  const StatsDonut = (props) => {
1371
1416
  const { withCenterLabel = false, centerLabel, centerValue, showLegend = true, legendPosition = "right", containerStyle = {}, } = props;
1372
1417
  // Configure legend position based on prop
@@ -1414,7 +1459,7 @@ const StatsDonut = (props) => {
1414
1459
  },
1415
1460
  ]
1416
1461
  : [];
1417
- return (jsxRuntime.jsx("div", { style: Object.assign({ width: "100%", height: "100%", minHeight: "300px", display: "flex", justifyContent: "center", alignItems: "center" }, containerStyle), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsx(Plot$1, { data: [
1462
+ return (jsxRuntime.jsx("div", { style: Object.assign({ width: "100%", height: "100%", minHeight: "300px", display: "flex", justifyContent: "center", alignItems: "center" }, containerStyle), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsx(Plot$4, { data: [
1418
1463
  {
1419
1464
  type: "pie",
1420
1465
  values: props.values,
@@ -1447,6 +1492,713 @@ const StatsDonut = (props) => {
1447
1492
  }, useResizeHandler: true }) }) }));
1448
1493
  };
1449
1494
 
1495
+ const Plot$3 = React.lazy(() => import('react-plotly.js'));
1496
+ const BoxPlot = (props) => {
1497
+ const { data, width = 600, height = 400, title = "Box Plot", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "boxplot", extraLayoutConfig = {}, } = props;
1498
+ // Ref for plot container
1499
+ const containerRef = React.useRef(null);
1500
+ // State for custom tooltip
1501
+ const [tooltip, setTooltip] = React.useState({ visible: false, x: 0, y: 0, content: "", color: "#1f77b4" });
1502
+ const plotlyData = data.map((trace) => {
1503
+ const boxDefinition = isBoxPlotDataSummary(trace.values)
1504
+ ? {
1505
+ y0: trace.y !== undefined ? trace.y : [trace.label], // Position the box at this y-value
1506
+ lowerfence: [trace.values.lowerWhisker],
1507
+ q1: [trace.values.q1],
1508
+ median: [trace.values.median],
1509
+ q3: [trace.values.q3],
1510
+ upperfence: [trace.values.upperWhisker],
1511
+ mean: trace.values.mean ? [trace.values.mean] : undefined,
1512
+ }
1513
+ : {
1514
+ x: trace.values,
1515
+ y0: trace.y !== undefined ? trace.y : undefined,
1516
+ }; // Values map to x because the boxplot is horizontal
1517
+ return Object.assign({ type: "box", orientation: "h", name: trace.label, fillcolor: trace.fill === "none" ? "rgba(255, 255, 255, 0)" : undefined, line: {
1518
+ color: trace.color || "#1f77b4", // Default to Plotly's default blue if no color provided
1519
+ }, boxpoints: false, hoverinfo: "none" }, boxDefinition);
1520
+ });
1521
+ const layout = Object.assign(Object.assign({}, extraLayoutConfig), { title: {
1522
+ text: title,
1523
+ }, showlegend: false, autosize: true, width: undefined, height: undefined, margin: Object.assign({ l: 50, r: 35, t: 50, b: 50, pad: 4 }, extraLayoutConfig.margin), xaxis: {
1524
+ title: {
1525
+ text: xAxisTitle,
1526
+ },
1527
+ // range: displayXAxis, // Fixed range prevents axis shifting during interaction or data updates
1528
+ showgrid: true,
1529
+ zeroline: false,
1530
+ showline: true,
1531
+ mirror: "ticks",
1532
+ gridcolor: "#efefef",
1533
+ gridwidth: 0.2,
1534
+ zerolinecolor: "#969696",
1535
+ zerolinewidth: 1,
1536
+ linecolor: "#bababa",
1537
+ linewidth: 1,
1538
+ fixedrange: true, // Disable zooming
1539
+ ticklabelposition: "outside",
1540
+ // tickformat: isDateArray(data) ? dateTickFormat : d3FormatValueString, // Format ticks for dates
1541
+ // automargin: true, // Adjust margin if tick labels rotate
1542
+ // hoverformat: isNumberArray(allData) ? d3FormatValueString : undefined,
1543
+ }, yaxis: Object.assign({ title: {
1544
+ text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
1545
+ standoff: 12, // Add space between title and axis
1546
+ }, 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) });
1547
+ const config = {
1548
+ responsive: true, // Make the plot responsive
1549
+ displayModeBar: false, // Hide the mode bar
1550
+ displaylogo: false, // Hide the Plotly logo
1551
+ scrollZoom: false, // Disable zooming with scroll
1552
+ staticPlot: false, // Enable interactivity
1553
+ };
1554
+ const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
1555
+ return (jsxRuntime.jsx("div", { ref: containerRef,
1556
+ // className={`plot-container ${plotId}`}
1557
+ style: Object.assign({}, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsxs("div", { style: {
1558
+ position: "relative",
1559
+ width: "100%",
1560
+ height: "100%",
1561
+ }, children: [jsxRuntime.jsx(Plot$3, { data: plotlyData, layout: layout, config: config, useResizeHandler: true,
1562
+ // onHover={handleHover}
1563
+ // onUnhover={handleUnhover}
1564
+ style: {
1565
+ width: "100%",
1566
+ height: "100%",
1567
+ display: "block",
1568
+ transition: "opacity 0.15s ease-in-out",
1569
+ } }, `boxplot-${plotId || "default"}`), tooltip.visible && (jsxRuntime.jsx("div", { style: {
1570
+ position: "fixed",
1571
+ left: tooltip.x + 10,
1572
+ top: tooltip.y - 10,
1573
+ backgroundColor: "white",
1574
+ color: "#333",
1575
+ padding: "8px 12px",
1576
+ borderRadius: "4px",
1577
+ fontSize: "13px",
1578
+ lineHeight: "1.5",
1579
+ pointerEvents: "none",
1580
+ zIndex: 1000,
1581
+ whiteSpace: "nowrap",
1582
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
1583
+ border: `2px solid ${tooltip.color}`,
1584
+ fontFamily: '"Open Sans", verdana, arial, sans-serif',
1585
+ }, dangerouslySetInnerHTML: { __html: tooltip.content } }))] }) }) }));
1586
+ };
1587
+ function isBoxPlotDataSummary(value) {
1588
+ return (typeof value === "object" &&
1589
+ value !== null &&
1590
+ typeof value.lowerWhisker === "number" &&
1591
+ typeof value.q1 === "number" &&
1592
+ typeof value.median === "number" &&
1593
+ typeof value.q3 === "number" &&
1594
+ typeof value.upperWhisker === "number" &&
1595
+ (value.mean === undefined || typeof value.mean === "number"));
1596
+ }
1597
+
1598
+ const SVGBoxWithLine = (props) => {
1599
+ const boxWidth = 35;
1600
+ const boxHeight = 15;
1601
+ const lineWidth = boxWidth * 2; // 90% width
1602
+ const lineY = boxHeight / 2; // Vertical center
1603
+ return (jsxRuntime.jsxs("svg", { width: lineWidth, height: boxHeight, viewBox: `0 0 ${lineWidth + 3} ${boxHeight + 3}`, children: [jsxRuntime.jsx("line", { x1: 0, y1: lineY, x2: (lineWidth - boxWidth) / 2, y2: lineY, stroke: props.color, strokeWidth: "2" }), jsxRuntime.jsx("line", { x1: (lineWidth + boxWidth) / 2, y1: lineY, x2: lineWidth, y2: lineY, stroke: props.color, strokeWidth: "2" }), jsxRuntime.jsx("line", { x1: 0, y1: lineY - 5, x2: 0, y2: lineY + 5, stroke: props.color, strokeWidth: "2" }), jsxRuntime.jsx("line", { x1: lineWidth, y1: lineY - 5, x2: lineWidth, y2: lineY + 5, stroke: props.color, strokeWidth: "2" }), jsxRuntime.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 }), jsxRuntime.jsx("rect", { x: (lineWidth - boxWidth) / 2, y: "0", width: boxWidth, height: boxHeight, stroke: props.color, strokeWidth: "2", fill: "none" }), jsxRuntime.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" })] }));
1604
+ };
1605
+ // export const OutlinedBox = (props: BoxProps) => {
1606
+ // return (
1607
+ // <Box
1608
+ // border={`2px solid ${props.color}`}
1609
+ // width={BOX_SIZE}
1610
+ // height={BOX_SIZE}
1611
+ // mr={1}
1612
+ // />
1613
+ // );
1614
+ // };
1615
+ // export const FilledBox = (props: BoxProps) => {
1616
+ // return (
1617
+ // <Box
1618
+ // width={BOX_SIZE}
1619
+ // height={BOX_SIZE}
1620
+ // sx={{
1621
+ // backgroundColor: props.color,
1622
+ // border: `2px solid ${props.color}`,
1623
+ // }}
1624
+ // mr={1}
1625
+ // />
1626
+ // );
1627
+ // };
1628
+ // export const ShadedBox = (props: BoxProps) => {
1629
+ // return (
1630
+ // <Box
1631
+ // width={BOX_SIZE}
1632
+ // height={BOX_SIZE}
1633
+ // sx={{
1634
+ // border: `2px solid ${props.color}`,
1635
+ // position: "relative",
1636
+ // "&::before": {
1637
+ // content: '""',
1638
+ // position: "absolute",
1639
+ // top: 0,
1640
+ // left: 0,
1641
+ // right: 0,
1642
+ // bottom: 0,
1643
+ // backgroundColor: `${props.color}`,
1644
+ // opacity: 0.5,
1645
+ // },
1646
+ // }}
1647
+ // mr={1}
1648
+ // />
1649
+ // );
1650
+ // };
1651
+ // export const LegendItem = (props: LegendItemProps) => {
1652
+ // return (
1653
+ // <Box
1654
+ // key={props.label}
1655
+ // display="flex"
1656
+ // alignItems="center"
1657
+ // mr={1}
1658
+ // sx={{
1659
+ // fontFamily: "Open Sans, verdana, arial, sans-serif",
1660
+ // }}
1661
+ // >
1662
+ // {props.boxStyle === "shaded" ? (
1663
+ // <ShadedBox color={props.color} />
1664
+ // ) : props.boxStyle === "outlined" ? (
1665
+ // <OutlinedBox color={props.color} />
1666
+ // ) : (
1667
+ // <FilledBox color={props.color} />
1668
+ // )}
1669
+ // {props.label}
1670
+ // </Box>
1671
+ // );
1672
+ // };
1673
+ const LegendBoxPlotItem = (props) => {
1674
+ return (jsxRuntime.jsxs(Box, { display: "flex", alignItems: "center", mr: 4, sx: {
1675
+ fontFamily: "Open Sans, verdana, arial, sans-serif",
1676
+ }, children: [jsxRuntime.jsx(SVGBoxWithLine, { color: props.color, boxStyle: props.boxStyle }), props.label] }, props.label));
1677
+ };
1678
+
1679
+ // This component takes grouped data and transforms it for displaying with the BoxPlot component.
1680
+ // The highest level is the group (ex, rating bracket), and then within each group are individual boxes (ex, mine vs others' throws within that rating bracket)
1681
+ // Importantly, this component is currently optimized for two boxes per group. It could be extended in the future to allow for more within-group boxes if useful.
1682
+ const PairedComparisonsBoxPlot = (props) => {
1683
+ const { groups, pairLabels, width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, showLegend = true, plotId = "paired-comparisons-boxplot", } = props;
1684
+ // Transform the grouped data into an array for BoxPlot
1685
+ const boxPlotData = groups.flatMap((group, groupIndex) => {
1686
+ const groupYPosition = groupIndex; // Position the group on the y-axis based on its index
1687
+ group.color || "orange";
1688
+ return group.boxes.map((box, boxIndex) => (Object.assign(Object.assign({}, box), {
1689
+ // color: box.color || groupColor, // Use box color if provided, otherwise use group color
1690
+ color: "#75757f", fill: boxIndex % 2 === 0 ? "none" : "auto", y: groupYPosition + 0.15 + (boxIndex - 1) * 0.3 })));
1691
+ });
1692
+ // We have to construct nice ticks. We can position them with the group indices.
1693
+ const tickvals = groups.map((_, index) => index);
1694
+ const ticktext = groups.map((group) => group.groupLabel);
1695
+ // Draw some gray rectangles to live behind the boxes to help separate the groups
1696
+ const separatorShapes = groups.map((group, groupIndex) => {
1697
+ return {
1698
+ type: "rect",
1699
+ x0: 0,
1700
+ x1: 1,
1701
+ xref: "paper",
1702
+ y0: groupIndex - 0.5,
1703
+ y1: groupIndex + 0.5,
1704
+ yref: "y",
1705
+ fillcolor: groupIndex % 2 === 0 ? "#ffffff" : "#f8f8f8",
1706
+ opacity: 0.05,
1707
+ layer: "below",
1708
+ line: {
1709
+ width: 0,
1710
+ },
1711
+ };
1712
+ });
1713
+ const groupAnnotations = groups.map((group, groupIndex) => ({
1714
+ type: "line",
1715
+ yref: "y",
1716
+ xref: "paper",
1717
+ x0: -0.01,
1718
+ x1: -0.01,
1719
+ y0: groupIndex - 0.4, // Align with the center of the group
1720
+ y1: groupIndex + 0.4,
1721
+ line: {
1722
+ color: group.color || "orange",
1723
+ width: 7,
1724
+ },
1725
+ }));
1726
+ const differenceAnnotations = [];
1727
+ const differenceBetweenMediansLines = [];
1728
+ groups.forEach((group, groupIndex) => {
1729
+ // console.log(group.boxes);
1730
+ if ((!isBoxPlotDataSummary(group.boxes[0].values) &&
1731
+ group.boxes[0].values.length == 0) ||
1732
+ (!isBoxPlotDataSummary(group.boxes[1].values) &&
1733
+ group.boxes[1].values.length == 0)) {
1734
+ return; // Don't show an annotation if we don't have data for both boxes
1735
+ }
1736
+ const medianA = isBoxPlotDataSummary(group.boxes[0].values)
1737
+ ? group.boxes[0].values.median
1738
+ : computeMedian(group.boxes[0].values);
1739
+ const medianB = isBoxPlotDataSummary(group.boxes[1].values)
1740
+ ? group.boxes[1].values.median
1741
+ : computeMedian(group.boxes[1].values);
1742
+ const q1A = isBoxPlotDataSummary(group.boxes[0].values)
1743
+ ? group.boxes[0].values.q1
1744
+ : computeQuartile(group.boxes[0].values, 1);
1745
+ const q3A = isBoxPlotDataSummary(group.boxes[0].values)
1746
+ ? group.boxes[0].values.q3
1747
+ : computeQuartile(group.boxes[0].values, 3);
1748
+ const q1B = isBoxPlotDataSummary(group.boxes[1].values)
1749
+ ? group.boxes[1].values.q1
1750
+ : computeQuartile(group.boxes[1].values, 1);
1751
+ const q3B = isBoxPlotDataSummary(group.boxes[1].values)
1752
+ ? group.boxes[1].values.q3
1753
+ : computeQuartile(group.boxes[1].values, 3);
1754
+ const differenceBetweenMedians = medianB - medianA;
1755
+ // If we have the quartiles for the first box, we can determine if the second box's median is inside or outside the box
1756
+ const annotationColor = isBoxPlotDataSummary(group.boxes[0].values)
1757
+ ? q3B < q1A || q1B > q3A
1758
+ ? "red"
1759
+ : medianB < q1A || medianB > q3A
1760
+ ? "orange"
1761
+ : "gray"
1762
+ : "gray";
1763
+ if (annotationColor === "gray") {
1764
+ return; // Don't show an annotation if the medians are close enough that they overlap in the boxes
1765
+ }
1766
+ differenceAnnotations.push({
1767
+ yref: "y",
1768
+ xref: "x",
1769
+ x: medianB > medianA ? medianB : medianA, // Position the annotation at the larger median
1770
+ y: groupIndex, // Align with the center of the group
1771
+ text: ` ${differenceBetweenMedians.toFixed(1)}`,
1772
+ showarrow: false,
1773
+ xanchor: "left",
1774
+ align: "left",
1775
+ font: {
1776
+ size: 12,
1777
+ color: annotationColor,
1778
+ style: "italic",
1779
+ },
1780
+ });
1781
+ differenceBetweenMediansLines.push({
1782
+ type: "line",
1783
+ x0: medianA,
1784
+ x1: medianB,
1785
+ xref: "x",
1786
+ y0: groupIndex,
1787
+ y1: groupIndex,
1788
+ yref: "y",
1789
+ line: {
1790
+ color: annotationColor,
1791
+ width: 2,
1792
+ },
1793
+ });
1794
+ differenceBetweenMediansLines.push({
1795
+ type: "line",
1796
+ x0: medianA,
1797
+ x1: medianA,
1798
+ xref: "x",
1799
+ y0: groupIndex - 0.05,
1800
+ y1: groupIndex,
1801
+ yref: "y",
1802
+ line: {
1803
+ color: annotationColor,
1804
+ width: 2,
1805
+ },
1806
+ });
1807
+ differenceBetweenMediansLines.push({
1808
+ type: "line",
1809
+ x0: medianB,
1810
+ x1: medianB,
1811
+ xref: "x",
1812
+ y0: groupIndex,
1813
+ y1: groupIndex + 0.05,
1814
+ yref: "y",
1815
+ line: {
1816
+ color: annotationColor,
1817
+ width: 2,
1818
+ },
1819
+ });
1820
+ });
1821
+ const extraLayoutConfig = {
1822
+ yaxis: {
1823
+ type: "linear", // Use linear axis for numeric positioning of boxes
1824
+ tickmode: "array",
1825
+ tickvals,
1826
+ ticktext,
1827
+ range: [-0.5, groups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
1828
+ tickcolor: "#ffffff",
1829
+ showgrid: false,
1830
+ },
1831
+ margin: {
1832
+ t: 5,
1833
+ r: 50,
1834
+ },
1835
+ shapes: [
1836
+ ...separatorShapes,
1837
+ ...differenceBetweenMediansLines,
1838
+ ...groupAnnotations,
1839
+ ],
1840
+ annotations: differenceAnnotations,
1841
+ };
1842
+ const legendNode = (jsxRuntime.jsxs(material.Box, { display: "flex", justifyContent: "flex-end", mb: 2, children: [jsxRuntime.jsx(LegendBoxPlotItem, { label: pairLabels[0], color: "#626280", boxStyle: "shaded" }), jsxRuntime.jsx(LegendBoxPlotItem, { label: pairLabels[1], color: "#626280", boxStyle: "outlined" })] }));
1843
+ const containerStyles = Object.assign({ width: width, height: height, position: "relative", display: "flex", flexDirection: "column", gap: 0 }, containerStyleOverrides);
1844
+ return (jsxRuntime.jsxs("div", { style: Object.assign({}, containerStyles), children: [showLegend && legendNode, jsxRuntime.jsx(BoxPlot, { data: boxPlotData, width: width, height: height, title: title, xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, extraLayoutConfig: extraLayoutConfig, containerStyleOverrides: containerStyleOverrides, plotId: `${plotId}-boxplot` })] }));
1845
+ };
1846
+
1847
+ const Plot$2 = React.lazy(() => import('react-plotly.js'));
1848
+ const SummaryComparisonPlot = (props) => {
1849
+ const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, } = props;
1850
+ // Ref for plot container
1851
+ const containerRef = React.useRef(null);
1852
+ // State for custom tooltip
1853
+ const [tooltip, setTooltip] = React.useState({
1854
+ visible: false,
1855
+ x: 0,
1856
+ y: 0,
1857
+ content: "",
1858
+ color: "#1f77b4",
1859
+ });
1860
+ const handleHover = (event) => {
1861
+ var _a, _b;
1862
+ if (!event.points || event.points.length === 0)
1863
+ return;
1864
+ const point = event.points[0];
1865
+ const eventData = point.data;
1866
+ if (!eventData)
1867
+ return;
1868
+ // Use the name of the trace to identify the group, then find the corresponding data for that group to display in the tooltip
1869
+ // This way we don't have to worry if we hovered over the line or the marker, we can show the same tooltip content based on the group
1870
+ const groupName = eventData.name || "Unknown Group";
1871
+ const groupData = (_a = groups.find((g) => g.groupLabel === groupName)) === null || _a === void 0 ? void 0 : _a.data;
1872
+ if (!groupData)
1873
+ return;
1874
+ let content = `<div style="margin-bottom: 8px;">`;
1875
+ let contentColor = "orange";
1876
+ let tooltipX = event.event.clientX;
1877
+ let tooltipY = event.event.clientY;
1878
+ content += `<strong>${groupName}</strong><br/>`;
1879
+ const summarizedMinText = groupData.summarizedMin
1880
+ ? groupData.summarizedMin.toFixed(2)
1881
+ : "NA";
1882
+ const summarizedMaxText = groupData.summarizedMax
1883
+ ? groupData.summarizedMax.toFixed(2)
1884
+ : "NA";
1885
+ const comparedMedianText = groupData.comparedMedian !== undefined
1886
+ ? groupData.comparedMedian.toFixed(2)
1887
+ : "NA";
1888
+ content += `
1889
+ <table style="width: 100%; margin-top: 4px;">
1890
+ <tr>
1891
+ <td style="text-align: left; padding: 2px 18px 2px 0;">Population P25:</td>
1892
+ <td style="text-align: right; padding: 2px 0;">${summarizedMinText}</td>
1893
+ </tr>
1894
+ <tr>
1895
+ <td style="text-align: left; padding: 2px 18px 2px 0;">Population P75:</td>
1896
+ <td style="text-align: right; padding: 2px 0;">${summarizedMaxText}</td>
1897
+ </tr>
1898
+ <tr>
1899
+ <td style="text-align: left; padding: 2px 18px 2px 0;">My Median:</td>
1900
+ <td style="text-align: right; padding: 2px 0;">${comparedMedianText}</td>
1901
+ </tr>
1902
+ </table>
1903
+ `;
1904
+ contentColor = eventData.line
1905
+ ? eventData.line.color
1906
+ : eventData.marker.color || contentColor;
1907
+ // Position tooltip at the end of the line (75th percentile)
1908
+ // Use the xaxis d2p method to convert data coordinate to pixel coordinate
1909
+ if (point.xaxis && typeof point.xaxis.d2p === "function") {
1910
+ const pixelX = tooltipPosition === "right"
1911
+ ? point.xaxis.d2p(groupData.summarizedMax)
1912
+ : point.xaxis.d2p(groupData.summarizedMin);
1913
+ const pixelY = point.yaxis.d2p(eventData.y[0]); // Use the y coordinate of the line for vertical positioning
1914
+ const containerRect = (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
1915
+ if (containerRect) {
1916
+ tooltipX = pixelX + point.xaxis._offset + containerRect.left; // Adjust for x-axis offset and container position
1917
+ tooltipY = pixelY + point.yaxis._offset + containerRect.top; // Adjust for y-axis offset and container position
1918
+ }
1919
+ }
1920
+ setTooltip({
1921
+ visible: true,
1922
+ x: tooltipX,
1923
+ y: tooltipY,
1924
+ content,
1925
+ color: contentColor,
1926
+ });
1927
+ };
1928
+ const handleUnhover = () => {
1929
+ setTooltip((prev) => (Object.assign(Object.assign({}, prev), { visible: false })));
1930
+ };
1931
+ // Transform the data into a format suitable for a plotly scatterplot
1932
+ const plotlyData = groups.flatMap((group, groupIndex) => {
1933
+ const traces = [];
1934
+ if (group.data.summarizedMin !== undefined &&
1935
+ group.data.summarizedMax !== undefined) {
1936
+ traces.push({
1937
+ type: "scatter",
1938
+ mode: "lines",
1939
+ name: group.groupLabel,
1940
+ x: [group.data.summarizedMin, group.data.summarizedMax],
1941
+ y: [0 + groupIndex * 0.1, 0 + groupIndex * 0.1],
1942
+ line: {
1943
+ color: colorToRGBA(group.color || "orange", 1),
1944
+ width: 2,
1945
+ },
1946
+ showlegend: false,
1947
+ hoverinfo: "none",
1948
+ });
1949
+ }
1950
+ if (group.data.comparedMedian !== undefined) {
1951
+ traces.push({
1952
+ type: "scatter",
1953
+ mode: "markers",
1954
+ name: group.groupLabel,
1955
+ x: [group.data.comparedMedian],
1956
+ y: [0 + groupIndex * 0.1],
1957
+ marker: {
1958
+ color: group.color || "orange",
1959
+ size: 10,
1960
+ symbol: "circle",
1961
+ line: {
1962
+ color: "white",
1963
+ width: 1,
1964
+ },
1965
+ },
1966
+ showlegend: false,
1967
+ hoverinfo: "none",
1968
+ });
1969
+ }
1970
+ return traces;
1971
+ });
1972
+ const xRangeMin = groups.reduce((min, group) => {
1973
+ var _a, _b;
1974
+ return Math.min(min, (_a = group.data.summarizedMin) !== null && _a !== void 0 ? _a : Infinity, (_b = group.data.comparedMedian) !== null && _b !== void 0 ? _b : Infinity);
1975
+ }, 0);
1976
+ const xRangeMax = groups.reduce((max, group) => {
1977
+ var _a, _b;
1978
+ return Math.max(max, (_a = group.data.summarizedMax) !== null && _a !== void 0 ? _a : -Infinity, (_b = group.data.comparedMedian) !== null && _b !== void 0 ? _b : -Infinity);
1979
+ }, 0);
1980
+ const layout = {
1981
+ width: undefined,
1982
+ height: height,
1983
+ autosize: true,
1984
+ margin: {
1985
+ l: 130,
1986
+ r: 35, // Balance between ensuring the mean annotation doesn't get cut off and having too much margin.
1987
+ t: title ? 50 : 5,
1988
+ b: 50,
1989
+ pad: 4,
1990
+ // ...extraLayoutConfig.margin, // Merge in any extra margin config provided via props
1991
+ },
1992
+ title: {
1993
+ text: title,
1994
+ font: {
1995
+ size: 16,
1996
+ },
1997
+ xref: "paper",
1998
+ x: 0.5,
1999
+ xanchor: "center",
2000
+ },
2001
+ xaxis: {
2002
+ title: {
2003
+ text: xAxisTitle,
2004
+ font: {
2005
+ size: 14,
2006
+ },
2007
+ },
2008
+ showgrid: true,
2009
+ showline: true,
2010
+ fixedrange: true, // Disable zooming
2011
+ zeroline: false,
2012
+ range: [
2013
+ startXAxisAtZero ? 0 : xRangeMin - (xRangeMax - xRangeMin) * 0.3,
2014
+ xRangeMax + (xRangeMax - xRangeMin) * 0.3,
2015
+ ], // Add padding to the x-axis range
2016
+ mirror: true,
2017
+ gridcolor: "#efefef",
2018
+ gridwidth: 0.2,
2019
+ zerolinecolor: "#969696",
2020
+ zerolinewidth: 1,
2021
+ linecolor: "#bababa",
2022
+ linewidth: 1,
2023
+ },
2024
+ yaxis: {
2025
+ mirror: "ticks",
2026
+ gridcolor: "#efefef",
2027
+ gridwidth: 0.2,
2028
+ zerolinecolor: "#969696",
2029
+ zerolinewidth: 1,
2030
+ linecolor: "#bababa",
2031
+ linewidth: 1,
2032
+ showticklabels: true,
2033
+ showgrid: false,
2034
+ showline: true,
2035
+ zeroline: false,
2036
+ fixedrange: true, // Disable zooming and pan interactions
2037
+ tickmode: "array",
2038
+ tickvals: groups.map((_, index) => 0 + index * 0.1),
2039
+ ticktext: groups.map((group) => group.groupLabel),
2040
+ ticks: "inside",
2041
+ range: [-0.08, 0.08 + (groups.length - 1) * 0.1], // Add padding around the groups
2042
+ automargin: true,
2043
+ tickcolor: "white", // Hide default ticks since we're using them for group labels in the paired comparisons plot
2044
+ },
2045
+ hovermode: "y",
2046
+ };
2047
+ const containerStyles = Object.assign({ width: "100%", height: height, position: "relative", display: "flex", flexDirection: "column", gap: 0 }, containerStyleOverrides);
2048
+ const config = {
2049
+ responsive: true, // Enable responsive mode for width
2050
+ displayModeBar: false, // Hide the mode bar
2051
+ displaylogo: false, // Hide the Plotly logo
2052
+ scrollZoom: false, // Disable zooming with scroll
2053
+ staticPlot: false, // Enable interactivity
2054
+ };
2055
+ return (jsxRuntime.jsx("div", { ref: containerRef,
2056
+ // className={`plot-container ${plotId}`}
2057
+ style: Object.assign({}, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsxs("div", { style: {
2058
+ position: "relative",
2059
+ width: "100%",
2060
+ height: "100%",
2061
+ }, children: [jsxRuntime.jsx(Plot$2, { data: plotlyData, layout: layout, config: config, useResizeHandler: true, onHover: handleHover, onUnhover: handleUnhover, style: {
2062
+ width: "100%",
2063
+ height: `${height}px`,
2064
+ display: "block",
2065
+ transition: "opacity 0.15s ease-in-out",
2066
+ } }, `boxplot-${plotId || "default"}`), tooltip.visible && (jsxRuntime.jsx("div", { style: {
2067
+ position: "fixed",
2068
+ left: tooltipPosition === "right" ? tooltip.x + 10 : undefined,
2069
+ right: tooltipPosition === "left"
2070
+ ? window.innerWidth - tooltip.x + 10 // Measured in pixels from the right edge of the screen
2071
+ : undefined,
2072
+ transform: tooltipPosition === "right" ? "translateX(10px)" : undefined,
2073
+ top: tooltip.y - 18,
2074
+ backgroundColor: "rgba(255, 255, 255, 0.8)",
2075
+ borderRadius: "4px",
2076
+ color: "#333",
2077
+ padding: "8px 12px",
2078
+ fontSize: "13px",
2079
+ lineHeight: "1.5",
2080
+ pointerEvents: "none",
2081
+ zIndex: 1000,
2082
+ whiteSpace: "nowrap",
2083
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
2084
+ borderLeft: `7px solid ${tooltip.color}`,
2085
+ fontFamily: '"Open Sans", verdana, arial, sans-serif',
2086
+ }, dangerouslySetInnerHTML: { __html: tooltip.content } }))] }) }) }));
2087
+ };
2088
+ const SummaryComparisonPlotLegend = ({ comparedDataLabel, summarizedDataLabel, color = "orange", }) => {
2089
+ return (jsxRuntime.jsxs("div", { style: {
2090
+ display: "flex",
2091
+ gap: "20px",
2092
+ alignItems: "center",
2093
+ flexDirection: "row",
2094
+ }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsxRuntime.jsx(Box, { width: 13, height: 13, sx: {
2095
+ backgroundColor: color,
2096
+ borderRadius: "20px",
2097
+ } }), jsxRuntime.jsx("span", { children: comparedDataLabel })] }), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsxRuntime.jsx(Box, { width: 30, height: 3, sx: { backgroundColor: color } }), jsxRuntime.jsx("span", { children: summarizedDataLabel })] })] }));
2098
+ };
2099
+
2100
+ const Plot$1 = React.lazy(() => import('react-plotly.js'));
2101
+ const LinePlot = (props) => {
2102
+ var _a, _b;
2103
+ const { data, width = 600, height = 400, title, xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "boxplot", extraLayoutConfig = {}, additionalYAxis, additionalContextTrace, } = props;
2104
+ // Ref for plot container
2105
+ const containerRef = React.useRef(null);
2106
+ const plotlyData = data.map((trace) => {
2107
+ return {
2108
+ type: "scatter",
2109
+ mode: "lines+markers",
2110
+ x: trace.x,
2111
+ y: trace.y,
2112
+ name: trace.label,
2113
+ line: {
2114
+ color: trace.color || "#1f77b4", // Default to Plotly's default blue if no color provided
2115
+ },
2116
+ marker: {
2117
+ color: trace.color || "#1f77b4",
2118
+ size: 6,
2119
+ },
2120
+ zorder: 1, // Ensure lines are above any additional context traces (like scatter points)
2121
+ };
2122
+ });
2123
+ const layout = Object.assign(Object.assign(Object.assign({}, extraLayoutConfig), { title: {
2124
+ text: title,
2125
+ }, showlegend: false, autosize: true, width: undefined, height: undefined, hovermode: "closest", margin: {
2126
+ l: 50,
2127
+ r: additionalContextTrace ? 80 : 35, // Balance between ensuring the mean annotation doesn't get cut off and having too much margin.
2128
+ t: title ? 50 : 20, // Adjust top margin based on whether a title is provided
2129
+ b: 50,
2130
+ pad: 4,
2131
+ }, xaxis: {
2132
+ title: {
2133
+ text: xAxisTitle,
2134
+ },
2135
+ // range: displayXAxis, // Fixed range prevents axis shifting during interaction or data updates
2136
+ showgrid: true,
2137
+ zeroline: false,
2138
+ showline: true,
2139
+ mirror: "ticks",
2140
+ gridcolor: "#efefef",
2141
+ gridwidth: 0.2,
2142
+ zerolinecolor: "#969696",
2143
+ zerolinewidth: 1,
2144
+ linecolor: "#bababa",
2145
+ linewidth: 1,
2146
+ fixedrange: true, // Disable zooming
2147
+ ticklabelposition: "outside",
2148
+ }, yaxis: {
2149
+ title: {
2150
+ text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
2151
+ standoff: 12, // Add space between title and axis
2152
+ font: {
2153
+ color: ((_a = data[0]) === null || _a === void 0 ? void 0 : _a.color) || "#1f77b4", // Match y-axis title color to line color
2154
+ },
2155
+ },
2156
+ automargin: true, // Required for standoff to work properly
2157
+ showgrid: true,
2158
+ zeroline: false,
2159
+ showline: true,
2160
+ mirror: "ticks",
2161
+ gridcolor: "#efefef",
2162
+ gridwidth: 0.2,
2163
+ zerolinecolor: "#969696",
2164
+ zerolinewidth: 1,
2165
+ linecolor: "#bababa",
2166
+ linewidth: 1,
2167
+ fixedrange: true, // Disable zooming
2168
+ tickfont: {
2169
+ color: ((_b = data[0]) === null || _b === void 0 ? void 0 : _b.color) || "#1f77b4", // Match y-axis tick color to line color
2170
+ },
2171
+ tickcolor: "white", // Hide default ticks since we're using them for group labels in the paired comparisons plot
2172
+ ticklen: 10, // Give ticks a length to push labels away from y axis.
2173
+ // ticksuffix: " ", // Add space between y axis and ticks
2174
+ } }), (additionalYAxis
2175
+ ? { yaxis2: Object.assign(Object.assign({}, additionalYAxis), { side: "right", overlaying: "y" }) }
2176
+ : {}));
2177
+ const config = {
2178
+ responsive: true, // Make the plot responsive
2179
+ displayModeBar: false, // Hide the mode bar
2180
+ displaylogo: false, // Hide the Plotly logo
2181
+ scrollZoom: false, // Disable zooming with scroll
2182
+ staticPlot: false, // Enable interactivity
2183
+ };
2184
+ const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
2185
+ return (jsxRuntime.jsx("div", { ref: containerRef, style: Object.assign({}, containerStyles), children: jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx(Loading, {}), children: jsxRuntime.jsx("div", { style: {
2186
+ position: "relative",
2187
+ width: "100%",
2188
+ height: "100%",
2189
+ }, children: jsxRuntime.jsx(Plot$1, { data: [
2190
+ ...(additionalContextTrace ? [additionalContextTrace] : []),
2191
+ ...plotlyData,
2192
+ ], layout: layout, config: config, useResizeHandler: true, style: {
2193
+ width: "100%",
2194
+ height: "100%",
2195
+ display: "block",
2196
+ transition: "opacity 0.15s ease-in-out",
2197
+ }, onHover: () => {
2198
+ console.log("hovering");
2199
+ } }, `boxplot-${plotId || "default"}`) }) }) }));
2200
+ };
2201
+
1450
2202
  const Plot = React.lazy(() => import('react-plotly.js'));
1451
2203
  const TestPlot = (props) => {
1452
2204
  var _a, _b;
@@ -1466,9 +2218,14 @@ const TestPlot = (props) => {
1466
2218
  return jsxRuntime.jsx(Plot, { data: data, layout: layout });
1467
2219
  };
1468
2220
 
2221
+ exports.BoxPlot = BoxPlot;
1469
2222
  exports.HistogramPlot = HistogramPlot;
2223
+ exports.LinePlot = LinePlot;
2224
+ exports.PairedComparisonsBoxPlot = PairedComparisonsBoxPlot;
1470
2225
  exports.RadialHistogramPlot = RadialHistogramPlot;
1471
2226
  exports.StatsDonut = StatsDonut;
2227
+ exports.SummaryComparisonPlot = SummaryComparisonPlot;
2228
+ exports.SummaryComparisonPlotLegend = SummaryComparisonPlotLegend;
1472
2229
  exports.TestPlot = TestPlot;
1473
2230
  exports.isDateArray = isDateArray;
1474
2231
  exports.isNumberArray = isNumberArray;