@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
@@ -0,0 +1,387 @@
1
+ /**
2
+ * time-preservation.test.ts - Tests for time preservation on calendar date clicks
3
+ * Ensures time is preserved across single, range, and multi-date selection modes
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { KTDatepicker } from '../datepicker';
8
+ import { dateToTimeState, applyTimeToDate } from '../utils/time-utils';
9
+
10
+ describe('KTDatepicker Time Preservation', () => {
11
+ let container: HTMLElement;
12
+ let datepicker: KTDatepicker;
13
+
14
+ beforeEach(() => {
15
+ // Create a fresh container for each test
16
+ container = document.createElement('div');
17
+ container.innerHTML = `
18
+ <div class="kt-datepicker" data-kt-datepicker>
19
+ <input type="text" data-kt-datepicker-input placeholder="Select date">
20
+ </div>
21
+ `;
22
+ document.body.innerHTML = '';
23
+ document.body.appendChild(container);
24
+ });
25
+
26
+ afterEach(() => {
27
+ if (datepicker) {
28
+ datepicker.destroy();
29
+ }
30
+ document.body.innerHTML = '';
31
+ });
32
+
33
+ describe('Single Date Mode', () => {
34
+ it('should preserve time when selectedTime exists', () => {
35
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
36
+ const initialDate = new Date(2024, 0, 15, 14, 30, 0); // Jan 15, 2024, 2:30 PM
37
+
38
+ datepicker = new KTDatepicker(element, {
39
+ enableTime: true,
40
+ value: initialDate,
41
+ });
42
+
43
+ // Verify initial date has time set
44
+ const initialState = datepicker.getState();
45
+ expect(initialState.selectedDate).toBeDefined();
46
+ expect(initialState.selectedDate?.getHours()).toBe(14);
47
+ expect(initialState.selectedDate?.getMinutes()).toBe(30);
48
+
49
+ // Select a new date
50
+ const newDate = new Date(2024, 0, 20, 0, 0, 0); // Jan 20, 2024, midnight
51
+ datepicker.setDate(newDate);
52
+
53
+ // Verify time is preserved
54
+ const newState = datepicker.getState();
55
+ expect(newState.selectedDate).toBeDefined();
56
+ expect(newState.selectedDate?.getDate()).toBe(20);
57
+ expect(newState.selectedDate?.getHours()).toBe(14);
58
+ expect(newState.selectedDate?.getMinutes()).toBe(30);
59
+ });
60
+
61
+ it('should extract time from selectedDate when selectedTime is missing', () => {
62
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
63
+ const initialDate = new Date(2024, 0, 15, 16, 45, 0); // Jan 15, 2024, 4:45 PM
64
+
65
+ datepicker = new KTDatepicker(element, {
66
+ enableTime: true,
67
+ value: initialDate,
68
+ });
69
+
70
+ // Clear selectedTime state (simulate missing state)
71
+ const state = datepicker.getState();
72
+ (datepicker as any)._unifiedStateManager.updateState(
73
+ { selectedTime: null },
74
+ 'test-clear'
75
+ );
76
+
77
+ // Select a new date
78
+ const newDate = new Date(2024, 0, 25, 0, 0, 0);
79
+ datepicker.setDate(newDate);
80
+
81
+ // Verify time is extracted from selectedDate
82
+ const newState = datepicker.getState();
83
+ expect(newState.selectedDate?.getDate()).toBe(25);
84
+ expect(newState.selectedDate?.getHours()).toBe(16);
85
+ expect(newState.selectedDate?.getMinutes()).toBe(45);
86
+ });
87
+
88
+ it('should default to current time when no time exists', () => {
89
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
90
+ const now = new Date();
91
+
92
+ datepicker = new KTDatepicker(element, {
93
+ enableTime: true,
94
+ });
95
+
96
+ // Select a date
97
+ const newDate = new Date(2024, 0, 15, 0, 0, 0);
98
+ datepicker.setDate(newDate);
99
+
100
+ // Verify time defaults to current time
101
+ const state = datepicker.getState();
102
+ expect(state.selectedDate).toBeDefined();
103
+ expect(state.selectedDate?.getDate()).toBe(15);
104
+ // Allow 1-minute tolerance for test execution time
105
+ const timeDiff = Math.abs(state.selectedDate!.getHours() - now.getHours());
106
+ expect(timeDiff).toBeLessThanOrEqual(1);
107
+ });
108
+
109
+ it('should not reset time to midnight', () => {
110
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
111
+ const initialDate = new Date(2024, 0, 15, 10, 15, 30); // Jan 15, 2024, 10:15:30 AM
112
+
113
+ datepicker = new KTDatepicker(element, {
114
+ enableTime: true,
115
+ timeGranularity: 'second',
116
+ value: initialDate,
117
+ });
118
+
119
+ // Select a new date
120
+ const newDate = new Date(2024, 0, 20, 0, 0, 0);
121
+ datepicker.setDate(newDate);
122
+
123
+ // Verify time is NOT midnight
124
+ const state = datepicker.getState();
125
+ expect(state.selectedDate?.getHours()).not.toBe(0);
126
+ expect(state.selectedDate?.getMinutes()).not.toBe(0);
127
+ expect(state.selectedDate?.getSeconds()).not.toBe(0);
128
+ expect(state.selectedDate?.getHours()).toBe(10);
129
+ expect(state.selectedDate?.getMinutes()).toBe(15);
130
+ expect(state.selectedDate?.getSeconds()).toBe(30);
131
+ });
132
+ });
133
+
134
+ describe('Range Selection Mode', () => {
135
+ it('should preserve time for both start and end dates', () => {
136
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
137
+ const initialDate = new Date(2024, 0, 15, 14, 30, 0); // Jan 15, 2024, 2:30 PM
138
+
139
+ datepicker = new KTDatepicker(element, {
140
+ range: true,
141
+ enableTime: true,
142
+ });
143
+
144
+ // Manually set selectedTime to simulate time picker selection
145
+ // Use immediate update (third parameter = true)
146
+ (datepicker as any)._unifiedStateManager.updateState(
147
+ { selectedTime: { hour: 14, minute: 30, second: 0 } },
148
+ 'test-init',
149
+ true
150
+ );
151
+
152
+ // Select start date (first click creates start)
153
+ const startDate = new Date(2024, 0, 10, 0, 0, 0);
154
+ (datepicker as any)._selectRangeDate(startDate);
155
+
156
+ // Verify start has time preserved
157
+ const afterStartState = datepicker.getState();
158
+ expect(afterStartState.selectedRange?.start?.getDate()).toBe(10);
159
+ expect(afterStartState.selectedRange?.start?.getHours()).toBe(14);
160
+ expect(afterStartState.selectedRange?.start?.getMinutes()).toBe(30);
161
+
162
+ // Select end date (second click creates end)
163
+ const endDate = new Date(2024, 0, 20, 0, 0, 0);
164
+ (datepicker as any)._selectRangeDate(endDate);
165
+
166
+ // Verify both dates preserve time
167
+ const newState = datepicker.getState();
168
+ expect(newState.selectedRange).toBeDefined();
169
+ expect(newState.selectedRange?.start).toBeDefined();
170
+ expect(newState.selectedRange?.end).toBeDefined();
171
+
172
+ expect(newState.selectedRange?.start?.getDate()).toBe(10);
173
+ expect(newState.selectedRange?.start?.getHours()).toBe(14);
174
+ expect(newState.selectedRange?.start?.getMinutes()).toBe(30);
175
+
176
+ expect(newState.selectedRange?.end?.getDate()).toBe(20);
177
+ expect(newState.selectedRange?.end?.getHours()).toBe(14);
178
+ expect(newState.selectedRange?.end?.getMinutes()).toBe(30);
179
+ });
180
+
181
+ it('should not reset range dates to midnight', () => {
182
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
183
+ const initialDate = new Date(2024, 0, 15, 16, 45, 0); // Jan 15, 2024, 4:45 PM
184
+
185
+ datepicker = new KTDatepicker(element, {
186
+ range: true,
187
+ enableTime: true,
188
+ value: initialDate,
189
+ });
190
+
191
+ // Select start and end dates
192
+ const startDate = new Date(2024, 0, 10, 0, 0, 0);
193
+ (datepicker as any)._selectRangeDate(startDate);
194
+
195
+ const endDate = new Date(2024, 0, 25, 0, 0, 0);
196
+ (datepicker as any)._selectRangeDate(endDate);
197
+
198
+ // Verify time is NOT midnight for either date
199
+ const state = datepicker.getState();
200
+ expect(state.selectedRange?.start?.getHours()).not.toBe(0);
201
+ expect(state.selectedRange?.start?.getMinutes()).not.toBe(0);
202
+ expect(state.selectedRange?.end?.getHours()).not.toBe(0);
203
+ expect(state.selectedRange?.end?.getMinutes()).not.toBe(0);
204
+ });
205
+
206
+ it('should extract time from range start when selectedTime is missing', () => {
207
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
208
+
209
+ datepicker = new KTDatepicker(element, {
210
+ range: true,
211
+ enableTime: true,
212
+ valueRange: {
213
+ start: new Date(2024, 0, 10, 13, 20, 0), // Jan 10, 2024, 1:20 PM
214
+ end: new Date(2024, 0, 15, 13, 20, 0), // Jan 15, 2024, 1:20 PM
215
+ },
216
+ });
217
+
218
+ // Clear selectedTime
219
+ (datepicker as any)._unifiedStateManager.updateState(
220
+ { selectedTime: null },
221
+ 'test-clear'
222
+ );
223
+
224
+ // Reset range to only have start
225
+ (datepicker as any)._unifiedStateManager.setSelectedRange(
226
+ {
227
+ start: new Date(2024, 0, 10, 13, 20, 0),
228
+ end: null,
229
+ },
230
+ 'test-reset'
231
+ );
232
+
233
+ // Select a new end date
234
+ const newEndDate = new Date(2024, 0, 25, 0, 0, 0);
235
+ (datepicker as any)._selectRangeDate(newEndDate);
236
+
237
+ // Verify time is extracted from range start
238
+ const state = datepicker.getState();
239
+ expect(state.selectedRange?.end?.getDate()).toBe(25);
240
+ expect(state.selectedRange?.end?.getHours()).toBe(13);
241
+ expect(state.selectedRange?.end?.getMinutes()).toBe(20);
242
+ });
243
+ });
244
+
245
+ describe('Multi-Date Selection Mode', () => {
246
+ it('should preserve time for all selected dates', () => {
247
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
248
+ const initialDate = new Date(2024, 0, 15, 11, 30, 0); // Jan 15, 2024, 11:30 AM
249
+
250
+ datepicker = new KTDatepicker(element, {
251
+ multiDate: true,
252
+ enableTime: true,
253
+ values: [initialDate],
254
+ });
255
+
256
+ // Verify initial date has time set
257
+ const initialState = datepicker.getState();
258
+ expect(initialState.selectedDates).toBeDefined();
259
+ expect(initialState.selectedDates.length).toBe(1);
260
+ expect(initialState.selectedDates[0].getHours()).toBe(11);
261
+ expect(initialState.selectedDates[0].getMinutes()).toBe(30);
262
+
263
+ // Select multiple dates
264
+ const date1 = new Date(2024, 0, 10, 0, 0, 0);
265
+ (datepicker as any)._selectMultiDate(date1);
266
+
267
+ const date2 = new Date(2024, 0, 20, 0, 0, 0);
268
+ (datepicker as any)._selectMultiDate(date2);
269
+
270
+ const date3 = new Date(2024, 0, 25, 0, 0, 0);
271
+ (datepicker as any)._selectMultiDate(date3);
272
+
273
+ // Verify all dates preserve time
274
+ const newState = datepicker.getState();
275
+ expect(newState.selectedDates).toBeDefined();
276
+ expect(newState.selectedDates.length).toBeGreaterThan(1);
277
+
278
+ newState.selectedDates.forEach((date) => {
279
+ expect(date.getHours()).toBe(11);
280
+ expect(date.getMinutes()).toBe(30);
281
+ });
282
+ });
283
+
284
+ it('should not reset multi-dates to midnight', () => {
285
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
286
+ const initialDate = new Date(2024, 0, 15, 9, 15, 0); // Jan 15, 2024, 9:15 AM
287
+
288
+ datepicker = new KTDatepicker(element, {
289
+ multiDate: true,
290
+ enableTime: true,
291
+ value: initialDate,
292
+ });
293
+
294
+ // Select dates
295
+ const date1 = new Date(2024, 0, 10, 0, 0, 0);
296
+ (datepicker as any)._selectMultiDate(date1);
297
+
298
+ const date2 = new Date(2024, 0, 20, 0, 0, 0);
299
+ (datepicker as any)._selectMultiDate(date2);
300
+
301
+ // Verify time is NOT midnight for any date
302
+ const state = datepicker.getState();
303
+ state.selectedDates.forEach((date) => {
304
+ expect(date.getHours()).not.toBe(0);
305
+ expect(date.getMinutes()).not.toBe(0);
306
+ });
307
+ });
308
+
309
+ it('should extract time from existing selected dates when selectedTime is missing', () => {
310
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
311
+
312
+ datepicker = new KTDatepicker(element, {
313
+ multiDate: true,
314
+ enableTime: true,
315
+ values: [
316
+ new Date(2024, 0, 10, 15, 45, 0), // Jan 10, 2024, 3:45 PM
317
+ new Date(2024, 0, 15, 15, 45, 0), // Jan 15, 2024, 3:45 PM
318
+ ],
319
+ });
320
+
321
+ // Clear selectedTime
322
+ (datepicker as any)._unifiedStateManager.updateState(
323
+ { selectedTime: null },
324
+ 'test-clear'
325
+ );
326
+
327
+ // Select a new date
328
+ const newDate = new Date(2024, 0, 25, 0, 0, 0);
329
+ (datepicker as any)._selectMultiDate(newDate);
330
+
331
+ // Verify time is extracted from existing dates
332
+ const state = datepicker.getState();
333
+ const addedDate = state.selectedDates.find((d) => d.getDate() === 25);
334
+ expect(addedDate).toBeDefined();
335
+ expect(addedDate?.getHours()).toBe(15);
336
+ expect(addedDate?.getMinutes()).toBe(45);
337
+ });
338
+ });
339
+
340
+ describe('Date-Only Mode (No Time)', () => {
341
+ it('should not apply time when enableTime is false', () => {
342
+ const element = container.querySelector('[data-kt-datepicker]') as HTMLElement;
343
+
344
+ datepicker = new KTDatepicker(element, {
345
+ enableTime: false,
346
+ });
347
+
348
+ // Select a date
349
+ const date = new Date(2024, 0, 15, 0, 0, 0);
350
+ datepicker.setDate(date);
351
+
352
+ // Verify time remains at midnight (default for date-only)
353
+ const state = datepicker.getState();
354
+ expect(state.selectedDate?.getDate()).toBe(15);
355
+ // Time should be normalized to midnight for date-only mode
356
+ expect(state.selectedDate?.getHours()).toBe(0);
357
+ expect(state.selectedDate?.getMinutes()).toBe(0);
358
+ expect(state.selectedDate?.getSeconds()).toBe(0);
359
+ });
360
+ });
361
+
362
+ describe('Time Utility Functions', () => {
363
+ it('dateToTimeState should extract time correctly', () => {
364
+ const date = new Date(2024, 0, 15, 14, 30, 45);
365
+ const timeState = dateToTimeState(date);
366
+
367
+ expect(timeState.hour).toBe(14);
368
+ expect(timeState.minute).toBe(30);
369
+ expect(timeState.second).toBe(45);
370
+ });
371
+
372
+ it('applyTimeToDate should preserve date and apply time', () => {
373
+ const date = new Date(2024, 0, 15, 0, 0, 0);
374
+ const timeState = { hour: 16, minute: 45, second: 30 };
375
+
376
+ const result = applyTimeToDate(date, timeState);
377
+
378
+ expect(result.getFullYear()).toBe(2024);
379
+ expect(result.getMonth()).toBe(0);
380
+ expect(result.getDate()).toBe(15);
381
+ expect(result.getHours()).toBe(16);
382
+ expect(result.getMinutes()).toBe(45);
383
+ expect(result.getSeconds()).toBe(30);
384
+ });
385
+ });
386
+ });
387
+
@@ -0,0 +1,40 @@
1
+ import { KTDatepickerConfig } from './types';
2
+
3
+ /**
4
+ * Default configuration for KTDatepicker
5
+ */
6
+ export const defaultDatepickerConfig: KTDatepickerConfig = {
7
+ templates: {},
8
+ format: 'yyyy-MM-dd',
9
+ range: false,
10
+ multiDate: false,
11
+ minDate: undefined,
12
+ maxDate: undefined,
13
+ disabled: false,
14
+ locale: 'en-US',
15
+ placeholder: '',
16
+ value: undefined,
17
+ valueRange: undefined,
18
+ values: undefined,
19
+ showOnFocus: true,
20
+ closeOnSelect: false, // Default: don't close dropdown by default
21
+ closeOnOutsideClick: true, // Default: close dropdown when clicking outside
22
+ // Number of calendar months to display side-by-side (horizontal). Default: 1.
23
+ visibleMonths: 1,
24
+ // Time-related defaults
25
+ enableTime: false,
26
+ timeGranularity: 'minute',
27
+ timeFormat: '24h',
28
+ minTime: undefined,
29
+ maxTime: undefined,
30
+ timeStep: 1,
31
+ // Dropdown defaults
32
+ dropdownPlacement: 'bottom-end', // Position relative to calendar button (right side)
33
+ dropdownOffset: '0, 5',
34
+ dropdownBoundary: 'clippingParents',
35
+ dropdownWidth: 'auto', // 'auto' for fixed CSS width (20rem), 'toggle' to match input width, or custom string value
36
+ dropdownZindex: undefined,
37
+ dropdownContainer: undefined,
38
+ // Custom classes for template elements
39
+ classes: {},
40
+ };
@@ -0,0 +1,8 @@
1
+ /*
2
+ * config/index.ts - Configuration module exports
3
+ * Provides types, interfaces, and default configuration for KTDatepicker
4
+ */
5
+
6
+ export * from './types';
7
+ export * from './interfaces';
8
+ export * from './config';
@@ -0,0 +1,82 @@
1
+ /*
2
+ * interfaces.ts - Interfaces for KTDatepicker components
3
+ * Defines contracts between components to reduce coupling and improve testability.
4
+ */
5
+
6
+ import { KTDatepickerConfig, KTDatepickerState, KTDatepickerTemplateStrings } from './types';
7
+
8
+ /**
9
+ * Interface for UI rendering operations
10
+ */
11
+ export interface IUIRenderer {
12
+ renderContainer(): HTMLElement;
13
+ renderInputWrapper(calendarButtonHtml: string): HTMLElement;
14
+ renderDropdown(): HTMLElement;
15
+ renderDropdownContent(dropdownEl: HTMLElement): void;
16
+ renderCalendarButton(): string;
17
+ updateState(newState: KTDatepickerState): void;
18
+ updateConfig(newConfig: KTDatepickerConfig): void;
19
+ }
20
+
21
+ /**
22
+ * Interface for state management operations
23
+ */
24
+ export interface IStateManager {
25
+ isOpen(): boolean;
26
+ isTransitioning(): boolean;
27
+ isDisabled(): boolean;
28
+ open(source?: string): boolean;
29
+ close(source?: string): boolean;
30
+ toggle(source?: string): boolean;
31
+ enable(source?: string): boolean;
32
+ disable(source?: string): boolean;
33
+ setFocus(focused: boolean, source?: string): boolean;
34
+ getState(): any;
35
+ subscribe(observer: (event: any) => void): () => void;
36
+ dispose(): void;
37
+ }
38
+
39
+ /**
40
+ * Interface for event management operations
41
+ */
42
+ export interface IEventManager {
43
+ addListener(element: HTMLElement, event: string, handler: (e: Event) => void): void;
44
+ removeListener(element: HTMLElement, event: string, handler: (e: Event) => void): void;
45
+ removeAllListeners(element: HTMLElement): void;
46
+ }
47
+
48
+ /**
49
+ * Interface for dropdown positioning operations
50
+ */
51
+ export interface IDropdownManager {
52
+ open(): void;
53
+ close(): void;
54
+ isOpen(): boolean;
55
+ updatePosition(): void;
56
+ dispose(): void;
57
+ }
58
+
59
+ /**
60
+ * Interface for date selection operations
61
+ */
62
+ export interface IDateSelector {
63
+ selectDate(date: Date): void;
64
+ selectRange(start: Date, end: Date): void;
65
+ selectMultipleDates(dates: Date[]): void;
66
+ clearSelection(): void;
67
+ getSelectedDate(): Date | null;
68
+ getSelectedRange(): { start: Date | null; end: Date | null } | null;
69
+ getSelectedDates(): Date[];
70
+ }
71
+
72
+ /**
73
+ * Interface for input management operations
74
+ */
75
+ export interface IInputManager {
76
+ setValue(value: string): void;
77
+ getValue(): string;
78
+ setPlaceholder(placeholder: string): void;
79
+ setDisabled(disabled: boolean): void;
80
+ focus(): void;
81
+ blur(): void;
82
+ }
@@ -0,0 +1,188 @@
1
+ /*
2
+ * types.ts - Type definitions for KTDatepicker (revamp)
3
+ * Defines config, template, and state interfaces for modular, extensible template customization.
4
+ */
5
+
6
+ // Template keys for all customizable UI fragments
7
+ export type KTDatepickerTemplateKey =
8
+ | 'container'
9
+ | 'header'
10
+ | 'footer'
11
+ | 'calendarGrid'
12
+ | 'dayCell'
13
+ | 'monthYearSelect'
14
+ | 'monthSelection'
15
+ | 'yearSelection'
16
+ | 'inputWrapper'
17
+ | 'segmentedDateInput'
18
+ | 'segmentedDateRangeInput'
19
+ | 'placeholder'
20
+ | 'displayWrapper'
21
+ | 'displayElement'
22
+ | 'timePanel'
23
+ | 'multiDateTag'
24
+ | 'emptyState'
25
+ | 'calendarButton'
26
+ | 'dropdown'
27
+ | 'prevButton'
28
+ | 'nextButton'
29
+ | 'calendarTable'
30
+ | 'calendarRow'
31
+ | 'calendarBody'
32
+ | 'todayButton'
33
+ | 'clearButton'
34
+ | 'applyButton'
35
+ | 'multiMonthContainer'
36
+ | 'dateSegment'
37
+ | 'segmentSeparator'
38
+ | 'timePickerWrapper'
39
+ | 'timeDisplay'
40
+ | 'timeControls'
41
+ | 'timeUnit'
42
+ | 'timeUpButton'
43
+ | 'timeDownButton'
44
+ | 'timeValue'
45
+ | 'timeSeparator'
46
+ | 'ampmControl'
47
+ | 'ampmButton'
48
+ | 'panel'
49
+ | 'liveRegion'
50
+ | 'rangeStartContainer'
51
+ | 'rangeEndContainer';
52
+
53
+ // Template string map
54
+ export type KTDatepickerTemplateStrings = {
55
+ [K in KTDatepickerTemplateKey]?: string | ((data: any) => string);
56
+ };
57
+
58
+ /**
59
+ * Configuration options for KTDatepicker
60
+ */
61
+ export interface KTDatepickerConfig {
62
+ /** Custom templates for UI fragments */
63
+ templates?: KTDatepickerTemplateStrings;
64
+ /** Date format string (e.g. 'yyyy-mm-dd', 'dd/mm/yyyy') */
65
+ format?: string;
66
+ /** Enable date range selection */
67
+ range?: boolean;
68
+ /** Enable multi-date selection */
69
+ multiDate?: boolean;
70
+ /** Minimum selectable date */
71
+ minDate?: Date | string;
72
+ /** Maximum selectable date */
73
+ maxDate?: Date | string;
74
+ /** Disable the datepicker */
75
+ disabled?: boolean;
76
+ /** Locale for date formatting (e.g. 'en-US') */
77
+ locale?: string;
78
+ /** Placeholder text for the input */
79
+ placeholder?: string;
80
+ /** Initial selected date */
81
+ value?: Date | string;
82
+ /** Initial selected date range */
83
+ valueRange?: { start: Date | string; end: Date | string };
84
+ /** Initial selected dates (multi-date) */
85
+ values?: (Date | string)[];
86
+ /** Custom class for the root element */
87
+ className?: string;
88
+ /** Whether to show the calendar on input focus (default: true) */
89
+ showOnFocus?: boolean;
90
+ /** Whether to close the calendar on date selection (default: true) */
91
+ closeOnSelect?: boolean;
92
+ /** Whether to close the calendar when clicking outside (default: true) */
93
+ closeOnOutsideClick?: boolean;
94
+ /**
95
+ * Number of calendar months to display side-by-side (horizontal). Default: 1.
96
+ */
97
+ visibleMonths?: number;
98
+ /** Enable time selection (default: false) */
99
+ enableTime?: boolean;
100
+ /** Time granularity - smallest unit to display (default: 'minute') */
101
+ timeGranularity?: 'second' | 'minute' | 'hour';
102
+ /** Time format - 12 or 24 hour (default: '24h') */
103
+ timeFormat?: '12h' | '24h';
104
+ /** Minimum time constraint (format: 'HH:MM' or 'HH:MM:SS') */
105
+ minTime?: string;
106
+ /** Maximum time constraint (format: 'HH:MM' or 'HH:MM:SS') */
107
+ maxTime?: string;
108
+ /** Time step increment in minutes (default: 1) */
109
+ timeStep?: number;
110
+ /** Dropdown positioning and behavior options */
111
+ dropdownPlacement?: string;
112
+ dropdownOffset?: string;
113
+ dropdownBoundary?: string;
114
+ dropdownWidth?: 'auto' | 'toggle' | string;
115
+ dropdownZindex?: number;
116
+ dropdownContainer?: string;
117
+ /** Custom classes for template elements */
118
+ classes?: {
119
+ container?: string;
120
+ header?: string;
121
+ footer?: string;
122
+ calendarGrid?: string;
123
+ dayCell?: string;
124
+ monthYearSelect?: string;
125
+ monthSelection?: string;
126
+ yearSelection?: string;
127
+ inputWrapper?: string;
128
+ segmentedDateInput?: string;
129
+ segmentedDateRangeInput?: string;
130
+ dateSegment?: string;
131
+ segmentSeparator?: string;
132
+ placeholder?: string;
133
+ displayWrapper?: string;
134
+ displayElement?: string;
135
+ timePanel?: string;
136
+ multiDateTag?: string;
137
+ emptyState?: string;
138
+ calendarButton?: string;
139
+ dropdown?: string;
140
+ prevButton?: string;
141
+ nextButton?: string;
142
+ calendarTable?: string;
143
+ calendarRow?: string;
144
+ calendarBody?: string;
145
+ todayButton?: string;
146
+ clearButton?: string;
147
+ applyButton?: string;
148
+ multiMonthContainer?: string;
149
+ };
150
+ /** Any additional custom config options */
151
+ [key: string]: any;
152
+ }
153
+
154
+ // Time state interface
155
+ export interface TimeState {
156
+ hour: number;
157
+ minute: number;
158
+ second: number;
159
+ }
160
+
161
+ // State interface for KTDatepicker
162
+ /**
163
+ * Dropdown state interface
164
+ */
165
+ export interface DropdownState {
166
+ isOpen: boolean;
167
+ isTransitioning: boolean;
168
+ isDisabled: boolean;
169
+ isFocused: boolean;
170
+ }
171
+
172
+ export interface KTDatepickerState {
173
+ currentDate: Date;
174
+ selectedDate: Date | null;
175
+ selectedRange: { start: Date | null; end: Date | null } | null;
176
+ selectedDates: Date[];
177
+ selectedTime: TimeState | null;
178
+ timeGranularity: 'second' | 'minute' | 'hour';
179
+ viewMode: 'days' | 'months' | 'years';
180
+ isOpen: boolean;
181
+ isFocused: boolean;
182
+ isTransitioning: boolean;
183
+ isDisabled: boolean;
184
+ validationErrors: string[];
185
+ isValid: boolean;
186
+ // Dropdown state (consolidated from legacy state manager)
187
+ dropdownState: DropdownState;
188
+ }