UNPKG

11.8 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.default = void 0;
9
10var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
12var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
13
14var _react = _interopRequireDefault(require("react"));
15
16var _propTypes = _interopRequireDefault(require("prop-types"));
17
18var _popper = _interopRequireDefault(require("popper.js"));
19
20var _utils = require("@material-ui/utils");
21
22var _styles = require("@material-ui/styles");
23
24var _Portal = _interopRequireDefault(require("../Portal"));
25
26var _createChainedFunction = _interopRequireDefault(require("../utils/createChainedFunction"));
27
28var _setRef = _interopRequireDefault(require("../utils/setRef"));
29
30var _useForkRef = _interopRequireDefault(require("../utils/useForkRef"));
31
32var _ownerWindow = _interopRequireDefault(require("../utils/ownerWindow"));
33
34function flipPlacement(placement, theme) {
35 var direction = theme && theme.direction || 'ltr';
36
37 if (direction === 'ltr') {
38 return placement;
39 }
40
41 switch (placement) {
42 case 'bottom-end':
43 return 'bottom-start';
44
45 case 'bottom-start':
46 return 'bottom-end';
47
48 case 'top-end':
49 return 'top-start';
50
51 case 'top-start':
52 return 'top-end';
53
54 default:
55 return placement;
56 }
57}
58
59function getAnchorEl(anchorEl) {
60 return typeof anchorEl === 'function' ? anchorEl() : anchorEl;
61}
62
63var useEnhancedEffect = typeof window !== 'undefined' ? _react.default.useLayoutEffect : _react.default.useEffect;
64var defaultPopperOptions = {};
65/**
66 * Poppers rely on the 3rd party library [Popper.js](https://github.com/FezVrasta/popper.js) for positioning.
67 */
68
69var Popper = _react.default.forwardRef(function Popper(props, ref) {
70 var anchorEl = props.anchorEl,
71 children = props.children,
72 container = props.container,
73 _props$disablePortal = props.disablePortal,
74 disablePortal = _props$disablePortal === void 0 ? false : _props$disablePortal,
75 _props$keepMounted = props.keepMounted,
76 keepMounted = _props$keepMounted === void 0 ? false : _props$keepMounted,
77 modifiers = props.modifiers,
78 open = props.open,
79 _props$placement = props.placement,
80 initialPlacement = _props$placement === void 0 ? 'bottom' : _props$placement,
81 _props$popperOptions = props.popperOptions,
82 popperOptions = _props$popperOptions === void 0 ? defaultPopperOptions : _props$popperOptions,
83 popperRefProp = props.popperRef,
84 _props$transition = props.transition,
85 transition = _props$transition === void 0 ? false : _props$transition,
86 other = (0, _objectWithoutProperties2.default)(props, ["anchorEl", "children", "container", "disablePortal", "keepMounted", "modifiers", "open", "placement", "popperOptions", "popperRef", "transition"]);
87
88 var tooltipRef = _react.default.useRef(null);
89
90 var ownRef = (0, _useForkRef.default)(tooltipRef, ref);
91
92 var popperRef = _react.default.useRef(null);
93
94 var handlePopperRef = (0, _useForkRef.default)(popperRef, popperRefProp);
95
96 var handlePopperRefRef = _react.default.useRef(handlePopperRef);
97
98 useEnhancedEffect(function () {
99 handlePopperRefRef.current = handlePopperRef;
100 }, [handlePopperRef]);
101
102 _react.default.useImperativeHandle(popperRefProp, function () {
103 return popperRef.current;
104 }, []);
105
106 var _React$useState = _react.default.useState(true),
107 exited = _React$useState[0],
108 setExited = _React$useState[1];
109
110 var theme = (0, _styles.useTheme)();
111 var rtlPlacement = flipPlacement(initialPlacement, theme);
112 /**
113 * placement initialized from prop but can change during lifetime if modifiers.flip.
114 * modifiers.flip is essentially a flip for controlled/uncontrolled behavior
115 */
116
117 var _React$useState2 = _react.default.useState(rtlPlacement),
118 placement = _React$useState2[0],
119 setPlacement = _React$useState2[1];
120
121 _react.default.useEffect(function () {
122 if (popperRef.current) {
123 popperRef.current.update();
124 }
125 });
126
127 var handleOpen = _react.default.useCallback(function () {
128 if (!tooltipRef.current || !anchorEl || !open) {
129 return;
130 }
131
132 if (popperRef.current) {
133 popperRef.current.destroy();
134 handlePopperRefRef.current(null);
135 }
136
137 var handlePopperUpdate = function handlePopperUpdate(data) {
138 setPlacement(data.placement);
139 };
140
141 var resolvedAnchorEl = getAnchorEl(anchorEl);
142
143 if (process.env.NODE_ENV !== 'production') {
144 var containerWindow = (0, _ownerWindow.default)(resolvedAnchorEl);
145
146 if (resolvedAnchorEl instanceof containerWindow.Element) {
147 var box = resolvedAnchorEl.getBoundingClientRect();
148
149 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
150 console.warn(['Material-UI: the `anchorEl` prop provided to the component is invalid.', 'The anchor element should be part of the document layout.', "Make sure the element is present in the document or that it's not display none."].join('\n'));
151 }
152 }
153 }
154
155 var popper = new _popper.default(getAnchorEl(anchorEl), tooltipRef.current, (0, _extends2.default)({
156 placement: rtlPlacement
157 }, popperOptions, {
158 modifiers: (0, _extends2.default)({}, disablePortal ? {} : {
159 // It's using scrollParent by default, we can use the viewport when using a portal.
160 preventOverflow: {
161 boundariesElement: 'window'
162 }
163 }, {}, modifiers, {}, popperOptions.modifiers),
164 // We could have been using a custom modifier like react-popper is doing.
165 // But it seems this is the best public API for this use case.
166 onCreate: (0, _createChainedFunction.default)(handlePopperUpdate, popperOptions.onCreate),
167 onUpdate: (0, _createChainedFunction.default)(handlePopperUpdate, popperOptions.onUpdate)
168 }));
169 handlePopperRefRef.current(popper);
170 }, [anchorEl, disablePortal, modifiers, open, rtlPlacement, popperOptions]);
171
172 var handleRef = _react.default.useCallback(function (node) {
173 (0, _setRef.default)(ownRef, node);
174 handleOpen();
175 }, [ownRef, handleOpen]);
176
177 var handleEnter = function handleEnter() {
178 setExited(false);
179 };
180
181 var handleClose = function handleClose() {
182 if (!popperRef.current) {
183 return;
184 }
185
186 popperRef.current.destroy();
187 handlePopperRefRef.current(null);
188 };
189
190 var handleExited = function handleExited() {
191 setExited(true);
192 handleClose();
193 };
194
195 _react.default.useEffect(function () {
196 // Let's update the popper position.
197 handleOpen();
198 }, [handleOpen]);
199
200 _react.default.useEffect(function () {
201 return function () {
202 handleClose();
203 };
204 }, []);
205
206 _react.default.useEffect(function () {
207 if (!open && !transition) {
208 // Otherwise handleExited will call this.
209 handleClose();
210 }
211 }, [open, transition]);
212
213 if (!keepMounted && !open && (!transition || exited)) {
214 return null;
215 }
216
217 var childProps = {
218 placement: placement
219 };
220
221 if (transition) {
222 childProps.TransitionProps = {
223 in: open,
224 onEnter: handleEnter,
225 onExited: handleExited
226 };
227 }
228
229 return _react.default.createElement(_Portal.default, {
230 disablePortal: disablePortal,
231 container: container
232 }, _react.default.createElement("div", (0, _extends2.default)({
233 ref: handleRef,
234 role: "tooltip"
235 }, other, {
236 style: (0, _extends2.default)({
237 // Prevents scroll issue, waiting for Popper.js to add this style once initiated.
238 position: 'fixed',
239 // Fix Popper.js display issue
240 top: 0,
241 left: 0
242 }, other.style)
243 }), typeof children === 'function' ? children(childProps) : children));
244});
245
246process.env.NODE_ENV !== "production" ? Popper.propTypes = {
247 /**
248 * This is the reference element, or a function that returns the reference element,
249 * that may be used to set the position of the popover.
250 * The return value will passed as the reference object of the Popper
251 * instance.
252 *
253 * The reference element should be an HTML Element instance or a referenceObject:
254 * https://popper.js.org/popper-documentation.html#referenceObject.
255 */
256 anchorEl: (0, _utils.chainPropTypes)(_propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]), function (props) {
257 if (props.open) {
258 var resolvedAnchorEl = getAnchorEl(props.anchorEl);
259 var containerWindow = (0, _ownerWindow.default)(resolvedAnchorEl);
260
261 if (resolvedAnchorEl instanceof containerWindow.Element) {
262 var box = resolvedAnchorEl.getBoundingClientRect();
263
264 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
265 return new Error(['Material-UI: the `anchorEl` prop provided to the component is invalid.', 'The anchor element should be part of the document layout.', "Make sure the element is present in the document or that it's not display none."].join('\n'));
266 }
267 } else if (!resolvedAnchorEl || typeof resolvedAnchorEl.clientWidth !== 'number' || typeof resolvedAnchorEl.clientHeight !== 'number' || typeof resolvedAnchorEl.getBoundingClientRect !== 'function') {
268 return new Error(['Material-UI: the `anchorEl` prop provided to the component is invalid.', 'It should be an HTML Element instance or a referenceObject:', 'https://popper.js.org/popper-documentation.html#referenceObject.'].join('\n'));
269 }
270 }
271
272 return null;
273 }),
274
275 /**
276 * Popper render function or node.
277 */
278 children: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func]).isRequired,
279
280 /**
281 * A node, component instance, or function that returns either.
282 * The `container` will passed to the Modal component.
283 * By default, it uses the body of the anchorEl's top-level document object,
284 * so it's simply `document.body` most of the time.
285 */
286 container: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]),
287
288 /**
289 * Disable the portal behavior.
290 * The children stay within it's parent DOM hierarchy.
291 */
292 disablePortal: _propTypes.default.bool,
293
294 /**
295 * Always keep the children in the DOM.
296 * This prop can be useful in SEO situation or
297 * when you want to maximize the responsiveness of the Popper.
298 */
299 keepMounted: _propTypes.default.bool,
300
301 /**
302 * Popper.js is based on a "plugin-like" architecture,
303 * most of its features are fully encapsulated "modifiers".
304 *
305 * A modifier is a function that is called each time Popper.js needs to
306 * compute the position of the popper.
307 * For this reason, modifiers should be very performant to avoid bottlenecks.
308 * To learn how to create a modifier, [read the modifiers documentation](https://github.com/FezVrasta/popper.js/blob/master/docs/_includes/popper-documentation.md#modifiers--object).
309 */
310 modifiers: _propTypes.default.object,
311
312 /**
313 * If `true`, the popper is visible.
314 */
315 open: _propTypes.default.bool.isRequired,
316
317 /**
318 * Popper placement.
319 */
320 placement: _propTypes.default.oneOf(['bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
321
322 /**
323 * Options provided to the [`popper.js`](https://github.com/FezVrasta/popper.js) instance.
324 */
325 popperOptions: _propTypes.default.object,
326
327 /**
328 * A ref that points to the used popper instance.
329 */
330 popperRef: _utils.refType,
331
332 /**
333 * Help supporting a react-transition-group/Transition component.
334 */
335 transition: _propTypes.default.bool
336} : void 0;
337var _default = Popper;
338exports.default = _default;
\No newline at end of file