bolt-table 0.1.38 → 0.1.40

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/README.md CHANGED
@@ -181,6 +181,14 @@ Available icon keys: `gripVertical`, `sortAsc`, `sortDesc`, `filter`, `filterCle
181
181
  | `hideGlobalSearch` | `boolean` | `false` | Hide the global search input above the table |
182
182
  | `globalSearchValue` | `string` | — | Controlled global search value |
183
183
  | `onGlobalSearchChange` | `(value: string) => void` | — | Called when the global search input changes |
184
+ | `rowDragEnabled` | `boolean` | `false` | Show a drag grip handle on each row for drag-and-drop reordering |
185
+ | `onRowReorder` | `(fromIndex, toIndex) => void` | — | Called when the user drops a row into a new position |
186
+ | `aiMode` | `boolean` | `false` | Enable the AI assistant button in the toolbar |
187
+ | `aiConfig` | `BoltTableAIConfig` | — | AI provider configuration (API key, model, etc.) |
188
+ | `onAIQuery` | `(query, context) => Promise<AIResponse>` | — | Custom AI query handler (overrides built-in AI) |
189
+ | `onAIResponse` | `(response: AIResponse) => void` | — | Called after AI applies operations |
190
+ | `aiPlaceholder` | `string` | `"Ask AI anything..."` | Placeholder text for the AI search bar |
191
+ | `aiButtonLabel` | `ReactNode` | `"Ask AI"` | Label for the AI button |
184
192
 
185
193
  ---
186
194
 
@@ -749,6 +757,145 @@ By default, BoltTable auto-sizes to its content. To fill a fixed-height containe
749
757
  </div>
750
758
  ```
751
759
 
760
+ ### Row drag-and-drop reordering
761
+
762
+ Enable row reordering with a drag grip handle on each row. Users can drag rows to new positions.
763
+
764
+ ```tsx
765
+ const [data, setData] = useState<User[]>(initialData);
766
+
767
+ <BoltTable
768
+ columns={columns}
769
+ data={data}
770
+ rowKey="id"
771
+ rowDragEnabled
772
+ onRowReorder={(fromIndex, toIndex) => {
773
+ setData(prev => {
774
+ const next = [...prev];
775
+ const [moved] = next.splice(fromIndex, 1);
776
+ next.splice(toIndex, 0, moved);
777
+ return next;
778
+ });
779
+ }}
780
+ />
781
+ ```
782
+
783
+ A grip icon appears as a pinned column on the left. Drag a row by its grip handle and drop it onto another row — the blue indicator line shows where it will land.
784
+
785
+ > **Note:** `onRowReorder` receives the indices within the current page's visible data. If you use server-side pagination, map these back to your full dataset accordingly.
786
+
787
+ ---
788
+
789
+ ### Column auto-fit width
790
+
791
+ Double-click the resize handle (right edge) of any column header to automatically fit the column width to its content. The table measures the header title and all visible cell content, then sets the optimal width (clamped between 60px and 800px).
792
+
793
+ ```tsx
794
+ // No extra configuration needed — auto-fit is built into every column's resize handle.
795
+ // Just double-click the right edge of any header.
796
+
797
+ <BoltTable columns={columns} data={data} />
798
+ ```
799
+
800
+ You still get notified via `onColumnResize` when an auto-fit happens:
801
+
802
+ ```tsx
803
+ <BoltTable
804
+ columns={columns}
805
+ data={data}
806
+ onColumnResize={(columnKey, newWidth) => {
807
+ console.log(`Column ${columnKey} auto-fitted to ${newWidth}px`);
808
+ }}
809
+ />
810
+ ```
811
+
812
+ ---
813
+
814
+ ### AI mode
815
+
816
+ Enable the AI assistant to let users interact with the table using natural language. The AI can filter, sort, style, resize, reorder, pin columns, and navigate pages.
817
+
818
+ ```tsx
819
+ <BoltTable
820
+ columns={columns}
821
+ data={data}
822
+ rowKey="id"
823
+ aiMode
824
+ aiConfig={{
825
+ provider: 'openai', // 'openai' | 'anthropic' | 'custom'
826
+ apiKey: 'sk-...',
827
+ model: 'gpt-4o-mini', // optional, defaults vary by provider
828
+ }}
829
+ onAIResponse={(response) => {
830
+ console.log('AI applied:', response.message);
831
+ }}
832
+ />
833
+ ```
834
+
835
+ **Custom AI handler** (bring your own backend):
836
+
837
+ ```tsx
838
+ <BoltTable
839
+ columns={columns}
840
+ data={data}
841
+ aiMode
842
+ onAIQuery={async (query, { data, columns }) => {
843
+ const res = await fetch('/api/table-ai', {
844
+ method: 'POST',
845
+ body: JSON.stringify({ query, schema: columns.map(c => c.key) }),
846
+ });
847
+ return res.json(); // must return { operations: [...], message: "..." }
848
+ }}
849
+ />
850
+ ```
851
+
852
+ **Saved filters** — when AI answers a query, a "Save Filter" button appears. Clicking it saves the operations to localStorage. Saved filters can be re-applied instantly with zero AI calls. The saved filters dropdown appears automatically when you have saved filters.
853
+
854
+ ```tsx
855
+ // Saved filters are stored automatically using the columnPersistence storageKey.
856
+ // To enable saved filters, just use aiMode with columnPersistence:
857
+ <BoltTable
858
+ columns={columns}
859
+ data={data}
860
+ aiMode
861
+ aiConfig={{ provider: 'openai', apiKey: 'sk-...' }}
862
+ columnPersistence={{ storageKey: 'my-table' }}
863
+ />
864
+ ```
865
+
866
+ ---
867
+
868
+ ### Safe row keys (NaN / undefined / duplicates)
869
+
870
+ BoltTable handles edge cases in row keys automatically. If your `rowKey` field contains `undefined`, `null`, `NaN`, or empty strings, the table falls back to index-based keys instead of producing broken or colliding keys.
871
+
872
+ ```tsx
873
+ // All of these work correctly — no crashes, no duplicate key warnings:
874
+
875
+ // Missing id field
876
+ <BoltTable data={[{ name: 'Alice' }, { name: 'Bob' }]} rowKey="id" columns={columns} />
877
+
878
+ // NaN values
879
+ <BoltTable data={[{ id: NaN, name: 'Alice' }]} rowKey="id" columns={columns} />
880
+
881
+ // Duplicate ids — automatically deduplicated
882
+ <BoltTable
883
+ data={[
884
+ { id: 1, name: 'Alice' },
885
+ { id: 1, name: 'Alice (copy)' },
886
+ ]}
887
+ rowKey="id"
888
+ columns={columns}
889
+ />
890
+
891
+ // rowKey function that might return undefined
892
+ <BoltTable
893
+ data={data}
894
+ rowKey={(record) => record.uuid} // safe even if uuid is undefined
895
+ columns={columns}
896
+ />
897
+ ```
898
+
752
899
  ---
