@beppla/tapas-ui 1.4.6 → 1.4.8

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.
@@ -12,7 +12,7 @@
12
12
  | `minRows` | `number` | `0` | 最小行数(如果实际数据行数少于此值,会添加占位行) |
13
13
  | `minColumns` | `number` | `0` | 最小列数(如果实际数据列数少于此值,会添加占位列) |
14
14
  | `placeholderRowHeight` | `number` | `rowHeight` (56) | 占位行的高度 |
15
- | `placeholderColumnWidth` | `number` | `150` | 占位列的宽度 |
15
+ | `placeholderColumnWidth` | `number \| number[]` | `150` | 占位列的宽度(可以是数字或数组为每列单独设置) |
16
16
 
17
17
  ## 工作原理
18
18
 
@@ -84,6 +84,8 @@
84
84
 
85
85
  ### 自定义占位尺寸
86
86
 
87
+ **统一设置所有占位列宽度:**
88
+
87
89
  ```tsx
88
90
  <StatisticsTable
89
91
  rows={rows}
@@ -93,11 +95,34 @@
93
95
  minRows={10}
94
96
  minColumns={5}
95
97
  placeholderRowHeight={80} // 占位行高度 80px
96
- placeholderColumnWidth={200} // 占位列宽度 200px
98
+ placeholderColumnWidth={200} // 所有占位列宽度 200px
97
99
  maxHeight={600}
98
100
  />
99
101
  ```
100
102
 
103
+ **为每个占位列单独设置宽度(更好地适配屏幕宽度):**
104
+
105
+ ```tsx
106
+ <StatisticsTable
107
+ rows={rows} // 假设有 2 列数据
108
+ columns={columns}
109
+ cells={cells}
110
+ enablePlaceholder={true}
111
+ minColumns={5} // 需要 3 个占位列
112
+ placeholderColumnWidth={[180, 250, 120]} // 为每个占位列单独设置宽度
113
+ // 占位列 1: 180px
114
+ // 占位列 2: 250px
115
+ // 占位列 3: 120px
116
+ />
117
+ ```
118
+
119
+ 如果数组长度少于所需的占位列数量,最后一个值会被重复使用:
120
+
121
+ ```tsx
122
+ placeholderColumnWidth={[200, 150]} // minColumns=5, 需要 3 个占位列
123
+ // 结果: [200, 150, 150] - 最后的值 (150) 被重复使用
124
+ ```
125
+
101
126
  ### 配合统计功能
102
127
 
103
128
  ```tsx
@@ -8,10 +8,12 @@ A versatile table component for displaying matrix data with optional row and col
8
8
  - ✅ Row statistics (Sum/Mean columns)
9
9
  - ✅ Column statistics (Sum/Mean rows)
10
10
  - ✅ Horizontal and vertical scrolling
11
- - ✅ Pagination support
11
+ - ✅ Virtual scrolling for large datasets (high performance)
12
+ - ✅ External pagination support
12
13
  - ✅ Loading state
13
14
  - ✅ Empty state
14
15
  - ✅ Placeholder rows and columns (fill container when data is insufficient)
16
+ - ✅ Internationalization (i18n) support
15
17
  - ✅ Customizable styling
16
18
  - ✅ TypeScript support
17
19
  - ✅ Cross-platform (Web & Mobile)
@@ -87,22 +89,43 @@ const cells = [
87
89
  />
88
90
  ```
89
91
 
90
- ## With Pagination
92
+ ## With External Pagination
93
+
94
+ The StatisticsTable component doesn't include built-in pagination. Use the separate Pagination component for better layout control:
91
95
 
