@keenthemes/ktui 1.0.28 → 1.1.0

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 (288) hide show
  1. package/README.md +27 -0
  2. package/dist/ktui.js +8780 -6199
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +2744 -1367
  6. package/lib/cjs/components/alert/alert.js +1025 -0
  7. package/lib/cjs/components/alert/alert.js.map +1 -0
  8. package/lib/cjs/components/alert/index.js +20 -0
  9. package/lib/cjs/components/alert/index.js.map +1 -0
  10. package/lib/cjs/components/alert/templates.js +120 -0
  11. package/lib/cjs/components/alert/templates.js.map +1 -0
  12. package/lib/cjs/components/alert/types.js +7 -0
  13. package/lib/cjs/components/alert/types.js.map +1 -0
  14. package/lib/cjs/components/datepicker/config/config.js +42 -0
  15. package/lib/cjs/components/datepicker/config/config.js.map +1 -0
  16. package/lib/cjs/components/datepicker/config/index.js +24 -0
  17. package/lib/cjs/components/datepicker/config/index.js.map +1 -0
  18. package/lib/cjs/components/datepicker/config/interfaces.js +7 -0
  19. package/lib/cjs/components/datepicker/config/interfaces.js.map +1 -0
  20. package/lib/cjs/components/datepicker/config/types.js +7 -0
  21. package/lib/cjs/components/datepicker/config/types.js.map +1 -0
  22. package/lib/cjs/components/datepicker/core/event-manager.js +135 -0
  23. package/lib/cjs/components/datepicker/core/event-manager.js.map +1 -0
  24. package/lib/cjs/components/datepicker/core/focus-manager.js +167 -0
  25. package/lib/cjs/components/datepicker/core/focus-manager.js.map +1 -0
  26. package/lib/cjs/components/datepicker/core/helpers.js +219 -0
  27. package/lib/cjs/components/datepicker/core/helpers.js.map +1 -0
  28. package/lib/cjs/components/datepicker/core/index.js +25 -0
  29. package/lib/cjs/components/datepicker/core/index.js.map +1 -0
  30. package/lib/cjs/components/datepicker/core/unified-state-manager.js +394 -0
  31. package/lib/cjs/components/datepicker/core/unified-state-manager.js.map +1 -0
  32. package/lib/cjs/components/datepicker/datepicker.js +2066 -763
  33. package/lib/cjs/components/datepicker/datepicker.js.map +1 -1
  34. package/lib/cjs/components/datepicker/index.js +19 -8
  35. package/lib/cjs/components/datepicker/index.js.map +1 -1
  36. package/lib/cjs/components/datepicker/ui/index.js +23 -0
  37. package/lib/cjs/components/datepicker/ui/index.js.map +1 -0
  38. package/lib/cjs/components/datepicker/ui/input/dropdown.js +489 -0
  39. package/lib/cjs/components/datepicker/ui/input/dropdown.js.map +1 -0
  40. package/lib/cjs/components/datepicker/ui/input/index.js +23 -0
  41. package/lib/cjs/components/datepicker/ui/input/index.js.map +1 -0
  42. package/lib/cjs/components/datepicker/ui/input/segmented-input.js +640 -0
  43. package/lib/cjs/components/datepicker/ui/input/segmented-input.js.map +1 -0
  44. package/lib/cjs/components/datepicker/ui/renderers/calendar.js +446 -0
  45. package/lib/cjs/components/datepicker/ui/renderers/calendar.js.map +1 -0
  46. package/lib/cjs/components/datepicker/ui/renderers/footer.js +42 -0
  47. package/lib/cjs/components/datepicker/ui/renderers/footer.js.map +1 -0
  48. package/lib/cjs/components/datepicker/ui/renderers/header.js +32 -0
  49. package/lib/cjs/components/datepicker/ui/renderers/header.js.map +1 -0
  50. package/lib/cjs/components/datepicker/ui/renderers/index.js +25 -0
  51. package/lib/cjs/components/datepicker/ui/renderers/index.js.map +1 -0
  52. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js +384 -0
  53. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js.map +1 -0
  54. package/lib/cjs/components/datepicker/ui/templates/index.js +22 -0
  55. package/lib/cjs/components/datepicker/ui/templates/index.js.map +1 -0
  56. package/lib/cjs/components/datepicker/ui/templates/templates.js +253 -0
  57. package/lib/cjs/components/datepicker/ui/templates/templates.js.map +1 -0
  58. package/lib/cjs/components/datepicker/utils/date-formatters.js +88 -0
  59. package/lib/cjs/components/datepicker/utils/date-formatters.js.map +1 -0
  60. package/lib/cjs/components/datepicker/utils/date-utils.js +194 -0
  61. package/lib/cjs/components/datepicker/utils/date-utils.js.map +1 -0
  62. package/lib/cjs/components/datepicker/utils/index.js +24 -0
  63. package/lib/cjs/components/datepicker/utils/index.js.map +1 -0
  64. package/lib/cjs/components/datepicker/utils/time-utils.js +213 -0
  65. package/lib/cjs/components/datepicker/utils/time-utils.js.map +1 -0
  66. package/lib/cjs/index.js +6 -1
  67. package/lib/cjs/index.js.map +1 -1
  68. package/lib/esm/components/alert/alert.js +1022 -0
  69. package/lib/esm/components/alert/alert.js.map +1 -0
  70. package/lib/esm/components/alert/index.js +4 -0
  71. package/lib/esm/components/alert/index.js.map +1 -0
  72. package/lib/esm/components/alert/templates.js +112 -0
  73. package/lib/esm/components/alert/templates.js.map +1 -0
  74. package/lib/esm/components/alert/types.js +6 -0
  75. package/lib/esm/components/alert/types.js.map +1 -0
  76. package/lib/esm/components/datepicker/config/config.js +39 -0
  77. package/lib/esm/components/datepicker/config/config.js.map +1 -0
  78. package/lib/esm/components/datepicker/config/index.js +8 -0
  79. package/lib/esm/components/datepicker/config/index.js.map +1 -0
  80. package/lib/esm/components/datepicker/config/interfaces.js +6 -0
  81. package/lib/esm/components/datepicker/config/interfaces.js.map +1 -0
  82. package/lib/esm/components/datepicker/config/types.js +6 -0
  83. package/lib/esm/components/datepicker/config/types.js.map +1 -0
  84. package/lib/esm/components/datepicker/core/event-manager.js +133 -0
  85. package/lib/esm/components/datepicker/core/event-manager.js.map +1 -0
  86. package/lib/esm/components/datepicker/core/focus-manager.js +164 -0
  87. package/lib/esm/components/datepicker/core/focus-manager.js.map +1 -0
  88. package/lib/esm/components/datepicker/core/helpers.js +211 -0
  89. package/lib/esm/components/datepicker/core/helpers.js.map +1 -0
  90. package/lib/esm/components/datepicker/core/index.js +9 -0
  91. package/lib/esm/components/datepicker/core/index.js.map +1 -0
  92. package/lib/esm/components/datepicker/core/unified-state-manager.js +391 -0
  93. package/lib/esm/components/datepicker/core/unified-state-manager.js.map +1 -0
  94. package/lib/esm/components/datepicker/datepicker.js +2065 -763
  95. package/lib/esm/components/datepicker/datepicker.js.map +1 -1
  96. package/lib/esm/components/datepicker/index.js +6 -8
  97. package/lib/esm/components/datepicker/index.js.map +1 -1
  98. package/lib/esm/components/datepicker/ui/index.js +7 -0
  99. package/lib/esm/components/datepicker/ui/index.js.map +1 -0
  100. package/lib/esm/components/datepicker/ui/input/dropdown.js +486 -0
  101. package/lib/esm/components/datepicker/ui/input/dropdown.js.map +1 -0
  102. package/lib/esm/components/datepicker/ui/input/index.js +7 -0
  103. package/lib/esm/components/datepicker/ui/input/index.js.map +1 -0
  104. package/lib/esm/components/datepicker/ui/input/segmented-input.js +637 -0
  105. package/lib/esm/components/datepicker/ui/input/segmented-input.js.map +1 -0
  106. package/lib/esm/components/datepicker/ui/renderers/calendar.js +443 -0
  107. package/lib/esm/components/datepicker/ui/renderers/calendar.js.map +1 -0
  108. package/lib/esm/components/datepicker/ui/renderers/footer.js +39 -0
  109. package/lib/esm/components/datepicker/ui/renderers/footer.js.map +1 -0
  110. package/lib/esm/components/datepicker/ui/renderers/header.js +29 -0
  111. package/lib/esm/components/datepicker/ui/renderers/header.js.map +1 -0
  112. package/lib/esm/components/datepicker/ui/renderers/index.js +9 -0
  113. package/lib/esm/components/datepicker/ui/renderers/index.js.map +1 -0
  114. package/lib/esm/components/datepicker/ui/renderers/time-picker.js +381 -0
  115. package/lib/esm/components/datepicker/ui/renderers/time-picker.js.map +1 -0
  116. package/lib/esm/components/datepicker/ui/templates/index.js +6 -0
  117. package/lib/esm/components/datepicker/ui/templates/index.js.map +1 -0
  118. package/lib/esm/components/datepicker/ui/templates/templates.js +242 -0
  119. package/lib/esm/components/datepicker/ui/templates/templates.js.map +1 -0
  120. package/lib/esm/components/datepicker/utils/date-formatters.js +83 -0
  121. package/lib/esm/components/datepicker/utils/date-formatters.js.map +1 -0
  122. package/lib/esm/components/datepicker/utils/date-utils.js +184 -0
  123. package/lib/esm/components/datepicker/utils/date-utils.js.map +1 -0
  124. package/lib/esm/components/datepicker/utils/index.js +8 -0
  125. package/lib/esm/components/datepicker/utils/index.js.map +1 -0
  126. package/lib/esm/components/datepicker/utils/time-utils.js +201 -0
  127. package/lib/esm/components/datepicker/utils/time-utils.js.map +1 -0
  128. package/lib/esm/index.js +4 -0
  129. package/lib/esm/index.js.map +1 -1
  130. package/package.json +22 -3
  131. package/src/components/alert/alert.css +429 -188
  132. package/src/components/alert/alert.ts +990 -0
  133. package/src/components/alert/index.ts +4 -0
  134. package/src/components/alert/templates.ts +110 -0
  135. package/src/components/alert/tests/accessibility/aria-roles.test.ts +19 -0
  136. package/src/components/alert/tests/accessibility/focus-management.test.ts +19 -0
  137. package/src/components/alert/tests/accessibility/keyboard-nav.test.ts +22 -0
  138. package/src/components/alert/tests/actions/confirm-cancel.test.ts +122 -0
  139. package/src/components/alert/tests/actions/input-field.test.ts +180 -0
  140. package/src/components/alert/tests/alert.basic.test.ts +126 -0
  141. package/src/components/alert/tests/alert.config.test.ts +75 -0
  142. package/src/components/alert/tests/alert.templates.test.ts +17 -0
  143. package/src/components/alert/tests/config/attribute-config.test.ts +94 -0
  144. package/src/components/alert/tests/config/json-config.test.ts +119 -0
  145. package/src/components/alert/tests/config/merging.test.ts +89 -0
  146. package/src/components/alert/tests/dismissal/auto-dismiss.test.ts +96 -0
  147. package/src/components/alert/tests/dismissal/escape-key-dismiss.test.ts +105 -0
  148. package/src/components/alert/tests/dismissal/manual-dismiss.test.ts +90 -0
  149. package/src/components/alert/tests/dismissal/outside-click-dismiss.test.ts +91 -0
  150. package/src/components/alert/tests/edge-cases/invalid-config.test.ts +19 -0
  151. package/src/components/alert/tests/edge-cases/multiple-alerts.test.ts +19 -0
  152. package/src/components/alert/tests/rendering/custom-content.test.ts +81 -0
  153. package/src/components/alert/tests/rendering/info-alert.test.ts +84 -0
  154. package/src/components/alert/tests/rendering/success-alert.test.ts +100 -0
  155. package/src/components/alert/tests/templates/default-templates.test.ts +16 -0
  156. package/src/components/alert/tests/templates/user-templates.test.ts +16 -0
  157. package/src/components/alert/types.ts +145 -0
  158. package/src/components/datepicker/__tests__/datepicker-events.test.ts +356 -0
  159. package/src/components/datepicker/__tests__/datepicker-init.test.ts +343 -0
  160. package/src/components/datepicker/__tests__/datepicker-integration.test.ts +435 -0
  161. package/src/components/datepicker/__tests__/datepicker-timezone.test.ts +220 -0
  162. package/src/components/datepicker/__tests__/segmented-input-focus.test.ts +380 -0
  163. package/src/components/datepicker/__tests__/selective-state-updates.test.ts +400 -0
  164. package/src/components/datepicker/__tests__/state-manager.test.ts +421 -0
  165. package/src/components/datepicker/__tests__/time-preservation.test.ts +387 -0
  166. package/src/components/datepicker/config/config.ts +40 -0
  167. package/src/components/datepicker/config/index.ts +8 -0
  168. package/src/components/datepicker/config/interfaces.ts +82 -0
  169. package/src/components/datepicker/config/types.ts +188 -0
  170. package/src/components/datepicker/core/event-manager.ts +159 -0
  171. package/src/components/datepicker/core/focus-manager.ts +201 -0
  172. package/src/components/datepicker/core/helpers.ts +231 -0
  173. package/src/components/datepicker/core/index.ts +9 -0
  174. package/src/components/datepicker/core/unified-state-manager.ts +459 -0
  175. package/src/components/datepicker/datepicker.css +429 -1
  176. package/src/components/datepicker/datepicker.ts +2538 -1277
  177. package/src/components/datepicker/index.ts +6 -8
  178. package/src/components/datepicker/ui/index.ts +7 -0
  179. package/src/components/datepicker/ui/input/dropdown.ts +552 -0
  180. package/src/components/datepicker/ui/input/index.ts +7 -0
  181. package/src/components/datepicker/ui/input/segmented-input.ts +638 -0
  182. package/src/components/datepicker/ui/renderers/__tests__/calendar-optimizations.test.ts +611 -0
  183. package/src/components/datepicker/ui/renderers/calendar.ts +530 -0
  184. package/src/components/datepicker/ui/renderers/footer.ts +43 -0
  185. package/src/components/datepicker/ui/renderers/header.ts +33 -0
  186. package/src/components/datepicker/ui/renderers/index.ts +9 -0
  187. package/src/components/datepicker/ui/renderers/time-picker.ts +438 -0
  188. package/src/components/datepicker/ui/templates/index.ts +6 -0
  189. package/src/components/datepicker/ui/templates/templates.ts +306 -0
  190. package/src/components/datepicker/utils/__tests__/date-formatters.test.ts +160 -0
  191. package/src/components/datepicker/utils/__tests__/date-utils-keys.test.ts +86 -0
  192. package/src/components/datepicker/utils/__tests__/date-utils-timezone.test.ts +215 -0
  193. package/src/components/datepicker/utils/date-formatters.ts +85 -0
  194. package/src/components/datepicker/utils/date-utils.ts +172 -0
  195. package/src/components/datepicker/utils/index.ts +8 -0
  196. package/src/components/datepicker/utils/time-utils.ts +221 -0
  197. package/src/index.ts +7 -1
  198. package/CONTRIBUTING.md +0 -101
  199. package/examples/datatable/checkbox-events-test.html +0 -400
  200. package/examples/datatable/credentials-test.html +0 -423
  201. package/examples/datatable/remote-checkbox-test.html +0 -365
  202. package/examples/datatable/sorting-test.html +0 -258
  203. package/examples/image-input/file-upload-example.html +0 -189
  204. package/examples/modal/persistent.html +0 -205
  205. package/examples/modal/remote-select-dropdown.html +0 -166
  206. package/examples/modal/select-dropdown-container.html +0 -129
  207. package/examples/select/avatar.html +0 -47
  208. package/examples/select/basic-usage.html +0 -39
  209. package/examples/select/country.html +0 -43
  210. package/examples/select/dark-mode.html +0 -93
  211. package/examples/select/description.html +0 -53
  212. package/examples/select/disable-option.html +0 -37
  213. package/examples/select/disable-select.html +0 -35
  214. package/examples/select/dropdowncontainer.html +0 -111
  215. package/examples/select/dynamic-methods.html +0 -273
  216. package/examples/select/formdata-remote.html +0 -161
  217. package/examples/select/global-config.html +0 -81
  218. package/examples/select/icon-multiple.html +0 -50
  219. package/examples/select/icon.html +0 -48
  220. package/examples/select/max-selection.html +0 -38
  221. package/examples/select/modal-container.html +0 -128
  222. package/examples/select/modal-positioning-test.html +0 -338
  223. package/examples/select/modal.html +0 -80
  224. package/examples/select/multiple.html +0 -40
  225. package/examples/select/native-selected.html +0 -64
  226. package/examples/select/placeholder.html +0 -40
  227. package/examples/select/remote-data-preselected.html +0 -283
  228. package/examples/select/remote-data.html +0 -38
  229. package/examples/select/search.html +0 -57
  230. package/examples/select/sizes.html +0 -94
  231. package/examples/select/tags-enhanced.html +0 -86
  232. package/examples/select/tags-icons.html +0 -57
  233. package/examples/select/template-customization.html +0 -61
  234. package/examples/sticky/README.md +0 -158
  235. package/examples/sticky/debug-sticky.html +0 -144
  236. package/examples/sticky/test-runner.html +0 -175
  237. package/examples/sticky/test-sticky-logic.js +0 -369
  238. package/examples/sticky/test-sticky-positioning.html +0 -386
  239. package/examples/toast/example.html +0 -479
  240. package/lib/cjs/components/datepicker/calendar.js +0 -1061
  241. package/lib/cjs/components/datepicker/calendar.js.map +0 -1
  242. package/lib/cjs/components/datepicker/config.js +0 -332
  243. package/lib/cjs/components/datepicker/config.js.map +0 -1
  244. package/lib/cjs/components/datepicker/dropdown.js +0 -635
  245. package/lib/cjs/components/datepicker/dropdown.js.map +0 -1
  246. package/lib/cjs/components/datepicker/events.js +0 -129
  247. package/lib/cjs/components/datepicker/events.js.map +0 -1
  248. package/lib/cjs/components/datepicker/keyboard.js +0 -536
  249. package/lib/cjs/components/datepicker/keyboard.js.map +0 -1
  250. package/lib/cjs/components/datepicker/locales.js +0 -78
  251. package/lib/cjs/components/datepicker/locales.js.map +0 -1
  252. package/lib/cjs/components/datepicker/templates.js +0 -403
  253. package/lib/cjs/components/datepicker/templates.js.map +0 -1
  254. package/lib/cjs/components/datepicker/types.js +0 -23
  255. package/lib/cjs/components/datepicker/types.js.map +0 -1
  256. package/lib/cjs/components/datepicker/utils.js +0 -524
  257. package/lib/cjs/components/datepicker/utils.js.map +0 -1
  258. package/lib/esm/components/datepicker/calendar.js +0 -1058
  259. package/lib/esm/components/datepicker/calendar.js.map +0 -1
  260. package/lib/esm/components/datepicker/config.js +0 -329
  261. package/lib/esm/components/datepicker/config.js.map +0 -1
  262. package/lib/esm/components/datepicker/dropdown.js +0 -632
  263. package/lib/esm/components/datepicker/dropdown.js.map +0 -1
  264. package/lib/esm/components/datepicker/events.js +0 -126
  265. package/lib/esm/components/datepicker/events.js.map +0 -1
  266. package/lib/esm/components/datepicker/keyboard.js +0 -533
  267. package/lib/esm/components/datepicker/keyboard.js.map +0 -1
  268. package/lib/esm/components/datepicker/locales.js +0 -74
  269. package/lib/esm/components/datepicker/locales.js.map +0 -1
  270. package/lib/esm/components/datepicker/templates.js +0 -390
  271. package/lib/esm/components/datepicker/templates.js.map +0 -1
  272. package/lib/esm/components/datepicker/types.js +0 -20
  273. package/lib/esm/components/datepicker/types.js.map +0 -1
  274. package/lib/esm/components/datepicker/utils.js +0 -508
  275. package/lib/esm/components/datepicker/utils.js.map +0 -1
  276. package/prettier.config.js +0 -9
  277. package/src/components/datepicker/calendar.ts +0 -1397
  278. package/src/components/datepicker/config.ts +0 -368
  279. package/src/components/datepicker/dropdown.ts +0 -757
  280. package/src/components/datepicker/events.ts +0 -149
  281. package/src/components/datepicker/keyboard.ts +0 -646
  282. package/src/components/datepicker/locales.ts +0 -80
  283. package/src/components/datepicker/templates.ts +0 -792
  284. package/src/components/datepicker/types.ts +0 -154
  285. package/src/components/datepicker/utils.ts +0 -631
  286. package/src/components/select/variants.css +0 -4
  287. package/tsconfig.json +0 -17
  288. package/webpack.config.js +0 -118
