1 | import { __assign, __extends } from "tslib";
|
2 | import * as React from 'react';
|
3 | import { modalize } from '@uifabric/utilities';
|
4 | import { elementContains, getNativeProps, divProperties, getFirstTabbable, getLastTabbable, getNextElement, getDocument, focusAsync, initializeComponentRef, on, } from '../../Utilities';
|
5 | var FocusTrapZone = (function (_super) {
|
6 | __extends(FocusTrapZone, _super);
|
7 | function FocusTrapZone(props) {
|
8 | var _this = _super.call(this, props) || this;
|
9 | _this._root = React.createRef();
|
10 | _this._firstBumper = React.createRef();
|
11 | _this._lastBumper = React.createRef();
|
12 | _this._hasFocus = false;
|
13 | _this._onRootFocus = function (ev) {
|
14 | if (_this.props.onFocus) {
|
15 | _this.props.onFocus(ev);
|
16 | }
|
17 | _this._hasFocus = true;
|
18 | };
|
19 | _this._onRootBlur = function (ev) {
|
20 | if (_this.props.onBlur) {
|
21 | _this.props.onBlur(ev);
|
22 | }
|
23 | var relatedTarget = ev.relatedTarget;
|
24 | if (ev.relatedTarget === null) {
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | relatedTarget = _this._getDocument().activeElement;
|
31 | }
|
32 | if (!elementContains(_this._root.current, relatedTarget)) {
|
33 | _this._hasFocus = false;
|
34 | }
|
35 | };
|
36 | _this._onFirstBumperFocus = function () {
|
37 | _this._onBumperFocus(true);
|
38 | };
|
39 | _this._onLastBumperFocus = function () {
|
40 | _this._onBumperFocus(false);
|
41 | };
|
42 | _this._onBumperFocus = function (isFirstBumper) {
|
43 | if (_this.props.disabled) {
|
44 | return;
|
45 | }
|
46 | var currentBumper = (isFirstBumper === _this._hasFocus
|
47 | ? _this._lastBumper.current
|
48 | : _this._firstBumper.current);
|
49 | if (_this._root.current) {
|
50 | var nextFocusable = isFirstBumper === _this._hasFocus
|
51 | ? getLastTabbable(_this._root.current, currentBumper, true, false)
|
52 | : getFirstTabbable(_this._root.current, currentBumper, true, false);
|
53 | if (nextFocusable) {
|
54 | if (_this._isBumper(nextFocusable)) {
|
55 |
|
56 |
|
57 | _this.focus();
|
58 | }
|
59 | else {
|
60 | nextFocusable.focus();
|
61 | }
|
62 | }
|
63 | }
|
64 | };
|
65 | _this._onFocusCapture = function (ev) {
|
66 | if (_this.props.onFocusCapture) {
|
67 | _this.props.onFocusCapture(ev);
|
68 | }
|
69 | if (ev.target !== ev.currentTarget && !_this._isBumper(ev.target)) {
|
70 |
|
71 |
|
72 | _this._previouslyFocusedElementInTrapZone = ev.target;
|
73 | }
|
74 | };
|
75 | _this._forceFocusInTrap = function (ev) {
|
76 | if (_this.props.disabled) {
|
77 | return;
|
78 | }
|
79 | if (FocusTrapZone._focusStack.length && _this === FocusTrapZone._focusStack[FocusTrapZone._focusStack.length - 1]) {
|
80 | var focusedElement = _this._getDocument().activeElement;
|
81 | if (!elementContains(_this._root.current, focusedElement)) {
|
82 | _this.focus();
|
83 | _this._hasFocus = true;
|
84 | ev.preventDefault();
|
85 | ev.stopPropagation();
|
86 | }
|
87 | }
|
88 | };
|
89 | _this._forceClickInTrap = function (ev) {
|
90 | if (_this.props.disabled) {
|
91 | return;
|
92 | }
|
93 | if (FocusTrapZone._focusStack.length && _this === FocusTrapZone._focusStack[FocusTrapZone._focusStack.length - 1]) {
|
94 | var clickedElement = ev.target;
|
95 | if (clickedElement && !elementContains(_this._root.current, clickedElement)) {
|
96 | _this.focus();
|
97 | _this._hasFocus = true;
|
98 | ev.preventDefault();
|
99 | ev.stopPropagation();
|
100 | }
|
101 | }
|
102 | };
|
103 | initializeComponentRef(_this);
|
104 | return _this;
|
105 | }
|
106 | FocusTrapZone.prototype.componentDidMount = function () {
|
107 | this._bringFocusIntoZone();
|
108 | this._updateEventHandlers(this.props);
|
109 | if (!this.props.disabled && this._root.current && this.props.enableAriaHiddenSiblings) {
|
110 | this._unmodalize = modalize(this._root.current);
|
111 | }
|
112 | };
|
113 | FocusTrapZone.prototype.UNSAFE_componentWillReceiveProps = function (nextProps) {
|
114 | var elementToFocusOnDismiss = nextProps.elementToFocusOnDismiss;
|
115 | if (elementToFocusOnDismiss && this._previouslyFocusedElementOutsideTrapZone !== elementToFocusOnDismiss) {
|
116 | this._previouslyFocusedElementOutsideTrapZone = elementToFocusOnDismiss;
|
117 | }
|
118 | this._updateEventHandlers(nextProps);
|
119 | };
|
120 | FocusTrapZone.prototype.componentDidUpdate = function (prevProps) {
|
121 | var prevForceFocusInsideTrap = prevProps.forceFocusInsideTrap !== undefined ? prevProps.forceFocusInsideTrap : true;
|
122 | var newForceFocusInsideTrap = this.props.forceFocusInsideTrap !== undefined ? this.props.forceFocusInsideTrap : true;
|
123 | var prevDisabled = prevProps.disabled !== undefined ? prevProps.disabled : false;
|
124 | var newDisabled = this.props.disabled !== undefined ? this.props.disabled : false;
|
125 | if ((!prevForceFocusInsideTrap && newForceFocusInsideTrap) || (prevDisabled && !newDisabled)) {
|
126 |
|
127 |
|
128 | this._bringFocusIntoZone();
|
129 | if (!this._unmodalize && this._root.current && this.props.enableAriaHiddenSiblings) {
|
130 | this._unmodalize = modalize(this._root.current);
|
131 | }
|
132 | }
|
133 | else if ((prevForceFocusInsideTrap && !newForceFocusInsideTrap) || (!prevDisabled && newDisabled)) {
|
134 |
|
135 |
|
136 | this._returnFocusToInitiator();
|
137 | if (this._unmodalize) {
|
138 | this._unmodalize();
|
139 | }
|
140 | }
|
141 | };
|
142 | FocusTrapZone.prototype.componentWillUnmount = function () {
|
143 |
|
144 | if (!this.props.disabled ||
|
145 | this.props.forceFocusInsideTrap ||
|
146 | !elementContains(this._root.current, this._getDocument().activeElement)) {
|
147 | this._returnFocusToInitiator();
|
148 | }
|
149 |
|
150 | if (this._disposeClickHandler) {
|
151 | this._disposeClickHandler();
|
152 | this._disposeClickHandler = undefined;
|
153 | }
|
154 | if (this._disposeFocusHandler) {
|
155 | this._disposeFocusHandler();
|
156 | this._disposeFocusHandler = undefined;
|
157 | }
|
158 | if (this._unmodalize) {
|
159 | this._unmodalize();
|
160 | }
|
161 |
|
162 | delete this._previouslyFocusedElementInTrapZone;
|
163 | delete this._previouslyFocusedElementOutsideTrapZone;
|
164 | };
|
165 | FocusTrapZone.prototype.render = function () {
|
166 | var _a = this.props, className = _a.className, _b = _a.disabled, disabled = _b === void 0 ? false : _b, ariaLabelledBy = _a.ariaLabelledBy;
|
167 | var divProps = getNativeProps(this.props, divProperties);
|
168 | var bumperProps = {
|
169 | 'aria-hidden': true,
|
170 | style: {
|
171 | pointerEvents: 'none',
|
172 | position: 'fixed',
|
173 | },
|
174 | tabIndex: disabled ? -1 : 0,
|
175 | 'data-is-visible': true,
|
176 | };
|
177 | return (React.createElement("div", __assign({}, divProps, { className: className, ref: this._root, "aria-labelledby": ariaLabelledBy, onFocusCapture: this._onFocusCapture, onFocus: this._onRootFocus, onBlur: this._onRootBlur }),
|
178 | React.createElement("div", __assign({}, bumperProps, { ref: this._firstBumper, onFocus: this._onFirstBumperFocus })),
|
179 | this.props.children,
|
180 | React.createElement("div", __assign({}, bumperProps, { ref: this._lastBumper, onFocus: this._onLastBumperFocus }))));
|
181 | };
|
182 | FocusTrapZone.prototype.focus = function () {
|
183 |
|
184 | var _a = this.props, focusPreviouslyFocusedInnerElement = _a.focusPreviouslyFocusedInnerElement, firstFocusableSelector = _a.firstFocusableSelector, firstFocusableTarget = _a.firstFocusableTarget;
|
185 | if (focusPreviouslyFocusedInnerElement &&
|
186 | this._previouslyFocusedElementInTrapZone &&
|
187 | elementContains(this._root.current, this._previouslyFocusedElementInTrapZone)) {
|
188 |
|
189 | this._focusAsync(this._previouslyFocusedElementInTrapZone);
|
190 | return;
|
191 | }
|
192 | var focusSelector = typeof firstFocusableSelector === 'string'
|
193 | ? firstFocusableSelector
|
194 | : firstFocusableSelector && firstFocusableSelector();
|
195 | var _firstFocusableChild = null;
|
196 | if (this._root.current) {
|
197 | if (typeof firstFocusableTarget === 'string') {
|
198 | _firstFocusableChild = this._root.current.querySelector(firstFocusableTarget);
|
199 | }
|
200 | else if (firstFocusableTarget) {
|
201 | _firstFocusableChild = firstFocusableTarget(this._root.current);
|
202 | }
|
203 | else if (focusSelector) {
|
204 | _firstFocusableChild = this._root.current.querySelector('.' + focusSelector);
|
205 | }
|
206 |
|
207 | if (!_firstFocusableChild) {
|
208 | _firstFocusableChild = getNextElement(this._root.current, this._root.current.firstChild, false, false, false, true);
|
209 | }
|
210 | }
|
211 | if (_firstFocusableChild) {
|
212 | this._focusAsync(_firstFocusableChild);
|
213 | }
|
214 | };
|
215 | FocusTrapZone.prototype._focusAsync = function (element) {
|
216 | if (!this._isBumper(element)) {
|
217 | focusAsync(element);
|
218 | }
|
219 | };
|
220 | FocusTrapZone.prototype._bringFocusIntoZone = function () {
|
221 | var _a = this.props, elementToFocusOnDismiss = _a.elementToFocusOnDismiss, _b = _a.disabled, disabled = _b === void 0 ? false : _b, _c = _a.disableFirstFocus, disableFirstFocus = _c === void 0 ? false : _c;
|
222 | if (disabled) {
|
223 | return;
|
224 | }
|
225 | FocusTrapZone._focusStack.push(this);
|
226 | this._previouslyFocusedElementOutsideTrapZone = elementToFocusOnDismiss
|
227 | ? elementToFocusOnDismiss
|
228 | : this._getDocument().activeElement;
|
229 | if (!disableFirstFocus && !elementContains(this._root.current, this._previouslyFocusedElementOutsideTrapZone)) {
|
230 | this.focus();
|
231 | }
|
232 | };
|
233 | FocusTrapZone.prototype._returnFocusToInitiator = function () {
|
234 | var _this = this;
|
235 | var ignoreExternalFocusing = this.props.ignoreExternalFocusing;
|
236 | FocusTrapZone._focusStack = FocusTrapZone._focusStack.filter(function (value) {
|
237 | return _this !== value;
|
238 | });
|
239 | var doc = this._getDocument();
|
240 | var activeElement = doc.activeElement;
|
241 | if (!ignoreExternalFocusing &&
|
242 | this._previouslyFocusedElementOutsideTrapZone &&
|
243 | typeof this._previouslyFocusedElementOutsideTrapZone.focus === 'function' &&
|
244 | (elementContains(this._root.current, activeElement) || activeElement === doc.body)) {
|
245 | this._focusAsync(this._previouslyFocusedElementOutsideTrapZone);
|
246 | }
|
247 | };
|
248 | FocusTrapZone.prototype._updateEventHandlers = function (newProps) {
|
249 | var _a = newProps.isClickableOutsideFocusTrap, isClickableOutsideFocusTrap = _a === void 0 ? false : _a, _b = newProps.forceFocusInsideTrap, forceFocusInsideTrap = _b === void 0 ? true : _b;
|
250 | if (forceFocusInsideTrap && !this._disposeFocusHandler) {
|
251 | this._disposeFocusHandler = on(window, 'focus', this._forceFocusInTrap, true);
|
252 | }
|
253 | else if (!forceFocusInsideTrap && this._disposeFocusHandler) {
|
254 | this._disposeFocusHandler();
|
255 | this._disposeFocusHandler = undefined;
|
256 | }
|
257 | if (!isClickableOutsideFocusTrap && !this._disposeClickHandler) {
|
258 | this._disposeClickHandler = on(window, 'click', this._forceClickInTrap, true);
|
259 | }
|
260 | else if (isClickableOutsideFocusTrap && this._disposeClickHandler) {
|
261 | this._disposeClickHandler();
|
262 | this._disposeClickHandler = undefined;
|
263 | }
|
264 | };
|
265 | FocusTrapZone.prototype._isBumper = function (element) {
|
266 | return element === this._firstBumper.current || element === this._lastBumper.current;
|
267 | };
|
268 | FocusTrapZone.prototype._getDocument = function () {
|
269 | return getDocument(this._root.current);
|
270 | };
|
271 | FocusTrapZone._focusStack = [];
|
272 | return FocusTrapZone;
|
273 | }(React.Component));
|
274 | export { FocusTrapZone };
|
275 |
|
\ | No newline at end of file |