UNPKG

4.59 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5exports.__esModule = true;
6exports["default"] = void 0;
7
8var _contains = _interopRequireDefault(require("dom-helpers/contains"));
9
10var _listen = _interopRequireDefault(require("dom-helpers/listen"));
11
12var _react = require("react");
13
14var _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
15
16var _warning = _interopRequireDefault(require("warning"));
17
18var _ownerDocument = _interopRequireDefault(require("./ownerDocument"));
19
20var escapeKeyCode = 27;
21
22var noop = function noop() {};
23
24function isLeftClickEvent(event) {
25 return event.button === 0;
26}
27
28function isModifiedEvent(event) {
29 return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
30}
31
32var getRefTarget = function getRefTarget(ref) {
33 return ref && ('current' in ref ? ref.current : ref);
34};
35
36/**
37 * The `useRootClose` hook registers your callback on the document
38 * when rendered. Powers the `<Overlay/>` component. This is used achieve modal
39 * style behavior where your callback is triggered when the user tries to
40 * interact with the rest of the document or hits the `esc` key.
41 *
42 * @param {Ref<HTMLElement>| HTMLElement} ref The element boundary
43 * @param {function} onRootClose
44 * @param {object=} options
45 * @param {boolean=} options.disabled
46 * @param {string=} options.clickTrigger The DOM event name (click, mousedown, etc) to attach listeners on
47 */
48function useRootClose(ref, onRootClose, _temp) {
49 var _ref = _temp === void 0 ? {} : _temp,
50 disabled = _ref.disabled,
51 _ref$clickTrigger = _ref.clickTrigger,
52 clickTrigger = _ref$clickTrigger === void 0 ? 'click' : _ref$clickTrigger;
53
54 var preventMouseRootCloseRef = (0, _react.useRef)(false);
55 var onClose = onRootClose || noop;
56 var handleMouseCapture = (0, _react.useCallback)(function (e) {
57 var _e$composedPath$;
58
59 var currentTarget = getRefTarget(ref);
60 (0, _warning["default"])(!!currentTarget, 'RootClose captured a close event but does not have a ref to compare it to. ' + 'useRootClose(), should be passed a ref that resolves to a DOM node');
61 preventMouseRootCloseRef.current = !currentTarget || isModifiedEvent(e) || !isLeftClickEvent(e) || !!(0, _contains["default"])(currentTarget, (_e$composedPath$ = e.composedPath == null ? void 0 : e.composedPath()[0]) != null ? _e$composedPath$ : e.target);
62 }, [ref]);
63 var handleMouse = (0, _useEventCallback["default"])(function (e) {
64 if (!preventMouseRootCloseRef.current) {
65 onClose(e);
66 }
67 });
68 var handleKeyUp = (0, _useEventCallback["default"])(function (e) {
69 if (e.keyCode === escapeKeyCode) {
70 onClose(e);
71 }
72 });
73 (0, _react.useEffect)(function () {
74 if (disabled || ref == null) return undefined; // Store the current event to avoid triggering handlers immediately
75 // https://github.com/facebook/react/issues/20074
76
77 var currentEvent = window.event;
78 var doc = (0, _ownerDocument["default"])(getRefTarget(ref)); // Use capture for this listener so it fires before React's listener, to
79 // avoid false positives in the contains() check below if the target DOM
80 // element is removed in the React mouse callback.
81
82 var removeMouseCaptureListener = (0, _listen["default"])(doc, clickTrigger, handleMouseCapture, true);
83 var removeMouseListener = (0, _listen["default"])(doc, clickTrigger, function (e) {
84 // skip if this event is the same as the one running when we added the handlers
85 if (e === currentEvent) {
86 currentEvent = undefined;
87 return;
88 }
89
90 handleMouse(e);
91 });
92 var removeKeyupListener = (0, _listen["default"])(doc, 'keyup', function (e) {
93 // skip if this event is the same as the one running when we added the handlers
94 if (e === currentEvent) {
95 currentEvent = undefined;
96 return;
97 }
98
99 handleKeyUp(e);
100 });
101 var mobileSafariHackListeners = [];
102
103 if ('ontouchstart' in doc.documentElement) {
104 mobileSafariHackListeners = [].slice.call(doc.body.children).map(function (el) {
105 return (0, _listen["default"])(el, 'mousemove', noop);
106 });
107 }
108
109 return function () {
110 removeMouseCaptureListener();
111 removeMouseListener();
112 removeKeyupListener();
113 mobileSafariHackListeners.forEach(function (remove) {
114 return remove();
115 });
116 };
117 }, [ref, disabled, clickTrigger, handleMouseCapture, handleMouse, handleKeyUp]);
118}
119
120var _default = useRootClose;
121exports["default"] = _default;
122module.exports = exports.default;
\No newline at end of file