@keenthemes/ktui 1.0.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 (426) hide show
  1. package/CONTRIBUTING.md +88 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +124 -0
  4. package/dist/ktui.js +19201 -0
  5. package/dist/ktui.min.js +2 -0
  6. package/dist/ktui.min.js.map +1 -0
  7. package/lib/cjs/components/accordion/accordion.js +168 -0
  8. package/lib/cjs/components/accordion/accordion.js.map +1 -0
  9. package/lib/cjs/components/accordion/index.js +6 -0
  10. package/lib/cjs/components/accordion/index.js.map +1 -0
  11. package/lib/cjs/components/accordion/types.js +3 -0
  12. package/lib/cjs/components/accordion/types.js.map +1 -0
  13. package/lib/cjs/components/collapse/collapse.js +169 -0
  14. package/lib/cjs/components/collapse/collapse.js.map +1 -0
  15. package/lib/cjs/components/collapse/index.js +6 -0
  16. package/lib/cjs/components/collapse/index.js.map +1 -0
  17. package/lib/cjs/components/collapse/types.js +3 -0
  18. package/lib/cjs/components/collapse/types.js.map +1 -0
  19. package/lib/cjs/components/component.js +135 -0
  20. package/lib/cjs/components/component.js.map +1 -0
  21. package/lib/cjs/components/config.js +26 -0
  22. package/lib/cjs/components/config.js.map +1 -0
  23. package/lib/cjs/components/config.umd.js +23 -0
  24. package/lib/cjs/components/config.umd.js.map +1 -0
  25. package/lib/cjs/components/constants.js +15 -0
  26. package/lib/cjs/components/constants.js.map +1 -0
  27. package/lib/cjs/components/datatable/datatable.js +1464 -0
  28. package/lib/cjs/components/datatable/datatable.js.map +1 -0
  29. package/lib/cjs/components/datatable/index.js +6 -0
  30. package/lib/cjs/components/datatable/index.js.map +1 -0
  31. package/lib/cjs/components/datatable/types.js +3 -0
  32. package/lib/cjs/components/datatable/types.js.map +1 -0
  33. package/lib/cjs/components/dismiss/dismiss.js +131 -0
  34. package/lib/cjs/components/dismiss/dismiss.js.map +1 -0
  35. package/lib/cjs/components/dismiss/index.js +6 -0
  36. package/lib/cjs/components/dismiss/index.js.map +1 -0
  37. package/lib/cjs/components/dismiss/types.js +3 -0
  38. package/lib/cjs/components/dismiss/types.js.map +1 -0
  39. package/lib/cjs/components/drawer/drawer.js +347 -0
  40. package/lib/cjs/components/drawer/drawer.js.map +1 -0
  41. package/lib/cjs/components/drawer/index.js +6 -0
  42. package/lib/cjs/components/drawer/index.js.map +1 -0
  43. package/lib/cjs/components/drawer/types.js +3 -0
  44. package/lib/cjs/components/drawer/types.js.map +1 -0
  45. package/lib/cjs/components/dropdown/dropdown.js +403 -0
  46. package/lib/cjs/components/dropdown/dropdown.js.map +1 -0
  47. package/lib/cjs/components/dropdown/index.js +6 -0
  48. package/lib/cjs/components/dropdown/index.js.map +1 -0
  49. package/lib/cjs/components/dropdown/types.js +3 -0
  50. package/lib/cjs/components/dropdown/types.js.map +1 -0
  51. package/lib/cjs/components/image-input/image-input.js +191 -0
  52. package/lib/cjs/components/image-input/image-input.js.map +1 -0
  53. package/lib/cjs/components/image-input/index.js +6 -0
  54. package/lib/cjs/components/image-input/index.js.map +1 -0
  55. package/lib/cjs/components/image-input/types.js +3 -0
  56. package/lib/cjs/components/image-input/types.js.map +1 -0
  57. package/lib/cjs/components/menu/index.js +6 -0
  58. package/lib/cjs/components/menu/index.js.map +1 -0
  59. package/lib/cjs/components/menu/menu.js +1021 -0
  60. package/lib/cjs/components/menu/menu.js.map +1 -0
  61. package/lib/cjs/components/menu/types.js +3 -0
  62. package/lib/cjs/components/menu/types.js.map +1 -0
  63. package/lib/cjs/components/modal/index.js +6 -0
  64. package/lib/cjs/components/modal/index.js.map +1 -0
  65. package/lib/cjs/components/modal/modal.js +316 -0
  66. package/lib/cjs/components/modal/modal.js.map +1 -0
  67. package/lib/cjs/components/modal/types.js +3 -0
  68. package/lib/cjs/components/modal/types.js.map +1 -0
  69. package/lib/cjs/components/reparent/index.js +6 -0
  70. package/lib/cjs/components/reparent/index.js.map +1 -0
  71. package/lib/cjs/components/reparent/reparent.js +93 -0
  72. package/lib/cjs/components/reparent/reparent.js.map +1 -0
  73. package/lib/cjs/components/reparent/types.js +3 -0
  74. package/lib/cjs/components/reparent/types.js.map +1 -0
  75. package/lib/cjs/components/scrollable/index.js +6 -0
  76. package/lib/cjs/components/scrollable/index.js.map +1 -0
  77. package/lib/cjs/components/scrollable/scrollable.js +259 -0
  78. package/lib/cjs/components/scrollable/scrollable.js.map +1 -0
  79. package/lib/cjs/components/scrollable/types.js +3 -0
  80. package/lib/cjs/components/scrollable/types.js.map +1 -0
  81. package/lib/cjs/components/scrollspy/index.js +6 -0
  82. package/lib/cjs/components/scrollspy/index.js.map +1 -0
  83. package/lib/cjs/components/scrollspy/scrollspy.js +174 -0
  84. package/lib/cjs/components/scrollspy/scrollspy.js.map +1 -0
  85. package/lib/cjs/components/scrollspy/types.js +3 -0
  86. package/lib/cjs/components/scrollspy/types.js.map +1 -0
  87. package/lib/cjs/components/scrollto/index.js +6 -0
  88. package/lib/cjs/components/scrollto/index.js.map +1 -0
  89. package/lib/cjs/components/scrollto/scrollto.js +103 -0
  90. package/lib/cjs/components/scrollto/scrollto.js.map +1 -0
  91. package/lib/cjs/components/scrollto/types.js +3 -0
  92. package/lib/cjs/components/scrollto/types.js.map +1 -0
  93. package/lib/cjs/components/stepper/index.js +6 -0
  94. package/lib/cjs/components/stepper/index.js.map +1 -0
  95. package/lib/cjs/components/stepper/stepper.js +258 -0
  96. package/lib/cjs/components/stepper/stepper.js.map +1 -0
  97. package/lib/cjs/components/stepper/types.js +3 -0
  98. package/lib/cjs/components/stepper/types.js.map +1 -0
  99. package/lib/cjs/components/sticky/index.js +6 -0
  100. package/lib/cjs/components/sticky/index.js.map +1 -0
  101. package/lib/cjs/components/sticky/sticky.js +297 -0
  102. package/lib/cjs/components/sticky/sticky.js.map +1 -0
  103. package/lib/cjs/components/sticky/types.js +3 -0
  104. package/lib/cjs/components/sticky/types.js.map +1 -0
  105. package/lib/cjs/components/tabs/index.js +6 -0
  106. package/lib/cjs/components/tabs/index.js.map +1 -0
  107. package/lib/cjs/components/tabs/tabs.js +146 -0
  108. package/lib/cjs/components/tabs/tabs.js.map +1 -0
  109. package/lib/cjs/components/tabs/types.js +3 -0
  110. package/lib/cjs/components/tabs/types.js.map +1 -0
  111. package/lib/cjs/components/theme/index.js +6 -0
  112. package/lib/cjs/components/theme/index.js.map +1 -0
  113. package/lib/cjs/components/theme/theme.js +147 -0
  114. package/lib/cjs/components/theme/theme.js.map +1 -0
  115. package/lib/cjs/components/theme/types.js +3 -0
  116. package/lib/cjs/components/theme/types.js.map +1 -0
  117. package/lib/cjs/components/toggle/index.js +6 -0
  118. package/lib/cjs/components/toggle/index.js.map +1 -0
  119. package/lib/cjs/components/toggle/toggle.js +139 -0
  120. package/lib/cjs/components/toggle/toggle.js.map +1 -0
  121. package/lib/cjs/components/toggle/types.js +3 -0
  122. package/lib/cjs/components/toggle/types.js.map +1 -0
  123. package/lib/cjs/components/toggle-password/index.js +6 -0
  124. package/lib/cjs/components/toggle-password/index.js.map +1 -0
  125. package/lib/cjs/components/toggle-password/toggle-password.js +131 -0
  126. package/lib/cjs/components/toggle-password/toggle-password.js.map +1 -0
  127. package/lib/cjs/components/toggle-password/types.js +3 -0
  128. package/lib/cjs/components/toggle-password/types.js.map +1 -0
  129. package/lib/cjs/components/tooltip/index.js +6 -0
  130. package/lib/cjs/components/tooltip/index.js.map +1 -0
  131. package/lib/cjs/components/tooltip/tooltip.js +271 -0
  132. package/lib/cjs/components/tooltip/tooltip.js.map +1 -0
  133. package/lib/cjs/components/tooltip/types.js +3 -0
  134. package/lib/cjs/components/tooltip/types.js.map +1 -0
  135. package/lib/cjs/helpers/data.js +33 -0
  136. package/lib/cjs/helpers/data.js.map +1 -0
  137. package/lib/cjs/helpers/dom.js +297 -0
  138. package/lib/cjs/helpers/dom.js.map +1 -0
  139. package/lib/cjs/helpers/event-handler.js +36 -0
  140. package/lib/cjs/helpers/event-handler.js.map +1 -0
  141. package/lib/cjs/helpers/utils.js +94 -0
  142. package/lib/cjs/helpers/utils.js.map +1 -0
  143. package/lib/cjs/index.js +105 -0
  144. package/lib/cjs/index.js.map +1 -0
  145. package/lib/cjs/types.js +3 -0
  146. package/lib/cjs/types.js.map +1 -0
  147. package/lib/esm/components/accordion/accordion.js +165 -0
  148. package/lib/esm/components/accordion/accordion.js.map +1 -0
  149. package/lib/esm/components/accordion/index.js +2 -0
  150. package/lib/esm/components/accordion/index.js.map +1 -0
  151. package/lib/esm/components/accordion/types.js +2 -0
  152. package/lib/esm/components/accordion/types.js.map +1 -0
  153. package/lib/esm/components/collapse/collapse.js +166 -0
  154. package/lib/esm/components/collapse/collapse.js.map +1 -0
  155. package/lib/esm/components/collapse/index.js +2 -0
  156. package/lib/esm/components/collapse/index.js.map +1 -0
  157. package/lib/esm/components/collapse/types.js +2 -0
  158. package/lib/esm/components/collapse/types.js.map +1 -0
  159. package/lib/esm/components/component.js +133 -0
  160. package/lib/esm/components/component.js.map +1 -0
  161. package/lib/esm/components/config.js +24 -0
  162. package/lib/esm/components/config.js.map +1 -0
  163. package/lib/esm/components/config.umd.js +23 -0
  164. package/lib/esm/components/config.umd.js.map +1 -0
  165. package/lib/esm/components/constants.js +12 -0
  166. package/lib/esm/components/constants.js.map +1 -0
  167. package/lib/esm/components/datatable/datatable.js +1461 -0
  168. package/lib/esm/components/datatable/datatable.js.map +1 -0
  169. package/lib/esm/components/datatable/index.js +2 -0
  170. package/lib/esm/components/datatable/index.js.map +1 -0
  171. package/lib/esm/components/datatable/types.js +2 -0
  172. package/lib/esm/components/datatable/types.js.map +1 -0
  173. package/lib/esm/components/dismiss/dismiss.js +128 -0
  174. package/lib/esm/components/dismiss/dismiss.js.map +1 -0
  175. package/lib/esm/components/dismiss/index.js +2 -0
  176. package/lib/esm/components/dismiss/index.js.map +1 -0
  177. package/lib/esm/components/dismiss/types.js +2 -0
  178. package/lib/esm/components/dismiss/types.js.map +1 -0
  179. package/lib/esm/components/drawer/drawer.js +344 -0
  180. package/lib/esm/components/drawer/drawer.js.map +1 -0
  181. package/lib/esm/components/drawer/index.js +2 -0
  182. package/lib/esm/components/drawer/index.js.map +1 -0
  183. package/lib/esm/components/drawer/types.js +2 -0
  184. package/lib/esm/components/drawer/types.js.map +1 -0
  185. package/lib/esm/components/dropdown/dropdown.js +400 -0
  186. package/lib/esm/components/dropdown/dropdown.js.map +1 -0
  187. package/lib/esm/components/dropdown/index.js +2 -0
  188. package/lib/esm/components/dropdown/index.js.map +1 -0
  189. package/lib/esm/components/dropdown/types.js +2 -0
  190. package/lib/esm/components/dropdown/types.js.map +1 -0
  191. package/lib/esm/components/image-input/image-input.js +188 -0
  192. package/lib/esm/components/image-input/image-input.js.map +1 -0
  193. package/lib/esm/components/image-input/index.js +2 -0
  194. package/lib/esm/components/image-input/index.js.map +1 -0
  195. package/lib/esm/components/image-input/types.js +2 -0
  196. package/lib/esm/components/image-input/types.js.map +1 -0
  197. package/lib/esm/components/menu/index.js +2 -0
  198. package/lib/esm/components/menu/index.js.map +1 -0
  199. package/lib/esm/components/menu/menu.js +1018 -0
  200. package/lib/esm/components/menu/menu.js.map +1 -0
  201. package/lib/esm/components/menu/types.js +2 -0
  202. package/lib/esm/components/menu/types.js.map +1 -0
  203. package/lib/esm/components/modal/index.js +2 -0
  204. package/lib/esm/components/modal/index.js.map +1 -0
  205. package/lib/esm/components/modal/modal.js +313 -0
  206. package/lib/esm/components/modal/modal.js.map +1 -0
  207. package/lib/esm/components/modal/types.js +2 -0
  208. package/lib/esm/components/modal/types.js.map +1 -0
  209. package/lib/esm/components/reparent/index.js +2 -0
  210. package/lib/esm/components/reparent/index.js.map +1 -0
  211. package/lib/esm/components/reparent/reparent.js +90 -0
  212. package/lib/esm/components/reparent/reparent.js.map +1 -0
  213. package/lib/esm/components/reparent/types.js +2 -0
  214. package/lib/esm/components/reparent/types.js.map +1 -0
  215. package/lib/esm/components/scrollable/index.js +2 -0
  216. package/lib/esm/components/scrollable/index.js.map +1 -0
  217. package/lib/esm/components/scrollable/scrollable.js +256 -0
  218. package/lib/esm/components/scrollable/scrollable.js.map +1 -0
  219. package/lib/esm/components/scrollable/types.js +2 -0
  220. package/lib/esm/components/scrollable/types.js.map +1 -0
  221. package/lib/esm/components/scrollspy/index.js +2 -0
  222. package/lib/esm/components/scrollspy/index.js.map +1 -0
  223. package/lib/esm/components/scrollspy/scrollspy.js +171 -0
  224. package/lib/esm/components/scrollspy/scrollspy.js.map +1 -0
  225. package/lib/esm/components/scrollspy/types.js +2 -0
  226. package/lib/esm/components/scrollspy/types.js.map +1 -0
  227. package/lib/esm/components/scrollto/index.js +2 -0
  228. package/lib/esm/components/scrollto/index.js.map +1 -0
  229. package/lib/esm/components/scrollto/scrollto.js +100 -0
  230. package/lib/esm/components/scrollto/scrollto.js.map +1 -0
  231. package/lib/esm/components/scrollto/types.js +2 -0
  232. package/lib/esm/components/scrollto/types.js.map +1 -0
  233. package/lib/esm/components/stepper/index.js +2 -0
  234. package/lib/esm/components/stepper/index.js.map +1 -0
  235. package/lib/esm/components/stepper/stepper.js +255 -0
  236. package/lib/esm/components/stepper/stepper.js.map +1 -0
  237. package/lib/esm/components/stepper/types.js +2 -0
  238. package/lib/esm/components/stepper/types.js.map +1 -0
  239. package/lib/esm/components/sticky/index.js +2 -0
  240. package/lib/esm/components/sticky/index.js.map +1 -0
  241. package/lib/esm/components/sticky/sticky.js +294 -0
  242. package/lib/esm/components/sticky/sticky.js.map +1 -0
  243. package/lib/esm/components/sticky/types.js +2 -0
  244. package/lib/esm/components/sticky/types.js.map +1 -0
  245. package/lib/esm/components/tabs/index.js +2 -0
  246. package/lib/esm/components/tabs/index.js.map +1 -0
  247. package/lib/esm/components/tabs/tabs.js +143 -0
  248. package/lib/esm/components/tabs/tabs.js.map +1 -0
  249. package/lib/esm/components/tabs/types.js +2 -0
  250. package/lib/esm/components/tabs/types.js.map +1 -0
  251. package/lib/esm/components/theme/index.js +2 -0
  252. package/lib/esm/components/theme/index.js.map +1 -0
  253. package/lib/esm/components/theme/theme.js +144 -0
  254. package/lib/esm/components/theme/theme.js.map +1 -0
  255. package/lib/esm/components/theme/types.js +2 -0
  256. package/lib/esm/components/theme/types.js.map +1 -0
  257. package/lib/esm/components/toggle/index.js +2 -0
  258. package/lib/esm/components/toggle/index.js.map +1 -0
  259. package/lib/esm/components/toggle/toggle.js +136 -0
  260. package/lib/esm/components/toggle/toggle.js.map +1 -0
  261. package/lib/esm/components/toggle/types.js +2 -0
  262. package/lib/esm/components/toggle/types.js.map +1 -0
  263. package/lib/esm/components/toggle-password/index.js +2 -0
  264. package/lib/esm/components/toggle-password/index.js.map +1 -0
  265. package/lib/esm/components/toggle-password/toggle-password.js +128 -0
  266. package/lib/esm/components/toggle-password/toggle-password.js.map +1 -0
  267. package/lib/esm/components/toggle-password/types.js +2 -0
  268. package/lib/esm/components/toggle-password/types.js.map +1 -0
  269. package/lib/esm/components/tooltip/index.js +2 -0
  270. package/lib/esm/components/tooltip/index.js.map +1 -0
  271. package/lib/esm/components/tooltip/tooltip.js +268 -0
  272. package/lib/esm/components/tooltip/tooltip.js.map +1 -0
  273. package/lib/esm/components/tooltip/types.js +2 -0
  274. package/lib/esm/components/tooltip/types.js.map +1 -0
  275. package/lib/esm/helpers/data.js +31 -0
  276. package/lib/esm/helpers/data.js.map +1 -0
  277. package/lib/esm/helpers/dom.js +295 -0
  278. package/lib/esm/helpers/dom.js.map +1 -0
  279. package/lib/esm/helpers/event-handler.js +34 -0
  280. package/lib/esm/helpers/event-handler.js.map +1 -0
  281. package/lib/esm/helpers/utils.js +92 -0
  282. package/lib/esm/helpers/utils.js.map +1 -0
  283. package/lib/esm/index.js +79 -0
  284. package/lib/esm/index.js.map +1 -0
  285. package/lib/esm/types.js +2 -0
  286. package/lib/esm/types.js.map +1 -0
  287. package/package.json +85 -0
  288. package/prettier.config.js +9 -0
  289. package/src/components/accordion/accordion-menu.css +51 -0
  290. package/src/components/accordion/accordion.css +86 -0
  291. package/src/components/accordion/accordion.ts +221 -0
  292. package/src/components/accordion/index.ts +7 -0
  293. package/src/components/accordion/types.ts +16 -0
  294. package/src/components/alert/alert.css +282 -0
  295. package/src/components/avatar/avatar.css +46 -0
  296. package/src/components/badge/badge.css +176 -0
  297. package/src/components/breadcrumb/breadcrumb.css +38 -0
  298. package/src/components/btn/btn.css +227 -0
  299. package/src/components/card/card.css +158 -0
  300. package/src/components/checkbox/checkbox.css +74 -0
  301. package/src/components/collapse/collapse.css +14 -0
  302. package/src/components/collapse/collapse.ts +200 -0
  303. package/src/components/collapse/index.ts +7 -0
  304. package/src/components/collapse/types.ts +16 -0
  305. package/src/components/component.ts +132 -0
  306. package/src/components/constants.ts +16 -0
  307. package/src/components/datatable/datatable-checkbox.ts +236 -0
  308. package/src/components/datatable/datatable-sort.ts +154 -0
  309. package/src/components/datatable/datatable.css +110 -0
  310. package/src/components/datatable/datatable.ts +1657 -0
  311. package/src/components/datatable/index.ts +19 -0
  312. package/src/components/datatable/types.ts +203 -0
  313. package/src/components/datepicker/calendar.ts +1397 -0
  314. package/src/components/datepicker/config.ts +368 -0
  315. package/src/components/datepicker/datepicker.css +7 -0
  316. package/src/components/datepicker/datepicker.ts +1287 -0
  317. package/src/components/datepicker/dropdown.ts +757 -0
  318. package/src/components/datepicker/events.ts +149 -0
  319. package/src/components/datepicker/index.ts +10 -0
  320. package/src/components/datepicker/keyboard.ts +646 -0
  321. package/src/components/datepicker/locales.ts +80 -0
  322. package/src/components/datepicker/templates.ts +792 -0
  323. package/src/components/datepicker/types.ts +154 -0
  324. package/src/components/datepicker/utils.ts +631 -0
  325. package/src/components/dismiss/dismiss.css +10 -0
  326. package/src/components/dismiss/dismiss.ts +152 -0
  327. package/src/components/dismiss/index.ts +7 -0
  328. package/src/components/dismiss/types.ts +17 -0
  329. package/src/components/drawer/drawer.css +97 -0
  330. package/src/components/drawer/drawer.ts +437 -0
  331. package/src/components/drawer/index.ts +7 -0
  332. package/src/components/drawer/types.ts +25 -0
  333. package/src/components/dropdown/dropdown-menu.css +56 -0
  334. package/src/components/dropdown/dropdown.css +46 -0
  335. package/src/components/dropdown/dropdown.ts +549 -0
  336. package/src/components/dropdown/index.ts +7 -0
  337. package/src/components/dropdown/types.ts +28 -0
  338. package/src/components/form/form.css +54 -0
  339. package/src/components/image-input/image-input.css +56 -0
  340. package/src/components/image-input/image-input.ts +249 -0
  341. package/src/components/image-input/index.ts +10 -0
  342. package/src/components/image-input/types.ts +12 -0
  343. package/src/components/input/input-group.css +42 -0
  344. package/src/components/input/input.css +136 -0
  345. package/src/components/kbd/kbd.css +30 -0
  346. package/src/components/label/label.css +20 -0
  347. package/src/components/link/link.css +81 -0
  348. package/src/components/modal/index.ts +7 -0
  349. package/src/components/modal/modal.css +73 -0
  350. package/src/components/modal/modal.ts +382 -0
  351. package/src/components/modal/types.ts +21 -0
  352. package/src/components/pagination/pagination.css +26 -0
  353. package/src/components/popover/popover.css +22 -0
  354. package/src/components/progress/progress.css +51 -0
  355. package/src/components/radio/radio.css +51 -0
  356. package/src/components/reparent/index.ts +7 -0
  357. package/src/components/reparent/reparent.ts +109 -0
  358. package/src/components/reparent/types.ts +15 -0
  359. package/src/components/scrollable/index.ts +10 -0
  360. package/src/components/scrollable/scrollable.css +29 -0
  361. package/src/components/scrollable/scrollable.ts +297 -0
  362. package/src/components/scrollable/types.ts +16 -0
  363. package/src/components/scrollspy/index.ts +7 -0
  364. package/src/components/scrollspy/scrollspy.css +13 -0
  365. package/src/components/scrollspy/scrollspy.ts +224 -0
  366. package/src/components/scrollspy/types.ts +15 -0
  367. package/src/components/scrollto/index.ts +7 -0
  368. package/src/components/scrollto/scrollto.ts +127 -0
  369. package/src/components/scrollto/types.ts +15 -0
  370. package/src/components/select/combobox.ts +305 -0
  371. package/src/components/select/config.ts +324 -0
  372. package/src/components/select/dropdown.ts +510 -0
  373. package/src/components/select/index.ts +13 -0
  374. package/src/components/select/option.ts +43 -0
  375. package/src/components/select/remote.ts +477 -0
  376. package/src/components/select/search.ts +430 -0
  377. package/src/components/select/select.css +105 -0
  378. package/src/components/select/select.ts +1916 -0
  379. package/src/components/select/tags.ts +123 -0
  380. package/src/components/select/templates.ts +531 -0
  381. package/src/components/select/types.ts +36 -0
  382. package/src/components/select/utils.ts +747 -0
  383. package/src/components/select/variants.css +5 -0
  384. package/src/components/separator/separator.css +14 -0
  385. package/src/components/skeleton/skeleton.css +10 -0
  386. package/src/components/stepper/index.ts +7 -0
  387. package/src/components/stepper/stepper.css +49 -0
  388. package/src/components/stepper/stepper.ts +308 -0
  389. package/src/components/stepper/types.ts +13 -0
  390. package/src/components/sticky/index.ts +7 -0
  391. package/src/components/sticky/sticky.css +22 -0
  392. package/src/components/sticky/sticky.ts +381 -0
  393. package/src/components/sticky/types.ts +23 -0
  394. package/src/components/switch/switch.css +110 -0
  395. package/src/components/table/table.css +168 -0
  396. package/src/components/tabs/index.ts +7 -0
  397. package/src/components/tabs/tabs.css +40 -0
  398. package/src/components/tabs/tabs.ts +190 -0
  399. package/src/components/tabs/types.ts +13 -0
  400. package/src/components/textarea/textarea.css +35 -0
  401. package/src/components/theme-switch/index.ts +10 -0
  402. package/src/components/theme-switch/theme-switch.css +22 -0
  403. package/src/components/theme-switch/theme-switch.ts +176 -0
  404. package/src/components/theme-switch/types.ts +15 -0
  405. package/src/components/toggle/index.ts +7 -0
  406. package/src/components/toggle/toggle.css +13 -0
  407. package/src/components/toggle/toggle.ts +173 -0
  408. package/src/components/toggle/types.ts +18 -0
  409. package/src/components/toggle-group/toggle-group.css +55 -0
  410. package/src/components/toggle-password/index.ts +10 -0
  411. package/src/components/toggle-password/toggle-password.css +13 -0
  412. package/src/components/toggle-password/toggle-password.ts +159 -0
  413. package/src/components/toggle-password/types.ts +13 -0
  414. package/src/components/tooltip/index.ts +7 -0
  415. package/src/components/tooltip/tooltip.css +18 -0
  416. package/src/components/tooltip/tooltip.ts +361 -0
  417. package/src/components/tooltip/types.ts +28 -0
  418. package/src/helpers/data.ts +46 -0
  419. package/src/helpers/dom.ts +405 -0
  420. package/src/helpers/event-handler.ts +61 -0
  421. package/src/helpers/utils.ts +183 -0
  422. package/src/index.ts +113 -0
  423. package/src/types.ts +23 -0
  424. package/styles.css +48 -0
  425. package/tsconfig.json +17 -0
  426. package/webpack.config.js +113 -0
