UNPKG

grommet

Version:

focus on the essential experience

216 lines (163 loc) 6.36 kB
"use strict"; exports.__esModule = true; exports.isNodeBeforeScroll = exports.isNodeAfterScroll = exports.findVisibleParent = exports.makeNodeUnfocusable = exports.makeNodeFocusable = exports.copyAttribute = exports.setTabIndex = exports.setFocusWithoutScroll = exports.getNewContainer = exports.getBodyChildElements = exports.getFirstFocusableDescendant = exports.findScrollParents = exports.filterByFocusable = void 0; var filterByFocusable = function filterByFocusable(elements) { return Array.prototype.filter.call(elements || [], function (element) { var currentTag = element.tagName.toLowerCase(); var validTags = /(svg|a|area|input|select|textarea|button|iframe|div)$/; var isValidTag = currentTag.match(validTags) && element.focus; if (currentTag === 'a') { return isValidTag && element.childNodes.length > 0 && element.getAttribute('href'); } if (currentTag === 'svg' || currentTag === 'div') { return isValidTag && element.hasAttribute('tabindex') && element.getAttribute('tabindex') !== '-1'; } return isValidTag; }); }; exports.filterByFocusable = filterByFocusable; var findScrollParents = function findScrollParents(element, horizontal) { var result = []; if (element) { var parent = element.parentNode; while (parent && parent.getBoundingClientRect) { var rect = parent.getBoundingClientRect(); // 10px is to account for borders and scrollbars in a lazy way if (horizontal) { if (rect.width && parent.scrollWidth > rect.width + 10) { result.push(parent); } } else if (rect.height && parent.scrollHeight > rect.height + 10) { result.push(parent); } parent = parent.parentNode; } // last scrollable element will be the document // if nothing else is scrollable in the page if (result.length === 0) { result.push(document); } else if (result[0].tagName.toLowerCase() === 'body') { result.length = 0; result.push(document); } } return result; }; exports.findScrollParents = findScrollParents; var getFirstFocusableDescendant = function getFirstFocusableDescendant(element) { var children = element.getElementsByTagName('*'); for (var i = 0; i < children.length; i += 1) { var child = children[i]; var tagName = child.tagName.toLowerCase(); if (tagName === 'input' || tagName === 'select') { return child; } } return undefined; }; exports.getFirstFocusableDescendant = getFirstFocusableDescendant; var getBodyChildElements = function getBodyChildElements() { var excludeMatch = /^(script|link)$/i; var children = []; [].forEach.call(document.body.children, function (node) { if (!excludeMatch.test(node.tagName)) { children.push(node); } }); return children; }; exports.getBodyChildElements = getBodyChildElements; var getNewContainer = function getNewContainer() { // setup DOM var container = document.createElement('div'); document.body.appendChild(container); return container; }; exports.getNewContainer = getNewContainer; var setFocusWithoutScroll = function setFocusWithoutScroll(element) { var x = window.scrollX; var y = window.scrollY; element.focus(); window.scrollTo(x, y); }; exports.setFocusWithoutScroll = setFocusWithoutScroll; var setTabIndex = function setTabIndex(tabIndex) { return function (element) { element.setAttribute('tabindex', tabIndex); }; }; exports.setTabIndex = setTabIndex; var copyAttribute = function copyAttribute(source) { return function (target) { return function (element) { element.setAttribute(target, element.getAttribute(source)); }; }; }; exports.copyAttribute = copyAttribute; var deleteAttribute = function deleteAttribute(attribute) { return function (element) { return element.removeAttribute(attribute); }; }; var unsetTabIndex = setTabIndex(-1); var saveTabIndex = copyAttribute('tabindex')('data-g-tabindex'); var restoreTabIndex = copyAttribute('data-g-tabindex')('tabindex'); var deleteTabIndex = deleteAttribute('tabindex'); var deleteTabIndexCopy = deleteAttribute('data-g-tabindex'); var makeNodeFocusable = function makeNodeFocusable(node) { // do not touch aria live containers so that announcements work if (!node.hasAttribute('aria-live')) { node.setAttribute('aria-hidden', false); // allow children to receive focus again filterByFocusable(node.getElementsByTagName('*')).forEach(function (child) { if (child.hasAttribute('data-g-tabindex')) { restoreTabIndex(child); } else { deleteTabIndex(child); } deleteTabIndexCopy(child); }); } }; exports.makeNodeFocusable = makeNodeFocusable; var makeNodeUnfocusable = function makeNodeUnfocusable(node) { // do not touch aria live containers so that announcements work if (!node.hasAttribute('aria-live')) { node.setAttribute('aria-hidden', true); // prevent children to receive focus filterByFocusable(node.getElementsByTagName('*')).forEach(function (child) { if (child.hasAttribute('tabindex')) { saveTabIndex(child); } unsetTabIndex(child); }); } }; exports.makeNodeUnfocusable = makeNodeUnfocusable; var findVisibleParent = function findVisibleParent(element) { if (element) { return element.offsetParent ? element : findVisibleParent(element.parentElement) || element; } return undefined; }; exports.findVisibleParent = findVisibleParent; var isNodeAfterScroll = function isNodeAfterScroll(node, target) { if (target === void 0) { target = window; } var _node$getBoundingClie = node.getBoundingClientRect(), bottom = _node$getBoundingClie.bottom; var _target$getBoundingCl = target.getBoundingClientRect(), height = _target$getBoundingCl.height, top = _target$getBoundingCl.top; return bottom >= top + height; }; exports.isNodeAfterScroll = isNodeAfterScroll; var isNodeBeforeScroll = function isNodeBeforeScroll(node, target) { if (target === void 0) { target = window; } var _node$getBoundingClie2 = node.getBoundingClientRect(), top = _node$getBoundingClie2.top; var _target$getBoundingCl2 = target.getBoundingClientRect(), targetTop = _target$getBoundingCl2.top; return top <= targetTop; }; exports.isNodeBeforeScroll = isNodeBeforeScroll;