beautiful-grid 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![NPM version](https://img.shields.io/npm/v/beautiful-grid.svg?style=flat)](https://npmjs.org/package/beautiful-grid)
6
6
  [![NPM downloads](https://img.shields.io/npm/dm/beautiful-grid.svg?style=flat)](https://npmjs.org/package/beautiful-grid)
7
7
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
8
- [![Coverage](https://img.shields.io/badge/coverage-85.02%25-brightgreen.svg)](https://github.com/axisj/beautiful-grid/actions/workflows/tests.yml)
8
+ [![Coverage](https://img.shields.io/badge/coverage-85.63%25-brightgreen.svg)](https://github.com/axisj/beautiful-grid/actions/workflows/tests.yml)
9
9
 
10
10
  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, server/client pagination, pivot table transforms, row reordering, and flexible theming.
11
11
 
package/cjs/BGrid.js CHANGED
@@ -79,7 +79,7 @@ var utils_1 = require("./utils");
79
79
  var store_1 = require("./store");
80
80
  function BGrid(_a) {
81
81
  var _b, _c, _d;
82
- var 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, 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, searchOptions = _a.searchOptions, contextMenuOptions = _a.contextMenuOptions;
82
+ 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, 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, searchOptions = _a.searchOptions, contextMenuOptions = _a.contextMenuOptions;
83
83
  var warnedSearchControlledRef = React.useRef({ open: false, query: false, pivotContextMenu: false });
84
84
  var pivotData = React.useMemo(function () {
85
85
  return (0, utils_1.createPivotData)({
@@ -475,7 +475,7 @@ function BGrid(_a) {
475
475
  initialShowBottomBar,
476
476
  ]);
477
477
  return (React.createElement(store_1.AppStoreProvider, { initialState: initialStoreState },
478
- React.createElement(Table_1.default, { columns: computedColumns,
478
+ React.createElement(Table_1.default, { ref: ref, columns: computedColumns,
479
479
  columnsGroup: resolvedColumnsGroup,
480
480
  columnGroups: resolvedColumnGroups,
481
481
  onChangeColumns: resolvedOnChangeColumns, width: width, height: height, className: className, style: style, loading: loading, spinning: spinning, scrollLeft: scrollLeft, scrollTop: scrollTop, headerHeight: headerHeight, footerHeight: footerHeight, bottomBarHeight: resolvedBottomBarHeight,
@@ -163,12 +163,13 @@ var LazyGridOptionalSurfaces = React.lazy(function () {
163
163
  }); });
164
164
  });
165
165
  function Table(props) {
166
+ var _a;
166
167
  var _this = this;
167
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4;
168
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
168
169
  var cellSelectionOptions = props.cellSelectionOptions, onChangeData = props.onChangeData, sourceIndexByVisibleIndex = props.sourceIndexByVisibleIndex;
169
- var cellSelectionEnabled = (_a = cellSelectionOptions === null || cellSelectionOptions === void 0 ? void 0 : cellSelectionOptions.enabled) !== null && _a !== void 0 ? _a : true;
170
+ var cellSelectionEnabled = (_b = cellSelectionOptions === null || cellSelectionOptions === void 0 ? void 0 : cellSelectionOptions.enabled) !== null && _b !== void 0 ? _b : true;
170
171
  // [Selector Group 1] Layout & Dimensions - 레이아웃 차원
171
- var _5 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
172
+ var _7 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
172
173
  width: s.width,
173
174
  height: s.height,
174
175
  containerBorderWidth: s.containerBorderWidth,
@@ -176,30 +177,30 @@ function Table(props) {
176
177
  style: s.style,
177
178
  itemHeight: s.itemHeight,
178
179
  itemPadding: s.itemPadding,
179
- }); })), width = _5.width, height = _5.height, containerBorderWidth = _5.containerBorderWidth, className = _5.className, style = _5.style, itemHeight = _5.itemHeight, itemPadding = _5.itemPadding;
180
+ }); })), width = _7.width, height = _7.height, containerBorderWidth = _7.containerBorderWidth, className = _7.className, style = _7.style, itemHeight = _7.itemHeight, itemPadding = _7.itemPadding;
180
181
  // [Selector Group 2] Header, Footer, Summary - 헤더/푸터/요약
181
- var _6 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
182
+ var _8 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
182
183
  headerHeight: s.headerHeight,
183
184
  footerHeight: s.footerHeight,
184
185
  bottomBarHeight: s.bottomBarHeight,
185
186
  summaryHeight: s.summaryHeight,
186
- }); })), headerHeight = _6.headerHeight, footerHeight = _6.footerHeight, bottomBarHeight = _6.bottomBarHeight, summaryHeight = _6.summaryHeight;
187
+ }); })), headerHeight = _8.headerHeight, footerHeight = _8.footerHeight, bottomBarHeight = _8.bottomBarHeight, summaryHeight = _8.summaryHeight;
187
188
  // [Selector Group 3] Scroll State - 스크롤 상태
188
- var _7 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
189
+ var _9 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
189
190
  scrollLeft: s.scrollLeft,
190
191
  scrollTop: s.scrollTop,
191
- }); })), scrollLeft = _7.scrollLeft, scrollTop = _7.scrollTop;
192
+ }); })), scrollLeft = _9.scrollLeft, scrollTop = _9.scrollTop;
192
193
  // [Selector Group 4] Content, Columns, Data - 컨텐츠 관련
193
- var _8 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
194
+ var _10 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
194
195
  contentBodyHeight: s.contentBodyHeight,
195
196
  columns: s.columns,
196
197
  data: s.data,
197
198
  frozenColumnsWidth: s.frozenColumnsWidth,
198
199
  frozenRowCount: s.frozenRowCount,
199
200
  frozenRowsHeight: s.frozenRowsHeight,
200
- }); })), contentBodyHeight = _8.contentBodyHeight, columns = _8.columns, data = _8.data, frozenColumnsWidth = _8.frozenColumnsWidth, frozenRowCount = _8.frozenRowCount, frozenRowsHeight = _8.frozenRowsHeight;
201
+ }); })), contentBodyHeight = _10.contentBodyHeight, columns = _10.columns, data = _10.data, frozenColumnsWidth = _10.frozenColumnsWidth, frozenRowCount = _10.frozenRowCount, frozenRowsHeight = _10.frozenRowsHeight;
201
202
  // [Selector Group 4-1] Cell selection & navigation - 셀 선택 및 내비게이션
202
- var _9 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
203
+ var _11 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
203
204
  cellSelectionRange: s.cellSelectionRange,
204
205
  cellSelectionRanges: s.cellSelectionRanges,
205
206
  activeCell: s.activeCell,
@@ -208,9 +209,9 @@ function Table(props) {
208
209
  editItemIndex: s.editItemIndex,
209
210
  editable: s.editable,
210
211
  handleClick: s.handleClick,
211
- }); })), cellSelectionRange = _9.cellSelectionRange, cellSelectionRanges = _9.cellSelectionRanges, activeCell = _9.activeCell, activeCellHost = _9.activeCellHost, cellNavigationOptions = _9.cellNavigationOptions, editItemIndex = _9.editItemIndex, editable = _9.editable, handleClick = _9.handleClick;
212
+ }); })), cellSelectionRange = _11.cellSelectionRange, cellSelectionRanges = _11.cellSelectionRanges, activeCell = _11.activeCell, activeCellHost = _11.activeCellHost, cellNavigationOptions = _11.cellNavigationOptions, editItemIndex = _11.editItemIndex, editable = _11.editable, handleClick = _11.handleClick;
212
213
  // [Selector Group 5] State - 상태
213
- var _10 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
214
+ var _12 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
214
215
  rowChecked: s.rowChecked,
215
216
  page: s.page,
216
217
  loading: s.loading,
@@ -220,8 +221,8 @@ function Table(props) {
220
221
  scrollbar: s.scrollbar,
221
222
  status: s.status,
222
223
  pagination: s.pagination,
223
- }); })), rowChecked = _10.rowChecked, page = _10.page, loading = _10.loading, spinning = _10.spinning, showLineNumber = _10.showLineNumber, summary = _10.summary, scrollbar = _10.scrollbar, status = _10.status, pagination = _10.pagination;
224
- var _11 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
224
+ }); })), rowChecked = _12.rowChecked, page = _12.page, loading = _12.loading, spinning = _12.spinning, showLineNumber = _12.showLineNumber, summary = _12.summary, scrollbar = _12.scrollbar, status = _12.status, pagination = _12.pagination;
225
+ var _13 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
225
226
  searchOpen: s.searchOpen,
226
227
  searchOptions: s.searchOptions,
227
228
  contextMenuOptions: s.contextMenuOptions,
