@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,23 +1,27 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
import { useRef as
|
|
4
|
-
import { useChartTooltip as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
import { jsxs as j, jsx as M } from "react/jsx-runtime";
|
|
2
|
+
import z from "plotly.js-dist";
|
|
3
|
+
import { useRef as u, useMemo as i, useEffect as D } from "react";
|
|
4
|
+
import { useChartTooltip as S } from "../ChartTooltip/ChartTooltip.js";
|
|
5
|
+
import { useElementSize as q } from "../../../hooks/use-element-size.js";
|
|
6
|
+
import { usePlotlyTheme as N } from "../../../hooks/use-plotly-theme.js";
|
|
7
|
+
import { cn as F } from "../../../lib/utils.js";
|
|
8
|
+
import { CHART_COLORS as G } from "../../../utils/colors.js";
|
|
9
|
+
const Z = ({
|
|
8
10
|
dataSeries: l,
|
|
9
|
-
width:
|
|
10
|
-
height:
|
|
11
|
-
title:
|
|
12
|
-
xTitle:
|
|
13
|
-
yTitle:
|
|
14
|
-
variant:
|
|
15
|
-
markerSize:
|
|
11
|
+
width: c,
|
|
12
|
+
height: b,
|
|
13
|
+
title: C = "Dot Plot",
|
|
14
|
+
xTitle: d,
|
|
15
|
+
yTitle: m,
|
|
16
|
+
variant: v = "default",
|
|
17
|
+
markerSize: g = 8
|
|
16
18
|
}) => {
|
|
17
|
-
const
|
|
19
|
+
const n = u(null), o = N(), { bindTooltip: R, tooltipElement: H } = S({ xLabel: d, yLabel: m }), [L, k] = q(), e = c ?? k.width, r = b ?? k.height, I = e > 0 && r > 0, W = c === void 0, _ = b === void 0, s = u({ width: e, height: r });
|
|
20
|
+
s.current = { width: e, height: r };
|
|
21
|
+
const p = u(!1), a = u({ width: 0, height: 0 }), E = i(
|
|
18
22
|
() => Array.isArray(l) ? l : [l],
|
|
19
23
|
[l]
|
|
20
|
-
),
|
|
24
|
+
), f = G, y = i(
|
|
21
25
|
() => [
|
|
22
26
|
"circle",
|
|
23
27
|
"square",
|
|
@@ -27,56 +31,56 @@ const H = ({
|
|
|
27
31
|
"star"
|
|
28
32
|
],
|
|
29
33
|
[]
|
|
30
|
-
),
|
|
31
|
-
...
|
|
32
|
-
color:
|
|
34
|
+
), P = i(() => E.map((t, h) => v === "default" ? {
|
|
35
|
+
...t,
|
|
36
|
+
color: t.color || f[0],
|
|
33
37
|
symbol: "circle",
|
|
34
|
-
size:
|
|
38
|
+
size: t.size || g
|
|
35
39
|
} : {
|
|
36
|
-
...
|
|
37
|
-
color:
|
|
38
|
-
symbol:
|
|
39
|
-
size:
|
|
40
|
-
}), [
|
|
41
|
-
() =>
|
|
40
|
+
...t,
|
|
41
|
+
color: t.color || f[h % f.length],
|
|
42
|
+
symbol: t.symbol || y[h % y.length],
|
|
43
|
+
size: t.size || g
|
|
44
|
+
}), [E, v, g, f, y]), w = o.gridColor, A = i(
|
|
45
|
+
() => P.map((t) => ({
|
|
42
46
|
type: "scatter",
|
|
43
|
-
x:
|
|
44
|
-
y:
|
|
47
|
+
x: t.x,
|
|
48
|
+
y: t.y,
|
|
45
49
|
mode: "markers",
|
|
46
|
-
name:
|
|
50
|
+
name: t.name,
|
|
47
51
|
marker: {
|
|
48
|
-
color:
|
|
49
|
-
size:
|
|
50
|
-
symbol:
|
|
52
|
+
color: t.color,
|
|
53
|
+
size: t.size,
|
|
54
|
+
symbol: t.symbol,
|
|
51
55
|
line: {
|
|
52
|
-
color:
|
|
56
|
+
color: o.paperBg,
|
|
53
57
|
width: 1
|
|
54
58
|
}
|
|
55
59
|
},
|
|
56
60
|
hoverinfo: "none"
|
|
57
61
|
})),
|
|
58
|
-
[
|
|
59
|
-
),
|
|
62
|
+
[P, o]
|
|
63
|
+
), x = i(
|
|
60
64
|
() => ({
|
|
61
|
-
tickcolor:
|
|
65
|
+
tickcolor: o.tickColor,
|
|
62
66
|
ticklen: 12,
|
|
63
67
|
tickwidth: 1,
|
|
64
68
|
ticks: "outside",
|
|
65
69
|
tickfont: {
|
|
66
70
|
size: 16,
|
|
67
|
-
color:
|
|
71
|
+
color: o.textColor,
|
|
68
72
|
family: "Inter, sans-serif",
|
|
69
73
|
weight: 400
|
|
70
74
|
},
|
|
71
|
-
linecolor:
|
|
75
|
+
linecolor: o.lineColor,
|
|
72
76
|
linewidth: 1,
|
|
73
77
|
position: 0,
|
|
74
78
|
zeroline: !1
|
|
75
79
|
}),
|
|
76
|
-
[
|
|
77
|
-
),
|
|
80
|
+
[o]
|
|
81
|
+
), B = i(
|
|
78
82
|
() => ({
|
|
79
|
-
text:
|
|
83
|
+
text: C,
|
|
80
84
|
x: 0.5,
|
|
81
85
|
y: 0.95,
|
|
82
86
|
xanchor: "center",
|
|
@@ -85,33 +89,39 @@ const H = ({
|
|
|
85
89
|
size: 32,
|
|
86
90
|
weight: 600,
|
|
87
91
|
family: "Inter, sans-serif",
|
|
88
|
-
color:
|
|
92
|
+
color: o.textColor,
|
|
89
93
|
lineheight: 1.2,
|
|
90
94
|
standoff: 30
|
|
91
95
|
}
|
|
92
96
|
}),
|
|
93
|
-
[
|
|
97
|
+
[C, o]
|
|
94
98
|
);
|
|
95
|
-
return
|
|
96
|
-
if (!
|
|
97
|
-
const
|
|
98
|
-
width: s,
|
|
99
|
-
height:
|
|
99
|
+
return D(() => {
|
|
100
|
+
if (!n.current || !I) return;
|
|
101
|
+
const t = {
|
|
102
|
+
width: s.current.width,
|
|
103
|
+
height: s.current.height,
|
|
100
104
|
font: {
|
|
101
105
|
family: "Inter, sans-serif"
|
|
102
106
|
},
|
|
103
|
-
title:
|
|
104
|
-
margin
|
|
107
|
+
title: B,
|
|
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 },
|
|
105
111
|
showlegend: !0,
|
|
106
112
|
legend: {
|
|
113
|
+
// Anchor to the bottom of the container (not the plot area) so the
|
|
114
|
+
// legend always clears the x-axis tick labels + title — a paper-relative
|
|
115
|
+
// fractional offset collapses into the ticks at small heights (SW-2157).
|
|
107
116
|
x: 0.5,
|
|
108
|
-
y:
|
|
117
|
+
y: 0,
|
|
109
118
|
xanchor: "center",
|
|
110
|
-
yanchor: "
|
|
119
|
+
yanchor: "bottom",
|
|
120
|
+
yref: "container",
|
|
111
121
|
orientation: "h",
|
|
112
122
|
font: {
|
|
113
123
|
size: 13,
|
|
114
|
-
color:
|
|
124
|
+
color: o.legendColor,
|
|
115
125
|
family: "Inter, sans-serif",
|
|
116
126
|
weight: 500,
|
|
117
127
|
lineheight: 18
|
|
@@ -119,60 +129,80 @@ const H = ({
|
|
|
119
129
|
},
|
|
120
130
|
xaxis: {
|
|
121
131
|
title: {
|
|
122
|
-
text:
|
|
132
|
+
text: d,
|
|
123
133
|
font: {
|
|
124
134
|
size: 16,
|
|
125
|
-
color:
|
|
135
|
+
color: o.textSecondary,
|
|
126
136
|
family: "Inter, sans-serif",
|
|
127
137
|
weight: 400
|
|
128
138
|
},
|
|
129
139
|
standoff: 15
|
|
130
140
|
},
|
|
131
|
-
gridcolor:
|
|
132
|
-
|
|
141
|
+
gridcolor: w,
|
|
142
|
+
// Reserve space for tick labels + the axis title so the bottom legend
|
|
143
|
+
// can't overlap them at small sizes (SW-2157).
|
|
144
|
+
automargin: !0,
|
|
145
|
+
...x
|
|
133
146
|
},
|
|
134
147
|
yaxis: {
|
|
135
148
|
title: {
|
|
136
|
-
text:
|
|
149
|
+
text: m,
|
|
137
150
|
font: {
|
|
138
151
|
size: 16,
|
|
139
|
-
color:
|
|
152
|
+
color: o.textSecondary,
|
|
140
153
|
family: "Inter, sans-serif",
|
|
141
154
|
weight: 400
|
|
142
155
|
},
|
|
143
156
|
standoff: 15
|
|
144
157
|
},
|
|
145
|
-
gridcolor:
|
|
146
|
-
|
|
158
|
+
gridcolor: w,
|
|
159
|
+
automargin: !0,
|
|
160
|
+
...x
|
|
147
161
|
},
|
|
148
|
-
paper_bgcolor:
|
|
149
|
-
plot_bgcolor:
|
|
150
|
-
},
|
|
151
|
-
|
|
162
|
+
paper_bgcolor: o.paperBg,
|
|
163
|
+
plot_bgcolor: o.plotBg
|
|
164
|
+
}, h = {
|
|
165
|
+
// Sizing is driven from the measured container; disable Plotly's own
|
|
166
|
+
// window-resize responsiveness (it can't see container resizes).
|
|
167
|
+
responsive: !1,
|
|
152
168
|
displayModeBar: !1,
|
|
153
169
|
displaylogo: !1
|
|
154
170
|
};
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
return () => {
|
|
158
|
-
|
|
171
|
+
z.newPlot(n.current, A, t, h), R(n.current);
|
|
172
|
+
const O = n.current;
|
|
173
|
+
return p.current = !0, a.current = { ...s.current }, () => {
|
|
174
|
+
O && (z.purge(O), p.current = !1);
|
|
159
175
|
};
|
|
160
|
-
}, [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
ref: r,
|
|
165
|
-
style: {
|
|
166
|
-
width: "100%",
|
|
167
|
-
height: "100%",
|
|
168
|
-
margin: "0"
|
|
169
|
-
}
|
|
176
|
+
}, [I, d, m, A, B, x, w, o, R]), D(() => {
|
|
177
|
+
const t = n.current;
|
|
178
|
+
!t || !p.current || e <= 0 || r <= 0 || a.current.width === e && a.current.height === r || (a.current = { width: e, height: r }, z.relayout(t, { width: e, height: r }).catch(
|
|
179
|
+
() => {
|
|
170
180
|
}
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
181
|
+
));
|
|
182
|
+
}, [e, r]), /* @__PURE__ */ j(
|
|
183
|
+
"div",
|
|
184
|
+
{
|
|
185
|
+
ref: L,
|
|
186
|
+
className: F("dotplot-container relative", W && "w-full", _ && "h-full"),
|
|
187
|
+
style: c === void 0 ? void 0 : { width: c },
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ M(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
ref: n,
|
|
193
|
+
style: {
|
|
194
|
+
width: "100%",
|
|
195
|
+
height: "100%",
|
|
196
|
+
margin: "0"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
H
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
);
|
|
174
204
|
};
|
|
175
205
|
export {
|
|
176
|
-
|
|
206
|
+
Z as DotPlot
|
|
177
207
|
};
|
|
178
208
|
//# sourceMappingURL=DotPlot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DotPlot.js","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.js","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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"}
|
|
@@ -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 s=require("react/jsx-runtime"),D=require("plotly.js-dist"),c=require("react"),U=require("../ChartTooltip/ChartTooltip.cjs"),Y=require("../../../hooks/use-element-size.cjs"),$=require("../../../hooks/use-plotly-theme.cjs"),E=require("../../../lib/utils.cjs"),G=require("../../../utils/colors.cjs");;/* empty css */const J=-.5,K=r=>r.reduce((o,u)=>o+u,0)/r.length,Q=(r,l)=>{const u=r.map(i=>Math.pow(i-l,2)).reduce((i,h)=>i+h,0)/r.length;return Math.sqrt(u)},Z=(r,l,o,u,i=100)=>{const h=[],x=[],p=(u-o)/(i-1);for(let n=0;n<i;n++){const e=o+n*p;h.push(e);const M=J*Math.pow((e-r)/l,2),j=1/(l*Math.sqrt(2*Math.PI))*Math.exp(M);x.push(j)}return{x:h,y:x}},tt=(r,l,o)=>{const u=Math.ceil((o.end-o.start)/o.size),i=Array(u).fill(0);l.forEach(n=>{if(n>=o.start&&n<=o.end){const e=Math.floor((n-o.start)/o.size);i[e]++}});const h=Math.max(...i),x=Math.max(...r),p=h/x;return r.map(n=>n*p)},et=({dataSeries:r,width:l,height:o,title:u="Histogram",xTitle:i="X Axis",yTitle:h="Frequency",bargap:x=.2,showDistributionLine:p=!1})=>{const n=c.useRef(null),e=$.usePlotlyTheme(),{bindTooltip:M,tooltipElement:j}=U.useChartTooltip({xLabel:i,yLabel:h}),[T,L]=Y.useElementSize(),f=l??L.width,g=o??L.height,I=f>0&&g>0,W=l===void 0,N=o===void 0,b=c.useRef({width:f,height:g});b.current={width:f,height:g};const R=c.useRef(!1),C=c.useRef({width:0,height:0}),q=c.useMemo(()=>Array.isArray(r)?r:[r],[r]),P=c.useMemo(()=>q.length>1?"stack":void 0,[q.length]),z=G.CHART_COLORS,y=c.useMemo(()=>q.map((t,m)=>{const d=typeof t.showDistributionLine>"u"?p:t.showDistributionLine;return{...t,color:t.color||z[m%z.length],opacity:d?.5:t.opacity||1,showDistributionLine:d,lineWidth:t.lineWidth||3}}),[q,p,z]),k=e.gridColor,S=c.useMemo(()=>y.map(t=>({type:"histogram",x:t.x,name:t.name,marker:{color:t.color,line:{color:e.paperBg,width:1},opacity:t.opacity},autobinx:t.autobinx,xbins:t.xbins,hoverinfo:"none"})),[y,e]),A=c.useMemo(()=>y.filter(t=>t.showDistributionLine).map(t=>{const m=K(t.x),d=Q(t.x,m),v=Math.min(...t.x),a=Math.max(...t.x),w=a-v,F=v-w*.1,O=a+w*.1,V=t.xbins||{start:F,end:O,size:w/10},H=Z(m,d,F,O,100),X=tt(H.y,t.x,V);return{type:"scatter",x:H.x,y:X,mode:"lines",name:`${t.name} Distribution`,line:{color:t.color,width:t.lineWidth},hoverinfo:"none"}}),[y]),B=c.useMemo(()=>[...S,...A],[S,A]);c.useEffect(()=>{if(!n.current||!I)return;const t={width:b.current.width,height:b.current.height,font:{family:"Inter, sans-serif"},showlegend:!1,margin:{l:90,r:40,b:80,t:40},xaxis:{title:{text:i,font:{size:16,color:e.textSecondary,family:"Inter, sans-serif",weight:400},standoff:20},gridcolor:k,tickcolor:e.tickColor,ticklen:8,tickwidth:1,ticks:"outside",linecolor:e.lineColor,linewidth:1,zeroline:!1,automargin:!0},yaxis:{title:{text:h,font:{size:16,color:e.textSecondary,family:"Inter, sans-serif",weight:400},standoff:20},gridcolor:k,tickcolor:e.tickColor,ticklen:8,tickwidth:1,ticks:"outside",linecolor:e.lineColor,linewidth:1,zeroline:!1,rangemode:"tozero",automargin:!0},barmode:P,bargap:x,paper_bgcolor:e.paperBg,plot_bgcolor:e.plotBg},m={responsive:!1,displayModeBar:!1,displaylogo:!1};D.newPlot(n.current,B,t,m),M(n.current);const d=n.current;return R.current=!0,C.current={...b.current},()=>{d&&(D.purge(d),R.current=!1)}},[I,i,h,x,B,P,k,e,M]),c.useEffect(()=>{const t=n.current;!t||!R.current||f<=0||g<=0||C.current.width===f&&C.current.height===g||(C.current={width:f,height:g},D.relayout(t,{width:f,height:g}).catch(()=>{}))},[f,g]);const _=({series:t})=>{const m=t.map((a,w)=>s.jsx(c.Fragment,{children:s.jsxs("div",{className:"legend-item",children:[s.jsx("span",{className:"color-box",style:{background:a.color}}),a.name,w<t.length-1&&s.jsx("span",{className:"divider"})]})},a.name)),d=[],v=6;for(let a=0;a<m.length;a+=v)d.push(s.jsx("div",{className:"legend-row",children:m.slice(a,a+v)},a));return s.jsx("div",{className:"legend-container",children:d})};return s.jsxs("div",{className:E.cn("histogram-container relative",W&&"w-full",N&&"h-full"),style:l===void 0?void 0:{width:l},children:[s.jsxs("div",{className:E.cn("chart-container",N&&"flex h-full flex-col"),children:[u&&s.jsx("div",{className:"title-container",children:s.jsx("h2",{className:"title",children:u})}),s.jsx("div",{ref:T,className:E.cn(N&&"min-h-0 flex-1"),children:s.jsx("div",{ref:n,style:{width:"100%",height:"100%",margin:"0"}})}),s.jsx(_,{series:y})]}),j]})};exports.Histogram=et;
|
|
2
2
|
//# sourceMappingURL=Histogram.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Histogram.cjs","sources":["../../../../src/components/charts/Histogram/Histogram.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\";\nimport \"./Histogram.scss\";\n\n/** Exponent coefficient for normal distribution calculation */\nconst NORMAL_DISTRIBUTION_EXPONENT_COEFF = -0.5;\n\ninterface HistogramDataSeries {\n x: number[];\n name: string;\n color?: string;\n autobinx?: boolean;\n xbins?: {\n start: number;\n end: number;\n size: number;\n };\n opacity?: number;\n showDistributionLine?: boolean;\n lineWidth?: number;\n}\n\ntype HistogramProps = {\n dataSeries: HistogramDataSeries | HistogramDataSeries[];\n width?: number;\n height?: number;\n title?: string;\n xTitle?: string;\n yTitle?: string;\n bargap?: number;\n showDistributionLine?: boolean;\n};\n\nconst calculateMean = (data: number[]): number => {\n const sum = data.reduce((acc, val) => acc + val, 0);\n return sum / data.length;\n};\n\nconst calculateStdDev = (data: number[], mean: number): number => {\n const squaredDiffs = data.map((value) => Math.pow(value - mean, 2));\n const variance =\n squaredDiffs.reduce((acc, val) => acc + val, 0) / data.length;\n return Math.sqrt(variance);\n};\n\nconst generateNormalDistributionPoints = (\n mean: number,\n stdDev: number,\n start: number,\n end: number,\n points = 100\n): { x: number[]; y: number[] } => {\n const xValues: number[] = [];\n const yValues: number[] = [];\n\n const step = (end - start) / (points - 1);\n\n for (let i = 0; i < points; i++) {\n const x = start + i * step;\n xValues.push(x);\n\n const exponent = NORMAL_DISTRIBUTION_EXPONENT_COEFF * Math.pow((x - mean) / stdDev, 2);\n const y = (1 / (stdDev * Math.sqrt(2 * Math.PI))) * Math.exp(exponent);\n yValues.push(y);\n }\n\n return { x: xValues, y: yValues };\n};\n\nconst scaleDistributionCurve = (\n yValues: number[],\n histogramData: number[],\n bins: { start: number; end: number; size: number }\n): number[] => {\n const binCount = Math.ceil((bins.end - bins.start) / bins.size);\n const binFrequencies = Array(binCount).fill(0);\n\n histogramData.forEach((value) => {\n if (value >= bins.start && value <= bins.end) {\n const binIndex = Math.floor((value - bins.start) / bins.size);\n binFrequencies[binIndex]++;\n }\n });\n\n const maxBinFrequency = Math.max(...binFrequencies);\n const maxCurveValue = Math.max(...yValues);\n\n const scaleFactor = maxBinFrequency / maxCurveValue;\n\n return yValues.map((y) => y * scaleFactor);\n};\n\nconst Histogram: React.FC<HistogramProps> = ({\n dataSeries,\n width = 480,\n height = 480,\n title = \"Histogram\",\n xTitle = \"X Axis\",\n yTitle = \"Frequency\",\n bargap = 0.2,\n showDistributionLine = false,\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 const effectiveBarMode = useMemo<\n \"stack\" | \"group\" | \"overlay\" | \"relative\" | undefined\n >(() => (seriesArray.length > 1 ? \"stack\" : undefined), [seriesArray.length]);\n\n const defaultColors = CHART_COLORS;\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n const hasDistributionLine =\n typeof series.showDistributionLine === \"undefined\"\n ? showDistributionLine\n : series.showDistributionLine;\n\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n opacity: hasDistributionLine ? 0.5 : series.opacity || 1,\n showDistributionLine: hasDistributionLine,\n lineWidth: series.lineWidth || 3,\n };\n });\n }, [seriesArray, showDistributionLine, defaultColors]);\n\n const gridColor = theme.gridColor;\n\n const histogramData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"histogram\" as const,\n x: series.x,\n name: series.name,\n marker: {\n color: series.color,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n opacity: series.opacity,\n },\n autobinx: series.autobinx,\n xbins: series.xbins,\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const distributionLines = useMemo(\n () =>\n seriesWithColors\n .filter((series) => series.showDistributionLine)\n .map((series) => {\n const mean = calculateMean(series.x);\n const stdDev = calculateStdDev(series.x, mean);\n\n const min = Math.min(...series.x);\n const max = Math.max(...series.x);\n const range = max - min;\n const start = min - range * 0.1;\n const end = max + range * 0.1;\n\n const bins = series.xbins || {\n start: start,\n end: end,\n size: range / 10,\n };\n\n const curvePoints = generateNormalDistributionPoints(\n mean,\n stdDev,\n start,\n end,\n 100,\n );\n\n const scaledYValues = scaleDistributionCurve(\n curvePoints.y,\n series.x,\n bins,\n );\n\n return {\n type: \"scatter\" as const,\n x: curvePoints.x,\n y: scaledYValues,\n mode: \"lines\" as const,\n name: `${series.name} Distribution`,\n line: {\n color: series.color,\n width: series.lineWidth,\n },\n hoverinfo: \"none\" as const,\n };\n }),\n [seriesWithColors],\n );\n\n const plotData = useMemo(\n () => [...histogramData, ...distributionLines],\n [histogramData, distributionLines],\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 showlegend: false,\n margin: { l: 90, r: 40, b: 80, t: 40 },\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: 20,\n },\n gridcolor: gridColor,\n tickcolor: theme.tickColor,\n ticklen: 8,\n tickwidth: 1,\n ticks: \"outside\" as const,\n linecolor: theme.lineColor,\n linewidth: 1,\n zeroline: false,\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: 20,\n },\n gridcolor: gridColor,\n tickcolor: theme.tickColor,\n ticklen: 8,\n tickwidth: 1,\n ticks: \"outside\" as const,\n linecolor: theme.lineColor,\n linewidth: 1,\n zeroline: false,\n rangemode: \"tozero\" as const,\n },\n barmode: effectiveBarMode,\n bargap: bargap,\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, bargap, plotData, effectiveBarMode, gridColor, theme, bindTooltip]);\n\n const ChartLegend: React.FC<{\n series: Array<{ name: string; color: string }>;\n }> = ({ series }) => {\n const items = series.map((item, i) => (\n <React.Fragment key={item.name}>\n <div className=\"legend-item\">\n <span className=\"color-box\" style={{ background: item.color }} />\n {item.name}\n {i < series.length - 1 && <span className=\"divider\" />}\n </div>\n </React.Fragment>\n ));\n\n const rows = [];\n const rowSize = 6;\n for (let i = 0; i < items.length; i += rowSize) {\n rows.push(\n <div className=\"legend-row\" key={i}>\n {items.slice(i, i + rowSize)}\n </div>\n );\n }\n\n return <div className=\"legend-container\">{rows}</div>;\n };\n\n return (\n <div className=\"histogram-container relative\" style={{ width: width }}>\n <div className=\"chart-container\">\n {title && (\n <div className=\"title-container\">\n <h2 className=\"title\">{title}</h2>\n </div>\n )}\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n <ChartLegend series={seriesWithColors} />\n </div>\n {tooltipElement}\n </div>\n );\n};\n\nexport { Histogram };\nexport type { HistogramDataSeries, HistogramProps };\n"],"names":["NORMAL_DISTRIBUTION_EXPONENT_COEFF","calculateMean","data","acc","val","calculateStdDev","mean","variance","value","generateNormalDistributionPoints","stdDev","start","end","points","xValues","yValues","step","i","x","exponent","y","scaleDistributionCurve","histogramData","bins","binCount","binFrequencies","binIndex","maxBinFrequency","maxCurveValue","scaleFactor","Histogram","dataSeries","width","height","title","xTitle","yTitle","bargap","showDistributionLine","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","seriesArray","useMemo","effectiveBarMode","defaultColors","CHART_COLORS","seriesWithColors","series","index","hasDistributionLine","gridColor","distributionLines","min","max","range","curvePoints","scaledYValues","plotData","useEffect","layout","config","Plotly","plotElement","ChartLegend","items","item","jsx","React","jsxs","rows","rowSize"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Histogram.cjs","sources":["../../../../src/components/charts/Histogram/Histogram.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\";\nimport \"./Histogram.scss\";\n\n/** Exponent coefficient for normal distribution calculation */\nconst NORMAL_DISTRIBUTION_EXPONENT_COEFF = -0.5;\n\ninterface HistogramDataSeries {\n x: number[];\n name: string;\n color?: string;\n autobinx?: boolean;\n xbins?: {\n start: number;\n end: number;\n size: number;\n };\n opacity?: number;\n showDistributionLine?: boolean;\n lineWidth?: number;\n}\n\ntype HistogramProps = {\n dataSeries: HistogramDataSeries | HistogramDataSeries[];\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 bargap?: number;\n showDistributionLine?: boolean;\n};\n\nconst calculateMean = (data: number[]): number => {\n const sum = data.reduce((acc, val) => acc + val, 0);\n return sum / data.length;\n};\n\nconst calculateStdDev = (data: number[], mean: number): number => {\n const squaredDiffs = data.map((value) => Math.pow(value - mean, 2));\n const variance =\n squaredDiffs.reduce((acc, val) => acc + val, 0) / data.length;\n return Math.sqrt(variance);\n};\n\nconst generateNormalDistributionPoints = (\n mean: number,\n stdDev: number,\n start: number,\n end: number,\n points = 100\n): { x: number[]; y: number[] } => {\n const xValues: number[] = [];\n const yValues: number[] = [];\n\n const step = (end - start) / (points - 1);\n\n for (let i = 0; i < points; i++) {\n const x = start + i * step;\n xValues.push(x);\n\n const exponent = NORMAL_DISTRIBUTION_EXPONENT_COEFF * Math.pow((x - mean) / stdDev, 2);\n const y = (1 / (stdDev * Math.sqrt(2 * Math.PI))) * Math.exp(exponent);\n yValues.push(y);\n }\n\n return { x: xValues, y: yValues };\n};\n\nconst scaleDistributionCurve = (\n yValues: number[],\n histogramData: number[],\n bins: { start: number; end: number; size: number }\n): number[] => {\n const binCount = Math.ceil((bins.end - bins.start) / bins.size);\n const binFrequencies = Array(binCount).fill(0);\n\n histogramData.forEach((value) => {\n if (value >= bins.start && value <= bins.end) {\n const binIndex = Math.floor((value - bins.start) / bins.size);\n binFrequencies[binIndex]++;\n }\n });\n\n const maxBinFrequency = Math.max(...binFrequencies);\n const maxCurveValue = Math.max(...yValues);\n\n const scaleFactor = maxBinFrequency / maxCurveValue;\n\n return yValues.map((y) => y * scaleFactor);\n};\n\nconst Histogram: React.FC<HistogramProps> = ({\n dataSeries,\n width,\n height,\n title = \"Histogram\",\n xTitle = \"X Axis\",\n yTitle = \"Frequency\",\n bargap = 0.2,\n showDistributionLine = 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. Histogram has its own HTML title + legend,\n // so we measure a wrapper around just the Plotly canvas. See AreaGraph for\n // the reference pattern.\n const [plotAreaRef, 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 const effectiveBarMode = useMemo<\n \"stack\" | \"group\" | \"overlay\" | \"relative\" | undefined\n >(() => (seriesArray.length > 1 ? \"stack\" : undefined), [seriesArray.length]);\n\n const defaultColors = CHART_COLORS;\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n const hasDistributionLine =\n typeof series.showDistributionLine === \"undefined\"\n ? showDistributionLine\n : series.showDistributionLine;\n\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n opacity: hasDistributionLine ? 0.5 : series.opacity || 1,\n showDistributionLine: hasDistributionLine,\n lineWidth: series.lineWidth || 3,\n };\n });\n }, [seriesArray, showDistributionLine, defaultColors]);\n\n const gridColor = theme.gridColor;\n\n const histogramData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"histogram\" as const,\n x: series.x,\n name: series.name,\n marker: {\n color: series.color,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n opacity: series.opacity,\n },\n autobinx: series.autobinx,\n xbins: series.xbins,\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const distributionLines = useMemo(\n () =>\n seriesWithColors\n .filter((series) => series.showDistributionLine)\n .map((series) => {\n const mean = calculateMean(series.x);\n const stdDev = calculateStdDev(series.x, mean);\n\n const min = Math.min(...series.x);\n const max = Math.max(...series.x);\n const range = max - min;\n const start = min - range * 0.1;\n const end = max + range * 0.1;\n\n const bins = series.xbins || {\n start: start,\n end: end,\n size: range / 10,\n };\n\n const curvePoints = generateNormalDistributionPoints(\n mean,\n stdDev,\n start,\n end,\n 100,\n );\n\n const scaledYValues = scaleDistributionCurve(\n curvePoints.y,\n series.x,\n bins,\n );\n\n return {\n type: \"scatter\" as const,\n x: curvePoints.x,\n y: scaledYValues,\n mode: \"lines\" as const,\n name: `${series.name} Distribution`,\n line: {\n color: series.color,\n width: series.lineWidth,\n },\n hoverinfo: \"none\" as const,\n };\n }),\n [seriesWithColors],\n );\n\n const plotData = useMemo(\n () => [...histogramData, ...distributionLines],\n [histogramData, distributionLines],\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 showlegend: false,\n margin: { l: 90, r: 40, b: 80, t: 40 },\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: 20,\n },\n gridcolor: gridColor,\n tickcolor: theme.tickColor,\n ticklen: 8,\n tickwidth: 1,\n ticks: \"outside\" as const,\n linecolor: theme.lineColor,\n linewidth: 1,\n zeroline: false,\n automargin: true,\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: 20,\n },\n gridcolor: gridColor,\n tickcolor: theme.tickColor,\n ticklen: 8,\n tickwidth: 1,\n ticks: \"outside\" as const,\n linecolor: theme.lineColor,\n linewidth: 1,\n zeroline: false,\n rangemode: \"tozero\" as const,\n automargin: true,\n },\n barmode: effectiveBarMode,\n bargap: bargap,\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, bargap, plotData, effectiveBarMode, 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 const ChartLegend: React.FC<{\n series: Array<{ name: string; color: string }>;\n }> = ({ series }) => {\n const items = series.map((item, i) => (\n <React.Fragment key={item.name}>\n <div className=\"legend-item\">\n <span className=\"color-box\" style={{ background: item.color }} />\n {item.name}\n {i < series.length - 1 && <span className=\"divider\" />}\n </div>\n </React.Fragment>\n ));\n\n const rows = [];\n const rowSize = 6;\n for (let i = 0; i < items.length; i += rowSize) {\n rows.push(\n <div className=\"legend-row\" key={i}>\n {items.slice(i, i + rowSize)}\n </div>\n );\n }\n\n return <div className=\"legend-container\">{rows}</div>;\n };\n\n return (\n <div\n className={cn(\"histogram-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n style={width === undefined ? undefined : { width }}\n >\n <div className={cn(\"chart-container\", fillHeight && \"flex h-full flex-col\")}>\n {title && (\n <div className=\"title-container\">\n <h2 className=\"title\">{title}</h2>\n </div>\n )}\n {/* Measured plot area — flexes to fill the space left by the title and\n legend in fill mode, so the Plotly canvas tracks it. */}\n <div ref={plotAreaRef} className={cn(fillHeight && \"min-h-0 flex-1\")}>\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n </div>\n <ChartLegend series={seriesWithColors} />\n </div>\n {tooltipElement}\n </div>\n );\n};\n\nexport { Histogram };\nexport type { HistogramDataSeries, HistogramProps };\n"],"names":["NORMAL_DISTRIBUTION_EXPONENT_COEFF","calculateMean","data","acc","val","calculateStdDev","mean","variance","value","generateNormalDistributionPoints","stdDev","start","end","points","xValues","yValues","step","i","x","exponent","y","scaleDistributionCurve","histogramData","bins","binCount","binFrequencies","binIndex","maxBinFrequency","maxCurveValue","scaleFactor","Histogram","dataSeries","width","height","title","xTitle","yTitle","bargap","showDistributionLine","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","plotAreaRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","seriesArray","useMemo","effectiveBarMode","defaultColors","CHART_COLORS","seriesWithColors","series","index","hasDistributionLine","gridColor","distributionLines","min","max","range","curvePoints","scaledYValues","plotData","useEffect","layout","config","Plotly","plotElement","ChartLegend","items","item","jsx","React","jsxs","rows","rowSize","cn"],"mappings":"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"}
|