@keenthemes/ktui 1.2.1 → 1.2.3

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 (304) hide show
  1. package/dist/ktui.js +2216 -845
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +24 -197
  5. package/lib/cjs/components/carousel/carousel.d.ts +102 -0
  6. package/lib/cjs/components/carousel/carousel.d.ts.map +1 -0
  7. package/lib/cjs/components/carousel/carousel.js +769 -0
  8. package/lib/cjs/components/carousel/carousel.js.map +1 -0
  9. package/lib/cjs/components/carousel/index.d.ts +7 -0
  10. package/lib/cjs/components/carousel/index.d.ts.map +1 -0
  11. package/lib/cjs/components/carousel/index.js +10 -0
  12. package/lib/cjs/components/carousel/index.js.map +1 -0
  13. package/lib/cjs/components/carousel/types.d.ts +36 -0
  14. package/lib/cjs/components/carousel/types.d.ts.map +1 -0
  15. package/lib/cjs/components/carousel/types.js +7 -0
  16. package/lib/cjs/components/carousel/types.js.map +1 -0
  17. package/lib/cjs/components/component.d.ts +3 -3
  18. package/lib/cjs/components/component.d.ts.map +1 -1
  19. package/lib/cjs/components/component.js +9 -1
  20. package/lib/cjs/components/component.js.map +1 -1
  21. package/lib/cjs/components/datatable/datatable-checkbox.d.ts +1 -1
  22. package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
  23. package/lib/cjs/components/datatable/datatable-checkbox.js +1 -1
  24. package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
  25. package/lib/cjs/components/datatable/datatable-sort.d.ts +1 -1
  26. package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
  27. package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
  28. package/lib/cjs/components/datatable/datatable.d.ts +2 -0
  29. package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
  30. package/lib/cjs/components/datatable/datatable.js +30 -17
  31. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  32. package/lib/cjs/components/datatable/types.d.ts +2 -1
  33. package/lib/cjs/components/datatable/types.d.ts.map +1 -1
  34. package/lib/cjs/components/drawer/drawer.d.ts.map +1 -1
  35. package/lib/cjs/components/drawer/drawer.js +3 -16
  36. package/lib/cjs/components/drawer/drawer.js.map +1 -1
  37. package/lib/cjs/components/dropdown/dropdown.d.ts +1 -1
  38. package/lib/cjs/components/dropdown/dropdown.d.ts.map +1 -1
  39. package/lib/cjs/components/dropdown/dropdown.js +2 -3
  40. package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
  41. package/lib/cjs/components/modal/modal.d.ts.map +1 -1
  42. package/lib/cjs/components/modal/modal.js +19 -13
  43. package/lib/cjs/components/modal/modal.js.map +1 -1
  44. package/lib/cjs/components/pin-input/index.d.ts +3 -0
  45. package/lib/cjs/components/pin-input/index.d.ts.map +1 -0
  46. package/lib/cjs/components/pin-input/index.js +6 -0
  47. package/lib/cjs/components/pin-input/index.js.map +1 -0
  48. package/lib/cjs/components/pin-input/pin-input.d.ts +56 -0
  49. package/lib/cjs/components/pin-input/pin-input.d.ts.map +1 -0
  50. package/lib/cjs/components/pin-input/pin-input.js +455 -0
  51. package/lib/cjs/components/pin-input/pin-input.js.map +1 -0
  52. package/lib/cjs/components/pin-input/types.d.ts +41 -0
  53. package/lib/cjs/components/pin-input/types.d.ts.map +1 -0
  54. package/lib/cjs/components/pin-input/types.js +6 -0
  55. package/lib/cjs/components/pin-input/types.js.map +1 -0
  56. package/lib/cjs/components/rating/rating.d.ts.map +1 -1
  57. package/lib/cjs/components/rating/rating.js.map +1 -1
  58. package/lib/cjs/components/select/combobox.d.ts.map +1 -1
  59. package/lib/cjs/components/select/combobox.js +25 -15
  60. package/lib/cjs/components/select/combobox.js.map +1 -1
  61. package/lib/cjs/components/select/config.d.ts +2 -2
  62. package/lib/cjs/components/select/config.d.ts.map +1 -1
  63. package/lib/cjs/components/select/config.js +10 -9
  64. package/lib/cjs/components/select/config.js.map +1 -1
  65. package/lib/cjs/components/select/dropdown.js.map +1 -1
  66. package/lib/cjs/components/select/option.d.ts +2 -1
  67. package/lib/cjs/components/select/option.d.ts.map +1 -1
  68. package/lib/cjs/components/select/option.js +9 -3
  69. package/lib/cjs/components/select/option.js.map +1 -1
  70. package/lib/cjs/components/select/remote.d.ts +1 -0
  71. package/lib/cjs/components/select/remote.d.ts.map +1 -1
  72. package/lib/cjs/components/select/remote.js +21 -14
  73. package/lib/cjs/components/select/remote.js.map +1 -1
  74. package/lib/cjs/components/select/search.d.ts +1 -1
  75. package/lib/cjs/components/select/search.d.ts.map +1 -1
  76. package/lib/cjs/components/select/search.js +34 -25
  77. package/lib/cjs/components/select/search.js.map +1 -1
  78. package/lib/cjs/components/select/select.d.ts +5 -3
  79. package/lib/cjs/components/select/select.d.ts.map +1 -1
  80. package/lib/cjs/components/select/select.js +31 -31
  81. package/lib/cjs/components/select/select.js.map +1 -1
  82. package/lib/cjs/components/select/tags.d.ts.map +1 -1
  83. package/lib/cjs/components/select/tags.js +22 -13
  84. package/lib/cjs/components/select/tags.js.map +1 -1
  85. package/lib/cjs/components/select/templates.d.ts.map +1 -1
  86. package/lib/cjs/components/select/templates.js +4 -4
  87. package/lib/cjs/components/select/templates.js.map +1 -1
  88. package/lib/cjs/components/select/types.d.ts +1 -1
  89. package/lib/cjs/components/select/types.d.ts.map +1 -1
  90. package/lib/cjs/components/select/utils.d.ts +4 -4
  91. package/lib/cjs/components/select/utils.d.ts.map +1 -1
  92. package/lib/cjs/components/select/utils.js +2 -3
  93. package/lib/cjs/components/select/utils.js.map +1 -1
  94. package/lib/cjs/components/sticky/sticky.d.ts +1 -1
  95. package/lib/cjs/components/sticky/sticky.d.ts.map +1 -1
  96. package/lib/cjs/components/sticky/sticky.js +13 -13
  97. package/lib/cjs/components/sticky/sticky.js.map +1 -1
  98. package/lib/cjs/components/theme-switch/theme-switch.d.ts +3 -0
  99. package/lib/cjs/components/theme-switch/theme-switch.d.ts.map +1 -1
  100. package/lib/cjs/components/theme-switch/theme-switch.js +17 -4
  101. package/lib/cjs/components/theme-switch/theme-switch.js.map +1 -1
  102. package/lib/cjs/components/toast/toast.d.ts.map +1 -1
  103. package/lib/cjs/components/toast/toast.js +17 -9
  104. package/lib/cjs/components/toast/toast.js.map +1 -1
  105. package/lib/cjs/components/toast/types.d.ts +3 -0
  106. package/lib/cjs/components/toast/types.d.ts.map +1 -1
  107. package/lib/cjs/components/toggle/toggle.d.ts +2 -0
  108. package/lib/cjs/components/toggle/toggle.d.ts.map +1 -1
  109. package/lib/cjs/components/toggle/toggle.js +11 -2
  110. package/lib/cjs/components/toggle/toggle.js.map +1 -1
  111. package/lib/cjs/components/toggle-password/toggle-password.d.ts.map +1 -1
  112. package/lib/cjs/components/toggle-password/toggle-password.js.map +1 -1
  113. package/lib/cjs/helpers/dom.d.ts +4 -4
  114. package/lib/cjs/helpers/dom.d.ts.map +1 -1
  115. package/lib/cjs/helpers/dom.js +8 -10
  116. package/lib/cjs/helpers/dom.js.map +1 -1
  117. package/lib/cjs/helpers/event-handler.d.ts +1 -1
  118. package/lib/cjs/helpers/event-handler.d.ts.map +1 -1
  119. package/lib/cjs/helpers/event-handler.js +3 -1
  120. package/lib/cjs/helpers/event-handler.js.map +1 -1
  121. package/lib/cjs/helpers/utils.d.ts +1 -1
  122. package/lib/cjs/helpers/utils.d.ts.map +1 -1
  123. package/lib/cjs/helpers/utils.js +4 -1
  124. package/lib/cjs/helpers/utils.js.map +1 -1
  125. package/lib/cjs/index.d.ts +8 -0
  126. package/lib/cjs/index.d.ts.map +1 -1
  127. package/lib/cjs/index.js +9 -1
  128. package/lib/cjs/index.js.map +1 -1
  129. package/lib/cjs/types.d.ts +1 -1
  130. package/lib/cjs/types.d.ts.map +1 -1
  131. package/lib/esm/components/carousel/carousel.d.ts +102 -0
  132. package/lib/esm/components/carousel/carousel.d.ts.map +1 -0
  133. package/lib/esm/components/carousel/carousel.js +766 -0
  134. package/lib/esm/components/carousel/carousel.js.map +1 -0
  135. package/lib/esm/components/carousel/index.d.ts +7 -0
  136. package/lib/esm/components/carousel/index.d.ts.map +1 -0
  137. package/lib/esm/components/carousel/index.js +6 -0
  138. package/lib/esm/components/carousel/index.js.map +1 -0
  139. package/lib/esm/components/carousel/types.d.ts +36 -0
  140. package/lib/esm/components/carousel/types.d.ts.map +1 -0
  141. package/lib/esm/components/carousel/types.js +6 -0
  142. package/lib/esm/components/carousel/types.js.map +1 -0
  143. package/lib/esm/components/component.d.ts +3 -3
  144. package/lib/esm/components/component.d.ts.map +1 -1
  145. package/lib/esm/components/component.js +9 -1
  146. package/lib/esm/components/component.js.map +1 -1
  147. package/lib/esm/components/datatable/datatable-checkbox.d.ts +1 -1
  148. package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
  149. package/lib/esm/components/datatable/datatable-checkbox.js +1 -1
  150. package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
  151. package/lib/esm/components/datatable/datatable-sort.d.ts +1 -1
  152. package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
  153. package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
  154. package/lib/esm/components/datatable/datatable.d.ts +2 -0
  155. package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
  156. package/lib/esm/components/datatable/datatable.js +30 -17
  157. package/lib/esm/components/datatable/datatable.js.map +1 -1
  158. package/lib/esm/components/datatable/types.d.ts +2 -1
  159. package/lib/esm/components/datatable/types.d.ts.map +1 -1
  160. package/lib/esm/components/drawer/drawer.d.ts.map +1 -1
  161. package/lib/esm/components/drawer/drawer.js +3 -16
  162. package/lib/esm/components/drawer/drawer.js.map +1 -1
  163. package/lib/esm/components/dropdown/dropdown.d.ts +1 -1
  164. package/lib/esm/components/dropdown/dropdown.d.ts.map +1 -1
  165. package/lib/esm/components/dropdown/dropdown.js +2 -3
  166. package/lib/esm/components/dropdown/dropdown.js.map +1 -1
  167. package/lib/esm/components/modal/modal.d.ts.map +1 -1
  168. package/lib/esm/components/modal/modal.js +19 -13
  169. package/lib/esm/components/modal/modal.js.map +1 -1
  170. package/lib/esm/components/pin-input/index.d.ts +3 -0
  171. package/lib/esm/components/pin-input/index.d.ts.map +1 -0
  172. package/lib/esm/components/pin-input/index.js +2 -0
  173. package/lib/esm/components/pin-input/index.js.map +1 -0
  174. package/lib/esm/components/pin-input/pin-input.d.ts +56 -0
  175. package/lib/esm/components/pin-input/pin-input.d.ts.map +1 -0
  176. package/lib/esm/components/pin-input/pin-input.js +452 -0
  177. package/lib/esm/components/pin-input/pin-input.js.map +1 -0
  178. package/lib/esm/components/pin-input/types.d.ts +41 -0
  179. package/lib/esm/components/pin-input/types.d.ts.map +1 -0
  180. package/lib/esm/components/pin-input/types.js +5 -0
  181. package/lib/esm/components/pin-input/types.js.map +1 -0
  182. package/lib/esm/components/rating/rating.d.ts.map +1 -1
  183. package/lib/esm/components/rating/rating.js.map +1 -1
  184. package/lib/esm/components/select/combobox.d.ts.map +1 -1
  185. package/lib/esm/components/select/combobox.js +25 -15
  186. package/lib/esm/components/select/combobox.js.map +1 -1
  187. package/lib/esm/components/select/config.d.ts +2 -2
  188. package/lib/esm/components/select/config.d.ts.map +1 -1
  189. package/lib/esm/components/select/config.js +10 -9
  190. package/lib/esm/components/select/config.js.map +1 -1
  191. package/lib/esm/components/select/dropdown.js.map +1 -1
  192. package/lib/esm/components/select/option.d.ts +2 -1
  193. package/lib/esm/components/select/option.d.ts.map +1 -1
  194. package/lib/esm/components/select/option.js +9 -3
  195. package/lib/esm/components/select/option.js.map +1 -1
  196. package/lib/esm/components/select/remote.d.ts +1 -0
  197. package/lib/esm/components/select/remote.d.ts.map +1 -1
  198. package/lib/esm/components/select/remote.js +21 -14
  199. package/lib/esm/components/select/remote.js.map +1 -1
  200. package/lib/esm/components/select/search.d.ts +1 -1
  201. package/lib/esm/components/select/search.d.ts.map +1 -1
  202. package/lib/esm/components/select/search.js +34 -25
  203. package/lib/esm/components/select/search.js.map +1 -1
  204. package/lib/esm/components/select/select.d.ts +5 -3
  205. package/lib/esm/components/select/select.d.ts.map +1 -1
  206. package/lib/esm/components/select/select.js +31 -31
  207. package/lib/esm/components/select/select.js.map +1 -1
  208. package/lib/esm/components/select/tags.d.ts.map +1 -1
  209. package/lib/esm/components/select/tags.js +22 -13
  210. package/lib/esm/components/select/tags.js.map +1 -1
  211. package/lib/esm/components/select/templates.d.ts.map +1 -1
  212. package/lib/esm/components/select/templates.js +4 -4
  213. package/lib/esm/components/select/templates.js.map +1 -1
  214. package/lib/esm/components/select/types.d.ts +1 -1
  215. package/lib/esm/components/select/types.d.ts.map +1 -1
  216. package/lib/esm/components/select/utils.d.ts +4 -4
  217. package/lib/esm/components/select/utils.d.ts.map +1 -1
  218. package/lib/esm/components/select/utils.js +2 -3
  219. package/lib/esm/components/select/utils.js.map +1 -1
  220. package/lib/esm/components/sticky/sticky.d.ts +1 -1
  221. package/lib/esm/components/sticky/sticky.d.ts.map +1 -1
  222. package/lib/esm/components/sticky/sticky.js +13 -13
  223. package/lib/esm/components/sticky/sticky.js.map +1 -1
  224. package/lib/esm/components/theme-switch/theme-switch.d.ts +3 -0
  225. package/lib/esm/components/theme-switch/theme-switch.d.ts.map +1 -1
  226. package/lib/esm/components/theme-switch/theme-switch.js +17 -4
  227. package/lib/esm/components/theme-switch/theme-switch.js.map +1 -1
  228. package/lib/esm/components/toast/toast.d.ts.map +1 -1
  229. package/lib/esm/components/toast/toast.js +17 -9
  230. package/lib/esm/components/toast/toast.js.map +1 -1
  231. package/lib/esm/components/toast/types.d.ts +3 -0
  232. package/lib/esm/components/toast/types.d.ts.map +1 -1
  233. package/lib/esm/components/toggle/toggle.d.ts +2 -0
  234. package/lib/esm/components/toggle/toggle.d.ts.map +1 -1
  235. package/lib/esm/components/toggle/toggle.js +11 -2
  236. package/lib/esm/components/toggle/toggle.js.map +1 -1
  237. package/lib/esm/components/toggle-password/toggle-password.d.ts.map +1 -1
  238. package/lib/esm/components/toggle-password/toggle-password.js.map +1 -1
  239. package/lib/esm/helpers/dom.d.ts +4 -4
  240. package/lib/esm/helpers/dom.d.ts.map +1 -1
  241. package/lib/esm/helpers/dom.js +8 -10
  242. package/lib/esm/helpers/dom.js.map +1 -1
  243. package/lib/esm/helpers/event-handler.d.ts +1 -1
  244. package/lib/esm/helpers/event-handler.d.ts.map +1 -1
  245. package/lib/esm/helpers/event-handler.js +3 -1
  246. package/lib/esm/helpers/event-handler.js.map +1 -1
  247. package/lib/esm/helpers/utils.d.ts +1 -1
  248. package/lib/esm/helpers/utils.d.ts.map +1 -1
  249. package/lib/esm/helpers/utils.js +4 -1
  250. package/lib/esm/helpers/utils.js.map +1 -1
  251. package/lib/esm/index.d.ts +8 -0
  252. package/lib/esm/index.d.ts.map +1 -1
  253. package/lib/esm/index.js +6 -0
  254. package/lib/esm/index.js.map +1 -1
  255. package/lib/esm/types.d.ts +1 -1
  256. package/lib/esm/types.d.ts.map +1 -1
  257. package/package.json +5 -2
  258. package/src/components/carousel/__tests__/carousel.test.ts +326 -0
  259. package/src/components/carousel/carousel.css +42 -0
  260. package/src/components/carousel/carousel.ts +847 -0
  261. package/src/components/carousel/index.ts +11 -0
  262. package/src/components/carousel/types.ts +38 -0
  263. package/src/components/clipboard/__tests__/clipboard.test.ts +4 -4
  264. package/src/components/component.ts +15 -5
  265. package/src/components/datatable/__tests__/currency-sort.test.ts +4 -3
  266. package/src/components/datatable/__tests__/pagination-reset.test.ts +7 -4
  267. package/src/components/datatable/__tests__/setup.ts +1 -1
  268. package/src/components/datatable/datatable-checkbox.ts +6 -4
  269. package/src/components/datatable/datatable-sort.ts +27 -7
  270. package/src/components/datatable/datatable.ts +64 -37
  271. package/src/components/datatable/types.ts +3 -1
  272. package/src/components/drawer/drawer.ts +3 -18
  273. package/src/components/dropdown/dropdown.ts +2 -3
  274. package/src/components/modal/modal.ts +22 -14
  275. package/src/components/pin-input/__tests__/pin-input.test.ts +928 -0
  276. package/src/components/pin-input/index.ts +6 -0
  277. package/src/components/pin-input/pin-input.ts +499 -0
  278. package/src/components/pin-input/types.ts +45 -0
  279. package/src/components/rating/rating.ts +0 -1
  280. package/src/components/repeater/__tests__/repeater.test.ts +5 -5
  281. package/src/components/select/__tests__/ux-behaviors.test.ts +4 -3
  282. package/src/components/select/combobox.ts +23 -16
  283. package/src/components/select/config.ts +15 -14
  284. package/src/components/select/dropdown.ts +1 -1
  285. package/src/components/select/option.ts +14 -4
  286. package/src/components/select/remote.ts +68 -56
  287. package/src/components/select/search.ts +30 -27
  288. package/src/components/select/select.ts +41 -37
  289. package/src/components/select/tags.ts +14 -8
  290. package/src/components/select/templates.ts +11 -6
  291. package/src/components/select/types.ts +1 -1
  292. package/src/components/select/utils.ts +7 -9
  293. package/src/components/sticky/sticky.ts +2 -2
  294. package/src/components/theme-switch/theme-switch.ts +22 -4
  295. package/src/components/toast/toast.ts +34 -21
  296. package/src/components/toast/types.ts +5 -1
  297. package/src/components/toggle/toggle.ts +12 -2
  298. package/src/components/toggle-password/toggle-password.ts +0 -1
  299. package/src/helpers/dom.ts +14 -17
  300. package/src/helpers/event-handler.ts +5 -6
  301. package/src/helpers/utils.ts +5 -2
  302. package/src/index.ts +18 -0
  303. package/src/types.ts +1 -1
  304. package/styles.css +1 -0
