import { defineComponent, ref, provide, unref, onMounted, nextTick, watch, onBeforeUnmount, renderSlot } from 'vue';
import '../../../constants/index.mjs';
import { getEdges, tryFocus, focusableStack, focusFirstDescendant, obtainAllFocusableElements } from './utils.mjs';
import { ON_MOUNT_FOCUS_EVT, ON_UNMOUNT_FOCUS_EVT, FOCUS_TRAP_INJECTION_KEY, FOCUS_ON_MOUNT, FOCUS_ON_MOUNT_OPTS, FOCUS_ON_UNMOUNT } from './tokens.mjs';
import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
import { EVENT_CODE } from '../../../constants/aria.mjs';

const _sfc_main = defineComponent({
  name: "ElFocusTrap",
  inheritAttrs: false,
  props: {
    loop: Boolean,
    trapped: Boolean
  },
  emits: [ON_MOUNT_FOCUS_EVT, ON_UNMOUNT_FOCUS_EVT],
  setup(props, { emit }) {
    const focusTrapRef = ref();
    const forwardRef = ref(null);
    let lastFocusBeforeMounted;
    let lastFocusAfterMounted;
    const focusLayer = {
      paused: false,
      pause() {
        this.paused = true;
      },
      resume() {
        this.paused = false;
      }
    };
    const onKeydown = (e) => {
      if (!props.loop && !props.trapped)
        return;
      if (focusLayer.paused)
        return;
      const { key, altKey, ctrlKey, metaKey, currentTarget, shiftKey } = e;
      const { loop } = props;
      const isTabbing = key === EVENT_CODE.tab && !altKey && !ctrlKey && !metaKey;
      const currentFocusingEl = document.activeElement;
      if (isTabbing && currentFocusingEl) {
        const container = currentTarget;
        const [first, last] = getEdges(container);
        const isTabbable = first && last;
        if (!isTabbable) {
          if (currentFocusingEl === container)
            e.preventDefault();
        } else {
          if (!shiftKey && currentFocusingEl === last) {
            e.preventDefault();
            if (loop)
              tryFocus(first, true);
          } else if (shiftKey && currentFocusingEl === first) {
            e.preventDefault();
            if (loop)
              tryFocus(last, true);
          }
        }
      }
    };
    provide(FOCUS_TRAP_INJECTION_KEY, {
      focusTrapRef: forwardRef,
      onKeydown
    });
    const focusOnMount = (e) => {
      emit(ON_MOUNT_FOCUS_EVT, e);
    };
    const focusOnUnmount = (e) => emit(ON_UNMOUNT_FOCUS_EVT, e);
    const onFocusIn = (e) => {
      const trapContainer = unref(forwardRef);
      if (focusLayer.paused || !trapContainer)
        return;
      const target = e.target;
      if (target && trapContainer.contains(target)) {
        lastFocusAfterMounted = target;
      } else {
        tryFocus(lastFocusAfterMounted, true);
      }
    };
    const onFocusOut = (e) => {
      const trapContainer = unref(forwardRef);
      if (focusLayer.paused || !trapContainer)
        return;
      if (!trapContainer.contains(e.relatedTarget)) {
        tryFocus(lastFocusAfterMounted, true);
      }
    };
    const cleanupDocumentListeners = () => {
      document.removeEventListener("focusin", onFocusIn);
      document.removeEventListener("focusout", onFocusOut);
    };
    onMounted(() => {
      const trapContainer = unref(forwardRef);
      if (trapContainer) {
        focusableStack.push(focusLayer);
        const prevFocusedElement = document.activeElement;
        lastFocusBeforeMounted = prevFocusedElement;
        const isPrevFocusContained = trapContainer.contains(prevFocusedElement);
        if (!isPrevFocusContained) {
          const mountEvent = new Event(FOCUS_ON_MOUNT, FOCUS_ON_MOUNT_OPTS);
          trapContainer.addEventListener(FOCUS_ON_MOUNT, focusOnMount);
          trapContainer.dispatchEvent(mountEvent);
          if (!mountEvent.defaultPrevented) {
            nextTick(() => {
              focusFirstDescendant(obtainAllFocusableElements(trapContainer), true);
              if (document.activeElement === prevFocusedElement) {
                tryFocus(trapContainer);
              }
            });
          }
        }
      }
      watch(() => props.trapped, (trapped) => {
        if (trapped) {
          document.addEventListener("focusin", onFocusIn);
          document.addEventListener("focusout", onFocusOut);
        } else {
          cleanupDocumentListeners();
        }
      }, { immediate: true });
    });
    onBeforeUnmount(() => {
      cleanupDocumentListeners();
      const trapContainer = unref(forwardRef);
      if (trapContainer) {
        trapContainer.removeEventListener(FOCUS_ON_MOUNT, focusOnMount);
        const unmountEvent = new Event(FOCUS_ON_UNMOUNT, FOCUS_ON_MOUNT_OPTS);
        trapContainer.addEventListener(FOCUS_ON_UNMOUNT, focusOnUnmount);
        trapContainer.dispatchEvent(unmountEvent);
        if (!unmountEvent.defaultPrevented) {
          tryFocus(lastFocusBeforeMounted != null ? lastFocusBeforeMounted : document.body, true);
        }
        trapContainer.removeEventListener(FOCUS_ON_UNMOUNT, focusOnMount);
        focusableStack.remove(focusLayer);
      }
    });
    return {
      focusTrapRef,
      forwardRef,
      onKeydown
    };
  }
});
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return renderSlot(_ctx.$slots, "default");
}
var ElFocusTrap = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__file", "/Users/linxinjian/work/element-plus/packages/components/focus-trap/src/focus-trap.vue"]]);

export { ElFocusTrap as default };
//# sourceMappingURL=focus-trap.mjs.map
