@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,1397 @@
1
+ /**
2
+ * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
+ * Copyright 2025 by Keenthemes Inc
4
+ * @version: 1.0.0
5
+ */
6
+ import { KTDatepickerStateManager } from './config';
7
+ import {
8
+ calendarGridTemplate,
9
+ dayTemplate,
10
+ datepickerContainerTemplate,
11
+ monthYearSelectTemplate,
12
+ monthSelectionTemplate,
13
+ yearSelectionTemplate,
14
+ } from './templates';
15
+ import {
16
+ generateCalendarMonth,
17
+ getLocaleConfig,
18
+ isSameDay,
19
+ isDateDisabled,
20
+ isDateBetween,
21
+ isWeekend,
22
+ formatDate,
23
+ parseDate,
24
+ isValidDate,
25
+ getDaysInMonth,
26
+ isDateEqual,
27
+ isDateInRange,
28
+ } from './utils';
29
+ import {
30
+ CalendarDayCellInterface,
31
+ DateRangeInterface,
32
+ KTDatepickerEvents,
33
+ } from './types';
34
+ import { KTDatepickerDropdown } from './dropdown';
35
+ import { KTDatepickerEventManager, KTDatepickerEventName } from './events';
36
+
37
+ /**
38
+ * Calendar component for the KTDatepicker
39
+ * Handles rendering and interactions with the calendar
40
+ */
41
+ export class KTDatepickerCalendar {
42
+ private _element: HTMLElement;
43
+ private _stateManager: KTDatepickerStateManager;
44
+ private _eventManager: KTDatepickerEventManager;
45
+ private _calendarContainer: HTMLElement | null = null;
46
+ private _dropdownElement: HTMLElement | null = null;
47
+ private _dropdownManager: KTDatepickerDropdown | null = null;
48
+ private _isVisible: boolean = false;
49
+ private _currentViewMonth: number;
50
+ private _currentViewYear: number;
51
+
52
+ /**
53
+ * Constructor for the KTDatepickerCalendar class
54
+ *
55
+ * @param element - The datepicker element
56
+ * @param stateManager - State manager for the datepicker
57
+ */
58
+ constructor(element: HTMLElement, stateManager: KTDatepickerStateManager) {
59
+ this._element = element;
60
+ this._stateManager = stateManager;
61
+ this._eventManager = stateManager.getEventManager();
62
+
63
+ // Get current date/time
64
+ const now = new Date();
65
+ this._currentViewMonth = now.getMonth();
66
+ this._currentViewYear = now.getFullYear();
67
+
68
+ this._initializeCalendar();
69
+ this._setupEventListeners();
70
+ }
71
+
72
+ /**
73
+ * Initialize the calendar
74
+ */
75
+ private _initializeCalendar(): void {
76
+ const config = this._stateManager.getConfig();
77
+ const locale = getLocaleConfig(config);
78
+
79
+ // Create calendar container
80
+ this._dropdownElement = document.createElement('div');
81
+ this._dropdownElement.className = 'kt-datepicker-dropdown';
82
+ this._dropdownElement.setAttribute('role', 'dialog');
83
+ this._dropdownElement.setAttribute('aria-modal', 'true');
84
+ this._dropdownElement.setAttribute('aria-label', 'Calendar');
85
+
86
+ // Hidden by default
87
+ this._dropdownElement.classList.add('hidden');
88
+ this._dropdownElement.setAttribute('aria-hidden', 'true');
89
+
90
+ // Create header for navigation
91
+ const headerElement = document.createElement('div');
92
+ headerElement.className = 'kt-datepicker-calendar-header';
93
+
94
+ // Left navigation button
95
+ const leftNavButton = document.createElement('button');
96
+ leftNavButton.type = 'button';
97
+ leftNavButton.className = 'kt-datepicker-calendar-left-nav-btn';
98
+ leftNavButton.setAttribute('aria-label', 'Previous month');
99
+ leftNavButton.innerHTML =
100
+ '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" /></svg>';
101
+ leftNavButton.addEventListener('click', () => this._navigateMonth(-1));
102
+
103
+ // Month and Year selector (center)
104
+ const headerCenter = document.createElement('div');
105
+ headerCenter.className = 'kt-datepicker-datepicker-header-middle';
106
+
107
+ // Right navigation button
108
+ const rightNavButton = document.createElement('button');
109
+ rightNavButton.type = 'button';
110
+ rightNavButton.className = 'kt-dropdown-calendar-right-nav-btn';
111
+ rightNavButton.setAttribute('aria-label', 'Next month');
112
+ rightNavButton.innerHTML =
113
+ '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" /></svg>';
114
+ rightNavButton.addEventListener('click', () => this._navigateMonth(1));
115
+
116
+ // Assemble header
117
+ headerElement.appendChild(leftNavButton);
118
+ headerElement.appendChild(headerCenter);
119
+ headerElement.appendChild(rightNavButton);
120
+ this._dropdownElement.appendChild(headerElement);
121
+
122
+ // Create calendar content container
123
+ this._calendarContainer = document.createElement('div');
124
+ this._calendarContainer.className = 'kt-datepicker-calendar-container';
125
+ this._dropdownElement.appendChild(this._calendarContainer);
126
+
127
+ // Add calendar footer with action buttons
128
+ const footerElement = document.createElement('div');
129
+ footerElement.className = 'kt-datepicker-calendar-footer';
130
+
131
+ // Today button
132
+ const todayButton = document.createElement('button');
133
+ todayButton.type = 'button';
134
+ todayButton.className = 'kt-datepicker-calendar-today-btn';
135
+ todayButton.textContent = 'Today';
136
+ todayButton.addEventListener('click', () => this._goToToday());
137
+
138
+ // Clear button
139
+ const clearButton = document.createElement('button');
140
+ clearButton.type = 'button';
141
+ clearButton.className = 'kt-datepicker-calendar-clear-btn';
142
+ clearButton.textContent = 'Clear';
143
+ clearButton.addEventListener('click', () => this._clearSelection());
144
+
145
+ // Apply button
146
+ const applyButton = document.createElement('button');
147
+ applyButton.type = 'button';
148
+ applyButton.className = 'kt-datepicker-calendar-clear-btn';
149
+ applyButton.textContent = 'Apply';
150
+ applyButton.addEventListener('click', () => this._applySelection());
151
+
152
+ // Assemble footer
153
+ footerElement.appendChild(todayButton);
154
+
155
+ const rightFooter = document.createElement('div');
156
+ rightFooter.className = 'kt-datepicker-calendar-footer-right';
157
+ rightFooter.appendChild(clearButton);
158
+ rightFooter.appendChild(applyButton);
159
+ footerElement.appendChild(rightFooter);
160
+
161
+ this._dropdownElement.appendChild(footerElement);
162
+
163
+ // Add to document
164
+ this._element.appendChild(this._dropdownElement);
165
+
166
+ // Initialize dropdown manager
167
+ this._initDropdownManager();
168
+
169
+ // Initialize calendar view
170
+ this._renderCalendarView();
171
+ }
172
+
173
+ /**
174
+ * Initialize the dropdown manager
175
+ */
176
+ private _initDropdownManager(): void {
177
+ const config = this._stateManager.getConfig();
178
+
179
+ // Use the display element rather than the input element
180
+ const displayElement = this._element.querySelector(
181
+ '[data-kt-datepicker-display]',
182
+ ) as HTMLElement;
183
+ const inputElement = this._element.querySelector(
184
+ '[data-kt-datepicker-input]',
185
+ ) as HTMLElement;
186
+ const triggerElement = displayElement || inputElement;
187
+
188
+ if (triggerElement && this._dropdownElement) {
189
+ this._dropdownManager = new KTDatepickerDropdown(
190
+ this._element,
191
+ triggerElement,
192
+ this._dropdownElement,
193
+ config,
194
+ );
195
+
196
+ // Add keyboard event listener to the trigger element
197
+ triggerElement.addEventListener('keydown', (e) => {
198
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
199
+ e.preventDefault();
200
+ if (!this._isVisible) {
201
+ // Open the dropdown
202
+ this._stateManager.setOpen(true);
203
+ }
204
+ }
205
+ });
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Set up event listeners for calendar interactions
211
+ */
212
+ private _setupEventListeners(): void {
213
+ if (!this._dropdownElement) return;
214
+
215
+ // Get elements
216
+ const prevMonthBtn = this._dropdownElement.querySelector(
217
+ 'button[aria-label="Previous Month"]',
218
+ );
219
+ const nextMonthBtn = this._dropdownElement.querySelector(
220
+ 'button[aria-label="Next Month"]',
221
+ );
222
+
223
+ // Find buttons by text content instead of using jQuery-style selectors
224
+ const buttons = this._dropdownElement.querySelectorAll('button');
225
+ let todayBtn: HTMLButtonElement | null = null;
226
+ let clearBtn: HTMLButtonElement | null = null;
227
+ let applyBtn: HTMLButtonElement | null = null;
228
+
229
+ buttons.forEach((btn) => {
230
+ const btnText = btn.textContent?.trim();
231
+ if (btnText === 'Today') todayBtn = btn as HTMLButtonElement;
232
+ else if (btnText === 'Clear') clearBtn = btn as HTMLButtonElement;
233
+ else if (btnText === 'Apply') applyBtn = btn as HTMLButtonElement;
234
+ });
235
+
236
+ const monthYearText = this._dropdownElement.querySelector(
237
+ '.kt-datepicker-calendar-monthyear-text',
238
+ );
239
+
240
+ // Month navigation
241
+ if (prevMonthBtn) {
242
+ prevMonthBtn.addEventListener('click', () => this._navigateMonth(-1));
243
+ }
244
+
245
+ if (nextMonthBtn) {
246
+ nextMonthBtn.addEventListener('click', () => this._navigateMonth(1));
247
+ }
248
+
249
+ // Month/year view toggle
250
+ if (monthYearText) {
251
+ monthYearText.addEventListener('click', () =>
252
+ this._toggleMonthYearView(),
253
+ );
254
+ }
255
+
256
+ // Today, Clear, Apply buttons
257
+ if (todayBtn) {
258
+ todayBtn.addEventListener('click', () => this._goToToday());
259
+ }
260
+
261
+ if (clearBtn) {
262
+ clearBtn.addEventListener('click', () => this._clearSelection());
263
+ }
264
+
265
+ if (applyBtn) {
266
+ applyBtn.addEventListener('click', () => this._applySelection());
267
+ }
268
+
269
+ // Handle day selection through event delegation
270
+ if (this._calendarContainer) {
271
+ this._calendarContainer.addEventListener('click', (e) => {
272
+ const target = e.target as HTMLElement;
273
+ const dayButton = target.closest('button[data-date]') as HTMLElement;
274
+
275
+ if (dayButton && !dayButton.hasAttribute('disabled')) {
276
+ // Get the date ID directly from the clicked button (YYYY-MM-DD format)
277
+ const dateIdAttr = dayButton.getAttribute('data-date-id');
278
+
279
+ if (dateIdAttr) {
280
+ // Parse the ISO date string to get exact year, month, day
281
+ const [year, month, day] = dateIdAttr
282
+ .split('-')
283
+ .map((part) => parseInt(part, 10));
284
+
285
+ // Create the date object from the parsed components
286
+ const clickedDate = new Date(year, month - 1, day); // Month is 0-indexed in JS
287
+ clickedDate.setHours(0, 0, 0, 0); // Set to midnight
288
+
289
+ // Handle this date directly instead of using day number
290
+ this._handleDateSelection(clickedDate, dayButton);
291
+ } else {
292
+ // Fallback to old method using day number if date-id is not present
293
+ const dateAttr = dayButton.getAttribute('data-date');
294
+ if (dateAttr) {
295
+ const day = parseInt(dateAttr, 10);
296
+ this._handleDaySelection(day);
297
+ }
298
+ }
299
+ }
300
+ });
301
+
302
+ // Add hover effect for range selection
303
+ this._calendarContainer.addEventListener('mouseover', (e) => {
304
+ const state = this._stateManager.getState();
305
+ const config = this._stateManager.getConfig();
306
+
307
+ // Only apply hover effect in range mode when start date is selected but end date is not
308
+ if (
309
+ !config.range ||
310
+ !state.selectedDateRange ||
311
+ !state.selectedDateRange.startDate ||
312
+ state.selectedDateRange.endDate
313
+ ) {
314
+ return;
315
+ }
316
+
317
+ const target = e.target as HTMLElement;
318
+ const dayButton = target.closest('button[data-date]');
319
+
320
+ if (dayButton && !dayButton.hasAttribute('disabled')) {
321
+ // Clear any existing hover classes
322
+ this._clearRangeHoverClasses();
323
+
324
+ // Get the proper date from the data-date-id attribute
325
+ const dateIdAttr = dayButton.getAttribute('data-date-id');
326
+
327
+ if (dateIdAttr) {
328
+ // Parse the ISO date string (YYYY-MM-DD)
329
+ const [year, month, day] = dateIdAttr
330
+ .split('-')
331
+ .map((part) => parseInt(part, 10));
332
+ const hoverDate = new Date(year, month - 1, day); // Month is 0-indexed in JS Date
333
+
334
+ // Apply hover effect between start date and current hover date
335
+ this._applyRangeHoverEffect(
336
+ state.selectedDateRange.startDate,
337
+ hoverDate,
338
+ );
339
+ } else {
340
+ // Fallback to old method if data-date-id is not present
341
+ const dateAttr = dayButton.getAttribute('data-date');
342
+ if (dateAttr) {
343
+ const day = parseInt(dateAttr, 10);
344
+ const hoverDate = new Date(state.currentDate);
345
+ hoverDate.setDate(day);
346
+
347
+ // Apply hover effect between start date and current hover date
348
+ this._applyRangeHoverEffect(
349
+ state.selectedDateRange.startDate,
350
+ hoverDate,
351
+ );
352
+ }
353
+ }
354
+ }
355
+ });
356
+
357
+ // Clear hover effect when mouse leaves the calendar
358
+ this._calendarContainer.addEventListener('mouseleave', () => {
359
+ this._clearRangeHoverClasses();
360
+ });
361
+ }
362
+
363
+ // Listen for state changes
364
+ this._eventManager.addEventListener(
365
+ KTDatepickerEventName.STATE_CHANGE,
366
+ (e: CustomEvent) => {
367
+ const detail = e.detail?.payload;
368
+ const config = this._stateManager.getConfig();
369
+
370
+ // For range selection, check if we need to keep the dropdown open
371
+ if (config.range && detail && detail.selectedDateRange) {
372
+ const { startDate, endDate } = detail.selectedDateRange;
373
+
374
+ // If start date is set but no end date, keep dropdown open
375
+ if (startDate && !endDate) {
376
+ this._stateManager.getState().isRangeSelectionInProgress = true;
377
+ } else if (startDate && endDate) {
378
+ this._stateManager.getState().isRangeSelectionInProgress = false;
379
+ }
380
+ }
381
+
382
+ // Update calendar view
383
+ this._updateCalendarView();
384
+ },
385
+ );
386
+
387
+ // Listen for other state changes
388
+ this._eventManager.addEventListener(KTDatepickerEventName.VIEW_CHANGE, () =>
389
+ this._updateViewMode(),
390
+ );
391
+ this._eventManager.addEventListener(KTDatepickerEventName.OPEN, () =>
392
+ this.show(),
393
+ );
394
+ this._eventManager.addEventListener(KTDatepickerEventName.CLOSE, () =>
395
+ this.hide(),
396
+ );
397
+ this._eventManager.addEventListener(KTDatepickerEventName.UPDATE, () =>
398
+ this._updateCalendarView(),
399
+ );
400
+
401
+ // Time inputs
402
+ const timeContainer = this._dropdownElement.querySelector(
403
+ '.kt-datepicker-calendar-time-container',
404
+ );
405
+ if (timeContainer) {
406
+ const hourInput = timeContainer.querySelector(
407
+ 'input[aria-label="Hour"]',
408
+ ) as HTMLInputElement;
409
+ const minuteInput = timeContainer.querySelector(
410
+ 'input[aria-label="Minute"]',
411
+ ) as HTMLInputElement;
412
+ const secondInput = timeContainer.querySelector(
413
+ 'input[aria-label="Second"]',
414
+ ) as HTMLInputElement;
415
+ const amButton = timeContainer.querySelector(
416
+ 'button[aria-label="AM"]',
417
+ ) as HTMLButtonElement;
418
+ const pmButton = timeContainer.querySelector(
419
+ 'button[aria-label="PM"]',
420
+ ) as HTMLButtonElement;
421
+
422
+ // Update AM/PM button texts
423
+ const config = this._stateManager.getConfig();
424
+ if (amButton) amButton.textContent = config.am;
425
+ if (pmButton) pmButton.textContent = config.pm;
426
+
427
+ // Time input listeners
428
+ if (hourInput) {
429
+ hourInput.addEventListener('change', () => this._handleTimeChange());
430
+ }
431
+
432
+ if (minuteInput) {
433
+ minuteInput.addEventListener('change', () => this._handleTimeChange());
434
+ }
435
+
436
+ if (secondInput) {
437
+ secondInput.addEventListener('change', () => this._handleTimeChange());
438
+ }
439
+
440
+ // AM/PM selection
441
+ if (amButton) {
442
+ amButton.addEventListener('click', () => this._setAmPm('AM'));
443
+ }
444
+
445
+ if (pmButton) {
446
+ pmButton.addEventListener('click', () => this._setAmPm('PM'));
447
+ }
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Render the calendar view based on current state
453
+ */
454
+ private _renderCalendarView(): void {
455
+ if (!this._calendarContainer) return;
456
+
457
+ const state = this._stateManager.getState();
458
+ const config = this._stateManager.getConfig();
459
+ const locale = getLocaleConfig(config);
460
+
461
+ // Clear existing content
462
+ this._calendarContainer.innerHTML = '';
463
+
464
+ // Set up proper container classes for multiple months view
465
+ if (config.visibleMonths > 1) {
466
+ // For multiple months, use a flex container with no wrapping
467
+ this._calendarContainer.className = 'kt-datepicker-calendar-container-mt';
468
+ } else {
469
+ this._calendarContainer.className = 'kt-datepicker-calendar-container';
470
+ }
471
+
472
+ // Render based on view mode
473
+ switch (state.viewMode) {
474
+ case 'days':
475
+ // For each visible month, create a calendar
476
+ for (let i = 0; i < config.visibleMonths; i++) {
477
+ // Calculate the month to display
478
+ const tempDate = new Date(state.currentDate);
479
+ tempDate.setMonth(state.currentDate.getMonth() + i);
480
+
481
+ const month = tempDate.getMonth();
482
+ const year = tempDate.getFullYear();
483
+
484
+ // Create month container
485
+ const monthContainer = document.createElement('div');
486
+
487
+ // Set appropriate class based on number of months
488
+ if (config.visibleMonths > 1) {
489
+ // For multiple months, use fixed width and properly spaced
490
+ monthContainer.className = 'kt-datepicker-calendar-month-mt';
491
+ monthContainer.setAttribute('data-month-id', `${month}-${year}`);
492
+ } else {
493
+ monthContainer.className = 'kt-datepicker-calendar-month';
494
+ }
495
+
496
+ // Add month header
497
+ const monthHeader = document.createElement('div');
498
+ monthHeader.className = 'kt-datepicker-calendar-month-header';
499
+ monthHeader.textContent = `${locale.monthNames[month]} ${year}`;
500
+ monthContainer.appendChild(monthHeader);
501
+
502
+ // Generate calendar grid
503
+ monthContainer.innerHTML += calendarGridTemplate(
504
+ locale,
505
+ config.weekDays,
506
+ );
507
+
508
+ // Get days for the month
509
+ const calendarMatrix = generateCalendarMonth(year, month, config);
510
+
511
+ // Render days
512
+ const daysBody = monthContainer.querySelector('tbody');
513
+ if (daysBody) {
514
+ daysBody.innerHTML = this._renderDays(calendarMatrix, month, year);
515
+ }
516
+
517
+ // Add to container
518
+ this._calendarContainer.appendChild(monthContainer);
519
+ }
520
+
521
+ // Update the month/year display in header
522
+ this._updateMonthYearDisplay();
523
+ break;
524
+
525
+ case 'months':
526
+ // Render month selection view with current month
527
+ const currentMonth = state.currentDate.getMonth();
528
+ this._calendarContainer.innerHTML = monthSelectionTemplate(
529
+ locale,
530
+ currentMonth,
531
+ );
532
+
533
+ // Add click events to month buttons
534
+ const monthButtons =
535
+ this._calendarContainer.querySelectorAll('button[data-month]');
536
+ monthButtons.forEach((btn) => {
537
+ btn.addEventListener('click', (e) => {
538
+ const target = e.target as HTMLElement;
539
+ const monthIdx = target.getAttribute('data-month');
540
+ if (monthIdx) {
541
+ this._selectMonth(parseInt(monthIdx, 10));
542
+ }
543
+ });
544
+ });
545
+ break;
546
+
547
+ case 'years':
548
+ // Get current year and calculate year range
549
+ const currentYear = state.currentDate.getFullYear();
550
+ const startYear = currentYear - Math.floor(config.visibleYears / 2);
551
+ const endYear = startYear + config.visibleYears - 1;
552
+
553
+ // Render year selection view
554
+ this._calendarContainer.innerHTML = yearSelectionTemplate(
555
+ startYear,
556
+ endYear,
557
+ currentYear,
558
+ );
559
+
560
+ // Add click events to year buttons
561
+ const yearButtons =
562
+ this._calendarContainer.querySelectorAll('button[data-year]');
563
+ yearButtons.forEach((btn) => {
564
+ btn.addEventListener('click', (e) => {
565
+ const target = e.target as HTMLElement;
566
+ const year = target.getAttribute('data-year');
567
+ if (year) {
568
+ this._selectYear(parseInt(year, 10));
569
+ }
570
+ });
571
+ });
572
+
573
+ // Add navigation for year ranges
574
+ const prevYearsBtn = this._calendarContainer.querySelector(
575
+ 'button[data-year-nav="prev"]',
576
+ );
577
+ if (prevYearsBtn) {
578
+ prevYearsBtn.addEventListener('click', () => {
579
+ const newYear = startYear - config.visibleYears;
580
+ const newDate = new Date(state.currentDate);
581
+ newDate.setFullYear(newYear);
582
+ this._stateManager.setCurrentDate(newDate);
583
+ this._renderCalendarView();
584
+ });
585
+ }
586
+
587
+ const nextYearsBtn = this._calendarContainer.querySelector(
588
+ 'button[data-year-nav="next"]',
589
+ );
590
+ if (nextYearsBtn) {
591
+ nextYearsBtn.addEventListener('click', () => {
592
+ const newYear = endYear + 1;
593
+ const newDate = new Date(state.currentDate);
594
+ newDate.setFullYear(newYear);
595
+ this._stateManager.setCurrentDate(newDate);
596
+ this._renderCalendarView();
597
+ });
598
+ }
599
+ break;
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Render days for a calendar month
605
+ *
606
+ * @param calendarMatrix - Matrix of dates for the month
607
+ * @param currentMonth - Current month
608
+ * @param currentYear - Current year
609
+ * @returns HTML string for the days
610
+ */
611
+ private _renderDays(
612
+ calendarMatrix: Date[][],
613
+ currentMonth: number,
614
+ currentYear: number,
615
+ ): string {
616
+ const state = this._stateManager.getState();
617
+ const config = this._stateManager.getConfig();
618
+ const today = new Date();
619
+ today.setHours(0, 0, 0, 0);
620
+
621
+ let html = '';
622
+
623
+ // Loop through each week
624
+ for (const week of calendarMatrix) {
625
+ html += '<tr>';
626
+
627
+ // Loop through each day in the week
628
+ for (const date of week) {
629
+ // Determine cell properties
630
+ const isCurrentMonth = date.getMonth() === currentMonth;
631
+ const isToday = isSameDay(date, today);
632
+ let isSelected = false;
633
+ let isRangeStart = false;
634
+ let isRangeEnd = false;
635
+ let isInRange = false;
636
+
637
+ // Check if date is selected
638
+ if (state.selectedDate && isSameDay(date, state.selectedDate)) {
639
+ isSelected = true;
640
+ }
641
+
642
+ // Check if date is in range for range selection
643
+ if (config.range && state.selectedDateRange) {
644
+ const { startDate, endDate } = state.selectedDateRange;
645
+
646
+ if (startDate && isSameDay(date, startDate)) {
647
+ isRangeStart = true;
648
+ isSelected = true;
649
+ }
650
+
651
+ if (endDate && isSameDay(date, endDate)) {
652
+ isRangeEnd = true;
653
+ isSelected = true;
654
+ }
655
+
656
+ if (startDate && endDate && isDateBetween(date, startDate, endDate)) {
657
+ isInRange = true;
658
+ }
659
+ }
660
+
661
+ // Check if date is in multi-date selection
662
+ if (config.multiDateSelection && state.selectedDates.length > 0) {
663
+ isSelected = state.selectedDates.some((d) => isSameDay(date, d));
664
+ }
665
+
666
+ // Check if date is disabled
667
+ const isDisabled = isDateDisabled(date, config);
668
+
669
+ // Check if weekend
670
+ const isWeekendDay = isWeekend(date);
671
+
672
+ // Get the actual month and year of this date (may differ from currentMonth/currentYear for adjacent months)
673
+ const actualMonth = date.getMonth();
674
+ const actualYear = date.getFullYear();
675
+
676
+ // Generate day cell
677
+ html += dayTemplate(
678
+ date.getDate(),
679
+ actualMonth,
680
+ actualYear,
681
+ isCurrentMonth,
682
+ isToday,
683
+ isSelected,
684
+ isDisabled,
685
+ isRangeStart,
686
+ isRangeEnd,
687
+ isInRange,
688
+ isWeekendDay,
689
+ );
690
+ }
691
+
692
+ html += '</tr>';
693
+ }
694
+
695
+ return html;
696
+ }
697
+
698
+ /**
699
+ * Update the month and year display in the header
700
+ */
701
+ private _updateMonthYearDisplay(): void {
702
+ if (!this._dropdownElement) return;
703
+
704
+ const state = this._stateManager.getState();
705
+ const config = this._stateManager.getConfig();
706
+ const locale = getLocaleConfig(config);
707
+
708
+ // Find the calendar header
709
+ const calendarHeader = this._dropdownElement.querySelector(
710
+ '.kt-datepicker-calendar-header',
711
+ );
712
+ if (!calendarHeader) return;
713
+
714
+ const currentMonth = state.currentDate.getMonth();
715
+ const currentYear = state.currentDate.getFullYear();
716
+
717
+ // Update the header with month/year selectors
718
+ calendarHeader.innerHTML = monthYearSelectTemplate(
719
+ locale,
720
+ currentMonth,
721
+ currentYear,
722
+ );
723
+
724
+ // Add event listeners to the month and year selectors
725
+ const monthSelector = calendarHeader.querySelector(
726
+ '.kt-datepicker-calendar-month-selector',
727
+ );
728
+ const yearSelector = calendarHeader.querySelector(
729
+ '.kt-datepicker-calendar-year-selector',
730
+ );
731
+
732
+ if (monthSelector) {
733
+ monthSelector.addEventListener('click', () => {
734
+ // Switch to months view
735
+ this._stateManager.setViewMode('months');
736
+ this._renderCalendarView();
737
+ });
738
+ }
739
+
740
+ if (yearSelector) {
741
+ yearSelector.addEventListener('click', () => {
742
+ // Switch to years view
743
+ this._stateManager.setViewMode('years');
744
+ this._renderCalendarView();
745
+ });
746
+ }
747
+ }
748
+
749
+ /**
750
+ * Navigate to a different month
751
+ *
752
+ * @param offset - Number of months to offset by
753
+ */
754
+ private _navigateMonth(offset: number): void {
755
+ const state = this._stateManager.getState();
756
+ const newDate = new Date(state.currentDate);
757
+ newDate.setMonth(newDate.getMonth() + offset);
758
+
759
+ this._stateManager.setCurrentDate(newDate);
760
+ this._renderCalendarView();
761
+ }
762
+
763
+ /**
764
+ * Handle direct date selection (new method that takes the actual date object)
765
+ *
766
+ * @param selectedDate - The exact date that was selected
767
+ * @param clickedButton - The button element that was clicked
768
+ */
769
+ private _handleDateSelection(
770
+ selectedDate: Date,
771
+ clickedButton: HTMLElement,
772
+ ): void {
773
+ const state = this._stateManager.getState();
774
+ const config = this._stateManager.getConfig();
775
+
776
+ // Check if the date is disabled (outside min/max range or explicitly disabled)
777
+ if (isDateDisabled(selectedDate, config)) {
778
+ console.log(
779
+ 'Date is disabled, ignoring selection:',
780
+ selectedDate.toISOString(),
781
+ );
782
+ return;
783
+ }
784
+
785
+ // Create a new date object set to noon of the selected date in local timezone
786
+ // This prevents timezone shifts causing the wrong date to be selected
787
+ const localSelectedDate = new Date(selectedDate);
788
+ localSelectedDate.setHours(12, 0, 0, 0);
789
+
790
+ // Set time if enabled
791
+ if (config.enableTime && state.selectedTime) {
792
+ localSelectedDate.setHours(
793
+ state.selectedTime.hours,
794
+ state.selectedTime.minutes,
795
+ state.selectedTime.seconds,
796
+ 0,
797
+ );
798
+ }
799
+
800
+ // Get the current range state before updating
801
+ const currentRange = state.selectedDateRange;
802
+ const isStartingNewRange =
803
+ !currentRange ||
804
+ !currentRange.startDate ||
805
+ (currentRange.startDate && currentRange.endDate);
806
+
807
+ // Determine if we're in a month different from the currently displayed one
808
+ const selectedMonth = localSelectedDate.getMonth();
809
+ const currentViewMonth = state.currentDate.getMonth();
810
+ const isInDifferentMonth = selectedMonth !== currentViewMonth;
811
+
812
+ console.log(
813
+ 'Selected date:',
814
+ localSelectedDate.toISOString(),
815
+ 'Month:',
816
+ selectedMonth,
817
+ 'Current view month:',
818
+ currentViewMonth,
819
+ 'Day of month:',
820
+ localSelectedDate.getDate(),
821
+ );
822
+
823
+ // Call the state manager's setSelectedDate method
824
+ this._stateManager.setSelectedDate(localSelectedDate);
825
+
826
+ // After setting the date, get the updated range state
827
+ const updatedRange = state.selectedDateRange;
828
+
829
+ // If we're in range mode, handle specific range selection behavior
830
+ if (config.range) {
831
+ if (isStartingNewRange) {
832
+ console.log(
833
+ 'Starting new range selection with date:',
834
+ localSelectedDate.toISOString(),
835
+ );
836
+
837
+ // If starting a range with a date in a different month, update the view
838
+ if (isInDifferentMonth) {
839
+ this._stateManager.setCurrentDate(localSelectedDate);
840
+ }
841
+
842
+ // Explicitly clear any hover effects when starting a new range
843
+ this._clearRangeHoverClasses();
844
+ } else {
845
+ // This is the second click to complete a range
846
+ console.log(
847
+ 'Completing range selection with date:',
848
+ localSelectedDate.toISOString(),
849
+ );
850
+
851
+ // If the selected range spans different months and we have multiple visible months
852
+ if (
853
+ updatedRange &&
854
+ updatedRange.startDate &&
855
+ updatedRange.endDate &&
856
+ config.visibleMonths > 1
857
+ ) {
858
+ // Determine range start and end months
859
+ const startMonth = updatedRange.startDate.getMonth();
860
+ const endMonth = updatedRange.endDate.getMonth();
861
+
862
+ // If range spans multiple months, update view to show the earlier month
863
+ if (startMonth !== endMonth) {
864
+ // Show the earlier month as the first visible month
865
+ const earlierDate =
866
+ updatedRange.startDate < updatedRange.endDate
867
+ ? updatedRange.startDate
868
+ : updatedRange.endDate;
869
+ this._stateManager.setCurrentDate(earlierDate);
870
+ }
871
+ }
872
+ }
873
+
874
+ // Close dropdown only if range selection is complete
875
+ if (updatedRange && updatedRange.startDate && updatedRange.endDate) {
876
+ this._stateManager.setOpen(false);
877
+ }
878
+ } else {
879
+ // For single date selection, close the dropdown
880
+ this._stateManager.setOpen(false);
881
+ }
882
+
883
+ // Update calendar view to reflect changes
884
+ this._updateCalendarView();
885
+ }
886
+
887
+ /**
888
+ * Handle day selection (legacy method, kept for backward compatibility)
889
+ *
890
+ * @param day - Day number
891
+ */
892
+ private _handleDaySelection(day: number): void {
893
+ const state = this._stateManager.getState();
894
+ const config = this._stateManager.getConfig();
895
+
896
+ // Find the clicked button element using data-date attribute
897
+ const dayButtons = this._calendarContainer?.querySelectorAll(
898
+ `button[data-date="${day}"]`,
899
+ );
900
+ if (!dayButtons || dayButtons.length === 0) return;
901
+
902
+ // First look for the button that matches the clicked target in the current month
903
+ let clickedButton: HTMLElement | null = null;
904
+
905
+ // Find the actual button that was likely clicked (prefer current month days)
906
+ for (let i = 0; i < dayButtons.length; i++) {
907
+ const button = dayButtons[i] as HTMLElement;
908
+ const parentCell = button.closest('td');
909
+
910
+ // Check if the day is in the current month (not faded)
911
+ const isCurrentMonth =
912
+ !button.classList.contains('current') &&
913
+ (!parentCell || !parentCell.classList.contains('current'));
914
+
915
+ if (isCurrentMonth) {
916
+ clickedButton = button;
917
+ break;
918
+ }
919
+ }
920
+
921
+ // If no current month button found, use the first one
922
+ if (!clickedButton && dayButtons.length > 0) {
923
+ clickedButton = dayButtons[0] as HTMLElement;
924
+ }
925
+
926
+ if (!clickedButton) return;
927
+
928
+ // Get the proper date from the data-date-id attribute which contains YYYY-MM-DD
929
+ const dateIdAttr = clickedButton.getAttribute('data-date-id');
930
+ if (!dateIdAttr) return;
931
+
932
+ // Parse the ISO date string
933
+ const [year, month, dayOfMonth] = dateIdAttr
934
+ .split('-')
935
+ .map((part) => parseInt(part, 10));
936
+
937
+ // Create the date object with the proper timezone handling
938
+ // We'll set it to noon in local time to avoid timezone issues
939
+ const selectedDate = new Date(year, month - 1, dayOfMonth, 12, 0, 0, 0); // Month is 0-indexed in JS Date, and setting time to noon
940
+
941
+ // First check if this date is disabled (outside min/max range)
942
+ if (isDateDisabled(selectedDate, config)) {
943
+ console.log(
944
+ 'Date is disabled, ignoring selection:',
945
+ selectedDate.toISOString(),
946
+ );
947
+ return;
948
+ }
949
+
950
+ // Use the new direct date selection method
951
+ this._handleDateSelection(selectedDate, clickedButton);
952
+ }
953
+
954
+ /**
955
+ * Toggle between days, months, and years view
956
+ */
957
+ private _toggleMonthYearView(): void {
958
+ const state = this._stateManager.getState();
959
+ let newMode: 'days' | 'months' | 'years';
960
+
961
+ switch (state.viewMode) {
962
+ case 'days':
963
+ newMode = 'months';
964
+ break;
965
+ case 'months':
966
+ newMode = 'years';
967
+ break;
968
+ case 'years':
969
+ newMode = 'days';
970
+ break;
971
+ default:
972
+ newMode = 'days';
973
+ }
974
+
975
+ this._stateManager.setViewMode(newMode);
976
+ this._renderCalendarView();
977
+ }
978
+
979
+ /**
980
+ * Update view mode based on state change
981
+ */
982
+ private _updateViewMode(): void {
983
+ this._renderCalendarView();
984
+ }
985
+
986
+ /**
987
+ * Go to today's date
988
+ */
989
+ private _goToToday(): void {
990
+ const today = new Date();
991
+ this._stateManager.setCurrentDate(today);
992
+ this._renderCalendarView();
993
+ }
994
+
995
+ /**
996
+ * Clear date selection
997
+ */
998
+ private _clearSelection(): void {
999
+ this._stateManager.setSelectedDate(null);
1000
+ this._updateCalendarView();
1001
+ }
1002
+
1003
+ /**
1004
+ * Apply current selection and close dropdown
1005
+ */
1006
+ private _applySelection(): void {
1007
+ const state = this._stateManager.getState();
1008
+ const config = this._stateManager.getConfig();
1009
+
1010
+ // For range selection, check if range selection is in progress
1011
+ if (config.range && state.isRangeSelectionInProgress) {
1012
+ console.log(
1013
+ 'Apply button clicked, but range selection in progress - keeping dropdown open',
1014
+ );
1015
+ // Don't close when range selection is in progress
1016
+ return;
1017
+ }
1018
+
1019
+ // Close dropdown for other cases
1020
+ this._stateManager.setOpen(false);
1021
+ }
1022
+
1023
+ /**
1024
+ * Handle time input changes
1025
+ */
1026
+ private _handleTimeChange(): void {
1027
+ if (!this._dropdownElement) return;
1028
+
1029
+ const timeContainer = this._dropdownElement.querySelector(
1030
+ '.kt-datepicker-calendar-time-container',
1031
+ );
1032
+ if (!timeContainer) return;
1033
+
1034
+ const hourInput = timeContainer.querySelector(
1035
+ 'input[aria-label="Hour"]',
1036
+ ) as HTMLInputElement;
1037
+ const minuteInput = timeContainer.querySelector(
1038
+ 'input[aria-label="Minute"]',
1039
+ ) as HTMLInputElement;
1040
+ const secondInput = timeContainer.querySelector(
1041
+ 'input[aria-label="Second"]',
1042
+ ) as HTMLInputElement;
1043
+ const amButton = timeContainer.querySelector(
1044
+ 'button[aria-label="AM"]',
1045
+ ) as HTMLButtonElement;
1046
+ const pmButton = timeContainer.querySelector(
1047
+ 'button[aria-label="PM"]',
1048
+ ) as HTMLButtonElement;
1049
+
1050
+ if (!hourInput || !minuteInput || !secondInput) return;
1051
+
1052
+ // Get input values
1053
+ let hours = parseInt(hourInput.value, 10);
1054
+ const minutes = parseInt(minuteInput.value, 10);
1055
+ const seconds = parseInt(secondInput.value, 10);
1056
+
1057
+ // Validate values
1058
+ const isValidHours = !isNaN(hours) && hours >= 0 && hours <= 23;
1059
+ const isValidMinutes = !isNaN(minutes) && minutes >= 0 && minutes <= 59;
1060
+ const isValidSeconds = !isNaN(seconds) && seconds >= 0 && seconds <= 59;
1061
+
1062
+ if (!isValidHours || !isValidMinutes || !isValidSeconds) return;
1063
+
1064
+ // Check if using 12-hour format and adjust for AM/PM
1065
+ const isPM = amButton && amButton.classList.contains('bg-blue-500');
1066
+ if (isPM && hours < 12) {
1067
+ hours += 12;
1068
+ } else if (!isPM && hours === 12) {
1069
+ hours = 0;
1070
+ }
1071
+
1072
+ // Update time in state
1073
+ this._stateManager.setSelectedTime({
1074
+ hours,
1075
+ minutes,
1076
+ seconds,
1077
+ ampm: isPM ? 'PM' : 'AM',
1078
+ });
1079
+
1080
+ // Update selected date with new time if a date is selected
1081
+ const state = this._stateManager.getState();
1082
+ if (state.selectedDate) {
1083
+ const updatedDate = new Date(state.selectedDate);
1084
+ updatedDate.setHours(hours, minutes, seconds, 0);
1085
+ this._stateManager.setSelectedDate(updatedDate);
1086
+ }
1087
+ }
1088
+
1089
+ /**
1090
+ * Set AM/PM selection
1091
+ *
1092
+ * @param period - 'AM' or 'PM'
1093
+ */
1094
+ private _setAmPm(period: 'AM' | 'PM'): void {
1095
+ if (!this._dropdownElement) return;
1096
+
1097
+ const timeContainer = this._dropdownElement.querySelector('.py-3.border-t');
1098
+ if (!timeContainer) return;
1099
+
1100
+ const amButton = timeContainer.querySelector(
1101
+ 'button[aria-label="AM"]',
1102
+ ) as HTMLButtonElement;
1103
+ const pmButton = timeContainer.querySelector(
1104
+ 'button[aria-label="PM"]',
1105
+ ) as HTMLButtonElement;
1106
+
1107
+ if (!amButton || !pmButton) return;
1108
+
1109
+ // Update button states
1110
+ if (period === 'AM') {
1111
+ amButton.classList.add('bg-blue-500', 'text-white', 'border-blue-500');
1112
+ amButton.classList.remove('bg-gray-50', 'hover:bg-gray-100');
1113
+ pmButton.classList.remove('bg-blue-500', 'text-white', 'border-blue-500');
1114
+ pmButton.classList.add('bg-gray-50', 'hover:bg-gray-100');
1115
+ } else {
1116
+ amButton.classList.remove('bg-blue-500', 'text-white', 'border-blue-500');
1117
+ amButton.classList.add('bg-gray-50', 'hover:bg-gray-100');
1118
+ pmButton.classList.add('bg-blue-500', 'text-white', 'border-blue-500');
1119
+ pmButton.classList.remove('bg-gray-50', 'hover:bg-gray-100');
1120
+ }
1121
+
1122
+ // Update time
1123
+ this._handleTimeChange();
1124
+ }
1125
+
1126
+ /**
1127
+ * Select a month
1128
+ *
1129
+ * @param month - Month index (0-11)
1130
+ */
1131
+ private _selectMonth(month: number): void {
1132
+ const state = this._stateManager.getState();
1133
+ const config = this._stateManager.getConfig();
1134
+ const newDate = new Date(state.currentDate);
1135
+ newDate.setMonth(month);
1136
+
1137
+ this._stateManager.setCurrentDate(newDate);
1138
+
1139
+ // Only change view mode if keepViewModeOnSelection is false
1140
+ if (!config.keepViewModeOnSelection) {
1141
+ this._stateManager.setViewMode('days');
1142
+ }
1143
+
1144
+ this._renderCalendarView();
1145
+ }
1146
+
1147
+ /**
1148
+ * Select a year
1149
+ *
1150
+ * @param year - Year value
1151
+ */
1152
+ private _selectYear(year: number): void {
1153
+ const state = this._stateManager.getState();
1154
+ const config = this._stateManager.getConfig();
1155
+ const newDate = new Date(state.currentDate);
1156
+ newDate.setFullYear(year);
1157
+
1158
+ this._stateManager.setCurrentDate(newDate);
1159
+
1160
+ // Only change view mode if keepViewModeOnSelection is false
1161
+ if (!config.keepViewModeOnSelection) {
1162
+ this._stateManager.setViewMode('months');
1163
+ }
1164
+
1165
+ this._renderCalendarView();
1166
+ }
1167
+
1168
+ /**
1169
+ * Update calendar view to reflect state changes
1170
+ */
1171
+ private _updateCalendarView(): void {
1172
+ this._renderCalendarView();
1173
+ this._updateTimeDisplay();
1174
+ }
1175
+
1176
+ /**
1177
+ * Update time inputs to reflect current time selection
1178
+ */
1179
+ private _updateTimeDisplay(): void {
1180
+ if (!this._dropdownElement) return;
1181
+
1182
+ const state = this._stateManager.getState();
1183
+ const config = this._stateManager.getConfig();
1184
+
1185
+ // Skip if time is not enabled
1186
+ if (!config.enableTime) return;
1187
+
1188
+ const timeContainer = this._dropdownElement.querySelector('.py-3.border-t');
1189
+ if (!timeContainer) return;
1190
+
1191
+ const hourInput = timeContainer.querySelector(
1192
+ 'input[aria-label="Hour"]',
1193
+ ) as HTMLInputElement;
1194
+ const minuteInput = timeContainer.querySelector(
1195
+ 'input[aria-label="Minute"]',
1196
+ ) as HTMLInputElement;
1197
+ const secondInput = timeContainer.querySelector(
1198
+ 'input[aria-label="Second"]',
1199
+ ) as HTMLInputElement;
1200
+ const amButton = timeContainer.querySelector(
1201
+ 'button[aria-label="AM"]',
1202
+ ) as HTMLButtonElement;
1203
+ const pmButton = timeContainer.querySelector(
1204
+ 'button[aria-label="PM"]',
1205
+ ) as HTMLButtonElement;
1206
+
1207
+ // Get time from selected date or default to current time
1208
+ let hours = 0;
1209
+ let minutes = 0;
1210
+ let seconds = 0;
1211
+ let isAM = true;
1212
+
1213
+ if (state.selectedTime) {
1214
+ hours = state.selectedTime.hours;
1215
+ minutes = state.selectedTime.minutes;
1216
+ seconds = state.selectedTime.seconds;
1217
+ isAM = state.selectedTime.ampm === 'AM';
1218
+ } else if (state.selectedDate) {
1219
+ hours = state.selectedDate.getHours();
1220
+ minutes = state.selectedDate.getMinutes();
1221
+ seconds = state.selectedDate.getSeconds();
1222
+ isAM = hours < 12;
1223
+ } else {
1224
+ const now = new Date();
1225
+ hours = now.getHours();
1226
+ minutes = now.getMinutes();
1227
+ seconds = now.getSeconds();
1228
+ isAM = hours < 12;
1229
+ }
1230
+
1231
+ // Adjust for 12-hour display if needed
1232
+ let displayHours = hours;
1233
+ if (hourInput && config.timeFormat.includes('h')) {
1234
+ displayHours = hours % 12;
1235
+ if (displayHours === 0) displayHours = 12;
1236
+ }
1237
+
1238
+ // Update input values
1239
+ if (hourInput)
1240
+ hourInput.value =
1241
+ config.forceLeadingZero && displayHours < 10
1242
+ ? `0${displayHours}`
1243
+ : `${displayHours}`;
1244
+ if (minuteInput)
1245
+ minuteInput.value =
1246
+ config.forceLeadingZero && minutes < 10 ? `0${minutes}` : `${minutes}`;
1247
+ if (secondInput)
1248
+ secondInput.value =
1249
+ config.forceLeadingZero && seconds < 10 ? `0${seconds}` : `${seconds}`;
1250
+
1251
+ // Update AM/PM buttons
1252
+ if (amButton && pmButton) {
1253
+ if (isAM) {
1254
+ amButton.classList.add('bg-blue-500', 'text-white', 'border-blue-500');
1255
+ amButton.classList.remove('bg-gray-50', 'hover:bg-gray-100');
1256
+ pmButton.classList.remove(
1257
+ 'bg-blue-500',
1258
+ 'text-white',
1259
+ 'border-blue-500',
1260
+ );
1261
+ pmButton.classList.add('bg-gray-50', 'hover:bg-gray-100');
1262
+ } else {
1263
+ amButton.classList.remove(
1264
+ 'bg-blue-500',
1265
+ 'text-white',
1266
+ 'border-blue-500',
1267
+ );
1268
+ amButton.classList.add('bg-gray-50', 'hover:bg-gray-100');
1269
+ pmButton.classList.add('bg-blue-500', 'text-white', 'border-blue-500');
1270
+ pmButton.classList.remove('bg-gray-50', 'hover:bg-gray-100');
1271
+ }
1272
+ }
1273
+ }
1274
+
1275
+ /**
1276
+ * Show the calendar dropdown
1277
+ */
1278
+ public show(): void {
1279
+ if (!this._dropdownElement || this._isVisible) return;
1280
+
1281
+ // Ensure we're in days view
1282
+ const state = this._stateManager.getState();
1283
+ if (state.viewMode !== 'days') {
1284
+ this._stateManager.setViewMode('days');
1285
+ }
1286
+
1287
+ // Render calendar before showing
1288
+ this._renderCalendarView();
1289
+ this._updateTimeDisplay();
1290
+
1291
+ // Show dropdown using dropdown manager
1292
+ if (this._dropdownManager) {
1293
+ this._dropdownManager.open();
1294
+ this._isVisible = true;
1295
+ }
1296
+ }
1297
+
1298
+ /**
1299
+ * Hide the calendar dropdown
1300
+ */
1301
+ public hide(): void {
1302
+ if (!this._dropdownElement || !this._isVisible) return;
1303
+
1304
+ // Hide dropdown using dropdown manager
1305
+ if (this._dropdownManager) {
1306
+ this._dropdownManager.close();
1307
+ this._isVisible = false;
1308
+ }
1309
+ }
1310
+
1311
+ /**
1312
+ * Update dropdown position
1313
+ */
1314
+ public updatePosition(): void {
1315
+ if (this._dropdownManager) {
1316
+ this._dropdownManager.updatePosition();
1317
+ }
1318
+ }
1319
+
1320
+ /**
1321
+ * Clear range hover classes from all day cells
1322
+ */
1323
+ private _clearRangeHoverClasses(): void {
1324
+ if (!this._calendarContainer) return;
1325
+
1326
+ // Find all day cells with hover classes across all month containers
1327
+ const hoverCells = this._calendarContainer.querySelectorAll(
1328
+ '.bg-blue-50, .text-blue-600, button[data-hover-date="true"]',
1329
+ );
1330
+
1331
+ hoverCells.forEach((cell) => {
1332
+ cell.classList.remove('bg-blue-50', 'text-blue-600');
1333
+ });
1334
+ }
1335
+
1336
+ /**
1337
+ * Apply hover effect to show potential range selection
1338
+ *
1339
+ * @param startDate - Start date of the range
1340
+ * @param hoverDate - Current date being hovered
1341
+ */
1342
+ private _applyRangeHoverEffect(startDate: Date, hoverDate: Date): void {
1343
+ if (!this._calendarContainer) return;
1344
+
1345
+ // Clear any existing hover effects first
1346
+ this._clearRangeHoverClasses();
1347
+
1348
+ // Normalize dates to midnight for comparison
1349
+ const startDateMidnight = new Date(startDate);
1350
+ startDateMidnight.setHours(0, 0, 0, 0);
1351
+
1352
+ const hoverDateMidnight = new Date(hoverDate);
1353
+ hoverDateMidnight.setHours(0, 0, 0, 0);
1354
+
1355
+ // Ensure proper order for comparison (start date <= end date)
1356
+ const rangeStart =
1357
+ startDateMidnight <= hoverDateMidnight
1358
+ ? startDateMidnight
1359
+ : hoverDateMidnight;
1360
+ const rangeEnd =
1361
+ startDateMidnight <= hoverDateMidnight
1362
+ ? hoverDateMidnight
1363
+ : startDateMidnight;
1364
+
1365
+ // Generate all dates in the range as ISO strings (YYYY-MM-DD)
1366
+ const dateRangeISOStrings: string[] = [];
1367
+ const currentDate = new Date(rangeStart);
1368
+
1369
+ while (currentDate <= rangeEnd) {
1370
+ // Format as YYYY-MM-DD
1371
+ const year = currentDate.getFullYear();
1372
+ const month = String(currentDate.getMonth() + 1).padStart(2, '0');
1373
+ const day = String(currentDate.getDate()).padStart(2, '0');
1374
+
1375
+ dateRangeISOStrings.push(`${year}-${month}-${day}`);
1376
+
1377
+ // Move to the next day
1378
+ currentDate.setDate(currentDate.getDate() + 1);
1379
+ }
1380
+
1381
+ // Apply hover effect to all day cells in the range using the date-id attribute
1382
+ dateRangeISOStrings.forEach((dateStr) => {
1383
+ // Find the day cell with matching date-id
1384
+ const dayCells = this._calendarContainer.querySelectorAll(
1385
+ `button[data-date-id="${dateStr}"]`,
1386
+ );
1387
+
1388
+ dayCells.forEach((cell) => {
1389
+ // Skip if this is already a selected date (has blue background)
1390
+ if (cell.classList.contains('bg-blue-600')) return;
1391
+
1392
+ // Apply hover effect
1393
+ cell.classList.add('bg-blue-50', 'text-blue-600');
1394
+ });
1395
+ });
1396
+ }
1397
+ }