92
96
  ```tsx
93
- <StatisticsTable
94
- rows={rows}
95
- columns={columns}
96
- cells={cells}
97
- pagination={{
98
- current: 1,
99
- pageSize: 10,
100
- total: 100,
101
- onChange: (page, pageSize) => {
102
- console.log('Page:', page, 'PageSize:', pageSize);
103
- },
104
- }}
105
- />
97
+ import { StatisticsTable, Pagination } from '@beppla/tapas-ui';
98
+
99
+ function MyTable() {
100
+ const [currentPage, setCurrentPage] = useState(1);
101
+ const pageSize = 10;
102
+
103
+ // Calculate paginated rows
104
+ const paginatedRows = rows.slice(
105
+ (currentPage - 1) * pageSize,
106
+ currentPage * pageSize
107
+ );
108
+
109
+ return (
110
+ <>
111
+ <StatisticsTable
112
+ rows={paginatedRows}
113
+ columns={columns}
114
+ cells={cells}
115
+ />
116
+ <Pagination
117
+ totalCount={`${rows.length} items`}
118
+ pageParams={{
119
+ page: currentPage,
120
+ pageSize: pageSize,
121
+ totalItems: rows.length,
122
+ totalPage: Math.ceil(rows.length / pageSize),
123
+ }}
124
+ onPageChange={(params) => setCurrentPage(params.page)}
125
+ />
126
+ </>
127
+ );
128
+ }
106
129
  ```
107
130
 
108
131
  ## With Placeholder Rows and Columns
@@ -134,11 +157,34 @@ You can control the size of placeholder rows and columns:
134
157
  minRows={10}
135
158
  minColumns={5}
136
159
  placeholderRowHeight={80} // Each placeholder row will be 80px tall
137
- placeholderColumnWidth={200} // Each placeholder column will be 200px wide
160
+ placeholderColumnWidth={200} // All placeholder columns will be 200px wide
138
161
  maxHeight={600}
139
162
  />
