summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-11-04 13:34:24 +0100
committerTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-11-04 13:34:24 +0100
commitd0f4e5fa0d48fe09293fdc61200ba7bf99759741 (patch)
treeedf0c1286d726b86769eaafda1c5d347beba1896
parent426c3d6ad4f57dbb503b7b7fbddc43c334d9c7ca (diff)
parent09886c5fb0035b3613d53f5059f96ab0415ddd47 (diff)
downloadmullvadvpn-d0f4e5fa0d48fe09293fdc61200ba7bf99759741.tar.xz
mullvadvpn-d0f4e5fa0d48fe09293fdc61200ba7bf99759741.zip
Merge branch 'accordion-animation'
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/accordion/components/AccordionContent.tsx35
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx106
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/AnimateContext.tsx55
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/animations.ts13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/fade.ts31
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/wipe.ts31
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/index.ts4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimate.ts18
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.ts31
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useHandleAnimationEnd.ts23
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/usePreviousValue.ts11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/types.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation-declaration.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/index.ts2
17 files changed, 25 insertions, 367 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/accordion/components/AccordionContent.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/accordion/components/AccordionContent.tsx
index beeb3ff238..99005180a0 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/accordion/components/AccordionContent.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/accordion/components/AccordionContent.tsx
@@ -1,26 +1,41 @@
+import { AnimatePresence, motion } from 'motion/react';
+import React from 'react';
import styled from 'styled-components';
-import { Animate } from '../../animate';
import { useAccordionContext } from '../AccordionContext';
export type AccordionContentProps = {
children?: React.ReactNode;
};
-const StyledAccordionContent = styled.div`
+const StyledAccordionContent = styled(motion.div)`
width: 100%;
+ overflow: hidden;
`;
+const variants = {
+ collapsed: { height: 0, opacity: 0 },
+ expanded: { height: 'auto', opacity: 1 },
+};
+
export function AccordionContent({ children }: AccordionContentProps) {
const { contentId, triggerId, expanded } = useAccordionContext();
return (
- <Animate
- present={expanded}
- animations={[{ type: 'wipe', direction: 'vertical' }]}
- duration="0.35s">
- <StyledAccordionContent id={contentId} aria-labelledby={triggerId} role="region">
- {children}
- </StyledAccordionContent>
- </Animate>
+ <AnimatePresence initial={false}>
+ {expanded && (
+ <StyledAccordionContent
+ id={contentId}
+ aria-labelledby={triggerId}
+ layout
+ role="region"
+ variants={variants}
+ initial="collapsed"
+ animate="expanded"
+ exit="collapsed"
+ transition={{ duration: 0.25, ease: 'easeInOut' }}>
+ {children}
+ </StyledAccordionContent>
+ )}
+ </AnimatePresence>
);
}
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
deleted file mode 100644
index 1c806b9a8d..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/Animate.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import React from 'react';
-import styled, { css, RuleSet } from 'styled-components';
-
-import { TransientProps } from '../../types';
-import { AnimateProvider, useAnimateContext } from './AnimateContext';
-import { useAnimate, useAnimations, useHandleAnimationEnd } from './hooks';
-import { Animation } from './types';
-
-type AnimateBaseProps = {
- initial?: boolean;
- present?: boolean;
- duration?: React.CSSProperties['animationDuration'];
- timingFunction?: React.CSSProperties['animationTimingFunction'];
- direction?: React.CSSProperties['animationDirection'];
- iterationCount?: React.CSSProperties['animationIterationCount'];
-
- animations: Animation[];
- children?: React.ReactNode;
-};
-
-export type AnimateProps = AnimateBaseProps &
- Omit<React.HTMLAttributes<HTMLDivElement>, keyof AnimateBaseProps>;
-
-const StyledDiv = styled.div<
- TransientProps<Omit<AnimateBaseProps, 'animations' | 'present'>> & {
- $animations: RuleSet;
- }
->`
- ${({
- $animations,
- $duration = '0.25s',
- $timingFunction = 'ease',
- $direction = 'normal',
- $iterationCount = '1',
- }) => {
- return css`
- // If the user prefers reduced motion, visibility still needs
- // to be toggled, otherwise this is handled by animations
- display: none;
-
- &&[data-present='true'] {
- display: block;
- }
-
- @media (prefers-reduced-motion: no-preference) {
- &&[data-animate='true'] {
- --duration: ${$duration};
- --timing-function: ${$timingFunction};
- --direction: ${$direction};
- --iteration-count: ${$iterationCount};
-
- interpolate-size: allow-keywords;
- transition-behavior: allow-discrete;
-
- overflow: clip;
- ${$animations}
- }
- }
- `;
- }}
-`;
-
-/**
- * Animate that applies animation to a wrapper around it's children.
- *
- * @param initial - Whether animation should trigger on mount.
- * @param present - Whether element is present, i.e rendered or not.
- * @param animations - List of animations to apply.
- */
-export function Animate({ animations, initial, present = true, children, ...props }: AnimateProps) {
- return (
- <AnimateProvider animations={animations} initial={initial} present={present}>
- <AnimateImpl {...props}>{children}</AnimateImpl>
- </AnimateProvider>
- );
-}
-
-export type AnimateImplProps = Omit<AnimateProps, 'animations' | 'present'>;
-
-function AnimateImpl({
- duration,
- timingFunction,
- direction,
- iterationCount,
- onAnimationEnd,
- ...props
-}: AnimateImplProps) {
- const { animatePresent } = useAnimateContext();
- const animations = useAnimations();
- const animate = useAnimate();
- const handleAnimationEnd = useHandleAnimationEnd();
-
- return (
- <StyledDiv
- data-animate={animate}
- data-present={animatePresent}
- onAnimationEnd={handleAnimationEnd}
- $animations={animations}
- $duration={duration}
- $timingFunction={timingFunction}
- $direction={direction}
- $iterationCount={iterationCount}
- {...props}
- />
- );
-}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/AnimateContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/AnimateContext.tsx
deleted file mode 100644
index d168f07a2e..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/AnimateContext.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React, { useState } from 'react';
-
-import { Animation } from './types';
-
-interface AnimateContextProps {
- animations: Animation[];
- animate: boolean;
- animatePresent: boolean;
- present: boolean;
- initial?: boolean;
- setAnimate: React.Dispatch<React.SetStateAction<boolean>>;
- setAnimatePresent: React.Dispatch<React.SetStateAction<boolean>>;
-}
-
-const AnimateContext = React.createContext<AnimateContextProps | undefined>(undefined);
-
-export const useAnimateContext = (): AnimateContextProps => {
- const context = React.useContext(AnimateContext);
- if (!context) {
- throw new Error('useButtonContext must be used within a ButtonProvider');
- }
- return context;
-};
-
-interface AnimateProviderProps {
- animations: Animation[];
- present: boolean;
- initial?: boolean;
- children: React.ReactNode;
-}
-
-export const AnimateProvider = ({
- animations,
- present,
- initial,
- children,
-}: AnimateProviderProps) => {
- const [animate, setAnimate] = React.useState<boolean>((initial && present) || false);
- const [animatePresent, setAnimatePresent] = useState<boolean>(present);
-
- return (
- <AnimateContext.Provider
- value={{
- animate,
- animatePresent,
- animations,
- initial,
- present,
- setAnimate,
- setAnimatePresent,
- }}>
- {children}
- </AnimateContext.Provider>
- );
-};
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
deleted file mode 100644
index bec40a1adf..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/animations.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-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
deleted file mode 100644
index f67b6e1dd5..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/fade.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-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
deleted file mode 100644
index 9616d4a908..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644
index bc0fff3807..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/animations/wipe.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { css } from 'styled-components';
-
-import { createAnimation } from '../utils';
-
-export const wipeDownIn = createAnimation(
- 'animation-wipe-down-in',
- css`
- from {
- display: none;
- max-height: 0;
- }
- to {
- display: block;
- max-height: min-content;
- }
- `,
-);
-
-export const wipeVerticalOut = createAnimation(
- 'animation-wipe-vertical-out',
- css`
- from {
- display: block;
- max-height: min-content;
- }
- to {
- display: none;
- max-height: 0;
- }
- `,
-);
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
deleted file mode 100644
index 8d6128d192..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './useAnimate';
-export * from './useAnimations';
-export * from './useHandleAnimationEnd';
-export * from './usePreviousValue';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimate.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimate.ts
deleted file mode 100644
index f67405c72d..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimate.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useEffect } from 'react';
-
-import { useAnimateContext } from '../AnimateContext';
-import { usePreviousValue } from './usePreviousValue';
-
-export const useAnimate = () => {
- const { animate, present, setAnimate, setAnimatePresent } = useAnimateContext();
- const previousPresent = usePreviousValue(present);
-
- useEffect(() => {
- if (present !== previousPresent) {
- setAnimate(true);
- setAnimatePresent(present);
- }
- }, [present, previousPresent, setAnimate, setAnimatePresent]);
-
- return animate;
-};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.ts
deleted file mode 100644
index 232bcd99b5..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useAnimations.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { css, RuleSet } from 'styled-components';
-
-import { useAnimateContext } from '../AnimateContext';
-import { animations } from '../animations';
-import { createAnimationDeclaration } from '../utils';
-
-export const useAnimations = () => {
- const { animations: values } = useAnimateContext();
-
- 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-present='true'] {
- ${createAnimationDeclaration(inAnimations)}
- }
- `;
-};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useHandleAnimationEnd.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useHandleAnimationEnd.ts
deleted file mode 100644
index 53389437bc..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/useHandleAnimationEnd.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { useCallback, useRef } from 'react';
-
-import { useAnimateContext } from '../AnimateContext';
-
-export const useHandleAnimationEnd = () => {
- const { animations, present, setAnimate, setAnimatePresent } = useAnimateContext();
- const animationsCount = animations.length;
- const animationsFinishedCount = useRef(0);
-
- const handleAnimationEnd = useCallback(() => {
- const nextAnimationsFinishedCount = animationsFinishedCount.current + 1;
-
- if (nextAnimationsFinishedCount === animationsCount) {
- animationsFinishedCount.current = 0;
- setAnimate(false);
- setAnimatePresent(present);
- } else {
- animationsFinishedCount.current = nextAnimationsFinishedCount;
- }
- }, [animationsCount, animationsFinishedCount, present, setAnimate, setAnimatePresent]);
-
- return handleAnimationEnd;
-};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/usePreviousValue.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/usePreviousValue.ts
deleted file mode 100644
index 1b835a7823..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/hooks/usePreviousValue.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { useEffect, useState } from 'react';
-
-export const usePreviousValue = <T>(value: T) => {
- const [previousValue, setPreviousValue] = useState(value);
-
- useEffect(() => {
- setPreviousValue(value);
- }, [setPreviousValue, value]);
-
- return previousValue;
-};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/index.ts
deleted file mode 100644
index c84772d940..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './Animate';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/types.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/types.ts
deleted file mode 100644
index 6b51da2889..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/types.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export type Animation = FadeAnimation | WipeAnimation;
-
-export type FadeAnimation = {
- type: 'fade';
-};
-
-export type WipeAnimation = {
- type: 'wipe';
- direction: 'vertical';
-};
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
deleted file mode 100644
index e1268e6b76..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation-declaration.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644
index 6f3905f5cd..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/create-animation.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644
index d3cf17a616..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/animate/utils/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './create-animation';
-export * from './create-animation-declaration';