@@ -230,9 +231,9 @@ function Table(props) {
230
231
  storeSourceIndexByVisibleIndex: s.sourceIndexByVisibleIndex,
231
232
  storeRowKey: s.rowKey,
232
233
  cellInteractionSession: s.cellInteractionSession,
233
- }); })), searchOpen = _11.searchOpen, searchOptions = _11.searchOptions, contextMenuOptions = _11.contextMenuOptions, activeSearchMatchIndex = _11.activeSearchMatchIndex, searchMatches = _11.searchMatches, storeSourceIndexByVisibleIndex = _11.storeSourceIndexByVisibleIndex, storeRowKey = _11.storeRowKey, cellInteractionSession = _11.cellInteractionSession;
234
+ }); })), searchOpen = _13.searchOpen, searchOptions = _13.searchOptions, contextMenuOptions = _13.contextMenuOptions, activeSearchMatchIndex = _13.activeSearchMatchIndex, searchMatches = _13.searchMatches, storeSourceIndexByVisibleIndex = _13.storeSourceIndexByVisibleIndex, storeRowKey = _13.storeRowKey, cellInteractionSession = _13.cellInteractionSession;
234
235
  // [Selector Group 6] State Setters - 차원 설정자
235
- var _12 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
236
+ var _14 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
236
237
  setHeight: s.setHeight,
237
238
  setWidth: s.setWidth,
238
239
  setContentBodyHeight: s.setContentBodyHeight,
@@ -245,9 +246,9 @@ function Table(props) {
245
246
  setItemPadding: s.setItemPadding,
246
247
  setLoading: s.setLoading,
247
248
  setSpinning: s.setSpinning,
248
- }); })), setHeight = _12.setHeight, setWidth = _12.setWidth, setContentBodyHeight = _12.setContentBodyHeight, setDisplayItemCount = _12.setDisplayItemCount, setHeaderHeight = _12.setHeaderHeight, setFooterHeight = _12.setFooterHeight, setBottomBarHeight = _12.setBottomBarHeight, setSummaryHeight = _12.setSummaryHeight, setItemHeight = _12.setItemHeight, setItemPadding = _12.setItemPadding, setLoading = _12.setLoading, setSpinning = _12.setSpinning;
249
+ }); })), setHeight = _14.setHeight, setWidth = _14.setWidth, setContentBodyHeight = _14.setContentBodyHeight, setDisplayItemCount = _14.setDisplayItemCount, setHeaderHeight = _14.setHeaderHeight, setFooterHeight = _14.setFooterHeight, setBottomBarHeight = _14.setBottomBarHeight, setSummaryHeight = _14.setSummaryHeight, setItemHeight = _14.setItemHeight, setItemPadding = _14.setItemPadding, setLoading = _14.setLoading, setSpinning = _14.setSpinning;
249
250
  // [Selector Group 7] Data & Column Setters - 데이터/열 설정자
250
- var _13 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
251
+ var _15 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
251
252
  setData: s.setData,
252
253
  setSourceData: s.setSourceData,
253
254
  setProcessedData: s.setProcessedData,
@@ -258,9 +259,9 @@ function Table(props) {
258
259
  setFrozenColumnIndex: s.setFrozenColumnIndex,
259
260
  setFrozenRowCount: s.setFrozenRowCount,
260
261
  setFrozenRowsHeight: s.setFrozenRowsHeight,
261
- }); })), setData = _13.setData, setSourceData = _13.setSourceData, setProcessedData = _13.setProcessedData, setColumns = _13.setColumns, setColumnsGroup = _13.setColumnsGroup, setColumnGroups = _13.setColumnGroups, setFrozenColumnsWidth = _13.setFrozenColumnsWidth, setFrozenColumnIndex = _13.setFrozenColumnIndex, setFrozenRowCount = _13.setFrozenRowCount, setFrozenRowsHeight = _13.setFrozenRowsHeight;
262
+ }); })), setData = _15.setData, setSourceData = _15.setSourceData, setProcessedData = _15.setProcessedData, setColumns = _15.setColumns, setColumnsGroup = _15.setColumnsGroup, setColumnGroups = _15.setColumnGroups, setFrozenColumnsWidth = _15.setFrozenColumnsWidth, setFrozenColumnIndex = _15.setFrozenColumnIndex, setFrozenRowCount = _15.setFrozenRowCount, setFrozenRowsHeight = _15.setFrozenRowsHeight;
262
263
  // [Selector Group 8] Row & Sort Setters - 행/정렬 설정자
263
- var _14 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
264
+ var _16 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
264
265
  setRowChecked: s.setRowChecked,
265
266
  setCheckedIndexesMap: s.setCheckedIndexesMap,
266
267
  setSort: s.setSort,
@@ -277,9 +278,9 @@ function Table(props) {
277
278
  requestSearchOpen: s.requestSearchOpen,
278
279
  openContextMenu: s.openContextMenu,
279
280
  commitCheckboxCell: s.commitCheckboxCell,
280
- }); })), setRowChecked = _14.setRowChecked, setCheckedIndexesMap = _14.setCheckedIndexesMap, setSort = _14.setSort, setSortParams = _14.setSortParams, setDataQuery = _14.setDataQuery, setDataControl = _14.setDataControl, setIcons = _14.setIcons, setActiveToolbox = _14.setActiveToolbox, setRowKey = _14.setRowKey, setFocusedRowKey = _14.setFocusedRowKey, setEditItem = _14.setEditItem, setSearchOptions = _14.setSearchOptions, setContextMenuOptions = _14.setContextMenuOptions, requestSearchOpen = _14.requestSearchOpen, openContextMenu = _14.openContextMenu, commitCheckboxCell = _14.commitCheckboxCell;
281
+ }); })), setRowChecked = _16.setRowChecked, setCheckedIndexesMap = _16.setCheckedIndexesMap, setSort = _16.setSort, setSortParams = _16.setSortParams, setDataQuery = _16.setDataQuery, setDataControl = _16.setDataControl, setIcons = _16.setIcons, setActiveToolbox = _16.setActiveToolbox, setRowKey = _16.setRowKey, setFocusedRowKey = _16.setFocusedRowKey, setEditItem = _16.setEditItem, setSearchOptions = _16.setSearchOptions, setContextMenuOptions = _16.setContextMenuOptions, requestSearchOpen = _16.requestSearchOpen, openContextMenu = _16.openContextMenu, commitCheckboxCell = _16.commitCheckboxCell;
281
282
  // [Selector Group 9] Pagination & Display Setters - 페이지/표시 설정자
282
- var _15 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
283
+ var _17 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
283
284
  setPage: s.setPage,
284
285
  setDisplayPaginationLength: s.setDisplayPaginationLength,
285
286
  setShowLineNumber: s.setShowLineNumber,
@@ -295,9 +296,9 @@ function Table(props) {
295
296
  setScrollbar: s.setScrollbar,
296
297
  setStatus: s.setStatus,
297
298
  setPagination: s.setPagination,
298
- }); })), setPage = _15.setPage, setDisplayPaginationLength = _15.setDisplayPaginationLength, setShowLineNumber = _15.setShowLineNumber, setMsg = _15.setMsg, setRowClassName = _15.setRowClassName, setCellMergeOptions = _15.setCellMergeOptions, setVariant = _15.setVariant, setSummary = _15.setSummary, setColumnSortable = _15.setColumnSortable, setReorder = _15.setReorder, setClassName = _15.setClassName, setStyle = _15.setStyle, setScrollbar = _15.setScrollbar, setStatus = _15.setStatus, setPagination = _15.setPagination;
299
+ }); })), setPage = _17.setPage, setDisplayPaginationLength = _17.setDisplayPaginationLength, setShowLineNumber = _17.setShowLineNumber, setMsg = _17.setMsg, setRowClassName = _17.setRowClassName, setCellMergeOptions = _17.setCellMergeOptions, setVariant = _17.setVariant, setSummary = _17.setSummary, setColumnSortable = _17.setColumnSortable, setReorder = _17.setReorder, setClassName = _17.setClassName, setStyle = _17.setStyle, setScrollbar = _17.setScrollbar, setStatus = _17.setStatus, setPagination = _17.setPagination;
299
300
  // [Selector Group 10] Edit & Event Setters - 편집/이벤트 설정자
300
- var _16 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
301
+ var _18 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
301
302
  setEditable: s.setEditable,
302
303
  setEditTrigger: s.setEditTrigger,
303
304
  setOnClick: s.setOnClick,
