pattanshetti / src /components /StickyBottomBar.tsx
triflix's picture
Upload 99 files
4be2b2b verified
import { ReactNode } from "react";
import { Button } from "@/components/ui/button";
import { ChevronUp, ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
import { useState } from "react";
interface StickyBottomBarProps {
summary: ReactNode;
actions: ReactNode;
expandable?: boolean;
expandedContent?: ReactNode;
className?: string;
}
export function StickyBottomBar({
summary,
actions,
expandable = false,
expandedContent,
className
}: StickyBottomBarProps) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className={cn(
"fixed bottom-0 left-0 right-0 z-30",
"bg-card border-t shadow-2xl",
"safe-area-inset-bottom",
"md:relative md:shadow-none md:rounded-lg md:border",
className
)}>
{/* Expanded Content */}
{expandable && isExpanded && expandedContent && (
<div className="px-4 py-4 border-b max-h-64 overflow-auto">
{expandedContent}
</div>
)}
{/* Main Bar */}
<div className="px-4 py-3 flex items-center justify-between gap-4">
{/* Summary */}
<div className="flex-1 min-w-0">
{summary}
</div>
{/* Actions */}
<div className="flex items-center gap-2 shrink-0">
{actions}
</div>
{/* Expand Toggle */}
{expandable && expandedContent && (
<Button
variant="ghost"
size="icon"
onClick={() => setIsExpanded(!isExpanded)}
className="h-10 w-10 shrink-0"
>
{isExpanded ? (
<ChevronDown className="h-5 w-5" />
) : (
<ChevronUp className="h-5 w-5" />
)}
</Button>
)}
</div>
</div>
);
}
// Preset variations for common use cases
interface FormBottomBarProps {
totalLabel: string;
totalValue: string | number;
saveLabel?: string;
onSave: () => void;
cancelLabel?: string;
onCancel?: () => void;
saveDisabled?: boolean;
expandable?: boolean;
breakdown?: Array<{ label: string; value: string | number }>;
}
export function FormBottomBar({
totalLabel,
totalValue,
saveLabel = "जतन करा",
onSave,
cancelLabel,
onCancel,
saveDisabled = false,
expandable = false,
breakdown
}: FormBottomBarProps) {
return (
<StickyBottomBar
summary={
<div className="space-y-1">
<div className="text-xs text-muted-foreground">{totalLabel}</div>
<div className="text-2xl font-bold truncate">
{typeof totalValue === 'number'
? `₹${totalValue.toLocaleString('en-IN')}`
: totalValue
}
</div>
</div>
}
actions={
<>
{cancelLabel && onCancel && (
<Button
variant="outline"
size="lg"
onClick={onCancel}
className="h-12 px-6 md:flex"
>
{cancelLabel}
</Button>
)}
<Button
size="lg"
onClick={onSave}
disabled={saveDisabled}
className="h-12 px-8 font-semibold"
>
{saveLabel}
</Button>
</>
}
expandable={expandable}
expandedContent={
breakdown && (
<div className="space-y-2">
<div className="font-semibold text-sm mb-3">तपशील</div>
{breakdown.map((item, index) => (
<div key={index} className="flex justify-between text-sm">
<span className="text-muted-foreground">{item.label}</span>
<span className="font-semibold">
{typeof item.value === 'number'
? `₹${item.value.toLocaleString('en-IN')}`
: item.value
}
</span>
</div>
))}
</div>
)
}
/>
);
}
// Simple action bar
interface ActionBottomBarProps {
primaryAction: {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: "default" | "destructive";
};
secondaryAction?: {
label: string;
onClick: () => void;
};
}
export function ActionBottomBar({
primaryAction,
secondaryAction
}: ActionBottomBarProps) {
return (
<div className={cn(
"fixed bottom-0 left-0 right-0 z-30",
"bg-card border-t shadow-2xl p-4",
"safe-area-inset-bottom",
"flex gap-3"
)}>
{secondaryAction && (
<Button
variant="outline"
size="lg"
onClick={secondaryAction.onClick}
className="flex-1 h-12 font-semibold"
>
{secondaryAction.label}
</Button>
)}
<Button
variant={primaryAction.variant || "default"}
size="lg"
onClick={primaryAction.onClick}
disabled={primaryAction.disabled}
className={cn(
"h-12 font-semibold",
secondaryAction ? "flex-1" : "w-full"
)}
>
{primaryAction.label}
</Button>
</div>
);
}
// Info bar with single action
interface InfoBottomBarProps {
icon?: ReactNode;
title: string;
description?: string;
action: {
label: string;
onClick: () => void;
icon?: ReactNode;
};
}
export function InfoBottomBar({
icon,
title,
description,
action
}: InfoBottomBarProps) {
return (
<div className={cn(
"fixed bottom-0 left-0 right-0 z-30",
"bg-card border-t shadow-2xl p-4",
"safe-area-inset-bottom",
"flex items-center gap-4"
)}>
{icon && (
<div className="shrink-0 text-primary">
{icon}
</div>
)}
<div className="flex-1 min-w-0">
<div className="font-semibold truncate">{title}</div>
{description && (
<div className="text-sm text-muted-foreground truncate">
{description}
</div>
)}
</div>
<Button
size="lg"
onClick={action.onClick}
className="h-12 px-6 shrink-0"
>
{action.icon && <span className="mr-2">{action.icon}</span>}
{action.label}
</Button>
</div>
);
}