File size: 4,592 Bytes
c09f67c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | "use client";
import Link from "next/link";
import { AppLogo } from "@/components/app-logo";
import { apps } from "@/data/apps";
interface IntegrationsSectionProps {
title?: string;
subtitle?: string;
}
// Split apps into two rows for animation
const midpoint = Math.ceil(apps.length / 2);
const row1Apps = apps.slice(0, midpoint);
const row2Apps = apps.slice(midpoint);
function IntegrationPill({
id,
name,
slug,
}: {
id: string;
name: string;
slug: string;
}) {
return (
<Link
href={`/integrations/${slug}`}
className="flex items-center gap-2 px-3 py-1.5 rounded-full border border-border bg-background whitespace-nowrap hover:border-foreground/20 transition-colors"
>
<div className="w-4 h-4 flex-shrink-0">
<AppLogo appId={id} />
</div>
<span className="font-sans text-sm text-foreground">{name}</span>
</Link>
);
}
export function IntegrationsSection({
title = "Works with the tools you already use",
subtitle = "Connect your banks, email, payments and accounting software in minutes.",
}: IntegrationsSectionProps) {
return (
<section className="bg-background py-12 sm:py-16 lg:py-24">
<div className="max-w-[1400px] mx-auto">
<div className="text-center space-y-4 mb-10">
<h2 className="font-serif text-2xl sm:text-2xl text-foreground">
{title}
</h2>
<p className="hidden sm:block font-sans text-base text-muted-foreground leading-normal">
{subtitle}
</p>
</div>
{/* Animated pill rows */}
<div className="relative overflow-hidden group/integrations">
{/* Gradient fade masks */}
<div
className="absolute inset-y-0 left-0 w-24 sm:w-32 z-10 pointer-events-none"
style={{
background:
"linear-gradient(to right, hsl(var(--background)) 0%, hsl(var(--background)) 30%, hsla(var(--background), 0.8) 50%, hsla(var(--background), 0.4) 70%, transparent 100%)",
}}
/>
<div
className="absolute inset-y-0 right-0 w-24 sm:w-32 z-10 pointer-events-none"
style={{
background:
"linear-gradient(to left, hsl(var(--background)) 0%, hsl(var(--background)) 30%, hsla(var(--background), 0.8) 50%, hsla(var(--background), 0.4) 70%, transparent 100%)",
}}
/>
<div className="space-y-3">
{/* Row 1 - moves left */}
<div className="flex animate-marquee-left group-hover/integrations:[animation-play-state:paused] will-change-transform">
<div className="flex gap-2 shrink-0 pr-2">
{row1Apps.map((app) => (
<IntegrationPill
key={app.id}
id={app.id}
name={app.name}
slug={app.slug}
/>
))}
</div>
<div className="flex gap-2 shrink-0 pr-2" aria-hidden="true">
{row1Apps.map((app) => (
<IntegrationPill
key={`dup-${app.id}`}
id={app.id}
name={app.name}
slug={app.slug}
/>
))}
</div>
</div>
{/* Row 2 - moves right */}
<div className="flex animate-marquee-right group-hover/integrations:[animation-play-state:paused] will-change-transform">
<div className="flex gap-2 shrink-0 pr-2">
{row2Apps.map((app) => (
<IntegrationPill
key={app.id}
id={app.id}
name={app.name}
slug={app.slug}
/>
))}
</div>
<div className="flex gap-2 shrink-0 pr-2" aria-hidden="true">
{row2Apps.map((app) => (
<IntegrationPill
key={`dup-${app.id}`}
id={app.id}
name={app.name}
slug={app.slug}
/>
))}
</div>
</div>
</div>
</div>
<div className="text-center mt-8">
<Link
href="/integrations"
className="font-sans text-sm text-muted-foreground hover:text-foreground transition-colors underline underline-offset-4"
>
View all integrations
</Link>
</div>
</div>
</section>
);
}
|