react-focus-trap
Version:
Traps focus for accessible dropdowns and modal content.
170 lines (130 loc) • 4.36 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = _interopDefault(require('react'));
/**
* FocalPoint
* The container that will maintain focus
*/
var timer = null;
var isDOM = typeof document !== 'undefined';
var defaultProps$1 = {
element: 'div'
};
var FocalPoint = (function (superclass) {
function FocalPoint(props, context) {
superclass.call(this, props, context);
this.anchor = null;
this.focus = this.focus.bind(this);
this._onBlur = this._onBlur.bind(this);
this._setRoot = this._setRoot.bind(this);
}
if ( superclass ) FocalPoint.__proto__ = superclass;
FocalPoint.prototype = Object.create( superclass && superclass.prototype );
FocalPoint.prototype.constructor = FocalPoint;
FocalPoint.prototype.contains = function contains (element) {
return this.root.contains(element)
};
FocalPoint.prototype.focus = function focus () {
if (this.contains(document.activeElement) === false) {
this.root.focus();
}
};
FocalPoint.prototype.trapFocus = function trapFocus (e) {
clearTimeout(timer);
timer = setTimeout(this.focus, 10);
};
FocalPoint.prototype.returnFocus = function returnFocus () {
// When transitioning between pages using hash route state,
// this anchor is some times lost. Do not attempt to focus
// on a non-existent anchor.
if (
this.anchor &&
typeof this.anchor === 'object' &&
typeof this.anchor.focus === 'function'
) {
this.anchor.focus();
}
};
FocalPoint.prototype.componentWillMount = function componentWillMount () {
if (isDOM) {
this.anchor = document.activeElement;
}
};
FocalPoint.prototype.componentDidMount = function componentDidMount () {
this.trapFocus();
document.addEventListener('focus', this._onBlur, true);
};
FocalPoint.prototype.componentWillUnmount = function componentWillUnmount () {
document.removeEventListener('focus', this._onBlur, true);
clearTimeout(timer);
this.returnFocus();
this.anchor = null;
};
FocalPoint.prototype.render = function render () {
var ref = this.props;
var children = ref.children;
var element = ref.element;
var className = ref.className;
return React.createElement(element, {
ref: this._setRoot,
tabIndex: 0,
className: className,
children: children
})
};
// Private -------------------------------------------------- //
FocalPoint.prototype._setRoot = function _setRoot (el) {
this.root = el;
};
FocalPoint.prototype._onBlur = function _onBlur (event) {
var current = this.anchor;
if (current && current.contains(event.target) === false) {
event.preventDefault();
this.trapFocus();
}
};
return FocalPoint;
}(React.Component));
FocalPoint.defaultProps = defaultProps$1;
var defaultProps = {
active: true,
className: 'focus-trap',
onExit: function () {}
};
var FocusTrap = (function (superclass) {
function FocusTrap(props, context) {
superclass.call(this, props, context);
this._onKeyUp = this._onKeyUp.bind(this);
}
if ( superclass ) FocusTrap.__proto__ = superclass;
FocusTrap.prototype = Object.create( superclass && superclass.prototype );
FocusTrap.prototype.constructor = FocusTrap;
FocusTrap.prototype.render = function render () {
var ref = this.props;
var active = ref.active;
var className = ref.className;
var children = ref.children;
var element = ref.element;
var onExit = ref.onExit;
if (!active) { return null }
return (
React.createElement( 'div', { className: (className + "-wrapper"), onKeyUp: this._onKeyUp },
React.createElement( 'div', {
'aria-hidden': "true", className: (className + "-backdrop"), onClick: onExit }),
React.createElement( FocalPoint, { className: className, element: element },
children
)
)
)
};
// Private -------------------------------------------------- //
FocusTrap.prototype._onKeyUp = function _onKeyUp (event) {
if (event.key === 'Escape') {
this.props.onExit();
}
};
return FocusTrap;
}(React.Component));
FocusTrap.defaultProps = defaultProps;
module.exports = FocusTrap;
//# sourceMappingURL=index.js.map