139 lines
5.8 KiB
TypeScript
139 lines
5.8 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Progress } from "@/components/ui/progress";
|
|
import { X, ArrowLeft, ArrowRight } from "lucide-react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import { usePage } from "@inertiajs/react";
|
|
import { Link } from "@inertiajs/react";
|
|
|
|
const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
|
const { sekilasInfo } = usePage().props as any;
|
|
const [isVisible, setIsVisible] = useState(true);
|
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
|
|
const progressValue = ((currentSlide + 1) / sekilasInfo.length) * 100;
|
|
|
|
const slides = sekilasInfo.map((info: any) => ({
|
|
title: info.JudulPost,
|
|
image: `/storage/${info.ImagePost}`,
|
|
description:
|
|
info.DescPost.replace(/<[^>]*>/g, "").slice(0, 160) + "...",
|
|
slug: info.SlugPost,
|
|
}));
|
|
|
|
const nextSlide = () => {
|
|
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
|
};
|
|
|
|
const prevSlide = () => {
|
|
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
|
};
|
|
|
|
const handleClose = () => {
|
|
setIsVisible(false);
|
|
setTimeout(onClose, 500);
|
|
};
|
|
|
|
return (
|
|
<AnimatePresence mode="wait">
|
|
{isVisible && sekilasInfo.length > 0 && (
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{
|
|
opacity: 1,
|
|
transition: {
|
|
duration: 0.3,
|
|
delay: 0.1,
|
|
},
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
transition: {
|
|
duration: 0.3,
|
|
},
|
|
}}
|
|
className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50 container md:max-w-none"
|
|
>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -50 }}
|
|
animate={{
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: {
|
|
type: "spring",
|
|
damping: 70,
|
|
stiffness: 500,
|
|
delay: 0.2,
|
|
duration: 0.5,
|
|
},
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
y: 50,
|
|
transition: {
|
|
type: "spring",
|
|
damping: 25,
|
|
stiffness: 300,
|
|
duration: 0.4,
|
|
},
|
|
}}
|
|
className="bg-white p-6 rounded-lg relative md:max-w-2xl mx-auto w-full h-50 md:h-5/6 flex flex-col justify-between md:justify-around"
|
|
>
|
|
<button
|
|
className="absolute top-2 right-2"
|
|
onClick={handleClose}
|
|
>
|
|
<X className="w-6 h-6 text-gray-700" />
|
|
</button>
|
|
<h2 className="md:text-md text-base font-bold text-center mb-4">
|
|
{slides[currentSlide].title}
|
|
</h2>
|
|
<div className="relative">
|
|
<img
|
|
src={slides[currentSlide].image}
|
|
alt="Popup Slide Image"
|
|
className="w-full h-full md:h-96 object-contain mb-4"
|
|
/>
|
|
<button
|
|
onClick={prevSlide}
|
|
className="absolute left-2 top-1/2 transform -translate-y-1/2 p-2 bg-white bg-opacity-50 rounded-full"
|
|
>
|
|
<ArrowLeft className="w-6 h-6 text-gray-700" />
|
|
</button>
|
|
<button
|
|
onClick={nextSlide}
|
|
className="absolute right-2 top-1/2 transform -translate-y-1/2 p-2 bg-white bg-opacity-50 rounded-full"
|
|
>
|
|
<ArrowRight className="w-6 h-6 text-gray-700" />
|
|
</button>
|
|
</div>
|
|
<div className="w-full mt-4">
|
|
<Progress
|
|
value={progressValue}
|
|
className="w-full"
|
|
/>
|
|
<div className="text-sm text-gray-500 text-center mt-2">
|
|
{currentSlide + 1} / {slides.length}
|
|
</div>
|
|
</div>
|
|
<p className="text-sm text-gray-600 text-center mb-4 overflow-hidden text-ellipsis whitespace-nowrap">
|
|
{slides[currentSlide].description}
|
|
</p>
|
|
<div className="flex justify-center">
|
|
<Link
|
|
href={`/sekilasinfo/${slides[currentSlide].slug}`}
|
|
>
|
|
<Button className="bg-green-800 text-white px-6 py-2 rounded-lg">
|
|
Selengkapnya
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
};
|
|
|
|
export default PopupModal;
|