@keenthemes/ktui 1.2.5 → 1.2.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.
Files changed (64) hide show
  1. package/dist/ktui.js +1965 -2690
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +60 -0
  5. package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
  6. package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
  7. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
  8. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  9. package/lib/cjs/components/datatable/datatable-layout-plugin.js +328 -0
  10. package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
  11. package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
  12. package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
  13. package/lib/cjs/components/datatable/datatable-local-provider.js +5 -3
  14. package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
  15. package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  16. package/lib/cjs/components/datatable/datatable-pagination-renderer.js +11 -12
  17. package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
  18. package/lib/cjs/components/datatable/datatable.d.ts +9 -0
  19. package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
  20. package/lib/cjs/components/datatable/datatable.js +90 -17
  21. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  22. package/lib/cjs/components/datatable/index.d.ts +1 -1
  23. package/lib/cjs/components/datatable/index.d.ts.map +1 -1
  24. package/lib/cjs/components/datatable/types.d.ts +27 -0
  25. package/lib/cjs/components/datatable/types.d.ts.map +1 -1
  26. package/lib/cjs/index.d.ts +1 -1
  27. package/lib/cjs/index.d.ts.map +1 -1
  28. package/lib/cjs/index.js.map +1 -1
  29. package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
  30. package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
  31. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
  32. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  33. package/lib/esm/components/datatable/datatable-layout-plugin.js +324 -0
  34. package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
  35. package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
  36. package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
  37. package/lib/esm/components/datatable/datatable-local-provider.js +5 -3
  38. package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
  39. package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  40. package/lib/esm/components/datatable/datatable-pagination-renderer.js +11 -12
  41. package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
  42. package/lib/esm/components/datatable/datatable.d.ts +9 -0
  43. package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
  44. package/lib/esm/components/datatable/datatable.js +90 -17
  45. package/lib/esm/components/datatable/datatable.js.map +1 -1
  46. package/lib/esm/components/datatable/index.d.ts +1 -1
  47. package/lib/esm/components/datatable/index.d.ts.map +1 -1
  48. package/lib/esm/components/datatable/types.d.ts +27 -0
  49. package/lib/esm/components/datatable/types.d.ts.map +1 -1
  50. package/lib/esm/index.d.ts +1 -1
  51. package/lib/esm/index.d.ts.map +1 -1
  52. package/lib/esm/index.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
  55. package/src/components/datatable/__tests__/pagination-reset.test.ts +18 -0
  56. package/src/components/datatable/datatable-checkbox.ts +5 -8
  57. package/src/components/datatable/datatable-layout-plugin.ts +449 -0
  58. package/src/components/datatable/datatable-local-provider.ts +15 -7
  59. package/src/components/datatable/datatable-pagination-renderer.ts +10 -13
  60. package/src/components/datatable/datatable.css +98 -0
  61. package/src/components/datatable/datatable.ts +109 -15
  62. package/src/components/datatable/index.ts +5 -0
  63. package/src/components/datatable/types.ts +33 -0
  64. package/src/index.ts +5 -0
