@keenthemes/ktui 1.1.4 → 1.1.5
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 +174 -49
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/lib/cjs/components/component.js +9 -0
- package/lib/cjs/components/component.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +80 -10
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +36 -5
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/drawer/drawer.js +43 -34
- package/lib/cjs/components/drawer/drawer.js.map +1 -1
- package/lib/cjs/components/dropdown/dropdown.js +6 -0
- package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
- package/lib/cjs/components/select/select.js +7 -4
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/esm/components/component.js +9 -0
- package/lib/esm/components/component.js.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +80 -10
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +36 -5
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/drawer/drawer.js +43 -34
- package/lib/esm/components/drawer/drawer.js.map +1 -1
- package/lib/esm/components/dropdown/dropdown.js +6 -0
- package/lib/esm/components/dropdown/dropdown.js.map +1 -1
- package/lib/esm/components/select/select.js +7 -4
- package/lib/esm/components/select/select.js.map +1 -1
- package/package.json +1 -1
- package/src/components/component.ts +10 -0
- package/src/components/datatable/datatable-sort.ts +89 -7
- package/src/components/datatable/datatable.ts +44 -13
- package/src/components/datatable/types.ts +14 -0
- package/src/components/drawer/drawer.ts +38 -35
- package/src/components/drawer/types.ts +4 -2
- package/src/components/dropdown/dropdown.ts +5 -0
|
@@ -44,7 +44,7 @@ export function createSortHandler<T = KTDataTableDataInterface>(
|
|
|
44
44
|
dispatchEvent: (eventName: string, eventData?: any) => void,
|
|
45
45
|
updateData: () => void,
|
|
46
46
|
): KTDataTableSortAPI<T> {
|
|
47
|
-
// Helper to compare values for sorting
|
|
47
|
+
// Helper to compare values for sorting (string)
|
|
48
48
|
function compareValues(
|
|
49
49
|
a: unknown,
|
|
50
50
|
b: unknown,
|
|
@@ -63,15 +63,80 @@ export function createSortHandler<T = KTDataTableDataInterface>(
|
|
|
63
63
|
: 0;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Parse value for numeric sort: strip currency/commas, then parseFloat
|
|
67
|
+
function parseNumeric(value: unknown): number {
|
|
68
|
+
if (value === null || value === undefined || value === '') {
|
|
69
|
+
return Number.NaN;
|
|
70
|
+
}
|
|
71
|
+
const s = String(value).replace(/[^0-9.-]/g, '');
|
|
72
|
+
const n = parseFloat(s);
|
|
73
|
+
return Number.isNaN(n) ? Number.NaN : n;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Compare two numbers; NaN sorts to the end for both asc and desc
|
|
77
|
+
function compareNumeric(
|
|
78
|
+
aNum: number,
|
|
79
|
+
bNum: number,
|
|
80
|
+
sortOrder: KTDataTableSortOrderInterface,
|
|
81
|
+
): number {
|
|
82
|
+
const aNaN = Number.isNaN(aNum);
|
|
83
|
+
const bNaN = Number.isNaN(bNum);
|
|
84
|
+
if (aNaN && bNaN) return 0;
|
|
85
|
+
if (aNaN) return 1;
|
|
86
|
+
if (bNaN) return -1;
|
|
87
|
+
if (aNum < bNum) return sortOrder === 'asc' ? -1 : 1;
|
|
88
|
+
if (aNum > bNum) return sortOrder === 'asc' ? 1 : -1;
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getColumnDef(
|
|
93
|
+
sortField: keyof T | number,
|
|
94
|
+
): {
|
|
95
|
+
sortType?: 'string' | 'numeric';
|
|
96
|
+
sortValue?: (
|
|
97
|
+
cellValue: unknown,
|
|
98
|
+
rowData: KTDataTableDataInterface,
|
|
99
|
+
) => number | string;
|
|
100
|
+
} | undefined {
|
|
101
|
+
const columns = config.columns;
|
|
102
|
+
if (!columns) return undefined;
|
|
103
|
+
const key =
|
|
104
|
+
typeof sortField === 'number'
|
|
105
|
+
? (Object.keys(columns)[sortField] as keyof T | undefined)
|
|
106
|
+
: sortField;
|
|
107
|
+
return key !== undefined ? columns[key as string] : undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
66
110
|
function sortData(
|
|
67
111
|
data: T[],
|
|
68
112
|
sortField: keyof T | number,
|
|
69
113
|
sortOrder: KTDataTableSortOrderInterface,
|
|
70
114
|
): T[] {
|
|
115
|
+
const columnDef = getColumnDef(sortField);
|
|
116
|
+
const sortValueFn = columnDef?.sortValue;
|
|
117
|
+
const useNumeric =
|
|
118
|
+
!sortValueFn && columnDef?.sortType === 'numeric';
|
|
119
|
+
|
|
71
120
|
return data.sort((a, b) => {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
121
|
+
const aRaw = a[sortField as keyof T] as unknown;
|
|
122
|
+
const bRaw = b[sortField as keyof T] as unknown;
|
|
123
|
+
|
|
124
|
+
if (typeof sortValueFn === 'function') {
|
|
125
|
+
const aVal = sortValueFn(aRaw, a as KTDataTableDataInterface);
|
|
126
|
+
const bVal = sortValueFn(bRaw, b as KTDataTableDataInterface);
|
|
127
|
+
const aNum = typeof aVal === 'number' ? aVal : parseNumeric(aVal);
|
|
128
|
+
const bNum = typeof bVal === 'number' ? bVal : parseNumeric(bVal);
|
|
129
|
+
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
130
|
+
return compareNumeric(aNum, bNum, sortOrder);
|
|
131
|
+
}
|
|
132
|
+
return compareValues(aVal, bVal, sortOrder);
|
|
133
|
+
}
|
|
134
|
+
if (useNumeric) {
|
|
135
|
+
const aNum = parseNumeric(aRaw);
|
|
136
|
+
const bNum = parseNumeric(bRaw);
|
|
137
|
+
return compareNumeric(aNum, bNum, sortOrder);
|
|
138
|
+
}
|
|
139
|
+
return compareValues(aRaw, bRaw, sortOrder);
|
|
75
140
|
});
|
|
76
141
|
}
|
|
77
142
|
|
|
@@ -97,24 +162,41 @@ export function createSortHandler<T = KTDataTableDataInterface>(
|
|
|
97
162
|
sortField: keyof T,
|
|
98
163
|
sortOrder: KTDataTableSortOrderInterface,
|
|
99
164
|
): void {
|
|
165
|
+
const baseClass = config.sort?.classes?.base || '';
|
|
100
166
|
const sortClass = sortOrder
|
|
101
167
|
? sortOrder === 'asc'
|
|
102
168
|
? config.sort?.classes?.asc || ''
|
|
103
169
|
: config.sort?.classes?.desc || ''
|
|
104
170
|
: '';
|
|
171
|
+
// Clear all headers: remove sort state so only the active column shows highlighted arrow
|
|
172
|
+
const allTh = theadElement.querySelectorAll('th');
|
|
173
|
+
allTh.forEach((header) => {
|
|
174
|
+
const el = header as HTMLElement;
|
|
175
|
+
el.setAttribute('aria-sort', 'none');
|
|
176
|
+
const sortElement = header.querySelector(`.${baseClass}`) as HTMLElement;
|
|
177
|
+
if (sortElement) {
|
|
178
|
+
sortElement.className = baseClass;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
// Apply sort state to the active column so table.css [aria-sort='asc'] / [aria-sort='desc'] can highlight the arrow
|
|
105
182
|
const th =
|
|
106
183
|
typeof sortField === 'number'
|
|
107
|
-
?
|
|
184
|
+
? allTh[sortField]
|
|
108
185
|
: (theadElement.querySelector(
|
|
109
186
|
`th[data-kt-datatable-column="${String(sortField)}"], th[data-kt-datatable-column-sort="${String(sortField)}"]`,
|
|
110
187
|
) as HTMLElement);
|
|
111
188
|
if (th) {
|
|
112
189
|
const sortElement = th.querySelector(
|
|
113
|
-
`.${
|
|
190
|
+
`.${baseClass}`,
|
|
114
191
|
) as HTMLElement;
|
|
115
192
|
if (sortElement) {
|
|
116
193
|
sortElement.className =
|
|
117
|
-
`${
|
|
194
|
+
`${baseClass} ${sortClass}`.trim();
|
|
195
|
+
}
|
|
196
|
+
if (sortOrder) {
|
|
197
|
+
th.setAttribute('aria-sort', sortOrder);
|
|
198
|
+
} else {
|
|
199
|
+
th.setAttribute('aria-sort', 'none');
|
|
118
200
|
}
|
|
119
201
|
}
|
|
120
202
|
}
|
|
@@ -73,7 +73,14 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
73
73
|
constructor(element: HTMLElement, config?: KTDataTableConfigInterface) {
|
|
74
74
|
super();
|
|
75
75
|
|
|
76
|
-
if (KTData.has(element as HTMLElement, this._name))
|
|
76
|
+
if (KTData.has(element as HTMLElement, this._name)) {
|
|
77
|
+
// Already initialized (e.g. by createInstances). Merge user config so columns/sortType etc. apply.
|
|
78
|
+
const existing = KTDataTable.getInstance(element as HTMLElement);
|
|
79
|
+
if (existing && config) {
|
|
80
|
+
existing._mergeConfig(config);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
77
84
|
|
|
78
85
|
this._defaultConfig = this._initDefaultConfig(config);
|
|
79
86
|
|
|
@@ -668,13 +675,13 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
668
675
|
this._storeOriginalClasses();
|
|
669
676
|
|
|
670
677
|
const rows = this._tbodyElement.querySelectorAll<HTMLTableRowElement>('tr');
|
|
671
|
-
|
|
678
|
+
|
|
672
679
|
// Filter th elements to only include those with data-kt-datatable-column attribute
|
|
673
680
|
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
674
681
|
? this._theadElement.querySelectorAll('th')
|
|
675
682
|
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
676
|
-
|
|
677
|
-
const ths: HTMLTableCellElement[] = Array.from(allThs).filter(th =>
|
|
683
|
+
|
|
684
|
+
const ths: HTMLTableCellElement[] = Array.from(allThs).filter(th =>
|
|
678
685
|
th.hasAttribute('data-kt-datatable-column')
|
|
679
686
|
);
|
|
680
687
|
|
|
@@ -708,15 +715,15 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
708
715
|
*/
|
|
709
716
|
private _localTableHeaderInvalidate(): boolean {
|
|
710
717
|
const { originalData } = this.getState();
|
|
711
|
-
|
|
718
|
+
|
|
712
719
|
// Count only th elements with data-kt-datatable-column attribute
|
|
713
720
|
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
714
721
|
? this._theadElement.querySelectorAll('th')
|
|
715
722
|
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
716
|
-
const currentTableHeaders = Array.from(allThs).filter(th =>
|
|
723
|
+
const currentTableHeaders = Array.from(allThs).filter(th =>
|
|
717
724
|
th.hasAttribute('data-kt-datatable-column')
|
|
718
725
|
).length;
|
|
719
|
-
|
|
726
|
+
|
|
720
727
|
const totalColumns = originalData.length
|
|
721
728
|
? Object.keys(originalData[0]).length
|
|
722
729
|
: 0;
|
|
@@ -1015,10 +1022,12 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1015
1022
|
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
1016
1023
|
? this._theadElement.querySelectorAll('th')
|
|
1017
1024
|
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
1018
|
-
|
|
1019
|
-
const ths: HTMLTableCellElement[] = Array.from(allThs).filter(th =>
|
|
1025
|
+
|
|
1026
|
+
const ths: HTMLTableCellElement[] = Array.from(allThs).filter(th =>
|
|
1020
1027
|
th.hasAttribute('data-kt-datatable-column')
|
|
1021
1028
|
);
|
|
1029
|
+
// When no th has data-kt-datatable-column, use all ths so we still render by column index (data extracted with numeric keys)
|
|
1030
|
+
const columnsToRender: HTMLTableCellElement[] = ths.length > 0 ? ths : Array.from(allThs);
|
|
1022
1031
|
|
|
1023
1032
|
this._data.forEach((item: T, rowIndex: number) => {
|
|
1024
1033
|
const row = document.createElement('tr');
|
|
@@ -1033,8 +1042,8 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1033
1042
|
? this.getState().originalDataAttributes[rowIndex]
|
|
1034
1043
|
: null;
|
|
1035
1044
|
|
|
1036
|
-
// Use
|
|
1037
|
-
|
|
1045
|
+
// Use columnsToRender so tables without data-kt-datatable-column still get cells (by index)
|
|
1046
|
+
columnsToRender.forEach((th, colIndex) => {
|
|
1038
1047
|
const colName = th.getAttribute('data-kt-datatable-column');
|
|
1039
1048
|
const td = document.createElement('td');
|
|
1040
1049
|
let value: any;
|
|
@@ -1811,8 +1820,30 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1811
1820
|
*/
|
|
1812
1821
|
public static init(): void {
|
|
1813
1822
|
if (typeof document === 'undefined') return;
|
|
1814
|
-
|
|
1815
|
-
|
|
1823
|
+
KTDataTable.createInstances();
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
/**
|
|
1827
|
+
* Force reinitialization of datatables by clearing existing instances.
|
|
1828
|
+
* Useful for Livewire wire:navigate where the DOM is replaced and new tables need to be initialized.
|
|
1829
|
+
*/
|
|
1830
|
+
public static reinit(): void {
|
|
1831
|
+
if (typeof document === 'undefined') return;
|
|
1832
|
+
const elements = document.querySelectorAll<HTMLElement>('[data-kt-datatable="true"]');
|
|
1833
|
+
elements.forEach((element) => {
|
|
1834
|
+
try {
|
|
1835
|
+
const instance = KTDataTable.getInstance(element);
|
|
1836
|
+
if (instance && typeof instance.dispose === 'function') {
|
|
1837
|
+
instance.dispose();
|
|
1838
|
+
}
|
|
1839
|
+
KTData.remove(element, 'datatable');
|
|
1840
|
+
element.removeAttribute('data-kt-datatable-initialized');
|
|
1841
|
+
element.classList.remove('datatable-initialized');
|
|
1842
|
+
} catch {
|
|
1843
|
+
// ignore per-element errors
|
|
1844
|
+
}
|
|
1845
|
+
});
|
|
1846
|
+
KTDataTable._instances.clear();
|
|
1816
1847
|
KTDataTable.createInstances();
|
|
1817
1848
|
}
|
|
1818
1849
|
|
|
@@ -101,6 +101,20 @@ export interface KTDataTableConfigInterface {
|
|
|
101
101
|
rowData: KTDataTableDataInterface,
|
|
102
102
|
row: HTMLTableRowElement,
|
|
103
103
|
) => void;
|
|
104
|
+
/**
|
|
105
|
+
* Sort comparison type for this column. When 'numeric', values are parsed
|
|
106
|
+
* (e.g. strip currency/commas) and compared as numbers.
|
|
107
|
+
*/
|
|
108
|
+
sortType?: 'string' | 'numeric';
|
|
109
|
+
/**
|
|
110
|
+
* Custom value used for sorting. When set, this is used instead of the raw
|
|
111
|
+
* cell value (and instead of sortType). Return a number or string to sort by.
|
|
112
|
+
* Use for custom formats (e.g. dates, combined fields, custom parsing).
|
|
113
|
+
*/
|
|
114
|
+
sortValue?: (
|
|
115
|
+
cellValue: KTDataTableDataInterface[keyof KTDataTableDataInterface] | string,
|
|
116
|
+
rowData: KTDataTableDataInterface,
|
|
117
|
+
) => number | string;
|
|
104
118
|
};
|
|
105
119
|
};
|
|
106
120
|
|
|
@@ -33,6 +33,7 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
33
33
|
persistent: false,
|
|
34
34
|
container: '',
|
|
35
35
|
focus: true,
|
|
36
|
+
keepInPlaceWithin: '',
|
|
36
37
|
};
|
|
37
38
|
protected override _config: KTDrawerConfigInterface = this._defaultConfig;
|
|
38
39
|
protected _isOpen: boolean = false;
|
|
@@ -91,26 +92,19 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
91
92
|
|
|
92
93
|
KTDrawer.hide();
|
|
93
94
|
|
|
94
|
-
//
|
|
95
|
-
//
|
|
95
|
+
// When container="body", move drawer to body only if NOT inside an element matching keepInPlaceWithin.
|
|
96
|
+
// When keepInPlaceWithin is set (e.g. for SPA/persisted layouts), keeping the drawer in place lets the host preserve it across navigations.
|
|
96
97
|
if (this._getOption('container') === 'body' && this._element.parentElement !== document.body) {
|
|
97
|
-
|
|
98
|
-
if (!
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const livewireComponent = originalParent.closest('[wire\\:id]');
|
|
104
|
-
const header = originalParent.closest('header#header');
|
|
105
|
-
if (livewireComponent) {
|
|
106
|
-
this._element.setAttribute('data-kt-drawer-original-wire-id', (livewireComponent as HTMLElement).getAttribute('wire:id') || '');
|
|
107
|
-
}
|
|
108
|
-
if (header) {
|
|
109
|
-
this._element.setAttribute('data-kt-drawer-original-in-header', 'true');
|
|
98
|
+
const keepInPlace = this._isKeepInPlace();
|
|
99
|
+
if (!keepInPlace) {
|
|
100
|
+
if (!this._element.hasAttribute('data-kt-drawer-original-parent-id')) {
|
|
101
|
+
const originalParent = this._element.parentElement;
|
|
102
|
+
if (originalParent && originalParent !== document.body) {
|
|
103
|
+
this._element.setAttribute('data-kt-drawer-original-parent-id', originalParent.id || '');
|
|
110
104
|
}
|
|
111
105
|
}
|
|
106
|
+
document.body.appendChild(this._element);
|
|
112
107
|
}
|
|
113
|
-
document.body.appendChild(this._element);
|
|
114
108
|
}
|
|
115
109
|
|
|
116
110
|
if (this._getOption('backdrop') === true) this._createBackdrop();
|
|
@@ -209,23 +203,11 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
209
203
|
protected _handleContainer(): void {
|
|
210
204
|
if (this._getOption('container')) {
|
|
211
205
|
if (this._getOption('container') === 'body') {
|
|
212
|
-
|
|
213
|
-
// If so, don't move it to body - keep it in place so Livewire can preserve it
|
|
214
|
-
// This follows the same pattern as dropdowns/menus which work with wire:navigate
|
|
215
|
-
const originalParent = this._element.parentNode;
|
|
216
|
-
const isInPersistedComponent = originalParent &&
|
|
217
|
-
((originalParent as HTMLElement).closest('[wire\\:id]') !== null ||
|
|
218
|
-
(originalParent as HTMLElement).closest('header#header') !== null);
|
|
219
|
-
|
|
220
|
-
if (isInPersistedComponent) {
|
|
221
|
-
// Don't move to body - keep in original location for Livewire persistence
|
|
222
|
-
// Use fixed positioning to achieve the same visual effect
|
|
223
|
-
// Ensure drawer has fixed positioning to work from its current location
|
|
206
|
+
if (this._isKeepInPlace()) {
|
|
224
207
|
if (!this._element.style.position || this._element.style.position === 'static') {
|
|
225
208
|
this._element.style.position = 'fixed';
|
|
226
209
|
}
|
|
227
210
|
} else {
|
|
228
|
-
// Not in persisted component - safe to move to body (follows original behavior)
|
|
229
211
|
document.body.appendChild(this._element);
|
|
230
212
|
}
|
|
231
213
|
} else {
|
|
@@ -236,6 +218,22 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
236
218
|
}
|
|
237
219
|
}
|
|
238
220
|
|
|
221
|
+
/** True when drawer is inside an element matching keepInPlaceWithin (so we keep it in place instead of moving to body). */
|
|
222
|
+
protected _isKeepInPlace(): boolean {
|
|
223
|
+
const selector = (this._getOption('keepInPlaceWithin') as string)?.trim();
|
|
224
|
+
if (!selector || !this._element?.parentElement) return false;
|
|
225
|
+
const parent = this._element.parentElement;
|
|
226
|
+
const selectors = selector.split(',').map((s) => s.trim()).filter(Boolean);
|
|
227
|
+
for (const sel of selectors) {
|
|
228
|
+
try {
|
|
229
|
+
if (parent.closest(sel) !== null) return true;
|
|
230
|
+
} catch {
|
|
231
|
+
// invalid selector, skip
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
239
237
|
protected _autoFocus(): void {
|
|
240
238
|
if (!this._element) return;
|
|
241
239
|
const input: HTMLInputElement | null = this._element.querySelector(
|
|
@@ -253,7 +251,12 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
253
251
|
this._backdropElement = document.createElement('DIV');
|
|
254
252
|
this._backdropElement.style.zIndex = (zindex - 1).toString();
|
|
255
253
|
this._backdropElement.setAttribute('data-kt-drawer-backdrop', 'true');
|
|
256
|
-
|
|
254
|
+
const parent = this._element.parentElement;
|
|
255
|
+
if (parent) {
|
|
256
|
+
parent.insertBefore(this._backdropElement, this._element);
|
|
257
|
+
} else {
|
|
258
|
+
document.body.append(this._backdropElement);
|
|
259
|
+
}
|
|
257
260
|
KTDom.reflow(this._backdropElement);
|
|
258
261
|
KTDom.addClass(
|
|
259
262
|
this._backdropElement,
|
|
@@ -284,8 +287,8 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
284
287
|
return KTUtils.stringToBoolean(this._getOption('enable'));
|
|
285
288
|
}
|
|
286
289
|
|
|
287
|
-
public toggle(): void {
|
|
288
|
-
return this._toggle();
|
|
290
|
+
public toggle(relatedTarget?: HTMLElement): void {
|
|
291
|
+
return this._toggle(relatedTarget);
|
|
289
292
|
}
|
|
290
293
|
|
|
291
294
|
public show(relatedTarget?: HTMLElement): void {
|
|
@@ -504,7 +507,7 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
504
507
|
const drawer = KTDrawer.getInstance(target);
|
|
505
508
|
|
|
506
509
|
if (drawer) {
|
|
507
|
-
drawer.toggle();
|
|
510
|
+
drawer.toggle(target);
|
|
508
511
|
} else {
|
|
509
512
|
// Drawer element not found - wait for it to appear (handles persisted Livewire components)
|
|
510
513
|
// Check if drawer exists in persisted components (might be in header that's persisted)
|
|
@@ -522,7 +525,7 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
522
525
|
// Get instance and toggle
|
|
523
526
|
const drawerInstance = KTDrawer.getInstance(drawerElement);
|
|
524
527
|
if (drawerInstance) {
|
|
525
|
-
drawerInstance.toggle();
|
|
528
|
+
drawerInstance.toggle(target);
|
|
526
529
|
}
|
|
527
530
|
} else {
|
|
528
531
|
// Drawer never appeared - trigger a reinit to see if it helps
|
|
@@ -537,7 +540,7 @@ export class KTDrawer extends KTComponent implements KTDrawerInterface {
|
|
|
537
540
|
}
|
|
538
541
|
const drawerInstance = KTDrawer.getInstance(drawerAfterReinit as HTMLElement);
|
|
539
542
|
if (drawerInstance) {
|
|
540
|
-
drawerInstance.toggle();
|
|
543
|
+
drawerInstance.toggle(target);
|
|
541
544
|
}
|
|
542
545
|
}
|
|
543
546
|
}, 500);
|
|
@@ -17,9 +17,11 @@ export interface KTDrawerConfigInterface {
|
|
|
17
17
|
persistent: boolean;
|
|
18
18
|
focus: boolean;
|
|
19
19
|
container: string;
|
|
20
|
+
/** When set, drawer is not moved to body when inside an element matching this selector (e.g. for SPA/persisted layouts). Comma-separated for multiple selectors. */
|
|
21
|
+
keepInPlaceWithin?: string;
|
|
20
22
|
}
|
|
21
23
|
export interface KTDrawerInterface {
|
|
22
|
-
show(): void;
|
|
24
|
+
show(relatedTarget?: HTMLElement): void;
|
|
23
25
|
hide(): void;
|
|
24
|
-
toggle(): void;
|
|
26
|
+
toggle(relatedTarget?: HTMLElement): void;
|
|
25
27
|
}
|
|
@@ -45,6 +45,8 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
|
|
|
45
45
|
protected _menuElement: HTMLElement;
|
|
46
46
|
protected _isTransitioning: boolean = false;
|
|
47
47
|
protected _isOpen: boolean = false;
|
|
48
|
+
/** Timestamp when _show() was last called; used to ignore duplicate _hide() from double handlers */
|
|
49
|
+
protected _shownAt: number = 0;
|
|
48
50
|
|
|
49
51
|
constructor(element: HTMLElement, config?: KTDropdownConfigInterface) {
|
|
50
52
|
super();
|
|
@@ -206,10 +208,13 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
|
|
|
206
208
|
this._fireEvent('shown');
|
|
207
209
|
this._dispatchEvent('shown');
|
|
208
210
|
});
|
|
211
|
+
this._shownAt = Date.now();
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
protected _hide(): void {
|
|
212
215
|
if (!this._isOpen || this._isTransitioning) return;
|
|
216
|
+
// If another handler fired _hide() right after _show() (e.g. double initHandlers), ignore
|
|
217
|
+
if (this._shownAt && Date.now() - this._shownAt < 150) return;
|
|
213
218
|
|
|
214
219
|
const payload = { cancel: false };
|
|
215
220
|
this._fireEvent('hide', payload);
|