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,739 @@
1
+ import { FieldInspector } from './chunk-LBTZVERO.js';
2
+ import { DEFAULT_CHART_SETTINGS } from './chunk-CJQQFNEY.js';
3
+ import { FieldMetadata, CalculatedFieldBadge } from './chunk-NDF5LCST.js';
4
+ import { useDataLayer, emptyFieldProfile, buildFieldProfile, ActionTooltip, Button, categoryLabel, getChartSummary, cn, DISTRIBUTION_BINS, binValues, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from './chunk-JNO7OXCC.js';
5
+ import { getChartDefinition } from './chunk-TLLRW6S3.js';
6
+ import { Info, Download, Settings2, ArrowUpDown, ArrowUp, ArrowDown, BarChart, ScatterChart, Table2 } from 'lucide-react';
7
+ import { useState, useMemo } from 'react';
8
+ import { createPortal } from 'react-dom';
9
+ import { toast } from 'sonner';
10
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
+
12
+ // src/hooks/useCreateCharts.ts
13
+ function useCreateCharts() {
14
+ const profiles = useDataLayer((s) => s.fieldProfiles);
15
+ const charts = useDataLayer((s) => s.charts);
16
+ const addChart = useDataLayer((s) => s.addChart);
17
+ const createChart = (type, field, initialLayout) => {
18
+ const layout = initialLayout ?? {
19
+ x: 0,
20
+ y: Math.max(0, ...charts.map((chart) => chart.layout.y + chart.layout.h)),
21
+ w: 6,
22
+ h: 4
23
+ };
24
+ const definition = getChartDefinition(type);
25
+ if (!definition) {
26
+ throw new Error(`Chart type ${type} not registered`);
27
+ }
28
+ const numeric = profiles.filter((profile) => profile.dataType === "numeric").map((profile) => profile.name);
29
+ const categories = profiles.filter((profile) => profile.dataType !== "numeric").map((profile) => profile.name);
30
+ const selectedField = field || (type === "row" ? categories[0] : numeric[0]) || profiles[0]?.name || "";
31
+ const settings = definition.createDefaultSettings(layout, selectedField);
32
+ if (settings.type === "scatter" || settings.type === "3d-scatter") {
33
+ settings.xField = numeric[0] || "__ID";
34
+ settings.yField = field || numeric[1] || numeric[0] || "__ID";
35
+ settings.xAxisLabel = "";
36
+ settings.yAxisLabel = "";
37
+ if (settings.type === "3d-scatter")
38
+ settings.zField = numeric[2] || numeric[0] || "__ID";
39
+ }
40
+ if (settings.type === "line") {
41
+ settings.xField = "__ID";
42
+ settings.seriesField = selectedField ? [selectedField] : [];
43
+ settings.xAxisLabel = "";
44
+ settings.yAxisLabel = "";
45
+ }
46
+ if (settings.type === "data-table")
47
+ settings.columns = profiles.slice(0, 8).map((profile) => ({ id: profile.name, field: profile.name }));
48
+ if (settings.type === "pivot") {
49
+ settings.rowFields = categories.slice(0, 1);
50
+ settings.columnField = categories[1] || "";
51
+ settings.valueFields = selectedField ? [{ field: selectedField, aggregation: "count" }] : [];
52
+ }
53
+ if (settings.type === "color-legend")
54
+ settings.fields = categories.slice(0, 1);
55
+ if (settings.type === "bar" || settings.type === "row") {
56
+ settings.xAxisLabel = settings.type === "row" ? "Records" : "";
57
+ settings.yAxisLabel = settings.type === "bar" ? "Records" : "";
58
+ }
59
+ if (settings.type === "boxplot") settings.yAxisLabel = "";
60
+ settings.title = "";
61
+ addChart(settings);
62
+ };
63
+ return { createChart };
64
+ }
65
+ function ChartActions({ columnName, dataType }) {
66
+ const { createChart } = useCreateCharts();
67
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
68
+ dataType === "categorical" && /* @__PURE__ */ jsx(ActionTooltip, { content: "Create row chart", children: /* @__PURE__ */ jsx(
69
+ Button,
70
+ {
71
+ variant: "ghost",
72
+ size: "icon",
73
+ className: "h-6 w-6",
74
+ "aria-label": `Create row chart for ${columnName}`,
75
+ onClick: () => createChart("row", columnName),
76
+ children: /* @__PURE__ */ jsx(BarChart, { className: "h-4 w-4 rotate-90" })
77
+ }
78
+ ) }),
79
+ dataType === "numeric" && /* @__PURE__ */ jsxs(Fragment, { children: [
80
+ /* @__PURE__ */ jsx(ActionTooltip, { content: "Create bar chart", children: /* @__PURE__ */ jsx(
81
+ Button,
82
+ {
83
+ variant: "ghost",
84
+ size: "icon",
85
+ className: "h-6 w-6",
86
+ "aria-label": `Create bar chart for ${columnName}`,
87
+ onClick: () => createChart("bar", columnName),
88
+ children: /* @__PURE__ */ jsx(BarChart, { className: "h-4 w-4" })
89
+ }
90
+ ) }),
91
+ /* @__PURE__ */ jsx(ActionTooltip, { content: "Create scatter plot", children: /* @__PURE__ */ jsx(
92
+ Button,
93
+ {
94
+ variant: "ghost",
95
+ size: "icon",
96
+ className: "h-6 w-6",
97
+ "aria-label": `Create scatter chart for ${columnName}`,
98
+ onClick: () => createChart("scatter", columnName),
99
+ children: /* @__PURE__ */ jsx(ScatterChart, { className: "h-4 w-4" })
100
+ }
101
+ ) })
102
+ ] }),
103
+ (dataType === "categorical" || dataType === "datetime") && /* @__PURE__ */ jsx(ActionTooltip, { content: "Create pivot table", children: /* @__PURE__ */ jsx(
104
+ Button,
105
+ {
106
+ variant: "ghost",
107
+ size: "icon",
108
+ className: "h-6 w-6",
109
+ "aria-label": `Create pivot table for ${columnName}`,
110
+ onClick: () => createChart("pivot", columnName),
111
+ children: /* @__PURE__ */ jsx(Table2, { className: "h-4 w-4" })
112
+ }
113
+ ) })
114
+ ] });
115
+ }
116
+ var WIDTH = 96;
117
+ var HEIGHT = 20;
118
+ var SHARE_SEGMENTS = 5;
119
+ var OTHER_LABEL = "Other values";
120
+ var percent = (share) => share > 0 && share < 0.01 ? "<1%" : `${Math.round(share * 100)}%`;
121
+ function SparkDetails({
122
+ bars,
123
+ total,
124
+ fieldLabel,
125
+ locate,
126
+ children
127
+ }) {
128
+ const [active, setActive] = useState();
129
+ const bar = active === void 0 ? void 0 : bars[active];
130
+ const onPointerMove = (event) => {
131
+ const box = event.currentTarget.getBoundingClientRect();
132
+ if (box.width <= 0) return;
133
+ const fraction = (event.clientX - box.left) / box.width;
134
+ const index = locate(Math.min(0.9999, Math.max(0, fraction)));
135
+ setActive(index >= 0 && index < bars.length ? index : void 0);
136
+ };
137
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { open: bar !== void 0, children: [
138
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
139
+ "span",
140
+ {
141
+ className: "eda-summary-spark-hit",
142
+ "aria-hidden": "true",
143
+ onPointerMove,
144
+ onPointerLeave: () => setActive(void 0),
145
+ children: children(active)
146
+ }
147
+ ) }),
148
+ bar && /* @__PURE__ */ jsxs(TooltipContent, { side: "top", collisionPadding: 12, children: [
149
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: fieldLabel }),
150
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: bar.label }),
151
+ /* @__PURE__ */ jsxs("p", { className: "tabular-nums", children: [
152
+ bar.count.toLocaleString(),
153
+ " ",
154
+ bar.count === 1 ? "row" : "rows",
155
+ total > 0 && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
156
+ " ",
157
+ "\xB7 ",
158
+ percent(bar.count / total)
159
+ ] })
160
+ ] })
161
+ ] })
162
+ ] }) });
163
+ }
164
+ function Histogram({ bars, total, fieldLabel }) {
165
+ const peak = Math.max(...bars.map((bar) => bar.count), 1);
166
+ const step = WIDTH / Math.max(1, bars.length);
167
+ const gap = bars.length > 1 ? Math.min(1, step * 0.2) : 0;
168
+ return /* @__PURE__ */ jsx(
169
+ SparkDetails,
170
+ {
171
+ bars,
172
+ total,
173
+ fieldLabel,
174
+ locate: (fraction) => Math.floor(fraction * bars.length),
175
+ children: (active) => /* @__PURE__ */ jsxs(
176
+ "svg",
177
+ {
178
+ className: "eda-summary-spark",
179
+ viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
180
+ preserveAspectRatio: "none",
181
+ focusable: "false",
182
+ children: [
183
+ active !== void 0 && /* @__PURE__ */ jsx(
184
+ "rect",
185
+ {
186
+ className: "eda-summary-spark-focus",
187
+ x: active * step,
188
+ y: 0,
189
+ width: step,
190
+ height: HEIGHT
191
+ }
192
+ ),
193
+ /* @__PURE__ */ jsx(
194
+ "line",
195
+ {
196
+ className: "eda-summary-spark-base",
197
+ x1: 0,
198
+ x2: WIDTH,
199
+ y1: HEIGHT - 0.5,
200
+ y2: HEIGHT - 0.5
201
+ }
202
+ ),
203
+ bars.map(({ count }, index) => {
204
+ if (count === 0) return null;
205
+ const height = Math.max(1.5, count / peak * (HEIGHT - 1));
206
+ return /* @__PURE__ */ jsx(
207
+ "rect",
208
+ {
209
+ className: "eda-summary-spark-bar",
210
+ "data-active": index === active || void 0,
211
+ x: index * step + gap / 2,
212
+ y: HEIGHT - height,
213
+ width: Math.max(0.5, step - gap),
214
+ height
215
+ },
216
+ index
217
+ );
218
+ })
219
+ ]
220
+ }
221
+ )
222
+ }
223
+ );
224
+ }
225
+ function ShareBar({ bars, total, fieldLabel }) {
226
+ const shares = bars.map((bar) => total > 0 ? bar.count / total : 0);
227
+ const starts = shares.map(
228
+ (_, index) => shares.slice(0, index).reduce((sum, share) => sum + share, 0)
229
+ );
230
+ return /* @__PURE__ */ jsx(
231
+ SparkDetails,
232
+ {
233
+ bars,
234
+ total,
235
+ fieldLabel,
236
+ locate: (fraction) => starts.findIndex(
237
+ (start, index) => fraction >= start && fraction < start + shares[index]
238
+ ),
239
+ children: (active) => /* @__PURE__ */ jsx(
240
+ "svg",
241
+ {
242
+ className: "eda-summary-spark",
243
+ viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
244
+ preserveAspectRatio: "none",
245
+ focusable: "false",
246
+ children: bars.map((bar, index) => /* @__PURE__ */ jsx(
247
+ "rect",
248
+ {
249
+ className: bar.muted ? "eda-summary-share-rest" : "eda-summary-share",
250
+ "data-rank": index,
251
+ "data-active": index === active || void 0,
252
+ x: starts[index] * WIDTH,
253
+ y: 5,
254
+ width: Math.max(0.5, shares[index] * WIDTH - 1),
255
+ height: HEIGHT - 10
256
+ },
257
+ index
258
+ ))
259
+ }
260
+ )
261
+ }
262
+ );
263
+ }
264
+ function dateBins(profile) {
265
+ const times = [];
266
+ let first;
267
+ let last;
268
+ for (const { value, count } of profile.categories?.distribution ?? []) {
269
+ const time = Date.parse(String(value));
270
+ if (!Number.isFinite(time)) continue;
271
+ for (let i = 0; i < count; i += 1) times.push(time);
272
+ if (!first || time < first.time) first = { time, value };
273
+ if (!last || time > last.time) last = { time, value };
274
+ }
275
+ if (!first || !last) return void 0;
276
+ times.sort((a, b) => a - b);
277
+ return {
278
+ first: first.value,
279
+ last: last.value,
280
+ firstTime: first.time,
281
+ lastTime: last.time,
282
+ bins: binValues(times, first.time, last.time)
283
+ };
284
+ }
285
+ function labelBins(bins, min, max, label) {
286
+ const oneBinPerValue = bins.length === 1 || bins.length < DISTRIBUTION_BINS && Number.isInteger(min) && bins.length === max - min + 1;
287
+ const width2 = (max - min) / Math.max(1, bins.length);
288
+ return bins.map((count, index) => {
289
+ if (oneBinPerValue) return { label: label(min + index), count };
290
+ const start = label(min + index * width2);
291
+ const end = label(
292
+ index === bins.length - 1 ? max : min + (index + 1) * width2
293
+ );
294
+ return { label: start === end ? start : `${start} \u2013 ${end}`, count };
295
+ });
296
+ }
297
+ var isoDate = (time) => new Date(time).toISOString().slice(0, 10);
298
+ function summarizeField(profile, format, fieldLabel = profile.name) {
299
+ const present = profile.totalCount - profile.nullCount - (profile.excludedCount ?? 0);
300
+ if (present <= 0) return void 0;
301
+ if (profile.statistics) {
302
+ const { min, max, median, bins = [] } = profile.statistics;
303
+ const low = format(min);
304
+ const high = format(max);
305
+ const bars2 = labelBins(bins, min, max, format);
306
+ const graphic = /* @__PURE__ */ jsx(Histogram, { bars: bars2, total: present, fieldLabel });
307
+ if (min === max) {
308
+ return { graphic, bars: bars2, low, description: `Every value is ${low}` };
309
+ }
310
+ return {
311
+ graphic,
312
+ bars: bars2,
313
+ low,
314
+ high,
315
+ statLabel: "median",
316
+ stat: format(median),
317
+ description: `Range ${low} to ${high}, median ${format(median)}`
318
+ };
319
+ }
320
+ if (profile.dataType === "datetime") {
321
+ const dates = dateBins(profile);
322
+ if (dates) {
323
+ const first = format(dates.first);
324
+ const last = format(dates.last);
325
+ const bars2 = labelBins(
326
+ dates.bins,
327
+ dates.firstTime,
328
+ dates.lastTime,
329
+ isoDate
330
+ );
331
+ return {
332
+ graphic: /* @__PURE__ */ jsx(Histogram, { bars: bars2, total: present, fieldLabel }),
333
+ bars: bars2,
334
+ low: first,
335
+ high: first === last ? void 0 : last,
336
+ description: `Dates from ${first} to ${last}`
337
+ };
338
+ }
339
+ }
340
+ const top = profile.categories?.topValues ?? [];
341
+ if (!top[0]) return void 0;
342
+ if (profile.uniqueCount === present && present > 1) {
343
+ const bars2 = [
344
+ { label: "Each value appears once", count: present, muted: true }
345
+ ];
346
+ return {
347
+ graphic: /* @__PURE__ */ jsx(ShareBar, { bars: bars2, total: present, fieldLabel }),
348
+ bars: bars2,
349
+ label: "All values unique",
350
+ description: `Every value is unique, for example ${categoryLabel(top[0].value)}`
351
+ };
352
+ }
353
+ const bars = top.slice(0, SHARE_SEGMENTS).map(({ value, count }) => ({ label: categoryLabel(value), count }));
354
+ const rest = present - bars.reduce((sum, bar) => sum + bar.count, 0);
355
+ if (rest > 0) bars.push({ label: OTHER_LABEL, count: rest, muted: true });
356
+ const leader = categoryLabel(top[0].value);
357
+ const share = percent(top[0].count / present);
358
+ return {
359
+ graphic: /* @__PURE__ */ jsx(ShareBar, { bars, total: present, fieldLabel }),
360
+ bars,
361
+ label: leader,
362
+ statLabel: "share",
363
+ stat: share,
364
+ description: `Most common value ${leader}, ${share} of rows with a value`
365
+ };
366
+ }
367
+ var MAX_TRACK = 16;
368
+ var width = (values) => Math.min(
369
+ MAX_TRACK,
370
+ Math.max(0, ...values.map((value) => value?.length ?? 0))
371
+ );
372
+ function readingColumns(summaries) {
373
+ const present = summaries.filter(
374
+ (summary) => summary !== void 0
375
+ );
376
+ const measuredLow = width(present.map((summary) => summary.low));
377
+ const high = width(present.map((summary) => summary.high));
378
+ const label = width(present.map((summary) => summary.label));
379
+ const low = measuredLow === 0 && high === 0 ? label : measuredLow;
380
+ return {
381
+ "--eda-summary-low": `${low}ch`,
382
+ "--eda-summary-high": `${high}ch`,
383
+ "--eda-summary-stat-label": `${width(present.map((summary) => summary.statLabel))}ch`,
384
+ "--eda-summary-stat": `${width(present.map((summary) => summary.stat))}ch`
385
+ };
386
+ }
387
+ function SortHeader({
388
+ column,
389
+ label,
390
+ sort,
391
+ onSort,
392
+ className
393
+ }) {
394
+ const active = sort?.column === column;
395
+ const Icon = !active ? ArrowUpDown : sort.direction === "asc" ? ArrowUp : ArrowDown;
396
+ return /* @__PURE__ */ jsx(
397
+ "th",
398
+ {
399
+ scope: "col",
400
+ className,
401
+ "aria-sort": active ? sort.direction === "asc" ? "ascending" : "descending" : "none",
402
+ children: /* @__PURE__ */ jsxs(
403
+ "button",
404
+ {
405
+ type: "button",
406
+ className: "eda-summary-sort",
407
+ "data-active": active || void 0,
408
+ onClick: () => onSort(column),
409
+ children: [
410
+ /* @__PURE__ */ jsx("span", { children: label }),
411
+ /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
412
+ ]
413
+ }
414
+ )
415
+ }
416
+ );
417
+ }
418
+ function CompactSummaryTable({
419
+ data,
420
+ onSort,
421
+ sort,
422
+ settings
423
+ }) {
424
+ const formatValue = useDataLayer((state) => state.formatFieldValue);
425
+ const getFieldLabel = useDataLayer((state) => state.getFieldLabel);
426
+ const label = getFieldLabel ?? ((field) => field);
427
+ const rows = data.map((profile) => ({
428
+ profile,
429
+ summary: summarizeField(
430
+ profile,
431
+ (value) => formatValue(profile.name, value),
432
+ label(profile.name)
433
+ )
434
+ }));
435
+ return /* @__PURE__ */ jsxs(
436
+ "table",
437
+ {
438
+ className: "eda-summary-table",
439
+ style: readingColumns(rows.map((row) => row.summary)),
440
+ children: [
441
+ /* @__PURE__ */ jsx("caption", { className: "sr-only", children: getChartSummary(settings) }),
442
+ /* @__PURE__ */ jsxs("colgroup", { children: [
443
+ /* @__PURE__ */ jsx("col", { className: "eda-summary-col-field" }),
444
+ /* @__PURE__ */ jsx("col", { className: "eda-summary-col-count" }),
445
+ /* @__PURE__ */ jsx("col", { className: "eda-summary-col-count" }),
446
+ /* @__PURE__ */ jsx("col", {}),
447
+ /* @__PURE__ */ jsx("col", { className: "eda-summary-col-actions" })
448
+ ] }),
449
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
450
+ /* @__PURE__ */ jsx(SortHeader, { column: "name", label: "Field", sort, onSort }),
451
+ /* @__PURE__ */ jsx(
452
+ SortHeader,
453
+ {
454
+ column: "uniqueCount",
455
+ label: "Distinct",
456
+ sort,
457
+ onSort,
458
+ className: "eda-summary-num"
459
+ }
460
+ ),
461
+ /* @__PURE__ */ jsx(
462
+ SortHeader,
463
+ {
464
+ column: "nullCount",
465
+ label: "Missing",
466
+ sort,
467
+ onSort,
468
+ className: "eda-summary-num"
469
+ }
470
+ ),
471
+ /* @__PURE__ */ jsx("th", { scope: "col", children: "Values" }),
472
+ /* @__PURE__ */ jsx("th", { scope: "col", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) })
473
+ ] }) }),
474
+ /* @__PURE__ */ jsx("tbody", { children: rows.map(({ profile, summary }) => {
475
+ const fieldLabel = label(profile.name);
476
+ const missingShare = profile.totalCount > 0 ? profile.nullCount / profile.totalCount : 0;
477
+ return /* @__PURE__ */ jsxs("tr", { children: [
478
+ /* @__PURE__ */ jsx("th", { scope: "row", className: "eda-summary-field", children: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
479
+ /* @__PURE__ */ jsx(
480
+ FieldMetadata,
481
+ {
482
+ profile,
483
+ label: fieldLabel,
484
+ compact: true,
485
+ showDetail: false,
486
+ className: "min-w-0"
487
+ }
488
+ ),
489
+ /* @__PURE__ */ jsx(CalculatedFieldBadge, { field: profile.name })
490
+ ] }) }),
491
+ /* @__PURE__ */ jsx("td", { className: "eda-summary-num", children: profile.uniqueCount.toLocaleString() }),
492
+ /* @__PURE__ */ jsx(
493
+ "td",
494
+ {
495
+ className: cn(
496
+ "eda-summary-num",
497
+ profile.nullCount > 0 && "eda-summary-missing"
498
+ ),
499
+ children: profile.nullCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
500
+ profile.nullCount.toLocaleString(),
501
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-share-label", children: missingShare < 0.01 ? "<1%" : `${Math.round(missingShare * 100)}%` })
502
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
503
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2013" }),
504
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "None" })
505
+ ] })
506
+ }
507
+ ),
508
+ /* @__PURE__ */ jsx("td", { className: "eda-summary-values", children: summary ? /* @__PURE__ */ jsxs("div", { className: "eda-summary-profile", children: [
509
+ summary.graphic,
510
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: summary.description }),
511
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "eda-summary-reading", children: [
512
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-range", children: summary.label !== void 0 ? /* @__PURE__ */ jsx("span", { className: "eda-summary-label", children: summary.label }) : /* @__PURE__ */ jsxs(Fragment, { children: [
513
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-low", children: summary.low }),
514
+ summary.high !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
515
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-sep", children: "\u2013" }),
516
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-high", children: summary.high })
517
+ ] })
518
+ ] }) }),
519
+ summary.stat !== void 0 && /* @__PURE__ */ jsxs("span", { className: "eda-summary-stat-group", children: [
520
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-stat-label", children: summary.statLabel }),
521
+ /* @__PURE__ */ jsx("span", { className: "eda-summary-stat", children: summary.stat })
522
+ ] })
523
+ ] })
524
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: profile.totalCount === 0 ? "No rows in the current filter" : "No values" }) }),
525
+ /* @__PURE__ */ jsx("td", { className: "eda-summary-actions-cell", children: /* @__PURE__ */ jsxs("div", { className: "eda-summary-actions", children: [
526
+ /* @__PURE__ */ jsx(FieldInspector, { field: profile.name, children: /* @__PURE__ */ jsx(
527
+ Button,
528
+ {
529
+ variant: "ghost",
530
+ size: "icon",
531
+ "aria-label": `Inspect ${fieldLabel}`,
532
+ children: /* @__PURE__ */ jsx(Settings2, {})
533
+ }
534
+ ) }),
535
+ /* @__PURE__ */ jsx(
536
+ ChartActions,
537
+ {
538
+ columnName: profile.name,
539
+ dataType: profile.dataType
540
+ }
541
+ )
542
+ ] }) })
543
+ ] }, profile.name);
544
+ }) })
545
+ ]
546
+ }
547
+ );
548
+ }
549
+ var exportToCSV = (profiles) => {
550
+ const headers = [
551
+ "Column",
552
+ "Type",
553
+ "Total",
554
+ "Unique",
555
+ "Missing",
556
+ "Min",
557
+ "Max",
558
+ "Mean",
559
+ "Median",
560
+ "StdDev",
561
+ "Top Values"
562
+ ];
563
+ const rows = profiles.map((profile) => [
564
+ profile.name,
565
+ profile.dataType,
566
+ profile.totalCount,
567
+ profile.uniqueCount,
568
+ profile.nullCount,
569
+ profile.statistics?.min ?? "",
570
+ profile.statistics?.max ?? "",
571
+ profile.statistics?.mean ?? "",
572
+ profile.statistics?.median ?? "",
573
+ profile.statistics?.stdDev ?? "",
574
+ profile.categories?.topValues.map((value) => `${categoryLabel(value.value)}(${value.count})`).join("; ") ?? ""
575
+ ]);
576
+ const csvContent = [
577
+ headers.join(","),
578
+ ...rows.map(
579
+ (row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",")
580
+ )
581
+ ].join("\n");
582
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
583
+ const link = document.createElement("a");
584
+ const url = URL.createObjectURL(blob);
585
+ link.href = url;
586
+ link.download = "summary_table.csv";
587
+ document.body.appendChild(link);
588
+ link.click();
589
+ document.body.removeChild(link);
590
+ URL.revokeObjectURL(url);
591
+ toast.success("Summary table exported to CSV");
592
+ };
593
+ function SummaryTable({
594
+ height,
595
+ settings,
596
+ toolbarTarget
597
+ }) {
598
+ const sourceProfiles = useDataLayer((state) => state.fieldProfiles);
599
+ const data = useDataLayer((state) => state.data);
600
+ const calculations = useDataLayer((state) => state.calculations);
601
+ const fieldSettings = useDataLayer((state) => state.fieldSettings);
602
+ const getColumnData = useDataLayer((state) => state.getColumnData);
603
+ const crossfilterWrapper = useDataLayer((state) => state.crossfilterWrapper);
604
+ const liveItems = useDataLayer((state) => state.liveItems);
605
+ const chartState = useDataLayer((state) => state.charts);
606
+ const [sortConfig, setSortConfig] = useState({
607
+ column: null,
608
+ direction: "asc"
609
+ });
610
+ const allProfiles = useMemo(() => {
611
+ const filteredIds = new Set(
612
+ chartState.length ? crossfilterWrapper.getFilteredRowIds() : data.map((row) => row.__ID)
613
+ );
614
+ const filteredRows = data.filter((row) => filteredIds.has(row.__ID));
615
+ const profileColumn = (name) => Object.fromEntries(
616
+ filteredRows.map((row) => [row.__ID, row[name]])
617
+ );
618
+ const source = sourceProfiles.map(
619
+ (profile) => filteredRows.length === 0 ? emptyFieldProfile(profile) : buildFieldProfile(
620
+ profile.name,
621
+ profileColumn(profile.name),
622
+ profile.dataType
623
+ )
624
+ );
625
+ const calculated = calculations.map((calculation) => {
626
+ const allColumnData = getColumnData(calculation.resultColumnName);
627
+ const filteredColumnData = Object.fromEntries(
628
+ filteredRows.map((row) => [row.__ID, allColumnData[row.__ID]])
629
+ );
630
+ const profile = buildFieldProfile(
631
+ calculation.resultColumnName,
632
+ allColumnData
633
+ );
634
+ return filteredRows.length === 0 ? emptyFieldProfile(profile) : buildFieldProfile(
635
+ calculation.resultColumnName,
636
+ filteredColumnData,
637
+ profile.dataType
638
+ );
639
+ });
640
+ return [...source, ...calculated];
641
+ }, [
642
+ sourceProfiles,
643
+ data,
644
+ calculations,
645
+ getColumnData,
646
+ crossfilterWrapper,
647
+ chartState,
648
+ liveItems,
649
+ fieldSettings
650
+ ]);
651
+ const sortedProfiles = useMemo(() => {
652
+ if (!sortConfig.column) {
653
+ return allProfiles;
654
+ }
655
+ return [...allProfiles].sort((a, b) => {
656
+ const aValue = a[sortConfig.column];
657
+ const bValue = b[sortConfig.column];
658
+ if (aValue === bValue) {
659
+ return 0;
660
+ }
661
+ if (aValue == null) {
662
+ return 1;
663
+ }
664
+ if (bValue == null) {
665
+ return -1;
666
+ }
667
+ const result = typeof aValue === "number" && typeof bValue === "number" ? aValue - bValue : String(aValue).localeCompare(String(bValue));
668
+ return sortConfig.direction === "asc" ? result : -result;
669
+ });
670
+ }, [allProfiles, sortConfig]);
671
+ const handleSort = (column) => {
672
+ setSortConfig((current) => {
673
+ if (current.column !== column) return { column, direction: "asc" };
674
+ if (current.direction === "asc") return { column, direction: "desc" };
675
+ return { column: null, direction: "asc" };
676
+ });
677
+ };
678
+ const toolbar = /* @__PURE__ */ jsxs("div", { className: "eda-table-toolbar-compact", children: [
679
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground tabular-nums", children: [
680
+ (allProfiles[0]?.totalCount ?? 0).toLocaleString(),
681
+ " rows"
682
+ ] }),
683
+ /* @__PURE__ */ jsx(ActionTooltip, { content: "Export summary as CSV", children: /* @__PURE__ */ jsx(
684
+ Button,
685
+ {
686
+ variant: "ghost",
687
+ size: "icon",
688
+ "aria-label": "Export summary as CSV",
689
+ onClick: () => exportToCSV(sortedProfiles),
690
+ children: /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" })
691
+ }
692
+ ) })
693
+ ] });
694
+ return /* @__PURE__ */ jsxs("div", { className: "eda-summary-view overflow-auto", style: { height }, children: [
695
+ toolbarTarget === void 0 ? toolbar : toolbarTarget && createPortal(toolbar, toolbarTarget),
696
+ /* @__PURE__ */ jsx(
697
+ CompactSummaryTable,
698
+ {
699
+ data: sortedProfiles,
700
+ onSort: handleSort,
701
+ sort: sortConfig,
702
+ settings
703
+ }
704
+ )
705
+ ] });
706
+ }
707
+
708
+ // src/components/charts/SummaryTable/SummaryTableSettingsPanel.tsx
709
+ function SummaryTableSettingsPanel() {
710
+ return null;
711
+ }
712
+
713
+ // src/components/charts/SummaryTable/definition.ts
714
+ var summaryTableDefinition = {
715
+ type: "summary",
716
+ name: "Summary Table",
717
+ description: "Display summary statistics for your data",
718
+ icon: Info,
719
+ component: SummaryTable,
720
+ settingsPanel: SummaryTableSettingsPanel,
721
+ createDefaultSettings: (layout) => ({
722
+ ...DEFAULT_CHART_SETTINGS,
723
+ id: crypto.randomUUID(),
724
+ type: "summary",
725
+ title: "Summary Table",
726
+ layout,
727
+ margin: { top: 20, right: 20, bottom: 20, left: 20 }
728
+ }),
729
+ validateSettings: () => {
730
+ return true;
731
+ },
732
+ getFilterFunction: () => {
733
+ return () => true;
734
+ }
735
+ };
736
+
737
+ export { summaryTableDefinition, useCreateCharts };
738
+ //# sourceMappingURL=chunk-PP4VCONW.js.map
739
+ //# sourceMappingURL=chunk-PP4VCONW.js.map