@@ -0,0 +1,6 @@
1
+ export { KTPinInput } from './pin-input';
2
+ export type {
3
+ KTPinInputConfigInterface,
4
+ KTPinInputEventPayloadInterface,
5
+ KTPinInputInterface,
6
+ } from './types';
@@ -0,0 +1,499 @@
1
+ /**
2
+ * KTUI - PIN / OTP multi-field input
3
+ * Copyright 2025 by Keenthemes Inc
4
+ */
5
+
6
+ import KTData from '../../helpers/data';
7
+ import KTComponent from '../component';
8
+ import {
9
+ KTPinInputConfigInterface,
10
+ KTPinInputEventPayloadInterface,
11
+ KTPinInputInterface,
12
+ } from './types';
13
+
14
+ declare global {
15
+ interface Window {
16
+ KTPinInput: typeof KTPinInput;
17
+ }
18
+ }
19
+
20
+ const ITEM_SELECTOR = '[data-kt-pin-input-item]';
21
+
22
+ export class KTPinInput extends KTComponent implements KTPinInputInterface {
23
+ protected override _name: string = 'pin-input';
24
+
25
+ protected override _defaultConfig: KTPinInputConfigInterface = {
26
+ lazy: false,
27
+ availableChars: '[0-9]',
28
+ name: '',
29
+ };
30
+
31
+ protected override _config: KTPinInputConfigInterface = this
32
+ ._defaultConfig as KTPinInputConfigInterface;
33
+
34
+ private _cells: HTMLInputElement[] = [];
35
+ private _hiddenInput: HTMLInputElement | null = null;
36
+ private _charRegex: RegExp | null = null;
37
+ private _wasComplete = false;
38
+
39
+ private _onKeydownBound = (e: KeyboardEvent) => this._onKeydown(e);
40
+ private _onBeforeInputBound = (e: Event) => this._onBeforeInput(e);
41
+ private _onInputBound = (e: Event) => this._onInput(e);
42
+ private _onPasteBound = (e: ClipboardEvent) => this._onPaste(e);
43
+
44
+ constructor(
45
+ element: HTMLElement,
46
+ config: KTPinInputConfigInterface | null = null,
47
+ ) {
48
+ super();
49
+
50
+ const cells = KTPinInput.collectItems(element);
51
+ if (cells.length === 0) {
52
+ return;
53
+ }
54
+
55
+ if (this._shouldSkipInit(element)) {
56
+ return;
57
+ }
58
+
59
+ this._cells = cells;
60
+ this._init(element);
61
+ this._buildConfig(config);
62
+ this._compileRegex();
63
+ this._ensureHiddenInput();
64
+ this._prepareCells();
65
+
66
+ const el = this._element;
67
+ if (el) {
68
+ el.addEventListener('keydown', this._onKeydownBound, true);
69
+ el.addEventListener('beforeinput', this._onBeforeInputBound, true);
70
+ el.addEventListener('input', this._onInputBound, true);
71
+ el.addEventListener('paste', this._onPasteBound, true);
72
+ }
73
+
74
+ this._syncFromDom(undefined, { silent: true });
75
+ }
76
+
77
+ private static collectItems(root: HTMLElement): HTMLInputElement[] {
78
+ return Array.from(
79
+ root.querySelectorAll<HTMLInputElement>(ITEM_SELECTOR),
80
+ ).filter((el) => root.contains(el));
81
+ }
82
+
83
+ private _compileRegex(): void {
84
+ const raw = this._getOption('availableChars');
85
+ const pattern =
86
+ typeof raw === 'string' && raw.trim() !== '' ? raw.trim() : '[0-9]';
87
+ try {
88
+ this._charRegex = new RegExp(pattern);
89
+ } catch {
90
+ this._charRegex = /[0-9]/;
91
+ }
92
+ }
93
+
94
+ private _isValidChar(char: string): boolean {
95
+ return Boolean(char && this._charRegex?.test(char));
96
+ }
97
+
98
+ private _enabledCells(): HTMLInputElement[] {
99
+ return this._cells.filter((c) => !c.disabled);
100
+ }
101
+
102
+ private _prepareCells(): void {
103
+ for (const cell of this._cells) {
104
+ cell.maxLength = 1;
105
+ cell.setAttribute('maxlength', '1');
106
+ }
107
+ }
108
+
109
+ private _activeCell(target: EventTarget | null): HTMLInputElement | null {
110
+ if (!(target instanceof HTMLInputElement)) {
111
+ return null;
112
+ }
113
+ if (!this._cells.includes(target) || target.disabled) {
114
+ return null;
115
+ }
116
+ return target;
117
+ }
118
+
119
+ private _cellIndex(cell: HTMLInputElement): number {
120
+ return this._cells.indexOf(cell);
121
+ }
122
+
123
+ private _focusNextEnabled(afterCell: HTMLInputElement): void {
124
+ const enabled = this._enabledCells();
125
+ const idx = enabled.indexOf(afterCell);
126
+ if (idx < 0 || idx >= enabled.length - 1) {
127
+ return;
128
+ }
129
+ const next = enabled[idx + 1];
130
+ next.focus();
131
+ next.select();
132
+ }
133
+
134
+ private _fillCellAndAdvance(cell: HTMLInputElement, char: string): void {
135
+ cell.value = char;
136
+ this._syncFromDom(this._cellIndex(cell));
137
+ this._focusNextEnabled(cell);
138
+ }
139
+
140
+ private _routeCharFromCell(target: HTMLInputElement, char: string): void {
141
+ const start = target.selectionStart ?? 0;
142
+ const end = target.selectionEnd ?? 0;
143
+ const v = target.value;
144
+ const replacing = end > start;
145
+ const atEnd = start === end && start === v.length;
146
+ if (v.length >= 1 && !replacing && atEnd) {
147
+ const enabled = this._enabledCells();
148
+ const idx = enabled.indexOf(target);
149
+ if (idx >= 0 && idx < enabled.length - 1) {
150
+ this._fillCellAndAdvance(enabled[idx + 1], char);
151
+ }
152
+ return;
153
+ }
154
+ this._fillCellAndAdvance(target, char);
155
+ }
156
+
157
+ private _ensureHiddenInput(): void {
158
+ const raw = this._getOption('name');
159
+ const name = typeof raw === 'string' ? raw.trim() : '';
160
+ if (!name || !this._element) {
161
+ this._hiddenInput = null;
162
+ return;
163
+ }
164
+
165
+ let h = this._element.querySelector<HTMLInputElement>(
166
+ 'input[type="hidden"][data-kt-pin-input-hidden]',
167
+ );
168
+ if (!h) {
169
+ h = document.createElement('input');
170
+ h.type = 'hidden';
171
+ h.name = name;
172
+ h.setAttribute('data-kt-pin-input-hidden', 'true');
173
+ this._element.appendChild(h);
174
+ }
175
+ this._hiddenInput = h;
176
+ }
177
+
178
+ private _focusCell(index: number, select = true): void {
179
+ const enabled = this._enabledCells();
180
+ const target = enabled[index];
181
+ if (!target) {
182
+ return;
183
+ }
184
+ target.focus();
185
+ if (select) {
186
+ target.select();
187
+ }
188
+ }
189
+
190
+ private _focusRelative(cell: HTMLInputElement, delta: number): void {
191
+ const enabled = this._enabledCells();
192
+ const idx = enabled.indexOf(cell);
193
+ if (idx < 0) {
194
+ return;
195
+ }
196
+ const next = idx + delta;
197
+ if (next >= 0 && next < enabled.length) {
198
+ enabled[next].focus();
199
+ enabled[next].select();
200
+ }
201
+ }
202
+
203
+ private _filterString(str: string): string {
204
+ let out = '';
205
+ for (let i = 0; i < str.length; i++) {
206
+ const ch = str[i];
207
+ if (this._isValidChar(ch)) {
208
+ out += ch;
209
+ }
210
+ }
211
+ return out;
212
+ }
213
+
214
+ private _buildPayload(cellIndex?: number): KTPinInputEventPayloadInterface {
215
+ const value = this.getValue();
216
+ const enabled = this._enabledCells();
217
+ const filled = enabled.filter((c) => Boolean(c.value)).length;
218
+ const complete = enabled.length > 0 && filled === enabled.length;
219
+ return {
220
+ value,
221
+ complete,
222
+ cellCount: enabled.length,
223
+ filledCount: filled,
224
+ ...(cellIndex !== undefined ? { cellIndex } : {}),
225
+ };
226
+ }
227
+
228
+ private _emit(payload: KTPinInputEventPayloadInterface): void {
229
+ this._fireEvent('input', payload);
230
+ this._dispatchEvent('kt.pin-input.input', payload);
231
+ this._dispatchEvent('kt.pin-input.change', payload);
232
+
233
+ if (payload.complete && !this._wasComplete) {
234
+ this._dispatchEvent('kt.pin-input.complete', payload);
235
+ }
236
+ this._wasComplete = payload.complete;
237
+ }
238
+
239
+ private _syncHidden(value: string): void {
240
+ if (this._hiddenInput) {
241
+ this._hiddenInput.value = value;
242
+ }
243
+ }
244
+
245
+ private _syncFromDom(cellIndex?: number, opts?: { silent?: boolean }): void {
246
+ const payload = this._buildPayload(cellIndex);
247
+ this._syncHidden(payload.value);
248
+ if (opts?.silent) {
249
+ this._wasComplete = payload.complete;
250
+ return;
251
+ }
252
+ this._emit(payload);
253
+ }
254
+
255
+ private _onKeydown(e: KeyboardEvent): void {
256
+ const target = this._activeCell(e.target);
257
+ if (!target) {
258
+ return;
259
+ }
260
+
261
+ if (e.key === 'Backspace') {
262
+ e.preventDefault();
263
+ if (target.value) {
264
+ target.value = '';
265
+ this._syncFromDom(this._cellIndex(target));
266
+ } else {
267
+ this._focusRelative(target, -1);
268
+ }
269
+ return;
270
+ }
271
+
272
+ if (e.key === 'ArrowLeft') {
273
+ e.preventDefault();
274
+ this._focusRelative(target, -1);
275
+ return;
276
+ }
277
+ if (e.key === 'ArrowRight') {
278
+ e.preventDefault();
279
+ this._focusRelative(target, 1);
280
+ return;
281
+ }
282
+ if (e.key === 'Home') {
283
+ e.preventDefault();
284
+ this._focusCell(0);
285
+ return;
286
+ }
287
+ if (e.key === 'End') {
288
+ e.preventDefault();
289
+ this._focusCell(this._enabledCells().length - 1);
290
+ return;
291
+ }
292
+
293
+ if (e.ctrlKey || e.metaKey || e.altKey) {
294
+ return;
295
+ }
296
+
297
+ if (e.key.length === 1) {
298
+ if (!this._isValidChar(e.key)) {
299
+ e.preventDefault();
300
+ return;
301
+ }
302
+ e.preventDefault();
303
+ this._routeCharFromCell(target, e.key);
304
+ }
305
+ }
306
+
307
+ private _onBeforeInput(e: Event): void {
308
+ if (!('inputType' in e)) {
309
+ return;
310
+ }
311
+ const ie = e as InputEvent;
312
+ if (ie.isComposing) {
313
+ return;
314
+ }
315
+ const target = this._activeCell(ie.target);
316
+ if (!target) {
317
+ return;
318
+ }
319
+
320
+ if (
321
+ ie.inputType === 'insertFromPaste' ||
322
+ ie.inputType === 'insertFromYank'
323
+ ) {
324
+ e.preventDefault();
325
+ return;
326
+ }
327
+
328
+ if (ie.inputType !== 'insertText' || ie.data == null) {
329
+ return;
330
+ }
331
+
332
+ const data = ie.data;
333
+ if (data.length > 1) {
334
+ e.preventDefault();
335
+ const filtered = this._filterString(data);
336
+ if (filtered.length) {
337
+ this._distributeFromIndex(this._cellIndex(target), filtered);
338
+ }
339
+ return;
340
+ }
341
+
342
+ if (data.length !== 1) {
343
+ return;
344
+ }
345
+
346
+ if (!this._isValidChar(data)) {
347
+ e.preventDefault();
348
+ return;
349
+ }
350
+
351
+ const start = target.selectionStart ?? 0;
352
+ const end = target.selectionEnd ?? 0;
353
+ const v = target.value;
354
+ const replacing = end > start;
355
+ const nextLen = replacing ? v.length - (end - start) + 1 : v.length + 1;
356
+ if (nextLen > 1 && !replacing) {
357
+ e.preventDefault();
358
+ this._routeCharFromCell(target, data);
359
+ }
360
+ }
361
+
362
+ private _onInput(e: Event): void {
363
+ const target = this._activeCell(e.target);
364
+ if (!target) {
365
+ return;
366
+ }
367
+
368
+ const v = target.value;
369
+ if (v.length > 1) {
370
+ const filtered = this._filterString(v);
371
+ target.value = '';
372
+ if (filtered.length) {
373
+ this._distributeFromIndex(this._cellIndex(target), filtered);
374
+ } else {
375
+ this._syncFromDom(this._cellIndex(target));
376
+ }
377
+ return;
378
+ }
379
+
380
+ if (v.length === 1 && !this._isValidChar(v)) {
381
+ target.value = '';
382
+ this._syncFromDom(this._cellIndex(target));
383
+ return;
384
+ }
385
+
386
+ this._syncFromDom(this._cellIndex(target));
387
+ if (v.length === 1) {
388
+ this._focusNextEnabled(target);
389
+ }
390
+ }
391
+
392
+ private _onPaste(e: ClipboardEvent): void {
393
+ const target = this._activeCell(e.target);
394
+ if (!target) {
395
+ return;
396
+ }
397
+ e.preventDefault();
398
+ const text = e.clipboardData?.getData('text') || '';
399
+ const filtered = this._filterString(text);
400
+ this._distributeFromIndex(this._cellIndex(target), filtered);
401
+ }
402
+
403
+ private _distributeFromIndex(startCellIndex: number, chars: string): void {
404
+ if (chars.length === 0) {
405
+ return;
406
+ }
407
+ const startCell = this._cells[startCellIndex];
408
+ if (!startCell || startCell.disabled) {
409
+ return;
410
+ }
411
+ const enabled = this._enabledCells();
412
+ const startIdx = enabled.indexOf(startCell);
413
+ if (startIdx < 0) {
414
+ return;
415
+ }
416
+ let charPos = 0;
417
+ for (let i = startIdx; i < enabled.length && charPos < chars.length; i++) {
418
+ enabled[i].value = chars[charPos];
419
+ charPos++;
420
+ }
421
+ if (charPos > 0) {
422
+ const lastIdx = Math.min(startIdx + charPos - 1, enabled.length - 1);
423
+ enabled[lastIdx].focus();
424
+ enabled[lastIdx].select();
425
+ }
426
+ this._syncFromDom(startCellIndex);
427
+ }
428
+
429
+ public getValue(): string {
430
+ return this._cells.map((c) => (c.disabled ? '' : c.value || '')).join('');
431
+ }
432
+
433
+ public setValue(value: string): void {
434
+ const enabled = this._enabledCells();
435
+ const filtered = this._filterString(typeof value === 'string' ? value : '');
436
+ for (let i = 0; i < enabled.length; i++) {
437
+ enabled[i].value = filtered[i] ?? '';
438
+ }
439
+ this._syncFromDom(0);
440
+ }
441
+
442
+ public override dispose(): void {
443
+ const el = this._element;
444
+ if (el) {
445
+ el.removeEventListener('keydown', this._onKeydownBound, true);
446
+ el.removeEventListener('beforeinput', this._onBeforeInputBound, true);
447
+ el.removeEventListener('input', this._onInputBound, true);
448
+ el.removeEventListener('paste', this._onPasteBound, true);
449
+ }
450
+ this._cells = [];
451
+ this._hiddenInput = null;
452
+ this._charRegex = null;
453
+ super.dispose();
454
+ }
455
+
456
+ public static getInstance(element: HTMLElement): KTPinInput | null {
457
+ if (!element) {
458
+ return null;
459
+ }
460
+ if (KTData.has(element, 'pin-input')) {
461
+ return KTData.get(element, 'pin-input') as KTPinInput;
462
+ }
463
+ return null;
464
+ }
465
+
466
+ public static getOrCreateInstance(
467
+ element: HTMLElement,
468
+ config?: KTPinInputConfigInterface,
469
+ ): KTPinInput | null {
470
+ const existing = this.getInstance(element);
471
+ if (existing) {
472
+ return existing;
473
+ }
474
+ if (this.collectItems(element).length === 0) {
475
+ return null;
476
+ }
477
+ new KTPinInput(element, config ?? undefined);
478
+ return this.getInstance(element);
479
+ }
480
+
481
+ public static createInstances(): void {
482
+ document
483
+ .querySelectorAll<HTMLElement>('[data-kt-pin-input]')
484
+ .forEach((el) => {
485
+ if (el.getAttribute('data-kt-pin-input-lazy') === 'true') {
486
+ return;
487
+ }
488
+ new KTPinInput(el);
489
+ });
490
+ }
491
+
492
+ public static init(): void {
493
+ KTPinInput.createInstances();
494
+ }
495
+ }
496
+
497
+ if (typeof window !== 'undefined') {
498
+ window.KTPinInput = KTPinInput;
499
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * KtUI - PIN / OTP multi-field input
3
+ */
4
+
5
+ export interface KTPinInputConfigInterface {
6
+ /**
7
+ * When true, skip auto-init; initialize manually with `getOrCreateInstance`.
8
+ */
9
+ lazy?: boolean;
10
+
11
+ /**
12
+ * Regular expression pattern string tested against **each** typed or pasted character.
13
+ * Default `[0-9]` (digits only). Examples: `[0-9a-fA-F]` (hex), `[a-zA-Z0-9]` (alphanumeric).
14
+ */
15
+ availableChars?: string;
16
+
17
+ /**
18
+ * If set, keeps a hidden `input[type="hidden"]` in sync for form posts.
19
+ * Also read from `data-kt-pin-input-name` on the root.
20
+ */
21
+ name?: string;
22
+ }
23
+
24
+ export interface KTPinInputEventPayloadInterface {
25
+ /** Concatenated values of all cells (including empty slots as empty string per cell). */
26
+ value: string;
27
+ /** True when every enabled cell has at least one character. */
28
+ complete: boolean;
29
+ /** Total enabled cell count. */
30
+ cellCount: number;
31
+ /** Enabled cells that currently have a value. */
32
+ filledCount: number;
33
+ /** Index of the cell that triggered the update, if known. */
34
+ cellIndex?: number;
35
+ }
36
+
37
+ export interface KTPinInputInterface {
38
+ getOption(name: string): unknown;
39
+ getElement(): HTMLElement;
40
+ getValue(): string;
41
+ setValue(value: string): void;
42
+ on(eventType: string, callback: CallableFunction): string;
43
+ off(eventType: string, eventId: string): void;
44
+ dispose(): void;
45
+ }
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import KTData from '../../helpers/data';
7
- import KTDom from '../../helpers/dom';
8
7
  import KTComponent from '../component';
9
8
  import { KTRatingConfigInterface, KTRatingInterface } from './types';
10
9
 
@@ -2,7 +2,7 @@
2
2
  * Tests for KTRepeater component
3
3
  */
4
4
 
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { describe, it, expect, afterEach } from 'vitest';
6
6
  import { KTRepeater } from '../repeater';
7
7
 
8
8
  function createFixture(options?: {
@@ -161,7 +161,7 @@ describe('KTRepeater', () => {
161
161
 
162
162
  describe('limit', () => {
163
163
  it('does not add more clones when at limit', () => {
164
- const { wrapper, trigger } = createFixture({ limit: 2 });
164
+ const { trigger, wrapper } = createFixture({ limit: 2 });
165
165
  const instance = new KTRepeater(trigger);
166
166
  expect(wrapper.children.length).toBe(1);
167
167
  instance.add();
@@ -174,7 +174,7 @@ describe('KTRepeater', () => {
174
174
  });
175
175
 
176
176
  it('disables trigger when at limit', () => {
177
- const { wrapper, trigger } = createFixture({ limit: 2 });
177
+ const { trigger } = createFixture({ limit: 2 });
178
178
  const instance = new KTRepeater(trigger);
179
179
  expect(trigger.hasAttribute('disabled')).toBe(false);
180
180
  instance.add();
@@ -201,7 +201,7 @@ describe('KTRepeater', () => {
201
201
  });
202
202
 
203
203
  it('allows unlimited clones when limit is 0 or omitted', () => {
204
- const { wrapper, trigger } = createFixture();
204
+ const { trigger, wrapper } = createFixture();
205
205
  trigger.removeAttribute('data-kt-repeater-limit');
206
206
  const instance = new KTRepeater(trigger);
207
207
  for (let i = 0; i < 5; i++) instance.add();
@@ -279,7 +279,7 @@ describe('KTRepeater', () => {
279
279
  });
280
280
 
281
281
  it('createInstances initializes all data-kt-repeater elements', () => {
282
- const { container, wrapper, trigger } = createFixture();
282
+ const { container, trigger } = createFixture();
283
283
  const trigger2 = document.createElement('button');
284
284
  trigger2.setAttribute('data-kt-repeater', '');
285
285
  trigger2.setAttribute('data-kt-repeater-target', '#repeater-target');
@@ -34,7 +34,7 @@ describe('KTSelect UX Behaviors', () => {
34
34
  /**
35
35
  * Helper to wait for KTSelect to fully initialize
36
36
  */
37
- const waitForInit = async (select: KTSelect): Promise<void> => {
37
+ const waitForInit = async (_select: KTSelect): Promise<void> => {
38
38
  // Wait for async initialization - KTSelect uses promises for setup
39
39
  await waitFor(200);
40
40
  // Wait for next tick to ensure all modules are initialized
@@ -52,7 +52,8 @@ describe('KTSelect UX Behaviors', () => {
52
52
  // Clean up all KTSelect instances
53
53
  const selects = document.querySelectorAll('.kt-select');
54
54
  selects.forEach((select) => {
55
- const instance = (select as any).instance;
55
+ const instance = (select as HTMLElement & { instance?: KTSelect })
56
+ .instance;
56
57
  if (instance && typeof instance.destroy === 'function') {
57
58
  instance.destroy();
58
59
  }
@@ -60,7 +61,7 @@ describe('KTSelect UX Behaviors', () => {
60
61
 
61
62
  // Clear document body
62
63
  document.body.innerHTML = '';
63
- container = null as any;
64
+ container = null as unknown as HTMLElement;
64
65
 
65
66
  // Clear all event listeners
66
67
  vi.clearAllMocks();