@keenthemes/ktui 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ktui.js +418 -144
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +139 -31
- package/examples/image-input/file-upload-example.html +189 -0
- package/examples/select/remote-data_.html +5 -0
- package/examples/select/test-optimizations.html +227 -0
- package/examples/select/test-remote-search.html +151 -0
- package/examples/sticky/README.md +158 -0
- package/examples/sticky/debug-sticky.html +144 -0
- package/examples/sticky/test-runner.html +175 -0
- package/examples/sticky/test-sticky-logic.js +369 -0
- package/examples/sticky/test-sticky-positioning.html +386 -0
- package/examples/toast/example.html +52 -0
- package/lib/cjs/components/component.js +5 -3
- package/lib/cjs/components/component.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +4 -0
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +9 -3
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/image-input/image-input.js +10 -2
- package/lib/cjs/components/image-input/image-input.js.map +1 -1
- package/lib/cjs/components/select/combobox.js +50 -20
- package/lib/cjs/components/select/combobox.js.map +1 -1
- package/lib/cjs/components/select/dropdown.js +4 -2
- package/lib/cjs/components/select/dropdown.js.map +1 -1
- package/lib/cjs/components/select/index.js.map +1 -1
- package/lib/cjs/components/select/option.js +2 -1
- package/lib/cjs/components/select/option.js.map +1 -1
- package/lib/cjs/components/select/remote.js +50 -50
- package/lib/cjs/components/select/remote.js.map +1 -1
- package/lib/cjs/components/select/search.js +7 -5
- package/lib/cjs/components/select/search.js.map +1 -1
- package/lib/cjs/components/select/select.js +199 -33
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/cjs/components/select/tags.js +3 -1
- package/lib/cjs/components/select/tags.js.map +1 -1
- package/lib/cjs/components/select/templates.js.map +1 -1
- package/lib/cjs/components/select/utils.js +23 -10
- package/lib/cjs/components/select/utils.js.map +1 -1
- package/lib/cjs/components/sticky/sticky.js +52 -14
- package/lib/cjs/components/sticky/sticky.js.map +1 -1
- package/lib/esm/components/component.js +5 -3
- package/lib/esm/components/component.js.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +4 -0
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +9 -3
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/image-input/image-input.js +10 -2
- package/lib/esm/components/image-input/image-input.js.map +1 -1
- package/lib/esm/components/select/combobox.js +50 -20
- package/lib/esm/components/select/combobox.js.map +1 -1
- package/lib/esm/components/select/dropdown.js +4 -2
- 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 +2 -1
- package/lib/esm/components/select/option.js.map +1 -1
- package/lib/esm/components/select/remote.js +50 -50
- package/lib/esm/components/select/remote.js.map +1 -1
- package/lib/esm/components/select/search.js +8 -6
- package/lib/esm/components/select/search.js.map +1 -1
- package/lib/esm/components/select/select.js +199 -33
- package/lib/esm/components/select/select.js.map +1 -1
- package/lib/esm/components/select/tags.js +3 -1
- package/lib/esm/components/select/tags.js.map +1 -1
- package/lib/esm/components/select/templates.js.map +1 -1
- package/lib/esm/components/select/utils.js +23 -10
- package/lib/esm/components/select/utils.js.map +1 -1
- package/lib/esm/components/sticky/sticky.js +52 -14
- package/lib/esm/components/sticky/sticky.js.map +1 -1
- package/package.json +1 -1
- package/src/components/component.ts +12 -11
- package/src/components/datatable/datatable-sort.ts +6 -0
- package/src/components/datatable/datatable.ts +90 -81
- package/src/components/image-input/image-input.ts +11 -2
- package/src/components/image-input/types.ts +1 -0
- package/src/components/input/input-group.css +1 -1
- package/src/components/input/input.css +1 -1
- package/src/components/scrollable/scrollable.css +3 -3
- package/src/components/select/combobox.ts +84 -34
- package/src/components/select/dropdown.ts +20 -11
- package/src/components/select/index.ts +6 -1
- package/src/components/select/option.ts +7 -6
- package/src/components/select/remote.ts +51 -52
- package/src/components/select/search.ts +51 -45
- package/src/components/select/select.css +12 -11
- package/src/components/select/select.ts +371 -102
- package/src/components/select/tags.ts +9 -3
- package/src/components/select/templates.ts +1 -4
- package/src/components/select/utils.ts +55 -20
- package/src/components/select/variants.css +0 -1
- package/src/components/sticky/sticky.ts +47 -16
- package/src/components/sticky/types.ts +3 -0
- package/src/components/table/table.css +1 -1
- package/src/components/textarea/textarea.css +1 -1
- package/src/components/toast/toast.css +84 -47
- package/src/components/toast/types.ts +3 -0
|
@@ -261,16 +261,13 @@ export class KTSelectRemote {
|
|
|
261
261
|
const labelField = this._config.dataFieldText || 'title';
|
|
262
262
|
|
|
263
263
|
if (this._config.debug)
|
|
264
|
-
console.log(
|
|
265
|
-
`Mapping fields: value=${valueField}, label=${labelField}`,
|
|
266
|
-
);
|
|
264
|
+
console.log(`Mapping fields: value=${valueField}, label=${labelField}`);
|
|
267
265
|
if (this._config.debug)
|
|
268
266
|
console.log('Item data:', JSON.stringify(item).substring(0, 200) + '...'); // Trimmed for readability
|
|
269
267
|
|
|
270
|
-
// Extract values using
|
|
268
|
+
// Extract values using improved getValue function
|
|
271
269
|
const getValue = (obj: any, path: string): any => {
|
|
272
|
-
if (!path) return null;
|
|
273
|
-
if (!obj) return null;
|
|
270
|
+
if (!path || !obj) return null;
|
|
274
271
|
|
|
275
272
|
try {
|
|
276
273
|
// Handle dot notation to access nested properties
|
|
@@ -296,7 +293,7 @@ export class KTSelectRemote {
|
|
|
296
293
|
? typeof result === 'object'
|
|
297
294
|
? JSON.stringify(result).substring(0, 50)
|
|
298
295
|
: String(result).substring(0, 50)
|
|
299
|
-
|
|
296
|
+
: 'null'
|
|
300
297
|
}`,
|
|
301
298
|
);
|
|
302
299
|
|
|
@@ -307,64 +304,66 @@ export class KTSelectRemote {
|
|
|
307
304
|
}
|
|
308
305
|
};
|
|
309
306
|
|
|
310
|
-
//
|
|
307
|
+
// Get ID and ensure it's a string
|
|
311
308
|
let id = getValue(item, valueField);
|
|
312
|
-
|
|
313
|
-
// Ensure id is always a proper string
|
|
314
309
|
if (id === null || id === undefined) {
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
console.log(`Using id.value as fallback: ${id}`);
|
|
325
|
-
} else if (item.id) {
|
|
326
|
-
id = String(item.id);
|
|
327
|
-
if (this._config.debug)
|
|
328
|
-
console.log(`Using direct item.id as fallback: ${id}`);
|
|
329
|
-
} else {
|
|
330
|
-
// If no ID found at all, use the title instead (will be extracted below)
|
|
331
|
-
if (this._config.debug)
|
|
332
|
-
console.log(`No ID found, will use title as fallback`);
|
|
333
|
-
id = null;
|
|
310
|
+
// Try common fallback fields for ID
|
|
311
|
+
const fallbackFields = ['id', 'value', 'key', 'pk'];
|
|
312
|
+
for (const field of fallbackFields) {
|
|
313
|
+
if (item[field] !== null && item[field] !== undefined) {
|
|
314
|
+
id = String(item[field]);
|
|
315
|
+
if (this._config.debug)
|
|
316
|
+
console.log(`Using fallback field '${field}' for ID: ${id}`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
334
319
|
}
|
|
335
|
-
} else if (typeof id === 'object') {
|
|
336
|
-
// If ID is an object, log the issue and set to null to use title fallback
|
|
337
|
-
console.warn(
|
|
338
|
-
`ID for path ${valueField} is an object, will use title fallback instead`,
|
|
339
|
-
);
|
|
340
|
-
id = null;
|
|
341
320
|
} else {
|
|
342
|
-
// Otherwise, ensure it's a string
|
|
343
321
|
id = String(id);
|
|
344
|
-
if (this._config.debug) console.log(`Final ID value: ${id}`);
|
|
345
322
|
}
|
|
346
323
|
|
|
347
|
-
//
|
|
324
|
+
// If still no ID, generate one
|
|
325
|
+
if (!id) {
|
|
326
|
+
id = `option-${Math.random().toString(36).substr(2, 9)}`;
|
|
327
|
+
if (this._config.debug) console.log(`Generated fallback ID: ${id}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Get label with proper fallbacks
|
|
348
331
|
let title = getValue(item, labelField);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
332
|
+
if (!title) {
|
|
333
|
+
// Try common fallback fields for title
|
|
334
|
+
const fallbackFields = [
|
|
335
|
+
'name',
|
|
336
|
+
'title',
|
|
337
|
+
'label',
|
|
338
|
+
'text',
|
|
339
|
+
'displayName',
|
|
340
|
+
'description',
|
|
341
|
+
];
|
|
342
|
+
for (const field of fallbackFields) {
|
|
343
|
+
if (item[field] !== null && item[field] !== undefined) {
|
|
344
|
+
title = String(item[field]);
|
|
345
|
+
if (this._config.debug)
|
|
346
|
+
console.log(`Using fallback field '${field}' for title: ${title}`);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
title = String(title);
|
|
352
|
+
}
|
|
352
353
|
|
|
353
|
-
// If
|
|
354
|
+
// If still no title, use ID as fallback
|
|
354
355
|
if (!title) {
|
|
355
|
-
|
|
356
|
-
if (item.name) title = String(item.name);
|
|
357
|
-
else if (item.title) title = String(item.title);
|
|
358
|
-
else if (item.label) title = String(item.label);
|
|
359
|
-
else if (item.text) title = String(item.text);
|
|
356
|
+
title = `Option ${id}`;
|
|
360
357
|
if (this._config.debug)
|
|
361
|
-
console.log(
|
|
358
|
+
console.log(`Using ID as fallback title: ${title}`);
|
|
362
359
|
}
|
|
363
360
|
|
|
364
|
-
// Create the option object with
|
|
365
|
-
const result = {
|
|
366
|
-
id: id
|
|
367
|
-
title: title
|
|
361
|
+
// Create the option object with consistent structure
|
|
362
|
+
const result: KTSelectOptionData = {
|
|
363
|
+
id: id,
|
|
364
|
+
title: title,
|
|
365
|
+
selected: Boolean(item.selected),
|
|
366
|
+
disabled: Boolean(item.disabled),
|
|
368
367
|
};
|
|
369
368
|
|
|
370
369
|
if (this._config.debug)
|
|
@@ -6,11 +6,7 @@
|
|
|
6
6
|
import { KTSelectConfigInterface } from './config';
|
|
7
7
|
import { KTSelect } from './select';
|
|
8
8
|
import { defaultTemplates } from './templates';
|
|
9
|
-
import {
|
|
10
|
-
filterOptions,
|
|
11
|
-
FocusManager,
|
|
12
|
-
EventManager,
|
|
13
|
-
} from './utils';
|
|
9
|
+
import { filterOptions, FocusManager, EventManager } from './utils';
|
|
14
10
|
|
|
15
11
|
export class KTSelectSearch {
|
|
16
12
|
private _select: KTSelect;
|
|
@@ -63,7 +59,7 @@ export class KTSelectSearch {
|
|
|
63
59
|
this._eventManager.addListener(
|
|
64
60
|
this._searchInput,
|
|
65
61
|
'keydown',
|
|
66
|
-
this._handleSearchKeyDown.bind(this)
|
|
62
|
+
this._handleSearchKeyDown.bind(this),
|
|
67
63
|
);
|
|
68
64
|
|
|
69
65
|
// Add blur event listener to ensure highlights are cleared when focus is lost
|
|
@@ -96,18 +92,20 @@ export class KTSelectSearch {
|
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
// Listen for dropdown close to reset options - ATTACH TO WRAPPER
|
|
99
|
-
this._select
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
this._select
|
|
96
|
+
.getWrapperElement()
|
|
97
|
+
.addEventListener('dropdown.close', () => {
|
|
98
|
+
this._focusManager.resetFocus();
|
|
99
|
+
// If clearSearchOnClose is false and there's a value, the search term and filtered state should persist.
|
|
100
|
+
// KTSelect's closeDropdown method already calls this._searchModule.clearSearch() (which clears highlights)
|
|
101
|
+
// and conditionally clears the input value based on KTSelect's config.clearSearchOnClose.
|
|
102
|
+
// This listener in search.ts seems to unconditionally clear everything.
|
|
103
|
+
// For now, keeping its original behavior:
|
|
104
|
+
this.clearSearch(); // Clears highlights from current options
|
|
105
|
+
this._searchInput.value = ''; // Clears the search input field
|
|
106
|
+
this._resetAllOptions(); // Shows all options, restores original text, removes highlights
|
|
107
|
+
this._clearNoResultsMessage(); // Clears any "no results" message
|
|
108
|
+
});
|
|
111
109
|
|
|
112
110
|
// Clear highlights when an option is selected - ATTACH TO ORIGINAL SELECT (standard 'change' event)
|
|
113
111
|
this._select.getElement().addEventListener('change', () => {
|
|
@@ -121,30 +119,32 @@ export class KTSelectSearch {
|
|
|
121
119
|
});
|
|
122
120
|
|
|
123
121
|
// Consolidated 'dropdown.show' event listener - ATTACH TO WRAPPER
|
|
124
|
-
this._select
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
122
|
+
this._select
|
|
123
|
+
.getWrapperElement()
|
|
124
|
+
.addEventListener('dropdown.show', () => {
|
|
125
|
+
this._focusManager.resetFocus(); // Always clear previous focus state
|
|
126
|
+
|
|
127
|
+
if (this._searchInput?.value) {
|
|
128
|
+
// If there's an existing search term:
|
|
129
|
+
// 1. Re-filter options. This ensures the display (hidden/visible) is correct
|
|
130
|
+
// and "no results" message is handled if query yields nothing.
|
|
131
|
+
this._filterOptions(this._searchInput.value);
|
|
132
|
+
} else {
|
|
133
|
+
// If search input is empty:
|
|
134
|
+
// 1. Reset all options to their full, unfiltered, original state.
|
|
135
|
+
this._resetAllOptions(); // Shows all, clears highlights from options, restores original text
|
|
136
|
+
// 2. Clear any "no results" message.
|
|
137
|
+
this._clearNoResultsMessage();
|
|
138
|
+
}
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
// Handle autofocus for the search input (this was one of the original separate listeners)
|
|
141
|
+
if (this._select.getConfig().searchAutofocus) {
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
this._searchInput?.focus(); // Focus search input
|
|
144
|
+
}, 50); // Delay to ensure dropdown is visible
|
|
145
|
+
}
|
|
146
|
+
this._select.updateSelectAllButtonState();
|
|
147
|
+
});
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -229,8 +229,10 @@ export class KTSelectSearch {
|
|
|
229
229
|
* This is typically called before applying new filters/highlights.
|
|
230
230
|
*/
|
|
231
231
|
private _restoreOptionContentsBeforeFilter(): void {
|
|
232
|
-
const options = Array.from(
|
|
233
|
-
|
|
232
|
+
const options = Array.from(
|
|
233
|
+
this._select.getOptionsElement(),
|
|
234
|
+
) as HTMLElement[];
|
|
235
|
+
options.forEach((option) => {
|
|
234
236
|
const value = option.getAttribute('data-value');
|
|
235
237
|
if (value && this._originalOptionContents.has(value)) {
|
|
236
238
|
const originalContent = this._originalOptionContents.get(value)!;
|
|
@@ -295,8 +297,12 @@ export class KTSelectSearch {
|
|
|
295
297
|
// Restore original content before filtering, so highlighting is applied fresh.
|
|
296
298
|
this._restoreOptionContentsBeforeFilter();
|
|
297
299
|
|
|
298
|
-
const visibleCount = filterOptions(
|
|
299
|
-
|
|
300
|
+
const visibleCount = filterOptions(
|
|
301
|
+
options,
|
|
302
|
+
query,
|
|
303
|
+
config,
|
|
304
|
+
dropdownElement,
|
|
305
|
+
(count) => this._handleNoResults(count),
|
|
300
306
|
);
|
|
301
307
|
|
|
302
308
|
this._select.updateSelectAllButtonState();
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
.kt-select-search-empty
|
|
63
|
+
.kt-select-search-empty {
|
|
64
64
|
@apply px-3.5 py-1 text-sm text-muted-foreground;
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -92,12 +92,13 @@
|
|
|
92
92
|
@apply grow flex items-center gap-2.5 py-1.75 px-2.5 rounded-md text-sm text-start cursor-pointer;
|
|
93
93
|
@apply hover:bg-accent hover:text-accent-foreground;
|
|
94
94
|
|
|
95
|
-
&[aria-disabled=true],
|
|
95
|
+
&[aria-disabled='true'],
|
|
96
96
|
&.disabled {
|
|
97
97
|
@apply opacity-60 pointer-events-none;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
&.selected {
|
|
100
|
+
&.selected {
|
|
101
|
+
}
|
|
101
102
|
|
|
102
103
|
&.focused,
|
|
103
104
|
&.hover,
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
@apply text-muted-foreground overflow-ellipsis truncate;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
.kt-select-display:not([data-multiple=true]) {
|
|
126
|
+
.kt-select-display:not([data-multiple='true']) {
|
|
126
127
|
@apply w-full overflow-ellipsis truncate;
|
|
127
128
|
}
|
|
128
129
|
}
|
|
@@ -133,7 +134,7 @@
|
|
|
133
134
|
@apply h-8.5 gap-1 ps-3 pe-6 text-[0.8125rem] leading-(--text-sm--line-height) rounded-md;
|
|
134
135
|
background-position: right 0.5rem center;
|
|
135
136
|
|
|
136
|
-
&[data-multiple=true] {
|
|
137
|
+
&[data-multiple='true'] {
|
|
137
138
|
@apply h-auto min-h-8.5 py-1.5 flex-wrap;
|
|
138
139
|
background-position: right 0.5rem top 0.675rem;
|
|
139
140
|
}
|
|
@@ -143,7 +144,7 @@
|
|
|
143
144
|
@apply h-7 gap-1 ps-2.5 pe-5 text-xs rounded-md;
|
|
144
145
|
background-position: right 0.5rem center;
|
|
145
146
|
|
|
146
|
-
&[data-multiple=true] {
|
|
147
|
+
&[data-multiple='true'] {
|
|
147
148
|
@apply h-auto min-h-7;
|
|
148
149
|
background-position: right 0.5rem top 0.575rem;
|
|
149
150
|
}
|
|
@@ -153,7 +154,7 @@
|
|
|
153
154
|
@apply h-10 gap-1.5 ps-4 pe-8 py-1 text-sm rounded-md;
|
|
154
155
|
background-position: right 0.6rem center;
|
|
155
156
|
|
|
156
|
-
&[data-multiple=true] {
|
|
157
|
+
&[data-multiple='true'] {
|
|
157
158
|
@apply h-auto min-h-10 py-2;
|
|
158
159
|
background-position: right 0.6rem top 0.85rem;
|
|
159
160
|
}
|
|
@@ -166,7 +167,7 @@
|
|
|
166
167
|
.kt-select {
|
|
167
168
|
background-position: left 0.5rem center;
|
|
168
169
|
|
|
169
|
-
&[data-multiple=true] {
|
|
170
|
+
&[data-multiple='true'] {
|
|
170
171
|
background-position: left 0.5rem top 0.675rem;
|
|
171
172
|
}
|
|
172
173
|
}
|
|
@@ -174,7 +175,7 @@
|
|
|
174
175
|
.kt-select-sm {
|
|
175
176
|
background-position: left 0.5rem center;
|
|
176
177
|
|
|
177
|
-
&[data-multiple=true] {
|
|
178
|
+
&[data-multiple='true'] {
|
|
178
179
|
background-position: left 0.5rem top 0.575rem;
|
|
179
180
|
}
|
|
180
181
|
}
|
|
@@ -182,7 +183,7 @@
|
|
|
182
183
|
.kt-select-lg {
|
|
183
184
|
background-position: left 0.75rem center;
|
|
184
185
|
|
|
185
|
-
&[data-multiple=true] {
|
|
186
|
+
&[data-multiple='true'] {
|
|
186
187
|
background-position: left 0.75rem top 0.85rem;
|
|
187
188
|
}
|
|
188
189
|
}
|
|
@@ -235,4 +236,4 @@
|
|
|
235
236
|
[data-kt-select-option]:hover & {
|
|
236
237
|
@slot;
|
|
237
238
|
}
|
|
238
|
-
}
|
|
239
|
+
}
|