bolt-table 0.1.13 → 0.1.15

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",
@@ -680,9 +716,6 @@ var DraggableHeader = import_react.default.memo(
680
716
  )
681
717
  ] });
682
718
  },
683
- // ── Custom memo comparator ─────────────────────────────────────────────────
684
- // Only re-render when props that actually affect this header's output change.
685
- // This prevents a sort/filter change on one column from re-rendering all others.
686
719
  (prevProps, nextProps) => {
687
720
  return prevProps.column.width === nextProps.column.width && prevProps.column.key === nextProps.column.key && prevProps.column.pinned === nextProps.column.pinned && prevProps.column.sortable === nextProps.column.sortable && prevProps.column.filterable === nextProps.column.filterable && prevProps.column.sorter === nextProps.column.sorter && prevProps.column.filterFn === nextProps.column.filterFn && prevProps.visualIndex === nextProps.visualIndex && prevProps.stickyOffset === nextProps.stickyOffset && prevProps.isLastColumn === nextProps.isLastColumn && prevProps.sortDirection === nextProps.sortDirection && prevProps.filterValue === nextProps.filterValue;
688
721
  }
@@ -972,11 +1005,6 @@ var Cell = import_react3.default.memo(
972
1005
  }
973
1006
  );
974
1007
  },
975
- // ── Custom memo comparator ─────────────────────────────────────────────────
976
- // Minimizes re-renders:
977
- // - __select__ cells: re-render only when selection changes
978
- // - __expand__ cells: re-render only when expand state changes
979
- // - Normal cells: re-render only when value or rowIndex changes
980
1008
  (prev, next) => {
981
1009
  if (prev.isLoading !== next.isLoading) return false;
982
1010
  if (prev.column.key === "__select__") {
@@ -1095,62 +1123,52 @@ var TableBody = ({
1095
1123
  const cellValue = row[col.dataIndex];
1096
1124
  const isRowShimmer = isLoading || rowKey.startsWith("__shimmer_");
1097
1125
  const recordFingerprint = hasRender && !isRowShimmer ? JSON.stringify(row) : void 0;
1098
- return (
1099
- /*
1100
- * Row wrapper div:
1101
- * - data-row-key: used by BoltTable's DOM-based hover system
1102
- * (mouseover reads this attribute to apply hover styles
1103
- * across all column divs for the same row simultaneously)
1104
- * - data-selected: presence/absence attribute consumed by the
1105
- * CSS injected by BoltTable for selected row background
1106
- * - Absolute positioned at virtualRow.start for virtualization
1107
- * - Height = virtualRow.size (includes expanded row height)
1108
- */
1109
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1110
- "div",
1111
- {
1112
- "data-row-key": rowKey,
1113
- "data-selected": isSelected || void 0,
1114
- style: {
1115
- position: "absolute",
1116
- top: `${virtualRow.start}px`,
1117
- left: 0,
1118
- right: 0,
1119
- height: `${virtualRow.size}px`
1120
- },
1121
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1122
- "div",
1123
- {
1124
- style: {
1125
- height: `${rowHeight}px`,
1126
- position: "relative"
1127
- },
1128
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1129
- Cell,
1130
- {
1131
- value: cellValue,
1132
- record: row,
1133
- column: col,
1134
- rowIndex: virtualRow.index,
1135
- classNames,
1136
- styles,
1137
- isSelected,
1138
- isExpanded,
1139
- rowSelection,
1140
- normalizedSelectedKeys,
1141
- rowKey,
1142
- allData: allDataForSelection,
1143
- getRowKey,
1144
- accentColor,
1145
- isLoading: isRowShimmer,
1146
- recordFingerprint
1147
- }
1148
- )
1149
- }
1150
- )
1126
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1127
+ "div",
1128
+ {
1129
+ "data-row-key": rowKey,
1130
+ "data-column-key": col.key,
1131
+ "data-bt-cell": "",
1132
+ "data-selected": isSelected || void 0,
1133
+ style: {
1134
+ position: "absolute",
1135
+ top: `${virtualRow.start}px`,
1136
+ left: 0,
1137
+ right: 0,
1138
+ height: `${virtualRow.size}px`
1151
1139
  },
1152
- `${rowKey}-${col.key}`
1153
- )
1140
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1141
+ "div",
1142
+ {
1143
+ style: {
1144
+ height: `${rowHeight}px`,
1145
+ position: "relative"
1146
+ },
1147
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1148
+ Cell,
1149
+ {
1150
+ value: cellValue,
1151
+ record: row,
1152
+ column: col,
1153
+ rowIndex: virtualRow.index,
1154
+ classNames,
1155
+ styles,
1156
+ isSelected,
1157
+ isExpanded,
1158
+ rowSelection,
1159
+ normalizedSelectedKeys,
1160
+ rowKey,
1161
+ allData: allDataForSelection,
1162
+ getRowKey,
1163
+ accentColor,
1164
+ isLoading: isRowShimmer,
1165
+ recordFingerprint
1166
+ }
1167
+ )
1168
+ }
1169
+ )
1170
+ },
1171
+ `${rowKey}-${col.key}`
1154
1172
  );
1155
1173
  })
1156
1174
  },
