@keenthemes/ktui 1.2.4 → 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 (152) hide show
  1. package/dist/ktui.js +2551 -2817
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +136 -40
  5. package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
  6. package/lib/cjs/components/datatable/datatable-checkbox.js +34 -15
  7. package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
  8. package/lib/cjs/components/datatable/datatable-contracts.d.ts +3 -3
  9. package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
  10. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
  11. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  12. package/lib/cjs/components/datatable/datatable-layout-plugin.js +328 -0
  13. package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
  14. package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
  15. package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
  16. package/lib/cjs/components/datatable/datatable-local-provider.js +18 -10
  17. package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
  18. package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  19. package/lib/cjs/components/datatable/datatable-pagination-renderer.js +40 -25
  20. package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
  21. package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  22. package/lib/cjs/components/datatable/datatable-remote-provider.js +3 -0
  23. package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
  24. package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  25. package/lib/cjs/components/datatable/datatable-table-renderer.js +14 -6
  26. package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
  27. package/lib/cjs/components/datatable/datatable.d.ts +9 -0
  28. package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
  29. package/lib/cjs/components/datatable/datatable.js +200 -61
  30. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  31. package/lib/cjs/components/datatable/index.d.ts +1 -1
  32. package/lib/cjs/components/datatable/index.d.ts.map +1 -1
  33. package/lib/cjs/components/datatable/types.d.ts +27 -0
  34. package/lib/cjs/components/datatable/types.d.ts.map +1 -1
  35. package/lib/cjs/components/dropdown/dropdown.d.ts +2 -2
  36. package/lib/cjs/components/dropdown/dropdown.d.ts.map +1 -1
  37. package/lib/cjs/components/dropdown/dropdown.js +68 -31
  38. package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
  39. package/lib/cjs/components/input-number/index.d.ts +7 -0
  40. package/lib/cjs/components/input-number/index.d.ts.map +1 -0
  41. package/lib/cjs/components/input-number/index.js +10 -0
  42. package/lib/cjs/components/input-number/index.js.map +1 -0
  43. package/lib/cjs/components/input-number/input-number.d.ts +40 -0
  44. package/lib/cjs/components/input-number/input-number.d.ts.map +1 -0
  45. package/lib/cjs/components/input-number/input-number.js +248 -0
  46. package/lib/cjs/components/input-number/input-number.js.map +1 -0
  47. package/lib/cjs/components/input-number/types.d.ts +30 -0
  48. package/lib/cjs/components/input-number/types.d.ts.map +1 -0
  49. package/lib/cjs/components/input-number/types.js +7 -0
  50. package/lib/cjs/components/input-number/types.js.map +1 -0
  51. package/lib/cjs/components/select/config.d.ts +1 -0
  52. package/lib/cjs/components/select/config.d.ts.map +1 -1
  53. package/lib/cjs/components/select/config.js +2 -1
  54. package/lib/cjs/components/select/config.js.map +1 -1
  55. package/lib/cjs/components/select/select.d.ts +8 -1
  56. package/lib/cjs/components/select/select.d.ts.map +1 -1
  57. package/lib/cjs/components/select/select.js +14 -1
  58. package/lib/cjs/components/select/select.js.map +1 -1
  59. package/lib/cjs/components/select/tags.d.ts.map +1 -1
  60. package/lib/cjs/components/select/tags.js +10 -0
  61. package/lib/cjs/components/select/tags.js.map +1 -1
  62. package/lib/cjs/index.d.ts +5 -1
  63. package/lib/cjs/index.d.ts.map +1 -1
  64. package/lib/cjs/index.js +5 -1
  65. package/lib/cjs/index.js.map +1 -1
  66. package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
  67. package/lib/esm/components/datatable/datatable-checkbox.js +34 -15
  68. package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
  69. package/lib/esm/components/datatable/datatable-contracts.d.ts +3 -3
  70. package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
  71. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
  72. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  73. package/lib/esm/components/datatable/datatable-layout-plugin.js +324 -0
  74. package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
  75. package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
  76. package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
  77. package/lib/esm/components/datatable/datatable-local-provider.js +18 -10
  78. package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
  79. package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  80. package/lib/esm/components/datatable/datatable-pagination-renderer.js +40 -25
  81. package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
  82. package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  83. package/lib/esm/components/datatable/datatable-remote-provider.js +3 -0
  84. package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
  85. package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  86. package/lib/esm/components/datatable/datatable-table-renderer.js +14 -6
  87. package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
  88. package/lib/esm/components/datatable/datatable.d.ts +9 -0
  89. package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
  90. package/lib/esm/components/datatable/datatable.js +200 -61
  91. package/lib/esm/components/datatable/datatable.js.map +1 -1
  92. package/lib/esm/components/datatable/index.d.ts +1 -1
  93. package/lib/esm/components/datatable/index.d.ts.map +1 -1
  94. package/lib/esm/components/datatable/types.d.ts +27 -0
  95. package/lib/esm/components/datatable/types.d.ts.map +1 -1
  96. package/lib/esm/components/dropdown/dropdown.d.ts +2 -2
  97. package/lib/esm/components/dropdown/dropdown.d.ts.map +1 -1
  98. package/lib/esm/components/dropdown/dropdown.js +68 -31
  99. package/lib/esm/components/dropdown/dropdown.js.map +1 -1
  100. package/lib/esm/components/input-number/index.d.ts +7 -0
  101. package/lib/esm/components/input-number/index.d.ts.map +1 -0
  102. package/lib/esm/components/input-number/index.js +6 -0
  103. package/lib/esm/components/input-number/index.js.map +1 -0
  104. package/lib/esm/components/input-number/input-number.d.ts +40 -0
  105. package/lib/esm/components/input-number/input-number.d.ts.map +1 -0
  106. package/lib/esm/components/input-number/input-number.js +245 -0
  107. package/lib/esm/components/input-number/input-number.js.map +1 -0
  108. package/lib/esm/components/input-number/types.d.ts +30 -0
  109. package/lib/esm/components/input-number/types.d.ts.map +1 -0
  110. package/lib/esm/components/input-number/types.js +6 -0
  111. package/lib/esm/components/input-number/types.js.map +1 -0
  112. package/lib/esm/components/select/config.d.ts +1 -0
  113. package/lib/esm/components/select/config.d.ts.map +1 -1
  114. package/lib/esm/components/select/config.js +2 -1
  115. package/lib/esm/components/select/config.js.map +1 -1
  116. package/lib/esm/components/select/select.d.ts +8 -1
  117. package/lib/esm/components/select/select.d.ts.map +1 -1
  118. package/lib/esm/components/select/select.js +14 -1
  119. package/lib/esm/components/select/select.js.map +1 -1
  120. package/lib/esm/components/select/tags.d.ts.map +1 -1
  121. package/lib/esm/components/select/tags.js +11 -1
  122. package/lib/esm/components/select/tags.js.map +1 -1
  123. package/lib/esm/index.d.ts +5 -1
  124. package/lib/esm/index.d.ts.map +1 -1
  125. package/lib/esm/index.js +3 -0
  126. package/lib/esm/index.js.map +1 -1
  127. package/package.json +5 -11
  128. package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
  129. package/src/components/datatable/__tests__/pagination-reset.test.ts +18 -0
  130. package/src/components/datatable/datatable-checkbox.ts +35 -27
  131. package/src/components/datatable/datatable-contracts.ts +3 -3
  132. package/src/components/datatable/datatable-layout-plugin.ts +449 -0
  133. package/src/components/datatable/datatable-local-provider.ts +21 -14
  134. package/src/components/datatable/datatable-pagination-renderer.ts +40 -29
  135. package/src/components/datatable/datatable-remote-provider.ts +3 -0
  136. package/src/components/datatable/datatable-table-renderer.ts +40 -32
  137. package/src/components/datatable/datatable.css +98 -0
  138. package/src/components/datatable/datatable.ts +223 -86
  139. package/src/components/datatable/index.ts +5 -0
  140. package/src/components/datatable/types.ts +33 -0
  141. package/src/components/dropdown/dropdown.ts +86 -58
  142. package/src/components/input/input-group.css +14 -1
  143. package/src/components/input-number/__tests__/input-number.test.ts +278 -0
  144. package/src/components/input-number/index.ts +11 -0
  145. package/src/components/input-number/input-number.ts +267 -0
  146. package/src/components/input-number/types.ts +32 -0
  147. package/src/components/select/__tests__/ux-behaviors.test.ts +72 -0
  148. package/src/components/select/config.ts +3 -1
  149. package/src/components/select/select.css +23 -20
  150. package/src/components/select/select.ts +15 -1
  151. package/src/components/select/tags.ts +14 -1
  152. package/src/index.ts +14 -0
