Popovers are interactive anchored dialogs that open on click by default, or hover.
-{{"component": "modules/components/ComponentLinkHeader.js", "design": false}}
+{{"component": "@mui/docs/ComponentLinkHeader", "design": false}}
{{"component": "modules/components/ComponentPageTabs.js"}}
@@ -48,84 +48,83 @@ import * as Popover from '@base_ui/react/Popover';
Popover is implemented using a collection of related components:
-- `` is a top-level component that wraps all other components.
-- `` contains the trigger element.
-- `` contains the popover content.
+- `` wraps around `` or a group of ``s.
+- `` is a top-level component that wraps the other components.
+- `` renders the trigger element.
+- `` renders the popover's positioning element.
+- `` renders the popover popup itself.
- `` renders an optional pointing arrow, placed inside the popup.
```tsx
-
-
-
-
-
-
- Popover
-
-
+
+
+
+
+
+
+
+
+
+
+```
+
+## Provider
+
+`Popover.Provider` provides a shared delay for hoverable popovers so that once a popover is shown, the rest of the popovers in the group don't wait for the delay before showing. You can wrap this globally, or around an individual group of hoverable popovers anywhere in your React tree (or both).
+
+```tsx
+
+
+
```
## Placement
-By default, the popover is placed on the top side of its anchor. To change this, use the `side` prop on `Popover.Popup`:
+By default, the popover is placed on the top side of its trigger, the default anchor. To change this, use the `side` prop:
```jsx
-
-
-
- Popover
+
+
+ Popover
+
```
You can also change the alignment of the popover in relation to its anchor. By default, it is centered, but it can be aligned to an edge of the anchor using the `alignment` prop:
```jsx
-
- Popover
-
+
+ Popover
+
```
-Possible alignment values are `center`, `start`, and `end`. The latter two are logical values that adapt to the writing direction (LTR or RTL).
-
Due to collision detection, the popover may change its placement to avoid overflow. Therefore, your explicitly specified `side` and `alignment` props act as "ideal", or preferred, values.
-To access the true rendered values, which may change as the result of a collision, the content element receives data attributes:
+To access the true rendered values, which may change as the result of a collision, the popup element receives data attributes:
```jsx
-// Rendered HTML
-
- Content
+// Rendered HTML (simplified)
+
+
+ Popover
+
```
This allows you to conditionally style the popover based on its rendered side or alignment.
-## Offsets
-
-### Side
+## Offset
-To offset the side position, use the `sideOffset` prop:
+The `sideOffset` prop creates a gap between the anchor and popover popup, while `alignmentOffset` slides the popover popup from its alignment, acting logically for `start` and `end` alignments.
```jsx
-
+
```
-This creates a gap between the anchor and its popover content.
-
-### Alignment
-
-To offset the alignment position, use the `alignmentOffset` prop:
-
-```jsx
-
-```
-
-This prop acts logically for the `start` and `end` alignments.
-
## Hover
-To create an infotip, which is a tooltip-like popover that is anchored to an icon that may contain interactive content, add the `openOnHover` prop:
+To open the popover on hover instead of click for pointer users, which enables creating tooltip-like popovers that may contain interactive content, add the `openOnHover` prop:
```jsx
@@ -133,28 +132,26 @@ To create an infotip, which is a tooltip-like popover that is anchored to an ico
### Delay
-By default, a popover that can open on hover waits until the user's cursor is at rest over the anchor element before it is opened. To change this timeout, use the `delay` prop, which represents how long the popover waits after the cursor rests to open in milliseconds:
+To change how long the popover waits until it opens or closes when `openOnHover` is enabled, use the `delay` and `closeDelay` props, which represent how long the poover waits after the cursor rests on the trigger to open, or moves away from the trigger to close, in milliseconds:
```jsx
-
+
```
-The close delay can also be configured:
+The delay type can be changed from `"rest"` (user's cursor is static over the trigger for the given timeout in milliseconds) to `"hover"` (the user's cursor has entered the trigger):
```jsx
-
+
```
-The delay type can be changed from `"rest"` (user's cursor is static for the given timeout in milliseconds) to `"hover"`:
+### Cursor following
+
+The popover can follow the cursor on both axes or one axis using the `followCursorAxis` prop when `openOnHover` is enabled. Possible values are: `none` (default), `both`, `x`, or `y`.
```jsx
-
+
```
-### Grouping
-
-To ensure nearby trigger elements' delays become `0` once one of the popovers of the group opens, use the `Popover.Group` component, wrapping the `Popover.Root`s with it.
-
## Controlled
To control the popover with external state, use the `open` and `onOpenChange` props:
@@ -170,54 +167,54 @@ function App() {
}
```
-## Default open
-
-To show the popover initially while leaving it uncontrolled, use the `defaultOpen` prop:
-
-```jsx
-
-```
-
## Arrow
To add an arrow (caret or triangle) inside the popover content that points toward the center of the anchor element, use the `Popover.Arrow` component:
```js
-
-
- Popover
-
+
+
+
+ Popover
+
+
```
It automatically positions a wrapper element that can be styled or contain a custom SVG shape.
-## Cursor following
+## Anchoring
-The popover can follow the cursor on both axes or one axis using the `followCursorAxis` prop on `Popover.Popup`. Possible values are: `none` (default), `both`, `x`, or `y`.
+By default, the `Trigger` acts as the anchor, but this can be changed to another element.
-## Anchoring
+- A DOM element (stored in React state):
+
+```jsx
+
+```
-By default, the `Trigger` acts as the anchor. This can be changed to another element, either virtual or real:
+- A React ref:
-```js
-
+```
+
+- A virtual element object, consisting of a `getBoundingClientRect` method and an optional `contextElement` property:
+
+```jsx
+ DOMRect,
- contextElement: domElement, // optional
+ // `contextElement` is an optional but recommended property when `getBoundingClientRect` is
+ // derived from a real element, to ensure collision detection and position updates work as
+ // expected in certain DOM trees.
+ contextElement: domNode,
}}
>
- Popover
-
```
## Styling
-The `Popover.Popup` element receives the following CSS variables:
+The `Popover.Positioner` element receives the following CSS variables, which can be used by `Popover.Popup`:
- `--anchor-width`: Specifies the width of the anchor element. You can use this to match the width of the popover with its anchor.
- `--anchor-height`: Specifies the height of the anchor element. You can use this to match the height of the popover with its anchor.
@@ -225,31 +222,19 @@ The `Popover.Popup` element receives the following CSS variables:
- `--available-height`: Specifies the available height of the popup before it overflows the viewport.
- `--transform-origin`: Specifies the origin of the popup element that represents the point of the anchor element's center. When animating scale, this allows it to correctly emanate from the center of the anchor.
-```jsx
-
- Content
-
-```
-
-By default, `maxWidth` and `maxHeight` are already specified using `--available-{width,height}` to prevent the popover from being too big to fit on the screen.
+By default, `maxWidth` and `maxHeight` are already specified on the positioner using `--available-{width,height}` to prevent the popover from being too big to fit on the screen.
## Animations
-CSS transitions or animations can be used to animate the popover opening or closing.
+The popover can animate when opening or closing with either:
-`Popover.Popup` receives a `data-status` attribute in one of four states:
+- CSS transitions
+- CSS animations
+- JavaScript animations
-- `unmounted`, indicating the popover is not mounted on the DOM.
-- `initial`, indicating the popover has been inserted into the DOM.
-- `opening`, indicating the popover is transitioning into the open state, immediately after insertion.
-- `closing`, indicating the popover is transitioning into the closed state.
+### CSS transitions
-Here is an example of how to apply a symmetric scale and fade transition:
+Here is an example of how to apply a symmetric scale and fade transition with the default conditionally-rendered behavior:
```jsx
Popover
@@ -257,20 +242,54 @@ Here is an example of how to apply a symmetric scale and fade transition:
```css
.PopoverPopup {
+ transform-origin: var(--transform-origin);
transition-property: opacity, transform;
transition-duration: 0.2s;
+ /* Represents the final styles once exited */
opacity: 0;
transform: scale(0.9);
- transform-origin: var(--transform-origin);
}
-.PopoverPopup[data-status='opening'] {
+/* Represents the final styles once entered */
+.PopoverPopup[data-state='open'] {
opacity: 1;
transform: scale(1);
}
+
+/* Represents the initial styles when entering */
+.PopoverPopup[data-entering] {
+ opacity: 0;
+ transform: scale(0.9);
+}
```
-CSS animations can also be used—useful for more complex animations with differing property durations:
+Styles need to be applied in three states:
+
+- The exiting styles, placed on the base element class
+- The open styles, placed on the base element class with `[data-state="open"]`
+- The entering styles, placed on the base element class with `[data-entering]`
+
+In newer browsers, there is a feature called `@starting-style` which allows transitions to occur on open for conditionally-mounted components:
+
+```css
+/* Base UI API - Polyfill */
+.PopoverPopup[data-entering] {
+ opacity: 0;
+ transform: scale(0.9);
+}
+
+/* Official Browser API - no Firefox support as of May 2024 */
+@starting-style {
+ .PopoverPopup[data-state='open'] {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+```
+
+### CSS animations
+
+CSS animations can also be used, requiring only two separate declarations:
```css
@keyframes scale-in {
@@ -288,14 +307,55 @@ CSS animations can also be used—useful for more complex animations with differ
}
.PopoverPopup {
- animation: scale-in 0.2s;
+ animation: scale-in 0.2s forwards;
}
-.PopoverPopup[data-status='closing'] {
+.PopoverPopup[data-exiting] {
animation: scale-out 0.2s forwards;
}
```
+### JavaScript animations
+
+The `keepMounted` prop lets an external library control the mounting, for example `framer-motion`'s `AnimatePresence` component.
+
+```js
+function App() {
+ const [open, setOpen] = useState(false);
+ return (
+
+ Trigger
+
+ {open && (
+
+
+ }
+ >
+ Popover
+
+
+ )}
+
+
+ );
+}
+```
+
+### Animation states
+
+Four states are available as data attributes to animate the popup, which enables full control depending on whether the popup is being animated with CSS transitions or animations, JavaScript, or is using the `keepMounted` prop.
+
+- `[data-state="open"]` - `open` state is `true`.
+- `[data-state="closed"]` - `open` state is `false`. Can still be mounted to the DOM if closing.
+- `[data-entering]` - the popup was just inserted to the DOM. The attribute is removed 1 animation frame later. Enables "starting styles" upon insertion for conditional rendering.
+- `[data-exiting]` - the popup is in the process of being removed from the DOM, but is still mounted.
+
### Instant animation
Animations can be removed under certain conditions using the `data-instant` attribute on `Popover.Popup`. This attribute can be used unconditionally, but it also has different values for granular checks:
diff --git a/docs/data/base/pagesApi.js b/docs/data/base/pagesApi.js
index e0dc130eeb..9b8662417c 100644
--- a/docs/data/base/pagesApi.js
+++ b/docs/data/base/pagesApi.js
@@ -82,18 +82,18 @@ module.exports = [
pathname: '/base-ui/react-popover/components-api/#popover-backdrop',
title: 'PopoverBackdrop',
},
- {
- pathname: '/base-ui/react-popover/components-api/#popover-group',
- title: 'PopoverGroup',
- },
{
pathname: '/base-ui/react-popover/components-api/#popover-popup',
title: 'PopoverPopup',
},
{
- pathname: '/base-ui/react-popover/components-api/#popover-popup-root',
+ pathname: '/base-ui/react-popover/components-api/#popover-positioner',
title: 'PopoverPositioner',
},
+ {
+ pathname: '/base-ui/react-popover/components-api/#popover-provider',
+ title: 'PopoverProvider',
+ },
{
pathname: '/base-ui/react-popover/components-api/#popover-root',
title: 'PopoverRoot',
@@ -172,7 +172,7 @@ module.exports = [
title: 'useOptionContextStabilizer',
},
{
- pathname: '/base-ui/react-popover/hooks-api/#use-popover-popup',
+ pathname: '/base-ui/react-popover/hooks-api/#use-popover-positioner',
title: 'usePopoverPositioner',
},
{
diff --git a/docs/pages/base-ui/api/popover-popup-root.json b/docs/pages/base-ui/api/popover-popup-root.json
deleted file mode 100644
index cb32dfc34c..0000000000
--- a/docs/pages/base-ui/api/popover-popup-root.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "props": {},
- "name": "PopoverPositioner",
- "imports": [
- "import * as Popover from '@base_ui/react/Popover';\nconst PopoverPositioner = Popover.PopupRoot;"
- ],
- "classes": [],
- "muiName": "PopoverPositioner",
- "filename": "/packages/mui-base/src/Popover/PopupRoot/PopoverPositioner.tsx",
- "inheritance": null,
- "demos": "
"
}
diff --git a/docs/pages/base-ui/api/use-popover-root.json b/docs/pages/base-ui/api/use-popover-root.json
index 14a87480e4..0ec2fbdd49 100644
--- a/docs/pages/base-ui/api/use-popover-root.json
+++ b/docs/pages/base-ui/api/use-popover-root.json
@@ -65,10 +65,7 @@
"required": true
},
"transitionStatus": {
- "type": {
- "name": "TransitionStatus | undefined",
- "description": "TransitionStatus | undefined"
- },
+ "type": { "name": "TransitionStatus", "description": "TransitionStatus" },
"required": true
}
},
diff --git a/docs/pages/base-ui/react-popover/[docsTab]/index.js b/docs/pages/base-ui/react-popover/[docsTab]/index.js
index 3e40f358ae..5827e7c821 100644
--- a/docs/pages/base-ui/react-popover/[docsTab]/index.js
+++ b/docs/pages/base-ui/react-popover/[docsTab]/index.js
@@ -5,12 +5,12 @@ import * as pageProps from 'docs-base/data/base/components/popover/popover.md?@m
import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
import PopoverArrowApiJsonPageContent from '../../api/popover-arrow.json';
import PopoverBackdropApiJsonPageContent from '../../api/popover-backdrop.json';
-import PopoverGroupApiJsonPageContent from '../../api/popover-group.json';
import PopoverPopupApiJsonPageContent from '../../api/popover-popup.json';
-import PopoverPositionerApiJsonPageContent from '../../api/popover-popup-root.json';
+import PopoverPositionerApiJsonPageContent from '../../api/popover-positioner.json';
+import PopoverProviderApiJsonPageContent from '../../api/popover-provider.json';
import PopoverRootApiJsonPageContent from '../../api/popover-root.json';
import PopoverTriggerApiJsonPageContent from '../../api/popover-trigger.json';
-import usePopoverPositionerApiJsonPageContent from '../../api/use-popover-popup.json';
+import usePopoverPositionerApiJsonPageContent from '../../api/use-popover-positioner.json';
import usePopoverRootApiJsonPageContent from '../../api/use-popover-root.json';
export default function Page(props) {
@@ -44,13 +44,6 @@ export const getStaticProps = () => {
);
const PopoverBackdropApiDescriptions = mapApiPageTranslations(PopoverBackdropApiReq);
- const PopoverGroupApiReq = require.context(
- 'docs-base/translations/api-docs/popover-group',
- false,
- /\.\/popover-group.*.json$/,
- );
- const PopoverGroupApiDescriptions = mapApiPageTranslations(PopoverGroupApiReq);
-
const PopoverPopupApiReq = require.context(
'docs-base/translations/api-docs/popover-popup',
false,
@@ -59,12 +52,19 @@ export const getStaticProps = () => {
const PopoverPopupApiDescriptions = mapApiPageTranslations(PopoverPopupApiReq);
const PopoverPositionerApiReq = require.context(
- 'docs-base/translations/api-docs/popover-popup-root',
+ 'docs-base/translations/api-docs/popover-positioner',
false,
- /\.\/popover-popup-root.*.json$/,
+ /\.\/popover-positioner.*.json$/,
);
const PopoverPositionerApiDescriptions = mapApiPageTranslations(PopoverPositionerApiReq);
+ const PopoverProviderApiReq = require.context(
+ 'docs-base/translations/api-docs/popover-provider',
+ false,
+ /\.\/popover-provider.*.json$/,
+ );
+ const PopoverProviderApiDescriptions = mapApiPageTranslations(PopoverProviderApiReq);
+
const PopoverRootApiReq = require.context(
'docs-base/translations/api-docs/popover-root',
false,
@@ -80,9 +80,9 @@ export const getStaticProps = () => {
const PopoverTriggerApiDescriptions = mapApiPageTranslations(PopoverTriggerApiReq);
const usePopoverPositionerApiReq = require.context(
- 'docs-base/translations/api-docs/use-popover-popup',
+ 'docs-base/translations/api-docs/use-popover-positioner',
false,
- /\.\/use-popover-popup.*.json$/,
+ /\.\/use-popover-positioner.*.json$/,
);
const usePopoverPositionerApiDescriptions = mapApiPageTranslations(usePopoverPositionerApiReq);
@@ -98,18 +98,18 @@ export const getStaticProps = () => {
componentsApiDescriptions: {
PopoverArrow: PopoverArrowApiDescriptions,
PopoverBackdrop: PopoverBackdropApiDescriptions,
- PopoverGroup: PopoverGroupApiDescriptions,
PopoverPopup: PopoverPopupApiDescriptions,
PopoverPositioner: PopoverPositionerApiDescriptions,
+ PopoverProvider: PopoverProviderApiDescriptions,
PopoverRoot: PopoverRootApiDescriptions,
PopoverTrigger: PopoverTriggerApiDescriptions,
},
componentsApiPageContents: {
PopoverArrow: PopoverArrowApiJsonPageContent,
PopoverBackdrop: PopoverBackdropApiJsonPageContent,
- PopoverGroup: PopoverGroupApiJsonPageContent,
PopoverPopup: PopoverPopupApiJsonPageContent,
PopoverPositioner: PopoverPositionerApiJsonPageContent,
+ PopoverProvider: PopoverProviderApiJsonPageContent,
PopoverRoot: PopoverRootApiJsonPageContent,
PopoverTrigger: PopoverTriggerApiJsonPageContent,
},
diff --git a/docs/translations/api-docs/popover-group/popover-group.json b/docs/translations/api-docs/popover-group/popover-group.json
deleted file mode 100644
index 518e92975a..0000000000
--- a/docs/translations/api-docs/popover-group/popover-group.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "componentDescription": "Groups popovers' delays together so that once one of the popovers opens, subsequent popovers will\nnot open with a delay.",
- "propDescriptions": {
- "closeDelay": {
- "description": "The delay in milliseconds until the popover content is closed."
- },
- "delay": {
- "description": "The delay in milliseconds until popovers within the group are open."
- },
- "timeout": {
- "description": "The timeout in milliseconds until the grouping logic is no longer active after the last popover in the group has closed."
- }
- },
- "classDescriptions": {}
-}
diff --git a/docs/translations/api-docs/popover-popup-root/popover-popup-root.json b/docs/translations/api-docs/popover-popup-root/popover-popup-root.json
deleted file mode 100644
index f93d4cbd8c..0000000000
--- a/docs/translations/api-docs/popover-popup-root/popover-popup-root.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "componentDescription": "", "propDescriptions": {}, "classDescriptions": {} }
diff --git a/docs/translations/api-docs/popover-popup/popover-popup.json b/docs/translations/api-docs/popover-popup/popover-popup.json
index aace2979db..8e37037584 100644
--- a/docs/translations/api-docs/popover-popup/popover-popup.json
+++ b/docs/translations/api-docs/popover-popup/popover-popup.json
@@ -1,45 +1,10 @@
{
"componentDescription": "The popover popup element.",
"propDescriptions": {
- "alignment": {
- "description": "The alignment of the popover element to the anchor element along its cross axis."
- },
- "alignmentOffset": {
- "description": "The offset of the popover element along its alignment axis."
- },
- "anchor": {
- "description": "The anchor element to which the popover content will be placed at."
- },
- "arrowPadding": {
- "description": "Determines the padding between the arrow and the popover content. Useful when the popover has rounded corners via border-radius."
- },
"className": {
"description": "Class names applied to the element or a function that returns them based on the component's state."
},
- "collisionBoundary": {
- "description": "The boundary that the popover element should be constrained to."
- },
- "collisionPadding": { "description": "The padding of the collision boundary." },
- "container": {
- "description": "The container element to which the popover content will be appended to."
- },
- "followCursorAxis": {
- "description": "Determines which axis the popover should follow the cursor on."
- },
- "hideWhenDetached": {
- "description": "If true, the popover will be hidden if it is detached from its anchor element due to differing clipping contexts."
- },
- "keepMounted": {
- "description": "If true, the popover content will be kept mounted in the DOM."
- },
- "render": { "description": "A function to customize rendering of the component." },
- "side": {
- "description": "The side of the anchor element that the popover element should align to."
- },
- "sideOffset": { "description": "The gap between the anchor element and the popover element." },
- "sticky": {
- "description": "If true, allow the popover to remain in stuck view while the anchor element is scrolled out of view."
- }
+ "render": { "description": "A function to customize rendering of the component." }
},
"classDescriptions": {}
}
diff --git a/docs/translations/api-docs/popover-positioner/popover-positioner.json b/docs/translations/api-docs/popover-positioner/popover-positioner.json
new file mode 100644
index 0000000000..3ca9149aca
--- /dev/null
+++ b/docs/translations/api-docs/popover-positioner/popover-positioner.json
@@ -0,0 +1,43 @@
+{
+ "componentDescription": "The popover positioner element.",
+ "propDescriptions": {
+ "alignment": {
+ "description": "The alignment of the popover element to the anchor element along its cross axis."
+ },
+ "alignmentOffset": {
+ "description": "The offset of the popover element along its alignment axis."
+ },
+ "anchor": { "description": "The anchor element to which the popover popup will be placed at." },
+ "arrowPadding": {
+ "description": "Determines the padding between the arrow and the popover popup's edges. Useful when the popover popup has rounded corners via border-radius."
+ },
+ "className": {
+ "description": "Class names applied to the element or a function that returns them based on the component's state."
+ },
+ "collisionBoundary": {
+ "description": "The boundary that the popover element should be constrained to."
+ },
+ "collisionPadding": { "description": "The padding of the collision boundary." },
+ "container": {
+ "description": "The container element to which the popover popup will be appended to."
+ },
+ "followCursorAxis": {
+ "description": "Determines which axis the popover should follow the cursor on."
+ },
+ "hideWhenDetached": {
+ "description": "If true, the popover will be hidden if it is detached from its anchor element due to differing clipping contexts."
+ },
+ "keepMounted": {
+ "description": "If true, tooltip stays mounted in the DOM when closed."
+ },
+ "render": { "description": "A function to customize rendering of the component." },
+ "side": {
+ "description": "The side of the anchor element that the popover element should align to."
+ },
+ "sideOffset": { "description": "The gap between the anchor element and the popover element." },
+ "sticky": {
+ "description": "If true, allow the popover to remain in stuck view while the anchor element is scrolled out of view."
+ }
+ },
+ "classDescriptions": {}
+}
diff --git a/docs/translations/api-docs/popover-provider/popover-provider.json b/docs/translations/api-docs/popover-provider/popover-provider.json
new file mode 100644
index 0000000000..8fb3385a83
--- /dev/null
+++ b/docs/translations/api-docs/popover-provider/popover-provider.json
@@ -0,0 +1,11 @@
+{
+ "componentDescription": "Provides a shared delay for popovers so that once a popover is shown, the rest of the popovers in\nthe group will not wait for the delay before showing.",
+ "propDescriptions": {
+ "closeDelay": { "description": "The delay in milliseconds until the popover popup is closed." },
+ "delay": { "description": "The delay in milliseconds until the popover popup is opened." },
+ "timeout": {
+ "description": "The timeout in milliseconds until the grouping logic is no longer active after the last popover in the group has closed."
+ }
+ },
+ "classDescriptions": {}
+}
diff --git a/docs/translations/api-docs/popover-root/popover-root.json b/docs/translations/api-docs/popover-root/popover-root.json
index f8f6fc72de..110598a5ca 100644
--- a/docs/translations/api-docs/popover-root/popover-root.json
+++ b/docs/translations/api-docs/popover-root/popover-root.json
@@ -1,27 +1,25 @@
{
"componentDescription": "The foundation for building custom-styled popovers.",
"propDescriptions": {
- "closeDelay": {
- "description": "The delay in milliseconds until the popover content is closed."
- },
+ "closeDelay": { "description": "The delay in milliseconds until the popover popup is closed." },
"defaultOpen": {
"description": "Specifies whether the popover is open initially when uncontrolled."
},
- "delay": { "description": "The delay in milliseconds until the popover content is opened." },
+ "delay": { "description": "The delay in milliseconds until the popover popup is opened." },
"delayType": {
- "description": "The delay type to use. rest means the delay represents how long the user's cursor must rest on the trigger before the popover content is opened. hover means the delay represents how long to wait once the user's cursor has entered the trigger."
+ "description": "The delay type to use. rest means the delay represents how long the user's cursor must rest on the trigger before the popover popup is opened. hover means the delay represents how long to wait as soon as the user's cursor has entered the trigger."
},
"followCursorAxis": {
"description": "Determines which axis the tooltip should follow the cursor on."
},
"onOpenChange": {
- "description": "Callback fired when the popover content is requested to be opened or closed. Use when controlled."
+ "description": "Callback fired when the popover popup is requested to be opened or closed. Use when controlled."
},
"open": {
- "description": "If true, the popover content is open. Use when controlled."
+ "description": "If true, the popover popup is open. Use when controlled."
},
"openOnHover": {
- "description": "If true, the popover content opens when the trigger is hovered."
+ "description": "If true, the popover popup opens when the trigger is hovered."
}
},
"classDescriptions": {}
diff --git a/docs/translations/api-docs/popover-trigger/popover-trigger.json b/docs/translations/api-docs/popover-trigger/popover-trigger.json
index c9ead1764a..763409f066 100644
--- a/docs/translations/api-docs/popover-trigger/popover-trigger.json
+++ b/docs/translations/api-docs/popover-trigger/popover-trigger.json
@@ -1,5 +1,10 @@
{
- "componentDescription": "Provides props for its child element to trigger the popover, anchoring it to the child element.",
- "propDescriptions": {},
+ "componentDescription": "Renders a trigger element that will open the popover.",
+ "propDescriptions": {
+ "className": {
+ "description": "Class names applied to the element or a function that returns them based on the component's state."
+ },
+ "render": { "description": "A function to customize rendering of the component." }
+ },
"classDescriptions": {}
}
diff --git a/docs/translations/api-docs/use-popover-popup/use-popover-popup.json b/docs/translations/api-docs/use-popover-positioner/use-popover-positioner.json
similarity index 67%
rename from docs/translations/api-docs/use-popover-popup/use-popover-popup.json
rename to docs/translations/api-docs/use-popover-positioner/use-popover-positioner.json
index 349a803747..1f3f6356f7 100644
--- a/docs/translations/api-docs/use-popover-popup/use-popover-popup.json
+++ b/docs/translations/api-docs/use-popover-positioner/use-popover-positioner.json
@@ -7,21 +7,17 @@
"alignmentOffset": {
"description": "The offset of the popover element along its alignment axis."
},
- "anchor": { "description": "The anchor element of the popover." },
+ "anchor": { "description": "The anchor element to which the popover popup will be placed at." },
"arrowPadding": {
- "description": "Determines the padding between the arrow and the popover content. Useful when the popover has rounded corners via border-radius."
- },
- "closeDelay": {
- "description": "The hover delay in milliseconds before the popover closes after the trigger element is unhovered."
+ "description": "Determines the padding between the arrow and the popover popup's edges. Useful when the popover popup has rounded corners via border-radius."
},
"collisionBoundary": {
"description": "The boundary that the popover element should be constrained to."
},
"collisionPadding": { "description": "The padding of the collision boundary." },
- "delay": {
- "description": "The hover delay in milliseconds before the popover opens after the trigger element is hovered."
+ "container": {
+ "description": "The container element to which the popover popup will be appended to."
},
- "delayType": { "description": "The type of hover open delay." },
"followCursorAxis": {
"description": "Determines which axis the popover should follow the cursor on."
},
@@ -29,15 +25,9 @@
"description": "If true, the popover will be hidden if it is detached from its anchor element due to differing clipping contexts."
},
"keepMounted": {
- "description": "If true, the popover will be mounted, including CSS transitions or animations."
- },
- "onOpenChange": {
- "description": "Callback fired when the popover is requested to be opened or closed."
- },
- "open": { "description": "If true, the popover will be open." },
- "openOnHover": {
- "description": "If true, the popover content opens when the trigger is hovered."
+ "description": "If true, tooltip stays mounted in the DOM when closed."
},
+ "open": { "description": "If true, the popover is open." },
"side": {
"description": "The side of the anchor element that the popover element should align to."
},
@@ -51,7 +41,7 @@
"arrowRef": { "description": "The ref of the popover arrow element." },
"arrowUncentered": { "description": "Determines if the arrow cannot be centered." },
"getArrowProps": { "description": "Props to spread on the popover arrow element." },
- "getPopupProps": { "description": "Props to spread on the popover content element." },
+ "getPositionerProps": { "description": "Props to spread on the popover positioner element." },
"mounted": {
"description": "Whether the popover is mounted, including CSS transitions or animations."
},
diff --git a/docs/translations/api-docs/use-popover-root/use-popover-root.json b/docs/translations/api-docs/use-popover-root/use-popover-root.json
index e642ed0afb..348dd21f74 100644
--- a/docs/translations/api-docs/use-popover-root/use-popover-root.json
+++ b/docs/translations/api-docs/use-popover-root/use-popover-root.json
@@ -1,15 +1,13 @@
{
"hookDescription": "Manages the root state for a popover.",
"parametersDescriptions": {
- "closeDelay": {
- "description": "The delay in milliseconds until the popover content is closed."
- },
+ "closeDelay": { "description": "The delay in milliseconds until the popover popup is closed." },
"defaultOpen": {
"description": "Specifies whether the popover is open initially when uncontrolled."
},
- "delay": { "description": "The delay in milliseconds until the popover content is opened." },
+ "delay": { "description": "The delay in milliseconds until the popover popup is opened." },
"delayType": {
- "description": "The delay type to use. rest means the delay represents how long the user's cursor must rest on the trigger before the popover content is opened. hover means the delay represents how long to wait once the user's cursor has entered the trigger."
+ "description": "The delay type to use. rest means the delay represents how long the user's cursor must rest on the trigger before the popover popup is opened. hover means the delay represents how long to wait as soon as the user's cursor has entered the trigger."
},
"followCursorAxis": {
"description": "Determines which axis the tooltip should follow the cursor on."
@@ -18,15 +16,15 @@
"description": "If true, tooltip stays mounted in the DOM when closed."
},
"onOpenChange": {
- "description": "Callback fired when the popover content is requested to be opened or closed. Use when controlled."
+ "description": "Callback fired when the popover popup is requested to be opened or closed. Use when controlled."
},
"open": {
- "description": "If true, the popover content is open. Use when controlled."
+ "description": "If true, the popover popup is open. Use when controlled."
},
"openOnHover": {
- "description": "If true, the popover content opens when the trigger is hovered."
+ "description": "If true, the popover popup opens when the trigger is hovered."
},
- "popupEl": { "description": "The element that contains the popover content." },
+ "popupEl": { "description": "The element that contains the popover popup." },
"triggerEl": { "description": "The element that triggers the popover." }
},
"returnValueDescriptions": {}
diff --git a/docs/translations/translations.json b/docs/translations/translations.json
index 2bbe0b273d..c33ee040bb 100644
--- a/docs/translations/translations.json
+++ b/docs/translations/translations.json
@@ -223,10 +223,10 @@
"/base-ui/react-checkbox": "Checkbox",
"/base-ui/react-number-field": "Number Field",
"/base-ui/react-switch": "Switch",
- "navigation": "Navigation",
- "/base-ui/react-tabs": "Tabs",
"data-display": "Data display",
"/base-ui/react-popover": "Popover",
+ "navigation": "Navigation",
+ "/base-ui/react-tabs": "Tabs",
"/base-ui/guides": "How-to guides",
"/base-ui/guides/next-js-app-router": "Next.js App Router"
}
diff --git a/packages/mui-base/package.json b/packages/mui-base/package.json
index 55072d3394..d41b8f8319 100644
--- a/packages/mui-base/package.json
+++ b/packages/mui-base/package.json
@@ -40,13 +40,8 @@
},
"dependencies": {
"@babel/runtime": "^7.24.5",
-<<<<<<< HEAD
- "@floating-ui/react": "^0.26.13",
+ "@floating-ui/react": "^0.26.16",
"@floating-ui/react-dom": "^2.1.0",
-=======
- "@floating-ui/react": "^0.26.15",
- "@floating-ui/react-dom": "^2.0.9",
->>>>>>> ef816f13 (Refactor to use root context)
"@floating-ui/utils": "^0.2.2",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
diff --git a/packages/mui-base/src/Popover/Arrow/PopoverArrow.test.tsx b/packages/mui-base/src/Popover/Arrow/PopoverArrow.test.tsx
index e85d3cfecc..f459b4ed6d 100644
--- a/packages/mui-base/src/Popover/Arrow/PopoverArrow.test.tsx
+++ b/packages/mui-base/src/Popover/Arrow/PopoverArrow.test.tsx
@@ -11,7 +11,9 @@ describe('', () => {
render(node) {
return render(
- {node}
+
+ {node}
+ ,
);
},
diff --git a/packages/mui-base/src/Popover/Arrow/PopoverArrow.tsx b/packages/mui-base/src/Popover/Arrow/PopoverArrow.tsx
index 4305e7186e..f9a414b3c2 100644
--- a/packages/mui-base/src/Popover/Arrow/PopoverArrow.tsx
+++ b/packages/mui-base/src/Popover/Arrow/PopoverArrow.tsx
@@ -2,11 +2,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import type { PopoverArrowOwnerState, PopoverArrowProps } from './PopoverArrow.types';
-import { resolveClassName } from '../../utils/resolveClassName';
-import { usePopoverPositionerContext } from '../Popup/PopoverPopupContext';
-import { evaluateRenderProp } from '../../utils/evaluateRenderProp';
-import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
-import { useStyleHooks } from './useStyleHooks';
+import { usePopoverPositionerContext } from '../Positioner/PopoverPositionerContext';
+import { popoverArrowStyleHookMapping } from './styleHooks';
+import { usePopoverRootContext } from '../Root/PopoverRootContext';
+import { useComponentRenderer } from '../../utils/useComponentRenderer';
+import { useForkRef } from '../../utils/useForkRef';
/**
* The tooltip arrow caret element.
@@ -23,10 +23,10 @@ const PopoverArrow = React.forwardRef(function PopoverArrow(
props: PopoverArrowProps,
forwardedRef: React.ForwardedRef,
) {
- const { render: renderProp, hideWhenUncentered = false, className, ...otherProps } = props;
- const render = renderProp ?? ;
+ const { className, render, hideWhenUncentered = false, ...otherProps } = props;
- const { open, arrowRef, side, alignment, arrowUncentered, getArrowProps } =
+ const { open } = usePopoverRootContext();
+ const { arrowRef, side, alignment, arrowUncentered, getArrowProps } =
usePopoverPositionerContext();
const ownerState: PopoverArrowOwnerState = React.useMemo(
@@ -34,26 +34,30 @@ const PopoverArrow = React.forwardRef(function PopoverArrow(
open,
side,
alignment,
+ arrowUncentered,
}),
- [open, side, alignment],
+ [open, side, alignment, arrowUncentered],
);
- const mergedRef = useRenderPropForkRef(render, arrowRef, forwardedRef);
+ const mergedRef = useForkRef(arrowRef, forwardedRef);
- const styleHooks = useStyleHooks(ownerState);
-
- const arrowProps = getArrowProps({
+ const { renderElement } = useComponentRenderer({
+ propGetter: getArrowProps,
+ render: render ?? 'div',
+ className,
+ ownerState,
ref: mergedRef,
- className: resolveClassName(className, ownerState),
- ...styleHooks,
- ...otherProps,
- style: {
- ...(hideWhenUncentered && arrowUncentered && { visibility: 'hidden' }),
- ...otherProps.style,
+ extraProps: {
+ ...otherProps,
+ style: {
+ ...(hideWhenUncentered && arrowUncentered && { visibility: 'hidden' }),
+ ...otherProps.style,
+ },
},
+ customStyleHookMapping: popoverArrowStyleHookMapping,
});
- return evaluateRenderProp(render, arrowProps, ownerState);
+ return renderElement();
});
PopoverArrow.propTypes /* remove-proptypes */ = {
diff --git a/packages/mui-base/src/Popover/Arrow/PopoverArrow.types.ts b/packages/mui-base/src/Popover/Arrow/PopoverArrow.types.ts
index b9693c5dd5..abcf62eb4a 100644
--- a/packages/mui-base/src/Popover/Arrow/PopoverArrow.types.ts
+++ b/packages/mui-base/src/Popover/Arrow/PopoverArrow.types.ts
@@ -5,6 +5,7 @@ export type PopoverArrowOwnerState = {
open: boolean;
side: Side;
alignment: 'start' | 'end' | 'center';
+ arrowUncentered: boolean;
};
export interface PopoverArrowProps extends BaseUIComponentProps<'div', PopoverArrowOwnerState> {
diff --git a/packages/mui-base/src/Popover/Arrow/styleHooks.ts b/packages/mui-base/src/Popover/Arrow/styleHooks.ts
new file mode 100644
index 0000000000..10e36860f5
--- /dev/null
+++ b/packages/mui-base/src/Popover/Arrow/styleHooks.ts
@@ -0,0 +1,10 @@
+import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps';
+import type { PopoverArrowOwnerState } from './PopoverArrow.types';
+
+export const popoverArrowStyleHookMapping: CustomStyleHookMapping = {
+ open(value) {
+ return {
+ 'data-state': value ? 'open' : 'closed',
+ };
+ },
+};
diff --git a/packages/mui-base/src/Popover/Arrow/useStyleHooks.ts b/packages/mui-base/src/Popover/Arrow/useStyleHooks.ts
deleted file mode 100644
index e03b4586d0..0000000000
--- a/packages/mui-base/src/Popover/Arrow/useStyleHooks.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type { PopoverArrowOwnerState } from './PopoverArrow.types';
-import { getStyleHookProps } from '../../utils/getStyleHookProps';
-
-/**
- * @ignore - internal hook.
- */
-export function useStyleHooks(ownerState: PopoverArrowOwnerState) {
- return React.useMemo(() => {
- return getStyleHookProps(ownerState, {
- open(value) {
- return {
- 'data-state': value ? 'open' : 'closed',
- };
- },
- });
- }, [ownerState]);
-}
diff --git a/packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.tsx b/packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.tsx
index 39cfe2d85f..9ed0c7c879 100644
--- a/packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.tsx
+++ b/packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.tsx
@@ -3,14 +3,8 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { FloatingOverlay, FloatingPortal } from '@floating-ui/react';
import type { PopoverBackdropProps } from './PopoverBackdrop.types';
-import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
-import { resolveClassName } from '../../utils/resolveClassName';
-import { evaluateRenderProp } from '../../utils/evaluateRenderProp';
-
-function defaultRender(props: React.ComponentPropsWithRef<'div'>) {
- return ;
-}
+import { useComponentRenderer } from '../../utils/useComponentRenderer';
/**
* Renders a backdrop for the popover.
@@ -27,31 +21,32 @@ const PopoverBackdrop = React.forwardRef(function PopoverBackdrop(
props: PopoverBackdropProps,
forwardedRef: React.ForwardedRef,
) {
- const { className, render: renderProp, keepMounted = false, ...otherProps } = props;
- const render = renderProp ?? defaultRender;
+ const { className, render, keepMounted = false, container, ...otherProps } = props;
const { open, mounted } = usePopoverRootContext();
- const mergedRef = useRenderPropForkRef(render, forwardedRef);
-
const ownerState = React.useMemo(() => ({ open }), [open]);
+ const { renderElement } = useComponentRenderer({
+ render: render ?? ,
+ className,
+ ownerState,
+ ref: forwardedRef,
+ extraProps: {
+ ...otherProps,
+ style: {
+ zIndex: 2147483647, // max z-index
+ ...otherProps.style,
+ },
+ },
+ });
+
const shouldRender = keepMounted || mounted;
if (!shouldRender) {
return null;
}
- const backdropProps = {
- ref: mergedRef,
- className: resolveClassName(className, ownerState),
- ...otherProps,
- style: {
- zIndex: 2147483647, // max z-index
- ...otherProps.style,
- },
- };
-
- return {evaluateRenderProp(render, backdropProps, ownerState)};
+ return {renderElement()};
});
PopoverBackdrop.propTypes /* remove-proptypes */ = {
diff --git a/packages/mui-base/src/Popover/Popup/PopoverPopup.test.tsx b/packages/mui-base/src/Popover/Popup/PopoverPopup.test.tsx
index f5eab4db15..7ea62d2d50 100644
--- a/packages/mui-base/src/Popover/Popup/PopoverPopup.test.tsx
+++ b/packages/mui-base/src/Popover/Popup/PopoverPopup.test.tsx
@@ -11,7 +11,11 @@ describe('', () => {
inheritComponent: 'div',
refInstanceof: window.HTMLDivElement,
render(node) {
- return render({node});
+ return render(
+
+ {node}
+ ,
+ );
},
skip: ['reactTestRenderer'],
}));
@@ -19,7 +23,9 @@ describe('', () => {
it('should render the children', async () => {
render(
- Content
+
+ Content
+ ,
);
diff --git a/packages/mui-base/src/Popover/Popup/PopoverPopup.tsx b/packages/mui-base/src/Popover/Popup/PopoverPopup.tsx
index 53678f12d7..964572ee1b 100644
--- a/packages/mui-base/src/Popover/Popup/PopoverPopup.tsx
+++ b/packages/mui-base/src/Popover/Popup/PopoverPopup.tsx
@@ -1,13 +1,11 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import { resolveClassName } from '../../utils/resolveClassName';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
-import { PopoverPopupContext } from './PopoverPopupContext';
-import { evaluateRenderProp } from '../../utils/evaluateRenderProp';
-import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
import { PopoverPopupOwnerState, PopoverPopupProps } from './PopoverPopup.types';
-import { usePopoverPositionerStyleHooks } from './useStyleHooks';
+import { popoverPopupStyleHookMapping } from './styleHooks';
+import { useComponentRenderer } from '../../utils/useComponentRenderer';
+import { usePopoverPositionerContext } from '../Positioner/PopoverPositionerContext';
/**
* The popover popup element.
@@ -24,53 +22,38 @@ const PopoverPopup = React.forwardRef(function PopoverPopup(
props: PopoverPopupProps,
forwardedRef: React.ForwardedRef,
) {
- const { className, render: renderProp, ...otherProps } = props;
- const render = renderProp ?? ;
+ const { className, render, ...otherProps } = props;
const { open, transitionStatus } = usePopoverRootContext();
+ const { side, alignment } = usePopoverPositionerContext();
const ownerState: PopoverPopupOwnerState = React.useMemo(
() => ({
open,
- side: popover.side,
- alignment: popover.alignment,
- status: transitionStatus,
+ side,
+ alignment,
+ entering: transitionStatus === 'entering',
+ exiting: transitionStatus === 'exiting',
}),
- [open, transitionStatus, popover.side, popover.alignment],
+ [open, transitionStatus, side, alignment],
);
- const contextValue = React.useMemo(
- () => ({
- ...ownerState,
- arrowRef: popover.arrowRef,
- arrowUncentered: popover.arrowUncentered,
- getArrowProps: popover.getArrowProps,
- }),
- [ownerState, popover.arrowRef, popover.arrowUncentered, popover.getArrowProps],
- );
-
- const mergedRef = useRenderPropForkRef(render, forwardedRef);
- const styleHooks = usePopoverPositionerStyleHooks(ownerState);
-
- // The content element needs to be a child of a wrapper floating element in order to avoid
- // conflicts with CSS transitions and the positioning transform.
- const popupProps = {
- ref: mergedRef,
- className: resolveClassName(className, ownerState),
- ...styleHooks,
- ...otherProps,
- style: {
- // must be relative to the content element.
- position: 'relative',
- ...otherProps.style,
+ const { renderElement } = useComponentRenderer({
+ ref: forwardedRef,
+ render: render ?? 'div',
+ className,
+ ownerState,
+ customStyleHookMapping: popoverPopupStyleHookMapping,
+ extraProps: {
+ style: {
+ position: 'relative',
+ ...otherProps.style,
+ },
+ ...otherProps,
},
- } as const;
+ });
- return (
-
- {evaluateRenderProp(render, popupProps, ownerState)}
-
- );
+ return renderElement();
});
PopoverPopup.propTypes /* remove-proptypes */ = {
@@ -78,26 +61,6 @@ PopoverPopup.propTypes /* remove-proptypes */ = {
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
- /**
- * The alignment of the popover element to the anchor element along its cross axis.
- * @default 'center'
- */
- alignment: PropTypes.oneOf(['center', 'end', 'start']),
- /**
- * The offset of the popover element along its alignment axis.
- * @default 0
- */
- alignmentOffset: PropTypes.number,
- /**
- * The anchor element to which the popover content will be placed at.
- */
- anchor: PropTypes /* @typescript-to-proptypes-ignore */.any,
- /**
- * Determines the padding between the arrow and the popover content. Useful when the popover
- * has rounded corners via `border-radius`.
- * @default 5
- */
- arrowPadding: PropTypes.number,
/**
* @ignore
*/
@@ -106,64 +69,10 @@ PopoverPopup.propTypes /* remove-proptypes */ = {
* Class names applied to the element or a function that returns them based on the component's state.
*/
className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
- /**
- * The boundary that the popover element should be constrained to.
- * @default 'clippingAncestors'
- */
- collisionBoundary: PropTypes /* @typescript-to-proptypes-ignore */.any,
- /**
- * The padding of the collision boundary.
- * @default 5
- */
- collisionPadding: PropTypes.oneOfType([
- PropTypes.number,
- PropTypes.shape({
- bottom: PropTypes.number,
- left: PropTypes.number,
- right: PropTypes.number,
- top: PropTypes.number,
- }),
- ]),
- /**
- * The container element to which the popover content will be appended to.
- */
- container: PropTypes /* @typescript-to-proptypes-ignore */.any,
- /**
- * Determines which axis the popover should follow the cursor on.
- * @default 'none'
- */
- followCursorAxis: PropTypes.oneOf(['both', 'none', 'x', 'y']),
- /**
- * If `true`, the popover will be hidden if it is detached from its anchor element due to
- * differing clipping contexts.
- * @default false
- */
- hideWhenDetached: PropTypes.bool,
- /**
- * If `true`, the popover content will be kept mounted in the DOM.
- * @default false
- */
- keepMounted: PropTypes.bool,
/**
* A function to customize rendering of the component.
*/
render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
- /**
- * The side of the anchor element that the popover element should align to.
- * @default 'bottom'
- */
- side: PropTypes.oneOf(['bottom', 'left', 'right', 'top']),
- /**
- * The gap between the anchor element and the popover element.
- * @default 0
- */
- sideOffset: PropTypes.number,
- /**
- * If `true`, allow the popover to remain in stuck view while the anchor element is scrolled out
- * of view.
- * @default false
- */
- sticky: PropTypes.bool,
/**
* @ignore
*/
diff --git a/packages/mui-base/src/Popover/Popup/PopoverPopup.types.ts b/packages/mui-base/src/Popover/Popup/PopoverPopup.types.ts
index 7bef7edef8..e55c607d6c 100644
--- a/packages/mui-base/src/Popover/Popup/PopoverPopup.types.ts
+++ b/packages/mui-base/src/Popover/Popup/PopoverPopup.types.ts
@@ -1,19 +1,12 @@
import type { Side } from '@floating-ui/react';
-import type { BaseUIComponentProps, GenericHTMLProps } from '../../utils/BaseUI.types';
-
-export interface PopoverPopupContextValue {
- open: boolean;
- side: Side;
- alignment: 'start' | 'end' | 'center';
- arrowRef: React.MutableRefObject;
- arrowUncentered: boolean;
- getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
-}
+import type { BaseUIComponentProps } from '../../utils/BaseUI.types';
export type PopoverPopupOwnerState = {
open: boolean;
side: Side;
alignment: 'start' | 'end' | 'center';
+ entering: boolean;
+ exiting: boolean;
};
export interface PopoverPopupProps extends BaseUIComponentProps<'div', PopoverPopupOwnerState> {}
diff --git a/packages/mui-base/src/Popover/Popup/PopoverPopupContext.ts b/packages/mui-base/src/Popover/Popup/PopoverPopupContext.ts
deleted file mode 100644
index f86e076101..0000000000
--- a/packages/mui-base/src/Popover/Popup/PopoverPopupContext.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import * as React from 'react';
-import type { PopoverPopupContextValue } from './PopoverPopup.types';
-
-export const PopoverPopupContext = React.createContext(null);
-
-export function usePopoverPositionerContext() {
- const context = React.useContext(PopoverPopupContext);
- if (context === null) {
- throw new Error(' must be used within the component');
- }
- return context;
-}
diff --git a/packages/mui-base/src/Popover/Popup/styleHooks.ts b/packages/mui-base/src/Popover/Popup/styleHooks.ts
new file mode 100644
index 0000000000..d6f2906288
--- /dev/null
+++ b/packages/mui-base/src/Popover/Popup/styleHooks.ts
@@ -0,0 +1,16 @@
+import type { PopoverPopupOwnerState } from './PopoverPopup.types';
+import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps';
+
+export const popoverPopupStyleHookMapping: CustomStyleHookMapping = {
+ entering(value) {
+ return value ? { 'data-entering': '' } : null;
+ },
+ exiting(value) {
+ return value ? { 'data-exiting': '' } : null;
+ },
+ open(value) {
+ return {
+ 'data-state': value ? 'open' : 'closed',
+ };
+ },
+};
diff --git a/packages/mui-base/src/Popover/Popup/useStyleHooks.ts b/packages/mui-base/src/Popover/Popup/useStyleHooks.ts
deleted file mode 100644
index a24051d183..0000000000
--- a/packages/mui-base/src/Popover/Popup/useStyleHooks.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import { getStyleHookProps } from '../../utils/getStyleHookProps';
-import type { PopoverPopupOwnerState } from './PopoverPopup.types';
-
-/**
- * @ignore - internal hook.
- */
-export function usePopoverPositionerStyleHooks(ownerState: PopoverPopupOwnerState) {
- return React.useMemo(() => {
- return getStyleHookProps(ownerState, {
- open(value) {
- return {
- 'data-state': value ? 'open' : 'closed',
- };
- },
- });
- }, [ownerState]);
-}
diff --git a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.test.tsx b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.test.tsx
index e69de29bb2..471c03071e 100644
--- a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.test.tsx
+++ b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.test.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import * as Popover from '@base_ui/react/Popover';
+import { createRenderer } from '@mui/internal-test-utils';
+import { describeConformance } from '../../../test/describeConformance';
+
+describe('', () => {
+ const { render } = createRenderer();
+
+ describeConformance(, () => ({
+ inheritComponent: 'div',
+ refInstanceof: window.HTMLDivElement,
+ render(node) {
+ return render({node});
+ },
+ skip: ['reactTestRenderer'],
+ }));
+});
diff --git a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.tsx b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.tsx
index 6bdc31b531..e348004060 100644
--- a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.tsx
+++ b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.tsx
@@ -1,10 +1,28 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { FloatingFocusManager, FloatingPortal } from '@floating-ui/react';
-import type { PopoverPositionerProps } from './PopoverPositioner.types';
-import { usePopoverPositioner } from './usePopoverPositioner';
+import type {
+ PopoverPositionerContextValue,
+ PopoverPositionerOwnerState,
+ PopoverPositionerProps,
+} from './PopoverPositioner.types';
+import { useComponentRenderer } from '../../utils/useComponentRenderer';
+import { useForkRef } from '../../utils/useForkRef';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
+import { usePopoverPositioner } from './usePopoverPositioner';
+import { PopoverPositionerContext } from './PopoverPositionerContext';
+/**
+ * The popover positioner element.
+ *
+ * Demos:
+ *
+ * - [Popover](https://mui.com/base-ui/react-popover/)
+ *
+ * API:
+ *
+ * - [PopoverPositioner API](https://mui.com/base-ui/react-popover/components-api/#popover-positioner)
+ */
const PopoverPositioner = React.forwardRef(function PopoverPositioner(
props: PopoverPositionerProps,
forwardedRef: React.ForwardedRef,
@@ -12,7 +30,7 @@ const PopoverPositioner = React.forwardRef(function PopoverPositioner(
const {
anchor,
className,
- render: renderProp,
+ render,
side = 'bottom',
alignment = 'center',
sideOffset = 0,
@@ -47,14 +65,56 @@ const PopoverPositioner = React.forwardRef(function PopoverPositioner(
followCursorAxis,
});
+ const ownerState: PopoverPositionerOwnerState = React.useMemo(
+ () => ({
+ open,
+ side: positioner.side,
+ alignment: positioner.alignment,
+ }),
+ [open, positioner.side, positioner.alignment],
+ );
+
+ const contextValue: PopoverPositionerContextValue = React.useMemo(
+ () => ({
+ side: positioner.side,
+ alignment: positioner.alignment,
+ arrowRef: positioner.arrowRef,
+ arrowUncentered: positioner.arrowUncentered,
+ getArrowProps: positioner.getArrowProps,
+ }),
+ [
+ positioner.side,
+ positioner.alignment,
+ positioner.arrowRef,
+ positioner.arrowUncentered,
+ positioner.getArrowProps,
+ ],
+ );
+
+ const mergedRef = useForkRef(forwardedRef, setPopupEl);
+
+ const { renderElement } = useComponentRenderer({
+ propGetter: positioner.getPositionerProps,
+ render: render ?? 'div',
+ className,
+ ownerState,
+ ref: mergedRef,
+ extraProps: otherProps,
+ });
+
+ const shouldRender = keepMounted || positioner.mounted;
+ if (!shouldRender) {
+ return null;
+ }
+
return (
-
-
-
- {props.children}
-
-
-
+
+
+
+ {renderElement()}
+
+
+
);
});
@@ -64,17 +124,141 @@ PopoverPositioner.propTypes /* remove-proptypes */ = {
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
- * @ignore
+ * The alignment of the popover element to the anchor element along its cross axis.
+ * @default 'center'
+ */
+ alignment: PropTypes.oneOf(['center', 'end', 'start']),
+ /**
+ * The offset of the popover element along its alignment axis.
+ * @default 0
+ */
+ alignmentOffset: PropTypes.number,
+ /**
+ * The anchor element to which the popover popup will be placed at.
+ */
+ anchor: PropTypes.oneOfType([
+ function (props, propName) {
+ if (props[propName] == null) {
+ return new Error("Prop '" + propName + "' is required but wasn't specified");
+ } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
+ return new Error("Expected prop '" + propName + "' to be of type Element");
+ }
+ },
+ PropTypes.func,
+ PropTypes.shape({
+ contextElement: function (props, propName) {
+ if (props[propName] == null) {
+ return null;
+ } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
+ return new Error("Expected prop '" + propName + "' to be of type Element");
+ }
+ },
+ getBoundingClientRect: PropTypes.func.isRequired,
+ }),
+ PropTypes.shape({
+ current: function (props, propName) {
+ if (props[propName] == null) {
+ return null;
+ } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
+ return new Error("Expected prop '" + propName + "' to be of type Element");
+ }
+ },
+ }),
+ ]),
+ /**
+ * Determines the padding between the arrow and the popover popup's edges. Useful when the popover
+ * popup has rounded corners via `border-radius`.
+ * @default 5
*/
- children: PropTypes.element.isRequired,
+ arrowPadding: PropTypes.number,
/**
* @ignore
*/
+ children: PropTypes.node,
+ /**
+ * Class names applied to the element or a function that returns them based on the component's state.
+ */
+ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
+ /**
+ * The boundary that the popover element should be constrained to.
+ * @default 'clippingAncestors'
+ */
+ collisionBoundary: PropTypes.oneOfType([
+ PropTypes.oneOf(['clippingAncestors']),
+ PropTypes.arrayOf(function (props, propName) {
+ if (props[propName] == null) {
+ return new Error("Prop '" + propName + "' is required but wasn't specified");
+ } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
+ return new Error("Expected prop '" + propName + "' to be of type Element");
+ }
+ }),
+ function (props, propName) {
+ if (props[propName] == null) {
+ return new Error("Prop '" + propName + "' is required but wasn't specified");
+ } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
+ return new Error("Expected prop '" + propName + "' to be of type Element");
+ }
+ },
+ PropTypes.shape({
+ height: PropTypes.number.isRequired,
+ width: PropTypes.number.isRequired,
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ }),
+ ]),
+ /**
+ * The padding of the collision boundary.
+ * @default 5
+ */
+ collisionPadding: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.shape({
+ bottom: PropTypes.number,
+ left: PropTypes.number,
+ right: PropTypes.number,
+ top: PropTypes.number,
+ }),
+ ]),
+ /**
+ * The container element to which the popover popup will be appended to.
+ */
container: PropTypes /* @typescript-to-proptypes-ignore */.any,
/**
- * @ignore
+ * Determines which axis the popover should follow the cursor on.
+ * @default 'none'
+ */
+ followCursorAxis: PropTypes.oneOf(['both', 'none', 'x', 'y']),
+ /**
+ * If `true`, the popover will be hidden if it is detached from its anchor element due to
+ * differing clipping contexts.
+ * @default false
+ */
+ hideWhenDetached: PropTypes.bool,
+ /**
+ * If `true`, tooltip stays mounted in the DOM when closed.
+ * @default false
+ */
+ keepMounted: PropTypes.bool,
+ /**
+ * A function to customize rendering of the component.
+ */
+ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
+ /**
+ * The side of the anchor element that the popover element should align to.
+ * @default 'bottom'
+ */
+ side: PropTypes.oneOf(['bottom', 'left', 'right', 'top']),
+ /**
+ * The gap between the anchor element and the popover element.
+ * @default 0
+ */
+ sideOffset: PropTypes.number,
+ /**
+ * If `true`, allow the popover to remain in stuck view while the anchor element is scrolled out
+ * of view.
+ * @default false
*/
- unstable_floatingContext: PropTypes /* @typescript-to-proptypes-ignore */.any,
+ sticky: PropTypes.bool,
} as any;
export { PopoverPositioner };
diff --git a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.types.ts b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.types.ts
index b5c7ebb341..f6502a8cd1 100644
--- a/packages/mui-base/src/Popover/Positioner/PopoverPositioner.types.ts
+++ b/packages/mui-base/src/Popover/Positioner/PopoverPositioner.types.ts
@@ -1,7 +1,15 @@
-import type { FloatingContext, Side } from '@floating-ui/react';
-import type { BaseUIComponentProps } from '../../utils/BaseUI.types';
+import type { Side } from '@floating-ui/react';
+import type { BaseUIComponentProps, GenericHTMLProps } from '../../utils/BaseUI.types';
import type { PopoverPositionerParameters } from './usePopoverPositioner.types';
+export interface PopoverPositionerContextValue {
+ side: Side;
+ alignment: 'start' | 'end' | 'center';
+ arrowRef: React.MutableRefObject;
+ arrowUncentered: boolean;
+ getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
+}
+
export type PopoverPositionerOwnerState = {
open: boolean;
side: Side;
@@ -10,7 +18,4 @@ export type PopoverPositionerOwnerState = {
export interface PopoverPositionerProps
extends PopoverPositionerParameters,
- BaseUIComponentProps<'div', PopoverPositionerOwnerState> {
- children: React.ReactElement;
- unstable_floatingContext: FloatingContext;
-}
+ BaseUIComponentProps<'div', PopoverPositionerOwnerState> {}
diff --git a/packages/mui-base/src/Popover/Positioner/PopoverPositionerContext.ts b/packages/mui-base/src/Popover/Positioner/PopoverPositionerContext.ts
new file mode 100644
index 0000000000..a00f473694
--- /dev/null
+++ b/packages/mui-base/src/Popover/Positioner/PopoverPositionerContext.ts
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import type { PopoverPositionerContextValue } from './PopoverPositioner.types';
+
+export const PopoverPositionerContext = React.createContext(
+ null,
+);
+
+export function usePopoverPositionerContext() {
+ const context = React.useContext(PopoverPositionerContext);
+ if (context === null) {
+ throw new Error(
+ ' and must be used within the component',
+ );
+ }
+ return context;
+}
diff --git a/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.tsx b/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.tsx
index f80d5a8611..c20e8ae6d0 100644
--- a/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.tsx
+++ b/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.tsx
@@ -32,7 +32,7 @@ import { usePopoverRootContext } from '../Root/PopoverRootContext';
*
* API:
*
- * - [usePopoverPositioner API](https://mui.com/base-ui/react-popover/hooks-api/#use-popover-popup)
+ * - [usePopoverPositioner API](https://mui.com/base-ui/react-popover/hooks-api/#use-popover-positioner)
*/
export function usePopoverPositioner(
params: UsePopoverPositionerParameters,
@@ -52,7 +52,7 @@ export function usePopoverPositioner(
arrowPadding = 5,
} = params;
- const { mounted, setMounted, rootContext, getRootPopupProps } = usePopoverRootContext();
+ const { mounted, rootContext, getRootPopupProps } = usePopoverRootContext();
// Using a ref assumes that the arrow element is always present in the DOM for the lifetime of the
// tooltip. If this assumption ends up being false, we can switch to state to manage the arrow's
@@ -112,12 +112,15 @@ export function usePopoverPositioner(
});
},
}),
- arrow(() => ({
- // `transform-origin` calculations rely on an element existing. If the arrow hasn't been set,
- // we'll create a fake element.
- element: arrowRef.current || document.createElement('div'),
- padding: arrowPadding,
- })),
+ arrow(
+ () => ({
+ // `transform-origin` calculations rely on an element existing. If the arrow hasn't been set,
+ // we'll create a fake element.
+ element: arrowRef.current || document.createElement('div'),
+ padding: arrowPadding,
+ }),
+ [arrowPadding],
+ ),
hideWhenDetached && hide(),
{
name: 'transformOrigin',
@@ -184,37 +187,27 @@ export function usePopoverPositioner(
const renderedSide = getSide(renderedPlacement);
const renderedAlignment = getAlignment(renderedPlacement) || 'center';
const isHidden = hideWhenDetached && middlewareData.hide?.referenceHidden;
- // TODO: While in the instant phase, if the tooltip is closing and no other tooltip is opening,
- // the `instantType` should be `undefined`. This ensures the close animation will play. This may
- // need an internal fix in Floating UI.
- const getPopupProps: UsePopoverPositionerReturnValue['getPopupProps'] = React.useCallback(
- (externalProps = {}) => {
- function handleTransitionOrAnimationEnd({ target }: React.SyntheticEvent) {
- const popupElement = refs.floating.current?.firstElementChild;
- if (target === popupElement) {
- setMounted((prevMounted) => (prevMounted ? false : prevMounted));
- }
- }
-
- return mergeReactProps(
- externalProps,
- getRootPopupProps({
- style: {
- ...floatingStyles,
- maxWidth: 'var(--available-width)',
- maxHeight: 'var(--available-height)',
- visibility: isHidden ? 'hidden' : undefined,
- pointerEvents: isHidden || followCursorAxis === 'both' ? 'none' : undefined,
- zIndex: 2147483647, // max z-index
- },
- onTransitionEnd: handleTransitionOrAnimationEnd,
- onAnimationEnd: handleTransitionOrAnimationEnd,
- }),
- );
- },
- [getRootPopupProps, floatingStyles, isHidden, followCursorAxis, setMounted, refs],
- );
+ const getPositionerProps: UsePopoverPositionerReturnValue['getPositionerProps'] =
+ React.useCallback(
+ (externalProps = {}) => {
+ return mergeReactProps(
+ externalProps,
+ getRootPopupProps({
+ role: 'presentation',
+ style: {
+ ...floatingStyles,
+ maxWidth: 'var(--available-width)',
+ maxHeight: 'var(--available-height)',
+ visibility: isHidden ? 'hidden' : undefined,
+ pointerEvents: isHidden || followCursorAxis === 'both' ? 'none' : undefined,
+ zIndex: 2147483647, // max z-index
+ },
+ }),
+ );
+ },
+ [getRootPopupProps, floatingStyles, isHidden, followCursorAxis],
+ );
const getArrowProps: UsePopoverPositionerReturnValue['getArrowProps'] = React.useCallback(
(externalProps = {}) => {
@@ -234,7 +227,7 @@ export function usePopoverPositioner(
return React.useMemo(
() => ({
mounted,
- getPopupProps,
+ getPositionerProps,
getArrowProps,
arrowRef,
arrowUncentered,
@@ -244,7 +237,7 @@ export function usePopoverPositioner(
}),
[
mounted,
- getPopupProps,
+ getPositionerProps,
getArrowProps,
arrowUncentered,
renderedSide,
diff --git a/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.types.ts b/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.types.ts
index b991a83dda..0e684d55b0 100644
--- a/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.types.ts
+++ b/packages/mui-base/src/Popover/Positioner/usePopoverPositioner.types.ts
@@ -8,7 +8,7 @@ export interface PopoverPositionerParameters {
*/
open?: boolean;
/**
- * The anchor element to which the popover content will be placed at.
+ * The anchor element to which the popover popup will be placed at.
*/
anchor?:
| Element
@@ -68,8 +68,8 @@ export interface PopoverPositionerParameters {
*/
followCursorAxis?: 'none' | 'both' | 'x' | 'y';
/**
- * Determines the padding between the arrow and the popover content. Useful when the popover
- * has rounded corners via `border-radius`.
+ * Determines the padding between the arrow and the popover popup's edges. Useful when the popover
+ * popup has rounded corners via `border-radius`.
* @default 5
*/
arrowPadding?: number;
@@ -84,9 +84,9 @@ export interface UsePopoverPositionerParameters extends PopoverPositionerParamet
export interface UsePopoverPositionerReturnValue {
/**
- * Props to spread on the popover content element.
+ * Props to spread on the popover positioner element.
*/
- getPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
+ getPositionerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
/**
* Props to spread on the popover arrow element.
*/
diff --git a/packages/mui-base/src/Popover/Group/PopoverGroup.tsx b/packages/mui-base/src/Popover/Provider/PopoverProvider.tsx
similarity index 75%
rename from packages/mui-base/src/Popover/Group/PopoverGroup.tsx
rename to packages/mui-base/src/Popover/Provider/PopoverProvider.tsx
index dadc594c02..b1535b4328 100644
--- a/packages/mui-base/src/Popover/Group/PopoverGroup.tsx
+++ b/packages/mui-base/src/Popover/Provider/PopoverProvider.tsx
@@ -2,11 +2,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { FloatingDelayGroup } from '@floating-ui/react';
-import type { PopoverGroupProps } from './PopoverGroup.types';
+import type { PopoverProviderProps } from './PopoverProvider.types';
/**
- * Groups popovers' delays together so that once one of the popovers opens, subsequent popovers will
- * not open with a delay.
+ * Provides a shared delay for popovers so that once a popover is shown, the rest of the popovers in
+ * the group will not wait for the delay before showing.
*
* Demos:
*
@@ -16,7 +16,7 @@ import type { PopoverGroupProps } from './PopoverGroup.types';
*
* - [PopoverGroup API](https://mui.com/base-ui/react-popover/components-api/#popover-group)
*/
-function PopoverGroup(props: PopoverGroupProps) {
+function PopoverProvider(props: PopoverProviderProps) {
const { delay = 0, closeDelay = 0, timeout = 400 } = props;
return (
@@ -25,7 +25,7 @@ function PopoverGroup(props: PopoverGroupProps) {
);
}
-PopoverGroup.propTypes /* remove-proptypes */ = {
+PopoverProvider.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
@@ -35,12 +35,12 @@ PopoverGroup.propTypes /* remove-proptypes */ = {
*/
children: PropTypes.node,
/**
- * The delay in milliseconds until the popover content is closed.
+ * The delay in milliseconds until the popover popup is closed.
* @default 0
*/
closeDelay: PropTypes.number,
/**
- * The delay in milliseconds until popovers within the group are open.
+ * The delay in milliseconds until the popover popup is opened.
* @default 0
*/
delay: PropTypes.number,
@@ -52,4 +52,4 @@ PopoverGroup.propTypes /* remove-proptypes */ = {
timeout: PropTypes.number,
} as any;
-export { PopoverGroup };
+export { PopoverProvider };
diff --git a/packages/mui-base/src/Popover/Group/PopoverGroup.types.ts b/packages/mui-base/src/Popover/Provider/PopoverProvider.types.ts
similarity index 63%
rename from packages/mui-base/src/Popover/Group/PopoverGroup.types.ts
rename to packages/mui-base/src/Popover/Provider/PopoverProvider.types.ts
index 52be77525e..da5e11e2e6 100644
--- a/packages/mui-base/src/Popover/Group/PopoverGroup.types.ts
+++ b/packages/mui-base/src/Popover/Provider/PopoverProvider.types.ts
@@ -1,12 +1,12 @@
-export interface PopoverGroupProps {
+export interface PopoverProviderProps {
children?: React.ReactNode;
/**
- * The delay in milliseconds until popovers within the group are open.
+ * The delay in milliseconds until the popover popup is opened.
* @default 0
*/
delay?: number;
/**
- * The delay in milliseconds until the popover content is closed.
+ * The delay in milliseconds until the popover popup is closed.
* @default 0
*/
closeDelay?: number;
diff --git a/packages/mui-base/src/Popover/Root/PopoverRoot.test.tsx b/packages/mui-base/src/Popover/Root/PopoverRoot.test.tsx
index 46f8419df3..4cf132f944 100644
--- a/packages/mui-base/src/Popover/Root/PopoverRoot.test.tsx
+++ b/packages/mui-base/src/Popover/Root/PopoverRoot.test.tsx
@@ -12,9 +12,7 @@ describe('', () => {
it('should render the children', () => {
render(
-
-
Content
-
+ Content,
);
@@ -25,10 +23,10 @@ describe('', () => {
it('should open when the anchor is clicked', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -44,10 +42,10 @@ describe('', () => {
it('should close when the anchor is clicked twice', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -69,7 +67,9 @@ describe('', () => {
it('should open when controlled open is true', async () => {
render(
- Content
+
+ Content
+ ,
);
@@ -81,7 +81,9 @@ describe('', () => {
it('should close when controlled open is false', async () => {
render(
- Content
+
+ Content
+ ,
);
@@ -102,10 +104,10 @@ describe('', () => {
setOpen(nextOpen);
}}
>
-
-
-
- Content
+
+
+ Content
+
);
}
@@ -144,10 +146,10 @@ describe('', () => {
setOpen(nextOpen);
}}
>
-
-
-
- Content
+
+
+ Content
+
);
}
@@ -172,10 +174,10 @@ describe('', () => {
it('should open when the component is rendered', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -187,10 +189,10 @@ describe('', () => {
it('should not open when the component is rendered and open is controlled', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -200,10 +202,10 @@ describe('', () => {
it('should not close when the component is rendered and open is controlled', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -215,9 +217,7 @@ describe('', () => {
it('should remain uncontrolled', async () => {
render(
-
-
-
+ Content,
);
@@ -240,10 +240,10 @@ describe('', () => {
it('should open after delay with rest type by default', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -266,10 +266,10 @@ describe('', () => {
it('should open after delay with hover type', async () => {
render(
-
-
-
- Content
+
+
+ Content
+ ,
);
@@ -299,7 +299,9 @@ describe('', () => {
- Content
+
+ Content
+
,
);
diff --git a/packages/mui-base/src/Popover/Root/PopoverRoot.tsx b/packages/mui-base/src/Popover/Root/PopoverRoot.tsx
index 5fd44b6407..e1333cb5e2 100644
--- a/packages/mui-base/src/Popover/Root/PopoverRoot.tsx
+++ b/packages/mui-base/src/Popover/Root/PopoverRoot.tsx
@@ -101,7 +101,7 @@ PopoverRoot.propTypes /* remove-proptypes */ = {
*/
children: PropTypes.node,
/**
- * The delay in milliseconds until the popover content is closed.
+ * The delay in milliseconds until the popover popup is closed.
* @default 0
*/
closeDelay: PropTypes.number,
@@ -110,14 +110,14 @@ PopoverRoot.propTypes /* remove-proptypes */ = {
*/
defaultOpen: PropTypes.bool,
/**
- * The delay in milliseconds until the popover content is opened.
+ * The delay in milliseconds until the popover popup is opened.
* @default 100
*/
delay: PropTypes.number,
/**
* The delay type to use. `rest` means the `delay` represents how long the user's cursor must
- * rest on the trigger before the popover content is opened. `hover` means the `delay` represents
- * how long to wait once the user's cursor has entered the trigger.
+ * rest on the trigger before the popover popup is opened. `hover` means the `delay` represents
+ * how long to wait as soon as the user's cursor has entered the trigger.
* @default 'rest'
*/
delayType: PropTypes.oneOf(['hover', 'rest']),
@@ -127,16 +127,16 @@ PopoverRoot.propTypes /* remove-proptypes */ = {
*/
followCursorAxis: PropTypes.oneOf(['both', 'none', 'x', 'y']),
/**
- * Callback fired when the popover content is requested to be opened or closed. Use when
+ * Callback fired when the popover popup is requested to be opened or closed. Use when
* controlled.
*/
onOpenChange: PropTypes.func,
/**
- * If `true`, the popover content is open. Use when controlled.
+ * If `true`, the popover popup is open. Use when controlled.
*/
open: PropTypes.bool,
/**
- * If `true`, the popover content opens when the trigger is hovered.
+ * If `true`, the popover popup opens when the trigger is hovered.
* @default false
*/
openOnHover: PropTypes.bool,
diff --git a/packages/mui-base/src/Popover/Root/PopoverRoot.types.ts b/packages/mui-base/src/Popover/Root/PopoverRoot.types.ts
index 63bbc057b4..78fc2b2061 100644
--- a/packages/mui-base/src/Popover/Root/PopoverRoot.types.ts
+++ b/packages/mui-base/src/Popover/Root/PopoverRoot.types.ts
@@ -1,5 +1,5 @@
import type { FloatingRootContext, OpenChangeReason } from '@floating-ui/react';
-import type { TransitionStatus } from '../../useTransitionStatus';
+import type { TransitionStatus } from '../../utils/useTransitionStatus';
import type { GenericHTMLProps } from '../../utils/BaseUI.types';
export interface PopoverRootContextValue {
@@ -15,7 +15,7 @@ export interface PopoverRootContextValue {
delayType: 'rest' | 'hover';
mounted: boolean;
setMounted: React.Dispatch>;
- transitionStatus: TransitionStatus | undefined;
+ transitionStatus: TransitionStatus;
rootContext: FloatingRootContext;
getTriggerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
@@ -28,11 +28,11 @@ export type RootOwnerState = {
export interface PopoverRootProps {
children: React.ReactNode;
/**
- * If `true`, the popover content is open. Use when controlled.
+ * If `true`, the popover popup is open. Use when controlled.
*/
open?: boolean;
/**
- * Callback fired when the popover content is requested to be opened or closed. Use when
+ * Callback fired when the popover popup is requested to be opened or closed. Use when
* controlled.
*/
onOpenChange?: (isOpen: boolean, event?: Event, reason?: OpenChangeReason) => void;
@@ -41,24 +41,24 @@ export interface PopoverRootProps {
*/
defaultOpen?: boolean;
/**
- * If `true`, the popover content opens when the trigger is hovered.
+ * If `true`, the popover popup opens when the trigger is hovered.
* @default false
*/
openOnHover?: boolean;
/**
- * The delay in milliseconds until the popover content is opened.
+ * The delay in milliseconds until the popover popup is opened.
* @default 100
*/
delay?: number;
/**
- * The delay in milliseconds until the popover content is closed.
+ * The delay in milliseconds until the popover popup is closed.
* @default 0
*/
closeDelay?: number;
/**
* The delay type to use. `rest` means the `delay` represents how long the user's cursor must
- * rest on the trigger before the popover content is opened. `hover` means the `delay` represents
- * how long to wait once the user's cursor has entered the trigger.
+ * rest on the trigger before the popover popup is opened. `hover` means the `delay` represents
+ * how long to wait as soon as the user's cursor has entered the trigger.
* @default 'rest'
*/
delayType?: 'rest' | 'hover';
diff --git a/packages/mui-base/src/Popover/Root/usePopoverRoot.ts b/packages/mui-base/src/Popover/Root/usePopoverRoot.ts
index 018489f160..e197eafc2f 100644
--- a/packages/mui-base/src/Popover/Root/usePopoverRoot.ts
+++ b/packages/mui-base/src/Popover/Root/usePopoverRoot.ts
@@ -13,7 +13,7 @@ import {
} from '@floating-ui/react';
import type { UsePopoverRootParameters, UsePopoverRootReturnValue } from './usePopoverRoot.types';
import { useControlled } from '../../utils/useControlled';
-import { useTransitionStatus } from '../../useTransitionStatus';
+import { useTransitionStatus } from '../../utils/useTransitionStatus';
import { ownerWindow } from '../../utils/owner';
/**
diff --git a/packages/mui-base/src/Popover/Root/usePopoverRoot.types.ts b/packages/mui-base/src/Popover/Root/usePopoverRoot.types.ts
index 44defc8fe7..dbdc0f7154 100644
--- a/packages/mui-base/src/Popover/Root/usePopoverRoot.types.ts
+++ b/packages/mui-base/src/Popover/Root/usePopoverRoot.types.ts
@@ -3,15 +3,15 @@ import type {
OpenChangeReason,
UseInteractionsReturn,
} from '@floating-ui/react';
-import type { TransitionStatus } from '../../useTransitionStatus';
+import type { TransitionStatus } from '../../utils/useTransitionStatus';
export interface UsePopoverRootParameters {
/**
- * If `true`, the popover content is open. Use when controlled.
+ * If `true`, the popover popup is open. Use when controlled.
*/
open?: boolean;
/**
- * Callback fired when the popover content is requested to be opened or closed. Use when
+ * Callback fired when the popover popup is requested to be opened or closed. Use when
* controlled.
*/
onOpenChange?: (isOpen: boolean, event?: Event, reason?: OpenChangeReason) => void;
@@ -21,24 +21,24 @@ export interface UsePopoverRootParameters {
*/
defaultOpen?: boolean;
/**
- * If `true`, the popover content opens when the trigger is hovered.
+ * If `true`, the popover popup opens when the trigger is hovered.
* @default false
*/
openOnHover?: boolean;
/**
- * The delay in milliseconds until the popover content is opened.
+ * The delay in milliseconds until the popover popup is opened.
* @default 100
*/
delay?: number;
/**
- * The delay in milliseconds until the popover content is closed.
+ * The delay in milliseconds until the popover popup is closed.
* @default 0
*/
closeDelay?: number;
/**
* The delay type to use. `rest` means the `delay` represents how long the user's cursor must
- * rest on the trigger before the popover content is opened. `hover` means the `delay` represents
- * how long to wait once the user's cursor has entered the trigger.
+ * rest on the trigger before the popover popup is opened. `hover` means the `delay` represents
+ * how long to wait as soon as the user's cursor has entered the trigger.
* @default 'rest'
*/
delayType?: 'hover' | 'rest';
@@ -47,7 +47,7 @@ export interface UsePopoverRootParameters {
*/
triggerEl?: Element | null;
/**
- * The element that contains the popover content.
+ * The element that contains the popover popup.
*/
popupEl?: HTMLElement | null;
/**
@@ -67,7 +67,7 @@ export interface UsePopoverRootReturnValue {
setOpen: (open: boolean, event?: Event, reason?: OpenChangeReason) => void;
mounted: boolean;
setMounted: React.Dispatch>;
- transitionStatus: TransitionStatus | undefined;
+ transitionStatus: TransitionStatus;
rootContext: FloatingRootContext;
getTriggerProps: UseInteractionsReturn['getReferenceProps'];
getRootPopupProps: UseInteractionsReturn['getFloatingProps'];
diff --git a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.test.tsx b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.test.tsx
index 0426f1405b..e6ab9bcd11 100644
--- a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.test.tsx
+++ b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.test.tsx
@@ -1,30 +1,17 @@
import * as React from 'react';
import * as Popover from '@base_ui/react/Popover';
-import { createRenderer, screen } from '@mui/internal-test-utils';
-import { expect } from 'chai';
+import { createRenderer } from '@mui/internal-test-utils';
+import { describeConformance } from '../../../test/describeConformance';
describe('', () => {
const { render } = createRenderer();
- it('should render the children with cloneElement', () => {
- render(
-
-
-
-
- ,
- );
-
- expect(screen.getByRole('button')).not.to.equal(null);
- });
-
- it('should render the children with a function render prop', () => {
- render(
-
- {(props) => }
- ,
- );
-
- expect(screen.getByRole('button')).not.to.equal(null);
- });
+ describeConformance(, () => ({
+ inheritComponent: 'button',
+ refInstanceof: window.HTMLButtonElement,
+ render(node) {
+ return render({node});
+ },
+ skip: ['reactTestRenderer'],
+ }));
});
diff --git a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.tsx b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.tsx
index 2f71de1642..36394260f0 100644
--- a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.tsx
+++ b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.tsx
@@ -1,13 +1,14 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import type { PopoverTriggerProps } from './PopoverTrigger.types';
-import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
+import type { PopoverTriggerOwnerState, PopoverTriggerProps } from './PopoverTrigger.types';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
-import { usePopoverTriggerStyleHooks } from './useStyleHooks';
+import { popoverTriggerStyleHookMapping } from './styleHooks';
+import { useComponentRenderer } from '../../utils/useComponentRenderer';
+import { useForkRef } from '../../utils/useForkRef';
/**
- * Provides props for its child element to trigger the popover, anchoring it to the child element.
+ * Renders a trigger element that will open the popover.
*
* Demos:
*
@@ -17,28 +18,30 @@ import { usePopoverTriggerStyleHooks } from './useStyleHooks';
*
* - [PopoverTrigger API](https://mui.com/base-ui/react-popover/components-api/#popover-trigger)
*/
-function PopoverTrigger(props: PopoverTriggerProps) {
- const { children } = props;
+const PopoverTrigger = React.forwardRef(function PopoverTrigger(
+ props: PopoverTriggerProps,
+ forwardedRef: React.ForwardedRef,
+) {
+ const { render, className, ...otherProps } = props;
const { open, setTriggerEl, getTriggerProps } = usePopoverRootContext();
- const mergedRef = useRenderPropForkRef(children, setTriggerEl);
+ const ownerState: PopoverTriggerOwnerState = React.useMemo(() => ({ open }), [open]);
- const ownerState = React.useMemo(() => ({ open }), [open]);
+ const mergedRef = useForkRef(forwardedRef, setTriggerEl);
- const styleHooks = usePopoverTriggerStyleHooks(ownerState);
-
- const mergedTriggerProps = getTriggerProps({
+ const { renderElement } = useComponentRenderer({
+ propGetter: getTriggerProps,
ref: mergedRef,
- ...styleHooks,
+ render: render ?? 'div',
+ className,
+ ownerState,
+ customStyleHookMapping: popoverTriggerStyleHookMapping,
+ extraProps: otherProps,
});
- if (typeof children === 'function') {
- return children(mergedTriggerProps);
- }
-
- return React.cloneElement(children, mergedTriggerProps);
-}
+ return renderElement();
+});
PopoverTrigger.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
@@ -46,9 +49,13 @@ PopoverTrigger.propTypes /* remove-proptypes */ = {
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
- * @ignore
+ * Class names applied to the element or a function that returns them based on the component's state.
+ */
+ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
+ /**
+ * A function to customize rendering of the component.
*/
- children: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired,
+ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
} as any;
export { PopoverTrigger };
diff --git a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.types.ts b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.types.ts
index 537181cdfb..3c332e5a81 100644
--- a/packages/mui-base/src/Popover/Trigger/PopoverTrigger.types.ts
+++ b/packages/mui-base/src/Popover/Trigger/PopoverTrigger.types.ts
@@ -1,9 +1,7 @@
-import type { GenericHTMLProps } from '../../utils/BaseUI.types';
+import type { BaseUIComponentProps } from '../../utils/BaseUI.types';
export type PopoverTriggerOwnerState = {
open: boolean;
};
-export interface PopoverTriggerProps {
- children: React.ReactElement | ((htmlProps: GenericHTMLProps) => React.ReactElement);
-}
+export interface PopoverTriggerProps extends BaseUIComponentProps {}
diff --git a/packages/mui-base/src/Popover/Trigger/styleHooks.ts b/packages/mui-base/src/Popover/Trigger/styleHooks.ts
new file mode 100644
index 0000000000..a5c42fc2fd
--- /dev/null
+++ b/packages/mui-base/src/Popover/Trigger/styleHooks.ts
@@ -0,0 +1,10 @@
+import type { PopoverTriggerOwnerState } from './PopoverTrigger.types';
+import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps';
+
+export const popoverTriggerStyleHookMapping: CustomStyleHookMapping = {
+ open(value) {
+ return {
+ 'data-state': value ? 'open' : 'closed',
+ };
+ },
+};
diff --git a/packages/mui-base/src/Popover/Trigger/useStyleHooks.ts b/packages/mui-base/src/Popover/Trigger/useStyleHooks.ts
deleted file mode 100644
index acf699278b..0000000000
--- a/packages/mui-base/src/Popover/Trigger/useStyleHooks.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type { PopoverTriggerOwnerState } from './PopoverTrigger.types';
-import { getStyleHookProps } from '../../utils/getStyleHookProps';
-
-/**
- * @ignore - internal hook.
- */
-export function usePopoverTriggerStyleHooks(ownerState: PopoverTriggerOwnerState) {
- return React.useMemo(() => {
- return getStyleHookProps(ownerState, {
- open(value) {
- return {
- 'data-state': value ? 'open' : 'closed',
- };
- },
- });
- }, [ownerState]);
-}
diff --git a/packages/mui-base/src/Popover/index.barrel.ts b/packages/mui-base/src/Popover/index.barrel.ts
index 6b9c2b1f00..e25374406f 100644
--- a/packages/mui-base/src/Popover/index.barrel.ts
+++ b/packages/mui-base/src/Popover/index.barrel.ts
@@ -2,16 +2,16 @@ export { PopoverRoot } from './Root/PopoverRoot';
export { PopoverPopup } from './Popup/PopoverPopup';
export { PopoverTrigger } from './Trigger/PopoverTrigger';
export { PopoverArrow } from './Arrow/PopoverArrow';
-export { PopoverGroup } from './Group/PopoverGroup';
+export { PopoverProvider } from './Provider/PopoverProvider';
export { PopoverBackdrop } from './Backdrop/PopoverBackdrop';
-export { usePopoverPositioner } from './Popup/usePopoverPositioner';
+export { usePopoverPositioner } from './Positioner/usePopoverPositioner';
export { usePopoverRoot } from './Root/usePopoverRoot';
export type { PopoverRootProps } from './Root/PopoverRoot.types';
export type * from './Trigger/PopoverTrigger.types';
export type { PopoverPopupOwnerState, PopoverPopupProps } from './Popup/PopoverPopup.types';
export type * from './Arrow/PopoverArrow.types';
-export type * from './Group/PopoverGroup.types';
+export type * from './Provider/PopoverProvider.types';
export type * from './Backdrop/PopoverBackdrop.types';
export type * from './Positioner/usePopoverPositioner.types';
export type * from './Root/usePopoverRoot.types';
diff --git a/packages/mui-base/src/Popover/index.ts b/packages/mui-base/src/Popover/index.ts
index 53dd57558d..cab9d2306d 100644
--- a/packages/mui-base/src/Popover/index.ts
+++ b/packages/mui-base/src/Popover/index.ts
@@ -1,8 +1,9 @@
export { PopoverRoot as Root } from './Root/PopoverRoot';
export { PopoverTrigger as Trigger } from './Trigger/PopoverTrigger';
+export { PopoverPositioner as Positioner } from './Positioner/PopoverPositioner';
export { PopoverPopup as Popup } from './Popup/PopoverPopup';
export { PopoverArrow as Arrow } from './Arrow/PopoverArrow';
-export { PopoverGroup as Group } from './Group/PopoverGroup';
+export { PopoverProvider as Provider } from './Provider/PopoverProvider';
export { PopoverBackdrop as Backdrop } from './Backdrop/PopoverBackdrop';
export { usePopoverRoot as useRoot } from './Root/usePopoverRoot';
export { usePopoverPositioner as usePositioner } from './Positioner/usePopoverPositioner';
@@ -12,6 +13,10 @@ export type {
PopoverTriggerProps as TriggerProps,
PopoverTriggerOwnerState as TriggerOwnerState,
} from './Trigger/PopoverTrigger.types';
+export type {
+ PopoverPositionerProps as PositionerProps,
+ PopoverPositionerOwnerState as PositionerOwnerState,
+} from './Positioner/PopoverPositioner.types';
export type {
PopoverPopupProps as PopupProps,
PopoverPopupOwnerState as PopupOwnerState,
@@ -20,7 +25,7 @@ export type {
PopoverArrowProps as ArrowProps,
PopoverArrowOwnerState as ArrowOwnerState,
} from './Arrow/PopoverArrow.types';
-export type { PopoverGroupProps as GroupProps } from './Group/PopoverGroup.types';
+export type { PopoverProviderProps as ProviderProps } from './Provider/PopoverProvider.types';
export type { PopoverBackdropProps as BackdropProps } from './Backdrop/PopoverBackdrop.types';
export type {
UsePopoverRootParameters as UseRootParameters,
diff --git a/packages/mui-base/src/index.ts b/packages/mui-base/src/index.ts
index b4c783b6a2..7edf6ea582 100644
--- a/packages/mui-base/src/index.ts
+++ b/packages/mui-base/src/index.ts
@@ -1,4 +1,5 @@
export * from './Checkbox/index.barrel';
export * from './NumberField/index.barrel';
+export * from './Popover/index.barrel';
export * from './Switch/index.barrel';
export * from './Tabs/index.barrel';
diff --git a/packages/mui-base/src/utils/BaseUI.types.ts b/packages/mui-base/src/utils/BaseUI.types.ts
index bcc0b5d458..5181aa5ecd 100644
--- a/packages/mui-base/src/utils/BaseUI.types.ts
+++ b/packages/mui-base/src/utils/BaseUI.types.ts
@@ -1,8 +1,6 @@
-<<<<<<< HEAD
import * as React from 'react';
-=======
+
export type GenericHTMLProps = React.HTMLAttributes & { ref?: React.Ref | undefined };
->>>>>>> cfcef1cf ([Popover] Component and Hook)
export type BaseUIEvent> = E & {
preventBaseUIHandler: () => void;
diff --git a/packages/mui-base/src/utils/getStyleHookProps.ts b/packages/mui-base/src/utils/getStyleHookProps.ts
index fe386c323e..1dfeef7881 100644
--- a/packages/mui-base/src/utils/getStyleHookProps.ts
+++ b/packages/mui-base/src/utils/getStyleHookProps.ts
@@ -2,17 +2,9 @@ export type CustomStyleHookMapping = {
[Property in keyof State]?: (state: State[Property]) => Record | null;
};
-<<<<<<< HEAD
export function getStyleHookProps>(
state: State,
customMapping?: CustomStyleHookMapping,
-=======
-export function getStyleHookProps>(
- state: State,
- customMapping?: Partial<
- Record Record | null>
- >,
->>>>>>> ef816f13 (Refactor to use root context)
) {
let props: Record = {};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cedf5d9c65..9ff52386fd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -572,9 +572,15 @@ importers:
'@babel/runtime':
specifier: ^7.24.5
version: 7.24.5
+ '@floating-ui/react':
+ specifier: ^0.26.16
+ version: 0.26.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@floating-ui/react-dom':
specifier: ^2.1.0
version: 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@floating-ui/utils':
+ specifier: ^0.2.2
+ version: 0.2.2
'@mui/types':
specifier: ^7.2.14
version: 7.2.14(@types/react@18.3.1)
@@ -1792,8 +1798,14 @@ packages:
react: '>=16.8.0'
react-dom: '>=16.8.0'
- '@floating-ui/utils@0.2.1':
- resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
+ '@floating-ui/react@0.26.16':
+ resolution: {integrity: sha512-HEf43zxZNAI/E781QIVpYSF3K2VH4TTYZpqecjdsFkjsaU1EbaWcM++kw0HXFffj7gDUcBFevX8s0rQGQpxkow==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.2':
+ resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==}
'@gitbeaker/core@38.12.1':
resolution: {integrity: sha512-8XMVcBIdVAAoxn7JtqmZ2Ee8f+AZLcCPmqEmPFOXY2jPS84y/DERISg/+sbhhb18iRy+ZsZhpWgQ/r3CkYNJOQ==}
@@ -8027,6 +8039,9 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+ tabbable@6.2.0:
+ resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+
table@6.8.2:
resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==}
engines: {node: '>=10.0.0'}
@@ -9892,12 +9907,12 @@ snapshots:
'@floating-ui/core@1.6.0':
dependencies:
- '@floating-ui/utils': 0.2.1
+ '@floating-ui/utils': 0.2.2
'@floating-ui/dom@1.6.3':
dependencies:
'@floating-ui/core': 1.6.0
- '@floating-ui/utils': 0.2.1
+ '@floating-ui/utils': 0.2.2
'@floating-ui/react-dom@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
@@ -9905,7 +9920,15 @@ snapshots:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- '@floating-ui/utils@0.2.1': {}
+ '@floating-ui/react@0.26.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@floating-ui/utils': 0.2.2
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ tabbable: 6.2.0
+
+ '@floating-ui/utils@0.2.2': {}
'@gitbeaker/core@38.12.1':
dependencies:
@@ -17512,6 +17535,8 @@ snapshots:
symbol-tree@3.2.4: {}
+ tabbable@6.2.0: {}
+
table@6.8.2:
dependencies:
ajv: 8.12.0
@@ -18305,4 +18330,3 @@ snapshots:
yocto-queue@1.0.0: {}
zwitch@1.0.5: {}
-
\ No newline at end of file