fix: improve modal behavior on web and Android scrolling
- Restructure ModalBase to use absolute-positioned backdrop behind card content, fixing modal stacking issues on web (React Native Web portals) - Hide EventOverlay when DeleteEventModal is open to prevent z-index conflicts on web - Add nestedScrollEnabled to CardBase ScrollView for Android - Use TouchableOpacity with delayPressIn in EventCard for scroll-friendly touch handling - Keep eventToDelete state stable during modal fade-out to prevent content flash between recurring/single variants - Fix German umlauts in DeleteEventModal
This commit is contained in:
@@ -188,14 +188,14 @@ const Calendar = () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to delete event:", error);
|
||||
} finally {
|
||||
setEventToDelete(null);
|
||||
}
|
||||
// Note: Don't clear eventToDelete here - it will be overwritten when opening a new modal.
|
||||
// Clearing it during fade-out animation causes the modal content to flash from recurring to single.
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setDeleteModalVisible(false);
|
||||
setEventToDelete(null);
|
||||
// Note: Don't clear eventToDelete - keeps modal content stable during fade-out animation
|
||||
};
|
||||
|
||||
// Get events for selected date
|
||||
@@ -222,7 +222,7 @@ const Calendar = () => {
|
||||
onDayPress={handleDayPress}
|
||||
/>
|
||||
<EventOverlay
|
||||
visible={selectedDate !== null}
|
||||
visible={selectedDate !== null && !deleteModalVisible}
|
||||
date={selectedDate}
|
||||
events={selectedDateEvents}
|
||||
onClose={handleCloseOverlay}
|
||||
|
||||
@@ -84,7 +84,10 @@ export const CardBase = ({
|
||||
|
||||
{/* Content */}
|
||||
{scrollable ? (
|
||||
<ScrollView style={{ maxHeight: maxContentHeight }}>
|
||||
<ScrollView
|
||||
style={{ maxHeight: maxContentHeight }}
|
||||
nestedScrollEnabled={true}
|
||||
>
|
||||
{contentElement}
|
||||
</ScrollView>
|
||||
) : (
|
||||
|
||||
@@ -77,7 +77,7 @@ export const DeleteEventModal = ({
|
||||
// Non-recurring event: simple confirmation
|
||||
<View>
|
||||
<Text className="text-base mb-4" style={{ color: theme.textPrimary }}>
|
||||
Moechtest du diesen Termin wirklich loeschen?
|
||||
Möchtest du diesen Termin wirklich löschen?
|
||||
</Text>
|
||||
<Pressable
|
||||
onPress={() => onConfirm("all")}
|
||||
@@ -90,7 +90,7 @@ export const DeleteEventModal = ({
|
||||
className="font-medium text-base text-center"
|
||||
style={{ color: theme.buttonText }}
|
||||
>
|
||||
Loeschen
|
||||
Löschen
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { View, Pressable } from "react-native";
|
||||
import { View, TouchableOpacity } from "react-native";
|
||||
import { ExpandedEvent } from "@calchat/shared";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { useThemeStore } from "../stores/ThemeStore";
|
||||
@@ -21,10 +21,12 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
|
||||
description={event.description}
|
||||
isRecurring={event.isRecurring}
|
||||
>
|
||||
{/* Action buttons */}
|
||||
{/* Action buttons - TouchableOpacity with delayPressIn allows ScrollView to detect scroll gestures */}
|
||||
<View className="flex-row justify-end mt-3 gap-3">
|
||||
<Pressable
|
||||
<TouchableOpacity
|
||||
onPress={onEdit}
|
||||
delayPressIn={100}
|
||||
activeOpacity={0.7}
|
||||
className="w-10 h-10 rounded-full items-center justify-center"
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
@@ -32,9 +34,11 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
|
||||
}}
|
||||
>
|
||||
<Feather name="edit-2" size={18} color={theme.textPrimary} />
|
||||
</Pressable>
|
||||
<Pressable
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={onDelete}
|
||||
delayPressIn={100}
|
||||
activeOpacity={0.7}
|
||||
className="w-10 h-10 rounded-full items-center justify-center"
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
@@ -42,7 +46,7 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
|
||||
}}
|
||||
>
|
||||
<Feather name="trash-2" size={18} color={theme.textPrimary} />
|
||||
</Pressable>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</EventCardBase>
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Modal, Pressable } from "react-native";
|
||||
import { Modal, Pressable, View } from "react-native";
|
||||
import { ReactNode } from "react";
|
||||
import { useThemeStore } from "../stores/ThemeStore";
|
||||
import { CardBase } from "./CardBase";
|
||||
@@ -36,19 +36,21 @@ export const ModalBase = ({
|
||||
animationType="fade"
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<Pressable
|
||||
className="flex-1 justify-center items-center"
|
||||
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
onPress={onClose}
|
||||
>
|
||||
<View className="flex-1 justify-center items-center">
|
||||
{/* Backdrop - absolute positioned behind the card */}
|
||||
<Pressable
|
||||
className="absolute inset-0"
|
||||
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
onPress={onClose}
|
||||
/>
|
||||
{/* Card content - on top, naturally blocks touches to backdrop */}
|
||||
<View
|
||||
className="w-11/12 rounded-2xl overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: theme.primeBg,
|
||||
borderWidth: 4,
|
||||
borderColor: theme.borderPrimary,
|
||||
}}
|
||||
onPress={(e) => e.stopPropagation()}
|
||||
>
|
||||
<CardBase
|
||||
title={title}
|
||||
@@ -65,8 +67,8 @@ export const ModalBase = ({
|
||||
>
|
||||
{children}
|
||||
</CardBase>
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user