11import styled from 'styled-components'
2- import React , { ReactElement , useEffect , useRef } from 'react'
2+ import React , { ReactElement , useEffect , useLayoutEffect , useRef } from 'react'
33import { get , COMMON , POSITION , SystemPositionProps , SystemCommonProps } from './constants'
44import { ComponentProps } from './utils/types'
55import { useOverlay , TouchOrMouseEvent } from './hooks'
66import Portal from './Portal'
77import sx , { SxProp } from './sx'
88import { useCombinedRefs } from './hooks/useCombinedRefs'
99import { AnchorSide } from './behaviors/anchoredPosition'
10+ import { useTheme } from './ThemeProvider'
1011
1112type StyledOverlayProps = {
1213 width ?: keyof typeof widthMap
@@ -33,8 +34,9 @@ const widthMap = {
3334 xxlarge : '960px' ,
3435 auto : 'auto'
3536}
37+ const animationDuration = 200
3638
37- function getSlideAnimationFactors ( anchorSide ?: AnchorSide ) : { x : number ; y : number } {
39+ function getSlideAnimationStartingVector ( anchorSide ?: AnchorSide ) : { x : number ; y : number } {
3840 if ( anchorSide ?. endsWith ( 'bottom' ) ) {
3941 return { x : 0 , y : - 1 }
4042 } else if ( anchorSide ?. endsWith ( 'top' ) ) {
@@ -59,8 +61,7 @@ const StyledOverlay = styled.div<StyledOverlayProps & SystemCommonProps & System
5961 width: ${ props => widthMap [ props . width || 'auto' ] } ;
6062 border-radius: 12px;
6163 overflow: hidden;
62- animation: overlay-appear 200ms ${ get ( 'animation.easeOutCubic' ) } ,
63- overlay-slide var(--slide-animation-timing, 200ms) ${ get ( 'animation.easeOutCubic' ) } ;
64+ animation: overlay-appear ${ animationDuration } ms ${ get ( 'animation.easeOutCubic' ) } ;
6465
6566 @keyframes overlay-appear {
6667 0% {
@@ -71,18 +72,6 @@ const StyledOverlay = styled.div<StyledOverlayProps & SystemCommonProps & System
7172 }
7273 }
7374
74- // split from overlay-appear so that the timing can be set to 0 initially or if we don't know which side to slide from
75- @keyframes overlay-slide {
76- 0% {
77- transform: translate(
78- calc(var(--slide-animation-x-factor) * ${ get ( 'space.2' ) } ),
79- calc(var(--slide-animation-y-factor) * ${ get ( 'space.2' ) } )
80- );
81- }
82- 100% {
83- }
84- }
85-
8675 :focus {
8776 outline: none;
8877 }
@@ -130,7 +119,9 @@ const Overlay = React.forwardRef<HTMLDivElement, OverlayProps>(
130119 ) : ReactElement => {
131120 const overlayRef = useRef < HTMLDivElement > ( null )
132121 const combinedRef = useCombinedRefs ( overlayRef , forwardedRef )
133- const slideAnimationFactors = getSlideAnimationFactors ( anchorSide )
122+ const { theme} = useTheme ( )
123+ const slideAnimationDistance = parseInt ( get ( 'space.2' ) ( theme ) . replace ( 'px' , '' ) )
124+ const slideAnimationEasing = get ( 'animation.easeOutCubic' ) ( theme )
134125
135126 useOverlay ( {
136127 overlayRef,
@@ -147,24 +138,25 @@ const Overlay = React.forwardRef<HTMLDivElement, OverlayProps>(
147138 }
148139 } , [ height , combinedRef ] )
149140
141+ useLayoutEffect ( ( ) => {
142+ const { x, y} = getSlideAnimationStartingVector ( anchorSide )
143+ if ( ( ! x && ! y ) || ! overlayRef . current ?. animate ) {
144+ return
145+ }
146+
147+ // JS animation is required because Safari does not allow css animations to start paused and then run
148+ overlayRef . current . animate (
149+ { transform : [ `translate(${ slideAnimationDistance * x } px, ${ slideAnimationDistance * y } px)` , `translate(0, 0)` ] } ,
150+ {
151+ duration : animationDuration ,
152+ easing : slideAnimationEasing
153+ }
154+ )
155+ } , [ anchorSide , slideAnimationDistance , slideAnimationEasing ] )
156+
150157 return (
151158 < Portal >
152- < StyledOverlay
153- aria-modal = "true"
154- role = { role }
155- height = { height }
156- { ...rest }
157- ref = { combinedRef }
158- style = {
159- {
160- '--slide-animation-x-factor' : slideAnimationFactors . x ,
161- '--slide-animation-y-factor' : slideAnimationFactors . y ,
162- '--slide-animation-timing' :
163- slideAnimationFactors . x === 0 && slideAnimationFactors . y === 0 ? '0' : undefined // 0 if we don't want to animate, fallback in css if we do
164- // eslint-disable-next-line @typescript-eslint/no-explicit-any
165- } as any
166- }
167- />
159+ < StyledOverlay aria-modal = "true" role = { role } height = { height } { ...rest } ref = { combinedRef } />
168160 </ Portal >
169161 )
170162 }
0 commit comments