@keenthemes/ktui 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/ktui.js +668 -685
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +5822 -0
  5. package/examples/select/avatar.html +47 -0
  6. package/examples/select/basic-usage.html +10 -14
  7. package/examples/select/{test.html → combobox-icons_.html} +13 -48
  8. package/examples/select/country.html +43 -0
  9. package/examples/select/description.html +25 -41
  10. package/examples/select/disable-option.html +10 -16
  11. package/examples/select/disable-select.html +7 -6
  12. package/examples/select/icon-multiple.html +23 -31
  13. package/examples/select/icon.html +20 -30
  14. package/examples/select/max-selection.html +8 -9
  15. package/examples/select/modal.html +19 -17
  16. package/examples/select/multiple.html +11 -13
  17. package/examples/select/placeholder.html +9 -12
  18. package/examples/select/search.html +30 -22
  19. package/examples/select/sizes.html +94 -0
  20. package/examples/select/template-customization.html +0 -3
  21. package/lib/cjs/components/component.js +1 -1
  22. package/lib/cjs/components/component.js.map +1 -1
  23. package/lib/cjs/components/datatable/datatable.js +1 -1
  24. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  25. package/lib/cjs/components/select/combobox.js +96 -61
  26. package/lib/cjs/components/select/combobox.js.map +1 -1
  27. package/lib/cjs/components/select/config.js +1 -3
  28. package/lib/cjs/components/select/config.js.map +1 -1
  29. package/lib/cjs/components/select/dropdown.js +32 -96
  30. package/lib/cjs/components/select/dropdown.js.map +1 -1
  31. package/lib/cjs/components/select/option.js +53 -20
  32. package/lib/cjs/components/select/option.js.map +1 -1
  33. package/lib/cjs/components/select/search.js +146 -97
  34. package/lib/cjs/components/select/search.js.map +1 -1
  35. package/lib/cjs/components/select/select.js +174 -120
  36. package/lib/cjs/components/select/select.js.map +1 -1
  37. package/lib/cjs/components/select/tags.js +0 -26
  38. package/lib/cjs/components/select/tags.js.map +1 -1
  39. package/lib/cjs/components/select/templates.js +130 -103
  40. package/lib/cjs/components/select/templates.js.map +1 -1
  41. package/lib/cjs/components/select/utils.js +33 -132
  42. package/lib/cjs/components/select/utils.js.map +1 -1
  43. package/lib/cjs/helpers/dom.js +0 -24
  44. package/lib/cjs/helpers/dom.js.map +1 -1
  45. package/lib/esm/components/component.js +1 -1
  46. package/lib/esm/components/component.js.map +1 -1
  47. package/lib/esm/components/datatable/datatable.js +1 -1
  48. package/lib/esm/components/datatable/datatable.js.map +1 -1
  49. package/lib/esm/components/select/combobox.js +96 -61
  50. package/lib/esm/components/select/combobox.js.map +1 -1
  51. package/lib/esm/components/select/config.js +1 -3
  52. package/lib/esm/components/select/config.js.map +1 -1
  53. package/lib/esm/components/select/dropdown.js +32 -96
  54. package/lib/esm/components/select/dropdown.js.map +1 -1
  55. package/lib/esm/components/select/option.js +53 -20
  56. package/lib/esm/components/select/option.js.map +1 -1
  57. package/lib/esm/components/select/search.js +146 -97
  58. package/lib/esm/components/select/search.js.map +1 -1
  59. package/lib/esm/components/select/select.js +174 -120
  60. package/lib/esm/components/select/select.js.map +1 -1
  61. package/lib/esm/components/select/tags.js +0 -26
  62. package/lib/esm/components/select/tags.js.map +1 -1
  63. package/lib/esm/components/select/templates.js +130 -103
  64. package/lib/esm/components/select/templates.js.map +1 -1
  65. package/lib/esm/components/select/utils.js +32 -130
  66. package/lib/esm/components/select/utils.js.map +1 -1
  67. package/lib/esm/helpers/dom.js +0 -24
  68. package/lib/esm/helpers/dom.js.map +1 -1
  69. package/package.json +9 -6
  70. package/src/components/component.ts +0 -4
  71. package/src/components/datatable/datatable.ts +1 -1
  72. package/src/components/input/input.css +1 -1
  73. package/src/components/scrollable/scrollable.css +9 -5
  74. package/src/components/select/combobox.ts +99 -88
  75. package/src/components/select/config.ts +2 -8
  76. package/src/components/select/dropdown.ts +43 -108
  77. package/src/components/select/option.ts +44 -25
  78. package/src/components/select/search.ts +158 -117
  79. package/src/components/select/select.css +97 -27
  80. package/src/components/select/select.ts +181 -127
  81. package/src/components/select/tags.ts +1 -27
  82. package/src/components/select/templates.ts +194 -131
  83. package/src/components/select/utils.ts +30 -166
  84. package/src/helpers/dom.ts +0 -30
  85. package/webpack.config.js +6 -1
  86. package/examples/select/combobox-icons.html +0 -58
  87. package/examples/select/icon-description.html +0 -56
  88. /package/examples/select/{combobox.html → combobox_.html} +0 -0
  89. /package/examples/select/{remote-data.html → remote-data_.html} +0 -0
  90. /package/examples/select/{tags-icons.html → tags-icons_.html} +0 -0
  91. /package/examples/select/{tags-selected.html → tags-selected_.html} +0 -0
  92. /package/examples/select/{tags.html → tags_.html} +0 -0
