@beppla/tapas-ui 1.4.2 → 1.4.4
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/commonjs/StatisticsTable/PLACEHOLDER_FEATURE.md +262 -0
- package/commonjs/StatisticsTable/README.md +79 -0
- package/commonjs/StatisticsTable/StatisticsTable.js +74 -16
- package/commonjs/StatisticsTable/StatisticsTable.js.map +1 -1
- package/module/StatisticsTable/PLACEHOLDER_FEATURE.md +262 -0
- package/module/StatisticsTable/README.md +79 -0
- package/module/StatisticsTable/StatisticsTable.js +74 -16
- package/module/StatisticsTable/StatisticsTable.js.map +1 -1
- package/package.json +1 -1
- package/typescript/StatisticsTable/StatisticsTable.d.ts +6 -1
- package/typescript/StatisticsTable/StatisticsTable.d.ts.map +1 -1
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# StatisticsTable 占位功能说明
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
为 StatisticsTable 组件添加了占位行和列的功能,当数据不足以撑满外层容器时,会自动添加占位行和列来填充空间。
|
|
6
|
+
|
|
7
|
+
## 新增属性
|
|
8
|
+
|
|
9
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
10
|
+
|--------|------|--------|------|
|
|
11
|
+
| `enablePlaceholder` | `boolean` | `false` | 是否启用占位功能 |
|
|
12
|
+
| `minRows` | `number` | `0` | 最小行数(如果实际数据行数少于此值,会添加占位行) |
|
|
13
|
+
| `minColumns` | `number` | `0` | 最小列数(如果实际数据列数少于此值,会添加占位列) |
|
|
14
|
+
| `placeholderRowHeight` | `number` | `rowHeight` (60) | 占位行的高度 |
|
|
15
|
+
| `placeholderColumnWidth` | `number` | `150` | 占位列的宽度 |
|
|
16
|
+
|
|
17
|
+
## 工作原理
|
|
18
|
+
|
|
19
|
+
### 占位行
|
|
20
|
+
- 当 `enablePlaceholder={true}` 且 `rows.length < minRows` 时
|
|
21
|
+
- 会在实际数据行之后添加 `(minRows - rows.length)` 个占位行
|
|
22
|
+
- 占位行的 key 格式为 `_placeholder_row_0`, `_placeholder_row_1` 等
|
|
23
|
+
- 每个占位行使用 `placeholderRowHeight` 高度(未设置时使用 `rowHeight`,默认 60px)
|
|
24
|
+
- 占位行内容为空,保持视觉整洁
|
|
25
|
+
|
|
26
|
+
### 占位列
|
|
27
|
+
- 当 `enablePlaceholder={true}` 且实际数据列数 `< minColumns` 时
|
|
28
|
+
- 会在实际数据列之后添加 `(minColumns - currentDataColumns)` 个占位列
|
|
29
|
+
- 占位列的 key 格式为 `_placeholder_col_0`, `_placeholder_col_1` 等
|
|
30
|
+
- 每个占位列使用 `placeholderColumnWidth` 宽度(默认 150px)
|
|
31
|
+
- 占位列内容为空,保持视觉整洁
|
|
32
|
+
|
|
33
|
+
### 灵活性
|
|
34
|
+
通过 `placeholderRowHeight` 和 `placeholderColumnWidth` 参数,可以精确控制占位元素的尺寸:
|
|
35
|
+
- **小尺寸占位**:适用于需要更紧凑的表格布局
|
|
36
|
+
- **大尺寸占位**:适用于需要更宽松的表格布局
|
|
37
|
+
- **精确控制**:可以计算容器大小并设置精确的占位尺寸来完美填充
|
|
38
|
+
|
|
39
|
+
### 与统计功能的兼容性
|
|
40
|
+
- 占位行和列完全兼容 Row Stats 和 Column Stats 功能
|
|
41
|
+
- 占位单元格会被正确识别并跳过内容渲染
|
|
42
|
+
- 统计计算不会包含占位行列的数据
|
|
43
|
+
|
|
44
|
+
## 使用示例
|
|
45
|
+
|
|
46
|
+
### 基础用法
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<StatisticsTable
|
|
50
|
+
rows={rows} // 假设只有 2 行数据
|
|
51
|
+
columns={columns} // 假设只有 2 列数据
|
|
52
|
+
cells={cells}
|
|
53
|
+
enablePlaceholder={true}
|
|
54
|
+
minRows={10} // 确保至少显示 10 行
|
|
55
|
+
minColumns={5} // 确保至少显示 5 列
|
|
56
|
+
maxHeight={600} // 设置容器高度
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 仅填充行
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<StatisticsTable
|
|
64
|
+
rows={rows}
|
|
65
|
+
columns={columns}
|
|
66
|
+
cells={cells}
|
|
67
|
+
enablePlaceholder={true}
|
|
68
|
+
minRows={10}
|
|
69
|
+
maxHeight={600}
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 仅填充列
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<StatisticsTable
|
|
77
|
+
rows={rows}
|
|
78
|
+
columns={columns}
|
|
79
|
+
cells={cells}
|
|
80
|
+
enablePlaceholder={true}
|
|
81
|
+
minColumns={5}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 自定义占位尺寸
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<StatisticsTable
|
|
89
|
+
rows={rows}
|
|
90
|
+
columns={columns}
|
|
91
|
+
cells={cells}
|
|
92
|
+
enablePlaceholder={true}
|
|
93
|
+
minRows={10}
|
|
94
|
+
minColumns={5}
|
|
95
|
+
placeholderRowHeight={80} // 占位行高度 80px
|
|
96
|
+
placeholderColumnWidth={200} // 占位列宽度 200px
|
|
97
|
+
maxHeight={600}
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 配合统计功能
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<StatisticsTable
|
|
105
|
+
rows={rows}
|
|
106
|
+
columns={columns}
|
|
107
|
+
cells={cells}
|
|
108
|
+
enablePlaceholder={true}
|
|
109
|
+
minRows={10}
|
|
110
|
+
minColumns={5}
|
|
111
|
+
placeholderRowHeight={70}
|
|
112
|
+
showRowStats={true} // 可以同时使用行统计
|
|
113
|
+
maxHeight={600}
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 精确填充容器
|
|
118
|
+
|
|
119
|
+
如果你知道容器的确切尺寸,可以计算需要的占位数量和尺寸:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const containerHeight = 600;
|
|
123
|
+
const headerHeight = 56;
|
|
124
|
+
const columnStatsHeight = showColumnStats ? 120 : 0;
|
|
125
|
+
const availableHeight = containerHeight - headerHeight - columnStatsHeight;
|
|
126
|
+
|
|
127
|
+
const dataRowsCount = 3;
|
|
128
|
+
const desiredTotalRows = Math.floor(availableHeight / 60);
|
|
129
|
+
|
|
130
|
+
<StatisticsTable
|
|
131
|
+
rows={rows} // 3 rows
|
|
132
|
+
columns={columns}
|
|
133
|
+
cells={cells}
|
|
134
|
+
enablePlaceholder={true}
|
|
135
|
+
minRows={desiredTotalRows}
|
|
136
|
+
placeholderRowHeight={60}
|
|
137
|
+
maxHeight={containerHeight}
|
|
138
|
+
/>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 使用场景
|
|
142
|
+
|
|
143
|
+
1. **固定大小表格**:需要表格保持固定的视觉大小,不随数据量变化
|
|
144
|
+
2. **数据加载中**:在数据还在加载但想保持布局稳定时
|
|
145
|
+
3. **预留空间**:为可能增加的数据预留视觉空间
|
|
146
|
+
4. **对齐要求**:多个表格需要保持相同的尺寸对齐
|
|
147
|
+
|
|
148
|
+
## 实现细节
|
|
149
|
+
|
|
150
|
+
### 内部实现
|
|
151
|
+
|
|
152
|
+
组件内部通过 `useMemo` 生成 `displayRows` 和 `displayColumns`:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const { displayRows, displayColumns } = useMemo(() => {
|
|
156
|
+
if (!enablePlaceholder) {
|
|
157
|
+
return { displayRows: rows, displayColumns: columns };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 处理列:如果当前列数少于最小列数,添加占位列
|
|
161
|
+
let finalColumns = [...columns];
|
|
162
|
+
const currentDataColumns = columns.filter(col => !col.isAction).length;
|
|
163
|
+
|
|
164
|
+
if (minColumns > 0 && currentDataColumns < minColumns) {
|
|
165
|
+
const missingColumns = minColumns - currentDataColumns;
|
|
166
|
+
const placeholderColumns = Array.from({ length: missingColumns }, (_, i) => ({
|
|
167
|
+
key: `_placeholder_col_${i}`,
|
|
168
|
+
title: '',
|
|
169
|
+
width: 150, // 使用默认宽度
|
|
170
|
+
}));
|
|
171
|
+
finalColumns = [...columns, ...placeholderColumns];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 处理行:如果当前行数少于最小行数,添加占位行
|
|
175
|
+
let finalRows = [...rows];
|
|
176
|
+
if (minRows > 0 && rows.length < minRows) {
|
|
177
|
+
const missingRows = minRows - rows.length;
|
|
178
|
+
const placeholderRows = Array.from({ length: missingRows }, (_, i) => ({
|
|
179
|
+
key: `_placeholder_row_${i}`,
|
|
180
|
+
label: '',
|
|
181
|
+
height: rowHeight, // 使用默认行高
|
|
182
|
+
}));
|
|
183
|
+
finalRows = [...rows, ...placeholderRows];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { displayRows: finalRows, displayColumns: finalColumns };
|
|
187
|
+
}, [rows, columns, enablePlaceholder, minRows, minColumns, rowHeight]);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 渲染处理
|
|
191
|
+
|
|
192
|
+
在渲染时,组件会检测占位行列并跳过内容渲染:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const isPlaceholder = row.key.startsWith('_placeholder_row_');
|
|
196
|
+
const isColPlaceholder = column.key.startsWith('_placeholder_col_');
|
|
197
|
+
|
|
198
|
+
// 占位单元格不渲染内容
|
|
199
|
+
<View style={[styles.cell, { width: colWidth }]}>
|
|
200
|
+
{!isColPlaceholder && !isPlaceholder && renderCellContent(column, cell, row.data)}
|
|
201
|
+
</View>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## 测试覆盖
|
|
205
|
+
|
|
206
|
+
新增了以下测试用例:
|
|
207
|
+
|
|
208
|
+
1. ✅ 禁用占位功能时不添加占位符
|
|
209
|
+
2. ✅ 数据列少于 minColumns 时添加占位列
|
|
210
|
+
3. ✅ 数据行少于 minRows 时添加占位行
|
|
211
|
+
4. ✅ 同时添加占位行和列
|
|
212
|
+
5. ✅ 数据满足最小要求时不添加占位符
|
|
213
|
+
6. ✅ 与 Row Stats 配合使用
|
|
214
|
+
7. ✅ 与 Column Stats 配合使用
|
|
215
|
+
8. ✅ 使用自定义占位行高度
|
|
216
|
+
9. ✅ 使用自定义占位列宽度
|
|
217
|
+
10. ✅ 同时使用自定义占位行高度和列宽度
|
|
218
|
+
|
|
219
|
+
## Storybook 示例
|
|
220
|
+
|
|
221
|
+
### 占位功能示例
|
|
222
|
+
|
|
223
|
+
添加了 6 个新的 Story 来展示占位功能:
|
|
224
|
+
|
|
225
|
+
1. `WithPlaceholderRows` - 仅占位行
|
|
226
|
+
2. `WithPlaceholderColumns` - 仅占位列
|
|
227
|
+
3. `WithPlaceholderRowsAndColumns` - 同时占位行列
|
|
228
|
+
4. `WithPlaceholderAndRowStats` - 占位行配合行统计
|
|
229
|
+
5. `WithPlaceholderAndColumnStats` - 占位行配合列统计
|
|
230
|
+
6. `WithCustomPlaceholderSize` - 自定义占位行列尺寸(80px行高 × 200px列宽)
|
|
231
|
+
|
|
232
|
+
### 滚动阴影示例
|
|
233
|
+
|
|
234
|
+
添加了 4 个新的 Story 来展示滚动阴影功能:
|
|
235
|
+
|
|
236
|
+
1. `WithScrollShadow` - 展示滚动阴影效果(上下左右四个方向)
|
|
237
|
+
2. `WithoutScrollShadow` - 对比:不使用滚动阴影(使用滚动条)
|
|
238
|
+
3. `ScrollShadowWithRowStats` - 滚动阴影配合行统计
|
|
239
|
+
4. `ScrollShadowWithColumnStats` - 滚动阴影配合列统计
|
|
240
|
+
|
|
241
|
+
## 注意事项
|
|
242
|
+
|
|
243
|
+
1. **性能考虑**:占位功能使用 `flex: 1`,不会增加额外的行列,性能影响极小
|
|
244
|
+
2. **视觉一致性**:占位单元格没有边框,保持视觉上的整洁
|
|
245
|
+
3. **数据完整性**:占位行列不会影响统计计算和数据导出
|
|
246
|
+
4. **互不干扰**:占位功能与现有所有功能完全兼容(统计、分页、虚拟化等)
|
|
247
|
+
|
|
248
|
+
## 更新日志
|
|
249
|
+
|
|
250
|
+
- ✅ 在 `StatisticsTable.tsx` 中添加占位行列逻辑
|
|
251
|
+
- ✅ 更新 `StatisticsTableProps` 接口,添加新属性
|
|
252
|
+
- `enablePlaceholder` - 启用占位功能
|
|
253
|
+
- `minRows` - 最小行数
|
|
254
|
+
- `minColumns` - 最小列数
|
|
255
|
+
- `placeholderRowHeight` - 自定义占位行高度
|
|
256
|
+
- `placeholderColumnWidth` - 自定义占位列宽度
|
|
257
|
+
- ✅ 更新 README.md,添加功能说明和使用示例
|
|
258
|
+
- ✅ 添加完整的测试用例(10个测试)
|
|
259
|
+
- ✅ 添加 Storybook 示例(6个 Stories)
|
|
260
|
+
- ✅ 添加滚动阴影 Storybook 示例(4个 Stories)
|
|
261
|
+
- ✅ 所有代码通过 Linter 检查
|
|
262
|
+
|
|
@@ -11,6 +11,7 @@ A versatile table component for displaying matrix data with optional row and col
|
|
|
11
11
|
- ✅ Pagination support
|
|
12
12
|
- ✅ Loading state
|
|
13
13
|
- ✅ Empty state
|
|
14
|
+
- ✅ Placeholder rows and columns (fill container when data is insufficient)
|
|
14
15
|
- ✅ Customizable styling
|
|
15
16
|
- ✅ TypeScript support
|
|
16
17
|
- ✅ Cross-platform (Web & Mobile)
|
|
@@ -104,6 +105,77 @@ const cells = [
|
|
|
104
105
|
/>
|
|
105
106
|
```
|
|
106
107
|
|
|
108
|
+
## With Placeholder Rows and Columns
|
|
109
|
+
|
|
110
|
+
When you have limited data but want to ensure a minimum table size, use placeholder rows and columns:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<StatisticsTable
|
|
114
|
+
rows={rows} // e.g. only 3 rows of data
|
|
115
|
+
columns={columns} // e.g. only 2 columns of data
|
|
116
|
+
cells={cells}
|
|
117
|
+
enablePlaceholder={true}
|
|
118
|
+
minRows={10} // Table will have 10 rows (3 data + 7 empty placeholders)
|
|
119
|
+
minColumns={5} // Table will have 5 columns (2 data + 3 empty placeholders)
|
|
120
|
+
maxHeight={600} // Set container height
|
|
121
|
+
/>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Customizing Placeholder Size
|
|
125
|
+
|
|
126
|
+
You can control the size of placeholder rows and columns:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
<StatisticsTable
|
|
130
|
+
rows={rows}
|
|
131
|
+
columns={columns}
|
|
132
|
+
cells={cells}
|
|
133
|
+
enablePlaceholder={true}
|
|
134
|
+
minRows={10}
|
|
135
|
+
minColumns={5}
|
|
136
|
+
placeholderRowHeight={80} // Each placeholder row will be 80px tall
|
|
137
|
+
placeholderColumnWidth={200} // Each placeholder column will be 200px wide
|
|
138
|
+
maxHeight={600}
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**How it works:**
|
|
143
|
+
- If `rows.length < minRows`: Adds `(minRows - rows.length)` empty placeholder rows
|
|
144
|
+
- If columns count `< minColumns`: Adds `(minColumns - columns.count)` empty placeholder columns
|
|
145
|
+
- Placeholder cells are visually empty but maintain table structure
|
|
146
|
+
- Each placeholder row uses `placeholderRowHeight` (defaults to `rowHeight`, which is 60px)
|
|
147
|
+
- Each placeholder column uses `placeholderColumnWidth` (defaults to 150px)
|
|
148
|
+
|
|
149
|
+
**Use cases:**
|
|
150
|
+
- Consistent table size regardless of data volume
|
|
151
|
+
- Visual stability during data loading
|
|
152
|
+
- Aligning multiple tables with different data volumes
|
|
153
|
+
- Filling empty space with placeholder cells
|
|
154
|
+
- Controlling exact table dimensions by setting custom placeholder sizes
|
|
155
|
+
|
|
156
|
+
## With Scroll Shadows
|
|
157
|
+
|
|
158
|
+
The table supports automatic scroll shadows on all four sides to indicate scrollable content:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<StatisticsTable
|
|
162
|
+
rows={rows}
|
|
163
|
+
columns={columns}
|
|
164
|
+
cells={cells}
|
|
165
|
+
enableScrollShadow={true} // Enable scroll shadows (default: true)
|
|
166
|
+
showScrollIndicator={false} // Hide scrollbars (default: false)
|
|
167
|
+
maxHeight={400}
|
|
168
|
+
/>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Scroll shadow features:
|
|
172
|
+
- **Left shadow**: Appears when content is scrolled horizontally to the right
|
|
173
|
+
- **Right shadow**: Appears when there's more content to scroll right (adjusts for row stats)
|
|
174
|
+
- **Top shadow**: Appears when content is scrolled vertically downward
|
|
175
|
+
- **Bottom shadow**: Appears when there's more content to scroll down (adjusts for column stats)
|
|
176
|
+
- Shadows automatically show/hide based on scroll position
|
|
177
|
+
- Works seamlessly with row statistics and column statistics
|
|
178
|
+
|
|
107
179
|
## Props
|
|
108
180
|
|
|
109
181
|
| Prop | Type | Default | Description |
|
|
@@ -118,6 +190,13 @@ const cells = [
|
|
|
118
190
|
| `emptyText` | `string` | `'No data'` | Text for empty state |
|
|
119
191
|
| `maxHeight` | `number` | `500` | Maximum table body height |
|
|
120
192
|
| `rowLabelWidth` | `number` | `150` | Width of first column (row labels) |
|
|
193
|
+
| `enablePlaceholder` | `boolean` | `false` | Enable placeholder rows/columns to fill container |
|
|
194
|
+
| `minRows` | `number` | `0` | Minimum number of rows (adds placeholders if needed) |
|
|
195
|
+
| `minColumns` | `number` | `0` | Minimum number of columns (adds placeholders if needed) |
|
|
196
|
+
| `placeholderRowHeight` | `number` | `rowHeight` (60) | Height of each placeholder row |
|
|
197
|
+
| `placeholderColumnWidth` | `number` | `150` | Width of each placeholder column |
|
|
198
|
+
| `enableScrollShadow` | `boolean` | `true` | Enable scroll shadows on all four sides |
|
|
199
|
+
| `showScrollIndicator` | `boolean` | `false` | Show scroll indicators/scrollbars |
|
|
121
200
|
| `style` | `ViewStyle` | - | Custom container styles |
|
|
122
201
|
|
|
123
202
|
## Type Definitions
|
|
@@ -126,7 +126,12 @@ function StatisticsTable({
|
|
|
126
126
|
rowStatsHeaderStyle,
|
|
127
127
|
columnStatsLabelStyle,
|
|
128
128
|
enableStatsAnimation = true,
|
|
129
|
-
statsAnimationDuration = 300
|
|
129
|
+
statsAnimationDuration = 300,
|
|
130
|
+
enablePlaceholder = false,
|
|
131
|
+
minRows = 0,
|
|
132
|
+
minColumns = 0,
|
|
133
|
+
placeholderRowHeight,
|
|
134
|
+
placeholderColumnWidth = 150
|
|
130
135
|
}) {
|
|
131
136
|
const {
|
|
132
137
|
theme
|
|
@@ -324,6 +329,54 @@ function StatisticsTable({
|
|
|
324
329
|
};
|
|
325
330
|
});
|
|
326
331
|
}, [rows, columns, matrix, showColumnStats]);
|
|
332
|
+
|
|
333
|
+
// 生成占位行和列
|
|
334
|
+
const {
|
|
335
|
+
displayRows,
|
|
336
|
+
displayColumns
|
|
337
|
+
} = (0, _react.useMemo)(() => {
|
|
338
|
+
if (!enablePlaceholder) {
|
|
339
|
+
return {
|
|
340
|
+
displayRows: rows,
|
|
341
|
+
displayColumns: columns
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 处理列:如果当前列数少于最小列数,添加占位列
|
|
346
|
+
let finalColumns = [...columns];
|
|
347
|
+
const currentDataColumns = columns.filter(col => !col.isAction).length;
|
|
348
|
+
if (minColumns > 0 && currentDataColumns < minColumns) {
|
|
349
|
+
const missingColumns = minColumns - currentDataColumns;
|
|
350
|
+
// 添加占位列来填充
|
|
351
|
+
const placeholderColumns = Array.from({
|
|
352
|
+
length: missingColumns
|
|
353
|
+
}, (_, i) => ({
|
|
354
|
+
key: `_placeholder_col_${i}`,
|
|
355
|
+
title: '',
|
|
356
|
+
width: placeholderColumnWidth // 使用自定义占位列宽度
|
|
357
|
+
}));
|
|
358
|
+
finalColumns = [...columns, ...placeholderColumns];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 处理行:如果当前行数少于最小行数,添加占位行
|
|
362
|
+
let finalRows = [...rows];
|
|
363
|
+
if (minRows > 0 && rows.length < minRows) {
|
|
364
|
+
const missingRows = minRows - rows.length;
|
|
365
|
+
// 添加占位行来填充
|
|
366
|
+
const placeholderRows = Array.from({
|
|
367
|
+
length: missingRows
|
|
368
|
+
}, (_, i) => ({
|
|
369
|
+
key: `_placeholder_row_${i}`,
|
|
370
|
+
label: '',
|
|
371
|
+
height: placeholderRowHeight || rowHeight // 使用自定义占位行高度,未设置则使用默认行高
|
|
372
|
+
}));
|
|
373
|
+
finalRows = [...rows, ...placeholderRows];
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
displayRows: finalRows,
|
|
377
|
+
displayColumns: finalColumns
|
|
378
|
+
};
|
|
379
|
+
}, [rows, columns, enablePlaceholder, minRows, minColumns, rowHeight, placeholderRowHeight, placeholderColumnWidth]);
|
|
327
380
|
const getColWidth = (0, _react.useCallback)(column => {
|
|
328
381
|
if (getColumnWidth) return getColumnWidth(column.key, column);
|
|
329
382
|
return column.width || 150;
|
|
@@ -489,7 +542,7 @@ function StatisticsTable({
|
|
|
489
542
|
})
|
|
490
543
|
});
|
|
491
544
|
}
|
|
492
|
-
const visibleRows = enableVirtualization ?
|
|
545
|
+
const visibleRows = enableVirtualization ? displayRows.slice(0, Math.ceil(maxHeight / virtualRowHeight)) : displayRows;
|
|
493
546
|
|
|
494
547
|
// Web 阴影样式
|
|
495
548
|
const webShadowStyle = _reactNative.Platform.OS === 'web' ? {
|
|
@@ -551,7 +604,7 @@ function StatisticsTable({
|
|
|
551
604
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
552
605
|
style: [styles.headerText, themedStyles.headerText]
|
|
553
606
|
})
|
|
554
|
-
}),
|
|
607
|
+
}), displayColumns.map(column => {
|
|
555
608
|
const colWidth = getColWidth(column);
|
|
556
609
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
557
610
|
style: [styles.headerCell, themedStyles.headerCell, {
|
|
@@ -575,13 +628,14 @@ function StatisticsTable({
|
|
|
575
628
|
const currentRowHeight = getRowHeightValue(row);
|
|
576
629
|
const isClickable = !!onRowPress;
|
|
577
630
|
const isRowHovered = hoveredCell?.rowKey === row.key;
|
|
631
|
+
const isPlaceholder = row.key.startsWith('_placeholder_row_');
|
|
578
632
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
579
633
|
style: [styles.row, themedStyles.row, {
|
|
580
634
|
minHeight: currentRowHeight
|
|
581
|
-
}, isRowHovered && themedStyles.rowHovered],
|
|
582
|
-
onPress: isClickable ? () => onRowPress(row.key, row.data) : undefined,
|
|
583
|
-
activeOpacity: isClickable ? 0.7 : 1,
|
|
584
|
-
disabled: !isClickable,
|
|
635
|
+
}, isRowHovered && !isPlaceholder && themedStyles.rowHovered],
|
|
636
|
+
onPress: isClickable && !isPlaceholder ? () => onRowPress(row.key, row.data) : undefined,
|
|
637
|
+
activeOpacity: isClickable && !isPlaceholder ? 0.7 : 1,
|
|
638
|
+
disabled: !isClickable || isPlaceholder,
|
|
585
639
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
586
640
|
style: [styles.cell, themedStyles.cell, {
|
|
587
641
|
width: rowLabelWidth
|
|
@@ -590,7 +644,7 @@ function StatisticsTable({
|
|
|
590
644
|
style: [styles.rowLabel, themedStyles.rowLabel],
|
|
591
645
|
children: row.label
|
|
592
646
|
})
|
|
593
|
-
}),
|
|
647
|
+
}), displayColumns.map(column => {
|
|
594
648
|
const cell = matrix[row.key]?.[column.key] || {
|
|
595
649
|
quantity: 0,
|
|
596
650
|
amount: 0,
|
|
@@ -600,14 +654,16 @@ function StatisticsTable({
|
|
|
600
654
|
const colWidth = getColWidth(column);
|
|
601
655
|
const isHovered = hoveredCell?.rowKey === row.key && hoveredCell?.columnKey === column.key;
|
|
602
656
|
const tooltipContent = isHovered ? getCellTooltipContent(row.key, column.key, cell, column) : null;
|
|
657
|
+
const isColPlaceholder = column.key.startsWith('_placeholder_col_');
|
|
658
|
+
const isRowPlaceholder = row.key.startsWith('_placeholder_row_');
|
|
603
659
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Hoverable.default, {
|
|
604
|
-
onHoverIn: () => handleCellHover(row.key, column.key, true),
|
|
605
|
-
onHoverOut: () => handleCellHover(row.key, column.key, false),
|
|
660
|
+
onHoverIn: () => !isRowPlaceholder && !isColPlaceholder && handleCellHover(row.key, column.key, true),
|
|
661
|
+
onHoverOut: () => !isRowPlaceholder && !isColPlaceholder && handleCellHover(row.key, column.key, false),
|
|
606
662
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
607
663
|
style: [styles.cell, themedStyles.cell, {
|
|
608
664
|
width: colWidth
|
|
609
|
-
}, isHovered && themedStyles.cellHovered],
|
|
610
|
-
children: [renderCellContent(column, cell, row.data), /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
|
|
665
|
+
}, isHovered && !isRowPlaceholder && !isColPlaceholder && themedStyles.cellHovered],
|
|
666
|
+
children: [!isColPlaceholder && !isRowPlaceholder && renderCellContent(column, cell, row.data), !isRowPlaceholder && !isColPlaceholder && /*#__PURE__*/(0, _jsxRuntime.jsx)(CellTooltip, {
|
|
611
667
|
visible: isHovered,
|
|
612
668
|
content: tooltipContent,
|
|
613
669
|
style: {
|
|
@@ -641,9 +697,10 @@ function StatisticsTable({
|
|
|
641
697
|
style: [styles.statsLabel, themedStyles.statsLabel],
|
|
642
698
|
children: columnStatsLabels.sum
|
|
643
699
|
})
|
|
644
|
-
}),
|
|
700
|
+
}), displayColumns.map(column => {
|
|
645
701
|
const colWidth = getColWidth(column);
|
|
646
|
-
|
|
702
|
+
const isPlaceholder = column.key.startsWith('_placeholder_col_');
|
|
703
|
+
if (column.isAction || isPlaceholder) {
|
|
647
704
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
648
705
|
style: [styles.cell, {
|
|
649
706
|
width: colWidth
|
|
@@ -691,9 +748,10 @@ function StatisticsTable({
|
|
|
691
748
|
style: [styles.statsLabel, themedStyles.statsLabel],
|
|
692
749
|
children: columnStatsLabels.mean
|
|
693
750
|
})
|
|
694
|
-
}),
|
|
751
|
+
}), displayColumns.map(column => {
|
|
695
752
|
const colWidth = getColWidth(column);
|
|
696
|
-
|
|
753
|
+
const isPlaceholder = column.key.startsWith('_placeholder_col_');
|
|
754
|
+
if (column.isAction || isPlaceholder) {
|
|
697
755
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
698
756
|
style: [styles.cell, {
|
|
699
757
|
width: colWidth
|