1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.Overlay = void 0;
|
19 | var tslib_1 = require("tslib");
|
20 | var classnames_1 = tslib_1.__importDefault(require("classnames"));
|
21 | var React = tslib_1.__importStar(require("react"));
|
22 | var react_dom_1 = require("react-dom");
|
23 | var react_transition_group_1 = require("react-transition-group");
|
24 | var common_1 = require("../../common");
|
25 | var props_1 = require("../../common/props");
|
26 | var utils_1 = require("../../common/utils");
|
27 | var portal_1 = require("../portal/portal");
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | var Overlay = (function (_super) {
|
34 | tslib_1.__extends(Overlay, _super);
|
35 | function Overlay() {
|
36 | var _this = _super !== null && _super.apply(this, arguments) || this;
|
37 | _this.isAutoFocusing = false;
|
38 | _this.state = {
|
39 | hasEverOpened: _this.props.isOpen,
|
40 | };
|
41 |
|
42 | _this.containerElement = null;
|
43 |
|
44 | _this.startFocusTrapElement = null;
|
45 |
|
46 | _this.endFocusTrapElement = null;
|
47 | _this.refHandlers = {
|
48 |
|
49 |
|
50 | container: function (ref) { return (_this.containerElement = (0, react_dom_1.findDOMNode)(ref)); },
|
51 | endFocusTrap: function (ref) { return (_this.endFocusTrapElement = ref); },
|
52 | startFocusTrap: function (ref) { return (_this.startFocusTrapElement = ref); },
|
53 | };
|
54 | _this.maybeRenderChild = function (child) {
|
55 | if ((0, utils_1.isFunction)(child)) {
|
56 | child = child();
|
57 | }
|
58 | if (child == null) {
|
59 | return null;
|
60 | }
|
61 |
|
62 |
|
63 | var decoratedChild = typeof child === "object" ? (React.cloneElement(child, {
|
64 | className: (0, classnames_1.default)(child.props.className, common_1.Classes.OVERLAY_CONTENT),
|
65 | })) : (React.createElement("span", { className: common_1.Classes.OVERLAY_CONTENT }, child));
|
66 | var _a = _this.props, onOpening = _a.onOpening, onOpened = _a.onOpened, onClosing = _a.onClosing, transitionDuration = _a.transitionDuration, transitionName = _a.transitionName;
|
67 | return (React.createElement(react_transition_group_1.CSSTransition, { classNames: transitionName, onEntering: onOpening, onEntered: onOpened, onExiting: onClosing, onExited: _this.handleTransitionExited, timeout: transitionDuration, addEndListener: _this.handleTransitionAddEnd }, decoratedChild));
|
68 | };
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | _this.handleStartFocusTrapElementFocus = function (e) {
|
76 | var _a;
|
77 | if (!_this.props.enforceFocus || _this.isAutoFocusing) {
|
78 | return;
|
79 | }
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | if (e.relatedTarget != null &&
|
85 | _this.containerElement.contains(e.relatedTarget) &&
|
86 | e.relatedTarget !== _this.endFocusTrapElement) {
|
87 | (_a = _this.endFocusTrapElement) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
|
88 | }
|
89 | };
|
90 | |
91 |
|
92 |
|
93 | _this.handleStartFocusTrapElementKeyDown = function (e) {
|
94 | var _a;
|
95 | if (!_this.props.enforceFocus) {
|
96 | return;
|
97 | }
|
98 |
|
99 |
|
100 | if (e.shiftKey && e.which === common_1.Keys.TAB) {
|
101 | var lastFocusableElement = _this.getKeyboardFocusableElements().pop();
|
102 | if (lastFocusableElement != null) {
|
103 | lastFocusableElement.focus();
|
104 | }
|
105 | else {
|
106 | (_a = _this.endFocusTrapElement) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
|
107 | }
|
108 | }
|
109 | };
|
110 | |
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | _this.handleEndFocusTrapElementFocus = function (e) {
|
117 | var _a, _b;
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | if (e.relatedTarget != null &&
|
125 | _this.containerElement.contains(e.relatedTarget) &&
|
126 | e.relatedTarget !== _this.startFocusTrapElement) {
|
127 | var firstFocusableElement = _this.getKeyboardFocusableElements().shift();
|
128 |
|
129 | if (!_this.isAutoFocusing && firstFocusableElement != null && firstFocusableElement !== e.relatedTarget) {
|
130 | firstFocusableElement.focus();
|
131 | }
|
132 | else {
|
133 | (_a = _this.startFocusTrapElement) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
|
134 | }
|
135 | }
|
136 | else {
|
137 | var lastFocusableElement = _this.getKeyboardFocusableElements().pop();
|
138 | if (lastFocusableElement != null) {
|
139 | lastFocusableElement.focus();
|
140 | }
|
141 | else {
|
142 |
|
143 | (_b = _this.startFocusTrapElement) === null || _b === void 0 ? void 0 : _b.focus({ preventScroll: true });
|
144 | }
|
145 | }
|
146 | };
|
147 | _this.handleTransitionExited = function (node) {
|
148 | var _a, _b;
|
149 | if (_this.props.shouldReturnFocusOnClose && _this.lastActiveElementBeforeOpened instanceof HTMLElement) {
|
150 | _this.lastActiveElementBeforeOpened.focus();
|
151 | }
|
152 | (_b = (_a = _this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a, node);
|
153 | };
|
154 | _this.handleBackdropMouseDown = function (e) {
|
155 | var _a;
|
156 | var _b = _this.props, backdropProps = _b.backdropProps, canOutsideClickClose = _b.canOutsideClickClose, enforceFocus = _b.enforceFocus, onClose = _b.onClose;
|
157 | if (canOutsideClickClose) {
|
158 | onClose === null || onClose === void 0 ? void 0 : onClose(e);
|
159 | }
|
160 | if (enforceFocus) {
|
161 | _this.bringFocusInsideOverlay();
|
162 | }
|
163 | (_a = backdropProps === null || backdropProps === void 0 ? void 0 : backdropProps.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(backdropProps, e);
|
164 | };
|
165 | _this.handleDocumentClick = function (e) {
|
166 | var _a = _this.props, canOutsideClickClose = _a.canOutsideClickClose, isOpen = _a.isOpen, onClose = _a.onClose;
|
167 |
|
168 | var eventTarget = (e.composed ? e.composedPath()[0] : e.target);
|
169 | var stackIndex = Overlay.openStack.indexOf(_this);
|
170 | var isClickInThisOverlayOrDescendant = Overlay.openStack
|
171 | .slice(stackIndex)
|
172 | .some(function (_a) {
|
173 | var elem = _a.containerElement;
|
174 |
|
175 |
|
176 | return elem && elem.contains(eventTarget) && !elem.isSameNode(eventTarget);
|
177 | });
|
178 | if (isOpen && !isClickInThisOverlayOrDescendant && canOutsideClickClose) {
|
179 |
|
180 | onClose === null || onClose === void 0 ? void 0 : onClose(e);
|
181 | }
|
182 | };
|
183 | |
184 |
|
185 |
|
186 |
|
187 | _this.handleDocumentFocus = function (e) {
|
188 |
|
189 | var eventTarget = e.composed ? e.composedPath()[0] : e.target;
|
190 | if (_this.props.enforceFocus &&
|
191 | _this.containerElement != null &&
|
192 | eventTarget instanceof Node &&
|
193 | !_this.containerElement.contains(eventTarget)) {
|
194 |
|
195 | e.preventDefault();
|
196 | e.stopImmediatePropagation();
|
197 | _this.bringFocusInsideOverlay();
|
198 | }
|
199 | };
|
200 | _this.handleKeyDown = function (e) {
|
201 | var _a = _this.props, canEscapeKeyClose = _a.canEscapeKeyClose, onClose = _a.onClose;
|
202 | if (e.key === "Escape" && canEscapeKeyClose) {
|
203 | onClose === null || onClose === void 0 ? void 0 : onClose(e);
|
204 |
|
205 | e.stopPropagation();
|
206 |
|
207 | e.preventDefault();
|
208 | }
|
209 | };
|
210 | _this.handleTransitionAddEnd = function () {
|
211 |
|
212 | };
|
213 | return _this;
|
214 | }
|
215 | Overlay.getDerivedStateFromProps = function (_a) {
|
216 | var hasEverOpened = _a.isOpen;
|
217 | if (hasEverOpened) {
|
218 | return { hasEverOpened: hasEverOpened };
|
219 | }
|
220 | return null;
|
221 | };
|
222 | Overlay.prototype.render = function () {
|
223 | var _a;
|
224 | var _b;
|
225 |
|
226 | if (this.props.lazy && !this.state.hasEverOpened) {
|
227 | return null;
|
228 | }
|
229 | var _c = this.props, autoFocus = _c.autoFocus, children = _c.children, className = _c.className, enforceFocus = _c.enforceFocus, usePortal = _c.usePortal, isOpen = _c.isOpen;
|
230 |
|
231 |
|
232 |
|
233 | var childrenWithTransitions = isOpen ? (_b = React.Children.map(children, this.maybeRenderChild)) !== null && _b !== void 0 ? _b : [] : [];
|
234 | var maybeBackdrop = this.maybeRenderBackdrop();
|
235 | if (maybeBackdrop !== null) {
|
236 | childrenWithTransitions.unshift(maybeBackdrop);
|
237 | }
|
238 | if (isOpen && (autoFocus || enforceFocus) && childrenWithTransitions.length > 0) {
|
239 | childrenWithTransitions.unshift(this.renderDummyElement("__start", {
|
240 | className: common_1.Classes.OVERLAY_START_FOCUS_TRAP,
|
241 | onFocus: this.handleStartFocusTrapElementFocus,
|
242 | onKeyDown: this.handleStartFocusTrapElementKeyDown,
|
243 | ref: this.refHandlers.startFocusTrap,
|
244 | }));
|
245 | if (enforceFocus) {
|
246 | childrenWithTransitions.push(this.renderDummyElement("__end", {
|
247 | className: common_1.Classes.OVERLAY_END_FOCUS_TRAP,
|
248 | onFocus: this.handleEndFocusTrapElementFocus,
|
249 | ref: this.refHandlers.endFocusTrap,
|
250 | }));
|
251 | }
|
252 | }
|
253 | var containerClasses = (0, classnames_1.default)(common_1.Classes.OVERLAY, (_a = {},
|
254 | _a[common_1.Classes.OVERLAY_OPEN] = isOpen,
|
255 | _a[common_1.Classes.OVERLAY_INLINE] = !usePortal,
|
256 | _a), className);
|
257 | var transitionGroup = (React.createElement(react_transition_group_1.TransitionGroup, { appear: true, "aria-live": "polite", className: containerClasses, component: "div", onKeyDown: this.handleKeyDown, ref: this.refHandlers.container }, childrenWithTransitions));
|
258 | if (usePortal) {
|
259 | return (React.createElement(portal_1.Portal, { className: this.props.portalClassName, container: this.props.portalContainer }, transitionGroup));
|
260 | }
|
261 | else {
|
262 | return transitionGroup;
|
263 | }
|
264 | };
|
265 | Overlay.prototype.componentDidMount = function () {
|
266 | if (this.props.isOpen) {
|
267 | this.overlayWillOpen();
|
268 | }
|
269 | };
|
270 | Overlay.prototype.componentDidUpdate = function (prevProps) {
|
271 | if (prevProps.isOpen && !this.props.isOpen) {
|
272 | this.overlayWillClose();
|
273 | }
|
274 | else if (!prevProps.isOpen && this.props.isOpen) {
|
275 | this.overlayWillOpen();
|
276 | }
|
277 | };
|
278 | Overlay.prototype.componentWillUnmount = function () {
|
279 | this.overlayWillClose();
|
280 | };
|
281 | |
282 |
|
283 |
|
284 |
|
285 | Overlay.prototype.bringFocusInsideOverlay = function () {
|
286 | var _this = this;
|
287 |
|
288 | return this.requestAnimationFrame(function () {
|
289 | var _a;
|
290 |
|
291 |
|
292 | var activeElement = (0, utils_1.getActiveElement)(_this.containerElement);
|
293 | if (_this.containerElement == null || activeElement == null || !_this.props.isOpen) {
|
294 | return;
|
295 | }
|
296 | var isFocusOutsideModal = !_this.containerElement.contains(activeElement);
|
297 | if (isFocusOutsideModal) {
|
298 | (_a = _this.startFocusTrapElement) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
|
299 | _this.isAutoFocusing = false;
|
300 | }
|
301 | });
|
302 | };
|
303 | Overlay.prototype.maybeRenderBackdrop = function () {
|
304 | var _a = this.props, backdropClassName = _a.backdropClassName, backdropProps = _a.backdropProps, hasBackdrop = _a.hasBackdrop, isOpen = _a.isOpen, transitionDuration = _a.transitionDuration, transitionName = _a.transitionName;
|
305 | if (hasBackdrop && isOpen) {
|
306 | return (React.createElement(react_transition_group_1.CSSTransition, { classNames: transitionName, key: "__backdrop", timeout: transitionDuration, addEndListener: this.handleTransitionAddEnd },
|
307 | React.createElement("div", tslib_1.__assign({}, backdropProps, { className: (0, classnames_1.default)(common_1.Classes.OVERLAY_BACKDROP, backdropClassName, backdropProps === null || backdropProps === void 0 ? void 0 : backdropProps.className), onMouseDown: this.handleBackdropMouseDown }))));
|
308 | }
|
309 | else {
|
310 | return null;
|
311 | }
|
312 | };
|
313 | Overlay.prototype.renderDummyElement = function (key, props) {
|
314 | var _a = this.props, transitionDuration = _a.transitionDuration, transitionName = _a.transitionName;
|
315 | return (React.createElement(react_transition_group_1.CSSTransition, { classNames: transitionName, key: key, addEndListener: this.handleTransitionAddEnd, timeout: transitionDuration, unmountOnExit: true },
|
316 | React.createElement("div", tslib_1.__assign({ tabIndex: 0 }, props))));
|
317 | };
|
318 | Overlay.prototype.getKeyboardFocusableElements = function () {
|
319 | var focusableElements = this.containerElement !== null
|
320 | ? Array.from(
|
321 |
|
322 |
|
323 |
|
324 | this.containerElement.querySelectorAll([
|
325 | 'a[href]:not([tabindex="-1"])',
|
326 | 'button:not([disabled]):not([tabindex="-1"])',
|
327 | 'details:not([tabindex="-1"])',
|
328 | 'input:not([disabled]):not([tabindex="-1"])',
|
329 | 'select:not([disabled]):not([tabindex="-1"])',
|
330 | 'textarea:not([disabled]):not([tabindex="-1"])',
|
331 | '[tabindex]:not([tabindex="-1"])',
|
332 | ].join(",")))
|
333 | : [];
|
334 | return focusableElements.filter(function (el) {
|
335 | return !el.classList.contains(common_1.Classes.OVERLAY_START_FOCUS_TRAP) &&
|
336 | !el.classList.contains(common_1.Classes.OVERLAY_END_FOCUS_TRAP);
|
337 | });
|
338 | };
|
339 | Overlay.prototype.overlayWillClose = function () {
|
340 | document.removeEventListener("focus", this.handleDocumentFocus, true);
|
341 | document.removeEventListener("mousedown", this.handleDocumentClick);
|
342 | var openStack = Overlay.openStack;
|
343 | var stackIndex = openStack.indexOf(this);
|
344 | if (stackIndex !== -1) {
|
345 | openStack.splice(stackIndex, 1);
|
346 | if (openStack.length > 0) {
|
347 | var lastOpenedOverlay = Overlay.getLastOpened();
|
348 |
|
349 |
|
350 |
|
351 | if (lastOpenedOverlay.props.autoFocus && lastOpenedOverlay.props.enforceFocus) {
|
352 | lastOpenedOverlay.bringFocusInsideOverlay();
|
353 | document.addEventListener("focus", lastOpenedOverlay.handleDocumentFocus, true);
|
354 | }
|
355 | }
|
356 | if (openStack.filter(function (o) { return o.props.usePortal && o.props.hasBackdrop; }).length === 0) {
|
357 | document.body.classList.remove(common_1.Classes.OVERLAY_OPEN);
|
358 | }
|
359 | }
|
360 | };
|
361 | Overlay.prototype.overlayWillOpen = function () {
|
362 | var getLastOpened = Overlay.getLastOpened, openStack = Overlay.openStack;
|
363 | if (openStack.length > 0) {
|
364 | document.removeEventListener("focus", getLastOpened().handleDocumentFocus, true);
|
365 | }
|
366 | openStack.push(this);
|
367 | if (this.props.autoFocus) {
|
368 | this.isAutoFocusing = true;
|
369 | this.bringFocusInsideOverlay();
|
370 | }
|
371 | if (this.props.enforceFocus) {
|
372 |
|
373 |
|
374 | document.addEventListener("focus", this.handleDocumentFocus, true);
|
375 | }
|
376 | if (this.props.canOutsideClickClose && !this.props.hasBackdrop) {
|
377 | document.addEventListener("mousedown", this.handleDocumentClick);
|
378 | }
|
379 | if (this.props.hasBackdrop && this.props.usePortal) {
|
380 |
|
381 | document.body.classList.add(common_1.Classes.OVERLAY_OPEN);
|
382 | }
|
383 | this.lastActiveElementBeforeOpened = (0, utils_1.getActiveElement)(this.containerElement);
|
384 | };
|
385 | Overlay.displayName = "".concat(props_1.DISPLAYNAME_PREFIX, ".Overlay");
|
386 | Overlay.defaultProps = {
|
387 | autoFocus: true,
|
388 | backdropProps: {},
|
389 | canEscapeKeyClose: true,
|
390 | canOutsideClickClose: true,
|
391 | enforceFocus: true,
|
392 | hasBackdrop: true,
|
393 | isOpen: false,
|
394 | lazy: true,
|
395 | shouldReturnFocusOnClose: true,
|
396 | transitionDuration: 300,
|
397 | transitionName: common_1.Classes.OVERLAY,
|
398 | usePortal: true,
|
399 | };
|
400 | Overlay.openStack = [];
|
401 | Overlay.getLastOpened = function () { return Overlay.openStack[Overlay.openStack.length - 1]; };
|
402 | return Overlay;
|
403 | }(common_1.AbstractPureComponent2));
|
404 | exports.Overlay = Overlay;
|
405 |
|
\ | No newline at end of file |