@@ -6,6 +6,7 @@
6
6
  import { KTSelectConfigInterface } from './config';
7
7
  import { KTSelect } from './select';
8
8
  import { filterOptions, renderTemplateString, stringToElement } from './utils';
9
+ import { defaultTemplates } from './templates';
9
10
 
10
11
  /**
11
12
  * KTSelectCombobox - Handles combobox-specific functionality for KTSelect
@@ -17,57 +18,36 @@ export class KTSelectCombobox {
17
18
  private _clearButtonElement: HTMLElement | null;
18
19
  private _boundInputHandler: (event: Event) => void;
19
20
  private _boundClearHandler: (event: MouseEvent) => void;
20
- private _valuesContainerElement: HTMLElement | null;
21
+ private _valuesContainerElement: HTMLElement | null; // For tags or displayTemplate output
21
22
 
22
23
  constructor(select: KTSelect) {
23
24
  this._select = select;
24
25
  this._config = select.getConfig();
25
26
 
26
- // Get the display element (could be the input directly or a parent div)
27
- const displayElement = select.getValueDisplayElement();
28
-
29
- // Find the input element - either it's the display element itself or a child
30
- this._searchInputElement =
31
- displayElement.tagName === 'INPUT'
32
- ? (displayElement as HTMLInputElement)
33
- : displayElement.querySelector('input[data-kt-select-search]');
34
-
35
- // Find the clear button robustly
36
- let clearButtonContainer: HTMLElement | null = null;
37
- if (displayElement.tagName === 'DIV') {
38
- clearButtonContainer = displayElement;
39
- } else if (displayElement.tagName === 'INPUT') {
40
- clearButtonContainer = displayElement.parentElement as HTMLElement;
41
- }
42
-
43
- this._clearButtonElement = clearButtonContainer
44
- ? clearButtonContainer.querySelector('[data-kt-select-clear-button]')
45
- : null;
27
+ const displayElement = select.getDisplayElement(); // KTSelect's main display element for combobox
46
28
 
47
- this._valuesContainerElement = displayElement?.closest('[data-kt-select-combobox]')?.querySelector('[data-kt-select-combobox-values]');
29
+ this._searchInputElement = displayElement.querySelector('input[data-kt-select-search]');
30
+ this._clearButtonElement = displayElement.querySelector('[data-kt-select-clear-button]');
31
+ this._valuesContainerElement = displayElement.querySelector('[data-kt-select-combobox-values]');
48
32
 
49
- // Create bound handler references to allow proper cleanup
50
33
  this._boundInputHandler = this._handleComboboxInput.bind(this);
51
34
  this._boundClearHandler = this._handleClearButtonClick.bind(this);
52
35
 
53
- // Attach event listeners
54
36
  this._attachEventListeners();
55
37
 
56
- // Reset combobox search state when dropdown closes
57
38
  this._select.getElement().addEventListener('dropdown.close', () => {
58
- this._searchInputElement.value = '';
59
- // this._toggleClearButtonVisibility('');
60
- this._select.showAllOptions();
39
+ // When dropdown closes, if not multi-select and not using displayTemplate,
40
+ // ensure input shows the selected value or placeholder.
41
+ if (!this._config.multiple && !this._config.displayTemplate) {
42
+ this.updateDisplay(this._select.getSelectedOptions());
43
+ } else {
44
+ // For tags or displayTemplate, the input should be clear for typing.
45
+ this._searchInputElement.value = '';
46
+ }
47
+ this._toggleClearButtonVisibility(this._searchInputElement.value);
48
+ // this._select.showAllOptions(); // showAllOptions might be too broad, filtering is managed by typing.
61
49
  });
62
50
 
63
- // When selection changes, update the input value to the selected option's text
64
- // this._select.getElement().addEventListener('change', () => {
65
- // // Only update the input value, do not reset the filter or show all options
66
- // const selectedValues = this._select.getSelectedOptions();
67
- // const content = this._select.renderDisplayTemplateForSelected(selectedValues);
68
- // this._valuesContainerElement?.append(stringToElement(content));
69
- // });
70
-
71
51
  if (this._config.debug) console.log('KTSelectCombobox initialized');
72
52
  }
73
53
 
@@ -75,25 +55,13 @@ export class KTSelectCombobox {
75
55
  * Attach event listeners specific to combobox
76
56
  */
