Skip to content

Commit db69e07

Browse files
committed
refactor: use js animation for slide to fix safari
1 parent 5835f86 commit db69e07

File tree

1 file changed

+25
-33
lines changed

1 file changed

+25
-33
lines changed

src/Overlay.tsx

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import styled from 'styled-components'
2-
import React, {ReactElement, useEffect, useRef} from 'react'
2+
import React, {ReactElement, useEffect, useLayoutEffect, useRef} from 'react'
33
import {get, COMMON, POSITION, SystemPositionProps, SystemCommonProps} from './constants'
44
import {ComponentProps} from './utils/types'
55
import {useOverlay, TouchOrMouseEvent} from './hooks'
66
import Portal from './Portal'
77
import sx, {SxProp} from './sx'
88
import {useCombinedRefs} from './hooks/useCombinedRefs'
99
import {AnchorSide} from './behaviors/anchoredPosition'
10+
import {useTheme} from './ThemeProvider'
1011

1112
type 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

Comments
 (0)