diff --git a/dist/a11y-nav.cjs b/dist/a11y-nav.cjs index e5d5025..4faa47d 100644 --- a/dist/a11y-nav.cjs +++ b/dist/a11y-nav.cjs @@ -1,2 +1,2 @@ -function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,s=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&s.getMenuDepthFromEl(e.el)===s.getMenuDepthFromEl(t.el)&&s.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),s.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:s,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n,s=this.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof this.options.bodyClass||s||document.body.classList.remove(this.options.bodyClass),t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){var n;t.el.classList.remove("a11y-nav-active"),t.el.classList.remove("a11y-nav-animate-out"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))},this.options.duration)):(t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:t}})))}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var s={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return s.menu.control=s,s}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}(); +function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,o=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&o.getMenuDepthFromEl(e.el)===o.getMenuDepthFromEl(t.el)&&o.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),o.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:o,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n=function(){var n,o=e.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof e.options.bodyClass||o||document.body.classList.remove(e.options.bodyClass),t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))};t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){n(),t.el.classList.remove("a11y-nav-animate-out")},this.options.duration)):n()}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var o={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return o.menu.control=o,o}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}(); //# sourceMappingURL=a11y-nav.cjs.map diff --git a/dist/a11y-nav.cjs.map b/dist/a11y-nav.cjs.map index dac7144..f08295c 100644 --- a/dist/a11y-nav.cjs.map +++ b/dist/a11y-nav.cjs.map @@ -1 +1 @@ -{"version":3,"file":"a11y-nav.cjs","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n } else {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","CustomEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","hasOtherOpenMenus","some","m","remove","_menu$el$parentElemen2","_menu$el$parentElemen3","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"a11y-nav.cjs","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n const close = () => {\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n close();\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n }, this.options.duration);\r\n } else {\r\n close();\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","_control$menu$el$quer","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","CustomEvent","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","close","_menu$el$parentElemen2","hasOtherOpenMenus","some","m","remove","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/dist/a11y-nav.modern.js b/dist/a11y-nav.modern.js index 2e90d68..bb0294f 100644 --- a/dist/a11y-nav.modern.js +++ b/dist/a11y-nav.modern.js @@ -1,2 +1,2 @@ -function e(){return e=Object.assign?Object.assign.bind():function(e){for(var t=1;te.menu),this.focusables=this.getFocusables(),this.options=e({},this.options,s),this.onButtonClick=this.onButtonClick.bind(this),this.onButtonKeyDown=this.onButtonKeyDown.bind(this),this.onFocusableKeyDown=this.onFocusableKeyDown.bind(this),this.onBlur=this.onBlur.bind(this),this.init()}init(){this.controls.forEach(e=>{e.menu.el.classList.add("a11y-nav-menu"),e.menu.el.setAttribute("tabindex","-1"),e.el.addEventListener("click",this.onButtonClick),e.el.addEventListener("keydown",this.onButtonKeyDown),"true"===e.el.getAttribute("aria-expanded")&&this.openMenu(e.menu,!0)}),this.focusables.forEach(e=>{e.addEventListener("keydown",this.onFocusableKeyDown)}),this.options.closeOnBlur&&this.nav.addEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("init",{detail:this}))}onButtonClick(e){const t=e.currentTarget,s=this.controls.find(e=>e.el===t),n="true"===(null==s?void 0:s.el.getAttribute("aria-expanded"));null!=s&&s.menu&&this.toggleMenu(s.menu,!n)}onButtonKeyDown(e){const t=this.getControlFromEl(e.target);if(!t)return;const s="true"===t.el.getAttribute("aria-expanded");if("Escape"===e.key)if(s)this.closeMenu(t.menu);else{const e=t.el.closest(".a11y-nav-active");if(e){const t=this.getMenuFromEl(e);t?(t.control.el.focus(),this.closeMenu(t)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}else if("ArrowDown"===e.key&&s){var n;e.preventDefault(),null==(n=t.menu.el.querySelector("a, button"))||n.focus()}else{const s=this.focusables.filter(e=>this.getMenuDepthFromEl(e)===this.getMenuDepthFromEl(t.el)),n=s.findIndex(e=>e===t.el);this.options.useArrowKeys&&this.controlFocusByKey(e,s.map(e=>e),n)}}onFocusableKeyDown(e){const t=this.getFocusableFromEl(e.target);if(!t)return;if(this.controls.find(e=>e.el===t))return;if("Escape"===e.key){const e=t.closest(".a11y-nav-active");if(e){const t=this.getMenuFromEl(e);t?(t.control.el.focus(),this.closeMenu(t)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}const s=this.focusables.filter(e=>this.getMenuDepthFromEl(e)===this.getMenuDepthFromEl(t)),n=s.findIndex(e=>e===t);this.options.useArrowKeys&&this.controlFocusByKey(e,s.map(e=>e),n)}onBlur(e){!this.nav.contains(e.relatedTarget)&&this.nav.querySelector(".a11y-nav-active")&&this.closeAllMenus()}controlFocusByKey(e,t,s){switch(e.key){case"ArrowUp":case"ArrowLeft":e.preventDefault(),s>-1&&t[Math.max(0,s-1)].focus();break;case"ArrowDown":case"ArrowRight":e.preventDefault(),s>-1&&t[Math.min(t.length-1,s+1)].focus()}}toggleMenu(e,t){t?this.openMenu(e):this.closeMenu(e)}openMenu(e,t=!1){var s;this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:e}})),this.menus.forEach(t=>{t.el!==e.el&&t.el.classList.contains("a11y-nav-active")&&this.getMenuDepthFromEl(t.el)===this.getMenuDepthFromEl(e.el)&&this.closeMenu(t)}),e.el.classList.add("a11y-nav-active"),e.control.el.setAttribute("aria-expanded","true"),null==(s=e.el.parentElement)||s.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(e.el.classList.add("a11y-nav-animate-in"),!t&&this.options.focusOnOpen&&setTimeout(()=>{e.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:e}}))},this.options.duration)):!t&&this.options.focusOnOpen&&(e.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:e}})))}closeMenu(e){if(!e.el.classList.contains("a11y-nav-active"))return;this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:e}})),e.el.querySelectorAll(".a11y-nav-menu").forEach(e=>{const t=this.getMenuFromEl(e);t&&this.closeMenu(t)});const t=this.menus.some(t=>t.el.classList.contains("a11y-nav-active")&&t.el!==e.el);var s;"string"!=typeof this.options.bodyClass||t||document.body.classList.remove(this.options.bodyClass),e.control.el.setAttribute("aria-expanded","false"),this.options.animate?(e.el.classList.remove("a11y-nav-animate-in"),e.el.classList.add("a11y-nav-animate-out"),setTimeout(()=>{var t;e.el.classList.remove("a11y-nav-active"),e.el.classList.remove("a11y-nav-animate-out"),null==(t=e.el.parentElement)||t.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:e}}))},this.options.duration)):(e.el.classList.remove("a11y-nav-active"),null==(s=e.el.parentElement)||s.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:e}})))}closeAllMenus(){this.menus.forEach(e=>{this.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)}getMenuDepthFromEl(e){let t=0,s=e.parentElement;for(;s&&s!==this.nav;)(s.classList.contains("a11y-nav-menu")||s===this.nav)&&t++,s=s.parentElement;return t}getMenuFromEl(e){var t;return null!=(t=this.menus.find(t=>t.el===e))?t:null}getControlFromEl(e){var t;return null!=(t=this.controls.find(t=>t.el===e))?t:null}getFocusableFromEl(e){var t;return null!=(t=this.focusables.find(t=>t===e))?t:null}getControls(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(e=>{const t=e.getAttribute("aria-controls"),s=document.getElementById(null!=t?t:"");if(s){const t={el:e,menu:{el:s,id:s.id,hadTabIndex:s.hasAttribute("tabindex")}};return t.menu.control=t,t}return null}).flatMap(e=>e?[e]:[])}getFocusables(){return Array.from(this.nav.querySelectorAll("a, button"))}destroy(){this.closeAllMenus(),this.controls.forEach(e=>{e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",this.onButtonClick),e.el.removeEventListener("keydown",this.onButtonKeyDown)}),this.focusables.forEach(e=>{e.removeEventListener("keydown",this.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))}}export{t as default}; +function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;et.menu),this.focusables=this.getFocusables(),this.options=t({},this.options,s),this.onButtonClick=this.onButtonClick.bind(this),this.onButtonKeyDown=this.onButtonKeyDown.bind(this),this.onFocusableKeyDown=this.onFocusableKeyDown.bind(this),this.onBlur=this.onBlur.bind(this),this.init()}init(){this.controls.forEach(t=>{t.menu.el.classList.add("a11y-nav-menu"),t.menu.el.setAttribute("tabindex","-1"),t.el.addEventListener("click",this.onButtonClick),t.el.addEventListener("keydown",this.onButtonKeyDown),"true"===t.el.getAttribute("aria-expanded")&&this.openMenu(t.menu,!0)}),this.focusables.forEach(t=>{t.addEventListener("keydown",this.onFocusableKeyDown)}),this.options.closeOnBlur&&this.nav.addEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("init",{detail:this}))}onButtonClick(t){const e=t.currentTarget,s=this.controls.find(t=>t.el===e),n="true"===(null==s?void 0:s.el.getAttribute("aria-expanded"));null!=s&&s.menu&&this.toggleMenu(s.menu,!n)}onButtonKeyDown(t){const e=this.getControlFromEl(t.target);if(!e)return;const s="true"===e.el.getAttribute("aria-expanded");if("Escape"===t.key)if(s)this.closeMenu(e.menu);else{const t=e.el.closest(".a11y-nav-active");if(t){const e=this.getMenuFromEl(t);e?(e.control.el.focus(),this.closeMenu(e)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}else if("ArrowDown"===t.key&&s){var n;t.preventDefault(),null==(n=e.menu.el.querySelector("a, button"))||n.focus()}else{const s=this.focusables.filter(t=>this.getMenuDepthFromEl(t)===this.getMenuDepthFromEl(e.el)),n=s.findIndex(t=>t===e.el);this.options.useArrowKeys&&this.controlFocusByKey(t,s.map(t=>t),n)}}onFocusableKeyDown(t){const e=this.getFocusableFromEl(t.target);if(!e)return;if(this.controls.find(t=>t.el===e))return;if("Escape"===t.key){const t=e.closest(".a11y-nav-active");if(t){const e=this.getMenuFromEl(t);e?(e.control.el.focus(),this.closeMenu(e)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}const s=this.focusables.filter(t=>this.getMenuDepthFromEl(t)===this.getMenuDepthFromEl(e)),n=s.findIndex(t=>t===e);this.options.useArrowKeys&&this.controlFocusByKey(t,s.map(t=>t),n)}onBlur(t){!this.nav.contains(t.relatedTarget)&&this.nav.querySelector(".a11y-nav-active")&&this.closeAllMenus()}controlFocusByKey(t,e,s){switch(t.key){case"ArrowUp":case"ArrowLeft":t.preventDefault(),s>-1&&e[Math.max(0,s-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),s>-1&&e[Math.min(e.length-1,s+1)].focus()}}toggleMenu(t,e){e?this.openMenu(t):this.closeMenu(t)}openMenu(t,e=!1){var s;this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(e=>{e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&this.getMenuDepthFromEl(e.el)===this.getMenuDepthFromEl(t.el)&&this.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(s=t.el.parentElement)||s.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(()=>{t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))}closeMenu(t){if(!t.el.classList.contains("a11y-nav-active"))return;this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(t=>{const e=this.getMenuFromEl(t);e&&this.closeMenu(e)});const e=()=>{var e;const s=this.menus.some(e=>e.el.classList.contains("a11y-nav-active")&&e.el!==t.el);"string"!=typeof this.options.bodyClass||s||document.body.classList.remove(this.options.bodyClass),t.el.classList.remove("a11y-nav-active"),null==(e=t.el.parentElement)||e.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:t}}))};t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(()=>{e(),t.el.classList.remove("a11y-nav-animate-out")},this.options.duration)):e()}closeAllMenus(){this.menus.forEach(t=>{this.closeMenu(t)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)}getMenuDepthFromEl(t){let e=0,s=t.parentElement;for(;s&&s!==this.nav;)(s.classList.contains("a11y-nav-menu")||s===this.nav)&&e++,s=s.parentElement;return e}getMenuFromEl(t){var e;return null!=(e=this.menus.find(e=>e.el===t))?e:null}getControlFromEl(t){var e;return null!=(e=this.controls.find(e=>e.el===t))?e:null}getFocusableFromEl(t){var e;return null!=(e=this.focusables.find(e=>e===t))?e:null}getControls(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(t=>{const e=t.getAttribute("aria-controls"),s=document.getElementById(null!=e?e:"");if(s){const e={el:t,menu:{el:s,id:s.id,hadTabIndex:s.hasAttribute("tabindex")}};return e.menu.control=e,e}return null}).flatMap(t=>t?[t]:[])}getFocusables(){return Array.from(this.nav.querySelectorAll("a, button"))}destroy(){this.closeAllMenus(),this.controls.forEach(t=>{t.menu.el.classList.remove("a11y-nav-menu"),t.menu.hadTabIndex||t.menu.el.removeAttribute("tabindex"),t.el.removeEventListener("click",this.onButtonClick),t.el.removeEventListener("keydown",this.onButtonKeyDown)}),this.focusables.forEach(t=>{t.removeEventListener("keydown",this.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))}}export{e as default}; //# sourceMappingURL=a11y-nav.modern.js.map diff --git a/dist/a11y-nav.modern.js.map b/dist/a11y-nav.modern.js.map index 75d7020..35d24b8 100644 --- a/dist/a11y-nav.modern.js.map +++ b/dist/a11y-nav.modern.js.map @@ -1 +1 @@ -{"version":3,"file":"a11y-nav.modern.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n } else {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["A11YNav","constructor","element","options","nav","this","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","CustomEvent","detail","event","button","currentTarget","find","toggleMenu","isOpen","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","querySelectorAll","childMenuEl","childMenu","some","m","hasOtherOpenMenus","remove","_menu$el$parentElemen2","_menu$el$parentElemen3","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"a11y-nav.modern.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n const close = () => {\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n close();\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n }, this.options.duration);\r\n } else {\r\n close();\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["A11YNav","constructor","element","options","nav","this","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","preventDefault","_control$menu$el$quer","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","findIndex","controlFocusByKey","currentIndex","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","CustomEvent","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","querySelectorAll","childMenuEl","childMenu","close","_menu$el$parentElemen2","some","m","hasOtherOpenMenus","remove","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/dist/a11y-nav.module.js b/dist/a11y-nav.module.js index 3c49a40..87eed0b 100644 --- a/dist/a11y-nav.module.js +++ b/dist/a11y-nav.module.js @@ -1,2 +1,2 @@ -function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,s=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&s.getMenuDepthFromEl(e.el)===s.getMenuDepthFromEl(t.el)&&s.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),s.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:s,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n,s=this.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof this.options.bodyClass||s||document.body.classList.remove(this.options.bodyClass),t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){var n;t.el.classList.remove("a11y-nav-active"),t.el.classList.remove("a11y-nav-animate-out"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))},this.options.duration)):(t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:t}})))}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var s={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return s.menu.control=s,s}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}();export{e as default}; +function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,o=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&o.getMenuDepthFromEl(e.el)===o.getMenuDepthFromEl(t.el)&&o.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),o.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:o,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n=function(){var n,o=e.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof e.options.bodyClass||o||document.body.classList.remove(e.options.bodyClass),t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))};t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){n(),t.el.classList.remove("a11y-nav-animate-out")},this.options.duration)):n()}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var o={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return o.menu.control=o,o}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}();export{e as default}; //# sourceMappingURL=a11y-nav.module.js.map diff --git a/dist/a11y-nav.module.js.map b/dist/a11y-nav.module.js.map index 5536761..4e54cac 100644 --- a/dist/a11y-nav.module.js.map +++ b/dist/a11y-nav.module.js.map @@ -1 +1 @@ -{"version":3,"file":"a11y-nav.module.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n } else {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["A11YNav","element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","CustomEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","hasOtherOpenMenus","some","m","remove","_menu$el$parentElemen2","_menu$el$parentElemen3","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"a11y-nav.module.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n const close = () => {\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n close();\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n }, this.options.duration);\r\n } else {\r\n close();\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["A11YNav","element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","_control$menu$el$quer","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","CustomEvent","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","close","_menu$el$parentElemen2","hasOtherOpenMenus","some","m","remove","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/dist/a11y-nav.umd.js b/dist/a11y-nav.umd.js index 42e3924..a324fff 100644 --- a/dist/a11y-nav.umd.js +++ b/dist/a11y-nav.umd.js @@ -1,3 +1,3 @@ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t||self).A11YNav=e()}(this,function(){function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,o=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&o.getMenuDepthFromEl(e.el)===o.getMenuDepthFromEl(t.el)&&o.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),o.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:o,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n,o=this.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof this.options.bodyClass||o||document.body.classList.remove(this.options.bodyClass),t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){var n;t.el.classList.remove("a11y-nav-active"),t.el.classList.remove("a11y-nav-animate-out"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))},this.options.duration)):(t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),this.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:this,menu:t}})))}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var o={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return o.menu.control=o,o}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}()}); +return function(){function e(e,n){this.nav=void 0,this.options=void 0,this.controls=void 0,this.menus=void 0,this.focusables=void 0,this.nav=e,this.options={animate:!0,duration:300,useArrowKeys:!0,closeOnBlur:!0,bodyClass:"a11y-nav-menu-open",focusOnOpen:!0},this.controls=this.getControls(),this.menus=this.controls.map(function(t){return t.menu}),this.focusables=this.getFocusables(),this.options=t({},this.options,n),this.onButtonClick=this.onButtonClick.bind(this),this.onButtonKeyDown=this.onButtonKeyDown.bind(this),this.onFocusableKeyDown=this.onFocusableKeyDown.bind(this),this.onBlur=this.onBlur.bind(this),this.init()}var n=e.prototype;return n.init=function(){var t=this;this.controls.forEach(function(e){e.menu.el.classList.add("a11y-nav-menu"),e.menu.el.setAttribute("tabindex","-1"),e.el.addEventListener("click",t.onButtonClick),e.el.addEventListener("keydown",t.onButtonKeyDown),"true"===e.el.getAttribute("aria-expanded")&&t.openMenu(e.menu,!0)}),this.focusables.forEach(function(e){e.addEventListener("keydown",t.onFocusableKeyDown)}),this.options.closeOnBlur&&this.nav.addEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("init",{detail:this}))},n.onButtonClick=function(t){var e=t.currentTarget,n=this.controls.find(function(t){return t.el===e}),o="true"===(null==n?void 0:n.el.getAttribute("aria-expanded"));null!=n&&n.menu&&this.toggleMenu(n.menu,!o)},n.onButtonKeyDown=function(t){var e=this,n=this.getControlFromEl(t.target);if(n){var o="true"===n.el.getAttribute("aria-expanded");if("Escape"===t.key)if(o)this.closeMenu(n.menu);else{var s=n.el.closest(".a11y-nav-active");if(s){var i=this.getMenuFromEl(s);i?(i.control.el.focus(),this.closeMenu(i)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}else if("ArrowDown"===t.key&&o){var a;t.preventDefault(),null==(a=n.menu.el.querySelector("a, button"))||a.focus()}else{var l=this.focusables.filter(function(t){return e.getMenuDepthFromEl(t)===e.getMenuDepthFromEl(n.el)}),u=l.findIndex(function(t){return t===n.el});this.options.useArrowKeys&&this.controlFocusByKey(t,l.map(function(t){return t}),u)}}},n.onFocusableKeyDown=function(t){var e=this,n=this.getFocusableFromEl(t.target);if(n&&!this.controls.find(function(t){return t.el===n})){if("Escape"===t.key){var o=n.closest(".a11y-nav-active");if(o){var s=this.getMenuFromEl(o);s?(s.control.el.focus(),this.closeMenu(s)):(this.focusables[0].focus(),this.closeAllMenus())}else this.focusables[0].focus(),this.closeAllMenus()}var i=this.focusables.filter(function(t){return e.getMenuDepthFromEl(t)===e.getMenuDepthFromEl(n)}),a=i.findIndex(function(t){return t===n});this.options.useArrowKeys&&this.controlFocusByKey(t,i.map(function(t){return t}),a)}},n.onBlur=function(t){!this.nav.contains(t.relatedTarget)&&this.nav.querySelector(".a11y-nav-active")&&this.closeAllMenus()},n.controlFocusByKey=function(t,e,n){switch(t.key){case"ArrowUp":case"ArrowLeft":t.preventDefault(),n>-1&&e[Math.max(0,n-1)].focus();break;case"ArrowDown":case"ArrowRight":t.preventDefault(),n>-1&&e[Math.min(e.length-1,n+1)].focus()}},n.toggleMenu=function(t,e){e?this.openMenu(t):this.closeMenu(t)},n.openMenu=function(t,e){var n,o=this;void 0===e&&(e=!1),this.nav.dispatchEvent(new CustomEvent("beforeOpen",{detail:{a11yNav:this,menu:t}})),this.menus.forEach(function(e){e.el!==t.el&&e.el.classList.contains("a11y-nav-active")&&o.getMenuDepthFromEl(e.el)===o.getMenuDepthFromEl(t.el)&&o.closeMenu(e)}),t.el.classList.add("a11y-nav-active"),t.control.el.setAttribute("aria-expanded","true"),null==(n=t.el.parentElement)||n.classList.add("a11y-nav-child-open"),"string"==typeof this.options.bodyClass&&this.options.bodyClass.length>0&&document.body.classList.add(this.options.bodyClass),this.options.animate?(t.el.classList.add("a11y-nav-animate-in"),!e&&this.options.focusOnOpen&&setTimeout(function(){t.el.focus({preventScroll:!0}),o.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:o,menu:t}}))},this.options.duration)):!e&&this.options.focusOnOpen&&(t.el.focus({preventScroll:!0}),this.nav.dispatchEvent(new CustomEvent("afterOpen",{detail:{a11yNav:this,menu:t}})))},n.closeMenu=function(t){var e=this;if(t.el.classList.contains("a11y-nav-active")){this.nav.dispatchEvent(new CustomEvent("beforeClose",{detail:{a11yNav:this,menu:t}})),t.el.querySelectorAll(".a11y-nav-menu").forEach(function(t){var n=e.getMenuFromEl(t);n&&e.closeMenu(n)});var n=function(){var n,o=e.menus.some(function(e){return e.el.classList.contains("a11y-nav-active")&&e.el!==t.el});"string"!=typeof e.options.bodyClass||o||document.body.classList.remove(e.options.bodyClass),t.el.classList.remove("a11y-nav-active"),null==(n=t.el.parentElement)||n.classList.remove("a11y-nav-child-open"),e.nav.dispatchEvent(new CustomEvent("afterClose",{detail:{a11yNav:e,menu:t}}))};t.control.el.setAttribute("aria-expanded","false"),this.options.animate?(t.el.classList.remove("a11y-nav-animate-in"),t.el.classList.add("a11y-nav-animate-out"),setTimeout(function(){n(),t.el.classList.remove("a11y-nav-animate-out")},this.options.duration)):n()}},n.closeAllMenus=function(){var t=this;this.menus.forEach(function(e){t.closeMenu(e)}),"string"==typeof this.options.bodyClass&&document.body.classList.remove(this.options.bodyClass)},n.getMenuDepthFromEl=function(t){for(var e=0,n=t.parentElement;n&&n!==this.nav;)(n.classList.contains("a11y-nav-menu")||n===this.nav)&&e++,n=n.parentElement;return e},n.getMenuFromEl=function(t){var e;return null!=(e=this.menus.find(function(e){return e.el===t}))?e:null},n.getControlFromEl=function(t){var e;return null!=(e=this.controls.find(function(e){return e.el===t}))?e:null},n.getFocusableFromEl=function(t){var e;return null!=(e=this.focusables.find(function(e){return e===t}))?e:null},n.getControls=function(){return Array.from(this.nav.querySelectorAll("button[aria-expanded][aria-controls]")).map(function(t){var e=t.getAttribute("aria-controls"),n=document.getElementById(null!=e?e:"");if(n){var o={el:t,menu:{el:n,id:n.id,hadTabIndex:n.hasAttribute("tabindex")}};return o.menu.control=o,o}return null}).flatMap(function(t){return t?[t]:[]})},n.getFocusables=function(){return Array.from(this.nav.querySelectorAll("a, button"))},n.destroy=function(){var t=this;this.closeAllMenus(),this.controls.forEach(function(e){e.menu.el.classList.remove("a11y-nav-menu"),e.menu.hadTabIndex||e.menu.el.removeAttribute("tabindex"),e.el.removeEventListener("click",t.onButtonClick),e.el.removeEventListener("keydown",t.onButtonKeyDown)}),this.focusables.forEach(function(e){e.removeEventListener("keydown",t.onFocusableKeyDown)}),this.nav.removeEventListener("focusout",this.onBlur),this.nav.dispatchEvent(new CustomEvent("destroy",{detail:{a11yNav:this}}))},e}()}); //# sourceMappingURL=a11y-nav.umd.js.map diff --git a/dist/a11y-nav.umd.js.map b/dist/a11y-nav.umd.js.map index 10a0450..6031f73 100644 --- a/dist/a11y-nav.umd.js.map +++ b/dist/a11y-nav.umd.js.map @@ -1 +1 @@ -{"version":3,"file":"a11y-nav.umd.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n } else {\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","CustomEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","hasOtherOpenMenus","some","m","remove","_menu$el$parentElemen2","_menu$el$parentElemen3","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"a11y-nav.umd.js","sources":["../src/a11y-nav.ts"],"sourcesContent":["import \"./a11y-nav.css\";\r\n\r\ninterface A11YNavOptions {\r\n animate?: boolean;\r\n duration?: number;\r\n useArrowKeys?: boolean;\r\n closeOnBlur?: boolean;\r\n bodyClass?: string | boolean;\r\n focusOnOpen?: boolean;\r\n}\r\n\r\ninterface Control {\r\n el: HTMLButtonElement;\r\n menu: Menu;\r\n}\r\n\r\ninterface Menu {\r\n el: HTMLElement;\r\n id: string;\r\n control: Control;\r\n hadTabIndex: boolean;\r\n}\r\n\r\nexport default class A11YNav {\r\n nav: HTMLElement;\r\n options: Required;\r\n controls: Control[];\r\n menus: Menu[];\r\n focusables: HTMLElement[];\r\n\r\n constructor(element: HTMLElement, options?: A11YNavOptions) {\r\n this.nav = element;\r\n this.options = {\r\n // adds delay for toggling menu open/close animation classes\r\n animate: true,\r\n // amount of time in ms for menu open/close animation\r\n duration: 300,\r\n // Enables use of arrow keys to navigate menus\r\n useArrowKeys: true,\r\n // Enables closing of menus when focus leaves the nav\r\n closeOnBlur: true,\r\n // Class to add to body when a menu is open. If false, no class is added.\r\n bodyClass: \"a11y-nav-menu-open\",\r\n // Focus menu that just opened\r\n focusOnOpen: true,\r\n };\r\n this.controls = this.getControls();\r\n this.menus = this.controls.map((control) => control.menu);\r\n this.focusables = this.getFocusables();\r\n\r\n // Set user-inputted options if available\r\n this.options = { ...this.options, ...options };\r\n\r\n // Bind event handlers\r\n this.onButtonClick = this.onButtonClick.bind(this);\r\n this.onButtonKeyDown = this.onButtonKeyDown.bind(this);\r\n this.onFocusableKeyDown = this.onFocusableKeyDown.bind(this);\r\n this.onBlur = this.onBlur.bind(this);\r\n\r\n this.init();\r\n }\r\n\r\n private init(): void {\r\n this.controls.forEach((control) => {\r\n // set initial properties\r\n control.menu.el.classList.add(\"a11y-nav-menu\");\r\n control.menu.el.setAttribute(\"tabindex\", \"-1\");\r\n\r\n // Attach event listeners\r\n control.el.addEventListener(\"click\", this.onButtonClick);\r\n control.el.addEventListener(\"keydown\", this.onButtonKeyDown);\r\n\r\n // Open menu if aria-expanded is true on page load\r\n if (control.el.getAttribute(\"aria-expanded\") === \"true\") {\r\n this.openMenu(control.menu, true);\r\n }\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.addEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n if (this.options.closeOnBlur) {\r\n this.nav.addEventListener(\"focusout\", this.onBlur);\r\n }\r\n\r\n this.nav.dispatchEvent(new CustomEvent(\"init\", { detail: this }));\r\n }\r\n\r\n private onButtonClick(event: MouseEvent): void {\r\n const button = event.currentTarget as HTMLButtonElement;\r\n const control = this.controls.find((control) => control.el === button);\r\n const isOpen = control?.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (control?.menu) {\r\n this.toggleMenu(control.menu, !isOpen);\r\n }\r\n }\r\n\r\n private onButtonKeyDown(event: KeyboardEvent): void {\r\n const control = this.getControlFromEl(event.target as HTMLButtonElement);\r\n\r\n if (!control) return;\r\n\r\n const hasMenuOpen = control.el.getAttribute(\"aria-expanded\") === \"true\";\r\n\r\n if (event.key === \"Escape\") {\r\n if (hasMenuOpen) {\r\n this.closeMenu(control.menu);\r\n } else {\r\n const closestMenuEl =\r\n control.el.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n } else if (event.key === \"ArrowDown\" && hasMenuOpen) {\r\n event.preventDefault();\r\n control.menu.el.querySelector(\"a, button\")?.focus();\r\n } else {\r\n const sameLevelFocusables = this.focusables.filter(\r\n (focusable) =>\r\n this.getMenuDepthFromEl(focusable) ===\r\n this.getMenuDepthFromEl(control.el)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex(\r\n (focusable) => focusable === control.el\r\n );\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((focusable) => focusable),\r\n currentIndex\r\n );\r\n }\r\n }\r\n }\r\n\r\n private onFocusableKeyDown(event: KeyboardEvent): void {\r\n const el = event.target as HTMLButtonElement;\r\n const focusable = this.getFocusableFromEl(el);\r\n\r\n if (!focusable) {\r\n return;\r\n }\r\n // Let onButtonKeyDown handle the rest\r\n else if (this.controls.find((control) => control.el === focusable)) {\r\n return;\r\n } else if (event.key === \"Escape\") {\r\n const closestMenuEl = focusable.closest(\".a11y-nav-active\");\r\n\r\n if (closestMenuEl) {\r\n const menu = this.getMenuFromEl(closestMenuEl);\r\n\r\n if (menu) {\r\n menu.control.el.focus();\r\n this.closeMenu(menu);\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n } else {\r\n this.focusables[0].focus();\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n const sameLevelFocusables = this.focusables.filter(\r\n (f) => this.getMenuDepthFromEl(f) === this.getMenuDepthFromEl(focusable)\r\n );\r\n const currentIndex = sameLevelFocusables.findIndex((f) => f === focusable);\r\n\r\n if (this.options.useArrowKeys) {\r\n this.controlFocusByKey(\r\n event,\r\n sameLevelFocusables.map((f) => f),\r\n currentIndex\r\n );\r\n }\r\n }\r\n\r\n private onBlur(event: FocusEvent): void {\r\n const menuContainsFocus = this.nav.contains(\r\n event.relatedTarget as HTMLElement\r\n );\r\n\r\n if (\r\n !menuContainsFocus &&\r\n !!this.nav.querySelector(\".a11y-nav-active\")\r\n ) {\r\n this.closeAllMenus();\r\n }\r\n }\r\n\r\n private controlFocusByKey(\r\n event: KeyboardEvent,\r\n els: HTMLElement[],\r\n currentIndex: number\r\n ): void {\r\n switch (event.key) {\r\n case \"ArrowUp\":\r\n case \"ArrowLeft\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const prevIndex = Math.max(0, currentIndex - 1);\r\n els[prevIndex].focus();\r\n }\r\n break;\r\n case \"ArrowDown\":\r\n case \"ArrowRight\":\r\n event.preventDefault();\r\n if (currentIndex > -1) {\r\n const nextIndex = Math.min(els.length - 1, currentIndex + 1);\r\n els[nextIndex].focus();\r\n }\r\n break;\r\n }\r\n }\r\n\r\n private toggleMenu(menu: Menu, show: boolean): void {\r\n if (show) {\r\n this.openMenu(menu);\r\n } else {\r\n this.closeMenu(menu);\r\n }\r\n }\r\n\r\n private openMenu(menu: Menu, forceNoFocus = false): void {\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n\r\n // Close all other menus on the same level\r\n this.menus.forEach((otherMenu) => {\r\n if (\r\n otherMenu.el !== menu.el &&\r\n otherMenu.el.classList.contains(\"a11y-nav-active\") &&\r\n this.getMenuDepthFromEl(otherMenu.el) ===\r\n this.getMenuDepthFromEl(menu.el)\r\n ) {\r\n this.closeMenu(otherMenu);\r\n }\r\n });\r\n\r\n // Assign classes/properties\r\n menu.el.classList.add(\"a11y-nav-active\");\r\n menu.control.el.setAttribute(\"aria-expanded\", \"true\");\r\n menu.el.parentElement?.classList.add(\"a11y-nav-child-open\");\r\n if (\r\n typeof this.options.bodyClass === \"string\" &&\r\n this.options.bodyClass.length > 0\r\n ) {\r\n document.body.classList.add(this.options.bodyClass);\r\n }\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.add(\"a11y-nav-animate-in\");\r\n\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n setTimeout(() => {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }, this.options.duration);\r\n }\r\n } else {\r\n if (!forceNoFocus && this.options.focusOnOpen) {\r\n menu.el.focus({\r\n preventScroll: true,\r\n });\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterOpen\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n }\r\n }\r\n\r\n private closeMenu(menu: Menu): void {\r\n // Skip this if it's already closed\r\n if (!menu.el.classList.contains(\"a11y-nav-active\")) return;\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"beforeClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n \r\n // Close all children menus currently open first\r\n menu.el\r\n .querySelectorAll(\".a11y-nav-menu\")\r\n .forEach((childMenuEl) => {\r\n const childMenu = this.getMenuFromEl(childMenuEl);\r\n\r\n if (childMenu) this.closeMenu(childMenu);\r\n });\r\n\r\n const close = () => {\r\n // Checks if any other menus are open on other levels\r\n const hasOtherOpenMenus = this.menus.some(\r\n (m) => m.el.classList.contains(\"a11y-nav-active\") && m.el !== menu.el\r\n );\r\n\r\n // Set classes/properties\r\n if (typeof this.options.bodyClass === \"string\" && !hasOtherOpenMenus) {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n\r\n menu.el.classList.remove(\"a11y-nav-active\");\r\n menu.el.parentElement?.classList.remove(\"a11y-nav-child-open\");\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"afterClose\", {\r\n detail: {\r\n a11yNav: this,\r\n menu,\r\n },\r\n })\r\n );\r\n }\r\n\r\n menu.control.el.setAttribute(\"aria-expanded\", \"false\");\r\n\r\n if (this.options.animate) {\r\n menu.el.classList.remove(\"a11y-nav-animate-in\");\r\n menu.el.classList.add(\"a11y-nav-animate-out\");\r\n\r\n setTimeout(() => {\r\n close();\r\n menu.el.classList.remove(\"a11y-nav-animate-out\");\r\n }, this.options.duration);\r\n } else {\r\n close();\r\n }\r\n }\r\n\r\n public closeAllMenus(): void {\r\n this.menus.forEach((menu) => {\r\n this.closeMenu(menu);\r\n });\r\n\r\n if (typeof this.options.bodyClass === \"string\") {\r\n document.body.classList.remove(this.options.bodyClass);\r\n }\r\n }\r\n\r\n /** Get the menu depth of an element */\r\n private getMenuDepthFromEl(element: HTMLElement): number {\r\n let level = 0;\r\n let parent = element.parentElement;\r\n\r\n while (parent && parent !== this.nav) {\r\n if (parent.classList.contains(\"a11y-nav-menu\") || parent === this.nav) {\r\n level++;\r\n }\r\n parent = parent.parentElement;\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private getMenuFromEl(element: HTMLElement): Menu | null {\r\n return this.menus.find((menu) => menu.el === element) ?? null;\r\n }\r\n\r\n private getControlFromEl(element: HTMLElement): Control | null {\r\n return this.controls.find((control) => control.el === element) ?? null;\r\n }\r\n\r\n private getFocusableFromEl(element: HTMLElement): HTMLElement | null {\r\n return this.focusables.find((focusable) => focusable === element) ?? null;\r\n }\r\n\r\n private getControls(): Control[] {\r\n return Array.from(\r\n this.nav.querySelectorAll(\r\n \"button[aria-expanded][aria-controls]\"\r\n )\r\n )\r\n .map((element) => {\r\n const id = element.getAttribute(\"aria-controls\");\r\n const menu = document.getElementById(id ?? \"\");\r\n\r\n if (menu) {\r\n const control: Control = {\r\n el: element,\r\n menu: {\r\n el: menu,\r\n id: menu.id,\r\n hadTabIndex: menu.hasAttribute(\"tabindex\"),\r\n },\r\n } as Control;\r\n\r\n control.menu.control = control;\r\n\r\n return control;\r\n } else {\r\n return null;\r\n }\r\n })\r\n .flatMap((control) => (control ? [control] : []));\r\n }\r\n\r\n private getFocusables(): HTMLElement[] {\r\n return Array.from(this.nav.querySelectorAll(\"a, button\"));\r\n }\r\n\r\n public destroy(): void {\r\n this.closeAllMenus();\r\n\r\n this.controls.forEach((control) => {\r\n control.menu.el.classList.remove(\"a11y-nav-menu\");\r\n if (!control.menu.hadTabIndex) {\r\n control.menu.el.removeAttribute(\"tabindex\");\r\n }\r\n\r\n control.el.removeEventListener(\"click\", this.onButtonClick);\r\n control.el.removeEventListener(\"keydown\", this.onButtonKeyDown);\r\n });\r\n\r\n this.focusables.forEach((focusable) => {\r\n focusable.removeEventListener(\"keydown\", this.onFocusableKeyDown);\r\n });\r\n\r\n this.nav.removeEventListener(\"focusout\", this.onBlur);\r\n\r\n this.nav.dispatchEvent(\r\n new CustomEvent(\"destroy\", {\r\n detail: {\r\n a11yNav: this,\r\n },\r\n })\r\n );\r\n }\r\n}\r\n"],"names":["element","options","this","nav","controls","menus","focusables","animate","duration","useArrowKeys","closeOnBlur","bodyClass","focusOnOpen","getControls","map","control","menu","getFocusables","_extends","onButtonClick","bind","onButtonKeyDown","onFocusableKeyDown","onBlur","init","_this","forEach","el","classList","add","setAttribute","addEventListener","getAttribute","openMenu","focusable","dispatchEvent","detail","event","button","currentTarget","find","isOpen","toggleMenu","_this2","getControlFromEl","target","hasMenuOpen","key","closeMenu","closestMenuEl","closest","getMenuFromEl","focus","closeAllMenus","_control$menu$el$quer","preventDefault","querySelector","sameLevelFocusables","filter","getMenuDepthFromEl","currentIndex","findIndex","controlFocusByKey","_this3","getFocusableFromEl","f","contains","relatedTarget","els","Math","max","min","length","show","forceNoFocus","_menu$el$parentElemen","_this4","CustomEvent","a11yNav","otherMenu","parentElement","document","body","setTimeout","preventScroll","_this5","querySelectorAll","childMenuEl","childMenu","close","_menu$el$parentElemen2","hasOtherOpenMenus","some","m","remove","_this6","level","parent","_this$menus$find","_this$controls$find","_this$focusables$find","Array","from","id","getElementById","hadTabIndex","hasAttribute","flatMap","destroy","_this7","removeAttribute","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0655bd4..09e0fea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "a11y-nav", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "a11y-nav", - "version": "1.3.0", + "version": "1.4.0", "license": "MIT", "devDependencies": { "@types/prismjs": "^1.26.0", diff --git a/package.json b/package.json index dc4f192..249cebe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "a11y-nav", - "version": "1.3.0", + "version": "1.4.0", "description": "Library for accessible navigations", "author": "mmahandev", "license": "MIT",