aria-ease 6.5.0 → 6.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1734,38 +1734,11 @@ function moveFocus(elementItems, currentIndex, direction) {
1734
1734
  function isClickableButNotSemantic(el) {
1735
1735
  return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
1736
1736
  }
1737
- function handleMenuClose(menuElement, menuTriggerButton) {
1738
- menuElement.style.display = "none";
1739
- const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
1740
- if (!menuTriggerButtonId) {
1741
- console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
1742
- return;
1743
- }
1744
- menuTriggerButton.setAttribute("aria-expanded", "false");
1745
- }
1746
- function hasSubmenu(menuItem) {
1747
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
1748
- }
1749
- function getSubmenuId(menuItem) {
1750
- return menuItem.getAttribute("aria-controls");
1751
- }
1752
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
1737
+ function handleKeyPress(event, elementItems, elementItemIndex) {
1753
1738
  const currentEl = elementItems.item(elementItemIndex);
1754
1739
  switch (event.key) {
1755
1740
  case "ArrowUp":
1756
1741
  case "ArrowLeft": {
1757
- if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
1758
- const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
1759
- if (labelledBy) {
1760
- const parentTrigger = document.getElementById(labelledBy);
1761
- if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
1762
- event.preventDefault();
1763
- closeSubmenu();
1764
- parentTrigger.focus();
1765
- return;
1766
- }
1767
- }
1768
- }
1769
1742
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
1770
1743
  event.preventDefault();
1771
1744
  moveFocus(elementItems, elementItemIndex, -1);
@@ -1780,14 +1753,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
1780
1753
  }
1781
1754
  case "ArrowDown":
1782
1755
  case "ArrowRight": {
1783
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
1784
- event.preventDefault();
1785
- const submenuId = getSubmenuId(currentEl);
1786
- if (submenuId) {
1787
- openSubmenu(submenuId);
1788
- return;
1789
- }
1790
- }
1791
1756
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
1792
1757
  event.preventDefault();
1793
1758
  moveFocus(elementItems, elementItemIndex, 1);
@@ -1803,15 +1768,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
1803
1768
  }
1804
1769
  case "Escape": {
1805
1770
  event.preventDefault();
1806
- if (menuElementDiv && triggerButton) {
1807
- if (getComputedStyle(menuElementDiv).display === "block") {
1808
- handleMenuClose(menuElementDiv, triggerButton);
1809
- if (onOpenChange) {
1810
- onOpenChange(false);
1811
- }
1812
- }
1813
- triggerButton.focus();
1814
- }
1815
1771
  break;
1816
1772
  }
1817
1773
  case "Enter":
@@ -1826,12 +1782,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
1826
1782
  break;
1827
1783
  }
1828
1784
  case "Tab": {
1829
- if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
1830
- handleMenuClose(menuElementDiv, triggerButton);
1831
- if (onOpenChange) {
1832
- onOpenChange(false);
1833
- }
1834
- }
1835
1785
  break;
1836
1786
  }
1837
1787
  default:
@@ -2069,15 +2019,88 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
2069
2019
  const items = getItems();
2070
2020
  items.forEach((item) => {
2071
2021
  item.setAttribute("role", "menuitem");
2022
+ if (item.hasAttribute("data-submenu-id")) {
2023
+ item.setAttribute("aria-haspopup", "menu");
2024
+ item.setAttribute("aria-controls", item.getAttribute("data-submenu-id"));
2025
+ }
2072
2026
  });
2073
2027
  }
2028
+ function moveFocus2(elementItems, currentIndex, direction) {
2029
+ const len = elementItems.length;
2030
+ const nextIndex = (currentIndex + direction + len) % len;
2031
+ elementItems.item(nextIndex).focus();
2032
+ }
2033
+ function hasSubmenu(menuItem) {
2034
+ return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
2035
+ }
2074
2036
  intializeMenuItems();
2037
+ function handleItemsKeydown(event, menuItem, menuItemIndex) {
2038
+ switch (event.key) {
2039
+ case "ArrowUp":
2040
+ case "ArrowLeft": {
2041
+ if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
2042
+ event.preventDefault();
2043
+ closeMenu();
2044
+ return;
2045
+ }
2046
+ event.preventDefault();
2047
+ moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
2048
+ break;
2049
+ }
2050
+ case "ArrowDown":
2051
+ case "ArrowRight": {
2052
+ if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
2053
+ event.preventDefault();
2054
+ const submenuId = menuItem.getAttribute("aria-controls");
2055
+ if (submenuId) {
2056
+ openSubmenu(submenuId);
2057
+ return;
2058
+ }
2059
+ }
2060
+ event.preventDefault();
2061
+ moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
2062
+ break;
2063
+ }
2064
+ case "Escape": {
2065
+ event.preventDefault();
2066
+ closeMenu();
2067
+ triggerButton.focus();
2068
+ if (onOpenChange) {
2069
+ onOpenChange(false);
2070
+ }
2071
+ break;
2072
+ }
2073
+ case "Enter":
2074
+ case " ": {
2075
+ event.preventDefault();
2076
+ menuItem.click();
2077
+ break;
2078
+ }
2079
+ case "Tab": {
2080
+ if (!event.shiftKey || event.shiftKey) {
2081
+ closeMenu();
2082
+ if (onOpenChange) {
2083
+ onOpenChange(false);
2084
+ }
2085
+ }
2086
+ break;
2087
+ }
2088
+ default:
2089
+ break;
2090
+ }
2091
+ }
2075
2092
  function isItemInNestedSubmenu(item) {
2076
2093
  let parent = item.parentElement;
2077
2094
  while (parent && parent !== menuDiv) {
2078
2095
  if (parent.getAttribute("role") === "menu") {
2079
2096
  return true;
2080
2097
  }
2098
+ if (parent.id) {
2099
+ const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
2100
+ if (parentMenuTrigger) {
2101
+ return true;
2102
+ }
2103
+ }
2081
2104
  parent = parent.parentElement;
2082
2105
  }
2083
2106
  return false;
@@ -2113,9 +2136,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
2113
2136
  }
