UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

130 lines (128 loc) 5.78 kB
/* tslint:disable:no-string-literal */ "use strict"; var dom_1 = require('./dom'); var IS_FOCUSABLE_ATTRIBUTE = 'data-is-focusable'; var IS_VISIBLE_ATTRIBUTE = 'data-is-visible'; var FOCUSZONE_ID_ATTRIBUTE = 'data-focuszone-id'; function getFirstFocusable(rootElement, currentElement, includeElementsInFocusZones) { return getNextElement(rootElement, currentElement, true, false, false, includeElementsInFocusZones); } exports.getFirstFocusable = getFirstFocusable; function getLastFocusable(rootElement, currentElement, includeElementsInFocusZones) { return getPreviousElement(rootElement, currentElement, true, false, true, includeElementsInFocusZones); } exports.getLastFocusable = getLastFocusable; /** * Attempts to focus the first focusable element that is a child or child's child of the rootElement. * @return True if focus was set, false if it was not. * @param {HTMLElement} rootElement - element to start the search for a focusable child. */ function focusFirstChild(rootElement) { var element = getNextElement(rootElement, rootElement, true, false, false, true); if (element) { element.focus(); return true; } return false; } exports.focusFirstChild = focusFirstChild; /** Traverse to find the previous element. */ function getPreviousElement(rootElement, currentElement, checkNode, suppressParentTraversal, traverseChildren, includeElementsInFocusZones) { if (!currentElement || currentElement === rootElement) { return null; } var isCurrentElementVisible = isElementVisible(currentElement); // Check its children. if (traverseChildren && (includeElementsInFocusZones || !isElementFocusZone(currentElement)) && isCurrentElementVisible) { var childMatch = getPreviousElement(rootElement, currentElement.lastElementChild, true, true, true, includeElementsInFocusZones); if (childMatch) { return childMatch; } } // Check the current node, if it's not the first traversal. if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement)) { return currentElement; } // Check its previous sibling. var siblingMatch = getPreviousElement(rootElement, currentElement.previousElementSibling, true, true, true, includeElementsInFocusZones); if (siblingMatch) { return siblingMatch; } // Check its parent. if (!suppressParentTraversal) { return getPreviousElement(rootElement, currentElement.parentElement, true, false, false, includeElementsInFocusZones); } return null; } exports.getPreviousElement = getPreviousElement; /** Traverse to find the next focusable element. */ function getNextElement(rootElement, currentElement, checkNode, suppressParentTraversal, suppressChildTraversal, includeElementsInFocusZones) { if (!currentElement || (currentElement === rootElement && suppressChildTraversal)) { return null; } var isCurrentElementVisible = isElementVisible(currentElement); // Check the current node, if it's not the first traversal. if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement)) { return currentElement; } // Check its children. if (!suppressChildTraversal && isCurrentElementVisible && (includeElementsInFocusZones || !isElementFocusZone(currentElement))) { var childMatch = getNextElement(rootElement, currentElement.firstElementChild, true, true, false, includeElementsInFocusZones); if (childMatch) { return childMatch; } } if (currentElement === rootElement) { return null; } // Check its sibling. var siblingMatch = getNextElement(rootElement, currentElement.nextElementSibling, true, true, false, includeElementsInFocusZones); if (siblingMatch) { return siblingMatch; } if (!suppressParentTraversal) { return getNextElement(rootElement, currentElement.parentElement, false, false, true, includeElementsInFocusZones); } return null; } exports.getNextElement = getNextElement; function isElementVisible(element) { // If the element is not valid, return false. if (!element || !element.getAttribute) { return false; } var visibilityAttribute = element.getAttribute(IS_VISIBLE_ATTRIBUTE); // If the element is explicitly marked with the visibility attribute, return that value as boolean. if (visibilityAttribute !== null && visibilityAttribute !== undefined) { return visibilityAttribute === 'true'; } // Fallback to other methods of determining actual visibility. return (element.offsetHeight !== 0 || element.offsetParent !== null || element.isVisible === true); // used as a workaround for testing. } exports.isElementVisible = isElementVisible; function isElementTabbable(element) { return (!!element && (element.tagName === 'A' || (element.tagName === 'BUTTON' && !element.disabled) || (element.tagName === 'INPUT' && !element.disabled) || (element.tagName === 'TEXTAREA' && !element.disabled) || (element.getAttribute && element.getAttribute(IS_FOCUSABLE_ATTRIBUTE) === 'true'))); } exports.isElementTabbable = isElementTabbable; function isElementFocusZone(element) { return element && !!element.getAttribute(FOCUSZONE_ID_ATTRIBUTE); } exports.isElementFocusZone = isElementFocusZone; function doesElementContainFocus(element) { var currentActiveElement = dom_1.getDocument(element).activeElement; if (currentActiveElement && dom_1.elementContains(element, currentActiveElement)) { return true; } return false; } exports.doesElementContainFocus = doesElementContainFocus; //# sourceMappingURL=focus.js.map