bolt-table 0.1.1 → 0.1.2

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.mjs CHANGED
@@ -1,17 +1,4 @@
1
1
  // src/BoltTable.tsx
2
- import {
3
- closestCenter,
4
- DndContext,
5
- DragOverlay,
6
- PointerSensor,
7
- useSensor,
8
- useSensors
9
- } from "@dnd-kit/core";
10
- import {
11
- arrayMove,
12
- horizontalListSortingStrategy,
13
- SortableContext
14
- } from "@dnd-kit/sortable";
15
2
  import { useVirtualizer } from "@tanstack/react-virtual";
16
3
  import React4, {
17
4
  useCallback,
@@ -19,9 +6,9 @@ import React4, {
19
6
  useRef as useRef4,
20
7
  useState as useState2
21
8
  } from "react";
9
+ import { createPortal as createPortal2 } from "react-dom";
22
10
 
23
11
  // src/DraggableHeader.tsx
24
- import { useSortable } from "@dnd-kit/sortable";
25
12
  import React, { useEffect, useRef, useState } from "react";
26
13
  import { createPortal } from "react-dom";
27
14
 
@@ -124,7 +111,8 @@ var DraggableHeader = React.memo(
124
111
  onFilter,
125
112
  onClearFilter,
126
113
  customContextMenuItems,
127
- icons
114
+ icons,
115
+ onColumnDragStart
128
116
  }) => {
129
117
  const effectivelySortable = isColumnSortable(column);
130
118
  const effectivelyFilterable = isColumnFilterable(column);
@@ -132,20 +120,6 @@ var DraggableHeader = React.memo(
132
120
  const [showFilterInput, setShowFilterInput] = useState(false);
133
121
  const filterInputRef = useRef(null);
134
122
  const menuRef = useRef(null);
135
- const {
136
- attributes,
137
- listeners,
138
- setNodeRef,
139
- transition,
140
- isDragging,
141
- // true while this header is being dragged
142
- isOver
143
- // true while another header is being dragged over this one
144
- } = useSortable({
145
- id: column.key,
146
- // Pinned columns cannot be dragged (their position is fixed by pinning)
147
- disabled: Boolean(column.pinned)
148
- });
149
123
  useEffect(() => {
150
124
  const handleClickOutside = (e) => {
151
125
  if (menuRef.current && !menuRef.current.contains(e.target)) {
@@ -181,54 +155,41 @@ var DraggableHeader = React.memo(
181
155
  const columnWidth = column.width ?? 150;
182
156
  const widthPx = `${columnWidth}px`;
183
157
  const isPinned = Boolean(column.pinned);
184
- const zIndex = isDragging ? 5 : isPinned ? 12 : 10;
185
- const style = {
158
+ const zIndex = isPinned ? 12 : 10;
159
+ const headerStyle = {
186
160
  position: "sticky",
187
161
  top: 0,
188
162
  zIndex,
189
- // Last column stretches to fill remaining space; all others are fixed width
190
163
  width: isLastColumn ? "100%" : widthPx,
191
164
  minWidth: widthPx,
192
165
  ...isLastColumn ? {} : { maxWidth: widthPx },
193
166
  gridColumn: visualIndex + 1,
194
167
  gridRow: 1,
195
- // Fade out slightly while being dragged
196
- opacity: isDragging ? 0.3 : 1,
197
- transition,
198
- borderWidth: "1px",
199
- // Show a dashed accent-colored border when another column is dragged over this one
200
- borderStyle: isOver ? "dashed" : "solid",
201
- ...isOver ? { borderColor: accentColor || "#1788ff" } : { borderLeftColor: "transparent" },
202
- // Sticky positioning for pinned columns
203
- ...column.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px`, position: "sticky" } : {},
204
- ...column.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px`, position: "sticky" } : {},
205
- // Pinned columns get a semi-transparent background so they visually
206
- // separate from scrolling content behind them
168
+ borderTop: "none",
169
+ borderRight: "none",
170
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
171
+ borderLeft: "none",
172
+ ...column.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
173
+ ...column.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
207
174
  ...isPinned ? {
208
- backgroundColor: styles?.pinnedBg ?? "rgba(255, 255, 255, 0.95)",
175
+ backgroundColor: styles?.pinnedBg ?? "Canvas",
209
176
  ...styles?.pinnedHeader
210
177
  } : {},
211
- // Column-level style overrides applied last (highest specificity)
212
178
  ...column.style,
213
- ...styles?.header
214
- };
215
- const headerStyle = {
216
- ...style,
217
- backgroundColor: style.backgroundColor ?? "rgba(248, 250, 252, 0.4)",
218
- position: "sticky",
179
+ ...styles?.header,
180
+ backgroundColor: styles?.pinnedBg && isPinned ? styles.pinnedBg : isPinned ? "Canvas" : "rgba(128,128,128,0.06)",
219
181
  display: "flex",
220
182
  height: 36,
221
183
  alignItems: "center",
222
184
  overflow: "hidden",
223
185
  textOverflow: "ellipsis",
224
186
  whiteSpace: "nowrap",
225
- backdropFilter: "blur(8px)"
187
+ ...isPinned ? {} : { backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" }
226
188
  };
227
189
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
228
190
  /* @__PURE__ */ jsxs2(
229
191
  "div",
230
192
  {
231
- ref: setNodeRef,
232
193
  "data-column-key": column.key,
233
194
  "data-bt-header": "",
234
195
  style: headerStyle,
@@ -238,8 +199,14 @@ var DraggableHeader = React.memo(
238
199
  /* @__PURE__ */ jsxs2(
239
200
  "div",
240
201
  {
241
- ...isPinned ? {} : attributes,
242
- ...isPinned ? {} : listeners,
202
+ role: isPinned ? void 0 : "button",
203
+ tabIndex: isPinned ? void 0 : 0,
204
+ "aria-roledescription": isPinned ? void 0 : "sortable",
205
+ onPointerDown: isPinned ? void 0 : (e) => {
206
+ if (e.button !== 0) return;
207
+ e.preventDefault();
208
+ onColumnDragStart?.(column.key, e);
209
+ },
243
210
  style: {
244
211
  position: "relative",
245
212
  zIndex: 10,
@@ -254,6 +221,7 @@ var DraggableHeader = React.memo(
254
221
  whiteSpace: "nowrap",
255
222
  paddingLeft: 8,
256
223
  paddingRight: 8,
224
+ borderLeft: "1px solid rgba(128,128,128,0.2)",
257
225
  fontWeight: 500,
258
226
  cursor: isPinned ? "default" : "grab"
259
227
  },
@@ -360,12 +328,12 @@ var DraggableHeader = React.memo(
360
328
  zIndex: 9999,
361
329
  minWidth: 160,
362
330
  borderRadius: 6,
363
- border: "1px solid #e5e7eb",
331
+ border: "1px solid rgba(128,128,128,0.2)",
364
332
  paddingTop: 4,
365
333
  paddingBottom: 4,
366
334
  boxShadow: "0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)",
367
335
  backdropFilter: "blur(12px)",
368
- backgroundColor: "rgba(255, 255, 255, 0.98)",
336
+ backgroundColor: "rgba(128,128,128,0.1)",
369
337
  left: `${contextMenu.x}px`,
370
338
  top: `${contextMenu.y}px`
371
339
  },
@@ -436,7 +404,7 @@ var DraggableHeader = React.memo(
436
404
  ]
437
405
  }
438
406
  ),
439
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid #e5e7eb" } })
407
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
440
408
  ] }),
441
409
  effectivelyFilterable && onFilter && /* @__PURE__ */ jsxs2(Fragment, { children: [
442
410
  showFilterInput ? /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 4, paddingLeft: 8, paddingRight: 8, paddingTop: 6, paddingBottom: 6 }, children: /* @__PURE__ */ jsx2(
@@ -450,7 +418,7 @@ var DraggableHeader = React.memo(
450
418
  style: {
451
419
  width: "100%",
452
420
  borderRadius: 4,
453
- border: "1px solid #e5e7eb",
421
+ border: "1px solid rgba(128,128,128,0.2)",
454
422
  paddingLeft: 6,
455
423
  paddingRight: 6,
456
424
  paddingTop: 2,
@@ -534,7 +502,7 @@ var DraggableHeader = React.memo(
534
502
  ]
535
503
  }
536
504
  ),
537
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid #e5e7eb" } })
505
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
538
506
  ] }),
539
507
  /* @__PURE__ */ jsxs2(
540
508
  "button",
@@ -603,7 +571,7 @@ var DraggableHeader = React.memo(
603
571
  }
604
572
  ),
605
573
  !isPinned && /* @__PURE__ */ jsxs2(Fragment, { children: [
606
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid #e5e7eb" } }),
574
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
607
575
  /* @__PURE__ */ jsxs2(
608
576
  "button",
609
577
  {
@@ -636,7 +604,7 @@ var DraggableHeader = React.memo(
636
604
  )
637
605
  ] }),
638
606
  customContextMenuItems && customContextMenuItems.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
639
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid #e5e7eb" } }),
607
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
640
608
  customContextMenuItems.map((item) => /* @__PURE__ */ jsxs2(
641
609
  "button",
642
610
  {
@@ -866,10 +834,11 @@ var Cell = React3.memo(
866
834
  display: "flex",
867
835
  alignItems: "center",
868
836
  overflow: "hidden",
869
- borderBottom: "1px solid #e5e7eb",
837
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
870
838
  paddingLeft: 8,
871
839
  paddingRight: 8,
872
840
  height: "100%",
841
+ boxSizing: "border-box",
873
842
  ...column.style,
874
843
  ...isPinned ? styles?.pinnedCell : void 0
875
844
  },
@@ -932,11 +901,12 @@ var Cell = React3.memo(
932
901
  display: "flex",
933
902
  alignItems: "center",
934
903
  overflow: "hidden",
935
- borderBottom: "1px solid #e5e7eb",
904
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
936
905
  paddingLeft: 8,
937
906
  paddingRight: 8,
938
907
  justifyContent: column.key === "__select__" || column.key === "__expand__" ? "center" : void 0,
939
908
  height: "100%",
909
+ boxSizing: "border-box",
940
910
  ...column.style,
941
911
  ...isPinned ? styles?.pinnedCell : void 0
942
912
  },
@@ -955,11 +925,12 @@ var Cell = React3.memo(
955
925
  overflow: "hidden",
956
926
  textOverflow: "ellipsis",
957
927
  whiteSpace: "nowrap",
958
- borderBottom: "1px solid #e5e7eb",
928
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
959
929
  paddingLeft: 8,
960
930
  paddingRight: 8,
961
931
  justifyContent: column.key === "__select__" || column.key === "__expand__" ? "center" : void 0,
962
932
  height: "100%",
933
+ boxSizing: "border-box",
963
934
  ...column.style,
964
935
  ...isPinned ? styles?.pinnedCell : void 0
965
936
  },
@@ -1032,6 +1003,7 @@ var TableBody = ({
1032
1003
  }) => {
1033
1004
  const virtualItems = rowVirtualizer.getVirtualItems();
1034
1005
  const totalSize = rowVirtualizer.getTotalSize();
1006
+ const selectedKeySet = useMemo(() => new Set(normalizedSelectedKeys), [normalizedSelectedKeys]);
1035
1007
  const columnStyles = useMemo(() => {
1036
1008
  return orderedColumns.map((col, colIndex) => {
1037
1009
  const stickyOffset = columnOffsets.get(col.key);
@@ -1051,8 +1023,7 @@ var TableBody = ({
1051
1023
  else if (col.pinned === "right" && stickyOffset !== void 0)
1052
1024
  style.right = `${stickyOffset}px`;
1053
1025
  if (isPinned) {
1054
- style.backdropFilter = "blur(14px)";
1055
- style.backgroundColor = styles?.pinnedBg ?? "rgba(255, 255, 255, 0.95)";
1026
+ style.backgroundColor = styles?.pinnedBg ?? "Canvas";
1056
1027
  if (styles?.pinnedCell) Object.assign(style, styles.pinnedCell);
1057
1028
  }
1058
1029
  return { key: col.key, style, isPinned };
@@ -1064,16 +1035,11 @@ var TableBody = ({
1064
1035
  return /* @__PURE__ */ jsx4(
1065
1036
  "div",
1066
1037
  {
1067
- style: {
1068
- ...colStyle.style,
1069
- overflow: "hidden",
1070
- textOverflow: "ellipsis",
1071
- whiteSpace: "nowrap"
1072
- },
1038
+ style: colStyle.style,
1073
1039
  children: virtualItems.map((virtualRow) => {
1074
1040
  const row = data[virtualRow.index];
1075
1041
  const rowKey = getRowKey ? getRowKey(row, virtualRow.index) : String(virtualRow.index);
1076
- const isSelected = normalizedSelectedKeys.includes(rowKey);
1042
+ const isSelected = selectedKeySet.has(rowKey);
1077
1043
  const isExpanded = resolvedExpandedKeys?.has(rowKey) ?? false;
1078
1044
  const cellValue = row[col.dataIndex];
1079
1045
  const isRowShimmer = isLoading || rowKey.startsWith("__shimmer_");
@@ -1098,20 +1064,14 @@ var TableBody = ({
1098
1064
  top: `${virtualRow.start}px`,
1099
1065
  left: 0,
1100
1066
  right: 0,
1101
- height: `${virtualRow.size}px`,
1102
- overflow: "hidden",
1103
- textOverflow: "ellipsis",
1104
- whiteSpace: "nowrap"
1067
+ height: `${virtualRow.size}px`
1105
1068
  },
1106
1069
  children: /* @__PURE__ */ jsx4(
1107
1070
  "div",
1108
1071
  {
1109
1072
  style: {
1110
1073
  height: `${rowHeight}px`,
1111
- position: "relative",
1112
- overflow: "hidden",
1113
- textOverflow: "ellipsis",
1114
- whiteSpace: "nowrap"
1074
+ position: "relative"
1115
1075
  },
1116
1076
  children: /* @__PURE__ */ jsx4(
1117
1077
  Cell,
@@ -1172,8 +1132,8 @@ var TableBody = ({
1172
1132
  width: scrollAreaWidth && scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : "100%",
1173
1133
  overflow: "auto",
1174
1134
  pointerEvents: "auto",
1175
- borderBottom: "1px solid #e5e7eb",
1176
- backgroundColor: "rgba(248, 250, 252, 0.4)",
1135
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
1136
+ backgroundColor: "rgba(128,128,128,0.06)",
1177
1137
  padding: 20,
1178
1138
  ...maxExpandedRowHeight ? { maxHeight: `${maxExpandedRowHeight}px` } : void 0,
1179
1139
  ...styles?.expandedRow
@@ -1211,7 +1171,13 @@ TableBody.displayName = "TableBody";
1211
1171
  var TableBody_default = TableBody;
1212
1172
 
1213
1173
  // src/BoltTable.tsx
1214
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1174
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1175
+ function arrayMove(arr, from, to) {
1176
+ const result = arr.slice();
1177
+ const [item] = result.splice(from, 1);
1178
+ result.splice(to, 0, item);
1179
+ return result;
1180
+ }
1215
1181
  var SHIMMER_WIDTHS2 = [55, 70, 45, 80, 60, 50, 75, 65, 40, 72];
1216
1182
  function BoltTable({
1217
1183
  columns: initialColumns,
@@ -1460,25 +1426,91 @@ function BoltTable({
1460
1426
  };
1461
1427
  }, []);
1462
1428
  const resizeStateRef = useRef4(null);
1463
- const sensors = useSensors(useSensor(PointerSensor));
1464
- const handleDragStart = (event) => {
1465
- if (event.active.id === "__select__" || event.active.id === "__expand__")
1466
- return;
1467
- setActiveId(event.active.id);
1468
- };
1469
- const handleDragEnd = (event) => {
1470
- const { active, over } = event;
1471
- if (over && active.id !== over.id) {
1472
- setColumnOrder((items) => {
1473
- const oldIndex = items.indexOf(active.id);
1474
- const newIndex = items.indexOf(over.id);
1475
- const newOrder = arrayMove(items, oldIndex, newIndex);
1476
- setTimeout(() => onColumnOrderChange?.(newOrder), 0);
1477
- return newOrder;
1478
- });
1479
- }
1480
- setActiveId(null);
1481
- };
1429
+ const overIdRef = useRef4(null);
1430
+ const dragActiveIdRef = useRef4(null);
1431
+ const ghostRef = useRef4(null);
1432
+ const onColumnOrderChangeRef = useRef4(onColumnOrderChange);
1433
+ onColumnOrderChangeRef.current = onColumnOrderChange;
1434
+ const handleColumnDragStart = useCallback(
1435
+ (columnKey, e) => {
1436
+ if (columnKey === "__select__" || columnKey === "__expand__") return;
1437
+ const headerEl = e.currentTarget.closest(
1438
+ "[data-column-key]"
1439
+ );
1440
+ if (!headerEl) return;
1441
+ const rect = headerEl.getBoundingClientRect();
1442
+ const offsetX = e.clientX - rect.left;
1443
+ const offsetY = e.clientY - rect.top;
1444
+ setActiveId(columnKey);
1445
+ dragActiveIdRef.current = columnKey;
1446
+ headerEl.setAttribute("data-dragging", "");
1447
+ const ghost = ghostRef.current;
1448
+ if (ghost) {
1449
+ ghost.style.display = "flex";
1450
+ ghost.style.width = `${rect.width}px`;
1451
+ ghost.style.left = `${e.clientX - offsetX}px`;
1452
+ ghost.style.top = `${rect.top}px`;
1453
+ }
1454
+ const onMove = (ev) => {
1455
+ if (ghost) {
1456
+ ghost.style.left = `${ev.clientX - offsetX}px`;
1457
+ ghost.style.top = `${ev.clientY - offsetY}px`;
1458
+ }
1459
+ const scrollEl = tableAreaRef.current;
1460
+ if (!scrollEl) return;
1461
+ const headers = scrollEl.querySelectorAll("[data-column-key]");
1462
+ let newOverId = null;
1463
+ headers.forEach((h) => {
1464
+ const key = h.dataset.columnKey;
1465
+ if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
1466
+ h.removeAttribute("data-drag-over");
1467
+ return;
1468
+ }
1469
+ const r = h.getBoundingClientRect();
1470
+ if (ev.clientX >= r.left && ev.clientX <= r.right && ev.clientY >= r.top - 20 && ev.clientY <= r.bottom + 20) {
1471
+ newOverId = key;
1472
+ h.setAttribute("data-drag-over", "");
1473
+ } else {
1474
+ h.removeAttribute("data-drag-over");
1475
+ }
1476
+ });
1477
+ overIdRef.current = newOverId;
1478
+ };
1479
+ const onUp = () => {
1480
+ document.removeEventListener("pointermove", onMove);
1481
+ document.removeEventListener("pointerup", onUp);
1482
+ const scrollEl = tableAreaRef.current;
1483
+ if (scrollEl) {
1484
+ scrollEl.querySelectorAll("[data-dragging]").forEach((h) => h.removeAttribute("data-dragging"));
1485
+ scrollEl.querySelectorAll("[data-drag-over]").forEach((h) => h.removeAttribute("data-drag-over"));
1486
+ }
1487
+ if (ghost) ghost.style.display = "none";
1488
+ const currentOverId = overIdRef.current;
1489
+ const currentActiveId = dragActiveIdRef.current;
1490
+ if (currentOverId && currentActiveId && currentOverId !== currentActiveId) {
1491
+ React4.startTransition(() => {
1492
+ setColumnOrder((items) => {
1493
+ const oldIndex = items.indexOf(currentActiveId);
1494
+ const newIndex = items.indexOf(currentOverId);
1495
+ if (oldIndex === -1 || newIndex === -1) return items;
1496
+ const newOrder = arrayMove(items, oldIndex, newIndex);
1497
+ setTimeout(
1498
+ () => onColumnOrderChangeRef.current?.(newOrder),
1499
+ 0
1500
+ );
1501
+ return newOrder;
1502
+ });
1503
+ });
1504
+ }
1505
+ setActiveId(null);
1506
+ dragActiveIdRef.current = null;
1507
+ overIdRef.current = null;
1508
+ };
1509
+ document.addEventListener("pointermove", onMove);
1510
+ document.addEventListener("pointerup", onUp);
1511
+ },
1512
+ []
1513
+ );
1482
1514
  const handleResizeStart = (columnKey, e) => {
1483
1515
  e.preventDefault();
1484
1516
  e.stopPropagation();
@@ -1532,17 +1564,19 @@ function BoltTable({
1532
1564
  if (!resizeStateRef.current) return;
1533
1565
  const { startX, startWidth, currentX, columnKey } = resizeStateRef.current;
1534
1566
  const finalWidth = Math.max(40, startWidth + (currentX - startX));
1535
- manuallyResizedRef.current.add(columnKey);
1536
- setColumnWidths((prev) => {
1537
- const next = new Map(prev);
1538
- next.set(columnKey, finalWidth);
1539
- return next;
1540
- });
1541
- onColumnResize?.(columnKey, finalWidth);
1542
1567
  resizeOverlayRef.current?.hide();
1543
1568
  resizeStateRef.current = null;
1544
1569
  document.removeEventListener("mousemove", handleResizeMove);
1545
1570
  document.removeEventListener("mouseup", handleResizeEnd);
1571
+ manuallyResizedRef.current.add(columnKey);
1572
+ React4.startTransition(() => {
1573
+ setColumnWidths((prev) => {
1574
+ const next = new Map(prev);
1575
+ next.set(columnKey, finalWidth);
1576
+ return next;
1577
+ });
1578
+ });
1579
+ onColumnResize?.(columnKey, finalWidth);
1546
1580
  }, [onColumnResize]);
1547
1581
  const { leftPinned, unpinned, rightPinned } = useMemo2(() => {
1548
1582
  const columnMap = new Map(columnsWithSelection.map((c) => [c.key, c]));
@@ -1842,31 +1876,25 @@ function BoltTable({
1842
1876
  };
1843
1877
  const HEADER_HEIGHT = 36;
1844
1878
  const MAX_AUTO_ROWS = 10;
1845
- const naturalContentHeight = rowVirtualizer.getTotalSize() + HEADER_HEIGHT;
1879
+ const virtualTotalSize = rowVirtualizer.getTotalSize();
1880
+ const naturalContentHeight = virtualTotalSize + HEADER_HEIGHT;
1846
1881
  const maxAutoHeight = MAX_AUTO_ROWS * rowHeight + HEADER_HEIGHT;
1847
1882
  const isEmpty = displayData.length === 0 && !showShimmer;
1848
1883
  const emptyMinHeight = 4 * rowHeight + HEADER_HEIGHT;
1849
1884
  const clampedAutoHeight = isEmpty ? emptyMinHeight : Math.min(naturalContentHeight, maxAutoHeight);
1850
- return /* @__PURE__ */ jsxs5(
1851
- DndContext,
1852
- {
1853
- sensors,
1854
- collisionDetection: closestCenter,
1855
- onDragStart: handleDragStart,
1856
- onDragEnd: handleDragEnd,
1857
- children: [
1858
- /* @__PURE__ */ jsxs5(
1859
- "div",
1860
- {
1861
- className,
1862
- style: {
1863
- display: "flex",
1864
- width: "100%",
1865
- flexDirection: "column",
1866
- ...autoHeight ? { maxHeight: "100%" } : { height: "100%" }
1867
- },
1868
- children: [
1869
- /* @__PURE__ */ jsx5("style", { children: `
1885
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
1886
+ /* @__PURE__ */ jsxs5(
1887
+ "div",
1888
+ {
1889
+ className,
1890
+ style: {
1891
+ display: "flex",
1892
+ width: "100%",
1893
+ flexDirection: "column",
1894
+ ...autoHeight ? { maxHeight: "100%" } : { height: "100%" }
1895
+ },
1896
+ children: [
1897
+ /* @__PURE__ */ jsx5("style", { children: `
1870
1898
  @keyframes bt-pulse {
1871
1899
  0%, 100% { opacity: 1; }
1872
1900
  50% { opacity: 0.5; }
@@ -1889,665 +1917,666 @@ function BoltTable({
1889
1917
  [data-bt-ctx-item]:not(:disabled):hover {
1890
1918
  background-color: rgba(0, 0, 0, 0.06);
1891
1919
  }
1920
+ [data-column-key][data-dragging] {
1921
+ opacity: 0.3 !important;
1922
+ }
1923
+ [data-column-key][data-drag-over] {
1924
+ border: 1px dashed ${accentColor} !important;
1925
+ }
1892
1926
  ` }),
1893
- /* @__PURE__ */ jsx5(
1894
- "div",
1895
- {
1896
- style: {
1897
- position: "relative",
1898
- ...autoHeight ? {
1899
- height: `${clampedAutoHeight}px`,
1900
- maxHeight: `${clampedAutoHeight}px`,
1901
- flexShrink: 1,
1902
- flexGrow: 0
1903
- } : { flex: "1 1 0%" }
1904
- },
1905
- children: layoutLoading ? (
1906
- /*
1907
- * ── Layout loading skeleton ──────────────────────────────────
1908
- * Shown when layoutLoading=true. Renders real column headers
1909
- * (based on orderedColumns) alongside shimmer body rows.
1910
- * Used for initial page load when column widths are not yet known.
1911
- */
1912
- /* @__PURE__ */ jsx5(
1927
+ /* @__PURE__ */ jsx5(
1928
+ "div",
1929
+ {
1930
+ style: {
1931
+ position: "relative",
1932
+ ...autoHeight ? {
1933
+ height: `${clampedAutoHeight}px`,
1934
+ maxHeight: `${clampedAutoHeight}px`,
1935
+ flexShrink: 1,
1936
+ flexGrow: 0
1937
+ } : { flex: "1 1 0%" }
1938
+ },
1939
+ children: layoutLoading ? (
1940
+ /*
1941
+ * ── Layout loading skeleton ──────────────────────────────────
1942
+ * Shown when layoutLoading=true. Renders real column headers
1943
+ * (based on orderedColumns) alongside shimmer body rows.
1944
+ * Used for initial page load when column widths are not yet known.
1945
+ */
1946
+ /* @__PURE__ */ jsx5(
1947
+ "div",
1948
+ {
1949
+ style: {
1950
+ position: "absolute",
1951
+ inset: 0,
1952
+ overflow: "auto",
1953
+ contain: "layout paint"
1954
+ },
1955
+ children: /* @__PURE__ */ jsxs5(
1913
1956
  "div",
1914
1957
  {
1915
1958
  style: {
1916
- position: "absolute",
1917
- inset: 0,
1918
- overflow: "auto",
1919
- contain: "layout paint"
1959
+ display: "grid",
1960
+ gridTemplateColumns,
1961
+ gridTemplateRows: "36px auto",
1962
+ minWidth: `${totalTableWidth}px`,
1963
+ width: "100%",
1964
+ position: "relative"
1920
1965
  },
1921
- children: /* @__PURE__ */ jsxs5(
1922
- "div",
1923
- {
1924
- style: {
1925
- display: "grid",
1926
- gridTemplateColumns,
1927
- gridTemplateRows: "36px auto",
1928
- minWidth: `${totalTableWidth}px`,
1929
- width: "100%",
1930
- position: "relative"
1931
- },
1932
- children: [
1933
- orderedColumns.map((column) => {
1966
+ children: [
1967
+ orderedColumns.map((column) => {
1968
+ const isPinned = !!column.pinned;
1969
+ const offset = columnOffsets.get(column.key);
1970
+ const isSystem = column.key === "__select__" || column.key === "__expand__";
1971
+ return /* @__PURE__ */ jsx5(
1972
+ "div",
1973
+ {
1974
+ className: isPinned ? classNames.pinnedHeader ?? "" : classNames.header ?? "",
1975
+ style: {
1976
+ display: "flex",
1977
+ height: 36,
1978
+ alignItems: "center",
1979
+ overflow: "hidden",
1980
+ textOverflow: "ellipsis",
1981
+ whiteSpace: "nowrap",
1982
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
1983
+ backdropFilter: "blur(8px)",
1984
+ position: "sticky",
1985
+ top: 0,
1986
+ zIndex: isPinned ? 13 : 10,
1987
+ ...isPinned ? {
1988
+ [column.pinned]: offset ?? 0,
1989
+ ...styles.pinnedHeader
1990
+ } : styles.header,
1991
+ paddingLeft: isSystem ? 0 : 8,
1992
+ paddingRight: isSystem ? 0 : 8
1993
+ }
1994
+ },
1995
+ column.key
1996
+ );
1997
+ }),
1998
+ /* @__PURE__ */ jsx5("div", { style: { gridColumn: "1 / -1" }, children: Array.from({ length: shimmerCount }).map((_, rowIndex) => /* @__PURE__ */ jsx5(
1999
+ "div",
2000
+ {
2001
+ style: {
2002
+ display: "grid",
2003
+ gridTemplateColumns,
2004
+ height: rowHeight
2005
+ },
2006
+ children: orderedColumns.map((column, colIndex) => {
1934
2007
  const isPinned = !!column.pinned;
1935
2008
  const offset = columnOffsets.get(column.key);
1936
2009
  const isSystem = column.key === "__select__" || column.key === "__expand__";
2010
+ const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
2011
+ return /* @__PURE__ */ jsx5(
2012
+ "div",
2013
+ {
2014
+ className: isPinned ? classNames.pinnedCell ?? "" : "",
2015
+ style: {
2016
+ display: "flex",
2017
+ alignItems: "center",
2018
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2019
+ ...isPinned ? {
2020
+ position: "sticky",
2021
+ [column.pinned]: offset ?? 0,
2022
+ zIndex: 5,
2023
+ ...styles.pinnedCell
2024
+ } : {},
2025
+ paddingLeft: isSystem ? 0 : 8,
2026
+ paddingRight: isSystem ? 0 : 8,
2027
+ justifyContent: isSystem ? "center" : void 0
2028
+ },
2029
+ children: /* @__PURE__ */ jsx5(
2030
+ "div",
2031
+ {
2032
+ style: {
2033
+ backgroundColor: "rgba(100, 116, 139, 0.15)",
2034
+ animation: "bt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
2035
+ borderRadius: isSystem ? 3 : 4,
2036
+ height: isSystem ? 16 : 14,
2037
+ width: isSystem ? 16 : `${widthPercent}%`,
2038
+ animationDelay: `${(rowIndex * 7 + colIndex) * 50}ms`
2039
+ }
2040
+ }
2041
+ )
2042
+ },
2043
+ column.key
2044
+ );
2045
+ })
2046
+ },
2047
+ rowIndex
2048
+ )) })
2049
+ ]
2050
+ }
2051
+ )
2052
+ }
2053
+ )
2054
+ ) : (
2055
+ /*
2056
+ * ── Main scroll container ────────────────────────────────────
2057
+ * absolute inset-0 so it fills whatever height the wrapper resolves to.
2058
+ * contain: layout paint — browser optimization hint that this element
2059
+ * is a layout and paint boundary (improves compositing performance).
2060
+ */
2061
+ /* @__PURE__ */ jsxs5(
2062
+ "div",
2063
+ {
2064
+ ref: tableAreaCallbackRef,
2065
+ style: {
2066
+ position: "absolute",
2067
+ inset: 0,
2068
+ overflow: "auto",
2069
+ contain: "layout paint"
2070
+ },
2071
+ children: [
2072
+ /* @__PURE__ */ jsx5(ResizeOverlay_default, { ref: resizeOverlayRef, accentColor }),
2073
+ /* @__PURE__ */ jsxs5(
2074
+ "div",
2075
+ {
2076
+ style: {
2077
+ display: "grid",
2078
+ gridTemplateColumns,
2079
+ gridTemplateRows: isEmpty ? "36px 1fr" : `36px ${virtualTotalSize}px`,
2080
+ minWidth: `${totalTableWidth}px`,
2081
+ width: "100%",
2082
+ position: "relative",
2083
+ ...isEmpty ? { height: "100%" } : {}
2084
+ },
2085
+ children: [
2086
+ orderedColumns.map((column, visualIndex) => {
2087
+ if (column.key === "__select__" && rowSelection) {
1937
2088
  return /* @__PURE__ */ jsx5(
1938
2089
  "div",
1939
2090
  {
1940
- className: isPinned ? classNames.pinnedHeader ?? "" : classNames.header ?? "",
2091
+ className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
1941
2092
  style: {
1942
2093
  display: "flex",
1943
2094
  height: 36,
1944
2095
  alignItems: "center",
2096
+ justifyContent: "center",
1945
2097
  overflow: "hidden",
1946
2098
  textOverflow: "ellipsis",
1947
2099
  whiteSpace: "nowrap",
1948
- borderTop: "1px solid #e5e7eb",
1949
- borderBottom: "1px solid #e5e7eb",
1950
- backdropFilter: "blur(8px)",
1951
- backgroundColor: isPinned ? "rgba(255, 255, 255, 0.95)" : "rgba(248, 250, 252, 0.4)",
2100
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2101
+ backgroundColor: "Canvas",
1952
2102
  position: "sticky",
2103
+ left: columnOffsets.get("__select__") ?? 0,
1953
2104
  top: 0,
1954
- zIndex: isPinned ? 13 : 10,
1955
- ...isPinned ? {
1956
- [column.pinned]: offset ?? 0,
1957
- ...styles.pinnedHeader
1958
- } : styles.header,
1959
- paddingLeft: isSystem ? 0 : 8,
1960
- paddingRight: isSystem ? 0 : 8
2105
+ zIndex: 13,
2106
+ width: "48px",
2107
+ ...styles.header,
2108
+ ...styles.pinnedHeader
2109
+ },
2110
+ children: rowSelection.type !== "radio" && !rowSelection.hideSelectAll && /* @__PURE__ */ jsx5(
2111
+ "input",
2112
+ {
2113
+ type: "checkbox",
2114
+ checked: data.length > 0 && normalizedSelectedKeys.length === data.length,
2115
+ ref: (input) => {
2116
+ if (input) {
2117
+ input.indeterminate = normalizedSelectedKeys.length > 0 && normalizedSelectedKeys.length < data.length;
2118
+ }
2119
+ },
2120
+ onChange: (e) => {
2121
+ if (e.target.checked) {
2122
+ const allKeys = data.map(
2123
+ (row, idx) => getRowKey(row, idx)
2124
+ );
2125
+ rowSelection.onSelectAll?.(
2126
+ true,
2127
+ data,
2128
+ data
2129
+ );
2130
+ rowSelection.onChange?.(allKeys, data, {
2131
+ type: "all"
2132
+ });
2133
+ } else {
2134
+ rowSelection.onSelectAll?.(false, [], data);
2135
+ rowSelection.onChange?.([], [], {
2136
+ type: "all"
2137
+ });
2138
+ }
2139
+ },
2140
+ style: { cursor: "pointer", accentColor }
2141
+ }
2142
+ )
2143
+ },
2144
+ "__select__"
2145
+ );
2146
+ }
2147
+ if (column.key === "__expand__") {
2148
+ return /* @__PURE__ */ jsx5(
2149
+ "div",
2150
+ {
2151
+ className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2152
+ style: {
2153
+ display: "flex",
2154
+ height: 36,
2155
+ alignItems: "center",
2156
+ justifyContent: "center",
2157
+ overflow: "hidden",
2158
+ textOverflow: "ellipsis",
2159
+ whiteSpace: "nowrap",
2160
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2161
+ backgroundColor: "Canvas",
2162
+ position: "sticky",
2163
+ left: columnOffsets.get("__expand__") ?? 0,
2164
+ top: 0,
2165
+ zIndex: 13,
2166
+ width: "40px",
2167
+ ...styles.header,
2168
+ ...styles.pinnedHeader
1961
2169
  }
1962
2170
  },
1963
- column.key
2171
+ "__expand__"
1964
2172
  );
1965
- }),
1966
- /* @__PURE__ */ jsx5("div", { style: { gridColumn: "1 / -1" }, children: Array.from({ length: shimmerCount }).map((_, rowIndex) => /* @__PURE__ */ jsx5(
2173
+ }
2174
+ return /* @__PURE__ */ jsx5(
2175
+ DraggableHeader_default,
2176
+ {
2177
+ column,
2178
+ accentColor,
2179
+ visualIndex,
2180
+ onResizeStart: handleResizeStart,
2181
+ onColumnDragStart: handleColumnDragStart,
2182
+ styles,
2183
+ classNames,
2184
+ gripIcon,
2185
+ hideGripIcon,
2186
+ icons,
2187
+ stickyOffset: columnOffsets.get(column.key),
2188
+ onTogglePin: handleTogglePin,
2189
+ onToggleHide: handleToggleHide,
2190
+ isLastColumn: visualIndex === orderedColumns.length - 1,
2191
+ sortDirection: sortState.key === column.key ? sortState.direction : null,
2192
+ onSort: handleSort,
2193
+ filterValue: columnFilters[column.key] ?? "",
2194
+ onFilter: handleColumnFilter,
2195
+ onClearFilter: handleClearFilter,
2196
+ customContextMenuItems: columnContextMenuItems
2197
+ },
2198
+ column.key
2199
+ );
2200
+ }),
2201
+ isEmpty ? (
2202
+ /*
2203
+ * ── Empty state ────────────────────────────────────────
2204
+ * col-span-full + height:100% fills the 1fr body grid row.
2205
+ *
2206
+ * The inner div uses `position: sticky; left: 0` with a fixed
2207
+ * width (scrollAreaWidth) to viewport-lock the empty state panel.
2208
+ * Without this, the empty message would scroll horizontally
2209
+ * with the grid content when there are many columns.
2210
+ */
2211
+ /* @__PURE__ */ jsx5(
1967
2212
  "div",
1968
2213
  {
1969
2214
  style: {
1970
- display: "grid",
1971
- gridTemplateColumns,
1972
- height: rowHeight
2215
+ gridColumn: "1 / -1",
2216
+ height: "100%",
2217
+ position: "relative"
1973
2218
  },
1974
- children: orderedColumns.map((column, colIndex) => {
1975
- const isPinned = !!column.pinned;
1976
- const offset = columnOffsets.get(column.key);
1977
- const isSystem = column.key === "__select__" || column.key === "__expand__";
1978
- const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
1979
- return /* @__PURE__ */ jsx5(
1980
- "div",
1981
- {
1982
- className: isPinned ? classNames.pinnedCell ?? "" : "",
1983
- style: {
1984
- display: "flex",
1985
- alignItems: "center",
1986
- borderBottom: "1px solid #e5e7eb",
1987
- ...isPinned ? {
1988
- position: "sticky",
1989
- backgroundColor: "rgba(255, 255, 255, 0.95)",
1990
- [column.pinned]: offset ?? 0,
1991
- zIndex: 5,
1992
- ...styles.pinnedCell
1993
- } : {},
1994
- paddingLeft: isSystem ? 0 : 8,
1995
- paddingRight: isSystem ? 0 : 8,
1996
- justifyContent: isSystem ? "center" : void 0
1997
- },
1998
- children: /* @__PURE__ */ jsx5(
1999
- "div",
2000
- {
2001
- style: {
2002
- backgroundColor: "rgba(100, 116, 139, 0.15)",
2003
- animation: "bt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
2004
- borderRadius: isSystem ? 3 : 4,
2005
- height: isSystem ? 16 : 14,
2006
- width: isSystem ? 16 : `${widthPercent}%`,
2007
- animationDelay: `${(rowIndex * 7 + colIndex) * 50}ms`
2008
- }
2009
- }
2010
- )
2011
- },
2012
- column.key
2013
- );
2014
- })
2015
- },
2016
- rowIndex
2017
- )) })
2018
- ]
2019
- }
2020
- )
2021
- }
2022
- )
2023
- ) : (
2024
- /*
2025
- * ── Main scroll container ────────────────────────────────────
2026
- * absolute inset-0 so it fills whatever height the wrapper resolves to.
2027
- * contain: layout paint — browser optimization hint that this element
2028
- * is a layout and paint boundary (improves compositing performance).
2029
- */
2030
- /* @__PURE__ */ jsxs5(
2031
- "div",
2032
- {
2033
- ref: tableAreaCallbackRef,
2034
- style: {
2035
- position: "absolute",
2036
- inset: 0,
2037
- overflow: "auto",
2038
- contain: "layout paint"
2039
- },
2040
- children: [
2041
- /* @__PURE__ */ jsx5(ResizeOverlay_default, { ref: resizeOverlayRef, accentColor }),
2042
- /* @__PURE__ */ jsxs5(
2043
- "div",
2044
- {
2045
- style: {
2046
- display: "grid",
2047
- gridTemplateColumns,
2048
- gridTemplateRows: isEmpty ? "36px 1fr" : "36px auto",
2049
- minWidth: `${totalTableWidth}px`,
2050
- width: "100%",
2051
- position: "relative",
2052
- ...isEmpty ? { height: "100%" } : {}
2053
- },
2054
- children: [
2055
- /* @__PURE__ */ jsx5(
2056
- SortableContext,
2057
- {
2058
- items: columnOrder,
2059
- strategy: horizontalListSortingStrategy,
2060
- children: orderedColumns.map((column, visualIndex) => {
2061
- if (column.key === "__select__" && rowSelection) {
2062
- return /* @__PURE__ */ jsx5(
2063
- "div",
2064
- {
2065
- className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2066
- style: {
2067
- display: "flex",
2068
- height: 36,
2069
- alignItems: "center",
2070
- justifyContent: "center",
2071
- overflow: "hidden",
2072
- textOverflow: "ellipsis",
2073
- whiteSpace: "nowrap",
2074
- borderTop: "1px solid #e5e7eb",
2075
- borderBottom: "1px solid #e5e7eb",
2076
- backdropFilter: "blur(8px)",
2077
- backgroundColor: "rgba(248, 250, 252, 0.4)",
2078
- position: "sticky",
2079
- left: columnOffsets.get("__select__") ?? 0,
2080
- top: 0,
2081
- zIndex: 13,
2082
- width: "48px",
2083
- ...styles.header,
2084
- ...styles.pinnedHeader
2085
- },
2086
- children: rowSelection.type !== "radio" && !rowSelection.hideSelectAll && /* @__PURE__ */ jsx5(
2087
- "input",
2088
- {
2089
- type: "checkbox",
2090
- checked: data.length > 0 && normalizedSelectedKeys.length === data.length,
2091
- ref: (input) => {
2092
- if (input) {
2093
- input.indeterminate = normalizedSelectedKeys.length > 0 && normalizedSelectedKeys.length < data.length;
2094
- }
2095
- },
2096
- onChange: (e) => {
2097
- if (e.target.checked) {
2098
- const allKeys = data.map(
2099
- (row, idx) => getRowKey(row, idx)
2100
- );
2101
- rowSelection.onSelectAll?.(
2102
- true,
2103
- data,
2104
- data
2105
- );
2106
- rowSelection.onChange?.(allKeys, data, {
2107
- type: "all"
2108
- });
2109
- } else {
2110
- rowSelection.onSelectAll?.(false, [], data);
2111
- rowSelection.onChange?.([], [], {
2112
- type: "all"
2113
- });
2114
- }
2115
- },
2116
- style: { cursor: "pointer", accentColor }
2117
- }
2118
- )
2119
- },
2120
- "__select__"
2121
- );
2122
- }
2123
- if (column.key === "__expand__") {
2124
- return /* @__PURE__ */ jsx5(
2125
- "div",
2126
- {
2127
- className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2128
- style: {
2129
- display: "flex",
2130
- height: 36,
2131
- alignItems: "center",
2132
- justifyContent: "center",
2133
- overflow: "hidden",
2134
- textOverflow: "ellipsis",
2135
- whiteSpace: "nowrap",
2136
- borderTop: "1px solid #e5e7eb",
2137
- borderBottom: "1px solid #e5e7eb",
2138
- backdropFilter: "blur(8px)",
2139
- backgroundColor: "rgba(248, 250, 252, 0.4)",
2140
- position: "sticky",
2141
- left: columnOffsets.get("__expand__") ?? 0,
2142
- top: 0,
2143
- zIndex: 13,
2144
- width: "40px",
2145
- ...styles.header,
2146
- ...styles.pinnedHeader
2147
- }
2148
- },
2149
- "__expand__"
2150
- );
2151
- }
2152
- return /* @__PURE__ */ jsx5(
2153
- DraggableHeader_default,
2154
- {
2155
- column,
2156
- accentColor,
2157
- visualIndex,
2158
- onResizeStart: handleResizeStart,
2159
- styles,
2160
- classNames,
2161
- gripIcon,
2162
- hideGripIcon,
2163
- icons,
2164
- stickyOffset: columnOffsets.get(column.key),
2165
- onTogglePin: handleTogglePin,
2166
- onToggleHide: handleToggleHide,
2167
- isLastColumn: visualIndex === orderedColumns.length - 1,
2168
- sortDirection: sortState.key === column.key ? sortState.direction : null,
2169
- onSort: handleSort,
2170
- filterValue: columnFilters[column.key] ?? "",
2171
- onFilter: handleColumnFilter,
2172
- onClearFilter: handleClearFilter,
2173
- customContextMenuItems: columnContextMenuItems
2174
- },
2175
- column.key
2176
- );
2177
- })
2178
- }
2179
- ),
2180
- isEmpty ? (
2181
- /*
2182
- * ── Empty state ────────────────────────────────────────
2183
- * col-span-full + height:100% fills the 1fr body grid row.
2184
- *
2185
- * The inner div uses `position: sticky; left: 0` with a fixed
2186
- * width (scrollAreaWidth) to viewport-lock the empty state panel.
2187
- * Without this, the empty message would scroll horizontally
2188
- * with the grid content when there are many columns.
2189
- */
2190
- /* @__PURE__ */ jsx5(
2219
+ children: /* @__PURE__ */ jsx5(
2191
2220
  "div",
2192
2221
  {
2193
2222
  style: {
2194
- gridColumn: "1 / -1",
2223
+ position: "sticky",
2224
+ left: 0,
2225
+ width: scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : "100%",
2195
2226
  height: "100%",
2196
- position: "relative"
2227
+ display: "flex",
2228
+ alignItems: "center",
2229
+ justifyContent: "center"
2197
2230
  },
2198
- children: /* @__PURE__ */ jsx5(
2231
+ children: emptyRenderer ?? /* @__PURE__ */ jsx5(
2199
2232
  "div",
2200
2233
  {
2201
2234
  style: {
2202
- position: "sticky",
2203
- left: 0,
2204
- width: scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : "100%",
2205
- height: "100%",
2206
2235
  display: "flex",
2236
+ flexDirection: "column",
2207
2237
  alignItems: "center",
2208
- justifyContent: "center"
2238
+ gap: 8,
2239
+ paddingTop: 32,
2240
+ paddingBottom: 32,
2241
+ color: "#6b7280"
2209
2242
  },
2210
- children: emptyRenderer ?? /* @__PURE__ */ jsx5(
2211
- "div",
2212
- {
2213
- style: {
2214
- display: "flex",
2215
- flexDirection: "column",
2216
- alignItems: "center",
2217
- gap: 8,
2218
- paddingTop: 32,
2219
- paddingBottom: 32,
2220
- color: "#6b7280"
2221
- },
2222
- children: /* @__PURE__ */ jsx5("span", { style: { fontSize: 14 }, children: "No data" })
2223
- }
2224
- )
2243
+ children: /* @__PURE__ */ jsx5("span", { style: { fontSize: 14 }, children: "No data" })
2225
2244
  }
2226
2245
  )
2227
2246
  }
2228
2247
  )
2229
- ) : (
2230
- /* ── Virtualized table body ─────────────────────────── */
2231
- /* @__PURE__ */ jsx5(
2232
- TableBody_default,
2233
- {
2234
- data: displayData,
2235
- orderedColumns,
2236
- rowVirtualizer,
2237
- columnOffsets,
2238
- styles,
2239
- classNames,
2240
- rowSelection: !showShimmer ? rowSelection : void 0,
2241
- normalizedSelectedKeys,
2242
- getRowKey,
2243
- expandable: !showShimmer ? expandable : void 0,
2244
- resolvedExpandedKeys,
2245
- rowHeight,
2246
- totalTableWidth,
2247
- scrollAreaWidth,
2248
- accentColor,
2249
- scrollContainerRef: tableAreaRef,
2250
- isLoading: showShimmer,
2251
- onExpandedRowResize: handleExpandedRowResize,
2252
- maxExpandedRowHeight
2253
- }
2254
- )
2255
- )
2256
- ]
2257
- }
2258
- )
2259
- ]
2260
- }
2261
- )
2262
- )
2263
- }
2264
- ),
2265
- pagination !== false && /* @__PURE__ */ jsxs5(
2266
- "div",
2267
- {
2268
- style: {
2269
- display: "flex",
2270
- height: 36,
2271
- alignItems: "center",
2272
- justifyContent: "space-between",
2273
- borderTop: "1px solid #e5e7eb",
2274
- paddingLeft: 12,
2275
- paddingRight: 12,
2276
- fontSize: 12,
2277
- backdropFilter: "blur(8px)",
2278
- backgroundColor: "rgba(255, 255, 255, 0.4)",
2279
- gap: 12
2280
- },
2281
- children: [
2282
- /* @__PURE__ */ jsx5("div", { style: { display: "flex", flex: "1 1 0%", alignItems: "center" }, children: (() => {
2283
- const rangeStart = total > 0 ? (currentPage - 1) * pageSize + 1 : 0;
2284
- const rangeEnd = Math.min(currentPage * pageSize, total);
2285
- return pagination?.showTotal ? /* @__PURE__ */ jsxs5("span", { style: { color: "#6b7280", fontSize: 12 }, children: [
2286
- "Showing",
2287
- " ",
2288
- pagination.showTotal(total, [rangeStart, rangeEnd]),
2289
- " of",
2290
- " ",
2291
- total,
2292
- " items"
2293
- ] }) : /* @__PURE__ */ jsxs5("span", { style: { color: "#6b7280", fontSize: 12 }, children: [
2294
- rangeStart,
2295
- "\u2013",
2296
- rangeEnd,
2297
- " of ",
2298
- total
2299
- ] });
2300
- })() }),
2301
- /* @__PURE__ */ jsxs5(
2302
- "div",
2303
- {
2304
- style: {
2305
- display: "flex",
2306
- flex: "1 1 0%",
2307
- alignItems: "center",
2308
- justifyContent: "center",
2309
- gap: 4
2310
- },
2311
- children: [
2312
- /* @__PURE__ */ jsx5(
2313
- "button",
2314
- {
2315
- onClick: () => handlePageChange(1),
2316
- disabled: currentPage === 1,
2317
- style: {
2318
- display: "inline-flex",
2319
- height: 24,
2320
- width: 24,
2321
- cursor: currentPage === 1 ? "not-allowed" : "pointer",
2322
- alignItems: "center",
2323
- justifyContent: "center",
2324
- fontSize: 12,
2325
- opacity: currentPage === 1 ? 0.3 : 1,
2326
- background: "none",
2327
- border: "none",
2328
- padding: 0,
2329
- color: "inherit"
2330
- },
2331
- title: "First page",
2332
- children: icons?.chevronsLeft ?? /* @__PURE__ */ jsx5(ChevronsLeftIcon, { style: { width: 12, height: 12 } })
2333
- }
2334
- ),
2335
- /* @__PURE__ */ jsx5(
2336
- "button",
2337
- {
2338
- onClick: () => handlePageChange(currentPage - 1),
2339
- disabled: currentPage === 1,
2340
- style: {
2341
- display: "inline-flex",
2342
- height: 24,
2343
- width: 24,
2344
- cursor: currentPage === 1 ? "not-allowed" : "pointer",
2345
- alignItems: "center",
2346
- justifyContent: "center",
2347
- fontSize: 12,
2348
- opacity: currentPage === 1 ? 0.3 : 1,
2349
- background: "none",
2350
- border: "none",
2351
- padding: 0,
2352
- color: "inherit"
2353
- },
2354
- title: "Previous page",
2355
- children: icons?.chevronLeft ?? /* @__PURE__ */ jsx5(ChevronLeftIcon, { style: { width: 12, height: 12 } })
2356
- }
2357
- ),
2358
- getPageNumbers().map((page) => {
2359
- if (page === "ellipsis-left" || page === "ellipsis-right") {
2360
- return /* @__PURE__ */ jsx5(
2361
- "span",
2248
+ }
2249
+ )
2250
+ ) : (
2251
+ /* ── Virtualized table body ─────────────────────────── */
2252
+ /* @__PURE__ */ jsx5(
2253
+ TableBody_default,
2362
2254
  {
2363
- style: {
2364
- color: "#6b7280",
2365
- paddingLeft: 4,
2366
- paddingRight: 4,
2367
- fontSize: 12,
2368
- userSelect: "none"
2369
- },
2370
- children: "..."
2371
- },
2372
- page
2373
- );
2374
- }
2375
- return /* @__PURE__ */ jsx5(
2376
- "button",
2377
- {
2378
- style: {
2379
- display: "inline-flex",
2380
- height: 24,
2381
- minWidth: 24,
2382
- cursor: "pointer",
2383
- alignItems: "center",
2384
- justifyContent: "center",
2385
- borderRadius: 4,
2386
- paddingLeft: 6,
2387
- paddingRight: 6,
2388
- fontSize: 12,
2389
- color: page === currentPage ? accentColor : void 0,
2390
- background: "none",
2391
- border: "none"
2392
- },
2393
- onClick: () => handlePageChange(page),
2394
- children: page
2395
- },
2396
- page
2397
- );
2398
- }),
2399
- /* @__PURE__ */ jsx5(
2400
- "button",
2401
- {
2402
- onClick: () => handlePageChange(currentPage + 1),
2403
- disabled: currentPage === totalPages,
2404
- style: {
2405
- display: "inline-flex",
2406
- height: 24,
2407
- width: 24,
2408
- cursor: currentPage === totalPages ? "not-allowed" : "pointer",
2409
- alignItems: "center",
2410
- justifyContent: "center",
2411
- fontSize: 12,
2412
- opacity: currentPage === totalPages ? 0.3 : 1,
2413
- background: "none",
2414
- border: "none",
2415
- padding: 0,
2416
- color: "inherit"
2417
- },
2418
- title: "Next page",
2419
- children: icons?.chevronRight ?? /* @__PURE__ */ jsx5(ChevronRightIcon, { style: { width: 12, height: 12 } })
2420
- }
2421
- ),
2422
- /* @__PURE__ */ jsx5(
2423
- "button",
2255
+ data: displayData,
2256
+ orderedColumns,
2257
+ rowVirtualizer,
2258
+ columnOffsets,
2259
+ styles,
2260
+ classNames,
2261
+ rowSelection: !showShimmer ? rowSelection : void 0,
2262
+ normalizedSelectedKeys,
2263
+ getRowKey,
2264
+ expandable: !showShimmer ? expandable : void 0,
2265
+ resolvedExpandedKeys,
2266
+ rowHeight,
2267
+ totalTableWidth,
2268
+ scrollAreaWidth,
2269
+ accentColor,
2270
+ scrollContainerRef: tableAreaRef,
2271
+ isLoading: showShimmer,
2272
+ onExpandedRowResize: handleExpandedRowResize,
2273
+ maxExpandedRowHeight
2274
+ }
2275
+ )
2276
+ )
2277
+ ]
2278
+ }
2279
+ )
2280
+ ]
2281
+ }
2282
+ )
2283
+ )
2284
+ }
2285
+ ),
2286
+ pagination !== false && /* @__PURE__ */ jsxs5(
2287
+ "div",
2288
+ {
2289
+ style: {
2290
+ display: "flex",
2291
+ height: 36,
2292
+ alignItems: "center",
2293
+ justifyContent: "space-between",
2294
+ borderTop: "1px solid rgba(128,128,128,0.2)",
2295
+ paddingLeft: 12,
2296
+ paddingRight: 12,
2297
+ fontSize: 12,
2298
+ backdropFilter: "blur(8px)",
2299
+ backgroundColor: "rgba(128,128,128,0.06)",
2300
+ gap: 12
2301
+ },
2302
+ children: [
2303
+ /* @__PURE__ */ jsx5("div", { style: { display: "flex", flex: "1 1 0%", alignItems: "center" }, children: (() => {
2304
+ const rangeStart = total > 0 ? (currentPage - 1) * pageSize + 1 : 0;
2305
+ const rangeEnd = Math.min(currentPage * pageSize, total);
2306
+ return pagination?.showTotal ? /* @__PURE__ */ jsxs5("span", { style: { color: "#6b7280", fontSize: 12 }, children: [
2307
+ "Showing",
2308
+ " ",
2309
+ pagination.showTotal(total, [rangeStart, rangeEnd]),
2310
+ " of",
2311
+ " ",
2312
+ total,
2313
+ " items"
2314
+ ] }) : /* @__PURE__ */ jsxs5("span", { style: { color: "#6b7280", fontSize: 12 }, children: [
2315
+ rangeStart,
2316
+ "\u2013",
2317
+ rangeEnd,
2318
+ " of ",
2319
+ total
2320
+ ] });
2321
+ })() }),
2322
+ /* @__PURE__ */ jsxs5(
2323
+ "div",
2324
+ {
2325
+ style: {
2326
+ display: "flex",
2327
+ flex: "1 1 0%",
2328
+ alignItems: "center",
2329
+ justifyContent: "center",
2330
+ gap: 4
2331
+ },
2332
+ children: [
2333
+ /* @__PURE__ */ jsx5(
2334
+ "button",
2335
+ {
2336
+ onClick: () => handlePageChange(1),
2337
+ disabled: currentPage === 1,
2338
+ style: {
2339
+ display: "inline-flex",
2340
+ height: 24,
2341
+ width: 24,
2342
+ cursor: currentPage === 1 ? "not-allowed" : "pointer",
2343
+ alignItems: "center",
2344
+ justifyContent: "center",
2345
+ fontSize: 12,
2346
+ opacity: currentPage === 1 ? 0.3 : 1,
2347
+ background: "none",
2348
+ border: "none",
2349
+ padding: 0,
2350
+ color: "inherit"
2351
+ },
2352
+ title: "First page",
2353
+ children: icons?.chevronsLeft ?? /* @__PURE__ */ jsx5(ChevronsLeftIcon, { style: { width: 12, height: 12 } })
2354
+ }
2355
+ ),
2356
+ /* @__PURE__ */ jsx5(
2357
+ "button",
2358
+ {
2359
+ onClick: () => handlePageChange(currentPage - 1),
2360
+ disabled: currentPage === 1,
2361
+ style: {
2362
+ display: "inline-flex",
2363
+ height: 24,
2364
+ width: 24,
2365
+ cursor: currentPage === 1 ? "not-allowed" : "pointer",
2366
+ alignItems: "center",
2367
+ justifyContent: "center",
2368
+ fontSize: 12,
2369
+ opacity: currentPage === 1 ? 0.3 : 1,
2370
+ background: "none",
2371
+ border: "none",
2372
+ padding: 0,
2373
+ color: "inherit"
2374
+ },
2375
+ title: "Previous page",
2376
+ children: icons?.chevronLeft ?? /* @__PURE__ */ jsx5(ChevronLeftIcon, { style: { width: 12, height: 12 } })
2377
+ }
2378
+ ),
2379
+ getPageNumbers().map((page) => {
2380
+ if (page === "ellipsis-left" || page === "ellipsis-right") {
2381
+ return /* @__PURE__ */ jsx5(
2382
+ "span",
2424
2383
  {
2425
- onClick: () => handlePageChange(totalPages),
2426
- disabled: currentPage === totalPages,
2427
2384
  style: {
2428
- display: "inline-flex",
2429
- height: 24,
2430
- width: 24,
2431
- cursor: currentPage === totalPages ? "not-allowed" : "pointer",
2432
- alignItems: "center",
2433
- justifyContent: "center",
2385
+ color: "#6b7280",
2386
+ paddingLeft: 4,
2387
+ paddingRight: 4,
2434
2388
  fontSize: 12,
2435
- opacity: currentPage === totalPages ? 0.3 : 1,
2436
- background: "none",
2437
- border: "none",
2438
- padding: 0,
2439
- color: "inherit"
2389
+ userSelect: "none"
2440
2390
  },
2441
- title: "Last page",
2442
- children: icons?.chevronsRight ?? /* @__PURE__ */ jsx5(ChevronsRightIcon, { style: { width: 12, height: 12 } })
2443
- }
2444
- )
2445
- ]
2446
- }
2447
- ),
2448
- /* @__PURE__ */ jsx5(
2449
- "div",
2450
- {
2451
- style: {
2452
- display: "flex",
2453
- flex: "1 1 0%",
2454
- alignItems: "center",
2455
- justifyContent: "flex-end",
2456
- gap: 8
2457
- },
2458
- children: /* @__PURE__ */ jsx5(
2459
- "select",
2391
+ children: "..."
2392
+ },
2393
+ page
2394
+ );
2395
+ }
2396
+ return /* @__PURE__ */ jsx5(
2397
+ "button",
2460
2398
  {
2461
- value: pageSize,
2462
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
2463
2399
  style: {
2400
+ display: "inline-flex",
2401
+ height: 24,
2402
+ minWidth: 24,
2464
2403
  cursor: "pointer",
2404
+ alignItems: "center",
2405
+ justifyContent: "center",
2465
2406
  borderRadius: 4,
2466
- border: "1px solid #e5e7eb",
2467
2407
  paddingLeft: 6,
2468
2408
  paddingRight: 6,
2469
- paddingTop: 2,
2470
- paddingBottom: 2,
2471
2409
  fontSize: 12,
2472
- height: 24,
2473
- background: "inherit",
2474
- color: "inherit"
2410
+ color: page === currentPage ? accentColor : void 0,
2411
+ background: "none",
2412
+ border: "none"
2475
2413
  },
2476
- children: [10, 15, 20, 25, 50, 100].map((size) => /* @__PURE__ */ jsxs5("option", { value: size, children: [
2477
- size,
2478
- " / page"
2479
- ] }, size))
2480
- }
2481
- )
2414
+ onClick: () => handlePageChange(page),
2415
+ children: page
2416
+ },
2417
+ page
2418
+ );
2419
+ }),
2420
+ /* @__PURE__ */ jsx5(
2421
+ "button",
2422
+ {
2423
+ onClick: () => handlePageChange(currentPage + 1),
2424
+ disabled: currentPage === totalPages,
2425
+ style: {
2426
+ display: "inline-flex",
2427
+ height: 24,
2428
+ width: 24,
2429
+ cursor: currentPage === totalPages ? "not-allowed" : "pointer",
2430
+ alignItems: "center",
2431
+ justifyContent: "center",
2432
+ fontSize: 12,
2433
+ opacity: currentPage === totalPages ? 0.3 : 1,
2434
+ background: "none",
2435
+ border: "none",
2436
+ padding: 0,
2437
+ color: "inherit"
2438
+ },
2439
+ title: "Next page",
2440
+ children: icons?.chevronRight ?? /* @__PURE__ */ jsx5(ChevronRightIcon, { style: { width: 12, height: 12 } })
2441
+ }
2442
+ ),
2443
+ /* @__PURE__ */ jsx5(
2444
+ "button",
2445
+ {
2446
+ onClick: () => handlePageChange(totalPages),
2447
+ disabled: currentPage === totalPages,
2448
+ style: {
2449
+ display: "inline-flex",
2450
+ height: 24,
2451
+ width: 24,
2452
+ cursor: currentPage === totalPages ? "not-allowed" : "pointer",
2453
+ alignItems: "center",
2454
+ justifyContent: "center",
2455
+ fontSize: 12,
2456
+ opacity: currentPage === totalPages ? 0.3 : 1,
2457
+ background: "none",
2458
+ border: "none",
2459
+ padding: 0,
2460
+ color: "inherit"
2461
+ },
2462
+ title: "Last page",
2463
+ children: icons?.chevronsRight ?? /* @__PURE__ */ jsx5(ChevronsRightIcon, { style: { width: 12, height: 12 } })
2464
+ }
2465
+ )
2466
+ ]
2467
+ }
2468
+ ),
2469
+ /* @__PURE__ */ jsx5(
2470
+ "div",
2471
+ {
2472
+ style: {
2473
+ display: "flex",
2474
+ flex: "1 1 0%",
2475
+ alignItems: "center",
2476
+ justifyContent: "flex-end",
2477
+ gap: 8
2478
+ },
2479
+ children: /* @__PURE__ */ jsx5(
2480
+ "select",
2481
+ {
2482
+ value: pageSize,
2483
+ onChange: (e) => handlePageSizeChange(Number(e.target.value)),
2484
+ style: {
2485
+ cursor: "pointer",
2486
+ borderRadius: 4,
2487
+ border: "1px solid rgba(128,128,128,0.2)",
2488
+ paddingLeft: 6,
2489
+ paddingRight: 6,
2490
+ paddingTop: 2,
2491
+ paddingBottom: 2,
2492
+ fontSize: 12,
2493
+ height: 24,
2494
+ background: "inherit",
2495
+ color: "inherit"
2496
+ },
2497
+ children: [10, 15, 20, 25, 50, 100].map((size) => /* @__PURE__ */ jsxs5("option", { value: size, children: [
2498
+ size,
2499
+ " / page"
2500
+ ] }, size))
2482
2501
  }
2483
2502
  )
2484
- ]
2485
- }
2486
- )
2487
- ]
2488
- }
2489
- ),
2490
- /* @__PURE__ */ jsx5(DragOverlay, { children: activeColumn ? /* @__PURE__ */ jsx5(
2491
- "div",
2492
- {
2493
- className: `${classNames.header ?? ""} ${classNames.dragHeader ?? ""}`,
2494
- style: {
2495
- display: "flex",
2496
- height: 36,
2497
- alignItems: "center",
2498
- overflow: "hidden",
2499
- textOverflow: "ellipsis",
2500
- whiteSpace: "nowrap",
2501
- border: "1px dashed #e5e7eb",
2502
- boxShadow: "0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1)",
2503
- backdropFilter: "blur(8px)",
2504
- width: `${activeColumn.width ?? 150}px`,
2505
- cursor: "grabbing",
2506
- ...styles.header,
2507
- ...styles.dragHeader
2508
- },
2509
- children: /* @__PURE__ */ jsxs5(
2510
- "div",
2511
- {
2512
- style: {
2513
- position: "relative",
2514
- zIndex: 10,
2515
- display: "flex",
2516
- height: "100%",
2517
- flex: "1 1 0%",
2518
- alignItems: "center",
2519
- gap: 4,
2520
- overflow: "hidden",
2521
- textOverflow: "ellipsis",
2522
- whiteSpace: "nowrap",
2523
- paddingLeft: 8,
2524
- paddingRight: 8,
2525
- fontWeight: 500
2526
- },
2527
- children: [
2528
- icons?.gripVertical ?? gripIcon ?? /* @__PURE__ */ jsx5(GripVerticalIcon, { style: { width: 12, height: 12, flexShrink: 0 } }),
2529
- /* @__PURE__ */ jsx5(
2530
- "div",
2531
- {
2532
- style: {
2533
- minWidth: 0,
2534
- overflow: "hidden",
2535
- textOverflow: "ellipsis",
2536
- whiteSpace: "nowrap",
2537
- textAlign: "left",
2538
- userSelect: "none"
2539
- },
2540
- children: typeof activeColumn.title === "string" ? activeColumn.title : activeColumn.key
2541
- }
2542
- )
2543
- ]
2544
- }
2545
- )
2546
- }
2547
- ) : null })
2548
- ]
2549
- }
2550
- );
2503
+ }
2504
+ )
2505
+ ]
2506
+ }
2507
+ )
2508
+ ]
2509
+ }
2510
+ ),
2511
+ typeof document !== "undefined" && createPortal2(
2512
+ /* @__PURE__ */ jsx5(
2513
+ "div",
2514
+ {
2515
+ ref: ghostRef,
2516
+ className: `${classNames.header ?? ""} ${classNames.dragHeader ?? ""}`,
2517
+ style: {
2518
+ display: "none",
2519
+ position: "fixed",
2520
+ zIndex: 99999,
2521
+ height: 36,
2522
+ alignItems: "center",
2523
+ overflow: "hidden",
2524
+ textOverflow: "ellipsis",
2525
+ whiteSpace: "nowrap",
2526
+ borderRadius: 6,
2527
+ border: "1px dashed rgba(128,128,128,0.3)",
2528
+ boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
2529
+ backdropFilter: "blur(16px)",
2530
+ WebkitBackdropFilter: "blur(16px)",
2531
+ backgroundColor: "rgba(128,128,128,0.12)",
2532
+ cursor: "grabbing",
2533
+ pointerEvents: "none",
2534
+ ...styles.header,
2535
+ ...styles.dragHeader
2536
+ },
2537
+ children: /* @__PURE__ */ jsxs5(
2538
+ "div",
2539
+ {
2540
+ style: {
2541
+ display: "flex",
2542
+ height: "100%",
2543
+ flex: "1 1 0%",
2544
+ alignItems: "center",
2545
+ gap: 4,
2546
+ overflow: "hidden",
2547
+ paddingLeft: 8,
2548
+ paddingRight: 8,
2549
+ fontWeight: 500
2550
+ },
2551
+ children: [
2552
+ icons?.gripVertical ?? gripIcon ?? /* @__PURE__ */ jsx5(
2553
+ GripVerticalIcon,
2554
+ {
2555
+ style: { width: 12, height: 12, flexShrink: 0 }
2556
+ }
2557
+ ),
2558
+ /* @__PURE__ */ jsx5(
2559
+ "div",
2560
+ {
2561
+ style: {
2562
+ minWidth: 0,
2563
+ overflow: "hidden",
2564
+ textOverflow: "ellipsis",
2565
+ whiteSpace: "nowrap",
2566
+ textAlign: "left",
2567
+ userSelect: "none"
2568
+ },
2569
+ children: activeColumn ? typeof activeColumn.title === "string" ? activeColumn.title : activeColumn.key : ""
2570
+ }
2571
+ )
2572
+ ]
2573
+ }
2574
+ )
2575
+ }
2576
+ ),
2577
+ document.body
2578
+ )
2579
+ ] });
2551
2580
  }
2552
2581
  export {
2553
2582
  BoltTable,