/** * Mobile Drawer Component * Slide-in navigation drawer from the left with backdrop */ class MobileDrawer extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); this.isOpen = false; this.handleBackdropClick = this.handleBackdropClick.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); } connectedCallback() { this.render(); this.setupEventListeners(); // Set initial aria-hidden state this.setAttribute("aria-hidden", "true"); } disconnectedCallback() { document.removeEventListener("keydown", this.handleKeyDown); } setupEventListeners() { // Close button const closeBtn = this.shadowRoot.querySelector(".close-button"); closeBtn?.addEventListener("click", () => this.close()); // Backdrop click const backdrop = this.shadowRoot.querySelector(".backdrop"); backdrop?.addEventListener("click", this.handleBackdropClick); // Escape key document.addEventListener("keydown", this.handleKeyDown); // Listen for global open event window.addEventListener("open-mobile-drawer", () => this.open()); window.addEventListener("close-mobile-drawer", () => this.close()); window.addEventListener("toggle-mobile-drawer", () => this.toggle()); } handleBackdropClick() { this.close(); } handleKeyDown(e) { if (e.key === "Escape" && this.isOpen) { this.close(); } } open() { this.isOpen = true; this.shadowRoot.querySelector(".drawer-container").classList.add("open"); this.setAttribute("aria-hidden", "false"); document.body.style.overflow = "hidden"; // Focus trap - focus first focusable element setTimeout(() => { const closeBtn = this.shadowRoot.querySelector(".close-button"); closeBtn?.focus(); }, 100); } close() { this.isOpen = false; this.shadowRoot.querySelector(".drawer-container").classList.remove("open"); this.setAttribute("aria-hidden", "true"); document.body.style.overflow = ""; } toggle() { if (this.isOpen) { this.close(); } else { this.open(); } } render() { this.shadowRoot.innerHTML = `
`; } } customElements.define("mobile-drawer", MobileDrawer);