/**
* BottomSheet — modal bas d'écran respectant le design Memento
* Usage:
* setV(false)} title="Titre">
* ...children
*
*/
import { useEffect, useRef } from 'react'
import {
View, Text, Modal, TouchableOpacity, Animated,
Pressable, StyleSheet,
} from 'react-native'
import { X } from 'lucide-react-native'
import { C } from '@/lib/theme'
interface Props {
visible: boolean
onClose: () => void
title?: string
children: React.ReactNode
}
export function BottomSheet({ visible, onClose, title, children }: Props) {
const translateY = useRef(new Animated.Value(400)).current
const opacity = useRef(new Animated.Value(0)).current
useEffect(() => {
if (visible) {
Animated.parallel([
Animated.spring(translateY, { toValue: 0, useNativeDriver: true, damping: 20, stiffness: 200 }),
Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true }),
]).start()
} else {
Animated.parallel([
Animated.timing(translateY, { toValue: 400, duration: 220, useNativeDriver: true }),
Animated.timing(opacity, { toValue: 0, duration: 200, useNativeDriver: true }),
]).start()
}
}, [visible])
return (
{/* Handle bar */}
{title && (
{title}
)}
{children}
)
}
const s = StyleSheet.create({
overlay: { flex: 1, backgroundColor: 'rgba(26,26,24,0.5)', justifyContent: 'flex-end' },
sheet: {
backgroundColor: C.paper,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
paddingBottom: 32,
shadowColor: '#000',
shadowOffset: { width: 0, height: -4 },
shadowOpacity: 0.12,
shadowRadius: 16,
elevation: 16,
},
handle: { width: 36, height: 4, backgroundColor: C.border, borderRadius: 2, alignSelf: 'center', marginTop: 12, marginBottom: 4 },
titleRow: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 20, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: C.border },
title: { flex: 1, fontSize: 15, fontWeight: '700', color: C.ink },
closeBtn: { padding: 2 },
})