bolt-table 0.1.37 → 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.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
- "aria-label": `Resize ${column.key} column`,
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
  {
@@ -676,66 +684,68 @@ function detectColumnType(key, data) {
676
684
  }
677
685
  return { type: "string", sample: sampleStr };
678
686
  }
687
+ var cachedSchema = null;
688
+ function buildSchemaFingerprint(columns, dataLen) {
689
+ return columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => c.key).join(",") + `:${dataLen}`;
690
+ }
679
691
  function buildSystemPrompt(columns, data) {
680
- const schemaLines = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => {
692
+ const fingerprint = buildSchemaFingerprint(columns, data.length);
693
+ if (cachedSchema?.fingerprint === fingerprint) {
694
+ return cachedSchema.prompt;
695
+ }
696
+ const cols = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__");
697
+ const schema = cols.map((c) => {
681
698
  const key = c.dataIndex ?? c.key;
682
699
  const title = typeof c.title === "string" ? c.title : c.key;
683
700
  const info = detectColumnType(key, data);
684
- return ` - key: "${c.key}", title: "${title}", dataIndex: "${key}", type: ${info.type}${info.sample ? ` (values: ${info.sample})` : ""}`;
701
+ const w = c.width ?? 150;
702
+ const pin = c.pinned ? `, pinned: "${c.pinned}"` : "";
703
+ const hidden = c.hidden ? ", hidden: true" : "";
704
+ const vals = info.sample ? "|vals: " + info.sample : "";
705
+ return ` ${c.key}|${key}|"${title}"|${info.type}|w:${w}${pin}${hidden}${vals}`;
685
706
  }).join("\n");
686
- const sample = data.slice(0, 5).map((row) => {
707
+ const sample = data.slice(0, 3).map((row) => {
687
708
  const obj = {};
688
- for (const col of columns) {
689
- if (col.key === "__select__" || col.key === "__expand__") continue;
709
+ for (const col of cols) {
690
710
  const di = col.dataIndex ?? col.key;
691
711
  obj[di] = row[di];
692
712
  }
693
713
  return obj;
694
714
  });
695
- return `You are a data table assistant. You help users query, filter, sort, and style tabular data.
696
- You MUST respond with ONLY a valid JSON object \u2014 no markdown fences, no explanation, no extra text.
697
-
698
- ## Table Schema
699
- ${schemaLines}
700
-
701
- ## Sample Data (first ${sample.length} of ${data.length} rows)
702
- ${JSON.stringify(sample, null, 2)}
703
-
704
- ## Available Operations
705
- Combine any of these in a single response:
715
+ const prompt = `Data table AI. Respond ONLY with valid JSON, no markdown/explanation.
706
716
 
707
- 1. **filter** \u2014 show only rows matching conditions
708
- { "type": "filter", "conditions": [{ "column": "<dataIndex>", "op": "<op>", "value": <val> }], "logic": "and" | "or" }
717
+ SCHEMA (key|dataIndex|title|type|width|flags|sample):
718
+ ${schema}
709
719
 
710
- 2. **rowStyle** \u2014 apply CSS styles to entire rows matching conditions
711
- { "type": "rowStyle", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
720
+ SAMPLE (${sample.length}/${data.length} rows):
721
+ ${JSON.stringify(sample)}
712
722
 
713
- 3. **cellStyle** \u2014 apply CSS styles to a specific column's cells matching conditions
714
- { "type": "cellStyle", "column": "<dataIndex>", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
723
+ COLUMN ORDER: [${cols.map((c) => `"${c.key}"`).join(",")}]
715
724
 
716
- 4. **sort** \u2014 sort data by a column
717
- { "type": "sort", "column": "<dataIndex>", "direction": "asc" | "desc" }
725
+ OPS (combine any):
726
+ filter: {type:"filter",conditions:[{column:"<dataIndex>",op:"<op>",value:<v>}],logic:"and"|"or"}
727
+ sort: {type:"sort",column:"<dataIndex>",direction:"asc"|"desc"}
728
+ rowStyle: {type:"rowStyle",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
729
+ cellStyle: {type:"cellStyle",column:"<dataIndex>",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
730
+ hideColumns: {type:"hideColumns",columns:["key",...]}
731
+ showColumns: {type:"showColumns",columns:["key",...]}
732
+ resizeColumn: {type:"resizeColumn",column:"<key>",width:<px>}
733
+ reorderColumns: {type:"reorderColumns",order:["key1","key2",...]} (full column order)
734
+ pinColumn: {type:"pinColumn",column:"<key>",pinned:"left"|"right"|false}
735
+ setPage: {type:"setPage",page:<number>}
718
736
 
719
- 5. **hideColumns** / **showColumns** \u2014 toggle column visibility
720
- { "type": "hideColumns" | "showColumns", "columns": ["<key>", ...] }
737
+ OPS: eq,neq,gt,gte,lt,lte,contains,notContains,startsWith,endsWith,in,notIn
721
738
 
722
- ## Operators
723
- eq, neq, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn
739
+ FORMAT: {"operations":[...],"message":"brief description"}
724
740
 
725
- ## Response format
726
- {
727
- "operations": [ ... ],
728
- "message": "Brief user-friendly description of what was applied"
729
- }
730
-
731
- ## Rules
732
- - Use the dataIndex values from the schema, not display titles.
733
- - For colors use semi-transparent values like "rgba(255,0,0,0.15)" so text stays readable.
734
- - CSS property names must be camelCase (e.g. "backgroundColor", "color", "fontWeight").
735
- - If the user asks to highlight / color / mark specific rows, use rowStyle or cellStyle.
736
- - If the user asks to show only certain rows, use filter.
737
- - You can combine filter + rowStyle + sort etc. in one response.
738
- - The "message" should be concise: what was done, in plain English.`;
741
+ RULES:
742
+ - Use dataIndex for data ops, key for column ops (hide/show/resize/reorder/pin).
743
+ - Colors: semi-transparent rgba. CSS props: camelCase.
744
+ - reorderColumns: provide FULL ordered array of ALL visible column keys.
745
+ - resizeColumn width: integer pixels (min 40, max 800).
746
+ - Combine multiple ops freely. Message: concise plain English.`;
747
+ cachedSchema = { fingerprint, prompt };
748
+ return prompt;
739
749
  }
740
750
  async function callAI(config, systemPrompt, userQuery) {
741
751
  const { provider, apiKey, model, baseUrl, maxTokens = 1024, temperature = 0.1 } = config;
@@ -901,6 +911,10 @@ function applyAIOperations(data, operations) {
901
911
  const cellStyleOps = [];
902
912
  const hideColumns = [];
903
913
  const showColumns = [];
914
+ const resizeOps = [];
915
+ let reorderOp = null;
916
+ const pinOps = [];
917
+ let setPageOp = null;
904
918
  for (const op of operations) {
905
919
  switch (op.type) {
906
920
  case "filter":
@@ -921,12 +935,24 @@ function applyAIOperations(data, operations) {
921
935
  case "showColumns":
922
936
  showColumns.push(...op.columns);
923
937
  break;
938
+ case "resizeColumn":
939
+ resizeOps.push(op);
940
+ break;
941
+ case "reorderColumns":
942
+ reorderOp = op;
943
+ break;
944
+ case "pinColumn":
945
+ pinOps.push(op);
946
+ break;
947
+ case "setPage":
948
+ setPageOp = op;
949
+ break;
924
950
  }
925
951
  }
926
952
  if (sortOp) {
927
953
  filteredData = applyAISort(filteredData, sortOp);
928
954
  }
929
- return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns };
955
+ return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns, resizeOps, reorderOp, pinOps, setPageOp };
930
956
  }
931
957
 
932
958
  // src/ResizeOverlay.tsx
@@ -1443,7 +1469,8 @@ var TableBody = ({
1443
1469
  enableDynamicRowHeight = false,
1444
1470
  onRowHeightChange,
1445
1471
  columnGridIndexMap,
1446
- cellStyleFn
1472
+ cellStyleFn,
1473
+ onRowDragStart
1447
1474
  }) => {
1448
1475
  const virtualItems = rowVirtualizer.getVirtualItems();
1449
1476
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1525,6 +1552,7 @@ var TableBody = ({
1525
1552
  "div",
1526
1553
  {
1527
1554
  "data-row-key": rowKey,
1555
+ "data-row-index": virtualRow.index,
1528
1556
  "data-column-key": col.key,
1529
1557
  "data-bt-cell": "",
1530
1558
  "data-selected": isSelected || void 0,
@@ -1537,7 +1565,45 @@ var TableBody = ({
1537
1565
  height: enableDynamicRowHeight ? void 0 : `${virtualRow.size}px`,
1538
1566
  minHeight: enableDynamicRowHeight ? `${rowHeight}px` : void 0
1539
1567
  },
1540
- children: enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ jsx4(
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(
1541
1607
  DynamicRowMeasurer,
1542
1608
  {
1543
1609
  index: virtualRow.index,
@@ -2032,7 +2098,9 @@ function BoltTable({
2032
2098
  onAIQuery,
2033
2099
  onAIResponse,
2034
2100
  aiPlaceholder = "Ask AI anything about your data...",
2035
- aiButtonLabel
2101
+ aiButtonLabel,
2102
+ rowDragEnabled = false,
2103
+ onRowReorder
2036
2104
  }) {
2037
2105
  const data = useMemo2(() => {
2038
2106
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -2219,6 +2287,104 @@ function BoltTable({
2219
2287
  const [aiFilteredDataKeys, setAiFilteredDataKeys] = useState3(null);
2220
2288
  const [aiSortKey, setAiSortKey] = useState3(null);
2221
2289
  const [aiSortDir, setAiSortDir] = useState3(null);
2290
+ const aiFiltersStorageKey = columnPersistence && typeof columnPersistence === "object" ? `bt-ai-filters-${columnPersistence.storageKey}` : "bt-ai-filters";
2291
+ const [savedAIFilters, setSavedAIFilters] = useState3(() => {
2292
+ try {
2293
+ const raw = localStorage.getItem(aiFiltersStorageKey);
2294
+ return raw ? JSON.parse(raw) : [];
2295
+ } catch {
2296
+ return [];
2297
+ }
2298
+ });
2299
+ const [showSavedFilters, setShowSavedFilters] = useState3(false);
2300
+ const [justSavedFilter, setJustSavedFilter] = useState3(false);
2301
+ const savedFiltersRef = useRef4(null);
2302
+ React4.useEffect(() => {
2303
+ if (!showSavedFilters) return;
2304
+ const close = (e) => {
2305
+ if (savedFiltersRef.current && !savedFiltersRef.current.contains(e.target)) {
2306
+ setShowSavedFilters(false);
2307
+ }
2308
+ };
2309
+ document.addEventListener("mousedown", close);
2310
+ return () => document.removeEventListener("mousedown", close);
2311
+ }, [showSavedFilters]);
2312
+ const saveCurrentAIFilter = useCallback2(() => {
2313
+ if (!aiResult) return;
2314
+ const label = aiQuery || aiResult.message;
2315
+ const entry = { label, operations: aiResult.operations, query: aiQuery };
2316
+ const next = [...savedAIFilters, entry];
2317
+ setSavedAIFilters(next);
2318
+ try {
2319
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2320
+ } catch {
2321
+ }
2322
+ setJustSavedFilter(true);
2323
+ setTimeout(() => setJustSavedFilter(false), 1500);
2324
+ }, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
2325
+ const removeSavedFilter = useCallback2((index) => {
2326
+ const next = savedAIFilters.filter((_, i) => i !== index);
2327
+ setSavedAIFilters(next);
2328
+ try {
2329
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2330
+ } catch {
2331
+ }
2332
+ }, [savedAIFilters, aiFiltersStorageKey]);
2333
+ const applySavedFilter = useCallback2((filter) => {
2334
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, filter.operations);
2335
+ setAiStyleOps(sOps);
2336
+ setAiCellStyleOps(csOps);
2337
+ if (filter.operations.some((op) => op.type === "filter")) {
2338
+ const keySet = /* @__PURE__ */ new Set();
2339
+ filteredData.forEach((row, idx) => {
2340
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2341
+ keySet.add(k);
2342
+ });
2343
+ setAiFilteredDataKeys(keySet);
2344
+ } else {
2345
+ setAiFilteredDataKeys(null);
2346
+ }
2347
+ if (sortOp) {
2348
+ setAiSortKey(sortOp.column);
2349
+ setAiSortDir(sortOp.direction);
2350
+ } else {
2351
+ setAiSortKey(null);
2352
+ setAiSortDir(null);
2353
+ }
2354
+ if (hideCols.length > 0 || showCols.length > 0) {
2355
+ setColumns(
2356
+ (prev) => prev.map((col) => {
2357
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2358
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2359
+ return col;
2360
+ })
2361
+ );
2362
+ }
2363
+ for (const rOp of resizeOps) {
2364
+ const w = Math.max(40, Math.min(800, rOp.width));
2365
+ setColumnWidths((prev) => {
2366
+ const n = new Map(prev);
2367
+ n.set(rOp.column, w);
2368
+ return n;
2369
+ });
2370
+ onColumnResize?.(rOp.column, w);
2371
+ }
2372
+ if (reorderOp) {
2373
+ setColumnOrder(reorderOp.order);
2374
+ onColumnOrderChange?.(reorderOp.order);
2375
+ }
2376
+ for (const pOp of pinOps) {
2377
+ setColumns(
2378
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2379
+ );
2380
+ onColumnPin?.(pOp.column, pOp.pinned);
2381
+ }
2382
+ if (setPageOp) {
2383
+ setInternalPage(setPageOp.page);
2384
+ }
2385
+ setAiResult({ operations: filter.operations, message: `Applied saved filter: ${filter.label}` });
2386
+ setShowSavedFilters(false);
2387
+ }, [data, rowKey, onColumnResize, onColumnOrderChange, onColumnPin]);
2222
2388
  const onAIResponseRef = useRef4(onAIResponse);
2223
2389
  onAIResponseRef.current = onAIResponse;
2224
2390
  const handleAISubmit = useCallback2(async () => {
@@ -2240,7 +2406,7 @@ function BoltTable({
2240
2406
  } else {
2241
2407
  throw new Error("AI mode requires either aiConfig or onAIQuery prop");
2242
2408
  }
2243
- const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns, showColumns } = applyAIOperations(data, response.operations);
2409
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, response.operations);
2244
2410
  setAiStyleOps(sOps);
2245
2411
  setAiCellStyleOps(csOps);
2246
2412
  if (response.operations.some((op) => op.type === "filter")) {
@@ -2260,15 +2426,37 @@ function BoltTable({
2260
2426
  setAiSortKey(null);
2261
2427
  setAiSortDir(null);
2262
2428
  }
2263
- if (hideColumns.length > 0 || showColumns.length > 0) {
2429
+ if (hideCols.length > 0 || showCols.length > 0) {
2264
2430
  setColumns(
2265
2431
  (prev) => prev.map((col) => {
2266
- if (hideColumns.includes(col.key)) return { ...col, hidden: true };
2267
- if (showColumns.includes(col.key)) return { ...col, hidden: false };
2432
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2433
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2268
2434
  return col;
2269
2435
  })
2270
2436
  );
2271
2437
  }
2438
+ for (const rOp of resizeOps) {
2439
+ const w = Math.max(40, Math.min(800, rOp.width));
2440
+ setColumnWidths((prev) => {
2441
+ const n = new Map(prev);
2442
+ n.set(rOp.column, w);
2443
+ return n;
2444
+ });
2445
+ onColumnResize?.(rOp.column, w);
2446
+ }
2447
+ if (reorderOp) {
2448
+ setColumnOrder(reorderOp.order);
2449
+ onColumnOrderChange?.(reorderOp.order);
2450
+ }
2451
+ for (const pOp of pinOps) {
2452
+ setColumns(
2453
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2454
+ );
2455
+ onColumnPin?.(pOp.column, pOp.pinned);
2456
+ }
2457
+ if (setPageOp) {
2458
+ setInternalPage(setPageOp.page);
2459
+ }
2272
2460
  setAiResult(response);
2273
2461
  onAIResponseRef.current?.(response);
2274
2462
  } catch (err) {
@@ -2346,17 +2534,29 @@ function BoltTable({
2346
2534
  }, []);
2347
2535
  const getRowKey = useCallback2(
2348
2536
  (record, index) => {
2349
- if (record == null) return String(index);
2537
+ if (record == null) return `__row_${index}`;
2350
2538
  try {
2351
- if (typeof rowKey === "function") return String(rowKey(record));
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
+ }
2352
2547
  if (typeof rowKey === "string") {
2353
2548
  const val = record[rowKey];
2354
- return val != null ? String(val) : String(index);
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;
2355
2555
  }
2356
2556
  } catch {
2357
- return String(index);
2557
+ return `__row_${index}`;
2358
2558
  }
2359
- return String(index);
2559
+ return `__row_${index}`;
2360
2560
  },
2361
2561
  [rowKey]
2362
2562
  );
@@ -2390,11 +2590,20 @@ function BoltTable({
2390
2590
  (record, index) => {
2391
2591
  if (record == null) return index;
2392
2592
  try {
2393
- if (typeof rowKey === "function") return rowKey(record);
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
+ }
2394
2600
  if (typeof rowKey === "string") {
2395
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;
2396
2605
  if (typeof val === "number" || typeof val === "string") return val;
2397
- return val != null ? String(val) : index;
2606
+ return str;
2398
2607
  }
2399
2608
  } catch {
2400
2609
  return index;
@@ -2481,6 +2690,19 @@ function BoltTable({
2481
2690
  };
2482
2691
  return [selectionColumn, ...columnsWithExpand];
2483
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]);
2484
2706
  const resizeOverlayRef = useRef4(null);
2485
2707
  const tableAreaRef = useRef4(null);
2486
2708
  const [scrollAreaWidth, setScrollAreaWidth] = useState3(0);
@@ -2563,7 +2785,7 @@ function BoltTable({
2563
2785
  onColumnOrderChangeRef.current = onColumnOrderChange;
2564
2786
  const handleColumnDragStart = useCallback2(
2565
2787
  (columnKey, e) => {
2566
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2788
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2567
2789
  const headerEl = e.currentTarget.closest(
2568
2790
  "[data-column-key]"
2569
2791
  );
@@ -2598,7 +2820,7 @@ function BoltTable({
2598
2820
  let newOverId = null;
2599
2821
  headers.forEach((h) => {
2600
2822
  const key = h.dataset.columnKey;
2601
- if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
2823
+ if (!key || key === "__select__" || key === "__expand__" || key === "__drag__" || key === columnKey) {
2602
2824
  h.removeAttribute("data-drag-over");
2603
2825
  return;
2604
2826
  }
@@ -2650,16 +2872,97 @@ function BoltTable({
2650
2872
  },
2651
2873
  []
2652
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
+ );
2653
2956
  const handleResizeStart = (columnKey, e) => {
2654
2957
  e.preventDefault();
2655
2958
  e.stopPropagation();
2656
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2657
- const columnIndex = columnsWithSelection.findIndex(
2959
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2960
+ const columnIndex = columnsWithDrag.findIndex(
2658
2961
  (col) => col.key === columnKey
2659
2962
  );
2660
2963
  if (columnIndex === -1) return;
2661
- if (columnsWithSelection[columnIndex].pinned) return;
2662
- const column = columnsWithSelection[columnIndex];
2964
+ if (columnsWithDrag[columnIndex].pinned) return;
2965
+ const column = columnsWithDrag[columnIndex];
2663
2966
  const startWidth = column.width ?? 150;
2664
2967
  resizeStateRef.current = {
2665
2968
  columnKey,
@@ -2716,9 +3019,50 @@ function BoltTable({
2716
3019
  });
2717
3020
  onColumnResize?.(columnKey, finalWidth);
2718
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]);
2719
3062
  const { leftPinned, unpinned, rightPinned } = useMemo2(() => {
2720
- const columnMap = new Map(columnsWithSelection.map((c) => [c.key, c]));
3063
+ const columnMap = new Map(columnsWithDrag.map((c) => [c.key, c]));
2721
3064
  const systemKeys = [
3065
+ ...rowDragEnabled && onRowReorder ? ["__drag__"] : [],
2722
3066
  ...rowSelection ? ["__select__"] : [],
2723
3067
  ...expandable ? ["__expand__"] : []
2724
3068
  ];
@@ -2730,7 +3074,7 @@ function BoltTable({
2730
3074
  else center.push(col);
2731
3075
  });
2732
3076
  return { leftPinned: left, unpinned: center, rightPinned: right };
2733
- }, [columnOrder, columnsWithSelection, rowSelection, expandable]);
3077
+ }, [columnOrder, columnsWithDrag, rowSelection, expandable, rowDragEnabled, onRowReorder]);
2734
3078
  const orderedColumns = useMemo2(
2735
3079
  () => [...leftPinned, ...unpinned, ...rightPinned],
2736
3080
  [leftPinned, unpinned, rightPinned]
@@ -2738,7 +3082,7 @@ function BoltTable({
2738
3082
  const freshOrderedColumns = useMemo2(() => {
2739
3083
  const latestMap = new Map(initialColumnsRef.current.map((c) => [c.key, c]));
2740
3084
  return orderedColumns.map((col) => {
2741
- if (col.key === "__select__" || col.key === "__expand__") return col;
3085
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return col;
2742
3086
  const latest = latestMap.get(col.key);
2743
3087
  if (!latest) return col;
2744
3088
  return {
@@ -3231,7 +3575,7 @@ function BoltTable({
3231
3575
  return freshOrderedColumns;
3232
3576
  return freshOrderedColumns.filter((col, idx) => {
3233
3577
  if (col.pinned) return true;
3234
- if (col.key === "__select__" || col.key === "__expand__") return true;
3578
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return true;
3235
3579
  return idx >= visibleColumnRange.start && idx <= visibleColumnRange.end;
3236
3580
  });
3237
3581
  }, [enableColumnVirtualization, visibleColumnRange, freshOrderedColumns]);
@@ -3378,8 +3722,11 @@ function BoltTable({
3378
3722
  [data-bt-resize]:hover [data-bt-resize-line] {
3379
3723
  opacity: 1 !important;
3380
3724
  }
3725
+ [data-bt-ctx-item] {
3726
+ transition: background-color 0.15s ease;
3727
+ }
3381
3728
  [data-bt-ctx-item]:not(:disabled):hover {
3382
- background-color: rgba(128, 128, 128, 0.15);
3729
+ background-color: rgba(128, 128, 128, 0.15) !important;
3383
3730
  }
3384
3731
  [data-bt-header][data-dragging] {
3385
3732
  opacity: 0.2 !important;
@@ -3388,6 +3735,17 @@ function BoltTable({
3388
3735
  border: 1px dashed ${accentColor} !important;
3389
3736
  }
3390
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
+ }
3391
3749
  @keyframes bt-spin { to { transform: rotate(360deg); } }
3392
3750
  @keyframes bt-ai-shimmer {
3393
3751
  0% { background-position: -200% 0; }
@@ -3406,7 +3764,7 @@ function BoltTable({
3406
3764
  borderBottom: "1px solid rgba(128,128,128,0.2)",
3407
3765
  fontSize: 12,
3408
3766
  flexShrink: 0,
3409
- overflow: "hidden"
3767
+ zIndex: 20
3410
3768
  },
3411
3769
  children: [
3412
3770
  /* @__PURE__ */ jsxs5(
@@ -3543,7 +3901,7 @@ function BoltTable({
3543
3901
  marginTop: 4
3544
3902
  },
3545
3903
  children: initialColumns.filter(
3546
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3904
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
3547
3905
  ).map((col) => {
3548
3906
  const current = columns.find(
3549
3907
  (c) => c.key === col.key
@@ -3598,6 +3956,110 @@ function BoltTable({
3598
3956
  }
3599
3957
  )
3600
3958
  ] }),
3959
+ aiMode && savedAIFilters.length > 0 && /* @__PURE__ */ jsxs5("div", { ref: savedFiltersRef, style: { position: "relative", flexShrink: 0 }, children: [
3960
+ /* @__PURE__ */ jsxs5(
3961
+ "button",
3962
+ {
3963
+ type: "button",
3964
+ onClick: () => setShowSavedFilters((p) => !p),
3965
+ style: {
3966
+ display: "flex",
3967
+ alignItems: "center",
3968
+ gap: 4,
3969
+ background: "none",
3970
+ border: "1px solid rgba(128,128,128,0.2)",
3971
+ borderRadius: 4,
3972
+ cursor: "pointer",
3973
+ padding: "4px 6px",
3974
+ color: "inherit",
3975
+ fontSize: 12
3976
+ },
3977
+ title: "Saved AI filters",
3978
+ children: [
3979
+ /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) }),
3980
+ /* @__PURE__ */ jsx5("span", { children: savedAIFilters.length })
3981
+ ]
3982
+ }
3983
+ ),
3984
+ showSavedFilters && /* @__PURE__ */ jsxs5(
3985
+ "div",
3986
+ {
3987
+ style: {
3988
+ position: "absolute",
3989
+ top: "100%",
3990
+ right: 0,
3991
+ zIndex: 99999,
3992
+ minWidth: 240,
3993
+ maxWidth: 360,
3994
+ maxHeight: 320,
3995
+ overflowY: "auto",
3996
+ borderRadius: 8,
3997
+ border: "1px solid rgba(128,128,128,0.2)",
3998
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3999
+ backdropFilter: "blur(16px)",
4000
+ WebkitBackdropFilter: "blur(16px)",
4001
+ backgroundColor: "rgba(128,128,128,0.08)",
4002
+ padding: "4px 0",
4003
+ marginTop: 4
4004
+ },
4005
+ children: [
4006
+ /* @__PURE__ */ jsx5("div", { style: { padding: "6px 12px", fontSize: 11, opacity: 0.5, fontWeight: 600 }, children: "Saved Filters" }),
4007
+ savedAIFilters.map((f, i) => /* @__PURE__ */ jsxs5(
4008
+ "div",
4009
+ {
4010
+ style: {
4011
+ display: "flex",
4012
+ alignItems: "center",
4013
+ gap: 6,
4014
+ padding: "6px 12px",
4015
+ cursor: "pointer",
4016
+ fontSize: 12
4017
+ },
4018
+ onMouseEnter: (e) => {
4019
+ e.currentTarget.style.backgroundColor = "rgba(128,128,128,0.15)";
4020
+ },
4021
+ onMouseLeave: (e) => {
4022
+ e.currentTarget.style.backgroundColor = "transparent";
4023
+ },
4024
+ children: [
4025
+ /* @__PURE__ */ jsx5(
4026
+ "span",
4027
+ {
4028
+ style: { flex: "1 1 0%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
4029
+ onClick: () => applySavedFilter(f),
4030
+ children: f.label
4031
+ }
4032
+ ),
4033
+ /* @__PURE__ */ jsx5(
4034
+ "button",
4035
+ {
4036
+ type: "button",
4037
+ onClick: (e) => {
4038
+ e.stopPropagation();
4039
+ removeSavedFilter(i);
4040
+ },
4041
+ style: {
4042
+ display: "flex",
4043
+ alignItems: "center",
4044
+ background: "none",
4045
+ border: "none",
4046
+ cursor: "pointer",
4047
+ padding: 2,
4048
+ color: "GrayText",
4049
+ flexShrink: 0
4050
+ },
4051
+ title: "Remove",
4052
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 10, height: 10 } })
4053
+ }
4054
+ )
4055
+ ]
4056
+ },
4057
+ i
4058
+ ))
4059
+ ]
4060
+ }
4061
+ )
4062
+ ] }),
3601
4063
  aiMode && /* @__PURE__ */ jsxs5(
3602
4064
  "button",
3603
4065
  {
@@ -3763,6 +4225,38 @@ function BoltTable({
3763
4225
  children: [
3764
4226
  /* @__PURE__ */ jsx5("span", { style: { color: accentColor, display: "flex", flexShrink: 0 }, children: icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 14, height: 14 } }) }),
3765
4227
  /* @__PURE__ */ jsx5("span", { style: { flex: "1 1 0%", opacity: 0.85 }, children: aiResult.message }),
4228
+ /* @__PURE__ */ jsxs5(
4229
+ "button",
4230
+ {
4231
+ type: "button",
4232
+ onClick: saveCurrentAIFilter,
4233
+ disabled: justSavedFilter,
4234
+ style: {
4235
+ display: "flex",
4236
+ alignItems: "center",
4237
+ gap: 4,
4238
+ background: justSavedFilter ? `${accentColor}25` : `${accentColor}12`,
4239
+ border: `1px solid ${justSavedFilter ? accentColor : `${accentColor}30`}`,
4240
+ borderRadius: 4,
4241
+ cursor: justSavedFilter ? "default" : "pointer",
4242
+ padding: "2px 8px",
4243
+ color: accentColor,
4244
+ fontSize: 11,
4245
+ flexShrink: 0,
4246
+ fontWeight: 500,
4247
+ transition: "all 0.2s ease"
4248
+ },
4249
+ title: justSavedFilter ? "Filter saved!" : "Save this filter for quick access later",
4250
+ 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: [
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" }),
4253
+ /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
4254
+ /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
4255
+ ] }),
4256
+ /* @__PURE__ */ jsx5("span", { children: justSavedFilter ? "Saved" : "Save Filter" })
4257
+ ]
4258
+ }
4259
+ ),
3766
4260
  /* @__PURE__ */ jsxs5(
3767
4261
  "button",
3768
4262
  {
@@ -3866,7 +4360,7 @@ function BoltTable({
3866
4360
  orderedColumns.map((column) => {
3867
4361
  const isPinned = !!column.pinned;
3868
4362
  const offset = columnOffsets.get(column.key);
3869
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4363
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
3870
4364
  return /* @__PURE__ */ jsx5(
3871
4365
  "div",
3872
4366
  {
@@ -3905,7 +4399,7 @@ function BoltTable({
3905
4399
  children: orderedColumns.map((column, colIndex) => {
3906
4400
  const isPinned = !!column.pinned;
3907
4401
  const offset = columnOffsets.get(column.key);
3908
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4402
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
3909
4403
  const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
3910
4404
  return /* @__PURE__ */ jsx5(
3911
4405
  "div",
@@ -4115,7 +4609,7 @@ function BoltTable({
4115
4609
  }),
4116
4610
  (() => {
4117
4611
  const firstDataColIndex = orderedColumns.findIndex(
4118
- (c) => c.key !== "__select__" && c.key !== "__expand__"
4612
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
4119
4613
  );
4120
4614
  return orderedColumns.map((column, visualIndex) => {
4121
4615
  const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
@@ -4192,6 +4686,39 @@ function BoltTable({
4192
4686
  "__select__"
4193
4687
  );
4194
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
+ }
4195
4722
  if (column.key === "__expand__") {
4196
4723
  return /* @__PURE__ */ jsx5(
4197
4724
  "div",
@@ -4256,7 +4783,8 @@ function BoltTable({
4256
4783
  disabledFilters,
4257
4784
  headerGridRow: leafGridRow,
4258
4785
  headerHeight: leafHeight,
4259
- stickyTop: leafStickyTop
4786
+ stickyTop: leafStickyTop,
4787
+ onAutoFitColumn: handleAutoFitColumn
4260
4788
  },
4261
4789
  column.key
4262
4790
  );
@@ -4341,7 +4869,8 @@ function BoltTable({
4341
4869
  enableDynamicRowHeight,
4342
4870
  onRowHeightChange: handleRowHeightChange,
4343
4871
  columnGridIndexMap,
4344
- 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
4345
4874
  }
4346
4875
  )
4347
4876
  ]
@@ -4695,6 +5224,35 @@ function BoltTable({
4695
5224
  ),
4696
5225
  document.body
4697
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
+ ),
4698
5256
  cellContextMenu && mounted && (() => {
4699
5257
  const menuCol = freshOrderedColumns.find(
4700
5258
  (c) => c.key === cellContextMenu.columnKey