import { getTabbableElements } from "./chunk.LXDTFLWU.js"; import { __yieldStar } from "./chunk.KIILAQWQ.js"; // src/internal/active-elements.ts function* activeElements(activeElement = document.activeElement) { if (activeElement === null || activeElement === void 0) return; yield activeElement; if ("shadowRoot" in activeElement && activeElement.shadowRoot && activeElement.shadowRoot.mode !== "closed") { yield* __yieldStar(activeElements(activeElement.shadowRoot.activeElement)); } } function getDeepestActiveElement() { return [...activeElements()].pop(); } // src/internal/modal.ts var activeModals = []; var Modal = class { constructor(element) { this.tabDirection = "forward"; this.handleFocusIn = () => { if (!this.isActive()) return; this.checkFocus(); }; this.handleKeyDown = (event) => { var _a; if (event.key !== "Tab" || this.isExternalActivated) return; if (!this.isActive()) return; const currentActiveElement = getDeepestActiveElement(); this.previousFocus = currentActiveElement; if (this.previousFocus && this.possiblyHasTabbableChildren(this.previousFocus)) { return; } if (event.shiftKey) { this.tabDirection = "backward"; } else { this.tabDirection = "forward"; } const tabbableElements = getTabbableElements(this.element); let currentFocusIndex = tabbableElements.findIndex((el) => el === currentActiveElement); this.previousFocus = this.currentFocus; const addition = this.tabDirection === "forward" ? 1 : -1; while (true) { if (currentFocusIndex + addition >= tabbableElements.length) { currentFocusIndex = 0; } else if (currentFocusIndex + addition < 0) { currentFocusIndex = tabbableElements.length - 1; } else { currentFocusIndex += addition; } this.previousFocus = this.currentFocus; const nextFocus = ( /** @type {HTMLElement} */ tabbableElements[currentFocusIndex] ); if (this.tabDirection === "backward") { if (this.previousFocus && this.possiblyHasTabbableChildren(this.previousFocus)) { return; } } if (nextFocus && this.possiblyHasTabbableChildren(nextFocus)) { return; } event.preventDefault(); this.currentFocus = nextFocus; (_a = this.currentFocus) == null ? void 0 : _a.focus({ preventScroll: false }); const allActiveElements = [...activeElements()]; if (allActiveElements.includes(this.currentFocus) || !allActiveElements.includes(this.previousFocus)) { break; } } setTimeout(() => this.checkFocus()); }; this.handleKeyUp = () => { this.tabDirection = "forward"; }; this.element = element; this.elementsWithTabbableControls = ["iframe"]; } /** Activates focus trapping. */ activate() { activeModals.push(this.element); document.addEventListener("focusin", this.handleFocusIn); document.addEventListener("keydown", this.handleKeyDown); document.addEventListener("keyup", this.handleKeyUp); } /** Deactivates focus trapping. */ deactivate() { activeModals = activeModals.filter((modal) => modal !== this.element); this.currentFocus = null; document.removeEventListener("focusin", this.handleFocusIn); document.removeEventListener("keydown", this.handleKeyDown); document.removeEventListener("keyup", this.handleKeyUp); } /** Determines if this modal element is currently active or not. */ isActive() { return activeModals[activeModals.length - 1] === this.element; } /** Activates external modal behavior and temporarily disables focus trapping. */ activateExternal() { this.isExternalActivated = true; } /** Deactivates external modal behavior and re-enables focus trapping. */ deactivateExternal() { this.isExternalActivated = false; } checkFocus() { if (this.isActive() && !this.isExternalActivated) { const tabbableElements = getTabbableElements(this.element); if (!this.element.matches(":focus-within")) { const start = tabbableElements[0]; const end = tabbableElements[tabbableElements.length - 1]; const target = this.tabDirection === "forward" ? start : end; if (typeof (target == null ? void 0 : target.focus) === "function") { this.currentFocus = target; target.focus({ preventScroll: false }); } } } } possiblyHasTabbableChildren(element) { return this.elementsWithTabbableControls.includes(element.tagName.toLowerCase()) || element.hasAttribute("controls"); } }; export { Modal };