140
163
  ```
141
164
 
165
+ **Individual placeholder column widths** for better screen adaptation:
166
+
167
+ ```tsx
168
+ <StatisticsTable
169
+ rows={rows} // 2 data columns
170
+ columns={columns}
171
+ cells={cells}
172
+ enablePlaceholder={true}
173
+ minColumns={5} // Need 3 placeholder columns
174
+ placeholderColumnWidth={[180, 220, 150]} // Individual widths for each placeholder
175
+ // Placeholder column 1: 180px
176
+ // Placeholder column 2: 220px
177
+ // Placeholder column 3: 150px
178
+ />
179
+ ```
180
+
181
+ If array length is less than placeholder columns needed, the last value will be reused:
182
+
183
+ ```tsx
184
+ placeholderColumnWidth={[200, 150]} // minColumns=5, need 3 placeholders
185
+ // Result: [200, 150, 150] - last value (150) reused
186
+ ```
187
+
142
188
  **How it works:**
143
189
  - If `rows.length < minRows`: Adds `(minRows - rows.length)` empty placeholder rows
144
190
  - If columns count `< minColumns`: Adds `(minColumns - columns.count)` empty placeholder columns
@@ -176,6 +222,102 @@ Scroll shadow features:
176
222
  - Shadows automatically show/hide based on scroll position
177
223
  - Works seamlessly with row statistics and column statistics
178
224
 
225
+ ## Virtual Scrolling for Large Datasets
226
+
227
+ For tables with hundreds or thousands of rows, enable virtual scrolling for optimal performance:
228
+
229
+ ```tsx
230
+ <StatisticsTable
231
+ rows={thousandsOfRows}
232
+ columns={columns}
233
+ cells={cells}
234
+ enableVirtualization={true}
235
+ virtualRowHeight={56} // Default row height for calculation
236
+ maxHeight={600}
237
+ />
238
+ ```
239
+
240
+ **How it works:**
241
+ - Only renders rows visible in the viewport (plus a small buffer)
242
+ - Automatically calculates which rows to render based on scroll position
243
+ - Maintains proper scroll behavior with placeholder spaces above/below
244
+ - Supports dynamic row heights via `getRowHeight` prop
245
+ - Typical performance: 10,000+ rows scroll smoothly
246
+
247
+ **Performance tips:**
248
+ - Set `virtualRowHeight` to match your average row height
249
+ - Use `getRowHeight` for dynamic heights, but be aware it's called frequently
250
+ - For uniform row heights, use the default `rowHeight` for best performance
251
+
252
+ **Example with dynamic heights:**
253
+
254
+ ```tsx
255
+ <StatisticsTable
256
+ rows={largeDataset}
257
+ columns={columns}
258
+ cells={cells}
259
+ enableVirtualization={true}
260
+ getRowHeight={(rowKey, rowData) => {
261
+ // Return height based on row data
262
+ return rowData?.isExpanded ? 120 : 56;
263
+ }}
264
+ maxHeight={600}
265
+ />
266
+ ```
267
+
268
+ ## Internationalization (i18n)
269
+
270
+ Customize loading and empty state texts for different languages:
271
+
272
+ ```tsx
273
+ // English
274
+ <StatisticsTable
275
+ rows={rows}
276
+ columns={columns}
277
+ cells={cells}
278
+ loading={isLoading}
279
+ loadingText="Loading..."
280
+ emptyText="No data available"
281
+ />
282
+
283
+ // Chinese
284
+ <StatisticsTable
285
+ rows={rows}
286
+ columns={columns}
287
+ cells={cells}
288
+ loading={isLoading}
289
+ loadingText="加载中..."
290
+ emptyText="暂无数据"
291
+ />
292
+
293
+ // Spanish
294
+ <StatisticsTable
295
+ rows={rows}
296
+ columns={columns}
297
+ cells={cells}
298
+ loading={isLoading}
299
+ loadingText="Cargando..."
300
+ emptyText="No hay datos"
301
+ />
302
+
303
+ // With i18n library
304
+ import { useTranslation } from 'react-i18next';
305
+
306
+ function MyTable() {
307
+ const { t } = useTranslation();
308
+
309
+ return (
310
+ <StatisticsTable
311
+ rows={rows}
312
+ columns={columns}
313
+ cells={cells}
314
+ loadingText={t('table.loading')}
315
+ emptyText={t('table.empty')}
316
+ />
317
+ );
318
+ }
319
+ ```
320
+
179
321
  ## Props
180
322
 
181
323
  | Prop | Type | Default | Description |
@@ -185,16 +327,19 @@ Scroll shadow features:
185
327
  | `cells` | `StatisticsTableCell[]` | Required | Cell data |
186
328
  | `showRowStats` | `boolean` | `false` | Show row statistics (Sum/Mean columns) |
187
329
  | `showColumnStats` | `boolean` | `false` | Show column statistics (Sum/Mean rows) |
188
- | `pagination` | `PaginationConfig \| false` | `false` | Pagination configuration |
189
330
  | `loading` | `boolean` | `false` | Show loading state |
190
- | `emptyText` | `string` | `'No data'` | Text for empty state |
331
+ | `loadingText` | `string` | `'Loading...'` | Text to display during loading (for i18n) |
332
+ | `emptyText` | `string` | `'No data'` | Text to display when empty (for i18n) |
191
333
  | `maxHeight` | `number` | `500` | Maximum table body height |
192
334
  | `rowLabelWidth` | `number` | `150` | Width of first column (row labels) |
193
335
  | `enablePlaceholder` | `boolean` | `false` | Enable placeholder rows/columns to fill container |
194
336
  | `minRows` | `number` | `0` | Minimum number of rows (adds placeholders if needed) |
195
337
  | `minColumns` | `number` | `0` | Minimum number of columns (adds placeholders if needed) |
196
338
  | `placeholderRowHeight` | `number` | `rowHeight` (56) | Height of each placeholder row |
197
- | `placeholderColumnWidth` | `number` | `150` | Width of each placeholder column |
339
+ | `placeholderColumnWidth` | `number \| number[]` | `150` | Width of placeholder columns (single number or array for each column) |
340
+ | `enableVirtualization` | `boolean` | `false` | Enable virtual scrolling for large datasets |
341
+ | `virtualRowHeight` | `number` | `56` | Average row height for virtual scroll calculations |
342
+ | `getRowHeight` | `(rowKey, rowData) => number` | - | Dynamic row height function (used in virtual scrolling) |
198
343
  | `enableScrollShadow` | `boolean` | `true` | Enable scroll shadows on all four sides |
199
344
  | `showScrollIndicator` | `boolean` | `false` | Show scroll indicators/scrollbars |
200
345
  | `style` | `ViewStyle` | - | Custom container styles |
@@ -214,13 +359,6 @@ interface StatisticsTableCell {
214
359
  quantity: number;
215
360
  amount: number;
216
361
  }
217
-
218
- interface PaginationConfig {
219
- current: number;
220
- pageSize: number;
221
- total: number;
222
- onChange?: (page: number, pageSize: number) => void;
223
- }
224
362
  ```
