@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.
Files changed (196) hide show
  1. package/README.md +14 -5
  2. package/dist/ktui.js +1538 -786
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +85 -5
  6. package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
  7. package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
  8. package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
  9. package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
  10. package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
  11. package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
  12. package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
  13. package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
  14. package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
  15. package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
  16. package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
  17. package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
  18. package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
  19. package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
  20. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
  21. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  22. package/lib/cjs/components/datatable/datatable-layout-plugin.js +338 -0
  23. package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
  24. package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
  25. package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
  26. package/lib/cjs/components/datatable/datatable-local-provider.js +85 -27
  27. package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
  28. package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  29. package/lib/cjs/components/datatable/datatable-pagination-renderer.js +13 -13
  30. package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
  31. package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
  32. package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
  33. package/lib/cjs/components/datatable/datatable-registry.js +66 -0
  34. package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
  35. package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  36. package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
  37. package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
  38. package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
  39. package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
  40. package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
  41. package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
  42. package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
  43. package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
  44. package/lib/cjs/components/datatable/datatable-sort.js +86 -58
  45. package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
  46. package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
  47. package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
  48. package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
  49. package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
  50. package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
  51. package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
  52. package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
  53. package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
  54. package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
  55. package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  56. package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
  57. package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
  58. package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
  59. package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
  60. package/lib/cjs/components/datatable/datatable-utils.js +15 -0
  61. package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
  62. package/lib/cjs/components/datatable/datatable.d.ts +35 -34
  63. package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
  64. package/lib/cjs/components/datatable/datatable.js +233 -497
  65. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  66. package/lib/cjs/components/datatable/index.d.ts +1 -1
  67. package/lib/cjs/components/datatable/index.d.ts.map +1 -1
  68. package/lib/cjs/components/datatable/types.d.ts +127 -11
  69. package/lib/cjs/components/datatable/types.d.ts.map +1 -1
  70. package/lib/cjs/index.d.ts +1 -1
  71. package/lib/cjs/index.d.ts.map +1 -1
  72. package/lib/cjs/index.js +6 -0
  73. package/lib/cjs/index.js.map +1 -1
  74. package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
  75. package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
  76. package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
  77. package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
  78. package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
  79. package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
  80. package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
  81. package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
  82. package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
  83. package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
  84. package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
  85. package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
  86. package/lib/esm/components/datatable/datatable-defaults.js +190 -0
  87. package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
  88. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
  89. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  90. package/lib/esm/components/datatable/datatable-layout-plugin.js +334 -0
  91. package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
  92. package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
  93. package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
  94. package/lib/esm/components/datatable/datatable-local-provider.js +85 -27
  95. package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
  96. package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  97. package/lib/esm/components/datatable/datatable-pagination-renderer.js +13 -13
  98. package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
  99. package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
  100. package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
  101. package/lib/esm/components/datatable/datatable-registry.js +63 -0
  102. package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
  103. package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  104. package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
  105. package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
  106. package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
  107. package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
  108. package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
  109. package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
  110. package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
  111. package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
  112. package/lib/esm/components/datatable/datatable-sort.js +85 -57
  113. package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
  114. package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
  115. package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
  116. package/lib/esm/components/datatable/datatable-spinner.js +51 -0
  117. package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
  118. package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
  119. package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
  120. package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
  121. package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
  122. package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
  123. package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  124. package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
  125. package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
  126. package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
  127. package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
  128. package/lib/esm/components/datatable/datatable-utils.js +12 -0
  129. package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
  130. package/lib/esm/components/datatable/datatable.d.ts +35 -34
  131. package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
  132. package/lib/esm/components/datatable/datatable.js +235 -499
  133. package/lib/esm/components/datatable/datatable.js.map +1 -1
  134. package/lib/esm/components/datatable/index.d.ts +1 -1
  135. package/lib/esm/components/datatable/index.d.ts.map +1 -1
  136. package/lib/esm/components/datatable/types.d.ts +127 -11
  137. package/lib/esm/components/datatable/types.d.ts.map +1 -1
  138. package/lib/esm/index.d.ts +1 -1
  139. package/lib/esm/index.d.ts.map +1 -1
  140. package/lib/esm/index.js +6 -0
  141. package/lib/esm/index.js.map +1 -1
  142. package/package.json +5 -1
  143. package/skills/ktui/SKILL.md +711 -0
  144. package/skills/ktui-datatable/SKILL.md +302 -0
  145. package/skills/ktui-install/SKILL.md +150 -0
  146. package/skills/ktui-select/SKILL.md +271 -0
  147. package/src/components/__tests__/component.test.ts +347 -0
  148. package/src/components/collapse/collapse.css +2 -2
  149. package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
  150. package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
  151. package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
  152. package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
  153. package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
  154. package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
  155. package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
  156. package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
  157. package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
  158. package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
  159. package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
  160. package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
  161. package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
  162. package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
  163. package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
  164. package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
  165. package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
  166. package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
  167. package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
  168. package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
  169. package/src/components/datatable/__tests__/pagination-reset.test.ts +147 -6
  170. package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
  171. package/src/components/datatable/__tests__/setup.ts +12 -4
  172. package/src/components/datatable/datatable-checkbox.ts +139 -143
  173. package/src/components/datatable/datatable-column-utils.ts +63 -0
  174. package/src/components/datatable/datatable-contracts.ts +2 -3
  175. package/src/components/datatable/datatable-defaults.ts +204 -0
  176. package/src/components/datatable/datatable-layout-plugin.ts +459 -0
  177. package/src/components/datatable/datatable-local-provider.ts +106 -35
  178. package/src/components/datatable/datatable-pagination-renderer.ts +13 -15
  179. package/src/components/datatable/datatable-registry.ts +89 -0
  180. package/src/components/datatable/datatable-remote-provider.ts +1 -3
  181. package/src/components/datatable/datatable-search-handler.ts +97 -0
  182. package/src/components/datatable/datatable-sort.ts +111 -66
  183. package/src/components/datatable/datatable-spinner.ts +103 -0
  184. package/src/components/datatable/datatable-state-persistence.ts +67 -0
  185. package/src/components/datatable/datatable-table-renderer.ts +81 -18
  186. package/src/components/datatable/datatable-utils.ts +12 -0
  187. package/src/components/datatable/datatable.css +98 -0
  188. package/src/components/datatable/datatable.ts +288 -583
  189. package/src/components/datatable/index.ts +8 -0
  190. package/src/components/datatable/types.ts +157 -23
  191. package/src/helpers/__tests__/dom.test.ts +776 -0
  192. package/src/helpers/__tests__/utils.test.ts +332 -0
  193. package/src/index.ts +15 -0
  194. package/skills/ktui-components/SKILL.md +0 -41
  195. package/skills/ktui-theming/SKILL.md +0 -50
  196. package/src/components/datatable/datatable-event-adapter.ts +0 -21
