classement tout les mois

This commit is contained in:
2025-10-21 16:52:47 +02:00
parent 3aa5907b51
commit 60e64fa892
3 changed files with 220 additions and 60 deletions
+128 -60
View File
@@ -1,4 +1,5 @@
import { BlurView } from "expo-blur";
import { Portal } from "@gorhom/portal";
import React, {
useCallback,
useEffect,
@@ -52,8 +53,27 @@ const ProjectDropDown = ({
}) => {
const [isOpen, setIsOpen] = useState(false);
const [triggerLayout, setTriggerLayout] = useState(null);
const [triggerWindowLayout, setTriggerWindowLayout] = useState(null);
const triggerRef = useRef(null);
const dropdownAnimation = useRef(new Animated.Value(0)).current;
const measureTriggerPosition = useCallback(() => {
if (triggerRef.current?.measureInWindow) {
try {
triggerRef.current.measureInWindow((x, y, width, height) => {
setTriggerWindowLayout({
x: x ?? 0,
y: y ?? 0,
width: width ?? triggerLayout?.width ?? 0,
height: height ?? triggerLayout?.height ?? 0,
});
});
} catch (error) {
console.warn("ProjectDropDown: measureInWindow failed", error);
}
}
}, [triggerLayout?.height, triggerLayout?.width]);
const normalizedProjects = Array.isArray(projects) ? projects : [];
const isDisabled = normalizedProjects.length === 0;
@@ -80,8 +100,11 @@ const ProjectDropDown = ({
if (isDisabled) {
return;
}
if (!isOpen) {
measureTriggerPosition();
}
setIsOpen((prev) => !prev);
}, [isDisabled]);
}, [isDisabled, isOpen, measureTriggerPosition]);
const closeDropdown = useCallback(() => setIsOpen(false), []);
@@ -121,6 +144,12 @@ const ProjectDropDown = ({
}).start();
}, [dropdownAnimation, isOpen]);
useEffect(() => {
if (isOpen) {
measureTriggerPosition();
}
}, [isOpen, measureTriggerPosition]);
const overlayTranslateY = useMemo(
() =>
dropdownAnimation.interpolate({
@@ -158,15 +187,81 @@ const ProjectDropDown = ({
[dropdownAnimation]
);
const overlayPositionStyle = useMemo(() => {
const widthCandidate =
dropdownWidth ||
triggerWindowLayout?.width ||
triggerLayout?.width ||
0;
const width = widthCandidate || 0;
const windowWidth = Dimensions.get("window").width || 0;
const leftBase = triggerWindowLayout?.x ?? 0;
const left =
width > 0 && windowWidth > 0
? Math.min(Math.max(leftBase, 0), Math.max(0, windowWidth - width))
: Math.max(leftBase, 0);
return {
top: triggerWindowLayout?.y ?? 0,
left,
width: width || undefined,
};
}, [dropdownWidth, triggerLayout?.width, triggerWindowLayout]);
const dropdownContent = (
<BlurView
intensity={BUTTON_BLUR_INTENSITY}
tint="dark"
style={styles.dropdownOverlay}
>
<ProjectRow
isTitle={true}
isOpen={isOpen}
project={currentProject}
formatDate={formatDate}
onSelect={toggleDropdown}
/>
<View style={styles.dropdownOverlayContent}>
<FlatList
data={dropdownProjects}
keyExtractor={(project, index) =>
project?.id || project?.title || `project-${index}`
}
ItemSeparatorComponent={() => <View style={{ height: 10 }} />}
renderItem={({ item: project }) => (
<ProjectRow
project={project}
formatDate={formatDate}
isSelected={currentProject?.id === project?.id}
onSelect={handleSelect}
onModify={handleModify}
/>
)}
nestedScrollEnabled
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.dropdownListContent}
style={styles.dropdownList}
/>
<GradientButton
containerStyle={styles.createButtonContainer}
title="Commencer à créer"
onPress={handleCreate}
/>
</View>
</BlurView>
);
return (
<View style={[styles.container, style]}>
{isOpen ? (
<Pressable style={styles.backdrop} onPress={closeDropdown} />
) : null}
<View
ref={triggerRef}
style={styles.triggerWrapper}
onLayout={(event) => setTriggerLayout(event.nativeEvent.layout)}
onLayout={(event) => {
setTriggerLayout(event.nativeEvent.layout);
measureTriggerPosition();
}}
>
<Animated.View
pointerEvents={isOpen ? "none" : "auto"}
@@ -196,61 +291,29 @@ const ProjectDropDown = ({
</BlurView>
</Animated.View>
<Animated.View
pointerEvents={isOpen ? "auto" : "none"}
style={[
styles.dropdownOverlayContainer,
{ width: dropdownWidth || "100%" },
{
opacity: dropdownAnimation,
transform: [
{ translateY: overlayTranslateY },
{ scale: overlayScale },
],
},
]}
>
<BlurView
intensity={BUTTON_BLUR_INTENSITY}
tint="dark"
style={styles.dropdownOverlay}
>
<ProjectRow
isTitle={true}
isOpen={isOpen}
project={currentProject}
formatDate={formatDate}
onSelect={toggleDropdown}
/>
<View style={styles.dropdownOverlayContent}>
<FlatList
data={dropdownProjects}
keyExtractor={(project, index) =>
project?.id || project?.title || `project-${index}`
}
ItemSeparatorComponent={() => <View style={{ height: 10 }} />}
renderItem={({ item: project }) => (
<ProjectRow
project={project}
formatDate={formatDate}
isSelected={currentProject?.id === project?.id}
onSelect={handleSelect}
onModify={handleModify}
/>
)}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.dropdownListContent}
style={styles.dropdownList}
/>
<GradientButton
containerStyle={styles.createButtonContainer}
title="Commencer à créer"
onPress={handleCreate}
/>
{isOpen && triggerWindowLayout ? (
<Portal>
<View style={styles.portalContainer}>
<Pressable style={styles.backdrop} onPress={closeDropdown} />
<Animated.View
pointerEvents="auto"
style={[
styles.dropdownOverlayContainer,
overlayPositionStyle,
{
opacity: dropdownAnimation,
transform: [
{ translateY: overlayTranslateY },
{ scale: overlayScale },
],
},
]}
>
{dropdownContent}
</Animated.View>
</View>
</BlurView>
</Animated.View>
</Portal>
) : null}
</View>
</View>
);
@@ -435,6 +498,11 @@ const styles = StyleSheet.create({
chevronOpen: {
transform: [{ rotate: "180deg" }],
},
portalContainer: {
...StyleSheet.absoluteFillObject,
zIndex: 999,
elevation: 999,
},
backdrop: {
...StyleSheet.absoluteFillObject,
zIndex: 1,