225
363
 
226
364
  ## Use Cases
@@ -7,7 +7,6 @@ exports.StatisticsTable = StatisticsTable;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _themed = require("@rneui/themed");
10
- var _Pagination = _interopRequireDefault(require("../Pagination/Pagination"));
11
10
  var _Hoverable = _interopRequireDefault(require("../Hoverable/Hoverable"));
12
11
  var _jsxRuntime = require("react/jsx-runtime");
13
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -92,8 +91,8 @@ function StatisticsTable({
92
91
  cells,
93
92
  showRowStats = false,
94
93
  showColumnStats = false,
95
- pagination,
96
94
  loading = false,
95
+ loadingText = 'Loading...',
97
96
  emptyText = 'No data',
98
97
  maxHeight = 500,
99
98
  rowLabelWidth = 150,
@@ -152,6 +151,9 @@ function StatisticsTable({
152
151
  isAtBottom: false
153
152
  });
154
153
 
154
+ // 虚拟滚动状态
155
+ const [virtualScrollOffset, setVirtualScrollOffset] = (0, _react.useState)(0);
156
+
155
157
  // Hover 状态
156
158
  const [hoveredCell, setHoveredCell] = (0, _react.useState)(null);
157
159
  const scrollViewRef = (0, _react.useRef)(null);
@@ -194,12 +196,13 @@ function StatisticsTable({
194
196
  backgroundColor: colors.colorSurface // 米色背景
195
197
  },
196
198
  header: {
197
- backgroundColor: headerStyle?.backgroundColor || colors.colorSurface,
198
- // 米色
199
+ backgroundColor: headerStyle?.backgroundColor || colors.colorSurface7,
200
+ // rgba(0, 0, 0, 0.08)
199
201
  borderBottomColor: colors.colorTableBorder
200
202
  },
201
203
  headerCell: {
202
- borderRightColor: colors.colorTableBorder
204
+ borderRightColor: colors.colorTableBorder,
205
+ backgroundColor: headerStyle?.backgroundColor || colors.colorSurface7
203
206
  },
204
207
  headerText: {
205
208
  color: headerStyle?.textColor || colors.colorTextPrimary,
@@ -208,7 +211,7 @@ function StatisticsTable({
208
211
  },
209
212
  // Row Stats 表头样式
210
213
  statsHeaderCell: {
211
- backgroundColor: rowStatsHeaderStyle?.backgroundColor || 'rgba(0, 0, 0, 0.08)',
214
+ backgroundColor: rowStatsHeaderStyle?.backgroundColor || colors.colorSurface7,
212
215
  borderRightColor: colors.colorTableBorder,
213
216
  borderBottomColor: colors.colorTableBorder
214
217
  },
@@ -261,10 +264,6 @@ function StatisticsTable({
261
264
  fontSize: columnStatsLabelStyle?.fontSize || 14,
262
265
  fontWeight: columnStatsLabelStyle?.fontWeight || '600'
263
266
  },
264
- paginationContainer: {
265
- borderTopColor: colors.colorTableBorder,
266
- backgroundColor: colors.colorSurface
267
- },
268
267
  // Column Stats 分隔线
269
268
  columnStatsDivider: {
270
269
  borderTopWidth: 2,
@@ -350,11 +349,15 @@ function StatisticsTable({
350
349
  // 添加占位列来填充
351
350
  const placeholderColumns = Array.from({
352
351
  length: missingColumns
353
- }, (_, i) => ({
354
- key: `_placeholder_col_${i}`,
355
- title: '',
356
- width: placeholderColumnWidth // 使用自定义占位列宽度
357
- }));
352
+ }, (_, i) => {
353
+ // 支持数组形式的宽度设置
354
+ const width = Array.isArray(placeholderColumnWidth) ? placeholderColumnWidth[i] || placeholderColumnWidth[placeholderColumnWidth.length - 1] || 150 : placeholderColumnWidth;
355
+ return {
356
+ key: `_placeholder_col_${i}`,
357
+ title: '',
358
+ width: width
359
+ };
360
+ });
358
361
  finalColumns = [...columns, ...placeholderColumns];
359
362
  }
360
363
 
@@ -413,8 +416,13 @@ function StatisticsTable({
413
416
  y: contentOffset.y,
414
417
  animated: false
415
418
  });
419
+
420
+ // 更新虚拟滚动偏移量
421
+ if (enableVirtualization) {
422
+ setVirtualScrollOffset(contentOffset.y);
423
+ }
416
424
  handleScroll(event);
417
- }, [handleScroll]);
425
+ }, [handleScroll, enableVirtualization]);
418
426
 
419
427
  // 处理单元格 hover
420
428
  const handleCellHover = (0, _react.useCallback)((rowKey, columnKey, isHovered) => {
@@ -523,7 +531,10 @@ function StatisticsTable({
523
531
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
524
532
  style: styles.loadingRow,
525
533
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
526
- children: "Loading..."
534
+ style: {
535
+ color: colors.colorTextPlaceholder
536
+ },
537
+ children: loadingText
527
538
  })
528
539
  })
529
540
  });
