- Updated all component headers and documentation
- Changed navbar and footer branding
- Updated homepage hero badge
- Modified page title in index.html
- Simplified footer text to 'Built with ❤️'
- Consistent V2 capitalization across all references
115 lines
3.9 KiB
JavaScript
115 lines
3.9 KiB
JavaScript
import { focusOn } from './commands';
|
|
import { getTabbableNodes, contains, getFocusableNodes } from './utils/DOMutils';
|
|
import { asArray } from './utils/array';
|
|
/**
|
|
* for a given `element` in a given `scope` returns focusable siblings
|
|
* @param element - base element
|
|
* @param scope - common parent. Can be document, but better to narrow it down for performance reasons
|
|
* @returns {prev,next} - references to a focusable element before and after
|
|
* @returns undefined - if operation is not applicable
|
|
*/
|
|
export var getRelativeFocusable = function (element, scope, useTabbables) {
|
|
if (!element || !scope) {
|
|
console.error('no element or scope given');
|
|
return {};
|
|
}
|
|
var shards = asArray(scope);
|
|
if (shards.every(function (shard) { return !contains(shard, element); })) {
|
|
console.error('Active element is not contained in the scope');
|
|
return {};
|
|
}
|
|
var focusables = useTabbables
|
|
? getTabbableNodes(shards, new Map())
|
|
: getFocusableNodes(shards, new Map());
|
|
var current = focusables.findIndex(function (_a) {
|
|
var node = _a.node;
|
|
return node === element;
|
|
});
|
|
if (current === -1) {
|
|
// an edge case, when anchor element is not found
|
|
return undefined;
|
|
}
|
|
return {
|
|
prev: focusables[current - 1],
|
|
next: focusables[current + 1],
|
|
first: focusables[0],
|
|
last: focusables[focusables.length - 1],
|
|
};
|
|
};
|
|
var getBoundary = function (shards, useTabbables) {
|
|
var set = useTabbables
|
|
? getTabbableNodes(asArray(shards), new Map())
|
|
: getFocusableNodes(asArray(shards), new Map());
|
|
return {
|
|
first: set[0],
|
|
last: set[set.length - 1],
|
|
};
|
|
};
|
|
var defaultOptions = function (options) {
|
|
return Object.assign({
|
|
scope: document.body,
|
|
cycle: true,
|
|
onlyTabbable: true,
|
|
}, options);
|
|
};
|
|
var moveFocus = function (fromElement, options, cb) {
|
|
if (options === void 0) { options = {}; }
|
|
var newOptions = defaultOptions(options);
|
|
var solution = getRelativeFocusable(fromElement, newOptions.scope, newOptions.onlyTabbable);
|
|
if (!solution) {
|
|
return;
|
|
}
|
|
var target = cb(solution, newOptions.cycle);
|
|
if (target) {
|
|
focusOn(target.node, newOptions.focusOptions);
|
|
}
|
|
};
|
|
/**
|
|
* focuses next element in the tab-order
|
|
* @param fromElement - common parent to scope active element search or tab cycle order
|
|
* @param {FocusNextOptions} [options] - focus options
|
|
*/
|
|
export var focusNextElement = function (fromElement, options) {
|
|
if (options === void 0) { options = {}; }
|
|
moveFocus(fromElement, options, function (_a, cycle) {
|
|
var next = _a.next, first = _a.first;
|
|
return next || (cycle && first);
|
|
});
|
|
};
|
|
/**
|
|
* focuses prev element in the tab order
|
|
* @param fromElement - common parent to scope active element search or tab cycle order
|
|
* @param {FocusNextOptions} [options] - focus options
|
|
*/
|
|
export var focusPrevElement = function (fromElement, options) {
|
|
if (options === void 0) { options = {}; }
|
|
moveFocus(fromElement, options, function (_a, cycle) {
|
|
var prev = _a.prev, last = _a.last;
|
|
return prev || (cycle && last);
|
|
});
|
|
};
|
|
var pickBoundary = function (scope, options, what) {
|
|
var _a;
|
|
var boundary = getBoundary(scope, (_a = options.onlyTabbable) !== null && _a !== void 0 ? _a : true);
|
|
var node = boundary[what];
|
|
if (node) {
|
|
focusOn(node.node, options.focusOptions);
|
|
}
|
|
};
|
|
/**
|
|
* focuses first element in the tab-order
|
|
* @param {FocusNextOptions} options - focus options
|
|
*/
|
|
export var focusFirstElement = function (scope, options) {
|
|
if (options === void 0) { options = {}; }
|
|
pickBoundary(scope, options, 'first');
|
|
};
|
|
/**
|
|
* focuses last element in the tab order
|
|
* @param {FocusNextOptions} options - focus options
|
|
*/
|
|
export var focusLastElement = function (scope, options) {
|
|
if (options === void 0) { options = {}; }
|
|
pickBoundary(scope, options, 'last');
|
|
};
|