exploreda 0.0.5 → 0.1.0

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 (208) hide show
  1. package/README.md +144 -0
  2. package/dist/ExplorEda.cjs +4608 -10858
  3. package/dist/ExplorEda.cjs.map +1 -1
  4. package/dist/ExplorEda.css +2332 -1653
  5. package/dist/ExplorEda.css.map +1 -1
  6. package/dist/ExplorEda.d.cts +125 -278
  7. package/dist/ExplorEda.d.ts +125 -278
  8. package/dist/ExplorEda.js +4521 -10782
  9. package/dist/ExplorEda.js.map +1 -1
  10. package/dist/calculations.cjs +18 -0
  11. package/dist/calculations.cjs.map +1 -0
  12. package/dist/calculations.d.cts +63 -0
  13. package/dist/calculations.d.ts +63 -0
  14. package/dist/calculations.js +3 -0
  15. package/dist/calculations.js.map +1 -0
  16. package/dist/charts/bar.cjs +27 -0
  17. package/dist/charts/bar.cjs.map +1 -0
  18. package/dist/charts/bar.d.cts +2 -0
  19. package/dist/charts/bar.d.ts +2 -0
  20. package/dist/charts/bar.js +18 -0
  21. package/dist/charts/bar.js.map +1 -0
  22. package/dist/charts/box-plot.cjs +26 -0
  23. package/dist/charts/box-plot.cjs.map +1 -0
  24. package/dist/charts/box-plot.d.cts +2 -0
  25. package/dist/charts/box-plot.d.ts +2 -0
  26. package/dist/charts/box-plot.js +17 -0
  27. package/dist/charts/box-plot.js.map +1 -0
  28. package/dist/charts/color-legend.cjs +22 -0
  29. package/dist/charts/color-legend.cjs.map +1 -0
  30. package/dist/charts/color-legend.d.cts +2 -0
  31. package/dist/charts/color-legend.d.ts +2 -0
  32. package/dist/charts/color-legend.js +13 -0
  33. package/dist/charts/color-legend.js.map +1 -0
  34. package/dist/charts/data-table.cjs +22 -0
  35. package/dist/charts/data-table.cjs.map +1 -0
  36. package/dist/charts/data-table.d.cts +2 -0
  37. package/dist/charts/data-table.d.ts +2 -0
  38. package/dist/charts/data-table.js +13 -0
  39. package/dist/charts/data-table.js.map +1 -0
  40. package/dist/charts/line.cjs +30 -0
  41. package/dist/charts/line.cjs.map +1 -0
  42. package/dist/charts/line.d.cts +2 -0
  43. package/dist/charts/line.d.ts +2 -0
  44. package/dist/charts/line.js +17 -0
  45. package/dist/charts/line.js.map +1 -0
  46. package/dist/charts/markdown.cjs +16 -0
  47. package/dist/charts/markdown.cjs.map +1 -0
  48. package/dist/charts/markdown.css +77 -0
  49. package/dist/charts/markdown.css.map +1 -0
  50. package/dist/charts/markdown.d.cts +2 -0
  51. package/dist/charts/markdown.d.ts +2 -0
  52. package/dist/charts/markdown.js +7 -0
  53. package/dist/charts/markdown.js.map +1 -0
  54. package/dist/charts/pivot-table.cjs +23 -0
  55. package/dist/charts/pivot-table.cjs.map +1 -0
  56. package/dist/charts/pivot-table.d.cts +2 -0
  57. package/dist/charts/pivot-table.d.ts +2 -0
  58. package/dist/charts/pivot-table.js +14 -0
  59. package/dist/charts/pivot-table.js.map +1 -0
  60. package/dist/charts/row.cjs +27 -0
  61. package/dist/charts/row.cjs.map +1 -0
  62. package/dist/charts/row.d.cts +6 -0
  63. package/dist/charts/row.d.ts +6 -0
  64. package/dist/charts/row.js +18 -0
  65. package/dist/charts/row.js.map +1 -0
  66. package/dist/charts/scatter.cjs +24 -0
  67. package/dist/charts/scatter.cjs.map +1 -0
  68. package/dist/charts/scatter.d.cts +2 -0
  69. package/dist/charts/scatter.d.ts +2 -0
  70. package/dist/charts/scatter.js +15 -0
  71. package/dist/charts/scatter.js.map +1 -0
  72. package/dist/charts/summary-table.cjs +19 -0
  73. package/dist/charts/summary-table.cjs.map +1 -0
  74. package/dist/charts/summary-table.d.cts +2 -0
  75. package/dist/charts/summary-table.d.ts +2 -0
  76. package/dist/charts/summary-table.js +10 -0
  77. package/dist/charts/summary-table.js.map +1 -0
  78. package/dist/charts/three-d-scatter.cjs +24 -0
  79. package/dist/charts/three-d-scatter.cjs.map +1 -0
  80. package/dist/charts/three-d-scatter.d.cts +6 -0
  81. package/dist/charts/three-d-scatter.d.ts +6 -0
  82. package/dist/charts/three-d-scatter.js +15 -0
  83. package/dist/charts/three-d-scatter.js.map +1 -0
  84. package/dist/chunk-3PGYFLE3.cjs +499 -0
  85. package/dist/chunk-3PGYFLE3.cjs.map +1 -0
  86. package/dist/chunk-4HNDKMWF.js +167 -0
  87. package/dist/chunk-4HNDKMWF.js.map +1 -0
  88. package/dist/chunk-4QNENKTV.cjs +1214 -0
  89. package/dist/chunk-4QNENKTV.cjs.map +1 -0
  90. package/dist/chunk-54M32JJB.js +132 -0
  91. package/dist/chunk-54M32JJB.js.map +1 -0
  92. package/dist/chunk-5B23JVH3.cjs +2238 -0
  93. package/dist/chunk-5B23JVH3.cjs.map +1 -0
  94. package/dist/chunk-5LH6NSNL.js +456 -0
  95. package/dist/chunk-5LH6NSNL.js.map +1 -0
  96. package/dist/chunk-6DOKKCJ5.cjs +313 -0
  97. package/dist/chunk-6DOKKCJ5.cjs.map +1 -0
  98. package/dist/chunk-6HZX5CBT.cjs +1074 -0
  99. package/dist/chunk-6HZX5CBT.cjs.map +1 -0
  100. package/dist/chunk-6U5DOIQM.js +1071 -0
  101. package/dist/chunk-6U5DOIQM.js.map +1 -0
  102. package/dist/chunk-7GBZIK2R.cjs +80 -0
  103. package/dist/chunk-7GBZIK2R.cjs.map +1 -0
  104. package/dist/chunk-7PTJNIOD.js +520 -0
  105. package/dist/chunk-7PTJNIOD.js.map +1 -0
  106. package/dist/chunk-AHZYQ7FN.js +319 -0
  107. package/dist/chunk-AHZYQ7FN.js.map +1 -0
  108. package/dist/chunk-AOODAOL4.cjs +55 -0
  109. package/dist/chunk-AOODAOL4.cjs.map +1 -0
  110. package/dist/chunk-ASKFIRMC.cjs +165 -0
  111. package/dist/chunk-ASKFIRMC.cjs.map +1 -0
  112. package/dist/chunk-B7OQUBH4.cjs +171 -0
  113. package/dist/chunk-B7OQUBH4.cjs.map +1 -0
  114. package/dist/chunk-BIMEHDLB.js +72 -0
  115. package/dist/chunk-BIMEHDLB.js.map +1 -0
  116. package/dist/chunk-C62KHV7S.js +1225 -0
  117. package/dist/chunk-C62KHV7S.js.map +1 -0
  118. package/dist/chunk-C7GQT65O.cjs +1119 -0
  119. package/dist/chunk-C7GQT65O.cjs.map +1 -0
  120. package/dist/chunk-CGDWDV4Z.js +1211 -0
  121. package/dist/chunk-CGDWDV4Z.js.map +1 -0
  122. package/dist/chunk-CJQQFNEY.js +69 -0
  123. package/dist/chunk-CJQQFNEY.js.map +1 -0
  124. package/dist/chunk-DHYLSXNR.js +964 -0
  125. package/dist/chunk-DHYLSXNR.js.map +1 -0
  126. package/dist/chunk-DKFA4KOJ.cjs +87 -0
  127. package/dist/chunk-DKFA4KOJ.cjs.map +1 -0
  128. package/dist/chunk-DS5BS7PP.cjs +357 -0
  129. package/dist/chunk-DS5BS7PP.cjs.map +1 -0
  130. package/dist/chunk-EGMFQSGK.js +909 -0
  131. package/dist/chunk-EGMFQSGK.js.map +1 -0
  132. package/dist/chunk-EU5EFHO5.cjs +53 -0
  133. package/dist/chunk-EU5EFHO5.cjs.map +1 -0
  134. package/dist/chunk-FM46QSDM.js +837 -0
  135. package/dist/chunk-FM46QSDM.js.map +1 -0
  136. package/dist/chunk-GV2LRR32.cjs +843 -0
  137. package/dist/chunk-GV2LRR32.cjs.map +1 -0
  138. package/dist/chunk-H3XKFSA5.cjs +322 -0
  139. package/dist/chunk-H3XKFSA5.cjs.map +1 -0
  140. package/dist/chunk-H5AVMMCE.js +347 -0
  141. package/dist/chunk-H5AVMMCE.js.map +1 -0
  142. package/dist/chunk-JDMUN4VK.cjs +986 -0
  143. package/dist/chunk-JDMUN4VK.cjs.map +1 -0
  144. package/dist/chunk-JNO7OXCC.js +2171 -0
  145. package/dist/chunk-JNO7OXCC.js.map +1 -0
  146. package/dist/chunk-JQMG4ZR7.js +111 -0
  147. package/dist/chunk-JQMG4ZR7.js.map +1 -0
  148. package/dist/chunk-L2Q2R43U.cjs +118 -0
  149. package/dist/chunk-L2Q2R43U.cjs.map +1 -0
  150. package/dist/chunk-LBTZVERO.js +466 -0
  151. package/dist/chunk-LBTZVERO.js.map +1 -0
  152. package/dist/chunk-LUUGGTTK.cjs +1227 -0
  153. package/dist/chunk-LUUGGTTK.cjs.map +1 -0
  154. package/dist/chunk-MEEPH7ZC.cjs +911 -0
  155. package/dist/chunk-MEEPH7ZC.cjs.map +1 -0
  156. package/dist/chunk-MJPDJ5AS.js +184 -0
  157. package/dist/chunk-MJPDJ5AS.js.map +1 -0
  158. package/dist/chunk-MJROUIDZ.cjs +219 -0
  159. package/dist/chunk-MJROUIDZ.cjs.map +1 -0
  160. package/dist/chunk-MUWSBYJS.js +528 -0
  161. package/dist/chunk-MUWSBYJS.js.map +1 -0
  162. package/dist/chunk-NDF5LCST.js +1088 -0
  163. package/dist/chunk-NDF5LCST.js.map +1 -0
  164. package/dist/chunk-NGWTPED4.js +311 -0
  165. package/dist/chunk-NGWTPED4.js.map +1 -0
  166. package/dist/chunk-PP4VCONW.js +739 -0
  167. package/dist/chunk-PP4VCONW.js.map +1 -0
  168. package/dist/chunk-PRMC2A6P.cjs +488 -0
  169. package/dist/chunk-PRMC2A6P.cjs.map +1 -0
  170. package/dist/chunk-PST6UU6S.cjs +742 -0
  171. package/dist/chunk-PST6UU6S.cjs.map +1 -0
  172. package/dist/chunk-Q7LK2UWH.js +33 -0
  173. package/dist/chunk-Q7LK2UWH.js.map +1 -0
  174. package/dist/chunk-SFV5LZPD.js +162 -0
  175. package/dist/chunk-SFV5LZPD.js.map +1 -0
  176. package/dist/chunk-SKXGYJJG.cjs +370 -0
  177. package/dist/chunk-SKXGYJJG.cjs.map +1 -0
  178. package/dist/chunk-SWLNKY6R.js +477 -0
  179. package/dist/chunk-SWLNKY6R.js.map +1 -0
  180. package/dist/chunk-TGVLNHUB.cjs +483 -0
  181. package/dist/chunk-TGVLNHUB.cjs.map +1 -0
  182. package/dist/chunk-TLLRW6S3.js +74 -0
  183. package/dist/chunk-TLLRW6S3.js.map +1 -0
  184. package/dist/chunk-TRAIXZ6M.cjs +140 -0
  185. package/dist/chunk-TRAIXZ6M.cjs.map +1 -0
  186. package/dist/chunk-U74A5VK5.cjs +74 -0
  187. package/dist/chunk-U74A5VK5.cjs.map +1 -0
  188. package/dist/chunk-UAVDLBTA.cjs +74 -0
  189. package/dist/chunk-UAVDLBTA.cjs.map +1 -0
  190. package/dist/chunk-UJWS6TRV.js +354 -0
  191. package/dist/chunk-UJWS6TRV.js.map +1 -0
  192. package/dist/chunk-UUJMUPB3.cjs +531 -0
  193. package/dist/chunk-UUJMUPB3.cjs.map +1 -0
  194. package/dist/chunk-VTKHZIJB.cjs +549 -0
  195. package/dist/chunk-VTKHZIJB.cjs.map +1 -0
  196. package/dist/chunk-YXPAWUXO.js +51 -0
  197. package/dist/chunk-YXPAWUXO.js.map +1 -0
  198. package/dist/chunk-ZD64SCZB.js +80 -0
  199. package/dist/chunk-ZD64SCZB.js.map +1 -0
  200. package/dist/core.cjs +28 -0
  201. package/dist/core.cjs.map +1 -0
  202. package/dist/core.d.cts +22 -0
  203. package/dist/core.d.ts +22 -0
  204. package/dist/core.js +3 -0
  205. package/dist/core.js.map +1 -0
  206. package/dist/dataLayerState-B4XmQjQN.d.cts +258 -0
  207. package/dist/dataLayerState-B4XmQjQN.d.ts +258 -0
  208. package/package.json +32 -52
