animation

This commit is contained in:
Thomas Demirdjian
2025-10-03 17:25:44 +02:00
parent 45b7571c14
commit 3d14e47770
+116 -46
View File
@@ -1,5 +1,7 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { import {
Animated,
Easing,
FlatList, FlatList,
Image, Image,
Pressable, Pressable,
@@ -43,6 +45,7 @@ const ProjectDropDown = ({
}) => { }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [triggerLayout, setTriggerLayout] = useState(null); const [triggerLayout, setTriggerLayout] = useState(null);
const dropdownAnimation = useRef(new Animated.Value(0)).current;
const normalizedProjects = Array.isArray(projects) ? projects : []; const normalizedProjects = Array.isArray(projects) ? projects : [];
const isDisabled = normalizedProjects.length === 0; const isDisabled = normalizedProjects.length === 0;
@@ -102,6 +105,52 @@ const ProjectDropDown = ({
[triggerLayout], [triggerLayout],
); );
useEffect(() => {
Animated.timing(dropdownAnimation, {
toValue: isOpen ? 1 : 0,
duration: 220,
easing: isOpen ? Easing.out(Easing.cubic) : Easing.in(Easing.cubic),
useNativeDriver: true,
}).start();
}, [dropdownAnimation, isOpen]);
const overlayTranslateY = useMemo(
() =>
dropdownAnimation.interpolate({
inputRange: [0, 1],
outputRange: [-12, 0],
}),
[dropdownAnimation],
);
const overlayScale = useMemo(
() =>
dropdownAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0.96, 1],
}),
[dropdownAnimation],
);
const triggerOpacity = useMemo(
() =>
dropdownAnimation.interpolate({
inputRange: [0, 1],
outputRange: [1, 0],
extrapolate: "clamp",
}),
[dropdownAnimation],
);
const triggerTranslateY = useMemo(
() =>
dropdownAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0, -6],
}),
[dropdownAnimation],
);
return ( return (
<View style={[styles.container, style]}> <View style={[styles.container, style]}>
{isOpen ? ( {isOpen ? (
@@ -112,44 +161,63 @@ const ProjectDropDown = ({
style={styles.triggerWrapper} style={styles.triggerWrapper}
onLayout={(event) => setTriggerLayout(event.nativeEvent.layout)} onLayout={(event) => setTriggerLayout(event.nativeEvent.layout)}
> >
<BlurView <Animated.View
intensity={BUTTON_BLUR_INTENSITY} pointerEvents={isOpen ? "none" : "auto"}
tint="dark"
style={[ style={[
styles.dropdownBlur, styles.dropdownTriggerContainer,
isDisabled && styles.dropdownBlurDisabled, {
isOpen && styles.dropdownBlurHidden, opacity: triggerOpacity,
transform: [{ translateY: triggerTranslateY }],
},
]} ]}
> >
<ProjectRow <BlurView
isTitle={true} intensity={BUTTON_BLUR_INTENSITY}
isOpen={isOpen} tint="dark"
project={currentProject} style={[
formatDate={formatDate} styles.dropdownBlur,
onSelect={toggleDropdown} isDisabled && styles.dropdownBlurDisabled,
/> ]}
</BlurView> >
<ProjectRow
isTitle={true}
isOpen={isOpen}
project={currentProject}
formatDate={formatDate}
onSelect={toggleDropdown}
/>
</BlurView>
</Animated.View>
<BlurView <Animated.View
intensity={BUTTON_BLUR_INTENSITY}
tint="dark"
pointerEvents={isOpen ? "auto" : "none"} pointerEvents={isOpen ? "auto" : "none"}
style={[ style={[
styles.dropdownOverlay, styles.dropdownOverlayContainer,
{ width: dropdownWidth || "100%" }, { width: dropdownWidth || "100%" },
!isOpen && styles.dropdownOverlayHidden, {
opacity: dropdownAnimation,
transform: [
{ translateY: overlayTranslateY },
{ scale: overlayScale },
],
},
]} ]}
> >
<ProjectRow <BlurView
isTitle={true} intensity={BUTTON_BLUR_INTENSITY}
isOpen={isOpen} tint="dark"
project={currentProject} style={styles.dropdownOverlay}
formatDate={formatDate} >
onSelect={toggleDropdown} <ProjectRow
/> isTitle={true}
isOpen={isOpen}
project={currentProject}
formatDate={formatDate}
onSelect={toggleDropdown}
/>
<View style={styles.dropdownOverlayContent}> <View style={styles.dropdownOverlayContent}>
<FlatList <FlatList
data={dropdownProjects} data={dropdownProjects}
keyExtractor={(project, index) => keyExtractor={(project, index) =>
project?.id || project?.title || `project-${index}` project?.id || project?.title || `project-${index}`
@@ -168,13 +236,14 @@ const ProjectDropDown = ({
contentContainerStyle={styles.dropdownListContent} contentContainerStyle={styles.dropdownListContent}
style={styles.dropdownList} style={styles.dropdownList}
/> />
<GradientButton <GradientButton
containerStyle={styles.createButtonContainer} containerStyle={styles.createButtonContainer}
title="Commencer à créer" title="Commencer à créer"
onPress={handleCreate} onPress={handleCreate}
/> />
</View> </View>
</BlurView> </BlurView>
</Animated.View>
</View> </View>
</View> </View>
); );
@@ -295,29 +364,30 @@ const styles = StyleSheet.create({
dropdownBlurDisabled: { dropdownBlurDisabled: {
opacity: 0.6, opacity: 0.6,
}, },
dropdownBlurHidden: {
opacity: 0,
pointerEvents: "none",
},
triggerWrapper: { triggerWrapper: {
position: "relative", position: "relative",
zIndex: 3, zIndex: 3,
}, },
dropdownOverlay: { dropdownTriggerContainer: {
width: "100%",
},
dropdownOverlayContainer: {
position: "absolute", position: "absolute",
top: 0, top: 0,
left: 0, left: 0,
borderRadius: 16, borderRadius: 16,
overflow: "hidden",
zIndex: 4,
elevation: 4,
maxHeight: 400,
},
dropdownOverlay: {
flex: 1,
width: "100%",
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 8, paddingVertical: 8,
gap: 9, gap: 9,
maxHeight: 400, maxHeight: 400,
zIndex: 4,
elevation: 4,
overflow: "hidden",
},
dropdownOverlayHidden: {
opacity: 0,
}, },
dropdownOverlayContent: { dropdownOverlayContent: {
maxHeight: 260, maxHeight: 260,