pixelize-design-library 2.2.192 → 2.2.194

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.
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import { TableBodyPageProps } from "../TableProps";
3
- declare const TableBody: ({ data, isCheckbox, columns, startRow, columnWidths, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, selections, isLoading, onRowClick, isContent, isLink, isActionFreeze, loadingSkeletonRows, }: TableBodyPageProps) => React.JSX.Element;
3
+ declare const TableBody: ({ data, isCheckbox, columns, startRow, columnWidths, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, selections, isLoading, onRowClick, isContent, isLink, isActionFreeze, loadingSkeletonRows, scrollContainerRef, }: TableBodyPageProps) => React.JSX.Element;
4
4
  export default TableBody;
@@ -84,10 +84,62 @@ var TableRow = react_2.default.memo(function (_a) {
84
84
  });
85
85
  var TableBody = function (_a) {
86
86
  var _b, _c, _d, _e, _f, _g, _h;
87
- var data = _a.data, isCheckbox = _a.isCheckbox, columns = _a.columns, startRow = _a.startRow, columnWidths = _a.columnWidths, freezedBgColor = _a.freezedBgColor, freezedTextColor = _a.freezedTextColor, noBorders = _a.noBorders, handleCheckbox = _a.handleCheckbox, selections = _a.selections, isLoading = _a.isLoading, onRowClick = _a.onRowClick, isContent = _a.isContent, isLink = _a.isLink, isActionFreeze = _a.isActionFreeze, loadingSkeletonRows = _a.loadingSkeletonRows;
87
+ var data = _a.data, isCheckbox = _a.isCheckbox, columns = _a.columns, startRow = _a.startRow, columnWidths = _a.columnWidths, freezedBgColor = _a.freezedBgColor, freezedTextColor = _a.freezedTextColor, noBorders = _a.noBorders, handleCheckbox = _a.handleCheckbox, selections = _a.selections, isLoading = _a.isLoading, onRowClick = _a.onRowClick, isContent = _a.isContent, isLink = _a.isLink, isActionFreeze = _a.isActionFreeze, loadingSkeletonRows = _a.loadingSkeletonRows, scrollContainerRef = _a.scrollContainerRef;
88
88
  var theme = (0, useCustomTheme_1.useCustomTheme)();
89
89
  var _j = (0, react_2.useState)(new Set()), expandedRows = _j[0], setExpandedRows = _j[1];
90
90
  var selectionsSet = (0, react_2.useMemo)(function () { return new Set(selections); }, [selections]);
91
+ // --- Row virtualization ---------------------------------------------------
92
+ // Rows are a fixed 45px tall (see the `height={45}` on every <Td/> below),
93
+ // and the scroll viewport is capped at ~500px, so only ~11 rows are ever
94
+ // visible. Mounting all 50/100 page rows is what makes paging freeze. We
95
+ // render only the rows inside the viewport (+ overscan) and reserve the
96
+ // remaining height with empty spacer rows. Spacer rows (rather than CSS
97
+ // transforms) keep the table in normal flow so the sticky header and
98
+ // `position: sticky` frozen columns keep working.
99
+ // Disabled when expandable content rows are present, since those break the
100
+ // fixed-height assumption.
101
+ var ROW_HEIGHT = 45;
102
+ var OVERSCAN = 6;
103
+ var VIRTUALIZE_THRESHOLD = 30;
104
+ var shouldVirtualize = !isContent && data.length > VIRTUALIZE_THRESHOLD;
105
+ var _k = (0, react_2.useState)(0), scrollTop = _k[0], setScrollTop = _k[1];
106
+ var _l = (0, react_2.useState)(0), viewportH = _l[0], setViewportH = _l[1];
107
+ (0, react_2.useEffect)(function () {
108
+ var el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
109
+ if (!el || !shouldVirtualize)
110
+ return;
111
+ var onScroll = function () { return setScrollTop(el.scrollTop); };
112
+ var measure = function () { return setViewportH(el.clientHeight); };
113
+ measure();
114
+ setScrollTop(el.scrollTop);
115
+ el.addEventListener("scroll", onScroll, { passive: true });
116
+ window.addEventListener("resize", measure);
117
+ return function () {
118
+ el.removeEventListener("scroll", onScroll);
119
+ window.removeEventListener("resize", measure);
120
+ };
121
+ }, [scrollContainerRef, shouldVirtualize, data.length]);
122
+ // New page: jump back to the top so the window recomputes from row 0.
123
+ (0, react_2.useEffect)(function () {
124
+ if (!shouldVirtualize)
125
+ return;
126
+ var el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
127
+ if (el)
128
+ el.scrollTop = 0;
129
+ setScrollTop(0);
130
+ }, [startRow, shouldVirtualize, scrollContainerRef]);
131
+ var total = data.length;
132
+ var firstIndex = 0;
133
+ var lastIndex = total;
134
+ if (shouldVirtualize) {
135
+ var vh = viewportH || 500;
136
+ firstIndex = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
137
+ var count = Math.ceil(vh / ROW_HEIGHT) + OVERSCAN * 2;
138
+ lastIndex = Math.min(total, firstIndex + count);
139
+ }
140
+ var visibleRows = shouldVirtualize ? data.slice(firstIndex, lastIndex) : data;
141
+ var topPad = firstIndex * ROW_HEIGHT;
142
+ var bottomPad = (total - lastIndex) * ROW_HEIGHT;
91
143
  var toggleRowExpansion = (0, react_2.useCallback)(function (rowIndex) {
92
144
  setExpandedRows(function (prev) {
93
145
  var newSet = new Set(prev);
@@ -133,12 +185,17 @@ var TableBody = function (_a) {
133
185
  react_2.default.createElement(react_1.Td, { colSpan: totalVisibleColumns, p: 0, border: "none", position: "relative", height: 0 },
134
186
  react_2.default.createElement(react_1.Box, { position: "absolute", top: 4, left: "50%", transform: "translateX(-50%)", zIndex: 10 },
135
187
  react_2.default.createElement(react_1.Spinner, { size: "sm", color: ((_h = (_g = theme.colors) === null || _g === void 0 ? void 0 : _g.primary) === null || _h === void 0 ? void 0 : _h[500]) || "blue.500" }))))),
136
- data.map(function (row, index) {
188
+ shouldVirtualize && topPad > 0 && (react_2.default.createElement("tr", { "aria-hidden": "true", style: { height: topPad } },
189
+ react_2.default.createElement("td", { colSpan: totalVisibleColumns, style: { padding: 0, border: "none", height: topPad } }))),
190
+ visibleRows.map(function (row, i) {
137
191
  var _a;
192
+ var index = firstIndex + i;
138
193
  var rowIndex = startRow + index;
139
194
  var isExpanded = expandedRows.has(rowIndex);
140
195
  var isChecked = selectionsSet.has(row.id);
141
196
  return (react_2.default.createElement(TableRow, { key: (_a = row.id) !== null && _a !== void 0 ? _a : rowIndex, row: row, rowIndex: rowIndex, index: index, isChecked: isChecked, isExpanded: isExpanded, isContent: isContent, isLink: isLink, isCheckbox: isCheckbox, isLoading: isLoading, isActionFreeze: isActionFreeze, columns: columns, leftOffsets: leftOffsets, noBorders: noBorders, freezedBgColor: freezedBgColor, freezedTextColor: freezedTextColor, theme: theme, borderStyle: borderStyle, hoverStyle: hoverStyle, handleCheckbox: handleCheckbox, onRowClick: onRowClick, toggleRowExpansion: toggleRowExpansion }));
142
- })));
197
+ }),
198
+ shouldVirtualize && bottomPad > 0 && (react_2.default.createElement("tr", { "aria-hidden": "true", style: { height: bottomPad } },
199
+ react_2.default.createElement("td", { colSpan: totalVisibleColumns, style: { padding: 0, border: "none", height: bottomPad } })))));
143
200
  };
