@beppla/tapas-ui 1.2.36 → 1.4.0

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.
@@ -6,11 +6,86 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.StatisticsTable = StatisticsTable;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
- var _Text = _interopRequireDefault(require("../Text/Text"));
9
+ var _themed = require("@rneui/themed");
10
10
  var _Pagination = _interopRequireDefault(require("../Pagination/Pagination"));
11
+ var _Hoverable = _interopRequireDefault(require("../Hoverable/Hoverable"));
11
12
  var _jsxRuntime = require("react/jsx-runtime");
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
+ // Tooltip 内容类型
16
+
17
+ // Tooltip 组件
18
+ const CellTooltip = ({
19
+ visible,
20
+ content,
21
+ style
22
+ }) => {
23
+ if (!visible || !content) return null;
24
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
25
+ style: [tooltipStyles.container, style],
26
+ children: content.customContent ? content.customContent : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
27
+ children: [content.title && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
28
+ style: tooltipStyles.title,
29
+ children: content.title
30
+ }), content.subtitle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
31
+ style: tooltipStyles.subtitle,
32
+ children: content.subtitle
33
+ }), content.items?.map((item, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
34
+ style: tooltipStyles.row,
35
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
36
+ style: tooltipStyles.label,
37
+ children: item.label
38
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
39
+ style: tooltipStyles.value,
40
+ children: item.value
41
+ })]
42
+ }, index))]
43
+ })
44
+ });
45
+ };
46
+ const tooltipStyles = _reactNative.StyleSheet.create({
47
+ container: {
48
+ position: 'absolute',
49
+ backgroundColor: '#FFF',
50
+ borderRadius: 8,
51
+ padding: 12,
52
+ shadowColor: '#000',
53
+ shadowOffset: {
54
+ width: 0,
55
+ height: 2
56
+ },
57
+ shadowOpacity: 0.15,
58
+ shadowRadius: 8,
59
+ elevation: 5,
60
+ zIndex: 100,
61
+ minWidth: 150
62
+ },
63
+ title: {
64
+ fontSize: 14,
65
+ fontWeight: '600',
66
+ color: 'rgba(0, 0, 0, 0.87)',
67
+ marginBottom: 4
68
+ },
69
+ subtitle: {
70
+ fontSize: 12,
71
+ color: 'rgba(0, 0, 0, 0.54)',
72
+ marginBottom: 8
73
+ },
74
+ row: {
75
+ flexDirection: 'row',
76
+ justifyContent: 'space-between',
77
+ marginTop: 4
78
+ },
79
+ label: {
80
+ fontSize: 12,
81
+ color: 'rgba(0, 0, 0, 0.54)'
82
+ },
83
+ value: {
84
+ fontSize: 12,
85
+ fontWeight: '600',
86
+ color: 'rgba(0, 0, 0, 0.87)'
87
+ }
88
+ });
14
89
  function StatisticsTable({
15
90
  rows,
16
91
  columns,
@@ -33,20 +108,164 @@ function StatisticsTable({
33
108
  getRowHeight,
34
109
  getColumnWidth,
35
110
  enableScrollShadow = true,
36
- scrollShadowColor = 'rgba(0, 0, 0, 0.1)',
37
- scrollShadowSize = 8
111
+ showScrollIndicator = false,
112
+ statsColumnWidth = 100,
113
+ onCellHover,
114
+ renderCellTooltip,
115
+ renderRowStatsTooltip,
116
+ renderColumnStatsTooltip,
117
+ rowStatsHeaders = {
118
+ sum: 'Sum (€)',
119
+ mean: 'Mean (€)'
120
+ },
121
+ columnStatsLabels = {
122
+ sum: 'Sum',
123
+ mean: 'Mean'
124
+ },
125
+ headerStyle,
126
+ rowStatsHeaderStyle,
127
+ columnStatsLabelStyle,
128
+ enableStatsAnimation = true,
129
+ statsAnimationDuration = 300
38
130
  }) {
131
+ const {
132
+ theme
133
+ } = (0, _themed.useTheme)();
134
+ const colors = theme.colors;
135
+
136
+ // 动画值
137
+ const rowStatsAnimation = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
138
+ const columnStatsAnimation = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
139
+
39
140
  // 滚动状态
40
- const [horizontalScrollState, setHorizontalScrollState] = (0, _react.useState)({
41
- isAtStart: true,
42
- isAtEnd: false
43
- });
44
- const [verticalScrollState, setVerticalScrollState] = (0, _react.useState)({
141
+ const [scrollState, setScrollState] = (0, _react.useState)({
142
+ left: 0,
143
+ top: 0,
45
144
  isAtStart: true,
46
- isAtEnd: false
145
+ isAtEnd: false,
146
+ isAtTop: true,
147
+ isAtBottom: false
47
148
  });
48
- const horizontalScrollRef = (0, _react.useRef)(null);
49
- const verticalScrollRef = (0, _react.useRef)(null);
149
+
150
+ // Hover 状态
151
+ const [hoveredCell, setHoveredCell] = (0, _react.useState)(null);
152
+ const scrollViewRef = (0, _react.useRef)(null);
153
+ const bodyScrollViewRef = (0, _react.useRef)(null);
154
+ const rowStatsScrollRef = (0, _react.useRef)(null);
155
+
156
+ // Column stats 和 Row stats 是互斥的,columnStats 优先
157
+ const actualShowColumnStats = showColumnStats;
158
+ const actualShowRowStats = showRowStats && !showColumnStats;
159
+
160
+ // Row Stats 动画效果
161
+ (0, _react.useEffect)(() => {
162
+ if (enableStatsAnimation && actualShowRowStats) {
163
+ _reactNative.Animated.timing(rowStatsAnimation, {
164
+ toValue: 1,
165
+ duration: statsAnimationDuration,
166
+ useNativeDriver: true
167
+ }).start();
168
+ } else {
169
+ rowStatsAnimation.setValue(actualShowRowStats ? 1 : 0);
170
+ }
171
+ }, [actualShowRowStats, enableStatsAnimation, statsAnimationDuration, rowStatsAnimation]);
172
+
173
+ // Column Stats 动画效果
174
+ (0, _react.useEffect)(() => {
175
+ if (enableStatsAnimation && actualShowColumnStats) {
176
+ _reactNative.Animated.timing(columnStatsAnimation, {
177
+ toValue: 1,
178
+ duration: statsAnimationDuration,
179
+ useNativeDriver: true
180
+ }).start();
181
+ } else {
182
+ columnStatsAnimation.setValue(actualShowColumnStats ? 1 : 0);
183
+ }
184
+ }, [actualShowColumnStats, enableStatsAnimation, statsAnimationDuration, columnStatsAnimation]);
185
+
186
+ // 动态样式 - 根据设计图
187
+ const themedStyles = (0, _react.useMemo)(() => ({
188
+ container: {
189
+ backgroundColor: colors.colorSurface // 米色背景
190
+ },
191
+ header: {
192
+ backgroundColor: headerStyle?.backgroundColor || colors.colorSurface,
193
+ // 米色
194
+ borderBottomColor: colors.colorTableBorder
195
+ },
196
+ headerCell: {
197
+ borderRightColor: colors.colorTableBorder
198
+ },
199
+ headerText: {
200
+ color: headerStyle?.textColor || colors.colorTextPrimary,
201
+ fontSize: headerStyle?.fontSize || 14,
202
+ fontWeight: headerStyle?.fontWeight || '500'
203
+ },
204
+ // Row Stats 表头样式
205
+ statsHeaderCell: {
206
+ backgroundColor: rowStatsHeaderStyle?.backgroundColor || 'rgba(0, 0, 0, 0.08)',
207
+ borderRightColor: colors.colorTableBorder,
208
+ borderBottomColor: colors.colorTableBorder
209
+ },
210
+ statsHeaderText: {
211
+ color: rowStatsHeaderStyle?.textColor || colors.colorTextPrimary,
212
+ fontSize: rowStatsHeaderStyle?.fontSize || 14,
213
+ fontWeight: rowStatsHeaderStyle?.fontWeight || '500'
214
+ },
215
+ // 数据行
216
+ row: {
217
+ borderBottomColor: colors.colorTableBorder,
218
+ backgroundColor: colors.colorSurface // 米色
219
+ },
220
+ // Hover 行
221
+ rowHovered: {
222
+ backgroundColor: colors.colorSurface1 // 白色
223
+ },
224
+ // Hover 单元格 - rgba(0, 0, 0, 0.08)
225
+ cellHovered: {
226
+ backgroundColor: 'rgba(0, 0, 0, 0.08)'
227
+ },
228
+ // Column Stats 行(Sum/Mean)- 纯白色背景
229
+ columnStatsRow: {
230
+ backgroundColor: '#FFFFFF',
231
+ // 纯白色
232
+ borderBottomColor: colors.colorTableBorder
233
+ },
234
+ // Row Stats 列 - 白色背景
235
+ rowStatsCell: {
236
+ backgroundColor: colors.colorSurface1,
237
+ // 白色
238
+ borderBottomColor: colors.colorTableBorder,
239
+ borderRightColor: colors.colorTableBorder
240
+ },
241
+ cell: {
242
+ borderRightColor: colors.colorTableBorder
243
+ },
244
+ rowLabel: {
245
+ color: colors.colorTextPrimary
246
+ },
247
+ cellQuantity: {
248
+ color: colors.colorTextPrimary
249
+ },
250
+ cellAmount: {
251
+ color: colors.colorTextSecondary
252
+ },
253
+ statsLabel: {
254
+ color: columnStatsLabelStyle?.textColor || colors.colorTextBrand,
255
+ // 棕色
256
+ fontSize: columnStatsLabelStyle?.fontSize || 14,
257
+ fontWeight: columnStatsLabelStyle?.fontWeight || '600'
258
+ },
259
+ paginationContainer: {
260
+ borderTopColor: colors.colorTableBorder,
261
+ backgroundColor: colors.colorSurface
262
+ },
263
+ // Column Stats 分隔线
264
+ columnStatsDivider: {
265
+ borderTopWidth: 2,
266
+ borderTopColor: colors.colorTableBorder
267
+ }
268
+ }), [colors, headerStyle, rowStatsHeaderStyle, columnStatsLabelStyle]);
50
269
 
51
270
  // 构建矩阵数据
52
271
  const matrix = (0, _react.useMemo)(() => {
@@ -66,34 +285,30 @@ function StatisticsTable({
66
285
  return data;
67
286
  }, [rows, columns, cells]);
68
287
 
69
- // 计算行统计(每行的总和和平均值)
288
+ // 计算行统计
70
289
  const rowStats = (0, _react.useMemo)(() => {
71
290
  if (!showRowStats) return null;
72
291
  return rows.map(row => {
73
- const rowCells = columns.filter(col => !col.isAction) // 排除 action
74
- .map(col => matrix[row.key]?.[col.key] || {
292
+ const rowCells = columns.filter(col => !col.isAction).map(col => matrix[row.key]?.[col.key] || {
75
293
  quantity: 0,
76
294
  amount: 0
77
295
  });
78
296
  const sumQuantity = rowCells.reduce((sum, cell) => sum + cell.quantity, 0);
79
297
  const sumAmount = rowCells.reduce((sum, cell) => sum + cell.amount, 0);
80
298
  const count = rowCells.length || 1;
81
- const meanQuantity = sumQuantity / count;
82
- const meanAmount = sumAmount / count;
83
299
  return {
84
300
  sumQuantity,
85
301
  sumAmount,
86
- meanQuantity,
87
- meanAmount
302
+ meanQuantity: sumQuantity / count,
303
+ meanAmount: sumAmount / count
88
304
  };
89
305
  });
90
306
  }, [rows, columns, matrix, showRowStats]);
91
307
 
92
- // 计算列统计(每列的总和和平均值)
308
+ // 计算列统计
93
309
  const columnStats = (0, _react.useMemo)(() => {
94
310
  if (!showColumnStats) return null;
95
- return columns.filter(col => !col.isAction) // 排除 action 列
96
- .map(col => {
311
+ return columns.filter(col => !col.isAction).map(col => {
97
312
  const colCells = rows.map(row => matrix[row.key]?.[col.key] || {
98
313
  quantity: 0,
99
314
  amount: 0
@@ -101,119 +316,160 @@ function StatisticsTable({
101
316
  const sumQuantity = colCells.reduce((sum, cell) => sum + cell.quantity, 0);
102
317
  const sumAmount = colCells.reduce((sum, cell) => sum + cell.amount, 0);
103
318
  const count = colCells.length || 1;
104
- const meanQuantity = sumQuantity / count;
105
- const meanAmount = sumAmount / count;
106
319
  return {
107
320
  sumQuantity,
108
321
  sumAmount,
109
- meanQuantity,
110
- meanAmount
322
+ meanQuantity: sumQuantity / count,
323
+ meanAmount: sumAmount / count
111
324
  };
112
325
  });
113
326
  }, [rows, columns, matrix, showColumnStats]);
114
-
115
- // 获取列宽
116
327
  const getColWidth = (0, _react.useCallback)(column => {
117
- if (getColumnWidth) {
118
- return getColumnWidth(column.key, column);
119
- }
328
+ if (getColumnWidth) return getColumnWidth(column.key, column);
120
329
  return column.width || 150;
121
330
  }, [getColumnWidth]);
122
-
123
- // 获取行高
124
331
  const getRowHeightValue = (0, _react.useCallback)(row => {
125
332
  if (row.height) return row.height;
126
- if (getRowHeight) {
127
- return getRowHeight(row.key, row.data);
128
- }
333
+ if (getRowHeight) return getRowHeight(row.key, row.data);
129
334
  return rowHeight;
130
335
  }, [rowHeight, getRowHeight]);
131
336
 
132
- // 处理水平滚动
133
- const handleHorizontalScroll = (0, _react.useCallback)(event => {
337
+ // 处理滚动
338
+ const handleScroll = (0, _react.useCallback)(event => {
134
339
  const {
135
340
  contentOffset,
136
341
  contentSize,
137
342
  layoutMeasurement
138
343
  } = event.nativeEvent;
139
- const isAtStart = contentOffset.x <= 0;
140
- const isAtEnd = contentOffset.x + layoutMeasurement.width >= contentSize.width - 1;
141
- setHorizontalScrollState({
142
- isAtStart,
143
- isAtEnd
344
+ setScrollState({
345
+ left: contentOffset.x,
346
+ top: contentOffset.y,
347
+ isAtStart: contentOffset.x <= 0,
348
+ isAtEnd: contentOffset.x + layoutMeasurement.width >= contentSize.width - 1,
349
+ isAtTop: contentOffset.y <= 0,
350
+ isAtBottom: contentOffset.y + layoutMeasurement.height >= contentSize.height - 1
144
351
  });
145
352
  }, []);
146
353
 
147
- // 处理垂直滚动
148
- const handleVerticalScroll = (0, _react.useCallback)(event => {
354
+ // 同步 Row Stats 滚动
355
+ const handleBodyScroll = (0, _react.useCallback)(event => {
149
356
  const {
150
- contentOffset,
151
- contentSize,
152
- layoutMeasurement
357
+ contentOffset
153
358
  } = event.nativeEvent;
154
- const isAtStart = contentOffset.y <= 0;
155
- const isAtEnd = contentOffset.y + layoutMeasurement.height >= contentSize.height - 1;
156
- setVerticalScrollState({
157
- isAtStart,
158
- isAtEnd
359
+ rowStatsScrollRef.current?.scrollTo({
360
+ y: contentOffset.y,
361
+ animated: false
159
362
  });
160
- }, []);
363
+ handleScroll(event);
364
+ }, [handleScroll]);
365
+
366
+ // 处理单元格 hover
367
+ const handleCellHover = (0, _react.useCallback)((rowKey, columnKey, isHovered) => {
368
+ if (isHovered) {
369
+ setHoveredCell({
370
+ rowKey,
371
+ columnKey
372
+ });
373
+ } else {
374
+ setHoveredCell(null);
375
+ }
376
+ onCellHover?.(rowKey, columnKey, isHovered);
377
+ }, [onCellHover]);
161
378
 
162
- // 获取单元格对齐方式
163
- const getCellAlignment = (0, _react.useCallback)((column, isHeader = false) => {
164
- if (column.align) return column.align;
165
- if (isHeader) return 'center';
166
- // 默认:数字右对齐,文本左对齐
167
- if (column.dataType === 'number' || column.dataType === 'currency') {
168
- return 'right';
379
+ // 获取单元格 tooltip 内容
380
+ const getCellTooltipContent = (0, _react.useCallback)((rowKey, columnKey, cell, column) => {
381
+ if (renderCellTooltip) {
382
+ return renderCellTooltip(rowKey, columnKey, cell, column);
169
383
  }
170
- return 'left';
171
- }, []);
384
+ return {
385
+ title: column.title,
386
+ items: [{
387
+ label: 'Quantity',
388
+ value: formatQuantity(cell.quantity)
389
+ }, {
390
+ label: 'Amount',
391
+ value: formatAmount(cell.amount)
392
+ }]
393
+ };
394
+ }, [renderCellTooltip, formatQuantity, formatAmount]);
172
395
 
173
- // 渲染单元格内容
174
- const renderCellContent = (0, _react.useCallback)((column, cell, rowData) => {
175
- // 自定义渲染
176
- if (column.render) {
177
- return column.render(cell, rowData);
396
+ // 获取行统计 tooltip
397
+ const getRowStatsTooltipContent = (0, _react.useCallback)((rowKey, stats, storeCount) => {
398
+ if (renderRowStatsTooltip) {
399
+ return renderRowStatsTooltip(rowKey, stats, storeCount);
178
400
  }
401
+ const row = rows.find(r => r.key === rowKey);
402
+ return {
403
+ title: row?.label || rowKey,
404
+ subtitle: `${storeCount} stores`,
405
+ items: [{
406
+ label: 'Sum',
407
+ value: formatAmount(stats.sumAmount)
408
+ }, {
409
+ label: 'Mean',
410
+ value: formatAmount(stats.meanAmount)
411
+ }]
412
+ };
413
+ }, [renderRowStatsTooltip, formatAmount, rows]);
179
414
 
180
- // Action
415
+ // 获取列统计 tooltip
416
+ const getColumnStatsTooltipContent = (0, _react.useCallback)((columnKey, column, stats) => {
417
+ if (renderColumnStatsTooltip) {
418
+ return renderColumnStatsTooltip(columnKey, column, stats);
419
+ }
420
+ return {
421
+ title: column.title,
422
+ items: [{
423
+ label: 'Sum',
424
+ value: formatAmount(stats.sumAmount)
425
+ }, {
426
+ label: 'Mean',
427
+ value: formatAmount(stats.meanAmount)
428
+ }]
429
+ };
430
+ }, [renderColumnStatsTooltip, formatAmount]);
431
+
432
+ // 渲染单元格内容
433
+ const renderCellContent = (0, _react.useCallback)((column, cell, rowData) => {
434
+ if (column.render) return column.render(cell, rowData);
181
435
  if (column.isAction && rowActions) {
182
436
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
183
437
  style: styles.actionContainer,
184
438
  children: rowActions.map((action, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
185
- style: styles.actionButton,
439
+ style: [styles.actionButton, {
440
+ backgroundColor: colors.colorTextAccent
441
+ }],
186
442
  onPress: () => action.onPress(cell.rowKey, rowData),
187
443
  activeOpacity: 0.7,
188
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
189
- style: styles.actionButtonText,
444
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
445
+ style: [styles.actionButtonText, {
446
+ color: colors.colorTextInvert
447
+ }],
190
448
  children: action.label
191
449
  })
192
450
  }, index))
193
451
  });
194
452
  }
195
-
196
- // 默认渲染
197
- const align = getCellAlignment(column);
198
453
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
199
- style: [styles.cellContentContainer, {
200
- alignItems: align === 'left' ? 'flex-start' : align === 'right' ? 'flex-end' : 'center'
201
- }],
202
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
203
- style: [styles.cellQuantity, align === 'right' && styles.cellQuantityRight],
454
+ style: styles.cellContentContainer,
455
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
456
+ style: [styles.cellQuantity, themedStyles.cellQuantity],
204
457
  children: formatQuantity(cell.quantity)
205
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
206
- style: [styles.cellAmount, align === 'right' && styles.cellAmountRight],
458
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
459
+ style: [styles.cellAmount, themedStyles.cellAmount],
207
460
  children: formatAmount(cell.amount)
208
461
  })]
209
462
  });
210
- }, [rowActions, getCellAlignment, formatQuantity, formatAmount]);
463
+ }, [rowActions, formatQuantity, formatAmount, colors, themedStyles]);
464
+
465
+ // 计算统计列宽度
466
+ const statsColumnsWidth = actualShowRowStats ? statsColumnWidth * 2 : 0;
211
467
  if (loading) {
212
468
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
213
- style: [styles.container, style],
469
+ style: [styles.container, themedStyles.container, style],
214
470
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
215
471
  style: styles.loadingRow,
216
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
472
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
217
473
  children: "Loading..."
218
474
  })
219
475
  })
@@ -221,282 +477,361 @@ function StatisticsTable({
221
477
  }
222
478
  if (rows.length === 0 || columns.length === 0) {
223
479
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
224
- style: [styles.container, style],
480
+ style: [styles.container, themedStyles.container, style],
225
481
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
226
482
  style: styles.emptyRow,
227
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
228
- style: styles.emptyText,
483
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
484
+ style: [styles.emptyText, {
485
+ color: colors.colorTextPlaceholder
486
+ }],
229
487
  children: emptyText
230
488
  })
231
489
  })
232
490
  });
233
491
  }
234
-
235
- // 虚拟滚动实现(简化版)
236
492
  const visibleRows = enableVirtualization ? rows.slice(0, Math.ceil(maxHeight / virtualRowHeight)) : rows;
493
+
494
+ // Web 阴影样式
495
+ const webShadowStyle = _reactNative.Platform.OS === 'web' ? {
496
+ leftShadow: {
497
+ boxShadow: '2px 0 6px 4px rgba(0, 0, 0, 0.08)'
498
+ },
499
+ rightShadow: {
500
+ boxShadow: '-2px 0 6px 4px rgba(0, 0, 0, 0.08)'
501
+ },
502
+ topShadow: {
503
+ boxShadow: '0 2px 6px 4px rgba(0, 0, 0, 0.08)'
504
+ },
505
+ bottomShadow: {
506
+ boxShadow: '0 -2px 6px 4px rgba(0, 0, 0, 0.08)'
507
+ }
508
+ } : {};
509
+
510
+ // 隐藏滚动条的样式
511
+ const hideScrollbarStyle = _reactNative.Platform.OS === 'web' ? {
512
+ scrollbarWidth: 'none',
513
+ msOverflowStyle: 'none'
514
+ } : {};
515
+ const columnStatsHeight = actualShowColumnStats ? 120 : 0;
516
+ const storeCount = columns.filter(c => !c.isAction).length;
237
517
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
238
- style: [styles.container, style],
239
- children: [enableScrollShadow && !horizontalScrollState.isAtStart && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
240
- style: [styles.shadowLeft, {
241
- width: scrollShadowSize,
242
- backgroundColor: 'transparent',
243
- shadowColor: scrollShadowColor
244
- }]
245
- }), enableScrollShadow && !horizontalScrollState.isAtEnd && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
246
- style: [styles.shadowRight, {
247
- width: scrollShadowSize,
248
- backgroundColor: 'transparent',
249
- shadowColor: scrollShadowColor
250
- }]
251
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
252
- ref: horizontalScrollRef,
253
- horizontal: true,
254
- showsHorizontalScrollIndicator: _reactNative.Platform.OS === 'web',
255
- onScroll: handleHorizontalScroll,
256
- scrollEventThrottle: 16,
257
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
258
- children: [enableScrollShadow && !verticalScrollState.isAtStart && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
259
- style: [styles.shadowTop, {
260
- height: scrollShadowSize,
261
- backgroundColor: 'transparent',
262
- shadowColor: scrollShadowColor
263
- }]
264
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
265
- style: styles.header,
266
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
267
- style: [styles.headerCell, {
268
- width: rowLabelWidth
269
- }],
270
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
271
- style: styles.headerText
272
- })
273
- }), columns.map(column => {
274
- const align = getCellAlignment(column, true);
275
- const colWidth = getColWidth(column);
276
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
277
- style: [styles.headerCell, {
278
- width: colWidth,
279
- minWidth: column.minWidth,
280
- maxWidth: column.maxWidth
281
- }, align === 'right' && styles.headerCellRight, align === 'left' && styles.headerCellLeft],
282
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
283
- style: [styles.headerText, align === 'right' && styles.headerTextRight, align === 'left' && styles.headerTextLeft],
284
- children: column.title
285
- })
286
- }, column.key);
287
- }), showRowStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
288
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
289
- style: [styles.headerCell, {
290
- width: 120
291
- }],
292
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
293
- style: styles.headerText,
294
- children: "Sum"
295
- })
296
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
297
- style: [styles.headerCell, {
298
- width: 120
299
- }],
300
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
301
- style: styles.headerText,
302
- children: "Mean"
303
- })
304
- })]
305
- })]
306
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
307
- ref: verticalScrollRef,
308
- style: {
309
- maxHeight
310
- },
311
- showsVerticalScrollIndicator: _reactNative.Platform.OS === 'web',
312
- removeClippedSubviews: enableVirtualization,
313
- onScroll: handleVerticalScroll,
518
+ style: [styles.container, themedStyles.container, style],
519
+ children: [enableScrollShadow && !scrollState.isAtStart && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
520
+ style: [styles.shadowBar, styles.shadowLeft, _reactNative.Platform.OS === 'web' && webShadowStyle.leftShadow]
521
+ }), enableScrollShadow && !scrollState.isAtEnd && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
522
+ style: [styles.shadowBar, styles.shadowRight, {
523
+ right: actualShowRowStats ? statsColumnsWidth : 0
524
+ }, _reactNative.Platform.OS === 'web' && webShadowStyle.rightShadow]
525
+ }), enableScrollShadow && !scrollState.isAtTop && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
526
+ style: [styles.shadowBarHorizontal, styles.shadowTop, _reactNative.Platform.OS === 'web' && webShadowStyle.topShadow]
527
+ }), enableScrollShadow && !scrollState.isAtBottom && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
528
+ style: [styles.shadowBarHorizontal, styles.shadowBottom, {
529
+ bottom: actualShowColumnStats ? columnStatsHeight : 0
530
+ }, _reactNative.Platform.OS === 'web' && webShadowStyle.bottomShadow]
531
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
532
+ style: styles.tableContainer,
533
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
534
+ style: {
535
+ flex: 1
536
+ },
537
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
538
+ ref: scrollViewRef,
539
+ horizontal: true,
540
+ showsHorizontalScrollIndicator: showScrollIndicator,
541
+ onScroll: handleScroll,
314
542
  scrollEventThrottle: 16,
