@smallwebco/tinypivot-react 1.0.13 → 1.0.15
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.cjs +89 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +105 -49
- package/dist/index.js.map +1 -1
- package/dist/style.css +119 -3
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -183,9 +183,10 @@ declare function usePivotTable(data: Record<string, unknown>[]): UsePivotTableRe
|
|
|
183
183
|
*/
|
|
184
184
|
declare function setLicenseKey(key: string): Promise<void>;
|
|
185
185
|
/**
|
|
186
|
-
* Enable demo mode
|
|
186
|
+
* Enable demo mode - requires the correct demo secret
|
|
187
|
+
* Returns true if activation succeeded, false if secret was invalid
|
|
187
188
|
*/
|
|
188
|
-
declare function enableDemoMode():
|
|
189
|
+
declare function enableDemoMode(secret: string): Promise<boolean>;
|
|
189
190
|
/**
|
|
190
191
|
* Configure the license secret
|
|
191
192
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -183,9 +183,10 @@ declare function usePivotTable(data: Record<string, unknown>[]): UsePivotTableRe
|
|
|
183
183
|
*/
|
|
184
184
|
declare function setLicenseKey(key: string): Promise<void>;
|
|
185
185
|
/**
|
|
186
|
-
* Enable demo mode
|
|
186
|
+
* Enable demo mode - requires the correct demo secret
|
|
187
|
+
* Returns true if activation succeeded, false if secret was invalid
|
|
187
188
|
*/
|
|
188
|
-
declare function enableDemoMode():
|
|
189
|
+
declare function enableDemoMode(secret: string): Promise<boolean>;
|
|
189
190
|
/**
|
|
190
191
|
* Configure the license secret
|
|
191
192
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/components/DataGrid.tsx
|
|
2
|
-
import { useState as useState8, useMemo as useMemo8, useCallback as useCallback8, useEffect as
|
|
2
|
+
import { useState as useState8, useMemo as useMemo8, useCallback as useCallback8, useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
|
|
5
5
|
// src/hooks/useExcelGrid.ts
|
|
6
|
-
import { useState, useMemo, useCallback } from "react";
|
|
6
|
+
import { useState, useMemo, useCallback, useEffect } from "react";
|
|
7
7
|
import {
|
|
8
8
|
useReactTable,
|
|
9
9
|
getCoreRowModel,
|
|
@@ -26,13 +26,17 @@ function useExcelGrid(options) {
|
|
|
26
26
|
const [columnVisibility, setColumnVisibility] = useState({});
|
|
27
27
|
const [globalFilter, setGlobalFilter] = useState("");
|
|
28
28
|
const [columnStatsCache, setColumnStatsCache] = useState({});
|
|
29
|
+
const dataSignature = useMemo(
|
|
30
|
+
() => `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
31
|
+
[data]
|
|
32
|
+
);
|
|
29
33
|
const columnKeys = useMemo(() => {
|
|
30
34
|
if (data.length === 0) return [];
|
|
31
35
|
return Object.keys(data[0]);
|
|
32
36
|
}, [data]);
|
|
33
37
|
const getColumnStats = useCallback(
|
|
34
38
|
(columnKey) => {
|
|
35
|
-
const cacheKey = `${columnKey}-${
|
|
39
|
+
const cacheKey = `${columnKey}-${dataSignature}`;
|
|
36
40
|
if (!columnStatsCache[cacheKey]) {
|
|
37
41
|
const stats = getColumnUniqueValues(data, columnKey);
|
|
38
42
|
setColumnStatsCache((prev) => ({ ...prev, [cacheKey]: stats }));
|
|
@@ -40,11 +44,14 @@ function useExcelGrid(options) {
|
|
|
40
44
|
}
|
|
41
45
|
return columnStatsCache[cacheKey];
|
|
42
46
|
},
|
|
43
|
-
[data, columnStatsCache]
|
|
47
|
+
[data, columnStatsCache, dataSignature]
|
|
44
48
|
);
|
|
45
49
|
const clearStatsCache = useCallback(() => {
|
|
46
50
|
setColumnStatsCache({});
|
|
47
51
|
}, []);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
clearStatsCache();
|
|
54
|
+
}, [dataSignature, clearStatsCache]);
|
|
48
55
|
const columnDefs = useMemo(() => {
|
|
49
56
|
return columnKeys.map((key) => {
|
|
50
57
|
const stats = getColumnStats(key);
|
|
@@ -172,7 +179,7 @@ function useExcelGrid(options) {
|
|
|
172
179
|
}
|
|
173
180
|
|
|
174
181
|
// src/hooks/usePivotTable.ts
|
|
175
|
-
import { useState as useState3, useMemo as useMemo3, useEffect, useCallback as useCallback3 } from "react";
|
|
182
|
+
import { useState as useState3, useMemo as useMemo3, useEffect as useEffect2, useCallback as useCallback3 } from "react";
|
|
176
183
|
import {
|
|
177
184
|
computeAvailableFields,
|
|
178
185
|
getUnassignedFields,
|
|
@@ -212,11 +219,17 @@ async function setLicenseKey(key) {
|
|
|
212
219
|
}
|
|
213
220
|
notifyListeners();
|
|
214
221
|
}
|
|
215
|
-
function enableDemoMode() {
|
|
222
|
+
async function enableDemoMode(secret) {
|
|
223
|
+
const demoLicense = await getDemoLicenseInfo(secret);
|
|
224
|
+
if (!demoLicense) {
|
|
225
|
+
console.warn("[TinyPivot] Demo mode activation failed - invalid secret");
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
216
228
|
globalDemoMode = true;
|
|
217
|
-
globalLicenseInfo =
|
|
229
|
+
globalLicenseInfo = demoLicense;
|
|
218
230
|
console.info("[TinyPivot] Demo mode enabled - all Pro features unlocked for evaluation");
|
|
219
231
|
notifyListeners();
|
|
232
|
+
return true;
|
|
220
233
|
}
|
|
221
234
|
function configureLicenseSecret(secret) {
|
|
222
235
|
coreConfigureLicenseSecret(secret);
|
|
@@ -304,7 +317,7 @@ function usePivotTable(data) {
|
|
|
304
317
|
showColumnTotals
|
|
305
318
|
});
|
|
306
319
|
}, [data, isConfigured, canUsePivot, rowFields, columnFields, valueFields, showRowTotals, showColumnTotals]);
|
|
307
|
-
|
|
320
|
+
useEffect2(() => {
|
|
308
321
|
if (data.length === 0) return;
|
|
309
322
|
const newKeys = Object.keys(data[0]);
|
|
310
323
|
const storageKey = generateStorageKey(newKeys);
|
|
@@ -333,7 +346,7 @@ function usePivotTable(data) {
|
|
|
333
346
|
}
|
|
334
347
|
}
|
|
335
348
|
}, [data]);
|
|
336
|
-
|
|
349
|
+
useEffect2(() => {
|
|
337
350
|
if (!currentStorageKey) return;
|
|
338
351
|
const config = {
|
|
339
352
|
rowFields,
|
|
@@ -693,7 +706,7 @@ function useColumnResize(initialWidths, minWidth = 60, maxWidth = 600) {
|
|
|
693
706
|
}
|
|
694
707
|
|
|
695
708
|
// src/components/ColumnFilter.tsx
|
|
696
|
-
import { useState as useState5, useEffect as
|
|
709
|
+
import { useState as useState5, useEffect as useEffect3, useRef, useCallback as useCallback5, useMemo as useMemo5 } from "react";
|
|
697
710
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
698
711
|
function ColumnFilter({
|
|
699
712
|
columnName,
|
|
@@ -768,7 +781,7 @@ function ColumnFilter({
|
|
|
768
781
|
onFilter([]);
|
|
769
782
|
onClose();
|
|
770
783
|
}, [onFilter, onClose]);
|
|
771
|
-
|
|
784
|
+
useEffect3(() => {
|
|
772
785
|
const handleClickOutside = (event) => {
|
|
773
786
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
774
787
|
onClose();
|
|
@@ -777,7 +790,7 @@ function ColumnFilter({
|
|
|
777
790
|
document.addEventListener("mousedown", handleClickOutside);
|
|
778
791
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
779
792
|
}, [onClose]);
|
|
780
|
-
|
|
793
|
+
useEffect3(() => {
|
|
781
794
|
const handleKeydown = (event) => {
|
|
782
795
|
if (event.key === "Escape") {
|
|
783
796
|
onClose();
|
|
@@ -788,10 +801,10 @@ function ColumnFilter({
|
|
|
788
801
|
document.addEventListener("keydown", handleKeydown);
|
|
789
802
|
return () => document.removeEventListener("keydown", handleKeydown);
|
|
790
803
|
}, [onClose, applyFilter]);
|
|
791
|
-
|
|
804
|
+
useEffect3(() => {
|
|
792
805
|
searchInputRef.current?.focus();
|
|
793
806
|
}, []);
|
|
794
|
-
|
|
807
|
+
useEffect3(() => {
|
|
795
808
|
setLocalSelected(new Set(selectedValues));
|
|
796
809
|
}, [selectedValues]);
|
|
797
810
|
return /* @__PURE__ */ jsxs("div", { ref: dropdownRef, className: "vpg-filter-dropdown", children: [
|
|
@@ -1320,6 +1333,20 @@ function PivotSkeleton({
|
|
|
1320
1333
|
if (!activeFilters || activeFilters.length === 0) return "";
|
|
1321
1334
|
return activeFilters.map((f) => f.column).join(", ");
|
|
1322
1335
|
}, [activeFilters]);
|
|
1336
|
+
const [showFilterTooltip, setShowFilterTooltip] = useState7(false);
|
|
1337
|
+
const filterTooltipDetails = useMemo7(() => {
|
|
1338
|
+
if (!activeFilters || activeFilters.length === 0) return [];
|
|
1339
|
+
return activeFilters.map((f) => {
|
|
1340
|
+
const maxDisplay = 5;
|
|
1341
|
+
const displayValues = f.values.slice(0, maxDisplay);
|
|
1342
|
+
const remaining = f.values.length - maxDisplay;
|
|
1343
|
+
return {
|
|
1344
|
+
column: f.column,
|
|
1345
|
+
values: displayValues,
|
|
1346
|
+
remaining: remaining > 0 ? remaining : 0
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
}, [activeFilters]);
|
|
1323
1350
|
const handleDragOver = useCallback7(
|
|
1324
1351
|
(area, event) => {
|
|
1325
1352
|
event.preventDefault();
|
|
@@ -1444,37 +1471,66 @@ function PivotSkeleton({
|
|
|
1444
1471
|
/* @__PURE__ */ jsx3("span", { children: "Pivot Table" })
|
|
1445
1472
|
] }),
|
|
1446
1473
|
/* @__PURE__ */ jsxs3("div", { className: "vpg-header-right", children: [
|
|
1447
|
-
hasActiveFilters && /* @__PURE__ */ jsxs3(
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
"path",
|
|
1474
|
+
hasActiveFilters && /* @__PURE__ */ jsxs3(
|
|
1475
|
+
"div",
|
|
1476
|
+
{
|
|
1477
|
+
className: "vpg-filter-indicator",
|
|
1478
|
+
onMouseEnter: () => setShowFilterTooltip(true),
|
|
1479
|
+
onMouseLeave: () => setShowFilterTooltip(false),
|
|
1480
|
+
children: [
|
|
1481
|
+
/* @__PURE__ */ jsx3(
|
|
1482
|
+
"svg",
|
|
1457
1483
|
{
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1484
|
+
className: "vpg-filter-icon",
|
|
1485
|
+
fill: "none",
|
|
1486
|
+
stroke: "currentColor",
|
|
1487
|
+
viewBox: "0 0 24 24",
|
|
1488
|
+
children: /* @__PURE__ */ jsx3(
|
|
1489
|
+
"path",
|
|
1490
|
+
{
|
|
1491
|
+
strokeLinecap: "round",
|
|
1492
|
+
strokeLinejoin: "round",
|
|
1493
|
+
strokeWidth: 2,
|
|
1494
|
+
d: "M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
|
|
1495
|
+
}
|
|
1496
|
+
)
|
|
1462
1497
|
}
|
|
1463
|
-
)
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1498
|
+
),
|
|
1499
|
+
/* @__PURE__ */ jsxs3("span", { className: "vpg-filter-text", children: [
|
|
1500
|
+
"Filtered: ",
|
|
1501
|
+
/* @__PURE__ */ jsx3("strong", { children: filterSummary }),
|
|
1502
|
+
filteredRowCount !== void 0 && totalRowCount !== void 0 && /* @__PURE__ */ jsxs3("span", { className: "vpg-filter-count", children: [
|
|
1503
|
+
"(",
|
|
1504
|
+
filteredRowCount.toLocaleString(),
|
|
1505
|
+
" of ",
|
|
1506
|
+
totalRowCount.toLocaleString(),
|
|
1507
|
+
" rows)"
|
|
1508
|
+
] })
|
|
1509
|
+
] }),
|
|
1510
|
+
showFilterTooltip && /* @__PURE__ */ jsxs3("div", { className: "vpg-filter-tooltip", children: [
|
|
1511
|
+
/* @__PURE__ */ jsx3("div", { className: "vpg-tooltip-header", children: "Active Filters" }),
|
|
1512
|
+
filterTooltipDetails.map((filter) => /* @__PURE__ */ jsxs3("div", { className: "vpg-tooltip-filter", children: [
|
|
1513
|
+
/* @__PURE__ */ jsx3("div", { className: "vpg-tooltip-column", children: filter.column }),
|
|
1514
|
+
/* @__PURE__ */ jsxs3("div", { className: "vpg-tooltip-values", children: [
|
|
1515
|
+
filter.values.map((val, idx) => /* @__PURE__ */ jsx3("span", { className: "vpg-tooltip-value", children: val }, idx)),
|
|
1516
|
+
filter.remaining > 0 && /* @__PURE__ */ jsxs3("span", { className: "vpg-tooltip-more", children: [
|
|
1517
|
+
"+",
|
|
1518
|
+
filter.remaining,
|
|
1519
|
+
" more"
|
|
1520
|
+
] })
|
|
1521
|
+
] })
|
|
1522
|
+
] }, filter.column)),
|
|
1523
|
+
filteredRowCount !== void 0 && totalRowCount !== void 0 && /* @__PURE__ */ jsxs3("div", { className: "vpg-tooltip-summary", children: [
|
|
1524
|
+
"Showing ",
|
|
1525
|
+
filteredRowCount.toLocaleString(),
|
|
1526
|
+
" of ",
|
|
1527
|
+
totalRowCount.toLocaleString(),
|
|
1528
|
+
" rows"
|
|
1529
|
+
] })
|
|
1530
|
+
] })
|
|
1531
|
+
]
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1478
1534
|
isConfigured && /* @__PURE__ */ jsxs3("div", { className: "vpg-config-summary", children: [
|
|
1479
1535
|
/* @__PURE__ */ jsxs3("span", { className: "vpg-summary-badge vpg-rows", children: [
|
|
1480
1536
|
rowFields.length,
|
|
@@ -1886,7 +1942,7 @@ function DataGrid({
|
|
|
1886
1942
|
const end = start + pageSize;
|
|
1887
1943
|
return searchFilteredData.slice(start, end);
|
|
1888
1944
|
}, [enablePagination, searchFilteredData, currentPage, pageSize]);
|
|
1889
|
-
|
|
1945
|
+
useEffect4(() => {
|
|
1890
1946
|
setCurrentPage(1);
|
|
1891
1947
|
}, [columnFilters, globalSearchTerm]);
|
|
1892
1948
|
const selectionBounds = useMemo8(() => {
|
|
@@ -1924,7 +1980,7 @@ function DataGrid({
|
|
|
1924
1980
|
const avg = sum / values.length;
|
|
1925
1981
|
return { count, sum, avg, numericCount: values.length };
|
|
1926
1982
|
}, [selectionBounds, rows, columnKeys]);
|
|
1927
|
-
|
|
1983
|
+
useEffect4(() => {
|
|
1928
1984
|
if (data.length === 0) return;
|
|
1929
1985
|
const widths = {};
|
|
1930
1986
|
const sampleSize = Math.min(100, data.length);
|
|
@@ -1955,7 +2011,7 @@ function DataGrid({
|
|
|
1955
2011
|
},
|
|
1956
2012
|
[enableColumnResize, columnWidths]
|
|
1957
2013
|
);
|
|
1958
|
-
|
|
2014
|
+
useEffect4(() => {
|
|
1959
2015
|
if (!resizingColumnId) return;
|
|
1960
2016
|
const handleResizeMove = (event) => {
|
|
1961
2017
|
const diff = event.clientX - resizeStartX;
|
|
@@ -1985,7 +2041,7 @@ function DataGrid({
|
|
|
1985
2041
|
},
|
|
1986
2042
|
[enableVerticalResize, gridHeight]
|
|
1987
2043
|
);
|
|
1988
|
-
|
|
2044
|
+
useEffect4(() => {
|
|
1989
2045
|
if (!isResizingVertically) return;
|
|
1990
2046
|
const handleVerticalResizeMove = (event) => {
|
|
1991
2047
|
const diff = event.clientY - verticalResizeStartY;
|
|
@@ -2103,12 +2159,12 @@ function DataGrid({
|
|
|
2103
2159
|
},
|
|
2104
2160
|
[isSelecting]
|
|
2105
2161
|
);
|
|
2106
|
-
|
|
2162
|
+
useEffect4(() => {
|
|
2107
2163
|
const handleMouseUp = () => setIsSelecting(false);
|
|
2108
2164
|
document.addEventListener("mouseup", handleMouseUp);
|
|
2109
2165
|
return () => document.removeEventListener("mouseup", handleMouseUp);
|
|
2110
2166
|
}, []);
|
|
2111
|
-
|
|
2167
|
+
useEffect4(() => {
|
|
2112
2168
|
const handleKeydown = (event) => {
|
|
2113
2169
|
if ((event.ctrlKey || event.metaKey) && event.key === "c" && selectionBounds) {
|
|
2114
2170
|
event.preventDefault();
|