mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-29 07:01:44 +00:00
Sync uncommitted changes across bot-docs, cloud, and docs apps. Add SDK documentation and project registry.
101 lines
4.4 KiB
TypeScript
101 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
|
|
interface Testimonial {
|
|
quote: string;
|
|
author: string;
|
|
url: string;
|
|
}
|
|
|
|
export function TestimonialCarousel({ testimonials }: { testimonials: Testimonial[] }) {
|
|
const half = Math.ceil(testimonials.length / 2);
|
|
const topRow = testimonials.slice(0, half);
|
|
const bottomRow = testimonials.slice(half);
|
|
|
|
const row1 = useMemo(() => [...topRow, ...topRow], [topRow]);
|
|
const row2 = useMemo(() => [...bottomRow, ...bottomRow], [bottomRow]);
|
|
|
|
const pixelsPerItem = 336;
|
|
const pixelsPerSecond = 50;
|
|
const duration1 = (topRow.length * pixelsPerItem) / pixelsPerSecond;
|
|
const duration2 = (bottomRow.length * pixelsPerItem) / pixelsPerSecond;
|
|
|
|
return (
|
|
<section className="mb-14 overflow-hidden -mx-6 px-6">
|
|
<div className="flex items-center justify-between mb-5 px-6">
|
|
<h2 className="text-xl font-semibold flex items-center gap-2.5">
|
|
<span className="text-brand font-bold">⟩</span> What People Say
|
|
</h2>
|
|
<a href="/shoutouts" className="text-sm text-brand font-medium hover:text-[#00e5cc] transition-colors">
|
|
View all →
|
|
</a>
|
|
</div>
|
|
<div
|
|
className="flex flex-col gap-2 -mx-6 overflow-hidden"
|
|
style={{
|
|
maskImage: 'linear-gradient(to right, transparent, black 5%, black 95%, transparent)',
|
|
WebkitMaskImage: 'linear-gradient(to right, transparent, black 5%, black 95%, transparent)',
|
|
}}
|
|
>
|
|
<div
|
|
className="flex gap-4 w-max py-2 animate-[scroll-left_var(--duration)_linear_infinite] hover:[animation-play-state:paused]"
|
|
style={{ '--duration': `${duration1}s` } as React.CSSProperties}
|
|
>
|
|
{row1.map((t, i) => (
|
|
<a
|
|
key={`r1-${i}`}
|
|
href={t.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-start gap-3 p-4 min-w-[320px] max-w-[400px] rounded-xl border border-fd-border bg-[rgba(10,15,26,0.7)] backdrop-blur-sm no-underline text-fd-foreground transition-all duration-250 hover:border-brand hover:-translate-y-0.5 hover:shadow-[0_8px_24px_rgba(255,77,77,0.15)] shrink-0"
|
|
>
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={`https://ui-avatars.com/api/?name=${encodeURIComponent(t.author)}&background=fd4444&color=fff&size=88&bold=true`}
|
|
alt={t.author}
|
|
className="w-11 h-11 rounded-full shrink-0 border-2 border-fd-border bg-fd-muted"
|
|
loading="lazy"
|
|
/>
|
|
<div className="flex flex-col gap-1.5 min-w-0">
|
|
<p className="text-sm leading-snug text-fd-muted-foreground line-clamp-3">
|
|
“{t.quote}”
|
|
</p>
|
|
<span className="text-xs font-semibold text-brand">@{t.author}</span>
|
|
</div>
|
|
</a>
|
|
))}
|
|
</div>
|
|
<div
|
|
className="flex gap-4 w-max py-2 animate-[scroll-right_var(--duration)_linear_infinite] hover:[animation-play-state:paused]"
|
|
style={{ '--duration': `${duration2}s` } as React.CSSProperties}
|
|
>
|
|
{row2.map((t, i) => (
|
|
<a
|
|
key={`r2-${i}`}
|
|
href={t.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-start gap-3 p-4 min-w-[320px] max-w-[400px] rounded-xl border border-fd-border bg-[rgba(10,15,26,0.7)] backdrop-blur-sm no-underline text-fd-foreground transition-all duration-250 hover:border-brand hover:-translate-y-0.5 hover:shadow-[0_8px_24px_rgba(255,77,77,0.15)] shrink-0"
|
|
>
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={`https://ui-avatars.com/api/?name=${encodeURIComponent(t.author)}&background=fd4444&color=fff&size=88&bold=true`}
|
|
alt={t.author}
|
|
className="w-11 h-11 rounded-full shrink-0 border-2 border-fd-border bg-fd-muted"
|
|
loading="lazy"
|
|
/>
|
|
<div className="flex flex-col gap-1.5 min-w-0">
|
|
<p className="text-sm leading-snug text-fd-muted-foreground line-clamp-3">
|
|
“{t.quote}”
|
|
</p>
|
|
<span className="text-xs font-semibold text-brand">@{t.author}</span>
|
|
</div>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|