@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.
Files changed (36) hide show
  1. package/dist/components/charts/AreaGraph/AreaGraph.cjs +1 -1
  2. package/dist/components/charts/AreaGraph/AreaGraph.cjs.map +1 -1
  3. package/dist/components/charts/AreaGraph/AreaGraph.js +149 -132
  4. package/dist/components/charts/AreaGraph/AreaGraph.js.map +1 -1
  5. package/dist/components/charts/BarGraph/BarGraph.cjs +1 -1
  6. package/dist/components/charts/BarGraph/BarGraph.cjs.map +1 -1
  7. package/dist/components/charts/BarGraph/BarGraph.js +124 -95
  8. package/dist/components/charts/BarGraph/BarGraph.js.map +1 -1
  9. package/dist/components/charts/Boxplot/Boxplot.cjs +1 -1
  10. package/dist/components/charts/Boxplot/Boxplot.cjs.map +1 -1
  11. package/dist/components/charts/Boxplot/Boxplot.js +116 -87
  12. package/dist/components/charts/Boxplot/Boxplot.js.map +1 -1
  13. package/dist/components/charts/DotPlot/DotPlot.cjs +1 -1
  14. package/dist/components/charts/DotPlot/DotPlot.cjs.map +1 -1
  15. package/dist/components/charts/DotPlot/DotPlot.js +113 -83
  16. package/dist/components/charts/DotPlot/DotPlot.js.map +1 -1
  17. package/dist/components/charts/Histogram/Histogram.cjs +1 -1
  18. package/dist/components/charts/Histogram/Histogram.cjs.map +1 -1
  19. package/dist/components/charts/Histogram/Histogram.js +128 -105
  20. package/dist/components/charts/Histogram/Histogram.js.map +1 -1
  21. package/dist/components/charts/LineGraph/LineGraph.cjs +1 -1
  22. package/dist/components/charts/LineGraph/LineGraph.cjs.map +1 -1
  23. package/dist/components/charts/LineGraph/LineGraph.js +109 -87
  24. package/dist/components/charts/LineGraph/LineGraph.js.map +1 -1
  25. package/dist/components/charts/ScatterGraph/ScatterGraph.cjs +1 -1
  26. package/dist/components/charts/ScatterGraph/ScatterGraph.cjs.map +1 -1
  27. package/dist/components/charts/ScatterGraph/ScatterGraph.js +114 -90
  28. package/dist/components/charts/ScatterGraph/ScatterGraph.js.map +1 -1
  29. package/dist/hooks/use-element-size.cjs +2 -0
  30. package/dist/hooks/use-element-size.cjs.map +1 -0
  31. package/dist/hooks/use-element-size.js +21 -0
  32. package/dist/hooks/use-element-size.js.map +1 -0
  33. package/dist/index.css +1 -1
  34. package/dist/index.d.ts +56 -0
  35. package/dist/index.tailwind.css +1 -1
  36. package/package.json +1 -1
