bolt-table 0.1.21 → 0.1.23

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
@@ -116,10 +116,11 @@ var DraggableHeader = React.memo(
116
116
  onClearFilter,
117
117
  customContextMenuItems,
118
118
  icons,
119
- onColumnDragStart
119
+ onColumnDragStart,
120
+ disabledFilters
120
121
  }) => {
121
122
  const effectivelySortable = isColumnSortable(column);
122
- const effectivelyFilterable = isColumnFilterable(column);
123
+ const effectivelyFilterable = !disabledFilters && isColumnFilterable(column);
123
124
  const [contextMenu, setContextMenu] = useState(null);
124
125
  const [showFilterInput, setShowFilterInput] = useState(false);
125
126
  const filterInputRef = useRef(null);
@@ -350,7 +351,7 @@ var DraggableHeader = React.memo(
350
351
  }
351
352
  ),
352
353
  contextMenu && typeof document !== "undefined" && createPortal(
353
- /* @__PURE__ */ jsxs2(
354
+ /* @__PURE__ */ jsx2(
354
355
  "div",
355
356
  {
356
357
  ref: menuRef,
@@ -370,308 +371,229 @@ var DraggableHeader = React.memo(
370
371
  top: `${contextMenu.y}px`
371
372
  },
372
373
  role: "menu",
373
- children: [
374
- effectivelySortable && onSort && /* @__PURE__ */ jsxs2(Fragment, { children: [
375
- /* @__PURE__ */ jsxs2(
376
- "button",
377
- {
378
- "data-bt-ctx-item": "",
379
- style: {
380
- cursor: "pointer",
381
- display: "flex",
382
- width: "100%",
383
- alignItems: "center",
384
- gap: 8,
385
- paddingLeft: 12,
386
- paddingRight: 12,
387
- paddingTop: 6,
388
- paddingBottom: 6,
389
- textAlign: "left",
390
- background: "none",
391
- border: "none",
392
- fontSize: "inherit",
393
- color: "inherit",
394
- fontWeight: sortDirection === "asc" ? 600 : void 0,
395
- ...sortDirection === "asc" ? { color: accentColor } : {}
396
- },
397
- onClick: () => {
398
- onSort(column.key, "asc");
399
- setContextMenu(null);
400
- },
401
- children: [
402
- icons?.sortAsc ?? /* @__PURE__ */ jsx2(ArrowUpAZIcon, { style: { width: 12, height: 12 } }),
403
- "Sort Ascending"
404
- ]
405
- }
406
- ),
407
- /* @__PURE__ */ jsxs2(
408
- "button",
409
- {
410
- "data-bt-ctx-item": "",
411
- style: {
412
- cursor: "pointer",
413
- display: "flex",
414
- width: "100%",
415
- alignItems: "center",
416
- gap: 8,
417
- paddingLeft: 12,
418
- paddingRight: 12,
419
- paddingTop: 6,
420
- paddingBottom: 6,
421
- textAlign: "left",
422
- background: "none",
423
- border: "none",
424
- fontSize: "inherit",
425
- color: "inherit",
426
- fontWeight: sortDirection === "desc" ? 600 : void 0,
427
- ...sortDirection === "desc" ? { color: accentColor } : {}
428
- },
429
- onClick: () => {
430
- onSort(column.key, "desc");
431
- setContextMenu(null);
432
- },
433
- children: [
434
- icons?.sortDesc ?? /* @__PURE__ */ jsx2(ArrowDownAZIcon, { style: { width: 12, height: 12 } }),
435
- "Sort Descending"
436
- ]
437
- }
438
- ),
439
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
440
- ] }),
441
- effectivelyFilterable && onFilter && /* @__PURE__ */ jsxs2(Fragment, { children: [
442
- showFilterInput ? /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 4, paddingLeft: 8, paddingRight: 8, paddingTop: 6, paddingBottom: 6 }, children: /* @__PURE__ */ jsx2(
443
- "input",
444
- {
445
- ref: filterInputRef,
446
- type: "text",
447
- autoFocus: true,
448
- defaultValue: filterValue,
449
- placeholder: "Filter...",
450
- style: {
451
- width: "100%",
452
- borderRadius: 4,
453
- border: "1px solid rgba(128,128,128,0.2)",
454
- paddingLeft: 6,
455
- paddingRight: 6,
456
- paddingTop: 2,
457
- paddingBottom: 2,
458
- fontSize: 12,
459
- outline: "none",
460
- background: "inherit",
461
- color: "inherit"
462
- },
463
- onKeyDown: (e) => {
464
- if (e.key === "Enter") {
465
- onFilter(
466
- column.key,
467
- e.target.value
468
- );
469
- setShowFilterInput(false);
374
+ children: (() => {
375
+ const ctxItemBase = {
376
+ cursor: "pointer",
377
+ display: "flex",
378
+ width: "100%",
379
+ alignItems: "center",
380
+ gap: 8,
381
+ paddingLeft: 12,
382
+ paddingRight: 12,
383
+ paddingTop: 6,
384
+ paddingBottom: 6,
385
+ textAlign: "left",
386
+ background: "none",
387
+ border: "none",
388
+ fontSize: "inherit",
389
+ color: "inherit",
390
+ ...styles?.contextMenuItem
391
+ };
392
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
393
+ effectivelySortable && onSort && /* @__PURE__ */ jsxs2(Fragment, { children: [
394
+ /* @__PURE__ */ jsxs2(
395
+ "button",
396
+ {
397
+ "data-bt-ctx-item": "",
398
+ style: {
399
+ ...ctxItemBase,
400
+ fontWeight: sortDirection === "asc" ? 600 : void 0,
401
+ ...sortDirection === "asc" ? { color: accentColor } : {}
402
+ },
403
+ onClick: () => {
404
+ onSort(column.key, "asc");
470
405
  setContextMenu(null);
406
+ },
407
+ children: [
408
+ icons?.sortAsc ?? /* @__PURE__ */ jsx2(ArrowUpAZIcon, { style: { width: 12, height: 12 } }),
409
+ "Sort Ascending"
410
+ ]
411
+ }
412
+ ),
413
+ /* @__PURE__ */ jsxs2(
414
+ "button",
415
+ {
416
+ "data-bt-ctx-item": "",
417
+ style: {
418
+ ...ctxItemBase,
419
+ fontWeight: sortDirection === "desc" ? 600 : void 0,
420
+ ...sortDirection === "desc" ? { color: accentColor } : {}
421
+ },
422
+ onClick: () => {
423
+ onSort(column.key, "desc");
424
+ setContextMenu(null);
425
+ },
426
+ children: [
427
+ icons?.sortDesc ?? /* @__PURE__ */ jsx2(ArrowDownAZIcon, { style: { width: 12, height: 12 } }),
428
+ "Sort Descending"
429
+ ]
430
+ }
431
+ ),
432
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
433
+ ] }),
434
+ effectivelyFilterable && onFilter && /* @__PURE__ */ jsxs2(Fragment, { children: [
435
+ showFilterInput ? /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 4, paddingLeft: 8, paddingRight: 8, paddingTop: 6, paddingBottom: 6 }, children: /* @__PURE__ */ jsx2(
436
+ "input",
437
+ {
438
+ ref: filterInputRef,
439
+ type: "text",
440
+ autoFocus: true,
441
+ defaultValue: filterValue,
442
+ placeholder: "Filter...",
443
+ style: {
444
+ width: "100%",
445
+ borderRadius: 4,
446
+ border: "1px solid rgba(128,128,128,0.2)",
447
+ paddingLeft: 6,
448
+ paddingRight: 6,
449
+ paddingTop: 2,
450
+ paddingBottom: 2,
451
+ fontSize: 12,
452
+ outline: "none",
453
+ background: "inherit",
454
+ color: "inherit"
455
+ },
456
+ onKeyDown: (e) => {
457
+ if (e.key === "Enter") {
458
+ onFilter(
459
+ column.key,
460
+ e.target.value
461
+ );
462
+ setShowFilterInput(false);
463
+ setContextMenu(null);
464
+ }
465
+ if (e.key === "Escape") {
466
+ setShowFilterInput(false);
467
+ }
471
468
  }
472
- if (e.key === "Escape") {
469
+ }
470
+ ) }) : /* @__PURE__ */ jsxs2(
471
+ "button",
472
+ {
473
+ "data-bt-ctx-item": "",
474
+ style: ctxItemBase,
475
+ onClick: () => {
476
+ setShowFilterInput(true);
477
+ },
478
+ children: [
479
+ icons?.filter ?? /* @__PURE__ */ jsx2(FilterIcon, { style: { width: 12, height: 12 } }),
480
+ filterValue ? `Filtered: "${filterValue}"` : "Filter Column"
481
+ ]
482
+ }
483
+ ),
484
+ filterValue && /* @__PURE__ */ jsxs2(
485
+ "button",
486
+ {
487
+ "data-bt-ctx-item": "",
488
+ style: {
489
+ ...ctxItemBase,
490
+ color: "#ef4444"
491
+ },
492
+ onClick: () => {
493
+ onClearFilter?.(column.key);
473
494
  setShowFilterInput(false);
474
- }
495
+ setContextMenu(null);
496
+ },
497
+ children: [
498
+ icons?.filterClear ?? /* @__PURE__ */ jsx2(FilterXIcon, { style: { width: 12, height: 12 } }),
499
+ "Clear Filter"
500
+ ]
475
501
  }
476
- }
477
- ) }) : /* @__PURE__ */ jsxs2(
478
- "button",
479
- {
480
- "data-bt-ctx-item": "",
481
- style: {
482
- cursor: "pointer",
483
- display: "flex",
484
- width: "100%",
485
- alignItems: "center",
486
- gap: 8,
487
- paddingLeft: 12,
488
- paddingRight: 12,
489
- paddingTop: 6,
490
- paddingBottom: 6,
491
- textAlign: "left",
492
- background: "none",
493
- border: "none",
494
- fontSize: "inherit",
495
- color: "inherit"
496
- },
497
- onClick: () => {
498
- setShowFilterInput(true);
499
- },
500
- children: [
501
- icons?.filter ?? /* @__PURE__ */ jsx2(FilterIcon, { style: { width: 12, height: 12 } }),
502
- filterValue ? `Filtered: "${filterValue}"` : "Filter Column"
503
- ]
504
- }
505
- ),
506
- filterValue && /* @__PURE__ */ jsxs2(
502
+ ),
503
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
504
+ ] }),
505
+ /* @__PURE__ */ jsxs2(
507
506
  "button",
508
507
  {
509
508
  "data-bt-ctx-item": "",
510
- style: {
511
- cursor: "pointer",
512
- display: "flex",
513
- width: "100%",
514
- alignItems: "center",
515
- gap: 8,
516
- paddingLeft: 12,
517
- paddingRight: 12,
518
- paddingTop: 6,
519
- paddingBottom: 6,
520
- textAlign: "left",
521
- background: "none",
522
- border: "none",
523
- fontSize: "inherit",
524
- color: "#ef4444"
525
- },
509
+ style: ctxItemBase,
526
510
  onClick: () => {
527
- onClearFilter?.(column.key);
528
- setShowFilterInput(false);
511
+ onTogglePin?.(
512
+ column.key,
513
+ column.pinned === "left" ? false : "left"
514
+ );
529
515
  setContextMenu(null);
530
516
  },
531
517
  children: [
532
- icons?.filterClear ?? /* @__PURE__ */ jsx2(FilterXIcon, { style: { width: 12, height: 12 } }),
533
- "Clear Filter"
518
+ column.pinned === "left" ? icons?.pinOff ?? /* @__PURE__ */ jsx2(PinOffIcon, { style: { width: 12, height: 12 } }) : icons?.pin ?? /* @__PURE__ */ jsx2(PinIcon, { style: { width: 12, height: 12 } }),
519
+ column.pinned === "left" ? "Unpin Left" : "Pin Left"
534
520
  ]
535
521
  }
536
522
  ),
537
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } })
538
- ] }),
539
- /* @__PURE__ */ jsxs2(
540
- "button",
541
- {
542
- "data-bt-ctx-item": "",
543
- style: {
544
- cursor: "pointer",
545
- display: "flex",
546
- width: "100%",
547
- alignItems: "center",
548
- gap: 8,
549
- paddingLeft: 12,
550
- paddingRight: 12,
551
- paddingTop: 6,
552
- paddingBottom: 6,
553
- textAlign: "left",
554
- background: "none",
555
- border: "none",
556
- fontSize: "inherit",
557
- color: "inherit"
558
- },
559
- onClick: () => {
560
- onTogglePin?.(
561
- column.key,
562
- column.pinned === "left" ? false : "left"
563
- );
564
- setContextMenu(null);
565
- },
566
- children: [
567
- column.pinned === "left" ? icons?.pinOff ?? /* @__PURE__ */ jsx2(PinOffIcon, { style: { width: 12, height: 12 } }) : icons?.pin ?? /* @__PURE__ */ jsx2(PinIcon, { style: { width: 12, height: 12 } }),
568
- column.pinned === "left" ? "Unpin Left" : "Pin Left"
569
- ]
570
- }
571
- ),
572
- /* @__PURE__ */ jsxs2(
573
- "button",
574
- {
575
- "data-bt-ctx-item": "",
576
- style: {
577
- cursor: "pointer",
578
- display: "flex",
579
- width: "100%",
580
- alignItems: "center",
581
- gap: 8,
582
- paddingLeft: 12,
583
- paddingRight: 12,
584
- paddingTop: 6,
585
- paddingBottom: 6,
586
- textAlign: "left",
587
- background: "none",
588
- border: "none",
589
- fontSize: "inherit",
590
- color: "inherit"
591
- },
592
- onClick: () => {
593
- onTogglePin?.(
594
- column.key,
595
- column.pinned === "right" ? false : "right"
596
- );
597
- setContextMenu(null);
598
- },
599
- children: [
600
- column.pinned === "right" ? icons?.pinOff ?? /* @__PURE__ */ jsx2(PinOffIcon, { style: { width: 12, height: 12 } }) : icons?.pin ?? /* @__PURE__ */ jsx2(PinIcon, { style: { width: 12, height: 12 } }),
601
- column.pinned === "right" ? "Unpin Right" : "Pin Right"
602
- ]
603
- }
604
- ),
605
- !isPinned && /* @__PURE__ */ jsxs2(Fragment, { children: [
606
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
607
523
  /* @__PURE__ */ jsxs2(
608
524
  "button",
609
525
  {
610
526
  "data-bt-ctx-item": "",
611
- style: {
612
- cursor: "pointer",
613
- display: "flex",
614
- width: "100%",
615
- alignItems: "center",
616
- gap: 8,
617
- paddingLeft: 12,
618
- paddingRight: 12,
619
- paddingTop: 6,
620
- paddingBottom: 6,
621
- textAlign: "left",
622
- background: "none",
623
- border: "none",
624
- fontSize: "inherit",
625
- color: "inherit"
626
- },
527
+ style: ctxItemBase,
627
528
  onClick: () => {
628
- onToggleHide?.(column.key);
529
+ onTogglePin?.(
530
+ column.key,
531
+ column.pinned === "right" ? false : "right"
532
+ );
629
533
  setContextMenu(null);
630
534
  },
631
535
  children: [
632
- icons?.eyeOff ?? /* @__PURE__ */ jsx2(EyeOffIcon, { style: { width: 12, height: 12 } }),
633
- "Hide Column"
536
+ column.pinned === "right" ? icons?.pinOff ?? /* @__PURE__ */ jsx2(PinOffIcon, { style: { width: 12, height: 12 } }) : icons?.pin ?? /* @__PURE__ */ jsx2(PinIcon, { style: { width: 12, height: 12 } }),
537
+ column.pinned === "right" ? "Unpin Right" : "Pin Right"
634
538
  ]
635
539
  }
636
- )
637
- ] }),
638
- customContextMenuItems && customContextMenuItems.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
639
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
640
- customContextMenuItems.map((item) => /* @__PURE__ */ jsxs2(
641
- "button",
642
- {
643
- "data-bt-ctx-item": "",
644
- disabled: item.disabled,
645
- style: {
646
- display: "flex",
647
- width: "100%",
648
- alignItems: "center",
649
- gap: 8,
650
- paddingLeft: 12,
651
- paddingRight: 12,
652
- paddingTop: 6,
653
- paddingBottom: 6,
654
- textAlign: "left",
655
- background: "none",
656
- border: "none",
657
- fontSize: "inherit",
658
- cursor: item.disabled ? "not-allowed" : "pointer",
659
- opacity: item.disabled ? 0.5 : 1,
660
- color: item.danger ? "#ef4444" : "inherit"
661
- },
662
- onClick: () => {
663
- item.onClick(column.key);
664
- setContextMenu(null);
540
+ ),
541
+ !isPinned && /* @__PURE__ */ jsxs2(Fragment, { children: [
542
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
543
+ /* @__PURE__ */ jsxs2(
544
+ "button",
545
+ {
546
+ "data-bt-ctx-item": "",
547
+ style: ctxItemBase,
548
+ onClick: () => {
549
+ onToggleHide?.(column.key);
550
+ setContextMenu(null);
551
+ },
552
+ children: [
553
+ icons?.eyeOff ?? /* @__PURE__ */ jsx2(EyeOffIcon, { style: { width: 12, height: 12 } }),
554
+ "Hide Column"
555
+ ]
556
+ }
557
+ )
558
+ ] }),
559
+ customContextMenuItems && customContextMenuItems.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
560
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
561
+ customContextMenuItems.map((item) => /* @__PURE__ */ jsxs2(
562
+ "button",
563
+ {
564
+ "data-bt-ctx-item": "",
565
+ disabled: item.disabled,
566
+ style: {
567
+ display: "flex",
568
+ width: "100%",
569
+ alignItems: "center",
570
+ gap: 8,
571
+ paddingLeft: 12,
572
+ paddingRight: 12,
573
+ paddingTop: 6,
574
+ paddingBottom: 6,
575
+ textAlign: "left",
576
+ background: "none",
577
+ border: "none",
578
+ fontSize: "inherit",
579
+ cursor: item.disabled ? "not-allowed" : "pointer",
580
+ opacity: item.disabled ? 0.5 : 1,
581
+ color: item.danger ? "#ef4444" : "inherit"
582
+ },
583
+ onClick: () => {
584
+ item.onClick(column.key);
585
+ setContextMenu(null);
586
+ },
587
+ children: [
588
+ item.icon && /* @__PURE__ */ jsx2("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: item.icon }),
589
+ item.label
590
+ ]
665
591
  },
666
- children: [
667
- item.icon && /* @__PURE__ */ jsx2("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: item.icon }),
668
- item.label
669
- ]
670
- },
671
- item.key
672
- ))
673
- ] })
674
- ]
592
+ item.key
593
+ ))
594
+ ] })
595
+ ] });
596
+ })()
675
597
  }
