@shopify/polaris
Version:
Shopify’s product component library
61 lines (60 loc) • 2.25 kB
JavaScript
import React from 'react';
import { closest } from '@shopify/javascript-utilities/dom';
import { focusFirstFocusableNode, findFirstFocusableNode, focusLastFocusableNode, } from '@shopify/javascript-utilities/focus';
import { EventListener } from '../EventListener';
import { Focus } from '../Focus';
export class TrapFocus extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {
shouldFocusSelf: undefined,
};
this.setFocusTrapWrapper = (node) => {
this.focusTrapWrapper = node;
};
this.handleBlur = (event) => {
const { relatedTarget } = event;
const { focusTrapWrapper } = this;
const { trapping = true } = this.props;
if (relatedTarget == null || trapping === false) {
return;
}
if (focusTrapWrapper &&
!focusTrapWrapper.contains(relatedTarget) &&
!closest(relatedTarget, '[data-polaris-overlay]')) {
event.preventDefault();
if (event.srcElement === findFirstFocusableNode(focusTrapWrapper)) {
return focusLastFocusableNode(focusTrapWrapper);
}
focusFirstFocusableNode(focusTrapWrapper);
}
};
}
componentDidMount() {
this.setState(this.handleTrappingChange());
}
handleTrappingChange() {
const { trapping = true } = this.props;
if (this.focusTrapWrapper.contains(document.activeElement)) {
return { shouldFocusSelf: false };
}
return { shouldFocusSelf: trapping };
}
render() {
const { children } = this.props;
return (<Focus disabled={this.shouldDisable} root={this.focusTrapWrapper}>
<div ref={this.setFocusTrapWrapper}>
<EventListener event="focusout" handler={this.handleBlur}/>
{children}
</div>
</Focus>);
}
get shouldDisable() {
const { trapping = true } = this.props;
const { shouldFocusSelf } = this.state;
if (shouldFocusSelf === undefined) {
return true;
}
return shouldFocusSelf ? !trapping : !shouldFocusSelf;
}
}