315
- children: [visibleRows.map((row, rowIndex) => {
316
- const rowStat = rowStats?.[rowIndex];
317
- const isClickable = !!onRowPress;
318
- const currentRowHeight = getRowHeightValue(row);
319
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
320
- style: [styles.row, {
321
- minHeight: currentRowHeight
322
- }],
323
- onPress: isClickable ? () => onRowPress(row.key, row.data) : undefined,
324
- activeOpacity: isClickable ? 0.7 : 1,
325
- disabled: !isClickable,
543
+ style: hideScrollbarStyle,
544
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
545
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
546
+ style: [styles.header, themedStyles.header],
326
547
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
327
- style: [styles.cell, {
328
- width: rowLabelWidth,
329
- minHeight: currentRowHeight
548
+ style: [styles.headerCell, themedStyles.headerCell, {
549
+ width: rowLabelWidth
330
550
  }],
331
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
332
- style: styles.rowLabel,
333
- children: row.label
551
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
552
+ style: [styles.headerText, themedStyles.headerText]
334
553
  })
335
554
  }), columns.map(column => {
336
- const cell = matrix[row.key]?.[column.key] || {
337
- quantity: 0,
338
- amount: 0,
339
- rowKey: row.key,
340
- columnKey: column.key
341
- };
342
- const align = getCellAlignment(column);
343
555
  const colWidth = getColWidth(column);
344
556
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
345
- style: [styles.cell, {
346
- width: colWidth,
347
- minWidth: column.minWidth,
348
- maxWidth: column.maxWidth,
349
- minHeight: currentRowHeight
350
- }, align === 'right' && styles.cellRight, align === 'left' && styles.cellLeft],
351
- children: renderCellContent(column, cell, row.data)
557
+ style: [styles.headerCell, themedStyles.headerCell, {
558
+ width: colWidth
559
+ }],
560
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
561
+ style: [styles.headerText, themedStyles.headerText],
562
+ children: column.title
563
+ })
352
564
  }, column.key);
