td-plots 1.11.1 → 1.11.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/components/SplitBoxPlot.d.ts +0 -1
- package/dist/index.esm.js +60 -32
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +60 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1538,7 +1538,6 @@ const BoxPlot = (props) => {
|
|
|
1538
1538
|
staticPlot: false, // Enable interactivity
|
|
1539
1539
|
};
|
|
1540
1540
|
const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
|
|
1541
|
-
console.log("layout:", layout); // Debugging log to inspect the final layout object
|
|
1542
1541
|
return (jsx("div", { ref: containerRef,
|
|
1543
1542
|
// className={`plot-container ${plotId}`}
|
|
1544
1543
|
style: Object.assign({}, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsxs("div", { style: {
|
|
@@ -1819,7 +1818,7 @@ const PairedComparisonsBoxPlot = (props) => {
|
|
|
1819
1818
|
// This component shows a set of main boxplots as one collection, then allowes for additional
|
|
1820
1819
|
// boxes to be shown in a subplot above the main boxplots for comparison.
|
|
1821
1820
|
const SplitBoxPlot = (props) => {
|
|
1822
|
-
const { groups, additionalGroups = [], width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, secondXAxisTitle, secondYAxisTitle, containerStyleOverrides, plotId = "paired-comparisons-boxplot", xAnnotations = [],
|
|
1821
|
+
const { groups, additionalGroups = [], width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, secondXAxisTitle, secondYAxisTitle, containerStyleOverrides, plotId = "paired-comparisons-boxplot", xAnnotations = [], } = props;
|
|
1823
1822
|
// Transform the grouped data into an array for BoxPlot
|
|
1824
1823
|
const boxPlotData = [
|
|
1825
1824
|
...groups.flatMap((group, groupIndex) => {
|
|
@@ -1923,8 +1922,9 @@ const SplitBoxPlot = (props) => {
|
|
|
1923
1922
|
});
|
|
1924
1923
|
const totalGroups = groups.length + additionalGroups.length;
|
|
1925
1924
|
const yAxisSplitProportion = additionalGroups.length > 0 ? groups.length / totalGroups : 1;
|
|
1926
|
-
// Render y-axis titles as annotations so
|
|
1927
|
-
//
|
|
1925
|
+
// Render y-axis titles as annotations so we can specifically set xshift in px from the axis.
|
|
1926
|
+
// Calculating approximate label width in pixels to ensure the title is far enough away from the longest label,
|
|
1927
|
+
// with a minimum distance to look good even with short labels.
|
|
1928
1928
|
const charWidth = 8; // ~px per character for Open Sans 14px (Plotly title font)
|
|
1929
1929
|
const primaryMaxChars = Math.max(0, ...groups.map((g) => g.label.length));
|
|
1930
1930
|
const secondaryMaxChars = additionalGroups.length > 0
|
|
@@ -1932,16 +1932,13 @@ const SplitBoxPlot = (props) => {
|
|
|
1932
1932
|
: primaryMaxChars;
|
|
1933
1933
|
const maxLabelChars = Math.max(primaryMaxChars, secondaryMaxChars);
|
|
1934
1934
|
const approxLabelPx = maxLabelChars * charWidth;
|
|
1935
|
-
const leftMargin = approxLabelPx + 75; // tick labels + title + gaps
|
|
1936
|
-
const plotContentWidth = Math.max(1, width - leftMargin - 50);
|
|
1937
|
-
// 0.83 corrects for charWidth=8 overestimating rendered tick-label width (Open Sans 12px ≈ 6.6px/char)
|
|
1938
|
-
const titleXPaper = -(approxLabelPx * 0.83) / plotContentWidth;
|
|
1939
1935
|
const yAxisTitleAnnotations = [];
|
|
1940
1936
|
if (yAxisTitle) {
|
|
1941
1937
|
const domainTop = additionalGroups.length > 0 ? yAxisSplitProportion - 0.01 : 1;
|
|
1942
1938
|
yAxisTitleAnnotations.push({
|
|
1943
1939
|
text: yAxisTitle,
|
|
1944
|
-
x:
|
|
1940
|
+
x: 0,
|
|
1941
|
+
xshift: -120,
|
|
1945
1942
|
y: domainTop / 2,
|
|
1946
1943
|
xref: "paper",
|
|
1947
1944
|
yref: "paper",
|
|
@@ -1959,7 +1956,8 @@ const SplitBoxPlot = (props) => {
|
|
|
1959
1956
|
if (secondYAxisTitle && additionalGroups.length > 0) {
|
|
1960
1957
|
yAxisTitleAnnotations.push({
|
|
1961
1958
|
text: secondYAxisTitle,
|
|
1962
|
-
x:
|
|
1959
|
+
x: 0,
|
|
1960
|
+
xshift: -120,
|
|
1963
1961
|
y: (yAxisSplitProportion + 1) / 2,
|
|
1964
1962
|
xref: "paper",
|
|
1965
1963
|
yref: "paper",
|
|
@@ -1979,7 +1977,7 @@ const SplitBoxPlot = (props) => {
|
|
|
1979
1977
|
tickmode: "array",
|
|
1980
1978
|
tickvals,
|
|
1981
1979
|
ticktext,
|
|
1982
|
-
automargin: true,
|
|
1980
|
+
// automargin: true,
|
|
1983
1981
|
range: [-0.5, groups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
|
|
1984
1982
|
domain: additionalGroups.length > 0 ? [0, yAxisSplitProportion - 0.01] : [0, 1], // Ensure primary y-axis takes full height
|
|
1985
1983
|
tickcolor: "#ffffff",
|
|
@@ -1994,8 +1992,8 @@ const SplitBoxPlot = (props) => {
|
|
|
1994
1992
|
anchor: "y", // Anchor to primary y-axis
|
|
1995
1993
|
}, margin: {
|
|
1996
1994
|
t: title ? 50 : 70,
|
|
1997
|
-
r:
|
|
1998
|
-
l:
|
|
1995
|
+
r: 20,
|
|
1996
|
+
l: Math.max(130, approxLabelPx + 40),
|
|
1999
1997
|
}, shapes: [
|
|
2000
1998
|
...separatorShapes,
|
|
2001
1999
|
// ...differenceBetweenMediansLines,
|
|
@@ -2018,12 +2016,13 @@ const SplitBoxPlot = (props) => {
|
|
|
2018
2016
|
// ...differenceAnnotations,
|
|
2019
2017
|
...xAnnotations.map((annotation, annotationIndex) => ({
|
|
2020
2018
|
x: annotation.x,
|
|
2021
|
-
y: 1
|
|
2019
|
+
y: 1,
|
|
2022
2020
|
xref: "x",
|
|
2023
2021
|
yref: "paper",
|
|
2024
2022
|
text: annotation.text || "",
|
|
2025
2023
|
showarrow: false,
|
|
2026
2024
|
yanchor: "bottom",
|
|
2025
|
+
yshift: 18 + annotationIndex * 20, // stack in pixels — stable across screen sizes
|
|
2027
2026
|
font: {
|
|
2028
2027
|
color: annotation.color || "rgba(255, 0, 0, 0.7)",
|
|
2029
2028
|
size: 12,
|
|
@@ -2243,9 +2242,10 @@ const SummaryComparisonPlot = (props) => {
|
|
|
2243
2242
|
// Add tick label for the compared annotation value
|
|
2244
2243
|
if (comparedAnnotations.length > 0) {
|
|
2245
2244
|
comparedAnnotations.push({
|
|
2246
|
-
x:
|
|
2245
|
+
x: 0, // Position to the left of the y-axis
|
|
2247
2246
|
y: 1.1, // Should match the y position of the compared median annotation
|
|
2248
2247
|
xref: "paper",
|
|
2248
|
+
xshift: -10,
|
|
2249
2249
|
yref: "paper",
|
|
2250
2250
|
text: "My AVG",
|
|
2251
2251
|
showarrow: false,
|
|
@@ -2651,21 +2651,37 @@ const BarPlot = (props) => {
|
|
|
2651
2651
|
// Ref for plot container
|
|
2652
2652
|
const containerRef = useRef(null);
|
|
2653
2653
|
const plotMetaRef = useRef(null);
|
|
2654
|
+
// Pixel offsets from the x-axis line for the bar group shapes.
|
|
2655
|
+
// 45px clears tick marks + labels + axis title; the 15px band sits below all of them.
|
|
2656
|
+
const BAR_GROUP_SHAPE_TOP_PX = 8;
|
|
2657
|
+
const BAR_GROUP_SHAPE_HEIGHT_PX = 20;
|
|
2658
|
+
// px above the top of the plot area — lands between x2 tick labels (~15px) and x2 title (~35px)
|
|
2659
|
+
const X_ANNOTATION_TOP_PX = 22;
|
|
2660
|
+
// Tracks the rendered plot area height so we can convert the shape's fixed pixel
|
|
2661
|
+
// center back into a paper y coordinate for the annotation.
|
|
2662
|
+
const [plotAreaHeight, setPlotAreaHeight] = useState(300);
|
|
2654
2663
|
const [barGroupTooltip, setBarGroupTooltip] = useState(null);
|
|
2655
2664
|
const y2AxisPosition = 0.72; // Position of the secondary y-axis (histogram) as a fraction of the plot height
|
|
2656
2665
|
const capturePlotMeta = (_figure, graphDiv) => {
|
|
2657
|
-
var _a, _b, _c;
|
|
2666
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2658
2667
|
try {
|
|
2659
2668
|
const m = (_a = graphDiv._fullLayout) === null || _a === void 0 ? void 0 : _a.margin;
|
|
2660
2669
|
const r = (_c = (_b = graphDiv._fullLayout) === null || _b === void 0 ? void 0 : _b.xaxis) === null || _c === void 0 ? void 0 : _c.range;
|
|
2670
|
+
const h = (_d = graphDiv._fullLayout) === null || _d === void 0 ? void 0 : _d.height;
|
|
2661
2671
|
if (m && r) {
|
|
2662
2672
|
plotMetaRef.current = {
|
|
2663
2673
|
xRange: r,
|
|
2664
2674
|
margin: { l: m.l, r: m.r, t: m.t, b: m.b },
|
|
2675
|
+
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,
|
|
2665
2676
|
};
|
|
2677
|
+
if (h) {
|
|
2678
|
+
const newPlotH = h - m.t - m.b;
|
|
2679
|
+
if (newPlotH > 0)
|
|
2680
|
+
setPlotAreaHeight(newPlotH);
|
|
2681
|
+
}
|
|
2666
2682
|
}
|
|
2667
2683
|
}
|
|
2668
|
-
catch (
|
|
2684
|
+
catch (_g) { }
|
|
2669
2685
|
};
|
|
2670
2686
|
const handleBarGroupMouseMove = (e) => {
|
|
2671
2687
|
var _a;
|
|
@@ -2675,18 +2691,18 @@ const BarPlot = (props) => {
|
|
|
2675
2691
|
setBarGroupTooltip(null);
|
|
2676
2692
|
return;
|
|
2677
2693
|
}
|
|
2678
|
-
const { xRange, margin } = plotMetaRef.current;
|
|
2694
|
+
const { xRange, margin, height: plotlyHeight } = plotMetaRef.current;
|
|
2679
2695
|
const rect = containerRef.current.getBoundingClientRect();
|
|
2680
2696
|
const mouseX = e.clientX - rect.left;
|
|
2681
2697
|
const mouseY = e.clientY - rect.top;
|
|
2682
|
-
const containerHeight = containerRef.current.clientHeight;
|
|
2683
2698
|
const containerWidth = containerRef.current.clientWidth;
|
|
2684
2699
|
const plotW = containerWidth - margin.l - margin.r;
|
|
2685
|
-
|
|
2686
|
-
const plotBottom =
|
|
2687
|
-
//
|
|
2688
|
-
|
|
2689
|
-
const
|
|
2700
|
+
// Use Plotly's own reported height so plotBottom is consistent with the margin values
|
|
2701
|
+
const plotBottom = plotlyHeight - margin.b - 35; // Subtracting and extra 35px to finally get it in the right spot.
|
|
2702
|
+
// Shapes use yanchor=0 (x-axis line) with negative pixel y values, which maps to
|
|
2703
|
+
// positive screen-y (downward). Strip bounds mirror those pixel offsets exactly.
|
|
2704
|
+
const stripTop = plotBottom;
|
|
2705
|
+
const stripBottom = plotBottom + BAR_GROUP_SHAPE_TOP_PX + BAR_GROUP_SHAPE_HEIGHT_PX + 5; // Give a little extra room on the bottom.
|
|
2690
2706
|
if (mouseY < stripTop || mouseY > stripBottom) {
|
|
2691
2707
|
setBarGroupTooltip(null);
|
|
2692
2708
|
return;
|
|
@@ -2837,8 +2853,12 @@ const BarPlot = (props) => {
|
|
|
2837
2853
|
yref: "paper",
|
|
2838
2854
|
x0: group.min,
|
|
2839
2855
|
x1: group.max > xMax ? xMax + barWidth : group.max,
|
|
2840
|
-
|
|
2841
|
-
|
|
2856
|
+
// Anchor at the x-axis line (paper y=0) so the pixel offset is consistent
|
|
2857
|
+
// regardless of plot height — avoids overlap with tick labels on small screens.
|
|
2858
|
+
yanchor: 0,
|
|
2859
|
+
ysizemode: "pixel",
|
|
2860
|
+
y0: -BAR_GROUP_SHAPE_TOP_PX,
|
|
2861
|
+
y1: -28,
|
|
2842
2862
|
fillcolor: group.color,
|
|
2843
2863
|
line: {
|
|
2844
2864
|
width: 1,
|
|
@@ -2851,7 +2871,10 @@ const BarPlot = (props) => {
|
|
|
2851
2871
|
xref: "x",
|
|
2852
2872
|
yref: "paper",
|
|
2853
2873
|
x: barGroups[0].min + barDataWidth / 2,
|
|
2854
|
-
y
|
|
2874
|
+
// Convert the pixel center of the shape band to a paper y coordinate using
|
|
2875
|
+
// the actual rendered plot area height so it stays inside the shape on any screen size.
|
|
2876
|
+
y: -18 /
|
|
2877
|
+
plotAreaHeight,
|
|
2855
2878
|
text: barGroupTooltipTitle,
|
|
2856
2879
|
showarrow: false,
|
|
2857
2880
|
yanchor: "middle",
|
|
@@ -2890,7 +2913,9 @@ const BarPlot = (props) => {
|
|
|
2890
2913
|
l: 50,
|
|
2891
2914
|
r: 35, // Balance between ensuring the mean annotation doesn't get cut off and having too much margin.
|
|
2892
2915
|
t: 40 + (title ? 50 : 0), // Add extra top margin if there is a title
|
|
2893
|
-
b:
|
|
2916
|
+
b: barGroups.length > 0
|
|
2917
|
+
? BAR_GROUP_SHAPE_TOP_PX + BAR_GROUP_SHAPE_HEIGHT_PX + 15
|
|
2918
|
+
: 50,
|
|
2894
2919
|
pad: 4,
|
|
2895
2920
|
}, xaxis: {
|
|
2896
2921
|
title: {
|
|
@@ -2909,12 +2934,15 @@ const BarPlot = (props) => {
|
|
|
2909
2934
|
linewidth: 1,
|
|
2910
2935
|
fixedrange: true, // Disable zooming
|
|
2911
2936
|
automargin: true, // Adjust margin if tick labels rotate
|
|
2912
|
-
ticklen:
|
|
2937
|
+
ticklen: barGroups.length > 0
|
|
2938
|
+
? BAR_GROUP_SHAPE_TOP_PX + BAR_GROUP_SHAPE_HEIGHT_PX
|
|
2939
|
+
: 8, // tick length in px.
|
|
2913
2940
|
tickcolor: "white",
|
|
2914
2941
|
} }, (data2 && {
|
|
2915
2942
|
xaxis2: {
|
|
2916
2943
|
title: {
|
|
2917
2944
|
text: xAxis2Title,
|
|
2945
|
+
standoff: 22,
|
|
2918
2946
|
},
|
|
2919
2947
|
side: "top",
|
|
2920
2948
|
anchor: "y2", // Anchor to secondary y-axis
|
|
@@ -2935,7 +2963,7 @@ const BarPlot = (props) => {
|
|
|
2935
2963
|
})), { yaxis: {
|
|
2936
2964
|
title: {
|
|
2937
2965
|
text: yAxisTitle !== null && yAxisTitle !== void 0 ? yAxisTitle : "Count",
|
|
2938
|
-
standoff:
|
|
2966
|
+
standoff: 15, // Add space between title and axis
|
|
2939
2967
|
},
|
|
2940
2968
|
domain: data2 ? [0, y2AxisPosition - 0.01] : [0, 1], // Ensure primary y-axis takes full height
|
|
2941
2969
|
automargin: true, // Required for standoff to work properly
|
|
@@ -2982,7 +3010,7 @@ const BarPlot = (props) => {
|
|
|
2982
3010
|
x0: annotation.x,
|
|
2983
3011
|
x1: annotation.x,
|
|
2984
3012
|
y0: 0,
|
|
2985
|
-
y1: 1
|
|
3013
|
+
y1: 1 + (X_ANNOTATION_TOP_PX - 4) / plotAreaHeight,
|
|
2986
3014
|
xref: "x",
|
|
2987
3015
|
yref: "paper",
|
|
2988
3016
|
line: {
|
|
@@ -2995,7 +3023,7 @@ const BarPlot = (props) => {
|
|
|
2995
3023
|
...(barGroupShapeAnnotation ? [barGroupShapeAnnotation] : []),
|
|
2996
3024
|
...xAnnotations.map((annotation) => ({
|
|
2997
3025
|
x: annotation.x,
|
|
2998
|
-
y: 1
|
|
3026
|
+
y: 1 + X_ANNOTATION_TOP_PX / plotAreaHeight,
|
|
2999
3027
|
xref: "x",
|
|
3000
3028
|
yref: "paper",
|
|
3001
3029
|
text: annotation.text || "",
|