@beppla/tapas-ui 1.0.54 → 1.0.56
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/components/DataCell/DataCell.tsx +186 -14
- package/components/Grid/StaticFixedSizeGrid.tsx +21 -10
- package/package/commonjs/DataCell/DataCell.js +172 -14
- package/package/commonjs/DataCell/DataCell.js.map +1 -1
- package/package/commonjs/Grid/StaticFixedSizeGrid.js +23 -11
- package/package/commonjs/Grid/StaticFixedSizeGrid.js.map +1 -1
- package/package/module/DataCell/DataCell.js +172 -15
- package/package/module/DataCell/DataCell.js.map +1 -1
- package/package/module/Grid/StaticFixedSizeGrid.js +23 -11
- package/package/module/Grid/StaticFixedSizeGrid.js.map +1 -1
- package/package/typescript/DataCell/DataCell.d.ts +10 -1
- package/package/typescript/DataCell/DataCell.d.ts.map +1 -1
- package/package/typescript/Grid/StaticFixedSizeGrid.d.ts.map +1 -1
- package/package.json +1 -1
- package/package/CHANGELOG.md +0 -351
- package/package/README.md +0 -103
- package/package/assets/assets/adaptive-icon.png +0 -0
- package/package/assets/assets/favicon.png +0 -0
- package/package/assets/assets/fonts/customfont.ttf +0 -0
- package/package/assets/assets/icon.png +0 -0
- package/package/assets/assets/logo.png +0 -0
- package/package/assets/assets/splash.png +0 -0
- package/package/assets/assets/svg/customfont.svg +0 -332
- package/package/assets/assets/svg/logo.svg +0 -9
- package/package/assets/assets/tapas/favicon.png +0 -0
- package/package/assets/assets/tapas/logo.png +0 -0
- package/package/assets/assets/tapas/logo_no_word.png +0 -0
- package/package/assets/assets/tapas/logo_transparent_white.png +0 -0
- package/package/init.js +0 -179
- package/package/package.json +0 -84
|
@@ -1,9 +1,50 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useState } from "react";
|
|
1
|
+
import React, { useCallback, useMemo, useState, useEffect } from "react";
|
|
2
2
|
import { View, Text, Pressable, ViewProps, TextStyle, StyleProp } from "react-native";
|
|
3
3
|
import { withTheme, makeStyles } from "@rneui/themed";
|
|
4
4
|
import TapasIcon from "../Icons/TapasIcon";
|
|
5
5
|
// import { TapasDropdown } from "../Dropdown/Dropdown";
|
|
6
6
|
|
|
7
|
+
// 全局下拉菜单状态管理
|
|
8
|
+
const globalDropdownState: {
|
|
9
|
+
activeColumn: number | null;
|
|
10
|
+
activeRow: number | null;
|
|
11
|
+
closeAll: () => void;
|
|
12
|
+
onDropdownStateChange?: (columnIndex: number | null, rowIndex: number | null) => void;
|
|
13
|
+
} = {
|
|
14
|
+
activeColumn: null,
|
|
15
|
+
activeRow: null,
|
|
16
|
+
closeAll: () => {},
|
|
17
|
+
onDropdownStateChange: undefined,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// 注册和注销下拉菜单的函数
|
|
21
|
+
const registerDropdown = (columnIndex: number, rowIndex: number, closeCallback: () => void) => {
|
|
22
|
+
globalDropdownState.activeColumn = columnIndex;
|
|
23
|
+
globalDropdownState.activeRow = rowIndex;
|
|
24
|
+
globalDropdownState.closeAll = closeCallback;
|
|
25
|
+
// 通知状态变化
|
|
26
|
+
globalDropdownState.onDropdownStateChange?.(columnIndex, rowIndex);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const unregisterDropdown = () => {
|
|
30
|
+
globalDropdownState.activeColumn = null;
|
|
31
|
+
globalDropdownState.activeRow = null;
|
|
32
|
+
globalDropdownState.closeAll = () => {};
|
|
33
|
+
// 通知状态变化
|
|
34
|
+
globalDropdownState.onDropdownStateChange?.(null, null);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const closeAllDropdowns = () => {
|
|
38
|
+
if (globalDropdownState.closeAll) {
|
|
39
|
+
globalDropdownState.closeAll();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// 设置下拉菜单状态变化监听器
|
|
44
|
+
const setDropdownStateChangeListener = (callback: (columnIndex: number | null, rowIndex: number | null) => void) => {
|
|
45
|
+
globalDropdownState.onDropdownStateChange = callback;
|
|
46
|
+
};
|
|
47
|
+
|
|
7
48
|
export type DataType =
|
|
8
49
|
| "text"
|
|
9
50
|
| "number"
|
|
@@ -90,6 +131,14 @@ export interface TapasDataCellProps extends ViewProps {
|
|
|
90
131
|
minHeight?: number;
|
|
91
132
|
/** 是否为空白单元格 */
|
|
92
133
|
isBlank?: boolean;
|
|
134
|
+
/** 下拉菜单状态变化回调 */
|
|
135
|
+
onDropdownStateChange?: (isOpen: boolean) => void;
|
|
136
|
+
/** Grid 相关信息,用于计算下拉菜单位置 */
|
|
137
|
+
gridInfo?: {
|
|
138
|
+
totalRows?: number;
|
|
139
|
+
rowHeight?: number;
|
|
140
|
+
gridHeight?: number;
|
|
141
|
+
};
|
|
93
142
|
}
|
|
94
143
|
|
|
95
144
|
const TapasDataCell: React.FC<TapasDataCellProps> = ({
|
|
@@ -122,6 +171,8 @@ const TapasDataCell: React.FC<TapasDataCellProps> = ({
|
|
|
122
171
|
disabled = false,
|
|
123
172
|
minHeight = 40,
|
|
124
173
|
isBlank = false,
|
|
174
|
+
onDropdownStateChange,
|
|
175
|
+
gridInfo,
|
|
125
176
|
style,
|
|
126
177
|
...otherProps
|
|
127
178
|
}) => {
|
|
@@ -224,6 +275,111 @@ const TapasDataCell: React.FC<TapasDataCellProps> = ({
|
|
|
224
275
|
// 下拉菜单状态
|
|
225
276
|
const [dropdownVisible, setDropdownVisible] = useState(false);
|
|
226
277
|
const [dropdownPosition, setDropdownPosition] = useState({ top: 32, right: 0 });
|
|
278
|
+
|
|
279
|
+
// 计算下拉菜单的最佳位置
|
|
280
|
+
const calculateDropdownPosition = useCallback(() => {
|
|
281
|
+
// 更准确地计算下拉菜单的实际高度
|
|
282
|
+
const itemHeight = 32; // 每个选项的高度
|
|
283
|
+
const borderWidth = 2; // 边框宽度
|
|
284
|
+
const padding = 8; // 内边距
|
|
285
|
+
const shadowHeight = 4; // 阴影高度
|
|
286
|
+
|
|
287
|
+
// 考虑文本长度对高度的影响
|
|
288
|
+
const maxTextLength = Math.max(...actions.map(action => action.label.length));
|
|
289
|
+
const textHeightAdjustment = maxTextLength > 10 ? 4 : 0; // 长文本可能需要更多高度
|
|
290
|
+
|
|
291
|
+
// 计算下拉菜单的宽度(考虑文本长度)
|
|
292
|
+
// const minWidth = 120; // 最小宽度
|
|
293
|
+
// const textWidth = maxTextLength * 8; // 估算文本宽度
|
|
294
|
+
// const _actualWidth = Math.max(minWidth, textWidth + 24); // 24px 为内边距和图标空间
|
|
295
|
+
|
|
296
|
+
const actualHeight = actions.length * itemHeight + borderWidth + padding + shadowHeight + textHeightAdjustment;
|
|
297
|
+
|
|
298
|
+
const buttonHeight = 32; // 按钮高度
|
|
299
|
+
const cellHeight = gridInfo?.rowHeight || 56; // 使用传入的 rowHeight 或默认值
|
|
300
|
+
const gridHeight = gridInfo?.gridHeight || 400; // 使用传入的 gridHeight 或默认值
|
|
301
|
+
|
|
302
|
+
// 根据行索引估算当前单元格在 Grid 中的位置
|
|
303
|
+
const currentRowIndex = rowIndex || 0;
|
|
304
|
+
|
|
305
|
+
// 计算当前单元格距离 Grid 底部的距离
|
|
306
|
+
const currentCellBottom = (currentRowIndex + 1) * cellHeight;
|
|
307
|
+
const spaceBelow = gridHeight - currentCellBottom;
|
|
308
|
+
|
|
309
|
+
// 计算上方可用空间
|
|
310
|
+
const currentCellTop = currentRowIndex * cellHeight;
|
|
311
|
+
const spaceAbove = currentCellTop;
|
|
312
|
+
|
|
313
|
+
// 添加安全边距,确保下拉菜单不会紧贴边界
|
|
314
|
+
const safetyMargin = 8;
|
|
315
|
+
const requiredSpace = actualHeight + safetyMargin;
|
|
316
|
+
|
|
317
|
+
// 智能选择显示位置
|
|
318
|
+
if (requiredSpace > spaceBelow && requiredSpace <= spaceAbove) {
|
|
319
|
+
// 下方空间不够,但上方空间足够,显示在上方
|
|
320
|
+
return {
|
|
321
|
+
top: -actualHeight - 4, // 按钮上方,留 4px 间距
|
|
322
|
+
right: 0,
|
|
323
|
+
};
|
|
324
|
+
} else if (requiredSpace > spaceBelow && requiredSpace > spaceAbove) {
|
|
325
|
+
// 上下空间都不够,选择空间较大的一侧
|
|
326
|
+
if (spaceAbove > spaceBelow) {
|
|
327
|
+
return {
|
|
328
|
+
top: -actualHeight - 4, // 按钮上方
|
|
329
|
+
right: 0,
|
|
330
|
+
};
|
|
331
|
+
} else {
|
|
332
|
+
return {
|
|
333
|
+
top: buttonHeight + 4, // 按钮下方
|
|
334
|
+
right: 0,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
// 下方空间足够,显示在下方
|
|
339
|
+
return {
|
|
340
|
+
top: buttonHeight + 4, // 按钮下方,留 4px 间距
|
|
341
|
+
right: 0,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}, [actions.length, rowIndex, gridInfo]);
|
|
345
|
+
|
|
346
|
+
// 全局下拉菜单管理
|
|
347
|
+
useEffect(() => {
|
|
348
|
+
const closeDropdown = () => {
|
|
349
|
+
setDropdownVisible(false);
|
|
350
|
+
unregisterDropdown();
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// 注册当前下拉菜单
|
|
354
|
+
if (dropdownVisible) {
|
|
355
|
+
registerDropdown(columnIndex || 0, rowIndex || 0, closeDropdown);
|
|
356
|
+
} else {
|
|
357
|
+
unregisterDropdown();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 清理函数
|
|
361
|
+
return () => {
|
|
362
|
+
unregisterDropdown();
|
|
363
|
+
};
|
|
364
|
+
}, [dropdownVisible, columnIndex, rowIndex]);
|
|
365
|
+
|
|
366
|
+
// 通知父组件下拉菜单状态变化
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
onDropdownStateChange?.(dropdownVisible);
|
|
369
|
+
}, [dropdownVisible, onDropdownStateChange]);
|
|
370
|
+
|
|
371
|
+
// 监听全局关闭事件
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
// const _handleGlobalClose = () => {
|
|
374
|
+
// if (globalDropdownState.activeColumn !== columnIndex ||
|
|
375
|
+
// globalDropdownState.activeRow !== rowIndex) {
|
|
376
|
+
// setDropdownVisible(false);
|
|
377
|
+
// }
|
|
378
|
+
// };
|
|
379
|
+
|
|
380
|
+
// 这里可以添加全局事件监听器
|
|
381
|
+
// 为了简化,我们在点击其他单元格时关闭当前下拉菜单
|
|
382
|
+
}, [columnIndex, rowIndex]);
|
|
227
383
|
|
|
228
384
|
// 渲染操作按钮
|
|
229
385
|
const renderActions = () => {
|
|
@@ -267,23 +423,34 @@ const TapasDataCell: React.FC<TapasDataCellProps> = ({
|
|
|
267
423
|
|
|
268
424
|
// 多个操作显示下拉菜单
|
|
269
425
|
return (
|
|
270
|
-
<View style={
|
|
426
|
+
<View style={[
|
|
427
|
+
styles.dropdownContainer,
|
|
428
|
+
{
|
|
429
|
+
// 只有当前激活的下拉菜单容器才有高 z-index
|
|
430
|
+
zIndex: globalDropdownState.activeColumn === columnIndex && globalDropdownState.activeRow === rowIndex ? 2147483646 : 1,
|
|
431
|
+
}
|
|
432
|
+
]}>
|
|
271
433
|
<Pressable
|
|
272
434
|
style={styles.actionButton}
|
|
273
435
|
onPress={(event) => {
|
|
274
436
|
// 阻止事件冒泡,避免触发外层的 attachCellPress
|
|
275
437
|
event.stopPropagation();
|
|
276
438
|
|
|
277
|
-
if (
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
439
|
+
if (dropdownVisible) {
|
|
440
|
+
// 如果当前下拉菜单已经打开,直接关闭
|
|
441
|
+
setDropdownVisible(false);
|
|
442
|
+
} else {
|
|
443
|
+
// 如果当前下拉菜单关闭,先关闭其他所有下拉菜单,然后打开当前菜单
|
|
444
|
+
closeAllDropdowns();
|
|
445
|
+
|
|
446
|
+
// 计算并设置下拉菜单位置
|
|
447
|
+
setDropdownPosition(calculateDropdownPosition());
|
|
448
|
+
|
|
449
|
+
// 延迟打开当前下拉菜单,确保其他菜单已关闭
|
|
450
|
+
setTimeout(() => {
|
|
451
|
+
setDropdownVisible(true);
|
|
452
|
+
}, 0);
|
|
284
453
|
}
|
|
285
|
-
|
|
286
|
-
setDropdownVisible(!dropdownVisible);
|
|
287
454
|
}}
|
|
288
455
|
disabled={disabled}
|
|
289
456
|
>
|
|
@@ -450,7 +617,10 @@ const useStyles = makeStyles((theme, props?: {
|
|
|
450
617
|
},
|
|
451
618
|
dropdownContainer: {
|
|
452
619
|
position: "relative",
|
|
453
|
-
|
|
620
|
+
// 默认使用较低的 z-index,只有激活的下拉菜单容器才有高 z-index
|
|
621
|
+
zIndex: 1,
|
|
622
|
+
// 确保容器本身不会创建新的 stacking context
|
|
623
|
+
isolation: "auto",
|
|
454
624
|
},
|
|
455
625
|
dropdownMenu: {
|
|
456
626
|
position: "absolute", // 使用绝对定位
|
|
@@ -467,9 +637,11 @@ const useStyles = makeStyles((theme, props?: {
|
|
|
467
637
|
shadowRadius: 3.84,
|
|
468
638
|
elevation: 5,
|
|
469
639
|
minWidth: 120,
|
|
470
|
-
zIndex:
|
|
640
|
+
zIndex: 2147483647, // 更高的 z-index,确保显示在所有元素之上
|
|
471
641
|
// 确保下拉菜单不被父容器的 overflow 隐藏
|
|
472
642
|
overflow: "visible",
|
|
643
|
+
// 确保下拉菜单在正确的层级上
|
|
644
|
+
isolation: "isolate",
|
|
473
645
|
},
|
|
474
646
|
dropdownItem: {
|
|
475
647
|
paddingHorizontal: 12,
|
|
@@ -495,4 +667,4 @@ export default withTheme(TapasDataCell);
|
|
|
495
667
|
|
|
496
668
|
// Export aliases for compatibility
|
|
497
669
|
export const DataCell = withTheme(TapasDataCell);
|
|
498
|
-
export { TapasDataCell };
|
|
670
|
+
export { TapasDataCell, setDropdownStateChangeListener };
|
|
@@ -58,6 +58,7 @@ const ItemWrapper: React.FC<any> = ({ data, columnIndex, rowIndex, style }) => {
|
|
|
58
58
|
columnHeaderHeight,
|
|
59
59
|
border,
|
|
60
60
|
theme,
|
|
61
|
+
hasActions,
|
|
61
62
|
} = data;
|
|
62
63
|
|
|
63
64
|
if (
|
|
@@ -68,13 +69,17 @@ const ItemWrapper: React.FC<any> = ({ data, columnIndex, rowIndex, style }) => {
|
|
|
68
69
|
return null;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
// 检测当前列是否有 actions 按钮
|
|
73
|
+
const shouldShowActions = hasActions && hasActions(columnIndex ?? 0, rowIndex ?? 0);
|
|
74
|
+
|
|
71
75
|
// 确保传递正确的参数给children函数
|
|
72
76
|
const childrenProps: ChildrenProps = {
|
|
73
77
|
columnIndex: columnIndex ?? 0,
|
|
74
78
|
rowIndex: rowIndex ?? 0,
|
|
75
79
|
style: {
|
|
76
80
|
...style,
|
|
77
|
-
overflow: "visible"
|
|
81
|
+
// 根据 hasActions 决定是否允许 overflow: "visible"
|
|
82
|
+
overflow: shouldShowActions ? "visible" : "hidden",
|
|
78
83
|
borderBottom: border
|
|
79
84
|
? `1px solid ${theme.grey3 ?? "rgba(0, 0, 0, 0.12)"}`
|
|
80
85
|
: "none",
|
|
@@ -210,6 +215,7 @@ const InnerElementType = forwardRef(
|
|
|
210
215
|
columnHeaderHeight,
|
|
211
216
|
}}
|
|
212
217
|
style={{
|
|
218
|
+
// 保持列头的 overflow: "hidden" 以确保正确的滚动行为
|
|
213
219
|
overflow: "hidden",
|
|
214
220
|
// borderTopLeftRadius: theme?.radius?.mini ?? 4,
|
|
215
221
|
backgroundColor:
|
|
@@ -223,7 +229,11 @@ const InnerElementType = forwardRef(
|
|
|
223
229
|
)}
|
|
224
230
|
<div ref={ref} {...rest} style={{
|
|
225
231
|
...style,
|
|
226
|
-
width:
|
|
232
|
+
width: totalWidth,
|
|
233
|
+
// width: rest.width, // 使用正确的 grid 宽度减去 sticky 列宽度
|
|
234
|
+
// width: rest.width - stickyColumnsWidth, // 使用正确的 grid 宽度减去 sticky 列宽度
|
|
235
|
+
// position: "sticky", // 固定容器位置,不随滚动移动
|
|
236
|
+
// left: 0, // 固定在左侧
|
|
227
237
|
}}>
|
|
228
238
|
{scrollLeft > 0 && (
|
|
229
239
|
<StickyColumn
|
|
@@ -231,7 +241,7 @@ const InnerElementType = forwardRef(
|
|
|
231
241
|
backgroundColor: theme?.background ?? "#F9F2E8",
|
|
232
242
|
zIndex: 5,
|
|
233
243
|
top: 0,
|
|
234
|
-
width:
|
|
244
|
+
width: 1, // 左侧阴影条宽度应该是 1
|
|
235
245
|
height: height,
|
|
236
246
|
left: 0,
|
|
237
247
|
marginTop: headersRender ? -1 * (columnHeaderHeight || 40) : 0,
|
|
@@ -268,7 +278,7 @@ const InnerElementType = forwardRef(
|
|
|
268
278
|
top: 0,
|
|
269
279
|
width: stickyColumnsWidth,
|
|
270
280
|
height: columnHeaderHeight || 40,
|
|
271
|
-
left: width - stickyColumnsWidth - (border ? 1 : 0),
|
|
281
|
+
left: width - stickyColumnsWidth - (border ? 1 : 0), // 使用总宽度计算位置
|
|
272
282
|
marginTop:
|
|
273
283
|
scrollLeft === 0
|
|
274
284
|
? -1 * (columnHeaderHeight || 40)
|
|
@@ -278,7 +288,6 @@ const InnerElementType = forwardRef(
|
|
|
278
288
|
borderLeft: border
|
|
279
289
|
? `1px solid ${theme?.grey3 ?? "rgba(0, 0, 0, 0.12)"}`
|
|
280
290
|
: "none",
|
|
281
|
-
// borderTopRightRadius: theme?.radius?.mini ?? 4,
|
|
282
291
|
}}
|
|
283
292
|
>
|
|
284
293
|
<VariableSizeGrid
|
|
@@ -292,8 +301,7 @@ const InnerElementType = forwardRef(
|
|
|
292
301
|
width={stickyColumnsWidth}
|
|
293
302
|
itemData={{ ItemRenderer: headersRender, stickyColumnIndices }}
|
|
294
303
|
style={{
|
|
295
|
-
overflow: "
|
|
296
|
-
// borderTopRightRadius: theme?.radius?.mini ?? 4,
|
|
304
|
+
overflow: "hidden",
|
|
297
305
|
backgroundColor:
|
|
298
306
|
columnHeaderBackgroundColor ||
|
|
299
307
|
(theme?.colors?.grey4 ?? "rgba(0, 0, 0, 0.08)"),
|
|
@@ -309,7 +317,7 @@ const InnerElementType = forwardRef(
|
|
|
309
317
|
backgroundColor: theme?.background ?? "#F9F2E8",
|
|
310
318
|
zIndex: 2,
|
|
311
319
|
top: headersRender ? columnHeaderHeight || 40 : 0,
|
|
312
|
-
left:
|
|
320
|
+
left: width - stickyColumnsWidth - (border ? 1 : 0), // 使用总宽度计算位置
|
|
313
321
|
width: stickyColumnsWidth,
|
|
314
322
|
height: "100%",
|
|
315
323
|
position: "sticky",
|
|
@@ -329,7 +337,9 @@ const InnerElementType = forwardRef(
|
|
|
329
337
|
rowHeight={rowHeight}
|
|
330
338
|
width={stickyColumnsWidth}
|
|
331
339
|
itemData={{ ItemRenderer, stickyColumnIndices, border, hasActions: hasActionsFn }}
|
|
332
|
-
style={{
|
|
340
|
+
style={{
|
|
341
|
+
overflow: hasActions ? "visible": "hidden"
|
|
342
|
+
}}
|
|
333
343
|
>
|
|
334
344
|
{StickyColumnCell}
|
|
335
345
|
</VariableSizeGrid>
|
|
@@ -433,7 +443,8 @@ const StickyList = ({
|
|
|
433
443
|
: rest.theme.radius.mini ?? 4,
|
|
434
444
|
scrollbarWidth: "none",
|
|
435
445
|
msOverflowStyle: "none",
|
|
436
|
-
overflow: "visible"
|
|
446
|
+
// 移除 overflow: "visible",恢复 Grid 的正常滚动功能
|
|
447
|
+
// Modal 下拉菜单不需要这个设置
|
|
437
448
|
}}
|
|
438
449
|
itemData={{
|
|
439
450
|
ItemRenderer: children,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = exports.TapasDataCell = exports.DataCell = void 0;
|
|
6
|
+
exports.setDropdownStateChangeListener = exports.default = exports.TapasDataCell = exports.DataCell = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _themed = require("@rneui/themed");
|
|
@@ -14,6 +14,40 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
14
14
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
15
15
|
// import { TapasDropdown } from "../Dropdown/Dropdown";
|
|
16
16
|
|
|
17
|
+
// 全局下拉菜单状态管理
|
|
18
|
+
const globalDropdownState = {
|
|
19
|
+
activeColumn: null,
|
|
20
|
+
activeRow: null,
|
|
21
|
+
closeAll: () => {},
|
|
22
|
+
onDropdownStateChange: undefined
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// 注册和注销下拉菜单的函数
|
|
26
|
+
const registerDropdown = (columnIndex, rowIndex, closeCallback) => {
|
|
27
|
+
globalDropdownState.activeColumn = columnIndex;
|
|
28
|
+
globalDropdownState.activeRow = rowIndex;
|
|
29
|
+
globalDropdownState.closeAll = closeCallback;
|
|
30
|
+
// 通知状态变化
|
|
31
|
+
globalDropdownState.onDropdownStateChange?.(columnIndex, rowIndex);
|
|
32
|
+
};
|
|
33
|
+
const unregisterDropdown = () => {
|
|
34
|
+
globalDropdownState.activeColumn = null;
|
|
35
|
+
globalDropdownState.activeRow = null;
|
|
36
|
+
globalDropdownState.closeAll = () => {};
|
|
37
|
+
// 通知状态变化
|
|
38
|
+
globalDropdownState.onDropdownStateChange?.(null, null);
|
|
39
|
+
};
|
|
40
|
+
const closeAllDropdowns = () => {
|
|
41
|
+
if (globalDropdownState.closeAll) {
|
|
42
|
+
globalDropdownState.closeAll();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// 设置下拉菜单状态变化监听器
|
|
47
|
+
const setDropdownStateChangeListener = callback => {
|
|
48
|
+
globalDropdownState.onDropdownStateChange = callback;
|
|
49
|
+
};
|
|
50
|
+
exports.setDropdownStateChangeListener = setDropdownStateChangeListener;
|
|
17
51
|
const TapasDataCell = ({
|
|
18
52
|
value,
|
|
19
53
|
dataType = "text",
|
|
@@ -47,6 +81,8 @@ const TapasDataCell = ({
|
|
|
47
81
|
disabled = false,
|
|
48
82
|
minHeight = 40,
|
|
49
83
|
isBlank = false,
|
|
84
|
+
onDropdownStateChange,
|
|
85
|
+
gridInfo,
|
|
50
86
|
style,
|
|
51
87
|
...otherProps
|
|
52
88
|
}) => {
|
|
@@ -148,6 +184,114 @@ const TapasDataCell = ({
|
|
|
148
184
|
right: 0
|
|
149
185
|
});
|
|
150
186
|
|
|
187
|
+
// 计算下拉菜单的最佳位置
|
|
188
|
+
const calculateDropdownPosition = (0, _react.useCallback)(() => {
|
|
189
|
+
// 更准确地计算下拉菜单的实际高度
|
|
190
|
+
const itemHeight = 32; // 每个选项的高度
|
|
191
|
+
const borderWidth = 2; // 边框宽度
|
|
192
|
+
const padding = 8; // 内边距
|
|
193
|
+
const shadowHeight = 4; // 阴影高度
|
|
194
|
+
|
|
195
|
+
// 考虑文本长度对高度的影响
|
|
196
|
+
const maxTextLength = Math.max(...actions.map(action => action.label.length));
|
|
197
|
+
const textHeightAdjustment = maxTextLength > 10 ? 4 : 0; // 长文本可能需要更多高度
|
|
198
|
+
|
|
199
|
+
// 计算下拉菜单的宽度(考虑文本长度)
|
|
200
|
+
// const minWidth = 120; // 最小宽度
|
|
201
|
+
// const textWidth = maxTextLength * 8; // 估算文本宽度
|
|
202
|
+
// const _actualWidth = Math.max(minWidth, textWidth + 24); // 24px 为内边距和图标空间
|
|
203
|
+
|
|
204
|
+
const actualHeight = actions.length * itemHeight + borderWidth + padding + shadowHeight + textHeightAdjustment;
|
|
205
|
+
const buttonHeight = 32; // 按钮高度
|
|
206
|
+
const cellHeight = gridInfo?.rowHeight || 56; // 使用传入的 rowHeight 或默认值
|
|
207
|
+
const gridHeight = gridInfo?.gridHeight || 400; // 使用传入的 gridHeight 或默认值
|
|
208
|
+
|
|
209
|
+
// 根据行索引估算当前单元格在 Grid 中的位置
|
|
210
|
+
const currentRowIndex = rowIndex || 0;
|
|
211
|
+
|
|
212
|
+
// 计算当前单元格距离 Grid 底部的距离
|
|
213
|
+
const currentCellBottom = (currentRowIndex + 1) * cellHeight;
|
|
214
|
+
const spaceBelow = gridHeight - currentCellBottom;
|
|
215
|
+
|
|
216
|
+
// 计算上方可用空间
|
|
217
|
+
const currentCellTop = currentRowIndex * cellHeight;
|
|
218
|
+
const spaceAbove = currentCellTop;
|
|
219
|
+
|
|
220
|
+
// 添加安全边距,确保下拉菜单不会紧贴边界
|
|
221
|
+
const safetyMargin = 8;
|
|
222
|
+
const requiredSpace = actualHeight + safetyMargin;
|
|
223
|
+
|
|
224
|
+
// 智能选择显示位置
|
|
225
|
+
if (requiredSpace > spaceBelow && requiredSpace <= spaceAbove) {
|
|
226
|
+
// 下方空间不够,但上方空间足够,显示在上方
|
|
227
|
+
return {
|
|
228
|
+
top: -actualHeight - 4,
|
|
229
|
+
// 按钮上方,留 4px 间距
|
|
230
|
+
right: 0
|
|
231
|
+
};
|
|
232
|
+
} else if (requiredSpace > spaceBelow && requiredSpace > spaceAbove) {
|
|
233
|
+
// 上下空间都不够,选择空间较大的一侧
|
|
234
|
+
if (spaceAbove > spaceBelow) {
|
|
235
|
+
return {
|
|
236
|
+
top: -actualHeight - 4,
|
|
237
|
+
// 按钮上方
|
|
238
|
+
right: 0
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
return {
|
|
242
|
+
top: buttonHeight + 4,
|
|
243
|
+
// 按钮下方
|
|
244
|
+
right: 0
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// 下方空间足够,显示在下方
|
|
249
|
+
return {
|
|
250
|
+
top: buttonHeight + 4,
|
|
251
|
+
// 按钮下方,留 4px 间距
|
|
252
|
+
right: 0
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}, [actions.length, rowIndex, gridInfo]);
|
|
256
|
+
|
|
257
|
+
// 全局下拉菜单管理
|
|
258
|
+
(0, _react.useEffect)(() => {
|
|
259
|
+
const closeDropdown = () => {
|
|
260
|
+
setDropdownVisible(false);
|
|
261
|
+
unregisterDropdown();
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// 注册当前下拉菜单
|
|
265
|
+
if (dropdownVisible) {
|
|
266
|
+
registerDropdown(columnIndex || 0, rowIndex || 0, closeDropdown);
|
|
267
|
+
} else {
|
|
268
|
+
unregisterDropdown();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 清理函数
|
|
272
|
+
return () => {
|
|
273
|
+
unregisterDropdown();
|
|
274
|
+
};
|
|
275
|
+
}, [dropdownVisible, columnIndex, rowIndex]);
|
|
276
|
+
|
|
277
|
+
// 通知父组件下拉菜单状态变化
|
|
278
|
+
(0, _react.useEffect)(() => {
|
|
279
|
+
onDropdownStateChange?.(dropdownVisible);
|
|
280
|
+
}, [dropdownVisible, onDropdownStateChange]);
|
|
281
|
+
|
|
282
|
+
// 监听全局关闭事件
|
|
283
|
+
(0, _react.useEffect)(() => {
|
|
284
|
+
// const _handleGlobalClose = () => {
|
|
285
|
+
// if (globalDropdownState.activeColumn !== columnIndex ||
|
|
286
|
+
// globalDropdownState.activeRow !== rowIndex) {
|
|
287
|
+
// setDropdownVisible(false);
|
|
288
|
+
// }
|
|
289
|
+
// };
|
|
290
|
+
|
|
291
|
+
// 这里可以添加全局事件监听器
|
|
292
|
+
// 为了简化,我们在点击其他单元格时关闭当前下拉菜单
|
|
293
|
+
}, [columnIndex, rowIndex]);
|
|
294
|
+
|
|
151
295
|
// 渲染操作按钮
|
|
152
296
|
const renderActions = () => {
|
|
153
297
|
if (!showActions || actions.length === 0 || isBlank) {
|
|
@@ -185,22 +329,30 @@ const TapasDataCell = ({
|
|
|
185
329
|
|
|
186
330
|
// 多个操作显示下拉菜单
|
|
187
331
|
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
188
|
-
style: styles.dropdownContainer
|
|
332
|
+
style: [styles.dropdownContainer, {
|
|
333
|
+
// 只有当前激活的下拉菜单容器才有高 z-index
|
|
334
|
+
zIndex: globalDropdownState.activeColumn === columnIndex && globalDropdownState.activeRow === rowIndex ? 2147483646 : 1
|
|
335
|
+
}]
|
|
189
336
|
}, /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
190
337
|
style: styles.actionButton,
|
|
191
338
|
onPress: event => {
|
|
192
339
|
// 阻止事件冒泡,避免触发外层的 attachCellPress
|
|
193
340
|
event.stopPropagation();
|
|
194
|
-
if (
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
341
|
+
if (dropdownVisible) {
|
|
342
|
+
// 如果当前下拉菜单已经打开,直接关闭
|
|
343
|
+
setDropdownVisible(false);
|
|
344
|
+
} else {
|
|
345
|
+
// 如果当前下拉菜单关闭,先关闭其他所有下拉菜单,然后打开当前菜单
|
|
346
|
+
closeAllDropdowns();
|
|
347
|
+
|
|
348
|
+
// 计算并设置下拉菜单位置
|
|
349
|
+
setDropdownPosition(calculateDropdownPosition());
|
|
350
|
+
|
|
351
|
+
// 延迟打开当前下拉菜单,确保其他菜单已关闭
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
setDropdownVisible(true);
|
|
354
|
+
}, 0);
|
|
202
355
|
}
|
|
203
|
-
setDropdownVisible(!dropdownVisible);
|
|
204
356
|
},
|
|
205
357
|
disabled: disabled
|
|
206
358
|
}, /*#__PURE__*/_react.default.createElement(_TapasIcon.default, {
|
|
@@ -321,7 +473,10 @@ const useStyles = (0, _themed.makeStyles)((theme, props) => {
|
|
|
321
473
|
},
|
|
322
474
|
dropdownContainer: {
|
|
323
475
|
position: "relative",
|
|
324
|
-
|
|
476
|
+
// 默认使用较低的 z-index,只有激活的下拉菜单容器才有高 z-index
|
|
477
|
+
zIndex: 1,
|
|
478
|
+
// 确保容器本身不会创建新的 stacking context
|
|
479
|
+
isolation: "auto"
|
|
325
480
|
},
|
|
326
481
|
dropdownMenu: {
|
|
327
482
|
position: "absolute",
|
|
@@ -339,9 +494,12 @@ const useStyles = (0, _themed.makeStyles)((theme, props) => {
|
|
|
339
494
|
shadowRadius: 3.84,
|
|
340
495
|
elevation: 5,
|
|
341
496
|
minWidth: 120,
|
|
342
|
-
zIndex:
|
|
497
|
+
zIndex: 2147483647,
|
|
498
|
+
// 更高的 z-index,确保显示在所有元素之上
|
|
343
499
|
// 确保下拉菜单不被父容器的 overflow 隐藏
|
|
344
|
-
overflow: "visible"
|
|
500
|
+
overflow: "visible",
|
|
501
|
+
// 确保下拉菜单在正确的层级上
|
|
502
|
+
isolation: "isolate"
|
|
345
503
|
},
|
|
346
504
|
dropdownItem: {
|
|
347
505
|
paddingHorizontal: 12,
|