menuFoundation.ts 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  2. import { handlePrevent, isPrintableCharacter, findIndexByCharacter, getAncestorNodeByRole, getMenuButton, setFocusToFirstItem, setFocusToItem, setFocusToNextMenuitem, setFocusToPreviousMenuItem } from '../utils/a11y';
  3. export default class DropdownMenuFoundation extends BaseFoundation<Partial<DefaultAdapter>> {
  4. menuItemNodes: HTMLElement[] = null;
  5. firstChars: string[] = [];
  6. // if trigger is click, auto focus to the first menu item
  7. autoFocus(ulElement: any): void {
  8. const trigger = this._adapter.getContext('trigger');
  9. if (trigger === 'click'){
  10. // find all non-disabled li under this menu and set focus to the first menu
  11. this.menuItemNodes = [...ulElement.getElementsByTagName('li')].filter(item => item.ariaDisabled !== "true");
  12. setFocusToFirstItem(this.menuItemNodes);
  13. }
  14. }
  15. handleEscape(menu: Element): void {
  16. const trigger = this._adapter.getContext('trigger');
  17. if (trigger === 'custom'){
  18. const menuButton = menu && getMenuButton(document.querySelectorAll(`[data-popupId]`), menu.id);
  19. menuButton.focus();
  20. }
  21. }
  22. setFocusByFirstCharacter(curItem: any, char: string): void {
  23. const index = findIndexByCharacter(this.menuItemNodes, curItem, this.firstChars, char);
  24. if (index >= 0) {
  25. setFocusToItem(this.menuItemNodes, this.menuItemNodes[index]);
  26. }
  27. }
  28. onMenuKeydown(event: any): void {
  29. const menu = getAncestorNodeByRole(event.target, 'tooltip');
  30. if (!this.menuItemNodes){
  31. this.menuItemNodes = [...(event.target.parentNode).getElementsByTagName('li')].filter(item => item.ariaDisabled !== "true");
  32. }
  33. if (this.firstChars.length === 0){
  34. this.menuItemNodes.forEach((item: Element) => {
  35. this.firstChars.push(item.textContent.trim()[0].toLowerCase());
  36. });
  37. }
  38. // get the currently focused menu item
  39. const curItem = this.menuItemNodes.find(item => item.tabIndex === 0);
  40. switch (event.key) {
  41. case ' ':
  42. case 'Enter':
  43. event.target.click();
  44. handlePrevent(event);
  45. break;
  46. case 'Escape':
  47. this.handleEscape(menu);
  48. break;
  49. case 'ArrowUp':
  50. setFocusToPreviousMenuItem(this.menuItemNodes, curItem);
  51. handlePrevent(event);
  52. break;
  53. case 'ArrowDown':
  54. setFocusToNextMenuitem(this.menuItemNodes, curItem);
  55. handlePrevent(event);
  56. break;
  57. default:
  58. if (isPrintableCharacter(event.key)) {
  59. this.setFocusByFirstCharacter(curItem, event.key);
  60. handlePrevent(event);
  61. }
  62. break;
  63. }
  64. }
  65. }