bolt-table 0.1.13 → 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/dist/index.js CHANGED
@@ -110,6 +110,10 @@ var PinOffIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtim
110
110
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12", y1: "17", y2: "22" }),
111
111
  /* @__PURE__ */ (0, import_jsx_runtime.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" })
112
112
  ] });
113
+ var CopyIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
116
+ ] });
113
117
  var EyeOffIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
114
118
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }),
115
119
  /* @__PURE__ */ (0, import_jsx_runtime.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" }),
@@ -165,21 +169,49 @@ var DraggableHeader = import_react.default.memo(
165
169
  return () => document.removeEventListener("mousedown", handleClickOutside);
166
170
  }
167
171
  }, [contextMenu]);
168
- const handleContextMenu = (e) => {
169
- e.preventDefault();
170
- e.stopPropagation();
172
+ const showContextMenuAt = (clientX, clientY) => {
171
173
  const menuWidth = 160;
172
174
  const menuHeight = 180;
173
- let x = e.clientX;
174
- let y = e.clientY;
175
- if (x + menuWidth > window.innerWidth) {
175
+ let x = clientX;
176
+ let y = clientY;
177
+ if (x + menuWidth > window.innerWidth)
176
178
  x = window.innerWidth - menuWidth - 10;
177
- }
178
- if (y + menuHeight > window.innerHeight) {
179
+ if (y + menuHeight > window.innerHeight)
179
180
  y = window.innerHeight - menuHeight - 10;
180
- }
181
181
  setContextMenu({ x, y });
182
182
  };
183
+ const handleContextMenu = (e) => {
184
+ e.preventDefault();
185
+ e.stopPropagation();
186
+ showContextMenuAt(e.clientX, e.clientY);
187
+ };
188
+ const longPressTimerRef = (0, import_react.useRef)(
189
+ null
190
+ );
191
+ const touchStartRef = (0, import_react.useRef)(null);
192
+ const cancelLongPress = () => {
193
+ if (longPressTimerRef.current) {
194
+ clearTimeout(longPressTimerRef.current);
195
+ longPressTimerRef.current = null;
196
+ }
197
+ touchStartRef.current = null;
198
+ };
199
+ const handleTouchStart = (e) => {
200
+ cancelLongPress();
201
+ const touch = e.touches[0];
202
+ touchStartRef.current = { x: touch.clientX, y: touch.clientY };
203
+ longPressTimerRef.current = setTimeout(() => {
204
+ longPressTimerRef.current = null;
205
+ showContextMenuAt(touch.clientX, touch.clientY);
206
+ }, 500);
207
+ };
208
+ const handleTouchMove = (e) => {
209
+ if (!touchStartRef.current) return;
210
+ const touch = e.touches[0];
211
+ const dx = touch.clientX - touchStartRef.current.x;
212
+ const dy = touch.clientY - touchStartRef.current.y;
213
+ if (Math.abs(dx) > 10 || Math.abs(dy) > 10) cancelLongPress();
214
+ };
183
215
  const handleResizeStart = (e) => {
184
216
  e.preventDefault();
185
217
  e.stopPropagation();
@@ -229,6 +261,10 @@ var DraggableHeader = import_react.default.memo(
229
261
  style: headerStyle,
230
262
  className: `${column.className ?? ""} ${classNames?.header ?? ""} ${isPinned ? classNames?.pinnedHeader ?? "" : ""}`,
231
263
  onContextMenu: handleContextMenu,
264
+ onTouchStart: handleTouchStart,
265
+ onTouchMove: handleTouchMove,
266
+ onTouchEnd: cancelLongPress,
267
+ onTouchCancel: cancelLongPress,
232
268
  children: [
233
269
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
234
270
  "div",
@@ -1110,6 +1146,8 @@ var TableBody = ({
1110
1146
  "div",
1111
1147
  {
1112
1148
  "data-row-key": rowKey,
1149
+ "data-column-key": col.key,
1150
+ "data-bt-cell": "",
1113
1151
  "data-selected": isSelected || void 0,
1114
1152
  style: {
1115
1153
  position: "absolute",
@@ -1224,85 +1262,100 @@ var TableBody = ({
1224
1262
  style: {
1225
1263
  gridColumn: "1 / -1",
1226
1264
  gridRow: 2,
1227
- position: "sticky",
1228
- top: headerHeight,
1265
+ height: `${totalSize}px`,
1266
+ position: "relative",
1229
1267
  zIndex: 20,
1230
- boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
1268
+ pointerEvents: "none"
1231
1269
  },
1232
- children: pinnedTopData.map((row, rowIdx) => {
1233
- const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1234
- const isSelected = selectedKeySet.has(rk);
1235
- const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1236
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1237
- "div",
1238
- {
1239
- className: classNames?.pinnedRow ?? "",
1240
- style: {
1241
- display: "grid",
1242
- gridTemplateColumns: gridTemplateColumns ?? "",
1243
- minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1244
- ...styles?.pinnedRow
1245
- },
1246
- children: orderedColumns.map((col) => {
1247
- const cellValue = row[col.dataIndex];
1248
- const stickyOffset = columnOffsets.get(col.key);
1249
- const isPinned = Boolean(col.pinned);
1250
- let zIndex = 0;
1251
- if (col.key === "__select__" || col.key === "__expand__")
1252
- zIndex = 11;
1253
- else if (isPinned) zIndex = 2;
1254
- const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1255
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1256
- "div",
1257
- {
1258
- "data-row-key": rk,
1259
- "data-selected": isSelected || void 0,
1260
- style: {
1261
- position: isPinned ? "sticky" : "relative",
1262
- ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1263
- ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1264
- zIndex,
1265
- backgroundColor: pinnedRowBg,
1266
- ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1267
- },
1268
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1270
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1271
+ "div",
1272
+ {
1273
+ style: {
1274
+ position: "sticky",
1275
+ top: headerHeight,
1276
+ pointerEvents: "auto",
1277
+ boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
1278
+ },
1279
+ children: pinnedTopData.map((row, rowIdx) => {
1280
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1281
+ const isSelected = selectedKeySet.has(rk);
1282
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1283
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1284
+ "div",
1285
+ {
1286
+ className: classNames?.pinnedRow ?? "",
1287
+ style: {
1288
+ display: "grid",
1289
+ gridTemplateColumns: gridTemplateColumns ?? "",
1290
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1291
+ ...styles?.pinnedRow
1292
+ },
1293
+ children: orderedColumns.map((col) => {
1294
+ const cellValue = row[col.dataIndex];
1295
+ const stickyOffset = columnOffsets.get(col.key);
1296
+ const isPinned = Boolean(col.pinned);
1297
+ let zIndex = 0;
1298
+ if (col.key === "__select__" || col.key === "__expand__")
1299
+ zIndex = 11;
1300
+ else if (isPinned) zIndex = 2;
1301
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1302
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1269
1303
  "div",
1270
1304
  {
1305
+ "data-row-key": rk,
1306
+ "data-column-key": col.key,
1307
+ "data-bt-cell": "",
1308
+ "data-selected": isSelected || void 0,
1271
1309
  style: {
1272
- height: `${rowHeight}px`,
1273
- position: "relative"
1310
+ position: isPinned ? "sticky" : "relative",
1311
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1312
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1313
+ zIndex,
1314
+ backgroundColor: pinnedRowBg,
1315
+ backdropFilter: "blur(12px)",
1316
+ WebkitBackdropFilter: "blur(12px)",
1317
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1274
1318
  },
1275
1319
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1276
- Cell,
1320
+ "div",
1277
1321
  {
1278
- value: cellValue,
1279
- record: row,
1280
- column: col,
1281
- rowIndex: rowIdx,
1282
- classNames,
1283
- styles,
1284
- isSelected,
1285
- isExpanded,
1286
- rowSelection,
1287
- normalizedSelectedKeys,
1288
- rowKey: rk,
1289
- allData: allDataForSelection,
1290
- getRowKey,
1291
- accentColor,
1292
- isLoading: false,
1293
- recordFingerprint
1322
+ style: {
1323
+ height: `${rowHeight}px`,
1324
+ position: "relative"
1325
+ },
1326
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1327
+ Cell,
1328
+ {
1329
+ value: cellValue,
1330
+ record: row,
1331
+ column: col,
1332
+ rowIndex: rowIdx,
1333
+ classNames,
1334
+ styles,
1335
+ isSelected,
1336
+ isExpanded,
1337
+ rowSelection,
1338
+ normalizedSelectedKeys,
1339
+ rowKey: rk,
1340
+ allData: allDataForSelection,
1341
+ getRowKey,
1342
+ accentColor,
1343
+ isLoading: false,
1344
+ recordFingerprint
1345
+ }
1346
+ )
1294
1347
  }
1295
1348
  )
1296
- }
1297
- )
1298
- },
1299
- col.key
1300
- );
1301
- })
1302
- },
1303
- `pinned-top-${rk}`
1304
- );
1305
- })
1349
+ },
1350
+ col.key
1351
+ );
1352
+ })
1353
+ },
1354
+ `pinned-top-${rk}`
1355
+ );
1356
+ })
1357
+ }
1358
+ )
1306
1359
  }
