@keenthemes/ktui 1.1.1 → 1.1.3

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 (81) hide show
  1. package/dist/ktui.js +674 -225
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +13 -1
  5. package/lib/cjs/components/component.js +22 -0
  6. package/lib/cjs/components/component.js.map +1 -1
  7. package/lib/cjs/components/datatable/datatable.js +7 -1
  8. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  9. package/lib/cjs/components/drawer/drawer.js +255 -9
  10. package/lib/cjs/components/drawer/drawer.js.map +1 -1
  11. package/lib/cjs/components/dropdown/dropdown.js +55 -8
  12. package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
  13. package/lib/cjs/components/select/combobox.js +0 -2
  14. package/lib/cjs/components/select/combobox.js.map +1 -1
  15. package/lib/cjs/components/select/config.js +4 -1
  16. package/lib/cjs/components/select/config.js.map +1 -1
  17. package/lib/cjs/components/select/dropdown.js +0 -16
  18. package/lib/cjs/components/select/dropdown.js.map +1 -1
  19. package/lib/cjs/components/select/remote.js +0 -40
  20. package/lib/cjs/components/select/remote.js.map +1 -1
  21. package/lib/cjs/components/select/search.js +93 -22
  22. package/lib/cjs/components/select/search.js.map +1 -1
  23. package/lib/cjs/components/select/select.js +180 -114
  24. package/lib/cjs/components/select/select.js.map +1 -1
  25. package/lib/cjs/components/select/tags.js +0 -2
  26. package/lib/cjs/components/select/tags.js.map +1 -1
  27. package/lib/cjs/components/sticky/sticky.js +44 -5
  28. package/lib/cjs/components/sticky/sticky.js.map +1 -1
  29. package/lib/cjs/helpers/data.js +8 -0
  30. package/lib/cjs/helpers/data.js.map +1 -1
  31. package/lib/cjs/helpers/event-handler.js +6 -5
  32. package/lib/cjs/helpers/event-handler.js.map +1 -1
  33. package/lib/cjs/index.js.map +1 -1
  34. package/lib/esm/components/component.js +22 -0
  35. package/lib/esm/components/component.js.map +1 -1
  36. package/lib/esm/components/datatable/datatable.js +7 -1
  37. package/lib/esm/components/datatable/datatable.js.map +1 -1
  38. package/lib/esm/components/drawer/drawer.js +255 -9
  39. package/lib/esm/components/drawer/drawer.js.map +1 -1
  40. package/lib/esm/components/dropdown/dropdown.js +55 -8
  41. package/lib/esm/components/dropdown/dropdown.js.map +1 -1
  42. package/lib/esm/components/select/combobox.js +0 -2
  43. package/lib/esm/components/select/combobox.js.map +1 -1
  44. package/lib/esm/components/select/config.js +4 -1
  45. package/lib/esm/components/select/config.js.map +1 -1
  46. package/lib/esm/components/select/dropdown.js +0 -16
  47. package/lib/esm/components/select/dropdown.js.map +1 -1
  48. package/lib/esm/components/select/remote.js +0 -40
  49. package/lib/esm/components/select/remote.js.map +1 -1
  50. package/lib/esm/components/select/search.js +93 -22
  51. package/lib/esm/components/select/search.js.map +1 -1
  52. package/lib/esm/components/select/select.js +180 -114
  53. package/lib/esm/components/select/select.js.map +1 -1
  54. package/lib/esm/components/select/tags.js +0 -2
  55. package/lib/esm/components/select/tags.js.map +1 -1
  56. package/lib/esm/components/sticky/sticky.js +44 -5
  57. package/lib/esm/components/sticky/sticky.js.map +1 -1
  58. package/lib/esm/helpers/data.js +8 -0
  59. package/lib/esm/helpers/data.js.map +1 -1
  60. package/lib/esm/helpers/event-handler.js +6 -5
  61. package/lib/esm/helpers/event-handler.js.map +1 -1
  62. package/lib/esm/index.js.map +1 -1
  63. package/package.json +6 -4
  64. package/src/components/component.ts +26 -0
  65. package/src/components/datatable/__tests__/race-conditions.test.ts +7 -7
  66. package/src/components/datatable/datatable.ts +8 -1
  67. package/src/components/drawer/drawer.ts +266 -10
  68. package/src/components/dropdown/dropdown.ts +63 -8
  69. package/src/components/select/__tests__/ux-behaviors.test.ts +997 -0
  70. package/src/components/select/combobox.ts +0 -1
  71. package/src/components/select/config.ts +7 -1
  72. package/src/components/select/dropdown.ts +0 -24
  73. package/src/components/select/remote.ts +0 -49
  74. package/src/components/select/search.ts +97 -24
  75. package/src/components/select/select.css +5 -1
  76. package/src/components/select/select.ts +211 -153
  77. package/src/components/select/tags.ts +0 -1
  78. package/src/components/sticky/sticky.ts +55 -5
  79. package/src/helpers/data.ts +10 -0
  80. package/src/helpers/event-handler.ts +7 -6
  81. package/src/index.ts +2 -0