@@ -542,7 +553,75 @@ function StatisticsTable({
542
553
  })
543
554
  });
544
555
  }
545
- const visibleRows = enableVirtualization ? displayRows.slice(0, Math.ceil(maxHeight / virtualRowHeight)) : displayRows;
556
+
557
+ // 虚拟滚动计算
558
+ const virtualScrollData = (0, _react.useMemo)(() => {
559
+ if (!enableVirtualization) {
560
+ return {
561
+ visibleRows: displayRows,
562
+ startIndex: 0,
563
+ endIndex: displayRows.length,
564
+ offsetTop: 0,
565
+ offsetBottom: 0,
566
+ totalHeight: 0
567
+ };
568
+ }
569
+
570
+ // 计算每行的累积高度
571
+ const rowHeights = [];
572
+ let totalHeight = 0;
573
+ displayRows.forEach(row => {
574
+ const height = row.height || (getRowHeight ? getRowHeight(row.key, row.data) : rowHeight || virtualRowHeight);
575
+ rowHeights.push(height);
576
+ totalHeight += height;
577
+ });
578
+
579
+ // 计算可视区域
580
+ const viewportHeight = maxHeight - (actualShowColumnStats ? 120 : 0);
581
+ const scrollTop = virtualScrollOffset;
582
+
583
+ // 查找起始索引
584
+ let startIndex = 0;
585
+ let accumulatedHeight = 0;
586
+ for (let i = 0; i < rowHeights.length; i++) {
587
+ const currentHeight = rowHeights[i] || virtualRowHeight;
588
+ if (accumulatedHeight + currentHeight > scrollTop) {
589
+ startIndex = i;
590
+ break;
591
+ }
592
+ accumulatedHeight += currentHeight;
593
+ }
594
+
595
+ // 添加缓冲区(上下各多渲染几行)
596
+ const overscan = 3;
597
+ startIndex = Math.max(0, startIndex - overscan);
598
+
599
+ // 查找结束索引
600
+ let endIndex = startIndex;
601
+ let visibleHeight = 0;
602
+ for (let i = startIndex; i < rowHeights.length; i++) {
603
+ const currentHeight = rowHeights[i] || virtualRowHeight;
604
+ visibleHeight += currentHeight;
605
+ endIndex = i + 1;
606
+ if (visibleHeight >= viewportHeight) {
607
+ break;
608
+ }
609
+ }
610
+ endIndex = Math.min(displayRows.length, endIndex + overscan);
611
+
612
+ // 计算上下占位空间
613
+ const offsetTop = rowHeights.slice(0, startIndex).reduce((sum, h) => sum + h, 0);
614
+ const offsetBottom = rowHeights.slice(endIndex).reduce((sum, h) => sum + h, 0);
615
+ return {
616
+ visibleRows: displayRows.slice(startIndex, endIndex),
617
+ startIndex,
618
+ endIndex,
619
+ offsetTop,
620
+ offsetBottom,
621
+ totalHeight
622
+ };
623
+ }, [enableVirtualization, displayRows, virtualScrollOffset, maxHeight, actualShowColumnStats, rowHeight, getRowHeight]);
624
+ const visibleRows = virtualScrollData.visibleRows;
546
625
 
