@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
|
@@ -499,7 +499,11 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
499
499
|
// Set search value
|
|
500
500
|
if (searchElement) {
|
|
501
501
|
searchElement.value =
|
|
502
|
-
search === undefined || search === null
|
|
502
|
+
search === undefined || search === null
|
|
503
|
+
? ''
|
|
504
|
+
: typeof search === 'string'
|
|
505
|
+
? search
|
|
506
|
+
: String(search);
|
|
503
507
|
}
|
|
504
508
|
|
|
505
509
|
if (searchElement) {
|
|
@@ -834,7 +838,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
834
838
|
private _createUrl(
|
|
835
839
|
pathOrUrl: string,
|
|
836
840
|
baseUrl: string | null = window.location.origin,
|
|
837
|
-
)
|
|
841
|
+
): URL {
|
|
838
842
|
// Regular expression to check if the input is a full URL
|
|
839
843
|
const isFullUrl = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(pathOrUrl);
|
|
840
844
|
|
|
@@ -988,10 +992,12 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
988
992
|
|
|
989
993
|
if (typeof columnDef.render === 'function') {
|
|
990
994
|
const result = columnDef.render.call(this, item[key], item, this);
|
|
991
|
-
if (
|
|
995
|
+
if (
|
|
996
|
+
result instanceof HTMLElement ||
|
|
997
|
+
result instanceof DocumentFragment
|
|
998
|
+
) {
|
|
992
999
|
td.appendChild(result);
|
|
993
|
-
}
|
|
994
|
-
else if (typeof result === 'string') {
|
|
1000
|
+
} else if (typeof result === 'string') {
|
|
995
1001
|
td.innerHTML = result as string;
|
|
996
1002
|
}
|
|
997
1003
|
} else {
|
|
@@ -1423,77 +1429,80 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1423
1429
|
return id;
|
|
1424
1430
|
}
|
|
1425
1431
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1432
|
+
/**
|
|
1433
|
+
* Clean up all event listeners, handlers, and DOM nodes created by this instance.
|
|
1434
|
+
* This method is called before re-rendering or when disposing the component.
|
|
1435
|
+
*/
|
|
1436
|
+
private _dispose() {
|
|
1437
|
+
// --- 1. Remove search input event listener (debounced) ---
|
|
1438
|
+
const tableId: string = this._tableId();
|
|
1439
|
+
const searchElement: HTMLInputElement | null =
|
|
1440
|
+
document.querySelector<HTMLInputElement>(
|
|
1441
|
+
`[data-kt-datatable-search="#${tableId}"]`,
|
|
1442
|
+
);
|
|
1443
|
+
if (searchElement && (searchElement as any)._debouncedSearch) {
|
|
1444
|
+
searchElement.removeEventListener(
|
|
1445
|
+
'keyup',
|
|
1446
|
+
(searchElement as any)._debouncedSearch,
|
|
1447
|
+
);
|
|
1448
|
+
delete (searchElement as any)._debouncedSearch;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// --- 2. Remove page size dropdown event listener ---
|
|
1452
|
+
if (this._sizeElement && this._sizeElement.onchange) {
|
|
1453
|
+
this._sizeElement.onchange = null;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// --- 3. Remove all pagination button event listeners ---
|
|
1457
|
+
if (this._paginationElement) {
|
|
1458
|
+
// Remove all child nodes (buttons) to ensure no lingering listeners
|
|
1459
|
+
while (this._paginationElement.firstChild) {
|
|
1460
|
+
this._paginationElement.removeChild(this._paginationElement.firstChild);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// --- 4. Dispose of handler objects (checkbox, sort) ---
|
|
1465
|
+
// KTDataTableCheckboxAPI does not have a dispose method, but we can remove header checkbox listener
|
|
1466
|
+
if (
|
|
1467
|
+
this._checkbox &&
|
|
1468
|
+
typeof (this._checkbox as any).dispose === 'function'
|
|
1469
|
+
) {
|
|
1470
|
+
(this._checkbox as any).dispose();
|
|
1471
|
+
} else {
|
|
1472
|
+
// Remove header checkbox event listener if possible
|
|
1473
|
+
const headerCheckElement = this._element.querySelector<HTMLInputElement>(
|
|
1474
|
+
this._config.attributes.check,
|
|
1475
|
+
);
|
|
1476
|
+
if (headerCheckElement) {
|
|
1477
|
+
headerCheckElement.replaceWith(headerCheckElement.cloneNode(true));
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
// KTDataTableSortAPI does not have a dispose method, but we can remove th click listeners by replacing them
|
|
1481
|
+
if (this._theadElement) {
|
|
1482
|
+
const ths = this._theadElement.querySelectorAll('th');
|
|
1483
|
+
ths.forEach((th) => {
|
|
1484
|
+
th.replaceWith(th.cloneNode(true));
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// --- 5. Remove spinner DOM node if it exists ---
|
|
1489
|
+
const spinner = this._element.querySelector<HTMLElement>(
|
|
1490
|
+
this._config.attributes.spinner,
|
|
1491
|
+
);
|
|
1492
|
+
if (spinner && spinner.parentNode) {
|
|
1493
|
+
spinner.parentNode.removeChild(spinner);
|
|
1494
|
+
}
|
|
1495
|
+
this._element.classList.remove(this._config.loadingClass);
|
|
1496
|
+
|
|
1497
|
+
// --- 6. Remove instance reference from the DOM element ---
|
|
1498
|
+
if ((this._element as any).instance) {
|
|
1499
|
+
delete (this._element as any).instance;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// --- 7. (Optional) Clear localStorage state ---
|
|
1503
|
+
// Uncomment the following line if you want to clear state on dispose:
|
|
1504
|
+
// this._deleteState();
|
|
1505
|
+
}
|
|
1497
1506
|
|
|
1498
1507
|
private _debounce(func: Function, wait: number) {
|
|
1499
1508
|
let timeout: number | undefined;
|
|
@@ -1770,9 +1779,9 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1770
1779
|
*/
|
|
1771
1780
|
|
|
1772
1781
|
export function initAllDataTables(): void {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1782
|
+
if (typeof document !== 'undefined') {
|
|
1783
|
+
KTDataTable.createInstances();
|
|
1784
|
+
// Optionally assign to window for legacy support
|
|
1785
|
+
window.KTDataTable = KTDataTable;
|
|
1786
|
+
}
|
|
1778
1787
|
}
|
|
@@ -26,6 +26,7 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
26
26
|
protected _previewElement: HTMLElement;
|
|
27
27
|
protected _previewUrl: string = '';
|
|
28
28
|
protected _lastMode: string;
|
|
29
|
+
protected _selectedFile: File | null = null;
|
|
29
30
|
|
|
30
31
|
constructor(
|
|
31
32
|
element: HTMLElement,
|
|
@@ -87,6 +88,7 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
87
88
|
};
|
|
88
89
|
|
|
89
90
|
reader.readAsDataURL(this._inputElement.files[0]);
|
|
91
|
+
this._selectedFile = this._inputElement.files[0];
|
|
90
92
|
this._inputElement.value = '';
|
|
91
93
|
this._hiddenElement.value = '';
|
|
92
94
|
this._lastMode = 'new';
|
|
@@ -125,6 +127,7 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
125
127
|
|
|
126
128
|
this._inputElement.value = '';
|
|
127
129
|
this._hiddenElement.value = '';
|
|
130
|
+
this._selectedFile = null;
|
|
128
131
|
|
|
129
132
|
this._lastMode = 'saved';
|
|
130
133
|
} else if (this._lastMode == 'saved') {
|
|
@@ -138,6 +141,7 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
138
141
|
|
|
139
142
|
this._hiddenElement.value = '1';
|
|
140
143
|
this._inputElement.value = '';
|
|
144
|
+
this._selectedFile = null;
|
|
141
145
|
|
|
142
146
|
this._lastMode = 'placeholder';
|
|
143
147
|
} else if (this._lastMode == 'placeholder') {
|
|
@@ -154,6 +158,7 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
154
158
|
|
|
155
159
|
this._inputElement.value = '';
|
|
156
160
|
this._hiddenElement.value = '';
|
|
161
|
+
this._selectedFile = null;
|
|
157
162
|
|
|
158
163
|
this._lastMode = 'saved';
|
|
159
164
|
}
|
|
@@ -187,11 +192,11 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
public isEmpty(): boolean {
|
|
190
|
-
return this.
|
|
195
|
+
return this._selectedFile === null;
|
|
191
196
|
}
|
|
192
197
|
|
|
193
198
|
public isChanged(): boolean {
|
|
194
|
-
return this.
|
|
199
|
+
return this._selectedFile !== null;
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
public remove(): void {
|
|
@@ -210,6 +215,10 @@ export class KTImageInput extends KTComponent implements KTImageInputInterface {
|
|
|
210
215
|
return this._getPreviewUrl();
|
|
211
216
|
}
|
|
212
217
|
|
|
218
|
+
public getSelectedFile(): File | null {
|
|
219
|
+
return this._selectedFile;
|
|
220
|
+
}
|
|
221
|
+
|
|
213
222
|
public static getInstance(element: HTMLElement): KTImageInput {
|
|
214
223
|
if (!element) return null;
|
|
215
224
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
.kt-input {
|
|
10
10
|
@apply outline-0 block w-full bg-background border border-input shadow-xs shadow-[rgba(0,0,0,0.05)] transition-[color,box-shadow] text-foreground placeholder:text-muted-foreground;
|
|
11
11
|
@apply focus-visible:ring-ring/30 focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px];
|
|
12
|
-
@apply disabled:cursor-not-allowed disabled:opacity-60;
|
|
12
|
+
@apply disabled:cursor-not-allowed disabled:opacity-60;
|
|
13
13
|
@apply [&[readonly]]:bg-muted/80 [&[readonly]]:cursor-not-allowed [&[readonly]]:text-secondary-foreground/80;
|
|
14
14
|
@apply file:h-full [&[type=file]]:py-0;
|
|
15
15
|
@apply file:border-solid file:border-input file:bg-transparent file:font-medium file:not-italic file:text-foreground file:p-0 file:border-0 file:border-e;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
:root {
|
|
8
8
|
--kt-scrollable-scrollbar-size: 5px;
|
|
9
9
|
--kt-scrollable-thumb-color: var(--color-accent);
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
.kt-scrollable::-webkit-scrollbar {
|
|
13
13
|
width: var(--kt-scrollable-scrollbar-size);
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
.kt-scrollable::-webkit-scrollbar-thumb {
|
|
22
22
|
background: var(--kt-scrollable-thumb-color);
|
|
23
23
|
border-radius: var(--kt-scrollable-scrollbar-size);
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
|
|
26
26
|
/* Firefox */
|
|
27
|
-
@-moz-document url-prefix() {
|
|
27
|
+
@-moz-document url-prefix() {
|
|
28
28
|
.kt-scrollable {
|
|
29
29
|
scrollbar-width: thin;
|
|
30
30
|
scrollbar-color: var(--kt-scrollable-thumb-color) transparent;
|
|
@@ -26,9 +26,15 @@ export class KTSelectCombobox {
|
|
|
26
26
|
|
|
27
27
|
const displayElement = select.getDisplayElement(); // KTSelect's main display element for combobox
|
|
28
28
|
|
|
29
|
-
this._searchInputElement = displayElement.querySelector(
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
this._searchInputElement = displayElement.querySelector(
|
|
30
|
+
'input[data-kt-select-search]',
|
|
31
|
+
);
|
|
32
|
+
this._clearButtonElement = displayElement.querySelector(
|
|
33
|
+
'[data-kt-select-clear-button]',
|
|
34
|
+
);
|
|
35
|
+
this._valuesContainerElement = displayElement.querySelector(
|
|
36
|
+
'[data-kt-select-combobox-values]',
|
|
37
|
+
);
|
|
32
38
|
|
|
33
39
|
this._boundInputHandler = this._handleComboboxInput.bind(this);
|
|
34
40
|
this._boundClearHandler = this._handleClearButtonClick.bind(this);
|
|
@@ -42,7 +48,7 @@ export class KTSelectCombobox {
|
|
|
42
48
|
this.updateDisplay(this._select.getSelectedOptions());
|
|
43
49
|
} else {
|
|
44
50
|
// For tags or displayTemplate, the input should be clear for typing.
|
|
45
|
-
|
|
51
|
+
this._searchInputElement.value = '';
|
|
46
52
|
}
|
|
47
53
|
this._toggleClearButtonVisibility(this._searchInputElement.value);
|
|
48
54
|
// this._select.showAllOptions(); // showAllOptions might be too broad, filtering is managed by typing.
|
|
@@ -56,11 +62,18 @@ export class KTSelectCombobox {
|
|
|
56
62
|
*/
|
|
57
63
|
private _attachEventListeners(): void {
|
|
58
64
|
this._removeEventListeners();
|
|
59
|
-
if (this._searchInputElement) {
|
|
60
|
-
|
|
65
|
+
if (this._searchInputElement) {
|
|
66
|
+
// Ensure element exists
|
|
67
|
+
this._searchInputElement.addEventListener(
|
|
68
|
+
'input',
|
|
69
|
+
this._boundInputHandler,
|
|
70
|
+
);
|
|
61
71
|
}
|
|
62
72
|
if (this._clearButtonElement) {
|
|
63
|
-
this._clearButtonElement.addEventListener(
|
|
73
|
+
this._clearButtonElement.addEventListener(
|
|
74
|
+
'click',
|
|
75
|
+
this._boundClearHandler,
|
|
76
|
+
);
|
|
64
77
|
}
|
|
65
78
|
}
|
|
66
79
|
|
|
@@ -69,10 +82,16 @@ export class KTSelectCombobox {
|
|
|
69
82
|
*/
|
|
70
83
|
private _removeEventListeners(): void {
|
|
71
84
|
if (this._searchInputElement) {
|
|
72
|
-
this._searchInputElement.removeEventListener(
|
|
85
|
+
this._searchInputElement.removeEventListener(
|
|
86
|
+
'input',
|
|
87
|
+
this._boundInputHandler,
|
|
88
|
+
);
|
|
73
89
|
}
|
|
74
90
|
if (this._clearButtonElement) {
|
|
75
|
-
this._clearButtonElement.removeEventListener(
|
|
91
|
+
this._clearButtonElement.removeEventListener(
|
|
92
|
+
'click',
|
|
93
|
+
this._boundClearHandler,
|
|
94
|
+
);
|
|
76
95
|
}
|
|
77
96
|
}
|
|
78
97
|
|
|
@@ -85,7 +104,8 @@ export class KTSelectCombobox {
|
|
|
85
104
|
|
|
86
105
|
this._toggleClearButtonVisibility(query);
|
|
87
106
|
|
|
88
|
-
if (!(this._select as any).isDropdownOpen()) {
|
|
107
|
+
if (!(this._select as any).isDropdownOpen()) {
|
|
108
|
+
// Use public getter
|
|
89
109
|
this._select.openDropdown();
|
|
90
110
|
}
|
|
91
111
|
// For single select without displayTemplate, if user types, they are essentially clearing the previous selection text
|
|
@@ -127,7 +147,11 @@ export class KTSelectCombobox {
|
|
|
127
147
|
if (!this._clearButtonElement) return;
|
|
128
148
|
const hasSelectedItems = this._select.getSelectedOptions().length > 0;
|
|
129
149
|
|
|
130
|
-
if (
|
|
150
|
+
if (
|
|
151
|
+
inputValue.length > 0 ||
|
|
152
|
+
(hasSelectedItems &&
|
|
153
|
+
(this._config.multiple || this._config.displayTemplate))
|
|
154
|
+
) {
|
|
131
155
|
this._clearButtonElement.classList.remove('hidden');
|
|
132
156
|
} else {
|
|
133
157
|
this._clearButtonElement.classList.add('hidden');
|
|
@@ -138,7 +162,9 @@ export class KTSelectCombobox {
|
|
|
138
162
|
* Filter options for combobox based on input query
|
|
139
163
|
*/
|
|
140
164
|
private _filterOptionsForCombobox(query: string): void {
|
|
141
|
-
const options = Array.from(
|
|
165
|
+
const options = Array.from(
|
|
166
|
+
this._select.getOptionsElement(),
|
|
167
|
+
) as HTMLElement[];
|
|
142
168
|
const config = this._select.getConfig();
|
|
143
169
|
const dropdownElement = this._select.getDropdownElement();
|
|
144
170
|
filterOptions(options, query, config, dropdownElement);
|
|
@@ -160,41 +186,65 @@ export class KTSelectCombobox {
|
|
|
160
186
|
this._valuesContainerElement.innerHTML = '';
|
|
161
187
|
}
|
|
162
188
|
|
|
163
|
-
if (this._config.tags && this._valuesContainerElement) {
|
|
164
|
-
|
|
189
|
+
if (this._config.tags && this._valuesContainerElement) {
|
|
190
|
+
// Combobox + Tags
|
|
191
|
+
selectedOptions.forEach((value) => {
|
|
165
192
|
// Ensure value is properly escaped for querySelector
|
|
166
|
-
const optionElement = this._select
|
|
193
|
+
const optionElement = this._select
|
|
194
|
+
.getElement()
|
|
195
|
+
.querySelector(
|
|
196
|
+
`option[value="${CSS.escape(value)}"]`,
|
|
197
|
+
) as HTMLOptionElement;
|
|
167
198
|
if (optionElement) {
|
|
168
199
|
const tagElement = defaultTemplates.tag(optionElement, this._config);
|
|
169
200
|
this._valuesContainerElement.appendChild(tagElement);
|
|
170
201
|
}
|
|
171
202
|
});
|
|
172
203
|
this._searchInputElement.value = ''; // Input field is for typing new searches
|
|
173
|
-
this._searchInputElement.placeholder =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
204
|
+
this._searchInputElement.placeholder =
|
|
205
|
+
selectedOptions.length > 0
|
|
206
|
+
? ''
|
|
207
|
+
: this._config.placeholder || 'Select...';
|
|
208
|
+
} else if (this._config.displayTemplate && this._valuesContainerElement) {
|
|
209
|
+
// Combobox + DisplayTemplate (no tags)
|
|
210
|
+
this._valuesContainerElement.innerHTML =
|
|
211
|
+
this._select.renderDisplayTemplateForSelected(selectedOptions);
|
|
177
212
|
this._searchInputElement.value = ''; // Input field is for typing new searches
|
|
178
|
-
this._searchInputElement.placeholder =
|
|
179
|
-
|
|
180
|
-
|
|
213
|
+
this._searchInputElement.placeholder =
|
|
214
|
+
selectedOptions.length > 0
|
|
215
|
+
? ''
|
|
216
|
+
: this._config.placeholder || 'Select...';
|
|
217
|
+
} else if (this._config.multiple && this._valuesContainerElement) {
|
|
218
|
+
// Combobox + Multiple (no tags, no display template)
|
|
181
219
|
// For simplicity, join text. A proper tag implementation would be more complex here.
|
|
182
|
-
this._valuesContainerElement.innerHTML = selectedOptions
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
this._valuesContainerElement.innerHTML = selectedOptions
|
|
221
|
+
.map((value) => {
|
|
222
|
+
const optionEl = this._select
|
|
223
|
+
.getElement()
|
|
224
|
+
.querySelector(`option[value="${CSS.escape(value)}"]`);
|
|
225
|
+
return optionEl ? optionEl.textContent : '';
|
|
226
|
+
})
|
|
227
|
+
.join(', '); // Basic comma separation
|
|
186
228
|
this._searchInputElement.value = '';
|
|
187
|
-
this._searchInputElement.placeholder =
|
|
188
|
-
|
|
189
|
-
|
|
229
|
+
this._searchInputElement.placeholder =
|
|
230
|
+
selectedOptions.length > 0
|
|
231
|
+
? ''
|
|
232
|
+
: this._config.placeholder || 'Select...';
|
|
233
|
+
} else if (!this._config.multiple && selectedOptions.length > 0) {
|
|
234
|
+
// Single select combobox: display selected option's text in the input
|
|
190
235
|
const selectedValue = selectedOptions[0];
|
|
191
|
-
const optionElement = this._select
|
|
192
|
-
|
|
236
|
+
const optionElement = this._select
|
|
237
|
+
.getElement()
|
|
238
|
+
.querySelector(`option[value="${CSS.escape(selectedValue)}"]`);
|
|
239
|
+
this._searchInputElement.value = optionElement
|
|
240
|
+
? optionElement.textContent || ''
|
|
241
|
+
: '';
|
|
193
242
|
// placeholder is implicitly handled by input value for single select
|
|
194
|
-
|
|
195
|
-
|
|
243
|
+
} else {
|
|
244
|
+
// No selection or not fitting above categories (e.g. single select, no items)
|
|
196
245
|
this._searchInputElement.value = '';
|
|
197
|
-
this._searchInputElement.placeholder =
|
|
246
|
+
this._searchInputElement.placeholder =
|
|
247
|
+
this._config.placeholder || 'Select...';
|
|
198
248
|
// _valuesContainerElement is already cleared if it exists
|
|
199
249
|
}
|
|
200
250
|
this._toggleClearButtonVisibility(this._searchInputElement.value);
|
|
@@ -106,9 +106,7 @@ export class KTSelectDropdown extends KTComponent {
|
|
|
106
106
|
|
|
107
107
|
if (this._config.disabled) {
|
|
108
108
|
if (this._config.debug)
|
|
109
|
-
console.log(
|
|
110
|
-
'KTSelectDropdown._handleToggleClick: select is disabled',
|
|
111
|
-
);
|
|
109
|
+
console.log('KTSelectDropdown._handleToggleClick: select is disabled');
|
|
112
110
|
return;
|
|
113
111
|
}
|
|
114
112
|
|
|
@@ -251,9 +249,7 @@ export class KTSelectDropdown extends KTComponent {
|
|
|
251
249
|
public open(): void {
|
|
252
250
|
if (this._config.disabled) {
|
|
253
251
|
if (this._config.debug)
|
|
254
|
-
console.log(
|
|
255
|
-
'KTSelectDropdown.open: select is disabled, not opening',
|
|
256
|
-
);
|
|
252
|
+
console.log('KTSelectDropdown.open: select is disabled, not opening');
|
|
257
253
|
return;
|
|
258
254
|
}
|
|
259
255
|
if (this._isOpen || this._isTransitioning) return;
|
|
@@ -279,17 +275,26 @@ export class KTSelectDropdown extends KTComponent {
|
|
|
279
275
|
}
|
|
280
276
|
|
|
281
277
|
// Consider the dropdown's current z-index if it's already set and higher
|
|
282
|
-
const currentDropdownZIndexStr = KTDom.getCssProp(
|
|
278
|
+
const currentDropdownZIndexStr = KTDom.getCssProp(
|
|
279
|
+
this._dropdownElement,
|
|
280
|
+
'z-index',
|
|
281
|
+
);
|
|
283
282
|
if (currentDropdownZIndexStr && currentDropdownZIndexStr !== 'auto') {
|
|
284
283
|
const currentDropdownZIndex = parseInt(currentDropdownZIndexStr);
|
|
285
|
-
if (
|
|
284
|
+
if (
|
|
285
|
+
!isNaN(currentDropdownZIndex) &&
|
|
286
|
+
currentDropdownZIndex > (zIndexToApply || 0)
|
|
287
|
+
) {
|
|
286
288
|
zIndexToApply = currentDropdownZIndex;
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
// Ensure dropdown is above elements within its original toggle's parent context
|
|
291
293
|
const toggleParentContextZindex = KTDom.getHighestZindex(this._element); // _element is the select wrapper
|
|
292
|
-
if (
|
|
294
|
+
if (
|
|
295
|
+
toggleParentContextZindex !== null &&
|
|
296
|
+
toggleParentContextZindex >= (zIndexToApply || 0)
|
|
297
|
+
) {
|
|
293
298
|
zIndexToApply = toggleParentContextZindex + 1;
|
|
294
299
|
}
|
|
295
300
|
|
|
@@ -379,7 +384,9 @@ export class KTSelectDropdown extends KTComponent {
|
|
|
379
384
|
|
|
380
385
|
KTDom.transitionEnd(this._dropdownElement, completeTransition);
|
|
381
386
|
|
|
382
|
-
if (
|
|
387
|
+
if (
|
|
388
|
+
KTDom.getCssProp(this._dropdownElement, 'transition-duration') === '0s'
|
|
389
|
+
) {
|
|
383
390
|
completeTransition();
|
|
384
391
|
}
|
|
385
392
|
}
|
|
@@ -422,7 +429,9 @@ export class KTSelectDropdown extends KTComponent {
|
|
|
422
429
|
private _resolveDropdownContainer(): HTMLElement | null {
|
|
423
430
|
const containerSelector = this._config.dropdownContainer;
|
|
424
431
|
if (containerSelector && containerSelector !== 'body') {
|
|
425
|
-
const containerElement = document.querySelector(
|
|
432
|
+
const containerElement = document.querySelector(
|
|
433
|
+
containerSelector,
|
|
434
|
+
) as HTMLElement | null;
|
|
426
435
|
if (!containerElement && this._config.debug) {
|
|
427
436
|
console.warn(
|
|
428
437
|
`KTSelectDropdown: dropdownContainer selector "${containerSelector}" not found. Dropdown will remain in its default position.`,
|
|
@@ -9,5 +9,10 @@ export { KTSelectCombobox } from './combobox';
|
|
|
9
9
|
export { KTSelectSearch } from './search';
|
|
10
10
|
export { KTSelectTags } from './tags';
|
|
11
11
|
export { KTSelectDropdown } from './dropdown';
|
|
12
|
-
export {
|
|
12
|
+
export {
|
|
13
|
+
filterOptions,
|
|
14
|
+
FocusManager,
|
|
15
|
+
EventManager,
|
|
16
|
+
TypeToSearchBuffer,
|
|
17
|
+
} from './utils';
|
|
13
18
|
export { KTSelectConfigInterface, KTSelectOption } from './config';
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import KTComponent from '../component';
|
|
7
|
-
import {
|
|
8
|
-
KTSelectConfigInterface,
|
|
9
|
-
} from './config';
|
|
7
|
+
import { KTSelectConfigInterface } from './config';
|
|
10
8
|
import { defaultTemplates } from './templates';
|
|
11
9
|
|
|
12
10
|
export class KTSelectOption extends KTComponent {
|
|
@@ -15,7 +13,7 @@ export class KTSelectOption extends KTComponent {
|
|
|
15
13
|
protected override readonly _config: KTSelectConfigInterface; // Holds option-specific data from data-kt-*
|
|
16
14
|
private _globalConfig: KTSelectConfigInterface; // Main select's config
|
|
17
15
|
|
|
18
|
-
constructor(element: HTMLElement, config?: KTSelectConfigInterface
|
|
16
|
+
constructor(element: HTMLElement, config?: KTSelectConfigInterface) {
|
|
19
17
|
super();
|
|
20
18
|
|
|
21
19
|
// Always initialize a new option instance
|
|
@@ -30,13 +28,16 @@ export class KTSelectOption extends KTComponent {
|
|
|
30
28
|
// Ensure optionsConfig is initialized
|
|
31
29
|
if (this._globalConfig) {
|
|
32
30
|
this._globalConfig.optionsConfig = this._globalConfig.optionsConfig || {};
|
|
33
|
-
this._globalConfig.optionsConfig[(element as HTMLInputElement).value] =
|
|
31
|
+
this._globalConfig.optionsConfig[(element as HTMLInputElement).value] =
|
|
32
|
+
this._config;
|
|
34
33
|
// console.log('[KTSelectOption] Populating _globalConfig.optionsConfig for value', (element as HTMLInputElement).value, 'with:', JSON.parse(JSON.stringify(this._config)));
|
|
35
34
|
// console.log('[KTSelectOption] _globalConfig.optionsConfig is now:', JSON.parse(JSON.stringify(this._globalConfig.optionsConfig)));
|
|
36
35
|
} else {
|
|
37
36
|
// Handle case where _globalConfig might be undefined, though constructor expects it.
|
|
38
37
|
// This might indicate a need to ensure config is always passed or has a default.
|
|
39
|
-
console.warn(
|
|
38
|
+
console.warn(
|
|
39
|
+
'KTSelectOption: _globalConfig is undefined during constructor.',
|
|
40
|
+
);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// Don't store in KTData to avoid Singleton pattern issues
|