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,1074 @@
1
+ 'use strict';
2
+
3
+ var chunkPRMC2A6P_cjs = require('./chunk-PRMC2A6P.cjs');
4
+ var chunkU74A5VK5_cjs = require('./chunk-U74A5VK5.cjs');
5
+ var chunkEU5EFHO5_cjs = require('./chunk-EU5EFHO5.cjs');
6
+ var chunkC7GQT65O_cjs = require('./chunk-C7GQT65O.cjs');
7
+ var chunk3PGYFLE3_cjs = require('./chunk-3PGYFLE3.cjs');
8
+ var chunkMJROUIDZ_cjs = require('./chunk-MJROUIDZ.cjs');
9
+ var chunk5B23JVH3_cjs = require('./chunk-5B23JVH3.cjs');
10
+ var lucideReact = require('lucide-react');
11
+ var react = require('react');
12
+ var reactDom = require('react-dom');
13
+ var jsxRuntime = require('react/jsx-runtime');
14
+
15
+ function TableHeader({ className, ...props }) {
16
+ return /* @__PURE__ */ jsxRuntime.jsx(
17
+ "thead",
18
+ {
19
+ "data-slot": "table-header",
20
+ className: chunk5B23JVH3_cjs.cn("[&_tr]:border-b", className),
21
+ ...props
22
+ }
23
+ );
24
+ }
25
+ function TableBody({ className, ...props }) {
26
+ return /* @__PURE__ */ jsxRuntime.jsx(
27
+ "tbody",
28
+ {
29
+ "data-slot": "table-body",
30
+ className: chunk5B23JVH3_cjs.cn("[&_tr:last-child]:border-0", className),
31
+ ...props
32
+ }
33
+ );
34
+ }
35
+ function TableRow({ className, ...props }) {
36
+ return /* @__PURE__ */ jsxRuntime.jsx(
37
+ "tr",
38
+ {
39
+ "data-slot": "table-row",
40
+ className: chunk5B23JVH3_cjs.cn(
41
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
42
+ className
43
+ ),
44
+ ...props
45
+ }
46
+ );
47
+ }
48
+ function TableHead({ className, ...props }) {
49
+ return /* @__PURE__ */ jsxRuntime.jsx(
50
+ "th",
51
+ {
52
+ "data-slot": "table-head",
53
+ className: chunk5B23JVH3_cjs.cn(
54
+ "text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
55
+ className
56
+ ),
57
+ ...props
58
+ }
59
+ );
60
+ }
61
+ function TableCell({ className, ...props }) {
62
+ return /* @__PURE__ */ jsxRuntime.jsx(
63
+ "td",
64
+ {
65
+ "data-slot": "table-cell",
66
+ className: chunk5B23JVH3_cjs.cn(
67
+ "p-0.5 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
68
+ className
69
+ ),
70
+ ...props
71
+ }
72
+ );
73
+ }
74
+
75
+ // src/components/charts/DataTable/filteredRows.ts
76
+ function getFilteredRows(data, liveItems, settings) {
77
+ const ids = new Set(
78
+ liveItems?.items.filter((item) => item.value > 0).map((item) => item.key)
79
+ );
80
+ const rows = data.filter((row) => ids.has(row.__ID));
81
+ const search = settings.globalSearch.toLowerCase();
82
+ const filtered = rows.filter((row) => {
83
+ if (search && !Object.entries(row).some(
84
+ ([field, value]) => field !== "__ID" && value !== null && value !== void 0 && String(value).toLowerCase().includes(search)
85
+ )) {
86
+ return false;
87
+ }
88
+ return settings.filters.every(
89
+ (filter) => chunkEU5EFHO5_cjs.applyFilter(row[filter.field], filter)
90
+ );
91
+ });
92
+ if (settings.sortBy) {
93
+ const field = settings.sortBy;
94
+ filtered.sort((a, b) => {
95
+ const comparison = compareValues(a[field], b[field]);
96
+ return settings.sortDirection === "desc" ? -comparison : comparison;
97
+ });
98
+ }
99
+ return filtered;
100
+ }
101
+ function isNumeric(value) {
102
+ if (typeof value === "number") {
103
+ return true;
104
+ }
105
+ if (typeof value !== "string") {
106
+ return false;
107
+ }
108
+ return !isNaN(Number(value)) && !isNaN(parseFloat(value));
109
+ }
110
+ function compareValues(a, b) {
111
+ if (a == null && b == null) return 0;
112
+ if (a === null || a === void 0) {
113
+ return 1;
114
+ }
115
+ if (b === null || b === void 0) {
116
+ return -1;
117
+ }
118
+ if (a === b) {
119
+ return 0;
120
+ }
121
+ const aStr = String(a);
122
+ const bStr = String(b);
123
+ if (isNumeric(a) && isNumeric(b)) {
124
+ return Number(a) - Number(b);
125
+ }
126
+ return aStr.localeCompare(bStr, void 0, { numeric: true });
127
+ }
128
+ function DataTableBody({
129
+ settings,
130
+ rows,
131
+ scrollTop = 0,
132
+ viewportHeight = 400
133
+ }) {
134
+ const calculations = chunk5B23JVH3_cjs.useDataLayer((state) => state.calculations) ?? [];
135
+ const manager = chunk5B23JVH3_cjs.useDataLayer((state) => state.calculationManager);
136
+ const data = chunk5B23JVH3_cjs.useDataLayer((state) => state.data);
137
+ const formatFieldValue = chunk5B23JVH3_cjs.useDataLayer((state) => state.formatFieldValue);
138
+ chunk5B23JVH3_cjs.useDataLayer((state) => state.fieldSettings);
139
+ const format = formatFieldValue ?? ((_, value) => String(value ?? "\u2014"));
140
+ const liveItems = chunk5B23JVH3_cjs.useDataLayer((state) => state.getLiveItems(settings));
141
+ const filteredByColumns = react.useMemo(
142
+ () => rows ?? getFilteredRows(data, liveItems, settings),
143
+ [rows, data, liveItems, settings]
144
+ );
145
+ const sortedData = filteredByColumns;
146
+ const start = Math.min(
147
+ Math.max(0, Math.floor((scrollTop - 36) / 30) - 6),
148
+ Math.max(0, sortedData.length - 1)
149
+ );
150
+ const end = Math.min(
151
+ sortedData.length,
152
+ start + Math.ceil(viewportHeight / 30) + 12
153
+ );
154
+ const visibleRows = sortedData.slice(start, end);
155
+ const spacer = (count, key) => count > 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { "aria-hidden": "true", className: "eda-table-spacer", children: /* @__PURE__ */ jsxRuntime.jsx(
156
+ "td",
157
+ {
158
+ colSpan: settings.columns.length,
159
+ style: { height: count * 30, padding: 0, border: 0 }
160
+ }
161
+ ) }, key);
162
+ return /* @__PURE__ */ jsxRuntime.jsxs(TableBody, { children: [
163
+ spacer(start, "before"),
164
+ visibleRows.length > 0 ? visibleRows.map((row, index) => /* @__PURE__ */ jsxRuntime.jsx(TableRow, { "aria-rowindex": start + index + 2, children: settings.columns.map((column, index2) => /* @__PURE__ */ jsxRuntime.jsx(
165
+ TableCell,
166
+ {
167
+ className: index2 === 0 ? "sticky left-0 z-10 bg-background" : "",
168
+ style: {
169
+ width: column.width,
170
+ textAlign: typeof row[column.field] === "number" ? "right" : "left"
171
+ },
172
+ children: calculations.some(
173
+ (calc) => calc.resultColumnName === column.field
174
+ ) ? /* @__PURE__ */ jsxRuntime.jsx(chunkC7GQT65O_cjs.CalculatedFieldBadge, { field: column.field, rowId: row.__ID, children: manager?.getErrors(column.field).has(row.__ID) ? "Error" : format(column.field, row[column.field]) }) : format(column.field, row[column.field])
175
+ },
176
+ column.id
177
+ )) }, String(row.__ID))) : /* @__PURE__ */ jsxRuntime.jsx(TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(TableCell, { colSpan: settings.columns.length, className: "text-center", children: "No rows match the current filters." }) }),
178
+ spacer(sortedData.length - end, "after")
179
+ ] });
180
+ }
181
+ var MAX_VALUE_FILTER_OPTIONS = 10;
182
+ function ColumnFilter({
183
+ columnId,
184
+ columnLabel,
185
+ profile,
186
+ filter,
187
+ onChange,
188
+ onClear,
189
+ local = false
190
+ }) {
191
+ const updateRange = (type, key, raw) => {
192
+ const current = filter?.type === type ? filter : { type, field: profile.name };
193
+ const value = raw === "" ? void 0 : type === "range" ? Number(raw) : raw;
194
+ if (value !== void 0 && type === "range" && !Number.isFinite(value)) {
195
+ return;
196
+ }
197
+ const next = { ...current, [key]: value };
198
+ if ((next.type === "range" || next.type === "date-range") && next.min === void 0 && next.max === void 0) {
199
+ onChange(columnId);
200
+ } else {
201
+ onChange(columnId, next);
202
+ }
203
+ };
204
+ const updateValues = (value, checked) => {
205
+ const selected = filter?.type === "value" ? filter.values : [];
206
+ const next = checked ? [...selected, value] : selected.filter((item) => !chunk5B23JVH3_cjs.categoryEqual(item, value));
207
+ onChange(
208
+ columnId,
209
+ next.length > 0 ? { type: "value", field: profile.name, values: next } : void 0
210
+ );
211
+ };
212
+ const textFilter = filter?.type === "text" ? filter : { type: "text", field: profile.name, operator: "contains", value: "" };
213
+ const rangeFilter = filter?.type === "range" ? filter : void 0;
214
+ const dateFilter = filter?.type === "date-range" ? filter : void 0;
215
+ const lowCardinality = profile.dataType === "boolean" || profile.dataType === "categorical" && profile.uniqueCount <= MAX_VALUE_FILTER_OPTIONS;
216
+ const updateText = (next) => onChange(columnId, next.value === "" ? void 0 : next);
217
+ return /* @__PURE__ */ jsxRuntime.jsxs(
218
+ "div",
219
+ {
220
+ className: "grid w-64 max-w-full gap-3 text-sm",
221
+ onClick: (event) => event.stopPropagation(),
222
+ children: [
223
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
224
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "font-semibold", children: [
225
+ "Filter ",
226
+ columnLabel
227
+ ] }),
228
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
229
+ local ? "Filters only this table." : "Filters all charts and rows.",
230
+ " ",
231
+ "Changes apply immediately."
232
+ ] })
233
+ ] }),
234
+ lowCardinality ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-h-56 flex-col gap-2 overflow-auto py-1", children: [
235
+ (profile.categories?.distribution ?? []).map(({ value }) => {
236
+ const label = chunk5B23JVH3_cjs.categoryLabel(value);
237
+ return /* @__PURE__ */ jsxRuntime.jsxs(
238
+ "label",
239
+ {
240
+ className: "flex items-center gap-2 text-sm",
241
+ children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(
243
+ "input",
244
+ {
245
+ type: "checkbox",
246
+ checked: filter?.type === "value" && chunk5B23JVH3_cjs.categoryIncludes(filter.values, value),
247
+ onChange: (event) => updateValues(value, event.currentTarget.checked)
248
+ }
249
+ ),
250
+ label
251
+ ]
252
+ },
253
+ chunk5B23JVH3_cjs.categoryKey(value)
254
+ );
255
+ }),
256
+ profile.nullCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
257
+ /* @__PURE__ */ jsxRuntime.jsx(
258
+ "input",
259
+ {
260
+ type: "checkbox",
261
+ checked: filter?.type === "value" && filter.values.some((item) => item == null),
262
+ onChange: (event) => updateValues(null, event.currentTarget.checked)
263
+ }
264
+ ),
265
+ "Missing"
266
+ ] })
267
+ ] }) : profile.dataType === "numeric" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
268
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
269
+ "Minimum (inclusive)",
270
+ /* @__PURE__ */ jsxRuntime.jsx(
271
+ chunkMJROUIDZ_cjs.Input,
272
+ {
273
+ type: "number",
274
+ "aria-label": `Minimum ${columnLabel}`,
275
+ placeholder: "Min",
276
+ value: rangeFilter?.min ?? "",
277
+ onChange: (event) => updateRange("range", "min", event.target.value),
278
+ className: "h-8 w-full"
279
+ }
280
+ )
281
+ ] }),
282
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
283
+ "Maximum (inclusive)",
284
+ /* @__PURE__ */ jsxRuntime.jsx(
285
+ chunkMJROUIDZ_cjs.Input,
286
+ {
287
+ type: "number",
288
+ "aria-label": `Maximum ${columnLabel}`,
289
+ placeholder: "Max",
290
+ value: rangeFilter?.max ?? "",
291
+ onChange: (event) => updateRange("range", "max", event.target.value),
292
+ className: "h-8 w-full"
293
+ }
294
+ )
295
+ ] })
296
+ ] }) : profile.dataType === "datetime" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
297
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
298
+ "From (inclusive)",
299
+ /* @__PURE__ */ jsxRuntime.jsx(
300
+ chunkMJROUIDZ_cjs.Input,
301
+ {
302
+ type: "date",
303
+ "aria-label": `Start date ${columnLabel}`,
304
+ value: dateFilter?.min ?? "",
305
+ onInput: (event) => updateRange("date-range", "min", event.currentTarget.value),
306
+ className: "h-8 w-full"
307
+ }
308
+ )
309
+ ] }),
310
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
311
+ "Through (inclusive)",
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ chunkMJROUIDZ_cjs.Input,
314
+ {
315
+ type: "date",
316
+ "aria-label": `End date ${columnLabel}`,
317
+ value: dateFilter?.max ?? "",
318
+ onInput: (event) => updateRange("date-range", "max", event.currentTarget.value),
319
+ className: "h-8 w-full"
320
+ }
321
+ )
322
+ ] })
323
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
324
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
325
+ "Match",
326
+ /* @__PURE__ */ jsxRuntime.jsxs(
327
+ "select",
328
+ {
329
+ "aria-label": `Text operator ${columnLabel}`,
330
+ value: textFilter.operator,
331
+ onChange: (event) => updateText({
332
+ ...textFilter,
333
+ operator: event.target.value
334
+ }),
335
+ className: "h-8 rounded-md border bg-transparent px-2 text-sm",
336
+ children: [
337
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "contains", children: "Contains" }),
338
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "equals", children: "Equals" }),
339
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "startsWith", children: "Starts with" }),
340
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "endsWith", children: "Ends with" })
341
+ ]
342
+ }
343
+ )
344
+ ] }),
345
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "grid gap-1 text-xs", children: [
346
+ "Text",
347
+ /* @__PURE__ */ jsxRuntime.jsx(
348
+ chunkMJROUIDZ_cjs.Input,
349
+ {
350
+ "aria-label": `Filter text ${columnLabel}`,
351
+ placeholder: `Filter ${columnLabel}...`,
352
+ value: textFilter.value,
353
+ onChange: (event) => updateText({ ...textFilter, value: event.target.value }),
354
+ className: "h-8 w-full"
355
+ }
356
+ )
357
+ ] })
358
+ ] }),
359
+ /* @__PURE__ */ jsxRuntime.jsxs(
360
+ chunk5B23JVH3_cjs.Button,
361
+ {
362
+ variant: "ghost",
363
+ size: "sm",
364
+ className: "justify-self-start",
365
+ "aria-label": `Clear filter for ${columnLabel}`,
366
+ onClick: onClear,
367
+ children: [
368
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FilterX, { className: "h-4 w-4" }),
369
+ " Clear filter"
370
+ ]
371
+ }
372
+ )
373
+ ]
374
+ }
375
+ );
376
+ }
377
+ function DataTableHeader({
378
+ settings,
379
+ onSettingsChange,
380
+ onColumnResize,
381
+ localFilters = false
382
+ }) {
383
+ const { columns, sortBy, sortDirection, filters } = settings;
384
+ const updateChart = chunk5B23JVH3_cjs.useDataLayer((state) => state.updateChart);
385
+ const update = onSettingsChange ?? ((next) => updateChart(settings.id, next));
386
+ const fieldProfiles = chunk5B23JVH3_cjs.useDataLayer((state) => state.fieldProfiles) ?? [];
387
+ const calculations = chunk5B23JVH3_cjs.useDataLayer((state) => state.calculations) ?? [];
388
+ const getFieldLabel = chunk5B23JVH3_cjs.useDataLayer((state) => state.getFieldLabel);
389
+ const fieldSettings = chunk5B23JVH3_cjs.useDataLayer((state) => state.fieldSettings);
390
+ const label = getFieldLabel ?? ((field) => field);
391
+ const getColumnData = chunk5B23JVH3_cjs.useDataLayer((state) => state.getColumnData);
392
+ const nonce = chunk5B23JVH3_cjs.useDataLayer((state) => state.nonce);
393
+ const profiles = react.useMemo(
394
+ () => [
395
+ ...fieldProfiles,
396
+ ...calculations.filter(
397
+ (calc) => columns.some((column) => column.field === calc.resultColumnName)
398
+ ).map(
399
+ (calc) => chunk5B23JVH3_cjs.buildFieldProfile(
400
+ calc.resultColumnName,
401
+ getColumnData(calc.resultColumnName)
402
+ )
403
+ )
404
+ ],
405
+ [fieldProfiles, calculations, columns, getColumnData, nonce, fieldSettings]
406
+ );
407
+ const [activeFilter, setActiveFilter] = react.useState(null);
408
+ const [resizingColumn, setResizingColumn] = react.useState(null);
409
+ const [tempWidths, setTempWidths] = react.useState({});
410
+ const resizeCleanup = react.useRef(null);
411
+ react.useEffect(() => () => resizeCleanup.current?.(), []);
412
+ const handleSort = (field) => {
413
+ if (sortBy === field) {
414
+ update({
415
+ sortDirection: sortDirection === "asc" ? "desc" : "asc"
416
+ });
417
+ } else {
418
+ update({
419
+ sortBy: field,
420
+ sortDirection: "asc"
421
+ });
422
+ }
423
+ };
424
+ const handleFilterChange = (columnId, filter) => {
425
+ const column = columns.find((col) => col.id === columnId);
426
+ if (!column) {
427
+ return;
428
+ }
429
+ const newFilters = filters.filter((f) => f.field !== column.field);
430
+ if (filter) {
431
+ newFilters.push({ ...filter, field: column.field });
432
+ }
433
+ update({
434
+ filters: newFilters
435
+ });
436
+ };
437
+ const handleFilterClear = (columnId) => {
438
+ const column = columns.find((col) => col.id === columnId);
439
+ if (!column) {
440
+ return;
441
+ }
442
+ const newFilters = filters.filter((f) => f.field !== column.field);
443
+ update({
444
+ filters: newFilters
445
+ });
446
+ };
447
+ const handleResizeStart = (e, columnId) => {
448
+ e.preventDefault();
449
+ e.stopPropagation();
450
+ e.currentTarget.setPointerCapture?.(e.pointerId);
451
+ resizeCleanup.current?.();
452
+ setResizingColumn(columnId);
453
+ const startX = e.clientX;
454
+ const column = columns.find((col) => col.id === columnId);
455
+ const startWidth = e.currentTarget.parentElement?.getBoundingClientRect().width || column?.width || 120;
456
+ let width = startWidth;
457
+ const handleMouseMove = (e2) => {
458
+ width = Math.max(50, startWidth + e2.clientX - startX);
459
+ setTempWidths((prev) => ({ ...prev, [columnId]: width }));
460
+ onColumnResize?.(columnId, width);
461
+ };
462
+ const handleCancel = () => resizeCleanup.current?.();
463
+ const handleMouseUp = () => {
464
+ const newColumns = columns.map(
465
+ (col) => col.id === columnId ? { ...col, width } : col
466
+ );
467
+ update({ columns: newColumns });
468
+ resizeCleanup.current = null;
469
+ setResizingColumn(null);
470
+ setTempWidths({});
471
+ onColumnResize?.(columnId, null);
472
+ window.removeEventListener("pointermove", handleMouseMove);
473
+ window.removeEventListener("pointerup", handleMouseUp);
474
+ window.removeEventListener("pointercancel", handleCancel);
475
+ };
476
+ resizeCleanup.current = () => {
477
+ window.removeEventListener("pointermove", handleMouseMove);
478
+ window.removeEventListener("pointerup", handleMouseUp);
479
+ window.removeEventListener("pointercancel", handleCancel);
480
+ setResizingColumn(null);
481
+ setTempWidths({});
482
+ onColumnResize?.(columnId, null);
483
+ resizeCleanup.current = null;
484
+ };
485
+ window.addEventListener("pointermove", handleMouseMove);
486
+ window.addEventListener("pointerup", handleMouseUp);
487
+ window.addEventListener("pointercancel", handleCancel);
488
+ };
489
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow, { children: columns.map((column, index) => {
490
+ const profile = profiles.find(
491
+ (fieldProfile) => fieldProfile.name === column.field
492
+ ) ?? {
493
+ name: column.field,
494
+ dataType: "categorical",
495
+ totalCount: 0,
496
+ uniqueCount: 11,
497
+ nullCount: 0
498
+ };
499
+ const filter = filters.find(
500
+ (f) => f.field === column.field
501
+ );
502
+ return /* @__PURE__ */ jsxRuntime.jsxs(
503
+ TableHead,
504
+ {
505
+ className: `relative select-none ${index === 0 ? "sticky left-0 z-20 bg-background" : ""}`,
506
+ style: {
507
+ width: resizingColumn === column.id ? tempWidths[column.id] || column.width : column.width
508
+ },
509
+ "aria-sort": sortBy === column.field ? sortDirection === "asc" ? "ascending" : "descending" : "none",
510
+ children: [
511
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "eda-column-heading", children: [
512
+ /* @__PURE__ */ jsxRuntime.jsxs(
513
+ "button",
514
+ {
515
+ type: "button",
516
+ className: "flex min-w-0 flex-1 items-center gap-1 text-left",
517
+ "aria-label": `Sort by ${column.field}`,
518
+ onClick: () => handleSort(column.field),
519
+ children: [
520
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label(column.field) }),
521
+ sortBy === column.field && (sortDirection === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" }))
522
+ ]
523
+ }
524
+ ),
525
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "eda-column-actions", children: [
526
+ /* @__PURE__ */ jsxRuntime.jsx(chunkC7GQT65O_cjs.CalculatedFieldBadge, { field: column.field }),
527
+ /* @__PURE__ */ jsxRuntime.jsx(chunkPRMC2A6P_cjs.FieldInspector, { field: column.field, children: /* @__PURE__ */ jsxRuntime.jsx(
528
+ chunk5B23JVH3_cjs.Button,
529
+ {
530
+ variant: "ghost",
531
+ size: "icon",
532
+ className: "h-6 w-6",
533
+ "aria-label": `Inspect ${column.field}`,
534
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { className: "h-3.5 w-3.5" })
535
+ }
536
+ ) }),
537
+ /* @__PURE__ */ jsxRuntime.jsxs(
538
+ chunkMJROUIDZ_cjs.Popover,
539
+ {
540
+ open: activeFilter === column.id,
541
+ onOpenChange: (open) => setActiveFilter(open ? column.id : null),
542
+ children: [
543
+ /* @__PURE__ */ jsxRuntime.jsx(chunk5B23JVH3_cjs.ActionTooltip, { content: `Filter ${label(column.field)}`, children: /* @__PURE__ */ jsxRuntime.jsx(chunkMJROUIDZ_cjs.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
544
+ chunk5B23JVH3_cjs.Button,
545
+ {
546
+ variant: "ghost",
547
+ size: "sm",
548
+ className: `eda-column-filter ${filter ? "is-active" : ""}`,
549
+ "aria-label": `Filter ${column.field}`,
550
+ "aria-expanded": activeFilter === column.id,
551
+ onClick: (event) => {
552
+ event.stopPropagation();
553
+ setActiveFilter(
554
+ activeFilter === column.id ? null : column.id
555
+ );
556
+ },
557
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "h-4 w-4" })
558
+ }
559
+ ) }) }),
560
+ /* @__PURE__ */ jsxRuntime.jsx(
561
+ chunkMJROUIDZ_cjs.PopoverContent,
562
+ {
563
+ className: "w-auto max-w-[calc(100vw-24px)]",
564
+ align: "start",
565
+ collisionPadding: 12,
566
+ children: /* @__PURE__ */ jsxRuntime.jsx(
567
+ ColumnFilter,
568
+ {
569
+ local: localFilters,
570
+ columnId: column.id,
571
+ columnLabel: label(column.field),
572
+ profile,
573
+ filter,
574
+ onChange: handleFilterChange,
575
+ onClear: () => handleFilterClear(column.id)
576
+ }
577
+ )
578
+ }
579
+ )
580
+ ]
581
+ }
582
+ )
583
+ ] })
584
+ ] }),
585
+ /* @__PURE__ */ jsxRuntime.jsx(
586
+ "div",
587
+ {
588
+ role: "separator",
589
+ "aria-orientation": "vertical",
590
+ "aria-label": `Resize ${column.field} column`,
591
+ className: "eda-column-resize",
592
+ "data-resizing": resizingColumn === column.id,
593
+ tabIndex: 0,
594
+ "aria-valuemin": 50,
595
+ "aria-valuenow": tempWidths[column.id] ?? column.width ?? 120,
596
+ onKeyDown: (event) => {
597
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") {
598
+ return;
599
+ }
600
+ event.preventDefault();
601
+ const width = Math.max(
602
+ 50,
603
+ event.currentTarget.parentElement.getBoundingClientRect().width + (event.key === "ArrowRight" ? 16 : -16)
604
+ );
605
+ update({
606
+ columns: columns.map(
607
+ (item) => item.id === column.id ? { ...item, width } : item
608
+ )
609
+ });
610
+ },
611
+ onPointerDown: (e) => handleResizeStart(e, column.id)
612
+ }
613
+ )
614
+ ]
615
+ },
616
+ column.id
617
+ );
618
+ }) }) }) });
619
+ }
620
+ function DataTableToolbar({
621
+ settings,
622
+ rows,
623
+ onSettingsChange,
624
+ compact = false,
625
+ localFilters = false
626
+ }) {
627
+ const [fieldSearch, setFieldSearch] = react.useState("");
628
+ const getColumnData = chunk5B23JVH3_cjs.useDataLayer((state) => state.getColumnData);
629
+ const fieldProfiles = chunk5B23JVH3_cjs.useDataLayer((state) => state.fieldProfiles);
630
+ const getFieldLabel = chunk5B23JVH3_cjs.useDataLayer((state) => state.getFieldLabel);
631
+ const updateChart = chunk5B23JVH3_cjs.useDataLayer((state) => state.updateChart);
632
+ const data = chunk5B23JVH3_cjs.useDataLayer((state) => state.data);
633
+ const liveItems = chunk5B23JVH3_cjs.useDataLayer((state) => state.getLiveItems(settings));
634
+ const getColumnNames = chunk5B23JVH3_cjs.useDataLayer((state) => state.getColumnNames);
635
+ const filteredData = rows ?? getFilteredRows(data, liveItems, settings);
636
+ const handleSearch = (globalSearch) => {
637
+ if (onSettingsChange) onSettingsChange({ globalSearch });
638
+ else updateChart(settings.id, { globalSearch });
639
+ };
640
+ const handleExport = () => {
641
+ const csvContent = toTableCsv(
642
+ filteredData,
643
+ settings.columns.map((column) => column.field)
644
+ );
645
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
646
+ const link = document.createElement("a");
647
+ const url = URL.createObjectURL(blob);
648
+ link.setAttribute("href", url);
649
+ link.setAttribute(
650
+ "download",
651
+ `table-export-${(/* @__PURE__ */ new Date()).toISOString()}.csv`
652
+ );
653
+ link.style.visibility = "hidden";
654
+ document.body.appendChild(link);
655
+ link.click();
656
+ document.body.removeChild(link);
657
+ URL.revokeObjectURL(url);
658
+ };
659
+ const search = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "eda-table-search relative min-w-0", children: [
660
+ /* @__PURE__ */ jsxRuntime.jsx(
661
+ chunkMJROUIDZ_cjs.Input,
662
+ {
663
+ className: "h-8 text-xs",
664
+ placeholder: "Search this table\u2026",
665
+ "aria-label": "Search table",
666
+ value: settings.globalSearch,
667
+ onChange: (event) => handleSearch(event.target.value)
668
+ }
669
+ ),
670
+ settings.globalSearch && /* @__PURE__ */ jsxRuntime.jsx(
671
+ chunk5B23JVH3_cjs.Button,
672
+ {
673
+ variant: "ghost",
674
+ size: "icon",
675
+ className: "absolute right-0 top-0 h-8 w-8",
676
+ "aria-label": "Clear table search",
677
+ onClick: () => handleSearch(""),
678
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
679
+ }
680
+ )
681
+ ] });
682
+ return /* @__PURE__ */ jsxRuntime.jsxs(
683
+ "div",
684
+ {
685
+ className: compact ? "eda-table-toolbar-compact" : "eda-table-toolbar",
686
+ children: [
687
+ !compact && search,
688
+ localFilters && /* @__PURE__ */ jsxRuntime.jsxs(chunkMJROUIDZ_cjs.Popover, { children: [
689
+ /* @__PURE__ */ jsxRuntime.jsx(chunkMJROUIDZ_cjs.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
690
+ chunk5B23JVH3_cjs.Button,
691
+ {
692
+ variant: "ghost",
693
+ size: "sm",
694
+ className: "eda-columns-trigger",
695
+ "aria-label": "Choose visible columns",
696
+ children: [
697
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Columns3, { className: "h-3.5 w-3.5" }),
698
+ " Columns",
699
+ " ",
700
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: settings.columns.length })
701
+ ]
702
+ }
703
+ ) }),
704
+ /* @__PURE__ */ jsxRuntime.jsxs(
705
+ chunkMJROUIDZ_cjs.PopoverContent,
706
+ {
707
+ align: "end",
708
+ "aria-label": "Visible columns",
709
+ className: "w-96 space-y-3",
710
+ children: [
711
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
712
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold", children: "Visible columns" }),
713
+ /* @__PURE__ */ jsxRuntime.jsx(
714
+ chunk5B23JVH3_cjs.Button,
715
+ {
716
+ variant: "ghost",
717
+ size: "sm",
718
+ onClick: () => onSettingsChange?.({
719
+ columns: getColumnNames().filter((field) => field !== "__ID").map(
720
+ (field) => settings.columns.find(
721
+ (column) => column.field === field
722
+ ) ?? { id: field, field }
723
+ )
724
+ }),
725
+ children: "Show all"
726
+ }
727
+ )
728
+ ] }),
729
+ /* @__PURE__ */ jsxRuntime.jsx(
730
+ chunkMJROUIDZ_cjs.Input,
731
+ {
732
+ "aria-label": "Find columns",
733
+ placeholder: "Find a column\u2026",
734
+ value: fieldSearch,
735
+ onChange: (event) => setFieldSearch(event.target.value)
736
+ }
737
+ ),
738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid max-h-72 gap-1 overflow-y-auto", children: getColumnNames().filter(
739
+ (field) => field !== "__ID" && field.toLowerCase().includes(fieldSearch.toLowerCase())
740
+ ).map((field) => {
741
+ const checked = settings.columns.some(
742
+ (column) => column.field === field
743
+ );
744
+ return /* @__PURE__ */ jsxRuntime.jsxs(
745
+ "label",
746
+ {
747
+ className: "flex min-w-0 cursor-pointer items-center gap-2 rounded px-2 py-2 hover:bg-muted",
748
+ children: [
749
+ /* @__PURE__ */ jsxRuntime.jsx(
750
+ "input",
751
+ {
752
+ type: "checkbox",
753
+ checked,
754
+ "aria-label": getFieldLabel(field),
755
+ onChange: (event) => onSettingsChange?.({
756
+ columns: event.target.checked ? [...settings.columns, { id: field, field }] : settings.columns.filter(
757
+ (column) => column.field !== field
758
+ )
759
+ })
760
+ }
761
+ ),
762
+ /* @__PURE__ */ jsxRuntime.jsx(
763
+ chunkC7GQT65O_cjs.FieldMetadata,
764
+ {
765
+ profile: chunkC7GQT65O_cjs.resolveFieldProfile(
766
+ field,
767
+ fieldProfiles,
768
+ getColumnData
769
+ ),
770
+ label: getFieldLabel(field),
771
+ compact: true
772
+ }
773
+ )
774
+ ]
775
+ },
776
+ field
777
+ );
778
+ }) })
779
+ ]
780
+ }
781
+ )
782
+ ] }),
783
+ /* @__PURE__ */ jsxRuntime.jsxs(
784
+ "span",
785
+ {
786
+ className: "ml-auto whitespace-nowrap text-muted-foreground tabular-nums",
787
+ "aria-live": "polite",
788
+ children: [
789
+ filteredData.length.toLocaleString(),
790
+ " ",
791
+ filteredData.length === 1 ? "row" : "rows"
792
+ ]
793
+ }
794
+ ),
795
+ compact && /* @__PURE__ */ jsxRuntime.jsxs(chunkMJROUIDZ_cjs.Popover, { children: [
796
+ /* @__PURE__ */ jsxRuntime.jsx(chunk5B23JVH3_cjs.ActionTooltip, { content: "Search rows", children: /* @__PURE__ */ jsxRuntime.jsx(chunkMJROUIDZ_cjs.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
797
+ chunk5B23JVH3_cjs.Button,
798
+ {
799
+ variant: "ghost",
800
+ size: "icon",
801
+ "aria-label": settings.globalSearch ? `Search rows: ${settings.globalSearch}` : "Search rows",
802
+ className: settings.globalSearch ? "text-primary bg-accent" : "",
803
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "h-3.5 w-3.5" })
804
+ }
805
+ ) }) }),
806
+ /* @__PURE__ */ jsxRuntime.jsx(
807
+ chunkMJROUIDZ_cjs.PopoverContent,
808
+ {
809
+ align: "end",
810
+ className: "eda-table-search-popover w-64 p-2",
811
+ children: search
812
+ }
813
+ )
814
+ ] }),
815
+ /* @__PURE__ */ jsxRuntime.jsx(chunk5B23JVH3_cjs.ActionTooltip, { content: "Export rows as CSV", children: /* @__PURE__ */ jsxRuntime.jsx(
816
+ chunk5B23JVH3_cjs.Button,
817
+ {
818
+ variant: "ghost",
819
+ size: "icon",
820
+ onClick: handleExport,
821
+ "aria-label": "Export rows as CSV",
822
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3.5 w-3.5" })
823
+ }
824
+ ) })
825
+ ]
826
+ }
827
+ );
828
+ }
829
+ function toTableCsv(rows, fields) {
830
+ const cell = (value) => {
831
+ const text = String(value ?? "");
832
+ return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
833
+ };
834
+ return [
835
+ fields.map(cell).join(","),
836
+ ...rows.map((row) => fields.map((field) => cell(row[field])).join(","))
837
+ ].join("\n");
838
+ }
839
+ function DataTable({
840
+ settings,
841
+ height,
842
+ width,
843
+ rows,
844
+ onSettingsChange,
845
+ toolbarTarget
846
+ }) {
847
+ const data = chunk5B23JVH3_cjs.useDataLayer((state) => state.data);
848
+ const liveItems = chunk5B23JVH3_cjs.useDataLayer((state) => state.getLiveItems(settings));
849
+ const updateChart = chunk5B23JVH3_cjs.useDataLayer((state) => state.updateChart);
850
+ const getColumnData = chunk5B23JVH3_cjs.useDataLayer((state) => state.getColumnData);
851
+ const calculations = chunk5B23JVH3_cjs.useDataLayer((state) => state.calculations);
852
+ const nonce = chunk5B23JVH3_cjs.useDataLayer((state) => state.nonce);
853
+ const resolvedRows = react.useMemo(() => {
854
+ const source = rows ?? data;
855
+ if (!calculations.length) {
856
+ return source;
857
+ }
858
+ const columns = calculations.map(
859
+ (calc) => [calc.resultColumnName, getColumnData(calc.resultColumnName)]
860
+ );
861
+ return source.map((row) => ({
862
+ ...row,
863
+ ...Object.fromEntries(
864
+ columns.map(([name, values]) => [name, values[row.__ID]])
865
+ )
866
+ }));
867
+ }, [rows, data, calculations, getColumnData, nonce]);
868
+ const scrollRef = react.useRef(null);
869
+ const [scrollTop, setScrollTop] = react.useState(0);
870
+ const [columnWidths, setColumnWidths] = react.useState({});
871
+ const update = onSettingsChange ?? ((next) => updateChart(settings.id, next));
872
+ const filteredRows = react.useMemo(
873
+ () => getFilteredRows(
874
+ resolvedRows,
875
+ rows ? {
876
+ items: rows.map((row) => ({ key: row.__ID, value: 1 }))} : liveItems,
877
+ settings
878
+ ),
879
+ [resolvedRows, rows, liveItems, settings]
880
+ );
881
+ react.useEffect(() => {
882
+ if (scrollRef.current) {
883
+ scrollRef.current.scrollTop = 0;
884
+ }
885
+ setScrollTop(0);
886
+ }, [
887
+ settings.globalSearch,
888
+ settings.filters,
889
+ settings.sortBy,
890
+ settings.sortDirection,
891
+ filteredRows.length
892
+ ]);
893
+ const toolbar = /* @__PURE__ */ jsxRuntime.jsx(
894
+ DataTableToolbar,
895
+ {
896
+ settings,
897
+ rows: filteredRows,
898
+ onSettingsChange: update,
899
+ compact: toolbarTarget !== void 0,
900
+ localFilters: rows !== void 0
901
+ }
902
+ );
903
+ return /* @__PURE__ */ jsxRuntime.jsxs(
904
+ "div",
905
+ {
906
+ className: "eda-table-view flex min-h-0 flex-col w-full",
907
+ style: { height },
908
+ children: [
909
+ toolbarTarget === void 0 ? toolbar : toolbarTarget && reactDom.createPortal(toolbar, toolbarTarget),
910
+ /* @__PURE__ */ jsxRuntime.jsx(
911
+ "div",
912
+ {
913
+ ref: scrollRef,
914
+ className: "eda-table-scroll relative min-h-0 flex-1 overflow-auto",
915
+ tabIndex: 0,
916
+ "aria-label": "Data rows, scroll to see more",
917
+ onScroll: (event) => setScrollTop(event.currentTarget.scrollTop),
918
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
919
+ "table",
920
+ {
921
+ className: "eda-data-table border-collapse",
922
+ "aria-rowcount": filteredRows.length + 1,
923
+ style: {
924
+ tableLayout: "fixed",
925
+ width: Math.max(
926
+ width,
927
+ settings.columns.reduce(
928
+ (sum, column) => sum + (columnWidths[column.id] ?? column.width ?? Math.max(110, column.field.length * 7 + 42)),
929
+ 0
930
+ )
931
+ )
932
+ },
933
+ children: [
934
+ /* @__PURE__ */ jsxRuntime.jsx("caption", { className: "sr-only", children: chunk5B23JVH3_cjs.getChartSummary(settings) }),
935
+ /* @__PURE__ */ jsxRuntime.jsx("colgroup", { children: settings.columns.map((column) => /* @__PURE__ */ jsxRuntime.jsx(
936
+ "col",
937
+ {
938
+ style: {
939
+ width: columnWidths[column.id] ?? column.width ?? Math.max(110, column.field.length * 7 + 42)
940
+ }
941
+ },
942
+ column.id
943
+ )) }),
944
+ /* @__PURE__ */ jsxRuntime.jsx(
945
+ DataTableHeader,
946
+ {
947
+ localFilters: rows !== void 0,
948
+ settings,
949
+ onSettingsChange: update,
950
+ onColumnResize: (id, width2) => setColumnWidths((current) => {
951
+ const next = { ...current };
952
+ if (width2 === null) {
953
+ delete next[id];
954
+ } else {
955
+ next[id] = width2;
956
+ }
957
+ return next;
958
+ })
959
+ }
960
+ ),
961
+ /* @__PURE__ */ jsxRuntime.jsx(
962
+ DataTableBody,
963
+ {
964
+ settings,
965
+ rows: filteredRows,
966
+ scrollTop,
967
+ viewportHeight: height - (toolbarTarget === void 0 ? 38 : 0)
968
+ }
969
+ )
970
+ ]
971
+ }
972
+ )
973
+ }
974
+ )
975
+ ]
976
+ }
977
+ );
978
+ }
979
+ function DataTableSettingsPanel({
980
+ settings,
981
+ onSettingsChange
982
+ }) {
983
+ const availableFields = chunk3PGYFLE3_cjs.useColumnNames();
984
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
985
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
986
+ /* @__PURE__ */ jsxRuntime.jsx(chunkMJROUIDZ_cjs.Label, { children: "Columns" }),
987
+ /* @__PURE__ */ jsxRuntime.jsx(
988
+ chunk3PGYFLE3_cjs.multi_select_default,
989
+ {
990
+ options: availableFields.map((f) => ({
991
+ label: f,
992
+ value: f
993
+ })),
994
+ value: settings.columns.map((col) => ({
995
+ label: col.field,
996
+ value: col.field
997
+ })),
998
+ onChange: (values) => onSettingsChange({
999
+ ...settings,
1000
+ columns: values.map(
1001
+ (v) => settings.columns.find(
1002
+ (column) => column.field === v.value
1003
+ ) ?? {
1004
+ id: v.value,
1005
+ field: v.value
1006
+ }
1007
+ )
1008
+ })
1009
+ }
1010
+ )
1011
+ ] }),
1012
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1013
+ /* @__PURE__ */ jsxRuntime.jsx(chunkMJROUIDZ_cjs.Label, { children: "Global Search" }),
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ chunkMJROUIDZ_cjs.Input,
1016
+ {
1017
+ placeholder: "Search all columns...",
1018
+ value: settings.globalSearch,
1019
+ onChange: (e) => onSettingsChange({
1020
+ ...settings,
1021
+ globalSearch: e.target.value
1022
+ })
1023
+ }
1024
+ )
1025
+ ] })
1026
+ ] });
1027
+ }
1028
+
1029
+ // src/components/charts/DataTable/definition.ts
1030
+ var dataTableDefinition = {
1031
+ type: "data-table",
1032
+ name: "Data Table",
1033
+ description: "Interactive data table with sorting and filtering",
1034
+ icon: lucideReact.Table,
1035
+ component: DataTable,
1036
+ settingsPanel: DataTableSettingsPanel,
1037
+ createDefaultSettings: (layout) => ({
1038
+ ...chunkU74A5VK5_cjs.DEFAULT_CHART_SETTINGS,
1039
+ id: crypto.randomUUID(),
1040
+ type: "data-table",
1041
+ title: "Data Table",
1042
+ layout,
1043
+ margin: { top: 5, right: 5, bottom: 5, left: 5 },
1044
+ columns: [],
1045
+ sortDirection: "asc",
1046
+ filters: [],
1047
+ globalSearch: ""
1048
+ }),
1049
+ validateSettings: (settings) => {
1050
+ return settings.columns.length > 0;
1051
+ },
1052
+ getFilterFunction: (settings, fieldGetter) => {
1053
+ if (settings.filters.length === 0) {
1054
+ return () => true;
1055
+ }
1056
+ const filterFunctions = settings.filters.map((filter) => {
1057
+ const column = settings.columns.find((col) => col.field === filter.field);
1058
+ if (!column) {
1059
+ return () => true;
1060
+ }
1061
+ const dataHash = fieldGetter(column.field);
1062
+ return (d) => {
1063
+ const value = dataHash[d];
1064
+ return chunkEU5EFHO5_cjs.applyFilter(value, filter);
1065
+ };
1066
+ });
1067
+ return (d) => filterFunctions.every((fn) => fn(d));
1068
+ }
1069
+ };
1070
+
1071
+ exports.DataTable = DataTable;
1072
+ exports.dataTableDefinition = dataTableDefinition;
1073
+ //# sourceMappingURL=chunk-6HZX5CBT.cjs.map
1074
+ //# sourceMappingURL=chunk-6HZX5CBT.cjs.map