@keenthemes/ktui 1.0.12 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/ktui.js +738 -700
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +5824 -0
  5. package/examples/select/avatar.html +47 -0
  6. package/examples/select/basic-usage.html +10 -14
  7. package/examples/select/{test.html → combobox-icons_.html} +13 -48
  8. package/examples/select/country.html +43 -0
  9. package/examples/select/description.html +25 -41
  10. package/examples/select/disable-option.html +10 -16
  11. package/examples/select/disable-select.html +7 -6
  12. package/examples/select/icon-multiple.html +23 -31
  13. package/examples/select/icon.html +20 -30
  14. package/examples/select/max-selection.html +8 -9
  15. package/examples/select/modal.html +16 -17
  16. package/examples/select/multiple.html +11 -13
  17. package/examples/select/placeholder.html +9 -12
  18. package/examples/select/search.html +30 -22
  19. package/examples/select/sizes.html +94 -0
  20. package/examples/select/template-customization.html +0 -3
  21. package/lib/cjs/components/component.js +1 -1
  22. package/lib/cjs/components/component.js.map +1 -1
  23. package/lib/cjs/components/datatable/datatable.js +14 -11
  24. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  25. package/lib/cjs/components/select/combobox.js +96 -61
  26. package/lib/cjs/components/select/combobox.js.map +1 -1
  27. package/lib/cjs/components/select/config.js +13 -8
  28. package/lib/cjs/components/select/config.js.map +1 -1
  29. package/lib/cjs/components/select/dropdown.js +32 -96
  30. package/lib/cjs/components/select/dropdown.js.map +1 -1
  31. package/lib/cjs/components/select/option.js +53 -20
  32. package/lib/cjs/components/select/option.js.map +1 -1
  33. package/lib/cjs/components/select/search.js +146 -97
  34. package/lib/cjs/components/select/search.js.map +1 -1
  35. package/lib/cjs/components/select/select.js +219 -118
  36. package/lib/cjs/components/select/select.js.map +1 -1
  37. package/lib/cjs/components/select/tags.js +0 -26
  38. package/lib/cjs/components/select/tags.js.map +1 -1
  39. package/lib/cjs/components/select/templates.js +130 -105
  40. package/lib/cjs/components/select/templates.js.map +1 -1
  41. package/lib/cjs/components/select/utils.js +33 -132
  42. package/lib/cjs/components/select/utils.js.map +1 -1
  43. package/lib/cjs/helpers/dom.js +0 -24
  44. package/lib/cjs/helpers/dom.js.map +1 -1
  45. package/lib/esm/components/component.js +1 -1
  46. package/lib/esm/components/component.js.map +1 -1
  47. package/lib/esm/components/datatable/datatable.js +14 -11
  48. package/lib/esm/components/datatable/datatable.js.map +1 -1
  49. package/lib/esm/components/select/combobox.js +96 -61
  50. package/lib/esm/components/select/combobox.js.map +1 -1
  51. package/lib/esm/components/select/config.js +13 -8
  52. package/lib/esm/components/select/config.js.map +1 -1
  53. package/lib/esm/components/select/dropdown.js +32 -96
  54. package/lib/esm/components/select/dropdown.js.map +1 -1
  55. package/lib/esm/components/select/option.js +53 -20
  56. package/lib/esm/components/select/option.js.map +1 -1
  57. package/lib/esm/components/select/search.js +146 -97
  58. package/lib/esm/components/select/search.js.map +1 -1
  59. package/lib/esm/components/select/select.js +219 -118
  60. package/lib/esm/components/select/select.js.map +1 -1
  61. package/lib/esm/components/select/tags.js +0 -26
  62. package/lib/esm/components/select/tags.js.map +1 -1
  63. package/lib/esm/components/select/templates.js +130 -105
  64. package/lib/esm/components/select/templates.js.map +1 -1
  65. package/lib/esm/components/select/utils.js +32 -130
  66. package/lib/esm/components/select/utils.js.map +1 -1
  67. package/lib/esm/helpers/dom.js +0 -24
  68. package/lib/esm/helpers/dom.js.map +1 -1
  69. package/package.json +9 -6
  70. package/src/components/component.ts +0 -4
  71. package/src/components/datatable/datatable.ts +14 -11
  72. package/src/components/input/input.css +1 -1
  73. package/src/components/scrollable/scrollable.css +9 -5
  74. package/src/components/select/combobox.ts +98 -87
  75. package/src/components/select/config.ts +16 -13
  76. package/src/components/select/dropdown.ts +43 -108
  77. package/src/components/select/option.ts +44 -25
  78. package/src/components/select/search.ts +158 -117
  79. package/src/components/select/select.css +99 -27
  80. package/src/components/select/select.ts +236 -128
  81. package/src/components/select/tags.ts +1 -27
  82. package/src/components/select/templates.ts +191 -132
  83. package/src/components/select/utils.ts +30 -166
  84. package/src/components/toast/toast.css +1 -1
  85. package/src/helpers/dom.ts +0 -30
  86. package/webpack.config.js +6 -1
  87. package/examples/select/combobox-icons.html +0 -58
  88. package/examples/select/icon-description.html +0 -56
  89. /package/examples/select/{combobox.html → combobox_.html} +0 -0
  90. /package/examples/select/{remote-data.html → remote-data_.html} +0 -0
  91. /package/examples/select/{tags-icons.html → tags-icons_.html} +0 -0
  92. /package/examples/select/{tags-selected.html → tags-selected_.html} +0 -0
  93. /package/examples/select/{tags.html → tags_.html} +0 -0
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
@@ -8368,16 +8436,19 @@ var KTDataTable = /** @class */ (function (_super) {
8368
8436
  if (!_sizeElement) {
8369
8437
  return _sizeElement;
8370
8438
  }
8371
- // Create <option> elements for each page size option
8372
- var options = this._config.pageSizes.map(function (size) {
8373
- var option = document.createElement('option');
8374
- option.value = String(size);
8375
- option.text = String(size);
8376
- option.selected = _this.getState().pageSize === size;
8377
- return option;
8378
- });
8379
- // Add the <option> elements to the provided element
8380
- _sizeElement.append.apply(_sizeElement, options);
8439
+ // Wait for the element to be attached to the DOM
8440
+ setTimeout(function () {
8441
+ // Create <option> elements for each page size option
8442
+ var options = _this._config.pageSizes.map(function (size) {
8443
+ var option = document.createElement('option');
8444
+ option.value = String(size);
8445
+ option.text = String(size);
8446
+ option.selected = _this.getState().pageSize === size;
8447
+ return option;
8448
+ });
8449
+ // Add the <option> elements to the provided element
8450
+ _sizeElement.append.apply(_sizeElement, options);
8451
+ }, 100);
8381
8452
  // Create an event listener for the "change" event on the element
8382
8453
  var _pageSizeControlsEvent = function (event) {
8383
8454
  // When the element changes, reload the page with the new page size and page number 1
@@ -9217,7 +9288,7 @@ var utils_1 = __webpack_require__(9011);
9217
9288
  *
9218
9289
  * A specialized dropdown implementation for the KTSelect component.
9219
9290
  * This module handles the dropdown functionality for the select component,
9220
- * including positioning, showing/hiding, and keyboard navigation.
9291
+ * including positioning and showing/hiding.
9221
9292
  */
9222
9293
  var KTSelectDropdown = /** @class */ (function (_super) {
9223
9294
  __extends(KTSelectDropdown, _super);
@@ -9228,7 +9299,7 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9228
9299
  * @param dropdownElement The dropdown content element
9229
9300
  * @param config The configuration options
9230
9301
  */
9231
- function KTSelectDropdown(element, toggleElement, dropdownElement, config) {
9302
+ function KTSelectDropdown(element, toggleElement, dropdownElement, config, ktSelectInstance) {
9232
9303
  var _this = _super.call(this) || this;
9233
9304
  _this._name = 'select-dropdown';
9234
9305
  // State
@@ -9239,6 +9310,7 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9239
9310
  _this._toggleElement = toggleElement;
9240
9311
  _this._dropdownElement = dropdownElement;
9241
9312
  _this._config = config;
9313
+ _this._ktSelectInstance = ktSelectInstance; // Assign instance
9242
9314
  _this._eventManager = new utils_1.EventManager();
9243
9315
  _this._focusManager = new utils_1.FocusManager(dropdownElement, '[data-kt-select-option]', config);
9244
9316
  _this._setupEventListeners();
@@ -9259,7 +9331,18 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9259
9331
  KTSelectDropdown.prototype._handleToggleClick = function (event) {
9260
9332
  event.preventDefault();
9261
9333
  event.stopPropagation();
9262
- this.toggle();
9334
+ if (this._config.disabled) {
9335
+ if (this._config.debug)
9336
+ console.log('KTSelectDropdown._handleToggleClick: select is disabled');
9337
+ return;
9338
+ }
9339
+ // Call KTSelect's methods
9340
+ if (this._ktSelectInstance.isDropdownOpen()) {
9341
+ this._ktSelectInstance.closeDropdown();
9342
+ }
9343
+ else {
9344
+ this._ktSelectInstance.openDropdown();
9345
+ }
9263
9346
  };
9264
9347
  /**
9265
9348
  * Handle clicks outside the dropdown
@@ -9270,7 +9353,8 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9270
9353
  var target = event.target;
9271
9354
  if (!this._element.contains(target) &&
9272
9355
  !this._dropdownElement.contains(target)) {
9273
- this.close();
9356
+ // Call KTSelect's closeDropdown method
9357
+ this._ktSelectInstance.closeDropdown();
9274
9358
  }
9275
9359
  };
9276
9360
  /**
@@ -9372,29 +9456,6 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9372
9456
  this._popperInstance.update();
9373
9457
  }
9374
9458
  };
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
9459
  /**
9399
9460
  * Open the dropdown
9400
9461
  */
@@ -9407,71 +9468,41 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9407
9468
  }
9408
9469
  if (this._isOpen || this._isTransitioning)
9409
9470
  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
9471
  // Begin opening transition
9419
9472
  this._isTransitioning = true;
9420
- // Set initial styles - remove display: block and use class toggling instead
9473
+ // Set initial styles
9421
9474
  this._dropdownElement.classList.remove('hidden');
9422
9475
  this._dropdownElement.style.opacity = '0';
9423
9476
  // Set dropdown width
9424
9477
  this._setDropdownWidth();
9425
- // Make sure the element is visible for transitioning
9478
+ // Reflow
9426
9479
  dom_1.default.reflow(this._dropdownElement);
9427
- // Apply z-index if configured
9480
+ // Apply z-index
9428
9481
  if (this._config.dropdownZindex) {
9429
9482
  this._dropdownElement.style.zIndex =
9430
9483
  this._config.dropdownZindex.toString();
9431
9484
  }
9432
9485
  else {
9433
- // Auto-calculate z-index
9434
9486
  var parentZindex = dom_1.default.getHighestZindex(this._element);
9435
9487
  if (parentZindex) {
9436
9488
  this._dropdownElement.style.zIndex = (parentZindex + 1).toString();
9437
9489
  }
9438
9490
  }
9439
- // Initialize popper for positioning
9491
+ // Initialize popper
9440
9492
  this._initPopper();
9441
- // Add active classes
9493
+ // Add active classes for visual state
9442
9494
  this._dropdownElement.classList.add('open');
9443
9495
  this._toggleElement.classList.add('active');
9444
- this._toggleElement.setAttribute('aria-expanded', 'true');
9496
+ // ARIA attributes will be handled by KTSelect
9445
9497
  // Start transition
9446
9498
  this._dropdownElement.style.opacity = '1';
9447
9499
  // Handle transition end
9448
9500
  dom_1.default.transitionEnd(this._dropdownElement, function () {
9449
9501
  _this._isTransitioning = false;
9450
9502
  _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);
9503
+ // Focus and events will be handled by KTSelect
9463
9504
  });
9464
9505
  };
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
9506
  /**
9476
9507
  * Close the dropdown
9477
9508
  */
@@ -9484,34 +9515,19 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9484
9515
  console.log('KTSelectDropdown.close - early return: dropdown not open or is transitioning');
9485
9516
  return;
9486
9517
  }
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
- }
9518
+ // Events and ARIA will be handled by KTSelect
9498
9519
  if (this._config.debug)
9499
9520
  console.log('KTSelectDropdown.close - starting transition');
9500
- // Begin closing transition
9501
9521
  this._isTransitioning = true;
9502
- // Start transition
9503
9522
  this._dropdownElement.style.opacity = '0';
9504
- // Use a combination of transition end and a fallback timer
9505
9523
  var transitionComplete = false;
9506
- // Set a fixed-duration fallback in case the transition event doesn't fire
9507
9524
  var fallbackTimer = setTimeout(function () {
9508
9525
  if (!transitionComplete) {
9509
9526
  if (_this._config.debug)
9510
9527
  console.log('KTSelectDropdown.close - fallback timer triggered');
9511
9528
  completeTransition();
9512
9529
  }
9513
- }, 300); // 300ms should be enough for most transitions
9514
- // Setup the transition end function
9530
+ }, 300);
9515
9531
  var completeTransition = function () {
9516
9532
  if (transitionComplete)
9517
9533
  return;
@@ -9519,30 +9535,21 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9519
9535
  clearTimeout(fallbackTimer);
9520
9536
  if (_this._config.debug)
9521
9537
  console.log('KTSelectDropdown.close - transition ended');
9522
- // Remove active classes
9523
9538
  _this._dropdownElement.classList.add('hidden');
9524
9539
  _this._dropdownElement.classList.remove('open');
9525
9540
  _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
9541
+ // ARIA attributes will be handled by KTSelect
9532
9542
  _this._destroyPopper();
9533
- // Update state
9534
9543
  _this._isTransitioning = false;
9535
9544
  _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);
