@keenthemes/ktui 1.0.12 → 1.0.14

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.
Files changed (93) hide show
  1. package/dist/ktui.js +738 -700
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +5824 -0
  5. package/examples/select/avatar.html +47 -0
  6. package/examples/select/basic-usage.html +10 -14
  7. package/examples/select/{test.html → combobox-icons_.html} +13 -48
  8. package/examples/select/country.html +43 -0
  9. package/examples/select/description.html +25 -41
  10. package/examples/select/disable-option.html +10 -16
  11. package/examples/select/disable-select.html +7 -6
  12. package/examples/select/icon-multiple.html +23 -31
  13. package/examples/select/icon.html +20 -30
  14. package/examples/select/max-selection.html +8 -9
  15. package/examples/select/modal.html +16 -17
  16. package/examples/select/multiple.html +11 -13
  17. package/examples/select/placeholder.html +9 -12
  18. package/examples/select/search.html +30 -22
  19. package/examples/select/sizes.html +94 -0
  20. package/examples/select/template-customization.html +0 -3
  21. package/lib/cjs/components/component.js +1 -1
  22. package/lib/cjs/components/component.js.map +1 -1
  23. package/lib/cjs/components/datatable/datatable.js +14 -11
  24. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  25. package/lib/cjs/components/select/combobox.js +96 -61
  26. package/lib/cjs/components/select/combobox.js.map +1 -1
  27. package/lib/cjs/components/select/config.js +13 -8
  28. package/lib/cjs/components/select/config.js.map +1 -1
  29. package/lib/cjs/components/select/dropdown.js +32 -96
  30. package/lib/cjs/components/select/dropdown.js.map +1 -1
  31. package/lib/cjs/components/select/option.js +53 -20
  32. package/lib/cjs/components/select/option.js.map +1 -1
  33. package/lib/cjs/components/select/search.js +146 -97
  34. package/lib/cjs/components/select/search.js.map +1 -1
  35. package/lib/cjs/components/select/select.js +219 -118
  36. package/lib/cjs/components/select/select.js.map +1 -1
  37. package/lib/cjs/components/select/tags.js +0 -26
  38. package/lib/cjs/components/select/tags.js.map +1 -1
  39. package/lib/cjs/components/select/templates.js +130 -105
  40. package/lib/cjs/components/select/templates.js.map +1 -1
  41. package/lib/cjs/components/select/utils.js +33 -132
  42. package/lib/cjs/components/select/utils.js.map +1 -1
  43. package/lib/cjs/helpers/dom.js +0 -24
  44. package/lib/cjs/helpers/dom.js.map +1 -1
  45. package/lib/esm/components/component.js +1 -1
  46. package/lib/esm/components/component.js.map +1 -1
  47. package/lib/esm/components/datatable/datatable.js +14 -11
  48. package/lib/esm/components/datatable/datatable.js.map +1 -1
  49. package/lib/esm/components/select/combobox.js +96 -61
  50. package/lib/esm/components/select/combobox.js.map +1 -1
  51. package/lib/esm/components/select/config.js +13 -8
  52. package/lib/esm/components/select/config.js.map +1 -1
  53. package/lib/esm/components/select/dropdown.js +32 -96
  54. package/lib/esm/components/select/dropdown.js.map +1 -1
  55. package/lib/esm/components/select/option.js +53 -20
  56. package/lib/esm/components/select/option.js.map +1 -1
  57. package/lib/esm/components/select/search.js +146 -97
  58. package/lib/esm/components/select/search.js.map +1 -1
  59. package/lib/esm/components/select/select.js +219 -118
  60. package/lib/esm/components/select/select.js.map +1 -1
  61. package/lib/esm/components/select/tags.js +0 -26
  62. package/lib/esm/components/select/tags.js.map +1 -1
  63. package/lib/esm/components/select/templates.js +130 -105
  64. package/lib/esm/components/select/templates.js.map +1 -1
  65. package/lib/esm/components/select/utils.js +32 -130
  66. package/lib/esm/components/select/utils.js.map +1 -1
  67. package/lib/esm/helpers/dom.js +0 -24
  68. package/lib/esm/helpers/dom.js.map +1 -1
  69. package/package.json +9 -6
  70. package/src/components/component.ts +0 -4
  71. package/src/components/datatable/datatable.ts +14 -11
  72. package/src/components/input/input.css +1 -1
  73. package/src/components/scrollable/scrollable.css +9 -5
  74. package/src/components/select/combobox.ts +98 -87
  75. package/src/components/select/config.ts +16 -13
  76. package/src/components/select/dropdown.ts +43 -108
  77. package/src/components/select/option.ts +44 -25
  78. package/src/components/select/search.ts +158 -117
  79. package/src/components/select/select.css +99 -27
  80. package/src/components/select/select.ts +236 -128
  81. package/src/components/select/tags.ts +1 -27
  82. package/src/components/select/templates.ts +191 -132
  83. package/src/components/select/utils.ts +30 -166
  84. package/src/components/toast/toast.css +1 -1
  85. package/src/helpers/dom.ts +0 -30
  86. package/webpack.config.js +6 -1
  87. package/examples/select/combobox-icons.html +0 -58
  88. package/examples/select/icon-description.html +0 -56
  89. /package/examples/select/{combobox.html → combobox_.html} +0 -0
  90. /package/examples/select/{remote-data.html → remote-data_.html} +0 -0
  91. /package/examples/select/{tags-icons.html → tags-icons_.html} +0 -0
  92. /package/examples/select/{tags-selected.html → tags-selected_.html} +0 -0
  93. /package/examples/select/{tags.html → tags_.html} +0 -0
