22
33import { useRef , useState , useEffect } from "react" ;
44import Autoplay from "embla-carousel-autoplay" ;
5- import { Award } from "lucide-react" ;
5+ import { Sparkles } from "lucide-react" ;
66import {
77 Carousel ,
88 CarouselContent ,
@@ -13,6 +13,7 @@ import {
1313} from "@/components/ui/carousel" ;
1414import { Skeleton } from "@/components/ui/skeleton" ;
1515import JargonCard from "@/components/jargon/jargon-card" ;
16+ import { cn } from "@/lib/utils" ;
1617
1718export type FeaturedJargonCarouselItem = {
1819 id : string ;
@@ -26,8 +27,10 @@ export type FeaturedJargonCarouselItem = {
2627
2728export default function FeaturedJargonCarousel ( {
2829 featuredJargons,
30+ className,
2931} : {
3032 featuredJargons : FeaturedJargonCarouselItem [ ] ;
33+ className ?: string ;
3134} ) {
3235 const plugin = useRef ( Autoplay ( { delay : 2000 , stopOnInteraction : true } ) ) ;
3336 const [ api , setApi ] = useState < CarouselApi > ( ) ;
@@ -53,70 +56,86 @@ export default function FeaturedJargonCarousel({
5356 } , [ api ] ) ;
5457
5558 return (
56- < div className = "flex flex-col gap-3" >
57- < h2 className = "flex items-center gap-2 text-lg font-bold" >
58- < Award className = "h-5 w-5" />
59- 하이라이트
60- </ h2 >
61- < Carousel
62- setApi = { setApi }
63- plugins = { [ plugin . current ] }
64- className = "w-full"
65- onMouseEnter = { plugin . current . stop }
66- onMouseLeave = { plugin . current . reset }
67- >
68- < CarouselContent className = "-ml-4" >
69- { featuredJargons . length > 0
70- ? featuredJargons . map ( ( jargon ) => (
71- < CarouselItem
72- key = { jargon . id }
73- className = "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4"
74- >
75- < JargonCard
76- jargon = { {
77- id : jargon . id ,
78- name : jargon . name ,
79- slug : jargon . slug ,
80- translations : jargon . translation
81- ? [ jargon . translation ]
82- : [ ] ,
83- categories : jargon . categories ,
84- commentCount : jargon . comment_count ,
85- updatedAt : jargon . updated_at ,
86- } }
87- />
88- </ CarouselItem >
89- ) )
90- : Array . from ( { length : 8 } ) . map ( ( _ , index ) => (
91- < CarouselItem
92- key = { index }
93- className = "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4"
94- >
95- < div className = "bg-card text-card-foreground flex h-full flex-col gap-1 rounded-md p-3" >
96- { /* Category chips skeleton */ }
97- < div className = "flex flex-wrap gap-2" >
98- < Skeleton className = "h-5 w-12 rounded-full" />
99- < Skeleton className = "h-5 w-10 rounded-full" />
100- </ div >
101-
102- { /* Name skeleton */ }
103- < Skeleton className = "h-5 w-3/4" />
59+ < div
60+ className = { cn (
61+ "relative rounded-xl border border-stone-300/50 bg-stone-100/30 p-4 dark:border-stone-700/40 dark:bg-stone-800/20" ,
62+ className ,
63+ ) }
64+ >
65+ < div className = "flex flex-col gap-3" >
66+ < h2 className = "flex items-center gap-2 text-lg font-bold" >
67+ < span className = "text-stone-500 dark:text-stone-400" >
68+ < Sparkles className = "h-4 w-4" />
69+ </ span >
70+ < span className = "text-stone-800 dark:text-stone-100" > 하이라이트</ span >
71+ </ h2 >
72+ < Carousel
73+ setApi = { setApi }
74+ plugins = { [ plugin . current ] }
75+ className = "relative w-full"
76+ onMouseEnter = { plugin . current . stop }
77+ onMouseLeave = { plugin . current . reset }
78+ >
79+ { /* Left fade overlay */ }
80+ { canScrollPrev && (
81+ < div className = "pointer-events-none absolute top-0 left-0 z-10 h-full w-16 bg-gradient-to-r from-[#F6F1E9]/75 to-transparent dark:from-[#1E1B1A]" />
82+ ) }
83+ { /* Right fade overlay */ }
84+ { canScrollNext && (
85+ < div className = "pointer-events-none absolute top-0 right-0 z-10 h-full w-16 bg-gradient-to-l from-[#F6F1E9]/75 to-transparent dark:from-[#1E1B1A]" />
86+ ) }
87+ < CarouselContent className = "-ml-3" >
88+ { featuredJargons . length > 0
89+ ? featuredJargons . map ( ( jargon ) => (
90+ < CarouselItem
91+ key = { jargon . id }
92+ className = "basis-full pl-3 md:basis-1/2 lg:basis-1/3 xl:basis-1/4"
93+ >
94+ < JargonCard
95+ jargon = { {
96+ id : jargon . id ,
97+ name : jargon . name ,
98+ slug : jargon . slug ,
99+ translations : jargon . translation
100+ ? [ jargon . translation ]
101+ : [ ] ,
102+ categories : jargon . categories ,
103+ commentCount : jargon . comment_count ,
104+ updatedAt : jargon . updated_at ,
105+ } }
106+ compact
107+ />
108+ </ CarouselItem >
109+ ) )
110+ : Array . from ( { length : 8 } ) . map ( ( _ , index ) => (
111+ < CarouselItem
112+ key = { index }
113+ className = "basis-full pl-3 md:basis-1/2 lg:basis-1/3 xl:basis-1/4"
114+ >
115+ < div className = "bg-card text-card-foreground flex h-full flex-col gap-1 rounded-md p-3" >
116+ { /* Name skeleton */ }
117+ < Skeleton className = "h-5 w-3/4" />
104118
105- { /* Translation skeleton */ }
106- < Skeleton className = "h-4 w-full" />
119+ { /* Translation skeleton */ }
120+ < Skeleton className = "h-4 w-full" />
107121
108- { /* Footer skeleton (comment count + timestamp) */ }
109- < div className = "mt-auto flex gap-1 self-end" >
110- < Skeleton className = "h-4 w-12" />
111- < Skeleton className = "h-4 w-16" />
122+ { /* Footer skeleton (comment count + timestamp) */ }
123+ < div className = "mt-auto flex gap-1 self-end" >
124+ < Skeleton className = "h-4 w-12" />
125+ < Skeleton className = "h-4 w-16" />
126+ </ div >
112127 </ div >
113- </ div >
114- </ CarouselItem >
115- ) ) }
116- </ CarouselContent >
117- { canScrollPrev && < CarouselPrevious className = "left-1" /> }
118- { canScrollNext && < CarouselNext className = "right-1" /> }
119- </ Carousel >
128+ </ CarouselItem >
129+ ) ) }
130+ </ CarouselContent >
131+ { canScrollPrev && (
132+ < CarouselPrevious className = "left-2 z-20 border-stone-200 bg-white hover:bg-stone-50 dark:border-stone-700 dark:bg-stone-900 dark:hover:bg-stone-800" />
133+ ) }
134+ { canScrollNext && (
135+ < CarouselNext className = "right-2 z-20 border-stone-200 bg-white hover:bg-stone-50 dark:border-stone-700 dark:bg-stone-900 dark:hover:bg-stone-800" />
136+ ) }
137+ </ Carousel >
138+ </ div >
120139 </ div >
121140 ) ;
122141}
0 commit comments