@keenthemes/ktui 1.1.4 → 1.1.6
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 +11061 -10799
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +33 -27
- package/lib/cjs/components/collapse/collapse.js +0 -2
- package/lib/cjs/components/collapse/collapse.js.map +1 -1
- package/lib/cjs/components/component.js +11 -0
- package/lib/cjs/components/component.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +80 -11
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +77 -24
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/drawer/drawer.js +63 -42
- 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/scrollto/scrollto.js +0 -2
- package/lib/cjs/components/scrollto/scrollto.js.map +1 -1
- package/lib/cjs/components/select/combobox.js.map +1 -1
- package/lib/cjs/components/select/dropdown.js.map +1 -1
- package/lib/cjs/components/select/remote.js.map +1 -1
- package/lib/cjs/components/select/search.js +9 -5
- package/lib/cjs/components/select/search.js.map +1 -1
- package/lib/cjs/components/select/select.js +29 -9
- package/lib/cjs/components/select/select.js.map +1 -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 +10 -0
- package/lib/cjs/components/select/utils.js.map +1 -1
- package/lib/cjs/components/sticky/sticky.js +104 -24
- package/lib/cjs/components/sticky/sticky.js.map +1 -1
- package/lib/cjs/components/theme-switch/theme-switch.js +0 -2
- package/lib/cjs/components/theme-switch/theme-switch.js.map +1 -1
- package/lib/cjs/components/toast/toast.js +1 -2
- package/lib/cjs/components/toast/toast.js.map +1 -1
- package/lib/cjs/helpers/dom.js +0 -2
- package/lib/cjs/helpers/dom.js.map +1 -1
- package/lib/esm/components/collapse/collapse.js +0 -2
- package/lib/esm/components/collapse/collapse.js.map +1 -1
- package/lib/esm/components/component.js +11 -0
- package/lib/esm/components/component.js.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +80 -11
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +77 -24
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/drawer/drawer.js +63 -42
- 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/scrollto/scrollto.js +0 -2
- package/lib/esm/components/scrollto/scrollto.js.map +1 -1
- package/lib/esm/components/select/combobox.js.map +1 -1
- package/lib/esm/components/select/dropdown.js.map +1 -1
- package/lib/esm/components/select/remote.js.map +1 -1
- package/lib/esm/components/select/search.js +9 -5
- package/lib/esm/components/select/search.js.map +1 -1
- package/lib/esm/components/select/select.js +29 -9
- package/lib/esm/components/select/select.js.map +1 -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 +10 -0
- package/lib/esm/components/select/utils.js.map +1 -1
- package/lib/esm/components/sticky/sticky.js +104 -24
- package/lib/esm/components/sticky/sticky.js.map +1 -1
- package/lib/esm/components/theme-switch/theme-switch.js +0 -2
- package/lib/esm/components/theme-switch/theme-switch.js.map +1 -1
- package/lib/esm/components/toast/toast.js +1 -2
- package/lib/esm/components/toast/toast.js.map +1 -1
- package/lib/esm/helpers/dom.js +0 -2
- package/lib/esm/helpers/dom.js.map +1 -1
- package/package.json +14 -7
- package/src/components/collapse/collapse.ts +0 -3
- package/src/components/component.ts +14 -4
- package/src/components/datatable/__tests__/currency-sort.test.ts +108 -0
- package/src/components/datatable/__tests__/multi-row-headers.test.ts +121 -0
- package/src/components/datatable/__tests__/pagination-reset.test.ts +13 -5
- package/src/components/datatable/__tests__/race-conditions.test.ts +138 -78
- package/src/components/datatable/__tests__/setup.ts +9 -4
- package/src/components/datatable/datatable-sort.ts +88 -10
- package/src/components/datatable/datatable.css +4 -4
- package/src/components/datatable/datatable.ts +91 -30
- package/src/components/datatable/types.ts +16 -0
- package/src/components/drawer/drawer.ts +97 -57
- package/src/components/drawer/types.ts +4 -2
- package/src/components/dropdown/dropdown.ts +8 -1
- package/src/components/scrollto/scrollto.ts +0 -3
- package/src/components/select/__tests__/ux-behaviors.test.ts +274 -8
- package/src/components/select/combobox.ts +0 -1
- package/src/components/select/dropdown.ts +0 -2
- package/src/components/select/remote.ts +1 -6
- package/src/components/select/search.ts +14 -7
- package/src/components/select/select.ts +29 -29
- package/src/components/select/tags.ts +0 -1
- package/src/components/select/templates.ts +8 -8
- package/src/components/select/utils.ts +15 -2
- package/src/components/sticky/__tests__/sticky.test.ts +205 -0
- package/src/components/sticky/sticky.ts +119 -21
- package/src/components/sticky/types.ts +3 -0
- package/src/components/theme-switch/theme-switch.ts +0 -3
- package/src/components/toast/toast.ts +3 -2
- package/src/helpers/dom.ts +0 -3
|
@@ -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,79 @@ 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(sortField: keyof T | number):
|
|
93
|
+
| {
|
|
94
|
+
sortType?: 'string' | 'numeric';
|
|
95
|
+
sortValue?: (
|
|
96
|
+
cellValue: unknown,
|
|
97
|
+
rowData: KTDataTableDataInterface,
|
|
98
|
+
) => number | string;
|
|
99
|
+
}
|
|
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 = !sortValueFn && columnDef?.sortType === 'numeric';
|
|
118
|
+
|
|
71
119
|
return data.sort((a, b) => {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
120
|
+
const aRaw = a[sortField as keyof T] as unknown;
|
|
121
|
+
const bRaw = b[sortField as keyof T] as unknown;
|
|
122
|
+
|
|
123
|
+
if (typeof sortValueFn === 'function') {
|
|
124
|
+
const aVal = sortValueFn(aRaw, a as KTDataTableDataInterface);
|
|
125
|
+
const bVal = sortValueFn(bRaw, b as KTDataTableDataInterface);
|
|
126
|
+
const aNum = typeof aVal === 'number' ? aVal : parseNumeric(aVal);
|
|
127
|
+
const bNum = typeof bVal === 'number' ? bVal : parseNumeric(bVal);
|
|
128
|
+
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
129
|
+
return compareNumeric(aNum, bNum, sortOrder);
|
|
130
|
+
}
|
|
131
|
+
return compareValues(aVal, bVal, sortOrder);
|
|
132
|
+
}
|
|
133
|
+
if (useNumeric) {
|
|
134
|
+
const aNum = parseNumeric(aRaw);
|
|
135
|
+
const bNum = parseNumeric(bRaw);
|
|
136
|
+
return compareNumeric(aNum, bNum, sortOrder);
|
|
137
|
+
}
|
|
138
|
+
return compareValues(aRaw, bRaw, sortOrder);
|
|
75
139
|
});
|
|
76
140
|
}
|
|
77
141
|
|
|
@@ -97,24 +161,38 @@ export function createSortHandler<T = KTDataTableDataInterface>(
|
|
|
97
161
|
sortField: keyof T,
|
|
98
162
|
sortOrder: KTDataTableSortOrderInterface,
|
|
99
163
|
): void {
|
|
164
|
+
const baseClass = config.sort?.classes?.base || '';
|
|
100
165
|
const sortClass = sortOrder
|
|
101
166
|
? sortOrder === 'asc'
|
|
102
167
|
? config.sort?.classes?.asc || ''
|
|
103
168
|
: config.sort?.classes?.desc || ''
|
|
104
169
|
: '';
|
|
170
|
+
// Clear all headers: remove sort state so only the active column shows highlighted arrow
|
|
171
|
+
const allTh = theadElement.querySelectorAll('th');
|
|
172
|
+
allTh.forEach((header) => {
|
|
173
|
+
const el = header as HTMLElement;
|
|
174
|
+
el.setAttribute('aria-sort', 'none');
|
|
175
|
+
const sortElement = header.querySelector(`.${baseClass}`) as HTMLElement;
|
|
176
|
+
if (sortElement) {
|
|
177
|
+
sortElement.className = baseClass;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// Apply sort state to the active column so table.css [aria-sort='asc'] / [aria-sort='desc'] can highlight the arrow
|
|
105
181
|
const th =
|
|
106
182
|
typeof sortField === 'number'
|
|
107
|
-
?
|
|
183
|
+
? allTh[sortField]
|
|
108
184
|
: (theadElement.querySelector(
|
|
109
185
|
`th[data-kt-datatable-column="${String(sortField)}"], th[data-kt-datatable-column-sort="${String(sortField)}"]`,
|
|
110
186
|
) as HTMLElement);
|
|
111
187
|
if (th) {
|
|
112
|
-
const sortElement = th.querySelector(
|
|
113
|
-
`.${config.sort?.classes?.base}`,
|
|
114
|
-
) as HTMLElement;
|
|
188
|
+
const sortElement = th.querySelector(`.${baseClass}`) as HTMLElement;
|
|
115
189
|
if (sortElement) {
|
|
116
|
-
sortElement.className =
|
|
117
|
-
|
|
190
|
+
sortElement.className = `${baseClass} ${sortClass}`.trim();
|
|
191
|
+
}
|
|
192
|
+
if (sortOrder) {
|
|
193
|
+
th.setAttribute('aria-sort', sortOrder);
|
|
194
|
+
} else {
|
|
195
|
+
th.setAttribute('aria-sort', 'none');
|
|
118
196
|
}
|
|
119
197
|
}
|
|
120
198
|
}
|
|
@@ -92,19 +92,19 @@
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
@custom-variant kt-datatable-sort-asc {
|
|
95
|
-
&.[
|
|
95
|
+
&.[aria-sort='asc']:where([data-kt-datatable] *) {
|
|
96
96
|
@slot;
|
|
97
97
|
}
|
|
98
|
-
[data-kt-datatable] [
|
|
98
|
+
[data-kt-datatable] [aria-sort='asc'] & {
|
|
99
99
|
@slot;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
@custom-variant kt-datatable-sort-desc {
|
|
104
|
-
&.[
|
|
104
|
+
&.[aria-sort='desc']:where([data-kt-datatable] *) {
|
|
105
105
|
@slot;
|
|
106
106
|
}
|
|
107
|
-
[data-kt-datatable] [
|
|
107
|
+
[data-kt-datatable] [aria-sort='desc'] & {
|
|
108
108
|
@slot;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -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,14 +675,14 @@ 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 =>
|
|
678
|
-
th.hasAttribute('data-kt-datatable-column')
|
|
683
|
+
|
|
684
|
+
const ths: HTMLTableCellElement[] = Array.from(allThs).filter((th) =>
|
|
685
|
+
th.hasAttribute('data-kt-datatable-column'),
|
|
679
686
|
);
|
|
680
687
|
|
|
681
688
|
rows.forEach((row: HTMLTableRowElement) => {
|
|
@@ -708,22 +715,47 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
708
715
|
*/
|
|
709
716
|
private _localTableHeaderInvalidate(): boolean {
|
|
710
717
|
const { originalData } = this.getState();
|
|
711
|
-
|
|
712
|
-
// Count only th elements with data-kt-datatable-column attribute
|
|
713
|
-
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
714
|
-
? this._theadElement.querySelectorAll('th')
|
|
715
|
-
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
716
|
-
const currentTableHeaders = Array.from(allThs).filter(th =>
|
|
717
|
-
th.hasAttribute('data-kt-datatable-column')
|
|
718
|
-
).length;
|
|
719
|
-
|
|
718
|
+
|
|
720
719
|
const totalColumns = originalData.length
|
|
721
720
|
? Object.keys(originalData[0]).length
|
|
722
721
|
: 0;
|
|
723
722
|
|
|
723
|
+
// Count th elements with data-kt-datatable-column; when none (e.g. multi-row headers), use logical column count so we don't falsely invalidate
|
|
724
|
+
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
725
|
+
? this._theadElement.querySelectorAll('th')
|
|
726
|
+
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
727
|
+
const thsWithColumn = Array.from(allThs).filter((th) =>
|
|
728
|
+
th.hasAttribute('data-kt-datatable-column'),
|
|
729
|
+
);
|
|
730
|
+
const currentTableHeaders =
|
|
731
|
+
thsWithColumn.length > 0
|
|
732
|
+
? thsWithColumn.length
|
|
733
|
+
: this._getLogicalColumnCount();
|
|
734
|
+
|
|
724
735
|
return currentTableHeaders !== totalColumns;
|
|
725
736
|
}
|
|
726
737
|
|
|
738
|
+
/**
|
|
739
|
+
* Returns the logical data column count (number of data columns), used for multi-row headers
|
|
740
|
+
* where querySelectorAll('th') would overcount. Prefers state.originalData, then first tbody row td count.
|
|
741
|
+
* @returns {number} Number of data columns, or 0 if unknown
|
|
742
|
+
*/
|
|
743
|
+
private _getLogicalColumnCount(): number {
|
|
744
|
+
const { originalData } = this.getState();
|
|
745
|
+
if (originalData && originalData.length > 0) {
|
|
746
|
+
return Object.keys(originalData[0]).length;
|
|
747
|
+
}
|
|
748
|
+
if (this._tbodyElement) {
|
|
749
|
+
const firstRow = this._tbodyElement.querySelector<HTMLTableRowElement>(
|
|
750
|
+
'tr',
|
|
751
|
+
);
|
|
752
|
+
if (firstRow) {
|
|
753
|
+
return firstRow.querySelectorAll<HTMLTableCellElement>('td').length;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return 0;
|
|
757
|
+
}
|
|
758
|
+
|
|
727
759
|
/**
|
|
728
760
|
* Fetch data from the server
|
|
729
761
|
*/
|
|
@@ -763,13 +795,13 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
763
795
|
response,
|
|
764
796
|
error: String(error),
|
|
765
797
|
status: response.status,
|
|
766
|
-
statusText: response.statusText
|
|
798
|
+
statusText: response.statusText,
|
|
767
799
|
});
|
|
768
800
|
this._dispatchEvent('parseError', {
|
|
769
801
|
response,
|
|
770
802
|
error: String(error),
|
|
771
803
|
status: response.status,
|
|
772
|
-
statusText: response.statusText
|
|
804
|
+
statusText: response.statusText,
|
|
773
805
|
});
|
|
774
806
|
return;
|
|
775
807
|
}
|
|
@@ -855,7 +887,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
855
887
|
private async _performFetchRequest(
|
|
856
888
|
queryParams: URLSearchParams,
|
|
857
889
|
): Promise<Response> {
|
|
858
|
-
|
|
890
|
+
const requestMethod: RequestInit['method'] = this._config.requestMethod;
|
|
859
891
|
let requestBody: RequestInit['body'] | undefined = undefined;
|
|
860
892
|
|
|
861
893
|
// Cancel previous request to prevent race conditions
|
|
@@ -1015,10 +1047,15 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1015
1047
|
const allThs: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
1016
1048
|
? this._theadElement.querySelectorAll('th')
|
|
1017
1049
|
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
1018
|
-
|
|
1019
|
-
const ths: HTMLTableCellElement[] = Array.from(allThs).filter(th =>
|
|
1020
|
-
th.hasAttribute('data-kt-datatable-column')
|
|
1050
|
+
|
|
1051
|
+
const ths: HTMLTableCellElement[] = Array.from(allThs).filter((th) =>
|
|
1052
|
+
th.hasAttribute('data-kt-datatable-column'),
|
|
1021
1053
|
);
|
|
1054
|
+
// When no th has data-kt-datatable-column (e.g. multi-row headers), use logical column count from tbody so we don't overcount thead cells
|
|
1055
|
+
const columnsToRender: HTMLTableCellElement[] =
|
|
1056
|
+
ths.length > 0 ? ths : [];
|
|
1057
|
+
const logicalColumnCount =
|
|
1058
|
+
ths.length > 0 ? ths.length : this._getLogicalColumnCount();
|
|
1022
1059
|
|
|
1023
1060
|
this._data.forEach((item: T, rowIndex: number) => {
|
|
1024
1061
|
const row = document.createElement('tr');
|
|
@@ -1033,9 +1070,9 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1033
1070
|
? this.getState().originalDataAttributes[rowIndex]
|
|
1034
1071
|
: null;
|
|
1035
1072
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
const colName = th
|
|
1073
|
+
for (let colIndex = 0; colIndex < logicalColumnCount; colIndex++) {
|
|
1074
|
+
const th = columnsToRender[colIndex];
|
|
1075
|
+
const colName = th?.getAttribute('data-kt-datatable-column');
|
|
1039
1076
|
const td = document.createElement('td');
|
|
1040
1077
|
let value: any;
|
|
1041
1078
|
if (colName && Object.prototype.hasOwnProperty.call(item, colName)) {
|
|
@@ -1063,7 +1100,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1063
1100
|
}
|
|
1064
1101
|
|
|
1065
1102
|
row.appendChild(td);
|
|
1066
|
-
}
|
|
1103
|
+
}
|
|
1067
1104
|
} else {
|
|
1068
1105
|
Object.keys(this._config.columns).forEach(
|
|
1069
1106
|
(key: keyof T, colIndex: number) => {
|
|
@@ -1117,9 +1154,9 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1117
1154
|
private _noticeOnTable(message: string = ''): void {
|
|
1118
1155
|
const row = this._tableElement.tBodies[0].insertRow();
|
|
1119
1156
|
const cell = row.insertCell();
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1157
|
+
const logicalCount = this._getLogicalColumnCount();
|
|
1158
|
+
// Use logical column count so multi-row headers don't overcount; fallback to 1 when 0 so message still displays
|
|
1159
|
+
cell.colSpan = logicalCount > 0 ? logicalCount : 1;
|
|
1123
1160
|
cell.innerHTML = message;
|
|
1124
1161
|
}
|
|
1125
1162
|
|
|
@@ -1472,7 +1509,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1472
1509
|
const state = JSON.parse(stateString) as KTDataTableStateInterface;
|
|
1473
1510
|
if (state) this._config._state = state;
|
|
1474
1511
|
return state;
|
|
1475
|
-
} catch {}
|
|
1512
|
+
} catch {}
|
|
1476
1513
|
|
|
1477
1514
|
return null;
|
|
1478
1515
|
}
|
|
@@ -1811,8 +1848,32 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1811
1848
|
*/
|
|
1812
1849
|
public static init(): void {
|
|
1813
1850
|
if (typeof document === 'undefined') return;
|
|
1814
|
-
|
|
1815
|
-
|
|
1851
|
+
KTDataTable.createInstances();
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* Force reinitialization of datatables by clearing existing instances.
|
|
1856
|
+
* Useful for Livewire wire:navigate where the DOM is replaced and new tables need to be initialized.
|
|
1857
|
+
*/
|
|
1858
|
+
public static reinit(): void {
|
|
1859
|
+
if (typeof document === 'undefined') return;
|
|
1860
|
+
const elements = document.querySelectorAll<HTMLElement>(
|
|
1861
|
+
'[data-kt-datatable="true"]',
|
|
1862
|
+
);
|
|
1863
|
+
elements.forEach((element) => {
|
|
1864
|
+
try {
|
|
1865
|
+
const instance = KTDataTable.getInstance(element);
|
|
1866
|
+
if (instance && typeof instance.dispose === 'function') {
|
|
1867
|
+
instance.dispose();
|
|
1868
|
+
}
|
|
1869
|
+
KTData.remove(element, 'datatable');
|
|
1870
|
+
element.removeAttribute('data-kt-datatable-initialized');
|
|
1871
|
+
element.classList.remove('datatable-initialized');
|
|
1872
|
+
} catch {
|
|
1873
|
+
// ignore per-element errors
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
KTDataTable._instances.clear();
|
|
1816
1877
|
KTDataTable.createInstances();
|
|
1817
1878
|
}
|
|
1818
1879
|
|
|
@@ -101,6 +101,22 @@ 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:
|
|
116
|
+
| KTDataTableDataInterface[keyof KTDataTableDataInterface]
|
|
117
|
+
| string,
|
|
118
|
+
rowData: KTDataTableDataInterface,
|
|
119
|
+
) => number | string;
|
|
104
120
|
};
|
|
105
121
|
};
|
|
106
122
|
|