@@ -35,6 +35,9 @@ export class KTSelect extends KTComponent {
35
35
  // Static global configuration
36
36
  private static globalConfig: Partial<KTSelectConfigInterface> = {};
37
37
 
38
+ // Static registry for tracking open dropdowns (global dropdown management)
39
+ private static openDropdowns: Set<KTSelect> = new Set();
40
+
38
41
  // DOM elements
39
42
  private _wrapperElement: HTMLElement;
40
43
  private _displayElement: HTMLElement;
@@ -91,8 +94,6 @@ export class KTSelect extends KTComponent {
91
94
  this._state
92
95
  .setItems()
93
96
  .then(() => {
94
- if (this._config.debug)
95
- console.log('Setting up component after remote data is loaded');
96
97
  this._setupComponent();
97
98
  })
98
99
  .catch((error) => {
@@ -137,14 +138,51 @@ export class KTSelect extends KTComponent {
137
138
  } as KTSelectConfigInterface;
138
139
  }
139
140
 
141
+ /**
142
+ * Override _dispatchEvent to also dispatch on document for global listeners (jQuery compatibility)
143
+ */
144
+ protected override _dispatchEvent(eventType: string, payload: object = null): void {
145
+ // Call parent method to dispatch on element (existing behavior)
146
+ super._dispatchEvent(eventType, payload);
147
+
148
+ // Also dispatch on document if configured
149
+ const dispatchGlobalEvents =
150
+ this._config.dispatchGlobalEvents !== false; // Default to true
151
+ if (dispatchGlobalEvents) {
152
+ // Create event detail structure
153
+ const eventDetail = {
154
+ payload,
155
+ instance: this, // Include component instance reference
156
+ element: this._element, // Include element reference
157
+ };
158
+
159
+ // Dispatch non-namespaced event on document (for jQuery compatibility: $(document).on('show', ...))
160
+ const nonNamespacedEvent = new CustomEvent(eventType, {
161
+ detail: eventDetail,
162
+ bubbles: true,
163
+ cancelable: true,
164
+ composed: true, // Allow event to cross shadow DOM boundaries
165
+ });
166
+ document.dispatchEvent(nonNamespacedEvent);
167
+
168
+ // Also dispatch namespaced event on document (for namespaced listeners: $(document).on('kt-select:show', ...))
169
+ const namespacedEventType = `kt-select:${eventType}`;
170
+ const namespacedEvent = new CustomEvent(namespacedEventType, {
171
+ detail: eventDetail,
172
+ bubbles: true,
173
+ cancelable: true,
174
+ composed: true, // Allow event to cross shadow DOM boundaries
175
+ });
176
+ document.dispatchEvent(namespacedEvent);
177
+ }
178
+ }
179
+
140
180
  /**
141
181
  * Initialize remote data fetching
142
182
  */
143
183
  private _initializeRemoteData() {
144
184
  if (!this._remoteModule || !this._config.remote) return;
145
185
 
146
- if (this._config.debug)
147
- console.log('Initializing remote data with URL:', this._config.dataUrl);
148
186
 
149
187
  // For remote data, we need to create the HTML structure first
150
188
  // so that the component can be properly initialized
@@ -158,7 +196,6 @@ export class KTSelect extends KTComponent {
158
196
  this._remoteModule
159
197
  .fetchData()
160
198
  .then((items) => {
161
- if (this._config.debug) console.log('Remote data fetched:', items);
162
199
 
163
200
  // Remove placeholder/loading options before setting new items
164
201
  this._clearExistingOptions();
@@ -170,8 +207,6 @@ export class KTSelect extends KTComponent {
170
207
  // Generate options from the fetched data
171
208
  this._generateOptionsHtml(this._element);
172
209
 
173
- if (this._config.debug)
174
- console.log('Generating options HTML from remote data');
175
210
 
176
211
  // Update the dropdown to show the new options
177
212
  this._updateDropdownWithNewOptions();
@@ -208,12 +243,6 @@ export class KTSelect extends KTComponent {
208
243
 
209
244
  if (selectedOptions.length > 0) {
210
245
  this._preSelectedValues = selectedOptions.map((opt) => opt.value);
211
- if (this._config.debug) {
212
- console.log(
213
- 'Captured pre-selected values before clearing:',
214
- this._preSelectedValues,
215
- );
216
- }
217
246
  }
218
247
 
219
248
  // Keep only the empty/placeholder option and remove the rest
@@ -288,9 +317,6 @@ export class KTSelect extends KTComponent {
288
317
  '[data-kt-select-option]',
289
318
  ) as NodeListOf<HTMLElement>;
290
319
 
291
- if (this._config.debug) {
292
- console.log(`Rendered ${optionsData.length} options in dropdown`);
293
- }
294
320
  }
295
321
 
296
322
  /**
@@ -322,12 +348,6 @@ export class KTSelect extends KTComponent {
322
348
 
323
349
  // Apply pre-selected values captured before remote data was loaded
324
350
  if (this._preSelectedValues.length > 0) {
325
- if (this._config.debug) {
326
- console.log(
327
- 'Applying pre-selected values after remote data loaded:',
328
- this._preSelectedValues,
329
- );
330
- }
331
351
 
332
352
  // Get all available option values from the loaded remote data
333
353
  const availableValues = Array.from(
@@ -345,9 +365,6 @@ export class KTSelect extends KTComponent {
345
365
  ? validPreSelectedValues
346
366
  : [validPreSelectedValues[0]];
347
367
 
348
- if (this._config.debug) {
349
- console.log('Selecting matched values:', valuesToSelect);
350
- }
351
368
 
352
369
  // Get any existing selections from _preSelectOptions (e.g., data-kt-select-pre-selected)
353
370
  const existingSelections = this._state.getSelectedOptions();
@@ -370,11 +387,6 @@ export class KTSelect extends KTComponent {
370
387
  // Update the visual display
371
388
  this.updateSelectedOptionDisplay();
372
389
  this._updateSelectedOptionClass();
373
- } else if (this._config.debug) {
374
- console.log(
375
- 'None of the pre-selected values matched remote data:',
376
- this._preSelectedValues,
377
- );
378
390
  }
379
391
 
380
392
  // Clear the pre-selected values array after processing
@@ -492,7 +504,6 @@ export class KTSelect extends KTComponent {
492
504
  this._showDropdownMessage('error', message);
493
505
 
494
506
  if (!this._wrapperElement) {
495
- if (this._config.debug) console.log('Setting up component after error');
496
507
  this._setupComponent();
497
508
  }
498
509
  }
@@ -626,8 +637,6 @@ export class KTSelect extends KTComponent {
626
637
  `[data-kt-select-option]`,
627
638
  ) as NodeListOf<HTMLElement>;
628
639
 
629
- if (this._config.debug)
630
- console.log(`Added ${newItems.length} more options to dropdown`);
631
640
  }
632
641
 
633
642
  /**
@@ -831,13 +840,28 @@ export class KTSelect extends KTComponent {
831
840
  }
832
841
 
833
842
  // Get search input element - this is used for the search functionality
843
+ // First try to find the actual input element (not the wrapper div)
834
844
  this._searchInputElement = this._dropdownContentElement.querySelector(
835
- `[data-kt-select-search]`,
845
+ `input[data-kt-select-search]`,
836
846
  ) as HTMLInputElement;
837
847
 
838
- // If not found in dropdown, check if it's the display element itself
848
+ // If not found, try the wrapper selector (for backward compatibility)
849
+ if (!this._searchInputElement) {
850
+ const searchWrapper = this._dropdownContentElement.querySelector(
851
+ `[data-kt-select-search]`,
852
+ ) as HTMLElement;
853
+ if (searchWrapper) {
854
+ this._searchInputElement = searchWrapper.querySelector(
855
+ 'input',
856
+ ) as HTMLInputElement;
857
+ }
858
+ }
859
+
860
+ // If still not found in dropdown, check if it's the display element itself (combobox mode)
839
861
  if (!this._searchInputElement) {
840
- this._searchInputElement = this._displayElement as HTMLInputElement;
862
+ this._searchInputElement = this._displayElement.querySelector(
863
+ 'input[data-kt-select-search]',
864
+ ) as HTMLInputElement;
841
865
  }
842
866
 
843
867
  this._selectAllButton = this._wrapperElement.querySelector(
@@ -937,8 +961,6 @@ export class KTSelect extends KTComponent {
937
961
  private _generateOptionsHtml(element: HTMLElement) {
938
962
  const items = this._state.getItems() || [];
939
963
 
940
- if (this._config.debug)
941
- console.log(`Generating options HTML from ${items.length} items`);
942
964
 
943
965
  // Only modify options if we have items to replace them with
944
966
  if (items && items.length > 0) {
@@ -975,9 +997,6 @@ export class KTSelect extends KTComponent {
975
997
  extractedLabel !== null ? String(extractedLabel) : 'Unnamed option';
976
998
  }
977
999
 
978
- // Log the extracted values for debugging
979
- if (this._config.debug)
980
- console.log(`Option: value=${value}, label=${label}`);
981
1000
 
982
1001
  // Set option attributes
983
1002
  optionElement.value = value;
@@ -990,10 +1009,6 @@ export class KTSelect extends KTComponent {
990
1009
  element.appendChild(optionElement);
991
1010
  });
992
1011
 
993
- if (this._config.debug)
994
- console.log(`Added ${items.length} options to select element`);
995
- } else {
996
- if (this._config.debug) console.log('No items to generate options from');
997
1012
  }
998
1013
  }
999
1014
 
@@ -1008,10 +1023,6 @@ export class KTSelect extends KTComponent {
1008
1023
  .split('.')
1009
1024
  .reduce((o, k) => (o && o[k] !== undefined ? o[k] : null), obj);
1010
1025
 
1011
- if (this._config.debug)
1012
- console.log(
1013
- `Extracting [${key}] from object => ${result !== null ? JSON.stringify(result) : 'null'}`,
1014
- );
1015
1026
  return result;
1016
1027
  }
1017
1028
 
@@ -1052,42 +1063,57 @@ export class KTSelect extends KTComponent {
1052
1063
  */
1053
1064
  public openDropdown() {
1054
1065
  if (this._config.disabled) {
1055
- if (this._config.debug)
1056
- console.log('openDropdown: select is disabled, not opening');
1057
1066
  return;
1058
1067
  }
1059
- if (this._config.debug)
1060
- console.log(
1061
- 'openDropdown called, dropdownModule exists:',
1062
- !!this._dropdownModule,
1063
- );
1064
1068
 
1065
1069
  if (!this._dropdownModule) {
1066
- if (this._config.debug)
1067
- console.log('Early return from openDropdown - module missing');
1068
1070
  return;
1069
1071
  }
1070
1072
 
1071
1073
  // Don't open dropdown if the select is disabled
1072
1074
  if (this._config.disabled) {
1073
- if (this._config.debug)
1074
- console.log('Early return from openDropdown - select is disabled');
1075
1075
  return;
1076
1076
  }
1077
1077
 
1078
- if (this._config.debug)
1079
- console.log('Opening dropdown via dropdownModule...');
1078
+ // Global dropdown management: close other open dropdowns if configured
1079
+ const closeOnOtherOpen =
1080
+ this._config.closeOnOtherOpen !== false; // Default to true
1081
+ if (closeOnOtherOpen) {
1082
+ // Close all other open dropdowns
1083
+ const otherSelectsToClose: KTSelect[] = [];
1084
+ KTSelect.openDropdowns.forEach((otherSelect) => {
1085
+ const isOther = otherSelect !== this;
1086
+ const isOpen = otherSelect._dropdownIsOpen;
1087
+ if (isOther && isOpen) {
1088
+ otherSelectsToClose.push(otherSelect);
1089
+ }
1090
+ });
1091
+ otherSelectsToClose.forEach((otherSelect) => {
1092
+ otherSelect.closeDropdown();
1093
+ });
1094
+ }
1095
+
1080
1096
 
1081
1097
  // Set our internal flag to match what we're doing
1082
1098
  this._dropdownIsOpen = true;
1083
1099
 
1100
+ // Add to registry
1101
+ KTSelect.openDropdowns.add(this);
1102
+
1084
1103
  // Open the dropdown via the module
1085
1104
  this._dropdownModule.open();
1086
1105
 
1087
- // Dispatch custom event
1106
+ // Dispatch custom events
1088
1107
  this._dispatchEvent('show');
1089
1108
  this._fireEvent('show');
1090
1109
 
1110
+ // Dispatch dropdown.show event on wrapper for search module
1111
+ const dropdownShowEvent = new CustomEvent('dropdown.show', {
1112
+ bubbles: true,
1113
+ cancelable: true,
1114
+ });
1115
+ this._wrapperElement.dispatchEvent(dropdownShowEvent);
1116
+
1091
1117
  // Update ARIA states
1092
1118
  this._setAriaAttributes();
1093
1119
 
@@ -1095,30 +1121,35 @@ export class KTSelect extends KTComponent {
1095
1121
  this.updateSelectAllButtonState();
1096
1122
 
1097
1123
  // Focus the first selected option or first option if nothing selected
1098
- this._focusSelectedOption();
1124
+ // BUT: Skip this if search autofocus is enabled, as we want search input to get focus
1125
+ if (!(this._config.enableSearch && this._config.searchAutofocus)) {
1126
+ this._focusSelectedOption();
1127
+ }
1128
+
1129
+ // Dispatch dropdown.show event on the wrapper element for search module
1130
+ // Use requestAnimationFrame to ensure dropdown is visible and transition has started
1131
+ requestAnimationFrame(() => {
1132
+ requestAnimationFrame(() => {
1133
+ if (this._wrapperElement) {
1134
+ const dropdownShowEvent = new CustomEvent('dropdown.show', {
1135
+ bubbles: true,
1136
+ cancelable: true,
1137
+ });
1138
+ this._wrapperElement.dispatchEvent(dropdownShowEvent);
1139
+ }
1140
+ });
1141
+ });
1099
1142
  }
1100
1143
 
1101
1144
  /**
1102
1145
  * Close the dropdown
1103
1146
  */
1104
1147
  public closeDropdown() {
1105
- if (this._config.debug)
1106
- console.log(
1107
- 'closeDropdown called, dropdownModule exists:',
1108
- !!this._dropdownModule,
1109
- );
1110
-
1111
1148
  // Only check if dropdown module exists, not dropdownIsOpen flag
1112
1149
  if (!this._dropdownModule) {
1113
- if (this._config.debug)
1114
- console.log('Early return from closeDropdown - module missing');
1115
1150
  return;
1116
1151
  }
1117
1152
 
1118
- // Always close by delegating to the dropdown module, which is the source of truth
1119
- if (this._config.debug)
1120
- console.log('Closing dropdown via dropdownModule...');
1121
-
1122
1153
  // Clear search input if the dropdown is closing
1123
1154
  if (this._searchModule && this._searchInputElement) {
1124
1155
  // Clear search input if configured to do so
@@ -1133,6 +1164,9 @@ export class KTSelect extends KTComponent {
1133
1164
  // Set our internal flag to match what we're doing
1134
1165
  this._dropdownIsOpen = false;
1135
1166
 
1167
+ // Remove from registry
1168
+ KTSelect.openDropdowns.delete(this);
1169
+
1136
1170
  // Call the dropdown module's close method
1137
1171
  this._dropdownModule.close();
1138
1172
 
@@ -1141,13 +1175,19 @@ export class KTSelect extends KTComponent {
1141
1175
  this._focusManager.resetFocus();
1142
1176
  }
1143
1177
 
1144
- // Dispatch custom events
1178
+ // Dispatch custom events on the select element
1145
1179
  this._dispatchEvent('close');
1146
1180
  this._fireEvent('close');
1147
1181
 
1182
+ // Dispatch dropdown.close event on wrapper for search module
1183
+ const dropdownCloseEvent = new CustomEvent('dropdown.close', {
1184
+ bubbles: true,
1185
+ cancelable: true,
1186
+ });
1187
+ this._wrapperElement.dispatchEvent(dropdownCloseEvent);
1188
+
1148
1189
  // Update ARIA states
1149
1190
  this._setAriaAttributes();
1150
- if (this._config.debug) console.log('closeDropdown complete');
1151
1191
  }
1152
1192
 
1153
1193
  /**
@@ -1187,8 +1227,6 @@ export class KTSelect extends KTComponent {
1187
1227
  private _selectOption(value: string) {
1188
1228
  // Prevent selection if the option is disabled (in dropdown or original select)
1189
1229
  if (this._isOptionDisabled(value)) {
1190
- if (this._config.debug)
1191
- console.log('_selectOption: Option is disabled, ignoring selection');
1192
1230
  return;
1193
1231
  }
1194
1232
 
@@ -1338,11 +1376,6 @@ export class KTSelect extends KTComponent {
1338
1376
  typeof this._config.maxSelections === 'number' &&
1339
1377
  selectedValues.length >= this._config.maxSelections;
1340
1378
 
1341
- if (this._config.debug)
1342
- console.log(
1343
- 'Updating selected classes for options, selected values:',
1344
- selectedValues,
1345
- );
1346
1379
 
1347
1380
  allOptions.forEach((option) => {
1348
1381
  const optionValue = option.getAttribute('data-value');
@@ -1401,6 +1434,65 @@ export class KTSelect extends KTComponent {
1401
1434
  this._fireEvent('change');
1402
1435
  }
1403
1436
 
1437
+ /**
1438
+ * Deselect a specific option by value
1439
+ * @param value The value of the option to deselect
1440
+ * @public
1441
+ */
1442
+ public deselectOption(value: string): void {
1443
+ // Check if the option is currently selected
1444
+ if (!this._state.isSelected(value)) {
1445
+ return; // Already deselected
1446
+ }
1447
+
1448
+ // For single-select mode, check if clearing is allowed
1449
+ if (!this._config.multiple && !this._config.allowClear) {
1450
+ return; // Cannot deselect in single-select mode unless allowClear is true
1451
+ }
1452
+
1453
+ // Remove from selected options
1454
+ if (this._config.multiple) {
1455
+ // For multiple select, just toggle it off
1456
+ this._state.toggleSelectedOptions(value);
1457
+ } else {
1458
+ // For single select, clear all selections
1459
+ this._state.setSelectedOptions([]);
1460
+ }
1461
+
1462
+ // Update the native select element
1463
+ const optionEl = Array.from(this._element.querySelectorAll('option')).find(
1464
+ (opt) => opt.value === value,
1465
+ ) as HTMLOptionElement;
1466
+
1467
+ if (optionEl) {
1468
+ optionEl.selected = false;
1469
+ }
1470
+
1471
+ // For single select, clear the native select value
1472
+ if (!this._config.multiple) {
1473
+ (this._element as HTMLSelectElement).value = '';
1474
+ }
1475
+
1476
+ // Update the display
1477
+ this.updateSelectedOptionDisplay();
1478
+ this._updateSelectedOptionClass();
1479
+
1480
+ // Update select all button state
1481
+ this.updateSelectAllButtonState();
1482
+
1483
+ // Dispatch change event
1484
+ this._dispatchEvent('change', {
1485
+ value: value,
1486
+ selected: false,
1487
+ selectedOptions: this.getSelectedOptions(),
1488
+ });
1489
+ this._fireEvent('change', {
1490
+ value: value,
1491
+ selected: false,
1492
+ selectedOptions: this.getSelectedOptions(),
1493
+ });
1494
+ }
1495
+
1404
1496
  /**
1405
1497
  * Set selected options programmatically
1406
1498
  */
@@ -1462,8 +1554,6 @@ export class KTSelect extends KTComponent {
1462
1554
  * Handle clicking on an option in the dropdown
1463
1555
  */
1464
1556
  private _handleOptionClick(event: Event) {
1465
- if (this._config.debug)
1466
- console.log('_handleOptionClick called', event.target);
1467
1557
  event.preventDefault();
1468
1558
  event.stopPropagation();
1469
1559
 
@@ -1473,31 +1563,22 @@ export class KTSelect extends KTComponent {
1473
1563
  ) as HTMLElement;
1474
1564
 
1475
1565
  if (!clickedOption) {
1476
- if (this._config.debug) console.log('No clicked option found');
1477
1566
  return;
1478
1567
  }
1479
1568
 
1480
1569
  // Check if the option is disabled
1481
1570
  if (clickedOption.getAttribute('aria-disabled') === 'true') {
1482
- if (this._config.debug) console.log('Option is disabled, ignoring click');
1483
1571
  return;
1484
1572
  }
1485
1573
 
1486
1574
  // Use dataset.value to get the option value
1487
1575
  const optionValue = clickedOption.dataset.value;
1488
1576
  if (optionValue === undefined) {
1489
- if (this._config.debug) console.log('Option value is undefined');
1490
1577
  return;
1491
1578
  }
1492
1579
 
1493
- if (this._config.debug) console.log('Option clicked:', optionValue);
1494
-
1495
1580
  // If in single-select mode and the clicked option is already selected, just close the dropdown.
1496
1581
  if (!this._config.multiple && this._state.isSelected(optionValue)) {
1497
- if (this._config.debug)
1498
- console.log(
1499
- 'Single select mode: clicked already selected option. Closing dropdown.',
1500
- );
1501
1582
  this.closeDropdown();
1502
1583
  return;
1503
1584
  }
@@ -1654,32 +1735,22 @@ export class KTSelect extends KTComponent {
1654
1735
  public toggleSelection(value: string): void {
1655
1736
  // Prevent selection if the option is disabled (in dropdown or original select)
1656
1737
  if (this._isOptionDisabled(value)) {
1657
- if (this._config.debug)
1658
- console.log('toggleSelection: Option is disabled, ignoring selection');
1659
1738
  return;
1660
1739
  }
1661
1740
 
1662
1741
  // Get current selection state
1663
1742
  const isSelected = this._state.isSelected(value);
1664
- if (this._config.debug)
1665
- console.log(
1666
- `toggleSelection called for value: ${value}, isSelected: ${isSelected}, multiple: ${this._config.multiple}`,
1667
- );
1668
1743
 
1669
- // If already selected in single select mode, do nothing (can't deselect in single select)
1744
+ // If already selected in single select mode, allow deselecting only if allowClear is true
1670
1745
  if (isSelected && !this._config.multiple) {
1671
- if (this._config.debug)
1672
- console.log(
1673
- 'Early return from toggleSelection - already selected in single select mode',
1674
- );
1675
- return;
1746
+ if (this._config.allowClear) {
1747
+ // Use the deselectOption method to handle clearing
1748
+ this.deselectOption(value);
1749
+ return;
1750
+ }
1751
+ return; // Can't deselect in single select mode when allowClear is false
1676
1752
  }
1677
1753
 
1678
- if (this._config.debug)
1679
- console.log(
1680
- `Toggling selection for option: ${value}, currently selected: ${isSelected}`,
1681
- );
1682
-
1683
1754
  // Ensure any search input is cleared when selection changes
1684
1755
  if (this._searchModule) {
1685
1756
  this._searchModule.clearSearch();
@@ -1711,19 +1782,20 @@ export class KTSelect extends KTComponent {
1711
1782
  // Update option classes without re-rendering the dropdown content
1712
1783
  this._updateSelectedOptionClass();
1713
1784
 
1714
- // For single select mode, always close the dropdown after selection
1785
+ // For single select mode, close the dropdown after selection unless closeOnEnter is false
1715
1786
  // For multiple select mode, keep the dropdown open to allow multiple selections
1716
1787
  if (!this._config.multiple) {
1717
- if (this._config.debug)
1718
- console.log(
1719
- 'About to call closeDropdown() for single select mode - always close after selection',
1720
- );
1721
- this.closeDropdown();
1788
+ // Check if we should close based on closeOnEnter config
1789
+ // closeOnEnter only applies to Enter key selections, but for backward compatibility,
1790
+ // we'll respect it for all selections when explicitly set to false
1791
+ const shouldClose =
1792
+ this._config.closeOnEnter !== false; // Default to true
1793
+ if (shouldClose) {
1794
+ this.closeDropdown();
1795
+ } else {
1796
+ this.updateSelectAllButtonState();
1797
+ }
1722
1798
  } else {
1723
- if (this._config.debug)
1724
- console.log(
1725
- 'Multiple select mode - keeping dropdown open for additional selections',
1726
- );
1727
1799
  // Don't close dropdown in multiple select mode to allow multiple selections
1728
1800
  this.updateSelectAllButtonState();
1729
1801
  }
@@ -1879,12 +1951,6 @@ export class KTSelect extends KTComponent {
1879
1951
  availableValues.includes(value),
1880
1952
  );
1881
1953
 
1882
- if (this._config.debug && currentlySelected.length > 0) {
1883
- console.log(
1884
- 'update(): Preserving selections that exist in new data:',
1885
- validSelections,
1886
- );
1887
- }
1888
1954
 
1889
1955
  // Add new options from remote data and restore selection state
1890
1956
  items.forEach((item) => {
@@ -1980,12 +2046,6 @@ export class KTSelect extends KTComponent {
1980
2046
  availableValues.includes(value),
1981
2047
  );
1982
2048
 
1983
- if (this._config.debug && currentlySelected.length > 0) {
1984
- console.log(
1985
- 'reload(): Preserving selections that exist in new data:',
1986
- validSelections,
1987
- );
1988
- }
1989
2049
 
1990
2050
  // Mark preserved selections on new options
1991
2051
  validSelections.forEach((value) => {
@@ -2063,12 +2123,6 @@ export class KTSelect extends KTComponent {
2063
2123
  availableValues.includes(value),
2064
2124
  );
2065
2125
 
2066
- if (this._config.debug && currentlySelected.length > 0) {
2067
- console.log(
2068
- 'refresh(): Preserving selections that exist in new data:',
2069
- validSelections,
2070
- );
2071
- }
2072
2126
 
2073
2127
  // Add new options and restore selection state
2074
2128
  items.forEach((item) => {
@@ -2296,9 +2350,6 @@ export class KTSelect extends KTComponent {
2296
2350
  }
2297
2351
  this.updateSelectAllButtonState();
2298
2352
 
2299
- if (this._config.debug) {
2300
- console.log('Restored original options after search clear');
2301
- }
2302
2353
  }
2303
2354
 
2304
2355
  /**
@@ -2358,11 +2409,6 @@ export class KTSelect extends KTComponent {
2358
2409
  this._element.appendChild(optionElement);
2359
2410
  });
2360
2411
 
2361
- if (this._config.debug) {
2362
- console.log(
2363
- `Updated original select with ${items.length} search results`,
2364
- );
2365
- }
2366
2412
  }
2367
2413
 
2368
2414
  /**
@@ -2502,10 +2548,6 @@ export class KTSelect extends KTComponent {
2502
2548
  !this._config.multiple &&
2503
2549
  this._state.isSelected(val)
2504
2550
  ) {
2505
- if (this._config.debug)
2506
- console.log(
2507
- 'Enter on already selected item in single-select mode. Closing.',
2508
- );
2509
2551
  this.closeDropdown();
2510
2552
  event.preventDefault();
2511
2553
  break;
@@ -2728,4 +2770,20 @@ export class KTSelect extends KTComponent {
2728
2770
  ? this._config.clearAllText
2729
2771
  : this._config.selectAllText;
2730
2772
  }
2773
+
2774
+ /**
2775
+ * Destroy the component and clean up resources
2776
+ */
2777
+ public destroy(): void {
2778
+ // Remove from global dropdown registry
2779
+ KTSelect.openDropdowns.delete(this);
2780
+
2781
+ // Close dropdown if open
2782
+ if (this._dropdownIsOpen) {
2783
+ this.closeDropdown();
2784
+ }
2785
+
2786
+ // Call parent dispose method
2787
+ super.dispose();
2788
+ }
2731
2789
  }
@@ -26,7 +26,6 @@ export class KTSelectTags {
26
26
  this._valueDisplayElement = select.getValueDisplayElement();
27
27
  this._eventManager = new EventManager();
28
28
 
29
- if (this._config.debug) console.log('KTSelectTags initialized');
30
29
  }
31
30
 
32
31
  /**