1307
1360
  ),
1308
1361
  pinnedBottomData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -1311,86 +1364,103 @@ var TableBody = ({
1311
1364
  style: {
1312
1365
  gridColumn: "1 / -1",
1313
1366
  gridRow: 2,
1314
- position: "sticky",
1315
- bottom: 0,
1316
- alignSelf: "end",
1367
+ height: `${totalSize}px`,
1368
+ position: "relative",
1317
1369
  zIndex: 20,
1318
- boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
1370
+ pointerEvents: "none",
1371
+ display: "flex",
1372
+ flexDirection: "column"
1319
1373
  },
1320
- children: pinnedBottomData.map((row, rowIdx) => {
1321
- const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1322
- const isSelected = selectedKeySet.has(rk);
1323
- const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1324
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1325
- "div",
1326
- {
1327
- className: classNames?.pinnedRow ?? "",
1328
- style: {
1329
- display: "grid",
1330
- gridTemplateColumns: gridTemplateColumns ?? "",
1331
- minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1332
- ...styles?.pinnedRow
1333
- },
1334
- children: orderedColumns.map((col) => {
1335
- const cellValue = row[col.dataIndex];
1336
- const stickyOffset = columnOffsets.get(col.key);
1337
- const isPinned = Boolean(col.pinned);
1338
- let zIndex = 0;
1339
- if (col.key === "__select__" || col.key === "__expand__")
1340
- zIndex = 11;
1341
- else if (isPinned) zIndex = 2;
1342
- const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1343
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1344
- "div",
1345
- {
1346
- "data-row-key": rk,
1347
- "data-selected": isSelected || void 0,
1348
- style: {
1349
- position: isPinned ? "sticky" : "relative",
1350
- ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1351
- ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1352
- zIndex,
1353
- backgroundColor: pinnedRowBg,
1354
- ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1355
- },
1356
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1374
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1375
+ "div",
1376
+ {
1377
+ style: {
1378
+ marginTop: "auto",
1379
+ position: "sticky",
1380
+ bottom: 0,
1381
+ pointerEvents: "auto",
1382
+ boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
1383
+ },
1384
+ children: pinnedBottomData.map((row, rowIdx) => {
1385
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1386
+ const isSelected = selectedKeySet.has(rk);
1387
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1388
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1389
+ "div",
1390
+ {
1391
+ className: classNames?.pinnedRow ?? "",
1392
+ style: {
1393
+ display: "grid",
1394
+ gridTemplateColumns: gridTemplateColumns ?? "",
1395
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1396
+ ...styles?.pinnedRow
1397
+ },
1398
+ children: orderedColumns.map((col) => {
1399
+ const cellValue = row[col.dataIndex];
1400
+ const stickyOffset = columnOffsets.get(col.key);
1401
+ const isPinned = Boolean(col.pinned);
1402
+ let zIndex = 0;
1403
+ if (col.key === "__select__" || col.key === "__expand__")
1404
+ zIndex = 11;
1405
+ else if (isPinned) zIndex = 2;
1406
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1407
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1357
1408
  "div",
1358
1409
  {
1410
+ "data-row-key": rk,
1411
+ "data-column-key": col.key,
1412
+ "data-bt-cell": "",
1413
+ "data-selected": isSelected || void 0,
1359
1414
  style: {
1360
- height: `${rowHeight}px`,
1361
- position: "relative"
1415
+ position: isPinned ? "sticky" : "relative",
1416
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1417
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1418
+ zIndex,
1419
+ backgroundColor: pinnedRowBg,
1420
+ backdropFilter: "blur(12px)",
1421
+ WebkitBackdropFilter: "blur(12px)",
1422
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1362
1423
  },
1363
1424
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1364
- Cell,
1425
+ "div",
1365
1426
  {
1366
- value: cellValue,
1367
- record: row,
1368
- column: col,
1369
- rowIndex: rowIdx,
1370
- classNames,
1371
- styles,
1372
- isSelected,
1373
- isExpanded,
1374
- rowSelection,
1375
- normalizedSelectedKeys,
1376
- rowKey: rk,
1377
- allData: allDataForSelection,
1378
- getRowKey,
1379
- accentColor,
1380
- isLoading: false,
1381
- recordFingerprint
1427
+ style: {
1428
+ height: `${rowHeight}px`,
1429
+ position: "relative"
1430
+ },
1431
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1432
+ Cell,
1433
+ {
1434
+ value: cellValue,
1435
+ record: row,
1436
+ column: col,
1437
+ rowIndex: rowIdx,
1438
+ classNames,
1439
+ styles,
1440
+ isSelected,
1441
+ isExpanded,
1442
+ rowSelection,
1443
+ normalizedSelectedKeys,
1444
+ rowKey: rk,
1445
+ allData: allDataForSelection,
1446
+ getRowKey,
1447
+ accentColor,
1448
+ isLoading: false,
1449
+ recordFingerprint
1450
+ }
1451
+ )
1382
1452
  }
1383
1453
  )
1384
- }
1385
- )
1386
- },
1387
- col.key
1388
- );
1389
- })
1390
- },
1391
- `pinned-bottom-${rk}`
1392
- );
1393
- })
1454
+ },
1455
+ col.key
1456
+ );
1457
+ })
1458
+ },
1459
+ `pinned-bottom-${rk}`
1460
+ );
1461
+ })
1462
+ }
1463
+ )
1394
1464
  }