@@ -306,8 +307,8 @@ function Table(props) {
306
307
  setOnLoadMore: s.setOnLoadMore,
307
308
  setScroll: s.setScroll,
308
309
  setInitialized: s.setInitialized,
309
- }); })), setEditable = _16.setEditable, setEditTrigger = _16.setEditTrigger, setOnClick = _16.setOnClick, setOnChangeColumns = _16.setOnChangeColumns, setOnChangeData = _16.setOnChangeData, setOnLoadMore = _16.setOnLoadMore, setScroll = _16.setScroll, setInitialized = _16.setInitialized;
310
- var _17 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
310
+ }); })), setEditable = _18.setEditable, setEditTrigger = _18.setEditTrigger, setOnClick = _18.setOnClick, setOnChangeColumns = _18.setOnChangeColumns, setOnChangeData = _18.setOnChangeData, setOnLoadMore = _18.setOnLoadMore, setScroll = _18.setScroll, setInitialized = _18.setInitialized;
311
+ var _19 = (0, store_1.useAppStore)((0, shallow_1.useShallow)(function (s) { return ({
311
312
  setCellSelectionRanges: s.setCellSelectionRanges,
312
313
  setCellSelecting: s.setCellSelecting,
313
314
  clearCellSelection: s.clearCellSelection,
@@ -315,9 +316,10 @@ function Table(props) {
315
316
  moveActiveCell: s.moveActiveCell,
316
317
  setCellNavigationOptions: s.setCellNavigationOptions,
317
318
  syncActiveCellToBounds: s.syncActiveCellToBounds,
318
- }); })), setCellSelectionRanges = _17.setCellSelectionRanges, setCellSelecting = _17.setCellSelecting, clearCellSelection = _17.clearCellSelection, setActiveCell = _17.setActiveCell, moveActiveCell = _17.moveActiveCell, setCellNavigationOptions = _17.setCellNavigationOptions, syncActiveCellToBounds = _17.syncActiveCellToBounds;
319
+ }); })), setCellSelectionRanges = _19.setCellSelectionRanges, setCellSelecting = _19.setCellSelecting, clearCellSelection = _19.clearCellSelection, setActiveCell = _19.setActiveCell, moveActiveCell = _19.moveActiveCell, setCellNavigationOptions = _19.setCellNavigationOptions, syncActiveCellToBounds = _19.syncActiveCellToBounds;
319
320
  var trHeight = itemHeight + itemPadding * 2;
320
321
  var scrollableBodyHeight = Math.max(contentBodyHeight - frozenRowsHeight, 0);
322
+ var scrollOverscanRows = Math.max(KEYBOARD_NAVIGATION_ROW_WINDOW_SIZE, Math.ceil(scrollableBodyHeight / Math.max(trHeight, 1)));
321
323
  var mainViewportWidth = Math.max(width - (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0), 0);
322
324
  var logicalBodyContentHeight = data.length * trHeight;
323
325
  var virtualScrollWindowMetrics = React.useMemo(function () {
@@ -327,7 +329,7 @@ function Table(props) {
327
329
  enabled: scrollbar.variant !== 'native',
328
330
  });
329
331
  }, [contentBodyHeight, logicalBodyContentHeight, scrollbar.variant]);
330
- var _18 = __read(React.useState(0), 2), virtualScrollBase = _18[0], setVirtualScrollBase = _18[1];
332
+ var _20 = __read(React.useState(0), 2), virtualScrollBase = _20[0], setVirtualScrollBase = _20[1];
331
333
  var virtualScrollBaseRef = (0, react_1.useRef)(0);
332
334
  var containerRef = (0, react_1.useRef)(null);
333
335
  var searchPopoverRef = (0, react_1.useRef)(null);
@@ -337,7 +339,8 @@ function Table(props) {
337
339
  var logicalVerticalScrollState = React.useMemo(function () { return ({
338
340
  scrollTop: scrollTop,
339
341
  scrollHeight: virtualScrollWindowMetrics.logicalContentHeight,
340
- }); }, [scrollTop, virtualScrollWindowMetrics.logicalContentHeight]);
342
+ maxScrollTop: virtualScrollWindowMetrics.logicalMaxScroll,
343
+ }); }, [scrollTop, virtualScrollWindowMetrics.logicalContentHeight, virtualScrollWindowMetrics.logicalMaxScroll]);
341
344
  var hasMultiCellSelection = React.useMemo(function () {
342
345
  return cellSelectionRanges.length > 1 ||
343
346
  cellSelectionRanges.some(function (range) {
@@ -447,9 +450,11 @@ function Table(props) {
447
450
  rowHeight: trHeight,
448
451
  frozenRowCount: frozenRowCount,
449
452
  totalRowCount: data.length,
453
+ overscan: scrollOverscanRows,
454
+ leadingOverscan: scrollOverscanRows,
450
455
  windowSize: ((_a = props.reorder) === null || _a === void 0 ? void 0 : _a.enabled) ? 1 : KEYBOARD_NAVIGATION_ROW_WINDOW_SIZE,
451
456
  });
452
- }, [data.length, frozenRowCount, (_b = props.reorder) === null || _b === void 0 ? void 0 : _b.enabled, scrollTop, scrollableBodyHeight, trHeight]);
457
+ }, [data.length, frozenRowCount, (_c = props.reorder) === null || _c === void 0 ? void 0 : _c.enabled, scrollOverscanRows, scrollTop, scrollableBodyHeight, trHeight]);
453
458
  var frozenRowRange = React.useMemo(function () { return ({ startRowIndex: 0, endRowIndex: frozenRowCount }); }, [frozenRowCount]);
