UNPKG

21.8 kBJavaScriptView Raw
1'use client';
2
3import _extends from "@babel/runtime/helpers/esm/extends";
4import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
5import * as React from 'react';
6import PropTypes from 'prop-types';
7import clsx from 'clsx';
8import { useSlotProps, isHostComponent } from '@mui/base/utils';
9import composeClasses from '@mui/utils/composeClasses';
10import HTMLElementType from '@mui/utils/HTMLElementType';
11import refType from '@mui/utils/refType';
12import elementTypeAcceptingRef from '@mui/utils/elementTypeAcceptingRef';
13import integerPropType from '@mui/utils/integerPropType';
14import chainPropTypes from '@mui/utils/chainPropTypes';
15import styled from '../styles/styled';
16import { useDefaultProps } from '../DefaultPropsProvider';
17import debounce from '../utils/debounce';
18import ownerDocument from '../utils/ownerDocument';
19import ownerWindow from '../utils/ownerWindow';
20import useForkRef from '../utils/useForkRef';
21import Grow from '../Grow';
22import Modal from '../Modal';
23import PaperBase from '../Paper';
24import { getPopoverUtilityClass } from './popoverClasses';
25import { jsx as _jsx } from "react/jsx-runtime";
26export function getOffsetTop(rect, vertical) {
27 var offset = 0;
28 if (typeof vertical === 'number') {
29 offset = vertical;
30 } else if (vertical === 'center') {
31 offset = rect.height / 2;
32 } else if (vertical === 'bottom') {
33 offset = rect.height;
34 }
35 return offset;
36}
37export function getOffsetLeft(rect, horizontal) {
38 var offset = 0;
39 if (typeof horizontal === 'number') {
40 offset = horizontal;
41 } else if (horizontal === 'center') {
42 offset = rect.width / 2;
43 } else if (horizontal === 'right') {
44 offset = rect.width;
45 }
46 return offset;
47}
48function getTransformOriginValue(transformOrigin) {
49 return [transformOrigin.horizontal, transformOrigin.vertical].map(function (n) {
50 return typeof n === 'number' ? "".concat(n, "px") : n;
51 }).join(' ');
52}
53function resolveAnchorEl(anchorEl) {
54 return typeof anchorEl === 'function' ? anchorEl() : anchorEl;
55}
56var useUtilityClasses = function useUtilityClasses(ownerState) {
57 var classes = ownerState.classes;
58 var slots = {
59 root: ['root'],
60 paper: ['paper']
61 };
62 return composeClasses(slots, getPopoverUtilityClass, classes);
63};
64export var PopoverRoot = styled(Modal, {
65 name: 'MuiPopover',
66 slot: 'Root',
67 overridesResolver: function overridesResolver(props, styles) {
68 return styles.root;
69 }
70})({});
71export var PopoverPaper = styled(PaperBase, {
72 name: 'MuiPopover',
73 slot: 'Paper',
74 overridesResolver: function overridesResolver(props, styles) {
75 return styles.paper;
76 }
77})({
78 position: 'absolute',
79 overflowY: 'auto',
80 overflowX: 'hidden',
81 // So we see the popover when it's empty.
82 // It's most likely on issue on userland.
83 minWidth: 16,
84 minHeight: 16,
85 maxWidth: 'calc(100% - 32px)',
86 maxHeight: 'calc(100% - 32px)',
87 // We disable the focus ring for mouse, touch and keyboard users.
88 outline: 0
89});
90var Popover = /*#__PURE__*/React.forwardRef(function Popover(inProps, ref) {
91 var _slotProps$paper, _slots$root, _slots$paper;
92 var props = useDefaultProps({
93 props: inProps,
94 name: 'MuiPopover'
95 });
96 var action = props.action,
97 anchorEl = props.anchorEl,
98 _props$anchorOrigin = props.anchorOrigin,
99 anchorOrigin = _props$anchorOrigin === void 0 ? {
100 vertical: 'top',
101 horizontal: 'left'
102 } : _props$anchorOrigin,
103 anchorPosition = props.anchorPosition,
104 _props$anchorReferenc = props.anchorReference,
105 anchorReference = _props$anchorReferenc === void 0 ? 'anchorEl' : _props$anchorReferenc,
106 children = props.children,
107 className = props.className,
108 containerProp = props.container,
109 _props$elevation = props.elevation,
110 elevation = _props$elevation === void 0 ? 8 : _props$elevation,
111 _props$marginThreshol = props.marginThreshold,
112 marginThreshold = _props$marginThreshol === void 0 ? 16 : _props$marginThreshol,
113 open = props.open,
114 _props$PaperProps = props.PaperProps,
115 PaperPropsProp = _props$PaperProps === void 0 ? {} : _props$PaperProps,
116 slots = props.slots,
117 slotProps = props.slotProps,
118 _props$transformOrigi = props.transformOrigin,
119 transformOrigin = _props$transformOrigi === void 0 ? {
120 vertical: 'top',
121 horizontal: 'left'
122 } : _props$transformOrigi,
123 _props$TransitionComp = props.TransitionComponent,
124 TransitionComponent = _props$TransitionComp === void 0 ? Grow : _props$TransitionComp,
125 _props$transitionDura = props.transitionDuration,
126 transitionDurationProp = _props$transitionDura === void 0 ? 'auto' : _props$transitionDura,
127 _props$TransitionProp = props.TransitionProps,
128 _props$TransitionProp2 = _props$TransitionProp === void 0 ? {} : _props$TransitionProp,
129 onEntering = _props$TransitionProp2.onEntering,
130 TransitionProps = _objectWithoutProperties(_props$TransitionProp2, ["onEntering"]),
131 _props$disableScrollL = props.disableScrollLock,
132 disableScrollLock = _props$disableScrollL === void 0 ? false : _props$disableScrollL,
133 other = _objectWithoutProperties(props, ["action", "anchorEl", "anchorOrigin", "anchorPosition", "anchorReference", "children", "className", "container", "elevation", "marginThreshold", "open", "PaperProps", "slots", "slotProps", "transformOrigin", "TransitionComponent", "transitionDuration", "TransitionProps", "disableScrollLock"]);
134 var externalPaperSlotProps = (_slotProps$paper = slotProps == null ? void 0 : slotProps.paper) != null ? _slotProps$paper : PaperPropsProp;
135 var paperRef = React.useRef();
136 var handlePaperRef = useForkRef(paperRef, externalPaperSlotProps.ref);
137 var ownerState = _extends({}, props, {
138 anchorOrigin: anchorOrigin,
139 anchorReference: anchorReference,
140 elevation: elevation,
141 marginThreshold: marginThreshold,
142 externalPaperSlotProps: externalPaperSlotProps,
143 transformOrigin: transformOrigin,
144 TransitionComponent: TransitionComponent,
145 transitionDuration: transitionDurationProp,
146 TransitionProps: TransitionProps
147 });
148 var classes = useUtilityClasses(ownerState);
149
150 // Returns the top/left offset of the position
151 // to attach to on the anchor element (or body if none is provided)
152 var getAnchorOffset = React.useCallback(function () {
153 if (anchorReference === 'anchorPosition') {
154 if (process.env.NODE_ENV !== 'production') {
155 if (!anchorPosition) {
156 console.error('MUI: You need to provide a `anchorPosition` prop when using ' + '<Popover anchorReference="anchorPosition" />.');
157 }
158 }
159 return anchorPosition;
160 }
161 var resolvedAnchorEl = resolveAnchorEl(anchorEl);
162
163 // If an anchor element wasn't provided, just use the parent body element of this Popover
164 var anchorElement = resolvedAnchorEl && resolvedAnchorEl.nodeType === 1 ? resolvedAnchorEl : ownerDocument(paperRef.current).body;
165 var anchorRect = anchorElement.getBoundingClientRect();
166 if (process.env.NODE_ENV !== 'production') {
167 var box = anchorElement.getBoundingClientRect();
168 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
169 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'));
170 }
171 }
172 return {
173 top: anchorRect.top + getOffsetTop(anchorRect, anchorOrigin.vertical),
174 left: anchorRect.left + getOffsetLeft(anchorRect, anchorOrigin.horizontal)
175 };
176 }, [anchorEl, anchorOrigin.horizontal, anchorOrigin.vertical, anchorPosition, anchorReference]);
177
178 // Returns the base transform origin using the element
179 var getTransformOrigin = React.useCallback(function (elemRect) {
180 return {
181 vertical: getOffsetTop(elemRect, transformOrigin.vertical),
182 horizontal: getOffsetLeft(elemRect, transformOrigin.horizontal)
183 };
184 }, [transformOrigin.horizontal, transformOrigin.vertical]);
185 var getPositioningStyle = React.useCallback(function (element) {
186 var elemRect = {
187 width: element.offsetWidth,
188 height: element.offsetHeight
189 };
190
191 // Get the transform origin point on the element itself
192 var elemTransformOrigin = getTransformOrigin(elemRect);
193 if (anchorReference === 'none') {
194 return {
195 top: null,
196 left: null,
197 transformOrigin: getTransformOriginValue(elemTransformOrigin)
198 };
199 }
200
201 // Get the offset of the anchoring element
202 var anchorOffset = getAnchorOffset();
203
204 // Calculate element positioning
205 var top = anchorOffset.top - elemTransformOrigin.vertical;
206 var left = anchorOffset.left - elemTransformOrigin.horizontal;
207 var bottom = top + elemRect.height;
208 var right = left + elemRect.width;
209
210 // Use the parent window of the anchorEl if provided
211 var containerWindow = ownerWindow(resolveAnchorEl(anchorEl));
212
213 // Window thresholds taking required margin into account
214 var heightThreshold = containerWindow.innerHeight - marginThreshold;
215 var widthThreshold = containerWindow.innerWidth - marginThreshold;
216
217 // Check if the vertical axis needs shifting
218 if (marginThreshold !== null && top < marginThreshold) {
219 var diff = top - marginThreshold;
220 top -= diff;
221 elemTransformOrigin.vertical += diff;
222 } else if (marginThreshold !== null && bottom > heightThreshold) {
223 var _diff = bottom - heightThreshold;
224 top -= _diff;
225 elemTransformOrigin.vertical += _diff;
226 }
227 if (process.env.NODE_ENV !== 'production') {
228 if (elemRect.height > heightThreshold && elemRect.height && heightThreshold) {
229 console.error(['MUI: The popover component is too tall.', "Some part of it can not be seen on the screen (".concat(elemRect.height - heightThreshold, "px)."), 'Please consider adding a `max-height` to improve the user-experience.'].join('\n'));
230 }
231 }
232
233 // Check if the horizontal axis needs shifting
234 if (marginThreshold !== null && left < marginThreshold) {
235 var _diff2 = left - marginThreshold;
236 left -= _diff2;
237 elemTransformOrigin.horizontal += _diff2;
238 } else if (right > widthThreshold) {
239 var _diff3 = right - widthThreshold;
240 left -= _diff3;
241 elemTransformOrigin.horizontal += _diff3;
242 }
243 return {
244 top: "".concat(Math.round(top), "px"),
245 left: "".concat(Math.round(left), "px"),
246 transformOrigin: getTransformOriginValue(elemTransformOrigin)
247 };
248 }, [anchorEl, anchorReference, getAnchorOffset, getTransformOrigin, marginThreshold]);
249 var _React$useState = React.useState(open),
250 isPositioned = _React$useState[0],
251 setIsPositioned = _React$useState[1];
252 var setPositioningStyles = React.useCallback(function () {
253 var element = paperRef.current;
254 if (!element) {
255 return;
256 }
257 var positioning = getPositioningStyle(element);
258 if (positioning.top !== null) {
259 element.style.top = positioning.top;
260 }
261 if (positioning.left !== null) {
262 element.style.left = positioning.left;
263 }
264 element.style.transformOrigin = positioning.transformOrigin;
265 setIsPositioned(true);
266 }, [getPositioningStyle]);
267 React.useEffect(function () {
268 if (disableScrollLock) {
269 window.addEventListener('scroll', setPositioningStyles);
270 }
271 return function () {
272 return window.removeEventListener('scroll', setPositioningStyles);
273 };
274 }, [anchorEl, disableScrollLock, setPositioningStyles]);
275 var handleEntering = function handleEntering(element, isAppearing) {
276 if (onEntering) {
277 onEntering(element, isAppearing);
278 }
279 setPositioningStyles();
280 };
281 var handleExited = function handleExited() {
282 setIsPositioned(false);
283 };
284 React.useEffect(function () {
285 if (open) {
286 setPositioningStyles();
287 }
288 });
289 React.useImperativeHandle(action, function () {
290 return open ? {
291 updatePosition: function updatePosition() {
292 setPositioningStyles();
293 }
294 } : null;
295 }, [open, setPositioningStyles]);
296 React.useEffect(function () {
297 if (!open) {
298 return undefined;
299 }
300 var handleResize = debounce(function () {
301 setPositioningStyles();
302 });
303 var containerWindow = ownerWindow(anchorEl);
304 containerWindow.addEventListener('resize', handleResize);
305 return function () {
306 handleResize.clear();
307 containerWindow.removeEventListener('resize', handleResize);
308 };
309 }, [anchorEl, open, setPositioningStyles]);
310 var transitionDuration = transitionDurationProp;
311 if (transitionDurationProp === 'auto' && !TransitionComponent.muiSupportAuto) {
312 transitionDuration = undefined;
313 }
314
315 // If the container prop is provided, use that
316 // If the anchorEl prop is provided, use its parent body element as the container
317 // If neither are provided let the Modal take care of choosing the container
318 var container = containerProp || (anchorEl ? ownerDocument(resolveAnchorEl(anchorEl)).body : undefined);
319 var RootSlot = (_slots$root = slots == null ? void 0 : slots.root) != null ? _slots$root : PopoverRoot;
320 var PaperSlot = (_slots$paper = slots == null ? void 0 : slots.paper) != null ? _slots$paper : PopoverPaper;
321 var paperProps = useSlotProps({
322 elementType: PaperSlot,
323 externalSlotProps: _extends({}, externalPaperSlotProps, {
324 style: isPositioned ? externalPaperSlotProps.style : _extends({}, externalPaperSlotProps.style, {
325 opacity: 0
326 })
327 }),
328 additionalProps: {
329 elevation: elevation,
330 ref: handlePaperRef
331 },
332 ownerState: ownerState,
333 className: clsx(classes.paper, externalPaperSlotProps == null ? void 0 : externalPaperSlotProps.className)
334 });
335 var _useSlotProps = useSlotProps({
336 elementType: RootSlot,
337 externalSlotProps: (slotProps == null ? void 0 : slotProps.root) || {},
338 externalForwardedProps: other,
339 additionalProps: {
340 ref: ref,
341 slotProps: {
342 backdrop: {
343 invisible: true
344 }
345 },
346 container: container,
347 open: open
348 },
349 ownerState: ownerState,
350 className: clsx(classes.root, className)
351 }),
352 rootSlotPropsProp = _useSlotProps.slotProps,
353 rootProps = _objectWithoutProperties(_useSlotProps, ["slotProps"]);
354 return /*#__PURE__*/_jsx(RootSlot, _extends({}, rootProps, !isHostComponent(RootSlot) && {
355 slotProps: rootSlotPropsProp,
356 disableScrollLock: disableScrollLock
357 }, {
358 children: /*#__PURE__*/_jsx(TransitionComponent, _extends({
359 appear: true,
360 in: open,
361 onEntering: handleEntering,
362 onExited: handleExited,
363 timeout: transitionDuration
364 }, TransitionProps, {
365 children: /*#__PURE__*/_jsx(PaperSlot, _extends({}, paperProps, {
366 children: children
367 }))
368 }))
369 }));
370});
371process.env.NODE_ENV !== "production" ? Popover.propTypes /* remove-proptypes */ = {
372 // ┌────────────────────────────── Warning ──────────────────────────────┐
373 // │ These PropTypes are generated from the TypeScript type definitions. │
374 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
375 // └─────────────────────────────────────────────────────────────────────┘
376 /**
377 * A ref for imperative actions.
378 * It currently only supports updatePosition() action.
379 */
380 action: refType,
381 /**
382 * An HTML element, [PopoverVirtualElement](/material-ui/react-popover/#virtual-element),
383 * or a function that returns either.
384 * It's used to set the position of the popover.
385 */
386 anchorEl: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.func]), function (props) {
387 if (props.open && (!props.anchorReference || props.anchorReference === 'anchorEl')) {
388 var resolvedAnchorEl = resolveAnchorEl(props.anchorEl);
389 if (resolvedAnchorEl && resolvedAnchorEl.nodeType === 1) {
390 var box = resolvedAnchorEl.getBoundingClientRect();
391 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
392 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'));
393 }
394 } else {
395 return new Error(['MUI: The `anchorEl` prop provided to the component is invalid.', "It should be an Element or PopoverVirtualElement instance but it's `".concat(resolvedAnchorEl, "` instead.")].join('\n'));
396 }
397 }
398 return null;
399 }),
400 /**
401 * This is the point on the anchor where the popover's
402 * `anchorEl` will attach to. This is not used when the
403 * anchorReference is 'anchorPosition'.
404 *
405 * Options:
406 * vertical: [top, center, bottom];
407 * horizontal: [left, center, right].
408 * @default {
409 * vertical: 'top',
410 * horizontal: 'left',
411 * }
412 */
413 anchorOrigin: PropTypes.shape({
414 horizontal: PropTypes.oneOfType([PropTypes.oneOf(['center', 'left', 'right']), PropTypes.number]).isRequired,
415 vertical: PropTypes.oneOfType([PropTypes.oneOf(['bottom', 'center', 'top']), PropTypes.number]).isRequired
416 }),
417 /**
418 * This is the position that may be used to set the position of the popover.
419 * The coordinates are relative to the application's client area.
420 */
421 anchorPosition: PropTypes.shape({
422 left: PropTypes.number.isRequired,
423 top: PropTypes.number.isRequired
424 }),
425 /**
426 * This determines which anchor prop to refer to when setting
427 * the position of the popover.
428 * @default 'anchorEl'
429 */
430 anchorReference: PropTypes.oneOf(['anchorEl', 'anchorPosition', 'none']),
431 /**
432 * The content of the component.
433 */
434 children: PropTypes.node,
435 /**
436 * Override or extend the styles applied to the component.
437 */
438 classes: PropTypes.object,
439 /**
440 * @ignore
441 */
442 className: PropTypes.string,
443 /**
444 * An HTML element, component instance, or function that returns either.
445 * The `container` will passed to the Modal component.
446 *
447 * By default, it uses the body of the anchorEl's top-level document object,
448 * so it's simply `document.body` most of the time.
449 */
450 container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.func]),
451 /**
452 * Disable the scroll lock behavior.
453 * @default false
454 */
455 disableScrollLock: PropTypes.bool,
456 /**
457 * The elevation of the popover.
458 * @default 8
459 */
460 elevation: integerPropType,
461 /**
462 * Specifies how close to the edge of the window the popover can appear.
463 * If null, the popover will not be constrained by the window.
464 * @default 16
465 */
466 marginThreshold: PropTypes.number,
467 /**
468 * Callback fired when the component requests to be closed.
469 * The `reason` parameter can optionally be used to control the response to `onClose`.
470 */
471 onClose: PropTypes.func,
472 /**
473 * If `true`, the component is shown.
474 */
475 open: PropTypes.bool.isRequired,
476 /**
477 * Props applied to the [`Paper`](/material-ui/api/paper/) element.
478 *
479 * This prop is an alias for `slotProps.paper` and will be overriden by it if both are used.
480 * @deprecated Use `slotProps.paper` instead.
481 *
482 * @default {}
483 */
484 PaperProps: PropTypes /* @typescript-to-proptypes-ignore */.shape({
485 component: elementTypeAcceptingRef
486 }),
487 /**
488 * The extra props for the slot components.
489 * You can override the existing props or add new ones.
490 *
491 * @default {}
492 */
493 slotProps: PropTypes.shape({
494 paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
495 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
496 }),
497 /**
498 * The components used for each slot inside.
499 *
500 * @default {}
501 */
502 slots: PropTypes.shape({
503 paper: PropTypes.elementType,
504 root: PropTypes.elementType
505 }),
506 /**
507 * The system prop that allows defining system overrides as well as additional CSS styles.
508 */
509 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
510 /**
511 * This is the point on the popover which
512 * will attach to the anchor's origin.
513 *
514 * Options:
515 * vertical: [top, center, bottom, x(px)];
516 * horizontal: [left, center, right, x(px)].
517 * @default {
518 * vertical: 'top',
519 * horizontal: 'left',
520 * }
521 */
522 transformOrigin: PropTypes.shape({
523 horizontal: PropTypes.oneOfType([PropTypes.oneOf(['center', 'left', 'right']), PropTypes.number]).isRequired,
524 vertical: PropTypes.oneOfType([PropTypes.oneOf(['bottom', 'center', 'top']), PropTypes.number]).isRequired
525 }),
526 /**
527 * The component used for the transition.
528 * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
529 * @default Grow
530 */
531 TransitionComponent: PropTypes.elementType,
532 /**
533 * Set to 'auto' to automatically calculate transition time based on height.
534 * @default 'auto'
535 */
536 transitionDuration: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number, PropTypes.shape({
537 appear: PropTypes.number,
538 enter: PropTypes.number,
539 exit: PropTypes.number
540 })]),
541 /**
542 * Props applied to the transition element.
543 * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component.
544 * @default {}
545 */
546 TransitionProps: PropTypes.object
547} : void 0;
548export default Popover;
\No newline at end of file