Initial react editor website template

This commit is contained in:
Rami Bitar
2026-08-29 14:03:50 -04:00
commit 3e072967e6
61 changed files with 7041 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
import { isValidElement } from 'react';
export type TestimonialsProps = {
heading?: string;
testimonials?: Array<{ quote: string; name: string; role: string; avatarUrl?: string }>;
};
export default function Testimonials({ heading = '', testimonials = [] }: TestimonialsProps) {
return (
<section id="testimonials" className="scroll-mt-20 bg-background py-20 sm:py-28">
<div className="section-shell">
<div className="max-w-2xl">
<h2 className="text-4xl font-normal leading-[0.98] tracking-[-0.04em] sm:text-5xl lg:text-6xl">{heading}</h2>
</div>
<div className="mt-12 grid gap-4 lg:grid-cols-3">
{testimonials.map((testimonial, index) => (
<figure key={index} className="flex min-h-72 flex-col pt-7">
<blockquote className="text-xl font-normal leading-8 tracking-[-0.02em]">“{testimonial.quote}”</blockquote>
<figcaption className="mt-auto flex items-center gap-3 pt-10">
{testimonial.avatarUrl ? <img src={testimonial.avatarUrl} alt="" className="size-10 rounded-full object-cover" /> : <span aria-hidden="true" className="flex size-10 items-center justify-center rounded-full bg-muted text-sm font-semibold">{getInitial(testimonial.name)}</span>}
<span><span className="block text-sm font-medium">{testimonial.name}</span><span className="block text-xs text-muted-foreground">{testimonial.role}</span></span>
</figcaption>
</figure>
))}
</div>
</div>
</section>
);
}
function getInitial(value: unknown): string {
if (typeof value === 'string' || typeof value === 'number') {
return String(value).trim().charAt(0).toUpperCase() || '•';
}
if (Array.isArray(value)) {
for (const child of value) {
const initial = getInitial(child);
if (initial !== '•') return initial;
}
}
if (isValidElement<{ children?: unknown }>(value)) {
return getInitial(value.props.children);
}
return '•';
}