diff --git a/packages/@mantine/core/src/components/Modal/Modal.story.tsx b/packages/@mantine/core/src/components/Modal/Modal.story.tsx
index e1de48a762..a7c8706119 100644
--- a/packages/@mantine/core/src/components/Modal/Modal.story.tsx
+++ b/packages/@mantine/core/src/components/Modal/Modal.story.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
import { useDisclosure } from '@mantine/hooks';
import { Button } from '../Button';
import { Menu } from '../Menu';
@@ -31,7 +32,13 @@ export function Usage() {
{content}
-
+ console.log('onExited') }}
+ onExitTransitionEnd={() => console.log('onExitTransitionEnd')}
+ >
diff --git a/packages/@mantine/core/src/components/ModalBase/ModalBase.context.ts b/packages/@mantine/core/src/components/ModalBase/ModalBase.context.ts
index fe0547f989..e63da6c257 100644
--- a/packages/@mantine/core/src/components/ModalBase/ModalBase.context.ts
+++ b/packages/@mantine/core/src/components/ModalBase/ModalBase.context.ts
@@ -10,6 +10,8 @@ interface ModalBaseContextValue {
getTitleId: () => string;
getBodyId: () => string;
transitionProps: Partial | undefined;
+ onExitTransitionEnd: (() => void) | undefined;
+ onEnterTransitionEnd: (() => void) | undefined;
zIndex: string | number | undefined;
opened: boolean;
diff --git a/packages/@mantine/core/src/components/ModalBase/ModalBase.tsx b/packages/@mantine/core/src/components/ModalBase/ModalBase.tsx
index be6d993008..4f3bda434f 100644
--- a/packages/@mantine/core/src/components/ModalBase/ModalBase.tsx
+++ b/packages/@mantine/core/src/components/ModalBase/ModalBase.tsx
@@ -54,6 +54,12 @@ export interface ModalBaseProps extends BoxProps, ElementProps<'div', 'title'> {
/** Props added to the `Transition` component that used to animate overlay and body, use to configure duration and animation type, `{ duration: 200, transition: 'pop' }` by default */
transitionProps?: TransitionOverride;
+ /** Called when exit transition ends */
+ onExitTransitionEnd?: () => void;
+
+ /** Called when enter transition ends */
+ onEnterTransitionEnd?: () => void;
+
/** Determines whether `onClose` should be called when user presses the escape key, `true` by default */
closeOnEscape?: boolean;
@@ -84,6 +90,8 @@ export const ModalBase = forwardRef(
onClose,
id,
transitionProps,
+ onExitTransitionEnd,
+ onEnterTransitionEnd,
trapFocus,
closeOnEscape,
returnFocus,
@@ -114,6 +122,8 @@ export const ModalBase = forwardRef(
opened,
onClose,
closeOnClickOutside,
+ onExitTransitionEnd,
+ onEnterTransitionEnd,
transitionProps: { ...transitionProps, keepMounted },
getTitleId: () => `${_id}-title`,
getBodyId: () => `${_id}-body`,
diff --git a/packages/@mantine/core/src/components/ModalBase/ModalBaseContent.tsx b/packages/@mantine/core/src/components/ModalBase/ModalBaseContent.tsx
index 3ead27f149..306e9a84a3 100644
--- a/packages/@mantine/core/src/components/ModalBase/ModalBaseContent.tsx
+++ b/packages/@mantine/core/src/components/ModalBase/ModalBaseContent.tsx
@@ -31,6 +31,14 @@ export const ModalBaseContent = forwardRef {
+ ctx.onExitTransitionEnd?.();
+ ctx.transitionProps?.onExited?.();
+ }}
+ onEntered={() => {
+ ctx.onEnterTransitionEnd?.();
+ ctx.transitionProps?.onEntered?.();
+ }}
{...transitionProps}
>
{(transitionStyles) => (
diff --git a/packages/@mantine/core/src/components/Popover/Popover.story.tsx b/packages/@mantine/core/src/components/Popover/Popover.story.tsx
index c71d3627fa..7801af3cd5 100644
--- a/packages/@mantine/core/src/components/Popover/Popover.story.tsx
+++ b/packages/@mantine/core/src/components/Popover/Popover.story.tsx
@@ -28,7 +28,12 @@ export function Uncontrolled() {
}}
>
-
console.log('closed')} onOpen={() => console.log('opened')}>
+ console.log('closed')}
+ onOpen={() => console.log('opened')}
+ onExitTransitionEnd={() => console.log('exited')}
+ onEnterTransitionEnd={() => console.log('entered')}
+ >
diff --git a/packages/@mantine/core/src/components/Popover/Popover.tsx b/packages/@mantine/core/src/components/Popover/Popover.tsx
index 855c3c83df..e4df18fafc 100644
--- a/packages/@mantine/core/src/components/Popover/Popover.tsx
+++ b/packages/@mantine/core/src/components/Popover/Popover.tsx
@@ -61,6 +61,12 @@ export interface __PopoverProps {
/** Props passed down to the `Transition` component that used to animate dropdown presence, use to configure duration and animation type, `{ duration: 150, transition: 'fade' }` by default */
transitionProps?: TransitionOverride;
+ /** Called when exit transition ends */
+ onExitTransitionEnd?: () => void;
+
+ /** Called when enter transition ends */
+ onEnterTransitionEnd?: () => void;
+
/** Dropdown width, or `'target'` to make dropdown width the same as target element, `'max-content'` by default */
width?: PopoverWidth;
@@ -186,6 +192,8 @@ export function Popover(_props: PopoverProps) {
positionDependencies,
opened,
transitionProps,
+ onExitTransitionEnd,
+ onEnterTransitionEnd,
width,
middlewares,
withArrow,
@@ -279,6 +287,16 @@ export function Popover(_props: PopoverProps) {
[popover.floating.refs.setFloating]
);
+ const onExited = useCallback(() => {
+ transitionProps?.onExited?.();
+ onExitTransitionEnd?.();
+ }, [transitionProps?.onExited, onExitTransitionEnd]);
+
+ const onEntered = useCallback(() => {
+ transitionProps?.onEntered?.();
+ onEnterTransitionEnd?.();
+ }, [transitionProps?.onEntered, onEnterTransitionEnd]);
+
return (