UNPKG

7.47 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import PropTypes from 'prop-types';
4import React, { useState } from 'react';
5import ReactDOM from 'react-dom';
6import useCallbackRef from '@restart/hooks/useCallbackRef';
7import useMergedRefs from '@restart/hooks/useMergedRefs';
8import { placements } from './popper';
9import usePopper from './usePopper';
10import useRootClose from './useRootClose';
11import useWaitForDOMRef from './useWaitForDOMRef';
12import mergeOptionsWithPopperConfig from './mergeOptionsWithPopperConfig';
13
14/**
15 * Built on top of `Popper.js`, the overlay component is
16 * great for custom tooltip overlays.
17 */
18var Overlay = /*#__PURE__*/React.forwardRef(function (props, outerRef) {
19 var flip = props.flip,
20 offset = props.offset,
21 placement = props.placement,
22 _props$containerPaddi = props.containerPadding,
23 containerPadding = _props$containerPaddi === void 0 ? 5 : _props$containerPaddi,
24 _props$popperConfig = props.popperConfig,
25 popperConfig = _props$popperConfig === void 0 ? {} : _props$popperConfig,
26 Transition = props.transition;
27
28 var _useCallbackRef = useCallbackRef(),
29 rootElement = _useCallbackRef[0],
30 attachRef = _useCallbackRef[1];
31
32 var _useCallbackRef2 = useCallbackRef(),
33 arrowElement = _useCallbackRef2[0],
34 attachArrowRef = _useCallbackRef2[1];
35
36 var mergedRef = useMergedRefs(attachRef, outerRef);
37 var container = useWaitForDOMRef(props.container);
38 var target = useWaitForDOMRef(props.target);
39
40 var _useState = useState(!props.show),
41 exited = _useState[0],
42 setExited = _useState[1];
43
44 var _usePopper = usePopper(target, rootElement, mergeOptionsWithPopperConfig({
45 placement: placement,
46 enableEvents: !!props.show,
47 containerPadding: containerPadding || 5,
48 flip: flip,
49 offset: offset,
50 arrowElement: arrowElement,
51 popperConfig: popperConfig
52 })),
53 styles = _usePopper.styles,
54 attributes = _usePopper.attributes,
55 popper = _objectWithoutPropertiesLoose(_usePopper, ["styles", "attributes"]);
56
57 if (props.show) {
58 if (exited) setExited(false);
59 } else if (!props.transition && !exited) {
60 setExited(true);
61 }
62
63 var handleHidden = function handleHidden() {
64 setExited(true);
65
66 if (props.onExited) {
67 props.onExited.apply(props, arguments);
68 }
69 }; // Don't un-render the overlay while it's transitioning out.
70
71
72 var mountOverlay = props.show || Transition && !exited;
73 useRootClose(rootElement, props.onHide, {
74 disabled: !props.rootClose || props.rootCloseDisabled,
75 clickTrigger: props.rootCloseEvent
76 });
77
78 if (!mountOverlay) {
79 // Don't bother showing anything if we don't have to.
80 return null;
81 }
82
83 var child = props.children(_extends({}, popper, {
84 show: !!props.show,
85 props: _extends({}, attributes.popper, {
86 style: styles.popper,
87 ref: mergedRef
88 }),
89 arrowProps: _extends({}, attributes.arrow, {
90 style: styles.arrow,
91 ref: attachArrowRef
92 })
93 }));
94
95 if (Transition) {
96 var onExit = props.onExit,
97 onExiting = props.onExiting,
98 onEnter = props.onEnter,
99 onEntering = props.onEntering,
100 onEntered = props.onEntered;
101 child = /*#__PURE__*/React.createElement(Transition, {
102 "in": props.show,
103 appear: true,
104 onExit: onExit,
105 onExiting: onExiting,
106 onExited: handleHidden,
107 onEnter: onEnter,
108 onEntering: onEntering,
109 onEntered: onEntered
110 }, child);
111 }
112
113 return container ? /*#__PURE__*/ReactDOM.createPortal(child, container) : null;
114});
115Overlay.displayName = 'Overlay';
116Overlay.propTypes = {
117 /**
118 * Set the visibility of the Overlay
119 */
120 show: PropTypes.bool,
121
122 /** Specify where the overlay element is positioned in relation to the target element */
123 placement: PropTypes.oneOf(placements),
124
125 /**
126 * A DOM Element, Ref to an element, or function that returns either. The `target` element is where
127 * the overlay is positioned relative to.
128 */
129 target: PropTypes.any,
130
131 /**
132 * A DOM Element, Ref to an element, or function that returns either. The `container` will have the Portal children
133 * appended to it.
134 */
135 container: PropTypes.any,
136
137 /**
138 * Enables the Popper.js `flip` modifier, allowing the Overlay to
139 * automatically adjust it's placement in case of overlap with the viewport or toggle.
140 * Refer to the [flip docs](https://popper.js.org/popper-documentation.html#modifiers..flip.enabled) for more info
141 */
142 flip: PropTypes.bool,
143
144 /**
145 * A render prop that returns an element to overlay and position. See
146 * the [react-popper documentation](https://github.com/FezVrasta/react-popper#children) for more info.
147 *
148 * @type {Function ({
149 * show: boolean,
150 * placement: Placement,
151 * update: () => void,
152 * forceUpdate: () => void,
153 * props: {
154 * ref: (?HTMLElement) => void,
155 * style: { [string]: string | number },
156 * aria-labelledby: ?string
157 * [string]: string | number,
158 * },
159 * arrowProps: {
160 * ref: (?HTMLElement) => void,
161 * style: { [string]: string | number },
162 * [string]: string | number,
163 * },
164 * }) => React.Element}
165 */
166 children: PropTypes.func.isRequired,
167
168 /**
169 * Control how much space there is between the edge of the boundary element and overlay.
170 * A convenience shortcut to setting `popperConfig.modfiers.preventOverflow.padding`
171 */
172 containerPadding: PropTypes.number,
173
174 /**
175 * A set of popper options and props passed directly to react-popper's Popper component.
176 */
177 popperConfig: PropTypes.object,
178
179 /**
180 * Specify whether the overlay should trigger `onHide` when the user clicks outside the overlay
181 */
182 rootClose: PropTypes.bool,
183
184 /**
185 * Specify event for toggling overlay
186 */
187 rootCloseEvent: PropTypes.oneOf(['click', 'mousedown']),
188
189 /**
190 * Specify disabled for disable RootCloseWrapper
191 */
192 rootCloseDisabled: PropTypes.bool,
193
194 /**
195 * A Callback fired by the Overlay when it wishes to be hidden.
196 *
197 * __required__ when `rootClose` is `true`.
198 *
199 * @type func
200 */
201 onHide: function onHide(props) {
202 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
203 args[_key - 1] = arguments[_key];
204 }
205
206 if (props.rootClose) {
207 var _PropTypes$func;
208
209 return (_PropTypes$func = PropTypes.func).isRequired.apply(_PropTypes$func, [props].concat(args));
210 }
211
212 return PropTypes.func.apply(PropTypes, [props].concat(args));
213 },
214
215 /**
216 * A `react-transition-group@2.0.0` `<Transition/>` component
217 * used to animate the overlay as it changes visibility.
218 */
219 // @ts-ignore
220 transition: PropTypes.elementType,
221
222 /**
223 * Callback fired before the Overlay transitions in
224 */
225 onEnter: PropTypes.func,
226
227 /**
228 * Callback fired as the Overlay begins to transition in
229 */
230 onEntering: PropTypes.func,
231
232 /**
233 * Callback fired after the Overlay finishes transitioning in
234 */
235 onEntered: PropTypes.func,
236
237 /**
238 * Callback fired right before the Overlay transitions out
239 */
240 onExit: PropTypes.func,
241
242 /**
243 * Callback fired as the Overlay begins to transition out
244 */
245 onExiting: PropTypes.func,
246
247 /**
248 * Callback fired after the Overlay finishes transitioning out
249 */
250 onExited: PropTypes.func
251};
252export default Overlay;
\No newline at end of file