@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,747 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
* @version: 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
// utils.ts
|
|
7
|
+
|
|
8
|
+
import { KTSelect } from './select';
|
|
9
|
+
import { defaultTemplates, getTemplateStrings } from './templates';
|
|
10
|
+
import { KTSelectConfigInterface } from './config';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Format a number as a currency string
|
|
14
|
+
*/
|
|
15
|
+
export function formatCurrency(value: number): string {
|
|
16
|
+
return new Intl.NumberFormat('en-US', {
|
|
17
|
+
style: 'currency',
|
|
18
|
+
currency: 'USD',
|
|
19
|
+
}).format(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Filter options based on a search query
|
|
24
|
+
*/
|
|
25
|
+
export function filterOptions(
|
|
26
|
+
options: HTMLElement[],
|
|
27
|
+
query: string,
|
|
28
|
+
config: KTSelectConfigInterface,
|
|
29
|
+
dropdownElement: HTMLElement,
|
|
30
|
+
onVisibleCount?: (count: number) => void,
|
|
31
|
+
): number {
|
|
32
|
+
let visibleOptionsCount = 0;
|
|
33
|
+
|
|
34
|
+
// Clear existing "no results" messages
|
|
35
|
+
const noResultsElement = dropdownElement.querySelector(
|
|
36
|
+
'[data-kt-select-no-results]',
|
|
37
|
+
);
|
|
38
|
+
if (noResultsElement) {
|
|
39
|
+
noResultsElement.remove();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// For empty query, ensure highlights are cleared from all options
|
|
43
|
+
if (!query || query.trim() === '') {
|
|
44
|
+
// Just make all options visible without highlighting
|
|
45
|
+
for (const option of options) {
|
|
46
|
+
// Make option visible by removing hidden classes and inline styles
|
|
47
|
+
option.classList.remove('hidden');
|
|
48
|
+
|
|
49
|
+
// Clean up any inline display styles from legacy code
|
|
50
|
+
if (
|
|
51
|
+
option.hasAttribute('style') &&
|
|
52
|
+
option.getAttribute('style').includes('display:')
|
|
53
|
+
) {
|
|
54
|
+
const styleAttr = option.getAttribute('style');
|
|
55
|
+
if (
|
|
56
|
+
styleAttr.trim() === 'display: none;' ||
|
|
57
|
+
styleAttr.trim() === 'display: block;'
|
|
58
|
+
) {
|
|
59
|
+
option.removeAttribute('style');
|
|
60
|
+
} else {
|
|
61
|
+
option.setAttribute(
|
|
62
|
+
'style',
|
|
63
|
+
styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
visibleOptionsCount++;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Call the callback with the visible count if provided
|
|
71
|
+
if (onVisibleCount) {
|
|
72
|
+
onVisibleCount(visibleOptionsCount);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return visibleOptionsCount;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Filter options based on query
|
|
79
|
+
for (const option of options) {
|
|
80
|
+
const optionText = option.textContent?.toLowerCase() || '';
|
|
81
|
+
const isMatch = optionText.includes(query.toLowerCase());
|
|
82
|
+
|
|
83
|
+
if (isMatch || query.trim() === '') {
|
|
84
|
+
// Show option by removing the hidden class and any display inline styles
|
|
85
|
+
option.classList.remove('hidden');
|
|
86
|
+
|
|
87
|
+
// Remove any inline display styles that might be present
|
|
88
|
+
if (
|
|
89
|
+
option.hasAttribute('style') &&
|
|
90
|
+
option.getAttribute('style').includes('display:')
|
|
91
|
+
) {
|
|
92
|
+
const styleAttr = option.getAttribute('style');
|
|
93
|
+
if (
|
|
94
|
+
styleAttr.trim() === 'display: none;' ||
|
|
95
|
+
styleAttr.trim() === 'display: block;'
|
|
96
|
+
) {
|
|
97
|
+
option.removeAttribute('style');
|
|
98
|
+
} else {
|
|
99
|
+
option.setAttribute(
|
|
100
|
+
'style',
|
|
101
|
+
styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
visibleOptionsCount++;
|
|
107
|
+
|
|
108
|
+
// Apply highlighting if needed - but preserve the option structure
|
|
109
|
+
if (isMatch && config.searchHighlight && query.trim() !== '') {
|
|
110
|
+
// Clone option elements that contain icons or descriptions
|
|
111
|
+
const hasIcon =
|
|
112
|
+
option.querySelector('[data-kt-select-option-icon]') !== null;
|
|
113
|
+
const hasDescription =
|
|
114
|
+
option.querySelector('[data-kt-select-option-description]') !== null;
|
|
115
|
+
|
|
116
|
+
if (hasIcon || hasDescription) {
|
|
117
|
+
// Only highlight the text part without changing structure
|
|
118
|
+
const titleElement = option.querySelector(
|
|
119
|
+
'[data-kt-option-title]',
|
|
120
|
+
) as HTMLElement;
|
|
121
|
+
if (titleElement) {
|
|
122
|
+
highlightTextInElement(titleElement, query, config);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
// Simple option with just text - standard highlighting
|
|
126
|
+
highlightTextInElement(option, query, config);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
// Hide option using hidden class
|
|
131
|
+
option.classList.add('hidden');
|
|
132
|
+
|
|
133
|
+
// Remove any inline display styles
|
|
134
|
+
if (
|
|
135
|
+
option.hasAttribute('style') &&
|
|
136
|
+
option.getAttribute('style').includes('display:')
|
|
137
|
+
) {
|
|
138
|
+
const styleAttr = option.getAttribute('style');
|
|
139
|
+
if (
|
|
140
|
+
styleAttr.trim() === 'display: none;' ||
|
|
141
|
+
styleAttr.trim() === 'display: block;'
|
|
142
|
+
) {
|
|
143
|
+
option.removeAttribute('style');
|
|
144
|
+
} else {
|
|
145
|
+
option.setAttribute(
|
|
146
|
+
'style',
|
|
147
|
+
styleAttr.replace(/display:\s*[^;]+;?/gi, '').trim(),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Early exit if maxItems limit is reached
|
|
154
|
+
if (config.searchMaxItems && visibleOptionsCount >= config.searchMaxItems) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Call the callback with the visible count if provided
|
|
160
|
+
if (onVisibleCount) {
|
|
161
|
+
onVisibleCount(visibleOptionsCount);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return visibleOptionsCount;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Highlight text only within a specific element, preserving other elements
|
|
169
|
+
*/
|
|
170
|
+
export function highlightTextInElement(
|
|
171
|
+
element: HTMLElement,
|
|
172
|
+
query: string,
|
|
173
|
+
config: KTSelectConfigInterface,
|
|
174
|
+
): void {
|
|
175
|
+
if (!element || !query || query.trim() === '') return;
|
|
176
|
+
|
|
177
|
+
const queryLower = query.toLowerCase();
|
|
178
|
+
|
|
179
|
+
function walk(node: Node) {
|
|
180
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
181
|
+
const text = node.nodeValue || '';
|
|
182
|
+
const textLower = text.toLowerCase();
|
|
183
|
+
const matchIndex = textLower.indexOf(queryLower);
|
|
184
|
+
|
|
185
|
+
if (matchIndex !== -1) {
|
|
186
|
+
const before = text.slice(0, matchIndex);
|
|
187
|
+
const match = text.slice(matchIndex, matchIndex + query.length);
|
|
188
|
+
const after = text.slice(matchIndex + query.length);
|
|
189
|
+
|
|
190
|
+
const frag = document.createDocumentFragment();
|
|
191
|
+
if (before) frag.appendChild(document.createTextNode(before));
|
|
192
|
+
|
|
193
|
+
// Use the highlight template, which returns an HTMLElement
|
|
194
|
+
const highlightSpan = defaultTemplates.highlight(config, match);
|
|
195
|
+
frag.appendChild(highlightSpan);
|
|
196
|
+
|
|
197
|
+
if (after) frag.appendChild(document.createTextNode(after));
|
|
198
|
+
|
|
199
|
+
node.parentNode?.replaceChild(frag, node);
|
|
200
|
+
// Only highlight the first occurrence in this node
|
|
201
|
+
}
|
|
202
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
203
|
+
// Don't re-highlight already highlighted nodes
|
|
204
|
+
if ((node as HTMLElement).classList.contains('highlight')) return;
|
|
205
|
+
Array.from(node.childNodes).forEach(walk);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
walk(element);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Focus manager for keyboard navigation
|
|
213
|
+
* Consolidates redundant focus management logic into shared functions
|
|
214
|
+
*/
|
|
215
|
+
export class FocusManager {
|
|
216
|
+
private _element: HTMLElement;
|
|
217
|
+
private _optionsSelector: string;
|
|
218
|
+
private _focusedOptionIndex: number | null = null;
|
|
219
|
+
private _focusClass: string;
|
|
220
|
+
private _hoverClass: string;
|
|
221
|
+
private _bgClass: string;
|
|
222
|
+
private _fontClass: string;
|
|
223
|
+
private _eventManager: EventManager;
|
|
224
|
+
|
|
225
|
+
constructor(
|
|
226
|
+
element: HTMLElement,
|
|
227
|
+
optionsSelector: string = '[data-kt-select-option]',
|
|
228
|
+
config?: KTSelectConfigInterface,
|
|
229
|
+
) {
|
|
230
|
+
this._element = element;
|
|
231
|
+
this._optionsSelector = optionsSelector;
|
|
232
|
+
this._eventManager = new EventManager();
|
|
233
|
+
|
|
234
|
+
// Use config values if provided, otherwise fallback to defaults
|
|
235
|
+
this._focusClass = config?.focusClass || 'option-focused';
|
|
236
|
+
this._hoverClass = config?.hoverClass || 'hovered';
|
|
237
|
+
this._bgClass = config?.bgClass || 'bg-blue-50';
|
|
238
|
+
this._fontClass = config?.fontClass || 'font-medium';
|
|
239
|
+
|
|
240
|
+
// Add click handler to update focus state when options are clicked
|
|
241
|
+
this._setupOptionClickHandlers();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Set up click handlers for all options to update focus state
|
|
246
|
+
*/
|
|
247
|
+
private _setupOptionClickHandlers(): void {
|
|
248
|
+
// Add click handler to the options container
|
|
249
|
+
this._eventManager.addListener(this._element, 'click', (e: Event) => {
|
|
250
|
+
const target = e.target as HTMLElement;
|
|
251
|
+
const optionElement = target.closest(this._optionsSelector);
|
|
252
|
+
|
|
253
|
+
if (optionElement) {
|
|
254
|
+
// First clear all focus
|
|
255
|
+
this.resetFocus();
|
|
256
|
+
|
|
257
|
+
// Then update the focused index based on the clicked option
|
|
258
|
+
const options = this.getVisibleOptions();
|
|
259
|
+
const clickedIndex = options.indexOf(optionElement as HTMLElement);
|
|
260
|
+
|
|
261
|
+
if (clickedIndex >= 0) {
|
|
262
|
+
this._focusedOptionIndex = clickedIndex;
|
|
263
|
+
this.applyFocus(options[clickedIndex]);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get all visible options
|
|
271
|
+
*/
|
|
272
|
+
public getVisibleOptions(): HTMLElement[] {
|
|
273
|
+
return Array.from(
|
|
274
|
+
this._element.querySelectorAll(this._optionsSelector),
|
|
275
|
+
).filter((option) => {
|
|
276
|
+
const element = option as HTMLElement;
|
|
277
|
+
// Check only for hidden class
|
|
278
|
+
if (element.classList.contains('hidden')) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
// Also check inline styles for backward compatibility
|
|
282
|
+
if (element.style.display === 'none') {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
}) as HTMLElement[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Focus the next visible option
|
|
291
|
+
*/
|
|
292
|
+
public focusNext(): HTMLElement | null {
|
|
293
|
+
const options = this.getVisibleOptions();
|
|
294
|
+
if (options.length === 0) return null;
|
|
295
|
+
|
|
296
|
+
this.resetFocus();
|
|
297
|
+
|
|
298
|
+
if (this._focusedOptionIndex === null) {
|
|
299
|
+
this._focusedOptionIndex = 0;
|
|
300
|
+
} else {
|
|
301
|
+
this._focusedOptionIndex =
|
|
302
|
+
(this._focusedOptionIndex + 1) % options.length;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const option = options[this._focusedOptionIndex];
|
|
306
|
+
this.applyFocus(option);
|
|
307
|
+
this.scrollIntoView(option);
|
|
308
|
+
|
|
309
|
+
return option;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Focus the previous visible option
|
|
314
|
+
*/
|
|
315
|
+
public focusPrevious(): HTMLElement | null {
|
|
316
|
+
const options = this.getVisibleOptions();
|
|
317
|
+
if (options.length === 0) return null;
|
|
318
|
+
|
|
319
|
+
this.resetFocus();
|
|
320
|
+
|
|
321
|
+
if (this._focusedOptionIndex === null) {
|
|
322
|
+
this._focusedOptionIndex = options.length - 1;
|
|
323
|
+
} else {
|
|
324
|
+
this._focusedOptionIndex =
|
|
325
|
+
(this._focusedOptionIndex - 1 + options.length) % options.length;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const option = options[this._focusedOptionIndex];
|
|
329
|
+
this.applyFocus(option);
|
|
330
|
+
this.scrollIntoView(option);
|
|
331
|
+
|
|
332
|
+
return option;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Apply focus to a specific option
|
|
337
|
+
*/
|
|
338
|
+
public applyFocus(option: HTMLElement): void {
|
|
339
|
+
if (!option) return;
|
|
340
|
+
|
|
341
|
+
// Remove focus from all options first
|
|
342
|
+
this.resetFocus();
|
|
343
|
+
|
|
344
|
+
// Add focus to this option
|
|
345
|
+
option.classList.add(this._focusClass);
|
|
346
|
+
option.classList.add(this._hoverClass);
|
|
347
|
+
option.classList.add(this._bgClass);
|
|
348
|
+
option.classList.add(this._fontClass);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Reset focus on all options
|
|
353
|
+
*/
|
|
354
|
+
public resetFocus(): void {
|
|
355
|
+
// Find all elements with the focus classes
|
|
356
|
+
const focusedElements = this._element.querySelectorAll(
|
|
357
|
+
`.${this._focusClass}, .${this._hoverClass}, .${this._bgClass}, .${this._fontClass}`,
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Remove classes from all elements
|
|
361
|
+
focusedElements.forEach((element) => {
|
|
362
|
+
element.classList.remove(this._focusClass);
|
|
363
|
+
element.classList.remove(this._hoverClass);
|
|
364
|
+
element.classList.remove(this._bgClass);
|
|
365
|
+
element.classList.remove(this._fontClass);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Reset index if visible options have changed
|
|
369
|
+
const visibleOptions = this.getVisibleOptions();
|
|
370
|
+
if (
|
|
371
|
+
this._focusedOptionIndex !== null &&
|
|
372
|
+
this._focusedOptionIndex >= visibleOptions.length
|
|
373
|
+
) {
|
|
374
|
+
this._focusedOptionIndex = null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Ensure the focused option is visible in the scrollable container
|
|
380
|
+
*/
|
|
381
|
+
public scrollIntoView(option: HTMLElement): void {
|
|
382
|
+
if (!option) return;
|
|
383
|
+
|
|
384
|
+
const container = this._element.querySelector(
|
|
385
|
+
'[data-kt-select-options-container]',
|
|
386
|
+
);
|
|
387
|
+
if (!container) return;
|
|
388
|
+
|
|
389
|
+
const optionRect = option.getBoundingClientRect();
|
|
390
|
+
const containerRect = container.getBoundingClientRect();
|
|
391
|
+
|
|
392
|
+
// Check if option is below the visible area
|
|
393
|
+
if (optionRect.bottom > containerRect.bottom) {
|
|
394
|
+
option.scrollIntoView({ block: 'end', behavior: 'smooth' });
|
|
395
|
+
}
|
|
396
|
+
// Check if option is above the visible area
|
|
397
|
+
else if (optionRect.top < containerRect.top) {
|
|
398
|
+
option.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Focus a specific option by its value
|
|
404
|
+
*/
|
|
405
|
+
public focusOptionByValue(value: string): boolean {
|
|
406
|
+
const options = this.getVisibleOptions();
|
|
407
|
+
const index = options.findIndex((option) => option.dataset.value === value);
|
|
408
|
+
|
|
409
|
+
if (index >= 0) {
|
|
410
|
+
this._focusedOptionIndex = index;
|
|
411
|
+
this.applyFocus(options[index]);
|
|
412
|
+
this.scrollIntoView(options[index]);
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Get the currently focused option
|
|
421
|
+
*/
|
|
422
|
+
public getFocusedOption(): HTMLElement | null {
|
|
423
|
+
const options = this.getVisibleOptions();
|
|
424
|
+
|
|
425
|
+
if (
|
|
426
|
+
this._focusedOptionIndex !== null &&
|
|
427
|
+
this._focusedOptionIndex < options.length
|
|
428
|
+
) {
|
|
429
|
+
return options[this._focusedOptionIndex];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Get the index of the currently focused option
|
|
437
|
+
*/
|
|
438
|
+
public getFocusedIndex(): number | null {
|
|
439
|
+
return this._focusedOptionIndex;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Set the focused option index directly
|
|
444
|
+
*/
|
|
445
|
+
public setFocusedIndex(index: number | null): void {
|
|
446
|
+
this._focusedOptionIndex = index;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Clean up event listeners
|
|
451
|
+
*/
|
|
452
|
+
public dispose(): void {
|
|
453
|
+
if (this._eventManager) {
|
|
454
|
+
this._eventManager.removeAllListeners(this._element);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Shared keyboard navigation handler for dropdown options
|
|
461
|
+
* Can be used by both combobox and search modules
|
|
462
|
+
*/
|
|
463
|
+
export function handleDropdownKeyNavigation(
|
|
464
|
+
event: KeyboardEvent,
|
|
465
|
+
select: KTSelect,
|
|
466
|
+
config: {
|
|
467
|
+
multiple?: boolean;
|
|
468
|
+
closeOnSelect?: boolean;
|
|
469
|
+
},
|
|
470
|
+
callbacks?: {
|
|
471
|
+
onArrowUp?: () => void;
|
|
472
|
+
onArrowDown?: () => void;
|
|
473
|
+
onEnter?: () => void;
|
|
474
|
+
onClose?: () => void;
|
|
475
|
+
},
|
|
476
|
+
): void {
|
|
477
|
+
try {
|
|
478
|
+
// Get the dropdown state
|
|
479
|
+
const isDropdownOpen = (select as any)._dropdownIsOpen;
|
|
480
|
+
|
|
481
|
+
// Log the event to help debug
|
|
482
|
+
const origin = 'handleDropdownKeyNavigation';
|
|
483
|
+
if (select.getConfig && select.getConfig().debug)
|
|
484
|
+
console.log(
|
|
485
|
+
`[${origin}] Key: ${event.key}, Dropdown open: ${isDropdownOpen}`,
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
// Handle basic keyboard navigation
|
|
489
|
+
switch (event.key) {
|
|
490
|
+
case 'ArrowDown':
|
|
491
|
+
if (!isDropdownOpen) {
|
|
492
|
+
if (select.getConfig && select.getConfig().debug)
|
|
493
|
+
console.log(`[${origin}] Opening dropdown on ArrowDown`);
|
|
494
|
+
select.openDropdown();
|
|
495
|
+
|
|
496
|
+
// Focus the first option after opening
|
|
497
|
+
setTimeout(() => {
|
|
498
|
+
(select as any)._focusNextOption();
|
|
499
|
+
}, 50);
|
|
500
|
+
} else if (callbacks?.onArrowDown) {
|
|
501
|
+
if (select.getConfig && select.getConfig().debug)
|
|
502
|
+
console.log(`[${origin}] Using custom onArrowDown callback`);
|
|
503
|
+
callbacks.onArrowDown();
|
|
504
|
+
} else {
|
|
505
|
+
if (select.getConfig && select.getConfig().debug)
|
|
506
|
+
console.log(`[${origin}] Using default _focusNextOption`);
|
|
507
|
+
const focusedOption = (select as any)._focusNextOption();
|
|
508
|
+
|
|
509
|
+
// Ensure we have a focused option
|
|
510
|
+
if (focusedOption) {
|
|
511
|
+
if (select.getConfig && select.getConfig().debug)
|
|
512
|
+
console.log(`[${origin}] Focused next option:`, focusedOption);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
event.preventDefault();
|
|
516
|
+
break;
|
|
517
|
+
|
|
518
|
+
case 'ArrowUp':
|
|
519
|
+
if (!isDropdownOpen) {
|
|
520
|
+
if (select.getConfig && select.getConfig().debug)
|
|
521
|
+
console.log(`[${origin}] Opening dropdown on ArrowUp`);
|
|
522
|
+
select.openDropdown();
|
|
523
|
+
|
|
524
|
+
// Focus the last option after opening
|
|
525
|
+
setTimeout(() => {
|
|
526
|
+
(select as any)._focusPreviousOption();
|
|
527
|
+
}, 50);
|
|
528
|
+
} else if (callbacks?.onArrowUp) {
|
|
529
|
+
if (select.getConfig && select.getConfig().debug)
|
|
530
|
+
console.log(`[${origin}] Using custom onArrowUp callback`);
|
|
531
|
+
callbacks.onArrowUp();
|
|
532
|
+
} else {
|
|
533
|
+
if (select.getConfig && select.getConfig().debug)
|
|
534
|
+
console.log(`[${origin}] Using default _focusPreviousOption`);
|
|
535
|
+
const focusedOption = (select as any)._focusPreviousOption();
|
|
536
|
+
|
|
537
|
+
// Ensure we have a focused option
|
|
538
|
+
if (focusedOption) {
|
|
539
|
+
if (select.getConfig && select.getConfig().debug)
|
|
540
|
+
console.log(
|
|
541
|
+
`[${origin}] Focused previous option:`,
|
|
542
|
+
focusedOption,
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
event.preventDefault();
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case 'Enter':
|
|
550
|
+
// Prevent form submission
|
|
551
|
+
event.preventDefault();
|
|
552
|
+
|
|
553
|
+
if (isDropdownOpen) {
|
|
554
|
+
if (select.getConfig && select.getConfig().debug)
|
|
555
|
+
console.log(`[${origin}] Enter pressed with dropdown open`);
|
|
556
|
+
|
|
557
|
+
// For combobox mode, ensure we update the input value directly
|
|
558
|
+
const isCombobox = select.getConfig().mode === 'combobox';
|
|
559
|
+
const comboboxModule = (select as any)._comboboxModule;
|
|
560
|
+
|
|
561
|
+
if (callbacks?.onEnter) {
|
|
562
|
+
if (select.getConfig && select.getConfig().debug)
|
|
563
|
+
console.log(`[${origin}] Using custom onEnter callback`);
|
|
564
|
+
callbacks.onEnter();
|
|
565
|
+
} else {
|
|
566
|
+
if (select.getConfig && select.getConfig().debug)
|
|
567
|
+
console.log(`[${origin}] Using default selectFocusedOption`);
|
|
568
|
+
// Make sure there is a focused option before trying to select it
|
|
569
|
+
if (
|
|
570
|
+
(select as any)._focusManager &&
|
|
571
|
+
(select as any)._focusManager.getFocusedOption()
|
|
572
|
+
) {
|
|
573
|
+
select.selectFocusedOption();
|
|
574
|
+
} else {
|
|
575
|
+
// If no option is focused, try to focus the first one
|
|
576
|
+
const focusedOption = (select as any)._focusNextOption();
|
|
577
|
+
// Only select if an option was successfully focused
|
|
578
|
+
if (focusedOption) {
|
|
579
|
+
select.selectFocusedOption();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Close dropdown after selection if not multiple and closeOnSelect is true
|
|
585
|
+
if (!config.multiple && config.closeOnSelect !== false) {
|
|
586
|
+
if (select.getConfig && select.getConfig().debug)
|
|
587
|
+
console.log(`[${origin}] Closing dropdown after selection`);
|
|
588
|
+
select.closeDropdown();
|
|
589
|
+
}
|
|
590
|
+
} else {
|
|
591
|
+
// If dropdown is closed, open it on Enter
|
|
592
|
+
if (select.getConfig && select.getConfig().debug)
|
|
593
|
+
console.log(`[${origin}] Opening dropdown on Enter`);
|
|
594
|
+
select.openDropdown();
|
|
595
|
+
|
|
596
|
+
// Focus the first option after opening
|
|
597
|
+
setTimeout(() => {
|
|
598
|
+
(select as any)._focusNextOption();
|
|
599
|
+
}, 50);
|
|
600
|
+
}
|
|
601
|
+
break;
|
|
602
|
+
|
|
603
|
+
case 'Tab':
|
|
604
|
+
// Only handle tab if dropdown is open
|
|
605
|
+
if (isDropdownOpen) {
|
|
606
|
+
if (select.getConfig && select.getConfig().debug)
|
|
607
|
+
console.log(`[${origin}] Closing dropdown on Tab`);
|
|
608
|
+
select.closeDropdown();
|
|
609
|
+
if (callbacks?.onClose) {
|
|
610
|
+
callbacks.onClose();
|
|
611
|
+
}
|
|
612
|
+
// Don't prevent default tab behavior - let it move focus naturally
|
|
613
|
+
}
|
|
614
|
+
break;
|
|
615
|
+
|
|
616
|
+
case 'Escape':
|
|
617
|
+
// Only handle escape if dropdown is open
|
|
618
|
+
if (isDropdownOpen) {
|
|
619
|
+
if (select.getConfig && select.getConfig().debug)
|
|
620
|
+
console.log(`[${origin}] Closing dropdown on Escape`);
|
|
621
|
+
select.closeDropdown();
|
|
622
|
+
if (callbacks?.onClose) {
|
|
623
|
+
callbacks.onClose();
|
|
624
|
+
}
|
|
625
|
+
event.preventDefault(); // Prevent other escape handlers
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
|
|
629
|
+
case ' ': // Space key
|
|
630
|
+
// If dropdown is closed, space should open it (but not if in combobox mode)
|
|
631
|
+
if (!isDropdownOpen && !(select.getConfig().mode === 'combobox')) {
|
|
632
|
+
if (select.getConfig && select.getConfig().debug)
|
|
633
|
+
console.log(`[${origin}] Opening dropdown on Space`);
|
|
634
|
+
select.openDropdown();
|
|
635
|
+
|
|
636
|
+
// Focus the first option after opening
|
|
637
|
+
setTimeout(() => {
|
|
638
|
+
(select as any)._focusNextOption();
|
|
639
|
+
}, 50);
|
|
640
|
+
|
|
641
|
+
event.preventDefault();
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
} catch (error) {
|
|
646
|
+
console.error('Error in keyboard navigation handler:', error);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Centralized event listener management
|
|
652
|
+
*/
|
|
653
|
+
export class EventManager {
|
|
654
|
+
private _boundHandlers: Map<
|
|
655
|
+
string,
|
|
656
|
+
Map<EventListenerOrEventListenerObject, EventListenerOrEventListenerObject>
|
|
657
|
+
> = new Map();
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Add an event listener with a bound context
|
|
661
|
+
*/
|
|
662
|
+
public addListener(
|
|
663
|
+
element: HTMLElement,
|
|
664
|
+
event: string,
|
|
665
|
+
handler: EventListenerOrEventListenerObject,
|
|
666
|
+
context?: any,
|
|
667
|
+
): void {
|
|
668
|
+
if (!element) return;
|
|
669
|
+
|
|
670
|
+
// Create a bound version of the handler if context provided
|
|
671
|
+
const boundHandler: EventListenerOrEventListenerObject =
|
|
672
|
+
context && typeof handler === 'function'
|
|
673
|
+
? handler.bind(context)
|
|
674
|
+
: handler;
|
|
675
|
+
|
|
676
|
+
// Store the relationship between original and bound handler
|
|
677
|
+
if (!this._boundHandlers.has(event)) {
|
|
678
|
+
this._boundHandlers.set(event, new Map());
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const eventMap = this._boundHandlers.get(event)!;
|
|
682
|
+
eventMap.set(handler, boundHandler);
|
|
683
|
+
|
|
684
|
+
// Add the event listener
|
|
685
|
+
element.addEventListener(event, boundHandler);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Remove an event listener
|
|
690
|
+
*/
|
|
691
|
+
public removeListener(
|
|
692
|
+
element: HTMLElement,
|
|
693
|
+
event: string,
|
|
694
|
+
handler: EventListenerOrEventListenerObject,
|
|
695
|
+
): void {
|
|
696
|
+
if (!element) return;
|
|
697
|
+
|
|
698
|
+
const eventMap = this._boundHandlers.get(event);
|
|
699
|
+
if (!eventMap) return;
|
|
700
|
+
|
|
701
|
+
// Get the bound version of the handler
|
|
702
|
+
const boundHandler = eventMap.get(handler);
|
|
703
|
+
if (!boundHandler) return;
|
|
704
|
+
|
|
705
|
+
// Remove the event listener
|
|
706
|
+
element.removeEventListener(event, boundHandler);
|
|
707
|
+
|
|
708
|
+
// Clean up the map
|
|
709
|
+
eventMap.delete(handler);
|
|
710
|
+
if (eventMap.size === 0) {
|
|
711
|
+
this._boundHandlers.delete(event);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Remove all event listeners
|
|
717
|
+
*/
|
|
718
|
+
public removeAllListeners(element: HTMLElement): void {
|
|
719
|
+
if (!element) return;
|
|
720
|
+
|
|
721
|
+
// Go through each event type
|
|
722
|
+
this._boundHandlers.forEach((eventMap, event) => {
|
|
723
|
+
// For each event type, go through each handler
|
|
724
|
+
eventMap.forEach((boundHandler, originalHandler) => {
|
|
725
|
+
element.removeEventListener(event, boundHandler);
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// Clear the maps
|
|
730
|
+
this._boundHandlers.clear();
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Debounce function to limit how often a function can be called
|
|
736
|
+
*/
|
|
737
|
+
export function debounce(
|
|
738
|
+
func: (...args: any[]) => void,
|
|
739
|
+
delay: number,
|
|
740
|
+
): (...args: any[]) => void {
|
|
741
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
742
|
+
|
|
743
|
+
return function (...args: any[]) {
|
|
744
|
+
clearTimeout(timeout);
|
|
745
|
+
timeout = setTimeout(() => func(...args), delay);
|
|
746
|
+
};
|
|
747
|
+
}
|