@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
@@ -69,6 +69,7 @@ var KTSelect = /** @class */ (function (_super) {
69
69
  _this._dropdownModule = null;
70
70
  _this._loadMoreIndicator = null;
71
71
  _this._typeToSearchBuffer = new utils_1.TypeToSearchBuffer();
72
+ _this._mutationObserver = null;
72
73
  // Search debounce timeout
73
74
  _this._searchDebounceTimeout = null;
74
75
  // Store original options HTML for restoring after search
@@ -163,15 +164,16 @@ var KTSelect = /** @class */ (function (_super) {
163
164
  var optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
164
165
  if (!optionsContainer)
165
166
  return;
167
+ // Clear previous messages
168
+ optionsContainer.innerHTML = '';
166
169
  switch (type) {
167
170
  case 'error':
168
- optionsContainer.innerHTML = templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message }));
171
+ optionsContainer.appendChild(templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message })));
169
172
  break;
170
173
  case 'loading':
171
- optionsContainer.innerHTML = templates_1.defaultTemplates.loading(this._config, message || 'Loading...').outerHTML;
174
+ optionsContainer.appendChild(templates_1.defaultTemplates.loading(this._config, message || 'Loading...'));
172
175
  break;
173
176
  case 'empty':
174
- optionsContainer.innerHTML = '';
175
177
  optionsContainer.appendChild(templates_1.defaultTemplates.empty(this._config));
176
178
  break;
177
179
  }
@@ -343,20 +345,15 @@ var KTSelect = /** @class */ (function (_super) {
343
345
  // Initialize focus manager after dropdown element is created
344
346
  this._focusManager = new utils_1.FocusManager(this._dropdownContentElement, '[data-kt-select-option]', this._config);
345
347
  // Initialize dropdown module after all elements are created
346
- this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config);
348
+ this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config, this);
347
349
  // Update display and set ARIA attributes
348
350
  this._updateDisplayAndAriaAttributes();
349
351
  this.updateSelectedOptionDisplay();
350
352
  this._setAriaAttributes();
351
353
  // Attach event listeners after all modules are initialized
352
354
  this._attachEventListeners();
355
+ this._observeNativeSelect();
353
356
  };
354
- /**
355
- * Initialize options HTML from data
356
- */
357
- // private _initializeOptionsHtml() {
358
- // this._generateOptionsHtml(this._element);
359
- // }
360
357
  /**
361
358
  * Creates the HTML structure for the select component
362
359
  */