9545
+ // Events will be handled by KTSelect
9541
9546
  if (_this._config.debug)
9542
- console.log('KTSelectDropdown.close - complete, events fired');
9547
+ console.log('KTSelectDropdown.close - visual part complete');
9543
9548
  };
9544
- // Handle transition end via the utility but also have the fallback
9545
9549
  dom_1.default.transitionEnd(this._dropdownElement, completeTransition);
9550
+ if (dom_1.default.getCssProp(this._dropdownElement, 'transition-duration') === '0s') {
9551
+ completeTransition();
9552
+ }
9546
9553
  };
9547
9554
  /**
9548
9555
  * Check if dropdown is open
@@ -9910,15 +9917,17 @@ var KTSelectSearch = /** @class */ (function () {
9910
9917
  console.log('Initializing search module with input:', this._searchInput);
9911
9918
  // First remove any existing listeners to prevent duplicates
9912
9919
  this._removeEventListeners();
9913
- // Add the event listener
9920
+ // Add the input event listener for filtering
9914
9921
  this._eventManager.addListener(this._searchInput, 'input', this.handleSearchInput);
9922
+ // Add keydown event listener for navigation, selection, and escape
9923
+ this._eventManager.addListener(this._searchInput, 'keydown', this._handleSearchKeyDown.bind(this));
9915
9924
  // Add blur event listener to ensure highlights are cleared when focus is lost
9916
9925
  this._eventManager.addListener(this._searchInput, 'blur', function () {
9917
9926
  // Small delay to prevent race conditions with selection
9918
9927
  setTimeout(function () {
9919
9928
  if (!_this._searchInput.value) {
9920
9929
  _this._resetAllOptions();
9921
- _this.clearSearchHighlights();
9930
+ _this.clearSearch();
9922
9931
  }
9923
9932
  }, 100);
9924
9933
  });
@@ -9936,39 +9945,48 @@ var KTSelectSearch = /** @class */ (function () {
9936
9945
  _this.refreshOptionCache();
9937
9946
  });
9938
9947
  }
9939
- // Listen for dropdown close to reset options if search is empty
9940
- this._select.getElement().addEventListener('dropdown.close', function () {
9948
+ // Listen for dropdown close to reset options - ATTACH TO WRAPPER
9949
+ this._select.getWrapperElement().addEventListener('dropdown.close', function () {
9941
9950
  _this._focusManager.resetFocus();
9942
- _this.clearSearchHighlights();
9943
- _this._searchInput.value = '';
9944
- _this._resetAllOptions();
9945
- _this._clearNoResultsMessage();
9951
+ // If clearSearchOnClose is false and there's a value, the search term and filtered state should persist.
9952
+ // KTSelect's closeDropdown method already calls this._searchModule.clearSearch() (which clears highlights)
9953
+ // and conditionally clears the input value based on KTSelect's config.clearSearchOnClose.
9954
+ // This listener in search.ts seems to unconditionally clear everything.
9955
+ // For now, keeping its original behavior:
9956
+ _this.clearSearch(); // Clears highlights from current options
9957
+ _this._searchInput.value = ''; // Clears the search input field
9958
+ _this._resetAllOptions(); // Shows all options, restores original text, removes highlights
9959
+ _this._clearNoResultsMessage(); // Clears any "no results" message
9946
9960
  });
9947
- // Clear highlights when an option is selected
9961
+ // Clear highlights when an option is selected - ATTACH TO ORIGINAL SELECT (standard 'change' event)
9948
9962
  this._select.getElement().addEventListener('change', function () {
9949
- _this.clearSearchHighlights();
9963
+ _this.clearSearch();
9950
9964
  // Close dropdown if configured to do so
9951
- if (_this._select.getConfig().closeOnSelect &&
9952
- !_this._select.getConfig().multiple) {
9953
- _this._select.closeDropdown();
9954
- }
9965
+ _this._select.closeDropdown();
9955
9966
  });
9956
- // Autofocus on search input
9957
- if (this._select.getConfig().searchAutofocus) {
9958
- this._select.getElement().addEventListener('dropdown.show', function () {
9967
+ // Consolidated 'dropdown.show' event listener - ATTACH TO WRAPPER
9968
+ this._select.getWrapperElement().addEventListener('dropdown.show', function () {
9969
+ var _a;
9970
+ _this._focusManager.resetFocus(); // Always clear previous focus state
9971
+ if ((_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.value) {
9972
+ // If there's an existing search term:
9973
+ // 1. Re-filter options. This ensures the display (hidden/visible) is correct
9974
+ // and "no results" message is handled if query yields nothing.
9975
+ _this._filterOptions(_this._searchInput.value);
9976
+ }
9977
+ else {
9978
+ // If search input is empty:
9979
+ // 1. Reset all options to their full, unfiltered, original state.
9980
+ _this._resetAllOptions(); // Shows all, clears highlights from options, restores original text
9981
+ // 2. Clear any "no results" message.
9982
+ _this._clearNoResultsMessage();
9983
+ }
9984
+ // Handle autofocus for the search input (this was one of the original separate listeners)
9985
+ if (_this._select.getConfig().searchAutofocus) {
9959
9986
  setTimeout(function () {
9960
9987
  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();
9988
+ (_a = _this._searchInput) === null || _a === void 0 ? void 0 : _a.focus(); // Focus search input
9989
+ }, 50); // Delay to ensure dropdown is visible
9972
9990
  }
9973
9991
  });
9974
9992
  }
@@ -9983,18 +10001,42 @@ var KTSelectSearch = /** @class */ (function () {
9983
10001
  }
9984
10002
  };
9985
10003
  /**
9986
- * Select the currently focused option
10004
+ * Handles keydown events on the search input for navigation and actions.
9987
10005
  */
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
- }
10006
+ KTSelectSearch.prototype._handleSearchKeyDown = function (event) {
10007
+ var key = event.key;
10008
+ switch (key) {
10009
+ case 'ArrowDown':
10010
+ event.preventDefault();
10011
+ this._focusManager.focusNext();
10012
+ break;
10013
+ case 'ArrowUp':
10014
+ event.preventDefault();
10015
+ this._focusManager.focusPrevious();
10016
+ break;
10017
+ case 'Enter':
10018
+ event.preventDefault();
10019
+ // Always attempt to select the first available option in the list.
10020
+ // focusFirst() finds, focuses, and returns the first visible, non-disabled option.
10021
+ var firstAvailableOption = this._focusManager.focusFirst();
10022
+ if (firstAvailableOption) {
10023
+ var optionValue = firstAvailableOption.getAttribute('data-value');
10024
+ if (optionValue) {
10025
+ this._select.toggleSelection(optionValue);
10026
+ // KTSelect.toggleSelection handles closing the dropdown based on config.closeOnSelect and config.multiple
10027
+ }
10028
+ }
10029
+ break;
10030
+ case 'Escape':
10031
+ event.preventDefault();
10032
+ this._searchInput.value = '';
10033
+ this.clearSearch();
10034
+ this._resetAllOptions();
10035
+ this._clearNoResultsMessage();
10036
+ this._focusManager.focusFirst();
10037
+ break;
10038
+ default:
10039
+ break;
9998
10040
  }
9999
10041
  };
10000
10042
  /**
@@ -10005,42 +10047,66 @@ var KTSelectSearch = /** @class */ (function () {
10005
10047
  var _this = this;
10006
10048
  // Wait for options to be initialized
10007
10049
  setTimeout(function () {
10050
+ _this._originalOptionContents.clear(); // Clear before re-caching
10008
10051
  var options = Array.from(_this._select.getOptionsElement());
10009
10052
  options.forEach(function (option) {
10010
10053
  var value = option.getAttribute('data-value');
10011
10054
  if (value) {
10055
+ // Store the full innerHTML as the original content
10012
10056
  _this._originalOptionContents.set(value, option.innerHTML);
10013
10057
  }
10014
10058
  });
10015
10059
  }, 0);
10016
10060
  };
10061
+ /**
10062
+ * Restores the innerHTML of all options from the cache if they have been modified.
10063
+ * This is typically called before applying new filters/highlights.
10064
+ */
10065
+ KTSelectSearch.prototype._restoreOptionContentsBeforeFilter = function () {
10066
+ var _this = this;
10067
+ var options = Array.from(this._select.getOptionsElement());
10068
+ options.forEach(function (option) {
10069
+ var value = option.getAttribute('data-value');
10070
+ if (value && _this._originalOptionContents.has(value)) {
10071
+ var originalContent = _this._originalOptionContents.get(value);
10072
+ // Only restore if current content is different, to avoid unnecessary DOM manipulation
10073
+ if (option.innerHTML !== originalContent) {
10074
+ option.innerHTML = originalContent;
10075
+ }
10076
+ }
10077
+ });
10078
+ };
10017
10079
  KTSelectSearch.prototype._handleSearchInput = function (event) {
10018
- var query = event.target.value.toLowerCase();
10080
+ var query = event.target.value;
10019
10081
  var config = this._select.getConfig();
10020
10082
  // Reset focused option when search changes
10021
10083
  this._focusManager.resetFocus();
10022
- // If search query is empty, clear all highlights
10084
+ // Restore original content for all options before filtering/highlighting again
10085
+ this._restoreOptionContentsBeforeFilter();
10023
10086
  if (query.trim() === '') {
10024
- this.clearSearchHighlights();
10087
+ this._resetAllOptions();
10088
+ this._focusManager.focusFirst(); // Focus first option when search is cleared
10089
+ return;
10025
10090
  }
10026
- // For remote search, we don't filter locally
10027
- // The KTSelect component will handle the remote search
10091
+ // For remote search, KTSelect component handles it.
10092
+ // KTSelect will call refreshAfterSearch on this module when remote data is updated.
10028
10093
  if (config.remote && config.searchParam) {
10029
- // If query is too short, reset all options to visible state
10030
10094
  if (query.length < config.searchMinLength) {
10031
10095
  this._resetAllOptions();
10032
10096
  this._clearNoResultsMessage();
10097
+ this._focusManager.focusFirst(); // Focus first if query too short
10033
10098
  }
10034
- // Otherwise, let KTSelect handle remote search
10035
10099
  return;
10036
10100
  }
10037
10101
  // For local search
10038
10102
  if (query.length >= config.searchMinLength) {
10039
10103
  this._filterOptions(query);
10104
+ this._focusManager.focusFirst(); // Focus first visible option after local filtering
10040
10105
  }
10041
10106
  else {
10042
10107
  this._resetAllOptions();
10043
10108
  this._clearNoResultsMessage();
10109
+ this._focusManager.focusFirst(); // Focus first if query too short and not remote
10044
10110
  }
10045
10111
  };
10046
10112
  KTSelectSearch.prototype._filterOptions = function (query) {
@@ -10052,20 +10118,11 @@ var KTSelectSearch = /** @class */ (function () {
10052
10118
  if (this._originalOptionContents.size === 0) {
10053
10119
  this._cacheOriginalOptionContents();
10054
10120
  }
10055
- // Use the shared filterOptions utility
10056
- (0, utils_1.filterOptions)(options, query, config, dropdownElement, function (visibleCount) {
10057
- return _this._handleNoResults(visibleCount);
10121
+ // Restore original content before filtering, so highlighting is applied fresh.
10122
+ this._restoreOptionContentsBeforeFilter();
10123
+ var visibleCount = (0, utils_1.filterOptions)(options, query, config, dropdownElement, function (count) {
10124
+ return _this._handleNoResults(count);
10058
10125
  });
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
10126
  };
10070
10127
  /**
10071
10128
  * Reset all options to their original state
@@ -10074,34 +10131,29 @@ var KTSelectSearch = /** @class */ (function () {
10074
10131
  var _this = this;
10075
10132
  // Show all options
10076
10133
  var options = Array.from(this._select.getOptionsElement());
10077
- // Cache original option HTML if not already cached
10134
+ // Ensure the cache is populated if it's somehow empty here
10078
10135
  if (this._originalOptionContents.size === 0) {
10079
10136
  this._cacheOriginalOptionContents();
10080
10137
  }
10081
10138
  options.forEach(function (option) {
10082
- // Remove the hidden class
10083
10139
  option.classList.remove('hidden');
10140
+ if (option.style.display === 'none')
10141
+ option.style.display = ''; // Ensure visible
10084
10142
  // Restore original HTML content (remove highlights)
10085
10143
  var value = option.getAttribute('data-value');
10086
10144
  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());
10145
+ var originalContent = _this._originalOptionContents.get(value);
10146
+ // Only update if different, to minimize DOM changes
10147
+ if (option.innerHTML !== originalContent) {
10148
+ option.innerHTML = originalContent;
10099
10149
  }
10100
10150
  }
10101
10151
  });
10152
+ this._clearNoResultsMessage(); // Ensure no results message is cleared when resetting
10102
10153
  };
10103
10154
  KTSelectSearch.prototype._handleNoResults = function (visibleOptionsCount) {
10104
- if (visibleOptionsCount === 0 && this._searchInput.value.trim() !== '') {
10155
+ var _a, _b;
10156
+ if (visibleOptionsCount === 0 && ((_b = (_a = this._searchInput) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.trim()) !== '') {
10105
10157
  this._showNoResultsMessage();
10106
10158
  }
10107
10159
  else {
@@ -10131,28 +10183,22 @@ var KTSelectSearch = /** @class */ (function () {
10131
10183
  * Public method to explicitly clear all search highlights
10132
10184
  * This is called when search is reset or selection changes
10133
10185
  */
10134
- KTSelectSearch.prototype.clearSearchHighlights = function () {
10186
+ KTSelectSearch.prototype.clearSearch = function () {
10135
10187
  var _this = this;
10136
10188
  // Restore original option content (removes highlighting)
10137
- var options = Array.from(this._select.getOptionsElement());
10138
- options.forEach(function (option) {
10189
+ var optionsToClear = Array.from(this._select.getOptionsElement());
10190
+ // Ensure cache is available
10191
+ if (this._originalOptionContents.size === 0 && optionsToClear.length > 0) {
10192
+ this._cacheOriginalOptionContents();
10193
+ }
10194
+ optionsToClear.forEach(function (option) {
10139
10195
  var value = option.getAttribute('data-value');
10140
10196
  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;
10197
+ var originalContent = _this._originalOptionContents.get(value);
10198
+ // Only restore if different
10199
+ if (option.innerHTML !== originalContent) {
10200
+ option.innerHTML = originalContent;
10201
+ }
10156
10202
  }
10157
10203
  });
10158
10204
  };
@@ -10163,14 +10209,23 @@ var KTSelectSearch = /** @class */ (function () {
10163
10209
  var _this = this;
10164
10210
  // Re-cache all option contents
10165
10211
  this._originalOptionContents.clear();
10166
- var options = Array.from(this._select.getOptionsElement());
10167
- options.forEach(function (option) {
10212
+ var currentOptions = Array.from(this._select.getOptionsElement());
10213
+ currentOptions.forEach(function (option) {
10168
10214
  var value = option.getAttribute('data-value');
10169
10215
  if (value) {
10170
10216
  _this._originalOptionContents.set(value, option.innerHTML);
10171
10217
  }
10172
10218
  });
10173
10219
  };
10220
+ /**
10221
+ * Called after search (local or remote via KTSelect) to reset focus.
10222
+ */
10223
+ KTSelectSearch.prototype.refreshAfterSearch = function () {
10224
+ this._focusManager.resetFocus();
10225
+ this._focusManager.focusFirst();
10226
+ // Re-cache original contents as options might have changed (especially after remote search)
10227
+ this.refreshOptionCache();
10228
+ };
10174
10229
  /**
10175
10230
  * Clean up all resources used by the search module
10176
10231
  */
@@ -10178,12 +10233,13 @@ var KTSelectSearch = /** @class */ (function () {
10178
10233
  // Remove all event listeners
10179
10234
  this._removeEventListeners();
10180
10235
  // Clear all references
10181
- this._focusManager.dispose();
10182
- this._eventManager.removeAllListeners(null);
10236
+ if (this._focusManager) {
10237
+ this._focusManager.dispose();
10238
+ }
10183
10239
  // Clear cached content
10184
10240
  this._originalOptionContents.clear();
10185
10241
  // Clear highlight elements
10186
- this.clearSearchHighlights();
10242
+ this.clearSearch();
10187
10243
  };
10188
10244
  return KTSelectSearch;
10189
10245
  }());
@@ -11305,6 +11361,7 @@ var KTSelect = /** @class */ (function (_super) {
11305
11361
  _this._dropdownModule = null;
11306
11362
  _this._loadMoreIndicator = null;
11307
11363
  _this._typeToSearchBuffer = new utils_1.TypeToSearchBuffer();
11364
+ _this._mutationObserver = null;
11308
11365
  // Search debounce timeout
11309
11366
  _this._searchDebounceTimeout = null;
11310
11367
  // Store original options HTML for restoring after search
@@ -11399,15 +11456,16 @@ var KTSelect = /** @class */ (function (_super) {
11399
11456
  var optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
11400
11457
  if (!optionsContainer)
11401
11458
  return;
11459
+ // Clear previous messages
11460
+ optionsContainer.innerHTML = '';
11402
11461
  switch (type) {
11403
11462
  case 'error':
11404
- optionsContainer.innerHTML = templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message }));
11463
+ optionsContainer.appendChild(templates_1.defaultTemplates.error(__assign(__assign({}, this._config), { errorMessage: message })));
11405
11464
  break;
11406
11465
  case 'loading':
11407
- optionsContainer.innerHTML = templates_1.defaultTemplates.loading(this._config, message || 'Loading...').outerHTML;
11466
+ optionsContainer.appendChild(templates_1.defaultTemplates.loading(this._config, message || 'Loading...'));
11408
11467
  break;
11409
11468
  case 'empty':
11410
- optionsContainer.innerHTML = '';
11411
11469
  optionsContainer.appendChild(templates_1.defaultTemplates.empty(this._config));
11412
11470
  break;
11413
11471
  }
@@ -11579,20 +11637,15 @@ var KTSelect = /** @class */ (function (_super) {
11579
11637
  // Initialize focus manager after dropdown element is created
11580
11638
  this._focusManager = new utils_1.FocusManager(this._dropdownContentElement, '[data-kt-select-option]', this._config);
11581
11639
  // Initialize dropdown module after all elements are created
11582
- this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config);
11640
+ this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config, this);
11583
11641
  // Update display and set ARIA attributes
11584
11642
  this._updateDisplayAndAriaAttributes();
11585
11643
  this.updateSelectedOptionDisplay();
11586
11644
  this._setAriaAttributes();
11587
11645
  // Attach event listeners after all modules are initialized
11588
11646
  this._attachEventListeners();
11647
+ this._observeNativeSelect();
11589
11648
  };
11590
- /**
11591
- * Initialize options HTML from data
11592
- */
11593
- // private _initializeOptionsHtml() {
11594
- // this._generateOptionsHtml(this._element);
11595
- // }
11596
11649
  /**
11597
11650
  * Creates the HTML structure for the select component
11598
11651
  */
@@ -11607,7 +11660,13 @@ var KTSelect = /** @class */ (function (_super) {
11607
11660
  wrapperElement.appendChild(displayElement);
11608
11661
  // Move classes from original select to display element
11609
11662
  if (this._element.classList.length > 0) {
11610
- (_a = displayElement.classList).add.apply(_a, Array.from(this._element.classList));
11663
+ // Exclude kt-select class from being added to the wrapper element
11664
+ var classes = Array.from(this._element.classList).filter(function (className) { return className !== 'kt-select'; });
11665
+ (_a = wrapperElement.classList).add.apply(_a, classes);
11666
+ // If element has class kt-select, move it to display element
11667
+ if (this._element.classList.contains('kt-select')) {
11668
+ displayElement.classList.add('kt-select');
11669
+ }
11611
11670
  this._element.className = '';
11612
11671
  }
11613
11672
  // Create an empty dropdown first (without options) using template
@@ -11641,7 +11700,7 @@ var KTSelect = /** @class */ (function (_super) {
11641
11700
  wrapperElement.appendChild(dropdownElement);
11642
11701
  // Insert after the original element
11643
11702
  this._element.after(wrapperElement);
11644
- this._element.style.display = 'none';
11703
+ this._element.classList.add('hidden');
11645
11704
  };
11646
11705
  /**
11647
11706
  * Setup all element references after DOM is created
@@ -11662,7 +11721,6 @@ var KTSelect = /** @class */ (function (_super) {
11662
11721
  if (!this._searchInputElement) {
11663
11722
  this._searchInputElement = this._displayElement;
11664
11723
  }
11665
- this._valueDisplayElement = this._wrapperElement.querySelector("[data-kt-select-value]");
11666
11724
  this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
11667
11725
  };
11668
11726
  /**
@@ -11673,16 +11731,10 @@ var KTSelect = /** @class */ (function (_super) {
11673
11731
  document.addEventListener('click', this._handleDocumentClick.bind(this));
11674
11732
  // Dropdown option click events
11675
11733
  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));
11734
+ // Attach centralized keyboard handler to the wrapper element.
11735
+ // Events from focusable children like _displayElement or _searchInputElement (if present) will bubble up.
11736
+ if (this._wrapperElement) {
11737
+ this._wrapperElement.addEventListener('keydown', this._handleKeyboardEvent.bind(this));
11686
11738
  }
11687
11739
  };
11688
11740
  /**
@@ -11825,37 +11877,10 @@ var KTSelect = /** @class */ (function (_super) {
11825
11877
  * DROPDOWN MANAGEMENT
11826
11878
  * ========================================================================
11827
11879
  */
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
11880
  /**
11855
11881
  * Open the dropdown
11856
11882
  */
11857
11883
  KTSelect.prototype.openDropdown = function () {
11858
- var _this = this;
11859
11884
  if (this._config.disabled) {
11860
11885
  if (this._config.debug)
11861
11886
  console.log('openDropdown: select is disabled, not opening');
@@ -11883,14 +11908,6 @@ var KTSelect = /** @class */ (function (_super) {
11883
11908
  // Dispatch custom event
11884
11909
  this._dispatchEvent('show');
11885
11910
  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
11911
  // Update ARIA states
11895
11912
  this._setAriaAttributes();
11896
11913
  // Focus the first selected option or first option if nothing selected
@@ -11911,14 +11928,14 @@ var KTSelect = /** @class */ (function (_super) {
11911
11928
  // Always close by delegating to the dropdown module, which is the source of truth
11912
11929
  if (this._config.debug)
11913
11930
  console.log('Closing dropdown via dropdownModule...');
11914
- // Clear search input and highlights if the dropdown is closing
11931
+ // Clear search input if the dropdown is closing
11915
11932
  if (this._searchModule && this._searchInputElement) {
11916
11933
  // Clear search input if configured to do so
11917
11934
  if (this._config.clearSearchOnClose) {
11918
11935
  this._searchInputElement.value = '';
11919
11936
  }
11920
- // Always clear the highlights when dropdown closes
11921
- this._searchModule.clearSearchHighlights();
11937
+ // Clear search input when dropdown closes
11938
+ this._searchModule.clearSearch();
11922
11939
  }
11923
11940
  // Set our internal flag to match what we're doing
11924
11941
  this._dropdownIsOpen = false;
@@ -11952,10 +11969,13 @@ var KTSelect = /** @class */ (function (_super) {
11952
11969
  var selectedOptions = this.getSelectedOptions();
11953
11970
  if (selectedOptions.length === 0)
11954
11971
  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);
11972
+ // Iterate through selected options and focus the first one that is visible
11973
+ for (var _i = 0, selectedOptions_1 = selectedOptions; _i < selectedOptions_1.length; _i++) {
11974
+ var value = selectedOptions_1[_i];
11975
+ if (this._focusManager && this._focusManager.focusOptionByValue(value)) {
11976
+ break; // Stop after focusing the first found selected and visible option
11977
+ }
11978
+ }
11959
11979
  };
11960
11980
  /**
11961
11981
  * ========================================================================
@@ -12018,38 +12038,62 @@ var KTSelect = /** @class */ (function (_super) {
12018
12038
  */
12019
12039
  KTSelect.prototype.updateSelectedOptionDisplay = function () {
12020
12040
  var selectedOptions = this.getSelectedOptions();
12021
- // Tag mode: render tags if enabled
12022
- if (this._config.tags && this._tagsModule) {
12041
+ var tagsEnabled = this._config.tags && this._tagsModule;
12042
+ var valueDisplayEl = this.getValueDisplayElement();
12043
+ if (tagsEnabled) {
12044
+ // Tags module will render tags if selectedOptions > 0, or clear them if selectedOptions === 0.
12023
12045
  this._tagsModule.updateTagsDisplay(selectedOptions);
12024
- return;
12046
+ }
12047
+ // Guard against valueDisplayEl being null due to template modifications
12048
+ if (!valueDisplayEl) {
12049
+ if (this._config.debug) {
12050
+ console.warn('KTSelect: Value display element is null. Cannot update display or placeholder. Check template for [data-kt-select-value].');
12051
+ }
12052
+ return; // Nothing to display on if the element is missing
12025
12053
  }
12026
12054
  if (typeof this._config.renderSelected === 'function') {
12027
- // Use the custom renderSelected function if provided
12028
- this._valueDisplayElement.innerHTML = this._config.renderSelected(selectedOptions);
12055
+ valueDisplayEl.innerHTML = this._config.renderSelected(selectedOptions);
12029
12056
  }
12030
12057
  else {
12031
12058
  if (selectedOptions.length === 0) {
12032
- var placeholder = templates_1.defaultTemplates.placeholder(this._config);
12033
- this._valueDisplayElement.replaceChildren(placeholder);
12059
+ // No options selected: display placeholder.
12060
+ // This runs if tags are off, OR if tags are on but no items are selected (tags module would have cleared tags).
12061
+ var placeholderEl = templates_1.defaultTemplates.placeholder(this._config);
12062
+ valueDisplayEl.replaceChildren(placeholderEl);
12034
12063
  }
12035
12064
  else {
12036
- var content = '';
12037
- if (this._config.displayTemplate) {
12038
- var selectedValues = this.getSelectedOptions();
12039
- content = this.renderDisplayTemplateForSelected(selectedValues);
12065
+ // Options are selected.
12066
+ if (tagsEnabled) {
12067
+ // Tags are enabled AND options are selected: tags module has rendered them.
12068
+ // Clear valueDisplayEl as tags are the primary display.
12069
+ valueDisplayEl.innerHTML = '';
12040
12070
  }
12041
12071
  else {
12042
- // If no displayTemplate is provided, use the default comma-separated list of selected options
12043
- content = this.getSelectedOptionsText();
12072
+ // Tags are not enabled AND options are selected: render normal text display.
12073
+ var content = '';
12074
+ if (this._config.displayTemplate) {
12075
+ content = this.renderDisplayTemplateForSelected(this.getSelectedOptions());
12076
+ }
12077
+ else {
12078
+ content = this.getSelectedOptionsText();
12079
+ }
12080
+ valueDisplayEl.innerHTML = content;
12044
12081
  }
12045
- this._valueDisplayElement.innerHTML = content;
12046
12082
  }
12047
12083
  }
12048
12084
  };
12085
+ /**
12086
+ * Check if an option was originally disabled in the HTML
12087
+ */
12088
+ KTSelect.prototype._isOptionOriginallyDisabled = function (value) {
12089
+ var originalOption = Array.from(this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
12090
+ return originalOption ? originalOption.disabled : false;
12091
+ };
12049
12092
  /**
12050
12093
  * Update CSS classes for selected options
12051
12094
  */
12052
12095
  KTSelect.prototype._updateSelectedOptionClass = function () {
12096
+ var _this = this;
12053
12097
  var allOptions = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
12054
12098
  var selectedValues = this._state.getSelectedOptions();
12055
12099
  var maxReached = typeof this._config.maxSelections === 'number' &&
@@ -12061,9 +12105,11 @@ var KTSelect = /** @class */ (function (_super) {
12061
12105
  if (!optionValue)
12062
12106
  return;
12063
12107
  var isSelected = selectedValues.includes(optionValue);
12108
+ var isOriginallyDisabled = _this._isOptionOriginallyDisabled(optionValue);
12064
12109
  if (isSelected) {
12065
12110
  option.classList.add('selected');
12066
12111
  option.setAttribute('aria-selected', 'true');
12112
+ // Selected options should not be visually hidden or disabled by maxSelections logic
12067
12113
  option.classList.remove('hidden');
12068
12114
  option.classList.remove('disabled');
12069
12115
  option.removeAttribute('aria-disabled');
@@ -12071,7 +12117,8 @@ var KTSelect = /** @class */ (function (_super) {
12071
12117
  else {
12072
12118
  option.classList.remove('selected');
12073
12119
  option.setAttribute('aria-selected', 'false');
12074
- if (maxReached) {
12120
+ // An option should be disabled if it was originally disabled OR if maxSelections is reached
12121
+ if (isOriginallyDisabled || maxReached) {
12075
12122
  option.classList.add('disabled');
12076
12123
  option.setAttribute('aria-disabled', 'true');
12077
12124
  }
@@ -12131,17 +12178,6 @@ var KTSelect = /** @class */ (function (_super) {
12131
12178
  * EVENT HANDLERS
12132
12179
  * ========================================================================
12133
12180
  */
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
12181
  /**
12146
12182
  * Handle click within the dropdown
12147
12183
  */
@@ -12182,6 +12218,13 @@ var KTSelect = /** @class */ (function (_super) {
12182
12218
  }
12183
12219
  if (this._config.debug)
12184
12220
  console.log('Option clicked:', optionValue);
12221
+ // If in single-select mode and the clicked option is already selected, just close the dropdown.
12222
+ if (!this._config.multiple && this._state.isSelected(optionValue)) {
12223
+ if (this._config.debug)
12224
+ console.log('Single select mode: clicked already selected option. Closing dropdown.');
12225
+ this.closeDropdown();
12226
+ return;
12227
+ }
12185
12228
  // Use toggleSelection instead of _selectOption to prevent re-rendering
12186
12229
  this.toggleSelection(optionValue);
12187
12230
  };
@@ -12245,7 +12288,13 @@ var KTSelect = /** @class */ (function (_super) {
12245
12288
  * Get value display element
12246
12289
  */
12247
12290
  KTSelect.prototype.getValueDisplayElement = function () {
12248
- return this._valueDisplayElement;
12291
+ return this._displayElement;
12292
+ };
12293
+ /**
12294
+ * Get wrapper element
12295
+ */
12296
+ KTSelect.prototype.getWrapperElement = function () {
12297
+ return this._wrapperElement;
12249
12298
  };
12250
12299
  /**
12251
12300
  * Show all options in the dropdown
@@ -12255,6 +12304,7 @@ var KTSelect = /** @class */ (function (_super) {
12255
12304
  var options = Array.from(this._wrapperElement.querySelectorAll("[data-kt-select-option]"));
12256
12305
  // Show all options by removing the hidden class and any inline styles
12257
12306
  options.forEach(function (option) {
12307
+ var _a;
12258
12308
  // Remove hidden class
12259
12309
  option.classList.remove('hidden');
12260
12310
  // Clean up any existing inline styles for backward compatibility
@@ -12268,7 +12318,7 @@ var KTSelect = /** @class */ (function (_super) {
12268
12318
  }
12269
12319
  else {
12270
12320
  // Otherwise, remove just the display property
12271
- option.setAttribute('style', styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim());
12321
+ 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
12322
  }
12273
12323
  }
12274
12324
  }
@@ -12278,7 +12328,7 @@ var KTSelect = /** @class */ (function (_super) {
12278
12328
  this._searchInputElement.value = '';
12279
12329
  // If we have a search module, clear any search filtering
12280
12330
  if (this._searchModule) {
12281
- this._searchModule.clearSearchHighlights();
12331
+ this._searchModule.clearSearch();
12282
12332
  }
12283
12333
  }
12284
12334
  };
@@ -12307,7 +12357,7 @@ var KTSelect = /** @class */ (function (_super) {
12307
12357
  // Get current selection state
12308
12358
  var isSelected = this._state.isSelected(value);
12309
12359
  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));
12360
+ console.log("toggleSelection called for value: ".concat(value, ", isSelected: ").concat(isSelected, ", multiple: ").concat(this._config.multiple));
12311
12361
  // If already selected in single select mode, do nothing (can't deselect in single select)
12312
12362
  if (isSelected && !this._config.multiple) {
12313
12363
  if (this._config.debug)
@@ -12316,9 +12366,9 @@ var KTSelect = /** @class */ (function (_super) {
12316
12366
  }
12317
12367
  if (this._config.debug)
12318
12368
  console.log("Toggling selection for option: ".concat(value, ", currently selected: ").concat(isSelected));
12319
- // Ensure any search highlights are cleared when selection changes
12369
+ // Ensure any search input is cleared when selection changes
12320
12370
  if (this._searchModule) {
12321
- this._searchModule.clearSearchHighlights();
12371
+ this._searchModule.clearSearch();
12322
12372
  }
12323
12373
  // Toggle the selection in the state
12324
12374
  this._state.toggleSelectedOptions(value);
@@ -12341,15 +12391,14 @@ var KTSelect = /** @class */ (function (_super) {
12341
12391
  // Update option classes without re-rendering the dropdown content
12342
12392
  this._updateSelectedOptionClass();
12343
12393
  // For single select mode, always close the dropdown after selection
12344
- // For multiple select mode, only close if closeOnSelect is true
12345
12394
  if (!this._config.multiple) {
12346
12395
  if (this._config.debug)
12347
12396
  console.log('About to call closeDropdown() for single select mode - always close after selection');
12348
12397
  this.closeDropdown();
12349
12398
  }
12350
- else if (this._config.closeOnSelect) {
12399
+ else {
12351
12400
  if (this._config.debug)
12352
- console.log('About to call closeDropdown() for multiple select with closeOnSelect:true');
12401
+ console.log('About to call closeDropdown() for multiple select');
12353
12402
  this.closeDropdown();
12354
12403
  }
12355
12404
  // Dispatch custom change event with additional data
@@ -12449,9 +12498,9 @@ var KTSelect = /** @class */ (function (_super) {
12449
12498
  .then(function () {
12450
12499
  // Update options in the dropdown
12451
12500
  _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();
12501
+ // Refresh the search module to update focus and cache
12502
+ if (_this._searchModule) {
12503
+ _this._searchModule.refreshAfterSearch();
12455
12504
  }
12456
12505
  })
12457
12506
  .catch(function (error) {
@@ -12548,19 +12597,47 @@ var KTSelect = /** @class */ (function (_super) {
12548
12597
  * Centralized keyboard event handler for all select modes
12549
12598
  */
12550
12599
  KTSelect.prototype._handleKeyboardEvent = function (event) {
12600
+ // If the event target is the search input and the event was already handled (defaultPrevented),
12601
+ // then return early to avoid duplicate processing by this broader handler.
12602
+ if (event.target === this._searchInputElement && event.defaultPrevented) {
12603
+ return;
12604
+ }
12551
12605
  var isOpen = this._dropdownIsOpen;
12552
12606
  var config = this._config;
12553
12607
  var focusManager = this._focusManager;
12554
12608
  var buffer = this._typeToSearchBuffer;
12555
- // Ignore modifier keys
12609
+ // If the event target is the search input, let it handle most typing keys naturally.
12610
+ if (event.target === this._searchInputElement) {
12611
+ // Allow navigation keys like ArrowDown, ArrowUp, Escape, Enter (for search/selection) to be handled by the logic below.
12612
+ // For other keys (characters, space, backspace, delete), let the input field process them.
12613
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' &&
12614
+ event.key !== 'Escape' && event.key !== 'Enter' && event.key !== 'Tab' &&
12615
+ event.key !== 'Home' && event.key !== 'End') {
12616
+ // If it's a character key and we are NOT type-to-searching (because search has focus)
12617
+ // then let the input field handle it for its own value.
12618
+ // The search module's 'input' event will handle filtering based on the input's value.
12619
+ buffer.clear(); // Clear type-to-search buffer when typing in search field
12620
+ return;
12621
+ }
12622
+ // For Enter specifically in search input, we might want to select the focused option or submit search.
12623
+ // This is handled later in the switch.
12624
+ }
12625
+ // Ignore modifier keys (except for specific combinations if added later)
12556
12626
  if (event.altKey || event.ctrlKey || event.metaKey)
12557
12627
  return;
12558
- // Type-to-search: only for single char keys
12559
- if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/)) {
12628
+ // Type-to-search: only for single char keys, when search input does not have focus
12629
+ if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/) && document.activeElement !== this._searchInputElement) {
12560
12630
  buffer.push(event.key);
12561
12631
  var str = buffer.getBuffer();
12562
- focusManager.focusByString(str);
12563
- return;
12632
+ if (isOpen) {
12633
+ focusManager.focusByString(str);
12634
+ }
12635
+ else {
12636
+ // If closed, type-to-search could potentially open and select.
12637
+ // For now, let's assume it only works when open or opens it first.
12638
+ // Or, we could find the matching option and set it directly without opening.
12639
+ }
12640
+ return; // Type-to-search handles the event
12564
12641
  }
12565
12642
  switch (event.key) {
12566
12643
  case 'ArrowDown':
@@ -12594,18 +12671,28 @@ var KTSelect = /** @class */ (function (_super) {
12594
12671
  case 'Enter':
12595
12672
  case ' ': // Space
12596
12673
  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
- }
12674
+ var focusedOptionEl = this._focusManager.getFocusedOption();
12675
+ if (focusedOptionEl) {
12676
+ var val = focusedOptionEl.dataset.value;
12677
+ // If single select, and the item is already selected, just close.
12678
+ if (val !== undefined && !this._config.multiple && this._state.isSelected(val)) {
12679
+ if (this._config.debug)
12680
+ console.log('Enter on already selected item in single-select mode. Closing.');
12681
+ this.closeDropdown();
12682
+ event.preventDefault();
12683
+ break;
12605
12684
  }
12606
12685
  }
12607
- // Prevent form submit
12608
- event.preventDefault();
12686
+ // Proceed with selection if not handled above
12687
+ this.selectFocusedOption();
12688
+ // Close dropdown if configured to do so (for new selections)
12689
+ if (!this._config.multiple) {
12690
+ // This will also be true for the case handled above, but closeDropdown is idempotent.
12691
+ // However, the break above prevents this from being reached for that specific case.
12692
+ this.closeDropdown();
12693
+ }
12694
+ event.preventDefault(); // Prevent form submission or other default actions
12695
+ break;
12609
12696
  }
12610
12697
  else {
12611
12698
  this.openDropdown();
@@ -12649,6 +12736,76 @@ var KTSelect = /** @class */ (function (_super) {
12649
12736
  }).filter(Boolean)));
12650
12737
  return contentArray.join(displaySeparator);
12651
12738
  };
12739
+ KTSelect.prototype.getDisplayElement = function () {
12740
+ return this._displayElement;
12741
+ };
12742
+ KTSelect.prototype._observeNativeSelect = function () {
12743
+ var _this = this;
12744
+ if (this._mutationObserver)
12745
+ return; // Prevent double observers
12746
+ this._mutationObserver = new MutationObserver(function (mutations) {
12747
+ var needsRebuild = false;
12748
+ var needsSelectionSync = false;
12749
+ for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
12750
+ var mutation = mutations_1[_i];
12751
+ if (mutation.type === 'childList') {
12752
+ // Option(s) added or removed
12753
+ needsRebuild = true;
12754
+ }
12755
+ else if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
12756
+ if (mutation.attributeName === 'selected') {
12757
+ needsSelectionSync = true;
12758
+ }
12759
+ }
12760
+ }
12761
+ if (needsRebuild) {
12762
+ // Rebuild the custom dropdown options
12763
+ _this._rebuildOptionsFromNative();
12764
+ }
12765
+ if (needsSelectionSync) {
12766
+ _this._syncSelectionFromNative();
12767
+ }
12768
+ });
12769
+ this._mutationObserver.observe(this._element, {
12770
+ childList: true,
12771
+ attributes: true,
12772
+ subtree: true,
12773
+ attributeFilter: ['selected'],
12774
+ });
12775
+ };
12776
+ KTSelect.prototype._rebuildOptionsFromNative = function () {
12777
+ var _this = this;
12778
+ // Remove and rebuild the custom dropdown options from the native select
12779
+ if (this._dropdownContentElement) {
12780
+ var optionsContainer_1 = this._dropdownContentElement.querySelector('[data-kt-select-options]');
12781
+ if (optionsContainer_1) {
12782
+ optionsContainer_1.innerHTML = '';
12783
+ var options = Array.from(this._element.querySelectorAll('option'));
12784
+ options.forEach(function (optionElement) {
12785
+ if (optionElement.value === '' &&
12786
+ optionElement.textContent.trim() === '') {
12787
+ return;
12788
+ }
12789
+ var selectOption = new option_1.KTSelectOption(optionElement, _this._config);
12790
+ var renderedOption = selectOption.render();
12791
+ optionsContainer_1.appendChild(renderedOption);
12792
+ });
12793
+ // Update internal references
12794
+ this._options = this._wrapperElement.querySelectorAll('[data-kt-select-option]');
12795
+ }
12796
+ }
12797
+ // Sync selection after rebuilding
12798
+ this._syncSelectionFromNative();
12799
+ this.updateSelectedOptionDisplay();
12800
+ this._updateSelectedOptionClass();
12801
+ };
12802
+ KTSelect.prototype._syncSelectionFromNative = function () {
12803
+ // Sync internal state from the native select's selected options
12804
+ var selected = Array.from(this._element.querySelectorAll('option:checked')).map(function (opt) { return opt.value; });
12805
+ this._state.setSelectedOptions(this._config.multiple ? selected : selected[0] || '');
12806
+ this.updateSelectedOptionDisplay();
12807
+ this._updateSelectedOptionClass();
12808
+ };
12652
12809
  /**
12653
12810
  * ========================================================================
12654
12811
  * STATIC METHODS
@@ -14564,32 +14721,6 @@ var KTSelectTags = /** @class */ (function () {
14564
14721
  wrapper.insertBefore(tag, _this._valueDisplayElement);
14565
14722
  });
14566
14723
  };
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
14724
  /**
14594
14725
  * Remove a tag and its selection
14595
14726
  */
@@ -15451,30 +15582,6 @@ var KTDom = {
15451
15582
  }
15452
15583
  return attributes;
15453
15584
  },
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
15585
  ready: function (callback) {
15479
15586
  if (document.readyState === 'loading') {
15480
15587
  document.addEventListener('DOMContentLoaded', function () {
@@ -15492,7 +15599,7 @@ exports["default"] = KTDom;
15492
15599
  /***/ }),
15493
15600
 
15494
15601
  /***/ 9011:
15495
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
15602
+ /***/ (function(__unused_webpack_module, exports) {
15496
15603
 
15497
15604
 
15498
15605
  /**
@@ -15500,15 +15607,12 @@ exports["default"] = KTDom;
15500
15607
  * Copyright 2025 by Keenthemes Inc
15501
15608
  */
15502
15609
  Object.defineProperty(exports, "__esModule", ({ value: true }));
15503
- exports.TypeToSearchBuffer = exports.EventManager = exports.FocusManager = exports.highlightTextInElementDebounced = void 0;
15610
+ exports.TypeToSearchBuffer = exports.EventManager = exports.FocusManager = void 0;
15504
15611
  exports.formatCurrency = formatCurrency;
15505
15612
  exports.filterOptions = filterOptions;
15506
- exports.highlightTextInElement = highlightTextInElement;
15507
15613
  exports.debounce = debounce;
15508
15614
  exports.renderTemplateString = renderTemplateString;
15509
15615
  exports.stringToElement = stringToElement;
15510
- // utils.ts
15511
- var templates_1 = __webpack_require__(9069);
15512
15616
  /**
15513
15617
  * Format a number as a currency string
15514
15618
  */
@@ -15522,148 +15626,50 @@ function formatCurrency(value) {
15522
15626
  * Filter options based on a search query
15523
15627
  */
15524
15628
  function filterOptions(options, query, config, dropdownElement, onVisibleCount) {
15525
- var _a;
15526
15629
  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
15630
+ // For empty query, make all options visible
15631
+ // The KTSelectSearch class is now responsible for restoring original content before calling this.
15533
15632
  if (!query || query.trim() === '') {
15534
- // Just make all options visible without highlighting
15535
15633
  for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
15536
15634
  var option = options_1[_i];
15537
- // Make option visible by removing hidden classes and inline styles
15538
15635
  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;
15636
+ // Remove inline display style if it was used to hide
15637
+ if (option.style.display === 'none') {
15638
+ option.style.display = '';
15561
15639
  }
15640
+ // At this point, option.innerHTML should be its original.
15562
15641
  visibleOptionsCount++;
15563
15642
  }
15564
- // Call the callback with the visible count if provided
15565
15643
  if (onVisibleCount) {
15566
15644
  onVisibleCount(visibleOptionsCount);
15567
15645
  }
15568
15646
  return visibleOptionsCount;
15569
15647
  }
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
15648
+ var queryLower = query.toLowerCase();
15649
+ for (var _a = 0, options_2 = options; _a < options_2.length; _a++) {
15650
+ var option = options_2[_a];
15651
+ // Use data-text for matching if available, otherwise fall back to textContent
15652
+ var optionText = (option.dataset.text || option.textContent || '').toLowerCase();
15653
+ var isMatch = optionText.includes(queryLower);
15654
+ if (isMatch) {
15579
15655
  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
- }
15656
+ if (option.style.display === 'none')
15657
+ option.style.display = ''; // Ensure visible
15592
15658
  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
15659
  }
15600
15660
  else {
15601
- // Hide option using hidden class
15602
15661
  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
15662
  }
15663
+ // Early exit if maxItems limit is reached (optional)
15664
+ // if (config.searchMaxItems && visibleOptionsCount >= config.searchMaxItems) {
15665
+ // break;
15666
+ // }
15620
15667
  }
15621
- // Call the callback with the visible count if provided
15622
15668
  if (onVisibleCount) {
15623
15669
  onVisibleCount(visibleOptionsCount);
15624
15670
  }
15625
15671
  return visibleOptionsCount;
15626
15672
  }
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
15673
  /**
15668
15674
  * Focus manager for keyboard navigation
15669
15675
  * Consolidates redundant focus management logic into shared functions
@@ -15765,6 +15771,7 @@ var FocusManager = /** @class */ (function () {
15765
15771
  if (!option.classList.contains('disabled') &&
15766
15772
  option.getAttribute('aria-disabled') !== 'true' &&
15767
15773
  (((_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)))) {
15774
+ this.resetFocus();
15768
15775
  this._focusedOptionIndex = idx;
15769
15776
  this.applyFocus(option);
15770
15777
  this.scrollIntoView(option);
@@ -15823,12 +15830,14 @@ var FocusManager = /** @class */ (function () {
15823
15830
  FocusManager.prototype.applyFocus = function (option) {
15824
15831
  if (!option)
15825
15832
  return;
15833
+ // Ensure it's not disabled
15826
15834
  if (option.classList.contains('disabled') || option.getAttribute('aria-disabled') === 'true') {
15827
15835
  return;
15828
15836
  }
15829
- this.resetFocus();
15837
+ // DO NOT CALL resetFocus() here. Caller's responsibility.
15830
15838
  option.classList.add(this._focusClass);
15831
15839
  option.classList.add(this._hoverClass);
15840
+ // _triggerFocusChange needs _focusedOptionIndex to be set by the caller before this.
15832
15841
  this._triggerFocusChange();
15833
15842
  };
15834
15843
  /**
@@ -15841,12 +15850,7 @@ var FocusManager = /** @class */ (function () {
15841
15850
  focusedElements.forEach(function (element) {
15842
15851
  element.classList.remove(_this._focusClass, _this._hoverClass);
15843
15852
  });
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
- }
15853
+ this._focusedOptionIndex = null; // Always reset the index
15850
15854
  };
15851
15855
  /**
15852
15856
  * Ensure the focused option is visible in the scrollable container
@@ -15875,10 +15879,14 @@ var FocusManager = /** @class */ (function () {
15875
15879
  var options = this.getVisibleOptions();
15876
15880
  var index = options.findIndex(function (option) { return option.dataset.value === value; });
15877
15881
  if (index >= 0) {
15878
- this._focusedOptionIndex = index;
15879
- this.applyFocus(options[index]);
15880
- this.scrollIntoView(options[index]);
15881
- return true;
15882
+ var optionToFocus = options[index];
15883
+ if (!optionToFocus.classList.contains('disabled') && optionToFocus.getAttribute('aria-disabled') !== 'true') {
15884
+ this.resetFocus();
15885
+ this._focusedOptionIndex = index;
15886
+ this.applyFocus(optionToFocus);
15887
+ this.scrollIntoView(optionToFocus);
15888
+ return true;
15889
+ }
15882
15890
  }
15883
15891
  return false;
15884
15892
  };
@@ -16095,20 +16103,19 @@ var utils_1 = __webpack_require__(9011);
16095
16103
  * Users can override any template by providing a matching key in the config.templates object.
16096
16104
  */
16097
16105
  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>",
16106
+ dropdown: "<div data-kt-select-dropdown class=\"kt-select-dropdown hidden {{class}}\" style=\"z-index: {{zindex}};\"></div>",
16107
+ options: "<ul role=\"listbox\" aria-label=\"{{label}}\" class=\"kt-select-options {{class}}\" data-kt-select-options=\"true\"></ul>",
16108
+ error: "<li class=\"kt-select-error\" role=\"alert\"></li>",
16102
16109
  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>",
16110
+ 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",
16111
+ placeholder: "<div data-kt-select-placeholder class=\"kt-select-placeholder {{class}}\"></div>",
16112
+ 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",
16113
+ 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",
16114
+ 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>",
16115
+ empty: "<li data-kt-select-empty class=\"kt-select-no-result {{class}}\" role=\"status\"></li>",
16116
+ loading: "<li class=\"kt-select-loading {{class}}\" role=\"status\" aria-live=\"polite\"></li>",
16117
+ tag: "<div data-kt-select-tag=\"true\" class=\"kt-select-tag {{class}}\"></div>",
16118
+ loadMore: "<li class=\"kt-select-load-more {{class}}\" data-kt-select-load-more=\"true\"></li>",
16112
16119
  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
16120
  };
16114
16121
  /**
@@ -16147,30 +16154,31 @@ function getTemplateStrings(config) {
16147
16154
  * Default templates for KTSelect component
16148
16155
  */
16149
16156
  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
16157
  /**
16159
16158
  * Renders the dropdown content
16160
16159
  */
16161
16160
  dropdown: function (config) {
16161
+ var _a;
16162
16162
  var template = getTemplateStrings(config).dropdown;
16163
- var content = config.content || '';
16163
+ // If a custom dropdownTemplate is provided, it's responsible for its own content.
16164
+ // Otherwise, the base template is used, and content is appended later.
16164
16165
  if (config.dropdownTemplate) {
16165
- content = (0, utils_1.renderTemplateString)(config.dropdownTemplate, {
16166
+ var renderedCustomTemplate = (0, utils_1.renderTemplateString)(config.dropdownTemplate, {
16166
16167
  zindex: config.zindex ? String(config.zindex) : '',
16167
- content: config.content || '',
16168
+ // content: config.content || '', // No longer pass content to custom template directly here
16168
16169
  class: config.dropdownClass || '',
16169
16170
  });
16171
+ // The custom template IS the dropdown element
16172
+ var customDropdownEl = stringToElement(renderedCustomTemplate);
16173
+ if (config.zindex)
16174
+ customDropdownEl.style.zIndex = String(config.zindex);
16175
+ if (config.dropdownClass)
16176
+ (_a = customDropdownEl.classList).add.apply(_a, config.dropdownClass.split(' '));
16177
+ return customDropdownEl;
16170
16178
  }
16171
16179
  var html = template
16172
16180
  .replace('{{zindex}}', config.zindex ? String(config.zindex) : '')
16173
- .replace('{{content}}', content)
16181
+ // .replace('{{content}}', '') // Content is no longer part of the base template string
16174
16182
  .replace('{{class}}', config.dropdownClass || '');
16175
16183
  return stringToElement(html);
16176
16184
  },
@@ -16182,7 +16190,7 @@ exports.defaultTemplates = {
16182
16190
  var html = template
16183
16191
  .replace('{{label}}', config.label || 'Options')
16184
16192
  .replace('{{height}}', config.height ? String(config.height) : '250')
16185
- .replace('{{options}}', config.options || '')
16193
+ // .replace('{{options}}', '') // Options are now appended dynamically
16186
16194
  .replace('{{class}}', config.optionsClass || '');
16187
16195
  return stringToElement(html);
16188
16196
  },
@@ -16190,84 +16198,98 @@ exports.defaultTemplates = {
16190
16198
  * Renders the load more button for pagination
16191
16199
  */
16192
16200
  loadMore: function (config) {
16193
- var html = getTemplateStrings(config).loadMore.replace('{{loadMoreText}}', config.loadMoreText || 'Load more...');
16194
- return stringToElement(html);
16201
+ var html = getTemplateStrings(config)
16202
+ .loadMore // .replace('{{loadMoreText}}', config.loadMoreText || 'Load more...') // Content is no longer in template string
16203
+ .replace('{{class}}', config.loadMoreClass || '');
16204
+ var element = stringToElement(html);
16205
+ element.textContent = config.loadMoreText || 'Load more...';
16206
+ return element;
16195
16207
  },
16196
16208
  /**
16197
16209
  * Renders an error message in the dropdown
16198
16210
  */
16199
16211
  error: function (config) {
16212
+ // Changed return type to HTMLElement
16200
16213
  var template = getTemplateStrings(config).error;
16201
- return template
16202
- .replace('{{errorMessage}}', config.errorMessage || 'An error occurred')
16214
+ var html = template
16215
+ // .replace('{{errorMessage}}', config.errorMessage || 'An error occurred') // Content is no longer in template string
16203
16216
  .replace('{{class}}', config.errorClass || '');
16217
+ var element = stringToElement(html);
16218
+ element.textContent = config.errorMessage || 'An error occurred';
16219
+ return element;
16204
16220
  },
16205
16221
  /**
16206
16222
  * Renders the main container for the select component
16207
16223
  */
16208
16224
  wrapper: function (config) {
16209
- var html = getTemplateStrings(config).wrapper
16210
- .replace('{{class}}', config.wrapperClass || '');
16225
+ var html = getTemplateStrings(config).wrapper.replace('{{class}}', config.wrapperClass || '');
16211
16226
  var element = stringToElement(html);
16212
- element.setAttribute('data-kt-select-combobox', config.combobox ? 'true' : 'false');
16213
- element.setAttribute('data-kt-select-tags', config.tags ? 'true' : 'false');
16214
16227
  return element;
16215
16228
  },
16216
16229
  /**
16217
16230
  * Renders the display element (trigger) for the select
16218
16231
  */
16219
16232
  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')
16233
+ var html = getTemplateStrings(config)
16234
+ .display.replace('{{tabindex}}', config.disabled ? '-1' : '0')
16231
16235
  .replace('{{label}}', config.label || config.placeholder || 'Select...')
16232
16236
  .replace('{{disabled}}', config.disabled ? 'aria-disabled="true"' : '')
16233
16237
  .replace('{{placeholder}}', config.placeholder || 'Select...')
16234
- .replace('{{class}}', config.displayClass || '')
16235
- .replace('{{content}}', content);
16236
- return stringToElement(html);
16238
+ .replace('{{class}}', config.displayClass || '');
16239
+ var element = stringToElement(html);
16240
+ // Add data-multiple attribute if in multiple select mode
16241
+ if (config.multiple) {
16242
+ element.setAttribute('data-multiple', 'true');
16243
+ }
16244
+ return element;
16237
16245
  },
16238
16246
  /**
16239
16247
  * Renders a single option
16240
16248
  */
16241
16249
  option: function (option, config) {
16250
+ var _a, _b;
16242
16251
  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;
16252
+ var optionData;
16253
+ if (isHtmlOption) {
16254
+ // If it's a plain HTMLOptionElement, construct data similarly to how KTSelectOption would
16255
+ // This branch might be less common if KTSelectOption instances are always used for rendering.
16256
+ var el = option;
16257
+ var textContent = el.textContent || '';
16258
+ 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]) || {}));
16259
+ }
16260
+ else {
16261
+ // If it's a KTSelectOption class instance (from './option')
16262
+ // which should have the getOptionDataForTemplate method.
16263
+ optionData = option.getOptionDataForTemplate();
16264
+ }
16265
+ var content = optionData.text; // Default content to option's text
16252
16266
  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
- });
16267
+ // Use the user-provided template string, rendering with the full optionData.
16268
+ // renderTemplateString will replace {{key}} with values from optionData.
16269
+ content = (0, utils_1.renderTemplateString)(config.optionTemplate, optionData);
16262
16270
  }
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);
16271
+ else {
16272
+ content = optionData.text || optionData.content; // Prefer explicit text, fallback to content
16273
+ }
16274
+ // Use the core option template string as the base structure.
16275
+ var baseTemplate = getTemplateStrings(config).option;
16276
+ var optionClasses = [config.optionClass || ''];
16277
+ if (optionData.disabled) {
16278
+ optionClasses.push('disabled');
16279
+ }
16280
+ // Populate the base template for the <li> attributes.
16281
+ // The actual display content (text or custom HTML) will be set on the inner span later.
16282
+ var html = (0, utils_1.renderTemplateString)(baseTemplate, __assign(__assign({}, optionData), { class: optionClasses.join(' ').trim() || '', selected: optionData.selected
16283
+ ? 'aria-selected="true"'
16284
+ : 'aria-selected="false"', disabled: optionData.disabled ? 'aria-disabled="true"' : '', content: content }));
16285
+ var element = stringToElement(html);
16286
+ // If a custom option template is provided, replace the element's innerHTML with the content.
16287
+ if (config.optionTemplate) {
16288
+ element.innerHTML = content;
16289
+ }
16290
+ // Ensure data-text attribute is set to the original, clean text for searching/filtering
16291
+ element.setAttribute('data-text', ((_b = optionData === null || optionData === void 0 ? void 0 : optionData.text) === null || _b === void 0 ? void 0 : _b.trim()) || '');
16292
+ return element;
16271
16293
  },
16272
16294
  /**
16273
16295
  * Renders the search input
@@ -16282,68 +16304,79 @@ exports.defaultTemplates = {
16282
16304
  * Renders the no results message
16283
16305
  */
16284
16306
  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);
16307
+ var html = getTemplateStrings(config).empty.replace('{{class}}', config.emptyClass || '');
16308
+ var element = stringToElement(html);
16309
+ element.textContent = config.searchNotFoundText || 'No results found';
16310
+ return element;
16289
16311
  },
16290
16312
  /**
16291
16313
  * Renders the loading state
16292
16314
  */
16293
16315
  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);
16316
+ var html = getTemplateStrings(config).loading.replace('{{class}}', config.loadingClass || '');
16317
+ var element = stringToElement(html);
16318
+ element.textContent = loadingMessage || 'Loading options...';
16319
+ return element;
16298
16320
  },
16299
16321
  /**
16300
16322
  * Renders a tag for multi-select
16301
16323
  */
16302
16324
  tag: function (option, config) {
16325
+ var _a;
16303
16326
  var template = getTemplateStrings(config).tag;
16304
- var content = option.title;
16327
+ var preparedContent = option.title; // Default content is the option's title
16305
16328
  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));
16329
+ var tagTemplateString_1 = config.tagTemplate;
16330
+ var optionValue = option.getAttribute('data-value') || option.value;
16331
+ // Replace all {{varname}} in option.innerHTML with values from _config.optionsConfig
16332
+ Object.entries(((_a = config.optionsConfig) === null || _a === void 0 ? void 0 : _a[optionValue]) || {}).forEach(function (_a) {
16333
+ var key = _a[0], val = _a[1];
16334
+ if (typeof val === 'string' ||
16335
+ typeof val === 'number' ||
16336
+ typeof val === 'boolean') {
16337
+ tagTemplateString_1 = tagTemplateString_1.replace(new RegExp("{{".concat(key, "}}"), 'g'), String(val));
16314
16338
  }
16315
16339
  });
16316
- content = (0, utils_1.renderTemplateString)(tagTemplate_1, {
16340
+ // Render the custom tag template with option data
16341
+ preparedContent = (0, utils_1.renderTemplateString)(tagTemplateString_1, {
16317
16342
  title: option.title,
16318
16343
  id: option.id,
16319
- class: config.tagClass || '',
16320
- content: option.innerHTML,
16321
- text: option.innerText,
16344
+ class: config.tagClass || '', // This class is for content, not the main tag div
16345
+ // content: option.innerHTML, // Avoid direct innerHTML from option due to potential XSS
16346
+ text: option.innerText || option.textContent || '',
16347
+ value: optionValue,
16322
16348
  });
16323
16349
  }
16324
- content += getTemplateStrings(config).tagRemoveButton;
16350
+ // Append the remove button HTML string to the prepared content
16351
+ preparedContent += getTemplateStrings(config).tagRemoveButton;
16325
16352
  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);
16353
+ // .replace('{{title}}', option.title) // Title is part of preparedContent if using custom template
16354
+ // .replace('{{id}}', option.id) // ID is part of preparedContent if using custom template
16355
+ .replace('{{class}}', config.tagClass || ''); // Class for the main tag div
16356
+ var element = stringToElement(html);
16357
+ element.innerHTML = preparedContent; // Set the fully prepared content (text/HTML + remove button)
16358
+ return element;
16331
16359
  },
16332
16360
  /**
16333
16361
  * Renders the placeholder for the select
16334
16362
  */
16335
16363
  placeholder: function (config) {
16336
- var html = getTemplateStrings(config)
16337
- .placeholder.replace('{{class}}', config.placeholderClass || '');
16364
+ var html = getTemplateStrings(config).placeholder.replace('{{class}}', config.placeholderClass || '');
16338
16365
  var content = config.placeholder || 'Select...';
16339
16366
  if (config.placeholderTemplate) {
16340
16367
  content = (0, utils_1.renderTemplateString)(config.placeholderTemplate, {
16341
16368
  placeholder: config.placeholder || 'Select...',
16342
16369
  class: config.placeholderClass || '',
16343
16370
  });
16371
+ var element = stringToElement(html);
16372
+ element.innerHTML = content; // For templates, content can be HTML
16373
+ return element;
16374
+ }
16375
+ else {
16376
+ var element = stringToElement(html);
16377
+ element.textContent = content; // For simple text, use textContent
16378
+ return element;
16344
16379
  }
16345
- html = html.replace('{{content}}', content);
16346
- return stringToElement(html);
16347
16380
  },
16348
16381
  };
16349
16382
 
@@ -17492,9 +17525,9 @@ exports.DefaultConfig = {
17492
17525
  paginationLimitParam: 'limit', // Parameter name for items per page
17493
17526
  paginationTotalParam: 'total', // Parameter name for total items
17494
17527
  // Selection Behavior
17528
+ allowClear: false, // Allow clearing the selection (if true, an empty value can be set)
17495
17529
  multiple: false, // Enable/disable multi-select
17496
17530
  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
17531
  disabled: false, // Disable the select component
17499
17532
  isRequired: false, // Make selection required
17500
17533
  // Search Functionality
@@ -17504,7 +17537,6 @@ exports.DefaultConfig = {
17504
17537
  searchMinLength: 0, // Minimum characters required to trigger search
17505
17538
  searchMaxItems: 50, // Maximum number of search results to display
17506
17539
  searchNotFoundText: 'No results found', // Text to display when no search results are found
17507
- searchHighlight: true, // Highlight matching search terms within the options
17508
17540
  clearSearchOnClose: true, // Clear search input when dropdown closes
17509
17541
  // Multi-Select Display
17510
17542
  selectAllText: 'Select all', // Text for the "Select All" option (if implemented)
@@ -17529,7 +17561,7 @@ var KTSelectState = /** @class */ (function () {
17529
17561
  this._config = this._initDefaultConfig(config);
17530
17562
  }
17531
17563
  KTSelectState.prototype._initDefaultConfig = function (config) {
17532
- return __assign(__assign({}, exports.DefaultConfig), config);
17564
+ return __assign(__assign(__assign({}, exports.DefaultConfig), config), config.config);
17533
17565
  };
17534
17566
  KTSelectState.prototype.setItems = function (items, query) {
17535
17567
  var _this = this;
@@ -17598,11 +17630,17 @@ var KTSelectState = /** @class */ (function () {
17598
17630
  return this._config.items || [];
17599
17631
  };
17600
17632
  KTSelectState.prototype.setItemsFromOptions = function (options) {
17601
- this._config.items = options.map(function (option) { return ({
17602
- id: option.value,
17603
- title: option.textContent || '',
17604
- // Add other properties from option element if needed
17605
- }); });
17633
+ this._config.items = options.map(function (option) {
17634
+ var item = {
17635
+ id: option.value,
17636
+ title: option.textContent || option.value, // Use value as fallback for title
17637
+ // 'selected' property will be definitively set by _preSelectOptions
17638
+ disabled: option.disabled,
17639
+ };
17640
+ return item;
17641
+ });
17642
+ // The 'selected' status of these items and the overall component selection state
17643
+ // are now fully managed by _preSelectOptions in KTSelect during initialization.
17606
17644
  };
17607
17645
  KTSelectState.prototype.getConfig = function () {
17608
17646
  return this._config;