353
- }), showRowStats && rowStat && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
565
+ })]
566
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
567
+ ref: bodyScrollViewRef,
568
+ style: {
569
+ maxHeight: maxHeight - columnStatsHeight
570
+ },
571
+ showsVerticalScrollIndicator: showScrollIndicator,
572
+ onScroll: handleBodyScroll,
573
+ scrollEventThrottle: 16,
574
+ children: visibleRows.map(row => {
575
+ const currentRowHeight = getRowHeightValue(row);
576
+ const isClickable = !!onRowPress;
577
+ const isRowHovered = hoveredCell?.rowKey === row.key;
578
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
579
+ style: [styles.row, themedStyles.row, {
580
+ minHeight: currentRowHeight
581
+ }, isRowHovered && themedStyles.rowHovered],
582
+ onPress: isClickable ? () => onRowPress(row.key, row.data) : undefined,
583
+ activeOpacity: isClickable ? 0.7 : 1,
584
+ disabled: !isClickable,
585
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
586
+ style: [styles.cell, themedStyles.cell, {
587
+ width: rowLabelWidth
588
+ }],
589
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
590
+ style: [styles.rowLabel, themedStyles.rowLabel],
591
+ children: row.label
592
+ })
593
+ }), columns.map(column => {
594
+ const cell = matrix[row.key]?.[column.key] || {
595
+ quantity: 0,
596
+ amount: 0,
597
+ rowKey: row.key,
598
+ columnKey: column.key
599
+ };
600
+ const colWidth = getColWidth(column);
601
+ const isHovered = hoveredCell?.rowKey === row.key && hoveredCell?.columnKey === column.key;
602
+ const tooltipContent = isHovered ? getCellTooltipContent(row.key, column.key, cell, column) : null;
603
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Hoverable.default, {
604
+ onHoverIn: () => handleCellHover(row.key, column.key, true),
605
+ onHoverOut: () => handleCellHover(row.key, column.key, false),
606
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
607
+ style: [styles.cell, themedStyles.cell, {
608
+ width: colWidth
609
+ }, isHovered && themedStyles.cellHovered],
610
+ children: [renderCellContent(column, cell, row.data), /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
611
+ visible: isHovered,
612
+ content: tooltipContent,
613
+ style: {
614
+ bottom: '100%',
615
+ left: 0,
616
+ marginBottom: 8
617
+ }
618
+ })]
619
+ })
620
+ }, column.key);
621
+ })]
622
+ }, row.key);
623
+ })
624
+ }), actualShowColumnStats && columnStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
625
+ style: [styles.columnStatsContainer, themedStyles.columnStatsDivider, enableStatsAnimation && {
626
+ opacity: columnStatsAnimation,
627
+ transform: [{
628
+ translateY: columnStatsAnimation.interpolate({
629
+ inputRange: [0, 1],
630
+ outputRange: [60, 0]
631
+ })
632
+ }]
633
+ }],
634
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
635
+ style: [styles.row, themedStyles.columnStatsRow],
354
636
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
355
- style: [styles.cell, styles.cellRight, {
356
- width: 120
637
+ style: [styles.cell, themedStyles.cell, {
638
+ width: rowLabelWidth
357
639
  }],
358
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
359
- style: styles.cellContentContainer,
360
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
361
- style: [styles.cellQuantity, styles.cellQuantityRight],
362
- children: formatQuantity(rowStat.sumQuantity)
363
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
364
- style: [styles.cellAmount, styles.cellAmountRight],
365
- children: formatAmount(rowStat.sumAmount)
366
- })]
640
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
641
+ style: [styles.statsLabel, themedStyles.statsLabel],
642
+ children: columnStatsLabels.sum
367
643
  })