144
201
  exports.default = TableBody;
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var react_1 = __importStar(require("react"));
40
+ var react_2 = require("@testing-library/react");
41
+ require("@testing-library/jest-dom");
42
+ var react_3 = require("@chakra-ui/react");
43
+ var TableBody_1 = __importDefault(require("./TableBody"));
44
+ jest.mock("../../../Theme/useCustomTheme", function () { return ({
45
+ useCustomTheme: function () { return ({
46
+ colors: {
47
+ background: { 50: "#fff", 100: "#f9f9f9" },
48
+ backgroundColor: { light: "#fafafa", secondary: "#eee", muted: "#f3f3f3" },
49
+ disabled: { 100: "#eee" },
50
+ gray: { 100: "#f0f0f0", 200: "#eee", 300: "#ddd", 400: "#ccc", 500: "#bbb", 600: "#666" },
51
+ border: { 500: "#ddd" },
52
+ primary: { 500: "#3182ce" },
53
+ text: { 500: "#555" },
54
+ secondary: { opacity: { 4: "rgba(0,0,0,0.04)" } },
55
+ white: "#fff",
56
+ },
57
+ shadows: { lg: "0 0 0" },
58
+ }); },
59
+ }); });
60
+ var columns = [{ id: "name", label: "Name" }];
61
+ var makeData = function (n) {
62
+ return Array.from({ length: n }, function (_, i) { return ({ id: i, name: "Row ".concat(i) }); });
63
+ };
64
+ var Harness = function (_a) {
65
+ var count = _a.count;
66
+ var ref = (0, react_1.useRef)(null);
67
+ return (react_1.default.createElement(react_3.ChakraProvider, null,
68
+ react_1.default.createElement("div", { ref: ref },
69
+ react_1.default.createElement(react_3.Table, null,
70
+ react_1.default.createElement(react_3.Tbody, null,
71
+ react_1.default.createElement(TableBody_1.default, { data: makeData(count), columns: columns, startRow: 0, endRow: count, columnWidths: [120], isCheckbox: false, isContent: false, isLink: false, isActionFreeze: false, noBorders: false, selections: [], handleCheckbox: function () { }, scrollContainerRef: ref }))))));
72
+ };
73
+ describe("TableBody virtualization", function () {
74
+ it("windows large pages and reserves height with spacer rows", function () {
75
+ var _a = (0, react_2.render)(react_1.default.createElement(Harness, { count: 100 })), container = _a.container, queryByText = _a.queryByText;
76
+ // jsdom clientHeight=0 -> 500px fallback -> ceil(500/45)+12 = 24 rows
77
+ expect(queryByText("Row 0")).toBeInTheDocument();
78
+ expect(queryByText("Row 23")).toBeInTheDocument();
79
+ // Rows beyond the window are not mounted.
80
+ expect(queryByText("Row 50")).not.toBeInTheDocument();
81
+ expect(queryByText("Row 99")).not.toBeInTheDocument();
82
+ // A trailing spacer reserves the height of the unrendered rows.
83
+ var spacers = container.querySelectorAll('tr[aria-hidden="true"]');
84
+ expect(spacers.length).toBe(1);
85
+ expect(spacers[0]).toHaveStyle({ height: "".concat((100 - 24) * 45, "px") });
86
+ });
87
+ it("renders every row and no spacers below the threshold", function () {
88
+ var _a = (0, react_2.render)(react_1.default.createElement(Harness, { count: 30 })), container = _a.container, queryByText = _a.queryByText;
89
+ expect(queryByText("Row 0")).toBeInTheDocument();
90
+ expect(queryByText("Row 29")).toBeInTheDocument();
91
+ expect(container.querySelectorAll('tr[aria-hidden="true"]').length).toBe(0);
92
+ });
93
+ });
@@ -278,5 +278,5 @@ function Table(_a) {
278
278
  ? false
279
279
  : "indeterminate", isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze, setColumnsSearch: setColumnsSearch, columnsSearch: columnsSearch, isSelecting: isSelecting })),
