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