547
626
  // Web 阴影样式
548
627
  const webShadowStyle = _reactNative.Platform.OS === 'web' ? {
@@ -616,7 +695,7 @@ function StatisticsTable({
616
695
  })
617
696
  }, column.key);
618
697
  })]
619
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
698
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
620
699
  ref: bodyScrollViewRef,
621
700
  style: {
622
701
  maxHeight: maxHeight - columnStatsHeight
@@ -624,15 +703,24 @@ function StatisticsTable({
624
703
  showsVerticalScrollIndicator: showScrollIndicator,
625
704
  onScroll: handleBodyScroll,
626
705
  scrollEventThrottle: 16,
627
- children: visibleRows.map(row => {
706
+ children: [enableVirtualization && virtualScrollData.offsetTop > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
707
+ style: {
708
+ height: virtualScrollData.offsetTop
709
+ }
710
+ }), visibleRows.map((row, rowIndex) => {
628
711
  const currentRowHeight = getRowHeightValue(row);
629
712
  const isClickable = !!onRowPress;
630
713
  const isRowHovered = hoveredCell?.rowKey === row.key;
631
714
  const isPlaceholder = row.key.startsWith('_placeholder_row_');
715
+ const isLastRow = rowIndex === visibleRows.length - 1;
632
716
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
633
717
  style: [styles.row, themedStyles.row, {
634
718
  minHeight: currentRowHeight
635
- }, isRowHovered && !isPlaceholder && themedStyles.rowHovered],
719
+ }, isRowHovered && !isPlaceholder && themedStyles.rowHovered,
720
+ // 最后一行且没有 Column Stats 时移除底部边框,避免与表格底边重合
721
+ isLastRow && !actualShowColumnStats && {
722
+ borderBottomWidth: 0
723
+ }],
636
724
  onPress: isClickable && !isPlaceholder ? () => onRowPress(row.key, row.data) : undefined,
637
725
  activeOpacity: isClickable && !isPlaceholder ? 0.7 : 1,
638
726
  disabled: !isClickable || isPlaceholder,
@@ -676,7 +764,11 @@ function StatisticsTable({
676
764
  }, column.key);
677
765
  })]
678
766
  }, row.key);
679
- })
767
+ }), enableVirtualization && virtualScrollData.offsetBottom > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
768
+ style: {
769
+ height: virtualScrollData.offsetBottom
770
+ }
771
+ })]
680
772
  }), actualShowColumnStats && columnStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