368
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
369
- style: [styles.cell, styles.cellRight, {
370
- width: 120
644
+ }), columns.map(column => {
645
+ const colWidth = getColWidth(column);
646
+ if (column.isAction) {
647
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
648
+ style: [styles.cell, {
649
+ width: colWidth
650
+ }]
651
+ }, column.key);
652
+ }
653
+ const stat = columnStats[columns.filter(c => !c.isAction).indexOf(column)];
654
+ const isHovered = hoveredCell?.rowKey === '__column_sum__' && hoveredCell?.columnKey === column.key;
655
+ const tooltipContent = isHovered && stat ? getColumnStatsTooltipContent(column.key, column, stat) : null;
656
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Hoverable.default, {
657
+ onHoverIn: () => handleCellHover('__column_sum__', column.key, true),
658
+ onHoverOut: () => handleCellHover('__column_sum__', column.key, false),
659
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
660
+ style: [styles.cell, themedStyles.cell, {
661
+ width: colWidth
662
+ }, isHovered && themedStyles.cellHovered],
663
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
664
+ style: styles.cellContentContainer,
665
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
666
+ style: [styles.cellQuantity, themedStyles.cellQuantity],
667
+ children: formatQuantity(stat?.sumQuantity || 0)
668
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
669
+ style: [styles.cellAmount, themedStyles.cellAmount],
670
+ children: formatAmount(stat?.sumAmount || 0)
671
+ })]
672
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
673
+ visible: isHovered,
674
+ content: tooltipContent,
675
+ style: {
676
+ bottom: '100%',
677
+ left: 0,
678
+ marginBottom: 8
679
+ }
680
+ })]
681
+ })
682
+ }, column.key);
683
+ })]
684
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
685
+ style: [styles.row, themedStyles.columnStatsRow],
686
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
687
+ style: [styles.cell, themedStyles.cell, {
688
+ width: rowLabelWidth
371
689
  }],