@@ -1166,8 +1184,6 @@ var TableBody = ({
1166
1184
  height: `${totalSize}px`,
1167
1185
  position: "relative",
1168
1186
  zIndex: 15,
1169
- // pointerEvents: none on the overlay so hover/click pass through
1170
- // to the cells below for rows that are NOT expanded
1171
1187
  pointerEvents: "none"
1172
1188
  },
1173
1189
  children: virtualItems.map((virtualRow) => {
@@ -1199,7 +1215,6 @@ var TableBody = ({
1199
1215
  {
1200
1216
  style: {
1201
1217
  position: "absolute",
1202
- // Position immediately below the row's base height
1203
1218
  top: virtualRow.start + rowHeight,
1204
1219
  left: 0,
1205
1220
  right: 0
@@ -1224,85 +1239,100 @@ var TableBody = ({
1224
1239
  style: {
1225
1240
  gridColumn: "1 / -1",
1226
1241
  gridRow: 2,
1227
- position: "sticky",
1228
- top: headerHeight,
1242
+ height: `${totalSize}px`,
1243
+ position: "relative",
1229
1244
  zIndex: 20,
1230
- boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
1245
+ pointerEvents: "none"
1231
1246
  },
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)(
1247
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1248
+ "div",
1249
+ {
1250
+ style: {
1251
+ position: "sticky",
1252
+ top: headerHeight,
1253
+ pointerEvents: "auto",
1254
+ boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
1255
+ },
1256
+ children: pinnedTopData.map((row, rowIdx) => {
1257
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1258
+ const isSelected = selectedKeySet.has(rk);
1259
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1260
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1261
+ "div",
1262
+ {
1263
+ className: classNames?.pinnedRow ?? "",
1264
+ style: {
1265
+ display: "grid",
1266
+ gridTemplateColumns: gridTemplateColumns ?? "",
1267
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1268
+ ...styles?.pinnedRow
1269
+ },
1270
+ children: orderedColumns.map((col) => {
1271
+ const cellValue = row[col.dataIndex];
1272
+ const stickyOffset = columnOffsets.get(col.key);
1273
+ const isPinned = Boolean(col.pinned);
1274
+ let zIndex = 0;
1275
+ if (col.key === "__select__" || col.key === "__expand__")
1276
+ zIndex = 11;
1277
+ else if (isPinned) zIndex = 2;
1278
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1279
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1269
1280
  "div",
1270
1281
  {
1282
+ "data-row-key": rk,
1283
+ "data-column-key": col.key,
1284
+ "data-bt-cell": "",
1285
+ "data-selected": isSelected || void 0,
1271
1286
  style: {
1272
- height: `${rowHeight}px`,
1273
- position: "relative"
1287
+ position: isPinned ? "sticky" : "relative",
1288
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1289
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1290
+ zIndex,
1291
+ backgroundColor: pinnedRowBg,
1292
+ backdropFilter: "blur(12px)",
1293
+ WebkitBackdropFilter: "blur(12px)",
1294
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1274
1295
  },
1275
1296
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1276
- Cell,
1297
+ "div",
1277
1298
  {
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
1299
+ style: {
1300
+ height: `${rowHeight}px`,
1301
+ position: "relative"
1302
+ },
1303
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1304
+ Cell,
1305
+ {
1306
+ value: cellValue,
1307
+ record: row,
1308
+ column: col,
1309
+ rowIndex: rowIdx,
1310
+ classNames,
1311
+ styles,
1312
+ isSelected,
1313
+ isExpanded,
1314
+ rowSelection,
1315
+ normalizedSelectedKeys,
1316
+ rowKey: rk,
1317
+ allData: allDataForSelection,
1318
+ getRowKey,
1319
+ accentColor,
1320
+ isLoading: false,
1321
+ recordFingerprint
1322
+ }
1323
+ )
1294
1324
  }
1295
1325
  )
1296
- }
1297
- )
1298
- },
1299
- col.key
1300
- );
1301
- })
1302
- },
1303
- `pinned-top-${rk}`
1304
- );
1305
- })
1326
+ },
1327
+ col.key
1328
+ );
1329
+ })
1330
+ },
1331
+ `pinned-top-${rk}`
1332
+ );
1333
+ })
1334
+ }
1335
+ )
1306
1336
  }
1307
1337
  ),
1308
1338
  pinnedBottomData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -1311,86 +1341,103 @@ var TableBody = ({
1311
1341
  style: {
1312
1342
  gridColumn: "1 / -1",
1313
1343
  gridRow: 2,
1314
- position: "sticky",
1315
- bottom: 0,
1316
- alignSelf: "end",
1344
+ height: `${totalSize}px`,
1345
+ position: "relative",
1317
1346
  zIndex: 20,
1318
- boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
1347
+ pointerEvents: "none",
1348
+ display: "flex",
1349
+ flexDirection: "column"
1319
1350
  },
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)(
1351
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1352
+ "div",
1353
+ {
1354
+ style: {
1355
+ marginTop: "auto",
1356
+ position: "sticky",
1357
+ bottom: 0,
1358
+ pointerEvents: "auto",
1359
+ boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
1360
+ },
1361
+ children: pinnedBottomData.map((row, rowIdx) => {
1362
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1363
+ const isSelected = selectedKeySet.has(rk);
1364
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1365
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1366
+ "div",
1367
+ {
1368
+ className: classNames?.pinnedRow ?? "",
1369
+ style: {
1370
+ display: "grid",
1371
+ gridTemplateColumns: gridTemplateColumns ?? "",
1372
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1373
+ ...styles?.pinnedRow
1374
+ },
1375
+ children: orderedColumns.map((col) => {
1376
+ const cellValue = row[col.dataIndex];
1377
+ const stickyOffset = columnOffsets.get(col.key);
1378
+ const isPinned = Boolean(col.pinned);
1379
+ let zIndex = 0;
1380
+ if (col.key === "__select__" || col.key === "__expand__")
1381
+ zIndex = 11;
1382
+ else if (isPinned) zIndex = 2;
1383
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1357
1385
  "div",
1358
1386
  {
1387
+ "data-row-key": rk,
1388
+ "data-column-key": col.key,
1389
+ "data-bt-cell": "",
1390
+ "data-selected": isSelected || void 0,
1359
1391
  style: {
1360
- height: `${rowHeight}px`,
1361
- position: "relative"
1392
+ position: isPinned ? "sticky" : "relative",
1393
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1394
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1395
+ zIndex,
1396
+ backgroundColor: pinnedRowBg,
1397
+ backdropFilter: "blur(12px)",
1398
+ WebkitBackdropFilter: "blur(12px)",
1399
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1362
1400
  },
1363
1401
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1364
- Cell,
1402
+ "div",
1365
1403
  {
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
1404
+ style: {
1405
+ height: `${rowHeight}px`,
1406
+ position: "relative"
1407
+ },
1408
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1409
+ Cell,
1410
+ {
1411
+ value: cellValue,
1412
+ record: row,
1413
+ column: col,
1414
+ rowIndex: rowIdx,
1415
+ classNames,
1416
+ styles,
1417
+ isSelected,
1418
+ isExpanded,
1419
+ rowSelection,
1420
+ normalizedSelectedKeys,
1421
+ rowKey: rk,
1422
+ allData: allDataForSelection,
1423
+ getRowKey,
1424
+ accentColor,
1425
+ isLoading: false,
1426
+ recordFingerprint
1427
+ }
1428
+ )
1382
1429
  }
1383
1430
  )
1384
- }
1385
- )
1386
- },
1387
- col.key
1388
- );
1389
- })
1390
- },
1391
- `pinned-bottom-${rk}`
1392
- );
1393
- })
1431
+ },
1432
+ col.key
1433
+ );
1434
+ })
1435
+ },
1436
+ `pinned-bottom-${rk}`
1437
+ );
1438
+ })
1439
+ }
1440
+ )
1394
1441
  }