@@ -1,6 +1,7 @@
1
- /**
2
- * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
- * Copyright 2025 by Keenthemes Inc
1
+ /*
2
+ * datepicker.ts - Main implementation for KTDatepicker component
3
+ * Provides single, range, and multi-date selection with segmented input UI.
4
+ * Modular rendering and state helpers are imported from datepicker-helpers.ts.
4
5
  */
5
6
  var __extends = (this && this.__extends) || (function () {
6
7
  var extendStatics = function (d, b) {
@@ -17,930 +18,2231 @@ var __extends = (this && this.__extends) || (function () {
17
18
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18
19
  };
19
20
  })();
21
+ var __assign = (this && this.__assign) || function () {
22
+ __assign = Object.assign || function(t) {
23
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
24
+ s = arguments[i];
25
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
26
+ t[p] = s[p];
27
+ }
28
+ return t;
29
+ };
30
+ return __assign.apply(this, arguments);
31
+ };
32
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
33
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
34
+ if (ar || !(i in from)) {
35
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
36
+ ar[i] = from[i];
37
+ }
38
+ }
39
+ return to.concat(ar || Array.prototype.slice.call(from));
40
+ };
20
41
  import KTComponent from '../component';
21
- import { KTDatepickerCalendar } from './calendar';
22
- import { KTDatepickerStateManager } from './config';
23
- import { KTDatepickerKeyboard } from './keyboard';
24
- import { formatDate, parseDate, isDateDisabled } from './utils';
25
- import { segmentedDateInputTemplate, segmentedDateRangeInputTemplate, placeholderTemplate, } from './templates';
26
- import { KTDatepickerEventName } from './events';
27
- // Helper function to replace stringToElement
28
- function createElement(html) {
29
- var template = document.createElement('template');
30
- template.innerHTML = html.trim();
31
- return template.content.firstChild;
32
- }
42
+ import { getTemplateStrings, defaultTemplates, createTemplateRenderer, renderTemplateString, mergeClassData } from './ui/templates/templates';
43
+ import { defaultDatepickerConfig } from './config/config';
44
+ import { renderHeader } from './ui/renderers/header';
45
+ import { renderCalendar } from './ui/renderers/calendar';
46
+ import { renderFooter } from './ui/renderers/footer';
47
+ import { renderTimePicker } from './ui/renderers/time-picker';
48
+ import { EventManager } from './core/event-manager';
49
+ import { FocusManager } from './core/focus-manager';
50
+ import { KTDatepickerDropdown } from './ui/input/dropdown';
51
+ import { KTDatepickerUnifiedStateManager } from './core/unified-state-manager';
52
+ import { formatDateToLocalString } from './utils/date-utils';
53
+ import { formatDate, normalizeDateToMidnight } from './utils/date-formatters';
54
+ import { dateToTimeState, applyTimeToDate, validateTime } from './utils/time-utils';
55
+ import { renderSingleSegmentedInputUI, renderRangeSegmentedInputUI, instantiateSingleSegmentedInput, instantiateRangeSegmentedInputs, updateRangeSelection } from './core/helpers';
33
56
  /**
34
- * KTDatepicker - Main datepicker component class
35
- * Manages the datepicker functionality and integration with input elements
57
+ * KTDatepicker
58
+ *
59
+ * Datepicker component for selecting single, range, or multiple dates.
60
+ *
61
+ * Features:
62
+ * - Opens on input focus or calendar button click (configurable)
63
+ * - Supports single, range, and multi-date modes
64
+ * - Customizable via templates and data attributes
65
+ * - Keyboard navigation and accessibility support
36
66
  */
