bolt-table 0.1.12 → 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
@@ -1034,15 +1034,26 @@ var TableBody = ({
1034
1034
  expandable,
1035
1035
  resolvedExpandedKeys,
1036
1036
  rowHeight = 40,
1037
+ totalTableWidth,
1037
1038
  scrollAreaWidth,
1038
1039
  accentColor,
1039
1040
  isLoading = false,
1040
1041
  onExpandedRowResize,
1041
- maxExpandedRowHeight
1042
+ maxExpandedRowHeight,
1043
+ pinnedTopData = [],
1044
+ pinnedBottomData = [],
1045
+ gridTemplateColumns,
1046
+ headerHeight = 36
1042
1047
  }) => {
1043
1048
  const virtualItems = rowVirtualizer.getVirtualItems();
1044
1049
  const totalSize = rowVirtualizer.getTotalSize();
1045
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;
1046
1057
  const columnStyles = (0, import_react3.useMemo)(() => {
1047
1058
  return orderedColumns.map((col, colIndex) => {
1048
1059
  const stickyOffset = columnOffsets.get(col.key);
@@ -1128,7 +1139,7 @@ var TableBody = ({
1128
1139
  rowSelection,
1129
1140
  normalizedSelectedKeys,
1130
1141
  rowKey,
1131
- allData: data,
1142
+ allData: allDataForSelection,
1132
1143
  getRowKey,
1133
1144
  accentColor,
1134
1145
  isLoading: isRowShimmer,
@@ -1206,6 +1217,181 @@ var TableBody = ({
1206
1217
  );
1207
1218
  })
1208
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
+ }
1209
1395
  )
1210
1396
  ] });
1211
1397
  };
@@ -1241,6 +1427,7 @@ function BoltTable({
1241
1427
  onColumnPin,
1242
1428
  onColumnHide,
1243
1429
  rowSelection,
1430
+ rowPinning,
1244
1431
  expandable,
1245
1432
  rowKey = "id",
1246
1433
  onEndReached,
@@ -1781,6 +1968,35 @@ function BoltTable({
1781
1968
  }
1782
1969
  return result;
1783
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;
1784
2000
  const columnFiltersKey = Object.keys(columnFilters).sort().map((k) => `${k}:${columnFilters[k]}`).join("|");
1785
2001
  import_react4.default.useEffect(() => {
1786
2002
  tableAreaRef.current?.scrollTo({ top: 0 });
@@ -1788,12 +2004,12 @@ function BoltTable({
1788
2004
  const pgEnabled = pagination !== false && !!pagination;
1789
2005
  const pgSize = pgEnabled ? pagination.pageSize ?? 10 : 10;
1790
2006
  const pgCurrent = pgEnabled ? Number(pagination.current ?? 1) : 1;
1791
- const needsClientPagination = pgEnabled && processedData.length > pgSize;
2007
+ const needsClientPagination = pgEnabled && unpinnedProcessedData.length > pgSize;
1792
2008
  const paginatedData = (0, import_react4.useMemo)(() => {
1793
- if (!needsClientPagination) return processedData;
2009
+ if (!needsClientPagination) return unpinnedProcessedData;
1794
2010
  const start = (pgCurrent - 1) * pgSize;
1795
- return processedData.slice(start, start + pgSize);
1796
- }, [processedData, needsClientPagination, pgCurrent, pgSize]);
2011
+ return unpinnedProcessedData.slice(start, start + pgSize);
2012
+ }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
1797
2013
  const shimmerCount = pgEnabled ? pgSize : 15;
1798
2014
  const showShimmer = isLoading && processedData.length === 0;
1799
2015
  const shimmerData = (0, import_react4.useMemo)(() => {
@@ -1851,8 +2067,9 @@ function BoltTable({
1851
2067
  return cached ? rowHeight + cached : rowHeight + expandedRowHeight;
1852
2068
  },
1853
2069
  overscan: 5,
1854
- // Render 5 extra rows above and below the visible window
1855
- 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
1856
2073
  });
1857
2074
  const rowVirtualizerRef = (0, import_react4.useRef)(rowVirtualizer);
1858
2075
  rowVirtualizerRef.current = rowVirtualizer;
@@ -1892,7 +2109,7 @@ function BoltTable({
1892
2109
  const activeColumn = activeId ? orderedColumns.find((col) => col.key === activeId) : null;
1893
2110
  const currentPage = pgCurrent;
1894
2111
  const pageSize = pgSize;
1895
- 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;
1896
2113
  const lastKnownTotalRef = (0, import_react4.useRef)(0);
1897
2114
  if (!isLoading || rawTotal > 0) {
1898
2115
  lastKnownTotalRef.current = rawTotal;
@@ -2329,7 +2546,11 @@ function BoltTable({
2329
2546
  scrollContainerRef: tableAreaRef,
2330
2547
  isLoading: showShimmer,
2331
2548
  onExpandedRowResize: handleExpandedRowResize,
2332
- maxExpandedRowHeight
2549
+ maxExpandedRowHeight,
2550
+ pinnedTopData: pinnedTopRows,
2551
+ pinnedBottomData: pinnedBottomRows,
2552
+ gridTemplateColumns,
2553
+ headerHeight: HEADER_HEIGHT
2333
2554
  }
2334
2555
  )
2335
2556
  )