bolt-table 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -114,6 +114,10 @@ var CopyIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.
114
114
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
115
115
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
116
116
  ] });
117
+ var PencilIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }),
119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m15 5 4 4" })
120
+ ] });
117
121
  var EyeOffIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
118
122
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }),
119
123
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }),
@@ -151,7 +155,10 @@ var DraggableHeader = import_react.default.memo(
151
155
  customContextMenuItems,
152
156
  icons,
153
157
  onColumnDragStart,
154
- disabledFilters
158
+ disabledFilters,
159
+ headerGridRow = 1,
160
+ headerHeight = 36,
161
+ stickyTop = 0
155
162
  }) => {
156
163
  const effectivelySortable = isColumnSortable(column);
157
164
  const effectivelyFilterable = !disabledFilters && isColumnFilterable(column);
@@ -225,23 +232,23 @@ var DraggableHeader = import_react.default.memo(
225
232
  const zIndex = isPinned ? 12 : 10;
226
233
  const headerStyle = {
227
234
  position: "sticky",
228
- top: 0,
235
+ top: stickyTop,
229
236
  zIndex,
230
237
  width: isLastColumn ? "100%" : widthPx,
231
238
  minWidth: widthPx,
232
239
  ...isLastColumn ? {} : { maxWidth: widthPx },
233
240
  gridColumn: visualIndex + 1,
234
- gridRow: 1,
241
+ gridRow: headerGridRow,
235
242
  display: "flex",
236
- height: 36,
243
+ height: headerHeight,
237
244
  alignItems: "center",
238
245
  overflow: "hidden",
239
246
  textOverflow: "ellipsis",
240
247
  whiteSpace: "nowrap",
241
248
  borderTop: "none",
242
- borderRight: "none",
249
+ borderRight: "0.5px solid rgba(128,128,128,0.2)",
243
250
  borderBottom: "1px solid rgba(128,128,128,0.2)",
244
- borderLeft: "none",
251
+ borderLeft: "0.5px solid rgba(128,128,128,0.2)",
245
252
  ...column.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
246
253
  ...column.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
247
254
  ...column.style,
@@ -635,7 +642,7 @@ var DraggableHeader = import_react.default.memo(
635
642
  ] });
636
643
  },
637
644
  (prevProps, nextProps) => {
638
- 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;
645
+ 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 && prevProps.headerGridRow === nextProps.headerGridRow && prevProps.headerHeight === nextProps.headerHeight && prevProps.stickyTop === nextProps.stickyTop;
639
646
  }
640
647
  );
641
648
  DraggableHeader.displayName = "DraggableHeader";
@@ -781,6 +788,61 @@ var ResizeOverlay_default = ResizeOverlay;
781
788
  var import_react3 = __toESM(require("react"));
782
789
  var import_jsx_runtime4 = require("react/jsx-runtime");
783
790
  var SHIMMER_WIDTHS = [55, 70, 45, 80, 60, 50, 75, 65];
791
+ var EditableCell = ({
792
+ value,
793
+ record,
794
+ column,
795
+ rowIndex,
796
+ onEdit,
797
+ onEditComplete
798
+ }) => {
799
+ const [draft, setDraft] = (0, import_react3.useState)(String(value ?? ""));
800
+ const inputRef = (0, import_react3.useRef)(null);
801
+ (0, import_react3.useEffect)(() => {
802
+ setDraft(String(value ?? ""));
803
+ requestAnimationFrame(() => {
804
+ inputRef.current?.focus();
805
+ inputRef.current?.select();
806
+ });
807
+ }, [value]);
808
+ const commit = (0, import_react3.useCallback)(() => {
809
+ const raw = String(value ?? "");
810
+ if (draft !== raw && column.dataIndex) {
811
+ const coerced = typeof value === "number" && !Number.isNaN(Number(draft)) ? Number(draft) : draft;
812
+ onEdit(coerced, record, column.dataIndex, rowIndex);
813
+ }
814
+ onEditComplete();
815
+ }, [draft, value, column.dataIndex, record, rowIndex, onEdit, onEditComplete]);
816
+ const cancel = (0, import_react3.useCallback)(() => {
817
+ onEditComplete();
818
+ }, [onEditComplete]);
819
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
820
+ "input",
821
+ {
822
+ ref: inputRef,
823
+ value: draft,
824
+ onChange: (e) => setDraft(e.target.value),
825
+ onBlur: commit,
826
+ onKeyDown: (e) => {
827
+ if (e.key === "Enter") commit();
828
+ if (e.key === "Escape") cancel();
829
+ },
830
+ style: {
831
+ width: "100%",
832
+ height: "100%",
833
+ border: "none",
834
+ outline: "none",
835
+ background: "transparent",
836
+ font: "inherit",
837
+ color: "inherit",
838
+ padding: 0,
839
+ margin: 0,
840
+ boxSizing: "border-box"
841
+ }
842
+ }
843
+ );
844
+ };
845
+ EditableCell.displayName = "EditableCell";
784
846
  var Cell = import_react3.default.memo(
785
847
  ({
786
848
  value,
@@ -796,7 +858,10 @@ var Cell = import_react3.default.memo(
796
858
  getRowKey,
797
859
  getRawRowKey,
798
860
  accentColor,
799
- isLoading
861
+ isLoading,
862
+ onEdit,
863
+ isEditing,
864
+ onEditComplete
800
865
  }) => {
801
866
  const isPinned = Boolean(column.pinned);
802
867
  if (isLoading && column.key !== "__select__" && column.key !== "__expand__") {
@@ -875,7 +940,7 @@ var Cell = import_react3.default.memo(
875
940
  type: "multiple"
876
941
  });
877
942
  },
878
- style: { cursor: "pointer", accentColor }
943
+ style: { cursor: "pointer", accentColor, colorScheme: "light dark" }
879
944
  }
880
945
  );