2114
2137
  submenuInstance.openMenu();
2115
2138
  }
2116
- function closeSubmenu() {
2117
- closeMenu();
2118
- }
2119
2139
  function onOpenChange(isOpen) {
2120
2140
  if (callback?.onOpenChange) {
2121
2141
  try {
@@ -2127,19 +2147,9 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
2127
2147
  }
2128
2148
  function addListeners() {
2129
2149
  const items = getFilteredItems();
2130
- const nodeListLike = toNodeListLike(items);
2131
2150
  items.forEach((menuItem, index) => {
2132
2151
  if (!handlerMap.has(menuItem)) {
2133
- const handler = (event) => handleKeyPress(
2134
- event,
2135
- nodeListLike,
2136
- index,
2137
- menuDiv,
2138
- triggerButton,
2139
- openSubmenu,
2140
- closeSubmenu,
2141
- onOpenChange
2142
- );
2152
+ const handler = (event) => handleItemsKeydown(event, menuItem, index);
2143
2153
  menuItem.addEventListener("keydown", handler);
2144
2154
  handlerMap.set(menuItem, handler);
2145
2155
  }
package/dist/index.js CHANGED
@@ -208,38 +208,11 @@ function moveFocus(elementItems, currentIndex, direction) {
208
208
  function isClickableButNotSemantic(el) {
209
209
  return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
210
210
  }
211
- function handleMenuClose(menuElement, menuTriggerButton) {
212
- menuElement.style.display = "none";
213
- const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
214
- if (!menuTriggerButtonId) {
215
- console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
216
- return;
217
- }
218
- menuTriggerButton.setAttribute("aria-expanded", "false");
219
- }
220
- function hasSubmenu(menuItem) {
221
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
222
- }
223
- function getSubmenuId(menuItem) {
224
- return menuItem.getAttribute("aria-controls");
225
- }
226
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
211
+ function handleKeyPress(event, elementItems, elementItemIndex) {
227
212
  const currentEl = elementItems.item(elementItemIndex);
228
213
  switch (event.key) {
229
214
  case "ArrowUp":
230
215
  case "ArrowLeft": {
231
- if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
232
- const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
233
- if (labelledBy) {
234
- const parentTrigger = document.getElementById(labelledBy);
235
- if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
236
- event.preventDefault();
237
- closeSubmenu();
238
- parentTrigger.focus();
239
- return;
240
- }
241
- }
242
- }
243
216
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
244
217
  event.preventDefault();
245
218
  moveFocus(elementItems, elementItemIndex, -1);
@@ -254,14 +227,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
254
227
  }
255
228
  case "ArrowDown":
256
229
  case "ArrowRight": {
257
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
258
- event.preventDefault();
259
- const submenuId = getSubmenuId(currentEl);
260
- if (submenuId) {
261
- openSubmenu(submenuId);
262
- return;
263
- }
264
- }
265
230
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
266
231
  event.preventDefault();
267
232
  moveFocus(elementItems, elementItemIndex, 1);
@@ -277,15 +242,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
277
242
  }
278
243
  case "Escape": {
279
244
  event.preventDefault();
280
- if (menuElementDiv && triggerButton) {
281
- if (getComputedStyle(menuElementDiv).display === "block") {
282
- handleMenuClose(menuElementDiv, triggerButton);
283
- if (onOpenChange) {
284
- onOpenChange(false);
285
- }
286
- }
287
- triggerButton.focus();
288
- }
289
245
  break;
290
246
  }
291
247
  case "Enter":
@@ -300,12 +256,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
300
256
  break;
301
257
  }
302
258
  case "Tab": {
303
- if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
304
- handleMenuClose(menuElementDiv, triggerButton);
305
- if (onOpenChange) {
306
- onOpenChange(false);
307
- }
308
- }
309
259
  break;
310
260
  }
311
261
  default:
@@ -543,15 +493,88 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
543
493
  const items = getItems();
544
494
  items.forEach((item) => {
545
495
  item.setAttribute("role", "menuitem");
496
+ if (item.hasAttribute("data-submenu-id")) {
497
+ item.setAttribute("aria-haspopup", "menu");
498
+ item.setAttribute("aria-controls", item.getAttribute("data-submenu-id"));
499
+ }
546
500
  });
547
501
  }
502
+ function moveFocus2(elementItems, currentIndex, direction) {
503
+ const len = elementItems.length;
504
+ const nextIndex = (currentIndex + direction + len) % len;
505
+ elementItems.item(nextIndex).focus();
506
+ }
507
+ function hasSubmenu(menuItem) {
508
+ return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
509
+ }
548
510
  intializeMenuItems();
511
+ function handleItemsKeydown(event, menuItem, menuItemIndex) {
512
+ switch (event.key) {
513
+ case "ArrowUp":
514
+ case "ArrowLeft": {
515
+ if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
516
+ event.preventDefault();
517
+ closeMenu();
518
+ return;
519
+ }
520
+ event.preventDefault();
521
+ moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
522
+ break;
523
+ }
524
+ case "ArrowDown":
525
+ case "ArrowRight": {
526
+ if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
527
+ event.preventDefault();
528
+ const submenuId = menuItem.getAttribute("aria-controls");
529
+ if (submenuId) {
530
+ openSubmenu(submenuId);
531
+ return;
532
+ }
533
+ }
534
+ event.preventDefault();
535
+ moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
536
+ break;
537
+ }
538
+ case "Escape": {
539
+ event.preventDefault();
540
+ closeMenu();
541
+ triggerButton.focus();
542
+ if (onOpenChange) {
543
+ onOpenChange(false);
544
+ }
545
+ break;
546
+ }
547
+ case "Enter":
548
+ case " ": {
549
+ event.preventDefault();
550
+ menuItem.click();
551
+ break;
552
+ }
553
+ case "Tab": {
554
+ if (!event.shiftKey || event.shiftKey) {
555
+ closeMenu();
556
+ if (onOpenChange) {
557
+ onOpenChange(false);
558
+ }
559
+ }
560
+ break;
561
+ }
562
+ default:
563
+ break;
564
+ }
565
+ }
549
566
  function isItemInNestedSubmenu(item) {
550
567
  let parent = item.parentElement;
551
568
  while (parent && parent !== menuDiv) {
552
569
  if (parent.getAttribute("role") === "menu") {
553
570
  return true;
554
571
  }
572
+ if (parent.id) {
573
+ const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
574
+ if (parentMenuTrigger) {
575
+ return true;
576
+ }
577
+ }
555
578
  parent = parent.parentElement;
556
579
  }
557
580
  return false;
@@ -587,9 +610,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
587
610
  }