280
280
  react_1.default.createElement(react_3.Tbody, null,
281
- react_1.default.createElement(TableBody_1.default, { data: _filteredData, columns: columnsList, startRow: startRow, endRow: endRow, isCheckbox: isCheckbox, columnWidths: columnWidths, noBorders: noBorders, freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_z = theme.colors) === null || _z === void 0 ? void 0 : _z.gray[600], handleCheckbox: handleCheckbox, selections: selection, isLoading: isTableLoading, loadingSkeletonRows: loadingSkeletonRows, onRowClick: onRowClick, isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze }))))))));
281
+ react_1.default.createElement(TableBody_1.default, { data: _filteredData, columns: columnsList, startRow: startRow, endRow: endRow, scrollContainerRef: tableContainerRef, isCheckbox: isCheckbox, columnWidths: columnWidths, noBorders: noBorders, freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_z = theme.colors) === null || _z === void 0 ? void 0 : _z.gray[600], handleCheckbox: handleCheckbox, selections: selection, isLoading: isTableLoading, loadingSkeletonRows: loadingSkeletonRows, onRowClick: onRowClick, isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze }))))))));
282
282
  }
@@ -118,6 +118,7 @@ export type TableBodyPageProps = Pick<TableProps, "columns" | "isCheckbox" | "da
118
118
  selections: (string | number)[];
119
119
  isContent: boolean;
120
120
  isLink: boolean;
121
+ scrollContainerRef?: MutableRefObject<HTMLDivElement | null>;
121
122
  };
122
123
  export type TableFiltersProps = {
123
124
  header: DataObject;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelize-design-library",
3
- "version": "2.2.192",
3
+ "version": "2.2.194",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",