@keenthemes/ktui 1.0.19 → 1.0.21

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 (111) hide show
  1. package/dist/ktui.js +690 -166
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +165 -31
  5. package/examples/image-input/file-upload-example.html +189 -0
  6. package/examples/select/remote-data_.html +5 -0
  7. package/examples/select/test-optimizations.html +227 -0
  8. package/examples/select/test-remote-search.html +151 -0
  9. package/examples/sticky/README.md +158 -0
  10. package/examples/sticky/debug-sticky.html +144 -0
  11. package/examples/sticky/test-runner.html +175 -0
  12. package/examples/sticky/test-sticky-logic.js +369 -0
  13. package/examples/sticky/test-sticky-positioning.html +386 -0
  14. package/examples/toast/example.html +52 -0
  15. package/lib/cjs/components/component.js +59 -5
  16. package/lib/cjs/components/component.js.map +1 -1
  17. package/lib/cjs/components/datatable/datatable-sort.js +4 -0
  18. package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
  19. package/lib/cjs/components/datatable/datatable.js +79 -12
  20. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  21. package/lib/cjs/components/image-input/image-input.js +10 -2
  22. package/lib/cjs/components/image-input/image-input.js.map +1 -1
  23. package/lib/cjs/components/select/combobox.js +50 -20
  24. package/lib/cjs/components/select/combobox.js.map +1 -1
  25. package/lib/cjs/components/select/config.js +1 -0
  26. package/lib/cjs/components/select/config.js.map +1 -1
  27. package/lib/cjs/components/select/dropdown.js +4 -2
  28. package/lib/cjs/components/select/dropdown.js.map +1 -1
  29. package/lib/cjs/components/select/index.js.map +1 -1
  30. package/lib/cjs/components/select/option.js +2 -1
  31. package/lib/cjs/components/select/option.js.map +1 -1
  32. package/lib/cjs/components/select/remote.js +50 -50
  33. package/lib/cjs/components/select/remote.js.map +1 -1
  34. package/lib/cjs/components/select/search.js +15 -5
  35. package/lib/cjs/components/select/search.js.map +1 -1
  36. package/lib/cjs/components/select/select.js +273 -32
  37. package/lib/cjs/components/select/select.js.map +1 -1
  38. package/lib/cjs/components/select/tags.js +3 -1
  39. package/lib/cjs/components/select/tags.js.map +1 -1
  40. package/lib/cjs/components/select/templates.js +6 -0
  41. package/lib/cjs/components/select/templates.js.map +1 -1
  42. package/lib/cjs/components/select/utils.js +23 -10
  43. package/lib/cjs/components/select/utils.js.map +1 -1
  44. package/lib/cjs/components/stepper/stepper.js +59 -12
  45. package/lib/cjs/components/stepper/stepper.js.map +1 -1
  46. package/lib/cjs/components/sticky/sticky.js +52 -14
  47. package/lib/cjs/components/sticky/sticky.js.map +1 -1
  48. package/lib/esm/components/component.js +59 -5
  49. package/lib/esm/components/component.js.map +1 -1
  50. package/lib/esm/components/datatable/datatable-sort.js +4 -0
  51. package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
  52. package/lib/esm/components/datatable/datatable.js +78 -12
  53. package/lib/esm/components/datatable/datatable.js.map +1 -1
  54. package/lib/esm/components/image-input/image-input.js +10 -2
  55. package/lib/esm/components/image-input/image-input.js.map +1 -1
  56. package/lib/esm/components/select/combobox.js +50 -20
  57. package/lib/esm/components/select/combobox.js.map +1 -1
  58. package/lib/esm/components/select/config.js +1 -0
  59. package/lib/esm/components/select/config.js.map +1 -1
  60. package/lib/esm/components/select/dropdown.js +4 -2
  61. package/lib/esm/components/select/dropdown.js.map +1 -1
  62. package/lib/esm/components/select/index.js +1 -1
  63. package/lib/esm/components/select/index.js.map +1 -1
  64. package/lib/esm/components/select/option.js +2 -1
  65. package/lib/esm/components/select/option.js.map +1 -1
  66. package/lib/esm/components/select/remote.js +50 -50
  67. package/lib/esm/components/select/remote.js.map +1 -1
  68. package/lib/esm/components/select/search.js +16 -6
  69. package/lib/esm/components/select/search.js.map +1 -1
  70. package/lib/esm/components/select/select.js +273 -32
  71. package/lib/esm/components/select/select.js.map +1 -1
  72. package/lib/esm/components/select/tags.js +3 -1
  73. package/lib/esm/components/select/tags.js.map +1 -1
  74. package/lib/esm/components/select/templates.js +6 -0
  75. package/lib/esm/components/select/templates.js.map +1 -1
  76. package/lib/esm/components/select/utils.js +23 -10
  77. package/lib/esm/components/select/utils.js.map +1 -1
  78. package/lib/esm/components/stepper/stepper.js +59 -12
  79. package/lib/esm/components/stepper/stepper.js.map +1 -1
  80. package/lib/esm/components/sticky/sticky.js +52 -14
  81. package/lib/esm/components/sticky/sticky.js.map +1 -1
  82. package/package.json +2 -2
  83. package/src/components/component.ts +19 -4
  84. package/src/components/datatable/datatable-sort.ts +6 -0
  85. package/src/components/datatable/datatable.ts +98 -15
  86. package/src/components/datatable/types.ts +5 -1
  87. package/src/components/image-input/image-input.ts +11 -2
  88. package/src/components/image-input/types.ts +1 -0
  89. package/src/components/input/input-group.css +1 -1
  90. package/src/components/input/input.css +1 -1
  91. package/src/components/scrollable/scrollable.css +3 -3
  92. package/src/components/select/combobox.ts +84 -34
  93. package/src/components/select/config.ts +2 -0
  94. package/src/components/select/dropdown.ts +20 -11
  95. package/src/components/select/index.ts +6 -1
  96. package/src/components/select/option.ts +7 -6
  97. package/src/components/select/remote.ts +51 -52
  98. package/src/components/select/search.ts +59 -44
  99. package/src/components/select/select.css +26 -17
  100. package/src/components/select/select.ts +472 -101
  101. package/src/components/select/tags.ts +9 -3
  102. package/src/components/select/templates.ts +10 -0
  103. package/src/components/select/utils.ts +55 -20
  104. package/src/components/select/variants.css +0 -1
  105. package/src/components/stepper/stepper.ts +2 -2
  106. package/src/components/sticky/sticky.ts +47 -16
  107. package/src/components/sticky/types.ts +3 -0
  108. package/src/components/table/table.css +1 -1
  109. package/src/components/textarea/textarea.css +1 -1
  110. package/src/components/toast/toast.css +84 -47
  111. package/src/components/toast/types.ts +3 -0