@@ -8,11 +8,15 @@
8
8
  import {
9
9
  KTDataTableConfigInterface,
10
10
  KTDataTableCheckChangePayloadInterface,
11
- KTDataTableStateInterface,
12
11
  } from './types';
13
12
  import KTEventHandler from '../../helpers/event-handler';
14
13
  import { KTCallableType } from '../../types';
15
14
 
15
+ export interface KTDataTableCheckboxDeps {
16
+ getState: () => { selectedRows?: string[] };
17
+ setSelectedRows: (rows: string[]) => void;
18
+ }
19
+
16
20
  export interface KTDataTableCheckboxAPI {
17
21
  init(): void;
18
22
  check(): void;
@@ -21,244 +25,236 @@ export interface KTDataTableCheckboxAPI {
21
25
  isChecked(): boolean;
22
26
  getChecked(): string[];
23
27
  updateState(): void;
28
+ dispose(): void;
24
29
  }
25
30
 
26
- // Main function to create checkbox logic for a datatable instance
27
- export function createCheckboxHandler(
28
- element: HTMLElement,
29
- config: KTDataTableConfigInterface,
30
- fireEvent: (eventName: string, eventData?: object) => void,
31
- ): KTDataTableCheckboxAPI {
32
- let headerChecked = false;
33
- let headerCheckElement: HTMLInputElement | null = null;
34
- let targetElements: NodeListOf<HTMLInputElement> | null = null;
31
+ export class KTDataTableCheckboxHandler implements KTDataTableCheckboxAPI {
32
+ private _element: HTMLElement;
33
+ private _config: KTDataTableConfigInterface;
34
+ private _fireEvent: (eventName: string, eventData?: object) => void;
35
+ private _deps: KTDataTableCheckboxDeps;
36
+ private _headerChecked = false;
37
+ private _headerCheckElement: HTMLInputElement | null = null;
38
+ private _targetElements: NodeListOf<HTMLInputElement> | null = null;
39
+ private _delegatedEventId: string | null = null;
40
+ private readonly _preserveSelection: boolean;
35
41
 
36
- // Default: preserve selection across all pages
37
- const preserveSelection = config.checkbox?.preserveSelection !== false;
38
-
39
- function ensureState(): KTDataTableStateInterface {
40
- let state = config._state;
41
- if (!state) {
42
- state = {} as KTDataTableStateInterface;
43
- config._state = state;
44
- }
45
- return state;
42
+ constructor(
43
+ element: HTMLElement,
44
+ config: KTDataTableConfigInterface,
45
+ fireEvent: (eventName: string, eventData?: object) => void,
46
+ deps: KTDataTableCheckboxDeps,
47
+ ) {
48
+ this._element = element;
49
+ this._config = config;
50
+ this._fireEvent = fireEvent;
51
+ this._deps = deps;
52
+ this._preserveSelection = config.checkbox?.preserveSelection !== false;
46
53
  }
47
54
 
48
- // Helper: get selectedRows from state, always as string[]
49
- function getSelectedRows(): string[] {
50
- const state = ensureState();
51
- if (!Array.isArray(state.selectedRows)) state.selectedRows = [];
52
- return state.selectedRows.map(String);
55
+ private _checkboxListener = (event: MouseEvent) => {
56
+ this._checkboxToggle(event);
57
+ };
58
+
59
+ private _getSelectedRows(): string[] {
60
+ const rows = this._deps.getState().selectedRows;
61
+ return Array.isArray(rows) ? rows.map(String) : [];
53
62
  }
54
63
 
55
- // Helper: set selectedRows in state
56
- function setSelectedRows(rows: string[]) {
57
- const state = ensureState();
58
- state.selectedRows = Array.from(new Set(rows.map(String)));
64
+ private _setSelectedRows(rows: string[]) {
65
+ this._deps.setSelectedRows(Array.from(new Set(rows.map(String))));
59
66
  }
60
67
 
61
- // Helper: get all visible row IDs (values)
62
- function getVisibleRowIds(): string[] {
63
- if (!targetElements) return [];
64
- return Array.from(targetElements)
68
+ private _getVisibleRowIds(): string[] {
69
+ if (!this._targetElements) return [];
70
+ return Array.from(this._targetElements)
65
71
  .map((el) => el.value)
66
72
  .filter((v) => v != null && v !== '');
67
73
  }
68
74
 
69
- // Listener for header checkbox
70
- const checkboxListener = (event: MouseEvent) => {
71
- checkboxToggle(event);
72
- };
73
-
74
- function init() {
75
- const attrs = config.attributes;
75
+ public init() {
76
+ const attrs = this._config.attributes;
76
77
  if (!attrs?.check || !attrs.checkbox) {
77
78
  return;
78
79
  }
79
- headerCheckElement = element.querySelector<HTMLInputElement>(attrs.check);
80
- if (!headerCheckElement) return;
81
- headerChecked = headerCheckElement.checked;
82
- targetElements = element.querySelectorAll<HTMLInputElement>(attrs.checkbox);
83
- checkboxHandler();
84
- reapplyCheckedStates();
85
- updateHeaderCheckboxState();
80
+ this._headerCheckElement =
81
+ this._element.querySelector<HTMLInputElement>(attrs.check);
82
+ if (!this._headerCheckElement) return;
83
+ this._headerChecked = this._headerCheckElement.checked;
84
+ this._targetElements =
85
+ this._element.querySelectorAll<HTMLInputElement>(attrs.checkbox);
86
+ this._checkboxHandler();
87
+ this._reapplyCheckedStates();
88
+ this._updateHeaderCheckboxState();
86
89
  }
87
90
 
88
- function checkboxHandler() {
89
- if (!headerCheckElement) return;
90
- const rowCheckboxSelector = config.attributes?.checkbox;
91
+ private _checkboxHandler() {
92
+ if (!this._headerCheckElement) return;
93
+ const rowCheckboxSelector = this._config.attributes?.checkbox;
91
94
  if (!rowCheckboxSelector) return;
92
- headerCheckElement.addEventListener('click', checkboxListener);
93
- KTEventHandler.on(
94
- document.body,
95
+ this._headerCheckElement.addEventListener('click', this._checkboxListener);
96
+ this._delegatedEventId = KTEventHandler.on(
97
+ this._element,
95
98
  rowCheckboxSelector,
96
99
  'input',
97
100
  ((event?: Event) => {
98
- if (event) handleRowCheckboxChange(event);
101
+ if (event) this._handleRowCheckboxChange(event);
99
102
  }) as KTCallableType,
100
103
  );
101
104
  }
102
105
 
103
- // When a row checkbox is changed
104
- function handleRowCheckboxChange(event: Event) {
106
+ private _handleRowCheckboxChange(event: Event) {
105
107
  const input = event.target as HTMLInputElement;
106
108
  if (!input) return;
107
109
  const value = input.value;
108
- let selectedRows = getSelectedRows();
110
+ let selectedRows = this._getSelectedRows();
109
111
  const wasChecked = selectedRows.includes(value);
110
112
  const isNowChecked = input.checked;
111
113
 
112
- // Update state first
113
114
  if (isNowChecked) {
114
115
  if (!wasChecked) selectedRows.push(value);
115
116
  } else {
116
117
  selectedRows = selectedRows.filter((v) => v !== value);
117
118
  }
118
- setSelectedRows(selectedRows);
119
- updateHeaderCheckboxState();
119
+ this._setSelectedRows(selectedRows);
120
+ this._updateHeaderCheckboxState();
120
121
 
121
- // Fire specific checked/unchecked events after state is updated
122
122
  if (isNowChecked && !wasChecked) {
123
- fireEvent('checked');
123
+ this._fireEvent('checked', { value });
124
124
  } else if (!isNowChecked && wasChecked) {
125
- fireEvent('unchecked');
125
+ this._fireEvent('unchecked', { value });
126
126
  }
127
127
 
128
- // Always fire changed event for backward compatibility
129
- fireEvent('changed');
128
+ this._fireEvent('changed');
130
129
  }
131
130
 
132
- // When the header checkbox is toggled
133
- function checkboxToggle(_event?: Event) {
134
- const checked = !isChecked();
135
- // Update state first, then fire events
136
- change(checked);
137
- // Fire checked/unchecked events after state is updated
131
+ private _checkboxToggle(_event?: Event) {
132
+ const checked = !this.isChecked();
133
+ this._change(checked);
138
134
  const eventType = checked ? 'checked' : 'unchecked';
139
- fireEvent(eventType);
135
+ this._fireEvent(eventType);
140
136
  }
141
137
 
142
- // Change all visible checkboxes and update selectedRows
143
- function change(checked: boolean) {
138
+ private _change(checked: boolean) {
144
139
  const payload: KTDataTableCheckChangePayloadInterface = { cancel: false };
145
- fireEvent('change', payload);
140
+ this._fireEvent('change', payload);
146
141
  if (payload.cancel === true) return;
147
- headerChecked = checked;
148
- if (headerCheckElement) headerCheckElement.checked = checked;
149
- if (targetElements) {
150
- const visibleIds = getVisibleRowIds();
151
- let selectedRows = getSelectedRows();
142
+ this._headerChecked = checked;
143
+ if (this._headerCheckElement) this._headerCheckElement.checked = checked;
144
+ if (this._targetElements) {
145
+ const visibleIds = this._getVisibleRowIds();
146
+ let selectedRows = this._getSelectedRows();
152
147
  if (checked) {
153
- // Add all visible IDs to selectedRows
154
- selectedRows = preserveSelection
148
+ selectedRows = this._preserveSelection
155
149
  ? Array.from(new Set([...selectedRows, ...visibleIds]))
156
150
  : visibleIds;
157
151
  } else {
158
- // Remove all visible IDs from selectedRows
159
- selectedRows = preserveSelection
152
+ selectedRows = this._preserveSelection
160
153
  ? selectedRows.filter((v) => !visibleIds.includes(v))
161
154
  : [];
162
155
  }
163
- setSelectedRows(selectedRows);
164
- // Update visible checkboxes
165
- targetElements.forEach((element: HTMLInputElement) => {
156
+ this._setSelectedRows(selectedRows);
157
+ this._targetElements.forEach((element: HTMLInputElement) => {
166
158
  if (element) {
167
159
  element.checked = checked;
168
160
  }
169
161
  });
170
162
  }
171
- updateHeaderCheckboxState();
172
- fireEvent('changed');
163
+ this._updateHeaderCheckboxState();
164
+ this._fireEvent('changed');
173
165
  }
174
166
 
175
- // Reapply checked state to visible checkboxes based on selectedRows
176
- function reapplyCheckedStates() {
177
- const selectedRows = getSelectedRows();
178
- if (!targetElements) return;
179
- targetElements.forEach((element: HTMLInputElement) => {
167
+ private _reapplyCheckedStates() {
168
+ const selectedRows = this._getSelectedRows();
169
+ if (!this._targetElements) return;
170
+ this._targetElements.forEach((element: HTMLInputElement) => {
180
171
  if (!element) return;
181
172
  const value = element.value;
182
173
  element.checked = selectedRows.includes(value);
183
- // Update row class
184
174
  const row = element.closest('tr');
185
- if (row && config.checkbox?.checkedClass) {
175
+ if (row && this._config.checkbox?.checkedClass) {
186
176
  if (element.checked) {
187
- row.classList.add(config.checkbox.checkedClass);
177
+ row.classList.add(this._config.checkbox.checkedClass);
188
178
  } else {
189
- row.classList.remove(config.checkbox.checkedClass);
179
+ row.classList.remove(this._config.checkbox.checkedClass);
190
180
  }
191
181
  }
192
182
  });
193
183
  }
194
184
 
195
- // Update header checkbox state (checked/indeterminate/unchecked)
196
- function updateHeaderCheckboxState() {
197
- if (!headerCheckElement || !targetElements) return;
198
- const total = targetElements.length;
185
+ private _updateHeaderCheckboxState() {
186
+ if (!this._headerCheckElement || !this._targetElements) return;
187
+ const total = this._targetElements.length;
199
188
  let checked = 0;
200
189
  for (let i = 0; i < total; i++) {
201
- if (targetElements[i].checked) checked++;
190
+ if (this._targetElements[i].checked) checked++;
202
191
  }
203
192
  if (checked === 0) {
204
- headerCheckElement.indeterminate = false;
205
- headerCheckElement.checked = false;
206
- headerChecked = false;
193
+ this._headerCheckElement.indeterminate = false;
194
+ this._headerCheckElement.checked = false;
195
+ this._headerChecked = false;
207
196
  } else if (checked > 0 && checked < total) {
208
- headerCheckElement.indeterminate = true;
209
- headerCheckElement.checked = false;
210
- headerChecked = false;
197
+ this._headerCheckElement.indeterminate = true;
198
+ this._headerCheckElement.checked = false;
199
+ this._headerChecked = false;
211
200
  } else if (checked === total) {
212
- headerCheckElement.indeterminate = false;
213
- headerCheckElement.checked = true;
214
- headerChecked = true;
201
+ this._headerCheckElement.indeterminate = false;
202
+ this._headerCheckElement.checked = true;
203
+ this._headerChecked = true;
215
204
  }
216
205
  }
217
206
 
218
- // Fix: isChecked() implementation
219
- function isChecked(): boolean {
220
- return headerChecked;
207
+ public isChecked(): boolean {
208
+ return this._headerChecked;
221
209
  }
222
210
 
223
- function getChecked(): string[] {
224
- return getSelectedRows();
211
+ public getChecked(): string[] {
212
+ return this._getSelectedRows();
225
213
  }
226
214
 
227
- function check() {
228
- change(true);
229
- reapplyCheckedStates();
230
- updateHeaderCheckboxState();
215
+ public check() {
216
+ this._change(true);
217
+ this._reapplyCheckedStates();
218
+ this._updateHeaderCheckboxState();
231
219
  }
232
220
 
233
- function uncheck() {
234
- change(false);
235
- reapplyCheckedStates();
236
- updateHeaderCheckboxState();
221
+ public uncheck() {
222
+ this._change(false);
223
+ this._reapplyCheckedStates();
224
+ this._updateHeaderCheckboxState();
237
225
  }
238
226
 
239
- function toggle() {
240
- checkboxToggle();
241
- reapplyCheckedStates();
242
- updateHeaderCheckboxState();
227
+ public toggle() {
228
+ this._checkboxToggle();
229
+ this._reapplyCheckedStates();
230
+ this._updateHeaderCheckboxState();
243
231
  }
244
232
 
245
- function updateState() {
246
- const rowCheckSel = config.attributes?.checkbox;
233
+ public updateState() {
234
+ const rowCheckSel = this._config.attributes?.checkbox;
247
235
  if (!rowCheckSel) {
248
236
  return;
249
237
  }
250
- targetElements = element.querySelectorAll<HTMLInputElement>(rowCheckSel);
251
- reapplyCheckedStates();
252
- updateHeaderCheckboxState();
238
+ this._targetElements =
239
+ this._element.querySelectorAll<HTMLInputElement>(rowCheckSel);
240
+ this._reapplyCheckedStates();
241
+ this._updateHeaderCheckboxState();
253
242
  }
254
243
 
255
- return {
256
- init,
257
- check,
258
- uncheck,
259
- toggle,
260
- isChecked,
261
- getChecked,
262
- updateState,
263
- };
244
+ public dispose() {
245
+ if (this._headerCheckElement) {
246
+ this._headerCheckElement.removeEventListener(
247
+ 'click',
248
+ this._checkboxListener,
249
+ );
250
+ }
251
+ const rowCheckboxSelector = this._config.attributes?.checkbox;
252
+ if (this._delegatedEventId && rowCheckboxSelector) {
253
+ KTEventHandler.off(this._element, 'input', this._delegatedEventId);
254
+ this._delegatedEventId = null;
255
+ }
256
+ this._headerCheckElement = null;
257
+ this._targetElements = null;
258
+ }
264
259
  }
260
+
@@ -0,0 +1,63 @@
1
+ /**
2
+ * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
+ * Copyright 2025 by Keenthemes Inc
4
+ */
5
+
6
+ /**
7
+ * Shared column resolution utilities for KTDataTable.
8
+ * Eliminates duplicated column discovery logic across local-provider,
9
+ * table-renderer, and the main datatable class.
10
+ */
11
+
12
+ export interface ResolvedColumns {
13
+ /** All <th> elements from the thead */
14
+ allThs: HTMLTableCellElement[];
15
+ /** Only <th> elements that have data-kt-datatable-column attribute */
16
+ typedThs: HTMLTableCellElement[];
17
+ /** The column list to use for rendering: typedThs if all ths have the attr, else allThs */
18
+ columnsByIndex: HTMLTableCellElement[];
19
+ }
20
+
21
+ /**
22
+ * Resolve column headers from a thead element.
23
+ * Returns both the raw th list and the typed (data-kt-datatable-column) subset.
24
+ * When some ths lack the attribute, columnsByIndex falls back to all ths by index.
25
+ */
26
+ export function resolveColumns(
27
+ theadElement: HTMLTableSectionElement | null,
28
+ ): ResolvedColumns {
29
+ const allThs: HTMLTableCellElement[] = theadElement
30
+ ? Array.from(theadElement.querySelectorAll('th'))
31
+ : [];
32
+
33
+ const typedThs = allThs.filter((th) =>
34
+ th.hasAttribute('data-kt-datatable-column'),
35
+ );
36
+
37
+ const columnsByIndex = typedThs.length > 0 ? typedThs : allThs;
38
+
39
+ return { allThs, typedThs, columnsByIndex };
40
+ }
41
+
42
+ /**
43
+ * Get the logical column count: number of data columns.
44
+ * Prefers originalData keys, falls back to first tbody row td count,
45
+ * then to resolved columns from thead.
46
+ */
47
+ export function getLogicalColumnCount(
48
+ theadElement: HTMLTableSectionElement | null,
49
+ tbodyElement: HTMLTableSectionElement | null,
50
+ originalData?: Array<Record<string, unknown>>,
51
+ ): number {
52
+ if (originalData && originalData.length > 0) {
53
+ return Object.keys(originalData[0]).length;
54
+ }
55
+ if (tbodyElement) {
56
+ const firstRow = tbodyElement.querySelector<HTMLTableRowElement>('tr');
57
+ if (firstRow) {
58
+ return firstRow.querySelectorAll('td').length;
59
+ }
60
+ }
61
+ const { columnsByIndex } = resolveColumns(theadElement);
62
+ return columnsByIndex.length;
63
+ }
@@ -11,6 +11,7 @@ import {
11
11
  KTDataTableInterface,
12
12
  KTDataTableSortOrderInterface,
13
13
  KTDataTableStateInterface,
14
+ OriginalTableClasses,
14
15
  } from './types';
15
16
 
16
17
  export type KTDataTableEmit = (eventName: string, eventData?: object) => void;
@@ -64,9 +65,7 @@ export interface KTDataTableTableRendererInput<
64
65
  data: T[];
65
66
  getLogicalColumnCount: () => number;
66
67
  getState: () => KTDataTableStateInterface;
67
- originalTbodyClass: string;
68
- originalTrClasses: string[];
69
- originalTdClasses: string[][];
68
+ originalClasses: OriginalTableClasses;
70
69
  tableElement: HTMLTableElement;
71
70
  theadElement: HTMLTableSectionElement;
72
71
  }