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