exploreda 0.0.6 → 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 -11288
  3. package/dist/ExplorEda.cjs.map +1 -1
  4. package/dist/ExplorEda.css +2282 -1689
  5. package/dist/ExplorEda.css.map +1 -1
  6. package/dist/ExplorEda.d.cts +124 -278
  7. package/dist/ExplorEda.d.ts +124 -278
  8. package/dist/ExplorEda.js +4479 -11166
  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 +30 -52
@@ -0,0 +1,964 @@
1
+ import { useTraceRevision, useChartTrace, useChartTraceApi, useTraceSource, guideTargets, resolveGuideTrace } from './chunk-54M32JJB.js';
2
+ import { useColorScales } from './chunk-4HNDKMWF.js';
3
+ import { DEFAULT_CHART_SETTINGS } from './chunk-CJQQFNEY.js';
4
+ import { BaseChart, formatTick } from './chunk-UJWS6TRV.js';
5
+ import { buildScale, planChartMargin, numericScale, planAxes, getRangeFilterForField, describeScale, findAxisGuide } from './chunk-7PTJNIOD.js';
6
+ import { FieldSelector } from './chunk-SFV5LZPD.js';
7
+ import { Switch } from './chunk-Q7LK2UWH.js';
8
+ import { useGetLiveIds, useGetColumnDataForIds, useGetColumnData } from './chunk-ZD64SCZB.js';
9
+ import { applyFilter } from './chunk-YXPAWUXO.js';
10
+ import { Label } from './chunk-MJPDJ5AS.js';
11
+ import { useDataLayer, categoryIncludes, categoryEqual, displayAggregateValue, finiteNumber, finiteNumbers, categoryKey, categoryLabel, categoryValue, getChartAxisLabel, cn, isMissingValue, isNumberLike, calculateGroupedAggregate } from './chunk-JNO7OXCC.js';
12
+ import { ChartBarBig } from 'lucide-react';
13
+ import * as React from 'react';
14
+ import { useId, useState, useMemo, useCallback } from 'react';
15
+ import { scaleBand } from 'd3-scale';
16
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
17
+ import * as SliderPrimitive from '@radix-ui/react-slider';
18
+
19
+ // src/components/charts/BarChart/bins.ts
20
+ function numericBins(all, values, count) {
21
+ const low = Math.min(...all);
22
+ const high = Math.max(...all);
23
+ if (!Number.isFinite(low) || !Number.isFinite(high)) return [];
24
+ const min = low === high ? low - 0.5 : low;
25
+ const max = low === high ? high + 0.5 : high;
26
+ const size = Math.max(2, Math.min(100, Math.round(count)));
27
+ const step = (max - min) / size;
28
+ const bins = Array.from({ length: size }, (_, i) => {
29
+ const start = min + i * step;
30
+ const end = min + (i + 1) * step;
31
+ return {
32
+ label: `[${start}, ${end}${i === size - 1 ? "]" : ")"}`,
33
+ start,
34
+ end,
35
+ value: 0,
36
+ isNumeric: true
37
+ };
38
+ });
39
+ for (const value of values) {
40
+ if (!Number.isFinite(value) || value < min || value > max) continue;
41
+ bins[Math.min(size - 1, Math.floor((value - min) / step))].value++;
42
+ }
43
+ return bins;
44
+ }
45
+
46
+ // src/components/charts/BarChart/barPlan.ts
47
+ var X_SCALE_PADDING = 0.05;
48
+ var Y_SCALE_PADDING = 0.1;
49
+ var DEFAULT_FILL = "#3479a8";
50
+ var FILTERED_OUT_FILL = "rgb(156 163 175)";
51
+ function isNumericBarField(settings, snapshot) {
52
+ return !settings.aggregateId && snapshot.fieldType !== "categorical" && !settings.forceString && snapshot.allValues.some((value) => finiteNumber(value) !== void 0) && snapshot.allValues.filter((value) => !isMissingValue(value)).every(isNumberLike);
53
+ }
54
+ function countRows(settings, snapshot) {
55
+ const spec = {
56
+ id: `${settings.id}:count`,
57
+ name: `Count by ${settings.field}`,
58
+ groupField: settings.field,
59
+ aggregation: "count"
60
+ };
61
+ const result = calculateGroupedAggregate(
62
+ snapshot.liveIds.map((id) => ({
63
+ __ID: id,
64
+ [settings.field]: snapshot.fieldData[id]
65
+ })),
66
+ spec
67
+ );
68
+ const byKey = new Map(
69
+ result.rows.map((row) => [categoryKey(row.groupValue), row])
70
+ );
71
+ const categories = new Map(
72
+ snapshot.allValues.map((value) => [categoryKey(value), categoryValue(value)])
73
+ );
74
+ const rows = [...categories].map(
75
+ ([key, category]) => byKey.get(key) ?? {
76
+ id: `${spec.id}:${key}`,
77
+ groupValue: category,
78
+ groupLabel: categoryLabel(category),
79
+ value: 0,
80
+ rowCount: 0,
81
+ contributors: []
82
+ }
83
+ );
84
+ return { ...result, rows };
85
+ }
86
+ function binRows(settings, snapshot) {
87
+ const all = finiteNumbers(snapshot.allValues);
88
+ const live = snapshot.liveIds.flatMap((id) => {
89
+ const value = finiteNumber(snapshot.fieldData[id]);
90
+ return value === void 0 ? [] : [{ id, value }];
91
+ });
92
+ const bins = numericBins(
93
+ all,
94
+ live.map((item) => item.value),
95
+ settings.binCount || 20
96
+ );
97
+ const members = bins.map(() => []);
98
+ if (bins.length) {
99
+ const min = bins[0].start;
100
+ const max = bins[bins.length - 1].end;
101
+ const step = (max - min) / bins.length;
102
+ for (const item of live) {
103
+ if (item.value < min || item.value > max) continue;
104
+ members[Math.min(bins.length - 1, Math.floor((item.value - min) / step))].push(item);
105
+ }
106
+ }
107
+ return bins.map((bin, index) => ({
108
+ bin: { start: bin.start, end: bin.end },
109
+ id: `bin:${bin.start}:${bin.end}`,
110
+ groupValue: bin.start,
111
+ groupLabel: bin.label,
112
+ value: bin.value,
113
+ rowCount: members[index].length,
114
+ contributors: members[index].map((item) => ({
115
+ sourceId: item.id,
116
+ input: snapshot.fieldData[item.id],
117
+ included: true
118
+ }))
119
+ }));
120
+ }
121
+ function extremes(values) {
122
+ const lower = values.reduce(
123
+ (winner, item) => item.value < (winner?.value ?? 0) ? item : winner,
124
+ void 0
125
+ );
126
+ const upper = values.reduce(
127
+ (winner, item) => item.value > (winner?.value ?? 0) ? item : winner,
128
+ void 0
129
+ );
130
+ const end = (item) => item ? { source: "bar", id: item.id, label: item.label, value: item.value } : { source: "zero", value: 0 };
131
+ return { lower: end(lower), upper: end(upper) };
132
+ }
133
+ function planBarChart({
134
+ settings,
135
+ snapshot,
136
+ width,
137
+ height,
138
+ getColor,
139
+ getFieldLabel,
140
+ formatFieldValue,
141
+ aggregateScope = "This result uses the current globally filtered source rows"
142
+ }) {
143
+ const numeric = isNumericBarField(settings, snapshot);
144
+ const mode = settings.aggregateId ? "aggregate" : numeric ? "bin" : "count";
145
+ const spec = snapshot.aggregate?.spec;
146
+ const result = mode === "aggregate" ? snapshot.aggregate : mode === "count" ? countRows(settings, snapshot) : void 0;
147
+ const rows = mode === "bin" ? binRows(settings, snapshot) : result?.rows ?? [];
148
+ let domainValues;
149
+ if (mode === "aggregate") {
150
+ domainValues = rows.flatMap(
151
+ (row) => typeof row.value === "number" && Number.isFinite(row.value) ? [{ id: row.id, label: row.groupLabel, value: row.value }] : []
152
+ );
153
+ } else if (mode === "bin") {
154
+ const all = finiteNumbers(snapshot.allValues);
155
+ domainValues = numericBins(all, all, settings.binCount || 20).map(
156
+ (bin) => ({ id: `bin:${bin.start}:${bin.end}`, label: bin.label, value: bin.value })
157
+ );
158
+ } else {
159
+ const counts = /* @__PURE__ */ new Map();
160
+ for (const value of snapshot.allValues) {
161
+ const key = categoryKey(value);
162
+ const item = counts.get(key);
163
+ if (item) item.value += 1;
164
+ else
165
+ counts.set(key, {
166
+ id: `${settings.id}:count:${key}`,
167
+ label: categoryLabel(categoryValue(value)),
168
+ value: 1
169
+ });
170
+ }
171
+ domainValues = [...counts.values()];
172
+ }
173
+ const { lower, upper } = extremes(domainValues);
174
+ let yDomain;
175
+ let padding;
176
+ if (mode === "aggregate") {
177
+ const pad = lower.value === upper.value ? 0.5 : (upper.value - lower.value) * Y_SCALE_PADDING;
178
+ yDomain = [lower.value - (lower.value < 0 ? pad : 0), upper.value + pad];
179
+ padding = lower.value === upper.value ? "0.5 above and below" : "10% of the value span";
180
+ } else {
181
+ yDomain = [0, Math.max(1, upper.value * (1 + Y_SCALE_PADDING))];
182
+ padding = "10% above the tallest bar";
183
+ }
184
+ const xField = mode === "aggregate" && spec ? spec.groupField : settings.field;
185
+ const yField = mode === "aggregate" && spec && spec.aggregation !== "count" ? spec.measureField : void 0;
186
+ const xLabel = getChartAxisLabel(
187
+ xField,
188
+ settings.xAxisLabel,
189
+ getFieldLabel
190
+ );
191
+ const yLabel = settings.yAxisLabel || (mode === "aggregate" && spec ? spec.aggregation === "count" ? "Count" : getFieldLabel(spec.measureField ?? "Measure") : "");
192
+ const { margin, policy } = planChartMargin({
193
+ margin: settings.margin,
194
+ width,
195
+ yDomain,
196
+ hasXLabel: Boolean(xLabel),
197
+ hasYLabel: Boolean(yLabel)
198
+ });
199
+ const plotWidth = width - margin.left - margin.right;
200
+ const plotHeight = height - margin.top - margin.bottom;
201
+ let xScale;
202
+ let xDomainSource;
203
+ if (mode === "bin") {
204
+ const all = finiteNumbers(snapshot.allValues);
205
+ const low = Math.min(...all);
206
+ const high = Math.max(...all);
207
+ const min = low === high ? low - 0.5 : low;
208
+ const max = low === high ? high + 0.5 : high;
209
+ const pad = (max - min) * X_SCALE_PADDING;
210
+ xScale = numericScale(settings.xAxis).domain([min - pad, max + pad]).range([0, plotWidth]);
211
+ xDomainSource = {
212
+ population: "all source rows",
213
+ rows: all.length,
214
+ bounds: [low, high],
215
+ padding: "5% on each side"
216
+ };
217
+ } else {
218
+ const labels = mode === "aggregate" ? rows.map((row) => row.groupLabel) : Array.from(new Set(snapshot.allValues.map(categoryLabel)));
219
+ xScale = scaleBand().domain(labels).range([0, plotWidth]).padding(0.3);
220
+ }
221
+ const yScale = numericScale(settings.yAxis).domain(yDomain).range([plotHeight, 0]);
222
+ const baseline = yScale(0);
223
+ const format = (field) => (value) => field && formatFieldValue ? formatFieldValue(field, value) : formatTick(value);
224
+ const symlog = (label, type) => [label, type === "symlog" && "symlog"].filter(Boolean).join(" \xB7 ");
225
+ const axes = planAxes({
226
+ plotWidth,
227
+ plotHeight,
228
+ margin,
229
+ marginPolicy: policy,
230
+ x: {
231
+ scale: xScale,
232
+ scaleType: settings.xAxis.scaleType,
233
+ field: xField,
234
+ fieldLabel: xField ? getFieldLabel(xField) : void 0,
235
+ density: settings.xGridLines,
236
+ grid: settings.xAxis.grid,
237
+ format: format(xField),
238
+ label: symlog(xLabel, settings.xAxis.scaleType),
239
+ labelSource: settings.xAxisLabel ? "chart-setting" : "field-label",
240
+ domainSource: xDomainSource
241
+ },
242
+ y: {
243
+ scale: yScale,
244
+ scaleType: settings.yAxis.scaleType,
245
+ field: yField,
246
+ fieldLabel: yLabel || void 0,
247
+ density: settings.yGridLines,
248
+ grid: settings.yAxis.grid ?? true,
249
+ format: format(yField),
250
+ label: symlog(yLabel, settings.yAxis.scaleType),
251
+ labelSource: settings.yAxisLabel ? "chart-setting" : "field-label",
252
+ zero: true,
253
+ domainSource: {
254
+ population: mode === "aggregate" ? "the current aggregate result" : "all source rows",
255
+ rows: domainValues.length,
256
+ bounds: [lower.value, upper.value],
257
+ padding,
258
+ lower: lower.label ? { label: lower.label, value: lower.value } : void 0,
259
+ upper: upper.label ? { label: upper.label, value: upper.value } : void 0
260
+ }
261
+ }
262
+ });
263
+ const valueFilter = mode === "aggregate" ? void 0 : settings.filters.find(
264
+ (filter) => filter.type === "value" && filter.field === settings.field
265
+ );
266
+ const rangeFilter = mode === "bin" ? getRangeFilterForField(settings.filters, settings.field) : void 0;
267
+ const bars = [];
268
+ const unplotted = [];
269
+ rows.forEach((row, order) => {
270
+ if (typeof row.value !== "number" || !Number.isFinite(row.value)) {
271
+ unplotted.push({ row, reason: "No valid numbers" });
272
+ return;
273
+ }
274
+ let x;
275
+ let barWidth;
276
+ let bin;
277
+ if (row.bin) {
278
+ const linear = xScale;
279
+ const { start, end } = row.bin;
280
+ bin = { start, end, closed: order === rows.length - 1 };
281
+ x = linear(start);
282
+ const span = linear(end) - x;
283
+ if (span < 1) return;
284
+ barWidth = Math.max(0, span - 1);
285
+ } else {
286
+ const band = xScale;
287
+ const at = band(row.groupLabel);
288
+ if (at === void 0 || band.bandwidth() < 1) return;
289
+ x = at;
290
+ barWidth = band.bandwidth();
291
+ }
292
+ const valuePosition = yScale(row.value);
293
+ const baseFill = getColor(row.groupValue) || DEFAULT_FILL;
294
+ const passes = rangeFilter ? applyFilter(row.groupValue, rangeFilter) : valueFilter ? applyFilter(row.groupValue, valueFilter) : true;
295
+ bars.push({
296
+ id: `bar:${row.id}`,
297
+ order,
298
+ label: row.groupLabel,
299
+ groupValue: row.groupValue,
300
+ bin,
301
+ value: row.value,
302
+ x,
303
+ y: Math.min(baseline, valuePosition),
304
+ width: barWidth,
305
+ height: Math.max(1, Math.abs(baseline - valuePosition)),
306
+ baseline,
307
+ fill: passes ? baseFill : FILTERED_OUT_FILL,
308
+ fillSource: passes ? { kind: "color-scale", scaleId: settings.colorScaleId, value: row.groupValue } : {
309
+ kind: "own-filter",
310
+ scaleId: settings.colorScaleId,
311
+ value: row.groupValue,
312
+ baseFill
313
+ },
314
+ selected: mode === "count" && valueFilter ? applyFilter(row.groupValue, valueFilter) : void 0,
315
+ row
316
+ });
317
+ });
318
+ return {
319
+ revision: snapshot.revision,
320
+ mode,
321
+ width,
322
+ height,
323
+ field: xField,
324
+ fieldLabel: getFieldLabel(xField),
325
+ valueLabel: yLabel || "Row count",
326
+ aggregation: mode === "aggregate" && spec ? `${spec.aggregation}${spec.measureField ? ` of ${getFieldLabel(spec.measureField)}` : ""}` : "count",
327
+ axes,
328
+ xScale: describeScale(xScale, settings.xAxis.scaleType),
329
+ yScale: describeScale(yScale, settings.yAxis.scaleType),
330
+ bars,
331
+ groupOrder: rows.map((row) => row.groupLabel),
332
+ unplotted,
333
+ domain: {
334
+ population: mode === "aggregate" ? "current result" : "all source rows",
335
+ values: domainValues,
336
+ lower,
337
+ upper,
338
+ padding,
339
+ domain: yDomain
340
+ },
341
+ zeroBaseline: baseline,
342
+ scopeNote: mode === "aggregate" ? aggregateScope : mode === "count" ? "Rows after other chart filters; this chart's selected categories are shown in color" : "Rows after other chart filters; this chart's range is shown in color"
343
+ };
344
+ }
345
+ function findBarForRow(plan, id) {
346
+ return plan.bars.find(
347
+ (bar) => bar.row.contributors.some((contributor) => contributor.sourceId === id)
348
+ );
349
+ }
350
+ function barAt(plan, x, y) {
351
+ return plan.bars.find(
352
+ (bar) => x >= bar.x && x <= bar.x + bar.width && y >= bar.y && y <= bar.y + bar.height
353
+ );
354
+ }
355
+
356
+ // src/components/charts/BarChart/barTrace.ts
357
+ function resolveBarTrace(plan, kind, id) {
358
+ if (kind === "guide") return resolveGuideTrace(plan.axes, id, plan.revision);
359
+ if (kind !== "bar") return void 0;
360
+ const mark = plan.bars.find((bar) => bar.id === id);
361
+ if (!mark) return void 0;
362
+ return {
363
+ kind: "bar",
364
+ id,
365
+ revision: plan.revision,
366
+ mark,
367
+ mode: plan.mode,
368
+ field: plan.field,
369
+ fieldLabel: plan.fieldLabel,
370
+ valueLabel: plan.valueLabel,
371
+ aggregation: plan.aggregation,
372
+ groupOrder: plan.groupOrder,
373
+ domain: plan.domain,
374
+ xScale: plan.xScale,
375
+ yScale: plan.yScale,
376
+ scopeNote: plan.scopeNote
377
+ };
378
+ }
379
+ function barTraceTargets(plan) {
380
+ return [
381
+ ...plan.bars.map((bar) => ({
382
+ kind: "bar",
383
+ id: bar.id,
384
+ label: `Bar: ${bar.label}`
385
+ })),
386
+ ...guideTargets(plan.axes)
387
+ ];
388
+ }
389
+ function findBarTraceRow(plan, id) {
390
+ const bar = findBarForRow(plan, id);
391
+ return bar && { kind: "bar", id: bar.id };
392
+ }
393
+ function HoverReadout({ plan, id }) {
394
+ const bar = plan.bars.find((item) => item.id === id);
395
+ const guide = bar ? void 0 : findAxisGuide(plan.axes, id)?.guide;
396
+ if (!bar && !guide) return null;
397
+ return /* @__PURE__ */ jsx(
398
+ "div",
399
+ {
400
+ className: "pointer-events-none absolute left-2 top-2 max-w-[min(16rem,70%)] rounded border border-border bg-card/95 px-2 py-1 text-xs text-card-foreground shadow-sm",
401
+ role: "status",
402
+ children: bar ? /* @__PURE__ */ jsxs(Fragment, { children: [
403
+ /* @__PURE__ */ jsxs("div", { children: [
404
+ "Bar \xB7 ",
405
+ bar.label
406
+ ] }),
407
+ /* @__PURE__ */ jsx("div", { children: bar.bin ? `Bin interval: ${bar.bin.start} to ${bar.bin.end}` : `Value: ${bar.value}` })
408
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
409
+ /* @__PURE__ */ jsxs("div", { children: [
410
+ guide.axis === "x" ? "Horizontal" : "Vertical",
411
+ " ",
412
+ guide.role
413
+ ] }),
414
+ guide.value !== void 0 && /* @__PURE__ */ jsxs("div", { children: [
415
+ "Value: ",
416
+ String(guide.value)
417
+ ] }),
418
+ guide.role !== "zero" && guide.label && /* @__PURE__ */ jsxs("div", { children: [
419
+ "Label: ",
420
+ guide.label.fullText
421
+ ] })
422
+ ] })
423
+ }
424
+ );
425
+ }
426
+ function BarChart({
427
+ settings,
428
+ width,
429
+ height,
430
+ facetIds,
431
+ aggregateResult,
432
+ aggregateScope
433
+ }) {
434
+ const getAggregateResult = useDataLayer((s) => s.getAggregateResult);
435
+ const getFieldLabel = useDataLayer((s) => s.getFieldLabel);
436
+ const formatFieldValue = useDataLayer((s) => s.formatFieldValue);
437
+ const fieldSettings = useDataLayer((s) => s.fieldSettings);
438
+ const nonce = useDataLayer((s) => s.nonce);
439
+ const aggregates = useDataLayer((s) => s.aggregates);
440
+ const updateChart = useDataLayer((s) => s.updateChart);
441
+ const { getColorForValue } = useColorScales();
442
+ const liveIds = useGetLiveIds(settings, facetIds);
443
+ const allValues = useGetColumnDataForIds(settings.field);
444
+ const fieldData = useGetColumnData(settings.field);
445
+ const revision = useTraceRevision(settings);
446
+ const trace = useChartTrace();
447
+ const traceApi = useChartTraceApi();
448
+ const owner = useId();
449
+ const [hoveredId, setHoveredId] = useState(null);
450
+ const resolvedAggregate = useMemo(() => {
451
+ return settings.aggregateId ? getAggregateResult(settings.aggregateId, liveIds) : void 0;
452
+ }, [
453
+ settings.aggregateId,
454
+ getAggregateResult,
455
+ aggregates,
456
+ fieldSettings,
457
+ liveIds,
458
+ nonce
459
+ ]);
460
+ const plan = useMemo(
461
+ () => planBarChart({
462
+ settings,
463
+ width,
464
+ height,
465
+ snapshot: {
466
+ revision,
467
+ allValues,
468
+ liveIds,
469
+ fieldData,
470
+ fieldType: fieldSettings[settings.field]?.type,
471
+ aggregate: aggregateResult ?? resolvedAggregate
472
+ },
473
+ getColor: (value) => getColorForValue(settings.colorScaleId, value, "#3479a8"),
474
+ getFieldLabel,
475
+ formatFieldValue,
476
+ aggregateScope
477
+ }),
478
+ // Label and format getters are stable; field settings carry their changes.
479
+ // eslint-disable-next-line react-hooks/exhaustive-deps
480
+ [
481
+ settings,
482
+ width,
483
+ height,
484
+ revision,
485
+ allValues,
486
+ liveIds,
487
+ fieldData,
488
+ fieldSettings,
489
+ aggregateResult,
490
+ resolvedAggregate,
491
+ getColorForValue,
492
+ aggregateScope
493
+ ]
494
+ );
495
+ const xScale = useMemo(() => buildScale(plan.xScale), [plan.xScale]);
496
+ const yScale = useMemo(
497
+ () => buildScale(plan.yScale),
498
+ [plan.yScale]
499
+ );
500
+ const source = useMemo(
501
+ () => ({
502
+ role: "chart",
503
+ revision: plan.revision,
504
+ resolve: (kind, id) => resolveBarTrace(plan, kind, id),
505
+ findRow: (id) => findBarTraceRow(plan, id),
506
+ targets: () => barTraceTargets(plan)
507
+ }),
508
+ [plan]
509
+ );
510
+ useTraceSource(owner, source);
511
+ const inspect = useCallback(
512
+ (kind, id) => traceApi?.inspect(owner, kind, id),
513
+ [owner, traceApi]
514
+ );
515
+ const selected = trace?.selection?.owner === owner ? trace.selection : void 0;
516
+ const valueFilter = settings.filters.find(
517
+ (f) => f.type === "value" && f.field === settings.field
518
+ );
519
+ const toggleCategory = useCallback(
520
+ (label) => {
521
+ const filterValues = valueFilter?.values ?? [];
522
+ const newValues = categoryIncludes(filterValues, label) ? filterValues.filter((f) => !categoryEqual(f, label)) : [...filterValues, label];
523
+ const newFilters = settings.filters.filter(
524
+ (f) => f.type !== "value" || f.field !== settings.field
525
+ );
526
+ if (newValues.length > 0) {
527
+ newFilters.push({
528
+ type: "value",
529
+ field: settings.field,
530
+ values: newValues
531
+ });
532
+ }
533
+ updateChart(settings.id, { filters: newFilters });
534
+ },
535
+ [settings.id, settings.field, settings.filters, updateChart, valueFilter]
536
+ );
537
+ const handleBrushChange = useCallback(
538
+ (extent) => {
539
+ const newFilters = settings.filters.filter(
540
+ (f) => f.field !== settings.field
541
+ );
542
+ if (extent && plan.mode === "bin") {
543
+ const linear = xScale;
544
+ newFilters.push({
545
+ type: "range",
546
+ field: settings.field,
547
+ min: linear.invert(extent[0][0]),
548
+ max: linear.invert(extent[1][0])
549
+ });
550
+ } else if (extent) {
551
+ return;
552
+ }
553
+ updateChart(settings.id, { filters: newFilters });
554
+ },
555
+ [plan.mode, settings.field, settings.filters, settings.id, updateChart, xScale]
556
+ );
557
+ const isCount = plan.mode === "count";
558
+ const isAggregate = plan.mode === "aggregate";
559
+ return /* @__PURE__ */ jsxs("div", { className: "relative", style: { width, height }, children: [
560
+ /* @__PURE__ */ jsx(
561
+ BaseChart,
562
+ {
563
+ width,
564
+ height,
565
+ xScale,
566
+ yScale,
567
+ axes: plan.axes,
568
+ brushingMode: plan.mode === "bin" ? "horizontal" : "none",
569
+ onBrushChange: handleBrushChange,
570
+ settings,
571
+ onInspectGuide: (id) => inspect("guide", id),
572
+ onHoverTarget: setHoveredId,
573
+ onInspectPlot: ([x, y]) => {
574
+ const bar = barAt(plan, x, y);
575
+ return bar ? Boolean(inspect("bar", bar.id)) : false;
576
+ },
577
+ activeGuideId: selected?.kind === "guide" ? selected.id : hoveredId,
578
+ children: /* @__PURE__ */ jsx("g", { children: plan.bars.map((bar) => {
579
+ const measureLabel = isAggregate ? displayAggregateValue(bar.value) : `${bar.value} records`;
580
+ const clickable = isCount || plan.mode === "bin";
581
+ return /* @__PURE__ */ jsx(
582
+ "rect",
583
+ {
584
+ "data-plan-id": bar.id,
585
+ "data-aggregate-row-id": bar.row.id,
586
+ x: bar.x,
587
+ y: bar.y,
588
+ width: bar.width,
589
+ height: bar.height,
590
+ rx: 1.5,
591
+ role: "button",
592
+ tabIndex: 0,
593
+ "aria-label": `${bar.label}: ${measureLabel}`,
594
+ "aria-pressed": bar.selected,
595
+ className: `chart-mark ${clickable ? "cursor-pointer" : ""}`,
596
+ style: {
597
+ fill: bar.fill,
598
+ ...selected?.id === bar.id ? { stroke: "var(--primary)", strokeWidth: 2 } : {}
599
+ },
600
+ onKeyDown: (event) => {
601
+ if (event.key !== "Enter" && event.key !== " ") return;
602
+ if (event.altKey && event.key === "Enter") {
603
+ event.preventDefault();
604
+ inspect("bar", bar.id);
605
+ } else if (isCount && !event.altKey) {
606
+ event.preventDefault();
607
+ toggleCategory(bar.groupValue);
608
+ }
609
+ },
610
+ onClick: (event) => {
611
+ if (event.altKey) {
612
+ event.preventDefault();
613
+ event.stopPropagation();
614
+ inspect("bar", bar.id);
615
+ } else if (isCount) {
616
+ toggleCategory(bar.groupValue);
617
+ }
618
+ }
619
+ },
620
+ bar.id
621
+ );
622
+ }) })
623
+ }
624
+ ),
625
+ hoveredId && /* @__PURE__ */ jsx(HoverReadout, { plan, id: hoveredId })
626
+ ] });
627
+ }
628
+ function Slider({
629
+ className,
630
+ defaultValue,
631
+ value,
632
+ min = 0,
633
+ max = 100,
634
+ ...props
635
+ }) {
636
+ const _values = React.useMemo(
637
+ () => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max],
638
+ [value, defaultValue, min, max]
639
+ );
640
+ return /* @__PURE__ */ jsxs(
641
+ SliderPrimitive.Root,
642
+ {
643
+ "data-slot": "slider",
644
+ defaultValue,
645
+ value,
646
+ min,
647
+ max,
648
+ className: cn(
649
+ "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
650
+ className
651
+ ),
652
+ ...props,
653
+ children: [
654
+ /* @__PURE__ */ jsx(
655
+ SliderPrimitive.Track,
656
+ {
657
+ "data-slot": "slider-track",
658
+ className: cn(
659
+ "bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
660
+ ),
661
+ children: /* @__PURE__ */ jsx(
662
+ SliderPrimitive.Range,
663
+ {
664
+ "data-slot": "slider-range",
665
+ className: cn(
666
+ "bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
667
+ )
668
+ }
669
+ )
670
+ }
671
+ ),
672
+ Array.from({ length: _values.length }, (_, index) => /* @__PURE__ */ jsx(
673
+ SliderPrimitive.Thumb,
674
+ {
675
+ "data-slot": "slider-thumb",
676
+ className: "border-primary bg-background ring-ring/20 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
677
+ },
678
+ index
679
+ ))
680
+ ]
681
+ }
682
+ );
683
+ }
684
+ function getAggregateName(aggregation, groupField, measureField) {
685
+ if (aggregation === "count") {
686
+ return `Count by ${groupField}`;
687
+ }
688
+ return `${aggregation === "sum" ? "Sum" : "Average"} of ${measureField ?? ""} by ${groupField}`;
689
+ }
690
+ function BarChartSettingsPanel({
691
+ settings,
692
+ onSettingsChange
693
+ }) {
694
+ const { getOrCreateScaleForField } = useColorScales();
695
+ const aggregate = useDataLayer(
696
+ (state) => settings.aggregateId ? state.getAggregate(settings.aggregateId) : void 0
697
+ );
698
+ const addAggregate = useDataLayer((state) => state.addAggregate);
699
+ const updateAggregate = useDataLayer((state) => state.updateAggregate);
700
+ const getColumnNames = useDataLayer((state) => state.getColumnNames);
701
+ const getColumnData = useDataLayer((state) => state.getColumnData);
702
+ const calculations = useDataLayer((state) => state.calculations);
703
+ const fieldProfiles = useDataLayer((state) => state.fieldProfiles);
704
+ const fieldNames = useMemo(() => {
705
+ return getColumnNames();
706
+ }, [getColumnNames, calculations]);
707
+ const measureFields = useMemo(() => {
708
+ const numeric = fieldNames.filter(
709
+ (field) => fieldProfiles.some(
710
+ (profile) => profile.name === field && profile.dataType === "numeric"
711
+ )
712
+ );
713
+ calculations.forEach((calculation) => {
714
+ const values = Object.values(getColumnData(calculation.resultColumnName));
715
+ if (values.some((value) => finiteNumber(value) !== void 0)) {
716
+ numeric.push(calculation.resultColumnName);
717
+ }
718
+ });
719
+ if (aggregate?.measureField && !numeric.includes(aggregate.measureField)) {
720
+ numeric.unshift(aggregate.measureField);
721
+ }
722
+ return numeric;
723
+ }, [
724
+ aggregate?.measureField,
725
+ calculations,
726
+ fieldNames,
727
+ fieldProfiles,
728
+ getColumnData
729
+ ]);
730
+ const [error, setError] = useState();
731
+ const createAggregate = (aggregation) => {
732
+ const measureField = measureFields[0];
733
+ if (aggregation !== "count" && !measureField) {
734
+ setError("Sum and average need a numeric measure field.");
735
+ return;
736
+ }
737
+ try {
738
+ const aggregate2 = addAggregate({
739
+ name: getAggregateName(aggregation, settings.field, measureField),
740
+ groupField: settings.field,
741
+ aggregation,
742
+ ...aggregation === "count" ? {} : { measureField }
743
+ });
744
+ onSettingsChange({
745
+ ...settings,
746
+ field: aggregate2.measureField ?? aggregate2.groupField,
747
+ aggregateId: aggregate2.id,
748
+ title: aggregate2.name,
749
+ xAxisLabel: "",
750
+ yAxisLabel: ""
751
+ });
752
+ setError(void 0);
753
+ } catch (cause) {
754
+ setError(
755
+ cause instanceof Error ? cause.message : "Could not create grouped bar"
756
+ );
757
+ }
758
+ };
759
+ if (settings.aggregateId) {
760
+ if (!aggregate) {
761
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: "Bar aggregation not found." });
762
+ }
763
+ const updateDefinition = (updates) => {
764
+ try {
765
+ const next = { ...aggregate, ...updates };
766
+ const name = getAggregateName(
767
+ next.aggregation,
768
+ next.groupField,
769
+ next.measureField
770
+ );
771
+ updateAggregate(settings.aggregateId, { ...updates, name });
772
+ onSettingsChange({
773
+ ...settings,
774
+ field: next.aggregation === "count" ? next.groupField : next.measureField ?? next.groupField,
775
+ ...settings.title === aggregate.name ? { title: name } : {}
776
+ });
777
+ setError(void 0);
778
+ } catch (cause) {
779
+ setError(
780
+ cause instanceof Error ? cause.message : "Could not update grouped summary"
781
+ );
782
+ }
783
+ };
784
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
785
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: error }),
786
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
787
+ /* @__PURE__ */ jsx(Label, { children: "Group by" }),
788
+ /* @__PURE__ */ jsx(
789
+ FieldSelector,
790
+ {
791
+ label: "",
792
+ placeholder: "Group by",
793
+ value: aggregate.groupField,
794
+ onChange: (value) => updateDefinition({ groupField: value })
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsx(Label, { htmlFor: "bar-operation", children: "Operation" }),
798
+ /* @__PURE__ */ jsxs(
799
+ "select",
800
+ {
801
+ id: "bar-operation",
802
+ className: "h-9 rounded-md border-input bg-background px-2 text-sm",
803
+ value: aggregate.aggregation,
804
+ onChange: (event) => {
805
+ if (event.target.value === "category") {
806
+ onSettingsChange({
807
+ ...settings,
808
+ field: aggregate.groupField,
809
+ aggregateId: void 0,
810
+ ...settings.title === aggregate.name ? { title: "" } : {}
811
+ });
812
+ return;
813
+ }
814
+ updateDefinition({
815
+ aggregation: event.target.value,
816
+ ...event.target.value !== "count" && !aggregate.measureField ? { measureField: measureFields[0] } : {}
817
+ });
818
+ },
819
+ children: [
820
+ /* @__PURE__ */ jsx("option", { value: "category", children: "Category counts / bins" }),
821
+ /* @__PURE__ */ jsx("option", { value: "count", children: "Count rows" }),
822
+ /* @__PURE__ */ jsx("option", { value: "sum", disabled: !measureFields.length, children: "Sum" }),
823
+ /* @__PURE__ */ jsx("option", { value: "average", disabled: !measureFields.length, children: "Average" })
824
+ ]
825
+ }
826
+ ),
827
+ aggregate.aggregation !== "count" && /* @__PURE__ */ jsxs(Fragment, { children: [
828
+ /* @__PURE__ */ jsx(Label, { children: "Measure" }),
829
+ /* @__PURE__ */ jsx(
830
+ FieldSelector,
831
+ {
832
+ label: "",
833
+ placeholder: "Measure",
834
+ value: aggregate.measureField ?? "",
835
+ fields: measureFields,
836
+ onChange: (value) => updateDefinition({ measureField: value })
837
+ }
838
+ )
839
+ ] })
840
+ ] })
841
+ ] });
842
+ }
843
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
844
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: error }),
845
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center gap-4", children: [
846
+ /* @__PURE__ */ jsx(Label, { children: "Group by" }),
847
+ /* @__PURE__ */ jsx(
848
+ FieldSelector,
849
+ {
850
+ label: "",
851
+ placeholder: "Group by",
852
+ value: settings.field,
853
+ onChange: (value) => onSettingsChange({ ...settings, field: value })
854
+ }
855
+ ),
856
+ /* @__PURE__ */ jsx(Label, { htmlFor: "bar-operation", children: "Operation" }),
857
+ /* @__PURE__ */ jsxs(
858
+ "select",
859
+ {
860
+ id: "bar-operation",
861
+ className: "h-9 rounded-md border-input bg-background px-2 text-sm",
862
+ defaultValue: "category",
863
+ onChange: (event) => {
864
+ if (event.target.value !== "category") {
865
+ createAggregate(event.target.value);
866
+ }
867
+ },
868
+ children: [
869
+ /* @__PURE__ */ jsx("option", { value: "category", children: "Count rows" }),
870
+ /* @__PURE__ */ jsx("option", { value: "sum", disabled: !measureFields.length, children: "Sum" }),
871
+ /* @__PURE__ */ jsx("option", { value: "average", disabled: !measureFields.length, children: "Average" })
872
+ ]
873
+ }
874
+ ),
875
+ /* @__PURE__ */ jsxs(Label, { children: [
876
+ "Bins \xB7 ",
877
+ settings.binCount ?? 10
878
+ ] }),
879
+ /* @__PURE__ */ jsx(
880
+ Slider,
881
+ {
882
+ "aria-label": "Histogram bin count",
883
+ value: [settings.binCount ?? 10],
884
+ min: 2,
885
+ max: 50,
886
+ step: 1,
887
+ onValueChange: ([value]) => onSettingsChange({ ...settings, binCount: value })
888
+ }
889
+ ),
890
+ /* @__PURE__ */ jsx("div", { className: "col-start-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
891
+ /* @__PURE__ */ jsx(
892
+ Switch,
893
+ {
894
+ id: "forceString",
895
+ checked: settings.forceString,
896
+ onCheckedChange: (checked) => onSettingsChange({
897
+ ...settings,
898
+ forceString: checked
899
+ })
900
+ }
901
+ ),
902
+ /* @__PURE__ */ jsx(Label, { htmlFor: "forceString", children: "Treat values as categories" })
903
+ ] }) }),
904
+ /* @__PURE__ */ jsx("div", { className: "col-start-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
905
+ /* @__PURE__ */ jsx(
906
+ Switch,
907
+ {
908
+ id: "colorField",
909
+ checked: settings.field === settings.colorField,
910
+ onCheckedChange: (checked) => onSettingsChange({
911
+ ...settings,
912
+ colorField: checked ? settings.field : void 0,
913
+ colorScaleId: checked && settings.field ? getOrCreateScaleForField(settings.field) : void 0
914
+ })
915
+ }
916
+ ),
917
+ /* @__PURE__ */ jsx(Label, { htmlFor: "colorField", children: "Use as color field" })
918
+ ] }) })
919
+ ] })
920
+ ] });
921
+ }
922
+
923
+ // src/components/charts/BarChart/definition.ts
924
+ var barChartDefinition = {
925
+ type: "bar",
926
+ name: "Bar Chart",
927
+ description: "Display data as vertical bars",
928
+ icon: ChartBarBig,
929
+ component: BarChart,
930
+ settingsPanel: BarChartSettingsPanel,
931
+ createDefaultSettings: (layout, field) => ({
932
+ ...DEFAULT_CHART_SETTINGS,
933
+ id: crypto.randomUUID(),
934
+ field: field ?? "",
935
+ binCount: 10,
936
+ type: "bar",
937
+ title: "Bar Chart",
938
+ layout,
939
+ margin: { top: 20, right: 20, bottom: 30, left: 60 },
940
+ filters: []
941
+ }),
942
+ validateSettings: (settings) => {
943
+ return !!settings.field;
944
+ },
945
+ getFilterFunction: (settings, fieldGetter) => {
946
+ const filter = settings.aggregateId ? [...settings.filters].reverse().find(
947
+ (f) => f.type === "range" || f.type === "date-range"
948
+ ) : settings.filters.find((f) => f.field === settings.field);
949
+ const dataHash = fieldGetter(filter?.field ?? settings.field);
950
+ return (d) => {
951
+ const value = dataHash[d];
952
+ if (filter) {
953
+ if (!applyFilter(value, filter)) {
954
+ return false;
955
+ }
956
+ }
957
+ return true;
958
+ };
959
+ }
960
+ };
961
+
962
+ export { barChartDefinition };
963
+ //# sourceMappingURL=chunk-DHYLSXNR.js.map
964
+ //# sourceMappingURL=chunk-DHYLSXNR.js.map