881
946
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -900,8 +965,22 @@ var Cell = import_react3.default.memo(
900
965
  }
901
966
  );
902
967
  }
968
+ const isEditable = !!column.editable && !column.render && !!onEdit;
969
+ const showEditor = isEditable && isEditing && onEditComplete;
903
970
  let content;
904
- if (column.render) {
971
+ if (showEditor) {
972
+ content = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
973
+ EditableCell,
974
+ {
975
+ value,
976
+ record,
977
+ column,
978
+ rowIndex,
979
+ onEdit,
980
+ onEditComplete
981
+ }
982
+ );
983
+ } else if (column.render) {
905
984
  try {
906
985
  content = column.render(value, record, rowIndex);
907
986
  } catch {
@@ -930,7 +1009,7 @@ var Cell = import_react3.default.memo(
930
1009
  ...isPinned ? styles?.pinnedCell : void 0,
931
1010
  ...styles?.cell
932
1011
  },
933
- children: isSystem ? content : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1012
+ children: isSystem ? content : showEditor ? content : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
934
1013
  "div",
935
1014
  {
936
1015
  style: {
@@ -951,6 +1030,10 @@ var Cell = import_react3.default.memo(
951
1030
  if (prev.column.pinned !== next.column.pinned) return false;
952
1031
  if (prev.classNames !== next.classNames) return false;
953
1032
  if (prev.styles !== next.styles) return false;
1033
+ if (prev.column.editable !== next.column.editable) return false;
1034
+ if (prev.onEdit !== next.onEdit) return false;
1035
+ if (prev.isEditing !== next.isEditing) return false;
1036
+ if (prev.onEditComplete !== next.onEditComplete) return false;
954
1037
  if (prev.column.key === "__select__") {
955
1038
  return prev.isSelected === next.isSelected && prev.normalizedSelectedKeys === next.normalizedSelectedKeys;
956
1039
  }
@@ -1018,7 +1101,11 @@ var TableBody = ({
1018
1101
  gridTemplateColumns,
1019
1102
  headerHeight = 36,
1020
1103
  rowClassName,
1021
- rowStyle
1104
+ rowStyle,
1105
+ bodyGridRow = 2,
1106
+ onEdit,
1107
+ editingCell,
1108
+ onEditComplete
1022
1109
  }) => {
1023
1110
  const virtualItems = rowVirtualizer.getVirtualItems();
1024
1111
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1040,7 +1127,7 @@ var TableBody = ({
1040
1127
  else if (isPinned) zIndex = 2;
1041
1128
  const style = {
1042
1129
  gridColumn: colIndex + 1,
1043
- gridRow: 2,
1130
+ gridRow: bodyGridRow,
1044
1131
  height: `${totalSize}px`,
1045
1132
  position: isPinned ? "sticky" : "relative",
1046
1133
  zIndex
@@ -1054,7 +1141,7 @@ var TableBody = ({
1054
1141
  }
1055
1142
  return { key: col.key, style, isPinned };
1056
1143
  });
1057
- }, [safeColumns, columnOffsets, totalSize, styles]);
1144
+ }, [safeColumns, columnOffsets, totalSize, styles, bodyGridRow]);
1058
1145
  if (safeData.length === 0 || safeColumns.length === 0) return null;
1059
1146
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1060
1147
  columnStyles.map((colStyle, colIndex) => {
@@ -1072,7 +1159,7 @@ var TableBody = ({
1072
1159
  const rowKey = getRowKey ? getRowKey(row, virtualRow.index) : String(virtualRow.index);
1073
1160
  const isSelected = selectedKeySet.has(rowKey);
1074
1161
  const isExpanded = resolvedExpandedKeys?.has(rowKey) ?? false;
1075
- const cellValue = row[col.dataIndex];
1162
+ const cellValue = col.dataIndex != null ? row[col.dataIndex] : void 0;
1076
1163
  const isRowShimmer = isLoading || rowKey.startsWith("__shimmer_");
1077
1164
  let recordFingerprint;
1078
1165
  if (hasRender && !isRowShimmer) {
@@ -1134,7 +1221,10 @@ var TableBody = ({
1134
1221
  getRawRowKey,
1135
1222
  accentColor,
1136
1223
  isLoading: isRowShimmer,
1137
- recordFingerprint
1224
+ recordFingerprint,
1225
+ onEdit,
1226
+ isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1227
+ onEditComplete
1138
1228
  }
1139
1229
  )
1140
1230
  }
@@ -1152,7 +1242,7 @@ var TableBody = ({
1152
1242
  {
1153
1243
  style: {
1154
1244
  gridColumn: "1 / -1",
1155
- gridRow: 2,
1245
+ gridRow: bodyGridRow,
1156
1246
  height: `${totalSize}px`,
1157
1247
  position: "relative",
1158
1248
  zIndex: 15,
@@ -1216,7 +1306,7 @@ var TableBody = ({
1216
1306
  {
1217
1307
  style: {
1218
1308
  gridColumn: "1 / -1",
1219
- gridRow: 2,
1309
+ gridRow: bodyGridRow,
1220
1310
  height: `${totalSize}px`,
1221
1311
  position: "relative",
1222
1312
  zIndex: 20,
@@ -1258,7 +1348,7 @@ var TableBody = ({
1258
1348
  ...rowSty
1259
1349
  },
1260
1350
  children: safeColumns.map((col) => {
1261
- const cellValue = row[col.dataIndex];
1351
+ const cellValue = col.dataIndex != null ? row[col.dataIndex] : void 0;
1262
1352
  const stickyOffset = columnOffsets.get(col.key);
1263
1353
  const isPinned = Boolean(col.pinned);
1264
1354
  let zIndex = 0;
@@ -1316,7 +1406,10 @@ var TableBody = ({
1316
1406
  getRawRowKey,
1317
1407
  accentColor,
1318
1408
  isLoading: false,
1319
- recordFingerprint
1409
+ recordFingerprint,
1410
+ onEdit,
1411
+ isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1412
+ onEditComplete
1320
1413
  }
1321
1414
  )
1322
1415
  }
@@ -1338,7 +1431,7 @@ var TableBody = ({
1338
1431
  {
1339
1432
  style: {
1340
1433
  gridColumn: "1 / -1",
1341
- gridRow: 2,
1434
+ gridRow: bodyGridRow,
1342
1435
  height: `${totalSize}px`,
1343
1436
  position: "relative",
1344
1437
  zIndex: 20,
@@ -1383,7 +1476,7 @@ var TableBody = ({
1383
1476
  ...rowSty
1384
1477
  },
1385
1478
  children: safeColumns.map((col) => {
1386
- const cellValue = row[col.dataIndex];
1479
+ const cellValue = col.dataIndex != null ? row[col.dataIndex] : void 0;
1387
1480
  const stickyOffset = columnOffsets.get(col.key);
1388
1481
  const isPinned = Boolean(col.pinned);
1389
1482
  let zIndex = 0;
@@ -1441,7 +1534,10 @@ var TableBody = ({
1441
1534
  getRawRowKey,
1442
1535
  accentColor,
1443
1536
  isLoading: false,
1444
- recordFingerprint
1537
+ recordFingerprint,
1538
+ onEdit,
1539
+ isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1540
+ onEditComplete
1445
1541
  }
1446
1542
  )
1447
1543
  }
@@ -1471,6 +1567,18 @@ function arrayMove(arr, from, to) {
1471
1567
  result.splice(to, 0, item);
1472
1568
  return result;
1473
1569
  }
1570
+ function flattenColumns(columns) {
1571
+ const result = [];
1572
+ for (const col of columns) {
1573
+ if (col == null) continue;
1574
+ if (col.children && col.children.length > 0) {
1575
+ result.push(...flattenColumns(col.children));
1576
+ } else {
1577
+ result.push(col);
1578
+ }
1579
+ }
1580
+ return result;
1581
+ }
1474
1582
  var SHIMMER_WIDTHS2 = [55, 70, 45, 80, 60, 50, 75, 65, 40, 72];
1475
1583
  var EMPTY_CLASSNAMES = {};
1476
1584
  var EMPTY_STYLES = {};
@@ -1513,7 +1621,8 @@ function BoltTable({
1513
1621
  rowStyle,
1514
1622
  disabledFilters,
1515
1623
  onCopy,
1516
- keepPinnedRowsAcrossPages
1624
+ keepPinnedRowsAcrossPages,
1625
+ onEdit
1517
1626
  }) {
1518
1627
  const data = (0, import_react4.useMemo)(() => {
1519
1628
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -1522,11 +1631,43 @@ function BoltTable({
1522
1631
  }, [rawData]);
1523
1632
  const initialColumns = (0, import_react4.useMemo)(() => {
1524
1633
  if (!Array.isArray(rawInitialColumns)) return STABLE_EMPTY_COLS;
1525
- const filtered = rawInitialColumns.filter(
1634
+ const safe = rawInitialColumns.filter(
1526
1635
  (col) => col != null && typeof col.key === "string"
1527
1636
  );
1528
- return filtered.length > 0 ? filtered : STABLE_EMPTY_COLS;
1637
+ const flattened = flattenColumns(safe);
1638
+ const validated = flattened.filter(
1639
+ (col) => col != null && typeof col.key === "string"
1640
+ );
1641
+ return validated.length > 0 ? validated : STABLE_EMPTY_COLS;
1642
+ }, [rawInitialColumns]);
1643
+ const headerGroups = (0, import_react4.useMemo)(() => {
1644
+ if (!Array.isArray(rawInitialColumns)) return [];
1645
+ const groups = [];
1646
+ for (const col of rawInitialColumns) {
1647
+ if (col != null && typeof col.key === "string" && col.children && col.children.length > 0) {
1648
+ const leafKeys = flattenColumns([col]).filter((c) => c != null && typeof c.key === "string").map((c) => c.key);
1649
+ if (leafKeys.length > 0) {
1650
+ groups.push({
1651
+ key: col.key,
1652
+ title: col.title,
1653
+ childKeys: leafKeys,
1654
+ style: col.style,
1655
+ className: col.className
1656
+ });
1657
+ }
1658
+ }
1659
+ }
1660
+ return groups;
1529
1661
  }, [rawInitialColumns]);
1662
+ const hasColumnGroups = headerGroups.length > 0;
1663
+ const groupedColumnKeySet = (0, import_react4.useMemo)(() => {
1664
+ if (!hasColumnGroups) return null;
1665
+ const keys = /* @__PURE__ */ new Set();
1666
+ for (const g of headerGroups) {
1667
+ for (const k of g.childKeys) keys.add(k);
1668
+ }
1669
+ return keys;
1670
+ }, [headerGroups, hasColumnGroups]);
1530
1671
  const [columns, setColumns] = (0, import_react4.useState)(initialColumns);
1531
1672
  const [columnOrder, setColumnOrder] = (0, import_react4.useState)(
1532
1673
  () => initialColumns.map((c) => c.key)
@@ -1779,6 +1920,17 @@ function BoltTable({
1779
1920
  };
1780
1921
  }, []);
1781
1922
  const resizeStateRef = (0, import_react4.useRef)(null);
1923
+ const columnGroupMapRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
1924
+ (0, import_react4.useMemo)(() => {
1925
+ const map = /* @__PURE__ */ new Map();
1926
+ for (const g of headerGroups) {
1927
+ for (const k of g.childKeys) map.set(k, g.key);
1928
+ }
1929
+ for (const col of initialColumns) {
1930
+ if (!map.has(col.key)) map.set(col.key, null);
1931
+ }
1932
+ columnGroupMapRef.current = map;
1933
+ }, [headerGroups, initialColumns]);
1782
1934
  const overIdRef = (0, import_react4.useRef)(null);
1783
1935
  const dragActiveIdRef = (0, import_react4.useRef)(null);
1784
1936
  const ghostRef = (0, import_react4.useRef)(null);
@@ -1807,6 +1959,7 @@ function BoltTable({
1807
1959
  const grabStyle = document.createElement("style");
1808
1960
  grabStyle.textContent = "* { cursor: grabbing !important; }";
1809
1961
  document.head.appendChild(grabStyle);
1962
+ const draggedGroup = columnGroupMapRef.current.get(columnKey);
1810
1963
  const onMove = (ev) => {
1811
1964
  if (ghost) {
1812
1965
  ghost.style.left = `${ev.clientX - offsetX}px`;
@@ -1822,6 +1975,11 @@ function BoltTable({
1822
1975
  h.removeAttribute("data-drag-over");
1823
1976
  return;
1824
1977
  }
1978
+ const targetGroup = columnGroupMapRef.current.get(key);
1979
+ if (draggedGroup !== targetGroup) {
1980
+ h.removeAttribute("data-drag-over");
1981
+ return;
1982
+ }
1825
1983
  const r = h.getBoundingClientRect();
1826
1984
  if (ev.clientX >= r.left && ev.clientX <= r.right && ev.clientY >= r.top - 20 && ev.clientY <= r.bottom + 20) {
1827
1985
  newOverId = key;
@@ -1844,7 +2002,7 @@ function BoltTable({
1844
2002
  if (ghost) ghost.style.display = "none";
1845
2003
  const currentOverId = overIdRef.current;
1846
2004
  const currentActiveId = dragActiveIdRef.current;
1847
- if (currentOverId && currentActiveId && currentOverId !== currentActiveId) {
2005
+ if (currentOverId && currentActiveId && currentOverId !== currentActiveId && columnGroupMapRef.current.get(currentActiveId) === columnGroupMapRef.current.get(currentOverId)) {
1848
2006
  import_react4.default.startTransition(() => {
1849
2007
  setColumnOrder((items) => {
1850
2008
  const oldIndex = items.indexOf(currentActiveId);
@@ -2084,7 +2242,7 @@ function BoltTable({
2084
2242
  try {
2085
2243
  const col = columnsLookupRef.current.find((c) => c.key === key);
2086
2244
  if (typeof col?.filterFn === "function") {
2087
- return col.filterFn(columnFilters[key], row, col.dataIndex);
2245
+ return col.filterFn(columnFilters[key], row, col.dataIndex ?? key);
2088
2246
  }
2089
2247
  const cellVal = String(row[key] ?? "").toLowerCase();
2090
2248
  return cellVal.includes(columnFilters[key].toLowerCase());
@@ -2190,6 +2348,10 @@ function BoltTable({
2190
2348
  () => new Set((resolvedRowPinning?.bottom ?? []).map(String)),
2191
2349
  [resolvedRowPinning?.bottom]
2192
2350
  );
2351
+ const [editingCell, setEditingCell] = (0, import_react4.useState)(null);
2352
+ const handleEditComplete = (0, import_react4.useCallback)(() => {
2353
+ setEditingCell(null);
2354
+ }, []);
2193
2355
  const [cellContextMenu, setCellContextMenu] = (0, import_react4.useState)(null);
2194
2356
  const cellMenuRef = (0, import_react4.useRef)(null);
2195
2357
  const cellLongPressTimer = (0, import_react4.useRef)(
@@ -2400,7 +2562,7 @@ function BoltTable({
2400
2562
  totalPages
2401
2563
  ];
2402
2564
  };
2403
- const HEADER_HEIGHT = 36;
2565
+ const HEADER_HEIGHT = hasColumnGroups ? 72 : 36;
2404
2566
  const MAX_AUTO_ROWS = 10;
2405
2567
  const virtualTotalSize = rowVirtualizer.getTotalSize();
2406
2568
  const naturalContentHeight = virtualTotalSize + HEADER_HEIGHT;
@@ -2595,7 +2757,7 @@ function BoltTable({
2595
2757
  style: {
2596
2758
  display: "grid",
2597
2759
  gridTemplateColumns,
2598
- gridTemplateRows: isEmpty ? "36px 1fr" : `36px ${virtualTotalSize}px`,
2760
+ gridTemplateRows: isEmpty ? hasColumnGroups ? "36px 36px 1fr" : "36px 1fr" : hasColumnGroups ? `36px 36px ${virtualTotalSize}px` : `36px ${virtualTotalSize}px`,
2599
2761
  minWidth: `${totalTableWidth}px`,
2600
2762
  width: "100%",
2601
2763
  position: "relative",
@@ -2613,7 +2775,8 @@ function BoltTable({
2613
2775
  const hasCopy = !!col?.copy;
2614
2776
  const hasRowPin = !!rowPinning;
2615
2777
  const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2616
- if (!hasCopy && !hasRowPin && !hasCellItems) return;
2778
+ const hasEdit = !!col?.editable && !col?.render && !!onEdit;
2779
+ if (!hasCopy && !hasRowPin && !hasCellItems && !hasEdit) return;
2617
2780
  e.preventDefault();
2618
2781
  setCellContextMenu({
2619
2782
  x: Math.min(e.clientX, window.innerWidth - 200),
@@ -2642,7 +2805,8 @@ function BoltTable({
2642
2805
  const hasCopy = !!col?.copy;
2643
2806
  const hasRowPin = !!rowPinning;
2644
2807
  const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2645
- if (!hasCopy && !hasRowPin && !hasCellItems) return;
2808
+ const hasEdit = !!col?.editable && !col?.render && !!onEdit;
2809
+ if (!hasCopy && !hasRowPin && !hasCellItems && !hasEdit) return;
2646
2810
  setCellContextMenu({
2647
2811
  x: Math.min(touch.clientX, window.innerWidth - 200),
2648
2812
  y: Math.min(touch.clientY, window.innerHeight - 200),
@@ -2662,7 +2826,50 @@ function BoltTable({
2662
2826
  onTouchEnd: cancelCellLongPress,
2663
2827
  onTouchCancel: cancelCellLongPress,
2664
2828
  children: [
2829
+ hasColumnGroups && headerGroups.map((group) => {
2830
+ let minIdx = Infinity;
2831
+ let maxIdx = -1;
2832
+ orderedColumns.forEach((col, idx) => {
2833
+ if (group.childKeys.includes(col.key)) {
2834
+ minIdx = Math.min(minIdx, idx);
2835
+ maxIdx = Math.max(maxIdx, idx);
2836
+ }
2837
+ });
2838
+ if (minIdx === Infinity) return null;
2839
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2840
+ "div",
2841
+ {
2842
+ "data-bt-header": "",
2843
+ className: `${group.className ?? ""} ${classNames.header ?? ""}`,
2844
+ style: {
2845
+ gridColumn: `${minIdx + 1} / ${maxIdx + 2}`,
2846
+ gridRow: 1,
2847
+ position: "sticky",
2848
+ top: 0,
2849
+ zIndex: 10,
2850
+ display: "flex",
2851
+ height: 36,
2852
+ alignItems: "center",
2853
+ justifyContent: "center",
2854
+ overflow: "hidden",
2855
+ textOverflow: "ellipsis",
2856
+ whiteSpace: "nowrap",
2857
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
2858
+ fontWeight: 500,
2859
+ userSelect: "none",
2860
+ ...group.style,
2861
+ ...styles.header
2862
+ },
2863
+ children: group.title
2864
+ },
2865
+ `group-${group.key}`
2866
+ );
2867
+ }),
2665
2868
  orderedColumns.map((column, visualIndex) => {
2869
+ const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
2870
+ const leafGridRow = hasColumnGroups ? isInGroup ? 2 : "1 / 3" : 1;
2871
+ const leafHeight = hasColumnGroups && !isInGroup ? 72 : 36;
2872
+ const leafStickyTop = hasColumnGroups && isInGroup ? 36 : 0;
2666
2873
  if (column.key === "__select__" && rowSelection) {
2667
2874
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2668
2875
  "div",
@@ -2672,7 +2879,7 @@ function BoltTable({
2672
2879
  className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2673
2880
  style: {
2674
2881
  display: "flex",
2675
- height: 36,
2882
+ height: leafHeight,
2676
2883
  alignItems: "center",
2677
2884
  justifyContent: "center",
2678
2885
  overflow: "hidden",
@@ -2684,6 +2891,7 @@ function BoltTable({
2684
2891
  top: 0,
2685
2892
  zIndex: 13,
2686
2893
  width: "48px",
2894
+ gridRow: leafGridRow,
2687
2895
  ...styles.header,
2688
2896
  ...styles.pinnedHeader
2689
2897
  },
@@ -2717,7 +2925,7 @@ function BoltTable({
2717
2925
  });
2718
2926
  }
2719
2927
  },
2720
- style: { cursor: "pointer", accentColor }
2928
+ style: { cursor: "pointer", accentColor, colorScheme: "light dark" }
2721
2929
  }
2722
2930
  )
2723
2931
  },
@@ -2733,7 +2941,7 @@ function BoltTable({
2733
2941
  className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
2734
2942
  style: {
2735
2943
  display: "flex",
2736
- height: 36,
2944
+ height: leafHeight,
2737
2945
  alignItems: "center",
2738
2946
  justifyContent: "center",
2739
2947
  overflow: "hidden",
@@ -2745,6 +2953,7 @@ function BoltTable({
2745
2953
  top: 0,
2746
2954
  zIndex: 13,
2747
2955
  width: "40px",
2956
+ gridRow: leafGridRow,
2748
2957
  ...styles.header,
2749
2958
  ...styles.pinnedHeader
2750
2959
  }
@@ -2775,7 +2984,10 @@ function BoltTable({
2775
2984
  onFilter: handleColumnFilter,
2776
2985
  onClearFilter: handleClearFilter,
2777
2986
  customContextMenuItems: column.columnHeaderContextMenuItems ? [...columnContextMenuItems ?? [], ...column.columnHeaderContextMenuItems] : columnContextMenuItems,
2778
- disabledFilters
2987
+ disabledFilters,
2988
+ headerGridRow: leafGridRow,
2989
+ headerHeight: leafHeight,
2990
+ stickyTop: leafStickyTop
2779
2991
  },
2780
2992
  column.key
2781
2993
  );
@@ -2846,7 +3058,11 @@ function BoltTable({
2846
3058
  gridTemplateColumns,
2847
3059
  headerHeight: HEADER_HEIGHT,
2848
3060
  rowClassName,
2849
- rowStyle
3061
+ rowStyle,
3062
+ bodyGridRow: hasColumnGroups ? 3 : 2,
3063
+ onEdit,
3064
+ editingCell,
3065
+ onEditComplete: handleEditComplete
2850
3066
  }
2851
3067
  )
2852
3068
  ]
@@ -2860,6 +3076,7 @@ function BoltTable({
2860
3076
  pgEnabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2861
3077
  "div",
2862
3078
  {
3079
+ className: classNames.pagination ?? "",
2863
3080
  style: {
2864
3081
  display: "flex",
2865
3082
  height: 36,
@@ -2871,27 +3088,42 @@ function BoltTable({
2871
3088
  fontSize: 12,
2872
3089
  backdropFilter: "blur(8px)",
2873
3090
  backgroundColor: "rgba(128,128,128,0.06)",
2874
- gap: 12
3091
+ gap: 12,
3092
+ ...styles.pagination
2875
3093
  },
2876
3094
  children: [
2877
3095
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flex: "1 1 0%", alignItems: "center" }, children: (() => {
2878
3096
  const rangeStart = total > 0 ? (currentPage - 1) * pageSize + 1 : 0;
2879
3097
  const rangeEnd = Math.min(currentPage * pageSize, total);
2880
- return typeof pagination === "object" && pagination?.showTotal ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { color: "GrayText", fontSize: 12 }, children: [
2881
- "Showing",
2882
- " ",
2883
- pagination.showTotal(total, [rangeStart, rangeEnd]),
2884
- " of",
2885
- " ",
2886
- total,
2887
- " items"
2888
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { color: "GrayText", fontSize: 12 }, children: [
2889
- rangeStart,
2890
- "\u2013",
2891
- rangeEnd,
2892
- " of ",
2893
- total
2894
- ] });
3098
+ return typeof pagination === "object" && pagination?.showTotal ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3099
+ "span",
3100
+ {
3101
+ className: classNames.paginationInfo ?? "",
3102
+ style: { color: "GrayText", fontSize: 12, ...styles.paginationInfo },
3103
+ children: [
3104
+ "Showing",
3105
+ " ",
3106
+ pagination.showTotal(total, [rangeStart, rangeEnd]),
3107
+ " of",
3108
+ " ",
3109
+ total,
3110
+ " items"
3111
+ ]
3112
+ }
3113
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3114
+ "span",
3115
+ {
3116
+ className: classNames.paginationInfo ?? "",
3117
+ style: { color: "GrayText", fontSize: 12, ...styles.paginationInfo },
3118
+ children: [
3119
+ rangeStart,
3120
+ "\u2013",
3121
+ rangeEnd,
3122
+ " of ",
3123
+ total
3124
+ ]
3125
+ }
3126
+ );
2895
3127
  })() }),
2896
3128
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2897
3129
  "div",
@@ -2909,6 +3141,7 @@ function BoltTable({
2909
3141
  {
2910
3142
  onClick: () => handlePageChange(1),
2911
3143
  disabled: currentPage === 1,
3144
+ className: classNames.paginationButton ?? "",
2912
3145
  style: {
2913
3146
  display: "inline-flex",
2914
3147
  height: 24,
@@ -2921,7 +3154,8 @@ function BoltTable({
2921
3154
  background: "none",
2922
3155
  border: "none",
2923
3156
  padding: 0,
2924
- color: "inherit"
3157
+ color: "inherit",
3158
+ ...styles.paginationButton
2925
3159
  },
2926
3160
  title: "First page",
2927
3161
  children: icons?.chevronsLeft ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronsLeftIcon, { style: { width: 12, height: 12 } })
@@ -2932,6 +3166,7 @@ function BoltTable({
2932
3166
  {
2933
3167
  onClick: () => handlePageChange(currentPage - 1),
2934
3168
  disabled: currentPage === 1,
3169
+ className: classNames.paginationButton ?? "",
2935
3170
  style: {
2936
3171
  display: "inline-flex",
2937
3172
  height: 24,
@@ -2944,7 +3179,8 @@ function BoltTable({
2944
3179
  background: "none",
2945
3180
  border: "none",
2946
3181
  padding: 0,
2947
- color: "inherit"
3182
+ color: "inherit",
3183
+ ...styles.paginationButton
2948
3184
  },
2949
3185
  title: "Previous page",
2950
3186
  children: icons?.chevronLeft ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronLeftIcon, { style: { width: 12, height: 12 } })
@@ -2967,9 +3203,11 @@ function BoltTable({
2967
3203
  page
2968
3204
  );
2969
3205
  }
3206
+ const isActivePage = page === currentPage;
2970
3207
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2971
3208
  "button",
2972
3209
  {
3210
+ className: `${classNames.paginationButton ?? ""} ${isActivePage ? classNames.paginationActiveButton ?? "" : ""}`.trim() || void 0,
2973
3211
  style: {
2974
3212
  display: "inline-flex",
2975
3213
  height: 24,
@@ -2981,9 +3219,11 @@ function BoltTable({
2981
3219
  paddingLeft: 6,
2982
3220
  paddingRight: 6,
2983
3221
  fontSize: 12,
2984
- color: page === currentPage ? accentColor : void 0,
3222
+ color: isActivePage ? accentColor : void 0,
2985
3223
  background: "none",
2986
- border: "none"
3224
+ border: "none",
3225
+ ...styles.paginationButton,
3226
+ ...isActivePage ? styles.paginationActiveButton : void 0
2987
3227
  },
2988
3228
  onClick: () => handlePageChange(page),
2989
3229
  children: page
@@ -2996,6 +3236,7 @@ function BoltTable({
2996
3236
  {
2997
3237
  onClick: () => handlePageChange(currentPage + 1),
2998
3238
  disabled: currentPage === totalPages,
3239
+ className: classNames.paginationButton ?? "",
2999
3240
  style: {
3000
3241
  display: "inline-flex",
3001
3242
  height: 24,
@@ -3008,7 +3249,8 @@ function BoltTable({
3008
3249
  background: "none",
3009
3250
  border: "none",
3010
3251
  padding: 0,
3011
- color: "inherit"
3252
+ color: "inherit",
3253
+ ...styles.paginationButton
3012
3254
  },
3013
3255
  title: "Next page",
3014
3256
  children: icons?.chevronRight ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronRightIcon, { style: { width: 12, height: 12 } })
@@ -3019,6 +3261,7 @@ function BoltTable({
3019
3261
  {
3020
3262
  onClick: () => handlePageChange(totalPages),
3021
3263
  disabled: currentPage === totalPages,
3264
+ className: classNames.paginationButton ?? "",
3022
3265
  style: {
3023
3266
  display: "inline-flex",
3024
3267
  height: 24,
@@ -3031,7 +3274,8 @@ function BoltTable({
3031
3274
  background: "none",
3032
3275
  border: "none",
3033
3276
  padding: 0,
3034
- color: "inherit"
3277
+ color: "inherit",
3278
+ ...styles.paginationButton
3035
3279
  },
3036
3280
  title: "Last page",
3037
3281
  children: icons?.chevronsRight ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronsRightIcon, { style: { width: 12, height: 12 } })
@@ -3055,6 +3299,7 @@ function BoltTable({
3055
3299
  {
3056
3300
  value: pageSize,
3057
3301
  onChange: (e) => handlePageSizeChange(Number(e.target.value)),
3302
+ className: classNames.paginationSelect ?? "",
3058
3303
  style: {
3059
3304
  cursor: "pointer",
3060
3305
  borderRadius: 4,
@@ -3066,7 +3311,8 @@ function BoltTable({
3066
3311
  fontSize: 12,
3067
3312
  height: 24,
3068
3313
  background: "inherit",
3069
- color: "inherit"
3314
+ color: "inherit",
3315
+ ...styles.paginationSelect
3070
3316
  },
3071
3317
  children: (typeof pagination === "object" && pagination?.pageSizeOptions ? pagination.pageSizeOptions : [10, 15, 20, 25, 50, 100]).map((size) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("option", { value: size, children: [
3072
3318
  size,
@@ -3161,6 +3407,7 @@ function BoltTable({
3161
3407
  );
3162
3408
  const hasCopy = !!menuCol?.copy;
3163
3409
  const hasRowPin = !!rowPinning;
3410
+ const hasEdit = !!menuCol?.editable && !menuCol?.render && !!onEdit;
3164
3411
  let menuRecord;
3165
3412
  let menuRowIndex = 0;
3166
3413
  const allRows = [
@@ -3177,7 +3424,7 @@ function BoltTable({
3177
3424
  break;
3178
3425
  }
3179
3426
  }
3180
- const menuValue = menuRecord && menuCol ? menuRecord[menuCol.dataIndex] : void 0;
3427
+ const menuValue = menuRecord && menuCol?.dataIndex != null ? menuRecord[menuCol.dataIndex] : void 0;
3181
3428
  const btnStyle = {
3182
3429
  display: "flex",
3183
3430
  width: "100%",
@@ -3276,7 +3523,39 @@ function BoltTable({
3276
3523
  }
3277
3524
  )
3278
3525
  ] }),
3279
- hasRowPin && hasCopy && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3526
+ hasRowPin && (hasCopy || hasEdit) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3527
+ "div",
3528
+ {
3529
+ style: {
3530
+ borderTop: "1px solid rgba(128,128,128,0.2)",
3531
+ margin: "4px 0"
3532
+ }
3533
+ }
3534
+ ),
3535
+ hasEdit && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3536
+ "button",
3537
+ {
3538
+ "data-bt-ctx-item": true,
3539
+ style: btnStyle,
3540
+ onClick: () => {
3541
+ setEditingCell({
3542
+ rowKey: cellContextMenu.rowKey,
3543
+ columnKey: cellContextMenu.columnKey
3544
+ });
3545
+ setCellContextMenu(null);
3546
+ },
3547
+ children: [
3548
+ icons?.edit ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3549
+ PencilIcon,
3550
+ {
3551
+ style: { width: 14, height: 14, flexShrink: 0 }
3552
+ }
3553
+ ),
3554
+ "Edit"
3555
+ ]
3556
+ }
3557
+ ),
3558
+ (hasEdit || hasRowPin) && hasCopy && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3280
3559
  "div",
3281
3560
  {
3282
3561
  style: {
@@ -3308,7 +3587,7 @@ function BoltTable({
3308
3587
  }
3309
3588
  ),
3310
3589
  menuCol?.columnCellContextMenuItems && menuCol.columnCellContextMenuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3311
- (hasCopy || hasRowPin) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3590
+ (hasCopy || hasRowPin || hasEdit) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3312
3591
  "div",
3313
3592
  {
3314
3593
  style: {