@@ -0,0 +1,257 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { KTDataTable } from '../datatable';
3
+
4
+ type DataRow = {
5
+ id: string;
6
+ name: string;
7
+ status: string;
8
+ };
9
+
10
+ const rows: DataRow[] = [
11
+ { id: '1', name: 'Alpha', status: 'Active' },
12
+ { id: '2', name: 'Beta', status: 'Pending' },
13
+ { id: '3', name: 'Gamma', status: 'Disabled' },
14
+ ];
15
+
16
+ const createDatatableFixture = () => {
17
+ const wrapper = document.createElement('div');
18
+ wrapper.className = 'kt-table-wrapper';
19
+
20
+ const container = document.createElement('div');
21
+ container.id = 'kt_datatable_locked_layout';
22
+ container.setAttribute('data-kt-datatable', 'true');
23
+
24
+ const table = document.createElement('table');
25
+ table.setAttribute('data-kt-datatable-table', 'true');
26
+
27
+ const thead = document.createElement('thead');
28
+ thead.innerHTML = `
29
+ <tr>
30
+ <th data-kt-datatable-column="id">ID</th>
31
+ <th data-kt-datatable-column="name">Name</th>
32
+ <th data-kt-datatable-column="status">Status</th>
33
+ </tr>
34
+ `;
35
+ table.appendChild(thead);
36
+
37
+ const tbody = document.createElement('tbody');
38
+ rows.forEach((row) => {
39
+ const tr = document.createElement('tr');
40
+ tr.innerHTML = `<td>${row.id}</td><td>${row.name}</td><td>${row.status}</td>`;
41
+ tbody.appendChild(tr);
42
+ });
43
+ table.appendChild(tbody);
44
+
45
+ wrapper.appendChild(table);
46
+ container.appendChild(wrapper);
47
+
48
+ const info = document.createElement('span');
49
+ info.setAttribute('data-kt-datatable-info', 'true');
50
+ const size = document.createElement('select');
51
+ size.setAttribute('data-kt-datatable-size', 'true');
52
+ const pagination = document.createElement('div');
53
+ pagination.setAttribute('data-kt-datatable-pagination', 'true');
54
+
55
+ container.appendChild(info);
56
+ container.appendChild(size);
57
+ container.appendChild(pagination);
58
+ document.body.appendChild(container);
59
+
60
+ const sizedCells = table.querySelectorAll<HTMLTableCellElement>('th, td');
61
+ sizedCells.forEach((cell) => {
62
+ Object.defineProperty(cell, 'getBoundingClientRect', {
63
+ value: () =>
64
+ ({
65
+ width: 120,
66
+ height: 40,
67
+ top: 0,
68
+ left: 0,
69
+ right: 0,
70
+ bottom: 0,
71
+ x: 0,
72
+ y: 0,
73
+ toJSON: () => ({}),
74
+ }) as DOMRect,
75
+ });
76
+ });
77
+
78
+ return { container, table };
79
+ };
80
+
81
+ describe('KTDataTable locked layout plugin', () => {
82
+ beforeEach(() => {
83
+ vi.useFakeTimers();
84
+ });
85
+
86
+ it('applies locked header, rows and columns in local mode and keeps them after sorting', async () => {
87
+ const { container, table } = createDatatableFixture();
88
+
89
+ const datatable = new KTDataTable<DataRow>(container, {
90
+ stateSave: false,
91
+ columns: {
92
+ id: { title: 'ID' },
93
+ name: { title: 'Name' },
94
+ status: { title: 'Status' },
95
+ },
96
+ lockedLayout: {
97
+ stickyHeader: true,
98
+ stickyRows: { top: 1, bottom: 1 },
99
+ stickyColumns: { left: ['id'], right: ['status'] },
100
+ },
101
+ });
102
+
103
+ await vi.runAllTimersAsync();
104
+
105
+ expect(
106
+ table.querySelectorAll('.kt-datatable-locked-header').length,
107
+ ).toBeGreaterThan(0);
108
+ expect(
109
+ table.querySelectorAll('.kt-datatable-locked-top-row').length,
110
+ ).toBeGreaterThan(0);
111
+ expect(
112
+ table.querySelectorAll('.kt-datatable-locked-bottom-row').length,
113
+ ).toBeGreaterThan(0);
114
+ expect(
115
+ table.querySelectorAll('.kt-datatable-locked-left').length,
116
+ ).toBeGreaterThan(0);
117
+ expect(
118
+ table.querySelectorAll('.kt-datatable-locked-right').length,
119
+ ).toBeGreaterThan(0);
120
+
121
+ datatable.sort('name');
122
+ await vi.runAllTimersAsync();
123
+
124
+ expect(
125
+ table.querySelectorAll('.kt-datatable-locked-cell').length,
126
+ ).toBeGreaterThan(0);
127
+ });
128
+
129
+ it('reapplies locked layout after remote fetch redraw', async () => {
130
+ const { container, table } = createDatatableFixture();
131
+ const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
132
+ new Response(JSON.stringify({ data: rows, totalCount: rows.length }), {
133
+ status: 200,
134
+ headers: { 'Content-Type': 'application/json' },
135
+ }),
136
+ );
137
+
138
+ new KTDataTable<DataRow>(container, {
139
+ stateSave: false,
140
+ apiEndpoint: 'https://example.test/datatable',
141
+ requestMethod: 'GET',
142
+ mapResponse: (data) => data,
143
+ lockedLayout: {
144
+ stickyHeader: true,
145
+ stickyColumns: { left: ['id'] },
146
+ },
147
+ });
148
+
149
+ await vi.runAllTimersAsync();
150
+
151
+ expect(fetchMock).toHaveBeenCalled();
152
+ expect(
153
+ table.querySelectorAll('.kt-datatable-locked-header').length,
154
+ ).toBeGreaterThan(0);
155
+ expect(
156
+ table.querySelectorAll('.kt-datatable-locked-left').length,
157
+ ).toBeGreaterThan(0);
158
+ });
159
+
160
+ it('keeps pagination state when using locked columns in local mode', async () => {
161
+ const { container } = createDatatableFixture();
162
+
163
+ const datatable = new KTDataTable<DataRow>(container, {
164
+ stateSave: false,
165
+ pageSize: 2,
166
+ columns: {
167
+ id: { title: 'ID' },
168
+ name: { title: 'Name' },
169
+ status: { title: 'Status' },
170
+ },
171
+ lockedLayout: {
172
+ stickyHeader: true,
173
+ stickyColumns: { left: ['id'] },
174
+ },
175
+ });
176
+
177
+ await vi.runAllTimersAsync();
178
+ expect(datatable.getState().totalPages).toBe(2);
179
+
180
+ datatable.goPage(2);
181
+ await vi.runAllTimersAsync();
182
+ expect(datatable.getState().page).toBe(2);
183
+ expect(datatable.getState().totalPages).toBe(2);
184
+ expect(datatable.getState().totalItems).toBe(rows.length);
185
+ });
186
+
187
+ it('uses collapsed table borders for row-only locked layout', async () => {
188
+ const { container, table } = createDatatableFixture();
189
+
190
+ new KTDataTable<DataRow>(container, {
191
+ stateSave: false,
192
+ lockedLayout: {
193
+ stickyHeader: true,
194
+ stickyRows: { top: 1, bottom: 1 },
195
+ },
196
+ });
197
+
198
+ await vi.runAllTimersAsync();
199
+
200
+ expect(table.classList.contains('kt-datatable-locked-layout')).toBe(true);
201
+ expect(
202
+ table.classList.contains('kt-datatable-locked-layout-separate'),
203
+ ).toBe(false);
204
+ expect(table.style.borderCollapse).not.toBe('separate');
205
+ expect(
206
+ table.tHead?.classList.contains('kt-datatable-locked-header-section'),
207
+ ).toBe(true);
208
+ const stickyHeaderCell = table.querySelector(
209
+ 'th.kt-datatable-locked-header',
210
+ ) as HTMLTableCellElement | null;
211
+ expect(stickyHeaderCell?.style.position).not.toBe('sticky');
212
+ });
213
+
214
+ it('uses separate table borders when sticky columns are enabled', async () => {
215
+ const { container, table } = createDatatableFixture();
216
+
217
+ new KTDataTable<DataRow>(container, {
218
+ stateSave: false,
219
+ lockedLayout: {
220
+ stickyHeader: true,
221
+ stickyColumns: { left: ['id'] },
222
+ },
223
+ });
224
+
225
+ await vi.runAllTimersAsync();
226
+
227
+ expect(
228
+ table.classList.contains('kt-datatable-locked-layout-separate'),
229
+ ).toBe(true);
230
+ expect(table.style.borderCollapse).toBe('separate');
231
+ });
232
+
233
+ it('does not set inline background color for locked header columns', async () => {
234
+ const { container, table } = createDatatableFixture();
235
+
236
+ new KTDataTable<DataRow>(container, {
237
+ stateSave: false,
238
+ columns: {
239
+ id: { title: 'ID' },
240
+ name: { title: 'Name' },
241
+ status: { title: 'Status' },
242
+ },
243
+ lockedLayout: {
244
+ stickyHeader: true,
245
+ stickyColumns: { left: ['id'] },
246
+ },
247
+ });
248
+
249
+ await vi.runAllTimersAsync();
250
+
251
+ const lockedHeaderCell = table.querySelector(
252
+ 'th.kt-datatable-locked-left',
253
+ ) as HTMLTableCellElement | null;
254
+ expect(lockedHeaderCell).not.toBeNull();
255
+ expect(lockedHeaderCell?.style.backgroundColor).toBe('');
256
+ });
257
+ });
@@ -552,6 +552,24 @@ describe('KTDataTable - Pagination Reset', () => {
552
552
  });
