Initial commit: expo-popcore-app
This commit is contained in:
428
components/ui/alert-dialog.tsx
Normal file
428
components/ui/alert-dialog.tsx
Normal file
@@ -0,0 +1,428 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
Modal,
|
||||
View,
|
||||
Text,
|
||||
Pressable,
|
||||
type ViewStyle,
|
||||
type TextStyle,
|
||||
StyleSheet,
|
||||
StatusBar,
|
||||
Platform,
|
||||
} from "react-native";
|
||||
import { BlurView } from "expo-blur";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
import { Button, buttonVariants } from "./button";
|
||||
|
||||
interface AlertDialogContextValue {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const AlertDialogContext = React.createContext<AlertDialogContextValue | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const useAlertDialogContext = () => {
|
||||
const context = React.useContext(AlertDialogContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"AlertDialog components must be used within AlertDialog provider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface AlertDialogProps {
|
||||
children: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
function AlertDialog({ children, open, onOpenChange }: AlertDialogProps) {
|
||||
const [internalOpen, setInternalOpen] = React.useState(false);
|
||||
|
||||
const isControlled = open !== undefined;
|
||||
const isOpen = isControlled ? open : internalOpen;
|
||||
|
||||
const handleSetOpen = React.useCallback(
|
||||
(value: boolean) => {
|
||||
if (!isControlled) {
|
||||
setInternalOpen(value);
|
||||
}
|
||||
onOpenChange?.(value);
|
||||
},
|
||||
[isControlled, onOpenChange]
|
||||
);
|
||||
|
||||
const value: AlertDialogContextValue = {
|
||||
open: isOpen,
|
||||
setOpen: handleSetOpen,
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialogContext.Provider value={value}>
|
||||
{children}
|
||||
</AlertDialogContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogTriggerProps {
|
||||
children: React.ReactNode;
|
||||
asChild?: boolean;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogTrigger({
|
||||
children,
|
||||
asChild,
|
||||
style,
|
||||
}: AlertDialogTriggerProps) {
|
||||
const { setOpen } = useAlertDialogContext();
|
||||
|
||||
if (asChild && React.isValidElement(children)) {
|
||||
return React.cloneElement(children as React.ReactElement<any>, {
|
||||
onPress: () => setOpen(true),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable onPress={() => setOpen(true)} style={style}>
|
||||
{children}
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogPortalProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function AlertDialogPortal({ children }: AlertDialogPortalProps) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
interface AlertDialogOverlayProps {
|
||||
className?: string;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogOverlay({ className, style }: AlertDialogOverlayProps) {
|
||||
return (
|
||||
<View
|
||||
className={cn("absolute inset-0", className)}
|
||||
style={[styles.overlay, style]}
|
||||
>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
intensity={40}
|
||||
tint="dark"
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{ backgroundColor: "#00000080" },
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogContentProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogContent({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
}: AlertDialogContentProps) {
|
||||
const { open, setOpen } = useAlertDialogContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (Platform.OS === "android") {
|
||||
if (open) {
|
||||
StatusBar.setBackgroundColor("rgba(0,0,0,0.5)", true);
|
||||
} else {
|
||||
StatusBar.setBackgroundColor("transparent", true);
|
||||
}
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
transparent
|
||||
visible={open}
|
||||
animationType="none"
|
||||
statusBarTranslucent={Platform.OS === 'android'}
|
||||
onRequestClose={() => setOpen(false)}
|
||||
hardwareAccelerated
|
||||
>
|
||||
<View style={styles.modalContainer}>
|
||||
<Pressable
|
||||
style={styles.backdrop}
|
||||
onPress={() => setOpen(false)}
|
||||
android_ripple={null}
|
||||
>
|
||||
<AlertDialogOverlay />
|
||||
</Pressable>
|
||||
|
||||
<View
|
||||
className={cn(
|
||||
"bg-background w-[90%] max-w-[500px] rounded-lg border border-border p-6 shadow-2xl gap-4",
|
||||
className
|
||||
)}
|
||||
style={[styles.content, style]}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogHeaderProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogHeader({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
}: AlertDialogHeaderProps) {
|
||||
return (
|
||||
<View
|
||||
className={cn("flex-col gap-2", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogFooterProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogFooter({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
}: AlertDialogFooterProps) {
|
||||
return (
|
||||
<View
|
||||
className={cn("flex flex-row gap-2 justify-end", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogTitleProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: TextStyle;
|
||||
}
|
||||
|
||||
function AlertDialogTitle({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
}: AlertDialogTitleProps) {
|
||||
return (
|
||||
<Text
|
||||
className={cn("text-lg font-semibold text-foreground", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogDescriptionProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: TextStyle;
|
||||
}
|
||||
|
||||
function AlertDialogDescription({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
}: AlertDialogDescriptionProps) {
|
||||
return (
|
||||
<Text
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogActionProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
onPress?: () => void;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogAction({
|
||||
children,
|
||||
className,
|
||||
onPress,
|
||||
style,
|
||||
}: AlertDialogActionProps) {
|
||||
const { setOpen } = useAlertDialogContext();
|
||||
|
||||
const handlePress = () => {
|
||||
onPress?.();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onPress={handlePress}
|
||||
className={cn("flex-1", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
interface AlertDialogCancelProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
onPress?: () => void;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
function AlertDialogCancel({
|
||||
children,
|
||||
className,
|
||||
onPress,
|
||||
style,
|
||||
}: AlertDialogCancelProps) {
|
||||
const { setOpen } = useAlertDialogContext();
|
||||
|
||||
const handlePress = () => {
|
||||
onPress?.();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={handlePress}
|
||||
className={cn("flex-1", className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
interface DeleteAlertDialogActionProps {
|
||||
onPress?: () => void;
|
||||
style?: ViewStyle;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function DeleteAlertDialogAction({
|
||||
onPress,
|
||||
style,
|
||||
className,
|
||||
}: DeleteAlertDialogActionProps) {
|
||||
return (
|
||||
<AlertDialogAction
|
||||
onPress={onPress}
|
||||
className={className}
|
||||
style={{ ...styles.deleteActionButton, ...style }}
|
||||
>
|
||||
<Text style={styles.deleteActionText}>确认删除</Text>
|
||||
</AlertDialogAction>
|
||||
);
|
||||
}
|
||||
|
||||
function CancelAlertDialogAction({
|
||||
onPress,
|
||||
style,
|
||||
className,
|
||||
}: DeleteAlertDialogActionProps) {
|
||||
return (
|
||||
<AlertDialogAction
|
||||
onPress={onPress}
|
||||
className={className}
|
||||
style={{ ...styles.cancelActionButton, ...style }}
|
||||
>
|
||||
<Text style={styles.cancelActionText}>暂不</Text>
|
||||
</AlertDialogAction>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modalContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
...Platform.select({
|
||||
android: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
}),
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
},
|
||||
overlay: {
|
||||
zIndex: 1,
|
||||
},
|
||||
content: {
|
||||
zIndex: 2,
|
||||
},
|
||||
deleteActionButton: {
|
||||
backgroundColor: "#FA3F2C",
|
||||
alignItems: 'center'
|
||||
},
|
||||
deleteActionText: {
|
||||
color: "#FFFFFF",
|
||||
fontSize: 14,
|
||||
fontWeight: "500",
|
||||
},
|
||||
cancelActionButton: {
|
||||
backgroundColor: '#262A31',
|
||||
alignItems: 'center'
|
||||
},
|
||||
cancelActionText: {
|
||||
color: "#CCCCCC",
|
||||
fontSize: 14,
|
||||
}
|
||||
});
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
DeleteAlertDialogAction,
|
||||
CancelAlertDialogAction
|
||||
};
|
||||
Reference in New Issue
Block a user