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