@@ -66,6 +66,7 @@ var KTSelect = /** @class */ (function (_super) {
66
66
  _this._dropdownModule = null;
67
67
  _this._loadMoreIndicator = null;
68
68
  _this._typeToSearchBuffer = new TypeToSearchBuffer();
69
+ _this._mutationObserver = null;
69
70
  // Search debounce timeout
70
71
  _this._searchDebounceTimeout = null;
71
72
  // Store original options HTML for restoring after search
@@ -160,15 +161,16 @@ var KTSelect = /** @class */ (function (_super) {
160
161
  var optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
161
162
  if (!optionsContainer)
162
163
  return;
164
+ // Clear previous messages
165
+ optionsContainer.innerHTML = '';
163
166
  switch (type) {
164
167
  case 'error':
165
- optionsContainer.innerHTML = defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message }));
168
+ optionsContainer.appendChild(defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message })));
166
169
  break;
167
170
  case 'loading':
168
- optionsContainer.innerHTML = defaultTemplates.loading(this._config, message || 'Loading...').outerHTML;
171
+ optionsContainer.appendChild(defaultTemplates.loading(this._config, message || 'Loading...'));
169
172
  break;
170
173
  case 'empty':
171
- optionsContainer.innerHTML = '';
172
174
  optionsContainer.appendChild(defaultTemplates.empty(this._config));
173
175
  break;
174
176
  }
@@ -340,20 +342,15 @@ var KTSelect = /** @class */ (function (_super) {
340
342
  // Initialize focus manager after dropdown element is created
341
343
  this._focusManager = new FocusManager(this._dropdownContentElement, '[data-kt-select-option]', this._config);
342
344
  // Initialize dropdown module after all elements are created
343
- this._dropdownModule = new KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config);
345
+ this._dropdownModule = new KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config, this);
344
346
  // Update display and set ARIA attributes
345
347
  this._updateDisplayAndAriaAttributes();
346
348
  this.updateSelectedOptionDisplay();
347
349
  this._setAriaAttributes();
348
350
  // Attach event listeners after all modules are initialized
349
351
  this._attachEventListeners();
352
+ this._observeNativeSelect();
350
353
  };
351
- /**
352
- * Initialize options HTML from data
353
- */
354
- // private _initializeOptionsHtml() {
355
- // this._generateOptionsHtml(this._element);
356
- // }
357
354
  /**
358
355
  * Creates the HTML structure for the select component
359
356
  */
