beautiful-grid 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +48 -0
  2. package/cjs/BGrid.js +24 -20
  3. package/cjs/components/Table.js +153 -78
  4. package/cjs/components/TableBody.js +51 -173
  5. package/cjs/components/TableBodyFrozen.js +1 -1
  6. package/cjs/components/TableBodyRow.js +241 -0
  7. package/cjs/store/createAppStore.js +32 -47
  8. package/cjs/utils/cellSelectionGeometry.js +18 -7
  9. package/cjs/utils/checkedIndexes.js +45 -0
  10. package/cjs/utils/coordinate.js +15 -10
  11. package/cjs/utils/getVisibleScrollableRowRange.js +25 -6
  12. package/cjs/utils/gridSearch.js +1 -1
  13. package/cjs/utils/index.js +2 -0
  14. package/cjs/utils/rowHeightMetrics.js +61 -0
  15. package/cjs/utils/useBodyData.js +53 -21
  16. package/esm/BGrid.js +20 -15
  17. package/esm/components/Table.js +100 -27
  18. package/esm/components/TableBody.js +50 -106
  19. package/esm/components/TableBodyFrozen.js +1 -1
  20. package/esm/components/TableBodyRow.js +121 -0
  21. package/esm/store/createAppStore.js +13 -25
  22. package/esm/utils/cellSelectionGeometry.js +16 -7
  23. package/esm/utils/checkedIndexes.js +19 -0
  24. package/esm/utils/coordinate.js +8 -3
  25. package/esm/utils/getVisibleScrollableRowRange.js +24 -6
  26. package/esm/utils/gridSearch.js +1 -1
  27. package/esm/utils/index.js +2 -0
  28. package/esm/utils/rowHeightMetrics.js +56 -0
  29. package/esm/utils/useBodyData.js +26 -17
  30. package/package.json +1 -1
  31. package/style.css +4 -0
  32. package/types/BGrid.d.ts +1 -1
  33. package/types/components/Table.d.ts +2 -0
  34. package/types/components/TableBody.d.ts +12 -2
  35. package/types/components/TableBodyFrozen.d.ts +4 -1
  36. package/types/components/TableBodyRow.d.ts +56 -0
  37. package/types/types.d.ts +9 -4
  38. package/types/utils/cellSelectionGeometry.d.ts +2 -1
  39. package/types/utils/checkedIndexes.d.ts +1 -0
  40. package/types/utils/coordinate.d.ts +2 -1
  41. package/types/utils/getVisibleScrollableRowRange.d.ts +3 -1
  42. package/types/utils/gridSearch.d.ts +1 -1
  43. package/types/utils/index.d.ts +2 -0
  44. package/types/utils/rowHeightMetrics.d.ts +12 -0
