react-auto-smart-table 1.0.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1218 @@
1
+ // src/analyzer/detectTypes.ts
2
+ var EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
3
+ var URL_REGEX = /^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6}\.?)(\/[\w.-]*)*\/?$/i;
4
+ var IMAGE_REGEX = /\.(jpeg|jpg|gif|png|webp|svg|bmp)(\?.*)?$/i;
5
+ var isEmail = (value) => EMAIL_REGEX.test(value);
6
+ var isImage = (value) => {
7
+ return URL_REGEX.test(value) && IMAGE_REGEX.test(value);
8
+ };
9
+ var isUrl = (value) => {
10
+ if (isImage(value)) return false;
11
+ return URL_REGEX.test(value);
12
+ };
13
+ var isDate = (value) => {
14
+ if (value instanceof Date) return !isNaN(value.getTime());
15
+ if (typeof value === "string" || typeof value === "number") {
16
+ if (typeof value === "number") return false;
17
+ if (typeof value === "string" && value.trim() !== "" && !isNaN(Number(value))) return false;
18
+ const datePattern = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?Z?)?$|^\d{1,2}[-/]\d{1,2}[-/]\d{2,4}$/;
19
+ if (typeof value === "string" && !datePattern.test(value.trim())) {
20
+ return false;
21
+ }
22
+ const d = new Date(value);
23
+ return !isNaN(d.getTime()) && String(d) !== "Invalid Date";
24
+ }
25
+ return false;
26
+ };
27
+ var isCurrency = (value) => {
28
+ return typeof value === "string" && /^[£$€¥]?\s*-?\d+(?:,\d{3})*(?:\.\d+)?\s*[£$€¥]?$/.test(value) && /[£$€¥]/.test(value);
29
+ };
30
+ var isPercentage = (value) => {
31
+ return typeof value === "string" && /^-?\d+(?:\.\d+)?%$/.test(value);
32
+ };
33
+ var isNumber = (value) => {
34
+ if (typeof value === "number") return true;
35
+ if (typeof value === "string") {
36
+ return value.trim() !== "" && !isNaN(Number(value));
37
+ }
38
+ return false;
39
+ };
40
+ var isBoolean = (value) => {
41
+ if (typeof value === "boolean") return true;
42
+ if (typeof value === "string") {
43
+ const lower = value.toLowerCase();
44
+ return lower === "true" || lower === "false" || lower === "yes" || lower === "no";
45
+ }
46
+ return false;
47
+ };
48
+ var detectValueType = (value) => {
49
+ if (value === null || value === void 0 || value === "") return "string";
50
+ const strVal = String(value);
51
+ if (isEmail(strVal)) return "email";
52
+ if (isUrl(strVal)) return "url";
53
+ if (isImage(strVal)) return "image";
54
+ if (isDate(value)) return "date";
55
+ if (isCurrency(strVal)) return "currency";
56
+ if (isPercentage(strVal)) return "percentage";
57
+ if (isNumber(value)) return "number";
58
+ if (isBoolean(value)) return "boolean";
59
+ return "string";
60
+ };
61
+
62
+ // src/analyzer/sampleDataset.ts
63
+ function sampleDataset(dataset, maxSampleSize = 200) {
64
+ if (!dataset || !dataset.length) return [];
65
+ if (dataset.length <= maxSampleSize) return [...dataset];
66
+ const sample = [];
67
+ const indices = /* @__PURE__ */ new Set();
68
+ while (sample.length < maxSampleSize) {
69
+ const idx = Math.floor(Math.random() * dataset.length);
70
+ if (!indices.has(idx)) {
71
+ indices.add(idx);
72
+ sample.push(dataset[idx]);
73
+ }
74
+ }
75
+ return sample;
76
+ }
77
+
78
+ // src/analyzer/computeStats.ts
79
+ var computeColumnStats = (columnKey, dataset, type) => {
80
+ const stats = {};
81
+ const uniqueVals = /* @__PURE__ */ new Set();
82
+ let min = void 0;
83
+ let max = void 0;
84
+ for (const row of dataset) {
85
+ const val = row[columnKey];
86
+ if (val !== null && val !== void 0 && val !== "") {
87
+ uniqueVals.add(val);
88
+ if (type === "number" || type === "currency" || type === "percentage") {
89
+ let numVal = NaN;
90
+ if (type === "number") numVal = Number(val);
91
+ else if (type === "currency" || type === "percentage") {
92
+ const clean = String(val).replace(/[^0-9.-]/g, "");
93
+ numVal = Number(clean);
94
+ }
95
+ if (!isNaN(numVal)) {
96
+ if (min === void 0 || numVal < min) min = numVal;
97
+ if (max === void 0 || numVal > max) max = numVal;
98
+ }
99
+ } else if (type === "date") {
100
+ const d = new Date(val).getTime();
101
+ if (!isNaN(d)) {
102
+ if (min === void 0 || d < min) min = d;
103
+ if (max === void 0 || d > max) max = d;
104
+ }
105
+ }
106
+ }
107
+ }
108
+ stats.uniqueValues = uniqueVals.size;
109
+ if (min !== void 0) stats.min = min;
110
+ if (max !== void 0) stats.max = max;
111
+ return stats;
112
+ };
113
+
114
+ // src/schema/buildSchema.ts
115
+ var buildSchema = (dataset, registry) => {
116
+ if (!dataset || dataset.length === 0) return {};
117
+ const sample = sampleDataset(dataset, 200);
118
+ const sampleSize = sample.length;
119
+ const keys = /* @__PURE__ */ new Set();
120
+ for (const row of sample) {
121
+ for (const key of Object.keys(row)) {
122
+ keys.add(key);
123
+ }
124
+ }
125
+ const schema = {};
126
+ for (const key of keys) {
127
+ const values = sample.map((row) => row[key]);
128
+ let detectedTypeStr = null;
129
+ if (registry) {
130
+ detectedTypeStr = registry.runDetectors(key, values);
131
+ }
132
+ if (detectedTypeStr) {
133
+ const isNull = values.some((v) => v === null || v === void 0);
134
+ schema[key] = {
135
+ key,
136
+ type: detectedTypeStr,
137
+ nullable: isNull
138
+ };
139
+ const stats2 = computeColumnStats(key, sample, detectedTypeStr);
140
+ schema[key].uniqueValues = stats2.uniqueValues;
141
+ if (stats2.min !== void 0) schema[key].min = stats2.min;
142
+ if (stats2.max !== void 0) schema[key].max = stats2.max;
143
+ continue;
144
+ }
145
+ let nullCount = 0;
146
+ const typeCounts = {};
147
+ for (const row of sample) {
148
+ const val = row[key];
149
+ if (val === null || val === void 0 || val === "") {
150
+ nullCount++;
151
+ } else {
152
+ const detectedType = detectValueType(val);
153
+ typeCounts[detectedType] = (typeCounts[detectedType] || 0) + 1;
154
+ }
155
+ }
156
+ const validValuesCount = sampleSize - nullCount;
157
+ let dominantType = "string";
158
+ if (validValuesCount > 0) {
159
+ let maxCount = 0;
160
+ let topType = "string";
161
+ for (const [type, count] of Object.entries(typeCounts)) {
162
+ if (count > maxCount) {
163
+ maxCount = count;
164
+ topType = type;
165
+ }
166
+ }
167
+ const confidence = maxCount / validValuesCount;
168
+ dominantType = confidence > 0.7 ? topType : "string";
169
+ }
170
+ const stats = computeColumnStats(key, sample, dominantType);
171
+ const columnSchema = {
172
+ key,
173
+ type: dominantType,
174
+ nullable: nullCount > 0,
175
+ uniqueValues: stats.uniqueValues
176
+ };
177
+ if (stats.min !== void 0) columnSchema.min = stats.min;
178
+ if (stats.max !== void 0) columnSchema.max = stats.max;
179
+ schema[key] = columnSchema;
180
+ }
181
+ return schema;
182
+ };
183
+
184
+ // src/hooks/useSorting.ts
185
+ import { useState, useMemo } from "react";
186
+ var useSorting = (data, schema, initialSortColumn = null, initialSortDirection = "asc") => {
187
+ const [sortColumn, setSortColumn] = useState(initialSortColumn);
188
+ const [sortDirection, setSortDirection] = useState(initialSortDirection);
189
+ const handleSort = (columnKey) => {
190
+ if (sortColumn === columnKey) {
191
+ if (sortDirection === "asc") {
192
+ setSortDirection("desc");
193
+ } else {
194
+ setSortColumn(null);
195
+ setSortDirection("asc");
196
+ }
197
+ } else {
198
+ setSortColumn(columnKey);
199
+ setSortDirection("asc");
200
+ }
201
+ };
202
+ const sortedData = useMemo(() => {
203
+ if (!sortColumn || !data || data.length === 0) return data;
204
+ const columnType = schema[sortColumn]?.type || "string";
205
+ return [...data].sort((a, b) => {
206
+ let valA = a[sortColumn];
207
+ let valB = b[sortColumn];
208
+ if (valA === valB) return 0;
209
+ if (valA === null || valA === void 0) return sortDirection === "asc" ? 1 : -1;
210
+ if (valB === null || valB === void 0) return sortDirection === "asc" ? -1 : 1;
211
+ let comparison = 0;
212
+ switch (columnType) {
213
+ case "number":
214
+ case "currency":
215
+ case "percentage":
216
+ const numA = typeof valA === "number" ? valA : Number(String(valA).replace(/[^0-9.-]/g, ""));
217
+ const numB = typeof valB === "number" ? valB : Number(String(valB).replace(/[^0-9.-]/g, ""));
218
+ comparison = (isNaN(numA) ? 0 : numA) - (isNaN(numB) ? 0 : numB);
219
+ break;
220
+ case "date":
221
+ const dateA = new Date(valA).getTime();
222
+ const dateB = new Date(valB).getTime();
223
+ comparison = (isNaN(dateA) ? 0 : dateA) - (isNaN(dateB) ? 0 : dateB);
224
+ break;
225
+ case "boolean":
226
+ const boolA = valA === true || String(valA).toLowerCase() === "true" || String(valA).toLowerCase() === "yes" ? 1 : 0;
227
+ const boolB = valB === true || String(valB).toLowerCase() === "true" || String(valB).toLowerCase() === "yes" ? 1 : 0;
228
+ comparison = boolA - boolB;
229
+ break;
230
+ default:
231
+ comparison = String(valA).localeCompare(String(valB));
232
+ break;
233
+ }
234
+ return sortDirection === "asc" ? comparison : -comparison;
235
+ });
236
+ }, [data, schema, sortColumn, sortDirection]);
237
+ return { sortedData, sortColumn, sortDirection, handleSort };
238
+ };
239
+
240
+ // src/hooks/useFilters.ts
241
+ import { useState as useState2, useMemo as useMemo2 } from "react";
242
+ var useFilters = (data, schema) => {
243
+ const [filters, setFilters] = useState2({});
244
+ const setFilter = (columnKey, value) => {
245
+ setFilters((prev) => {
246
+ const next = { ...prev };
247
+ if (value === null || value === void 0 || value === "") {
248
+ delete next[columnKey];
249
+ } else {
250
+ next[columnKey] = value;
251
+ }
252
+ return next;
253
+ });
254
+ };
255
+ const clearFilters = () => setFilters({});
256
+ const filteredData = useMemo2(() => {
257
+ if (!filters || Object.keys(filters).length === 0 || !data || data.length === 0) {
258
+ return data;
259
+ }
260
+ return data.filter((row) => {
261
+ for (const [key, filterValue] of Object.entries(filters)) {
262
+ const rowValue = row[key];
263
+ const type = schema[key]?.type || "string";
264
+ if (rowValue === null || rowValue === void 0) {
265
+ if (filterValue !== null && filterValue !== "") return false;
266
+ continue;
267
+ }
268
+ switch (type) {
269
+ case "string":
270
+ case "email":
271
+ case "url":
272
+ if (typeof filterValue === "string") {
273
+ if (!String(rowValue).toLowerCase().includes(filterValue.toLowerCase())) return false;
274
+ }
275
+ break;
276
+ case "number":
277
+ case "currency":
278
+ case "percentage":
279
+ if (typeof filterValue === "object" && filterValue !== null) {
280
+ const numVal = typeof rowValue === "number" ? rowValue : Number(String(rowValue).replace(/[^0-9.-]/g, ""));
281
+ if (isNaN(numVal)) return false;
282
+ const { min, max } = filterValue;
283
+ if (min !== void 0 && numVal < min) return false;
284
+ if (max !== void 0 && numVal > max) return false;
285
+ }
286
+ break;
287
+ case "date":
288
+ if (typeof filterValue === "object" && filterValue !== null) {
289
+ const dateVal = new Date(rowValue).getTime();
290
+ if (isNaN(dateVal)) return false;
291
+ const { start, end } = filterValue;
292
+ if (start && dateVal < new Date(start).getTime()) return false;
293
+ if (end && dateVal > new Date(end).getTime()) return false;
294
+ }
295
+ break;
296
+ case "boolean":
297
+ if (typeof filterValue === "boolean") {
298
+ const boolVal = rowValue === true || String(rowValue).toLowerCase() === "true" || String(rowValue).toLowerCase() === "yes";
299
+ if (boolVal !== filterValue) return false;
300
+ }
301
+ break;
302
+ }
303
+ }
304
+ return true;
305
+ });
306
+ }, [data, schema, filters]);
307
+ return { filteredData, filters, setFilter, clearFilters };
308
+ };
309
+
310
+ // src/hooks/usePagination.ts
311
+ import { useState as useState3, useMemo as useMemo3 } from "react";
312
+ var usePagination = (data, initialPageSize = 10) => {
313
+ const [currentPage, setCurrentPage] = useState3(1);
314
+ const [pageSize, setPageSize] = useState3(initialPageSize);
315
+ const totalPages = Math.max(1, Math.ceil(data.length / pageSize));
316
+ const safeCurrentPage = Math.min(currentPage, totalPages);
317
+ const paginatedData = useMemo3(() => {
318
+ const startIndex = (safeCurrentPage - 1) * pageSize;
319
+ return data.slice(startIndex, startIndex + pageSize);
320
+ }, [data, safeCurrentPage, pageSize]);
321
+ return {
322
+ paginatedData,
323
+ currentPage: safeCurrentPage,
324
+ pageSize,
325
+ totalPages,
326
+ setPage: setCurrentPage,
327
+ setPageSize
328
+ };
329
+ };
330
+
331
+ // src/insights/aggregationEngine.ts
332
+ var aggregateData = (dataset, dimension, metrics) => {
333
+ if (!dataset || dataset.length === 0) return [];
334
+ if (!dimension) return [];
335
+ const groups = /* @__PURE__ */ new Map();
336
+ for (const row of dataset) {
337
+ let dimValue = row[dimension];
338
+ if (dimValue === null || dimValue === void 0) {
339
+ dimValue = "Unknown";
340
+ } else if (typeof dimValue === "boolean") {
341
+ dimValue = dimValue ? "Yes" : "No";
342
+ }
343
+ if (!groups.has(dimValue)) {
344
+ const initPoint = { [dimension]: dimValue };
345
+ for (const m of metrics) {
346
+ initPoint[m] = 0;
347
+ }
348
+ groups.set(dimValue, initPoint);
349
+ }
350
+ const group = groups.get(dimValue);
351
+ for (const m of metrics) {
352
+ const val = row[m];
353
+ let numVal = 0;
354
+ if (typeof val === "number") {
355
+ numVal = val;
356
+ } else if (typeof val === "string") {
357
+ numVal = Number(val.replace(/[^0-9.-]/g, ""));
358
+ }
359
+ if (!isNaN(numVal)) {
360
+ group[m] += numVal;
361
+ }
362
+ }
363
+ }
364
+ return Array.from(groups.values());
365
+ };
366
+ var aggregateDataByCount = (dataset, dimension) => {
367
+ if (!dataset || dataset.length === 0) return [];
368
+ const groups = /* @__PURE__ */ new Map();
369
+ for (const row of dataset) {
370
+ let dimValue = row[dimension];
371
+ if (dimValue === null || dimValue === void 0) {
372
+ dimValue = "Unknown";
373
+ } else if (typeof dimValue === "boolean") {
374
+ dimValue = dimValue ? "Yes" : "No";
375
+ }
376
+ groups.set(dimValue, (groups.get(dimValue) || 0) + 1);
377
+ }
378
+ return Array.from(groups.entries()).map(([dim, count]) => ({
379
+ [dimension]: dim,
380
+ count
381
+ }));
382
+ };
383
+
384
+ // src/insights/chartDetector.ts
385
+ var detectChartFields = (schema) => {
386
+ const dimensions = [];
387
+ const metrics = [];
388
+ for (const [key, col] of Object.entries(schema)) {
389
+ const lowerKey = key.toLowerCase();
390
+ const isIdField = lowerKey === "id" || lowerKey === "_id" || lowerKey === "uuid" || lowerKey === "guid" || lowerKey.endsWith("id") || lowerKey.endsWith("_id") || lowerKey.endsWith("uuid");
391
+ if (isIdField) {
392
+ continue;
393
+ }
394
+ const role = determineFieldRole(col.type, col.uniqueValues);
395
+ if (role === "dimension") {
396
+ dimensions.push(key);
397
+ } else if (role === "metric") {
398
+ metrics.push(key);
399
+ }
400
+ }
401
+ return { dimensions, metrics };
402
+ };
403
+ var determineFieldRole = (type, uniqueValues) => {
404
+ switch (type) {
405
+ case "boolean":
406
+ return "dimension";
407
+ case "string":
408
+ if (uniqueValues !== void 0 && uniqueValues > 1 && uniqueValues <= 20) {
409
+ return "dimension";
410
+ }
411
+ return "discard";
412
+ case "date":
413
+ return "dimension";
414
+ case "number":
415
+ case "currency":
416
+ case "percentage":
417
+ return "metric";
418
+ default:
419
+ return "discard";
420
+ }
421
+ };
422
+
423
+ // src/utils/stringUtils.ts
424
+ var ACRONYMS = ["ID", "URL", "API", "JSON", "UUID", "SKU", "IP", "VAT", "GST"];
425
+ var toTitleCase = (str) => {
426
+ if (!str) return "";
427
+ const spaced = str.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").trim();
428
+ return spaced.split(" ").filter(Boolean).map((word) => {
429
+ const upper = word.toUpperCase();
430
+ if (ACRONYMS.includes(upper)) {
431
+ return upper;
432
+ }
433
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
434
+ }).join(" ");
435
+ };
436
+
437
+ // src/insights/insightBuilder.ts
438
+ var generateInsights = (dataset, schema) => {
439
+ if (!dataset || dataset.length === 0 || !schema) return [];
440
+ const { dimensions, metrics } = detectChartFields(schema);
441
+ const widgets = [];
442
+ const maxCharts = 4;
443
+ if (dimensions.length > 0 && metrics.length > 0) {
444
+ for (const dim of dimensions) {
445
+ if (widgets.length >= maxCharts) break;
446
+ const dimInfo = schema[dim];
447
+ const targetMetrics = metrics.slice(0, 2);
448
+ const aggregated = aggregateData(dataset, dim, targetMetrics);
449
+ if (dimInfo.type === "date") {
450
+ aggregated.sort((a, b) => new Date(a[dim]).getTime() - new Date(b[dim]).getTime());
451
+ } else {
452
+ const primaryMetric = targetMetrics[0];
453
+ aggregated.sort((a, b) => (b[primaryMetric] || 0) - (a[primaryMetric] || 0));
454
+ }
455
+ const isLine = dimInfo.type === "date";
456
+ const isPie = !isLine && aggregated.length > 0 && aggregated.length <= 5 && targetMetrics.length === 1;
457
+ widgets.push({
458
+ id: `chart-${dim}-metrics`,
459
+ title: `${targetMetrics.map(toTitleCase).join(" & ")} by ${toTitleCase(dim)}`,
460
+ type: isPie ? "pie" : isLine ? "line" : "bar",
461
+ data: aggregated,
462
+ xAxisKey: dim,
463
+ yAxisKeys: targetMetrics
464
+ });
465
+ }
466
+ }
467
+ if (dimensions.length > 0 && widgets.length < maxCharts) {
468
+ for (const dim of dimensions) {
469
+ if (widgets.length >= maxCharts) break;
470
+ if (widgets.some((w) => w.id.includes(dim))) continue;
471
+ const dimInfo = schema[dim];
472
+ const aggregated = aggregateDataByCount(dataset, dim);
473
+ if (dimInfo.type === "date") {
474
+ aggregated.sort((a, b) => new Date(a[dim]).getTime() - new Date(b[dim]).getTime());
475
+ } else {
476
+ aggregated.sort((a, b) => b.count - a.count);
477
+ }
478
+ const isLine = dimInfo.type === "date";
479
+ widgets.push({
480
+ id: `chart-${dim}-count`,
481
+ title: `Distribution of ${toTitleCase(dim)}`,
482
+ type: isLine ? "line" : aggregated.length <= 6 ? "pie" : "bar",
483
+ data: aggregated,
484
+ xAxisKey: dim,
485
+ yAxisKeys: ["count"]
486
+ });
487
+ }
488
+ }
489
+ return widgets;
490
+ };
491
+
492
+ // src/components/SmartTable.tsx
493
+ import React3, { useMemo as useMemo5 } from "react";
494
+
495
+ // src/components/TableHeader.tsx
496
+ import { jsx, jsxs } from "react/jsx-runtime";
497
+ var getColumnWidthClass = (key, type) => {
498
+ const lowerKey = key.toLowerCase();
499
+ if (lowerKey === "id" || lowerKey === "_id" || type === "boolean") return "rst-col-xs";
500
+ if (type === "date" || type === "number" || lowerKey.includes("status")) return "rst-col-sm";
501
+ if (lowerKey.includes("role") || lowerKey.includes("country") || lowerKey.includes("category")) return "rst-col-md";
502
+ if (lowerKey.includes("name") || lowerKey.includes("email") || lowerKey.includes("url") || lowerKey.includes("description")) return "rst-col-flex";
503
+ return "rst-col-md";
504
+ };
505
+ var TableHeader = ({
506
+ schema,
507
+ sortable = false,
508
+ sortColumn = null,
509
+ sortDirection = "asc",
510
+ onSort
511
+ }) => {
512
+ const columns = Object.keys(schema);
513
+ return /* @__PURE__ */ jsx("thead", { className: "rst-header", children: /* @__PURE__ */ jsx("tr", { className: "rst-header-row", children: columns.map((colKey) => {
514
+ const colInfo = schema[colKey];
515
+ const widthClass = getColumnWidthClass(colKey, colInfo.type);
516
+ return /* @__PURE__ */ jsx(
517
+ "th",
518
+ {
519
+ className: `rst-th rst-th-col-${colKey} rst-th-type-${colInfo.type} ${widthClass} ${sortable && onSort ? "rst-sortable" : ""} ${sortColumn === colKey ? "rst-sort-active" : ""}`,
520
+ onClick: () => {
521
+ if (sortable && onSort) onSort(colKey);
522
+ },
523
+ children: /* @__PURE__ */ jsxs("div", { className: "rst-header-content", children: [
524
+ /* @__PURE__ */ jsx("span", { children: toTitleCase(colKey) }),
525
+ sortable && onSort && /* @__PURE__ */ jsx("span", { className: "rst-sort-icon", children: sortColumn === colKey ? sortDirection === "asc" ? "\u2191" : "\u2193" : "\u21C5" })
526
+ ] })
527
+ },
528
+ colKey
529
+ );
530
+ }) }) });
531
+ };
532
+
533
+ // src/components/TableBody.tsx
534
+ import React, { useRef } from "react";
535
+
536
+ // src/renderers/numberRenderer.tsx
537
+ import { jsx as jsx2 } from "react/jsx-runtime";
538
+ var NumberRenderer = ({ value }) => {
539
+ if (value === null || value === void 0) return null;
540
+ const num = Number(value);
541
+ if (!isNaN(num)) {
542
+ return /* @__PURE__ */ jsx2("span", { className: "rst-cell-number", children: new Intl.NumberFormat().format(num) });
543
+ }
544
+ return /* @__PURE__ */ jsx2("span", { className: "rst-cell-number", children: String(value) });
545
+ };
546
+
547
+ // src/renderers/emailRenderer.tsx
548
+ import { jsx as jsx3 } from "react/jsx-runtime";
549
+ var EmailRenderer = ({ value }) => {
550
+ if (!value) return null;
551
+ const email = String(value);
552
+ return /* @__PURE__ */ jsx3("a", { href: `mailto:${email}`, className: "rst-cell-email", onClick: (e) => e.stopPropagation(), children: email });
553
+ };
554
+
555
+ // src/renderers/urlRenderer.tsx
556
+ import { jsx as jsx4 } from "react/jsx-runtime";
557
+ var UrlRenderer = ({ value }) => {
558
+ if (!value) return null;
559
+ const url = String(value);
560
+ const href = /^https?:\/\//i.test(url) ? url : `https://${url}`;
561
+ return /* @__PURE__ */ jsx4(
562
+ "a",
563
+ {
564
+ href,
565
+ target: "_blank",
566
+ rel: "noopener noreferrer",
567
+ className: "rst-cell-url",
568
+ onClick: (e) => e.stopPropagation(),
569
+ children: url
570
+ }
571
+ );
572
+ };
573
+
574
+ // src/renderers/imageRenderer.tsx
575
+ import { jsx as jsx5 } from "react/jsx-runtime";
576
+ var ImageRenderer = ({ value }) => {
577
+ if (!value) return null;
578
+ const src = String(value);
579
+ return /* @__PURE__ */ jsx5(
580
+ "img",
581
+ {
582
+ src,
583
+ alt: "cell content",
584
+ className: "rst-cell-image",
585
+ style: { maxWidth: "50px", maxHeight: "50px", objectFit: "contain", borderRadius: "4px" },
586
+ loading: "lazy"
587
+ }
588
+ );
589
+ };
590
+
591
+ // src/renderers/percentageRenderer.tsx
592
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
593
+ var PercentageRenderer = ({ value }) => {
594
+ if (value === null || value === void 0) return null;
595
+ const str = String(value);
596
+ if (str.includes("%")) {
597
+ return /* @__PURE__ */ jsx6("span", { className: "rst-cell-percentage", children: str });
598
+ }
599
+ const num = Number(value);
600
+ if (!isNaN(num)) {
601
+ return /* @__PURE__ */ jsxs2("span", { className: "rst-cell-percentage", children: [
602
+ (num * 100).toFixed(2),
603
+ "%"
604
+ ] });
605
+ }
606
+ return /* @__PURE__ */ jsx6("span", { className: "rst-cell-percentage", children: str });
607
+ };
608
+
609
+ // src/renderers/dateRenderer.tsx
610
+ import { jsx as jsx7 } from "react/jsx-runtime";
611
+ var DateRenderer = ({ value }) => {
612
+ if (!value) return null;
613
+ const d = new Date(value);
614
+ if (isNaN(d.getTime())) {
615
+ return /* @__PURE__ */ jsx7("span", { className: "rst-cell-date", children: String(value) });
616
+ }
617
+ return /* @__PURE__ */ jsx7("span", { className: "rst-cell-date", title: d.toISOString(), children: d.toLocaleDateString(void 0, {
618
+ year: "numeric",
619
+ month: "short",
620
+ day: "numeric"
621
+ }) });
622
+ };
623
+
624
+ // src/renderers/booleanRenderer.tsx
625
+ import { jsx as jsx8 } from "react/jsx-runtime";
626
+ var BooleanRenderer = ({ value }) => {
627
+ if (value === null || value === void 0) return null;
628
+ let isTrue = false;
629
+ if (typeof value === "boolean") {
630
+ isTrue = value;
631
+ } else if (typeof value === "string") {
632
+ const lower = value.toLowerCase();
633
+ isTrue = lower === "true" || lower === "yes";
634
+ } else if (typeof value === "number") {
635
+ isTrue = value > 0;
636
+ }
637
+ return /* @__PURE__ */ jsx8(
638
+ "span",
639
+ {
640
+ className: `rst-cell-boolean rst-cell-boolean-${isTrue ? "true" : "false"}`,
641
+ style: {
642
+ display: "inline-block",
643
+ padding: "2px 8px",
644
+ borderRadius: "12px",
645
+ fontSize: "0.85em",
646
+ fontWeight: "bold",
647
+ backgroundColor: isTrue ? "#e6f4ea" : "#fce8e6",
648
+ color: isTrue ? "#137333" : "#c5221f"
649
+ },
650
+ children: isTrue ? "Yes" : "No"
651
+ }
652
+ );
653
+ };
654
+
655
+ // src/renderers/defaultRenderer.tsx
656
+ import { jsx as jsx9 } from "react/jsx-runtime";
657
+ var DefaultRenderer = ({ value }) => {
658
+ if (value === null || value === void 0) return null;
659
+ if (typeof value === "object") {
660
+ return /* @__PURE__ */ jsx9("span", { className: "rst-cell-default", children: JSON.stringify(value) });
661
+ }
662
+ return /* @__PURE__ */ jsx9("span", { className: "rst-cell-default", children: String(value) });
663
+ };
664
+
665
+ // src/plugins/currencyPlugin.tsx
666
+ import { jsx as jsx10 } from "react/jsx-runtime";
667
+ var currencyPlugin = {
668
+ detect: (columnKey, sample) => {
669
+ const lowerKey = columnKey.toLowerCase();
670
+ if (lowerKey.includes("amount") || lowerKey.includes("price") || lowerKey.includes("revenue")) {
671
+ return "currency";
672
+ }
673
+ return null;
674
+ },
675
+ render: ({ value }) => {
676
+ if (value === null || value === void 0) return null;
677
+ let num = NaN;
678
+ if (typeof value === "number") num = value;
679
+ else if (typeof value === "string") num = Number(value.replace(/[^0-9.-]/g, ""));
680
+ if (isNaN(num)) return /* @__PURE__ */ jsx10("span", { children: String(value) });
681
+ const formatted = new Intl.NumberFormat("en-US", {
682
+ style: "currency",
683
+ currency: "USD"
684
+ }).format(num);
685
+ return /* @__PURE__ */ jsx10("span", { style: { color: num < 0 ? "#d32f2f" : "#388e3c", fontWeight: "bold" }, children: formatted });
686
+ }
687
+ };
688
+
689
+ // src/plugins/percentagePlugin.tsx
690
+ import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
691
+ var percentagePlugin = {
692
+ detect: (columnKey, sample) => {
693
+ const lowerKey = columnKey.toLowerCase();
694
+ if (lowerKey.includes("percent") || lowerKey.includes("rate")) {
695
+ return "percentage";
696
+ }
697
+ return null;
698
+ },
699
+ render: ({ value }) => {
700
+ if (value === null || value === void 0) return null;
701
+ let str = String(value);
702
+ let num = Number(value);
703
+ if (!str.includes("%") && !isNaN(num)) {
704
+ return /* @__PURE__ */ jsxs3("span", { style: {
705
+ backgroundColor: "#e3f2fd",
706
+ padding: "2px 6px",
707
+ borderRadius: "4px",
708
+ color: "#1565c0",
709
+ fontSize: "0.9em",
710
+ fontWeight: 600
711
+ }, children: [
712
+ (num * 100).toFixed(1),
713
+ "%"
714
+ ] });
715
+ }
716
+ return /* @__PURE__ */ jsx11("span", { style: {
717
+ backgroundColor: "#e3f2fd",
718
+ padding: "2px 6px",
719
+ borderRadius: "4px",
720
+ color: "#1565c0",
721
+ fontSize: "0.9em",
722
+ fontWeight: 600
723
+ }, children: str });
724
+ }
725
+ };
726
+
727
+ // src/renderers/cellRendererFactory.ts
728
+ var getCellRenderer = (type, registry) => {
729
+ if (registry) {
730
+ for (const plugin of registry.getPlugins()) {
731
+ if (type === "currency" && plugin.render && (plugin === currencyPlugin || plugin.detect?.("amount", []) === "currency")) {
732
+ return plugin.render;
733
+ }
734
+ if (type === "percentage" && plugin.render && (plugin === percentagePlugin || plugin.detect?.("percentage", []) === "percentage")) {
735
+ return plugin.render;
736
+ }
737
+ }
738
+ }
739
+ switch (type) {
740
+ case "number":
741
+ case "currency":
742
+ return NumberRenderer;
743
+ case "email":
744
+ return EmailRenderer;
745
+ case "url":
746
+ return UrlRenderer;
747
+ case "image":
748
+ return ImageRenderer;
749
+ case "percentage":
750
+ return PercentageRenderer;
751
+ case "date":
752
+ return DateRenderer;
753
+ case "boolean":
754
+ return BooleanRenderer;
755
+ case "string":
756
+ default:
757
+ return DefaultRenderer;
758
+ }
759
+ };
760
+
761
+ // src/components/TableBody.tsx
762
+ import { useVirtual } from "react-virtual";
763
+ import { jsx as jsx12 } from "react/jsx-runtime";
764
+ var TableBody = ({ data, schema, registry }) => {
765
+ const columns = Object.keys(schema);
766
+ const parentRef = useRef(null);
767
+ const rowVirtualizer = useVirtual({
768
+ size: data.length,
769
+ parentRef,
770
+ estimateSize: React.useCallback(() => 45, []),
771
+ overscan: 5
772
+ });
773
+ const isVirtualized = data.length > 20;
774
+ if (!isVirtualized) {
775
+ return /* @__PURE__ */ jsx12("tbody", { className: "rst-body", children: data.map((row, index) => /* @__PURE__ */ jsx12("tr", { className: "rst-row", children: columns.map((colKey) => {
776
+ const colInfo = schema[colKey];
777
+ const Renderer = getCellRenderer(colInfo.type, registry);
778
+ const widthClass = getColumnWidthClass(colKey, colInfo.type);
779
+ const val = row[colKey];
780
+ return /* @__PURE__ */ jsx12(
781
+ "td",
782
+ {
783
+ className: `rst-td rst-td-col-${colKey} rst-td-type-${colInfo.type} ${widthClass}`,
784
+ children: colInfo.type === "boolean" ? /* @__PURE__ */ jsx12("span", { className: `rst-badge ${val ? "rst-badge-yes" : "rst-badge-no"}`, children: val ? "Yes" : "No" }) : /* @__PURE__ */ jsx12(Renderer, { value: val })
785
+ },
786
+ colKey
787
+ );
788
+ }) }, index)) });
789
+ }
790
+ return /* @__PURE__ */ jsx12(
791
+ "tbody",
792
+ {
793
+ className: "rst-body",
794
+ ref: parentRef,
795
+ style: {
796
+ height: `${rowVirtualizer.totalSize}px`,
797
+ width: "100%",
798
+ position: "relative"
799
+ },
800
+ children: rowVirtualizer.virtualItems.map((virtualRow) => {
801
+ const row = data[virtualRow.index];
802
+ return /* @__PURE__ */ jsx12(
803
+ "tr",
804
+ {
805
+ className: "rst-row",
806
+ style: {
807
+ position: "absolute",
808
+ top: 0,
809
+ left: 0,
810
+ width: "100%",
811
+ height: `${virtualRow.size}px`,
812
+ transform: `translateY(${virtualRow.start}px)`
813
+ },
814
+ children: columns.map((colKey) => {
815
+ const val = row[colKey];
816
+ const colInfo = schema[colKey];
817
+ const Renderer = getCellRenderer(colInfo.type, registry);
818
+ const widthClass = getColumnWidthClass(colKey, colInfo.type);
819
+ return /* @__PURE__ */ jsx12(
820
+ "td",
821
+ {
822
+ className: `rst-td rst-td-col-${colKey} rst-td-type-${colInfo.type} ${widthClass}`,
823
+ children: colInfo.type === "boolean" ? /* @__PURE__ */ jsx12("span", { className: `rst-badge ${val ? "rst-badge-yes" : "rst-badge-no"}`, children: val ? "Yes" : "No" }) : /* @__PURE__ */ jsx12(Renderer, { value: val })
824
+ },
825
+ colKey
826
+ );
827
+ })
828
+ },
829
+ virtualRow.index
830
+ );
831
+ })
832
+ }
833
+ );
834
+ };
835
+
836
+ // src/components/FilterPanel.tsx
837
+ import { jsx as jsx13, jsxs as jsxs4 } from "react/jsx-runtime";
838
+ var FilterPanel = ({ schema, filters, onFilterChange, onClear }) => {
839
+ const handleStringChange = (key, e) => {
840
+ onFilterChange(key, e.target.value || null);
841
+ };
842
+ const handleNumberChange = (key, field, value) => {
843
+ const current = filters[key] || {};
844
+ const num = parseFloat(value);
845
+ const next = { ...current };
846
+ if (isNaN(num)) {
847
+ delete next[field];
848
+ } else {
849
+ next[field] = num;
850
+ }
851
+ if (Object.keys(next).length === 0) {
852
+ onFilterChange(key, null);
853
+ } else {
854
+ onFilterChange(key, next);
855
+ }
856
+ };
857
+ const handleDateChange = (key, field, value) => {
858
+ const current = filters[key] || {};
859
+ const next = { ...current };
860
+ if (!value) {
861
+ delete next[field];
862
+ } else {
863
+ next[field] = value;
864
+ }
865
+ if (Object.keys(next).length === 0) {
866
+ onFilterChange(key, null);
867
+ } else {
868
+ onFilterChange(key, next);
869
+ }
870
+ };
871
+ const handleBooleanToggle = (key) => {
872
+ const current = filters[key];
873
+ if (current === true) {
874
+ onFilterChange(key, false);
875
+ } else if (current === false) {
876
+ onFilterChange(key, null);
877
+ } else {
878
+ onFilterChange(key, true);
879
+ }
880
+ };
881
+ return /* @__PURE__ */ jsxs4("div", { className: "rst-filter-panel", children: [
882
+ Object.entries(schema).map(([key, colInfo]) => {
883
+ const type = colInfo.type;
884
+ const currentFilter = filters[key];
885
+ return /* @__PURE__ */ jsxs4("div", { className: `rst-filter-group rst-filter-type-${type}`, children: [
886
+ /* @__PURE__ */ jsxs4("label", { children: [
887
+ toTitleCase(key),
888
+ " Filter"
889
+ ] }),
890
+ (type === "string" || type === "email" || type === "url") && /* @__PURE__ */ jsx13(
891
+ "input",
892
+ {
893
+ type: "text",
894
+ placeholder: `Search ${toTitleCase(key)}...`,
895
+ value: currentFilter || "",
896
+ onChange: (e) => handleStringChange(key, e)
897
+ }
898
+ ),
899
+ (type === "number" || type === "currency" || type === "percentage") && /* @__PURE__ */ jsxs4("div", { className: "rst-filter-range", children: [
900
+ /* @__PURE__ */ jsx13(
901
+ "input",
902
+ {
903
+ type: "number",
904
+ placeholder: "Min",
905
+ value: currentFilter?.min ?? "",
906
+ onChange: (e) => handleNumberChange(key, "min", e.target.value)
907
+ }
908
+ ),
909
+ /* @__PURE__ */ jsx13(
910
+ "input",
911
+ {
912
+ type: "number",
913
+ placeholder: "Max",
914
+ value: currentFilter?.max ?? "",
915
+ onChange: (e) => handleNumberChange(key, "max", e.target.value)
916
+ }
917
+ )
918
+ ] }),
919
+ type === "date" && /* @__PURE__ */ jsxs4("div", { className: "rst-filter-range", children: [
920
+ /* @__PURE__ */ jsx13(
921
+ "input",
922
+ {
923
+ type: "date",
924
+ title: "Start Date",
925
+ value: currentFilter?.start || "",
926
+ onChange: (e) => handleDateChange(key, "start", e.target.value)
927
+ }
928
+ ),
929
+ /* @__PURE__ */ jsx13(
930
+ "input",
931
+ {
932
+ type: "date",
933
+ title: "End Date",
934
+ value: currentFilter?.end || "",
935
+ onChange: (e) => handleDateChange(key, "end", e.target.value)
936
+ }
937
+ )
938
+ ] }),
939
+ type === "boolean" && /* @__PURE__ */ jsx13(
940
+ "button",
941
+ {
942
+ className: "rst-filter-toggle",
943
+ onClick: () => handleBooleanToggle(key),
944
+ style: {
945
+ backgroundColor: currentFilter === true ? "#e6f4ea" : currentFilter === false ? "#fce8e6" : "#fff"
946
+ },
947
+ children: currentFilter === true ? "Only Yes" : currentFilter === false ? "Only No" : "All"
948
+ }
949
+ )
950
+ ] }, key);
951
+ }),
952
+ Object.keys(filters).length > 0 && /* @__PURE__ */ jsx13(
953
+ "button",
954
+ {
955
+ className: "rst-filter-clear-btn",
956
+ onClick: onClear,
957
+ children: "Clear All Filters"
958
+ }
959
+ )
960
+ ] });
961
+ };
962
+
963
+ // src/components/InsightsPanel.tsx
964
+ import { useMemo as useMemo4 } from "react";
965
+ import {
966
+ BarChart,
967
+ Bar,
968
+ LineChart,
969
+ Line,
970
+ PieChart,
971
+ Pie,
972
+ Cell,
973
+ XAxis,
974
+ YAxis,
975
+ CartesianGrid,
976
+ Tooltip,
977
+ Legend,
978
+ ResponsiveContainer
979
+ } from "recharts";
980
+ import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
981
+ var COLORS = ["#8884d8", "#82ca9d", "#ffc658", "#ff8042", "#a4de6c", "#d0ed57"];
982
+ var InsightsPanel = ({ data, schema }) => {
983
+ const insights = useMemo4(() => {
984
+ return generateInsights(data, schema);
985
+ }, [data, schema]);
986
+ if (!insights || insights.length === 0) {
987
+ return null;
988
+ }
989
+ return /* @__PURE__ */ jsxs5("div", { className: "rst-insights-panel", children: [
990
+ /* @__PURE__ */ jsx14("h3", { children: "AI Generated Insights" }),
991
+ /* @__PURE__ */ jsx14("div", { className: "rst-insights-grid", children: insights.map((widget) => /* @__PURE__ */ jsxs5("div", { className: "rst-insight-card", children: [
992
+ /* @__PURE__ */ jsx14("h4", { className: "rst-insight-title", children: widget.title }),
993
+ /* @__PURE__ */ jsx14("div", { className: "rst-insight-chart-wrapper", style: { height: "240px" }, children: /* @__PURE__ */ jsx14(ResponsiveContainer, { width: "100%", height: "100%", children: widget.type === "bar" ? /* @__PURE__ */ jsxs5(BarChart, { data: widget.data, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [
994
+ /* @__PURE__ */ jsx14(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
995
+ /* @__PURE__ */ jsx14(XAxis, { dataKey: widget.xAxisKey, tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
996
+ /* @__PURE__ */ jsx14(YAxis, { tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
997
+ /* @__PURE__ */ jsx14(
998
+ Tooltip,
999
+ {
1000
+ contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" },
1001
+ cursor: { fill: "#f1f5f9" }
1002
+ }
1003
+ ),
1004
+ /* @__PURE__ */ jsx14(Legend, { wrapperStyle: { fontSize: "11px", paddingTop: "10px" }, iconType: "circle" }),
1005
+ widget.yAxisKeys.map((yKey, index) => /* @__PURE__ */ jsx14(Bar, { dataKey: yKey, fill: COLORS[index % COLORS.length], radius: [4, 4, 0, 0], barSize: 32 }, yKey))
1006
+ ] }) : widget.type === "line" ? /* @__PURE__ */ jsxs5(LineChart, { data: widget.data, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [
1007
+ /* @__PURE__ */ jsx14(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
1008
+ /* @__PURE__ */ jsx14(XAxis, { dataKey: widget.xAxisKey, tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
1009
+ /* @__PURE__ */ jsx14(YAxis, { tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
1010
+ /* @__PURE__ */ jsx14(Tooltip, { contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" } }),
1011
+ /* @__PURE__ */ jsx14(Legend, { wrapperStyle: { fontSize: "11px", paddingTop: "10px" }, iconType: "circle" }),
1012
+ widget.yAxisKeys.map((yKey, index) => /* @__PURE__ */ jsx14(Line, { type: "monotone", dataKey: yKey, stroke: COLORS[index % COLORS.length], strokeWidth: 2.5, dot: { r: 4, strokeWidth: 2, fill: "#fff" }, activeDot: { r: 6, strokeWidth: 0 } }, yKey))
1013
+ ] }) : /* @__PURE__ */ jsxs5(PieChart, { children: [
1014
+ /* @__PURE__ */ jsx14(Tooltip, { contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" } }),
1015
+ /* @__PURE__ */ jsx14(Legend, { wrapperStyle: { fontSize: "11px" }, iconType: "circle" }),
1016
+ /* @__PURE__ */ jsx14(
1017
+ Pie,
1018
+ {
1019
+ data: widget.data,
1020
+ cx: "50%",
1021
+ cy: "45%",
1022
+ innerRadius: 60,
1023
+ outerRadius: 80,
1024
+ fill: "#8884d8",
1025
+ paddingAngle: 5,
1026
+ dataKey: widget.yAxisKeys[0],
1027
+ nameKey: widget.xAxisKey,
1028
+ label: false,
1029
+ children: widget.data.map((entry, index) => /* @__PURE__ */ jsx14(Cell, { fill: COLORS[index % COLORS.length] }, `cell-${index}`))
1030
+ }
1031
+ )
1032
+ ] }) }) })
1033
+ ] }, widget.id)) })
1034
+ ] });
1035
+ };
1036
+
1037
+ // src/plugins/pluginRegistry.ts
1038
+ var PluginRegistry = class {
1039
+ constructor() {
1040
+ this.plugins = [];
1041
+ }
1042
+ register(pluginOrPlugins) {
1043
+ if (Array.isArray(pluginOrPlugins)) {
1044
+ this.plugins.push(...pluginOrPlugins);
1045
+ } else {
1046
+ this.plugins.push(pluginOrPlugins);
1047
+ }
1048
+ }
1049
+ clear() {
1050
+ this.plugins = [];
1051
+ }
1052
+ runDetectors(columnKey, sample) {
1053
+ for (const plugin of this.plugins) {
1054
+ if (plugin.detect) {
1055
+ const detectedType = plugin.detect(columnKey, sample);
1056
+ if (detectedType) {
1057
+ return detectedType;
1058
+ }
1059
+ }
1060
+ }
1061
+ return null;
1062
+ }
1063
+ getRenderers() {
1064
+ const renderers = {};
1065
+ for (const plugin of this.plugins) {
1066
+ }
1067
+ return renderers;
1068
+ }
1069
+ getPlugins() {
1070
+ return this.plugins;
1071
+ }
1072
+ };
1073
+ var globalPluginRegistry = new PluginRegistry();
1074
+
1075
+ // src/components/SmartTable.tsx
1076
+ import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
1077
+ var SmartTable = ({
1078
+ data,
1079
+ sortable = false,
1080
+ filterable = false,
1081
+ pagination = false,
1082
+ insights = false,
1083
+ plugins = []
1084
+ }) => {
1085
+ const registry = useMemo5(() => {
1086
+ const reg = new PluginRegistry();
1087
+ if (plugins && plugins.length > 0) {
1088
+ reg.register(plugins);
1089
+ }
1090
+ return reg;
1091
+ }, [plugins]);
1092
+ const schema = useMemo5(() => {
1093
+ return buildSchema(data, registry);
1094
+ }, [data, registry]);
1095
+ const { filteredData, filters, setFilter, clearFilters } = useFilters(data, schema);
1096
+ const dataToFilter = filterable ? filteredData : data;
1097
+ const { sortedData, sortColumn, sortDirection, handleSort } = useSorting(dataToFilter, schema);
1098
+ const dataToRenderOrPaginate = sortable ? sortedData : dataToFilter;
1099
+ const { paginatedData, currentPage, totalPages, setPage } = usePagination(dataToRenderOrPaginate, 10);
1100
+ const finalData = pagination ? paginatedData : dataToRenderOrPaginate;
1101
+ const [showFilters, setShowFilters] = React3.useState(true);
1102
+ if (!data || data.length === 0) {
1103
+ return /* @__PURE__ */ jsx15("div", { className: "rst-container", children: /* @__PURE__ */ jsx15("div", { className: "rst-empty", children: "No data to display." }) });
1104
+ }
1105
+ const summaryStats = /* @__PURE__ */ jsxs6("div", { className: "rst-summary-strip", children: [
1106
+ /* @__PURE__ */ jsxs6("span", { children: [
1107
+ data.length,
1108
+ " Records"
1109
+ ] }),
1110
+ /* @__PURE__ */ jsx15("span", { className: "rst-dot", children: "\u2022" }),
1111
+ /* @__PURE__ */ jsxs6("span", { children: [
1112
+ Object.keys(schema).length,
1113
+ " Columns"
1114
+ ] }),
1115
+ /* @__PURE__ */ jsx15("span", { className: "rst-dot", children: "\u2022" }),
1116
+ /* @__PURE__ */ jsxs6("span", { children: [
1117
+ "Types: ",
1118
+ Array.from(new Set(Object.values(schema).map((c) => c.type))).join(", ")
1119
+ ] })
1120
+ ] });
1121
+ return /* @__PURE__ */ jsxs6("div", { className: "rst-container", children: [
1122
+ /* @__PURE__ */ jsx15("header", { className: "rst-main-header", children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end" }, children: [
1123
+ /* @__PURE__ */ jsxs6("div", { children: [
1124
+ /* @__PURE__ */ jsx15("h2", { children: "Smart Table" }),
1125
+ /* @__PURE__ */ jsx15("p", { children: "Auto-generated insights, filters, and records" })
1126
+ ] }),
1127
+ filterable && /* @__PURE__ */ jsxs6(
1128
+ "button",
1129
+ {
1130
+ className: "rst-toggle-filters-btn",
1131
+ onClick: () => setShowFilters(!showFilters),
1132
+ children: [
1133
+ "Filters ",
1134
+ showFilters ? "\u25B2" : "\u25BC"
1135
+ ]
1136
+ }
1137
+ )
1138
+ ] }) }),
1139
+ insights && /* @__PURE__ */ jsx15(InsightsPanel, { data: dataToRenderOrPaginate, schema }),
1140
+ summaryStats,
1141
+ filterable && showFilters && /* @__PURE__ */ jsx15("section", { className: "rst-card", children: /* @__PURE__ */ jsx15(
1142
+ FilterPanel,
1143
+ {
1144
+ schema,
1145
+ filters,
1146
+ onFilterChange: setFilter,
1147
+ onClear: clearFilters
1148
+ }
1149
+ ) }),
1150
+ data.length > 0 && finalData.length === 0 && /* @__PURE__ */ jsxs6("div", { className: "rst-empty-results-card", children: [
1151
+ /* @__PURE__ */ jsx15("div", { className: "rst-empty-icon", children: "\u{1F50D}" }),
1152
+ /* @__PURE__ */ jsx15("h3", { children: "No results found" }),
1153
+ /* @__PURE__ */ jsx15("p", { children: "Try adjusting your filters to find what you're looking for." }),
1154
+ /* @__PURE__ */ jsx15("button", { className: "btn btn-primary", onClick: clearFilters, children: "Clear All Filters" })
1155
+ ] }),
1156
+ data.length > 0 && finalData.length > 0 && /* @__PURE__ */ jsxs6("section", { className: "rst-card", children: [
1157
+ /* @__PURE__ */ jsx15("div", { className: "rst-table-container", children: /* @__PURE__ */ jsxs6("table", { className: "rst-table", children: [
1158
+ /* @__PURE__ */ jsx15(
1159
+ TableHeader,
1160
+ {
1161
+ schema,
1162
+ sortable,
1163
+ sortColumn,
1164
+ sortDirection,
1165
+ onSort: handleSort
1166
+ }
1167
+ ),
1168
+ /* @__PURE__ */ jsx15(TableBody, { data: finalData, schema, registry })
1169
+ ] }) }),
1170
+ pagination && /* @__PURE__ */ jsxs6("div", { className: "rst-pagination", children: [
1171
+ /* @__PURE__ */ jsx15(
1172
+ "button",
1173
+ {
1174
+ disabled: currentPage === 1,
1175
+ onClick: () => setPage(currentPage - 1),
1176
+ children: "Previous"
1177
+ }
1178
+ ),
1179
+ /* @__PURE__ */ jsxs6("span", { children: [
1180
+ "Page ",
1181
+ currentPage,
1182
+ " of ",
1183
+ totalPages
1184
+ ] }),
1185
+ /* @__PURE__ */ jsx15(
1186
+ "button",
1187
+ {
1188
+ disabled: currentPage === totalPages,
1189
+ onClick: () => setPage(currentPage + 1),
1190
+ children: "Next"
1191
+ }
1192
+ )
1193
+ ] })
1194
+ ] })
1195
+ ] });
1196
+ };
1197
+ export {
1198
+ FilterPanel,
1199
+ InsightsPanel,
1200
+ PluginRegistry,
1201
+ SmartTable,
1202
+ aggregateData,
1203
+ aggregateDataByCount,
1204
+ buildSchema,
1205
+ computeColumnStats,
1206
+ currencyPlugin,
1207
+ detectChartFields,
1208
+ detectValueType,
1209
+ generateInsights,
1210
+ getCellRenderer,
1211
+ globalPluginRegistry,
1212
+ percentagePlugin,
1213
+ sampleDataset,
1214
+ useFilters,
1215
+ usePagination,
1216
+ useSorting
1217
+ };
1218
+ //# sourceMappingURL=index.mjs.map