123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', { value: true });
- var vue = require('vue');
- var lodashUnified = require('lodash-unified');
- var utils = require('./utils.js');
- var tokens = require('./tokens.js');
- var pluginVue_exportHelper = require('../../../_virtual/plugin-vue_export-helper.js');
- var index = require('../../../hooks/use-escape-keydown/index.js');
- var aria = require('../../../constants/aria.js');
- var shared = require('@vue/shared');
- const _sfc_main = vue.defineComponent({
- name: "ElFocusTrap",
- inheritAttrs: false,
- props: {
- loop: Boolean,
- trapped: Boolean,
- focusTrapEl: Object,
- focusStartEl: {
- type: [Object, String],
- default: "first"
- }
- },
- emits: [
- tokens.ON_TRAP_FOCUS_EVT,
- tokens.ON_RELEASE_FOCUS_EVT,
- "focusin",
- "focusout",
- "focusout-prevented",
- "release-requested"
- ],
- setup(props, { emit }) {
- const forwardRef = vue.ref();
- let lastFocusBeforeTrapped;
- let lastFocusAfterTrapped;
- const { focusReason } = utils.useFocusReason();
- index.useEscapeKeydown((event) => {
- if (props.trapped && !focusLayer.paused) {
- emit("release-requested", event);
- }
- });
- 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 { code, altKey, ctrlKey, metaKey, currentTarget, shiftKey } = e;
- const { loop } = props;
- const isTabbing = code === aria.EVENT_CODE.tab && !altKey && !ctrlKey && !metaKey;
- const currentFocusingEl = document.activeElement;
- if (isTabbing && currentFocusingEl) {
- const container = currentTarget;
- const [first, last] = utils.getEdges(container);
- const isTabbable = first && last;
- if (!isTabbable) {
- if (currentFocusingEl === container) {
- const focusoutPreventedEvent = utils.createFocusOutPreventedEvent({
- focusReason: focusReason.value
- });
- emit("focusout-prevented", focusoutPreventedEvent);
- if (!focusoutPreventedEvent.defaultPrevented) {
- e.preventDefault();
- }
- }
- } else {
- if (!shiftKey && currentFocusingEl === last) {
- const focusoutPreventedEvent = utils.createFocusOutPreventedEvent({
- focusReason: focusReason.value
- });
- emit("focusout-prevented", focusoutPreventedEvent);
- if (!focusoutPreventedEvent.defaultPrevented) {
- e.preventDefault();
- if (loop)
- utils.tryFocus(first, true);
- }
- } else if (shiftKey && [first, container].includes(currentFocusingEl)) {
- const focusoutPreventedEvent = utils.createFocusOutPreventedEvent({
- focusReason: focusReason.value
- });
- emit("focusout-prevented", focusoutPreventedEvent);
- if (!focusoutPreventedEvent.defaultPrevented) {
- e.preventDefault();
- if (loop)
- utils.tryFocus(last, true);
- }
- }
- }
- }
- };
- vue.provide(tokens.FOCUS_TRAP_INJECTION_KEY, {
- focusTrapRef: forwardRef,
- onKeydown
- });
- vue.watch(() => props.focusTrapEl, (focusTrapEl) => {
- if (focusTrapEl) {
- forwardRef.value = focusTrapEl;
- }
- }, { immediate: true });
- vue.watch([forwardRef], ([forwardRef2], [oldForwardRef]) => {
- if (forwardRef2) {
- forwardRef2.addEventListener("keydown", onKeydown);
- forwardRef2.addEventListener("focusin", onFocusIn);
- forwardRef2.addEventListener("focusout", onFocusOut);
- }
- if (oldForwardRef) {
- oldForwardRef.removeEventListener("keydown", onKeydown);
- oldForwardRef.removeEventListener("focusin", onFocusIn);
- oldForwardRef.removeEventListener("focusout", onFocusOut);
- }
- });
- const trapOnFocus = (e) => {
- emit(tokens.ON_TRAP_FOCUS_EVT, e);
- };
- const releaseOnFocus = (e) => emit(tokens.ON_RELEASE_FOCUS_EVT, e);
- const onFocusIn = (e) => {
- const trapContainer = vue.unref(forwardRef);
- if (!trapContainer)
- return;
- const target = e.target;
- const relatedTarget = e.relatedTarget;
- const isFocusedInTrap = target && trapContainer.contains(target);
- if (!props.trapped) {
- const isPrevFocusedInTrap = relatedTarget && trapContainer.contains(relatedTarget);
- if (!isPrevFocusedInTrap) {
- lastFocusBeforeTrapped = relatedTarget;
- }
- }
- if (isFocusedInTrap)
- emit("focusin", e);
- if (focusLayer.paused)
- return;
- if (props.trapped) {
- if (isFocusedInTrap) {
- lastFocusAfterTrapped = target;
- } else {
- utils.tryFocus(lastFocusAfterTrapped, true);
- }
- }
- };
- const onFocusOut = (e) => {
- const trapContainer = vue.unref(forwardRef);
- if (focusLayer.paused || !trapContainer)
- return;
- if (props.trapped) {
- const relatedTarget = e.relatedTarget;
- if (!lodashUnified.isNil(relatedTarget) && !trapContainer.contains(relatedTarget)) {
- setTimeout(() => {
- if (!focusLayer.paused && props.trapped) {
- const focusoutPreventedEvent = utils.createFocusOutPreventedEvent({
- focusReason: focusReason.value
- });
- emit("focusout-prevented", focusoutPreventedEvent);
- if (!focusoutPreventedEvent.defaultPrevented) {
- utils.tryFocus(lastFocusAfterTrapped, true);
- }
- }
- }, 0);
- }
- } else {
- const target = e.target;
- const isFocusedInTrap = target && trapContainer.contains(target);
- if (!isFocusedInTrap)
- emit("focusout", e);
- }
- };
- async function startTrap() {
- await vue.nextTick();
- const trapContainer = vue.unref(forwardRef);
- if (trapContainer) {
- utils.focusableStack.push(focusLayer);
- const prevFocusedElement = trapContainer.contains(document.activeElement) ? lastFocusBeforeTrapped : document.activeElement;
- lastFocusBeforeTrapped = prevFocusedElement;
- const isPrevFocusContained = trapContainer.contains(prevFocusedElement);
- if (!isPrevFocusContained) {
- const focusEvent = new Event(tokens.FOCUS_AFTER_TRAPPED, tokens.FOCUS_AFTER_TRAPPED_OPTS);
- trapContainer.addEventListener(tokens.FOCUS_AFTER_TRAPPED, trapOnFocus);
- trapContainer.dispatchEvent(focusEvent);
- if (!focusEvent.defaultPrevented) {
- vue.nextTick(() => {
- let focusStartEl = props.focusStartEl;
- if (!shared.isString(focusStartEl)) {
- utils.tryFocus(focusStartEl);
- if (document.activeElement !== focusStartEl) {
- focusStartEl = "first";
- }
- }
- if (focusStartEl === "first") {
- utils.focusFirstDescendant(utils.obtainAllFocusableElements(trapContainer), true);
- }
- if (document.activeElement === prevFocusedElement || focusStartEl === "container") {
- utils.tryFocus(trapContainer);
- }
- });
- }
- }
- }
- }
- function stopTrap() {
- const trapContainer = vue.unref(forwardRef);
- if (trapContainer) {
- trapContainer.removeEventListener(tokens.FOCUS_AFTER_TRAPPED, trapOnFocus);
- const releasedEvent = new CustomEvent(tokens.FOCUS_AFTER_RELEASED, {
- ...tokens.FOCUS_AFTER_TRAPPED_OPTS,
- detail: {
- focusReason: focusReason.value
- }
- });
- trapContainer.addEventListener(tokens.FOCUS_AFTER_RELEASED, releaseOnFocus);
- trapContainer.dispatchEvent(releasedEvent);
- if (!releasedEvent.defaultPrevented && (focusReason.value == "keyboard" || !utils.isFocusCausedByUserEvent() || trapContainer.contains(document.activeElement))) {
- utils.tryFocus(lastFocusBeforeTrapped != null ? lastFocusBeforeTrapped : document.body);
- }
- trapContainer.removeEventListener(tokens.FOCUS_AFTER_RELEASED, releaseOnFocus);
- utils.focusableStack.remove(focusLayer);
- }
- }
- vue.onMounted(() => {
- if (props.trapped) {
- startTrap();
- }
- vue.watch(() => props.trapped, (trapped) => {
- if (trapped) {
- startTrap();
- } else {
- stopTrap();
- }
- });
- });
- vue.onBeforeUnmount(() => {
- if (props.trapped) {
- stopTrap();
- }
- if (forwardRef.value) {
- forwardRef.value.removeEventListener("keydown", onKeydown);
- forwardRef.value.removeEventListener("focusin", onFocusIn);
- forwardRef.value.removeEventListener("focusout", onFocusOut);
- forwardRef.value = void 0;
- }
- });
- return {
- onKeydown
- };
- }
- });
- function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
- return vue.renderSlot(_ctx.$slots, "default", { handleKeydown: _ctx.onKeydown });
- }
- var ElFocusTrap = /* @__PURE__ */ pluginVue_exportHelper["default"](_sfc_main, [["render", _sfc_render], ["__file", "focus-trap.vue"]]);
- exports["default"] = ElFocusTrap;
- //# sourceMappingURL=focus-trap.js.map
|