@@ -61,12 +61,16 @@ var KTSelect = /** @class */ (function (_super) {
61
61
  // Core properties
62
62
  _this._name = 'select';
63
63
  _this._dataOptionPrefix = 'kt-'; // Use 'kt-' prefix to support data-kt-select-option attributes
64
+ // Cached DOM references for performance
65
+ _this._optionsContainer = null;
64
66
  // State
65
67
  _this._dropdownIsOpen = false;
66
68
  _this._comboboxModule = null;
67
69
  _this._tagsModule = null;
68
70
  _this._dropdownModule = null;
69
71
  _this._loadMoreIndicator = null;
72
+ _this._selectAllButton = null;
73
+ _this._selectAllButtonToggle = null;
70
74
  _this._typeToSearchBuffer = new utils_1.TypeToSearchBuffer();
71
75
  _this._mutationObserver = null;
72
76
  // Search debounce timeout
@@ -112,6 +116,10 @@ var KTSelect = /** @class */ (function (_super) {
112
116
  return;
113
117
  if (this._config.debug)
114
118
  console.log('Initializing remote data with URL:', this._config.dataUrl);
119
+ // For remote data, we need to create the HTML structure first
120
+ // so that the component can be properly initialized
121
+ this._createHtmlStructure();
122
+ this._setupElementReferences();
115
123
  // Show loading state
116
124
  this._renderLoadingState();
117
125
  // Fetch remote data
@@ -130,7 +138,10 @@ var KTSelect = /** @class */ (function (_super) {
130
138
  _this._generateOptionsHtml(_this._element);
131
139
  if (_this._config.debug)
132
140
  console.log('Generating options HTML from remote data');
133
- _this._setupComponent();
141
+ // Update the dropdown to show the new options
142
+ _this._updateDropdownWithNewOptions();
143
+ // Complete the component setup with the fetched data
144
+ _this._completeRemoteSetup();
134
145
  // Add pagination "Load More" button if needed
135
146
  if (_this._config.pagination && _this._remoteModule.hasMorePages()) {
136
147
  _this._addLoadMoreButton();
@@ -154,6 +165,108 @@ var KTSelect = /** @class */ (function (_super) {
154
165
  var options = Array.from(this._element.querySelectorAll('option:not([value=""])'));
155
166
  options.forEach(function (option) { return option.remove(); });
156
167
  };
168
+ /**
169
+ * Unified method to render options in dropdown - eliminates code duplication
170
+ */
171
+ KTSelect.prototype._renderOptionsInDropdown = function (optionsData, clearContainer) {
172
+ var _this = this;
173
+ if (clearContainer === void 0) { clearContainer = true; }
174
+ if (!this._dropdownContentElement)
175
+ return;
176
+ // Use cached options container for better performance
177
+ var optionsContainer = this._optionsContainer ||
178
+ this._dropdownContentElement.querySelector('[data-kt-select-options]');
179
+ if (!optionsContainer)
180
+ return;
181
+ // Clear container if requested
182
+ if (clearContainer) {
183
+ optionsContainer.innerHTML = '';
184
+ }
185
+ // Use DocumentFragment for efficient DOM manipulation
186
+ var fragment = document.createDocumentFragment();
187
+ // Process options data
188
+ optionsData.forEach(function (optionData) {
189
+ var optionElement;
190
+ // Handle different input types
191
+ if (optionData instanceof HTMLOptionElement) {
192
+ // Skip empty placeholder options
193
+ if (optionData.value === '' && optionData.textContent.trim() === '') {
194
+ return;
195
+ }
196
+ optionElement = optionData;
197
+ }
198
+ else {
199
+ // Handle KTSelectOptionData objects - cast to ensure type safety
200
+ var dataItem = optionData;
201
+ optionElement = document.createElement('option');
202
+ optionElement.value = dataItem.id || '';
203
+ optionElement.textContent = dataItem.title || '';
204
+ if (dataItem.selected) {
205
+ optionElement.setAttribute('selected', 'selected');
206
+ }
207
+ if (dataItem.disabled) {
208
+ optionElement.setAttribute('disabled', 'disabled');
209
+ }
210
+ }
211
+ // Create KTSelectOption instance for proper rendering
212
+ var selectOption = new option_1.KTSelectOption(optionElement, _this._config);
213
+ var renderedOption = selectOption.render();
214
+ // Add to fragment for batch DOM operation
215
+ fragment.appendChild(renderedOption);
216
+ });
217
+ // Batch append all options at once
218
+ optionsContainer.appendChild(fragment);
219
+ // Update options NodeList
220
+ this._options = this._wrapperElement.querySelectorAll('[data-kt-select-option]');
221
+ if (this._config.debug) {
222
+ console.log("Rendered ".concat(optionsData.length, " options in dropdown"));
223
+ }
224
+ };
225
+ /**
226
+ * Update dropdown with new options from the original select element
227
+ */
228
+ KTSelect.prototype._updateDropdownWithNewOptions = function () {
229
+ // Get all options from the original select element
230
+ var options = Array.from(this._element.querySelectorAll('option'));
231
+ // Use unified renderer
232
+ this._renderOptionsInDropdown(options, true);
233
+ };
234
+ /**
235
+ * Complete the setup for remote data after HTML structure is created
236
+ */
237
+ KTSelect.prototype._completeRemoteSetup = function () {
238
+ // Initialize options
239
+ this._preSelectOptions(this._element);
240
+ // Apply disabled state if needed
241
+ this._applyInitialDisabledState();
242
+ // Initialize search if enabled
243
+ if (this._config.enableSearch) {
244
+ this._initializeSearchModule();
245
+ }
246
+ // Initialize combobox if enabled
247
+ if (this._config.combobox) {
248
+ this._comboboxModule = new combobox_1.KTSelectCombobox(this);
249
+ }
250
+ // Initialize tags if enabled
251
+ if (this._config.tags) {
252
+ this._tagsModule = new tags_1.KTSelectTags(this);
253
+ }
254
+ // Initialize focus manager after dropdown element is created
255
+ this._focusManager = new utils_1.FocusManager(this._dropdownContentElement, '[data-kt-select-option]', this._config);
256
+ // Initialize dropdown module after all elements are created
257
+ this._dropdownModule = new dropdown_1.KTSelectDropdown(this._wrapperElement, this._displayElement, this._dropdownContentElement, this._config, this);
258
+ // Update display and set ARIA attributes
259
+ this._updateDisplayAndAriaAttributes();
260
+ this.updateSelectedOptionDisplay();
261
+ this._setAriaAttributes();
262
+ // Update select all button state
263
+ this.updateSelectAllButtonState();
264
+ // Focus the first selected option or first option if nothing selected
265
+ this._focusSelectedOption();
266
+ // Attach event listeners after all modules are initialized
267
+ this._attachEventListeners();
268
+ this._observeNativeSelect();
269
+ };
157
270
  /**
158
271
  * Helper to show a dropdown message (error, loading, noResults)
159
272
  */
@@ -347,6 +460,10 @@ var KTSelect = /** @class */ (function (_super) {
347
460
  this._updateDisplayAndAriaAttributes();
348
461
  this.updateSelectedOptionDisplay();
349
462
  this._setAriaAttributes();
463
+ // Update select all button state
464
+ this.updateSelectAllButtonState();
465
+ // Focus the first selected option or first option if nothing selected
466
+ this._focusSelectedOption();
350
467
  // Attach event listeners after all modules are initialized
351
468
  this._attachEventListeners();
352
469
  this._observeNativeSelect();
@@ -366,13 +483,19 @@ var KTSelect = /** @class */ (function (_super) {
366
483
  // Move classes from original select to wrapper and display elements
367
484
  if (this._element.classList.length > 0) {
368
485
  var originalClasses = Array.from(this._element.classList);
369
- var displaySpecificClasses_1 = ['kt-select', 'kt-select-sm', 'kt-select-lg'];
486
+ var displaySpecificClasses_1 = [
487
+ 'kt-select',
488
+ 'kt-select-sm',
489
+ 'kt-select-lg',
490
+ ];
370
491
  var classesForWrapper = originalClasses.filter(function (className) { return !displaySpecificClasses_1.includes(className); });
371
492
  if (classesForWrapper.length > 0) {
372
493
  (_a = wrapperElement.classList).add.apply(_a, classesForWrapper);
373
494
  }
374
495
  // Move display-specific classes to display element
375
- var classesForDisplay = originalClasses.filter(function (className) { return displaySpecificClasses_1.includes(className); });
496
+ var classesForDisplay = originalClasses.filter(function (className) {
497
+ return displaySpecificClasses_1.includes(className);
498
+ });
376
499
  if (classesForDisplay.length > 0) {
377
500
  (_b = displayElement.classList).add.apply(_b, classesForDisplay);
378
501
  }
@@ -385,9 +508,14 @@ var KTSelect = /** @class */ (function (_super) {
385
508
  var searchElement = templates_1.defaultTemplates.search(this._config);
386
509
  dropdownElement.appendChild(searchElement);
387
510
  }
511
+ // Add select all button if needed
512
+ if (this._config.multiple && this._config.enableSelectAll) {
513
+ var selectAllElement = templates_1.defaultTemplates.selectAll(this._config);
514
+ dropdownElement.appendChild(selectAllElement);
515
+ }
388
516
  // Create options container using template
389
517
  var optionsContainer = templates_1.defaultTemplates.options(this._config);
390
- // Add each option directly to the container
518
+ // Add each option directly to the container (only if options exist)
391
519
  options.forEach(function (optionElement) {
392
520
  // Skip empty placeholder options (only if BOTH value AND text are empty)
393
521
  // This allows options with empty value but visible text to display in dropdown
@@ -414,13 +542,18 @@ var KTSelect = /** @class */ (function (_super) {
414
542
  */
415
543
  KTSelect.prototype._setupElementReferences = function () {
416
544
  this._wrapperElement = this._element.nextElementSibling;
545
+ // Safety check - ensure wrapper element exists
546
+ if (!this._wrapperElement) {
547
+ console.error('KTSelect: Wrapper element not found. HTML structure may not be created properly.');
548
+ return;
549
+ }
417
550
  // Get display element
418
551
  this._displayElement = this._wrapperElement.querySelector("[data-kt-select-display]");
419
552
  // Get dropdown content element - this is critical for dropdown functionality
420
553
  this._dropdownContentElement = this._wrapperElement.querySelector("[data-kt-select-dropdown]");
421
554
  if (!this._dropdownContentElement) {
422
- console.log(this._element);
423
- console.error('Dropdown content element not found', this._wrapperElement);
555
+ console.error('KTSelect: Dropdown content element not found', this._wrapperElement);
556
+ return;
424
557
  }
425
558
  // Get search input element - this is used for the search functionality
426
559
  this._searchInputElement = this._dropdownContentElement.querySelector("[data-kt-select-search]");
@@ -428,6 +561,9 @@ var KTSelect = /** @class */ (function (_super) {
428
561
  if (!this._searchInputElement) {
429
562
  this._searchInputElement = this._displayElement;
430
563
  }
564
+ this._selectAllButton = this._wrapperElement.querySelector('[data-kt-select-select-all]');
565
+ // Cache the options container for performance
566
+ this._optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
431
567
  this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
432
568
  };
433
569
  /**
@@ -438,6 +574,13 @@ var KTSelect = /** @class */ (function (_super) {
438
574
  document.addEventListener('click', this._handleDocumentClick.bind(this));
439
575
  // Dropdown option click events
440
576
  this._eventManager.addListener(this._dropdownContentElement, 'click', this._handleDropdownOptionClick.bind(this));
577
+ if (this._selectAllButton) {
578
+ this._selectAllButtonToggle =
579
+ this._selectAllButton.querySelector('button');
580
+ if (this._selectAllButtonToggle) {
581
+ this._eventManager.addListener(this._selectAllButtonToggle, 'click', this._handleSelectAllClick.bind(this));
582
+ }
583
+ }
441
584
  // Attach centralized keyboard handler to the wrapper element.
442
585
  // Events from focusable children like _displayElement or _searchInputElement (if present) will bubble up.
443
586
  if (this._wrapperElement) {
@@ -603,6 +746,8 @@ var KTSelect = /** @class */ (function (_super) {
603
746
  this._fireEvent('show');
604
747
  // Update ARIA states
605
748
  this._setAriaAttributes();
749
+ // Update select all button state
750
+ this.updateSelectAllButtonState();
606
751
  // Focus the first selected option or first option if nothing selected
607
752
  this._focusSelectedOption();
608
753
  };
@@ -830,6 +975,8 @@ var KTSelect = /** @class */ (function (_super) {
830
975
  this._state.setSelectedOptions([]);
831
976
  this.updateSelectedOptionDisplay();
832
977
  this._updateSelectedOptionClass();
978
+ // Update select all button state
979
+ this.updateSelectAllButtonState();
833
980
  // Dispatch change event
834
981
  this._dispatchEvent('change');
835
982
  this._fireEvent('change');
@@ -1094,6 +1241,7 @@ var KTSelect = /** @class */ (function (_super) {
1094
1241
  if (this._config.debug)
1095
1242
  console.log('Multiple select mode - keeping dropdown open for additional selections');
1096
1243
  // Don't close dropdown in multiple select mode to allow multiple selections
1244
+ this.updateSelectAllButtonState();
1097
1245
  }
1098
1246
  // Dispatch custom change event with additional data
1099
1247
  this._dispatchEvent('change', {
@@ -1173,6 +1321,8 @@ var KTSelect = /** @class */ (function (_super) {
1173
1321
  var query = event.target.value;
1174
1322
  // Check if the query is long enough
1175
1323
  if (query.length < (this._config.searchMinLength || 0)) {
1324
+ // Restore original options if query is too short
1325
+ this._restoreOriginalOptions();
1176
1326
  return;
1177
1327
  }
1178
1328
  // Debounce the search
@@ -1196,6 +1346,7 @@ var KTSelect = /** @class */ (function (_super) {
1196
1346
  if (_this._searchModule) {
1197
1347
  _this._searchModule.refreshAfterSearch();
1198
1348
  }
1349
+ _this.updateSelectAllButtonState();
1199
1350
  })
1200
1351
  .catch(function (error) {
1201
1352
  console.error('Error updating search results:', error);
@@ -1226,7 +1377,36 @@ var KTSelect = /** @class */ (function (_super) {
1226
1377
  * @param message Error message
1227
1378
  */
1228
1379
  KTSelect.prototype._renderSearchErrorState = function (message) {
1380
+ var _this = this;
1229
1381
  this._showDropdownMessage('error', message);
1382
+ // Restore original options after error with a delay
1383
+ setTimeout(function () {
1384
+ _this._restoreOriginalOptions();
1385
+ }, 2000);
1386
+ };
1387
+ /**
1388
+ * Restore original options when search is cleared
1389
+ */
1390
+ KTSelect.prototype._restoreOriginalOptions = function () {
1391
+ if (!this._dropdownContentElement || !this._originalOptionsHtml)
1392
+ return;
1393
+ // Use cached options container for better performance
1394
+ var optionsContainer = this._optionsContainer ||
1395
+ this._dropdownContentElement.querySelector('[data-kt-select-options]');
1396
+ if (!optionsContainer)
1397
+ return;
1398
+ // Restore original options
1399
+ optionsContainer.innerHTML = this._originalOptionsHtml;
1400
+ // Update options NodeList
1401
+ this._options = this._wrapperElement.querySelectorAll('[data-kt-select-option]');
1402
+ // Refresh search module
1403
+ if (this._searchModule) {
1404
+ this._searchModule.refreshAfterSearch();
1405
+ }
1406
+ this.updateSelectAllButtonState();
1407
+ if (this._config.debug) {
1408
+ console.log('Restored original options after search clear');
1409
+ }
1230
1410
  };
1231
1411
  /**
1232
1412
  * Update search results in the dropdown
@@ -1235,31 +1415,24 @@ var KTSelect = /** @class */ (function (_super) {
1235
1415
  KTSelect.prototype._updateSearchResults = function (items) {
1236
1416
  if (!this._dropdownContentElement)
1237
1417
  return;
1238
- var optionsContainer = this._dropdownContentElement.querySelector('[data-kt-select-options]');
1418
+ // Use cached options container for better performance
1419
+ var optionsContainer = this._optionsContainer ||
1420
+ this._dropdownContentElement.querySelector('[data-kt-select-options]');
1239
1421
  if (!optionsContainer)
1240
1422
  return;
1241
- // Clear current options
1242
- optionsContainer.innerHTML = '';
1423
+ // Handle empty results
1243
1424
  if (items.length === 0) {
1244
- // Show no results message using template for consistency and customization
1425
+ optionsContainer.innerHTML = '';
1245
1426
  var noResultsElement = templates_1.defaultTemplates.searchEmpty(this._config);
1246
1427
  optionsContainer.appendChild(noResultsElement);
1247
1428
  return;
1248
1429
  }
1249
- // Process each item individually to create options
1250
- items.forEach(function (item) {
1251
- // Create option for the original select
1252
- var selectOption = document.createElement('option');
1253
- selectOption.value = item.id;
1254
- // Add to dropdown container
1255
- optionsContainer.appendChild(selectOption);
1256
- });
1430
+ // Use unified renderer for search results
1431
+ this._renderOptionsInDropdown(items, true);
1257
1432
  // Add pagination "Load More" button if needed
1258
1433
  if (this._config.pagination && this._remoteModule.hasMorePages()) {
1259
1434
  this._addLoadMoreButton();
1260
1435
  }
1261
- // Update options NodeList
1262
- this._options = this._wrapperElement.querySelectorAll("[data-kt-select-option]");
1263
1436
  };
1264
1437
  /**
1265
1438
  * Check if dropdown is open
@@ -1271,10 +1444,12 @@ var KTSelect = /** @class */ (function (_super) {
1271
1444
  var _this = this;
1272
1445
  var selectedValues = this.getSelectedOptions();
1273
1446
  var displaySeparator = this._config.displaySeparator || ', ';
1274
- var texts = selectedValues.map(function (value) {
1447
+ var texts = selectedValues
1448
+ .map(function (value) {
1275
1449
  var option = Array.from(_this._options).find(function (opt) { return opt.getAttribute('data-value') === value; });
1276
1450
  return (option === null || option === void 0 ? void 0 : option.getAttribute('data-text')) || '';
1277
- }).filter(Boolean);
1451
+ })
1452
+ .filter(Boolean);
1278
1453
  return texts.join(displaySeparator);
1279
1454
  };
1280
1455
  /**
@@ -1282,7 +1457,9 @@ var KTSelect = /** @class */ (function (_super) {
1282
1457
  */
1283
1458
  KTSelect.prototype._isOptionDisabled = function (value) {
1284
1459
  var dropdownOption = Array.from(this._options).find(function (opt) { return opt.getAttribute('data-value') === value; });
1285
- var isDropdownDisabled = dropdownOption && (dropdownOption.classList.contains('disabled') || dropdownOption.getAttribute('aria-disabled') === 'true');
1460
+ var isDropdownDisabled = dropdownOption &&
1461
+ (dropdownOption.classList.contains('disabled') ||
1462
+ dropdownOption.getAttribute('aria-disabled') === 'true');
1286
1463
  var selectOption = Array.from(this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
1287
1464
  var isNativeDisabled = selectOption && selectOption.disabled;
1288
1465
  return Boolean(isDropdownDisabled || isNativeDisabled);
@@ -1304,9 +1481,14 @@ var KTSelect = /** @class */ (function (_super) {
1304
1481
  if (event.target === this._searchInputElement) {
1305
1482
  // Allow navigation keys like ArrowDown, ArrowUp, Escape, Enter (for search/selection) to be handled by the logic below.
1306
1483
  // For other keys (characters, space, backspace, delete), let the input field process them.
1307
- if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' &&
1308
- event.key !== 'Escape' && event.key !== 'Enter' && event.key !== 'Tab' &&
1309
- event.key !== 'Home' && event.key !== 'End') {
1484
+ if (event.key !== 'ArrowDown' &&
1485
+ event.key !== 'ArrowUp' &&
1486
+ event.key !== 'Escape' &&
1487
+ event.key !== 'Enter' &&
1488
+ event.key !== 'Tab' &&
1489
+ event.key !== 'Home' &&
1490
+ event.key !== 'End' &&
1491
+ event.key !== ' ') {
1310
1492
  // If it's a character key and we are NOT type-to-searching (because search has focus)
1311
1493
  // then let the input field handle it for its own value.
1312
1494
  // The search module's 'input' event will handle filtering based on the input's value.
@@ -1320,7 +1502,10 @@ var KTSelect = /** @class */ (function (_super) {
1320
1502
  if (event.altKey || event.ctrlKey || event.metaKey)
1321
1503
  return;
1322
1504
  // Type-to-search: only for single char keys, when search input does not have focus
1323
- if (event.key.length === 1 && !event.repeat && !event.key.match(/\s/) && document.activeElement !== this._searchInputElement) {
1505
+ if (event.key.length === 1 &&
1506
+ !event.repeat &&
1507
+ !event.key.match(/\s/) &&
1508
+ document.activeElement !== this._searchInputElement) {
1324
1509
  buffer.push(event.key);
1325
1510
  var str = buffer.getBuffer();
1326
1511
  if (isOpen) {
@@ -1369,7 +1554,9 @@ var KTSelect = /** @class */ (function (_super) {
1369
1554
  if (focusedOptionEl) {
1370
1555
  var val = focusedOptionEl.dataset.value;
1371
1556
  // If single select, and the item is already selected, just close.
1372
- if (val !== undefined && !this._config.multiple && this._state.isSelected(val)) {
1557
+ if (val !== undefined &&
1558
+ !this._config.multiple &&
1559
+ this._state.isSelected(val)) {
1373
1560
  if (this._config.debug)
1374
1561
  console.log('Enter on already selected item in single-select mode. Closing.');
1375
1562
  this.closeDropdown();
@@ -1410,7 +1597,8 @@ var KTSelect = /** @class */ (function (_super) {
1410
1597
  var _this = this;
1411
1598
  var optionsConfig = this._config.optionsConfig || {};
1412
1599
  var displaySeparator = this._config.displaySeparator || ', ';
1413
- var contentArray = Array.from(new Set(selectedValues.map(function (value) {
1600
+ var contentArray = Array.from(new Set(selectedValues
1601
+ .map(function (value) {
1414
1602
  var option = Array.from(_this._options).find(function (opt) { return opt.getAttribute('data-value') === value; });
1415
1603
  if (!option)
1416
1604
  return '';
@@ -1419,7 +1607,7 @@ var KTSelect = /** @class */ (function (_super) {
1419
1607
  // Replace all {{varname}} in option.innerHTML with values from _config
1420
1608
  Object.entries(optionsConfig[value] || {}).forEach(function (_a) {
1421
1609
  var key = _a[0], val = _a[1];
1422
- if (["string", "number", "boolean"].includes(typeof val)) {
1610
+ if (['string', 'number', 'boolean'].includes(typeof val)) {
1423
1611
  displayTemplate = displayTemplate.replace(new RegExp("{{".concat(key, "}}"), 'g'), String(val));
1424
1612
  }
1425
1613
  });
@@ -1428,7 +1616,8 @@ var KTSelect = /** @class */ (function (_super) {
1428
1616
  selectedTexts: _this.getSelectedOptionsText() || '',
1429
1617
  text: text,
1430
1618
  });
1431
- }).filter(Boolean)));
1619
+ })
1620
+ .filter(Boolean)));
1432
1621
  return contentArray.join(displaySeparator);
1433
1622
  };
1434
1623
  KTSelect.prototype.getDisplayElement = function () {
@@ -1447,7 +1636,8 @@ var KTSelect = /** @class */ (function (_super) {
1447
1636
  // Option(s) added or removed
1448
1637
  needsRebuild = true;
1449
1638
  }
1450
- else if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
1639
+ else if (mutation.type === 'attributes' &&
1640
+ mutation.target instanceof HTMLOptionElement) {
1451
1641
  if (mutation.attributeName === 'selected') {
1452
1642
  needsSelectionSync = true;
1453
1643
  }
@@ -1500,6 +1690,57 @@ var KTSelect = /** @class */ (function (_super) {
1500
1690
  this._state.setSelectedOptions(this._config.multiple ? selected : selected[0] || '');
1501
1691
  this.updateSelectedOptionDisplay();
1502
1692
  this._updateSelectedOptionClass();
1693
+ this.updateSelectAllButtonState();
1694
+ };
1695
+ KTSelect.prototype._handleSelectAllClick = function (event) {
1696
+ event.preventDefault();
1697
+ event.stopPropagation();
1698
+ var visibleOptions = this._focusManager
1699
+ .getVisibleOptions()
1700
+ .filter(function (opt) { return opt.getAttribute('aria-disabled') !== 'true'; });
1701
+ if (visibleOptions.length === 0)
1702
+ return;
1703
+ var visibleValues = visibleOptions.map(function (opt) { return opt.dataset.value; });
1704
+ var selectedValues = new Set(this.getSelectedOptions());
1705
+ var isAllSelected = visibleOptions.every(function (opt) {
1706
+ return selectedValues.has(opt.dataset.value);
1707
+ });
1708
+ if (isAllSelected) {
1709
+ // Deselect all visible
1710
+ visibleValues.forEach(function (value) { return selectedValues.delete(value); });
1711
+ }
1712
+ else {
1713
+ // Select all visible
1714
+ visibleValues.forEach(function (value) { return selectedValues.add(value); });
1715
+ }
1716
+ this._state.setSelectedOptions(Array.from(selectedValues));
1717
+ this.updateSelectedOptionDisplay();
1718
+ this._updateSelectedOptionClass();
1719
+ this.updateSelectAllButtonState();
1720
+ this._dispatchEvent('change');
1721
+ this._fireEvent('change');
1722
+ };
1723
+ KTSelect.prototype.updateSelectAllButtonState = function () {
1724
+ if (!this._config.multiple ||
1725
+ !this._config.enableSelectAll ||
1726
+ !this._selectAllButtonToggle) {
1727
+ return;
1728
+ }
1729
+ var visibleOptions = this._focusManager
1730
+ .getVisibleOptions()
1731
+ .filter(function (opt) { return opt.getAttribute('aria-disabled') !== 'true'; });
1732
+ if (visibleOptions.length === 0) {
1733
+ this._selectAllButton.style.display = 'none';
1734
+ return;
1735
+ }
1736
+ this._selectAllButton.style.display = '';
1737
+ var selectedValues = new Set(this.getSelectedOptions());
1738
+ var isAllSelected = visibleOptions.every(function (opt) {
1739
+ return selectedValues.has(opt.dataset.value);
1740
+ });
1741
+ this._selectAllButtonToggle.textContent = isAllSelected
1742
+ ? this._config.clearAllText
1743
+ : this._config.selectAllText;
1503
1744
  };
1504
1745
  /**
1505
1746
  * ========================================================================