fix(app): add a quick modal motion preset and settle instant modals (#1943)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-24 12:50:16 +02:00
1 parent 2377a4c9d0
commit 9219b886fe
2 files changed
+123 -58

No files matched your search

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import styles from '@app/features/app/components/dialogs/Modal.module.css';
import {resolveModalBackdropMotionSpec, resolveModalMotionSpec} from '@app/features/app/components/dialogs/ModalMotion';
import {useModalBackHandler} from '@app/features/app/hooks/useModalBackHandler';
import {CLOSE_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import * as PopoutCommands from '@app/features/ui/commands/PopoutCommands';
@@ -103,51 +104,16 @@ const RootComponent = React.forwardRef<HTMLDivElement, ModalProps>(
},
[ref],
);
const mobileFullscreenAnimations = {
initial: {opacity: 0},
animate: {opacity: 1},
exit: {opacity: 0},
};
const profileSlideAnimations = prefersReducedMotion
? {
initial: {opacity: 0},
animate: {opacity: 1},
exit: {opacity: 0},
}
: {
initial: {opacity: 0, x: 14},
animate: {opacity: 1, x: 0},
exit: {opacity: 0, x: 14},
};
const defaultAnimations = prefersReducedMotion
? {
initial: {opacity: 0},
animate: {opacity: 1},
exit: {opacity: 0},
}
: {
initial: {opacity: 0, scale: 0.95},
animate: {opacity: 1, scale: 1},
exit: {opacity: 0, scale: 0.95},
};
const animations = isFullscreenOnMobile
? mobileFullscreenAnimations
: transitionPreset === 'profile-slide'
? profileSlideAnimations
: defaultAnimations;
const rootTransition: Transition =
prefersReducedMotion || transitionPreset === 'instant'
? {duration: 0}
: isFullscreenOnMobile
? {duration: 0.15}
: transitionPreset === 'profile-slide'
? {duration: 0.14, ease: 'easeOut'}
: {
type: 'spring',
stiffness: 400,
damping: 30,
mass: 0.8,
};
const motionSpec = resolveModalMotionSpec({
transitionPreset,
prefersReducedMotion,
isFullscreenOnMobile,
});
const backdropMotionSpec = resolveModalBackdropMotionSpec({
transitionPreset,
prefersReducedMotion,
isMobile,
});
const handleBackdropClickEvent = useCallback(
(event: React.MouseEvent) => {
if (event.target === event.currentTarget) {
@@ -226,23 +192,16 @@ const RootComponent = React.forwardRef<HTMLDivElement, ModalProps>(
};
}, [isVisible]);
const isCenteredOnMobile = isMobile && !useFullscreenLayer;
const shouldInstantBackdrop = isMobile && !prefersReducedMotion;
return (
<FloatingPortal root={portalHost ?? undefined} data-flx="app.modal.floating-portal">
{needsBackdrop && isVisible && (
<motion.div
className={styles.modalBackdrop}
style={{zIndex: backdropZIndex}}
initial={{opacity: shouldInstantBackdrop || isInstantTransition ? 0.85 : 0}}
animate={{opacity: 0.85}}
exit={{opacity: 0}}
transition={
prefersReducedMotion || isInstantTransition
? {duration: 0}
: shouldInstantBackdrop
? {duration: 0.15}
: {duration: 0.2}
}
initial={backdropMotionSpec.initial}
animate={backdropMotionSpec.animate}
exit={backdropMotionSpec.exit}
transition={backdropMotionSpec.transition as Transition}
data-flx="app.modal.modal-backdrop"
/>
)}
@@ -326,8 +285,10 @@ const RootComponent = React.forwardRef<HTMLDivElement, ModalProps>(
className,
)}
data-flx="app.modal.root"
{...animations}
transition={rootTransition}
initial={motionSpec.initial}
animate={motionSpec.animate}
exit={motionSpec.exit}
transition={motionSpec.transition as Transition}
onAnimationStart={handleAnimationStart}
onAnimationComplete={handleAnimationComplete}
ref={setMotionElementRef}