@keenthemes/ktui 1.0.12 → 1.0.13

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 (92) hide show
  1. package/dist/ktui.js +668 -685
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +5822 -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 +19 -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 +1 -1
  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 +1 -3
  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 +174 -120
  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 -103
  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 +1 -1
  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 +1 -3
  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 +174 -120
  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 -103
  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 +1 -1
  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 +99 -88
  75. package/src/components/select/config.ts +2 -8
  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 +97 -27
  80. package/src/components/select/select.ts +181 -127
  81. package/src/components/select/tags.ts +1 -27
  82. package/src/components/select/templates.ts +194 -131
  83. package/src/components/select/utils.ts +30 -166
  84. package/src/helpers/dom.ts +0 -30
  85. package/webpack.config.js +6 -1
  86. package/examples/select/combobox-icons.html +0 -58
  87. package/examples/select/icon-description.html +0 -56
  88. /package/examples/select/{combobox.html → combobox_.html} +0 -0
  89. /package/examples/select/{remote-data.html → remote-data_.html} +0 -0
  90. /package/examples/select/{tags-icons.html → tags-icons_.html} +0 -0
  91. /package/examples/select/{tags-selected.html → tags-selected_.html} +0 -0
  92. /package/examples/select/{tags.html → tags_.html} +0 -0
package/dist/ktui.js CHANGED
@@ -2453,7 +2453,7 @@ var KTComponent = /** @class */ (function () {
2453
2453
  if (config === void 0) { config = {}; }
2454
2454
  if (!this._element)
2455
2455
  return;
2456
- this._config = __assign(__assign(__assign(__assign(__assign({}, this._defaultConfig), this._getGlobalConfig()), dom_1.default.getDataAttributes(this._element, this._dataOptionPrefix + this._name)), dom_1.default.getDataAttributesByJson(this._element, this._dataOptionPrefix + this._name + '-config')), config);
2456
+ this._config = __assign(__assign(__assign(__assign({}, this._defaultConfig), this._getGlobalConfig()), dom_1.default.getDataAttributes(this._element, this._dataOptionPrefix + this._name)), config);
2457
2457
  };
2458
2458
  KTComponent.prototype.dispose = function () {
2459
2459
  if (!this._element)
@@ -5653,6 +5653,17 @@ var __extends = (this && this.__extends) || (function () {
5653
5653
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
5654
5654
  };
5655
5655
  })();
5656
+ var __assign = (this && this.__assign) || function () {
5657
+ __assign = Object.assign || function(t) {
5658
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5659
+ s = arguments[i];
5660
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
5661
+ t[p] = s[p];
5662
+ }
5663
+ return t;
5664
+ };
5665
+ return __assign.apply(this, arguments);
5666
+ };
5656
5667
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5657
5668
  exports.KTSelectOption = void 0;
5658
5669
  var component_1 = __webpack_require__(2658);
@@ -5665,39 +5676,61 @@ var KTSelectOption = /** @class */ (function (_super) {
5665
5676
  _this._dataOptionPrefix = 'kt-'; // Use 'kt-' prefix to support data-kt-select-option attributes
5666
5677
  // Always initialize a new option instance
5667
5678
  _this._init(element);
5668
- _this._buildConfig();
5669
5679
  _this._globalConfig = config;
5680
+ _this._buildConfig();
5670
5681
  // Clean the config
5671
5682
  _this._config = _this._config[''] || {};
5672
5683
  // Add the option config to the global config
5673
- _this._globalConfig.optionsConfig = _this._globalConfig.optionsConfig || {};
5674
- _this._globalConfig.optionsConfig[element.value] = _this._config;
5684
+ // Ensure optionsConfig is initialized
5685
+ if (_this._globalConfig) {
5686
+ _this._globalConfig.optionsConfig = _this._globalConfig.optionsConfig || {};
5687
+ _this._globalConfig.optionsConfig[element.value] = _this._config;
5688
+ }
5689
+ else {
5690
+ // Handle case where _globalConfig might be undefined, though constructor expects it.
5691
+ // This might indicate a need to ensure config is always passed or has a default.
5692
+ console.warn('KTSelectOption: _globalConfig is undefined during constructor.');
5693
+ }
5675
5694
  // Don't store in KTData to avoid Singleton pattern issues
5676
5695
  // Each option should be a unique instance
5677
5696
  element.instance = _this;
5678
5697
  return _this;
5679
5698
  }
5699
+ Object.defineProperty(KTSelectOption.prototype, "id", {
5700
+ get: function () {
5701
+ return this.getHTMLOptionElement().value;
5702
+ },
5703
+ enumerable: false,
5704
+ configurable: true
5705
+ });
5706
+ Object.defineProperty(KTSelectOption.prototype, "title", {
5707
+ get: function () {
5708
+ return this.getHTMLOptionElement().textContent || '';
5709
+ },
5710
+ enumerable: false,
5711
+ configurable: true
5712
+ });
5680
5713
  KTSelectOption.prototype.getHTMLOptionElement = function () {
5681
5714
  return this._element;
5682
5715
  };
5716
+ /**
5717
+ * Gathers all necessary data for rendering this option,
5718
+ * including standard HTML attributes and custom data-kt-* attributes.
5719
+ */
5720
+ KTSelectOption.prototype.getOptionDataForTemplate = function () {
5721
+ var el = this.getHTMLOptionElement();
5722
+ var text = el.textContent || '';
5723
+ return __assign(__assign({}, this._config), {
5724
+ // Standard HTMLOptionElement properties
5725
+ value: el.value, text: text, selected: el.selected, disabled: el.disabled,
5726
+ // Provide 'content' for convenience in templates, defaulting to text.
5727
+ // User's optionTemplate can then use {{content}} or specific fields like {{text}}, {{icon}}, etc.
5728
+ content: text });
5729
+ };
5683
5730
  KTSelectOption.prototype.render = function () {
5684
- var _this = this;
5685
- var optionElement = this.getHTMLOptionElement();
5686
- // Get the original template
5687
- var originalTemplate = this._globalConfig.optionTemplate;
5688
- if (this._globalConfig.optionTemplate) {
5689
- // Replace all {{varname}} in option.innerHTML with values from _config
5690
- Object.entries(this._config || {}).forEach(function (_a) {
5691
- var key = _a[0], value = _a[1];
5692
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
5693
- _this._globalConfig.optionTemplate = _this._globalConfig.optionTemplate.replace(new RegExp("{{".concat(key, "}}"), 'g'), String(value));
5694
- }
5695
- });
5696
- }
5697
- var template = templates_1.defaultTemplates.option(optionElement, this._globalConfig);
5698
- // Restore the original template
5699
- this._globalConfig.optionTemplate = originalTemplate;
5700
- return template;
5731
+ // 'this' is the KTSelectOption instance.
5732
+ // defaultTemplates.option will handle using this instance's data along with _globalConfig.
5733
+ return templates_1.defaultTemplates.option(this, this._globalConfig);
5701
5734
  };
5702
5735
  return KTSelectOption;
5703
5736
  }(component_1.default));