1395
1442
  )
1396
1443
  ] });
@@ -1428,6 +1475,7 @@ function BoltTable({
1428
1475
  onColumnHide,
1429
1476
  rowSelection,
1430
1477
  rowPinning,
1478
+ onRowPin,
1431
1479
  expandable,
1432
1480
  rowKey = "id",
1433
1481
  onEndReached,
@@ -1774,7 +1822,6 @@ function BoltTable({
1774
1822
  areaRect,
1775
1823
  headerLeftInContent,
1776
1824
  40,
1777
- // minimum column width
1778
1825
  scrollTop,
1779
1826
  scrollLeft,
1780
1827
  headerLeftInContent + startWidth
@@ -1997,6 +2044,44 @@ function BoltTable({
1997
2044
  }, [processedData, rowPinning, getRowKey]);
1998
2045
  const pinnedTopHeight = pinnedTopRows.length * rowHeight;
1999
2046
  const pinnedBottomHeight = pinnedBottomRows.length * rowHeight;
2047
+ const pinnedTopKeySet = (0, import_react4.useMemo)(
2048
+ () => new Set((rowPinning?.top ?? []).map(String)),
2049
+ [rowPinning?.top]
2050
+ );
2051
+ const pinnedBottomKeySet = (0, import_react4.useMemo)(
2052
+ () => new Set((rowPinning?.bottom ?? []).map(String)),
2053
+ [rowPinning?.bottom]
2054
+ );
2055
+ const [cellContextMenu, setCellContextMenu] = (0, import_react4.useState)(null);
2056
+ const cellMenuRef = (0, import_react4.useRef)(null);
2057
+ const cellLongPressTimer = (0, import_react4.useRef)(
2058
+ null
2059
+ );
2060
+ const cellTouchStart = (0, import_react4.useRef)(null);
2061
+ const cancelCellLongPress = (0, import_react4.useCallback)(() => {
2062
+ if (cellLongPressTimer.current) {
2063
+ clearTimeout(cellLongPressTimer.current);
2064
+ cellLongPressTimer.current = null;
2065
+ }
2066
+ cellTouchStart.current = null;
2067
+ }, []);
2068
+ import_react4.default.useEffect(() => {
2069
+ if (!cellContextMenu) return;
2070
+ const close = (e) => {
2071
+ if (cellMenuRef.current && cellMenuRef.current.contains(e.target))
2072
+ return;
2073
+ setCellContextMenu(null);
2074
+ };
2075
+ const onKey = (e) => {
2076
+ if (e.key === "Escape") setCellContextMenu(null);
2077
+ };
2078
+ document.addEventListener("mousedown", close);
2079
+ document.addEventListener("keydown", onKey);
2080
+ return () => {
2081
+ document.removeEventListener("mousedown", close);
2082
+ document.removeEventListener("keydown", onKey);
2083
+ };
2084
+ }, [cellContextMenu]);
2000
2085
  const columnFiltersKey = Object.keys(columnFilters).sort().map((k) => `${k}:${columnFilters[k]}`).join("|");
2001
2086
  import_react4.default.useEffect(() => {
2002
2087
  tableAreaRef.current?.scrollTo({ top: 0 });
@@ -2212,354 +2297,382 @@ function BoltTable({
2212
2297
  flexGrow: 0
2213
2298
  } : { flex: "1 1 0%" }
2214
2299
  },
2215
- children: layoutLoading ? (
2216
- /*
2217
- * ── Layout loading skeleton ──────────────────────────────────
2218
- * Shown when layoutLoading=true. Renders real column headers
2219
- * (based on orderedColumns) alongside shimmer body rows.
2220
- * Used for initial page load when column widths are not yet known.
2221
- */
2222
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2223
- "div",
2224
- {
2225
- style: {
2226
- position: "absolute",
2227
- inset: 0,
2228
- overflow: "auto",
2229
- contain: "layout paint"
2230
- },
2231
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2300
+ children: layoutLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2301
+ "div",
2302
+ {
2303
+ style: {
2304
+ position: "absolute",
2305
+ inset: 0,
2306
+ overflow: "auto",
2307
+ contain: "layout paint"
2308
+ },
2309
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2310
+ "div",
2311
+ {
2312
+ style: {
2313
+ display: "grid",
2314
+ gridTemplateColumns,
2315
+ gridTemplateRows: "36px auto",
2316
+ minWidth: `${totalTableWidth}px`,
2317
+ width: "100%",
2318
+ position: "relative"
2319
+ },
2320
+ children: [
2321
+ orderedColumns.map((column) => {
2322
+ const isPinned = !!column.pinned;
2323
+ const offset = columnOffsets.get(column.key);
2324
+ const isSystem = column.key === "__select__" || column.key === "__expand__";
2325
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2326
+ "div",
2327
+ {
2328
+ className: isPinned ? classNames.pinnedHeader ?? "" : classNames.header ?? "",
2329
+ style: {
2330
+ display: "flex",
2331
+ height: 36,
2332
+ alignItems: "center",
2333
+ overflow: "hidden",
2334
+ textOverflow: "ellipsis",
2335
+ whiteSpace: "nowrap",
2336
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2337
+ backdropFilter: "blur(8px)",
2338
+ position: "sticky",
2339
+ top: 0,
2340
+ zIndex: isPinned ? 13 : 10,
2341
+ ...isPinned ? {
2342
+ [column.pinned]: offset ?? 0,
2343
+ ...styles.pinnedHeader
2344
+ } : styles.header,
2345
+ paddingLeft: isSystem ? 0 : 8,
2346
+ paddingRight: isSystem ? 0 : 8
2347
+ }
2348
+ },
2349
+ column.key
2350
+ );
2351
+ }),
2352
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { gridColumn: "1 / -1" }, children: Array.from({ length: shimmerCount }).map((_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2353
+ "div",
2354
+ {
2355
+ style: {
2356
+ display: "grid",
2357
+ gridTemplateColumns,
2358
+ height: rowHeight
2359
+ },
2360
+ children: orderedColumns.map((column, colIndex) => {
2361
+ const isPinned = !!column.pinned;
2362
+ const offset = columnOffsets.get(column.key);
2363
+ const isSystem = column.key === "__select__" || column.key === "__expand__";
2364
+ const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
2365
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2366
+ "div",
2367
+ {
2368
+ className: isPinned ? classNames.pinnedCell ?? "" : "",
2369
+ style: {
2370
+ display: "flex",
2371
+ alignItems: "center",
2372
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2373
+ ...isPinned ? {
2374
+ position: "sticky",
2375
+ [column.pinned]: offset ?? 0,
2376
+ zIndex: 5,
2377
+ ...styles.pinnedCell
2378
+ } : {},
2379
+ paddingLeft: isSystem ? 0 : 8,
2380
+ paddingRight: isSystem ? 0 : 8,
2381
+ justifyContent: isSystem ? "center" : void 0
2382
+ },
2383
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2384
+ "div",
2385
+ {
2386
+ style: {
2387
+ backgroundColor: "rgba(100, 116, 139, 0.15)",
2388
+ animation: "bt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
2389
+ borderRadius: isSystem ? 3 : 4,
2390
+ height: isSystem ? 16 : 14,
2391
+ width: isSystem ? 16 : `${widthPercent}%`,
2392
+ animationDelay: `${(rowIndex * 7 + colIndex) * 50}ms`
2393
+ }
2394
+ }
2395
+ )
2396
+ },
2397
+ column.key
2398
+ );
2399
+ })
2400
+ },
2401
+ rowIndex
2402
+ )) })
2403
+ ]
2404
+ }
2405
+ )
2406
+ }
2407
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2408
+ "div",
2409
+ {
2410
+ ref: tableAreaCallbackRef,
2411
+ style: {
2412
+ position: "absolute",
2413
+ inset: 0,
2414
+ overflow: "auto",
2415
+ contain: "layout paint"
2416
+ },
2417
+ children: [
2418
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ResizeOverlay_default, { ref: resizeOverlayRef, accentColor }),
2419
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2232
2420
  "div",
2233
2421
  {
2234
2422
  style: {
2235
2423
  display: "grid",
2236
2424
  gridTemplateColumns,
2237
- gridTemplateRows: "36px auto",
2425
+ gridTemplateRows: isEmpty ? "36px 1fr" : `36px ${virtualTotalSize}px`,
2238
2426
  minWidth: `${totalTableWidth}px`,
2239
2427
  width: "100%",
2240
- position: "relative"
2428
+ position: "relative",
2429
+ ...isEmpty ? { height: "100%" } : {}
2430
+ },
2431
+ onContextMenu: (e) => {
2432
+ const cell = e.target.closest("[data-bt-cell]");
2433
+ if (!cell) return;
2434
+ const rk = cell.dataset.rowKey;
2435
+ const ck = cell.dataset.columnKey;
2436
+ if (!rk || !ck) return;
2437
+ const col = freshOrderedColumns.find(
2438
+ (c) => c.key === ck
2439
+ );
2440
+ const hasCopy = col?.copy;
2441
+ const hasRowPin = !!onRowPin;
2442
+ if (!hasCopy && !hasRowPin) return;
2443
+ e.preventDefault();
2444
+ setCellContextMenu({
2445
+ x: Math.min(e.clientX, window.innerWidth - 200),
2446
+ y: Math.min(e.clientY, window.innerHeight - 200),
2447
+ rowKey: rk,
2448
+ columnKey: ck
2449
+ });
2450
+ },
2451
+ onTouchStart: (e) => {
2452
+ cancelCellLongPress();
2453
+ const cell = e.target.closest("[data-bt-cell]");
2454
+ if (!cell) return;
2455
+ const touch = e.touches[0];
2456
+ cellTouchStart.current = {
2457
+ x: touch.clientX,
2458
+ y: touch.clientY
2459
+ };
2460
+ const rk = cell.dataset.rowKey;
2461
+ const ck = cell.dataset.columnKey;
2462
+ cellLongPressTimer.current = setTimeout(() => {
2463
+ cellLongPressTimer.current = null;
2464
+ if (!rk || !ck) return;
2465
+ const col = freshOrderedColumns.find(
2466
+ (c) => c.key === ck
2467
+ );
2468
+ const hasCopy = col?.copy;
2469
+ const hasRowPin = !!onRowPin;
2470
+ if (!hasCopy && !hasRowPin) return;
2471
+ setCellContextMenu({
2472
+ x: Math.min(touch.clientX, window.innerWidth - 200),
2473
+ y: Math.min(touch.clientY, window.innerHeight - 200),
2474
+ rowKey: rk,
2475
+ columnKey: ck
2476
+ });
2477
+ }, 500);
2478
+ },
2479
+ onTouchMove: (e) => {
2480
+ if (!cellTouchStart.current) return;
2481
+ const touch = e.touches[0];
2482
+ const dx = touch.clientX - cellTouchStart.current.x;
2483
+ const dy = touch.clientY - cellTouchStart.current.y;
2484
+ if (Math.abs(dx) > 10 || Math.abs(dy) > 10)
2485
+ cancelCellLongPress();
2241
2486
  },
2487
+ onTouchEnd: cancelCellLongPress,
2488
+ onTouchCancel: cancelCellLongPress,
2242
2489
  children: [
2243
- orderedColumns.map((column) => {
2244
- const isPinned = !!column.pinned;
2245
- const offset = columnOffsets.get(column.key);
2246
- const isSystem = column.key === "__select__" || column.key === "__expand__";
2490
+ orderedColumns.map((column, visualIndex) => {
2491
+ if (column.key === "__select__" && rowSelection) {
2492
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2493
+ "div",
2494
+ {
2495
+ className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2496
+ style: {
2497
+ display: "flex",
2498
+ height: 36,
2499
+ alignItems: "center",
2500
+ justifyContent: "center",
2501
+ overflow: "hidden",
2502
+ textOverflow: "ellipsis",
2503
+ whiteSpace: "nowrap",
2504
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2505
+ backgroundColor: styles?.pinnedBg,
2506
+ position: "sticky",
2507
+ left: columnOffsets.get("__select__") ?? 0,
2508
+ top: 0,
2509
+ zIndex: 13,
2510
+ width: "48px",
2511
+ ...styles.header,
2512
+ ...styles.pinnedHeader
2513
+ },
2514
+ children: rowSelection.type !== "radio" && !rowSelection.hideSelectAll && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2515
+ "input",
2516
+ {
2517
+ type: "checkbox",
2518
+ checked: data.length > 0 && normalizedSelectedKeys.length === data.length,
2519
+ ref: (input) => {
2520
+ if (input) {
2521
+ input.indeterminate = normalizedSelectedKeys.length > 0 && normalizedSelectedKeys.length < data.length;
2522
+ }
2523
+ },
2524
+ onChange: (e) => {
2525
+ if (e.target.checked) {
2526
+ const allKeys = data.map(
2527
+ (row, idx) => getRowKey(row, idx)
2528
+ );
2529
+ rowSelection.onSelectAll?.(
2530
+ true,
2531
+ data,
2532
+ data
2533
+ );
2534
+ rowSelection.onChange?.(allKeys, data, {
2535
+ type: "all"
2536
+ });
2537
+ } else {
2538
+ rowSelection.onSelectAll?.(false, [], data);
2539
+ rowSelection.onChange?.([], [], {
2540
+ type: "all"
2541
+ });
2542
+ }
2543
+ },
2544
+ style: { cursor: "pointer", accentColor }
2545
+ }
2546
+ )
2547
+ },
2548
+ "__select__"
2549
+ );
2550
+ }
2551
+ if (column.key === "__expand__") {
2552
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2553
+ "div",
2554
+ {
2555
+ className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2556
+ style: {
2557
+ display: "flex",
2558
+ height: 36,
2559
+ alignItems: "center",
2560
+ justifyContent: "center",
2561
+ overflow: "hidden",
2562
+ textOverflow: "ellipsis",
2563
+ whiteSpace: "nowrap",
2564
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2565
+ backgroundColor: styles?.pinnedBg,
2566
+ position: "sticky",
2567
+ left: columnOffsets.get("__expand__") ?? 0,
2568
+ top: 0,
2569
+ zIndex: 13,
2570
+ width: "40px",
2571
+ ...styles.header,
2572
+ ...styles.pinnedHeader
2573
+ }
2574
+ },
2575
+ "__expand__"
2576
+ );
2577
+ }
2247
2578
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2248
- "div",
2579
+ DraggableHeader_default,
2249
2580
  {
2250
- className: isPinned ? classNames.pinnedHeader ?? "" : classNames.header ?? "",
2251
- style: {
2252
- display: "flex",
2253
- height: 36,
2254
- alignItems: "center",
2255
- overflow: "hidden",
2256
- textOverflow: "ellipsis",
2257
- whiteSpace: "nowrap",
2258
- borderBottom: "1px solid rgba(128,128,128,0.2)",
2259
- backdropFilter: "blur(8px)",
2260
- position: "sticky",
2261
- top: 0,
2262
- zIndex: isPinned ? 13 : 10,
2263
- ...isPinned ? {
2264
- [column.pinned]: offset ?? 0,
2265
- ...styles.pinnedHeader
2266
- } : styles.header,
2267
- paddingLeft: isSystem ? 0 : 8,
2268
- paddingRight: isSystem ? 0 : 8
2269
- }
2581
+ column,
2582
+ accentColor,
2583
+ visualIndex,
2584
+ onResizeStart: handleResizeStart,
2585
+ onColumnDragStart: handleColumnDragStart,
2586
+ styles,
2587
+ classNames,
2588
+ gripIcon,
2589
+ hideGripIcon,
2590
+ icons,
2591
+ stickyOffset: columnOffsets.get(column.key),
2592
+ onTogglePin: handleTogglePin,
2593
+ onToggleHide: handleToggleHide,
2594
+ isLastColumn: visualIndex === orderedColumns.length - 1,
2595
+ sortDirection: sortState.key === column.key ? sortState.direction : null,
2596
+ onSort: handleSort,
2597
+ filterValue: columnFilters[column.key] ?? "",
2598
+ onFilter: handleColumnFilter,
2599
+ onClearFilter: handleClearFilter,
2600
+ customContextMenuItems: columnContextMenuItems
2270
2601
  },
2271
2602
  column.key
2272
2603
  );
2273
2604
  }),
2274
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { gridColumn: "1 / -1" }, children: Array.from({ length: shimmerCount }).map((_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2605
+ isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2275
2606
  "div",
2276
2607
  {
2277
2608
  style: {
2278
- display: "grid",
2279
- gridTemplateColumns,
2280
- height: rowHeight
2609
+ gridColumn: "1 / -1",
2610
+ height: "100%",
2611
+ position: "relative"
2281
2612
  },
2282
- children: orderedColumns.map((column, colIndex) => {
2283
- const isPinned = !!column.pinned;
2284
- const offset = columnOffsets.get(column.key);
2285
- const isSystem = column.key === "__select__" || column.key === "__expand__";
2286
- const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
2287
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2288
- "div",
2289
- {
2290
- className: isPinned ? classNames.pinnedCell ?? "" : "",
2291
- style: {
2292
- display: "flex",
2293
- alignItems: "center",
2294
- borderBottom: "1px solid rgba(128,128,128,0.2)",
2295
- ...isPinned ? {
2296
- position: "sticky",
2297
- [column.pinned]: offset ?? 0,
2298
- zIndex: 5,
2299
- ...styles.pinnedCell
2300
- } : {},
2301
- paddingLeft: isSystem ? 0 : 8,
2302
- paddingRight: isSystem ? 0 : 8,
2303
- justifyContent: isSystem ? "center" : void 0
2304
- },
2305
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2306
- "div",
2307
- {
2308
- style: {
2309
- backgroundColor: "rgba(100, 116, 139, 0.15)",
2310
- animation: "bt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
2311
- borderRadius: isSystem ? 3 : 4,
2312
- height: isSystem ? 16 : 14,
2313
- width: isSystem ? 16 : `${widthPercent}%`,
2314
- animationDelay: `${(rowIndex * 7 + colIndex) * 50}ms`
2315
- }
2316
- }
2317
- )
2318
- },
2319
- column.key
2320
- );
2321
- })
2322
- },
2323
- rowIndex
2324
- )) })
2325
- ]
2326
- }
2327
- )
2328
- }
2329
- )
2330
- ) : (
2331
- /*
2332
- * ── Main scroll container ────────────────────────────────────
2333
- * absolute inset-0 so it fills whatever height the wrapper resolves to.
2334
- * contain: layout paint — browser optimization hint that this element
2335
- * is a layout and paint boundary (improves compositing performance).
2336
- */
2337
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2338
- "div",
2339
- {
2340
- ref: tableAreaCallbackRef,
2341
- style: {
2342
- position: "absolute",
2343
- inset: 0,
2344
- overflow: "auto",
2345
- contain: "layout paint"
2346
- },
2347
- children: [
2348
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ResizeOverlay_default, { ref: resizeOverlayRef, accentColor }),
2349
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2350
- "div",
2351
- {
2352
- style: {
2353
- display: "grid",
2354
- gridTemplateColumns,
2355
- gridTemplateRows: isEmpty ? "36px 1fr" : `36px ${virtualTotalSize}px`,
2356
- minWidth: `${totalTableWidth}px`,
2357
- width: "100%",
2358
- position: "relative",
2359
- ...isEmpty ? { height: "100%" } : {}
2360
- },
2361
- children: [
2362
- orderedColumns.map((column, visualIndex) => {
2363
- if (column.key === "__select__" && rowSelection) {
2364
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2365
- "div",
2366
- {
2367
- className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2368
- style: {
2369
- display: "flex",
2370
- height: 36,
2371
- alignItems: "center",
2372
- justifyContent: "center",
2373
- overflow: "hidden",
2374
- textOverflow: "ellipsis",
2375
- whiteSpace: "nowrap",
2376
- borderBottom: "1px solid rgba(128,128,128,0.2)",
2377
- backgroundColor: styles?.pinnedBg,
2378
- position: "sticky",
2379
- left: columnOffsets.get("__select__") ?? 0,
2380
- top: 0,
2381
- zIndex: 13,
2382
- width: "48px",
2383
- ...styles.header,
2384
- ...styles.pinnedHeader
2385
- },
2386
- children: rowSelection.type !== "radio" && !rowSelection.hideSelectAll && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2387
- "input",
2388
- {
2389
- type: "checkbox",
2390
- checked: data.length > 0 && normalizedSelectedKeys.length === data.length,
2391
- ref: (input) => {
2392
- if (input) {
2393
- input.indeterminate = normalizedSelectedKeys.length > 0 && normalizedSelectedKeys.length < data.length;
2394
- }
2395
- },
2396
- onChange: (e) => {
2397
- if (e.target.checked) {
2398
- const allKeys = data.map(
2399
- (row, idx) => getRowKey(row, idx)
2400
- );
2401
- rowSelection.onSelectAll?.(
2402
- true,
2403
- data,
2404
- data
2405
- );
2406
- rowSelection.onChange?.(allKeys, data, {
2407
- type: "all"
2408
- });
2409
- } else {
2410
- rowSelection.onSelectAll?.(false, [], data);
2411
- rowSelection.onChange?.([], [], {
2412
- type: "all"
2413
- });
2414
- }
2415
- },
2416
- style: { cursor: "pointer", accentColor }
2417
- }
2418
- )
2419
- },
2420
- "__select__"
2421
- );
2422
- }
2423
- if (column.key === "__expand__") {
2424
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2425
- "div",
2426
- {
2427
- className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2428
- style: {
2429
- display: "flex",
2430
- height: 36,
2431
- alignItems: "center",
2432
- justifyContent: "center",
2433
- overflow: "hidden",
2434
- textOverflow: "ellipsis",
2435
- whiteSpace: "nowrap",
2436
- borderBottom: "1px solid rgba(128,128,128,0.2)",
2437
- backgroundColor: styles?.pinnedBg,
2438
- position: "sticky",
2439
- left: columnOffsets.get("__expand__") ?? 0,
2440
- top: 0,
2441
- zIndex: 13,
2442
- width: "40px",
2443
- ...styles.header,
2444
- ...styles.pinnedHeader
2445
- }
2446
- },
2447
- "__expand__"
2448
- );
2449
- }
2450
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2451
- DraggableHeader_default,
2452
- {
2453
- column,
2454
- accentColor,
2455
- visualIndex,
2456
- onResizeStart: handleResizeStart,
2457
- onColumnDragStart: handleColumnDragStart,
2458
- styles,
2459
- classNames,
2460
- gripIcon,
2461
- hideGripIcon,
2462
- icons,
2463
- stickyOffset: columnOffsets.get(column.key),
2464
- onTogglePin: handleTogglePin,
2465
- onToggleHide: handleToggleHide,
2466
- isLastColumn: visualIndex === orderedColumns.length - 1,
2467
- sortDirection: sortState.key === column.key ? sortState.direction : null,
2468
- onSort: handleSort,
2469
- filterValue: columnFilters[column.key] ?? "",
2470
- onFilter: handleColumnFilter,
2471
- onClearFilter: handleClearFilter,
2472
- customContextMenuItems: columnContextMenuItems
2473
- },
2474
- column.key
2475
- );
2476
- }),
2477
- isEmpty ? (
2478
- /*
2479
- * ── Empty state ────────────────────────────────────────
2480
- * col-span-full + height:100% fills the 1fr body grid row.
2481
- *
2482
- * The inner div uses `position: sticky; left: 0` with a fixed
2483
- * width (scrollAreaWidth) to viewport-lock the empty state panel.
2484
- * Without this, the empty message would scroll horizontally
2485
- * with the grid content when there are many columns.
2486
- */
2487
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2613
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2488
2614
  "div",
2489
2615
  {
2490
2616
  style: {
2491
- gridColumn: "1 / -1",
2617
+ position: "sticky",
2618
+ left: 0,
2619
+ width: scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : "100%",
2492
2620
  height: "100%",
2493
- position: "relative"
2621
+ display: "flex",
2622
+ alignItems: "center",
2623
+ justifyContent: "center"
2494
2624
  },
2495
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2625
+ children: emptyRenderer ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2496
2626
  "div",
2497
2627
  {
2498
2628
  style: {
2499
- position: "sticky",
2500
- left: 0,
2501
- width: scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : "100%",
2502
- height: "100%",
2503
2629
  display: "flex",
2630
+ flexDirection: "column",
2504
2631
  alignItems: "center",
2505
- justifyContent: "center"
2632
+ gap: 8,
2633
+ paddingTop: 32,
2634
+ paddingBottom: 32,
2635
+ color: "GrayText"
2506
2636
  },
2507
- children: emptyRenderer ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2508
- "div",
2509
- {
2510
- style: {
2511
- display: "flex",
2512
- flexDirection: "column",
2513
- alignItems: "center",
2514
- gap: 8,
2515
- paddingTop: 32,
2516
- paddingBottom: 32,
2517
- color: "GrayText"
2518
- },
2519
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 14 }, children: "No data" })
2520
- }
2521
- )
2637
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 14 }, children: "No data" })
2522
2638
  }
