{"version":3,"file":"focus.js","sourceRoot":"../src/","sources":["focus.ts"],"names":[],"mappings":"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","sourcesContent":["import { elementContainsAttribute } from './dom/elementContainsAttribute';\nimport { elementContains } from './dom/elementContains';\nimport { getParent } from './dom/getParent';\nimport { getWindow } from './dom/getWindow';\nimport { getDocument } from './dom/getDocument';\n\nconst IS_FOCUSABLE_ATTRIBUTE = 'data-is-focusable';\nconst IS_VISIBLE_ATTRIBUTE = 'data-is-visible';\nconst FOCUSZONE_ID_ATTRIBUTE = 'data-focuszone-id';\nconst FOCUSZONE_SUB_ATTRIBUTE = 'data-is-sub-focuszone';\n\n/**\n * Gets the first focusable element.\n *\n * @public\n */\nexport function getFirstFocusable(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement,\n  includeElementsInFocusZones?: boolean,\n): HTMLElement | null {\n  return getNextElement(\n    rootElement,\n    currentElement,\n    true /*checkNode*/,\n    false /*suppressParentTraversal*/,\n    false /*suppressChildTraversal*/,\n    includeElementsInFocusZones,\n  );\n}\n\n/**\n * Gets the last focusable element.\n *\n * @public\n */\nexport function getLastFocusable(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement,\n  includeElementsInFocusZones?: boolean,\n): HTMLElement | null {\n  return getPreviousElement(\n    rootElement,\n    currentElement,\n    true /*checkNode*/,\n    false /*suppressParentTraversal*/,\n    true /*traverseChildren*/,\n    includeElementsInFocusZones,\n  );\n}\n\n/**\n * Gets the first tabbable element. (The difference between focusable and tabbable is that tabbable elements are\n * focusable elements that also have tabIndex != -1.)\n * @param rootElement - The parent element to search beneath.\n * @param currentElement - The descendant of rootElement to start the search at.  This element is the first one checked,\n * and iteration continues forward.  Typical use passes rootElement.firstChild.\n * @param includeElementsInFocusZones - true if traversal should go into FocusZone descendants.\n * @param checkNode - Include currentElement in search when true. Defaults to true.\n * @public\n */\nexport function getFirstTabbable(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement,\n  includeElementsInFocusZones?: boolean,\n  checkNode: boolean = true,\n): HTMLElement | null {\n  return getNextElement(\n    rootElement,\n    currentElement,\n    checkNode,\n    false /*suppressParentTraversal*/,\n    false /*suppressChildTraversal*/,\n    includeElementsInFocusZones,\n    false /*allowFocusRoot*/,\n    true /*tabbable*/,\n  );\n}\n\n/**\n * Gets the last tabbable element. (The difference between focusable and tabbable is that tabbable elements are\n * focusable elements that also have tabIndex != -1.)\n * @param rootElement - The parent element to search beneath.\n * @param currentElement - The descendant of rootElement to start the search at.  This element is the first one checked,\n * and iteration continues in reverse.  Typical use passes rootElement.lastChild.\n * @param includeElementsInFocusZones - true if traversal should go into FocusZone descendants.\n * @param checkNode - Include currentElement in search when true. Defaults to true.\n * @public\n */\nexport function getLastTabbable(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement,\n  includeElementsInFocusZones?: boolean,\n  checkNode: boolean = true,\n): HTMLElement | null {\n  return getPreviousElement(\n    rootElement,\n    currentElement,\n    checkNode,\n    false /*suppressParentTraversal*/,\n    true /*traverseChildren*/,\n    includeElementsInFocusZones,\n    false /*allowFocusRoot*/,\n    true /*tabbable*/,\n  );\n}\n\n/**\n * Attempts to focus the first focusable element that is a child or child's child of the rootElement.\n *\n * @public\n * @param rootElement - Element to start the search for a focusable child.\n * @returns True if focus was set, false if it was not.\n */\nexport function focusFirstChild(rootElement: HTMLElement): boolean {\n  let element: HTMLElement | null = getNextElement(rootElement, rootElement, true, false, false, true);\n\n  if (element) {\n    focusAsync(element);\n    return true;\n  }\n  return false;\n}\n\n/**\n * Traverse to find the previous element.\n * If tabbable is true, the element must have tabIndex != -1.\n *\n * @public\n */\nexport function getPreviousElement(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement | null,\n  checkNode?: boolean,\n  suppressParentTraversal?: boolean,\n  traverseChildren?: boolean,\n  includeElementsInFocusZones?: boolean,\n  allowFocusRoot?: boolean,\n  tabbable?: boolean,\n): HTMLElement | null {\n  if (!currentElement || (!allowFocusRoot && currentElement === rootElement)) {\n    return null;\n  }\n\n  let isCurrentElementVisible = isElementVisible(currentElement);\n\n  // Check its children.\n  if (\n    traverseChildren &&\n    isCurrentElementVisible &&\n    (includeElementsInFocusZones || !(isElementFocusZone(currentElement) || isElementFocusSubZone(currentElement)))\n  ) {\n    const childMatch = getPreviousElement(\n      rootElement,\n      currentElement.lastElementChild as HTMLElement,\n      true,\n      true,\n      true,\n      includeElementsInFocusZones,\n      allowFocusRoot,\n      tabbable,\n    );\n\n    if (childMatch) {\n      if ((tabbable && isElementTabbable(childMatch, true)) || !tabbable) {\n        return childMatch;\n      }\n\n      const childMatchSiblingMatch = getPreviousElement(\n        rootElement,\n        childMatch.previousElementSibling as HTMLElement,\n        true,\n        true,\n        true,\n        includeElementsInFocusZones,\n        allowFocusRoot,\n        tabbable,\n      );\n      if (childMatchSiblingMatch) {\n        return childMatchSiblingMatch;\n      }\n\n      let childMatchParent = childMatch.parentElement;\n\n      // At this point if we have not found any potential matches\n      // start looking at the rest of the subtree under the currentParent.\n      // NOTE: We do not want to recurse here because doing so could\n      // cause elements to get skipped.\n      while (childMatchParent && childMatchParent !== currentElement) {\n        const childMatchParentMatch = getPreviousElement(\n          rootElement,\n          childMatchParent.previousElementSibling as HTMLElement,\n          true,\n          true,\n          true,\n          includeElementsInFocusZones,\n          allowFocusRoot,\n          tabbable,\n        );\n\n        if (childMatchParentMatch) {\n          return childMatchParentMatch;\n        }\n\n        childMatchParent = childMatchParent.parentElement;\n      }\n    }\n  }\n\n  // Check the current node, if it's not the first traversal.\n  if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement, tabbable)) {\n    return currentElement;\n  }\n\n  // Check its previous sibling.\n  const siblingMatch = getPreviousElement(\n    rootElement,\n    currentElement.previousElementSibling as HTMLElement,\n    true,\n    true,\n    true,\n    includeElementsInFocusZones,\n    allowFocusRoot,\n    tabbable,\n  );\n\n  if (siblingMatch) {\n    return siblingMatch;\n  }\n\n  // Check its parent.\n  if (!suppressParentTraversal) {\n    return getPreviousElement(\n      rootElement,\n      currentElement.parentElement,\n      true,\n      false,\n      false,\n      includeElementsInFocusZones,\n      allowFocusRoot,\n      tabbable,\n    );\n  }\n\n  return null;\n}\n\n/**\n * Traverse to find the next focusable element.\n * If tabbable is true, the element must have tabIndex != -1.\n *\n * @public\n * @param checkNode - Include currentElement in search when true.\n */\nexport function getNextElement(\n  rootElement: HTMLElement,\n  currentElement: HTMLElement | null,\n  checkNode?: boolean,\n  suppressParentTraversal?: boolean,\n  suppressChildTraversal?: boolean,\n  includeElementsInFocusZones?: boolean,\n  allowFocusRoot?: boolean,\n  tabbable?: boolean,\n): HTMLElement | null {\n  if (!currentElement || (currentElement === rootElement && suppressChildTraversal && !allowFocusRoot)) {\n    return null;\n  }\n\n  let isCurrentElementVisible = isElementVisible(currentElement);\n\n  // Check the current node, if it's not the first traversal.\n  if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement, tabbable)) {\n    return currentElement;\n  }\n\n  // Check its children.\n  if (\n    !suppressChildTraversal &&\n    isCurrentElementVisible &&\n    (includeElementsInFocusZones || !(isElementFocusZone(currentElement) || isElementFocusSubZone(currentElement)))\n  ) {\n    const childMatch = getNextElement(\n      rootElement,\n      currentElement.firstElementChild as HTMLElement,\n      true,\n      true,\n      false,\n      includeElementsInFocusZones,\n      allowFocusRoot,\n      tabbable,\n    );\n\n    if (childMatch) {\n      return childMatch;\n    }\n  }\n\n  if (currentElement === rootElement) {\n    return null;\n  }\n\n  // Check its sibling.\n  const siblingMatch = getNextElement(\n    rootElement,\n    currentElement.nextElementSibling as HTMLElement,\n    true,\n    true,\n    false,\n    includeElementsInFocusZones,\n    allowFocusRoot,\n    tabbable,\n  );\n\n  if (siblingMatch) {\n    return siblingMatch;\n  }\n\n  if (!suppressParentTraversal) {\n    return getNextElement(\n      rootElement,\n      currentElement.parentElement,\n      false,\n      false,\n      true,\n      includeElementsInFocusZones,\n      allowFocusRoot,\n      tabbable,\n    );\n  }\n\n  return null;\n}\n\n/**\n * Determines if an element is visible.\n *\n * @public\n */\nexport function isElementVisible(element: HTMLElement | undefined | null): boolean {\n  // If the element is not valid, return false.\n  if (!element || !element.getAttribute) {\n    return false;\n  }\n\n  const visibilityAttribute = element.getAttribute(IS_VISIBLE_ATTRIBUTE);\n\n  // If the element is explicitly marked with the visibility attribute, return that value as boolean.\n  if (visibilityAttribute !== null && visibilityAttribute !== undefined) {\n    return visibilityAttribute === 'true';\n  }\n\n  // Fallback to other methods of determining actual visibility.\n  return (\n    element.offsetHeight !== 0 ||\n    element.offsetParent !== null ||\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    (element as any).isVisible === true\n  ); // used as a workaround for testing.\n}\n\n/**\n * Determines if an element can receive focus programmatically or via a mouse click.\n * If checkTabIndex is true, additionally checks to ensure the element can be focused with the tab key,\n * meaning tabIndex != -1.\n *\n * @public\n */\nexport function isElementTabbable(element: HTMLElement, checkTabIndex?: boolean): boolean {\n  // If this element is null or is disabled, it is not considered tabbable.\n  if (!element || (element as HTMLButtonElement).disabled) {\n    return false;\n  }\n\n  let tabIndex = 0;\n  let tabIndexAttributeValue = null;\n\n  if (element && element.getAttribute) {\n    tabIndexAttributeValue = element.getAttribute('tabIndex');\n\n    if (tabIndexAttributeValue) {\n      tabIndex = parseInt(tabIndexAttributeValue, 10);\n    }\n  }\n\n  let isFocusableAttribute = element.getAttribute ? element.getAttribute(IS_FOCUSABLE_ATTRIBUTE) : null;\n  let isTabIndexSet = tabIndexAttributeValue !== null && tabIndex >= 0;\n\n  const result =\n    !!element &&\n    isFocusableAttribute !== 'false' &&\n    (element.tagName === 'A' ||\n      element.tagName === 'BUTTON' ||\n      element.tagName === 'INPUT' ||\n      element.tagName === 'TEXTAREA' ||\n      element.tagName === 'SELECT' ||\n      isFocusableAttribute === 'true' ||\n      isTabIndexSet);\n\n  return checkTabIndex ? tabIndex !== -1 && result : result;\n}\n\n/**\n * Determines if a given element is a focus zone.\n *\n * @public\n */\nexport function isElementFocusZone(element?: HTMLElement): boolean {\n  return !!(element && element.getAttribute && !!element.getAttribute(FOCUSZONE_ID_ATTRIBUTE));\n}\n\n/**\n * Determines if a given element is a focus sub zone.\n *\n * @public\n */\nexport function isElementFocusSubZone(element?: HTMLElement): boolean {\n  return !!(element && element.getAttribute && element.getAttribute(FOCUSZONE_SUB_ATTRIBUTE) === 'true');\n}\n\n/**\n * Determines if an element, or any of its children, contain focus.\n *\n * @public\n */\nexport function doesElementContainFocus(element: HTMLElement): boolean {\n  let document = getDocument(element);\n  let currentActiveElement: HTMLElement | undefined = document && (document.activeElement as HTMLElement);\n  if (currentActiveElement && elementContains(element, currentActiveElement)) {\n    return true;\n  }\n  return false;\n}\n\n/**\n * Determines if an, or any of its ancestors, sepcificies that it doesn't want focus to wrap\n * @param element - element to start searching from\n * @param noWrapDataAttribute - the no wrap data attribute to match (either)\n * @returns true if focus should wrap, false otherwise\n */\nexport function shouldWrapFocus(\n  element: HTMLElement,\n  noWrapDataAttribute: 'data-no-vertical-wrap' | 'data-no-horizontal-wrap',\n): boolean {\n  return elementContainsAttribute(element, noWrapDataAttribute) === 'true' ? false : true;\n}\n\nlet targetToFocusOnNextRepaint: HTMLElement | { focus: () => void } | null | undefined = undefined;\n\n/**\n * Sets focus to an element asynchronously. The focus will be set at the next browser repaint,\n * meaning it won't cause any extra recalculations. If more than one focusAsync is called during one frame,\n * only the latest called focusAsync element will actually be focused\n * @param element - The element to focus\n */\nexport function focusAsync(element: HTMLElement | { focus: () => void } | undefined | null): void {\n  if (element) {\n    // An element was already queued to be focused, so replace that one with the new element\n    if (targetToFocusOnNextRepaint) {\n      targetToFocusOnNextRepaint = element;\n      return;\n    }\n\n    targetToFocusOnNextRepaint = element;\n\n    const win = getWindow(element as Element);\n\n    if (win) {\n      // element.focus() is a no-op if the element is no longer in the DOM, meaning this is always safe\n      win.requestAnimationFrame(() => {\n        const focusableElement = targetToFocusOnNextRepaint as HTMLElement | null;\n\n        // We are done focusing for this frame, so reset the queued focus element\n        targetToFocusOnNextRepaint = undefined;\n\n        if (focusableElement) {\n          if (focusableElement.getAttribute && focusableElement.getAttribute(IS_FOCUSABLE_ATTRIBUTE) === 'true') {\n            // Normally, a FocusZone would be responsible for setting the tabindex values on all its descendants.\n            // However, even this animation frame callback can pre-empt the rendering of a FocusZone's child elements,\n            // so it may be necessary to set the tabindex directly here.\n            if (!focusableElement.getAttribute('tabindex')) {\n              focusableElement.setAttribute('tabindex', '0');\n            }\n          }\n\n          focusableElement.focus();\n        }\n      });\n    }\n  }\n}\n\n/**\n * Finds the closest focusable element via an index path from a parent. See\n * `getElementIndexPath` for getting an index path from an element to a child.\n */\nexport function getFocusableByIndexPath(parent: HTMLElement, path: number[]): HTMLElement | undefined {\n  let element = parent;\n\n  for (const index of path) {\n    const nextChild = element.children[Math.min(index, element.children.length - 1)] as HTMLElement;\n\n    if (!nextChild) {\n      break;\n    }\n    element = nextChild;\n  }\n\n  element =\n    isElementTabbable(element) && isElementVisible(element)\n      ? element\n      : getNextElement(parent, element, true) || getPreviousElement(parent, element)!;\n\n  return element as HTMLElement;\n}\n\n/**\n * Finds the element index path from a parent element to a child element.\n *\n * If you had this node structure: \"A has children [B, C] and C has child D\",\n * the index path from A to D would be [1, 0], or `parent.chidren[1].children[0]`.\n */\nexport function getElementIndexPath(fromElement: HTMLElement, toElement: HTMLElement): number[] {\n  const path: number[] = [];\n\n  while (toElement && fromElement && toElement !== fromElement) {\n    const parent = getParent(toElement, true);\n\n    if (parent === null) {\n      return [];\n    }\n\n    path.unshift(Array.prototype.indexOf.call(parent.children, toElement));\n    toElement = parent;\n  }\n\n  return path;\n}\n"]}