@tetrascience-npm/tetrascience-react-ui 0.6.0-beta.97.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 +149 -132
- 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,158 +1,172 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
import { useRef as
|
|
4
|
-
import { useChartTooltip as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import { jsxs as G, jsx as O } from "react/jsx-runtime";
|
|
2
|
+
import g from "plotly.js-dist";
|
|
3
|
+
import { useRef as k, useMemo as m, useEffect as Y } from "react";
|
|
4
|
+
import { useChartTooltip as T } from "../ChartTooltip/ChartTooltip.js";
|
|
5
|
+
import { useElementSize as q } from "../../../hooks/use-element-size.js";
|
|
6
|
+
import { usePlotlyTheme as F } from "../../../hooks/use-plotly-theme.js";
|
|
7
|
+
import { cn as J } from "../../../lib/utils.js";
|
|
8
|
+
import { seriesColor as K } from "../../../utils/colors.js";
|
|
9
|
+
const it = ({
|
|
10
|
+
dataSeries: x,
|
|
11
|
+
width: I,
|
|
12
|
+
height: _,
|
|
13
|
+
xRange: u,
|
|
14
|
+
yRange: p,
|
|
15
|
+
xTitle: v,
|
|
16
|
+
yTitle: b,
|
|
17
|
+
title: L = "Scatter Plot"
|
|
16
18
|
}) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const a = k(null), r = F(), { bindTooltip: N, tooltipElement: j } = T({ xLabel: v, yLabel: b }), [H, A] = q(), s = I ?? A.width, c = _ ?? A.height, P = s > 0 && c > 0, W = I === void 0, D = _ === void 0, w = k({ width: s, height: c });
|
|
20
|
+
w.current = { width: s, height: c };
|
|
21
|
+
const E = k(!1), M = k({ width: 0, height: 0 }), { xMin: S, xMax: X, yMin: U, yMax: V } = m(() => {
|
|
22
|
+
let o = Number.MAX_VALUE, t = Number.MIN_VALUE, n = Number.MAX_VALUE, e = Number.MIN_VALUE;
|
|
23
|
+
x.forEach((l) => {
|
|
20
24
|
l.x.forEach((i) => {
|
|
21
|
-
|
|
25
|
+
o = Math.min(o, i), t = Math.max(t, i);
|
|
22
26
|
}), l.y.forEach((i) => {
|
|
23
|
-
n = Math.min(n, i),
|
|
27
|
+
n = Math.min(n, i), e = Math.max(e, i);
|
|
24
28
|
});
|
|
25
29
|
});
|
|
26
|
-
const
|
|
30
|
+
const d = (t - o) * 0.1, y = (e - n) * 0.1;
|
|
27
31
|
return {
|
|
28
|
-
xMin:
|
|
29
|
-
xMax: t +
|
|
32
|
+
xMin: o - d,
|
|
33
|
+
xMax: t + d,
|
|
30
34
|
yMin: n - y,
|
|
31
|
-
yMax:
|
|
35
|
+
yMax: e + y
|
|
32
36
|
};
|
|
33
|
-
}, [
|
|
34
|
-
() =>
|
|
35
|
-
[
|
|
36
|
-
),
|
|
37
|
-
() =>
|
|
38
|
-
[
|
|
39
|
-
),
|
|
40
|
-
const
|
|
41
|
-
let t = Math.pow(10, Math.floor(Math.log10(
|
|
42
|
-
|
|
37
|
+
}, [x]), h = m(
|
|
38
|
+
() => u || [S, X],
|
|
39
|
+
[u, S, X]
|
|
40
|
+
), f = m(
|
|
41
|
+
() => p || [U, V],
|
|
42
|
+
[p, U, V]
|
|
43
|
+
), z = m(() => {
|
|
44
|
+
const o = h[1] - h[0];
|
|
45
|
+
let t = Math.pow(10, Math.floor(Math.log10(o)));
|
|
46
|
+
o / t > 10 && (t = t * 2), o / t < 4 && (t = t / 2);
|
|
43
47
|
const n = [];
|
|
44
|
-
let
|
|
45
|
-
for (;
|
|
46
|
-
n.push(
|
|
48
|
+
let e = Math.ceil(h[0] / t) * t;
|
|
49
|
+
for (; e <= h[1]; )
|
|
50
|
+
n.push(e), e += t;
|
|
47
51
|
return n;
|
|
48
|
-
}, [
|
|
49
|
-
const
|
|
50
|
-
let t = Math.pow(10, Math.floor(Math.log10(
|
|
51
|
-
|
|
52
|
+
}, [h]), B = m(() => {
|
|
53
|
+
const o = f[1] - f[0];
|
|
54
|
+
let t = Math.pow(10, Math.floor(Math.log10(o)));
|
|
55
|
+
o / t > 10 && (t = t * 2), o / t < 4 && (t = t / 2);
|
|
52
56
|
const n = [];
|
|
53
|
-
let
|
|
54
|
-
for (;
|
|
55
|
-
n.push(
|
|
57
|
+
let e = Math.ceil(f[0] / t) * t;
|
|
58
|
+
for (; e <= f[1]; )
|
|
59
|
+
n.push(e), e += t;
|
|
56
60
|
return n;
|
|
57
|
-
}, [
|
|
61
|
+
}, [f]), C = m(
|
|
58
62
|
() => ({
|
|
59
|
-
tickcolor:
|
|
63
|
+
tickcolor: r.tickColor,
|
|
60
64
|
ticklen: 12,
|
|
61
65
|
tickwidth: 1,
|
|
62
66
|
ticks: "outside",
|
|
63
67
|
tickfont: {
|
|
64
68
|
size: 16,
|
|
65
|
-
color:
|
|
69
|
+
color: r.textColor,
|
|
66
70
|
family: "Inter, sans-serif",
|
|
67
71
|
weight: 400
|
|
68
72
|
},
|
|
69
|
-
linecolor:
|
|
73
|
+
linecolor: r.lineColor,
|
|
70
74
|
linewidth: 1,
|
|
71
75
|
position: 0,
|
|
72
76
|
zeroline: !1
|
|
73
77
|
}),
|
|
74
|
-
[
|
|
78
|
+
[r]
|
|
75
79
|
);
|
|
76
|
-
return
|
|
77
|
-
if (!
|
|
78
|
-
const
|
|
80
|
+
return Y(() => {
|
|
81
|
+
if (!a.current || !P) return;
|
|
82
|
+
const o = x.map((l, i) => ({
|
|
79
83
|
x: l.x,
|
|
80
84
|
y: l.y,
|
|
81
85
|
type: "scatter",
|
|
82
86
|
mode: "markers",
|
|
83
87
|
name: l.name,
|
|
84
88
|
marker: {
|
|
85
|
-
color:
|
|
89
|
+
color: K(i, l.color),
|
|
86
90
|
size: 10,
|
|
87
91
|
symbol: "circle"
|
|
88
92
|
},
|
|
89
93
|
hoverinfo: "none"
|
|
90
94
|
})), t = {
|
|
91
95
|
title: {
|
|
92
|
-
text:
|
|
96
|
+
text: L,
|
|
93
97
|
font: {
|
|
94
98
|
size: 32,
|
|
95
99
|
family: "Inter, sans-serif",
|
|
96
|
-
color:
|
|
100
|
+
color: r.textColor
|
|
97
101
|
}
|
|
98
102
|
},
|
|
99
|
-
width: w,
|
|
100
|
-
height:
|
|
101
|
-
margin
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
width: w.current.width,
|
|
104
|
+
height: w.current.height,
|
|
105
|
+
// Bottom margin reserves room for tick labels, the x-axis title, and the
|
|
106
|
+
// container-anchored bottom legend stacked beneath them.
|
|
107
|
+
margin: { l: 80, r: 30, b: 96, t: 60, pad: 10 },
|
|
108
|
+
paper_bgcolor: r.paperBg,
|
|
109
|
+
plot_bgcolor: r.plotBg,
|
|
104
110
|
font: {
|
|
105
111
|
family: "Inter, sans-serif"
|
|
106
112
|
},
|
|
107
113
|
dragmode: !1,
|
|
108
114
|
xaxis: {
|
|
109
115
|
title: {
|
|
110
|
-
text:
|
|
116
|
+
text: v,
|
|
111
117
|
font: {
|
|
112
118
|
size: 16,
|
|
113
|
-
color:
|
|
119
|
+
color: r.textSecondary,
|
|
114
120
|
family: "Inter, sans-serif",
|
|
115
121
|
weight: 400
|
|
116
122
|
},
|
|
117
123
|
standoff: 32
|
|
118
124
|
},
|
|
119
|
-
gridcolor:
|
|
120
|
-
range:
|
|
121
|
-
autorange: !
|
|
125
|
+
gridcolor: r.gridColor,
|
|
126
|
+
range: u,
|
|
127
|
+
autorange: !u,
|
|
122
128
|
tickmode: "array",
|
|
123
|
-
tickvals:
|
|
124
|
-
ticktext:
|
|
129
|
+
tickvals: z,
|
|
130
|
+
ticktext: z.map(String),
|
|
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
|
+
...C
|
|
127
136
|
},
|
|
128
137
|
yaxis: {
|
|
129
138
|
title: {
|
|
130
|
-
text:
|
|
139
|
+
text: b,
|
|
131
140
|
font: {
|
|
132
141
|
size: 16,
|
|
133
|
-
color:
|
|
142
|
+
color: r.textSecondary,
|
|
134
143
|
family: "Inter, sans-serif",
|
|
135
144
|
weight: 400
|
|
136
145
|
},
|
|
137
146
|
standoff: 30
|
|
138
147
|
},
|
|
139
|
-
gridcolor:
|
|
140
|
-
range:
|
|
141
|
-
autorange: !
|
|
148
|
+
gridcolor: r.gridColor,
|
|
149
|
+
range: p,
|
|
150
|
+
autorange: !p,
|
|
142
151
|
tickmode: "array",
|
|
143
|
-
tickvals:
|
|
152
|
+
tickvals: B,
|
|
144
153
|
showgrid: !0,
|
|
145
|
-
|
|
154
|
+
automargin: !0,
|
|
155
|
+
...C
|
|
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: 16,
|
|
155
|
-
color:
|
|
169
|
+
color: r.legendColor,
|
|
156
170
|
family: "Inter, sans-serif",
|
|
157
171
|
weight: 500
|
|
158
172
|
}
|
|
@@ -160,15 +174,19 @@ const q = ({
|
|
|
160
174
|
showlegend: !0,
|
|
161
175
|
hovermode: "closest"
|
|
162
176
|
}, n = {
|
|
163
|
-
|
|
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
|
-
|
|
183
|
+
g.newPlot(a.current, o, t, n), N(a.current);
|
|
184
|
+
const e = a.current;
|
|
185
|
+
E.current = !0, M.current = { ...w.current };
|
|
186
|
+
const d = e, y = { color: r.spikeColor, width: 2 };
|
|
187
|
+
return d.on("plotly_hover", (l) => {
|
|
170
188
|
const i = l.points[0];
|
|
171
|
-
i &&
|
|
189
|
+
i && g.relayout(e, {
|
|
172
190
|
shapes: [
|
|
173
191
|
{
|
|
174
192
|
type: "line",
|
|
@@ -194,17 +212,23 @@ const q = ({
|
|
|
194
212
|
}
|
|
195
213
|
]
|
|
196
214
|
});
|
|
197
|
-
}),
|
|
198
|
-
|
|
215
|
+
}), d.on("plotly_unhover", () => {
|
|
216
|
+
g.relayout(e, { shapes: [] });
|
|
199
217
|
}), () => {
|
|
200
|
-
|
|
218
|
+
e && (g.purge(e), E.current = !1);
|
|
201
219
|
};
|
|
202
|
-
}, [
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
}, [x, P, u, p, v, b, L, h, f, z, B, C, r, N]), Y(() => {
|
|
221
|
+
const o = a.current;
|
|
222
|
+
!o || !E.current || s <= 0 || c <= 0 || M.current.width === s && M.current.height === c || (M.current = { width: s, height: c }, g.relayout(o, { width: s, height: c }).catch(
|
|
223
|
+
() => {
|
|
224
|
+
}
|
|
225
|
+
));
|
|
226
|
+
}, [s, c]), /* @__PURE__ */ G("div", { ref: H, className: J("relative", W && "w-full", D && "h-full"), children: [
|
|
227
|
+
/* @__PURE__ */ O("div", { ref: a, style: { width: "100%", height: "100%" } }),
|
|
228
|
+
j
|
|
205
229
|
] });
|
|
206
230
|
};
|
|
207
231
|
export {
|
|
208
|
-
|
|
232
|
+
it as ScatterGraph
|
|
209
233
|
};
|
|
210
234
|
//# sourceMappingURL=ScatterGraph.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScatterGraph.js","sources":["../../../../src/components/charts/ScatterGraph/ScatterGraph.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\ninterface ScatterDataPoint {\n x: number;\n y: number;\n additionalInfo?: Record<string, string | number>;\n}\n\ninterface ScatterDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n}\n\ninterface ScatterGraphProps {\n dataSeries: ScatterDataSeries[];\n width?: number;\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n xTitle?: string;\n yTitle?: string;\n title?: string;\n}\n\nconst ScatterGraph: React.FC<ScatterGraphProps> = ({\n dataSeries,\n width = 1000,\n height = 600,\n xRange,\n yRange,\n xTitle = \"Columns\",\n yTitle = \"Rows\",\n title = \"Scatter Plot\",\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n const { xMin, xMax, yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveXRange = useMemo(\n () => xRange || [xMin, xMax],\n [xRange, xMin, xMax],\n );\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(() => {\n const range = effectiveXRange[1] - effectiveXRange[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(effectiveXRange[0] / step) * step;\n while (current <= effectiveXRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveXRange]);\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 useEffect(() => {\n if (!plotRef.current) return;\n\n const plotData = dataSeries.map((series, index) => ({\n x: series.x,\n y: series.y,\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: seriesColor(index, series.color),\n size: 10,\n symbol: \"circle\" as const,\n },\n hoverinfo: \"none\" as const,\n }));\n\n const layout = {\n title: {\n text: title,\n font: {\n size: 32,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n },\n width,\n height,\n margin: { l: 80, r: 30, b: 80, t: 60, pad: 10 },\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: 32,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: xTicks,\n ticktext: xTicks.map(String),\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: 30,\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: 16,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n },\n },\n showlegend: true,\n hovermode: \"closest\" as const,\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 // Crosshair guide lines through the hovered point. Drawn as layout shapes\n // with `layer: \"below\"` so they sit behind the markers — native Plotly\n // spikelines always render on top and can't be moved behind.\n const emitter = plotElement as unknown as Plotly.PlotlyHTMLElement;\n const crosshairLine = { color: theme.spikeColor, width: 2 };\n emitter.on(\"plotly_hover\", (eventData) => {\n const point = eventData.points[0];\n if (!point) return;\n void Plotly.relayout(plotElement, {\n shapes: [\n {\n type: \"line\",\n xref: \"x\",\n yref: \"paper\",\n x0: point.x,\n x1: point.x,\n y0: 0,\n y1: 1,\n line: crosshairLine,\n layer: \"below\",\n },\n {\n type: \"line\",\n xref: \"paper\",\n yref: \"y\",\n x0: 0,\n x1: 1,\n y0: point.y,\n y1: point.y,\n line: crosshairLine,\n layer: \"below\",\n },\n ],\n });\n });\n emitter.on(\"plotly_unhover\", () => {\n void Plotly.relayout(plotElement, { shapes: [] });\n });\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [dataSeries, width, height, xRange, yRange, xTitle, yTitle, title, effectiveXRange, effectiveYRange, xTicks, yTicks, tickOptions, theme, bindTooltip]);\n\n return (\n <div className=\"relative size-full\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { ScatterGraph };\nexport type { ScatterDataPoint, ScatterDataSeries, ScatterGraphProps };\n"],"names":["ScatterGraph","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","xMin","xMax","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveXRange","effectiveYRange","xTicks","range","step","ticks","current","yTicks","tickOptions","useEffect","plotData","index","seriesColor","layout","config","Plotly","plotElement","emitter","crosshairLine","eventData","point","jsxs","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ScatterGraph.js","sources":["../../../../src/components/charts/ScatterGraph/ScatterGraph.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\ninterface ScatterDataPoint {\n x: number;\n y: number;\n additionalInfo?: Record<string, string | number>;\n}\n\ninterface ScatterDataSeries {\n x: number[];\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n}\n\ninterface ScatterGraphProps {\n dataSeries: ScatterDataSeries[];\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}\n\nconst ScatterGraph: React.FC<ScatterGraphProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n xTitle,\n yTitle,\n title = \"Scatter Plot\",\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 { xMin, xMax, yMin, yMax } = useMemo(() => {\n let minX = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.x.forEach((x) => {\n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n });\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const xPadding = (maxX - minX) * 0.1;\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n xMin: minX - xPadding,\n xMax: maxX + xPadding,\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveXRange = useMemo(\n () => xRange || [xMin, xMax],\n [xRange, xMin, xMax],\n );\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const xTicks = useMemo(() => {\n const range = effectiveXRange[1] - effectiveXRange[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(effectiveXRange[0] / step) * step;\n while (current <= effectiveXRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveXRange]);\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 useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const plotData = dataSeries.map((series, index) => ({\n x: series.x,\n y: series.y,\n type: \"scatter\" as const,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: seriesColor(index, series.color),\n size: 10,\n symbol: \"circle\" as const,\n },\n hoverinfo: \"none\" as const,\n }));\n\n const layout = {\n title: {\n text: title,\n font: {\n size: 32,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n },\n },\n width: sizeRef.current.width,\n height: sizeRef.current.height,\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: 30, b: 96, t: 60, pad: 10 },\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: 32,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n tickmode: \"array\" as const,\n tickvals: xTicks,\n ticktext: xTicks.map(String),\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: 30,\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: 16,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n },\n },\n showlegend: true,\n hovermode: \"closest\" as const,\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 // Crosshair guide lines through the hovered point. Drawn as layout shapes\n // with `layer: \"below\"` so they sit behind the markers — native Plotly\n // spikelines always render on top and can't be moved behind.\n const emitter = plotElement as unknown as Plotly.PlotlyHTMLElement;\n const crosshairLine = { color: theme.spikeColor, width: 2 };\n emitter.on(\"plotly_hover\", (eventData) => {\n const point = eventData.points[0];\n if (!point) return;\n void Plotly.relayout(plotElement, {\n shapes: [\n {\n type: \"line\",\n xref: \"x\",\n yref: \"paper\",\n x0: point.x,\n x1: point.x,\n y0: 0,\n y1: 1,\n line: crosshairLine,\n layer: \"below\",\n },\n {\n type: \"line\",\n xref: \"paper\",\n yref: \"y\",\n x0: 0,\n x1: 1,\n y0: point.y,\n y1: point.y,\n line: crosshairLine,\n layer: \"below\",\n },\n ],\n });\n });\n emitter.on(\"plotly_unhover\", () => {\n void Plotly.relayout(plotElement, { shapes: [] });\n });\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, xTitle, yTitle, title, effectiveXRange, effectiveYRange, xTicks, yTicks, tickOptions, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves the hover/crosshair 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 ref={containerRef} className={cn(\"relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}>\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { ScatterGraph };\nexport type { ScatterDataPoint, ScatterDataSeries, ScatterGraphProps };\n"],"names":["ScatterGraph","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","xMin","xMax","yMin","yMax","useMemo","minX","maxX","minY","maxY","series","x","y","xPadding","yPadding","effectiveXRange","effectiveYRange","xTicks","range","step","ticks","current","yTicks","tickOptions","useEffect","plotData","index","seriesColor","layout","config","Plotly","plotElement","emitter","crosshairLine","eventData","point","jsxs","cn","jsx"],"mappings":";;;;;;;;AA2CA,MAAMA,KAA4C,CAAC;AAAA,EACjD,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,OAAAC,IAAQ;AACV,MAAM;AACJ,QAAMC,IAAUC,EAAuB,IAAI,GACrCC,IAAQC,EAAA,GACR,EAAE,aAAAC,GAAa,gBAAAC,EAAA,IAAmBC,EAAgB,EAAE,QAAQT,GAAQ,QAAQC,GAAQ,GAIpF,CAACS,GAAcC,CAAQ,IAAIC,EAAA,GAC3BC,IAAgBjB,KAASe,EAAS,OAClCG,IAAiBjB,KAAUc,EAAS,QACpCI,IAAUF,IAAgB,KAAKC,IAAiB,GAGhDE,IAAYpB,MAAU,QACtBqB,IAAapB,MAAW,QACxBqB,IAAUd,EAAO,EAAE,OAAOS,GAAe,QAAQC,GAAgB;AACvE,EAAAI,EAAQ,UAAU,EAAE,OAAOL,GAAe,QAAQC,EAAA;AAClD,QAAMK,IAAgBf,EAAO,EAAK,GAG5BgB,IAAiBhB,EAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,GAE/C,EAAE,MAAAiB,GAAM,MAAAC,GAAM,MAAAC,GAAM,MAAAC,EAAA,IAASC,EAAQ,MAAM;AAC/C,QAAIC,IAAO,OAAO,WACdC,IAAO,OAAO,WACdC,IAAO,OAAO,WACdC,IAAO,OAAO;AAElB,IAAAlC,EAAW,QAAQ,CAACmC,MAAW;AAC7B,MAAAA,EAAO,EAAE,QAAQ,CAACC,MAAM;AACtB,QAAAL,IAAO,KAAK,IAAIA,GAAMK,CAAC,GACvBJ,IAAO,KAAK,IAAIA,GAAMI,CAAC;AAAA,MACzB,CAAC,GACDD,EAAO,EAAE,QAAQ,CAACE,MAAM;AACtB,QAAAJ,IAAO,KAAK,IAAIA,GAAMI,CAAC,GACvBH,IAAO,KAAK,IAAIA,GAAMG,CAAC;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAED,UAAMC,KAAYN,IAAOD,KAAQ,KAC3BQ,KAAYL,IAAOD,KAAQ;AAEjC,WAAO;AAAA,MACL,MAAMF,IAAOO;AAAA,MACb,MAAMN,IAAOM;AAAA,MACb,MAAML,IAAOM;AAAA,MACb,MAAML,IAAOK;AAAA,IAAA;AAAA,EAEjB,GAAG,CAACvC,CAAU,CAAC,GAETwC,IAAkBV;AAAA,IACtB,MAAM3B,KAAU,CAACuB,GAAMC,CAAI;AAAA,IAC3B,CAACxB,GAAQuB,GAAMC,CAAI;AAAA,EAAA,GAGfc,IAAkBX;AAAA,IACtB,MAAM1B,KAAU,CAACwB,GAAMC,CAAI;AAAA,IAC3B,CAACzB,GAAQwB,GAAMC,CAAI;AAAA,EAAA,GAGfa,IAASZ,EAAQ,MAAM;AAC3B,UAAMa,IAAQH,EAAgB,CAAC,IAAIA,EAAgB,CAAC;AACpD,QAAII,IAAO,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,MAAMD,CAAK,CAAC,CAAC;AAErD,IAAIA,IAAQC,IAAO,OAAIA,IAAOA,IAAO,IACjCD,IAAQC,IAAO,MAAGA,IAAOA,IAAO;AAEpC,UAAMC,IAAQ,CAAA;AACd,QAAIC,IAAU,KAAK,KAAKN,EAAgB,CAAC,IAAII,CAAI,IAAIA;AACrD,WAAOE,KAAWN,EAAgB,CAAC;AACjC,MAAAK,EAAM,KAAKC,CAAO,GAClBA,KAAWF;AAEb,WAAOC;AAAA,EACT,GAAG,CAACL,CAAe,CAAC,GAEdO,IAASjB,EAAQ,MAAM;AAC3B,UAAMa,IAAQF,EAAgB,CAAC,IAAIA,EAAgB,CAAC;AACpD,QAAIG,IAAO,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,MAAMD,CAAK,CAAC,CAAC;AAErD,IAAIA,IAAQC,IAAO,OAAIA,IAAOA,IAAO,IACjCD,IAAQC,IAAO,MAAGA,IAAOA,IAAO;AAEpC,UAAMC,IAAQ,CAAA;AACd,QAAIC,IAAU,KAAK,KAAKL,EAAgB,CAAC,IAAIG,CAAI,IAAIA;AACrD,WAAOE,KAAWL,EAAgB,CAAC;AACjC,MAAAI,EAAM,KAAKC,CAAO,GAClBA,KAAWF;AAEb,WAAOC;AAAA,EACT,GAAG,CAACJ,CAAe,CAAC,GAEdO,IAAclB;AAAA,IAClB,OAAO;AAAA,MACL,WAAWpB,EAAM;AAAA,MACjB,SAAS;AAAA,MACT,WAAW;AAAA,MACX,OAAO;AAAA,MACP,UAAU;AAAA,QACR,MAAM;AAAA,QACN,OAAOA,EAAM;AAAA,QACb,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA;AAAA,MAEV,WAAWA,EAAM;AAAA,MACjB,WAAW;AAAA,MACX,UAAU;AAAA,MACV,UAAU;AAAA,IAAA;AAAA,IAEZ,CAACA,CAAK;AAAA,EAAA;AAGR,SAAAuC,EAAU,MAAM;AACd,QAAI,CAACzC,EAAQ,WAAW,CAACY,EAAS;AAElC,UAAM8B,IAAWlD,EAAW,IAAI,CAACmC,GAAQgB,OAAW;AAAA,MAClD,GAAGhB,EAAO;AAAA,MACV,GAAGA,EAAO;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAMA,EAAO;AAAA,MACb,QAAQ;AAAA,QACN,OAAOiB,EAAYD,GAAOhB,EAAO,KAAK;AAAA,QACtC,MAAM;AAAA,QACN,QAAQ;AAAA,MAAA;AAAA,MAEV,WAAW;AAAA,IAAA,EACX,GAEIkB,IAAS;AAAA,MACb,OAAO;AAAA,QACL,MAAM9C;AAAA,QACN,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,OAAOG,EAAM;AAAA,QAAA;AAAA,MACf;AAAA,MAEF,OAAOa,EAAQ,QAAQ;AAAA,MACvB,QAAQA,EAAQ,QAAQ;AAAA;AAAA;AAAA,MAGxB,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,KAAK,GAAA;AAAA,MAC3C,eAAeb,EAAM;AAAA,MACrB,cAAcA,EAAM;AAAA,MACpB,MAAM;AAAA,QACJ,QAAQ;AAAA,MAAA;AAAA,MAEV,UAAU;AAAA,MACV,OAAO;AAAA,QACL,OAAO;AAAA,UACL,MAAML;AAAA,UACN,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAOK,EAAM;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,UAAA;AAAA,UAEV,UAAU;AAAA,QAAA;AAAA,QAEZ,WAAWA,EAAM;AAAA,QACjB,OAAOP;AAAA,QACP,WAAW,CAACA;AAAA,QACZ,UAAU;AAAA,QACV,UAAUuC;AAAA,QACV,UAAUA,EAAO,IAAI,MAAM;AAAA,QAC3B,UAAU;AAAA;AAAA;AAAA,QAGV,YAAY;AAAA,QACZ,GAAGM;AAAA,MAAA;AAAA,MAEL,OAAO;AAAA,QACL,OAAO;AAAA,UACL,MAAM1C;AAAA,UACN,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAOI,EAAM;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,UAAA;AAAA,UAEV,UAAU;AAAA,QAAA;AAAA,QAEZ,WAAWA,EAAM;AAAA,QACjB,OAAON;AAAA,QACP,WAAW,CAACA;AAAA,QACZ,UAAU;AAAA,QACV,UAAU2C;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,GAAGC;AAAA,MAAA;AAAA,MAEL,QAAQ;AAAA;AAAA;AAAA;AAAA,QAIN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,QACT,SAAS;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,QACb,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,OAAOtC,EAAM;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,QAAA;AAAA,MACV;AAAA,MAEF,YAAY;AAAA,MACZ,WAAW;AAAA,IAAA,GAGP4C,IAAS;AAAA;AAAA;AAAA,MAGb,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,IAAA;AAGf,IAAAC,EAAO,QAAQ/C,EAAQ,SAAS0C,GAAUG,GAAQC,CAAM,GACxD1C,EAAYJ,EAAQ,OAAO;AAG3B,UAAMgD,IAAchD,EAAQ;AAC5B,IAAAgB,EAAc,UAAU,IACxBC,EAAe,UAAU,EAAE,GAAGF,EAAQ,QAAA;AAKtC,UAAMkC,IAAUD,GACVE,IAAgB,EAAE,OAAOhD,EAAM,YAAY,OAAO,EAAA;AACxD,WAAA+C,EAAQ,GAAG,gBAAgB,CAACE,MAAc;AACxC,YAAMC,IAAQD,EAAU,OAAO,CAAC;AAChC,MAAKC,KACAL,EAAO,SAASC,GAAa;AAAA,QAChC,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,IAAII,EAAM;AAAA,YACV,IAAIA,EAAM;AAAA,YACV,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,MAAMF;AAAA,YACN,OAAO;AAAA,UAAA;AAAA,UAET;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,IAAIE,EAAM;AAAA,YACV,IAAIA,EAAM;AAAA,YACV,MAAMF;AAAA,YACN,OAAO;AAAA,UAAA;AAAA,QACT;AAAA,MACF,CACD;AAAA,IACH,CAAC,GACDD,EAAQ,GAAG,kBAAkB,MAAM;AACjC,MAAKF,EAAO,SAASC,GAAa,EAAE,QAAQ,CAAA,GAAI;AAAA,IAClD,CAAC,GAEM,MAAM;AACX,MAAIA,MACFD,EAAO,MAAMC,CAAW,GACxBhC,EAAc,UAAU;AAAA,IAE5B;AAAA,EACF,GAAG,CAACxB,GAAYoB,GAASjB,GAAQC,GAAQC,GAAQC,GAAQC,GAAOiC,GAAiBC,GAAiBC,GAAQK,GAAQC,GAAatC,GAAOE,CAAW,CAAC,GAIlJqC,EAAU,MAAM;AACd,UAAMO,IAAchD,EAAQ;AAC5B,IAAI,CAACgD,KAAe,CAAChC,EAAc,WAAWN,KAAiB,KAAKC,KAAkB,KAMpFM,EAAe,QAAQ,UAAUP,KACjCO,EAAe,QAAQ,WAAWN,MAIpCM,EAAe,UAAU,EAAE,OAAOP,GAAe,QAAQC,EAAA,GAEpDoC,EAAO,SAASC,GAAa,EAAE,OAAOtC,GAAe,QAAQC,EAAA,CAAgB,EAAE;AAAA,MAClF,MAAM;AAAA,MAAC;AAAA,IAAA;AAAA,EAEX,GAAG,CAACD,GAAeC,CAAc,CAAC,GAGhC,gBAAA0C,EAAC,OAAA,EAAI,KAAK9C,GAAc,WAAW+C,EAAG,YAAYzC,KAAa,UAAUC,KAAc,QAAQ,GAC7F,UAAA;AAAA,IAAA,gBAAAyC,EAAC,OAAA,EAAI,KAAKvD,GAAS,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAA,EAAO,CAAG;AAAA,IAC5DK;AAAA,EAAA,GACH;AAEJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react");function f(){const r=n.useRef(null),[u,h]=n.useState({width:0,height:0});return n.useEffect(()=>{const s=r.current;if(!s||typeof ResizeObserver>"u")return;const i=new ResizeObserver(d=>{const e=d[0];if(!e)return;const o=Math.round(e.contentRect.width),c=Math.round(e.contentRect.height);h(t=>t.width===o&&t.height===c?t:{width:o,height:c})});return i.observe(s),()=>i.disconnect()},[]),[r,u]}exports.useElementSize=f;
|
|
2
|
+
//# sourceMappingURL=use-element-size.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-element-size.cjs","sources":["../../src/hooks/use-element-size.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\n/** Element dimensions in CSS pixels, rounded to whole numbers. */\nexport interface ElementSize {\n width: number;\n height: number;\n}\n\n/**\n * Observe an element's content-box size with a `ResizeObserver` and return the\n * latest rounded `{ width, height }`.\n *\n * Attach the returned `ref` to the element whose size should drive layout\n * (e.g. the flex/grid cell a chart needs to fill). The size is `{ 0, 0 }` until\n * the first measurement, and stays `{ 0, 0 }` in environments without a\n * `ResizeObserver` (e.g. SSR), so callers should treat a zero dimension as\n * \"not yet measured\" and skip work that needs real pixels.\n *\n * @example\n * ```tsx\n * const [containerRef, { width, height }] = useElementSize<HTMLDivElement>();\n * return <div ref={containerRef} className=\"size-full\">…</div>;\n * ```\n */\nexport function useElementSize<T extends HTMLElement = HTMLDivElement>(): [\n React.RefObject<T | null>,\n ElementSize,\n] {\n const ref = useRef<T>(null);\n const [size, setSize] = useState<ElementSize>({ width: 0, height: 0 });\n\n useEffect(() => {\n const element = ref.current;\n if (!element || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const width = Math.round(entry.contentRect.width);\n const height = Math.round(entry.contentRect.height);\n // Bail on identical measurements so consumers driving Plotly layout off\n // this size don't trigger a redundant relayout (and a feedback loop).\n setSize((prev) =>\n prev.width === width && prev.height === height ? prev : { width, height },\n );\n });\n\n observer.observe(element);\n return () => observer.disconnect();\n }, []);\n\n return [ref, size];\n}\n"],"names":["useElementSize","ref","useRef","size","setSize","useState","useEffect","element","observer","entries","entry","width","height","prev"],"mappings":"yGAwBO,SAASA,GAGd,CACA,MAAMC,EAAMC,EAAAA,OAAU,IAAI,EACpB,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SAAsB,CAAE,MAAO,EAAG,OAAQ,EAAG,EAErEC,OAAAA,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAUN,EAAI,QACpB,GAAI,CAACM,GAAW,OAAO,eAAmB,IAAa,OAEvD,MAAMC,EAAW,IAAI,eAAgBC,GAAY,CAC/C,MAAMC,EAAQD,EAAQ,CAAC,EACvB,GAAI,CAACC,EAAO,OACZ,MAAMC,EAAQ,KAAK,MAAMD,EAAM,YAAY,KAAK,EAC1CE,EAAS,KAAK,MAAMF,EAAM,YAAY,MAAM,EAGlDN,EAASS,GACPA,EAAK,QAAUF,GAASE,EAAK,SAAWD,EAASC,EAAO,CAAE,MAAAF,EAAO,OAAAC,CAAA,CAAO,CAE5E,CAAC,EAED,OAAAJ,EAAS,QAAQD,CAAO,EACjB,IAAMC,EAAS,WAAA,CACxB,EAAG,CAAA,CAAE,EAEE,CAACP,EAAKE,CAAI,CACnB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useRef as f, useState as d, useEffect as w } from "react";
|
|
2
|
+
function R() {
|
|
3
|
+
const n = f(null), [c, h] = d({ width: 0, height: 0 });
|
|
4
|
+
return w(() => {
|
|
5
|
+
const r = n.current;
|
|
6
|
+
if (!r || typeof ResizeObserver > "u") return;
|
|
7
|
+
const s = new ResizeObserver((u) => {
|
|
8
|
+
const e = u[0];
|
|
9
|
+
if (!e) return;
|
|
10
|
+
const i = Math.round(e.contentRect.width), o = Math.round(e.contentRect.height);
|
|
11
|
+
h(
|
|
12
|
+
(t) => t.width === i && t.height === o ? t : { width: i, height: o }
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
return s.observe(r), () => s.disconnect();
|
|
16
|
+
}, []), [n, c];
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
R as useElementSize
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=use-element-size.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-element-size.js","sources":["../../src/hooks/use-element-size.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\n/** Element dimensions in CSS pixels, rounded to whole numbers. */\nexport interface ElementSize {\n width: number;\n height: number;\n}\n\n/**\n * Observe an element's content-box size with a `ResizeObserver` and return the\n * latest rounded `{ width, height }`.\n *\n * Attach the returned `ref` to the element whose size should drive layout\n * (e.g. the flex/grid cell a chart needs to fill). The size is `{ 0, 0 }` until\n * the first measurement, and stays `{ 0, 0 }` in environments without a\n * `ResizeObserver` (e.g. SSR), so callers should treat a zero dimension as\n * \"not yet measured\" and skip work that needs real pixels.\n *\n * @example\n * ```tsx\n * const [containerRef, { width, height }] = useElementSize<HTMLDivElement>();\n * return <div ref={containerRef} className=\"size-full\">…</div>;\n * ```\n */\nexport function useElementSize<T extends HTMLElement = HTMLDivElement>(): [\n React.RefObject<T | null>,\n ElementSize,\n] {\n const ref = useRef<T>(null);\n const [size, setSize] = useState<ElementSize>({ width: 0, height: 0 });\n\n useEffect(() => {\n const element = ref.current;\n if (!element || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const width = Math.round(entry.contentRect.width);\n const height = Math.round(entry.contentRect.height);\n // Bail on identical measurements so consumers driving Plotly layout off\n // this size don't trigger a redundant relayout (and a feedback loop).\n setSize((prev) =>\n prev.width === width && prev.height === height ? prev : { width, height },\n );\n });\n\n observer.observe(element);\n return () => observer.disconnect();\n }, []);\n\n return [ref, size];\n}\n"],"names":["useElementSize","ref","useRef","size","setSize","useState","useEffect","element","observer","entries","entry","width","height","prev"],"mappings":";AAwBO,SAASA,IAGd;AACA,QAAMC,IAAMC,EAAU,IAAI,GACpB,CAACC,GAAMC,CAAO,IAAIC,EAAsB,EAAE,OAAO,GAAG,QAAQ,GAAG;AAErE,SAAAC,EAAU,MAAM;AACd,UAAMC,IAAUN,EAAI;AACpB,QAAI,CAACM,KAAW,OAAO,iBAAmB,IAAa;AAEvD,UAAMC,IAAW,IAAI,eAAe,CAACC,MAAY;AAC/C,YAAMC,IAAQD,EAAQ,CAAC;AACvB,UAAI,CAACC,EAAO;AACZ,YAAMC,IAAQ,KAAK,MAAMD,EAAM,YAAY,KAAK,GAC1CE,IAAS,KAAK,MAAMF,EAAM,YAAY,MAAM;AAGlD,MAAAN;AAAA,QAAQ,CAACS,MACPA,EAAK,UAAUF,KAASE,EAAK,WAAWD,IAASC,IAAO,EAAE,OAAAF,GAAO,QAAAC,EAAA;AAAA,MAAO;AAAA,IAE5E,CAAC;AAED,WAAAJ,EAAS,QAAQD,CAAO,GACjB,MAAMC,EAAS,WAAA;AAAA,EACxB,GAAG,CAAA,CAAE,GAEE,CAACP,GAAKE,CAAI;AACnB;"}
|