@@ -0,0 +1,747 @@
1
+ /**
2
+ * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
+ * Copyright 2025 by Keenthemes Inc
4
+ * @version: 1.0.0
5
+ */
6
+ // utils.ts
7
+
8
+ import { KTSelect } from './select';
9
+ import { defaultTemplates, getTemplateStrings } from './templates';
10
+ import { KTSelectConfigInterface } from './config';
11
+
12
+ /**
13
+ * Format a number as a currency string
14
+ */
15
+ export function formatCurrency(value: number): string {
16
+ return new Intl.NumberFormat('en-US', {
17
+ style: 'currency',
18
+ currency: 'USD',
19
+ }).format(value);
20
+ }
21
+
22
+ /**
23
+ * Filter options based on a search query
24
+ */
25
+ export function filterOptions(
26
+ options: HTMLElement[],
27
+ query: string,
28
+ config: KTSelectConfigInterface,
29
+ dropdownElement: HTMLElement,
30
+ onVisibleCount?: (count: number) => void,
31
+ ): number {
32
+ let visibleOptionsCount = 0;
33
+
34
+ // Clear existing "no results" messages
35
+ const noResultsElement = dropdownElement.querySelector(
36
+ '[data-kt-select-no-results]',
37
+ );
38
+ if (noResultsElement) {
39
+ noResultsElement.remove();
40
+ }
41
+
42
+ // For empty query, ensure highlights are cleared from all options
43
+ if (!query || query.trim() === '') {
44
+ // Just make all options visible without highlighting
45
+ for (const option of options) {
46
+ // Make option visible by removing hidden classes and inline styles
47
+ option.classList.remove('hidden');
48
+
49
+ // Clean up any inline display styles from legacy code
50
+ if (
51
+ option.hasAttribute('style') &&
52
+ option.getAttribute('style').includes('display:')
53
+ ) {
54
+ const styleAttr = option.getAttribute('style');
55
+ if (
56
+ styleAttr.trim() === 'display: none;' ||
57
+ styleAttr.trim() === 'display: block;'
58
+ ) {
59
+ option.removeAttribute('style');
60
+ } else {
61
+ option.setAttribute(
62
+ 'style',
63
+ styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
64
+ );
65
+ }
66
+ }
67
+ visibleOptionsCount++;
68
+ }
69
+
70
+ // Call the callback with the visible count if provided
71
+ if (onVisibleCount) {
72
+ onVisibleCount(visibleOptionsCount);
73
+ }
74
+
75
+ return visibleOptionsCount;
76
+ }
77
+
78
+ // Filter options based on query
79
+ for (const option of options) {
80
+ const optionText = option.textContent?.toLowerCase() || '';
81
+ const isMatch = optionText.includes(query.toLowerCase());
82
+
83
+ if (isMatch || query.trim() === '') {
84
+ // Show option by removing the hidden class and any display inline styles
85
+ option.classList.remove('hidden');
86
+
87
+ // Remove any inline display styles that might be present
88
+ if (
89
+ option.hasAttribute('style') &&
90
+ option.getAttribute('style').includes('display:')
91
+ ) {
92
+ const styleAttr = option.getAttribute('style');
93
+ if (
94
+ styleAttr.trim() === 'display: none;' ||
95
+ styleAttr.trim() === 'display: block;'
96
+ ) {
97
+ option.removeAttribute('style');
98
+ } else {
99
+ option.setAttribute(
100
+ 'style',
101
+ styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
102
+ );
103
+ }
104
+ }
105
+
106
+ visibleOptionsCount++;
107
+
108
+ // Apply highlighting if needed - but preserve the option structure
109
+ if (isMatch && config.searchHighlight && query.trim() !== '') {
110
+ // Clone option elements that contain icons or descriptions
111
+ const hasIcon =
112
+ option.querySelector('[data-kt-select-option-icon]') !== null;
113
+ const hasDescription =
114
+ option.querySelector('[data-kt-select-option-description]') !== null;
115
+
116
+ if (hasIcon || hasDescription) {
117
+ // Only highlight the text part without changing structure
118
+ const titleElement = option.querySelector(
119
+ '[data-kt-option-title]',
120
+ ) as HTMLElement;
121
+ if (titleElement) {
122
+ highlightTextInElement(titleElement, query, config);
123
+ }
124
+ } else {
125
+ // Simple option with just text - standard highlighting
126
+ highlightTextInElement(option, query, config);
127
+ }
128
+ }
129
+ } else {
130
+ // Hide option using hidden class
131
+ option.classList.add('hidden');
132
+
133
+ // Remove any inline display styles
134
+ if (
135
+ option.hasAttribute('style') &&
136
+ option.getAttribute('style').includes('display:')
137
+ ) {
138
+ const styleAttr = option.getAttribute('style');
139
+ if (
140
+ styleAttr.trim() === 'display: none;' ||
141
+ styleAttr.trim() === 'display: block;'
142
+ ) {
143
+ option.removeAttribute('style');
144
+ } else {
145
+ option.setAttribute(
146
+ 'style',
147
+ styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
148
+ );
149
+ }
150
+ }
151
+ }
152
+
153
+ // Early exit if maxItems limit is reached
154
+ if (config.searchMaxItems && visibleOptionsCount >= config.searchMaxItems) {
155
+ break;
156
+ }
157
+ }
158
+
159
+ // Call the callback with the visible count if provided
160
+ if (onVisibleCount) {
161
+ onVisibleCount(visibleOptionsCount);
162
+ }
163
+
164
+ return visibleOptionsCount;
165
+ }
166
+
167
+ /**
168
+ * Highlight text only within a specific element, preserving other elements
169
+ */
170
+ export function highlightTextInElement(
171
+ element: HTMLElement,
172
+ query: string,
173
+ config: KTSelectConfigInterface,
174
+ ): void {
175
+ if (!element || !query || query.trim() === '') return;
176
+
177
+ const queryLower = query.toLowerCase();
178
+
179
+ function walk(node: Node) {
180
+ if (node.nodeType === Node.TEXT_NODE) {
181
+ const text = node.nodeValue || '';
182
+ const textLower = text.toLowerCase();
183
+ const matchIndex = textLower.indexOf(queryLower);
184
+
185
+ if (matchIndex !== -1) {
186
+ const before = text.slice(0, matchIndex);
187
+ const match = text.slice(matchIndex, matchIndex + query.length);
188
+ const after = text.slice(matchIndex + query.length);
189
+
190
+ const frag = document.createDocumentFragment();
191
+ if (before) frag.appendChild(document.createTextNode(before));
192
+
193
+ // Use the highlight template, which returns an HTMLElement
194
+ const highlightSpan = defaultTemplates.highlight(config, match);
195
+ frag.appendChild(highlightSpan);
196
+
197
+ if (after) frag.appendChild(document.createTextNode(after));
198
+
199
+ node.parentNode?.replaceChild(frag, node);
200
+ // Only highlight the first occurrence in this node
201
+ }
202
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
203
+ // Don't re-highlight already highlighted nodes
204
+ if ((node as HTMLElement).classList.contains('highlight')) return;
205
+ Array.from(node.childNodes).forEach(walk);
206
+ }
207
+ }
208
+ walk(element);
209
+ }
210
+
211
+ /**
212
+ * Focus manager for keyboard navigation
213
+ * Consolidates redundant focus management logic into shared functions
214
+ */
215
+ export class FocusManager {
216
+ private _element: HTMLElement;
217
+ private _optionsSelector: string;
218
+ private _focusedOptionIndex: number | null = null;
219
+ private _focusClass: string;
220
+ private _hoverClass: string;
221
+ private _bgClass: string;
222
+ private _fontClass: string;
223
+ private _eventManager: EventManager;
224
+
225
+ constructor(
226
+ element: HTMLElement,
227
+ optionsSelector: string = '[data-kt-select-option]',
228
+ config?: KTSelectConfigInterface,
229
+ ) {
230
+ this._element = element;
231
+ this._optionsSelector = optionsSelector;
232
+ this._eventManager = new EventManager();
233
+
234
+ // Use config values if provided, otherwise fallback to defaults
235
+ this._focusClass = config?.focusClass || 'option-focused';
236
+ this._hoverClass = config?.hoverClass || 'hovered';
237
+ this._bgClass = config?.bgClass || 'bg-blue-50';
238
+ this._fontClass = config?.fontClass || 'font-medium';
239
+
240
+ // Add click handler to update focus state when options are clicked
241
+ this._setupOptionClickHandlers();
242
+ }
243
+
244
+ /**
245
+ * Set up click handlers for all options to update focus state
246
+ */
247
+ private _setupOptionClickHandlers(): void {
248
+ // Add click handler to the options container
249
+ this._eventManager.addListener(this._element, 'click', (e: Event) => {
250
+ const target = e.target as HTMLElement;
251
+ const optionElement = target.closest(this._optionsSelector);
252
+
253
+ if (optionElement) {
254
+ // First clear all focus
255
+ this.resetFocus();
256
+
257
+ // Then update the focused index based on the clicked option
258
+ const options = this.getVisibleOptions();
259
+ const clickedIndex = options.indexOf(optionElement as HTMLElement);
260
+
261
+ if (clickedIndex >= 0) {
262
+ this._focusedOptionIndex = clickedIndex;
263
+ this.applyFocus(options[clickedIndex]);
264
+ }
265
+ }
266
+ });
267
+ }
268
+
269
+ /**
270
+ * Get all visible options
271
+ */
272
+ public getVisibleOptions(): HTMLElement[] {
273
+ return Array.from(
274
+ this._element.querySelectorAll(this._optionsSelector),
275
+ ).filter((option) => {
276
+ const element = option as HTMLElement;
277
+ // Check only for hidden class
278
+ if (element.classList.contains('hidden')) {
279
+ return false;
280
+ }
281
+ // Also check inline styles for backward compatibility
282
+ if (element.style.display === 'none') {
283
+ return false;
284
+ }
285
+ return true;
286
+ }) as HTMLElement[];
287
+ }
288
+
289
+ /**
290
+ * Focus the next visible option
291
+ */
292
+ public focusNext(): HTMLElement | null {
293
+ const options = this.getVisibleOptions();
294
+ if (options.length === 0) return null;
295
+
296
+ this.resetFocus();
297
+
298
+ if (this._focusedOptionIndex === null) {
299
+ this._focusedOptionIndex = 0;
300
+ } else {
301
+ this._focusedOptionIndex =
302
+ (this._focusedOptionIndex + 1) % options.length;
303
+ }
304
+
305
+ const option = options[this._focusedOptionIndex];
306
+ this.applyFocus(option);
307
+ this.scrollIntoView(option);
308
+
309
+ return option;
310
+ }
311
+
312
+ /**
313
+ * Focus the previous visible option
314
+ */
315
+ public focusPrevious(): HTMLElement | null {
316
+ const options = this.getVisibleOptions();
317
+ if (options.length === 0) return null;
318
+
319
+ this.resetFocus();
320
+
321
+ if (this._focusedOptionIndex === null) {
322
+ this._focusedOptionIndex = options.length - 1;
323
+ } else {
324
+ this._focusedOptionIndex =
325
+ (this._focusedOptionIndex - 1 + options.length) % options.length;
326
+ }
327
+
328
+ const option = options[this._focusedOptionIndex];
329
+ this.applyFocus(option);
330
+ this.scrollIntoView(option);
331
+
332
+ return option;
333
+ }
334
+
335
+ /**
336
+ * Apply focus to a specific option
337
+ */
338
+ public applyFocus(option: HTMLElement): void {
339
+ if (!option) return;
340
+
341
+ // Remove focus from all options first
342
+ this.resetFocus();
343
+
344
+ // Add focus to this option
345
+ option.classList.add(this._focusClass);
346
+ option.classList.add(this._hoverClass);
347
+ option.classList.add(this._bgClass);
348
+ option.classList.add(this._fontClass);
349
+ }
350
+
351
+ /**
352
+ * Reset focus on all options
353
+ */
354
+ public resetFocus(): void {
355
+ // Find all elements with the focus classes
356
+ const focusedElements = this._element.querySelectorAll(
357
+ `.${this._focusClass}, .${this._hoverClass}, .${this._bgClass}, .${this._fontClass}`,
358
+ );
359
+
360
+ // Remove classes from all elements
361
+ focusedElements.forEach((element) => {
362
+ element.classList.remove(this._focusClass);
363
+ element.classList.remove(this._hoverClass);
364
+ element.classList.remove(this._bgClass);
365
+ element.classList.remove(this._fontClass);
366
+ });
367
+
368
+ // Reset index if visible options have changed
369
+ const visibleOptions = this.getVisibleOptions();
370
+ if (
371
+ this._focusedOptionIndex !== null &&
372
+ this._focusedOptionIndex >= visibleOptions.length
373
+ ) {
374
+ this._focusedOptionIndex = null;
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Ensure the focused option is visible in the scrollable container
380
+ */
381
+ public scrollIntoView(option: HTMLElement): void {
382
+ if (!option) return;
383
+
384
+ const container = this._element.querySelector(
385
+ '[data-kt-select-options-container]',
386
+ );
387
+ if (!container) return;
388
+
389
+ const optionRect = option.getBoundingClientRect();
390
+ const containerRect = container.getBoundingClientRect();
391
+
392
+ // Check if option is below the visible area
393
+ if (optionRect.bottom > containerRect.bottom) {
394
+ option.scrollIntoView({ block: 'end', behavior: 'smooth' });
395
+ }
396
+ // Check if option is above the visible area
397
+ else if (optionRect.top < containerRect.top) {
398
+ option.scrollIntoView({ block: 'start', behavior: 'smooth' });
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Focus a specific option by its value
404
+ */
405
+ public focusOptionByValue(value: string): boolean {
406
+ const options = this.getVisibleOptions();
407
+ const index = options.findIndex((option) => option.dataset.value === value);
408
+
409
+ if (index >= 0) {
410
+ this._focusedOptionIndex = index;
411
+ this.applyFocus(options[index]);
412
+ this.scrollIntoView(options[index]);
413
+ return true;
414
+ }
415
+
416
+ return false;
417
+ }
418
+
419
+ /**
420
+ * Get the currently focused option
421
+ */
422
+ public getFocusedOption(): HTMLElement | null {
423
+ const options = this.getVisibleOptions();
424
+
425
+ if (
426
+ this._focusedOptionIndex !== null &&
427
+ this._focusedOptionIndex < options.length
428
+ ) {
429
+ return options[this._focusedOptionIndex];
430
+ }
431
+
432
+ return null;
433
+ }
434
+
435
+ /**
436
+ * Get the index of the currently focused option
437
+ */
438
+ public getFocusedIndex(): number | null {
439
+ return this._focusedOptionIndex;
440
+ }
441
+
442
+ /**
443
+ * Set the focused option index directly
444
+ */
445
+ public setFocusedIndex(index: number | null): void {
446
+ this._focusedOptionIndex = index;
447
+ }
448
+
449
+ /**
450
+ * Clean up event listeners
451
+ */
452
+ public dispose(): void {
453
+ if (this._eventManager) {
454
+ this._eventManager.removeAllListeners(this._element);
455
+ }
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Shared keyboard navigation handler for dropdown options
461
+ * Can be used by both combobox and search modules
462
+ */
463
+ export function handleDropdownKeyNavigation(
464
+ event: KeyboardEvent,
465
+ select: KTSelect,
466
+ config: {
467
+ multiple?: boolean;
468
+ closeOnSelect?: boolean;
469
+ },
470
+ callbacks?: {
471
+ onArrowUp?: () => void;
472
+ onArrowDown?: () => void;
473
+ onEnter?: () => void;
474
+ onClose?: () => void;
475
+ },
476
+ ): void {
477
+ try {
478
+ // Get the dropdown state
479
+ const isDropdownOpen = (select as any)._dropdownIsOpen;
480
+
481
+ // Log the event to help debug
482
+ const origin = 'handleDropdownKeyNavigation';
483
+ if (select.getConfig && select.getConfig().debug)
484
+ console.log(
485
+ `[${origin}] Key: ${event.key}, Dropdown open: ${isDropdownOpen}`,
486
+ );
487
+
488
+ // Handle basic keyboard navigation
489
+ switch (event.key) {
490
+ case 'ArrowDown':
491
+ if (!isDropdownOpen) {
492
+ if (select.getConfig && select.getConfig().debug)
493
+ console.log(`[${origin}] Opening dropdown on ArrowDown`);
494
+ select.openDropdown();
495
+
496
+ // Focus the first option after opening
497
+ setTimeout(() => {
498
+ (select as any)._focusNextOption();
499
+ }, 50);
500
+ } else if (callbacks?.onArrowDown) {
501
+ if (select.getConfig && select.getConfig().debug)
502
+ console.log(`[${origin}] Using custom onArrowDown callback`);
503
+ callbacks.onArrowDown();
504
+ } else {
505
+ if (select.getConfig && select.getConfig().debug)
506
+ console.log(`[${origin}] Using default _focusNextOption`);
507
+ const focusedOption = (select as any)._focusNextOption();
508
+
509
+ // Ensure we have a focused option
510
+ if (focusedOption) {
511
+ if (select.getConfig && select.getConfig().debug)
512
+ console.log(`[${origin}] Focused next option:`, focusedOption);
513
+ }
514
+ }
515
+ event.preventDefault();
516
+ break;
517
+
518
+ case 'ArrowUp':
519
+ if (!isDropdownOpen) {
520
+ if (select.getConfig && select.getConfig().debug)
521
+ console.log(`[${origin}] Opening dropdown on ArrowUp`);
522
+ select.openDropdown();
523
+
524
+ // Focus the last option after opening
525
+ setTimeout(() => {
526
+ (select as any)._focusPreviousOption();
527
+ }, 50);
528
+ } else if (callbacks?.onArrowUp) {
529
+ if (select.getConfig && select.getConfig().debug)
530
+ console.log(`[${origin}] Using custom onArrowUp callback`);
531
+ callbacks.onArrowUp();
532
+ } else {
533
+ if (select.getConfig && select.getConfig().debug)
534
+ console.log(`[${origin}] Using default _focusPreviousOption`);
535
+ const focusedOption = (select as any)._focusPreviousOption();
536
+
537
+ // Ensure we have a focused option
538
+ if (focusedOption) {
539
+ if (select.getConfig && select.getConfig().debug)
540
+ console.log(
541
+ `[${origin}] Focused previous option:`,
542
+ focusedOption,
543
+ );
544
+ }
545
+ }
546
+ event.preventDefault();
547
+ break;
548
+
549
+ case 'Enter':
550
+ // Prevent form submission
551
+ event.preventDefault();
552
+
553
+ if (isDropdownOpen) {
554
+ if (select.getConfig && select.getConfig().debug)
555
+ console.log(`[${origin}] Enter pressed with dropdown open`);
556
+
557
+ // For combobox mode, ensure we update the input value directly
558
+ const isCombobox = select.getConfig().mode === 'combobox';
559
+ const comboboxModule = (select as any)._comboboxModule;
560
+
561
+ if (callbacks?.onEnter) {
562
+ if (select.getConfig && select.getConfig().debug)
563
+ console.log(`[${origin}] Using custom onEnter callback`);
564
+ callbacks.onEnter();
565
+ } else {
566
+ if (select.getConfig && select.getConfig().debug)
567
+ console.log(`[${origin}] Using default selectFocusedOption`);
568
+ // Make sure there is a focused option before trying to select it
569
+ if (
570
+ (select as any)._focusManager &&
571
+ (select as any)._focusManager.getFocusedOption()
572
+ ) {
573
+ select.selectFocusedOption();
574
+ } else {
575
+ // If no option is focused, try to focus the first one
576
+ const focusedOption = (select as any)._focusNextOption();
577
+ // Only select if an option was successfully focused
578
+ if (focusedOption) {
579
+ select.selectFocusedOption();
580
+ }
581
+ }
582
+ }
583
+
584
+ // Close dropdown after selection if not multiple and closeOnSelect is true
585
+ if (!config.multiple && config.closeOnSelect !== false) {
586
+ if (select.getConfig && select.getConfig().debug)
587
+ console.log(`[${origin}] Closing dropdown after selection`);
588
+ select.closeDropdown();
589
+ }
590
+ } else {
591
+ // If dropdown is closed, open it on Enter
592
+ if (select.getConfig && select.getConfig().debug)
593
+ console.log(`[${origin}] Opening dropdown on Enter`);
594
+ select.openDropdown();
595
+
596
+ // Focus the first option after opening
597
+ setTimeout(() => {
598
+ (select as any)._focusNextOption();
599
+ }, 50);
600
+ }
601
+ break;
602
+
603
+ case 'Tab':
604
+ // Only handle tab if dropdown is open
605
+ if (isDropdownOpen) {
606
+ if (select.getConfig && select.getConfig().debug)
607
+ console.log(`[${origin}] Closing dropdown on Tab`);
608
+ select.closeDropdown();
609
+ if (callbacks?.onClose) {
610
+ callbacks.onClose();
611
+ }
612
+ // Don't prevent default tab behavior - let it move focus naturally
613
+ }
614
+ break;
615
+
616
+ case 'Escape':
617
+ // Only handle escape if dropdown is open
618
+ if (isDropdownOpen) {
619
+ if (select.getConfig && select.getConfig().debug)
620
+ console.log(`[${origin}] Closing dropdown on Escape`);
621
+ select.closeDropdown();
622
+ if (callbacks?.onClose) {
623
+ callbacks.onClose();
624
+ }
625
+ event.preventDefault(); // Prevent other escape handlers
626
+ }
627
+ break;
628
+
629
+ case ' ': // Space key
630
+ // If dropdown is closed, space should open it (but not if in combobox mode)
631
+ if (!isDropdownOpen && !(select.getConfig().mode === 'combobox')) {
632
+ if (select.getConfig && select.getConfig().debug)
633
+ console.log(`[${origin}] Opening dropdown on Space`);
634
+ select.openDropdown();
635
+
636
+ // Focus the first option after opening
637
+ setTimeout(() => {
638
+ (select as any)._focusNextOption();
639
+ }, 50);
640
+
641
+ event.preventDefault();
642
+ }
643
+ break;
644
+ }
645
+ } catch (error) {
646
+ console.error('Error in keyboard navigation handler:', error);
647
+ }
648
+ }
649
+
650
+ /**
651
+ * Centralized event listener management
652
+ */
653
+ export class EventManager {
654
+ private _boundHandlers: Map<
655
+ string,
656
+ Map<EventListenerOrEventListenerObject, EventListenerOrEventListenerObject>
657
+ > = new Map();
658
+
659
+ /**
660
+ * Add an event listener with a bound context
661
+ */
662
+ public addListener(
663
+ element: HTMLElement,
664
+ event: string,
665
+ handler: EventListenerOrEventListenerObject,
666
+ context?: any,
667
+ ): void {
668
+ if (!element) return;
669
+
670
+ // Create a bound version of the handler if context provided
671
+ const boundHandler: EventListenerOrEventListenerObject =
672
+ context && typeof handler === 'function'
673
+ ? handler.bind(context)
674
+ : handler;
675
+
676
+ // Store the relationship between original and bound handler
677
+ if (!this._boundHandlers.has(event)) {
678
+ this._boundHandlers.set(event, new Map());
679
+ }
680
+
681
+ const eventMap = this._boundHandlers.get(event)!;
682
+ eventMap.set(handler, boundHandler);
683
+
684
+ // Add the event listener
685
+ element.addEventListener(event, boundHandler);
686
+ }
687
+
688
+ /**
689
+ * Remove an event listener
690
+ */
691
+ public removeListener(
692
+ element: HTMLElement,
693
+ event: string,
694
+ handler: EventListenerOrEventListenerObject,
695
+ ): void {
696
+ if (!element) return;
697
+
698
+ const eventMap = this._boundHandlers.get(event);
699
+ if (!eventMap) return;
700
+
701
+ // Get the bound version of the handler
702
+ const boundHandler = eventMap.get(handler);
703
+ if (!boundHandler) return;
704
+
705
+ // Remove the event listener
706
+ element.removeEventListener(event, boundHandler);
707
+
708
+ // Clean up the map
709
+ eventMap.delete(handler);
710
+ if (eventMap.size === 0) {
711
+ this._boundHandlers.delete(event);
712
+ }
713
+ }
714
+
715
+ /**
716
+ * Remove all event listeners
717
+ */
718
+ public removeAllListeners(element: HTMLElement): void {
719
+ if (!element) return;
720
+
721
+ // Go through each event type
722
+ this._boundHandlers.forEach((eventMap, event) => {
723
+ // For each event type, go through each handler
724
+ eventMap.forEach((boundHandler, originalHandler) => {
725
+ element.removeEventListener(event, boundHandler);
726
+ });
727
+ });
728
+
729
+ // Clear the maps
730
+ this._boundHandlers.clear();
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Debounce function to limit how often a function can be called
736
+ */
737
+ export function debounce(
738
+ func: (...args: any[]) => void,
739
+ delay: number,
740
+ ): (...args: any[]) => void {
741
+ let timeout: ReturnType<typeof setTimeout>;
742
+
743
+ return function (...args: any[]) {
744
+ clearTimeout(timeout);
745
+ timeout = setTimeout(() => func(...args), delay);
746
+ };
747
+ }