bolt-table 0.1.38 → 0.1.39
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 +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +323 -35
- package/dist/index.mjs +323 -35
- package/package.json +1 -1
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
|
|
package/dist/index.js
CHANGED
|
@@ -184,7 +184,8 @@ var DraggableHeader = import_react.default.memo(
|
|
|
184
184
|
headerGridRow = 1,
|
|
185
185
|
headerHeight = 36,
|
|
186
186
|
stickyTop = 0,
|
|
187
|
-
isFirstColumn = false
|
|
187
|
+
isFirstColumn = false,
|
|
188
|
+
onAutoFitColumn
|
|
188
189
|
}) => {
|
|
189
190
|
const effectivelySortable = isColumnSortable(column);
|
|
190
191
|
const effectivelyFilterable = !disabledFilters && isColumnFilterable(column);
|
|
@@ -252,6 +253,12 @@ var DraggableHeader = import_react.default.memo(
|
|
|
252
253
|
if (column.pinned) return;
|
|
253
254
|
onResizeStart?.(column.key, e);
|
|
254
255
|
};
|
|
256
|
+
const handleResizeDoubleClick = (e) => {
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
e.stopPropagation();
|
|
259
|
+
if (column.pinned) return;
|
|
260
|
+
onAutoFitColumn?.(column.key);
|
|
261
|
+
};
|
|
255
262
|
const columnWidth = column.width ?? 150;
|
|
256
263
|
const widthPx = `${columnWidth}px`;
|
|
257
264
|
const isPinned = Boolean(column.pinned);
|
|
@@ -399,7 +406,8 @@ var DraggableHeader = import_react.default.memo(
|
|
|
399
406
|
padding: 0
|
|
400
407
|
},
|
|
401
408
|
onMouseDown: handleResizeStart,
|
|
402
|
-
|
|
409
|
+
onDoubleClick: handleResizeDoubleClick,
|
|
410
|
+
"aria-label": `Resize ${column.key} column (double-click to auto-fit)`,
|
|
403
411
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
404
412
|
"div",
|
|
405
413
|
{
|
|
@@ -1490,7 +1498,8 @@ var TableBody = ({
|
|
|
1490
1498
|
enableDynamicRowHeight = false,
|
|
1491
1499
|
onRowHeightChange,
|
|
1492
1500
|
columnGridIndexMap,
|
|
1493
|
-
cellStyleFn
|
|
1501
|
+
cellStyleFn,
|
|
1502
|
+
onRowDragStart
|
|
1494
1503
|
}) => {
|
|
1495
1504
|
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
1496
1505
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
@@ -1572,6 +1581,7 @@ var TableBody = ({
|
|
|
1572
1581
|
"div",
|
|
1573
1582
|
{
|
|
1574
1583
|
"data-row-key": rowKey,
|
|
1584
|
+
"data-row-index": virtualRow.index,
|
|
1575
1585
|
"data-column-key": col.key,
|
|
1576
1586
|
"data-bt-cell": "",
|
|
1577
1587
|
"data-selected": isSelected || void 0,
|
|
@@ -1584,7 +1594,45 @@ var TableBody = ({
|
|
|
1584
1594
|
height: enableDynamicRowHeight ? void 0 : `${virtualRow.size}px`,
|
|
1585
1595
|
minHeight: enableDynamicRowHeight ? `${rowHeight}px` : void 0
|
|
1586
1596
|
},
|
|
1587
|
-
children:
|
|
1597
|
+
children: col.key === "__drag__" && onRowDragStart ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1598
|
+
"div",
|
|
1599
|
+
{
|
|
1600
|
+
style: {
|
|
1601
|
+
height: `${rowHeight}px`,
|
|
1602
|
+
display: "flex",
|
|
1603
|
+
alignItems: "center",
|
|
1604
|
+
justifyContent: "center",
|
|
1605
|
+
borderBottom: "1px solid rgba(128,128,128,0.2)",
|
|
1606
|
+
...rowSty
|
|
1607
|
+
},
|
|
1608
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1609
|
+
"span",
|
|
1610
|
+
{
|
|
1611
|
+
"data-bt-row-grip": "",
|
|
1612
|
+
onPointerDown: (e) => {
|
|
1613
|
+
if (e.button !== 0) return;
|
|
1614
|
+
onRowDragStart(virtualRow.index, e);
|
|
1615
|
+
},
|
|
1616
|
+
style: {
|
|
1617
|
+
display: "flex",
|
|
1618
|
+
alignItems: "center",
|
|
1619
|
+
justifyContent: "center",
|
|
1620
|
+
touchAction: "none",
|
|
1621
|
+
width: "100%",
|
|
1622
|
+
height: "100%"
|
|
1623
|
+
},
|
|
1624
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1625
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "5", r: "1" }),
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
|
|
1627
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "19", r: "1" }),
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "5", r: "1" }),
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "19", r: "1" })
|
|
1631
|
+
] })
|
|
1632
|
+
}
|
|
1633
|
+
)
|
|
1634
|
+
}
|
|
1635
|
+
) : enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1588
1636
|
DynamicRowMeasurer,
|
|
1589
1637
|
{
|
|
1590
1638
|
index: virtualRow.index,
|
|
@@ -2079,7 +2127,9 @@ function BoltTable({
|
|
|
2079
2127
|
onAIQuery,
|
|
2080
2128
|
onAIResponse,
|
|
2081
2129
|
aiPlaceholder = "Ask AI anything about your data...",
|
|
2082
|
-
aiButtonLabel
|
|
2130
|
+
aiButtonLabel,
|
|
2131
|
+
rowDragEnabled = false,
|
|
2132
|
+
onRowReorder
|
|
2083
2133
|
}) {
|
|
2084
2134
|
const data = (0, import_react4.useMemo)(() => {
|
|
2085
2135
|
if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
|
|
@@ -2276,6 +2326,7 @@ function BoltTable({
|
|
|
2276
2326
|
}
|
|
2277
2327
|
});
|
|
2278
2328
|
const [showSavedFilters, setShowSavedFilters] = (0, import_react4.useState)(false);
|
|
2329
|
+
const [justSavedFilter, setJustSavedFilter] = (0, import_react4.useState)(false);
|
|
2279
2330
|
const savedFiltersRef = (0, import_react4.useRef)(null);
|
|
2280
2331
|
import_react4.default.useEffect(() => {
|
|
2281
2332
|
if (!showSavedFilters) return;
|
|
@@ -2297,6 +2348,8 @@ function BoltTable({
|
|
|
2297
2348
|
localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
|
|
2298
2349
|
} catch {
|
|
2299
2350
|
}
|
|
2351
|
+
setJustSavedFilter(true);
|
|
2352
|
+
setTimeout(() => setJustSavedFilter(false), 1500);
|
|
2300
2353
|
}, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
|
|
2301
2354
|
const removeSavedFilter = (0, import_react4.useCallback)((index) => {
|
|
2302
2355
|
const next = savedAIFilters.filter((_, i) => i !== index);
|
|
@@ -2510,17 +2563,29 @@ function BoltTable({
|
|
|
2510
2563
|
}, []);
|
|
2511
2564
|
const getRowKey = (0, import_react4.useCallback)(
|
|
2512
2565
|
(record, index) => {
|
|
2513
|
-
if (record == null) return
|
|
2566
|
+
if (record == null) return `__row_${index}`;
|
|
2514
2567
|
try {
|
|
2515
|
-
if (typeof rowKey === "function")
|
|
2568
|
+
if (typeof rowKey === "function") {
|
|
2569
|
+
const result = rowKey(record);
|
|
2570
|
+
const str = String(result);
|
|
2571
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
|
|
2572
|
+
return `__row_${index}`;
|
|
2573
|
+
}
|
|
2574
|
+
return str;
|
|
2575
|
+
}
|
|
2516
2576
|
if (typeof rowKey === "string") {
|
|
2517
2577
|
const val = record[rowKey];
|
|
2518
|
-
|
|
2578
|
+
if (val == null) return `__row_${index}`;
|
|
2579
|
+
const str = String(val);
|
|
2580
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
|
|
2581
|
+
return `__row_${index}`;
|
|
2582
|
+
}
|
|
2583
|
+
return str;
|
|
2519
2584
|
}
|
|
2520
2585
|
} catch {
|
|
2521
|
-
return
|
|
2586
|
+
return `__row_${index}`;
|
|
2522
2587
|
}
|
|
2523
|
-
return
|
|
2588
|
+
return `__row_${index}`;
|
|
2524
2589
|
},
|
|
2525
2590
|
[rowKey]
|
|
2526
2591
|
);
|
|
@@ -2554,11 +2619,20 @@ function BoltTable({
|
|
|
2554
2619
|
(record, index) => {
|
|
2555
2620
|
if (record == null) return index;
|
|
2556
2621
|
try {
|
|
2557
|
-
if (typeof rowKey === "function")
|
|
2622
|
+
if (typeof rowKey === "function") {
|
|
2623
|
+
const result = rowKey(record);
|
|
2624
|
+
if (result == null || typeof result === "number" && Number.isNaN(result)) return index;
|
|
2625
|
+
const str = String(result);
|
|
2626
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
|
|
2627
|
+
return result;
|
|
2628
|
+
}
|
|
2558
2629
|
if (typeof rowKey === "string") {
|
|
2559
2630
|
const val = record[rowKey];
|
|
2631
|
+
if (val == null || typeof val === "number" && Number.isNaN(val)) return index;
|
|
2632
|
+
const str = String(val);
|
|
2633
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
|
|
2560
2634
|
if (typeof val === "number" || typeof val === "string") return val;
|
|
2561
|
-
return
|
|
2635
|
+
return str;
|
|
2562
2636
|
}
|
|
2563
2637
|
} catch {
|
|
2564
2638
|
return index;
|
|
@@ -2645,6 +2719,19 @@ function BoltTable({
|
|
|
2645
2719
|
};
|
|
2646
2720
|
return [selectionColumn, ...columnsWithExpand];
|
|
2647
2721
|
}, [rowSelection, columnsWithExpand]);
|
|
2722
|
+
const columnsWithDrag = (0, import_react4.useMemo)(() => {
|
|
2723
|
+
if (!rowDragEnabled || !onRowReorder) return columnsWithSelection;
|
|
2724
|
+
const dragColumn = {
|
|
2725
|
+
key: "__drag__",
|
|
2726
|
+
dataIndex: "__drag__",
|
|
2727
|
+
title: "",
|
|
2728
|
+
width: 36,
|
|
2729
|
+
pinned: "left",
|
|
2730
|
+
hidden: false,
|
|
2731
|
+
render: () => null
|
|
2732
|
+
};
|
|
2733
|
+
return [dragColumn, ...columnsWithSelection];
|
|
2734
|
+
}, [rowDragEnabled, onRowReorder, columnsWithSelection]);
|
|
2648
2735
|
const resizeOverlayRef = (0, import_react4.useRef)(null);
|
|
2649
2736
|
const tableAreaRef = (0, import_react4.useRef)(null);
|
|
2650
2737
|
const [scrollAreaWidth, setScrollAreaWidth] = (0, import_react4.useState)(0);
|
|
@@ -2727,7 +2814,7 @@ function BoltTable({
|
|
|
2727
2814
|
onColumnOrderChangeRef.current = onColumnOrderChange;
|
|
2728
2815
|
const handleColumnDragStart = (0, import_react4.useCallback)(
|
|
2729
2816
|
(columnKey, e) => {
|
|
2730
|
-
if (columnKey === "__select__" || columnKey === "__expand__") return;
|
|
2817
|
+
if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
|
|
2731
2818
|
const headerEl = e.currentTarget.closest(
|
|
2732
2819
|
"[data-column-key]"
|
|
2733
2820
|
);
|
|
@@ -2762,7 +2849,7 @@ function BoltTable({
|
|
|
2762
2849
|
let newOverId = null;
|
|
2763
2850
|
headers.forEach((h) => {
|
|
2764
2851
|
const key = h.dataset.columnKey;
|
|
2765
|
-
if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
|
|
2852
|
+
if (!key || key === "__select__" || key === "__expand__" || key === "__drag__" || key === columnKey) {
|
|
2766
2853
|
h.removeAttribute("data-drag-over");
|
|
2767
2854
|
return;
|
|
2768
2855
|
}
|
|
@@ -2814,16 +2901,97 @@ function BoltTable({
|
|
|
2814
2901
|
},
|
|
2815
2902
|
[]
|
|
2816
2903
|
);
|
|
2904
|
+
const rowDragGhostRef = (0, import_react4.useRef)(null);
|
|
2905
|
+
const rowDragFromRef = (0, import_react4.useRef)(null);
|
|
2906
|
+
const rowDragOverRef = (0, import_react4.useRef)(null);
|
|
2907
|
+
const onRowReorderRef = (0, import_react4.useRef)(onRowReorder);
|
|
2908
|
+
onRowReorderRef.current = onRowReorder;
|
|
2909
|
+
const handleRowDragStart = (0, import_react4.useCallback)(
|
|
2910
|
+
(rowIndex, e) => {
|
|
2911
|
+
if (!onRowReorderRef.current) return;
|
|
2912
|
+
e.preventDefault();
|
|
2913
|
+
rowDragFromRef.current = rowIndex;
|
|
2914
|
+
rowDragOverRef.current = null;
|
|
2915
|
+
const target = e.currentTarget.closest("[data-row-key]");
|
|
2916
|
+
const rowHeight2 = target?.offsetHeight ?? 40;
|
|
2917
|
+
const ghost = rowDragGhostRef.current;
|
|
2918
|
+
if (ghost) {
|
|
2919
|
+
ghost.textContent = `Row ${rowIndex + 1}`;
|
|
2920
|
+
ghost.style.display = "flex";
|
|
2921
|
+
ghost.style.left = `${e.clientX + 12}px`;
|
|
2922
|
+
ghost.style.top = `${e.clientY - 12}px`;
|
|
2923
|
+
ghost.style.height = `${Math.min(rowHeight2, 36)}px`;
|
|
2924
|
+
}
|
|
2925
|
+
const grabStyle = document.createElement("style");
|
|
2926
|
+
grabStyle.textContent = "* { cursor: grabbing !important; }";
|
|
2927
|
+
document.head.appendChild(grabStyle);
|
|
2928
|
+
const scrollEl = tableAreaRef.current;
|
|
2929
|
+
const onMove = (ev) => {
|
|
2930
|
+
if (ghost) {
|
|
2931
|
+
ghost.style.left = `${ev.clientX + 12}px`;
|
|
2932
|
+
ghost.style.top = `${ev.clientY - 12}px`;
|
|
2933
|
+
}
|
|
2934
|
+
if (!scrollEl) return;
|
|
2935
|
+
const cells = scrollEl.querySelectorAll("[data-bt-cell][data-row-key]");
|
|
2936
|
+
let closestIdx = null;
|
|
2937
|
+
let closestDist = Infinity;
|
|
2938
|
+
cells.forEach((cell) => {
|
|
2939
|
+
const rk = cell.dataset.rowKey;
|
|
2940
|
+
if (!rk) return;
|
|
2941
|
+
const rect = cell.getBoundingClientRect();
|
|
2942
|
+
const midY = rect.top + rect.height / 2;
|
|
2943
|
+
const dist = Math.abs(ev.clientY - midY);
|
|
2944
|
+
if (dist < closestDist) {
|
|
2945
|
+
closestDist = dist;
|
|
2946
|
+
const idxAttr = cell.dataset.rowIndex;
|
|
2947
|
+
if (idxAttr != null) closestIdx = Number(idxAttr);
|
|
2948
|
+
}
|
|
2949
|
+
});
|
|
2950
|
+
scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
|
|
2951
|
+
(el) => el.removeAttribute("data-row-drag-over")
|
|
2952
|
+
);
|
|
2953
|
+
if (closestIdx != null && closestIdx !== rowDragFromRef.current) {
|
|
2954
|
+
rowDragOverRef.current = closestIdx;
|
|
2955
|
+
scrollEl.querySelectorAll(
|
|
2956
|
+
`[data-bt-cell][data-row-index="${closestIdx}"]`
|
|
2957
|
+
).forEach((el) => el.setAttribute("data-row-drag-over", ""));
|
|
2958
|
+
} else {
|
|
2959
|
+
rowDragOverRef.current = null;
|
|
2960
|
+
}
|
|
2961
|
+
};
|
|
2962
|
+
const onUp = () => {
|
|
2963
|
+
document.removeEventListener("pointermove", onMove);
|
|
2964
|
+
document.removeEventListener("pointerup", onUp);
|
|
2965
|
+
grabStyle.remove();
|
|
2966
|
+
if (ghost) ghost.style.display = "none";
|
|
2967
|
+
if (scrollEl) {
|
|
2968
|
+
scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
|
|
2969
|
+
(el) => el.removeAttribute("data-row-drag-over")
|
|
2970
|
+
);
|
|
2971
|
+
}
|
|
2972
|
+
const from = rowDragFromRef.current;
|
|
2973
|
+
const to = rowDragOverRef.current;
|
|
2974
|
+
if (from != null && to != null && from !== to) {
|
|
2975
|
+
onRowReorderRef.current?.(from, to);
|
|
2976
|
+
}
|
|
2977
|
+
rowDragFromRef.current = null;
|
|
2978
|
+
rowDragOverRef.current = null;
|
|
2979
|
+
};
|
|
2980
|
+
document.addEventListener("pointermove", onMove);
|
|
2981
|
+
document.addEventListener("pointerup", onUp);
|
|
2982
|
+
},
|
|
2983
|
+
[]
|
|
2984
|
+
);
|
|
2817
2985
|
const handleResizeStart = (columnKey, e) => {
|
|
2818
2986
|
e.preventDefault();
|
|
2819
2987
|
e.stopPropagation();
|
|
2820
|
-
if (columnKey === "__select__" || columnKey === "__expand__") return;
|
|
2821
|
-
const columnIndex =
|
|
2988
|
+
if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
|
|
2989
|
+
const columnIndex = columnsWithDrag.findIndex(
|
|
2822
2990
|
(col) => col.key === columnKey
|
|
2823
2991
|
);
|
|
2824
2992
|
if (columnIndex === -1) return;
|
|
2825
|
-
if (
|
|
2826
|
-
const column =
|
|
2993
|
+
if (columnsWithDrag[columnIndex].pinned) return;
|
|
2994
|
+
const column = columnsWithDrag[columnIndex];
|
|
2827
2995
|
const startWidth = column.width ?? 150;
|
|
2828
2996
|
resizeStateRef.current = {
|
|
2829
2997
|
columnKey,
|
|
@@ -2880,9 +3048,50 @@ function BoltTable({
|
|
|
2880
3048
|
});
|
|
2881
3049
|
onColumnResize?.(columnKey, finalWidth);
|
|
2882
3050
|
}, [onColumnResize]);
|
|
3051
|
+
const handleAutoFitColumn = (0, import_react4.useCallback)((columnKey) => {
|
|
3052
|
+
const scrollEl = tableAreaRef.current;
|
|
3053
|
+
if (!scrollEl) return;
|
|
3054
|
+
const col = columnsWithDrag.find((c) => c.key === columnKey);
|
|
3055
|
+
if (!col) return;
|
|
3056
|
+
const headerEl = scrollEl.querySelector(
|
|
3057
|
+
`[data-column-key="${columnKey}"] [data-bt-grip]`
|
|
3058
|
+
)?.parentElement ?? scrollEl.querySelector(`[data-column-key="${columnKey}"]`);
|
|
3059
|
+
let maxWidth = 0;
|
|
3060
|
+
if (headerEl) {
|
|
3061
|
+
const title = typeof col.title === "string" ? col.title : col.key;
|
|
3062
|
+
const canvas = document.createElement("canvas");
|
|
3063
|
+
const ctx = canvas.getContext("2d");
|
|
3064
|
+
if (ctx) {
|
|
3065
|
+
const computedStyle = window.getComputedStyle(headerEl);
|
|
3066
|
+
ctx.font = `${computedStyle.fontWeight} ${computedStyle.fontSize} ${computedStyle.fontFamily}`;
|
|
3067
|
+
maxWidth = ctx.measureText(title).width + 60;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
const cells = scrollEl.querySelectorAll(
|
|
3071
|
+
`[data-bt-cell][data-column-key="${columnKey}"]`
|
|
3072
|
+
);
|
|
3073
|
+
cells.forEach((cell) => {
|
|
3074
|
+
const inner = cell.querySelector("div > div");
|
|
3075
|
+
if (inner) {
|
|
3076
|
+
const scrollW = inner.scrollWidth;
|
|
3077
|
+
if (scrollW > maxWidth) maxWidth = scrollW;
|
|
3078
|
+
}
|
|
3079
|
+
});
|
|
3080
|
+
const finalWidth = Math.max(60, Math.min(Math.ceil(maxWidth) + 24, 800));
|
|
3081
|
+
manuallyResizedRef.current.add(columnKey);
|
|
3082
|
+
import_react4.default.startTransition(() => {
|
|
3083
|
+
setColumnWidths((prev) => {
|
|
3084
|
+
const next = new Map(prev);
|
|
3085
|
+
next.set(columnKey, finalWidth);
|
|
3086
|
+
return next;
|
|
3087
|
+
});
|
|
3088
|
+
});
|
|
3089
|
+
onColumnResize?.(columnKey, finalWidth);
|
|
3090
|
+
}, [columnsWithDrag, onColumnResize]);
|
|
2883
3091
|
const { leftPinned, unpinned, rightPinned } = (0, import_react4.useMemo)(() => {
|
|
2884
|
-
const columnMap = new Map(
|
|
3092
|
+
const columnMap = new Map(columnsWithDrag.map((c) => [c.key, c]));
|
|
2885
3093
|
const systemKeys = [
|
|
3094
|
+
...rowDragEnabled && onRowReorder ? ["__drag__"] : [],
|
|
2886
3095
|
...rowSelection ? ["__select__"] : [],
|
|
2887
3096
|
...expandable ? ["__expand__"] : []
|
|
2888
3097
|
];
|
|
@@ -2894,7 +3103,7 @@ function BoltTable({
|
|
|
2894
3103
|
else center.push(col);
|
|
2895
3104
|
});
|
|
2896
3105
|
return { leftPinned: left, unpinned: center, rightPinned: right };
|
|
2897
|
-
}, [columnOrder,
|
|
3106
|
+
}, [columnOrder, columnsWithDrag, rowSelection, expandable, rowDragEnabled, onRowReorder]);
|
|
2898
3107
|
const orderedColumns = (0, import_react4.useMemo)(
|
|
2899
3108
|
() => [...leftPinned, ...unpinned, ...rightPinned],
|
|
2900
3109
|
[leftPinned, unpinned, rightPinned]
|
|
@@ -2902,7 +3111,7 @@ function BoltTable({
|
|
|
2902
3111
|
const freshOrderedColumns = (0, import_react4.useMemo)(() => {
|
|
2903
3112
|
const latestMap = new Map(initialColumnsRef.current.map((c) => [c.key, c]));
|
|
2904
3113
|
return orderedColumns.map((col) => {
|
|
2905
|
-
if (col.key === "__select__" || col.key === "__expand__") return col;
|
|
3114
|
+
if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return col;
|
|
2906
3115
|
const latest = latestMap.get(col.key);
|
|
2907
3116
|
if (!latest) return col;
|
|
2908
3117
|
return {
|
|
@@ -3395,7 +3604,7 @@ function BoltTable({
|
|
|
3395
3604
|
return freshOrderedColumns;
|
|
3396
3605
|
return freshOrderedColumns.filter((col, idx) => {
|
|
3397
3606
|
if (col.pinned) return true;
|
|
3398
|
-
if (col.key === "__select__" || col.key === "__expand__") return true;
|
|
3607
|
+
if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return true;
|
|
3399
3608
|
return idx >= visibleColumnRange.start && idx <= visibleColumnRange.end;
|
|
3400
3609
|
});
|
|
3401
3610
|
}, [enableColumnVirtualization, visibleColumnRange, freshOrderedColumns]);
|
|
@@ -3542,8 +3751,11 @@ function BoltTable({
|
|
|
3542
3751
|
[data-bt-resize]:hover [data-bt-resize-line] {
|
|
3543
3752
|
opacity: 1 !important;
|
|
3544
3753
|
}
|
|
3754
|
+
[data-bt-ctx-item] {
|
|
3755
|
+
transition: background-color 0.15s ease;
|
|
3756
|
+
}
|
|
3545
3757
|
[data-bt-ctx-item]:not(:disabled):hover {
|
|
3546
|
-
background-color: rgba(128, 128, 128, 0.15);
|
|
3758
|
+
background-color: rgba(128, 128, 128, 0.15) !important;
|
|
3547
3759
|
}
|
|
3548
3760
|
[data-bt-header][data-dragging] {
|
|
3549
3761
|
opacity: 0.2 !important;
|
|
@@ -3552,6 +3764,17 @@ function BoltTable({
|
|
|
3552
3764
|
border: 1px dashed ${accentColor} !important;
|
|
3553
3765
|
}
|
|
3554
3766
|
${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
|
|
3767
|
+
[data-row-drag-over] {
|
|
3768
|
+
box-shadow: 0 -2px 0 0 ${accentColor} inset;
|
|
3769
|
+
}
|
|
3770
|
+
[data-bt-row-grip] {
|
|
3771
|
+
cursor: grab;
|
|
3772
|
+
opacity: 0.3;
|
|
3773
|
+
transition: opacity 0.15s;
|
|
3774
|
+
}
|
|
3775
|
+
[data-bt-row-grip]:hover {
|
|
3776
|
+
opacity: 0.8;
|
|
3777
|
+
}
|
|
3555
3778
|
@keyframes bt-spin { to { transform: rotate(360deg); } }
|
|
3556
3779
|
@keyframes bt-ai-shimmer {
|
|
3557
3780
|
0% { background-position: -200% 0; }
|
|
@@ -3707,7 +3930,7 @@ function BoltTable({
|
|
|
3707
3930
|
marginTop: 4
|
|
3708
3931
|
},
|
|
3709
3932
|
children: initialColumns.filter(
|
|
3710
|
-
(c) => c.key !== "__select__" && c.key !== "__expand__"
|
|
3933
|
+
(c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
|
|
3711
3934
|
).map((col) => {
|
|
3712
3935
|
const current = columns.find(
|
|
3713
3936
|
(c) => c.key === col.key
|
|
@@ -4036,14 +4259,15 @@ function BoltTable({
|
|
|
4036
4259
|
{
|
|
4037
4260
|
type: "button",
|
|
4038
4261
|
onClick: saveCurrentAIFilter,
|
|
4262
|
+
disabled: justSavedFilter,
|
|
4039
4263
|
style: {
|
|
4040
4264
|
display: "flex",
|
|
4041
4265
|
alignItems: "center",
|
|
4042
4266
|
gap: 4,
|
|
4043
|
-
background: `${accentColor}12`,
|
|
4044
|
-
border: `1px solid ${accentColor}30`,
|
|
4267
|
+
background: justSavedFilter ? `${accentColor}25` : `${accentColor}12`,
|
|
4268
|
+
border: `1px solid ${justSavedFilter ? accentColor : `${accentColor}30`}`,
|
|
4045
4269
|
borderRadius: 4,
|
|
4046
|
-
cursor: "pointer",
|
|
4270
|
+
cursor: justSavedFilter ? "default" : "pointer",
|
|
4047
4271
|
padding: "2px 8px",
|
|
4048
4272
|
color: accentColor,
|
|
4049
4273
|
fontSize: 11,
|
|
@@ -4051,14 +4275,14 @@ function BoltTable({
|
|
|
4051
4275
|
fontWeight: 500,
|
|
4052
4276
|
transition: "all 0.2s ease"
|
|
4053
4277
|
},
|
|
4054
|
-
title: "Save this filter for quick access later",
|
|
4278
|
+
title: justSavedFilter ? "Filter saved!" : "Save this filter for quick access later",
|
|
4055
4279
|
children: [
|
|
4056
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4280
|
+
justSavedFilter ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4057
4281
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
|
|
4058
4282
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "17 21 17 13 7 13 7 21" }),
|
|
4059
4283
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "7 3 7 8 15 8" })
|
|
4060
4284
|
] }),
|
|
4061
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Save Filter" })
|
|
4285
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: justSavedFilter ? "Saved" : "Save Filter" })
|
|
4062
4286
|
]
|
|
4063
4287
|
}
|
|
4064
4288
|
),
|
|
@@ -4165,7 +4389,7 @@ function BoltTable({
|
|
|
4165
4389
|
orderedColumns.map((column) => {
|
|
4166
4390
|
const isPinned = !!column.pinned;
|
|
4167
4391
|
const offset = columnOffsets.get(column.key);
|
|
4168
|
-
const isSystem = column.key === "__select__" || column.key === "__expand__";
|
|
4392
|
+
const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
|
|
4169
4393
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
4170
4394
|
"div",
|
|
4171
4395
|
{
|
|
@@ -4204,7 +4428,7 @@ function BoltTable({
|
|
|
4204
4428
|
children: orderedColumns.map((column, colIndex) => {
|
|
4205
4429
|
const isPinned = !!column.pinned;
|
|
4206
4430
|
const offset = columnOffsets.get(column.key);
|
|
4207
|
-
const isSystem = column.key === "__select__" || column.key === "__expand__";
|
|
4431
|
+
const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
|
|
4208
4432
|
const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
|
|
4209
4433
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
4210
4434
|
"div",
|
|
@@ -4414,7 +4638,7 @@ function BoltTable({
|
|
|
4414
4638
|
}),
|
|
4415
4639
|
(() => {
|
|
4416
4640
|
const firstDataColIndex = orderedColumns.findIndex(
|
|
4417
|
-
(c) => c.key !== "__select__" && c.key !== "__expand__"
|
|
4641
|
+
(c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
|
|
4418
4642
|
);
|
|
4419
4643
|
return orderedColumns.map((column, visualIndex) => {
|
|
4420
4644
|
const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
|
|
@@ -4491,6 +4715,39 @@ function BoltTable({
|
|
|
4491
4715
|
"__select__"
|
|
4492
4716
|
);
|
|
4493
4717
|
}
|
|
4718
|
+
if (column.key === "__drag__") {
|
|
4719
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
4720
|
+
"div",
|
|
4721
|
+
{
|
|
4722
|
+
"data-bt-header": "",
|
|
4723
|
+
"data-bt-pinned": "",
|
|
4724
|
+
className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
|
|
4725
|
+
style: {
|
|
4726
|
+
display: "flex",
|
|
4727
|
+
height: leafHeight,
|
|
4728
|
+
alignItems: "center",
|
|
4729
|
+
justifyContent: "center",
|
|
4730
|
+
overflow: "hidden",
|
|
4731
|
+
whiteSpace: "nowrap",
|
|
4732
|
+
boxSizing: "border-box",
|
|
4733
|
+
position: "sticky",
|
|
4734
|
+
left: columnOffsets.get("__drag__") ?? 0,
|
|
4735
|
+
top: 0,
|
|
4736
|
+
zIndex: 13,
|
|
4737
|
+
width: "36px",
|
|
4738
|
+
gridRow: leafGridRow,
|
|
4739
|
+
...styles.header,
|
|
4740
|
+
...styles.pinnedHeader,
|
|
4741
|
+
borderTop: "none",
|
|
4742
|
+
borderLeft: "none",
|
|
4743
|
+
borderBottom: "1px solid rgba(128,128,128,0.2)",
|
|
4744
|
+
borderRight: "1px solid rgba(128,128,128,0.2)"
|
|
4745
|
+
},
|
|
4746
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GripVerticalIcon, { style: { width: 12, height: 12, opacity: 0.4 } })
|
|
4747
|
+
},
|
|
4748
|
+
"__drag__"
|
|
4749
|
+
);
|
|
4750
|
+
}
|
|
4494
4751
|
if (column.key === "__expand__") {
|
|
4495
4752
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
4496
4753
|
"div",
|
|
@@ -4555,7 +4812,8 @@ function BoltTable({
|
|
|
4555
4812
|
disabledFilters,
|
|
4556
4813
|
headerGridRow: leafGridRow,
|
|
4557
4814
|
headerHeight: leafHeight,
|
|
4558
|
-
stickyTop: leafStickyTop
|
|
4815
|
+
stickyTop: leafStickyTop,
|
|
4816
|
+
onAutoFitColumn: handleAutoFitColumn
|
|
4559
4817
|
},
|
|
4560
4818
|
column.key
|
|
4561
4819
|
);
|
|
@@ -4640,7 +4898,8 @@ function BoltTable({
|
|
|
4640
4898
|
enableDynamicRowHeight,
|
|
4641
4899
|
onRowHeightChange: handleRowHeightChange,
|
|
4642
4900
|
columnGridIndexMap,
|
|
4643
|
-
cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
|
|
4901
|
+
cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0,
|
|
4902
|
+
onRowDragStart: rowDragEnabled && onRowReorder ? handleRowDragStart : void 0
|
|
4644
4903
|
}
|
|
4645
4904
|
)
|
|
4646
4905
|
]
|
|
@@ -4994,6 +5253,35 @@ function BoltTable({
|
|
|
4994
5253
|
),
|
|
4995
5254
|
document.body
|
|
4996
5255
|
),
|
|
5256
|
+
mounted && rowDragEnabled && onRowReorder && (0, import_react_dom2.createPortal)(
|
|
5257
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
5258
|
+
"div",
|
|
5259
|
+
{
|
|
5260
|
+
ref: rowDragGhostRef,
|
|
5261
|
+
style: {
|
|
5262
|
+
display: "none",
|
|
5263
|
+
position: "fixed",
|
|
5264
|
+
zIndex: 99999,
|
|
5265
|
+
height: 32,
|
|
5266
|
+
fontSize: 11,
|
|
5267
|
+
alignItems: "center",
|
|
5268
|
+
justifyContent: "center",
|
|
5269
|
+
padding: "0 12px",
|
|
5270
|
+
borderRadius: 6,
|
|
5271
|
+
border: `1px dashed ${accentColor}60`,
|
|
5272
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
5273
|
+
backdropFilter: "blur(16px)",
|
|
5274
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
5275
|
+
backgroundColor: "rgba(128,128,128,0.12)",
|
|
5276
|
+
cursor: "grabbing",
|
|
5277
|
+
pointerEvents: "none",
|
|
5278
|
+
fontWeight: 500,
|
|
5279
|
+
color: accentColor
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
),
|
|
5283
|
+
document.body
|
|
5284
|
+
),
|
|
4997
5285
|
cellContextMenu && mounted && (() => {
|
|
4998
5286
|
const menuCol = freshOrderedColumns.find(
|
|
4999
5287
|
(c) => c.key === cellContextMenu.columnKey
|
package/dist/index.mjs
CHANGED
|
@@ -149,7 +149,8 @@ var DraggableHeader = React.memo(
|
|
|
149
149
|
headerGridRow = 1,
|
|
150
150
|
headerHeight = 36,
|
|
151
151
|
stickyTop = 0,
|
|
152
|
-
isFirstColumn = false
|
|
152
|
+
isFirstColumn = false,
|
|
153
|
+
onAutoFitColumn
|
|
153
154
|
}) => {
|
|
154
155
|
const effectivelySortable = isColumnSortable(column);
|
|
155
156
|
const effectivelyFilterable = !disabledFilters && isColumnFilterable(column);
|
|
@@ -217,6 +218,12 @@ var DraggableHeader = React.memo(
|
|
|
217
218
|
if (column.pinned) return;
|
|
218
219
|
onResizeStart?.(column.key, e);
|
|
219
220
|
};
|
|
221
|
+
const handleResizeDoubleClick = (e) => {
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
if (column.pinned) return;
|
|
225
|
+
onAutoFitColumn?.(column.key);
|
|
226
|
+
};
|
|
220
227
|
const columnWidth = column.width ?? 150;
|
|
221
228
|
const widthPx = `${columnWidth}px`;
|
|
222
229
|
const isPinned = Boolean(column.pinned);
|
|
@@ -364,7 +371,8 @@ var DraggableHeader = React.memo(
|
|
|
364
371
|
padding: 0
|
|
365
372
|
},
|
|
366
373
|
onMouseDown: handleResizeStart,
|
|
367
|
-
|
|
374
|
+
onDoubleClick: handleResizeDoubleClick,
|
|
375
|
+
"aria-label": `Resize ${column.key} column (double-click to auto-fit)`,
|
|
368
376
|
children: /* @__PURE__ */ jsx2(
|
|
369
377
|
"div",
|
|
370
378
|
{
|
|
@@ -1461,7 +1469,8 @@ var TableBody = ({
|
|
|
1461
1469
|
enableDynamicRowHeight = false,
|
|
1462
1470
|
onRowHeightChange,
|
|
1463
1471
|
columnGridIndexMap,
|
|
1464
|
-
cellStyleFn
|
|
1472
|
+
cellStyleFn,
|
|
1473
|
+
onRowDragStart
|
|
1465
1474
|
}) => {
|
|
1466
1475
|
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
1467
1476
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
@@ -1543,6 +1552,7 @@ var TableBody = ({
|
|
|
1543
1552
|
"div",
|
|
1544
1553
|
{
|
|
1545
1554
|
"data-row-key": rowKey,
|
|
1555
|
+
"data-row-index": virtualRow.index,
|
|
1546
1556
|
"data-column-key": col.key,
|
|
1547
1557
|
"data-bt-cell": "",
|
|
1548
1558
|
"data-selected": isSelected || void 0,
|
|
@@ -1555,7 +1565,45 @@ var TableBody = ({
|
|
|
1555
1565
|
height: enableDynamicRowHeight ? void 0 : `${virtualRow.size}px`,
|
|
1556
1566
|
minHeight: enableDynamicRowHeight ? `${rowHeight}px` : void 0
|
|
1557
1567
|
},
|
|
1558
|
-
children:
|
|
1568
|
+
children: col.key === "__drag__" && onRowDragStart ? /* @__PURE__ */ jsx4(
|
|
1569
|
+
"div",
|
|
1570
|
+
{
|
|
1571
|
+
style: {
|
|
1572
|
+
height: `${rowHeight}px`,
|
|
1573
|
+
display: "flex",
|
|
1574
|
+
alignItems: "center",
|
|
1575
|
+
justifyContent: "center",
|
|
1576
|
+
borderBottom: "1px solid rgba(128,128,128,0.2)",
|
|
1577
|
+
...rowSty
|
|
1578
|
+
},
|
|
1579
|
+
children: /* @__PURE__ */ jsx4(
|
|
1580
|
+
"span",
|
|
1581
|
+
{
|
|
1582
|
+
"data-bt-row-grip": "",
|
|
1583
|
+
onPointerDown: (e) => {
|
|
1584
|
+
if (e.button !== 0) return;
|
|
1585
|
+
onRowDragStart(virtualRow.index, e);
|
|
1586
|
+
},
|
|
1587
|
+
style: {
|
|
1588
|
+
display: "flex",
|
|
1589
|
+
alignItems: "center",
|
|
1590
|
+
justifyContent: "center",
|
|
1591
|
+
touchAction: "none",
|
|
1592
|
+
width: "100%",
|
|
1593
|
+
height: "100%"
|
|
1594
|
+
},
|
|
1595
|
+
children: /* @__PURE__ */ jsxs4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1596
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "5", r: "1" }),
|
|
1597
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "12", r: "1" }),
|
|
1598
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "19", r: "1" }),
|
|
1599
|
+
/* @__PURE__ */ jsx4("circle", { cx: "15", cy: "5", r: "1" }),
|
|
1600
|
+
/* @__PURE__ */ jsx4("circle", { cx: "15", cy: "12", r: "1" }),
|
|
1601
|
+
/* @__PURE__ */ jsx4("circle", { cx: "15", cy: "19", r: "1" })
|
|
1602
|
+
] })
|
|
1603
|
+
}
|
|
1604
|
+
)
|
|
1605
|
+
}
|
|
1606
|
+
) : enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ jsx4(
|
|
1559
1607
|
DynamicRowMeasurer,
|
|
1560
1608
|
{
|
|
1561
1609
|
index: virtualRow.index,
|
|
@@ -2050,7 +2098,9 @@ function BoltTable({
|
|
|
2050
2098
|
onAIQuery,
|
|
2051
2099
|
onAIResponse,
|
|
2052
2100
|
aiPlaceholder = "Ask AI anything about your data...",
|
|
2053
|
-
aiButtonLabel
|
|
2101
|
+
aiButtonLabel,
|
|
2102
|
+
rowDragEnabled = false,
|
|
2103
|
+
onRowReorder
|
|
2054
2104
|
}) {
|
|
2055
2105
|
const data = useMemo2(() => {
|
|
2056
2106
|
if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
|
|
@@ -2247,6 +2297,7 @@ function BoltTable({
|
|
|
2247
2297
|
}
|
|
2248
2298
|
});
|
|
2249
2299
|
const [showSavedFilters, setShowSavedFilters] = useState3(false);
|
|
2300
|
+
const [justSavedFilter, setJustSavedFilter] = useState3(false);
|
|
2250
2301
|
const savedFiltersRef = useRef4(null);
|
|
2251
2302
|
React4.useEffect(() => {
|
|
2252
2303
|
if (!showSavedFilters) return;
|
|
@@ -2268,6 +2319,8 @@ function BoltTable({
|
|
|
2268
2319
|
localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
|
|
2269
2320
|
} catch {
|
|
2270
2321
|
}
|
|
2322
|
+
setJustSavedFilter(true);
|
|
2323
|
+
setTimeout(() => setJustSavedFilter(false), 1500);
|
|
2271
2324
|
}, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
|
|
2272
2325
|
const removeSavedFilter = useCallback2((index) => {
|
|
2273
2326
|
const next = savedAIFilters.filter((_, i) => i !== index);
|
|
@@ -2481,17 +2534,29 @@ function BoltTable({
|
|
|
2481
2534
|
}, []);
|
|
2482
2535
|
const getRowKey = useCallback2(
|
|
2483
2536
|
(record, index) => {
|
|
2484
|
-
if (record == null) return
|
|
2537
|
+
if (record == null) return `__row_${index}`;
|
|
2485
2538
|
try {
|
|
2486
|
-
if (typeof rowKey === "function")
|
|
2539
|
+
if (typeof rowKey === "function") {
|
|
2540
|
+
const result = rowKey(record);
|
|
2541
|
+
const str = String(result);
|
|
2542
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
|
|
2543
|
+
return `__row_${index}`;
|
|
2544
|
+
}
|
|
2545
|
+
return str;
|
|
2546
|
+
}
|
|
2487
2547
|
if (typeof rowKey === "string") {
|
|
2488
2548
|
const val = record[rowKey];
|
|
2489
|
-
|
|
2549
|
+
if (val == null) return `__row_${index}`;
|
|
2550
|
+
const str = String(val);
|
|
2551
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") {
|
|
2552
|
+
return `__row_${index}`;
|
|
2553
|
+
}
|
|
2554
|
+
return str;
|
|
2490
2555
|
}
|
|
2491
2556
|
} catch {
|
|
2492
|
-
return
|
|
2557
|
+
return `__row_${index}`;
|
|
2493
2558
|
}
|
|
2494
|
-
return
|
|
2559
|
+
return `__row_${index}`;
|
|
2495
2560
|
},
|
|
2496
2561
|
[rowKey]
|
|
2497
2562
|
);
|
|
@@ -2525,11 +2590,20 @@ function BoltTable({
|
|
|
2525
2590
|
(record, index) => {
|
|
2526
2591
|
if (record == null) return index;
|
|
2527
2592
|
try {
|
|
2528
|
-
if (typeof rowKey === "function")
|
|
2593
|
+
if (typeof rowKey === "function") {
|
|
2594
|
+
const result = rowKey(record);
|
|
2595
|
+
if (result == null || typeof result === "number" && Number.isNaN(result)) return index;
|
|
2596
|
+
const str = String(result);
|
|
2597
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
|
|
2598
|
+
return result;
|
|
2599
|
+
}
|
|
2529
2600
|
if (typeof rowKey === "string") {
|
|
2530
2601
|
const val = record[rowKey];
|
|
2602
|
+
if (val == null || typeof val === "number" && Number.isNaN(val)) return index;
|
|
2603
|
+
const str = String(val);
|
|
2604
|
+
if (str === "undefined" || str === "null" || str === "NaN" || str === "") return index;
|
|
2531
2605
|
if (typeof val === "number" || typeof val === "string") return val;
|
|
2532
|
-
return
|
|
2606
|
+
return str;
|
|
2533
2607
|
}
|
|
2534
2608
|
} catch {
|
|
2535
2609
|
return index;
|
|
@@ -2616,6 +2690,19 @@ function BoltTable({
|
|
|
2616
2690
|
};
|
|
2617
2691
|
return [selectionColumn, ...columnsWithExpand];
|
|
2618
2692
|
}, [rowSelection, columnsWithExpand]);
|
|
2693
|
+
const columnsWithDrag = useMemo2(() => {
|
|
2694
|
+
if (!rowDragEnabled || !onRowReorder) return columnsWithSelection;
|
|
2695
|
+
const dragColumn = {
|
|
2696
|
+
key: "__drag__",
|
|
2697
|
+
dataIndex: "__drag__",
|
|
2698
|
+
title: "",
|
|
2699
|
+
width: 36,
|
|
2700
|
+
pinned: "left",
|
|
2701
|
+
hidden: false,
|
|
2702
|
+
render: () => null
|
|
2703
|
+
};
|
|
2704
|
+
return [dragColumn, ...columnsWithSelection];
|
|
2705
|
+
}, [rowDragEnabled, onRowReorder, columnsWithSelection]);
|
|
2619
2706
|
const resizeOverlayRef = useRef4(null);
|
|
2620
2707
|
const tableAreaRef = useRef4(null);
|
|
2621
2708
|
const [scrollAreaWidth, setScrollAreaWidth] = useState3(0);
|
|
@@ -2698,7 +2785,7 @@ function BoltTable({
|
|
|
2698
2785
|
onColumnOrderChangeRef.current = onColumnOrderChange;
|
|
2699
2786
|
const handleColumnDragStart = useCallback2(
|
|
2700
2787
|
(columnKey, e) => {
|
|
2701
|
-
if (columnKey === "__select__" || columnKey === "__expand__") return;
|
|
2788
|
+
if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
|
|
2702
2789
|
const headerEl = e.currentTarget.closest(
|
|
2703
2790
|
"[data-column-key]"
|
|
2704
2791
|
);
|
|
@@ -2733,7 +2820,7 @@ function BoltTable({
|
|
|
2733
2820
|
let newOverId = null;
|
|
2734
2821
|
headers.forEach((h) => {
|
|
2735
2822
|
const key = h.dataset.columnKey;
|
|
2736
|
-
if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
|
|
2823
|
+
if (!key || key === "__select__" || key === "__expand__" || key === "__drag__" || key === columnKey) {
|
|
2737
2824
|
h.removeAttribute("data-drag-over");
|
|
2738
2825
|
return;
|
|
2739
2826
|
}
|
|
@@ -2785,16 +2872,97 @@ function BoltTable({
|
|
|
2785
2872
|
},
|
|
2786
2873
|
[]
|
|
2787
2874
|
);
|
|
2875
|
+
const rowDragGhostRef = useRef4(null);
|
|
2876
|
+
const rowDragFromRef = useRef4(null);
|
|
2877
|
+
const rowDragOverRef = useRef4(null);
|
|
2878
|
+
const onRowReorderRef = useRef4(onRowReorder);
|
|
2879
|
+
onRowReorderRef.current = onRowReorder;
|
|
2880
|
+
const handleRowDragStart = useCallback2(
|
|
2881
|
+
(rowIndex, e) => {
|
|
2882
|
+
if (!onRowReorderRef.current) return;
|
|
2883
|
+
e.preventDefault();
|
|
2884
|
+
rowDragFromRef.current = rowIndex;
|
|
2885
|
+
rowDragOverRef.current = null;
|
|
2886
|
+
const target = e.currentTarget.closest("[data-row-key]");
|
|
2887
|
+
const rowHeight2 = target?.offsetHeight ?? 40;
|
|
2888
|
+
const ghost = rowDragGhostRef.current;
|
|
2889
|
+
if (ghost) {
|
|
2890
|
+
ghost.textContent = `Row ${rowIndex + 1}`;
|
|
2891
|
+
ghost.style.display = "flex";
|
|
2892
|
+
ghost.style.left = `${e.clientX + 12}px`;
|
|
2893
|
+
ghost.style.top = `${e.clientY - 12}px`;
|
|
2894
|
+
ghost.style.height = `${Math.min(rowHeight2, 36)}px`;
|
|
2895
|
+
}
|
|
2896
|
+
const grabStyle = document.createElement("style");
|
|
2897
|
+
grabStyle.textContent = "* { cursor: grabbing !important; }";
|
|
2898
|
+
document.head.appendChild(grabStyle);
|
|
2899
|
+
const scrollEl = tableAreaRef.current;
|
|
2900
|
+
const onMove = (ev) => {
|
|
2901
|
+
if (ghost) {
|
|
2902
|
+
ghost.style.left = `${ev.clientX + 12}px`;
|
|
2903
|
+
ghost.style.top = `${ev.clientY - 12}px`;
|
|
2904
|
+
}
|
|
2905
|
+
if (!scrollEl) return;
|
|
2906
|
+
const cells = scrollEl.querySelectorAll("[data-bt-cell][data-row-key]");
|
|
2907
|
+
let closestIdx = null;
|
|
2908
|
+
let closestDist = Infinity;
|
|
2909
|
+
cells.forEach((cell) => {
|
|
2910
|
+
const rk = cell.dataset.rowKey;
|
|
2911
|
+
if (!rk) return;
|
|
2912
|
+
const rect = cell.getBoundingClientRect();
|
|
2913
|
+
const midY = rect.top + rect.height / 2;
|
|
2914
|
+
const dist = Math.abs(ev.clientY - midY);
|
|
2915
|
+
if (dist < closestDist) {
|
|
2916
|
+
closestDist = dist;
|
|
2917
|
+
const idxAttr = cell.dataset.rowIndex;
|
|
2918
|
+
if (idxAttr != null) closestIdx = Number(idxAttr);
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
|
|
2922
|
+
(el) => el.removeAttribute("data-row-drag-over")
|
|
2923
|
+
);
|
|
2924
|
+
if (closestIdx != null && closestIdx !== rowDragFromRef.current) {
|
|
2925
|
+
rowDragOverRef.current = closestIdx;
|
|
2926
|
+
scrollEl.querySelectorAll(
|
|
2927
|
+
`[data-bt-cell][data-row-index="${closestIdx}"]`
|
|
2928
|
+
).forEach((el) => el.setAttribute("data-row-drag-over", ""));
|
|
2929
|
+
} else {
|
|
2930
|
+
rowDragOverRef.current = null;
|
|
2931
|
+
}
|
|
2932
|
+
};
|
|
2933
|
+
const onUp = () => {
|
|
2934
|
+
document.removeEventListener("pointermove", onMove);
|
|
2935
|
+
document.removeEventListener("pointerup", onUp);
|
|
2936
|
+
grabStyle.remove();
|
|
2937
|
+
if (ghost) ghost.style.display = "none";
|
|
2938
|
+
if (scrollEl) {
|
|
2939
|
+
scrollEl.querySelectorAll("[data-row-drag-over]").forEach(
|
|
2940
|
+
(el) => el.removeAttribute("data-row-drag-over")
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
const from = rowDragFromRef.current;
|
|
2944
|
+
const to = rowDragOverRef.current;
|
|
2945
|
+
if (from != null && to != null && from !== to) {
|
|
2946
|
+
onRowReorderRef.current?.(from, to);
|
|
2947
|
+
}
|
|
2948
|
+
rowDragFromRef.current = null;
|
|
2949
|
+
rowDragOverRef.current = null;
|
|
2950
|
+
};
|
|
2951
|
+
document.addEventListener("pointermove", onMove);
|
|
2952
|
+
document.addEventListener("pointerup", onUp);
|
|
2953
|
+
},
|
|
2954
|
+
[]
|
|
2955
|
+
);
|
|
2788
2956
|
const handleResizeStart = (columnKey, e) => {
|
|
2789
2957
|
e.preventDefault();
|
|
2790
2958
|
e.stopPropagation();
|
|
2791
|
-
if (columnKey === "__select__" || columnKey === "__expand__") return;
|
|
2792
|
-
const columnIndex =
|
|
2959
|
+
if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
|
|
2960
|
+
const columnIndex = columnsWithDrag.findIndex(
|
|
2793
2961
|
(col) => col.key === columnKey
|
|
2794
2962
|
);
|
|
2795
2963
|
if (columnIndex === -1) return;
|
|
2796
|
-
if (
|
|
2797
|
-
const column =
|
|
2964
|
+
if (columnsWithDrag[columnIndex].pinned) return;
|
|
2965
|
+
const column = columnsWithDrag[columnIndex];
|
|
2798
2966
|
const startWidth = column.width ?? 150;
|
|
2799
2967
|
resizeStateRef.current = {
|
|
2800
2968
|
columnKey,
|
|
@@ -2851,9 +3019,50 @@ function BoltTable({
|
|
|
2851
3019
|
});
|
|
2852
3020
|
onColumnResize?.(columnKey, finalWidth);
|
|
2853
3021
|
}, [onColumnResize]);
|
|
3022
|
+
const handleAutoFitColumn = useCallback2((columnKey) => {
|
|
3023
|
+
const scrollEl = tableAreaRef.current;
|
|
3024
|
+
if (!scrollEl) return;
|
|
3025
|
+
const col = columnsWithDrag.find((c) => c.key === columnKey);
|
|
3026
|
+
if (!col) return;
|
|
3027
|
+
const headerEl = scrollEl.querySelector(
|
|
3028
|
+
`[data-column-key="${columnKey}"] [data-bt-grip]`
|
|
3029
|
+
)?.parentElement ?? scrollEl.querySelector(`[data-column-key="${columnKey}"]`);
|
|
3030
|
+
let maxWidth = 0;
|
|
3031
|
+
if (headerEl) {
|
|
3032
|
+
const title = typeof col.title === "string" ? col.title : col.key;
|
|
3033
|
+
const canvas = document.createElement("canvas");
|
|
3034
|
+
const ctx = canvas.getContext("2d");
|
|
3035
|
+
if (ctx) {
|
|
3036
|
+
const computedStyle = window.getComputedStyle(headerEl);
|
|
3037
|
+
ctx.font = `${computedStyle.fontWeight} ${computedStyle.fontSize} ${computedStyle.fontFamily}`;
|
|
3038
|
+
maxWidth = ctx.measureText(title).width + 60;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
const cells = scrollEl.querySelectorAll(
|
|
3042
|
+
`[data-bt-cell][data-column-key="${columnKey}"]`
|
|
3043
|
+
);
|
|
3044
|
+
cells.forEach((cell) => {
|
|
3045
|
+
const inner = cell.querySelector("div > div");
|
|
3046
|
+
if (inner) {
|
|
3047
|
+
const scrollW = inner.scrollWidth;
|
|
3048
|
+
if (scrollW > maxWidth) maxWidth = scrollW;
|
|
3049
|
+
}
|
|
3050
|
+
});
|
|
3051
|
+
const finalWidth = Math.max(60, Math.min(Math.ceil(maxWidth) + 24, 800));
|
|
3052
|
+
manuallyResizedRef.current.add(columnKey);
|
|
3053
|
+
React4.startTransition(() => {
|
|
3054
|
+
setColumnWidths((prev) => {
|
|
3055
|
+
const next = new Map(prev);
|
|
3056
|
+
next.set(columnKey, finalWidth);
|
|
3057
|
+
return next;
|
|
3058
|
+
});
|
|
3059
|
+
});
|
|
3060
|
+
onColumnResize?.(columnKey, finalWidth);
|
|
3061
|
+
}, [columnsWithDrag, onColumnResize]);
|
|
2854
3062
|
const { leftPinned, unpinned, rightPinned } = useMemo2(() => {
|
|
2855
|
-
const columnMap = new Map(
|
|
3063
|
+
const columnMap = new Map(columnsWithDrag.map((c) => [c.key, c]));
|
|
2856
3064
|
const systemKeys = [
|
|
3065
|
+
...rowDragEnabled && onRowReorder ? ["__drag__"] : [],
|
|
2857
3066
|
...rowSelection ? ["__select__"] : [],
|
|
2858
3067
|
...expandable ? ["__expand__"] : []
|
|
2859
3068
|
];
|
|
@@ -2865,7 +3074,7 @@ function BoltTable({
|
|
|
2865
3074
|
else center.push(col);
|
|
2866
3075
|
});
|
|
2867
3076
|
return { leftPinned: left, unpinned: center, rightPinned: right };
|
|
2868
|
-
}, [columnOrder,
|
|
3077
|
+
}, [columnOrder, columnsWithDrag, rowSelection, expandable, rowDragEnabled, onRowReorder]);
|
|
2869
3078
|
const orderedColumns = useMemo2(
|
|
2870
3079
|
() => [...leftPinned, ...unpinned, ...rightPinned],
|
|
2871
3080
|
[leftPinned, unpinned, rightPinned]
|
|
@@ -2873,7 +3082,7 @@ function BoltTable({
|
|
|
2873
3082
|
const freshOrderedColumns = useMemo2(() => {
|
|
2874
3083
|
const latestMap = new Map(initialColumnsRef.current.map((c) => [c.key, c]));
|
|
2875
3084
|
return orderedColumns.map((col) => {
|
|
2876
|
-
if (col.key === "__select__" || col.key === "__expand__") return col;
|
|
3085
|
+
if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return col;
|
|
2877
3086
|
const latest = latestMap.get(col.key);
|
|
2878
3087
|
if (!latest) return col;
|
|
2879
3088
|
return {
|
|
@@ -3366,7 +3575,7 @@ function BoltTable({
|
|
|
3366
3575
|
return freshOrderedColumns;
|
|
3367
3576
|
return freshOrderedColumns.filter((col, idx) => {
|
|
3368
3577
|
if (col.pinned) return true;
|
|
3369
|
-
if (col.key === "__select__" || col.key === "__expand__") return true;
|
|
3578
|
+
if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return true;
|
|
3370
3579
|
return idx >= visibleColumnRange.start && idx <= visibleColumnRange.end;
|
|
3371
3580
|
});
|
|
3372
3581
|
}, [enableColumnVirtualization, visibleColumnRange, freshOrderedColumns]);
|
|
@@ -3513,8 +3722,11 @@ function BoltTable({
|
|
|
3513
3722
|
[data-bt-resize]:hover [data-bt-resize-line] {
|
|
3514
3723
|
opacity: 1 !important;
|
|
3515
3724
|
}
|
|
3725
|
+
[data-bt-ctx-item] {
|
|
3726
|
+
transition: background-color 0.15s ease;
|
|
3727
|
+
}
|
|
3516
3728
|
[data-bt-ctx-item]:not(:disabled):hover {
|
|
3517
|
-
background-color: rgba(128, 128, 128, 0.15);
|
|
3729
|
+
background-color: rgba(128, 128, 128, 0.15) !important;
|
|
3518
3730
|
}
|
|
3519
3731
|
[data-bt-header][data-dragging] {
|
|
3520
3732
|
opacity: 0.2 !important;
|
|
@@ -3523,6 +3735,17 @@ function BoltTable({
|
|
|
3523
3735
|
border: 1px dashed ${accentColor} !important;
|
|
3524
3736
|
}
|
|
3525
3737
|
${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
|
|
3738
|
+
[data-row-drag-over] {
|
|
3739
|
+
box-shadow: 0 -2px 0 0 ${accentColor} inset;
|
|
3740
|
+
}
|
|
3741
|
+
[data-bt-row-grip] {
|
|
3742
|
+
cursor: grab;
|
|
3743
|
+
opacity: 0.3;
|
|
3744
|
+
transition: opacity 0.15s;
|
|
3745
|
+
}
|
|
3746
|
+
[data-bt-row-grip]:hover {
|
|
3747
|
+
opacity: 0.8;
|
|
3748
|
+
}
|
|
3526
3749
|
@keyframes bt-spin { to { transform: rotate(360deg); } }
|
|
3527
3750
|
@keyframes bt-ai-shimmer {
|
|
3528
3751
|
0% { background-position: -200% 0; }
|
|
@@ -3678,7 +3901,7 @@ function BoltTable({
|
|
|
3678
3901
|
marginTop: 4
|
|
3679
3902
|
},
|
|
3680
3903
|
children: initialColumns.filter(
|
|
3681
|
-
(c) => c.key !== "__select__" && c.key !== "__expand__"
|
|
3904
|
+
(c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
|
|
3682
3905
|
).map((col) => {
|
|
3683
3906
|
const current = columns.find(
|
|
3684
3907
|
(c) => c.key === col.key
|
|
@@ -4007,14 +4230,15 @@ function BoltTable({
|
|
|
4007
4230
|
{
|
|
4008
4231
|
type: "button",
|
|
4009
4232
|
onClick: saveCurrentAIFilter,
|
|
4233
|
+
disabled: justSavedFilter,
|
|
4010
4234
|
style: {
|
|
4011
4235
|
display: "flex",
|
|
4012
4236
|
alignItems: "center",
|
|
4013
4237
|
gap: 4,
|
|
4014
|
-
background: `${accentColor}12`,
|
|
4015
|
-
border: `1px solid ${accentColor}30`,
|
|
4238
|
+
background: justSavedFilter ? `${accentColor}25` : `${accentColor}12`,
|
|
4239
|
+
border: `1px solid ${justSavedFilter ? accentColor : `${accentColor}30`}`,
|
|
4016
4240
|
borderRadius: 4,
|
|
4017
|
-
cursor: "pointer",
|
|
4241
|
+
cursor: justSavedFilter ? "default" : "pointer",
|
|
4018
4242
|
padding: "2px 8px",
|
|
4019
4243
|
color: accentColor,
|
|
4020
4244
|
fontSize: 11,
|
|
@@ -4022,14 +4246,14 @@ function BoltTable({
|
|
|
4022
4246
|
fontWeight: 500,
|
|
4023
4247
|
transition: "all 0.2s ease"
|
|
4024
4248
|
},
|
|
4025
|
-
title: "Save this filter for quick access later",
|
|
4249
|
+
title: justSavedFilter ? "Filter saved!" : "Save this filter for quick access later",
|
|
4026
4250
|
children: [
|
|
4027
|
-
/* @__PURE__ */ jsxs5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4251
|
+
justSavedFilter ? /* @__PURE__ */ jsx5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4028
4252
|
/* @__PURE__ */ jsx5("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
|
|
4029
4253
|
/* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
|
|
4030
4254
|
/* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
|
|
4031
4255
|
] }),
|
|
4032
|
-
/* @__PURE__ */ jsx5("span", { children: "Save Filter" })
|
|
4256
|
+
/* @__PURE__ */ jsx5("span", { children: justSavedFilter ? "Saved" : "Save Filter" })
|
|
4033
4257
|
]
|
|
4034
4258
|
}
|
|
4035
4259
|
),
|
|
@@ -4136,7 +4360,7 @@ function BoltTable({
|
|
|
4136
4360
|
orderedColumns.map((column) => {
|
|
4137
4361
|
const isPinned = !!column.pinned;
|
|
4138
4362
|
const offset = columnOffsets.get(column.key);
|
|
4139
|
-
const isSystem = column.key === "__select__" || column.key === "__expand__";
|
|
4363
|
+
const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
|
|
4140
4364
|
return /* @__PURE__ */ jsx5(
|
|
4141
4365
|
"div",
|
|
4142
4366
|
{
|
|
@@ -4175,7 +4399,7 @@ function BoltTable({
|
|
|
4175
4399
|
children: orderedColumns.map((column, colIndex) => {
|
|
4176
4400
|
const isPinned = !!column.pinned;
|
|
4177
4401
|
const offset = columnOffsets.get(column.key);
|
|
4178
|
-
const isSystem = column.key === "__select__" || column.key === "__expand__";
|
|
4402
|
+
const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
|
|
4179
4403
|
const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
|
|
4180
4404
|
return /* @__PURE__ */ jsx5(
|
|
4181
4405
|
"div",
|
|
@@ -4385,7 +4609,7 @@ function BoltTable({
|
|
|
4385
4609
|
}),
|
|
4386
4610
|
(() => {
|
|
4387
4611
|
const firstDataColIndex = orderedColumns.findIndex(
|
|
4388
|
-
(c) => c.key !== "__select__" && c.key !== "__expand__"
|
|
4612
|
+
(c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
|
|
4389
4613
|
);
|
|
4390
4614
|
return orderedColumns.map((column, visualIndex) => {
|
|
4391
4615
|
const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
|
|
@@ -4462,6 +4686,39 @@ function BoltTable({
|
|
|
4462
4686
|
"__select__"
|
|
4463
4687
|
);
|
|
4464
4688
|
}
|
|
4689
|
+
if (column.key === "__drag__") {
|
|
4690
|
+
return /* @__PURE__ */ jsx5(
|
|
4691
|
+
"div",
|
|
4692
|
+
{
|
|
4693
|
+
"data-bt-header": "",
|
|
4694
|
+
"data-bt-pinned": "",
|
|
4695
|
+
className: `${classNames.header ?? ""} ${classNames.pinnedHeader ?? ""}`,
|
|
4696
|
+
style: {
|
|
4697
|
+
display: "flex",
|
|
4698
|
+
height: leafHeight,
|
|
4699
|
+
alignItems: "center",
|
|
4700
|
+
justifyContent: "center",
|
|
4701
|
+
overflow: "hidden",
|
|
4702
|
+
whiteSpace: "nowrap",
|
|
4703
|
+
boxSizing: "border-box",
|
|
4704
|
+
position: "sticky",
|
|
4705
|
+
left: columnOffsets.get("__drag__") ?? 0,
|
|
4706
|
+
top: 0,
|
|
4707
|
+
zIndex: 13,
|
|
4708
|
+
width: "36px",
|
|
4709
|
+
gridRow: leafGridRow,
|
|
4710
|
+
...styles.header,
|
|
4711
|
+
...styles.pinnedHeader,
|
|
4712
|
+
borderTop: "none",
|
|
4713
|
+
borderLeft: "none",
|
|
4714
|
+
borderBottom: "1px solid rgba(128,128,128,0.2)",
|
|
4715
|
+
borderRight: "1px solid rgba(128,128,128,0.2)"
|
|
4716
|
+
},
|
|
4717
|
+
children: /* @__PURE__ */ jsx5(GripVerticalIcon, { style: { width: 12, height: 12, opacity: 0.4 } })
|
|
4718
|
+
},
|
|
4719
|
+
"__drag__"
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4465
4722
|
if (column.key === "__expand__") {
|
|
4466
4723
|
return /* @__PURE__ */ jsx5(
|
|
4467
4724
|
"div",
|
|
@@ -4526,7 +4783,8 @@ function BoltTable({
|
|
|
4526
4783
|
disabledFilters,
|
|
4527
4784
|
headerGridRow: leafGridRow,
|
|
4528
4785
|
headerHeight: leafHeight,
|
|
4529
|
-
stickyTop: leafStickyTop
|
|
4786
|
+
stickyTop: leafStickyTop,
|
|
4787
|
+
onAutoFitColumn: handleAutoFitColumn
|
|
4530
4788
|
},
|
|
4531
4789
|
column.key
|
|
4532
4790
|
);
|
|
@@ -4611,7 +4869,8 @@ function BoltTable({
|
|
|
4611
4869
|
enableDynamicRowHeight,
|
|
4612
4870
|
onRowHeightChange: handleRowHeightChange,
|
|
4613
4871
|
columnGridIndexMap,
|
|
4614
|
-
cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
|
|
4872
|
+
cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0,
|
|
4873
|
+
onRowDragStart: rowDragEnabled && onRowReorder ? handleRowDragStart : void 0
|
|
4615
4874
|
}
|
|
4616
4875
|
)
|
|
4617
4876
|
]
|
|
@@ -4965,6 +5224,35 @@ function BoltTable({
|
|
|
4965
5224
|
),
|
|
4966
5225
|
document.body
|
|
4967
5226
|
),
|
|
5227
|
+
mounted && rowDragEnabled && onRowReorder && createPortal2(
|
|
5228
|
+
/* @__PURE__ */ jsx5(
|
|
5229
|
+
"div",
|
|
5230
|
+
{
|
|
5231
|
+
ref: rowDragGhostRef,
|
|
5232
|
+
style: {
|
|
5233
|
+
display: "none",
|
|
5234
|
+
position: "fixed",
|
|
5235
|
+
zIndex: 99999,
|
|
5236
|
+
height: 32,
|
|
5237
|
+
fontSize: 11,
|
|
5238
|
+
alignItems: "center",
|
|
5239
|
+
justifyContent: "center",
|
|
5240
|
+
padding: "0 12px",
|
|
5241
|
+
borderRadius: 6,
|
|
5242
|
+
border: `1px dashed ${accentColor}60`,
|
|
5243
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
5244
|
+
backdropFilter: "blur(16px)",
|
|
5245
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
5246
|
+
backgroundColor: "rgba(128,128,128,0.12)",
|
|
5247
|
+
cursor: "grabbing",
|
|
5248
|
+
pointerEvents: "none",
|
|
5249
|
+
fontWeight: 500,
|
|
5250
|
+
color: accentColor
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
),
|
|
5254
|
+
document.body
|
|
5255
|
+
),
|
|
4968
5256
|
cellContextMenu && mounted && (() => {
|
|
4969
5257
|
const menuCol = freshOrderedColumns.find(
|
|
4970
5258
|
(c) => c.key === cellContextMenu.columnKey
|
package/package.json
CHANGED