@keenthemes/ktui 1.2.5 → 1.2.7
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/README.md +14 -5
- package/dist/ktui.js +1538 -786
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +85 -5
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
- package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
- package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +338 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +85 -27
- package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-registry.js +66 -0
- package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +86 -58
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
- package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-utils.js +15 -0
- package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable.d.ts +35 -34
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +233 -497
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/datatable/index.d.ts +1 -1
- package/lib/cjs/components/datatable/index.d.ts.map +1 -1
- package/lib/cjs/components/datatable/types.d.ts +127 -11
- package/lib/cjs/components/datatable/types.d.ts.map +1 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
- package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
- package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-defaults.js +190 -0
- package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js +334 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +85 -27
- package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-registry.js +63 -0
- package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
- package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
- package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +85 -57
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-spinner.js +51 -0
- package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-utils.js +12 -0
- package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable.d.ts +35 -34
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +235 -499
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/datatable/index.d.ts +1 -1
- package/lib/esm/components/datatable/index.d.ts.map +1 -1
- package/lib/esm/components/datatable/types.d.ts +127 -11
- package/lib/esm/components/datatable/types.d.ts.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +6 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/ktui/SKILL.md +711 -0
- package/skills/ktui-datatable/SKILL.md +302 -0
- package/skills/ktui-install/SKILL.md +150 -0
- package/skills/ktui-select/SKILL.md +271 -0
- package/src/components/__tests__/component.test.ts +347 -0
- package/src/components/collapse/collapse.css +2 -2
- package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
- package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
- package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
- package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
- package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
- package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
- package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
- package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
- package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
- package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
- package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
- package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
- package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
- package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
- package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
- package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
- package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
- package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
- package/src/components/datatable/__tests__/pagination-reset.test.ts +147 -6
- package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
- package/src/components/datatable/__tests__/setup.ts +12 -4
- package/src/components/datatable/datatable-checkbox.ts +139 -143
- package/src/components/datatable/datatable-column-utils.ts +63 -0
- package/src/components/datatable/datatable-contracts.ts +2 -3
- package/src/components/datatable/datatable-defaults.ts +204 -0
- package/src/components/datatable/datatable-layout-plugin.ts +459 -0
- package/src/components/datatable/datatable-local-provider.ts +106 -35
- package/src/components/datatable/datatable-pagination-renderer.ts +13 -15
- package/src/components/datatable/datatable-registry.ts +89 -0
- package/src/components/datatable/datatable-remote-provider.ts +1 -3
- package/src/components/datatable/datatable-search-handler.ts +97 -0
- package/src/components/datatable/datatable-sort.ts +111 -66
- package/src/components/datatable/datatable-spinner.ts +103 -0
- package/src/components/datatable/datatable-state-persistence.ts +67 -0
- package/src/components/datatable/datatable-table-renderer.ts +81 -18
- package/src/components/datatable/datatable-utils.ts +12 -0
- package/src/components/datatable/datatable.css +98 -0
- package/src/components/datatable/datatable.ts +288 -583
- package/src/components/datatable/index.ts +8 -0
- package/src/components/datatable/types.ts +157 -23
- package/src/helpers/__tests__/dom.test.ts +776 -0
- package/src/helpers/__tests__/utils.test.ts +332 -0
- package/src/index.ts +15 -0
- package/skills/ktui-components/SKILL.md +0 -41
- package/skills/ktui-theming/SKILL.md +0 -50
- package/src/components/datatable/datatable-event-adapter.ts +0 -21
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
KTDataTableAttributeInterface,
|
|
9
9
|
KTDataTableConfigInterface,
|
|
10
10
|
KTDataTableDataInterface,
|
|
11
|
-
KTDataTableStateInterface,
|
|
12
11
|
} from './types';
|
|
13
12
|
import {
|
|
14
13
|
KTDataTableDataProvider,
|
|
@@ -16,8 +15,36 @@ import {
|
|
|
16
15
|
KTDataTableProviderResult,
|
|
17
16
|
KTDataTableStateStore,
|
|
18
17
|
} from './datatable-contracts';
|
|
18
|
+
import { resolveColumns } from './datatable-column-utils';
|
|
19
|
+
import { stripHtml } from './datatable-utils';
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
type FilterMatcher = (cellValue: unknown, filterValue: unknown) => boolean;
|
|
22
|
+
|
|
23
|
+
const FILTER_MATCHERS: Record<string, FilterMatcher> = {
|
|
24
|
+
text: (cellValue, filterValue) => {
|
|
25
|
+
if (!filterValue) return true;
|
|
26
|
+
return stripHtml(cellValue)
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.includes(String(filterValue).toLowerCase());
|
|
29
|
+
},
|
|
30
|
+
numeric: (cellValue, filterValue) => {
|
|
31
|
+
const num = parseFloat(
|
|
32
|
+
String(cellValue ?? '').replace(/[^0-9.-]/g, ''),
|
|
33
|
+
);
|
|
34
|
+
return !Number.isNaN(num) && num === filterValue;
|
|
35
|
+
},
|
|
36
|
+
dateRange: (cellValue, filterValue) => {
|
|
37
|
+
const range = filterValue as { from?: string; to?: string };
|
|
38
|
+
if (!range?.from && !range?.to) return true;
|
|
39
|
+
const cellDate = new Date(String(cellValue ?? ''));
|
|
40
|
+
if (Number.isNaN(cellDate.getTime())) return false;
|
|
41
|
+
if (range.from && cellDate < new Date(range.from)) return false;
|
|
42
|
+
if (range.to && cellDate > new Date(range.to)) return false;
|
|
43
|
+
return true;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface KTDataTableLocalProviderOptions {
|
|
21
48
|
config: KTDataTableConfigInterface;
|
|
22
49
|
elements: () => KTDataTableLocalProviderElements;
|
|
23
50
|
getLogicalColumnCount: () => number;
|
|
@@ -28,7 +55,7 @@ interface KTDataTableLocalProviderOptions<T extends KTDataTableDataInterface> {
|
|
|
28
55
|
export class KTDataTableLocalDataProvider<
|
|
29
56
|
T extends KTDataTableDataInterface,
|
|
30
57
|
> implements KTDataTableDataProvider<T> {
|
|
31
|
-
constructor(private readonly options: KTDataTableLocalProviderOptions
|
|
58
|
+
constructor(private readonly options: KTDataTableLocalProviderOptions) {}
|
|
32
59
|
|
|
33
60
|
public async fetch(): Promise<KTDataTableProviderResult<T>> {
|
|
34
61
|
return this.fetchSync();
|
|
@@ -37,13 +64,17 @@ export class KTDataTableLocalDataProvider<
|
|
|
37
64
|
public fetchSync(): KTDataTableProviderResult<T> {
|
|
38
65
|
const state = this.options.stateStore.getState();
|
|
39
66
|
let { originalData } = state;
|
|
67
|
+
const skipDomInvalidation = Boolean(
|
|
68
|
+
this.options.config.lockedLayout || this.options.config.layoutPlugin,
|
|
69
|
+
);
|
|
40
70
|
|
|
41
71
|
if (
|
|
42
72
|
!this.options.elements().tableElement ||
|
|
43
73
|
originalData === undefined ||
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
(!skipDomInvalidation &&
|
|
75
|
+
(this.tableConfigInvalidate() ||
|
|
76
|
+
this.localTableHeaderInvalidate() ||
|
|
77
|
+
this.localTableContentInvalidate()))
|
|
47
78
|
) {
|
|
48
79
|
const { originalData, originalDataAttributes } =
|
|
49
80
|
this.localExtractTableContent();
|
|
@@ -73,6 +104,18 @@ export class KTDataTableLocalDataProvider<
|
|
|
73
104
|
}
|
|
74
105
|
}
|
|
75
106
|
|
|
107
|
+
// Apply column filters
|
|
108
|
+
const { filters } = this.options.stateStore.getState();
|
|
109
|
+
if (filters && filters.length > 0) {
|
|
110
|
+
filteredData = data = data.filter((item: T) => {
|
|
111
|
+
return filters.every((filter) => {
|
|
112
|
+
const cellValue = item[filter.column as keyof T];
|
|
113
|
+
const matcher = FILTER_MATCHERS[filter.type];
|
|
114
|
+
return matcher ? matcher(cellValue, filter.value) : true;
|
|
115
|
+
});
|
|
116
|
+
}) as T[];
|
|
117
|
+
}
|
|
118
|
+
|
|
76
119
|
const sortCallback = this.options.config.sort?.callback;
|
|
77
120
|
if (
|
|
78
121
|
sortField !== undefined &&
|
|
@@ -80,7 +123,12 @@ export class KTDataTableLocalDataProvider<
|
|
|
80
123
|
sortOrder !== '' &&
|
|
81
124
|
typeof sortCallback === 'function'
|
|
82
125
|
) {
|
|
83
|
-
data = sortCallback.call(
|
|
126
|
+
data = sortCallback.call(
|
|
127
|
+
this,
|
|
128
|
+
data,
|
|
129
|
+
sortField as string,
|
|
130
|
+
sortOrder,
|
|
131
|
+
) as T[];
|
|
84
132
|
}
|
|
85
133
|
|
|
86
134
|
if (data?.length > 0) {
|
|
@@ -102,6 +150,30 @@ export class KTDataTableLocalDataProvider<
|
|
|
102
150
|
);
|
|
103
151
|
|
|
104
152
|
if (this.options.stateStore.getState()._contentChecksum !== checksum) {
|
|
153
|
+
const domRowCount =
|
|
154
|
+
tbodyElement.querySelectorAll<HTMLTableRowElement>('tr').length;
|
|
155
|
+
const storedRowCount =
|
|
156
|
+
this.options.stateStore.getState().originalData?.length ?? 0;
|
|
157
|
+
|
|
158
|
+
// Programmatic dataset with an empty tbody must not re-import from the DOM.
|
|
159
|
+
if (domRowCount === 0 && storedRowCount > 0) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// After pagination redraw the tbody only holds the current page. A checksum
|
|
164
|
+
// mismatch there must not shrink originalData to pageSize rows.
|
|
165
|
+
if (
|
|
166
|
+
storedRowCount > 0 &&
|
|
167
|
+
domRowCount > 0 &&
|
|
168
|
+
domRowCount < storedRowCount
|
|
169
|
+
) {
|
|
170
|
+
const { pageSize } = this.options.stateStore.getState();
|
|
171
|
+
if (domRowCount <= pageSize) {
|
|
172
|
+
this.options.stateStore.patchState({ _contentChecksum: checksum });
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
105
177
|
this.options.stateStore.patchState({ _contentChecksum: checksum });
|
|
106
178
|
return true;
|
|
107
179
|
}
|
|
@@ -112,10 +184,14 @@ export class KTDataTableLocalDataProvider<
|
|
|
112
184
|
private tableConfigInvalidate(): boolean {
|
|
113
185
|
const { _state, ...restConfig } = this.options.config;
|
|
114
186
|
const checksum: string = KTUtils.checksum(JSON.stringify(restConfig));
|
|
187
|
+
const previousChecksum = _state?._configChecksum ?? '';
|
|
115
188
|
|
|
116
|
-
if (
|
|
189
|
+
if (previousChecksum !== checksum) {
|
|
117
190
|
this.options.stateStore.patchState({ _configChecksum: checksum });
|
|
118
|
-
|
|
191
|
+
// First load skips this check (originalData === undefined). On the first
|
|
192
|
+
// pagination fetch, previousChecksum is still empty — record it but do
|
|
193
|
+
// not re-extract from the paginated DOM (that would shrink originalData).
|
|
194
|
+
return previousChecksum !== '';
|
|
119
195
|
}
|
|
120
196
|
|
|
121
197
|
return false;
|
|
@@ -132,15 +208,7 @@ export class KTDataTableLocalDataProvider<
|
|
|
132
208
|
this.options.storeOriginalClasses();
|
|
133
209
|
|
|
134
210
|
const rows = tbodyElement.querySelectorAll<HTMLTableRowElement>('tr');
|
|
135
|
-
const
|
|
136
|
-
? theadElement.querySelectorAll('th')
|
|
137
|
-
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
138
|
-
|
|
139
|
-
const ths: HTMLTableCellElement[] = Array.from(allThs).filter((th) =>
|
|
140
|
-
th.hasAttribute('data-kt-datatable-column'),
|
|
141
|
-
);
|
|
142
|
-
const columnsByIndex: HTMLTableCellElement[] =
|
|
143
|
-
ths.length > 0 && ths.length !== allThs.length ? Array.from(allThs) : ths;
|
|
211
|
+
const { columnsByIndex } = resolveColumns(theadElement);
|
|
144
212
|
|
|
145
213
|
rows.forEach((row: HTMLTableRowElement) => {
|
|
146
214
|
const dataRow: T = {} as T;
|
|
@@ -169,25 +237,28 @@ export class KTDataTableLocalDataProvider<
|
|
|
169
237
|
|
|
170
238
|
private localTableHeaderInvalidate(): boolean {
|
|
171
239
|
const { originalData } = this.options.stateStore.getState();
|
|
240
|
+
if (!originalData?.length) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
172
244
|
const { theadElement } = this.options.elements();
|
|
245
|
+
const { typedThs } = resolveColumns(theadElement);
|
|
173
246
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
if (typedThs.length === 0) {
|
|
248
|
+
return (
|
|
249
|
+
this.options.getLogicalColumnCount() !==
|
|
250
|
+
Object.keys(originalData[0]).length
|
|
251
|
+
);
|
|
252
|
+
}
|
|
177
253
|
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
:
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
? allThs.length
|
|
188
|
-
: thsWithColumn.length
|
|
189
|
-
: this.options.getLogicalColumnCount();
|
|
190
|
-
|
|
191
|
-
return currentTableHeaders !== totalColumns;
|
|
254
|
+
const typedColumnNames = typedThs
|
|
255
|
+
.map((th) => th.getAttribute('data-kt-datatable-column'))
|
|
256
|
+
.filter((name): name is string => Boolean(name));
|
|
257
|
+
const dataColumnKeys = Object.keys(originalData[0]);
|
|
258
|
+
const matchingTypedColumns = typedColumnNames.filter((name) =>
|
|
259
|
+
dataColumnKeys.includes(name),
|
|
260
|
+
).length;
|
|
261
|
+
|
|
262
|
+
return typedColumnNames.length !== matchingTypedColumns;
|
|
192
263
|
}
|
|
193
264
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
KTDataTablePaginationRenderer,
|
|
9
9
|
KTDataTablePaginationRendererInput,
|
|
10
10
|
} from './datatable-contracts';
|
|
11
|
+
import { DEFAULT_PAGE_MORE_LIMIT, DEFAULT_PAGE_SIZES } from './datatable-defaults';
|
|
11
12
|
|
|
12
13
|
export class KTDataTableDomPaginationRenderer implements KTDataTablePaginationRenderer {
|
|
13
14
|
public render(
|
|
@@ -26,6 +27,7 @@ export class KTDataTableDomPaginationRenderer implements KTDataTablePaginationRe
|
|
|
26
27
|
return () => {
|
|
27
28
|
if (input.sizeElement) {
|
|
28
29
|
input.sizeElement.onchange = null;
|
|
30
|
+
this.removeChildElements(input.sizeElement);
|
|
29
31
|
}
|
|
30
32
|
if (input.paginationElement) {
|
|
31
33
|
this.removeChildElements(input.paginationElement);
|
|
@@ -50,19 +52,16 @@ export class KTDataTableDomPaginationRenderer implements KTDataTablePaginationRe
|
|
|
50
52
|
return;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
const pageSizes = input.config.pageSizes ??
|
|
55
|
+
const pageSizes = input.config.pageSizes ?? DEFAULT_PAGE_SIZES;
|
|
56
|
+
const options = pageSizes.map((size: number) => {
|
|
57
|
+
const option = document.createElement('option') as HTMLOptionElement;
|
|
58
|
+
option.value = String(size);
|
|
59
|
+
option.text = String(size);
|
|
60
|
+
option.selected = input.state.pageSize === size;
|
|
61
|
+
return option;
|
|
62
|
+
});
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
const options = pageSizes.map((size: number) => {
|
|
57
|
-
const option = document.createElement('option') as HTMLOptionElement;
|
|
58
|
-
option.value = String(size);
|
|
59
|
-
option.text = String(size);
|
|
60
|
-
option.selected = input.state.pageSize === size;
|
|
61
|
-
return option;
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
input.sizeElement.append(...options);
|
|
65
|
-
}, 100);
|
|
64
|
+
input.sizeElement.append(...options);
|
|
66
65
|
|
|
67
66
|
input.sizeElement.onchange = (event: Event) => {
|
|
68
67
|
input.reloadPageSize(
|
|
@@ -92,8 +91,7 @@ export class KTDataTableDomPaginationRenderer implements KTDataTablePaginationRe
|
|
|
92
91
|
return;
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
const infoTemplate =
|
|
96
|
-
input.config.info ?? '{start}-{end} of {total}';
|
|
94
|
+
const infoTemplate = input.config.info ?? '{start}-{end} of {total}';
|
|
97
95
|
input.infoElement.textContent = infoTemplate
|
|
98
96
|
.replace(
|
|
99
97
|
'{start}',
|
|
@@ -120,7 +118,7 @@ export class KTDataTableDomPaginationRenderer implements KTDataTablePaginationRe
|
|
|
120
118
|
|
|
121
119
|
const { page: currentPage, totalPages } = input.state;
|
|
122
120
|
const { previous, next, number, more } = pagination;
|
|
123
|
-
const pageMoreLimit = input.config.pageMoreLimit ??
|
|
121
|
+
const pageMoreLimit = input.config.pageMoreLimit ?? DEFAULT_PAGE_MORE_LIMIT;
|
|
124
122
|
|
|
125
123
|
const createButton = (
|
|
126
124
|
text: string,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import KTData from '../../helpers/data';
|
|
7
|
+
|
|
8
|
+
type DataTableInstance = {
|
|
9
|
+
dispose(): void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface KTDataTableRegistry<T extends DataTableInstance> {
|
|
13
|
+
register(element: HTMLElement, instance: T): void;
|
|
14
|
+
get(element: HTMLElement): T | undefined;
|
|
15
|
+
remove(element: HTMLElement): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
createAll(factory: (element: HTMLElement) => T): void;
|
|
18
|
+
reinit(factory: (element: HTMLElement) => T): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createDataTableRegistry<
|
|
22
|
+
T extends DataTableInstance,
|
|
23
|
+
>(): KTDataTableRegistry<T> {
|
|
24
|
+
type ElementWithInstance = HTMLElement & { instance?: T };
|
|
25
|
+
const instances = new Map<HTMLElement, T>();
|
|
26
|
+
|
|
27
|
+
function toElementWithInstance(element: HTMLElement): ElementWithInstance {
|
|
28
|
+
return element as ElementWithInstance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function register(element: HTMLElement, instance: T): void {
|
|
32
|
+
instances.set(element, instance);
|
|
33
|
+
toElementWithInstance(element).instance = instance;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function get(element: HTMLElement): T | undefined {
|
|
37
|
+
return instances.get(element) ?? toElementWithInstance(element).instance;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function remove(element: HTMLElement): void {
|
|
41
|
+
instances.delete(element);
|
|
42
|
+
const el = toElementWithInstance(element);
|
|
43
|
+
if (el.instance) delete el.instance;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function clear(): void {
|
|
47
|
+
instances.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createAll(factory: (element: HTMLElement) => T): void {
|
|
51
|
+
if (typeof document === 'undefined') return;
|
|
52
|
+
const elements = document.querySelectorAll<HTMLElement>(
|
|
53
|
+
'[data-kt-datatable="true"]',
|
|
54
|
+
);
|
|
55
|
+
elements.forEach((element) => {
|
|
56
|
+
if (
|
|
57
|
+
element.hasAttribute('data-kt-datatable') &&
|
|
58
|
+
!element.classList.contains('datatable-initialized')
|
|
59
|
+
) {
|
|
60
|
+
const instance = factory(element);
|
|
61
|
+
register(element, instance);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function reinit(factory: (element: HTMLElement) => T): void {
|
|
67
|
+
if (typeof document === 'undefined') return;
|
|
68
|
+
const elements = document.querySelectorAll<HTMLElement>(
|
|
69
|
+
'[data-kt-datatable="true"]',
|
|
70
|
+
);
|
|
71
|
+
elements.forEach((element) => {
|
|
72
|
+
try {
|
|
73
|
+
const instance = get(element);
|
|
74
|
+
if (instance && typeof instance.dispose === 'function') {
|
|
75
|
+
instance.dispose();
|
|
76
|
+
}
|
|
77
|
+
KTData.remove(element, 'datatable');
|
|
78
|
+
element.removeAttribute('data-kt-datatable-initialized');
|
|
79
|
+
element.classList.remove('datatable-initialized');
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore per-element errors
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
clear();
|
|
85
|
+
createAll(factory);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { register, get, remove, clear, createAll, reinit };
|
|
89
|
+
}
|
|
@@ -61,7 +61,7 @@ export class KTDataTableRemoteDataProvider<
|
|
|
61
61
|
try {
|
|
62
62
|
responseData = await response.json();
|
|
63
63
|
} catch (error) {
|
|
64
|
-
this.options.eventAdapter.emit('
|
|
64
|
+
this.options.eventAdapter.emit('fetchError', {
|
|
65
65
|
response,
|
|
66
66
|
error: String(error),
|
|
67
67
|
status: response.status,
|
|
@@ -74,8 +74,6 @@ export class KTDataTableRemoteDataProvider<
|
|
|
74
74
|
return { data: [], totalItems: 0, skipped: true };
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
this.options.eventAdapter.emit('fetched', { response: responseData });
|
|
78
|
-
|
|
79
77
|
if (typeof this.options.config.mapResponse === 'function') {
|
|
80
78
|
responseData = this.options.config.mapResponse.call(this, responseData);
|
|
81
79
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Search binding utilities for KTDataTable.
|
|
8
|
+
* Manages debounced search input binding and cleanup.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type SearchElementWithDebounce = HTMLInputElement & {
|
|
12
|
+
_debouncedSearch?: EventListener;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export interface KTDataTableSearchHandler {
|
|
16
|
+
attach(
|
|
17
|
+
tableId: string,
|
|
18
|
+
currentSearch: string | object | undefined,
|
|
19
|
+
delay: number,
|
|
20
|
+
onSearch: (query: string) => void,
|
|
21
|
+
): void;
|
|
22
|
+
detach(tableId: string): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createSearchHandler(): KTDataTableSearchHandler {
|
|
26
|
+
function findSearchElement(tableId: string): HTMLInputElement | null {
|
|
27
|
+
return document.querySelector<HTMLInputElement>(
|
|
28
|
+
`[data-kt-datatable-search="#${tableId}"]`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function asSearchElementWithDebounce(
|
|
33
|
+
element: HTMLInputElement,
|
|
34
|
+
): SearchElementWithDebounce {
|
|
35
|
+
return element as SearchElementWithDebounce;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function debounce<TArgs extends unknown[]>(
|
|
39
|
+
func: (...args: TArgs) => void,
|
|
40
|
+
wait: number,
|
|
41
|
+
): (...args: TArgs) => void {
|
|
42
|
+
let timeout: number | undefined;
|
|
43
|
+
return function (...args: TArgs) {
|
|
44
|
+
const later = () => {
|
|
45
|
+
clearTimeout(timeout);
|
|
46
|
+
func(...args);
|
|
47
|
+
};
|
|
48
|
+
clearTimeout(timeout);
|
|
49
|
+
timeout = window.setTimeout(later, wait);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function attach(
|
|
54
|
+
tableId: string,
|
|
55
|
+
currentSearch: string | object | undefined,
|
|
56
|
+
delay: number,
|
|
57
|
+
onSearch: (query: string) => void,
|
|
58
|
+
): void {
|
|
59
|
+
const searchElement = findSearchElement(tableId);
|
|
60
|
+
if (!searchElement) return;
|
|
61
|
+
|
|
62
|
+
// Restore search value from state
|
|
63
|
+
if (currentSearch !== undefined && currentSearch !== null) {
|
|
64
|
+
searchElement.value =
|
|
65
|
+
typeof currentSearch === 'string'
|
|
66
|
+
? currentSearch
|
|
67
|
+
: String(currentSearch);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Remove existing debounced listener if any
|
|
71
|
+
const el = asSearchElementWithDebounce(searchElement);
|
|
72
|
+
if (el._debouncedSearch) {
|
|
73
|
+
searchElement.removeEventListener('keyup', el._debouncedSearch);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Create and attach new debounced search
|
|
77
|
+
const debouncedSearch = debounce(() => {
|
|
78
|
+
onSearch(searchElement.value);
|
|
79
|
+
}, delay);
|
|
80
|
+
|
|
81
|
+
el._debouncedSearch = debouncedSearch;
|
|
82
|
+
searchElement.addEventListener('keyup', debouncedSearch);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function detach(tableId: string): void {
|
|
86
|
+
const searchElement = findSearchElement(tableId);
|
|
87
|
+
if (!searchElement) return;
|
|
88
|
+
|
|
89
|
+
const el = asSearchElementWithDebounce(searchElement);
|
|
90
|
+
if (el._debouncedSearch) {
|
|
91
|
+
searchElement.removeEventListener('keyup', el._debouncedSearch);
|
|
92
|
+
delete el._debouncedSearch;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { attach, detach };
|
|
97
|
+
}
|