@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.
- package/dist/ktui.js +674 -225
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +13 -1
- package/lib/cjs/components/component.js +22 -0
- package/lib/cjs/components/component.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +7 -1
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/drawer/drawer.js +255 -9
- package/lib/cjs/components/drawer/drawer.js.map +1 -1
- package/lib/cjs/components/dropdown/dropdown.js +55 -8
- package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
- package/lib/cjs/components/select/combobox.js +0 -2
- package/lib/cjs/components/select/combobox.js.map +1 -1
- package/lib/cjs/components/select/config.js +4 -1
- package/lib/cjs/components/select/config.js.map +1 -1
- package/lib/cjs/components/select/dropdown.js +0 -16
- package/lib/cjs/components/select/dropdown.js.map +1 -1
- package/lib/cjs/components/select/remote.js +0 -40
- package/lib/cjs/components/select/remote.js.map +1 -1
- package/lib/cjs/components/select/search.js +93 -22
- package/lib/cjs/components/select/search.js.map +1 -1
- package/lib/cjs/components/select/select.js +180 -114
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/cjs/components/select/tags.js +0 -2
- package/lib/cjs/components/select/tags.js.map +1 -1
- package/lib/cjs/components/sticky/sticky.js +44 -5
- package/lib/cjs/components/sticky/sticky.js.map +1 -1
- package/lib/cjs/helpers/data.js +8 -0
- package/lib/cjs/helpers/data.js.map +1 -1
- package/lib/cjs/helpers/event-handler.js +6 -5
- package/lib/cjs/helpers/event-handler.js.map +1 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/component.js +22 -0
- package/lib/esm/components/component.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +7 -1
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/drawer/drawer.js +255 -9
- package/lib/esm/components/drawer/drawer.js.map +1 -1
- package/lib/esm/components/dropdown/dropdown.js +55 -8
- package/lib/esm/components/dropdown/dropdown.js.map +1 -1
- package/lib/esm/components/select/combobox.js +0 -2
- package/lib/esm/components/select/combobox.js.map +1 -1
- package/lib/esm/components/select/config.js +4 -1
- package/lib/esm/components/select/config.js.map +1 -1
- package/lib/esm/components/select/dropdown.js +0 -16
- package/lib/esm/components/select/dropdown.js.map +1 -1
- package/lib/esm/components/select/remote.js +0 -40
- package/lib/esm/components/select/remote.js.map +1 -1
- package/lib/esm/components/select/search.js +93 -22
- package/lib/esm/components/select/search.js.map +1 -1
- package/lib/esm/components/select/select.js +180 -114
- package/lib/esm/components/select/select.js.map +1 -1
- package/lib/esm/components/select/tags.js +0 -2
- package/lib/esm/components/select/tags.js.map +1 -1
- package/lib/esm/components/sticky/sticky.js +44 -5
- package/lib/esm/components/sticky/sticky.js.map +1 -1
- package/lib/esm/helpers/data.js +8 -0
- package/lib/esm/helpers/data.js.map +1 -1
- package/lib/esm/helpers/event-handler.js +6 -5
- package/lib/esm/helpers/event-handler.js.map +1 -1
- package/lib/esm/index.js.map +1 -1
- package/package.json +6 -4
- package/src/components/component.ts +26 -0
- package/src/components/datatable/__tests__/race-conditions.test.ts +7 -7
- package/src/components/datatable/datatable.ts +8 -1
- package/src/components/drawer/drawer.ts +266 -10
- package/src/components/dropdown/dropdown.ts +63 -8
- package/src/components/select/__tests__/ux-behaviors.test.ts +997 -0
- package/src/components/select/combobox.ts +0 -1
- package/src/components/select/config.ts +7 -1
- package/src/components/select/dropdown.ts +0 -24
- package/src/components/select/remote.ts +0 -49
- package/src/components/select/search.ts +97 -24
- package/src/components/select/select.css +5 -1
- package/src/components/select/select.ts +211 -153
- package/src/components/select/tags.ts +0 -1
- package/src/components/sticky/sticky.ts +55 -5
- package/src/helpers/data.ts +10 -0
- package/src/helpers/event-handler.ts +7 -6
- 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
|
|
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
|
|
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
|
|
1079
|
-
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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,
|
|
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
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
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
|
}
|