2523
2639
  )
2524
2640
  }
2525
2641
  )
2526
- ) : (
2527
- /* ── Virtualized table body ─────────────────────────── */
2528
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2529
- TableBody_default,
2530
- {
2531
- data: displayData,
2532
- orderedColumns: freshOrderedColumns,
2533
- rowVirtualizer,
2534
- columnOffsets,
2535
- styles,
2536
- classNames,
2537
- rowSelection: !showShimmer ? rowSelection : void 0,
2538
- normalizedSelectedKeys,
2539
- getRowKey,
2540
- expandable: !showShimmer ? expandable : void 0,
2541
- resolvedExpandedKeys,
2542
- rowHeight,
2543
- totalTableWidth,
2544
- scrollAreaWidth,
2545
- accentColor,
2546
- scrollContainerRef: tableAreaRef,
2547
- isLoading: showShimmer,
2548
- onExpandedRowResize: handleExpandedRowResize,
2549
- maxExpandedRowHeight,
2550
- pinnedTopData: pinnedTopRows,
2551
- pinnedBottomData: pinnedBottomRows,
2552
- gridTemplateColumns,
2553
- headerHeight: HEADER_HEIGHT
2554
- }
2555
- )
2556
- )
2557
- ]
2558
- }
2559
- )
2560
- ]
2561
- }
2562
- )
2642
+ }
2643
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2644
+ TableBody_default,
2645
+ {
2646
+ data: displayData,
2647
+ orderedColumns: freshOrderedColumns,
2648
+ rowVirtualizer,
2649
+ columnOffsets,
2650
+ styles,
2651
+ classNames,
2652
+ rowSelection: !showShimmer ? rowSelection : void 0,
2653
+ normalizedSelectedKeys,
2654
+ getRowKey,
2655
+ expandable: !showShimmer ? expandable : void 0,
2656
+ resolvedExpandedKeys,
2657
+ rowHeight,
2658
+ totalTableWidth,
2659
+ scrollAreaWidth,
2660
+ accentColor,
2661
+ scrollContainerRef: tableAreaRef,
2662
+ isLoading: showShimmer,
2663
+ onExpandedRowResize: handleExpandedRowResize,
2664
+ maxExpandedRowHeight,
2665
+ pinnedTopData: pinnedTopRows,
2666
+ pinnedBottomData: pinnedBottomRows,
2667
+ gridTemplateColumns,
2668
+ headerHeight: HEADER_HEIGHT
2669
+ }
2670
+ )
2671
+ ]
2672
+ }
2673
+ )
2674
+ ]
2675
+ }
2563
2676
  )
