{"version":3,"file":"useTrapFocus.mjs","sources":["../../../../src/composables/useTrapFocus.ts"],"sourcesContent":["import { useAppGlobal } from './useAppGlobal'\nimport { useDocument } from './useDocument'\nimport { useWindow } from './useWindow'\n\nconst FOCUSABLE_ELEMENTS_SELECTOR = ':where(a, button, input, textarea, select):not([disabled]), *[tabindex]'\n\nexport const useTrapFocus = () => {\n  const document = useDocument()\n  const window = useWindow()\n\n  const trapInEl = useAppGlobal<null | HTMLElement>('trapInEl', null)\n\n  let focusableElements: HTMLElement[] = []\n  let firstFocusableElement: HTMLElement | null = null\n  let lastFocusableElement: HTMLElement | null = null\n\n  const isFocusIn = (evt: Event) => {\n    return trapInEl.value?.contains(evt.target as Node) || false\n  }\n\n  const focusFirstElement = () => {\n    firstFocusableElement?.focus()\n  }\n  const focusLastElement = () => {\n    lastFocusableElement?.focus()\n  }\n\n  const onKeydown = (evt: KeyboardEvent) => {\n    const isTabPressed = evt.code === 'Tab'\n    const isShiftPressed = evt.shiftKey\n\n    if (!isTabPressed) {\n      return\n    }\n\n    if (!isFocusIn(evt)) {\n      evt.preventDefault()\n      isShiftPressed ? focusLastElement() : focusFirstElement()\n\n      return\n    }\n\n    if (document.value?.activeElement === lastFocusableElement && !isShiftPressed) {\n      evt.preventDefault()\n      focusFirstElement()\n      return\n    }\n\n    if (document.value?.activeElement === firstFocusableElement && isShiftPressed) {\n      evt.preventDefault()\n      focusLastElement()\n    }\n  }\n\n  const trapFocusIn = (el: HTMLElement) => {\n    trapInEl.value = el\n\n    freeFocus()\n    trapFocus()\n  }\n\n  const trapFocus = () => {\n    if (!trapInEl.value) {\n      return\n    }\n\n    focusableElements = Array.from(trapInEl.value.querySelectorAll(FOCUSABLE_ELEMENTS_SELECTOR))\n    firstFocusableElement = focusableElements[0]\n    lastFocusableElement = focusableElements[focusableElements.length - 1]\n\n    window.value?.addEventListener('keydown', onKeydown)\n  }\n  const freeFocus = () => {\n    focusableElements = []\n    firstFocusableElement = null\n    lastFocusableElement = null\n\n    window.value?.removeEventListener('keydown', onKeydown)\n  }\n\n  return {\n    trapFocus,\n    freeFocus,\n    trapFocusIn,\n  }\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,8BAA8B;AAE7B,MAAM,eAAe,MAAM;AAChC,QAAM,WAAW;AACjB,QAAM,SAAS;AAET,QAAA,WAAW,aAAiC,YAAY,IAAI;AAElE,MAAI,oBAAmC,CAAA;AACvC,MAAI,wBAA4C;AAChD,MAAI,uBAA2C;AAEzC,QAAA,YAAY,CAAC,QAAe;;AAChC,aAAO,cAAS,UAAT,mBAAgB,SAAS,IAAI,YAAmB;AAAA,EAAA;AAGzD,QAAM,oBAAoB,MAAM;AAC9B,mEAAuB;AAAA,EAAM;AAE/B,QAAM,mBAAmB,MAAM;AAC7B,iEAAsB;AAAA,EAAM;AAGxB,QAAA,YAAY,CAAC,QAAuB;;AAClC,UAAA,eAAe,IAAI,SAAS;AAClC,UAAM,iBAAiB,IAAI;AAE3B,QAAI,CAAC,cAAc;AACjB;AAAA,IACF;AAEI,QAAA,CAAC,UAAU,GAAG,GAAG;AACnB,UAAI,eAAe;AACF,uBAAA,qBAAqB;AAEtC;AAAA,IACF;AAEA,UAAI,cAAS,UAAT,mBAAgB,mBAAkB,wBAAwB,CAAC,gBAAgB;AAC7E,UAAI,eAAe;AACD;AAClB;AAAA,IACF;AAEA,UAAI,cAAS,UAAT,mBAAgB,mBAAkB,yBAAyB,gBAAgB;AAC7E,UAAI,eAAe;AACF;IACnB;AAAA,EAAA;AAGI,QAAA,cAAc,CAAC,OAAoB;AACvC,aAAS,QAAQ;AAEP;AACA;EAAA;AAGZ,QAAM,YAAY,MAAM;;AAClB,QAAA,CAAC,SAAS,OAAO;AACnB;AAAA,IACF;AAEA,wBAAoB,MAAM,KAAK,SAAS,MAAM,iBAAiB,2BAA2B,CAAC;AAC3F,4BAAwB,kBAAkB,CAAC;AACpB,2BAAA,kBAAkB,kBAAkB,SAAS,CAAC;AAE9D,iBAAA,UAAA,mBAAO,iBAAiB,WAAW;AAAA,EAAS;AAErD,QAAM,YAAY,MAAM;;AACtB,wBAAoB,CAAA;AACI,4BAAA;AACD,2BAAA;AAEhB,iBAAA,UAAA,mBAAO,oBAAoB,WAAW;AAAA,EAAS;AAGjD,SAAA;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}