77
57
  private _attachEventListeners(): void {
78
- // First remove any existing listeners to prevent duplicates
79
58
  this._removeEventListeners();
80
-
81
- // Add input event handler to filter options as user types
82
- this._searchInputElement.addEventListener('input', this._boundInputHandler);
83
-
84
- // Add clear button click event listener
59
+ if (this._searchInputElement) { // Ensure element exists
60
+ this._searchInputElement.addEventListener('input', this._boundInputHandler);
61
+ }
85
62
  if (this._clearButtonElement) {
86
- this._clearButtonElement.addEventListener(
87
- 'click',
88
- this._boundClearHandler,
89
- );
63
+ this._clearButtonElement.addEventListener('click', this._boundClearHandler);
90
64
  }
91
-
92
- if (this._config.debug)
93
- console.log(
94
- 'Combobox event listeners attached to:',
95
- this._searchInputElement,
96
- );
97
65
  }
98
66
 
99
67
  /**
@@ -101,17 +69,10 @@ export class KTSelectCombobox {
101
69
  */
102
70
  private _removeEventListeners(): void {
103
71
  if (this._searchInputElement) {
104
- this._searchInputElement.removeEventListener(
105
- 'input',
106
- this._boundInputHandler,
107
- );
72
+ this._searchInputElement.removeEventListener('input', this._boundInputHandler);
108
73
  }
109
-
110
74
  if (this._clearButtonElement) {
111
- this._clearButtonElement.removeEventListener(
112
- 'click',
113
- this._boundClearHandler,
114
- );
75
+ this._clearButtonElement.removeEventListener('click', this._boundClearHandler);
115
76
  }
116
77
  }
117
78
 
