bolt-table 0.1.17 → 0.1.18

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
@@ -67,6 +67,25 @@ interface ColumnType<T = unknown> {
67
67
  * `true` copies the raw value; pass a function for custom copy text.
68
68
  */
69
69
  copy?: boolean | ((value: unknown, record: T, index: number) => string);
70
+ /** Custom context menu items appended to this column's header right-click menu. */
71
+ columnHeaderContextMenuItems?: ColumnContextMenuItem[];
72
+ /** Custom context menu items appended to every cell's right-click menu in this column. */
73
+ columnCellContextMenuItems?: CellContextMenuItem<T>[];
74
+ }
75
+ /** A single item in the cell right-click context menu (column-level). */
76
+ interface CellContextMenuItem<T = unknown> {
77
+ /** Unique identifier for this menu item, used as the React `key`. */
78
+ key: string;
79
+ /** The label shown in the menu. Can be a string or React node. */
80
+ label: React.ReactNode;
81
+ /** Optional icon shown to the left of the label. */
82
+ icon?: React.ReactNode;
83
+ /** When `true`, the label renders in red to indicate a destructive action. */
84
+ danger?: boolean;
85
+ /** When `true`, the item is grayed out and click handler is not called. */
86
+ disabled?: boolean;
87
+ /** Called when the user clicks this menu item. Receives the column key, row record, and row index. */
88
+ onClick: (columnKey: string, record: T, rowIndex: number) => void;
70
89
  }
71
90
  /** A single item in the column header right-click context menu. */
72
91
  interface ColumnContextMenuItem {
@@ -201,11 +220,6 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
201
220
  readonly onEndReachedThreshold?: number;
202
221
  /** When true and data is empty, shows shimmer skeleton rows. With data, appends shimmer rows at bottom. */
203
222
  readonly isLoading?: boolean;
204
- /** Scroll indicator configuration (reserved for future use). */
205
- readonly scrollIndicators?: {
206
- vertical?: boolean;
207
- horizontal?: boolean;
208
- };
209
223
  /** Called when the user changes sort direction. Provide for server-side sorting. */
210
224
  readonly onSortChange?: (columnKey: string, direction: SortDirection) => void;
211
225
  /** Called when the user applies or clears a column filter. Provide for server-side filtering. */
@@ -370,4 +384,4 @@ interface TableBodyProps {
370
384
  }
371
385
  declare const TableBody: React$1.FC<TableBodyProps>;
372
386
 
373
- export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowPinningConfig, type RowSelectionConfig, type SortDirection, TableBody };
387
+ export { BoltTable, type BoltTableIcons, type CellContextMenuItem, 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
@@ -67,6 +67,25 @@ interface ColumnType<T = unknown> {
67
67
  * `true` copies the raw value; pass a function for custom copy text.
68
68
  */
69
69
  copy?: boolean | ((value: unknown, record: T, index: number) => string);
70
+ /** Custom context menu items appended to this column's header right-click menu. */
71
+ columnHeaderContextMenuItems?: ColumnContextMenuItem[];
72
+ /** Custom context menu items appended to every cell's right-click menu in this column. */
73
+ columnCellContextMenuItems?: CellContextMenuItem<T>[];
74
+ }
75
+ /** A single item in the cell right-click context menu (column-level). */
76
+ interface CellContextMenuItem<T = unknown> {
77
+ /** Unique identifier for this menu item, used as the React `key`. */
78
+ key: string;
79
+ /** The label shown in the menu. Can be a string or React node. */
80
+ label: React.ReactNode;
81
+ /** Optional icon shown to the left of the label. */
82
+ icon?: React.ReactNode;
83
+ /** When `true`, the label renders in red to indicate a destructive action. */
84
+ danger?: boolean;
85
+ /** When `true`, the item is grayed out and click handler is not called. */
86
+ disabled?: boolean;
87
+ /** Called when the user clicks this menu item. Receives the column key, row record, and row index. */
88
+ onClick: (columnKey: string, record: T, rowIndex: number) => void;
70
89
  }
71
90
  /** A single item in the column header right-click context menu. */
72
91
  interface ColumnContextMenuItem {
@@ -201,11 +220,6 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
201
220
  readonly onEndReachedThreshold?: number;
202
221
  /** When true and data is empty, shows shimmer skeleton rows. With data, appends shimmer rows at bottom. */
203
222
  readonly isLoading?: boolean;
204
- /** Scroll indicator configuration (reserved for future use). */
205
- readonly scrollIndicators?: {
206
- vertical?: boolean;
207
- horizontal?: boolean;
208
- };
209
223
  /** Called when the user changes sort direction. Provide for server-side sorting. */
210
224
  readonly onSortChange?: (columnKey: string, direction: SortDirection) => void;
211
225
  /** Called when the user applies or clears a column filter. Provide for server-side filtering. */
@@ -370,4 +384,4 @@ interface TableBodyProps {
370
384
  }
371
385
  declare const TableBody: React$1.FC<TableBodyProps>;
372
386
 
373
- export { BoltTable, type BoltTableIcons, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowPinningConfig, type RowSelectionConfig, type SortDirection, TableBody };
387
+ export { BoltTable, type BoltTableIcons, type CellContextMenuItem, type ColumnContextMenuItem, type ColumnType, type DataRecord, DraggableHeader, type ExpandableConfig, type PaginationType, ResizeOverlay, type RowPinningConfig, type RowSelectionConfig, type SortDirection, TableBody };
package/dist/index.js CHANGED
@@ -713,7 +713,7 @@ var DraggableHeader = import_react.default.memo(
713
713
  ] });
