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.d.mts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +635 -77
- package/dist/index.mjs +635 -77
- package/package.json +1 -1
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
|
{
|
|
@@ -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
|
|
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
|
-
|
|
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,
|
|
742
|
+
const sample = data.slice(0, 3).map((row) => {
|
|
722
743
|
const obj = {};
|
|
723
|
-
for (const col of
|
|
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
|
-
|
|
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
|
-
|
|
743
|
-
|
|
752
|
+
SCHEMA (key|dataIndex|title|type|width|flags|sample):
|
|
753
|
+
${schema}
|
|
744
754
|
|
|
745
|
-
|
|
746
|
-
|
|
755
|
+
SAMPLE (${sample.length}/${data.length} rows):
|
|
756
|
+
${JSON.stringify(sample)}
|
|
747
757
|
|
|
748
|
-
|
|
749
|
-
{ "type": "cellStyle", "column": "<dataIndex>", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
|
|
758
|
+
COLUMN ORDER: [${cols.map((c) => `"${c.key}"`).join(",")}]
|
|
750
759
|
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
|
|
755
|
-
{ "type": "hideColumns" | "showColumns", "columns": ["<key>", ...] }
|
|
772
|
+
OPS: eq,neq,gt,gte,lt,lte,contains,notContains,startsWith,endsWith,in,notIn
|
|
756
773
|
|
|
757
|
-
|
|
758
|
-
eq, neq, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn
|
|
774
|
+
FORMAT: {"operations":[...],"message":"brief description"}
|
|
759
775
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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:
|
|
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 (
|
|
2458
|
+
if (hideCols.length > 0 || showCols.length > 0) {
|
|
2293
2459
|
setColumns(
|
|
2294
2460
|
(prev) => prev.map((col) => {
|
|
2295
|
-
if (
|
|
2296
|
-
if (
|
|
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
|
|
2566
|
+
if (record == null) return `__row_${index}`;
|
|
2379
2567
|
try {
|
|
2380
|
-
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
|
+
}
|
|
2381
2576
|
if (typeof rowKey === "string") {
|
|
2382
2577
|
const val = record[rowKey];
|
|
2383
|
-
|
|
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
|
|
2586
|
+
return `__row_${index}`;
|
|
2387
2587
|
}
|
|
2388
|
-
return
|
|
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")
|
|
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
|
|
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 =
|
|
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 (
|
|
2691
|
-
const column =
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|