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,2171 @@
1
+ import { getChartDefinition, chartRegistry } from './chunk-TLLRW6S3.js';
2
+ import { parseExpression } from './chunk-H5AVMMCE.js';
3
+ import crossfilter from 'crossfilter2';
4
+ import isEqual from 'react-fast-compare';
5
+ import { utcFormat } from 'd3-time-format';
6
+ import { createContext, useRef, useEffect, useContext } from 'react';
7
+ import { useStore, createStore } from 'zustand';
8
+ import { jsx, jsxs } from 'react/jsx-runtime';
9
+ import { clsx } from 'clsx';
10
+ import { twMerge } from 'tailwind-merge';
11
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
12
+ import { Slot } from '@radix-ui/react-slot';
13
+ import { cva } from 'class-variance-authority';
14
+
15
+ // src/lib/categories.ts
16
+ var categoryValue = (value) => value ?? null;
17
+ var categoryKey = (value) => {
18
+ if (value == null) return JSON.stringify(["missing"]);
19
+ if (typeof value === "number" && !Number.isFinite(value)) {
20
+ return JSON.stringify(["number", String(value)]);
21
+ }
22
+ return JSON.stringify([typeof value, value]);
23
+ };
24
+ function categoryEqual(left, right) {
25
+ return left == null && right == null || left === right || typeof left === "number" && typeof right === "number" && Number.isNaN(left) && Number.isNaN(right);
26
+ }
27
+ function categoryIncludes(values, value) {
28
+ return values.includes(value) || value == null && values.some((item) => item == null);
29
+ }
30
+ function categoryLabel(value) {
31
+ if (value == null) return "(missing)";
32
+ if (typeof value === "string" && (!value.trim() || !Number.isNaN(Number(value)) || /^(true|false|null|undefined|NaN|Other categories|\(missing\))$/.test(
33
+ value
34
+ ) || value.startsWith('"')))
35
+ return JSON.stringify(value);
36
+ return String(value);
37
+ }
38
+
39
+ // src/lib/dateTime.ts
40
+ function dateTimestamp(value) {
41
+ return Date.parse(
42
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/.test(value) ? `${value}Z` : value
43
+ );
44
+ }
45
+
46
+ // src/lib/fieldSettings.ts
47
+ var validTypes = /* @__PURE__ */ new Set([
48
+ "numeric",
49
+ "categorical",
50
+ "datetime",
51
+ "boolean"
52
+ ]);
53
+ var validFormats = /* @__PURE__ */ new Set([
54
+ "auto",
55
+ "number",
56
+ "currency",
57
+ "percent",
58
+ "date",
59
+ "datetime"
60
+ ]);
61
+ function getFieldSettingsError(settings) {
62
+ if (settings.type && !validTypes.has(settings.type)) {
63
+ return "Choose a valid field type.";
64
+ }
65
+ if (settings.format && !validFormats.has(settings.format)) {
66
+ return "Choose a valid display format.";
67
+ }
68
+ if (settings.precision !== void 0 && (!Number.isInteger(settings.precision) || settings.precision < 0 || settings.precision > 20)) {
69
+ return "Precision must be a whole number from 0 to 20.";
70
+ }
71
+ if (settings.currency !== void 0 && !/^[A-Z]{3}$/.test(settings.currency)) {
72
+ return "Currency must be a three-letter code, such as USD.";
73
+ }
74
+ if (settings.datePreset !== void 0 && !["iso", "month-day-year", "day-month-year"].includes(settings.datePreset)) {
75
+ return "Choose a valid date input preset.";
76
+ }
77
+ if (settings.nullTokens !== void 0 && settings.nullTokens.some((token) => typeof token !== "string")) {
78
+ return "Null tokens must be text values.";
79
+ }
80
+ return void 0;
81
+ }
82
+ function validCalendarDate(year, month, day) {
83
+ const timestamp = Date.UTC(year, month - 1, day);
84
+ const date = new Date(timestamp);
85
+ return Number.isFinite(timestamp) && date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
86
+ }
87
+ function parseDate(value, preset = "iso") {
88
+ const text = value.trim();
89
+ if (preset === "month-day-year" || preset === "day-month-year") {
90
+ const match = text.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/);
91
+ if (match) {
92
+ const [, first, second, yearText2] = match;
93
+ const year = Number(yearText2);
94
+ const month = preset === "month-day-year" ? Number(first) : Number(second);
95
+ const day = preset === "month-day-year" ? Number(second) : Number(first);
96
+ const parsed = Date.UTC(year, month - 1, day);
97
+ if (validCalendarDate(year, month, day)) {
98
+ return parsed;
99
+ }
100
+ }
101
+ return NaN;
102
+ }
103
+ const isoMatch = text.match(
104
+ /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:\d{2})?)?$/
105
+ );
106
+ if (!isoMatch) return NaN;
107
+ const [, yearText, monthText, dayText] = isoMatch;
108
+ if (!validCalendarDate(
109
+ Number(yearText),
110
+ Number(monthText),
111
+ Number(dayText)
112
+ )) {
113
+ return NaN;
114
+ }
115
+ return dateTimestamp(text);
116
+ }
117
+ function convertFieldValue(raw, type, settings = {}) {
118
+ if (raw == null) {
119
+ return { value: void 0 };
120
+ }
121
+ const text = typeof raw === "string" ? raw.trim() : String(raw);
122
+ if (settings.nullTokens?.some((token) => token === text)) {
123
+ return { value: void 0 };
124
+ }
125
+ if (type !== "categorical" && text === "") {
126
+ return { value: void 0 };
127
+ }
128
+ switch (type) {
129
+ case "categorical":
130
+ return { value: raw };
131
+ case "numeric": {
132
+ const value = typeof raw === "number" ? raw : Number(text);
133
+ return Number.isFinite(value) ? { value } : { value: void 0, error: "Not a finite number" };
134
+ }
135
+ case "boolean":
136
+ if (raw === true || raw === false) {
137
+ return { value: raw };
138
+ }
139
+ if (text.toLowerCase() === "true") {
140
+ return { value: true };
141
+ }
142
+ if (text.toLowerCase() === "false") {
143
+ return { value: false };
144
+ }
145
+ return { value: void 0, error: "Expected true or false" };
146
+ case "datetime": {
147
+ const timestamp = parseDate(text, settings.datePreset);
148
+ return Number.isFinite(timestamp) ? { value: new Date(timestamp).toISOString() } : { value: void 0, error: "Invalid date" };
149
+ }
150
+ }
151
+ }
152
+ function getFieldLabel(field, settings = {}) {
153
+ const label = settings.label?.trim() || field;
154
+ return settings.unit ? `${label} (${settings.unit})` : label;
155
+ }
156
+ function hasFieldDisplayFormat(settings = {}) {
157
+ return Boolean(
158
+ settings.format && settings.format !== "auto" || settings.precision !== void 0 || settings.unit
159
+ );
160
+ }
161
+ function formatFieldValue(field, value, settings = {}) {
162
+ if (value == null) {
163
+ return "\u2014";
164
+ }
165
+ if (typeof value === "boolean") {
166
+ return value ? "Yes" : "No";
167
+ }
168
+ if (typeof value === "string" && value.trim() === "") {
169
+ return "\u2014";
170
+ }
171
+ const format = settings.format ?? "auto";
172
+ if (format === "date" || format === "datetime") {
173
+ const date = typeof value === "number" ? new Date(value) : new Date(String(value));
174
+ if (Number.isFinite(date.getTime())) {
175
+ const options = {
176
+ timeZone: "UTC",
177
+ dateStyle: "medium"
178
+ };
179
+ if (format === "datetime") options.timeStyle = "short";
180
+ const text = new Intl.DateTimeFormat("en-US", options).format(date);
181
+ return settings.unit ? `${text} ${settings.unit}` : text;
182
+ }
183
+ return String(value);
184
+ }
185
+ const numberValue = format === "number" || format === "currency" || format === "percent" || typeof value === "number" && format === "auto" ? Number(value) : NaN;
186
+ if (Number.isFinite(numberValue)) {
187
+ const precision = Number.isInteger(settings.precision) && settings.precision !== void 0 && settings.precision >= 0 && settings.precision <= 20 ? settings.precision : void 0;
188
+ const options = {
189
+ maximumFractionDigits: precision ?? 3,
190
+ minimumFractionDigits: precision
191
+ };
192
+ if (format === "currency") {
193
+ options.style = "currency";
194
+ options.currency = /^[A-Z]{3}$/.test(settings.currency ?? "") ? settings.currency : "USD";
195
+ } else if (format === "percent") {
196
+ options.style = "percent";
197
+ }
198
+ const text = new Intl.NumberFormat("en-US", options).format(numberValue);
199
+ return settings.unit ? `${text} ${settings.unit}` : text;
200
+ }
201
+ return String(value);
202
+ }
203
+ function applyFieldSettings(rows, fieldSettings, inferredTypes) {
204
+ return rows.map((row) => {
205
+ const next = { ...row };
206
+ Object.entries(fieldSettings).forEach(([field, settings]) => {
207
+ if (!settings.type && !(settings.nullTokens && settings.nullTokens.length > 0) && !settings.datePreset) {
208
+ return;
209
+ }
210
+ const type = settings.type ?? inferredTypes[field];
211
+ if (!type || !(field in row)) {
212
+ return;
213
+ }
214
+ next[field] = convertFieldValue(
215
+ row[field],
216
+ type,
217
+ settings
218
+ ).value;
219
+ });
220
+ return next;
221
+ });
222
+ }
223
+ function buildConversionPreview(field, rows, settings = {}, inferredType) {
224
+ const effectiveType = settings.type ?? inferredType;
225
+ const examples = [];
226
+ const failures = [];
227
+ let validCount = 0;
228
+ let missingCount = 0;
229
+ rows.forEach((row, rowIndex) => {
230
+ const raw = row[field];
231
+ const converts = settings.type !== void 0 || Boolean(settings.datePreset) || Boolean(settings.nullTokens?.length);
232
+ const result = converts ? convertFieldValue(raw, effectiveType, settings) : { value: raw };
233
+ if (result.error) {
234
+ failures.push({ row: rowIndex, raw, reason: result.error });
235
+ } else if (raw == null || result.value == null) {
236
+ missingCount += 1;
237
+ } else {
238
+ validCount += 1;
239
+ }
240
+ if (examples.length < 8 && (raw != null || result.error)) {
241
+ examples.push({ row: rowIndex, raw, value: result.value });
242
+ }
243
+ });
244
+ return {
245
+ field,
246
+ inferredType,
247
+ effectiveType,
248
+ totalCount: rows.length,
249
+ validCount,
250
+ missingCount,
251
+ failedCount: failures.length,
252
+ examples,
253
+ failures
254
+ };
255
+ }
256
+
257
+ // src/components/charts/chartAccessibility.ts
258
+ var chartNames = {
259
+ row: "Row chart",
260
+ bar: "Bar chart",
261
+ scatter: "Scatter plot",
262
+ line: "Line chart",
263
+ boxplot: "Box plot",
264
+ "3d-scatter": "3D scatter plot",
265
+ pivot: "Pivot table",
266
+ "data-table": "Data table",
267
+ summary: "Summary table",
268
+ "color-legend": "Color legend",
269
+ markdown: "Markdown note"
270
+ };
271
+ function getChartTitle(settings, getFieldLabel2 = (field) => field) {
272
+ const title = settings.title.trim();
273
+ if (settings.type === "color-legend" && settings.fields.length === 1 && (!title || settings.title === "Color Legend")) {
274
+ return getFieldLabel2(settings.fields[0]);
275
+ }
276
+ if (title) {
277
+ return title;
278
+ }
279
+ const field = settings.type === "bar" || settings.type === "row" || settings.type === "boxplot" ? settings.field : settings.type === "scatter" || settings.type === "3d-scatter" ? settings.yField : settings.type === "line" && settings.seriesField.length === 1 ? settings.seriesField[0] : void 0;
280
+ if (!field) {
281
+ return getChartDefinition(settings.type).name;
282
+ }
283
+ const label = field === "__ID" ? "Row sequence" : getFieldLabel2(field);
284
+ if (settings.type === "bar") {
285
+ return `Distribution of ${label}`;
286
+ }
287
+ if (settings.type === "row") {
288
+ return `Rows by ${label}`;
289
+ }
290
+ return `${getChartDefinition(settings.type).name} \xB7 ${label}`;
291
+ }
292
+ function getChartFields(settings) {
293
+ const fields = (() => {
294
+ switch (settings.type) {
295
+ case "line":
296
+ return [settings.xField, ...settings.seriesField];
297
+ case "scatter":
298
+ return [settings.xField, settings.yField, settings.colorField];
299
+ case "3d-scatter":
300
+ return [
301
+ settings.xField,
302
+ settings.yField,
303
+ settings.zField,
304
+ settings.colorField,
305
+ settings.sizeField
306
+ ];
307
+ case "pivot":
308
+ return [
309
+ ...settings.rowFields,
310
+ settings.columnField,
311
+ ...settings.valueFields.map((field) => field.field)
312
+ ];
313
+ case "data-table":
314
+ return settings.columns.map((column) => column.field);
315
+ case "color-legend":
316
+ return settings.fields;
317
+ default:
318
+ return [settings.field, settings.colorField];
319
+ }
320
+ })();
321
+ return Array.from(
322
+ new Set(fields.filter((field) => Boolean(field)))
323
+ );
324
+ }
325
+ function getChartAxisFields(settings) {
326
+ switch (settings.type) {
327
+ case "bar":
328
+ return { x: settings.field };
329
+ case "row":
330
+ return { y: settings.field };
331
+ case "scatter":
332
+ return { x: settings.xField, y: settings.yField };
333
+ case "line":
334
+ return {
335
+ x: settings.xField,
336
+ y: settings.seriesField.length === 1 ? settings.seriesField[0] : void 0
337
+ };
338
+ case "boxplot":
339
+ return { y: settings.field };
340
+ case "3d-scatter":
341
+ return { x: settings.xField, y: settings.yField };
342
+ default:
343
+ return {};
344
+ }
345
+ }
346
+ function getChartAxisLabel(field, local, getFieldLabel2 = (value) => value) {
347
+ return local || (field === "__ID" ? "Row sequence" : field ? getFieldLabel2(field) : "");
348
+ }
349
+ function getChartSummary(settings, getFieldLabel2 = (field) => field) {
350
+ const name = chartNames[settings.type] ?? "Chart";
351
+ if (settings.type === "summary") {
352
+ return `${name} of all data columns.`;
353
+ }
354
+ if (settings.type === "markdown") {
355
+ return `${name}.`;
356
+ }
357
+ const fields = getChartFields(settings);
358
+ return fields.length ? `${name} showing ${fields.map(getFieldLabel2).join(", ")}.` : `${name} with no data fields selected.`;
359
+ }
360
+ var CrossfilterWrapper = class {
361
+ ref;
362
+ nonce = 0;
363
+ charts = /* @__PURE__ */ new Map();
364
+ idFunction;
365
+ // assume this gets set after creation
366
+ fieldGetter = () => ({});
367
+ constructor(data, idFunction) {
368
+ this.ref = crossfilter(data);
369
+ this.idFunction = idFunction;
370
+ }
371
+ setFieldGetter(fieldGetter) {
372
+ this.fieldGetter = fieldGetter;
373
+ }
374
+ updateChart(chart) {
375
+ const existing = this.charts.get(chart.id);
376
+ if (!existing) {
377
+ this.addChart(chart);
378
+ return;
379
+ }
380
+ if (!isEqual(existing.chart, chart)) this.updateChartFilters(chart);
381
+ existing.chart = chart;
382
+ }
383
+ updateChartFilters(chart) {
384
+ const filterFunc = this.getFilterFunction(chart);
385
+ const foundChart = this.charts.get(chart.id);
386
+ const dimension = foundChart?.dimension;
387
+ if (!dimension) {
388
+ return;
389
+ }
390
+ dimension.filterFunction(filterFunc);
391
+ }
392
+ addChart(chart) {
393
+ const dimension = this.ref.dimension(this.idFunction);
394
+ const chartDimension = {
395
+ dimension,
396
+ chart,
397
+ group: dimension.group()
398
+ };
399
+ this.charts.set(chart.id, chartDimension);
400
+ this.updateChartFilters(chart);
401
+ }
402
+ removeChart(chart) {
403
+ const savedChart = this.charts.get(chart.id);
404
+ if (!savedChart) {
405
+ throw new Error(`Chart ${chart.id} not found`);
406
+ }
407
+ savedChart.dimension.filterAll();
408
+ savedChart.group.dispose();
409
+ savedChart.dimension.dispose();
410
+ this.charts.delete(chart.id);
411
+ }
412
+ removeAllCharts() {
413
+ for (const chart of this.charts.values()) {
414
+ chart.dimension.filterAll();
415
+ chart.group.dispose();
416
+ chart.dimension.dispose();
417
+ }
418
+ this.charts.clear();
419
+ }
420
+ getFilterFunction(chart) {
421
+ const definition = getChartDefinition(chart.type);
422
+ return definition.getFilterFunction(chart, this.fieldGetter);
423
+ }
424
+ getFilteredRowCount() {
425
+ return this.ref.allFiltered().length;
426
+ }
427
+ getFilteredRowIds() {
428
+ return this.ref.allFiltered().map(this.idFunction);
429
+ }
430
+ getAllData() {
431
+ const data = {};
432
+ const commonNonce = ++this.nonce;
433
+ for (const chart of this.charts.values()) {
434
+ data[chart.chart.id] = {
435
+ items: chart.group.all(),
436
+ nonce: commonNonce
437
+ };
438
+ }
439
+ return data;
440
+ }
441
+ };
442
+ function numericValue(value) {
443
+ if (value == null || value === "") throw new Error("Missing numeric value");
444
+ if (typeof value !== "number" && typeof value !== "string" || typeof value === "string" && !value.trim() || !Number.isFinite(Number(value))) {
445
+ throw new Error(`Invalid numeric value: ${String(value)}`);
446
+ }
447
+ return Number(value);
448
+ }
449
+ function dateValue(value) {
450
+ const date = value instanceof Date ? value : new Date(dateTimestamp(String(value)));
451
+ if (!Number.isFinite(date.getTime())) {
452
+ throw new Error(`Invalid date: ${String(value)}`);
453
+ }
454
+ return date;
455
+ }
456
+ var calculationFunctions = {
457
+ sum: {
458
+ description: "Add values within this row.",
459
+ syntax: "sum(x, y, \u2026)",
460
+ minArgs: 1,
461
+ evaluate: (...values) => values.reduce((sum, value) => sum + numericValue(value), 0)
462
+ },
463
+ avg: {
464
+ description: "Average values within this row, not across rows.",
465
+ syntax: "avg(x, y, \u2026)",
466
+ minArgs: 1,
467
+ evaluate: (...values) => values.reduce((sum, value) => sum + numericValue(value), 0) / values.length
468
+ },
469
+ min: {
470
+ description: "Return the smallest supplied value.",
471
+ syntax: "min(x, y, \u2026)",
472
+ minArgs: 1,
473
+ evaluate: (...values) => Math.min(...values.map(numericValue))
474
+ },
475
+ max: {
476
+ description: "Return the largest supplied value.",
477
+ syntax: "max(x, y, \u2026)",
478
+ minArgs: 1,
479
+ evaluate: (...values) => Math.max(...values.map(numericValue))
480
+ },
481
+ count: {
482
+ description: "Count supplied arguments, including missing values.",
483
+ syntax: "count(x, y, \u2026)",
484
+ minArgs: 0,
485
+ evaluate: (...values) => values.length
486
+ },
487
+ formatdate: {
488
+ description: "Format a UTC date. Use %Y-%m for a month label.",
489
+ syntax: 'formatDate(date, "%Y-%m-%d")',
490
+ minArgs: 2,
491
+ maxArgs: 2,
492
+ evaluate: (value, format) => utcFormat(String(format))(dateValue(value))
493
+ },
494
+ extractdatecomponent: {
495
+ description: "Extract a UTC year, month, day, quarter, or ISO week.",
496
+ syntax: 'extractDateComponent(date, "year")',
497
+ minArgs: 2,
498
+ maxArgs: 2,
499
+ evaluate: (value, component) => {
500
+ const date = dateValue(value);
501
+ switch (component) {
502
+ case "year":
503
+ return date.getUTCFullYear();
504
+ case "month":
505
+ return date.getUTCMonth() + 1;
506
+ case "day":
507
+ return date.getUTCDate();
508
+ case "quarter":
509
+ return Math.floor(date.getUTCMonth() / 3) + 1;
510
+ case "week":
511
+ return Number(utcFormat("%V")(date));
512
+ default:
513
+ throw new Error(`Unknown date component: ${String(component)}`);
514
+ }
515
+ }
516
+ }
517
+ };
518
+ function getFunction(name, argumentCount) {
519
+ const key = name.toLowerCase();
520
+ const fn = Object.hasOwn(calculationFunctions, key) ? calculationFunctions[key] : void 0;
521
+ if (!fn) throw new Error(`Unknown function: ${name}`);
522
+ if (argumentCount < fn.minArgs || fn.maxArgs !== void 0 && argumentCount > fn.maxArgs) {
523
+ throw new Error(`Invalid arguments. Use ${fn.syntax}`);
524
+ }
525
+ return fn;
526
+ }
527
+
528
+ // src/lib/calculations/engine/Calculator.ts
529
+ function validateExpression(expression, fields) {
530
+ for (const field of expression.dependencies) {
531
+ if (!fields.includes(field)) throw new Error(`Unknown field: ${field}`);
532
+ }
533
+ switch (expression.type) {
534
+ case "function":
535
+ getFunction(expression.functionName, expression.arguments.length);
536
+ expression.arguments.forEach((arg) => validateExpression(arg, fields));
537
+ break;
538
+ case "basic":
539
+ validateExpression(expression.left, fields);
540
+ validateExpression(expression.right, fields);
541
+ break;
542
+ case "unary":
543
+ validateExpression(expression.operand, fields);
544
+ break;
545
+ case "ternary":
546
+ validateExpression(expression.condition, fields);
547
+ validateExpression(expression.trueBranch, fields);
548
+ validateExpression(expression.falseBranch, fields);
549
+ }
550
+ }
551
+ var Calculator = class {
552
+ constructor(context, steps) {
553
+ this.context = context;
554
+ this.steps = steps;
555
+ }
556
+ depth = 0;
557
+ evaluate(expression) {
558
+ try {
559
+ return { success: true, value: this.value(expression) };
560
+ } catch (error) {
561
+ return {
562
+ success: false,
563
+ value: null,
564
+ error: error instanceof Error ? error.message : String(error)
565
+ };
566
+ }
567
+ }
568
+ value(expression) {
569
+ const depth = this.depth++;
570
+ const label = expression.type === "literal" ? expression.dependencies[0] ?? String(expression.value) : expression.type === "function" ? expression.functionName : expression.type === "ternary" ? "conditional" : expression.operator;
571
+ try {
572
+ const result = this.evaluateValue(expression);
573
+ if (typeof result === "number" && !Number.isFinite(result)) {
574
+ throw new Error("Calculation produced a non-finite number");
575
+ }
576
+ this.steps?.push({ id: expression.id, depth, label, value: result });
577
+ return result;
578
+ } catch (error) {
579
+ this.steps?.push({
580
+ id: expression.id,
581
+ depth,
582
+ label,
583
+ error: error instanceof Error ? error.message : String(error)
584
+ });
585
+ throw error;
586
+ } finally {
587
+ this.depth--;
588
+ }
589
+ }
590
+ evaluateValue(expression) {
591
+ switch (expression.type) {
592
+ case "literal": {
593
+ const field = expression.dependencies[0];
594
+ if (field === void 0) return expression.value;
595
+ if (!this.context.variables.has(field))
596
+ throw new Error(`Undefined variable: ${field}`);
597
+ return this.context.variables.get(field);
598
+ }
599
+ case "function":
600
+ return getFunction(
601
+ expression.functionName,
602
+ expression.arguments.length
603
+ ).evaluate(...expression.arguments.map((arg) => this.value(arg)));
604
+ case "ternary":
605
+ if (!expression.condition || !expression.trueBranch || !expression.falseBranch) {
606
+ throw new Error("Invalid ternary expression: missing branches");
607
+ }
608
+ return this.value(
609
+ this.value(expression.condition) ? expression.trueBranch : expression.falseBranch
610
+ );
611
+ case "unary": {
612
+ if (!expression.operand)
613
+ throw new Error("Invalid unary expression: missing operand");
614
+ const value = this.value(expression.operand);
615
+ switch (expression.operator) {
616
+ case "!":
617
+ return !value;
618
+ case "-":
619
+ return -numericValue(value);
620
+ case "+":
621
+ return numericValue(value);
622
+ default:
623
+ throw new Error(`Unknown unary operator: ${expression.operator}`);
624
+ }
625
+ }
626
+ case "basic": {
627
+ if (!expression.left || !expression.right)
628
+ throw new Error("Invalid basic expression: missing operands");
629
+ const left = this.value(expression.left);
630
+ if (expression.operator === "&&")
631
+ return Boolean(left) && Boolean(this.value(expression.right));
632
+ if (expression.operator === "||")
633
+ return Boolean(left) || Boolean(this.value(expression.right));
634
+ const right = this.value(expression.right);
635
+ if (expression.operator === "==")
636
+ return left == null ? right == null : left === right;
637
+ if (expression.operator === "!=")
638
+ return left == null ? right != null : left !== right;
639
+ const a = numericValue(left);
640
+ const b = numericValue(right);
641
+ switch (expression.operator) {
642
+ case "+":
643
+ return a + b;
644
+ case "-":
645
+ return a - b;
646
+ case "*":
647
+ return a * b;
648
+ case "/":
649
+ if (b === 0) throw new Error("Division by zero");
650
+ return a / b;
651
+ case "^":
652
+ return a ** b;
653
+ case ">":
654
+ return a > b;
655
+ case ">=":
656
+ return a >= b;
657
+ case "<":
658
+ return a < b;
659
+ case "<=":
660
+ return a <= b;
661
+ default:
662
+ throw new Error(`Unknown operator: ${expression.operator}`);
663
+ }
664
+ }
665
+ default:
666
+ throw new Error("Unknown expression type");
667
+ }
668
+ }
669
+ };
670
+
671
+ // src/lib/calculations/CalculationState.ts
672
+ var CalculationManager = class {
673
+ constructor(data, calculations = []) {
674
+ this.data = data;
675
+ this.setCalculations(calculations);
676
+ }
677
+ calculations = [];
678
+ results = /* @__PURE__ */ new Map();
679
+ errors = /* @__PURE__ */ new Map();
680
+ setCalculations(calculations) {
681
+ const sourceFields = new Set(this.data.flatMap((row) => Object.keys(row)));
682
+ const names = calculations.map((calc) => calc.resultColumnName);
683
+ for (const name of names) {
684
+ if (!name.trim()) throw new Error("Enter a result column name");
685
+ if (sourceFields.has(name) || names.filter((other) => other === name).length > 1) {
686
+ throw new Error(`Field already exists: ${name}`);
687
+ }
688
+ }
689
+ const fields = [...sourceFields, ...names];
690
+ const visiting = /* @__PURE__ */ new Set();
691
+ const visited = /* @__PURE__ */ new Set();
692
+ const visit = (calc) => {
693
+ const name = calc.resultColumnName;
694
+ if (visiting.has(name)) throw new Error(`Circular calculation: ${name}`);
695
+ if (visited.has(name)) return;
696
+ visiting.add(name);
697
+ validateExpression(calc.expression, fields);
698
+ for (const field of calc.expression.dependencies) {
699
+ const dependency = calculations.find(
700
+ (item) => item.resultColumnName === field
701
+ );
702
+ if (dependency) visit(dependency);
703
+ }
704
+ visiting.delete(name);
705
+ visited.add(name);
706
+ };
707
+ calculations.forEach(visit);
708
+ const affected = /* @__PURE__ */ new Set([
709
+ ...this.calculations.map((calc) => calc.resultColumnName),
710
+ ...names
711
+ ]);
712
+ this.calculations = [...calculations];
713
+ this.results.clear();
714
+ this.errors.clear();
715
+ return affected;
716
+ }
717
+ addCalculation(calculation) {
718
+ return this.setCalculations([...this.calculations, calculation]);
719
+ }
720
+ updateCalculation(name, calculation) {
721
+ if (!this.calculations.some((calc) => calc.resultColumnName === name)) {
722
+ throw new Error(`Unknown calculation: ${name}`);
723
+ }
724
+ return this.setCalculations(
725
+ this.calculations.map(
726
+ (calc) => calc.resultColumnName === name ? calculation : calc
727
+ )
728
+ );
729
+ }
730
+ removeCalculation(name) {
731
+ return this.setCalculations(
732
+ this.calculations.filter((calc) => calc.resultColumnName !== name)
733
+ );
734
+ }
735
+ getCalculations() {
736
+ return [...this.calculations];
737
+ }
738
+ getErrors(name) {
739
+ const calculation = this.calculations.find(
740
+ (calc) => calc.resultColumnName === name
741
+ );
742
+ if (calculation) this.executeCalculation(calculation);
743
+ return this.errors.get(name) ?? /* @__PURE__ */ new Map();
744
+ }
745
+ traceRow(name, rowId) {
746
+ const calc = this.calculations.find(
747
+ (item) => item.resultColumnName === name
748
+ );
749
+ const row = this.data.find((item) => item.__ID === rowId);
750
+ if (!calc || !row) return void 0;
751
+ const dependencies = calc.expression.dependencies.flatMap((field) => {
752
+ const trace = this.traceRow(field, rowId);
753
+ return trace ? [trace] : [];
754
+ });
755
+ const variables = new Map(Object.entries(row));
756
+ for (const field of calc.expression.dependencies) {
757
+ const dependency = dependencies.find((item) => item.field === field);
758
+ if (dependency) variables.set(field, dependency.value);
759
+ }
760
+ const steps = [];
761
+ const result = new Calculator(
762
+ { data: this.data, variables },
763
+ steps
764
+ ).evaluate(calc.expression);
765
+ return {
766
+ field: name,
767
+ expression: calc.expression.rawInput,
768
+ value: result.success && !(result.value instanceof Date) ? result.value ?? void 0 : void 0,
769
+ error: result.error,
770
+ steps,
771
+ inputs: calc.expression.dependencies.map((field) => ({
772
+ field,
773
+ value: variables.get(field)
774
+ })),
775
+ dependencies
776
+ };
777
+ }
778
+ executeCalculation(calculation) {
779
+ const name = calculation.resultColumnName;
780
+ const cached = this.results.get(name);
781
+ if (cached) return cached;
782
+ for (const field of calculation.expression.dependencies) {
783
+ const dependency = this.calculations.find(
784
+ (calc) => calc.resultColumnName === field
785
+ );
786
+ if (dependency) this.executeCalculation(dependency);
787
+ }
788
+ const values = /* @__PURE__ */ new Map();
789
+ const errors = /* @__PURE__ */ new Map();
790
+ for (const row of this.data) {
791
+ const variables = new Map(Object.entries(row));
792
+ for (const field of calculation.expression.dependencies) {
793
+ const results = this.results.get(field);
794
+ variables.set(field, results ? results.get(row.__ID) : row[field]);
795
+ }
796
+ const result = new Calculator({ data: this.data, variables }).evaluate(
797
+ calculation.expression
798
+ );
799
+ values.set(
800
+ row.__ID,
801
+ result.success && !(result.value instanceof Date) ? result.value ?? void 0 : void 0
802
+ );
803
+ if (!result.success)
804
+ errors.set(row.__ID, result.error ?? "Calculation failed");
805
+ }
806
+ this.results.set(name, values);
807
+ this.errors.set(name, errors);
808
+ return values;
809
+ }
810
+ };
811
+
812
+ // src/lib/numeric.ts
813
+ function isMissingValue(value) {
814
+ return value === void 0 || value === null || typeof value === "string" && value.trim() === "";
815
+ }
816
+ function isNumberLike(value) {
817
+ if (typeof value === "number") return true;
818
+ if (typeof value !== "string" || value.trim() === "") return false;
819
+ return value.trim() === "NaN" || !Number.isNaN(Number(value));
820
+ }
821
+ function numericExclusionReason(value) {
822
+ if (value === void 0 || value === null || value === "") {
823
+ return "Missing value";
824
+ }
825
+ if (typeof value === "boolean") return "Boolean values are not numeric";
826
+ if (typeof value === "string" && value.trim() === "") return "Blank value";
827
+ return Number.isFinite(Number(value)) ? void 0 : "Not a finite number";
828
+ }
829
+ function finiteNumber(value) {
830
+ if (numericExclusionReason(value) !== void 0) return void 0;
831
+ return Number(value);
832
+ }
833
+ function finiteNumbers(values) {
834
+ const numbers = [];
835
+ for (const value of values) {
836
+ const number = finiteNumber(value);
837
+ if (number !== void 0) numbers.push(number);
838
+ }
839
+ return numbers;
840
+ }
841
+
842
+ // src/components/SummaryTable/utils/dataTypeDetection.ts
843
+ function detectColumnType(columnData) {
844
+ const values = Object.values(columnData);
845
+ const nonNullValues = values.filter((v) => !isMissingValue(v));
846
+ if (nonNullValues.length === 0) {
847
+ return "categorical";
848
+ }
849
+ const booleanValues = /* @__PURE__ */ new Set(["true", "false", true, false]);
850
+ if (nonNullValues.every((v) => booleanValues.has(v))) {
851
+ return "boolean";
852
+ }
853
+ if (nonNullValues.every(isNumberLike)) {
854
+ return "numeric";
855
+ }
856
+ if (nonNullValues.every((v) => !isNaN(Date.parse(String(v))))) {
857
+ return "datetime";
858
+ }
859
+ return "categorical";
860
+ }
861
+
862
+ // src/components/SummaryTable/utils/statisticsCalculator.ts
863
+ var DISTRIBUTION_BINS = 24;
864
+ function binValues(sorted, min, max, binCount = DISTRIBUTION_BINS) {
865
+ if (sorted.length === 0 || !Number.isFinite(min) || !Number.isFinite(max)) {
866
+ return [];
867
+ }
868
+ if (min === max) return [sorted.length];
869
+ if (max - min + 1 < binCount && sorted.every(Number.isInteger)) {
870
+ binCount = max - min + 1;
871
+ const bins2 = new Array(binCount).fill(0);
872
+ for (const value of sorted) bins2[value - min] += 1;
873
+ return bins2;
874
+ }
875
+ const bins = new Array(binCount).fill(0);
876
+ const width = (max - min) / binCount;
877
+ for (const value of sorted) {
878
+ if (!Number.isFinite(value)) continue;
879
+ const index = Math.min(binCount - 1, Math.floor((value - min) / width));
880
+ bins[index] += 1;
881
+ }
882
+ return bins;
883
+ }
884
+ function calculateColumnStatistics(columnData, dataType) {
885
+ const values = Object.values(columnData);
886
+ const totalCount = values.length;
887
+ const nullCount = values.filter((v) => v == null).length;
888
+ const nonNullValues = values.filter((v) => v != null);
889
+ const uniqueValues = new Set(nonNullValues);
890
+ const uniqueCount = uniqueValues.size;
891
+ if (dataType === "numeric") {
892
+ const missingCount = values.filter(isMissingValue).length;
893
+ const numericValues = finiteNumbers(values);
894
+ const numericBase = {
895
+ dataType,
896
+ totalCount,
897
+ uniqueCount,
898
+ nullCount: missingCount,
899
+ excludedCount: totalCount - missingCount - numericValues.length
900
+ };
901
+ if (numericValues.length === 0) {
902
+ return numericBase;
903
+ }
904
+ const sorted = [...numericValues].sort((a, b) => a - b);
905
+ const min = sorted.at(0);
906
+ const max = sorted.at(-1);
907
+ if (min === void 0 || max === void 0) {
908
+ return numericBase;
909
+ }
910
+ const sum = numericValues.reduce((a, b) => a + b, 0);
911
+ const mean = sum / numericValues.length;
912
+ const lower = sorted[Math.floor((sorted.length - 1) / 2)];
913
+ const upper = sorted[Math.floor(sorted.length / 2)];
914
+ if (lower === void 0 || upper === void 0) {
915
+ return numericBase;
916
+ }
917
+ const median = (lower + upper) / 2;
918
+ const squaredDiffs = numericValues.map((v) => Math.pow(v - mean, 2));
919
+ const variance = squaredDiffs.reduce((a, b) => a + b, 0) / numericValues.length;
920
+ const stdDev = Math.sqrt(variance);
921
+ return {
922
+ ...numericBase,
923
+ statistics: {
924
+ min,
925
+ max,
926
+ mean,
927
+ median,
928
+ stdDev,
929
+ bins: binValues(sorted, min, max)
930
+ }
931
+ };
932
+ } else {
933
+ const counts = /* @__PURE__ */ new Map();
934
+ nonNullValues.forEach(
935
+ (value) => counts.set(value, (counts.get(value) ?? 0) + 1)
936
+ );
937
+ const distribution = [...counts].map(([value, count]) => ({
938
+ value,
939
+ count
940
+ }));
941
+ const topValues = [...distribution].sort((a, b) => b.count - a.count).slice(0, 5);
942
+ return {
943
+ dataType,
944
+ totalCount,
945
+ uniqueCount,
946
+ nullCount,
947
+ categories: {
948
+ topValues,
949
+ distribution
950
+ }
951
+ };
952
+ }
953
+ }
954
+
955
+ // src/lib/fieldProfiles.ts
956
+ function buildFieldProfile(name, columnData, typeOverride) {
957
+ return {
958
+ name,
959
+ ...calculateColumnStatistics(
960
+ columnData,
961
+ typeOverride ?? detectColumnType(columnData)
962
+ )
963
+ };
964
+ }
965
+ function emptyFieldProfile(profile) {
966
+ return {
967
+ name: profile.name,
968
+ dataType: profile.dataType,
969
+ totalCount: 0,
970
+ uniqueCount: 0,
971
+ nullCount: 0,
972
+ statistics: void 0,
973
+ categories: profile.categories ? { topValues: [], distribution: [] } : void 0
974
+ };
975
+ }
976
+ function buildFieldProfiles(rows, typeOverrides2 = {}) {
977
+ const fields = /* @__PURE__ */ new Set();
978
+ for (const row of rows) {
979
+ Object.keys(row).filter((field) => field !== "__ID").forEach((field) => fields.add(field));
980
+ }
981
+ return Array.from(fields, (name) => {
982
+ const columnData = {};
983
+ rows.forEach((row, index) => {
984
+ columnData[index] = row[name];
985
+ });
986
+ return buildFieldProfile(name, columnData, typeOverrides2[name]);
987
+ });
988
+ }
989
+
990
+ // src/lib/aggregates.ts
991
+ function displayAggregateValue(value) {
992
+ if (value === void 0) {
993
+ return "undefined";
994
+ }
995
+ if (value === null) {
996
+ return "null";
997
+ }
998
+ if (typeof value === "string") {
999
+ return JSON.stringify(value);
1000
+ }
1001
+ return String(value);
1002
+ }
1003
+ function numericInputs(values) {
1004
+ const inputs = [];
1005
+ const exclusions = [];
1006
+ values.forEach((value, index) => {
1007
+ const reason = numericExclusionReason(value);
1008
+ if (reason) {
1009
+ exclusions.push({ index, reason });
1010
+ } else {
1011
+ inputs.push({ value: Number(value), index });
1012
+ }
1013
+ });
1014
+ return { inputs, exclusions };
1015
+ }
1016
+ function aggregateValues(aggregation, values) {
1017
+ if (aggregation === "count") {
1018
+ return {
1019
+ value: values.length,
1020
+ included: new Set(values.map((_, index) => index)),
1021
+ exclusions: []
1022
+ };
1023
+ }
1024
+ const { inputs, exclusions } = numericInputs(values);
1025
+ if (inputs.length === 0) {
1026
+ return { value: void 0, included: /* @__PURE__ */ new Set(), exclusions };
1027
+ }
1028
+ const total = inputs.reduce((sum, input) => sum + input.value, 0);
1029
+ return {
1030
+ value: aggregation === "sum" ? total : total / inputs.length,
1031
+ included: new Set(inputs.map((input) => input.index)),
1032
+ exclusions
1033
+ };
1034
+ }
1035
+ function calculateGroupedAggregate(rows, spec, rawInputs = {}, exclusionReasons = {}) {
1036
+ if (spec.aggregation !== "count" && !spec.measureField) {
1037
+ throw new Error(`${spec.aggregation} requires a measure field`);
1038
+ }
1039
+ const groups = /* @__PURE__ */ new Map();
1040
+ rows.forEach((row) => {
1041
+ const value = categoryValue(row[spec.groupField]);
1042
+ const key = categoryKey(value);
1043
+ const group = groups.get(key);
1044
+ if (group) {
1045
+ group.push(row);
1046
+ } else {
1047
+ groups.set(key, [row]);
1048
+ }
1049
+ });
1050
+ const resultRows = Array.from(groups, ([key, group]) => {
1051
+ const values = group.map(
1052
+ (row) => spec.measureField ? row[spec.measureField] : void 0
1053
+ );
1054
+ const result = aggregateValues(spec.aggregation, values);
1055
+ const exclusionByIndex = new Map(
1056
+ result.exclusions.map((exclusion) => [exclusion.index, exclusion.reason])
1057
+ );
1058
+ return {
1059
+ id: `${spec.id}:${key}`,
1060
+ groupValue: categoryValue(group[0]?.[spec.groupField]),
1061
+ groupLabel: categoryLabel(categoryValue(group[0]?.[spec.groupField])),
1062
+ value: result.value,
1063
+ rowCount: group.length,
1064
+ contributors: group.map((row, index) => ({
1065
+ sourceId: row.__ID,
1066
+ input: values[index],
1067
+ rawInput: rawInputs[row.__ID],
1068
+ included: result.included.has(index),
1069
+ exclusionReason: result.included.has(index) ? void 0 : exclusionReasons[row.__ID] ?? exclusionByIndex.get(index)
1070
+ }))
1071
+ };
1072
+ });
1073
+ return { spec, rows: resultRows, sourceRowCount: rows.length };
1074
+ }
1075
+
1076
+ // src/utils/compactChartLayouts.ts
1077
+ function overlaps(a, b) {
1078
+ return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
1079
+ }
1080
+ function compactChartLayouts(items) {
1081
+ const order = items.map((item, index) => ({ item, index })).sort(
1082
+ (a, b) => a.item.layout.y - b.item.layout.y || a.item.layout.x - b.item.layout.x
1083
+ );
1084
+ const placed = [];
1085
+ const result = [...items];
1086
+ for (const { item, index } of order) {
1087
+ const layout = { ...item.layout };
1088
+ while (layout.y > 0 && !placed.some((other) => overlaps(other, { ...layout, y: layout.y - 1 }))) {
1089
+ layout.y -= 1;
1090
+ }
1091
+ placed.push(layout);
1092
+ result[index] = layout.y === item.layout.y ? item : { ...item, layout };
1093
+ }
1094
+ return result;
1095
+ }
1096
+
1097
+ // src/providers/lib/dataLayerState.ts
1098
+ function initializeData(data) {
1099
+ const dataWithIds = data.map((row, index) => ({
1100
+ ...row,
1101
+ __ID: index
1102
+ }));
1103
+ const emptyColumn = dataWithIds.reduce(
1104
+ (acc, row) => {
1105
+ acc[row.__ID] = void 0;
1106
+ return acc;
1107
+ },
1108
+ {}
1109
+ );
1110
+ return { dataWithIds, emptyColumn };
1111
+ }
1112
+ function typeOverrides(settings) {
1113
+ return Object.fromEntries(
1114
+ Object.entries(settings).filter(([, value]) => value.type).map(([field, value]) => [field, value.type])
1115
+ );
1116
+ }
1117
+ function validateFieldSettings(settings) {
1118
+ for (const [field, value] of Object.entries(settings)) {
1119
+ const error = getFieldSettingsError(value);
1120
+ if (error) {
1121
+ throw new Error(`Invalid settings for ${field}: ${error}`);
1122
+ }
1123
+ }
1124
+ }
1125
+ var toVector3 = ({ x, y, z }) => ({
1126
+ x,
1127
+ y,
1128
+ z,
1129
+ toArray: () => [x, y, z],
1130
+ clone: () => toVector3({ x, y, z })
1131
+ });
1132
+ function toRuntimeChart(chart) {
1133
+ if (chart.type !== "3d-scatter") {
1134
+ return chart;
1135
+ }
1136
+ return {
1137
+ ...chart,
1138
+ cameraPosition: toVector3(chart.cameraPosition),
1139
+ cameraTarget: toVector3(chart.cameraTarget)
1140
+ };
1141
+ }
1142
+ function toSavedChart(chart) {
1143
+ if (chart.type !== "3d-scatter") {
1144
+ return chart;
1145
+ }
1146
+ return {
1147
+ ...chart,
1148
+ cameraPosition: {
1149
+ x: chart.cameraPosition.x,
1150
+ y: chart.cameraPosition.y,
1151
+ z: chart.cameraPosition.z
1152
+ },
1153
+ cameraTarget: {
1154
+ x: chart.cameraTarget.x,
1155
+ y: chart.cameraTarget.y,
1156
+ z: chart.cameraTarget.z
1157
+ }
1158
+ };
1159
+ }
1160
+ function toRuntimeCalculations(calculations) {
1161
+ return calculations.map(({ resultColumnName, expression }) => ({
1162
+ resultColumnName,
1163
+ expression: parseExpression(expression)
1164
+ }));
1165
+ }
1166
+ function toSavedCalculations(calculations) {
1167
+ return calculations.map(({ resultColumnName, expression }) => ({
1168
+ resultColumnName,
1169
+ expression: expression.rawInput
1170
+ }));
1171
+ }
1172
+ function validateAggregateState(aggregates, charts, fieldNames) {
1173
+ const ids = /* @__PURE__ */ new Set();
1174
+ const availableFields = fieldNames ? new Set(fieldNames) : void 0;
1175
+ for (const aggregate of aggregates) {
1176
+ if (!aggregate.id || !aggregate.name.trim() || !aggregate.groupField.trim() || !["count", "sum", "average"].includes(aggregate.aggregation) || aggregate.aggregation !== "count" && !aggregate.measureField?.trim()) {
1177
+ throw new Error("Invalid grouped summary definition");
1178
+ }
1179
+ if (availableFields && !availableFields.has(aggregate.groupField)) {
1180
+ throw new Error(
1181
+ `Grouped summary ${aggregate.name} references missing group field ${aggregate.groupField}`
1182
+ );
1183
+ }
1184
+ if (availableFields && aggregate.measureField && !availableFields.has(aggregate.measureField)) {
1185
+ throw new Error(
1186
+ `Grouped summary ${aggregate.name} references missing measure field ${aggregate.measureField}`
1187
+ );
1188
+ }
1189
+ if (ids.has(aggregate.id)) {
1190
+ throw new Error(`Duplicate grouped summary id: ${aggregate.id}`);
1191
+ }
1192
+ ids.add(aggregate.id);
1193
+ }
1194
+ for (const chart of charts) {
1195
+ const aggregateId = chart.aggregateId;
1196
+ if (aggregateId !== void 0 && (typeof aggregateId !== "string" || !ids.has(aggregateId))) {
1197
+ throw new Error(
1198
+ `Chart references missing grouped summary: ${String(aggregateId)}`
1199
+ );
1200
+ }
1201
+ }
1202
+ }
1203
+ function getDefaultRowsSettings(fields, calculationNames = []) {
1204
+ const allFields = [
1205
+ ...fields,
1206
+ ...calculationNames.filter((field) => !fields.includes(field))
1207
+ ];
1208
+ return {
1209
+ columns: allFields.map((field) => ({ id: field, field })),
1210
+ sortDirection: "asc",
1211
+ filters: [],
1212
+ globalSearch: ""
1213
+ };
1214
+ }
1215
+ function getSavedStateFingerprint(savedData) {
1216
+ return JSON.stringify(
1217
+ {
1218
+ ...savedData,
1219
+ metadata: {
1220
+ ...savedData.metadata,
1221
+ modifiedAt: ""
1222
+ }
1223
+ },
1224
+ (_key, value) => {
1225
+ if (value === void 0) {
1226
+ return { __exploreda_fingerprint: "undefined" };
1227
+ }
1228
+ if (typeof value === "number" && !Number.isFinite(value)) {
1229
+ return {
1230
+ __exploreda_fingerprint: Number.isNaN(value) ? "NaN" : value === Infinity ? "Infinity" : "-Infinity"
1231
+ };
1232
+ }
1233
+ return value;
1234
+ }
1235
+ );
1236
+ }
1237
+ function getDataAndCrossfilterWrapper(data, fieldGetter, charts) {
1238
+ const { dataWithIds, emptyColumn } = initializeData(data);
1239
+ const newCrossFilter = new CrossfilterWrapper(
1240
+ dataWithIds,
1241
+ (d) => d.__ID
1242
+ );
1243
+ if (fieldGetter) {
1244
+ newCrossFilter.setFieldGetter(fieldGetter);
1245
+ }
1246
+ return {
1247
+ data: dataWithIds,
1248
+ emptyColumn,
1249
+ crossfilterWrapper: newCrossFilter,
1250
+ charts: [],
1251
+ colorScales: [],
1252
+ calculationManager: new CalculationManager(dataWithIds),
1253
+ calculations: []
1254
+ };
1255
+ }
1256
+ function createDefaultWorkspaceCharts(fieldProfiles) {
1257
+ if (fieldProfiles.length === 0) {
1258
+ return [];
1259
+ }
1260
+ const summary = chartRegistry.has("summary") ? getChartDefinition("summary").createDefaultSettings({
1261
+ x: 0,
1262
+ y: 0,
1263
+ w: 5,
1264
+ h: 6
1265
+ }) : void 0;
1266
+ const table = chartRegistry.has("data-table") ? getChartDefinition("data-table").createDefaultSettings({
1267
+ x: 5,
1268
+ y: 0,
1269
+ w: 7,
1270
+ h: 6
1271
+ }) : void 0;
1272
+ if (table?.type === "data-table") {
1273
+ table.columns = fieldProfiles.map(({ name }) => ({
1274
+ id: name,
1275
+ field: name
1276
+ }));
1277
+ }
1278
+ return [summary, table].filter(
1279
+ (item) => Boolean(item)
1280
+ );
1281
+ }
1282
+ var getInitialStoreState = (initProps) => {
1283
+ const rawData = initProps?.data ?? [];
1284
+ const fieldSettings = initProps?.savedData?.fieldSettings ?? {};
1285
+ validateFieldSettings(fieldSettings);
1286
+ const inferredTypes = Object.fromEntries(
1287
+ buildFieldProfiles(rawData, typeOverrides(fieldSettings)).map((profile) => [
1288
+ profile.name,
1289
+ profile.dataType
1290
+ ])
1291
+ );
1292
+ const runtimeData = applyFieldSettings(rawData, fieldSettings, inferredTypes);
1293
+ const {
1294
+ data: initData,
1295
+ emptyColumn: initialEmptyColumn,
1296
+ crossfilterWrapper,
1297
+ calculationManager: ogCalculationManager
1298
+ } = getDataAndCrossfilterWrapper(runtimeData);
1299
+ const fieldProfiles = buildFieldProfiles(
1300
+ runtimeData,
1301
+ typeOverrides(fieldSettings)
1302
+ );
1303
+ if (!crossfilterWrapper || !initData || !ogCalculationManager) {
1304
+ throw new Error(
1305
+ "Data, crossfilterWrapper, or calculationManager not found"
1306
+ );
1307
+ }
1308
+ if (initProps?.savedData) {
1309
+ const savedData = initProps.savedData;
1310
+ ogCalculationManager.setCalculations(
1311
+ toRuntimeCalculations(savedData.calculations)
1312
+ );
1313
+ const newCalculations = ogCalculationManager.getCalculations();
1314
+ validateAggregateState(savedData.aggregates ?? [], savedData.charts, [
1315
+ ...fieldProfiles.map((profile) => profile.name),
1316
+ ...newCalculations.map((calculation) => calculation.resultColumnName)
1317
+ ]);
1318
+ const restoredColorScales = savedData.colorScales.map(
1319
+ (scale) => {
1320
+ if (scale.type === "categorical") {
1321
+ return {
1322
+ ...scale,
1323
+ mapping: new Map(scale.mapping)
1324
+ };
1325
+ }
1326
+ return scale;
1327
+ }
1328
+ );
1329
+ return {
1330
+ rawData,
1331
+ data: initData,
1332
+ fieldProfiles,
1333
+ emptyColumn: initialEmptyColumn,
1334
+ crossfilterWrapper,
1335
+ calculationManager: ogCalculationManager,
1336
+ calculations: newCalculations,
1337
+ charts: savedData.charts.map(toRuntimeChart),
1338
+ colorScales: restoredColorScales,
1339
+ gridSettings: savedData.gridSettings,
1340
+ rowsSettings: savedData.rowsSettings ?? getDefaultRowsSettings(
1341
+ fieldProfiles.map((profile) => profile.name),
1342
+ newCalculations.map((calculation) => calculation.resultColumnName)
1343
+ ),
1344
+ metadata: savedData.metadata,
1345
+ columnCache: {},
1346
+ calcColumnCache: {},
1347
+ nonce: 0,
1348
+ fileName: void 0,
1349
+ fieldSettings,
1350
+ aggregates: savedData.aggregates ?? []
1351
+ };
1352
+ }
1353
+ return {
1354
+ rawData,
1355
+ data: initData,
1356
+ fieldProfiles,
1357
+ emptyColumn: initialEmptyColumn,
1358
+ crossfilterWrapper,
1359
+ calculationManager: ogCalculationManager,
1360
+ calculations: [],
1361
+ charts: initProps?.charts ?? createDefaultWorkspaceCharts(fieldProfiles),
1362
+ colorScales: [],
1363
+ gridSettings: {
1364
+ columnCount: 12,
1365
+ rowHeight: 100,
1366
+ containerPadding: 10,
1367
+ showBackgroundMarkers: true
1368
+ },
1369
+ rowsSettings: getDefaultRowsSettings(
1370
+ fieldProfiles.map((profile) => profile.name)
1371
+ ),
1372
+ metadata: {
1373
+ name: "Untitled",
1374
+ version: 1,
1375
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1376
+ modifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1377
+ },
1378
+ columnCache: {},
1379
+ calcColumnCache: {},
1380
+ nonce: 0,
1381
+ fileName: void 0,
1382
+ fieldSettings,
1383
+ aggregates: []
1384
+ };
1385
+ };
1386
+ var createDataLayerStore = (initProps) => {
1387
+ const initialState = getInitialStoreState(initProps);
1388
+ const { crossfilterWrapper } = initialState;
1389
+ if (!crossfilterWrapper) {
1390
+ throw new Error("crossfilterWrapper not found in initial state");
1391
+ }
1392
+ const store = createStore()((set, get) => ({
1393
+ ...initialState,
1394
+ liveItems: {},
1395
+ filterReset: 0,
1396
+ setData: (rawData, fileName, useDefaults = true) => {
1397
+ const inferredTypes = Object.fromEntries(
1398
+ buildFieldProfiles(rawData, typeOverrides({})).map((profile) => [
1399
+ profile.name,
1400
+ profile.dataType
1401
+ ])
1402
+ );
1403
+ const runtimeData = applyFieldSettings(rawData, {}, inferredTypes);
1404
+ const {
1405
+ data: newData,
1406
+ emptyColumn: newEmptyColumn,
1407
+ crossfilterWrapper: newCrossfilter,
1408
+ calculationManager: newCalculationManager
1409
+ } = getDataAndCrossfilterWrapper(runtimeData, get().getColumnData);
1410
+ const fieldProfiles = buildFieldProfiles(runtimeData, typeOverrides({}));
1411
+ if (!newData || !newEmptyColumn || !newCrossfilter || !newCalculationManager) {
1412
+ throw new Error("Failed to reset data layer");
1413
+ }
1414
+ const charts = useDefaults ? createDefaultWorkspaceCharts(fieldProfiles) : [];
1415
+ charts.forEach((chart) => newCrossfilter.addChart(chart));
1416
+ set({
1417
+ rawData,
1418
+ data: newData,
1419
+ fieldProfiles,
1420
+ emptyColumn: newEmptyColumn,
1421
+ fileName,
1422
+ crossfilterWrapper: newCrossfilter,
1423
+ calculationManager: newCalculationManager,
1424
+ calculations: [],
1425
+ charts,
1426
+ colorScales: [],
1427
+ rowsSettings: getDefaultRowsSettings(
1428
+ fieldProfiles.map((profile) => profile.name)
1429
+ ),
1430
+ liveItems: newCrossfilter.getAllData(),
1431
+ columnCache: {},
1432
+ calcColumnCache: {},
1433
+ fieldSettings: {},
1434
+ aggregates: [],
1435
+ nonce: get().nonce + 1,
1436
+ filterReset: get().filterReset + 1
1437
+ });
1438
+ },
1439
+ updateFieldSettings: (field, updates) => {
1440
+ const current = get().fieldSettings[field] ?? {};
1441
+ const nextFieldSettings = { ...current, ...updates };
1442
+ const settingsError = getFieldSettingsError(nextFieldSettings);
1443
+ if (settingsError) {
1444
+ throw new Error(settingsError);
1445
+ }
1446
+ const nextFieldSettingsMap = { ...get().fieldSettings };
1447
+ if (Object.keys(nextFieldSettings).length === 0) {
1448
+ delete nextFieldSettingsMap[field];
1449
+ } else {
1450
+ nextFieldSettingsMap[field] = nextFieldSettings;
1451
+ }
1452
+ const typeChanged = current.type !== nextFieldSettings.type || current.datePreset !== nextFieldSettings.datePreset || JSON.stringify(current.nullTokens?.filter(Boolean) ?? []) !== JSON.stringify(nextFieldSettings.nullTokens?.filter(Boolean) ?? []);
1453
+ if (!typeChanged) {
1454
+ set((state) => ({
1455
+ fieldSettings: nextFieldSettingsMap,
1456
+ nonce: state.nonce + 1
1457
+ }));
1458
+ return;
1459
+ }
1460
+ const inferredTypes = Object.fromEntries(
1461
+ buildFieldProfiles(
1462
+ get().rawData,
1463
+ typeOverrides(nextFieldSettingsMap)
1464
+ ).map((profile) => [profile.name, profile.dataType])
1465
+ );
1466
+ const runtimeRows = applyFieldSettings(
1467
+ get().rawData,
1468
+ nextFieldSettingsMap,
1469
+ inferredTypes
1470
+ );
1471
+ const nextData = initializeData(runtimeRows);
1472
+ const nextCrossfilter = new CrossfilterWrapper(
1473
+ nextData.dataWithIds,
1474
+ (row) => row.__ID
1475
+ );
1476
+ const nextCharts = get().charts.map((chart) => ({
1477
+ ...chart,
1478
+ filters: chart.filters.filter((filter) => filter.field !== field),
1479
+ facet: typeChanged && (chart.facet.rowVariable === field || chart.facet.type === "grid" && chart.facet.columnVariable === field) ? { ...chart.facet, visibleFacetIds: void 0 } : chart.facet
1480
+ }));
1481
+ nextCharts.forEach((chart) => nextCrossfilter.addChart(chart));
1482
+ const nextManager = new CalculationManager(
1483
+ nextData.dataWithIds,
1484
+ get().calculations
1485
+ );
1486
+ set((state) => ({
1487
+ fieldSettings: nextFieldSettingsMap,
1488
+ data: nextData.dataWithIds,
1489
+ emptyColumn: nextData.emptyColumn,
1490
+ fieldProfiles: buildFieldProfiles(
1491
+ runtimeRows,
1492
+ typeOverrides(nextFieldSettingsMap)
1493
+ ),
1494
+ crossfilterWrapper: nextCrossfilter,
1495
+ calculationManager: nextManager,
1496
+ charts: nextCharts,
1497
+ rowsSettings: {
1498
+ ...state.rowsSettings,
1499
+ filters: state.rowsSettings.filters.filter(
1500
+ (filter) => filter.field !== field
1501
+ )
1502
+ },
1503
+ columnCache: {},
1504
+ calcColumnCache: {},
1505
+ liveItems: {},
1506
+ nonce: state.nonce + 1,
1507
+ filterReset: state.filterReset + 1
1508
+ }));
1509
+ nextCrossfilter.setFieldGetter(get().getColumnData);
1510
+ nextCharts.forEach((chart) => nextCrossfilter.updateChartFilters(chart));
1511
+ set({ liveItems: nextCrossfilter.getAllData() });
1512
+ },
1513
+ getFieldLabel: (field) => getFieldLabel(field, get().fieldSettings[field]),
1514
+ formatFieldValue: (field, value) => formatFieldValue(field, value, get().fieldSettings[field]),
1515
+ getFieldConversionPreview: (field, previewSettings) => {
1516
+ const rawRows = get().rawData;
1517
+ const rawProfile = buildFieldProfiles(rawRows).find(
1518
+ (profile) => profile.name === field
1519
+ );
1520
+ return buildConversionPreview(
1521
+ field,
1522
+ rawRows,
1523
+ previewSettings ?? get().fieldSettings[field],
1524
+ rawProfile?.dataType ?? "categorical"
1525
+ );
1526
+ },
1527
+ addAggregate: (spec) => {
1528
+ if (!spec.name.trim() || !spec.groupField.trim()) {
1529
+ throw new Error("Bar aggregates need a name and group field");
1530
+ }
1531
+ if (spec.aggregation !== "count" && !spec.measureField?.trim()) {
1532
+ throw new Error(`${spec.aggregation} requires a measure field`);
1533
+ }
1534
+ validateAggregateState(
1535
+ [{ ...spec, id: "new" }],
1536
+ [],
1537
+ get().getColumnNames()
1538
+ );
1539
+ const aggregate = { ...spec, id: crypto.randomUUID() };
1540
+ set((state) => ({
1541
+ aggregates: [...state.aggregates, aggregate],
1542
+ nonce: state.nonce + 1
1543
+ }));
1544
+ return aggregate;
1545
+ },
1546
+ updateAggregate: (id, updates) => {
1547
+ const current = get().aggregates.find((aggregate) => aggregate.id === id);
1548
+ if (!current) {
1549
+ throw new Error("Grouped summary was not found");
1550
+ }
1551
+ const next = { ...current, ...updates };
1552
+ if (!next.name.trim() || !next.groupField.trim()) {
1553
+ throw new Error("Bar aggregates need a name and group field");
1554
+ }
1555
+ if (next.aggregation !== "count" && !next.measureField?.trim()) {
1556
+ throw new Error(`${next.aggregation} requires a measure field`);
1557
+ }
1558
+ validateAggregateState([next], [], get().getColumnNames());
1559
+ set((state) => ({
1560
+ aggregates: state.aggregates.map(
1561
+ (aggregate) => aggregate.id === id ? next : aggregate
1562
+ ),
1563
+ nonce: state.nonce + 1
1564
+ }));
1565
+ },
1566
+ removeAggregate: (id) => {
1567
+ const aggregate = get().aggregates.find((item) => item.id === id);
1568
+ if (!aggregate) {
1569
+ return;
1570
+ }
1571
+ if (get().charts.some(
1572
+ (chart) => chart.aggregateId === id
1573
+ )) {
1574
+ throw new Error(
1575
+ `Remove charts using ${aggregate.name} before deleting the grouped summary`
1576
+ );
1577
+ }
1578
+ set((state) => ({
1579
+ aggregates: state.aggregates.filter((item) => item.id !== id),
1580
+ nonce: state.nonce + 1
1581
+ }));
1582
+ },
1583
+ getAggregate: (id) => get().aggregates.find((item) => item.id === id),
1584
+ getAggregateResult: (id, sourceIds) => {
1585
+ const spec = get().aggregates.find((item) => item.id === id);
1586
+ if (!spec) {
1587
+ return void 0;
1588
+ }
1589
+ const ids = sourceIds ?? get().crossfilterWrapper.getFilteredRowIds();
1590
+ const groupData = get().getColumnData(spec.groupField);
1591
+ const measureData = spec.measureField ? get().getColumnData(spec.measureField) : void 0;
1592
+ const rawRows = get().rawData;
1593
+ const rawInputs = {};
1594
+ const exclusionReasons = {};
1595
+ const calculationField = get().calculations.some(
1596
+ (calculation) => calculation.resultColumnName === spec.measureField
1597
+ );
1598
+ const measureProfile = get().fieldProfiles.find(
1599
+ (profile) => profile.name === spec.measureField
1600
+ );
1601
+ const measureSettings = spec.measureField ? get().fieldSettings[spec.measureField] ?? {} : {};
1602
+ const rows = ids.map((sourceId) => ({
1603
+ __ID: sourceId,
1604
+ [spec.groupField]: groupData[sourceId],
1605
+ ...spec.measureField ? {
1606
+ [spec.measureField]: measureData?.[sourceId]
1607
+ } : {}
1608
+ }));
1609
+ if (spec.measureField) {
1610
+ ids.forEach((sourceId) => {
1611
+ const rawValue = rawRows[sourceId]?.[spec.measureField];
1612
+ if (!calculationField) {
1613
+ rawInputs[sourceId] = rawValue;
1614
+ const conversion = convertFieldValue(
1615
+ rawValue,
1616
+ measureSettings.type ?? measureProfile?.dataType ?? "categorical",
1617
+ measureSettings
1618
+ );
1619
+ if (conversion.error) {
1620
+ exclusionReasons[sourceId] = `Conversion failed: ${conversion.error}`;
1621
+ }
1622
+ }
1623
+ });
1624
+ }
1625
+ return calculateGroupedAggregate(rows, spec, rawInputs, exclusionReasons);
1626
+ },
1627
+ addChart: (chartSettings) => {
1628
+ const { crossfilterWrapper: crossfilterWrapper2 } = get();
1629
+ const newChart = {
1630
+ ...chartSettings,
1631
+ id: crypto.randomUUID()
1632
+ };
1633
+ crossfilterWrapper2.addChart(newChart);
1634
+ set((state) => ({
1635
+ charts: [...state.charts, newChart],
1636
+ liveItems: crossfilterWrapper2.getAllData()
1637
+ }));
1638
+ },
1639
+ removeChart: (chart) => {
1640
+ const { crossfilterWrapper: crossfilterWrapper2 } = get();
1641
+ crossfilterWrapper2.removeChart(chart);
1642
+ set((state) => ({
1643
+ charts: compactChartLayouts(
1644
+ state.charts.filter((ogChart) => ogChart.id !== chart.id)
1645
+ ),
1646
+ liveItems: crossfilterWrapper2.getAllData()
1647
+ }));
1648
+ },
1649
+ removeAllCharts: () => {
1650
+ const { crossfilterWrapper: crossfilterWrapper2 } = get();
1651
+ crossfilterWrapper2.removeAllCharts();
1652
+ set({ charts: [], liveItems: crossfilterWrapper2.getAllData() });
1653
+ },
1654
+ updateChart: (id, settings) => {
1655
+ const { crossfilterWrapper: crossfilterWrapper2 } = get();
1656
+ const chart = get().charts.find((chart2) => chart2.id === id);
1657
+ if (!chart) {
1658
+ return;
1659
+ }
1660
+ const updatedChart = { ...chart, ...settings, id };
1661
+ crossfilterWrapper2.updateChart(updatedChart);
1662
+ set((state) => ({
1663
+ charts: state.charts.map(
1664
+ (chart2) => chart2.id === id ? updatedChart : chart2
1665
+ ),
1666
+ liveItems: crossfilterWrapper2.getAllData()
1667
+ }));
1668
+ },
1669
+ updateChartLayouts: (layouts) => {
1670
+ set((state) => ({
1671
+ charts: state.charts.map((chart) => {
1672
+ const layout = layouts[chart.id];
1673
+ return layout ? { ...chart, layout } : chart;
1674
+ })
1675
+ }));
1676
+ },
1677
+ addColorScale: (scale) => {
1678
+ const newScale = {
1679
+ ...scale,
1680
+ id: crypto.randomUUID()
1681
+ };
1682
+ set((state) => ({
1683
+ colorScales: [...state.colorScales, newScale]
1684
+ }));
1685
+ return newScale;
1686
+ },
1687
+ removeColorScale: (id) => {
1688
+ set((state) => ({
1689
+ colorScales: state.colorScales.filter((scale) => scale.id !== id)
1690
+ }));
1691
+ },
1692
+ updateColorScale: (id, updates) => {
1693
+ set((state) => ({
1694
+ colorScales: state.colorScales.map(
1695
+ (scale) => scale.id === id ? { ...scale, ...updates } : scale
1696
+ )
1697
+ }));
1698
+ },
1699
+ clearAllFilters: () => {
1700
+ const { charts, crossfilterWrapper: crossfilterWrapper2 } = get();
1701
+ const newCharts = charts.map((chart) => ({
1702
+ ...chart,
1703
+ filters: [],
1704
+ ...chart.type === "data-table" ? { globalSearch: "" } : {}
1705
+ }));
1706
+ for (const chart of newCharts) {
1707
+ crossfilterWrapper2.updateChart(chart);
1708
+ }
1709
+ set((state) => ({
1710
+ charts: newCharts,
1711
+ rowsSettings: {
1712
+ ...state.rowsSettings,
1713
+ filters: [],
1714
+ globalSearch: ""
1715
+ },
1716
+ liveItems: crossfilterWrapper2.getAllData(),
1717
+ filterReset: state.filterReset + 1
1718
+ }));
1719
+ },
1720
+ clearFilter: (chart) => {
1721
+ const { updateChart } = get();
1722
+ updateChart(chart.id, { filters: [] });
1723
+ },
1724
+ getLiveItems: (chart) => {
1725
+ const { liveItems } = get();
1726
+ const liveItemsForChart = liveItems[chart.id] ?? void 0;
1727
+ return liveItemsForChart;
1728
+ },
1729
+ getColumnNames() {
1730
+ const { fieldProfiles, calculations } = get();
1731
+ const baseColumns = fieldProfiles.map((profile) => profile.name);
1732
+ const calcFields = calculations.map((calc) => calc.resultColumnName);
1733
+ return [...baseColumns, ...calcFields];
1734
+ },
1735
+ getColumnData(field) {
1736
+ const {
1737
+ columnCache,
1738
+ data,
1739
+ calcColumnCache,
1740
+ calculations,
1741
+ emptyColumn,
1742
+ calculationManager
1743
+ } = get();
1744
+ if (!field) {
1745
+ return emptyColumn;
1746
+ }
1747
+ if (Object.hasOwn(columnCache, field)) {
1748
+ return columnCache[field];
1749
+ }
1750
+ const calculation = calculations.find(
1751
+ (calc) => calc.resultColumnName === field
1752
+ );
1753
+ if (calculation) {
1754
+ if (Object.hasOwn(calcColumnCache, field)) {
1755
+ return calcColumnCache[field];
1756
+ }
1757
+ const resultMap = calculationManager.executeCalculation(calculation);
1758
+ const columnData2 = {};
1759
+ resultMap.forEach((value, key) => {
1760
+ columnData2[key] = value;
1761
+ });
1762
+ Object.defineProperty(calcColumnCache, field, {
1763
+ value: columnData2,
1764
+ enumerable: true,
1765
+ configurable: true
1766
+ });
1767
+ return columnData2;
1768
+ }
1769
+ if (!data.some((row) => field in row)) {
1770
+ return emptyColumn;
1771
+ }
1772
+ const columnData = {};
1773
+ data.forEach((row) => {
1774
+ columnData[row.__ID] = row[field];
1775
+ });
1776
+ Object.defineProperty(columnCache, field, {
1777
+ value: columnData,
1778
+ enumerable: true,
1779
+ configurable: true
1780
+ });
1781
+ return columnData;
1782
+ },
1783
+ // Calculation management
1784
+ addCalculation: async (calculation) => {
1785
+ get().calculationManager.addCalculation(calculation);
1786
+ refreshCalculations();
1787
+ return calculation;
1788
+ },
1789
+ removeCalculation: (name) => {
1790
+ assertUnusedCalculation(name);
1791
+ get().calculationManager.removeCalculation(name);
1792
+ refreshCalculations();
1793
+ },
1794
+ updateCalculation: (name, calculation) => {
1795
+ if (name !== calculation.resultColumnName) {
1796
+ assertUnusedCalculation(name);
1797
+ }
1798
+ get().calculationManager.updateCalculation(name, calculation);
1799
+ refreshCalculations();
1800
+ },
1801
+ updateGridSettings: (settings) => {
1802
+ set((state) => ({
1803
+ gridSettings: {
1804
+ ...state.gridSettings,
1805
+ ...settings
1806
+ }
1807
+ }));
1808
+ },
1809
+ updateRowsSettings: (settings) => {
1810
+ set((state) => ({
1811
+ rowsSettings: { ...state.rowsSettings, ...settings }
1812
+ }));
1813
+ },
1814
+ // Save/Restore functionality
1815
+ saveToStructure: () => {
1816
+ const state = get();
1817
+ const serializedColorScales = state.colorScales.map((scale) => {
1818
+ if (scale.type === "categorical") {
1819
+ return {
1820
+ ...scale,
1821
+ mapping: Array.from(scale.mapping.entries())
1822
+ };
1823
+ }
1824
+ return scale;
1825
+ });
1826
+ return {
1827
+ charts: state.charts.map(toSavedChart),
1828
+ calculations: toSavedCalculations(state.calculations),
1829
+ gridSettings: state.gridSettings,
1830
+ metadata: {
1831
+ ...state.metadata,
1832
+ modifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1833
+ },
1834
+ colorScales: serializedColorScales,
1835
+ rowsSettings: state.rowsSettings,
1836
+ fieldSettings: state.fieldSettings,
1837
+ aggregates: state.aggregates
1838
+ };
1839
+ },
1840
+ restoreFromStructure: (savedData) => {
1841
+ const data = get().rawData;
1842
+ get().restoreAnalysisFromStructure({
1843
+ format: "exploreda-analysis",
1844
+ version: 1,
1845
+ data,
1846
+ settings: savedData
1847
+ });
1848
+ },
1849
+ saveAnalysisToStructure: () => {
1850
+ const state = get();
1851
+ return {
1852
+ format: "exploreda-analysis",
1853
+ version: 1,
1854
+ data: state.rawData,
1855
+ settings: state.saveToStructure()
1856
+ };
1857
+ },
1858
+ restoreAnalysisFromStructure: (savedData) => {
1859
+ const rawData = savedData.data;
1860
+ const fieldSettings = savedData.settings.fieldSettings ?? {};
1861
+ validateFieldSettings(fieldSettings);
1862
+ const inferredTypes = Object.fromEntries(
1863
+ buildFieldProfiles(rawData, typeOverrides(fieldSettings)).map(
1864
+ (profile) => [profile.name, profile.dataType]
1865
+ )
1866
+ );
1867
+ const runtimeData = applyFieldSettings(
1868
+ rawData,
1869
+ fieldSettings,
1870
+ inferredTypes
1871
+ );
1872
+ const next = getDataAndCrossfilterWrapper(runtimeData);
1873
+ const nextData = next.data;
1874
+ const nextCrossfilter = next.crossfilterWrapper;
1875
+ const nextEmptyColumn = next.emptyColumn;
1876
+ if (!nextData || !nextCrossfilter || !nextEmptyColumn || !next.calculationManager) {
1877
+ throw new Error("Failed to restore analysis rows");
1878
+ }
1879
+ const calculations = toRuntimeCalculations(
1880
+ savedData.settings.calculations
1881
+ );
1882
+ const calculationManager = new CalculationManager(nextData, calculations);
1883
+ const fieldProfiles = buildFieldProfiles(
1884
+ runtimeData,
1885
+ typeOverrides(fieldSettings)
1886
+ );
1887
+ validateAggregateState(
1888
+ savedData.settings.aggregates ?? [],
1889
+ savedData.settings.charts,
1890
+ [
1891
+ ...fieldProfiles.map((profile) => profile.name),
1892
+ ...calculations.map((calculation) => calculation.resultColumnName)
1893
+ ]
1894
+ );
1895
+ const charts = savedData.settings.charts.map(toRuntimeChart);
1896
+ const fieldGetter = (field) => {
1897
+ const calculation = calculations.find(
1898
+ (item) => item.resultColumnName === field
1899
+ );
1900
+ if (calculation) {
1901
+ const values = {};
1902
+ calculationManager.executeCalculation(calculation).forEach((value, id) => values[id] = value);
1903
+ return values;
1904
+ }
1905
+ if (!nextData.some((row) => field in row)) {
1906
+ return nextEmptyColumn;
1907
+ }
1908
+ return Object.fromEntries(
1909
+ nextData.map((row) => [row.__ID, row[field]])
1910
+ );
1911
+ };
1912
+ nextCrossfilter.setFieldGetter(fieldGetter);
1913
+ charts.forEach((chart) => nextCrossfilter.addChart(chart));
1914
+ const colorScales = savedData.settings.colorScales.map(
1915
+ (scale) => scale.type === "categorical" ? { ...scale, mapping: new Map(scale.mapping) } : scale
1916
+ );
1917
+ set((state) => ({
1918
+ rawData,
1919
+ data: nextData,
1920
+ fieldProfiles,
1921
+ emptyColumn: nextEmptyColumn,
1922
+ fileName: void 0,
1923
+ crossfilterWrapper: nextCrossfilter,
1924
+ calculationManager,
1925
+ calculations: calculationManager.getCalculations(),
1926
+ charts,
1927
+ colorScales,
1928
+ gridSettings: savedData.settings.gridSettings,
1929
+ rowsSettings: savedData.settings.rowsSettings ?? getDefaultRowsSettings(
1930
+ fieldProfiles.map((profile) => profile.name),
1931
+ calculations.map((calculation) => calculation.resultColumnName)
1932
+ ),
1933
+ metadata: savedData.settings.metadata,
1934
+ liveItems: nextCrossfilter.getAllData(),
1935
+ columnCache: {},
1936
+ calcColumnCache: {},
1937
+ nonce: state.nonce + 1,
1938
+ filterReset: state.filterReset + 1,
1939
+ fieldSettings,
1940
+ aggregates: savedData.settings.aggregates ?? []
1941
+ }));
1942
+ nextCrossfilter.setFieldGetter(get().getColumnData);
1943
+ }
1944
+ }));
1945
+ function assertUnusedCalculation(name) {
1946
+ const { charts, aggregates } = store.getState();
1947
+ if (aggregates.some(
1948
+ (aggregate) => aggregate.groupField === name || aggregate.measureField === name
1949
+ )) {
1950
+ throw new Error(
1951
+ `Remove ${name} from its grouped summaries before renaming or deleting it`
1952
+ );
1953
+ }
1954
+ if (charts.some(
1955
+ (chart) => getChartFields(chart).includes(name) || chart.filters.some((filter) => filter.field === name) || chart.facet.rowVariable === name || chart.facet.type === "grid" && chart.facet.columnVariable === name
1956
+ )) {
1957
+ throw new Error(
1958
+ `Remove ${name} from its charts and colors before renaming or deleting it`
1959
+ );
1960
+ }
1961
+ }
1962
+ function refreshCalculations() {
1963
+ const { calculationManager, crossfilterWrapper: crossfilterWrapper2, charts } = store.getState();
1964
+ const previousCalculations = store.getState().calculations;
1965
+ const calculations = calculationManager.getCalculations();
1966
+ const previousNames = new Set(
1967
+ previousCalculations.map((calculation) => calculation.resultColumnName)
1968
+ );
1969
+ const nextNames = new Set(
1970
+ calculations.map((calculation) => calculation.resultColumnName)
1971
+ );
1972
+ const addedNames = calculations.map((calculation) => calculation.resultColumnName).filter((name) => !previousNames.has(name));
1973
+ store.setState((state) => ({
1974
+ calculations,
1975
+ calcColumnCache: {},
1976
+ rowsSettings: {
1977
+ ...state.rowsSettings,
1978
+ columns: [
1979
+ ...state.rowsSettings.columns.filter(
1980
+ (column) => !previousNames.has(column.field) || nextNames.has(column.field)
1981
+ ),
1982
+ ...addedNames.filter(
1983
+ (name) => !state.rowsSettings.columns.some(
1984
+ (column) => column.field === name
1985
+ )
1986
+ ).map((name) => ({ id: name, field: name }))
1987
+ ]
1988
+ }
1989
+ }));
1990
+ charts.forEach((chart) => crossfilterWrapper2.updateChartFilters(chart));
1991
+ store.setState((state) => ({
1992
+ liveItems: crossfilterWrapper2.getAllData(),
1993
+ nonce: state.nonce + 1
1994
+ }));
1995
+ }
1996
+ crossfilterWrapper.setFieldGetter(store.getState().getColumnData);
1997
+ store.getState().charts.forEach((chart) => {
1998
+ crossfilterWrapper.addChart(chart);
1999
+ });
2000
+ store.setState({ liveItems: crossfilterWrapper.getAllData() });
2001
+ return store;
2002
+ };
2003
+ var DataLayerContext = createContext(null);
2004
+ function DataLayerProvider({
2005
+ children,
2006
+ ...props
2007
+ }) {
2008
+ const storeRef = useRef(null);
2009
+ const propsRef = useRef(props);
2010
+ const onStateChangeRef = useRef(props.onStateChange);
2011
+ const suppressStateChangeRef = useRef(false);
2012
+ const savedStateFingerprintRef = useRef(void 0);
2013
+ onStateChangeRef.current = props.onStateChange;
2014
+ const nextData = props.data;
2015
+ const nextSavedData = props.savedData;
2016
+ if (!storeRef.current) {
2017
+ storeRef.current = createDataLayerStore(props);
2018
+ }
2019
+ useEffect(() => {
2020
+ const store = storeRef.current;
2021
+ const previousProps = propsRef.current;
2022
+ const dataChanged = previousProps.data !== nextData;
2023
+ const savedDataChanged = previousProps.savedData !== nextSavedData;
2024
+ suppressStateChangeRef.current = true;
2025
+ try {
2026
+ if (dataChanged) {
2027
+ store.getState().setData(nextData ?? [], void 0, !nextSavedData);
2028
+ if (nextSavedData) {
2029
+ store.getState().restoreFromStructure(nextSavedData);
2030
+ }
2031
+ } else if (savedDataChanged) {
2032
+ if (nextSavedData) {
2033
+ store.getState().restoreFromStructure(nextSavedData);
2034
+ } else {
2035
+ store.getState().setData(store.getState().rawData);
2036
+ }
2037
+ }
2038
+ } finally {
2039
+ suppressStateChangeRef.current = false;
2040
+ savedStateFingerprintRef.current = getSavedStateFingerprint(
2041
+ store.getState().saveToStructure()
2042
+ );
2043
+ }
2044
+ propsRef.current = { data: nextData, savedData: nextSavedData };
2045
+ }, [nextData, nextSavedData]);
2046
+ useEffect(() => {
2047
+ const store = storeRef.current;
2048
+ savedStateFingerprintRef.current = getSavedStateFingerprint(
2049
+ store.getState().saveToStructure()
2050
+ );
2051
+ return store.subscribe((state) => {
2052
+ const savedData = state.saveToStructure();
2053
+ const nextFingerprint = getSavedStateFingerprint(savedData);
2054
+ if (nextFingerprint === savedStateFingerprintRef.current) {
2055
+ return;
2056
+ }
2057
+ savedStateFingerprintRef.current = nextFingerprint;
2058
+ if (!suppressStateChangeRef.current) {
2059
+ onStateChangeRef.current?.(savedData);
2060
+ }
2061
+ });
2062
+ }, []);
2063
+ return /* @__PURE__ */ jsx(DataLayerContext.Provider, { value: storeRef.current, children });
2064
+ }
2065
+ function useDataLayer(selector) {
2066
+ const store = useContext(DataLayerContext);
2067
+ if (!store) {
2068
+ throw new Error("Missing DataLayerContext.Provider in the tree");
2069
+ }
2070
+ return useStore(store, selector);
2071
+ }
2072
+ function cn(...inputs) {
2073
+ return twMerge(clsx(inputs));
2074
+ }
2075
+ function TooltipProvider({
2076
+ delayDuration = 140,
2077
+ ...props
2078
+ }) {
2079
+ return /* @__PURE__ */ jsx(
2080
+ TooltipPrimitive.Provider,
2081
+ {
2082
+ "data-slot": "tooltip-provider",
2083
+ delayDuration,
2084
+ ...props
2085
+ }
2086
+ );
2087
+ }
2088
+ function Tooltip({
2089
+ ...props
2090
+ }) {
2091
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
2092
+ }
2093
+ function TooltipTrigger({
2094
+ ...props
2095
+ }) {
2096
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
2097
+ }
2098
+ function TooltipContent({
2099
+ className,
2100
+ sideOffset = 4,
2101
+ children,
2102
+ ...props
2103
+ }) {
2104
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
2105
+ TooltipPrimitive.Content,
2106
+ {
2107
+ "data-slot": "tooltip-content",
2108
+ sideOffset,
2109
+ className: cn("eda-soft-tooltip z-50", className),
2110
+ ...props,
2111
+ children
2112
+ }
2113
+ ) });
2114
+ }
2115
+ function ActionTooltip({
2116
+ children,
2117
+ content
2118
+ }) {
2119
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
2120
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
2121
+ /* @__PURE__ */ jsx(TooltipContent, { children: content })
2122
+ ] }) });
2123
+ }
2124
+ var buttonVariants = cva(
2125
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0",
2126
+ {
2127
+ variants: {
2128
+ variant: {
2129
+ default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
2130
+ destructive: "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
2131
+ outline: "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
2132
+ secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
2133
+ ghost: "hover:bg-accent hover:text-accent-foreground",
2134
+ link: "text-primary underline-offset-4 hover:underline"
2135
+ },
2136
+ size: {
2137
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
2138
+ sm: "h-8 rounded-md px-3 has-[>svg]:px-2.5",
2139
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
2140
+ icon: "size-9"
2141
+ }
2142
+ },
2143
+ defaultVariants: {
2144
+ variant: "default",
2145
+ size: "default"
2146
+ }
2147
+ }
2148
+ );
2149
+ function Button({
2150
+ className,
2151
+ variant,
2152
+ size,
2153
+ asChild = false,
2154
+ tooltip,
2155
+ ...props
2156
+ }) {
2157
+ const Comp = asChild ? Slot : "button";
2158
+ const button = /* @__PURE__ */ jsx(
2159
+ Comp,
2160
+ {
2161
+ "data-slot": "button",
2162
+ className: cn(buttonVariants({ variant, size, className })),
2163
+ ...props
2164
+ }
2165
+ );
2166
+ return tooltip ? /* @__PURE__ */ jsx(ActionTooltip, { content: tooltip, children: button }) : button;
2167
+ }
2168
+
2169
+ export { ActionTooltip, Button, CalculationManager, DISTRIBUTION_BINS, DataLayerProvider, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, binValues, buildConversionPreview, buildFieldProfile, buttonVariants, calculateGroupedAggregate, calculationFunctions, categoryEqual, categoryIncludes, categoryKey, categoryLabel, categoryValue, cn, convertFieldValue, dateTimestamp, detectColumnType, displayAggregateValue, emptyFieldProfile, finiteNumber, finiteNumbers, formatFieldValue, getChartAxisFields, getChartAxisLabel, getChartFields, getChartSummary, getChartTitle, getFieldLabel, getFieldSettingsError, hasFieldDisplayFormat, initializeData, isMissingValue, isNumberLike, numericInputs, useDataLayer };
2170
+ //# sourceMappingURL=chunk-JNO7OXCC.js.map
2171
+ //# sourceMappingURL=chunk-JNO7OXCC.js.map