1395
1465
  )
1396
1466
  ] });
@@ -1428,6 +1498,7 @@ function BoltTable({
1428
1498
  onColumnHide,
1429
1499
  rowSelection,
1430
1500
  rowPinning,
1501
+ onRowPin,
1431
1502
  expandable,
1432
1503
  rowKey = "id",
1433
1504
  onEndReached,
@@ -1997,6 +2068,44 @@ function BoltTable({
1997
2068
  }, [processedData, rowPinning, getRowKey]);
1998
2069
  const pinnedTopHeight = pinnedTopRows.length * rowHeight;
1999
2070
  const pinnedBottomHeight = pinnedBottomRows.length * rowHeight;
2071
+ const pinnedTopKeySet = (0, import_react4.useMemo)(
2072
+ () => new Set((rowPinning?.top ?? []).map(String)),
2073
+ [rowPinning?.top]
2074
+ );
2075
+ const pinnedBottomKeySet = (0, import_react4.useMemo)(
2076
+ () => new Set((rowPinning?.bottom ?? []).map(String)),
2077
+ [rowPinning?.bottom]
2078
+ );
2079
+ const [cellContextMenu, setCellContextMenu] = (0, import_react4.useState)(null);
2080
+ const cellMenuRef = (0, import_react4.useRef)(null);
2081
+ const cellLongPressTimer = (0, import_react4.useRef)(
2082
+ null
2083
+ );
2084
+ const cellTouchStart = (0, import_react4.useRef)(null);
2085
+ const cancelCellLongPress = (0, import_react4.useCallback)(() => {
2086
+ if (cellLongPressTimer.current) {
2087
+ clearTimeout(cellLongPressTimer.current);
2088
+ cellLongPressTimer.current = null;
2089
+ }
2090
+ cellTouchStart.current = null;
2091
+ }, []);
2092
+ import_react4.default.useEffect(() => {
2093
+ if (!cellContextMenu) return;
2094
+ const close = (e) => {
2095
+ if (cellMenuRef.current && cellMenuRef.current.contains(e.target))
2096
+ return;
2097
+ setCellContextMenu(null);
2098
+ };
2099
+ const onKey = (e) => {
2100
+ if (e.key === "Escape") setCellContextMenu(null);
2101
+ };
2102
+ document.addEventListener("mousedown", close);
2103
+ document.addEventListener("keydown", onKey);
2104
+ return () => {
2105
+ document.removeEventListener("mousedown", close);
2106
+ document.removeEventListener("keydown", onKey);
2107
+ };
2108
+ }, [cellContextMenu]);
2000
2109
  const columnFiltersKey = Object.keys(columnFilters).sort().map((k) => `${k}:${columnFilters[k]}`).join("|");
2001
2110
  import_react4.default.useEffect(() => {
2002
2111
  tableAreaRef.current?.scrollTo({ top: 0 });
@@ -2358,6 +2467,64 @@ function BoltTable({
2358
2467
  position: "relative",
2359
2468
  ...isEmpty ? { height: "100%" } : {}
2360
2469
  },
2470
+ onContextMenu: (e) => {
2471
+ const cell = e.target.closest("[data-bt-cell]");
2472
+ if (!cell) return;
2473
+ const rk = cell.dataset.rowKey;
2474
+ const ck = cell.dataset.columnKey;
2475
+ if (!rk || !ck) return;
2476
+ const col = freshOrderedColumns.find(
2477
+ (c) => c.key === ck
2478
+ );
2479
+ const hasCopy = col?.copy;
2480
+ const hasRowPin = !!onRowPin;
2481
+ if (!hasCopy && !hasRowPin) return;
2482
+ e.preventDefault();
2483
+ setCellContextMenu({
2484
+ x: Math.min(e.clientX, window.innerWidth - 200),
2485
+ y: Math.min(e.clientY, window.innerHeight - 200),
2486
+ rowKey: rk,
2487
+ columnKey: ck
2488
+ });
2489
+ },
2490
+ onTouchStart: (e) => {
2491
+ cancelCellLongPress();
2492
+ const cell = e.target.closest("[data-bt-cell]");
2493
+ if (!cell) return;
2494
+ const touch = e.touches[0];
2495
+ cellTouchStart.current = {
2496
+ x: touch.clientX,
2497
+ y: touch.clientY
2498
+ };
2499
+ const rk = cell.dataset.rowKey;
2500
+ const ck = cell.dataset.columnKey;
2501
+ cellLongPressTimer.current = setTimeout(() => {
2502
+ cellLongPressTimer.current = null;
2503
+ if (!rk || !ck) return;
2504
+ const col = freshOrderedColumns.find(
2505
+ (c) => c.key === ck
2506
+ );
2507
+ const hasCopy = col?.copy;
2508
+ const hasRowPin = !!onRowPin;
2509
+ if (!hasCopy && !hasRowPin) return;
2510
+ setCellContextMenu({
2511
+ x: Math.min(touch.clientX, window.innerWidth - 200),
2512
+ y: Math.min(touch.clientY, window.innerHeight - 200),
2513
+ rowKey: rk,
2514
+ columnKey: ck
2515
+ });
2516
+ }, 500);
2517
+ },
2518
+ onTouchMove: (e) => {
2519
+ if (!cellTouchStart.current) return;
2520
+ const touch = e.touches[0];
2521
+ const dx = touch.clientX - cellTouchStart.current.x;
2522
+ const dy = touch.clientY - cellTouchStart.current.y;
2523
+ if (Math.abs(dx) > 10 || Math.abs(dy) > 10)
2524
+ cancelCellLongPress();
2525
+ },
2526
+ onTouchEnd: cancelCellLongPress,
2527
+ onTouchCancel: cancelCellLongPress,
2361
2528
  children: [
2362
2529
  orderedColumns.map((column, visualIndex) => {
2363
2530
  if (column.key === "__select__" && rowSelection) {
@@ -2856,7 +3023,166 @@ function BoltTable({
2856
3023
  }
2857
3024
  ),
2858
3025
  document.body
2859
- )
3026
+ ),
3027
+ cellContextMenu && typeof document !== "undefined" && (() => {
3028
+ const menuCol = freshOrderedColumns.find(
3029
+ (c) => c.key === cellContextMenu.columnKey
3030
+ );
3031
+ const isPinnedTop = pinnedTopKeySet.has(cellContextMenu.rowKey);
3032
+ const isPinnedBottom = pinnedBottomKeySet.has(
3033
+ cellContextMenu.rowKey
3034
+ );
3035
+ const hasCopy = menuCol?.copy;
3036
+ const hasRowPin = !!onRowPin;
3037
+ let menuRecord;
3038
+ let menuRowIndex = 0;
3039
+ const allRows = [
3040
+ ...pinnedTopRows,
3041
+ ...displayData,
3042
+ ...pinnedBottomRows
3043
+ ];
3044
+ for (let i = 0; i < allRows.length; i++) {
3045
+ const rk = getRowKey(allRows[i], i);
3046
+ if (rk === cellContextMenu.rowKey) {
3047
+ menuRecord = allRows[i];
3048
+ menuRowIndex = i;
3049
+ break;
3050
+ }
3051
+ }
3052
+ const menuValue = menuRecord && menuCol ? menuRecord[menuCol.dataIndex] : void 0;
3053
+ const btnStyle = {
3054
+ display: "flex",
3055
+ width: "100%",
3056
+ alignItems: "center",
3057
+ gap: 8,
3058
+ background: "none",
3059
+ border: "none",
3060
+ padding: "6px 12px",
3061
+ fontSize: 12,
3062
+ cursor: "pointer",
3063
+ color: "inherit",
3064
+ whiteSpace: "nowrap"
3065
+ };
3066
+ return (0, import_react_dom2.createPortal)(
3067
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3068
+ "div",
3069
+ {
3070
+ ref: cellMenuRef,
3071
+ style: {
3072
+ position: "fixed",
3073
+ top: cellContextMenu.y,
3074
+ left: cellContextMenu.x,
3075
+ zIndex: 99999,
3076
+ minWidth: 170,
3077
+ borderRadius: 8,
3078
+ border: "1px solid rgba(128,128,128,0.2)",
3079
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3080
+ backdropFilter: "blur(16px)",
3081
+ WebkitBackdropFilter: "blur(16px)",
3082
+ backgroundColor: "rgba(128,128,128,0.08)",
3083
+ padding: "4px 0",
3084
+ fontSize: 10
3085
+ },
3086
+ children: [
3087
+ hasRowPin && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3088
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3089
+ "button",
3090
+ {
3091
+ "data-bt-ctx-item": true,
3092
+ style: btnStyle,
3093
+ onClick: () => {
3094
+ onRowPin(
3095
+ cellContextMenu.rowKey,
3096
+ isPinnedTop ? false : "top"
3097
+ );
3098
+ setCellContextMenu(null);
3099
+ },
3100
+ children: [
3101
+ isPinnedTop ? icons?.pinOff ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3102
+ PinOffIcon,
3103
+ {
3104
+ style: { width: 14, height: 14, flexShrink: 0 }
3105
+ }
3106
+ ) : icons?.pin ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3107
+ PinIcon,
3108
+ {
3109
+ style: { width: 14, height: 14, flexShrink: 0 }
3110
+ }
3111
+ ),
3112
+ isPinnedTop ? "Unpin Row from Top" : "Pin Row to Top"
3113
+ ]
3114
+ }
3115
+ ),
3116
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3117
+ "button",
3118
+ {
3119
+ "data-bt-ctx-item": true,
3120
+ style: btnStyle,
3121
+ onClick: () => {
3122
+ onRowPin(
3123
+ cellContextMenu.rowKey,
3124
+ isPinnedBottom ? false : "bottom"
3125
+ );
3126
+ setCellContextMenu(null);
3127
+ },
3128
+ children: [
3129
+ isPinnedBottom ? icons?.pinOff ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3130
+ PinOffIcon,
3131
+ {
3132
+ style: { width: 14, height: 14, flexShrink: 0 }
3133
+ }
3134
+ ) : icons?.pin ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3135
+ PinIcon,
3136
+ {
3137
+ style: {
3138
+ width: 14,
3139
+ height: 14,
3140
+ flexShrink: 0,
3141
+ transform: "rotate(180deg)"
3142
+ }
3143
+ }
3144
+ ),
3145
+ isPinnedBottom ? "Unpin Row from Bottom" : "Pin Row to Bottom"
3146
+ ]
3147
+ }
3148
+ )
3149
+ ] }),
3150
+ hasRowPin && hasCopy && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3151
+ "div",
3152
+ {
3153
+ style: {
3154
+ borderTop: "1px solid rgba(128,128,128,0.2)",
3155
+ margin: "4px 0"
3156
+ }
3157
+ }
3158
+ ),
3159
+ hasCopy && menuRecord && menuCol && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3160
+ "button",
3161
+ {
3162
+ "data-bt-ctx-item": true,
3163
+ style: btnStyle,
3164
+ onClick: () => {
3165
+ const text = typeof menuCol.copy === "function" ? menuCol.copy(menuValue, menuRecord, menuRowIndex) : String(menuValue ?? "");
3166
+ navigator.clipboard?.writeText(text);
3167
+ setCellContextMenu(null);
3168
+ },
3169
+ children: [
3170
+ icons?.copy ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3171
+ CopyIcon,
3172
+ {
3173
+ style: { width: 14, height: 14, flexShrink: 0 }
3174
+ }
3175
+ ),
3176
+ "Copy"
3177
+ ]
3178
+ }
3179
+ )
3180
+ ]
3181
+ }
3182
+ ),
3183
+ document.body
3184
+ );
3185
+ })()
2860
3186
  ] });
2861
3187
  }
2862
3188
  // Annotate the CommonJS export names for ESM import in node: