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 +152 -10
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +323 -35
- package/dist/index.mjs +323 -35
- package/package.json +5 -6
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
|
-
[](https://www.npmjs.com/package/bolt-table)
|
|
785
|
-
[](./LICENSE)
|
|
786
|
-
[](https://github.com/venkateshwebdev/Bolt-Table)
|
|
787
|
-
[](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
|
|