feat: update fixing glitch swiper spit

This commit is contained in:
hanif salafi 2025-09-10 12:05:12 +07:00
parent b7a2b445a9
commit 78497bfcb1
1 changed files with 102 additions and 40 deletions

View File

@ -47,6 +47,7 @@ import {
// Swiper
import { Swiper, SwiperSlide } from "swiper/react";
import { Swiper as SwiperType } from "swiper";
import "swiper/css";
import "swiper/css/free-mode";
import "swiper/css/navigation";
@ -206,7 +207,7 @@ export default function FormConvertSPIT() {
// <-- changed: detailThumb is now FileType[] (objects from API) -->
const [detailThumb, setDetailThumb] = useState<FileType[]>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperType | null>(null);
const [files, setFiles] = useState<FileType[]>([]);
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [articleIds, setArticleIds] = useState<string[]>([]);
@ -229,6 +230,13 @@ export default function FormConvertSPIT() {
checkUserPermissions();
}, [userLevelId, roleId]);
// Handle Swiper re-initialization when detailThumb changes
useEffect(() => {
if (thumbsSwiper && detailThumb.length > 0) {
thumbsSwiper.update();
}
}, [detailThumb, thumbsSwiper]);
const initializeComponent = async () => {
try {
setIsLoading(true);
@ -282,7 +290,10 @@ export default function FormConvertSPIT() {
try {
const response = await getTagsBySubCategoryId(categoryId);
if (response?.data?.data?.length > 0) {
const tagsMerge = [...tags, response?.data?.data];
// Extract tag names from the response objects
const apiTags = response?.data?.data?.map((tag: any) => tag.tagName);
// Merge with existing tags, ensuring uniqueness
const tagsMerge = Array.from(new Set([...tags, ...apiTags]));
setTags(tagsMerge);
}
} catch (error) {
@ -332,16 +343,16 @@ export default function FormConvertSPIT() {
(details as any).contentRewriteDescription || ""
);
// Set category and load category tags
if (details.categoryId) {
setSelectedCategoryId(details.categoryId);
await loadTags(details.categoryId);
}
// Set content tags and merge with category tags
if (details.contentTag) {
const initialTags = details.contentTag
.split(",")
.map((tag: string) => tag.trim());
setTags(initialTags);
const contentTags = details.contentTag.split(",").map((tag: string) => tag.trim());
setTags(prev => Array.from(new Set([...prev, ...contentTags])));
}
} catch (error) {
console.error("Failed to load detail:", error);
@ -544,7 +555,6 @@ export default function FormConvertSPIT() {
const getPlacementData = () => {
const placementData: PlacementData[] = [];
console.log("filePlacements : ", filePlacements);
for (let i = 0; i < filePlacements.length; i++) {
if (filePlacements[i].length > 0) {
const placements = filePlacements[i];
@ -950,68 +960,114 @@ export default function FormConvertSPIT() {
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-4">
<div className="space-y-4 overflow-hidden">
<Swiper
key={`main-swiper-${detailThumb.length}`}
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
navigation={true}
className="w-full h-96"
spaceBetween={10}
loop={false}
watchSlidesProgress={true}
allowTouchMove={true}
resistance={true}
resistanceRatio={0.85}
preventClicks={false}
preventClicksPropagation={false}
slidesPerView={1}
centeredSlides={true}
>
{detailThumb.map((item) => (
<SwiperSlide key={item.contentId}>
<SwiperSlide key={item.contentId} className="!w-full">
{item.contentType === "VIDEO" ? (
<div className="relative max-h-screen overflow-hidden">
<div className="w-full max-h-screen aspect-video">
<div className="w-full h-full object-contain">
{/* main video player */}
<div className="relative w-full h-96 overflow-hidden rounded-lg">
<video
className="object-contain h-full w-full rounded-lg"
className="w-full h-full object-contain rounded-lg"
src={item.contentFile}
controls
// playsInline to better on mobile
playsInline
// you can set poster if available: poster={item.thumbnailFileUrl}
poster={item.thumbnailFileUrl || undefined}
title={`Video ${item.contentId}`}
onError={(e) => {
console.error('Video load error:', e);
e.currentTarget.style.display = 'none';
}}
/>
</div>
</div>
</div>
) : (
<div className="w-full h-96 flex items-center justify-center overflow-hidden rounded-lg">
<img
src={item.contentFile}
alt={`Media ${item.contentId}`}
className="w-full h-full object-cover rounded-lg"
className="max-w-full max-h-full object-contain rounded-lg"
style={{ maxWidth: '100%', maxHeight: '100%' }}
onError={(e) => {
console.error('Image load error:', e);
e.currentTarget.style.display = 'none';
}}
/>
</div>
)}
</SwiperSlide>
))}
</Swiper>
<Swiper
key={`thumbs-swiper-${detailThumb.length}`}
onSwiper={setThumbsSwiper}
slidesPerView={8}
spaceBetween={8}
modules={[Pagination, Thumbs]}
freeMode={true}
watchSlidesProgress={true}
modules={[FreeMode, Thumbs]}
breakpoints={{
320: {
slidesPerView: 3,
spaceBetween: 4,
},
640: {
slidesPerView: 5,
spaceBetween: 6,
},
768: {
slidesPerView: 6,
spaceBetween: 8,
},
1024: {
slidesPerView: 8,
spaceBetween: 8,
},
}}
className="w-full"
loop={false}
allowTouchMove={true}
resistance={true}
resistanceRatio={0.85}
preventClicks={false}
preventClicksPropagation={false}
>
{detailThumb.map((item) => (
<SwiperSlide key={`thumb-${item.contentId}`}>
<SwiperSlide key={`thumb-${item.contentId}`} className="!w-auto flex-shrink-0">
{item.contentType === "VIDEO" ? (
<div className="relative w-full h-16 rounded cursor-pointer overflow-hidden">
<div className="relative w-20 h-16 rounded cursor-pointer overflow-hidden flex-shrink-0">
{/* use preload metadata so browser doesn't download full video */}
<video
src={item.contentFile}
className="w-full h-16 object-cover"
className="w-full h-full object-cover"
muted
preload="metadata"
playsInline
// no controls in thumbnail
tabIndex={-1}
onError={(e) => {
console.error('Thumbnail video load error:', e);
e.currentTarget.style.display = 'none';
}}
/>
<div className="absolute inset-0 flex items-center justify-center bg-black/30 pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6 text-white"
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 24 24"
>
@ -1020,11 +1076,17 @@ export default function FormConvertSPIT() {
</div>
</div>
) : (
<div className="w-20 h-16 rounded cursor-pointer overflow-hidden flex-shrink-0">
<img
src={item.contentFile}
alt={`Thumbnail ${item.contentId}`}
className="w-full h-16 object-cover rounded cursor-pointer"
className="w-full h-full object-cover"
onError={(e) => {
console.error('Thumbnail image load error:', e);
e.currentTarget.style.display = 'none';
}}
/>
</div>
)}
</SwiperSlide>
))}