@@ -371,7 +368,13 @@ var KTSelect = /** @class */ (function (_super) {
371
368
  wrapperElement.appendChild(displayElement);
372
369
  // Move classes from original select to display element
373
370
  if (this._element.classList.length > 0) {
374
- (_a = displayElement.classList).add.apply(_a, Array.from(this._element.classList));
371
+ // Exclude kt-select class from being added to the wrapper element
372
+ var classes = Array.from(this._element.classList).filter(function (className) { return className !== 'kt-select'; });
373
+ (_a = wrapperElement.classList).add.apply(_a, classes);
374
+ // If element has class kt-select, move it to display element
375
+ if (this._element.classList.contains('kt-select')) {
376
+ displayElement.classList.add('kt-select');
377
+ }
375
378
  this._element.className = '';
376
379
  }
377
380
  // Create an empty dropdown first (without options) using template
@@ -405,7 +408,7 @@ var KTSelect = /** @class */ (function (_super) {
405
408
  wrapperElement.appendChild(dropdownElement);
406
409
  // Insert after the original element
407
410
  this._element.after(wrapperElement);
408
- this._element.style.display = 'none';
411
+ this._element.classList.add('hidden');
409
412
  };
410
413
  /**
411
414
  * Setup all element references after DOM is created
@@ -426,7 +429,6 @@ var KTSelect = /** @class */ (function (_super) {
426
429
  if (!this._searchInputElement) {
427
430
  this._searchInputElement = this._displayElement;
428
431
  }
429
- this._valueDisplayElement = this._wrapperElement.querySelector("[data-kt-select-value]");
430
432
  this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
431
433
  };
432
434
  /**
@@ -437,16 +439,10 @@ var KTSelect = /** @class */ (function (_super) {
437
439
  document.addEventListener('click', this._handleDocumentClick.bind(this));
438
440
  // Dropdown option click events
439
441
  this._eventManager.addListener(this._dropdownContentElement, 'click', this._handleDropdownOptionClick.bind(this));
440
- // Only attach click handler to display element
441
- // this._eventManager.addListener(
442
- // this._wrapperElement,
443
- // 'click',
444
- // this._handleDropdownClick.bind(this),
445
- // );
446
- // Attach centralized keyboard handler
447
- var keyboardTarget = this._searchInputElement || this._wrapperElement;
448
- if (keyboardTarget) {
449
- keyboardTarget.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
442
+ // Attach centralized keyboard handler to the wrapper element.
443
+ // Events from focusable children like _displayElement or _searchInputElement (if present) will bubble up.
444
+ if (this._wrapperElement) {
445
+ this._wrapperElement.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
450
446
  }
451
447
  };
452
448
  /**
@@ -589,37 +585,10 @@ var KTSelect = /** @class */ (function (_super) {
589
585
  * DROPDOWN MANAGEMENT
590
586
  * ========================================================================
591
587
  */
592
- /**
593
- * Toggle dropdown visibility
594
- * @deprecated
595
- */
596
- KTSelect.prototype.toggleDropdown = function () {
597
- if (this._config.disabled) {
598
- if (this._config.debug)
599
- console.log('toggleDropdown: select is disabled, not opening');
600
- return;
601
- }
602
- if (this._config.debug)
603
- console.log('toggleDropdown called');
604
- if (this._dropdownModule) {
605
- // Always use the dropdown module's state to determine whether to open or close
606
- if (this._dropdownModule.isOpen()) {
607
- if (this._config.debug)
608
- console.log('Dropdown is open, closing...');
609
- this.closeDropdown();
610
- }
611
- else {
612
- if (this._config.debug)
613
- console.log('Dropdown is closed, opening...');
614
- this.openDropdown();
615
- }
616
- }
617
- };
618
588
  /**
619
589
  * Open the dropdown
620
590
  */
621
591
  KTSelect.prototype.openDropdown = function () {
622
- var _this = this;
623
592
  if (this._config.disabled) {
624
593
  if (this._config.debug)
625
594
  console.log('openDropdown: select is disabled, not opening');
@@ -647,14 +616,6 @@ var KTSelect = /** @class */ (function (_super) {
647
616
  // Dispatch custom event
648
617
  this._dispatchEvent('show');
649
618
  this._fireEvent('show');
650
- // Focus search input if configured and exists
651
- if (this._config.enableSearch &&
652
- this._config.searchAutofocus &&
653
- this._searchInputElement) {
654
- setTimeout(function () {
655
- _this._searchInputElement.focus();
656
- }, 50);
657
- }
658
619
  // Update ARIA states
659
620
  this._setAriaAttributes();
660
621
  // Focus the first selected option or first option if nothing selected
@@ -675,14 +636,14 @@ var KTSelect = /** @class */ (function (_super) {
675
636
  // Always close by delegating to the dropdown module, which is the source of truth
676
637
  if (this._config.debug)
677
638
  console.log('Closing dropdown via dropdownModule...');
678
- // Clear search input and highlights if the dropdown is closing
639
+ // Clear search input if the dropdown is closing
679
640
  if (this._searchModule && this._searchInputElement) {
680
641
  // Clear search input if configured to do so
681
642
  if (this._config.clearSearchOnClose) {
682
643
  this._searchInputElement.value = '';
683
644
  }
684
- // Always clear the highlights when dropdown closes
685
- this._searchModule.clearSearchHighlights();
645
+ // Clear search input when dropdown closes
646
+ this._searchModule.clearSearch();
686
647
  }
687
648
  // Set our internal flag to match what we're doing
688
649
  this._dropdownIsOpen = false;
@@ -716,10 +677,13 @@ var KTSelect = /** @class */ (function (_super) {
716
677
  var selectedOptions = this.getSelectedOptions();
717
678
  if (selectedOptions.length === 0)
718
679
  return;
719
- // Get the first selected option element
720
- var firstSelectedValue = selectedOptions[0];
721
- // Use the FocusManager to focus on the option
722
- this._focusManager.focusOptionByValue(firstSelectedValue);
680
+ // Iterate through selected options and focus the first one that is visible
681
+ for (var _i = 0, selectedOptions_1 = selectedOptions; _i < selectedOptions_1.length; _i++) {
682
+ var value = selectedOptions_1[_i];
683
+ if (this._focusManager && this._focusManager.focusOptionByValue(value)) {
684
+ break; // Stop after focusing the first found selected and visible option
685
+ }
686
+ }
723
687
  };
724
688
  /**
725
689
  * ========================================================================
@@ -782,38 +746,62 @@ var KTSelect = /** @class */ (function (_super) {
782
746
  */
783
747
  KTSelect.prototype.updateSelectedOptionDisplay = function () {
784
748
  var selectedOptions = this.getSelectedOptions();
785
- // Tag mode: render tags if enabled
786
- if (this._config.tags && this._tagsModule) {
749
+ var tagsEnabled = this._config.tags && this._tagsModule;
750
+ var valueDisplayEl = this.getValueDisplayElement();
751
+ if (tagsEnabled) {
752
+ // Tags module will render tags if selectedOptions > 0, or clear them if selectedOptions === 0.
787
753
  this._tagsModule.updateTagsDisplay(selectedOptions);
788
- return;
754
+ }
755
+ // Guard against valueDisplayEl being null due to template modifications
756
+ if (!valueDisplayEl) {
757
+ if (this._config.debug) {
758
+ console.warn('KTSelect: Value display element is null. Cannot update display or placeholder. Check template for [data-kt-select-value].');
759
+ }
760
+ return; // Nothing to display on if the element is missing
789
761
  }
790
762
  if (typeof this._config.renderSelected === 'function') {
791
- // Use the custom renderSelected function if provided
792
- this._valueDisplayElement.innerHTML = this._config.renderSelected(selectedOptions);
763
+ valueDisplayEl.innerHTML = this._config.renderSelected(selectedOptions);
793
764
  }
794
765
  else {
795
766
  if (selectedOptions.length === 0) {
796
- var placeholder = templates_1.defaultTemplates.placeholder(this._config);
797
- this._valueDisplayElement.replaceChildren(placeholder);
767
+ // No options selected: display placeholder.
768
+ // This runs if tags are off, OR if tags are on but no items are selected (tags module would have cleared tags).
769
+ var placeholderEl = templates_1.defaultTemplates.placeholder(this._config);
770
+ valueDisplayEl.replaceChildren(placeholderEl);
798
771
  }
799
772
  else {
800
- var content = '';
801
- if (this._config.displayTemplate) {
802
- var selectedValues = this.getSelectedOptions();
803
- content = this.renderDisplayTemplateForSelected(selectedValues);
773
+ // Options are selected.
774
+ if (tagsEnabled) {
775
+ // Tags are enabled AND options are selected: tags module has rendered them.
776
+ // Clear valueDisplayEl as tags are the primary display.
777
+ valueDisplayEl.innerHTML = '';
804
778
  }
805
779
  else {
806
- // If no displayTemplate is provided, use the default comma-separated list of selected options
807
- content = this.getSelectedOptionsText();
780
+ // Tags are not enabled AND options are selected: render normal text display.
781
+ var content = '';
782
+ if (this._config.displayTemplate) {
783
+ content = this.renderDisplayTemplateForSelected(this.getSelectedOptions());
784
+ }
785
+ else {
786
+ content = this.getSelectedOptionsText();
787
+ }
788
+ valueDisplayEl.innerHTML = content;
808
789
  }
809
- this._valueDisplayElement.innerHTML = content;
810
790
  }
811
791
  }
812
792
  };
793
+ /**
794
+ * Check if an option was originally disabled in the HTML
795
+ */
796
+ KTSelect.prototype._isOptionOriginallyDisabled = function (value) {
797
+ var originalOption = Array.from(this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
798
+ return originalOption ? originalOption.disabled : false;
799
+ };
813
800
  /**
814
801
  * Update CSS classes for selected options
815
802
  */
816
803
  KTSelect.prototype._updateSelectedOptionClass = function () {
804
+ var _this = this;
817
805
  var allOptions = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
818
806
  var selectedValues = this._state.getSelectedOptions();
819
807
  var maxReached = typeof this._config.maxSelections === 'number' &&
@@ -825,9 +813,11 @@ var KTSelect = /** @class */ (function (_super) {
825
813
  if (!optionValue)
826
814
  return;
827
815
  var isSelected = selectedValues.includes(optionValue);
816
+ var isOriginallyDisabled = _this._isOptionOriginallyDisabled(optionValue);
828
817
  if (isSelected) {
829
818
  option.classList.add('selected');
830
819
  option.setAttribute('aria-selected', 'true');
820
+ // Selected options should not be visually hidden or disabled by maxSelections logic
831
821
  option.classList.remove('hidden');
832
822
  option.classList.remove('disabled');
833
823
  option.removeAttribute('aria-disabled');
@@ -835,7 +825,8 @@ var KTSelect = /** @class */ (function (_super) {
835
825
  else {
836
826
  option.classList.remove('selected');
837
827
  option.setAttribute('aria-selected', 'false');
838
- if (maxReached) {
828
+ // An option should be disabled if it was originally disabled OR if maxSelections is reached
829
+ if (isOriginallyDisabled || maxReached) {
839
830
  option.classList.add('disabled');
840
831
  option.setAttribute('aria-disabled', 'true');
841
832
  }
@@ -895,17 +886,6 @@ var KTSelect = /** @class */ (function (_super) {
895
886
  * EVENT HANDLERS
896
887
  * ========================================================================
897
888
  */
898
- /**
899
- * Handle display element click
900
- * @deprecated
901
- */
902
- KTSelect.prototype._handleDropdownClick = function (event) {
903
- if (this._config.debug)
904
- console.log('Display element clicked', event.target);
905
- event.preventDefault();
906
- event.stopPropagation(); // Prevent event bubbling
907
- this.toggleDropdown();
908
- };
909
889
  /**
910
890
  * Handle click within the dropdown
911
891
  */
@@ -946,6 +926,13 @@ var KTSelect = /** @class */ (function (_super) {
946
926
  }
947
927
  if (this._config.debug)
948
928
  console.log('Option clicked:', optionValue);
929
+ // If in single-select mode and the clicked option is already selected, just close the dropdown.
930
+ if (!this._config.multiple && this._state.isSelected(optionValue)) {
931
+ if (this._config.debug)
932
+ console.log('Single select mode: clicked already selected option. Closing dropdown.');
933
+ this.closeDropdown();
934
+ return;
935
+ }
949
936
  // Use toggleSelection instead of _selectOption to prevent re-rendering
950
937
  this.toggleSelection(optionValue);
951
938
  };
@@ -1009,7 +996,13 @@ var KTSelect = /** @class */ (function (_super) {
1009
996
  * Get value display element
1010
997
  */
1011
998
  KTSelect.prototype.getValueDisplayElement = function () {
1012
- return this._valueDisplayElement;
999
+ return this._displayElement;
1000
+ };
1001
+ /**
1002
+ * Get wrapper element
1003
+ */
1004
+ KTSelect.prototype.getWrapperElement = function () {
1005
+ return this._wrapperElement;
1013
1006
  };
1014
1007
  /**
1015
1008
  * Show all options in the dropdown
@@ -1019,6 +1012,7 @@ var KTSelect = /** @class */ (function (_super) {
1019
1012
  var options = Array.from(this._wrapperElement.querySelectorAll("[data-kt-select-option]"));
1020
1013
  // Show all options by removing the hidden class and any inline styles
1021
1014
  options.forEach(function (option) {
1015
+ var _a;
1022
1016
  // Remove hidden class
1023
1017
  option.classList.remove('hidden');
1024
1018
  // Clean up any existing inline styles for backward compatibility
@@ -1032,7 +1026,7 @@ var KTSelect = /** @class */ (function (_super) {
1032
1026
  }
1033
1027
  else {
1034
1028
  // Otherwise, remove just the display property
1035
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
1029
+ option.setAttribute('style', (_a = styleAttr === null || styleAttr === void 0 ? void 0 : styleAttr.replace(/display:\s*[^;]+;?/gi, '')) === null || _a === void 0 ? void 0 : _a.trim());
1036
1030
  }
1037
1031
  }
1038
1032
  }
@@ -1042,7 +1036,7 @@ var KTSelect = /** @class */ (function (_super) {
1042
1036
  this._searchInputElement.value = '';
1043
1037
  // If we have a search module, clear any search filtering
1044
1038
  if (this._searchModule) {
1045
- this._searchModule.clearSearchHighlights();
1039
+ this._searchModule.clearSearch();
1046
1040
  }
1047
1041
  }
1048
1042
  };
@@ -1071,7 +1065,7 @@ var KTSelect = /** @class */ (function (_super) {
1071
1065
  // Get current selection state
1072
1066
  var isSelected = this._state.isSelected(value);
1073
1067
  if (this._config.debug)
1074
- console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple, ", closeOnSelect: ").concat(this._config.closeOnSelect));
1068
+ console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple));
1075
1069
  // If already selected in single select mode, do nothing (can't deselect in single select)
1076
1070
  if (isSelected && !this._config.multiple) {
1077
1071
  if (this._config.debug)
@@ -1080,9 +1074,9 @@ var KTSelect = /** @class */ (function (_super) {
1080
1074
  }
1081
1075
  if (this._config.debug)
1082
1076
  console.log("Toggling selection for option: ".concat(value, ", currently selected: ").concat(isSelected));
1083
- // Ensure any search highlights are cleared when selection changes
1077
+ // Ensure any search input is cleared when selection changes
1084
1078
  if (this._searchModule) {
1085
- this._searchModule.clearSearchHighlights();
1079
+ this._searchModule.clearSearch();
1086
1080
  }
1087
1081
  // Toggle the selection in the state
1088
1082
  this._state.toggleSelectedOptions(value);
@@ -1105,15 +1099,14 @@ var KTSelect = /** @class */ (function (_super) {
1105
1099
  // Update option classes without re-rendering the dropdown content
1106
1100
  this._updateSelectedOptionClass();
1107
1101
  // For single select mode, always close the dropdown after selection
1108
- // For multiple select mode, only close if closeOnSelect is true
1109
1102
  if (!this._config.multiple) {
1110
1103
  if (this._config.debug)
1111
1104
  console.log('About to call closeDropdown() for single select mode - always close after selection');
1112
1105
  this.closeDropdown();
1113
1106
  }
1114
- else if (this._config.closeOnSelect) {
1107
+ else {
1115
1108
  if (this._config.debug)
1116
- console.log('About to call closeDropdown() for multiple select with closeOnSelect:true');
1109
+ console.log('About to call closeDropdown() for multiple select');
1117
1110
  this.closeDropdown();
1118
1111
  }
1119
1112
  // Dispatch custom change event with additional data
@@ -1213,9 +1206,9 @@ var KTSelect = /** @class */ (function (_super) {
1213
1206
  .then(function () {
1214
1207
  // Update options in the dropdown
1215
1208
  _this._updateSearchResults(items);
1216
- // Refresh the search module's option cache if search is enabled
1217
- if (_this._searchModule && _this._config.enableSearch) {
1218
- _this._searchModule.refreshOptionCache();
1209
+ // Refresh the search module to update focus and cache
1210
+ if (_this._searchModule) {
1211
+ _this._searchModule.refreshAfterSearch();
1219
1212
  }
1220
1213
  })
1221
1214
  .catch(function (error) {
@@ -1312,19 +1305,47 @@ var KTSelect = /** @class */ (function (_super) {
1312
1305
  * Centralized keyboard event handler for all select modes
1313
1306
  */
1314
1307
  KTSelect.prototype._handleKeyboardEvent = function (event) {
1308
+ // If the event target is the search input and the event was already handled (defaultPrevented),
1309
+ // then return early to avoid duplicate processing by this broader handler.
1310
+ if (event.target === this._searchInputElement && event.defaultPrevented) {
1311
+ return;
1312
+ }
1315
1313
  var isOpen = this._dropdownIsOpen;
1316
1314
  var config = this._config;
1317
1315
  var focusManager = this._focusManager;
1318
1316
  var buffer = this._typeToSearchBuffer;
1319
- // Ignore modifier keys
1317
+ // If the event target is the search input, let it handle most typing keys naturally.
1318
+ if (event.target === this._searchInputElement) {
1319
+ // Allow navigation keys like ArrowDown, ArrowUp, Escape, Enter (for search/selection) to be handled by the logic below.
1320
+ // For other keys (characters, space, backspace, delete), let the input field process them.
1321
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' &&
1322
+ event.key !== 'Escape' && event.key !== 'Enter' && event.key !== 'Tab' &&
1323
+ event.key !== 'Home' && event.key !== 'End') {
1324
+ // If it's a character key and we are NOT type-to-searching (because search has focus)
1325
+ // then let the input field handle it for its own value.
1326
+ // The search module's 'input' event will handle filtering based on the input's value.
1327
+ buffer.clear(); // Clear type-to-search buffer when typing in search field
1328
+ return;
1329
+ }
1330
+ // For Enter specifically in search input, we might want to select the focused option or submit search.
1331
+ // This is handled later in the switch.
1332
+ }
1333
+ // Ignore modifier keys (except for specific combinations if added later)
1320
1334
  if (event.altKey || event.ctrlKey || event.metaKey)
1321
1335
  return;
1322
- // Type-to-search: only for single char keys
1323
- if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/)) {
1336
+ // Type-to-search: only for single char keys, when search input does not have focus
1337
+ if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/) && document.activeElement !== this._searchInputElement) {
1324
1338
  buffer.push(event.key);
1325
1339
  var str = buffer.getBuffer();
1326
- focusManager.focusByString(str);
1327
- return;
1340
+ if (isOpen) {
1341
+ focusManager.focusByString(str);
1342
+ }
1343
+ else {
1344
+ // If closed, type-to-search could potentially open and select.
1345
+ // For now, let's assume it only works when open or opens it first.
1346
+ // Or, we could find the matching option and set it directly without opening.
1347
+ }
1348
+ return; // Type-to-search handles the event
1328
1349
  }
1329
1350
  switch (event.key) {
1330
1351
  case 'ArrowDown':
@@ -1358,18 +1379,28 @@ var KTSelect = /** @class */ (function (_super) {
1358
1379
  case 'Enter':
1359
1380
  case ' ': // Space
1360
1381
  if (isOpen) {
1361
- var focused = focusManager.getFocusedOption();
1362
- if (focused) {
1363
- var value = focused.dataset.value;
1364
- if (value) {
1365
- this.toggleSelection(value);
1366
- if (!config.multiple && config.closeOnSelect) {
1367
- this.closeDropdown();
1368
- }
1382
+ var focusedOptionEl = this._focusManager.getFocusedOption();
1383
+ if (focusedOptionEl) {
1384
+ var val = focusedOptionEl.dataset.value;
1385
+ // If single select, and the item is already selected, just close.
1386
+ if (val !== undefined && !this._config.multiple && this._state.isSelected(val)) {
1387
+ if (this._config.debug)
1388
+ console.log('Enter on already selected item in single-select mode. Closing.');
1389
+ this.closeDropdown();
1390
+ event.preventDefault();
1391
+ break;
1369
1392
  }
1370
1393
  }
1371
- // Prevent form submit
1372
- event.preventDefault();
1394
+ // Proceed with selection if not handled above
1395
+ this.selectFocusedOption();
1396
+ // Close dropdown if configured to do so (for new selections)
1397
+ if (!this._config.multiple) {
1398
+ // This will also be true for the case handled above, but closeDropdown is idempotent.
1399
+ // However, the break above prevents this from being reached for that specific case.
1400
+ this.closeDropdown();
1401
+ }
1402
+ event.preventDefault(); // Prevent form submission or other default actions
1403
+ break;
1373
1404
  }
1374
1405
  else {
1375
1406
  this.openDropdown();
@@ -1413,6 +1444,76 @@ var KTSelect = /** @class */ (function (_super) {
1413
1444
  }).filter(Boolean)));
1414
1445
  return contentArray.join(displaySeparator);
1415
1446
  };
1447
+ KTSelect.prototype.getDisplayElement = function () {
1448
+ return this._displayElement;
1449
+ };
1450
+ KTSelect.prototype._observeNativeSelect = function () {
1451
+ var _this = this;
1452
+ if (this._mutationObserver)
1453
+ return; // Prevent double observers
1454
+ this._mutationObserver = new MutationObserver(function (mutations) {
1455
+ var needsRebuild = false;
1456
+ var needsSelectionSync = false;
1457
+ for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
1458
+ var mutation = mutations_1[_i];
1459
+ if (mutation.type === 'childList') {
1460
+ // Option(s) added or removed
1461
+ needsRebuild = true;
1462
+ }
1463
+ else if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
1464
+ if (mutation.attributeName === 'selected') {
1465
+ needsSelectionSync = true;
1466
+ }
1467
+ }
1468
+ }
1469
+ if (needsRebuild) {
1470
+ // Rebuild the custom dropdown options
1471
+ _this._rebuildOptionsFromNative();
1472
+ }
1473
+ if (needsSelectionSync) {
1474
+ _this._syncSelectionFromNative();
1475
+ }
1476
+ });
1477
+ this._mutationObserver.observe(this._element, {
1478
+ childList: true,
1479
+ attributes: true,
1480
+ subtree: true,
1481
+ attributeFilter: ['selected'],
1482
+ });
1483
+ };
1484
+ KTSelect.prototype._rebuildOptionsFromNative = function () {
1485
+ var _this = this;
1486
+ // Remove and rebuild the custom dropdown options from the native select
1487
+ if (this._dropdownContentElement) {
1488
+ var optionsContainer_1 = this._dropdownContentElement.querySelector('[data-kt-select-options]');
1489
+ if (optionsContainer_1) {
1490
+ optionsContainer_1.innerHTML = '';
1491
+ var options = Array.from(this._element.querySelectorAll('option'));
1492
+ options.forEach(function (optionElement) {
1493
+ if (optionElement.value === '' &&
1494
+ optionElement.textContent.trim() === '') {
1495
+ return;
1496
+ }
1497
+ var selectOption = new option_1.KTSelectOption(optionElement, _this._config);
1498
+ var renderedOption = selectOption.render();
1499
+ optionsContainer_1.appendChild(renderedOption);
1500
+ });
1501
+ // Update internal references
1502
+ this._options = this._wrapperElement.querySelectorAll('[data-kt-select-option]');
1503
+ }
1504
+ }
1505
+ // Sync selection after rebuilding
1506
+ this._syncSelectionFromNative();
1507
+ this.updateSelectedOptionDisplay();
1508
+ this._updateSelectedOptionClass();
1509
+ };
1510
+ KTSelect.prototype._syncSelectionFromNative = function () {
1511
+ // Sync internal state from the native select's selected options
1512
+ var selected = Array.from(this._element.querySelectorAll('option:checked')).map(function (opt) { return opt.value; });
1513
+ this._state.setSelectedOptions(this._config.multiple ? selected : selected[0] || '');
1514
+ this.updateSelectedOptionDisplay();
1515
+ this._updateSelectedOptionClass();
1516
+ };
1416
1517
  /**
1417
1518
  * ========================================================================
1418
1519
  * STATIC METHODS