UNPKG

easepick-plugin-keyboard

Version:

Adds keyboard navigation for [easepick](https://easepick.com/) library.

219 lines (218 loc) 6.63 kB
// src/index.ts import { BasePlugin } from "@easepick/base-plugin"; var KeyboardPlugin = class extends BasePlugin { constructor() { super(...arguments); this.binds = { onKeydown: this.onKeydown.bind(this), handleShow: this.handleShow.bind(this), updateTabIndex: this.updateTabIndex.bind(this) }; this.options = { unitIndex: 1, dayIndex: 2, openHotkeys: ["Enter", "Space"] }; } getName() { return "KeyboardPlugin"; } onAttach() { const element = this.picker.options.element; element.addEventListener("keydown", this.binds.handleShow, { capture: true }); this.picker.on("keydown", this.binds.onKeydown); this.picker.on("show", this.binds.updateTabIndex); this.picker.on("hide", this.binds.updateTabIndex); this.picker.on("render", this.binds.updateTabIndex); } onDetach() { const element = this.picker.options.element; element.removeEventListener("keydown", this.binds.handleShow, { capture: true }); this.picker.off("keydown", this.binds.onKeydown); this.picker.off("show", this.binds.updateTabIndex); this.picker.off("hide", this.binds.updateTabIndex); this.picker.off("render", this.binds.updateTabIndex); } updateTabIndex() { const show = this.picker.ui.container.classList.contains("show"); const days = this.picker.ui.container.querySelectorAll( ".unit.day:not(.locked,.not-available)" ); [...days].forEach( (el) => el.tabIndex = show ? this.options.dayIndex || 0 : -1 ); const units = this.picker.ui.container.querySelectorAll(".unit:not(.day)"); [...units].forEach( (el) => el.tabIndex = show ? this.options.unitIndex || 0 : -1 ); } handleShow(event) { const { openHotkeys } = this.options; switch (true) { case (openHotkeys == null ? void 0 : openHotkeys.includes(event.code)): event.preventDefault(); this.picker.show({ target: this.picker.options.element }); break; case ["Escape"].includes(event.code): this.picker.hide(); break; } } onKeydown(event) { this.onMouseEnter(event); switch (event.code) { case "ArrowUp": case "ArrowDown": this.verticalMove(event); break; case "ArrowLeft": case "ArrowRight": this.horizontalMove(event); break; case "Enter": case "Space": this.handleEnter(event); break; case "Escape": this.picker.hide(); break; } } findAllowableDaySibling(layout, target, isAllow) { const elms = Array.from( layout.querySelectorAll( `.day[tabindex="${this.options.dayIndex}"]` ) ); const targetIdx = elms.indexOf(target); return elms.filter((el, idx) => { return isAllow(idx, targetIdx) && el.tabIndex === this.options.dayIndex; })[0]; } changeMonth(evt) { var _a; const arrows = { ArrowLeft: "previous", ArrowRight: "next" }; const button = this.picker.ui.container.querySelector( `.${arrows[evt.code]}-button[tabindex="${this.options.unitIndex}"]` ); if (button && !((_a = button.parentElement) == null ? void 0 : _a.classList.contains(`no-${arrows[evt.code]}-month`))) { button.dispatchEvent(new Event("click", { bubbles: true })); setTimeout(() => { let focusEl = null; switch (evt.code) { case "ArrowLeft": { const elms = this.picker.ui.container.querySelectorAll( `.day[tabindex="${this.options.dayIndex}"]` ); focusEl = elms[elms.length - 1]; break; } case "ArrowRight": focusEl = this.picker.ui.container.querySelector( `.day[tabindex="${this.options.dayIndex}"]` ); break; } if (focusEl) { focusEl.focus(); } }); } } verticalMove(evt) { const target = evt.target; if (target.classList.contains("day")) { evt.preventDefault(); const nextElement = this.findAllowableDaySibling( this.picker.ui.container, target, (idx, targetIdx) => { targetIdx = evt.code === "ArrowUp" ? targetIdx - 7 : targetIdx + 7; return idx === targetIdx; } ); if (nextElement) { nextElement.focus(); } } } horizontalMove(evt) { const target = evt.target; if (target.classList.contains("day")) { evt.preventDefault(); const nextElement = this.findAllowableDaySibling( this.picker.ui.container, target, (idx, targetIdx) => { targetIdx = evt.code === "ArrowLeft" ? targetIdx - 1 : targetIdx + 1; return idx === targetIdx; } ); if (nextElement) { nextElement.focus(); } else { this.changeMonth(evt); } } } handleEnter(evt) { const target = evt.target; if (target.classList.contains("day")) { evt.preventDefault(); target.dispatchEvent(new Event("click", { bubbles: true })); setTimeout(() => { const rangePlugin = this.picker.PluginManager.getInstance("RangePlugin"); if (rangePlugin || !this.picker.options.autoApply) { const { container } = this.picker.ui; const endSelected = container.querySelector(".day.end"); if (endSelected) { this.focusTo(".apply-button"); } else { this.focusTo(".day.selected"); } } }); } if (target.classList.contains("unit")) { if (target.classList.contains("preset-button")) { if (!this.picker.options.autoApply) { this.focusTo(".apply-button"); } } if (target.classList.contains("previous-button")) { this.focusTo(".calendar:first-child > .header .previous-button"); } if (target.classList.contains("next-button")) { this.focusTo(".calendar:last-child > .header .next-button"); } } } onMouseEnter(evt) { const target = evt.target; if (target.classList.contains("day")) { setTimeout(() => { const e = this.picker.ui.shadowRoot.activeElement; if (e) { e.dispatchEvent(new Event("mouseenter", { bubbles: true })); } }); } } focusTo(selectors, timeout = 0) { setTimeout(() => { const { container } = this.picker.ui; const element = container.querySelector(selectors); element == null ? void 0 : element.focus(); }, timeout); } }; export { KeyboardPlugin };