bolt-table 0.1.11 → 0.1.13

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.d.mts CHANGED
@@ -569,6 +569,38 @@ interface PaginationType {
569
569
  */
570
570
  showTotal?: (total: number, range: [number, number]) => ReactNode;
571
571
  }
572
+ /**
573
+ * Configuration for row pinning.
574
+ * When provided, the specified rows are rendered as sticky rows at the top
575
+ * and/or bottom of the table body, remaining visible during vertical scroll.
576
+ *
577
+ * Pinned rows are excluded from pagination — they are always visible regardless
578
+ * of which page the user is on. Filtering still applies: if a pinned row's key
579
+ * doesn't exist in the (filtered) data, it simply won't appear.
580
+ *
581
+ * @example
582
+ * // Pin two rows to the top and one to the bottom
583
+ * rowPinning={{ top: ['row-1', 'row-3'], bottom: ['row-10'] }}
584
+ *
585
+ * @example
586
+ * // Controlled pinning — manage pinned keys in parent state
587
+ * const [pinning, setPinning] = useState<RowPinningConfig>({ top: ['header-row'] });
588
+ * <BoltTable rowPinning={pinning} ... />
589
+ */
590
+ interface RowPinningConfig {
591
+ /**
592
+ * Row keys to pin at the top of the table.
593
+ * These rows stick below the column headers during vertical scroll.
594
+ * Order is preserved — rows are rendered in the order listed here.
595
+ */
596
+ top?: React.Key[];
597
+ /**
598
+ * Row keys to pin at the bottom of the table.
599
+ * These rows stick to the bottom of the visible area during vertical scroll.
600
+ * Order is preserved — rows are rendered in the order listed here.
601
+ */
602
+ bottom?: React.Key[];
603
+ }
572
604
  /**
573
605
  * The base type for a row record in BoltTable.
574
606
  * All row data objects must be indexable by string keys.
@@ -910,6 +942,17 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
910
942
  * }}
911
943
  */
912
944
  readonly rowSelection?: RowSelectionConfig<T>;
945
+ /**
946
+ * Row pinning configuration. When provided, the specified rows are rendered
947
+ * as sticky rows at the top and/or bottom of the table body.
948
+ *
949
+ * Pinned rows transcend pagination — they are always visible regardless of
950
+ * which page the user is on. Filtering still applies.
951
+ *
952
+ * @example
953
+ * rowPinning={{ top: ['row-1', 'row-3'], bottom: ['row-10'] }}
954
+ */
955
+ readonly rowPinning?: RowPinningConfig;
913
956
  /**
914
957
  * Called when the user scrolls near the bottom of the table.
915
958
  * Use this for infinite scroll / load-more behavior.
@@ -1106,6 +1149,11 @@ interface ClassNamesTypes {
1106
1149
  * Does not affect the row itself, only the expanded panel.
1107
1150
  */
1108
1151
  expandedRow?: string;
1152
+ /**
1153
+ * Applied to each pinned row's wrapper div (the grid row containing all cells).
1154
+ * Use this to add a border, background, or separator for pinned rows.
1155
+ */
1156
+ pinnedRow?: string;
1109
1157
  }
1110
1158
  /**
1111
1159
  * Inline style overrides for specific regions of BoltTable.
@@ -1146,6 +1194,17 @@ interface StylesTypes {
1146
1194
  pinnedCell?: CSSProperties;
1147
1195
  /** Inline styles for the expanded row content panel */
1148
1196
  expandedRow?: CSSProperties;
1197
+ /** Inline styles for pinned row wrappers (the grid row containing all cells) */
1198
+ pinnedRow?: CSSProperties;
1199
+ /**
1200
+ * CSS color string for the background of pinned row cells.
1201
+ * Should be opaque so that scrolling content behind pinned rows is hidden.
1202
+ * Falls back to `pinnedBg` if not set.
1203
+ *
1204
+ * @example
1205
+ * pinnedRowBg: 'rgba(255, 255, 255, 0.98)'
1206
+ */
1207
+ pinnedRowBg?: string;
1149
1208
  /**
1150
1209
  * CSS color string applied as the background of hovered rows.
1151
1210
  * Defaults to `hsl(var(--muted) / 0.5)` if omitted.
@@ -1210,7 +1269,7 @@ interface StylesTypes {
1210
1269
  * autoHeight={false}
1211
1270
  * />
1212
1271
  */
