react-open-source-grid 1.6.5 → 1.6.7
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-BbaZajrS.js → index-DgcHJP8T.js} +89 -3
- package/dist/assets/index.js +1 -1
- package/dist/assets/{layoutPersistence-CPItuVwj.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/types.d.ts +2 -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 +1314 -5
- package/dist/lib/index.css +634 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1300 -4
- package/package.json +1 -1
package/dist/lib/index.cjs
CHANGED
|
@@ -341,6 +341,7 @@ __export(index_exports, {
|
|
|
341
341
|
ColumnFilters: () => ColumnFilters,
|
|
342
342
|
CurrencyCell: () => CurrencyCell,
|
|
343
343
|
DataGrid: () => DataGrid,
|
|
344
|
+
DateEditor: () => DateEditor,
|
|
344
345
|
DensityToggle: () => DensityToggle,
|
|
345
346
|
ExportMenu: () => ExportMenu,
|
|
346
347
|
FacetedSearch: () => FacetedSearch,
|
|
@@ -353,12 +354,16 @@ __export(index_exports, {
|
|
|
353
354
|
LayoutPersistenceManager: () => LayoutPersistenceManager,
|
|
354
355
|
LayoutPresetsManager: () => LayoutPresetsManager,
|
|
355
356
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
357
|
+
MarkdownEditor: () => MarkdownEditor,
|
|
356
358
|
MarketDataEngine: () => MarketDataEngine,
|
|
357
359
|
MarketDataGrid: () => MarketDataGrid,
|
|
360
|
+
MultiSelectEditor: () => MultiSelectEditor,
|
|
361
|
+
NumericEditor: () => NumericEditor,
|
|
358
362
|
PivotToolbar: () => PivotToolbar,
|
|
359
363
|
PriorityIndicator: () => PriorityIndicator,
|
|
360
364
|
ProgressBar: () => ProgressBar,
|
|
361
365
|
Rating: () => Rating,
|
|
366
|
+
RichSelectEditor: () => RichSelectEditor,
|
|
362
367
|
ServerAdapter: () => ServerAdapter,
|
|
363
368
|
ServerSideDataSource: () => ServerSideDataSource,
|
|
364
369
|
StatusChip: () => StatusChip,
|
|
@@ -378,14 +383,17 @@ __export(index_exports, {
|
|
|
378
383
|
createMockWebSocket: () => createMockWebSocket,
|
|
379
384
|
createPreset: () => createPreset,
|
|
380
385
|
darkTheme: () => darkTheme,
|
|
386
|
+
debounce: () => debounce2,
|
|
381
387
|
densityConfigs: () => densityConfigs,
|
|
382
388
|
downloadCSV: () => downloadCSV,
|
|
383
389
|
expandAllNodes: () => expandAllNodes,
|
|
384
390
|
exportPivotToCSV: () => exportPivotToCSV,
|
|
385
391
|
exportToCSV: () => exportToCSV,
|
|
386
392
|
exportToXLSX: () => exportToXLSX,
|
|
393
|
+
filterOptions: () => filterOptions,
|
|
387
394
|
filterTree: () => filterTree,
|
|
388
395
|
flattenTree: () => flattenTree,
|
|
396
|
+
formatNumber: () => formatNumber,
|
|
389
397
|
generateDensityCSS: () => generateDensityCSS,
|
|
390
398
|
generateFilename: () => generateFilename,
|
|
391
399
|
generatePresetId: () => generatePresetId,
|
|
@@ -403,12 +411,17 @@ __export(index_exports, {
|
|
|
403
411
|
isTreeNode: () => isTreeNode,
|
|
404
412
|
loadDensityMode: () => loadDensityMode,
|
|
405
413
|
materialTheme: () => materialTheme,
|
|
414
|
+
parseFormattedNumber: () => parseFormattedNumber,
|
|
406
415
|
quartzTheme: () => quartzTheme,
|
|
407
416
|
saveDensityMode: () => saveDensityMode,
|
|
408
417
|
themes: () => themes,
|
|
409
418
|
toggleNodeExpansion: () => toggleNodeExpansion,
|
|
410
419
|
useDensityMode: () => useDensityMode,
|
|
411
|
-
|
|
420
|
+
useEditorAutoFocus: () => useEditorAutoFocus,
|
|
421
|
+
useEditorClickOutside: () => useEditorClickOutside,
|
|
422
|
+
useEditorKeyboardNavigation: () => useEditorKeyboardNavigation,
|
|
423
|
+
useMarketData: () => useMarketData,
|
|
424
|
+
usePopupPosition: () => usePopupPosition
|
|
412
425
|
});
|
|
413
426
|
module.exports = __toCommonJS(index_exports);
|
|
414
427
|
|
|
@@ -2759,7 +2772,16 @@ var GridBody = ({
|
|
|
2759
2772
|
onKeyDown: (e) => handleKeyDown(e, rowIndex, columnIndex),
|
|
2760
2773
|
tabIndex: isCellFocused ? 0 : -1
|
|
2761
2774
|
},
|
|
2762
|
-
isEditing ?
|
|
2775
|
+
isEditing ? column.editor ? column.editor({
|
|
2776
|
+
value: editState.value,
|
|
2777
|
+
row,
|
|
2778
|
+
column,
|
|
2779
|
+
onChange: handleEditChange,
|
|
2780
|
+
onCommit: handleEditComplete,
|
|
2781
|
+
onCancel: () => dispatch({ type: "END_EDIT" }),
|
|
2782
|
+
autoFocus: true,
|
|
2783
|
+
...column.editorParams
|
|
2784
|
+
}) : /* @__PURE__ */ import_react6.default.createElement(
|
|
2763
2785
|
"input",
|
|
2764
2786
|
{
|
|
2765
2787
|
ref: editInputRef,
|
|
@@ -5430,7 +5452,7 @@ var LayoutPresetsManager = ({
|
|
|
5430
5452
|
setLoading(false);
|
|
5431
5453
|
}
|
|
5432
5454
|
};
|
|
5433
|
-
const
|
|
5455
|
+
const formatDate2 = (timestamp) => {
|
|
5434
5456
|
return new Date(timestamp).toLocaleString();
|
|
5435
5457
|
};
|
|
5436
5458
|
return /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative inline-block" }, /* @__PURE__ */ import_react14.default.createElement(
|
|
@@ -5544,7 +5566,7 @@ var LayoutPresetsManager = ({
|
|
|
5544
5566
|
if (buttons) buttons.style.opacity = "0";
|
|
5545
5567
|
}
|
|
5546
5568
|
},
|
|
5547
|
-
/* @__PURE__ */ import_react14.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ import_react14.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ",
|
|
5569
|
+
/* @__PURE__ */ import_react14.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" } }, /* @__PURE__ */ import_react14.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement("h4", { style: { fontSize: "14px", fontWeight: "500", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.name), preset.description && /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, preset.description), /* @__PURE__ */ import_react14.default.createElement("p", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px" } }, "Updated: ", formatDate2(preset.updatedAt))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "preset-actions", style: { display: "flex", gap: "4px", marginLeft: "8px", opacity: 0, transition: "opacity 0.2s" } }, /* @__PURE__ */ import_react14.default.createElement(
|
|
5548
5570
|
"button",
|
|
5549
5571
|
{
|
|
5550
5572
|
onClick: (e) => handleUpdatePreset(preset, e),
|
|
@@ -12641,6 +12663,1280 @@ var PivotToolbar = ({
|
|
|
12641
12663
|
} }, /* @__PURE__ */ import_react33.default.createElement("strong", null, "\u{1F4A1} Tip:"), " Select a Row Group column to organize data by, a Pivot column whose values become new columns, and a Value column to aggregate.")
|
|
12642
12664
|
);
|
|
12643
12665
|
};
|
|
12666
|
+
|
|
12667
|
+
// src/editors/editorUtils.ts
|
|
12668
|
+
var import_react34 = require("react");
|
|
12669
|
+
function useEditorKeyboardNavigation(handlers, config = {}) {
|
|
12670
|
+
const {
|
|
12671
|
+
commitOnEnter = true,
|
|
12672
|
+
cancelOnEscape = true,
|
|
12673
|
+
commitOnTab = true,
|
|
12674
|
+
preventDefault = true,
|
|
12675
|
+
stopPropagation = true
|
|
12676
|
+
} = config;
|
|
12677
|
+
const handleKeyDown = (0, import_react34.useCallback)(
|
|
12678
|
+
(event) => {
|
|
12679
|
+
let handled = false;
|
|
12680
|
+
switch (event.key) {
|
|
12681
|
+
case "Enter":
|
|
12682
|
+
if (commitOnEnter && handlers.onEnter) {
|
|
12683
|
+
handlers.onEnter();
|
|
12684
|
+
handled = true;
|
|
12685
|
+
}
|
|
12686
|
+
break;
|
|
12687
|
+
case "Escape":
|
|
12688
|
+
if (cancelOnEscape && handlers.onEscape) {
|
|
12689
|
+
handlers.onEscape();
|
|
12690
|
+
handled = true;
|
|
12691
|
+
}
|
|
12692
|
+
break;
|
|
12693
|
+
case "Tab":
|
|
12694
|
+
if (commitOnTab && handlers.onTab) {
|
|
12695
|
+
handlers.onTab(event.shiftKey);
|
|
12696
|
+
handled = true;
|
|
12697
|
+
}
|
|
12698
|
+
break;
|
|
12699
|
+
case "ArrowUp":
|
|
12700
|
+
if (handlers.onArrowUp) {
|
|
12701
|
+
handlers.onArrowUp();
|
|
12702
|
+
handled = true;
|
|
12703
|
+
}
|
|
12704
|
+
break;
|
|
12705
|
+
case "ArrowDown":
|
|
12706
|
+
if (handlers.onArrowDown) {
|
|
12707
|
+
handlers.onArrowDown();
|
|
12708
|
+
handled = true;
|
|
12709
|
+
}
|
|
12710
|
+
break;
|
|
12711
|
+
}
|
|
12712
|
+
if (handled) {
|
|
12713
|
+
if (preventDefault) {
|
|
12714
|
+
event.preventDefault();
|
|
12715
|
+
}
|
|
12716
|
+
if (stopPropagation) {
|
|
12717
|
+
event.stopPropagation();
|
|
12718
|
+
}
|
|
12719
|
+
}
|
|
12720
|
+
},
|
|
12721
|
+
[
|
|
12722
|
+
handlers,
|
|
12723
|
+
commitOnEnter,
|
|
12724
|
+
cancelOnEscape,
|
|
12725
|
+
commitOnTab,
|
|
12726
|
+
preventDefault,
|
|
12727
|
+
stopPropagation
|
|
12728
|
+
]
|
|
12729
|
+
);
|
|
12730
|
+
return { handleKeyDown };
|
|
12731
|
+
}
|
|
12732
|
+
function useEditorAutoFocus(autoFocus = true, selectAll = false) {
|
|
12733
|
+
const elementRef = (0, import_react34.useRef)(null);
|
|
12734
|
+
(0, import_react34.useEffect)(() => {
|
|
12735
|
+
if (autoFocus && elementRef.current) {
|
|
12736
|
+
elementRef.current.focus();
|
|
12737
|
+
if (selectAll && elementRef.current instanceof HTMLInputElement) {
|
|
12738
|
+
elementRef.current.select();
|
|
12739
|
+
}
|
|
12740
|
+
}
|
|
12741
|
+
}, [autoFocus, selectAll]);
|
|
12742
|
+
return elementRef;
|
|
12743
|
+
}
|
|
12744
|
+
function useEditorClickOutside(ref, onClickOutside, enabled = true) {
|
|
12745
|
+
(0, import_react34.useEffect)(() => {
|
|
12746
|
+
if (!enabled) return;
|
|
12747
|
+
const handleClickOutside = (event) => {
|
|
12748
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
12749
|
+
onClickOutside();
|
|
12750
|
+
}
|
|
12751
|
+
};
|
|
12752
|
+
const timeoutId = setTimeout(() => {
|
|
12753
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
12754
|
+
}, 0);
|
|
12755
|
+
return () => {
|
|
12756
|
+
clearTimeout(timeoutId);
|
|
12757
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
12758
|
+
};
|
|
12759
|
+
}, [ref, onClickOutside, enabled]);
|
|
12760
|
+
}
|
|
12761
|
+
function usePopupPosition(anchorRef, popupRef, isOpen, placement = "auto") {
|
|
12762
|
+
(0, import_react34.useEffect)(() => {
|
|
12763
|
+
if (!isOpen || !anchorRef.current || !popupRef.current) return;
|
|
12764
|
+
const anchor = anchorRef.current;
|
|
12765
|
+
const popup = popupRef.current;
|
|
12766
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
12767
|
+
const popupRect = popup.getBoundingClientRect();
|
|
12768
|
+
const viewportHeight = window.innerHeight;
|
|
12769
|
+
const viewportWidth = window.innerWidth;
|
|
12770
|
+
let top = 0;
|
|
12771
|
+
let left = 0;
|
|
12772
|
+
let actualPlacement = placement;
|
|
12773
|
+
if (placement === "auto") {
|
|
12774
|
+
const spaceBelow = viewportHeight - anchorRect.bottom;
|
|
12775
|
+
const spaceAbove = anchorRect.top;
|
|
12776
|
+
const spaceRight = viewportWidth - anchorRect.right;
|
|
12777
|
+
const spaceLeft = anchorRect.left;
|
|
12778
|
+
if (spaceBelow >= popupRect.height || spaceBelow >= spaceAbove) {
|
|
12779
|
+
actualPlacement = "bottom";
|
|
12780
|
+
} else if (spaceAbove >= popupRect.height) {
|
|
12781
|
+
actualPlacement = "top";
|
|
12782
|
+
} else if (spaceRight >= popupRect.width) {
|
|
12783
|
+
actualPlacement = "right";
|
|
12784
|
+
} else if (spaceLeft >= popupRect.width) {
|
|
12785
|
+
actualPlacement = "left";
|
|
12786
|
+
} else {
|
|
12787
|
+
actualPlacement = "bottom";
|
|
12788
|
+
}
|
|
12789
|
+
}
|
|
12790
|
+
switch (actualPlacement) {
|
|
12791
|
+
case "bottom":
|
|
12792
|
+
top = anchorRect.bottom + window.scrollY;
|
|
12793
|
+
left = anchorRect.left + window.scrollX;
|
|
12794
|
+
break;
|
|
12795
|
+
case "top":
|
|
12796
|
+
top = anchorRect.top + window.scrollY - popupRect.height;
|
|
12797
|
+
left = anchorRect.left + window.scrollX;
|
|
12798
|
+
break;
|
|
12799
|
+
case "right":
|
|
12800
|
+
top = anchorRect.top + window.scrollY;
|
|
12801
|
+
left = anchorRect.right + window.scrollX;
|
|
12802
|
+
break;
|
|
12803
|
+
case "left":
|
|
12804
|
+
top = anchorRect.top + window.scrollY;
|
|
12805
|
+
left = anchorRect.left + window.scrollX - popupRect.width;
|
|
12806
|
+
break;
|
|
12807
|
+
}
|
|
12808
|
+
const margin = 8;
|
|
12809
|
+
if (left + popupRect.width > viewportWidth) {
|
|
12810
|
+
left = viewportWidth - popupRect.width - margin;
|
|
12811
|
+
}
|
|
12812
|
+
if (left < margin) {
|
|
12813
|
+
left = margin;
|
|
12814
|
+
}
|
|
12815
|
+
if (top + popupRect.height > viewportHeight + window.scrollY) {
|
|
12816
|
+
top = viewportHeight + window.scrollY - popupRect.height - margin;
|
|
12817
|
+
}
|
|
12818
|
+
if (top < window.scrollY + margin) {
|
|
12819
|
+
top = window.scrollY + margin;
|
|
12820
|
+
}
|
|
12821
|
+
popup.style.top = `${top}px`;
|
|
12822
|
+
popup.style.left = `${left}px`;
|
|
12823
|
+
popup.style.minWidth = `${anchorRect.width}px`;
|
|
12824
|
+
}, [isOpen, anchorRef, popupRef, placement]);
|
|
12825
|
+
}
|
|
12826
|
+
function debounce2(func, wait) {
|
|
12827
|
+
let timeout = null;
|
|
12828
|
+
return function executedFunction(...args) {
|
|
12829
|
+
const later = () => {
|
|
12830
|
+
timeout = null;
|
|
12831
|
+
func(...args);
|
|
12832
|
+
};
|
|
12833
|
+
if (timeout) {
|
|
12834
|
+
clearTimeout(timeout);
|
|
12835
|
+
}
|
|
12836
|
+
timeout = setTimeout(later, wait);
|
|
12837
|
+
};
|
|
12838
|
+
}
|
|
12839
|
+
function formatNumber(value, decimals = 0, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12840
|
+
const fixed = value.toFixed(decimals);
|
|
12841
|
+
const [integerPart, decimalPart] = fixed.split(".");
|
|
12842
|
+
const formattedInteger = integerPart.replace(
|
|
12843
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
12844
|
+
thousandsSeparator
|
|
12845
|
+
);
|
|
12846
|
+
return decimalPart ? `${formattedInteger}${decimalSeparator}${decimalPart}` : formattedInteger;
|
|
12847
|
+
}
|
|
12848
|
+
function parseFormattedNumber(value, thousandsSeparator = ",", decimalSeparator = ".") {
|
|
12849
|
+
if (!value || typeof value !== "string") return null;
|
|
12850
|
+
const normalized = value.replace(new RegExp(`\\${thousandsSeparator}`, "g"), "").replace(new RegExp(`\\${decimalSeparator}`), ".");
|
|
12851
|
+
const parsed = parseFloat(normalized);
|
|
12852
|
+
return isNaN(parsed) ? null : parsed;
|
|
12853
|
+
}
|
|
12854
|
+
function filterOptions(options, searchQuery) {
|
|
12855
|
+
if (!searchQuery.trim()) return options;
|
|
12856
|
+
const lowerQuery = searchQuery.toLowerCase();
|
|
12857
|
+
return options.filter(
|
|
12858
|
+
(option) => option.label.toLowerCase().includes(lowerQuery)
|
|
12859
|
+
);
|
|
12860
|
+
}
|
|
12861
|
+
|
|
12862
|
+
// src/editors/RichSelectEditor.tsx
|
|
12863
|
+
var import_react35 = __toESM(require("react"), 1);
|
|
12864
|
+
function RichSelectEditor(props) {
|
|
12865
|
+
const {
|
|
12866
|
+
value,
|
|
12867
|
+
onChange,
|
|
12868
|
+
onCommit,
|
|
12869
|
+
onCancel,
|
|
12870
|
+
autoFocus = true,
|
|
12871
|
+
options,
|
|
12872
|
+
placeholder = "Select...",
|
|
12873
|
+
allowClear = false,
|
|
12874
|
+
filterable = true,
|
|
12875
|
+
renderOptionLabel,
|
|
12876
|
+
maxDropdownHeight = 300
|
|
12877
|
+
} = props;
|
|
12878
|
+
const [isOpen] = (0, import_react35.useState)(true);
|
|
12879
|
+
const [searchQuery, setSearchQuery] = (0, import_react35.useState)("");
|
|
12880
|
+
const [focusedIndex, setFocusedIndex] = (0, import_react35.useState)(-1);
|
|
12881
|
+
const containerRef = (0, import_react35.useRef)(null);
|
|
12882
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
12883
|
+
const dropdownRef = (0, import_react35.useRef)(null);
|
|
12884
|
+
const optionRefs = (0, import_react35.useRef)([]);
|
|
12885
|
+
const filteredOptions = (0, import_react35.useMemo)(
|
|
12886
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
12887
|
+
[options, searchQuery, filterable]
|
|
12888
|
+
);
|
|
12889
|
+
const selectedOption = (0, import_react35.useMemo)(
|
|
12890
|
+
() => options.find((opt) => opt.value === value),
|
|
12891
|
+
[options, value]
|
|
12892
|
+
);
|
|
12893
|
+
(0, import_react35.useEffect)(() => {
|
|
12894
|
+
if (selectedOption) {
|
|
12895
|
+
const index = filteredOptions.findIndex(
|
|
12896
|
+
(opt) => opt.value === selectedOption.value
|
|
12897
|
+
);
|
|
12898
|
+
if (index !== -1 && focusedIndex === -1) {
|
|
12899
|
+
queueMicrotask(() => setFocusedIndex(index));
|
|
12900
|
+
}
|
|
12901
|
+
}
|
|
12902
|
+
}, [selectedOption, filteredOptions]);
|
|
12903
|
+
(0, import_react35.useEffect)(() => {
|
|
12904
|
+
var _a;
|
|
12905
|
+
if (focusedIndex >= 0 && optionRefs.current[focusedIndex]) {
|
|
12906
|
+
(_a = optionRefs.current[focusedIndex]) == null ? void 0 : _a.scrollIntoView({
|
|
12907
|
+
block: "nearest",
|
|
12908
|
+
behavior: "smooth"
|
|
12909
|
+
});
|
|
12910
|
+
}
|
|
12911
|
+
}, [focusedIndex]);
|
|
12912
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
12913
|
+
useEditorClickOutside(
|
|
12914
|
+
containerRef,
|
|
12915
|
+
() => {
|
|
12916
|
+
if (isOpen) {
|
|
12917
|
+
onCommit();
|
|
12918
|
+
}
|
|
12919
|
+
},
|
|
12920
|
+
true
|
|
12921
|
+
);
|
|
12922
|
+
const handleSelectOption = (option) => {
|
|
12923
|
+
if (!option.disabled) {
|
|
12924
|
+
onChange(option.value);
|
|
12925
|
+
onCommit();
|
|
12926
|
+
}
|
|
12927
|
+
};
|
|
12928
|
+
const handleClear = (e) => {
|
|
12929
|
+
e.stopPropagation();
|
|
12930
|
+
onChange(null);
|
|
12931
|
+
setSearchQuery("");
|
|
12932
|
+
setFocusedIndex(-1);
|
|
12933
|
+
};
|
|
12934
|
+
const handleArrowDown = () => {
|
|
12935
|
+
setFocusedIndex((prev) => {
|
|
12936
|
+
var _a;
|
|
12937
|
+
let next = prev + 1;
|
|
12938
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12939
|
+
next++;
|
|
12940
|
+
}
|
|
12941
|
+
return next < filteredOptions.length ? next : prev;
|
|
12942
|
+
});
|
|
12943
|
+
};
|
|
12944
|
+
const handleArrowUp = () => {
|
|
12945
|
+
setFocusedIndex((prev) => {
|
|
12946
|
+
var _a;
|
|
12947
|
+
let next = prev - 1;
|
|
12948
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
12949
|
+
next--;
|
|
12950
|
+
}
|
|
12951
|
+
return next >= 0 ? next : prev;
|
|
12952
|
+
});
|
|
12953
|
+
};
|
|
12954
|
+
const handleEnter = () => {
|
|
12955
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
12956
|
+
const option = filteredOptions[focusedIndex];
|
|
12957
|
+
if (option && !option.disabled) {
|
|
12958
|
+
handleSelectOption(option);
|
|
12959
|
+
}
|
|
12960
|
+
} else {
|
|
12961
|
+
onCommit();
|
|
12962
|
+
}
|
|
12963
|
+
};
|
|
12964
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
12965
|
+
{
|
|
12966
|
+
onEnter: handleEnter,
|
|
12967
|
+
onEscape: onCancel,
|
|
12968
|
+
onArrowUp: handleArrowUp,
|
|
12969
|
+
onArrowDown: handleArrowDown
|
|
12970
|
+
},
|
|
12971
|
+
{
|
|
12972
|
+
commitOnTab: true,
|
|
12973
|
+
commitOnBlur: false
|
|
12974
|
+
}
|
|
12975
|
+
);
|
|
12976
|
+
const renderLabel = (option) => {
|
|
12977
|
+
if (renderOptionLabel) {
|
|
12978
|
+
return renderOptionLabel(option);
|
|
12979
|
+
}
|
|
12980
|
+
return /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ import_react35.default.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-text" }, /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-label" }, option.label), option.description && /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-option-description" }, option.description)));
|
|
12981
|
+
};
|
|
12982
|
+
return /* @__PURE__ */ import_react35.default.createElement(
|
|
12983
|
+
"div",
|
|
12984
|
+
{
|
|
12985
|
+
ref: containerRef,
|
|
12986
|
+
className: "editor-container editor-richselect-container",
|
|
12987
|
+
onKeyDown: handleKeyDown
|
|
12988
|
+
},
|
|
12989
|
+
/* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ import_react35.default.createElement(
|
|
12990
|
+
"input",
|
|
12991
|
+
{
|
|
12992
|
+
ref: inputRef,
|
|
12993
|
+
type: "text",
|
|
12994
|
+
className: "editor-input editor-richselect-input",
|
|
12995
|
+
value: filterable ? searchQuery : (selectedOption == null ? void 0 : selectedOption.label) || "",
|
|
12996
|
+
onChange: (e) => {
|
|
12997
|
+
if (filterable) {
|
|
12998
|
+
setSearchQuery(e.target.value);
|
|
12999
|
+
setFocusedIndex(-1);
|
|
13000
|
+
}
|
|
13001
|
+
},
|
|
13002
|
+
placeholder: (selectedOption == null ? void 0 : selectedOption.label) || placeholder,
|
|
13003
|
+
"aria-label": "Select option",
|
|
13004
|
+
"aria-expanded": isOpen,
|
|
13005
|
+
"aria-autocomplete": "list",
|
|
13006
|
+
"aria-controls": "richselect-dropdown",
|
|
13007
|
+
autoComplete: "off"
|
|
13008
|
+
}
|
|
13009
|
+
), /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-input-actions" }, allowClear && value !== null && value !== void 0 && /* @__PURE__ */ import_react35.default.createElement(
|
|
13010
|
+
"button",
|
|
13011
|
+
{
|
|
13012
|
+
type: "button",
|
|
13013
|
+
className: "editor-clear-btn",
|
|
13014
|
+
onClick: handleClear,
|
|
13015
|
+
"aria-label": "Clear selection",
|
|
13016
|
+
tabIndex: -1
|
|
13017
|
+
},
|
|
13018
|
+
"\xD7"
|
|
13019
|
+
), /* @__PURE__ */ import_react35.default.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC"))),
|
|
13020
|
+
isOpen && /* @__PURE__ */ import_react35.default.createElement(
|
|
13021
|
+
"div",
|
|
13022
|
+
{
|
|
13023
|
+
ref: dropdownRef,
|
|
13024
|
+
id: "richselect-dropdown",
|
|
13025
|
+
className: "editor-dropdown",
|
|
13026
|
+
role: "listbox",
|
|
13027
|
+
style: { maxHeight: maxDropdownHeight }
|
|
13028
|
+
},
|
|
13029
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ import_react35.default.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => /* @__PURE__ */ import_react35.default.createElement(
|
|
13030
|
+
"div",
|
|
13031
|
+
{
|
|
13032
|
+
key: option.value,
|
|
13033
|
+
ref: (el) => {
|
|
13034
|
+
optionRefs.current[index] = el;
|
|
13035
|
+
},
|
|
13036
|
+
className: `editor-dropdown-option ${option.value === value ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
13037
|
+
role: "option",
|
|
13038
|
+
"aria-selected": option.value === value,
|
|
13039
|
+
"aria-disabled": option.disabled,
|
|
13040
|
+
onClick: () => handleSelectOption(option),
|
|
13041
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
13042
|
+
},
|
|
13043
|
+
renderLabel(option)
|
|
13044
|
+
))
|
|
13045
|
+
)
|
|
13046
|
+
);
|
|
13047
|
+
}
|
|
13048
|
+
RichSelectEditor.displayName = "RichSelectEditor";
|
|
13049
|
+
|
|
13050
|
+
// src/editors/DateEditor.tsx
|
|
13051
|
+
var import_react36 = __toESM(require("react"), 1);
|
|
13052
|
+
function DateEditor(props) {
|
|
13053
|
+
const {
|
|
13054
|
+
value,
|
|
13055
|
+
onChange,
|
|
13056
|
+
onCommit,
|
|
13057
|
+
onCancel,
|
|
13058
|
+
autoFocus = true,
|
|
13059
|
+
dateFormat = "yyyy-MM-dd",
|
|
13060
|
+
showTime = false,
|
|
13061
|
+
minDate,
|
|
13062
|
+
maxDate,
|
|
13063
|
+
autoCommit = false
|
|
13064
|
+
} = props;
|
|
13065
|
+
const [isOpen] = (0, import_react36.useState)(true);
|
|
13066
|
+
const [inputValue, setInputValue] = (0, import_react36.useState)("");
|
|
13067
|
+
const [viewDate, setViewDate] = (0, import_react36.useState)(/* @__PURE__ */ new Date());
|
|
13068
|
+
const containerRef = (0, import_react36.useRef)(null);
|
|
13069
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
13070
|
+
const calendarRef = (0, import_react36.useRef)(null);
|
|
13071
|
+
const parsedValue = (0, import_react36.useMemo)(() => {
|
|
13072
|
+
if (!value) return null;
|
|
13073
|
+
if (value instanceof Date) return value;
|
|
13074
|
+
const parsed = new Date(value);
|
|
13075
|
+
return isNaN(parsed.getTime()) ? null : parsed;
|
|
13076
|
+
}, [value]);
|
|
13077
|
+
import_react36.default.useEffect(() => {
|
|
13078
|
+
if (parsedValue) {
|
|
13079
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
13080
|
+
setViewDate(parsedValue);
|
|
13081
|
+
}
|
|
13082
|
+
}, [parsedValue, dateFormat, showTime]);
|
|
13083
|
+
usePopupPosition(containerRef, calendarRef, isOpen, "auto");
|
|
13084
|
+
useEditorClickOutside(containerRef, () => {
|
|
13085
|
+
if (isOpen) {
|
|
13086
|
+
onCommit();
|
|
13087
|
+
}
|
|
13088
|
+
}, true);
|
|
13089
|
+
const handleSelectDate = (date) => {
|
|
13090
|
+
const newDate = new Date(date);
|
|
13091
|
+
if (parsedValue && !showTime) {
|
|
13092
|
+
newDate.setHours(parsedValue.getHours());
|
|
13093
|
+
newDate.setMinutes(parsedValue.getMinutes());
|
|
13094
|
+
newDate.setSeconds(parsedValue.getSeconds());
|
|
13095
|
+
}
|
|
13096
|
+
onChange(newDate);
|
|
13097
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
13098
|
+
if (autoCommit && !showTime) {
|
|
13099
|
+
onCommit();
|
|
13100
|
+
}
|
|
13101
|
+
};
|
|
13102
|
+
const handleTimeChange = (hours, minutes) => {
|
|
13103
|
+
const newDate = parsedValue ? new Date(parsedValue) : /* @__PURE__ */ new Date();
|
|
13104
|
+
newDate.setHours(hours);
|
|
13105
|
+
newDate.setMinutes(minutes);
|
|
13106
|
+
onChange(newDate);
|
|
13107
|
+
setInputValue(formatDate(newDate, dateFormat, showTime));
|
|
13108
|
+
};
|
|
13109
|
+
const handleInputChange = (e) => {
|
|
13110
|
+
const newValue = e.target.value;
|
|
13111
|
+
setInputValue(newValue);
|
|
13112
|
+
const parsed = parseDate(newValue, dateFormat);
|
|
13113
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
13114
|
+
onChange(parsed);
|
|
13115
|
+
setViewDate(parsed);
|
|
13116
|
+
}
|
|
13117
|
+
};
|
|
13118
|
+
const handleInputBlur = () => {
|
|
13119
|
+
if (parsedValue) {
|
|
13120
|
+
setInputValue(formatDate(parsedValue, dateFormat, showTime));
|
|
13121
|
+
}
|
|
13122
|
+
};
|
|
13123
|
+
const handlePrevMonth = () => {
|
|
13124
|
+
setViewDate((prev) => {
|
|
13125
|
+
const newDate = new Date(prev);
|
|
13126
|
+
newDate.setMonth(newDate.getMonth() - 1);
|
|
13127
|
+
return newDate;
|
|
13128
|
+
});
|
|
13129
|
+
};
|
|
13130
|
+
const handleNextMonth = () => {
|
|
13131
|
+
setViewDate((prev) => {
|
|
13132
|
+
const newDate = new Date(prev);
|
|
13133
|
+
newDate.setMonth(newDate.getMonth() + 1);
|
|
13134
|
+
return newDate;
|
|
13135
|
+
});
|
|
13136
|
+
};
|
|
13137
|
+
const { handleKeyDown } = useEditorKeyboardNavigation(
|
|
13138
|
+
{
|
|
13139
|
+
onEnter: onCommit,
|
|
13140
|
+
onEscape: onCancel
|
|
13141
|
+
},
|
|
13142
|
+
{
|
|
13143
|
+
commitOnTab: true,
|
|
13144
|
+
commitOnBlur: false
|
|
13145
|
+
}
|
|
13146
|
+
);
|
|
13147
|
+
const calendarDays = (0, import_react36.useMemo)(() => {
|
|
13148
|
+
return generateCalendarDays(viewDate, parsedValue, minDate, maxDate);
|
|
13149
|
+
}, [viewDate, parsedValue, minDate, maxDate]);
|
|
13150
|
+
return /* @__PURE__ */ import_react36.default.createElement(
|
|
13151
|
+
"div",
|
|
13152
|
+
{
|
|
13153
|
+
ref: containerRef,
|
|
13154
|
+
className: "editor-container editor-date-container",
|
|
13155
|
+
onKeyDown: handleKeyDown
|
|
13156
|
+
},
|
|
13157
|
+
/* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-input-wrapper" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13158
|
+
"input",
|
|
13159
|
+
{
|
|
13160
|
+
ref: inputRef,
|
|
13161
|
+
type: "text",
|
|
13162
|
+
className: "editor-input editor-date-input",
|
|
13163
|
+
value: inputValue,
|
|
13164
|
+
onChange: handleInputChange,
|
|
13165
|
+
onBlur: handleInputBlur,
|
|
13166
|
+
placeholder: dateFormat,
|
|
13167
|
+
"aria-label": "Date input",
|
|
13168
|
+
autoComplete: "off"
|
|
13169
|
+
}
|
|
13170
|
+
), /* @__PURE__ */ import_react36.default.createElement("span", { className: "editor-calendar-icon", "aria-hidden": "true" }, "\u{1F4C5}")),
|
|
13171
|
+
isOpen && /* @__PURE__ */ import_react36.default.createElement("div", { ref: calendarRef, className: "editor-dropdown editor-calendar" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-header" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13172
|
+
"button",
|
|
13173
|
+
{
|
|
13174
|
+
type: "button",
|
|
13175
|
+
className: "editor-calendar-nav",
|
|
13176
|
+
onClick: handlePrevMonth,
|
|
13177
|
+
"aria-label": "Previous month"
|
|
13178
|
+
},
|
|
13179
|
+
"\u2039"
|
|
13180
|
+
), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-title" }, viewDate.toLocaleDateString("en-US", {
|
|
13181
|
+
month: "long",
|
|
13182
|
+
year: "numeric"
|
|
13183
|
+
})), /* @__PURE__ */ import_react36.default.createElement(
|
|
13184
|
+
"button",
|
|
13185
|
+
{
|
|
13186
|
+
type: "button",
|
|
13187
|
+
className: "editor-calendar-nav",
|
|
13188
|
+
onClick: handleNextMonth,
|
|
13189
|
+
"aria-label": "Next month"
|
|
13190
|
+
},
|
|
13191
|
+
"\u203A"
|
|
13192
|
+
)), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-weekdays" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ import_react36.default.createElement("div", { key: day, className: "editor-calendar-weekday" }, day))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-days" }, calendarDays.map((day, index) => /* @__PURE__ */ import_react36.default.createElement(
|
|
13193
|
+
"button",
|
|
13194
|
+
{
|
|
13195
|
+
key: index,
|
|
13196
|
+
type: "button",
|
|
13197
|
+
className: `editor-calendar-day ${day.className}`,
|
|
13198
|
+
onClick: () => day.date && handleSelectDate(day.date),
|
|
13199
|
+
disabled: day.disabled,
|
|
13200
|
+
"aria-label": day.date ? day.date.toDateString() : ""
|
|
13201
|
+
},
|
|
13202
|
+
day.label
|
|
13203
|
+
))), showTime && /* @__PURE__ */ import_react36.default.createElement("div", { className: "editor-calendar-time" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
13204
|
+
"input",
|
|
13205
|
+
{
|
|
13206
|
+
type: "time",
|
|
13207
|
+
className: "editor-time-input",
|
|
13208
|
+
value: parsedValue ? `${String(parsedValue.getHours()).padStart(2, "0")}:${String(
|
|
13209
|
+
parsedValue.getMinutes()
|
|
13210
|
+
).padStart(2, "0")}` : "00:00",
|
|
13211
|
+
onChange: (e) => {
|
|
13212
|
+
const [hours, minutes] = e.target.value.split(":").map(Number);
|
|
13213
|
+
handleTimeChange(hours, minutes);
|
|
13214
|
+
},
|
|
13215
|
+
"aria-label": "Time input"
|
|
13216
|
+
}
|
|
13217
|
+
)))
|
|
13218
|
+
);
|
|
13219
|
+
}
|
|
13220
|
+
DateEditor.displayName = "DateEditor";
|
|
13221
|
+
function formatDate(date, format, showTime) {
|
|
13222
|
+
const year = date.getFullYear();
|
|
13223
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
13224
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
13225
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
13226
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
13227
|
+
let formatted = format.replace("yyyy", String(year)).replace("MM", month).replace("dd", day);
|
|
13228
|
+
if (showTime) {
|
|
13229
|
+
formatted += ` ${hours}:${minutes}`;
|
|
13230
|
+
}
|
|
13231
|
+
return formatted;
|
|
13232
|
+
}
|
|
13233
|
+
function parseDate(value, format) {
|
|
13234
|
+
if (!value) return null;
|
|
13235
|
+
try {
|
|
13236
|
+
const parts = value.split(/[-/\s:]/);
|
|
13237
|
+
if (parts.length < 3) return null;
|
|
13238
|
+
let year, month, day;
|
|
13239
|
+
let hours = 0, minutes = 0;
|
|
13240
|
+
if (format.startsWith("yyyy")) {
|
|
13241
|
+
year = parseInt(parts[0], 10);
|
|
13242
|
+
month = parseInt(parts[1], 10) - 1;
|
|
13243
|
+
day = parseInt(parts[2], 10);
|
|
13244
|
+
} else if (format.startsWith("MM")) {
|
|
13245
|
+
month = parseInt(parts[0], 10) - 1;
|
|
13246
|
+
day = parseInt(parts[1], 10);
|
|
13247
|
+
year = parseInt(parts[2], 10);
|
|
13248
|
+
} else {
|
|
13249
|
+
day = parseInt(parts[0], 10);
|
|
13250
|
+
month = parseInt(parts[1], 10) - 1;
|
|
13251
|
+
year = parseInt(parts[2], 10);
|
|
13252
|
+
}
|
|
13253
|
+
if (parts.length >= 5) {
|
|
13254
|
+
hours = parseInt(parts[3], 10);
|
|
13255
|
+
minutes = parseInt(parts[4], 10);
|
|
13256
|
+
}
|
|
13257
|
+
const date = new Date(year, month, day, hours, minutes);
|
|
13258
|
+
return isNaN(date.getTime()) ? null : date;
|
|
13259
|
+
} catch {
|
|
13260
|
+
return null;
|
|
13261
|
+
}
|
|
13262
|
+
}
|
|
13263
|
+
function generateCalendarDays(viewDate, selectedDate, minDate, maxDate) {
|
|
13264
|
+
const year = viewDate.getFullYear();
|
|
13265
|
+
const month = viewDate.getMonth();
|
|
13266
|
+
const firstDay = new Date(year, month, 1);
|
|
13267
|
+
const lastDay = new Date(year, month + 1, 0);
|
|
13268
|
+
const daysInMonth = lastDay.getDate();
|
|
13269
|
+
const startingDayOfWeek = firstDay.getDay();
|
|
13270
|
+
const days = [];
|
|
13271
|
+
const prevMonth = new Date(year, month, 0);
|
|
13272
|
+
const prevMonthDays = prevMonth.getDate();
|
|
13273
|
+
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
|
13274
|
+
const date = new Date(year, month - 1, prevMonthDays - i);
|
|
13275
|
+
days.push({
|
|
13276
|
+
date,
|
|
13277
|
+
label: String(prevMonthDays - i),
|
|
13278
|
+
className: "other-month",
|
|
13279
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
13280
|
+
});
|
|
13281
|
+
}
|
|
13282
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
13283
|
+
const date = new Date(year, month, i);
|
|
13284
|
+
const isSelected = selectedDate && isSameDay(date, selectedDate);
|
|
13285
|
+
const isToday = isSameDay(date, /* @__PURE__ */ new Date());
|
|
13286
|
+
const disabled = isDateDisabled(date, minDate, maxDate);
|
|
13287
|
+
days.push({
|
|
13288
|
+
date,
|
|
13289
|
+
label: String(i),
|
|
13290
|
+
className: `${isSelected ? "selected" : ""} ${isToday ? "today" : ""} ${disabled ? "disabled" : ""}`.trim(),
|
|
13291
|
+
disabled
|
|
13292
|
+
});
|
|
13293
|
+
}
|
|
13294
|
+
const remainingDays = 42 - days.length;
|
|
13295
|
+
for (let i = 1; i <= remainingDays; i++) {
|
|
13296
|
+
const date = new Date(year, month + 1, i);
|
|
13297
|
+
days.push({
|
|
13298
|
+
date,
|
|
13299
|
+
label: String(i),
|
|
13300
|
+
className: "other-month",
|
|
13301
|
+
disabled: isDateDisabled(date, minDate, maxDate)
|
|
13302
|
+
});
|
|
13303
|
+
}
|
|
13304
|
+
return days;
|
|
13305
|
+
}
|
|
13306
|
+
function isSameDay(date1, date2) {
|
|
13307
|
+
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
|
|
13308
|
+
}
|
|
13309
|
+
function isDateDisabled(date, minDate, maxDate) {
|
|
13310
|
+
if (minDate && date < minDate) return true;
|
|
13311
|
+
if (maxDate && date > maxDate) return true;
|
|
13312
|
+
return false;
|
|
13313
|
+
}
|
|
13314
|
+
|
|
13315
|
+
// src/editors/NumericEditor.tsx
|
|
13316
|
+
var import_react37 = __toESM(require("react"), 1);
|
|
13317
|
+
function NumericEditor(props) {
|
|
13318
|
+
const {
|
|
13319
|
+
value,
|
|
13320
|
+
onChange,
|
|
13321
|
+
onCommit,
|
|
13322
|
+
onCancel,
|
|
13323
|
+
autoFocus = true,
|
|
13324
|
+
min,
|
|
13325
|
+
max,
|
|
13326
|
+
step = 1,
|
|
13327
|
+
decimals = 0,
|
|
13328
|
+
prefix = "",
|
|
13329
|
+
suffix = "",
|
|
13330
|
+
allowNegative = true,
|
|
13331
|
+
showSteppers = true,
|
|
13332
|
+
thousandsSeparator = ",",
|
|
13333
|
+
decimalSeparator = "."
|
|
13334
|
+
} = props;
|
|
13335
|
+
const [inputValue, setInputValue] = (0, import_react37.useState)("");
|
|
13336
|
+
const [isFocused, setIsFocused] = (0, import_react37.useState)(true);
|
|
13337
|
+
const inputRef = useEditorAutoFocus(autoFocus, true);
|
|
13338
|
+
(0, import_react37.useEffect)(() => {
|
|
13339
|
+
if (value !== null && value !== void 0) {
|
|
13340
|
+
if (isFocused) {
|
|
13341
|
+
queueMicrotask(() => setInputValue(String(value)));
|
|
13342
|
+
} else {
|
|
13343
|
+
queueMicrotask(() => setInputValue(formatNumber(value, decimals, thousandsSeparator, decimalSeparator)));
|
|
13344
|
+
}
|
|
13345
|
+
} else {
|
|
13346
|
+
queueMicrotask(() => setInputValue(""));
|
|
13347
|
+
}
|
|
13348
|
+
}, [value, isFocused, decimals, thousandsSeparator, decimalSeparator]);
|
|
13349
|
+
const clampValue = (val) => {
|
|
13350
|
+
let clamped = val;
|
|
13351
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
13352
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
13353
|
+
return clamped;
|
|
13354
|
+
};
|
|
13355
|
+
const handleInputChange = (e) => {
|
|
13356
|
+
const newValue = e.target.value;
|
|
13357
|
+
setInputValue(newValue);
|
|
13358
|
+
if (newValue === "" || newValue === "-") {
|
|
13359
|
+
onChange(null);
|
|
13360
|
+
return;
|
|
13361
|
+
}
|
|
13362
|
+
let cleaned = newValue;
|
|
13363
|
+
if (prefix) cleaned = cleaned.replace(prefix, "");
|
|
13364
|
+
if (suffix) cleaned = cleaned.replace(suffix, "");
|
|
13365
|
+
const parsed = parseFormattedNumber(cleaned, thousandsSeparator, decimalSeparator);
|
|
13366
|
+
if (parsed !== null && !isNaN(parsed)) {
|
|
13367
|
+
if (!allowNegative && parsed < 0) {
|
|
13368
|
+
return;
|
|
13369
|
+
}
|
|
13370
|
+
const clamped = clampValue(parsed);
|
|
13371
|
+
onChange(clamped);
|
|
13372
|
+
}
|
|
13373
|
+
};
|
|
13374
|
+
const handleIncrement = () => {
|
|
13375
|
+
const currentValue = value ?? 0;
|
|
13376
|
+
const newValue = clampValue(currentValue + step);
|
|
13377
|
+
onChange(newValue);
|
|
13378
|
+
setInputValue(String(newValue));
|
|
13379
|
+
};
|
|
13380
|
+
const handleDecrement = () => {
|
|
13381
|
+
const currentValue = value ?? 0;
|
|
13382
|
+
const newValue = clampValue(currentValue - step);
|
|
13383
|
+
onChange(newValue);
|
|
13384
|
+
setInputValue(String(newValue));
|
|
13385
|
+
};
|
|
13386
|
+
const handleFocus = () => {
|
|
13387
|
+
setIsFocused(true);
|
|
13388
|
+
if (value !== null && value !== void 0) {
|
|
13389
|
+
setInputValue(String(value));
|
|
13390
|
+
}
|
|
13391
|
+
};
|
|
13392
|
+
const handleBlur = () => {
|
|
13393
|
+
setIsFocused(false);
|
|
13394
|
+
if (value !== null && value !== void 0) {
|
|
13395
|
+
const formatted = formatNumber(value, decimals, thousandsSeparator, decimalSeparator);
|
|
13396
|
+
setInputValue(formatted);
|
|
13397
|
+
}
|
|
13398
|
+
setTimeout(() => onCommit(), 100);
|
|
13399
|
+
};
|
|
13400
|
+
const handleKeyDown = (e) => {
|
|
13401
|
+
if (e.key === "ArrowUp") {
|
|
13402
|
+
e.preventDefault();
|
|
13403
|
+
handleIncrement();
|
|
13404
|
+
} else if (e.key === "ArrowDown") {
|
|
13405
|
+
e.preventDefault();
|
|
13406
|
+
handleDecrement();
|
|
13407
|
+
}
|
|
13408
|
+
};
|
|
13409
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
13410
|
+
{
|
|
13411
|
+
onEnter: onCommit,
|
|
13412
|
+
onEscape: onCancel
|
|
13413
|
+
},
|
|
13414
|
+
{
|
|
13415
|
+
commitOnTab: true,
|
|
13416
|
+
commitOnBlur: false,
|
|
13417
|
+
preventDefault: false
|
|
13418
|
+
// Let handleKeyDown handle arrow keys
|
|
13419
|
+
}
|
|
13420
|
+
);
|
|
13421
|
+
const handleCombinedKeyDown = (e) => {
|
|
13422
|
+
handleKeyDown(e);
|
|
13423
|
+
handleEditorKeyDown(e);
|
|
13424
|
+
};
|
|
13425
|
+
const displayValue = isFocused ? inputValue : inputValue ? `${prefix}${inputValue}${suffix}` : "";
|
|
13426
|
+
return /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-container editor-numeric-container" }, /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-input-wrapper" }, prefix && !isFocused && /* @__PURE__ */ import_react37.default.createElement("span", { className: "editor-numeric-prefix" }, prefix), /* @__PURE__ */ import_react37.default.createElement(
|
|
13427
|
+
"input",
|
|
13428
|
+
{
|
|
13429
|
+
ref: inputRef,
|
|
13430
|
+
type: "text",
|
|
13431
|
+
inputMode: "decimal",
|
|
13432
|
+
className: "editor-input editor-numeric-input",
|
|
13433
|
+
value: displayValue,
|
|
13434
|
+
onChange: handleInputChange,
|
|
13435
|
+
onFocus: handleFocus,
|
|
13436
|
+
onBlur: handleBlur,
|
|
13437
|
+
onKeyDown: handleCombinedKeyDown,
|
|
13438
|
+
"aria-label": "Numeric input",
|
|
13439
|
+
"aria-valuemin": min,
|
|
13440
|
+
"aria-valuemax": max,
|
|
13441
|
+
"aria-valuenow": value ?? void 0,
|
|
13442
|
+
autoComplete: "off"
|
|
13443
|
+
}
|
|
13444
|
+
), suffix && !isFocused && /* @__PURE__ */ import_react37.default.createElement("span", { className: "editor-numeric-suffix" }, suffix), showSteppers && /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-numeric-steppers" }, /* @__PURE__ */ import_react37.default.createElement(
|
|
13445
|
+
"button",
|
|
13446
|
+
{
|
|
13447
|
+
type: "button",
|
|
13448
|
+
className: "editor-numeric-stepper editor-numeric-increment",
|
|
13449
|
+
onClick: handleIncrement,
|
|
13450
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13451
|
+
disabled: max !== void 0 && (value ?? 0) >= max,
|
|
13452
|
+
"aria-label": "Increment",
|
|
13453
|
+
tabIndex: -1
|
|
13454
|
+
},
|
|
13455
|
+
"\u25B2"
|
|
13456
|
+
), /* @__PURE__ */ import_react37.default.createElement(
|
|
13457
|
+
"button",
|
|
13458
|
+
{
|
|
13459
|
+
type: "button",
|
|
13460
|
+
className: "editor-numeric-stepper editor-numeric-decrement",
|
|
13461
|
+
onClick: handleDecrement,
|
|
13462
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
13463
|
+
disabled: min !== void 0 && (value ?? 0) <= min,
|
|
13464
|
+
"aria-label": "Decrement",
|
|
13465
|
+
tabIndex: -1
|
|
13466
|
+
},
|
|
13467
|
+
"\u25BC"
|
|
13468
|
+
))), (min !== void 0 || max !== void 0) && /* @__PURE__ */ import_react37.default.createElement("div", { className: "editor-numeric-range" }, min !== void 0 && /* @__PURE__ */ import_react37.default.createElement("span", null, "Min: ", min), max !== void 0 && /* @__PURE__ */ import_react37.default.createElement("span", null, "Max: ", max)));
|
|
13469
|
+
}
|
|
13470
|
+
NumericEditor.displayName = "NumericEditor";
|
|
13471
|
+
|
|
13472
|
+
// src/editors/MultiSelectEditor.tsx
|
|
13473
|
+
var import_react38 = __toESM(require("react"), 1);
|
|
13474
|
+
function MultiSelectEditor(props) {
|
|
13475
|
+
const {
|
|
13476
|
+
value = [],
|
|
13477
|
+
onChange,
|
|
13478
|
+
onCommit,
|
|
13479
|
+
onCancel,
|
|
13480
|
+
autoFocus = true,
|
|
13481
|
+
options,
|
|
13482
|
+
placeholder = "Select...",
|
|
13483
|
+
maxTagCount = 3,
|
|
13484
|
+
filterable = true,
|
|
13485
|
+
maxDropdownHeight = 300,
|
|
13486
|
+
allowEmpty = true
|
|
13487
|
+
} = props;
|
|
13488
|
+
const [isOpen] = (0, import_react38.useState)(true);
|
|
13489
|
+
const [searchQuery, setSearchQuery] = (0, import_react38.useState)("");
|
|
13490
|
+
const [focusedIndex, setFocusedIndex] = (0, import_react38.useState)(-1);
|
|
13491
|
+
const containerRef = (0, import_react38.useRef)(null);
|
|
13492
|
+
const inputRef = useEditorAutoFocus(autoFocus);
|
|
13493
|
+
const dropdownRef = (0, import_react38.useRef)(null);
|
|
13494
|
+
const tagContainerRef = (0, import_react38.useRef)(null);
|
|
13495
|
+
const selectedValues = (0, import_react38.useMemo)(
|
|
13496
|
+
() => Array.isArray(value) ? value : [],
|
|
13497
|
+
[value]
|
|
13498
|
+
);
|
|
13499
|
+
const filteredOptions = (0, import_react38.useMemo)(
|
|
13500
|
+
() => filterable ? filterOptions(options, searchQuery) : options,
|
|
13501
|
+
[options, searchQuery, filterable]
|
|
13502
|
+
);
|
|
13503
|
+
const selectedOptions = (0, import_react38.useMemo)(
|
|
13504
|
+
() => options.filter((opt) => selectedValues.includes(opt.value)),
|
|
13505
|
+
[options, selectedValues]
|
|
13506
|
+
);
|
|
13507
|
+
usePopupPosition(containerRef, dropdownRef, isOpen, "auto");
|
|
13508
|
+
useEditorClickOutside(
|
|
13509
|
+
containerRef,
|
|
13510
|
+
() => {
|
|
13511
|
+
if (isOpen) {
|
|
13512
|
+
onCommit();
|
|
13513
|
+
}
|
|
13514
|
+
},
|
|
13515
|
+
true
|
|
13516
|
+
);
|
|
13517
|
+
const handleToggleOption = (option) => {
|
|
13518
|
+
if (option.disabled) return;
|
|
13519
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13520
|
+
let newValues;
|
|
13521
|
+
if (isSelected) {
|
|
13522
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13523
|
+
return;
|
|
13524
|
+
}
|
|
13525
|
+
newValues = selectedValues.filter((v) => v !== option.value);
|
|
13526
|
+
} else {
|
|
13527
|
+
newValues = [...selectedValues, option.value];
|
|
13528
|
+
}
|
|
13529
|
+
onChange(newValues);
|
|
13530
|
+
};
|
|
13531
|
+
const handleRemoveTag = (optionValue, e) => {
|
|
13532
|
+
e.stopPropagation();
|
|
13533
|
+
if (!allowEmpty && selectedValues.length === 1) {
|
|
13534
|
+
return;
|
|
13535
|
+
}
|
|
13536
|
+
const newValues = selectedValues.filter((v) => v !== optionValue);
|
|
13537
|
+
onChange(newValues);
|
|
13538
|
+
};
|
|
13539
|
+
const handleKeyDown = (e) => {
|
|
13540
|
+
if (e.key === "Backspace" && searchQuery === "" && selectedValues.length > 0) {
|
|
13541
|
+
e.preventDefault();
|
|
13542
|
+
if (allowEmpty || selectedValues.length > 1) {
|
|
13543
|
+
const newValues = selectedValues.slice(0, -1);
|
|
13544
|
+
onChange(newValues);
|
|
13545
|
+
}
|
|
13546
|
+
}
|
|
13547
|
+
};
|
|
13548
|
+
const handleArrowDown = () => {
|
|
13549
|
+
setFocusedIndex((prev) => {
|
|
13550
|
+
var _a;
|
|
13551
|
+
let next = prev + 1;
|
|
13552
|
+
while (next < filteredOptions.length && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13553
|
+
next++;
|
|
13554
|
+
}
|
|
13555
|
+
return next < filteredOptions.length ? next : prev;
|
|
13556
|
+
});
|
|
13557
|
+
};
|
|
13558
|
+
const handleArrowUp = () => {
|
|
13559
|
+
setFocusedIndex((prev) => {
|
|
13560
|
+
var _a;
|
|
13561
|
+
let next = prev - 1;
|
|
13562
|
+
while (next >= 0 && ((_a = filteredOptions[next]) == null ? void 0 : _a.disabled)) {
|
|
13563
|
+
next--;
|
|
13564
|
+
}
|
|
13565
|
+
return next >= 0 ? next : prev;
|
|
13566
|
+
});
|
|
13567
|
+
};
|
|
13568
|
+
const handleEnter = () => {
|
|
13569
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
13570
|
+
const option = filteredOptions[focusedIndex];
|
|
13571
|
+
if (option && !option.disabled) {
|
|
13572
|
+
handleToggleOption(option);
|
|
13573
|
+
}
|
|
13574
|
+
} else {
|
|
13575
|
+
onCommit();
|
|
13576
|
+
}
|
|
13577
|
+
};
|
|
13578
|
+
const { handleKeyDown: handleEditorKeyDown } = useEditorKeyboardNavigation(
|
|
13579
|
+
{
|
|
13580
|
+
onEnter: handleEnter,
|
|
13581
|
+
onEscape: onCancel,
|
|
13582
|
+
onArrowUp: handleArrowUp,
|
|
13583
|
+
onArrowDown: handleArrowDown
|
|
13584
|
+
},
|
|
13585
|
+
{
|
|
13586
|
+
commitOnTab: true,
|
|
13587
|
+
commitOnBlur: false
|
|
13588
|
+
}
|
|
13589
|
+
);
|
|
13590
|
+
const handleCombinedKeyDown = (e) => {
|
|
13591
|
+
handleKeyDown(e);
|
|
13592
|
+
handleEditorKeyDown(e);
|
|
13593
|
+
};
|
|
13594
|
+
const visibleTags = selectedOptions.slice(0, maxTagCount);
|
|
13595
|
+
const collapsedCount = Math.max(0, selectedOptions.length - maxTagCount);
|
|
13596
|
+
return /* @__PURE__ */ import_react38.default.createElement(
|
|
13597
|
+
"div",
|
|
13598
|
+
{
|
|
13599
|
+
ref: containerRef,
|
|
13600
|
+
className: "editor-container editor-multiselect-container",
|
|
13601
|
+
onKeyDown: handleCombinedKeyDown
|
|
13602
|
+
},
|
|
13603
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13604
|
+
"div",
|
|
13605
|
+
{
|
|
13606
|
+
ref: tagContainerRef,
|
|
13607
|
+
className: "editor-input-wrapper editor-multiselect-wrapper"
|
|
13608
|
+
},
|
|
13609
|
+
/* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-multiselect-tags" }, visibleTags.map((option) => /* @__PURE__ */ import_react38.default.createElement("div", { key: option.value, className: "editor-tag" }, option.icon && /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-tag-icon" }, option.icon), /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-tag-label" }, option.label), /* @__PURE__ */ import_react38.default.createElement(
|
|
13610
|
+
"button",
|
|
13611
|
+
{
|
|
13612
|
+
type: "button",
|
|
13613
|
+
className: "editor-tag-remove",
|
|
13614
|
+
onClick: (e) => handleRemoveTag(option.value, e),
|
|
13615
|
+
"aria-label": `Remove ${option.label}`,
|
|
13616
|
+
tabIndex: -1
|
|
13617
|
+
},
|
|
13618
|
+
"\xD7"
|
|
13619
|
+
))), collapsedCount > 0 && /* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-tag editor-tag-collapsed" }, "+", collapsedCount)),
|
|
13620
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13621
|
+
"input",
|
|
13622
|
+
{
|
|
13623
|
+
ref: inputRef,
|
|
13624
|
+
type: "text",
|
|
13625
|
+
className: "editor-input editor-multiselect-input",
|
|
13626
|
+
value: searchQuery,
|
|
13627
|
+
onChange: (e) => {
|
|
13628
|
+
setSearchQuery(e.target.value);
|
|
13629
|
+
setFocusedIndex(-1);
|
|
13630
|
+
},
|
|
13631
|
+
placeholder: selectedValues.length === 0 ? placeholder : "",
|
|
13632
|
+
"aria-label": "Search options",
|
|
13633
|
+
"aria-expanded": isOpen,
|
|
13634
|
+
"aria-autocomplete": "list",
|
|
13635
|
+
"aria-controls": "multiselect-dropdown",
|
|
13636
|
+
autoComplete: "off"
|
|
13637
|
+
}
|
|
13638
|
+
),
|
|
13639
|
+
/* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-dropdown-icon", "aria-hidden": "true" }, "\u25BC")
|
|
13640
|
+
),
|
|
13641
|
+
isOpen && /* @__PURE__ */ import_react38.default.createElement(
|
|
13642
|
+
"div",
|
|
13643
|
+
{
|
|
13644
|
+
ref: dropdownRef,
|
|
13645
|
+
id: "multiselect-dropdown",
|
|
13646
|
+
className: "editor-dropdown",
|
|
13647
|
+
role: "listbox",
|
|
13648
|
+
"aria-multiselectable": "true",
|
|
13649
|
+
style: { maxHeight: maxDropdownHeight }
|
|
13650
|
+
},
|
|
13651
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-dropdown-empty" }, "No options found") : filteredOptions.map((option, index) => {
|
|
13652
|
+
const isSelected = selectedValues.includes(option.value);
|
|
13653
|
+
return /* @__PURE__ */ import_react38.default.createElement(
|
|
13654
|
+
"div",
|
|
13655
|
+
{
|
|
13656
|
+
key: option.value,
|
|
13657
|
+
className: `editor-dropdown-option editor-multiselect-option ${isSelected ? "selected" : ""} ${option.disabled ? "disabled" : ""} ${index === focusedIndex ? "focused" : ""}`,
|
|
13658
|
+
role: "option",
|
|
13659
|
+
"aria-selected": isSelected,
|
|
13660
|
+
"aria-disabled": option.disabled,
|
|
13661
|
+
onClick: () => handleToggleOption(option),
|
|
13662
|
+
onMouseEnter: () => !option.disabled && setFocusedIndex(index)
|
|
13663
|
+
},
|
|
13664
|
+
/* @__PURE__ */ import_react38.default.createElement(
|
|
13665
|
+
"input",
|
|
13666
|
+
{
|
|
13667
|
+
type: "checkbox",
|
|
13668
|
+
className: "editor-multiselect-checkbox",
|
|
13669
|
+
checked: isSelected,
|
|
13670
|
+
onChange: () => {
|
|
13671
|
+
},
|
|
13672
|
+
disabled: option.disabled,
|
|
13673
|
+
tabIndex: -1,
|
|
13674
|
+
"aria-hidden": "true"
|
|
13675
|
+
}
|
|
13676
|
+
),
|
|
13677
|
+
/* @__PURE__ */ import_react38.default.createElement("div", { className: "editor-option-content" }, option.icon && /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-option-icon" }, option.icon), /* @__PURE__ */ import_react38.default.createElement("span", { className: "editor-option-label" }, option.label))
|
|
13678
|
+
);
|
|
13679
|
+
})
|
|
13680
|
+
)
|
|
13681
|
+
);
|
|
13682
|
+
}
|
|
13683
|
+
MultiSelectEditor.displayName = "MultiSelectEditor";
|
|
13684
|
+
|
|
13685
|
+
// src/editors/MarkdownEditor.tsx
|
|
13686
|
+
var import_react39 = __toESM(require("react"), 1);
|
|
13687
|
+
function MarkdownEditor(props) {
|
|
13688
|
+
const {
|
|
13689
|
+
value = "",
|
|
13690
|
+
onChange,
|
|
13691
|
+
onCommit,
|
|
13692
|
+
onCancel,
|
|
13693
|
+
autoFocus = true,
|
|
13694
|
+
maxLength,
|
|
13695
|
+
showPreview = true,
|
|
13696
|
+
minHeight = 150,
|
|
13697
|
+
maxHeight = 400,
|
|
13698
|
+
rows = 6
|
|
13699
|
+
} = props;
|
|
13700
|
+
const [internalValue, setInternalValue] = (0, import_react39.useState)(value || "");
|
|
13701
|
+
const [showPreviewPanel, setShowPreviewPanel] = (0, import_react39.useState)(showPreview);
|
|
13702
|
+
const containerRef = (0, import_react39.useRef)(null);
|
|
13703
|
+
const textareaRef = useEditorAutoFocus(autoFocus, true);
|
|
13704
|
+
useEditorClickOutside(
|
|
13705
|
+
containerRef,
|
|
13706
|
+
() => {
|
|
13707
|
+
onCommit();
|
|
13708
|
+
},
|
|
13709
|
+
true
|
|
13710
|
+
);
|
|
13711
|
+
const handleChange = (e) => {
|
|
13712
|
+
const newValue = e.target.value;
|
|
13713
|
+
if (maxLength && newValue.length > maxLength) {
|
|
13714
|
+
return;
|
|
13715
|
+
}
|
|
13716
|
+
setInternalValue(newValue);
|
|
13717
|
+
onChange(newValue);
|
|
13718
|
+
};
|
|
13719
|
+
const handleKeyDown = (e) => {
|
|
13720
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
13721
|
+
e.preventDefault();
|
|
13722
|
+
onCommit();
|
|
13723
|
+
return;
|
|
13724
|
+
}
|
|
13725
|
+
if (e.key === "Escape") {
|
|
13726
|
+
e.preventDefault();
|
|
13727
|
+
onCancel();
|
|
13728
|
+
return;
|
|
13729
|
+
}
|
|
13730
|
+
if (e.key === "Tab") {
|
|
13731
|
+
e.preventDefault();
|
|
13732
|
+
const start = e.currentTarget.selectionStart;
|
|
13733
|
+
const end = e.currentTarget.selectionEnd;
|
|
13734
|
+
const newValue = internalValue.substring(0, start) + " " + internalValue.substring(end);
|
|
13735
|
+
setInternalValue(newValue);
|
|
13736
|
+
onChange(newValue);
|
|
13737
|
+
setTimeout(() => {
|
|
13738
|
+
if (textareaRef.current) {
|
|
13739
|
+
textareaRef.current.selectionStart = start + 1;
|
|
13740
|
+
textareaRef.current.selectionEnd = start + 1;
|
|
13741
|
+
}
|
|
13742
|
+
}, 0);
|
|
13743
|
+
return;
|
|
13744
|
+
}
|
|
13745
|
+
if (e.ctrlKey || e.metaKey) {
|
|
13746
|
+
let insertion = "";
|
|
13747
|
+
let cursorOffset = 0;
|
|
13748
|
+
switch (e.key) {
|
|
13749
|
+
case "b":
|
|
13750
|
+
insertion = "****";
|
|
13751
|
+
cursorOffset = 2;
|
|
13752
|
+
break;
|
|
13753
|
+
case "i":
|
|
13754
|
+
insertion = "__";
|
|
13755
|
+
cursorOffset = 1;
|
|
13756
|
+
break;
|
|
13757
|
+
case "k":
|
|
13758
|
+
insertion = "[](url)";
|
|
13759
|
+
cursorOffset = 1;
|
|
13760
|
+
break;
|
|
13761
|
+
default:
|
|
13762
|
+
return;
|
|
13763
|
+
}
|
|
13764
|
+
if (insertion) {
|
|
13765
|
+
e.preventDefault();
|
|
13766
|
+
const start = e.currentTarget.selectionStart;
|
|
13767
|
+
const end = e.currentTarget.selectionEnd;
|
|
13768
|
+
const selectedText = internalValue.substring(start, end);
|
|
13769
|
+
let newValue;
|
|
13770
|
+
let newCursorPos;
|
|
13771
|
+
if (selectedText) {
|
|
13772
|
+
const before = insertion.substring(0, cursorOffset);
|
|
13773
|
+
const after = insertion.substring(cursorOffset);
|
|
13774
|
+
newValue = internalValue.substring(0, start) + before + selectedText + after + internalValue.substring(end);
|
|
13775
|
+
newCursorPos = start + before.length + selectedText.length;
|
|
13776
|
+
} else {
|
|
13777
|
+
newValue = internalValue.substring(0, start) + insertion + internalValue.substring(end);
|
|
13778
|
+
newCursorPos = start + cursorOffset;
|
|
13779
|
+
}
|
|
13780
|
+
setInternalValue(newValue);
|
|
13781
|
+
onChange(newValue);
|
|
13782
|
+
setTimeout(() => {
|
|
13783
|
+
if (textareaRef.current) {
|
|
13784
|
+
textareaRef.current.selectionStart = newCursorPos;
|
|
13785
|
+
textareaRef.current.selectionEnd = newCursorPos;
|
|
13786
|
+
textareaRef.current.focus();
|
|
13787
|
+
}
|
|
13788
|
+
}, 0);
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
};
|
|
13792
|
+
const charCount = internalValue.length;
|
|
13793
|
+
const charCountClass = maxLength && charCount > maxLength * 0.9 ? "warning" : "";
|
|
13794
|
+
const previewHtml = (0, import_react39.useMemo)(() => {
|
|
13795
|
+
return renderMarkdownPreview(internalValue);
|
|
13796
|
+
}, [internalValue]);
|
|
13797
|
+
return /* @__PURE__ */ import_react39.default.createElement(
|
|
13798
|
+
"div",
|
|
13799
|
+
{
|
|
13800
|
+
ref: containerRef,
|
|
13801
|
+
className: "editor-container editor-markdown-container",
|
|
13802
|
+
style: { minHeight }
|
|
13803
|
+
},
|
|
13804
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13805
|
+
"button",
|
|
13806
|
+
{
|
|
13807
|
+
type: "button",
|
|
13808
|
+
className: "editor-toolbar-btn",
|
|
13809
|
+
onClick: () => {
|
|
13810
|
+
if (textareaRef.current) {
|
|
13811
|
+
const start = textareaRef.current.selectionStart;
|
|
13812
|
+
const end = textareaRef.current.selectionEnd;
|
|
13813
|
+
const selectedText = internalValue.substring(start, end);
|
|
13814
|
+
const newValue = internalValue.substring(0, start) + `**${selectedText || "bold"}**` + internalValue.substring(end);
|
|
13815
|
+
setInternalValue(newValue);
|
|
13816
|
+
onChange(newValue);
|
|
13817
|
+
textareaRef.current.focus();
|
|
13818
|
+
}
|
|
13819
|
+
},
|
|
13820
|
+
title: "Bold (Ctrl+B)",
|
|
13821
|
+
"aria-label": "Bold"
|
|
13822
|
+
},
|
|
13823
|
+
/* @__PURE__ */ import_react39.default.createElement("strong", null, "B")
|
|
13824
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13825
|
+
"button",
|
|
13826
|
+
{
|
|
13827
|
+
type: "button",
|
|
13828
|
+
className: "editor-toolbar-btn",
|
|
13829
|
+
onClick: () => {
|
|
13830
|
+
if (textareaRef.current) {
|
|
13831
|
+
const start = textareaRef.current.selectionStart;
|
|
13832
|
+
const end = textareaRef.current.selectionEnd;
|
|
13833
|
+
const selectedText = internalValue.substring(start, end);
|
|
13834
|
+
const newValue = internalValue.substring(0, start) + `_${selectedText || "italic"}_` + internalValue.substring(end);
|
|
13835
|
+
setInternalValue(newValue);
|
|
13836
|
+
onChange(newValue);
|
|
13837
|
+
textareaRef.current.focus();
|
|
13838
|
+
}
|
|
13839
|
+
},
|
|
13840
|
+
title: "Italic (Ctrl+I)",
|
|
13841
|
+
"aria-label": "Italic"
|
|
13842
|
+
},
|
|
13843
|
+
/* @__PURE__ */ import_react39.default.createElement("em", null, "I")
|
|
13844
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13845
|
+
"button",
|
|
13846
|
+
{
|
|
13847
|
+
type: "button",
|
|
13848
|
+
className: "editor-toolbar-btn",
|
|
13849
|
+
onClick: () => {
|
|
13850
|
+
if (textareaRef.current) {
|
|
13851
|
+
const start = textareaRef.current.selectionStart;
|
|
13852
|
+
const end = textareaRef.current.selectionEnd;
|
|
13853
|
+
const selectedText = internalValue.substring(start, end);
|
|
13854
|
+
const newValue = internalValue.substring(0, start) + `[${selectedText || "link text"}](url)` + internalValue.substring(end);
|
|
13855
|
+
setInternalValue(newValue);
|
|
13856
|
+
onChange(newValue);
|
|
13857
|
+
textareaRef.current.focus();
|
|
13858
|
+
}
|
|
13859
|
+
},
|
|
13860
|
+
title: "Link (Ctrl+K)",
|
|
13861
|
+
"aria-label": "Link"
|
|
13862
|
+
},
|
|
13863
|
+
"\u{1F517}"
|
|
13864
|
+
)), /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-toolbar-group" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13865
|
+
"button",
|
|
13866
|
+
{
|
|
13867
|
+
type: "button",
|
|
13868
|
+
className: `editor-toolbar-btn ${showPreviewPanel ? "active" : ""}`,
|
|
13869
|
+
onClick: () => setShowPreviewPanel(!showPreviewPanel),
|
|
13870
|
+
title: "Toggle preview",
|
|
13871
|
+
"aria-label": "Toggle preview",
|
|
13872
|
+
"aria-pressed": showPreviewPanel
|
|
13873
|
+
},
|
|
13874
|
+
"\u{1F441}"
|
|
13875
|
+
))),
|
|
13876
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: `editor-markdown-content ${showPreviewPanel ? "split" : ""}` }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-editor" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13877
|
+
"textarea",
|
|
13878
|
+
{
|
|
13879
|
+
ref: textareaRef,
|
|
13880
|
+
className: "editor-textarea editor-markdown-textarea",
|
|
13881
|
+
value: internalValue,
|
|
13882
|
+
onChange: handleChange,
|
|
13883
|
+
onKeyDown: handleKeyDown,
|
|
13884
|
+
rows,
|
|
13885
|
+
maxLength,
|
|
13886
|
+
placeholder: "Enter markdown text...",
|
|
13887
|
+
"aria-label": "Markdown editor",
|
|
13888
|
+
style: { maxHeight }
|
|
13889
|
+
}
|
|
13890
|
+
), /* @__PURE__ */ import_react39.default.createElement("div", { className: `editor-markdown-char-count ${charCountClass}` }, charCount, maxLength && ` / ${maxLength}`)), showPreviewPanel && /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-preview" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-preview-label" }, "Preview"), /* @__PURE__ */ import_react39.default.createElement(
|
|
13891
|
+
"div",
|
|
13892
|
+
{
|
|
13893
|
+
className: "editor-markdown-preview-content",
|
|
13894
|
+
dangerouslySetInnerHTML: { __html: previewHtml }
|
|
13895
|
+
}
|
|
13896
|
+
))),
|
|
13897
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-footer" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-hint" }, /* @__PURE__ */ import_react39.default.createElement("kbd", null, "Ctrl+Enter"), " to save \u2022 ", /* @__PURE__ */ import_react39.default.createElement("kbd", null, "Esc"), " to cancel"), /* @__PURE__ */ import_react39.default.createElement("div", { className: "editor-markdown-actions" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
13898
|
+
"button",
|
|
13899
|
+
{
|
|
13900
|
+
type: "button",
|
|
13901
|
+
className: "editor-btn editor-btn-secondary",
|
|
13902
|
+
onClick: onCancel
|
|
13903
|
+
},
|
|
13904
|
+
"Cancel"
|
|
13905
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
13906
|
+
"button",
|
|
13907
|
+
{
|
|
13908
|
+
type: "button",
|
|
13909
|
+
className: "editor-btn editor-btn-primary",
|
|
13910
|
+
onClick: onCommit
|
|
13911
|
+
},
|
|
13912
|
+
"Save"
|
|
13913
|
+
)))
|
|
13914
|
+
);
|
|
13915
|
+
}
|
|
13916
|
+
MarkdownEditor.displayName = "MarkdownEditor";
|
|
13917
|
+
function renderMarkdownPreview(markdown) {
|
|
13918
|
+
if (!markdown) return '<p class="editor-markdown-empty">Nothing to preview</p>';
|
|
13919
|
+
let html = markdown;
|
|
13920
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
13921
|
+
html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
|
|
13922
|
+
html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
|
|
13923
|
+
html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
|
|
13924
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
13925
|
+
html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
|
|
13926
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
13927
|
+
html = html.replace(/_(.+?)_/g, "<em>$1</em>");
|
|
13928
|
+
html = html.replace(/`(.+?)`/g, "<code>$1</code>");
|
|
13929
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
13930
|
+
html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
|
|
13931
|
+
html = html.replace(/^- (.+)$/gim, "<li>$1</li>");
|
|
13932
|
+
html = html.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>");
|
|
13933
|
+
html = html.replace(/\n\n/g, "</p><p>");
|
|
13934
|
+
html = html.replace(/\n/g, "<br>");
|
|
13935
|
+
if (!html.startsWith("<h") && !html.startsWith("<ul") && !html.startsWith("<p")) {
|
|
13936
|
+
html = "<p>" + html + "</p>";
|
|
13937
|
+
}
|
|
13938
|
+
return html;
|
|
13939
|
+
}
|
|
12644
13940
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12645
13941
|
0 && (module.exports = {
|
|
12646
13942
|
AdvancedFilterBuilder,
|
|
@@ -12650,6 +13946,7 @@ var PivotToolbar = ({
|
|
|
12650
13946
|
ColumnFilters,
|
|
12651
13947
|
CurrencyCell,
|
|
12652
13948
|
DataGrid,
|
|
13949
|
+
DateEditor,
|
|
12653
13950
|
DensityToggle,
|
|
12654
13951
|
ExportMenu,
|
|
12655
13952
|
FacetedSearch,
|
|
@@ -12662,12 +13959,16 @@ var PivotToolbar = ({
|
|
|
12662
13959
|
LayoutPersistenceManager,
|
|
12663
13960
|
LayoutPresetsManager,
|
|
12664
13961
|
LocalStorageAdapter,
|
|
13962
|
+
MarkdownEditor,
|
|
12665
13963
|
MarketDataEngine,
|
|
12666
13964
|
MarketDataGrid,
|
|
13965
|
+
MultiSelectEditor,
|
|
13966
|
+
NumericEditor,
|
|
12667
13967
|
PivotToolbar,
|
|
12668
13968
|
PriorityIndicator,
|
|
12669
13969
|
ProgressBar,
|
|
12670
13970
|
Rating,
|
|
13971
|
+
RichSelectEditor,
|
|
12671
13972
|
ServerAdapter,
|
|
12672
13973
|
ServerSideDataSource,
|
|
12673
13974
|
StatusChip,
|
|
@@ -12687,14 +13988,17 @@ var PivotToolbar = ({
|
|
|
12687
13988
|
createMockWebSocket,
|
|
12688
13989
|
createPreset,
|
|
12689
13990
|
darkTheme,
|
|
13991
|
+
debounce,
|
|
12690
13992
|
densityConfigs,
|
|
12691
13993
|
downloadCSV,
|
|
12692
13994
|
expandAllNodes,
|
|
12693
13995
|
exportPivotToCSV,
|
|
12694
13996
|
exportToCSV,
|
|
12695
13997
|
exportToXLSX,
|
|
13998
|
+
filterOptions,
|
|
12696
13999
|
filterTree,
|
|
12697
14000
|
flattenTree,
|
|
14001
|
+
formatNumber,
|
|
12698
14002
|
generateDensityCSS,
|
|
12699
14003
|
generateFilename,
|
|
12700
14004
|
generatePresetId,
|
|
@@ -12712,10 +14016,15 @@ var PivotToolbar = ({
|
|
|
12712
14016
|
isTreeNode,
|
|
12713
14017
|
loadDensityMode,
|
|
12714
14018
|
materialTheme,
|
|
14019
|
+
parseFormattedNumber,
|
|
12715
14020
|
quartzTheme,
|
|
12716
14021
|
saveDensityMode,
|
|
12717
14022
|
themes,
|
|
12718
14023
|
toggleNodeExpansion,
|
|
12719
14024
|
useDensityMode,
|
|
12720
|
-
|
|
14025
|
+
useEditorAutoFocus,
|
|
14026
|
+
useEditorClickOutside,
|
|
14027
|
+
useEditorKeyboardNavigation,
|
|
14028
|
+
useMarketData,
|
|
14029
|
+
usePopupPosition
|
|
12721
14030
|
});
|