676
598
  ),
677
599
  document.body
@@ -679,7 +601,7 @@ var DraggableHeader = React.memo(
679
601
  ] });
680
602
  },
681
603
  (prevProps, nextProps) => {
682
- 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 && prevProps.classNames === nextProps.classNames && prevProps.styles === nextProps.styles && prevProps.customContextMenuItems === nextProps.customContextMenuItems;
604
+ 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 && prevProps.classNames === nextProps.classNames && prevProps.styles === nextProps.styles && prevProps.customContextMenuItems === nextProps.customContextMenuItems && prevProps.disabledFilters === nextProps.disabledFilters;
683
605
  }
684
606
  );
685
607
  DraggableHeader.displayName = "DraggableHeader";
@@ -1554,7 +1476,10 @@ function BoltTable({
1554
1476
  layoutLoading,
1555
1477
  emptyRenderer,
1556
1478
  rowClassName,
1557
- rowStyle
1479
+ rowStyle,
1480
+ disabledFilters,
1481
+ onCopy,
1482
+ keepPinnedRowsAcrossPages
1558
1483
  }) {
1559
1484
  const data = useMemo2(() => {
1560
1485
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -2002,12 +1927,11 @@ function BoltTable({
2002
1927
  if (col.key === "__select__" || col.key === "__expand__") return col;
2003
1928
  const latest = latestMap.get(col.key);
2004
1929
  if (!latest) return col;
2005
- if (col.render === latest.render && col.shimmerRender === latest.shimmerRender)
2006
- return col;
2007
1930
  return {
2008
- ...col,
2009
- render: latest.render,
2010
- shimmerRender: latest.shimmerRender
1931
+ ...latest,
1932
+ width: col.width,
1933
+ hidden: col.hidden,
1934
+ pinned: col.pinned
2011
1935
  };
2012
1936
  });
2013
1937
  }, [orderedColumns, initialColumns]);
@@ -2053,6 +1977,24 @@ function BoltTable({
2053
1977
  const column = columns.find((col) => col.key === columnKey);
2054
1978
  if (column && !column.pinned) onColumnHide?.(columnKey, !column.hidden);
2055
1979
  };
1980
+ const [internalRowPinning, setInternalRowPinning] = useState2({ top: [], bottom: [] });
1981
+ const resolvedRowPinning = rowPinning === true ? internalRowPinning : rowPinning && typeof rowPinning === "object" ? rowPinning : void 0;
1982
+ const handleRowPin = useCallback((rk, pinned) => {
1983
+ if (onRowPin) {
1984
+ onRowPin(rk, pinned);
1985
+ return;
1986
+ }
1987
+ if (rowPinning === true) {
1988
+ setInternalRowPinning((prev) => {
1989
+ const rkStr = String(rk);
1990
+ const newTop = (prev.top ?? []).filter((k) => String(k) !== rkStr);
1991
+ const newBottom = (prev.bottom ?? []).filter((k) => String(k) !== rkStr);
1992
+ if (pinned === "top") newTop.push(rk);
1993
+ else if (pinned === "bottom") newBottom.push(rk);
1994
+ return { top: newTop, bottom: newBottom };
1995
+ });
1996
+ }
1997
+ }, [onRowPin, rowPinning]);
2056
1998
  const onSortChangeRef = useRef4(onSortChange);
2057
1999
  onSortChangeRef.current = onSortChange;
2058
2000
  const [sortState, setSortState] = useState2({ key: "", direction: null });
@@ -2152,43 +2094,67 @@ function BoltTable({
2152
2094
  }
2153
2095
  return result;
2154
2096
  }, [data, sortState, columnFilters]);
2097
+ const pinnedRowCacheRef = useRef4(/* @__PURE__ */ new Map());
2155
2098
  const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = useMemo2(() => {
2156
- if (!rowPinning || !rowPinning.top?.length && !rowPinning.bottom?.length) {
2099
+ if (!resolvedRowPinning || !resolvedRowPinning.top?.length && !resolvedRowPinning.bottom?.length) {
2100
+ if (keepPinnedRowsAcrossPages) pinnedRowCacheRef.current.clear();
2157
2101
  return {
2158
2102
  pinnedTopRows: [],
2159
2103
  pinnedBottomRows: [],
2160
2104
  unpinnedProcessedData: processedData
2161
2105
  };
2162
2106
  }
2163
- const topKeySet = new Set((rowPinning.top ?? []).map(String));
2164
- const bottomKeySet = new Set((rowPinning.bottom ?? []).map(String));
2107
+ const topKeySet = new Set((resolvedRowPinning.top ?? []).map(String));
2108
+ const bottomKeySet = new Set((resolvedRowPinning.bottom ?? []).map(String));
2165
2109
  const topMap = /* @__PURE__ */ new Map();
2166
2110
  const bottomMap = /* @__PURE__ */ new Map();
2167
2111
  const rest = [];
2168
2112
  processedData.forEach((row, idx) => {
2169
2113
  if (row == null) return;
2170
2114
  const key = getRowKey(row, idx);
2171
- if (topKeySet.has(key)) topMap.set(key, row);
2172
- else if (bottomKeySet.has(key)) bottomMap.set(key, row);
2173
- else rest.push(row);
2115
+ if (topKeySet.has(key)) {
2116
+ topMap.set(key, row);
2117
+ if (keepPinnedRowsAcrossPages) pinnedRowCacheRef.current.set(key, row);
2118
+ } else if (bottomKeySet.has(key)) {
2119
+ bottomMap.set(key, row);
2120
+ if (keepPinnedRowsAcrossPages) pinnedRowCacheRef.current.set(key, row);
2121
+ } else {
2122
+ rest.push(row);
2123
+ }
2174
2124
  });
2175
- const orderedTop = (rowPinning.top ?? []).map((k) => topMap.get(String(k))).filter((r) => r !== void 0);
2176
- const orderedBottom = (rowPinning.bottom ?? []).map((k) => bottomMap.get(String(k))).filter((r) => r !== void 0);
2125
+ if (keepPinnedRowsAcrossPages) {
2126
+ for (const k of topKeySet) {
2127
+ if (!topMap.has(k) && pinnedRowCacheRef.current.has(k)) {
2128
+ topMap.set(k, pinnedRowCacheRef.current.get(k));
2129
+ }
2130
+ }
2131
+ for (const k of bottomKeySet) {
2132
+ if (!bottomMap.has(k) && pinnedRowCacheRef.current.has(k)) {
2133
+ bottomMap.set(k, pinnedRowCacheRef.current.get(k));
2134
+ }
2135
+ }
2136
+ const allPinnedKeys = /* @__PURE__ */ new Set([...topKeySet, ...bottomKeySet]);
2137
+ for (const cachedKey of pinnedRowCacheRef.current.keys()) {
2138
+ if (!allPinnedKeys.has(cachedKey)) pinnedRowCacheRef.current.delete(cachedKey);
2139
+ }
2140
+ }
2141
+ const orderedTop = (resolvedRowPinning.top ?? []).map((k) => topMap.get(String(k))).filter((r) => r !== void 0);
2142
+ const orderedBottom = (resolvedRowPinning.bottom ?? []).map((k) => bottomMap.get(String(k))).filter((r) => r !== void 0);
2177
2143
  return {
2178
2144
  pinnedTopRows: orderedTop,
2179
2145
  pinnedBottomRows: orderedBottom,
2180
2146
  unpinnedProcessedData: rest
2181
2147
  };
2182
- }, [processedData, rowPinning, getRowKey]);
2148
+ }, [processedData, resolvedRowPinning, getRowKey, keepPinnedRowsAcrossPages]);
2183
2149
  const pinnedTopHeight = pinnedTopRows.length * rowHeight;
