feat(ui): redesign testimonial slider to manual snap carousel with equal card heights
This commit is contained in:
@@ -1,51 +1,31 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React from 'react'
|
||||
import './TestimonialSlider.css'
|
||||
|
||||
/**
|
||||
* Auto-rotating testimonial slider with quotes.
|
||||
* Testimonials are passed as props (from content/meta/testimonials.yaml later).
|
||||
* Manual testimonial slider (carousel).
|
||||
* Equal heights, horizontal scroll, no autoplay.
|
||||
*/
|
||||
export default function TestimonialSlider({ testimonials = [] }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (testimonials.length <= 1) return
|
||||
const interval = setInterval(() => {
|
||||
setActiveIndex(prev => (prev + 1) % testimonials.length)
|
||||
}, 6000)
|
||||
return () => clearInterval(interval)
|
||||
}, [testimonials.length])
|
||||
|
||||
if (testimonials.length === 0) return null
|
||||
|
||||
const current = testimonials[activeIndex]
|
||||
|
||||
return (
|
||||
<section className="testimonials-section app-container">
|
||||
<div className="testimonial-card glass-panel">
|
||||
<div className="testimonial-quote">
|
||||
<span className="quote-mark">"</span>
|
||||
<p>{current.quote}</p>
|
||||
</div>
|
||||
<div className="testimonial-author">
|
||||
{current.photo && <img src={current.photo} alt={current.name} className="author-photo" />}
|
||||
<div>
|
||||
<strong>{current.name}</strong>
|
||||
<span>{current.role}</span>
|
||||
<div className="testimonial-scroll-container">
|
||||
{testimonials.map((current, i) => (
|
||||
<div className="testimonial-card glass-panel" key={i}>
|
||||
<div className="testimonial-quote">
|
||||
<span className="quote-mark">"</span>
|
||||
<p>{current.quote}</p>
|
||||
</div>
|
||||
<div className="testimonial-author">
|
||||
{current.photo && <img src={current.photo} alt={current.name} className="author-photo" loading="lazy" />}
|
||||
<div>
|
||||
<strong>{current.name}</strong>
|
||||
<span>{current.role}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{testimonials.length > 1 && (
|
||||
<div className="testimonial-dots">
|
||||
{testimonials.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`dot ${i === activeIndex ? 'active' : ''}`}
|
||||
onClick={() => setActiveIndex(i)}
|
||||
aria-label={`Testimonial ${i + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user