bolt-table 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +228 -2
- package/dist/index.d.mts +120 -2
- package/dist/index.d.ts +120 -2
- package/dist/index.js +567 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +567 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -76,6 +76,10 @@ var PinOffIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgB
|
|
|
76
76
|
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "17", y2: "22" }),
|
|
77
77
|
/* @__PURE__ */ jsx("path", { d: "M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4" })
|
|
78
78
|
] });
|
|
79
|
+
var CopyIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgBase, style, className, children: [
|
|
80
|
+
/* @__PURE__ */ jsx("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
|
|
81
|
+
/* @__PURE__ */ jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
|
|
82
|
+
] });
|
|
79
83
|
var EyeOffIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgBase, style, className, children: [
|
|
80
84
|
/* @__PURE__ */ jsx("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }),
|
|
81
85
|
/* @__PURE__ */ jsx("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }),
|
|
@@ -131,21 +135,49 @@ var DraggableHeader = React.memo(
|
|
|
131
135
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
132
136
|
}
|
|
133
137
|
}, [contextMenu]);
|
|
134
|
-
const
|
|
135
|
-
e.preventDefault();
|
|
136
|
-
e.stopPropagation();
|
|
138
|
+
const showContextMenuAt = (clientX, clientY) => {
|
|
137
139
|
const menuWidth = 160;
|
|
138
140
|
const menuHeight = 180;
|
|
139
|
-
let x =
|
|
140
|
-
let y =
|
|
141
|
-
if (x + menuWidth > window.innerWidth)
|
|
141
|
+
let x = clientX;
|
|
142
|
+
let y = clientY;
|
|
143
|
+
if (x + menuWidth > window.innerWidth)
|
|
142
144
|
x = window.innerWidth - menuWidth - 10;
|
|
143
|
-
|
|
144
|
-
if (y + menuHeight > window.innerHeight) {
|
|
145
|
+
if (y + menuHeight > window.innerHeight)
|
|
145
146
|
y = window.innerHeight - menuHeight - 10;
|
|
146
|
-
}
|
|
147
147
|
setContextMenu({ x, y });
|
|
148
148
|
};
|
|
149
|
+
const handleContextMenu = (e) => {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
showContextMenuAt(e.clientX, e.clientY);
|
|
153
|
+
};
|
|
154
|
+
const longPressTimerRef = useRef(
|
|
155
|
+
null
|
|
156
|
+
);
|
|
157
|
+
const touchStartRef = useRef(null);
|
|
158
|
+
const cancelLongPress = () => {
|
|
159
|
+
if (longPressTimerRef.current) {
|
|
160
|
+
clearTimeout(longPressTimerRef.current);
|
|
161
|
+
longPressTimerRef.current = null;
|
|
162
|
+
}
|
|
163
|
+
touchStartRef.current = null;
|
|
164
|
+
};
|
|
165
|
+
const handleTouchStart = (e) => {
|
|
166
|
+
cancelLongPress();
|
|
167
|
+
const touch = e.touches[0];
|
|
168
|
+
touchStartRef.current = { x: touch.clientX, y: touch.clientY };
|
|
169
|
+
longPressTimerRef.current = setTimeout(() => {
|
|
170
|
+
longPressTimerRef.current = null;
|
|
171
|
+
showContextMenuAt(touch.clientX, touch.clientY);
|
|
172
|
+
}, 500);
|
|
173
|
+
};
|
|
174
|
+
const handleTouchMove = (e) => {
|
|
175
|
+
if (!touchStartRef.current) return;
|
|
176
|
+
const touch = e.touches[0];
|
|
177
|
+
const dx = touch.clientX - touchStartRef.current.x;
|
|
178
|
+
const dy = touch.clientY - touchStartRef.current.y;
|
|
179
|
+
if (Math.abs(dx) > 10 || Math.abs(dy) > 10) cancelLongPress();
|
|
180
|
+
};
|
|
149
181
|
const handleResizeStart = (e) => {
|
|
150
182
|
e.preventDefault();
|
|
151
183
|
e.stopPropagation();
|
|
@@ -195,6 +227,10 @@ var DraggableHeader = React.memo(
|
|
|
195
227
|
style: headerStyle,
|
|
196
228
|
className: `${column.className ?? ""} ${classNames?.header ?? ""} ${isPinned ? classNames?.pinnedHeader ?? "" : ""}`,
|
|
197
229
|
onContextMenu: handleContextMenu,
|
|
230
|
+
onTouchStart: handleTouchStart,
|
|
231
|
+
onTouchMove: handleTouchMove,
|
|
232
|
+
onTouchEnd: cancelLongPress,
|
|
233
|
+
onTouchCancel: cancelLongPress,
|
|
198
234
|
children: [
|
|
199
235
|
/* @__PURE__ */ jsxs2(
|
|
200
236
|
"div",
|
|
@@ -1000,15 +1036,26 @@ var TableBody = ({
|
|
|
1000
1036
|
expandable,
|
|
1001
1037
|
resolvedExpandedKeys,
|
|
1002
1038
|
rowHeight = 40,
|
|
1039
|
+
totalTableWidth,
|
|
1003
1040
|
scrollAreaWidth,
|
|
1004
1041
|
accentColor,
|
|
1005
1042
|
isLoading = false,
|
|
1006
1043
|
onExpandedRowResize,
|
|
1007
|
-
maxExpandedRowHeight
|
|
1044
|
+
maxExpandedRowHeight,
|
|
1045
|
+
pinnedTopData = [],
|
|
1046
|
+
pinnedBottomData = [],
|
|
1047
|
+
gridTemplateColumns,
|
|
1048
|
+
headerHeight = 36
|
|
1008
1049
|
}) => {
|
|
1009
1050
|
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
1010
1051
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1011
1052
|
const selectedKeySet = useMemo(() => new Set(normalizedSelectedKeys), [normalizedSelectedKeys]);
|
|
1053
|
+
const allDataForSelection = useMemo(() => {
|
|
1054
|
+
if (pinnedTopData.length === 0 && pinnedBottomData.length === 0)
|
|
1055
|
+
return data;
|
|
1056
|
+
return [...pinnedTopData, ...data, ...pinnedBottomData];
|
|
1057
|
+
}, [pinnedTopData, data, pinnedBottomData]);
|
|
1058
|
+
const pinnedRowBg = styles?.pinnedRowBg ?? styles?.pinnedBg;
|
|
1012
1059
|
const columnStyles = useMemo(() => {
|
|
1013
1060
|
return orderedColumns.map((col, colIndex) => {
|
|
1014
1061
|
const stickyOffset = columnOffsets.get(col.key);
|
|
@@ -1065,6 +1112,8 @@ var TableBody = ({
|
|
|
1065
1112
|
"div",
|
|
1066
1113
|
{
|
|
1067
1114
|
"data-row-key": rowKey,
|
|
1115
|
+
"data-column-key": col.key,
|
|
1116
|
+
"data-bt-cell": "",
|
|
1068
1117
|
"data-selected": isSelected || void 0,
|
|
1069
1118
|
style: {
|
|
1070
1119
|
position: "absolute",
|
|
@@ -1094,7 +1143,7 @@ var TableBody = ({
|
|
|
1094
1143
|
rowSelection,
|
|
1095
1144
|
normalizedSelectedKeys,
|
|
1096
1145
|
rowKey,
|
|
1097
|
-
allData:
|
|
1146
|
+
allData: allDataForSelection,
|
|
1098
1147
|
getRowKey,
|
|
1099
1148
|
accentColor,
|
|
1100
1149
|
isLoading: isRowShimmer,
|
|
@@ -1172,6 +1221,213 @@ var TableBody = ({
|
|
|
1172
1221
|
);
|
|
1173
1222
|
})
|
|
1174
1223
|
}
|
|
1224
|
+
),
|
|
1225
|
+
pinnedTopData.length > 0 && /* @__PURE__ */ jsx4(
|
|
1226
|
+
"div",
|
|
1227
|
+
{
|
|
1228
|
+
style: {
|
|
1229
|
+
gridColumn: "1 / -1",
|
|
1230
|
+
gridRow: 2,
|
|
1231
|
+
height: `${totalSize}px`,
|
|
1232
|
+
position: "relative",
|
|
1233
|
+
zIndex: 20,
|
|
1234
|
+
pointerEvents: "none"
|
|
1235
|
+
},
|
|
1236
|
+
children: /* @__PURE__ */ jsx4(
|
|
1237
|
+
"div",
|
|
1238
|
+
{
|
|
1239
|
+
style: {
|
|
1240
|
+
position: "sticky",
|
|
1241
|
+
top: headerHeight,
|
|
1242
|
+
pointerEvents: "auto",
|
|
1243
|
+
boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
|
|
1244
|
+
},
|
|
1245
|
+
children: pinnedTopData.map((row, rowIdx) => {
|
|
1246
|
+
const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
|
|
1247
|
+
const isSelected = selectedKeySet.has(rk);
|
|
1248
|
+
const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
|
|
1249
|
+
return /* @__PURE__ */ jsx4(
|
|
1250
|
+
"div",
|
|
1251
|
+
{
|
|
1252
|
+
className: classNames?.pinnedRow ?? "",
|
|
1253
|
+
style: {
|
|
1254
|
+
display: "grid",
|
|
1255
|
+
gridTemplateColumns: gridTemplateColumns ?? "",
|
|
1256
|
+
minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
|
|
1257
|
+
...styles?.pinnedRow
|
|
1258
|
+
},
|
|
1259
|
+
children: orderedColumns.map((col) => {
|
|
1260
|
+
const cellValue = row[col.dataIndex];
|
|
1261
|
+
const stickyOffset = columnOffsets.get(col.key);
|
|
1262
|
+
const isPinned = Boolean(col.pinned);
|
|
1263
|
+
let zIndex = 0;
|
|
1264
|
+
if (col.key === "__select__" || col.key === "__expand__")
|
|
1265
|
+
zIndex = 11;
|
|
1266
|
+
else if (isPinned) zIndex = 2;
|
|
1267
|
+
const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
|
|
1268
|
+
return /* @__PURE__ */ jsx4(
|
|
1269
|
+
"div",
|
|
1270
|
+
{
|
|
1271
|
+
"data-row-key": rk,
|
|
1272
|
+
"data-column-key": col.key,
|
|
1273
|
+
"data-bt-cell": "",
|
|
1274
|
+
"data-selected": isSelected || void 0,
|
|
1275
|
+
style: {
|
|
1276
|
+
position: isPinned ? "sticky" : "relative",
|
|
1277
|
+
...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
|
|
1278
|
+
...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
|
|
1279
|
+
zIndex,
|
|
1280
|
+
backgroundColor: pinnedRowBg,
|
|
1281
|
+
backdropFilter: "blur(12px)",
|
|
1282
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
1283
|
+
...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
|
|
1284
|
+
},
|
|
1285
|
+
children: /* @__PURE__ */ jsx4(
|
|
1286
|
+
"div",
|
|
1287
|
+
{
|
|
1288
|
+
style: {
|
|
1289
|
+
height: `${rowHeight}px`,
|
|
1290
|
+
position: "relative"
|
|
1291
|
+
},
|
|
1292
|
+
children: /* @__PURE__ */ jsx4(
|
|
1293
|
+
Cell,
|
|
1294
|
+
{
|
|
1295
|
+
value: cellValue,
|
|
1296
|
+
record: row,
|
|
1297
|
+
column: col,
|
|
1298
|
+
rowIndex: rowIdx,
|
|
1299
|
+
classNames,
|
|
1300
|
+
styles,
|
|
1301
|
+
isSelected,
|
|
1302
|
+
isExpanded,
|
|
1303
|
+
rowSelection,
|
|
1304
|
+
normalizedSelectedKeys,
|
|
1305
|
+
rowKey: rk,
|
|
1306
|
+
allData: allDataForSelection,
|
|
1307
|
+
getRowKey,
|
|
1308
|
+
accentColor,
|
|
1309
|
+
isLoading: false,
|
|
1310
|
+
recordFingerprint
|
|
1311
|
+
}
|
|
1312
|
+
)
|
|
1313
|
+
}
|
|
1314
|
+
)
|
|
1315
|
+
},
|
|
1316
|
+
col.key
|
|
1317
|
+
);
|
|
1318
|
+
})
|
|
1319
|
+
},
|
|
1320
|
+
`pinned-top-${rk}`
|
|
1321
|
+
);
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
)
|
|
1325
|
+
}
|
|
1326
|
+
),
|
|
1327
|
+
pinnedBottomData.length > 0 && /* @__PURE__ */ jsx4(
|
|
1328
|
+
"div",
|
|
1329
|
+
{
|
|
1330
|
+
style: {
|
|
1331
|
+
gridColumn: "1 / -1",
|
|
1332
|
+
gridRow: 2,
|
|
1333
|
+
height: `${totalSize}px`,
|
|
1334
|
+
position: "relative",
|
|
1335
|
+
zIndex: 20,
|
|
1336
|
+
pointerEvents: "none",
|
|
1337
|
+
display: "flex",
|
|
1338
|
+
flexDirection: "column"
|
|
1339
|
+
},
|
|
1340
|
+
children: /* @__PURE__ */ jsx4(
|
|
1341
|
+
"div",
|
|
1342
|
+
{
|
|
1343
|
+
style: {
|
|
1344
|
+
marginTop: "auto",
|
|
1345
|
+
position: "sticky",
|
|
1346
|
+
bottom: 0,
|
|
1347
|
+
pointerEvents: "auto",
|
|
1348
|
+
boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
|
|
1349
|
+
},
|
|
1350
|
+
children: pinnedBottomData.map((row, rowIdx) => {
|
|
1351
|
+
const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
|
|
1352
|
+
const isSelected = selectedKeySet.has(rk);
|
|
1353
|
+
const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
|
|
1354
|
+
return /* @__PURE__ */ jsx4(
|
|
1355
|
+
"div",
|
|
1356
|
+
{
|
|
1357
|
+
className: classNames?.pinnedRow ?? "",
|
|
1358
|
+
style: {
|
|
1359
|
+
display: "grid",
|
|
1360
|
+
gridTemplateColumns: gridTemplateColumns ?? "",
|
|
1361
|
+
minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
|
|
1362
|
+
...styles?.pinnedRow
|
|
1363
|
+
},
|
|
1364
|
+
children: orderedColumns.map((col) => {
|
|
1365
|
+
const cellValue = row[col.dataIndex];
|
|
1366
|
+
const stickyOffset = columnOffsets.get(col.key);
|
|
1367
|
+
const isPinned = Boolean(col.pinned);
|
|
1368
|
+
let zIndex = 0;
|
|
1369
|
+
if (col.key === "__select__" || col.key === "__expand__")
|
|
1370
|
+
zIndex = 11;
|
|
1371
|
+
else if (isPinned) zIndex = 2;
|
|
1372
|
+
const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
|
|
1373
|
+
return /* @__PURE__ */ jsx4(
|
|
1374
|
+
"div",
|
|
1375
|
+
{
|
|
1376
|
+
"data-row-key": rk,
|
|
1377
|
+
"data-column-key": col.key,
|
|
1378
|
+
"data-bt-cell": "",
|
|
1379
|
+
"data-selected": isSelected || void 0,
|
|
1380
|
+
style: {
|
|
1381
|
+
position: isPinned ? "sticky" : "relative",
|
|
1382
|
+
...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
|
|
1383
|
+
...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
|
|
1384
|
+
zIndex,
|
|
1385
|
+
backgroundColor: pinnedRowBg,
|
|
1386
|
+
backdropFilter: "blur(12px)",
|
|
1387
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
1388
|
+
...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
|
|
1389
|
+
},
|
|
1390
|
+
children: /* @__PURE__ */ jsx4(
|
|
1391
|
+
"div",
|
|
1392
|
+
{
|
|
1393
|
+
style: {
|
|
1394
|
+
height: `${rowHeight}px`,
|
|
1395
|
+
position: "relative"
|
|
1396
|
+
},
|
|
1397
|
+
children: /* @__PURE__ */ jsx4(
|
|
1398
|
+
Cell,
|
|
1399
|
+
{
|
|
1400
|
+
value: cellValue,
|
|
1401
|
+
record: row,
|
|
1402
|
+
column: col,
|
|
1403
|
+
rowIndex: rowIdx,
|
|
1404
|
+
classNames,
|
|
1405
|
+
styles,
|
|
1406
|
+
isSelected,
|
|
1407
|
+
isExpanded,
|
|
1408
|
+
rowSelection,
|
|
1409
|
+
normalizedSelectedKeys,
|
|
1410
|
+
rowKey: rk,
|
|
1411
|
+
allData: allDataForSelection,
|
|
1412
|
+
getRowKey,
|
|
1413
|
+
accentColor,
|
|
1414
|
+
isLoading: false,
|
|
1415
|
+
recordFingerprint
|
|
1416
|
+
}
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
)
|
|
1420
|
+
},
|
|
1421
|
+
col.key
|
|
1422
|
+
);
|
|
1423
|
+
})
|
|
1424
|
+
},
|
|
1425
|
+
`pinned-bottom-${rk}`
|
|
1426
|
+
);
|
|
1427
|
+
})
|
|
1428
|
+
}
|
|
1429
|
+
)
|
|
1430
|
+
}
|
|
1175
1431
|
)
|
|
1176
1432
|
] });
|
|
1177
1433
|
};
|
|
@@ -1207,6 +1463,8 @@ function BoltTable({
|
|
|
1207
1463
|
onColumnPin,
|
|
1208
1464
|
onColumnHide,
|
|
1209
1465
|
rowSelection,
|
|
1466
|
+
rowPinning,
|
|
1467
|
+
onRowPin,
|
|
1210
1468
|
expandable,
|
|
1211
1469
|
rowKey = "id",
|
|
1212
1470
|
onEndReached,
|
|
@@ -1747,6 +2005,73 @@ function BoltTable({
|
|
|
1747
2005
|
}
|
|
1748
2006
|
return result;
|
|
1749
2007
|
}, [data, sortState, columnFilters]);
|
|
2008
|
+
const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = useMemo2(() => {
|
|
2009
|
+
if (!rowPinning || !rowPinning.top?.length && !rowPinning.bottom?.length) {
|
|
2010
|
+
return {
|
|
2011
|
+
pinnedTopRows: [],
|
|
2012
|
+
pinnedBottomRows: [],
|
|
2013
|
+
unpinnedProcessedData: processedData
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
const topKeySet = new Set((rowPinning.top ?? []).map(String));
|
|
2017
|
+
const bottomKeySet = new Set((rowPinning.bottom ?? []).map(String));
|
|
2018
|
+
const topMap = /* @__PURE__ */ new Map();
|
|
2019
|
+
const bottomMap = /* @__PURE__ */ new Map();
|
|
2020
|
+
const rest = [];
|
|
2021
|
+
processedData.forEach((row, idx) => {
|
|
2022
|
+
const key = getRowKey(row, idx);
|
|
2023
|
+
if (topKeySet.has(key)) topMap.set(key, row);
|
|
2024
|
+
else if (bottomKeySet.has(key)) bottomMap.set(key, row);
|
|
2025
|
+
else rest.push(row);
|
|
2026
|
+
});
|
|
2027
|
+
const orderedTop = (rowPinning.top ?? []).map((k) => topMap.get(String(k))).filter((r) => r !== void 0);
|
|
2028
|
+
const orderedBottom = (rowPinning.bottom ?? []).map((k) => bottomMap.get(String(k))).filter((r) => r !== void 0);
|
|
2029
|
+
return {
|
|
2030
|
+
pinnedTopRows: orderedTop,
|
|
2031
|
+
pinnedBottomRows: orderedBottom,
|
|
2032
|
+
unpinnedProcessedData: rest
|
|
2033
|
+
};
|
|
2034
|
+
}, [processedData, rowPinning, getRowKey]);
|
|
2035
|
+
const pinnedTopHeight = pinnedTopRows.length * rowHeight;
|
|
2036
|
+
const pinnedBottomHeight = pinnedBottomRows.length * rowHeight;
|
|
2037
|
+
const pinnedTopKeySet = useMemo2(
|
|
2038
|
+
() => new Set((rowPinning?.top ?? []).map(String)),
|
|
2039
|
+
[rowPinning?.top]
|
|
2040
|
+
);
|
|
2041
|
+
const pinnedBottomKeySet = useMemo2(
|
|
2042
|
+
() => new Set((rowPinning?.bottom ?? []).map(String)),
|
|
2043
|
+
[rowPinning?.bottom]
|
|
2044
|
+
);
|
|
2045
|
+
const [cellContextMenu, setCellContextMenu] = useState2(null);
|
|
2046
|
+
const cellMenuRef = useRef4(null);
|
|
2047
|
+
const cellLongPressTimer = useRef4(
|
|
2048
|
+
null
|
|
2049
|
+
);
|
|
2050
|
+
const cellTouchStart = useRef4(null);
|
|
2051
|
+
const cancelCellLongPress = useCallback(() => {
|
|
2052
|
+
if (cellLongPressTimer.current) {
|
|
2053
|
+
clearTimeout(cellLongPressTimer.current);
|
|
2054
|
+
cellLongPressTimer.current = null;
|
|
2055
|
+
}
|
|
2056
|
+
cellTouchStart.current = null;
|
|
2057
|
+
}, []);
|
|
2058
|
+
React4.useEffect(() => {
|
|
2059
|
+
if (!cellContextMenu) return;
|
|
2060
|
+
const close = (e) => {
|
|
2061
|
+
if (cellMenuRef.current && cellMenuRef.current.contains(e.target))
|
|
2062
|
+
return;
|
|
2063
|
+
setCellContextMenu(null);
|
|
2064
|
+
};
|
|
2065
|
+
const onKey = (e) => {
|
|
2066
|
+
if (e.key === "Escape") setCellContextMenu(null);
|
|
2067
|
+
};
|
|
2068
|
+
document.addEventListener("mousedown", close);
|
|
2069
|
+
document.addEventListener("keydown", onKey);
|
|
2070
|
+
return () => {
|
|
2071
|
+
document.removeEventListener("mousedown", close);
|
|
2072
|
+
document.removeEventListener("keydown", onKey);
|
|
2073
|
+
};
|
|
2074
|
+
}, [cellContextMenu]);
|
|
1750
2075
|
const columnFiltersKey = Object.keys(columnFilters).sort().map((k) => `${k}:${columnFilters[k]}`).join("|");
|
|
1751
2076
|
React4.useEffect(() => {
|
|
1752
2077
|
tableAreaRef.current?.scrollTo({ top: 0 });
|
|
@@ -1754,12 +2079,12 @@ function BoltTable({
|
|
|
1754
2079
|
const pgEnabled = pagination !== false && !!pagination;
|
|
1755
2080
|
const pgSize = pgEnabled ? pagination.pageSize ?? 10 : 10;
|
|
1756
2081
|
const pgCurrent = pgEnabled ? Number(pagination.current ?? 1) : 1;
|
|
1757
|
-
const needsClientPagination = pgEnabled &&
|
|
2082
|
+
const needsClientPagination = pgEnabled && unpinnedProcessedData.length > pgSize;
|
|
1758
2083
|
const paginatedData = useMemo2(() => {
|
|
1759
|
-
if (!needsClientPagination) return
|
|
2084
|
+
if (!needsClientPagination) return unpinnedProcessedData;
|
|
1760
2085
|
const start = (pgCurrent - 1) * pgSize;
|
|
1761
|
-
return
|
|
1762
|
-
}, [
|
|
2086
|
+
return unpinnedProcessedData.slice(start, start + pgSize);
|
|
2087
|
+
}, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
|
|
1763
2088
|
const shimmerCount = pgEnabled ? pgSize : 15;
|
|
1764
2089
|
const showShimmer = isLoading && processedData.length === 0;
|
|
1765
2090
|
const shimmerData = useMemo2(() => {
|
|
@@ -1817,8 +2142,9 @@ function BoltTable({
|
|
|
1817
2142
|
return cached ? rowHeight + cached : rowHeight + expandedRowHeight;
|
|
1818
2143
|
},
|
|
1819
2144
|
overscan: 5,
|
|
1820
|
-
|
|
1821
|
-
|
|
2145
|
+
getItemKey: (index) => shimmerData ? `__shimmer_${index}__` : getRowKey(displayData[index], index),
|
|
2146
|
+
paddingStart: pinnedTopHeight,
|
|
2147
|
+
paddingEnd: pinnedBottomHeight
|
|
1822
2148
|
});
|
|
1823
2149
|
const rowVirtualizerRef = useRef4(rowVirtualizer);
|
|
1824
2150
|
rowVirtualizerRef.current = rowVirtualizer;
|
|
@@ -1858,7 +2184,7 @@ function BoltTable({
|
|
|
1858
2184
|
const activeColumn = activeId ? orderedColumns.find((col) => col.key === activeId) : null;
|
|
1859
2185
|
const currentPage = pgCurrent;
|
|
1860
2186
|
const pageSize = pgSize;
|
|
1861
|
-
const rawTotal = pgEnabled ? pagination.total ?? (needsClientPagination ?
|
|
2187
|
+
const rawTotal = pgEnabled ? pagination.total ?? (needsClientPagination ? unpinnedProcessedData.length : data.length) : data.length;
|
|
1862
2188
|
const lastKnownTotalRef = useRef4(0);
|
|
1863
2189
|
if (!isLoading || rawTotal > 0) {
|
|
1864
2190
|
lastKnownTotalRef.current = rawTotal;
|
|
@@ -2107,6 +2433,64 @@ function BoltTable({
|
|
|
2107
2433
|
position: "relative",
|
|
2108
2434
|
...isEmpty ? { height: "100%" } : {}
|
|
2109
2435
|
},
|
|
2436
|
+
onContextMenu: (e) => {
|
|
2437
|
+
const cell = e.target.closest("[data-bt-cell]");
|
|
2438
|
+
if (!cell) return;
|
|
2439
|
+
const rk = cell.dataset.rowKey;
|
|
2440
|
+
const ck = cell.dataset.columnKey;
|
|
2441
|
+
if (!rk || !ck) return;
|
|
2442
|
+
const col = freshOrderedColumns.find(
|
|
2443
|
+
(c) => c.key === ck
|
|
2444
|
+
);
|
|
2445
|
+
const hasCopy = col?.copy;
|
|
2446
|
+
const hasRowPin = !!onRowPin;
|
|
2447
|
+
if (!hasCopy && !hasRowPin) return;
|
|
2448
|
+
e.preventDefault();
|
|
2449
|
+
setCellContextMenu({
|
|
2450
|
+
x: Math.min(e.clientX, window.innerWidth - 200),
|
|
2451
|
+
y: Math.min(e.clientY, window.innerHeight - 200),
|
|
2452
|
+
rowKey: rk,
|
|
2453
|
+
columnKey: ck
|
|
2454
|
+
});
|
|
2455
|
+
},
|
|
2456
|
+
onTouchStart: (e) => {
|
|
2457
|
+
cancelCellLongPress();
|
|
2458
|
+
const cell = e.target.closest("[data-bt-cell]");
|
|
2459
|
+
if (!cell) return;
|
|
2460
|
+
const touch = e.touches[0];
|
|
2461
|
+
cellTouchStart.current = {
|
|
2462
|
+
x: touch.clientX,
|
|
2463
|
+
y: touch.clientY
|
|
2464
|
+
};
|
|
2465
|
+
const rk = cell.dataset.rowKey;
|
|
2466
|
+
const ck = cell.dataset.columnKey;
|
|
2467
|
+
cellLongPressTimer.current = setTimeout(() => {
|
|
2468
|
+
cellLongPressTimer.current = null;
|
|
2469
|
+
if (!rk || !ck) return;
|
|
2470
|
+
const col = freshOrderedColumns.find(
|
|
2471
|
+
(c) => c.key === ck
|
|
2472
|
+
);
|
|
2473
|
+
const hasCopy = col?.copy;
|
|
2474
|
+
const hasRowPin = !!onRowPin;
|
|
2475
|
+
if (!hasCopy && !hasRowPin) return;
|
|
2476
|
+
setCellContextMenu({
|
|
2477
|
+
x: Math.min(touch.clientX, window.innerWidth - 200),
|
|
2478
|
+
y: Math.min(touch.clientY, window.innerHeight - 200),
|
|
2479
|
+
rowKey: rk,
|
|
2480
|
+
columnKey: ck
|
|
2481
|
+
});
|
|
2482
|
+
}, 500);
|
|
2483
|
+
},
|
|
2484
|
+
onTouchMove: (e) => {
|
|
2485
|
+
if (!cellTouchStart.current) return;
|
|
2486
|
+
const touch = e.touches[0];
|
|
2487
|
+
const dx = touch.clientX - cellTouchStart.current.x;
|
|
2488
|
+
const dy = touch.clientY - cellTouchStart.current.y;
|
|
2489
|
+
if (Math.abs(dx) > 10 || Math.abs(dy) > 10)
|
|
2490
|
+
cancelCellLongPress();
|
|
2491
|
+
},
|
|
2492
|
+
onTouchEnd: cancelCellLongPress,
|
|
2493
|
+
onTouchCancel: cancelCellLongPress,
|
|
2110
2494
|
children: [
|
|
2111
2495
|
orderedColumns.map((column, visualIndex) => {
|
|
2112
2496
|
if (column.key === "__select__" && rowSelection) {
|
|
@@ -2295,7 +2679,11 @@ function BoltTable({
|
|
|
2295
2679
|
scrollContainerRef: tableAreaRef,
|
|
2296
2680
|
isLoading: showShimmer,
|
|
2297
2681
|
onExpandedRowResize: handleExpandedRowResize,
|
|
2298
|
-
maxExpandedRowHeight
|
|
2682
|
+
maxExpandedRowHeight,
|
|
2683
|
+
pinnedTopData: pinnedTopRows,
|
|
2684
|
+
pinnedBottomData: pinnedBottomRows,
|
|
2685
|
+
gridTemplateColumns,
|
|
2686
|
+
headerHeight: HEADER_HEIGHT
|
|
2299
2687
|
}
|
|
2300
2688
|
)
|
|
2301
2689
|
)
|
|
@@ -2601,7 +2989,166 @@ function BoltTable({
|
|
|
2601
2989
|
}
|
|
2602
2990
|
),
|
|
2603
2991
|
document.body
|
|
2604
|
-
)
|
|
2992
|
+
),
|
|
2993
|
+
cellContextMenu && typeof document !== "undefined" && (() => {
|
|
2994
|
+
const menuCol = freshOrderedColumns.find(
|
|
2995
|
+
(c) => c.key === cellContextMenu.columnKey
|
|
2996
|
+
);
|
|
2997
|
+
const isPinnedTop = pinnedTopKeySet.has(cellContextMenu.rowKey);
|
|
2998
|
+
const isPinnedBottom = pinnedBottomKeySet.has(
|
|
2999
|
+
cellContextMenu.rowKey
|
|
3000
|
+
);
|
|
3001
|
+
const hasCopy = menuCol?.copy;
|
|
3002
|
+
const hasRowPin = !!onRowPin;
|
|
3003
|
+
let menuRecord;
|
|
3004
|
+
let menuRowIndex = 0;
|
|
3005
|
+
const allRows = [
|
|
3006
|
+
...pinnedTopRows,
|
|
3007
|
+
...displayData,
|
|
3008
|
+
...pinnedBottomRows
|
|
3009
|
+
];
|
|
3010
|
+
for (let i = 0; i < allRows.length; i++) {
|
|
3011
|
+
const rk = getRowKey(allRows[i], i);
|
|
3012
|
+
if (rk === cellContextMenu.rowKey) {
|
|
3013
|
+
menuRecord = allRows[i];
|
|
3014
|
+
menuRowIndex = i;
|
|
3015
|
+
break;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
const menuValue = menuRecord && menuCol ? menuRecord[menuCol.dataIndex] : void 0;
|
|
3019
|
+
const btnStyle = {
|
|
3020
|
+
display: "flex",
|
|
3021
|
+
width: "100%",
|
|
3022
|
+
alignItems: "center",
|
|
3023
|
+
gap: 8,
|
|
3024
|
+
background: "none",
|
|
3025
|
+
border: "none",
|
|
3026
|
+
padding: "6px 12px",
|
|
3027
|
+
fontSize: 12,
|
|
3028
|
+
cursor: "pointer",
|
|
3029
|
+
color: "inherit",
|
|
3030
|
+
whiteSpace: "nowrap"
|
|
3031
|
+
};
|
|
3032
|
+
return createPortal2(
|
|
3033
|
+
/* @__PURE__ */ jsxs5(
|
|
3034
|
+
"div",
|
|
3035
|
+
{
|
|
3036
|
+
ref: cellMenuRef,
|
|
3037
|
+
style: {
|
|
3038
|
+
position: "fixed",
|
|
3039
|
+
top: cellContextMenu.y,
|
|
3040
|
+
left: cellContextMenu.x,
|
|
3041
|
+
zIndex: 99999,
|
|
3042
|
+
minWidth: 170,
|
|
3043
|
+
borderRadius: 8,
|
|
3044
|
+
border: "1px solid rgba(128,128,128,0.2)",
|
|
3045
|
+
boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
|
|
3046
|
+
backdropFilter: "blur(16px)",
|
|
3047
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
3048
|
+
backgroundColor: "rgba(128,128,128,0.08)",
|
|
3049
|
+
padding: "4px 0",
|
|
3050
|
+
fontSize: 10
|
|
3051
|
+
},
|
|
3052
|
+
children: [
|
|
3053
|
+
hasRowPin && /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
3054
|
+
/* @__PURE__ */ jsxs5(
|
|
3055
|
+
"button",
|
|
3056
|
+
{
|
|
3057
|
+
"data-bt-ctx-item": true,
|
|
3058
|
+
style: btnStyle,
|
|
3059
|
+
onClick: () => {
|
|
3060
|
+
onRowPin(
|
|
3061
|
+
cellContextMenu.rowKey,
|
|
3062
|
+
isPinnedTop ? false : "top"
|
|
3063
|
+
);
|
|
3064
|
+
setCellContextMenu(null);
|
|
3065
|
+
},
|
|
3066
|
+
children: [
|
|
3067
|
+
isPinnedTop ? icons?.pinOff ?? /* @__PURE__ */ jsx5(
|
|
3068
|
+
PinOffIcon,
|
|
3069
|
+
{
|
|
3070
|
+
style: { width: 14, height: 14, flexShrink: 0 }
|
|
3071
|
+
}
|
|
3072
|
+
) : icons?.pin ?? /* @__PURE__ */ jsx5(
|
|
3073
|
+
PinIcon,
|
|
3074
|
+
{
|
|
3075
|
+
style: { width: 14, height: 14, flexShrink: 0 }
|
|
3076
|
+
}
|
|
3077
|
+
),
|
|
3078
|
+
isPinnedTop ? "Unpin Row from Top" : "Pin Row to Top"
|
|
3079
|
+
]
|
|
3080
|
+
}
|
|
3081
|
+
),
|
|
3082
|
+
/* @__PURE__ */ jsxs5(
|
|
3083
|
+
"button",
|
|
3084
|
+
{
|
|
3085
|
+
"data-bt-ctx-item": true,
|
|
3086
|
+
style: btnStyle,
|
|
3087
|
+
onClick: () => {
|
|
3088
|
+
onRowPin(
|
|
3089
|
+
cellContextMenu.rowKey,
|
|
3090
|
+
isPinnedBottom ? false : "bottom"
|
|
3091
|
+
);
|
|
3092
|
+
setCellContextMenu(null);
|
|
3093
|
+
},
|
|
3094
|
+
children: [
|
|
3095
|
+
isPinnedBottom ? icons?.pinOff ?? /* @__PURE__ */ jsx5(
|
|
3096
|
+
PinOffIcon,
|
|
3097
|
+
{
|
|
3098
|
+
style: { width: 14, height: 14, flexShrink: 0 }
|
|
3099
|
+
}
|
|
3100
|
+
) : icons?.pin ?? /* @__PURE__ */ jsx5(
|
|
3101
|
+
PinIcon,
|
|
3102
|
+
{
|
|
3103
|
+
style: {
|
|
3104
|
+
width: 14,
|
|
3105
|
+
height: 14,
|
|
3106
|
+
flexShrink: 0,
|
|
3107
|
+
transform: "rotate(180deg)"
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
),
|
|
3111
|
+
isPinnedBottom ? "Unpin Row from Bottom" : "Pin Row to Bottom"
|
|
3112
|
+
]
|
|
3113
|
+
}
|
|
3114
|
+
)
|
|
3115
|
+
] }),
|
|
3116
|
+
hasRowPin && hasCopy && /* @__PURE__ */ jsx5(
|
|
3117
|
+
"div",
|
|
3118
|
+
{
|
|
3119
|
+
style: {
|
|
3120
|
+
borderTop: "1px solid rgba(128,128,128,0.2)",
|
|
3121
|
+
margin: "4px 0"
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
),
|
|
3125
|
+
hasCopy && menuRecord && menuCol && /* @__PURE__ */ jsxs5(
|
|
3126
|
+
"button",
|
|
3127
|
+
{
|
|
3128
|
+
"data-bt-ctx-item": true,
|
|
3129
|
+
style: btnStyle,
|
|
3130
|
+
onClick: () => {
|
|
3131
|
+
const text = typeof menuCol.copy === "function" ? menuCol.copy(menuValue, menuRecord, menuRowIndex) : String(menuValue ?? "");
|
|
3132
|
+
navigator.clipboard?.writeText(text);
|
|
3133
|
+
setCellContextMenu(null);
|
|
3134
|
+
},
|
|
3135
|
+
children: [
|
|
3136
|
+
icons?.copy ?? /* @__PURE__ */ jsx5(
|
|
3137
|
+
CopyIcon,
|
|
3138
|
+
{
|
|
3139
|
+
style: { width: 14, height: 14, flexShrink: 0 }
|
|
3140
|
+
}
|
|
3141
|
+
),
|
|
3142
|
+
"Copy"
|
|
3143
|
+
]
|
|
3144
|
+
}
|
|
3145
|
+
)
|
|
3146
|
+
]
|
|
3147
|
+
}
|
|
3148
|
+
),
|
|
3149
|
+
document.body
|
|
3150
|
+
);
|
|
3151
|
+
})()
|
|
2605
3152
|
] });
|
|
2606
3153
|
}
|
|
2607
3154
|
export {
|