714
714
  },
715
715
  (prevProps, nextProps) => {
716
- 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;
716
+ 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;
717
717
  }
718
718
  );
719
719
  DraggableHeader.displayName = "DraggableHeader";
@@ -2489,7 +2489,8 @@ function BoltTable({
2489
2489
  );
2490
2490
  const hasCopy = col?.copy;
2491
2491
  const hasRowPin = !!onRowPin;
2492
- if (!hasCopy && !hasRowPin) return;
2492
+ const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2493
+ if (!hasCopy && !hasRowPin && !hasCellItems) return;
2493
2494
  e.preventDefault();
2494
2495
  setCellContextMenu({
2495
2496
  x: Math.min(e.clientX, window.innerWidth - 200),
@@ -2517,7 +2518,8 @@ function BoltTable({
2517
2518
  );
2518
2519
  const hasCopy = col?.copy;
2519
2520
  const hasRowPin = !!onRowPin;
2520
- if (!hasCopy && !hasRowPin) return;
2521
+ const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2522
+ if (!hasCopy && !hasRowPin && !hasCellItems) return;
2521
2523
  setCellContextMenu({
2522
2524
  x: Math.min(touch.clientX, window.innerWidth - 200),
2523
2525
  y: Math.min(touch.clientY, window.innerHeight - 200),
@@ -2649,7 +2651,7 @@ function BoltTable({
2649
2651
  filterValue: columnFilters[column.key] ?? "",
2650
2652
  onFilter: handleColumnFilter,
2651
2653
  onClearFilter: handleClearFilter,
2652
- customContextMenuItems: columnContextMenuItems
2654
+ customContextMenuItems: column.columnHeaderContextMenuItems ? [...columnContextMenuItems ?? [], ...column.columnHeaderContextMenuItems] : columnContextMenuItems
2653
2655
  },
2654
2656
  column.key
2655
2657
  );
@@ -3174,7 +3176,42 @@ function BoltTable({
3174
3176
  "Copy"
3175
3177
  ]
3176
3178
  }
3177
- )
3179
+ ),
3180
+ menuCol?.columnCellContextMenuItems && menuCol.columnCellContextMenuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3181
+ (hasCopy || hasRowPin) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3182
+ "div",
3183
+ {
3184
+ style: {
3185
+ borderTop: "1px solid rgba(128,128,128,0.2)",
3186
+ margin: "4px 0"
3187
+ }
3188
+ }
3189
+ ),
3190
+ menuCol.columnCellContextMenuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3191
+ "button",
3192
+ {
3193
+ "data-bt-ctx-item": "",
3194
+ disabled: item.disabled,
3195
+ style: {
3196
+ ...btnStyle,
3197
+ cursor: item.disabled ? "not-allowed" : "pointer",
3198
+ opacity: item.disabled ? 0.5 : 1,
3199
+ color: item.danger ? "#ef4444" : "inherit"
3200
+ },
3201
+ onClick: () => {
3202
+ if (menuRecord) {
3203
+ item.onClick(menuCol.key, menuRecord, menuRowIndex);
3204
+ }
3205
+ setCellContextMenu(null);
3206
+ },
3207
+ children: [
3208
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { display: "flex", width: 14, height: 14, alignItems: "center", justifyContent: "center", flexShrink: 0 }, children: item.icon }),
3209
+ item.label
3210
+ ]
3211
+ },
3212
+ item.key
3213
+ ))
3214
+ ] })
3178
3215
  ]