372
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
373
- style: styles.cellContentContainer,
374
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
375
- style: [styles.cellQuantity, styles.cellQuantityRight],
376
- children: formatQuantity(Math.round(rowStat.meanQuantity * 10) / 10)
377
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
378
- style: [styles.cellAmount, styles.cellAmountRight],
379
- children: formatAmount(rowStat.meanAmount)
380
- })]
690
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
691
+ style: [styles.statsLabel, themedStyles.statsLabel],
692
+ children: columnStatsLabels.mean
381
693
  })
694
+ }), columns.map(column => {
695
+ const colWidth = getColWidth(column);
696
+ if (column.isAction) {
697
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
698
+ style: [styles.cell, {
699
+ width: colWidth
700
+ }]
701
+ }, column.key);
702
+ }
703
+ const stat = columnStats[columns.filter(c => !c.isAction).indexOf(column)];
704
+ const isHovered = hoveredCell?.rowKey === '__column_mean__' && hoveredCell?.columnKey === column.key;
705
+ const tooltipContent = isHovered && stat ? getColumnStatsTooltipContent(column.key, column, stat) : null;
706
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Hoverable.default, {
707
+ onHoverIn: () => handleCellHover('__column_mean__', column.key, true),
708
+ onHoverOut: () => handleCellHover('__column_mean__', column.key, false),
709
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
710
+ style: [styles.cell, themedStyles.cell, {
711
+ width: colWidth
712
+ }, isHovered && themedStyles.cellHovered],
713
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
714
+ style: styles.cellContentContainer,
715
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
716
+ style: [styles.cellQuantity, themedStyles.cellQuantity],
717
+ children: formatQuantity(Math.round((stat?.meanQuantity || 0) * 10) / 10)
718
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
719
+ style: [styles.cellAmount, themedStyles.cellAmount],
720
+ children: formatAmount(stat?.meanAmount || 0)
721
+ })]
722
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
723
+ visible: isHovered,
724
+ content: tooltipContent,
725
+ style: {
726
+ bottom: '100%',
727
+ left: 0,
728
+ marginBottom: 8
729
+ }
730
+ })]
731
+ })
732
+ }, column.key);
382
733
  })]
