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.js
CHANGED
|
@@ -2356,7 +2356,16 @@ var GridBody = ({
|
|
|
2356
2356
|
onKeyDown: (e) => handleKeyDown(e, rowIndex, columnIndex),
|
|
2357
2357
|
tabIndex: isCellFocused ? 0 : -1
|
|
2358
2358
|
},
|
|
2359
|
-
isEditing ?
|
|
2359
|
+
isEditing ? column.editor ? column.editor({
|
|
2360
|
+
value: editState.value,
|
|
2361
|
+
row,
|
|
2362
|
+
column,
|
|
2363
|
+
onChange: handleEditChange,
|
|
2364
|
+
onCommit: handleEditComplete,
|
|
2365
|
+
onCancel: () => dispatch({ type: "END_EDIT" }),
|
|
2366
|
+
autoFocus: true,
|
|
2367
|
+
...column.editorParams
|
|
2368
|
+
}) : /* @__PURE__ */ React6.createElement(
|
|
2360
2369
|
"input",
|
|
2361
2370
|
{
|
|
2362
2371
|
ref: editInputRef,
|
|
@@ -5027,7 +5036,7 @@ var LayoutPresetsManager = ({
|
|
|
5027
5036
|
setLoading(false);
|
|
5028
5037
|
}
|
|
5029
5038
|
};
|
|
5030
|
-
const
|
|
5039
|
+
const formatDate2 = (timestamp) => {
|
|
5031
5040
|
return new Date(timestamp).toLocaleString();
|
|
5032
5041
|
};
|
|
5033
5042
|
return /* @__PURE__ */ React14.createElement("div", { className: "relative inline-block" }, /* @__PURE__ */ React14.createElement(
|
|
@@ -5141,7 +5150,7 @@ var LayoutPresetsManager = ({
|
|
|
5141
5150
|
if (buttons) buttons.style.opacity = "0";
|
|
5142
5151
|
}
|
|
5143
5152
|
},
|
|
5144
|
-
/* @__PURE__ */ React14.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ React14.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React14.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ React14.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ React14.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ",
|
|
5153
|
+
/* @__PURE__ */ React14.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ React14.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React14.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ React14.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ React14.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ", formatDate2(preset.updatedAt))), /* @__PURE__ */ React14.createElement("div", { className: "preset-actions", style: { display: "flex", gap: "4px", marginLeft: "8px", opacity: 0, transition: "opacity 0.2s" } }, /* @__PURE__ */ React14.createElement(
|
|
5145
5154
|
"button",
|
|
5146
5155
|
{
|
|
5147
5156
|
onClick: (e) => handleUpdatePreset(preset, e),
|
|
@@ -8043,6 +8052,195 @@ var GridApiImpl = class {
|
|
|
8043
8052
|
}
|
|
8044
8053
|
};
|
|
8045
8054
|
|
|
8055
|
+
// src/components/DataGrid/pivotEngine.ts
|
|
8056
|
+
var AGGREGATORS = {
|
|
8057
|
+
sum: (values) => values.reduce((acc, val) => acc + val, 0),
|
|
8058
|
+
count: (values) => values.length,
|
|
8059
|
+
avg: (values) => values.length > 0 ? values.reduce((acc, val) => acc + val, 0) / values.length : 0,
|
|
8060
|
+
min: (values) => values.length > 0 ? Math.min(...values) : 0,
|
|
8061
|
+
max: (values) => values.length > 0 ? Math.max(...values) : 0
|
|
8062
|
+
};
|
|
8063
|
+
function getUniqueValues(data, column) {
|
|
8064
|
+
const uniqueSet = /* @__PURE__ */ new Set();
|
|
8065
|
+
data.forEach((row) => {
|
|
8066
|
+
const value = row[column];
|
|
8067
|
+
if (value !== null && value !== void 0) {
|
|
8068
|
+
uniqueSet.add(String(value));
|
|
8069
|
+
}
|
|
8070
|
+
});
|
|
8071
|
+
return Array.from(uniqueSet).sort();
|
|
8072
|
+
}
|
|
8073
|
+
function getAggregatorFn(aggregator) {
|
|
8074
|
+
if (typeof aggregator === "function") {
|
|
8075
|
+
return aggregator;
|
|
8076
|
+
}
|
|
8077
|
+
return AGGREGATORS[aggregator] || AGGREGATORS.sum;
|
|
8078
|
+
}
|
|
8079
|
+
function toNumber(value) {
|
|
8080
|
+
if (typeof value === "number") return value;
|
|
8081
|
+
if (typeof value === "string") {
|
|
8082
|
+
const num = parseFloat(value);
|
|
8083
|
+
return isNaN(num) ? 0 : num;
|
|
8084
|
+
}
|
|
8085
|
+
return 0;
|
|
8086
|
+
}
|
|
8087
|
+
function buildPivot(data, config) {
|
|
8088
|
+
const {
|
|
8089
|
+
pivotColumn,
|
|
8090
|
+
valueColumn,
|
|
8091
|
+
rowGroupColumn,
|
|
8092
|
+
aggregator,
|
|
8093
|
+
showTotals = false,
|
|
8094
|
+
showGrandTotal = false
|
|
8095
|
+
} = config;
|
|
8096
|
+
if (!data || data.length === 0) {
|
|
8097
|
+
return {
|
|
8098
|
+
columns: [],
|
|
8099
|
+
rows: [],
|
|
8100
|
+
pivotValues: []
|
|
8101
|
+
};
|
|
8102
|
+
}
|
|
8103
|
+
const pivotValues = getUniqueValues(data, pivotColumn);
|
|
8104
|
+
const rowGroupValues = getUniqueValues(data, rowGroupColumn);
|
|
8105
|
+
const aggregatorFn = getAggregatorFn(aggregator);
|
|
8106
|
+
const groupedData = /* @__PURE__ */ new Map();
|
|
8107
|
+
rowGroupValues.forEach((rowValue) => {
|
|
8108
|
+
const pivotMap = /* @__PURE__ */ new Map();
|
|
8109
|
+
pivotValues.forEach((pivotValue) => {
|
|
8110
|
+
pivotMap.set(pivotValue, []);
|
|
8111
|
+
});
|
|
8112
|
+
groupedData.set(rowValue, pivotMap);
|
|
8113
|
+
});
|
|
8114
|
+
data.forEach((row) => {
|
|
8115
|
+
const rowValue = String(row[rowGroupColumn] ?? "");
|
|
8116
|
+
const pivotValue = String(row[pivotColumn] ?? "");
|
|
8117
|
+
const value = toNumber(row[valueColumn]);
|
|
8118
|
+
if (groupedData.has(rowValue)) {
|
|
8119
|
+
const pivotMap = groupedData.get(rowValue);
|
|
8120
|
+
if (pivotMap.has(pivotValue)) {
|
|
8121
|
+
pivotMap.get(pivotValue).push(value);
|
|
8122
|
+
}
|
|
8123
|
+
}
|
|
8124
|
+
});
|
|
8125
|
+
const columns = [
|
|
8126
|
+
{
|
|
8127
|
+
field: rowGroupColumn,
|
|
8128
|
+
headerName: formatHeaderName(rowGroupColumn),
|
|
8129
|
+
width: 180,
|
|
8130
|
+
sortable: true,
|
|
8131
|
+
filterable: true,
|
|
8132
|
+
isPivotColumn: false
|
|
8133
|
+
}
|
|
8134
|
+
];
|
|
8135
|
+
pivotValues.forEach((pivotValue) => {
|
|
8136
|
+
columns.push({
|
|
8137
|
+
field: pivotValue,
|
|
8138
|
+
headerName: pivotValue,
|
|
8139
|
+
width: 120,
|
|
8140
|
+
sortable: true,
|
|
8141
|
+
filterable: true,
|
|
8142
|
+
isPivotColumn: true
|
|
8143
|
+
});
|
|
8144
|
+
});
|
|
8145
|
+
if (showGrandTotal) {
|
|
8146
|
+
columns.push({
|
|
8147
|
+
field: "__grandTotal",
|
|
8148
|
+
headerName: "Grand Total",
|
|
8149
|
+
width: 120,
|
|
8150
|
+
sortable: true,
|
|
8151
|
+
filterable: false,
|
|
8152
|
+
isTotalColumn: true
|
|
8153
|
+
});
|
|
8154
|
+
}
|
|
8155
|
+
const rows = [];
|
|
8156
|
+
rowGroupValues.forEach((rowValue) => {
|
|
8157
|
+
const pivotMap = groupedData.get(rowValue);
|
|
8158
|
+
const row = {
|
|
8159
|
+
[rowGroupColumn]: rowValue,
|
|
8160
|
+
__id: rowValue
|
|
8161
|
+
// Add unique ID for grid
|
|
8162
|
+
};
|
|
8163
|
+
let grandTotal = 0;
|
|
8164
|
+
pivotValues.forEach((pivotValue) => {
|
|
8165
|
+
const values = pivotMap.get(pivotValue);
|
|
8166
|
+
const aggregatedValue = aggregatorFn(values);
|
|
8167
|
+
row[pivotValue] = aggregatedValue;
|
|
8168
|
+
grandTotal += aggregatedValue;
|
|
8169
|
+
});
|
|
8170
|
+
if (showGrandTotal) {
|
|
8171
|
+
row["__grandTotal"] = grandTotal;
|
|
8172
|
+
}
|
|
8173
|
+
rows.push(row);
|
|
8174
|
+
});
|
|
8175
|
+
let totalsRow;
|
|
8176
|
+
if (showTotals) {
|
|
8177
|
+
totalsRow = {
|
|
8178
|
+
[rowGroupColumn]: "Total",
|
|
8179
|
+
__id: "__totals",
|
|
8180
|
+
__isTotal: true
|
|
8181
|
+
};
|
|
8182
|
+
let overallGrandTotal = 0;
|
|
8183
|
+
pivotValues.forEach((pivotValue) => {
|
|
8184
|
+
const allValues = [];
|
|
8185
|
+
groupedData.forEach((pivotMap) => {
|
|
8186
|
+
const values = pivotMap.get(pivotValue);
|
|
8187
|
+
allValues.push(...values);
|
|
8188
|
+
});
|
|
8189
|
+
const total = aggregatorFn(allValues);
|
|
8190
|
+
totalsRow[pivotValue] = total;
|
|
8191
|
+
overallGrandTotal += total;
|
|
8192
|
+
});
|
|
8193
|
+
if (showGrandTotal) {
|
|
8194
|
+
totalsRow["__grandTotal"] = overallGrandTotal;
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
8197
|
+
return {
|
|
8198
|
+
columns,
|
|
8199
|
+
rows,
|
|
8200
|
+
pivotValues,
|
|
8201
|
+
totalsRow
|
|
8202
|
+
};
|
|
8203
|
+
}
|
|
8204
|
+
function formatHeaderName(field) {
|
|
8205
|
+
return field.split(/(?=[A-Z])|_|-/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
8206
|
+
}
|
|
8207
|
+
function exportPivotToCSV(pivotResult) {
|
|
8208
|
+
const { columns, rows, totalsRow } = pivotResult;
|
|
8209
|
+
const headers = columns.map((col) => col.headerName).join(",");
|
|
8210
|
+
const dataRows = rows.map((row) => {
|
|
8211
|
+
return columns.map((col) => {
|
|
8212
|
+
const value = row[col.field];
|
|
8213
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
8214
|
+
return `"${value}"`;
|
|
8215
|
+
}
|
|
8216
|
+
return value ?? "";
|
|
8217
|
+
}).join(",");
|
|
8218
|
+
});
|
|
8219
|
+
if (totalsRow) {
|
|
8220
|
+
const totalRow = columns.map((col) => {
|
|
8221
|
+
const value = totalsRow[col.field];
|
|
8222
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
8223
|
+
return `"${value}"`;
|
|
8224
|
+
}
|
|
8225
|
+
return value ?? "";
|
|
8226
|
+
}).join(",");
|
|
8227
|
+
dataRows.push(totalRow);
|
|
8228
|
+
}
|
|
8229
|
+
return [headers, ...dataRows].join("\n");
|
|
8230
|
+
}
|
|
8231
|
+
function downloadCSV(csvContent, filename = "pivot-table.csv") {
|
|
8232
|
+
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
8233
|
+
const link = document.createElement("a");
|
|
8234
|
+
const url = URL.createObjectURL(blob);
|
|
8235
|
+
link.setAttribute("href", url);
|
|
8236
|
+
link.setAttribute("download", filename);
|
|
8237
|
+
link.style.visibility = "hidden";
|
|
8238
|
+
document.body.appendChild(link);
|
|
8239
|
+
link.click();
|
|
8240
|
+
document.body.removeChild(link);
|
|
8241
|
+
URL.revokeObjectURL(url);
|
|
8242
|
+
}
|
|
8243
|
+
|
|
8046
8244
|
// src/components/DataGrid/DataGrid.tsx
|
|
8047
8245
|
var DataGrid = forwardRef(({
|
|
8048
8246
|
columns,
|
|
@@ -8057,6 +8255,7 @@ var DataGrid = forwardRef(({
|
|
|
8057
8255
|
rowPinConfig,
|
|
8058
8256
|
contextMenuConfig,
|
|
8059
8257
|
tooltipConfig,
|
|
8258
|
+
pivotConfig,
|
|
8060
8259
|
tableId,
|
|
8061
8260
|
theme: _theme = "quartz",
|
|
8062
8261
|
densityMode: _densityMode = "normal",
|
|
@@ -8077,6 +8276,42 @@ var DataGrid = forwardRef(({
|
|
|
8077
8276
|
);
|
|
8078
8277
|
const [internalRows, setInternalRows] = useState14(null);
|
|
8079
8278
|
const activeRows = internalRows !== null ? internalRows : rows;
|
|
8279
|
+
const { pivotedData, effectiveColumns } = useMemo4(() => {
|
|
8280
|
+
if (!pivotConfig) {
|
|
8281
|
+
return { pivotedData: activeRows, effectiveColumns: columns };
|
|
8282
|
+
}
|
|
8283
|
+
const pivotResult = buildPivot(activeRows, pivotConfig);
|
|
8284
|
+
console.log("Pivot Result:", {
|
|
8285
|
+
columnsCount: pivotResult.columns.length,
|
|
8286
|
+
columns: pivotResult.columns.map((c) => c.field),
|
|
8287
|
+
rowsCount: pivotResult.rows.length,
|
|
8288
|
+
sampleRow: pivotResult.rows[0]
|
|
8289
|
+
});
|
|
8290
|
+
const pivotColumns = pivotResult.columns.map((col) => ({
|
|
8291
|
+
field: col.field,
|
|
8292
|
+
headerName: col.headerName,
|
|
8293
|
+
width: col.width || 120,
|
|
8294
|
+
sortable: col.sortable !== false,
|
|
8295
|
+
filterable: col.filterable !== false,
|
|
8296
|
+
editable: false,
|
|
8297
|
+
renderCell: col.isTotalColumn || col.isPivotColumn ? (row) => {
|
|
8298
|
+
const value = row[col.field];
|
|
8299
|
+
if (typeof value === "number") {
|
|
8300
|
+
return /* @__PURE__ */ React20.createElement("span", { style: {
|
|
8301
|
+
fontWeight: col.isTotalColumn ? "700" : "600",
|
|
8302
|
+
color: col.isTotalColumn ? "#0f766e" : "#475569"
|
|
8303
|
+
} }, value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
|
|
8304
|
+
}
|
|
8305
|
+
return value;
|
|
8306
|
+
} : void 0
|
|
8307
|
+
}));
|
|
8308
|
+
const allRows = pivotResult.totalsRow ? [...pivotResult.rows, pivotResult.totalsRow] : pivotResult.rows;
|
|
8309
|
+
const rowsWithId = allRows.map((row, index) => ({
|
|
8310
|
+
...row,
|
|
8311
|
+
id: row.__id || row.id || `pivot-row-${index}`
|
|
8312
|
+
}));
|
|
8313
|
+
return { pivotedData: rowsWithId, effectiveColumns: pivotColumns };
|
|
8314
|
+
}, [activeRows, columns, pivotConfig]);
|
|
8080
8315
|
const rowsRef = useRef10(rows);
|
|
8081
8316
|
useEffect9(() => {
|
|
8082
8317
|
if (rows !== rowsRef.current) {
|
|
@@ -8179,8 +8414,8 @@ var DataGrid = forwardRef(({
|
|
|
8179
8414
|
gridApiRef.current = new GridApiImpl(
|
|
8180
8415
|
state,
|
|
8181
8416
|
dispatch,
|
|
8182
|
-
|
|
8183
|
-
|
|
8417
|
+
effectiveColumns,
|
|
8418
|
+
pivotedData,
|
|
8184
8419
|
containerRef,
|
|
8185
8420
|
persistenceManager,
|
|
8186
8421
|
setInternalRows
|
|
@@ -8190,24 +8425,24 @@ var DataGrid = forwardRef(({
|
|
|
8190
8425
|
useEffect9(() => {
|
|
8191
8426
|
if (gridApiRef.current && !gridApiRef.current.isDestroyed()) {
|
|
8192
8427
|
gridApiRef.current.updateState(state);
|
|
8193
|
-
gridApiRef.current.updateData(
|
|
8428
|
+
gridApiRef.current.updateData(effectiveColumns, pivotedData);
|
|
8194
8429
|
gridApiRef.current.updateCallbacks(setInternalRows);
|
|
8195
8430
|
}
|
|
8196
|
-
}, [state,
|
|
8431
|
+
}, [state, effectiveColumns, pivotedData, setInternalRows]);
|
|
8197
8432
|
useImperativeHandle(ref, () => {
|
|
8198
8433
|
if (!gridApiRef.current || gridApiRef.current.isDestroyed()) {
|
|
8199
8434
|
gridApiRef.current = new GridApiImpl(
|
|
8200
8435
|
state,
|
|
8201
8436
|
dispatch,
|
|
8202
|
-
|
|
8203
|
-
|
|
8437
|
+
effectiveColumns,
|
|
8438
|
+
pivotedData,
|
|
8204
8439
|
containerRef,
|
|
8205
8440
|
persistenceManager,
|
|
8206
8441
|
setInternalRows
|
|
8207
8442
|
);
|
|
8208
8443
|
}
|
|
8209
8444
|
return gridApiRef.current;
|
|
8210
|
-
}, [state,
|
|
8445
|
+
}, [state, effectiveColumns, pivotedData, persistenceManager]);
|
|
8211
8446
|
const onGridReadyCalledRef = useRef10(false);
|
|
8212
8447
|
const onGridReadyCallbackRef = useRef10(onGridReady);
|
|
8213
8448
|
useEffect9(() => {
|
|
@@ -8288,9 +8523,11 @@ var DataGrid = forwardRef(({
|
|
|
8288
8523
|
...middleColumns,
|
|
8289
8524
|
...pinnedRightFields
|
|
8290
8525
|
];
|
|
8526
|
+
console.log("Display column order:", displayColumnOrder, "from state.columnOrder:", state.columnOrder);
|
|
8291
8527
|
useEffect9(() => {
|
|
8292
|
-
|
|
8293
|
-
|
|
8528
|
+
console.log("Dispatching RESET_COLUMNS with", effectiveColumns.length, "columns:", effectiveColumns.map((c) => c.field));
|
|
8529
|
+
dispatch({ type: "RESET_COLUMNS", payload: effectiveColumns });
|
|
8530
|
+
}, [effectiveColumns]);
|
|
8294
8531
|
useEffect9(() => {
|
|
8295
8532
|
if (onSelectionChange) {
|
|
8296
8533
|
onSelectionChange(Array.from(state.selection.selectedRows));
|
|
@@ -8306,17 +8543,17 @@ var DataGrid = forwardRef(({
|
|
|
8306
8543
|
}, [state.pinnedRowsTop, state.pinnedRowsBottom]);
|
|
8307
8544
|
useEffect9(() => {
|
|
8308
8545
|
if (state.sortConfig.field) {
|
|
8309
|
-
const column =
|
|
8546
|
+
const column = effectiveColumns.find((c) => c.field === state.sortConfig.field);
|
|
8310
8547
|
if (column) {
|
|
8311
8548
|
announceSorting(column.headerName, state.sortConfig.direction);
|
|
8312
8549
|
}
|
|
8313
8550
|
}
|
|
8314
|
-
}, [state.sortConfig.field, state.sortConfig.direction]);
|
|
8551
|
+
}, [state.sortConfig.field, state.sortConfig.direction, effectiveColumns]);
|
|
8315
8552
|
const sortedRows = useMemo4(() => {
|
|
8316
8553
|
if (!state.sortConfig.field || !state.sortConfig.direction) {
|
|
8317
|
-
return
|
|
8554
|
+
return pivotedData;
|
|
8318
8555
|
}
|
|
8319
|
-
const sorted = [...
|
|
8556
|
+
const sorted = [...pivotedData].sort((a, b) => {
|
|
8320
8557
|
const aValue = a[state.sortConfig.field];
|
|
8321
8558
|
const bValue = b[state.sortConfig.field];
|
|
8322
8559
|
if (aValue == null && bValue == null) return 0;
|
|
@@ -8333,7 +8570,7 @@ var DataGrid = forwardRef(({
|
|
|
8333
8570
|
sorted.reverse();
|
|
8334
8571
|
}
|
|
8335
8572
|
return sorted;
|
|
8336
|
-
}, [
|
|
8573
|
+
}, [pivotedData, state.sortConfig]);
|
|
8337
8574
|
const filteredRows = useMemo4(() => {
|
|
8338
8575
|
if (!hasActiveFilters(state.filterConfig)) {
|
|
8339
8576
|
return sortedRows;
|
|
@@ -8479,7 +8716,7 @@ var DataGrid = forwardRef(({
|
|
|
8479
8716
|
/* @__PURE__ */ React20.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__ */ React20.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React20.createElement(
|
|
8480
8717
|
ColumnChooser,
|
|
8481
8718
|
{
|
|
8482
|
-
columns,
|
|
8719
|
+
columns: effectiveColumns,
|
|
8483
8720
|
columnOrder: state.columnOrder,
|
|
8484
8721
|
hiddenColumns: state.hiddenColumns,
|
|
8485
8722
|
onToggleVisibility: (field) => dispatch({ type: "TOGGLE_COLUMN_VISIBILITY", payload: field }),
|
|
@@ -8489,7 +8726,7 @@ var DataGrid = forwardRef(({
|
|
|
8489
8726
|
), /* @__PURE__ */ React20.createElement(
|
|
8490
8727
|
ExportMenu,
|
|
8491
8728
|
{
|
|
8492
|
-
columns,
|
|
8729
|
+
columns: effectiveColumns,
|
|
8493
8730
|
fullDataset: rows,
|
|
8494
8731
|
filteredData: filteredRows.filter((r) => !("isGroup" in r)),
|
|
8495
8732
|
selectedRows: state.selection.selectedRows,
|
|
@@ -8507,7 +8744,7 @@ var DataGrid = forwardRef(({
|
|
|
8507
8744
|
/* @__PURE__ */ React20.createElement(
|
|
8508
8745
|
GroupByPanel,
|
|
8509
8746
|
{
|
|
8510
|
-
columns,
|
|
8747
|
+
columns: effectiveColumns,
|
|
8511
8748
|
groupBy: state.groupBy,
|
|
8512
8749
|
dispatch
|
|
8513
8750
|
}
|
|
@@ -8515,7 +8752,7 @@ var DataGrid = forwardRef(({
|
|
|
8515
8752
|
/* @__PURE__ */ React20.createElement("div", { role: "rowgroup", style: { position: "sticky", top: 0, zIndex: 20, width: "100%" } }, /* @__PURE__ */ React20.createElement(
|
|
8516
8753
|
GridHeader,
|
|
8517
8754
|
{
|
|
8518
|
-
columns,
|
|
8755
|
+
columns: effectiveColumns,
|
|
8519
8756
|
columnOrder: state.columnOrder,
|
|
8520
8757
|
displayColumnOrder,
|
|
8521
8758
|
columnWidths: state.columnWidths,
|
|
@@ -8534,7 +8771,7 @@ var DataGrid = forwardRef(({
|
|
|
8534
8771
|
), /* @__PURE__ */ React20.createElement(
|
|
8535
8772
|
ColumnFilters,
|
|
8536
8773
|
{
|
|
8537
|
-
columns,
|
|
8774
|
+
columns: effectiveColumns,
|
|
8538
8775
|
displayColumnOrder,
|
|
8539
8776
|
columnWidths: state.columnWidths,
|
|
8540
8777
|
filterConfig: state.filterConfig,
|
|
@@ -8547,7 +8784,7 @@ var DataGrid = forwardRef(({
|
|
|
8547
8784
|
/* @__PURE__ */ React20.createElement(
|
|
8548
8785
|
GridBody,
|
|
8549
8786
|
{
|
|
8550
|
-
columns,
|
|
8787
|
+
columns: effectiveColumns,
|
|
8551
8788
|
rows: (virtualScrollConfig == null ? void 0 : virtualScrollConfig.enabled) ? unpinnedRows : paginatedRows,
|
|
8552
8789
|
pinnedRowsTop: pinnedRowsTopData,
|
|
8553
8790
|
pinnedRowsBottom: pinnedRowsBottomData,
|
|
@@ -8587,7 +8824,7 @@ var DataGrid = forwardRef(({
|
|
|
8587
8824
|
(footerConfig == null ? void 0 : footerConfig.show) && footerConfig.aggregates && /* @__PURE__ */ React20.createElement(
|
|
8588
8825
|
GridFooter,
|
|
8589
8826
|
{
|
|
8590
|
-
columns,
|
|
8827
|
+
columns: effectiveColumns,
|
|
8591
8828
|
displayColumnOrder,
|
|
8592
8829
|
columnWidths: state.columnWidths,
|
|
8593
8830
|
aggregates: globalAggregates,
|
|
@@ -11752,6 +11989,1532 @@ function createMockFeed(config) {
|
|
|
11752
11989
|
createConnection: () => createMockWebSocket(feed)
|
|
11753
11990
|
};
|
|
11754
11991
|
}
|
|
11992
|
+
|
|
11993
|
+
// src/components/DataGrid/PivotToolbar.tsx
|
|
11994
|
+
import React29, { useState as useState20, useMemo as useMemo8 } from "react";
|
|
11995
|
+
var AGGREGATOR_OPTIONS = [
|
|
11996
|
+
{ value: "sum", label: "Sum" },
|
|
11997
|
+
{ value: "avg", label: "Average" },
|
|
11998
|
+
{ value: "count", label: "Count" },
|
|
11999
|
+
{ value: "min", label: "Minimum" },
|
|
12000
|
+
{ value: "max", label: "Maximum" }
|
|
12001
|
+
];
|
|
12002
|
+
var PivotToolbar = ({
|
|
12003
|
+
columns,
|
|
12004
|
+
pivotConfig,
|
|
12005
|
+
onPivotToggle,
|
|
12006
|
+
onConfigChange,
|
|
12007
|
+
isPivotMode = false,
|
|
12008
|
+
style,
|
|
12009
|
+
className
|
|
12010
|
+
}) => {
|
|
12011
|
+
const [isExpanded, setIsExpanded] = useState20(isPivotMode);
|
|
12012
|
+
const [rowGroupColumn, setRowGroupColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.rowGroupColumn) || "");
|
|
12013
|
+
const [pivotColumn, setPivotColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.pivotColumn) || "");
|
|
12014
|
+
const [valueColumn, setValueColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.valueColumn) || "");
|
|
12015
|
+
const [aggregator, setAggregator] = useState20(
|
|
12016
|
+
(pivotConfig == null ? void 0 : pivotConfig.aggregator) || "sum"
|
|
12017
|
+
);
|
|
12018
|
+
const [showTotals, setShowTotals] = useState20((pivotConfig == null ? void 0 : pivotConfig.showTotals) ?? true);
|
|
12019
|
+
const [showGrandTotal, setShowGrandTotal] = useState20((pivotConfig == null ? void 0 : pivotConfig.showGrandTotal) ?? true);
|
|
12020
|
+
const isConfigValid = useMemo8(() => {
|
|
12021
|
+
return rowGroupColumn && pivotColumn && valueColumn;
|
|
12022
|
+
}, [rowGroupColumn, pivotColumn, valueColumn]);
|
|
12023
|
+
const handleApply = () => {
|
|
12024
|
+
if (!isConfigValid) return;
|
|
12025
|
+
const config = {
|
|
12026
|
+
rowGroupColumn,
|
|
12027
|
+
pivotColumn,
|
|
12028
|
+
valueColumn,
|
|
12029
|
+
aggregator,
|
|
12030
|
+
showTotals,
|
|
12031
|
+
showGrandTotal
|
|
12032
|
+
};
|
|
12033
|
+
onConfigChange(config);
|
|
12034
|
+
onPivotToggle(true);
|
|
12035
|
+
};
|
|
12036
|
+
const handleClear = () => {
|
|
12037
|
+
setRowGroupColumn("");
|
|
12038
|
+
setPivotColumn("");
|
|
12039
|
+
setValueColumn("");
|
|
12040
|
+
setAggregator("sum");
|
|
12041
|
+
setShowTotals(true);
|
|
12042
|
+
setShowGrandTotal(true);
|
|
12043
|
+
onConfigChange(null);
|
|
12044
|
+
onPivotToggle(false);
|
|
12045
|
+
setIsExpanded(false);
|
|
12046
|
+
};
|
|
12047
|
+
const handleToggle = () => {
|
|
12048
|
+
setIsExpanded(!isExpanded);
|
|
12049
|
+
};
|
|
12050
|
+
return /* @__PURE__ */ React29.createElement(
|
|
12051
|
+
"div",
|
|
12052
|
+
{
|
|
12053
|
+
className,
|
|
12054
|
+
style: {
|
|
12055
|
+
backgroundColor: "#f8fafc",
|
|
12056
|
+
border: "1px solid #e2e8f0",
|
|
12057
|
+
borderRadius: "8px",
|
|
12058
|
+
padding: "12px",
|
|
12059
|
+
...style
|
|
12060
|
+
}
|
|
12061
|
+
},
|
|
12062
|
+
/* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: isExpanded ? "12px" : "0" } }, /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React29.createElement("span", { style: { fontSize: "20px" } }, "\u{1F4CA}"), /* @__PURE__ */ React29.createElement("h3", { style: { margin: 0, fontSize: "14px", fontWeight: "600", color: "#1e293b" } }, "Pivot Table"), isPivotMode && /* @__PURE__ */ React29.createElement("span", { style: {
|
|
12063
|
+
backgroundColor: "#10b981",
|
|
12064
|
+
color: "white",
|
|
12065
|
+
padding: "2px 8px",
|
|
12066
|
+
borderRadius: "12px",
|
|
12067
|
+
fontSize: "11px",
|
|
12068
|
+
fontWeight: "600"
|
|
12069
|
+
} }, "Active")), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", gap: "8px" } }, isPivotMode && /* @__PURE__ */ React29.createElement(
|
|
12070
|
+
"button",
|
|
12071
|
+
{
|
|
12072
|
+
onClick: handleClear,
|
|
12073
|
+
style: {
|
|
12074
|
+
padding: "6px 12px",
|
|
12075
|
+
fontSize: "13px",
|
|
12076
|
+
fontWeight: "500",
|
|
12077
|
+
color: "#dc2626",
|
|
12078
|
+
backgroundColor: "#fee2e2",
|
|
12079
|
+
border: "1px solid #fecaca",
|
|
12080
|
+
borderRadius: "6px",
|
|
12081
|
+
cursor: "pointer",
|
|
12082
|
+
transition: "all 0.15s"
|
|
12083
|
+
},
|
|
12084
|
+
onMouseEnter: (e) => {
|
|
12085
|
+
e.currentTarget.style.backgroundColor = "#fecaca";
|
|
12086
|
+
},
|
|
12087
|
+
onMouseLeave: (e) => {
|
|
12088
|
+
e.currentTarget.style.backgroundColor = "#fee2e2";
|
|
12089
|
+
}
|
|
12090
|
+
},
|
|
12091
|
+
"Clear Pivot"
|
|
12092
|
+
), /* @__PURE__ */ React29.createElement(
|
|
12093
|
+
"button",
|
|
12094
|
+
{
|
|
12095
|
+
onClick: handleToggle,
|
|
12096
|
+
style: {
|
|
12097
|
+
padding: "6px 10px",
|
|
12098
|
+
fontSize: "13px",
|
|
12099
|
+
color: "#64748b",
|
|
12100
|
+
backgroundColor: "white",
|
|
12101
|
+
border: "1px solid #cbd5e1",
|
|
12102
|
+
borderRadius: "6px",
|
|
12103
|
+
cursor: "pointer",
|
|
12104
|
+
transition: "all 0.15s"
|
|
12105
|
+
},
|
|
12106
|
+
onMouseEnter: (e) => {
|
|
12107
|
+
e.currentTarget.style.backgroundColor = "#f1f5f9";
|
|
12108
|
+
},
|
|
12109
|
+
onMouseLeave: (e) => {
|
|
12110
|
+
e.currentTarget.style.backgroundColor = "white";
|
|
12111
|
+
}
|
|
12112
|
+
},
|
|
12113
|
+
isExpanded ? "\u25B2" : "\u25BC"
|
|
12114
|
+
))),
|
|
12115
|
+
isExpanded && /* @__PURE__ */ React29.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "12px" } }, /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Row Group By ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
|
|
12116
|
+
"select",
|
|
12117
|
+
{
|
|
12118
|
+
value: rowGroupColumn,
|
|
12119
|
+
onChange: (e) => setRowGroupColumn(e.target.value),
|
|
12120
|
+
style: {
|
|
12121
|
+
width: "100%",
|
|
12122
|
+
padding: "8px",
|
|
12123
|
+
fontSize: "13px",
|
|
12124
|
+
border: "1px solid #cbd5e1",
|
|
12125
|
+
borderRadius: "6px",
|
|
12126
|
+
backgroundColor: "white",
|
|
12127
|
+
color: "#1e293b",
|
|
12128
|
+
cursor: "pointer"
|
|
12129
|
+
}
|
|
12130
|
+
},
|
|
12131
|
+
/* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
|
|
12132
|
+
columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12133
|
+
)), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Pivot Column ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
|
|
12134
|
+
"select",
|
|
12135
|
+
{
|
|
12136
|
+
value: pivotColumn,
|
|
12137
|
+
onChange: (e) => setPivotColumn(e.target.value),
|
|
12138
|
+
style: {
|
|
12139
|
+
width: "100%",
|
|
12140
|
+
padding: "8px",
|
|
12141
|
+
fontSize: "13px",
|
|
12142
|
+
border: "1px solid #cbd5e1",
|
|
12143
|
+
borderRadius: "6px",
|
|
12144
|
+
backgroundColor: "white",
|
|
12145
|
+
color: "#1e293b",
|
|
12146
|
+
cursor: "pointer"
|
|
12147
|
+
}
|
|
12148
|
+
},
|
|
12149
|
+
/* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
|
|
12150
|
+
columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12151
|
+
)), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Value Column ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
|
|
12152
|
+
"select",
|
|
12153
|
+
{
|
|
12154
|
+
value: valueColumn,
|
|
12155
|
+
onChange: (e) => setValueColumn(e.target.value),
|
|
12156
|
+
style: {
|
|
12157
|
+
width: "100%",
|
|
12158
|
+
padding: "8px",
|
|
12159
|
+
fontSize: "13px",
|
|
12160
|
+
border: "1px solid #cbd5e1",
|
|
12161
|
+
borderRadius: "6px",
|
|
12162
|
+
backgroundColor: "white",
|
|
12163
|
+
color: "#1e293b",
|
|
12164
|
+
cursor: "pointer"
|
|
12165
|
+
}
|
|
12166
|
+
},
|
|
12167
|
+
/* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
|
|
12168
|
+
columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
|
|
12169
|
+
)), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Aggregation"), /* @__PURE__ */ React29.createElement(
|
|
12170
|
+
"select",
|
|
12171
|
+
{
|
|
12172
|
+
value: aggregator,
|
|
12173
|
+
onChange: (e) => setAggregator(e.target.value),
|
|
12174
|
+
style: {
|
|
12175
|
+
width: "100%",
|
|
12176
|
+
padding: "8px",
|
|
12177
|
+
fontSize: "13px",
|
|
12178
|
+
border: "1px solid #cbd5e1",
|
|
12179
|
+
borderRadius: "6px",
|
|
12180
|
+
backgroundColor: "white",
|
|
12181
|
+
color: "#1e293b",
|
|
12182
|
+
cursor: "pointer"
|
|
12183
|
+
}
|
|
12184
|
+
},
|
|
12185
|
+
AGGREGATOR_OPTIONS.map((opt) => /* @__PURE__ */ React29.createElement("option", { key: opt.value, value: opt.value }, opt.label))
|
|
12186
|
+
)), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "8px", justifyContent: "center" } }, /* @__PURE__ */ React29.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ React29.createElement(
|
|
12187
|
+
"input",
|
|
12188
|
+
{
|
|
12189
|
+
type: "checkbox",
|
|
12190
|
+
checked: showTotals,
|
|
12191
|
+
onChange: (e) => setShowTotals(e.target.checked),
|
|
12192
|
+
style: { cursor: "pointer" }
|
|
12193
|
+
}
|
|
12194
|
+
), "Show Totals Row"), /* @__PURE__ */ React29.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ React29.createElement(
|
|
12195
|
+
"input",
|
|
12196
|
+
{
|
|
12197
|
+
type: "checkbox",
|
|
12198
|
+
checked: showGrandTotal,
|
|
12199
|
+
onChange: (e) => setShowGrandTotal(e.target.checked),
|
|
12200
|
+
style: { cursor: "pointer" }
|
|
12201
|
+
}
|
|
12202
|
+
), "Show Grand Total Column")), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "flex-end" } }, /* @__PURE__ */ React29.createElement(
|
|
12203
|
+
"button",
|
|
12204
|
+
{
|
|
12205
|
+
onClick: handleApply,
|
|
12206
|
+
disabled: !isConfigValid,
|
|
12207
|
+
style: {
|
|
12208
|
+
width: "100%",
|
|
12209
|
+
padding: "10px",
|
|
12210
|
+
fontSize: "14px",
|
|
12211
|
+
fontWeight: "600",
|
|
12212
|
+
color: "white",
|
|
12213
|
+
backgroundColor: isConfigValid ? "#2563eb" : "#94a3b8",
|
|
12214
|
+
border: "none",
|
|
12215
|
+
borderRadius: "6px",
|
|
12216
|
+
cursor: isConfigValid ? "pointer" : "not-allowed",
|
|
12217
|
+
transition: "all 0.15s",
|
|
12218
|
+
boxShadow: isConfigValid ? "0 2px 4px rgba(37, 99, 235, 0.2)" : "none"
|
|
12219
|
+
},
|
|
12220
|
+
onMouseEnter: (e) => {
|
|
12221
|
+
if (isConfigValid) {
|
|
12222
|
+
e.currentTarget.style.backgroundColor = "#1d4ed8";
|
|
12223
|
+
}
|
|
12224
|
+
},
|
|
12225
|
+
onMouseLeave: (e) => {
|
|
12226
|
+
if (isConfigValid) {
|
|
12227
|
+
e.currentTarget.style.backgroundColor = "#2563eb";
|
|
12228
|
+
}
|
|
12229
|
+
}
|
|
12230
|
+
},
|
|
12231
|
+
"Apply Pivot"
|
|
12232
|
+
))),
|
|
12233
|
+
isExpanded && /* @__PURE__ */ React29.createElement("div", { style: {
|
|
12234
|
+
marginTop: "12px",
|
|
12235
|
+
padding: "10px",
|
|
12236
|
+
backgroundColor: "#eff6ff",
|
|
12237
|
+
border: "1px solid #bfdbfe",
|
|
12238
|
+
borderRadius: "6px",
|
|
12239
|
+
fontSize: "12px",
|
|
12240
|
+
color: "#1e40af"
|
|
12241
|
+
} }, /* @__PURE__ */ React29.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.")
|
|
12242
|
+
);
|
|
12243
|
+
};
|
|
12244
|
+
|
|
12245
|
+
// src/editors/editorUtils.ts
|
|
12246
|
+
import { useEffect as useEffect14, useRef as useRef15, useCallback as useCallback11 } from "react";
|
|
12247
|
+
function useEditorKeyboardNavigation(handlers, config = {}) {
|
|
12248
|
+
const {
|
|
12249
|
+
commitOnEnter = true,
|
|
12250
|
+
cancelOnEscape = true,
|
|
12251
|
+
commitOnTab = true,
|
|
12252
|
+
preventDefault = true,
|
|
12253
|
+
stopPropagation = true
|
|
12254
|
+
} = config;
|
|
12255
|
+
const handleKeyDown = useCallback11(
|
|
12256
|
+
(event) => {
|
|
12257
|
+
let handled = false;
|
|
12258
|
+
switch (event.key) {
|
|
12259
|
+
case "Enter":
|
|
12260
|
+
if (commitOnEnter && handlers.onEnter) {
|
|
12261
|
+
handlers.onEnter();
|
|
12262
|
+
handled = true;
|
|
12263
|
+
}
|
|
12264
|
+
break;
|
|
12265
|
+
case "Escape":
|
|
12266
|
+
if (cancelOnEscape && handlers.onEscape) {
|
|
12267
|
+
handlers.onEscape();
|
|
12268
|
+
handled = true;
|
|
12269
|
+
}
|
|
12270
|
+
break;
|
|
12271
|
+
case "Tab":
|
|
12272
|
+
if (commitOnTab && handlers.onTab) {
|
|
12273
|
+
handlers.onTab(event.shiftKey);
|
|
12274
|
+
handled = true;
|
|
12275
|
+
}
|
|
12276
|
+
break;
|
|
12277
|
+
case "ArrowUp":
|
|
12278
|
+
if (handlers.onArrowUp) {
|
|
12279
|
+
handlers.onArrowUp();
|
|
12280
|
+
handled = true;
|
|
12281
|
+
}
|
|
12282
|
+
break;
|
|
12283
|
+
case "ArrowDown":
|
|
12284
|
+
if (handlers.onArrowDown) {
|
|
12285
|
+
handlers.onArrowDown();
|
|
12286
|
+
handled = true;
|
|
12287
|
+
}
|
|
12288
|
+
break;
|
|
12289
|
+
}
|
|
12290
|
+
if (handled) {
|
|
12291
|
+
if (preventDefault) {
|
|
12292
|
+
event.preventDefault();
|
|
12293
|
+
}
|
|
12294
|
+
if (stopPropagation) {
|
|
12295
|
+
event.stopPropagation();
|
|
12296
|
+
}
|
|
12297
|
+
}
|
|
12298
|
+
},
|
|
12299
|
+
[
|
|
12300
|
+
handlers,
|
|
12301
|
+
commitOnEnter,
|
|
12302
|
+
cancelOnEscape,
|
|
12303
|
+
commitOnTab,
|
|
12304
|
+
preventDefault,
|
|
12305
|
+
stopPropagation
|
|
12306
|
+
]
|
|
12307
|
+
);
|
|
12308
|
+
return { handleKeyDown };
|
|
12309
|
+
}
|
|
12310
|
+
function useEditorAutoFocus(autoFocus = true, selectAll = false) {
|
|
12311
|
+
const elementRef = useRef15(null);
|
|
12312
|
+
useEffect14(() => {
|
|
12313
|
+
if (autoFocus && elementRef.current) {
|
|
12314
|
+
elementRef.current.focus();
|
|
12315
|
+
if (selectAll && elementRef.current instanceof HTMLInputElement) {
|
|
12316
|
+
elementRef.current.select();
|
|
12317
|
+
}
|
|
12318
|
+
}
|
|
12319
|
+
}, [autoFocus, selectAll]);
|
|
12320
|
+
return elementRef;
|
|
12321
|
+
}
|
|
12322
|
+
function useEditorClickOutside(ref, onClickOutside, enabled = true) {
|
|
12323
|
+
useEffect14(() => {
|
|
12324
|
+
if (!enabled) return;
|
|
12325
|
+
const handleClickOutside = (event) => {
|
|
12326
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
12327
|
+
onClickOutside();
|
|
12328
|
+
}
|
|
12329
|
+
};
|
|
12330
|
+
const timeoutId = setTimeout(() => {
|
|
12331
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
12332
|
+
}, 0);
|
|
12333
|
+
return () => {
|
|
12334
|
+
clearTimeout(timeoutId);
|
|
12335
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
12336
|
+
};
|
|
12337
|
+
}, [ref, onClickOutside, enabled]);
|
|
12338
|
+
}
|
|
12339
|
+
function usePopupPosition(anchorRef, popupRef, isOpen, placement = "auto") {
|
|
12340
|
+
useEffect14(() => {
|
|
12341
|
+
if (!isOpen || !anchorRef.current || !popupRef.current) return;
|
|
12342
|
+
const anchor = anchorRef.current;
|
|
12343
|
+
const popup = popupRef.current;
|
|
12344
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
12345
|
+
const popupRect = popup.getBoundingClientRect();
|
|
12346
|
+
const viewportHeight = window.innerHeight;
|
|
12347
|
+
const viewportWidth = window.innerWidth;
|
|
12348
|
+
let top = 0;
|
|
12349
|
+
let left = 0;
|
|
12350
|
+
let actualPlacement = placement;
|
|
12351
|
+
if (placement === "auto") {
|
|
12352
|
+
const spaceBelow = viewportHeight - anchorRect.bottom;
|
|
12353
|
+
const spaceAbove = anchorRect.top;
|
|
12354
|
+
const spaceRight = viewportWidth - anchorRect.right;
|
|
12355
|
+
const spaceLeft = anchorRect.left;
|
|
12356
|
+
if (spaceBelow >= popupRect.height || spaceBelow >= spaceAbove) {
|
|
12357
|
+
actualPlacement = "bottom";
|
|
12358
|
+
} else if (spaceAbove >= popupRect.height) {
|
|
12359
|
+
actualPlacement = "top";
|
|
12360
|
+
} else if (spaceRight >= popupRect.width) {
|
|
12361
|
+
actualPlacement = "right";
|
|
12362
|
+
} else if (spaceLeft >= popupRect.width) {
|
|
12363
|
+
actualPlacement = "left";
|
|
12364
|
+
} else {
|
|
12365
|
+
actualPlacement = "bottom";
|
|
12366
|
+
}
|
|
12367
|
+
}
|
|
12368
|
+
switch (actualPlacement) {
|
|
12369
|
+
case "bottom":
|
|
12370
|
+
top = anchorRect.bottom + window.scrollY;
|
|
12371
|
+
left = anchorRect.left + window.scrollX;
|
|
12372
|
+
break;
|
|
12373
|
+
case "top":
|
|
12374
|
+
top = anchorRect.top + window.scrollY - popupRect.height;
|
|
12375
|
+
left = anchorRect.left + window.scrollX;
|
|
12376
|
+
break;
|
|
12377
|
+
case "right":
|
|
12378
|
+
top = anchorRect.top + window.scrollY;
|
|
12379
|
+
left = anchorRect.right + window.scrollX;
|
|
12380
|
+
break;
|
|
12381
|
+
case "left":
|
|
12382
|
+
top = anchorRect.top + window.scrollY;
|
|
12383
|
+
left = anchorRect.left + window.scrollX - popupRect.width;
|
|
12384
|
+
break;
|
|
12385
|
+
}
|
|
12386
|
+
const margin = 8;
|
|
12387
|
+
if (left + popupRect.width > viewportWidth) {
|
|
12388
|
+
left = viewportWidth - popupRect.width - margin;
|
|
12389
|
+
}
|
|
12390
|
+
if (left < margin) {
|
|
12391
|
+
left = margin;
|
|
12392
|
+
}
|
|
12393
|
+
if (top + popupRect.height > viewportHeight + window.scrollY) {
|
|
12394
|
+
top = viewportHeight + window.scrollY - popupRect.height - margin;
|
|
12395
|
+
}
|
|
12396
|
+
if (top < window.scrollY + margin) {
|
|
12397
|
+
top = window.scrollY + margin;
|
|
12398
|
+
}
|
|
12399
|
+
popup.style.top = `${top}px`;
|
|
12400
|
+
popup.style.left = `${left}px`;
|
|
12401
|
+
popup.style.minWidth = `${anchorRect.width}px`;
|
|
12402
|
+
}, [isOpen, anchorRef, popupRef, placement]);
|
|
12403
|
+
}
|
|
12404
|
+
function debounce2(func, wait) {
|
|
12405
|
+
let timeout = null;
|
|
12406
|
+
return function executedFunction(...args) {
|
|
12407
|
+
const later = () => {
|
|
12408
|
+
timeout = null;
|
|
12409
|
+
func(...args);
|
|
12410
|
+
};
|
|
12411
|
+
if (timeout) {
|
|
12412
|
+
clearTimeout(timeout);
|
|
12413
|
+
}
|
|
12414
|
+
timeout = setTimeout(later, wait);
|
|
12415
|
+
};
|
|
12416
|
+
}
|
|
12417
|
+
function formatNumber(value, decimals = 0, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12418
|
+
const fixed = value.toFixed(decimals);
|
|
12419
|
+
const [integerPart, decimalPart] = fixed.split(".");
|
|
12420
|
+
const formattedInteger = integerPart.replace(
|
|
12421
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
12422
|
+
thousandsSeparator
|
|
12423
|
+
);
|
|
12424
|
+
return decimalPart ? `${formattedInteger}${decimalSeparator}${decimalPart}` : formattedInteger;
|
|
12425
|
+
}
|
|
12426
|
+
function parseFormattedNumber(value, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12427
|
+
if (!value || typeof value !== "string") return null;
|
|
12428
|
+
const normalized = value.replace(new RegExp(`\\${thousandsSeparator}`, "g"), "").replace(new RegExp(`\\${decimalSeparator}`), ".");
|
|
12429
|
+
const parsed = parseFloat(normalized);
|
|
12430
|
+
return isNaN(parsed) ? null : parsed;
|
|
12431
|
+
}
|
|
12432
|
+
function filterOptions(options, searchQuery) {
|
|
12433
|
+
if (!searchQuery.trim()) return options;
|
|
12434
|
+
const lowerQuery = searchQuery.toLowerCase();
|
|
12435
|
+
return options.filter(
|
|
12436
|
+
(option) => option.label.toLowerCase().includes(lowerQuery)
|
|
12437
|
+
);
|
|
12438
|
+
}
|
|
12439
|
+
|
|
12440
|
+
// src/editors/RichSelectEditor.tsx
|
|
12441
|
+
import React30, { useState as useState21, useRef as useRef16, useEffect as useEffect15, useMemo as useMemo9 } from "react";
|
|
12442
|
+
function RichSelectEditor(props) {
|
|
12443
|
+
const {
|
|
12444
|
+
value,
|
|
12445
|
+
onChange,
|
|
12446
|
+
onCommit,
|
|
12447
|
+
onCancel,
|
|
12448
|
+
autoFocus = true,
|
|
12449
|
+
options,
|
|
12450
|
+
placeholder = "Select...",
|
|
12451
|
+
allowClear = false,
|
|
12452
|
+
filterable = true,
|
|
12453
|
+
renderOptionLabel,
|
|
12454
|
+
maxDropdownHeight = 300
|
|
12455
|
+
} = props;
|
|
12456
|
+
const [isOpen] = useState21(true);
|
|
12457
|
+
const [searchQuery, setSearchQuery] = useState21("");
|
|
12458
|
+
const [focusedIndex, setFocusedIndex] = useState21(-1);
|
|
12459
|
+
const containerRef = useRef16(null);
|
|
12460
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
12461
|
+
const dropdownRef = useRef16(null);
|
|
12462
|
+
const optionRefs = useRef16([]);
|
|
12463
|
+
const filteredOptions = useMemo9(
|
|
12464
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
12465
|
+
[options, searchQuery, filterable]
|
|
12466
|
+
);
|
|
12467
|
+
const selectedOption = useMemo9(
|
|
12468
|
+
() => options.find((opt) => opt.value === value),
|
|
12469
|
+
[options, value]
|
|
12470
|
+
);
|
|
12471
|
+
useEffect15(() => {
|
|
12472
|
+
if (selectedOption) {
|
|
12473
|
+
const index = filteredOptions.findIndex(
|
|
12474
|
+
(opt) => opt.value === selectedOption.value
|
|
12475
|
+
);
|
|
12476
|
+
if (index !== -1 && focusedIndex === -1) {
|
|
12477
|
+
queueMicrotask(() => setFocusedIndex(index));
|
|
12478
|
+
}
|
|
12479
|
+
}
|
|
12480
|
+
}, [selectedOption, filteredOptions]);
|
|
12481
|
+
useEffect15(() => {
|
|
12482
|
+
var _a;
|
|
12483
|
+
if (focusedIndex >= 0 && optionRefs.current[focusedIndex]) {
|
|
12484
|
+
(_a = optionRefs.current[focusedIndex]) == null ? void 0 : _a.scrollIntoView({
|
|
12485
|
+
block: "nearest",
|
|
12486
|
+
behavior: "smooth"
|
|
12487
|
+
});
|
|
12488
|
+
}
|
|
12489
|
+
}, [focusedIndex]);
|
|
12490
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
12491
|
+
useEditorClickOutside(
|
|
12492
|
+
containerRef,
|
|
12493
|
+
() => {
|
|
12494
|
+
if (isOpen) {
|
|
12495
|
+
onCommit();
|
|
12496
|
+
}
|
|
12497
|
+
},
|
|
12498
|
+
true
|
|
12499
|
+
);
|
|
12500
|
+
const handleSelectOption = (option) => {
|
|
12501
|
+
if (!option.disabled) {
|
|
12502
|
+
onChange(option.value);
|
|
12503
|
+
onCommit();
|
|
12504
|
+
}
|
|
12505
|
+
};
|
|
12506
|
+
const handleClear = (e) => {
|
|
12507
|
+
e.stopPropagation();
|
|
12508
|
+
onChange(null);
|
|
12509
|
+
setSearchQuery("");
|
|
12510
|
+
setFocusedIndex(-1);
|
|
12511
|
+
};
|
|
12512
|
+
const handleArrowDown = () => {
|
|
12513
|
+
setFocusedIndex((prev) => {
|
|
12514
|
+
var _a;
|
|
12515
|
+
let next = prev + 1;
|
|
12516
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12517
|
+
next++;
|
|
12518
|
+
}
|
|
12519
|
+
return next < filteredOptions.length ? next : prev;
|
|
12520
|
+
});
|
|
12521
|
+
};
|
|
12522
|
+
const handleArrowUp = () => {
|
|
12523
|
+
setFocusedIndex((prev) => {
|
|
12524
|
+
var _a;
|
|
12525
|
+
let next = prev - 1;
|
|
12526
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12527
|
+
next--;
|
|
12528
|
+
}
|
|
12529
|
+
return next >= 0 ? next : prev;
|
|
12530
|
+
});
|
|
12531
|
+
};
|
|
12532
|
+
const handleEnter = () => {
|
|
12533
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
12534
|
+
const option = filteredOptions[focusedIndex];
|
|
12535
|
+
if (option && !option.disabled) {
|
|
12536
|
+
handleSelectOption(option);
|
|
12537
|
+
}
|
|
12538
|
+
} else {
|
|
12539
|
+
onCommit();
|
|
12540
|
+
}
|
|
12541
|
+
};
|
|
12542
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
12543
|
+
{
|
|
12544
|
+
onEnter: handleEnter,
|
|
12545
|
+
onEscape: onCancel,
|
|
12546
|
+
onArrowUp: handleArrowUp,
|
|
12547
|
+
onArrowDown: handleArrowDown
|
|
12548
|
+
},
|
|
12549
|
+
{
|
|
12550
|
+
commitOnTab: true,
|
|
12551
|
+
commitOnBlur: false
|
|
12552
|
+
}
|
|
12553
|
+
);
|
|
12554
|
+
const renderLabel = (option) => {
|
|
12555
|
+
if (renderOptionLabel) {
|
|
12556
|
+
return renderOptionLabel(option);
|
|
12557
|
+
}
|
|
12558
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ React30.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ React30.createElement("div", { className: "editor-option-text" }, /* @__PURE__ */ React30.createElement("div", { className: "editor-option-label" }, option.label), option.description && /* @__PURE__ */ React30.createElement("div", { className: "editor-option-description" }, option.description)));
|
|
12559
|
+
};
|
|
12560
|
+
return /* @__PURE__ */ React30.createElement(
|
|
12561
|
+
"div",
|
|
12562
|
+
{
|
|
12563
|
+
ref: containerRef,
|
|
12564
|
+
className: "editor-container editor-richselect-container",
|
|
12565
|
+
onKeyDown: handleKeyDown
|
|
12566
|
+
},
|
|
12567
|
+
/* @__PURE__ */ React30.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ React30.createElement(
|
|
12568
|
+
"input",
|
|
12569
|
+
{
|
|
12570
|
+
ref: inputRef,
|
|
12571
|
+
type: "text",
|
|
12572
|
+
className: "editor-input editor-richselect-input",
|
|
12573
|
+
value: filterable ? searchQuery : (selectedOption == null ? void 0 : selectedOption.label) || "",
|
|
12574
|
+
onChange: (e) => {
|
|
12575
|
+
if (filterable) {
|
|
12576
|
+
setSearchQuery(e.target.value);
|
|
12577
|
+
setFocusedIndex(-1);
|
|
12578
|
+
}
|
|
12579
|
+
},
|
|
12580
|
+
placeholder: (selectedOption == null ? void 0 : selectedOption.label) || placeholder,
|
|
12581
|
+
"aria-label": "Select option",
|
|
12582
|
+
"aria-expanded": isOpen,
|
|
12583
|
+
"aria-autocomplete": "list",
|
|
12584
|
+
"aria-controls": "richselect-dropdown",
|
|
12585
|
+
autoComplete: "off"
|
|
12586
|
+
}
|
|
12587
|
+
), /* @__PURE__ */ React30.createElement("div", { className: "editor-input-actions" }, allowClear && value !== null && value !== void 0 && /* @__PURE__ */ React30.createElement(
|
|
12588
|
+
"button",
|
|
12589
|
+
{
|
|
12590
|
+
type: "button",
|
|
12591
|
+
className: "editor-clear-btn",
|
|
12592
|
+
onClick: handleClear,
|
|
12593
|
+
"aria-label": "Clear selection",
|
|
12594
|
+
tabIndex: -1
|
|
12595
|
+
},
|
|
12596
|
+
"\xD7"
|
|
12597
|
+
), /* @__PURE__ */ React30.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC"))),
|
|
12598
|
+
isOpen && /* @__PURE__ */ React30.createElement(
|
|
12599
|
+
"div",
|
|
12600
|
+
{
|
|
12601
|
+
ref: dropdownRef,
|
|
12602
|
+
id: "richselect-dropdown",
|
|
12603
|
+
className: "editor-dropdown",
|
|
12604
|
+
role: "listbox",
|
|
12605
|
+
style: { maxHeight: maxDropdownHeight }
|
|
12606
|
+
},
|
|
12607
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ React30.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => /* @__PURE__ */ React30.createElement(
|
|
12608
|
+
"div",
|
|
12609
|
+
{
|
|
12610
|
+
key: option.value,
|
|
12611
|
+
ref: (el) => {
|
|
12612
|
+
optionRefs.current[index] = el;
|
|
12613
|
+
},
|
|
12614
|
+
className: `editor-dropdown-option ${option.value === value ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
12615
|
+
role: "option",
|
|
12616
|
+
"aria-selected": option.value === value,
|
|
12617
|
+
"aria-disabled": option.disabled,
|
|
12618
|
+
onClick: () => handleSelectOption(option),
|
|
12619
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
12620
|
+
},
|
|
12621
|
+
renderLabel(option)
|
|
12622
|
+
))
|
|
12623
|
+
)
|
|
12624
|
+
);
|
|
12625
|
+
}
|
|
12626
|
+
RichSelectEditor.displayName = "RichSelectEditor";
|
|
12627
|
+
|
|
12628
|
+
// src/editors/DateEditor.tsx
|
|
12629
|
+
import React31, { useState as useState22, useRef as useRef17, useMemo as useMemo10 } from "react";
|
|
12630
|
+
function DateEditor(props) {
|
|
12631
|
+
const {
|
|
12632
|
+
value,
|
|
12633
|
+
onChange,
|
|
12634
|
+
onCommit,
|
|
12635
|
+
onCancel,
|
|
12636
|
+
autoFocus = true,
|
|
12637
|
+
dateFormat = "yyyy-MM-dd",
|
|
12638
|
+
showTime = false,
|
|
12639
|
+
minDate,
|
|
12640
|
+
maxDate,
|
|
12641
|
+
autoCommit = false
|
|
12642
|
+
} = props;
|
|
12643
|
+
const [isOpen] = useState22(true);
|
|
12644
|
+
const [inputValue, setInputValue] = useState22("");
|
|
12645
|
+
const [viewDate, setViewDate] = useState22(/* @__PURE__ */ new Date());
|
|
12646
|
+
const containerRef = useRef17(null);
|
|
12647
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
12648
|
+
const calendarRef = useRef17(null);
|
|
12649
|
+
const parsedValue = useMemo10(() => {
|
|
12650
|
+
if (!value) return null;
|
|
12651
|
+
if (value instanceof Date) return value;
|
|
12652
|
+
const parsed = new Date(value);
|
|
12653
|
+
return isNaN(parsed.getTime()) ? null : parsed;
|
|
12654
|
+
}, [value]);
|
|
12655
|
+
React31.useEffect(() => {
|
|
12656
|
+
if (parsedValue) {
|
|
12657
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
12658
|
+
setViewDate(parsedValue);
|
|
12659
|
+
}
|
|
12660
|
+
}, [parsedValue, dateFormat, showTime]);
|
|
12661
|
+
usePopupPosition(containerRef, calendarRef, isOpen, "auto");
|
|
12662
|
+
useEditorClickOutside(containerRef, () => {
|
|
12663
|
+
if (isOpen) {
|
|
12664
|
+
onCommit();
|
|
12665
|
+
}
|
|
12666
|
+
}, true);
|
|
12667
|
+
const handleSelectDate = (date) => {
|
|
12668
|
+
const newDate = new Date(date);
|
|
12669
|
+
if (parsedValue && !showTime) {
|
|
12670
|
+
newDate.setHours(parsedValue.getHours());
|
|
12671
|
+
newDate.setMinutes(parsedValue.getMinutes());
|
|
12672
|
+
newDate.setSeconds(parsedValue.getSeconds());
|
|
12673
|
+
}
|
|
12674
|
+
onChange(newDate);
|
|
12675
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
12676
|
+
if (autoCommit && !showTime) {
|
|
12677
|
+
onCommit();
|
|
12678
|
+
}
|
|
12679
|
+
};
|
|
12680
|
+
const handleTimeChange = (hours, minutes) => {
|
|
12681
|
+
const newDate = parsedValue ? new Date(parsedValue) : /* @__PURE__ */ new Date();
|
|
12682
|
+
newDate.setHours(hours);
|
|
12683
|
+
newDate.setMinutes(minutes);
|
|
12684
|
+
onChange(newDate);
|
|
12685
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
12686
|
+
};
|
|
12687
|
+
const handleInputChange = (e) => {
|
|
12688
|
+
const newValue = e.target.value;
|
|
12689
|
+
setInputValue(newValue);
|
|
12690
|
+
const parsed = parseDate(newValue, dateFormat);
|
|
12691
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
12692
|
+
onChange(parsed);
|
|
12693
|
+
setViewDate(parsed);
|
|
12694
|
+
}
|
|
12695
|
+
};
|
|
12696
|
+
const handleInputBlur = () => {
|
|
12697
|
+
if (parsedValue) {
|
|
12698
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
12699
|
+
}
|
|
12700
|
+
};
|
|
12701
|
+
const handlePrevMonth = () => {
|
|
12702
|
+
setViewDate((prev) => {
|
|
12703
|
+
const newDate = new Date(prev);
|
|
12704
|
+
newDate.setMonth(newDate.getMonth() - 1);
|
|
12705
|
+
return newDate;
|
|
12706
|
+
});
|
|
12707
|
+
};
|
|
12708
|
+
const handleNextMonth = () => {
|
|
12709
|
+
setViewDate((prev) => {
|
|
12710
|
+
const newDate = new Date(prev);
|
|
12711
|
+
newDate.setMonth(newDate.getMonth() + 1);
|
|
12712
|
+
return newDate;
|
|
12713
|
+
});
|
|
12714
|
+
};
|
|
12715
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
12716
|
+
{
|
|
12717
|
+
onEnter: onCommit,
|
|
12718
|
+
onEscape: onCancel
|
|
12719
|
+
},
|
|
12720
|
+
{
|
|
12721
|
+
commitOnTab: true,
|
|
12722
|
+
commitOnBlur: false
|
|
12723
|
+
}
|
|
12724
|
+
);
|
|
12725
|
+
const calendarDays = useMemo10(() => {
|
|
12726
|
+
return generateCalendarDays(viewDate, parsedValue, minDate, maxDate);
|
|
12727
|
+
}, [viewDate, parsedValue, minDate, maxDate]);
|
|
12728
|
+
return /* @__PURE__ */ React31.createElement(
|
|
12729
|
+
"div",
|
|
12730
|
+
{
|
|
12731
|
+
ref: containerRef,
|
|
12732
|
+
className: "editor-container editor-date-container",
|
|
12733
|
+
onKeyDown: handleKeyDown
|
|
12734
|
+
},
|
|
12735
|
+
/* @__PURE__ */ React31.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ React31.createElement(
|
|
12736
|
+
"input",
|
|
12737
|
+
{
|
|
12738
|
+
ref: inputRef,
|
|
12739
|
+
type: "text",
|
|
12740
|
+
className: "editor-input editor-date-input",
|
|
12741
|
+
value: inputValue,
|
|
12742
|
+
onChange: handleInputChange,
|
|
12743
|
+
onBlur: handleInputBlur,
|
|
12744
|
+
placeholder: dateFormat,
|
|
12745
|
+
"aria-label": "Date input",
|
|
12746
|
+
autoComplete: "off"
|
|
12747
|
+
}
|
|
12748
|
+
), /* @__PURE__ */ React31.createElement("span", { className: "editor-calendar-icon", "aria-hidden": "true" }, "\u{1F4C5}")),
|
|
12749
|
+
isOpen && /* @__PURE__ */ React31.createElement("div", { ref: calendarRef, className: "editor-dropdown editor-calendar" }, /* @__PURE__ */ React31.createElement("div", { className: "editor-calendar-header" }, /* @__PURE__ */ React31.createElement(
|
|
12750
|
+
"button",
|
|
12751
|
+
{
|
|
12752
|
+
type: "button",
|
|
12753
|
+
className: "editor-calendar-nav",
|
|
12754
|
+
onClick: handlePrevMonth,
|
|
12755
|
+
"aria-label": "Previous month"
|
|
12756
|
+
},
|
|
12757
|
+
"\u2039"
|
|
12758
|
+
), /* @__PURE__ */ React31.createElement("div", { className: "editor-calendar-title" }, viewDate.toLocaleDateString("en-US", {
|
|
12759
|
+
month: "long",
|
|
12760
|
+
year: "numeric"
|
|
12761
|
+
})), /* @__PURE__ */ React31.createElement(
|
|
12762
|
+
"button",
|
|
12763
|
+
{
|
|
12764
|
+
type: "button",
|
|
12765
|
+
className: "editor-calendar-nav",
|
|
12766
|
+
onClick: handleNextMonth,
|
|
12767
|
+
"aria-label": "Next month"
|
|
12768
|
+
},
|
|
12769
|
+
"\u203A"
|
|
12770
|
+
)), /* @__PURE__ */ React31.createElement("div", { className: "editor-calendar-weekdays" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React31.createElement("div", { key: day, className: "editor-calendar-weekday" }, day))), /* @__PURE__ */ React31.createElement("div", { className: "editor-calendar-days" }, calendarDays.map((day, index) => /* @__PURE__ */ React31.createElement(
|
|
12771
|
+
"button",
|
|
12772
|
+
{
|
|
12773
|
+
key: index,
|
|
12774
|
+
type: "button",
|
|
12775
|
+
className: `editor-calendar-day ${day.className}`,
|
|
12776
|
+
onClick: () => day.date && handleSelectDate(day.date),
|
|
12777
|
+
disabled: day.disabled,
|
|
12778
|
+
"aria-label": day.date ? day.date.toDateString() : ""
|
|
12779
|
+
},
|
|
12780
|
+
day.label
|
|
12781
|
+
))), showTime && /* @__PURE__ */ React31.createElement("div", { className: "editor-calendar-time" }, /* @__PURE__ */ React31.createElement(
|
|
12782
|
+
"input",
|
|
12783
|
+
{
|
|
12784
|
+
type: "time",
|
|
12785
|
+
className: "editor-time-input",
|
|
12786
|
+
value: parsedValue ? `${String(parsedValue.getHours()).padStart(2, "0")}:${String(
|
|
12787
|
+
parsedValue.getMinutes()
|
|
12788
|
+
).padStart(2, "0")}` : "00:00",
|
|
12789
|
+
onChange: (e) => {
|
|
12790
|
+
const [hours, minutes] = e.target.value.split(":").map(Number);
|
|
12791
|
+
handleTimeChange(hours, minutes);
|
|
12792
|
+
},
|
|
12793
|
+
"aria-label": "Time input"
|
|
12794
|
+
}
|
|
12795
|
+
)))
|
|
12796
|
+
);
|
|
12797
|
+
}
|
|
12798
|
+
DateEditor.displayName = "DateEditor";
|
|
12799
|
+
function formatDate(date, format, showTime) {
|
|
12800
|
+
const year = date.getFullYear();
|
|
12801
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
12802
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
12803
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
12804
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
12805
|
+
let formatted = format.replace("yyyy", String(year)).replace("MM", month).replace("dd", day);
|
|
12806
|
+
if (showTime) {
|
|
12807
|
+
formatted += ` ${hours}:${minutes}`;
|
|
12808
|
+
}
|
|
12809
|
+
return formatted;
|
|
12810
|
+
}
|
|
12811
|
+
function parseDate(value, format) {
|
|
12812
|
+
if (!value) return null;
|
|
12813
|
+
try {
|
|
12814
|
+
const parts = value.split(/[-/\s:]/);
|
|
12815
|
+
if (parts.length < 3) return null;
|
|
12816
|
+
let year, month, day;
|
|
12817
|
+
let hours = 0, minutes = 0;
|
|
12818
|
+
if (format.startsWith("yyyy")) {
|
|
12819
|
+
year = parseInt(parts[0], 10);
|
|
12820
|
+
month = parseInt(parts[1], 10) - 1;
|
|
12821
|
+
day = parseInt(parts[2], 10);
|
|
12822
|
+
} else if (format.startsWith("MM")) {
|
|
12823
|
+
month = parseInt(parts[0], 10) - 1;
|
|
12824
|
+
day = parseInt(parts[1], 10);
|
|
12825
|
+
year = parseInt(parts[2], 10);
|
|
12826
|
+
} else {
|
|
12827
|
+
day = parseInt(parts[0], 10);
|
|
12828
|
+
month = parseInt(parts[1], 10) - 1;
|
|
12829
|
+
year = parseInt(parts[2], 10);
|
|
12830
|
+
}
|
|
12831
|
+
if (parts.length >= 5) {
|
|
12832
|
+
hours = parseInt(parts[3], 10);
|
|
12833
|
+
minutes = parseInt(parts[4], 10);
|
|
12834
|
+
}
|
|
12835
|
+
const date = new Date(year, month, day, hours, minutes);
|
|
12836
|
+
return isNaN(date.getTime()) ? null : date;
|
|
12837
|
+
} catch {
|
|
12838
|
+
return null;
|
|
12839
|
+
}
|
|
12840
|
+
}
|
|
12841
|
+
function generateCalendarDays(viewDate, selectedDate, minDate, maxDate) {
|
|
12842
|
+
const year = viewDate.getFullYear();
|
|
12843
|
+
const month = viewDate.getMonth();
|
|
12844
|
+
const firstDay = new Date(year, month, 1);
|
|
12845
|
+
const lastDay = new Date(year, month + 1, 0);
|
|
12846
|
+
const daysInMonth = lastDay.getDate();
|
|
12847
|
+
const startingDayOfWeek = firstDay.getDay();
|
|
12848
|
+
const days = [];
|
|
12849
|
+
const prevMonth = new Date(year, month, 0);
|
|
12850
|
+
const prevMonthDays = prevMonth.getDate();
|
|
12851
|
+
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
|
12852
|
+
const date = new Date(year, month - 1, prevMonthDays - i);
|
|
12853
|
+
days.push({
|
|
12854
|
+
date,
|
|
12855
|
+
label: String(prevMonthDays - i),
|
|
12856
|
+
className: "other-month",
|
|
12857
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
12858
|
+
});
|
|
12859
|
+
}
|
|
12860
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
12861
|
+
const date = new Date(year, month, i);
|
|
12862
|
+
const isSelected = selectedDate && isSameDay(date, selectedDate);
|
|
12863
|
+
const isToday = isSameDay(date, /* @__PURE__ */ new Date());
|
|
12864
|
+
const disabled = isDateDisabled(date, minDate, maxDate);
|
|
12865
|
+
days.push({
|
|
12866
|
+
date,
|
|
12867
|
+
label: String(i),
|
|
12868
|
+
className: `${isSelected ? "selected" : ""} ${isToday ? "today" : ""} ${disabled ? "disabled" : ""}`.trim(),
|
|
12869
|
+
disabled
|
|
12870
|
+
});
|
|
12871
|
+
}
|
|
12872
|
+
const remainingDays = 42 - days.length;
|
|
12873
|
+
for (let i = 1; i <= remainingDays; i++) {
|
|
12874
|
+
const date = new Date(year, month + 1, i);
|
|
12875
|
+
days.push({
|
|
12876
|
+
date,
|
|
12877
|
+
label: String(i),
|
|
12878
|
+
className: "other-month",
|
|
12879
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
12880
|
+
});
|
|
12881
|
+
}
|
|
12882
|
+
return days;
|
|
12883
|
+
}
|
|
12884
|
+
function isSameDay(date1, date2) {
|
|
12885
|
+
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
|
|
12886
|
+
}
|
|
12887
|
+
function isDateDisabled(date, minDate, maxDate) {
|
|
12888
|
+
if (minDate && date < minDate) return true;
|
|
12889
|
+
if (maxDate && date > maxDate) return true;
|
|
12890
|
+
return false;
|
|
12891
|
+
}
|
|
12892
|
+
|
|
12893
|
+
// src/editors/NumericEditor.tsx
|
|
12894
|
+
import React32, { useState as useState23, useEffect as useEffect16 } from "react";
|
|
12895
|
+
function NumericEditor(props) {
|
|
12896
|
+
const {
|
|
12897
|
+
value,
|
|
12898
|
+
onChange,
|
|
12899
|
+
onCommit,
|
|
12900
|
+
onCancel,
|
|
12901
|
+
autoFocus = true,
|
|
12902
|
+
min,
|
|
12903
|
+
max,
|
|
12904
|
+
step = 1,
|
|
12905
|
+
decimals = 0,
|
|
12906
|
+
prefix = "",
|
|
12907
|
+
suffix = "",
|
|
12908
|
+
allowNegative = true,
|
|
12909
|
+
showSteppers = true,
|
|
12910
|
+
thousandsSeparator = ",",
|
|
12911
|
+
decimalSeparator = "."
|
|
12912
|
+
} = props;
|
|
12913
|
+
const [inputValue, setInputValue] = useState23("");
|
|
12914
|
+
const [isFocused, setIsFocused] = useState23(true);
|
|
12915
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
12916
|
+
useEffect16(() => {
|
|
12917
|
+
if (value !== null && value !== void 0) {
|
|
12918
|
+
if (isFocused) {
|
|
12919
|
+
queueMicrotask(() => setInputValue(String(value)));
|
|
12920
|
+
} else {
|
|
12921
|
+
queueMicrotask(() => setInputValue(formatNumber(value, decimals, thousandsSeparator, decimalSeparator)));
|
|
12922
|
+
}
|
|
12923
|
+
} else {
|
|
12924
|
+
queueMicrotask(() => setInputValue(""));
|
|
12925
|
+
}
|
|
12926
|
+
}, [value, isFocused, decimals, thousandsSeparator, decimalSeparator]);
|
|
12927
|
+
const clampValue = (val) => {
|
|
12928
|
+
let clamped = val;
|
|
12929
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
12930
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
12931
|
+
return clamped;
|
|
12932
|
+
};
|
|
12933
|
+
const handleInputChange = (e) => {
|
|
12934
|
+
const newValue = e.target.value;
|
|
12935
|
+
setInputValue(newValue);
|
|
12936
|
+
if (newValue === "" || newValue === "-") {
|
|
12937
|
+
onChange(null);
|
|
12938
|
+
return;
|
|
12939
|
+
}
|
|
12940
|
+
let cleaned = newValue;
|
|
12941
|
+
if (prefix) cleaned = cleaned.replace(prefix, "");
|
|
12942
|
+
if (suffix) cleaned = cleaned.replace(suffix, "");
|
|
12943
|
+
const parsed = parseFormattedNumber(cleaned, thousandsSeparator, decimalSeparator);
|
|
12944
|
+
if (parsed !== null && !isNaN(parsed)) {
|
|
12945
|
+
if (!allowNegative && parsed < 0) {
|
|
12946
|
+
return;
|
|
12947
|
+
}
|
|
12948
|
+
const clamped = clampValue(parsed);
|
|
12949
|
+
onChange(clamped);
|
|
12950
|
+
}
|
|
12951
|
+
};
|
|
12952
|
+
const handleIncrement = () => {
|
|
12953
|
+
const currentValue = value ?? 0;
|
|
12954
|
+
const newValue = clampValue(currentValue + step);
|
|
12955
|
+
onChange(newValue);
|
|
12956
|
+
setInputValue(String(newValue));
|
|
12957
|
+
};
|
|
12958
|
+
const handleDecrement = () => {
|
|
12959
|
+
const currentValue = value ?? 0;
|
|
12960
|
+
const newValue = clampValue(currentValue - step);
|
|
12961
|
+
onChange(newValue);
|
|
12962
|
+
setInputValue(String(newValue));
|
|
12963
|
+
};
|
|
12964
|
+
const handleFocus = () => {
|
|
12965
|
+
setIsFocused(true);
|
|
12966
|
+
if (value !== null && value !== void 0) {
|
|
12967
|
+
setInputValue(String(value));
|
|
12968
|
+
}
|
|
12969
|
+
};
|
|
12970
|
+
const handleBlur = () => {
|
|
12971
|
+
setIsFocused(false);
|
|
12972
|
+
if (value !== null && value !== void 0) {
|
|
12973
|
+
const formatted = formatNumber(value, decimals, thousandsSeparator, decimalSeparator);
|
|
12974
|
+
setInputValue(formatted);
|
|
12975
|
+
}
|
|
12976
|
+
setTimeout(() => onCommit(), 100);
|
|
12977
|
+
};
|
|
12978
|
+
const handleKeyDown = (e) => {
|
|
12979
|
+
if (e.key === "ArrowUp") {
|
|
12980
|
+
e.preventDefault();
|
|
12981
|
+
handleIncrement();
|
|
12982
|
+
} else if (e.key === "ArrowDown") {
|
|
12983
|
+
e.preventDefault();
|
|
12984
|
+
handleDecrement();
|
|
12985
|
+
}
|
|
12986
|
+
};
|
|
12987
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
12988
|
+
{
|
|
12989
|
+
onEnter: onCommit,
|
|
12990
|
+
onEscape: onCancel
|
|
12991
|
+
},
|
|
12992
|
+
{
|
|
12993
|
+
commitOnTab: true,
|
|
12994
|
+
commitOnBlur: false,
|
|
12995
|
+
preventDefault: false
|
|
12996
|
+
// Let handleKeyDown handle arrow keys
|
|
12997
|
+
}
|
|
12998
|
+
);
|
|
12999
|
+
const handleCombinedKeyDown = (e) => {
|
|
13000
|
+
handleKeyDown(e);
|
|
13001
|
+
handleEditorKeyDown(e);
|
|
13002
|
+
};
|
|
13003
|
+
const displayValue = isFocused ? inputValue : inputValue ? `${prefix}${inputValue}${suffix}` : "";
|
|
13004
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "editor-container editor-numeric-container" }, /* @__PURE__ */ React32.createElement("div", { className: "editor-input-wrapper" }, prefix && !isFocused && /* @__PURE__ */ React32.createElement("span", { className: "editor-numeric-prefix" }, prefix), /* @__PURE__ */ React32.createElement(
|
|
13005
|
+
"input",
|
|
13006
|
+
{
|
|
13007
|
+
ref: inputRef,
|
|
13008
|
+
type: "text",
|
|
13009
|
+
inputMode: "decimal",
|
|
13010
|
+
className: "editor-input editor-numeric-input",
|
|
13011
|
+
value: displayValue,
|
|
13012
|
+
onChange: handleInputChange,
|
|
13013
|
+
onFocus: handleFocus,
|
|
13014
|
+
onBlur: handleBlur,
|
|
13015
|
+
onKeyDown: handleCombinedKeyDown,
|
|
13016
|
+
"aria-label": "Numeric input",
|
|
13017
|
+
"aria-valuemin": min,
|
|
13018
|
+
"aria-valuemax": max,
|
|
13019
|
+
"aria-valuenow": value ?? void 0,
|
|
13020
|
+
autoComplete: "off"
|
|
13021
|
+
}
|
|
13022
|
+
), suffix && !isFocused && /* @__PURE__ */ React32.createElement("span", { className: "editor-numeric-suffix" }, suffix), showSteppers && /* @__PURE__ */ React32.createElement("div", { className: "editor-numeric-steppers" }, /* @__PURE__ */ React32.createElement(
|
|
13023
|
+
"button",
|
|
13024
|
+
{
|
|
13025
|
+
type: "button",
|
|
13026
|
+
className: "editor-numeric-stepper editor-numeric-increment",
|
|
13027
|
+
onClick: handleIncrement,
|
|
13028
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13029
|
+
disabled: max !== void 0 && (value ?? 0) >= max,
|
|
13030
|
+
"aria-label": "Increment",
|
|
13031
|
+
tabIndex: -1
|
|
13032
|
+
},
|
|
13033
|
+
"\u25B2"
|
|
13034
|
+
), /* @__PURE__ */ React32.createElement(
|
|
13035
|
+
"button",
|
|
13036
|
+
{
|
|
13037
|
+
type: "button",
|
|
13038
|
+
className: "editor-numeric-stepper editor-numeric-decrement",
|
|
13039
|
+
onClick: handleDecrement,
|
|
13040
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13041
|
+
disabled: min !== void 0 && (value ?? 0) <= min,
|
|
13042
|
+
"aria-label": "Decrement",
|
|
13043
|
+
tabIndex: -1
|
|
13044
|
+
},
|
|
13045
|
+
"\u25BC"
|
|
13046
|
+
))), (min !== void 0 || max !== void 0) && /* @__PURE__ */ React32.createElement("div", { className: "editor-numeric-range" }, min !== void 0 && /* @__PURE__ */ React32.createElement("span", null, "Min: ", min), max !== void 0 && /* @__PURE__ */ React32.createElement("span", null, "Max: ", max)));
|
|
13047
|
+
}
|
|
13048
|
+
NumericEditor.displayName = "NumericEditor";
|
|
13049
|
+
|
|
13050
|
+
// src/editors/MultiSelectEditor.tsx
|
|
13051
|
+
import React33, { useState as useState24, useRef as useRef18, useMemo as useMemo11 } from "react";
|
|
13052
|
+
function MultiSelectEditor(props) {
|
|
13053
|
+
const {
|
|
13054
|
+
value = [],
|
|
13055
|
+
onChange,
|
|
13056
|
+
onCommit,
|
|
13057
|
+
onCancel,
|
|
13058
|
+
autoFocus = true,
|
|
13059
|
+
options,
|
|
13060
|
+
placeholder = "Select...",
|
|
13061
|
+
maxTagCount = 3,
|
|
13062
|
+
filterable = true,
|
|
13063
|
+
maxDropdownHeight = 300,
|
|
13064
|
+
allowEmpty = true
|
|
13065
|
+
} = props;
|
|
13066
|
+
const [isOpen] = useState24(true);
|
|
13067
|
+
const [searchQuery, setSearchQuery] = useState24("");
|
|
13068
|
+
const [focusedIndex, setFocusedIndex] = useState24(-1);
|
|
13069
|
+
const containerRef = useRef18(null);
|
|
13070
|
+
const inputRef = useEditorAutoFocus(autoFocus);
|
|
13071
|
+
const dropdownRef = useRef18(null);
|
|
13072
|
+
const tagContainerRef = useRef18(null);
|
|
13073
|
+
const selectedValues = useMemo11(
|
|
13074
|
+
() => Array.isArray(value) ? value : [],
|
|
13075
|
+
[value]
|
|
13076
|
+
);
|
|
13077
|
+
const filteredOptions = useMemo11(
|
|
13078
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
13079
|
+
[options, searchQuery, filterable]
|
|
13080
|
+
);
|
|
13081
|
+
const selectedOptions = useMemo11(
|
|
13082
|
+
() => options.filter((opt) => selectedValues.includes(opt.value)),
|
|
13083
|
+
[options, selectedValues]
|
|
13084
|
+
);
|
|
13085
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
13086
|
+
useEditorClickOutside(
|
|
13087
|
+
containerRef,
|
|
13088
|
+
() => {
|
|
13089
|
+
if (isOpen) {
|
|
13090
|
+
onCommit();
|
|
13091
|
+
}
|
|
13092
|
+
},
|
|
13093
|
+
true
|
|
13094
|
+
);
|
|
13095
|
+
const handleToggleOption = (option) => {
|
|
13096
|
+
if (option.disabled) return;
|
|
13097
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13098
|
+
let newValues;
|
|
13099
|
+
if (isSelected) {
|
|
13100
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13101
|
+
return;
|
|
13102
|
+
}
|
|
13103
|
+
newValues = selectedValues.filter((v) => v !== option.value);
|
|
13104
|
+
} else {
|
|
13105
|
+
newValues = [...selectedValues, option.value];
|
|
13106
|
+
}
|
|
13107
|
+
onChange(newValues);
|
|
13108
|
+
};
|
|
13109
|
+
const handleRemoveTag = (optionValue, e) => {
|
|
13110
|
+
e.stopPropagation();
|
|
13111
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13112
|
+
return;
|
|
13113
|
+
}
|
|
13114
|
+
const newValues = selectedValues.filter((v) => v !== optionValue);
|
|
13115
|
+
onChange(newValues);
|
|
13116
|
+
};
|
|
13117
|
+
const handleKeyDown = (e) => {
|
|
13118
|
+
if (e.key === "Backspace" && searchQuery === "" && selectedValues.length > 0) {
|
|
13119
|
+
e.preventDefault();
|
|
13120
|
+
if (allowEmpty || selectedValues.length > 1) {
|
|
13121
|
+
const newValues = selectedValues.slice(0, -1);
|
|
13122
|
+
onChange(newValues);
|
|
13123
|
+
}
|
|
13124
|
+
}
|
|
13125
|
+
};
|
|
13126
|
+
const handleArrowDown = () => {
|
|
13127
|
+
setFocusedIndex((prev) => {
|
|
13128
|
+
var _a;
|
|
13129
|
+
let next = prev + 1;
|
|
13130
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13131
|
+
next++;
|
|
13132
|
+
}
|
|
13133
|
+
return next < filteredOptions.length ? next : prev;
|
|
13134
|
+
});
|
|
13135
|
+
};
|
|
13136
|
+
const handleArrowUp = () => {
|
|
13137
|
+
setFocusedIndex((prev) => {
|
|
13138
|
+
var _a;
|
|
13139
|
+
let next = prev - 1;
|
|
13140
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13141
|
+
next--;
|
|
13142
|
+
}
|
|
13143
|
+
return next >= 0 ? next : prev;
|
|
13144
|
+
});
|
|
13145
|
+
};
|
|
13146
|
+
const handleEnter = () => {
|
|
13147
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
13148
|
+
const option = filteredOptions[focusedIndex];
|
|
13149
|
+
if (option && !option.disabled) {
|
|
13150
|
+
handleToggleOption(option);
|
|
13151
|
+
}
|
|
13152
|
+
} else {
|
|
13153
|
+
onCommit();
|
|
13154
|
+
}
|
|
13155
|
+
};
|
|
13156
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
13157
|
+
{
|
|
13158
|
+
onEnter: handleEnter,
|
|
13159
|
+
onEscape: onCancel,
|
|
13160
|
+
onArrowUp: handleArrowUp,
|
|
13161
|
+
onArrowDown: handleArrowDown
|
|
13162
|
+
},
|
|
13163
|
+
{
|
|
13164
|
+
commitOnTab: true,
|
|
13165
|
+
commitOnBlur: false
|
|
13166
|
+
}
|
|
13167
|
+
);
|
|
13168
|
+
const handleCombinedKeyDown = (e) => {
|
|
13169
|
+
handleKeyDown(e);
|
|
13170
|
+
handleEditorKeyDown(e);
|
|
13171
|
+
};
|
|
13172
|
+
const visibleTags = selectedOptions.slice(0, maxTagCount);
|
|
13173
|
+
const collapsedCount = Math.max(0, selectedOptions.length - maxTagCount);
|
|
13174
|
+
return /* @__PURE__ */ React33.createElement(
|
|
13175
|
+
"div",
|
|
13176
|
+
{
|
|
13177
|
+
ref: containerRef,
|
|
13178
|
+
className: "editor-container editor-multiselect-container",
|
|
13179
|
+
onKeyDown: handleCombinedKeyDown
|
|
13180
|
+
},
|
|
13181
|
+
/* @__PURE__ */ React33.createElement(
|
|
13182
|
+
"div",
|
|
13183
|
+
{
|
|
13184
|
+
ref: tagContainerRef,
|
|
13185
|
+
className: "editor-input-wrapper editor-multiselect-wrapper"
|
|
13186
|
+
},
|
|
13187
|
+
/* @__PURE__ */ React33.createElement("div", { className: "editor-multiselect-tags" }, visibleTags.map((option) => /* @__PURE__ */ React33.createElement("div", { key: option.value, className: "editor-tag" }, option.icon && /* @__PURE__ */ React33.createElement("span", { className: "editor-tag-icon" }, option.icon), /* @__PURE__ */ React33.createElement("span", { className: "editor-tag-label" }, option.label), /* @__PURE__ */ React33.createElement(
|
|
13188
|
+
"button",
|
|
13189
|
+
{
|
|
13190
|
+
type: "button",
|
|
13191
|
+
className: "editor-tag-remove",
|
|
13192
|
+
onClick: (e) => handleRemoveTag(option.value, e),
|
|
13193
|
+
"aria-label": `Remove ${option.label}`,
|
|
13194
|
+
tabIndex: -1
|
|
13195
|
+
},
|
|
13196
|
+
"\xD7"
|
|
13197
|
+
))), collapsedCount > 0 && /* @__PURE__ */ React33.createElement("div", { className: "editor-tag editor-tag-collapsed" }, "+", collapsedCount)),
|
|
13198
|
+
/* @__PURE__ */ React33.createElement(
|
|
13199
|
+
"input",
|
|
13200
|
+
{
|
|
13201
|
+
ref: inputRef,
|
|
13202
|
+
type: "text",
|
|
13203
|
+
className: "editor-input editor-multiselect-input",
|
|
13204
|
+
value: searchQuery,
|
|
13205
|
+
onChange: (e) => {
|
|
13206
|
+
setSearchQuery(e.target.value);
|
|
13207
|
+
setFocusedIndex(-1);
|
|
13208
|
+
},
|
|
13209
|
+
placeholder: selectedValues.length === 0 ? placeholder : "",
|
|
13210
|
+
"aria-label": "Search options",
|
|
13211
|
+
"aria-expanded": isOpen,
|
|
13212
|
+
"aria-autocomplete": "list",
|
|
13213
|
+
"aria-controls": "multiselect-dropdown",
|
|
13214
|
+
autoComplete: "off"
|
|
13215
|
+
}
|
|
13216
|
+
),
|
|
13217
|
+
/* @__PURE__ */ React33.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC")
|
|
13218
|
+
),
|
|
13219
|
+
isOpen && /* @__PURE__ */ React33.createElement(
|
|
13220
|
+
"div",
|
|
13221
|
+
{
|
|
13222
|
+
ref: dropdownRef,
|
|
13223
|
+
id: "multiselect-dropdown",
|
|
13224
|
+
className: "editor-dropdown",
|
|
13225
|
+
role: "listbox",
|
|
13226
|
+
"aria-multiselectable": "true",
|
|
13227
|
+
style: { maxHeight: maxDropdownHeight }
|
|
13228
|
+
},
|
|
13229
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ React33.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => {
|
|
13230
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13231
|
+
return /* @__PURE__ */ React33.createElement(
|
|
13232
|
+
"div",
|
|
13233
|
+
{
|
|
13234
|
+
key: option.value,
|
|
13235
|
+
className: `editor-dropdown-option editor-multiselect-option ${isSelected ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
13236
|
+
role: "option",
|
|
13237
|
+
"aria-selected": isSelected,
|
|
13238
|
+
"aria-disabled": option.disabled,
|
|
13239
|
+
onClick: () => handleToggleOption(option),
|
|
13240
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
13241
|
+
},
|
|
13242
|
+
/* @__PURE__ */ React33.createElement(
|
|
13243
|
+
"input",
|
|
13244
|
+
{
|
|
13245
|
+
type: "checkbox",
|
|
13246
|
+
className: "editor-multiselect-checkbox",
|
|
13247
|
+
checked: isSelected,
|
|
13248
|
+
onChange: () => {
|
|
13249
|
+
},
|
|
13250
|
+
disabled: option.disabled,
|
|
13251
|
+
tabIndex: -1,
|
|
13252
|
+
"aria-hidden": "true"
|
|
13253
|
+
}
|
|
13254
|
+
),
|
|
13255
|
+
/* @__PURE__ */ React33.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ React33.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ React33.createElement("span", { className: "editor-option-label" }, option.label))
|
|
13256
|
+
);
|
|
13257
|
+
})
|
|
13258
|
+
)
|
|
13259
|
+
);
|
|
13260
|
+
}
|
|
13261
|
+
MultiSelectEditor.displayName = "MultiSelectEditor";
|
|
13262
|
+
|
|
13263
|
+
// src/editors/MarkdownEditor.tsx
|
|
13264
|
+
import React34, { useState as useState25, useRef as useRef19, useMemo as useMemo12 } from "react";
|
|
13265
|
+
function MarkdownEditor(props) {
|
|
13266
|
+
const {
|
|
13267
|
+
value = "",
|
|
13268
|
+
onChange,
|
|
13269
|
+
onCommit,
|
|
13270
|
+
onCancel,
|
|
13271
|
+
autoFocus = true,
|
|
13272
|
+
maxLength,
|
|
13273
|
+
showPreview = true,
|
|
13274
|
+
minHeight = 150,
|
|
13275
|
+
maxHeight = 400,
|
|
13276
|
+
rows = 6
|
|
13277
|
+
} = props;
|
|
13278
|
+
const [internalValue, setInternalValue] = useState25(value || "");
|
|
13279
|
+
const [showPreviewPanel, setShowPreviewPanel] = useState25(showPreview);
|
|
13280
|
+
const containerRef = useRef19(null);
|
|
13281
|
+
const textareaRef = useEditorAutoFocus(autoFocus, true);
|
|
13282
|
+
useEditorClickOutside(
|
|
13283
|
+
containerRef,
|
|
13284
|
+
() => {
|
|
13285
|
+
onCommit();
|
|
13286
|
+
},
|
|
13287
|
+
true
|
|
13288
|
+
);
|
|
13289
|
+
const handleChange = (e) => {
|
|
13290
|
+
const newValue = e.target.value;
|
|
13291
|
+
if (maxLength && newValue.length > maxLength) {
|
|
13292
|
+
return;
|
|
13293
|
+
}
|
|
13294
|
+
setInternalValue(newValue);
|
|
13295
|
+
onChange(newValue);
|
|
13296
|
+
};
|
|
13297
|
+
const handleKeyDown = (e) => {
|
|
13298
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
13299
|
+
e.preventDefault();
|
|
13300
|
+
onCommit();
|
|
13301
|
+
return;
|
|
13302
|
+
}
|
|
13303
|
+
if (e.key === "Escape") {
|
|
13304
|
+
e.preventDefault();
|
|
13305
|
+
onCancel();
|
|
13306
|
+
return;
|
|
13307
|
+
}
|
|
13308
|
+
if (e.key === "Tab") {
|
|
13309
|
+
e.preventDefault();
|
|
13310
|
+
const start = e.currentTarget.selectionStart;
|
|
13311
|
+
const end = e.currentTarget.selectionEnd;
|
|
13312
|
+
const newValue = internalValue.substring(0, start) + " " + internalValue.substring(end);
|
|
13313
|
+
setInternalValue(newValue);
|
|
13314
|
+
onChange(newValue);
|
|
13315
|
+
setTimeout(() => {
|
|
13316
|
+
if (textareaRef.current) {
|
|
13317
|
+
textareaRef.current.selectionStart = start + 1;
|
|
13318
|
+
textareaRef.current.selectionEnd = start + 1;
|
|
13319
|
+
}
|
|
13320
|
+
}, 0);
|
|
13321
|
+
return;
|
|
13322
|
+
}
|
|
13323
|
+
if (e.ctrlKey || e.metaKey) {
|
|
13324
|
+
let insertion = "";
|
|
13325
|
+
let cursorOffset = 0;
|
|
13326
|
+
switch (e.key) {
|
|
13327
|
+
case "b":
|
|
13328
|
+
insertion = "****";
|
|
13329
|
+
cursorOffset = 2;
|
|
13330
|
+
break;
|
|
13331
|
+
case "i":
|
|
13332
|
+
insertion = "__";
|
|
13333
|
+
cursorOffset = 1;
|
|
13334
|
+
break;
|
|
13335
|
+
case "k":
|
|
13336
|
+
insertion = "[](url)";
|
|
13337
|
+
cursorOffset = 1;
|
|
13338
|
+
break;
|
|
13339
|
+
default:
|
|
13340
|
+
return;
|
|
13341
|
+
}
|
|
13342
|
+
if (insertion) {
|
|
13343
|
+
e.preventDefault();
|
|
13344
|
+
const start = e.currentTarget.selectionStart;
|
|
13345
|
+
const end = e.currentTarget.selectionEnd;
|
|
13346
|
+
const selectedText = internalValue.substring(start, end);
|
|
13347
|
+
let newValue;
|
|
13348
|
+
let newCursorPos;
|
|
13349
|
+
if (selectedText) {
|
|
13350
|
+
const before = insertion.substring(0, cursorOffset);
|
|
13351
|
+
const after = insertion.substring(cursorOffset);
|
|
13352
|
+
newValue = internalValue.substring(0, start) + before + selectedText + after + internalValue.substring(end);
|
|
13353
|
+
newCursorPos = start + before.length + selectedText.length;
|
|
13354
|
+
} else {
|
|
13355
|
+
newValue = internalValue.substring(0, start) + insertion + internalValue.substring(end);
|
|
13356
|
+
newCursorPos = start + cursorOffset;
|
|
13357
|
+
}
|
|
13358
|
+
setInternalValue(newValue);
|
|
13359
|
+
onChange(newValue);
|
|
13360
|
+
setTimeout(() => {
|
|
13361
|
+
if (textareaRef.current) {
|
|
13362
|
+
textareaRef.current.selectionStart = newCursorPos;
|
|
13363
|
+
textareaRef.current.selectionEnd = newCursorPos;
|
|
13364
|
+
textareaRef.current.focus();
|
|
13365
|
+
}
|
|
13366
|
+
}, 0);
|
|
13367
|
+
}
|
|
13368
|
+
}
|
|
13369
|
+
};
|
|
13370
|
+
const charCount = internalValue.length;
|
|
13371
|
+
const charCountClass = maxLength && charCount > maxLength * 0.9 ? "warning" : "";
|
|
13372
|
+
const previewHtml = useMemo12(() => {
|
|
13373
|
+
return renderMarkdownPreview(internalValue);
|
|
13374
|
+
}, [internalValue]);
|
|
13375
|
+
return /* @__PURE__ */ React34.createElement(
|
|
13376
|
+
"div",
|
|
13377
|
+
{
|
|
13378
|
+
ref: containerRef,
|
|
13379
|
+
className: "editor-container editor-markdown-container",
|
|
13380
|
+
style: { minHeight }
|
|
13381
|
+
},
|
|
13382
|
+
/* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-toolbar" }, /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ React34.createElement(
|
|
13383
|
+
"button",
|
|
13384
|
+
{
|
|
13385
|
+
type: "button",
|
|
13386
|
+
className: "editor-toolbar-btn",
|
|
13387
|
+
onClick: () => {
|
|
13388
|
+
if (textareaRef.current) {
|
|
13389
|
+
const start = textareaRef.current.selectionStart;
|
|
13390
|
+
const end = textareaRef.current.selectionEnd;
|
|
13391
|
+
const selectedText = internalValue.substring(start, end);
|
|
13392
|
+
const newValue = internalValue.substring(0, start) + `**${selectedText || "bold"}**` + internalValue.substring(end);
|
|
13393
|
+
setInternalValue(newValue);
|
|
13394
|
+
onChange(newValue);
|
|
13395
|
+
textareaRef.current.focus();
|
|
13396
|
+
}
|
|
13397
|
+
},
|
|
13398
|
+
title: "Bold (Ctrl+B)",
|
|
13399
|
+
"aria-label": "Bold"
|
|
13400
|
+
},
|
|
13401
|
+
/* @__PURE__ */ React34.createElement("strong", null, "B")
|
|
13402
|
+
), /* @__PURE__ */ React34.createElement(
|
|
13403
|
+
"button",
|
|
13404
|
+
{
|
|
13405
|
+
type: "button",
|
|
13406
|
+
className: "editor-toolbar-btn",
|
|
13407
|
+
onClick: () => {
|
|
13408
|
+
if (textareaRef.current) {
|
|
13409
|
+
const start = textareaRef.current.selectionStart;
|
|
13410
|
+
const end = textareaRef.current.selectionEnd;
|
|
13411
|
+
const selectedText = internalValue.substring(start, end);
|
|
13412
|
+
const newValue = internalValue.substring(0, start) + `_${selectedText || "italic"}_` + internalValue.substring(end);
|
|
13413
|
+
setInternalValue(newValue);
|
|
13414
|
+
onChange(newValue);
|
|
13415
|
+
textareaRef.current.focus();
|
|
13416
|
+
}
|
|
13417
|
+
},
|
|
13418
|
+
title: "Italic (Ctrl+I)",
|
|
13419
|
+
"aria-label": "Italic"
|
|
13420
|
+
},
|
|
13421
|
+
/* @__PURE__ */ React34.createElement("em", null, "I")
|
|
13422
|
+
), /* @__PURE__ */ React34.createElement(
|
|
13423
|
+
"button",
|
|
13424
|
+
{
|
|
13425
|
+
type: "button",
|
|
13426
|
+
className: "editor-toolbar-btn",
|
|
13427
|
+
onClick: () => {
|
|
13428
|
+
if (textareaRef.current) {
|
|
13429
|
+
const start = textareaRef.current.selectionStart;
|
|
13430
|
+
const end = textareaRef.current.selectionEnd;
|
|
13431
|
+
const selectedText = internalValue.substring(start, end);
|
|
13432
|
+
const newValue = internalValue.substring(0, start) + `[${selectedText || "link text"}](url)` + internalValue.substring(end);
|
|
13433
|
+
setInternalValue(newValue);
|
|
13434
|
+
onChange(newValue);
|
|
13435
|
+
textareaRef.current.focus();
|
|
13436
|
+
}
|
|
13437
|
+
},
|
|
13438
|
+
title: "Link (Ctrl+K)",
|
|
13439
|
+
"aria-label": "Link"
|
|
13440
|
+
},
|
|
13441
|
+
"\u{1F517}"
|
|
13442
|
+
)), /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ React34.createElement(
|
|
13443
|
+
"button",
|
|
13444
|
+
{
|
|
13445
|
+
type: "button",
|
|
13446
|
+
className: `editor-toolbar-btn ${showPreviewPanel ? "active" : ""}`,
|
|
13447
|
+
onClick: () => setShowPreviewPanel(!showPreviewPanel),
|
|
13448
|
+
title: "Toggle preview",
|
|
13449
|
+
"aria-label": "Toggle preview",
|
|
13450
|
+
"aria-pressed": showPreviewPanel
|
|
13451
|
+
},
|
|
13452
|
+
"\u{1F441}"
|
|
13453
|
+
))),
|
|
13454
|
+
/* @__PURE__ */ React34.createElement("div", { className: `editor-markdown-content ${showPreviewPanel ? "split" : ""}` }, /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-editor" }, /* @__PURE__ */ React34.createElement(
|
|
13455
|
+
"textarea",
|
|
13456
|
+
{
|
|
13457
|
+
ref: textareaRef,
|
|
13458
|
+
className: "editor-textarea editor-markdown-textarea",
|
|
13459
|
+
value: internalValue,
|
|
13460
|
+
onChange: handleChange,
|
|
13461
|
+
onKeyDown: handleKeyDown,
|
|
13462
|
+
rows,
|
|
13463
|
+
maxLength,
|
|
13464
|
+
placeholder: "Enter markdown text...",
|
|
13465
|
+
"aria-label": "Markdown editor",
|
|
13466
|
+
style: { maxHeight }
|
|
13467
|
+
}
|
|
13468
|
+
), /* @__PURE__ */ React34.createElement("div", { className: `editor-markdown-char-count ${charCountClass}` }, charCount, maxLength && ` / ${maxLength}`)), showPreviewPanel && /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-preview" }, /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-preview-label" }, "Preview"), /* @__PURE__ */ React34.createElement(
|
|
13469
|
+
"div",
|
|
13470
|
+
{
|
|
13471
|
+
className: "editor-markdown-preview-content",
|
|
13472
|
+
dangerouslySetInnerHTML: { __html: previewHtml }
|
|
13473
|
+
}
|
|
13474
|
+
))),
|
|
13475
|
+
/* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-footer" }, /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-hint" }, /* @__PURE__ */ React34.createElement("kbd", null, "Ctrl+Enter"), " to save \u2022 ", /* @__PURE__ */ React34.createElement("kbd", null, "Esc"), " to cancel"), /* @__PURE__ */ React34.createElement("div", { className: "editor-markdown-actions" }, /* @__PURE__ */ React34.createElement(
|
|
13476
|
+
"button",
|
|
13477
|
+
{
|
|
13478
|
+
type: "button",
|
|
13479
|
+
className: "editor-btn editor-btn-secondary",
|
|
13480
|
+
onClick: onCancel
|
|
13481
|
+
},
|
|
13482
|
+
"Cancel"
|
|
13483
|
+
), /* @__PURE__ */ React34.createElement(
|
|
13484
|
+
"button",
|
|
13485
|
+
{
|
|
13486
|
+
type: "button",
|
|
13487
|
+
className: "editor-btn editor-btn-primary",
|
|
13488
|
+
onClick: onCommit
|
|
13489
|
+
},
|
|
13490
|
+
"Save"
|
|
13491
|
+
)))
|
|
13492
|
+
);
|
|
13493
|
+
}
|
|
13494
|
+
MarkdownEditor.displayName = "MarkdownEditor";
|
|
13495
|
+
function renderMarkdownPreview(markdown) {
|
|
13496
|
+
if (!markdown) return '<p class="editor-markdown-empty">Nothing to preview</p>';
|
|
13497
|
+
let html = markdown;
|
|
13498
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
13499
|
+
html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
|
|
13500
|
+
html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
|
|
13501
|
+
html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
|
|
13502
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
13503
|
+
html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
|
|
13504
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
13505
|
+
html = html.replace(/_(.+?)_/g, "<em>$1</em>");
|
|
13506
|
+
html = html.replace(/`(.+?)`/g, "<code>$1</code>");
|
|
13507
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
13508
|
+
html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
|
|
13509
|
+
html = html.replace(/^- (.+)$/gim, "<li>$1</li>");
|
|
13510
|
+
html = html.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>");
|
|
13511
|
+
html = html.replace(/\n\n/g, "</p><p>");
|
|
13512
|
+
html = html.replace(/\n/g, "<br>");
|
|
13513
|
+
if (!html.startsWith("<h") && !html.startsWith("<ul") && !html.startsWith("<p")) {
|
|
13514
|
+
html = "<p>" + html + "</p>";
|
|
13515
|
+
}
|
|
13516
|
+
return html;
|
|
13517
|
+
}
|
|
11755
13518
|
export {
|
|
11756
13519
|
AdvancedFilterBuilder,
|
|
11757
13520
|
BadgeCell,
|
|
@@ -11760,6 +13523,7 @@ export {
|
|
|
11760
13523
|
ColumnFilters,
|
|
11761
13524
|
CurrencyCell,
|
|
11762
13525
|
DataGrid,
|
|
13526
|
+
DateEditor,
|
|
11763
13527
|
DensityToggle,
|
|
11764
13528
|
ExportMenu,
|
|
11765
13529
|
FacetedSearch,
|
|
@@ -11772,11 +13536,16 @@ export {
|
|
|
11772
13536
|
LayoutPersistenceManager,
|
|
11773
13537
|
LayoutPresetsManager,
|
|
11774
13538
|
LocalStorageAdapter,
|
|
13539
|
+
MarkdownEditor,
|
|
11775
13540
|
MarketDataEngine,
|
|
11776
13541
|
MarketDataGrid,
|
|
13542
|
+
MultiSelectEditor,
|
|
13543
|
+
NumericEditor,
|
|
13544
|
+
PivotToolbar,
|
|
11777
13545
|
PriorityIndicator,
|
|
11778
13546
|
ProgressBar,
|
|
11779
13547
|
Rating,
|
|
13548
|
+
RichSelectEditor,
|
|
11780
13549
|
ServerAdapter,
|
|
11781
13550
|
ServerSideDataSource,
|
|
11782
13551
|
StatusChip,
|
|
@@ -11786,6 +13555,7 @@ export {
|
|
|
11786
13555
|
VirtualScroller,
|
|
11787
13556
|
WebSocketMockFeed,
|
|
11788
13557
|
alpineTheme,
|
|
13558
|
+
buildPivot,
|
|
11789
13559
|
buildTreeFromFlat,
|
|
11790
13560
|
collapseAllNodes,
|
|
11791
13561
|
countTreeNodes,
|
|
@@ -11795,12 +13565,17 @@ export {
|
|
|
11795
13565
|
createMockWebSocket,
|
|
11796
13566
|
createPreset,
|
|
11797
13567
|
darkTheme,
|
|
13568
|
+
debounce2 as debounce,
|
|
11798
13569
|
densityConfigs,
|
|
13570
|
+
downloadCSV,
|
|
11799
13571
|
expandAllNodes,
|
|
13572
|
+
exportPivotToCSV,
|
|
11800
13573
|
exportToCSV,
|
|
11801
13574
|
exportToXLSX,
|
|
13575
|
+
filterOptions,
|
|
11802
13576
|
filterTree,
|
|
11803
13577
|
flattenTree,
|
|
13578
|
+
formatNumber,
|
|
11804
13579
|
generateDensityCSS,
|
|
11805
13580
|
generateFilename,
|
|
11806
13581
|
generatePresetId,
|
|
@@ -11818,10 +13593,15 @@ export {
|
|
|
11818
13593
|
isTreeNode,
|
|
11819
13594
|
loadDensityMode,
|
|
11820
13595
|
materialTheme,
|
|
13596
|
+
parseFormattedNumber,
|
|
11821
13597
|
quartzTheme,
|
|
11822
13598
|
saveDensityMode,
|
|
11823
13599
|
themes,
|
|
11824
13600
|
toggleNodeExpansion,
|
|
11825
13601
|
useDensityMode,
|
|
11826
|
-
|
|
13602
|
+
useEditorAutoFocus,
|
|
13603
|
+
useEditorClickOutside,
|
|
13604
|
+
useEditorKeyboardNavigation,
|
|
13605
|
+
useMarketData,
|
|
13606
|
+
usePopupPosition
|
|
11827
13607
|
};
|