1213
- declare function BoltTable<T extends DataRecord = DataRecord>({ columns: initialColumns, data, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
1272
+ declare function BoltTable<T extends DataRecord = DataRecord>({ columns: initialColumns, data, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
1214
1273
 
1215
1274
  /**
1216
1275
  * Props for the DraggableHeader component.
@@ -1569,6 +1628,28 @@ interface TableBodyProps {
1569
1628
  * maxExpandedRowHeight={300}
1570
1629
  */
1571
1630
  maxExpandedRowHeight?: number;
1631
+ /**
1632
+ * Rows pinned to the top of the table body.
1633
+ * Rendered as non-virtualized sticky rows below the column headers.
1634
+ */
1635
+ pinnedTopData?: DataRecord[];
1636
+ /**
1637
+ * Rows pinned to the bottom of the table body.
1638
+ * Rendered as non-virtualized sticky rows at the bottom of the visible area.
1639
+ */
1640
+ pinnedBottomData?: DataRecord[];
1641
+ /**
1642
+ * The CSS `gridTemplateColumns` string matching the parent grid.
1643
+ * Needed for pinned row sub-grids to align with the main column layout.
1644
+ */
1645
+ gridTemplateColumns?: string;
1646
+ /**
1647
+ * Height of the column header row in pixels.
1648
+ * Used to position the sticky top pinned rows below the headers.
1649
+ *
1650
+ * @default 36
1651
+ */
1652
+ headerHeight?: number;
1572
1653
  }
1573
1654
  /**
1574
1655
  * TableBody — the virtualized body renderer for BoltTable.
@@ -1590,4 +1671,4 @@ interface TableBodyProps {
1590
1671
  */
1591
1672
  declare const TableBody: React$1.FC<TableBodyProps>;
1592
1673
 
1593
- export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowSelectionConfig, type SortDirection, TableBody };
1674
+ export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowPinningConfig, type RowSelectionConfig, type SortDirection, TableBody };
package/dist/index.d.ts CHANGED
@@ -569,6 +569,38 @@ interface PaginationType {
569
569
  */
570
570
  showTotal?: (total: number, range: [number, number]) => ReactNode;
571
571
  }
572
+ /**
573
+ * Configuration for row pinning.
574
+ * When provided, the specified rows are rendered as sticky rows at the top
575
+ * and/or bottom of the table body, remaining visible during vertical scroll.
576
+ *
577
+ * Pinned rows are excluded from pagination — they are always visible regardless
578
+ * of which page the user is on. Filtering still applies: if a pinned row's key
579
+ * doesn't exist in the (filtered) data, it simply won't appear.
580
+ *
581
+ * @example
582
+ * // Pin two rows to the top and one to the bottom
583
+ * rowPinning={{ top: ['row-1', 'row-3'], bottom: ['row-10'] }}
584
+ *
585
+ * @example
586
+ * // Controlled pinning — manage pinned keys in parent state
587
+ * const [pinning, setPinning] = useState<RowPinningConfig>({ top: ['header-row'] });
588
+ * <BoltTable rowPinning={pinning} ... />
589
+ */
590
+ interface RowPinningConfig {
591
+ /**
592
+ * Row keys to pin at the top of the table.
593
+ * These rows stick below the column headers during vertical scroll.
594
+ * Order is preserved — rows are rendered in the order listed here.
595
+ */
596
+ top?: React.Key[];
597
+ /**
598
+ * Row keys to pin at the bottom of the table.
599
+ * These rows stick to the bottom of the visible area during vertical scroll.
600
+ * Order is preserved — rows are rendered in the order listed here.
601
+ */
602
+ bottom?: React.Key[];
603
+ }
572
604
  /**
573
605
  * The base type for a row record in BoltTable.
574
606
  * All row data objects must be indexable by string keys.
@@ -910,6 +942,17 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
910
942
  * }}
911
943
  */
912
944
  readonly rowSelection?: RowSelectionConfig<T>;
945
+ /**
946
+ * Row pinning configuration. When provided, the specified rows are rendered
947
+ * as sticky rows at the top and/or bottom of the table body.
948
+ *
949
+ * Pinned rows transcend pagination — they are always visible regardless of
950
+ * which page the user is on. Filtering still applies.
951
+ *
952
+ * @example
953
+ * rowPinning={{ top: ['row-1', 'row-3'], bottom: ['row-10'] }}
954
+ */
955
+ readonly rowPinning?: RowPinningConfig;
913
956
  /**
914
957
  * Called when the user scrolls near the bottom of the table.
915
958
  * Use this for infinite scroll / load-more behavior.
@@ -1106,6 +1149,11 @@ interface ClassNamesTypes {
1106
1149
  * Does not affect the row itself, only the expanded panel.
1107
1150
  */
1108
1151
  expandedRow?: string;
1152
+ /**
1153
+ * Applied to each pinned row's wrapper div (the grid row containing all cells).
1154
+ * Use this to add a border, background, or separator for pinned rows.
1155
+ */
1156
+ pinnedRow?: string;
1109
1157
  }
