You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
8.2 KiB
JavaScript
239 lines
8.2 KiB
JavaScript
'use client';
|
|
|
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
const _excluded = ["anchor", "children", "container", "disablePortal", "keepMounted", "middleware", "offset", "open", "placement", "slotProps", "slots", "strategy", "withTransition"];
|
|
import * as React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { autoUpdate, flip, offset, shift, useFloating } from '@floating-ui/react-dom';
|
|
import { HTMLElementType, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useForkRef as useForkRef } from '@mui/utils';
|
|
import { unstable_composeClasses as composeClasses } from '../composeClasses';
|
|
import { Portal } from '../Portal';
|
|
import { useSlotProps } from '../utils';
|
|
import { useClassNamesOverride } from '../utils/ClassNameConfigurator';
|
|
import { getPopupUtilityClass } from './popupClasses';
|
|
import { useTransitionTrigger, TransitionContext } from '../useTransition';
|
|
import { PopupContext } from './PopupContext';
|
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
function useUtilityClasses(ownerState) {
|
|
const {
|
|
open
|
|
} = ownerState;
|
|
const slots = {
|
|
root: ['root', open && 'open']
|
|
};
|
|
return composeClasses(slots, useClassNamesOverride(getPopupUtilityClass));
|
|
}
|
|
function resolveAnchor(anchor) {
|
|
return typeof anchor === 'function' ? anchor() : anchor;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Demos:
|
|
*
|
|
* - [Popup](https://mui.com/base-ui/react-popup/)
|
|
*
|
|
* API:
|
|
*
|
|
* - [Popup API](https://mui.com/base-ui/react-popup/components-api/#popup)
|
|
*/
|
|
const Popup = /*#__PURE__*/React.forwardRef(function Popup(props, forwardedRef) {
|
|
const {
|
|
anchor: anchorProp,
|
|
children,
|
|
container,
|
|
disablePortal = false,
|
|
keepMounted = false,
|
|
middleware,
|
|
offset: offsetProp = 0,
|
|
open = false,
|
|
placement = 'bottom',
|
|
slotProps = {},
|
|
slots = {},
|
|
strategy = 'absolute',
|
|
withTransition = false
|
|
} = props,
|
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
const {
|
|
refs,
|
|
elements,
|
|
floatingStyles,
|
|
update,
|
|
placement: finalPlacement
|
|
} = useFloating({
|
|
elements: {
|
|
reference: resolveAnchor(anchorProp)
|
|
},
|
|
open,
|
|
middleware: middleware ?? [offset(offsetProp ?? 0), flip(), shift()],
|
|
placement,
|
|
strategy,
|
|
whileElementsMounted: !keepMounted ? autoUpdate : undefined
|
|
});
|
|
const handleRef = useForkRef(refs.setFloating, forwardedRef);
|
|
useEnhancedEffect(() => {
|
|
if (keepMounted && open && elements.reference && elements.floating) {
|
|
const cleanup = autoUpdate(elements.reference, elements.floating, update);
|
|
return cleanup;
|
|
}
|
|
return undefined;
|
|
}, [keepMounted, open, elements, update]);
|
|
const ownerState = _extends({}, props, {
|
|
disablePortal,
|
|
keepMounted,
|
|
offset,
|
|
open,
|
|
placement,
|
|
finalPlacement,
|
|
strategy,
|
|
withTransition
|
|
});
|
|
const {
|
|
contextValue,
|
|
hasExited: hasTransitionExited
|
|
} = useTransitionTrigger(open);
|
|
const visibility = keepMounted && hasTransitionExited ? 'hidden' : undefined;
|
|
const classes = useUtilityClasses(ownerState);
|
|
const Root = slots?.root ?? 'div';
|
|
const rootProps = useSlotProps({
|
|
elementType: Root,
|
|
externalSlotProps: slotProps.root,
|
|
externalForwardedProps: other,
|
|
ownerState,
|
|
className: classes.root,
|
|
additionalProps: {
|
|
ref: handleRef,
|
|
role: 'tooltip',
|
|
style: _extends({}, floatingStyles, {
|
|
visibility
|
|
})
|
|
}
|
|
});
|
|
const popupContextValue = React.useMemo(() => ({
|
|
placement: finalPlacement
|
|
}), [finalPlacement]);
|
|
const shouldRender = keepMounted || !hasTransitionExited;
|
|
if (!shouldRender) {
|
|
return null;
|
|
}
|
|
return /*#__PURE__*/_jsx(Portal, {
|
|
disablePortal: disablePortal,
|
|
container: container,
|
|
children: /*#__PURE__*/_jsx(PopupContext.Provider, {
|
|
value: popupContextValue,
|
|
children: /*#__PURE__*/_jsx(TransitionContext.Provider, {
|
|
value: contextValue,
|
|
children: /*#__PURE__*/_jsx(Root, _extends({}, rootProps, {
|
|
children: children
|
|
}))
|
|
})
|
|
})
|
|
});
|
|
});
|
|
process.env.NODE_ENV !== "production" ? Popup.propTypes /* remove-proptypes */ = {
|
|
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
// └─────────────────────────────────────────────────────────────────────┘
|
|
/**
|
|
* An HTML element, [virtual element](https://floating-ui.com/docs/virtual-elements),
|
|
* or a function that returns either.
|
|
* It's used to set the position of the popup.
|
|
*/
|
|
anchor: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.object, PropTypes.func]),
|
|
/**
|
|
* @ignore
|
|
*/
|
|
children: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.node, PropTypes.func]),
|
|
/**
|
|
* An HTML element or function that returns one. The container will have the portal children appended to it.
|
|
* By default, it uses the body of the top-level document object, so it's `document.body` in these cases.
|
|
*/
|
|
container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.func]),
|
|
/**
|
|
* If `true`, the popup will be rendered where it is defined, without the use of portals.
|
|
* @default false
|
|
*/
|
|
disablePortal: PropTypes.bool,
|
|
/**
|
|
* If `true`, the popup will exist in the DOM even if it's closed.
|
|
* Its visibility will be controlled by the `visibility` CSS property.
|
|
*
|
|
* Otherwise, a closed popup will be removed from the DOM.
|
|
*
|
|
* @default false
|
|
*/
|
|
keepMounted: PropTypes.bool,
|
|
/**
|
|
* Collection of Floating UI middleware to use when positioning the popup.
|
|
* If not provided, the [`offset`](https://floating-ui.com/docs/offset)
|
|
* and [`flip`](https://floating-ui.com/docs/flip) functions will be used.
|
|
*
|
|
* @see https://floating-ui.com/docs/computePosition#middleware
|
|
*/
|
|
middleware: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([false]), PropTypes.shape({
|
|
fn: PropTypes.func.isRequired,
|
|
name: PropTypes.string.isRequired,
|
|
options: PropTypes.any
|
|
})])),
|
|
/**
|
|
* Distance between a popup and the trigger element.
|
|
* This prop is ignored when custom `middleware` is provided.
|
|
*
|
|
* @default 0
|
|
* @see https://floating-ui.com/docs/offset
|
|
*/
|
|
offset: PropTypes.oneOfType([PropTypes.func, PropTypes.number, PropTypes.shape({
|
|
alignmentAxis: PropTypes.number,
|
|
crossAxis: PropTypes.number,
|
|
mainAxis: PropTypes.number
|
|
})]),
|
|
/**
|
|
* If `true`, the popup is visible.
|
|
*
|
|
* @default false
|
|
*/
|
|
open: PropTypes.bool,
|
|
/**
|
|
* Determines where to place the popup relative to the trigger element.
|
|
*
|
|
* @default 'bottom'
|
|
* @see https://floating-ui.com/docs/computePosition#placement
|
|
*/
|
|
placement: PropTypes.oneOf(['bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
|
|
/**
|
|
* The props used for each slot inside the Popup.
|
|
*
|
|
* @default {}
|
|
*/
|
|
slotProps: PropTypes.shape({
|
|
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
|
|
}),
|
|
/**
|
|
* The components used for each slot inside the Popup.
|
|
* Either a string to use a HTML element or a component.
|
|
*
|
|
* @default {}
|
|
*/
|
|
slots: PropTypes.shape({
|
|
root: PropTypes.elementType
|
|
}),
|
|
/**
|
|
* The type of CSS position property to use (absolute or fixed).
|
|
*
|
|
* @default 'absolute'
|
|
* @see https://floating-ui.com/docs/computePosition#strategy
|
|
*/
|
|
strategy: PropTypes.oneOf(['absolute', 'fixed']),
|
|
/**
|
|
* If `true`, the popup will not disappear immediately when it needs to be closed
|
|
* but wait until the exit transition has finished.
|
|
* In such a case, a function form of `children` must be used and `onExited`
|
|
* callback function must be called when the transition or animation finish.
|
|
*
|
|
* @default false
|
|
*/
|
|
withTransition: PropTypes.bool
|
|
} : void 0;
|
|
export { Popup }; |