accessible-menu
Advanced tools
Comparing version 1.0.0-beta.3 to 1.0.0-beta.4
@@ -5,2 +5,13 @@ # Changelog | ||
## [1.0.0-beta.4](https://github.com/NickDJM/accessible-menu/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2019-11-22) | ||
### ⚠ BREAKING CHANGES | ||
* **menu:** Constructor params converted to object | ||
### Features | ||
* **menu:** add support for root dropdown menus ([1f85f4e](https://github.com/NickDJM/accessible-menu/commit/1f85f4e729fee4b1a05b7847d77c73209502a08b)), closes [#15](https://github.com/NickDJM/accessible-menu/issues/15) | ||
## [1.0.0-beta.3](https://github.com/NickDJM/accessible-menu/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2019-11-21) | ||
@@ -7,0 +18,0 @@ |
@@ -33,6 +33,10 @@ "use strict"; | ||
* | ||
* @param {object} menuItemElement - The menu item in the DOM. | ||
* @param {Menu} parentMenu - The parent menu. | ||
* @param {object} param0 - The menu item object. | ||
* @param {object} param0.menuItemElement - The menu item in the DOM. | ||
* @param {Menu} param0.parentMenu - The parent menu. | ||
*/ | ||
function MenuItem(menuItemElement, parentMenu) { | ||
function MenuItem(_ref) { | ||
var menuItemElement = _ref.menuItemElement, | ||
parentMenu = _ref.parentMenu; | ||
_classCallCheck(this, MenuItem); | ||
@@ -116,2 +120,8 @@ | ||
}, | ||
parentElement: function parentElement(value) { | ||
// Ensure value is an HTML element. | ||
if (!(value instanceof HTMLElement)) { | ||
throw new TypeError("parentElement must be an HTML Element."); | ||
} | ||
}, | ||
menu: function menu(value) { | ||
@@ -144,10 +154,2 @@ // Ensure value is an Menu element. | ||
}, | ||
parentMenuItem: function parentMenuItem(value) { | ||
// Value is allowed to be null. | ||
if (value === null) return; | ||
if (!(value instanceof MenuItem)) { | ||
throw new TypeError("parentMenuItem must be a MenuItem."); | ||
} | ||
}, | ||
rootMenu: function rootMenu(value) { | ||
@@ -169,14 +171,20 @@ // Value is allowed to be null. | ||
* | ||
* @param {object} menuToggleElement - The toggle element in the DOM. | ||
* @param {Menu} menu - The menu controlled by the this toggle. | ||
* @param {string} openClass - The class to use when a submenu is open. | ||
* @param {Menu} parentMenu - The menu containing the toggle. | ||
* @param {MenuItem} parentMenuItem - The menu item containing the toggle. | ||
* @param {Menu} rootMenu - The root menu containing the toggle. | ||
* @param {object} param0 - The menu toggle object. | ||
* @param {HTMLElement} param0.menuToggleElement - The toggle element in the DOM. | ||
* @param {HTMLElement} param0.parentElement - The element containing the menu. | ||
* @param {Menu} param0.menu - The menu controlled by the this toggle. | ||
* @param {string} param0.openClass - The class to use when a submenu is open. | ||
* @param {Menu} param0.parentMenu - The menu containing the toggle. | ||
* @param {Menu} param0.rootMenu - The root menu containing the toggle. | ||
*/ | ||
function MenuToggle(menuToggleElement, menu) { | ||
var openClass = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "show"; | ||
var parentMenu = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; | ||
var parentMenuItem = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; | ||
var rootMenu = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null; | ||
function MenuToggle(_ref2) { | ||
var menuToggleElement = _ref2.menuToggleElement, | ||
parentElement = _ref2.parentElement, | ||
menu = _ref2.menu, | ||
_ref2$openClass = _ref2.openClass, | ||
openClass = _ref2$openClass === void 0 ? "show" : _ref2$openClass, | ||
_ref2$parentMenu = _ref2.parentMenu, | ||
parentMenu = _ref2$parentMenu === void 0 ? null : _ref2$parentMenu, | ||
_ref2$rootMenu = _ref2.rootMenu, | ||
rootMenu = _ref2$rootMenu === void 0 ? null : _ref2$rootMenu; | ||
@@ -187,12 +195,12 @@ _classCallCheck(this, MenuToggle); | ||
validate$1.menuToggleElement(menuToggleElement); | ||
validate$1.parentElement(parentElement); | ||
validate$1.menu(menu); | ||
validate$1.openClass(openClass); | ||
validate$1.parentMenu(parentMenu); | ||
validate$1.parentMenuItem(parentMenuItem); | ||
validate$1.rootMenu(rootMenu); | ||
this.domElements = { | ||
toggle: menuToggleElement | ||
toggle: menuToggleElement, | ||
menuItem: parentElement | ||
}; | ||
this.elements = { | ||
menuItem: parentMenuItem, | ||
menu: menu, | ||
@@ -258,3 +266,3 @@ parentMenu: parentMenu, | ||
this.element.setAttribute("aria-expanded", "true"); | ||
this.menuItem.element.classList.add(this.openClass); | ||
this.menuItemElement.classList.add(this.openClass); | ||
this.menu.element.classList.add(this.openClass); // Close all sibling menus. | ||
@@ -264,3 +272,3 @@ | ||
this.parentMenu.currentFocus = "child"; | ||
if (this.parentMenu) this.parentMenu.currentFocus = "child"; | ||
this.menu.currentFocus = "self"; // Set the new focus. | ||
@@ -283,3 +291,3 @@ | ||
this.element.setAttribute("aria-expanded", "false"); | ||
this.menuItem.element.classList.remove(this.openClass); | ||
this.menuItemElement.classList.remove(this.openClass); | ||
this.menu.element.classList.remove(this.openClass); // Close all child menus. | ||
@@ -290,5 +298,5 @@ | ||
this.menu.currentFocus = "none"; | ||
this.parentMenu.currentFocus = "self"; // Set the new focus. | ||
if (this.parentMenu) this.parentMenu.currentFocus = "self"; // Set the new focus. | ||
this.parentMenu.focusCurrentChild(); | ||
if (this.parentMenu) this.parentMenu.focusCurrentChild(); | ||
} | ||
@@ -363,19 +371,21 @@ } | ||
_this3.close(); | ||
} else if (_this3.parentMenu.isTopLevel && key === "ArrowRight") { | ||
// The Right Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
} else if (_this3.parentMenu && _this3.parentMenu.isTopLevel) { | ||
if (key === "ArrowRight") { | ||
// The Right Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
_this3.close(); | ||
_this3.close(); | ||
_this3.parentMenu.focusNextChild(); | ||
} else if (_this3.parentMenu.isTopLevel && key === "ArrowLeft") { | ||
// The Left Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
_this3.parentMenu.focusNextChild(); | ||
} else if (key === "ArrowLeft") { | ||
// The Left Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
_this3.close(); | ||
_this3.close(); | ||
_this3.parentMenu.focusPreviousChild(); | ||
_this3.parentMenu.focusPreviousChild(); | ||
} | ||
} | ||
}); | ||
this.menuItem.element.addEventListener("keydown", function (event) { | ||
this.menuItemElement.addEventListener("keydown", function (event) { | ||
var key = event.key; | ||
@@ -412,5 +422,5 @@ | ||
}, { | ||
key: "menuItem", | ||
key: "menuItemElement", | ||
get: function get() { | ||
return this.elements.menuItem; | ||
return this.domElements.menuItem; | ||
} | ||
@@ -528,2 +538,14 @@ /** | ||
} | ||
}, | ||
isDropdown: function isDropdown(controller, container) { | ||
// Values are allowed to be null if both are null. | ||
if (controller === null && container === null) return; // Ensure value is an HTML element. | ||
if (!(controller instanceof HTMLElement)) { | ||
throw new TypeError("controllerElement must be an HTML Element if containerElement is provided."); | ||
} | ||
if (!(container instanceof HTMLElement)) { | ||
throw new TypeError("containerElement must be an HTML Element if controllerElement is provided."); | ||
} | ||
} | ||
@@ -538,13 +560,27 @@ }; | ||
* | ||
* @param {object} menuElement - The menu element in the DOM. | ||
* @param {string} menuItemSelector - The selector string for menu items. | ||
* @param {string} submenuItemSelector - The selector string for submenu items. | ||
* @param {string} submenuToggleSelector - The selector string for submenu toggle triggers. | ||
* @param {string} submenuSelector - The selector string for the submenu itself. | ||
* @param {string} submenuOpenClass - The class to use when a submenu is open. | ||
* @param {boolean} isTopLevel - Flags the menu as a top-level menu. | ||
* @param {object} param0 - The menu object. | ||
* @param {HTMLElement} param0.menuElement - The menu element in the DOM. | ||
* @param {string} param0.menuItemSelector - The selector string for menu items. | ||
* @param {string} param0.submenuItemSelector - The selector string for submenu items. | ||
* @param {string} param0.submenuToggleSelector - The selector string for submenu toggle triggers. | ||
* @param {string} param0.submenuSelector - The selector string for the submenu itself. | ||
* @param {string} param0.submenuOpenClass - The class to use when a submenu is open. | ||
* @param {boolean} param0.isTopLevel - Flags the menu as a top-level menu. | ||
* @param {HTMLElement} param0.controllerElement - The element controlling the menu in the DOM. | ||
* @param {HTMLElement} param0.containerElement - The element containing the menu in the DOM. | ||
*/ | ||
function Menu(menuElement, menuItemSelector, submenuItemSelector, submenuToggleSelector, submenuSelector) { | ||
var submenuOpenClass = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "show"; | ||
var isTopLevel = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : true; | ||
function Menu(_ref3) { | ||
var menuElement = _ref3.menuElement, | ||
menuItemSelector = _ref3.menuItemSelector, | ||
submenuItemSelector = _ref3.submenuItemSelector, | ||
submenuToggleSelector = _ref3.submenuToggleSelector, | ||
submenuSelector = _ref3.submenuSelector, | ||
_ref3$submenuOpenClas = _ref3.submenuOpenClass, | ||
submenuOpenClass = _ref3$submenuOpenClas === void 0 ? "show" : _ref3$submenuOpenClas, | ||
_ref3$isTopLevel = _ref3.isTopLevel, | ||
isTopLevel = _ref3$isTopLevel === void 0 ? true : _ref3$isTopLevel, | ||
_ref3$controllerEleme = _ref3.controllerElement, | ||
controllerElement = _ref3$controllerEleme === void 0 ? null : _ref3$controllerEleme, | ||
_ref3$containerElemen = _ref3.containerElement, | ||
containerElement = _ref3$containerElemen === void 0 ? null : _ref3$containerElemen; | ||
@@ -561,4 +597,7 @@ _classCallCheck(this, Menu); | ||
validate$2.isTopLevel(isTopLevel); | ||
validate$2.isDropdown(controllerElement, containerElement); | ||
this.domElements = { | ||
menu: menuElement, | ||
controller: controllerElement, | ||
container: containerElement, | ||
menuItems: Array.from(menuElement.querySelectorAll(menuItemSelector)).filter(function (item) { | ||
@@ -603,2 +642,13 @@ return item.parentElement === menuElement; | ||
this.handleClick(); | ||
if (this.controllerElement && this.containerElement) { | ||
// Create a new MenuToggle to control the menu. | ||
var toggle = new MenuToggle({ | ||
menuToggleElement: this.controllerElement, | ||
parentElement: this.containerElement, | ||
menu: this, | ||
openClass: this.openClass | ||
}); | ||
toggle.initialize(); | ||
} | ||
} | ||
@@ -622,3 +672,6 @@ /** | ||
// Create a new MenuItem. | ||
var menuItem = new MenuItem(element, _this4); // Add the item to the list of menu items. | ||
var menuItem = new MenuItem({ | ||
menuItemElement: element, | ||
parentMenu: _this4 | ||
}); // Add the item to the list of menu items. | ||
@@ -637,6 +690,20 @@ _this4.elements.menuItems.push(menuItem); // Initialize the menu item. | ||
var menu = new Menu(submenu, _this4.selector["menu-items"], _this4.selector["submenu-items"], _this4.selector["submenu-toggle"], _this4.selector.submenu, _this4.openClass, false); | ||
var menu = new Menu({ | ||
menuElement: submenu, | ||
menuItemSelector: _this4.selector["menu-items"], | ||
submenuItemSelector: _this4.selector["submenu-items"], | ||
submenuToggleSelector: _this4.selector["submenu-toggle"], | ||
submenuSelector: _this4.selector.submenu, | ||
submenuOpenClass: _this4.openClass, | ||
isTopLevel: false | ||
}); | ||
menu.initialize(); // Create the new MenuToggle. | ||
var toggle = new MenuToggle(toggler, menu, _this4.openClass, _this4, menuItem); | ||
var toggle = new MenuToggle({ | ||
menuToggleElement: toggler, | ||
parentElement: element, | ||
menu: menu, | ||
openClass: _this4.openClass, | ||
parentMenu: _this4 | ||
}); | ||
toggle.initialize(); // Add it to the list of submenu items. | ||
@@ -882,2 +949,24 @@ | ||
/** | ||
* The menu's controller element in the DOM. | ||
* | ||
* @returns {HTMLElement} - The controller element. | ||
*/ | ||
}, { | ||
key: "controllerElement", | ||
get: function get() { | ||
return this.domElements.controller; | ||
} | ||
/** | ||
* The menu's container element in the DOM. | ||
* | ||
* @returns {HTMLElement} - The container element. | ||
*/ | ||
}, { | ||
key: "containerElement", | ||
get: function get() { | ||
return this.domElements.container; | ||
} | ||
/** | ||
* The menu item DOM elements contained in the menu. | ||
@@ -884,0 +973,0 @@ * |
@@ -1,1 +0,1 @@ | ||
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n<t.length;n++){var s=t[n];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(e,s.key,s)}}function _createClass(e,t,n){return t&&_defineProperties(e.prototype,t),n&&_defineProperties(e,n),e}var AccessibleMenu=function(){var e={menuItemElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuItemElement must be an HTML Element.")},parentMenu:function(e){if(!(e instanceof i))throw new TypeError("parentMenu must be a Menu.")}},t=function(){function t(n,s){_classCallCheck(this,t),e.menuItemElement(n),e.parentMenu(s),this.domElements={menuItem:n,link:n.querySelector("a")},this.elements={parent:s}}return _createClass(t,[{key:"initialize",value:function(){this.element.setAttribute("role","menuitem"),this.link.tabIndex=-1}},{key:"focus",value:function(){this.element.querySelector("a").focus()}},{key:"element",get:function(){return this.domElements.menuItem}},{key:"link",get:function(){return this.domElements.link}},{key:"parentMenu",get:function(){return this.elements.parent}}]),t}(),n={menuToggleElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuToggleElement must be an HTML Element.")},menu:function(e){if(!(e instanceof i))throw new TypeError("menu must be a Menu.")},openClass:function(e){if("string"!=typeof e)throw TypeError("openClass must be a string.");if(e.replace(/[_a-zA-Z0-9-]/g,"").length>0)throw Error("openClass must be a valid CSS class.")},parentMenu:function(e){if(null!==e&&!(e instanceof i))throw new TypeError("parentMenu must be a Menu.")},parentMenuItem:function(e){if(null!==e&&!(e instanceof t))throw new TypeError("parentMenuItem must be a MenuItem.")},rootMenu:function(e){if(null!==e&&!(e instanceof i))throw new TypeError("rootMenu must be a Menu.")}},s=function(){function e(t,s){var u=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"show",i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null;_classCallCheck(this,e),n.menuToggleElement(t),n.menu(s),n.openClass(u),n.parentMenu(i),n.parentMenuItem(o),n.rootMenu(r),this.domElements={toggle:t},this.elements={menuItem:o,menu:s,parentMenu:i,rootMenu:r||i},this.openClass=u}return _createClass(e,[{key:"initialize",value:function(){var e=this;if(this.element.setAttribute("aria-haspopup","true"),this.element.setAttribute("aria-expanded","false"),this.element.setAttribute("role","button"),""===this.element.id||""===this.menu.element.id){var t=Math.random().toString(36).replace(/[^a-z]+/g,"").substr(0,10),n="".concat(this.element.innerText.toLowerCase().replace(/[^a-zA-Z0-9\s]/g,"").replace(/\s/g,"-"),"-").concat(t);this.element.id=this.element.id||"".concat(n,"-menu-button"),this.menu.element.id=this.menu.element.id||"".concat(n,"-menu")}this.menu.element.setAttribute("aria-labelledby",this.element.id),this.element.setAttribute("aria-controls",this.menu.element.id),this.element.addEventListener("click",(function(t){t.preventDefault(),t.stopPropagation(),e.toggle()})),this.handleKeydown()}},{key:"open",value:function(){this.isOpen||(this.isOpen=!0,this.element.setAttribute("aria-expanded","true"),this.menuItem.element.classList.add(this.openClass),this.menu.element.classList.add(this.openClass),this.closeSiblings(),this.parentMenu.currentFocus="child",this.menu.currentFocus="self",this.menu.focusFirstChild())}},{key:"close",value:function(){this.isOpen&&(this.isOpen=!1,this.element.setAttribute("aria-expanded","false"),this.menuItem.element.classList.remove(this.openClass),this.menu.element.classList.remove(this.openClass),this.closeChildren(),this.menu.currentFocus="none",this.parentMenu.currentFocus="self",this.parentMenu.focusCurrentChild())}},{key:"toggle",value:function(){this.isOpen?this.close():this.open()}},{key:"closeSiblings",value:function(){var e=this;try{this.parentMenu.menuToggles.forEach((function(t){t!==e&&t.close()}))}catch(e){}}},{key:"closeChildren",value:function(){this.menu.menuToggles.forEach((function(e){return e.close()}))}},{key:"handleKeydown",value:function(){var e=this;function t(e){e.preventDefault(),e.stopPropagation()}this.menu.element.addEventListener("keydown",(function(n){var s=n.key;"Escape"===s?(t(n),e.close()):e.parentMenu.isTopLevel&&"ArrowRight"===s?(t(n),e.close(),e.parentMenu.focusNextChild()):e.parentMenu.isTopLevel&&"ArrowLeft"===s&&(t(n),e.close(),e.parentMenu.focusPreviousChild())})),this.menuItem.element.addEventListener("keydown",(function(n){var s=n.key;"none"===e.menu.currentFocus&&e.parentMenu.isTopLevel&&("ArrowUp"===s?(t(n),e.open(),e.menu.focusLastChild()):"ArrowDown"===s&&(t(n),e.open()))}))}},{key:"element",get:function(){return this.domElements.toggle}},{key:"menuItem",get:function(){return this.elements.menuItem}},{key:"menu",get:function(){return this.elements.menu}},{key:"parentMenu",get:function(){return this.elements.parentMenu}},{key:"rootMenu",get:function(){return this.elements.rootMenu}},{key:"isOpen",get:function(){return this.show},set:function(e){if("boolean"!=typeof e)throw new TypeError("Open state must be true or false.");this.show=e}}]),e}(),u={menuElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuElement must be an HTML Element.")},menuItemSelector:function(e){if("string"!=typeof e)throw new TypeError("menuItemSelector must be a CSS selector string.")},submenuItemSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuItemSelector must be a CSS selector string.")},submenuToggleSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuToggleSelector must be a CSS selector string.")},submenuSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuSelector must be a CSS selector string.")},submenuOpenClass:function(e){if("string"!=typeof e)throw TypeError("submenuOpenClass must be a string.");if(e.replace(/[_a-zA-Z0-9-]/g,"").length>0)throw Error("submenuOpenClass must be a valid CSS class.")},isTopLevel:function(e){if("boolean"!=typeof e)throw new TypeError("isTopLevel must be true or false")}},i=function(){function e(t,n,s,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"show",l=!(arguments.length>6&&void 0!==arguments[6])||arguments[6];_classCallCheck(this,e),u.menuElement(t),u.menuItemSelector(n),u.submenuItemSelector(s),u.submenuToggleSelector(i),u.submenuSelector(o),u.submenuOpenClass(r),u.isTopLevel(l),this.domElements={menu:t,menuItems:Array.from(t.querySelectorAll(n)).filter((function(e){return e.parentElement===t})),submenuItems:Array.from(t.querySelectorAll(s)).filter((function(e){return e.parentElement===t}))},this.domSelectors={"menu-items":n,"submenu-items":s,"submenu-toggle":i,submenu:o},this.elements={menuItems:[],menuToggles:[]},this.focussedChild=-1,this.focusState="none",this.openClass=r,this.root=l}return _createClass(e,[{key:"initialize",value:function(){this.element.setAttribute("role","menu"),this.element.tabIndex=0,this.createMenuItems(),this.handleKeydown(),this.handleClick()}},{key:"createMenuItems",value:function(){var n=this;this.menuItemElements.forEach((function(u){var i=new t(u,n);if(n.elements.menuItems.push(i),i.initialize(),n.submenuItemElements.includes(u)){var o=u.querySelector(n.selector["submenu-toggle"]),r=new e(u.querySelector(n.selector.submenu),n.selector["menu-items"],n.selector["submenu-items"],n.selector["submenu-toggle"],n.selector.submenu,n.openClass,!1);r.initialize();var l=new s(o,r,n.openClass,n,i);l.initialize(),n.elements.menuToggles.push(l)}}))}},{key:"handleKeydown",value:function(){var e=this;function t(e){e.preventDefault(),e.stopPropagation()}this.element.addEventListener("keydown",(function(n){var s=n.key,u=n.code,i=n.altKey,o=n.crtlKey,r=n.metaKey,l=i||o||r;"none"===e.currentFocus?("Enter"===s||" "===s&&"Space"===u)&&(t(n),e.currentFocus="self",e.focusFirstChild()):"self"===e.currentFocus&&("Escape"===s?(t(n),e.focus(),e.currentFocus="none"):e.isTopLevel||"ArrowUp"!==s?e.isTopLevel&&"ArrowRight"===s?(t(n),e.focusNextChild()):e.isTopLevel||"ArrowDown"!==s?e.isTopLevel&&"ArrowLeft"===s?(t(n),e.focusPreviousChild()):"Home"===s?(t(n),e.focusFirstChild()):"End"===s?(t(n),e.focusLastChild()):s.match(/^[a-zA-Z]{1}$/)&&!l&&(t(n),e.focusNextChildWithCharacter(s)):(t(n),e.focusNextChild()):(t(n),e.focusPreviousChild())),"none"!==e.currentFocus&&"Tab"===s&&(e.blur(),e.closeChildren())}))}},{key:"handleClick",value:function(){var e=this;document.addEventListener("click",(function(t){e.element.contains(t.target)||e.element===t.target||(e.blur(),e.closeChildren())}))}},{key:"focus",value:function(){this.focussedChild=0,this.currentFocus="self",this.element.focus()}},{key:"blur",value:function(){this.focussedChild=-1,this.currentFocus="none",this.element.blur()}},{key:"focusFirstChild",value:function(){this.focussedChild=0,this.focusCurrentChild()}},{key:"focusLastChild",value:function(){this.focussedChild=this.menuItems.length-1,this.focusCurrentChild()}},{key:"focusNextChild",value:function(){this.focussedChild===this.menuItems.length-1?this.focusFirstChild():(this.focussedChild=this.focussedChild+1,this.focusCurrentChild())}},{key:"focusPreviousChild",value:function(){0===this.focussedChild?this.focusLastChild():(this.focussedChild=this.focussedChild-1,this.focusCurrentChild())}},{key:"focusCurrentChild",value:function(){-1!==this.focussedChild&&this.menuItems[this.focussedChild].focus()}},{key:"focusNextChildWithCharacter",value:function(e){for(var t=e.toLowerCase(),n=this.focussedChild+1,s=!1;!s&&n<this.menuItems.length;){this.menuItems[n].element.innerText.toLowerCase().startsWith(t)&&(s=!0,this.focussedChild=n,this.focusCurrentChild()),n++}}},{key:"closeChildren",value:function(){this.menuToggles.forEach((function(e){return e.close()}))}},{key:"element",get:function(){return this.domElements.menu}},{key:"menuItemElements",get:function(){return this.domElements.menuItems}},{key:"submenuItemElements",get:function(){return this.domElements.submenuItems}},{key:"menuItems",get:function(){return this.elements.menuItems}},{key:"menuToggles",get:function(){return this.elements.menuToggles}},{key:"selector",get:function(){return this.domSelectors}},{key:"currentFocus",get:function(){return this.focusState},set:function(e){if(!["self","child","none"].includes(e))throw new Error("Focus state must be 'self', 'child', or 'none'.");this.focusState=e}},{key:"openClass",get:function(){return this.submenuOpenClass},set:function(e){if("string"!=typeof e)throw new TypeError("Class must be a string.");this.submenuOpenClass=e}},{key:"isTopLevel",get:function(){return this.root},set:function(e){if("boolean"!=typeof e)throw new TypeError("Top-level flag must be true or false.");this.root=e}}]),e}();return i}(); | ||
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n<t.length;n++){var s=t[n];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(e,s.key,s)}}function _createClass(e,t,n){return t&&_defineProperties(e.prototype,t),n&&_defineProperties(e,n),e}var AccessibleMenu=function(){var e={menuItemElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuItemElement must be an HTML Element.")},parentMenu:function(e){if(!(e instanceof u))throw new TypeError("parentMenu must be a Menu.")}},t=function(){function t(n){var s=n.menuItemElement,o=n.parentMenu;_classCallCheck(this,t),e.menuItemElement(s),e.parentMenu(o),this.domElements={menuItem:s,link:s.querySelector("a")},this.elements={parent:o}}return _createClass(t,[{key:"initialize",value:function(){this.element.setAttribute("role","menuitem"),this.link.tabIndex=-1}},{key:"focus",value:function(){this.element.querySelector("a").focus()}},{key:"element",get:function(){return this.domElements.menuItem}},{key:"link",get:function(){return this.domElements.link}},{key:"parentMenu",get:function(){return this.elements.parent}}]),t}(),n={menuToggleElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuToggleElement must be an HTML Element.")},parentElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("parentElement must be an HTML Element.")},menu:function(e){if(!(e instanceof u))throw new TypeError("menu must be a Menu.")},openClass:function(e){if("string"!=typeof e)throw TypeError("openClass must be a string.");if(e.replace(/[_a-zA-Z0-9-]/g,"").length>0)throw Error("openClass must be a valid CSS class.")},parentMenu:function(e){if(null!==e&&!(e instanceof u))throw new TypeError("parentMenu must be a Menu.")},rootMenu:function(e){if(null!==e&&!(e instanceof u))throw new TypeError("rootMenu must be a Menu.")}},s=function(){function e(t){var s=t.menuToggleElement,o=t.parentElement,u=t.menu,i=t.openClass,r=void 0===i?"show":i,l=t.parentMenu,m=void 0===l?null:l,c=t.rootMenu,a=void 0===c?null:c;_classCallCheck(this,e),n.menuToggleElement(s),n.parentElement(o),n.menu(u),n.openClass(r),n.parentMenu(m),n.rootMenu(a),this.domElements={toggle:s,menuItem:o},this.elements={menu:u,parentMenu:m,rootMenu:a||m},this.openClass=r}return _createClass(e,[{key:"initialize",value:function(){var e=this;if(this.element.setAttribute("aria-haspopup","true"),this.element.setAttribute("aria-expanded","false"),this.element.setAttribute("role","button"),""===this.element.id||""===this.menu.element.id){var t=Math.random().toString(36).replace(/[^a-z]+/g,"").substr(0,10),n="".concat(this.element.innerText.toLowerCase().replace(/[^a-zA-Z0-9\s]/g,"").replace(/\s/g,"-"),"-").concat(t);this.element.id=this.element.id||"".concat(n,"-menu-button"),this.menu.element.id=this.menu.element.id||"".concat(n,"-menu")}this.menu.element.setAttribute("aria-labelledby",this.element.id),this.element.setAttribute("aria-controls",this.menu.element.id),this.element.addEventListener("click",(function(t){t.preventDefault(),t.stopPropagation(),e.toggle()})),this.handleKeydown()}},{key:"open",value:function(){this.isOpen||(this.isOpen=!0,this.element.setAttribute("aria-expanded","true"),this.menuItemElement.classList.add(this.openClass),this.menu.element.classList.add(this.openClass),this.closeSiblings(),this.parentMenu&&(this.parentMenu.currentFocus="child"),this.menu.currentFocus="self",this.menu.focusFirstChild())}},{key:"close",value:function(){this.isOpen&&(this.isOpen=!1,this.element.setAttribute("aria-expanded","false"),this.menuItemElement.classList.remove(this.openClass),this.menu.element.classList.remove(this.openClass),this.closeChildren(),this.menu.currentFocus="none",this.parentMenu&&(this.parentMenu.currentFocus="self"),this.parentMenu&&this.parentMenu.focusCurrentChild())}},{key:"toggle",value:function(){this.isOpen?this.close():this.open()}},{key:"closeSiblings",value:function(){var e=this;try{this.parentMenu.menuToggles.forEach((function(t){t!==e&&t.close()}))}catch(e){}}},{key:"closeChildren",value:function(){this.menu.menuToggles.forEach((function(e){return e.close()}))}},{key:"handleKeydown",value:function(){var e=this;function t(e){e.preventDefault(),e.stopPropagation()}this.menu.element.addEventListener("keydown",(function(n){var s=n.key;"Escape"===s?(t(n),e.close()):e.parentMenu&&e.parentMenu.isTopLevel&&("ArrowRight"===s?(t(n),e.close(),e.parentMenu.focusNextChild()):"ArrowLeft"===s&&(t(n),e.close(),e.parentMenu.focusPreviousChild()))})),this.menuItemElement.addEventListener("keydown",(function(n){var s=n.key;"none"===e.menu.currentFocus&&e.parentMenu.isTopLevel&&("ArrowUp"===s?(t(n),e.open(),e.menu.focusLastChild()):"ArrowDown"===s&&(t(n),e.open()))}))}},{key:"element",get:function(){return this.domElements.toggle}},{key:"menuItemElement",get:function(){return this.domElements.menuItem}},{key:"menu",get:function(){return this.elements.menu}},{key:"parentMenu",get:function(){return this.elements.parentMenu}},{key:"rootMenu",get:function(){return this.elements.rootMenu}},{key:"isOpen",get:function(){return this.show},set:function(e){if("boolean"!=typeof e)throw new TypeError("Open state must be true or false.");this.show=e}}]),e}(),o={menuElement:function(e){if(!(e instanceof HTMLElement))throw new TypeError("menuElement must be an HTML Element.")},menuItemSelector:function(e){if("string"!=typeof e)throw new TypeError("menuItemSelector must be a CSS selector string.")},submenuItemSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuItemSelector must be a CSS selector string.")},submenuToggleSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuToggleSelector must be a CSS selector string.")},submenuSelector:function(e){if("string"!=typeof e)throw new TypeError("submenuSelector must be a CSS selector string.")},submenuOpenClass:function(e){if("string"!=typeof e)throw TypeError("submenuOpenClass must be a string.");if(e.replace(/[_a-zA-Z0-9-]/g,"").length>0)throw Error("submenuOpenClass must be a valid CSS class.")},isTopLevel:function(e){if("boolean"!=typeof e)throw new TypeError("isTopLevel must be true or false")},isDropdown:function(e,t){if(null!==e||null!==t){if(!(e instanceof HTMLElement))throw new TypeError("controllerElement must be an HTML Element if containerElement is provided.");if(!(t instanceof HTMLElement))throw new TypeError("containerElement must be an HTML Element if controllerElement is provided.")}}},u=function(){function e(t){var n=t.menuElement,s=t.menuItemSelector,u=t.submenuItemSelector,i=t.submenuToggleSelector,r=t.submenuSelector,l=t.submenuOpenClass,m=void 0===l?"show":l,c=t.isTopLevel,a=void 0===c||c,h=t.controllerElement,f=void 0===h?null:h,p=t.containerElement,d=void 0===p?null:p;_classCallCheck(this,e),o.menuElement(n),o.menuItemSelector(s),o.submenuItemSelector(u),o.submenuToggleSelector(i),o.submenuSelector(r),o.submenuOpenClass(m),o.isTopLevel(a),o.isDropdown(f,d),this.domElements={menu:n,controller:f,container:d,menuItems:Array.from(n.querySelectorAll(s)).filter((function(e){return e.parentElement===n})),submenuItems:Array.from(n.querySelectorAll(u)).filter((function(e){return e.parentElement===n}))},this.domSelectors={"menu-items":s,"submenu-items":u,"submenu-toggle":i,submenu:r},this.elements={menuItems:[],menuToggles:[]},this.focussedChild=-1,this.focusState="none",this.openClass=m,this.root=a}return _createClass(e,[{key:"initialize",value:function(){(this.element.setAttribute("role","menu"),this.element.tabIndex=0,this.createMenuItems(),this.handleKeydown(),this.handleClick(),this.controllerElement&&this.containerElement)&&new s({menuToggleElement:this.controllerElement,parentElement:this.containerElement,menu:this,openClass:this.openClass}).initialize()}},{key:"createMenuItems",value:function(){var n=this;this.menuItemElements.forEach((function(o){var u=new t({menuItemElement:o,parentMenu:n});if(n.elements.menuItems.push(u),u.initialize(),n.submenuItemElements.includes(o)){var i=o.querySelector(n.selector["submenu-toggle"]),r=new e({menuElement:o.querySelector(n.selector.submenu),menuItemSelector:n.selector["menu-items"],submenuItemSelector:n.selector["submenu-items"],submenuToggleSelector:n.selector["submenu-toggle"],submenuSelector:n.selector.submenu,submenuOpenClass:n.openClass,isTopLevel:!1});r.initialize();var l=new s({menuToggleElement:i,parentElement:o,menu:r,openClass:n.openClass,parentMenu:n});l.initialize(),n.elements.menuToggles.push(l)}}))}},{key:"handleKeydown",value:function(){var e=this;function t(e){e.preventDefault(),e.stopPropagation()}this.element.addEventListener("keydown",(function(n){var s=n.key,o=n.code,u=n.altKey,i=n.crtlKey,r=n.metaKey,l=u||i||r;"none"===e.currentFocus?("Enter"===s||" "===s&&"Space"===o)&&(t(n),e.currentFocus="self",e.focusFirstChild()):"self"===e.currentFocus&&("Escape"===s?(t(n),e.focus(),e.currentFocus="none"):e.isTopLevel||"ArrowUp"!==s?e.isTopLevel&&"ArrowRight"===s?(t(n),e.focusNextChild()):e.isTopLevel||"ArrowDown"!==s?e.isTopLevel&&"ArrowLeft"===s?(t(n),e.focusPreviousChild()):"Home"===s?(t(n),e.focusFirstChild()):"End"===s?(t(n),e.focusLastChild()):s.match(/^[a-zA-Z]{1}$/)&&!l&&(t(n),e.focusNextChildWithCharacter(s)):(t(n),e.focusNextChild()):(t(n),e.focusPreviousChild())),"none"!==e.currentFocus&&"Tab"===s&&(e.blur(),e.closeChildren())}))}},{key:"handleClick",value:function(){var e=this;document.addEventListener("click",(function(t){e.element.contains(t.target)||e.element===t.target||(e.blur(),e.closeChildren())}))}},{key:"focus",value:function(){this.focussedChild=0,this.currentFocus="self",this.element.focus()}},{key:"blur",value:function(){this.focussedChild=-1,this.currentFocus="none",this.element.blur()}},{key:"focusFirstChild",value:function(){this.focussedChild=0,this.focusCurrentChild()}},{key:"focusLastChild",value:function(){this.focussedChild=this.menuItems.length-1,this.focusCurrentChild()}},{key:"focusNextChild",value:function(){this.focussedChild===this.menuItems.length-1?this.focusFirstChild():(this.focussedChild=this.focussedChild+1,this.focusCurrentChild())}},{key:"focusPreviousChild",value:function(){0===this.focussedChild?this.focusLastChild():(this.focussedChild=this.focussedChild-1,this.focusCurrentChild())}},{key:"focusCurrentChild",value:function(){-1!==this.focussedChild&&this.menuItems[this.focussedChild].focus()}},{key:"focusNextChildWithCharacter",value:function(e){for(var t=e.toLowerCase(),n=this.focussedChild+1,s=!1;!s&&n<this.menuItems.length;){this.menuItems[n].element.innerText.toLowerCase().startsWith(t)&&(s=!0,this.focussedChild=n,this.focusCurrentChild()),n++}}},{key:"closeChildren",value:function(){this.menuToggles.forEach((function(e){return e.close()}))}},{key:"element",get:function(){return this.domElements.menu}},{key:"controllerElement",get:function(){return this.domElements.controller}},{key:"containerElement",get:function(){return this.domElements.container}},{key:"menuItemElements",get:function(){return this.domElements.menuItems}},{key:"submenuItemElements",get:function(){return this.domElements.submenuItems}},{key:"menuItems",get:function(){return this.elements.menuItems}},{key:"menuToggles",get:function(){return this.elements.menuToggles}},{key:"selector",get:function(){return this.domSelectors}},{key:"currentFocus",get:function(){return this.focusState},set:function(e){if(!["self","child","none"].includes(e))throw new Error("Focus state must be 'self', 'child', or 'none'.");this.focusState=e}},{key:"openClass",get:function(){return this.submenuOpenClass},set:function(e){if("string"!=typeof e)throw new TypeError("Class must be a string.");this.submenuOpenClass=e}},{key:"isTopLevel",get:function(){return this.root},set:function(e){if("boolean"!=typeof e)throw new TypeError("Top-level flag must be true or false.");this.root=e}}]),e}();return u}(); |
{ | ||
"name": "accessible-menu", | ||
"version": "1.0.0-beta.3", | ||
"version": "1.0.0-beta.4", | ||
"description": "A JavaScript library to help you generate WAI-ARIA accessible menus in the DOM.", | ||
@@ -5,0 +5,0 @@ "main": "src/menu.js", |
@@ -67,10 +67,10 @@ # accessible-menu | ||
```jsx | ||
const menu = new AccessibleMenu( | ||
menuDOMObject, | ||
"menu-item-css-selector", | ||
"menu-item-with-dropdown-css-selector", | ||
"dropdown-toggle-css-selector", | ||
"dropdown-menu-css-selector", | ||
"class-to-open-menus" | ||
); | ||
const menu = new AccessibleMenu({ | ||
menuElement: menuDOMObject, | ||
menuItemSelector: "menu-item-css-selector", | ||
submenuItemSelector: "menu-item-with-dropdown-css-selector", | ||
submenuToggleSelector: "dropdown-toggle-css-selector", | ||
submenuSelector: "dropdown-menu-css-selector", | ||
submenuOpenClass: "class-to-open-menus" | ||
}); | ||
@@ -77,0 +77,0 @@ menu.initialize(); |
111
src/menu.js
@@ -54,2 +54,19 @@ import MenuItem from "./menuItem"; | ||
} | ||
}, | ||
isDropdown: (controller, container) => { | ||
// Values are allowed to be null if both are null. | ||
if (controller === null && container === null) return; | ||
// Ensure value is an HTML element. | ||
if (!(controller instanceof HTMLElement)) { | ||
throw new TypeError( | ||
"controllerElement must be an HTML Element if containerElement is provided." | ||
); | ||
} | ||
if (!(container instanceof HTMLElement)) { | ||
throw new TypeError( | ||
"containerElement must be an HTML Element if controllerElement is provided." | ||
); | ||
} | ||
} | ||
@@ -62,11 +79,14 @@ }; | ||
* | ||
* @param {object} menuElement - The menu element in the DOM. | ||
* @param {string} menuItemSelector - The selector string for menu items. | ||
* @param {string} submenuItemSelector - The selector string for submenu items. | ||
* @param {string} submenuToggleSelector - The selector string for submenu toggle triggers. | ||
* @param {string} submenuSelector - The selector string for the submenu itself. | ||
* @param {string} submenuOpenClass - The class to use when a submenu is open. | ||
* @param {boolean} isTopLevel - Flags the menu as a top-level menu. | ||
* @param {object} param0 - The menu object. | ||
* @param {HTMLElement} param0.menuElement - The menu element in the DOM. | ||
* @param {string} param0.menuItemSelector - The selector string for menu items. | ||
* @param {string} param0.submenuItemSelector - The selector string for submenu items. | ||
* @param {string} param0.submenuToggleSelector - The selector string for submenu toggle triggers. | ||
* @param {string} param0.submenuSelector - The selector string for the submenu itself. | ||
* @param {string} param0.submenuOpenClass - The class to use when a submenu is open. | ||
* @param {boolean} param0.isTopLevel - Flags the menu as a top-level menu. | ||
* @param {HTMLElement} param0.controllerElement - The element controlling the menu in the DOM. | ||
* @param {HTMLElement} param0.containerElement - The element containing the menu in the DOM. | ||
*/ | ||
constructor( | ||
constructor({ | ||
menuElement, | ||
@@ -78,4 +98,6 @@ menuItemSelector, | ||
submenuOpenClass = "show", | ||
isTopLevel = true | ||
) { | ||
isTopLevel = true, | ||
controllerElement = null, | ||
containerElement = null | ||
}) { | ||
// Run validations. | ||
@@ -89,5 +111,8 @@ validate.menuElement(menuElement); | ||
validate.isTopLevel(isTopLevel); | ||
validate.isDropdown(controllerElement, containerElement); | ||
this.domElements = { | ||
menu: menuElement, | ||
controller: controllerElement, | ||
container: containerElement, | ||
menuItems: Array.from( | ||
@@ -130,2 +155,13 @@ menuElement.querySelectorAll(menuItemSelector) | ||
this.handleClick(); | ||
if (this.controllerElement && this.containerElement) { | ||
// Create a new MenuToggle to control the menu. | ||
const toggle = new MenuToggle({ | ||
menuToggleElement: this.controllerElement, | ||
parentElement: this.containerElement, | ||
menu: this, | ||
openClass: this.openClass | ||
}); | ||
toggle.initialize(); | ||
} | ||
} | ||
@@ -143,2 +179,20 @@ | ||
/** | ||
* The menu's controller element in the DOM. | ||
* | ||
* @returns {HTMLElement} - The controller element. | ||
*/ | ||
get controllerElement() { | ||
return this.domElements.controller; | ||
} | ||
/** | ||
* The menu's container element in the DOM. | ||
* | ||
* @returns {HTMLElement} - The container element. | ||
*/ | ||
get containerElement() { | ||
return this.domElements.container; | ||
} | ||
/** | ||
* The menu item DOM elements contained in the menu. | ||
@@ -257,3 +311,6 @@ * | ||
// Create a new MenuItem. | ||
const menuItem = new MenuItem(element, this); | ||
const menuItem = new MenuItem({ | ||
menuItemElement: element, | ||
parentMenu: this | ||
}); | ||
@@ -275,21 +332,21 @@ // Add the item to the list of menu items. | ||
// Create the new Menu and initialize it. | ||
const menu = new Menu( | ||
submenu, | ||
this.selector["menu-items"], | ||
this.selector["submenu-items"], | ||
this.selector["submenu-toggle"], | ||
this.selector.submenu, | ||
this.openClass, | ||
false | ||
); | ||
const menu = new Menu({ | ||
menuElement: submenu, | ||
menuItemSelector: this.selector["menu-items"], | ||
submenuItemSelector: this.selector["submenu-items"], | ||
submenuToggleSelector: this.selector["submenu-toggle"], | ||
submenuSelector: this.selector.submenu, | ||
submenuOpenClass: this.openClass, | ||
isTopLevel: false | ||
}); | ||
menu.initialize(); | ||
// Create the new MenuToggle. | ||
const toggle = new MenuToggle( | ||
toggler, | ||
menu, | ||
this.openClass, | ||
this, | ||
menuItem | ||
); | ||
const toggle = new MenuToggle({ | ||
menuToggleElement: toggler, | ||
parentElement: element, | ||
menu: menu, | ||
openClass: this.openClass, | ||
parentMenu: this | ||
}); | ||
toggle.initialize(); | ||
@@ -296,0 +353,0 @@ |
@@ -22,6 +22,7 @@ import Menu from "./menu"; | ||
* | ||
* @param {object} menuItemElement - The menu item in the DOM. | ||
* @param {Menu} parentMenu - The parent menu. | ||
* @param {object} param0 - The menu item object. | ||
* @param {object} param0.menuItemElement - The menu item in the DOM. | ||
* @param {Menu} param0.parentMenu - The parent menu. | ||
*/ | ||
constructor(menuItemElement, parentMenu) { | ||
constructor({ menuItemElement, parentMenu }) { | ||
// Run validations. | ||
@@ -28,0 +29,0 @@ validate.menuItemElement(menuItemElement); |
@@ -11,2 +11,8 @@ import Menu from "./menu"; | ||
}, | ||
parentElement: value => { | ||
// Ensure value is an HTML element. | ||
if (!(value instanceof HTMLElement)) { | ||
throw new TypeError("parentElement must be an HTML Element."); | ||
} | ||
}, | ||
menu: value => { | ||
@@ -39,10 +45,2 @@ // Ensure value is an Menu element. | ||
}, | ||
parentMenuItem: value => { | ||
// Value is allowed to be null. | ||
if (value === null) return; | ||
if (!(value instanceof MenuItem)) { | ||
throw new TypeError("parentMenuItem must be a MenuItem."); | ||
} | ||
}, | ||
rootMenu: value => { | ||
@@ -63,30 +61,31 @@ // Value is allowed to be null. | ||
* | ||
* @param {object} menuToggleElement - The toggle element in the DOM. | ||
* @param {Menu} menu - The menu controlled by the this toggle. | ||
* @param {string} openClass - The class to use when a submenu is open. | ||
* @param {Menu} parentMenu - The menu containing the toggle. | ||
* @param {MenuItem} parentMenuItem - The menu item containing the toggle. | ||
* @param {Menu} rootMenu - The root menu containing the toggle. | ||
* @param {object} param0 - The menu toggle object. | ||
* @param {HTMLElement} param0.menuToggleElement - The toggle element in the DOM. | ||
* @param {HTMLElement} param0.parentElement - The element containing the menu. | ||
* @param {Menu} param0.menu - The menu controlled by the this toggle. | ||
* @param {string} param0.openClass - The class to use when a submenu is open. | ||
* @param {Menu} param0.parentMenu - The menu containing the toggle. | ||
* @param {Menu} param0.rootMenu - The root menu containing the toggle. | ||
*/ | ||
constructor( | ||
constructor({ | ||
menuToggleElement, | ||
parentElement, | ||
menu, | ||
openClass = "show", | ||
parentMenu = null, | ||
parentMenuItem = null, | ||
rootMenu = null | ||
) { | ||
}) { | ||
// Run validations. | ||
validate.menuToggleElement(menuToggleElement); | ||
validate.parentElement(parentElement); | ||
validate.menu(menu); | ||
validate.openClass(openClass); | ||
validate.parentMenu(parentMenu); | ||
validate.parentMenuItem(parentMenuItem); | ||
validate.rootMenu(rootMenu); | ||
this.domElements = { | ||
toggle: menuToggleElement | ||
toggle: menuToggleElement, | ||
menuItem: parentElement | ||
}; | ||
this.elements = { | ||
menuItem: parentMenuItem, | ||
menu: menu, | ||
@@ -155,4 +154,4 @@ parentMenu: parentMenu, | ||
*/ | ||
get menuItem() { | ||
return this.elements.menuItem; | ||
get menuItemElement() { | ||
return this.domElements.menuItem; | ||
} | ||
@@ -219,3 +218,3 @@ | ||
this.element.setAttribute("aria-expanded", "true"); | ||
this.menuItem.element.classList.add(this.openClass); | ||
this.menuItemElement.classList.add(this.openClass); | ||
this.menu.element.classList.add(this.openClass); | ||
@@ -227,3 +226,3 @@ | ||
// Set proper focus states to parent & child. | ||
this.parentMenu.currentFocus = "child"; | ||
if (this.parentMenu) this.parentMenu.currentFocus = "child"; | ||
this.menu.currentFocus = "self"; | ||
@@ -246,3 +245,3 @@ | ||
this.element.setAttribute("aria-expanded", "false"); | ||
this.menuItem.element.classList.remove(this.openClass); | ||
this.menuItemElement.classList.remove(this.openClass); | ||
this.menu.element.classList.remove(this.openClass); | ||
@@ -255,6 +254,6 @@ | ||
this.menu.currentFocus = "none"; | ||
this.parentMenu.currentFocus = "self"; | ||
if (this.parentMenu) this.parentMenu.currentFocus = "self"; | ||
// Set the new focus. | ||
this.parentMenu.focusCurrentChild(); | ||
if (this.parentMenu) this.parentMenu.focusCurrentChild(); | ||
} | ||
@@ -314,15 +313,17 @@ } | ||
this.close(); | ||
} else if (this.parentMenu.isTopLevel && key === "ArrowRight") { | ||
// The Right Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
this.close(); | ||
this.parentMenu.focusNextChild(); | ||
} else if (this.parentMenu.isTopLevel && key === "ArrowLeft") { | ||
// The Left Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
this.close(); | ||
this.parentMenu.focusPreviousChild(); | ||
} else if (this.parentMenu && this.parentMenu.isTopLevel) { | ||
if (key === "ArrowRight") { | ||
// The Right Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
this.close(); | ||
this.parentMenu.focusNextChild(); | ||
} else if (key === "ArrowLeft") { | ||
// The Left Arrow key should focus the next menu item in the parent menu. | ||
preventDefault(event); | ||
this.close(); | ||
this.parentMenu.focusPreviousChild(); | ||
} | ||
} | ||
}); | ||
this.menuItem.element.addEventListener("keydown", event => { | ||
this.menuItemElement.addEventListener("keydown", event => { | ||
const { key } = event; | ||
@@ -329,0 +330,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
93882
1858