37
67
  var KTDatepicker = /** @class */ (function (_super) {
38
68
  __extends(KTDatepicker, _super);
39
69
  /**
40
- * Constructor for the KTDatepicker class.
70
+ * Constructor: Initializes the datepicker component
41
71
  */
42
72
  function KTDatepicker(element, config) {
43
73
  var _this = _super.call(this) || this;
44
74
  _this._name = 'datepicker';
45
- _this._dateInputElement = null;
46
- _this._startDateInputElement = null;
47
- _this._endDateInputElement = null;
48
- _this._displayElement = null;
49
- _this._useSegmentedDisplay = false;
50
- _this._displayWrapper = null;
51
- _this._displayText = null;
52
- _this._currentDate = null;
53
- _this._currentRange = null;
54
- _this._segmentFocused = null;
55
- // Check if the element already has a datepicker instance attached to it
56
- if (element.getAttribute('data-kt-datepicker-initialized') === 'true') {
57
- return _this;
58
- }
59
- // Initialize the datepicker with the provided element
75
+ _this._defaultConfig = defaultDatepickerConfig;
76
+ _this._config = defaultDatepickerConfig;
77
+ _this._userTemplates = {};
78
+ _this._input = null;
79
+ _this._focusManager = null;
80
+ _this._dropdownModule = null;
81
+ _this._timePickerRenderer = null;
82
+ _this._unsubscribeFromState = null;
83
+ // Dynamic element detection
84
+ _this._elementObserver = null;
85
+ // DOM element cache for performance optimization
86
+ _this._cachedElements = {
87
+ calendarElement: null,
88
+ timePickerElement: null,
89
+ startContainer: null,
90
+ endContainer: null,
91
+ yearElement: null,
92
+ monthElement: null,
93
+ dayElement: null,
94
+ hourElement: null,
95
+ minuteElement: null,
96
+ secondElement: null,
97
+ ampmElement: null,
98
+ monthYearElement: null,
99
+ timeDisplay: null
100
+ };
101
+ /** Handler for Apply button in multi-date mode */
102
+ _this._onApplyMultiDate = function (e) {
103
+ // Apply button clicked in multi-date mode
104
+ };
105
+ _this._onToday = function (e) {
106
+ e.preventDefault();
107
+ var today = new Date();
108
+ _this.setDate(today);
109
+ };
110
+ // Stub for _onClear (to be implemented next)
111
+ _this._onClear = function (e) {
112
+ e.preventDefault();
113
+ // Clear all selection states using unified state manager
114
+ _this._unifiedStateManager.updateState({
115
+ selectedDate: null,
116
+ selectedRange: { start: null, end: null },
117
+ selectedDates: [],
118
+ selectedTime: null
119
+ }, 'clear');
120
+ if (_this._input) {
121
+ var evt = new Event('change', { bubbles: true });
122
+ _this._input.dispatchEvent(evt);
123
+ }
124
+ };
125
+ /**
126
+ * Handler for Apply button - confirms selection and closes dropdown
127
+ * Used in range and multi-date modes
128
+ */
129
+ _this._onApply = function (e) {
130
+ e.preventDefault();
131
+ e.stopPropagation();
132
+ var currentState = _this._unifiedStateManager.getState();
133
+ // Ensure input value is updated with current selection
134
+ _this._updateInput(currentState);
135
+ // Fire onChange event if there's a selection
136
+ if (_this._config.range && currentState.selectedRange) {
137
+ if (currentState.selectedRange.start || currentState.selectedRange.end) {
138
+ _this._fireDatepickerEvent('onChange', currentState.selectedRange, _this);
139
+ }
140
+ }
141
+ else if (_this._config.multiDate && currentState.selectedDates.length > 0) {
142
+ _this._fireDatepickerEvent('onChange', currentState.selectedDates, _this);
143
+ }
144
+ else if (currentState.selectedDate) {
145
+ _this._fireDatepickerEvent('onChange', currentState.selectedDate, _this);
146
+ }
147
+ // Trigger input change event
148
+ if (_this._input) {
149
+ var evt = new Event('change', { bubbles: true });
150
+ _this._input.dispatchEvent(evt);
151
+ }
152
+ // Close the dropdown
153
+ _this._unifiedStateManager.setDropdownOpen(false, 'apply-button');
154
+ };
155
+ /**
156
+ * Centralized keyboard event handler for all datepicker keyboard interactions.
157
+ * Handles navigation, selection, and closing for input, calendar, and popover.
158
+ * Covers: Tab, Shift+Tab, Arrow keys, Enter, Space, Escape, Home, End, PageUp, PageDown.
159
+ */
160
+ _this._onKeyDown = function (e) {
161
+ var _a;
162
+ if (!_this._unifiedStateManager.isDropdownOpen())
163
+ return;
164
+ var target = e.target;
165
+ // Check if segmented input is focused - let it handle its own keyboard events
166
+ if (target.closest('[data-segment]')) {
167
+ return; // Let segmented input handle its own keyboard events
168
+ }
169
+ // Handle Escape key
170
+ if (e.key === 'Escape') {
171
+ e.preventDefault();
172
+ return;
173
+ }
174
+ // Handle Tab/Shift+Tab: allow normal tabbing, but trap focus within dropdown if needed
175
+ if (e.key === 'Tab') {
176
+ // Optionally implement focus trap if required
177
+ return;
178
+ }
179
+ // Handle Arrow keys, Home/End, PageUp/PageDown for calendar grid navigation
180
+ var isCalendarGrid = target.closest('[data-kt-datepicker-calendar-grid]');
181
+ if (isCalendarGrid) {
182
+ // Find all day buttons
183
+ var dayButtons = Array.from(isCalendarGrid.querySelectorAll('button[data-day]'));
184
+ var currentIndex = dayButtons.findIndex(function (btn) { return btn === target; });
185
+ var nextIndex_1 = currentIndex;
186
+ if (e.key === 'ArrowRight')
187
+ nextIndex_1 = Math.min(dayButtons.length - 1, currentIndex + 1);
188
+ if (e.key === 'ArrowLeft')
189
+ nextIndex_1 = Math.max(0, currentIndex - 1);
190
+ if (e.key === 'ArrowDown')
191
+ nextIndex_1 = Math.min(dayButtons.length - 1, currentIndex + 7);
192
+ if (e.key === 'ArrowUp')
193
+ nextIndex_1 = Math.max(0, currentIndex - 7);
194
+ if (e.key === 'Home')
195
+ nextIndex_1 = Math.floor(currentIndex / 7) * 7;
196
+ if (e.key === 'End')
197
+ nextIndex_1 = Math.min(dayButtons.length - 1, Math.floor(currentIndex / 7) * 7 + 6);
198
+ if (e.key === 'PageUp' || e.key === 'PageDown') {
199
+ // Change month and focus first day
200
+ _this._changeMonth(e.key === 'PageUp' ? -1 : 1);
201
+ setTimeout(function () {
202
+ var newGrid = _this._element.querySelector('[data-kt-datepicker-calendar-grid]');
203
+ if (newGrid) {
204
+ var newButtons = Array.from(newGrid.querySelectorAll('button[data-day]'));
205
+ if (newButtons.length > 0) {
206
+ // Set roving tabindex
207
+ newButtons.forEach(function (btn, idx) { return btn.tabIndex = idx === 0 ? 0 : -1; });
208
+ newButtons[0].focus();
209
+ }
210
+ }
211
+ }, 0);
212
+ e.preventDefault();
213
+ return;
214
+ }
215
+ if (nextIndex_1 !== currentIndex && dayButtons[nextIndex_1]) {
216
+ // Set roving tabindex
217
+ dayButtons.forEach(function (btn, idx) { return btn.tabIndex = idx === nextIndex_1 ? 0 : -1; });
218
+ dayButtons[nextIndex_1].focus();
219
+ e.preventDefault();
220
+ return;
221
+ }
222
+ // Enter/Space: select date
223
+ if (e.key === 'Enter' || e.key === ' ') {
224
+ (_a = dayButtons[currentIndex]) === null || _a === void 0 ? void 0 : _a.click();
225
+ // Optionally announce selection to screen reader
226
+ var liveRegion = _this._element.querySelector('[data-kt-datepicker-live]');
227
+ if (liveRegion && dayButtons[currentIndex]) {
228
+ liveRegion.textContent = "Selected ".concat(dayButtons[currentIndex].getAttribute('aria-label'));
229
+ }
230
+ e.preventDefault();
231
+ return;
232
+ }
233
+ }
234
+ // Handle navigation for header buttons (prev/next month)
235
+ if (target.hasAttribute('data-kt-datepicker-prev') || target.hasAttribute('data-kt-datepicker-next')) {
236
+ if (e.key === 'Enter' || e.key === ' ') {
237
+ target.dispatchEvent(new MouseEvent('click', { bubbles: true }));
238
+ // Optionally announce navigation to screen reader
239
+ var liveRegion = _this._element.querySelector('[data-kt-datepicker-live]');
240
+ if (liveRegion) {
241
+ liveRegion.textContent = target.hasAttribute('data-kt-datepicker-prev') ? 'Previous month' : 'Next month';
242
+ }
243
+ e.preventDefault();
244
+ return;
245
+ }
246
+ }
247
+ // Handle footer buttons (today, clear, apply)
248
+ if (target.hasAttribute('data-kt-datepicker-today') || target.hasAttribute('data-kt-datepicker-clear') || target.hasAttribute('data-kt-datepicker-apply')) {
249
+ if (e.key === 'Enter' || e.key === ' ') {
250
+ target.dispatchEvent(new MouseEvent('click', { bubbles: true }));
251
+ // Optionally announce action to screen reader
252
+ var liveRegion = _this._element.querySelector('[data-kt-datepicker-live]');
253
+ if (liveRegion) {
254
+ if (target.hasAttribute('data-kt-datepicker-today'))
255
+ liveRegion.textContent = 'Today selected';
256
+ if (target.hasAttribute('data-kt-datepicker-clear'))
257
+ liveRegion.textContent = 'Selection cleared';
258
+ if (target.hasAttribute('data-kt-datepicker-apply'))
259
+ liveRegion.textContent = 'Selection applied';
260
+ }
261
+ e.preventDefault();
262
+ return;
263
+ }
264
+ }
265
+ };
266
+ /**
267
+ * Handler for input focus event, opens the datepicker if showOnFocus is true and input is not disabled/readonly
268
+ */
269
+ _this._onInputFocus = function (e) {
270
+ if (!_this._input)
271
+ return;
272
+ if (_this._input.hasAttribute('disabled') || _this._input.hasAttribute('readonly'))
273
+ return;
274
+ if (_this._config.showOnFocus) {
275
+ _this.open();
276
+ }
277
+ };
278
+ /**
279
+ * Handler for document click event, closes the datepicker if click is outside the component
280
+ */
281
+ _this._handleDocumentClick = function (e) {
282
+ // Skip if click-outside is disabled
283
+ if (!_this._config.closeOnOutsideClick)
284
+ return;
285
+ // Skip if dropdown is not open
286
+ if (!_this._unifiedStateManager.isDropdownOpen())
287
+ return;
288
+ var targetElement = e.target;
289
+ // Find the dropdown element (it's rendered in body, not inside _element)
290
+ var dropdownElement = document.querySelector("[data-kt-datepicker-dropdown][data-kt-datepicker-instance-id=\"".concat(_this._instanceId, "\"]"));
291
+ // Check if click is outside both the datepicker element and the dropdown
292
+ var isInsideDatepicker = _this._element.contains(targetElement);
293
+ var isInsideDropdown = dropdownElement && dropdownElement.contains(targetElement);
294
+ if (!isInsideDatepicker && !isInsideDropdown) {
295
+ _this.close();
296
+ }
297
+ };
298
+ // Generate unique instance ID
299
+ _this._instanceId = "datepicker-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
300
+ // Add instance ID to element for debugging
301
+ element.setAttribute('data-kt-datepicker-instance-id', _this._instanceId);
60
302
  _this._init(element);
61
- // Build the configuration object by merging the default config with the provided config
303
+ // Build config using the standard KTComponent approach
62
304
  _this._buildConfig(config);
63
- // Store the instance of the datepicker directly on the element
305
+ _this._templateSet = getTemplateStrings(_this._config);
306
+ _this._templateRenderer = createTemplateRenderer(_this._templateSet);
307
+ // Initialize unified state manager
308
+ _this._unifiedStateManager = new KTDatepickerUnifiedStateManager({
309
+ enableValidation: true,
310
+ enableDebugging: _this._config.debug || false,
311
+ enableUpdateBatching: true,
312
+ batchDelay: 16
313
+ });
314
+ // Subscribe to state changes
315
+ _this._unsubscribeFromState = _this._unifiedStateManager.subscribe(_this);
316
+ // Set placeholder from config if available
317
+ if (_this._input && _this._config.placeholder) {
318
+ _this._input.setAttribute('placeholder', _this._config.placeholder);
319
+ }
320
+ // Set disabled state from config if available
321
+ if (_this._input && _this._config.disabled) {
322
+ _this._input.setAttribute('disabled', 'true');
323
+ // Also set disabled state in unified state manager
324
+ _this._unifiedStateManager.setDropdownDisabled(true, 'config');
325
+ }
326
+ // --- Time initialization ---
327
+ if (_this._config.enableTime) {
328
+ _this._unifiedStateManager.updateState({
329
+ timeGranularity: _this._config.timeGranularity || 'minute'
330
+ }, 'config');
331
+ // Initialize time from selected date or current time
332
+ var baseDate = _this._unifiedStateManager.getState().selectedDate || _this._unifiedStateManager.getState().currentDate || new Date();
333
+ _this._unifiedStateManager.setSelectedTime(dateToTimeState(baseDate), 'config');
334
+ }
335
+ // --- Mode-specific initialization ---
336
+ if (_this._config.range && _this._config.valueRange) {
337
+ _this._initRangeFromConfig();
338
+ }
339
+ else if (_this._config.multiDate && Array.isArray(_this._config.values)) {
340
+ _this._initMultiDateFromConfig();
341
+ }
342
+ else if (_this._config.value) {
343
+ _this._initSingleDateFromConfig();
344
+ }
345
+ // --- Input focus event for showOnFocus ---
346
+ if (_this._input) {
347
+ _this._eventManager.addListener(_this._input, 'focus', _this._onInputFocus);
348
+ }
349
+ // --- Document click event for outside click detection ---
350
+ _this._eventManager.addListener(document, 'click', _this._handleDocumentClick);
64
351
  element.instance = _this;
65
- // Ensure the element is focusable
66
- _this._element.setAttribute('tabindex', '0');
67
- _this._element.classList.add('kt-datepicker', 'relative', 'focus:outline-none');
68
- // Mark as initialized
69
- _this._element.setAttribute('data-kt-datepicker-initialized', 'true');
70
- // Find input elements
71
- _this._initializeInputElements();
72
- // Create display element if needed
73
- _this._createDisplayElement();
74
- // Create state manager first
75
- _this._state = new KTDatepickerStateManager(_this._element, _this._config);
76
- _this._config = _this._state.getConfig();
77
- // Initialize the calendar and keyboard after creating the state manager
78
- _this._calendar = new KTDatepickerCalendar(_this._element, _this._state);
79
- _this._keyboard = new KTDatepickerKeyboard(_this._element, _this._state);
80
- // Initialize event manager
81
- _this._eventManager = _this._state.getEventManager();
82
- // Set up event listeners
83
- _this._setupEventListeners();
84
- // Initialize with any default values
85
- _this._initializeDefaultValues();
352
+ _this._render();
86
353
  return _this;
87
354
  }
88
355
  /**
89
- * Initialize input elements
356
+ * Initialize the datepicker components after configuration is set
90
357
  */
91
- KTDatepicker.prototype._initializeInputElements = function () {
92
- // Get main input element - will be hidden
93
- this._dateInputElement = this._element.querySelector('[data-kt-datepicker-input]');
94
- // Hide the input element and make it only for data storage
95
- if (this._dateInputElement) {
96
- this._dateInputElement.classList.add('hidden', 'sr-only');
97
- this._dateInputElement.setAttribute('aria-hidden', 'true');
98
- this._dateInputElement.tabIndex = -1;
358
+ KTDatepicker.prototype._initializeDatepicker = function () {
359
+ // Set up templates
360
+ this._templateSet = getTemplateStrings(this._config);
361
+ this._templateRenderer = createTemplateRenderer(this._templateSet);
362
+ // Initialize state manager
363
+ this._unifiedStateManager = new KTDatepickerUnifiedStateManager({
364
+ enableValidation: true,
365
+ enableDebugging: this._config.debug || false,
366
+ enableUpdateBatching: true,
367
+ batchDelay: 16
368
+ });
369
+ // Set initial state
370
+ this._unifiedStateManager.updateState(this._getInitialState(), 'initialization', true);
371
+ // Subscribe to state changes
372
+ this._unsubscribeFromState = this._unifiedStateManager.subscribe(this);
373
+ // Initialize event manager
374
+ this._eventManager = new EventManager();
375
+ // Set up instance ID for debugging
376
+ this._instanceId = "datepicker-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
377
+ this._element.setAttribute('data-kt-datepicker-instance-id', this._instanceId);
378
+ // Set placeholder from config if available
379
+ if (this._input && this._config.placeholder) {
380
+ this._input.setAttribute('placeholder', this._config.placeholder);
381
+ }
382
+ // Set disabled state from config if available
383
+ if (this._input && this._config.disabled) {
384
+ this._input.setAttribute('disabled', 'true');
385
+ // Also set disabled state in unified state manager
386
+ this._unifiedStateManager.setDropdownDisabled(true, 'config');
387
+ }
388
+ // --- Time initialization ---
389
+ if (this._config.enableTime) {
390
+ this._unifiedStateManager.updateState({
391
+ timeGranularity: this._config.timeGranularity || 'minute'
392
+ }, 'config');
393
+ // Initialize time from selected date or current time
394
+ var baseDate = this._unifiedStateManager.getState().selectedDate || this._unifiedStateManager.getState().currentDate || new Date();
395
+ this._unifiedStateManager.setSelectedTime(dateToTimeState(baseDate), 'config');
396
+ }
397
+ // --- Mode-specific initialization ---
398
+ if (this._config.range && this._config.valueRange) {
399
+ this._initRangeFromConfig();
400
+ }
401
+ else if (this._config.multiDate && Array.isArray(this._config.values)) {
402
+ this._initMultiDateFromConfig();
99
403
  }
100
- // Get range input elements if applicable
101
- this._startDateInputElement = this._element.querySelector('[data-kt-datepicker-start]');
102
- this._endDateInputElement = this._element.querySelector('[data-kt-datepicker-end]');
103
- // Get display element if exists
104
- this._displayElement = this._element.querySelector('[data-kt-datepicker-display]');
105
- // Check if we should use segmented display
106
- this._useSegmentedDisplay =
107
- this._element.hasAttribute('data-kt-datepicker-segmented') ||
108
- this._element.hasAttribute('data-kt-datepicker-segmented-input');
404
+ else if (this._config.value) {
405
+ this._initSingleDateFromConfig();
406
+ }
407
+ // --- Input focus event for showOnFocus ---
408
+ if (this._input) {
409
+ this._eventManager.addListener(this._input, 'focus', this._onInputFocus);
410
+ }
411
+ // --- Document click event for outside click detection ---
412
+ this._eventManager.addListener(document, 'click', this._handleDocumentClick);
413
+ this._element.instance = this;
414
+ // Initial render
415
+ this._render();
109
416
  };
110
417
  /**
111
- * Create display element for datepicker
418
+ * Get the initial state for the datepicker
112
419
  */
113
- KTDatepicker.prototype._createDisplayElement = function () {
114
- var _this = this;
115
- var _a;
116
- // Skip if already created
117
- if (this._displayElement) {
420
+ KTDatepicker.prototype._getInitialState = function () {
421
+ var now = new Date();
422
+ var selectedDate = this._config.value ? new Date(this._config.value) : null;
423
+ var selectedTime = selectedDate && this._config.enableTime ? dateToTimeState(selectedDate) : null;
424
+ return {
425
+ currentDate: selectedDate || now,
426
+ selectedDate: selectedDate,
427
+ selectedRange: null,
428
+ selectedDates: [],
429
+ selectedTime: selectedTime,
430
+ timeGranularity: this._config.timeGranularity || 'minute',
431
+ viewMode: 'days',
432
+ isOpen: false,
433
+ isFocused: false,
434
+ isTransitioning: false,
435
+ isDisabled: !!this._config.disabled,
436
+ validationErrors: [],
437
+ isValid: true,
438
+ dropdownState: {
439
+ isOpen: false,
440
+ isTransitioning: false,
441
+ isDisabled: !!this._config.disabled,
442
+ isFocused: false,
443
+ },
444
+ };
445
+ };
446
+ /**
447
+ * Initialize DOM element cache for performance optimization
448
+ */
449
+ KTDatepicker.prototype._initializeElementCache = function () {
450
+ var _a, _b;
451
+ // Cache main container elements
452
+ this._cachedElements.calendarElement = this._element.querySelector('[data-kt-datepicker-calendar-table]');
453
+ this._cachedElements.timePickerElement = this._element.querySelector('[data-kt-datepicker-time-container]');
454
+ // Cache range mode containers
455
+ this._cachedElements.startContainer = this._element.querySelector('[data-kt-datepicker-start-container]');
456
+ this._cachedElements.endContainer = this._element.querySelector('[data-kt-datepicker-end-container]');
457
+ // Cache segmented input elements
458
+ this._cachedElements.yearElement = this._element.querySelector('[data-segment="year"]');
459
+ this._cachedElements.monthElement = this._element.querySelector('[data-segment="month"]');
460
+ this._cachedElements.dayElement = this._element.querySelector('[data-segment="day"]');
461
+ this._cachedElements.hourElement = this._element.querySelector('[data-segment="hour"]');
462
+ this._cachedElements.minuteElement = this._element.querySelector('[data-segment="minute"]');
463
+ this._cachedElements.secondElement = this._element.querySelector('[data-segment="second"]');
464
+ this._cachedElements.ampmElement = this._element.querySelector('[data-segment="ampm"]');
465
+ // Cache navigation and display elements
466
+ this._cachedElements.monthYearElement = (_a = this._cachedElements.calendarElement) === null || _a === void 0 ? void 0 : _a.querySelector('[data-kt-datepicker-month-year]');
467
+ this._cachedElements.timeDisplay = (_b = this._cachedElements.timePickerElement) === null || _b === void 0 ? void 0 : _b.querySelector('[data-kt-datepicker-time-value]');
468
+ };
469
+ /**
470
+ * Refresh DOM element cache when structure changes
471
+ */
472
+ KTDatepicker.prototype._refreshElementCache = function () {
473
+ this._initializeElementCache();
474
+ };
475
+ /**
476
+ * Update input field with current state
477
+ */
478
+ KTDatepicker.prototype._updateInput = function (state) {
479
+ if (!this._input)
118
480
  return;
481
+ // Update input value
482
+ var value = '';
483
+ if (this._config.range && state.selectedRange) {
484
+ value = this._formatRange(state.selectedRange.start, state.selectedRange.end);
119
485
  }
120
- // Get format from config or use default
121
- var format = this._config.format || 'mm/dd/yyyy';
122
- var placeholder = ((_a = this._dateInputElement) === null || _a === void 0 ? void 0 : _a.getAttribute('placeholder')) || format;
123
- // Create wrapper for display element
124
- this._displayWrapper = document.createElement('div');
125
- this._displayWrapper.className =
126
- 'kt-datepicker-display-wrapper kt-datepicker-display-segment';
127
- this._displayWrapper.setAttribute('role', 'combobox');
128
- this._displayWrapper.setAttribute('aria-haspopup', 'dialog');
129
- this._displayWrapper.setAttribute('aria-expanded', 'false');
130
- this._element.appendChild(this._displayWrapper);
131
- if (this._useSegmentedDisplay) {
132
- // Create segmented display for better date part selection
133
- var displayContainer = document.createElement('div');
134
- displayContainer.className = 'kt-datepicker-display-element';
135
- displayContainer.setAttribute('tabindex', '0');
136
- displayContainer.setAttribute('role', 'textbox');
137
- displayContainer.setAttribute('aria-label', placeholder);
138
- displayContainer.setAttribute('data-kt-datepicker-display', '');
139
- // Add segmented template based on range mode
140
- if (this._config.range) {
141
- displayContainer.innerHTML = segmentedDateRangeInputTemplate(this._config.format || 'mm/dd/yyyy');
142
- }
143
- else {
144
- displayContainer.innerHTML = segmentedDateInputTemplate(this._config.format || 'mm/dd/yyyy');
145
- }
146
- this._displayElement = displayContainer;
147
- this._displayWrapper.appendChild(this._displayElement);
148
- // Add click handlers for segments
149
- var segments = this._displayElement.querySelectorAll('[data-segment]');
150
- segments.forEach(function (segment) {
151
- segment.addEventListener('click', function (e) {
152
- e.stopPropagation();
153
- var segmentType = segment.getAttribute('data-segment');
154
- _this._handleSegmentClick(segmentType);
155
- });
156
- });
486
+ else if (this._config.multiDate && state.selectedDates.length > 0) {
487
+ value = this._formatMultiDate(state.selectedDates);
488
+ }
489
+ else if (state.selectedDate) {
490
+ value = this._formatSingleDate(state.selectedDate);
491
+ }
492
+ if (this._input.value !== value) {
493
+ this._input.value = value;
494
+ }
495
+ // Update disabled state
496
+ if (state.isDisabled) {
497
+ this._input.setAttribute('disabled', 'true');
157
498
  }
158
499
  else {
159
- // Create simple display element
160
- this._displayElement = document.createElement('div');
161
- this._displayElement.className = 'kt-datepicker-display-element';
162
- this._displayElement.setAttribute('tabindex', '0');
163
- this._displayElement.setAttribute('role', 'textbox');
164
- this._displayElement.setAttribute('aria-label', placeholder);
165
- this._displayElement.setAttribute('data-placeholder', placeholder);
166
- this._displayElement.setAttribute('data-kt-datepicker-display', '');
167
- // Create display text element
168
- this._displayText = document.createElement('span');
169
- this._displayText.className = 'kt-datepicker-display-text';
170
- this._displayText.textContent = placeholder;
171
- this._displayText.classList.add('text-gray-400');
172
- this._displayElement.appendChild(this._displayText);
173
- this._displayWrapper.appendChild(this._displayElement);
174
- }
175
- // Add click event to display element
176
- this._displayElement.addEventListener('click', function (e) {
177
- e.preventDefault();
178
- if (!_this._state.getState().isOpen) {
179
- _this._state.setOpen(true);
180
- }
181
- });
182
- // Enhanced keyboard event handling for display element
183
- this._displayElement.addEventListener('keydown', function (e) {
184
- if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
185
- e.preventDefault();
186
- e.stopPropagation();
187
- // If not already open, open the dropdown
188
- if (!_this._state.getState().isOpen) {
189
- _this._state.setOpen(true);
190
- // Dispatch a custom event to notify about the keyboard open
191
- _this._eventManager.dispatchKeyboardOpenEvent();
192
- }
193
- }
194
- });
500
+ this._input.removeAttribute('disabled');
501
+ }
502
+ // Update placeholder
503
+ if (value) {
504
+ this._input.removeAttribute('placeholder');
505
+ }
506
+ else {
507
+ this._input.setAttribute('placeholder', 'Select date...');
508
+ }
195
509
  };
196
510
  /**
197
- * Handle segment click to focus and open appropriate view
198
- *
199
- * @param segmentType - Type of segment clicked
511
+ * Update single date segmented input
200
512
  */
201
- KTDatepicker.prototype._handleSegmentClick = function (segmentType) {
202
- if (!segmentType)
203
- return;
204
- // Store the focused segment
205
- this._segmentFocused = segmentType;
206
- // Remove highlight from all segments
207
- this._removeSegmentHighlights();
208
- // Add highlight to clicked segment
209
- if (this._displayElement) {
210
- var segment = this._displayElement.querySelector("[data-segment=\"".concat(segmentType, "\"]"));
211
- if (segment) {
212
- segment.classList.add('kt-datepicker-segment-focused');
213
- }
513
+ KTDatepicker.prototype._updateSingleSegmentedInput = function (state) {
514
+ var dateToUse = state.selectedDate || state.currentDate;
515
+ if (dateToUse) {
516
+ this._updateDateSegments(dateToUse);
517
+ }
518
+ // Update time segments if time is enabled
519
+ if (state.selectedTime) {
520
+ this._updateTimeSegments(state.selectedTime);
214
521
  }
215
- // Set the appropriate view mode based on segment type
216
- if (segmentType.includes('day')) {
217
- // Day segment - open in days view (default)
218
- this._state.setViewMode('days');
219
- this._state.setOpen(true);
522
+ };
523
+ /**
524
+ * Update date segments in a specific container
525
+ */
526
+ KTDatepicker.prototype._updateDateSegmentsInContainer = function (date, container) {
527
+ // For range mode, we need to query within the specific container
528
+ var yearElement = container.querySelector('[data-segment="year"]');
529
+ var monthElement = container.querySelector('[data-segment="month"]');
530
+ var dayElement = container.querySelector('[data-segment="day"]');
531
+ if (yearElement) {
532
+ yearElement.textContent = date.getFullYear().toString();
220
533
  }
221
- else if (segmentType.includes('month')) {
222
- // Month segment - open in months view
223
- this._state.setViewMode('months');
224
- this._state.setOpen(true);
534
+ if (monthElement) {
535
+ monthElement.textContent = (date.getMonth() + 1).toString().padStart(2, '0');
225
536
  }
226
- else if (segmentType.includes('year')) {
227
- // Year segment - open in years view
228
- this._state.setViewMode('years');
229
- this._state.setOpen(true);
537
+ if (dayElement) {
538
+ dayElement.textContent = date.getDate().toString().padStart(2, '0');
230
539
  }
231
540
  };
232
541
  /**
233
- * Set up event listeners
542
+ * Update date segments (year, month, day)
234
543
  */
235
- KTDatepicker.prototype._setupEventListeners = function () {
236
- var _this = this;
237
- // Listen for state changes
238
- this._eventManager.addEventListener(KTDatepickerEventName.STATE_CHANGE, function (e) {
239
- var state = e.detail.state;
240
- // Update ARIA attributes based on open state
241
- if (_this._displayWrapper) {
242
- _this._displayWrapper.setAttribute('aria-expanded', state.isOpen.toString());
243
- }
244
- // Update display when closing
245
- if (!state.isOpen && state.prevIsOpen) {
246
- _this._syncDisplayWithSelectedDate();
247
- }
248
- });
249
- // Set up change event listener to update input values
250
- this._eventManager.addEventListener(KTDatepickerEventName.DATE_CHANGE, this._handleDateChange.bind(this));
251
- // Add keyboard events to the root element
252
- this._element.addEventListener('keydown', function (e) {
253
- if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
254
- var state = _this._state.getState();
255
- if (!state.isOpen) {
256
- e.preventDefault();
257
- _this._state.setOpen(true);
258
- }
259
- }
260
- });
261
- // Add keyboard navigation for segments
262
- if (this._displayElement && this._useSegmentedDisplay) {
263
- this._displayElement.addEventListener('keydown', this._handleSegmentKeydown.bind(this));
544
+ KTDatepicker.prototype._updateDateSegments = function (date) {
545
+ this._updateDateSegmentsInContainer(date, this._element);
546
+ };
547
+ /**
548
+ * Update time segments
549
+ */
550
+ KTDatepicker.prototype._updateTimeSegments = function (time) {
551
+ if (this._cachedElements.hourElement) {
552
+ this._cachedElements.hourElement.textContent = time.hour.toString().padStart(2, '0');
553
+ }
554
+ if (this._cachedElements.minuteElement) {
555
+ this._cachedElements.minuteElement.textContent = time.minute.toString().padStart(2, '0');
556
+ }
557
+ if (this._cachedElements.secondElement) {
558
+ this._cachedElements.secondElement.textContent = time.second.toString().padStart(2, '0');
559
+ }
560
+ if (this._cachedElements.ampmElement) {
561
+ this._cachedElements.ampmElement.textContent = time.hour >= 12 ? 'PM' : 'AM';
264
562
  }
265
563
  };
266
564
  /**
267
- * Handle keyboard navigation between segments
268
- *
269
- * @param e - Keyboard event
565
+ * Update calendar display
270
566
  */
271
- KTDatepicker.prototype._handleSegmentKeydown = function (e) {
272
- // Only handle if we have a focused segment
273
- if (!this._segmentFocused)
567
+ KTDatepicker.prototype._updateCalendar = function (state) {
568
+ var _this = this;
569
+ // Try to find dropdown first
570
+ var dropdownEl = this._element.querySelector('[data-kt-datepicker-dropdown]');
571
+ if (!dropdownEl && this._instanceId) {
572
+ dropdownEl = document.querySelector("[data-kt-datepicker-dropdown][data-kt-datepicker-instance-id=\"".concat(this._instanceId, "\"]"));
573
+ }
574
+ if (!dropdownEl) {
274
575
  return;
275
- var target = e.target;
276
- var segmentType = target.getAttribute('data-segment');
277
- if (!segmentType)
576
+ }
577
+ // Find ALL calendar tables (for multi-month view support)
578
+ var calendarElements = dropdownEl.querySelectorAll('[data-kt-datepicker-calendar-table]');
579
+ // If no calendars found, return early
580
+ if (calendarElements.length === 0) {
278
581
  return;
279
- // Handle keyboard navigation
280
- switch (e.key) {
281
- case 'ArrowLeft':
282
- e.preventDefault();
283
- this._navigateSegments('prev', segmentType);
284
- break;
285
- case 'ArrowRight':
286
- e.preventDefault();
287
- this._navigateSegments('next', segmentType);
288
- break;
289
- case 'Tab':
290
- // Let default tab behavior work, but update focus segment when tabbing
291
- this._segmentFocused = segmentType;
292
- // Remove highlight from all segments
293
- this._removeSegmentHighlights();
294
- // Add highlight to current segment
295
- target.classList.add('segment-focused');
296
- break;
297
- case 'Enter':
298
- case ' ':
299
- e.preventDefault();
300
- this._handleSegmentClick(segmentType);
301
- break;
582
+ }
583
+ // Update cache with first calendar (for backward compatibility)
584
+ if (calendarElements.length > 0) {
585
+ this._cachedElements.calendarElement = calendarElements[0];
586
+ }
587
+ // Update range state on ALL calendar elements for hover handlers to access dynamically
588
+ if (this._config.range && state.selectedRange) {
589
+ calendarElements.forEach(function (calendar) {
590
+ if (state.selectedRange.start) {
591
+ calendar.setAttribute('data-kt-range-start', formatDateToLocalString(state.selectedRange.start));
592
+ }
593
+ else {
594
+ calendar.removeAttribute('data-kt-range-start');
595
+ }
596
+ if (state.selectedRange.end) {
597
+ calendar.setAttribute('data-kt-range-end', formatDateToLocalString(state.selectedRange.end));
598
+ }
599
+ else {
600
+ calendar.removeAttribute('data-kt-range-end');
601
+ }
602
+ });
603
+ }
604
+ else if (this._config.range) {
605
+ // No range selected, clear attributes from all calendars
606
+ calendarElements.forEach(function (calendar) {
607
+ calendar.removeAttribute('data-kt-range-start');
608
+ calendar.removeAttribute('data-kt-range-end');
609
+ });
610
+ }
611
+ // Update date selection highlighting for ALL calendars
612
+ calendarElements.forEach(function (calendarElement) {
613
+ _this._updateDateSelection(state, calendarElement);
614
+ });
615
+ // Update navigation (month/year display) - only update first calendar for navigation
616
+ if (calendarElements.length > 0) {
617
+ this._updateNavigation(state, calendarElements[0]);
302
618
  }
303
619
  };
304
620
  /**
305
- * Navigate between segments with keyboard
306
- *
307
- * @param direction - 'prev' or 'next'
308
- * @param currentSegment - Current segment identifier
621
+ * Update date selection highlighting
309
622
  */
310
- KTDatepicker.prototype._navigateSegments = function (direction, currentSegment) {
311
- if (!this._displayElement)
312
- return;
313
- // Define segment order
314
- var segments;
315
- if (this._config.range) {
316
- segments = [
317
- 'start-month',
318
- 'start-day',
319
- 'start-year',
320
- 'end-month',
321
- 'end-day',
322
- 'end-year',
323
- ];
623
+ KTDatepicker.prototype._updateDateSelection = function (state, calendarElement) {
624
+ var _this = this;
625
+ var _a, _b;
626
+ // Clear previous selections
627
+ var selectedCells = calendarElement.querySelectorAll('[data-kt-selected]');
628
+ selectedCells.forEach(function (cell) {
629
+ cell.removeAttribute('data-kt-selected');
630
+ cell.removeAttribute('aria-selected');
631
+ cell.classList.remove('active'); // Remove active class if present (legacy support)
632
+ });
633
+ // Clear previous range highlighting (only if range is complete, not during hover preview)
634
+ // Check if we're in hover preview mode by checking if range has start but no end
635
+ if (((_a = state.selectedRange) === null || _a === void 0 ? void 0 : _a.start) && ((_b = state.selectedRange) === null || _b === void 0 ? void 0 : _b.end)) {
636
+ // Range is complete, clear all hover-range attributes to re-apply for completed range
637
+ var hoverRangeCells = calendarElement.querySelectorAll('[data-kt-hover-range]');
638
+ hoverRangeCells.forEach(function (cell) {
639
+ cell.removeAttribute('data-kt-hover-range');
640
+ });
324
641
  }
325
- else {
326
- segments = ['month', 'day', 'year'];
642
+ // Highlight selected date(s)
643
+ if (state.selectedDate) {
644
+ this._highlightDate(state.selectedDate, calendarElement);
327
645
  }
328
- // Find current index
329
- var currentIndex = segments.indexOf(currentSegment);
330
- if (currentIndex === -1)
331
- return;
332
- // Calculate new index
333
- var newIndex;
334
- if (direction === 'prev') {
335
- newIndex = currentIndex === 0 ? segments.length - 1 : currentIndex - 1;
646
+ // Highlight range start/end dates and dates in between
647
+ if (state.selectedRange) {
648
+ this._highlightDateRange(state.selectedRange, calendarElement);
336
649
  }
337
- else {
338
- newIndex = currentIndex === segments.length - 1 ? 0 : currentIndex + 1;
650
+ // Highlight multi-date selections
651
+ if (state.selectedDates.length > 0) {
652
+ state.selectedDates.forEach(function (date) { return _this._highlightDate(date, calendarElement); });
339
653
  }
340
- // Find new segment element
341
- var newSegment = this._displayElement.querySelector("[data-segment=\"".concat(segments[newIndex], "\"]"));
342
- if (!newSegment)
343
- return;
344
- // Update focus
345
- newSegment.focus();
346
- this._segmentFocused = segments[newIndex];
347
- // Remove highlight from all segments
348
- this._removeSegmentHighlights();
349
- // Add highlight to new segment
350
- newSegment.classList.add('segment-focused');
351
654
  };
352
655
  /**
353
- * Remove highlight from all segments
656
+ * Highlight a specific date
657
+ * Uses data-kt-selected attribute (class="active" removed for consistency)
354
658
  */
355
- KTDatepicker.prototype._removeSegmentHighlights = function () {
356
- if (!this._displayElement)
357
- return;
358
- var segments = this._displayElement.querySelectorAll('.segment-part');
359
- segments.forEach(function (segment) {
360
- segment.classList.remove('segment-focused');
361
- });
659
+ KTDatepicker.prototype._highlightDate = function (date, calendarElement) {
660
+ var cell = this._findDayCell(date, calendarElement);
661
+ if (cell) {
662
+ cell.setAttribute('data-kt-selected', 'true');
663
+ cell.setAttribute('aria-selected', 'true');
664
+ // Note: class="active" is redundant - CSS already targets data-kt-selected
665
+ }
362
666
  };
363
667
  /**
364
- * Sync display element with the selected date
668
+ * Highlight a date range
669
+ * Uses data-kt-hover-range for both hover preview and completed ranges (consolidated)
365
670
  */
366
- KTDatepicker.prototype._syncDisplayWithSelectedDate = function () {
367
- var _a;
368
- if (!this._displayElement)
369
- return;
370
- var state = this._state.getState();
371
- var selectedDate = state.selectedDate;
372
- var selectedDateRange = state.selectedDateRange;
373
- if (this._useSegmentedDisplay) {
374
- // Update segmented display elements
375
- if (selectedDate) {
376
- // Single date
377
- var daySegment = this._displayElement.querySelector('[data-segment="day"]');
378
- var monthSegment = this._displayElement.querySelector('[data-segment="month"]');
379
- var yearSegment = this._displayElement.querySelector('[data-segment="year"]');
380
- if (daySegment) {
381
- daySegment.textContent = selectedDate
382
- .getDate()
383
- .toString()
384
- .padStart(2, '0');
385
- }
386
- if (monthSegment) {
387
- monthSegment.textContent = (selectedDate.getMonth() + 1)
388
- .toString()
389
- .padStart(2, '0');
390
- }
391
- if (yearSegment) {
392
- yearSegment.textContent = selectedDate.getFullYear().toString();
393
- }
671
+ KTDatepicker.prototype._highlightDateRange = function (range, calendarElement) {
672
+ if (!range.start || !range.end) {
673
+ // If only start or end is set, just highlight that date (hover preview handles the rest)
674
+ if (range.start) {
675
+ this._highlightDate(range.start, calendarElement);
394
676
  }
395
- else if (selectedDateRange && selectedDateRange.startDate) {
396
- // Range selection
397
- var startDay = this._displayElement.querySelector('[data-segment="start-day"]');
398
- var startMonth = this._displayElement.querySelector('[data-segment="start-month"]');
399
- var startYear = this._displayElement.querySelector('[data-segment="start-year"]');
400
- if (startDay) {
401
- startDay.textContent = selectedDateRange.startDate
402
- .getDate()
403
- .toString()
404
- .padStart(2, '0');
405
- }
406
- if (startMonth) {
407
- startMonth.textContent = (selectedDateRange.startDate.getMonth() + 1)
408
- .toString()
409
- .padStart(2, '0');
410
- }
411
- if (startYear) {
412
- startYear.textContent = selectedDateRange.startDate
413
- .getFullYear()
414
- .toString();
415
- }
416
- if (selectedDateRange.endDate) {
417
- var endDay = this._displayElement.querySelector('[data-segment="end-day"]');
418
- var endMonth = this._displayElement.querySelector('[data-segment="end-month"]');
419
- var endYear = this._displayElement.querySelector('[data-segment="end-year"]');
420
- if (endDay) {
421
- endDay.textContent = selectedDateRange.endDate
422
- .getDate()
423
- .toString()
424
- .padStart(2, '0');
425
- }
426
- if (endMonth) {
427
- endMonth.textContent = (selectedDateRange.endDate.getMonth() + 1)
428
- .toString()
429
- .padStart(2, '0');
430
- }
431
- if (endYear) {
432
- endYear.textContent = selectedDateRange.endDate
433
- .getFullYear()
434
- .toString();
435
- }
436
- }
677
+ if (range.end) {
678
+ this._highlightDate(range.end, calendarElement);
437
679
  }
680
+ return;
438
681
  }
439
- else if (this._displayText) {
440
- // Simple display
441
- if (selectedDate) {
442
- // Clear placeholder styling
443
- this._displayText.classList.remove('placeholder');
444
- // Format date(s) based on config
445
- if (this._config.range &&
446
- selectedDateRange &&
447
- selectedDateRange.startDate &&
448
- selectedDateRange.endDate) {
449
- this._displayText.textContent = "".concat(formatDate(selectedDateRange.startDate, this._config.format, this._config), " - ").concat(formatDate(selectedDateRange.endDate, this._config.format, this._config));
450
- }
451
- else {
452
- this._displayText.textContent = formatDate(selectedDate, this._config.format, this._config);
682
+ // Normalize dates to local midnight for accurate comparison
683
+ var start = new Date(range.start);
684
+ start.setHours(0, 0, 0, 0);
685
+ var end = new Date(range.end);
686
+ end.setHours(0, 0, 0, 0);
687
+ // Determine actual start and end (handle backward selection)
688
+ var actualStart = start <= end ? start : end;
689
+ var actualEnd = start <= end ? end : start;
690
+ // Highlight start and end dates
691
+ this._highlightDate(actualStart, calendarElement);
692
+ this._highlightDate(actualEnd, calendarElement);
693
+ // Find all calendars in multi-month view to highlight range across all visible months
694
+ var dropdownEl = calendarElement.closest('[data-kt-datepicker-dropdown]');
695
+ var allCalendars = dropdownEl
696
+ ? Array.from(dropdownEl.querySelectorAll('[data-kt-datepicker-calendar-table]'))
697
+ : [calendarElement];
698
+ // Highlight all dates in between using data-kt-hover-range (same as hover preview)
699
+ var current = new Date(actualStart);
700
+ current.setDate(current.getDate() + 1); // Start from day after start
701
+ while (current < actualEnd) {
702
+ var dateLocal = formatDateToLocalString(current);
703
+ // Search across all calendars to find the cell
704
+ for (var _i = 0, allCalendars_1 = allCalendars; _i < allCalendars_1.length; _i++) {
705
+ var calendar = allCalendars_1[_i];
706
+ var cell = calendar.querySelector("td[data-kt-datepicker-day][data-date=\"".concat(dateLocal, "\"]"));
707
+ if (cell) {
708
+ // Use data-kt-hover-range for completed ranges (consolidated with hover preview)
709
+ cell.setAttribute('data-kt-hover-range', 'true');
710
+ break; // Found in this calendar, no need to search others
453
711
  }
454
712
  }
455
- else {
456
- // No date selected, show format as placeholder
457
- var placeholder = ((_a = this._displayElement) === null || _a === void 0 ? void 0 : _a.getAttribute('data-placeholder')) ||
458
- this._config.format;
459
- this._displayText.textContent = placeholder;
460
- this._displayText.classList.add('placeholder');
461
- }
713
+ current.setDate(current.getDate() + 1);
462
714
  }
463
715
  };
464
716
  /**
465
- * Handle date change events
466
- *
467
- * @param e - Custom event with date change details
717
+ * Find day cell for a specific date
718
+ * Uses data-date attribute (YYYY-MM-DD format) for accurate date matching across months
719
+ * Searches within the provided calendar element, or across all calendars in multi-month view
468
720
  */
469
- KTDatepicker.prototype._handleDateChange = function (e) {
470
- var _a;
471
- var detail = (_a = e.detail) === null || _a === void 0 ? void 0 : _a.payload;
472
- if (!detail)
473
- return;
474
- // Handle single date selection
475
- if (detail.selectedDate) {
476
- var formattedDate = formatDate(detail.selectedDate, this._config.format, this._config);
477
- // Update hidden input value
478
- if (this._dateInputElement) {
479
- this._dateInputElement.value = formattedDate;
480
- // Dispatch change event on input to trigger form validation
481
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
482
- }
483
- // Update display element
484
- this._updateDisplayElement(detail.selectedDate);
485
- }
486
- // Handle date range selection
487
- if (detail.selectedDateRange && this._config.range) {
488
- var _b = detail.selectedDateRange, startDate = _b.startDate, endDate = _b.endDate;
489
- // Format the range for the hidden input
490
- if (this._dateInputElement) {
491
- var displayValue = '';
492
- if (startDate) {
493
- displayValue = formatDate(startDate, this._config.format, this._config);
494
- if (endDate) {
495
- var endFormatted = formatDate(endDate, this._config.format, this._config);
496
- displayValue += "".concat(this._config.rangeSeparator).concat(endFormatted);
721
+ KTDatepicker.prototype._findDayCell = function (date, calendarElement) {
722
+ var dateLocal = formatDateToLocalString(date); // Use local timezone date string for accurate matching (YYYY-MM-DD)
723
+ // First, try to find in the provided calendar element
724
+ var cells = calendarElement.querySelectorAll('td[data-kt-datepicker-day]');
725
+ for (var _i = 0, _a = Array.from(cells); _i < _a.length; _i++) {
726
+ var cell = _a[_i];
727
+ var cellDate = cell.getAttribute('data-date');
728
+ if (cellDate === dateLocal) {
729
+ return cell;
730
+ }
731
+ }
732
+ // If not found in provided calendar, search across all calendars in multi-month view
733
+ // This handles cases where the date might be in a different visible month
734
+ var dropdownEl = calendarElement.closest('[data-kt-datepicker-dropdown]');
735
+ if (dropdownEl) {
736
+ var allCalendars = dropdownEl.querySelectorAll('[data-kt-datepicker-calendar-table]');
737
+ for (var _b = 0, _c = Array.from(allCalendars); _b < _c.length; _b++) {
738
+ var calendar = _c[_b];
739
+ var allCells = calendar.querySelectorAll('td[data-kt-datepicker-day]');
740
+ for (var _d = 0, _e = Array.from(allCells); _d < _e.length; _d++) {
741
+ var cell = _e[_d];
742
+ var cellDate = cell.getAttribute('data-date');
743
+ if (cellDate === dateLocal) {
744
+ return cell;
497
745
  }
498
746
  }
499
- this._dateInputElement.value = displayValue;
500
- // Dispatch change event on input
501
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
502
747
  }
503
- // Update individual start/end inputs if they exist
504
- if (this._startDateInputElement && startDate) {
505
- this._startDateInputElement.value = formatDate(startDate, this._config.format, this._config);
506
- this._startDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
507
- }
508
- if (this._endDateInputElement && endDate) {
509
- this._endDateInputElement.value = formatDate(endDate, this._config.format, this._config);
510
- this._endDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
511
- }
512
- // Update display element for range
513
- this._updateRangeDisplayElement(startDate, endDate);
514
748
  }
749
+ // No fallback - if date not found by data-date, it's not in any visible calendar view
750
+ // This prevents incorrect matches (e.g., matching Oct 20 when looking for Nov 20)
751
+ return null;
515
752
  };
516
753
  /**
517
- * Update the display element for a single date
518
- *
519
- * @param date - The date to display
754
+ * Update navigation display
520
755
  */
521
- KTDatepicker.prototype._updateDisplayElement = function (date) {
522
- var _a;
523
- if (!this._displayElement)
524
- return;
525
- if (!date) {
526
- // If no date, show placeholder
527
- var placeholder = ((_a = this._dateInputElement) === null || _a === void 0 ? void 0 : _a.getAttribute('placeholder')) || 'Select date';
528
- this._displayElement.innerHTML = placeholderTemplate(placeholder);
756
+ KTDatepicker.prototype._updateNavigation = function (state, calendarElement) {
757
+ if (this._cachedElements.monthYearElement) {
758
+ var month = state.currentDate.toLocaleDateString('en-US', { month: 'long' });
759
+ var year = state.currentDate.getFullYear();
760
+ this._cachedElements.monthYearElement.textContent = "".concat(month, " ").concat(year);
761
+ }
762
+ };
763
+ /**
764
+ * Update time picker display
765
+ */
766
+ KTDatepicker.prototype._updateTimePicker = function (state) {
767
+ if (!this._cachedElements.timePickerElement || !state.selectedTime)
529
768
  return;
769
+ if (this._cachedElements.timeDisplay) {
770
+ var timeString = "".concat(state.selectedTime.hour.toString().padStart(2, '0'), ":").concat(state.selectedTime.minute.toString().padStart(2, '0'), ":").concat(state.selectedTime.second.toString().padStart(2, '0'));
771
+ this._cachedElements.timeDisplay.textContent = timeString;
772
+ }
773
+ };
774
+ /**
775
+ * Fire events based on state changes using the centralized event system
776
+ */
777
+ KTDatepicker.prototype._fireEvents = function (newState, oldState) {
778
+ var _a, _b, _c, _d;
779
+ // Fire onChange when selected date changes
780
+ if (newState.selectedDate !== oldState.selectedDate ||
781
+ ((_a = newState.selectedRange) === null || _a === void 0 ? void 0 : _a.start) !== ((_b = oldState.selectedRange) === null || _b === void 0 ? void 0 : _b.start) ||
782
+ ((_c = newState.selectedRange) === null || _c === void 0 ? void 0 : _c.end) !== ((_d = oldState.selectedRange) === null || _d === void 0 ? void 0 : _d.end) ||
783
+ JSON.stringify(newState.selectedDates) !== JSON.stringify(oldState.selectedDates)) {
784
+ var selectedValue = null;
785
+ if (this._config.range && newState.selectedRange) {
786
+ // For range mode, pass the end date if both are selected, otherwise null
787
+ selectedValue = newState.selectedRange.end || newState.selectedRange.start;
788
+ }
789
+ else if (this._config.multiDate && newState.selectedDates.length > 0) {
790
+ // For multi-date mode, pass the last selected date
791
+ selectedValue = newState.selectedDates[newState.selectedDates.length - 1];
792
+ }
793
+ else {
794
+ // For single date mode
795
+ selectedValue = newState.selectedDate;
796
+ }
797
+ this._fireDatepickerEvent('onChange', selectedValue, this);
530
798
  }
531
- if (this._useSegmentedDisplay) {
532
- // Update segmented display
533
- var day = date.getDate();
534
- var month = date.getMonth() + 1;
535
- var year = date.getFullYear();
536
- var daySegment = this._displayElement.querySelector('[data-segment="day"]');
537
- var monthSegment = this._displayElement.querySelector('[data-segment="month"]');
538
- var yearSegment = this._displayElement.querySelector('[data-segment="year"]');
539
- if (daySegment)
540
- daySegment.textContent = day < 10 ? "0".concat(day) : "".concat(day);
541
- if (monthSegment)
542
- monthSegment.textContent = month < 10 ? "0".concat(month) : "".concat(month);
543
- if (yearSegment)
544
- yearSegment.textContent = "".concat(year);
799
+ // Fire onOpen when dropdown opens
800
+ if (newState.isOpen && !oldState.isOpen) {
801
+ this._fireDatepickerEvent('onOpen', this);
545
802
  }
546
- else {
547
- // Simple display
548
- this._displayElement.textContent = formatDate(date, this._config.format, this._config);
803
+ // Fire onClose when dropdown closes
804
+ if (!newState.isOpen && oldState.isOpen) {
805
+ this._fireDatepickerEvent('onClose', this);
549
806
  }
550
807
  };
551
808
  /**
552
- * Update the display element for a date range
553
- *
554
- * @param startDate - The start date of the range
555
- * @param endDate - The end date of the range
809
+ * Centralized event firing system - safely dispatches events with error handling
556
810
  */
557
- KTDatepicker.prototype._updateRangeDisplayElement = function (startDate, endDate) {
558
- var _a;
559
- if (!this._displayElement)
560
- return;
561
- if (!startDate) {
562
- // If no date, show placeholder
563
- var placeholder = ((_a = this._dateInputElement) === null || _a === void 0 ? void 0 : _a.getAttribute('placeholder')) ||
564
- 'Select date range';
565
- this._displayElement.innerHTML = placeholderTemplate(placeholder);
566
- return;
811
+ KTDatepicker.prototype._fireDatepickerEvent = function (eventName) {
812
+ var args = [];
813
+ for (var _i = 1; _i < arguments.length; _i++) {
814
+ args[_i - 1] = arguments[_i];
567
815
  }
568
- if (this._useSegmentedDisplay) {
569
- // Update segmented range display
570
- // Start date segments
571
- var startDay = this._displayElement.querySelector('[data-segment="start-day"]');
572
- var startMonth = this._displayElement.querySelector('[data-segment="start-month"]');
573
- var startYear = this._displayElement.querySelector('[data-segment="start-year"]');
574
- if (startDay)
575
- startDay.textContent =
576
- startDate.getDate() < 10
577
- ? "0".concat(startDate.getDate())
578
- : "".concat(startDate.getDate());
579
- if (startMonth)
580
- startMonth.textContent =
581
- startDate.getMonth() + 1 < 10
582
- ? "0".concat(startDate.getMonth() + 1)
583
- : "".concat(startDate.getMonth() + 1);
584
- if (startYear)
585
- startYear.textContent = "".concat(startDate.getFullYear());
586
- // End date segments
587
- if (endDate) {
588
- var endDay = this._displayElement.querySelector('[data-segment="end-day"]');
589
- var endMonth = this._displayElement.querySelector('[data-segment="end-month"]');
590
- var endYear = this._displayElement.querySelector('[data-segment="end-year"]');
591
- if (endDay)
592
- endDay.textContent =
593
- endDate.getDate() < 10
594
- ? "0".concat(endDate.getDate())
595
- : "".concat(endDate.getDate());
596
- if (endMonth)
597
- endMonth.textContent =
598
- endDate.getMonth() + 1 < 10
599
- ? "0".concat(endDate.getMonth() + 1)
600
- : "".concat(endDate.getMonth() + 1);
601
- if (endYear)
602
- endYear.textContent = "".concat(endDate.getFullYear());
816
+ try {
817
+ var eventHandler = this._config[eventName];
818
+ if (typeof eventHandler === 'function') {
819
+ eventHandler.apply(void 0, args);
603
820
  }
604
821
  }
605
- else {
606
- // Simple display
607
- var displayText = formatDate(startDate, this._config.format, this._config);
608
- if (endDate) {
609
- var endFormatted = formatDate(endDate, this._config.format, this._config);
610
- displayText += "".concat(this._config.rangeSeparator).concat(endFormatted);
611
- }
612
- this._displayElement.textContent = displayText;
822
+ catch (error) {
823
+ // Don't let event handler errors break the datepicker
613
824
  }
614
825
  };
615
- /**
616
- * Handle input change events
617
- *
618
- * @param e - Input change event
619
- */
620
- KTDatepicker.prototype._handleInputChange = function (e) {
621
- var input = e.target;
622
- var inputValue = input.value.trim();
623
- if (!inputValue) {
624
- // Clear selection if input is empty
625
- this._state.setSelectedDate(null);
626
- return;
826
+ // --- Mode-specific helpers ---
827
+ /** Initialize single date from config */
828
+ KTDatepicker.prototype._initSingleDateFromConfig = function () {
829
+ if (this._config.value) {
830
+ var date = new Date(this._config.value);
831
+ this._unifiedStateManager.setSelectedDate(date, 'config');
832
+ this._unifiedStateManager.setCurrentDate(date, 'config');
627
833
  }
628
- if (this._config.range) {
629
- // Handle range input
630
- var rangeParts = inputValue.split(this._config.rangeSeparator);
631
- if (rangeParts.length === 2) {
632
- var startDate = parseDate(rangeParts[0].trim(), this._config.format, this._config);
633
- var endDate = parseDate(rangeParts[1].trim(), this._config.format, this._config);
634
- // Validate dates are within min/max constraints
635
- if (startDate && isDateDisabled(startDate, this._config)) {
636
- console.log('Start date from input is outside allowed range:', startDate.toISOString());
637
- return;
638
- }
639
- if (endDate && isDateDisabled(endDate, this._config)) {
640
- console.log('End date from input is outside allowed range:', endDate.toISOString());
641
- return;
834
+ };
835
+ /** Initialize range from config */
836
+ KTDatepicker.prototype._initRangeFromConfig = function () {
837
+ if (this._config.valueRange) {
838
+ var start = this._config.valueRange.start ? new Date(this._config.valueRange.start) : null;
839
+ var end = this._config.valueRange.end ? new Date(this._config.valueRange.end) : null;
840
+ this._unifiedStateManager.setSelectedRange({ start: start, end: end }, 'config');
841
+ }
842
+ };
843
+ /** Initialize multi-date from config */
844
+ KTDatepicker.prototype._initMultiDateFromConfig = function () {
845
+ if (Array.isArray(this._config.values)) {
846
+ var dates = this._config.values.map(function (v) { return new Date(v); });
847
+ this._unifiedStateManager.setSelectedDates(dates, 'config');
848
+ }
849
+ };
850
+ /** Format single date for input */
851
+ KTDatepicker.prototype._formatSingleDate = function (date) {
852
+ if (!date)
853
+ return '';
854
+ // If time is enabled, include time in the format
855
+ if (this._config.enableTime) {
856
+ if (this._config.format && typeof this._config.format === 'string') {
857
+ // Check if format already includes time tokens
858
+ var hasTimeTokens = /[Hhms]/.test(this._config.format);
859
+ if (hasTimeTokens) {
860
+ return formatDate(date, this._config.format);
642
861
  }
643
- if (startDate && endDate) {
644
- this.setDateRange(startDate, endDate);
862
+ else {
863
+ // Add time format based on granularity and format
864
+ var timeFormat = this._getTimeFormat();
865
+ var dateFormat = this._config.format;
866
+ return formatDate(date, "".concat(dateFormat, " ").concat(timeFormat));
645
867
  }
646
868
  }
647
- else if (rangeParts.length === 1) {
648
- var singleDate = parseDate(rangeParts[0].trim(), this._config.format, this._config);
649
- // Validate date is within min/max constraints
650
- if (singleDate && isDateDisabled(singleDate, this._config)) {
651
- console.log('Date from input is outside allowed range:', singleDate.toISOString());
652
- return;
653
- }
654
- if (singleDate) {
655
- this.setDateRange(singleDate, null);
656
- }
869
+ else {
870
+ // Default format with time
871
+ var timeFormat = this._getTimeFormat();
872
+ return "".concat(date.toLocaleDateString(this._config.locale || 'en-US'), " ").concat(formatDate(date, timeFormat));
657
873
  }
658
874
  }
659
875
  else {
660
- // Handle single date input
661
- var parsedDate = parseDate(inputValue, this._config.format, this._config);
662
- // Validate date is within min/max constraints
663
- if (parsedDate && isDateDisabled(parsedDate, this._config)) {
664
- console.log('Date from input is outside allowed range:', parsedDate.toISOString());
665
- return;
876
+ // Time not enabled, use original logic
877
+ if (this._config.format && typeof this._config.format === 'string') {
878
+ return formatDate(date, this._config.format);
666
879
  }
667
- if (parsedDate) {
668
- this.setDate(parsedDate);
880
+ else if (this._config.locale) {
881
+ return date.toLocaleDateString(this._config.locale);
882
+ }
883
+ else {
884
+ return date.toLocaleDateString();
669
885
  }
670
886
  }
671
887
  };
672
888
  /**
673
- * Initialize with default values from input
889
+ * Get time format string based on granularity and time format
890
+ * @returns Time format string
674
891
  */
675
- KTDatepicker.prototype._initializeDefaultValues = function () {
676
- // Set min and max dates from attributes if they exist
677
- var minDateAttr = this._element.getAttribute('data-kt-datepicker-min-date');
678
- var maxDateAttr = this._element.getAttribute('data-kt-datepicker-max-date');
679
- if (minDateAttr) {
680
- var minDate = parseDate(minDateAttr, this._config.format, this._config);
681
- if (minDate) {
682
- this.setMinDate(minDate);
683
- }
892
+ KTDatepicker.prototype._getTimeFormat = function () {
893
+ var granularity = this._unifiedStateManager.getState().timeGranularity || 'minute';
894
+ var timeFormat = this._config.timeFormat || '24h';
895
+ switch (granularity) {
896
+ case 'hour':
897
+ return timeFormat === '12h' ? 'HH a' : 'HH';
898
+ case 'minute':
899
+ return timeFormat === '12h' ? 'HH:mm a' : 'HH:mm';
900
+ case 'second':
901
+ return timeFormat === '12h' ? 'HH:mm:ss a' : 'HH:mm:ss';
902
+ default:
903
+ return timeFormat === '12h' ? 'HH:mm a' : 'HH:mm';
684
904
  }
685
- if (maxDateAttr) {
686
- var maxDate = parseDate(maxDateAttr, this._config.format, this._config);
687
- if (maxDate) {
688
- this.setMaxDate(maxDate);
689
- }
905
+ };
906
+ /** Format range for input */
907
+ KTDatepicker.prototype._formatRange = function (start, end) {
908
+ if (start && end) {
909
+ return "".concat(this._formatSingleDate(start), " \u2013 ").concat(this._formatSingleDate(end));
690
910
  }
691
- // Check for default value in main input
692
- if (this._dateInputElement && this._dateInputElement.value) {
693
- this._handleInputChange({
694
- target: this._dateInputElement,
695
- });
911
+ else if (start) {
912
+ return this._formatSingleDate(start);
696
913
  }
697
- // Check for default values in range inputs
698
- else if (this._config.range &&
699
- this._startDateInputElement &&
700
- this._startDateInputElement.value) {
701
- var startDate = parseDate(this._startDateInputElement.value, this._config.format, this._config);
702
- var endDate = null;
703
- if (this._endDateInputElement && this._endDateInputElement.value) {
704
- endDate = parseDate(this._endDateInputElement.value, this._config.format, this._config);
914
+ return '';
915
+ };
916
+ /** Format multi-date for input */
917
+ KTDatepicker.prototype._formatMultiDate = function (dates) {
918
+ var _this = this;
919
+ return dates.map(function (d) { return _this._formatSingleDate(d); }).join(', ');
920
+ };
921
+ /** Select a single date */
922
+ KTDatepicker.prototype._selectSingleDate = function (date) {
923
+ // Preserve time if time is enabled
924
+ if (this._config.enableTime) {
925
+ var timeToUse = this._unifiedStateManager.getState().selectedTime;
926
+ // If no selectedTime, try to extract from current selectedDate
927
+ if (!timeToUse) {
928
+ var currentSelectedDate = this._unifiedStateManager.getState().selectedDate;
929
+ if (currentSelectedDate) {
930
+ timeToUse = dateToTimeState(currentSelectedDate);
931
+ }
705
932
  }
706
- if (startDate) {
707
- this.setDateRange(startDate, endDate);
933
+ // If still no time, default to current time
934
+ if (!timeToUse) {
935
+ timeToUse = dateToTimeState(new Date());
708
936
  }
937
+ var dateWithTime = applyTimeToDate(date, timeToUse);
938
+ this._unifiedStateManager.setSelectedDate(dateWithTime, 'calendar');
939
+ }
940
+ else {
941
+ this._unifiedStateManager.setSelectedDate(date, 'calendar');
942
+ }
943
+ // Dispatch change event
944
+ if (this._input) {
945
+ var evt = new Event('change', { bubbles: true });
946
+ this._input.dispatchEvent(evt);
709
947
  }
710
948
  };
711
949
  /**
712
- * ========================================================================
713
- * Public API
714
- * ========================================================================
715
- */
716
- /**
717
- * Get the currently selected date
718
- *
719
- * @returns Selected date, null if no selection, or date range object
950
+ * Select a range date (calendar click or segmented input change)
951
+ * Updates both segmented inputs and internal state.
720
952
  */
721
- KTDatepicker.prototype.getDate = function () {
722
- var state = this._state.getState();
723
- var config = this._state.getConfig();
724
- if (config.range) {
725
- return state.selectedDateRange || { startDate: null, endDate: null };
953
+ KTDatepicker.prototype._selectRangeDate = function (date) {
954
+ var _a;
955
+ var currentState = this._unifiedStateManager.getState();
956
+ // First, let updateRangeSelection handle the date logic (it normalizes to midnight for comparison)
957
+ var newRange = updateRangeSelection(currentState.selectedRange, date);
958
+ // Then, if time is enabled, apply time to both start and end dates
959
+ if (this._config.enableTime) {
960
+ var timeToUse = currentState.selectedTime;
961
+ // If no selectedTime, try to extract from current selectedDate or range dates
962
+ if (!timeToUse) {
963
+ // Check if we have a start date with time
964
+ if ((_a = currentState.selectedRange) === null || _a === void 0 ? void 0 : _a.start) {
965
+ timeToUse = dateToTimeState(currentState.selectedRange.start);
966
+ }
967
+ else if (currentState.selectedDate) {
968
+ timeToUse = dateToTimeState(currentState.selectedDate);
969
+ }
970
+ }
971
+ // If still no time, default to current time
972
+ if (!timeToUse) {
973
+ timeToUse = dateToTimeState(new Date());
974
+ }
975
+ // Apply time to both start and end dates
976
+ var rangeWithTime = {
977
+ start: newRange.start ? applyTimeToDate(newRange.start, timeToUse) : null,
978
+ end: newRange.end ? applyTimeToDate(newRange.end, timeToUse) : null
979
+ };
980
+ this._unifiedStateManager.setSelectedRange(rangeWithTime, 'calendar');
726
981
  }
727
982
  else {
728
- return state.selectedDate;
983
+ this._unifiedStateManager.setSelectedRange(newRange, 'calendar');
984
+ }
985
+ if (this._input) {
986
+ var evt = new Event('change', { bubbles: true });
987
+ this._input.dispatchEvent(evt);
729
988
  }
730
989
  };
731
- /**
732
- * Set the selected date
733
- *
734
- * @param date - Date to select or null to clear selection
735
- */
736
- KTDatepicker.prototype.setDate = function (date) {
737
- // Skip if the date is disabled (outside min/max range)
738
- if (date && isDateDisabled(date, this._config)) {
739
- console.log('Date is disabled in setDate, ignoring selection:', date.toISOString());
740
- return;
990
+ /** Select a multi-date */
991
+ KTDatepicker.prototype._selectMultiDate = function (date) {
992
+ var currentState = this._unifiedStateManager.getState();
993
+ var currentDates = currentState.selectedDates || [];
994
+ // Preserve time if time is enabled
995
+ var dateToSelect = date;
996
+ if (this._config.enableTime) {
997
+ var timeToUse = currentState.selectedTime;
998
+ // If no selectedTime, try to extract from existing selected dates
999
+ if (!timeToUse && currentDates.length > 0) {
1000
+ timeToUse = dateToTimeState(currentDates[0]);
1001
+ }
1002
+ else if (!timeToUse && currentState.selectedDate) {
1003
+ timeToUse = dateToTimeState(currentState.selectedDate);
1004
+ }
1005
+ // If still no time, default to current time
1006
+ if (!timeToUse) {
1007
+ timeToUse = dateToTimeState(new Date());
1008
+ }
1009
+ dateToSelect = applyTimeToDate(date, timeToUse);
741
1010
  }
742
- this._state.setSelectedDate(date);
743
- if (date) {
744
- this._state.setCurrentDate(date);
1011
+ var exists = currentDates.some(function (d) { return d.getTime() === dateToSelect.getTime(); });
1012
+ var newDates;
1013
+ if (exists) {
1014
+ newDates = currentDates.filter(function (d) { return d.getTime() !== dateToSelect.getTime(); });
745
1015
  }
746
- // Update the display
747
- this._updateDisplayElement(date);
748
- // Update hidden input
749
- if (this._dateInputElement && date) {
750
- this._dateInputElement.value = formatDate(date, this._config.format, this._config);
751
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
1016
+ else {
1017
+ newDates = __spreadArray(__spreadArray([], currentDates, true), [dateToSelect], false);
752
1018
  }
753
- else if (this._dateInputElement) {
754
- this._dateInputElement.value = '';
755
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
1019
+ this._unifiedStateManager.setSelectedDates(newDates, 'calendar');
1020
+ if (this._input) {
1021
+ var evt = new Event('change', { bubbles: true });
1022
+ this._input.dispatchEvent(evt);
756
1023
  }
757
1024
  };
758
- /**
759
- * Get the currently selected date range
760
- *
761
- * @returns Selected date range or null if no selection
762
- */
763
- KTDatepicker.prototype.getDateRange = function () {
764
- var state = this._state.getState();
765
- return state.selectedDateRange;
1025
+ KTDatepicker.prototype._init = function (element) {
1026
+ this._element = element;
1027
+ // Find or assign the input
1028
+ this._input = this._element.querySelector('input[data-kt-datepicker-input]');
1029
+ if (!this._input) {
1030
+ // Fallback: find the first input and add the attribute
1031
+ var firstInput = this._element.querySelector('input');
1032
+ if (firstInput) {
1033
+ firstInput.setAttribute('data-kt-datepicker-input', '');
1034
+ this._input = firstInput;
1035
+ }
1036
+ else {
1037
+ // If no input exists, create one and append
1038
+ var newInput = document.createElement('input');
1039
+ newInput.setAttribute('data-kt-datepicker-input', '');
1040
+ this._element.appendChild(newInput);
1041
+ this._input = newInput;
1042
+ }
1043
+ }
766
1044
  };
767
1045
  /**
768
- * Set the selected date range
769
- *
770
- * @param start - Start date of the range
771
- * @param end - End date of the range
1046
+ * Build config by merging defaults and user config
772
1047
  */
773
- KTDatepicker.prototype.setDateRange = function (start, end) {
774
- var _a;
775
- var state = this._state.getState();
776
- // Ensure we're in range mode
777
- if (!this._config.range) {
778
- console.warn('Cannot set date range when range mode is disabled');
779
- return;
1048
+ KTDatepicker.prototype._buildConfig = function (config) {
1049
+ // First call parent to read data attributes
1050
+ _super.prototype._buildConfig.call(this, config);
1051
+ // Merge templates separately to ensure correct type
1052
+ var mergedTemplates = __assign(__assign(__assign({}, defaultTemplates), (this._config.templates || {})), (this._userTemplates || {}));
1053
+ // Determine closeOnSelect default based on mode and requirements
1054
+ var closeOnSelect;
1055
+ if (typeof this._config.closeOnSelect !== 'undefined') {
1056
+ // User explicitly set closeOnSelect, respect their choice
1057
+ closeOnSelect = this._config.closeOnSelect;
780
1058
  }
781
- // Validate start and end dates are within min/max range
782
- if (start && isDateDisabled(start, this._config)) {
783
- console.log('Start date is disabled in setDateRange, ignoring selection:', start.toISOString());
784
- return;
1059
+ else if (this._config.enableTime) {
1060
+ // Time-enabled: never close on date selection
1061
+ closeOnSelect = false;
785
1062
  }
786
- if (end && isDateDisabled(end, this._config)) {
787
- console.log('End date is disabled in setDateRange, ignoring selection:', end.toISOString());
788
- return;
1063
+ else if (this._config.range) {
1064
+ // Range mode: handle clicks inside dropdown
1065
+ closeOnSelect = false;
1066
+ }
1067
+ else if (this._config.multiDate) {
1068
+ // Multi-date mode: don't close on individual selections
1069
+ closeOnSelect = false;
1070
+ }
1071
+ else {
1072
+ // Single date only: close on date click
1073
+ closeOnSelect = true;
1074
+ }
1075
+ // Merge with data attributes and passed config
1076
+ // Important: data attributes (this._config) must come after defaults to override them
1077
+ this._config = __assign(__assign(__assign(__assign({}, defaultDatepickerConfig), this._config), (config || {})), { templates: mergedTemplates, closeOnSelect: closeOnSelect });
1078
+ // Initialize event manager
1079
+ this._eventManager = new EventManager();
1080
+ // Initialize focus manager for keyboard navigation
1081
+ this._focusManager = new FocusManager({
1082
+ enableFocusTrapping: true,
1083
+ enableFocusRestoration: true,
1084
+ enableKeyboardNavigation: true,
1085
+ enableDebugging: this._config.debug || false
1086
+ });
1087
+ };
1088
+ /**
1089
+ * Public method to set/override templates at runtime (supports string or function)
1090
+ */
1091
+ KTDatepicker.prototype.setTemplates = function (templates) {
1092
+ this._userTemplates = __assign(__assign({}, this._userTemplates), templates);
1093
+ this._templateSet = getTemplateStrings(this._config);
1094
+ this._templateRenderer.updateTemplates(this._templateSet);
1095
+ this._render();
1096
+ };
1097
+ /**
1098
+ * Render the main container and set this._container
1099
+ */
1100
+ KTDatepicker.prototype._renderContainer = function () {
1101
+ var containerEl = this._templateRenderer.renderTemplateToElement({
1102
+ templateKey: 'container',
1103
+ data: {},
1104
+ configClasses: this._config.classes
1105
+ });
1106
+ this._container = containerEl;
1107
+ return containerEl;
1108
+ };
1109
+ /**
1110
+ * Render the input wrapper and calendar button, move/create input element
1111
+ * In range mode, renders two segmented inputs (start/end) using the segmentedDateRangeInput template.
1112
+ */
1113
+ KTDatepicker.prototype._renderInputWrapper = function (calendarButtonHtml) {
1114
+ var _this = this;
1115
+ var inputWrapperTpl = this._templateSet.inputWrapper || defaultTemplates.inputWrapper;
1116
+ // Set input to hidden instead of removing it, so it remains in DOM for form submission
1117
+ if (this._input) {
1118
+ this._input.type = 'hidden';
1119
+ // Remove any visible styling classes that might interfere
1120
+ this._input.classList.remove('hidden');
1121
+ }
1122
+ if (this._config.range) {
1123
+ var rangeTpl = this._templateSet.segmentedDateRangeInput || defaultTemplates.segmentedDateRangeInput;
1124
+ var _a = renderRangeSegmentedInputUI(inputWrapperTpl, rangeTpl, calendarButtonHtml, this._config), inputWrapperEl_1 = _a.inputWrapperEl, startContainer = _a.startContainer, endContainer = _a.endContainer;
1125
+ instantiateRangeSegmentedInputs(startContainer, endContainer, this._unifiedStateManager.getState(), this._config, function (date) {
1126
+ var _a;
1127
+ var end = ((_a = _this._unifiedStateManager.getState().selectedRange) === null || _a === void 0 ? void 0 : _a.end) || null;
1128
+ var newEnd = end;
1129
+ if (end && date > end)
1130
+ newEnd = null;
1131
+ _this._unifiedStateManager.updateState({ selectedRange: { start: date, end: newEnd } }, 'range-selection');
1132
+ _this._render();
1133
+ }, function (date) {
1134
+ var _a;
1135
+ var start = ((_a = _this._unifiedStateManager.getState().selectedRange) === null || _a === void 0 ? void 0 : _a.start) || null;
1136
+ var newStart = start;
1137
+ if (start && date < start)
1138
+ newStart = null;
1139
+ _this._unifiedStateManager.updateState({ selectedRange: { start: newStart, end: date } }, 'range-selection');
1140
+ _this._render();
1141
+ });
1142
+ return inputWrapperEl_1;
1143
+ }
1144
+ // Single-date mode
1145
+ var inputWrapperEl = renderSingleSegmentedInputUI(inputWrapperTpl, calendarButtonHtml, this._config);
1146
+ // Find the segmented input container that was rendered by the template system
1147
+ var segmentedInputContainer = inputWrapperEl.querySelector('[data-kt-datepicker-segmented-input]');
1148
+ instantiateSingleSegmentedInput(segmentedInputContainer, this._unifiedStateManager.getState(), this._config, function (date) {
1149
+ _this.setDate(date);
1150
+ });
1151
+ return inputWrapperEl;
1152
+ };
1153
+ /**
1154
+ * Bind event listener to the calendar button and input wrapper
1155
+ */
1156
+ KTDatepicker.prototype._bindCalendarButtonEvent = function (inputWrapperEl) {
1157
+ var _this = this;
1158
+ var buttonEl = inputWrapperEl.querySelector('button[data-kt-datepicker-calendar-btn]');
1159
+ if (buttonEl && buttonEl instanceof HTMLButtonElement) {
1160
+ buttonEl.type = 'button';
1161
+ buttonEl.setAttribute('aria-label', this._config.calendarButtonAriaLabel || 'Open calendar');
1162
+ buttonEl.addEventListener('click', function (e) {
1163
+ e.preventDefault();
1164
+ e.stopPropagation();
1165
+ if (_this._config.disabled || buttonEl.hasAttribute('disabled')) {
1166
+ return;
1167
+ }
1168
+ _this.toggle();
1169
+ });
1170
+ }
1171
+ // Add click handler to entire input wrapper for better UX
1172
+ inputWrapperEl.addEventListener('click', function (e) {
1173
+ // Don't handle if disabled
1174
+ if (_this._config.disabled) {
1175
+ return;
1176
+ }
1177
+ // Don't handle if target is a focusable input element (segmented input)
1178
+ var target = e.target;
1179
+ if (target.hasAttribute('contenteditable') || target.hasAttribute('data-segment')) {
1180
+ return;
1181
+ }
1182
+ // Don't handle if already handled by button click
1183
+ if (target === buttonEl || (buttonEl === null || buttonEl === void 0 ? void 0 : buttonEl.contains(target))) {
1184
+ return;
1185
+ }
1186
+ // Open the datepicker
1187
+ _this.open();
1188
+ });
1189
+ };
1190
+ /**
1191
+ * Render the dropdown container from template
1192
+ */
1193
+ KTDatepicker.prototype._renderDropdown = function () {
1194
+ var dropdownEl = this._templateRenderer.renderTemplateToElement({
1195
+ templateKey: 'dropdown',
1196
+ data: {},
1197
+ configClasses: this._config.classes
1198
+ });
1199
+ dropdownEl.setAttribute('data-kt-datepicker-dropdown', '');
1200
+ // Add instance association for better identification
1201
+ if (this._instanceId) {
1202
+ dropdownEl.setAttribute('data-kt-datepicker-instance-id', this._instanceId);
1203
+ }
1204
+ if (!this._unifiedStateManager.isDropdownOpen()) {
1205
+ dropdownEl.classList.add('hidden');
1206
+ }
1207
+ return dropdownEl;
1208
+ };
1209
+ /**
1210
+ * Render header, calendar, and footer into the dropdown element
1211
+ */
1212
+ KTDatepicker.prototype._renderDropdownContent = function (dropdownEl) {
1213
+ var _this = this;
1214
+ var _a;
1215
+ var visibleMonths = (_a = this._config.visibleMonths) !== null && _a !== void 0 ? _a : 1;
1216
+ if (visibleMonths === 1) {
1217
+ this._renderSingleMonth(dropdownEl);
1218
+ }
1219
+ else {
1220
+ this._renderMultiMonth(dropdownEl, visibleMonths);
1221
+ }
1222
+ // --- Render footer using template-driven buttons (conditional by mode) ---
1223
+ var isRange = !!this._config.range;
1224
+ var isMultiDate = !!this._config.multiDate;
1225
+ var showFooter = isRange || isMultiDate;
1226
+ if (showFooter) {
1227
+ var todayButtonHtml = undefined;
1228
+ var clearButtonHtml = undefined;
1229
+ var applyButtonHtml = undefined;
1230
+ var todayButtonTpl = this._templateSet.todayButton || defaultTemplates.todayButton;
1231
+ var clearButtonTpl = this._templateSet.clearButton || defaultTemplates.clearButton;
1232
+ var applyButtonTpl = this._templateSet.applyButton || defaultTemplates.applyButton;
1233
+ // Only show Today/Clear if explicitly enabled in config/templates
1234
+ if (this._config.showTodayButton) {
1235
+ todayButtonHtml = typeof todayButtonTpl === 'function' ? todayButtonTpl({}) : todayButtonTpl;
1236
+ }
1237
+ if (this._config.showClearButton) {
1238
+ clearButtonHtml = typeof clearButtonTpl === 'function' ? clearButtonTpl({}) : clearButtonTpl;
1239
+ }
1240
+ // Always show Apply in range/multi-date mode
1241
+ applyButtonHtml = typeof applyButtonTpl === 'function' ? applyButtonTpl({}) : applyButtonTpl;
1242
+ var footer = renderFooter(this._templateSet.footer, { todayButton: todayButtonHtml, clearButton: clearButtonHtml, applyButton: applyButtonHtml }, this._onToday, this._onClear, this._onApply);
1243
+ dropdownEl.appendChild(footer);
1244
+ }
1245
+ // --- Render time picker if enabled ---
1246
+ var currentState = this._unifiedStateManager.getState();
1247
+ if (this._config.enableTime && currentState.selectedTime) {
1248
+ var timePickerContainer = this._templateRenderer.renderTemplateToElement({
1249
+ templateKey: 'timePickerWrapper',
1250
+ data: {},
1251
+ configClasses: this._config.classes
1252
+ });
1253
+ timePickerContainer.setAttribute('data-kt-datepicker-time-container', '');
1254
+ // Store the time picker renderer result
1255
+ this._timePickerRenderer = renderTimePicker(timePickerContainer, {
1256
+ time: currentState.selectedTime,
1257
+ granularity: currentState.timeGranularity,
1258
+ format: this._config.timeFormat || '24h',
1259
+ minTime: this._config.minTime,
1260
+ maxTime: this._config.maxTime,
1261
+ timeStep: this._config.timeStep || 1,
1262
+ disabled: !!this._config.disabled,
1263
+ onChange: function (newTime) {
1264
+ // Update unified state manager
1265
+ _this._unifiedStateManager.updateState({
1266
+ selectedTime: newTime
1267
+ }, 'time-picker');
1268
+ // Apply time to selected date if exists
1269
+ var updatedState = _this._unifiedStateManager.getState();
1270
+ if (updatedState.selectedDate) {
1271
+ var dateWithTime = applyTimeToDate(updatedState.selectedDate, newTime);
1272
+ _this._unifiedStateManager.updateState({
1273
+ selectedDate: dateWithTime
1274
+ }, 'time-picker');
1275
+ }
1276
+ // Update the time picker renderer with the new state
1277
+ if (_this._timePickerRenderer) {
1278
+ _this._timePickerRenderer.update(newTime);
1279
+ }
1280
+ },
1281
+ templates: this._templateSet
1282
+ });
1283
+ dropdownEl.appendChild(timePickerContainer);
1284
+ }
1285
+ };
1286
+ /**
1287
+ * Render single month calendar
1288
+ */
1289
+ KTDatepicker.prototype._renderSingleMonth = function (dropdownEl) {
1290
+ var _this = this;
1291
+ var prevButtonHtml;
1292
+ var nextButtonHtml;
1293
+ var prevButtonTpl = this._templateSet.prevButton || defaultTemplates.prevButton;
1294
+ var nextButtonTpl = this._templateSet.nextButton || defaultTemplates.nextButton;
1295
+ prevButtonHtml = typeof prevButtonTpl === 'function' ? prevButtonTpl({}) : prevButtonTpl;
1296
+ nextButtonHtml = typeof nextButtonTpl === 'function' ? nextButtonTpl({}) : nextButtonTpl;
1297
+ var currentState = this._unifiedStateManager.getState();
1298
+ var header = renderHeader(this._templateSet.header, {
1299
+ month: currentState.currentDate.toLocaleString(this._config.locale, { month: 'long' }),
1300
+ year: currentState.currentDate.getFullYear(),
1301
+ prevButton: prevButtonHtml,
1302
+ nextButton: nextButtonHtml,
1303
+ }, function (e) { e.stopPropagation(); _this._changeMonth(-1); }, function (e) { e.stopPropagation(); _this._changeMonth(1); });
1304
+ dropdownEl.appendChild(header);
1305
+ var dayClickHandler = function (day) {
1306
+ _this.setDate(day);
1307
+ };
1308
+ var calendar = renderCalendar(this._templateSet.dayCell, this._getCalendarDays(currentState.currentDate), currentState.currentDate, currentState.selectedDate, dayClickHandler, this._config.locale, this._config.range ? currentState.selectedRange : undefined, this._config.multiDate ? currentState.selectedDates : undefined);
1309
+ dropdownEl.appendChild(calendar);
1310
+ };
1311
+ /**
1312
+ * Get array of dates for multi-month display
1313
+ * @param baseDate - The base date to calculate months from
1314
+ * @param count - Number of months to generate
1315
+ * @returns Array of dates representing the first day of each month
1316
+ */
1317
+ KTDatepicker.prototype._getMultiMonthDates = function (baseDate, count) {
1318
+ var dates = [];
1319
+ for (var i = 0; i < count; i++) {
1320
+ var monthDate = new Date(baseDate.getFullYear(), baseDate.getMonth() + i, 1);
1321
+ dates.push(monthDate);
1322
+ }
1323
+ return dates;
1324
+ };
1325
+ /**
1326
+ * Render a single calendar month for multi-month display
1327
+ * @param monthDate - The date representing the month to render
1328
+ * @param index - Index of this month in the multi-month display
1329
+ * @param totalMonths - Total number of months being displayed
1330
+ * @returns HTMLElement containing the rendered month
1331
+ */
1332
+ KTDatepicker.prototype._renderMultiMonthCalendar = function (monthDate, index, totalMonths) {
1333
+ var _this = this;
1334
+ // Navigation buttons: only first gets prev, only last gets next
1335
+ var prevButtonHtml = '';
1336
+ var nextButtonHtml = '';
1337
+ if (index === 0) {
1338
+ var prevButtonTpl = this._templateSet.prevButton || defaultTemplates.prevButton;
1339
+ prevButtonHtml = typeof prevButtonTpl === 'function' ? prevButtonTpl({}) : prevButtonTpl;
1340
+ }
1341
+ if (index === totalMonths - 1) {
1342
+ var nextButtonTpl = this._templateSet.nextButton || defaultTemplates.nextButton;
1343
+ nextButtonHtml = typeof nextButtonTpl === 'function' ? nextButtonTpl({}) : nextButtonTpl;
1344
+ }
1345
+ var header = renderHeader(this._templateSet.header, {
1346
+ month: monthDate.toLocaleString(this._config.locale, { month: 'long' }),
1347
+ year: monthDate.getFullYear(),
1348
+ prevButton: prevButtonHtml,
1349
+ nextButton: nextButtonHtml,
1350
+ }, function (e) { e.stopPropagation(); _this._changeMonth(-1); }, function (e) { e.stopPropagation(); _this._changeMonth(1); });
1351
+ var currentState = this._unifiedStateManager.getState();
1352
+ var calendar = renderCalendar(this._templateSet.dayCell, this._getCalendarDays(monthDate), monthDate, currentState.selectedDate, function (day) { _this.setDate(day); }, this._config.locale, this._config.range ? currentState.selectedRange : undefined, this._config.multiDate ? currentState.selectedDates : undefined);
1353
+ // Create panel element and append header + calendar directly to preserve event listeners
1354
+ // Instead of converting to HTML strings which lose event listeners
1355
+ var panel = document.createElement('div');
1356
+ panel.setAttribute('data-kt-datepicker-panel', '');
1357
+ // Apply default panel styling (flex flex-col gap-1.5 from CSS)
1358
+ panel.className = 'flex flex-col gap-1.5';
1359
+ // Append header and calendar directly (preserves event listeners)
1360
+ panel.appendChild(header);
1361
+ panel.appendChild(calendar);
1362
+ return panel;
1363
+ };
1364
+ /**
1365
+ * Render multi-month calendar
1366
+ */
1367
+ KTDatepicker.prototype._renderMultiMonth = function (dropdownEl, visibleMonths) {
1368
+ var _this = this;
1369
+ var _a;
1370
+ var currentState = this._unifiedStateManager.getState();
1371
+ var baseDate = new Date(currentState.currentDate);
1372
+ // Get all month dates for multi-month display
1373
+ var monthDates = this._getMultiMonthDates(baseDate, visibleMonths);
1374
+ // Create multi-month container element
1375
+ var multiMonthContainer = document.createElement('div');
1376
+ multiMonthContainer.setAttribute('data-kt-datepicker-multimonth-container', '');
1377
+ // Apply classes: flex flex-col md:flex-row gap-4
1378
+ multiMonthContainer.className = 'flex flex-col md:flex-row gap-4';
1379
+ if ((_a = this._config.classes) === null || _a === void 0 ? void 0 : _a.multiMonthContainer) {
1380
+ multiMonthContainer.className += ' ' + this._config.classes.multiMonthContainer;
1381
+ }
1382
+ // Render each month panel and append directly (preserves event listeners)
1383
+ monthDates.forEach(function (monthDate, index) {
1384
+ var panel = _this._renderMultiMonthCalendar(monthDate, index, visibleMonths);
1385
+ multiMonthContainer.appendChild(panel);
1386
+ });
1387
+ dropdownEl.appendChild(multiMonthContainer);
1388
+ };
1389
+ /**
1390
+ * Render the datepicker UI using templates
1391
+ */
1392
+ KTDatepicker.prototype._render = function () {
1393
+ var _this = this;
1394
+ // Performance marker: Start render
1395
+ if (typeof performance !== 'undefined' && this._config.debug) {
1396
+ performance.mark('datepicker-render-start');
1397
+ }
1398
+ // Store current state before rendering
1399
+ var wasOpen = this._unifiedStateManager.isDropdownOpen();
1400
+ var selectedDate = this._unifiedStateManager.getState().selectedDate;
1401
+ var selectedRange = this._unifiedStateManager.getState().selectedRange;
1402
+ var selectedDates = this._unifiedStateManager.getState().selectedDates;
1403
+ // Remove any previous container
1404
+ if (this._container && this._container.parentNode) {
1405
+ this._container.parentNode.removeChild(this._container);
1406
+ }
1407
+ // Render main container from template
1408
+ var containerEl = this._renderContainer();
1409
+ // Remove any previous input wrapper
1410
+ var existingWrapper = this._element.querySelector('[data-kt-datepicker-input-wrapper]');
1411
+ if (existingWrapper && existingWrapper.parentNode) {
1412
+ existingWrapper.parentNode.removeChild(existingWrapper);
1413
+ }
1414
+ // Render calendar button from template
1415
+ var calendarButtonTpl = this._templateSet.calendarButton || defaultTemplates.calendarButton;
1416
+ var calendarButtonHtml;
1417
+ if (typeof calendarButtonTpl === 'function') {
1418
+ var classData = mergeClassData('calendarButton', { ariaLabel: this._config.calendarButtonAriaLabel || 'Open calendar' }, this._config.classes);
1419
+ calendarButtonHtml = calendarButtonTpl(classData);
1420
+ }
1421
+ else {
1422
+ var classData = mergeClassData('calendarButton', { ariaLabel: this._config.calendarButtonAriaLabel || 'Open calendar' }, this._config.classes);
1423
+ calendarButtonHtml = renderTemplateString(calendarButtonTpl, classData);
1424
+ }
1425
+ // Render input wrapper and calendar button from template
1426
+ var inputWrapperEl = this._renderInputWrapper(calendarButtonHtml);
1427
+ // Attach calendar button event listener
1428
+ this._bindCalendarButtonEvent(inputWrapperEl);
1429
+ // Insert wrapper at the start of the element
1430
+ this._element.insertBefore(inputWrapperEl, this._element.firstChild);
1431
+ // --- Dropdown rendering and attachment ---
1432
+ // Remove any previous dropdown
1433
+ var existingDropdown = this._element.querySelector('[data-kt-datepicker-dropdown]');
1434
+ if (existingDropdown && existingDropdown.parentNode) {
1435
+ existingDropdown.parentNode.removeChild(existingDropdown);
1436
+ }
1437
+ // Render dropdown from template system (never inline)
1438
+ var dropdownEl = this._renderDropdown();
1439
+ this._renderDropdownContent(dropdownEl);
1440
+ this._attachDropdown(inputWrapperEl, dropdownEl);
1441
+ // Restore state
1442
+ this._unifiedStateManager.updateState({
1443
+ selectedDate: selectedDate,
1444
+ selectedRange: selectedRange,
1445
+ selectedDates: selectedDates
1446
+ }, 'render-restore');
1447
+ // Restore open state
1448
+ if (wasOpen) {
1449
+ this._unifiedStateManager.setDropdownOpen(true, 'render-restore');
1450
+ // The dropdown module will automatically open via observer pattern
1451
+ }
1452
+ this._updatePlaceholder();
1453
+ this._updateDisabledState();
1454
+ // Enforce min/max dates after rendering
1455
+ // Use requestAnimationFrame to align with browser render cycle
1456
+ requestAnimationFrame(function () {
1457
+ _this._enforceMinMaxDates();
1458
+ });
1459
+ // Attach keyboard event listeners
1460
+ if (this._input) {
1461
+ this._eventManager.removeListener(this._input, 'keydown', this._onKeyDown);
1462
+ this._eventManager.addListener(this._input, 'keydown', this._onKeyDown);
1463
+ }
1464
+ if (dropdownEl) {
1465
+ this._eventManager.removeListener(dropdownEl, 'keydown', this._onKeyDown);
1466
+ this._eventManager.addListener(dropdownEl, 'keydown', this._onKeyDown);
1467
+ }
1468
+ // Ensure live region exists
1469
+ var liveRegion = this._element.querySelector('[data-kt-datepicker-live]');
1470
+ if (!liveRegion) {
1471
+ liveRegion = this._templateRenderer.renderTemplateToElement({
1472
+ templateKey: 'liveRegion',
1473
+ data: {},
1474
+ configClasses: this._config.classes
1475
+ });
1476
+ this._element.appendChild(liveRegion);
1477
+ }
1478
+ // Initialize DOM element cache after rendering
1479
+ this._initializeElementCache();
1480
+ // Performance marker: End render
1481
+ if (typeof performance !== 'undefined' && this._config.debug) {
1482
+ performance.mark('datepicker-render-end');
1483
+ performance.measure('datepicker-render', 'datepicker-render-start', 'datepicker-render-end');
1484
+ }
1485
+ };
1486
+ /**
1487
+ * Attach the dropdown after the input wrapper
1488
+ */
1489
+ KTDatepicker.prototype._attachDropdown = function (inputWrapperEl, dropdownEl) {
1490
+ // Clean up existing dropdown module
1491
+ if (this._dropdownModule) {
1492
+ this._dropdownModule.dispose();
1493
+ }
1494
+ // Always attach dropdown element to DOM first
1495
+ if (inputWrapperEl.nextSibling) {
1496
+ this._element.insertBefore(dropdownEl, inputWrapperEl.nextSibling);
1497
+ }
1498
+ else {
1499
+ this._element.appendChild(dropdownEl);
1500
+ }
1501
+ // Find the toggle element (calendar button)
1502
+ var toggleElement = this._element.querySelector('button[data-kt-datepicker-calendar-btn]');
1503
+ if (toggleElement) {
1504
+ // Create new dropdown module
1505
+ this._dropdownModule = new KTDatepickerDropdown(this._element, toggleElement, dropdownEl, this._config);
1506
+ // Connect dropdown module to unified state manager
1507
+ if (this._dropdownModule) {
1508
+ this._dropdownModule.setUnifiedStateManager(this._unifiedStateManager);
1509
+ this._unifiedStateManager.subscribe(this._dropdownModule);
1510
+ }
1511
+ }
1512
+ };
1513
+ KTDatepicker.prototype._getCalendarDays = function (date) {
1514
+ var year = date.getFullYear();
1515
+ var month = date.getMonth();
1516
+ var firstDay = new Date(year, month, 1);
1517
+ var lastDay = new Date(year, month + 1, 0);
1518
+ var days = [];
1519
+ // Start from Sunday of the first week
1520
+ var start = new Date(firstDay);
1521
+ start.setDate(firstDay.getDate() - firstDay.getDay());
1522
+ // End at Saturday of the last week
1523
+ var end = new Date(lastDay);
1524
+ end.setDate(lastDay.getDate() + (6 - lastDay.getDay()));
1525
+ // Reuse date object by incrementing instead of creating new ones
1526
+ var currentDate = new Date(start);
1527
+ while (currentDate <= end) {
1528
+ days.push(new Date(currentDate)); // Create new Date instance for each day to avoid mutation issues
1529
+ currentDate.setDate(currentDate.getDate() + 1);
789
1530
  }
790
- // Reset range selection state
791
- this._state.getState().isRangeSelectionStart = true;
792
- // Set start date
793
- if (start) {
794
- if (!state.selectedDateRange) {
795
- state.selectedDateRange = { startDate: null, endDate: null };
1531
+ return days;
1532
+ };
1533
+ KTDatepicker.prototype._changeMonth = function (offset) {
1534
+ var _this = this;
1535
+ var currentState = this._unifiedStateManager.getState();
1536
+ var d = new Date(currentState.currentDate);
1537
+ d.setMonth(d.getMonth() + offset);
1538
+ // Update the unified state manager and wait for state to propagate
1539
+ this._unifiedStateManager.updateState({
1540
+ currentDate: d
1541
+ }, 'month-navigation');
1542
+ // Ensure state is fully updated before proceeding
1543
+ setTimeout(function () {
1544
+ // Force a state refresh to ensure we have the latest state
1545
+ var updatedState = _this._unifiedStateManager.getState();
1546
+ // Check if the state actually changed
1547
+ if (updatedState.currentDate.getMonth() === d.getMonth()) {
1548
+ // Use multi-month update if multiple months are visible
1549
+ if (_this._config.visibleMonths && _this._config.visibleMonths > 1) {
1550
+ _this._updateMultiMonthCalendarContent();
1551
+ }
1552
+ else {
1553
+ _this._updateCalendarContent();
1554
+ }
1555
+ }
1556
+ else {
1557
+ // Wait a bit more for state to propagate
1558
+ setTimeout(function () {
1559
+ if (_this._config.visibleMonths && _this._config.visibleMonths > 1) {
1560
+ _this._updateMultiMonthCalendarContent();
1561
+ }
1562
+ else {
1563
+ _this._updateCalendarContent();
1564
+ }
1565
+ }, 50);
796
1566
  }
797
- state.selectedDateRange.startDate = start;
798
- this._state.setCurrentDate(start);
799
- // Set end date if provided
800
- if (end) {
801
- state.selectedDateRange.endDate = end;
1567
+ }, 10);
1568
+ };
1569
+ /**
1570
+ * Update only the calendar content without recreating the dropdown
1571
+ * This preserves the dropdown state while updating the month view
1572
+ */
1573
+ KTDatepicker.prototype._updateCalendarContent = function () {
1574
+ var _this = this;
1575
+ // Performance marker: Start incremental update
1576
+ if (typeof performance !== 'undefined' && this._config.debug) {
1577
+ performance.mark('datepicker-incremental-update-start');
1578
+ }
1579
+ // Instance-scoped dropdown element selection strategy
1580
+ var dropdownEl = null;
1581
+ // First priority: find dropdown within current datepicker instance
1582
+ dropdownEl = this._element.querySelector('[data-kt-datepicker-dropdown]');
1583
+ // Second priority: if instance ID is available, find by instance ID
1584
+ if (!dropdownEl && this._instanceId) {
1585
+ dropdownEl = document.querySelector("[data-kt-datepicker-dropdown][data-kt-datepicker-instance-id=\"".concat(this._instanceId, "\"]"));
1586
+ }
1587
+ // Third priority: check dropdown module reference
1588
+ if (!dropdownEl && this._dropdownModule) {
1589
+ var dropdownModuleElement = this._dropdownModule._dropdownElement;
1590
+ if (dropdownModuleElement) {
1591
+ dropdownEl = dropdownModuleElement;
1592
+ }
1593
+ }
1594
+ // Final fallback: global search with warnings (only if no other instances exist)
1595
+ if (!dropdownEl) {
1596
+ var allDropdowns = document.querySelectorAll('[data-kt-datepicker-dropdown]');
1597
+ if (allDropdowns.length === 1) {
1598
+ // Only one dropdown exists globally, safe to use
1599
+ dropdownEl = allDropdowns[0];
1600
+ }
1601
+ else if (allDropdowns.length > 1) {
1602
+ // Multiple dropdowns exist - this could cause cross-instance issues
1603
+ this._render();
1604
+ return;
802
1605
  }
803
1606
  else {
804
- state.selectedDateRange.endDate = null;
805
- }
806
- // Update display element
807
- this._updateRangeDisplayElement(start, end);
808
- // Update hidden inputs
809
- if (this._dateInputElement) {
810
- var inputValue = formatDate(start, this._config.format, this._config);
811
- if (end) {
812
- inputValue += "".concat(this._config.rangeSeparator).concat(formatDate(end, this._config.format, this._config));
1607
+ this._render();
1608
+ return;
1609
+ }
1610
+ }
1611
+ if (!dropdownEl) {
1612
+ // Fallback to full render if dropdown doesn't exist (should be rare)
1613
+ this._render();
1614
+ return;
1615
+ }
1616
+ // Clear existing calendar content and update only the calendar
1617
+ var calendarEl = dropdownEl.querySelector('[data-kt-datepicker-calendar-table]');
1618
+ if (calendarEl) {
1619
+ // Remove the old calendar
1620
+ calendarEl.remove();
1621
+ // Get current state - ensure we have the most up-to-date state
1622
+ var currentState = this._unifiedStateManager.getState();
1623
+ // Update the month/year display in the header with multiple selector strategies
1624
+ var monthYearEl = dropdownEl.querySelector('[data-kt-datepicker-month-year]');
1625
+ // Fallback selectors if primary selector fails
1626
+ if (!monthYearEl) {
1627
+ monthYearEl = dropdownEl.querySelector('[data-kt-datepicker-month]');
1628
+ }
1629
+ // Additional fallback: look for span containing month and year
1630
+ if (!monthYearEl) {
1631
+ var spans = dropdownEl.querySelectorAll('span');
1632
+ monthYearEl = Array.from(spans).find(function (span) {
1633
+ return span.textContent && span.textContent.match(/[A-Za-z]+ \d{4}/);
1634
+ });
1635
+ }
1636
+ // Final fallback: any span in header
1637
+ if (!monthYearEl) {
1638
+ var headerEl_1 = dropdownEl.querySelector('[data-kt-datepicker-header]');
1639
+ if (headerEl_1) {
1640
+ monthYearEl = headerEl_1.querySelector('span');
813
1641
  }
814
- this._dateInputElement.value = inputValue;
815
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
816
1642
  }
817
- if (this._startDateInputElement) {
818
- this._startDateInputElement.value = formatDate(start, this._config.format, this._config);
819
- this._startDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
1643
+ if (monthYearEl) {
1644
+ var newMonthYear = "".concat(currentState.currentDate.toLocaleString(this._config.locale, { month: 'long' }), " ").concat(currentState.currentDate.getFullYear());
1645
+ monthYearEl.textContent = newMonthYear;
1646
+ }
1647
+ // Render only the new calendar
1648
+ var dayClickHandler = function (day) {
1649
+ _this.setDate(day);
1650
+ };
1651
+ var newCalendar = renderCalendar(this._templateSet.dayCell, this._getCalendarDays(currentState.currentDate), currentState.currentDate, currentState.selectedDate, dayClickHandler, this._config.locale, this._config.range ? currentState.selectedRange : undefined, this._config.multiDate ? currentState.selectedDates : undefined);
1652
+ // Insert the new calendar after the header
1653
+ var headerEl = dropdownEl.querySelector('[data-kt-datepicker-header]');
1654
+ if (headerEl) {
1655
+ headerEl.insertAdjacentElement('afterend', newCalendar);
820
1656
  }
821
- if (this._endDateInputElement && end) {
822
- this._endDateInputElement.value = formatDate(end, this._config.format, this._config);
823
- this._endDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
1657
+ else {
1658
+ // Fallback: append to dropdown
1659
+ dropdownEl.appendChild(newCalendar);
824
1660
  }
825
- else if (this._endDateInputElement) {
826
- this._endDateInputElement.value = '';
1661
+ // Refresh cache with the new calendar element
1662
+ this._cachedElements.calendarElement = newCalendar;
1663
+ // Enforce min/max dates after calendar update
1664
+ this._enforceMinMaxDates();
1665
+ // Performance marker: End incremental update
1666
+ if (typeof performance !== 'undefined' && this._config.debug) {
1667
+ performance.mark('datepicker-incremental-update-end');
1668
+ performance.measure('datepicker-incremental-update', 'datepicker-incremental-update-start', 'datepicker-incremental-update-end');
827
1669
  }
828
- // Dispatch change event
829
- this._eventManager.dispatchEvent(KTDatepickerEventName.DATE_CHANGE, {
830
- selectedDateRange: state.selectedDateRange,
831
- });
832
1670
  }
833
1671
  else {
834
- // Clear selection
835
- this._state.getState().selectedDateRange = null;
836
- // Clear display
837
- if (this._displayElement) {
838
- var placeholder = ((_a = this._dateInputElement) === null || _a === void 0 ? void 0 : _a.getAttribute('placeholder')) ||
839
- 'Select date range';
840
- this._displayElement.innerHTML = placeholderTemplate(placeholder);
841
- }
842
- // Clear inputs
843
- if (this._dateInputElement) {
844
- this._dateInputElement.value = '';
845
- this._dateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
846
- }
847
- if (this._startDateInputElement) {
848
- this._startDateInputElement.value = '';
849
- this._startDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
850
- }
851
- if (this._endDateInputElement) {
852
- this._endDateInputElement.value = '';
853
- this._endDateInputElement.dispatchEvent(new Event('change', { bubbles: true }));
854
- }
855
- this._eventManager.dispatchEvent(KTDatepickerEventName.DATE_CHANGE, {
856
- selectedDateRange: null,
857
- });
1672
+ // If calendar element not found, fallback to full render
1673
+ this._render();
858
1674
  }
859
1675
  };
860
1676
  /**
861
- * Set the minimum selectable date
862
- *
863
- * @param minDate - Minimum date or null to remove constraint
1677
+ * Update multi-month calendar content without recreating the dropdown
1678
+ * This preserves the dropdown state while updating all visible months
864
1679
  */
865
- KTDatepicker.prototype.setMinDate = function (minDate) {
866
- this._config.minDate = minDate;
867
- // Refresh calendar view to apply new constraints
868
- this._eventManager.dispatchEvent(KTDatepickerEventName.UPDATE);
1680
+ KTDatepicker.prototype._updateMultiMonthCalendarContent = function () {
1681
+ var _this = this;
1682
+ // Instance-scoped dropdown element selection strategy
1683
+ var dropdownEl = null;
1684
+ // First priority: find dropdown within current datepicker instance
1685
+ dropdownEl = this._element.querySelector('[data-kt-datepicker-dropdown]');
1686
+ // Second priority: if instance ID is available, find by instance ID
1687
+ if (!dropdownEl && this._instanceId) {
1688
+ dropdownEl = document.querySelector("[data-kt-datepicker-dropdown][data-kt-datepicker-instance-id=\"".concat(this._instanceId, "\"]"));
1689
+ }
1690
+ // Third priority: check dropdown module reference
1691
+ if (!dropdownEl && this._dropdownModule) {
1692
+ var dropdownModuleElement = this._dropdownModule._dropdownElement;
1693
+ if (dropdownModuleElement) {
1694
+ dropdownEl = dropdownModuleElement;
1695
+ }
1696
+ }
1697
+ // Final fallback: global search with warnings (only if no other instances exist)
1698
+ if (!dropdownEl) {
1699
+ var allDropdowns = document.querySelectorAll('[data-kt-datepicker-dropdown]');
1700
+ if (allDropdowns.length === 1) {
1701
+ // Only one dropdown exists globally, safe to use
1702
+ dropdownEl = allDropdowns[0];
1703
+ }
1704
+ else if (allDropdowns.length > 1) {
1705
+ // Multiple dropdowns exist - this could cause cross-instance issues
1706
+ this._render();
1707
+ return;
1708
+ }
1709
+ else {
1710
+ this._render();
1711
+ return;
1712
+ }
1713
+ }
1714
+ if (!dropdownEl) {
1715
+ // Fallback to full render if dropdown doesn't exist (should be rare)
1716
+ this._render();
1717
+ return;
1718
+ }
1719
+ // Get current state - ensure we have the most up-to-date state
1720
+ var currentState = this._unifiedStateManager.getState();
1721
+ var visibleMonths = this._config.visibleMonths || 1;
1722
+ // Find the multi-month container
1723
+ var multiMonthContainer = dropdownEl.querySelector('[data-kt-datepicker-multimonth-container]');
1724
+ if (!multiMonthContainer) {
1725
+ // Multi-month container not found, fallback to full render
1726
+ this._render();
1727
+ return;
1728
+ }
1729
+ // Clear existing multi-month content
1730
+ multiMonthContainer.innerHTML = '';
1731
+ // Get all month dates for multi-month display
1732
+ var monthDates = this._getMultiMonthDates(currentState.currentDate, visibleMonths);
1733
+ // Re-render each month using the helper method
1734
+ monthDates.forEach(function (monthDate, index) {
1735
+ var panel = _this._renderMultiMonthCalendar(monthDate, index, visibleMonths);
1736
+ multiMonthContainer.appendChild(panel);
1737
+ });
1738
+ // Enforce min/max dates after multi-month calendar update
1739
+ this._enforceMinMaxDates();
1740
+ console.log('[KTDatepicker] Multi-month calendar content updated successfully');
869
1741
  };
870
1742
  /**
871
- * Set the maximum selectable date
872
- *
873
- * @param maxDate - Maximum date or null to remove constraint
1743
+ * Set the selected date (single, range, or multi-date)
1744
+ * @param date - The date to select
874
1745
  */
875
- KTDatepicker.prototype.setMaxDate = function (maxDate) {
876
- this._config.maxDate = maxDate;
877
- // Refresh calendar view to apply new constraints
878
- this._eventManager.dispatchEvent(KTDatepickerEventName.UPDATE);
1746
+ KTDatepicker.prototype.setDate = function (date) {
1747
+ console.log('🗓️ [KTDatepicker] setDate called with:', date);
1748
+ // Prevent selection if date is outside min/max
1749
+ if (this._config.minDate && date < new Date(this._config.minDate)) {
1750
+ console.log('🗓️ [KTDatepicker] setDate blocked: date is before minDate');
1751
+ return;
1752
+ }
1753
+ if (this._config.maxDate && date > new Date(this._config.maxDate)) {
1754
+ console.log('🗓️ [KTDatepicker] setDate blocked: date is after maxDate');
1755
+ return;
1756
+ }
1757
+ // Validate time constraints if time is enabled
1758
+ if (this._config.enableTime) {
1759
+ var timeState = dateToTimeState(date);
1760
+ var validation = validateTime(timeState, this._config.minTime, this._config.maxTime);
1761
+ if (!validation.isValid) {
1762
+ console.log('🗓️ [KTDatepicker] setDate blocked: time validation failed:', validation.error);
1763
+ return;
1764
+ }
1765
+ }
1766
+ if (this._config.multiDate) {
1767
+ this._selectMultiDate(date);
1768
+ return;
1769
+ }
1770
+ if (this._config.range) {
1771
+ this._selectRangeDate(date);
1772
+ return;
1773
+ }
1774
+ this._selectSingleDate(date);
1775
+ };
1776
+ /**
1777
+ * Get the selected date
1778
+ */
1779
+ KTDatepicker.prototype.getDate = function () {
1780
+ return this._unifiedStateManager.getState().selectedDate;
879
1781
  };
880
1782
  /**
881
- * Update the datepicker (refresh view)
1783
+ * Update the datepicker (re-render)
882
1784
  */
883
1785
  KTDatepicker.prototype.update = function () {
884
- // Trigger calendar update through events
885
- this._eventManager.dispatchEvent(KTDatepickerEventName.UPDATE);
1786
+ this._render();
886
1787
  };
887
1788
  /**
888
- * Destroy the datepicker instance and clean up
1789
+ * Clean up event listeners and DOM on destroy
889
1790
  */
890
1791
  KTDatepicker.prototype.destroy = function () {
891
- // Remove event listeners
892
- this._eventManager.removeEventListener(KTDatepickerEventName.DATE_CHANGE, this._handleDateChange.bind(this));
893
- if (this._dateInputElement) {
894
- this._dateInputElement.removeEventListener('change', this._handleInputChange.bind(this));
1792
+ console.log('🗓️ [KTDatepicker] destroy() called');
1793
+ if (this._container && this._container.parentNode) {
1794
+ this._container.parentNode.removeChild(this._container);
1795
+ }
1796
+ // Clean up event manager
1797
+ this._eventManager.removeListener(document, 'click', this._handleDocumentClick);
1798
+ if (this._input) {
1799
+ this._eventManager.removeAllListeners(this._input);
1800
+ }
1801
+ // Clean up focus manager
1802
+ if (this._focusManager) {
1803
+ this._focusManager.dispose();
1804
+ }
1805
+ // Clean up dropdown module
1806
+ if (this._dropdownModule) {
1807
+ this._dropdownModule.dispose();
1808
+ }
1809
+ // Clean up unified state manager
1810
+ if (this._unsubscribeFromState) {
1811
+ this._unsubscribeFromState();
1812
+ this._unsubscribeFromState = null;
1813
+ }
1814
+ if (this._unifiedStateManager) {
1815
+ this._unifiedStateManager.dispose();
1816
+ }
1817
+ // Clean up element observer
1818
+ if (this._elementObserver) {
1819
+ this._elementObserver.disconnect();
1820
+ this._elementObserver = null;
1821
+ }
1822
+ // Clean up time picker renderer
1823
+ if (this._timePickerRenderer) {
1824
+ this._timePickerRenderer.cleanup();
1825
+ this._timePickerRenderer = null;
1826
+ }
1827
+ // Clear DOM element cache
1828
+ this._cachedElements = {
1829
+ calendarElement: null,
1830
+ timePickerElement: null,
1831
+ startContainer: null,
1832
+ endContainer: null,
1833
+ yearElement: null,
1834
+ monthElement: null,
1835
+ dayElement: null,
1836
+ hourElement: null,
1837
+ minuteElement: null,
1838
+ secondElement: null,
1839
+ ampmElement: null,
1840
+ monthYearElement: null,
1841
+ timeDisplay: null
1842
+ };
1843
+ this._element.instance = null;
1844
+ console.log('🗓️ [KTDatepicker] destroy() completed');
1845
+ };
1846
+ /**
1847
+ * Start observing for dynamic element creation
1848
+ */
1849
+ KTDatepicker.prototype._startElementObservation = function () {
1850
+ if (this._elementObserver) {
1851
+ this._elementObserver.disconnect();
895
1852
  }
896
- if (this._displayElement) {
897
- this._displayElement.remove();
1853
+ this._elementObserver = new MutationObserver(function (mutations) {
1854
+ mutations.forEach(function (mutation) {
1855
+ if (mutation.type === 'childList') {
1856
+ mutation.addedNodes.forEach(function (node) {
1857
+ if (node.nodeType === Node.ELEMENT_NODE) {
1858
+ var element = node;
1859
+ // Segmented input elements are now handled by direct UI updates
1860
+ }
1861
+ });
1862
+ }
1863
+ });
1864
+ });
1865
+ this._elementObserver.observe(this._element, {
1866
+ childList: true,
1867
+ subtree: true
1868
+ });
1869
+ };
1870
+ /**
1871
+ * Update the input placeholder if no date is selected
1872
+ */
1873
+ KTDatepicker.prototype._updatePlaceholder = function () {
1874
+ var currentState = this._unifiedStateManager.getState();
1875
+ if (this._input && !currentState.selectedDate && this._config.placeholder) {
1876
+ this._input.setAttribute('placeholder', this._config.placeholder);
1877
+ console.log('🗓️ [KTDatepicker] _render: Placeholder set to:', this._config.placeholder);
898
1878
  }
899
- // Remove instance from element
900
- this._element.removeAttribute('data-kt-datepicker-initialized');
901
- delete this._element.instance;
902
- // Remove initialized class
903
- this._element.classList.remove('relative');
904
- // Remove from instances map
905
- KTDatepicker._instances.delete(this._element);
906
1879
  };
907
1880
  /**
908
- * Dispatch a custom event
1881
+ * Update the disabled state of the input and calendar button
909
1882
  *
910
- * @param eventName - Name of the event
911
- * @param payload - Optional event payload
1883
+ * Accessibility rationale:
1884
+ * - When disabling the calendar button, set:
1885
+ * - disabled attribute (removes from tab order, blocks interaction)
1886
+ * - aria-disabled="true" (announces as disabled to screen readers)
1887
+ * - tabindex="-1" (removes from tab order for extra safety)
1888
+ * - When enabling, remove these attributes.
1889
+ * This matches accessibility best practices and ensures the button is properly announced and not focusable when disabled.
912
1890
  */
913
- KTDatepicker.prototype._dispatchEvent = function (eventName, payload) {
914
- this._eventManager.dispatchEvent(eventName, payload);
1891
+ KTDatepicker.prototype._updateDisabledState = function () {
1892
+ var calendarButton = this._element.querySelector('button[data-kt-datepicker-calendar-btn]');
1893
+ if (this._input && this._config.disabled) {
1894
+ this._input.setAttribute('disabled', 'true');
1895
+ if (calendarButton) {
1896
+ // Set disabled attribute
1897
+ calendarButton.setAttribute('disabled', 'true');
1898
+ // Set aria-disabled for screen readers
1899
+ calendarButton.setAttribute('aria-disabled', 'true');
1900
+ // Remove from tab order
1901
+ calendarButton.setAttribute('tabindex', '-1');
1902
+ }
1903
+ console.log('🗓️ [KTDatepicker] _render: Input and calendar button disabled');
1904
+ }
1905
+ else if (this._input) {
1906
+ this._input.removeAttribute('disabled');
1907
+ if (calendarButton) {
1908
+ // Remove disabled attribute
1909
+ calendarButton.removeAttribute('disabled');
1910
+ // Remove aria-disabled attribute
1911
+ calendarButton.removeAttribute('aria-disabled');
1912
+ // Restore tab order (remove tabindex)
1913
+ calendarButton.removeAttribute('tabindex');
1914
+ }
1915
+ }
915
1916
  };
916
1917
  /**
917
- * Create instances for all datepicker elements on the page
1918
+ * Update the segmented input UI to reflect current state
918
1919
  */
919
- KTDatepicker.createInstances = function () {
1920
+ KTDatepicker.prototype._updateSegmentedInput = function (state) {
920
1921
  var _this = this;
921
- var elements = document.querySelectorAll('[data-kt-datepicker]');
922
- elements.forEach(function (element) {
923
- if (element.hasAttribute('data-kt-datepicker') &&
924
- !element.getAttribute('data-kt-datepicker-initialized')) {
925
- // Create instance
926
- var instance = new KTDatepicker(element);
927
- _this._instances.set(element, instance);
1922
+ var segmentedContainer = this._element.querySelector('[data-kt-datepicker-segmented-input]');
1923
+ if (!segmentedContainer)
1924
+ return;
1925
+ // Re-instantiate the segmented input with the current state
1926
+ // This will update the display without recreating the entire UI
1927
+ instantiateSingleSegmentedInput(segmentedContainer, state, this._config, function (date) { return _this._handleSegmentedInputChange(date); });
1928
+ };
1929
+ /**
1930
+ * Update the range segmented inputs UI to reflect current state
1931
+ */
1932
+ KTDatepicker.prototype._updateRangeSegmentedInput = function (state) {
1933
+ var _this = this;
1934
+ var startContainer = this._element.querySelector('[data-kt-datepicker-start-container]');
1935
+ var endContainer = this._element.querySelector('[data-kt-datepicker-end-container]');
1936
+ if (!startContainer || !endContainer)
1937
+ return;
1938
+ // Re-instantiate the range segmented inputs with the current state
1939
+ // This will update the display without recreating the entire UI
1940
+ instantiateRangeSegmentedInputs(startContainer, endContainer, state, this._config, function (date) {
1941
+ var _a;
1942
+ var end = ((_a = state.selectedRange) === null || _a === void 0 ? void 0 : _a.end) || null;
1943
+ var newEnd = end;
1944
+ if (end && date > end)
1945
+ newEnd = null;
1946
+ _this._unifiedStateManager.updateState({ selectedRange: { start: date, end: newEnd } }, 'range-selection');
1947
+ _this._updateCalendarContent();
1948
+ }, function (date) {
1949
+ var _a;
1950
+ var start = ((_a = state.selectedRange) === null || _a === void 0 ? void 0 : _a.start) || null;
1951
+ var newStart = start;
1952
+ if (start && date < start)
1953
+ newStart = null;
1954
+ _this._unifiedStateManager.updateState({ selectedRange: { start: newStart, end: date } }, 'range-selection');
1955
+ _this._updateCalendarContent();
1956
+ });
1957
+ };
1958
+ /**
1959
+ * Handle changes from the segmented input
1960
+ */
1961
+ KTDatepicker.prototype._handleSegmentedInputChange = function (date) {
1962
+ console.log('🗓️ [KTDatepicker] Segmented input change:', date);
1963
+ // When date changes from segmented input, also update the calendar view to show the selected date's month/year
1964
+ var currentState = this._unifiedStateManager.getState();
1965
+ var newCurrentDate = new Date(date.getFullYear(), date.getMonth(), 1); // First day of selected date's month
1966
+ // Prepare state updates
1967
+ var stateUpdates = {
1968
+ selectedDate: date,
1969
+ currentDate: newCurrentDate
1970
+ };
1971
+ // Update selectedTime if time is enabled
1972
+ if (this._config.enableTime) {
1973
+ stateUpdates.selectedTime = dateToTimeState(date);
1974
+ }
1975
+ // Update both selectedDate and currentDate to sync the calendar view
1976
+ // Use immediate update to ensure events fire synchronously
1977
+ this._unifiedStateManager.updateState(stateUpdates, 'segmented-input', true);
1978
+ // Update calendar content to sync dropdown even when closed
1979
+ this._updateCalendarContent();
1980
+ // Fire onChange event
1981
+ this._fireDatepickerEvent('onChange', date, this);
1982
+ };
1983
+ /**
1984
+ * Normalize a date to midnight for date-only comparison (removes time component)
1985
+ */
1986
+ /**
1987
+ * Disable day buttons outside min/max date range
1988
+ */
1989
+ KTDatepicker.prototype._enforceMinMaxDates = function () {
1990
+ if (this._config.minDate || this._config.maxDate) {
1991
+ // Normalize min/max dates to midnight for date-only comparison
1992
+ var minDate_1 = this._config.minDate ? normalizeDateToMidnight(new Date(this._config.minDate)) : null;
1993
+ var maxDate_1 = this._config.maxDate ? normalizeDateToMidnight(new Date(this._config.maxDate)) : null;
1994
+ // Find calendar element - try multiple strategies
1995
+ var calendarElement = this._cachedElements.calendarElement;
1996
+ if (!calendarElement || !calendarElement.isConnected) {
1997
+ // Try to find dropdown first
1998
+ var dropdownEl = this._element.querySelector('[data-kt-datepicker-dropdown]');
1999
+ if (!dropdownEl && this._instanceId) {
2000
+ dropdownEl = document.querySelector("[data-kt-datepicker-dropdown][data-kt-datepicker-instance-id=\"".concat(this._instanceId, "\"]"));
2001
+ }
2002
+ if (dropdownEl) {
2003
+ calendarElement = dropdownEl.querySelector('[data-kt-datepicker-calendar-table]');
2004
+ }
928
2005
  }
2006
+ if (!calendarElement)
2007
+ return;
2008
+ // Get all day cells using the calendar element scope
2009
+ var dayCells = calendarElement.querySelectorAll('td[data-kt-datepicker-day]');
2010
+ dayCells.forEach(function (td) {
2011
+ // Get the actual date from the data-date attribute (stored as ISO string)
2012
+ var dateISO = td.getAttribute('data-date');
2013
+ if (!dateISO)
2014
+ return;
2015
+ // Parse the ISO date and normalize to midnight for comparison
2016
+ var cellDate = normalizeDateToMidnight(new Date(dateISO));
2017
+ var btn = td.querySelector('button[data-day]');
2018
+ if (!btn)
2019
+ return;
2020
+ var disabled = false;
2021
+ // Compare dates (normalized to midnight) for proper date-only comparison
2022
+ if (minDate_1 && cellDate < minDate_1)
2023
+ disabled = true;
2024
+ if (maxDate_1 && cellDate > maxDate_1)
2025
+ disabled = true;
2026
+ if (disabled) {
2027
+ btn.setAttribute('disabled', 'true');
2028
+ td.setAttribute('data-out-of-range', 'true');
2029
+ }
2030
+ else {
2031
+ btn.removeAttribute('disabled');
2032
+ td.removeAttribute('data-out-of-range');
2033
+ }
2034
+ });
2035
+ }
2036
+ };
2037
+ /**
2038
+ * Opens the datepicker dropdown.
2039
+ */
2040
+ KTDatepicker.prototype.open = function () {
2041
+ if (this._unifiedStateManager.isDropdownOpen()) {
2042
+ console.log('🗓️ [KTDatepicker] open() skipped: already open');
2043
+ return;
2044
+ }
2045
+ if (this._config.disabled) {
2046
+ console.log('🗓️ [KTDatepicker] open() blocked: datepicker is disabled');
2047
+ return;
2048
+ }
2049
+ console.log('🗓️ [KTDatepicker] open() called, attempting to open dropdown');
2050
+ // Use unified state management
2051
+ var success = this._unifiedStateManager.setDropdownOpen(true, 'datepicker-open');
2052
+ if (!success) {
2053
+ console.log('🗓️ [KTDatepicker] open() blocked by state validation');
2054
+ return;
2055
+ }
2056
+ console.log('🗓️ [KTDatepicker] State manager open() successful, dropdown module:', this._dropdownModule);
2057
+ // Ensure dropdown content is rendered before opening
2058
+ var dropdownEl = this._element.querySelector('[data-kt-datepicker-dropdown]');
2059
+ if (dropdownEl) {
2060
+ // Always render dropdown content to ensure it's up to date
2061
+ console.log('🗓️ [KTDatepicker] Rendering dropdown content before opening');
2062
+ this._renderDropdownContent(dropdownEl);
2063
+ }
2064
+ // Use dropdown module if available
2065
+ if (this._dropdownModule) {
2066
+ console.log('🗓️ [KTDatepicker] Dropdown module available, state change will trigger open');
2067
+ // The dropdown module will automatically open via observer pattern
2068
+ }
2069
+ else {
2070
+ console.log('🗓️ [KTDatepicker] No dropdown module, using fallback');
2071
+ }
2072
+ // Don't call _render() here as it recreates the dropdown module
2073
+ // The dropdown module handles its own visibility
2074
+ };
2075
+ /**
2076
+ * Closes the datepicker dropdown.
2077
+ */
2078
+ KTDatepicker.prototype.close = function () {
2079
+ var _a;
2080
+ console.log('🗓️ [KTDatepicker] close() called, current state:', {
2081
+ stateManagerOpen: this._unifiedStateManager.isDropdownOpen(),
2082
+ dropdownModuleOpen: (_a = this._dropdownModule) === null || _a === void 0 ? void 0 : _a.isOpen(),
2083
+ stateManagerState: this._unifiedStateManager.getDropdownState()
929
2084
  });
2085
+ if (!this._unifiedStateManager.isDropdownOpen()) {
2086
+ console.log('🗓️ [KTDatepicker] close() skipped: already closed');
2087
+ return;
2088
+ }
2089
+ // Debug log with stack trace
2090
+ console.log('[KTDatepicker] close() called. Dropdown will close. Stack trace:', new Error().stack);
2091
+ // Use unified state management
2092
+ var success = this._unifiedStateManager.setDropdownOpen(false, 'datepicker-close');
2093
+ if (!success) {
2094
+ console.log('🗓️ [KTDatepicker] close() blocked by state validation');
2095
+ return;
2096
+ }
2097
+ console.log('🗓️ [KTDatepicker] State manager close() successful');
2098
+ // Use dropdown module if available
2099
+ if (this._dropdownModule) {
2100
+ console.log('🗓️ [KTDatepicker] Dropdown module available, state change will trigger close');
2101
+ // The dropdown module will automatically close via observer pattern
2102
+ }
2103
+ else {
2104
+ console.log('🗓️ [KTDatepicker] No dropdown module, using fallback');
2105
+ }
2106
+ // Don't call _render() here as it recreates the dropdown module
2107
+ // The dropdown module handles its own visibility
2108
+ };
2109
+ KTDatepicker.prototype.toggle = function () {
2110
+ if (this._unifiedStateManager.isDropdownOpen()) {
2111
+ this.close();
2112
+ }
2113
+ else {
2114
+ this.open();
2115
+ }
930
2116
  };
931
2117
  /**
932
- * Initialize all datepickers on the page
2118
+ * Returns whether the datepicker dropdown is currently open.
933
2119
  */
934
- KTDatepicker.init = function () {
935
- KTDatepicker.createInstances();
2120
+ KTDatepicker.prototype.isOpen = function () {
2121
+ return this._unifiedStateManager.isDropdownOpen();
936
2122
  };
937
2123
  /**
938
- * ========================================================================
939
- * Static instances
940
- * ========================================================================
2124
+ * Returns the current state of the datepicker component.
941
2125
  */
942
- KTDatepicker._instances = new Map();
2126
+ KTDatepicker.prototype.getState = function () {
2127
+ return __assign({}, this._unifiedStateManager.getState());
2128
+ };
2129
+ /**
2130
+ * Returns the current dropdown state.
2131
+ */
2132
+ KTDatepicker.prototype.getDropdownState = function () {
2133
+ return this._unifiedStateManager.getDropdownState();
2134
+ };
2135
+ /**
2136
+ * StateObserver implementation: Called when state changes
2137
+ */
2138
+ /**
2139
+ * StateObserver implementation: Returns update priority (lower = higher priority)
2140
+ */
2141
+ KTDatepicker.prototype.getUpdatePriority = function () {
2142
+ return 10; // Medium priority
2143
+ };
2144
+ /**
2145
+ * StateObserver implementation: Handle state changes from unified state manager
2146
+ */
2147
+ KTDatepicker.prototype.onStateChange = function (newState, oldState) {
2148
+ this._handleStateChange(newState, oldState);
2149
+ };
2150
+ /**
2151
+ * Handle state changes from the unified state manager
2152
+ */
2153
+ KTDatepicker.prototype._handleStateChange = function (newState, oldState) {
2154
+ console.log('🗓️ [KTDatepicker] _handleStateChange called:', { newState: newState, oldState: oldState });
2155
+ // Skip UI updates if this is from segmented input arrow navigation
2156
+ if (typeof window !== 'undefined' && window.__ktui_segmented_input_arrow_navigation) {
2157
+ console.log('🗓️ [KTDatepicker] Skipping UI update due to segmented input navigation');
2158
+ return;
2159
+ }
2160
+ // Get changed properties for selective updates
2161
+ var changedProperties = this._unifiedStateManager.getLastChangedProperties();
2162
+ // Get the source of the last state update
2163
+ var lastUpdateSource = this._unifiedStateManager.getLastUpdateSource();
2164
+ // Update dropdown state if open/closed changed
2165
+ if (changedProperties.has('isOpen') && newState.isOpen !== oldState.isOpen) {
2166
+ if (this._dropdownModule) {
2167
+ console.log('🗓️ [KTDatepicker] Dropdown module available, state change will trigger open');
2168
+ // The dropdown module will automatically open via observer pattern
2169
+ }
2170
+ else {
2171
+ console.log('🗓️ [KTDatepicker] No dropdown module, using fallback');
2172
+ }
2173
+ // Don't call _render() here as it recreates the dropdown module
2174
+ // The dropdown module handles its own visibility
2175
+ }
2176
+ // Update disabled state only if it changed
2177
+ if (changedProperties.has('isDisabled') && newState.isDisabled !== oldState.isDisabled) {
2178
+ this._updateDisabledState();
2179
+ }
2180
+ // Selective UI updates based on changed properties
2181
+ // Update input field only if selection-related properties changed
2182
+ if (changedProperties.has('selectedDate') ||
2183
+ changedProperties.has('selectedRange') ||
2184
+ changedProperties.has('selectedDates') ||
2185
+ changedProperties.has('selectedRange.start') ||
2186
+ changedProperties.has('selectedRange.end')) {
2187
+ this._updateInput(newState);
2188
+ }
2189
+ // Update segmented input only if selectedDate changed (and not from segmented input itself)
2190
+ if (this._config.range) {
2191
+ if ((changedProperties.has('selectedRange') ||
2192
+ changedProperties.has('selectedRange.start') ||
2193
+ changedProperties.has('selectedRange.end')) &&
2194
+ lastUpdateSource !== 'range-selection') {
2195
+ this._updateRangeSegmentedInput(newState);
2196
+ }
2197
+ else if (lastUpdateSource === 'range-selection') {
2198
+ console.log('🗓️ [KTDatepicker] Skipping _updateRangeSegmentedInput to preserve focus during typing');
2199
+ }
2200
+ }
2201
+ else {
2202
+ if (changedProperties.has('selectedDate') && lastUpdateSource !== 'segmented-input') {
2203
+ this._updateSegmentedInput(newState);
2204
+ }
2205
+ else if (lastUpdateSource === 'segmented-input') {
2206
+ console.log('🗓️ [KTDatepicker] Skipping _updateSegmentedInput to preserve focus during typing');
2207
+ }
2208
+ }
2209
+ // Update calendar highlighting only if selection state changed
2210
+ if (changedProperties.has('selectedDate') ||
2211
+ changedProperties.has('selectedRange') ||
2212
+ changedProperties.has('selectedRange.start') ||
2213
+ changedProperties.has('selectedRange.end') ||
2214
+ changedProperties.has('selectedDates') ||
2215
+ changedProperties.has('currentDate')) {
2216
+ this._updateCalendar(newState);
2217
+ }
2218
+ // Update time picker only if selectedTime changed
2219
+ if (changedProperties.has('selectedTime')) {
2220
+ this._updateTimePicker(newState);
2221
+ }
2222
+ // Fire events based on state changes
2223
+ this._fireEvents(newState, oldState);
2224
+ // Start observing for dynamic element creation
2225
+ this._startElementObservation();
2226
+ };
2227
+ // Static init method for auto-initialization
2228
+ KTDatepicker.init = function () {
2229
+ var elements = document.querySelectorAll('[data-kt-datepicker]');
2230
+ elements.forEach(function (el) {
2231
+ if (!el.instance) {
2232
+ new KTDatepicker(el);
2233
+ }
2234
+ });
2235
+ };
943
2236
  return KTDatepicker;
944
2237
  }(KTComponent));
945
2238
  export { KTDatepicker };
2239
+ // Optionally, export a static init method for auto-initialization
2240
+ export function initDatepickers() {
2241
+ var elements = document.querySelectorAll('[data-kt-datepicker]');
2242
+ elements.forEach(function (el) {
2243
+ if (!el.instance) {
2244
+ new KTDatepicker(el);
2245
+ }
2246
+ });
2247
+ }
946
2248
  //# sourceMappingURL=datepicker.js.map