rsjs-menu.js 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. // @ts-nocheck
  2. function overflowMenu(subtree = document) {
  3. subtree.querySelectorAll("[data-overflow-menu]").forEach(menuRoot => {
  4. const
  5. button = menuRoot.querySelector("[aria-haspopup]"),
  6. menu = menuRoot.querySelector("[role=menu]"),
  7. items = [...menu.querySelectorAll("[role=menuitem]")];
  8. const isOpen = () => !menu.hidden;
  9. items.forEach(item => item.setAttribute("tabindex", "-1"));
  10. function toggleMenu(open = !isOpen()) {
  11. if (open) {
  12. menu.hidden = false;
  13. button.setAttribute("aria-expanded", "true");
  14. items[0].focus();
  15. } else {
  16. menu.hidden = true;
  17. button.setAttribute("aria-expanded", "false");
  18. }
  19. }
  20. toggleMenu(isOpen());
  21. button.addEventListener("click", () => toggleMenu());
  22. menuRoot.addEventListener("blur", e => console.log(e) || toggleMenu(false));
  23. window.addEventListener("click", function clickAway(event) {
  24. if (!menuRoot.isConnected) window.removeEventListener("click", clickAway);
  25. if (!menuRoot.contains(event.target)) toggleMenu(false);
  26. })
  27. const currentIndex = () => {
  28. const idx = items.indexOf(document.activeElement);
  29. if (idx === -1) return 0;
  30. return idx;
  31. }
  32. menuRoot.addEventListener("keydown", e => {
  33. if (e.key === "ArrowUp") {
  34. items[currentIndex() - 1]?.focus();
  35. } else if (e.key === "ArrowDown") {
  36. items[currentIndex() + 1]?.focus();
  37. } else if (e.key === "Space") {
  38. items[currentIndex()].click();
  39. } else if (e.key === "Home") {
  40. items[0].focus();
  41. } else if (e.key === "End") {
  42. items[items.length - 1].focus();
  43. } else if (e.key === "Escape") {
  44. toggleMenu(false);
  45. button.focus();
  46. } else if (e.key === "Tab") {
  47. toggleMenu(false);
  48. }
  49. })
  50. })
  51. }
  52. addEventListener("htmx:load", e => overflowMenu(e.target));