@@ -0,0 +1,837 @@
1
+ import { useColorScales } from './chunk-4HNDKMWF.js';
2
+ import { DEFAULT_CHART_SETTINGS } from './chunk-CJQQFNEY.js';
3
+ import { BaseChart } from './chunk-UJWS6TRV.js';
4
+ import { numericScale } from './chunk-7PTJNIOD.js';
5
+ import { FieldSelector, ComboBox } from './chunk-SFV5LZPD.js';
6
+ import { Switch } from './chunk-Q7LK2UWH.js';
7
+ import { useGetColumnDataForIds, useGetLiveData } from './chunk-ZD64SCZB.js';
8
+ import { applyFilter } from './chunk-YXPAWUXO.js';
9
+ import { Label } from './chunk-MJPDJ5AS.js';
10
+ import { useDataLayer, getChartAxisFields, getChartAxisLabel, finiteNumbers, categoryValue, finiteNumber, categoryLabel, categoryIncludes, categoryEqual } from './chunk-JNO7OXCC.js';
11
+ import { scaleBand } from 'd3-scale';
12
+ import natsort from 'natsort';
13
+ import { useState, useMemo, useCallback } from 'react';
14
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
15
+
16
+ // src/components/charts/BoxPlot/boxPlotCalculations.ts
17
+ var MAX_BEE_SWARM_POINTS_PER_GROUP = 300;
18
+ function getQuartile(sortedData, q) {
19
+ const pos = (sortedData.length - 1) * q;
20
+ const base = Math.floor(pos);
21
+ const rest = pos - base;
22
+ const value = sortedData[base];
23
+ const next = sortedData[base + 1];
24
+ if (next !== void 0) {
25
+ return value + rest * (next - value);
26
+ } else {
27
+ return value;
28
+ }
29
+ }
30
+ function calculateBoxPlotStats(data, whiskerType) {
31
+ if (data.length === 0) {
32
+ return {
33
+ q1: 0,
34
+ median: 0,
35
+ q3: 0,
36
+ iqr: 0,
37
+ whiskerLow: 0,
38
+ whiskerHigh: 0,
39
+ outliers: [],
40
+ totalCount: 0
41
+ };
42
+ }
43
+ const sortedData = [...data].sort((a, b) => a - b);
44
+ const q1 = getQuartile(sortedData, 0.25);
45
+ const median = getQuartile(sortedData, 0.5);
46
+ const q3 = getQuartile(sortedData, 0.75);
47
+ const iqr = q3 - q1;
48
+ let whiskerLow;
49
+ let whiskerHigh;
50
+ let outliers;
51
+ switch (whiskerType) {
52
+ case "tukey": {
53
+ const lowerFence = q1 - 1.5 * iqr;
54
+ const upperFence = q3 + 1.5 * iqr;
55
+ whiskerLow = sortedData.find((value) => value >= lowerFence) ?? sortedData[0];
56
+ whiskerHigh = [...sortedData].reverse().find((value) => value <= upperFence) ?? sortedData[sortedData.length - 1];
57
+ if (whiskerLow > whiskerHigh) {
58
+ whiskerLow = sortedData[0];
59
+ whiskerHigh = sortedData[sortedData.length - 1];
60
+ }
61
+ outliers = sortedData.filter((x) => x < whiskerLow || x > whiskerHigh);
62
+ break;
63
+ }
64
+ case "minmax": {
65
+ whiskerLow = sortedData[0];
66
+ whiskerHigh = sortedData[sortedData.length - 1];
67
+ outliers = [];
68
+ break;
69
+ }
70
+ case "stdDev": {
71
+ const mean = sortedData.reduce((sum, x) => sum + x, 0) / sortedData.length;
72
+ const stdDev = Math.sqrt(
73
+ sortedData.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0) / sortedData.length
74
+ );
75
+ whiskerLow = mean - 2 * stdDev;
76
+ whiskerHigh = mean + 2 * stdDev;
77
+ outliers = sortedData.filter((x) => x < whiskerLow || x > whiskerHigh);
78
+ break;
79
+ }
80
+ }
81
+ return {
82
+ q1,
83
+ median,
84
+ q3,
85
+ iqr,
86
+ whiskerLow,
87
+ whiskerHigh,
88
+ outliers,
89
+ totalCount: data.length
90
+ };
91
+ }
92
+ function calculateKernelDensity(data, bandwidth, numPoints = 100) {
93
+ const sortedData = [...data].sort((a, b) => a - b);
94
+ if (sortedData.length === 0) {
95
+ return [];
96
+ }
97
+ const min = sortedData[0];
98
+ const max = sortedData[sortedData.length - 1];
99
+ const range = max - min;
100
+ const points = Array.from({ length: numPoints }, (_, i) => {
101
+ const t = i / (numPoints - 1);
102
+ return min + t * range;
103
+ });
104
+ const density = points.map((x) => {
105
+ const sum = sortedData.reduce((acc, xi) => {
106
+ const diff = x - xi;
107
+ return acc + Math.exp(-(diff * diff) / (2 * bandwidth * bandwidth));
108
+ }, 0);
109
+ const densityValue = sum / (sortedData.length * bandwidth * Math.sqrt(2 * Math.PI));
110
+ return [x, densityValue];
111
+ });
112
+ const maxDensity = Math.max(...density.map(([, y]) => y));
113
+ return density.map(([x, y]) => [x, y / maxDensity]);
114
+ }
115
+ function calculateBeeSwarmPositions(data, width, maxPoints = MAX_BEE_SWARM_POINTS_PER_GROUP, seed = 0, yToPixel = (value) => value) {
116
+ if (data.length > maxPoints) {
117
+ const shuffled = [...data];
118
+ let state = seed >>> 0;
119
+ const random = () => {
120
+ state = state * 1664525 + 1013904223 >>> 0;
121
+ return state / 2 ** 32;
122
+ };
123
+ for (let i = shuffled.length - 1; i > 0; i--) {
124
+ const j = Math.floor(random() * (i + 1));
125
+ const value = shuffled[i];
126
+ shuffled[i] = shuffled[j];
127
+ shuffled[j] = value;
128
+ }
129
+ const sampledData = shuffled.slice(0, maxPoints);
130
+ return calculateBeeSwarmPositionsForData(sampledData, width, yToPixel);
131
+ }
132
+ return calculateBeeSwarmPositionsForData(data, width, yToPixel);
133
+ }
134
+ function calculateBeeSwarmPositionsForData(data, width, yToPixel) {
135
+ const positions = [];
136
+ const radius = 2;
137
+ const spacing = radius * 2;
138
+ const sortedData = [...data].sort((a, b) => a - b);
139
+ const screenY = sortedData.map(yToPixel);
140
+ for (let index = 0; index < sortedData.length; index++) {
141
+ const value = sortedData[index];
142
+ const pointY = screenY[index];
143
+ let x = 0;
144
+ let attempts = 0;
145
+ const maxAttempts = 100;
146
+ while (attempts < maxAttempts) {
147
+ const overlaps = positions.some(([px], positionIndex) => {
148
+ const dx = x - px;
149
+ const dy = pointY - screenY[positionIndex];
150
+ return dx * dx + dy * dy < spacing * spacing;
151
+ });
152
+ if (!overlaps) {
153
+ break;
154
+ }
155
+ x = (x + spacing) % width;
156
+ attempts++;
157
+ }
158
+ positions.push([x, value]);
159
+ }
160
+ return positions;
161
+ }
162
+ var Y_SCALE_PADDING = 0.1;
163
+ var BOX_PADDING = 0.2;
164
+ function BoxPlot({
165
+ settings,
166
+ width,
167
+ height,
168
+ facetIds
169
+ }) {
170
+ const [tooltip, setTooltip] = useState(null);
171
+ const updateChart = useDataLayer((s) => s.updateChart);
172
+ const getFieldLabel = useDataLayer((s) => s.getFieldLabel);
173
+ const fieldSettings = useDataLayer((s) => s.fieldSettings);
174
+ const formatFieldValue = useDataLayer((s) => s.formatFieldValue);
175
+ const { getColorForValue } = useColorScales();
176
+ void fieldSettings[settings.field];
177
+ if (settings.colorField) void fieldSettings[settings.colorField];
178
+ const allData = useGetColumnDataForIds(settings.field);
179
+ const allGroupData = useGetColumnDataForIds(settings.colorField);
180
+ const liveData = useGetLiveData(settings, settings.field, facetIds);
181
+ const colorFieldData = useGetLiveData(
182
+ settings,
183
+ settings.colorField,
184
+ facetIds
185
+ );
186
+ const hasColorField = !!settings.colorField;
187
+ const axisFields = getChartAxisFields(settings);
188
+ const xAxisLabel = getChartAxisLabel(
189
+ axisFields.x,
190
+ settings.xAxisLabel,
191
+ getFieldLabel
192
+ );
193
+ const yAxisLabel = getChartAxisLabel(
194
+ axisFields.y,
195
+ settings.yAxisLabel,
196
+ getFieldLabel
197
+ );
198
+ const margin = {
199
+ ...settings.margin,
200
+ left: Math.max(settings.margin.left, yAxisLabel ? 64 : 42),
201
+ bottom: Math.max(settings.margin.bottom, xAxisLabel ? 44 : 28)
202
+ };
203
+ const innerWidth = width - margin.left - margin.right;
204
+ const innerHeight = height - margin.top - margin.bottom;
205
+ const groupedData = useMemo(() => {
206
+ if (!hasColorField) {
207
+ const validData = finiteNumbers(liveData);
208
+ const result2 = [
209
+ {
210
+ group: "All Data",
211
+ data: validData
212
+ }
213
+ ];
214
+ return result2;
215
+ }
216
+ const groups = /* @__PURE__ */ new Map();
217
+ liveData.forEach((value, index) => {
218
+ const colorValue = categoryValue(colorFieldData[index]);
219
+ const numValue = finiteNumber(value);
220
+ if (numValue === void 0) return;
221
+ if (!groups.has(colorValue)) groups.set(colorValue, []);
222
+ groups.get(colorValue).push(numValue);
223
+ });
224
+ const result = Array.from(groups.entries()).map(([group, data]) => ({
225
+ group,
226
+ data
227
+ }));
228
+ return result;
229
+ }, [liveData, colorFieldData, hasColorField]);
230
+ const groupStats = useMemo(() => {
231
+ const stats = groupedData.map(({ group, data }) => ({
232
+ group,
233
+ stats: calculateBoxPlotStats(data, settings.whiskerType)
234
+ }));
235
+ return stats;
236
+ }, [groupedData, settings.whiskerType]);
237
+ const groupKDEs = useMemo(() => {
238
+ if (!settings.violinOverlay) {
239
+ return null;
240
+ }
241
+ return groupedData.map(({ group, data }) => {
242
+ const mean = data.reduce((sum, value) => sum + value, 0) / data.length;
243
+ const stdDev = Math.sqrt(
244
+ data.reduce((sum, value) => sum + (value - mean) ** 2, 0) / data.length
245
+ );
246
+ const autoBandwidth = Math.max(
247
+ 1e-3,
248
+ 1.06 * (stdDev || 1) * Math.pow(Math.max(1, data.length), -0.2)
249
+ );
250
+ const bandwidth = settings.autoBandwidth ? autoBandwidth : settings.violinBandwidth;
251
+ return {
252
+ group,
253
+ kde: calculateKernelDensity(data, bandwidth)
254
+ };
255
+ });
256
+ }, [
257
+ settings.violinOverlay,
258
+ settings.autoBandwidth,
259
+ settings.violinBandwidth,
260
+ groupedData
261
+ ]);
262
+ const xScale = useMemo(() => {
263
+ const groups = settings.colorField ? [...new Set(allGroupData.map(categoryLabel))] : ["All Data"];
264
+ const medians = /* @__PURE__ */ new Map();
265
+ if (settings.sortBy === "median") {
266
+ const values = /* @__PURE__ */ new Map();
267
+ allData.forEach((value, index) => {
268
+ const number = finiteNumber(value);
269
+ if (number === void 0) return;
270
+ const group = settings.colorField ? categoryLabel(allGroupData[index]) : "All Data";
271
+ if (!values.has(group)) values.set(group, []);
272
+ values.get(group).push(number);
273
+ });
274
+ values.forEach(
275
+ (data, group) => medians.set(
276
+ group,
277
+ calculateBoxPlotStats(data, settings.whiskerType).median
278
+ )
279
+ );
280
+ }
281
+ const sortedGroups = [...groups].sort((a, b) => {
282
+ if (settings.sortBy === "median") {
283
+ return (medians.get(b) ?? 0) - (medians.get(a) ?? 0);
284
+ }
285
+ return natsort()(a, b);
286
+ });
287
+ const scale = scaleBand().domain(sortedGroups).range([0, innerWidth]).padding(BOX_PADDING);
288
+ return scale;
289
+ }, [
290
+ allData,
291
+ allGroupData,
292
+ settings.colorField,
293
+ innerWidth,
294
+ settings.sortBy,
295
+ settings.whiskerType
296
+ ]);
297
+ const createPath = useCallback((points) => {
298
+ if (points.length === 0) {
299
+ return "";
300
+ }
301
+ return points.map((point, i) => `${i === 0 ? "M" : "L"} ${point[0]} ${point[1]}`).join(" ");
302
+ }, []);
303
+ const yScale = useMemo(() => {
304
+ const values = finiteNumbers(allData);
305
+ const min = Math.min(...values);
306
+ const max = Math.max(...values);
307
+ const range = max - min;
308
+ const padding = range * Y_SCALE_PADDING;
309
+ const scale = numericScale(settings.yAxis).domain([
310
+ settings.yAxis.scaleType === "symlog" ? min : min - padding,
311
+ max + padding
312
+ ]).range([innerHeight, 0]);
313
+ return scale;
314
+ }, [allData, innerHeight, settings.yAxis]);
315
+ const groupBeeSwarmPositions = useMemo(() => {
316
+ if (!settings.beeSwarmOverlay) {
317
+ return null;
318
+ }
319
+ return groupedData.map(({ group, data }) => ({
320
+ group,
321
+ positions: calculateBeeSwarmPositions(
322
+ data,
323
+ xScale.bandwidth(),
324
+ MAX_BEE_SWARM_POINTS_PER_GROUP,
325
+ 0,
326
+ yScale
327
+ )
328
+ }));
329
+ }, [groupedData, settings.beeSwarmOverlay, xScale, yScale]);
330
+ const beeSwarmIsSampled = settings.beeSwarmOverlay && groupedData.some(
331
+ ({ data }) => data.length > MAX_BEE_SWARM_POINTS_PER_GROUP
332
+ );
333
+ const activeFilter = useMemo(() => {
334
+ return settings.filters.find(
335
+ (f) => f.field === settings.colorField
336
+ );
337
+ }, [settings.filters, settings.colorField]);
338
+ const handleBoxClick = useCallback(
339
+ (group) => {
340
+ if (!settings.colorField) {
341
+ return;
342
+ }
343
+ let newValues = [];
344
+ if (activeFilter && activeFilter.type === "value") {
345
+ if (categoryIncludes(activeFilter.values, group)) {
346
+ newValues = activeFilter.values.filter(
347
+ (v) => !categoryEqual(v, group)
348
+ );
349
+ } else {
350
+ newValues = [...activeFilter.values, group];
351
+ }
352
+ } else {
353
+ newValues = [group];
354
+ }
355
+ const newFilters = settings.filters.filter(
356
+ (f) => f.field !== settings.colorField
357
+ );
358
+ if (newValues.length > 0) {
359
+ newFilters.push({
360
+ type: "value",
361
+ field: settings.colorField,
362
+ values: newValues
363
+ });
364
+ }
365
+ updateChart(settings.id, { filters: newFilters });
366
+ },
367
+ [
368
+ settings.colorField,
369
+ settings.filters,
370
+ settings.id,
371
+ activeFilter,
372
+ updateChart
373
+ ]
374
+ );
375
+ const isGroupFiltered = useCallback(
376
+ (group) => {
377
+ if (!settings.colorField) {
378
+ return true;
379
+ }
380
+ if (!activeFilter || activeFilter.type !== "value" || !activeFilter.values) {
381
+ return true;
382
+ }
383
+ return applyFilter(group, activeFilter);
384
+ },
385
+ [settings.colorField, activeFilter]
386
+ );
387
+ return /* @__PURE__ */ jsxs(
388
+ "div",
389
+ {
390
+ className: "relative",
391
+ style: { width, height },
392
+ onPointerLeave: () => setTooltip(null),
393
+ children: [
394
+ tooltip && /* @__PURE__ */ jsx("div", { role: "tooltip", className: "eda-tooltip", children: tooltip }),
395
+ /* @__PURE__ */ jsxs(
396
+ BaseChart,
397
+ {
398
+ width,
399
+ height,
400
+ xScale,
401
+ yScale,
402
+ brushingMode: "none",
403
+ settings: { ...settings, margin },
404
+ children: [
405
+ beeSwarmIsSampled && /* @__PURE__ */ jsxs(
406
+ "text",
407
+ {
408
+ x: innerWidth,
409
+ y: 14,
410
+ textAnchor: "end",
411
+ fontSize: 10,
412
+ className: "fill-muted-foreground",
413
+ pointerEvents: "none",
414
+ children: [
415
+ "Sample \u2264",
416
+ MAX_BEE_SWARM_POINTS_PER_GROUP,
417
+ "/group \xB7 boxes use all rows"
418
+ ]
419
+ }
420
+ ),
421
+ groupStats.filter(({ stats }) => stats.totalCount > 0).map(({ group, stats }) => {
422
+ const isFiltered = isGroupFiltered(group);
423
+ const boxColor = isFiltered ? getColorForValue(
424
+ settings.colorScaleId,
425
+ group,
426
+ settings.styles.boxFill
427
+ ) : "rgb(156 163 175)";
428
+ const xPos = xScale(categoryLabel(group)) ?? 0;
429
+ const boxWidth = xScale.bandwidth();
430
+ const kde = groupKDEs?.find((g) => g.group === group)?.kde;
431
+ const firstKde = kde?.[0];
432
+ const lastKde = kde?.at(-1);
433
+ const beeSwarmPositions = groupBeeSwarmPositions?.find(
434
+ (g) => g.group === group
435
+ )?.positions;
436
+ const format = (value) => formatFieldValue(settings.field, value);
437
+ const formatGroup = (value) => settings.colorField ? formatFieldValue(settings.colorField, value) : categoryLabel(value);
438
+ const boxTooltipContent = /* @__PURE__ */ jsxs("div", { children: [
439
+ /* @__PURE__ */ jsxs("p", { className: "font-medium", children: [
440
+ formatGroup(group),
441
+ " ",
442
+ /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
443
+ "\xB7 ",
444
+ stats.totalCount,
445
+ " rows"
446
+ ] })
447
+ ] }),
448
+ /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-[auto_auto] gap-x-3", children: [
449
+ /* @__PURE__ */ jsx("dt", { children: "Median" }),
450
+ /* @__PURE__ */ jsx("dd", { className: "text-right tabular-nums", children: format(stats.median) }),
451
+ /* @__PURE__ */ jsx("dt", { children: "Middle 50%" }),
452
+ /* @__PURE__ */ jsxs("dd", { className: "text-right tabular-nums", children: [
453
+ format(stats.q1),
454
+ "\u2013",
455
+ format(stats.q3)
456
+ ] }),
457
+ stats.outliers.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
458
+ /* @__PURE__ */ jsx("dt", { children: "Outliers" }),
459
+ /* @__PURE__ */ jsx("dd", { className: "text-right", children: stats.outliers.length })
460
+ ] })
461
+ ] })
462
+ ] });
463
+ const whiskerTooltipContent = /* @__PURE__ */ jsxs("div", { children: [
464
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: formatGroup(group) }),
465
+ /* @__PURE__ */ jsxs("p", { children: [
466
+ "Whiskers ",
467
+ format(stats.whiskerLow),
468
+ "\u2013",
469
+ format(stats.whiskerHigh)
470
+ ] })
471
+ ] });
472
+ return /* @__PURE__ */ jsxs("g", { transform: `translate(${xPos}, 0)`, children: [
473
+ /* @__PURE__ */ jsx(
474
+ "line",
475
+ {
476
+ onPointerEnter: () => setTooltip(whiskerTooltipContent),
477
+ onPointerLeave: () => setTooltip(null),
478
+ x1: boxWidth / 2,
479
+ x2: boxWidth / 2,
480
+ y1: yScale(stats.whiskerHigh),
481
+ y2: yScale(stats.whiskerLow),
482
+ stroke: boxColor,
483
+ strokeWidth: settings.styles.whiskerStrokeWidth
484
+ }
485
+ ),
486
+ /* @__PURE__ */ jsx(
487
+ "rect",
488
+ {
489
+ onPointerEnter: () => setTooltip(boxTooltipContent),
490
+ onPointerMove: () => setTooltip(boxTooltipContent),
491
+ onPointerLeave: () => setTooltip(null),
492
+ onFocus: () => setTooltip(boxTooltipContent),
493
+ onBlur: () => setTooltip(null),
494
+ role: settings.colorField ? "button" : void 0,
495
+ tabIndex: settings.colorField ? 0 : void 0,
496
+ "aria-label": `${group}: median ${stats.median.toFixed(2)}, ${stats.totalCount} records`,
497
+ "aria-pressed": activeFilter?.type === "value" ? categoryIncludes(activeFilter.values, group) : false,
498
+ onKeyDown: (event) => {
499
+ if (event.key === "Enter" || event.key === " ") {
500
+ event.preventDefault();
501
+ handleBoxClick(group);
502
+ }
503
+ },
504
+ className: "chart-mark",
505
+ rx: 2,
506
+ x: boxWidth * 0.15,
507
+ y: yScale(stats.q3),
508
+ width: boxWidth * 0.7,
509
+ height: yScale(stats.q1) - yScale(stats.q3),
510
+ fill: boxColor,
511
+ stroke: settings.styles.boxStroke,
512
+ strokeWidth: settings.styles.boxStrokeWidth,
513
+ onClick: () => handleBoxClick(group),
514
+ style: {
515
+ cursor: settings.colorField ? "pointer" : "default"
516
+ }
517
+ }
518
+ ),
519
+ /* @__PURE__ */ jsx(
520
+ "line",
521
+ {
522
+ pointerEvents: "none",
523
+ x1: 0,
524
+ x2: boxWidth,
525
+ y1: yScale(stats.median),
526
+ y2: yScale(stats.median),
527
+ stroke: settings.styles.medianStroke,
528
+ strokeWidth: settings.styles.medianStrokeWidth
529
+ }
530
+ ),
531
+ settings.showOutliers && stats.outliers.map((value, i) => /* @__PURE__ */ jsx(
532
+ "circle",
533
+ {
534
+ onPointerEnter: () => setTooltip(/* @__PURE__ */ jsxs("span", { children: [
535
+ "Outlier: ",
536
+ format(value)
537
+ ] })),
538
+ onPointerLeave: () => setTooltip(null),
539
+ cx: boxWidth / 2,
540
+ cy: yScale(value),
541
+ r: settings.styles.outlierSize,
542
+ fill: boxColor,
543
+ stroke: settings.styles.outlierStroke
544
+ },
545
+ i
546
+ )),
547
+ settings.violinOverlay && kde && firstKde && lastKde && /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
548
+ "path",
549
+ {
550
+ d: createPath(
551
+ settings.beeSwarmOverlay ? (
552
+ // If bee swarm is enabled, only show left half of violin
553
+ [
554
+ [boxWidth / 2, yScale(firstKde[0])],
555
+ ...kde.map(
556
+ ([x, y]) => [
557
+ boxWidth / 2 - y * boxWidth * 0.4,
558
+ yScale(x)
559
+ ]
560
+ ),
561
+ [boxWidth / 2, yScale(lastKde[0])]
562
+ ]
563
+ ) : (
564
+ // Otherwise show full violin
565
+ [
566
+ [boxWidth / 2, yScale(firstKde[0])],
567
+ ...kde.map(
568
+ ([x, y]) => [
569
+ boxWidth / 2 + y * boxWidth * 0.4,
570
+ yScale(x)
571
+ ]
572
+ ),
573
+ ...[...kde].reverse().map(
574
+ ([x, y]) => [
575
+ boxWidth / 2 - y * boxWidth * 0.4,
576
+ yScale(x)
577
+ ]
578
+ )
579
+ ]
580
+ )
581
+ ),
582
+ fill: boxColor,
583
+ fillOpacity: 0.2,
584
+ stroke: boxColor,
585
+ strokeWidth: 1,
586
+ pointerEvents: "none"
587
+ }
588
+ ) }),
589
+ settings.beeSwarmOverlay && beeSwarmPositions && /* @__PURE__ */ jsx("g", { children: beeSwarmPositions.map(([x, y], i) => /* @__PURE__ */ jsx(
590
+ "circle",
591
+ {
592
+ cx: settings.violinOverlay ? boxWidth / 2 + Math.abs(x) : boxWidth / 2 + x,
593
+ cy: yScale(y),
594
+ r: 2,
595
+ fill: boxColor,
596
+ fillOpacity: 0.5,
597
+ stroke: "none",
598
+ pointerEvents: "none"
599
+ },
600
+ i
601
+ )) })
602
+ ] }, categoryLabel(group));
603
+ })
604
+ ]
605
+ }
606
+ )
607
+ ]
608
+ }
609
+ );
610
+ }
611
+ function BoxPlotSettingsPanel({
612
+ settings,
613
+ onSettingsChange
614
+ }) {
615
+ const { getOrCreateScaleForField } = useColorScales();
616
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
617
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
618
+ /* @__PURE__ */ jsx(Label, { htmlFor: "field", children: "Field" }),
619
+ /* @__PURE__ */ jsx(
620
+ FieldSelector,
621
+ {
622
+ label: "",
623
+ value: settings.field,
624
+ onChange: (value) => onSettingsChange({ ...settings, field: value })
625
+ }
626
+ )
627
+ ] }),
628
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
629
+ /* @__PURE__ */ jsx(Label, { htmlFor: "colorField", children: "Color Field" }),
630
+ /* @__PURE__ */ jsx(
631
+ FieldSelector,
632
+ {
633
+ label: "",
634
+ value: settings.colorField ?? "",
635
+ allowClear: true,
636
+ onChange: (value) => onSettingsChange({
637
+ ...settings,
638
+ colorField: value || void 0,
639
+ colorScaleId: value ? getOrCreateScaleForField(value) : void 0
640
+ })
641
+ }
642
+ )
643
+ ] }),
644
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
645
+ /* @__PURE__ */ jsx(Label, { htmlFor: "whiskerType", children: "Whisker Type" }),
646
+ /* @__PURE__ */ jsx(
647
+ ComboBox,
648
+ {
649
+ value: settings.whiskerType,
650
+ options: ["tukey", "minmax", "stdDev"],
651
+ onChange: (value) => onSettingsChange({
652
+ ...settings,
653
+ whiskerType: value
654
+ }),
655
+ placeholder: "Select whisker type",
656
+ optionToString: (option) => option
657
+ }
658
+ )
659
+ ] }),
660
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
661
+ /* @__PURE__ */ jsx(Label, { htmlFor: "showOutliers", children: "Show Outliers" }),
662
+ /* @__PURE__ */ jsx(
663
+ Switch,
664
+ {
665
+ id: "showOutliers",
666
+ checked: settings.showOutliers,
667
+ onCheckedChange: (checked) => onSettingsChange({ ...settings, showOutliers: checked })
668
+ }
669
+ )
670
+ ] }),
671
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
672
+ /* @__PURE__ */ jsx(Label, { htmlFor: "sortBy", children: "Sort By" }),
673
+ /* @__PURE__ */ jsx(
674
+ ComboBox,
675
+ {
676
+ value: settings.sortBy,
677
+ options: ["median", "label"],
678
+ onChange: (value) => onSettingsChange({
679
+ ...settings,
680
+ sortBy: value
681
+ }),
682
+ placeholder: "Select sorting method",
683
+ optionToString: (option) => option
684
+ }
685
+ )
686
+ ] }),
687
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
688
+ /* @__PURE__ */ jsx(Label, { htmlFor: "violinOverlay", children: "Violin Overlay" }),
689
+ /* @__PURE__ */ jsx(
690
+ Switch,
691
+ {
692
+ id: "violinOverlay",
693
+ checked: settings.violinOverlay,
694
+ onCheckedChange: (checked) => onSettingsChange({ ...settings, violinOverlay: checked })
695
+ }
696
+ )
697
+ ] }),
698
+ settings.violinOverlay && /* @__PURE__ */ jsxs(Fragment, { children: [
699
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
700
+ /* @__PURE__ */ jsx(Label, { htmlFor: "autoBandwidth", children: "Auto Bandwidth" }),
701
+ /* @__PURE__ */ jsx(
702
+ Switch,
703
+ {
704
+ id: "autoBandwidth",
705
+ checked: settings.autoBandwidth,
706
+ onCheckedChange: (checked) => onSettingsChange({ ...settings, autoBandwidth: checked })
707
+ }
708
+ )
709
+ ] }),
710
+ !settings.autoBandwidth && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
711
+ /* @__PURE__ */ jsx(Label, { htmlFor: "violinBandwidth", children: "Bandwidth" }),
712
+ /* @__PURE__ */ jsx(
713
+ "input",
714
+ {
715
+ type: "number",
716
+ id: "violinBandwidth",
717
+ min: "0.1",
718
+ max: "1",
719
+ step: "0.1",
720
+ value: settings.violinBandwidth,
721
+ onChange: (e) => onSettingsChange({
722
+ ...settings,
723
+ violinBandwidth: parseFloat(e.target.value)
724
+ }),
725
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
726
+ }
727
+ )
728
+ ] })
729
+ ] }),
730
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
731
+ /* @__PURE__ */ jsx(Label, { htmlFor: "beeSwarmOverlay", children: "Bee Swarm" }),
732
+ /* @__PURE__ */ jsx(
733
+ Switch,
734
+ {
735
+ id: "beeSwarmOverlay",
736
+ checked: settings.beeSwarmOverlay,
737
+ onCheckedChange: (checked) => onSettingsChange({ ...settings, beeSwarmOverlay: checked })
738
+ }
739
+ )
740
+ ] })
741
+ ] });
742
+ }
743
+ function BoxPlotIcon({ className }) {
744
+ return /* @__PURE__ */ jsxs(
745
+ "svg",
746
+ {
747
+ xmlns: "http://www.w3.org/2000/svg",
748
+ width: "200",
749
+ height: "200",
750
+ viewBox: "0 0 32 32",
751
+ className,
752
+ children: [
753
+ /* @__PURE__ */ jsx(
754
+ "path",
755
+ {
756
+ fill: "currentColor",
757
+ d: "M22 6V4H12v2h4v2h-4v12h4v2h-4v2h10v-2h-4v-2h4V8h-4V6Zm-8 12v-3h6v3Zm6-5h-6v-3h6Z"
758
+ }
759
+ ),
760
+ /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M30 30H4a2 2 0 0 1-2-2V2h2v26h26Z" })
761
+ ]
762
+ }
763
+ );
764
+ }
765
+
766
+ // src/components/charts/BoxPlot/definition.ts
767
+ var boxPlotDefinition = {
768
+ type: "boxplot",
769
+ name: "Box Plot",
770
+ description: "Display distribution of values with quartiles and outliers",
771
+ icon: BoxPlotIcon,
772
+ component: BoxPlot,
773
+ settingsPanel: BoxPlotSettingsPanel,
774
+ createDefaultSettings: (layout, field) => ({
775
+ ...DEFAULT_CHART_SETTINGS,
776
+ id: crypto.randomUUID(),
777
+ field: field ?? "",
778
+ type: "boxplot",
779
+ title: "Box Plot",
780
+ layout,
781
+ whiskerType: "tukey",
782
+ showOutliers: true,
783
+ violinOverlay: false,
784
+ beeSwarmOverlay: false,
785
+ sortBy: "label",
786
+ violinBandwidth: 0.3,
787
+ autoBandwidth: true,
788
+ styles: {
789
+ boxFill: "hsl(217.2 91.2% 59.8%)",
790
+ boxStroke: "black",
791
+ boxStrokeWidth: 1,
792
+ medianStroke: "white",
793
+ medianStrokeWidth: 2,
794
+ whiskerStroke: "black",
795
+ whiskerStrokeWidth: 1,
796
+ outlierSize: 3,
797
+ outlierStroke: "black",
798
+ outlierFill: "none"
799
+ },
800
+ filters: [],
801
+ xAxis: DEFAULT_CHART_SETTINGS.xAxis,
802
+ yAxis: DEFAULT_CHART_SETTINGS.yAxis,
803
+ margin: DEFAULT_CHART_SETTINGS.margin,
804
+ facet: DEFAULT_CHART_SETTINGS.facet,
805
+ colorScaleId: void 0,
806
+ colorField: void 0,
807
+ xAxisLabel: "",
808
+ yAxisLabel: "",
809
+ xGridLines: 5,
810
+ yGridLines: 5
811
+ }),
812
+ validateSettings: (settings) => {
813
+ return !!settings.field;
814
+ },
815
+ getFilterFunction: (settings, fieldGetter) => {
816
+ if (!settings.colorField) {
817
+ return () => true;
818
+ }
819
+ const dataHash = fieldGetter(settings.colorField);
820
+ const filter = settings.filters.find(
821
+ (f) => f.field === settings.colorField
822
+ );
823
+ return (d) => {
824
+ const value = dataHash[d];
825
+ if (filter) {
826
+ if (!applyFilter(value, filter)) {
827
+ return false;
828
+ }
829
+ }
830
+ return true;
831
+ };
832
+ }
833
+ };
834
+
835
+ export { boxPlotDefinition };
836
+ //# sourceMappingURL=chunk-FM46QSDM.js.map
837
+ //# sourceMappingURL=chunk-FM46QSDM.js.map