3179
3216
  }
3180
3217
  ),
package/dist/index.mjs CHANGED
@@ -679,7 +679,7 @@ var DraggableHeader = React.memo(
679
679
  ] });
680
680
  },
681
681
  (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;
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;
683
683
  }
684
684
  );
685
685
  DraggableHeader.displayName = "DraggableHeader";
@@ -2455,7 +2455,8 @@ function BoltTable({
2455
2455
  );
2456
2456
  const hasCopy = col?.copy;
2457
2457
  const hasRowPin = !!onRowPin;
2458
- if (!hasCopy && !hasRowPin) return;
2458
+ const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2459
+ if (!hasCopy && !hasRowPin && !hasCellItems) return;
2459
2460
  e.preventDefault();
2460
2461
  setCellContextMenu({
2461
2462
  x: Math.min(e.clientX, window.innerWidth - 200),
@@ -2483,7 +2484,8 @@ function BoltTable({
2483
2484
  );
2484
2485
  const hasCopy = col?.copy;
2485
2486
  const hasRowPin = !!onRowPin;
2486
- if (!hasCopy && !hasRowPin) return;
2487
+ const hasCellItems = col?.columnCellContextMenuItems && col.columnCellContextMenuItems.length > 0;
2488
+ if (!hasCopy && !hasRowPin && !hasCellItems) return;
2487
2489
  setCellContextMenu({
2488
2490
  x: Math.min(touch.clientX, window.innerWidth - 200),
2489
2491
  y: Math.min(touch.clientY, window.innerHeight - 200),
@@ -2615,7 +2617,7 @@ function BoltTable({
2615
2617
  filterValue: columnFilters[column.key] ?? "",
2616
2618
  onFilter: handleColumnFilter,
2617
2619
  onClearFilter: handleClearFilter,
2618
- customContextMenuItems: columnContextMenuItems
2620
+ customContextMenuItems: column.columnHeaderContextMenuItems ? [...columnContextMenuItems ?? [], ...column.columnHeaderContextMenuItems] : columnContextMenuItems
2619
2621
  },
2620
2622
  column.key
2621
2623
  );
@@ -3140,7 +3142,42 @@ function BoltTable({
3140
3142
  "Copy"
3141
3143
  ]
3142
3144
  }
3143
- )
3145
+ ),
3146
+ menuCol?.columnCellContextMenuItems && menuCol.columnCellContextMenuItems.length > 0 && /* @__PURE__ */ jsxs5(Fragment4, { children: [
3147
+ (hasCopy || hasRowPin) && /* @__PURE__ */ jsx5(
3148
+ "div",
3149
+ {
3150
+ style: {
3151
+ borderTop: "1px solid rgba(128,128,128,0.2)",
3152
+ margin: "4px 0"
3153
+ }
3154
+ }
3155
+ ),
3156
+ menuCol.columnCellContextMenuItems.map((item) => /* @__PURE__ */ jsxs5(
3157
+ "button",
3158
+ {
3159
+ "data-bt-ctx-item": "",
3160
+ disabled: item.disabled,
3161
+ style: {
3162
+ ...btnStyle,
3163
+ cursor: item.disabled ? "not-allowed" : "pointer",
3164
+ opacity: item.disabled ? 0.5 : 1,
3165
+ color: item.danger ? "#ef4444" : "inherit"
3166
+ },
3167
+ onClick: () => {
3168
+ if (menuRecord) {
3169
+ item.onClick(menuCol.key, menuRecord, menuRowIndex);
3170
+ }
3171
+ setCellContextMenu(null);
3172
+ },
3173
+ children: [
3174
+ item.icon && /* @__PURE__ */ jsx5("span", { style: { display: "flex", width: 14, height: 14, alignItems: "center", justifyContent: "center", flexShrink: 0 }, children: item.icon }),
3175
+ item.label
3176
+ ]
3177
+ },
3178
+ item.key
3179
+ ))
3180
+ ] })
3144
3181
  ]
3145
3182
  }
3146
3183
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bolt-table",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Virtualized React table with column drag & drop, pinning, resizing, sorting, filtering, and pagination.",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",