1110
1158
  /**
1111
1159
  * Inline style overrides for specific regions of BoltTable.
@@ -1146,6 +1194,17 @@ interface StylesTypes {
1146
1194
  pinnedCell?: CSSProperties;
1147
1195
  /** Inline styles for the expanded row content panel */
1148
1196
  expandedRow?: CSSProperties;
1197
+ /** Inline styles for pinned row wrappers (the grid row containing all cells) */
1198
+ pinnedRow?: CSSProperties;
1199
+ /**
1200
+ * CSS color string for the background of pinned row cells.
1201
+ * Should be opaque so that scrolling content behind pinned rows is hidden.
1202
+ * Falls back to `pinnedBg` if not set.
1203
+ *
1204
+ * @example
1205
+ * pinnedRowBg: 'rgba(255, 255, 255, 0.98)'
1206
+ */
1207
+ pinnedRowBg?: string;
1149
1208
  /**
1150
1209
  * CSS color string applied as the background of hovered rows.
1151
1210
  * Defaults to `hsl(var(--muted) / 0.5)` if omitted.
@@ -1210,7 +1269,7 @@ interface StylesTypes {
1210
1269
  * autoHeight={false}
1211
1270
  * />
1212
1271
  */
1213
- declare function BoltTable<T extends DataRecord = DataRecord>({ columns: initialColumns, data, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
1272
+ declare function BoltTable<T extends DataRecord = DataRecord>({ columns: initialColumns, data, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
1214
1273
 
1215
1274
  /**
1216
1275
  * Props for the DraggableHeader component.
@@ -1569,6 +1628,28 @@ interface TableBodyProps {
1569
1628
  * maxExpandedRowHeight={300}
1570
1629
  */
1571
1630
  maxExpandedRowHeight?: number;
1631
+ /**
1632
+ * Rows pinned to the top of the table body.
1633
+ * Rendered as non-virtualized sticky rows below the column headers.
1634
+ */
1635
+ pinnedTopData?: DataRecord[];
1636
+ /**
1637
+ * Rows pinned to the bottom of the table body.
1638
+ * Rendered as non-virtualized sticky rows at the bottom of the visible area.
1639
+ */
1640
+ pinnedBottomData?: DataRecord[];
1641
+ /**
1642
+ * The CSS `gridTemplateColumns` string matching the parent grid.
1643
+ * Needed for pinned row sub-grids to align with the main column layout.
1644
+ */
1645
+ gridTemplateColumns?: string;
1646
+ /**
1647
+ * Height of the column header row in pixels.
1648
+ * Used to position the sticky top pinned rows below the headers.
1649
+ *
1650
+ * @default 36
1651
+ */
1652
+ headerHeight?: number;
1572
1653
  }
1573
1654
  /**
1574
1655
  * TableBody — the virtualized body renderer for BoltTable.
@@ -1590,4 +1671,4 @@ interface TableBodyProps {
1590
1671
  */
1591
1672
  declare const TableBody: React$1.FC<TableBodyProps>;
1592
1673
 
1593
- export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowSelectionConfig, type SortDirection, TableBody };
1674
+ export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowPinningConfig, type RowSelectionConfig, type SortDirection, TableBody };
package/dist/index.js CHANGED
@@ -986,7 +986,9 @@ var Cell = import_react3.default.memo(
986
986
  return prev.isExpanded === next.isExpanded;
987
987
  }
988
988
  if (prev.column.render) {
989
- return prev.record === next.record && prev.rowIndex === next.rowIndex;
989
+ if (prev.recordFingerprint !== next.recordFingerprint) return false;
990
+ if (prev.rowIndex !== next.rowIndex) return false;
991
+ return prev.column.render === next.column.render;
990
992
  }
991
993
  return prev.value === next.value && prev.rowIndex === next.rowIndex && prev.column.key === next.column.key;
992
994
  }
