diff options
| author | Oliver <oliver@mohlin.dev> | 2025-03-28 13:12:55 +0100 |
|---|---|---|
| committer | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-05-28 10:33:24 +0200 |
| commit | 69f2b7bb1b0af94155ca2ebe58e2fb9616a9484a (patch) | |
| tree | 6fd36337884a87afa44814e7bd87b383009349cb | |
| parent | 4ab68b8218f25994684bed94d701dbcd898ccdc4 (diff) | |
| download | mullvadvpn-69f2b7bb1b0af94155ca2ebe58e2fb9616a9484a.tar.xz mullvadvpn-69f2b7bb1b0af94155ca2ebe58e2fb9616a9484a.zip | |
Update animation component to allow passing multiple animations
12 files changed, 208 insertions, 46 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx index d0edee746e..ffcc462385 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx @@ -1,14 +1,83 @@ -import { AnimatePresentVertical, AnimatePresentVerticalProps } from './components'; +import React from 'react'; +import styled, { css } from 'styled-components'; -export type AnimateProps = { - type: 'present-vertical'; -} & AnimatePresentVerticalProps; +import { TransientProps } from '../../types'; +import { useAnimations } from './hooks'; -export function Animate({ type, ...props }: AnimateProps) { - switch (type) { - case 'present-vertical': - return <AnimatePresentVertical {...props} />; - default: - return type satisfies never; - } +export type Animation = FadeAnimation | WipeAnimation; + +export type FadeAnimation = { + type: 'fade'; +}; + +export type WipeAnimation = { + type: 'wipe'; + direction: 'vertical'; +}; + +export type AnimateProps = React.HTMLAttributes<HTMLDivElement> & { + present?: boolean; + duration?: React.CSSProperties['animationDuration']; + timingFunction?: React.CSSProperties['animationTimingFunction']; + direction?: React.CSSProperties['animationDirection']; + iterationCount?: React.CSSProperties['animationIterationCount']; + + animations: Animation[]; + children?: React.ReactNode; +}; + +const StyledDiv = styled.div<TransientProps<AnimateProps>>` + ${({ + $animations, + $duration = '0.25s', + $timingFunction = 'ease', + $direction = 'normal', + $iterationCount = '1', + }) => { + const animations = useAnimations($animations); + return css` + &&[data-present-animation='true'] { + display: none; + + &&[data-show='true'] { + display: block; + } + } + @media (prefers-reduced-motion: no-preference) { + --duration: ${$duration}; + --timing-function: ${$timingFunction}; + --direction: ${$direction}; + --iteration-count: ${$iterationCount}; + + interpolate-size: allow-keywords; + transition-behavior: allow-discrete; + + overflow: clip; + ${animations} + } + `; + }} +`; + +export function Animate({ + present, + animations, + duration, + timingFunction, + direction, + iterationCount, + ...props +}: AnimateProps) { + return ( + <StyledDiv + data-show={present} + data-present-animation={present === undefined ? false : true} + $animations={animations} + $duration={duration} + $timingFunction={timingFunction} + $direction={direction} + $iterationCount={iterationCount} + {...props} + /> + ); } diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/animations.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/animations.ts new file mode 100644 index 0000000000..bec40a1adf --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/animations.ts @@ -0,0 +1,13 @@ +import { fadeIn, fadeOut } from './fade'; +import { wipeDownIn, wipeVerticalOut } from './wipe'; + +export const animations = { + fade: { + in: fadeIn, + out: fadeOut, + }, + wipeDown: { + in: wipeDownIn, + out: wipeVerticalOut, + }, +} as const; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/fade.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/fade.ts new file mode 100644 index 0000000000..f67b6e1dd5 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/fade.ts @@ -0,0 +1,31 @@ +import { css } from 'styled-components'; + +import { createAnimation } from '../utils'; + +export const fadeIn = createAnimation( + 'animation-fade-in', + css` + from { + display: none; + opacity: 0; + } + to { + display: block; + opacity: 1; + } + `, +); + +export const fadeOut = createAnimation( + 'animation-fade-out', + css` + from { + display: block; + opacity: 1; + } + to { + display: none; + opacity: 0; + } + `, +); diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/index.ts new file mode 100644 index 0000000000..9616d4a908 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/index.ts @@ -0,0 +1 @@ +export * from './animations'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/wipe.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/wipe.ts new file mode 100644 index 0000000000..3bd6ecc6d9 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/wipe.ts @@ -0,0 +1,31 @@ +import { css } from 'styled-components'; + +import { createAnimation } from '../utils'; + +export const wipeDownIn = createAnimation( + 'animation-wipe-down-in', + css` + from { + display: none; + height: 0; + } + to { + display: block; + height: min-content; + } + `, +); + +export const wipeVerticalOut = createAnimation( + 'animation-wipe-vertical-out', + css` + from { + display: block; + height: min-content; + } + to { + display: none; + height: 0; + } + `, +); diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/AnimatePresentVertical.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/AnimatePresentVertical.tsx deleted file mode 100644 index 31536c93f9..0000000000 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/AnimatePresentVertical.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -export interface AnimatePresentVerticalProps extends React.HTMLAttributes<HTMLDivElement> { - present?: boolean; - children?: React.ReactNode; -} - -const StyledDiv = styled.div` - --display-start: none; - --height-start: 0; - --display-end: block; - --height-end: min-content; - - overflow: clip; - transition-property: display, height; - transition-duration: 0.25s; - transition-timing-function: ease; - interpolate-size: allow-keywords; - transition-behavior: allow-discrete; - display: var(--display-start); - height: var(--height-start); - &&[data-present='true'] { - display: var(--display-end); - height: var(--height-end); - @starting-style { - height: var(--height-start); - } - } -`; - -export function AnimatePresentVertical({ present, ...props }: AnimatePresentVerticalProps) { - return <StyledDiv data-present={present} {...props} />; -} diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/index.ts deleted file mode 100644 index 6bea1c015a..0000000000 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './AnimatePresentVertical'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/index.ts new file mode 100644 index 0000000000..8811e6a063 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/index.ts @@ -0,0 +1 @@ +export * from './useAnimations'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.tsx new file mode 100644 index 0000000000..1b44da8a30 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.tsx @@ -0,0 +1,29 @@ +import { css, RuleSet } from 'styled-components'; + +import { Animation } from '../Animate'; +import { animations } from '../animations'; +import { createAnimationDeclaration } from '../utils'; + +export const useAnimations = (values: Animation[]) => { + const inAnimations: Array<{ name: string; rule: RuleSet }> = []; + const outAnimations: Array<{ name: string; rule: RuleSet }> = []; + + values.forEach((animation) => { + if (animation.type === 'fade') { + inAnimations.push(animations.fade.in); + outAnimations.push(animations.fade.out); + } else if (animation.type === 'wipe' && animation.direction === 'vertical') { + inAnimations.push(animations.wipeDown.in); + outAnimations.push(animations.wipeDown.out); + } + }); + + return css` + ${inAnimations.map((animation) => animation.rule)} + ${outAnimations.map((animation) => animation.rule)} + ${createAnimationDeclaration(outAnimations)} + &&[data-show='true'] { + ${createAnimationDeclaration(inAnimations)} + } + `; +}; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation-declaration.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation-declaration.ts new file mode 100644 index 0000000000..e1268e6b76 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation-declaration.ts @@ -0,0 +1,10 @@ +import { css } from 'styled-components'; + +export const createAnimationDeclaration = (animations: Array<{ name: string }>) => css` + animation: ${animations + .map( + ({ name }) => + `${name} var(--duration) var(--timing-function) var(--direction) var(--iteration-count)`, + ) + .join(', ')}; +`; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation.ts new file mode 100644 index 0000000000..6f3905f5cd --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation.ts @@ -0,0 +1,10 @@ +import { css, RuleSet } from 'styled-components'; + +export const createAnimation = (name: string, frames: RuleSet) => ({ + name, + rule: css` + @keyframes ${name} { + ${frames} + } + `, +}); diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/index.ts new file mode 100644 index 0000000000..d3cf17a616 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/index.ts @@ -0,0 +1,2 @@ +export * from './create-animation'; +export * from './create-animation-declaration'; |
