@tetrascience-npm/tetrascience-react-ui 0.6.0-beta.98.1 → 0.6.0-beta.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/charts/AreaGraph/AreaGraph.cjs +1 -1
- package/dist/components/charts/AreaGraph/AreaGraph.cjs.map +1 -1
- package/dist/components/charts/AreaGraph/AreaGraph.js +132 -103
- package/dist/components/charts/AreaGraph/AreaGraph.js.map +1 -1
- package/dist/components/charts/BarGraph/BarGraph.cjs +1 -1
- package/dist/components/charts/BarGraph/BarGraph.cjs.map +1 -1
- package/dist/components/charts/BarGraph/BarGraph.js +124 -95
- package/dist/components/charts/BarGraph/BarGraph.js.map +1 -1
- package/dist/components/charts/Boxplot/Boxplot.cjs +1 -1
- package/dist/components/charts/Boxplot/Boxplot.cjs.map +1 -1
- package/dist/components/charts/Boxplot/Boxplot.js +116 -87
- package/dist/components/charts/Boxplot/Boxplot.js.map +1 -1
- package/dist/components/charts/DotPlot/DotPlot.cjs +1 -1
- package/dist/components/charts/DotPlot/DotPlot.cjs.map +1 -1
- package/dist/components/charts/DotPlot/DotPlot.js +113 -83
- package/dist/components/charts/DotPlot/DotPlot.js.map +1 -1
- package/dist/components/charts/Histogram/Histogram.cjs +1 -1
- package/dist/components/charts/Histogram/Histogram.cjs.map +1 -1
- package/dist/components/charts/Histogram/Histogram.js +128 -105
- package/dist/components/charts/Histogram/Histogram.js.map +1 -1
- package/dist/components/charts/LineGraph/LineGraph.cjs +1 -1
- package/dist/components/charts/LineGraph/LineGraph.cjs.map +1 -1
- package/dist/components/charts/LineGraph/LineGraph.js +109 -87
- package/dist/components/charts/LineGraph/LineGraph.js.map +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.cjs +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.cjs.map +1 -1
- package/dist/components/charts/ScatterGraph/ScatterGraph.js +114 -90
- package/dist/components/charts/ScatterGraph/ScatterGraph.js.map +1 -1
- package/dist/hooks/use-element-size.cjs +2 -0
- package/dist/hooks/use-element-size.cjs.map +1 -0
- package/dist/hooks/use-element-size.js +21 -0
- package/dist/hooks/use-element-size.js.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +56 -0
- package/dist/index.tailwind.css +1 -1
- package/package.json +1 -1
|
@@ -1,65 +1,69 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
import { useRef as
|
|
4
|
-
import { useChartTooltip as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
import { jsxs as Y, jsx as H } from "react/jsx-runtime";
|
|
2
|
+
import k from "plotly.js-dist";
|
|
3
|
+
import { useRef as g, useMemo as u, useEffect as j } from "react";
|
|
4
|
+
import { useChartTooltip as V } from "../ChartTooltip/ChartTooltip.js";
|
|
5
|
+
import { useElementSize as D } from "../../../hooks/use-element-size.js";
|
|
6
|
+
import { usePlotlyTheme as F } from "../../../hooks/use-plotly-theme.js";
|
|
7
|
+
import { cn as R } from "../../../lib/utils.js";
|
|
8
|
+
import { seriesColor as X } from "../../../utils/colors.js";
|
|
9
|
+
const $ = -1.8, et = ({
|
|
8
10
|
dataSeries: p,
|
|
9
|
-
width:
|
|
10
|
-
height:
|
|
11
|
-
xRange:
|
|
12
|
-
yRange:
|
|
13
|
-
xTitle:
|
|
14
|
-
yTitle:
|
|
15
|
-
title:
|
|
16
|
-
showPoints:
|
|
11
|
+
width: v,
|
|
12
|
+
height: I,
|
|
13
|
+
xRange: y,
|
|
14
|
+
yRange: h,
|
|
15
|
+
xTitle: x,
|
|
16
|
+
yTitle: w,
|
|
17
|
+
title: z = "Boxplot",
|
|
18
|
+
showPoints: E = !1
|
|
17
19
|
}) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const a = g(null), o = F(), { bindTooltip: C, tooltipElement: A } = V({ xLabel: x, yLabel: w }), [S, N] = D(), n = v ?? N.width, i = I ?? N.height, _ = n > 0 && i > 0, T = v === void 0, U = I === void 0, d = g({ width: n, height: i });
|
|
21
|
+
d.current = { width: n, height: i };
|
|
22
|
+
const M = g(!1), m = g({ width: 0, height: 0 }), { yMin: B, yMax: L } = u(() => {
|
|
23
|
+
let e = Number.MAX_VALUE, t = Number.MIN_VALUE;
|
|
24
|
+
p.forEach((l) => {
|
|
25
|
+
l.y.forEach((r) => {
|
|
26
|
+
e = Math.min(e, r), t = Math.max(t, r);
|
|
23
27
|
});
|
|
24
28
|
});
|
|
25
|
-
const
|
|
29
|
+
const c = (t - e) * 0.1;
|
|
26
30
|
return {
|
|
27
|
-
yMin: e -
|
|
28
|
-
yMax:
|
|
31
|
+
yMin: e - c,
|
|
32
|
+
yMax: t + c
|
|
29
33
|
};
|
|
30
|
-
}, [p]),
|
|
31
|
-
() =>
|
|
32
|
-
[
|
|
33
|
-
),
|
|
34
|
-
const e =
|
|
35
|
-
let
|
|
36
|
-
e /
|
|
37
|
-
const
|
|
38
|
-
let
|
|
39
|
-
for (;
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
}, [
|
|
34
|
+
}, [p]), f = u(
|
|
35
|
+
() => h || [B, L],
|
|
36
|
+
[h, B, L]
|
|
37
|
+
), O = u(() => {
|
|
38
|
+
const e = f[1] - f[0];
|
|
39
|
+
let t = Math.pow(10, Math.floor(Math.log10(e)));
|
|
40
|
+
e / t > 10 && (t = t * 2), e / t < 4 && (t = t / 2);
|
|
41
|
+
const c = [];
|
|
42
|
+
let l = Math.ceil(f[0] / t) * t;
|
|
43
|
+
for (; l <= f[1]; )
|
|
44
|
+
c.push(l), l += t;
|
|
45
|
+
return c;
|
|
46
|
+
}, [f]), b = u(
|
|
43
47
|
() => ({
|
|
44
|
-
tickcolor:
|
|
48
|
+
tickcolor: o.tickColor,
|
|
45
49
|
ticklen: 12,
|
|
46
50
|
tickwidth: 1,
|
|
47
51
|
ticks: "outside",
|
|
48
52
|
tickfont: {
|
|
49
53
|
size: 16,
|
|
50
|
-
color:
|
|
54
|
+
color: o.textColor,
|
|
51
55
|
family: "Inter, sans-serif",
|
|
52
56
|
weight: 400
|
|
53
57
|
},
|
|
54
|
-
linecolor:
|
|
58
|
+
linecolor: o.lineColor,
|
|
55
59
|
linewidth: 1,
|
|
56
60
|
position: 0,
|
|
57
61
|
zeroline: !1
|
|
58
62
|
}),
|
|
59
|
-
[
|
|
60
|
-
),
|
|
63
|
+
[o]
|
|
64
|
+
), P = u(
|
|
61
65
|
() => ({
|
|
62
|
-
text:
|
|
66
|
+
text: z,
|
|
63
67
|
x: 0.5,
|
|
64
68
|
y: 0.95,
|
|
65
69
|
xanchor: "center",
|
|
@@ -68,17 +72,17 @@ const A = -1.8, W = ({
|
|
|
68
72
|
size: 32,
|
|
69
73
|
weight: 600,
|
|
70
74
|
family: "Inter, sans-serif",
|
|
71
|
-
color:
|
|
75
|
+
color: o.textColor,
|
|
72
76
|
lineheight: 1.2,
|
|
73
77
|
standoff: 30
|
|
74
78
|
}
|
|
75
79
|
}),
|
|
76
|
-
[
|
|
80
|
+
[z, o]
|
|
77
81
|
);
|
|
78
|
-
return
|
|
79
|
-
if (!
|
|
80
|
-
const e = p.map((r,
|
|
81
|
-
const
|
|
82
|
+
return j(() => {
|
|
83
|
+
if (!a.current || !_) return;
|
|
84
|
+
const e = p.map((r, W) => {
|
|
85
|
+
const s = X(W, r.color);
|
|
82
86
|
return {
|
|
83
87
|
y: r.y,
|
|
84
88
|
x: r.x,
|
|
@@ -86,95 +90,120 @@ const A = -1.8, W = ({
|
|
|
86
90
|
name: r.name,
|
|
87
91
|
hoverinfo: "none",
|
|
88
92
|
marker: {
|
|
89
|
-
color:
|
|
93
|
+
color: s
|
|
90
94
|
},
|
|
91
95
|
line: {
|
|
92
|
-
color:
|
|
96
|
+
color: s
|
|
93
97
|
},
|
|
94
|
-
fillcolor: typeof
|
|
98
|
+
fillcolor: typeof s == "string" && s.startsWith("#") && s.length === 7 ? `${s}40` : s,
|
|
95
99
|
// Add transparency for hex colors only
|
|
96
|
-
boxpoints:
|
|
100
|
+
boxpoints: E ? r.boxpoints || "outliers" : !1,
|
|
97
101
|
jitter: r.jitter || 0.3,
|
|
98
|
-
pointpos: r.pointpos ||
|
|
102
|
+
pointpos: r.pointpos || $
|
|
99
103
|
};
|
|
100
|
-
}),
|
|
101
|
-
width: d,
|
|
102
|
-
height:
|
|
103
|
-
title:
|
|
104
|
-
margin
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
}), t = {
|
|
105
|
+
width: d.current.width,
|
|
106
|
+
height: d.current.height,
|
|
107
|
+
title: P,
|
|
108
|
+
// Bottom margin reserves room for tick labels, the x-axis title, and the
|
|
109
|
+
// container-anchored bottom legend stacked beneath them.
|
|
110
|
+
margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },
|
|
111
|
+
paper_bgcolor: o.paperBg,
|
|
112
|
+
plot_bgcolor: o.plotBg,
|
|
107
113
|
font: {
|
|
108
114
|
family: "Inter, sans-serif"
|
|
109
115
|
},
|
|
110
116
|
dragmode: !1,
|
|
111
117
|
xaxis: {
|
|
112
118
|
title: {
|
|
113
|
-
text:
|
|
119
|
+
text: x,
|
|
114
120
|
font: {
|
|
115
121
|
size: 16,
|
|
116
|
-
color:
|
|
122
|
+
color: o.textSecondary,
|
|
117
123
|
family: "Inter, sans-serif",
|
|
118
124
|
weight: 400
|
|
119
125
|
},
|
|
120
126
|
standoff: 15
|
|
121
127
|
},
|
|
122
|
-
gridcolor:
|
|
123
|
-
range:
|
|
124
|
-
autorange: !
|
|
128
|
+
gridcolor: o.gridColor,
|
|
129
|
+
range: y,
|
|
130
|
+
autorange: !y,
|
|
125
131
|
showgrid: !0,
|
|
126
|
-
|
|
132
|
+
// Reserve space for tick labels + the axis title so the bottom legend
|
|
133
|
+
// can't overlap them at small sizes (SW-2157).
|
|
134
|
+
automargin: !0,
|
|
135
|
+
...b
|
|
127
136
|
},
|
|
128
137
|
yaxis: {
|
|
129
138
|
title: {
|
|
130
|
-
text:
|
|
139
|
+
text: w,
|
|
131
140
|
font: {
|
|
132
141
|
size: 16,
|
|
133
|
-
color:
|
|
142
|
+
color: o.textSecondary,
|
|
134
143
|
family: "Inter, sans-serif",
|
|
135
144
|
weight: 400
|
|
136
145
|
},
|
|
137
146
|
standoff: 15
|
|
138
147
|
},
|
|
139
|
-
gridcolor:
|
|
140
|
-
range:
|
|
141
|
-
autorange: !
|
|
148
|
+
gridcolor: o.gridColor,
|
|
149
|
+
range: h,
|
|
150
|
+
autorange: !h,
|
|
142
151
|
tickmode: "array",
|
|
143
|
-
tickvals:
|
|
152
|
+
tickvals: O,
|
|
144
153
|
showgrid: !0,
|
|
145
|
-
|
|
154
|
+
automargin: !0,
|
|
155
|
+
...b
|
|
146
156
|
},
|
|
147
157
|
legend: {
|
|
158
|
+
// Anchor to the bottom of the container (not the plot area) so the
|
|
159
|
+
// legend always clears the x-axis tick labels + title — a paper-relative
|
|
160
|
+
// fractional offset collapses into the ticks at small heights (SW-2157).
|
|
148
161
|
x: 0.5,
|
|
149
|
-
y:
|
|
162
|
+
y: 0,
|
|
150
163
|
xanchor: "center",
|
|
151
|
-
yanchor: "
|
|
164
|
+
yanchor: "bottom",
|
|
165
|
+
yref: "container",
|
|
152
166
|
orientation: "h",
|
|
153
167
|
font: {
|
|
154
168
|
size: 13,
|
|
155
|
-
color:
|
|
169
|
+
color: o.legendColor,
|
|
156
170
|
family: "Inter, sans-serif",
|
|
157
171
|
weight: 500,
|
|
158
172
|
lineheight: 18
|
|
159
173
|
}
|
|
160
174
|
},
|
|
161
175
|
showlegend: !0
|
|
162
|
-
},
|
|
163
|
-
|
|
176
|
+
}, c = {
|
|
177
|
+
// Sizing is driven from the measured container; disable Plotly's own
|
|
178
|
+
// window-resize responsiveness (it can't see container resizes).
|
|
179
|
+
responsive: !1,
|
|
164
180
|
displayModeBar: !1,
|
|
165
181
|
displaylogo: !1
|
|
166
182
|
};
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
return () => {
|
|
170
|
-
|
|
183
|
+
k.newPlot(a.current, e, t, c), C(a.current);
|
|
184
|
+
const l = a.current;
|
|
185
|
+
return M.current = !0, m.current = { ...d.current }, () => {
|
|
186
|
+
l && (k.purge(l), M.current = !1);
|
|
171
187
|
};
|
|
172
|
-
}, [p,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
188
|
+
}, [p, _, y, h, f, x, w, E, P, b, O, o, C]), j(() => {
|
|
189
|
+
const e = a.current;
|
|
190
|
+
!e || !M.current || n <= 0 || i <= 0 || m.current.width === n && m.current.height === i || (m.current = { width: n, height: i }, k.relayout(e, { width: n, height: i }).catch(
|
|
191
|
+
() => {
|
|
192
|
+
}
|
|
193
|
+
));
|
|
194
|
+
}, [n, i]), /* @__PURE__ */ Y(
|
|
195
|
+
"div",
|
|
196
|
+
{
|
|
197
|
+
ref: S,
|
|
198
|
+
className: R("boxplot-container relative", T && "w-full", U && "h-full"),
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ H("div", { ref: a, style: { width: "100%", height: "100%" } }),
|
|
201
|
+
A
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
);
|
|
176
205
|
};
|
|
177
206
|
export {
|
|
178
|
-
|
|
207
|
+
et as Boxplot
|
|
179
208
|
};
|
|
180
209
|
//# sourceMappingURL=Boxplot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Boxplot.js","sources":["../../../../src/components/charts/Boxplot/Boxplot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { seriesColor } from \"@/utils/colors\";\n\n/** Default point position offset from the box edge */\nconst DEFAULT_POINT_POSITION = -1.8;\n\ninterface BoxDataSeries {\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n x?: string[] | number[];\n boxpoints?: \"all\" | \"outliers\" | \"suspectedoutliers\" | false;\n jitter?: number;\n pointpos?: number;\n}\n\ninterface BoxplotProps {\n dataSeries: BoxDataSeries[];\n width?: number;\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n xTitle?: string;\n yTitle?: string;\n title?: string;\n showPoints?: boolean;\n}\n\nconst Boxplot: React.FC<BoxplotProps> = ({\n dataSeries,\n width = 1000,\n height = 600,\n xRange,\n yRange,\n xTitle = \"Columns\",\n yTitle = \"Rows\",\n title = \"Boxplot\",\n showPoints = false,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n const { yMin, yMax } = useMemo(() => {\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current) return;\n\n const data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n y: series.y,\n x: series.x,\n type: \"box\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color,\n },\n line: {\n color,\n },\n fillcolor:\n typeof color === \"string\" && color.startsWith(\"#\") && color.length === 7\n ? `${color}40`\n : color, // Add transparency for hex colors only\n boxpoints: showPoints\n ? series.boxpoints || \"outliers\"\n : (false as const),\n jitter: series.jitter || 0.3,\n pointpos: series.pointpos || DEFAULT_POINT_POSITION,\n };\n });\n\n const layout = {\n width,\n height: height,\n title: titleOptions,\n margin: { l: 80, r: 40, b: 80, t: 80, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n showgrid: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n ...tickOptions,\n },\n legend: {\n x: 0.5,\n y: -0.2,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n showlegend: true,\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n // Cleanup function\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [dataSeries, width, height, xRange, yRange, effectiveYRange, xTitle, yTitle, showPoints, titleOptions, tickOptions, yTicks, theme, bindTooltip]);\n\n return (\n <div className=\"boxplot-container relative\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { Boxplot };\nexport type { BoxDataSeries, BoxplotProps };\n"],"names":["DEFAULT_POINT_POSITION","Boxplot","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","showPoints","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","yMin","yMax","useMemo","minY","maxY","series","y","yPadding","effectiveYRange","yTicks","range","step","ticks","current","tickOptions","titleOptions","useEffect","data","index","color","seriesColor","layout","config","Plotly","plotElement","jsxs","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Boxplot.js","sources":["../../../../src/components/charts/Boxplot/Boxplot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\n/** Default point position offset from the box edge */\nconst DEFAULT_POINT_POSITION = -1.8;\n\ninterface BoxDataSeries {\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n x?: string[] | number[];\n boxpoints?: \"all\" | \"outliers\" | \"suspectedoutliers\" | false;\n jitter?: number;\n pointpos?: number;\n}\n\ninterface BoxplotProps {\n dataSeries: BoxDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n xTitle?: string;\n yTitle?: string;\n title?: string;\n showPoints?: boolean;\n}\n\nconst Boxplot: React.FC<BoxplotProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n xTitle,\n yTitle,\n title = \"Boxplot\",\n showPoints = false,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaGraph for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n\n const { yMin, yMax } = useMemo(() => {\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n y: series.y,\n x: series.x,\n type: \"box\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color,\n },\n line: {\n color,\n },\n fillcolor:\n typeof color === \"string\" && color.startsWith(\"#\") && color.length === 7\n ? `${color}40`\n : color, // Add transparency for hex colors only\n boxpoints: showPoints\n ? series.boxpoints || \"outliers\"\n : (false as const),\n jitter: series.jitter || 0.3,\n pointpos: series.pointpos || DEFAULT_POINT_POSITION,\n };\n });\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n title: titleOptions,\n // Bottom margin reserves room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n showlegend: true,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n\n // Cleanup function\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, effectiveYRange, xTitle, yTitle, showPoints, titleOptions, tickOptions, yTicks, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n void Plotly.relayout(plotElement, { width: resolvedWidth, height: resolvedHeight }).catch(\n () => {},\n );\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"boxplot-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { Boxplot };\nexport type { BoxDataSeries, BoxplotProps };\n"],"names":["DEFAULT_POINT_POSITION","Boxplot","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","showPoints","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","yMin","yMax","useMemo","minY","maxY","series","y","yPadding","effectiveYRange","yTicks","range","step","ticks","current","tickOptions","titleOptions","useEffect","data","index","color","seriesColor","layout","config","Plotly","plotElement","jsxs","cn","jsx"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=require("react/jsx-runtime"),x=require("plotly.js-dist"),o=require("react"),D=require("../ChartTooltip/ChartTooltip.cjs"),H=require("../../../hooks/use-element-size.cjs"),L=require("../../../hooks/use-plotly-theme.cjs"),T=require("../../../lib/utils.cjs"),W=require("../../../utils/colors.cjs"),_=({dataSeries:i,width:s,height:b,title:z="Dot Plot",xTitle:h,yTitle:d,variant:C="default",markerSize:g=8})=>{const l=o.useRef(null),t=L.usePlotlyTheme(),{bindTooltip:R,tooltipElement:S}=D.useChartTooltip({xLabel:h,yLabel:d}),[j,v]=H.useElementSize(),r=s??v.width,n=b??v.height,q=r>0&&n>0,A=s===void 0,B=b===void 0,c=o.useRef({width:r,height:n});c.current={width:r,height:n};const m=o.useRef(!1),u=o.useRef({width:0,height:0}),M=o.useMemo(()=>Array.isArray(i)?i:[i],[i]),a=W.CHART_COLORS,y=o.useMemo(()=>["circle","square","diamond","triangle-up","triangle-down","star"],[]),P=o.useMemo(()=>M.map((e,f)=>C==="default"?{...e,color:e.color||a[0],symbol:"circle",size:e.size||g}:{...e,color:e.color||a[f%a.length],symbol:e.symbol||y[f%y.length],size:e.size||g}),[M,C,g,a,y]),p=t.gridColor,k=o.useMemo(()=>P.map(e=>({type:"scatter",x:e.x,y:e.y,mode:"markers",name:e.name,marker:{color:e.color,size:e.size,symbol:e.symbol,line:{color:t.paperBg,width:1}},hoverinfo:"none"})),[P,t]),w=o.useMemo(()=>({tickcolor:t.tickColor,ticklen:12,tickwidth:1,ticks:"outside",tickfont:{size:16,color:t.textColor,family:"Inter, sans-serif",weight:400},linecolor:t.lineColor,linewidth:1,position:0,zeroline:!1}),[t]),E=o.useMemo(()=>({text:z,x:.5,y:.95,xanchor:"center",yanchor:"top",font:{size:32,weight:600,family:"Inter, sans-serif",color:t.textColor,lineheight:1.2,standoff:30}}),[z,t]);return o.useEffect(()=>{if(!l.current||!q)return;const e={width:c.current.width,height:c.current.height,font:{family:"Inter, sans-serif"},title:E,margin:{l:80,r:40,b:96,t:80,pad:0},showlegend:!0,legend:{x:.5,y:0,xanchor:"center",yanchor:"bottom",yref:"container",orientation:"h",font:{size:13,color:t.legendColor,family:"Inter, sans-serif",weight:500,lineheight:18}},xaxis:{title:{text:h,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:p,automargin:!0,...w},yaxis:{title:{text:d,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:p,automargin:!0,...w},paper_bgcolor:t.paperBg,plot_bgcolor:t.plotBg},f={responsive:!1,displayModeBar:!1,displaylogo:!1};x.newPlot(l.current,k,e,f),R(l.current);const I=l.current;return m.current=!0,u.current={...c.current},()=>{I&&(x.purge(I),m.current=!1)}},[q,h,d,k,E,w,p,t,R]),o.useEffect(()=>{const e=l.current;!e||!m.current||r<=0||n<=0||u.current.width===r&&u.current.height===n||(u.current={width:r,height:n},x.relayout(e,{width:r,height:n}).catch(()=>{}))},[r,n]),O.jsxs("div",{ref:j,className:T.cn("dotplot-container relative",A&&"w-full",B&&"h-full"),style:s===void 0?void 0:{width:s},children:[O.jsx("div",{ref:l,style:{width:"100%",height:"100%",margin:"0"}}),S]})};exports.DotPlot=_;
|
|
2
2
|
//# sourceMappingURL=DotPlot.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DotPlot.cjs","sources":["../../../../src/components/charts/DotPlot/DotPlot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { CHART_COLORS } from \"@/utils/colors\";\n\ntype MarkerSymbol =\n | \"circle\"\n | \"square\"\n | \"diamond\"\n | \"triangle-up\"\n | \"triangle-down\"\n | \"star\";\n\ninterface DotPlotDataSeries {\n x: number[];\n y: number[];\n name: string;\n color?: string;\n symbol?: MarkerSymbol;\n size?: number;\n}\n\ntype DotPlotVariant = \"default\" | \"stacked\";\n\ntype DotPlotProps = {\n dataSeries: DotPlotDataSeries | DotPlotDataSeries[];\n width?: number;\n height?: number;\n title?: string;\n xTitle?: string;\n yTitle?: string;\n variant?: DotPlotVariant;\n markerSize?: number;\n};\n\nconst DotPlot: React.FC<DotPlotProps> = ({\n dataSeries,\n width = 1000,\n height = 600,\n title = \"Dot Plot\",\n xTitle = \"Columns\",\n yTitle = \"Rows\",\n variant = \"default\",\n markerSize = 8,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n const seriesArray = useMemo(\n () => (Array.isArray(dataSeries) ? dataSeries : [dataSeries]),\n [dataSeries],\n );\n\n const defaultColors = CHART_COLORS;\n\n const defaultSymbols: MarkerSymbol[] = useMemo(\n () => [\n \"circle\",\n \"square\",\n \"diamond\",\n \"triangle-up\",\n \"triangle-down\",\n \"star\",\n ],\n [],\n );\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n if (variant === \"default\") {\n // Default variant: all circles, use first color or series color\n return {\n ...series,\n color: series.color || defaultColors[0],\n symbol: \"circle\" as MarkerSymbol,\n size: series.size || markerSize,\n };\n } else {\n // Stacked variant: different symbols and colors for each series\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n symbol:\n series.symbol || defaultSymbols[index % defaultSymbols.length],\n size: series.size || markerSize,\n };\n }\n });\n }, [seriesArray, variant, markerSize, defaultColors, defaultSymbols]);\n\n const gridColor = theme.gridColor;\n\n const plotData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"scatter\" as const,\n x: series.x,\n y: series.y,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: series.color,\n size: series.size,\n symbol: series.symbol,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n },\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current) return;\n\n const layout = {\n width,\n height,\n font: {\n family: \"Inter, sans-serif\",\n },\n title: titleOptions,\n margin: { l: 80, r: 40, b: 80, t: 80, pad: 0 },\n showlegend: true,\n legend: {\n x: 0.5,\n y: -0.2,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n ...tickOptions,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [width, height, xTitle, yTitle, plotData, titleOptions, tickOptions, gridColor, theme, bindTooltip]);\n\n return (\n <div className=\"dotplot-container relative\" style={{ width: width }}>\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n {tooltipElement}\n </div>\n );\n};\n\nexport { DotPlot };\nexport type { DotPlotDataSeries, DotPlotProps, DotPlotVariant, MarkerSymbol };\n"],"names":["DotPlot","dataSeries","width","height","title","xTitle","yTitle","variant","markerSize","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","seriesArray","useMemo","defaultColors","CHART_COLORS","defaultSymbols","seriesWithColors","series","index","gridColor","plotData","tickOptions","titleOptions","useEffect","layout","config","Plotly","plotElement","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DotPlot.cjs","sources":["../../../../src/components/charts/DotPlot/DotPlot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { CHART_COLORS } from \"@/utils/colors\";\n\ntype MarkerSymbol =\n | \"circle\"\n | \"square\"\n | \"diamond\"\n | \"triangle-up\"\n | \"triangle-down\"\n | \"star\";\n\ninterface DotPlotDataSeries {\n x: number[];\n y: number[];\n name: string;\n color?: string;\n symbol?: MarkerSymbol;\n size?: number;\n}\n\ntype DotPlotVariant = \"default\" | \"stacked\";\n\ntype DotPlotProps = {\n dataSeries: DotPlotDataSeries | DotPlotDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n title?: string;\n xTitle?: string;\n yTitle?: string;\n variant?: DotPlotVariant;\n markerSize?: number;\n};\n\nconst DotPlot: React.FC<DotPlotProps> = ({\n dataSeries,\n width,\n height,\n title = \"Dot Plot\",\n xTitle,\n yTitle,\n variant = \"default\",\n markerSize = 8,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaGraph for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n const seriesArray = useMemo(\n () => (Array.isArray(dataSeries) ? dataSeries : [dataSeries]),\n [dataSeries],\n );\n\n const defaultColors = CHART_COLORS;\n\n const defaultSymbols: MarkerSymbol[] = useMemo(\n () => [\n \"circle\",\n \"square\",\n \"diamond\",\n \"triangle-up\",\n \"triangle-down\",\n \"star\",\n ],\n [],\n );\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n if (variant === \"default\") {\n // Default variant: all circles, use first color or series color\n return {\n ...series,\n color: series.color || defaultColors[0],\n symbol: \"circle\" as MarkerSymbol,\n size: series.size || markerSize,\n };\n } else {\n // Stacked variant: different symbols and colors for each series\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n symbol:\n series.symbol || defaultSymbols[index % defaultSymbols.length],\n size: series.size || markerSize,\n };\n }\n });\n }, [seriesArray, variant, markerSize, defaultColors, defaultSymbols]);\n\n const gridColor = theme.gridColor;\n\n const plotData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"scatter\" as const,\n x: series.x,\n y: series.y,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: series.color,\n size: series.size,\n symbol: series.symbol,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n },\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n font: {\n family: \"Inter, sans-serif\",\n },\n title: titleOptions,\n // Bottom margin reserves room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },\n showlegend: true,\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n automargin: true,\n ...tickOptions,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [hasSize, xTitle, yTitle, plotData, titleOptions, tickOptions, gridColor, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n void Plotly.relayout(plotElement, { width: resolvedWidth, height: resolvedHeight }).catch(\n () => {},\n );\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"dotplot-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n style={width === undefined ? undefined : { width }}\n >\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n {tooltipElement}\n </div>\n );\n};\n\nexport { DotPlot };\nexport type { DotPlotDataSeries, DotPlotProps, DotPlotVariant, MarkerSymbol };\n"],"names":["DotPlot","dataSeries","width","height","title","xTitle","yTitle","variant","markerSize","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","seriesArray","useMemo","defaultColors","CHART_COLORS","defaultSymbols","seriesWithColors","series","index","gridColor","plotData","tickOptions","titleOptions","useEffect","layout","config","Plotly","plotElement","jsxs","cn","jsx"],"mappings":"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"}
|