383
734
  })]
384
- }, row.key);
385
- }), showColumnStats && columnStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
386
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
387
- style: [styles.row, styles.statsRow],
388
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
389
- style: [styles.cell, {
390
- width: rowLabelWidth
391
- }],
392
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
393
- style: styles.statsLabel,
394
- children: "Sum"
395
- })
396
- }), columns.map(column => {
397
- const colWidth = getColWidth(column);
398
- if (column.isAction) {
399
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
400
- style: [styles.cell, {
401
- width: colWidth,
402
- minWidth: column.minWidth,
403
- maxWidth: column.maxWidth
404
- }]
405
- }, column.key);
406
- }
407
- const stat = columnStats[columns.filter(c => !c.isAction).indexOf(column)];
408
- const align = getCellAlignment(column);
409
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
410
- style: [styles.cell, {
411
- width: colWidth,
412
- minWidth: column.minWidth,
413
- maxWidth: column.maxWidth
414
- }, align === 'right' && styles.cellRight, align === 'left' && styles.cellLeft],
415
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
735
+ })]
736
+ })
737
+ })
738
+ }), actualShowRowStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
739
+ style: [styles.rowStatsContainer, {
740
+ width: statsColumnsWidth
741
+ }, _reactNative.Platform.OS === 'web' && {
742
+ boxShadow: '-2px 0 6px rgba(0, 0, 0, 0.08)'
743
+ }, enableStatsAnimation && {
744
+ opacity: rowStatsAnimation,
745
+ transform: [{
746
+ translateX: rowStatsAnimation.interpolate({
747
+ inputRange: [0, 1],
748
+ outputRange: [statsColumnsWidth, 0]
749
+ })
750
+ }]
751
+ }],
752
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
753
+ style: [styles.header, themedStyles.header],
754
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
755
+ style: [styles.headerCell, themedStyles.statsHeaderCell, {
756
+ width: statsColumnWidth
757
+ }],
758
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
759
+ style: [styles.headerText, themedStyles.statsHeaderText],
760
+ children: rowStatsHeaders.sum
761
+ })
762
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
763
+ style: [styles.headerCell, themedStyles.statsHeaderCell, {
764
+ width: statsColumnWidth
765
+ }],
766
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
767
+ style: [styles.headerText, themedStyles.statsHeaderText],
768
+ children: rowStatsHeaders.mean
769
+ })
770
+ })]
771
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
772
+ ref: rowStatsScrollRef,
773
+ style: {
774
+ maxHeight: maxHeight - columnStatsHeight
775
+ },
776
+ showsVerticalScrollIndicator: false,
777
+ scrollEnabled: false,
778
+ children: visibleRows.map((row, rowIndex) => {
779
+ const rowStat = rowStats?.[rowIndex];
780
+ const currentRowHeight = getRowHeightValue(row);
781
+ const isRowHovered = hoveredCell?.rowKey === row.key;
782
+ const isSumHovered = hoveredCell?.rowKey === row.key && hoveredCell?.columnKey === '__row_sum__';
783
+ const sumTooltipContent = isSumHovered && rowStat ? getRowStatsTooltipContent(row.key, rowStat, storeCount) : null;
784
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
785
+ style: [styles.row, themedStyles.rowStatsCell, {
786
+ minHeight: currentRowHeight
787
+ }, isRowHovered && themedStyles.rowHovered],
788
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Hoverable.default, {
789
+ onHoverIn: () => handleCellHover(row.key, '__row_sum__', true),
790
+ onHoverOut: () => handleCellHover(row.key, '__row_sum__', false),
791
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
792
+ style: [styles.cell, themedStyles.cell, {
793
+ width: statsColumnWidth
794
+ }],
795
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
416
796
  style: styles.cellContentContainer,
417
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
418
- style: [styles.cellQuantity, align === 'right' && styles.cellQuantityRight],
419
- children: formatQuantity(stat?.sumQuantity || 0)
420
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
421
- style: [styles.cellAmount, align === 'right' && styles.cellAmountRight],
422
- children: formatAmount(stat?.sumAmount || 0)
797
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
798
+ style: [styles.cellQuantity, themedStyles.cellQuantity],
799
+ children: formatQuantity(rowStat?.sumQuantity || 0)
800
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
801
+ style: [styles.cellAmount, themedStyles.cellAmount],
802
+ children: formatAmount(rowStat?.sumAmount || 0)
423
803
  })]