@@ -120,19 +81,23 @@ export class KTSelectCombobox {
120
81
  */
121
82
  private _handleComboboxInput(event: Event): void {
122
83
  const inputElement = event.target as HTMLInputElement;
123
- const query = inputElement.value.toLowerCase();
84
+ const query = inputElement.value;
124
85
 
125
- if (this._config.debug) console.log('Combobox input event, query:', query);
86
+ this._toggleClearButtonVisibility(query);
126
87
 
127
- // Toggle clear button visibility based on input value
128
- // this._toggleClearButtonVisibility(query);
129
-
130
- // If dropdown isn't open, open it when user starts typing
131
- if (!(this._select as any)._dropdownIsOpen) {
88
+ if (!(this._select as any).isDropdownOpen()) { // Use public getter
132
89
  this._select.openDropdown();
133
90
  }
91
+ // For single select without displayTemplate, if user types, they are essentially clearing the previous selection text
92
+ // The actual selection state isn't cleared until they pick a new option or clear explicitly.
93
+ // For multi-select or with displayTemplate, the input is purely for search.
94
+ if (this._config.multiple || this._config.displayTemplate) {
95
+ // Values are in _valuesContainerElement, input is for search
96
+ } else {
97
+ // Single select, no displayTemplate: If user types, it implies they might be changing selection.
98
+ // We don't clear the actual _select state here, just the visual in input.
99
+ }
134
100
 
135
- // Filter options based on input
136
101
  this._filterOptionsForCombobox(query);
137
102
  }
138
103
 
@@ -143,35 +108,26 @@ export class KTSelectCombobox {
143
108
  event.preventDefault();
144
109
  event.stopPropagation();
145
110
 
146
- // Clear the input
147
111
  this._searchInputElement.value = '';
148
-
149
- // Hide the clear button
150
- // this._toggleClearButtonVisibility('');
151
-
152
- // Show all options and open dropdown
153
- this._select.showAllOptions();
154
- this._select.openDropdown();
155
-
156
- // Clear the current selection
157
- this._select.clearSelection();
158
-
159
- // Clear the combobox values container if present (for displayTemplate)
112
+ this._toggleClearButtonVisibility('');
160
113
  if (this._valuesContainerElement) {
161
114
  this._valuesContainerElement.innerHTML = '';
162
115
  }
163
-
164
- // Focus on the input
116
+ this._select.clearSelection(); // This will also trigger updateSelectedOptionDisplay
117
+ this._select.showAllOptions(); // Show all options after clearing
118
+ this._select.openDropdown();
165
119
  this._searchInputElement.focus();
166
120
  }
167
121
 
168
122
  /**
169
- * Toggle clear button visibility based on input value
123
+ * Toggle clear button visibility based on input value and selection state.
124
+ * Clear button should be visible if there's text in input OR if there are selected items (for multi/displayTemplate modes).
170
125
  */
171
- private _toggleClearButtonVisibility(value: string): void {
126
+ private _toggleClearButtonVisibility(inputValue: string): void {
172
127
  if (!this._clearButtonElement) return;
128
+ const hasSelectedItems = this._select.getSelectedOptions().length > 0;
173
129
 
174
- if (value.length > 0) {
130
+ if (inputValue.length > 0 || (hasSelectedItems && (this._config.multiple || this._config.displayTemplate))) {
175
131
  this._clearButtonElement.classList.remove('hidden');
176
132
  } else {
177
133
  this._clearButtonElement.classList.add('hidden');
@@ -182,11 +138,66 @@ export class KTSelectCombobox {
182
138
  * Filter options for combobox based on input query
183
139
  */
184
140
  private _filterOptionsForCombobox(query: string): void {
185
- // Use the same filter logic as KTSelectSearch
186
141
  const options = Array.from(this._select.getOptionsElement()) as HTMLElement[];
187
142
  const config = this._select.getConfig();
188
143
  const dropdownElement = this._select.getDropdownElement();
189
144
  filterOptions(options, query, config, dropdownElement);
145
+ // After filtering, focusManager in KTSelectSearch (if search is also enabled there)
146
+ // or the main FocusManager should adjust focus if needed.
147
+ // For combobox, this filtering is the primary search mechanism.
148
+ // We might need to tell select's focus manager to focus first option.
149
+ (this._select as any)._focusManager.focusFirst(); // Consider if this is always right
150
+ }
151
+
152
+ /**
153
+ * Updates the combobox display (input field or values container) based on selection.
154
+ */
155
+ public updateDisplay(selectedOptions: string[]): void {
156
+ if (!this._searchInputElement) return;
157
+
158
+ // Always clear the values container first if it exists
159
+ if (this._valuesContainerElement) {
160
+ this._valuesContainerElement.innerHTML = '';
161
+ }
162
+
163
+ if (this._config.tags && this._valuesContainerElement) { // Combobox + Tags
164
+ selectedOptions.forEach(value => {
165
+ // Ensure value is properly escaped for querySelector
166
+ const optionElement = this._select.getElement().querySelector(`option[value="${CSS.escape(value)}"]`) as HTMLOptionElement;
167
+ if (optionElement) {
168
+ const tagElement = defaultTemplates.tag(optionElement, this._config);
169
+ this._valuesContainerElement.appendChild(tagElement);
170
+ }
171
+ });
172
+ this._searchInputElement.value = ''; // Input field is for typing new searches
173
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
174
+
175
+ } else if (this._config.displayTemplate && this._valuesContainerElement) { // Combobox + DisplayTemplate (no tags)
176
+ this._valuesContainerElement.innerHTML = this._select.renderDisplayTemplateForSelected(selectedOptions);
177
+ this._searchInputElement.value = ''; // Input field is for typing new searches
178
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
179
+
180
+ } else if (this._config.multiple && this._valuesContainerElement) { // Combobox + Multiple (no tags, no display template)
181
+ // For simplicity, join text. A proper tag implementation would be more complex here.
182
+ this._valuesContainerElement.innerHTML = selectedOptions.map(value => {
183
+ const optionEl = this._select.getElement().querySelector(`option[value="${CSS.escape(value)}"]`);
184
+ return optionEl ? optionEl.textContent : '';
185
+ }).join(', '); // Basic comma separation
186
+ this._searchInputElement.value = '';
187
+ this._searchInputElement.placeholder = selectedOptions.length > 0 ? '' : (this._config.placeholder || 'Select...');
188
+
189
+ } else if (!this._config.multiple && selectedOptions.length > 0) { // Single select combobox: display selected option's text in the input
190
+ const selectedValue = selectedOptions[0];
191
+ const optionElement = this._select.getElement().querySelector(`option[value="${CSS.escape(selectedValue)}"]`);
192
+ this._searchInputElement.value = optionElement ? optionElement.textContent || '' : '';
193
+ // placeholder is implicitly handled by input value for single select
194
+
195
+ } else { // No selection or not fitting above categories (e.g. single select, no items)
196
+ this._searchInputElement.value = '';
197
+ this._searchInputElement.placeholder = this._config.placeholder || 'Select...';
198
+ // _valuesContainerElement is already cleared if it exists
199
+ }
200
+ this._toggleClearButtonVisibility(this._searchInputElement.value);
190
201
  }
191
202
 
192
203
  /**
@@ -42,7 +42,6 @@ export const DefaultConfig: KTSelectConfigInterface = {
42
42
  // Selection Behavior
43
43
  multiple: false, // Enable/disable multi-select
44
44
  maxSelections: null, // Maximum number of selections allowed in multi-select mode (null for unlimited)
45
- closeOnSelect: false, // Close the dropdown after selecting an option (single-select only)
46
45
  disabled: false, // Disable the select component
47
46
  isRequired: false, // Make selection required
48
47
 
@@ -53,7 +52,6 @@ export const DefaultConfig: KTSelectConfigInterface = {
53
52
  searchMinLength: 0, // Minimum characters required to trigger search
54
53
  searchMaxItems: 50, // Maximum number of search results to display
55
54
  searchNotFoundText: 'No results found', // Text to display when no search results are found
56
- searchHighlight: true, // Highlight matching search terms within the options
57
55
  clearSearchOnClose: true, // Clear search input when dropdown closes
58
56
 
59
57
  // Multi-Select Display
@@ -88,7 +86,6 @@ export interface KTSelectConfigInterface {
88
86
  // Selection Behavior
89
87
  multiple?: boolean;
90
88
  maxSelections?: number | null;
91
- closeOnSelect?: boolean;
92
89
  disabled?: boolean;
93
90
  isRequired?: boolean;
94
91
 
@@ -99,7 +96,6 @@ export interface KTSelectConfigInterface {
99
96
  searchMinLength?: number;
100
97
  searchMaxItems?: number;
101
98
  searchNotFoundText?: string;
102
- searchHighlight?: boolean;
103
99
  searchDebounce?: number;
104
100
  searchParam?: string;
105
101
  clearSearchOnClose?: boolean;
@@ -154,7 +150,6 @@ export interface KTSelectConfigInterface {
154
150
  loadMoreClass?: string;
155
151
  wrapperClass?: string;
156
152
  errorClass?: string;
157
- highlightClass?: string;
158
153
 
159
154
  // New Config
160
155
  tags?: boolean;
@@ -164,19 +159,17 @@ export interface KTSelectConfigInterface {
164
159
  placeholderTemplate?: string;
165
160
  displaySeparator?: string;
166
161
  displayTemplate?: string;
167
- displayToggle?: boolean;
168
162
  displayMaxSelected?: number;
169
163
  optionTemplate?: string;
170
164
  optionClass?: string;
171
165
  tagTemplate?: string;
172
- displayToggleClass?: string;
173
- displayToggleTemplate?: string;
174
166
 
175
167
  templates?: Partial<typeof coreTemplateStrings>;
176
168
 
177
169
  dropdownTemplate?: string;
178
170
 
179
171
  // Option Configuration
172
+ config?: KTSelectConfigInterface; // config from data-kt-select-config attribute
180
173
  optionsConfig?: Record<string, KTSelectConfigInterface>;
181
174
  }
182
175
 
@@ -200,6 +193,7 @@ export class KTSelectState {
200
193
  return {
201
194
  ...DefaultConfig,
202
195
  ...config,
196
+ ...config.config,
203
197
  };
204
198
  }
205
199
 
@@ -13,13 +13,14 @@ import KTData from '../../helpers/data';
13
13
  import KTComponent from '../component';
14
14
  import { KTSelectConfigInterface } from './config';
15
15
  import { FocusManager, EventManager } from './utils';
16
+ import { KTSelect } from './select'; // Added import
16
17
 
17
18
  /**
18
19
  * KTSelectDropdown
19
20
  *
20
21
  * A specialized dropdown implementation for the KTSelect component.
21
22
  * This module handles the dropdown functionality for the select component,
22
- * including positioning, showing/hiding, and keyboard navigation.
23
+ * including positioning and showing/hiding.
23
24
  */
24
25
  export class KTSelectDropdown extends KTComponent {
25
26
  protected override readonly _name: string = 'select-dropdown';
@@ -36,6 +37,7 @@ export class KTSelectDropdown extends KTComponent {
36
37
  private _popperInstance: PopperInstance | null = null;
37
38
  private _eventManager: EventManager;
38
39
  private _focusManager: FocusManager;
40
+ private _ktSelectInstance: KTSelect; // Added instance variable
39
41
 
40
42
  /**
41
43
  * Constructor
@@ -49,6 +51,7 @@ export class KTSelectDropdown extends KTComponent {
49
51
  toggleElement: HTMLElement,
50
52
  dropdownElement: HTMLElement,
51
53
  config: KTSelectConfigInterface,
54
+ ktSelectInstance: KTSelect, // Added parameter
52
55
  ) {
53
56
  super();
54
57
 
@@ -56,6 +59,7 @@ export class KTSelectDropdown extends KTComponent {
56
59
  this._toggleElement = toggleElement;
57
60
  this._dropdownElement = dropdownElement;
58
61
  this._config = config;
62
+ this._ktSelectInstance = ktSelectInstance; // Assign instance
59
63
  this._eventManager = new EventManager();
60
64
  this._focusManager = new FocusManager(
61
65
  dropdownElement,
@@ -92,7 +96,20 @@ export class KTSelectDropdown extends KTComponent {
92
96
  event.preventDefault();
93
97
  event.stopPropagation();
94
98
 
95
- this.toggle();
99
+ if (this._config.disabled) {
100
+ if (this._config.debug)
101
+ console.log(
102
+ 'KTSelectDropdown._handleToggleClick: select is disabled',
103
+ );
104
+ return;
105
+ }
106
+
107
+ // Call KTSelect's methods
108
+ if (this._ktSelectInstance.isDropdownOpen()) {
109
+ this._ktSelectInstance.closeDropdown();
110
+ } else {
111
+ this._ktSelectInstance.openDropdown();
112
+ }
96
113
  }
97
114
 
98
115
  /**
@@ -107,7 +124,8 @@ export class KTSelectDropdown extends KTComponent {
107
124
  !this._element.contains(target) &&
108
125
  !this._dropdownElement.contains(target)
109
126
  ) {
110
- this.close();
127
+ // Call KTSelect's closeDropdown method
128
+ this._ktSelectInstance.closeDropdown();
111
129
  }
112
130
  }
113
131
 
@@ -219,81 +237,50 @@ export class KTSelectDropdown extends KTComponent {
219
237
  }
220
238
  }
221
239
 
222
- /**
223
- * Toggle the dropdown
224
- */
225
- public toggle(): void {
226
- if (this._config.disabled) {
227
- if (this._config.debug) console.log('KTSelectDropdown.toggle: select is disabled, not toggling');
228
- return;
229
- }
230
- if (this._config.debug)
231
- console.log('KTSelectDropdown.toggle called - isOpen:', this._isOpen);
232
-
233
- if (this._isTransitioning) {
234
- if (this._config.debug)
235
- console.log('KTSelectDropdown.toggle - ignoring during transition');
236
- return;
237
- }
238
-
239
- if (this._isOpen) {
240
- this.close();
241
- } else {
242
- this.open();
243
- }
244
- }
245
-
246
240
  /**
247
241
  * Open the dropdown
248
242
  */
249
243
  public open(): void {
250
244
  if (this._config.disabled) {
251
- if (this._config.debug) console.log('KTSelectDropdown.open: select is disabled, not opening');
245
+ if (this._config.debug)
246
+ console.log(
247
+ 'KTSelectDropdown.open: select is disabled, not opening',
248
+ );
252
249
  return;
253
250
  }
254
251
  if (this._isOpen || this._isTransitioning) return;
255
252
 
256
- // Fire before show event
257
- const beforeShowEvent = new CustomEvent('kt.select.dropdown.show', {
258
- bubbles: true,
259
- cancelable: true,
260
- });
261
- this._element.dispatchEvent(beforeShowEvent);
262
-
263
- if (beforeShowEvent.defaultPrevented) return;
264
-
265
253
  // Begin opening transition
266
254
  this._isTransitioning = true;
267
255
 
268
- // Set initial styles - remove display: block and use class toggling instead
256
+ // Set initial styles
269
257
  this._dropdownElement.classList.remove('hidden');
270
258
  this._dropdownElement.style.opacity = '0';
271
259
 
272
260
  // Set dropdown width
273
261
  this._setDropdownWidth();
274
262
 
275
- // Make sure the element is visible for transitioning
263
+ // Reflow
276
264
  KTDom.reflow(this._dropdownElement);
277
265
 
278
- // Apply z-index if configured
266
+ // Apply z-index
279
267
  if (this._config.dropdownZindex) {
280
268
  this._dropdownElement.style.zIndex =
281
269
  this._config.dropdownZindex.toString();
282
270
  } else {
283
- // Auto-calculate z-index
284
271
  const parentZindex = KTDom.getHighestZindex(this._element);
285
272
  if (parentZindex) {
286
273
  this._dropdownElement.style.zIndex = (parentZindex + 1).toString();
287
274
  }
288
275
  }
289
276
 
290
- // Initialize popper for positioning
277
+ // Initialize popper
291
278
  this._initPopper();
292
279
 
293
- // Add active classes
280
+ // Add active classes for visual state
294
281
  this._dropdownElement.classList.add('open');
295
282
  this._toggleElement.classList.add('active');
296
- this._toggleElement.setAttribute('aria-expanded', 'true');
283
+ // ARIA attributes will be handled by KTSelect
297
284
 
298
285
  // Start transition
299
286
  this._dropdownElement.style.opacity = '1';
@@ -302,36 +289,10 @@ export class KTSelectDropdown extends KTComponent {
302
289
  KTDom.transitionEnd(this._dropdownElement, () => {
303
290
  this._isTransitioning = false;
304
291
  this._isOpen = true;
305
-
306
- // Focus the first item if search is enabled
307
- if (this._config.enableSearch) {
308
- const searchInput = this._dropdownElement.querySelector(
309
- 'input[type="search"]',
310
- );
311
- if (searchInput) {
312
- (searchInput as HTMLInputElement).focus();
313
- }
314
- }
315
-
316
- // Fire after show event
317
- const afterShowEvent = new CustomEvent('kt.select.dropdown.shown', {
318
- bubbles: true,
319
- });
320
- this._element.dispatchEvent(afterShowEvent);
292
+ // Focus and events will be handled by KTSelect
321
293
  });
322
294
  }
323
295
 
324
- /**
325
- * Focus the first option in the dropdown
326
- */
327
- private _focusFirstOption(): void {
328
- const firstOption = this._focusManager.getVisibleOptions()[0];
329
- if (firstOption) {
330
- this._focusManager.applyFocus(firstOption);
331
- this._focusManager.scrollIntoView(firstOption);
332
- }
333
- }
334
-
335
296
  /**
336
297
  * Close the dropdown
337
298
  */
@@ -352,42 +313,23 @@ export class KTSelectDropdown extends KTComponent {
352
313
  return;
353
314
  }
354
315
 
355
- // Fire before hide event
356
- const beforeHideEvent = new CustomEvent('kt.select.dropdown.hide', {
357
- bubbles: true,
358
- cancelable: true,
359
- });
360
- this._element.dispatchEvent(beforeHideEvent);
361
-
362
- if (beforeHideEvent.defaultPrevented) {
363
- if (this._config.debug)
364
- console.log(
365
- 'KTSelectDropdown.close - canceling due to defaultPrevented on beforeHideEvent',
366
- );
367
- return;
368
- }
316
+ // Events and ARIA will be handled by KTSelect
369
317
 
370
318
  if (this._config.debug)
371
319
  console.log('KTSelectDropdown.close - starting transition');
372
- // Begin closing transition
373
320
  this._isTransitioning = true;
374
321
 
375
- // Start transition
376
322
  this._dropdownElement.style.opacity = '0';
377
323
 
378
- // Use a combination of transition end and a fallback timer
379
324
  let transitionComplete = false;
380
-
381
- // Set a fixed-duration fallback in case the transition event doesn't fire
382
325
  const fallbackTimer = setTimeout(() => {
383
326
  if (!transitionComplete) {
384
327
  if (this._config.debug)
385
328
  console.log('KTSelectDropdown.close - fallback timer triggered');
386
329
  completeTransition();
387
330
  }
388
- }, 300); // 300ms should be enough for most transitions
331
+ }, 300);
389
332
 
390
- // Setup the transition end function
391
333
  const completeTransition = () => {
392
334
  if (transitionComplete) return;
393
335
  transitionComplete = true;
@@ -395,35 +337,28 @@ export class KTSelectDropdown extends KTComponent {
395
337
 
396
338
  if (this._config.debug)
397
339
  console.log('KTSelectDropdown.close - transition ended');
398
- // Remove active classes
340
+
399
341
  this._dropdownElement.classList.add('hidden');
400
342
  this._dropdownElement.classList.remove('open');
401
343
  this._toggleElement.classList.remove('active');
402
- this._toggleElement.setAttribute('aria-expanded', 'false');
403
-
404
- // Reset styles - replace display: none with adding hidden class
405
- this._dropdownElement.classList.add('hidden');
406
- this._dropdownElement.style.opacity = '';
407
- this._dropdownElement.style.zIndex = '';
344
+ // ARIA attributes will be handled by KTSelect
408
345
 
409
- // Destroy popper
410
346
  this._destroyPopper();
411
347
 
412
- // Update state
413
348
  this._isTransitioning = false;
414
349
  this._isOpen = false;
415
350
 
416
- // Fire after hide event
417
- const afterHideEvent = new CustomEvent('kt.select.dropdown.hidden', {
418
- bubbles: true,
419
- });
420
- this._element.dispatchEvent(afterHideEvent);
351
+ // Events will be handled by KTSelect
352
+
421
353
  if (this._config.debug)
422
- console.log('KTSelectDropdown.close - complete, events fired');
354
+ console.log('KTSelectDropdown.close - visual part complete');
423
355
  };
424
356
 
425
- // Handle transition end via the utility but also have the fallback
426
357
  KTDom.transitionEnd(this._dropdownElement, completeTransition);
358
+
359
+ if (KTDom.getCssProp(this._dropdownElement, 'transition-duration') === '0s') {
360
+ completeTransition();
361
+ }
427
362
  }
428
363
 
429
364
  /**