588
611
  submenuInstance.openMenu();
589
612
  }
590
- function closeSubmenu() {
591
- closeMenu();
592
- }
593
613
  function onOpenChange(isOpen) {
594
614
  if (callback?.onOpenChange) {
595
615
  try {
@@ -601,19 +621,9 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
601
621
  }
602
622
  function addListeners() {
603
623
  const items = getFilteredItems();
604
- const nodeListLike = toNodeListLike(items);
605
624
  items.forEach((menuItem, index) => {
606
625
  if (!handlerMap.has(menuItem)) {
607
- const handler = (event) => handleKeyPress(
608
- event,
609
- nodeListLike,
610
- index,
611
- menuDiv,
612
- triggerButton,
613
- openSubmenu,
614
- closeSubmenu,
615
- onOpenChange
616
- );
626
+ const handler = (event) => handleItemsKeydown(event, menuItem, index);
617
627
  menuItem.addEventListener("keydown", handler);
618
628
  handlerMap.set(menuItem, handler);
619
629
  }
@@ -88,4 +88,4 @@ interface MenuCallback {
88
88
  onOpenChange?: (isOpen: boolean) => void;
89
89
  }
90
90
 
91
- export type { AccessibilityInstance as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccordionConfig as a };
91
+ export type { AccordionConfig as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccessibilityInstance as a };
@@ -88,4 +88,4 @@ interface MenuCallback {
88
88
  onOpenChange?: (isOpen: boolean) => void;
89
89
  }
90
90
 
91
- export type { AccessibilityInstance as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccordionConfig as a };
91
+ export type { AccordionConfig as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccessibilityInstance as a };
@@ -1,4 +1,4 @@
1
- import { a as AccordionConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { A as AccordionConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes an accordion accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -1,4 +1,4 @@
1
- import { a as AccordionConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { A as AccordionConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes an accordion accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -23,15 +23,11 @@ function moveFocus(elementItems, currentIndex, direction) {
23
23
  function isClickableButNotSemantic(el) {
24
24
  return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
25
25
  }
26
- function hasSubmenu(menuItem) {
27
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
28
- }
29
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
26
+ function handleKeyPress(event, elementItems, elementItemIndex) {
30
27
  const currentEl = elementItems.item(elementItemIndex);
31
28
  switch (event.key) {
32
29
  case "ArrowUp":
33
30
  case "ArrowLeft": {
34
- if (event.key === "ArrowLeft" && menuElementDiv) ;
35
31
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
36
32
  event.preventDefault();
37
33
  moveFocus(elementItems, elementItemIndex, -1);
@@ -46,7 +42,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
46
42
  }
47
43
  case "ArrowDown":
48
44
  case "ArrowRight": {
49
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) ;
50
45
  if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
51
46
  event.preventDefault();
52
47
  moveFocus(elementItems, elementItemIndex, 1);
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to block. The block traps focus and can be interacted with using the keyboard.
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to block. The block traps focus and can be interacted with using the keyboard.
@@ -1,4 +1,75 @@
1
- import { handleKeyPress } from '../chunk-ZJXZZDUR.js';
1
+ // src/utils/handleKeyPress/handleKeyPress.ts
2
+ function isTextInput(el) {
3
+ if (el.tagName !== "INPUT") return false;
4
+ const type = el.type;
5
+ return ["text", "email", "password", "tel", "number"].includes(type);
6
+ }
7
+ function isTextArea(el) {
8
+ return el.tagName === "TEXTAREA";
9
+ }
10
+ function isNativeButton(el) {
11
+ return el.tagName === "BUTTON" || el.tagName === "INPUT" && ["button", "submit", "reset"].includes(el.type);
12
+ }
13
+ function isLink(el) {
14
+ return el.tagName === "A";
15
+ }
16
+ function moveFocus(elementItems, currentIndex, direction) {
17
+ const len = elementItems.length;
18
+ const nextIndex = (currentIndex + direction + len) % len;
19
+ elementItems.item(nextIndex).focus();
20
+ }
21
+ function isClickableButNotSemantic(el) {
22
+ return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
23
+ }
24
+ function handleKeyPress(event, elementItems, elementItemIndex) {
25
+ const currentEl = elementItems.item(elementItemIndex);
26
+ switch (event.key) {
27
+ case "ArrowUp":
28
+ case "ArrowLeft": {
29
+ if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
30
+ event.preventDefault();
31
+ moveFocus(elementItems, elementItemIndex, -1);
32
+ } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
33
+ const cursorStart = currentEl.selectionStart;
34
+ if (cursorStart === 0) {
35
+ event.preventDefault();
36
+ moveFocus(elementItems, elementItemIndex, -1);
37
+ }
38
+ }
39
+ break;
40
+ }
41
+ case "ArrowDown":
42
+ case "ArrowRight": {
43
+ if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
44
+ event.preventDefault();
45
+ moveFocus(elementItems, elementItemIndex, 1);
46
+ } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
47
+ const value = currentEl.value;
48
+ const cursorEnd = currentEl.selectionStart;
49
+ if (cursorEnd === value.length) {
50
+ event.preventDefault();
51
+ moveFocus(elementItems, elementItemIndex, 1);
52
+ }
53
+ }
54
+ break;
55
+ }
56
+ case "Escape": {
57
+ event.preventDefault();
58
+ break;
59
+ }
60
+ case "Enter":
61
+ case " ": {
62
+ if (!isNativeButton(currentEl) && !isLink(currentEl) && isClickableButNotSemantic(currentEl)) {
63
+ event.preventDefault();
64
+ currentEl.click();
65
+ } else if (isNativeButton(currentEl)) {
66
+ event.preventDefault();
67
+ currentEl.click();
68
+ }
69
+ break;
70
+ }
71
+ }
72
+ }
2
73
 
3
74
  // src/block/src/makeBlockAccessible/makeBlockAccessible.ts
4
75
  function makeBlockAccessible({ blockId, blockItemsClass }) {
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a checkbox group accessible by managing ARIA attributes and keyboard interaction.
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes a checkbox group accessible by managing ARIA attributes and keyboard interaction.
@@ -1,4 +1,4 @@
1
- import { C as ComboboxConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { C as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a Combobox accessible by adding appropriate ARIA attributes, keyboard interactions and focus management.
@@ -1,4 +1,4 @@
1
- import { C as ComboboxConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { C as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes a Combobox accessible by adding appropriate ARIA attributes, keyboard interactions and focus management.
@@ -1,131 +1,5 @@
1
1
  'use strict';
2
2
 
3
- // src/utils/handleKeyPress/handleKeyPress.ts
4
- function isTextInput(el) {
5
- if (el.tagName !== "INPUT") return false;
6
- const type = el.type;
7
- return ["text", "email", "password", "tel", "number"].includes(type);
8
- }
9
- function isTextArea(el) {
10
- return el.tagName === "TEXTAREA";
11
- }
12
- function isNativeButton(el) {
13
- return el.tagName === "BUTTON" || el.tagName === "INPUT" && ["button", "submit", "reset"].includes(el.type);
14
- }
15
- function isLink(el) {
16
- return el.tagName === "A";
17
- }
18
- function moveFocus(elementItems, currentIndex, direction) {
19
- const len = elementItems.length;
20
- const nextIndex = (currentIndex + direction + len) % len;
21
- elementItems.item(nextIndex).focus();
22
- }
23
- function isClickableButNotSemantic(el) {
24
- return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
25
- }
26
- function handleMenuClose(menuElement, menuTriggerButton) {
27
- menuElement.style.display = "none";
28
- const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
29
- if (!menuTriggerButtonId) {
30
- console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
31
- return;
32
- }
33
- menuTriggerButton.setAttribute("aria-expanded", "false");
34
- }
35
- function hasSubmenu(menuItem) {
36
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
37
- }
38
- function getSubmenuId(menuItem) {
39
- return menuItem.getAttribute("aria-controls");
40
- }
41
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
42
- const currentEl = elementItems.item(elementItemIndex);
43
- switch (event.key) {
44
- case "ArrowUp":
45
- case "ArrowLeft": {
46
- if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
47
- const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
48
- if (labelledBy) {
49
- const parentTrigger = document.getElementById(labelledBy);
50
- if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
51
- event.preventDefault();
52
- closeSubmenu();
53
- parentTrigger.focus();
54
- return;
55
- }
56
- }
57
- }
58
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
59
- event.preventDefault();
60
- moveFocus(elementItems, elementItemIndex, -1);
61
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
62
- const cursorStart = currentEl.selectionStart;
63
- if (cursorStart === 0) {
64
- event.preventDefault();
65
- moveFocus(elementItems, elementItemIndex, -1);
66
- }
67
- }
68
- break;
69
- }
70
- case "ArrowDown":
71
- case "ArrowRight": {
72
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
73
- event.preventDefault();
74
- const submenuId = getSubmenuId(currentEl);
75
- if (submenuId) {
76
- openSubmenu(submenuId);
77
- return;
78
- }
79
- }
80
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
81
- event.preventDefault();
82
- moveFocus(elementItems, elementItemIndex, 1);
83
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
84
- const value = currentEl.value;
85
- const cursorEnd = currentEl.selectionStart;
86
- if (cursorEnd === value.length) {
87
- event.preventDefault();
88
- moveFocus(elementItems, elementItemIndex, 1);
89
- }
90
- }
91
- break;
92
- }
93
- case "Escape": {
94
- event.preventDefault();
95
- if (menuElementDiv && triggerButton) {
96
- if (getComputedStyle(menuElementDiv).display === "block") {
97
- handleMenuClose(menuElementDiv, triggerButton);
98
- if (onOpenChange) {
99
- onOpenChange(false);
100
- }
101
- }
102
- triggerButton.focus();
103
- }
104
- break;
105
- }
106
- case "Enter":
107
- case " ": {
108
- if (!isNativeButton(currentEl) && !isLink(currentEl) && isClickableButNotSemantic(currentEl)) {
109
- event.preventDefault();
110
- currentEl.click();
111
- } else if (isNativeButton(currentEl)) {
112
- event.preventDefault();
113
- currentEl.click();
114
- }
115
- break;
116
- }
117
- case "Tab": {
118
- if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
119
- handleMenuClose(menuElementDiv, triggerButton);
120
- if (onOpenChange) {
121
- onOpenChange(false);
122
- }
123
- }
124
- break;
125
- }
126
- }
127
- }
128
-
129
3
  // src/menu/src/makeMenuAccessible/makeMenuAccessible.ts
130
4
  function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
131
5
  const menuDiv = document.querySelector(`#${menuId}`);
@@ -202,15 +76,86 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
202
76
  const items = getItems();
203
77
  items.forEach((item) => {
204
78
  item.setAttribute("role", "menuitem");
79
+ if (item.hasAttribute("data-submenu-id")) {
80
+ item.setAttribute("aria-haspopup", "menu");
81
+ item.setAttribute("aria-controls", item.getAttribute("data-submenu-id"));
82
+ }
205
83
  });
206
84
  }
85
+ function moveFocus(elementItems, currentIndex, direction) {
86
+ const len = elementItems.length;
87
+ const nextIndex = (currentIndex + direction + len) % len;
88
+ elementItems.item(nextIndex).focus();
89
+ }
90
+ function hasSubmenu(menuItem) {
91
+ return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
92
+ }
207
93
  intializeMenuItems();
94
+ function handleItemsKeydown(event, menuItem, menuItemIndex) {
95
+ switch (event.key) {
96
+ case "ArrowUp":
97
+ case "ArrowLeft": {
98
+ if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
99
+ event.preventDefault();
100
+ closeMenu();
101
+ return;
102
+ }
103
+ event.preventDefault();
104
+ moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
105
+ break;
106
+ }
107
+ case "ArrowDown":
108
+ case "ArrowRight": {
109
+ if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
110
+ event.preventDefault();
111
+ const submenuId = menuItem.getAttribute("aria-controls");
112
+ if (submenuId) {
113
+ openSubmenu(submenuId);
114
+ return;
115
+ }
116
+ }
117
+ event.preventDefault();
118
+ moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
119
+ break;
120
+ }
121
+ case "Escape": {
122
+ event.preventDefault();
123
+ closeMenu();
124
+ triggerButton.focus();
125
+ if (onOpenChange) {
126
+ onOpenChange(false);
127
+ }
128
+ break;
129
+ }
130
+ case "Enter":
131
+ case " ": {
132
+ event.preventDefault();
133
+ menuItem.click();
134
+ break;
135
+ }
136
+ case "Tab": {
137
+ if (!event.shiftKey || event.shiftKey) {
138
+ closeMenu();
139
+ if (onOpenChange) {
140
+ onOpenChange(false);
141
+ }
142
+ }
143
+ break;
144
+ }
145
+ }
146
+ }
208
147
  function isItemInNestedSubmenu(item) {
209
148
  let parent = item.parentElement;
210
149
  while (parent && parent !== menuDiv) {
211
150
  if (parent.getAttribute("role") === "menu") {
212
151
  return true;
213
152
  }
153
+ if (parent.id) {
154
+ const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
155
+ if (parentMenuTrigger) {
156
+ return true;
157
+ }
158
+ }
214
159
  parent = parent.parentElement;
215
160
  }
216
161
  return false;
@@ -246,9 +191,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
246
191
  }
247
192
  submenuInstance.openMenu();
248
193
  }
249
- function closeSubmenu() {
250
- closeMenu();
251
- }
252
194
  function onOpenChange(isOpen) {
253
195
  if (callback?.onOpenChange) {
254
196
  try {
@@ -260,19 +202,9 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
260
202
  }
261
203
  function addListeners() {
262
204
  const items = getFilteredItems();
263
- const nodeListLike = toNodeListLike(items);
264
205
  items.forEach((menuItem, index) => {
265
206
  if (!handlerMap.has(menuItem)) {
266
- const handler = (event) => handleKeyPress(
267
- event,
268
- nodeListLike,
269
- index,
270
- menuDiv,
271
- triggerButton,
272
- openSubmenu,
273
- closeSubmenu,
274
- onOpenChange
275
- );
207
+ const handler = (event) => handleItemsKeydown(event, menuItem, index);
276
208
  menuItem.addEventListener("keydown", handler);
277
209
  handlerMap.set(menuItem, handler);
278
210
  }
@@ -1,4 +1,4 @@
1
- import { M as MenuConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { M as MenuConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to toggle menu. The menu traps focus and can be interacted with using the keyboard. The first interactive item of the menu has focus when menu open.
@@ -1,4 +1,4 @@
1
- import { M as MenuConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { M as MenuConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to toggle menu. The menu traps focus and can be interacted with using the keyboard. The first interactive item of the menu has focus when menu open.
@@ -1,5 +1,3 @@
1
- import { handleKeyPress } from '../chunk-ZJXZZDUR.js';
2
-
3
1
  // src/menu/src/makeMenuAccessible/makeMenuAccessible.ts
4
2
  function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
5
3
  const menuDiv = document.querySelector(`#${menuId}`);
@@ -76,15 +74,86 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
76
74
  const items = getItems();
77
75
  items.forEach((item) => {
78
76
  item.setAttribute("role", "menuitem");
77
+ if (item.hasAttribute("data-submenu-id")) {
78
+ item.setAttribute("aria-haspopup", "menu");
79
+ item.setAttribute("aria-controls", item.getAttribute("data-submenu-id"));
80
+ }
79
81
  });
80
82
  }
83
+ function moveFocus(elementItems, currentIndex, direction) {
84
+ const len = elementItems.length;
85
+ const nextIndex = (currentIndex + direction + len) % len;
86
+ elementItems.item(nextIndex).focus();
87
+ }
88
+ function hasSubmenu(menuItem) {
89
+ return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
90
+ }
81
91
  intializeMenuItems();
92
+ function handleItemsKeydown(event, menuItem, menuItemIndex) {
93
+ switch (event.key) {
94
+ case "ArrowUp":
95
+ case "ArrowLeft": {
96
+ if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
97
+ event.preventDefault();
98
+ closeMenu();
99
+ return;
100
+ }
101
+ event.preventDefault();
102
+ moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
103
+ break;
104
+ }
105
+ case "ArrowDown":
106
+ case "ArrowRight": {
107
+ if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
108
+ event.preventDefault();
109
+ const submenuId = menuItem.getAttribute("aria-controls");
110
+ if (submenuId) {
111
+ openSubmenu(submenuId);
112
+ return;
113
+ }
114
+ }
115
+ event.preventDefault();
116
+ moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
117
+ break;
118
+ }
119
+ case "Escape": {
120
+ event.preventDefault();
121
+ closeMenu();
122
+ triggerButton.focus();
123
+ if (onOpenChange) {
124
+ onOpenChange(false);
125
+ }
126
+ break;
127
+ }
128
+ case "Enter":
129
+ case " ": {
130
+ event.preventDefault();
131
+ menuItem.click();
132
+ break;
133
+ }
134
+ case "Tab": {
135
+ if (!event.shiftKey || event.shiftKey) {
136
+ closeMenu();
137
+ if (onOpenChange) {
138
+ onOpenChange(false);
139
+ }
140
+ }
141
+ break;
142
+ }
143
+ }
144
+ }
82
145
  function isItemInNestedSubmenu(item) {
83
146
  let parent = item.parentElement;
84
147
  while (parent && parent !== menuDiv) {
85
148
  if (parent.getAttribute("role") === "menu") {
86
149
  return true;
87
150
  }
151
+ if (parent.id) {
152
+ const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
153
+ if (parentMenuTrigger) {
154
+ return true;
155
+ }
156
+ }
88
157
  parent = parent.parentElement;
89
158
  }
90
159
  return false;
@@ -120,9 +189,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
120
189
  }
121
190
  submenuInstance.openMenu();
122
191
  }
123
- function closeSubmenu() {
124
- closeMenu();
125
- }
126
192
  function onOpenChange(isOpen) {
127
193
  if (callback?.onOpenChange) {
128
194
  try {
@@ -134,19 +200,9 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
134
200
  }
135
201
  function addListeners() {
136
202
  const items = getFilteredItems();
137
- const nodeListLike = toNodeListLike(items);
138
203
  items.forEach((menuItem, index) => {
139
204
  if (!handlerMap.has(menuItem)) {
140
- const handler = (event) => handleKeyPress(
141
- event,
142
- nodeListLike,
143
- index,
144
- menuDiv,
145
- triggerButton,
146
- openSubmenu,
147
- closeSubmenu,
148
- onOpenChange
149
- );
205
+ const handler = (event) => handleItemsKeydown(event, menuItem, index);
150
206
  menuItem.addEventListener("keydown", handler);
151
207
  handlerMap.set(menuItem, handler);
152
208
  }
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a radio group accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes a radio group accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -1,4 +1,4 @@
1
- import { T as TabsConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { T as TabsConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes tabs accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -1,4 +1,4 @@
1
- import { T as TabsConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { T as TabsConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes tabs accessible by managing ARIA attributes, keyboard interaction, and state.
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a toggle button accessible by managing ARIA attributes and keyboard interactions.
@@ -1,4 +1,4 @@
1
- import { A as AccessibilityInstance } from '../Types.d-yGC2bBaB.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes a toggle button accessible by managing ARIA attributes and keyboard interactions.
@@ -3,7 +3,7 @@
3
3
  "id": "aria-ease.accordion",
4
4
  "version": "1.0.0",
5
5
  "created": "09-02-2026",
6
- "lastUpdated": "28-02-2026",
6
+ "lastUpdated": "15-03-2026",
7
7
  "description": "ARIA Accordion interaction contract. Validates the ARIA and interaction contract for a custom accordion component following the ARIA Authoring Practices Guide accordion with show/hide pattern.",
8
8
  "source": {
9
9
  "apg": "https://www.w3.org/WAI/ARIA/apg/patterns/accordion/",
@@ -3,7 +3,7 @@
3
3
  "id": "aria-ease.combobox.listbox",
4
4
  "version": "1.0.0",
5
5
  "created": "11-02-2026",
6
- "lastUpdated": "28-02-2026",
6
+ "lastUpdated": "15-03-2026",
7
7
  "description": "ARIA Combobox with Listbox popup interaction contract. Validates the ARIA and interaction contract for a custom combobox with listbox component following the ARIA Authoring Practices Guide combobox with listbox popup pattern.",
8
8
  "source": {
9
9
  "apg": "https://www.w3.org/WAI/ARIA/apg/patterns/combobox/",
@@ -3,7 +3,7 @@
3
3
  "id": "aria-ease.menu",
4
4
  "version": "1.0.0",
5
5
  "created": "11-02-2026",
6
- "lastUpdated": "28-02-2026",
6
+ "lastUpdated": "15-03-2026",
7
7
  "description": "ARIA Menu interaction contract. Validates the ARIA and interaction contract for a custom menu component following the ARIA Authoring Practices Guide menu with popup pattern.",
8
8
  "source": {
9
9
  "apg": "https://www.w3.org/WAI/ARIA/apg/patterns/menubar/",
@@ -16,7 +16,7 @@
16
16
  "trigger": "[data-test-id=menu-trigger]",
17
17
  "container": "[role=menu]",
18
18
  "items": "[role=menuitem], [role=menuitemradio], [role=menuitemcheckbox]",
19
- "submenuTrigger": "[role=menu] [role=menuitem][aria-haspopup=true], [role=menu] [role=menuitemradio][aria-haspopup=true], [role=menu] [role=menuitemcheckbox][aria-haspopup=true], [role=menu] [role=menuitem][aria-haspopup=menu], [role=menu] [role=menuitemradio][aria-haspopup=menu], [role=menu] [role=menuitemcheckbox][aria-haspopup=menu]",
19
+ "submenuTrigger": "[role=menu] [role=menuitem][aria-haspopup=menu][aria-controls], [role=menu] [role=menuitemradio][aria-haspopup=menu][aria-controls], [role=menu] [role=menuitemcheckbox][aria-haspopup=menu][aria-controls], [data-submenu-id]",
20
20
  "submenu": "[role=menu][aria-labelledby]",
21
21
  "focusable": "[role=menuitem], [role=menuitemradio], [role=menuitemcheckbox]",
22
22
  "relative": "[role=menuitem], [role=menuitemradio], [role=menuitemcheckbox]",
@@ -3,7 +3,7 @@
3
3
  "id": "aria-ease.tabs",
4
4
  "version": "1.0.0",
5
5
  "created": "28-02-2026",
6
- "lastUpdated": "28-02-2026",
6
+ "lastUpdated": "15-03-2026",
7
7
  "description": "ARIA tabs interaction contract. Validates the ARIA and interaction contract for a custom tabs component following the ARIA Authoring Practices Guide pattern.",
8
8
  "source": {
9
9
  "apg": "https://www.w3.org/WAI/ARIA/apg/patterns/tabs/",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aria-ease",
3
- "version": "6.5.0",
3
+ "version": "6.5.1",
4
4
  "description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
5
5
  "main": "dist/index.cjs",
6
6
  "type": "module",
@@ -1,127 +0,0 @@
1
- // src/utils/handleKeyPress/handleKeyPress.ts
2
- function isTextInput(el) {
3
- if (el.tagName !== "INPUT") return false;
4
- const type = el.type;
5
- return ["text", "email", "password", "tel", "number"].includes(type);
6
- }
7
- function isTextArea(el) {
8
- return el.tagName === "TEXTAREA";
9
- }
10
- function isNativeButton(el) {
11
- return el.tagName === "BUTTON" || el.tagName === "INPUT" && ["button", "submit", "reset"].includes(el.type);
12
- }
13
- function isLink(el) {
14
- return el.tagName === "A";
15
- }
16
- function moveFocus(elementItems, currentIndex, direction) {
17
- const len = elementItems.length;
18
- const nextIndex = (currentIndex + direction + len) % len;
19
- elementItems.item(nextIndex).focus();
20
- }
21
- function isClickableButNotSemantic(el) {
22
- return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
23
- }
24
- function handleMenuClose(menuElement, menuTriggerButton) {
25
- menuElement.style.display = "none";
26
- const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
27
- if (!menuTriggerButtonId) {
28
- console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
29
- return;
30
- }
31
- menuTriggerButton.setAttribute("aria-expanded", "false");
32
- }
33
- function hasSubmenu(menuItem) {
34
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
35
- }
36
- function getSubmenuId(menuItem) {
37
- return menuItem.getAttribute("aria-controls");
38
- }
39
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
40
- const currentEl = elementItems.item(elementItemIndex);
41
- switch (event.key) {
42
- case "ArrowUp":
43
- case "ArrowLeft": {
44
- if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
45
- const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
46
- if (labelledBy) {
47
- const parentTrigger = document.getElementById(labelledBy);
48
- if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
49
- event.preventDefault();
50
- closeSubmenu();
51
- parentTrigger.focus();
52
- return;
53
- }
54
- }
55
- }
56
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
57
- event.preventDefault();
58
- moveFocus(elementItems, elementItemIndex, -1);
59
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
60
- const cursorStart = currentEl.selectionStart;
61
- if (cursorStart === 0) {
62
- event.preventDefault();
63
- moveFocus(elementItems, elementItemIndex, -1);
64
- }
65
- }
66
- break;
67
- }
68
- case "ArrowDown":
69
- case "ArrowRight": {
70
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
71
- event.preventDefault();
72
- const submenuId = getSubmenuId(currentEl);
73
- if (submenuId) {
74
- openSubmenu(submenuId);
75
- return;
76
- }
77
- }
78
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
79
- event.preventDefault();
80
- moveFocus(elementItems, elementItemIndex, 1);
81
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
82
- const value = currentEl.value;
83
- const cursorEnd = currentEl.selectionStart;
84
- if (cursorEnd === value.length) {
85
- event.preventDefault();
86
- moveFocus(elementItems, elementItemIndex, 1);
87
- }
88
- }
89
- break;
90
- }
91
- case "Escape": {
92
- event.preventDefault();
93
- if (menuElementDiv && triggerButton) {
94
- if (getComputedStyle(menuElementDiv).display === "block") {
95
- handleMenuClose(menuElementDiv, triggerButton);
96
- if (onOpenChange) {
97
- onOpenChange(false);
98
- }
99
- }
100
- triggerButton.focus();
101
- }
102
- break;
103
- }
104
- case "Enter":
105
- case " ": {
106
- if (!isNativeButton(currentEl) && !isLink(currentEl) && isClickableButNotSemantic(currentEl)) {
107
- event.preventDefault();
108
- currentEl.click();
109
- } else if (isNativeButton(currentEl)) {
110
- event.preventDefault();
111
- currentEl.click();
112
- }
113
- break;
114
- }
115
- case "Tab": {
116
- if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
117
- handleMenuClose(menuElementDiv, triggerButton);
118
- if (onOpenChange) {
119
- onOpenChange(false);
120
- }
121
- }
122
- break;
123
- }
124
- }
125
- }
126
-
127
- export { handleKeyPress };