@@ -1032,15 +1034,26 @@ var TableBody = ({
1032
1034
  expandable,
1033
1035
  resolvedExpandedKeys,
1034
1036
  rowHeight = 40,
1037
+ totalTableWidth,
1035
1038
  scrollAreaWidth,
1036
1039
  accentColor,
1037
1040
  isLoading = false,
1038
1041
  onExpandedRowResize,
1039
- maxExpandedRowHeight
1042
+ maxExpandedRowHeight,
1043
+ pinnedTopData = [],
1044
+ pinnedBottomData = [],
1045
+ gridTemplateColumns,
1046
+ headerHeight = 36
1040
1047
  }) => {
1041
1048
  const virtualItems = rowVirtualizer.getVirtualItems();
1042
1049
  const totalSize = rowVirtualizer.getTotalSize();
1043
1050
  const selectedKeySet = (0, import_react3.useMemo)(() => new Set(normalizedSelectedKeys), [normalizedSelectedKeys]);
1051
+ const allDataForSelection = (0, import_react3.useMemo)(() => {
1052
+ if (pinnedTopData.length === 0 && pinnedBottomData.length === 0)
1053
+ return data;
1054
+ return [...pinnedTopData, ...data, ...pinnedBottomData];
1055
+ }, [pinnedTopData, data, pinnedBottomData]);
1056
+ const pinnedRowBg = styles?.pinnedRowBg ?? styles?.pinnedBg;
1044
1057
  const columnStyles = (0, import_react3.useMemo)(() => {
1045
1058
  return orderedColumns.map((col, colIndex) => {
1046
1059
  const stickyOffset = columnOffsets.get(col.key);
@@ -1069,6 +1082,7 @@ var TableBody = ({
1069
1082
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1070
1083
  columnStyles.map((colStyle, colIndex) => {
1071
1084
  const col = orderedColumns[colIndex];
1085
+ const hasRender = !!col.render;
1072
1086
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1073
1087
  "div",
1074
1088
  {
@@ -1080,6 +1094,7 @@ var TableBody = ({
1080
1094
  const isExpanded = resolvedExpandedKeys?.has(rowKey) ?? false;
1081
1095
  const cellValue = row[col.dataIndex];
1082
1096
  const isRowShimmer = isLoading || rowKey.startsWith("__shimmer_");
1097
+ const recordFingerprint = hasRender && !isRowShimmer ? JSON.stringify(row) : void 0;
1083
1098
  return (
1084
1099
  /*
1085
1100
  * Row wrapper div:
@@ -1124,10 +1139,11 @@ var TableBody = ({
1124
1139
  rowSelection,
1125
1140
  normalizedSelectedKeys,
1126
1141
  rowKey,
1127
- allData: data,
1142
+ allData: allDataForSelection,
1128
1143
  getRowKey,
1129
1144
  accentColor,
1130
- isLoading: isRowShimmer
1145
+ isLoading: isRowShimmer,
1146
+ recordFingerprint
1131
1147
  }
1132
1148
  )
1133
1149
  }
@@ -1201,6 +1217,181 @@ var TableBody = ({
1201
1217
  );
1202
1218
  })
1203
1219
  }
1220
+ ),
1221
+ pinnedTopData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1222
+ "div",
1223
+ {
1224
+ style: {
1225
+ gridColumn: "1 / -1",
1226
+ gridRow: 2,
1227
+ position: "sticky",
1228
+ top: headerHeight,
1229
+ zIndex: 20,
1230
+ boxShadow: "0 2px 6px -1px rgba(0,0,0,0.08)"
1231
+ },
1232
+ children: pinnedTopData.map((row, rowIdx) => {
1233
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1234
+ const isSelected = selectedKeySet.has(rk);
1235
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1236
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1237
+ "div",
1238
+ {
1239
+ className: classNames?.pinnedRow ?? "",
1240
+ style: {
1241
+ display: "grid",
1242
+ gridTemplateColumns: gridTemplateColumns ?? "",
1243
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1244
+ ...styles?.pinnedRow
1245
+ },
1246
+ children: orderedColumns.map((col) => {
1247
+ const cellValue = row[col.dataIndex];
1248
+ const stickyOffset = columnOffsets.get(col.key);
1249
+ const isPinned = Boolean(col.pinned);
1250
+ let zIndex = 0;
1251
+ if (col.key === "__select__" || col.key === "__expand__")
1252
+ zIndex = 11;
1253
+ else if (isPinned) zIndex = 2;
1254
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1255
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1256
+ "div",
1257
+ {
1258
+ "data-row-key": rk,
1259
+ "data-selected": isSelected || void 0,
1260
+ style: {
1261
+ position: isPinned ? "sticky" : "relative",
1262
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1263
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1264
+ zIndex,
1265
+ backgroundColor: pinnedRowBg,
1266
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1267
+ },
1268
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1269
+ "div",
1270
+ {
1271
+ style: {
1272
+ height: `${rowHeight}px`,
1273
+ position: "relative"
1274
+ },
1275
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1276
+ Cell,
1277
+ {
1278
+ value: cellValue,
1279
+ record: row,
1280
+ column: col,
1281
+ rowIndex: rowIdx,
1282
+ classNames,
1283
+ styles,
1284
+ isSelected,
1285
+ isExpanded,
1286
+ rowSelection,
1287
+ normalizedSelectedKeys,
1288
+ rowKey: rk,
1289
+ allData: allDataForSelection,
1290
+ getRowKey,
1291
+ accentColor,
1292
+ isLoading: false,
1293
+ recordFingerprint
1294
+ }
1295
+ )
1296
+ }
1297
+ )
1298
+ },
1299
+ col.key
1300
+ );
1301
+ })
1302
+ },
1303
+ `pinned-top-${rk}`
1304
+ );
1305
+ })
1306
+ }
1307
+ ),
1308
+ pinnedBottomData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1309
+ "div",
1310
+ {
1311
+ style: {
1312
+ gridColumn: "1 / -1",
1313
+ gridRow: 2,
1314
+ position: "sticky",
1315
+ bottom: 0,
1316
+ alignSelf: "end",
1317
+ zIndex: 20,
1318
+ boxShadow: "0 -2px 6px -1px rgba(0,0,0,0.08)"
1319
+ },
1320
+ children: pinnedBottomData.map((row, rowIdx) => {
1321
+ const rk = getRowKey ? getRowKey(row, rowIdx) : String(rowIdx);
1322
+ const isSelected = selectedKeySet.has(rk);
1323
+ const isExpanded = resolvedExpandedKeys?.has(rk) ?? false;
1324
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1325
+ "div",
1326
+ {
1327
+ className: classNames?.pinnedRow ?? "",
1328
+ style: {
1329
+ display: "grid",
1330
+ gridTemplateColumns: gridTemplateColumns ?? "",
1331
+ minWidth: totalTableWidth ? `${totalTableWidth}px` : void 0,
1332
+ ...styles?.pinnedRow
1333
+ },
1334
+ children: orderedColumns.map((col) => {
1335
+ const cellValue = row[col.dataIndex];
1336
+ const stickyOffset = columnOffsets.get(col.key);
1337
+ const isPinned = Boolean(col.pinned);
1338
+ let zIndex = 0;
1339
+ if (col.key === "__select__" || col.key === "__expand__")
1340
+ zIndex = 11;
1341
+ else if (isPinned) zIndex = 2;
1342
+ const recordFingerprint = col.render ? JSON.stringify(row) : void 0;
1343
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1344
+ "div",
1345
+ {
1346
+ "data-row-key": rk,
1347
+ "data-selected": isSelected || void 0,
1348
+ style: {
1349
+ position: isPinned ? "sticky" : "relative",
1350
+ ...col.pinned === "left" && stickyOffset !== void 0 ? { left: `${stickyOffset}px` } : {},
1351
+ ...col.pinned === "right" && stickyOffset !== void 0 ? { right: `${stickyOffset}px` } : {},
1352
+ zIndex,
1353
+ backgroundColor: pinnedRowBg,
1354
+ ...isPinned && styles?.pinnedCell ? styles.pinnedCell : {}
1355
+ },
1356
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1357
+ "div",
1358
+ {
1359
+ style: {
1360
+ height: `${rowHeight}px`,
1361
+ position: "relative"
1362
+ },
1363
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1364
+ Cell,
1365
+ {
1366
+ value: cellValue,
1367
+ record: row,
1368
+ column: col,
1369
+ rowIndex: rowIdx,
1370
+ classNames,
1371
+ styles,
1372
+ isSelected,
1373
+ isExpanded,
1374
+ rowSelection,
1375
+ normalizedSelectedKeys,
1376
+ rowKey: rk,
1377
+ allData: allDataForSelection,
1378
+ getRowKey,
1379
+ accentColor,
1380
+ isLoading: false,
1381
+ recordFingerprint
1382
+ }
1383
+ )
1384
+ }
1385
+ )
1386
+ },
1387
+ col.key
1388
+ );
1389
+ })
1390
+ },
1391
+ `pinned-bottom-${rk}`
1392
+ );
1393
+ })
1394
+ }
1204
1395
  )
1205
1396
  ] });
1206
1397
  };
@@ -1236,6 +1427,7 @@ function BoltTable({
1236
1427
  onColumnPin,
1237
1428
  onColumnHide,
1238
1429
  rowSelection,
1430
+ rowPinning,
1239
1431
  expandable,
1240
1432
  rowKey = "id",
1241
1433
  onEndReached,
@@ -1634,6 +1826,23 @@ function BoltTable({
1634
1826
  () => [...leftPinned, ...unpinned, ...rightPinned],
1635
1827
  [leftPinned, unpinned, rightPinned]
1636
1828
  );
1829
+ const freshOrderedColumns = (0, import_react4.useMemo)(() => {
1830
+ const latestMap = new Map(
1831
+ initialColumnsRef.current.map((c) => [c.key, c])
1832
+ );
1833
+ return orderedColumns.map((col) => {
1834
+ if (col.key === "__select__" || col.key === "__expand__") return col;
1835
+ const latest = latestMap.get(col.key);
1836
+ if (!latest) return col;
1837
+ if (col.render === latest.render && col.shimmerRender === latest.shimmerRender)
1838
+ return col;
1839
+ return {
1840
+ ...col,
1841
+ render: latest.render,
1842
+ shimmerRender: latest.shimmerRender
1843
+ };
1844
+ });
1845
+ }, [orderedColumns, initialColumns]);
1637
1846
  const totalTableWidth = (0, import_react4.useMemo)(
1638
1847
  () => orderedColumns.slice(0, -1).reduce((sum, col) => sum + (col.width ?? 150), 0) + (orderedColumns.at(-1)?.width ?? 150),
1639
1848
  [orderedColumns]
@@ -1759,6 +1968,35 @@ function BoltTable({
1759
1968
  }
1760
1969
  return result;
1761
1970
  }, [data, sortState, columnFilters]);
1971
+ const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = (0, import_react4.useMemo)(() => {
1972
+ if (!rowPinning || !rowPinning.top?.length && !rowPinning.bottom?.length) {
1973
+ return {
1974
+ pinnedTopRows: [],
1975
+ pinnedBottomRows: [],
1976
+ unpinnedProcessedData: processedData
1977
+ };
1978
+ }
1979
+ const topKeySet = new Set((rowPinning.top ?? []).map(String));
1980
+ const bottomKeySet = new Set((rowPinning.bottom ?? []).map(String));
1981
+ const topMap = /* @__PURE__ */ new Map();
1982
+ const bottomMap = /* @__PURE__ */ new Map();
1983
+ const rest = [];
1984
+ processedData.forEach((row, idx) => {
1985
+ const key = getRowKey(row, idx);
1986
+ if (topKeySet.has(key)) topMap.set(key, row);
1987
+ else if (bottomKeySet.has(key)) bottomMap.set(key, row);
1988
+ else rest.push(row);
1989
+ });
1990
+ const orderedTop = (rowPinning.top ?? []).map((k) => topMap.get(String(k))).filter((r) => r !== void 0);
1991
+ const orderedBottom = (rowPinning.bottom ?? []).map((k) => bottomMap.get(String(k))).filter((r) => r !== void 0);
1992
+ return {
1993
+ pinnedTopRows: orderedTop,
1994
+ pinnedBottomRows: orderedBottom,
1995
+ unpinnedProcessedData: rest
1996
+ };
1997
+ }, [processedData, rowPinning, getRowKey]);
1998
+ const pinnedTopHeight = pinnedTopRows.length * rowHeight;
1999
+ const pinnedBottomHeight = pinnedBottomRows.length * rowHeight;
1762
2000
  const columnFiltersKey = Object.keys(columnFilters).sort().map((k) => `${k}:${columnFilters[k]}`).join("|");
1763
2001
  import_react4.default.useEffect(() => {
1764
2002
  tableAreaRef.current?.scrollTo({ top: 0 });
@@ -1766,12 +2004,12 @@ function BoltTable({
1766
2004
  const pgEnabled = pagination !== false && !!pagination;
1767
2005
  const pgSize = pgEnabled ? pagination.pageSize ?? 10 : 10;
1768
2006
  const pgCurrent = pgEnabled ? Number(pagination.current ?? 1) : 1;
1769
- const needsClientPagination = pgEnabled && processedData.length > pgSize;
2007
+ const needsClientPagination = pgEnabled && unpinnedProcessedData.length > pgSize;
1770
2008
  const paginatedData = (0, import_react4.useMemo)(() => {
1771
- if (!needsClientPagination) return processedData;
2009
+ if (!needsClientPagination) return unpinnedProcessedData;
1772
2010
  const start = (pgCurrent - 1) * pgSize;
1773
- return processedData.slice(start, start + pgSize);
1774
- }, [processedData, needsClientPagination, pgCurrent, pgSize]);
2011
+ return unpinnedProcessedData.slice(start, start + pgSize);
2012
+ }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
1775
2013
  const shimmerCount = pgEnabled ? pgSize : 15;
1776
2014
  const showShimmer = isLoading && processedData.length === 0;
1777
2015
  const shimmerData = (0, import_react4.useMemo)(() => {
@@ -1829,8 +2067,9 @@ function BoltTable({
1829
2067
  return cached ? rowHeight + cached : rowHeight + expandedRowHeight;
1830
2068
  },
1831
2069
  overscan: 5,
1832
- // Render 5 extra rows above and below the visible window
1833
- getItemKey: (index) => shimmerData ? `__shimmer_${index}__` : getRowKey(displayData[index], index)
2070
+ getItemKey: (index) => shimmerData ? `__shimmer_${index}__` : getRowKey(displayData[index], index),
2071
+ paddingStart: pinnedTopHeight,
2072
+ paddingEnd: pinnedBottomHeight
1834
2073
  });
1835
2074
  const rowVirtualizerRef = (0, import_react4.useRef)(rowVirtualizer);
1836
2075
  rowVirtualizerRef.current = rowVirtualizer;
@@ -1870,7 +2109,7 @@ function BoltTable({
1870
2109
  const activeColumn = activeId ? orderedColumns.find((col) => col.key === activeId) : null;
1871
2110
  const currentPage = pgCurrent;
1872
2111
  const pageSize = pgSize;
1873
- const rawTotal = pgEnabled ? pagination.total ?? (needsClientPagination ? processedData.length : data.length) : data.length;
2112
+ const rawTotal = pgEnabled ? pagination.total ?? (needsClientPagination ? unpinnedProcessedData.length : data.length) : data.length;
1874
2113
  const lastKnownTotalRef = (0, import_react4.useRef)(0);
1875
2114
  if (!isLoading || rawTotal > 0) {
1876
2115
  lastKnownTotalRef.current = rawTotal;
@@ -2290,7 +2529,7 @@ function BoltTable({
2290
2529
  TableBody_default,
2291
2530
  {
2292
2531
  data: displayData,
2293
- orderedColumns,
2532
+ orderedColumns: freshOrderedColumns,
2294
2533
  rowVirtualizer,
2295
2534
  columnOffsets,
2296
2535
  styles,
@@ -2307,7 +2546,11 @@ function BoltTable({
2307
2546
  scrollContainerRef: tableAreaRef,
2308
2547
  isLoading: showShimmer,
2309
2548
  onExpandedRowResize: handleExpandedRowResize,
2310
- maxExpandedRowHeight
2549
+ maxExpandedRowHeight,
2550
+ pinnedTopData: pinnedTopRows,
2551
+ pinnedBottomData: pinnedBottomRows,
2552
+ gridTemplateColumns,
2553
+ headerHeight: HEADER_HEIGHT
2311
2554
  }
2312
2555
  )
2313
2556
  )
@@ -2556,6 +2799,7 @@ function BoltTable({
2556
2799
  position: "fixed",
2557
2800
  zIndex: 99999,
2558
2801
  height: 36,
2802
+ fontSize: 12,
2559
2803
  alignItems: "center",
2560
2804
  overflow: "hidden",
2561
2805
  textOverflow: "ellipsis",