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.
@@ -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
- useMarketData: () => useMarketData
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 ? /* @__PURE__ */ import_react6.default.createElement(
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 formatDate = (timestamp) => {
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: ", formatDate(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(
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
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
- useMarketData
14025
+ useEditorAutoFocus,
14026
+ useEditorClickOutside,
14027
+ useEditorKeyboardNavigation,
14028
+ useMarketData,
14029
+ usePopupPosition
12721
14030
  });