553
553
 
554
554
  describe('Edge Cases', () => {
555
+ it('keeps total pages stable when navigating to page 2 in local mode', () => {
556
+ const { container } = createMockDataTable(6);
557
+ datatable = new KTDataTable(container, {
558
+ pageSize: 5,
559
+ stateSave: false,
560
+ });
561
+
562
+ expect(datatable.getState().totalPages).toBe(2);
563
+ datatable.goPage(2);
564
+ expect(datatable.getState().page).toBe(2);
565
+ expect(datatable.getState().totalPages).toBe(2);
566
+
567
+ // Trigger another redraw cycle to ensure local mode does not shrink original data.
568
+ datatable.reload();
569
+ expect(datatable.getState().page).toBe(2);
570
+ expect(datatable.getState().totalPages).toBe(2);
571
+ });
572
+
555
573
  it('should handle search reset on page 1 (no-op)', () => {
556
574
  const { container } = createMockDataTable(25);
557
575
  datatable = new KTDataTable(container, {
@@ -90,14 +90,11 @@ export function createCheckboxHandler(
90
90
  const rowCheckboxSelector = config.attributes?.checkbox;
91
91
  if (!rowCheckboxSelector) return;
92
92
  headerCheckElement.addEventListener('click', checkboxListener);
93
- KTEventHandler.on(
94
- document.body,
95
- rowCheckboxSelector,
96
- 'input',
97
- ((event?: Event) => {
98
- if (event) handleRowCheckboxChange(event);
99
- }) as KTCallableType,
100
- );
93
+ KTEventHandler.on(document.body, rowCheckboxSelector, 'input', ((
94
+ event?: Event,
95
+ ) => {
96
+ if (event) handleRowCheckboxChange(event);
97
+ }) as KTCallableType);
101
98
  }
102
99
 
103
100
  // When a row checkbox is changed