@@ -368,7 +365,13 @@ var KTSelect = /** @class */ (function (_super) {
368
365
  wrapperElement.appendChild(displayElement);
369
366
  // Move classes from original select to display element
370
367
  if (this._element.classList.length > 0) {
371
- (_a = displayElement.classList).add.apply(_a, Array.from(this._element.classList));
368
+ // Exclude kt-select class from being added to the wrapper element
369
+ var classes = Array.from(this._element.classList).filter(function (className) { return className !== 'kt-select'; });
370
+ (_a = wrapperElement.classList).add.apply(_a, classes);
371
+ // If element has class kt-select, move it to display element
372
+ if (this._element.classList.contains('kt-select')) {
373
+ displayElement.classList.add('kt-select');
374
+ }
372
375
  this._element.className = '';
373
376
  }
374
377
  // Create an empty dropdown first (without options) using template
@@ -402,7 +405,7 @@ var KTSelect = /** @class */ (function (_super) {
402
405
  wrapperElement.appendChild(dropdownElement);
403
406
  // Insert after the original element
404
407
  this._element.after(wrapperElement);
405
- this._element.style.display = 'none';
408
+ this._element.classList.add('hidden');
406
409
  };
407
410
  /**
408
411
  * Setup all element references after DOM is created
@@ -423,7 +426,6 @@ var KTSelect = /** @class */ (function (_super) {
423
426
  if (!this._searchInputElement) {
424
427
  this._searchInputElement = this._displayElement;
425
428
  }
426
- this._valueDisplayElement = this._wrapperElement.querySelector("[data-kt-select-value]");
427
429
  this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
428
430
  };
429
431
  /**
@@ -434,16 +436,10 @@ var KTSelect = /** @class */ (function (_super) {
434
436
  document.addEventListener('click', this._handleDocumentClick.bind(this));
435
437
  // Dropdown option click events
436
438
  this._eventManager.addListener(this._dropdownContentElement, 'click', this._handleDropdownOptionClick.bind(this));
437
- // Only attach click handler to display element
438
- // this._eventManager.addListener(
439
- // this._wrapperElement,
440
- // 'click',
441
- // this._handleDropdownClick.bind(this),
442
- // );
443
- // Attach centralized keyboard handler
444
- var keyboardTarget = this._searchInputElement || this._wrapperElement;
445
- if (keyboardTarget) {
446
- keyboardTarget.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
439
+ // Attach centralized keyboard handler to the wrapper element.
440
+ // Events from focusable children like _displayElement or _searchInputElement (if present) will bubble up.
441
+ if (this._wrapperElement) {
442
+ this._wrapperElement.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
447
443
  }
448
444
  };
449
445
  /**
@@ -586,37 +582,10 @@ var KTSelect = /** @class */ (function (_super) {
586
582
  * DROPDOWN MANAGEMENT
587
583
  * ========================================================================
588
584
  */
589
- /**
590
- * Toggle dropdown visibility
591
- * @deprecated
592
- */
593
- KTSelect.prototype.toggleDropdown = function () {
594
- if (this._config.disabled) {
595
- if (this._config.debug)
596
- console.log('toggleDropdown: select is disabled, not opening');
597
- return;
598
- }
599
- if (this._config.debug)
600
- console.log('toggleDropdown called');
601
- if (this._dropdownModule) {
602
- // Always use the dropdown module's state to determine whether to open or close
603
- if (this._dropdownModule.isOpen()) {
604
- if (this._config.debug)
605
- console.log('Dropdown is open, closing...');
606
- this.closeDropdown();
607
- }
608
- else {
609
- if (this._config.debug)
610
- console.log('Dropdown is closed, opening...');
611
- this.openDropdown();
612
- }
613
- }
614
- };
615
585
  /**
616
586
  * Open the dropdown
617
587
  */
618
588
  KTSelect.prototype.openDropdown = function () {
619
- var _this = this;
620
589
  if (this._config.disabled) {
621
590
  if (this._config.debug)
622
591
  console.log('openDropdown: select is disabled, not opening');
@@ -644,14 +613,6 @@ var KTSelect = /** @class */ (function (_super) {
644
613
  // Dispatch custom event
645
614
  this._dispatchEvent('show');
646
615
  this._fireEvent('show');
647
- // Focus search input if configured and exists
648
- if (this._config.enableSearch &&
649
- this._config.searchAutofocus &&
650
- this._searchInputElement) {
651
- setTimeout(function () {
652
- _this._searchInputElement.focus();
653
- }, 50);
654
- }
655
616
  // Update ARIA states
656
617
  this._setAriaAttributes();
657
618
  // Focus the first selected option or first option if nothing selected
@@ -672,14 +633,14 @@ var KTSelect = /** @class */ (function (_super) {
672
633
  // Always close by delegating to the dropdown module, which is the source of truth
673
634
  if (this._config.debug)
674
635
  console.log('Closing dropdown via dropdownModule...');
675
- // Clear search input and highlights if the dropdown is closing
636
+ // Clear search input if the dropdown is closing
676
637
  if (this._searchModule && this._searchInputElement) {
677
638
  // Clear search input if configured to do so
678
639
  if (this._config.clearSearchOnClose) {
679
640
  this._searchInputElement.value = '';
680
641
  }
681
- // Always clear the highlights when dropdown closes
682
- this._searchModule.clearSearchHighlights();
642
+ // Clear search input when dropdown closes
643
+ this._searchModule.clearSearch();
683
644
  }
684
645
  // Set our internal flag to match what we're doing
685
646
  this._dropdownIsOpen = false;
@@ -713,10 +674,13 @@ var KTSelect = /** @class */ (function (_super) {
713
674
  var selectedOptions = this.getSelectedOptions();
714
675
  if (selectedOptions.length === 0)
715
676
  return;
716
- // Get the first selected option element
717
- var firstSelectedValue = selectedOptions[0];
718
- // Use the FocusManager to focus on the option
719
- this._focusManager.focusOptionByValue(firstSelectedValue);
677
+ // Iterate through selected options and focus the first one that is visible
678
+ for (var _i = 0, selectedOptions_1 = selectedOptions; _i < selectedOptions_1.length; _i++) {
679
+ var value = selectedOptions_1[_i];
680
+ if (this._focusManager && this._focusManager.focusOptionByValue(value)) {
681
+ break; // Stop after focusing the first found selected and visible option
682
+ }
683
+ }
720
684
  };
721
685
  /**
722
686
  * ========================================================================
@@ -779,38 +743,62 @@ var KTSelect = /** @class */ (function (_super) {
779
743
  */
780
744
  KTSelect.prototype.updateSelectedOptionDisplay = function () {
781
745
  var selectedOptions = this.getSelectedOptions();
782
- // Tag mode: render tags if enabled
783
- if (this._config.tags && this._tagsModule) {
746
+ var tagsEnabled = this._config.tags && this._tagsModule;
747
+ var valueDisplayEl = this.getValueDisplayElement();
748
+ if (tagsEnabled) {
749
+ // Tags module will render tags if selectedOptions > 0, or clear them if selectedOptions === 0.
784
750
  this._tagsModule.updateTagsDisplay(selectedOptions);
785
- return;
751
+ }
752
+ // Guard against valueDisplayEl being null due to template modifications
753
+ if (!valueDisplayEl) {
754
+ if (this._config.debug) {
755
+ console.warn('KTSelect: Value display element is null. Cannot update display or placeholder. Check template for [data-kt-select-value].');
756
+ }
757
+ return; // Nothing to display on if the element is missing
786
758
  }
787
759
  if (typeof this._config.renderSelected === 'function') {
788
- // Use the custom renderSelected function if provided
789
- this._valueDisplayElement.innerHTML = this._config.renderSelected(selectedOptions);
760
+ valueDisplayEl.innerHTML = this._config.renderSelected(selectedOptions);
790
761
  }
791
762
  else {
792
763
  if (selectedOptions.length === 0) {
793
- var placeholder = defaultTemplates.placeholder(this._config);
794
- this._valueDisplayElement.replaceChildren(placeholder);
764
+ // No options selected: display placeholder.
765
+ // This runs if tags are off, OR if tags are on but no items are selected (tags module would have cleared tags).
766
+ var placeholderEl = defaultTemplates.placeholder(this._config);
767
+ valueDisplayEl.replaceChildren(placeholderEl);
795
768
  }
796
769
  else {
797
- var content = '';
798
- if (this._config.displayTemplate) {
799
- var selectedValues = this.getSelectedOptions();
800
- content = this.renderDisplayTemplateForSelected(selectedValues);
770
+ // Options are selected.
771
+ if (tagsEnabled) {
772
+ // Tags are enabled AND options are selected: tags module has rendered them.
773
+ // Clear valueDisplayEl as tags are the primary display.
774
+ valueDisplayEl.innerHTML = '';
801
775
  }
802
776
  else {
803
- // If no displayTemplate is provided, use the default comma-separated list of selected options
804
- content = this.getSelectedOptionsText();
777
+ // Tags are not enabled AND options are selected: render normal text display.
778
+ var content = '';
779
+ if (this._config.displayTemplate) {
780
+ content = this.renderDisplayTemplateForSelected(this.getSelectedOptions());
781
+ }
782
+ else {
783
+ content = this.getSelectedOptionsText();
784
+ }
785
+ valueDisplayEl.innerHTML = content;
805
786
  }
806
- this._valueDisplayElement.innerHTML = content;
807
787
  }
808
788
  }
809
789
  };
790
+ /**
791
+ * Check if an option was originally disabled in the HTML
792
+ */
793
+ KTSelect.prototype._isOptionOriginallyDisabled = function (value) {
794
+ var originalOption = Array.from(this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
795
+ return originalOption ? originalOption.disabled : false;
796
+ };
810
797
  /**
811
798
  * Update CSS classes for selected options
812
799
  */
813
800
  KTSelect.prototype._updateSelectedOptionClass = function () {
801
+ var _this = this;
814
802
  var allOptions = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
815
803
  var selectedValues = this._state.getSelectedOptions();
816
804
  var maxReached = typeof this._config.maxSelections === 'number' &&
@@ -822,9 +810,11 @@ var KTSelect = /** @class */ (function (_super) {
822
810
  if (!optionValue)
823
811
  return;
824
812
  var isSelected = selectedValues.includes(optionValue);
813
+ var isOriginallyDisabled = _this._isOptionOriginallyDisabled(optionValue);
825
814
  if (isSelected) {
826
815
  option.classList.add('selected');
827
816
  option.setAttribute('aria-selected', 'true');
817
+ // Selected options should not be visually hidden or disabled by maxSelections logic
828
818
  option.classList.remove('hidden');
829
819
  option.classList.remove('disabled');
830
820
  option.removeAttribute('aria-disabled');
@@ -832,7 +822,8 @@ var KTSelect = /** @class */ (function (_super) {
832
822
  else {
833
823
  option.classList.remove('selected');
834
824
  option.setAttribute('aria-selected', 'false');
835
- if (maxReached) {
825
+ // An option should be disabled if it was originally disabled OR if maxSelections is reached
826
+ if (isOriginallyDisabled || maxReached) {
836
827
  option.classList.add('disabled');
837
828
  option.setAttribute('aria-disabled', 'true');
838
829
  }
@@ -892,17 +883,6 @@ var KTSelect = /** @class */ (function (_super) {
892
883
  * EVENT HANDLERS
893
884
  * ========================================================================
894
885
  */
895
- /**
896
- * Handle display element click
897
- * @deprecated
898
- */
899
- KTSelect.prototype._handleDropdownClick = function (event) {
900
- if (this._config.debug)
901
- console.log('Display element clicked', event.target);
902
- event.preventDefault();
903
- event.stopPropagation(); // Prevent event bubbling
904
- this.toggleDropdown();
905
- };
906
886
  /**
907
887
  * Handle click within the dropdown
908
888
  */
@@ -943,6 +923,13 @@ var KTSelect = /** @class */ (function (_super) {
943
923
  }
944
924
  if (this._config.debug)
945
925
  console.log('Option clicked:', optionValue);
926
+ // If in single-select mode and the clicked option is already selected, just close the dropdown.
927
+ if (!this._config.multiple && this._state.isSelected(optionValue)) {
928
+ if (this._config.debug)
929
+ console.log('Single select mode: clicked already selected option. Closing dropdown.');
930
+ this.closeDropdown();
931
+ return;
932
+ }
946
933
  // Use toggleSelection instead of _selectOption to prevent re-rendering
947
934
  this.toggleSelection(optionValue);
948
935
  };
@@ -1006,7 +993,13 @@ var KTSelect = /** @class */ (function (_super) {
1006
993
  * Get value display element
1007
994
  */
1008
995
  KTSelect.prototype.getValueDisplayElement = function () {
1009
- return this._valueDisplayElement;
996
+ return this._displayElement;
997
+ };
998
+ /**
999
+ * Get wrapper element
1000
+ */
1001
+ KTSelect.prototype.getWrapperElement = function () {
1002
+ return this._wrapperElement;
1010
1003
  };
1011
1004
  /**
1012
1005
  * Show all options in the dropdown
@@ -1016,6 +1009,7 @@ var KTSelect = /** @class */ (function (_super) {
1016
1009
  var options = Array.from(this._wrapperElement.querySelectorAll("[data-kt-select-option]"));
1017
1010
  // Show all options by removing the hidden class and any inline styles
1018
1011
  options.forEach(function (option) {
1012
+ var _a;
1019
1013
  // Remove hidden class
1020
1014
  option.classList.remove('hidden');
1021
1015
  // Clean up any existing inline styles for backward compatibility
@@ -1029,7 +1023,7 @@ var KTSelect = /** @class */ (function (_super) {
1029
1023
  }
1030
1024
  else {
1031
1025
  // Otherwise, remove just the display property
1032
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
1026
+ option.setAttribute('style', (_a = styleAttr === null || styleAttr === void 0 ? void 0 : styleAttr.replace(/display:\s*[^;]+;?/gi, '')) === null || _a === void 0 ? void 0 : _a.trim());
1033
1027
  }
1034
1028
  }
1035
1029
  }
@@ -1039,7 +1033,7 @@ var KTSelect = /** @class */ (function (_super) {
1039
1033
  this._searchInputElement.value = '';
1040
1034
  // If we have a search module, clear any search filtering
1041
1035
  if (this._searchModule) {
1042
- this._searchModule.clearSearchHighlights();
1036
+ this._searchModule.clearSearch();
1043
1037
  }
1044
1038
  }
1045
1039
  };
@@ -1068,7 +1062,7 @@ var KTSelect = /** @class */ (function (_super) {
1068
1062
  // Get current selection state
1069
1063
  var isSelected = this._state.isSelected(value);
1070
1064
  if (this._config.debug)
1071
- console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple, ", closeOnSelect: ").concat(this._config.closeOnSelect));
1065
+ console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple));
1072
1066
  // If already selected in single select mode, do nothing (can't deselect in single select)
1073
1067
  if (isSelected && !this._config.multiple) {
1074
1068
  if (this._config.debug)
@@ -1077,9 +1071,9 @@ var KTSelect = /** @class */ (function (_super) {
1077
1071
  }
1078
1072
  if (this._config.debug)
1079
1073
  console.log("Toggling selection for option: ".concat(value, ", currently selected: ").concat(isSelected));
1080
- // Ensure any search highlights are cleared when selection changes
1074
+ // Ensure any search input is cleared when selection changes
1081
1075
  if (this._searchModule) {
1082
- this._searchModule.clearSearchHighlights();
1076
+ this._searchModule.clearSearch();
1083
1077
  }
1084
1078
  // Toggle the selection in the state
1085
1079
  this._state.toggleSelectedOptions(value);
@@ -1102,15 +1096,14 @@ var KTSelect = /** @class */ (function (_super) {
1102
1096
  // Update option classes without re-rendering the dropdown content
1103
1097
  this._updateSelectedOptionClass();
1104
1098
  // For single select mode, always close the dropdown after selection
1105
- // For multiple select mode, only close if closeOnSelect is true
1106
1099
  if (!this._config.multiple) {
1107
1100
  if (this._config.debug)
1108
1101
  console.log('About to call closeDropdown() for single select mode - always close after selection');
1109
1102
  this.closeDropdown();
1110
1103
  }
1111
- else if (this._config.closeOnSelect) {
1104
+ else {
1112
1105
  if (this._config.debug)
1113
- console.log('About to call closeDropdown() for multiple select with closeOnSelect:true');
1106
+ console.log('About to call closeDropdown() for multiple select');
1114
1107
  this.closeDropdown();
1115
1108
  }
1116
1109
  // Dispatch custom change event with additional data
@@ -1210,9 +1203,9 @@ var KTSelect = /** @class */ (function (_super) {
1210
1203
  .then(function () {
1211
1204
  // Update options in the dropdown
1212
1205
  _this._updateSearchResults(items);
1213
- // Refresh the search module's option cache if search is enabled
1214
- if (_this._searchModule && _this._config.enableSearch) {
1215
- _this._searchModule.refreshOptionCache();
1206
+ // Refresh the search module to update focus and cache
1207
+ if (_this._searchModule) {
1208
+ _this._searchModule.refreshAfterSearch();
1216
1209
  }
1217
1210
  })
1218
1211
  .catch(function (error) {
@@ -1309,19 +1302,47 @@ var KTSelect = /** @class */ (function (_super) {
1309
1302
  * Centralized keyboard event handler for all select modes
1310
1303
  */
1311
1304
  KTSelect.prototype._handleKeyboardEvent = function (event) {
1305
+ // If the event target is the search input and the event was already handled (defaultPrevented),
1306
+ // then return early to avoid duplicate processing by this broader handler.
1307
+ if (event.target === this._searchInputElement && event.defaultPrevented) {
1308
+ return;
1309
+ }
1312
1310
  var isOpen = this._dropdownIsOpen;
1313
1311
  var config = this._config;
1314
1312
  var focusManager = this._focusManager;
1315
1313
  var buffer = this._typeToSearchBuffer;
1316
- // Ignore modifier keys
1314
+ // If the event target is the search input, let it handle most typing keys naturally.
1315
+ if (event.target === this._searchInputElement) {
1316
+ // Allow navigation keys like ArrowDown, ArrowUp, Escape, Enter (for search/selection) to be handled by the logic below.
1317
+ // For other keys (characters, space, backspace, delete), let the input field process them.
1318
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' &&
1319
+ event.key !== 'Escape' && event.key !== 'Enter' && event.key !== 'Tab' &&
1320
+ event.key !== 'Home' && event.key !== 'End') {
1321
+ // If it's a character key and we are NOT type-to-searching (because search has focus)
1322
+ // then let the input field handle it for its own value.
1323
+ // The search module's 'input' event will handle filtering based on the input's value.
1324
+ buffer.clear(); // Clear type-to-search buffer when typing in search field
1325
+ return;
1326
+ }
1327
+ // For Enter specifically in search input, we might want to select the focused option or submit search.
1328
+ // This is handled later in the switch.
1329
+ }
1330
+ // Ignore modifier keys (except for specific combinations if added later)
1317
1331
  if (event.altKey || event.ctrlKey || event.metaKey)
1318
1332
  return;
1319
- // Type-to-search: only for single char keys
1320
- if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/)) {
1333
+ // Type-to-search: only for single char keys, when search input does not have focus
1334
+ if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/) && document.activeElement !== this._searchInputElement) {
1321
1335
  buffer.push(event.key);
1322
1336
  var str = buffer.getBuffer();
1323
- focusManager.focusByString(str);
1324
- return;
1337
+ if (isOpen) {
1338
+ focusManager.focusByString(str);
1339
+ }
1340
+ else {
1341
+ // If closed, type-to-search could potentially open and select.
1342
+ // For now, let's assume it only works when open or opens it first.
1343
+ // Or, we could find the matching option and set it directly without opening.
1344
+ }
1345
+ return; // Type-to-search handles the event
1325
1346
  }
1326
1347
  switch (event.key) {
1327
1348
  case 'ArrowDown':
@@ -1355,18 +1376,28 @@ var KTSelect = /** @class */ (function (_super) {
1355
1376
  case 'Enter':
1356
1377
  case ' ': // Space
1357
1378
  if (isOpen) {
1358
- var focused = focusManager.getFocusedOption();
1359
- if (focused) {
1360
- var value = focused.dataset.value;
1361
- if (value) {
1362
- this.toggleSelection(value);
1363
- if (!config.multiple && config.closeOnSelect) {
1364
- this.closeDropdown();
1365
- }
1379
+ var focusedOptionEl = this._focusManager.getFocusedOption();
1380
+ if (focusedOptionEl) {
1381
+ var val = focusedOptionEl.dataset.value;
1382
+ // If single select, and the item is already selected, just close.
1383
+ if (val !== undefined && !this._config.multiple && this._state.isSelected(val)) {
1384
+ if (this._config.debug)
1385
+ console.log('Enter on already selected item in single-select mode. Closing.');
1386
+ this.closeDropdown();
1387
+ event.preventDefault();
1388
+ break;
1366
1389
  }
1367
1390
  }
1368
- // Prevent form submit
1369
- event.preventDefault();
1391
+ // Proceed with selection if not handled above
1392
+ this.selectFocusedOption();
1393
+ // Close dropdown if configured to do so (for new selections)
1394
+ if (!this._config.multiple) {
1395
+ // This will also be true for the case handled above, but closeDropdown is idempotent.
1396
+ // However, the break above prevents this from being reached for that specific case.
1397
+ this.closeDropdown();
1398
+ }
1399
+ event.preventDefault(); // Prevent form submission or other default actions
1400
+ break;
1370
1401
  }
1371
1402
  else {
1372
1403
  this.openDropdown();
@@ -1410,6 +1441,76 @@ var KTSelect = /** @class */ (function (_super) {
1410
1441
  }).filter(Boolean)));
1411
1442
  return contentArray.join(displaySeparator);
1412
1443
  };
1444
+ KTSelect.prototype.getDisplayElement = function () {
1445
+ return this._displayElement;
1446
+ };
1447
+ KTSelect.prototype._observeNativeSelect = function () {
1448
+ var _this = this;
1449
+ if (this._mutationObserver)
1450
+ return; // Prevent double observers
1451
+ this._mutationObserver = new MutationObserver(function (mutations) {
1452
+ var needsRebuild = false;
1453
+ var needsSelectionSync = false;
1454
+ for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
1455
+ var mutation = mutations_1[_i];
1456
+ if (mutation.type === 'childList') {
1457
+ // Option(s) added or removed
1458
+ needsRebuild = true;
1459
+ }
1460
+ else if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
1461
+ if (mutation.attributeName === 'selected') {
1462
+ needsSelectionSync = true;
1463
+ }
1464
+ }
1465
+ }
1466
+ if (needsRebuild) {
1467
+ // Rebuild the custom dropdown options
1468
+ _this._rebuildOptionsFromNative();
1469
+ }
1470
+ if (needsSelectionSync) {
1471
+ _this._syncSelectionFromNative();
1472
+ }
1473
+ });
1474
+ this._mutationObserver.observe(this._element, {
1475
+ childList: true,
1476
+ attributes: true,
1477
+ subtree: true,
1478
+ attributeFilter: ['selected'],
1479
+ });
1480
+ };
1481
+ KTSelect.prototype._rebuildOptionsFromNative = function () {
1482
+ var _this = this;
1483
+ // Remove and rebuild the custom dropdown options from the native select
1484
+ if (this._dropdownContentElement) {
1485
+ var optionsContainer_1 = this._dropdownContentElement.querySelector('[data-kt-select-options]');
1486
+ if (optionsContainer_1) {
1487
+ optionsContainer_1.innerHTML = '';
1488
+ var options = Array.from(this._element.querySelectorAll('option'));
1489
+ options.forEach(function (optionElement) {
1490
+ if (optionElement.value === '' &&
1491
+ optionElement.textContent.trim() === '') {
1492
+ return;
1493
+ }
1494
+ var selectOption = new KTSelectOption(optionElement, _this._config);
1495
+ var renderedOption = selectOption.render();
1496
+ optionsContainer_1.appendChild(renderedOption);
1497
+ });
1498
+ // Update internal references
1499
+ this._options = this._wrapperElement.querySelectorAll('[data-kt-select-option]');
1500
+ }
1501
+ }
1502
+ // Sync selection after rebuilding
1503
+ this._syncSelectionFromNative();
1504
+ this.updateSelectedOptionDisplay();
1505
+ this._updateSelectedOptionClass();
1506
+ };
1507
+ KTSelect.prototype._syncSelectionFromNative = function () {
1508
+ // Sync internal state from the native select's selected options
1509
+ var selected = Array.from(this._element.querySelectorAll('option:checked')).map(function (opt) { return opt.value; });
1510
+ this._state.setSelectedOptions(this._config.multiple ? selected : selected[0] || '');
1511
+ this.updateSelectedOptionDisplay();
1512
+ this._updateSelectedOptionClass();
1513
+ };
1413
1514
  /**
1414
1515
  * ========================================================================
1415
1516
  * STATIC METHODS