refactor: add CardBase and ModalBase components

- Add CardBase: reusable card with header, content, footer
  - Configurable via props: padding, border, text size, background
- Add ModalBase: modal wrapper using CardBase internally
  - Provides backdrop, click-outside-to-close, Android back button
- Refactor EventCardBase to use CardBase
- Refactor DeleteEventModal to use ModalBase
- Refactor EventOverlay (calendar.tsx) to use ModalBase
- Update CLAUDE.md with component documentation
This commit is contained in:
2026-01-25 21:50:19 +01:00
parent 2b999d9b0f
commit 726334c155
7 changed files with 366 additions and 265 deletions

View File

@@ -86,13 +86,15 @@ src/
│ ├── BaseButton.tsx # Reusable button component (themed, supports children) │ ├── BaseButton.tsx # Reusable button component (themed, supports children)
│ ├── Header.tsx # Header component (themed) │ ├── Header.tsx # Header component (themed)
│ ├── AuthButton.tsx # Reusable button for auth screens (themed, with shadow) │ ├── AuthButton.tsx # Reusable button for auth screens (themed, with shadow)
│ ├── CardBase.tsx # Reusable card component (header + content + optional footer)
│ ├── ModalBase.tsx # Reusable modal with backdrop (uses CardBase, click-outside-to-close)
│ ├── ChatBubble.tsx # Reusable chat bubble component (used by ChatMessage & TypingIndicator) │ ├── ChatBubble.tsx # Reusable chat bubble component (used by ChatMessage & TypingIndicator)
│ ├── TypingIndicator.tsx # Animated typing indicator (. .. ...) shown while waiting for AI response │ ├── TypingIndicator.tsx # Animated typing indicator (. .. ...) shown while waiting for AI response
│ ├── EventCardBase.tsx # Shared event card layout with icons (used by EventCard & ProposedEventCard) │ ├── EventCardBase.tsx # Event card layout with icons (uses CardBase)
│ ├── EventCard.tsx # Calendar event card (uses EventCardBase + edit/delete buttons) │ ├── EventCard.tsx # Calendar event card (uses EventCardBase + edit/delete buttons)
│ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal (skeleton) │ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal (skeleton)
│ ├── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons) │ ├── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons)
│ └── DeleteEventModal.tsx # Unified delete confirmation modal (recurring: 3 options, non-recurring: simple confirm) │ └── DeleteEventModal.tsx # Delete confirmation modal (uses ModalBase)
├── Themes.tsx # Theme definitions: THEMES object with defaultLight/defaultDark, Theme type ├── Themes.tsx # Theme definitions: THEMES object with defaultLight/defaultDark, Theme type
├── logging/ ├── logging/
│ ├── index.ts # Re-exports │ ├── index.ts # Re-exports
@@ -157,6 +159,58 @@ setTheme("defaultDark"); // or "defaultLight"
**Note:** `shadowColor` only works on iOS. Android uses `elevation` with system-defined shadow colors. **Note:** `shadowColor` only works on iOS. Android uses `elevation` with system-defined shadow colors.
### Base Components (CardBase & ModalBase)
Reusable base components for cards and modals with consistent styling.
**CardBase** - Card structure with header, content, and optional footer:
```typescript
<CardBase
title="Title"
subtitle="Optional subtitle"
footer={{ label: "Button", onPress: () => {} }}
// Styling props (all optional):
headerPadding={4} // p-{n}, default: px-3 py-2
contentPadding={4} // p-{n}, default: px-3 py-2
headerTextSize="text-lg" // "text-sm" | "text-base" | "text-lg" | "text-xl"
borderWidth={2} // outer border, default: 2
headerBorderWidth={3} // header bottom border, default: borderWidth
contentBg={theme.primeBg} // content background color, default: theme.secondaryBg
scrollable={true} // wrap content in ScrollView
maxContentHeight={400} // for scrollable content
>
{children}
</CardBase>
```
**ModalBase** - Modal with backdrop using CardBase internally:
```typescript
<ModalBase
visible={isVisible}
onClose={() => setVisible(false)}
title="Modal Title"
subtitle="Optional"
footer={{ label: "Close", onPress: onClose }}
scrollable={true}
maxContentHeight={400}
>
{children}
</ModalBase>
```
ModalBase provides: transparent Modal + backdrop (click-outside-to-close) + Android back button support.
**Component Hierarchy:**
```
CardBase
├── ModalBase (uses CardBase)
│ ├── DeleteEventModal
│ └── EventOverlay (in calendar.tsx)
└── EventCardBase (uses CardBase)
├── EventCard
└── ProposedEventCard
```
### Backend Architecture (apps/server) ### Backend Architecture (apps/server)
``` ```
@@ -461,10 +515,13 @@ NODE_ENV=development # development = pretty logs, production = JSON
- keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled" - keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled"
- `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate) - fully implemented with recurring delete modes - `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate) - fully implemented with recurring delete modes
- `ChatService`: sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation() - fully implemented with cursor pagination and recurring delete support - `ChatService`: sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation() - fully implemented with cursor pagination and recurring delete support
- `EventCardBase`: Shared base component with event layout (header, date/time/recurring icons, description) - used by both EventCard and ProposedEventCard - `CardBase`: Reusable card component with header (title/subtitle), content area, and optional footer button - configurable padding, border, text size via props
- `ModalBase`: Reusable modal wrapper with backdrop, uses CardBase internally - provides click-outside-to-close and Android back button support
- `EventCardBase`: Event card with date/time/recurring icons - uses CardBase for structure
- `EventCard`: Uses EventCardBase + edit/delete buttons for calendar display - `EventCard`: Uses EventCardBase + edit/delete buttons for calendar display
- `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions with deleteMode display) - `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions with deleteMode display)
- `DeleteEventModal`: Unified delete confirmation modal - shows three options for recurring events (single/future/all), simple confirm for non-recurring - `DeleteEventModal`: Delete confirmation modal using ModalBase - shows three options for recurring events (single/future/all), simple confirm for non-recurring
- `EventOverlay` (in calendar.tsx): Day events overlay using ModalBase - shows EventCards for selected day
- `Themes.tsx`: Theme definitions with THEMES object (defaultLight, defaultDark) including all color tokens (textPrimary, borderPrimary, eventIndicator, secondaryBg, shadowColor, etc.) - `Themes.tsx`: Theme definitions with THEMES object (defaultLight, defaultDark) including all color tokens (textPrimary, borderPrimary, eventIndicator, secondaryBg, shadowColor, etc.)
- `EventsStore`: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[] - `EventsStore`: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[]
- `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages(), isWaitingForResponse/setWaitingForResponse() for typing indicator - loads from server on mount and persists across tab switches - `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages(), isWaitingForResponse/setWaitingForResponse() for typing indicator - loads from server on mount and persists across tab switches

