diff --git a/README.md b/README.md index 0a5c1fb8..d9483ce6 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ Additional props: `snapToSequentialPoint`: Disabled velocity based swiping for snap points. This means that a snap point won't be skipped even if the velocity is high enough. Useful if each snap point in a drawer is equally important. +`onAnimationEnd`: Gets triggered after the open or close animation ends, it receives an `open` argument with the `open` state of the drawer by the time the function was triggered. Useful to revert any state changes for example. + `[data-vaul-no-drag]`: When interacting with an element with this data attribute, the drawer won't be dragged. ### Controlled Drawer diff --git a/src/index.tsx b/src/index.tsx index 565b0518..8dde005e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -52,9 +52,11 @@ export type DialogProps = { onClose?: () => void; direction?: 'top' | 'bottom' | 'left' | 'right'; defaultOpen?: boolean; + disablePreventScroll?: boolean; repositionInputs?: boolean; snapToSequentialPoint?: boolean; container?: HTMLElement | null; + onAnimationEnd?: (open: boolean) => void; } & (WithFadeFromProps | WithoutFadeFromProps); export function Root({ @@ -78,14 +80,22 @@ export function Root({ noBodyStyles, direction = 'bottom', defaultOpen = false, + disablePreventScroll = true, snapToSequentialPoint = false, repositionInputs = true, + onAnimationEnd, container, }: DialogProps) { const [isOpen = false, setIsOpen] = useControllableState({ defaultProp: defaultOpen, prop: openProp, - onChange: onOpenChange, + onChange: (o: boolean) => { + onOpenChange?.(o); + + setTimeout(() => { + onAnimationEnd?.(o); + }, TRANSITIONS.DURATION * 1000); + }, }); const [hasBeenOpened, setHasBeenOpened] = React.useState(false); const [isDragging, setIsDragging] = React.useState(false); @@ -132,7 +142,8 @@ export function Root({ }); usePreventScroll({ - isDisabled: !isOpen || isDragging || !modal || justReleased || !hasBeenOpened || !repositionInputs, + isDisabled: + !isOpen || isDragging || !modal || justReleased || !hasBeenOpened || !repositionInputs || !disablePreventScroll, }); function getScale() { @@ -392,11 +403,13 @@ export function Root({ return () => window.visualViewport?.removeEventListener('resize', onVisualViewportChange); }, [activeSnapPointIndex, snapPoints, snapPointsOffset]); - function closeDrawer() { + function closeDrawer(fromWithin?: boolean) { cancelDrag(); onClose?.(); - setIsOpen(false); + if (!fromWithin) { + setIsOpen(false); + } setTimeout(() => { if (snapPoints) { @@ -607,8 +620,9 @@ export function Root({ if (open) { setHasBeenOpened(true); } else { - closeDrawer(); + closeDrawer(true); } + setIsOpen(open); }} open={isOpen} @@ -669,12 +683,10 @@ export const Overlay = React.forwardRef & { - onAnimationEnd?: (open: boolean) => void; -}; +export type ContentProps = React.ComponentPropsWithoutRef; export const Content = React.forwardRef(function ( - { onPointerDownOutside, onAnimationEnd, style, ...rest }, + { onPointerDownOutside, style, ...rest }, ref, ) { const { @@ -824,7 +836,6 @@ export function NestedRoot({ onDrag, onOpenChange, ...rest }: DialogProps) { if (o) { onNestedOpenChange(o); } - onOpenChange?.(o); }} onRelease={onNestedRelease} {...rest}