office-ui-fabric-react
Version:
Reusable React components for building experiences for Office 365.
130 lines (128 loc) • 5.78 kB
JavaScript
/* 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