@@ -6515,52 +6548,35 @@ Object.defineProperty(exports, "KTScrollspy", ({ enumerable: true, get: function
6515
6548
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6516
6549
  exports.KTSelectCombobox = void 0;
6517
6550
  var utils_1 = __webpack_require__(9011);
6551
+ var templates_1 = __webpack_require__(9069);
6518
6552
  /**
6519
6553
  * KTSelectCombobox - Handles combobox-specific functionality for KTSelect
6520
6554
  */
6521
6555
  var KTSelectCombobox = /** @class */ (function () {
6522
6556
  function KTSelectCombobox(select) {
6523
6557
  var _this = this;
6524
- var _a;
6525
6558
  this._select = select;
6526
6559
  this._config = select.getConfig();
6527
- // Get the display element (could be the input directly or a parent div)
6528
- var displayElement = select.getValueDisplayElement();
6529
- // Find the input element - either it's the display element itself or a child
6530
- this._searchInputElement =
6531
- displayElement.tagName === 'INPUT'
6532
- ? displayElement
6533
- : displayElement.querySelector('input[data-kt-select-search]');
6534
- // Find the clear button robustly
6535
- var clearButtonContainer = null;
6536
- if (displayElement.tagName === 'DIV') {
6537
- clearButtonContainer = displayElement;
6538
- }
6539
- else if (displayElement.tagName === 'INPUT') {
6540
- clearButtonContainer = displayElement.parentElement;
6541
- }
6542
- this._clearButtonElement = clearButtonContainer
6543
- ? clearButtonContainer.querySelector('[data-kt-select-clear-button]')
6544
- : null;
6545
- this._valuesContainerElement = (_a = displayElement === null || displayElement === void 0 ? void 0 : displayElement.closest('[data-kt-select-combobox]')) === null || _a === void 0 ? void 0 : _a.querySelector('[data-kt-select-combobox-values]');
6546
- // Create bound handler references to allow proper cleanup
6560
+ var displayElement = select.getDisplayElement(); // KTSelect's main display element for combobox
6561
+ this._searchInputElement = displayElement.querySelector('input[data-kt-select-search]');
6562
+ this._clearButtonElement = displayElement.querySelector('[data-kt-select-clear-button]');
6563
+ this._valuesContainerElement = displayElement.querySelector('[data-kt-select-combobox-values]');
6547
6564
  this._boundInputHandler = this._handleComboboxInput.bind(this);
6548
6565
  this._boundClearHandler = this._handleClearButtonClick.bind(this);
6549
- // Attach event listeners
6550
6566
  this._attachEventListeners();
6551
- // Reset combobox search state when dropdown closes
6552
6567
  this._select.getElement().addEventListener('dropdown.close', function () {
6553
- _this._searchInputElement.value = '';
6554
- // this._toggleClearButtonVisibility('');
6555
- _this._select.showAllOptions();
6568
+ // When dropdown closes, if not multi-select and not using displayTemplate,
6569
+ // ensure input shows the selected value or placeholder.
6570
+ if (!_this._config.multiple && !_this._config.displayTemplate) {
6571
+ _this.updateDisplay(_this._select.getSelectedOptions());
6572
+ }
6573
+ else {
6574
+ // For tags or displayTemplate, the input should be clear for typing.
6575
+ _this._searchInputElement.value = '';
6576
+ }
6577
+ _this._toggleClearButtonVisibility(_this._searchInputElement.value);
6578
+ // this._select.showAllOptions(); // showAllOptions might be too broad, filtering is managed by typing.
6556
6579
  });
6557
- // When selection changes, update the input value to the selected option's text
6558
- // this._select.getElement().addEventListener('change', () => {
6559
- // // Only update the input value, do not reset the filter or show all options
6560
- // const selectedValues = this._select.getSelectedOptions();
6561
- // const content = this._select.renderDisplayTemplateForSelected(selectedValues);
6562
- // this._valuesContainerElement?.append(stringToElement(content));
6563
- // });
6564
6580
  if (this._config.debug)
6565
6581
  console.log('KTSelectCombobox initialized');
6566
6582
  }
@@ -6568,16 +6584,13 @@ var KTSelectCombobox = /** @class */ (function () {
6568
6584
  * Attach event listeners specific to combobox
6569
6585
  */
6570
6586
  KTSelectCombobox.prototype._attachEventListeners = function () {
6571
- // First remove any existing listeners to prevent duplicates
6572
6587
  this._removeEventListeners();
6573
- // Add input event handler to filter options as user types
6574
- this._searchInputElement.addEventListener('input', this._boundInputHandler);
6575
- // Add clear button click event listener
6588
+ if (this._searchInputElement) { // Ensure element exists
6589
+ this._searchInputElement.addEventListener('input', this._boundInputHandler);
6590
+ }
6576
6591
  if (this._clearButtonElement) {
6577
6592
  this._clearButtonElement.addEventListener('click', this._boundClearHandler);
6578
6593
  }
6579
- if (this._config.debug)
6580
- console.log('Combobox event listeners attached to:', this._searchInputElement);
6581
6594
  };
6582
6595
  /**
6583
6596
  * Remove event listeners to prevent memory leaks or duplicates
@@ -6595,16 +6608,21 @@ var KTSelectCombobox = /** @class */ (function () {
6595
6608
  */
6596
6609
  KTSelectCombobox.prototype._handleComboboxInput = function (event) {
6597
6610
  var inputElement = event.target;
6598
- var query = inputElement.value.toLowerCase();
6599
- if (this._config.debug)
6600
- console.log('Combobox input event, query:', query);
6601
- // Toggle clear button visibility based on input value
6602
- // this._toggleClearButtonVisibility(query);
6603
- // If dropdown isn't open, open it when user starts typing
6604
- if (!this._select._dropdownIsOpen) {
6611
+ var query = inputElement.value;
6612
+ this._toggleClearButtonVisibility(query);
6613
+ if (!this._select.isDropdownOpen()) { // Use public getter
6605
6614
  this._select.openDropdown();
6606
6615
  }
6607
- // Filter options based on input
6616
+ // For single select without displayTemplate, if user types, they are essentially clearing the previous selection text
6617
+ // The actual selection state isn't cleared until they pick a new option or clear explicitly.
6618
+ // For multi-select or with displayTemplate, the input is purely for search.
6619
+ if (this._config.multiple || this._config.displayTemplate) {
6620
+ // Values are in _valuesContainerElement, input is for search
6621
+ }
6622
+ else {
6623
+ // Single select, no displayTemplate: If user types, it implies they might be changing selection.
6624
+ // We don't clear the actual _select state here, just the visual in input.
6625
+ }
6608
6626
  this._filterOptionsForCombobox(query);
6609
6627
  };
6610
6628
  /**
@@ -6613,29 +6631,25 @@ var KTSelectCombobox = /** @class */ (function () {
6613
6631
  KTSelectCombobox.prototype._handleClearButtonClick = function (event) {
6614
6632
  event.preventDefault();
6615
6633
  event.stopPropagation();
6616
- // Clear the input
6617
6634
  this._searchInputElement.value = '';
6618
- // Hide the clear button
6619
- // this._toggleClearButtonVisibility('');
6620
- // Show all options and open dropdown
6621
- this._select.showAllOptions();
6622
- this._select.openDropdown();
6623
- // Clear the current selection
6624
- this._select.clearSelection();
6625
- // Clear the combobox values container if present (for displayTemplate)
6635
+ this._toggleClearButtonVisibility('');
6626
6636
  if (this._valuesContainerElement) {
6627
6637
  this._valuesContainerElement.innerHTML = '';
6628
6638
  }
6629
- // Focus on the input
6639
+ this._select.clearSelection(); // This will also trigger updateSelectedOptionDisplay
6640
+ this._select.showAllOptions(); // Show all options after clearing
6641
+ this._select.openDropdown();
6630
6642
  this._searchInputElement.focus();
6631
6643
  };
6632
6644
  /**
6633
- * Toggle clear button visibility based on input value
6645
+ * Toggle clear button visibility based on input value and selection state.
6646
+ * Clear button should be visible if there's text in input OR if there are selected items (for multi/displayTemplate modes).
6634
6647
  */
6635
- KTSelectCombobox.prototype._toggleClearButtonVisibility = function (value) {
6648
+ KTSelectCombobox.prototype._toggleClearButtonVisibility = function (inputValue) {
6636
6649
  if (!this._clearButtonElement)
6637
6650
  return;
6638
- if (value.length > 0) {
6651
+ var hasSelectedItems = this._select.getSelectedOptions().length > 0;
6652
+ if (inputValue.length > 0 || (hasSelectedItems && (this._config.multiple || this._config.displayTemplate))) {
6639
6653
  this._clearButtonElement.classList.remove('hidden');
6640
6654
  }
6641
6655
  else {
@@ -6646,11 +6660,65 @@ var KTSelectCombobox = /** @class */ (function () {
6646
6660
  * Filter options for combobox based on input query
6647
6661
  */
6648
6662
  KTSelectCombobox.prototype._filterOptionsForCombobox = function (query) {
6649
- // Use the same filter logic as KTSelectSearch
6650
6663
  var options = Array.from(this._select.getOptionsElement());
6651
6664
  var config = this._select.getConfig();
6652
6665
  var dropdownElement = this._select.getDropdownElement();
6653
6666
  (0, utils_1.filterOptions)(options, query, config, dropdownElement);
6667
+ // After filtering, focusManager in KTSelectSearch (if search is also enabled there)
6668
+ // or the main FocusManager should adjust focus if needed.
6669
+ // For combobox, this filtering is the primary search mechanism.
6670
+ // We might need to tell select's focus manager to focus first option.
6671
+ this._select._focusManager.focusFirst(); // Consider if this is always right
6672
+ };
6673
+ /**
6674
+ * Updates the combobox display (input field or values container) based on selection.
6675
+ */
6676
+ KTSelectCombobox.prototype.updateDisplay = function (selectedOptions) {
6677
+ var _this = this;
6678
+ if (!this._searchInputElement)
6679
+ return;
6680
+ // Always clear the values container first if it exists
6681
+ if (this._valuesContainerElement) {
6682
+ this._valuesContainerElement.innerHTML = '';
6683
+ }
6684
+ if (this._config.tags && this._valuesContainerElement) { // Combobox + Tags
6685
+ selectedOptions.forEach(function (value) {
6686
+ // Ensure value is properly escaped for querySelector
6687
+ var optionElement = _this._select.getElement().querySelector("option[value=\"".concat(CSS.escape(value), "\"]"));
6688
+ if (optionElement) {
6689
+ var tagElement = templates_1.defaultTemplates.tag(optionElement, _this._config);
6690
+ _this._valuesContainerElement.appendChild(tagElement);
6691
+ }
6692
+ });
6693
+ this._searchInputElement.value = ''; // Input field is for typing new searches
6694
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
6695
+ }
6696
+ else if (this._config.displayTemplate && this._valuesContainerElement) { // Combobox + DisplayTemplate (no tags)
6697
+ this._valuesContainerElement.innerHTML = this._select.renderDisplayTemplateForSelected(selectedOptions);
6698
+ this._searchInputElement.value = ''; // Input field is for typing new searches
6699
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
6700
+ }
6701
+ else if (this._config.multiple && this._valuesContainerElement) { // Combobox + Multiple (no tags, no display template)
6702
+ // For simplicity, join text. A proper tag implementation would be more complex here.
6703
+ this._valuesContainerElement.innerHTML = selectedOptions.map(function (value) {
6704
+ var optionEl = _this._select.getElement().querySelector("option[value=\"".concat(CSS.escape(value), "\"]"));
6705
+ return optionEl ? optionEl.textContent : '';
6706
+ }).join(', '); // Basic comma separation
6707
+ this._searchInputElement.value = '';
6708
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
6709
+ }
6710
+ else if (!this._config.multiple && selectedOptions.length > 0) { // Single select combobox: display selected option's text in the input
6711
+ var selectedValue = selectedOptions[0];
6712
+ var optionElement = this._select.getElement().querySelector("option[value=\"".concat(CSS.escape(selectedValue), "\"]"));
6713
+ this._searchInputElement.value = optionElement ? optionElement.textContent || '' : '';
6714
+ // placeholder is implicitly handled by input value for single select
6715
+ }
6716
+ else { // No selection or not fitting above categories (e.g. single select, no items)
6717
+ this._searchInputElement.value = '';
6718
+ this._searchInputElement.placeholder = this._config.placeholder || 'Select...';
6719
+ // _valuesContainerElement is already cleared if it exists
6720
+ }
6721
+ this._toggleClearButtonVisibility(this._searchInputElement.value);
6654
6722
  };
6655
6723
  /**
6656
6724
  * Destroy the combobox component and clean up event listeners
@@ -7895,7 +7963,7 @@ var KTDataTable = /** @class */ (function (_super) {
7895
7963
  // Set search value
7896
7964
  if (searchElement) {
7897
7965
  searchElement.value =
7898
- typeof search === 'string' ? search : String(search);
7966
+ search === undefined || search === null ? '' : typeof search === 'string' ? search : String(search);
7899
7967
  }
7900
7968
  if (searchElement) {
7901
7969
  // Check if a debounced search function already exists
@@ -9217,7 +9285,7 @@ var utils_1 = __webpack_require__(9011);
9217
9285
  *
9218
9286
  * A specialized dropdown implementation for the KTSelect component.
9219
9287
  * This module handles the dropdown functionality for the select component,
9220
- * including positioning, showing/hiding, and keyboard navigation.
9288
+ * including positioning and showing/hiding.
9221
9289
  */
9222
9290
  var KTSelectDropdown = /** @class */ (function (_super) {
9223
9291
  __extends(KTSelectDropdown, _super);
@@ -9228,7 +9296,7 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9228
9296
  * @param dropdownElement The dropdown content element
9229
9297
  * @param config The configuration options
9230
9298
  */
9231
- function KTSelectDropdown(element, toggleElement, dropdownElement, config) {
9299
+ function KTSelectDropdown(element, toggleElement, dropdownElement, config, ktSelectInstance) {
9232
9300
  var _this = _super.call(this) || this;
9233
9301
  _this._name = 'select-dropdown';
9234
9302
  // State
@@ -9239,6 +9307,7 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9239
9307
  _this._toggleElement = toggleElement;
9240
9308
  _this._dropdownElement = dropdownElement;
9241
9309
  _this._config = config;
9310
+ _this._ktSelectInstance = ktSelectInstance; // Assign instance
9242
9311
  _this._eventManager = new utils_1.EventManager();
9243
9312
  _this._focusManager = new utils_1.FocusManager(dropdownElement, '[data-kt-select-option]', config);
9244
9313
  _this._setupEventListeners();
@@ -9259,7 +9328,18 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9259
9328
  KTSelectDropdown.prototype._handleToggleClick = function (event) {
9260
9329
  event.preventDefault();
9261
9330
  event.stopPropagation();
9262
- this.toggle();
9331
+ if (this._config.disabled) {
9332
+ if (this._config.debug)
9333
+ console.log('KTSelectDropdown._handleToggleClick: select is disabled');
9334
+ return;
9335
+ }
9336
+ // Call KTSelect's methods
9337
+ if (this._ktSelectInstance.isDropdownOpen()) {
9338
+ this._ktSelectInstance.closeDropdown();
9339
+ }
9340
+ else {
9341
+ this._ktSelectInstance.openDropdown();
9342
+ }
9263
9343
  };
9264
9344
  /**
9265
9345
  * Handle clicks outside the dropdown
@@ -9270,7 +9350,8 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9270
9350
  var target = event.target;
9271
9351
  if (!this._element.contains(target) &&
9272
9352
  !this._dropdownElement.contains(target)) {
9273
- this.close();
9353
+ // Call KTSelect's closeDropdown method
9354
+ this._ktSelectInstance.closeDropdown();
9274
9355
  }
9275
9356
  };
9276
9357
  /**
@@ -9372,29 +9453,6 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9372
9453
  this._popperInstance.update();
9373
9454
  }
9374
9455
  };
9375
- /**
9376
- * Toggle the dropdown
9377
- */
9378
- KTSelectDropdown.prototype.toggle = function () {
9379
- if (this._config.disabled) {
9380
- if (this._config.debug)
9381
- console.log('KTSelectDropdown.toggle: select is disabled, not toggling');
9382
- return;
9383
- }
9384
- if (this._config.debug)
9385
- console.log('KTSelectDropdown.toggle called - isOpen:', this._isOpen);
9386
- if (this._isTransitioning) {
9387
- if (this._config.debug)
9388
- console.log('KTSelectDropdown.toggle - ignoring during transition');
9389
- return;
9390
- }
9391
- if (this._isOpen) {
9392
- this.close();
9393
- }
9394
- else {
9395
- this.open();
9396
- }
9397
- };
9398
9456
  /**
9399
9457
  * Open the dropdown
9400
9458
  */
@@ -9407,71 +9465,41 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9407
9465
  }
9408
9466
  if (this._isOpen || this._isTransitioning)
9409
9467
  return;
9410
- // Fire before show event
9411
- var beforeShowEvent = new CustomEvent('kt.select.dropdown.show', {
9412
- bubbles: true,
9413
- cancelable: true,
9414
- });
9415
- this._element.dispatchEvent(beforeShowEvent);
9416
- if (beforeShowEvent.defaultPrevented)
9417
- return;
9418
9468
  // Begin opening transition
9419
9469
  this._isTransitioning = true;
9420
- // Set initial styles - remove display: block and use class toggling instead
9470
+ // Set initial styles
9421
9471
  this._dropdownElement.classList.remove('hidden');
9422
9472
  this._dropdownElement.style.opacity = '0';
9423
9473
  // Set dropdown width
9424
9474
  this._setDropdownWidth();
9425
- // Make sure the element is visible for transitioning
9475
+ // Reflow
9426
9476
  dom_1.default.reflow(this._dropdownElement);
9427
- // Apply z-index if configured
9477
+ // Apply z-index
9428
9478
  if (this._config.dropdownZindex) {
9429
9479
  this._dropdownElement.style.zIndex =
9430
9480
  this._config.dropdownZindex.toString();
9431
9481
  }
9432
9482
  else {
9433
- // Auto-calculate z-index
9434
9483
  var parentZindex = dom_1.default.getHighestZindex(this._element);
9435
9484
  if (parentZindex) {
9436
9485
  this._dropdownElement.style.zIndex = (parentZindex + 1).toString();
9437
9486
  }
9438
9487
  }
9439
- // Initialize popper for positioning
9488
+ // Initialize popper
9440
9489
  this._initPopper();
9441
- // Add active classes
9490
+ // Add active classes for visual state
9442
9491
  this._dropdownElement.classList.add('open');
9443
9492
  this._toggleElement.classList.add('active');
9444
- this._toggleElement.setAttribute('aria-expanded', 'true');
9493
+ // ARIA attributes will be handled by KTSelect
9445
9494
  // Start transition
9446
9495
  this._dropdownElement.style.opacity = '1';
9447
9496
  // Handle transition end
9448
9497
  dom_1.default.transitionEnd(this._dropdownElement, function () {
9449
9498
  _this._isTransitioning = false;
9450
9499
  _this._isOpen = true;
9451
- // Focus the first item if search is enabled
9452
- if (_this._config.enableSearch) {
9453
- var searchInput = _this._dropdownElement.querySelector('input[type="search"]');
9454
- if (searchInput) {
9455
- searchInput.focus();
9456
- }
9457
- }
9458
- // Fire after show event
9459
- var afterShowEvent = new CustomEvent('kt.select.dropdown.shown', {
9460
- bubbles: true,
9461
- });
9462
- _this._element.dispatchEvent(afterShowEvent);
9500
+ // Focus and events will be handled by KTSelect
9463
9501
  });
9464
9502
  };
9465
- /**
9466
- * Focus the first option in the dropdown
9467
- */
9468
- KTSelectDropdown.prototype._focusFirstOption = function () {
9469
- var firstOption = this._focusManager.getVisibleOptions()[0];
9470
- if (firstOption) {
9471
- this._focusManager.applyFocus(firstOption);
9472
- this._focusManager.scrollIntoView(firstOption);
9473
- }
9474
- };
9475
9503
  /**
9476
9504
  * Close the dropdown
9477
9505
  */
@@ -9484,34 +9512,19 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9484
9512
  console.log('KTSelectDropdown.close - early return: dropdown not open or is transitioning');
9485
9513
  return;
9486
9514
  }
9487
- // Fire before hide event
9488
- var beforeHideEvent = new CustomEvent('kt.select.dropdown.hide', {
9489
- bubbles: true,
9490
- cancelable: true,
9491
- });
9492
- this._element.dispatchEvent(beforeHideEvent);
9493
- if (beforeHideEvent.defaultPrevented) {
9494
- if (this._config.debug)
9495
- console.log('KTSelectDropdown.close - canceling due to defaultPrevented on beforeHideEvent');
9496
- return;
9497
- }
9515
+ // Events and ARIA will be handled by KTSelect
9498
9516
  if (this._config.debug)
9499
9517
  console.log('KTSelectDropdown.close - starting transition');
9500
- // Begin closing transition
9501
9518
  this._isTransitioning = true;
9502
- // Start transition
9503
9519
  this._dropdownElement.style.opacity = '0';
9504
- // Use a combination of transition end and a fallback timer
9505
9520
  var transitionComplete = false;
9506
- // Set a fixed-duration fallback in case the transition event doesn't fire
9507
9521
  var fallbackTimer = setTimeout(function () {
9508
9522
  if (!transitionComplete) {
9509
9523
  if (_this._config.debug)
9510
9524
  console.log('KTSelectDropdown.close - fallback timer triggered');
9511
9525
  completeTransition();
9512
9526
  }
9513
- }, 300); // 300ms should be enough for most transitions
9514
- // Setup the transition end function
9527
+ }, 300);
9515
9528
  var completeTransition = function () {
9516
9529
  if (transitionComplete)
9517
9530
  return;
@@ -9519,30 +9532,21 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9519
9532
  clearTimeout(fallbackTimer);
9520
9533
  if (_this._config.debug)
9521
9534
  console.log('KTSelectDropdown.close - transition ended');
9522
- // Remove active classes
9523
9535
  _this._dropdownElement.classList.add('hidden');
9524
9536
  _this._dropdownElement.classList.remove('open');
9525
9537
  _this._toggleElement.classList.remove('active');
9526
- _this._toggleElement.setAttribute('aria-expanded', 'false');
9527
- // Reset styles - replace display: none with adding hidden class
9528
- _this._dropdownElement.classList.add('hidden');
9529
- _this._dropdownElement.style.opacity = '';
9530
- _this._dropdownElement.style.zIndex = '';
9531
- // Destroy popper
9538
+ // ARIA attributes will be handled by KTSelect
9532
9539
  _this._destroyPopper();
9533
- // Update state
9534
9540
  _this._isTransitioning = false;
9535
9541
  _this._isOpen = false;
9536
- // Fire after hide event
9537
- var afterHideEvent = new CustomEvent('kt.select.dropdown.hidden', {
9538
- bubbles: true,
9539
- });
9540
- _this._element.dispatchEvent(afterHideEvent);
9542
+ // Events will be handled by KTSelect
9541
9543
  if (_this._config.debug)
9542
- console.log('KTSelectDropdown.close - complete, events fired');
9544
+ console.log('KTSelectDropdown.close - visual part complete');
9543
9545
  };
9544
- // Handle transition end via the utility but also have the fallback
9545
9546
  dom_1.default.transitionEnd(this._dropdownElement, completeTransition);
9547
+ if (dom_1.default.getCssProp(this._dropdownElement, 'transition-duration') === '0s') {
9548
+ completeTransition();
9549
+ }
9546
9550
  };
9547
9551
  /**
9548
9552
  * Check if dropdown is open
@@ -9910,15 +9914,17 @@ var KTSelectSearch = /** @class */ (function () {
9910
9914
  console.log('Initializing search module with input:', this._searchInput);
9911
9915
  // First remove any existing listeners to prevent duplicates
9912
9916
  this._removeEventListeners();
9913
- // Add the event listener
9917
+ // Add the input event listener for filtering
9914
9918
  this._eventManager.addListener(this._searchInput, 'input', this.handleSearchInput);
9919
+ // Add keydown event listener for navigation, selection, and escape
9920
+ this._eventManager.addListener(this._searchInput, 'keydown', this._handleSearchKeyDown.bind(this));
9915
9921
  // Add blur event listener to ensure highlights are cleared when focus is lost
9916
9922
  this._eventManager.addListener(this._searchInput, 'blur', function () {
9917
9923
  // Small delay to prevent race conditions with selection
9918
9924
  setTimeout(function () {
9919
9925
  if (!_this._searchInput.value) {
9920
9926
  _this._resetAllOptions();
9921
- _this.clearSearchHighlights();
9927
+ _this.clearSearch();
9922
9928
  }
9923
9929
  }, 100);
9924
9930
  });
@@ -9936,39 +9942,48 @@ var KTSelectSearch = /** @class */ (function () {
9936
9942
  _this.refreshOptionCache();
9937
9943
  });
9938
9944
  }
9939
- // Listen for dropdown close to reset options if search is empty
9940
- this._select.getElement().addEventListener('dropdown.close', function () {
9945
+ // Listen for dropdown close to reset options - ATTACH TO WRAPPER
9946
+ this._select.getWrapperElement().addEventListener('dropdown.close', function () {
9941
9947
  _this._focusManager.resetFocus();
9942
- _this.clearSearchHighlights();
9943
- _this._searchInput.value = '';
9944
- _this._resetAllOptions();
9945
- _this._clearNoResultsMessage();
9948
+ // If clearSearchOnClose is false and there's a value, the search term and filtered state should persist.
9949
+ // KTSelect's closeDropdown method already calls this._searchModule.clearSearch() (which clears highlights)
9950
+ // and conditionally clears the input value based on KTSelect's config.clearSearchOnClose.
9951
+ // This listener in search.ts seems to unconditionally clear everything.
9952
+ // For now, keeping its original behavior:
9953
+ _this.clearSearch(); // Clears highlights from current options
9954
+ _this._searchInput.value = ''; // Clears the search input field
9955
+ _this._resetAllOptions(); // Shows all options, restores original text, removes highlights
9956
+ _this._clearNoResultsMessage(); // Clears any "no results" message
9946
9957
  });
9947
- // Clear highlights when an option is selected
9958
+ // Clear highlights when an option is selected - ATTACH TO ORIGINAL SELECT (standard 'change' event)
9948
9959
  this._select.getElement().addEventListener('change', function () {
9949
- _this.clearSearchHighlights();
9960
+ _this.clearSearch();
9950
9961
  // Close dropdown if configured to do so
9951
- if (_this._select.getConfig().closeOnSelect &&
9952
- !_this._select.getConfig().multiple) {
9953
- _this._select.closeDropdown();
9954
- }
9962
+ _this._select.closeDropdown();
9955
9963
  });
9956
- // Autofocus on search input
9957
- if (this._select.getConfig().searchAutofocus) {
9958
- this._select.getElement().addEventListener('dropdown.show', function () {
9964
+ // Consolidated 'dropdown.show' event listener - ATTACH TO WRAPPER
9965
+ this._select.getWrapperElement().addEventListener('dropdown.show', function () {
9966
+ var _a;
9967
+ _this._focusManager.resetFocus(); // Always clear previous focus state
9968
+ if ((_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.value) {
9969
+ // If there's an existing search term:
9970
+ // 1. Re-filter options. This ensures the display (hidden/visible) is correct
9971
+ // and "no results" message is handled if query yields nothing.
9972
+ _this._filterOptions(_this._searchInput.value);
9973
+ }
9974
+ else {
9975
+ // If search input is empty:
9976
+ // 1. Reset all options to their full, unfiltered, original state.
9977
+ _this._resetAllOptions(); // Shows all, clears highlights from options, restores original text
9978
+ // 2. Clear any "no results" message.
9979
+ _this._clearNoResultsMessage();
9980
+ }
9981
+ // Handle autofocus for the search input (this was one of the original separate listeners)
9982
+ if (_this._select.getConfig().searchAutofocus) {
9959
9983
  setTimeout(function () {
9960
9984
  var _a;
9961
- // Add slight delay to ensure the dropdown and search input are visible
9962
- (_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.focus();
9963
- }, 50);
9964
- });
9965
- }
9966
- // Listen for explicit dropdown open event to clear highlights if needed
9967
- this._select.getElement().addEventListener('dropdown.show', function () {
9968
- var _a;
9969
- // If search input is empty, ensure highlights are cleared on open
9970
- if (!((_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.value)) {
9971
- _this.clearSearchHighlights();
9985
+ (_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.focus(); // Focus search input
9986
+ }, 50); // Delay to ensure dropdown is visible
9972
9987
  }
9973
9988
  });
9974
9989
  }
@@ -9983,18 +9998,42 @@ var KTSelectSearch = /** @class */ (function () {
9983
9998
  }
9984
9999
  };
9985
10000
  /**
9986
- * Select the currently focused option
10001
+ * Handles keydown events on the search input for navigation and actions.
9987
10002
  */
9988
- KTSelectSearch.prototype._selectFocusedOption = function () {
9989
- var focusedOption = this._focusManager.getFocusedOption();
9990
- if (focusedOption) {
9991
- var optionValue = focusedOption.getAttribute('data-value');
9992
- if (optionValue) {
9993
- // Ensure highlights are cleared before selection
9994
- this.clearSearchHighlights();
9995
- // Trigger the selection in the main select component
9996
- this._select['_selectOption'](optionValue);
9997
- }
10003
+ KTSelectSearch.prototype._handleSearchKeyDown = function (event) {
10004
+ var key = event.key;
10005
+ switch (key) {
10006
+ case 'ArrowDown':
10007
+ event.preventDefault();
10008
+ this._focusManager.focusNext();
10009
+ break;
10010
+ case 'ArrowUp':
10011
+ event.preventDefault();
10012
+ this._focusManager.focusPrevious();
10013
+ break;
10014
+ case 'Enter':
10015
+ event.preventDefault();
10016
+ // Always attempt to select the first available option in the list.
10017
+ // focusFirst() finds, focuses, and returns the first visible, non-disabled option.
10018
+ var firstAvailableOption = this._focusManager.focusFirst();
10019
+ if (firstAvailableOption) {
10020
+ var optionValue = firstAvailableOption.getAttribute('data-value');
10021
+ if (optionValue) {
10022
+ this._select.toggleSelection(optionValue);
10023
+ // KTSelect.toggleSelection handles closing the dropdown based on config.closeOnSelect and config.multiple
10024
+ }
10025
+ }
10026
+ break;
10027
+ case 'Escape':
10028
+ event.preventDefault();
10029
+ this._searchInput.value = '';
10030
+ this.clearSearch();
10031
+ this._resetAllOptions();
10032
+ this._clearNoResultsMessage();
10033
+ this._focusManager.focusFirst();
10034
+ break;
10035
+ default:
10036
+ break;
9998
10037
  }
9999
10038
  };
10000
10039
  /**
@@ -10005,42 +10044,66 @@ var KTSelectSearch = /** @class */ (function () {
10005
10044
  var _this = this;
10006
10045
  // Wait for options to be initialized
10007
10046
  setTimeout(function () {
10047
+ _this._originalOptionContents.clear(); // Clear before re-caching
10008
10048
  var options = Array.from(_this._select.getOptionsElement());
10009
10049
  options.forEach(function (option) {
10010
10050
  var value = option.getAttribute('data-value');
10011
10051
  if (value) {
10052
+ // Store the full innerHTML as the original content
10012
10053
  _this._originalOptionContents.set(value, option.innerHTML);
10013
10054
  }
10014
10055
  });
10015
10056
  }, 0);
10016
10057
  };
10058
+ /**
10059
+ * Restores the innerHTML of all options from the cache if they have been modified.
10060
+ * This is typically called before applying new filters/highlights.
10061
+ */
10062
+ KTSelectSearch.prototype._restoreOptionContentsBeforeFilter = function () {
10063
+ var _this = this;
10064
+ var options = Array.from(this._select.getOptionsElement());
10065
+ options.forEach(function (option) {
10066
+ var value = option.getAttribute('data-value');
10067
+ if (value && _this._originalOptionContents.has(value)) {
10068
+ var originalContent = _this._originalOptionContents.get(value);
10069
+ // Only restore if current content is different, to avoid unnecessary DOM manipulation
10070
+ if (option.innerHTML !== originalContent) {
10071
+ option.innerHTML = originalContent;
10072
+ }
10073
+ }
10074
+ });
10075
+ };
10017
10076
  KTSelectSearch.prototype._handleSearchInput = function (event) {
10018
- var query = event.target.value.toLowerCase();
10077
+ var query = event.target.value;
10019
10078
  var config = this._select.getConfig();
10020
10079
  // Reset focused option when search changes
10021
10080
  this._focusManager.resetFocus();
10022
- // If search query is empty, clear all highlights
10081
+ // Restore original content for all options before filtering/highlighting again
10082
+ this._restoreOptionContentsBeforeFilter();
10023
10083
  if (query.trim() === '') {
10024
- this.clearSearchHighlights();
10084
+ this._resetAllOptions();
10085
+ this._focusManager.focusFirst(); // Focus first option when search is cleared
10086
+ return;
10025
10087
  }
10026
- // For remote search, we don't filter locally
10027
- // The KTSelect component will handle the remote search
10088
+ // For remote search, KTSelect component handles it.
10089
+ // KTSelect will call refreshAfterSearch on this module when remote data is updated.
10028
10090
  if (config.remote && config.searchParam) {
10029
- // If query is too short, reset all options to visible state
10030
10091
  if (query.length < config.searchMinLength) {
10031
10092
  this._resetAllOptions();
10032
10093
  this._clearNoResultsMessage();
10094
+ this._focusManager.focusFirst(); // Focus first if query too short
10033
10095
  }
10034
- // Otherwise, let KTSelect handle remote search
10035
10096
  return;
10036
10097
  }
10037
10098
  // For local search
10038
10099
  if (query.length >= config.searchMinLength) {
10039
10100
  this._filterOptions(query);
10101
+ this._focusManager.focusFirst(); // Focus first visible option after local filtering
10040
10102
  }
10041
10103
  else {
10042
10104
  this._resetAllOptions();
10043
10105
  this._clearNoResultsMessage();
10106
+ this._focusManager.focusFirst(); // Focus first if query too short and not remote
10044
10107
  }
10045
10108
  };
10046
10109
  KTSelectSearch.prototype._filterOptions = function (query) {
@@ -10052,20 +10115,11 @@ var KTSelectSearch = /** @class */ (function () {
10052
10115
  if (this._originalOptionContents.size === 0) {
10053
10116
  this._cacheOriginalOptionContents();
10054
10117
  }
10055
- // Use the shared filterOptions utility
10056
- (0, utils_1.filterOptions)(options, query, config, dropdownElement, function (visibleCount) {
10057
- return _this._handleNoResults(visibleCount);
10118
+ // Restore original content before filtering, so highlighting is applied fresh.
10119
+ this._restoreOptionContentsBeforeFilter();
10120
+ var visibleCount = (0, utils_1.filterOptions)(options, query, config, dropdownElement, function (count) {
10121
+ return _this._handleNoResults(count);
10058
10122
  });
10059
- // Apply specialized text highlighting if needed
10060
- if (config.searchHighlight && query.trim() !== '') {
10061
- this._applyHighlightToDisplay(query);
10062
- }
10063
- };
10064
- /**
10065
- * Apply highlighting to the display element for multi-select
10066
- */
10067
- KTSelectSearch.prototype._applyHighlightToDisplay = function (query) {
10068
- // Implementation for display highlighting
10069
10123
  };
10070
10124
  /**
10071
10125
  * Reset all options to their original state
@@ -10074,34 +10128,29 @@ var KTSelectSearch = /** @class */ (function () {
10074
10128
  var _this = this;
10075
10129
  // Show all options
10076
10130
  var options = Array.from(this._select.getOptionsElement());
10077
- // Cache original option HTML if not already cached
10131
+ // Ensure the cache is populated if it's somehow empty here
10078
10132
  if (this._originalOptionContents.size === 0) {
10079
10133
  this._cacheOriginalOptionContents();
10080
10134
  }
10081
10135
  options.forEach(function (option) {
10082
- // Remove the hidden class
10083
10136
  option.classList.remove('hidden');
10137
+ if (option.style.display === 'none')
10138
+ option.style.display = ''; // Ensure visible
10084
10139
  // Restore original HTML content (remove highlights)
10085
10140
  var value = option.getAttribute('data-value');
10086
10141
  if (value && _this._originalOptionContents.has(value)) {
10087
- option.innerHTML = _this._originalOptionContents.get(value);
10088
- }
10089
- // Remove any display styling
10090
- if (option.hasAttribute('style') &&
10091
- option.getAttribute('style').includes('display:')) {
10092
- var styleAttr = option.getAttribute('style');
10093
- if (styleAttr.trim() === 'display: none;' ||
10094
- styleAttr.trim() === 'display: block;') {
10095
- option.removeAttribute('style');
10096
- }
10097
- else {
10098
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
10142
+ var originalContent = _this._originalOptionContents.get(value);
10143
+ // Only update if different, to minimize DOM changes
10144
+ if (option.innerHTML !== originalContent) {
10145
+ option.innerHTML = originalContent;
10099
10146
  }
10100
10147
  }
10101
10148
  });
10149
+ this._clearNoResultsMessage(); // Ensure no results message is cleared when resetting
10102
10150
  };
10103
10151
  KTSelectSearch.prototype._handleNoResults = function (visibleOptionsCount) {
10104
- if (visibleOptionsCount === 0 && this._searchInput.value.trim() !== '') {
10152
+ var _a, _b;
10153
+ if (visibleOptionsCount === 0 && ((_b = (_a = this._searchInput) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.trim()) !== '') {
10105
10154
  this._showNoResultsMessage();
10106
10155
  }
10107
10156
  else {
@@ -10131,28 +10180,22 @@ var KTSelectSearch = /** @class */ (function () {
10131
10180
  * Public method to explicitly clear all search highlights
10132
10181
  * This is called when search is reset or selection changes
10133
10182
  */
10134
- KTSelectSearch.prototype.clearSearchHighlights = function () {
10183
+ KTSelectSearch.prototype.clearSearch = function () {
10135
10184
  var _this = this;
10136
10185
  // Restore original option content (removes highlighting)
10137
- var options = Array.from(this._select.getOptionsElement());
10138
- options.forEach(function (option) {
10186
+ var optionsToClear = Array.from(this._select.getOptionsElement());
10187
+ // Ensure cache is available
10188
+ if (this._originalOptionContents.size === 0 && optionsToClear.length > 0) {
10189
+ this._cacheOriginalOptionContents();
10190
+ }
10191
+ optionsToClear.forEach(function (option) {
10139
10192
  var value = option.getAttribute('data-value');
10140
10193
  if (value && _this._originalOptionContents.has(value)) {
10141
- option.innerHTML = _this._originalOptionContents.get(value);
10142
- }
10143
- });
10144
- // Also clear highlights from the display element
10145
- this._clearDisplayHighlights();
10146
- };
10147
- /**
10148
- * Clear any highlights from the display element (selected values)
10149
- */
10150
- KTSelectSearch.prototype._clearDisplayHighlights = function () {
10151
- // Implementation for clearing display highlights
10152
- var options = Array.from(this._select.getOptionsElement());
10153
- options.forEach(function (option) {
10154
- if (option.dataset && !option.dataset.originalText) {
10155
- option.dataset.originalText = option.innerHTML;
10194
+ var originalContent = _this._originalOptionContents.get(value);
10195
+ // Only restore if different
10196
+ if (option.innerHTML !== originalContent) {
10197
+ option.innerHTML = originalContent;
10198
+ }
10156
10199
  }
10157
10200
  });
10158
10201
  };
@@ -10163,14 +10206,23 @@ var KTSelectSearch = /** @class */ (function () {
10163
10206
  var _this = this;
10164
10207
  // Re-cache all option contents
10165
10208
  this._originalOptionContents.clear();
10166
- var options = Array.from(this._select.getOptionsElement());
10167
- options.forEach(function (option) {
10209
+ var currentOptions = Array.from(this._select.getOptionsElement());
10210
+ currentOptions.forEach(function (option) {
10168
10211
  var value = option.getAttribute('data-value');
10169
10212
  if (value) {
10170
10213
  _this._originalOptionContents.set(value, option.innerHTML);
10171
10214
  }
10172
10215
  });
10173
10216
  };
10217
+ /**
10218
+ * Called after search (local or remote via KTSelect) to reset focus.
10219
+ */
10220
+ KTSelectSearch.prototype.refreshAfterSearch = function () {
10221
+ this._focusManager.resetFocus();
10222
+ this._focusManager.focusFirst();
10223
+ // Re-cache original contents as options might have changed (especially after remote search)
10224
+ this.refreshOptionCache();
10225
+ };
10174
10226
  /**
10175
10227
  * Clean up all resources used by the search module
10176
10228
  */
@@ -10178,12 +10230,13 @@ var KTSelectSearch = /** @class */ (function () {
10178
10230
  // Remove all event listeners
10179
10231
  this._removeEventListeners();
10180
10232
  // Clear all references
10181
- this._focusManager.dispose();
10182
- this._eventManager.removeAllListeners(null);
10233
+ if (this._focusManager) {
10234
+ this._focusManager.dispose();
10235
+ }
10183
10236
  // Clear cached content
10184
10237
  this._originalOptionContents.clear();
10185
10238
  // Clear highlight elements
10186
- this.clearSearchHighlights();
10239
+ this.clearSearch();
10187
10240
  };
10188
10241
  return KTSelectSearch;
10189
10242
  }());
@@ -11399,15 +11452,16 @@ var KTSelect = /** @class */ (function (_super) {
11399
11452
  var optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
11400
11453
  if (!optionsContainer)
11401
11454
  return;
11455
+ // Clear previous messages
11456
+ optionsContainer.innerHTML = '';
11402
11457
  switch (type) {
11403
11458
  case 'error':
11404
- optionsContainer.innerHTML = templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message }));
11459
+ optionsContainer.appendChild(templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message })));
11405
11460
  break;
11406
11461
  case 'loading':
11407
- optionsContainer.innerHTML = templates_1.defaultTemplates.loading(this._config, message || 'Loading...').outerHTML;
11462
+ optionsContainer.appendChild(templates_1.defaultTemplates.loading(this._config, message || 'Loading...'));
11408
11463
  break;
11409
11464
  case 'empty':
11410
- optionsContainer.innerHTML = '';
11411
11465
  optionsContainer.appendChild(templates_1.defaultTemplates.empty(this._config));
11412
11466
  break;
11413
11467
  }
@@ -11579,7 +11633,7 @@ var KTSelect = /** @class */ (function (_super) {
11579
11633
  // Initialize focus manager after dropdown element is created
11580
11634
  this._focusManager = new utils_1.FocusManager(this._dropdownContentElement, '[data-kt-select-option]', this._config);
11581
11635
  // Initialize dropdown module after all elements are created
11582
- this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config);
11636
+ this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config, this);
11583
11637
  // Update display and set ARIA attributes
11584
11638
  this._updateDisplayAndAriaAttributes();
11585
11639
  this.updateSelectedOptionDisplay();
@@ -11587,12 +11641,6 @@ var KTSelect = /** @class */ (function (_super) {
11587
11641
  // Attach event listeners after all modules are initialized
11588
11642
  this._attachEventListeners();
11589
11643
  };
11590
- /**
11591
- * Initialize options HTML from data
11592
- */
11593
- // private _initializeOptionsHtml() {
11594
- // this._generateOptionsHtml(this._element);
11595
- // }
11596
11644
  /**
11597
11645
  * Creates the HTML structure for the select component
11598
11646
  */
@@ -11662,7 +11710,6 @@ var KTSelect = /** @class */ (function (_super) {
11662
11710
  if (!this._searchInputElement) {
11663
11711
  this._searchInputElement = this._displayElement;
11664
11712
  }
11665
- this._valueDisplayElement = this._wrapperElement.querySelector("[data-kt-select-value]");
11666
11713
  this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
11667
11714
  };
11668
11715
  /**
@@ -11673,16 +11720,10 @@ var KTSelect = /** @class */ (function (_super) {
11673
11720
  document.addEventListener('click', this._handleDocumentClick.bind(this));
11674
11721
  // Dropdown option click events
11675
11722
  this._eventManager.addListener(this._dropdownContentElement, 'click', this._handleDropdownOptionClick.bind(this));
11676
- // Only attach click handler to display element
11677
- // this._eventManager.addListener(
11678
- // this._wrapperElement,
11679
- // 'click',
11680
- // this._handleDropdownClick.bind(this),
11681
- // );
11682
- // Attach centralized keyboard handler
11683
- var keyboardTarget = this._searchInputElement || this._wrapperElement;
11684
- if (keyboardTarget) {
11685
- keyboardTarget.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
11723
+ // Attach centralized keyboard handler to the wrapper element.
11724
+ // Events from focusable children like _displayElement or _searchInputElement (if present) will bubble up.
11725
+ if (this._wrapperElement) {
11726
+ this._wrapperElement.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
11686
11727
  }
11687
11728
  };
11688
11729
  /**
@@ -11825,37 +11866,10 @@ var KTSelect = /** @class */ (function (_super) {
11825
11866
  * DROPDOWN MANAGEMENT
11826
11867
  * ========================================================================
11827
11868
  */
11828
- /**
11829
- * Toggle dropdown visibility
11830
- * @deprecated
11831
- */
11832
- KTSelect.prototype.toggleDropdown = function () {
11833
- if (this._config.disabled) {
11834
- if (this._config.debug)
11835
- console.log('toggleDropdown: select is disabled, not opening');
11836
- return;
11837
- }
11838
- if (this._config.debug)
11839
- console.log('toggleDropdown called');
11840
- if (this._dropdownModule) {
11841
- // Always use the dropdown module's state to determine whether to open or close
11842
- if (this._dropdownModule.isOpen()) {
11843
- if (this._config.debug)
11844
- console.log('Dropdown is open, closing...');
11845
- this.closeDropdown();
11846
- }
11847
- else {
11848
- if (this._config.debug)
11849
- console.log('Dropdown is closed, opening...');
11850
- this.openDropdown();
11851
- }
11852
- }
11853
- };
11854
11869
  /**
11855
11870
  * Open the dropdown
11856
11871
  */
11857
11872
  KTSelect.prototype.openDropdown = function () {
11858
- var _this = this;
11859
11873
  if (this._config.disabled) {
11860
11874
  if (this._config.debug)
11861
11875
  console.log('openDropdown: select is disabled, not opening');
@@ -11883,14 +11897,6 @@ var KTSelect = /** @class */ (function (_super) {
11883
11897
  // Dispatch custom event
11884
11898
  this._dispatchEvent('show');
11885
11899
  this._fireEvent('show');
11886
- // Focus search input if configured and exists
11887
- if (this._config.enableSearch &&
11888
- this._config.searchAutofocus &&
11889
- this._searchInputElement) {
11890
- setTimeout(function () {
11891
- _this._searchInputElement.focus();
11892
- }, 50);
11893
- }
11894
11900
  // Update ARIA states
11895
11901
  this._setAriaAttributes();
11896
11902
  // Focus the first selected option or first option if nothing selected
@@ -11911,14 +11917,14 @@ var KTSelect = /** @class */ (function (_super) {
11911
11917
  // Always close by delegating to the dropdown module, which is the source of truth
11912
11918
  if (this._config.debug)
11913
11919
  console.log('Closing dropdown via dropdownModule...');
11914
- // Clear search input and highlights if the dropdown is closing
11920
+ // Clear search input if the dropdown is closing
11915
11921
  if (this._searchModule && this._searchInputElement) {
11916
11922
  // Clear search input if configured to do so
11917
11923
  if (this._config.clearSearchOnClose) {
11918
11924
  this._searchInputElement.value = '';
11919
11925
  }
11920
- // Always clear the highlights when dropdown closes
11921
- this._searchModule.clearSearchHighlights();
11926
+ // Clear search input when dropdown closes
11927
+ this._searchModule.clearSearch();
11922
11928
  }
11923
11929
  // Set our internal flag to match what we're doing
11924
11930
  this._dropdownIsOpen = false;
@@ -11952,10 +11958,13 @@ var KTSelect = /** @class */ (function (_super) {
11952
11958
  var selectedOptions = this.getSelectedOptions();
11953
11959
  if (selectedOptions.length === 0)
11954
11960
  return;
11955
- // Get the first selected option element
11956
- var firstSelectedValue = selectedOptions[0];
11957
- // Use the FocusManager to focus on the option
11958
- this._focusManager.focusOptionByValue(firstSelectedValue);
11961
+ // Iterate through selected options and focus the first one that is visible
11962
+ for (var _i = 0, selectedOptions_1 = selectedOptions; _i < selectedOptions_1.length; _i++) {
11963
+ var value = selectedOptions_1[_i];
11964
+ if (this._focusManager && this._focusManager.focusOptionByValue(value)) {
11965
+ break; // Stop after focusing the first found selected and visible option
11966
+ }
11967
+ }
11959
11968
  };
11960
11969
  /**
11961
11970
  * ========================================================================
@@ -12018,38 +12027,90 @@ var KTSelect = /** @class */ (function (_super) {
12018
12027
  */
12019
12028
  KTSelect.prototype.updateSelectedOptionDisplay = function () {
12020
12029
  var selectedOptions = this.getSelectedOptions();
12021
- // Tag mode: render tags if enabled
12022
- if (this._config.tags && this._tagsModule) {
12030
+ var tagsEnabled = this._config.tags && this._tagsModule;
12031
+ var valueDisplayEl = this.getValueDisplayElement();
12032
+ if (tagsEnabled) {
12033
+ // Tags module will render tags if selectedOptions > 0, or clear them if selectedOptions === 0.
12023
12034
  this._tagsModule.updateTagsDisplay(selectedOptions);
12024
- return;
12025
12035
  }
12036
+ // Guard against valueDisplayEl being null due to template modifications
12037
+ if (!valueDisplayEl) {
12038
+ if (this._config.debug) {
12039
+ console.warn('KTSelect: Value display element is null. Cannot update display or placeholder. Check template for [data-kt-select-value].');
12040
+ }
12041
+ return; // Nothing to display on if the element is missing
12042
+ }
12043
+ // 1. Custom render function takes highest precedence
12026
12044
  if (typeof this._config.renderSelected === 'function') {
12027
- // Use the custom renderSelected function if provided
12028
- this._valueDisplayElement.innerHTML = this._config.renderSelected(selectedOptions);
12045
+ valueDisplayEl.innerHTML = this._config.renderSelected(selectedOptions);
12046
+ return;
12047
+ }
12048
+ // 2. Custom displayTemplate string
12049
+ // Check if a custom display template string is provided directly in config or via config.templates
12050
+ var customDisplayTemplateString = this._config.displayTemplate || (this._config.templates && this._config.templates.display);
12051
+ if (customDisplayTemplateString) {
12052
+ // If tags are enabled and items are selected, the tags module handles display, so clear the main display area.
12053
+ if (tagsEnabled && selectedOptions.length > 0) {
12054
+ valueDisplayEl.innerHTML = '';
12055
+ }
12056
+ else {
12057
+ // Otherwise, render the custom display template.
12058
+ // If no options are selected, renderDisplayTemplateForSelected should handle showing a placeholder if the template supports it,
12059
+ // or we might need a separate placeholder rendering for custom templates if they don't handle empty selection.
12060
+ // For now, assume renderDisplayTemplateForSelected handles it or shows selected text.
12061
+ valueDisplayEl.innerHTML = this.renderDisplayTemplateForSelected(selectedOptions);
12062
+ }
12063
+ return;
12064
+ }
12065
+ // 3. Default template behavior (no custom function or string template)
12066
+ var textContainer = valueDisplayEl.querySelector('[data-kt-text-container="true"]');
12067
+ if (tagsEnabled && selectedOptions.length > 0) {
12068
+ // Tags are active and have content, clear the text container if it exists,
12069
+ // or the whole display if it doesn't (though it should with default template).
12070
+ if (textContainer) {
12071
+ textContainer.innerHTML = '';
12072
+ }
12073
+ else {
12074
+ valueDisplayEl.innerHTML = ''; // Fallback: clear entire display area
12075
+ }
12076
+ }
12077
+ else if (selectedOptions.length === 0) {
12078
+ // No options selected: display placeholder text in the text container.
12079
+ var placeholderTemplate = templates_1.defaultTemplates.placeholder(this._config);
12080
+ var placeholderText = placeholderTemplate.textContent || ''; // Get text from placeholder element
12081
+ if (textContainer) {
12082
+ textContainer.innerHTML = placeholderText;
12083
+ }
12084
+ else {
12085
+ // Fallback: If no text container, replace children of valueDisplayEl with the placeholder element itself.
12086
+ // This ensures the placeholder (which might have its own structure/classes) is displayed.
12087
+ valueDisplayEl.replaceChildren(placeholderTemplate);
12088
+ }
12029
12089
  }
12030
12090
  else {
12031
- if (selectedOptions.length === 0) {
12032
- var placeholder = templates_1.defaultTemplates.placeholder(this._config);
12033
- this._valueDisplayElement.replaceChildren(placeholder);
12091
+ // Options are selected, and tags are not enabled (or no selected options for tags):
12092
+ // Render normal selected text in the text container.
12093
+ var content = this.getSelectedOptionsText();
12094
+ if (textContainer) {
12095
+ textContainer.innerHTML = content;
12034
12096
  }
12035
12097
  else {
12036
- var content = '';
12037
- if (this._config.displayTemplate) {
12038
- var selectedValues = this.getSelectedOptions();
12039
- content = this.renderDisplayTemplateForSelected(selectedValues);
12040
- }
12041
- else {
12042
- // If no displayTemplate is provided, use the default comma-separated list of selected options
12043
- content = this.getSelectedOptionsText();
12044
- }
12045
- this._valueDisplayElement.innerHTML = content;
12098
+ valueDisplayEl.innerHTML = content; // Fallback: set content on whole display area
12046
12099
  }
12047
12100
  }
12048
12101
  };
12102
+ /**
12103
+ * Check if an option was originally disabled in the HTML
12104
+ */
12105
+ KTSelect.prototype._isOptionOriginallyDisabled = function (value) {
12106
+ var originalOption = Array.from(this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
12107
+ return originalOption ? originalOption.disabled : false;
12108
+ };
12049
12109
  /**
12050
12110
  * Update CSS classes for selected options
12051
12111
  */
12052
12112
  KTSelect.prototype._updateSelectedOptionClass = function () {
12113
+ var _this = this;
12053
12114
  var allOptions = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
12054
12115
  var selectedValues = this._state.getSelectedOptions();
12055
12116
  var maxReached = typeof this._config.maxSelections === 'number' &&
@@ -12061,9 +12122,11 @@ var KTSelect = /** @class */ (function (_super) {
12061
12122
  if (!optionValue)
12062
12123
  return;
12063
12124
  var isSelected = selectedValues.includes(optionValue);
12125
+ var isOriginallyDisabled = _this._isOptionOriginallyDisabled(optionValue);
12064
12126
  if (isSelected) {
12065
12127
  option.classList.add('selected');
12066
12128
  option.setAttribute('aria-selected', 'true');
12129
+ // Selected options should not be visually hidden or disabled by maxSelections logic
12067
12130
  option.classList.remove('hidden');
12068
12131
  option.classList.remove('disabled');
12069
12132
  option.removeAttribute('aria-disabled');
@@ -12071,7 +12134,8 @@ var KTSelect = /** @class */ (function (_super) {
12071
12134
  else {
12072
12135
  option.classList.remove('selected');
12073
12136
  option.setAttribute('aria-selected', 'false');
12074
- if (maxReached) {
12137
+ // An option should be disabled if it was originally disabled OR if maxSelections is reached
12138
+ if (isOriginallyDisabled || maxReached) {
12075
12139
  option.classList.add('disabled');
12076
12140
  option.setAttribute('aria-disabled', 'true');
12077
12141
  }
@@ -12131,17 +12195,6 @@ var KTSelect = /** @class */ (function (_super) {
12131
12195
  * EVENT HANDLERS
12132
12196
  * ========================================================================
12133
12197
  */
12134
- /**
12135
- * Handle display element click
12136
- * @deprecated
12137
- */
12138
- KTSelect.prototype._handleDropdownClick = function (event) {
12139
- if (this._config.debug)
12140
- console.log('Display element clicked', event.target);
12141
- event.preventDefault();
12142
- event.stopPropagation(); // Prevent event bubbling
12143
- this.toggleDropdown();
12144
- };
12145
12198
  /**
12146
12199
  * Handle click within the dropdown
12147
12200
  */
@@ -12182,6 +12235,13 @@ var KTSelect = /** @class */ (function (_super) {
12182
12235
  }
12183
12236
  if (this._config.debug)
12184
12237
  console.log('Option clicked:', optionValue);
12238
+ // If in single-select mode and the clicked option is already selected, just close the dropdown.
12239
+ if (!this._config.multiple && this._state.isSelected(optionValue)) {
12240
+ if (this._config.debug)
12241
+ console.log('Single select mode: clicked already selected option. Closing dropdown.');
12242
+ this.closeDropdown();
12243
+ return;
12244
+ }
12185
12245
  // Use toggleSelection instead of _selectOption to prevent re-rendering
12186
12246
  this.toggleSelection(optionValue);
12187
12247
  };
@@ -12245,7 +12305,13 @@ var KTSelect = /** @class */ (function (_super) {
12245
12305
  * Get value display element
12246
12306
  */
12247
12307
  KTSelect.prototype.getValueDisplayElement = function () {
12248
- return this._valueDisplayElement;
12308
+ return this._displayElement;
12309
+ };
12310
+ /**
12311
+ * Get wrapper element
12312
+ */
12313
+ KTSelect.prototype.getWrapperElement = function () {
12314
+ return this._wrapperElement;
12249
12315
  };
12250
12316
  /**
12251
12317
  * Show all options in the dropdown
@@ -12255,6 +12321,7 @@ var KTSelect = /** @class */ (function (_super) {
12255
12321
  var options = Array.from(this._wrapperElement.querySelectorAll("[data-kt-select-option]"));
12256
12322
  // Show all options by removing the hidden class and any inline styles
12257
12323
  options.forEach(function (option) {
12324
+ var _a;
12258
12325
  // Remove hidden class
12259
12326
  option.classList.remove('hidden');
12260
12327
  // Clean up any existing inline styles for backward compatibility
@@ -12268,7 +12335,7 @@ var KTSelect = /** @class */ (function (_super) {
12268
12335
  }
12269
12336
  else {
12270
12337
  // Otherwise, remove just the display property
12271
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
12338
+ option.setAttribute('style', (_a = styleAttr === null || styleAttr === void 0 ? void 0 : styleAttr.replace(/display:\s*[^;]+;?/gi, '')) === null || _a === void 0 ? void 0 : _a.trim());
12272
12339
  }
12273
12340
  }
12274
12341
  }
@@ -12278,7 +12345,7 @@ var KTSelect = /** @class */ (function (_super) {
12278
12345
  this._searchInputElement.value = '';
12279
12346
  // If we have a search module, clear any search filtering
12280
12347
  if (this._searchModule) {
12281
- this._searchModule.clearSearchHighlights();
12348
+ this._searchModule.clearSearch();
12282
12349
  }
12283
12350
  }
12284
12351
  };
@@ -12307,7 +12374,7 @@ var KTSelect = /** @class */ (function (_super) {
12307
12374
  // Get current selection state
12308
12375
  var isSelected = this._state.isSelected(value);
12309
12376
  if (this._config.debug)
12310
- console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple, ", closeOnSelect: ").concat(this._config.closeOnSelect));
12377
+ console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple));
12311
12378
  // If already selected in single select mode, do nothing (can't deselect in single select)
12312
12379
  if (isSelected && !this._config.multiple) {
12313
12380
  if (this._config.debug)
@@ -12316,9 +12383,9 @@ var KTSelect = /** @class */ (function (_super) {
12316
12383
  }
12317
12384
  if (this._config.debug)
12318
12385
  console.log("Toggling selection for option: ".concat(value, ", currently selected: ").concat(isSelected));
12319
- // Ensure any search highlights are cleared when selection changes
12386
+ // Ensure any search input is cleared when selection changes
12320
12387
  if (this._searchModule) {
12321
- this._searchModule.clearSearchHighlights();
12388
+ this._searchModule.clearSearch();
12322
12389
  }
12323
12390
  // Toggle the selection in the state
12324
12391
  this._state.toggleSelectedOptions(value);
@@ -12341,15 +12408,14 @@ var KTSelect = /** @class */ (function (_super) {
12341
12408
  // Update option classes without re-rendering the dropdown content
12342
12409
  this._updateSelectedOptionClass();
12343
12410
  // For single select mode, always close the dropdown after selection
12344
- // For multiple select mode, only close if closeOnSelect is true
12345
12411
  if (!this._config.multiple) {
12346
12412
  if (this._config.debug)
12347
12413
  console.log('About to call closeDropdown() for single select mode - always close after selection');
12348
12414
  this.closeDropdown();
12349
12415
  }
12350
- else if (this._config.closeOnSelect) {
12416
+ else {
12351
12417
  if (this._config.debug)
12352
- console.log('About to call closeDropdown() for multiple select with closeOnSelect:true');
12418
+ console.log('About to call closeDropdown() for multiple select');
12353
12419
  this.closeDropdown();
12354
12420
  }
12355
12421
  // Dispatch custom change event with additional data
@@ -12449,9 +12515,9 @@ var KTSelect = /** @class */ (function (_super) {
12449
12515
  .then(function () {
12450
12516
  // Update options in the dropdown
12451
12517
  _this._updateSearchResults(items);
12452
- // Refresh the search module's option cache if search is enabled
12453
- if (_this._searchModule && _this._config.enableSearch) {
12454
- _this._searchModule.refreshOptionCache();
12518
+ // Refresh the search module to update focus and cache
12519
+ if (_this._searchModule) {
12520
+ _this._searchModule.refreshAfterSearch();
12455
12521
  }
12456
12522
  })
12457
12523
  .catch(function (error) {
@@ -12548,19 +12614,47 @@ var KTSelect = /** @class */ (function (_super) {
12548
12614
  * Centralized keyboard event handler for all select modes
12549
12615
  */
12550
12616
  KTSelect.prototype._handleKeyboardEvent = function (event) {
12617
+ // If the event target is the search input and the event was already handled (defaultPrevented),
12618
+ // then return early to avoid duplicate processing by this broader handler.
12619
+ if (event.target === this._searchInputElement && event.defaultPrevented) {
12620
+ return;
12621
+ }
12551
12622
  var isOpen = this._dropdownIsOpen;
12552
12623
  var config = this._config;
12553
12624
  var focusManager = this._focusManager;
12554
12625
  var buffer = this._typeToSearchBuffer;
12555
- // Ignore modifier keys
12626
+ // If the event target is the search input, let it handle most typing keys naturally.
12627
+ if (event.target === this._searchInputElement) {
12628
+ // Allow navigation keys like ArrowDown, ArrowUp, Escape, Enter (for search/selection) to be handled by the logic below.
12629
+ // For other keys (characters, space, backspace, delete), let the input field process them.
12630
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' &&
12631
+ event.key !== 'Escape' && event.key !== 'Enter' && event.key !== 'Tab' &&
12632
+ event.key !== 'Home' && event.key !== 'End') {
12633
+ // If it's a character key and we are NOT type-to-searching (because search has focus)
12634
+ // then let the input field handle it for its own value.
12635
+ // The search module's 'input' event will handle filtering based on the input's value.
12636
+ buffer.clear(); // Clear type-to-search buffer when typing in search field
12637
+ return;
12638
+ }
12639
+ // For Enter specifically in search input, we might want to select the focused option or submit search.
12640
+ // This is handled later in the switch.
12641
+ }
12642
+ // Ignore modifier keys (except for specific combinations if added later)
12556
12643
  if (event.altKey || event.ctrlKey || event.metaKey)
12557
12644
  return;
12558
- // Type-to-search: only for single char keys
12559
- if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/)) {
12645
+ // Type-to-search: only for single char keys, when search input does not have focus
12646
+ if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/) && document.activeElement !== this._searchInputElement) {
12560
12647
  buffer.push(event.key);
12561
12648
  var str = buffer.getBuffer();
12562
- focusManager.focusByString(str);
12563
- return;
12649
+ if (isOpen) {
12650
+ focusManager.focusByString(str);
12651
+ }
12652
+ else {
12653
+ // If closed, type-to-search could potentially open and select.
12654
+ // For now, let's assume it only works when open or opens it first.
12655
+ // Or, we could find the matching option and set it directly without opening.
12656
+ }
12657
+ return; // Type-to-search handles the event
12564
12658
  }
12565
12659
  switch (event.key) {
12566
12660
  case 'ArrowDown':
@@ -12594,18 +12688,28 @@ var KTSelect = /** @class */ (function (_super) {
12594
12688
  case 'Enter':
12595
12689
  case ' ': // Space
12596
12690
  if (isOpen) {
12597
- var focused = focusManager.getFocusedOption();
12598
- if (focused) {
12599
- var value = focused.dataset.value;
12600
- if (value) {
12601
- this.toggleSelection(value);
12602
- if (!config.multiple && config.closeOnSelect) {
12603
- this.closeDropdown();
12604
- }
12691
+ var focusedOptionEl = this._focusManager.getFocusedOption();
12692
+ if (focusedOptionEl) {
12693
+ var val = focusedOptionEl.dataset.value;
12694
+ // If single select, and the item is already selected, just close.
12695
+ if (val !== undefined && !this._config.multiple && this._state.isSelected(val)) {
12696
+ if (this._config.debug)
12697
+ console.log('Enter on already selected item in single-select mode. Closing.');
12698
+ this.closeDropdown();
12699
+ event.preventDefault();
12700
+ break;
12605
12701
  }
12606
12702
  }
12607
- // Prevent form submit
12608
- event.preventDefault();
12703
+ // Proceed with selection if not handled above
12704
+ this.selectFocusedOption();
12705
+ // Close dropdown if configured to do so (for new selections)
12706
+ if (!this._config.multiple) {
12707
+ // This will also be true for the case handled above, but closeDropdown is idempotent.
12708
+ // However, the break above prevents this from being reached for that specific case.
12709
+ this.closeDropdown();
12710
+ }
12711
+ event.preventDefault(); // Prevent form submission or other default actions
12712
+ break;
12609
12713
  }
12610
12714
  else {
12611
12715
  this.openDropdown();
@@ -12649,6 +12753,9 @@ var KTSelect = /** @class */ (function (_super) {
12649
12753
  }).filter(Boolean)));
12650
12754
  return contentArray.join(displaySeparator);
12651
12755
  };
12756
+ KTSelect.prototype.getDisplayElement = function () {
12757
+ return this._displayElement;
12758
+ };
12652
12759
  /**
12653
12760
  * ========================================================================
12654
12761
  * STATIC METHODS
@@ -14564,32 +14671,6 @@ var KTSelectTags = /** @class */ (function () {
14564
14671
  wrapper.insertBefore(tag, _this._valueDisplayElement);
14565
14672
  });
14566
14673
  };
14567
- /**
14568
- * Get the label/text for an option by its value
14569
- */
14570
- KTSelectTags.prototype._getOptionLabel = function (optionValue) {
14571
- var _a, _b;
14572
- // First look for an option element in the dropdown with matching value
14573
- var optionElements = this._select.getOptionsElement();
14574
- for (var _i = 0, _c = Array.from(optionElements); _i < _c.length; _i++) {
14575
- var option = _c[_i];
14576
- if (option.dataset.value === optionValue) {
14577
- return ((_a = option.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || optionValue;
14578
- }
14579
- }
14580
- // If not found in dropdown, look in original select element
14581
- var originalOptions = this._select
14582
- .getElement()
14583
- .querySelectorAll('option');
14584
- for (var _d = 0, _e = Array.from(originalOptions); _d < _e.length; _d++) {
14585
- var option = _e[_d];
14586
- if (option.value === optionValue) {
14587
- return ((_b = option.textContent) === null || _b === void 0 ? void 0 : _b.trim()) || optionValue;
14588
- }
14589
- }
14590
- // If still not found, return the value itself
14591
- return optionValue;
14592
- };
14593
14674
  /**
14594
14675
  * Remove a tag and its selection
14595
14676
  */
@@ -15451,30 +15532,6 @@ var KTDom = {
15451
15532
  }
15452
15533
  return attributes;
15453
15534
  },
15454
- getDataAttributesByJson: function (element, prefix) {
15455
- if (!element) {
15456
- return {};
15457
- }
15458
- var rawValue = element.dataset[prefix];
15459
- if (!rawValue) {
15460
- return {};
15461
- }
15462
- var parsedValue = utils_1.default.parseDataAttribute(rawValue);
15463
- if (typeof parsedValue === 'string') {
15464
- try {
15465
- return JSON.parse(parsedValue);
15466
- }
15467
- catch (e) {
15468
- console.error("Invalid JSON format for '".concat(prefix, "': ").concat(e instanceof Error ? e.message : e, " ").concat(rawValue));
15469
- }
15470
- }
15471
- // If it's already an object, return as is
15472
- if (typeof parsedValue === 'object' && parsedValue !== null) {
15473
- return parsedValue;
15474
- }
15475
- // For other types (number, boolean, null), return an empty object
15476
- return {};
15477
- },
15478
15535
  ready: function (callback) {
15479
15536
  if (document.readyState === 'loading') {
15480
15537
  document.addEventListener('DOMContentLoaded', function () {
@@ -15492,7 +15549,7 @@ exports["default"] = KTDom;
15492
15549
  /***/ }),
15493
15550
 
15494
15551
  /***/ 9011:
15495
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
15552
+ /***/ (function(__unused_webpack_module, exports) {
15496
15553
 
15497
15554
 
15498
15555
  /**
@@ -15500,15 +15557,12 @@ exports["default"] = KTDom;
15500
15557
  * Copyright 2025 by Keenthemes Inc
15501
15558
  */
15502
15559
  Object.defineProperty(exports, "__esModule", ({ value: true }));
15503
- exports.TypeToSearchBuffer = exports.EventManager = exports.FocusManager = exports.highlightTextInElementDebounced = void 0;
15560
+ exports.TypeToSearchBuffer = exports.EventManager = exports.FocusManager = void 0;
15504
15561
  exports.formatCurrency = formatCurrency;
15505
15562
  exports.filterOptions = filterOptions;
15506
- exports.highlightTextInElement = highlightTextInElement;
15507
15563
  exports.debounce = debounce;
15508
15564
  exports.renderTemplateString = renderTemplateString;
15509
15565
  exports.stringToElement = stringToElement;
15510
- // utils.ts
15511
- var templates_1 = __webpack_require__(9069);
15512
15566
  /**
15513
15567
  * Format a number as a currency string
15514
15568
  */
@@ -15522,148 +15576,50 @@ function formatCurrency(value) {
15522
15576
  * Filter options based on a search query
15523
15577
  */
15524
15578
  function filterOptions(options, query, config, dropdownElement, onVisibleCount) {
15525
- var _a;
15526
15579
  var visibleOptionsCount = 0;
15527
- // Clear existing "no results" messages
15528
- var noResultsElement = dropdownElement.querySelector('[data-kt-select-no-results]');
15529
- if (noResultsElement) {
15530
- noResultsElement.remove();
15531
- }
15532
- // For empty query, ensure highlights are cleared from all options
15580
+ // For empty query, make all options visible
15581
+ // The KTSelectSearch class is now responsible for restoring original content before calling this.
15533
15582
  if (!query || query.trim() === '') {
15534
- // Just make all options visible without highlighting
15535
15583
  for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
15536
15584
  var option = options_1[_i];
15537
- // Make option visible by removing hidden classes and inline styles
15538
15585
  option.classList.remove('hidden');
15539
- // Clean up any inline display styles from legacy code
15540
- if (option.hasAttribute('style') &&
15541
- option.getAttribute('style').includes('display:')) {
15542
- var styleAttr = option.getAttribute('style');
15543
- if (styleAttr.trim() === 'display: none;' ||
15544
- styleAttr.trim() === 'display: block;') {
15545
- option.removeAttribute('style');
15546
- }
15547
- else {
15548
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
15549
- }
15550
- }
15551
- // Clear highlights by restoring original text content
15552
- if (option.dataset && option.dataset.originalText) {
15553
- option.innerHTML = option.dataset.originalText;
15554
- }
15555
- else {
15556
- option.innerHTML = option.textContent || '';
15557
- }
15558
- // Remove the cache if present
15559
- if (option.dataset && option.dataset.originalText) {
15560
- delete option.dataset.originalText;
15586
+ // Remove inline display style if it was used to hide
15587
+ if (option.style.display === 'none') {
15588
+ option.style.display = '';
15561
15589
  }
15590
+ // At this point, option.innerHTML should be its original.
15562
15591
  visibleOptionsCount++;
15563
15592
  }
15564
- // Call the callback with the visible count if provided
15565
15593
  if (onVisibleCount) {
15566
15594
  onVisibleCount(visibleOptionsCount);
15567
15595
  }
15568
15596
  return visibleOptionsCount;
15569
15597
  }
15570
- // Filter options based on query
15571
- for (var _b = 0, options_2 = options; _b < options_2.length; _b++) {
15572
- var option = options_2[_b];
15573
- var optionText = ((_a = option.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
15574
- var isMatch = optionText.includes(query.toLowerCase());
15575
- // Check if option is disabled
15576
- var isDisabled = option.classList.contains('disabled') || option.getAttribute('aria-disabled') === 'true';
15577
- if (isMatch || query.trim() === '') {
15578
- // Show option by removing the hidden class and any display inline styles
15598
+ var queryLower = query.toLowerCase();
15599
+ for (var _a = 0, options_2 = options; _a < options_2.length; _a++) {
15600
+ var option = options_2[_a];
15601
+ // Use data-text for matching if available, otherwise fall back to textContent
15602
+ var optionText = (option.dataset.text || option.textContent || '').toLowerCase();
15603
+ var isMatch = optionText.includes(queryLower);
15604
+ if (isMatch) {
15579
15605
  option.classList.remove('hidden');
15580
- // Remove any inline display styles that might be present
15581
- if (option.hasAttribute('style') &&
15582
- option.getAttribute('style').includes('display:')) {
15583
- var styleAttr = option.getAttribute('style');
15584
- if (styleAttr.trim() === 'display: none;' ||
15585
- styleAttr.trim() === 'display: block;') {
15586
- option.removeAttribute('style');
15587
- }
15588
- else {
15589
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
15590
- }
15591
- }
15606
+ if (option.style.display === 'none')
15607
+ option.style.display = ''; // Ensure visible
15592
15608
  visibleOptionsCount++;
15593
- if (config.searchHighlight && query.trim() !== '') {
15594
- if (option.dataset && !option.dataset.originalText) {
15595
- option.dataset.originalText = option.innerHTML;
15596
- }
15597
- (0, exports.highlightTextInElementDebounced)(option, query, config);
15598
- }
15599
15609
  }
15600
15610
  else {
15601
- // Hide option using hidden class
15602
15611
  option.classList.add('hidden');
15603
- // Remove any inline display styles
15604
- if (option.hasAttribute('style') &&
15605
- option.getAttribute('style').includes('display:')) {
15606
- var styleAttr = option.getAttribute('style');
15607
- if (styleAttr.trim() === 'display: none;' ||
15608
- styleAttr.trim() === 'display: block;') {
15609
- option.removeAttribute('style');
15610
- }
15611
- else {
15612
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
15613
- }
15614
- }
15615
- }
15616
- // Early exit if maxItems limit is reached
15617
- if (config.searchMaxItems && visibleOptionsCount >= config.searchMaxItems) {
15618
- break;
15619
15612
  }
15613
+ // Early exit if maxItems limit is reached (optional)
15614
+ // if (config.searchMaxItems && visibleOptionsCount >= config.searchMaxItems) {
15615
+ // break;
15616
+ // }
15620
15617
  }
15621
- // Call the callback with the visible count if provided
15622
15618
  if (onVisibleCount) {
15623
15619
  onVisibleCount(visibleOptionsCount);
15624
15620
  }
15625
15621
  return visibleOptionsCount;
15626
15622
  }
15627
- /**
15628
- * Highlight text only within a specific element, preserving other elements
15629
- */
15630
- function highlightTextInElement(element, query, config) {
15631
- if (!element || !query || query.trim() === '')
15632
- return;
15633
- var queryLower = query.toLowerCase();
15634
- var text = element.textContent || '';
15635
- if (!text)
15636
- return;
15637
- // Escape regex special characters in query
15638
- var escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15639
- var regex = new RegExp(escapedQuery, 'gi');
15640
- // Replace all matches with the highlight template
15641
- var lastIndex = 0;
15642
- var result = '';
15643
- var match;
15644
- var matches = [];
15645
- while ((match = regex.exec(text)) !== null) {
15646
- matches.push({ start: match.index, end: regex.lastIndex });
15647
- }
15648
- if (matches.length === 0) {
15649
- element.innerHTML = text;
15650
- return;
15651
- }
15652
- for (var i = 0; i < matches.length; i++) {
15653
- var _a = matches[i], start = _a.start, end = _a.end;
15654
- // Add text before match
15655
- result += text.slice(lastIndex, start);
15656
- // Add highlighted match using template
15657
- var highlighted = templates_1.defaultTemplates.highlight(config, text.slice(start, end)).outerHTML;
15658
- result += highlighted;
15659
- lastIndex = end;
15660
- }
15661
- // Add remaining text
15662
- result += text.slice(lastIndex);
15663
- element.innerHTML = result;
15664
- }
15665
- // Debounced version for performance
15666
- exports.highlightTextInElementDebounced = debounce(highlightTextInElement, 100);
15667
15623
  /**
15668
15624
  * Focus manager for keyboard navigation
15669
15625
  * Consolidates redundant focus management logic into shared functions
@@ -15765,6 +15721,7 @@ var FocusManager = /** @class */ (function () {
15765
15721
  if (!option.classList.contains('disabled') &&
15766
15722
  option.getAttribute('aria-disabled') !== 'true' &&
15767
15723
  (((_b = option.textContent) === null || _b === void 0 ? void 0 : _b.toLowerCase().startsWith(lowerStr)) || ((_c = option.dataset.value) === null || _c === void 0 ? void 0 : _c.toLowerCase().startsWith(lowerStr)))) {
15724
+ this.resetFocus();
15768
15725
  this._focusedOptionIndex = idx;
15769
15726
  this.applyFocus(option);
15770
15727
  this.scrollIntoView(option);
@@ -15823,12 +15780,14 @@ var FocusManager = /** @class */ (function () {
15823
15780
  FocusManager.prototype.applyFocus = function (option) {
15824
15781
  if (!option)
15825
15782
  return;
15783
+ // Ensure it's not disabled
15826
15784
  if (option.classList.contains('disabled') || option.getAttribute('aria-disabled') === 'true') {
15827
15785
  return;
15828
15786
  }
15829
- this.resetFocus();
15787
+ // DO NOT CALL resetFocus() here. Caller's responsibility.
15830
15788
  option.classList.add(this._focusClass);
15831
15789
  option.classList.add(this._hoverClass);
15790
+ // _triggerFocusChange needs _focusedOptionIndex to be set by the caller before this.
15832
15791
  this._triggerFocusChange();
15833
15792
  };
15834
15793
  /**
@@ -15841,12 +15800,7 @@ var FocusManager = /** @class */ (function () {
15841
15800
  focusedElements.forEach(function (element) {
15842
15801
  element.classList.remove(_this._focusClass, _this._hoverClass);
15843
15802
  });
15844
- // Reset index if visible options have changed
15845
- var visibleOptions = this.getVisibleOptions();
15846
- if (this._focusedOptionIndex !== null &&
15847
- this._focusedOptionIndex >= visibleOptions.length) {
15848
- this._focusedOptionIndex = null;
15849
- }
15803
+ this._focusedOptionIndex = null; // Always reset the index
15850
15804
  };
15851
15805
  /**
15852
15806
  * Ensure the focused option is visible in the scrollable container
@@ -15875,10 +15829,14 @@ var FocusManager = /** @class */ (function () {
15875
15829
  var options = this.getVisibleOptions();
15876
15830
  var index = options.findIndex(function (option) { return option.dataset.value === value; });
15877
15831
  if (index >= 0) {
15878
- this._focusedOptionIndex = index;
15879
- this.applyFocus(options[index]);
15880
- this.scrollIntoView(options[index]);
15881
- return true;
15832
+ var optionToFocus = options[index];
15833
+ if (!optionToFocus.classList.contains('disabled') && optionToFocus.getAttribute('aria-disabled') !== 'true') {
15834
+ this.resetFocus();
15835
+ this._focusedOptionIndex = index;
15836
+ this.applyFocus(optionToFocus);
15837
+ this.scrollIntoView(optionToFocus);
15838
+ return true;
15839
+ }
15882
15840
  }
15883
15841
  return false;
15884
15842
  };
@@ -16095,20 +16053,19 @@ var utils_1 = __webpack_require__(9011);
16095
16053
  * Users can override any template by providing a matching key in the config.templates object.
16096
16054
  */
16097
16055
  exports.coreTemplateStrings = {
16098
- dropdown: "<div data-kt-select-dropdown class=\"kt-select-dropdown hidden {{class}}\" style=\"z-index: {{zindex}};\">{{content}}</div>",
16099
- options: "<ul role=\"listbox\" aria-label=\"{{label}}\" class=\"kt-select-options {{class}}\" data-kt-select-options=\"true\">{{content}}</ul>",
16100
- error: "<li class=\"kt-select-error\" role=\"alert\">{{content}}</li>",
16101
- highlight: "<span data-kt-select-highlight class=\"kt-select-highlight highlighted {{class}}\">{{text}}</span>",
16056
+ dropdown: "<div data-kt-select-dropdown class=\"kt-select-dropdown hidden {{class}}\" style=\"z-index: {{zindex}};\"></div>",
16057
+ options: "<ul role=\"listbox\" aria-label=\"{{label}}\" class=\"kt-select-options {{class}}\" data-kt-select-options=\"true\"></ul>",
16058
+ error: "<li class=\"kt-select-error\" role=\"alert\"></li>",
16102
16059
  wrapper: "<div data-kt-select-wrapper class=\"kt-select-wrapper {{class}}\"></div>",
16103
- combobox: "\n\t\t<div data-kt-select-combobox data-kt-select-display class=\"kt-select-combobox {{class}}\">\n\t\t\t<input class=\"kt-input kt-select-combobox-input\" data-kt-select-search=\"true\" data-kt-select-value=\"true\" type=\"text\" placeholder=\"{{placeholder}}\" role=\"searchbox\" aria-label=\"{{label}}\" {{disabled}} />\n\t\t\t<button type=\"button\" data-kt-select-clear-button=\"true\" class=\"kt-select-combobox-clear-btn\" aria-label=\"Clear selection\">\n\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n\t\t\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n\t\t\t\t</svg>\n\t\t\t</button>\n\t\t</div>\n\t",
16104
- display: "\n\t\t<div data-kt-select-display class=\"kt-select-display {{class}}\" tabindex=\"{{tabindex}}\" role=\"button\" data-selected=\"0\" aria-haspopup=\"listbox\" aria-expanded=\"false\" aria-label=\"{{label}}\" {{disabled}}>\n\t\t\t<div data-kt-select-value=\"true\" class=\"kt-select-label\">{{content}}</div>\n\t\t</div>\n\t",
16105
- placeholder: "<div data-kt-select-placeholder class=\"kt-select-placeholder {{class}}\">{{content}}</div>",
16106
- option: "<li data-kt-select-option data-value=\"{{value}}\" data-text=\"{{text}}\" class=\"kt-select-option {{class}}\" role=\"option\" {{selected}} {{disabled}}>{{content}}</li>",
16107
- search: "<div data-kt-select-search class=\"kt-select-search {{class}}\"><input type=\"text\" data-kt-select-search=\"true\" placeholder=\"{{searchPlaceholder}}\" class=\"kt-input kt-select-search-input\" role=\"searchbox\" aria-label=\"{{searchPlaceholder}}\"/></div>",
16108
- empty: "<li data-kt-select-empty class=\"kt-select-no-result {{class}}\" role=\"status\">{{content}}</li>",
16109
- loading: "<li class=\"kt-select-loading {{class}}\" role=\"status\" aria-live=\"polite\">{{content}}</li>",
16110
- tag: "<div data-kt-select-tag=\"true\" class=\"kt-select-tag {{class}}\">\n\t\t\t{{content}}\n\t\t</div>",
16111
- loadMore: "<li class=\"kt-select-load-more {{class}}\" data-kt-select-load-more=\"true\">{{content}}</li>",
16060
+ combobox: "\n\t\t<div data-kt-select-combobox data-kt-select-display class=\"kt-select-combobox {{class}}\">\n\t\t\t<div data-kt-select-combobox-values=\"true\" class=\"kt-select-combobox-values\"></div>\n\t\t\t<input class=\"kt-input kt-select-combobox-input\" data-kt-select-search=\"true\" type=\"text\" placeholder=\"{{placeholder}}\" role=\"searchbox\" aria-label=\"{{label}}\" {{disabled}} />\n\t\t\t<button type=\"button\" data-kt-select-clear-button=\"true\" class=\"kt-select-combobox-clear-btn\" aria-label=\"Clear selection\">\n\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n\t\t\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n\t\t\t\t</svg>\n\t\t\t</button>\n\t\t</div>\n\t",
16061
+ placeholder: "<div data-kt-select-placeholder class=\"kt-select-placeholder {{class}}\"></div>",
16062
+ display: "\n\t\t<div data-kt-select-display class=\"kt-select-display {{class}}\" tabindex=\"{{tabindex}}\" role=\"button\" data-selected=\"0\" aria-haspopup=\"listbox\" aria-expanded=\"false\" aria-label=\"{{label}}\" {{disabled}}>\n\t\t\t<div class=\"kt-select-option-text\" data-kt-text-container=\"true\">{{text}}</div>\n\t\t</div>\n\t",
16063
+ option: "\n\t\t<li data-kt-select-option data-value=\"{{value}}\" data-text=\"{{text}}\" class=\"kt-select-option {{class}}\" role=\"option\" {{selected}} {{disabled}}>\n\t\t\t<div class=\"kt-select-option-text\" data-kt-text-container=\"true\">{{text}}</div><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-3.5 ms-auto hidden text-primary kt-select-option-selected:block\"><path d=\"M20 6 9 17l-5-5\"/></svg>\n\t\t</li>\n\t",
16064
+ search: "<div data-kt-select-search class=\"kt-select-search {{class}}\"><input type=\"text\" data-kt-select-search=\"true\" placeholder=\"{{searchPlaceholder}}\" class=\"kt-input kt-input-ghost\" role=\"searchbox\" aria-label=\"{{searchPlaceholder}}\"/></div>",
16065
+ empty: "<li data-kt-select-empty class=\"kt-select-no-result {{class}}\" role=\"status\"></li>",
16066
+ loading: "<li class=\"kt-select-loading {{class}}\" role=\"status\" aria-live=\"polite\"></li>",
16067
+ tag: "<div data-kt-select-tag=\"true\" class=\"kt-select-tag {{class}}\"></div>",
16068
+ loadMore: "<li class=\"kt-select-load-more {{class}}\" data-kt-select-load-more=\"true\"></li>",
16112
16069
  tagRemoveButton: "<button type=\"button\" data-kt-select-remove-button class=\"kt-select-tag-remove\" aria-label=\"Remove tag\" tabindex=\"0\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"3\" x2=\"9\" y2=\"9\"/><line x1=\"9\" y1=\"3\" x2=\"3\" y2=\"9\"/></svg></button>",
16113
16070
  };
16114
16071
  /**
@@ -16147,30 +16104,31 @@ function getTemplateStrings(config) {
16147
16104
  * Default templates for KTSelect component
16148
16105
  */
16149
16106
  exports.defaultTemplates = {
16150
- /**
16151
- * Renders a highlighted text
16152
- */
16153
- highlight: function (config, text) {
16154
- var template = getTemplateStrings(config).highlight;
16155
- var html = template.replace('{{text}}', text).replace('{{class}}', config.highlightClass || '');
16156
- return stringToElement(html);
16157
- },
16158
16107
  /**
16159
16108
  * Renders the dropdown content
16160
16109
  */
16161
16110
  dropdown: function (config) {
16111
+ var _a;
16162
16112
  var template = getTemplateStrings(config).dropdown;
16163
- var content = config.content || '';
16113
+ // If a custom dropdownTemplate is provided, it's responsible for its own content.
16114
+ // Otherwise, the base template is used, and content is appended later.
16164
16115
  if (config.dropdownTemplate) {
16165
- content = (0, utils_1.renderTemplateString)(config.dropdownTemplate, {
16116
+ var renderedCustomTemplate = (0, utils_1.renderTemplateString)(config.dropdownTemplate, {
16166
16117
  zindex: config.zindex ? String(config.zindex) : '',
16167
- content: config.content || '',
16118
+ // content: config.content || '', // No longer pass content to custom template directly here
16168
16119
  class: config.dropdownClass || '',
16169
16120
  });
16121
+ // The custom template IS the dropdown element
16122
+ var customDropdownEl = stringToElement(renderedCustomTemplate);
16123
+ if (config.zindex)
16124
+ customDropdownEl.style.zIndex = String(config.zindex);
16125
+ if (config.dropdownClass)
16126
+ (_a = customDropdownEl.classList).add.apply(_a, config.dropdownClass.split(' '));
16127
+ return customDropdownEl;
16170
16128
  }
16171
16129
  var html = template
16172
16130
  .replace('{{zindex}}', config.zindex ? String(config.zindex) : '')
16173
- .replace('{{content}}', content)
16131
+ // .replace('{{content}}', '') // Content is no longer part of the base template string
16174
16132
  .replace('{{class}}', config.dropdownClass || '');
16175
16133
  return stringToElement(html);
16176
16134
  },
@@ -16182,7 +16140,7 @@ exports.defaultTemplates = {
16182
16140
  var html = template
16183
16141
  .replace('{{label}}', config.label || 'Options')
16184
16142
  .replace('{{height}}', config.height ? String(config.height) : '250')
16185
- .replace('{{options}}', config.options || '')
16143
+ // .replace('{{options}}', '') // Options are now appended dynamically
16186
16144
  .replace('{{class}}', config.optionsClass || '');
16187
16145
  return stringToElement(html);
16188
16146
  },
@@ -16190,24 +16148,31 @@ exports.defaultTemplates = {
16190
16148
  * Renders the load more button for pagination
16191
16149
  */
16192
16150
  loadMore: function (config) {
16193
- var html = getTemplateStrings(config).loadMore.replace('{{loadMoreText}}', config.loadMoreText || 'Load more...');
16194
- return stringToElement(html);
16151
+ var html = getTemplateStrings(config)
16152
+ .loadMore // .replace('{{loadMoreText}}', config.loadMoreText || 'Load more...') // Content is no longer in template string
16153
+ .replace('{{class}}', config.loadMoreClass || '');
16154
+ var element = stringToElement(html);
16155
+ element.textContent = config.loadMoreText || 'Load more...';
16156
+ return element;
16195
16157
  },
16196
16158
  /**
16197
16159
  * Renders an error message in the dropdown
16198
16160
  */
16199
16161
  error: function (config) {
16162
+ // Changed return type to HTMLElement
16200
16163
  var template = getTemplateStrings(config).error;
16201
- return template
16202
- .replace('{{errorMessage}}', config.errorMessage || 'An error occurred')
16164
+ var html = template
16165
+ // .replace('{{errorMessage}}', config.errorMessage || 'An error occurred') // Content is no longer in template string
16203
16166
  .replace('{{class}}', config.errorClass || '');
16167
+ var element = stringToElement(html);
16168
+ element.textContent = config.errorMessage || 'An error occurred';
16169
+ return element;
16204
16170
  },
16205
16171
  /**
16206
16172
  * Renders the main container for the select component
16207
16173
  */
16208
16174
  wrapper: function (config) {
16209
- var html = getTemplateStrings(config).wrapper
16210
- .replace('{{class}}', config.wrapperClass || '');
16175
+ var html = getTemplateStrings(config).wrapper.replace('{{class}}', config.wrapperClass || '');
16211
16176
  var element = stringToElement(html);
16212
16177
  element.setAttribute('data-kt-select-combobox', config.combobox ? 'true' : 'false');
16213
16178
  element.setAttribute('data-kt-select-tags', config.tags ? 'true' : 'false');
@@ -16217,57 +16182,66 @@ exports.defaultTemplates = {
16217
16182
  * Renders the display element (trigger) for the select
16218
16183
  */
16219
16184
  display: function (config) {
16220
- if (config.combobox) {
16221
- var html_1 = getTemplateStrings(config)
16222
- .combobox.replace(/{{placeholder}}/g, config.placeholder || 'Select...')
16223
- .replace(/{{label}}/g, config.label || config.placeholder || 'Select...')
16224
- .replace('{{disabled}}', config.disabled ? 'disabled' : '')
16225
- .replace('{{class}}', config.displayClass || '');
16226
- return stringToElement(html_1);
16227
- }
16228
- var content = config.label || config.placeholder || 'Select...';
16229
- var html = getTemplateStrings(config).display
16230
- .replace('{{tabindex}}', config.disabled ? '-1' : '0')
16185
+ var html = getTemplateStrings(config)
16186
+ .display.replace('{{tabindex}}', config.disabled ? '-1' : '0')
16231
16187
  .replace('{{label}}', config.label || config.placeholder || 'Select...')
16232
16188
  .replace('{{disabled}}', config.disabled ? 'aria-disabled="true"' : '')
16233
16189
  .replace('{{placeholder}}', config.placeholder || 'Select...')
16234
- .replace('{{class}}', config.displayClass || '')
16235
- .replace('{{content}}', content);
16236
- return stringToElement(html);
16190
+ .replace('{{class}}', config.displayClass || '');
16191
+ var element = stringToElement(html);
16192
+ // Add data-multiple attribute if in multiple select mode
16193
+ if (config.multiple) {
16194
+ element.setAttribute('data-multiple', 'true');
16195
+ }
16196
+ return element;
16237
16197
  },
16238
16198
  /**
16239
16199
  * Renders a single option
16240
16200
  */
16241
16201
  option: function (option, config) {
16202
+ var _a, _b;
16242
16203
  var isHtmlOption = option instanceof HTMLOptionElement;
16243
- var value = isHtmlOption ? option.value : option.id;
16244
- var text = isHtmlOption ? option.text : option.title;
16245
- var disabled = isHtmlOption
16246
- ? option.disabled
16247
- : option.disabled === true;
16248
- var selected = isHtmlOption
16249
- ? option.selected
16250
- : !!option.selected;
16251
- var content = text;
16204
+ var optionData;
16205
+ if (isHtmlOption) {
16206
+ // If it's a plain HTMLOptionElement, construct data similarly to how KTSelectOption would
16207
+ // This branch might be less common if KTSelectOption instances are always used for rendering.
16208
+ var el = option;
16209
+ var textContent = el.textContent || '';
16210
+ optionData = __assign({ value: el.value, text: textContent, selected: el.selected, disabled: el.disabled, content: textContent }, (((_a = config.optionsConfig) === null || _a === void 0 ? void 0 : _a[el.value]) || {}));
16211
+ }
16212
+ else {
16213
+ // If it's a KTSelectOption class instance (from './option')
16214
+ // which should have the getOptionDataForTemplate method.
16215
+ optionData = option.getOptionDataForTemplate();
16216
+ }
16217
+ var content = optionData.text; // Default content to option's text
16252
16218
  if (config.optionTemplate) {
16253
- // Use the user template to render the content, but only for {{content}}
16254
- content = (0, utils_1.renderTemplateString)(config.optionTemplate, {
16255
- value: value,
16256
- text: text,
16257
- class: config.optionClass || '',
16258
- selected: selected ? 'aria-selected="true"' : 'aria-selected="false"',
16259
- disabled: disabled ? 'aria-disabled="true"' : '',
16260
- content: text,
16261
- });
16219
+ // Use the user-provided template string, rendering with the full optionData.
16220
+ // renderTemplateString will replace {{key}} with values from optionData.
16221
+ content = (0, utils_1.renderTemplateString)(config.optionTemplate, optionData);
16262
16222
  }
16263
- var html = getTemplateStrings(config).option
16264
- .replace('{{value}}', value)
16265
- .replace('{{text}}', text)
16266
- .replace('{{selected}}', selected ? 'aria-selected="true"' : 'aria-selected="false"')
16267
- .replace('{{disabled}}', disabled ? 'aria-disabled="true"' : '')
16268
- .replace('{{content}}', content)
16269
- .replace('{{class}}', config.optionClass || '');
16270
- return stringToElement(html);
16223
+ else {
16224
+ content = optionData.text || optionData.content; // Prefer explicit text, fallback to content
16225
+ }
16226
+ // Use the core option template string as the base structure.
16227
+ var baseTemplate = getTemplateStrings(config).option;
16228
+ var optionClasses = [config.optionClass || ''];
16229
+ if (optionData.disabled) {
16230
+ optionClasses.push('disabled');
16231
+ }
16232
+ // Populate the base template for the <li> attributes.
16233
+ // The actual display content (text or custom HTML) will be set on the inner span later.
16234
+ var html = (0, utils_1.renderTemplateString)(baseTemplate, __assign(__assign({}, optionData), { class: optionClasses.join(' ').trim() || '', selected: optionData.selected
16235
+ ? 'aria-selected="true"'
16236
+ : 'aria-selected="false"', disabled: optionData.disabled ? 'aria-disabled="true"' : '', content: content }));
16237
+ var element = stringToElement(html);
16238
+ // If a custom option template is provided, replace the element's innerHTML with the content.
16239
+ if (config.optionTemplate) {
16240
+ element.innerHTML = content;
16241
+ }
16242
+ // Ensure data-text attribute is set to the original, clean text for searching/filtering
16243
+ element.setAttribute('data-text', ((_b = optionData === null || optionData === void 0 ? void 0 : optionData.text) === null || _b === void 0 ? void 0 : _b.trim()) || '');
16244
+ return element;
16271
16245
  },
16272
16246
  /**
16273
16247
  * Renders the search input
@@ -16282,68 +16256,79 @@ exports.defaultTemplates = {
16282
16256
  * Renders the no results message
16283
16257
  */
16284
16258
  empty: function (config) {
16285
- var html = getTemplateStrings(config)
16286
- .empty.replace('{{searchNotFoundText}}', config.searchNotFoundText || 'No results found')
16287
- .replace('{{class}}', config.emptyClass || '');
16288
- return stringToElement(html);
16259
+ var html = getTemplateStrings(config).empty.replace('{{class}}', config.emptyClass || '');
16260
+ var element = stringToElement(html);
16261
+ element.textContent = config.searchNotFoundText || 'No results found';
16262
+ return element;
16289
16263
  },
16290
16264
  /**
16291
16265
  * Renders the loading state
16292
16266
  */
16293
16267
  loading: function (config, loadingMessage) {
16294
- var html = getTemplateStrings(config)
16295
- .loading.replace('{{loadingMessage}}', loadingMessage || 'Loading options...')
16296
- .replace('{{class}}', config.loadingClass || '');
16297
- return stringToElement(html);
16268
+ var html = getTemplateStrings(config).loading.replace('{{class}}', config.loadingClass || '');
16269
+ var element = stringToElement(html);
16270
+ element.textContent = loadingMessage || 'Loading options...';
16271
+ return element;
16298
16272
  },
16299
16273
  /**
16300
16274
  * Renders a tag for multi-select
16301
16275
  */
16302
16276
  tag: function (option, config) {
16277
+ var _a;
16303
16278
  var template = getTemplateStrings(config).tag;
16304
- var content = option.title;
16279
+ var preparedContent = option.title; // Default content is the option's title
16305
16280
  if (config.tagTemplate) {
16306
- var tagTemplate_1 = config.tagTemplate;
16307
- var text = option.getAttribute('data-text');
16308
- var value = option.getAttribute('data-value');
16309
- // Replace all {{varname}} in option.innerHTML with values from _config
16310
- Object.entries(config.optionsConfig[value] || {}).forEach(function (_a) {
16311
- var key = _a[0], value = _a[1];
16312
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
16313
- tagTemplate_1 = tagTemplate_1.replace(new RegExp("{{".concat(key, "}}"), 'g'), String(value));
16281
+ var tagTemplateString_1 = config.tagTemplate;
16282
+ var optionValue = option.getAttribute('data-value') || option.value;
16283
+ // Replace all {{varname}} in option.innerHTML with values from _config.optionsConfig
16284
+ Object.entries(((_a = config.optionsConfig) === null || _a === void 0 ? void 0 : _a[optionValue]) || {}).forEach(function (_a) {
16285
+ var key = _a[0], val = _a[1];
16286
+ if (typeof val === 'string' ||
16287
+ typeof val === 'number' ||
16288
+ typeof val === 'boolean') {
16289
+ tagTemplateString_1 = tagTemplateString_1.replace(new RegExp("{{".concat(key, "}}"), 'g'), String(val));
16314
16290
  }
16315
16291
  });
16316
- content = (0, utils_1.renderTemplateString)(tagTemplate_1, {
16292
+ // Render the custom tag template with option data
16293
+ preparedContent = (0, utils_1.renderTemplateString)(tagTemplateString_1, {
16317
16294
  title: option.title,
16318
16295
  id: option.id,
16319
- class: config.tagClass || '',
16320
- content: option.innerHTML,
16321
- text: option.innerText,
16296
+ class: config.tagClass || '', // This class is for content, not the main tag div
16297
+ // content: option.innerHTML, // Avoid direct innerHTML from option due to potential XSS
16298
+ text: option.innerText || option.textContent || '',
16299
+ value: optionValue,
16322
16300
  });
16323
16301
  }
16324
- content += getTemplateStrings(config).tagRemoveButton;
16302
+ // Append the remove button HTML string to the prepared content
16303
+ preparedContent += getTemplateStrings(config).tagRemoveButton;
16325
16304
  var html = template
16326
- .replace('{{title}}', option.title)
16327
- .replace('{{id}}', option.id)
16328
- .replace('{{content}}', content)
16329
- .replace('{{class}}', config.tagClass || '');
16330
- return stringToElement(html);
16305
+ // .replace('{{title}}', option.title) // Title is part of preparedContent if using custom template
16306
+ // .replace('{{id}}', option.id) // ID is part of preparedContent if using custom template
16307
+ .replace('{{class}}', config.tagClass || ''); // Class for the main tag div
16308
+ var element = stringToElement(html);
16309
+ element.innerHTML = preparedContent; // Set the fully prepared content (text/HTML + remove button)
16310
+ return element;
16331
16311
  },
16332
16312
  /**
16333
16313
  * Renders the placeholder for the select
16334
16314
  */
16335
16315
  placeholder: function (config) {
16336
- var html = getTemplateStrings(config)
16337
- .placeholder.replace('{{class}}', config.placeholderClass || '');
16316
+ var html = getTemplateStrings(config).placeholder.replace('{{class}}', config.placeholderClass || '');
16338
16317
  var content = config.placeholder || 'Select...';
16339
16318
  if (config.placeholderTemplate) {
16340
16319
  content = (0, utils_1.renderTemplateString)(config.placeholderTemplate, {
16341
16320
  placeholder: config.placeholder || 'Select...',
16342
16321
  class: config.placeholderClass || '',
16343
16322
  });
16323
+ var element = stringToElement(html);
16324
+ element.innerHTML = content; // For templates, content can be HTML
16325
+ return element;
16326
+ }
16327
+ else {
16328
+ var element = stringToElement(html);
16329
+ element.textContent = content; // For simple text, use textContent
16330
+ return element;
16344
16331
  }
16345
- html = html.replace('{{content}}', content);
16346
- return stringToElement(html);
16347
16332
  },
16348
16333
  };
16349
16334
 
@@ -17494,7 +17479,6 @@ exports.DefaultConfig = {
17494
17479
  // Selection Behavior
17495
17480
  multiple: false, // Enable/disable multi-select
17496
17481
  maxSelections: null, // Maximum number of selections allowed in multi-select mode (null for unlimited)
17497
- closeOnSelect: false, // Close the dropdown after selecting an option (single-select only)
17498
17482
  disabled: false, // Disable the select component
17499
17483
  isRequired: false, // Make selection required
17500
17484
  // Search Functionality
@@ -17504,7 +17488,6 @@ exports.DefaultConfig = {
17504
17488
  searchMinLength: 0, // Minimum characters required to trigger search
17505
17489
  searchMaxItems: 50, // Maximum number of search results to display
17506
17490
  searchNotFoundText: 'No results found', // Text to display when no search results are found
17507
- searchHighlight: true, // Highlight matching search terms within the options
17508
17491
  clearSearchOnClose: true, // Clear search input when dropdown closes
17509
17492
  // Multi-Select Display
17510
17493
  selectAllText: 'Select all', // Text for the "Select All" option (if implemented)
@@ -17529,7 +17512,7 @@ var KTSelectState = /** @class */ (function () {
17529
17512
  this._config = this._initDefaultConfig(config);
17530
17513
  }
17531
17514
  KTSelectState.prototype._initDefaultConfig = function (config) {
17532
- return __assign(__assign({}, exports.DefaultConfig), config);
17515
+ return __assign(__assign(__assign({}, exports.DefaultConfig), config), config.config);
17533
17516
  };
17534
17517
  KTSelectState.prototype.setItems = function (items, query) {
17535
17518
  var _this = this;