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.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
- "aria-label": `Resize ${column.key} column`,
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
  {
@@ -711,66 +719,68 @@ function detectColumnType(key, data) {
711
719
  }
712
720
  return { type: "string", sample: sampleStr };
713
721
  }
722
+ var cachedSchema = null;
723
+ function buildSchemaFingerprint(columns, dataLen) {
724
+ return columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => c.key).join(",") + `:${dataLen}`;
725
+ }
714
726
  function buildSystemPrompt(columns, data) {
715
- const schemaLines = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => {
727
+ const fingerprint = buildSchemaFingerprint(columns, data.length);
728
+ if (cachedSchema?.fingerprint === fingerprint) {
729
+ return cachedSchema.prompt;
730
+ }
731
+ const cols = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__");
732
+ const schema = cols.map((c) => {
716
733
  const key = c.dataIndex ?? c.key;
717
734
  const title = typeof c.title === "string" ? c.title : c.key;
718
735
  const info = detectColumnType(key, data);
719
- return ` - key: "${c.key}", title: "${title}", dataIndex: "${key}", type: ${info.type}${info.sample ? ` (values: ${info.sample})` : ""}`;
736
+ const w = c.width ?? 150;
737
+ const pin = c.pinned ? `, pinned: "${c.pinned}"` : "";
738
+ const hidden = c.hidden ? ", hidden: true" : "";
739
+ const vals = info.sample ? "|vals: " + info.sample : "";
740
+ return ` ${c.key}|${key}|"${title}"|${info.type}|w:${w}${pin}${hidden}${vals}`;
720
741
  }).join("\n");
721
- const sample = data.slice(0, 5).map((row) => {
742
+ const sample = data.slice(0, 3).map((row) => {
722
743
  const obj = {};
723
- for (const col of columns) {
724
- if (col.key === "__select__" || col.key === "__expand__") continue;
744
+ for (const col of cols) {
725
745
  const di = col.dataIndex ?? col.key;
726
746
  obj[di] = row[di];
727
747
  }
728
748
  return obj;
729
749
  });
730
- return `You are a data table assistant. You help users query, filter, sort, and style tabular data.
731
- You MUST respond with ONLY a valid JSON object \u2014 no markdown fences, no explanation, no extra text.
732
-
733
- ## Table Schema
734
- ${schemaLines}
735
-
736
- ## Sample Data (first ${sample.length} of ${data.length} rows)
737
- ${JSON.stringify(sample, null, 2)}
738
-
739
- ## Available Operations
740
- Combine any of these in a single response:
750
+ const prompt = `Data table AI. Respond ONLY with valid JSON, no markdown/explanation.
741
751
 
742
- 1. **filter** \u2014 show only rows matching conditions
743
- { "type": "filter", "conditions": [{ "column": "<dataIndex>", "op": "<op>", "value": <val> }], "logic": "and" | "or" }
752
+ SCHEMA (key|dataIndex|title|type|width|flags|sample):
753
+ ${schema}
744
754
 
745
- 2. **rowStyle** \u2014 apply CSS styles to entire rows matching conditions
746
- { "type": "rowStyle", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
755
+ SAMPLE (${sample.length}/${data.length} rows):
756
+ ${JSON.stringify(sample)}
747
757
 
748
- 3. **cellStyle** \u2014 apply CSS styles to a specific column's cells matching conditions
749
- { "type": "cellStyle", "column": "<dataIndex>", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
758
+ COLUMN ORDER: [${cols.map((c) => `"${c.key}"`).join(",")}]
750
759
 
751
- 4. **sort** \u2014 sort data by a column
752
- { "type": "sort", "column": "<dataIndex>", "direction": "asc" | "desc" }
760
+ OPS (combine any):
761
+ filter: {type:"filter",conditions:[{column:"<dataIndex>",op:"<op>",value:<v>}],logic:"and"|"or"}
762
+ sort: {type:"sort",column:"<dataIndex>",direction:"asc"|"desc"}
763
+ rowStyle: {type:"rowStyle",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
764
+ cellStyle: {type:"cellStyle",column:"<dataIndex>",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
765
+ hideColumns: {type:"hideColumns",columns:["key",...]}
766
+ showColumns: {type:"showColumns",columns:["key",...]}
767
+ resizeColumn: {type:"resizeColumn",column:"<key>",width:<px>}
768
+ reorderColumns: {type:"reorderColumns",order:["key1","key2",...]} (full column order)
769
+ pinColumn: {type:"pinColumn",column:"<key>",pinned:"left"|"right"|false}
770
+ setPage: {type:"setPage",page:<number>}
753
771
 
754
- 5. **hideColumns** / **showColumns** \u2014 toggle column visibility
755
- { "type": "hideColumns" | "showColumns", "columns": ["<key>", ...] }
772
+ OPS: eq,neq,gt,gte,lt,lte,contains,notContains,startsWith,endsWith,in,notIn
756
773
 
757
- ## Operators
758
- eq, neq, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn
774
+ FORMAT: {"operations":[...],"message":"brief description"}
759
775
 
760
- ## Response format
761
- {
762
- "operations": [ ... ],
763
- "message": "Brief user-friendly description of what was applied"
764
- }
765
-
766
- ## Rules
767
- - Use the dataIndex values from the schema, not display titles.
768
- - For colors use semi-transparent values like "rgba(255,0,0,0.15)" so text stays readable.
769
- - CSS property names must be camelCase (e.g. "backgroundColor", "color", "fontWeight").
770
- - If the user asks to highlight / color / mark specific rows, use rowStyle or cellStyle.
771
- - If the user asks to show only certain rows, use filter.
772
- - You can combine filter + rowStyle + sort etc. in one response.
773
- - The "message" should be concise: what was done, in plain English.`;
776
+ RULES:
777
+ - Use dataIndex for data ops, key for column ops (hide/show/resize/reorder/pin).
778
+ - Colors: semi-transparent rgba. CSS props: camelCase.
779
+ - reorderColumns: provide FULL ordered array of ALL visible column keys.
780
+ - resizeColumn width: integer pixels (min 40, max 800).
781
+ - Combine multiple ops freely. Message: concise plain English.`;
782
+ cachedSchema = { fingerprint, prompt };
783
+ return prompt;
774
784
  }
775
785
  async function callAI(config, systemPrompt, userQuery) {
776
786
  const { provider, apiKey, model, baseUrl, maxTokens = 1024, temperature = 0.1 } = config;
@@ -936,6 +946,10 @@ function applyAIOperations(data, operations) {
936
946
  const cellStyleOps = [];
937
947
  const hideColumns = [];
938
948
  const showColumns = [];
949
+ const resizeOps = [];
950
+ let reorderOp = null;
951
+ const pinOps = [];
952
+ let setPageOp = null;
939
953
  for (const op of operations) {
940
954
  switch (op.type) {
941
955
  case "filter":
@@ -956,12 +970,24 @@ function applyAIOperations(data, operations) {
956
970
  case "showColumns":
957
971
  showColumns.push(...op.columns);
958
972
  break;
973
+ case "resizeColumn":
974
+ resizeOps.push(op);
975
+ break;
976
+ case "reorderColumns":
977
+ reorderOp = op;
978
+ break;
979
+ case "pinColumn":
980
+ pinOps.push(op);
981
+ break;
982
+ case "setPage":
983
+ setPageOp = op;
984
+ break;
959
985
  }
960
986
  }
961
987
  if (sortOp) {
962
988
  filteredData = applyAISort(filteredData, sortOp);
963
989
  }
964
- return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns };
990
+ return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns, resizeOps, reorderOp, pinOps, setPageOp };
965
991
  }
966
992
 
967
993
  // src/ResizeOverlay.tsx
@@ -1472,7 +1498,8 @@ var TableBody = ({
1472
1498
  enableDynamicRowHeight = false,
1473
1499
  onRowHeightChange,
1474
1500
  columnGridIndexMap,
1475
- cellStyleFn
1501
+ cellStyleFn,
1502
+ onRowDragStart
1476
1503
  }) => {
1477
1504
  const virtualItems = rowVirtualizer.getVirtualItems();
1478
1505
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1554,6 +1581,7 @@ var TableBody = ({
1554
1581
  "div",
1555
1582
  {
1556
1583
  "data-row-key": rowKey,
1584
+ "data-row-index": virtualRow.index,
1557
1585
  "data-column-key": col.key,
1558
1586
  "data-bt-cell": "",
1559
1587
  "data-selected": isSelected || void 0,
@@ -1566,7 +1594,45 @@ var TableBody = ({
1566
1594
  height: enableDynamicRowHeight ? void 0 : `${virtualRow.size}px`,
1567
1595
  minHeight: enableDynamicRowHeight ? `${rowHeight}px` : void 0
1568
1596
  },
1569
- children: enableDynamicRowHeight && onRowHeightChange && colIndex === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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)(
1570
1636
  DynamicRowMeasurer,
1571
1637
  {
1572
1638
  index: virtualRow.index,
@@ -2061,7 +2127,9 @@ function BoltTable({
2061
2127
  onAIQuery,
2062
2128
  onAIResponse,
2063
2129
  aiPlaceholder = "Ask AI anything about your data...",
2064
- aiButtonLabel
2130
+ aiButtonLabel,
2131
+ rowDragEnabled = false,
2132
+ onRowReorder
2065
2133
  }) {
2066
2134
  const data = (0, import_react4.useMemo)(() => {
2067
2135
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -2248,6 +2316,104 @@ function BoltTable({
2248
2316
  const [aiFilteredDataKeys, setAiFilteredDataKeys] = (0, import_react4.useState)(null);
2249
2317
  const [aiSortKey, setAiSortKey] = (0, import_react4.useState)(null);
2250
2318
  const [aiSortDir, setAiSortDir] = (0, import_react4.useState)(null);
2319
+ const aiFiltersStorageKey = columnPersistence && typeof columnPersistence === "object" ? `bt-ai-filters-${columnPersistence.storageKey}` : "bt-ai-filters";
2320
+ const [savedAIFilters, setSavedAIFilters] = (0, import_react4.useState)(() => {
2321
+ try {
2322
+ const raw = localStorage.getItem(aiFiltersStorageKey);
2323
+ return raw ? JSON.parse(raw) : [];
2324
+ } catch {
2325
+ return [];
2326
+ }
2327
+ });
2328
+ const [showSavedFilters, setShowSavedFilters] = (0, import_react4.useState)(false);
2329
+ const [justSavedFilter, setJustSavedFilter] = (0, import_react4.useState)(false);
2330
+ const savedFiltersRef = (0, import_react4.useRef)(null);
2331
+ import_react4.default.useEffect(() => {
2332
+ if (!showSavedFilters) return;
2333
+ const close = (e) => {
2334
+ if (savedFiltersRef.current && !savedFiltersRef.current.contains(e.target)) {
2335
+ setShowSavedFilters(false);
2336
+ }
2337
+ };
2338
+ document.addEventListener("mousedown", close);
2339
+ return () => document.removeEventListener("mousedown", close);
2340
+ }, [showSavedFilters]);
2341
+ const saveCurrentAIFilter = (0, import_react4.useCallback)(() => {
2342
+ if (!aiResult) return;
2343
+ const label = aiQuery || aiResult.message;
2344
+ const entry = { label, operations: aiResult.operations, query: aiQuery };
2345
+ const next = [...savedAIFilters, entry];
2346
+ setSavedAIFilters(next);
2347
+ try {
2348
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2349
+ } catch {
2350
+ }
2351
+ setJustSavedFilter(true);
2352
+ setTimeout(() => setJustSavedFilter(false), 1500);
2353
+ }, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
2354
+ const removeSavedFilter = (0, import_react4.useCallback)((index) => {
2355
+ const next = savedAIFilters.filter((_, i) => i !== index);
2356
+ setSavedAIFilters(next);
2357
+ try {
2358
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2359
+ } catch {
2360
+ }
2361
+ }, [savedAIFilters, aiFiltersStorageKey]);
2362
+ const applySavedFilter = (0, import_react4.useCallback)((filter) => {
2363
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, filter.operations);
2364
+ setAiStyleOps(sOps);
2365
+ setAiCellStyleOps(csOps);
2366
+ if (filter.operations.some((op) => op.type === "filter")) {
2367
+ const keySet = /* @__PURE__ */ new Set();
2368
+ filteredData.forEach((row, idx) => {
2369
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2370
+ keySet.add(k);
2371
+ });
2372
+ setAiFilteredDataKeys(keySet);
2373
+ } else {
2374
+ setAiFilteredDataKeys(null);
2375
+ }
2376
+ if (sortOp) {
2377
+ setAiSortKey(sortOp.column);
2378
+ setAiSortDir(sortOp.direction);
2379
+ } else {
2380
+ setAiSortKey(null);
2381
+ setAiSortDir(null);
2382
+ }
2383
+ if (hideCols.length > 0 || showCols.length > 0) {
2384
+ setColumns(
2385
+ (prev) => prev.map((col) => {
2386
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2387
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2388
+ return col;
2389
+ })
2390
+ );
2391
+ }
2392
+ for (const rOp of resizeOps) {
2393
+ const w = Math.max(40, Math.min(800, rOp.width));
2394
+ setColumnWidths((prev) => {
2395
+ const n = new Map(prev);
2396
+ n.set(rOp.column, w);
2397
+ return n;
2398
+ });
2399
+ onColumnResize?.(rOp.column, w);
2400
+ }
2401
+ if (reorderOp) {
2402
+ setColumnOrder(reorderOp.order);
2403
+ onColumnOrderChange?.(reorderOp.order);
2404
+ }
2405
+ for (const pOp of pinOps) {
2406
+ setColumns(
2407
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2408
+ );
2409
+ onColumnPin?.(pOp.column, pOp.pinned);
2410
+ }
2411
+ if (setPageOp) {
2412
+ setInternalPage(setPageOp.page);
2413
+ }
2414
+ setAiResult({ operations: filter.operations, message: `Applied saved filter: ${filter.label}` });
2415
+ setShowSavedFilters(false);
2416
+ }, [data, rowKey, onColumnResize, onColumnOrderChange, onColumnPin]);
2251
2417
  const onAIResponseRef = (0, import_react4.useRef)(onAIResponse);
2252
2418
  onAIResponseRef.current = onAIResponse;
2253
2419
  const handleAISubmit = (0, import_react4.useCallback)(async () => {
@@ -2269,7 +2435,7 @@ function BoltTable({
2269
2435
  } else {
2270
2436
  throw new Error("AI mode requires either aiConfig or onAIQuery prop");
2271
2437
  }
2272
- const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns, showColumns } = applyAIOperations(data, response.operations);
2438
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, response.operations);
2273
2439
  setAiStyleOps(sOps);
2274
2440
  setAiCellStyleOps(csOps);
2275
2441
  if (response.operations.some((op) => op.type === "filter")) {
@@ -2289,15 +2455,37 @@ function BoltTable({
2289
2455
  setAiSortKey(null);
2290
2456
  setAiSortDir(null);
2291
2457
  }
2292
- if (hideColumns.length > 0 || showColumns.length > 0) {
2458
+ if (hideCols.length > 0 || showCols.length > 0) {
2293
2459
  setColumns(
2294
2460
  (prev) => prev.map((col) => {
2295
- if (hideColumns.includes(col.key)) return { ...col, hidden: true };
2296
- if (showColumns.includes(col.key)) return { ...col, hidden: false };
2461
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2462
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2297
2463
  return col;
2298
2464
  })
2299
2465
  );
2300
2466
  }
2467
+ for (const rOp of resizeOps) {
2468
+ const w = Math.max(40, Math.min(800, rOp.width));
2469
+ setColumnWidths((prev) => {
2470
+ const n = new Map(prev);
2471
+ n.set(rOp.column, w);
2472
+ return n;
2473
+ });
2474
+ onColumnResize?.(rOp.column, w);
2475
+ }
2476
+ if (reorderOp) {
2477
+ setColumnOrder(reorderOp.order);
2478
+ onColumnOrderChange?.(reorderOp.order);
2479
+ }
2480
+ for (const pOp of pinOps) {
2481
+ setColumns(
2482
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2483
+ );
2484
+ onColumnPin?.(pOp.column, pOp.pinned);
2485
+ }
2486
+ if (setPageOp) {
2487
+ setInternalPage(setPageOp.page);
2488
+ }
2301
2489
  setAiResult(response);
2302
2490
  onAIResponseRef.current?.(response);
2303
2491
  } catch (err) {
@@ -2375,17 +2563,29 @@ function BoltTable({
2375
2563
  }, []);
2376
2564
  const getRowKey = (0, import_react4.useCallback)(
2377
2565
  (record, index) => {
2378
- if (record == null) return String(index);
2566
+ if (record == null) return `__row_${index}`;
2379
2567
  try {
2380
- if (typeof rowKey === "function") return String(rowKey(record));
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
+ }
2381
2576
  if (typeof rowKey === "string") {
2382
2577
  const val = record[rowKey];
2383
- return val != null ? String(val) : String(index);
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;
2384
2584
  }
2385
2585
  } catch {
2386
- return String(index);
2586
+ return `__row_${index}`;
2387
2587
  }
2388
- return String(index);
2588
+ return `__row_${index}`;
2389
2589
  },
2390
2590
  [rowKey]
2391
2591
  );
@@ -2419,11 +2619,20 @@ function BoltTable({
2419
2619
  (record, index) => {
2420
2620
  if (record == null) return index;
2421
2621
  try {
2422
- if (typeof rowKey === "function") return rowKey(record);
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
+ }
2423
2629
  if (typeof rowKey === "string") {
2424
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;
2425
2634
  if (typeof val === "number" || typeof val === "string") return val;
2426
- return val != null ? String(val) : index;
2635
+ return str;
2427
2636
  }
2428
2637
  } catch {
2429
2638
  return index;
@@ -2510,6 +2719,19 @@ function BoltTable({
2510
2719
  };
2511
2720
  return [selectionColumn, ...columnsWithExpand];
2512
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]);
2513
2735
  const resizeOverlayRef = (0, import_react4.useRef)(null);
2514
2736
  const tableAreaRef = (0, import_react4.useRef)(null);
2515
2737
  const [scrollAreaWidth, setScrollAreaWidth] = (0, import_react4.useState)(0);
@@ -2592,7 +2814,7 @@ function BoltTable({
2592
2814
  onColumnOrderChangeRef.current = onColumnOrderChange;
2593
2815
  const handleColumnDragStart = (0, import_react4.useCallback)(
2594
2816
  (columnKey, e) => {
2595
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2817
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2596
2818
  const headerEl = e.currentTarget.closest(
2597
2819
  "[data-column-key]"
2598
2820
  );
@@ -2627,7 +2849,7 @@ function BoltTable({
2627
2849
  let newOverId = null;
2628
2850
  headers.forEach((h) => {
2629
2851
  const key = h.dataset.columnKey;
2630
- if (!key || key === "__select__" || key === "__expand__" || key === columnKey) {
2852
+ if (!key || key === "__select__" || key === "__expand__" || key === "__drag__" || key === columnKey) {
2631
2853
  h.removeAttribute("data-drag-over");
2632
2854
  return;
2633
2855
  }
@@ -2679,16 +2901,97 @@ function BoltTable({
2679
2901
  },
2680
2902
  []
2681
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
+ );
2682
2985
  const handleResizeStart = (columnKey, e) => {
2683
2986
  e.preventDefault();
2684
2987
  e.stopPropagation();
2685
- if (columnKey === "__select__" || columnKey === "__expand__") return;
2686
- const columnIndex = columnsWithSelection.findIndex(
2988
+ if (columnKey === "__select__" || columnKey === "__expand__" || columnKey === "__drag__") return;
2989
+ const columnIndex = columnsWithDrag.findIndex(
2687
2990
  (col) => col.key === columnKey
2688
2991
  );
2689
2992
  if (columnIndex === -1) return;
2690
- if (columnsWithSelection[columnIndex].pinned) return;
2691
- const column = columnsWithSelection[columnIndex];
2993
+ if (columnsWithDrag[columnIndex].pinned) return;
2994
+ const column = columnsWithDrag[columnIndex];
2692
2995
  const startWidth = column.width ?? 150;
2693
2996
  resizeStateRef.current = {
2694
2997
  columnKey,
@@ -2745,9 +3048,50 @@ function BoltTable({
2745
3048
  });
2746
3049
  onColumnResize?.(columnKey, finalWidth);
2747
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]);
2748
3091
  const { leftPinned, unpinned, rightPinned } = (0, import_react4.useMemo)(() => {
2749
- const columnMap = new Map(columnsWithSelection.map((c) => [c.key, c]));
3092
+ const columnMap = new Map(columnsWithDrag.map((c) => [c.key, c]));
2750
3093
  const systemKeys = [
3094
+ ...rowDragEnabled && onRowReorder ? ["__drag__"] : [],
2751
3095
  ...rowSelection ? ["__select__"] : [],
2752
3096
  ...expandable ? ["__expand__"] : []
2753
3097
  ];
@@ -2759,7 +3103,7 @@ function BoltTable({
2759
3103
  else center.push(col);
2760
3104
  });
2761
3105
  return { leftPinned: left, unpinned: center, rightPinned: right };
2762
- }, [columnOrder, columnsWithSelection, rowSelection, expandable]);
3106
+ }, [columnOrder, columnsWithDrag, rowSelection, expandable, rowDragEnabled, onRowReorder]);
2763
3107
  const orderedColumns = (0, import_react4.useMemo)(
2764
3108
  () => [...leftPinned, ...unpinned, ...rightPinned],
2765
3109
  [leftPinned, unpinned, rightPinned]
@@ -2767,7 +3111,7 @@ function BoltTable({
2767
3111
  const freshOrderedColumns = (0, import_react4.useMemo)(() => {
2768
3112
  const latestMap = new Map(initialColumnsRef.current.map((c) => [c.key, c]));
2769
3113
  return orderedColumns.map((col) => {
2770
- if (col.key === "__select__" || col.key === "__expand__") return col;
3114
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return col;
2771
3115
  const latest = latestMap.get(col.key);
2772
3116
  if (!latest) return col;
2773
3117
  return {
@@ -3260,7 +3604,7 @@ function BoltTable({
3260
3604
  return freshOrderedColumns;
3261
3605
  return freshOrderedColumns.filter((col, idx) => {
3262
3606
  if (col.pinned) return true;
3263
- if (col.key === "__select__" || col.key === "__expand__") return true;
3607
+ if (col.key === "__select__" || col.key === "__expand__" || col.key === "__drag__") return true;
3264
3608
  return idx >= visibleColumnRange.start && idx <= visibleColumnRange.end;
3265
3609
  });
3266
3610
  }, [enableColumnVirtualization, visibleColumnRange, freshOrderedColumns]);
@@ -3407,8 +3751,11 @@ function BoltTable({
3407
3751
  [data-bt-resize]:hover [data-bt-resize-line] {
3408
3752
  opacity: 1 !important;
3409
3753
  }
3754
+ [data-bt-ctx-item] {
3755
+ transition: background-color 0.15s ease;
3756
+ }
3410
3757
  [data-bt-ctx-item]:not(:disabled):hover {
3411
- background-color: rgba(128, 128, 128, 0.15);
3758
+ background-color: rgba(128, 128, 128, 0.15) !important;
3412
3759
  }
3413
3760
  [data-bt-header][data-dragging] {
3414
3761
  opacity: 0.2 !important;
@@ -3417,6 +3764,17 @@ function BoltTable({
3417
3764
  border: 1px dashed ${accentColor} !important;
3418
3765
  }
3419
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
+ }
3420
3778
  @keyframes bt-spin { to { transform: rotate(360deg); } }
3421
3779
  @keyframes bt-ai-shimmer {
3422
3780
  0% { background-position: -200% 0; }
@@ -3435,7 +3793,7 @@ function BoltTable({
3435
3793
  borderBottom: "1px solid rgba(128,128,128,0.2)",
3436
3794
  fontSize: 12,
3437
3795
  flexShrink: 0,
3438
- overflow: "hidden"
3796
+ zIndex: 20
3439
3797
  },
3440
3798
  children: [
3441
3799
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
@@ -3572,7 +3930,7 @@ function BoltTable({
3572
3930
  marginTop: 4
3573
3931
  },
3574
3932
  children: initialColumns.filter(
3575
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3933
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
3576
3934
  ).map((col) => {
3577
3935
  const current = columns.find(
3578
3936
  (c) => c.key === col.key
@@ -3627,6 +3985,110 @@ function BoltTable({
3627
3985
  }
3628
3986
  )
3629
3987
  ] }),
3988
+ aiMode && savedAIFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: savedFiltersRef, style: { position: "relative", flexShrink: 0 }, children: [
3989
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3990
+ "button",
3991
+ {
3992
+ type: "button",
3993
+ onClick: () => setShowSavedFilters((p) => !p),
3994
+ style: {
3995
+ display: "flex",
3996
+ alignItems: "center",
3997
+ gap: 4,
3998
+ background: "none",
3999
+ border: "1px solid rgba(128,128,128,0.2)",
4000
+ borderRadius: 4,
4001
+ cursor: "pointer",
4002
+ padding: "4px 6px",
4003
+ color: "inherit",
4004
+ fontSize: 12
4005
+ },
4006
+ title: "Saved AI filters",
4007
+ children: [
4008
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) }),
4009
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: savedAIFilters.length })
4010
+ ]
4011
+ }
4012
+ ),
4013
+ showSavedFilters && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4014
+ "div",
4015
+ {
4016
+ style: {
4017
+ position: "absolute",
4018
+ top: "100%",
4019
+ right: 0,
4020
+ zIndex: 99999,
4021
+ minWidth: 240,
4022
+ maxWidth: 360,
4023
+ maxHeight: 320,
4024
+ overflowY: "auto",
4025
+ borderRadius: 8,
4026
+ border: "1px solid rgba(128,128,128,0.2)",
4027
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
4028
+ backdropFilter: "blur(16px)",
4029
+ WebkitBackdropFilter: "blur(16px)",
4030
+ backgroundColor: "rgba(128,128,128,0.08)",
4031
+ padding: "4px 0",
4032
+ marginTop: 4
4033
+ },
4034
+ children: [
4035
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { padding: "6px 12px", fontSize: 11, opacity: 0.5, fontWeight: 600 }, children: "Saved Filters" }),
4036
+ savedAIFilters.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4037
+ "div",
4038
+ {
4039
+ style: {
4040
+ display: "flex",
4041
+ alignItems: "center",
4042
+ gap: 6,
4043
+ padding: "6px 12px",
4044
+ cursor: "pointer",
4045
+ fontSize: 12
4046
+ },
4047
+ onMouseEnter: (e) => {
4048
+ e.currentTarget.style.backgroundColor = "rgba(128,128,128,0.15)";
4049
+ },
4050
+ onMouseLeave: (e) => {
4051
+ e.currentTarget.style.backgroundColor = "transparent";
4052
+ },
4053
+ children: [
4054
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4055
+ "span",
4056
+ {
4057
+ style: { flex: "1 1 0%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
4058
+ onClick: () => applySavedFilter(f),
4059
+ children: f.label
4060
+ }
4061
+ ),
4062
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4063
+ "button",
4064
+ {
4065
+ type: "button",
4066
+ onClick: (e) => {
4067
+ e.stopPropagation();
4068
+ removeSavedFilter(i);
4069
+ },
4070
+ style: {
4071
+ display: "flex",
4072
+ alignItems: "center",
4073
+ background: "none",
4074
+ border: "none",
4075
+ cursor: "pointer",
4076
+ padding: 2,
4077
+ color: "GrayText",
4078
+ flexShrink: 0
4079
+ },
4080
+ title: "Remove",
4081
+ children: icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 10, height: 10 } })
4082
+ }
4083
+ )
4084
+ ]
4085
+ },
4086
+ i
4087
+ ))
4088
+ ]
4089
+ }
4090
+ )
4091
+ ] }),
3630
4092
  aiMode && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3631
4093
  "button",
3632
4094
  {
@@ -3792,6 +4254,38 @@ function BoltTable({
3792
4254
  children: [
3793
4255
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: accentColor, display: "flex", flexShrink: 0 }, children: icons?.sparkles ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparklesIcon, { style: { width: 14, height: 14 } }) }),
3794
4256
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { flex: "1 1 0%", opacity: 0.85 }, children: aiResult.message }),
4257
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4258
+ "button",
4259
+ {
4260
+ type: "button",
4261
+ onClick: saveCurrentAIFilter,
4262
+ disabled: justSavedFilter,
4263
+ style: {
4264
+ display: "flex",
4265
+ alignItems: "center",
4266
+ gap: 4,
4267
+ background: justSavedFilter ? `${accentColor}25` : `${accentColor}12`,
4268
+ border: `1px solid ${justSavedFilter ? accentColor : `${accentColor}30`}`,
4269
+ borderRadius: 4,
4270
+ cursor: justSavedFilter ? "default" : "pointer",
4271
+ padding: "2px 8px",
4272
+ color: accentColor,
4273
+ fontSize: 11,
4274
+ flexShrink: 0,
4275
+ fontWeight: 500,
4276
+ transition: "all 0.2s ease"
4277
+ },
4278
+ title: justSavedFilter ? "Filter saved!" : "Save this filter for quick access later",
4279
+ 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: [
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" }),
4282
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "17 21 17 13 7 13 7 21" }),
4283
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "7 3 7 8 15 8" })
4284
+ ] }),
4285
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: justSavedFilter ? "Saved" : "Save Filter" })
4286
+ ]
4287
+ }
4288
+ ),
3795
4289
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3796
4290
  "button",
3797
4291
  {
@@ -3895,7 +4389,7 @@ function BoltTable({
3895
4389
  orderedColumns.map((column) => {
3896
4390
  const isPinned = !!column.pinned;
3897
4391
  const offset = columnOffsets.get(column.key);
3898
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4392
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
3899
4393
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3900
4394
  "div",
3901
4395
  {
@@ -3934,7 +4428,7 @@ function BoltTable({
3934
4428
  children: orderedColumns.map((column, colIndex) => {
3935
4429
  const isPinned = !!column.pinned;
3936
4430
  const offset = columnOffsets.get(column.key);
3937
- const isSystem = column.key === "__select__" || column.key === "__expand__";
4431
+ const isSystem = column.key === "__select__" || column.key === "__expand__" || column.key === "__drag__";
3938
4432
  const widthPercent = SHIMMER_WIDTHS2[(rowIndex * 7 + colIndex) % SHIMMER_WIDTHS2.length];
3939
4433
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3940
4434
  "div",
@@ -4144,7 +4638,7 @@ function BoltTable({
4144
4638
  }),
4145
4639
  (() => {
4146
4640
  const firstDataColIndex = orderedColumns.findIndex(
4147
- (c) => c.key !== "__select__" && c.key !== "__expand__"
4641
+ (c) => c.key !== "__select__" && c.key !== "__expand__" && c.key !== "__drag__"
4148
4642
  );
4149
4643
  return orderedColumns.map((column, visualIndex) => {
4150
4644
  const isInGroup = groupedColumnKeySet?.has(column.key) ?? false;
@@ -4221,6 +4715,39 @@ function BoltTable({
4221
4715
  "__select__"
4222
4716
  );
4223
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
+ }
4224
4751
  if (column.key === "__expand__") {
4225
4752
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4226
4753
  "div",
@@ -4285,7 +4812,8 @@ function BoltTable({
4285
4812
  disabledFilters,
4286
4813
  headerGridRow: leafGridRow,
4287
4814
  headerHeight: leafHeight,
4288
- stickyTop: leafStickyTop
4815
+ stickyTop: leafStickyTop,
4816
+ onAutoFitColumn: handleAutoFitColumn
4289
4817
  },
4290
4818
  column.key
4291
4819
  );
@@ -4370,7 +4898,8 @@ function BoltTable({
4370
4898
  enableDynamicRowHeight,
4371
4899
  onRowHeightChange: handleRowHeightChange,
4372
4900
  columnGridIndexMap,
4373
- 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
4374
4903
  }
4375
4904
  )
4376
4905
  ]
@@ -4724,6 +5253,35 @@ function BoltTable({
4724
5253
  ),
4725
5254
  document.body
4726
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
+ ),
4727
5285
  cellContextMenu && mounted && (() => {
4728
5286
  const menuCol = freshOrderedColumns.find(
4729
5287
  (c) => c.key === cellContextMenu.columnKey