681
773
  style: [styles.columnStatsContainer, themedStyles.columnStatsDivider, enableStatsAnimation && {
682
774
  opacity: columnStatsAnimation,
@@ -826,15 +918,21 @@ function StatisticsTable({
826
918
  children: rowStatsHeaders.mean
827
919
  })
828
920
  })]
829
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
921
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
830
922
  ref: rowStatsScrollRef,
831
923
  style: {
832
924
  maxHeight: maxHeight - columnStatsHeight
833
925
  },
834
926
  showsVerticalScrollIndicator: false,
835
927
  scrollEnabled: false,
836
- children: visibleRows.map((row, rowIndex) => {
837
- const rowStat = rowStats?.[rowIndex];
928
+ children: [enableVirtualization && virtualScrollData.offsetTop > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
929
+ style: {
930
+ height: virtualScrollData.offsetTop
931
+ }
932
+ }), visibleRows.map((row, rowIndex) => {
933
+ // 计算在原始数组中的索引
934
+ const actualIndex = enableVirtualization ? virtualScrollData.startIndex + rowIndex : rowIndex;
935
+ const rowStat = rowStats?.[actualIndex];
838
936
  const currentRowHeight = getRowHeightValue(row);
839
937
  const isRowHovered = hoveredCell?.rowKey === row.key;
840
938
  const isSumHovered = hoveredCell?.rowKey === row.key && hoveredCell?.columnKey === '__row_sum__';
@@ -885,23 +983,13 @@ function StatisticsTable({
885
983
  })
886
984
  })]
887
985
  }, row.key);
888
- })
986
+ }), enableVirtualization && virtualScrollData.offsetBottom > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
987
+ style: {
988
+ height: virtualScrollData.offsetBottom
989
+ }
990
+ })]
889
991
  })]
890
992
  })]
891
- }), pagination !== false && pagination && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
892
- style: [styles.paginationContainer, themedStyles.paginationContainer],
893
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pagination.default, {
894
- totalCount: `${pagination.total} items`,
895
- pageParams: {
896
- page: pagination.current,
897
- pageSize: pagination.pageSize,
898
- totalItems: pagination.total,
899
- totalPage: Math.ceil(pagination.total / pagination.pageSize)
900
- },
901
- onPageChange: params => {
902
- pagination.onChange?.(params.page, params.pageSize);
903
- }
904
- })
905
993
  })]
906
994
  });
907
995
  }
@@ -990,9 +1078,6 @@ const styles = _reactNative.StyleSheet.create({
990
1078
  emptyText: {
991
1079
  fontSize: 14
992
1080
  },
993
- paginationContainer: {
994
- borderTopWidth: 1
995
- },
996
1081
  // Row Stats 容器
997
1082
  rowStatsContainer: {
998
1083
  position: 'relative'
@@ -1010,11 +1095,13 @@ const styles = _reactNative.StyleSheet.create({
1010
1095
  },
1011
1096
  shadowLeft: {
1012
1097
  left: 0,
1013
- top: 0,
1098
+ top: 40,
1099
+ // 从表头底部开始
1014
1100
  bottom: 0
1015
1101
  },
1016
1102
  shadowRight: {
1017
- top: 0,
1103
+ top: 40,
1104
+ // 从表头底部开始
1018
1105
  bottom: 0
1019
1106
  },
1020
1107
  // 水平阴影条
@@ -1027,7 +1114,7 @@ const styles = _reactNative.StyleSheet.create({
1027
1114
  backgroundColor: 'transparent'
1028
1115
  },
1029
1116
  shadowTop: {
1030
- top: 56 // header height
1117
+ top: 40 // header height (paddingVertical: 12*2 + content ~16)
1031
1118
  },
1032
1119
  shadowBottom: {
1033
1120
  // bottom 由动态样式设置