@keenthemes/ktui 1.2.6 → 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 +3775 -2298
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +25 -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.map +1 -1
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +11 -1
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +80 -24
- 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 +3 -2
- 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 +26 -34
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +155 -492
- 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 +100 -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.map +1 -1
- package/lib/esm/components/datatable/datatable-layout-plugin.js +11 -1
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +80 -24
- 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 +3 -2
- 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 +26 -34
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +157 -494
- 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 +100 -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__/multi-row-headers.test.ts +7 -7
- package/src/components/datatable/__tests__/pagination-reset.test.ts +129 -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 +144 -145
- 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 +11 -1
- package/src/components/datatable/datatable-local-provider.ts +91 -28
- package/src/components/datatable/datatable-pagination-renderer.ts +3 -2
- 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.ts +191 -580
- package/src/components/datatable/index.ts +3 -0
- package/src/components/datatable/types.ts +124 -23
- package/src/helpers/__tests__/dom.test.ts +776 -0
- package/src/helpers/__tests__/utils.test.ts +332 -0
- package/src/index.ts +10 -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
|
@@ -13,16 +13,18 @@ import {
|
|
|
13
13
|
KTDataTableColumnFilterInterface,
|
|
14
14
|
KTDataTableLayoutPluginContextInterface,
|
|
15
15
|
KTDataTableLayoutPluginInterface,
|
|
16
|
+
OriginalTableClasses,
|
|
16
17
|
} from './types';
|
|
17
18
|
import { KTOptionType } from '../../types';
|
|
18
|
-
import KTComponents from '../../index';
|
|
19
19
|
import KTData from '../../helpers/data';
|
|
20
20
|
import {
|
|
21
|
-
|
|
21
|
+
KTDataTableCheckboxHandler,
|
|
22
22
|
KTDataTableCheckboxAPI,
|
|
23
23
|
} from './datatable-checkbox';
|
|
24
|
-
import {
|
|
24
|
+
import { KTDataTableSortHandler, KTDataTableSortAPI } from './datatable-sort';
|
|
25
25
|
import { createStickyLayoutPlugin } from './datatable-layout-plugin';
|
|
26
|
+
import { DATATABLE_DEFAULTS, DEFAULT_PAGE_SIZES, DEFAULT_SEARCH_DELAY } from './datatable-defaults';
|
|
27
|
+
import { getLogicalColumnCount } from './datatable-column-utils';
|
|
26
28
|
import {
|
|
27
29
|
KTDataTableCleanup,
|
|
28
30
|
KTDataTableEventAdapter,
|
|
@@ -30,13 +32,20 @@ import {
|
|
|
30
32
|
KTDataTableStateStore,
|
|
31
33
|
KTDataTableTableRenderer,
|
|
32
34
|
} from './datatable-contracts';
|
|
33
|
-
import { createDataTableEventAdapter } from './datatable-event-adapter';
|
|
34
35
|
import { KTDataTableLocalDataProvider } from './datatable-local-provider';
|
|
35
36
|
import { KTDataTableRemoteDataProvider } from './datatable-remote-provider';
|
|
36
37
|
import { KTDataTableConfigStateStore } from './datatable-state-store';
|
|
37
38
|
import { KTDataTableDomPaginationRenderer } from './datatable-pagination-renderer';
|
|
38
39
|
import { KTDataTableDomTableRenderer } from './datatable-table-renderer';
|
|
39
40
|
import KTUtils from '../../helpers/utils';
|
|
41
|
+
import { createSearchHandler } from './datatable-search-handler';
|
|
42
|
+
import {
|
|
43
|
+
createStatePersistence,
|
|
44
|
+
resolveTableNamespace,
|
|
45
|
+
} from './datatable-state-persistence';
|
|
46
|
+
import { createSpinner } from './datatable-spinner';
|
|
47
|
+
import { createDataTableRegistry } from './datatable-registry';
|
|
48
|
+
import { stripHtml } from './datatable-utils';
|
|
40
49
|
|
|
41
50
|
/**
|
|
42
51
|
* Custom DataTable plugin class with server-side API, pagination, and sorting
|
|
@@ -47,24 +56,14 @@ import KTUtils from '../../helpers/utils';
|
|
|
47
56
|
* @param {HTMLElement} element The table element
|
|
48
57
|
* @param {KTDataTableConfigInterface} [config] Additional configuration options
|
|
49
58
|
*/
|
|
59
|
+
const datatableRegistry = createDataTableRegistry<
|
|
60
|
+
KTDataTable<KTDataTableDataInterface>
|
|
61
|
+
>();
|
|
62
|
+
|
|
50
63
|
export class KTDataTable<T extends KTDataTableDataInterface>
|
|
51
64
|
extends KTComponent
|
|
52
65
|
implements KTDataTableInterface
|
|
53
66
|
{
|
|
54
|
-
private static asElementWithInstance(element: HTMLElement): HTMLElement & {
|
|
55
|
-
instance?: KTDataTable<KTDataTableDataInterface>;
|
|
56
|
-
} {
|
|
57
|
-
return element as HTMLElement & {
|
|
58
|
-
instance?: KTDataTable<KTDataTableDataInterface>;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private static asSearchElementWithDebounce(
|
|
63
|
-
element: HTMLInputElement,
|
|
64
|
-
): HTMLInputElement & { _debouncedSearch?: EventListener } {
|
|
65
|
-
return element as HTMLInputElement & { _debouncedSearch?: EventListener };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
67
|
protected override _name: string = 'datatable';
|
|
69
68
|
protected override _config: KTDataTableConfigInterface;
|
|
70
69
|
protected override _defaultConfig: KTDataTableConfigInterface;
|
|
@@ -72,11 +71,13 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
72
71
|
private _tableElement: HTMLTableElement;
|
|
73
72
|
private _tbodyElement: HTMLTableSectionElement;
|
|
74
73
|
private _theadElement: HTMLTableSectionElement;
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
private _originalClasses: OriginalTableClasses = {
|
|
75
|
+
tbody: '',
|
|
76
|
+
thead: '',
|
|
77
|
+
tr: [],
|
|
78
|
+
td: [],
|
|
79
|
+
th: [],
|
|
80
|
+
};
|
|
80
81
|
|
|
81
82
|
private _infoElement: HTMLElement | null = null;
|
|
82
83
|
private _sizeElement: HTMLSelectElement | null = null;
|
|
@@ -93,6 +94,10 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
93
94
|
private _paginationRenderer: KTDataTablePaginationRenderer;
|
|
94
95
|
private _cleanupCallbacks: KTDataTableCleanup[] = [];
|
|
95
96
|
|
|
97
|
+
private _searchHandler = createSearchHandler();
|
|
98
|
+
private _statePersistence = createStatePersistence();
|
|
99
|
+
private _spinner = createSpinner();
|
|
100
|
+
|
|
96
101
|
private _data: T[] = [];
|
|
97
102
|
private _isFetching: boolean = false;
|
|
98
103
|
|
|
@@ -120,13 +125,14 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
120
125
|
this._buildConfig();
|
|
121
126
|
this._normalizePageSizeConfig();
|
|
122
127
|
this._stateStore = new KTDataTableConfigStateStore(this._config);
|
|
123
|
-
this._eventAdapter =
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
this._eventAdapter = {
|
|
129
|
+
emit: (eventName: string, eventData?: object) => {
|
|
130
|
+
this._emit(eventName, eventData);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
127
133
|
|
|
128
134
|
// Store the instance directly on the element
|
|
129
|
-
|
|
135
|
+
datatableRegistry.register(element, this);
|
|
130
136
|
|
|
131
137
|
this._initElements();
|
|
132
138
|
this._layoutPlugin = this._createLayoutPlugin();
|
|
@@ -135,27 +141,32 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
135
141
|
this._initDataProviders();
|
|
136
142
|
|
|
137
143
|
// Initialize checkbox handler
|
|
138
|
-
this._checkbox =
|
|
144
|
+
this._checkbox = new KTDataTableCheckboxHandler(
|
|
139
145
|
this._element,
|
|
140
146
|
this._config,
|
|
141
147
|
this._emit.bind(this),
|
|
148
|
+
{
|
|
149
|
+
getState: () => this._stateStore.getState(),
|
|
150
|
+
setSelectedRows: (rows) => {
|
|
151
|
+
this._stateStore.patchState({ selectedRows: rows });
|
|
152
|
+
},
|
|
153
|
+
},
|
|
142
154
|
);
|
|
143
155
|
|
|
144
156
|
// Initialize sort handler
|
|
145
|
-
this._sortHandler =
|
|
146
|
-
this._config,
|
|
147
|
-
this._theadElement,
|
|
148
|
-
() => ({
|
|
157
|
+
this._sortHandler = new KTDataTableSortHandler({
|
|
158
|
+
config: this._config,
|
|
159
|
+
theadElement: this._theadElement,
|
|
160
|
+
getState: () => ({
|
|
149
161
|
sortField: this.getState().sortField,
|
|
150
162
|
sortOrder: this.getState().sortOrder,
|
|
151
163
|
}),
|
|
152
|
-
(field, order) => {
|
|
164
|
+
setState: (field, order) => {
|
|
153
165
|
this._stateStore.setSort(field as never, order);
|
|
154
166
|
},
|
|
155
|
-
this.
|
|
156
|
-
this.
|
|
157
|
-
|
|
158
|
-
);
|
|
167
|
+
emit: this._emit.bind(this),
|
|
168
|
+
updateData: this._updateData.bind(this),
|
|
169
|
+
});
|
|
159
170
|
|
|
160
171
|
this._sortHandler.initSort();
|
|
161
172
|
|
|
@@ -169,12 +180,11 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
this._updateData();
|
|
172
|
-
|
|
173
|
-
this._emit('init');
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
private _emit(eventName: string, eventData?: object): void {
|
|
177
|
-
this.
|
|
186
|
+
this._fireEvent(eventName, eventData);
|
|
187
|
+
this._dispatchEvent(`kt.datatable.${eventName}`, eventData);
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
private _initDataProviders(): void {
|
|
@@ -229,7 +239,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
229
239
|
.map((size) => Number(size))
|
|
230
240
|
.filter((size) => Number.isFinite(size) && size > 0)
|
|
231
241
|
.map((size) => Math.floor(size));
|
|
232
|
-
const fallbackPageSizes = [
|
|
242
|
+
const fallbackPageSizes: number[] = [...DEFAULT_PAGE_SIZES];
|
|
233
243
|
this._config.pageSizes =
|
|
234
244
|
pageSizes.length > 0 ? Array.from(new Set(pageSizes)) : fallbackPageSizes;
|
|
235
245
|
|
|
@@ -267,117 +277,43 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
267
277
|
* @param config User-provided configuration options
|
|
268
278
|
* @returns Default configuration merged with user-provided options
|
|
269
279
|
*/
|
|
280
|
+
private _createDefaultSearchCallback(): (
|
|
281
|
+
data: KTDataTableDataInterface[],
|
|
282
|
+
search: string,
|
|
283
|
+
) => KTDataTableDataInterface[] {
|
|
284
|
+
return ((data: T[], search: string): T[] => {
|
|
285
|
+
if (!data || !search) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
const searchLower = search.toLowerCase();
|
|
289
|
+
return data.filter((item: T) => {
|
|
290
|
+
if (!item) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
return Object.values(item).some((value: KTOptionType) => {
|
|
294
|
+
if (
|
|
295
|
+
typeof value !== 'string' &&
|
|
296
|
+
typeof value !== 'number' &&
|
|
297
|
+
typeof value !== 'boolean'
|
|
298
|
+
) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
const valueText = stripHtml(value).toLowerCase();
|
|
302
|
+
return valueText.includes(searchLower);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}) as unknown as (data: KTDataTableDataInterface[], search: string) => KTDataTableDataInterface[];
|
|
306
|
+
}
|
|
307
|
+
|
|
270
308
|
private _initDefaultConfig(
|
|
271
309
|
config?: KTDataTableConfigInterface,
|
|
272
310
|
): KTDataTableConfigInterface {
|
|
273
311
|
return {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
requestMethod: 'GET',
|
|
278
|
-
/**
|
|
279
|
-
* Custom HTTP headers for the API request
|
|
280
|
-
*/
|
|
281
|
-
requestHeaders: {
|
|
282
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
283
|
-
},
|
|
284
|
-
/**
|
|
285
|
-
* Pagination info template
|
|
286
|
-
*/
|
|
287
|
-
info: '{start}-{end} of {total}',
|
|
288
|
-
/**
|
|
289
|
-
* Info text when there is no data
|
|
290
|
-
*/
|
|
291
|
-
infoEmpty: 'No records found',
|
|
292
|
-
/**
|
|
293
|
-
* Available page sizes
|
|
294
|
-
*/
|
|
295
|
-
pageSizes: [5, 10, 20, 30, 50],
|
|
296
|
-
/**
|
|
297
|
-
* Default page size
|
|
298
|
-
*/
|
|
299
|
-
pageSize: 10,
|
|
300
|
-
/**
|
|
301
|
-
* Enable or disable pagination more button
|
|
302
|
-
*/
|
|
303
|
-
pageMore: true,
|
|
304
|
-
/**
|
|
305
|
-
* Maximum number of pages before enabling pagination more button
|
|
306
|
-
*/
|
|
307
|
-
pageMoreLimit: 3,
|
|
308
|
-
/**
|
|
309
|
-
* Pagination button templates
|
|
310
|
-
*/
|
|
311
|
-
pagination: {
|
|
312
|
-
number: {
|
|
313
|
-
/**
|
|
314
|
-
* CSS classes to be added to the pagination button
|
|
315
|
-
*/
|
|
316
|
-
class: 'kt-datatable-pagination-button',
|
|
317
|
-
/**
|
|
318
|
-
* Text to be displayed in the pagination button
|
|
319
|
-
*/
|
|
320
|
-
text: '{page}',
|
|
321
|
-
},
|
|
322
|
-
previous: {
|
|
323
|
-
/**
|
|
324
|
-
* CSS classes to be added to the previous pagination button
|
|
325
|
-
*/
|
|
326
|
-
class: 'kt-datatable-pagination-button kt-datatable-pagination-prev',
|
|
327
|
-
/**
|
|
328
|
-
* Text to be displayed in the previous pagination button
|
|
329
|
-
*/
|
|
330
|
-
text: `
|
|
331
|
-
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
332
|
-
<path d="M8.86501 16.7882V12.8481H21.1459C21.3724 12.8481 21.5897 12.7581 21.7498 12.5979C21.91 12.4378 22 12.2205 22 11.994C22 11.7675 21.91 11.5503 21.7498 11.3901C21.5897 11.2299 21.3724 11.1399 21.1459 11.1399H8.86501V7.2112C8.86628 7.10375 8.83517 6.9984 8.77573 6.90887C8.7163 6.81934 8.63129 6.74978 8.53177 6.70923C8.43225 6.66869 8.32283 6.65904 8.21775 6.68155C8.11267 6.70405 8.0168 6.75766 7.94262 6.83541L2.15981 11.6182C2.1092 11.668 2.06901 11.7274 2.04157 11.7929C2.01413 11.8584 2 11.9287 2 11.9997C2 12.0707 2.01413 12.141 2.04157 12.2065C2.06901 12.272 2.1092 12.3314 2.15981 12.3812L7.94262 17.164C8.0168 17.2417 8.11267 17.2953 8.21775 17.3178C8.32283 17.3403 8.43225 17.3307 8.53177 17.2902C8.63129 17.2496 8.7163 17.18 8.77573 17.0905C8.83517 17.001 8.86628 16.8956 8.86501 16.7882Z" fill="currentColor"/>
|
|
333
|
-
</svg>
|
|
334
|
-
`,
|
|
335
|
-
},
|
|
336
|
-
next: {
|
|
337
|
-
/**
|
|
338
|
-
* CSS classes to be added to the next pagination button
|
|
339
|
-
*/
|
|
340
|
-
class: 'kt-datatable-pagination-button kt-datatable-pagination-next',
|
|
341
|
-
/**
|
|
342
|
-
* Text to be displayed in the next pagination button
|
|
343
|
-
*/
|
|
344
|
-
text: `
|
|
345
|
-
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
346
|
-
<path d="M15.135 7.21144V11.1516H2.85407C2.62756 11.1516 2.41032 11.2415 2.25015 11.4017C2.08998 11.5619 2 11.7791 2 12.0056C2 12.2321 2.08998 12.4494 2.25015 12.6096C2.41032 12.7697 2.62756 12.8597 2.85407 12.8597H15.135V16.7884C15.1337 16.8959 15.1648 17.0012 15.2243 17.0908C15.2837 17.1803 15.3687 17.2499 15.4682 17.2904C15.5677 17.3309 15.6772 17.3406 15.7822 17.3181C15.8873 17.2956 15.9832 17.242 16.0574 17.1642L21.8402 12.3814C21.8908 12.3316 21.931 12.2722 21.9584 12.2067C21.9859 12.1412 22 12.0709 22 11.9999C22 11.9289 21.9859 11.8586 21.9584 11.7931C21.931 11.7276 21.8908 11.6683 21.8402 11.6185L16.0574 6.83565C15.9832 6.75791 15.8873 6.70429 15.7822 6.68179C15.6772 6.65929 15.5677 6.66893 15.4682 6.70948C15.3687 6.75002 15.2837 6.81959 15.2243 6.90911C15.1648 6.99864 15.1337 7.10399 15.135 7.21144Z" fill="currentColor"/>
|
|
347
|
-
</svg>
|
|
348
|
-
`,
|
|
349
|
-
},
|
|
350
|
-
more: {
|
|
351
|
-
/**
|
|
352
|
-
* CSS classes to be added to the pagination more button
|
|
353
|
-
*/
|
|
354
|
-
class: 'kt-datatable-pagination-button kt-datatable-pagination-more',
|
|
355
|
-
/**
|
|
356
|
-
* Text to be displayed in the pagination more button
|
|
357
|
-
*/
|
|
358
|
-
text: '...',
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
/**
|
|
362
|
-
* Sorting options
|
|
363
|
-
*/
|
|
312
|
+
...DATATABLE_DEFAULTS,
|
|
313
|
+
// Per-instance state; DATATABLE_DEFAULTS._state is a shared singleton.
|
|
314
|
+
_state: {} as KTDataTableStateInterface,
|
|
364
315
|
sort: {
|
|
365
|
-
|
|
366
|
-
* CSS classes to be added to the sortable headers
|
|
367
|
-
*/
|
|
368
|
-
classes: {
|
|
369
|
-
base: 'kt-table-col',
|
|
370
|
-
asc: 'asc',
|
|
371
|
-
desc: 'desc',
|
|
372
|
-
},
|
|
373
|
-
/**
|
|
374
|
-
* Local sorting callback function
|
|
375
|
-
* Sorts the data array based on the sort field and order
|
|
376
|
-
* @param data Data array to be sorted
|
|
377
|
-
* @param sortField Property name of the data object to be sorted by
|
|
378
|
-
* @param sortOrder Sorting order (ascending or descending)
|
|
379
|
-
* @returns Sorted data array
|
|
380
|
-
*/
|
|
316
|
+
...DATATABLE_DEFAULTS.sort,
|
|
381
317
|
callback: (
|
|
382
318
|
data: T[],
|
|
383
319
|
sortField: keyof T | number,
|
|
@@ -389,110 +325,9 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
389
325
|
},
|
|
390
326
|
},
|
|
391
327
|
search: {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
* @default 500
|
|
395
|
-
*/
|
|
396
|
-
delay: 500, // ms
|
|
397
|
-
/**
|
|
398
|
-
* Local search callback function
|
|
399
|
-
* Filters the data array based on the search string
|
|
400
|
-
* @param data Data array to be filtered
|
|
401
|
-
* @param search Search string used to filter the data array
|
|
402
|
-
* @returns Filtered data array
|
|
403
|
-
*/
|
|
404
|
-
callback: (data: T[], search: string): T[] => {
|
|
405
|
-
if (!data || !search) {
|
|
406
|
-
return [];
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return data.filter((item: T) => {
|
|
410
|
-
if (!item) {
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return Object.values(item).some((value: KTOptionType) => {
|
|
415
|
-
if (
|
|
416
|
-
typeof value !== 'string' &&
|
|
417
|
-
typeof value !== 'number' &&
|
|
418
|
-
typeof value !== 'boolean'
|
|
419
|
-
) {
|
|
420
|
-
return false;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const valueText = String(value)
|
|
424
|
-
.replace(/<|>| /g, '')
|
|
425
|
-
.toLowerCase();
|
|
426
|
-
return valueText.includes(search.toLowerCase());
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
/**
|
|
432
|
-
* Loading spinner options
|
|
433
|
-
*/
|
|
434
|
-
loading: {
|
|
435
|
-
/**
|
|
436
|
-
* Template to be displayed during data fetching process
|
|
437
|
-
*/
|
|
438
|
-
template: `
|
|
439
|
-
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
|
440
|
-
<div class="kt-datatable-loading">
|
|
441
|
-
<svg class="animate-spin -ml-1 h-5 w-5 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
442
|
-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3"></circle>
|
|
443
|
-
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
444
|
-
</svg>
|
|
445
|
-
{content}
|
|
446
|
-
</div>
|
|
447
|
-
</div>
|
|
448
|
-
`,
|
|
449
|
-
/**
|
|
450
|
-
* Loading text to be displayed in the template
|
|
451
|
-
*/
|
|
452
|
-
content: 'Loading...',
|
|
328
|
+
...DATATABLE_DEFAULTS.search,
|
|
329
|
+
callback: this._createDefaultSearchCallback(),
|
|
453
330
|
},
|
|
454
|
-
/**
|
|
455
|
-
* Selectors of the elements to be targeted
|
|
456
|
-
*/
|
|
457
|
-
attributes: {
|
|
458
|
-
/**
|
|
459
|
-
* Data table element
|
|
460
|
-
*/
|
|
461
|
-
table: 'table[data-kt-datatable-table="true"]',
|
|
462
|
-
/**
|
|
463
|
-
* Pagination info element
|
|
464
|
-
*/
|
|
465
|
-
info: '[data-kt-datatable-info="true"]',
|
|
466
|
-
/**
|
|
467
|
-
* Page size dropdown element
|
|
468
|
-
*/
|
|
469
|
-
size: '[data-kt-datatable-size="true"]',
|
|
470
|
-
/**
|
|
471
|
-
* Pagination element
|
|
472
|
-
*/
|
|
473
|
-
pagination: '[data-kt-datatable-pagination="true"]',
|
|
474
|
-
/**
|
|
475
|
-
* Spinner element
|
|
476
|
-
*/
|
|
477
|
-
spinner: '[data-kt-datatable-spinner="true"]',
|
|
478
|
-
/**
|
|
479
|
-
* Checkbox element
|
|
480
|
-
*/
|
|
481
|
-
check: '[data-kt-datatable-check="true"]',
|
|
482
|
-
checkbox: '[data-kt-datatable-row-check="true"]',
|
|
483
|
-
},
|
|
484
|
-
/**
|
|
485
|
-
* Enable or disable state saving
|
|
486
|
-
*/
|
|
487
|
-
stateSave: true,
|
|
488
|
-
checkbox: {
|
|
489
|
-
checkedClass: 'checked',
|
|
490
|
-
},
|
|
491
|
-
/**
|
|
492
|
-
* Private properties
|
|
493
|
-
*/
|
|
494
|
-
_state: {} as KTDataTableStateInterface,
|
|
495
|
-
loadingClass: 'loading',
|
|
496
331
|
...config,
|
|
497
332
|
} as KTDataTableConfigInterface;
|
|
498
333
|
}
|
|
@@ -542,17 +377,17 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
542
377
|
private _storeOriginalClasses(): void {
|
|
543
378
|
// Store tbody class
|
|
544
379
|
if (this._tbodyElement) {
|
|
545
|
-
this.
|
|
380
|
+
this._originalClasses.tbody = this._tbodyElement.className || '';
|
|
546
381
|
}
|
|
547
382
|
|
|
548
383
|
// Store thead class and th classes
|
|
549
384
|
if (this._theadElement) {
|
|
550
|
-
this.
|
|
385
|
+
this._originalClasses.thead = this._theadElement.className || '';
|
|
551
386
|
|
|
552
387
|
// Store th classes
|
|
553
388
|
const thElements =
|
|
554
389
|
this._theadElement.querySelectorAll<HTMLTableCellElement>('th');
|
|
555
|
-
this.
|
|
390
|
+
this._originalClasses.th = Array.from(thElements).map(
|
|
556
391
|
(th) => th.className || '',
|
|
557
392
|
);
|
|
558
393
|
}
|
|
@@ -561,15 +396,15 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
561
396
|
if (this._tbodyElement) {
|
|
562
397
|
const originalRows =
|
|
563
398
|
this._tbodyElement.querySelectorAll<HTMLTableRowElement>('tr');
|
|
564
|
-
this.
|
|
399
|
+
this._originalClasses.tr = Array.from(originalRows).map(
|
|
565
400
|
(row) => row.className || '',
|
|
566
401
|
);
|
|
567
402
|
|
|
568
403
|
// Store td classes as a 2D array
|
|
569
|
-
this.
|
|
404
|
+
this._originalClasses.td = [];
|
|
570
405
|
Array.from(originalRows).forEach((row, rowIndex) => {
|
|
571
406
|
const tdElements = row.querySelectorAll<HTMLTableCellElement>('td');
|
|
572
|
-
this.
|
|
407
|
+
this._originalClasses.td[rowIndex] = Array.from(tdElements).map(
|
|
573
408
|
(td) => td.className || '',
|
|
574
409
|
);
|
|
575
410
|
});
|
|
@@ -584,9 +419,8 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
584
419
|
if (this._isFetching) return; // Prevent duplicate fetches
|
|
585
420
|
this._isFetching = true;
|
|
586
421
|
try {
|
|
587
|
-
this.
|
|
422
|
+
this._spinner.show(this._element, this._config, this._tableElement); // Show spinner before fetching data
|
|
588
423
|
|
|
589
|
-
this._emit('fetch');
|
|
590
424
|
const result =
|
|
591
425
|
typeof this._config.apiEndpoint === 'undefined'
|
|
592
426
|
? this._localProvider.fetchSync()
|
|
@@ -596,10 +430,11 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
596
430
|
this._data = result.data;
|
|
597
431
|
this._stateStore.patchState({ totalItems: result.totalItems });
|
|
598
432
|
await this._draw();
|
|
599
|
-
this._emit('fetched');
|
|
600
433
|
}
|
|
601
434
|
|
|
602
435
|
await this._finalize();
|
|
436
|
+
|
|
437
|
+
this._emit('update');
|
|
603
438
|
} finally {
|
|
604
439
|
// Finally block now correctly executes after promises resolve, not immediately
|
|
605
440
|
this._isFetching = false;
|
|
@@ -621,63 +456,29 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
621
456
|
this._sortHandler.initSort();
|
|
622
457
|
}
|
|
623
458
|
|
|
624
|
-
this.
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
459
|
+
this._searchHandler.attach(
|
|
460
|
+
this._tableId(),
|
|
461
|
+
this.getState().search,
|
|
462
|
+
this._config.search?.delay ?? DEFAULT_SEARCH_DELAY,
|
|
463
|
+
(query) => this.search(query),
|
|
464
|
+
);
|
|
629
465
|
|
|
630
466
|
/**
|
|
631
467
|
* Hide spinner
|
|
632
468
|
*/
|
|
633
|
-
this.
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Attach search event to the search input element
|
|
638
|
-
* @returns {void}
|
|
639
|
-
*/
|
|
640
|
-
private _attachSearchEvent(): void {
|
|
641
|
-
const tableId: string = this._tableId();
|
|
642
|
-
const searchElement: HTMLInputElement | null =
|
|
643
|
-
document.querySelector<HTMLInputElement>(
|
|
644
|
-
`[data-kt-datatable-search="#${tableId}"]`,
|
|
645
|
-
);
|
|
646
|
-
|
|
647
|
-
// Get search state
|
|
648
|
-
const { search } = this.getState();
|
|
649
|
-
// Set search value
|
|
650
|
-
if (searchElement) {
|
|
651
|
-
searchElement.value =
|
|
652
|
-
search === undefined || search === null
|
|
653
|
-
? ''
|
|
654
|
-
: typeof search === 'string'
|
|
655
|
-
? search
|
|
656
|
-
: String(search);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
if (searchElement) {
|
|
660
|
-
// Check if a debounced search function already exists
|
|
661
|
-
const searchWithDebounce =
|
|
662
|
-
KTDataTable.asSearchElementWithDebounce(searchElement);
|
|
663
|
-
if (searchWithDebounce._debouncedSearch) {
|
|
664
|
-
// Remove the existing debounced event listener
|
|
665
|
-
searchElement.removeEventListener(
|
|
666
|
-
'keyup',
|
|
667
|
-
searchWithDebounce._debouncedSearch,
|
|
668
|
-
);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Create a new debounced search function
|
|
672
|
-
const debouncedSearch = this._debounce(() => {
|
|
673
|
-
this.search(searchElement.value);
|
|
674
|
-
}, this._config.search?.delay ?? 500);
|
|
469
|
+
this._spinner.hide(this._element, this._config);
|
|
675
470
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
471
|
+
// Update content checksum AFTER all DOM modifications (checkbox init
|
|
472
|
+
// adds checked-class to <tr> elements which changes tbody innerHTML).
|
|
473
|
+
// If we save the checksum earlier (in _draw), the next fetchSync()
|
|
474
|
+
// sees a mismatch, re-extracts from the DOM, and loses rows that
|
|
475
|
+
// were on other pages — making pagination show empty.
|
|
476
|
+
if (!this._config.apiEndpoint) {
|
|
477
|
+
this._stateStore.patchState({
|
|
478
|
+
_contentChecksum: KTUtils.checksum(
|
|
479
|
+
JSON.stringify(this._tbodyElement.innerHTML),
|
|
480
|
+
),
|
|
481
|
+
});
|
|
681
482
|
}
|
|
682
483
|
}
|
|
683
484
|
|
|
@@ -687,18 +488,11 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
687
488
|
* @returns {number} Number of data columns, or 0 if unknown
|
|
688
489
|
*/
|
|
689
490
|
private _getLogicalColumnCount(): number {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const firstRow =
|
|
696
|
-
this._tbodyElement.querySelector<HTMLTableRowElement>('tr');
|
|
697
|
-
if (firstRow) {
|
|
698
|
-
return firstRow.querySelectorAll<HTMLTableCellElement>('td').length;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
return 0;
|
|
491
|
+
return getLogicalColumnCount(
|
|
492
|
+
this._theadElement,
|
|
493
|
+
this._tbodyElement,
|
|
494
|
+
this.getState().originalData as Array<Record<string, unknown>> | undefined,
|
|
495
|
+
);
|
|
702
496
|
}
|
|
703
497
|
|
|
704
498
|
/**
|
|
@@ -788,9 +582,8 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
788
582
|
this._stateStore.patchState({ totalPages, page });
|
|
789
583
|
|
|
790
584
|
this._layoutPlugin?.beforeDraw?.(this._getLayoutPluginContext());
|
|
791
|
-
this._emit('draw');
|
|
792
585
|
|
|
793
|
-
this.
|
|
586
|
+
this._cleanupForRedraw();
|
|
794
587
|
|
|
795
588
|
// Update the table and pagination controls
|
|
796
589
|
if (this._theadElement && this._tbodyElement) {
|
|
@@ -802,15 +595,6 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
802
595
|
}
|
|
803
596
|
|
|
804
597
|
this._layoutPlugin?.afterDraw?.(this._getLayoutPluginContext());
|
|
805
|
-
if (!this._config.apiEndpoint) {
|
|
806
|
-
this._stateStore.patchState({
|
|
807
|
-
_contentChecksum: KTUtils.checksum(
|
|
808
|
-
JSON.stringify(this._tbodyElement.innerHTML),
|
|
809
|
-
),
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
this._emit('drew');
|
|
814
598
|
|
|
815
599
|
// Spinner is hidden in _finalize() to ensure it stays visible until the entire request completes
|
|
816
600
|
// Removed duplicate _hideSpinner() call here to prevent premature hiding
|
|
@@ -831,9 +615,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
831
615
|
data: this._data,
|
|
832
616
|
getLogicalColumnCount: this._getLogicalColumnCount.bind(this),
|
|
833
617
|
getState: this.getState.bind(this),
|
|
834
|
-
|
|
835
|
-
originalTrClasses: this._originalTrClasses,
|
|
836
|
-
originalTdClasses: this._originalTdClasses,
|
|
618
|
+
originalClasses: this._originalClasses,
|
|
837
619
|
tableElement: this._tableElement,
|
|
838
620
|
theadElement: this._theadElement,
|
|
839
621
|
});
|
|
@@ -892,77 +674,21 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
892
674
|
return;
|
|
893
675
|
}
|
|
894
676
|
|
|
895
|
-
this._emit('pagination', { page: page });
|
|
896
|
-
|
|
897
677
|
if (page >= 1 && page <= this.getState().totalPages) {
|
|
898
678
|
this._stateStore.setPage(page);
|
|
899
679
|
this._updateData();
|
|
900
680
|
}
|
|
901
681
|
}
|
|
902
682
|
|
|
903
|
-
// Method to show the loading spinner
|
|
904
|
-
private _showSpinner(): void {
|
|
905
|
-
const root = this._element;
|
|
906
|
-
const spinnerSel = this._config.attributes?.spinner;
|
|
907
|
-
const fromDom =
|
|
908
|
-
root && spinnerSel ? root.querySelector<HTMLElement>(spinnerSel) : null;
|
|
909
|
-
const spinner = fromDom ?? this._createSpinner();
|
|
910
|
-
if (spinner) {
|
|
911
|
-
spinner.style.display = 'block';
|
|
912
|
-
}
|
|
913
|
-
root?.classList.add(this._config.loadingClass ?? 'loading');
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Method to hide the loading spinner
|
|
917
|
-
private _hideSpinner(): void {
|
|
918
|
-
const root = this._element;
|
|
919
|
-
const spinnerSel = this._config.attributes?.spinner;
|
|
920
|
-
const spinner =
|
|
921
|
-
root && spinnerSel ? root.querySelector<HTMLElement>(spinnerSel) : null;
|
|
922
|
-
if (spinner) {
|
|
923
|
-
spinner.style.display = 'none';
|
|
924
|
-
}
|
|
925
|
-
root?.classList.remove(this._config.loadingClass ?? 'loading');
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
// Method to create a spinner element if it doesn't exist
|
|
929
|
-
private _createSpinner(): HTMLElement | null {
|
|
930
|
-
const loading = this._config.loading;
|
|
931
|
-
if (!loading) {
|
|
932
|
-
return null;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const template = document.createElement('template');
|
|
936
|
-
template.innerHTML = loading.template
|
|
937
|
-
.trim()
|
|
938
|
-
.replace('{content}', loading.content);
|
|
939
|
-
const first = template.content.firstChild;
|
|
940
|
-
if (!first || !(first instanceof HTMLElement)) {
|
|
941
|
-
return null;
|
|
942
|
-
}
|
|
943
|
-
const spinner = first;
|
|
944
|
-
spinner.setAttribute('data-kt-datatable-spinner', 'true');
|
|
945
|
-
|
|
946
|
-
this._tableElement.appendChild(spinner);
|
|
947
|
-
|
|
948
|
-
return spinner;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
683
|
/**
|
|
952
684
|
* Saves the current state of the table to local storage.
|
|
953
685
|
* @returns {void}
|
|
954
686
|
*/
|
|
955
687
|
private _saveState(): void {
|
|
956
|
-
this.
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (ns) {
|
|
961
|
-
localStorage.setItem(
|
|
962
|
-
ns,
|
|
963
|
-
JSON.stringify(this.getState() as KTDataTableStateInterface),
|
|
964
|
-
);
|
|
965
|
-
}
|
|
688
|
+
this._statePersistence.save(
|
|
689
|
+
this._tableNamespace(),
|
|
690
|
+
this.getState() as KTDataTableStateInterface,
|
|
691
|
+
);
|
|
966
692
|
}
|
|
967
693
|
|
|
968
694
|
/**
|
|
@@ -970,24 +696,16 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
970
696
|
* @returns {Object} The saved state of the table, or null if no saved state exists.
|
|
971
697
|
*/
|
|
972
698
|
private _loadState(): KTDataTableStateInterface | null {
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const state = JSON.parse(stateString) as KTDataTableStateInterface;
|
|
978
|
-
if (state) this._stateStore.replaceState(state);
|
|
979
|
-
return state;
|
|
980
|
-
} catch {}
|
|
981
|
-
|
|
982
|
-
return null;
|
|
699
|
+
const ns = this._tableNamespace();
|
|
700
|
+
const saved = this._statePersistence.load(ns);
|
|
701
|
+
if (saved) this._stateStore.replaceState(saved);
|
|
702
|
+
return saved;
|
|
983
703
|
}
|
|
984
704
|
|
|
985
705
|
private _deleteState(): void {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
localStorage.removeItem(ns);
|
|
990
|
-
}
|
|
706
|
+
this._statePersistence.remove(
|
|
707
|
+
this._tableNamespace(),
|
|
708
|
+
);
|
|
991
709
|
}
|
|
992
710
|
|
|
993
711
|
/**
|
|
@@ -999,13 +717,12 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
999
717
|
* @returns {string} The namespace for the table's state.
|
|
1000
718
|
*/
|
|
1001
719
|
private _tableNamespace(): string {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
return this._tableId() ?? this._name;
|
|
720
|
+
return resolveTableNamespace(
|
|
721
|
+
this._config,
|
|
722
|
+
this._tableElement,
|
|
723
|
+
this._element,
|
|
724
|
+
this._name,
|
|
725
|
+
);
|
|
1009
726
|
}
|
|
1010
727
|
|
|
1011
728
|
private _tableId(): string {
|
|
@@ -1024,109 +741,51 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1024
741
|
* Clean up all event listeners, handlers, and DOM nodes created by this instance.
|
|
1025
742
|
* This method is called before re-rendering or when disposing the component.
|
|
1026
743
|
*/
|
|
1027
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Clean up event listeners and DOM artifacts for a redraw cycle.
|
|
746
|
+
* Does NOT remove the instance from the registry — the datatable
|
|
747
|
+
* remains accessible via getInstance() during the redraw window.
|
|
748
|
+
*/
|
|
749
|
+
private _cleanupForRedraw(): void {
|
|
1028
750
|
this._layoutPlugin?.dispose?.(this._getLayoutPluginContext());
|
|
1029
751
|
|
|
1030
|
-
|
|
1031
|
-
if (!root) {
|
|
752
|
+
if (!this._element) {
|
|
1032
753
|
return;
|
|
1033
754
|
}
|
|
1034
755
|
|
|
1035
756
|
this._cleanupCallbacks.forEach((cleanup) => cleanup());
|
|
1036
757
|
this._cleanupCallbacks = [];
|
|
1037
758
|
|
|
1038
|
-
|
|
1039
|
-
const tableId: string = this._tableId();
|
|
1040
|
-
const searchElement: HTMLInputElement | null =
|
|
1041
|
-
document.querySelector<HTMLInputElement>(
|
|
1042
|
-
`[data-kt-datatable-search="#${tableId}"]`,
|
|
1043
|
-
);
|
|
1044
|
-
if (searchElement) {
|
|
1045
|
-
const searchWithDebounce =
|
|
1046
|
-
KTDataTable.asSearchElementWithDebounce(searchElement);
|
|
1047
|
-
if (searchWithDebounce._debouncedSearch) {
|
|
1048
|
-
searchElement.removeEventListener(
|
|
1049
|
-
'keyup',
|
|
1050
|
-
searchWithDebounce._debouncedSearch,
|
|
1051
|
-
);
|
|
1052
|
-
delete searchWithDebounce._debouncedSearch;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
759
|
+
this._searchHandler.detach(this._tableId());
|
|
1055
760
|
|
|
1056
|
-
// --- 2. Remove page size dropdown event listener ---
|
|
1057
761
|
if (this._sizeElement && this._sizeElement.onchange) {
|
|
1058
762
|
this._sizeElement.onchange = null;
|
|
1059
763
|
}
|
|
1060
764
|
|
|
1061
|
-
// --- 3. Remove all pagination button event listeners ---
|
|
1062
765
|
if (this._paginationElement) {
|
|
1063
|
-
// Remove all child nodes (buttons) to ensure no lingering listeners
|
|
1064
766
|
while (this._paginationElement.firstChild) {
|
|
1065
767
|
this._paginationElement.removeChild(this._paginationElement.firstChild);
|
|
1066
768
|
}
|
|
1067
769
|
}
|
|
1068
770
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
const checkboxWithDispose = this._checkbox as KTDataTableCheckboxAPI & {
|
|
1072
|
-
dispose?: () => void;
|
|
1073
|
-
};
|
|
1074
|
-
if (this._checkbox && typeof checkboxWithDispose.dispose === 'function') {
|
|
1075
|
-
checkboxWithDispose.dispose();
|
|
1076
|
-
} else {
|
|
1077
|
-
const checkSel = this._config.attributes?.check;
|
|
1078
|
-
if (checkSel) {
|
|
1079
|
-
const headerCheckElement =
|
|
1080
|
-
root.querySelector<HTMLInputElement>(checkSel);
|
|
1081
|
-
if (headerCheckElement) {
|
|
1082
|
-
headerCheckElement.replaceWith(headerCheckElement.cloneNode(true));
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
// KTDataTableSortAPI does not have a dispose method, but we can remove th click listeners by replacing them
|
|
1087
|
-
if (this._theadElement) {
|
|
1088
|
-
const ths = this._theadElement.querySelectorAll('th');
|
|
1089
|
-
ths.forEach((th) => {
|
|
1090
|
-
th.replaceWith(th.cloneNode(true));
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// --- 5. Remove spinner DOM node if it exists ---
|
|
1095
|
-
const spinnerSel = this._config.attributes?.spinner;
|
|
1096
|
-
if (spinnerSel) {
|
|
1097
|
-
const spinner = root.querySelector<HTMLElement>(spinnerSel);
|
|
1098
|
-
if (spinner?.parentNode) {
|
|
1099
|
-
spinner.parentNode.removeChild(spinner);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
root.classList.remove(this._config.loadingClass ?? 'loading');
|
|
1103
|
-
|
|
1104
|
-
// --- 6. Remove instance reference from the DOM element ---
|
|
1105
|
-
const elementWithInstance = KTDataTable.asElementWithInstance(root);
|
|
1106
|
-
if (elementWithInstance.instance) {
|
|
1107
|
-
delete elementWithInstance.instance;
|
|
1108
|
-
}
|
|
771
|
+
this._checkbox.dispose();
|
|
772
|
+
this._sortHandler.dispose();
|
|
1109
773
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
// --- 7. (Optional) Clear localStorage state ---
|
|
1113
|
-
// Uncomment the following line if you want to clear state on dispose:
|
|
1114
|
-
// this._deleteState();
|
|
774
|
+
this._spinner.remove(this._element, this._config);
|
|
1115
775
|
}
|
|
1116
776
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
};
|
|
777
|
+
/**
|
|
778
|
+
* Full disposal — cleans up listeners AND removes the instance from
|
|
779
|
+
* the registry. Only called when the component is being destroyed.
|
|
780
|
+
*/
|
|
781
|
+
private _dispose(): void {
|
|
782
|
+
this._cleanupForRedraw();
|
|
783
|
+
|
|
784
|
+
const root = this._element;
|
|
785
|
+
if (root) {
|
|
786
|
+
datatableRegistry.remove(root);
|
|
787
|
+
KTData.remove(root, this._name);
|
|
788
|
+
}
|
|
1130
789
|
}
|
|
1131
790
|
|
|
1132
791
|
/**
|
|
@@ -1139,16 +798,22 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1139
798
|
|
|
1140
799
|
/**
|
|
1141
800
|
* Sorts the data in the table by the specified field.
|
|
801
|
+
* When `order` is provided, applies that sort direction instead of toggling.
|
|
1142
802
|
* @param field The field to sort by.
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
803
|
+
* @param order Optional sort direction (`asc`, `desc`, or `''` to clear).
|
|
804
|
+
*/
|
|
805
|
+
public sort(
|
|
806
|
+
field: keyof T | number,
|
|
807
|
+
order?: KTDataTableSortOrderInterface,
|
|
808
|
+
): void {
|
|
809
|
+
const sortOrder =
|
|
810
|
+
order !== undefined
|
|
811
|
+
? order
|
|
812
|
+
: this._sortHandler.toggleSortOrder(
|
|
813
|
+
this.getState().sortField,
|
|
814
|
+
this.getState().sortOrder,
|
|
815
|
+
field,
|
|
816
|
+
);
|
|
1152
817
|
this._sortHandler.setSortIcon(field as keyof T, sortOrder);
|
|
1153
818
|
this._stateStore.setSort(field as never, sortOrder);
|
|
1154
819
|
this._emit('sort', { field, order: sortOrder });
|
|
@@ -1185,19 +850,15 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1185
850
|
}
|
|
1186
851
|
|
|
1187
852
|
/**
|
|
1188
|
-
* Reloads the data from the
|
|
1189
|
-
*
|
|
853
|
+
* Reloads the data from the source (API or DOM) and redraws the table.
|
|
854
|
+
* @returns {Promise<void>}
|
|
1190
855
|
*/
|
|
1191
856
|
public reload(): void {
|
|
1192
|
-
this._emit('reload');
|
|
1193
|
-
|
|
1194
857
|
// Fetch the data from the server using the current sort and filter settings
|
|
1195
858
|
this._updateData();
|
|
1196
859
|
}
|
|
1197
860
|
|
|
1198
861
|
public redraw(page: number = 1): void {
|
|
1199
|
-
this._emit('redraw');
|
|
1200
|
-
|
|
1201
862
|
this._paginateData(page);
|
|
1202
863
|
}
|
|
1203
864
|
|
|
@@ -1205,20 +866,14 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1205
866
|
* Show the loading spinner of the data table.
|
|
1206
867
|
*/
|
|
1207
868
|
public showSpinner(): void {
|
|
1208
|
-
|
|
1209
|
-
* Show the loading spinner of the data table.
|
|
1210
|
-
*/
|
|
1211
|
-
this._showSpinner();
|
|
869
|
+
this._spinner.show(this._element, this._config, this._tableElement);
|
|
1212
870
|
}
|
|
1213
871
|
|
|
1214
872
|
/**
|
|
1215
873
|
* Hide the loading spinner of the data table.
|
|
1216
874
|
*/
|
|
1217
875
|
public hideSpinner(): void {
|
|
1218
|
-
|
|
1219
|
-
* Hide the loading spinner of the data table.
|
|
1220
|
-
*/
|
|
1221
|
-
this._hideSpinner();
|
|
876
|
+
this._spinner.hide(this._element, this._config);
|
|
1222
877
|
}
|
|
1223
878
|
|
|
1224
879
|
/**
|
|
@@ -1243,37 +898,12 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1243
898
|
this.reload();
|
|
1244
899
|
}
|
|
1245
900
|
|
|
1246
|
-
/**
|
|
1247
|
-
* Static variables
|
|
1248
|
-
*/
|
|
1249
|
-
private static _instances = new Map<
|
|
1250
|
-
HTMLElement,
|
|
1251
|
-
KTDataTable<KTDataTableDataInterface>
|
|
1252
|
-
>();
|
|
1253
|
-
|
|
1254
901
|
/**
|
|
1255
902
|
* Create KTDataTable instances for all elements with a data-kt-datatable="true" attribute.
|
|
1256
903
|
* This function is now browser-guarded and must be called explicitly.
|
|
1257
904
|
*/
|
|
1258
905
|
public static createInstances(): void {
|
|
1259
|
-
|
|
1260
|
-
const elements = document.querySelectorAll<HTMLElement>(
|
|
1261
|
-
'[data-kt-datatable="true"]',
|
|
1262
|
-
);
|
|
1263
|
-
|
|
1264
|
-
elements.forEach((element) => {
|
|
1265
|
-
if (
|
|
1266
|
-
element.hasAttribute('data-kt-datatable') &&
|
|
1267
|
-
!element.classList.contains('datatable-initialized')
|
|
1268
|
-
) {
|
|
1269
|
-
/**
|
|
1270
|
-
* Create an instance of KTDataTable for the given element
|
|
1271
|
-
* @param element The element to create an instance for
|
|
1272
|
-
*/
|
|
1273
|
-
const instance = new KTDataTable(element);
|
|
1274
|
-
this._instances.set(element, instance);
|
|
1275
|
-
}
|
|
1276
|
-
});
|
|
906
|
+
datatableRegistry.createAll((el) => new KTDataTable(el));
|
|
1277
907
|
}
|
|
1278
908
|
|
|
1279
909
|
/**
|
|
@@ -1285,14 +915,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1285
915
|
public static getInstance(
|
|
1286
916
|
element: HTMLElement,
|
|
1287
917
|
): KTDataTable<KTDataTableDataInterface> | undefined {
|
|
1288
|
-
|
|
1289
|
-
const instanceFromMap = this._instances.get(element);
|
|
1290
|
-
if (instanceFromMap) {
|
|
1291
|
-
return instanceFromMap;
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
// Fallback to element's instance property (for manually created instances)
|
|
1295
|
-
return KTDataTable.asElementWithInstance(element).instance;
|
|
918
|
+
return datatableRegistry.get(element);
|
|
1296
919
|
}
|
|
1297
920
|
|
|
1298
921
|
/**
|
|
@@ -1300,7 +923,6 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1300
923
|
* This function is now browser-guarded and must be called explicitly.
|
|
1301
924
|
*/
|
|
1302
925
|
public static init(): void {
|
|
1303
|
-
if (typeof document === 'undefined') return;
|
|
1304
926
|
KTDataTable.createInstances();
|
|
1305
927
|
}
|
|
1306
928
|
|
|
@@ -1309,25 +931,7 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1309
931
|
* Useful for Livewire wire:navigate where the DOM is replaced and new tables need to be initialized.
|
|
1310
932
|
*/
|
|
1311
933
|
public static reinit(): void {
|
|
1312
|
-
|
|
1313
|
-
const elements = document.querySelectorAll<HTMLElement>(
|
|
1314
|
-
'[data-kt-datatable="true"]',
|
|
1315
|
-
);
|
|
1316
|
-
elements.forEach((element) => {
|
|
1317
|
-
try {
|
|
1318
|
-
const instance = KTDataTable.getInstance(element);
|
|
1319
|
-
if (instance && typeof instance.dispose === 'function') {
|
|
1320
|
-
instance.dispose();
|
|
1321
|
-
}
|
|
1322
|
-
KTData.remove(element, 'datatable');
|
|
1323
|
-
element.removeAttribute('data-kt-datatable-initialized');
|
|
1324
|
-
element.classList.remove('datatable-initialized');
|
|
1325
|
-
} catch {
|
|
1326
|
-
// ignore per-element errors
|
|
1327
|
-
}
|
|
1328
|
-
});
|
|
1329
|
-
KTDataTable._instances.clear();
|
|
1330
|
-
KTDataTable.createInstances();
|
|
934
|
+
datatableRegistry.reinit((el) => new KTDataTable(el));
|
|
1331
935
|
}
|
|
1332
936
|
|
|
1333
937
|
/**
|
|
@@ -1373,13 +977,20 @@ export class KTDataTable<T extends KTDataTableDataInterface>
|
|
|
1373
977
|
}
|
|
1374
978
|
|
|
1375
979
|
/**
|
|
1376
|
-
*
|
|
980
|
+
* Re-apply checkbox checked states to visible rows after a redraw or pagination change.
|
|
1377
981
|
* @returns {void}
|
|
1378
982
|
*/
|
|
1379
|
-
public
|
|
983
|
+
public refreshCheckboxes(): void {
|
|
1380
984
|
this._checkbox.updateState();
|
|
1381
985
|
}
|
|
1382
986
|
|
|
987
|
+
/**
|
|
988
|
+
* @deprecated Use {@link refreshCheckboxes} instead.
|
|
989
|
+
*/
|
|
990
|
+
public update(): void {
|
|
991
|
+
this.refreshCheckboxes();
|
|
992
|
+
}
|
|
993
|
+
|
|
1383
994
|
// Other plugin methods can be added here
|
|
1384
995
|
}
|
|
1385
996
|
|