2184
2150
  const pinnedBottomHeight = pinnedBottomRows.length * rowHeight;
2185
2151
  const pinnedTopKeySet = useMemo2(
2186
- () => new Set((rowPinning?.top ?? []).map(String)),
2187
- [rowPinning?.top]
2152
+ () => new Set((resolvedRowPinning?.top ?? []).map(String)),
2153
+ [resolvedRowPinning?.top]
2188
2154
  );
2189
2155
  const pinnedBottomKeySet = useMemo2(
2190
- () => new Set((rowPinning?.bottom ?? []).map(String)),
2191
- [rowPinning?.bottom]
2156
+ () => new Set((resolvedRowPinning?.bottom ?? []).map(String)),
2157
+ [resolvedRowPinning?.bottom]
2192
2158
  );
2193
2159
  const [cellContextMenu, setCellContextMenu] = useState2(null);
2194
2160
  const cellMenuRef = useRef4(null);
@@ -2610,8 +2576,8 @@ function BoltTable({
2610
2576
  const col = freshOrderedColumns.find(
2611
2577
  (c) => c.key === ck
2612
2578
  );
2613
- const hasCopy = col?.copy;
2614
- const hasRowPin = !!onRowPin;
2579
+ const hasCopy = !!col?.copy;
2580
+ const hasRowPin = !!rowPinning;
2615
2581
  const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2616
2582
  if (!hasCopy && !hasRowPin && !hasCellItems) return;
2617
2583
  e.preventDefault();
@@ -2639,8 +2605,8 @@ function BoltTable({
2639
2605
  const col = freshOrderedColumns.find(
2640
2606
  (c) => c.key === ck
2641
2607
  );
2642
- const hasCopy = col?.copy;
2643
- const hasRowPin = !!onRowPin;
2608
+ const hasCopy = !!col?.copy;
2609
+ const hasRowPin = !!rowPinning;
2644
2610
  const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2645
2611
  if (!hasCopy && !hasRowPin && !hasCellItems) return;
2646
2612
  setCellContextMenu({
@@ -2774,7 +2740,8 @@ function BoltTable({
2774
2740
  filterValue: columnFilters[column.key] ?? "",
2775
2741
  onFilter: handleColumnFilter,
2776
2742
  onClearFilter: handleClearFilter,
2777
- customContextMenuItems: column.columnHeaderContextMenuItems ? [...columnContextMenuItems ?? [], ...column.columnHeaderContextMenuItems] : columnContextMenuItems
2743
+ customContextMenuItems: column.columnHeaderContextMenuItems ? [...columnContextMenuItems ?? [], ...column.columnHeaderContextMenuItems] : columnContextMenuItems,
2744
+ disabledFilters
2778
2745
  },
2779
2746
  column.key
2780
2747
  );
@@ -3158,8 +3125,8 @@ function BoltTable({
3158
3125
  const isPinnedBottom = pinnedBottomKeySet.has(
3159
3126
  cellContextMenu.rowKey
3160
3127
  );
3161
- const hasCopy = menuCol?.copy;
3162
- const hasRowPin = !!onRowPin;
3128
+ const hasCopy = !!menuCol?.copy;
3129
+ const hasRowPin = !!rowPinning;
3163
3130
  let menuRecord;
3164
3131
  let menuRowIndex = 0;
3165
3132
  const allRows = [
@@ -3188,7 +3155,8 @@ function BoltTable({
3188
3155
  fontSize: 12,
3189
3156
  cursor: "pointer",
3190
3157
  color: "inherit",
3191
- whiteSpace: "nowrap"
3158
+ whiteSpace: "nowrap",
3159
+ ...styles.contextMenuItem
3192
3160
  };
3193
3161
  return createPortal2(
3194
3162
  /* @__PURE__ */ jsxs5(
@@ -3218,7 +3186,7 @@ function BoltTable({
3218
3186
  "data-bt-ctx-item": true,
3219
3187
  style: btnStyle,
3220
3188
  onClick: () => {
3221
- onRowPin(
3189
+ handleRowPin(
3222
3190
  cellContextMenu.rowKey,
3223
3191
  isPinnedTop ? false : "top"
3224
3192
  );
@@ -3246,7 +3214,7 @@ function BoltTable({
3246
3214
  "data-bt-ctx-item": true,
3247
3215
  style: btnStyle,
3248
3216
  onClick: () => {
3249
- onRowPin(
3217
+ handleRowPin(
3250
3218
  cellContextMenu.rowKey,
3251
3219
  isPinnedBottom ? false : "bottom"
3252
3220
  );
@@ -3291,6 +3259,7 @@ function BoltTable({
3291
3259
  onClick: () => {
3292
3260
  const text = typeof menuCol.copy === "function" ? menuCol.copy(menuValue, menuRecord, menuRowIndex) : String(menuValue ?? "");
3293
3261
  navigator.clipboard?.writeText(text);
3262
+ onCopy?.(text, menuCol.key, menuRecord, menuRowIndex);
3294
3263
  setCellContextMenu(null);
3295
3264
  },
3296
3265
  children: [