UNPKG

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