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