package/README.md CHANGED
@@ -18,14 +18,34 @@
18
18
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/axisj/beautiful-grid/pulls)
19
19
  [![Docs](https://img.shields.io/badge/docs-bgrid.axisj.com-blue?logo=googlechrome&logoColor=white)](https://bgrid.axisj.com)
20
20
 
21
+ <p align="center">
22
+ <a href="https://bgrid.axisj.com">
23
+ <img src="https://raw.githubusercontent.com/axisj/beautiful-grid/main/site/public/og-image-en.png" alt="BeautifulGrid — Beautiful, Powerful Open-Source React Data Grid" width="100%" />
24
+ </a>
25
+ </p>
26
+
27
+ <p align="center">
28
+ <a href="https://bgrid.axisj.com"><strong>🌐 Interactive Docs & Live Demos</strong></a>&nbsp;&nbsp;•&nbsp;&nbsp;
29
+ <a href="https://youtu.be/BBq4hPsNDNI"><strong>▶️ Watch 1-Min Walkthrough (YouTube)</strong></a>
30
+ </p>
31
+
21
32
  BeautifulGrid is a high-performance, feature-packed, and beautifully designed open-source React Data Grid for data-heavy business applications. It combines zero-runtime-CSS styling with virtual scrolling, spreadsheet-like cell selection and clipboard operations, built-in and plugin cell editing, multi-level grouped headers, filtering & sorting toolboxes, recoverable column visibility, server/client pagination, pivot table transforms, row reordering, and flexible theming.
22
33
 
23
34
  Explore live interactive examples and in-depth documentation at [bgrid.axisj.com](https://bgrid.axisj.com).
24
35
 
25
36
  ---
26
37
 
38
+ ## Video Walkthrough
39
+
40
+ Watch 1-million row virtual scrolling, keyboard navigation, and interactive cell editing in action (1 min):
41
+
42
+ [![BeautifulGrid 1-Minute Walkthrough](https://img.youtube.com/vi/BBq4hPsNDNI/maxresdefault.jpg)](https://youtu.be/BBq4hPsNDNI)
43
+
44
+ ---
45
+
27
46
  ## Table of Contents
28
47
 
48
+ - [Video Walkthrough](#video-walkthrough)
29
49
  - [Install](#install)
30
50
  - [Quick Start](#quick-start)
31
51
  - [Core Concepts](#core-concepts)
@@ -180,6 +200,33 @@ export default function OrderGridPage() {
180
200
  5. **No Runtime CSS-in-JS**:
181
201
  - High performance and zero runtime overhead using static `.bgrid-*` classes and `--bgrid-*` CSS custom properties.
182
202
 
203
+ ### Variable Row Heights
204
+
205
+ Use `getRowHeight(row, index)` when rows need explicit, data-driven heights. The callback receives the row's
206
+ `values` object and its current displayed index (after client-side sorting or filtering), and returns the complete
207
+ row height in pixels:
208
+
209
+ ```tsx
210
+ <BGrid
211
+ itemHeight={20}
212
+ itemPadding={5}
213
+ getRowHeight={(row, index) => {
214
+ if (row.type === 'group') return 48;
215
+ if (index === 0) return 40;
216
+ return 30;
217
+ }}
218
+ {...props}
219
+ />
220
+ ```
221
+
222
+ BeautifulGrid caches all returned heights and cumulative offsets when the displayed data, callback, or fixed-height
223
+ fallback changes. Scrolling then finds rows with binary search and does not call `getRowHeight` again. Keep the
224
+ callback reference stable (for example with `useCallback`) to avoid rebuilding the cache on parent renders.
225
+
226
+ Automatic content-based row height measurement is intentionally not supported, because DOM measurement would make
227
+ large virtualized datasets less predictable. When `getRowHeight` is omitted—or returns a non-finite or non-positive
228
+ value—the existing `itemHeight + itemPadding * 2` fixed height is used.
229
+
183
230
  ---
184
231
 
185
232
  ## Styling & Theming
@@ -920,6 +967,7 @@ Below is a categorized reference of `<BGrid>` props. For exact TypeScript types,
920
967
  | `summaryHeight` | `number` | `30` | Summary row height in pixels. |
921
968
  | `itemHeight` | `number` | `15` | Body row content height. |
922
969
  | `itemPadding` | `number` | `7` | Body row top/bottom padding (total row height = `itemHeight + itemPadding * 2`). |
970
+ | `getRowHeight` | `(row: T, index: number) => number` | — | Complete rendered height for each displayed row. Invalid values fall back to `itemHeight + itemPadding * 2`. |
923
971
  | `frozenColumnIndex` | `number` | `0` | Pinned column boundary index (columns `< frozenColumnIndex` are fixed). |
924
972
  | `frozenRowCount` | `number` | `0` | Number of leading rows pinned below the top summary row. |
925
973
  | `showLineNumber` | `boolean` | `false` | Shows row index numbers and reorder handles on the left. |
package/cjs/BGrid.js CHANGED
@@ -92,7 +92,7 @@ function computeModelColumns(columns, frozenColumnIndex, duplicateToolboxColumnI
92
92
  }
93
93
  function BGrid(_a) {
94
94
  var _b, _c, _d;
95
- var ref = _a.ref, width = _a.width, height = _a.height, _e = _a.headerHeight, headerHeight = _e === void 0 ? 30 : _e, footerHeight = _a.footerHeight, _f = _a.summaryHeight, summaryHeight = _f === void 0 ? 30 : _f, _g = _a.itemHeight, itemHeight = _g === void 0 ? 15 : _g, _h = _a.itemPadding, itemPadding = _h === void 0 ? 7 : _h, columns = _a.columns, columnsGroup = _a.columnsGroup, columnGroups = _a.columnGroups, onChangeColumns = _a.onChangeColumns, _j = _a.frozenColumnIndex, frozenColumnIndex = _j === void 0 ? 0 : _j, _k = _a.frozenRowCount, frozenRowCount = _k === void 0 ? 0 : _k, _l = _a.data, data = _l === void 0 ? [] : _l, page = _a.page, scrollbar = _a.scrollbar, status = _a.status, pagination = _a.pagination, bottomBarHeight = _a.bottomBarHeight, _m = _a.scrollTop, scrollTop = _m === void 0 ? 0 : _m, _o = _a.scrollLeft, scrollLeft = _o === void 0 ? 0 : _o, className = _a.className, style = _a.style, rowChecked = _a.rowChecked, sort = _a.sort, onClick = _a.onClick, _p = _a.loading, loading = _p === void 0 ? false : _p, spinning = _a.spinning, disabled = _a.disabled, rowKey = _a.rowKey, selectedRowKey = _a.selectedRowKey, editable = _a.editable, onChangeData = _a.onChangeData, showLineNumber = _a.showLineNumber, msg = _a.msg, getRowClassName = _a.getRowClassName, _q = _a.editTrigger, editTrigger = _q === void 0 ? 'dblclick' : _q, cellMergeOptions = _a.cellMergeOptions, cellSelectionOptions = _a.cellSelectionOptions, cellNavigationOptions = _a.cellNavigationOptions, variant = _a.variant, summary = _a.summary, columnSortable = _a.columnSortable, reorder = _a.reorder, pivot = _a.pivot, dataControl = _a.dataControl, icons = _a.icons, columnVisibility = _a.columnVisibility, searchOptions = _a.searchOptions, contextMenuOptions = _a.contextMenuOptions;
95
+ var ref = _a.ref, width = _a.width, height = _a.height, _e = _a.headerHeight, headerHeight = _e === void 0 ? 30 : _e, footerHeight = _a.footerHeight, _f = _a.summaryHeight, summaryHeight = _f === void 0 ? 30 : _f, _g = _a.itemHeight, itemHeight = _g === void 0 ? 15 : _g, _h = _a.itemPadding, itemPadding = _h === void 0 ? 7 : _h, getRowHeight = _a.getRowHeight, columns = _a.columns, columnsGroup = _a.columnsGroup, columnGroups = _a.columnGroups, onChangeColumns = _a.onChangeColumns, _j = _a.frozenColumnIndex, frozenColumnIndex = _j === void 0 ? 0 : _j, _k = _a.frozenRowCount, frozenRowCount = _k === void 0 ? 0 : _k, _l = _a.data, data = _l === void 0 ? [] : _l, page = _a.page, scrollbar = _a.scrollbar, status = _a.status, pagination = _a.pagination, bottomBarHeight = _a.bottomBarHeight, _m = _a.scrollTop, scrollTop = _m === void 0 ? 0 : _m, _o = _a.scrollLeft, scrollLeft = _o === void 0 ? 0 : _o, className = _a.className, style = _a.style, rowChecked = _a.rowChecked, sort = _a.sort, onClick = _a.onClick, _p = _a.loading, loading = _p === void 0 ? false : _p, spinning = _a.spinning, disabled = _a.disabled, rowKey = _a.rowKey, selectedRowKey = _a.selectedRowKey, editable = _a.editable, onChangeData = _a.onChangeData, showLineNumber = _a.showLineNumber, msg = _a.msg, getRowClassName = _a.getRowClassName, _q = _a.editTrigger, editTrigger = _q === void 0 ? 'dblclick' : _q, cellMergeOptions = _a.cellMergeOptions, cellSelectionOptions = _a.cellSelectionOptions, cellNavigationOptions = _a.cellNavigationOptions, variant = _a.variant, summary = _a.summary, columnSortable = _a.columnSortable, reorder = _a.reorder, pivot = _a.pivot, dataControl = _a.dataControl, icons = _a.icons, columnVisibility = _a.columnVisibility, searchOptions = _a.searchOptions, contextMenuOptions = _a.contextMenuOptions;
96
96
  var warnedSearchControlledRef = React.useRef({
97
97
  open: false,
98
98
  query: false,
@@ -387,31 +387,34 @@ function BGrid(_a) {
387
387
  includeRows: false,
388
388
  });
389
389
  }
390
- var sourceIndexByVisibleIndex = resolvedData.map(function (_, i) { return i; });
391
- var visibleIndexBySourceIndex = new Map(resolvedData.map(function (_, i) { return [i, i]; }));
392
390
  return {
393
391
  rows: [],
394
392
  data: resolvedData,
395
- sourceIndexByVisibleIndex: sourceIndexByVisibleIndex,
396
- visibleIndexBySourceIndex: visibleIndexBySourceIndex,
393
+ sourceIndexByVisibleIndex: undefined,
394
+ visibleIndexBySourceIndex: undefined,
397
395
  };
398
396
  }, [queryColumns, resolvedData, resolvedDataControl === null || resolvedDataControl === void 0 ? void 0 : resolvedDataControl.mode, resolvedDataQuery, resolvedRowKey]);
399
397
  var displayData = processedResult.data;
398
+ var rowHeightMetrics = React.useMemo(function () { return (0, utils_1.createRowHeightMetrics)(displayData, itemHeight + itemPadding * 2, getRowHeight); }, [displayData, getRowHeight, itemHeight, itemPadding]);
400
399
  var resolvedFrozenRowCount = pivotEnabled
401
400
  ? 0
402
401
  : Math.min(Math.max(Math.floor(frozenRowCount), 0), displayData.length);
403
402
  var checkedIndexesMap = React.useMemo(function () {
404
403
  if ((resolvedRowChecked === null || resolvedRowChecked === void 0 ? void 0 : resolvedRowChecked.checkedRowKeys) && resolvedRowKey) {
405
- var map_1 = new Map();
406
- resolvedRowChecked.checkedRowKeys.forEach(function (key) {
407
- var fIndex = resolvedData === null || resolvedData === void 0 ? void 0 : resolvedData.findIndex(function (item, index, obj) {
408
- return (0, utils_1.getCellValueByRowKey)(resolvedRowKey, item.values) === key;
409
- });
410
- if (fIndex > -1) {
411
- map_1.set(fIndex, true);
404
+ var remainingKeys = new Set(resolvedRowChecked.checkedRowKeys);
405
+ var map = new Map();
406
+ if (remainingKeys.size > 0 && resolvedData) {
407
+ for (var i = 0; i < resolvedData.length; i++) {
408
+ var itemKey = (0, utils_1.getCellValueByRowKey)(resolvedRowKey, resolvedData[i].values);
409
+ if (remainingKeys.has(itemKey)) {
410
+ map.set(i, true);
411
+ remainingKeys.delete(itemKey);
412
+ if (remainingKeys.size === 0)
413
+ break;
414
+ }
412
415
  }
413
- });
414
- return map_1;
416
+ }
417
+ return map;
415
418
  }
416
419
  if (resolvedRowChecked === null || resolvedRowChecked === void 0 ? void 0 : resolvedRowChecked.checkedIndexes) {
417
420
  return new Map(resolvedRowChecked === null || resolvedRowChecked === void 0 ? void 0 : resolvedRowChecked.checkedIndexes.map(function (id) { return [id, true]; }));
@@ -455,7 +458,7 @@ function BGrid(_a) {
455
458
  }
456
459
  }, [bottomBarHeight, footerHeight]);
457
460
  var initialStoreState = React.useMemo(function () {
458
- var _a, _b;
461
+ var _a, _b, _c;
459
462
  var initialWidth = width !== undefined ? Math.max(width, 100) : 0;
460
463
  var initialHeight = height !== undefined ? Math.max(height, 100) : 0;
461
464
  var containerBorderWidth = 1;
@@ -467,7 +470,7 @@ function BGrid(_a) {
467
470
  containerBorderWidth * 2, 0)
468
471
  : 0;
469
472
  var displayItemCount = contentBodyHeight > 0 ? Math.ceil(contentBodyHeight / (itemHeight + itemPadding * 2)) : 0;
470
- var visibleCheckedCount = processedResult.sourceIndexByVisibleIndex.reduce(function (count, sourceIndex) { return count + (checkedIndexesMap.has(sourceIndex) ? 1 : 0); }, 0);
473
+ var visibleCheckedCount = (0, utils_1.countVisibleCheckedIndexes)(displayData.length, checkedIndexesMap, processedResult.sourceIndexByVisibleIndex);
471
474
  var checkedAll = displayData.length === 0
472
475
  ? false
473
476
  : visibleCheckedCount === displayData.length
@@ -489,7 +492,7 @@ function BGrid(_a) {
489
492
  itemPadding: itemPadding,
490
493
  frozenColumnIndex: resolvedFrozenColumnIndex,
491
494
  frozenRowCount: resolvedFrozenRowCount,
492
- frozenRowsHeight: resolvedFrozenRowCount * (itemHeight + itemPadding * 2),
495
+ frozenRowsHeight: (_a = rowHeightMetrics.offsets[resolvedFrozenRowCount]) !== null && _a !== void 0 ? _a : resolvedFrozenRowCount * (itemHeight + itemPadding * 2),
493
496
  frozenColumnsWidth: (0, utils_1.getFrozenColumnsWidth)({
494
497
  showLineNumber: resolvedShowLineNumber,
495
498
  rowChecked: resolvedRowChecked,
@@ -497,7 +500,7 @@ function BGrid(_a) {
497
500
  frozenColumnIndex: resolvedFrozenColumnIndex,
498
501
  columns: computedColumns,
499
502
  dataLength: displayData.length,
500
- reorderable: (_a = resolvedReorder === null || resolvedReorder === void 0 ? void 0 : resolvedReorder.enabled) !== null && _a !== void 0 ? _a : false,
503
+ reorderable: (_b = resolvedReorder === null || resolvedReorder === void 0 ? void 0 : resolvedReorder.enabled) !== null && _b !== void 0 ? _b : false,
501
504
  }),
502
505
  columns: computedColumns,
503
506
  columnsGroup: visibilityProjection.columnsGroup,
@@ -520,7 +523,7 @@ function BGrid(_a) {
520
523
  searchOptions: resolvedSearchOptions,
521
524
  contextMenuOptions: resolvedContextMenuOptions,
522
525
  page: resolvedPage,
523
- displayPaginationLength: resolvedPage ? (_b = resolvedPage.displayPaginationLength) !== null && _b !== void 0 ? _b : 5 : 0,
526
+ displayPaginationLength: resolvedPage ? (_c = resolvedPage.displayPaginationLength) !== null && _c !== void 0 ? _c : 5 : 0,
524
527
  loading: loading,
525
528
  disabled: resolvedDisabled,
526
529
  spinning: spinning,
@@ -558,6 +561,7 @@ function BGrid(_a) {
558
561
  summaryHeight,
559
562
  itemHeight,
560
563
  itemPadding,
564
+ rowHeightMetrics,
561
565
  resolvedPage,
562
566
  resolvedSummary,
563
567
  checkedIndexesMap,
@@ -609,7 +613,7 @@ function BGrid(_a) {
609
613
  onChangeColumns: resolvedOnChangeColumns, width: width, height: height, className: className, style: style, loading: loading, disabled: resolvedDisabled, spinning: spinning, scrollLeft: scrollLeft, scrollTop: scrollTop, headerHeight: headerHeight, footerHeight: footerHeight, bottomBarHeight: resolvedBottomBarHeight,
610
614
  scrollbar: resolvedScrollbar,
611
615
  status: resolvedStatus,
612
- pagination: resolvedPagination, summaryHeight: summaryHeight, itemHeight: itemHeight, itemPadding: itemPadding, frozenColumnIndex: resolvedFrozenColumnIndex,
616
+ pagination: resolvedPagination, summaryHeight: summaryHeight, itemHeight: itemHeight, itemPadding: itemPadding, rowHeightMetrics: rowHeightMetrics, frozenColumnIndex: resolvedFrozenColumnIndex,
613
617
  frozenRowCount: resolvedFrozenRowCount,
614
618
  rowChecked: resolvedRowChecked, checkedIndexesMap: checkedIndexesMap, sort: resolvedSort, sortParams: sortParams, dataQuery: resolvedDataQuery,
615
619
  dataControl: resolvedDataControl, icons: icons, columnVisibilityState: columnVisibilityState, searchOptions: resolvedSearchOptions,