2564
2677
  }
2565
2678
  ),
@@ -2856,7 +2969,166 @@ function BoltTable({
2856
2969
  }
2857
2970
  ),
2858
2971
  document.body
2859
- )
2972
+ ),
2973
+ cellContextMenu && typeof document !== "undefined" && (() => {
2974
+ const menuCol = freshOrderedColumns.find(
2975
+ (c) => c.key === cellContextMenu.columnKey
2976
+ );
2977
+ const isPinnedTop = pinnedTopKeySet.has(cellContextMenu.rowKey);
2978
+ const isPinnedBottom = pinnedBottomKeySet.has(
2979
+ cellContextMenu.rowKey
2980
+ );
2981
+ const hasCopy = menuCol?.copy;
2982
+ const hasRowPin = !!onRowPin;
2983
+ let menuRecord;
2984
+ let menuRowIndex = 0;
2985
+ const allRows = [
2986
+ ...pinnedTopRows,
2987
+ ...displayData,
2988
+ ...pinnedBottomRows
2989
+ ];
2990
+ for (let i = 0; i < allRows.length; i++) {
2991
+ const rk = getRowKey(allRows[i], i);
2992
+ if (rk === cellContextMenu.rowKey) {
2993
+ menuRecord = allRows[i];
2994
+ menuRowIndex = i;
2995
+ break;
2996
+ }
2997
+ }
2998
+ const menuValue = menuRecord && menuCol ? menuRecord[menuCol.dataIndex] : void 0;
2999
+ const btnStyle = {
3000
+ display: "flex",
3001
+ width: "100%",
3002
+ alignItems: "center",
3003
+ gap: 8,
3004
+ background: "none",
3005
+ border: "none",
3006
+ padding: "6px 12px",
3007
+ fontSize: 12,
3008
+ cursor: "pointer",
3009
+ color: "inherit",
3010
+ whiteSpace: "nowrap"
3011
+ };
3012
+ return (0, import_react_dom2.createPortal)(
3013
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3014
+ "div",
3015
+ {
3016
+ ref: cellMenuRef,
3017
+ style: {
3018
+ position: "fixed",
3019
+ top: cellContextMenu.y,
3020
+ left: cellContextMenu.x,
3021
+ zIndex: 99999,
3022
+ minWidth: 170,
3023
+ borderRadius: 8,
3024
+ border: "1px solid rgba(128,128,128,0.2)",
3025
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3026
+ backdropFilter: "blur(16px)",
3027
+ WebkitBackdropFilter: "blur(16px)",
3028
+ backgroundColor: "rgba(128,128,128,0.08)",
3029
+ padding: "4px 0",
3030
+ fontSize: 10
3031
+ },
3032
+ children: [
3033
+ hasRowPin && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3034
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3035
+ "button",
3036
+ {
3037
+ "data-bt-ctx-item": true,
3038
+ style: btnStyle,
3039
+ onClick: () => {
3040
+ onRowPin(
3041
+ cellContextMenu.rowKey,
3042
+ isPinnedTop ? false : "top"
3043
+ );
3044
+ setCellContextMenu(null);
3045
+ },
3046
+ children: [
3047
+ isPinnedTop ? icons?.pinOff ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3048
+ PinOffIcon,
3049
+ {
3050
+ style: { width: 14, height: 14, flexShrink: 0 }
3051
+ }
3052
+ ) : icons?.pin ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3053
+ PinIcon,
3054
+ {
3055
+ style: { width: 14, height: 14, flexShrink: 0 }
3056
+ }
3057
+ ),
3058
+ isPinnedTop ? "Unpin Row from Top" : "Pin Row to Top"
3059
+ ]
3060
+ }
3061
+ ),
3062
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3063
+ "button",
3064
+ {
3065
+ "data-bt-ctx-item": true,
3066
+ style: btnStyle,
3067
+ onClick: () => {
3068
+ onRowPin(
3069
+ cellContextMenu.rowKey,
3070
+ isPinnedBottom ? false : "bottom"
3071
+ );
3072
+ setCellContextMenu(null);
3073
+ },
3074
+ children: [
3075
+ isPinnedBottom ? icons?.pinOff ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3076
+ PinOffIcon,
3077
+ {
3078
+ style: { width: 14, height: 14, flexShrink: 0 }
3079
+ }
3080
+ ) : icons?.pin ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3081
+ PinIcon,
3082
+ {
3083
+ style: {
3084
+ width: 14,
3085
+ height: 14,
3086
+ flexShrink: 0,
3087
+ transform: "rotate(180deg)"
3088
+ }
3089
+ }
3090
+ ),
3091
+ isPinnedBottom ? "Unpin Row from Bottom" : "Pin Row to Bottom"
3092
+ ]
3093
+ }
3094
+ )
3095
+ ] }),
3096
+ hasRowPin && hasCopy && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3097
+ "div",
3098
+ {
3099
+ style: {
3100
+ borderTop: "1px solid rgba(128,128,128,0.2)",
3101
+ margin: "4px 0"
3102
+ }
3103
+ }
3104
+ ),
3105
+ hasCopy && menuRecord && menuCol && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3106
+ "button",
3107
+ {
3108
+ "data-bt-ctx-item": true,
3109
+ style: btnStyle,
3110
+ onClick: () => {
3111
+ const text = typeof menuCol.copy === "function" ? menuCol.copy(menuValue, menuRecord, menuRowIndex) : String(menuValue ?? "");
3112
+ navigator.clipboard?.writeText(text);
3113
+ setCellContextMenu(null);
3114
+ },
3115
+ children: [
3116
+ icons?.copy ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3117
+ CopyIcon,
3118
+ {
3119
+ style: { width: 14, height: 14, flexShrink: 0 }
3120
+ }
3121
+ ),
3122
+ "Copy"
3123
+ ]
3124
+ }
3125
+ )
3126
+ ]
3127
+ }
3128
+ ),
3129
+ document.body
3130
+ );
3131
+ })()
2860
3132
  ] });
2861
3133
  }
2862
3134
  // Annotate the CommonJS export names for ESM import in node:
@@ -2866,4 +3138,3 @@ function BoltTable({
2866
3138
  ResizeOverlay,
2867
3139
  TableBody
2868
3140
  });
2869
- //# sourceMappingURL=index.js.map