@keenthemes/ktui 1.0.11 → 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.
- package/dist/ktui.js +1687 -1517
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +5822 -0
- package/examples/select/avatar.html +47 -0
- package/examples/select/basic-usage.html +39 -0
- package/examples/select/combobox-icons_.html +59 -0
- package/examples/select/combobox_.html +46 -0
- package/examples/select/country.html +43 -0
- package/examples/select/description.html +53 -0
- package/examples/select/disable-option.html +37 -0
- package/examples/select/disable-select.html +35 -0
- package/examples/select/icon-multiple.html +50 -0
- package/examples/select/icon.html +48 -0
- package/examples/select/max-selection.html +38 -0
- package/examples/select/modal.html +72 -0
- package/examples/select/multiple.html +40 -0
- package/examples/select/placeholder.html +40 -0
- package/examples/select/remote-data_.html +32 -0
- package/examples/select/search.html +57 -0
- package/examples/select/sizes.html +94 -0
- package/examples/select/tags-icons_.html +58 -0
- package/examples/select/tags-selected_.html +59 -0
- package/examples/select/tags_.html +58 -0
- package/examples/select/template-customization.html +62 -0
- package/examples/toast/example.html +427 -0
- package/lib/cjs/components/datatable/datatable.js +23 -7
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/select/combobox.js +94 -141
- package/lib/cjs/components/select/combobox.js.map +1 -1
- package/lib/cjs/components/select/config.js +4 -18
- package/lib/cjs/components/select/config.js.map +1 -1
- package/lib/cjs/components/select/dropdown.js +35 -138
- package/lib/cjs/components/select/dropdown.js.map +1 -1
- package/lib/cjs/components/select/index.js +2 -1
- package/lib/cjs/components/select/index.js.map +1 -1
- package/lib/cjs/components/select/option.js +56 -6
- package/lib/cjs/components/select/option.js.map +1 -1
- package/lib/cjs/components/select/remote.js +1 -37
- package/lib/cjs/components/select/remote.js.map +1 -1
- package/lib/cjs/components/select/search.js +147 -128
- package/lib/cjs/components/select/search.js.map +1 -1
- package/lib/cjs/components/select/select.js +332 -391
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/cjs/components/select/tags.js +38 -56
- package/lib/cjs/components/select/tags.js.map +1 -1
- package/lib/cjs/components/select/templates.js +168 -200
- package/lib/cjs/components/select/templates.js.map +1 -1
- package/lib/cjs/components/select/types.js +0 -12
- package/lib/cjs/components/select/types.js.map +1 -1
- package/lib/cjs/components/select/utils.js +187 -339
- package/lib/cjs/components/select/utils.js.map +1 -1
- package/lib/cjs/components/toast/index.js +10 -0
- package/lib/cjs/components/toast/index.js.map +1 -0
- package/lib/cjs/components/toast/toast.js +543 -0
- package/lib/cjs/components/toast/toast.js.map +1 -0
- package/lib/cjs/components/toast/types.js +7 -0
- package/lib/cjs/components/toast/types.js.map +1 -0
- package/lib/cjs/index.js +5 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +23 -7
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/select/combobox.js +95 -142
- package/lib/esm/components/select/combobox.js.map +1 -1
- package/lib/esm/components/select/config.js +3 -17
- package/lib/esm/components/select/config.js.map +1 -1
- package/lib/esm/components/select/dropdown.js +35 -138
- package/lib/esm/components/select/dropdown.js.map +1 -1
- package/lib/esm/components/select/index.js +1 -1
- package/lib/esm/components/select/index.js.map +1 -1
- package/lib/esm/components/select/option.js +56 -6
- package/lib/esm/components/select/option.js.map +1 -1
- package/lib/esm/components/select/remote.js +1 -37
- package/lib/esm/components/select/remote.js.map +1 -1
- package/lib/esm/components/select/search.js +148 -129
- package/lib/esm/components/select/search.js.map +1 -1
- package/lib/esm/components/select/select.js +333 -392
- package/lib/esm/components/select/select.js.map +1 -1
- package/lib/esm/components/select/tags.js +38 -56
- package/lib/esm/components/select/tags.js.map +1 -1
- package/lib/esm/components/select/templates.js +167 -199
- package/lib/esm/components/select/templates.js.map +1 -1
- package/lib/esm/components/select/types.js +1 -11
- package/lib/esm/components/select/types.js.map +1 -1
- package/lib/esm/components/select/utils.js +184 -336
- package/lib/esm/components/select/utils.js.map +1 -1
- package/lib/esm/components/toast/index.js +6 -0
- package/lib/esm/components/toast/index.js.map +1 -0
- package/lib/esm/components/toast/toast.js +540 -0
- package/lib/esm/components/toast/toast.js.map +1 -0
- package/lib/esm/components/toast/types.js +6 -0
- package/lib/esm/components/toast/types.js.map +1 -0
- package/lib/esm/index.js +3 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +14 -9
- package/src/components/alert/alert.css +15 -2
- package/src/components/datatable/datatable.ts +25 -17
- package/src/components/input/input.css +3 -1
- package/src/components/link/link.css +2 -2
- package/src/components/scrollable/scrollable.css +9 -5
- package/src/components/select/combobox.ts +96 -192
- package/src/components/select/config.ts +35 -36
- package/src/components/select/dropdown.ts +43 -155
- package/src/components/select/index.ts +1 -1
- package/src/components/select/option.ts +50 -10
- package/src/components/select/remote.ts +2 -42
- package/src/components/select/search.ts +159 -158
- package/src/components/select/select.css +137 -18
- package/src/components/select/select.ts +354 -506
- package/src/components/select/tags.ts +37 -60
- package/src/components/select/templates.ts +254 -328
- package/src/components/select/types.ts +0 -10
- package/src/components/select/utils.ts +190 -416
- package/src/components/textarea/textarea.css +2 -1
- package/src/components/toast/index.ts +7 -0
- package/src/components/toast/toast.css +60 -0
- package/src/components/toast/toast.ts +605 -0
- package/src/components/toast/types.ts +169 -0
- package/src/index.ts +4 -0
- package/styles/main.css +3 -0
- package/styles/vars.css +138 -0
- package/styles.css +1 -0
- package/webpack.config.js +6 -1
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Copyright 2025 by Keenthemes Inc
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { KTSelectConfigInterface } from './config';
|
|
6
7
|
import { KTSelect } from './select';
|
|
7
8
|
import { defaultTemplates } from './templates';
|
|
8
9
|
import {
|
|
9
|
-
handleDropdownKeyNavigation,
|
|
10
10
|
filterOptions,
|
|
11
11
|
FocusManager,
|
|
12
12
|
EventManager,
|
|
@@ -17,10 +17,9 @@ export class KTSelectSearch {
|
|
|
17
17
|
private _searchInput: HTMLInputElement;
|
|
18
18
|
private _noResultsElement: HTMLElement | null = null;
|
|
19
19
|
private _originalOptionContents = new Map<string, string>();
|
|
20
|
-
private _boundKeyNavHandler: (event: KeyboardEvent) => void;
|
|
21
20
|
private _eventManager: EventManager;
|
|
22
21
|
private _focusManager: FocusManager;
|
|
23
|
-
private _config:
|
|
22
|
+
private _config: KTSelectConfigInterface;
|
|
24
23
|
|
|
25
24
|
// Public handler for search input (made public for event binding)
|
|
26
25
|
public handleSearchInput: (...args: any[]) => void;
|
|
@@ -34,7 +33,6 @@ export class KTSelectSearch {
|
|
|
34
33
|
'[data-kt-select-option]',
|
|
35
34
|
select.getConfig(),
|
|
36
35
|
);
|
|
37
|
-
this._boundKeyNavHandler = this._handleKeyboardNavigation.bind(this);
|
|
38
36
|
this.handleSearchInput = this._handleSearchInput.bind(this);
|
|
39
37
|
this._config = select.getConfig();
|
|
40
38
|
this._cacheOriginalOptionContents();
|
|
@@ -54,20 +52,27 @@ export class KTSelectSearch {
|
|
|
54
52
|
// First remove any existing listeners to prevent duplicates
|
|
55
53
|
this._removeEventListeners();
|
|
56
54
|
|
|
57
|
-
// Add the event listener
|
|
55
|
+
// Add the input event listener for filtering
|
|
58
56
|
this._eventManager.addListener(
|
|
59
57
|
this._searchInput,
|
|
60
58
|
'input',
|
|
61
59
|
this.handleSearchInput,
|
|
62
60
|
);
|
|
63
61
|
|
|
62
|
+
// Add keydown event listener for navigation, selection, and escape
|
|
63
|
+
this._eventManager.addListener(
|
|
64
|
+
this._searchInput,
|
|
65
|
+
'keydown',
|
|
66
|
+
this._handleSearchKeyDown.bind(this)
|
|
67
|
+
);
|
|
68
|
+
|
|
64
69
|
// Add blur event listener to ensure highlights are cleared when focus is lost
|
|
65
70
|
this._eventManager.addListener(this._searchInput, 'blur', () => {
|
|
66
71
|
// Small delay to prevent race conditions with selection
|
|
67
72
|
setTimeout(() => {
|
|
68
73
|
if (!this._searchInput.value) {
|
|
69
74
|
this._resetAllOptions();
|
|
70
|
-
this.
|
|
75
|
+
this.clearSearch();
|
|
71
76
|
}
|
|
72
77
|
}, 100);
|
|
73
78
|
});
|
|
@@ -90,56 +95,50 @@ export class KTSelectSearch {
|
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
this._eventManager.addListener(
|
|
96
|
-
this._searchInput,
|
|
97
|
-
'keydown',
|
|
98
|
-
this._boundKeyNavHandler,
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Listen for dropdown close to reset options if search is empty
|
|
102
|
-
this._select.getElement().addEventListener('dropdown.close', () => {
|
|
98
|
+
// Listen for dropdown close to reset options - ATTACH TO WRAPPER
|
|
99
|
+
this._select.getWrapperElement().addEventListener('dropdown.close', () => {
|
|
103
100
|
this._focusManager.resetFocus();
|
|
104
|
-
//
|
|
105
|
-
this.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
// If clearSearchOnClose is false and there's a value, the search term and filtered state should persist.
|
|
102
|
+
// KTSelect's closeDropdown method already calls this._searchModule.clearSearch() (which clears highlights)
|
|
103
|
+
// and conditionally clears the input value based on KTSelect's config.clearSearchOnClose.
|
|
104
|
+
// This listener in search.ts seems to unconditionally clear everything.
|
|
105
|
+
// For now, keeping its original behavior:
|
|
106
|
+
this.clearSearch(); // Clears highlights from current options
|
|
107
|
+
this._searchInput.value = ''; // Clears the search input field
|
|
108
|
+
this._resetAllOptions(); // Shows all options, restores original text, removes highlights
|
|
109
|
+
this._clearNoResultsMessage(); // Clears any "no results" message
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
// Clear highlights when an option is selected
|
|
112
|
+
// Clear highlights when an option is selected - ATTACH TO ORIGINAL SELECT (standard 'change' event)
|
|
116
113
|
this._select.getElement().addEventListener('change', () => {
|
|
117
|
-
this.
|
|
114
|
+
this.clearSearch();
|
|
118
115
|
|
|
119
116
|
// Close dropdown if configured to do so
|
|
120
|
-
|
|
121
|
-
this._select.getConfig().closeOnSelect &&
|
|
122
|
-
!this._select.getConfig().multiple
|
|
123
|
-
) {
|
|
124
|
-
this._select.closeDropdown();
|
|
125
|
-
}
|
|
117
|
+
this._select.closeDropdown();
|
|
126
118
|
});
|
|
127
119
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
120
|
+
// Consolidated 'dropdown.show' event listener - ATTACH TO WRAPPER
|
|
121
|
+
this._select.getWrapperElement().addEventListener('dropdown.show', () => {
|
|
122
|
+
this._focusManager.resetFocus(); // Always clear previous focus state
|
|
123
|
+
|
|
124
|
+
if (this._searchInput?.value) {
|
|
125
|
+
// If there's an existing search term:
|
|
126
|
+
// 1. Re-filter options. This ensures the display (hidden/visible) is correct
|
|
127
|
+
// and "no results" message is handled if query yields nothing.
|
|
128
|
+
this._filterOptions(this._searchInput.value);
|
|
129
|
+
} else {
|
|
130
|
+
// If search input is empty:
|
|
131
|
+
// 1. Reset all options to their full, unfiltered, original state.
|
|
132
|
+
this._resetAllOptions(); // Shows all, clears highlights from options, restores original text
|
|
133
|
+
// 2. Clear any "no results" message.
|
|
134
|
+
this._clearNoResultsMessage();
|
|
135
|
+
}
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
// Handle autofocus for the search input (this was one of the original separate listeners)
|
|
138
|
+
if (this._select.getConfig().searchAutofocus) {
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
this._searchInput?.focus(); // Focus search input
|
|
141
|
+
}, 50); // Delay to ensure dropdown is visible
|
|
143
142
|
}
|
|
144
143
|
});
|
|
145
144
|
}
|
|
@@ -156,54 +155,44 @@ export class KTSelectSearch {
|
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
/**
|
|
159
|
-
*
|
|
158
|
+
* Handles keydown events on the search input for navigation and actions.
|
|
160
159
|
*/
|
|
161
|
-
private
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.clearSearchHighlights();
|
|
160
|
+
private _handleSearchKeyDown(event: KeyboardEvent): void {
|
|
161
|
+
const key = event.key;
|
|
162
|
+
|
|
163
|
+
switch (key) {
|
|
164
|
+
case 'ArrowDown':
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
this._focusManager.focusNext();
|
|
167
|
+
break;
|
|
168
|
+
case 'ArrowUp':
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
this._focusManager.focusPrevious();
|
|
171
|
+
break;
|
|
172
|
+
case 'Enter':
|
|
173
|
+
event.preventDefault();
|
|
174
|
+
// Always attempt to select the first available option in the list.
|
|
175
|
+
// focusFirst() finds, focuses, and returns the first visible, non-disabled option.
|
|
176
|
+
const firstAvailableOption = this._focusManager.focusFirst();
|
|
177
|
+
|
|
178
|
+
if (firstAvailableOption) {
|
|
179
|
+
const optionValue = firstAvailableOption.getAttribute('data-value');
|
|
180
|
+
if (optionValue) {
|
|
181
|
+
this._select.toggleSelection(optionValue);
|
|
182
|
+
// KTSelect.toggleSelection handles closing the dropdown based on config.closeOnSelect and config.multiple
|
|
185
183
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const optionValue = focusedOption.getAttribute('data-value');
|
|
199
|
-
|
|
200
|
-
if (optionValue) {
|
|
201
|
-
// Ensure highlights are cleared before selection
|
|
202
|
-
this.clearSearchHighlights();
|
|
203
|
-
|
|
204
|
-
// Trigger the selection in the main select component
|
|
205
|
-
this._select['_selectOption'](optionValue);
|
|
206
|
-
}
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
case 'Escape':
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
this._searchInput.value = '';
|
|
189
|
+
this.clearSearch();
|
|
190
|
+
this._resetAllOptions();
|
|
191
|
+
this._clearNoResultsMessage();
|
|
192
|
+
this._focusManager.focusFirst();
|
|
193
|
+
break;
|
|
194
|
+
default:
|
|
195
|
+
break;
|
|
207
196
|
}
|
|
208
197
|
}
|
|
209
198
|
|
|
@@ -214,46 +203,71 @@ export class KTSelectSearch {
|
|
|
214
203
|
private _cacheOriginalOptionContents() {
|
|
215
204
|
// Wait for options to be initialized
|
|
216
205
|
setTimeout(() => {
|
|
206
|
+
this._originalOptionContents.clear(); // Clear before re-caching
|
|
217
207
|
const options = Array.from(this._select.getOptionsElement());
|
|
218
208
|
options.forEach((option) => {
|
|
219
209
|
const value = option.getAttribute('data-value');
|
|
220
210
|
if (value) {
|
|
211
|
+
// Store the full innerHTML as the original content
|
|
221
212
|
this._originalOptionContents.set(value, option.innerHTML);
|
|
222
213
|
}
|
|
223
214
|
});
|
|
224
215
|
}, 0);
|
|
225
216
|
}
|
|
226
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Restores the innerHTML of all options from the cache if they have been modified.
|
|
220
|
+
* This is typically called before applying new filters/highlights.
|
|
221
|
+
*/
|
|
222
|
+
private _restoreOptionContentsBeforeFilter(): void {
|
|
223
|
+
const options = Array.from(this._select.getOptionsElement()) as HTMLElement[];
|
|
224
|
+
options.forEach(option => {
|
|
225
|
+
const value = option.getAttribute('data-value');
|
|
226
|
+
if (value && this._originalOptionContents.has(value)) {
|
|
227
|
+
const originalContent = this._originalOptionContents.get(value)!;
|
|
228
|
+
// Only restore if current content is different, to avoid unnecessary DOM manipulation
|
|
229
|
+
if (option.innerHTML !== originalContent) {
|
|
230
|
+
option.innerHTML = originalContent;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
227
236
|
private _handleSearchInput(event: Event) {
|
|
228
|
-
const query = (event.target as HTMLInputElement).value
|
|
237
|
+
const query = (event.target as HTMLInputElement).value;
|
|
229
238
|
const config = this._select.getConfig();
|
|
230
239
|
|
|
231
240
|
// Reset focused option when search changes
|
|
232
241
|
this._focusManager.resetFocus();
|
|
233
242
|
|
|
234
|
-
//
|
|
243
|
+
// Restore original content for all options before filtering/highlighting again
|
|
244
|
+
this._restoreOptionContentsBeforeFilter();
|
|
245
|
+
|
|
235
246
|
if (query.trim() === '') {
|
|
236
|
-
this.
|
|
247
|
+
this._resetAllOptions();
|
|
248
|
+
this._focusManager.focusFirst(); // Focus first option when search is cleared
|
|
249
|
+
return;
|
|
237
250
|
}
|
|
238
251
|
|
|
239
|
-
// For remote search,
|
|
240
|
-
//
|
|
252
|
+
// For remote search, KTSelect component handles it.
|
|
253
|
+
// KTSelect will call refreshAfterSearch on this module when remote data is updated.
|
|
241
254
|
if (config.remote && config.searchParam) {
|
|
242
|
-
// If query is too short, reset all options to visible state
|
|
243
255
|
if (query.length < config.searchMinLength) {
|
|
244
256
|
this._resetAllOptions();
|
|
245
257
|
this._clearNoResultsMessage();
|
|
258
|
+
this._focusManager.focusFirst(); // Focus first if query too short
|
|
246
259
|
}
|
|
247
|
-
// Otherwise, let KTSelect handle remote search
|
|
248
260
|
return;
|
|
249
261
|
}
|
|
250
262
|
|
|
251
263
|
// For local search
|
|
252
264
|
if (query.length >= config.searchMinLength) {
|
|
253
265
|
this._filterOptions(query);
|
|
266
|
+
this._focusManager.focusFirst(); // Focus first visible option after local filtering
|
|
254
267
|
} else {
|
|
255
268
|
this._resetAllOptions();
|
|
256
269
|
this._clearNoResultsMessage();
|
|
270
|
+
this._focusManager.focusFirst(); // Focus first if query too short and not remote
|
|
257
271
|
}
|
|
258
272
|
}
|
|
259
273
|
|
|
@@ -269,22 +283,12 @@ export class KTSelectSearch {
|
|
|
269
283
|
this._cacheOriginalOptionContents();
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
this._handleNoResults(visibleCount),
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
// Apply specialized text highlighting if needed
|
|
278
|
-
if (config.searchHighlight && query.trim() !== '') {
|
|
279
|
-
this._applyHighlightToDisplay(query);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
286
|
+
// Restore original content before filtering, so highlighting is applied fresh.
|
|
287
|
+
this._restoreOptionContentsBeforeFilter();
|
|
282
288
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
private _applyHighlightToDisplay(query: string) {
|
|
287
|
-
// Implementation for display highlighting
|
|
289
|
+
const visibleCount = filterOptions(options, query, config, dropdownElement, (count) =>
|
|
290
|
+
this._handleNoResults(count),
|
|
291
|
+
);
|
|
288
292
|
}
|
|
289
293
|
|
|
290
294
|
/**
|
|
@@ -296,44 +300,31 @@ export class KTSelectSearch {
|
|
|
296
300
|
this._select.getOptionsElement(),
|
|
297
301
|
) as HTMLElement[];
|
|
298
302
|
|
|
299
|
-
//
|
|
303
|
+
// Ensure the cache is populated if it's somehow empty here
|
|
300
304
|
if (this._originalOptionContents.size === 0) {
|
|
301
305
|
this._cacheOriginalOptionContents();
|
|
302
306
|
}
|
|
303
307
|
|
|
304
308
|
options.forEach((option) => {
|
|
305
|
-
// Remove the hidden class
|
|
306
309
|
option.classList.remove('hidden');
|
|
310
|
+
if (option.style.display === 'none') option.style.display = ''; // Ensure visible
|
|
307
311
|
|
|
308
312
|
// Restore original HTML content (remove highlights)
|
|
309
313
|
const value = option.getAttribute('data-value');
|
|
310
314
|
if (value && this._originalOptionContents.has(value)) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (
|
|
316
|
-
option.hasAttribute('style') &&
|
|
317
|
-
option.getAttribute('style').includes('display:')
|
|
318
|
-
) {
|
|
319
|
-
const styleAttr = option.getAttribute('style');
|
|
320
|
-
if (
|
|
321
|
-
styleAttr.trim() === 'display: none;' ||
|
|
322
|
-
styleAttr.trim() === 'display: block;'
|
|
323
|
-
) {
|
|
324
|
-
option.removeAttribute('style');
|
|
325
|
-
} else {
|
|
326
|
-
option.setAttribute(
|
|
327
|
-
'style',
|
|
328
|
-
styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
|
|
329
|
-
);
|
|
315
|
+
const originalContent = this._originalOptionContents.get(value)!;
|
|
316
|
+
// Only update if different, to minimize DOM changes
|
|
317
|
+
if (option.innerHTML !== originalContent) {
|
|
318
|
+
option.innerHTML = originalContent;
|
|
330
319
|
}
|
|
331
320
|
}
|
|
332
321
|
});
|
|
322
|
+
|
|
323
|
+
this._clearNoResultsMessage(); // Ensure no results message is cleared when resetting
|
|
333
324
|
}
|
|
334
325
|
|
|
335
326
|
private _handleNoResults(visibleOptionsCount: number) {
|
|
336
|
-
if (visibleOptionsCount === 0 && this._searchInput
|
|
327
|
+
if (visibleOptionsCount === 0 && this._searchInput?.value?.trim() !== '') {
|
|
337
328
|
this._showNoResultsMessage();
|
|
338
329
|
} else {
|
|
339
330
|
this._clearNoResultsMessage();
|
|
@@ -344,11 +335,11 @@ export class KTSelectSearch {
|
|
|
344
335
|
this._clearNoResultsMessage();
|
|
345
336
|
|
|
346
337
|
const config = this._select.getConfig();
|
|
347
|
-
this._noResultsElement = defaultTemplates.
|
|
338
|
+
this._noResultsElement = defaultTemplates.empty(config);
|
|
348
339
|
|
|
349
340
|
const dropdownElement = this._select.getDropdownElement();
|
|
350
341
|
const optionsContainer = dropdownElement.querySelector(
|
|
351
|
-
'[data-kt-select-options
|
|
342
|
+
'[data-kt-select-options]',
|
|
352
343
|
);
|
|
353
344
|
if (optionsContainer) {
|
|
354
345
|
optionsContainer.appendChild(this._noResultsElement);
|
|
@@ -368,28 +359,27 @@ export class KTSelectSearch {
|
|
|
368
359
|
* Public method to explicitly clear all search highlights
|
|
369
360
|
* This is called when search is reset or selection changes
|
|
370
361
|
*/
|
|
371
|
-
public
|
|
362
|
+
public clearSearch() {
|
|
372
363
|
// Restore original option content (removes highlighting)
|
|
373
|
-
const
|
|
364
|
+
const optionsToClear = Array.from(
|
|
374
365
|
this._select.getOptionsElement(),
|
|
375
366
|
) as HTMLElement[];
|
|
376
367
|
|
|
377
|
-
|
|
368
|
+
// Ensure cache is available
|
|
369
|
+
if (this._originalOptionContents.size === 0 && optionsToClear.length > 0) {
|
|
370
|
+
this._cacheOriginalOptionContents();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
optionsToClear.forEach((option) => {
|
|
378
374
|
const value = option.getAttribute('data-value');
|
|
379
375
|
if (value && this._originalOptionContents.has(value)) {
|
|
380
|
-
|
|
376
|
+
const originalContent = this._originalOptionContents.get(value)!;
|
|
377
|
+
// Only restore if different
|
|
378
|
+
if (option.innerHTML !== originalContent) {
|
|
379
|
+
option.innerHTML = originalContent;
|
|
380
|
+
}
|
|
381
381
|
}
|
|
382
382
|
});
|
|
383
|
-
|
|
384
|
-
// Also clear highlights from the display element
|
|
385
|
-
this._clearDisplayHighlights();
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Clear any highlights from the display element (selected values)
|
|
390
|
-
*/
|
|
391
|
-
private _clearDisplayHighlights() {
|
|
392
|
-
// Implementation for clearing display highlights
|
|
393
383
|
}
|
|
394
384
|
|
|
395
385
|
/**
|
|
@@ -398,11 +388,11 @@ export class KTSelectSearch {
|
|
|
398
388
|
public refreshOptionCache(): void {
|
|
399
389
|
// Re-cache all option contents
|
|
400
390
|
this._originalOptionContents.clear();
|
|
401
|
-
const
|
|
391
|
+
const currentOptions = Array.from(
|
|
402
392
|
this._select.getOptionsElement(),
|
|
403
393
|
) as HTMLElement[];
|
|
404
394
|
|
|
405
|
-
|
|
395
|
+
currentOptions.forEach((option) => {
|
|
406
396
|
const value = option.getAttribute('data-value');
|
|
407
397
|
if (value) {
|
|
408
398
|
this._originalOptionContents.set(value, option.innerHTML);
|
|
@@ -410,6 +400,16 @@ export class KTSelectSearch {
|
|
|
410
400
|
});
|
|
411
401
|
}
|
|
412
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Called after search (local or remote via KTSelect) to reset focus.
|
|
405
|
+
*/
|
|
406
|
+
public refreshAfterSearch(): void {
|
|
407
|
+
this._focusManager.resetFocus();
|
|
408
|
+
this._focusManager.focusFirst();
|
|
409
|
+
// Re-cache original contents as options might have changed (especially after remote search)
|
|
410
|
+
this.refreshOptionCache();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
413
|
/**
|
|
414
414
|
* Clean up all resources used by the search module
|
|
415
415
|
*/
|
|
@@ -418,13 +418,14 @@ export class KTSelectSearch {
|
|
|
418
418
|
this._removeEventListeners();
|
|
419
419
|
|
|
420
420
|
// Clear all references
|
|
421
|
-
this._focusManager
|
|
422
|
-
|
|
421
|
+
if (this._focusManager) {
|
|
422
|
+
this._focusManager.dispose();
|
|
423
|
+
}
|
|
423
424
|
|
|
424
425
|
// Clear cached content
|
|
425
426
|
this._originalOptionContents.clear();
|
|
426
427
|
|
|
427
428
|
// Clear highlight elements
|
|
428
|
-
this.
|
|
429
|
+
this.clearSearch();
|
|
429
430
|
}
|
|
430
431
|
}
|