UNPKG

10 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var React = require('react');
6var portal = require('@reach/portal');
7var ownerDocument = require('@reach/utils/owner-document');
8require('@reach/utils/type-check');
9var noop = require('@reach/utils/noop');
10var devUtils = require('@reach/utils/dev-utils');
11var composeRefs = require('@reach/utils/compose-refs');
12var composeEventHandlers = require('@reach/utils/compose-event-handlers');
13var FocusLock = require('react-focus-lock');
14var reactRemoveScroll = require('react-remove-scroll');
15var PropTypes = require('prop-types');
16
17function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
18
19var FocusLock__default = /*#__PURE__*/_interopDefault(FocusLock);
20var PropTypes__default = /*#__PURE__*/_interopDefault(PropTypes);
21
22function _extends() {
23 _extends = Object.assign || function (target) {
24 for (var i = 1; i < arguments.length; i++) {
25 var source = arguments[i];
26
27 for (var key in source) {
28 if (Object.prototype.hasOwnProperty.call(source, key)) {
29 target[key] = source[key];
30 }
31 }
32 }
33
34 return target;
35 };
36
37 return _extends.apply(this, arguments);
38}
39
40function _objectWithoutPropertiesLoose(source, excluded) {
41 if (source == null) return {};
42 var target = {};
43 var sourceKeys = Object.keys(source);
44 var key, i;
45
46 for (i = 0; i < sourceKeys.length; i++) {
47 key = sourceKeys[i];
48 if (excluded.indexOf(key) >= 0) continue;
49 target[key] = source[key];
50 }
51
52 return target;
53}
54
55var _excluded = ["as", "isOpen"],
56 _excluded2 = ["allowPinchZoom", "as", "dangerouslyBypassFocusLock", "dangerouslyBypassScrollLock", "initialFocusRef", "onClick", "onDismiss", "onKeyDown", "onMouseDown", "unstable_lockFocusAcrossFrames"],
57 _excluded3 = ["as", "onClick", "onKeyDown"],
58 _excluded4 = ["allowPinchZoom", "initialFocusRef", "isOpen", "onDismiss"];
59({
60 allowPinchZoom: PropTypes__default['default'].bool,
61 dangerouslyBypassFocusLock: PropTypes__default['default'].bool,
62 dangerouslyBypassScrollLock: PropTypes__default['default'].bool,
63 // TODO:
64 initialFocusRef: function initialFocusRef() {
65 return null;
66 },
67 onDismiss: PropTypes__default['default'].func
68}); ////////////////////////////////////////////////////////////////////////////////
69
70/**
71 * DialogOverlay
72 *
73 * Low-level component if you need more control over the styles or rendering of
74 * the dialog overlay.
75 *
76 * Note: You must render a `DialogContent` inside.
77 *
78 * @see Docs https://reach.tech/dialog#dialogoverlay
79 */
80
81var DialogOverlay = /*#__PURE__*/React.forwardRef(function DialogOverlay(_ref, forwardedRef) {
82 var _ref$as = _ref.as,
83 Comp = _ref$as === void 0 ? "div" : _ref$as,
84 _ref$isOpen = _ref.isOpen,
85 isOpen = _ref$isOpen === void 0 ? true : _ref$isOpen,
86 props = _objectWithoutPropertiesLoose(_ref, _excluded);
87
88 devUtils.useCheckStyles("dialog"); // We want to ignore the immediate focus of a tooltip so it doesn't pop
89 // up again when the menu closes, only pops up when focus returns again
90 // to the tooltip (like native OS tooltips).
91
92 React.useEffect(function () {
93 if (isOpen) {
94 // @ts-ignore
95 window.__REACH_DISABLE_TOOLTIPS = true;
96 } else {
97 window.requestAnimationFrame(function () {
98 // Wait a frame so that this doesn't fire before tooltip does
99 // @ts-ignore
100 window.__REACH_DISABLE_TOOLTIPS = false;
101 });
102 }
103 }, [isOpen]);
104 return isOpen ? /*#__PURE__*/React.createElement(portal.Portal, {
105 "data-reach-dialog-wrapper": ""
106 }, /*#__PURE__*/React.createElement(DialogInner, _extends({
107 ref: forwardedRef,
108 as: Comp
109 }, props))) : null;
110});
111
112////////////////////////////////////////////////////////////////////////////////
113
114/**
115 * DialogInner
116 */
117var DialogInner = /*#__PURE__*/React.forwardRef(function DialogInner(_ref2, forwardedRef) {
118 var allowPinchZoom = _ref2.allowPinchZoom,
119 _ref2$as = _ref2.as,
120 Comp = _ref2$as === void 0 ? "div" : _ref2$as,
121 _ref2$dangerouslyBypa = _ref2.dangerouslyBypassFocusLock,
122 dangerouslyBypassFocusLock = _ref2$dangerouslyBypa === void 0 ? false : _ref2$dangerouslyBypa,
123 _ref2$dangerouslyBypa2 = _ref2.dangerouslyBypassScrollLock,
124 dangerouslyBypassScrollLock = _ref2$dangerouslyBypa2 === void 0 ? false : _ref2$dangerouslyBypa2,
125 initialFocusRef = _ref2.initialFocusRef,
126 onClick = _ref2.onClick,
127 _ref2$onDismiss = _ref2.onDismiss,
128 onDismiss = _ref2$onDismiss === void 0 ? noop.noop : _ref2$onDismiss,
129 onKeyDown = _ref2.onKeyDown,
130 onMouseDown = _ref2.onMouseDown,
131 unstable_lockFocusAcrossFrames = _ref2.unstable_lockFocusAcrossFrames,
132 props = _objectWithoutPropertiesLoose(_ref2, _excluded2);
133
134 var mouseDownTarget = React.useRef(null);
135 var overlayNode = React.useRef(null);
136 var ref = composeRefs.useComposedRefs(overlayNode, forwardedRef);
137 var activateFocusLock = React.useCallback(function () {
138 if (initialFocusRef && initialFocusRef.current) {
139 initialFocusRef.current.focus();
140 }
141 }, [initialFocusRef]);
142
143 function handleClick(event) {
144 if (mouseDownTarget.current === event.target) {
145 event.stopPropagation();
146 onDismiss(event);
147 }
148 }
149
150 function handleKeyDown(event) {
151 if (event.key === "Escape") {
152 event.stopPropagation();
153 onDismiss(event);
154 }
155 }
156
157 function handleMouseDown(event) {
158 mouseDownTarget.current = event.target;
159 }
160
161 React.useEffect(function () {
162 return overlayNode.current ? createAriaHider(overlayNode.current) : void null;
163 }, []);
164 return /*#__PURE__*/React.createElement(FocusLock__default['default'], {
165 autoFocus: true,
166 returnFocus: true,
167 onActivation: activateFocusLock,
168 disabled: dangerouslyBypassFocusLock,
169 crossFrame: unstable_lockFocusAcrossFrames != null ? unstable_lockFocusAcrossFrames : true
170 }, /*#__PURE__*/React.createElement(reactRemoveScroll.RemoveScroll, {
171 allowPinchZoom: allowPinchZoom,
172 enabled: !dangerouslyBypassScrollLock
173 }, /*#__PURE__*/React.createElement(Comp, _extends({}, props, {
174 ref: ref,
175 "data-reach-dialog-overlay": ""
176 /*
177 * We can ignore the `no-static-element-interactions` warning here
178 * because our overlay is only designed to capture any outside
179 * clicks, not to serve as a clickable element itself.
180 */
181 ,
182 onClick: composeEventHandlers.composeEventHandlers(onClick, handleClick),
183 onKeyDown: composeEventHandlers.composeEventHandlers(onKeyDown, handleKeyDown),
184 onMouseDown: composeEventHandlers.composeEventHandlers(onMouseDown, handleMouseDown)
185 }))));
186});
187
188/**
189 * DialogContent
190 *
191 * Low-level component if you need more control over the styles or rendering of
192 * the dialog content.
193 *
194 * Note: Must be a child of `DialogOverlay`.
195 *
196 * Note: You only need to use this when you are also styling `DialogOverlay`,
197 * otherwise you can use the high-level `Dialog` component and pass the props
198 * to it. Any props passed to `Dialog` component (besides `isOpen` and
199 * `onDismiss`) will be spread onto `DialogContent`.
200 *
201 * @see Docs https://reach.tech/dialog#dialogcontent
202 */
203
204
205var DialogContent = /*#__PURE__*/React.forwardRef(function DialogContent(_ref3, forwardedRef) {
206 var _ref3$as = _ref3.as,
207 Comp = _ref3$as === void 0 ? "div" : _ref3$as,
208 onClick = _ref3.onClick;
209 _ref3.onKeyDown;
210 var props = _objectWithoutPropertiesLoose(_ref3, _excluded3);
211
212 return /*#__PURE__*/React.createElement(Comp, _extends({
213 "aria-modal": "true",
214 role: "dialog",
215 tabIndex: -1
216 }, props, {
217 ref: forwardedRef,
218 "data-reach-dialog-content": "",
219 onClick: composeEventHandlers.composeEventHandlers(onClick, function (event) {
220 event.stopPropagation();
221 })
222 }));
223});
224
225/**
226 * Dialog
227 *
228 * High-level component to render a modal dialog window over the top of the page
229 * (or another dialog).
230 *
231 * @see Docs https://reach.tech/dialog#dialog
232 */
233
234
235var Dialog = /*#__PURE__*/React.forwardRef(function Dialog(_ref4, forwardedRef) {
236 var _ref4$allowPinchZoom = _ref4.allowPinchZoom,
237 allowPinchZoom = _ref4$allowPinchZoom === void 0 ? false : _ref4$allowPinchZoom,
238 initialFocusRef = _ref4.initialFocusRef,
239 isOpen = _ref4.isOpen,
240 _ref4$onDismiss = _ref4.onDismiss,
241 onDismiss = _ref4$onDismiss === void 0 ? noop.noop : _ref4$onDismiss,
242 props = _objectWithoutPropertiesLoose(_ref4, _excluded4);
243
244 return /*#__PURE__*/React.createElement(DialogOverlay, {
245 allowPinchZoom: allowPinchZoom,
246 initialFocusRef: initialFocusRef,
247 isOpen: isOpen,
248 onDismiss: onDismiss
249 }, /*#__PURE__*/React.createElement(DialogContent, _extends({
250 ref: forwardedRef
251 }, props)));
252});
253
254
255function createAriaHider(dialogNode) {
256 var originalValues = [];
257 var rootNodes = [];
258 var ownerDocument$1 = ownerDocument.getOwnerDocument(dialogNode);
259
260 if (!dialogNode) {
261
262 return noop.noop;
263 }
264
265 Array.prototype.forEach.call(ownerDocument$1.querySelectorAll("body > *"), function (node) {
266 var _dialogNode$parentNod, _dialogNode$parentNod2;
267
268 var portalNode = (_dialogNode$parentNod = dialogNode.parentNode) == null ? void 0 : (_dialogNode$parentNod2 = _dialogNode$parentNod.parentNode) == null ? void 0 : _dialogNode$parentNod2.parentNode;
269
270 if (node === portalNode) {
271 return;
272 }
273
274 var attr = node.getAttribute("aria-hidden");
275 var alreadyHidden = attr !== null && attr !== "false";
276
277 if (alreadyHidden) {
278 return;
279 }
280
281 originalValues.push(attr);
282 rootNodes.push(node);
283 node.setAttribute("aria-hidden", "true");
284 });
285 return function () {
286 rootNodes.forEach(function (node, index) {
287 var originalValue = originalValues[index];
288
289 if (originalValue === null) {
290 node.removeAttribute("aria-hidden");
291 } else {
292 node.setAttribute("aria-hidden", originalValue);
293 }
294 });
295 };
296}
297
298exports.Dialog = Dialog;
299exports.DialogContent = DialogContent;
300exports.DialogOverlay = DialogOverlay;
301exports.default = Dialog;