454
459
  var scrollableRowRange = React.useMemo(function () { return ({
455
460
  startRowIndex: visibleScrollableRows.startRowIndex,
@@ -617,6 +622,15 @@ function Table(props) {
617
622
  }, []);
618
623
  var latestScrollRef = (0, react_1.useRef)({ top: scrollTop, left: scrollLeft });
619
624
  var latestCommittedScrollRef = (0, react_1.useRef)({ top: scrollTop, left: scrollLeft });
625
+ var scrollPropsRef = (0, react_1.useRef)({ top: props.scrollTop, left: props.scrollLeft });
626
+ var syncedDataPropRef = (0, react_1.useRef)(props.data);
627
+ var _21 = __read(React.useState(null), 2), rowScrollRequest = _21[0], setRowScrollRequest = _21[1];
628
+ React.useImperativeHandle(props.ref, function () { return ({
629
+ scrollToRow: function (rowIndex, options) {
630
+ var _a;
631
+ setRowScrollRequest({ rowIndex: rowIndex, align: (_a = options === null || options === void 0 ? void 0 : options.align) !== null && _a !== void 0 ? _a : 'nearest' });
632
+ },
633
+ }); }, []);
620
634
  var scrollRafRef = (0, react_1.useRef)(null);
621
635
  var scrollIdleTimerRef = (0, react_1.useRef)(null);
622
636
  var selectionAutoScrollRafRef = (0, react_1.useRef)(null);
@@ -627,7 +641,7 @@ function Table(props) {
627
641
  var selectionMetricsRef = (0, react_1.useRef)({
628
642
  columns: columns,
629
643
  dataLength: data.length,
630
- frozenColumnIndex: (_c = props.frozenColumnIndex) !== null && _c !== void 0 ? _c : 0,
644
+ frozenColumnIndex: (_d = props.frozenColumnIndex) !== null && _d !== void 0 ? _d : 0,
631
645
  frozenRowCount: frozenRowCount,
632
646
  frozenColumnsWidth: frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0,
633
647
  frozenRowsHeight: frozenRowsHeight,
@@ -1489,7 +1503,7 @@ function Table(props) {
1489
1503
  setFrozenColumnIndex((_d = props.frozenColumnIndex) !== null && _d !== void 0 ? _d : 0);
1490
1504
  }, [
1491
1505
  props.frozenColumnIndex,
1492
- (_d = props.reorder) === null || _d === void 0 ? void 0 : _d.enabled,
1506
+ (_e = props.reorder) === null || _e === void 0 ? void 0 : _e.enabled,
1493
1507
  showLineNumber,
1494
1508
  rowChecked,
1495
1509
  itemHeight,
@@ -1505,7 +1519,7 @@ function Table(props) {
1505
1519
  var nextFrozenRowCount = Math.min(Math.max(Math.floor((_c = props.frozenRowCount) !== null && _c !== void 0 ? _c : 0), 0), rowCount);
1506
1520
  setFrozenRowCount(nextFrozenRowCount);
1507
1521
  setFrozenRowsHeight(nextFrozenRowCount * trHeight);
1508
- }, [data.length, (_e = props.data) === null || _e === void 0 ? void 0 : _e.length, props.frozenRowCount, setFrozenRowCount, setFrozenRowsHeight, trHeight]);
1522
+ }, [data.length, (_f = props.data) === null || _f === void 0 ? void 0 : _f.length, props.frozenRowCount, setFrozenRowCount, setFrozenRowsHeight, trHeight]);
1509
1523
  // [Group 5] Data & columns
1510
1524
  (0, react_1.useEffect)(function () {
1511
1525
  if (props.data !== undefined &&
@@ -1525,6 +1539,7 @@ function Table(props) {
1525
1539
  setSourceData(props.sourceData);
1526
1540
  }
1527
1541
  }
1542
+ syncedDataPropRef.current = props.data;
1528
1543
  }, [
1529
1544
  props.data,
1530
1545
  props.sourceData,
@@ -1595,19 +1610,23 @@ function Table(props) {
1595
1610
  }
1596
1611
  }, [
1597
1612
  props.page,
1598
- (_f = props.page) === null || _f === void 0 ? void 0 : _f.currentPage,
1599
- (_g = props.page) === null || _g === void 0 ? void 0 : _g.loading,
1600
- (_h = props.page) === null || _h === void 0 ? void 0 : _h.pageSize,
1601
- (_j = props.page) === null || _j === void 0 ? void 0 : _j.totalPages,
1602
- (_k = props.page) === null || _k === void 0 ? void 0 : _k.totalElements,
1603
- (_l = props.page) === null || _l === void 0 ? void 0 : _l.onChange,
1604
- (_m = props.page) === null || _m === void 0 ? void 0 : _m.displayPaginationLength,
1613
+ (_g = props.page) === null || _g === void 0 ? void 0 : _g.currentPage,
1614
+ (_h = props.page) === null || _h === void 0 ? void 0 : _h.loading,
1615
+ (_j = props.page) === null || _j === void 0 ? void 0 : _j.pageSize,
1616
+ (_k = props.page) === null || _k === void 0 ? void 0 : _k.totalPages,
1617
+ (_l = props.page) === null || _l === void 0 ? void 0 : _l.totalElements,
1618
+ (_m = props.page) === null || _m === void 0 ? void 0 : _m.onChange,
1619
+ (_o = props.page) === null || _o === void 0 ? void 0 : _o.displayPaginationLength,
1605
1620
  setPage,
1606
1621
  setDisplayPaginationLength,
1607
1622
  ]);
1623
+ var previousPageRef = (0, react_1.useRef)((_p = props.page) === null || _p === void 0 ? void 0 : _p.currentPage);
1608
1624
  (0, react_1.useEffect)(function () {
1609
- resetScrollPosition('top');
1610
- }, [resetScrollPosition, (_o = props.page) === null || _o === void 0 ? void 0 : _o.currentPage]);
1625
+ var _a, _b;
1626
+ if (previousPageRef.current !== ((_a = props.page) === null || _a === void 0 ? void 0 : _a.currentPage))
1627
+ resetScrollPosition('top');
1628
+ previousPageRef.current = (_b = props.page) === null || _b === void 0 ? void 0 : _b.currentPage;
1629
+ }, [resetScrollPosition, (_q = props.page) === null || _q === void 0 ? void 0 : _q.currentPage]);
1611
1630
  // [Group 8] Row key & selection
1612
1631
  (0, react_1.useEffect)(function () {
1613
1632
  setRowKey(props.rowKey);
@@ -1679,10 +1698,10 @@ function Table(props) {
1679
1698
  }, [
1680
1699
  columns.length,
1681
1700
  data.length,
1682
- (_q = (_p = props.cellNavigationOptions) === null || _p === void 0 ? void 0 : _p.activeCell) === null || _q === void 0 ? void 0 : _q.columnIndex,
1683
- (_s = (_r = props.cellNavigationOptions) === null || _r === void 0 ? void 0 : _r.activeCell) === null || _s === void 0 ? void 0 : _s.rowIndex,
1684
- (_u = (_t = props.cellNavigationOptions) === null || _t === void 0 ? void 0 : _t.defaultActiveCell) === null || _u === void 0 ? void 0 : _u.columnIndex,
1685
- (_w = (_v = props.cellNavigationOptions) === null || _v === void 0 ? void 0 : _v.defaultActiveCell) === null || _w === void 0 ? void 0 : _w.rowIndex,
1701
+ (_s = (_r = props.cellNavigationOptions) === null || _r === void 0 ? void 0 : _r.activeCell) === null || _s === void 0 ? void 0 : _s.columnIndex,
1702
+ (_u = (_t = props.cellNavigationOptions) === null || _t === void 0 ? void 0 : _t.activeCell) === null || _u === void 0 ? void 0 : _u.rowIndex,
1703
+ (_w = (_v = props.cellNavigationOptions) === null || _v === void 0 ? void 0 : _v.defaultActiveCell) === null || _w === void 0 ? void 0 : _w.columnIndex,
1704
+ (_y = (_x = props.cellNavigationOptions) === null || _x === void 0 ? void 0 : _x.defaultActiveCell) === null || _y === void 0 ? void 0 : _y.rowIndex,
1686
1705
  syncActiveCellToBounds,
1687
1706
  ]);
1688
1707
  // setInitialized + DOM event bindings
@@ -1691,7 +1710,7 @@ function Table(props) {
1691
1710
  var containerRefCurrent = containerRef.current;
1692
1711
  var scrollContainerRefCurrent = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
1693
1712
  if (scrollContainerRefCurrent) {
1694
- scrollContainerRefCurrent.removeEventListener('scroll', onScroll);
1713
+ scrollContainerRefCurrent.removeEventListener('scroll', onScroll, true);
1695
1714
  scrollContainerRefCurrent.addEventListener('scroll', onScroll, { passive: true, capture: true });
1696
1715
  }
1697
1716
  return function () {
@@ -1704,17 +1723,55 @@ function Table(props) {
1704
1723
  scrollIdleTimerRef.current = null;
1705
1724
  }
1706
1725
  containerRefCurrent === null || containerRefCurrent === void 0 ? void 0 : containerRefCurrent.removeAttribute('data-bgrid-scrolling');
1707
- scrollContainerRefCurrent === null || scrollContainerRefCurrent === void 0 ? void 0 : scrollContainerRefCurrent.removeEventListener('scroll', onScroll);
1726
+ scrollContainerRefCurrent === null || scrollContainerRefCurrent === void 0 ? void 0 : scrollContainerRefCurrent.removeEventListener('scroll', onScroll, true);
1708
1727
  };
1709
1728
  }, [onScroll, setInitialized]);
1710
1729
  React.useLayoutEffect(function () {
1711
1730
  var _a, _b;
1712
1731
  if (!scrollContainerRef.current)
1713
1732
  return;
1714
- var nextTop = (_a = props.scrollTop) !== null && _a !== void 0 ? _a : latestScrollRef.current.top;
1715
- var nextLeft = (_b = props.scrollLeft) !== null && _b !== void 0 ? _b : latestScrollRef.current.left;
1733
+ if (props.data !== syncedDataPropRef.current)
1734
+ return;
1735
+ // Resizing or changing the data also changes commitScroll. Only a changed
1736
+ // scroll prop should override the user's current position.
1737
+ var nextTop = props.scrollTop !== scrollPropsRef.current.top
1738
+ ? (_a = props.scrollTop) !== null && _a !== void 0 ? _a : latestScrollRef.current.top
1739
+ : latestScrollRef.current.top;
1740
+ var nextLeft = props.scrollLeft !== scrollPropsRef.current.left
1741
+ ? (_b = props.scrollLeft) !== null && _b !== void 0 ? _b : latestScrollRef.current.left
1742
+ : latestScrollRef.current.left;
1743
+ scrollPropsRef.current = { top: props.scrollTop, left: props.scrollLeft };
1716
1744
  commitScroll(nextTop, nextLeft);
1717
- }, [commitScroll, props.scrollLeft, props.scrollTop]);
1745
+ }, [commitScroll, data, props.data, props.scrollLeft, props.scrollTop]);
1746
+ React.useLayoutEffect(function () {
1747
+ if (!rowScrollRequest)
1748
+ return;
1749
+ // Prop data reaches the per-grid store in an effect. Wait for that render
1750
+ // so appending a row and requesting it in the same event is supported.
1751
+ if (props.data !== syncedDataPropRef.current)
1752
+ return;
1753
+ setRowScrollRequest(null);
1754
+ var rowIndex = rowScrollRequest.rowIndex, align = rowScrollRequest.align;
1755
+ if (!Number.isInteger(rowIndex) || rowIndex < frozenRowCount || rowIndex >= data.length)
1756
+ return;
1757
+ if (scrollableBodyHeight <= 0)
1758
+ return;
1759
+ var rowTop = (rowIndex - frozenRowCount) * trHeight;
1760
+ var rowBottom = rowTop + trHeight;
1761
+ var _a = latestScrollRef.current, top = _a.top, left = _a.left;
1762
+ var nextTop = top;
1763
+ if (align === 'start')
1764
+ nextTop = rowTop;
1765
+ else if (align === 'center')
1766
+ nextTop = rowTop - (scrollableBodyHeight - trHeight) / 2;
1767
+ else if (align === 'end')
1768
+ nextTop = rowBottom - scrollableBodyHeight;
1769
+ else if (rowTop < top)
1770
+ nextTop = rowTop;
1771
+ else if (rowBottom > top + scrollableBodyHeight)
1772
+ nextTop = rowBottom - scrollableBodyHeight;
1773
+ commitScroll(nextTop, left);
1774
+ }, [commitScroll, data, frozenRowCount, props.data, rowScrollRequest, scrollableBodyHeight, trHeight]);
1718
1775
  (0, react_1.useEffect)(function () {
1719
1776
  latestScrollRef.current = { top: scrollTop, left: scrollLeft };
1720
1777
  latestCommittedScrollRef.current = { top: scrollTop, left: scrollLeft };
@@ -1766,8 +1823,8 @@ function Table(props) {
1766
1823
  endCellSelectionDrag();
1767
1824
  clearCellSelection();
1768
1825
  }, [cellSelectionEnabled, clearCellSelection, endCellSelectionDrag]);
1769
- var clearCellSelectionOnEscape = (_y = (_x = props.cellSelectionOptions) === null || _x === void 0 ? void 0 : _x.clearOnEscape) !== null && _y !== void 0 ? _y : true;
1770
- var clearCellSelectionOnOutsideClick = (_0 = (_z = props.cellSelectionOptions) === null || _z === void 0 ? void 0 : _z.clearOnOutsideClick) !== null && _0 !== void 0 ? _0 : true;
1826
+ var clearCellSelectionOnEscape = (_0 = (_z = props.cellSelectionOptions) === null || _z === void 0 ? void 0 : _z.clearOnEscape) !== null && _0 !== void 0 ? _0 : true;
1827
+ var clearCellSelectionOnOutsideClick = (_2 = (_1 = props.cellSelectionOptions) === null || _1 === void 0 ? void 0 : _1.clearOnOutsideClick) !== null && _2 !== void 0 ? _2 : true;
1771
1828
  var keyboardRuntimeRef = (0, react_1.useRef)({
1772
1829
  activeCell: activeCell,
1773
1830
  cellInteractionSession: cellInteractionSession,
@@ -1785,7 +1842,7 @@ function Table(props) {
1785
1842
  editable: editable,
1786
1843
  editItemIndex: editItemIndex,
1787
1844
  endCellSelectionDrag: endCellSelectionDrag,
1788
- frozenColumnIndex: (_1 = props.frozenColumnIndex) !== null && _1 !== void 0 ? _1 : 0,
1845
+ frozenColumnIndex: (_3 = props.frozenColumnIndex) !== null && _3 !== void 0 ? _3 : 0,
1789
1846
  frozenRowCount: frozenRowCount,
1790
1847
  height: height,
1791
1848
  handleClick: handleClick,
@@ -1818,7 +1875,7 @@ function Table(props) {
1818
1875
  editable: editable,
1819
1876
  editItemIndex: editItemIndex,
1820
1877
  endCellSelectionDrag: endCellSelectionDrag,
1821
- frozenColumnIndex: (_2 = props.frozenColumnIndex) !== null && _2 !== void 0 ? _2 : 0,
1878
+ frozenColumnIndex: (_4 = props.frozenColumnIndex) !== null && _4 !== void 0 ? _4 : 0,
1822
1879
  frozenRowCount: frozenRowCount,
1823
1880
  height: height,
1824
1881
  handleClick: handleClick,
@@ -2200,14 +2257,14 @@ function Table(props) {
2200
2257
  };
2201
2258
  }, []);
2202
2259
  return (React.createElement(EditorPortalRoot_1.EditorPortalContext.Provider, { value: editorPortalContext },
2203
- React.createElement(Container, { ref: containerRef, role: 'grid', style: __assign(__assign({}, style), { width: width, height: height, borderWidth: "".concat(containerBorderWidth, "px") }), className: className, tabIndex: 0, onFocus: function (event) {
2260
+ React.createElement(Container, { ref: containerRef, role: 'grid', style: __assign(__assign({}, style), (_a = { width: width, height: height, borderWidth: "".concat(containerBorderWidth, "px") }, _a['--bgrid-virtual-row-height'] = "".concat(trHeight, "px"), _a)), className: className, tabIndex: 0, onFocus: function (event) {
2204
2261
  if (event.target !== event.currentTarget)
2205
2262
  return;
2206
2263
  var gateway = event.currentTarget.querySelector('[data-bgrid-text-editor-gateway="true"]');
2207
2264
  if (gateway instanceof HTMLInputElement) {
2208
2265
  gateway.focus({ preventScroll: true });
2209
2266
  }
2210
- }, "data-scroll-variant": scrollbar.variant, "data-vertical-scrollbar": scrollbar.variant !== 'native' && scrollbar.vertical.visible ? 'visible' : 'hidden', "data-bgrid-cell-selection-enabled": cellSelectionEnabled ? 'true' : 'false', "data-bgrid-frozen-columns": ((_3 = props.frozenColumnIndex) !== null && _3 !== void 0 ? _3 : 0) > 0 ? 'true' : 'false' },
2267
+ }, "data-scroll-variant": scrollbar.variant, "data-vertical-scrollbar": scrollbar.variant !== 'native' && scrollbar.vertical.visible ? 'visible' : 'hidden', "data-bgrid-cell-selection-enabled": cellSelectionEnabled ? 'true' : 'false', "data-bgrid-frozen-columns": ((_5 = props.frozenColumnIndex) !== null && _5 !== void 0 ? _5 : 0) > 0 ? 'true' : 'false' },
2211
2268
  scrollbar.variant !== 'native' && scrollbar.vertical.visible && (React.createElement("div", { className: 'bgrid-vertical-scrollbar-gutter', "aria-hidden": 'true' })),
2212
2269
  scrollbar.variant !== 'native' && scrollbar.vertical.visible && (React.createElement("div", { className: 'bgrid-vertical-scrollbar-area', style: {
2213
2270
  top: 0,
@@ -2236,6 +2293,11 @@ function Table(props) {
2236
2293
  React.createElement(TableSummaryFronzen_1.TableSummaryFrozen, { position: 'top' }))),
2237
2294
  React.createElement(Summary, { style: { left: frozenColumnsWidth }, role: 'rfdg-summary' },
2238
2295
  React.createElement(TableSummary_1.TableSummary, { position: 'top' })))),
2296
+ React.createElement("div", { className: 'bgrid-virtual-row-backdrop', "aria-hidden": 'true', style: {
2297
+ top: stickyTopHeight,
2298
+ height: contentBodyHeight,
2299
+ marginBottom: -contentBodyHeight,
2300
+ } }),
2239
2301
  React.createElement("div", { className: 'bgrid-body-scroll-content', style: { height: virtualScrollWindowMetrics.physicalContentHeight } },
2240
2302
  frozenRowCount > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-layer', style: { height: frozenRowsHeight, top: stickyTopHeight }, "data-bgrid-row-band": 'frozen' },
2241
2303
  (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0) > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-left bgrid-frozen-column-boundary', style: { width: frozenColumnsWidth }, role: 'rfdg-frozen-rows-left' },
@@ -2266,7 +2328,7 @@ function Table(props) {
2266
2328
  React.createElement(Summary, { style: { left: frozenColumnsWidth }, role: 'rfdg-summary' },
2267
2329
  React.createElement(TableSummary_1.TableSummary, { position: 'bottom' })))))),
2268
2330
  React.createElement(BodyViewport, { ref: bodyContainerRef, style: { top: stickyTopHeight, height: contentBodyHeight }, "data-last": !page ? 'true' : undefined },
2269
- ((_4 = rowReorderController.preview) === null || _4 === void 0 ? void 0 : _4.visible) && (React.createElement("div", { className: 'bgrid-row-reorder-preview', "data-bgrid-row-reorder-phase": rowReorderController.preview.phase, "aria-hidden": 'true' }, rowReorderController.preview.text)),
2331
+ ((_6 = rowReorderController.preview) === null || _6 === void 0 ? void 0 : _6.visible) && (React.createElement("div", { className: 'bgrid-row-reorder-preview', "data-bgrid-row-reorder-phase": rowReorderController.preview.phase, "aria-hidden": 'true' }, rowReorderController.preview.text)),
2270
2332
  React.createElement(Loading_1.default, { active: !!spinning, size: 'small' })),
2271
2333
  React.createElement("div", { className: 'bgrid-visually-hidden', role: 'status', "aria-live": 'polite', "aria-atomic": 'true' }, rowReorderController.announcement),
2272
2334
  React.createElement(CellTextEditorGateway_1.CellTextEditorGateway, { containerRef: containerRef }),
@@ -53,8 +53,8 @@ function getRowBottom(rowIndex, rowHeight) {
53
53
  return (rowIndex + 1) * rowHeight;
54
54
  }
55
55
  function ensureCellVisible(_a) {
56
- var _b, _c, _d, _e, _f, _g, _h, _j;
57
- var cell = _a.cell, scrollContainer = _a.scrollContainer, _k = _a.frozenColumnCount, frozenColumnCount = _k === void 0 ? 0 : _k, _l = _a.frozenRowCount, frozenRowCount = _l === void 0 ? 0 : _l, columns = _a.columns, rowHeight = _a.rowHeight, verticalScrollState = _a.verticalScrollState, viewportInsets = _a.viewportInsets;
56
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k;
57
+ var cell = _a.cell, scrollContainer = _a.scrollContainer, _l = _a.frozenColumnCount, frozenColumnCount = _l === void 0 ? 0 : _l, _m = _a.frozenRowCount, frozenRowCount = _m === void 0 ? 0 : _m, columns = _a.columns, rowHeight = _a.rowHeight, verticalScrollState = _a.verticalScrollState, viewportInsets = _a.viewportInsets;
58
58
  var currentScrollTop = (_c = (_b = verticalScrollState === null || verticalScrollState === void 0 ? void 0 : verticalScrollState.scrollTop) !== null && _b !== void 0 ? _b : scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollTop) !== null && _c !== void 0 ? _c : 0;
59
59
  var currentScrollLeft = (_d = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollLeft) !== null && _d !== void 0 ? _d : 0;
60
60
  var nextScrollTop = currentScrollTop;
@@ -66,10 +66,10 @@ function ensureCellVisible(_a) {
66
66
  var rowBottom = getRowBottom(targetRowIndex, rowHeight);
67
67
  var clientHeight = scrollContainer.clientHeight;
68
68
  var scrollHeight = (_e = verticalScrollState === null || verticalScrollState === void 0 ? void 0 : verticalScrollState.scrollHeight) !== null && _e !== void 0 ? _e : scrollContainer.scrollHeight;
69
- var maxScrollTop = Math.max(0, scrollHeight - clientHeight);
69
+ var maxScrollTop = Math.max(0, (_f = verticalScrollState === null || verticalScrollState === void 0 ? void 0 : verticalScrollState.maxScrollTop) !== null && _f !== void 0 ? _f : scrollHeight - clientHeight);
70
70
  if (clientHeight > 0) {
71
- var topInset = clamp((_f = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.top) !== null && _f !== void 0 ? _f : 0, 0, clientHeight);
72
- var bottomInset = clamp((_g = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.bottom) !== null && _g !== void 0 ? _g : 0, 0, Math.max(clientHeight - topInset, 0));
71
+ var topInset = clamp((_g = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.top) !== null && _g !== void 0 ? _g : 0, 0, clientHeight);
72
+ var bottomInset = clamp((_h = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.bottom) !== null && _h !== void 0 ? _h : 0, 0, Math.max(clientHeight - topInset, 0));
73
73
  var viewportTop = currentScrollTop + topInset;
74
74
  var viewportBottom = currentScrollTop + clientHeight - bottomInset;
75
75
  if (rowTop < viewportTop) {
@@ -90,8 +90,8 @@ function ensureCellVisible(_a) {
90
90
  var scrollWidth = scrollContainer.scrollWidth;
91
91
  var maxScrollLeft = Math.max(0, scrollWidth - clientWidth);
92
92
  if (clientWidth > 0) {
93
- var leftInset = clamp((_h = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.left) !== null && _h !== void 0 ? _h : 0, 0, clientWidth);
94
- var rightInset = clamp((_j = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.right) !== null && _j !== void 0 ? _j : 0, 0, Math.max(clientWidth - leftInset, 0));
93
+ var leftInset = clamp((_j = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.left) !== null && _j !== void 0 ? _j : 0, 0, clientWidth);
94
+ var rightInset = clamp((_k = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.right) !== null && _k !== void 0 ? _k : 0, 0, Math.max(clientWidth - leftInset, 0));
95
95
  var viewportLeft = currentScrollLeft + leftInset;
96
96
  var viewportRight = currentScrollLeft + clientWidth - rightInset;
97
97
  var visibleWidth = Math.max(clientWidth - leftInset - rightInset, 0);
@@ -6,7 +6,7 @@ exports.getVisibleScrollableRowRange = getVisibleScrollableRowRange;
6
6
  * The returned end index is exclusive.
7
7
  */
8
8
  function getVisibleScrollableRowRange(_a) {
9
- var scrollTop = _a.scrollTop, viewportHeight = _a.viewportHeight, rowHeight = _a.rowHeight, frozenRowCount = _a.frozenRowCount, totalRowCount = _a.totalRowCount, _b = _a.overscan, overscan = _b === void 0 ? 1 : _b, _c = _a.windowSize, windowSize = _c === void 0 ? 1 : _c;
9
+ var scrollTop = _a.scrollTop, viewportHeight = _a.viewportHeight, rowHeight = _a.rowHeight, frozenRowCount = _a.frozenRowCount, totalRowCount = _a.totalRowCount, _b = _a.overscan, overscan = _b === void 0 ? 1 : _b, _c = _a.leadingOverscan, leadingOverscan = _c === void 0 ? 0 : _c, _d = _a.windowSize, windowSize = _d === void 0 ? 1 : _d;
10
10
  var safeRowHeight = Math.max(rowHeight, 1);
11
11
  var safeTotal = Math.max(Math.floor(totalRowCount), 0);
12
12
  var safeFrozen = Math.min(Math.max(Math.floor(frozenRowCount), 0), safeTotal);
@@ -14,11 +14,12 @@ function getVisibleScrollableRowRange(_a) {
14
14
  var relativeStart = Math.min(Math.max(Math.floor(Math.max(scrollTop, 0) / safeRowHeight), 0), scrollableRowCount);
15
15
  var safeWindowSize = Math.max(Math.floor(windowSize), 1);
16
16
  var windowStart = Math.floor(relativeStart / safeWindowSize) * safeWindowSize;
17
- var visibleCount = Math.max(Math.ceil(Math.max(viewportHeight, 0) / safeRowHeight) + Math.max(overscan, 0), 0);
17
+ var renderStart = Math.max(windowStart - Math.max(Math.floor(leadingOverscan), 0), 0);
18
+ var visibleCount = Math.max(Math.ceil(Math.max(viewportHeight, 0) / safeRowHeight) + Math.max(Math.floor(overscan), 0), 0);
18
19
  return {
19
- startRowIndex: safeFrozen + windowStart,
20
+ startRowIndex: safeFrozen + renderStart,
20
21
  endRowIndex: Math.min(safeFrozen + windowStart + visibleCount + safeWindowSize - 1, safeTotal),
21
- paddingTop: windowStart * safeRowHeight,
22
+ paddingTop: renderStart * safeRowHeight,
22
23
  scrollContentHeight: scrollableRowCount * safeRowHeight,
23
24
  };
24
25
  }
package/esm/BGrid.js CHANGED
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import Table from './components/Table';
3
3
  import { createPivotData, buildHeaderMatrix, findDuplicateColumnIds, getCellValueByRowKey, getColumnId, getColumnKeyToken, getFrozenColumnsWidth, processDataQuery, resolveStatusOptions, resolvePaginationViewOptions, resolveScrollbarOptions, shouldRenderBottomBar, } from './utils';
4
4
  import { AppStoreProvider } from './store';
5
- export function BGrid({ width, height, headerHeight = 30, footerHeight, summaryHeight = 30, itemHeight = 15, itemPadding = 7, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex = 0, frozenRowCount = 0, data = [], page, scrollbar, status, pagination, bottomBarHeight, scrollTop = 0, scrollLeft = 0, className, style, rowChecked, sort, onClick, loading = false, spinning, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger = 'dblclick', cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, searchOptions, contextMenuOptions, }) {
5
+ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, summaryHeight = 30, itemHeight = 15, itemPadding = 7, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex = 0, frozenRowCount = 0, data = [], page, scrollbar, status, pagination, bottomBarHeight, scrollTop = 0, scrollLeft = 0, className, style, rowChecked, sort, onClick, loading = false, spinning, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger = 'dblclick', cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, searchOptions, contextMenuOptions, }) {
6
6
  const warnedSearchControlledRef = React.useRef({ open: false, query: false, pivotContextMenu: false });
7
7
  const pivotData = React.useMemo(() => {
8
8
  return createPivotData({
@@ -410,7 +410,7 @@ export function BGrid({ width, height, headerHeight = 30, footerHeight, summaryH
410
410
  initialShowBottomBar,
411
411
  ]);
412
412
  return (React.createElement(AppStoreProvider, { initialState: initialStoreState },
413
- React.createElement(Table, { columns: computedColumns,
413
+ React.createElement(Table, { ref: ref, columns: computedColumns,
414
414
  columnsGroup: resolvedColumnsGroup,
415
415
  columnGroups: resolvedColumnGroups,
416
416
  onChangeColumns: resolvedOnChangeColumns,
@@ -182,6 +182,7 @@ function Table(props) {
182
182
  })));
183
183
  const trHeight = itemHeight + itemPadding * 2;
184
184
  const scrollableBodyHeight = Math.max(contentBodyHeight - frozenRowsHeight, 0);
185
+ const scrollOverscanRows = Math.max(KEYBOARD_NAVIGATION_ROW_WINDOW_SIZE, Math.ceil(scrollableBodyHeight / Math.max(trHeight, 1)));
185
186
  const mainViewportWidth = Math.max(width - (frozenColumnsWidth ?? 0), 0);
186
187
  const logicalBodyContentHeight = data.length * trHeight;
187
188
  const virtualScrollWindowMetrics = React.useMemo(() => getVirtualScrollWindowMetrics({
@@ -199,7 +200,8 @@ function Table(props) {
199
200
  const logicalVerticalScrollState = React.useMemo(() => ({
200
201
  scrollTop,
201
202
  scrollHeight: virtualScrollWindowMetrics.logicalContentHeight,
202
- }), [scrollTop, virtualScrollWindowMetrics.logicalContentHeight]);
203
+ maxScrollTop: virtualScrollWindowMetrics.logicalMaxScroll,
204
+ }), [scrollTop, virtualScrollWindowMetrics.logicalContentHeight, virtualScrollWindowMetrics.logicalMaxScroll]);
203
205
  const hasMultiCellSelection = React.useMemo(() => cellSelectionRanges.length > 1 ||
204
206
  cellSelectionRanges.some(range => {
205
207
  const normalized = normalizeCellSelectionRange(range);
@@ -298,8 +300,10 @@ function Table(props) {
298
300
  rowHeight: trHeight,
299
301
  frozenRowCount,
300
302
  totalRowCount: data.length,
303
+ overscan: scrollOverscanRows,
304
+ leadingOverscan: scrollOverscanRows,
301
305
  windowSize: props.reorder?.enabled ? 1 : KEYBOARD_NAVIGATION_ROW_WINDOW_SIZE,
302
- }), [data.length, frozenRowCount, props.reorder?.enabled, scrollTop, scrollableBodyHeight, trHeight]);
306
+ }), [data.length, frozenRowCount, props.reorder?.enabled, scrollOverscanRows, scrollTop, scrollableBodyHeight, trHeight]);
303
307
  const frozenRowRange = React.useMemo(() => ({ startRowIndex: 0, endRowIndex: frozenRowCount }), [frozenRowCount]);
304
308
  const scrollableRowRange = React.useMemo(() => ({
305
309
  startRowIndex: visibleScrollableRows.startRowIndex,
@@ -465,6 +469,14 @@ function Table(props) {
465
469
  }, []);
466
470
  const latestScrollRef = useRef({ top: scrollTop, left: scrollLeft });
467
471
  const latestCommittedScrollRef = useRef({ top: scrollTop, left: scrollLeft });
472
+ const scrollPropsRef = useRef({ top: props.scrollTop, left: props.scrollLeft });
473
+ const syncedDataPropRef = useRef(props.data);
474
+ const [rowScrollRequest, setRowScrollRequest] = React.useState(null);
475
+ React.useImperativeHandle(props.ref, () => ({
476
+ scrollToRow(rowIndex, options) {
477
+ setRowScrollRequest({ rowIndex, align: options?.align ?? 'nearest' });
478
+ },
479
+ }), []);
468
480
  const scrollRafRef = useRef(null);
469
481
  const scrollIdleTimerRef = useRef(null);
470
482
  const selectionAutoScrollRafRef = useRef(null);
@@ -1333,6 +1345,7 @@ function Table(props) {
1333
1345
  setSourceData(props.sourceData);
1334
1346
  }
1335
1347
  }
1348
+ syncedDataPropRef.current = props.data;
1336
1349
  }, [
1337
1350
  props.data,
1338
1351
  props.sourceData,
@@ -1411,8 +1424,11 @@ function Table(props) {
1411
1424
  setPage,
1412
1425
  setDisplayPaginationLength,
1413
1426
  ]);
1427
+ const previousPageRef = useRef(props.page?.currentPage);
1414
1428
  useEffect(() => {
1415
- resetScrollPosition('top');
1429
+ if (previousPageRef.current !== props.page?.currentPage)
1430
+ resetScrollPosition('top');
1431
+ previousPageRef.current = props.page?.currentPage;
1416
1432
  }, [resetScrollPosition, props.page?.currentPage]);
1417
1433
  // [Group 8] Row key & selection
1418
1434
  useEffect(() => {
@@ -1497,7 +1513,7 @@ function Table(props) {
1497
1513
  const containerRefCurrent = containerRef.current;
1498
1514
  const scrollContainerRefCurrent = scrollContainerRef?.current;
1499
1515
  if (scrollContainerRefCurrent) {
1500
- scrollContainerRefCurrent.removeEventListener('scroll', onScroll);
1516
+ scrollContainerRefCurrent.removeEventListener('scroll', onScroll, true);
1501
1517
  scrollContainerRefCurrent.addEventListener('scroll', onScroll, { passive: true, capture: true });
1502
1518
  }
1503
1519
  return () => {
@@ -1510,16 +1526,54 @@ function Table(props) {
1510
1526
  scrollIdleTimerRef.current = null;
1511
1527
  }
1512
1528
  containerRefCurrent?.removeAttribute('data-bgrid-scrolling');
1513
- scrollContainerRefCurrent?.removeEventListener('scroll', onScroll);
1529
+ scrollContainerRefCurrent?.removeEventListener('scroll', onScroll, true);
1514
1530
  };
1515
1531
  }, [onScroll, setInitialized]);
1516
1532
  React.useLayoutEffect(() => {
1517
1533
  if (!scrollContainerRef.current)
1518
1534
  return;
1519
- const nextTop = props.scrollTop ?? latestScrollRef.current.top;
1520
- const nextLeft = props.scrollLeft ?? latestScrollRef.current.left;
1535
+ if (props.data !== syncedDataPropRef.current)
1536
+ return;
1537
+ // Resizing or changing the data also changes commitScroll. Only a changed
1538
+ // scroll prop should override the user's current position.
1539
+ const nextTop = props.scrollTop !== scrollPropsRef.current.top
1540
+ ? props.scrollTop ?? latestScrollRef.current.top
1541
+ : latestScrollRef.current.top;
1542
+ const nextLeft = props.scrollLeft !== scrollPropsRef.current.left
1543
+ ? props.scrollLeft ?? latestScrollRef.current.left
1544
+ : latestScrollRef.current.left;
1545
+ scrollPropsRef.current = { top: props.scrollTop, left: props.scrollLeft };
1521
1546
  commitScroll(nextTop, nextLeft);
1522
- }, [commitScroll, props.scrollLeft, props.scrollTop]);
1547
+ }, [commitScroll, data, props.data, props.scrollLeft, props.scrollTop]);
1548
+ React.useLayoutEffect(() => {
1549
+ if (!rowScrollRequest)
1550
+ return;
1551
+ // Prop data reaches the per-grid store in an effect. Wait for that render
1552
+ // so appending a row and requesting it in the same event is supported.
1553
+ if (props.data !== syncedDataPropRef.current)
1554
+ return;
1555
+ setRowScrollRequest(null);
1556
+ const { rowIndex, align } = rowScrollRequest;
1557
+ if (!Number.isInteger(rowIndex) || rowIndex < frozenRowCount || rowIndex >= data.length)
1558
+ return;
1559
+ if (scrollableBodyHeight <= 0)
1560
+ return;
1561
+ const rowTop = (rowIndex - frozenRowCount) * trHeight;
1562
+ const rowBottom = rowTop + trHeight;
1563
+ const { top, left } = latestScrollRef.current;
1564
+ let nextTop = top;
1565
+ if (align === 'start')
1566
+ nextTop = rowTop;
1567
+ else if (align === 'center')
1568
+ nextTop = rowTop - (scrollableBodyHeight - trHeight) / 2;
1569
+ else if (align === 'end')
1570
+ nextTop = rowBottom - scrollableBodyHeight;
1571
+ else if (rowTop < top)
1572
+ nextTop = rowTop;
1573
+ else if (rowBottom > top + scrollableBodyHeight)
1574
+ nextTop = rowBottom - scrollableBodyHeight;
1575
+ commitScroll(nextTop, left);
1576
+ }, [commitScroll, data, frozenRowCount, props.data, rowScrollRequest, scrollableBodyHeight, trHeight]);
1523
1577
  useEffect(() => {
1524
1578
  latestScrollRef.current = { top: scrollTop, left: scrollLeft };
1525
1579
  latestCommittedScrollRef.current = { top: scrollTop, left: scrollLeft };
@@ -1999,7 +2053,13 @@ function Table(props) {
1999
2053
  };
2000
2054
  }, []);
2001
2055
  return (React.createElement(EditorPortalContext.Provider, { value: editorPortalContext },
2002
- React.createElement(Container, { ref: containerRef, role: 'grid', style: { ...style, width, height, borderWidth: `${containerBorderWidth}px` }, className: className, tabIndex: 0, onFocus: event => {
2056
+ React.createElement(Container, { ref: containerRef, role: 'grid', style: {
2057
+ ...style,
2058
+ width,
2059
+ height,
2060
+ borderWidth: `${containerBorderWidth}px`,
2061
+ ['--bgrid-virtual-row-height']: `${trHeight}px`,
2062
+ }, className: className, tabIndex: 0, onFocus: event => {
2003
2063
  if (event.target !== event.currentTarget)
2004
2064
  return;
2005
2065
  const gateway = event.currentTarget.querySelector('[data-bgrid-text-editor-gateway="true"]');
@@ -2035,6 +2095,11 @@ function Table(props) {
2035
2095
  React.createElement(TableSummaryFrozen, { position: 'top' }))),
2036
2096
  React.createElement(Summary, { style: { left: frozenColumnsWidth }, role: 'rfdg-summary' },
2037
2097
  React.createElement(TableSummary, { position: 'top' })))),
2098
+ React.createElement("div", { className: 'bgrid-virtual-row-backdrop', "aria-hidden": 'true', style: {
2099
+ top: stickyTopHeight,
2100
+ height: contentBodyHeight,
2101
+ marginBottom: -contentBodyHeight,
2102
+ } }),
2038
2103
  React.createElement("div", { className: 'bgrid-body-scroll-content', style: { height: virtualScrollWindowMetrics.physicalContentHeight } },
2039
2104
  frozenRowCount > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-layer', style: { height: frozenRowsHeight, top: stickyTopHeight }, "data-bgrid-row-band": 'frozen' },
2040
2105
  (frozenColumnsWidth ?? 0) > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-left bgrid-frozen-column-boundary', style: { width: frozenColumnsWidth }, role: 'rfdg-frozen-rows-left' },
@@ -53,7 +53,7 @@ export function ensureCellVisible({ cell, scrollContainer, frozenColumnCount = 0
53
53
  const rowBottom = getRowBottom(targetRowIndex, rowHeight);
54
54
  const clientHeight = scrollContainer.clientHeight;
55
55
  const scrollHeight = verticalScrollState?.scrollHeight ?? scrollContainer.scrollHeight;
56
- const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
56
+ const maxScrollTop = Math.max(0, verticalScrollState?.maxScrollTop ?? scrollHeight - clientHeight);
57
57
  if (clientHeight > 0) {
58
58
  const topInset = clamp(viewportInsets?.top ?? 0, 0, clientHeight);
59
59
  const bottomInset = clamp(viewportInsets?.bottom ?? 0, 0, Math.max(clientHeight - topInset, 0));
@@ -2,7 +2,7 @@
2
2
  * Maps the scrollable body's physical scroll offset to logical data row indexes.
3
3
  * The returned end index is exclusive.
4
4
  */
5
- export function getVisibleScrollableRowRange({ scrollTop, viewportHeight, rowHeight, frozenRowCount, totalRowCount, overscan = 1, windowSize = 1, }) {
5
+ export function getVisibleScrollableRowRange({ scrollTop, viewportHeight, rowHeight, frozenRowCount, totalRowCount, overscan = 1, leadingOverscan = 0, windowSize = 1, }) {
6
6
  const safeRowHeight = Math.max(rowHeight, 1);
7
7
  const safeTotal = Math.max(Math.floor(totalRowCount), 0);
8
8
  const safeFrozen = Math.min(Math.max(Math.floor(frozenRowCount), 0), safeTotal);
@@ -10,11 +10,12 @@ export function getVisibleScrollableRowRange({ scrollTop, viewportHeight, rowHei
10
10
  const relativeStart = Math.min(Math.max(Math.floor(Math.max(scrollTop, 0) / safeRowHeight), 0), scrollableRowCount);
11
11
  const safeWindowSize = Math.max(Math.floor(windowSize), 1);
12
12
  const windowStart = Math.floor(relativeStart / safeWindowSize) * safeWindowSize;
13
- const visibleCount = Math.max(Math.ceil(Math.max(viewportHeight, 0) / safeRowHeight) + Math.max(overscan, 0), 0);
13
+ const renderStart = Math.max(windowStart - Math.max(Math.floor(leadingOverscan), 0), 0);
14
+ const visibleCount = Math.max(Math.ceil(Math.max(viewportHeight, 0) / safeRowHeight) + Math.max(Math.floor(overscan), 0), 0);
14
15
  return {
15
- startRowIndex: safeFrozen + windowStart,
16
+ startRowIndex: safeFrozen + renderStart,
16
17
  endRowIndex: Math.min(safeFrozen + windowStart + visibleCount + safeWindowSize - 1, safeTotal),
17
- paddingTop: windowStart * safeRowHeight,
18
+ paddingTop: renderStart * safeRowHeight,
18
19
  scrollContentHeight: scrollableRowCount * safeRowHeight,
19
20
  };
20
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beautiful-grid",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A beautiful, powerful, open-source React Data Grid.",
5
5
  "homepage": "https://bgrid.axisj.com",
6
6
  "bugs": {
package/style.css CHANGED
@@ -1561,6 +1561,25 @@
1561
1561
  background-color: var(--bgrid-body-bg);
1562
1562
  }
1563
1563
 
1564
+ /* Safari can advance its native scroll layer before React commits the next
1565
+ virtual-row window. Keep an inexpensive viewport-sized row grid behind the
1566
+ real tables so that compositor gaps retain the table's visual geometry. */
1567
+ .bgrid-virtual-row-backdrop {
1568
+ position: sticky;
1569
+ z-index: 0;
1570
+ box-sizing: border-box;
1571
+ min-width: 100%;
1572
+ pointer-events: none;
1573
+ background-color: var(--bgrid-body-bg);
1574
+ background-image: repeating-linear-gradient(
1575
+ to bottom,
1576
+ transparent 0,
1577
+ transparent calc(var(--bgrid-virtual-row-height, 28px) - 1px),
1578
+ var(--bgrid-border-color-base) calc(var(--bgrid-virtual-row-height, 28px) - 1px),
1579
+ var(--bgrid-border-color-base) var(--bgrid-virtual-row-height, 28px)
1580
+ );
1581
+ }
1582
+
1564
1583
  .bgrid-footer-container {
1565
1584
  position: relative;
1566
1585
  z-index: 10;
package/types/BGrid.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import * as React from 'react';
2
2
  import { BGridProps } from './types';
3
- export declare function BGrid<T = Record<string, any>>({ width, height, headerHeight, footerHeight, summaryHeight, itemHeight, itemPadding, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex, frozenRowCount, data, page, scrollbar, status, pagination, bottomBarHeight, scrollTop, scrollLeft, className, style, rowChecked, sort, onClick, loading, spinning, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger, cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, searchOptions, contextMenuOptions, }: BGridProps<T>): React.JSX.Element;
3
+ export declare function BGrid<T = Record<string, any>>({ ref, width, height, headerHeight, footerHeight, summaryHeight, itemHeight, itemPadding, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex, frozenRowCount, data, page, scrollbar, status, pagination, bottomBarHeight, scrollTop, scrollLeft, className, style, rowChecked, sort, onClick, loading, spinning, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger, cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, searchOptions, contextMenuOptions, }: BGridProps<T>): React.JSX.Element;
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { Key } from 'react';
3
3
  import { BGridCellNavigationOptions, AppModelColumn, BGridColumnGroup, BGridColumnGroupNode, BGridDataControl, BGridDataItem, BGridDataQuery, BGridPage, BGridProps, BGridRowChecked, BGridSortInfo, BGridSortParam, BGridResolvedScrollbarOptions, BGridResolvedStatusOptions, BGridResolvedPaginationViewOptions } from '../types';
4
4
  interface Props<T> {
5
+ ref?: BGridProps<T>['ref'];
5
6
  width?: number;
6
7
  height?: number;
7
8
  headerHeight?: number;
package/types/types.d.ts CHANGED
@@ -619,7 +619,20 @@ export interface BGridResolvedStatusOptions extends BGridStatusOptions {
619
619
  configured: boolean;
620
620
  }
621
621
  export type BGridResolvedPaginationViewOptions = Required<Pick<BGridPaginationViewOptions, 'visible'>> & Omit<BGridPaginationViewOptions, 'visible'>;
622
+ export interface BGridScrollToRowOptions {
623
+ /** Defaults to nearest: scroll only as far as needed to reveal the row. */
624
+ align?: 'start' | 'center' | 'end' | 'nearest';
625
+ }
626
+ export interface BGridRef {
627
+ /**
628
+ * Reveal a zero-based row in the displayed (sorted/filtered) data on the current page.
629
+ * Runs after pending data updates. Invalid indexes and frozen rows are ignored.
630
+ * Preserves horizontal scroll, cell focus and selection; does not load other pages.
631
+ */
632
+ scrollToRow: (rowIndex: number, options?: BGridScrollToRowOptions) => void;
633
+ }
622
634
  export interface BGridProps<T> {
635
+ ref?: React.Ref<BGridRef>;
623
636
  width: number;
624
637
  height: number;
625
638
  headerHeight?: number;
@@ -15,6 +15,7 @@ export interface EnsureCellVisibleParams {
15
15
  verticalScrollState?: {
16
16
  scrollTop: number;
17
17
  scrollHeight: number;
18
+ maxScrollTop?: number;
18
19
  };
19
20
  viewportInsets?: {
20
21
  top?: number;
@@ -5,6 +5,7 @@ export interface VisibleScrollableRowRangeParams {
5
5
  frozenRowCount: number;
6
6
  totalRowCount: number;
7
7
  overscan?: number;
8
+ leadingOverscan?: number;
8
9
  windowSize?: number;
9
10
  }
10
11
  export interface VisibleScrollableRowRange {
@@ -17,4 +18,4 @@ export interface VisibleScrollableRowRange {
17
18
  * Maps the scrollable body's physical scroll offset to logical data row indexes.
18
19
  * The returned end index is exclusive.
19
20
  */
20
- export declare function getVisibleScrollableRowRange({ scrollTop, viewportHeight, rowHeight, frozenRowCount, totalRowCount, overscan, windowSize, }: VisibleScrollableRowRangeParams): VisibleScrollableRowRange;
21
+ export declare function getVisibleScrollableRowRange({ scrollTop, viewportHeight, rowHeight, frozenRowCount, totalRowCount, overscan, leadingOverscan, windowSize, }: VisibleScrollableRowRangeParams): VisibleScrollableRowRange;