@burdenoff/microfe-bigconsole 2026.613.3 → 2026.613.4
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.
- package/dist/bigconsole/components/widgets/WidgetRegistry.js +55 -1
- package/dist/bigconsole/components/widgets/WidgetRegistry.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartWidget.js +123 -91
- package/dist/bigconsole/components/widgets/chart/ChartWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/AreaChart.js +40 -35
- package/dist/bigconsole/components/widgets/chart/charts/AreaChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/BarChart.js +30 -25
- package/dist/bigconsole/components/widgets/chart/charts/BarChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/ComboChart.js +139 -0
- package/dist/bigconsole/components/widgets/chart/charts/ComboChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/LineChart.js +35 -30
- package/dist/bigconsole/components/widgets/chart/charts/LineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/ScatterChart.js +128 -0
- package/dist/bigconsole/components/widgets/chart/charts/ScatterChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/WaterfallChart.js +111 -0
- package/dist/bigconsole/components/widgets/chart/charts/WaterfallChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/index.js +4 -0
- package/dist/bigconsole/components/widgets/chart/charts/referenceElements.js +49 -0
- package/dist/bigconsole/components/widgets/chart/charts/referenceElements.js.map +1 -0
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +72 -68
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +74 -24
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +87 -86
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js +55 -26
- package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js +25 -18
- package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +255 -107
- package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +91 -0
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -0
- package/dist/bigconsole/hooks/index.js +1 -0
- package/dist/bigconsole/hooks/useDashboardImageExport.js +1 -0
- package/dist/bigconsole/utils/widgetTypeMapping.js +1 -0
- package/dist/bigconsole/utils/widgetTypeMapping.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,10 +3,10 @@ import "../../../contexts/index.js";
|
|
|
3
3
|
import n from "./TableHeader.js";
|
|
4
4
|
import r from "./TableCell.js";
|
|
5
5
|
import i from "./TablePagination.js";
|
|
6
|
-
import { memo as a, useCallback as o,
|
|
7
|
-
import { jsx as
|
|
6
|
+
import { memo as a, useCallback as o, useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
|
|
7
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
8
8
|
//#region src/bigconsole/components/widgets/table/TableWidget.tsx
|
|
9
|
-
function
|
|
9
|
+
function p(e, t) {
|
|
10
10
|
return !t.column || !t.direction ? e : [...e].sort((e, n) => {
|
|
11
11
|
let r = e[t.column], i = n[t.column];
|
|
12
12
|
if (r == null && i == null) return 0;
|
|
@@ -16,7 +16,7 @@ function d(e, t) {
|
|
|
16
16
|
return a = typeof r == "number" && typeof i == "number" ? r - i : String(r).localeCompare(String(i)), t.direction === "asc" ? a : -a;
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function m(e, t, n) {
|
|
20
20
|
if (!t.trim()) return e;
|
|
21
21
|
let r = t.toLowerCase();
|
|
22
22
|
return e.filter((e) => n.some((t) => {
|
|
@@ -24,10 +24,21 @@ function f(e, t, n) {
|
|
|
24
24
|
return n != null && String(n).toLowerCase().includes(r);
|
|
25
25
|
}));
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
function h(e, t, n) {
|
|
28
|
+
switch (t) {
|
|
29
|
+
case "currency": return new Intl.NumberFormat("en-US", {
|
|
30
|
+
style: "currency",
|
|
31
|
+
currency: n || "USD"
|
|
32
|
+
}).format(e);
|
|
33
|
+
case "percentage": return `${(e * 100).toFixed(1)}%`;
|
|
34
|
+
case "number": return new Intl.NumberFormat("en-US").format(e);
|
|
35
|
+
default: return Number.isInteger(e) ? String(e) : e.toFixed(2);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
var g = a(function({ widget: a, data: g = [], onRowClick: _, onDrilldown: v }) {
|
|
39
|
+
let y = a.config, b = c(() => {
|
|
40
|
+
if (Array.isArray(g)) return g;
|
|
41
|
+
if (g && typeof g == "object") {
|
|
31
42
|
for (let e of [
|
|
32
43
|
"data",
|
|
33
44
|
"items",
|
|
@@ -38,20 +49,20 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
38
49
|
"sales",
|
|
39
50
|
"entries",
|
|
40
51
|
"values"
|
|
41
|
-
]) if (Array.isArray(
|
|
42
|
-
let e = Object.values(
|
|
52
|
+
]) if (Array.isArray(g[e])) return g[e];
|
|
53
|
+
let e = Object.values(g);
|
|
43
54
|
for (let t of e) if (Array.isArray(t)) return t;
|
|
44
|
-
return [
|
|
55
|
+
return [g];
|
|
45
56
|
}
|
|
46
57
|
return [];
|
|
47
|
-
}, [
|
|
48
|
-
column:
|
|
49
|
-
direction:
|
|
50
|
-
}), [
|
|
58
|
+
}, [g]), { handleDataClick: x, enabled: S } = t(a.id, y?.crossFilterMappings), { clearAllCrossFilters: C, hasCrossFilters: w } = e(), [T, E] = u({
|
|
59
|
+
column: y?.defaultSortColumn || null,
|
|
60
|
+
direction: y?.defaultSortDirection || null
|
|
61
|
+
}), [D, O] = u(""), [k, A] = u({
|
|
51
62
|
page: 0,
|
|
52
|
-
pageSize:
|
|
63
|
+
pageSize: y?.pageSize || 10,
|
|
53
64
|
totalItems: 0
|
|
54
|
-
}),
|
|
65
|
+
}), j = c(() => y?.columns?.length ? y.columns.map((e) => {
|
|
55
66
|
if (typeof e == "string") return {
|
|
56
67
|
key: e,
|
|
57
68
|
header: e.charAt(0).toUpperCase() + e.slice(1).replace(/_/g, " "),
|
|
@@ -65,41 +76,125 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
65
76
|
width: e.width == null ? void 0 : String(e.width),
|
|
66
77
|
align: e.align
|
|
67
78
|
};
|
|
68
|
-
}) :
|
|
79
|
+
}) : b.length > 0 ? Object.keys(b[0]).map((e) => ({
|
|
69
80
|
key: e,
|
|
70
81
|
header: e.charAt(0).toUpperCase() + e.slice(1).replace(/_/g, " "),
|
|
71
82
|
sortable: !0
|
|
72
|
-
})) : [], [
|
|
83
|
+
})) : [], [y?.columns, b]), M = c(() => {
|
|
73
84
|
let e = {};
|
|
74
|
-
return
|
|
85
|
+
return y?.columns?.forEach((t) => {
|
|
75
86
|
t.format && (t.key || t.field) && (e[t.key || t.field || ""] = { type: t.format });
|
|
76
87
|
}), e;
|
|
77
|
-
}, [
|
|
78
|
-
let e =
|
|
79
|
-
return
|
|
88
|
+
}, [y?.columns]), N = y?.conditionalRules, P = c(() => {
|
|
89
|
+
let e = {};
|
|
90
|
+
return y?.columns?.forEach((t) => {
|
|
91
|
+
let n = t.key || t.field;
|
|
92
|
+
n && t.conditionalRules?.length && (e[n] = t.conditionalRules);
|
|
93
|
+
}), e;
|
|
94
|
+
}, [y?.columns]), F = c(() => {
|
|
95
|
+
let e = {};
|
|
96
|
+
return y?.columns?.forEach((t) => {
|
|
97
|
+
let n = t.key || t.field;
|
|
98
|
+
n && t.total && t.total !== "none" && (e[n] = t.total);
|
|
99
|
+
}), e;
|
|
100
|
+
}, [y?.columns]), I = y?.showTotals === !0 || Object.keys(F).length > 0, L = c(() => {
|
|
101
|
+
let e = b;
|
|
102
|
+
return y?.showSearch !== !1 && (e = m(e, D, j)), e = p(e, T), e;
|
|
80
103
|
}, [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
b,
|
|
105
|
+
D,
|
|
106
|
+
T,
|
|
107
|
+
j,
|
|
108
|
+
y?.showSearch
|
|
109
|
+
]), R = c(() => {
|
|
110
|
+
let e = {};
|
|
111
|
+
for (let t of j) {
|
|
112
|
+
let n = 0;
|
|
113
|
+
for (let e of L) {
|
|
114
|
+
let r = e[t.key];
|
|
115
|
+
typeof r == "number" && Number.isFinite(r) && (n = Math.max(n, Math.abs(r)));
|
|
116
|
+
}
|
|
117
|
+
e[t.key] = n;
|
|
118
|
+
}
|
|
119
|
+
return e;
|
|
120
|
+
}, [j, L]), z = c(() => {
|
|
121
|
+
if (!I) return {};
|
|
122
|
+
let e = {};
|
|
123
|
+
for (let t of j) {
|
|
124
|
+
let n = F[t.key];
|
|
125
|
+
if (!n) {
|
|
126
|
+
e[t.key] = null;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
let r = L.map((e) => e[t.key]).filter((e) => typeof e == "number" && Number.isFinite(e));
|
|
130
|
+
n === "count" ? e[t.key] = L.length : r.length === 0 ? e[t.key] = null : n === "sum" ? e[t.key] = r.reduce((e, t) => e + t, 0) : n === "avg" ? e[t.key] = r.reduce((e, t) => e + t, 0) / r.length : n === "min" ? e[t.key] = Math.min(...r) : n === "max" ? e[t.key] = Math.max(...r) : e[t.key] = null;
|
|
131
|
+
}
|
|
132
|
+
return e;
|
|
133
|
+
}, [
|
|
134
|
+
I,
|
|
135
|
+
j,
|
|
136
|
+
F,
|
|
137
|
+
L
|
|
86
138
|
]);
|
|
87
|
-
|
|
88
|
-
|
|
139
|
+
c(() => {
|
|
140
|
+
A((e) => ({
|
|
89
141
|
...e,
|
|
90
|
-
totalItems:
|
|
142
|
+
totalItems: L.length
|
|
91
143
|
}));
|
|
92
|
-
}, [
|
|
93
|
-
let
|
|
94
|
-
if (
|
|
95
|
-
let e =
|
|
96
|
-
return
|
|
144
|
+
}, [L.length]);
|
|
145
|
+
let B = c(() => {
|
|
146
|
+
if (y?.showPagination === !1) return L;
|
|
147
|
+
let e = k.page * k.pageSize;
|
|
148
|
+
return L.slice(e, e + k.pageSize);
|
|
97
149
|
}, [
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
]),
|
|
102
|
-
|
|
150
|
+
L,
|
|
151
|
+
k,
|
|
152
|
+
y?.showPagination
|
|
153
|
+
]), V = y?.virtualized === !0, H = y?.rowHeight && y.rowHeight > 0 ? y.rowHeight : 44, U = l(null), [W, G] = u(0), [K, q] = u(0);
|
|
154
|
+
s(() => {
|
|
155
|
+
if (!V) return;
|
|
156
|
+
let e = U.current;
|
|
157
|
+
if (!e) return;
|
|
158
|
+
q(e.clientHeight);
|
|
159
|
+
let t = typeof ResizeObserver < "u" ? new ResizeObserver(() => q(e.clientHeight)) : void 0;
|
|
160
|
+
return t?.observe(e), () => t?.disconnect();
|
|
161
|
+
}, [V]);
|
|
162
|
+
let J = c(() => {
|
|
163
|
+
if (!V) return null;
|
|
164
|
+
let e = L.length, t = K > 0 ? Math.ceil(K / H) : 20, n = Math.max(0, Math.floor(W / H) - 6), r = Math.min(e, n + t + 12);
|
|
165
|
+
return {
|
|
166
|
+
rows: L.slice(n, r),
|
|
167
|
+
topSpacer: n * H,
|
|
168
|
+
bottomSpacer: Math.max(0, (e - r) * H),
|
|
169
|
+
startIndex: n
|
|
170
|
+
};
|
|
171
|
+
}, [
|
|
172
|
+
V,
|
|
173
|
+
L,
|
|
174
|
+
K,
|
|
175
|
+
W,
|
|
176
|
+
H
|
|
177
|
+
]), Y = V && J ? J.rows : B, X = V && J ? J.startIndex : 0, Z = y?.frozenColumns && y.frozenColumns > 0 ? y.frozenColumns : 0, { displayColumns: Q, frozenLefts: $ } = c(() => {
|
|
178
|
+
if (Z <= 0) return {
|
|
179
|
+
displayColumns: j,
|
|
180
|
+
frozenLefts: {}
|
|
181
|
+
};
|
|
182
|
+
let e = {}, t = 0;
|
|
183
|
+
return {
|
|
184
|
+
displayColumns: j.map((n, r) => {
|
|
185
|
+
if (r < Z) {
|
|
186
|
+
let r = n.width && parseInt(n.width, 10) || 160;
|
|
187
|
+
return e[n.key] = t, t += r, {
|
|
188
|
+
...n,
|
|
189
|
+
width: `${r}px`
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return n;
|
|
193
|
+
}),
|
|
194
|
+
frozenLefts: e
|
|
195
|
+
};
|
|
196
|
+
}, [j, Z]), ee = o((e) => {
|
|
197
|
+
E((t) => {
|
|
103
198
|
if (t.column !== e) return {
|
|
104
199
|
column: e,
|
|
105
200
|
direction: "asc"
|
|
@@ -110,35 +205,35 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
110
205
|
direction: n
|
|
111
206
|
};
|
|
112
207
|
});
|
|
113
|
-
}, []),
|
|
114
|
-
|
|
208
|
+
}, []), te = o((e) => {
|
|
209
|
+
A((t) => ({
|
|
115
210
|
...t,
|
|
116
211
|
page: e
|
|
117
212
|
}));
|
|
118
|
-
}, []),
|
|
119
|
-
|
|
213
|
+
}, []), ne = o((e) => {
|
|
214
|
+
A((t) => ({
|
|
120
215
|
...t,
|
|
121
216
|
pageSize: e,
|
|
122
217
|
page: 0
|
|
123
218
|
}));
|
|
124
|
-
}, []),
|
|
125
|
-
let n =
|
|
126
|
-
if (
|
|
127
|
-
let t =
|
|
128
|
-
|
|
219
|
+
}, []), re = o((e, t) => {
|
|
220
|
+
let n = y?.crossFilterMappings?.length || y?.enableCrossFilter;
|
|
221
|
+
if (S && n) {
|
|
222
|
+
let t = j[0]?.key;
|
|
223
|
+
x(e, t ? String(e[t]) : void 0);
|
|
129
224
|
}
|
|
130
|
-
|
|
225
|
+
v ? v(e) : _ && a.drilldowns?.length && _(e, t);
|
|
131
226
|
}, [
|
|
132
|
-
|
|
133
|
-
h,
|
|
134
|
-
a.drilldowns,
|
|
135
|
-
y,
|
|
227
|
+
_,
|
|
136
228
|
v,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
229
|
+
a.drilldowns,
|
|
230
|
+
S,
|
|
231
|
+
x,
|
|
232
|
+
j,
|
|
233
|
+
y?.crossFilterMappings,
|
|
234
|
+
y?.enableCrossFilter
|
|
235
|
+
]), ie = o(() => {
|
|
236
|
+
let e = [j.map((e) => e.header).join(","), ...L.map((e) => j.map((t) => {
|
|
142
237
|
let n = e[t.key];
|
|
143
238
|
if (n == null) return "";
|
|
144
239
|
let r = String(n);
|
|
@@ -146,27 +241,27 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
146
241
|
}).join(","))].join("\n"), t = new Blob([e], { type: "text/csv" }), n = URL.createObjectURL(t), r = document.createElement("a");
|
|
147
242
|
r.href = n, r.download = `${a.title || "table"}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`, r.click(), URL.revokeObjectURL(n);
|
|
148
243
|
}, [
|
|
149
|
-
|
|
150
|
-
|
|
244
|
+
j,
|
|
245
|
+
L,
|
|
151
246
|
a.title
|
|
152
|
-
]),
|
|
153
|
-
return /* @__PURE__ */
|
|
247
|
+
]), ae = y?.crossFilterMappings?.length || y?.enableCrossFilter, oe = !!(v || _ && a.drilldowns?.length || ae);
|
|
248
|
+
return /* @__PURE__ */ f("div", {
|
|
154
249
|
className: "flex flex-col h-full",
|
|
155
250
|
children: [
|
|
156
|
-
|
|
251
|
+
w && /* @__PURE__ */ d("div", {
|
|
157
252
|
className: "flex items-center gap-2 px-3 py-2 border-b border-border-default",
|
|
158
|
-
children: /* @__PURE__ */
|
|
253
|
+
children: /* @__PURE__ */ f("button", {
|
|
159
254
|
type: "button",
|
|
160
255
|
onClick: (e) => {
|
|
161
|
-
e.stopPropagation(),
|
|
256
|
+
e.stopPropagation(), C();
|
|
162
257
|
},
|
|
163
258
|
className: "\n inline-flex items-center gap-1.5\n px-2.5 py-1 rounded-md\n text-xs font-medium\n bg-action-primary-bg/10 text-action-primary-text\n hover:bg-action-primary-bg/20\n transition-colors\n ",
|
|
164
|
-
children: [/* @__PURE__ */
|
|
259
|
+
children: [/* @__PURE__ */ d("svg", {
|
|
165
260
|
className: "w-3.5 h-3.5",
|
|
166
261
|
fill: "none",
|
|
167
262
|
stroke: "currentColor",
|
|
168
263
|
viewBox: "0 0 24 24",
|
|
169
|
-
children: /* @__PURE__ */
|
|
264
|
+
children: /* @__PURE__ */ d("path", {
|
|
170
265
|
strokeLinecap: "round",
|
|
171
266
|
strokeLinejoin: "round",
|
|
172
267
|
strokeWidth: 2,
|
|
@@ -175,38 +270,38 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
175
270
|
}), "Clear Filter"]
|
|
176
271
|
})
|
|
177
272
|
}),
|
|
178
|
-
(
|
|
273
|
+
(y?.showSearch !== !1 || y?.showExport !== !1) && /* @__PURE__ */ f("div", {
|
|
179
274
|
className: "flex items-center justify-between p-3 border-b border-border-default",
|
|
180
|
-
children: [
|
|
275
|
+
children: [y?.showSearch !== !1 && /* @__PURE__ */ f("div", {
|
|
181
276
|
className: "relative flex items-center",
|
|
182
|
-
children: [/* @__PURE__ */
|
|
277
|
+
children: [/* @__PURE__ */ d("svg", {
|
|
183
278
|
className: "absolute left-3 w-4 h-4 text-text-secondary pointer-events-none",
|
|
184
279
|
fill: "none",
|
|
185
280
|
stroke: "currentColor",
|
|
186
281
|
viewBox: "0 0 24 24",
|
|
187
|
-
children: /* @__PURE__ */
|
|
282
|
+
children: /* @__PURE__ */ d("path", {
|
|
188
283
|
strokeLinecap: "round",
|
|
189
284
|
strokeLinejoin: "round",
|
|
190
285
|
strokeWidth: 2,
|
|
191
286
|
d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
192
287
|
})
|
|
193
|
-
}), /* @__PURE__ */
|
|
288
|
+
}), /* @__PURE__ */ d("input", {
|
|
194
289
|
type: "text",
|
|
195
290
|
placeholder: "Search products...",
|
|
196
|
-
value:
|
|
197
|
-
onChange: (e) =>
|
|
291
|
+
value: D,
|
|
292
|
+
onChange: (e) => O(e.target.value),
|
|
198
293
|
className: "\n pl-10 pr-4 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-action-primary-bg\n w-48 sm:w-64\n placeholder:text-text-tertiary\n "
|
|
199
294
|
})]
|
|
200
|
-
}),
|
|
295
|
+
}), y?.showExport !== !1 && /* @__PURE__ */ f("button", {
|
|
201
296
|
type: "button",
|
|
202
|
-
onClick:
|
|
297
|
+
onClick: ie,
|
|
203
298
|
className: "\n inline-flex items-center gap-2\n px-3 py-2\n text-sm font-medium\n text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n hover:bg-bg-muted\n transition-colors\n ",
|
|
204
|
-
children: [/* @__PURE__ */
|
|
299
|
+
children: [/* @__PURE__ */ d("svg", {
|
|
205
300
|
className: "w-4 h-4",
|
|
206
301
|
fill: "none",
|
|
207
302
|
stroke: "currentColor",
|
|
208
303
|
viewBox: "0 0 24 24",
|
|
209
|
-
children: /* @__PURE__ */
|
|
304
|
+
children: /* @__PURE__ */ d("path", {
|
|
210
305
|
strokeLinecap: "round",
|
|
211
306
|
strokeLinejoin: "round",
|
|
212
307
|
strokeWidth: 2,
|
|
@@ -215,44 +310,97 @@ var p = a(function({ widget: a, data: p = [], onRowClick: m, onDrilldown: h }) {
|
|
|
215
310
|
}), "Export CSV"]
|
|
216
311
|
})]
|
|
217
312
|
}),
|
|
218
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ f("div", {
|
|
314
|
+
ref: U,
|
|
219
315
|
className: "flex-1 overflow-auto",
|
|
220
|
-
|
|
316
|
+
onScroll: V ? (e) => G(e.target.scrollTop) : void 0,
|
|
317
|
+
children: [/* @__PURE__ */ f("table", {
|
|
221
318
|
className: "w-full",
|
|
222
|
-
children: [
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
319
|
+
children: [
|
|
320
|
+
/* @__PURE__ */ d(n, {
|
|
321
|
+
columns: Q,
|
|
322
|
+
sortState: T,
|
|
323
|
+
onSort: ee,
|
|
324
|
+
frozenLefts: Z > 0 ? $ : void 0
|
|
325
|
+
}),
|
|
326
|
+
/* @__PURE__ */ f("tbody", { children: [
|
|
327
|
+
V && J && J.topSpacer > 0 && /* @__PURE__ */ d("tr", {
|
|
328
|
+
"aria-hidden": "true",
|
|
329
|
+
style: { height: J.topSpacer },
|
|
330
|
+
children: /* @__PURE__ */ d("td", {
|
|
331
|
+
colSpan: Q.length,
|
|
332
|
+
style: {
|
|
333
|
+
padding: 0,
|
|
334
|
+
border: "none"
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
}),
|
|
338
|
+
Y.map((e, t) => {
|
|
339
|
+
let n = X + t;
|
|
340
|
+
return /* @__PURE__ */ d("tr", {
|
|
341
|
+
className: `
|
|
342
|
+
${oe ? "cursor-pointer hover:bg-bg-muted/50" : ""}
|
|
343
|
+
${n % 2 == 0 ? "bg-bg-surface" : "bg-bg-muted/30"}
|
|
344
|
+
`,
|
|
345
|
+
style: V ? { height: H } : void 0,
|
|
346
|
+
onClick: () => re(e, n),
|
|
347
|
+
children: Q.map((t) => /* @__PURE__ */ d(r, {
|
|
348
|
+
value: e[t.key],
|
|
349
|
+
column: t,
|
|
350
|
+
format: M[t.key],
|
|
351
|
+
rowData: e,
|
|
352
|
+
conditionalRules: P[t.key] ?? N,
|
|
353
|
+
dataBarMax: R[t.key],
|
|
354
|
+
stickyLeft: $[t.key]
|
|
355
|
+
}, t.key))
|
|
356
|
+
}, n);
|
|
357
|
+
}),
|
|
358
|
+
V && J && J.bottomSpacer > 0 && /* @__PURE__ */ d("tr", {
|
|
359
|
+
"aria-hidden": "true",
|
|
360
|
+
style: { height: J.bottomSpacer },
|
|
361
|
+
children: /* @__PURE__ */ d("td", {
|
|
362
|
+
colSpan: Q.length,
|
|
363
|
+
style: {
|
|
364
|
+
padding: 0,
|
|
365
|
+
border: "none"
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
] }),
|
|
370
|
+
I && L.length > 0 && /* @__PURE__ */ d("tfoot", { children: /* @__PURE__ */ d("tr", {
|
|
371
|
+
className: "bg-bg-muted/50 font-semibold border-t-2 border-border-default",
|
|
372
|
+
children: Q.map((e, t) => {
|
|
373
|
+
let n = z[e.key], r = M[e.key], i = $[e.key];
|
|
374
|
+
return /* @__PURE__ */ d("td", {
|
|
375
|
+
className: `
|
|
376
|
+
px-4 py-3 text-sm text-text-primary
|
|
377
|
+
${e.align === "center" ? "text-center" : ""}
|
|
378
|
+
${e.align === "right" ? "text-right" : "text-left"}
|
|
379
|
+
${i == null ? "" : "sticky z-10 bg-bg-muted"}
|
|
380
|
+
`,
|
|
381
|
+
style: i == null ? void 0 : { left: i },
|
|
382
|
+
children: n == null ? t === 0 ? "Total" : "" : h(n, r?.type, r?.currency)
|
|
383
|
+
}, e.key);
|
|
384
|
+
})
|
|
385
|
+
}) })
|
|
386
|
+
]
|
|
387
|
+
}), L.length === 0 && /* @__PURE__ */ d("div", {
|
|
240
388
|
className: "flex items-center justify-center py-12 text-text-secondary",
|
|
241
|
-
children:
|
|
389
|
+
children: D ? "No matching records found" : "No data available"
|
|
242
390
|
})]
|
|
243
391
|
}),
|
|
244
|
-
|
|
392
|
+
!V && y?.showPagination !== !1 && L.length > 0 && /* @__PURE__ */ d(i, {
|
|
245
393
|
pagination: {
|
|
246
|
-
...
|
|
247
|
-
totalItems:
|
|
394
|
+
...k,
|
|
395
|
+
totalItems: L.length
|
|
248
396
|
},
|
|
249
|
-
onPageChange:
|
|
250
|
-
onPageSizeChange:
|
|
397
|
+
onPageChange: te,
|
|
398
|
+
onPageSizeChange: ne
|
|
251
399
|
})
|
|
252
400
|
]
|
|
253
401
|
});
|
|
254
402
|
});
|
|
255
403
|
//#endregion
|
|
256
|
-
export {
|
|
404
|
+
export { g as default };
|
|
257
405
|
|
|
258
406
|
//# sourceMappingURL=TableWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableWidget.tsx"],"sourcesContent":["/**\n * TableWidget Component\n *\n * Data table with sorting, filtering, and pagination.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback } from 'react';\nimport type { Widget, TableConfig, TableColumnConfig } from '../../../types';\nimport { TableHeader, type ColumnDefinition, type SortState, type SortDirection } from './TableHeader';\nimport { TableCell, type CellFormatConfig } from './TableCell';\nimport { TablePagination, type PaginationState } from './TablePagination';\n// Phase 2: Cross-filtering support\nimport { useWidgetCrossFilter, useCrossFilterContext } from '../../../contexts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TableWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Table data (array of rows) */\n data?: Record<string, unknown>[];\n /** Click handler for row drilldown */\n onRowClick?: (row: Record<string, unknown>, index: number) => void;\n /** Drilldown handler - receives selected row data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction sortData(data: Record<string, unknown>[], sortState: SortState): Record<string, unknown>[] {\n if (!sortState.column || !sortState.direction) {\n return data;\n }\n\n return [...data].sort((a, b) => {\n const aValue = a[sortState.column!];\n const bValue = b[sortState.column!];\n\n // Handle null/undefined\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return sortState.direction === 'asc' ? 1 : -1;\n if (bValue == null) return sortState.direction === 'asc' ? -1 : 1;\n\n // Compare values\n let comparison = 0;\n if (typeof aValue === 'number' && typeof bValue === 'number') {\n comparison = aValue - bValue;\n } else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n return sortState.direction === 'asc' ? comparison : -comparison;\n });\n}\n\nfunction filterData(\n data: Record<string, unknown>[],\n searchQuery: string,\n columns: ColumnDefinition[]\n): Record<string, unknown>[] {\n if (!searchQuery.trim()) {\n return data;\n }\n\n const query = searchQuery.toLowerCase();\n return data.filter((row) =>\n columns.some((col) => {\n const value = row[col.key];\n return value != null && String(value).toLowerCase().includes(query);\n })\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TableWidget: FC<TableWidgetProps> = memo(function TableWidget({\n widget,\n data: rawData = [],\n onRowClick,\n onDrilldown,\n}) {\n const config = widget.config as unknown as TableConfig | undefined;\n\n // Normalize data - handle both array and object formats\n const data = useMemo(() => {\n // If it's already an array, use it directly\n if (Array.isArray(rawData)) {\n return rawData;\n }\n\n // If it's an object, try to find an array property\n if (rawData && typeof rawData === 'object') {\n // Check common array property names\n const possibleArrayProps = ['data', 'items', 'rows', 'results', 'records', 'list', 'sales', 'entries', 'values'];\n for (const prop of possibleArrayProps) {\n if (Array.isArray((rawData as Record<string, unknown>)[prop])) {\n return (rawData as Record<string, unknown>)[prop] as Record<string, unknown>[];\n }\n }\n\n // If object has array values, use the first one\n const values = Object.values(rawData);\n for (const value of values) {\n if (Array.isArray(value)) {\n return value as Record<string, unknown>[];\n }\n }\n\n // If it's a single object, wrap it in an array\n return [rawData as Record<string, unknown>];\n }\n\n return [];\n }, [rawData]);\n\n // Phase 2: Cross-filtering support\n const { handleDataClick: triggerCrossFilter, enabled: crossFilterEnabled } = useWidgetCrossFilter(\n widget.id,\n // Map first column to filter ID by default, can be configured\n config?.crossFilterMappings\n );\n const { clearAllCrossFilters, hasCrossFilters } = useCrossFilterContext();\n\n // State\n const [sortState, setSortState] = useState<SortState>({\n column: config?.defaultSortColumn || null,\n direction: (config?.defaultSortDirection as SortDirection) || null,\n });\n const [searchQuery, setSearchQuery] = useState('');\n const [pagination, setPagination] = useState<PaginationState>({\n page: 0,\n pageSize: config?.pageSize || 10,\n totalItems: 0,\n });\n\n // Build column definitions from config or data\n const columns: ColumnDefinition[] = useMemo(() => {\n if (config?.columns?.length) {\n return (config.columns as (string | TableColumnConfig)[]).map((col) => {\n // Handle both string and object column definitions\n if (typeof col === 'string') {\n return {\n key: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n sortable: true,\n };\n }\n // Object column definition — accept both .key and .field\n const fieldKey = col.key || col.field || '';\n return {\n key: fieldKey,\n header: col.label || col.header || fieldKey,\n sortable: col.sortable !== false,\n width: col.width != null ? String(col.width) : undefined,\n align: col.align as ColumnDefinition['align'],\n };\n }) as ColumnDefinition[];\n }\n\n // Auto-generate from first data row\n if (data.length > 0) {\n return Object.keys(data[0]).map((key) => ({\n key,\n header: key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' '),\n sortable: true,\n }));\n }\n\n return [];\n }, [config?.columns, data]);\n\n // Build cell format configs\n const cellFormats: Record<string, CellFormatConfig> = useMemo(() => {\n const formats: Record<string, CellFormatConfig> = {};\n config?.columns?.forEach((col) => {\n if (col.format && (col.key || col.field)) {\n formats[col.key || col.field || ''] = { type: col.format };\n }\n });\n return formats;\n }, [config?.columns]);\n\n // Process data: filter, sort, paginate\n const processedData = useMemo(() => {\n let result = data;\n\n // Filter\n if (config?.showSearch !== false) {\n result = filterData(result, searchQuery, columns);\n }\n\n // Sort\n result = sortData(result, sortState);\n\n return result;\n }, [data, searchQuery, sortState, columns, config?.showSearch]);\n\n // Update total items when data changes\n useMemo(() => {\n setPagination((prev) => ({\n ...prev,\n totalItems: processedData.length,\n }));\n }, [processedData.length]);\n\n // Paginate\n const paginatedData = useMemo(() => {\n if (config?.showPagination === false) {\n return processedData;\n }\n const start = pagination.page * pagination.pageSize;\n return processedData.slice(start, start + pagination.pageSize);\n }, [processedData, pagination, config?.showPagination]);\n\n // Handlers\n const handleSort = useCallback((column: string) => {\n setSortState((prev) => {\n if (prev.column !== column) {\n return { column, direction: 'asc' };\n }\n const nextDirection: SortDirection = prev.direction === 'asc' ? 'desc' : prev.direction === 'desc' ? null : 'asc';\n return {\n column: nextDirection ? column : null,\n direction: nextDirection,\n };\n });\n }, []);\n\n const handlePageChange = useCallback((page: number) => {\n setPagination((prev) => ({ ...prev, page }));\n }, []);\n\n const handlePageSizeChange = useCallback((pageSize: number) => {\n setPagination((prev) => ({ ...prev, pageSize, page: 0 }));\n }, []);\n\n const handleRowClick = useCallback(\n (row: Record<string, unknown>, index: number) => {\n // Trigger cross-filtering on row click when explicitly enabled via config\n const hasCrossFilterConfig = config?.crossFilterMappings?.length || config?.enableCrossFilter;\n if (crossFilterEnabled && hasCrossFilterConfig) {\n const firstColumn = columns[0]?.key;\n const label = firstColumn ? String(row[firstColumn]) : undefined;\n triggerCrossFilter(row as Record<string, unknown>, label);\n }\n\n // Trigger drilldown if configured\n if (onDrilldown) {\n onDrilldown(row);\n } else if (onRowClick && widget.drilldowns?.length) {\n onRowClick(row, index);\n }\n },\n [\n onRowClick,\n onDrilldown,\n widget.drilldowns,\n crossFilterEnabled,\n triggerCrossFilter,\n columns,\n config?.crossFilterMappings,\n config?.enableCrossFilter,\n ]\n );\n\n // Export to CSV\n const handleExport = useCallback(() => {\n const headers = columns.map((c) => c.header).join(',');\n const rows = processedData.map((row) =>\n columns\n .map((col) => {\n const value = row[col.key];\n if (value == null) return '';\n const str = String(value);\n // Escape quotes and wrap in quotes if contains comma\n if (str.includes(',') || str.includes('\"')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n })\n .join(',')\n );\n const csv = [headers, ...rows].join('\\n');\n const blob = new Blob([csv], { type: 'text/csv' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${widget.title || 'table'}-${new Date().toISOString().split('T')[0]}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }, [columns, processedData, widget.title]);\n\n // Row is only clickable if drilldown or cross-filter is explicitly configured\n const hasCrossFilterConfig = config?.crossFilterMappings?.length || config?.enableCrossFilter;\n const isClickable = Boolean(onDrilldown || (onRowClick && widget.drilldowns?.length) || hasCrossFilterConfig);\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Cross-filter active indicator with clear button */}\n {hasCrossFilters && (\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n clearAllCrossFilters();\n }}\n className=\"\n inline-flex items-center gap-1.5\n px-2.5 py-1 rounded-md\n text-xs font-medium\n bg-action-primary-bg/10 text-action-primary-text\n hover:bg-action-primary-bg/20\n transition-colors\n \"\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 19l-7-7m0 0l7-7m-7 7h18\" />\n </svg>\n Clear Filter\n </button>\n </div>\n )}\n\n {/* Toolbar */}\n {(config?.showSearch !== false || config?.showExport !== false) && (\n <div className=\"flex items-center justify-between p-3 border-b border-border-default\">\n {/* Search */}\n {config?.showSearch !== false && (\n <div className=\"relative flex items-center\">\n <svg\n className=\"absolute left-3 w-4 h-4 text-text-secondary pointer-events-none\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n <input\n type=\"text\"\n placeholder=\"Search products...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"\n pl-10 pr-4 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-action-primary-bg\n w-48 sm:w-64\n placeholder:text-text-tertiary\n \"\n />\n </div>\n )}\n\n {/* Export */}\n {config?.showExport !== false && (\n <button\n type=\"button\"\n onClick={handleExport}\n className=\"\n inline-flex items-center gap-2\n px-3 py-2\n text-sm font-medium\n text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n hover:bg-bg-muted\n transition-colors\n \"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n Export CSV\n </button>\n )}\n </div>\n )}\n\n {/* Table */}\n <div className=\"flex-1 overflow-auto\">\n <table className=\"w-full\">\n <TableHeader columns={columns} sortState={sortState} onSort={handleSort} />\n <tbody>\n {paginatedData.map((row, rowIndex) => (\n <tr\n key={rowIndex}\n className={`\n ${isClickable ? 'cursor-pointer hover:bg-bg-muted/50' : ''}\n ${rowIndex % 2 === 0 ? 'bg-bg-surface' : 'bg-bg-muted/30'}\n `}\n onClick={() => handleRowClick(row, rowIndex)}\n >\n {columns.map((column) => (\n <TableCell\n key={column.key}\n value={row[column.key]}\n column={column}\n format={cellFormats[column.key]}\n rowData={row}\n />\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n\n {paginatedData.length === 0 && (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n {searchQuery ? 'No matching records found' : 'No data available'}\n </div>\n )}\n </div>\n\n {/* Pagination */}\n {config?.showPagination !== false && processedData.length > 0 && (\n <TablePagination\n pagination={{ ...pagination, totalItems: processedData.length }}\n onPageChange={handlePageChange}\n onPageSizeChange={handlePageSizeChange}\n />\n )}\n </div>\n );\n});\n\nexport default TableWidget;\n"],"mappings":";;;;;;;;AAiCA,SAAS,EAAS,GAAiC,GAAiD;AAKlG,QAJI,CAAC,EAAU,UAAU,CAAC,EAAU,YAC3B,IAGF,CAAC,GAAG,EAAK,CAAC,MAAM,GAAG,MAAM;EAC9B,IAAM,IAAS,EAAE,EAAU,SACrB,IAAS,EAAE,EAAU;AAG3B,MAAI,KAAU,QAAQ,KAAU,KAAM,QAAO;AAC7C,MAAI,KAAU,KAAM,QAAO,EAAU,cAAc,QAAQ,IAAI;AAC/D,MAAI,KAAU,KAAM,QAAO,EAAU,cAAc,QAAQ,KAAK;EAGhE,IAAI,IAAa;AAOjB,SANA,AAGE,IAHE,OAAO,KAAW,YAAY,OAAO,KAAW,WACrC,IAAS,IAET,OAAO,EAAO,CAAC,cAAc,OAAO,EAAO,CAAC,EAGpD,EAAU,cAAc,QAAQ,IAAa,CAAC;GACrD;;AAGJ,SAAS,EACP,GACA,GACA,GAC2B;AAC3B,KAAI,CAAC,EAAY,MAAM,CACrB,QAAO;CAGT,IAAM,IAAQ,EAAY,aAAa;AACvC,QAAO,EAAK,QAAQ,MAClB,EAAQ,MAAM,MAAQ;EACpB,IAAM,IAAQ,EAAI,EAAI;AACtB,SAAO,KAAS,QAAQ,OAAO,EAAM,CAAC,aAAa,CAAC,SAAS,EAAM;GACnE,CACH;;AAOH,IAAa,IAAoC,EAAK,SAAqB,EACzE,WACA,MAAM,IAAU,EAAE,EAClB,eACA,kBACC;CACD,IAAM,IAAS,EAAO,QAGhB,IAAO,QAAc;AAEzB,MAAI,MAAM,QAAQ,EAAQ,CACxB,QAAO;AAIT,MAAI,KAAW,OAAO,KAAY,UAAU;AAG1C,QAAK,IAAM,KADgB;IAAC;IAAQ;IAAS;IAAQ;IAAW;IAAW;IAAQ;IAAS;IAAW;IAAS,CAE9G,KAAI,MAAM,QAAS,EAAoC,GAAM,CAC3D,QAAQ,EAAoC;GAKhD,IAAM,IAAS,OAAO,OAAO,EAAQ;AACrC,QAAK,IAAM,KAAS,EAClB,KAAI,MAAM,QAAQ,EAAM,CACtB,QAAO;AAKX,UAAO,CAAC,EAAmC;;AAG7C,SAAO,EAAE;IACR,CAAC,EAAQ,CAAC,EAGP,EAAE,iBAAiB,GAAoB,SAAS,MAAuB,EAC3E,EAAO,IAEP,GAAQ,oBACT,EACK,EAAE,yBAAsB,uBAAoB,GAAuB,EAGnE,CAAC,GAAW,KAAgB,EAAoB;EACpD,QAAQ,GAAQ,qBAAqB;EACrC,WAAY,GAAQ,wBAA0C;EAC/D,CAAC,EACI,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAY,KAAiB,EAA0B;EAC5D,MAAM;EACN,UAAU,GAAQ,YAAY;EAC9B,YAAY;EACb,CAAC,EAGI,IAA8B,QAC9B,GAAQ,SAAS,SACX,EAAO,QAA2C,KAAK,MAAQ;AAErE,MAAI,OAAO,KAAQ,SACjB,QAAO;GACL,KAAK;GACL,QAAQ,EAAI,OAAO,EAAE,CAAC,aAAa,GAAG,EAAI,MAAM,EAAE,CAAC,QAAQ,MAAM,IAAI;GACrE,UAAU;GACX;EAGH,IAAM,IAAW,EAAI,OAAO,EAAI,SAAS;AACzC,SAAO;GACL,KAAK;GACL,QAAQ,EAAI,SAAS,EAAI,UAAU;GACnC,UAAU,EAAI,aAAa;GAC3B,OAAO,EAAI,SAAS,OAA2B,KAAA,IAApB,OAAO,EAAI,MAAM;GAC5C,OAAO,EAAI;GACZ;GACD,GAIA,EAAK,SAAS,IACT,OAAO,KAAK,EAAK,GAAG,CAAC,KAAK,OAAS;EACxC;EACA,QAAQ,EAAI,OAAO,EAAE,CAAC,aAAa,GAAG,EAAI,MAAM,EAAE,CAAC,QAAQ,MAAM,IAAI;EACrE,UAAU;EACX,EAAE,GAGE,EAAE,EACR,CAAC,GAAQ,SAAS,EAAK,CAAC,EAGrB,IAAgD,QAAc;EAClE,IAAM,IAA4C,EAAE;AAMpD,SALA,GAAQ,SAAS,SAAS,MAAQ;AAChC,GAAI,EAAI,WAAW,EAAI,OAAO,EAAI,WAChC,EAAQ,EAAI,OAAO,EAAI,SAAS,MAAM,EAAE,MAAM,EAAI,QAAQ;IAE5D,EACK;IACN,CAAC,GAAQ,QAAQ,CAAC,EAGf,IAAgB,QAAc;EAClC,IAAI,IAAS;AAUb,SAPI,GAAQ,eAAe,OACzB,IAAS,EAAW,GAAQ,GAAa,EAAQ,GAInD,IAAS,EAAS,GAAQ,EAAU,EAE7B;IACN;EAAC;EAAM;EAAa;EAAW;EAAS,GAAQ;EAAW,CAAC;AAG/D,SAAc;AACZ,KAAe,OAAU;GACvB,GAAG;GACH,YAAY,EAAc;GAC3B,EAAE;IACF,CAAC,EAAc,OAAO,CAAC;CAG1B,IAAM,IAAgB,QAAc;AAClC,MAAI,GAAQ,mBAAmB,GAC7B,QAAO;EAET,IAAM,IAAQ,EAAW,OAAO,EAAW;AAC3C,SAAO,EAAc,MAAM,GAAO,IAAQ,EAAW,SAAS;IAC7D;EAAC;EAAe;EAAY,GAAQ;EAAe,CAAC,EAGjD,IAAa,GAAa,MAAmB;AACjD,KAAc,MAAS;AACrB,OAAI,EAAK,WAAW,EAClB,QAAO;IAAE;IAAQ,WAAW;IAAO;GAErC,IAAM,IAA+B,EAAK,cAAc,QAAQ,SAAS,EAAK,cAAc,SAAS,OAAO;AAC5G,UAAO;IACL,QAAQ,IAAgB,IAAS;IACjC,WAAW;IACZ;IACD;IACD,EAAE,CAAC,EAEA,IAAmB,GAAa,MAAiB;AACrD,KAAe,OAAU;GAAE,GAAG;GAAM;GAAM,EAAE;IAC3C,EAAE,CAAC,EAEA,IAAuB,GAAa,MAAqB;AAC7D,KAAe,OAAU;GAAE,GAAG;GAAM;GAAU,MAAM;GAAG,EAAE;IACxD,EAAE,CAAC,EAEA,IAAiB,GACpB,GAA8B,MAAkB;EAE/C,IAAM,IAAuB,GAAQ,qBAAqB,UAAU,GAAQ;AAC5E,MAAI,KAAsB,GAAsB;GAC9C,IAAM,IAAc,EAAQ,IAAI;AAEhC,KAAmB,GADL,IAAc,OAAO,EAAI,GAAa,GAAG,KAAA,EACE;;AAI3D,EAAI,IACF,EAAY,EAAI,GACP,KAAc,EAAO,YAAY,UAC1C,EAAW,GAAK,EAAM;IAG1B;EACE;EACA;EACA,EAAO;EACP;EACA;EACA;EACA,GAAQ;EACR,GAAQ;EACT,CACF,EAGK,IAAe,QAAkB;EAgBrC,IAAM,IAAM,CAfI,EAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAehC,GAdT,EAAc,KAAK,MAC9B,EACG,KAAK,MAAQ;GACZ,IAAM,IAAQ,EAAI,EAAI;AACtB,OAAI,KAAS,KAAM,QAAO;GAC1B,IAAM,IAAM,OAAO,EAAM;AAKzB,UAHI,EAAI,SAAS,IAAI,IAAI,EAAI,SAAS,KAAI,GACjC,IAAI,EAAI,QAAQ,MAAM,OAAK,CAAC,KAE9B;IACP,CACD,KAAK,IAAI,CACb,CAC6B,CAAC,KAAK,KAAK,EACnC,IAAO,IAAI,KAAK,CAAC,EAAI,EAAE,EAAE,MAAM,YAAY,CAAC,EAC5C,IAAM,IAAI,gBAAgB,EAAK,EAC/B,IAAI,SAAS,cAAc,IAAI;AAIrC,EAHA,EAAE,OAAO,GACT,EAAE,WAAW,GAAG,EAAO,SAAS,QAAQ,oBAAG,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC,GAAG,OAClF,EAAE,OAAO,EACT,IAAI,gBAAgB,EAAI;IACvB;EAAC;EAAS;EAAe,EAAO;EAAM,CAAC,EAGpC,IAAuB,GAAQ,qBAAqB,UAAU,GAAQ,mBACtE,IAAc,GAAQ,KAAgB,KAAc,EAAO,YAAY,UAAW;AAExF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEG,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAsB;;KAExB,WAAU;eANZ,CAeE,kBAAC,OAAD;MAAK,WAAU;MAAc,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACrE,kBAAC,QAAD;OAAM,eAAc;OAAQ,gBAAe;OAAQ,aAAa;OAAG,GAAE;OAAgC,CAAA;MACjG,CAAA,EAAA,eAEC;;IACL,CAAA;IAIN,GAAQ,eAAe,MAAS,GAAQ,eAAe,OACvD,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEG,GAAQ,eAAe,MACtB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,QAAO;MACP,SAAQ;gBAER,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,EACN,kBAAC,SAAD;MACE,MAAK;MACL,aAAY;MACZ,OAAO;MACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;MAC/C,WAAU;MAUV,CAAA,CACE;QAIP,GAAQ,eAAe,MACtB,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAeE,kBAAC,OAAD;MAAK,WAAU;MAAU,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACjE,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,EAAA,aAEC;OAEP;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,GAAD;MAAsB;MAAoB;MAAW,QAAQ;MAAc,CAAA,EAC3E,kBAAC,SAAD,EAAA,UACG,EAAc,KAAK,GAAK,MACvB,kBAAC,MAAD;MAEE,WAAW;oBACP,IAAc,wCAAwC,GAAG;oBACzD,IAAW,KAAM,IAAI,kBAAkB,iBAAiB;;MAE5D,eAAe,EAAe,GAAK,EAAS;gBAE3C,EAAQ,KAAK,MACZ,kBAAC,GAAD;OAEE,OAAO,EAAI,EAAO;OACV;OACR,QAAQ,EAAY,EAAO;OAC3B,SAAS;OACT,EALK,EAAO,IAKZ,CACF;MACC,EAhBE,EAgBF,CACL,EACI,CAAA,CACF;QAEP,EAAc,WAAW,KACxB,kBAAC,OAAD;KAAK,WAAU;eACZ,IAAc,8BAA8B;KACzC,CAAA,CAEJ;;GAGL,GAAQ,mBAAmB,MAAS,EAAc,SAAS,KAC1D,kBAAC,GAAD;IACE,YAAY;KAAE,GAAG;KAAY,YAAY,EAAc;KAAQ;IAC/D,cAAc;IACd,kBAAkB;IAClB,CAAA;GAEA;;EAER"}
|
|
1
|
+
{"version":3,"file":"TableWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableWidget.tsx"],"sourcesContent":["/**\n * TableWidget Component\n *\n * Data table with sorting, filtering, and pagination.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback, useRef, useEffect } from 'react';\nimport type { Widget, TableConfig, TableColumnConfig } from '../../../types';\nimport { TableHeader, type ColumnDefinition, type SortState, type SortDirection } from './TableHeader';\nimport { TableCell, type CellFormatConfig } from './TableCell';\nimport { TablePagination, type PaginationState } from './TablePagination';\nimport type { ConditionalRule } from '../utils/conditionalFormat';\n// Phase 2: Cross-filtering support\nimport { useWidgetCrossFilter, useCrossFilterContext } from '../../../contexts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TableWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Table data (array of rows) */\n data?: Record<string, unknown>[];\n /** Click handler for row drilldown */\n onRowClick?: (row: Record<string, unknown>, index: number) => void;\n /** Drilldown handler - receives selected row data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction sortData(data: Record<string, unknown>[], sortState: SortState): Record<string, unknown>[] {\n if (!sortState.column || !sortState.direction) {\n return data;\n }\n\n return [...data].sort((a, b) => {\n const aValue = a[sortState.column!];\n const bValue = b[sortState.column!];\n\n // Handle null/undefined\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return sortState.direction === 'asc' ? 1 : -1;\n if (bValue == null) return sortState.direction === 'asc' ? -1 : 1;\n\n // Compare values\n let comparison = 0;\n if (typeof aValue === 'number' && typeof bValue === 'number') {\n comparison = aValue - bValue;\n } else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n return sortState.direction === 'asc' ? comparison : -comparison;\n });\n}\n\nfunction filterData(\n data: Record<string, unknown>[],\n searchQuery: string,\n columns: ColumnDefinition[]\n): Record<string, unknown>[] {\n if (!searchQuery.trim()) {\n return data;\n }\n\n const query = searchQuery.toLowerCase();\n return data.filter((row) =>\n columns.some((col) => {\n const value = row[col.key];\n return value != null && String(value).toLowerCase().includes(query);\n })\n );\n}\n\n/** Format a footer total respecting the column's cell format. */\nfunction formatTotal(value: number, formatType?: string, currency?: string): string {\n switch (formatType) {\n case 'currency':\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency || 'USD' }).format(value);\n case 'percentage':\n return `${(value * 100).toFixed(1)}%`;\n case 'number':\n return new Intl.NumberFormat('en-US').format(value);\n default:\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TableWidget: FC<TableWidgetProps> = memo(function TableWidget({\n widget,\n data: rawData = [],\n onRowClick,\n onDrilldown,\n}) {\n const config = widget.config as unknown as TableConfig | undefined;\n\n // Normalize data - handle both array and object formats\n const data = useMemo(() => {\n // If it's already an array, use it directly\n if (Array.isArray(rawData)) {\n return rawData;\n }\n\n // If it's an object, try to find an array property\n if (rawData && typeof rawData === 'object') {\n // Check common array property names\n const possibleArrayProps = ['data', 'items', 'rows', 'results', 'records', 'list', 'sales', 'entries', 'values'];\n for (const prop of possibleArrayProps) {\n if (Array.isArray((rawData as Record<string, unknown>)[prop])) {\n return (rawData as Record<string, unknown>)[prop] as Record<string, unknown>[];\n }\n }\n\n // If object has array values, use the first one\n const values = Object.values(rawData);\n for (const value of values) {\n if (Array.isArray(value)) {\n return value as Record<string, unknown>[];\n }\n }\n\n // If it's a single object, wrap it in an array\n return [rawData as Record<string, unknown>];\n }\n\n return [];\n }, [rawData]);\n\n // Phase 2: Cross-filtering support\n const { handleDataClick: triggerCrossFilter, enabled: crossFilterEnabled } = useWidgetCrossFilter(\n widget.id,\n // Map first column to filter ID by default, can be configured\n config?.crossFilterMappings\n );\n const { clearAllCrossFilters, hasCrossFilters } = useCrossFilterContext();\n\n // State\n const [sortState, setSortState] = useState<SortState>({\n column: config?.defaultSortColumn || null,\n direction: (config?.defaultSortDirection as SortDirection) || null,\n });\n const [searchQuery, setSearchQuery] = useState('');\n const [pagination, setPagination] = useState<PaginationState>({\n page: 0,\n pageSize: config?.pageSize || 10,\n totalItems: 0,\n });\n\n // Build column definitions from config or data\n const columns: ColumnDefinition[] = useMemo(() => {\n if (config?.columns?.length) {\n return (config.columns as (string | TableColumnConfig)[]).map((col) => {\n // Handle both string and object column definitions\n if (typeof col === 'string') {\n return {\n key: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n sortable: true,\n };\n }\n // Object column definition — accept both .key and .field\n const fieldKey = col.key || col.field || '';\n return {\n key: fieldKey,\n header: col.label || col.header || fieldKey,\n sortable: col.sortable !== false,\n width: col.width != null ? String(col.width) : undefined,\n align: col.align as ColumnDefinition['align'],\n };\n }) as ColumnDefinition[];\n }\n\n // Auto-generate from first data row\n if (data.length > 0) {\n return Object.keys(data[0]).map((key) => ({\n key,\n header: key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' '),\n sortable: true,\n }));\n }\n\n return [];\n }, [config?.columns, data]);\n\n // Build cell format configs\n const cellFormats: Record<string, CellFormatConfig> = useMemo(() => {\n const formats: Record<string, CellFormatConfig> = {};\n config?.columns?.forEach((col) => {\n if (col.format && (col.key || col.field)) {\n formats[col.key || col.field || ''] = { type: col.format };\n }\n });\n return formats;\n }, [config?.columns]);\n\n // Per-column conditional rules (column-level overrides win over table-level).\n const tableRules = config?.conditionalRules;\n const columnRules: Record<string, ConditionalRule[] | undefined> = useMemo(() => {\n const map: Record<string, ConditionalRule[] | undefined> = {};\n config?.columns?.forEach((col) => {\n const fieldKey = col.key || col.field;\n if (fieldKey && col.conditionalRules?.length) {\n map[fieldKey] = col.conditionalRules;\n }\n });\n return map;\n }, [config?.columns]);\n\n // Per-column totals/aggregates for the footer row.\n const columnTotals: Record<string, TableColumnConfig['total']> = useMemo(() => {\n const map: Record<string, TableColumnConfig['total']> = {};\n config?.columns?.forEach((col) => {\n const fieldKey = col.key || col.field;\n if (fieldKey && col.total && col.total !== 'none') {\n map[fieldKey] = col.total;\n }\n });\n return map;\n }, [config?.columns]);\n\n const hasTotals = config?.showTotals === true || Object.keys(columnTotals).length > 0;\n\n // Process data: filter, sort, paginate\n const processedData = useMemo(() => {\n let result = data;\n\n // Filter\n if (config?.showSearch !== false) {\n result = filterData(result, searchQuery, columns);\n }\n\n // Sort\n result = sortData(result, sortState);\n\n return result;\n }, [data, searchQuery, sortState, columns, config?.showSearch]);\n\n // Max absolute numeric value per column — scales in-cell data bars.\n const dataBarMaxes: Record<string, number> = useMemo(() => {\n const maxes: Record<string, number> = {};\n for (const col of columns) {\n let max = 0;\n for (const row of processedData) {\n const v = row[col.key];\n if (typeof v === 'number' && Number.isFinite(v)) {\n max = Math.max(max, Math.abs(v));\n }\n }\n maxes[col.key] = max;\n }\n return maxes;\n }, [columns, processedData]);\n\n // Footer totals/aggregates per column (computed over the filtered set).\n const footerTotals: Record<string, number | null> = useMemo(() => {\n if (!hasTotals) return {};\n const result: Record<string, number | null> = {};\n for (const col of columns) {\n const agg = columnTotals[col.key];\n if (!agg) {\n result[col.key] = null;\n continue;\n }\n const nums = processedData\n .map((row) => row[col.key])\n .filter((v): v is number => typeof v === 'number' && Number.isFinite(v));\n if (agg === 'count') {\n result[col.key] = processedData.length;\n } else if (nums.length === 0) {\n result[col.key] = null;\n } else if (agg === 'sum') {\n result[col.key] = nums.reduce((a, b) => a + b, 0);\n } else if (agg === 'avg') {\n result[col.key] = nums.reduce((a, b) => a + b, 0) / nums.length;\n } else if (agg === 'min') {\n result[col.key] = Math.min(...nums);\n } else if (agg === 'max') {\n result[col.key] = Math.max(...nums);\n } else {\n result[col.key] = null;\n }\n }\n return result;\n }, [hasTotals, columns, columnTotals, processedData]);\n\n // Update total items when data changes\n useMemo(() => {\n setPagination((prev) => ({\n ...prev,\n totalItems: processedData.length,\n }));\n }, [processedData.length]);\n\n // Paginate\n const paginatedData = useMemo(() => {\n if (config?.showPagination === false) {\n return processedData;\n }\n const start = pagination.page * pagination.pageSize;\n return processedData.slice(start, start + pagination.pageSize);\n }, [processedData, pagination, config?.showPagination]);\n\n // ----------------------------------------------------------------------------\n // Row virtualization (windowing) — render only the visible slice of a large\n // dataset. Active when config.virtualized is set; bypasses pagination so the\n // whole filtered set scrolls in one container. No external dependency: a fixed\n // row height + top/bottom spacer rows reproduce the full scroll height.\n // ----------------------------------------------------------------------------\n const virtualized = config?.virtualized === true;\n const rowHeight = config?.rowHeight && config.rowHeight > 0 ? config.rowHeight : 44;\n const scrollRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState(0);\n const [viewportH, setViewportH] = useState(0);\n\n useEffect(() => {\n if (!virtualized) return undefined;\n const el = scrollRef.current;\n if (!el) return undefined;\n setViewportH(el.clientHeight);\n const ro =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(() => setViewportH(el.clientHeight)) : undefined;\n ro?.observe(el);\n return () => ro?.disconnect();\n }, [virtualized]);\n\n const OVERSCAN = 6;\n const virtualWindow = useMemo(() => {\n if (!virtualized) return null;\n const total = processedData.length;\n const visibleCount = viewportH > 0 ? Math.ceil(viewportH / rowHeight) : 20;\n const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - OVERSCAN);\n const endIndex = Math.min(total, startIndex + visibleCount + OVERSCAN * 2);\n return {\n rows: processedData.slice(startIndex, endIndex),\n topSpacer: startIndex * rowHeight,\n bottomSpacer: Math.max(0, (total - endIndex) * rowHeight),\n startIndex,\n };\n }, [virtualized, processedData, viewportH, scrollTop, rowHeight]);\n\n // The rows actually rendered in <tbody>: windowed slice or the paginated page.\n const renderRows = virtualized && virtualWindow ? virtualWindow.rows : paginatedData;\n const rowBaseIndex = virtualized && virtualWindow ? virtualWindow.startIndex : 0;\n\n // Sticky-left frozen columns: give the first N columns explicit widths so the\n // header and body align, then compute each one's cumulative left offset.\n const frozenCount = config?.frozenColumns && config.frozenColumns > 0 ? config.frozenColumns : 0;\n const FROZEN_DEFAULT_W = 160;\n const { displayColumns, frozenLefts } = useMemo(() => {\n if (frozenCount <= 0) return { displayColumns: columns, frozenLefts: {} as Record<string, number> };\n const lefts: Record<string, number> = {};\n let cursor = 0;\n const cols = columns.map((col, i) => {\n if (i < frozenCount) {\n const widthPx = col.width ? parseInt(col.width, 10) || FROZEN_DEFAULT_W : FROZEN_DEFAULT_W;\n lefts[col.key] = cursor;\n cursor += widthPx;\n return { ...col, width: `${widthPx}px` };\n }\n return col;\n });\n return { displayColumns: cols, frozenLefts: lefts };\n }, [columns, frozenCount]);\n\n // Handlers\n const handleSort = useCallback((column: string) => {\n setSortState((prev) => {\n if (prev.column !== column) {\n return { column, direction: 'asc' };\n }\n const nextDirection: SortDirection = prev.direction === 'asc' ? 'desc' : prev.direction === 'desc' ? null : 'asc';\n return {\n column: nextDirection ? column : null,\n direction: nextDirection,\n };\n });\n }, []);\n\n const handlePageChange = useCallback((page: number) => {\n setPagination((prev) => ({ ...prev, page }));\n }, []);\n\n const handlePageSizeChange = useCallback((pageSize: number) => {\n setPagination((prev) => ({ ...prev, pageSize, page: 0 }));\n }, []);\n\n const handleRowClick = useCallback(\n (row: Record<string, unknown>, index: number) => {\n // Trigger cross-filtering on row click when explicitly enabled via config\n const hasCrossFilterConfig = config?.crossFilterMappings?.length || config?.enableCrossFilter;\n if (crossFilterEnabled && hasCrossFilterConfig) {\n const firstColumn = columns[0]?.key;\n const label = firstColumn ? String(row[firstColumn]) : undefined;\n triggerCrossFilter(row as Record<string, unknown>, label);\n }\n\n // Trigger drilldown if configured\n if (onDrilldown) {\n onDrilldown(row);\n } else if (onRowClick && widget.drilldowns?.length) {\n onRowClick(row, index);\n }\n },\n [\n onRowClick,\n onDrilldown,\n widget.drilldowns,\n crossFilterEnabled,\n triggerCrossFilter,\n columns,\n config?.crossFilterMappings,\n config?.enableCrossFilter,\n ]\n );\n\n // Export to CSV\n const handleExport = useCallback(() => {\n const headers = columns.map((c) => c.header).join(',');\n const rows = processedData.map((row) =>\n columns\n .map((col) => {\n const value = row[col.key];\n if (value == null) return '';\n const str = String(value);\n // Escape quotes and wrap in quotes if contains comma\n if (str.includes(',') || str.includes('\"')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n })\n .join(',')\n );\n const csv = [headers, ...rows].join('\\n');\n const blob = new Blob([csv], { type: 'text/csv' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${widget.title || 'table'}-${new Date().toISOString().split('T')[0]}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }, [columns, processedData, widget.title]);\n\n // Row is only clickable if drilldown or cross-filter is explicitly configured\n const hasCrossFilterConfig = config?.crossFilterMappings?.length || config?.enableCrossFilter;\n const isClickable = Boolean(onDrilldown || (onRowClick && widget.drilldowns?.length) || hasCrossFilterConfig);\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Cross-filter active indicator with clear button */}\n {hasCrossFilters && (\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n clearAllCrossFilters();\n }}\n className=\"\n inline-flex items-center gap-1.5\n px-2.5 py-1 rounded-md\n text-xs font-medium\n bg-action-primary-bg/10 text-action-primary-text\n hover:bg-action-primary-bg/20\n transition-colors\n \"\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 19l-7-7m0 0l7-7m-7 7h18\" />\n </svg>\n Clear Filter\n </button>\n </div>\n )}\n\n {/* Toolbar */}\n {(config?.showSearch !== false || config?.showExport !== false) && (\n <div className=\"flex items-center justify-between p-3 border-b border-border-default\">\n {/* Search */}\n {config?.showSearch !== false && (\n <div className=\"relative flex items-center\">\n <svg\n className=\"absolute left-3 w-4 h-4 text-text-secondary pointer-events-none\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n <input\n type=\"text\"\n placeholder=\"Search products...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"\n pl-10 pr-4 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-action-primary-bg\n w-48 sm:w-64\n placeholder:text-text-tertiary\n \"\n />\n </div>\n )}\n\n {/* Export */}\n {config?.showExport !== false && (\n <button\n type=\"button\"\n onClick={handleExport}\n className=\"\n inline-flex items-center gap-2\n px-3 py-2\n text-sm font-medium\n text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n hover:bg-bg-muted\n transition-colors\n \"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n Export CSV\n </button>\n )}\n </div>\n )}\n\n {/* Table */}\n <div\n ref={scrollRef}\n className=\"flex-1 overflow-auto\"\n onScroll={virtualized ? (e) => setScrollTop((e.target as HTMLDivElement).scrollTop) : undefined}\n >\n <table className=\"w-full\">\n <TableHeader\n columns={displayColumns}\n sortState={sortState}\n onSort={handleSort}\n frozenLefts={frozenCount > 0 ? frozenLefts : undefined}\n />\n <tbody>\n {/* Virtualization top spacer */}\n {virtualized && virtualWindow && virtualWindow.topSpacer > 0 && (\n <tr aria-hidden=\"true\" style={{ height: virtualWindow.topSpacer }}>\n <td colSpan={displayColumns.length} style={{ padding: 0, border: 'none' }} />\n </tr>\n )}\n {renderRows.map((row, i) => {\n const rowIndex = rowBaseIndex + i;\n return (\n <tr\n key={rowIndex}\n className={`\n ${isClickable ? 'cursor-pointer hover:bg-bg-muted/50' : ''}\n ${rowIndex % 2 === 0 ? 'bg-bg-surface' : 'bg-bg-muted/30'}\n `}\n style={virtualized ? { height: rowHeight } : undefined}\n onClick={() => handleRowClick(row, rowIndex)}\n >\n {displayColumns.map((column) => (\n <TableCell\n key={column.key}\n value={row[column.key]}\n column={column}\n format={cellFormats[column.key]}\n rowData={row}\n conditionalRules={columnRules[column.key] ?? tableRules}\n dataBarMax={dataBarMaxes[column.key]}\n stickyLeft={frozenLefts[column.key]}\n />\n ))}\n </tr>\n );\n })}\n {/* Virtualization bottom spacer */}\n {virtualized && virtualWindow && virtualWindow.bottomSpacer > 0 && (\n <tr aria-hidden=\"true\" style={{ height: virtualWindow.bottomSpacer }}>\n <td colSpan={displayColumns.length} style={{ padding: 0, border: 'none' }} />\n </tr>\n )}\n </tbody>\n {hasTotals && processedData.length > 0 && (\n <tfoot>\n <tr className=\"bg-bg-muted/50 font-semibold border-t-2 border-border-default\">\n {displayColumns.map((column, colIndex) => {\n const total = footerTotals[column.key];\n const format = cellFormats[column.key];\n const frozen = frozenLefts[column.key];\n return (\n <td\n key={column.key}\n className={`\n px-4 py-3 text-sm text-text-primary\n ${column.align === 'center' ? 'text-center' : ''}\n ${column.align === 'right' ? 'text-right' : 'text-left'}\n ${frozen != null ? 'sticky z-10 bg-bg-muted' : ''}\n `}\n style={frozen != null ? { left: frozen } : undefined}\n >\n {total != null\n ? formatTotal(total, format?.type, format?.currency)\n : colIndex === 0\n ? 'Total'\n : ''}\n </td>\n );\n })}\n </tr>\n </tfoot>\n )}\n </table>\n\n {processedData.length === 0 && (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n {searchQuery ? 'No matching records found' : 'No data available'}\n </div>\n )}\n </div>\n\n {/* Pagination (suppressed in virtualized mode — the whole set scrolls) */}\n {!virtualized && config?.showPagination !== false && processedData.length > 0 && (\n <TablePagination\n pagination={{ ...pagination, totalItems: processedData.length }}\n onPageChange={handlePageChange}\n onPageSizeChange={handlePageSizeChange}\n />\n )}\n </div>\n );\n});\n\nexport default TableWidget;\n"],"mappings":"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"}
|