117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import React from "react";
|
|
import { View, Pressable, ScrollView, ActivityIndicator } from "react-native";
|
|
import { Text } from "@/components/ui/text";
|
|
import { cn } from "@/lib/utils";
|
|
import { ArrowLeft, ArrowRight } from "@/lib/icons";
|
|
import { useSirouRouter } from "@sirou/react-native";
|
|
import { AppRoutes } from "@/lib/routes";
|
|
|
|
interface Step {
|
|
key: string;
|
|
label: string;
|
|
}
|
|
|
|
interface FormFlowProps {
|
|
steps: Step[];
|
|
currentStep: number;
|
|
onNext: () => void;
|
|
onBack: () => void;
|
|
onComplete: () => void;
|
|
children: React.ReactNode;
|
|
loading?: boolean;
|
|
nextLabel?: string;
|
|
completeLabel?: string;
|
|
hideFooter?: boolean;
|
|
hideHeader?: boolean;
|
|
}
|
|
|
|
export function FormFlow({
|
|
steps,
|
|
currentStep,
|
|
onNext,
|
|
onBack,
|
|
onComplete,
|
|
children,
|
|
loading = false,
|
|
nextLabel = "Continue",
|
|
completeLabel = "Submit",
|
|
hideFooter = false,
|
|
hideHeader = false,
|
|
}: FormFlowProps) {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const isFirst = currentStep === 0;
|
|
const isLast = currentStep === steps.length - 1;
|
|
|
|
return (
|
|
<View className="flex-1 bg-background">
|
|
{!hideHeader && (
|
|
<View className="px-5 pt-4 pb-3 flex-row items-center justify-between">
|
|
<Pressable
|
|
onPress={isFirst ? () => nav.back() : onBack}
|
|
className="h-9 w-9 rounded-[8px] border border-border items-center justify-center"
|
|
>
|
|
<ArrowLeft size={18} color="#64748b" />
|
|
</Pressable>
|
|
<View className="flex-row items-center gap-1">
|
|
<Text className="text-muted-foreground text-[11px] font-sans-bold">
|
|
{currentStep + 1}
|
|
</Text>
|
|
<Text className="text-muted-foreground text-[11px] font-sans-medium">
|
|
/
|
|
</Text>
|
|
<Text className="text-muted-foreground text-[11px] font-sans-medium">
|
|
{steps.length}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
<View className="px-5 pb-5">
|
|
<View className="flex-row gap-1">
|
|
{steps.map((step, idx) => (
|
|
<View
|
|
key={step.key}
|
|
className={cn(
|
|
"h-1.5 rounded-full flex-1",
|
|
idx <= currentStep ? "bg-primary" : "bg-border",
|
|
)}
|
|
/>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
<ScrollView
|
|
className="flex-1 px-5"
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{ paddingBottom: 20 }}
|
|
>
|
|
{children}
|
|
</ScrollView>
|
|
|
|
{!hideFooter && (
|
|
<View className="px-5 pt-3 border-t border-border">
|
|
<Pressable
|
|
onPress={isLast ? onComplete : onNext}
|
|
className={cn(
|
|
"h-12 rounded-[8px] items-center justify-center flex-row gap-2",
|
|
loading ? "bg-primary/60" : "bg-primary",
|
|
)}
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator color="white" size="small" />
|
|
) : (
|
|
<>
|
|
<Text className="text-white font-sans-bold text-xs">
|
|
{isLast ? completeLabel : nextLabel}
|
|
</Text>
|
|
{!isLast && <ArrowRight size={16} color="white" />}
|
|
</>
|
|
)}
|
|
</Pressable>
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|