View File

@@ -1,11 +1,4 @@
import { import { Animated, Modal, Pressable, Text, View } from "react-native";
Animated,
Modal,
Pressable,
Text,
View,
ScrollView,
} from "react-native";
import { import {
DAYS, DAYS,
MONTHS, MONTHS,
@@ -16,6 +9,7 @@ import {
import Header from "../../components/Header"; import Header from "../../components/Header";
import { EventCard } from "../../components/EventCard"; import { EventCard } from "../../components/EventCard";
import { DeleteEventModal } from "../../components/DeleteEventModal"; import { DeleteEventModal } from "../../components/DeleteEventModal";
import { ModalBase } from "../../components/ModalBase";
import React, { import React, {
useCallback, useCallback,
useEffect, useEffect,
@@ -263,7 +257,6 @@ const EventOverlay = ({
onEditEvent, onEditEvent,
onDeleteEvent, onDeleteEvent,
}: EventOverlayProps) => { }: EventOverlayProps) => {
const { theme } = useThemeStore();
if (!date) return null; if (!date) return null;
const dateString = date.toLocaleDateString("de-DE", { const dateString = date.toLocaleDateString("de-DE", {
@@ -273,75 +266,27 @@ const EventOverlay = ({
year: "numeric", year: "numeric",
}); });
const subtitle = `${events.length} ${events.length === 1 ? "Termin" : "Termine"}`;
return ( return (
<Modal <ModalBase
visible={visible} visible={visible}
transparent={true} onClose={onClose}
animationType="fade" title={dateString}
onRequestClose={onClose} subtitle={subtitle}
footer={{ label: "Schliessen", onPress: onClose }}
scrollable={true}
maxContentHeight={400}
> >
<Pressable {events.map((event, index) => (
className="flex-1 justify-center items-center" <EventCard
style={{ backgroundColor: "rgba(0,0,0,0.5)" }} key={`${event.id}-${index}`}
onPress={onClose} event={event}
> onEdit={() => onEditEvent(event)}
<Pressable onDelete={() => onDeleteEvent(event)}
className="w-11/12 max-h-3/4 rounded-2xl overflow-hidden" />
style={{ ))}
backgroundColor: theme.primeBg, </ModalBase>
borderWidth: 4,
borderColor: theme.borderPrimary,
}}
onPress={(e) => e.stopPropagation()}
>
{/* Header */}
<View
className="px-4 py-3"
style={{
backgroundColor: theme.chatBot,
borderBottomWidth: 3,
borderBottomColor: theme.borderPrimary,
}}
>
<Text
className="font-bold text-lg"
style={{ color: theme.textPrimary }}
>
{dateString}
</Text>
<Text style={{ color: theme.textPrimary }}>
{events.length} {events.length === 1 ? "Termin" : "Termine"}
</Text>
</View>
{/* Events List */}
<ScrollView className="p-4" style={{ maxHeight: 400 }}>
{events.map((event, index) => (
<EventCard
key={`${event.id}-${index}`}
event={event}
onEdit={() => onEditEvent(event)}
onDelete={() => onDeleteEvent(event)}
/>
))}
</ScrollView>
{/* Close button */}
<Pressable
onPress={onClose}
className="py-3 items-center"
style={{
borderTopWidth: 1,
borderTopColor: theme.placeholderBg,
}}
>
<Text style={{ color: theme.primeFg }} className="font-bold">
Schließen
</Text>
</Pressable>
</Pressable>
</Pressable>
</Modal>
); );
}; };

View File

@@ -114,9 +114,7 @@ const Chat = () => {
const message = messages.find((m) => m.id === messageId); const message = messages.find((m) => m.id === messageId);
if (message?.proposedChanges) { if (message?.proposedChanges) {
const updatedProposals = message.proposedChanges.map((p) => const updatedProposals = message.proposedChanges.map((p) =>
p.id === proposalId p.id === proposalId ? { ...p, respondedAction: action } : p,
? { ...p, respondedAction: action }
: p,
); );
updateMessage(messageId, { proposedChanges: updatedProposals }); updateMessage(messageId, { proposedChanges: updatedProposals });
} }

View File

@@ -0,0 +1,113 @@
import { View, Text, Pressable, ScrollView } from "react-native";
import { ReactNode } from "react";
import { useThemeStore } from "../stores/ThemeStore";
type TextSize = "text-sm" | "text-base" | "text-lg" | "text-xl";
type CardBaseProps = {
title: string;
subtitle?: string;
children: ReactNode;
footer?: {
label: string;
onPress: () => void;
};
className?: string;
scrollable?: boolean;
maxContentHeight?: number;
borderWidth?: number;
headerBorderWidth?: number;
headerPadding?: number;
contentPadding?: number;
headerTextSize?: TextSize;
contentBg?: string;
};
export const CardBase = ({
title,
subtitle,
children,
footer,
className = "",
scrollable = false,
maxContentHeight,
borderWidth = 2,
headerBorderWidth,
headerPadding,
contentPadding,
headerTextSize = "text-base",
contentBg,
}: CardBaseProps) => {
const { theme } = useThemeStore();
const effectiveHeaderBorderWidth = headerBorderWidth ?? borderWidth;
const headerPaddingClass = headerPadding ? `p-${headerPadding}` : "px-3 py-2";
const contentPaddingClass = contentPadding
? `p-${contentPadding}`
: "px-3 py-2";
const contentElement = (
<View
className={contentPaddingClass}
style={{ backgroundColor: contentBg ?? theme.secondaryBg }}
>
{children}
</View>
);
return (
<View
className={`rounded-xl overflow-hidden ${className}`}
style={{ borderWidth, borderColor: theme.borderPrimary }}
>
{/* Header */}
<View
className={headerPaddingClass}
style={{
backgroundColor: theme.chatBot,
borderBottomWidth: effectiveHeaderBorderWidth,
borderBottomColor: theme.borderPrimary,
}}
>
<Text
className={`font-bold ${headerTextSize}`}
style={{ color: theme.textPrimary }}
>
{title}
</Text>
{subtitle && (
<Text style={{ color: theme.primeFg }} numberOfLines={1}>
{subtitle}
</Text>
)}
</View>
{/* Content */}
{scrollable ? (
<ScrollView style={{ maxHeight: maxContentHeight }}>
{contentElement}
</ScrollView>
) : (
contentElement
)}
{/* Footer (optional) */}
{footer && (
<Pressable
onPress={footer.onPress}
className="py-3 items-center"
style={{
borderTopWidth: 1,
borderTopColor: theme.placeholderBg,
}}
>
<Text style={{ color: theme.primeFg }} className="font-bold">
{footer.label}
</Text>
</Pressable>
)}
</View>
);
};
export default CardBase;

View File

@@ -1,6 +1,7 @@
import { Modal, Pressable, Text, View } from "react-native"; import { Pressable, Text, View } from "react-native";
import { RecurringDeleteMode } from "@calchat/shared"; import { RecurringDeleteMode } from "@calchat/shared";
import { useThemeStore } from "../stores/ThemeStore"; import { useThemeStore } from "../stores/ThemeStore";
import { ModalBase } from "./ModalBase";
type DeleteEventModalProps = { type DeleteEventModalProps = {
visible: boolean; visible: boolean;
@@ -13,24 +14,20 @@ type DeleteEventModalProps = {
type DeleteOption = { type DeleteOption = {
mode: RecurringDeleteMode; mode: RecurringDeleteMode;
label: string; label: string;
description: string;
}; };
const RECURRING_DELETE_OPTIONS: DeleteOption[] = [ const RECURRING_DELETE_OPTIONS: DeleteOption[] = [
{ {
mode: "single", mode: "single",
label: "Nur dieses Vorkommen", label: "Nur dieses Vorkommen",
description: "Nur der ausgewaehlte Termin wird entfernt",
}, },
{ {
mode: "future", mode: "future",
label: "Dieses und zukuenftige", label: "Dieses und zukünftige",
description: "Dieser und alle folgenden Termine werden entfernt",
}, },
{ {
mode: "all", mode: "all",
label: "Alle Vorkommen", label: "Alle Vorkommen",
description: "Die gesamte Terminserie wird geloescht",
}, },
]; ];
@@ -43,120 +40,61 @@ export const DeleteEventModal = ({
}: DeleteEventModalProps) => { }: DeleteEventModalProps) => {
const { theme } = useThemeStore(); const { theme } = useThemeStore();
const title = isRecurring
? "Wiederkehrenden Termin löschen"
: "Termin loeschen";
return ( return (
<Modal <ModalBase
visible={visible} visible={visible}
transparent={true} onClose={onCancel}
animationType="fade" title={title}
onRequestClose={onCancel} subtitle={eventTitle}
footer={{ label: "Abbrechen", onPress: onCancel }}
> >
<Pressable {isRecurring ? (
className="flex-1 justify-center items-center" // Recurring event: show three options
style={{ backgroundColor: "rgba(0,0,0,0.5)" }} RECURRING_DELETE_OPTIONS.map((option) => (
onPress={onCancel} <Pressable
> key={option.mode}
<Pressable onPress={() => onConfirm(option.mode)}
className="w-11/12 rounded-2xl overflow-hidden" className="py-3 px-4 rounded-lg mb-2"
style={{
backgroundColor: theme.primeBg,
borderWidth: 4,
borderColor: theme.borderPrimary,
}}
onPress={(e) => e.stopPropagation()}
>
{/* Header */}
<View
className="px-4 py-3"
style={{ style={{
backgroundColor: theme.chatBot, backgroundColor: theme.secondaryBg,
borderBottomWidth: 3, borderWidth: 1,
borderBottomColor: theme.borderPrimary, borderColor: theme.borderPrimary,
}} }}
> >
<Text <Text
className="font-bold text-lg" className="font-medium text-base"
style={{ color: theme.textPrimary }} style={{ color: theme.textPrimary }}
> >
{isRecurring {option.label}
? "Wiederkehrenden Termin loeschen"
: "Termin loeschen"}
</Text>
<Text style={{ color: theme.textSecondary }} numberOfLines={1}>
{eventTitle}
</Text>
</View>
{/* Content */}
<View className="p-4">
{isRecurring ? (
// Recurring event: show three options
RECURRING_DELETE_OPTIONS.map((option) => (
<Pressable
key={option.mode}
onPress={() => onConfirm(option.mode)}
className="py-3 px-4 rounded-lg mb-2"
style={{
backgroundColor: theme.secondaryBg,
borderWidth: 1,
borderColor: theme.borderPrimary,
}}
>
<Text
className="font-medium text-base"
style={{ color: theme.textPrimary }}
>
{option.label}
</Text>
<Text
className="text-sm mt-1"
style={{ color: theme.textMuted }}
>
{option.description}
</Text>
</Pressable>
))
) : (
// Non-recurring event: simple confirmation
<View>
<Text
className="text-base mb-4"
style={{ color: theme.textPrimary }}
>
Möchtest du diesen Termin wirklich löschen?
</Text>
<Pressable
onPress={() => onConfirm("all")}
className="py-3 px-4 rounded-lg"
style={{
backgroundColor: theme.rejectButton,
}}
>
<Text
className="font-medium text-base text-center"
style={{ color: theme.buttonText }}
>
Loeschen
</Text>
</Pressable>
</View>
)}
</View>
{/* Cancel button */}
<Pressable
onPress={onCancel}
className="py-3 items-center"
style={{
borderTopWidth: 1,
borderTopColor: theme.placeholderBg,
}}
>
<Text style={{ color: theme.primeFg }} className="font-bold">
Abbrechen
</Text> </Text>
</Pressable> </Pressable>
</Pressable> ))
</Pressable> ) : (
</Modal> // Non-recurring event: simple confirmation
<View>
<Text className="text-base mb-4" style={{ color: theme.textPrimary }}>
Moechtest du diesen Termin wirklich loeschen?
</Text>
<Pressable
onPress={() => onConfirm("all")}
className="py-3 px-4 rounded-lg"
style={{
backgroundColor: theme.rejectButton,
}}
>
<Text
className="font-medium text-base text-center"
style={{ color: theme.buttonText }}
>
Loeschen
</Text>
</Pressable>
</View>
)}
</ModalBase>
); );
}; };

View File

@@ -2,6 +2,7 @@ import { View, Text } from "react-native";
import { Feather } from "@expo/vector-icons"; import { Feather } from "@expo/vector-icons";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { useThemeStore } from "../stores/ThemeStore"; import { useThemeStore } from "../stores/ThemeStore";
import { CardBase } from "./CardBase";
type EventCardBaseProps = { type EventCardBaseProps = {
className?: string; className?: string;
@@ -61,84 +62,59 @@ export const EventCardBase = ({
children, children,
}: EventCardBaseProps) => { }: EventCardBaseProps) => {
const { theme } = useThemeStore(); const { theme } = useThemeStore();
return ( return (
<View <CardBase title={title} className={className} borderWidth={2}>
className={`rounded-xl overflow-hidden ${className}`} {/* Date */}
style={{ borderWidth: 2, borderColor: theme.borderPrimary }} <View className="flex-row items-center mb-1">
> <Feather
{/* Header with title */} name="calendar"
<View size={16}
className="px-3 py-2" color={theme.textPrimary}
style={{ style={{ marginRight: 8 }}
backgroundColor: theme.chatBot, />
borderBottomWidth: 2, <Text style={{ color: theme.textPrimary }}>
borderBottomColor: theme.borderPrimary, {formatDate(startTime)}
}}
>
<Text
className="font-bold text-base"
style={{ color: theme.textPrimary }}
>
{title}
</Text> </Text>
</View> </View>
{/* Content */} {/* Time with duration */}
<View <View className="flex-row items-center mb-1">
className="px-3 py-2" <Feather
style={{ backgroundColor: theme.secondaryBg }} name="clock"
> size={16}
{/* Date */} color={theme.textPrimary}
<View className="flex-row items-center mb-1"> style={{ marginRight: 8 }}
<Feather />
name="calendar" <Text style={{ color: theme.textPrimary }}>
size={16} {formatTime(startTime)} - {formatTime(endTime)} (
color={theme.textPrimary} {formatDuration(startTime, endTime)})
style={{ marginRight: 8 }} </Text>
/>
<Text style={{ color: theme.textPrimary }}>
{formatDate(startTime)}
</Text>
</View>
{/* Time with duration */}
<View className="flex-row items-center mb-1">
<Feather
name="clock"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
<Text style={{ color: theme.textPrimary }}>
{formatTime(startTime)} - {formatTime(endTime)} (
{formatDuration(startTime, endTime)})
</Text>
</View>
{/* Recurring indicator */}
{isRecurring && (
<View className="flex-row items-center mb-1">
<Feather
name="repeat"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
<Text style={{ color: theme.textPrimary }}>Wiederkehrend</Text>
</View>
)}
{/* Description */}
{description && (
<Text style={{ color: theme.textPrimary }} className="text-sm mt-1">
{description}
</Text>
)}
{/* Action buttons slot */}
{children}
</View> </View>
</View>
{/* Recurring indicator */}
{isRecurring && (
<View className="flex-row items-center mb-1">
<Feather
name="repeat"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
<Text style={{ color: theme.textPrimary }}>Wiederkehrend</Text>
</View>
)}
{/* Description */}
{description && (
<Text style={{ color: theme.textPrimary }} className="text-sm mt-1">
{description}
</Text>
)}
{/* Action buttons slot */}
{children}
</CardBase>
); );
}; };

View File

@@ -0,0 +1,74 @@
import { Modal, Pressable } from "react-native";
import { ReactNode } from "react";
import { useThemeStore } from "../stores/ThemeStore";
import { CardBase } from "./CardBase";
type ModalBaseProps = {
visible: boolean;
onClose: () => void;
title: string;
subtitle?: string;
children: ReactNode;
footer?: {
label: string;
onPress: () => void;
};
scrollable?: boolean;
maxContentHeight?: number;
};
export const ModalBase = ({
visible,
onClose,
title,
subtitle,
children,
footer,
scrollable,
maxContentHeight,
}: ModalBaseProps) => {
const { theme } = useThemeStore();
return (
<Modal
visible={visible}
transparent={true}
animationType="fade"
onRequestClose={onClose}
>
<Pressable
className="flex-1 justify-center items-center"
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
onPress={onClose}
>
<Pressable
className="w-11/12 rounded-2xl overflow-hidden"
style={{
backgroundColor: theme.primeBg,
borderWidth: 4,
borderColor: theme.borderPrimary,
}}
onPress={(e) => e.stopPropagation()}
>
<CardBase
title={title}
subtitle={subtitle}
footer={footer}
scrollable={scrollable}
maxContentHeight={maxContentHeight}
borderWidth={0}
headerBorderWidth={3}
headerPadding={4}
contentPadding={4}
headerTextSize="text-lg"
contentBg={theme.primeBg}
>
{children}
</CardBase>
</Pressable>
</Pressable>
</Modal>
);
};
export default ModalBase;