@@ -1,65 +1,69 @@
1
- import { jsxs as N, jsx as _ } from "react/jsx-runtime";
2
- import E from "plotly.js-dist";
3
- import { useRef as B, useMemo as f, useEffect as L } from "react";
4
- import { useChartTooltip as O } from "../ChartTooltip/ChartTooltip.js";
5
- import { usePlotlyTheme as P } from "../../../hooks/use-plotly-theme.js";
6
- import { seriesColor as j } from "../../../utils/colors.js";
7
- const A = -1.8, W = ({
1
+ import { jsxs as Y, jsx as H } from "react/jsx-runtime";
2
+ import k from "plotly.js-dist";
3
+ import { useRef as g, useMemo as u, useEffect as j } from "react";
4
+ import { useChartTooltip as V } from "../ChartTooltip/ChartTooltip.js";
5
+ import { useElementSize as D } from "../../../hooks/use-element-size.js";
6
+ import { usePlotlyTheme as F } from "../../../hooks/use-plotly-theme.js";
7
+ import { cn as R } from "../../../lib/utils.js";
8
+ import { seriesColor as X } from "../../../utils/colors.js";
9
+ const $ = -1.8, et = ({
8
10
  dataSeries: p,
9
- width: d = 1e3,
10
- height: x = 600,
11
- xRange: m,
12
- yRange: a,
13
- xTitle: h = "Columns",
14
- yTitle: u = "Rows",
15
- title: y = "Boxplot",
16
- showPoints: M = !1
11
+ width: v,
12
+ height: I,
13
+ xRange: y,
14
+ yRange: h,
15
+ xTitle: x,
16
+ yTitle: w,
17
+ title: z = "Boxplot",
18
+ showPoints: E = !1
17
19
  }) => {
18
- const c = B(null), t = P(), { bindTooltip: w, tooltipElement: v } = O({ xLabel: h, yLabel: u }), { yMin: b, yMax: k } = f(() => {
19
- let e = Number.MAX_VALUE, o = Number.MIN_VALUE;
20
- p.forEach((n) => {
21
- n.y.forEach((r) => {
22
- e = Math.min(e, r), o = Math.max(o, r);
20
+ const a = g(null), o = F(), { bindTooltip: C, tooltipElement: A } = V({ xLabel: x, yLabel: w }), [S, N] = D(), n = v ?? N.width, i = I ?? N.height, _ = n > 0 && i > 0, T = v === void 0, U = I === void 0, d = g({ width: n, height: i });
21
+ d.current = { width: n, height: i };
22
+ const M = g(!1), m = g({ width: 0, height: 0 }), { yMin: B, yMax: L } = u(() => {
23
+ let e = Number.MAX_VALUE, t = Number.MIN_VALUE;
24
+ p.forEach((l) => {
25
+ l.y.forEach((r) => {
26
+ e = Math.min(e, r), t = Math.max(t, r);
23
27
  });
24
28
  });
25
- const i = (o - e) * 0.1;
29
+ const c = (t - e) * 0.1;
26
30
  return {
27
- yMin: e - i,
28
- yMax: o + i
31
+ yMin: e - c,
32
+ yMax: t + c
29
33
  };
30
- }, [p]), s = f(
31
- () => a || [b, k],
32
- [a, b, k]
33
- ), C = f(() => {
34
- const e = s[1] - s[0];
35
- let o = Math.pow(10, Math.floor(Math.log10(e)));
36
- e / o > 10 && (o = o * 2), e / o < 4 && (o = o / 2);
37
- const i = [];
38
- let n = Math.ceil(s[0] / o) * o;
39
- for (; n <= s[1]; )
40
- i.push(n), n += o;
41
- return i;
42
- }, [s]), g = f(
34
+ }, [p]), f = u(
35
+ () => h || [B, L],
36
+ [h, B, L]
37
+ ), O = u(() => {
38
+ const e = f[1] - f[0];
39
+ let t = Math.pow(10, Math.floor(Math.log10(e)));
40
+ e / t > 10 && (t = t * 2), e / t < 4 && (t = t / 2);
41
+ const c = [];
42
+ let l = Math.ceil(f[0] / t) * t;
43
+ for (; l <= f[1]; )
44
+ c.push(l), l += t;
45
+ return c;
46
+ }, [f]), b = u(
43
47
  () => ({
44
- tickcolor: t.tickColor,
48
+ tickcolor: o.tickColor,
45
49
  ticklen: 12,
46
50
  tickwidth: 1,
47
51
  ticks: "outside",
48
52
  tickfont: {
49
53
  size: 16,
50
- color: t.textColor,
54
+ color: o.textColor,
51
55
  family: "Inter, sans-serif",
52
56
  weight: 400
53
57
  },
54
- linecolor: t.lineColor,
58
+ linecolor: o.lineColor,
55
59
  linewidth: 1,
56
60
  position: 0,
57
61
  zeroline: !1
58
62
  }),
59
- [t]
60
- ), I = f(
63
+ [o]
64
+ ), P = u(
61
65
  () => ({
62
- text: y,
66
+ text: z,
63
67
  x: 0.5,
64
68
  y: 0.95,
65
69
  xanchor: "center",
@@ -68,17 +72,17 @@ const A = -1.8, W = ({
68
72
  size: 32,
69
73
  weight: 600,
70
74
  family: "Inter, sans-serif",
71
- color: t.textColor,
75
+ color: o.textColor,
72
76
  lineheight: 1.2,
73
77
  standoff: 30
74
78
  }
75
79
  }),
76
- [y, t]
80
+ [z, o]
77
81
  );
78
- return L(() => {
79
- if (!c.current) return;
80
- const e = p.map((r, z) => {
81
- const l = j(z, r.color);
82
+ return j(() => {
83
+ if (!a.current || !_) return;
84
+ const e = p.map((r, W) => {
85
+ const s = X(W, r.color);
82
86
  return {
83
87
  y: r.y,
84
88
  x: r.x,
@@ -86,95 +90,120 @@ const A = -1.8, W = ({
86
90
  name: r.name,
87
91
  hoverinfo: "none",
88
92
  marker: {
89
- color: l
93
+ color: s
90
94
  },
91
95
  line: {
92
- color: l
96
+ color: s
93
97
  },
94
- fillcolor: typeof l == "string" && l.startsWith("#") && l.length === 7 ? `${l}40` : l,
98
+ fillcolor: typeof s == "string" && s.startsWith("#") && s.length === 7 ? `${s}40` : s,
95
99
  // Add transparency for hex colors only
96
- boxpoints: M ? r.boxpoints || "outliers" : !1,
100
+ boxpoints: E ? r.boxpoints || "outliers" : !1,
97
101
  jitter: r.jitter || 0.3,
98
- pointpos: r.pointpos || A
102
+ pointpos: r.pointpos || $
99
103
  };
100
- }), o = {
101
- width: d,
102
- height: x,
103
- title: I,
104
- margin: { l: 80, r: 40, b: 80, t: 80, pad: 0 },
105
- paper_bgcolor: t.paperBg,
106
- plot_bgcolor: t.plotBg,
104
+ }), t = {
105
+ width: d.current.width,
106
+ height: d.current.height,
107
+ title: P,
108
+ // Bottom margin reserves room for tick labels, the x-axis title, and the
109
+ // container-anchored bottom legend stacked beneath them.
110
+ margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },
111
+ paper_bgcolor: o.paperBg,
112
+ plot_bgcolor: o.plotBg,
107
113
  font: {
108
114
  family: "Inter, sans-serif"
109
115
  },
110
116
  dragmode: !1,
111
117
  xaxis: {
112
118
  title: {
113
- text: h,
119
+ text: x,
114
120
  font: {
115
121
  size: 16,
116
- color: t.textSecondary,
122
+ color: o.textSecondary,
117
123
  family: "Inter, sans-serif",
118
124
  weight: 400
119
125
  },
120
126
  standoff: 15
121
127
  },
122
- gridcolor: t.gridColor,
123
- range: m,
124
- autorange: !m,
128
+ gridcolor: o.gridColor,
129
+ range: y,
130
+ autorange: !y,
125
131
  showgrid: !0,
126
- ...g
132
+ // Reserve space for tick labels + the axis title so the bottom legend
133
+ // can't overlap them at small sizes (SW-2157).
134
+ automargin: !0,
135
+ ...b
127
136
  },
128
137
  yaxis: {
129
138
  title: {
130
- text: u,
139
+ text: w,
131
140
  font: {
132
141
  size: 16,
133
- color: t.textSecondary,
142
+ color: o.textSecondary,
134
143
  family: "Inter, sans-serif",
135
144
  weight: 400
136
145
  },
137
146
  standoff: 15
138
147
  },
139
- gridcolor: t.gridColor,
140
- range: a,
141
- autorange: !a,
148
+ gridcolor: o.gridColor,
149
+ range: h,
150
+ autorange: !h,
142
151
  tickmode: "array",
143
- tickvals: C,
152
+ tickvals: O,
144
153
  showgrid: !0,
145
- ...g
154
+ automargin: !0,
155
+ ...b
146
156
  },
147
157
  legend: {
158
+ // Anchor to the bottom of the container (not the plot area) so the
159
+ // legend always clears the x-axis tick labels + title — a paper-relative
160
+ // fractional offset collapses into the ticks at small heights (SW-2157).
148
161
  x: 0.5,
149
- y: -0.2,
162
+ y: 0,
150
163
  xanchor: "center",
151
- yanchor: "top",
164
+ yanchor: "bottom",
165
+ yref: "container",
152
166
  orientation: "h",
153
167
  font: {
154
168
  size: 13,
155
- color: t.legendColor,
169
+ color: o.legendColor,
156
170
  family: "Inter, sans-serif",
157
171
  weight: 500,
158
172
  lineheight: 18
159
173
  }
160
174
  },
161
175
  showlegend: !0
162
- }, i = {
163
- responsive: !0,
176
+ }, c = {
177
+ // Sizing is driven from the measured container; disable Plotly's own
178
+ // window-resize responsiveness (it can't see container resizes).
179
+ responsive: !1,
164
180
  displayModeBar: !1,
165
181
  displaylogo: !1
166
182
  };
167
- E.newPlot(c.current, e, o, i), w(c.current);
168
- const n = c.current;
169
- return () => {
170
- n && E.purge(n);
183
+ k.newPlot(a.current, e, t, c), C(a.current);
184
+ const l = a.current;
185
+ return M.current = !0, m.current = { ...d.current }, () => {
186
+ l && (k.purge(l), M.current = !1);
171
187
  };
172
- }, [p, d, x, m, a, s, h, u, M, I, g, C, t, w]), /* @__PURE__ */ N("div", { className: "boxplot-container relative", children: [
173
- /* @__PURE__ */ _("div", { ref: c, style: { width: "100%", height: "100%" } }),
174
- v
175
- ] });
188
+ }, [p, _, y, h, f, x, w, E, P, b, O, o, C]), j(() => {
189
+ const e = a.current;
190
+ !e || !M.current || n <= 0 || i <= 0 || m.current.width === n && m.current.height === i || (m.current = { width: n, height: i }, k.relayout(e, { width: n, height: i }).catch(
191
+ () => {
192
+ }
193
+ ));
194
+ }, [n, i]), /* @__PURE__ */ Y(
195
+ "div",
196
+ {
197
+ ref: S,
198
+ className: R("boxplot-container relative", T && "w-full", U && "h-full"),
199
+ children: [
200
+ /* @__PURE__ */ H("div", { ref: a, style: { width: "100%", height: "100%" } }),
201
+ A
202
+ ]
203
+ }
204
+ );
176
205
  };
177
206
  export {
178
- W as Boxplot
207
+ et as Boxplot
179
208
  };
180
209
  //# sourceMappingURL=Boxplot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Boxplot.js","sources":["../../../../src/components/charts/Boxplot/Boxplot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { seriesColor } from \"@/utils/colors\";\n\n/** Default point position offset from the box edge */\nconst DEFAULT_POINT_POSITION = -1.8;\n\ninterface BoxDataSeries {\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n x?: string[] | number[];\n boxpoints?: \"all\" | \"outliers\" | \"suspectedoutliers\" | false;\n jitter?: number;\n pointpos?: number;\n}\n\ninterface BoxplotProps {\n dataSeries: BoxDataSeries[];\n width?: number;\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n xTitle?: string;\n yTitle?: string;\n title?: string;\n showPoints?: boolean;\n}\n\nconst Boxplot: React.FC<BoxplotProps> = ({\n dataSeries,\n width = 1000,\n height = 600,\n xRange,\n yRange,\n xTitle = \"Columns\",\n yTitle = \"Rows\",\n title = \"Boxplot\",\n showPoints = false,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n const { yMin, yMax } = useMemo(() => {\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current) return;\n\n const data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n y: series.y,\n x: series.x,\n type: \"box\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color,\n },\n line: {\n color,\n },\n fillcolor:\n typeof color === \"string\" && color.startsWith(\"#\") && color.length === 7\n ? `${color}40`\n : color, // Add transparency for hex colors only\n boxpoints: showPoints\n ? series.boxpoints || \"outliers\"\n : (false as const),\n jitter: series.jitter || 0.3,\n pointpos: series.pointpos || DEFAULT_POINT_POSITION,\n };\n });\n\n const layout = {\n width,\n height: height,\n title: titleOptions,\n margin: { l: 80, r: 40, b: 80, t: 80, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n showgrid: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n ...tickOptions,\n },\n legend: {\n x: 0.5,\n y: -0.2,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n showlegend: true,\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n // Cleanup function\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [dataSeries, width, height, xRange, yRange, effectiveYRange, xTitle, yTitle, showPoints, titleOptions, tickOptions, yTicks, theme, bindTooltip]);\n\n return (\n <div className=\"boxplot-container relative\">\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { Boxplot };\nexport type { BoxDataSeries, BoxplotProps };\n"],"names":["DEFAULT_POINT_POSITION","Boxplot","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","showPoints","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","yMin","yMax","useMemo","minY","maxY","series","y","yPadding","effectiveYRange","yTicks","range","step","ticks","current","tickOptions","titleOptions","useEffect","data","index","color","seriesColor","layout","config","Plotly","plotElement","jsxs","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"Boxplot.js","sources":["../../../../src/components/charts/Boxplot/Boxplot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { seriesColor } from \"@/utils/colors\";\n\n/** Default point position offset from the box edge */\nconst DEFAULT_POINT_POSITION = -1.8;\n\ninterface BoxDataSeries {\n y: number[];\n name: string;\n /** Optional color override (auto-assigned from CHART_COLORS if not provided) */\n color?: string;\n x?: string[] | number[];\n boxpoints?: \"all\" | \"outliers\" | \"suspectedoutliers\" | false;\n jitter?: number;\n pointpos?: number;\n}\n\ninterface BoxplotProps {\n dataSeries: BoxDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n xRange?: [number, number];\n yRange?: [number, number];\n xTitle?: string;\n yTitle?: string;\n title?: string;\n showPoints?: boolean;\n}\n\nconst Boxplot: React.FC<BoxplotProps> = ({\n dataSeries,\n width,\n height,\n xRange,\n yRange,\n xTitle,\n yTitle,\n title = \"Boxplot\",\n showPoints = false,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaGraph for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n\n const { yMin, yMax } = useMemo(() => {\n let minY = Number.MAX_VALUE;\n let maxY = Number.MIN_VALUE;\n\n dataSeries.forEach((series) => {\n series.y.forEach((y) => {\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n });\n });\n\n const yPadding = (maxY - minY) * 0.1;\n\n return {\n yMin: minY - yPadding,\n yMax: maxY + yPadding,\n };\n }, [dataSeries]);\n\n const effectiveYRange = useMemo(\n () => yRange || [yMin, yMax],\n [yRange, yMin, yMax],\n );\n\n const yTicks = useMemo(() => {\n const range = effectiveYRange[1] - effectiveYRange[0];\n let step = Math.pow(10, Math.floor(Math.log10(range)));\n\n if (range / step > 10) step = step * 2;\n if (range / step < 4) step = step / 2;\n\n const ticks = [];\n let current = Math.ceil(effectiveYRange[0] / step) * step;\n while (current <= effectiveYRange[1]) {\n ticks.push(current);\n current += step;\n }\n return ticks;\n }, [effectiveYRange]);\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const data = dataSeries.map((series, index) => {\n const color = seriesColor(index, series.color);\n return {\n y: series.y,\n x: series.x,\n type: \"box\" as const,\n name: series.name,\n hoverinfo: \"none\" as const,\n marker: {\n color,\n },\n line: {\n color,\n },\n fillcolor:\n typeof color === \"string\" && color.startsWith(\"#\") && color.length === 7\n ? `${color}40`\n : color, // Add transparency for hex colors only\n boxpoints: showPoints\n ? series.boxpoints || \"outliers\"\n : (false as const),\n jitter: series.jitter || 0.3,\n pointpos: series.pointpos || DEFAULT_POINT_POSITION,\n };\n });\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n title: titleOptions,\n // Bottom margin reserves room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n font: {\n family: \"Inter, sans-serif\",\n },\n dragmode: false as const,\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: xRange,\n autorange: !xRange,\n showgrid: true,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: theme.gridColor,\n range: yRange,\n autorange: !yRange,\n tickmode: \"array\" as const,\n tickvals: yTicks,\n showgrid: true,\n automargin: true,\n ...tickOptions,\n },\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n showlegend: true,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, data, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n\n // Cleanup function\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [dataSeries, hasSize, xRange, yRange, effectiveYRange, xTitle, yTitle, showPoints, titleOptions, tickOptions, yTicks, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n void Plotly.relayout(plotElement, { width: resolvedWidth, height: resolvedHeight }).catch(\n () => {},\n );\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"boxplot-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n >\n <div ref={plotRef} style={{ width: \"100%\", height: \"100%\" }} />\n {tooltipElement}\n </div>\n );\n};\n\nexport { Boxplot };\nexport type { BoxDataSeries, BoxplotProps };\n"],"names":["DEFAULT_POINT_POSITION","Boxplot","dataSeries","width","height","xRange","yRange","xTitle","yTitle","title","showPoints","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","yMin","yMax","useMemo","minY","maxY","series","y","yPadding","effectiveYRange","yTicks","range","step","ticks","current","tickOptions","titleOptions","useEffect","data","index","color","seriesColor","layout","config","Plotly","plotElement","jsxs","cn","jsx"],"mappings":";;;;;;;;AAWA,MAAMA,IAAyB,MAiCzBC,KAAkC,CAAC;AAAA,EACvC,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,YAAAC,IAAa;AACf,MAAM;AACJ,QAAMC,IAAUC,EAAuB,IAAI,GACrCC,IAAQC,EAAA,GACR,EAAE,aAAAC,GAAa,gBAAAC,EAAA,IAAmBC,EAAgB,EAAE,QAAQV,GAAQ,QAAQC,GAAQ,GAIpF,CAACU,GAAcC,CAAQ,IAAIC,EAAA,GAC3BC,IAAgBlB,KAASgB,EAAS,OAClCG,IAAiBlB,KAAUe,EAAS,QACpCI,IAAUF,IAAgB,KAAKC,IAAiB,GAGhDE,IAAYrB,MAAU,QACtBsB,IAAarB,MAAW,QACxBsB,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,EAAA,IAASC,EAAQ,MAAM;AACnC,QAAIC,IAAO,OAAO,WACdC,IAAO,OAAO;AAElB,IAAA/B,EAAW,QAAQ,CAACgC,MAAW;AAC7B,MAAAA,EAAO,EAAE,QAAQ,CAACC,MAAM;AACtB,QAAAH,IAAO,KAAK,IAAIA,GAAMG,CAAC,GACvBF,IAAO,KAAK,IAAIA,GAAME,CAAC;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAED,UAAMC,KAAYH,IAAOD,KAAQ;AAEjC,WAAO;AAAA,MACL,MAAMA,IAAOI;AAAA,MACb,MAAMH,IAAOG;AAAA,IAAA;AAAA,EAEjB,GAAG,CAAClC,CAAU,CAAC,GAETmC,IAAkBN;AAAA,IACtB,MAAMzB,KAAU,CAACuB,GAAMC,CAAI;AAAA,IAC3B,CAACxB,GAAQuB,GAAMC,CAAI;AAAA,EAAA,GAGfQ,IAASP,EAAQ,MAAM;AAC3B,UAAMQ,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,GAEdM,IAAcZ;AAAA,IAClB,OAAO;AAAA,MACL,WAAWlB,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,GAGF+B,IAAeb;AAAA,IACnB,OAAO;AAAA,MACL,MAAMtB;AAAA,MACN,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,OAAOI,EAAM;AAAA,QACb,YAAY;AAAA,QACZ,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,IAEF,CAACJ,GAAOI,CAAK;AAAA,EAAA;AAGf,SAAAgC,EAAU,MAAM;AACd,QAAI,CAAClC,EAAQ,WAAW,CAACY,EAAS;AAElC,UAAMuB,IAAO5C,EAAW,IAAI,CAACgC,GAAQa,MAAU;AAC7C,YAAMC,IAAQC,EAAYF,GAAOb,EAAO,KAAK;AAC7C,aAAO;AAAA,QACL,GAAGA,EAAO;AAAA,QACV,GAAGA,EAAO;AAAA,QACV,MAAM;AAAA,QACN,MAAMA,EAAO;AAAA,QACb,WAAW;AAAA,QACX,QAAQ;AAAA,UACN,OAAAc;AAAA,QAAA;AAAA,QAEF,MAAM;AAAA,UACJ,OAAAA;AAAA,QAAA;AAAA,QAEF,WACE,OAAOA,KAAU,YAAYA,EAAM,WAAW,GAAG,KAAKA,EAAM,WAAW,IACnE,GAAGA,CAAK,OACRA;AAAA;AAAA,QACN,WAAWtC,IACPwB,EAAO,aAAa,aACnB;AAAA,QACL,QAAQA,EAAO,UAAU;AAAA,QACzB,UAAUA,EAAO,YAAYlC;AAAA,MAAA;AAAA,IAEjC,CAAC,GAEKkD,IAAS;AAAA,MACb,OAAOxB,EAAQ,QAAQ;AAAA,MACvB,QAAQA,EAAQ,QAAQ;AAAA,MACxB,OAAOkB;AAAA;AAAA;AAAA,MAGP,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,KAAK,EAAA;AAAA,MAC3C,eAAe/B,EAAM;AAAA,MACrB,cAAcA,EAAM;AAAA,MACpB,MAAM;AAAA,QACJ,QAAQ;AAAA,MAAA;AAAA,MAEV,UAAU;AAAA,MACV,OAAO;AAAA,QACL,OAAO;AAAA,UACL,MAAMN;AAAA,UACN,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAOM,EAAM;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,UAAA;AAAA,UAEV,UAAU;AAAA,QAAA;AAAA,QAEZ,WAAWA,EAAM;AAAA,QACjB,OAAOR;AAAA,QACP,WAAW,CAACA;AAAA,QACZ,UAAU;AAAA;AAAA;AAAA,QAGV,YAAY;AAAA,QACZ,GAAGsC;AAAA,MAAA;AAAA,MAEL,OAAO;AAAA,QACL,OAAO;AAAA,UACL,MAAMnC;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,UAAUgC;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,GAAGK;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,OAAO9B,EAAM;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,YAAY;AAAA,QAAA;AAAA,MACd;AAAA,MAEF,YAAY;AAAA,IAAA,GAGRsC,IAAS;AAAA;AAAA;AAAA,MAGb,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,IAAA;AAGf,IAAAC,EAAO,QAAQzC,EAAQ,SAASmC,GAAMI,GAAQC,CAAM,GACpDpC,EAAYJ,EAAQ,OAAO;AAG3B,UAAM0C,IAAc1C,EAAQ;AAC5B,WAAAgB,EAAc,UAAU,IACxBC,EAAe,UAAU,EAAE,GAAGF,EAAQ,QAAA,GAG/B,MAAM;AACX,MAAI2B,MACFD,EAAO,MAAMC,CAAW,GACxB1B,EAAc,UAAU;AAAA,IAE5B;AAAA,EACF,GAAG,CAACzB,GAAYqB,GAASlB,GAAQC,GAAQ+B,GAAiB9B,GAAQC,GAAQE,GAAYkC,GAAcD,GAAaL,GAAQzB,GAAOE,CAAW,CAAC,GAI5I8B,EAAU,MAAM;AACd,UAAMQ,IAAc1C,EAAQ;AAC5B,IAAI,CAAC0C,KAAe,CAAC1B,EAAc,WAAWN,KAAiB,KAAKC,KAAkB,KAMpFM,EAAe,QAAQ,UAAUP,KACjCO,EAAe,QAAQ,WAAWN,MAIpCM,EAAe,UAAU,EAAE,OAAOP,GAAe,QAAQC,EAAA,GAEpD8B,EAAO,SAASC,GAAa,EAAE,OAAOhC,GAAe,QAAQC,EAAA,CAAgB,EAAE;AAAA,MAClF,MAAM;AAAA,MAAC;AAAA,IAAA;AAAA,EAEX,GAAG,CAACD,GAAeC,CAAc,CAAC,GAGhC,gBAAAgC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAKpC;AAAA,MACL,WAAWqC,EAAG,8BAA8B/B,KAAa,UAAUC,KAAc,QAAQ;AAAA,MAEzF,UAAA;AAAA,QAAA,gBAAA+B,EAAC,OAAA,EAAI,KAAK7C,GAAS,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAA,EAAO,CAAG;AAAA,QAC5DK;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react/jsx-runtime"),P=require("plotly.js-dist"),e=require("react"),q=require("../ChartTooltip/ChartTooltip.cjs"),R=require("../../../hooks/use-plotly-theme.cjs"),I=require("../../../utils/colors.cjs"),v=({dataSeries:l,width:i=1e3,height:p=600,title:d="Dot Plot",xTitle:c="Columns",yTitle:a="Rows",variant:g="default",markerSize:u=8})=>{const r=e.useRef(null),t=R.usePlotlyTheme(),{bindTooltip:h,tooltipElement:k}=q.useChartTooltip({xLabel:c,yLabel:a}),x=e.useMemo(()=>Array.isArray(l)?l:[l],[l]),n=I.CHART_COLORS,f=e.useMemo(()=>["circle","square","diamond","triangle-up","triangle-down","star"],[]),b=e.useMemo(()=>x.map((o,s)=>g==="default"?{...o,color:o.color||n[0],symbol:"circle",size:o.size||u}:{...o,color:o.color||n[s%n.length],symbol:o.symbol||f[s%f.length],size:o.size||u}),[x,g,u,n,f]),m=t.gridColor,C=e.useMemo(()=>b.map(o=>({type:"scatter",x:o.x,y:o.y,mode:"markers",name:o.name,marker:{color:o.color,size:o.size,symbol:o.symbol,line:{color:t.paperBg,width:1}},hoverinfo:"none"})),[b,t]),y=e.useMemo(()=>({tickcolor:t.tickColor,ticklen:12,tickwidth:1,ticks:"outside",tickfont:{size:16,color:t.textColor,family:"Inter, sans-serif",weight:400},linecolor:t.lineColor,linewidth:1,position:0,zeroline:!1}),[t]),w=e.useMemo(()=>({text:d,x:.5,y:.95,xanchor:"center",yanchor:"top",font:{size:32,weight:600,family:"Inter, sans-serif",color:t.textColor,lineheight:1.2,standoff:30}}),[d,t]);return e.useEffect(()=>{if(!r.current)return;const o={width:i,height:p,font:{family:"Inter, sans-serif"},title:w,margin:{l:80,r:40,b:80,t:80,pad:0},showlegend:!0,legend:{x:.5,y:-.2,xanchor:"center",yanchor:"top",orientation:"h",font:{size:13,color:t.legendColor,family:"Inter, sans-serif",weight:500,lineheight:18}},xaxis:{title:{text:c,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:m,...y},yaxis:{title:{text:a,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:m,...y},paper_bgcolor:t.paperBg,plot_bgcolor:t.plotBg},s={responsive:!0,displayModeBar:!1,displaylogo:!1};P.newPlot(r.current,C,o,s),h(r.current);const z=r.current;return()=>{z&&P.purge(z)}},[i,p,c,a,C,w,y,m,t,h]),M.jsxs("div",{className:"dotplot-container relative",style:{width:i},children:[M.jsx("div",{ref:r,style:{width:"100%",height:"100%",margin:"0"}}),k]})};exports.DotPlot=v;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=require("react/jsx-runtime"),x=require("plotly.js-dist"),o=require("react"),D=require("../ChartTooltip/ChartTooltip.cjs"),H=require("../../../hooks/use-element-size.cjs"),L=require("../../../hooks/use-plotly-theme.cjs"),T=require("../../../lib/utils.cjs"),W=require("../../../utils/colors.cjs"),_=({dataSeries:i,width:s,height:b,title:z="Dot Plot",xTitle:h,yTitle:d,variant:C="default",markerSize:g=8})=>{const l=o.useRef(null),t=L.usePlotlyTheme(),{bindTooltip:R,tooltipElement:S}=D.useChartTooltip({xLabel:h,yLabel:d}),[j,v]=H.useElementSize(),r=s??v.width,n=b??v.height,q=r>0&&n>0,A=s===void 0,B=b===void 0,c=o.useRef({width:r,height:n});c.current={width:r,height:n};const m=o.useRef(!1),u=o.useRef({width:0,height:0}),M=o.useMemo(()=>Array.isArray(i)?i:[i],[i]),a=W.CHART_COLORS,y=o.useMemo(()=>["circle","square","diamond","triangle-up","triangle-down","star"],[]),P=o.useMemo(()=>M.map((e,f)=>C==="default"?{...e,color:e.color||a[0],symbol:"circle",size:e.size||g}:{...e,color:e.color||a[f%a.length],symbol:e.symbol||y[f%y.length],size:e.size||g}),[M,C,g,a,y]),p=t.gridColor,k=o.useMemo(()=>P.map(e=>({type:"scatter",x:e.x,y:e.y,mode:"markers",name:e.name,marker:{color:e.color,size:e.size,symbol:e.symbol,line:{color:t.paperBg,width:1}},hoverinfo:"none"})),[P,t]),w=o.useMemo(()=>({tickcolor:t.tickColor,ticklen:12,tickwidth:1,ticks:"outside",tickfont:{size:16,color:t.textColor,family:"Inter, sans-serif",weight:400},linecolor:t.lineColor,linewidth:1,position:0,zeroline:!1}),[t]),E=o.useMemo(()=>({text:z,x:.5,y:.95,xanchor:"center",yanchor:"top",font:{size:32,weight:600,family:"Inter, sans-serif",color:t.textColor,lineheight:1.2,standoff:30}}),[z,t]);return o.useEffect(()=>{if(!l.current||!q)return;const e={width:c.current.width,height:c.current.height,font:{family:"Inter, sans-serif"},title:E,margin:{l:80,r:40,b:96,t:80,pad:0},showlegend:!0,legend:{x:.5,y:0,xanchor:"center",yanchor:"bottom",yref:"container",orientation:"h",font:{size:13,color:t.legendColor,family:"Inter, sans-serif",weight:500,lineheight:18}},xaxis:{title:{text:h,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:p,automargin:!0,...w},yaxis:{title:{text:d,font:{size:16,color:t.textSecondary,family:"Inter, sans-serif",weight:400},standoff:15},gridcolor:p,automargin:!0,...w},paper_bgcolor:t.paperBg,plot_bgcolor:t.plotBg},f={responsive:!1,displayModeBar:!1,displaylogo:!1};x.newPlot(l.current,k,e,f),R(l.current);const I=l.current;return m.current=!0,u.current={...c.current},()=>{I&&(x.purge(I),m.current=!1)}},[q,h,d,k,E,w,p,t,R]),o.useEffect(()=>{const e=l.current;!e||!m.current||r<=0||n<=0||u.current.width===r&&u.current.height===n||(u.current={width:r,height:n},x.relayout(e,{width:r,height:n}).catch(()=>{}))},[r,n]),O.jsxs("div",{ref:j,className:T.cn("dotplot-container relative",A&&"w-full",B&&"h-full"),style:s===void 0?void 0:{width:s},children:[O.jsx("div",{ref:l,style:{width:"100%",height:"100%",margin:"0"}}),S]})};exports.DotPlot=_;
2
2
  //# sourceMappingURL=DotPlot.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DotPlot.cjs","sources":["../../../../src/components/charts/DotPlot/DotPlot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { CHART_COLORS } from \"@/utils/colors\";\n\ntype MarkerSymbol =\n | \"circle\"\n | \"square\"\n | \"diamond\"\n | \"triangle-up\"\n | \"triangle-down\"\n | \"star\";\n\ninterface DotPlotDataSeries {\n x: number[];\n y: number[];\n name: string;\n color?: string;\n symbol?: MarkerSymbol;\n size?: number;\n}\n\ntype DotPlotVariant = \"default\" | \"stacked\";\n\ntype DotPlotProps = {\n dataSeries: DotPlotDataSeries | DotPlotDataSeries[];\n width?: number;\n height?: number;\n title?: string;\n xTitle?: string;\n yTitle?: string;\n variant?: DotPlotVariant;\n markerSize?: number;\n};\n\nconst DotPlot: React.FC<DotPlotProps> = ({\n dataSeries,\n width = 1000,\n height = 600,\n title = \"Dot Plot\",\n xTitle = \"Columns\",\n yTitle = \"Rows\",\n variant = \"default\",\n markerSize = 8,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n const seriesArray = useMemo(\n () => (Array.isArray(dataSeries) ? dataSeries : [dataSeries]),\n [dataSeries],\n );\n\n const defaultColors = CHART_COLORS;\n\n const defaultSymbols: MarkerSymbol[] = useMemo(\n () => [\n \"circle\",\n \"square\",\n \"diamond\",\n \"triangle-up\",\n \"triangle-down\",\n \"star\",\n ],\n [],\n );\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n if (variant === \"default\") {\n // Default variant: all circles, use first color or series color\n return {\n ...series,\n color: series.color || defaultColors[0],\n symbol: \"circle\" as MarkerSymbol,\n size: series.size || markerSize,\n };\n } else {\n // Stacked variant: different symbols and colors for each series\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n symbol:\n series.symbol || defaultSymbols[index % defaultSymbols.length],\n size: series.size || markerSize,\n };\n }\n });\n }, [seriesArray, variant, markerSize, defaultColors, defaultSymbols]);\n\n const gridColor = theme.gridColor;\n\n const plotData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"scatter\" as const,\n x: series.x,\n y: series.y,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: series.color,\n size: series.size,\n symbol: series.symbol,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n },\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current) return;\n\n const layout = {\n width,\n height,\n font: {\n family: \"Inter, sans-serif\",\n },\n title: titleOptions,\n margin: { l: 80, r: 40, b: 80, t: 80, pad: 0 },\n showlegend: true,\n legend: {\n x: 0.5,\n y: -0.2,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n ...tickOptions,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n };\n\n const config = {\n responsive: true,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n }\n };\n }, [width, height, xTitle, yTitle, plotData, titleOptions, tickOptions, gridColor, theme, bindTooltip]);\n\n return (\n <div className=\"dotplot-container relative\" style={{ width: width }}>\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n {tooltipElement}\n </div>\n );\n};\n\nexport { DotPlot };\nexport type { DotPlotDataSeries, DotPlotProps, DotPlotVariant, MarkerSymbol };\n"],"names":["DotPlot","dataSeries","width","height","title","xTitle","yTitle","variant","markerSize","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","seriesArray","useMemo","defaultColors","CHART_COLORS","defaultSymbols","seriesWithColors","series","index","gridColor","plotData","tickOptions","titleOptions","useEffect","layout","config","Plotly","plotElement","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"DotPlot.cjs","sources":["../../../../src/components/charts/DotPlot/DotPlot.tsx"],"sourcesContent":["import Plotly from \"plotly.js-dist\";\nimport React, { useEffect, useRef, useMemo } from \"react\";\n\nimport { useChartTooltip } from \"../ChartTooltip\";\n\nimport { useElementSize } from \"@/hooks/use-element-size\";\nimport { usePlotlyTheme } from \"@/hooks/use-plotly-theme\";\nimport { cn } from \"@/lib/utils\";\nimport { CHART_COLORS } from \"@/utils/colors\";\n\ntype MarkerSymbol =\n | \"circle\"\n | \"square\"\n | \"diamond\"\n | \"triangle-up\"\n | \"triangle-down\"\n | \"star\";\n\ninterface DotPlotDataSeries {\n x: number[];\n y: number[];\n name: string;\n color?: string;\n symbol?: MarkerSymbol;\n size?: number;\n}\n\ntype DotPlotVariant = \"default\" | \"stacked\";\n\ntype DotPlotProps = {\n dataSeries: DotPlotDataSeries | DotPlotDataSeries[];\n /**\n * Fixed width in pixels. When omitted, the chart fills its container and\n * tracks the container's width via a `ResizeObserver`.\n */\n width?: number;\n /**\n * Fixed height in pixels. When omitted, the chart fills its container and\n * tracks the container's height via a `ResizeObserver`.\n */\n height?: number;\n title?: string;\n xTitle?: string;\n yTitle?: string;\n variant?: DotPlotVariant;\n markerSize?: number;\n};\n\nconst DotPlot: React.FC<DotPlotProps> = ({\n dataSeries,\n width,\n height,\n title = \"Dot Plot\",\n xTitle,\n yTitle,\n variant = \"default\",\n markerSize = 8,\n}) => {\n const plotRef = useRef<HTMLDivElement>(null);\n const theme = usePlotlyTheme();\n const { bindTooltip, tooltipElement } = useChartTooltip({ xLabel: xTitle, yLabel: yTitle });\n\n // Omitted width/height → fill the container and track its measured size;\n // explicit pixel values override. See AreaGraph for the reference pattern.\n const [containerRef, measured] = useElementSize<HTMLDivElement>();\n const resolvedWidth = width ?? measured.width;\n const resolvedHeight = height ?? measured.height;\n const hasSize = resolvedWidth > 0 && resolvedHeight > 0;\n // Fill is per-dimension: omit width to fill the container width, omit height\n // to fill its height (so e.g. a fixed width with a container-driven height works).\n const fillWidth = width === undefined;\n const fillHeight = height === undefined;\n const sizeRef = useRef({ width: resolvedWidth, height: resolvedHeight });\n sizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n const plotInitedRef = useRef(false);\n // Size last applied to the plot, so the resize effect can skip a redundant\n // relayout right after newPlot already drew at that size.\n const appliedSizeRef = useRef({ width: 0, height: 0 });\n const seriesArray = useMemo(\n () => (Array.isArray(dataSeries) ? dataSeries : [dataSeries]),\n [dataSeries],\n );\n\n const defaultColors = CHART_COLORS;\n\n const defaultSymbols: MarkerSymbol[] = useMemo(\n () => [\n \"circle\",\n \"square\",\n \"diamond\",\n \"triangle-up\",\n \"triangle-down\",\n \"star\",\n ],\n [],\n );\n\n const seriesWithColors = useMemo(() => {\n return seriesArray.map((series, index) => {\n if (variant === \"default\") {\n // Default variant: all circles, use first color or series color\n return {\n ...series,\n color: series.color || defaultColors[0],\n symbol: \"circle\" as MarkerSymbol,\n size: series.size || markerSize,\n };\n } else {\n // Stacked variant: different symbols and colors for each series\n return {\n ...series,\n color: series.color || defaultColors[index % defaultColors.length],\n symbol:\n series.symbol || defaultSymbols[index % defaultSymbols.length],\n size: series.size || markerSize,\n };\n }\n });\n }, [seriesArray, variant, markerSize, defaultColors, defaultSymbols]);\n\n const gridColor = theme.gridColor;\n\n const plotData = useMemo(\n () =>\n seriesWithColors.map((series) => ({\n type: \"scatter\" as const,\n x: series.x,\n y: series.y,\n mode: \"markers\" as const,\n name: series.name,\n marker: {\n color: series.color,\n size: series.size,\n symbol: series.symbol,\n line: {\n color: theme.paperBg,\n width: 1,\n },\n },\n hoverinfo: \"none\" as const,\n })),\n [seriesWithColors, theme],\n );\n\n const tickOptions = useMemo(\n () => ({\n tickcolor: theme.tickColor,\n ticklen: 12,\n tickwidth: 1,\n ticks: \"outside\" as const,\n tickfont: {\n size: 16,\n color: theme.textColor,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n linecolor: theme.lineColor,\n linewidth: 1,\n position: 0,\n zeroline: false,\n }),\n [theme],\n );\n\n const titleOptions = useMemo(\n () => ({\n text: title,\n x: 0.5,\n y: 0.95,\n xanchor: \"center\" as const,\n yanchor: \"top\" as const,\n font: {\n size: 32,\n weight: 600,\n family: \"Inter, sans-serif\",\n color: theme.textColor,\n lineheight: 1.2,\n standoff: 30,\n },\n }),\n [title, theme],\n );\n\n useEffect(() => {\n if (!plotRef.current || !hasSize) return;\n\n const layout = {\n width: sizeRef.current.width,\n height: sizeRef.current.height,\n font: {\n family: \"Inter, sans-serif\",\n },\n title: titleOptions,\n // Bottom margin reserves room for tick labels, the x-axis title, and the\n // container-anchored bottom legend stacked beneath them.\n margin: { l: 80, r: 40, b: 96, t: 80, pad: 0 },\n showlegend: true,\n legend: {\n // Anchor to the bottom of the container (not the plot area) so the\n // legend always clears the x-axis tick labels + title — a paper-relative\n // fractional offset collapses into the ticks at small heights (SW-2157).\n x: 0.5,\n y: 0,\n xanchor: \"center\" as const,\n yanchor: \"bottom\" as const,\n yref: \"container\" as const,\n orientation: \"h\" as const,\n font: {\n size: 13,\n color: theme.legendColor,\n family: \"Inter, sans-serif\",\n weight: 500,\n lineheight: 18,\n },\n },\n xaxis: {\n title: {\n text: xTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n // Reserve space for tick labels + the axis title so the bottom legend\n // can't overlap them at small sizes (SW-2157).\n automargin: true,\n ...tickOptions,\n },\n yaxis: {\n title: {\n text: yTitle,\n font: {\n size: 16,\n color: theme.textSecondary,\n family: \"Inter, sans-serif\",\n weight: 400,\n },\n standoff: 15,\n },\n gridcolor: gridColor,\n automargin: true,\n ...tickOptions,\n },\n paper_bgcolor: theme.paperBg,\n plot_bgcolor: theme.plotBg,\n };\n\n const config = {\n // Sizing is driven from the measured container; disable Plotly's own\n // window-resize responsiveness (it can't see container resizes).\n responsive: false,\n displayModeBar: false,\n displaylogo: false,\n };\n\n Plotly.newPlot(plotRef.current, plotData, layout, config);\n bindTooltip(plotRef.current);\n\n // Capture ref value for cleanup\n const plotElement = plotRef.current;\n plotInitedRef.current = true;\n appliedSizeRef.current = { ...sizeRef.current };\n\n return () => {\n if (plotElement) {\n Plotly.purge(plotElement);\n plotInitedRef.current = false;\n }\n };\n }, [hasSize, xTitle, yTitle, plotData, titleOptions, tickOptions, gridColor, theme, bindTooltip]);\n\n // Resize in place when the measured/overridden size changes — cheaper than\n // recreating the plot, and it preserves tooltip/event bindings.\n useEffect(() => {\n const plotElement = plotRef.current;\n if (!plotElement || !plotInitedRef.current || resolvedWidth <= 0 || resolvedHeight <= 0) {\n return;\n }\n // newPlot already drew at the current size; skip the redundant relayout\n // (it would queue an automargin redraw that can reject if we unmount first).\n if (\n appliedSizeRef.current.width === resolvedWidth &&\n appliedSizeRef.current.height === resolvedHeight\n ) {\n return;\n }\n appliedSizeRef.current = { width: resolvedWidth, height: resolvedHeight };\n // Swallow rejections from a relayout that races an unmount/purge.\n void Plotly.relayout(plotElement, { width: resolvedWidth, height: resolvedHeight }).catch(\n () => {},\n );\n }, [resolvedWidth, resolvedHeight]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"dotplot-container relative\", fillWidth && \"w-full\", fillHeight && \"h-full\")}\n style={width === undefined ? undefined : { width }}\n >\n <div\n ref={plotRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n margin: \"0\",\n }}\n />\n {tooltipElement}\n </div>\n );\n};\n\nexport { DotPlot };\nexport type { DotPlotDataSeries, DotPlotProps, DotPlotVariant, MarkerSymbol };\n"],"names":["DotPlot","dataSeries","width","height","title","xTitle","yTitle","variant","markerSize","plotRef","useRef","theme","usePlotlyTheme","bindTooltip","tooltipElement","useChartTooltip","containerRef","measured","useElementSize","resolvedWidth","resolvedHeight","hasSize","fillWidth","fillHeight","sizeRef","plotInitedRef","appliedSizeRef","seriesArray","useMemo","defaultColors","CHART_COLORS","defaultSymbols","seriesWithColors","series","index","gridColor","plotData","tickOptions","titleOptions","useEffect","layout","config","Plotly","plotElement","jsxs","cn","jsx"],"mappings":"+XAgDMA,EAAkC,CAAC,CACvC,WAAAC,EACA,MAAAC,EACA,OAAAC,EACA,MAAAC,EAAQ,WACR,OAAAC,EACA,OAAAC,EACA,QAAAC,EAAU,UACV,WAAAC,EAAa,CACf,IAAM,CACJ,MAAMC,EAAUC,EAAAA,OAAuB,IAAI,EACrCC,EAAQC,EAAAA,eAAA,EACR,CAAE,YAAAC,EAAa,eAAAC,CAAA,EAAmBC,EAAAA,gBAAgB,CAAE,OAAQV,EAAQ,OAAQC,EAAQ,EAIpF,CAACU,EAAcC,CAAQ,EAAIC,iBAAA,EAC3BC,EAAgBjB,GAASe,EAAS,MAClCG,EAAiBjB,GAAUc,EAAS,OACpCI,EAAUF,EAAgB,GAAKC,EAAiB,EAGhDE,EAAYpB,IAAU,OACtBqB,EAAapB,IAAW,OACxBqB,EAAUd,EAAAA,OAAO,CAAE,MAAOS,EAAe,OAAQC,EAAgB,EACvEI,EAAQ,QAAU,CAAE,MAAOL,EAAe,OAAQC,CAAA,EAClD,MAAMK,EAAgBf,EAAAA,OAAO,EAAK,EAG5BgB,EAAiBhB,EAAAA,OAAO,CAAE,MAAO,EAAG,OAAQ,EAAG,EAC/CiB,EAAcC,EAAAA,QAClB,IAAO,MAAM,QAAQ3B,CAAU,EAAIA,EAAa,CAACA,CAAU,EAC3D,CAACA,CAAU,CAAA,EAGP4B,EAAgBC,EAAAA,aAEhBC,EAAiCH,EAAAA,QACrC,IAAM,CACJ,SACA,SACA,UACA,cACA,gBACA,MAAA,EAEF,CAAA,CAAC,EAGGI,EAAmBJ,EAAAA,QAAQ,IACxBD,EAAY,IAAI,CAACM,EAAQC,IAC1B3B,IAAY,UAEP,CACL,GAAG0B,EACH,MAAOA,EAAO,OAASJ,EAAc,CAAC,EACtC,OAAQ,SACR,KAAMI,EAAO,MAAQzB,CAAA,EAIhB,CACL,GAAGyB,EACH,MAAOA,EAAO,OAASJ,EAAcK,EAAQL,EAAc,MAAM,EACjE,OACEI,EAAO,QAAUF,EAAeG,EAAQH,EAAe,MAAM,EAC/D,KAAME,EAAO,MAAQzB,CAAA,CAG1B,EACA,CAACmB,EAAapB,EAASC,EAAYqB,EAAeE,CAAc,CAAC,EAE9DI,EAAYxB,EAAM,UAElByB,EAAWR,EAAAA,QACf,IACEI,EAAiB,IAAKC,IAAY,CAChC,KAAM,UACN,EAAGA,EAAO,EACV,EAAGA,EAAO,EACV,KAAM,UACN,KAAMA,EAAO,KACb,OAAQ,CACN,MAAOA,EAAO,MACd,KAAMA,EAAO,KACb,OAAQA,EAAO,OACf,KAAM,CACJ,MAAOtB,EAAM,QACb,MAAO,CAAA,CACT,EAEF,UAAW,MAAA,EACX,EACJ,CAACqB,EAAkBrB,CAAK,CAAA,EAGpB0B,EAAcT,EAAAA,QAClB,KAAO,CACL,UAAWjB,EAAM,UACjB,QAAS,GACT,UAAW,EACX,MAAO,UACP,SAAU,CACR,KAAM,GACN,MAAOA,EAAM,UACb,OAAQ,oBACR,OAAQ,GAAA,EAEV,UAAWA,EAAM,UACjB,UAAW,EACX,SAAU,EACV,SAAU,EAAA,GAEZ,CAACA,CAAK,CAAA,EAGF2B,EAAeV,EAAAA,QACnB,KAAO,CACL,KAAMxB,EACN,EAAG,GACH,EAAG,IACH,QAAS,SACT,QAAS,MACT,KAAM,CACJ,KAAM,GACN,OAAQ,IACR,OAAQ,oBACR,MAAOO,EAAM,UACb,WAAY,IACZ,SAAU,EAAA,CACZ,GAEF,CAACP,EAAOO,CAAK,CAAA,EAGf4B,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC9B,EAAQ,SAAW,CAACY,EAAS,OAElC,MAAMmB,EAAS,CACb,MAAOhB,EAAQ,QAAQ,MACvB,OAAQA,EAAQ,QAAQ,OACxB,KAAM,CACJ,OAAQ,mBAAA,EAEV,MAAOc,EAGP,OAAQ,CAAE,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,IAAK,CAAA,EAC3C,WAAY,GACZ,OAAQ,CAIN,EAAG,GACH,EAAG,EACH,QAAS,SACT,QAAS,SACT,KAAM,YACN,YAAa,IACb,KAAM,CACJ,KAAM,GACN,MAAO3B,EAAM,YACb,OAAQ,oBACR,OAAQ,IACR,WAAY,EAAA,CACd,EAEF,MAAO,CACL,MAAO,CACL,KAAMN,EACN,KAAM,CACJ,KAAM,GACN,MAAOM,EAAM,cACb,OAAQ,oBACR,OAAQ,GAAA,EAEV,SAAU,EAAA,EAEZ,UAAWwB,EAGX,WAAY,GACZ,GAAGE,CAAA,EAEL,MAAO,CACL,MAAO,CACL,KAAM/B,EACN,KAAM,CACJ,KAAM,GACN,MAAOK,EAAM,cACb,OAAQ,oBACR,OAAQ,GAAA,EAEV,SAAU,EAAA,EAEZ,UAAWwB,EACX,WAAY,GACZ,GAAGE,CAAA,EAEL,cAAe1B,EAAM,QACrB,aAAcA,EAAM,MAAA,EAGhB8B,EAAS,CAGb,WAAY,GACZ,eAAgB,GAChB,YAAa,EAAA,EAGfC,EAAO,QAAQjC,EAAQ,QAAS2B,EAAUI,EAAQC,CAAM,EACxD5B,EAAYJ,EAAQ,OAAO,EAG3B,MAAMkC,EAAclC,EAAQ,QAC5B,OAAAgB,EAAc,QAAU,GACxBC,EAAe,QAAU,CAAE,GAAGF,EAAQ,OAAA,EAE/B,IAAM,CACPmB,IACFD,EAAO,MAAMC,CAAW,EACxBlB,EAAc,QAAU,GAE5B,CACF,EAAG,CAACJ,EAAShB,EAAQC,EAAQ8B,EAAUE,EAAcD,EAAaF,EAAWxB,EAAOE,CAAW,CAAC,EAIhG0B,EAAAA,UAAU,IAAM,CACd,MAAMI,EAAclC,EAAQ,QACxB,CAACkC,GAAe,CAAClB,EAAc,SAAWN,GAAiB,GAAKC,GAAkB,GAMpFM,EAAe,QAAQ,QAAUP,GACjCO,EAAe,QAAQ,SAAWN,IAIpCM,EAAe,QAAU,CAAE,MAAOP,EAAe,OAAQC,CAAA,EAEpDsB,EAAO,SAASC,EAAa,CAAE,MAAOxB,EAAe,OAAQC,CAAA,CAAgB,EAAE,MAClF,IAAM,CAAC,CAAA,EAEX,EAAG,CAACD,EAAeC,CAAc,CAAC,EAGhCwB,EAAAA,KAAC,MAAA,CACC,IAAK5B,EACL,UAAW6B,EAAAA,GAAG,6BAA8BvB,GAAa,SAAUC,GAAc,QAAQ,EACzF,MAAOrB,IAAU,OAAY,OAAY,CAAE,MAAAA,CAAA,EAE3C,SAAA,CAAA4C,EAAAA,IAAC,MAAA,CACC,IAAKrC,EACL,MAAO,CACL,MAAO,OACP,OAAQ,OACR,OAAQ,GAAA,CACV,CAAA,EAEDK,CAAA,CAAA,CAAA,CAGP"}