@momentum-ui/react-collaboration
Version:
Cisco Momentum UI Framework for React Collaboration Applications
136 lines • 5.23 kB
JavaScript
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