-
-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): #106 animated utility component
- Loading branch information
1 parent
9d480a3
commit fbe1f17
Showing
2 changed files
with
183 additions
and
0 deletions.
There are no files selected for viewing
182 changes: 182 additions & 0 deletions
182
packages/core/src/utils/Animated/Animated.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import { HTMLAttributes, CSSProperties, FC, useRef, useEffect, useMemo } from 'react'; | ||
import { jsx } from '@emotion/react'; | ||
import PropTypes from 'prop-types'; | ||
import anime from 'animejs'; | ||
import { | ||
ENTERING, | ||
EXITING, | ||
useAnimator | ||
} from '@arwes/animation'; | ||
|
||
interface AnimatedSettingsTransitionFunctionParams { | ||
targets: anime.AnimeAnimParams['targets'] | ||
duration: number | ||
delay?: number | ||
} | ||
|
||
type AnimatedSettingsTransitionFunction = (params: AnimatedSettingsTransitionFunctionParams) => void; | ||
|
||
// TODO: Use a stronger typing for anime parameters instead of "anime.AnimeParams". | ||
type AnimatedSettingsTransitionTypes = AnimatedSettingsTransitionFunction | anime.AnimeParams; | ||
|
||
type AnimatedSettingsTransition = AnimatedSettingsTransitionTypes | AnimatedSettingsTransitionTypes[]; | ||
|
||
interface AnimatedSettings { | ||
initialStyle?: CSSProperties | ||
entering?: AnimatedSettingsTransition | ||
exiting?: AnimatedSettingsTransition | ||
} | ||
|
||
// TODO: Set proper HTML element typings according to "as" prop value. | ||
// For now, all animated elements are asumed to be DIV elements. | ||
interface AnimatedProps extends HTMLAttributes<HTMLDivElement> { | ||
as?: keyof HTMLElementTagNameMap | ||
animated?: AnimatedSettings | ||
className?: string | ||
style?: CSSProperties | ||
} | ||
|
||
const Animated: FC<AnimatedProps> = props => { | ||
const { | ||
as: asProvided, | ||
animated, | ||
className, | ||
style, | ||
children, | ||
...otherProps | ||
} = props; | ||
|
||
const as = useMemo(() => asProvided || 'div', []); | ||
|
||
// TODO: Add external root ref. | ||
const rootRef = useRef<HTMLDivElement>(); | ||
|
||
const animator = useAnimator(); | ||
|
||
const dynamicStyle = animator?.animate | ||
? animated?.initialStyle | ||
: null; | ||
|
||
useEffect(() => { | ||
return () => { | ||
anime.remove(rootRef.current as HTMLDivElement); | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (!animator || !animator.animate || !animated) { | ||
return; | ||
} | ||
|
||
switch (animator.flow.value) { | ||
case ENTERING: { | ||
if (animated.entering) { | ||
const animationParams = { | ||
targets: rootRef.current, | ||
duration: animator.duration.enter | ||
}; | ||
|
||
const animations: AnimatedSettingsTransitionTypes[] = | ||
Array.isArray(animated.entering) | ||
? animated.entering | ||
: [animated.entering]; | ||
|
||
animations.forEach(animation => { | ||
if (typeof animation === 'function') { | ||
animation(animationParams); | ||
} | ||
else { | ||
anime({ | ||
easing: 'easeOutSine', | ||
...animation, | ||
targets: rootRef.current, | ||
duration: animator.duration.enter | ||
}); | ||
} | ||
}); | ||
} | ||
break; | ||
} | ||
|
||
case EXITING: { | ||
if (animated.exiting) { | ||
const animationParams = { | ||
targets: rootRef.current, | ||
duration: animator.duration.exit | ||
}; | ||
|
||
const animations: AnimatedSettingsTransitionTypes[] = | ||
Array.isArray(animated.exiting) | ||
? animated.exiting | ||
: [animated.exiting]; | ||
|
||
animations.forEach(animation => { | ||
if (typeof animation === 'function') { | ||
animation(animationParams); | ||
} | ||
else { | ||
anime({ | ||
easing: 'easeOutSine', | ||
...animation, | ||
targets: rootRef.current, | ||
duration: animator.duration.exit | ||
}); | ||
} | ||
}); | ||
} | ||
break; | ||
} | ||
} | ||
}, [animator?.flow]); | ||
|
||
return jsx(as, { | ||
...otherProps, | ||
className, | ||
style: { | ||
...style, | ||
...dynamicStyle | ||
}, | ||
ref: rootRef | ||
}, children); | ||
}; | ||
|
||
Animated.propTypes = { | ||
// @ts-expect-error | ||
as: PropTypes.string.isRequired, | ||
// @ts-expect-error | ||
animated: PropTypes.shape({ | ||
initialStyle: PropTypes.object, | ||
entering: PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.object, | ||
PropTypes.arrayOf( | ||
PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.object | ||
]) | ||
) | ||
]), | ||
exiting: PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.object, | ||
PropTypes.arrayOf( | ||
PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.object | ||
]) | ||
) | ||
]) | ||
}) | ||
}; | ||
|
||
Animated.defaultProps = { | ||
as: 'div' | ||
}; | ||
|
||
export { | ||
AnimatedSettingsTransitionFunctionParams, | ||
AnimatedSettingsTransitionFunction, | ||
AnimatedSettingsTransition, | ||
AnimatedProps, | ||
Animated | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Animated.component'; |