@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,1657 @@
|
|
|
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 KTComponent from '../component';
|
|
7
|
+
import {
|
|
8
|
+
KTDataTableDataInterface,
|
|
9
|
+
KTDataTableInterface,
|
|
10
|
+
KTDataTableConfigInterface as KTDataTableConfigInterface,
|
|
11
|
+
KTDataTableSortOrderInterface,
|
|
12
|
+
KTDataTableStateInterface,
|
|
13
|
+
KTDataTableColumnFilterInterface,
|
|
14
|
+
KTDataTableAttributeInterface,
|
|
15
|
+
} from './types';
|
|
16
|
+
import KTUtils from '../../helpers/utils';
|
|
17
|
+
import KTComponents from '../../index';
|
|
18
|
+
import KTData from '../../helpers/data';
|
|
19
|
+
import {
|
|
20
|
+
createCheckboxHandler,
|
|
21
|
+
KTDataTableCheckboxAPI,
|
|
22
|
+
} from './datatable-checkbox';
|
|
23
|
+
import { createSortHandler, KTDataTableSortAPI } from './datatable-sort';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Custom DataTable plugin class with server-side API, pagination, and sorting
|
|
27
|
+
* @classdesc A custom KTComponent class that integrates server-side API, pagination, and sorting functionality into a table.
|
|
28
|
+
* It supports fetching data from a server-side API, pagination, and sorting of the fetched data.
|
|
29
|
+
* @class
|
|
30
|
+
* @extends {KTComponent}
|
|
31
|
+
* @param {HTMLElement} element The table element
|
|
32
|
+
* @param {KTDataTableConfigInterface} [config] Additional configuration options
|
|
33
|
+
*/
|
|
34
|
+
export class KTDataTable<T extends KTDataTableDataInterface>
|
|
35
|
+
extends KTComponent
|
|
36
|
+
implements KTDataTableInterface
|
|
37
|
+
{
|
|
38
|
+
protected override _name: string = 'datatable';
|
|
39
|
+
protected override _config: KTDataTableConfigInterface;
|
|
40
|
+
protected override _defaultConfig: KTDataTableConfigInterface;
|
|
41
|
+
|
|
42
|
+
private _tableElement: HTMLTableElement;
|
|
43
|
+
private _tbodyElement: HTMLTableSectionElement;
|
|
44
|
+
private _theadElement: HTMLTableSectionElement;
|
|
45
|
+
private _originalTbodyClass: string = ''; // Store original tbody class
|
|
46
|
+
private _originalTrClasses: string[] = []; // Store original tr classes
|
|
47
|
+
private _originalTheadClass: string = ''; // Store original thead class
|
|
48
|
+
private _originalTdClasses: string[][] = []; // Store original td classes as a 2D array [row][col]
|
|
49
|
+
private _originalThClasses: string[] = []; // Store original th classes
|
|
50
|
+
|
|
51
|
+
private _infoElement: HTMLElement;
|
|
52
|
+
private _sizeElement: HTMLSelectElement;
|
|
53
|
+
private _paginationElement: HTMLElement;
|
|
54
|
+
|
|
55
|
+
private _checkbox: KTDataTableCheckboxAPI;
|
|
56
|
+
private _sortHandler: KTDataTableSortAPI<T>;
|
|
57
|
+
|
|
58
|
+
private _data: T[] = [];
|
|
59
|
+
|
|
60
|
+
constructor(element: HTMLElement, config?: KTDataTableConfigInterface) {
|
|
61
|
+
super();
|
|
62
|
+
|
|
63
|
+
if (KTData.has(element as HTMLElement, this._name)) return;
|
|
64
|
+
|
|
65
|
+
this._defaultConfig = this._initDefaultConfig(config);
|
|
66
|
+
|
|
67
|
+
this._init(element);
|
|
68
|
+
this._buildConfig();
|
|
69
|
+
|
|
70
|
+
// Store the instance directly on the element
|
|
71
|
+
(element as any).instance = this;
|
|
72
|
+
|
|
73
|
+
this._initElements();
|
|
74
|
+
|
|
75
|
+
// Initialize checkbox handler
|
|
76
|
+
this._checkbox = createCheckboxHandler(
|
|
77
|
+
this._element,
|
|
78
|
+
this._config,
|
|
79
|
+
(eventName: string, eventData?: any) => {
|
|
80
|
+
this._fireEvent(eventName, eventData);
|
|
81
|
+
this._dispatchEvent(eventName, eventData);
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Initialize sort handler
|
|
86
|
+
this._sortHandler = createSortHandler(
|
|
87
|
+
this._config,
|
|
88
|
+
this._theadElement,
|
|
89
|
+
() => ({
|
|
90
|
+
sortField: this.getState().sortField,
|
|
91
|
+
sortOrder: this.getState().sortOrder,
|
|
92
|
+
}),
|
|
93
|
+
(field, order) => {
|
|
94
|
+
this._config._state.sortField = field as never;
|
|
95
|
+
this._config._state.sortOrder = order;
|
|
96
|
+
},
|
|
97
|
+
this._fireEvent.bind(this),
|
|
98
|
+
this._dispatchEvent.bind(this),
|
|
99
|
+
this._updateData.bind(this),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
this._sortHandler.initSort();
|
|
103
|
+
|
|
104
|
+
if (this._config.stateSave === false) {
|
|
105
|
+
this._deleteState();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this._config.stateSave) {
|
|
109
|
+
this._loadState();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this._updateData();
|
|
113
|
+
|
|
114
|
+
this._fireEvent('init');
|
|
115
|
+
this._dispatchEvent('init');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Initialize default configuration for the datatable
|
|
120
|
+
* @param config User-provided configuration options
|
|
121
|
+
* @returns Default configuration merged with user-provided options
|
|
122
|
+
*/
|
|
123
|
+
private _initDefaultConfig(
|
|
124
|
+
config?: KTDataTableConfigInterface,
|
|
125
|
+
): KTDataTableConfigInterface {
|
|
126
|
+
return {
|
|
127
|
+
/**
|
|
128
|
+
* HTTP method for server-side API call
|
|
129
|
+
*/
|
|
130
|
+
requestMethod: 'GET',
|
|
131
|
+
/**
|
|
132
|
+
* Custom HTTP headers for the API request
|
|
133
|
+
*/
|
|
134
|
+
requestHeaders: {
|
|
135
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* Pagination info template
|
|
139
|
+
*/
|
|
140
|
+
info: '{start}-{end} of {total}',
|
|
141
|
+
/**
|
|
142
|
+
* Info text when there is no data
|
|
143
|
+
*/
|
|
144
|
+
infoEmpty: 'No records found',
|
|
145
|
+
/**
|
|
146
|
+
* Available page sizes
|
|
147
|
+
*/
|
|
148
|
+
pageSizes: [5, 10, 20, 30, 50],
|
|
149
|
+
/**
|
|
150
|
+
* Default page size
|
|
151
|
+
*/
|
|
152
|
+
pageSize: 10,
|
|
153
|
+
/**
|
|
154
|
+
* Enable or disable pagination more button
|
|
155
|
+
*/
|
|
156
|
+
pageMore: true,
|
|
157
|
+
/**
|
|
158
|
+
* Maximum number of pages before enabling pagination more button
|
|
159
|
+
*/
|
|
160
|
+
pageMoreLimit: 3,
|
|
161
|
+
/**
|
|
162
|
+
* Pagination button templates
|
|
163
|
+
*/
|
|
164
|
+
pagination: {
|
|
165
|
+
number: {
|
|
166
|
+
/**
|
|
167
|
+
* CSS classes to be added to the pagination button
|
|
168
|
+
*/
|
|
169
|
+
class: 'kt-datatable-pagination-button',
|
|
170
|
+
/**
|
|
171
|
+
* Text to be displayed in the pagination button
|
|
172
|
+
*/
|
|
173
|
+
text: '{page}',
|
|
174
|
+
},
|
|
175
|
+
previous: {
|
|
176
|
+
/**
|
|
177
|
+
* CSS classes to be added to the previous pagination button
|
|
178
|
+
*/
|
|
179
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-prev',
|
|
180
|
+
/**
|
|
181
|
+
* Text to be displayed in the previous pagination button
|
|
182
|
+
*/
|
|
183
|
+
text: `
|
|
184
|
+
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
185
|
+
<path d="M8.86501 16.7882V12.8481H21.1459C21.3724 12.8481 21.5897 12.7581 21.7498 12.5979C21.91 12.4378 22 12.2205 22 11.994C22 11.7675 21.91 11.5503 21.7498 11.3901C21.5897 11.2299 21.3724 11.1399 21.1459 11.1399H8.86501V7.2112C8.86628 7.10375 8.83517 6.9984 8.77573 6.90887C8.7163 6.81934 8.63129 6.74978 8.53177 6.70923C8.43225 6.66869 8.32283 6.65904 8.21775 6.68155C8.11267 6.70405 8.0168 6.75766 7.94262 6.83541L2.15981 11.6182C2.1092 11.668 2.06901 11.7274 2.04157 11.7929C2.01413 11.8584 2 11.9287 2 11.9997C2 12.0707 2.01413 12.141 2.04157 12.2065C2.06901 12.272 2.1092 12.3314 2.15981 12.3812L7.94262 17.164C8.0168 17.2417 8.11267 17.2953 8.21775 17.3178C8.32283 17.3403 8.43225 17.3307 8.53177 17.2902C8.63129 17.2496 8.7163 17.18 8.77573 17.0905C8.83517 17.001 8.86628 16.8956 8.86501 16.7882Z" fill="currentColor"/>
|
|
186
|
+
</svg>
|
|
187
|
+
`,
|
|
188
|
+
},
|
|
189
|
+
next: {
|
|
190
|
+
/**
|
|
191
|
+
* CSS classes to be added to the next pagination button
|
|
192
|
+
*/
|
|
193
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-next',
|
|
194
|
+
/**
|
|
195
|
+
* Text to be displayed in the next pagination button
|
|
196
|
+
*/
|
|
197
|
+
text: `
|
|
198
|
+
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
199
|
+
<path d="M15.135 7.21144V11.1516H2.85407C2.62756 11.1516 2.41032 11.2415 2.25015 11.4017C2.08998 11.5619 2 11.7791 2 12.0056C2 12.2321 2.08998 12.4494 2.25015 12.6096C2.41032 12.7697 2.62756 12.8597 2.85407 12.8597H15.135V16.7884C15.1337 16.8959 15.1648 17.0012 15.2243 17.0908C15.2837 17.1803 15.3687 17.2499 15.4682 17.2904C15.5677 17.3309 15.6772 17.3406 15.7822 17.3181C15.8873 17.2956 15.9832 17.242 16.0574 17.1642L21.8402 12.3814C21.8908 12.3316 21.931 12.2722 21.9584 12.2067C21.9859 12.1412 22 12.0709 22 11.9999C22 11.9289 21.9859 11.8586 21.9584 11.7931C21.931 11.7276 21.8908 11.6683 21.8402 11.6185L16.0574 6.83565C15.9832 6.75791 15.8873 6.70429 15.7822 6.68179C15.6772 6.65929 15.5677 6.66893 15.4682 6.70948C15.3687 6.75002 15.2837 6.81959 15.2243 6.90911C15.1648 6.99864 15.1337 7.10399 15.135 7.21144Z" fill="currentColor"/>
|
|
200
|
+
</svg>
|
|
201
|
+
`,
|
|
202
|
+
},
|
|
203
|
+
more: {
|
|
204
|
+
/**
|
|
205
|
+
* CSS classes to be added to the pagination more button
|
|
206
|
+
*/
|
|
207
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-more',
|
|
208
|
+
/**
|
|
209
|
+
* Text to be displayed in the pagination more button
|
|
210
|
+
*/
|
|
211
|
+
text: '...',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
/**
|
|
215
|
+
* Sorting options
|
|
216
|
+
*/
|
|
217
|
+
sort: {
|
|
218
|
+
/**
|
|
219
|
+
* CSS classes to be added to the sortable headers
|
|
220
|
+
*/
|
|
221
|
+
classes: {
|
|
222
|
+
base: 'kt-table-col',
|
|
223
|
+
asc: 'asc',
|
|
224
|
+
desc: 'desc',
|
|
225
|
+
},
|
|
226
|
+
/**
|
|
227
|
+
* Local sorting callback function
|
|
228
|
+
* Sorts the data array based on the sort field and order
|
|
229
|
+
* @param data Data array to be sorted
|
|
230
|
+
* @param sortField Property name of the data object to be sorted by
|
|
231
|
+
* @param sortOrder Sorting order (ascending or descending)
|
|
232
|
+
* @returns Sorted data array
|
|
233
|
+
*/
|
|
234
|
+
callback: (
|
|
235
|
+
data: T[],
|
|
236
|
+
sortField: keyof T | number,
|
|
237
|
+
sortOrder: KTDataTableSortOrderInterface,
|
|
238
|
+
): T[] => {
|
|
239
|
+
return this._sortHandler
|
|
240
|
+
? this._sortHandler.sortData(data, sortField, sortOrder)
|
|
241
|
+
: data;
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
search: {
|
|
245
|
+
/**
|
|
246
|
+
* Delay in milliseconds before the search function is applied to the data array
|
|
247
|
+
* @default 500
|
|
248
|
+
*/
|
|
249
|
+
delay: 500, // ms
|
|
250
|
+
/**
|
|
251
|
+
* Local search callback function
|
|
252
|
+
* Filters the data array based on the search string
|
|
253
|
+
* @param data Data array to be filtered
|
|
254
|
+
* @param search Search string used to filter the data array
|
|
255
|
+
* @returns Filtered data array
|
|
256
|
+
*/
|
|
257
|
+
callback: (data: T[], search: string): T[] => {
|
|
258
|
+
if (!data || !search) {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return data.filter((item: T) => {
|
|
263
|
+
if (!item) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return Object.values(item).some(
|
|
268
|
+
(value: string | number | boolean) => {
|
|
269
|
+
if (
|
|
270
|
+
typeof value !== 'string' &&
|
|
271
|
+
typeof value !== 'number' &&
|
|
272
|
+
typeof value !== 'boolean'
|
|
273
|
+
) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const valueText = String(value)
|
|
278
|
+
.replace(/<[^>]*>| /g, '')
|
|
279
|
+
.toLowerCase();
|
|
280
|
+
return valueText.includes(search.toLowerCase());
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
/**
|
|
287
|
+
* Loading spinner options
|
|
288
|
+
*/
|
|
289
|
+
loading: {
|
|
290
|
+
/**
|
|
291
|
+
* Template to be displayed during data fetching process
|
|
292
|
+
*/
|
|
293
|
+
template: `
|
|
294
|
+
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
|
295
|
+
<div class="kt-datatable-loading">
|
|
296
|
+
<svg class="animate-spin -ml-1 h-5 w-5 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
297
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3"></circle>
|
|
298
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
299
|
+
</svg>
|
|
300
|
+
{content}
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
`,
|
|
304
|
+
/**
|
|
305
|
+
* Loading text to be displayed in the template
|
|
306
|
+
*/
|
|
307
|
+
content: 'Loading...',
|
|
308
|
+
},
|
|
309
|
+
/**
|
|
310
|
+
* Selectors of the elements to be targeted
|
|
311
|
+
*/
|
|
312
|
+
attributes: {
|
|
313
|
+
/**
|
|
314
|
+
* Data table element
|
|
315
|
+
*/
|
|
316
|
+
table: 'table[data-kt-datatable-table="true"]',
|
|
317
|
+
/**
|
|
318
|
+
* Pagination info element
|
|
319
|
+
*/
|
|
320
|
+
info: '[data-kt-datatable-info="true"]',
|
|
321
|
+
/**
|
|
322
|
+
* Page size dropdown element
|
|
323
|
+
*/
|
|
324
|
+
size: '[data-kt-datatable-size="true"]',
|
|
325
|
+
/**
|
|
326
|
+
* Pagination element
|
|
327
|
+
*/
|
|
328
|
+
pagination: '[data-kt-datatable-pagination="true"]',
|
|
329
|
+
/**
|
|
330
|
+
* Spinner element
|
|
331
|
+
*/
|
|
332
|
+
spinner: '[data-kt-datatable-spinner="true"]',
|
|
333
|
+
/**
|
|
334
|
+
* Checkbox element
|
|
335
|
+
*/
|
|
336
|
+
check: '[data-kt-datatable-check="true"]',
|
|
337
|
+
checkbox: '[data-kt-datatable-row-check="true"]',
|
|
338
|
+
},
|
|
339
|
+
/**
|
|
340
|
+
* Enable or disable state saving
|
|
341
|
+
*/
|
|
342
|
+
stateSave: true,
|
|
343
|
+
checkbox: {
|
|
344
|
+
checkedClass: 'checked',
|
|
345
|
+
},
|
|
346
|
+
/**
|
|
347
|
+
* Private properties
|
|
348
|
+
*/
|
|
349
|
+
_state: {} as KTDataTableStateInterface,
|
|
350
|
+
loadingClass: 'loading',
|
|
351
|
+
...config,
|
|
352
|
+
} as KTDataTableConfigInterface;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Initialize table, tbody, thead, info, size, and pagination elements
|
|
357
|
+
* @returns {void}
|
|
358
|
+
*/
|
|
359
|
+
private _initElements(): void {
|
|
360
|
+
/**
|
|
361
|
+
* Data table element
|
|
362
|
+
*/
|
|
363
|
+
this._tableElement = this._element.querySelector<HTMLTableElement>(
|
|
364
|
+
this._config.attributes.table,
|
|
365
|
+
)!;
|
|
366
|
+
/**
|
|
367
|
+
* Table body element
|
|
368
|
+
*/
|
|
369
|
+
this._tbodyElement =
|
|
370
|
+
this._tableElement.tBodies[0] || this._tableElement.createTBody();
|
|
371
|
+
/**
|
|
372
|
+
* Table head element
|
|
373
|
+
*/
|
|
374
|
+
this._theadElement = this._tableElement.tHead!;
|
|
375
|
+
|
|
376
|
+
// Store original classes
|
|
377
|
+
this._storeOriginalClasses();
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Pagination info element
|
|
381
|
+
*/
|
|
382
|
+
this._infoElement = this._element.querySelector<HTMLElement>(
|
|
383
|
+
this._config.attributes.info,
|
|
384
|
+
)!;
|
|
385
|
+
/**
|
|
386
|
+
* Page size dropdown element
|
|
387
|
+
*/
|
|
388
|
+
this._sizeElement = this._element.querySelector<HTMLSelectElement>(
|
|
389
|
+
this._config.attributes.size,
|
|
390
|
+
)!;
|
|
391
|
+
/**
|
|
392
|
+
* Pagination element
|
|
393
|
+
*/
|
|
394
|
+
this._paginationElement = this._element.querySelector<HTMLElement>(
|
|
395
|
+
this._config.attributes.pagination,
|
|
396
|
+
)!;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Store original classes from table elements
|
|
401
|
+
* @returns {void}
|
|
402
|
+
*/
|
|
403
|
+
private _storeOriginalClasses(): void {
|
|
404
|
+
// Store tbody class
|
|
405
|
+
if (this._tbodyElement) {
|
|
406
|
+
this._originalTbodyClass = this._tbodyElement.className || '';
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Store thead class and th classes
|
|
410
|
+
if (this._theadElement) {
|
|
411
|
+
this._originalTheadClass = this._theadElement.className || '';
|
|
412
|
+
|
|
413
|
+
// Store th classes
|
|
414
|
+
const thElements =
|
|
415
|
+
this._theadElement.querySelectorAll<HTMLTableCellElement>('th');
|
|
416
|
+
this._originalThClasses = Array.from(thElements).map(
|
|
417
|
+
(th) => th.className || '',
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Store tr and td classes
|
|
422
|
+
if (this._tbodyElement) {
|
|
423
|
+
const originalRows =
|
|
424
|
+
this._tbodyElement.querySelectorAll<HTMLTableRowElement>('tr');
|
|
425
|
+
this._originalTrClasses = Array.from(originalRows).map(
|
|
426
|
+
(row) => row.className || '',
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// Store td classes as a 2D array
|
|
430
|
+
this._originalTdClasses = [];
|
|
431
|
+
Array.from(originalRows).forEach((row, rowIndex) => {
|
|
432
|
+
const tdElements = row.querySelectorAll<HTMLTableCellElement>('td');
|
|
433
|
+
this._originalTdClasses[rowIndex] = Array.from(tdElements).map(
|
|
434
|
+
(td) => td.className || '',
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Fetch data from the server or from the DOM if `apiEndpoint` is not defined.
|
|
442
|
+
* @returns {Promise<void>} Promise which is resolved after data has been fetched and checkbox plugin initialized.
|
|
443
|
+
*/
|
|
444
|
+
private async _updateData(): Promise<void> {
|
|
445
|
+
this._showSpinner(); // Show spinner before fetching data
|
|
446
|
+
|
|
447
|
+
// Fetch data from the DOM and initialize the checkbox plugin
|
|
448
|
+
return typeof this._config.apiEndpoint === 'undefined'
|
|
449
|
+
? this._fetchDataFromLocal().then(
|
|
450
|
+
this._finalize.bind(this) as () => Promise<void>,
|
|
451
|
+
)
|
|
452
|
+
: this._fetchDataFromServer().then(
|
|
453
|
+
this._finalize.bind(this) as () => Promise<void>,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Finalize data table after data has been fetched
|
|
459
|
+
* @returns {void}
|
|
460
|
+
*/
|
|
461
|
+
private _finalize(): void {
|
|
462
|
+
this._element.classList.add('datatable-initialized');
|
|
463
|
+
|
|
464
|
+
// Initialize checkbox logic
|
|
465
|
+
this._checkbox.init();
|
|
466
|
+
|
|
467
|
+
this._attachSearchEvent();
|
|
468
|
+
|
|
469
|
+
if (typeof KTComponents !== 'undefined') {
|
|
470
|
+
KTComponents.init();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Hide spinner
|
|
475
|
+
*/
|
|
476
|
+
this._hideSpinner();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Attach search event to the search input element
|
|
481
|
+
* @returns {void}
|
|
482
|
+
*/
|
|
483
|
+
private _attachSearchEvent(): void {
|
|
484
|
+
const tableId: string = this._tableId();
|
|
485
|
+
const searchElement: HTMLInputElement | null =
|
|
486
|
+
document.querySelector<HTMLInputElement>(
|
|
487
|
+
`[data-kt-datatable-search="#${tableId}"]`,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Get search state
|
|
491
|
+
const { search } = this.getState();
|
|
492
|
+
// Set search value
|
|
493
|
+
if (searchElement) {
|
|
494
|
+
searchElement.value =
|
|
495
|
+
typeof search === 'string' ? search : String(search);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (searchElement) {
|
|
499
|
+
// Check if a debounced search function already exists
|
|
500
|
+
if ((searchElement as any)._debouncedSearch) {
|
|
501
|
+
// Remove the existing debounced event listener
|
|
502
|
+
searchElement.removeEventListener(
|
|
503
|
+
'keyup',
|
|
504
|
+
(searchElement as any)._debouncedSearch,
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Create a new debounced search function
|
|
509
|
+
const debouncedSearch = this._debounce(() => {
|
|
510
|
+
this.search(searchElement.value);
|
|
511
|
+
}, this._config.search.delay);
|
|
512
|
+
|
|
513
|
+
// Store the new debounced function as a property of the element
|
|
514
|
+
(searchElement as any)._debouncedSearch = debouncedSearch;
|
|
515
|
+
|
|
516
|
+
// Add the new debounced event listener
|
|
517
|
+
searchElement.addEventListener('keyup', debouncedSearch);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Fetch data from the DOM
|
|
523
|
+
* Fetch data from the table element and save it to the `originalData` state property.
|
|
524
|
+
* This method is used when the data is not fetched from the server via an API endpoint.
|
|
525
|
+
*/
|
|
526
|
+
private async _fetchDataFromLocal(): Promise<void> {
|
|
527
|
+
this._fireEvent('fetch');
|
|
528
|
+
this._dispatchEvent('fetch');
|
|
529
|
+
|
|
530
|
+
const { sortField, sortOrder, page, pageSize, search } = this.getState();
|
|
531
|
+
let { originalData } = this.getState();
|
|
532
|
+
|
|
533
|
+
// If the table element or the original data is not defined, bail
|
|
534
|
+
if (
|
|
535
|
+
!this._tableElement ||
|
|
536
|
+
originalData === undefined ||
|
|
537
|
+
this._tableConfigInvalidate() ||
|
|
538
|
+
this._localTableHeaderInvalidate() ||
|
|
539
|
+
this._localTableContentInvalidate()
|
|
540
|
+
) {
|
|
541
|
+
this._deleteState();
|
|
542
|
+
|
|
543
|
+
const { originalData, originalDataAttributes } =
|
|
544
|
+
this._localExtractTableContent();
|
|
545
|
+
|
|
546
|
+
this._config._state.originalData = originalData;
|
|
547
|
+
this._config._state.originalDataAttributes = originalDataAttributes;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Update the original data variable
|
|
551
|
+
originalData = this.getState().originalData;
|
|
552
|
+
|
|
553
|
+
// Clone the original data
|
|
554
|
+
let _temp = (this._data = [...originalData] as T[]);
|
|
555
|
+
|
|
556
|
+
if (search) {
|
|
557
|
+
_temp = this._data = this._config.search.callback.call(
|
|
558
|
+
this,
|
|
559
|
+
this._data,
|
|
560
|
+
search,
|
|
561
|
+
) as T[];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// If sorting is defined, sort the data
|
|
565
|
+
if (
|
|
566
|
+
sortField !== undefined &&
|
|
567
|
+
sortOrder !== undefined &&
|
|
568
|
+
sortOrder !== ''
|
|
569
|
+
) {
|
|
570
|
+
if (typeof this._config.sort.callback === 'function') {
|
|
571
|
+
this._data = this._config.sort.callback.call(
|
|
572
|
+
this,
|
|
573
|
+
this._data,
|
|
574
|
+
sortField as string,
|
|
575
|
+
sortOrder,
|
|
576
|
+
) as T[];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// If there is data, slice it to the current page size
|
|
581
|
+
if (this._data?.length > 0) {
|
|
582
|
+
// Calculate the start and end indices for the current page
|
|
583
|
+
const startIndex = (page - 1) * pageSize;
|
|
584
|
+
const endIndex = startIndex + pageSize;
|
|
585
|
+
|
|
586
|
+
this._data = this._data.slice(startIndex, endIndex) as T[];
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Determine number of total rows
|
|
590
|
+
this._config._state.totalItems = _temp.length;
|
|
591
|
+
|
|
592
|
+
// Draw the data
|
|
593
|
+
await this._draw();
|
|
594
|
+
this._fireEvent('fetched');
|
|
595
|
+
this._dispatchEvent('fetched');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Checks if the table content has been invalidated by comparing the current checksum of the table body
|
|
600
|
+
* with the stored checksum in the state. If the checksums are different, the state is updated with the
|
|
601
|
+
* new checksum and `true` is returned. Otherwise, `false` is returned.
|
|
602
|
+
*
|
|
603
|
+
* @returns {boolean} `true` if the table content has been invalidated, `false` otherwise.
|
|
604
|
+
*/
|
|
605
|
+
private _localTableContentInvalidate(): boolean {
|
|
606
|
+
const checksum: string = KTUtils.checksum(
|
|
607
|
+
JSON.stringify(this._tbodyElement.innerHTML),
|
|
608
|
+
);
|
|
609
|
+
if (this.getState()._contentChecksum !== checksum) {
|
|
610
|
+
this._config._state._contentChecksum = checksum;
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
private _tableConfigInvalidate(): boolean {
|
|
617
|
+
// Remove _data and _state from config
|
|
618
|
+
const { _data, _state, ...restConfig } = this._config;
|
|
619
|
+
const checksum: string = KTUtils.checksum(JSON.stringify(restConfig));
|
|
620
|
+
if (_state._configChecksum !== checksum) {
|
|
621
|
+
this._config._state._configChecksum = checksum;
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Extract the table content and returns it as an object containing an array of original data and an array of original data attributes.
|
|
629
|
+
*
|
|
630
|
+
* @returns {{originalData: T[], originalDataAttributes: KTDataTableAttributeInterface[]}} - An object containing an array of original data and an array of original data attributes.
|
|
631
|
+
*/
|
|
632
|
+
private _localExtractTableContent(): {
|
|
633
|
+
originalData: T[];
|
|
634
|
+
originalDataAttributes: KTDataTableAttributeInterface[];
|
|
635
|
+
} {
|
|
636
|
+
const originalData: T[] = [];
|
|
637
|
+
const originalDataAttributes: KTDataTableAttributeInterface[] = [];
|
|
638
|
+
|
|
639
|
+
this._storeOriginalClasses();
|
|
640
|
+
|
|
641
|
+
const rows = this._tbodyElement.querySelectorAll<HTMLTableRowElement>('tr');
|
|
642
|
+
const ths: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
643
|
+
? this._theadElement.querySelectorAll('th')
|
|
644
|
+
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
645
|
+
|
|
646
|
+
rows.forEach((row: HTMLTableRowElement) => {
|
|
647
|
+
const dataRow: T = {} as T;
|
|
648
|
+
const dataRowAttribute: KTDataTableAttributeInterface =
|
|
649
|
+
{} as KTDataTableAttributeInterface;
|
|
650
|
+
|
|
651
|
+
row.querySelectorAll<HTMLTableCellElement>('td').forEach((td, index) => {
|
|
652
|
+
const colName = ths[index]?.getAttribute('data-kt-datatable-column');
|
|
653
|
+
if (colName) {
|
|
654
|
+
dataRow[colName as keyof T] = td.innerHTML?.trim() as T[keyof T];
|
|
655
|
+
} else {
|
|
656
|
+
// Store the original HTML for fallback
|
|
657
|
+
dataRow[index as keyof T] = td.innerHTML?.trim() as T[keyof T];
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
if (Object.keys(dataRow).length > 0) {
|
|
662
|
+
originalData.push(dataRow);
|
|
663
|
+
originalDataAttributes.push(dataRowAttribute);
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
return { originalData, originalDataAttributes };
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Check if the table header is invalidated
|
|
672
|
+
* @returns {boolean} - Returns true if the table header is invalidated, false otherwise
|
|
673
|
+
*/
|
|
674
|
+
private _localTableHeaderInvalidate(): boolean {
|
|
675
|
+
const { originalData } = this.getState();
|
|
676
|
+
const currentTableHeaders = this._theadElement
|
|
677
|
+
? this._theadElement.querySelectorAll('th').length
|
|
678
|
+
: 0;
|
|
679
|
+
const totalColumns = originalData.length
|
|
680
|
+
? Object.keys(originalData[0]).length
|
|
681
|
+
: 0;
|
|
682
|
+
|
|
683
|
+
return currentTableHeaders !== totalColumns;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Fetch data from the server
|
|
688
|
+
*/
|
|
689
|
+
private async _fetchDataFromServer(): Promise<void> {
|
|
690
|
+
this._fireEvent('fetch');
|
|
691
|
+
this._dispatchEvent('fetch');
|
|
692
|
+
|
|
693
|
+
const queryParams = this._getQueryParamsForFetchRequest();
|
|
694
|
+
const response = await this._performFetchRequest(queryParams);
|
|
695
|
+
|
|
696
|
+
let responseData = null;
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
responseData = await response.json();
|
|
700
|
+
} catch (error) {
|
|
701
|
+
this._noticeOnTable(
|
|
702
|
+
'Error parsing API response as JSON: ' + String(error),
|
|
703
|
+
);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
this._fireEvent('fetched', { response: responseData });
|
|
708
|
+
this._dispatchEvent('fetched', { response: responseData });
|
|
709
|
+
|
|
710
|
+
// Use the mapResponse function to transform the data if provided
|
|
711
|
+
if (typeof this._config.mapResponse === 'function') {
|
|
712
|
+
responseData = this._config.mapResponse.call(this, responseData);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
this._data = responseData.data;
|
|
716
|
+
|
|
717
|
+
this._config._state.totalItems = responseData.totalCount;
|
|
718
|
+
|
|
719
|
+
await this._draw();
|
|
720
|
+
this._fireEvent('fetched');
|
|
721
|
+
this._dispatchEvent('fetched');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Get the query params for a fetch request
|
|
726
|
+
* @returns The query params for the fetch request
|
|
727
|
+
*/
|
|
728
|
+
private _getQueryParamsForFetchRequest(): URLSearchParams {
|
|
729
|
+
// Get the current state of the datatable
|
|
730
|
+
const { page, pageSize, sortField, sortOrder, filters, search } =
|
|
731
|
+
this.getState();
|
|
732
|
+
|
|
733
|
+
// Create a new URLSearchParams object to store the query params
|
|
734
|
+
let queryParams = new URLSearchParams();
|
|
735
|
+
|
|
736
|
+
// Add the current page number and page size to the query params
|
|
737
|
+
queryParams.set('page', String(page));
|
|
738
|
+
queryParams.set('size', String(pageSize));
|
|
739
|
+
|
|
740
|
+
// If there is a sort order and field set, add them to the query params
|
|
741
|
+
if (sortOrder !== undefined) {
|
|
742
|
+
queryParams.set('sortOrder', String(sortOrder));
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (sortField !== undefined) {
|
|
746
|
+
queryParams.set('sortField', String(sortField));
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// If there are any filters set, add them to the query params
|
|
750
|
+
if (Array.isArray(filters) && filters.length) {
|
|
751
|
+
queryParams.set(
|
|
752
|
+
'filters',
|
|
753
|
+
JSON.stringify(
|
|
754
|
+
filters.map((filter: KTDataTableColumnFilterInterface) => ({
|
|
755
|
+
// Map the filter object to a simpler object with just the necessary properties
|
|
756
|
+
column: filter.column,
|
|
757
|
+
type: filter.type,
|
|
758
|
+
value: filter.value,
|
|
759
|
+
})),
|
|
760
|
+
),
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (search) {
|
|
765
|
+
queryParams.set(
|
|
766
|
+
'search',
|
|
767
|
+
typeof search === 'object' ? JSON.stringify(search) : search,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// If a mapRequest function is provided, call it with the query params object
|
|
772
|
+
if (typeof this._config.mapRequest === 'function') {
|
|
773
|
+
queryParams = this._config.mapRequest.call(this, queryParams);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Return the query params object
|
|
777
|
+
return queryParams;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
private async _performFetchRequest(
|
|
781
|
+
queryParams: URLSearchParams,
|
|
782
|
+
): Promise<Response> {
|
|
783
|
+
let requestMethod: RequestInit['method'] = this._config.requestMethod;
|
|
784
|
+
let requestBody: RequestInit['body'] | undefined = undefined;
|
|
785
|
+
|
|
786
|
+
// If the request method is POST, send the query params as the request body
|
|
787
|
+
if (requestMethod === 'POST') {
|
|
788
|
+
requestBody = queryParams;
|
|
789
|
+
} else if (requestMethod === 'GET') {
|
|
790
|
+
// If the request method is GET, append the query params to the API endpoint
|
|
791
|
+
const apiEndpointWithQueryParams = new URL(this._config.apiEndpoint);
|
|
792
|
+
apiEndpointWithQueryParams.search = queryParams.toString();
|
|
793
|
+
this._config.apiEndpoint = apiEndpointWithQueryParams.toString();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return fetch(this._config.apiEndpoint, {
|
|
797
|
+
method: requestMethod,
|
|
798
|
+
body: requestBody,
|
|
799
|
+
headers: this._config.requestHeaders,
|
|
800
|
+
}).catch((error) => {
|
|
801
|
+
// Trigger an error event
|
|
802
|
+
this._fireEvent('error', { error });
|
|
803
|
+
this._dispatchEvent('error', { error });
|
|
804
|
+
|
|
805
|
+
this._noticeOnTable('Error performing fetch request: ' + String(error));
|
|
806
|
+
throw error;
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Update the table and pagination controls with new data
|
|
812
|
+
* @returns {Promise<void>} A promise that resolves when the table and pagination controls are updated
|
|
813
|
+
*/
|
|
814
|
+
private async _draw(): Promise<void> {
|
|
815
|
+
this._config._state.totalPages =
|
|
816
|
+
Math.ceil(this.getState().totalItems / this.getState().pageSize) || 0;
|
|
817
|
+
|
|
818
|
+
this._fireEvent('draw');
|
|
819
|
+
this._dispatchEvent('draw');
|
|
820
|
+
|
|
821
|
+
this._dispose();
|
|
822
|
+
|
|
823
|
+
// Update the table and pagination controls
|
|
824
|
+
if (this._theadElement && this._tbodyElement) {
|
|
825
|
+
this._updateTable();
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (this._infoElement && this._paginationElement) {
|
|
829
|
+
this._updatePagination();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
this._fireEvent('drew');
|
|
833
|
+
this._dispatchEvent('drew');
|
|
834
|
+
|
|
835
|
+
this._hideSpinner(); // Hide spinner after data is fetched
|
|
836
|
+
|
|
837
|
+
if (this._config.stateSave) {
|
|
838
|
+
this._saveState();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Update the HTML table with new data
|
|
844
|
+
* @returns {HTMLTableSectionElement} The new table body element
|
|
845
|
+
*/
|
|
846
|
+
private _updateTable(): HTMLTableSectionElement {
|
|
847
|
+
// Clear the existing table contents using a more efficient method
|
|
848
|
+
while (this._tableElement.tBodies.length) {
|
|
849
|
+
this._tableElement.removeChild(this._tableElement.tBodies[0]);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Create the table body with the new data
|
|
853
|
+
const tbodyElement =
|
|
854
|
+
this._tableElement.createTBody() as HTMLTableSectionElement;
|
|
855
|
+
|
|
856
|
+
// Apply the original class to the new tbody element
|
|
857
|
+
if (this._originalTbodyClass) {
|
|
858
|
+
tbodyElement.className = this._originalTbodyClass;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
this._updateTableContent(tbodyElement);
|
|
862
|
+
|
|
863
|
+
return tbodyElement;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Update the table content
|
|
868
|
+
* @param tbodyElement The table body element
|
|
869
|
+
* @returns {HTMLTableSectionElement} The updated table body element
|
|
870
|
+
*/
|
|
871
|
+
private _updateTableContent(
|
|
872
|
+
tbodyElement: HTMLTableSectionElement,
|
|
873
|
+
): HTMLTableSectionElement {
|
|
874
|
+
const fragment = document.createDocumentFragment();
|
|
875
|
+
|
|
876
|
+
tbodyElement.textContent = ''; // Clear the tbody element
|
|
877
|
+
|
|
878
|
+
if (this._data.length === 0) {
|
|
879
|
+
this._noticeOnTable(this._config.infoEmpty || '');
|
|
880
|
+
return tbodyElement;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const ths: NodeListOf<HTMLTableCellElement> = this._theadElement
|
|
884
|
+
? this._theadElement.querySelectorAll('th')
|
|
885
|
+
: ([] as unknown as NodeListOf<HTMLTableCellElement>);
|
|
886
|
+
|
|
887
|
+
this._data.forEach((item: T, rowIndex: number) => {
|
|
888
|
+
const row = document.createElement('tr');
|
|
889
|
+
|
|
890
|
+
// Apply original tr class if available
|
|
891
|
+
if (this._originalTrClasses && this._originalTrClasses[rowIndex]) {
|
|
892
|
+
row.className = this._originalTrClasses[rowIndex];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (!this._config.columns) {
|
|
896
|
+
const dataRowAttributes = this.getState().originalDataAttributes
|
|
897
|
+
? this.getState().originalDataAttributes[rowIndex]
|
|
898
|
+
: null;
|
|
899
|
+
|
|
900
|
+
// Use the order of <th> elements to render <td>s in the correct order
|
|
901
|
+
ths.forEach((th, colIndex) => {
|
|
902
|
+
const colName = th.getAttribute('data-kt-datatable-column');
|
|
903
|
+
const td = document.createElement('td');
|
|
904
|
+
let value: any;
|
|
905
|
+
if (colName && Object.prototype.hasOwnProperty.call(item, colName)) {
|
|
906
|
+
value = item[colName as keyof T];
|
|
907
|
+
} else if (Object.prototype.hasOwnProperty.call(item, colIndex)) {
|
|
908
|
+
value = item[colIndex as keyof T];
|
|
909
|
+
} else {
|
|
910
|
+
value = '';
|
|
911
|
+
}
|
|
912
|
+
td.innerHTML = value as string;
|
|
913
|
+
|
|
914
|
+
// Apply original td class if available
|
|
915
|
+
if (
|
|
916
|
+
this._originalTdClasses &&
|
|
917
|
+
this._originalTdClasses[rowIndex] &&
|
|
918
|
+
this._originalTdClasses[rowIndex][colIndex]
|
|
919
|
+
) {
|
|
920
|
+
td.className = this._originalTdClasses[rowIndex][colIndex];
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (dataRowAttributes && dataRowAttributes[colIndex]) {
|
|
924
|
+
for (const attr in dataRowAttributes[colIndex]) {
|
|
925
|
+
td.setAttribute(attr, dataRowAttributes[colIndex][attr]);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
row.appendChild(td);
|
|
930
|
+
});
|
|
931
|
+
} else {
|
|
932
|
+
Object.keys(this._config.columns).forEach(
|
|
933
|
+
(key: keyof T, colIndex: number) => {
|
|
934
|
+
const td = document.createElement('td');
|
|
935
|
+
const columnDef = this._config.columns[key as string];
|
|
936
|
+
|
|
937
|
+
// Apply original td class if available
|
|
938
|
+
if (
|
|
939
|
+
this._originalTdClasses &&
|
|
940
|
+
this._originalTdClasses[rowIndex] &&
|
|
941
|
+
this._originalTdClasses[rowIndex][colIndex]
|
|
942
|
+
) {
|
|
943
|
+
td.className = this._originalTdClasses[rowIndex][colIndex];
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (typeof columnDef.render === 'function') {
|
|
947
|
+
td.innerHTML = columnDef.render.call(
|
|
948
|
+
this,
|
|
949
|
+
item[key] as string,
|
|
950
|
+
item,
|
|
951
|
+
this,
|
|
952
|
+
) as string;
|
|
953
|
+
} else {
|
|
954
|
+
td.textContent = item[key] as string;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (typeof columnDef.createdCell === 'function') {
|
|
958
|
+
columnDef.createdCell.call(this, td, item[key], item, row);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
row.appendChild(td);
|
|
962
|
+
},
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
fragment.appendChild(row);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
tbodyElement.appendChild(fragment);
|
|
970
|
+
return tbodyElement;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Show a notice on the table
|
|
975
|
+
* @param message The message to show. If empty, the message will be removed
|
|
976
|
+
* @returns {void}
|
|
977
|
+
*/
|
|
978
|
+
private _noticeOnTable(message: string = ''): void {
|
|
979
|
+
const row = this._tableElement.tBodies[0].insertRow();
|
|
980
|
+
const cell = row.insertCell();
|
|
981
|
+
cell.colSpan = this._theadElement
|
|
982
|
+
? this._theadElement.querySelectorAll('th').length
|
|
983
|
+
: 0;
|
|
984
|
+
cell.innerHTML = message;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
private _updatePagination(): void {
|
|
988
|
+
this._removeChildElements(this._sizeElement);
|
|
989
|
+
this._createPageSizeControls(this._sizeElement);
|
|
990
|
+
|
|
991
|
+
this._removeChildElements(this._paginationElement);
|
|
992
|
+
this._createPaginationControls(this._infoElement, this._paginationElement);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Removes all child elements from the given container element.
|
|
997
|
+
* @param container The container element to remove the child elements from.
|
|
998
|
+
*/
|
|
999
|
+
private _removeChildElements(container: HTMLElement): void {
|
|
1000
|
+
if (!container) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Loop through all child elements of the container and remove them one by one
|
|
1005
|
+
while (container.firstChild) {
|
|
1006
|
+
// Remove the first child element (which is the first element in the list of child elements)
|
|
1007
|
+
container.removeChild(container.firstChild);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Creates a container element for the items per page selector.
|
|
1013
|
+
* @param _sizeElement The element to create the page size controls in.
|
|
1014
|
+
* @returns The container element.
|
|
1015
|
+
*/
|
|
1016
|
+
private _createPageSizeControls(
|
|
1017
|
+
_sizeElement: HTMLSelectElement,
|
|
1018
|
+
): HTMLSelectElement {
|
|
1019
|
+
// If no element is provided, return early
|
|
1020
|
+
if (!_sizeElement) {
|
|
1021
|
+
return _sizeElement;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Create <option> elements for each page size option
|
|
1025
|
+
const options = this._config.pageSizes.map((size: number) => {
|
|
1026
|
+
const option = document.createElement('option') as HTMLOptionElement;
|
|
1027
|
+
option.value = String(size);
|
|
1028
|
+
option.text = String(size);
|
|
1029
|
+
option.selected = this.getState().pageSize === size;
|
|
1030
|
+
return option;
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
// Add the <option> elements to the provided element
|
|
1034
|
+
_sizeElement.append(...options);
|
|
1035
|
+
|
|
1036
|
+
// Create an event listener for the "change" event on the element
|
|
1037
|
+
const _pageSizeControlsEvent = (event: Event) => {
|
|
1038
|
+
// When the element changes, reload the page with the new page size and page number 1
|
|
1039
|
+
this._reloadPageSize(
|
|
1040
|
+
Number((event.target as HTMLSelectElement).value),
|
|
1041
|
+
1,
|
|
1042
|
+
);
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// Bind the event listener to the component instance
|
|
1046
|
+
_sizeElement.onchange = _pageSizeControlsEvent.bind(this);
|
|
1047
|
+
|
|
1048
|
+
// Return the element
|
|
1049
|
+
return _sizeElement;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Reloads the data with the specified page size and optional page number.
|
|
1054
|
+
* @param pageSize The new page size.
|
|
1055
|
+
* @param page The new page number (optional, defaults to 1).
|
|
1056
|
+
*/
|
|
1057
|
+
private _reloadPageSize(pageSize: number, page: number = 1): void {
|
|
1058
|
+
// Update the page size and page number in the state
|
|
1059
|
+
this._config._state.pageSize = pageSize;
|
|
1060
|
+
this._config._state.page = page;
|
|
1061
|
+
|
|
1062
|
+
// Update the data with the new page size and page number
|
|
1063
|
+
this._updateData();
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Creates the pagination controls for the component.
|
|
1068
|
+
* @param _infoElement The element to set the info text in.
|
|
1069
|
+
* @param _paginationElement The element to create the pagination controls in.
|
|
1070
|
+
* @return {HTMLElement} The element containing the pagination controls.
|
|
1071
|
+
*/
|
|
1072
|
+
private _createPaginationControls(
|
|
1073
|
+
_infoElement: HTMLElement,
|
|
1074
|
+
_paginationElement: HTMLElement,
|
|
1075
|
+
): HTMLElement {
|
|
1076
|
+
if (!_infoElement || !_paginationElement || this._data.length === 0) {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
this._setPaginationInfoText(_infoElement);
|
|
1081
|
+
const paginationContainer =
|
|
1082
|
+
this._createPaginationContainer(_paginationElement);
|
|
1083
|
+
|
|
1084
|
+
if (paginationContainer) {
|
|
1085
|
+
this._createPaginationButtons(paginationContainer);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
return paginationContainer;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Sets the info text for the pagination controls.
|
|
1093
|
+
* @param _infoElement The element to set the info text in.
|
|
1094
|
+
*/
|
|
1095
|
+
private _setPaginationInfoText(_infoElement: HTMLElement): void {
|
|
1096
|
+
_infoElement.textContent = this._config.info
|
|
1097
|
+
.replace(
|
|
1098
|
+
'{start}',
|
|
1099
|
+
(this.getState().page - 1) * this.getState().pageSize + 1 + '',
|
|
1100
|
+
)
|
|
1101
|
+
.replace(
|
|
1102
|
+
'{end}',
|
|
1103
|
+
Math.min(
|
|
1104
|
+
this.getState().page * this.getState().pageSize,
|
|
1105
|
+
this.getState().totalItems,
|
|
1106
|
+
) + '',
|
|
1107
|
+
)
|
|
1108
|
+
.replace('{total}', this.getState().totalItems + '');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Creates the container element for the pagination controls.
|
|
1113
|
+
* @param _paginationElement The element to create the pagination controls in.
|
|
1114
|
+
* @return {HTMLElement} The container element.
|
|
1115
|
+
*/
|
|
1116
|
+
private _createPaginationContainer(
|
|
1117
|
+
_paginationElement: HTMLElement,
|
|
1118
|
+
): HTMLElement {
|
|
1119
|
+
// No longer create a wrapping div. Just return the pagination element itself.
|
|
1120
|
+
return _paginationElement;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Creates the pagination buttons for the component.
|
|
1125
|
+
* @param paginationContainer The container element for the pagination controls.
|
|
1126
|
+
*/
|
|
1127
|
+
private _createPaginationButtons(paginationContainer: HTMLElement): void {
|
|
1128
|
+
const { page: currentPage, totalPages } = this.getState();
|
|
1129
|
+
const { previous, next, number, more } = this._config.pagination;
|
|
1130
|
+
|
|
1131
|
+
// Helper function to create a button
|
|
1132
|
+
const createButton = (
|
|
1133
|
+
text: string,
|
|
1134
|
+
className: string,
|
|
1135
|
+
disabled: boolean,
|
|
1136
|
+
handleClick: () => void,
|
|
1137
|
+
): HTMLButtonElement => {
|
|
1138
|
+
const button = document.createElement('button') as HTMLButtonElement;
|
|
1139
|
+
button.className = className;
|
|
1140
|
+
button.innerHTML = text;
|
|
1141
|
+
button.disabled = disabled;
|
|
1142
|
+
button.onclick = handleClick;
|
|
1143
|
+
return button;
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
// Add Previous Button
|
|
1147
|
+
paginationContainer.appendChild(
|
|
1148
|
+
createButton(
|
|
1149
|
+
previous.text,
|
|
1150
|
+
`${previous.class}${currentPage === 1 ? ' disabled' : ''}`,
|
|
1151
|
+
currentPage === 1,
|
|
1152
|
+
() => this._paginateData(currentPage - 1),
|
|
1153
|
+
),
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
// Calculate range of pages
|
|
1157
|
+
const pageMoreEnabled = this._config.pageMore;
|
|
1158
|
+
|
|
1159
|
+
if (pageMoreEnabled) {
|
|
1160
|
+
const maxButtons = this._config.pageMoreLimit;
|
|
1161
|
+
const range = this._calculatePageRange(
|
|
1162
|
+
currentPage,
|
|
1163
|
+
totalPages,
|
|
1164
|
+
maxButtons,
|
|
1165
|
+
);
|
|
1166
|
+
|
|
1167
|
+
// Add start ellipsis
|
|
1168
|
+
if (range.start > 1) {
|
|
1169
|
+
paginationContainer.appendChild(
|
|
1170
|
+
createButton(more.text, more.class, false, () =>
|
|
1171
|
+
this._paginateData(Math.max(1, range.start - 1)),
|
|
1172
|
+
),
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Add page buttons
|
|
1177
|
+
for (let i = range.start; i <= range.end; i++) {
|
|
1178
|
+
paginationContainer.appendChild(
|
|
1179
|
+
createButton(
|
|
1180
|
+
number.text.replace('{page}', i.toString()),
|
|
1181
|
+
`${number.class}${currentPage === i ? ' active disabled' : ''}`,
|
|
1182
|
+
currentPage === i,
|
|
1183
|
+
() => this._paginateData(i),
|
|
1184
|
+
),
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Add end ellipsis
|
|
1189
|
+
if (pageMoreEnabled && range.end < totalPages) {
|
|
1190
|
+
paginationContainer.appendChild(
|
|
1191
|
+
createButton(more.text, more.class, false, () =>
|
|
1192
|
+
this._paginateData(Math.min(totalPages, range.end + 1)),
|
|
1193
|
+
),
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
} else {
|
|
1197
|
+
// Add page buttons
|
|
1198
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
1199
|
+
paginationContainer.appendChild(
|
|
1200
|
+
createButton(
|
|
1201
|
+
number.text.replace('{page}', i.toString()),
|
|
1202
|
+
`${number.class}${currentPage === i ? ' active disabled' : ''}`,
|
|
1203
|
+
currentPage === i,
|
|
1204
|
+
() => this._paginateData(i),
|
|
1205
|
+
),
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// Add Next Button
|
|
1211
|
+
paginationContainer.appendChild(
|
|
1212
|
+
createButton(
|
|
1213
|
+
next.text,
|
|
1214
|
+
`${next.class}${currentPage === totalPages ? ' disabled' : ''}`,
|
|
1215
|
+
currentPage === totalPages,
|
|
1216
|
+
() => this._paginateData(currentPage + 1),
|
|
1217
|
+
),
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// New helper method to calculate page range
|
|
1222
|
+
private _calculatePageRange(
|
|
1223
|
+
currentPage: number,
|
|
1224
|
+
totalPages: number,
|
|
1225
|
+
maxButtons: number,
|
|
1226
|
+
): { start: number; end: number } {
|
|
1227
|
+
let startPage: number, endPage: number;
|
|
1228
|
+
const halfMaxButtons = Math.floor(maxButtons / 2);
|
|
1229
|
+
|
|
1230
|
+
if (totalPages <= maxButtons) {
|
|
1231
|
+
startPage = 1;
|
|
1232
|
+
endPage = totalPages;
|
|
1233
|
+
} else {
|
|
1234
|
+
startPage = Math.max(currentPage - halfMaxButtons, 1);
|
|
1235
|
+
endPage = Math.min(startPage + maxButtons - 1, totalPages);
|
|
1236
|
+
if (endPage - startPage < maxButtons - 1) {
|
|
1237
|
+
startPage = Math.max(endPage - maxButtons + 1, 1);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return { start: startPage, end: endPage };
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Method for handling pagination
|
|
1246
|
+
* @param page - The page number to navigate to
|
|
1247
|
+
*/
|
|
1248
|
+
private _paginateData(page: number): void {
|
|
1249
|
+
if (page < 1 || !Number.isInteger(page)) {
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
this._fireEvent('pagination', { page: page });
|
|
1254
|
+
this._dispatchEvent('pagination', { page: page });
|
|
1255
|
+
|
|
1256
|
+
if (page >= 1 && page <= this.getState().totalPages) {
|
|
1257
|
+
this._config._state.page = page;
|
|
1258
|
+
this._updateData();
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// Method to show the loading spinner
|
|
1263
|
+
private _showSpinner(): void {
|
|
1264
|
+
const spinner =
|
|
1265
|
+
this._element.querySelector<HTMLElement>(
|
|
1266
|
+
this._config.attributes.spinner,
|
|
1267
|
+
) || this._createSpinner();
|
|
1268
|
+
if (spinner) {
|
|
1269
|
+
spinner.style.display = 'block';
|
|
1270
|
+
}
|
|
1271
|
+
this._element.classList.add(this._config.loadingClass);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// Method to hide the loading spinner
|
|
1275
|
+
private _hideSpinner(): void {
|
|
1276
|
+
const spinner = this._element.querySelector<HTMLElement>(
|
|
1277
|
+
this._config.attributes.spinner,
|
|
1278
|
+
);
|
|
1279
|
+
if (spinner) {
|
|
1280
|
+
spinner.style.display = 'none';
|
|
1281
|
+
}
|
|
1282
|
+
this._element.classList.remove(this._config.loadingClass);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// Method to create a spinner element if it doesn't exist
|
|
1286
|
+
private _createSpinner(): HTMLElement {
|
|
1287
|
+
if (typeof this._config.loading === 'undefined') {
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const template = document.createElement('template');
|
|
1292
|
+
template.innerHTML = this._config.loading.template
|
|
1293
|
+
.trim()
|
|
1294
|
+
.replace('{content}', this._config.loading.content);
|
|
1295
|
+
const spinner = template.content.firstChild as HTMLElement;
|
|
1296
|
+
spinner.setAttribute('data-kt-datatable-spinner', 'true');
|
|
1297
|
+
|
|
1298
|
+
this._tableElement.appendChild(spinner);
|
|
1299
|
+
|
|
1300
|
+
return spinner;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Saves the current state of the table to local storage.
|
|
1305
|
+
* @returns {void}
|
|
1306
|
+
*/
|
|
1307
|
+
private _saveState(): void {
|
|
1308
|
+
this._fireEvent('stateSave');
|
|
1309
|
+
this._dispatchEvent('stateSave');
|
|
1310
|
+
|
|
1311
|
+
const ns: string = this._tableNamespace();
|
|
1312
|
+
|
|
1313
|
+
if (ns) {
|
|
1314
|
+
localStorage.setItem(
|
|
1315
|
+
ns,
|
|
1316
|
+
JSON.stringify(this.getState() as KTDataTableStateInterface),
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* Loads the saved state of the table from local storage, if it exists.
|
|
1323
|
+
* @returns {Object} The saved state of the table, or null if no saved state exists.
|
|
1324
|
+
*/
|
|
1325
|
+
private _loadState(): KTDataTableStateInterface | null {
|
|
1326
|
+
const stateString = localStorage.getItem(this._tableNamespace());
|
|
1327
|
+
if (!stateString) return null;
|
|
1328
|
+
|
|
1329
|
+
try {
|
|
1330
|
+
const state = JSON.parse(stateString) as KTDataTableStateInterface;
|
|
1331
|
+
if (state) this._config._state = state;
|
|
1332
|
+
return state;
|
|
1333
|
+
} catch {} // eslint-disable-line no-empty
|
|
1334
|
+
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
private _deleteState(): void {
|
|
1339
|
+
const ns = this._tableNamespace();
|
|
1340
|
+
|
|
1341
|
+
if (ns) {
|
|
1342
|
+
localStorage.removeItem(ns);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Gets the namespace for the table's state.
|
|
1348
|
+
* If a namespace is specified in the config, it is used.
|
|
1349
|
+
* Otherwise, if the table element has an ID, it is used.
|
|
1350
|
+
* Otherwise, if the component element has an ID, it is used.
|
|
1351
|
+
* Finally, the component's UID is used.
|
|
1352
|
+
* @returns {string} The namespace for the table's state.
|
|
1353
|
+
*/
|
|
1354
|
+
private _tableNamespace(): string {
|
|
1355
|
+
// Use the specified namespace, if one is given
|
|
1356
|
+
if (this._config.stateNamespace) {
|
|
1357
|
+
return this._config.stateNamespace;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// Fallback to the component's UID
|
|
1361
|
+
return this._tableId() ?? this._name;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
private _tableId(): string {
|
|
1365
|
+
let id: string = null;
|
|
1366
|
+
// If the table element has an ID, use that
|
|
1367
|
+
if (this._tableElement?.getAttribute('id')) {
|
|
1368
|
+
id = this._tableElement.getAttribute('id') as string;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// If the component element has an ID, use that
|
|
1372
|
+
if (this._element?.getAttribute('id')) {
|
|
1373
|
+
id = this._element.getAttribute('id') as string;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
return id;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
private _dispose() {
|
|
1380
|
+
// Remove all event listeners and clean up resources
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
private _debounce(func: Function, wait: number) {
|
|
1384
|
+
let timeout: number | undefined;
|
|
1385
|
+
return function (...args: any[]) {
|
|
1386
|
+
const later = () => {
|
|
1387
|
+
clearTimeout(timeout);
|
|
1388
|
+
func(...args);
|
|
1389
|
+
};
|
|
1390
|
+
clearTimeout(timeout);
|
|
1391
|
+
timeout = window.setTimeout(later, wait);
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Gets the current state of the table.
|
|
1397
|
+
* @returns {KTDataTableStateInterface} The current state of the table.
|
|
1398
|
+
*/
|
|
1399
|
+
public getState(): KTDataTableStateInterface {
|
|
1400
|
+
return {
|
|
1401
|
+
/**
|
|
1402
|
+
* The current page number.
|
|
1403
|
+
*/
|
|
1404
|
+
page: 1,
|
|
1405
|
+
/**
|
|
1406
|
+
* The field that the data is sorted by.
|
|
1407
|
+
*/
|
|
1408
|
+
sortField: null,
|
|
1409
|
+
/**
|
|
1410
|
+
* The sort order (ascending or descending).
|
|
1411
|
+
*/
|
|
1412
|
+
sortOrder: '',
|
|
1413
|
+
/**
|
|
1414
|
+
* The number of rows to display per page.
|
|
1415
|
+
*/
|
|
1416
|
+
pageSize: this._config.pageSize,
|
|
1417
|
+
|
|
1418
|
+
filters: [],
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Any additional state that may have been stored in the config.
|
|
1422
|
+
*/
|
|
1423
|
+
...this._config._state,
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Sorts the data in the table by the specified field.
|
|
1429
|
+
* @param field The field to sort by.
|
|
1430
|
+
*/
|
|
1431
|
+
public sort(field: keyof T | number): void {
|
|
1432
|
+
// Use the sort handler to update state and trigger sorting
|
|
1433
|
+
const state = this.getState();
|
|
1434
|
+
const sortOrder = this._sortHandler.toggleSortOrder(
|
|
1435
|
+
state.sortField,
|
|
1436
|
+
state.sortOrder,
|
|
1437
|
+
field,
|
|
1438
|
+
);
|
|
1439
|
+
this._sortHandler.setSortIcon(field as keyof T, sortOrder);
|
|
1440
|
+
this._config._state.sortField = field as never;
|
|
1441
|
+
this._config._state.sortOrder = sortOrder;
|
|
1442
|
+
this._fireEvent('sort', { field, order: sortOrder });
|
|
1443
|
+
this._dispatchEvent('sort', { field, order: sortOrder });
|
|
1444
|
+
this._updateData();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Navigates to the specified page in the data table.
|
|
1449
|
+
* @param page The page number to navigate to.
|
|
1450
|
+
*/
|
|
1451
|
+
public goPage(page: number): void {
|
|
1452
|
+
if (page < 1 || !Number.isInteger(page)) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// Navigate to the specified page
|
|
1457
|
+
this._paginateData(page);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Set the page size of the data table.
|
|
1462
|
+
* @param pageSize The new page size.
|
|
1463
|
+
*/
|
|
1464
|
+
public setPageSize(pageSize: number): void {
|
|
1465
|
+
if (!Number.isInteger(pageSize)) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Reload the page size of the data table.
|
|
1471
|
+
* @param pageSize The new page size.
|
|
1472
|
+
*/
|
|
1473
|
+
this._reloadPageSize(pageSize);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* Reloads the data from the server and updates the table.
|
|
1478
|
+
* Triggers the 'reload' event and the 'kt.datatable.reload' custom event.
|
|
1479
|
+
*/
|
|
1480
|
+
public reload(): void {
|
|
1481
|
+
this._fireEvent('reload');
|
|
1482
|
+
this._dispatchEvent('reload');
|
|
1483
|
+
|
|
1484
|
+
// Fetch the data from the server using the current sort and filter settings
|
|
1485
|
+
this._updateData();
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
public redraw(page: number = 1): void {
|
|
1489
|
+
this._fireEvent('redraw');
|
|
1490
|
+
this._dispatchEvent('redraw');
|
|
1491
|
+
|
|
1492
|
+
this._paginateData(page);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Show the loading spinner of the data table.
|
|
1497
|
+
*/
|
|
1498
|
+
public showSpinner(): void {
|
|
1499
|
+
/**
|
|
1500
|
+
* Show the loading spinner of the data table.
|
|
1501
|
+
*/
|
|
1502
|
+
this._showSpinner();
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* Hide the loading spinner of the data table.
|
|
1507
|
+
*/
|
|
1508
|
+
public hideSpinner(): void {
|
|
1509
|
+
/**
|
|
1510
|
+
* Hide the loading spinner of the data table.
|
|
1511
|
+
*/
|
|
1512
|
+
this._hideSpinner();
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* Filter data using the specified filter object.
|
|
1517
|
+
* Replaces the existing filter object for the column with the new one.
|
|
1518
|
+
* @param filter Filter object containing the column name and its value.
|
|
1519
|
+
* @returns The KTDataTable instance.
|
|
1520
|
+
* @throws Error if the filter object is null or undefined.
|
|
1521
|
+
*/
|
|
1522
|
+
public setFilter(filter: KTDataTableColumnFilterInterface): KTDataTable<T> {
|
|
1523
|
+
this._config._state.filters = [
|
|
1524
|
+
...(this.getState().filters || []).filter(
|
|
1525
|
+
(f) => f.column !== filter.column,
|
|
1526
|
+
),
|
|
1527
|
+
filter,
|
|
1528
|
+
];
|
|
1529
|
+
return this;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
public override dispose(): void {
|
|
1533
|
+
this._dispose();
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
public search(query: string | object): void {
|
|
1537
|
+
this._config._state.search = query;
|
|
1538
|
+
this.reload();
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Static variables
|
|
1543
|
+
*/
|
|
1544
|
+
private static _instances = new Map<
|
|
1545
|
+
HTMLElement,
|
|
1546
|
+
KTDataTable<KTDataTableDataInterface>
|
|
1547
|
+
>();
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* Create KTDataTable instances for all elements with a data-kt-datatable="true" attribute.
|
|
1551
|
+
*
|
|
1552
|
+
* This function should be called after the control(s) have been
|
|
1553
|
+
* loaded and parsed by the browser. It will create instances of
|
|
1554
|
+
* KTDataTable for all elements with a data-kt-datatable="true" attribute.
|
|
1555
|
+
*/
|
|
1556
|
+
public static createInstances(): void {
|
|
1557
|
+
const elements = document.querySelectorAll<HTMLElement>(
|
|
1558
|
+
'[data-kt-datatable="true"]',
|
|
1559
|
+
);
|
|
1560
|
+
|
|
1561
|
+
elements.forEach((element) => {
|
|
1562
|
+
if (
|
|
1563
|
+
element.hasAttribute('data-kt-datatable') &&
|
|
1564
|
+
!element.classList.contains('datatable-initialized')
|
|
1565
|
+
) {
|
|
1566
|
+
/**
|
|
1567
|
+
* Create an instance of KTDataTable for the given element
|
|
1568
|
+
* @param element The element to create an instance for
|
|
1569
|
+
*/
|
|
1570
|
+
const instance = new KTDataTable(element);
|
|
1571
|
+
this._instances.set(element, instance);
|
|
1572
|
+
}
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/**
|
|
1577
|
+
* Get the KTDataTable instance for a given element.
|
|
1578
|
+
*
|
|
1579
|
+
* @param element The element to retrieve the instance for
|
|
1580
|
+
* @returns The KTDataTable instance or undefined if not found
|
|
1581
|
+
*/
|
|
1582
|
+
public static getInstance(
|
|
1583
|
+
element: HTMLElement,
|
|
1584
|
+
): KTDataTable<KTDataTableDataInterface> | undefined {
|
|
1585
|
+
return this._instances.get(element);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Initializes all KTDataTable instances on the page.
|
|
1590
|
+
*
|
|
1591
|
+
* This function should be called after the control(s) have been
|
|
1592
|
+
* loaded and parsed by the browser.
|
|
1593
|
+
*/
|
|
1594
|
+
public static init(): void {
|
|
1595
|
+
// Create instances of KTDataTable for all elements with a
|
|
1596
|
+
// data-kt-datatable="true" attribute
|
|
1597
|
+
KTDataTable.createInstances();
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/**
|
|
1601
|
+
* Check if all visible rows are checked (header checkbox state)
|
|
1602
|
+
* @returns {boolean}
|
|
1603
|
+
*/
|
|
1604
|
+
public isChecked(): boolean {
|
|
1605
|
+
return this._checkbox.isChecked();
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Toggle all visible row checkboxes (header checkbox)
|
|
1610
|
+
* @returns {void}
|
|
1611
|
+
*/
|
|
1612
|
+
public toggle(): void {
|
|
1613
|
+
this._checkbox.toggle();
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* Check all visible row checkboxes
|
|
1618
|
+
* @returns {void}
|
|
1619
|
+
*/
|
|
1620
|
+
public check(): void {
|
|
1621
|
+
this._checkbox.check();
|
|
1622
|
+
this._fireEvent('checked');
|
|
1623
|
+
this._dispatchEvent('checked');
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* Uncheck all visible row checkboxes
|
|
1628
|
+
* @returns {void}
|
|
1629
|
+
*/
|
|
1630
|
+
public uncheck(): void {
|
|
1631
|
+
this._checkbox.uncheck();
|
|
1632
|
+
this._fireEvent('unchecked');
|
|
1633
|
+
this._dispatchEvent('unchecked');
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* Get all checked row IDs (across all pages if preserveSelection is true)
|
|
1638
|
+
* @returns {string[]}
|
|
1639
|
+
*/
|
|
1640
|
+
public getChecked(): string[] {
|
|
1641
|
+
return this._checkbox.getChecked();
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* Reapply checked state to visible checkboxes (after redraw/pagination)
|
|
1646
|
+
* @returns {void}
|
|
1647
|
+
*/
|
|
1648
|
+
public update(): void {
|
|
1649
|
+
this._checkbox.updateState();
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// Other plugin methods can be added here
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (typeof window !== 'undefined') {
|
|
1656
|
+
window.KTDataTable = KTDataTable;
|
|
1657
|
+
}
|