Initial commit

This commit is contained in:
Rami Bitar
2026-06-03 13:58:11 -04:00
commit 47b773444e
125 changed files with 16971 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { ComponentConfig } from "@reacteditor/core";
import { imageField } from "@reacteditor/plugin-media/field";
import { Award } from "lucide-react";
import { Logos, type LogosProps } from "@/components/logos/logos";
import { frontendAiMediaAdapter } from "@/services/media-adapter";
export const logosEditor: ComponentConfig<LogosProps> = {
label: "Press / Logos",
icon: <Award size={16} />,
category: "content",
defaultProps: {
tagline: "As seen in",
layout: "row",
items: [
{ src: "https://logo.clearbit.com/vogue.com", alt: "Vogue" },
{ src: "https://logo.clearbit.com/highsnobiety.com", alt: "Highsnobiety" },
{ src: "https://logo.clearbit.com/gq.com", alt: "GQ" },
{ src: "https://logo.clearbit.com/dezeen.com", alt: "Dezeen" },
{ src: "https://logo.clearbit.com/wallpaper.com", alt: "Wallpaper*" },
],
},
fields: {
tagline: { label: "Tagline", type: "text", contentEditable: true },
layout: {
label: "Layout",
type: "radio",
options: [
{ label: "Row", value: "row" },
{ label: "Marquee", value: "marquee" },
],
},
items: {
label: "Logos",
type: "array",
defaultItemProps: { src: "", alt: "" },
getItemSummary: (it) => it?.alt || "Logo",
arrayFields: {
src: { label: "Image", ...imageField({ adapter: frontendAiMediaAdapter }) },
alt: { label: "Alt text", type: "text", contentEditable: true },
},
},
},
render: (props) => <Logos {...props} />,
};

View File

@@ -0,0 +1,47 @@
import { Container } from "@/components/layout/Container";
import { Typography } from "@/components/Typography";
export type LogosProps = {
tagline: string;
items: Array<{ src: string; alt: string }>;
layout: "row" | "marquee";
};
export function Logos({ tagline, items, layout }: LogosProps) {
return (
<section className="border-y border-border bg-muted/40 py-12">
<Container>
{tagline ? (
<Typography variant="caption" className="mb-8 text-center">
{tagline}
</Typography>
) : null}
{layout === "marquee" ? (
<div className="overflow-hidden">
<div className="flex animate-[marquee_30s_linear_infinite] gap-16 [--gap:4rem]">
{[...items, ...items].map((it, i) => (
<img
key={i}
src={it.src}
alt={it.alt}
className="h-7 w-auto opacity-60 grayscale"
/>
))}
</div>
</div>
) : (
<div className="flex flex-wrap items-center justify-center gap-x-12 gap-y-6">
{items.map((it, i) => (
<img
key={i}
src={it.src}
alt={it.alt}
className="h-7 w-auto opacity-60 grayscale"
/>
))}
</div>
)}
</Container>
</section>
);
}