UNPKG

13.9 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3const _excluded = ["anchorEl", "children", "direction", "disablePortal", "modifiers", "open", "placement", "popperOptions", "popperRef", "slotProps", "slots", "TransitionProps", "ownerState"],
4 _excluded2 = ["anchorEl", "children", "container", "direction", "disablePortal", "keepMounted", "modifiers", "open", "placement", "popperOptions", "popperRef", "style", "transition", "slotProps", "slots"];
5import * as React from 'react';
6import { chainPropTypes, HTMLElementType, refType, unstable_ownerDocument as ownerDocument, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useForkRef as useForkRef } from '@mui/utils';
7import { createPopper } from '@popperjs/core';
8import PropTypes from 'prop-types';
9import composeClasses from '../composeClasses';
10import Portal from '../Portal';
11import { getPopperUtilityClass } from './popperClasses';
12import { useSlotProps } from '../utils';
13import { useClassNamesOverride } from '../utils/ClassNameConfigurator';
14import { jsx as _jsx } from "react/jsx-runtime";
15function flipPlacement(placement, direction) {
16 if (direction === 'ltr') {
17 return placement;
18 }
19 switch (placement) {
20 case 'bottom-end':
21 return 'bottom-start';
22 case 'bottom-start':
23 return 'bottom-end';
24 case 'top-end':
25 return 'top-start';
26 case 'top-start':
27 return 'top-end';
28 default:
29 return placement;
30 }
31}
32function resolveAnchorEl(anchorEl) {
33 return typeof anchorEl === 'function' ? anchorEl() : anchorEl;
34}
35function isHTMLElement(element) {
36 return element.nodeType !== undefined;
37}
38function isVirtualElement(element) {
39 return !isHTMLElement(element);
40}
41const useUtilityClasses = () => {
42 const slots = {
43 root: ['root']
44 };
45 return composeClasses(slots, useClassNamesOverride(getPopperUtilityClass));
46};
47const defaultPopperOptions = {};
48const PopperTooltip = /*#__PURE__*/React.forwardRef(function PopperTooltip(props, forwardedRef) {
49 const {
50 anchorEl,
51 children,
52 direction,
53 disablePortal,
54 modifiers,
55 open,
56 placement: initialPlacement,
57 popperOptions,
58 popperRef: popperRefProp,
59 slotProps = {},
60 slots = {},
61 TransitionProps
62 // @ts-ignore internal logic
63 // prevent from spreading to DOM, it can come from the parent component e.g. Select.
64 } = props,
65 other = _objectWithoutPropertiesLoose(props, _excluded);
66 const tooltipRef = React.useRef(null);
67 const ownRef = useForkRef(tooltipRef, forwardedRef);
68 const popperRef = React.useRef(null);
69 const handlePopperRef = useForkRef(popperRef, popperRefProp);
70 const handlePopperRefRef = React.useRef(handlePopperRef);
71 useEnhancedEffect(() => {
72 handlePopperRefRef.current = handlePopperRef;
73 }, [handlePopperRef]);
74 React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
75 const rtlPlacement = flipPlacement(initialPlacement, direction);
76 /**
77 * placement initialized from prop but can change during lifetime if modifiers.flip.
78 * modifiers.flip is essentially a flip for controlled/uncontrolled behavior
79 */
80 const [placement, setPlacement] = React.useState(rtlPlacement);
81 const [resolvedAnchorElement, setResolvedAnchorElement] = React.useState(resolveAnchorEl(anchorEl));
82 React.useEffect(() => {
83 if (popperRef.current) {
84 popperRef.current.forceUpdate();
85 }
86 });
87 React.useEffect(() => {
88 if (anchorEl) {
89 setResolvedAnchorElement(resolveAnchorEl(anchorEl));
90 }
91 }, [anchorEl]);
92 useEnhancedEffect(() => {
93 if (!resolvedAnchorElement || !open) {
94 return undefined;
95 }
96 const handlePopperUpdate = data => {
97 setPlacement(data.placement);
98 };
99 if (process.env.NODE_ENV !== 'production') {
100 if (resolvedAnchorElement && isHTMLElement(resolvedAnchorElement) && resolvedAnchorElement.nodeType === 1) {
101 const box = resolvedAnchorElement.getBoundingClientRect();
102 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
103 console.warn(['MUI: 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'));
104 }
105 }
106 }
107 let popperModifiers = [{
108 name: 'preventOverflow',
109 options: {
110 altBoundary: disablePortal
111 }
112 }, {
113 name: 'flip',
114 options: {
115 altBoundary: disablePortal
116 }
117 }, {
118 name: 'onUpdate',
119 enabled: true,
120 phase: 'afterWrite',
121 fn: ({
122 state
123 }) => {
124 handlePopperUpdate(state);
125 }
126 }];
127 if (modifiers != null) {
128 popperModifiers = popperModifiers.concat(modifiers);
129 }
130 if (popperOptions && popperOptions.modifiers != null) {
131 popperModifiers = popperModifiers.concat(popperOptions.modifiers);
132 }
133 const popper = createPopper(resolvedAnchorElement, tooltipRef.current, _extends({
134 placement: rtlPlacement
135 }, popperOptions, {
136 modifiers: popperModifiers
137 }));
138 handlePopperRefRef.current(popper);
139 return () => {
140 popper.destroy();
141 handlePopperRefRef.current(null);
142 };
143 }, [resolvedAnchorElement, disablePortal, modifiers, open, popperOptions, rtlPlacement]);
144 const childProps = {
145 placement: placement
146 };
147 if (TransitionProps !== null) {
148 childProps.TransitionProps = TransitionProps;
149 }
150 const classes = useUtilityClasses();
151 const Root = slots.root ?? 'div';
152 const rootProps = useSlotProps({
153 elementType: Root,
154 externalSlotProps: slotProps.root,
155 externalForwardedProps: other,
156 additionalProps: {
157 role: 'tooltip',
158 ref: ownRef
159 },
160 ownerState: props,
161 className: classes.root
162 });
163 return /*#__PURE__*/_jsx(Root, _extends({}, rootProps, {
164 children: typeof children === 'function' ? children(childProps) : children
165 }));
166});
167
168/**
169 * Poppers rely on the 3rd party library [Popper.js](https://popper.js.org/docs/v2/) for positioning.
170 *
171 * Demos:
172 *
173 * - [Popper](https://mui.com/base/react-popper/)
174 *
175 * API:
176 *
177 * - [Popper API](https://mui.com/base/react-popper/components-api/#popper)
178 */
179const Popper = /*#__PURE__*/React.forwardRef(function Popper(props, forwardedRef) {
180 const {
181 anchorEl,
182 children,
183 container: containerProp,
184 direction = 'ltr',
185 disablePortal = false,
186 keepMounted = false,
187 modifiers,
188 open,
189 placement = 'bottom',
190 popperOptions = defaultPopperOptions,
191 popperRef,
192 style,
193 transition = false,
194 slotProps = {},
195 slots = {}
196 } = props,
197 other = _objectWithoutPropertiesLoose(props, _excluded2);
198 const [exited, setExited] = React.useState(true);
199 const handleEnter = () => {
200 setExited(false);
201 };
202 const handleExited = () => {
203 setExited(true);
204 };
205 if (!keepMounted && !open && (!transition || exited)) {
206 return null;
207 }
208
209 // If the container prop is provided, use that
210 // If the anchorEl prop is provided, use its parent body element as the container
211 // If neither are provided let the Modal take care of choosing the container
212 let container;
213 if (containerProp) {
214 container = containerProp;
215 } else if (anchorEl) {
216 const resolvedAnchorEl = resolveAnchorEl(anchorEl);
217 container = resolvedAnchorEl && isHTMLElement(resolvedAnchorEl) ? ownerDocument(resolvedAnchorEl).body : ownerDocument(null).body;
218 }
219 const display = !open && keepMounted && (!transition || exited) ? 'none' : undefined;
220 const transitionProps = transition ? {
221 in: open,
222 onEnter: handleEnter,
223 onExited: handleExited
224 } : undefined;
225 return /*#__PURE__*/_jsx(Portal, {
226 disablePortal: disablePortal,
227 container: container,
228 children: /*#__PURE__*/_jsx(PopperTooltip, _extends({
229 anchorEl: anchorEl,
230 direction: direction,
231 disablePortal: disablePortal,
232 modifiers: modifiers,
233 ref: forwardedRef,
234 open: transition ? !exited : open,
235 placement: placement,
236 popperOptions: popperOptions,
237 popperRef: popperRef,
238 slotProps: slotProps,
239 slots: slots
240 }, other, {
241 style: _extends({
242 // Prevents scroll issue, waiting for Popper.js to add this style once initiated.
243 position: 'fixed',
244 // Fix Popper.js display issue
245 top: 0,
246 left: 0,
247 display
248 }, style),
249 TransitionProps: transitionProps,
250 children: children
251 }))
252 });
253});
254process.env.NODE_ENV !== "production" ? Popper.propTypes /* remove-proptypes */ = {
255 // ----------------------------- Warning --------------------------------
256 // | These PropTypes are generated from the TypeScript type definitions |
257 // | To update them edit TypeScript types and run "yarn proptypes" |
258 // ----------------------------------------------------------------------
259 /**
260 * An HTML element, [virtualElement](https://popper.js.org/docs/v2/virtual-elements/),
261 * or a function that returns either.
262 * It's used to set the position of the popper.
263 * The return value will passed as the reference object of the Popper instance.
264 */
265 anchorEl: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.object, PropTypes.func]), props => {
266 if (props.open) {
267 const resolvedAnchorEl = resolveAnchorEl(props.anchorEl);
268 if (resolvedAnchorEl && isHTMLElement(resolvedAnchorEl) && resolvedAnchorEl.nodeType === 1) {
269 const box = resolvedAnchorEl.getBoundingClientRect();
270 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
271 return new Error(['MUI: 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'));
272 }
273 } else if (!resolvedAnchorEl || typeof resolvedAnchorEl.getBoundingClientRect !== 'function' || isVirtualElement(resolvedAnchorEl) && resolvedAnchorEl.contextElement != null && resolvedAnchorEl.contextElement.nodeType !== 1) {
274 return new Error(['MUI: The `anchorEl` prop provided to the component is invalid.', 'It should be an HTML element instance or a virtualElement ', '(https://popper.js.org/docs/v2/virtual-elements/).'].join('\n'));
275 }
276 }
277 return null;
278 }),
279 /**
280 * Popper render function or node.
281 */
282 children: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.node, PropTypes.func]),
283 /**
284 * An HTML element or function that returns one.
285 * The `container` will have the portal children appended to it.
286 *
287 * By default, it uses the body of the top-level document object,
288 * so it's simply `document.body` most of the time.
289 */
290 container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.func]),
291 /**
292 * Direction of the text.
293 * @default 'ltr'
294 */
295 direction: PropTypes.oneOf(['ltr', 'rtl']),
296 /**
297 * The `children` will be under the DOM hierarchy of the parent component.
298 * @default false
299 */
300 disablePortal: PropTypes.bool,
301 /**
302 * Always keep the children in the DOM.
303 * This prop can be useful in SEO situation or
304 * when you want to maximize the responsiveness of the Popper.
305 * @default false
306 */
307 keepMounted: PropTypes.bool,
308 /**
309 * Popper.js is based on a "plugin-like" architecture,
310 * most of its features are fully encapsulated "modifiers".
311 *
312 * A modifier is a function that is called each time Popper.js needs to
313 * compute the position of the popper.
314 * For this reason, modifiers should be very performant to avoid bottlenecks.
315 * To learn how to create a modifier, [read the modifiers documentation](https://popper.js.org/docs/v2/modifiers/).
316 */
317 modifiers: PropTypes.arrayOf(PropTypes.shape({
318 data: PropTypes.object,
319 effect: PropTypes.func,
320 enabled: PropTypes.bool,
321 fn: PropTypes.func,
322 name: PropTypes.any,
323 options: PropTypes.object,
324 phase: PropTypes.oneOf(['afterMain', 'afterRead', 'afterWrite', 'beforeMain', 'beforeRead', 'beforeWrite', 'main', 'read', 'write']),
325 requires: PropTypes.arrayOf(PropTypes.string),
326 requiresIfExists: PropTypes.arrayOf(PropTypes.string)
327 })),
328 /**
329 * If `true`, the component is shown.
330 */
331 open: PropTypes.bool.isRequired,
332 /**
333 * Popper placement.
334 * @default 'bottom'
335 */
336 placement: PropTypes.oneOf(['auto-end', 'auto-start', 'auto', 'bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
337 /**
338 * Options provided to the [`Popper.js`](https://popper.js.org/docs/v2/constructors/#options) instance.
339 * @default {}
340 */
341 popperOptions: PropTypes.shape({
342 modifiers: PropTypes.array,
343 onFirstUpdate: PropTypes.func,
344 placement: PropTypes.oneOf(['auto-end', 'auto-start', 'auto', 'bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
345 strategy: PropTypes.oneOf(['absolute', 'fixed'])
346 }),
347 /**
348 * A ref that points to the used popper instance.
349 */
350 popperRef: refType,
351 /**
352 * The props used for each slot inside the Popper.
353 * @default {}
354 */
355 slotProps: PropTypes.shape({
356 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
357 }),
358 /**
359 * The components used for each slot inside the Popper.
360 * Either a string to use a HTML element or a component.
361 * @default {}
362 */
363 slots: PropTypes.shape({
364 root: PropTypes.elementType
365 }),
366 /**
367 * Help supporting a react-transition-group/Transition component.
368 * @default false
369 */
370 transition: PropTypes.bool
371} : void 0;
372export default Popper;
\No newline at end of file