UNPKG

@momentum-ui/react-collaboration

Version:

Cisco Momentum UI Framework for React Collaboration Applications

136 lines 5.23 kB
import { getKeyboardFocusableElements } from '../../utils/navigation'; import capitalize from 'lodash/capitalize'; import { orderElementsByDistance } from './SpatialNavigationProvider.utils'; /** * Track focusable elements in the app and move the focus to the next focusable element. */ var SpatialNavigation = /** @class */ (function () { function SpatialNavigation(goBackHandler) { var _this = this; /** Root element */ this.root = document.body; /** * Mutation Observer callback */ this.activeElementObserverCallback = function () { var currentActiveElement = _this.getActiveElement(); if (!currentActiveElement || !currentActiveElement.isConnected) { _this.initActiveElement(); } }; this.activeElementObserver = new MutationObserver(this.activeElementObserverCallback); this.goBackHandler = goBackHandler; } /** * Initialize the spatial navigation. */ SpatialNavigation.prototype.initActiveElement = function () { var elements = getKeyboardFocusableElements(this.root); this.findActiveElement(elements); }; /** * Update the list of the focusable elements in the app. */ SpatialNavigation.prototype.findActiveElement = function (elements) { var currentActiveElement = this.getActiveElement(); if (!currentActiveElement || !elements.includes(currentActiveElement)) { var nextActiveElement = elements[0]; if (nextActiveElement) { this.setActiveElementAndFocus(nextActiveElement); } } }; /** * Focus the next element in the given direction. * * @param direction User pressed arrow key. */ SpatialNavigation.prototype.focusNext = function (direction) { var _a, _b; var elements = getKeyboardFocusableElements(this.root); this.findActiveElement(elements); var currentActiveElement = this.getActiveElement(); if (!currentActiveElement) { return; } if (currentActiveElement !== document.activeElement) { return currentActiveElement.focus(); } // Check if the current active element has instruction to find the next element var nextElementSelector = currentActiveElement.dataset["spatial".concat(capitalize(direction))]; if (nextElementSelector) { var nextElement = this.root.querySelector(nextElementSelector); if (nextElement) { return this.setActiveElementAndFocus(nextElement); } } // Find the closest element in the given direction var results = orderElementsByDistance(currentActiveElement, elements, direction); var nextActiveElement = (_b = (_a = results[0]) === null || _a === void 0 ? void 0 : _a.element) !== null && _b !== void 0 ? _b : currentActiveElement; if (nextActiveElement) { this.setActiveElementAndFocus(nextActiveElement); } }; /** * Handle back action * * Either trigger click on goBack element if any * otherwise call default go back handler */ SpatialNavigation.prototype.goBack = function () { var _a; var goBackElement = getKeyboardFocusableElements(this.root).find(function (el) { return el.dataset.spatialGoBack; }); if (goBackElement) { goBackElement.click(); } else { (_a = this.goBackHandler) === null || _a === void 0 ? void 0 : _a.call(this); } }; /** * Set the active element. * * Also connect star tracking the active element. * @param element */ SpatialNavigation.prototype.setActiveElement = function (element) { var _a; if (element === ((_a = this.activeElement) === null || _a === void 0 ? void 0 : _a.deref())) return; this.activeElementObserver.disconnect(); if (element) { this.activeElement = new WeakRef(element); this.activeElementObserver.observe(element.parentElement, { childList: true }); } else { this.activeElement = undefined; } }; /** * Set the active element and call .focus() on it * @param element */ SpatialNavigation.prototype.setActiveElementAndFocus = function (element) { this.setActiveElement(element); // Focus the element asynchronously to make sure all calculations are done before focusing queueMicrotask(function () { return element.focus(); }); }; /** * Get active (focused) element */ SpatialNavigation.prototype.getActiveElement = function () { var _a; return (_a = this.activeElement) === null || _a === void 0 ? void 0 : _a.deref(); }; /** * Cleanup resources */ SpatialNavigation.prototype.dispose = function () { this.activeElement = undefined; this.activeElementObserver.disconnect(); this.activeElementObserver = undefined; }; return SpatialNavigation; }()); export { SpatialNavigation }; //# sourceMappingURL=SpatialNavigation.js.map