@@ -13,6 +13,7 @@ import KTDom from '../../helpers/dom';
13
13
  import KTData from '../../helpers/data';
14
14
  import KTEventHandler from '../../helpers/event-handler';
15
15
  import KTComponent from '../component';
16
+ import { KTCallableType } from '../../types';
16
17
  import { KTDropdownConfigInterface, KTDropdownInterface } from './types';
17
18
 
18
19
  declare global {
@@ -41,8 +42,8 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
41
42
  };
42
43
  protected override _config: KTDropdownConfigInterface = this._defaultConfig;
43
44
  protected _disabled: boolean = false;
44
- protected _toggleElement: HTMLElement;
45
- protected _menuElement: HTMLElement;
45
+ protected _toggleElement!: HTMLElement;
46
+ protected _menuElement!: HTMLElement;
46
47
  protected _isTransitioning: boolean = false;
47
48
  protected _isOpen: boolean = false;
48
49
  /** Timestamp when _show() was last called; used to ignore duplicate _hide() from double handlers */
@@ -56,20 +57,25 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
56
57
  }
57
58
 
58
59
  this._init(element);
60
+ if (!this._element) {
61
+ return;
62
+ }
59
63
  this._buildConfig(config);
60
64
 
61
- this._toggleElement = this._element.querySelector(
65
+ const toggle = this._element.querySelector<HTMLElement>(
62
66
  '[data-kt-dropdown-toggle]',
63
- ) as HTMLElement;
64
- if (!this._toggleElement) {
67
+ );
68
+ if (!toggle) {
65
69
  return;
66
70
  }
67
- this._menuElement = this._element.querySelector(
71
+ const menu = this._element.querySelector<HTMLElement>(
68
72
  '[data-kt-dropdown-menu]',
69
- ) as HTMLElement;
70
- if (!this._menuElement) {
73
+ );
74
+ if (!menu) {
71
75
  return;
72
76
  }
77
+ this._toggleElement = toggle;
78
+ this._menuElement = menu;
73
79
 
74
80
  KTData.set(this._menuElement, 'dropdownElement', this._element);
75
81
  this._setupNestedDropdowns();
@@ -125,10 +131,13 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
125
131
 
126
132
  if (this._getOption('trigger') !== 'hover') return;
127
133
 
128
- if (KTData.get(this._element, 'hover') === '1') {
129
- clearTimeout(KTData.get(this._element, 'timeout') as number);
130
- KTData.remove(this._element, 'hover');
131
- KTData.remove(this._element, 'timeout');
134
+ const root = this._element;
135
+ if (!root) return;
136
+
137
+ if (KTData.get(root, 'hover') === '1') {
138
+ clearTimeout(KTData.get(root, 'timeout') as number);
139
+ KTData.remove(root, 'hover');
140
+ KTData.remove(root, 'timeout');
132
141
  }
133
142
 
134
143
  this._show();
@@ -139,22 +148,26 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
139
148
 
140
149
  if (this._getOption('trigger') !== 'hover') return;
141
150
 
142
- const relatedTarget = event.relatedTarget as HTMLElement;
143
- const isWithinDropdown = this._element.contains(relatedTarget);
151
+ const root = this._element;
152
+ if (!root) return;
153
+
154
+ const relatedTarget = event.relatedTarget as HTMLElement | null;
155
+ const isWithinDropdown =
156
+ relatedTarget !== null && root.contains(relatedTarget);
144
157
 
145
158
  if (isWithinDropdown) return;
146
159
 
147
160
  const timeout = setTimeout(
148
161
  () => {
149
- if (KTData.get(this._element, 'hover') === '1') {
162
+ if (KTData.get(root, 'hover') === '1') {
150
163
  this._hide();
151
164
  }
152
165
  },
153
166
  parseInt(this._getOption('hoverTimeout') as string),
154
167
  );
155
168
 
156
- KTData.set(this._element, 'hover', '1');
157
- KTData.set(this._element, 'timeout', timeout);
169
+ KTData.set(root, 'hover', '1');
170
+ KTData.set(root, 'timeout', timeout);
158
171
  }
159
172
 
160
173
  protected _toggle(): void {
@@ -175,10 +188,13 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
175
188
  this._dispatchEvent('show', payload);
176
189
  if (payload.cancel) return;
177
190
 
178
- KTDropdown.hide(this._element);
191
+ const root = this._element;
192
+ if (!root) return;
193
+
194
+ KTDropdown.hide(root);
179
195
 
180
196
  let zIndex: number = parseInt(this._getOption('zindex') as string);
181
- const parentZindex: number = KTDom.getHighestZindex(this._element);
197
+ const parentZindex: number = KTDom.getHighestZindex(root);
182
198
 
183
199
  if (parentZindex !== null && parentZindex >= zIndex) {
184
200
  zIndex = parentZindex + 1;
@@ -197,7 +213,7 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
197
213
  );
198
214
  this._toggleElement.classList.add('active');
199
215
  this._menuElement.classList.add('open');
200
- this._element.classList.add('open');
216
+ root.classList.add('open');
201
217
 
202
218
  this._initPopper();
203
219
 
@@ -221,12 +237,15 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
221
237
  this._dispatchEvent('hide', payload);
222
238
  if (payload.cancel) return;
223
239
 
240
+ const root = this._element;
241
+ if (!root) return;
242
+
224
243
  this._menuElement.style.opacity = '1';
225
244
  KTDom.reflow(this._menuElement);
226
245
  this._menuElement.style.opacity = '0';
227
246
  this._menuElement.classList.remove('open');
228
247
  this._toggleElement.classList.remove('active');
229
- this._element.classList.remove('open');
248
+ root.classList.remove('open');
230
249
 
231
250
  KTDom.transitionEnd(this._menuElement, () => {
232
251
  this._isTransitioning = false;
@@ -262,20 +281,27 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
262
281
  this._menuElement,
263
282
  this._getPopperConfig(),
264
283
  );
265
- KTData.set(this._element, 'popper', popper);
284
+ const root = this._element;
285
+ if (root) {
286
+ KTData.set(root, 'popper', popper);
287
+ }
266
288
  }
267
289
  }
268
290
 
269
291
  protected _destroyPopper(): void {
270
- if (KTData.has(this._element, 'popper')) {
271
- (KTData.get(this._element, 'popper') as PopperInstance).destroy();
272
- KTData.remove(this._element, 'popper');
292
+ const root = this._element;
293
+ if (!root) return;
294
+ if (KTData.has(root, 'popper')) {
295
+ (KTData.get(root, 'popper') as PopperInstance).destroy();
296
+ KTData.remove(root, 'popper');
273
297
  }
274
298
  }
275
299
 
276
300
  protected _isDropdownOpen(): boolean {
301
+ const root = this._element;
302
+ if (!root) return false;
277
303
  return (
278
- this._element.classList.contains('open') &&
304
+ root.classList.contains('open') &&
279
305
  this._menuElement.classList.contains('open')
280
306
  );
281
307
  }
@@ -379,7 +405,7 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
379
405
  }
380
406
 
381
407
  // Static Methods
382
- public static getElement(reference: HTMLElement): HTMLElement {
408
+ public static getElement(reference: HTMLElement): HTMLElement | null {
383
409
  if (reference && reference.hasAttribute('data-kt-dropdown-initialized'))
384
410
  return reference;
385
411
 
@@ -407,12 +433,13 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
407
433
  return null;
408
434
  }
409
435
 
410
- public static getInstance(element: HTMLElement): KTDropdown {
411
- element = this.getElement(element);
436
+ public static getInstance(element: HTMLElement): KTDropdown | null {
437
+ const resolved = this.getElement(element);
412
438
 
413
- if (!element) {
439
+ if (!resolved) {
414
440
  return null;
415
441
  }
442
+ element = resolved;
416
443
 
417
444
  if (KTData.has(element, 'dropdown')) {
418
445
  const instance = KTData.get(element, 'dropdown') as KTDropdown;
@@ -490,8 +517,9 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
490
517
  document.addEventListener('keydown', (event: KeyboardEvent) => {
491
518
  const dropdownEl = document.querySelector(
492
519
  '.open[data-kt-dropdown-initialized]',
493
- );
494
- const dropdown = KTDropdown.getInstance(dropdownEl as HTMLElement);
520
+ ) as HTMLElement | null;
521
+ if (!dropdownEl) return;
522
+ const dropdown = KTDropdown.getInstance(dropdownEl);
495
523
  if (!dropdown || !dropdown._getOption('keyboard')) return;
496
524
 
497
525
  if (
@@ -508,12 +536,13 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
508
536
  document.body,
509
537
  '[data-kt-dropdown-toggle], [data-kt-dropdown-menu]',
510
538
  'mouseover',
511
- (event: Event, target: HTMLElement) => {
539
+ ((event?: Event, target?: HTMLElement) => {
540
+ if (!event || !target) return;
512
541
  const dropdown = KTDropdown.getInstance(target);
513
542
  if (dropdown && dropdown._getOption('trigger') === 'hover') {
514
543
  dropdown.mouseover(event as MouseEvent);
515
544
  }
516
- },
545
+ }) as KTCallableType,
517
546
  );
518
547
  }
519
548
 
@@ -522,41 +551,40 @@ export class KTDropdown extends KTComponent implements KTDropdownInterface {
522
551
  document.body,
523
552
  '[data-kt-dropdown-toggle], [data-kt-dropdown-menu]',
524
553
  'mouseout',
525
- (event: Event, target: HTMLElement) => {
554
+ ((event?: Event, target?: HTMLElement) => {
555
+ if (!event || !target) return;
526
556
  const dropdown = KTDropdown.getInstance(target);
527
557
  if (dropdown && dropdown._getOption('trigger') === 'hover') {
528
558
  dropdown.mouseout(event as MouseEvent);
529
559
  }
530
- },
560
+ }) as KTCallableType,
531
561
  );
532
562
  }
533
563
 
534
564
  public static handleClick(): void {
535
- KTEventHandler.on(
536
- document.body,
537
- '[data-kt-dropdown-toggle]',
538
- 'click',
539
- (event: Event, target: HTMLElement) => {
540
- const dropdown = KTDropdown.getInstance(target);
541
- if (dropdown) {
542
- dropdown.click(event);
543
- }
544
- },
545
- );
565
+ KTEventHandler.on(document.body, '[data-kt-dropdown-toggle]', 'click', ((
566
+ event?: Event,
567
+ target?: HTMLElement,
568
+ ) => {
569
+ if (!event || !target) return;
570
+ const dropdown = KTDropdown.getInstance(target);
571
+ if (dropdown) {
572
+ dropdown.click(event);
573
+ }
574
+ }) as KTCallableType);
546
575
  }
547
576
 
548
577
  public static handleDismiss(): void {
549
- KTEventHandler.on(
550
- document.body,
551
- '[data-kt-dropdown-dismiss]',
552
- 'click',
553
- (event: Event, target: HTMLElement) => {
554
- const dropdown = KTDropdown.getInstance(target);
555
- if (dropdown) {
556
- dropdown.hide();
557
- }
558
- },
559
- );
578
+ KTEventHandler.on(document.body, '[data-kt-dropdown-dismiss]', 'click', ((
579
+ event?: Event,
580
+ target?: HTMLElement,
581
+ ) => {
582
+ if (!event || !target) return;
583
+ const dropdown = KTDropdown.getInstance(target);
584
+ if (dropdown) {
585
+ dropdown.hide();
586
+ }
587
+ }) as KTCallableType);
560
588
  }
561
589
 
562
590
  public static initHandlers(): void {
@@ -30,8 +30,21 @@
30
30
  @apply rounded-e-none!;
31
31
  }
32
32
 
33
+ /* Leading button before field: square inline-end + drop shadow so join matches addon pattern */
34
+ .kt-btn:has(+ input.kt-input),
35
+ .kt-btn:has(+ textarea.kt-input),
36
+ .kt-btn:has(+ .kt-input) {
37
+ @apply rounded-e-none! shadow-none!;
38
+ }
39
+
40
+ .kt-btn:has(+ input.kt-input).kt-btn-outline,
41
+ .kt-btn:has(+ textarea.kt-input).kt-btn-outline,
42
+ .kt-btn:has(+ .kt-input).kt-btn-outline {
43
+ @apply border-e-0!;
44
+ }
45
+
33
46
  .kt-input + .kt-btn {
34
- @apply rounded-s-none!;
47
+ @apply rounded-s-none! shadow-none!;
35
48
  }
36
49
 
37
50
  .kt-input + .kt-btn.kt-btn-outline {
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Tests for KTInputNumber component
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
+ import { KTInputNumber } from '../input-number';
7
+
8
+ describe('KTInputNumber', () => {
9
+ let container: HTMLElement;
10
+
11
+ beforeEach(() => {
12
+ document.body.innerHTML = '';
13
+ container = document.createElement('div');
14
+ container.id = 'test-container';
15
+ document.body.appendChild(container);
16
+ });
17
+
18
+ afterEach(() => {
19
+ document.body.innerHTML = '';
20
+ });
21
+
22
+ describe('initialization', () => {
23
+ it('initializes on wrapper with nested number input', () => {
24
+ const wrap = document.createElement('div');
25
+ wrap.setAttribute('data-kt-input-number', 'true');
26
+ const input = document.createElement('input');
27
+ input.type = 'number';
28
+ input.min = '0';
29
+ input.max = '10';
30
+ input.value = '3';
31
+ wrap.appendChild(input);
32
+ container.appendChild(wrap);
33
+
34
+ const instance = new KTInputNumber(wrap);
35
+ expect(instance.getElement()).toBe(wrap);
36
+ expect(instance.getNumberInput()).toBe(input);
37
+ expect(instance.getValue()).toBe(3);
38
+ instance.dispose();
39
+ });
40
+
41
+ it('initializes on the number input root', () => {
42
+ const input = document.createElement('input');
43
+ input.type = 'number';
44
+ input.setAttribute('data-kt-input-number', 'true');
45
+ input.min = '0';
46
+ input.max = '5';
47
+ input.value = '2';
48
+ container.appendChild(input);
49
+
50
+ const instance = new KTInputNumber(input);
51
+ expect(instance.getElement()).toBe(input);
52
+ expect(instance.getNumberInput()).toBe(input);
53
+ instance.dispose();
54
+ });
55
+
56
+ it('binds to the first number input when multiple exist', () => {
57
+ const wrap = document.createElement('div');
58
+ wrap.setAttribute('data-kt-input-number', 'true');
59
+ const a = document.createElement('input');
60
+ a.type = 'number';
61
+ a.value = '1';
62
+ const b = document.createElement('input');
63
+ b.type = 'number';
64
+ b.value = '2';
65
+ wrap.appendChild(a);
66
+ wrap.appendChild(b);
67
+ container.appendChild(wrap);
68
+
69
+ const instance = new KTInputNumber(wrap);
70
+ expect(instance.getNumberInput()).toBe(a);
71
+ instance.dispose();
72
+ });
73
+
74
+ it('does not initialize without a number input', () => {
75
+ const wrap = document.createElement('div');
76
+ wrap.setAttribute('data-kt-input-number', 'true');
77
+ container.appendChild(wrap);
78
+ const instance = new KTInputNumber(wrap);
79
+ expect(instance.getElement()).toBeNull();
80
+ expect(instance.getNumberInput()).toBeNull();
81
+ });
82
+ });
83
+
84
+ describe('events', () => {
85
+ it('dispatches kt.input-number.input with payload on native input', () => {
86
+ const wrap = document.createElement('div');
87
+ wrap.setAttribute('data-kt-input-number', 'true');
88
+ const input = document.createElement('input');
89
+ input.type = 'number';
90
+ input.min = '0';
91
+ input.max = '100';
92
+ input.step = '2';
93
+ input.value = '4';
94
+ wrap.appendChild(input);
95
+ container.appendChild(wrap);
96
+
97
+ const instance = new KTInputNumber(wrap);
98
+ const spy = vi.fn();
99
+ wrap.addEventListener('kt.input-number.input', spy);
100
+
101
+ input.dispatchEvent(new Event('input', { bubbles: true }));
102
+ expect(spy).toHaveBeenCalledTimes(1);
103
+ expect(spy.mock.calls[0][0].detail.payload).toMatchObject({
104
+ value: 4,
105
+ valueAsString: '4',
106
+ min: 0,
107
+ max: 100,
108
+ step: 2,
109
+ });
110
+
111
+ instance.dispose();
112
+ });
113
+
114
+ it('dispatches kt.input-number.change on native change', () => {
115
+ const wrap = document.createElement('div');
116
+ wrap.setAttribute('data-kt-input-number', 'true');
117
+ const input = document.createElement('input');
118
+ input.type = 'number';
119
+ input.value = '7';
120
+ wrap.appendChild(input);
121
+ container.appendChild(wrap);
122
+
123
+ const instance = new KTInputNumber(wrap);
124
+ const spy = vi.fn();
125
+ wrap.addEventListener('kt.input-number.change', spy);
126
+
127
+ input.dispatchEvent(new Event('change', { bubbles: true }));
128
+ expect(spy).toHaveBeenCalledTimes(1);
129
+ expect(spy.mock.calls[0][0].detail.payload.value).toBe(7);
130
+
131
+ instance.dispose();
132
+ });
133
+
134
+ it('uses null value when input is empty', () => {
135
+ const wrap = document.createElement('div');
136
+ wrap.setAttribute('data-kt-input-number', 'true');
137
+ const input = document.createElement('input');
138
+ input.type = 'number';
139
+ input.value = '';
140
+ wrap.appendChild(input);
141
+ container.appendChild(wrap);
142
+
143
+ const instance = new KTInputNumber(wrap);
144
+ const spy = vi.fn();
145
+ wrap.addEventListener('kt.input-number.input', spy);
146
+
147
+ input.dispatchEvent(new Event('input', { bubbles: true }));
148
+ expect(spy.mock.calls[0][0].detail.payload).toMatchObject({
149
+ value: null,
150
+ valueAsString: '',
151
+ });
152
+
153
+ instance.dispose();
154
+ });
155
+
156
+ it('omits step in payload when step="any"', () => {
157
+ const wrap = document.createElement('div');
158
+ wrap.setAttribute('data-kt-input-number', 'true');
159
+ const input = document.createElement('input');
160
+ input.type = 'number';
161
+ input.min = '0';
162
+ input.max = '10';
163
+ input.step = 'any';
164
+ input.value = '3';
165
+ wrap.appendChild(input);
166
+ container.appendChild(wrap);
167
+
168
+ new KTInputNumber(wrap);
169
+ const spy = vi.fn();
170
+ wrap.addEventListener('kt.input-number.input', spy);
171
+
172
+ input.dispatchEvent(new Event('input', { bubbles: true }));
173
+ expect(spy.mock.calls[0][0].detail.payload.step).toBeUndefined();
174
+
175
+ KTInputNumber.getInstance(wrap)?.dispose();
176
+ });
177
+ });
178
+
179
+ describe('increment and decrement', () => {
180
+ it('stepUp increases value by step within max', () => {
181
+ const wrap = document.createElement('div');
182
+ wrap.setAttribute('data-kt-input-number', 'true');
183
+ const input = document.createElement('input');
184
+ input.type = 'number';
185
+ input.min = '0';
186
+ input.max = '10';
187
+ input.step = '2';
188
+ input.value = '4';
189
+ const inc = document.createElement('button');
190
+ inc.type = 'button';
191
+ inc.setAttribute('data-kt-input-number-increment', 'true');
192
+ wrap.appendChild(inc);
193
+ wrap.appendChild(input);
194
+ container.appendChild(wrap);
195
+
196
+ const instance = new KTInputNumber(wrap);
197
+ inc.dispatchEvent(new MouseEvent('click', { bubbles: true }));
198
+ expect(input.value).toBe('6');
199
+ instance.dispose();
200
+ });
201
+
202
+ it('stepDown decreases value by step within min', () => {
203
+ const wrap = document.createElement('div');
204
+ wrap.setAttribute('data-kt-input-number', 'true');
205
+ const input = document.createElement('input');
206
+ input.type = 'number';
207
+ input.min = '0';
208
+ input.max = '10';
209
+ input.step = '3';
210
+ input.value = '6';
211
+ const dec = document.createElement('button');
212
+ dec.type = 'button';
213
+ dec.setAttribute('data-kt-input-number-decrement', 'true');
214
+ wrap.appendChild(dec);
215
+ wrap.appendChild(input);
216
+ container.appendChild(wrap);
217
+
218
+ const instance = new KTInputNumber(wrap);
219
+ dec.dispatchEvent(new MouseEvent('click', { bubbles: true }));
220
+ expect(input.value).toBe('3');
221
+ instance.dispose();
222
+ });
223
+
224
+ it('does not adjust when input is disabled', () => {
225
+ const wrap = document.createElement('div');
226
+ wrap.setAttribute('data-kt-input-number', 'true');
227
+ const input = document.createElement('input');
228
+ input.type = 'number';
229
+ input.min = '0';
230
+ input.max = '10';
231
+ input.value = '5';
232
+ input.disabled = true;
233
+ const inc = document.createElement('button');
234
+ inc.type = 'button';
235
+ inc.setAttribute('data-kt-input-number-increment', 'true');
236
+ wrap.appendChild(inc);
237
+ wrap.appendChild(input);
238
+ container.appendChild(wrap);
239
+
240
+ const instance = new KTInputNumber(wrap);
241
+ inc.dispatchEvent(new MouseEvent('click', { bubbles: true }));
242
+ expect(input.value).toBe('5');
243
+ instance.dispose();
244
+ });
245
+ });
246
+
247
+ describe('dispose', () => {
248
+ it('removes listeners and clears instance', () => {
249
+ const wrap = document.createElement('div');
250
+ wrap.setAttribute('data-kt-input-number', 'true');
251
+ const input = document.createElement('input');
252
+ input.type = 'number';
253
+ input.value = '1';
254
+ wrap.appendChild(input);
255
+ container.appendChild(wrap);
256
+
257
+ const instance = new KTInputNumber(wrap);
258
+ instance.dispose();
259
+ expect(KTInputNumber.getInstance(wrap)).toBeNull();
260
+ });
261
+ });
262
+
263
+ describe('createInstances', () => {
264
+ it('skips lazy roots', () => {
265
+ const wrap = document.createElement('div');
266
+ wrap.setAttribute('data-kt-input-number', 'true');
267
+ wrap.setAttribute('data-kt-input-number-lazy', 'true');
268
+ const input = document.createElement('input');
269
+ input.type = 'number';
270
+ input.value = '1';
271
+ wrap.appendChild(input);
272
+ container.appendChild(wrap);
273
+
274
+ KTInputNumber.createInstances();
275
+ expect(KTInputNumber.getInstance(wrap)).toBeNull();
276
+ });
277
+ });
278
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
+ * Copyright 2025 by Keenthemes Inc
4
+ */
5
+
6
+ export { KTInputNumber } from './input-number';
7
+ export type {
8
+ KTInputNumberConfigInterface,
9
+ KTInputNumberEventPayloadInterface,
10
+ KTInputNumberInterface,
11
+ } from './types';