753
900
 
754
901
  ## Documentation
@@ -771,6 +918,11 @@ import type {
771
918
  SortDirection,
772
919
  DataRecord,
773
920
  BoltTableIcons,
921
+ // AI types
922
+ AIResponse,
923
+ AIOperation,
924
+ BoltTableAIConfig,
925
+ BoltTableConfig,
774
926
  } from 'bolt-table';
775
927
  ```
776
928
 
@@ -780,16 +932,6 @@ import type {
780
932
 
781
933
  MIT © [Venkatesh Sirigineedi](https://github.com/venkateshwebdev)
782
934
 
783
-
784
- [![npm version](https://img.shields.io/npm/v/bolt-table)](https://www.npmjs.com/package/bolt-table)
785
- [![license](https://img.shields.io/npm/l/bolt-table)](./LICENSE)
786
- [![github](https://img.shields.io/badge/GitHub-Source-181717?logo=github)](https://github.com/venkateshwebdev/Bolt-Table)
787
- [![website](https://img.shields.io/badge/Website-Live_Demo-blue?logo=vercel)](https://bolt-table.vercel.app/)
788
-
789
- ---
790
-
791
- ## Features
792
-
793
935
  - **Row virtualization** — only visible rows are rendered, powered by TanStack Virtual
794
936
  - **Drag to reorder columns** — custom zero-dependency drag-and-drop (no @dnd-kit needed)
795
937
  - **Column pinning** — pin columns to the left or right edge via right-click
package/dist/index.d.mts CHANGED
@@ -408,6 +408,10 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
408
408
  readonly aiPlaceholder?: string;
409
409
  /** Label for the AI button. Defaults to "Ask AI". */
410
410
  readonly aiButtonLabel?: React$1.ReactNode;
411
+ /** Enable row drag-and-drop reordering. When true, shows a grip handle on each row. Requires `onRowReorder`. */
412
+ readonly rowDragEnabled?: boolean;
413
+ /** Called when the user drops a row into a new position. Receives the old and new index. */
414
+ readonly onRowReorder?: (fromIndex: number, toIndex: number) => void;
411
415
  }
412
416
  interface ClassNamesTypes {
413
417
  /** Applied to all non-pinned column header cells. */
@@ -475,7 +479,7 @@ interface StylesTypes {
475
479
  /** Inline styles for the "X–Y of Z" info text. */
476
480
  paginationInfo?: CSSProperties;
477
481
  }
478
- declare function BoltTable<T extends DataRecord = DataRecord>({ columns: rawInitialColumns, data: rawData, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, onRowPin, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, rowClassName, rowStyle, disabledFilters, onCopy, keepPinnedRowsAcrossPages, onEdit, onRowClick, enableColumnVirtualization, enableDynamicRowHeight, columnPersistence, showColumnSettings, hideGlobalSearch, globalSearchValue, onGlobalSearchChange, toolbarContent, columnSettingsLabel, aiMode, aiConfig, onAIQuery, onAIResponse, aiPlaceholder, aiButtonLabel, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
482
+ declare function BoltTable<T extends DataRecord = DataRecord>({ columns: rawInitialColumns, data: rawData, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, onRowPin, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, rowClassName, rowStyle, disabledFilters, onCopy, keepPinnedRowsAcrossPages, onEdit, onRowClick, enableColumnVirtualization, enableDynamicRowHeight, columnPersistence, showColumnSettings, hideGlobalSearch, globalSearchValue, onGlobalSearchChange, toolbarContent, columnSettingsLabel, aiMode, aiConfig, onAIQuery, onAIResponse, aiPlaceholder, aiButtonLabel, rowDragEnabled, onRowReorder, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
479
483
 
480
484
  interface DraggableHeaderProps {
481
485
  /** Column definition for this header cell. */
@@ -528,8 +532,10 @@ interface DraggableHeaderProps {
528
532
  stickyTop?: number;
529
533
  /** When true, removes the left border (for the first visible column). */
530
534
  isFirstColumn?: boolean;
535
+ /** Called when the user double-clicks the resize handle to auto-fit column width. */
536
+ onAutoFitColumn?: (columnKey: string) => void;
531
537
  }
532
- declare const DraggableHeader: React$1.MemoExoticComponent<({ column, visualIndex, accentColor, onResizeStart, styles, classNames, hideGripIcon, gripIcon, stickyOffset, onTogglePin, onToggleHide, isLastColumn, sortDirection, onSort, filterValue, onFilter, onClearFilter, customContextMenuItems, icons, onColumnDragStart, disabledFilters, headerGridRow, headerHeight, stickyTop, isFirstColumn, }: DraggableHeaderProps) => react_jsx_runtime.JSX.Element>;
538
+ declare const DraggableHeader: React$1.MemoExoticComponent<({ column, visualIndex, accentColor, onResizeStart, styles, classNames, hideGripIcon, gripIcon, stickyOffset, onTogglePin, onToggleHide, isLastColumn, sortDirection, onSort, filterValue, onFilter, onClearFilter, customContextMenuItems, icons, onColumnDragStart, disabledFilters, headerGridRow, headerHeight, stickyTop, isFirstColumn, onAutoFitColumn, }: DraggableHeaderProps) => react_jsx_runtime.JSX.Element>;
533
539
 
534
540
  interface ResizeOverlayHandle {
535
541
  show: (viewportX: number, columnName: string, areaRect: DOMRect, headerLeftLocal: number, minSize: number, scrollTop: number, scrollLeft: number, initialLineX: number) => void;
@@ -614,6 +620,8 @@ interface TableBodyProps {
614
620
  columnGridIndexMap?: Map<string, number>;
615
621
  /** Optional AI cell style function. Returns extra styles for a specific cell. */
616
622
  cellStyleFn?: (record: DataRecord, columnKey: string) => React$1.CSSProperties | undefined;
623
+ /** Called when the user starts dragging a row by its grip handle. */
624
+ onRowDragStart?: (rowIndex: number, e: React$1.PointerEvent) => void;
617
625
  }
618
626
  declare const TableBody: React$1.FC<TableBodyProps>;
619
627
 
package/dist/index.d.ts CHANGED
@@ -408,6 +408,10 @@ interface BoltTableProps<T extends DataRecord = DataRecord> {
408
408
  readonly aiPlaceholder?: string;
409
409
  /** Label for the AI button. Defaults to "Ask AI". */
410
410
  readonly aiButtonLabel?: React$1.ReactNode;
411
+ /** Enable row drag-and-drop reordering. When true, shows a grip handle on each row. Requires `onRowReorder`. */
412
+ readonly rowDragEnabled?: boolean;
413
+ /** Called when the user drops a row into a new position. Receives the old and new index. */
414
+ readonly onRowReorder?: (fromIndex: number, toIndex: number) => void;
411
415
  }
412
416
  interface ClassNamesTypes {
413
417
  /** Applied to all non-pinned column header cells. */
@@ -475,7 +479,7 @@ interface StylesTypes {
475
479
  /** Inline styles for the "X–Y of Z" info text. */
476
480
  paginationInfo?: CSSProperties;
477
481
  }
478
- declare function BoltTable<T extends DataRecord = DataRecord>({ columns: rawInitialColumns, data: rawData, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, onRowPin, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, rowClassName, rowStyle, disabledFilters, onCopy, keepPinnedRowsAcrossPages, onEdit, onRowClick, enableColumnVirtualization, enableDynamicRowHeight, columnPersistence, showColumnSettings, hideGlobalSearch, globalSearchValue, onGlobalSearchChange, toolbarContent, columnSettingsLabel, aiMode, aiConfig, onAIQuery, onAIResponse, aiPlaceholder, aiButtonLabel, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
482
+ declare function BoltTable<T extends DataRecord = DataRecord>({ columns: rawInitialColumns, data: rawData, rowHeight, expandedRowHeight, maxExpandedRowHeight, accentColor, className, classNames, styles, gripIcon, hideGripIcon, icons, pagination, onPaginationChange, onColumnResize, onColumnOrderChange, onColumnPin, onColumnHide, rowSelection, rowPinning, onRowPin, expandable, rowKey, onEndReached, onEndReachedThreshold, isLoading, onSortChange, onFilterChange, columnContextMenuItems, autoHeight, layoutLoading, emptyRenderer, rowClassName, rowStyle, disabledFilters, onCopy, keepPinnedRowsAcrossPages, onEdit, onRowClick, enableColumnVirtualization, enableDynamicRowHeight, columnPersistence, showColumnSettings, hideGlobalSearch, globalSearchValue, onGlobalSearchChange, toolbarContent, columnSettingsLabel, aiMode, aiConfig, onAIQuery, onAIResponse, aiPlaceholder, aiButtonLabel, rowDragEnabled, onRowReorder, }: BoltTableProps<T>): react_jsx_runtime.JSX.Element;
479
483
 
480
484
  interface DraggableHeaderProps {
481
485
  /** Column definition for this header cell. */
@@ -528,8 +532,10 @@ interface DraggableHeaderProps {
528
532
  stickyTop?: number;
529
533
  /** When true, removes the left border (for the first visible column). */
530
534
  isFirstColumn?: boolean;
535
+ /** Called when the user double-clicks the resize handle to auto-fit column width. */
536
+ onAutoFitColumn?: (columnKey: string) => void;
531
537
  }
532
- declare const DraggableHeader: React$1.MemoExoticComponent<({ column, visualIndex, accentColor, onResizeStart, styles, classNames, hideGripIcon, gripIcon, stickyOffset, onTogglePin, onToggleHide, isLastColumn, sortDirection, onSort, filterValue, onFilter, onClearFilter, customContextMenuItems, icons, onColumnDragStart, disabledFilters, headerGridRow, headerHeight, stickyTop, isFirstColumn, }: DraggableHeaderProps) => react_jsx_runtime.JSX.Element>;
538
+ declare const DraggableHeader: React$1.MemoExoticComponent<({ column, visualIndex, accentColor, onResizeStart, styles, classNames, hideGripIcon, gripIcon, stickyOffset, onTogglePin, onToggleHide, isLastColumn, sortDirection, onSort, filterValue, onFilter, onClearFilter, customContextMenuItems, icons, onColumnDragStart, disabledFilters, headerGridRow, headerHeight, stickyTop, isFirstColumn, onAutoFitColumn, }: DraggableHeaderProps) => react_jsx_runtime.JSX.Element>;
533
539
 
534
540
  interface ResizeOverlayHandle {
535
541
  show: (viewportX: number, columnName: string, areaRect: DOMRect, headerLeftLocal: number, minSize: number, scrollTop: number, scrollLeft: number, initialLineX: number) => void;
@@ -614,6 +620,8 @@ interface TableBodyProps {
614
620
  columnGridIndexMap?: Map<string, number>;
615
621
  /** Optional AI cell style function. Returns extra styles for a specific cell. */
616
622
  cellStyleFn?: (record: DataRecord, columnKey: string) => React$1.CSSProperties | undefined;
623
+ /** Called when the user starts dragging a row by its grip handle. */
624
+ onRowDragStart?: (rowIndex: number, e: React$1.PointerEvent) => void;
617
625
  }
618
626
  declare const TableBody: React$1.FC<TableBodyProps>;
619
627