import { defineComponent, ref, reactive, watchEffect, nextTick, createBlock, openBlock, unref, withCtx, renderSlot } from 'vue'; import { isClient } from '@vueuse/shared'; import { c as createFocusScopesStack, r as removeLinks } from './stack.js'; import { A as AUTOFOCUS_ON_MOUNT, f as focusFirst, g as getTabbableCandidates, a as focus, b as AUTOFOCUS_ON_UNMOUNT, E as EVENT_OPTIONS, c as getTabbableEdges } from './utils.js'; import { u as useForwardExpose } from '../shared/useForwardExpose.js'; import { g as getActiveElement } from '../shared/getActiveElement.js'; import { P as Primitive } from '../Primitive/Primitive.js'; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "FocusScope", props: { loop: { type: Boolean, default: false }, trapped: { type: Boolean, default: false }, asChild: { type: Boolean }, as: {} }, emits: ["mountAutoFocus", "unmountAutoFocus"], setup(__props, { emit: __emit }) { const props = __props; const emits = __emit; const { currentRef, currentElement } = useForwardExpose(); const lastFocusedElementRef = ref(null); const focusScopesStack = createFocusScopesStack(); const focusScope = reactive({ paused: false, pause() { this.paused = true; }, resume() { this.paused = false; } }); watchEffect((cleanupFn) => { if (!isClient) return; const container = currentElement.value; if (!props.trapped) return; function handleFocusIn(event) { if (focusScope.paused || !container) return; const target = event.target; if (container.contains(target)) lastFocusedElementRef.value = target; else focus(lastFocusedElementRef.value, { select: true }); } function handleFocusOut(event) { if (focusScope.paused || !container) return; const relatedTarget = event.relatedTarget; if (relatedTarget === null) return; if (!container.contains(relatedTarget)) focus(lastFocusedElementRef.value, { select: true }); } function handleMutations(mutations) { const isLastFocusedElementExist = container.contains(lastFocusedElementRef.value); if (!isLastFocusedElementExist) focus(container); } document.addEventListener("focusin", handleFocusIn); document.addEventListener("focusout", handleFocusOut); const mutationObserver = new MutationObserver(handleMutations); if (container) mutationObserver.observe(container, { childList: true, subtree: true }); cleanupFn(() => { document.removeEventListener("focusin", handleFocusIn); document.removeEventListener("focusout", handleFocusOut); mutationObserver.disconnect(); }); }); watchEffect(async (cleanupFn) => { const container = currentElement.value; await nextTick(); if (!container) return; focusScopesStack.add(focusScope); const previouslyFocusedElement = getActiveElement(); const hasFocusedCandidate = container.contains(previouslyFocusedElement); if (!hasFocusedCandidate) { const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS); container.addEventListener(AUTOFOCUS_ON_MOUNT, (ev) => emits("mountAutoFocus", ev)); container.dispatchEvent(mountEvent); if (!mountEvent.defaultPrevented) { focusFirst(removeLinks(getTabbableCandidates(container)), { select: true }); if (getActiveElement() === previouslyFocusedElement) focus(container); } } cleanupFn(() => { container.removeEventListener(AUTOFOCUS_ON_MOUNT, (ev) => emits("mountAutoFocus", ev)); const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS); const unmountEventHandler = (ev) => { emits("unmountAutoFocus", ev); }; container.addEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler); container.dispatchEvent(unmountEvent); setTimeout(() => { if (!unmountEvent.defaultPrevented) focus(previouslyFocusedElement ?? document.body, { select: true }); container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler); focusScopesStack.remove(focusScope); }, 0); }); }); function handleKeyDown(event) { if (!props.loop && !props.trapped) return; if (focusScope.paused) return; const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey; const focusedElement = getActiveElement(); if (isTabKey && focusedElement) { const container = event.currentTarget; const [first, last] = getTabbableEdges(container); const hasTabbableElementsInside = first && last; if (!hasTabbableElementsInside) { if (focusedElement === container) event.preventDefault(); } else { if (!event.shiftKey && focusedElement === last) { event.preventDefault(); if (props.loop) focus(first, { select: true }); } else if (event.shiftKey && focusedElement === first) { event.preventDefault(); if (props.loop) focus(last, { select: true }); } } } } return (_ctx, _cache) => { return openBlock(), createBlock(unref(Primitive), { ref_key: "currentRef", ref: currentRef, tabindex: "-1", "as-child": _ctx.asChild, as: _ctx.as, onKeydown: handleKeyDown }, { default: withCtx(() => [ renderSlot(_ctx.$slots, "default") ]), _: 3 }, 8, ["as-child", "as"]); }; } }); export { _sfc_main as _ }; //# sourceMappingURL=FocusScope.js.map