react-open-source-grid 1.6.4 → 1.6.6
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/assets/{index-DqLWhpvP.css → index-CQcTDWao.css} +1 -1
- package/dist/assets/{index-BczRxCQn.js → index-DgcHJP8T.js} +283 -74
- package/dist/assets/index.js +1 -1
- package/dist/assets/{layoutPersistence-C_jVd_iy.js → layoutPersistence-BOTIXT8B.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/lib/components/AdvancedEditorsDemo.d.ts +15 -0
- package/dist/lib/components/DataGrid/PivotToolbar.d.ts +29 -0
- package/dist/lib/components/DataGrid/index.d.ts +4 -0
- package/dist/lib/components/DataGrid/pivotEngine.d.ts +55 -0
- package/dist/lib/components/DataGrid/types.d.ts +4 -0
- package/dist/lib/components/PivotDemo.d.ts +8 -0
- package/dist/lib/editors/DateEditor.d.ts +23 -0
- package/dist/lib/editors/MarkdownEditor.d.ts +23 -0
- package/dist/lib/editors/MultiSelectEditor.d.ts +31 -0
- package/dist/lib/editors/NumericEditor.d.ts +33 -0
- package/dist/lib/editors/RichSelectEditor.d.ts +32 -0
- package/dist/lib/editors/editorTypes.d.ts +60 -0
- package/dist/lib/editors/editorUtils.d.ts +38 -0
- package/dist/lib/editors/index.d.ts +12 -0
- package/dist/lib/index.cjs +1823 -26
- package/dist/lib/index.css +634 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1805 -25
- package/package.json +1 -1
package/dist/lib/index.cjs
CHANGED
|
@@ -341,6 +341,7 @@ __export(index_exports, {
|
|
|
341
341
|
ColumnFilters: () => ColumnFilters,
|
|
342
342
|
CurrencyCell: () => CurrencyCell,
|
|
343
343
|
DataGrid: () => DataGrid,
|
|
344
|
+
DateEditor: () => DateEditor,
|
|
344
345
|
DensityToggle: () => DensityToggle,
|
|
345
346
|
ExportMenu: () => ExportMenu,
|
|
346
347
|
FacetedSearch: () => FacetedSearch,
|
|
@@ -353,11 +354,16 @@ __export(index_exports, {
|
|
|
353
354
|
LayoutPersistenceManager: () => LayoutPersistenceManager,
|
|
354
355
|
LayoutPresetsManager: () => LayoutPresetsManager,
|
|
355
356
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
357
|
+
MarkdownEditor: () => MarkdownEditor,
|
|
356
358
|
MarketDataEngine: () => MarketDataEngine,
|
|
357
359
|
MarketDataGrid: () => MarketDataGrid,
|
|
360
|
+
MultiSelectEditor: () => MultiSelectEditor,
|
|
361
|
+
NumericEditor: () => NumericEditor,
|
|
362
|
+
PivotToolbar: () => PivotToolbar,
|
|
358
363
|
PriorityIndicator: () => PriorityIndicator,
|
|
359
364
|
ProgressBar: () => ProgressBar,
|
|
360
365
|
Rating: () => Rating,
|
|
366
|
+
RichSelectEditor: () => RichSelectEditor,
|
|
361
367
|
ServerAdapter: () => ServerAdapter,
|
|
362
368
|
ServerSideDataSource: () => ServerSideDataSource,
|
|
363
369
|
StatusChip: () => StatusChip,
|
|
@@ -367,6 +373,7 @@ __export(index_exports, {
|
|
|
367
373
|
VirtualScroller: () => VirtualScroller,
|
|
368
374
|
WebSocketMockFeed: () => WebSocketMockFeed,
|
|
369
375
|
alpineTheme: () => alpineTheme,
|
|
376
|
+
buildPivot: () => buildPivot,
|
|
370
377
|
buildTreeFromFlat: () => buildTreeFromFlat,
|
|
371
378
|
collapseAllNodes: () => collapseAllNodes,
|
|
372
379
|
countTreeNodes: () => countTreeNodes,
|
|
@@ -376,12 +383,17 @@ __export(index_exports, {
|
|
|
376
383
|
createMockWebSocket: () => createMockWebSocket,
|
|
377
384
|
createPreset: () => createPreset,
|
|
378
385
|
darkTheme: () => darkTheme,
|
|
386
|
+
debounce: () => debounce2,
|
|
379
387
|
densityConfigs: () => densityConfigs,
|
|
388
|
+
downloadCSV: () => downloadCSV,
|
|
380
389
|
expandAllNodes: () => expandAllNodes,
|
|
390
|
+
exportPivotToCSV: () => exportPivotToCSV,
|
|
381
391
|
exportToCSV: () => exportToCSV,
|
|
382
392
|
exportToXLSX: () => exportToXLSX,
|
|
393
|
+
filterOptions: () => filterOptions,
|
|
383
394
|
filterTree: () => filterTree,
|
|
384
395
|
flattenTree: () => flattenTree,
|
|
396
|
+
formatNumber: () => formatNumber,
|
|
385
397
|
generateDensityCSS: () => generateDensityCSS,
|
|
386
398
|
generateFilename: () => generateFilename,
|
|
387
399
|
generatePresetId: () => generatePresetId,
|
|
@@ -399,12 +411,17 @@ __export(index_exports, {
|
|
|
399
411
|
isTreeNode: () => isTreeNode,
|
|
400
412
|
loadDensityMode: () => loadDensityMode,
|
|
401
413
|
materialTheme: () => materialTheme,
|
|
414
|
+
parseFormattedNumber: () => parseFormattedNumber,
|
|
402
415
|
quartzTheme: () => quartzTheme,
|
|
403
416
|
saveDensityMode: () => saveDensityMode,
|
|
404
417
|
themes: () => themes,
|
|
405
418
|
toggleNodeExpansion: () => toggleNodeExpansion,
|
|
406
419
|
useDensityMode: () => useDensityMode,
|
|
407
|
-
|
|
420
|
+
useEditorAutoFocus: () => useEditorAutoFocus,
|
|
421
|
+
useEditorClickOutside: () => useEditorClickOutside,
|
|
422
|
+
useEditorKeyboardNavigation: () => useEditorKeyboardNavigation,
|
|
423
|
+
useMarketData: () => useMarketData,
|
|
424
|
+
usePopupPosition: () => usePopupPosition
|
|
408
425
|
});
|
|
409
426
|
module.exports = __toCommonJS(index_exports);
|
|
410
427
|
|
|
@@ -2755,7 +2772,16 @@ var GridBody = ({
|
|
|
2755
2772
|
onKeyDown: (e) => handleKeyDown(e, rowIndex, columnIndex),
|
|
2756
2773
|
tabIndex: isCellFocused ? 0 : -1
|
|
2757
2774
|
},
|
|
2758
|
-
isEditing ?
|
|
2775
|
+
isEditing ? column.editor ? column.editor({
|
|
2776
|
+
value: editState.value,
|
|
2777
|
+
row,
|
|
2778
|
+
column,
|
|
2779
|
+
onChange: handleEditChange,
|
|
2780
|
+
onCommit: handleEditComplete,
|
|
2781
|
+
onCancel: () => dispatch({ type: "END_EDIT" }),
|
|
2782
|
+
autoFocus: true,
|
|
2783
|
+
...column.editorParams
|
|
2784
|
+
}) : /* @__PURE__ */ import_react6.default.createElement(
|
|
2759
2785
|
"input",
|
|
2760
2786
|
{
|
|
2761
2787
|
ref: editInputRef,
|
|
@@ -5426,7 +5452,7 @@ var LayoutPresetsManager = ({
|
|
|
5426
5452
|
setLoading(false);
|
|
5427
5453
|
}
|
|
5428
5454
|
};
|
|
5429
|
-
const
|
|
5455
|
+
const formatDate2 = (timestamp) => {
|
|
5430
5456
|
return new Date(timestamp).toLocaleString();
|
|
5431
5457
|
};
|
|
5432
5458
|
return /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative inline-block" }, /* @__PURE__ */ import_react14.default.createElement(
|
|
@@ -5540,7 +5566,7 @@ var LayoutPresetsManager = ({
|
|
|
5540
5566
|
if (buttons) buttons.style.opacity = "0";
|
|
5541
5567
|
}
|
|
5542
5568
|
},
|
|
5543
|
-
/* @__PURE__ */ import_react14.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ import_react14.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ",
|
|
5569
|
+
/* @__PURE__ */ import_react14.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ import_react14.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ", formatDate2(preset.updatedAt))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "preset-actions", style: { display: "flex", gap: "4px", marginLeft: "8px", opacity: 0, transition: "opacity 0.2s" } }, /* @__PURE__ */ import_react14.default.createElement(
|
|
5544
5570
|
"button",
|
|
5545
5571
|
{
|
|
5546
5572
|
onClick: (e) => handleUpdatePreset(preset, e),
|
|
@@ -8445,6 +8471,195 @@ var GridApiImpl = class {
|
|
|
8445
8471
|
}
|
|
8446
8472
|
};
|
|
8447
8473
|
|
|
8474
|
+
// src/components/DataGrid/pivotEngine.ts
|
|
8475
|
+
var AGGREGATORS = {
|
|
8476
|
+
sum: (values) => values.reduce((acc, val) => acc + val, 0),
|
|
8477
|
+
count: (values) => values.length,
|
|
8478
|
+
avg: (values) => values.length > 0 ? values.reduce((acc, val) => acc + val, 0) / values.length : 0,
|
|
8479
|
+
min: (values) => values.length > 0 ? Math.min(...values) : 0,
|
|
8480
|
+
max: (values) => values.length > 0 ? Math.max(...values) : 0
|
|
8481
|
+
};
|
|
8482
|
+
function getUniqueValues(data, column) {
|
|
8483
|
+
const uniqueSet = /* @__PURE__ */ new Set();
|
|
8484
|
+
data.forEach((row) => {
|
|
8485
|
+
const value = row[column];
|
|
8486
|
+
if (value !== null && value !== void 0) {
|
|
8487
|
+
uniqueSet.add(String(value));
|
|
8488
|
+
}
|
|
8489
|
+
});
|
|
8490
|
+
return Array.from(uniqueSet).sort();
|
|
8491
|
+
}
|
|
8492
|
+
function getAggregatorFn(aggregator) {
|
|
8493
|
+
if (typeof aggregator === "function") {
|
|
8494
|
+
return aggregator;
|
|
8495
|
+
}
|
|
8496
|
+
return AGGREGATORS[aggregator] || AGGREGATORS.sum;
|
|
8497
|
+
}
|
|
8498
|
+
function toNumber(value) {
|
|
8499
|
+
if (typeof value === "number") return value;
|
|
8500
|
+
if (typeof value === "string") {
|
|
8501
|
+
const num = parseFloat(value);
|
|
8502
|
+
return isNaN(num) ? 0 : num;
|
|
8503
|
+
}
|
|
8504
|
+
return 0;
|
|
8505
|
+
}
|
|
8506
|
+
function buildPivot(data, config) {
|
|
8507
|
+
const {
|
|
8508
|
+
pivotColumn,
|
|
8509
|
+
valueColumn,
|
|
8510
|
+
rowGroupColumn,
|
|
8511
|
+
aggregator,
|
|
8512
|
+
showTotals = false,
|
|
8513
|
+
showGrandTotal = false
|
|
8514
|
+
} = config;
|
|
8515
|
+
if (!data || data.length === 0) {
|
|
8516
|
+
return {
|
|
8517
|
+
columns: [],
|
|
8518
|
+
rows: [],
|
|
8519
|
+
pivotValues: []
|
|
8520
|
+
};
|
|
8521
|
+
}
|
|
8522
|
+
const pivotValues = getUniqueValues(data, pivotColumn);
|
|
8523
|
+
const rowGroupValues = getUniqueValues(data, rowGroupColumn);
|
|
8524
|
+
const aggregatorFn = getAggregatorFn(aggregator);
|
|
8525
|
+
const groupedData = /* @__PURE__ */ new Map();
|
|
8526
|
+
rowGroupValues.forEach((rowValue) => {
|
|
8527
|
+
const pivotMap = /* @__PURE__ */ new Map();
|
|
8528
|
+
pivotValues.forEach((pivotValue) => {
|
|
8529
|
+
pivotMap.set(pivotValue, []);
|
|
8530
|
+
});
|
|
8531
|
+
groupedData.set(rowValue, pivotMap);
|
|
8532
|
+
});
|
|
8533
|
+
data.forEach((row) => {
|
|
8534
|
+
const rowValue = String(row[rowGroupColumn] ?? "");
|
|
8535
|
+
const pivotValue = String(row[pivotColumn] ?? "");
|
|
8536
|
+
const value = toNumber(row[valueColumn]);
|
|
8537
|
+
if (groupedData.has(rowValue)) {
|
|
8538
|
+
const pivotMap = groupedData.get(rowValue);
|
|
8539
|
+
if (pivotMap.has(pivotValue)) {
|
|
8540
|
+
pivotMap.get(pivotValue).push(value);
|
|
8541
|
+
}
|
|
8542
|
+
}
|
|
8543
|
+
});
|
|
8544
|
+
const columns = [
|
|
8545
|
+
{
|
|
8546
|
+
field: rowGroupColumn,
|
|
8547
|
+
headerName: formatHeaderName(rowGroupColumn),
|
|
8548
|
+
width: 180,
|
|
8549
|
+
sortable: true,
|
|
8550
|
+
filterable: true,
|
|
8551
|
+
isPivotColumn: false
|
|
8552
|
+
}
|
|
8553
|
+
];
|
|
8554
|
+
pivotValues.forEach((pivotValue) => {
|
|
8555
|
+
columns.push({
|
|
8556
|
+
field: pivotValue,
|
|
8557
|
+
headerName: pivotValue,
|
|
8558
|
+
width: 120,
|
|
8559
|
+
sortable: true,
|
|
8560
|
+
filterable: true,
|
|
8561
|
+
isPivotColumn: true
|
|
8562
|
+
});
|
|
8563
|
+
});
|
|
8564
|
+
if (showGrandTotal) {
|
|
8565
|
+
columns.push({
|
|
8566
|
+
field: "__grandTotal",
|
|
8567
|
+
headerName: "Grand Total",
|
|
8568
|
+
width: 120,
|
|
8569
|
+
sortable: true,
|
|
8570
|
+
filterable: false,
|
|
8571
|
+
isTotalColumn: true
|
|
8572
|
+
});
|
|
8573
|
+
}
|
|
8574
|
+
const rows = [];
|
|
8575
|
+
rowGroupValues.forEach((rowValue) => {
|
|
8576
|
+
const pivotMap = groupedData.get(rowValue);
|
|
8577
|
+
const row = {
|
|
8578
|
+
[rowGroupColumn]: rowValue,
|
|
8579
|
+
__id: rowValue
|
|
8580
|
+
// Add unique ID for grid
|
|
8581
|
+
};
|
|
8582
|
+
let grandTotal = 0;
|
|
8583
|
+
pivotValues.forEach((pivotValue) => {
|
|
8584
|
+
const values = pivotMap.get(pivotValue);
|
|
8585
|
+
const aggregatedValue = aggregatorFn(values);
|
|
8586
|
+
row[pivotValue] = aggregatedValue;
|
|
8587
|
+
grandTotal += aggregatedValue;
|
|
8588
|
+
});
|
|
8589
|
+
if (showGrandTotal) {
|
|
8590
|
+
row["__grandTotal"] = grandTotal;
|
|
8591
|
+
}
|
|
8592
|
+
rows.push(row);
|
|
8593
|
+
});
|
|
8594
|
+
let totalsRow;
|
|
8595
|
+
if (showTotals) {
|
|
8596
|
+
totalsRow = {
|
|
8597
|
+
[rowGroupColumn]: "Total",
|
|
8598
|
+
__id: "__totals",
|
|
8599
|
+
__isTotal: true
|
|
8600
|
+
};
|
|
8601
|
+
let overallGrandTotal = 0;
|
|
8602
|
+
pivotValues.forEach((pivotValue) => {
|
|
8603
|
+
const allValues = [];
|
|
8604
|
+
groupedData.forEach((pivotMap) => {
|
|
8605
|
+
const values = pivotMap.get(pivotValue);
|
|
8606
|
+
allValues.push(...values);
|
|
8607
|
+
});
|
|
8608
|
+
const total = aggregatorFn(allValues);
|
|
8609
|
+
totalsRow[pivotValue] = total;
|
|
8610
|
+
overallGrandTotal += total;
|
|
8611
|
+
});
|
|
8612
|
+
if (showGrandTotal) {
|
|
8613
|
+
totalsRow["__grandTotal"] = overallGrandTotal;
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
return {
|
|
8617
|
+
columns,
|
|
8618
|
+
rows,
|
|
8619
|
+
pivotValues,
|
|
8620
|
+
totalsRow
|
|
8621
|
+
};
|
|
8622
|
+
}
|
|
8623
|
+
function formatHeaderName(field) {
|
|
8624
|
+
return field.split(/(?=[A-Z])|_|-/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
8625
|
+
}
|
|
8626
|
+
function exportPivotToCSV(pivotResult) {
|
|
8627
|
+
const { columns, rows, totalsRow } = pivotResult;
|
|
8628
|
+
const headers = columns.map((col) => col.headerName).join(",");
|
|
8629
|
+
const dataRows = rows.map((row) => {
|
|
8630
|
+
return columns.map((col) => {
|
|
8631
|
+
const value = row[col.field];
|
|
8632
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
8633
|
+
return `"${value}"`;
|
|
8634
|
+
}
|
|
8635
|
+
return value ?? "";
|
|
8636
|
+
}).join(",");
|
|
8637
|
+
});
|
|
8638
|
+
if (totalsRow) {
|
|
8639
|
+
const totalRow = columns.map((col) => {
|
|
8640
|
+
const value = totalsRow[col.field];
|
|
8641
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
8642
|
+
return `"${value}"`;
|
|
8643
|
+
}
|
|
8644
|
+
return value ?? "";
|
|
8645
|
+
}).join(",");
|
|
8646
|
+
dataRows.push(totalRow);
|
|
8647
|
+
}
|
|
8648
|
+
return [headers, ...dataRows].join("\n");
|
|
8649
|
+
}
|
|
8650
|
+
function downloadCSV(csvContent, filename = "pivot-table.csv") {
|
|
8651
|
+
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
8652
|
+
const link = document.createElement("a");
|
|
8653
|
+
const url = URL.createObjectURL(blob);
|
|
8654
|
+
link.setAttribute("href", url);
|
|
8655
|
+
link.setAttribute("download", filename);
|
|
8656
|
+
link.style.visibility = "hidden";
|
|
8657
|
+
document.body.appendChild(link);
|
|
8658
|
+
link.click();
|
|
8659
|
+
document.body.removeChild(link);
|
|
8660
|
+
URL.revokeObjectURL(url);
|
|
8661
|
+
}
|
|
8662
|
+
|
|
8448
8663
|
// src/components/DataGrid/DataGrid.tsx
|
|
8449
8664
|
var DataGrid = (0, import_react23.forwardRef)(({
|
|
8450
8665
|
columns,
|
|
@@ -8459,6 +8674,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8459
8674
|
rowPinConfig,
|
|
8460
8675
|
contextMenuConfig,
|
|
8461
8676
|
tooltipConfig,
|
|
8677
|
+
pivotConfig,
|
|
8462
8678
|
tableId,
|
|
8463
8679
|
theme: _theme = "quartz",
|
|
8464
8680
|
densityMode: _densityMode = "normal",
|
|
@@ -8479,6 +8695,42 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8479
8695
|
);
|
|
8480
8696
|
const [internalRows, setInternalRows] = (0, import_react23.useState)(null);
|
|
8481
8697
|
const activeRows = internalRows !== null ? internalRows : rows;
|
|
8698
|
+
const { pivotedData, effectiveColumns } = (0, import_react23.useMemo)(() => {
|
|
8699
|
+
if (!pivotConfig) {
|
|
8700
|
+
return { pivotedData: activeRows, effectiveColumns: columns };
|
|
8701
|
+
}
|
|
8702
|
+
const pivotResult = buildPivot(activeRows, pivotConfig);
|
|
8703
|
+
console.log("Pivot Result:", {
|
|
8704
|
+
columnsCount: pivotResult.columns.length,
|
|
8705
|
+
columns: pivotResult.columns.map((c) => c.field),
|
|
8706
|
+
rowsCount: pivotResult.rows.length,
|
|
8707
|
+
sampleRow: pivotResult.rows[0]
|
|
8708
|
+
});
|
|
8709
|
+
const pivotColumns = pivotResult.columns.map((col) => ({
|
|
8710
|
+
field: col.field,
|
|
8711
|
+
headerName: col.headerName,
|
|
8712
|
+
width: col.width || 120,
|
|
8713
|
+
sortable: col.sortable !== false,
|
|
8714
|
+
filterable: col.filterable !== false,
|
|
8715
|
+
editable: false,
|
|
8716
|
+
renderCell: col.isTotalColumn || col.isPivotColumn ? (row) => {
|
|
8717
|
+
const value = row[col.field];
|
|
8718
|
+
if (typeof value === "number") {
|
|
8719
|
+
return /* @__PURE__ */ import_react23.default.createElement("span", { style: {
|
|
8720
|
+
fontWeight: col.isTotalColumn ? "700" : "600",
|
|
8721
|
+
color: col.isTotalColumn ? "#0f766e" : "#475569"
|
|
8722
|
+
} }, value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
|
|
8723
|
+
}
|
|
8724
|
+
return value;
|
|
8725
|
+
} : void 0
|
|
8726
|
+
}));
|
|
8727
|
+
const allRows = pivotResult.totalsRow ? [...pivotResult.rows, pivotResult.totalsRow] : pivotResult.rows;
|
|
8728
|
+
const rowsWithId = allRows.map((row, index) => ({
|
|
8729
|
+
...row,
|
|
8730
|
+
id: row.__id || row.id || `pivot-row-${index}`
|
|
8731
|
+
}));
|
|
8732
|
+
return { pivotedData: rowsWithId, effectiveColumns: pivotColumns };
|
|
8733
|
+
}, [activeRows, columns, pivotConfig]);
|
|
8482
8734
|
const rowsRef = (0, import_react23.useRef)(rows);
|
|
8483
8735
|
(0, import_react23.useEffect)(() => {
|
|
8484
8736
|
if (rows !== rowsRef.current) {
|
|
@@ -8581,8 +8833,8 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8581
8833
|
gridApiRef.current = new GridApiImpl(
|
|
8582
8834
|
state,
|
|
8583
8835
|
dispatch,
|
|
8584
|
-
|
|
8585
|
-
|
|
8836
|
+
effectiveColumns,
|
|
8837
|
+
pivotedData,
|
|
8586
8838
|
containerRef,
|
|
8587
8839
|
persistenceManager,
|
|
8588
8840
|
setInternalRows
|
|
@@ -8592,24 +8844,24 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8592
8844
|
(0, import_react23.useEffect)(() => {
|
|
8593
8845
|
if (gridApiRef.current && !gridApiRef.current.isDestroyed()) {
|
|
8594
8846
|
gridApiRef.current.updateState(state);
|
|
8595
|
-
gridApiRef.current.updateData(
|
|
8847
|
+
gridApiRef.current.updateData(effectiveColumns, pivotedData);
|
|
8596
8848
|
gridApiRef.current.updateCallbacks(setInternalRows);
|
|
8597
8849
|
}
|
|
8598
|
-
}, [state,
|
|
8850
|
+
}, [state, effectiveColumns, pivotedData, setInternalRows]);
|
|
8599
8851
|
(0, import_react23.useImperativeHandle)(ref, () => {
|
|
8600
8852
|
if (!gridApiRef.current || gridApiRef.current.isDestroyed()) {
|
|
8601
8853
|
gridApiRef.current = new GridApiImpl(
|
|
8602
8854
|
state,
|
|
8603
8855
|
dispatch,
|
|
8604
|
-
|
|
8605
|
-
|
|
8856
|
+
effectiveColumns,
|
|
8857
|
+
pivotedData,
|
|
8606
8858
|
containerRef,
|
|
8607
8859
|
persistenceManager,
|
|
8608
8860
|
setInternalRows
|
|
8609
8861
|
);
|
|
8610
8862
|
}
|
|
8611
8863
|
return gridApiRef.current;
|
|
8612
|
-
}, [state,
|
|
8864
|
+
}, [state, effectiveColumns, pivotedData, persistenceManager]);
|
|
8613
8865
|
const onGridReadyCalledRef = (0, import_react23.useRef)(false);
|
|
8614
8866
|
const onGridReadyCallbackRef = (0, import_react23.useRef)(onGridReady);
|
|
8615
8867
|
(0, import_react23.useEffect)(() => {
|
|
@@ -8690,9 +8942,11 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8690
8942
|
...middleColumns,
|
|
8691
8943
|
...pinnedRightFields
|
|
8692
8944
|
];
|
|
8945
|
+
console.log("Display column order:", displayColumnOrder, "from state.columnOrder:", state.columnOrder);
|
|
8693
8946
|
(0, import_react23.useEffect)(() => {
|
|
8694
|
-
|
|
8695
|
-
|
|
8947
|
+
console.log("Dispatching RESET_COLUMNS with", effectiveColumns.length, "columns:", effectiveColumns.map((c) => c.field));
|
|
8948
|
+
dispatch({ type: "RESET_COLUMNS", payload: effectiveColumns });
|
|
8949
|
+
}, [effectiveColumns]);
|
|
8696
8950
|
(0, import_react23.useEffect)(() => {
|
|
8697
8951
|
if (onSelectionChange) {
|
|
8698
8952
|
onSelectionChange(Array.from(state.selection.selectedRows));
|
|
@@ -8708,17 +8962,17 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8708
8962
|
}, [state.pinnedRowsTop, state.pinnedRowsBottom]);
|
|
8709
8963
|
(0, import_react23.useEffect)(() => {
|
|
8710
8964
|
if (state.sortConfig.field) {
|
|
8711
|
-
const column =
|
|
8965
|
+
const column = effectiveColumns.find((c) => c.field === state.sortConfig.field);
|
|
8712
8966
|
if (column) {
|
|
8713
8967
|
announceSorting(column.headerName, state.sortConfig.direction);
|
|
8714
8968
|
}
|
|
8715
8969
|
}
|
|
8716
|
-
}, [state.sortConfig.field, state.sortConfig.direction]);
|
|
8970
|
+
}, [state.sortConfig.field, state.sortConfig.direction, effectiveColumns]);
|
|
8717
8971
|
const sortedRows = (0, import_react23.useMemo)(() => {
|
|
8718
8972
|
if (!state.sortConfig.field || !state.sortConfig.direction) {
|
|
8719
|
-
return
|
|
8973
|
+
return pivotedData;
|
|
8720
8974
|
}
|
|
8721
|
-
const sorted = [...
|
|
8975
|
+
const sorted = [...pivotedData].sort((a, b) => {
|
|
8722
8976
|
const aValue = a[state.sortConfig.field];
|
|
8723
8977
|
const bValue = b[state.sortConfig.field];
|
|
8724
8978
|
if (aValue == null && bValue == null) return 0;
|
|
@@ -8735,7 +8989,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8735
8989
|
sorted.reverse();
|
|
8736
8990
|
}
|
|
8737
8991
|
return sorted;
|
|
8738
|
-
}, [
|
|
8992
|
+
}, [pivotedData, state.sortConfig]);
|
|
8739
8993
|
const filteredRows = (0, import_react23.useMemo)(() => {
|
|
8740
8994
|
if (!hasActiveFilters(state.filterConfig)) {
|
|
8741
8995
|
return sortedRows;
|
|
@@ -8881,7 +9135,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8881
9135
|
/* @__PURE__ */ import_react23.default.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", justifyContent: "space-between", paddingLeft: "16px", paddingRight: "16px", paddingTop: "10px", paddingBottom: "10px", backgroundColor: "var(--grid-bg-alt)", borderBottom: "var(--grid-border-width, 1px) solid var(--grid-border)", zIndex: 30 } }, /* @__PURE__ */ import_react23.default.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ import_react23.default.createElement(
|
|
8882
9136
|
ColumnChooser,
|
|
8883
9137
|
{
|
|
8884
|
-
columns,
|
|
9138
|
+
columns: effectiveColumns,
|
|
8885
9139
|
columnOrder: state.columnOrder,
|
|
8886
9140
|
hiddenColumns: state.hiddenColumns,
|
|
8887
9141
|
onToggleVisibility: (field) => dispatch({ type: "TOGGLE_COLUMN_VISIBILITY", payload: field }),
|
|
@@ -8891,7 +9145,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8891
9145
|
), /* @__PURE__ */ import_react23.default.createElement(
|
|
8892
9146
|
ExportMenu,
|
|
8893
9147
|
{
|
|
8894
|
-
columns,
|
|
9148
|
+
columns: effectiveColumns,
|
|
8895
9149
|
fullDataset: rows,
|
|
8896
9150
|
filteredData: filteredRows.filter((r) => !("isGroup" in r)),
|
|
8897
9151
|
selectedRows: state.selection.selectedRows,
|
|
@@ -8909,7 +9163,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8909
9163
|
/* @__PURE__ */ import_react23.default.createElement(
|
|
8910
9164
|
GroupByPanel,
|
|
8911
9165
|
{
|
|
8912
|
-
columns,
|
|
9166
|
+
columns: effectiveColumns,
|
|
8913
9167
|
groupBy: state.groupBy,
|
|
8914
9168
|
dispatch
|
|
8915
9169
|
}
|
|
@@ -8917,7 +9171,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8917
9171
|
/* @__PURE__ */ import_react23.default.createElement("div", { role: "rowgroup", style: { position: "sticky", top: 0, zIndex: 20, width: "100%" } }, /* @__PURE__ */ import_react23.default.createElement(
|
|
8918
9172
|
GridHeader,
|
|
8919
9173
|
{
|
|
8920
|
-
columns,
|
|
9174
|
+
columns: effectiveColumns,
|
|
8921
9175
|
columnOrder: state.columnOrder,
|
|
8922
9176
|
displayColumnOrder,
|
|
8923
9177
|
columnWidths: state.columnWidths,
|
|
@@ -8936,7 +9190,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8936
9190
|
), /* @__PURE__ */ import_react23.default.createElement(
|
|
8937
9191
|
ColumnFilters,
|
|
8938
9192
|
{
|
|
8939
|
-
columns,
|
|
9193
|
+
columns: effectiveColumns,
|
|
8940
9194
|
displayColumnOrder,
|
|
8941
9195
|
columnWidths: state.columnWidths,
|
|
8942
9196
|
filterConfig: state.filterConfig,
|
|
@@ -8949,7 +9203,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8949
9203
|
/* @__PURE__ */ import_react23.default.createElement(
|
|
8950
9204
|
GridBody,
|
|
8951
9205
|
{
|
|
8952
|
-
columns,
|
|
9206
|
+
columns: effectiveColumns,
|
|
8953
9207
|
rows: (virtualScrollConfig == null ? void 0 : virtualScrollConfig.enabled) ? unpinnedRows : paginatedRows,
|
|
8954
9208
|
pinnedRowsTop: pinnedRowsTopData,
|
|
8955
9209
|
pinnedRowsBottom: pinnedRowsBottomData,
|
|
@@ -8989,7 +9243,7 @@ var DataGrid = (0, import_react23.forwardRef)(({
|
|
|
8989
9243
|
(footerConfig == null ? void 0 : footerConfig.show) && footerConfig.aggregates && /* @__PURE__ */ import_react23.default.createElement(
|
|
8990
9244
|
GridFooter,
|
|
8991
9245
|
{
|
|
8992
|
-
columns,
|
|
9246
|
+
columns: effectiveColumns,
|
|
8993
9247
|
displayColumnOrder,
|
|
8994
9248
|
columnWidths: state.columnWidths,
|
|
8995
9249
|
aggregates: globalAggregates,
|
|
@@ -12157,6 +12411,1532 @@ function createMockFeed(config) {
|
|
|
12157
12411
|
createConnection: () => createMockWebSocket(feed)
|
|
12158
12412
|
};
|
|
12159
12413
|
}
|
|
12414
|
+
|
|
12415
|
+
// src/components/DataGrid/PivotToolbar.tsx
|
|
12416
|
+
var import_react33 = __toESM(require("react"), 1);
|
|
12417
|
+
var AGGREGATOR_OPTIONS = [
|
|
12418
|
+
{ value: "sum", label: "Sum" },
|
|
12419
|
+
{ value: "avg", label: "Average" },
|
|
12420
|
+
{ value: "count", label: "Count" },
|
|
12421
|
+
{ value: "min", label: "Minimum" },
|
|
12422
|
+
{ value: "max", label: "Maximum" }
|
|
12423
|
+
];
|
|
12424
|
+
var PivotToolbar = ({
|
|
12425
|
+
columns,
|
|
12426
|
+
pivotConfig,
|
|
12427
|
+
onPivotToggle,
|
|
12428
|
+
onConfigChange,
|
|
12429
|
+
isPivotMode = false,
|
|
12430
|
+
style,
|
|
12431
|
+
className
|
|
12432
|
+
}) => {
|
|
12433
|
+
const [isExpanded, setIsExpanded] = (0, import_react33.useState)(isPivotMode);
|
|
12434
|
+
const [rowGroupColumn, setRowGroupColumn] = (0, import_react33.useState)((pivotConfig == null ? void 0 : pivotConfig.rowGroupColumn) || "");
|
|
12435
|
+
const [pivotColumn, setPivotColumn] = (0, import_react33.useState)((pivotConfig == null ? void 0 : pivotConfig.pivotColumn) || "");
|
|
12436
|
+
const [valueColumn, setValueColumn] = (0, import_react33.useState)((pivotConfig == null ? void 0 : pivotConfig.valueColumn) || "");
|
|
12437
|
+
const [aggregator, setAggregator] = (0, import_react33.useState)(
|
|
12438
|
+
(pivotConfig == null ? void 0 : pivotConfig.aggregator) || "sum"
|
|
12439
|
+
);
|
|
12440
|
+
const [showTotals, setShowTotals] = (0, import_react33.useState)((pivotConfig == null ? void 0 : pivotConfig.showTotals) ?? true);
|
|
12441
|
+
const [showGrandTotal, setShowGrandTotal] = (0, import_react33.useState)((pivotConfig == null ? void 0 : pivotConfig.showGrandTotal) ?? true);
|
|
12442
|
+
const isConfigValid = (0, import_react33.useMemo)(() => {
|
|
12443
|
+
return rowGroupColumn && pivotColumn && valueColumn;
|
|
12444
|
+
}, [rowGroupColumn, pivotColumn, valueColumn]);
|
|
12445
|
+
const handleApply = () => {
|
|
12446
|
+
if (!isConfigValid) return;
|
|
12447
|
+
const config = {
|
|
12448
|
+
rowGroupColumn,
|
|
12449
|
+
pivotColumn,
|
|
12450
|
+
valueColumn,
|
|
12451
|
+
aggregator,
|
|
12452
|
+
showTotals,
|
|
12453
|
+
showGrandTotal
|
|
12454
|
+
};
|
|
12455
|
+
onConfigChange(config);
|
|
12456
|
+
onPivotToggle(true);
|
|
12457
|
+
};
|
|
12458
|
+
const handleClear = () => {
|
|
12459
|
+
setRowGroupColumn("");
|
|
12460
|
+
setPivotColumn("");
|
|
12461
|
+
setValueColumn("");
|
|
12462
|
+
setAggregator("sum");
|
|
12463
|
+
setShowTotals(true);
|
|
12464
|
+
setShowGrandTotal(true);
|
|
12465
|
+
onConfigChange(null);
|
|
12466
|
+
onPivotToggle(false);
|
|
12467
|
+
setIsExpanded(false);
|
|
12468
|
+
};
|
|
12469
|
+
const handleToggle = () => {
|
|
12470
|
+
setIsExpanded(!isExpanded);
|
|
12471
|
+
};
|
|
12472
|
+
return /* @__PURE__ */ import_react33.default.createElement(
|
|
12473
|
+
"div",
|
|
12474
|
+
{
|
|
12475
|
+
className,
|
|
12476
|
+
style: {
|
|
12477
|
+
backgroundColor: "#f8fafc",
|
|
12478
|
+
border: "1px solid #e2e8f0",
|
|
12479
|
+
borderRadius: "8px",
|
|
12480
|
+
padding: "12px",
|
|
12481
|
+
...style
|
|
12482
|
+
}
|
|
12483
|
+
},
|
|
12484
|
+
/* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: isExpanded ? "12px" : "0" } }, /* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ import_react33.default.createElement("span", { style: { fontSize: "20px" } }, "\u{1F4CA}"), /* @__PURE__ */ import_react33.default.createElement("h3", { style: { margin: 0, fontSize: "14px", fontWeight: "600", color: "#1e293b" } }, "Pivot Table"), isPivotMode && /* @__PURE__ */ import_react33.default.createElement("span", { style: {
|
|
12485
|
+
backgroundColor: "#10b981",
|
|
12486
|
+
color: "white",
|
|
12487
|
+
padding: "2px 8px",
|
|
12488
|
+
borderRadius: "12px",
|
|
12489
|
+
fontSize: "11px",
|
|
12490
|
+
fontWeight: "600"
|
|
12491
|
+
} }, "Active")), /* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "flex", gap: "8px" } }, isPivotMode && /* @__PURE__ */ import_react33.default.createElement(
|
|
12492
|
+
"button",
|
|
12493
|
+
{
|
|
12494
|
+
onClick: handleClear,
|
|
12495
|
+
style: {
|
|
12496
|
+
padding: "6px 12px",
|
|
12497
|
+
fontSize: "13px",
|
|
12498
|
+
fontWeight: "500",
|
|
12499
|
+
color: "#dc2626",
|
|
12500
|
+
backgroundColor: "#fee2e2",
|
|
12501
|
+
border: "1px solid #fecaca",
|
|
12502
|
+
borderRadius: "6px",
|
|
12503
|
+
cursor: "pointer",
|
|
12504
|
+
transition: "all 0.15s"
|
|
12505
|
+
},
|
|
12506
|
+
onMouseEnter: (e) => {
|
|
12507
|
+
e.currentTarget.style.backgroundColor = "#fecaca";
|
|
12508
|
+
},
|
|
12509
|
+
onMouseLeave: (e) => {
|
|
12510
|
+
e.currentTarget.style.backgroundColor = "#fee2e2";
|
|
12511
|
+
}
|
|
12512
|
+
},
|
|
12513
|
+
"Clear Pivot"
|
|
12514
|
+
), /* @__PURE__ */ import_react33.default.createElement(
|
|
12515
|
+
"button",
|
|
12516
|
+
{
|
|
12517
|
+
onClick: handleToggle,
|
|
12518
|
+
style: {
|
|
12519
|
+
padding: "6px 10px",
|
|
12520
|
+
fontSize: "13px",
|
|
12521
|
+
color: "#64748b",
|
|
12522
|
+
backgroundColor: "white",
|
|
12523
|
+
border: "1px solid #cbd5e1",
|
|
12524
|
+
borderRadius: "6px",
|
|
12525
|
+
cursor: "pointer",
|
|
12526
|
+
transition: "all 0.15s"
|
|
12527
|
+
},
|
|
12528
|
+
onMouseEnter: (e) => {
|
|
12529
|
+
e.currentTarget.style.backgroundColor = "#f1f5f9";
|
|
12530
|
+
},
|
|
12531
|
+
onMouseLeave: (e) => {
|
|
12532
|
+
e.currentTarget.style.backgroundColor = "white";
|
|
12533
|
+
}
|
|
12534
|
+
},
|
|
12535
|
+
isExpanded ? "\u25B2" : "\u25BC"
|
|
12536
|
+
))),
|
|
12537
|
+
isExpanded && /* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "12px" } }, /* @__PURE__ */ import_react33.default.createElement("div", null, /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Row Group By ", /* @__PURE__ */ import_react33.default.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ import_react33.default.createElement(
|
|
12538
|
+
"select",
|
|
12539
|
+
{
|
|
12540
|
+
value: rowGroupColumn,
|
|
12541
|
+
onChange: (e) => setRowGroupColumn(e.target.value),
|
|
12542
|
+
style: {
|
|
12543
|
+
width: "100%",
|
|
12544
|
+
padding: "8px",
|
|
12545
|
+
fontSize: "13px",
|
|
12546
|
+
border: "1px solid #cbd5e1",
|
|
12547
|
+
borderRadius: "6px",
|
|
12548
|
+
backgroundColor: "white",
|
|
12549
|
+
color: "#1e293b",
|
|
12550
|
+
cursor: "pointer"
|
|
12551
|
+
}
|
|
12552
|
+
},
|
|
12553
|
+
/* @__PURE__ */ import_react33.default.createElement("option", { value: "" }, "Select column..."),
|
|
12554
|
+
columns.map((col) => /* @__PURE__ */ import_react33.default.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12555
|
+
)), /* @__PURE__ */ import_react33.default.createElement("div", null, /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Pivot Column ", /* @__PURE__ */ import_react33.default.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ import_react33.default.createElement(
|
|
12556
|
+
"select",
|
|
12557
|
+
{
|
|
12558
|
+
value: pivotColumn,
|
|
12559
|
+
onChange: (e) => setPivotColumn(e.target.value),
|
|
12560
|
+
style: {
|
|
12561
|
+
width: "100%",
|
|
12562
|
+
padding: "8px",
|
|
12563
|
+
fontSize: "13px",
|
|
12564
|
+
border: "1px solid #cbd5e1",
|
|
12565
|
+
borderRadius: "6px",
|
|
12566
|
+
backgroundColor: "white",
|
|
12567
|
+
color: "#1e293b",
|
|
12568
|
+
cursor: "pointer"
|
|
12569
|
+
}
|
|
12570
|
+
},
|
|
12571
|
+
/* @__PURE__ */ import_react33.default.createElement("option", { value: "" }, "Select column..."),
|
|
12572
|
+
columns.map((col) => /* @__PURE__ */ import_react33.default.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12573
|
+
)), /* @__PURE__ */ import_react33.default.createElement("div", null, /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Value Column ", /* @__PURE__ */ import_react33.default.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ import_react33.default.createElement(
|
|
12574
|
+
"select",
|
|
12575
|
+
{
|
|
12576
|
+
value: valueColumn,
|
|
12577
|
+
onChange: (e) => setValueColumn(e.target.value),
|
|
12578
|
+
style: {
|
|
12579
|
+
width: "100%",
|
|
12580
|
+
padding: "8px",
|
|
12581
|
+
fontSize: "13px",
|
|
12582
|
+
border: "1px solid #cbd5e1",
|
|
12583
|
+
borderRadius: "6px",
|
|
12584
|
+
backgroundColor: "white",
|
|
12585
|
+
color: "#1e293b",
|
|
12586
|
+
cursor: "pointer"
|
|
12587
|
+
}
|
|
12588
|
+
},
|
|
12589
|
+
/* @__PURE__ */ import_react33.default.createElement("option", { value: "" }, "Select column..."),
|
|
12590
|
+
columns.map((col) => /* @__PURE__ */ import_react33.default.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12591
|
+
)), /* @__PURE__ */ import_react33.default.createElement("div", null, /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Aggregation"), /* @__PURE__ */ import_react33.default.createElement(
|
|
12592
|
+
"select",
|
|
12593
|
+
{
|
|
12594
|
+
value: aggregator,
|
|
12595
|
+
onChange: (e) => setAggregator(e.target.value),
|
|
12596
|
+
style: {
|
|
12597
|
+
width: "100%",
|
|
12598
|
+
padding: "8px",
|
|
12599
|
+
fontSize: "13px",
|
|
12600
|
+
border: "1px solid #cbd5e1",
|
|
12601
|
+
borderRadius: "6px",
|
|
12602
|
+
backgroundColor: "white",
|
|
12603
|
+
color: "#1e293b",
|
|
12604
|
+
cursor: "pointer"
|
|
12605
|
+
}
|
|
12606
|
+
},
|
|
12607
|
+
AGGREGATOR_OPTIONS.map((opt) => /* @__PURE__ */ import_react33.default.createElement("option", { key: opt.value, value: opt.value }, opt.label))
|
|
12608
|
+
)), /* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "8px", justifyContent: "center" } }, /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ import_react33.default.createElement(
|
|
12609
|
+
"input",
|
|
12610
|
+
{
|
|
12611
|
+
type: "checkbox",
|
|
12612
|
+
checked: showTotals,
|
|
12613
|
+
onChange: (e) => setShowTotals(e.target.checked),
|
|
12614
|
+
style: { cursor: "pointer" }
|
|
12615
|
+
}
|
|
12616
|
+
), "Show Totals Row"), /* @__PURE__ */ import_react33.default.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ import_react33.default.createElement(
|
|
12617
|
+
"input",
|
|
12618
|
+
{
|
|
12619
|
+
type: "checkbox",
|
|
12620
|
+
checked: showGrandTotal,
|
|
12621
|
+
onChange: (e) => setShowGrandTotal(e.target.checked),
|
|
12622
|
+
style: { cursor: "pointer" }
|
|
12623
|
+
}
|
|
12624
|
+
), "Show Grand Total Column")), /* @__PURE__ */ import_react33.default.createElement("div", { style: { display: "flex", alignItems: "flex-end" } }, /* @__PURE__ */ import_react33.default.createElement(
|
|
12625
|
+
"button",
|
|
12626
|
+
{
|
|
12627
|
+
onClick: handleApply,
|
|
12628
|
+
disabled: !isConfigValid,
|
|
12629
|
+
style: {
|
|
12630
|
+
width: "100%",
|
|
12631
|
+
padding: "10px",
|
|
12632
|
+
fontSize: "14px",
|
|
12633
|
+
fontWeight: "600",
|
|
12634
|
+
color: "white",
|
|
12635
|
+
backgroundColor: isConfigValid ? "#2563eb" : "#94a3b8",
|
|
12636
|
+
border: "none",
|
|
12637
|
+
borderRadius: "6px",
|
|
12638
|
+
cursor: isConfigValid ? "pointer" : "not-allowed",
|
|
12639
|
+
transition: "all 0.15s",
|
|
12640
|
+
boxShadow: isConfigValid ? "0 2px 4px rgba(37, 99, 235, 0.2)" : "none"
|
|
12641
|
+
},
|
|
12642
|
+
onMouseEnter: (e) => {
|
|
12643
|
+
if (isConfigValid) {
|
|
12644
|
+
e.currentTarget.style.backgroundColor = "#1d4ed8";
|
|
12645
|
+
}
|
|
12646
|
+
},
|
|
12647
|
+
onMouseLeave: (e) => {
|
|
12648
|
+
if (isConfigValid) {
|
|
12649
|
+
e.currentTarget.style.backgroundColor = "#2563eb";
|
|
12650
|
+
}
|
|
12651
|
+
}
|
|
12652
|
+
},
|
|
12653
|
+
"Apply Pivot"
|
|
12654
|
+
))),
|
|
12655
|
+
isExpanded && /* @__PURE__ */ import_react33.default.createElement("div", { style: {
|
|
12656
|
+
marginTop: "12px",
|
|
12657
|
+
padding: "10px",
|
|
12658
|
+
backgroundColor: "#eff6ff",
|
|
12659
|
+
border: "1px solid #bfdbfe",
|
|
12660
|
+
borderRadius: "6px",
|
|
12661
|
+
fontSize: "12px",
|
|
12662
|
+
color: "#1e40af"
|
|
12663
|
+
} }, /* @__PURE__ */ import_react33.default.createElement("strong", null, "\u{1F4A1} Tip:"), " Select a Row Group column to organize data by, a Pivot column whose values become new columns, and a Value column to aggregate.")
|
|
12664
|
+
);
|
|
12665
|
+
};
|
|
12666
|
+
|
|
12667
|
+
// src/editors/editorUtils.ts
|
|
12668
|
+
var import_react34 = require("react");
|
|
12669
|
+
function useEditorKeyboardNavigation(handlers, config = {}) {
|
|
12670
|
+
const {
|
|
12671
|
+
commitOnEnter = true,
|
|
12672
|
+
cancelOnEscape = true,
|
|
12673
|
+
commitOnTab = true,
|
|
12674
|
+
preventDefault = true,
|
|
12675
|
+
stopPropagation = true
|
|
12676
|
+
} = config;
|
|
12677
|
+
const handleKeyDown = (0, import_react34.useCallback)(
|
|
12678
|
+
(event) => {
|
|
12679
|
+
let handled = false;
|
|
12680
|
+
switch (event.key) {
|
|
12681
|
+
case "Enter":
|
|
12682
|
+
if (commitOnEnter && handlers.onEnter) {
|
|
12683
|
+
handlers.onEnter();
|
|
12684
|
+
handled = true;
|
|
12685
|
+
}
|
|
12686
|
+
break;
|
|
12687
|
+
case "Escape":
|
|
12688
|
+
if (cancelOnEscape && handlers.onEscape) {
|
|
12689
|
+
handlers.onEscape();
|
|
12690
|
+
handled = true;
|
|
12691
|
+
}
|
|
12692
|
+
break;
|
|
12693
|
+
case "Tab":
|
|
12694
|
+
if (commitOnTab && handlers.onTab) {
|
|
12695
|
+
handlers.onTab(event.shiftKey);
|
|
12696
|
+
handled = true;
|
|
12697
|
+
}
|
|
12698
|
+
break;
|
|
12699
|
+
case "ArrowUp":
|
|
12700
|
+
if (handlers.onArrowUp) {
|
|
12701
|
+
handlers.onArrowUp();
|
|
12702
|
+
handled = true;
|
|
12703
|
+
}
|
|
12704
|
+
break;
|
|
12705
|
+
case "ArrowDown":
|
|
12706
|
+
if (handlers.onArrowDown) {
|
|
12707
|
+
handlers.onArrowDown();
|
|
12708
|
+
handled = true;
|
|
12709
|
+
}
|
|
12710
|
+
break;
|
|
12711
|
+
}
|
|
12712
|
+
if (handled) {
|
|
12713
|
+
if (preventDefault) {
|
|
12714
|
+
event.preventDefault();
|
|
12715
|
+
}
|
|
12716
|
+
if (stopPropagation) {
|
|
12717
|
+
event.stopPropagation();
|
|
12718
|
+
}
|
|
12719
|
+
}
|
|
12720
|
+
},
|
|
12721
|
+
[
|
|
12722
|
+
handlers,
|
|
12723
|
+
commitOnEnter,
|
|
12724
|
+
cancelOnEscape,
|
|
12725
|
+
commitOnTab,
|
|
12726
|
+
preventDefault,
|
|
12727
|
+
stopPropagation
|
|
12728
|
+
]
|
|
12729
|
+
);
|
|
12730
|
+
return { handleKeyDown };
|
|
12731
|
+
}
|
|
12732
|
+
function useEditorAutoFocus(autoFocus = true, selectAll = false) {
|
|
12733
|
+
const elementRef = (0, import_react34.useRef)(null);
|
|
12734
|
+
(0, import_react34.useEffect)(() => {
|
|
12735
|
+
if (autoFocus && elementRef.current) {
|
|
12736
|
+
elementRef.current.focus();
|
|
12737
|
+
if (selectAll && elementRef.current instanceof HTMLInputElement) {
|
|
12738
|
+
elementRef.current.select();
|
|
12739
|
+
}
|
|
12740
|
+
}
|
|
12741
|
+
}, [autoFocus, selectAll]);
|
|
12742
|
+
return elementRef;
|
|
12743
|
+
}
|
|
12744
|
+
function useEditorClickOutside(ref, onClickOutside, enabled = true) {
|
|
12745
|
+
(0, import_react34.useEffect)(() => {
|
|
12746
|
+
if (!enabled) return;
|
|
12747
|
+
const handleClickOutside = (event) => {
|
|
12748
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
12749
|
+
onClickOutside();
|
|
12750
|
+
}
|
|
12751
|
+
};
|
|
12752
|
+
const timeoutId = setTimeout(() => {
|
|
12753
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
12754
|
+
}, 0);
|
|
12755
|
+
return () => {
|
|
12756
|
+
clearTimeout(timeoutId);
|
|
12757
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
12758
|
+
};
|
|
12759
|
+
}, [ref, onClickOutside, enabled]);
|
|
12760
|
+
}
|
|
12761
|
+
function usePopupPosition(anchorRef, popupRef, isOpen, placement = "auto") {
|
|
12762
|
+
(0, import_react34.useEffect)(() => {
|
|
12763
|
+
if (!isOpen || !anchorRef.current || !popupRef.current) return;
|
|
12764
|
+
const anchor = anchorRef.current;
|
|
12765
|
+
const popup = popupRef.current;
|
|
12766
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
12767
|
+
const popupRect = popup.getBoundingClientRect();
|
|
12768
|
+
const viewportHeight = window.innerHeight;
|
|
12769
|
+
const viewportWidth = window.innerWidth;
|
|
12770
|
+
let top = 0;
|
|
12771
|
+
let left = 0;
|
|
12772
|
+
let actualPlacement = placement;
|
|
12773
|
+
if (placement === "auto") {
|
|
12774
|
+
const spaceBelow = viewportHeight - anchorRect.bottom;
|
|
12775
|
+
const spaceAbove = anchorRect.top;
|
|
12776
|
+
const spaceRight = viewportWidth - anchorRect.right;
|
|
12777
|
+
const spaceLeft = anchorRect.left;
|
|
12778
|
+
if (spaceBelow >= popupRect.height || spaceBelow >= spaceAbove) {
|
|
12779
|
+
actualPlacement = "bottom";
|
|
12780
|
+
} else if (spaceAbove >= popupRect.height) {
|
|
12781
|
+
actualPlacement = "top";
|
|
12782
|
+
} else if (spaceRight >= popupRect.width) {
|
|
12783
|
+
actualPlacement = "right";
|
|
12784
|
+
} else if (spaceLeft >= popupRect.width) {
|
|
12785
|
+
actualPlacement = "left";
|
|
12786
|
+
} else {
|
|
12787
|
+
actualPlacement = "bottom";
|
|
12788
|
+
}
|
|
12789
|
+
}
|
|
12790
|
+
switch (actualPlacement) {
|
|
12791
|
+
case "bottom":
|
|
12792
|
+
top = anchorRect.bottom + window.scrollY;
|
|
12793
|
+
left = anchorRect.left + window.scrollX;
|
|
12794
|
+
break;
|
|
12795
|
+
case "top":
|
|
12796
|
+
top = anchorRect.top + window.scrollY - popupRect.height;
|
|
12797
|
+
left = anchorRect.left + window.scrollX;
|
|
12798
|
+
break;
|
|
12799
|
+
case "right":
|
|
12800
|
+
top = anchorRect.top + window.scrollY;
|
|
12801
|
+
left = anchorRect.right + window.scrollX;
|
|
12802
|
+
break;
|
|
12803
|
+
case "left":
|
|
12804
|
+
top = anchorRect.top + window.scrollY;
|
|
12805
|
+
left = anchorRect.left + window.scrollX - popupRect.width;
|
|
12806
|
+
break;
|
|
12807
|
+
}
|
|
12808
|
+
const margin = 8;
|
|
12809
|
+
if (left + popupRect.width > viewportWidth) {
|
|
12810
|
+
left = viewportWidth - popupRect.width - margin;
|
|
12811
|
+
}
|
|
12812
|
+
if (left < margin) {
|
|
12813
|
+
left = margin;
|
|
12814
|
+
}
|
|
12815
|
+
if (top + popupRect.height > viewportHeight + window.scrollY) {
|
|
12816
|
+
top = viewportHeight + window.scrollY - popupRect.height - margin;
|
|
12817
|
+
}
|
|
12818
|
+
if (top < window.scrollY + margin) {
|
|
12819
|
+
top = window.scrollY + margin;
|
|
12820
|
+
}
|
|
12821
|
+
popup.style.top = `${top}px`;
|
|
12822
|
+
popup.style.left = `${left}px`;
|
|
12823
|
+
popup.style.minWidth = `${anchorRect.width}px`;
|
|
12824
|
+
}, [isOpen, anchorRef, popupRef, placement]);
|
|
12825
|
+
}
|
|
12826
|
+
function debounce2(func, wait) {
|
|
12827
|
+
let timeout = null;
|
|
12828
|
+
return function executedFunction(...args) {
|
|
12829
|
+
const later = () => {
|
|
12830
|
+
timeout = null;
|
|
12831
|
+
func(...args);
|
|
12832
|
+
};
|
|
12833
|
+
if (timeout) {
|
|
12834
|
+
clearTimeout(timeout);
|
|
12835
|
+
}
|
|
12836
|
+
timeout = setTimeout(later, wait);
|
|
12837
|
+
};
|
|
12838
|
+
}
|
|
12839
|
+
function formatNumber(value, decimals = 0, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12840
|
+
const fixed = value.toFixed(decimals);
|
|
12841
|
+
const [integerPart, decimalPart] = fixed.split(".");
|
|
12842
|
+
const formattedInteger = integerPart.replace(
|
|
12843
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
12844
|
+
thousandsSeparator
|
|
12845
|
+
);
|
|
12846
|
+
return decimalPart ? `${formattedInteger}${decimalSeparator}${decimalPart}` : formattedInteger;
|
|
12847
|
+
}
|
|
12848
|
+
function parseFormattedNumber(value, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12849
|
+
if (!value || typeof value !== "string") return null;
|
|
12850
|
+
const normalized = value.replace(new RegExp(`\\${thousandsSeparator}`, "g"), "").replace(new RegExp(`\\${decimalSeparator}`), ".");
|
|
12851
|
+
const parsed = parseFloat(normalized);
|
|
12852
|
+
return isNaN(parsed) ? null : parsed;
|
|
12853
|
+
}
|
|
12854
|
+
function filterOptions(options, searchQuery) {
|
|
12855
|
+
if (!searchQuery.trim()) return options;
|
|
12856
|
+
const lowerQuery = searchQuery.toLowerCase();
|
|
12857
|
+
return options.filter(
|
|
12858
|
+
(option) => option.label.toLowerCase().includes(lowerQuery)
|
|
12859
|
+
);
|
|
12860
|
+
}
|
|
12861
|
+
|
|
12862
|
+
// src/editors/RichSelectEditor.tsx
|
|
12863
|
+
var import_react35 = __toESM(require("react"), 1);
|
|
12864
|
+
function RichSelectEditor(props) {
|
|
12865
|
+
const {
|
|
12866
|
+
value,
|
|
12867
|
+
onChange,
|
|
12868
|
+
onCommit,
|
|
12869
|
+
onCancel,
|
|
12870
|
+
autoFocus = true,
|
|
12871
|
+
options,
|
|
12872
|
+
placeholder = "Select...",
|
|
12873
|
+
allowClear = false,
|
|
12874
|
+
filterable = true,
|
|
12875
|
+
renderOptionLabel,
|
|
12876
|
+
maxDropdownHeight = 300
|
|
12877
|
+
} = props;
|
|
12878
|
+
const [isOpen] = (0, import_react35.useState)(true);
|
|
12879
|
+
const [searchQuery, setSearchQuery] = (0, import_react35.useState)("");
|
|
12880
|
+
const [focusedIndex, setFocusedIndex] = (0, import_react35.useState)(-1);
|
|
12881
|
+
const containerRef = (0, import_react35.useRef)(null);
|
|
12882
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
12883
|
+
const dropdownRef = (0, import_react35.useRef)(null);
|
|
12884
|
+
const optionRefs = (0, import_react35.useRef)([]);
|
|
12885
|
+
const filteredOptions = (0, import_react35.useMemo)(
|
|
12886
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
12887
|
+
[options, searchQuery, filterable]
|
|
12888
|
+
);
|
|
12889
|
+
const selectedOption = (0, import_react35.useMemo)(
|
|
12890
|
+
() => options.find((opt) => opt.value === value),
|
|
12891
|
+
[options, value]
|
|
12892
|
+
);
|
|
12893
|
+
(0, import_react35.useEffect)(() => {
|
|
12894
|
+
if (selectedOption) {
|
|
12895
|
+
const index = filteredOptions.findIndex(
|
|
12896
|
+
(opt) => opt.value === selectedOption.value
|
|
12897
|
+
);
|
|
12898
|
+
if (index !== -1 && focusedIndex === -1) {
|
|
12899
|
+
queueMicrotask(() => setFocusedIndex(index));
|
|
12900
|
+
}
|
|
12901
|
+
}
|
|
12902
|
+
}, [selectedOption, filteredOptions]);
|
|
12903
|
+
(0, import_react35.useEffect)(() => {
|
|
12904
|
+
var _a;
|
|
12905
|
+
if (focusedIndex >= 0 && optionRefs.current[focusedIndex]) {
|
|
12906
|
+
(_a = optionRefs.current[focusedIndex]) == null ? void 0 : _a.scrollIntoView({
|
|
12907
|
+
block: "nearest",
|
|
12908
|
+
behavior: "smooth"
|
|
12909
|
+
});
|
|
12910
|
+
}
|
|
12911
|
+
}, [focusedIndex]);
|
|
12912
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
12913
|
+
useEditorClickOutside(
|
|
12914
|
+
containerRef,
|
|
12915
|
+
() => {
|
|
12916
|
+
if (isOpen) {
|
|
12917
|
+
onCommit();
|
|
12918
|
+
}
|
|
12919
|
+
},
|
|
12920
|
+
true
|
|
12921
|
+
);
|
|
12922
|
+
const handleSelectOption = (option) => {
|
|
12923
|
+
if (!option.disabled) {
|
|
12924
|
+
onChange(option.value);
|
|
12925
|
+
onCommit();
|
|
12926
|
+
}
|
|
12927
|
+
};
|
|
12928
|
+
const handleClear = (e) => {
|
|
12929
|
+
e.stopPropagation();
|
|
12930
|
+
onChange(null);
|
|
12931
|
+
setSearchQuery("");
|
|
12932
|
+
setFocusedIndex(-1);
|
|
12933
|
+
};
|
|
12934
|
+
const handleArrowDown = () => {
|
|
12935
|
+
setFocusedIndex((prev) => {
|
|
12936
|
+
var _a;
|
|
12937
|
+
let next = prev + 1;
|
|
12938
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12939
|
+
next++;
|
|
12940
|
+
}
|
|
12941
|
+
return next < filteredOptions.length ? next : prev;
|
|
12942
|
+
});
|
|
12943
|
+
};
|
|
12944
|
+
const handleArrowUp = () => {
|
|
12945
|
+
setFocusedIndex((prev) => {
|
|
12946
|
+
var _a;
|
|
12947
|
+
let next = prev - 1;
|
|
12948
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12949
|
+
next--;
|
|
12950
|
+
}
|
|
12951
|
+
return next >= 0 ? next : prev;
|
|
12952
|
+
});
|
|
12953
|
+
};
|
|
12954
|
+
const handleEnter = () => {
|
|
12955
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
12956
|
+
const option = filteredOptions[focusedIndex];
|
|
12957
|
+
if (option && !option.disabled) {
|
|
12958
|
+
handleSelectOption(option);
|
|
12959
|
+
}
|
|
12960
|
+
} else {
|
|
12961
|
+
onCommit();
|
|
12962
|
+
}
|
|
12963
|
+
};
|
|
12964
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
12965
|
+
{
|
|
12966
|
+
onEnter: handleEnter,
|
|
12967
|
+
onEscape: onCancel,
|
|
12968
|
+
onArrowUp: handleArrowUp,
|
|
12969
|
+
onArrowDown: handleArrowDown
|
|
12970
|
+
},
|
|
12971
|
+
{
|
|
12972
|
+
commitOnTab: true,
|
|
12973
|
+
commitOnBlur: false
|
|
12974
|
+
}
|
|
12975
|
+
);
|
|
12976
|
+
const renderLabel = (option) => {
|
|
12977
|
+
if (renderOptionLabel) {
|
|
12978
|
+
return renderOptionLabel(option);
|
|
12979
|
+
}
|
|
12980
|
+
return /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ import_react35.default.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-text" }, /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-label" }, option.label), option.description && /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-description" }, option.description)));
|
|
12981
|
+
};
|
|
12982
|
+
return /* @__PURE__ */ import_react35.default.createElement(
|
|
12983
|
+
"div",
|
|
12984
|
+
{
|
|
12985
|
+
ref: containerRef,
|
|
12986
|
+
className: "editor-container editor-richselect-container",
|
|
12987
|
+
onKeyDown: handleKeyDown
|
|
12988
|
+
},
|
|
12989
|
+
/* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ import_react35.default.createElement(
|
|
12990
|
+
"input",
|
|
12991
|
+
{
|
|
12992
|
+
ref: inputRef,
|
|
12993
|
+
type: "text",
|
|
12994
|
+
className: "editor-input editor-richselect-input",
|
|
12995
|
+
value: filterable ? searchQuery : (selectedOption == null ? void 0 : selectedOption.label) || "",
|
|
12996
|
+
onChange: (e) => {
|
|
12997
|
+
if (filterable) {
|
|
12998
|
+
setSearchQuery(e.target.value);
|
|
12999
|
+
setFocusedIndex(-1);
|
|
13000
|
+
}
|
|
13001
|
+
},
|
|
13002
|
+
placeholder: (selectedOption == null ? void 0 : selectedOption.label) || placeholder,
|
|
13003
|
+
"aria-label": "Select option",
|
|
13004
|
+
"aria-expanded": isOpen,
|
|
13005
|
+
"aria-autocomplete": "list",
|
|
13006
|
+
"aria-controls": "richselect-dropdown",
|
|
13007
|
+
autoComplete: "off"
|
|
13008
|
+
}
|
|
13009
|
+
), /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-input-actions" }, allowClear && value !== null && value !== void 0 && /* @__PURE__ */ import_react35.default.createElement(
|
|
13010
|
+
"button",
|
|
13011
|
+
{
|
|
13012
|
+
type: "button",
|
|
13013
|
+
className: "editor-clear-btn",
|
|
13014
|
+
onClick: handleClear,
|
|
13015
|
+
"aria-label": "Clear selection",
|
|
13016
|
+
tabIndex: -1
|
|
13017
|
+
},
|
|
13018
|
+
"\xD7"
|
|
13019
|
+
), /* @__PURE__ */ import_react35.default.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC"))),
|
|
13020
|
+
isOpen && /* @__PURE__ */ import_react35.default.createElement(
|
|
13021
|
+
"div",
|
|
13022
|
+
{
|
|
13023
|
+
ref: dropdownRef,
|
|
13024
|
+
id: "richselect-dropdown",
|
|
13025
|
+
className: "editor-dropdown",
|
|
13026
|
+
role: "listbox",
|
|
13027
|
+
style: { maxHeight: maxDropdownHeight }
|
|
13028
|
+
},
|
|
13029
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => /* @__PURE__ */ import_react35.default.createElement(
|
|
13030
|
+
"div",
|
|
13031
|
+
{
|
|
13032
|
+
key: option.value,
|
|
13033
|
+
ref: (el) => {
|
|
13034
|
+
optionRefs.current[index] = el;
|
|
13035
|
+
},
|
|
13036
|
+
className: `editor-dropdown-option ${option.value === value ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
13037
|
+
role: "option",
|
|
13038
|
+
"aria-selected": option.value === value,
|
|
13039
|
+
"aria-disabled": option.disabled,
|
|
13040
|
+
onClick: () => handleSelectOption(option),
|
|
13041
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
13042
|
+
},
|
|
13043
|
+
renderLabel(option)
|
|
13044
|
+
))
|
|
13045
|
+
)
|
|
13046
|
+
);
|
|
13047
|
+
}
|
|
13048
|
+
RichSelectEditor.displayName = "RichSelectEditor";
|
|
13049
|
+
|
|
13050
|
+
// src/editors/DateEditor.tsx
|
|
13051
|
+
var import_react36 = __toESM(require("react"), 1);
|
|
13052
|
+
function DateEditor(props) {
|
|
13053
|
+
const {
|
|
13054
|
+
value,
|
|
13055
|
+
onChange,
|
|
13056
|
+
onCommit,
|
|
13057
|
+
onCancel,
|
|
13058
|
+
autoFocus = true,
|
|
13059
|
+
dateFormat = "yyyy-MM-dd",
|
|
13060
|
+
showTime = false,
|
|
13061
|
+
minDate,
|
|
13062
|
+
maxDate,
|
|
13063
|
+
autoCommit = false
|
|
13064
|
+
} = props;
|
|
13065
|
+
const [isOpen] = (0, import_react36.useState)(true);
|
|
13066
|
+
const [inputValue, setInputValue] = (0, import_react36.useState)("");
|
|
13067
|
+
const [viewDate, setViewDate] = (0, import_react36.useState)(/* @__PURE__ */ new Date());
|
|
13068
|
+
const containerRef = (0, import_react36.useRef)(null);
|
|
13069
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
13070
|
+
const calendarRef = (0, import_react36.useRef)(null);
|
|
13071
|
+
const parsedValue = (0, import_react36.useMemo)(() => {
|
|
13072
|
+
if (!value) return null;
|
|
13073
|
+
if (value instanceof Date) return value;
|
|
13074
|
+
const parsed = new Date(value);
|
|
13075
|
+
return isNaN(parsed.getTime()) ? null : parsed;
|
|
13076
|
+
}, [value]);
|
|
13077
|
+
import_react36.default.useEffect(() => {
|
|
13078
|
+
if (parsedValue) {
|
|
13079
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
13080
|
+
setViewDate(parsedValue);
|
|
13081
|
+
}
|
|
13082
|
+
}, [parsedValue, dateFormat, showTime]);
|
|
13083
|
+
usePopupPosition(containerRef, calendarRef, isOpen, "auto");
|
|
13084
|
+
useEditorClickOutside(containerRef, () => {
|
|
13085
|
+
if (isOpen) {
|
|
13086
|
+
onCommit();
|
|
13087
|
+
}
|
|
13088
|
+
}, true);
|
|
13089
|
+
const handleSelectDate = (date) => {
|
|
13090
|
+
const newDate = new Date(date);
|
|
13091
|
+
if (parsedValue && !showTime) {
|
|
13092
|
+
newDate.setHours(parsedValue.getHours());
|
|
13093
|
+
newDate.setMinutes(parsedValue.getMinutes());
|
|
13094
|
+
newDate.setSeconds(parsedValue.getSeconds());
|
|
13095
|
+
}
|
|
13096
|
+
onChange(newDate);
|
|
13097
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
13098
|
+
if (autoCommit && !showTime) {
|
|
13099
|
+
onCommit();
|
|
13100
|
+
}
|
|
13101
|
+
};
|
|
13102
|
+
const handleTimeChange = (hours, minutes) => {
|
|
13103
|
+
const newDate = parsedValue ? new Date(parsedValue) : /* @__PURE__ */ new Date();
|
|
13104
|
+
newDate.setHours(hours);
|
|
13105
|
+
newDate.setMinutes(minutes);
|
|
13106
|
+
onChange(newDate);
|
|
13107
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
13108
|
+
};
|
|
13109
|
+
const handleInputChange = (e) => {
|
|
13110
|
+
const newValue = e.target.value;
|
|
13111
|
+
setInputValue(newValue);
|
|
13112
|
+
const parsed = parseDate(newValue, dateFormat);
|
|
13113
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
13114
|
+
onChange(parsed);
|
|
13115
|
+
setViewDate(parsed);
|
|
13116
|
+
}
|
|
13117
|
+
};
|
|
13118
|
+
const handleInputBlur = () => {
|
|
13119
|
+
if (parsedValue) {
|
|
13120
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
13121
|
+
}
|
|
13122
|
+
};
|
|
13123
|
+
const handlePrevMonth = () => {
|
|
13124
|
+
setViewDate((prev) => {
|
|
13125
|
+
const newDate = new Date(prev);
|
|
13126
|
+
newDate.setMonth(newDate.getMonth() - 1);
|
|
13127
|
+
return newDate;
|
|
13128
|
+
});
|
|
13129
|
+
};
|
|
13130
|
+
const handleNextMonth = () => {
|
|
13131
|
+
setViewDate((prev) => {
|
|
13132
|
+
const newDate = new Date(prev);
|
|
13133
|
+
newDate.setMonth(newDate.getMonth() + 1);
|
|
13134
|
+
return newDate;
|
|
13135
|
+
});
|
|
13136
|
+
};
|
|
13137
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
13138
|
+
{
|
|
13139
|
+
onEnter: onCommit,
|
|
13140
|
+
onEscape: onCancel
|
|
13141
|
+
},
|
|
13142
|
+
{
|
|
13143
|
+
commitOnTab: true,
|
|
13144
|
+
commitOnBlur: false
|
|
13145
|
+
}
|
|
13146
|
+
);
|
|
13147
|
+
const calendarDays = (0, import_react36.useMemo)(() => {
|
|
13148
|
+
return generateCalendarDays(viewDate, parsedValue, minDate, maxDate);
|
|
13149
|
+
}, [viewDate, parsedValue, minDate, maxDate]);
|
|
13150
|
+
return /* @__PURE__ */ import_react36.default.createElement(
|
|
13151
|
+
"div",
|
|
13152
|
+
{
|
|
13153
|
+
ref: containerRef,
|
|
13154
|
+
className: "editor-container editor-date-container",
|
|
13155
|
+
onKeyDown: handleKeyDown
|
|
13156
|
+
},
|
|
13157
|
+
/* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13158
|
+
"input",
|
|
13159
|
+
{
|
|
13160
|
+
ref: inputRef,
|
|
13161
|
+
type: "text",
|
|
13162
|
+
className: "editor-input editor-date-input",
|
|
13163
|
+
value: inputValue,
|
|
13164
|
+
onChange: handleInputChange,
|
|
13165
|
+
onBlur: handleInputBlur,
|
|
13166
|
+
placeholder: dateFormat,
|
|
13167
|
+
"aria-label": "Date input",
|
|
13168
|
+
autoComplete: "off"
|
|
13169
|
+
}
|
|
13170
|
+
), /* @__PURE__ */ import_react36.default.createElement("span", { className: "editor-calendar-icon", "aria-hidden": "true" }, "\u{1F4C5}")),
|
|
13171
|
+
isOpen && /* @__PURE__ */ import_react36.default.createElement("div", { ref: calendarRef, className: "editor-dropdown editor-calendar" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-header" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13172
|
+
"button",
|
|
13173
|
+
{
|
|
13174
|
+
type: "button",
|
|
13175
|
+
className: "editor-calendar-nav",
|
|
13176
|
+
onClick: handlePrevMonth,
|
|
13177
|
+
"aria-label": "Previous month"
|
|
13178
|
+
},
|
|
13179
|
+
"\u2039"
|
|
13180
|
+
), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-title" }, viewDate.toLocaleDateString("en-US", {
|
|
13181
|
+
month: "long",
|
|
13182
|
+
year: "numeric"
|
|
13183
|
+
})), /* @__PURE__ */ import_react36.default.createElement(
|
|
13184
|
+
"button",
|
|
13185
|
+
{
|
|
13186
|
+
type: "button",
|
|
13187
|
+
className: "editor-calendar-nav",
|
|
13188
|
+
onClick: handleNextMonth,
|
|
13189
|
+
"aria-label": "Next month"
|
|
13190
|
+
},
|
|
13191
|
+
"\u203A"
|
|
13192
|
+
)), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-weekdays" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ import_react36.default.createElement("div", { key: day, className: "editor-calendar-weekday" }, day))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-days" }, calendarDays.map((day, index) => /* @__PURE__ */ import_react36.default.createElement(
|
|
13193
|
+
"button",
|
|
13194
|
+
{
|
|
13195
|
+
key: index,
|
|
13196
|
+
type: "button",
|
|
13197
|
+
className: `editor-calendar-day ${day.className}`,
|
|
13198
|
+
onClick: () => day.date && handleSelectDate(day.date),
|
|
13199
|
+
disabled: day.disabled,
|
|
13200
|
+
"aria-label": day.date ? day.date.toDateString() : ""
|
|
13201
|
+
},
|
|
13202
|
+
day.label
|
|
13203
|
+
))), showTime && /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-time" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13204
|
+
"input",
|
|
13205
|
+
{
|
|
13206
|
+
type: "time",
|
|
13207
|
+
className: "editor-time-input",
|
|
13208
|
+
value: parsedValue ? `${String(parsedValue.getHours()).padStart(2, "0")}:${String(
|
|
13209
|
+
parsedValue.getMinutes()
|
|
13210
|
+
).padStart(2, "0")}` : "00:00",
|
|
13211
|
+
onChange: (e) => {
|
|
13212
|
+
const [hours, minutes] = e.target.value.split(":").map(Number);
|
|
13213
|
+
handleTimeChange(hours, minutes);
|
|
13214
|
+
},
|
|
13215
|
+
"aria-label": "Time input"
|
|
13216
|
+
}
|
|
13217
|
+
)))
|
|
13218
|
+
);
|
|
13219
|
+
}
|
|
13220
|
+
DateEditor.displayName = "DateEditor";
|
|
13221
|
+
function formatDate(date, format, showTime) {
|
|
13222
|
+
const year = date.getFullYear();
|
|
13223
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
13224
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
13225
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
13226
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
13227
|
+
let formatted = format.replace("yyyy", String(year)).replace("MM", month).replace("dd", day);
|
|
13228
|
+
if (showTime) {
|
|
13229
|
+
formatted += ` ${hours}:${minutes}`;
|
|
13230
|
+
}
|
|
13231
|
+
return formatted;
|
|
13232
|
+
}
|
|
13233
|
+
function parseDate(value, format) {
|
|
13234
|
+
if (!value) return null;
|
|
13235
|
+
try {
|
|
13236
|
+
const parts = value.split(/[-/\s:]/);
|
|
13237
|
+
if (parts.length < 3) return null;
|
|
13238
|
+
let year, month, day;
|
|
13239
|
+
let hours = 0, minutes = 0;
|
|
13240
|
+
if (format.startsWith("yyyy")) {
|
|
13241
|
+
year = parseInt(parts[0], 10);
|
|
13242
|
+
month = parseInt(parts[1], 10) - 1;
|
|
13243
|
+
day = parseInt(parts[2], 10);
|
|
13244
|
+
} else if (format.startsWith("MM")) {
|
|
13245
|
+
month = parseInt(parts[0], 10) - 1;
|
|
13246
|
+
day = parseInt(parts[1], 10);
|
|
13247
|
+
year = parseInt(parts[2], 10);
|
|
13248
|
+
} else {
|
|
13249
|
+
day = parseInt(parts[0], 10);
|
|
13250
|
+
month = parseInt(parts[1], 10) - 1;
|
|
13251
|
+
year = parseInt(parts[2], 10);
|
|
13252
|
+
}
|
|
13253
|
+
if (parts.length >= 5) {
|
|
13254
|
+
hours = parseInt(parts[3], 10);
|
|
13255
|
+
minutes = parseInt(parts[4], 10);
|
|
13256
|
+
}
|
|
13257
|
+
const date = new Date(year, month, day, hours, minutes);
|
|
13258
|
+
return isNaN(date.getTime()) ? null : date;
|
|
13259
|
+
} catch {
|
|
13260
|
+
return null;
|
|
13261
|
+
}
|
|
13262
|
+
}
|
|
13263
|
+
function generateCalendarDays(viewDate, selectedDate, minDate, maxDate) {
|
|
13264
|
+
const year = viewDate.getFullYear();
|
|
13265
|
+
const month = viewDate.getMonth();
|
|
13266
|
+
const firstDay = new Date(year, month, 1);
|
|
13267
|
+
const lastDay = new Date(year, month + 1, 0);
|
|
13268
|
+
const daysInMonth = lastDay.getDate();
|
|
13269
|
+
const startingDayOfWeek = firstDay.getDay();
|
|
13270
|
+
const days = [];
|
|
13271
|
+
const prevMonth = new Date(year, month, 0);
|
|
13272
|
+
const prevMonthDays = prevMonth.getDate();
|
|
13273
|
+
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
|
13274
|
+
const date = new Date(year, month - 1, prevMonthDays - i);
|
|
13275
|
+
days.push({
|
|
13276
|
+
date,
|
|
13277
|
+
label: String(prevMonthDays - i),
|
|
13278
|
+
className: "other-month",
|
|
13279
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
13280
|
+
});
|
|
13281
|
+
}
|
|
13282
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
13283
|
+
const date = new Date(year, month, i);
|
|
13284
|
+
const isSelected = selectedDate && isSameDay(date, selectedDate);
|
|
13285
|
+
const isToday = isSameDay(date, /* @__PURE__ */ new Date());
|
|
13286
|
+
const disabled = isDateDisabled(date, minDate, maxDate);
|
|
13287
|
+
days.push({
|
|
13288
|
+
date,
|
|
13289
|
+
label: String(i),
|
|
13290
|
+
className: `${isSelected ? "selected" : ""} ${isToday ? "today" : ""} ${disabled ? "disabled" : ""}`.trim(),
|
|
13291
|
+
disabled
|
|
13292
|
+
});
|
|
13293
|
+
}
|
|
13294
|
+
const remainingDays = 42 - days.length;
|
|
13295
|
+
for (let i = 1; i <= remainingDays; i++) {
|
|
13296
|
+
const date = new Date(year, month + 1, i);
|
|
13297
|
+
days.push({
|
|
13298
|
+
date,
|
|
13299
|
+
label: String(i),
|
|
13300
|
+
className: "other-month",
|
|
13301
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
13302
|
+
});
|
|
13303
|
+
}
|
|
13304
|
+
return days;
|
|
13305
|
+
}
|
|
13306
|
+
function isSameDay(date1, date2) {
|
|
13307
|
+
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
|
|
13308
|
+
}
|
|
13309
|
+
function isDateDisabled(date, minDate, maxDate) {
|
|
13310
|
+
if (minDate && date < minDate) return true;
|
|
13311
|
+
if (maxDate && date > maxDate) return true;
|
|
13312
|
+
return false;
|
|
13313
|
+
}
|
|
13314
|
+
|
|
13315
|
+
// src/editors/NumericEditor.tsx
|
|
13316
|
+
var import_react37 = __toESM(require("react"), 1);
|
|
13317
|
+
function NumericEditor(props) {
|
|
13318
|
+
const {
|
|
13319
|
+
value,
|
|
13320
|
+
onChange,
|
|
13321
|
+
onCommit,
|
|
13322
|
+
onCancel,
|
|
13323
|
+
autoFocus = true,
|
|
13324
|
+
min,
|
|
13325
|
+
max,
|
|
13326
|
+
step = 1,
|
|
13327
|
+
decimals = 0,
|
|
13328
|
+
prefix = "",
|
|
13329
|
+
suffix = "",
|
|
13330
|
+
allowNegative = true,
|
|
13331
|
+
showSteppers = true,
|
|
13332
|
+
thousandsSeparator = ",",
|
|
13333
|
+
decimalSeparator = "."
|
|
13334
|
+
} = props;
|
|
13335
|
+
const [inputValue, setInputValue] = (0, import_react37.useState)("");
|
|
13336
|
+
const [isFocused, setIsFocused] = (0, import_react37.useState)(true);
|
|
13337
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
13338
|
+
(0, import_react37.useEffect)(() => {
|
|
13339
|
+
if (value !== null && value !== void 0) {
|
|
13340
|
+
if (isFocused) {
|
|
13341
|
+
queueMicrotask(() => setInputValue(String(value)));
|
|
13342
|
+
} else {
|
|
13343
|
+
queueMicrotask(() => setInputValue(formatNumber(value, decimals, thousandsSeparator, decimalSeparator)));
|
|
13344
|
+
}
|
|
13345
|
+
} else {
|
|
13346
|
+
queueMicrotask(() => setInputValue(""));
|
|
13347
|
+
}
|
|
13348
|
+
}, [value, isFocused, decimals, thousandsSeparator, decimalSeparator]);
|
|
13349
|
+
const clampValue = (val) => {
|
|
13350
|
+
let clamped = val;
|
|
13351
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
13352
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
13353
|
+
return clamped;
|
|
13354
|
+
};
|
|
13355
|
+
const handleInputChange = (e) => {
|
|
13356
|
+
const newValue = e.target.value;
|
|
13357
|
+
setInputValue(newValue);
|
|
13358
|
+
if (newValue === "" || newValue === "-") {
|
|
13359
|
+
onChange(null);
|
|
13360
|
+
return;
|
|
13361
|
+
}
|
|
13362
|
+
let cleaned = newValue;
|
|
13363
|
+
if (prefix) cleaned = cleaned.replace(prefix, "");
|
|
13364
|
+
if (suffix) cleaned = cleaned.replace(suffix, "");
|
|
13365
|
+
const parsed = parseFormattedNumber(cleaned, thousandsSeparator, decimalSeparator);
|
|
13366
|
+
if (parsed !== null && !isNaN(parsed)) {
|
|
13367
|
+
if (!allowNegative && parsed < 0) {
|
|
13368
|
+
return;
|
|
13369
|
+
}
|
|
13370
|
+
const clamped = clampValue(parsed);
|
|
13371
|
+
onChange(clamped);
|
|
13372
|
+
}
|
|
13373
|
+
};
|
|
13374
|
+
const handleIncrement = () => {
|
|
13375
|
+
const currentValue = value ?? 0;
|
|
13376
|
+
const newValue = clampValue(currentValue + step);
|
|
13377
|
+
onChange(newValue);
|
|
13378
|
+
setInputValue(String(newValue));
|
|
13379
|
+
};
|
|
13380
|
+
const handleDecrement = () => {
|
|
13381
|
+
const currentValue = value ?? 0;
|
|
13382
|
+
const newValue = clampValue(currentValue - step);
|
|
13383
|
+
onChange(newValue);
|
|
13384
|
+
setInputValue(String(newValue));
|
|
13385
|
+
};
|
|
13386
|
+
const handleFocus = () => {
|
|
13387
|
+
setIsFocused(true);
|
|
13388
|
+
if (value !== null && value !== void 0) {
|
|
13389
|
+
setInputValue(String(value));
|
|
13390
|
+
}
|
|
13391
|
+
};
|
|
13392
|
+
const handleBlur = () => {
|
|
13393
|
+
setIsFocused(false);
|
|
13394
|
+
if (value !== null && value !== void 0) {
|
|
13395
|
+
const formatted = formatNumber(value, decimals, thousandsSeparator, decimalSeparator);
|
|
13396
|
+
setInputValue(formatted);
|
|
13397
|
+
}
|
|
13398
|
+
setTimeout(() => onCommit(), 100);
|
|
13399
|
+
};
|
|
13400
|
+
const handleKeyDown = (e) => {
|
|
13401
|
+
if (e.key === "ArrowUp") {
|
|
13402
|
+
e.preventDefault();
|
|
13403
|
+
handleIncrement();
|
|
13404
|
+
} else if (e.key === "ArrowDown") {
|
|
13405
|
+
e.preventDefault();
|
|
13406
|
+
handleDecrement();
|
|
13407
|
+
}
|
|
13408
|
+
};
|
|
13409
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
13410
|
+
{
|
|
13411
|
+
onEnter: onCommit,
|
|
13412
|
+
onEscape: onCancel
|
|
13413
|
+
},
|
|
13414
|
+
{
|
|
13415
|
+
commitOnTab: true,
|
|
13416
|
+
commitOnBlur: false,
|
|
13417
|
+
preventDefault: false
|
|
13418
|
+
// Let handleKeyDown handle arrow keys
|
|
13419
|
+
}
|
|
13420
|
+
);
|
|
13421
|
+
const handleCombinedKeyDown = (e) => {
|
|
13422
|
+
handleKeyDown(e);
|
|
13423
|
+
handleEditorKeyDown(e);
|
|
13424
|
+
};
|
|
13425
|
+
const displayValue = isFocused ? inputValue : inputValue ? `${prefix}${inputValue}${suffix}` : "";
|
|
13426
|
+
return /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-container editor-numeric-container" }, /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-input-wrapper" }, prefix && !isFocused && /* @__PURE__ */ import_react37.default.createElement("span", { className: "editor-numeric-prefix" }, prefix), /* @__PURE__ */ import_react37.default.createElement(
|
|
13427
|
+
"input",
|
|
13428
|
+
{
|
|
13429
|
+
ref: inputRef,
|
|
13430
|
+
type: "text",
|
|
13431
|
+
inputMode: "decimal",
|
|
13432
|
+
className: "editor-input editor-numeric-input",
|
|
13433
|
+
value: displayValue,
|
|
13434
|
+
onChange: handleInputChange,
|
|
13435
|
+
onFocus: handleFocus,
|
|
13436
|
+
onBlur: handleBlur,
|
|
13437
|
+
onKeyDown: handleCombinedKeyDown,
|
|
13438
|
+
"aria-label": "Numeric input",
|
|
13439
|
+
"aria-valuemin": min,
|
|
13440
|
+
"aria-valuemax": max,
|
|
13441
|
+
"aria-valuenow": value ?? void 0,
|
|
13442
|
+
autoComplete: "off"
|
|
13443
|
+
}
|
|
13444
|
+
), suffix && !isFocused && /* @__PURE__ */ import_react37.default.createElement("span", { className: "editor-numeric-suffix" }, suffix), showSteppers && /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-numeric-steppers" }, /* @__PURE__ */ import_react37.default.createElement(
|
|
13445
|
+
"button",
|
|
13446
|
+
{
|
|
13447
|
+
type: "button",
|
|
13448
|
+
className: "editor-numeric-stepper editor-numeric-increment",
|
|
13449
|
+
onClick: handleIncrement,
|
|
13450
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13451
|
+
disabled: max !== void 0 && (value ?? 0) >= max,
|
|
13452
|
+
"aria-label": "Increment",
|
|
13453
|
+
tabIndex: -1
|
|
13454
|
+
},
|
|
13455
|
+
"\u25B2"
|
|
13456
|
+
), /* @__PURE__ */ import_react37.default.createElement(
|
|
13457
|
+
"button",
|
|
13458
|
+
{
|
|
13459
|
+
type: "button",
|
|
13460
|
+
className: "editor-numeric-stepper editor-numeric-decrement",
|
|
13461
|
+
onClick: handleDecrement,
|
|
13462
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13463
|
+
disabled: min !== void 0 && (value ?? 0) <= min,
|
|
13464
|
+
"aria-label": "Decrement",
|
|
13465
|
+
tabIndex: -1
|
|
13466
|
+
},
|
|
13467
|
+
"\u25BC"
|
|
13468
|
+
))), (min !== void 0 || max !== void 0) && /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-numeric-range" }, min !== void 0 && /* @__PURE__ */ import_react37.default.createElement("span", null, "Min: ", min), max !== void 0 && /* @__PURE__ */ import_react37.default.createElement("span", null, "Max: ", max)));
|
|
13469
|
+
}
|
|
13470
|
+
NumericEditor.displayName = "NumericEditor";
|
|
13471
|
+
|
|
13472
|
+
// src/editors/MultiSelectEditor.tsx
|
|
13473
|
+
var import_react38 = __toESM(require("react"), 1);
|
|
13474
|
+
function MultiSelectEditor(props) {
|
|
13475
|
+
const {
|
|
13476
|
+
value = [],
|
|
13477
|
+
onChange,
|
|
13478
|
+
onCommit,
|
|
13479
|
+
onCancel,
|
|
13480
|
+
autoFocus = true,
|
|
13481
|
+
options,
|
|
13482
|
+
placeholder = "Select...",
|
|
13483
|
+
maxTagCount = 3,
|
|
13484
|
+
filterable = true,
|
|
13485
|
+
maxDropdownHeight = 300,
|
|
13486
|
+
allowEmpty = true
|
|
13487
|
+
} = props;
|
|
13488
|
+
const [isOpen] = (0, import_react38.useState)(true);
|
|
13489
|
+
const [searchQuery, setSearchQuery] = (0, import_react38.useState)("");
|
|
13490
|
+
const [focusedIndex, setFocusedIndex] = (0, import_react38.useState)(-1);
|
|
13491
|
+
const containerRef = (0, import_react38.useRef)(null);
|
|
13492
|
+
const inputRef = useEditorAutoFocus(autoFocus);
|
|
13493
|
+
const dropdownRef = (0, import_react38.useRef)(null);
|
|
13494
|
+
const tagContainerRef = (0, import_react38.useRef)(null);
|
|
13495
|
+
const selectedValues = (0, import_react38.useMemo)(
|
|
13496
|
+
() => Array.isArray(value) ? value : [],
|
|
13497
|
+
[value]
|
|
13498
|
+
);
|
|
13499
|
+
const filteredOptions = (0, import_react38.useMemo)(
|
|
13500
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
13501
|
+
[options, searchQuery, filterable]
|
|
13502
|
+
);
|
|
13503
|
+
const selectedOptions = (0, import_react38.useMemo)(
|
|
13504
|
+
() => options.filter((opt) => selectedValues.includes(opt.value)),
|
|
13505
|
+
[options, selectedValues]
|
|
13506
|
+
);
|
|
13507
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
13508
|
+
useEditorClickOutside(
|
|
13509
|
+
containerRef,
|
|
13510
|
+
() => {
|
|
13511
|
+
if (isOpen) {
|
|
13512
|
+
onCommit();
|
|
13513
|
+
}
|
|
13514
|
+
},
|
|
13515
|
+
true
|
|
13516
|
+
);
|
|
13517
|
+
const handleToggleOption = (option) => {
|
|
13518
|
+
if (option.disabled) return;
|
|
13519
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13520
|
+
let newValues;
|
|
13521
|
+
if (isSelected) {
|
|
13522
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13523
|
+
return;
|
|
13524
|
+
}
|
|
13525
|
+
newValues = selectedValues.filter((v) => v !== option.value);
|
|
13526
|
+
} else {
|
|
13527
|
+
newValues = [...selectedValues, option.value];
|
|
13528
|
+
}
|
|
13529
|
+
onChange(newValues);
|
|
13530
|
+
};
|
|
13531
|
+
const handleRemoveTag = (optionValue, e) => {
|
|
13532
|
+
e.stopPropagation();
|
|
13533
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13534
|
+
return;
|
|
13535
|
+
}
|
|
13536
|
+
const newValues = selectedValues.filter((v) => v !== optionValue);
|
|
13537
|
+
onChange(newValues);
|
|
13538
|
+
};
|
|
13539
|
+
const handleKeyDown = (e) => {
|
|
13540
|
+
if (e.key === "Backspace" && searchQuery === "" && selectedValues.length > 0) {
|
|
13541
|
+
e.preventDefault();
|
|
13542
|
+
if (allowEmpty || selectedValues.length > 1) {
|
|
13543
|
+
const newValues = selectedValues.slice(0, -1);
|
|
13544
|
+
onChange(newValues);
|
|
13545
|
+
}
|
|
13546
|
+
}
|
|
13547
|
+
};
|
|
13548
|
+
const handleArrowDown = () => {
|
|
13549
|
+
setFocusedIndex((prev) => {
|
|
13550
|
+
var _a;
|
|
13551
|
+
let next = prev + 1;
|
|
13552
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13553
|
+
next++;
|
|
13554
|
+
}
|
|
13555
|
+
return next < filteredOptions.length ? next : prev;
|
|
13556
|
+
});
|
|
13557
|
+
};
|
|
13558
|
+
const handleArrowUp = () => {
|
|
13559
|
+
setFocusedIndex((prev) => {
|
|
13560
|
+
var _a;
|
|
13561
|
+
let next = prev - 1;
|
|
13562
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13563
|
+
next--;
|
|
13564
|
+
}
|
|
13565
|
+
return next >= 0 ? next : prev;
|
|
13566
|
+
});
|
|
13567
|
+
};
|
|
13568
|
+
const handleEnter = () => {
|
|
13569
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
13570
|
+
const option = filteredOptions[focusedIndex];
|
|
13571
|
+
if (option && !option.disabled) {
|
|
13572
|
+
handleToggleOption(option);
|
|
13573
|
+
}
|
|
13574
|
+
} else {
|
|
13575
|
+
onCommit();
|
|
13576
|
+
}
|
|
13577
|
+
};
|
|
13578
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
13579
|
+
{
|
|
13580
|
+
onEnter: handleEnter,
|
|
13581
|
+
onEscape: onCancel,
|
|
13582
|
+
onArrowUp: handleArrowUp,
|
|
13583
|
+
onArrowDown: handleArrowDown
|
|
13584
|
+
},
|
|
13585
|
+
{
|
|
13586
|
+
commitOnTab: true,
|
|
13587
|
+
commitOnBlur: false
|
|
13588
|
+
}
|
|
13589
|
+
);
|
|
13590
|
+
const handleCombinedKeyDown = (e) => {
|
|
13591
|
+
handleKeyDown(e);
|
|
13592
|
+
handleEditorKeyDown(e);
|
|
13593
|
+
};
|
|
13594
|
+
const visibleTags = selectedOptions.slice(0, maxTagCount);
|
|
13595
|
+
const collapsedCount = Math.max(0, selectedOptions.length - maxTagCount);
|
|
13596
|
+
return /* @__PURE__ */ import_react38.default.createElement(
|
|
13597
|
+
"div",
|
|
13598
|
+
{
|
|
13599
|
+
ref: containerRef,
|
|
13600
|
+
className: "editor-container editor-multiselect-container",
|
|
13601
|
+
onKeyDown: handleCombinedKeyDown
|
|
13602
|
+
},
|
|
13603
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13604
|
+
"div",
|
|
13605
|
+
{
|
|
13606
|
+
ref: tagContainerRef,
|
|
13607
|
+
className: "editor-input-wrapper editor-multiselect-wrapper"
|
|
13608
|
+
},
|
|
13609
|
+
/* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-multiselect-tags" }, visibleTags.map((option) => /* @__PURE__ */ import_react38.default.createElement("div", { key: option.value, className: "editor-tag" }, option.icon && /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-tag-icon" }, option.icon), /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-tag-label" }, option.label), /* @__PURE__ */ import_react38.default.createElement(
|
|
13610
|
+
"button",
|
|
13611
|
+
{
|
|
13612
|
+
type: "button",
|
|
13613
|
+
className: "editor-tag-remove",
|
|
13614
|
+
onClick: (e) => handleRemoveTag(option.value, e),
|
|
13615
|
+
"aria-label": `Remove ${option.label}`,
|
|
13616
|
+
tabIndex: -1
|
|
13617
|
+
},
|
|
13618
|
+
"\xD7"
|
|
13619
|
+
))), collapsedCount > 0 && /* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-tag editor-tag-collapsed" }, "+", collapsedCount)),
|
|
13620
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13621
|
+
"input",
|
|
13622
|
+
{
|
|
13623
|
+
ref: inputRef,
|
|
13624
|
+
type: "text",
|
|
13625
|
+
className: "editor-input editor-multiselect-input",
|
|
13626
|
+
value: searchQuery,
|
|
13627
|
+
onChange: (e) => {
|
|
13628
|
+
setSearchQuery(e.target.value);
|
|
13629
|
+
setFocusedIndex(-1);
|
|
13630
|
+
},
|
|
13631
|
+
placeholder: selectedValues.length === 0 ? placeholder : "",
|
|
13632
|
+
"aria-label": "Search options",
|
|
13633
|
+
"aria-expanded": isOpen,
|
|
13634
|
+
"aria-autocomplete": "list",
|
|
13635
|
+
"aria-controls": "multiselect-dropdown",
|
|
13636
|
+
autoComplete: "off"
|
|
13637
|
+
}
|
|
13638
|
+
),
|
|
13639
|
+
/* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC")
|
|
13640
|
+
),
|
|
13641
|
+
isOpen && /* @__PURE__ */ import_react38.default.createElement(
|
|
13642
|
+
"div",
|
|
13643
|
+
{
|
|
13644
|
+
ref: dropdownRef,
|
|
13645
|
+
id: "multiselect-dropdown",
|
|
13646
|
+
className: "editor-dropdown",
|
|
13647
|
+
role: "listbox",
|
|
13648
|
+
"aria-multiselectable": "true",
|
|
13649
|
+
style: { maxHeight: maxDropdownHeight }
|
|
13650
|
+
},
|
|
13651
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => {
|
|
13652
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13653
|
+
return /* @__PURE__ */ import_react38.default.createElement(
|
|
13654
|
+
"div",
|
|
13655
|
+
{
|
|
13656
|
+
key: option.value,
|
|
13657
|
+
className: `editor-dropdown-option editor-multiselect-option ${isSelected ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
13658
|
+
role: "option",
|
|
13659
|
+
"aria-selected": isSelected,
|
|
13660
|
+
"aria-disabled": option.disabled,
|
|
13661
|
+
onClick: () => handleToggleOption(option),
|
|
13662
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
13663
|
+
},
|
|
13664
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13665
|
+
"input",
|
|
13666
|
+
{
|
|
13667
|
+
type: "checkbox",
|
|
13668
|
+
className: "editor-multiselect-checkbox",
|
|
13669
|
+
checked: isSelected,
|
|
13670
|
+
onChange: () => {
|
|
13671
|
+
},
|
|
13672
|
+
disabled: option.disabled,
|
|
13673
|
+
tabIndex: -1,
|
|
13674
|
+
"aria-hidden": "true"
|
|
13675
|
+
}
|
|
13676
|
+
),
|
|
13677
|
+
/* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-option-label" }, option.label))
|
|
13678
|
+
);
|
|
13679
|
+
})
|
|
13680
|
+
)
|
|
13681
|
+
);
|
|
13682
|
+
}
|
|
13683
|
+
MultiSelectEditor.displayName = "MultiSelectEditor";
|
|
13684
|
+
|
|
13685
|
+
// src/editors/MarkdownEditor.tsx
|
|
13686
|
+
var import_react39 = __toESM(require("react"), 1);
|
|
13687
|
+
function MarkdownEditor(props) {
|
|
13688
|
+
const {
|
|
13689
|
+
value = "",
|
|
13690
|
+
onChange,
|
|
13691
|
+
onCommit,
|
|
13692
|
+
onCancel,
|
|
13693
|
+
autoFocus = true,
|
|
13694
|
+
maxLength,
|
|
13695
|
+
showPreview = true,
|
|
13696
|
+
minHeight = 150,
|
|
13697
|
+
maxHeight = 400,
|
|
13698
|
+
rows = 6
|
|
13699
|
+
} = props;
|
|
13700
|
+
const [internalValue, setInternalValue] = (0, import_react39.useState)(value || "");
|
|
13701
|
+
const [showPreviewPanel, setShowPreviewPanel] = (0, import_react39.useState)(showPreview);
|
|
13702
|
+
const containerRef = (0, import_react39.useRef)(null);
|
|
13703
|
+
const textareaRef = useEditorAutoFocus(autoFocus, true);
|
|
13704
|
+
useEditorClickOutside(
|
|
13705
|
+
containerRef,
|
|
13706
|
+
() => {
|
|
13707
|
+
onCommit();
|
|
13708
|
+
},
|
|
13709
|
+
true
|
|
13710
|
+
);
|
|
13711
|
+
const handleChange = (e) => {
|
|
13712
|
+
const newValue = e.target.value;
|
|
13713
|
+
if (maxLength && newValue.length > maxLength) {
|
|
13714
|
+
return;
|
|
13715
|
+
}
|
|
13716
|
+
setInternalValue(newValue);
|
|
13717
|
+
onChange(newValue);
|
|
13718
|
+
};
|
|
13719
|
+
const handleKeyDown = (e) => {
|
|
13720
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
13721
|
+
e.preventDefault();
|
|
13722
|
+
onCommit();
|
|
13723
|
+
return;
|
|
13724
|
+
}
|
|
13725
|
+
if (e.key === "Escape") {
|
|
13726
|
+
e.preventDefault();
|
|
13727
|
+
onCancel();
|
|
13728
|
+
return;
|
|
13729
|
+
}
|
|
13730
|
+
if (e.key === "Tab") {
|
|
13731
|
+
e.preventDefault();
|
|
13732
|
+
const start = e.currentTarget.selectionStart;
|
|
13733
|
+
const end = e.currentTarget.selectionEnd;
|
|
13734
|
+
const newValue = internalValue.substring(0, start) + " " + internalValue.substring(end);
|
|
13735
|
+
setInternalValue(newValue);
|
|
13736
|
+
onChange(newValue);
|
|
13737
|
+
setTimeout(() => {
|
|
13738
|
+
if (textareaRef.current) {
|
|
13739
|
+
textareaRef.current.selectionStart = start + 1;
|
|
13740
|
+
textareaRef.current.selectionEnd = start + 1;
|
|
13741
|
+
}
|
|
13742
|
+
}, 0);
|
|
13743
|
+
return;
|
|
13744
|
+
}
|
|
13745
|
+
if (e.ctrlKey || e.metaKey) {
|
|
13746
|
+
let insertion = "";
|
|
13747
|
+
let cursorOffset = 0;
|
|
13748
|
+
switch (e.key) {
|
|
13749
|
+
case "b":
|
|
13750
|
+
insertion = "****";
|
|
13751
|
+
cursorOffset = 2;
|
|
13752
|
+
break;
|
|
13753
|
+
case "i":
|
|
13754
|
+
insertion = "__";
|
|
13755
|
+
cursorOffset = 1;
|
|
13756
|
+
break;
|
|
13757
|
+
case "k":
|
|
13758
|
+
insertion = "[](url)";
|
|
13759
|
+
cursorOffset = 1;
|
|
13760
|
+
break;
|
|
13761
|
+
default:
|
|
13762
|
+
return;
|
|
13763
|
+
}
|
|
13764
|
+
if (insertion) {
|
|
13765
|
+
e.preventDefault();
|
|
13766
|
+
const start = e.currentTarget.selectionStart;
|
|
13767
|
+
const end = e.currentTarget.selectionEnd;
|
|
13768
|
+
const selectedText = internalValue.substring(start, end);
|
|
13769
|
+
let newValue;
|
|
13770
|
+
let newCursorPos;
|
|
13771
|
+
if (selectedText) {
|
|
13772
|
+
const before = insertion.substring(0, cursorOffset);
|
|
13773
|
+
const after = insertion.substring(cursorOffset);
|
|
13774
|
+
newValue = internalValue.substring(0, start) + before + selectedText + after + internalValue.substring(end);
|
|
13775
|
+
newCursorPos = start + before.length + selectedText.length;
|
|
13776
|
+
} else {
|
|
13777
|
+
newValue = internalValue.substring(0, start) + insertion + internalValue.substring(end);
|
|
13778
|
+
newCursorPos = start + cursorOffset;
|
|
13779
|
+
}
|
|
13780
|
+
setInternalValue(newValue);
|
|
13781
|
+
onChange(newValue);
|
|
13782
|
+
setTimeout(() => {
|
|
13783
|
+
if (textareaRef.current) {
|
|
13784
|
+
textareaRef.current.selectionStart = newCursorPos;
|
|
13785
|
+
textareaRef.current.selectionEnd = newCursorPos;
|
|
13786
|
+
textareaRef.current.focus();
|
|
13787
|
+
}
|
|
13788
|
+
}, 0);
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
};
|
|
13792
|
+
const charCount = internalValue.length;
|
|
13793
|
+
const charCountClass = maxLength && charCount > maxLength * 0.9 ? "warning" : "";
|
|
13794
|
+
const previewHtml = (0, import_react39.useMemo)(() => {
|
|
13795
|
+
return renderMarkdownPreview(internalValue);
|
|
13796
|
+
}, [internalValue]);
|
|
13797
|
+
return /* @__PURE__ */ import_react39.default.createElement(
|
|
13798
|
+
"div",
|
|
13799
|
+
{
|
|
13800
|
+
ref: containerRef,
|
|
13801
|
+
className: "editor-container editor-markdown-container",
|
|
13802
|
+
style: { minHeight }
|
|
13803
|
+
},
|
|
13804
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13805
|
+
"button",
|
|
13806
|
+
{
|
|
13807
|
+
type: "button",
|
|
13808
|
+
className: "editor-toolbar-btn",
|
|
13809
|
+
onClick: () => {
|
|
13810
|
+
if (textareaRef.current) {
|
|
13811
|
+
const start = textareaRef.current.selectionStart;
|
|
13812
|
+
const end = textareaRef.current.selectionEnd;
|
|
13813
|
+
const selectedText = internalValue.substring(start, end);
|
|
13814
|
+
const newValue = internalValue.substring(0, start) + `**${selectedText || "bold"}**` + internalValue.substring(end);
|
|
13815
|
+
setInternalValue(newValue);
|
|
13816
|
+
onChange(newValue);
|
|
13817
|
+
textareaRef.current.focus();
|
|
13818
|
+
}
|
|
13819
|
+
},
|
|
13820
|
+
title: "Bold (Ctrl+B)",
|
|
13821
|
+
"aria-label": "Bold"
|
|
13822
|
+
},
|
|
13823
|
+
/* @__PURE__ */ import_react39.default.createElement("strong", null, "B")
|
|
13824
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13825
|
+
"button",
|
|
13826
|
+
{
|
|
13827
|
+
type: "button",
|
|
13828
|
+
className: "editor-toolbar-btn",
|
|
13829
|
+
onClick: () => {
|
|
13830
|
+
if (textareaRef.current) {
|
|
13831
|
+
const start = textareaRef.current.selectionStart;
|
|
13832
|
+
const end = textareaRef.current.selectionEnd;
|
|
13833
|
+
const selectedText = internalValue.substring(start, end);
|
|
13834
|
+
const newValue = internalValue.substring(0, start) + `_${selectedText || "italic"}_` + internalValue.substring(end);
|
|
13835
|
+
setInternalValue(newValue);
|
|
13836
|
+
onChange(newValue);
|
|
13837
|
+
textareaRef.current.focus();
|
|
13838
|
+
}
|
|
13839
|
+
},
|
|
13840
|
+
title: "Italic (Ctrl+I)",
|
|
13841
|
+
"aria-label": "Italic"
|
|
13842
|
+
},
|
|
13843
|
+
/* @__PURE__ */ import_react39.default.createElement("em", null, "I")
|
|
13844
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13845
|
+
"button",
|
|
13846
|
+
{
|
|
13847
|
+
type: "button",
|
|
13848
|
+
className: "editor-toolbar-btn",
|
|
13849
|
+
onClick: () => {
|
|
13850
|
+
if (textareaRef.current) {
|
|
13851
|
+
const start = textareaRef.current.selectionStart;
|
|
13852
|
+
const end = textareaRef.current.selectionEnd;
|
|
13853
|
+
const selectedText = internalValue.substring(start, end);
|
|
13854
|
+
const newValue = internalValue.substring(0, start) + `[${selectedText || "link text"}](url)` + internalValue.substring(end);
|
|
13855
|
+
setInternalValue(newValue);
|
|
13856
|
+
onChange(newValue);
|
|
13857
|
+
textareaRef.current.focus();
|
|
13858
|
+
}
|
|
13859
|
+
},
|
|
13860
|
+
title: "Link (Ctrl+K)",
|
|
13861
|
+
"aria-label": "Link"
|
|
13862
|
+
},
|
|
13863
|
+
"\u{1F517}"
|
|
13864
|
+
)), /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13865
|
+
"button",
|
|
13866
|
+
{
|
|
13867
|
+
type: "button",
|
|
13868
|
+
className: `editor-toolbar-btn ${showPreviewPanel ? "active" : ""}`,
|
|
13869
|
+
onClick: () => setShowPreviewPanel(!showPreviewPanel),
|
|
13870
|
+
title: "Toggle preview",
|
|
13871
|
+
"aria-label": "Toggle preview",
|
|
13872
|
+
"aria-pressed": showPreviewPanel
|
|
13873
|
+
},
|
|
13874
|
+
"\u{1F441}"
|
|
13875
|
+
))),
|
|
13876
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: `editor-markdown-content ${showPreviewPanel ? "split" : ""}` }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-editor" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13877
|
+
"textarea",
|
|
13878
|
+
{
|
|
13879
|
+
ref: textareaRef,
|
|
13880
|
+
className: "editor-textarea editor-markdown-textarea",
|
|
13881
|
+
value: internalValue,
|
|
13882
|
+
onChange: handleChange,
|
|
13883
|
+
onKeyDown: handleKeyDown,
|
|
13884
|
+
rows,
|
|
13885
|
+
maxLength,
|
|
13886
|
+
placeholder: "Enter markdown text...",
|
|
13887
|
+
"aria-label": "Markdown editor",
|
|
13888
|
+
style: { maxHeight }
|
|
13889
|
+
}
|
|
13890
|
+
), /* @__PURE__ */ import_react39.default.createElement("div", { className: `editor-markdown-char-count ${charCountClass}` }, charCount, maxLength && ` / ${maxLength}`)), showPreviewPanel && /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-preview" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-preview-label" }, "Preview"), /* @__PURE__ */ import_react39.default.createElement(
|
|
13891
|
+
"div",
|
|
13892
|
+
{
|
|
13893
|
+
className: "editor-markdown-preview-content",
|
|
13894
|
+
dangerouslySetInnerHTML: { __html: previewHtml }
|
|
13895
|
+
}
|
|
13896
|
+
))),
|
|
13897
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-footer" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-hint" }, /* @__PURE__ */ import_react39.default.createElement("kbd", null, "Ctrl+Enter"), " to save \u2022 ", /* @__PURE__ */ import_react39.default.createElement("kbd", null, "Esc"), " to cancel"), /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-actions" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13898
|
+
"button",
|
|
13899
|
+
{
|
|
13900
|
+
type: "button",
|
|
13901
|
+
className: "editor-btn editor-btn-secondary",
|
|
13902
|
+
onClick: onCancel
|
|
13903
|
+
},
|
|
13904
|
+
"Cancel"
|
|
13905
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13906
|
+
"button",
|
|
13907
|
+
{
|
|
13908
|
+
type: "button",
|
|
13909
|
+
className: "editor-btn editor-btn-primary",
|
|
13910
|
+
onClick: onCommit
|
|
13911
|
+
},
|
|
13912
|
+
"Save"
|
|
13913
|
+
)))
|
|
13914
|
+
);
|
|
13915
|
+
}
|
|
13916
|
+
MarkdownEditor.displayName = "MarkdownEditor";
|
|
13917
|
+
function renderMarkdownPreview(markdown) {
|
|
13918
|
+
if (!markdown) return '<p class="editor-markdown-empty">Nothing to preview</p>';
|
|
13919
|
+
let html = markdown;
|
|
13920
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
13921
|
+
html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
|
|
13922
|
+
html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
|
|
13923
|
+
html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
|
|
13924
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
13925
|
+
html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
|
|
13926
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
13927
|
+
html = html.replace(/_(.+?)_/g, "<em>$1</em>");
|
|
13928
|
+
html = html.replace(/`(.+?)`/g, "<code>$1</code>");
|
|
13929
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
13930
|
+
html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
|
|
13931
|
+
html = html.replace(/^- (.+)$/gim, "<li>$1</li>");
|
|
13932
|
+
html = html.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>");
|
|
13933
|
+
html = html.replace(/\n\n/g, "</p><p>");
|
|
13934
|
+
html = html.replace(/\n/g, "<br>");
|
|
13935
|
+
if (!html.startsWith("<h") && !html.startsWith("<ul") && !html.startsWith("<p")) {
|
|
13936
|
+
html = "<p>" + html + "</p>";
|
|
13937
|
+
}
|
|
13938
|
+
return html;
|
|
13939
|
+
}
|
|
12160
13940
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12161
13941
|
0 && (module.exports = {
|
|
12162
13942
|
AdvancedFilterBuilder,
|
|
@@ -12166,6 +13946,7 @@ function createMockFeed(config) {
|
|
|
12166
13946
|
ColumnFilters,
|
|
12167
13947
|
CurrencyCell,
|
|
12168
13948
|
DataGrid,
|
|
13949
|
+
DateEditor,
|
|
12169
13950
|
DensityToggle,
|
|
12170
13951
|
ExportMenu,
|
|
12171
13952
|
FacetedSearch,
|
|
@@ -12178,11 +13959,16 @@ function createMockFeed(config) {
|
|
|
12178
13959
|
LayoutPersistenceManager,
|
|
12179
13960
|
LayoutPresetsManager,
|
|
12180
13961
|
LocalStorageAdapter,
|
|
13962
|
+
MarkdownEditor,
|
|
12181
13963
|
MarketDataEngine,
|
|
12182
13964
|
MarketDataGrid,
|
|
13965
|
+
MultiSelectEditor,
|
|
13966
|
+
NumericEditor,
|
|
13967
|
+
PivotToolbar,
|
|
12183
13968
|
PriorityIndicator,
|
|
12184
13969
|
ProgressBar,
|
|
12185
13970
|
Rating,
|
|
13971
|
+
RichSelectEditor,
|
|
12186
13972
|
ServerAdapter,
|
|
12187
13973
|
ServerSideDataSource,
|
|
12188
13974
|
StatusChip,
|
|
@@ -12192,6 +13978,7 @@ function createMockFeed(config) {
|
|
|
12192
13978
|
VirtualScroller,
|
|
12193
13979
|
WebSocketMockFeed,
|
|
12194
13980
|
alpineTheme,
|
|
13981
|
+
buildPivot,
|
|
12195
13982
|
buildTreeFromFlat,
|
|
12196
13983
|
collapseAllNodes,
|
|
12197
13984
|
countTreeNodes,
|
|
@@ -12201,12 +13988,17 @@ function createMockFeed(config) {
|
|
|
12201
13988
|
createMockWebSocket,
|
|
12202
13989
|
createPreset,
|
|
12203
13990
|
darkTheme,
|
|
13991
|
+
debounce,
|
|
12204
13992
|
densityConfigs,
|
|
13993
|
+
downloadCSV,
|
|
12205
13994
|
expandAllNodes,
|
|
13995
|
+
exportPivotToCSV,
|
|
12206
13996
|
exportToCSV,
|
|
12207
13997
|
exportToXLSX,
|
|
13998
|
+
filterOptions,
|
|
12208
13999
|
filterTree,
|
|
12209
14000
|
flattenTree,
|
|
14001
|
+
formatNumber,
|
|
12210
14002
|
generateDensityCSS,
|
|
12211
14003
|
generateFilename,
|
|
12212
14004
|
generatePresetId,
|
|
@@ -12224,10 +14016,15 @@ function createMockFeed(config) {
|
|
|
12224
14016
|
isTreeNode,
|
|
12225
14017
|
loadDensityMode,
|
|
12226
14018
|
materialTheme,
|
|
14019
|
+
parseFormattedNumber,
|
|
12227
14020
|
quartzTheme,
|
|
12228
14021
|
saveDensityMode,
|
|
12229
14022
|
themes,
|
|
12230
14023
|
toggleNodeExpansion,
|
|
12231
14024
|
useDensityMode,
|
|
12232
|
-
|
|
14025
|
+
useEditorAutoFocus,
|
|
14026
|
+
useEditorClickOutside,
|
|
14027
|
+
useEditorKeyboardNavigation,
|
|
14028
|
+
useMarketData,
|
|
14029
|
+
usePopupPosition
|
|
12233
14030
|
});
|