UNPKG

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