424
- })
425
- }, column.key);
426
- }), showRowStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
427
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
428
- style: [styles.cell, {
429
- width: 120
430
- }]
431
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
432
- style: [styles.cell, {
433
- width: 120
434
- }]
435
- })]
436
- })]
437
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
438
- style: [styles.row, styles.statsRow],
439
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
440
- style: [styles.cell, {
441
- width: rowLabelWidth
804
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
805
+ visible: isSumHovered,
806
+ content: sumTooltipContent,
807
+ style: {
808
+ bottom: '100%',
809
+ right: 0,
810
+ marginBottom: 8
811
+ }
812
+ })]
813
+ })
814
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
815
+ style: [styles.cell, themedStyles.cell, {
816
+ width: statsColumnWidth
442
817
  }],
443
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
444
- style: styles.statsLabel,
445
- children: "Mean"
818
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
819
+ style: styles.cellContentContainer,
820
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
821
+ style: [styles.cellQuantity, themedStyles.cellQuantity],
822
+ children: formatQuantity(Math.round((rowStat?.meanQuantity || 0) * 10) / 10)
823
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
824
+ style: [styles.cellAmount, themedStyles.cellAmount],
825
+ children: formatAmount(rowStat?.meanAmount || 0)
826
+ })]
446
827
  })
447
- }), columns.map(column => {
448
- const colWidth = getColWidth(column);
449
- if (column.isAction) {
450
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
451
- style: [styles.cell, {
452
- width: colWidth,
453
- minWidth: column.minWidth,
454
- maxWidth: column.maxWidth
455
- }]
456
- }, column.key);
457
- }
458
- const stat = columnStats[columns.filter(c => !c.isAction).indexOf(column)];
459
- const align = getCellAlignment(column);
460
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
461
- style: [styles.cell, {
462
- width: colWidth,
463
- minWidth: column.minWidth,
464
- maxWidth: column.maxWidth
465
- }, align === 'right' && styles.cellRight, align === 'left' && styles.cellLeft],
466
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
467
- style: styles.cellContentContainer,
468
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
469
- style: [styles.cellQuantity, align === 'right' && styles.cellQuantityRight],
470
- children: formatQuantity(Math.round((stat?.meanQuantity || 0) * 10) / 10)
471
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
472
- style: [styles.cellAmount, align === 'right' && styles.cellAmountRight],
473
- children: formatAmount(stat?.meanAmount || 0)
474
- })]
475
- })
476
- }, column.key);
477
- }), showRowStats && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
478
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
479
- style: [styles.cell, {
480
- width: 120
481
- }]
482
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
483
- style: [styles.cell, {
484
- width: 120
485
- }]
486
- })]
487
828
  })]
488
- })]
489
- })]
490
- }), enableScrollShadow && !verticalScrollState.isAtEnd && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
491
- style: [styles.shadowBottom, {
492
- height: scrollShadowSize,
493
- backgroundColor: 'transparent',
494
- shadowColor: scrollShadowColor
495
- }]
829
+ }, row.key);
830
+ })
496
831
  })]
497
- })
832
+ })]
498
833
  }), pagination !== false && pagination && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
