bolt-table 0.1.38 → 0.1.40

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
@@ -149,7 +149,8 @@ var DraggableHeader = React.memo(
149
149
  headerGridRow = 1,
150
150
  headerHeight = 36,
151
151
  stickyTop = 0,
152
- isFirstColumn = false
152
+ isFirstColumn = false,
153
+ onAutoFitColumn
153
154
  }) => {
154
155
  const effectivelySortable = isColumnSortable(column);
155
156
  const effectivelyFilterable = !disabledFilters && isColumnFilterable(column);
@@ -217,6 +218,12 @@ var DraggableHeader = React.memo(
217
218
  if (column.pinned) return;
218
219
  onResizeStart?.(column.key, e);
219
220
  };
221
+ const handleResizeDoubleClick = (e) => {
222
+ e.preventDefault();
223
+ e.stopPropagation();
224
+ if (column.pinned) return;
225
+ onAutoFitColumn?.(column.key);
226
+ };
220
227
  const columnWidth = column.width ?? 150;
221
228
  const widthPx = `${columnWidth}px`;
222
229
  const isPinned = Boolean(column.pinned);
@@ -364,7 +371,8 @@ var DraggableHeader = React.memo(
364
371
  padding: 0
365
372
  },
366
373
  onMouseDown: handleResizeStart,
367
- "aria-label": `Resize ${column.key} column`,
374
+ onDoubleClick: handleResizeDoubleClick,
375
+ "aria-label": `Resize ${column.key} column (double-click to auto-fit)`,
368
376
  children: /* @__PURE__ */ jsx2(
369
377
  "div",
370
378
  {
@@ -1461,7 +1469,8 @@ var TableBody = ({
1461
1469
  enableDynamicRowHeight = false,
1462
1470
  onRowHeightChange,
1463
1471
  columnGridIndexMap,
1464
- cellStyleFn
1472
+ cellStyleFn,
1473
+ onRowDragStart
1465
1474
  }) => {
1466
1475
  const virtualItems = rowVirtualizer.getVirtualItems();
1467
1476
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1543,6 +1552,7 @@ var TableBody = ({
1543
1552
  "div",
1544
1553
  {
1545
1554
  "data-row-key": rowKey,
1555
+ "data-row-index": virtualRow.index,
1546
1556
  "data-column-key": col.key,
1547
1557
  "data-bt-cell": "",
1548
1558
  "data-selected": isSelected || void 0,
@@ -1555,7 +1565,45 @@ var TableBody = ({
1555
1565
  height: enableDynamicRowHeight ? void 0 : `${virtualRow.size}px`,
1556
1566
  minHeight: enableDynamicRowHeight ? `${rowHeight}px` : void 0
1557
1567
  },
1558
- children: enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ jsx4(
1568
+ children: col.key === "__drag__" && onRowDragStart ? /* @__PURE__ */ jsx4(
1569
+ "div",
1570
+ {
1571
+ style: {
1572
+ height: `${rowHeight}px`,
1573
+ display: "flex",
1574
+ alignItems: "center",
1575
+ justifyContent: "center",
1576
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
1577
+ ...rowSty
1578
+ },
1579
+ children: /* @__PURE__ */ jsx4(
1580
+ "span",
1581
+ {
1582
+ "data-bt-row-grip": "",
1583
+ onPointerDown: (e) => {
1584
+ if (e.button !== 0) return;
1585
+ onRowDragStart(virtualRow.index, e);
1586
+ },
1587
+ style: {
1588
+ display: "flex",
1589
+ alignItems: "center",
1590
+ justifyContent: "center",
1591
+ touchAction: "none",
1592
+ width: "100%",
1593
+ height: "100%"
1594
+ },
1595
+ children: /* @__PURE__ */ jsxs4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1596
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "5", r: "1" }),
1597
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "12", r: "1" }),
1598
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "19", r: "1" }),
1599
+ /* @__PURE__ */ jsx4("circle", { cx: "15", cy: "5", r: "1" }),
1600
+ /* @__PURE__ */ jsx4("circle", { cx: "15", cy: "12", r: "1" }),
1601
+ /* @__PURE__ */ jsx4("circle", { cx: "15", cy: "19", r: "1" })
1602
+ ] })
1603
+ }
1604
+ )
1605
+ }
1606
+ ) : enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ jsx4(
1559
1607
  DynamicRowMeasurer,
1560
1608
  {
1561
1609
  index: virtualRow.index,
@@ -2050,7 +2098,9 @@ function BoltTable({
2050
2098
  onAIQuery,
2051
2099
  onAIResponse,
2052
2100
  aiPlaceholder = "Ask AI anything about your data...",
2053
- aiButtonLabel
2101
+ aiButtonLabel,
2102
+ rowDragEnabled = false,
2103
+ onRowReorder
2054
2104
  }) {
2055
2105
  const data = useMemo2(() => {
2056
2106
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -2247,6 +2297,7 @@ function BoltTable({
2247
2297
  }
2248
2298
  });
2249
2299
  const [showSavedFilters, setShowSavedFilters] = useState3(false);
2300
+ const [justSavedFilter, setJustSavedFilter] = useState3(false);
2250
2301
  const savedFiltersRef = useRef4(null);
2251
2302
  React4.useEffect(() => {
2252
2303
  if (!showSavedFilters) return;
@@ -2268,6 +2319,8 @@ function BoltTable({
2268
2319
  localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2269
2320
  } catch {
2270
2321
  }
2322
+ setJustSavedFilter(true);
2323
+ setTimeout(() => setJustSavedFilter(false), 1500);
2271
2324
  }, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
2272
2325
  const removeSavedFilter = useCallback2((index) => {
2273
2326
  const next = savedAIFilters.filter((_, i) => i !== index);
@@ -2481,17 +2534,29 @@ function BoltTable({
2481
2534
  }, []);
2482
2535
  const getRowKey = useCallback2(
2483
2536
  (record, index) => {
2484
- if (record == null) return String(index);
2537
+ if (record == null) return `__row_${index}`;
2485
2538
  try {
2486
- if (typeof rowKey === "function") return String(rowKey(record));
2539
+ if (typeof rowKey === "function") {
2540
+ const result = rowKey(record);
2541
+ const str = String(result);
2542
+ if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
2543
+ return `__row_${index}`;
2544
+ }
2545
+ return str;
2546
+ }
2487
2547
  if (typeof rowKey === "string") {
2488
2548
  const val = record[rowKey];
2489
- return val != null ? String(val) : String(index);
2549
+ if (val == null) return `__row_${index}`;
2550
+ const str = String(val);
2551
+ if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
2552
+ return `__row_${index}`;
2553
+ }
2554
+ return str;
2490
2555
  }
2491
2556
  } catch {
2492
- return String(index);
2557
+ return `__row_${index}`;
2493
2558
  }
2494
- return String(index);
2559
+ return `__row_${index}`;
2495
2560
  },
2496
2561
  [rowKey]
2497
2562
  );
@@ -2525,11 +2590,20 @@ function BoltTable({
2525
2590
  (record, index) => {
2526
2591
  if (record == null) return index;
2527
2592
  try {
2528
- if (typeof rowKey === "function") return rowKey(record);
2593
+ if (typeof rowKey === "function") {
2594
+ const result = rowKey(record);
2595
+ if (result == null || typeof result === "number" && Number.isNaN(result)) return index;
2596
+ const str = String(result);
2597
+ if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
2598
+ return result;
2599
+ }
2529
2600
  if (typeof rowKey === "string") {
2530
2601
  const val = record[rowKey];
2602
+ if (val == null || typeof val === "number" && Number.isNaN(val)) return index;
2603
+ const str = String(val);
2604
+ if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
2531
2605
  if (typeof val === "number" || typeof val === "string") return val;
2532
- return val != null ? String(val) : index;
2606
+ return str;
2533
2607
  }
2534
2608
  } catch {
2535
2609
  return index;
@@ -2616,6 +2690,19 @@ function BoltTable({
2616
2690
  };
2617
2691
  return [selectionColumn, ...columnsWithExpand];
2618
2692
  }, [rowSelection, columnsWithExpand]);
2693
+ const columnsWithDrag = useMemo2(() => {
2694
+ if (!rowDragEnabled || !onRowReorder) return columnsWithSelection;
2695
+ const dragColumn = {
2696
+ key: "__drag__",
2697
+ dataIndex: "__drag__",
2698
+ title: "",
2699
+ width: 36,
2700
+ pinned: "left",
2701
+ hidden: false,
2702
+ render: () => null
2703
+ };
2704
+ return [dragColumn, ...columnsWithSelection];
2705
+ }, [rowDragEnabled, onRowReorder, columnsWithSelection]);
2619
2706
  const resizeOverlayRef = useRef4(null);
2620
2707
  const tableAreaRef = useRef4(null);
2621
2708
  const [scrollAreaWidth, setScrollAreaWidth] = useState3(0);
@@ -2698,7 +2785,7 @@ function BoltTable({
2698
2785
  onColumnOrderChangeRef.current = onColumnOrderChange;
2699
2786
  const handleColumnDragStart = useCallback2(
2700
2787
  (columnKey, e) => {
2701
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2788
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2702
2789
  const headerEl = e.currentTarget.closest(
2703
2790
  "[data-column-key]"
2704
2791
  );
@@ -2733,7 +2820,7 @@ function BoltTable({
2733
2820
  let newOverId = null;
2734
2821
  headers.forEach((h) => {
2735
2822
  const key = h.dataset.columnKey;
2736
- if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
2823
+ if (!key || key === "__select__" || key === "__expand__" || key === "__drag__" || key === columnKey) {
2737
2824
  h.removeAttribute("data-drag-over");
2738
2825
  return;
2739
2826
  }
@@ -2785,16 +2872,97 @@ function BoltTable({
2785
2872
  },
2786
2873
  []
2787
2874
  );
2875
+ const rowDragGhostRef = useRef4(null);
2876
+ const rowDragFromRef = useRef4(null);
2877
+ const rowDragOverRef = useRef4(null);
2878
+ const onRowReorderRef = useRef4(onRowReorder);
2879
+ onRowReorderRef.current = onRowReorder;
2880
+ const handleRowDragStart = useCallback2(
2881
+ (rowIndex, e) => {
2882
+ if (!onRowReorderRef.current) return;
2883
+ e.preventDefault();
2884
+ rowDragFromRef.current = rowIndex;
2885
+ rowDragOverRef.current = null;
2886
+ const target = e.currentTarget.closest("[data-row-key]");
2887
+ const rowHeight2 = target?.offsetHeight ?? 40;
2888
+ const ghost = rowDragGhostRef.current;
2889
+ if (ghost) {
2890
+ ghost.textContent = `Row ${rowIndex + 1}`;
2891
+ ghost.style.display = "flex";
2892
+ ghost.style.left = `${e.clientX + 12}px`;
2893
+ ghost.style.top = `${e.clientY - 12}px`;
2894
+ ghost.style.height = `${Math.min(rowHeight2, 36)}px`;
2895
+ }
2896
+ const grabStyle = document.createElement("style");
2897
+ grabStyle.textContent = "* { cursor: grabbing !important; }";
2898
+ document.head.appendChild(grabStyle);
2899
+ const scrollEl = tableAreaRef.current;
2900
+ const onMove = (ev) => {
2901
+ if (ghost) {
2902
+ ghost.style.left = `${ev.clientX + 12}px`;
2903
+ ghost.style.top = `${ev.clientY - 12}px`;
2904
+ }
2905
+ if (!scrollEl) return;
2906
+ const cells = scrollEl.querySelectorAll("[data-bt-cell][data-row-key]");
2907
+ let closestIdx = null;
2908
+ let closestDist = Infinity;
2909
+ cells.forEach((cell) => {
2910
+ const rk = cell.dataset.rowKey;
2911
+ if (!rk) return;
2912
+ const rect = cell.getBoundingClientRect();
2913
+ const midY = rect.top + rect.height / 2;
2914
+ const dist = Math.abs(ev.clientY - midY);
2915
+ if (dist < closestDist) {
2916
+ closestDist = dist;
2917
+ const idxAttr = cell.dataset.rowIndex;
2918
+ if (idxAttr != null) closestIdx = Number(idxAttr);
2919
+ }
2920
+ });
2921
+ scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
2922
+ (el) => el.removeAttribute("data-row-drag-over")
2923
+ );
2924
+ if (closestIdx != null && closestIdx !== rowDragFromRef.current) {
2925
+ rowDragOverRef.current = closestIdx;
2926
+ scrollEl.querySelectorAll(
2927
+ `[data-bt-cell][data-row-index="${closestIdx}"]`
2928
+ ).forEach((el) => el.setAttribute("data-row-drag-over", ""));
2929
+ } else {
2930
+ rowDragOverRef.current = null;
2931
+ }
2932
+ };
2933
+ const onUp = () => {
2934
+ document.removeEventListener("pointermove", onMove);
2935
+ document.removeEventListener("pointerup", onUp);
2936
+ grabStyle.remove();
2937
+ if (ghost) ghost.style.display = "none";
2938
+ if (scrollEl) {
2939
+ scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
2940
+ (el) => el.removeAttribute("data-row-drag-over")
2941
+ );
2942
+ }
2943
+ const from = rowDragFromRef.current;
2944
+ const to = rowDragOverRef.current;
2945
+ if (from != null && to != null && from !== to) {
2946
+ onRowReorderRef.current?.(from, to);
2947
+ }
2948
+ rowDragFromRef.current = null;
2949
+ rowDragOverRef.current = null;
2950
+ };
2951
+ document.addEventListener("pointermove", onMove);
2952
+ document.addEventListener("pointerup", onUp);
2953
+ },
2954
+ []
2955
+ );
2788
2956
  const handleResizeStart = (columnKey, e) => {
2789
2957
  e.preventDefault();
2790
2958
  e.stopPropagation();
2791
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2792
- const columnIndex = columnsWithSelection.findIndex(
2959
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2960
+ const columnIndex = columnsWithDrag.findIndex(
2793
2961
  (col) => col.key === columnKey
2794
2962
  );
2795
2963
  if (columnIndex === -1) return;
2796
- if (columnsWithSelection[columnIndex].pinned) return;
2797
- const column = columnsWithSelection[columnIndex];
2964
+ if (columnsWithDrag[columnIndex].pinned) return;
2965
+ const column = columnsWithDrag[columnIndex];
2798
2966
  const startWidth = column.width ?? 150;
2799
2967
  resizeStateRef.current = {
2800
2968
  columnKey,
@@ -2851,9 +3019,50 @@ function BoltTable({
2851
3019
  });
2852
3020
  onColumnResize?.(columnKey, finalWidth);
2853
3021
  }, [onColumnResize]);
3022
+ const handleAutoFitColumn = useCallback2((columnKey) => {
3023
+ const scrollEl = tableAreaRef.current;
3024
+ if (!scrollEl) return;
3025
+ const col = columnsWithDrag.find((c) => c.key === columnKey);
3026
+ if (!col) return;
3027
+ const headerEl = scrollEl.querySelector(
3028
+ `[data-column-key="${columnKey}"] [data-bt-grip]`
3029
+ )?.parentElement ?? scrollEl.querySelector(`[data-column-key="${columnKey}"]`);
3030
+ let maxWidth = 0;
3031
+ if (headerEl) {
3032
+ const title = typeof col.title === "string" ? col.title : col.key;
3033
+ const canvas = document.createElement("canvas");
3034
+ const ctx = canvas.getContext("2d");
3035
+ if (ctx) {
3036
+ const computedStyle = window.getComputedStyle(headerEl);
3037
+ ctx.font = `${computedStyle.fontWeight} ${computedStyle.fontSize} ${computedStyle.fontFamily}`;
3038
+ maxWidth = ctx.measureText(title).width + 60;
3039
+ }
3040
+ }
3041
+ const cells = scrollEl.querySelectorAll(
3042
+ `[data-bt-cell][data-column-key="${columnKey}"]`
3043
+ );
3044
+ cells.forEach((cell) => {
3045
+ const inner = cell.querySelector("div > div");
3046
+ if (inner) {
3047
+ const scrollW = inner.scrollWidth;
3048
+ if (scrollW > maxWidth) maxWidth = scrollW;
3049
+ }
3050
+ });
3051
+ const finalWidth = Math.max(60, Math.min(Math.ceil(maxWidth) + 24, 800));
3052
+ manuallyResizedRef.current.add(columnKey);
3053
+ React4.startTransition(() => {
3054
+ setColumnWidths((prev) => {
3055
+ const next = new Map(prev);
3056
+ next.set(columnKey, finalWidth);
3057
+ return next;
3058
+ });
3059
+ });
3060
+ onColumnResize?.(columnKey, finalWidth);
3061
+ }, [columnsWithDrag, onColumnResize]);
2854
3062
  const { leftPinned, unpinned, rightPinned } = useMemo2(() => {
2855
- const columnMap = new Map(columnsWithSelection.map((c) => [c.key, c]));
3063
+ const columnMap = new Map(columnsWithDrag.map((c) => [c.key, c]));
2856
3064
  const systemKeys = [
3065
+ ...rowDragEnabled && onRowReorder ? ["__drag__"] : [],
2857
3066
  ...rowSelection ? ["__select__"] : [],
2858
3067
  ...expandable ? ["__expand__"] : []
2859
3068
  ];
@@ -2865,7 +3074,7 @@ function BoltTable({
2865
3074
  else center.push(col);
2866
3075
  });
2867
3076
  return { leftPinned: left, unpinned: center, rightPinned: right };
2868
- }, [columnOrder, columnsWithSelection, rowSelection, expandable]);
3077
+ }, [columnOrder, columnsWithDrag, rowSelection, expandable, rowDragEnabled, onRowReorder]);
2869
3078
  const orderedColumns = useMemo2(
2870
3079
  () => [...leftPinned, ...unpinned, ...rightPinned],
2871
3080
  [leftPinned, unpinned, rightPinned]
@@ -2873,7 +3082,7 @@ function BoltTable({
2873
3082
  const freshOrderedColumns = useMemo2(() => {
2874
3083
  const latestMap = new Map(initialColumnsRef.current.map((c) => [c.key, c]));
2875
3084
  return orderedColumns.map((col) => {
2876
- if (col.key === "__select__" || col.key === "__expand__") return col;
3085
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return col;
2877
3086
  const latest = latestMap.get(col.key);
2878
3087
  if (!latest) return col;
2879
3088
  return {
@@ -3366,7 +3575,7 @@ function BoltTable({
3366
3575
  return freshOrderedColumns;
3367
3576
  return freshOrderedColumns.filter((col, idx) => {
3368
3577
  if (col.pinned) return true;
3369
- if (col.key === "__select__" || col.key === "__expand__") return true;
3578
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return true;
3370
3579
  return idx >= visibleColumnRange.start && idx <= visibleColumnRange.end;
3371
3580
  });
3372
3581
  }, [enableColumnVirtualization, visibleColumnRange, freshOrderedColumns]);
@@ -3513,8 +3722,11 @@ function BoltTable({
3513
3722
  [data-bt-resize]:hover [data-bt-resize-line] {
3514
3723
  opacity: 1 !important;
3515
3724
  }
3725
+ [data-bt-ctx-item] {
3726
+ transition: background-color 0.15s ease;
3727
+ }
3516
3728
  [data-bt-ctx-item]:not(:disabled):hover {
3517
- background-color: rgba(128, 128, 128, 0.15);
3729
+ background-color: rgba(128, 128, 128, 0.15) !important;
3518
3730
  }
3519
3731
  [data-bt-header][data-dragging] {
3520
3732
  opacity: 0.2 !important;
@@ -3523,6 +3735,17 @@ function BoltTable({
3523
3735
  border: 1px dashed ${accentColor} !important;
3524
3736
  }
3525
3737
  ${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
3738
+ [data-row-drag-over] {
3739
+ box-shadow: 0 -2px 0 0 ${accentColor} inset;
3740
+ }
3741
+ [data-bt-row-grip] {
3742
+ cursor: grab;
3743
+ opacity: 0.3;
3744
+ transition: opacity 0.15s;
3745
+ }
3746
+ [data-bt-row-grip]:hover {
3747
+ opacity: 0.8;
3748
+ }
3526
3749
  @keyframes bt-spin { to { transform: rotate(360deg); } }
3527
3750
  @keyframes bt-ai-shimmer {
3528
3751
  0% { background-position: -200% 0; }
@@ -3678,7 +3901,7 @@ function BoltTable({
3678
3901
  marginTop: 4
3679
3902
  },
3680
3903
  children: initialColumns.filter(
3681
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3904
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
3682
3905
  ).map((col) => {
3683
3906
  const current = columns.find(
3684
3907
  (c) => c.key === col.key
@@ -4007,14 +4230,15 @@ function BoltTable({
4007
4230
  {
4008
4231
  type: "button",
4009
4232
  onClick: saveCurrentAIFilter,
4233
+ disabled: justSavedFilter,
4010
4234
  style: {
4011
4235
  display: "flex",
4012
4236
  alignItems: "center",
4013
4237
  gap: 4,
4014
- background: `${accentColor}12`,
4015
- border: `1px solid ${accentColor}30`,
4238
+ background: justSavedFilter ? `${accentColor}25` : `${accentColor}12`,
4239
+ border: `1px solid ${justSavedFilter ? accentColor : `${accentColor}30`}`,
4016
4240
  borderRadius: 4,
4017
- cursor: "pointer",
4241
+ cursor: justSavedFilter ? "default" : "pointer",
4018
4242
  padding: "2px 8px",
4019
4243
  color: accentColor,
4020
4244
  fontSize: 11,
@@ -4022,14 +4246,14 @@ function BoltTable({
4022
4246
  fontWeight: 500,
4023
4247
  transition: "all 0.2s ease"
4024
4248
  },
4025
- title: "Save this filter for quick access later",
4249
+ title: justSavedFilter ? "Filter saved!" : "Save this filter for quick access later",
4026
4250
  children: [
4027
- /* @__PURE__ */ jsxs5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4251
+ justSavedFilter ? /* @__PURE__ */ jsx5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4028
4252
  /* @__PURE__ */ jsx5("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
4029
4253
  /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
4030
4254
  /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
4031
4255
  ] }),
4032
- /* @__PURE__ */ jsx5("span", { children: "Save Filter" })
4256
+ /* @__PURE__ */ jsx5("span", { children: justSavedFilter ? "Saved" : "Save Filter" })
4033
4257
  ]
4034
4258
  }
4035
4259
  ),
@@ -4136,7 +4360,7 @@ function BoltTable({
4136
4360
  orderedColumns.map((column) => {
4137
4361
  const isPinned = !!column.pinned;
4138
4362
  const offset = columnOffsets.get(column.key);
4139
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4363
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
4140
4364
  return /* @__PURE__ */ jsx5(
4141
4365
  "div",
4142
4366
  {
@@ -4175,7 +4399,7 @@ function BoltTable({
4175
4399
  children: orderedColumns.map((column, colIndex) => {
4176
4400
  const isPinned = !!column.pinned;
4177
4401
  const offset = columnOffsets.get(column.key);
4178
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4402
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
4179
4403
  const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
4180
4404
  return /* @__PURE__ */ jsx5(
4181
4405
  "div",
@@ -4385,7 +4609,7 @@ function BoltTable({
4385
4609
  }),
4386
4610
  (() => {
4387
4611
  const firstDataColIndex = orderedColumns.findIndex(
4388
- (c) => c.key !== "__select__" && c.key !== "__expand__"
4612
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
4389
4613
  );
4390
4614
  return orderedColumns.map((column, visualIndex) => {
4391
4615
  const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
@@ -4462,6 +4686,39 @@ function BoltTable({
4462
4686
  "__select__"
4463
4687
  );
4464
4688
  }
4689
+ if (column.key === "__drag__") {
4690
+ return /* @__PURE__ */ jsx5(
4691
+ "div",
4692
+ {
4693
+ "data-bt-header": "",
4694
+ "data-bt-pinned": "",
4695
+ className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
4696
+ style: {
4697
+ display: "flex",
4698
+ height: leafHeight,
4699
+ alignItems: "center",
4700
+ justifyContent: "center",
4701
+ overflow: "hidden",
4702
+ whiteSpace: "nowrap",
4703
+ boxSizing: "border-box",
4704
+ position: "sticky",
4705
+ left: columnOffsets.get("__drag__") ?? 0,
4706
+ top: 0,
4707
+ zIndex: 13,
4708
+ width: "36px",
4709
+ gridRow: leafGridRow,
4710
+ ...styles.header,
4711
+ ...styles.pinnedHeader,
4712
+ borderTop: "none",
4713
+ borderLeft: "none",
4714
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
4715
+ borderRight: "1px solid rgba(128,128,128,0.2)"
4716
+ },
4717
+ children: /* @__PURE__ */ jsx5(GripVerticalIcon, { style: { width: 12, height: 12, opacity: 0.4 } })
4718
+ },
4719
+ "__drag__"
4720
+ );
4721
+ }
4465
4722
  if (column.key === "__expand__") {
4466
4723
  return /* @__PURE__ */ jsx5(
4467
4724
  "div",
@@ -4526,7 +4783,8 @@ function BoltTable({
4526
4783
  disabledFilters,
4527
4784
  headerGridRow: leafGridRow,
4528
4785
  headerHeight: leafHeight,
4529
- stickyTop: leafStickyTop
4786
+ stickyTop: leafStickyTop,
4787
+ onAutoFitColumn: handleAutoFitColumn
4530
4788
  },
4531
4789
  column.key
4532
4790
  );
@@ -4611,7 +4869,8 @@ function BoltTable({
4611
4869
  enableDynamicRowHeight,
4612
4870
  onRowHeightChange: handleRowHeightChange,
4613
4871
  columnGridIndexMap,
4614
- cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
4872
+ cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0,
4873
+ onRowDragStart: rowDragEnabled && onRowReorder ? handleRowDragStart : void 0
4615
4874
  }
4616
4875
  )
4617
4876
  ]
@@ -4965,6 +5224,35 @@ function BoltTable({
4965
5224
  ),
4966
5225
  document.body
4967
5226
  ),
5227
+ mounted && rowDragEnabled && onRowReorder && createPortal2(
5228
+ /* @__PURE__ */ jsx5(
5229
+ "div",
5230
+ {
5231
+ ref: rowDragGhostRef,
5232
+ style: {
5233
+ display: "none",
5234
+ position: "fixed",
5235
+ zIndex: 99999,
5236
+ height: 32,
5237
+ fontSize: 11,
5238
+ alignItems: "center",
5239
+ justifyContent: "center",
5240
+ padding: "0 12px",
5241
+ borderRadius: 6,
5242
+ border: `1px dashed ${accentColor}60`,
5243
+ boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
5244
+ backdropFilter: "blur(16px)",
5245
+ WebkitBackdropFilter: "blur(16px)",
5246
+ backgroundColor: "rgba(128,128,128,0.12)",
5247
+ cursor: "grabbing",
5248
+ pointerEvents: "none",
5249
+ fontWeight: 500,
5250
+ color: accentColor
5251
+ }
5252
+ }
5253
+ ),
5254
+ document.body
5255
+ ),
4968
5256
  cellContextMenu && mounted && (() => {
4969
5257
  const menuCol = freshOrderedColumns.find(
4970
5258
  (c) => c.key === cellContextMenu.columnKey
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bolt-table",
3
- "version": "0.1.38",
3
+ "version": "0.1.40",
4
4
  "description": "Virtualized React table with column drag & drop, pinning, resizing, sorting, filtering, and pagination.",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -18,10 +18,6 @@
18
18
  "README.md",
19
19
  "LICENSE"
20
20
  ],
21
- "scripts": {
22
- "build": "tsup",
23
- "prepublishOnly": "npm run build"
24
- },
25
21
  "peerDependencies": {
26
22
  "@tanstack/react-virtual": "^3.13.22",
27
23
  "react": ">=18",
@@ -32,5 +28,8 @@
32
28
  "@types/react-dom": "^19.2.3",
33
29
  "tsup": "^8.0.0",
34
30
  "typescript": "^5.0.0"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup"
35
34
  }
36
- }
35
+ }