499
- style: styles.paginationContainer,
834
+ style: [styles.paginationContainer, themedStyles.paginationContainer],
500
835
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pagination.default, {
501
836
  totalCount: `${pagination.total} items`,
502
837
  pageParams: {
@@ -514,93 +849,58 @@ function StatisticsTable({
514
849
  }
515
850
  const styles = _reactNative.StyleSheet.create({
516
851
  container: {
517
- backgroundColor: '#FFFFFF',
518
- borderRadius: 8,
852
+ borderRadius: 12,
519
853
  overflow: 'hidden'
520
854
  },
855
+ tableContainer: {
856
+ flexDirection: 'row'
857
+ },
521
858
  header: {
522
859
  flexDirection: 'row',
523
- backgroundColor: '#F5F5F5',
524
- borderBottomWidth: 2,
525
- borderBottomColor: '#E0E0E0'
860
+ borderBottomWidth: 1
526
861
  },
527
862
  headerCell: {
528
- padding: 12,
529
- borderRightWidth: 1,
530
- borderRightColor: '#E0E0E0',
863
+ paddingVertical: 16,
864
+ paddingHorizontal: 12,
531
865
  justifyContent: 'center',
532
866
  alignItems: 'center'
533
867
  },
534
- headerCellLeft: {
535
- alignItems: 'flex-start'
536
- },
537
- headerCellRight: {
538
- alignItems: 'flex-end'
539
- },
540
868
  headerText: {
541
869
  fontSize: 14,
542
- fontWeight: '600',
543
- color: '#2C3E50',
870
+ fontWeight: '500',
544
871
  textAlign: 'center'
545
872
  },
546
- headerTextLeft: {
547
- textAlign: 'left'
548
- },
549
- headerTextRight: {
550
- textAlign: 'right'
551
- },
552
873
  row: {
553
874
  flexDirection: 'row',
554
- borderBottomWidth: 1,
555
- borderBottomColor: '#F0F0F0',
556
- backgroundColor: '#FFFFFF'
557
- },
558
- statsRow: {
559
- backgroundColor: '#F9F9F9'
875
+ borderBottomWidth: 1
560
876
  },
561
877
  cell: {
562
- padding: 12,
878
+ paddingVertical: 16,
879
+ paddingHorizontal: 12,
563
880
  justifyContent: 'center',
564
- alignItems: 'center',
565
- borderRightWidth: 1,
566
- borderRightColor: '#F0F0F0'
567
- },
568
- cellLeft: {
569
- alignItems: 'flex-start'
570
- },
571
- cellRight: {
572
- alignItems: 'flex-end'
881
+ alignItems: 'center'
573
882
  },
574
883
  cellContentContainer: {
575
- width: '100%'
884
+ width: '100%',
885
+ alignItems: 'center'
576
886
  },
577
887
  rowLabel: {
578
888
  fontSize: 14,
579
- fontWeight: '500',
580
- color: '#2C3E50'
889
+ fontWeight: '500'
581
890
  },
582
891
  cellQuantity: {
583
- fontSize: 14,
584
- fontWeight: '500',
585
- color: '#2C3E50',
586
- marginBottom: 4,
587
- textAlign: 'left'
588
- },
589
- cellQuantityRight: {
590
- textAlign: 'right'
892
+ fontSize: 16,
893
+ fontWeight: '600',
894
+ marginBottom: 2,
895
+ textAlign: 'center'
591
896
  },
592
897
  cellAmount: {
593
898
  fontSize: 12,
594
- color: '#7F8C8D',
595
- textAlign: 'left'
596
- },
597
- cellAmountRight: {
598
- textAlign: 'right'
899
+ textAlign: 'center'
599
900
  },
600
901
  statsLabel: {
601
902
  fontSize: 14,
602
- fontWeight: '600',
603
- color: '#2C3E50'
903
+ fontWeight: '600'
604
904
  },
605
905
  actionContainer: {
606
906
  flexDirection: 'row',
@@ -610,13 +910,11 @@ const styles = _reactNative.StyleSheet.create({
610
910
  actionButton: {
611
911
  paddingHorizontal: 12,
612
912
  paddingVertical: 6,
613
- backgroundColor: '#FF6B00',
614
913
  borderRadius: 4
615
914
  },
616
915
  actionButtonText: {
617
916
  fontSize: 12,
618
- fontWeight: '600',
619
- color: '#FFFFFF'
917
+ fontWeight: '600'
620
918
  },
621
919
  loadingRow: {
622
920
  padding: 40,
@@ -629,71 +927,50 @@ const styles = _reactNative.StyleSheet.create({
629
927
  alignItems: 'center'
630
928
  },
631
929
  emptyText: {
632
- fontSize: 14,
633
- color: '#999'
930
+ fontSize: 14
634
931
  },
635
932
  paginationContainer: {
636
933
  padding: 16,
637
- borderTopWidth: 1,
638
- borderTopColor: '#E0E0E0',
639
- backgroundColor: '#FAFAFA'
934
+ borderTopWidth: 1
640
935
  },
641
- // 滚动阴影
642
- shadowLeft: {
936
+ // Row Stats 容器
937
+ rowStatsContainer: {
938
+ position: 'relative'
939
+ },
940
+ // Column Stats 容器
941
+ columnStatsContainer: {
942
+ // 顶部边框由 themedStyles.columnStatsDivider 提供
943
+ },
944
+ // 垂直阴影条
945
+ shadowBar: {
643
946
  position: 'absolute',
947
+ zIndex: 10,
948
+ width: 1,
949
+ backgroundColor: 'transparent'
950
+ },
951
+ shadowLeft: {
644
952
  left: 0,
645
953
  top: 0,
646
- bottom: 0,
647
- zIndex: 10,
648
- shadowOffset: {
649
- width: 2,
650
- height: 0
651
- },
652
- shadowOpacity: 0.15,
653
- shadowRadius: 4,
654
- elevation: 4
954
+ bottom: 0
655
955
  },
656
956
  shadowRight: {
657
- position: 'absolute',
658
- right: 0,
659
957
  top: 0,
660
- bottom: 0,
661
- zIndex: 10,
662
- shadowOffset: {
663
- width: -2,
664
- height: 0
665
- },
666
- shadowOpacity: 0.15,
667
- shadowRadius: 4,
668
- elevation: 4
958
+ bottom: 0
669
959
  },
670
- shadowTop: {
960
+ // 水平阴影条
961
+ shadowBarHorizontal: {
671
962
  position: 'absolute',
963
+ zIndex: 10,
964
+ height: 1,
672
965
  left: 0,
673
966
  right: 0,
674
- top: 0,
675
- zIndex: 10,
676
- shadowOffset: {
677
- width: 0,
678
- height: 2
679
- },
680
- shadowOpacity: 0.15,
681
- shadowRadius: 4,
682
- elevation: 4
967
+ backgroundColor: 'transparent'
968
+ },
969
+ shadowTop: {
970
+ top: 56 // header height
683
971
  },
684
972
  shadowBottom: {
685
- position: 'absolute',
686
- left: 0,
687
- right: 0,
688
- bottom: 0,
689
- zIndex: 10,
690
- shadowOffset: {
691
- width: 0,
692
- height: -2
693
- },
694
- shadowOpacity: 0.15,
695
- shadowRadius: 4,
696
- elevation: 4
973
+ // bottom 由动态样式设置
697
974
  }
698
975
  });
699
976
  //# sourceMappingURL=StatisticsTable.js.map