Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions apps/www/components/sections/testimonial-tweet-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use client"

import type { KeyboardEvent, MouseEvent, ReactNode } from "react"
import Link from "next/link"
import { ArrowUpRight } from "lucide-react"

import { Button } from "@/components/ui/button"

interface TestimonialTweetLinkProps {
children: ReactNode
tweetUrl: string
}

const interactiveSelector =
'a, button, input, select, textarea, summary, [role="button"], [role="link"]'

const isInteractiveTarget = (
target: EventTarget | null,
container: Element
) => {
if (!(target instanceof Element)) {
return false
}

const interactiveElement = target.closest(interactiveSelector)

return interactiveElement !== null && interactiveElement !== container
}

export function TestimonialTweetLink({
children,
tweetUrl,
}: TestimonialTweetLinkProps) {
const navigateToTweet = () => {
window.location.assign(tweetUrl)
}

const handleClick = (event: MouseEvent<HTMLDivElement>) => {
if (isInteractiveTarget(event.target, event.currentTarget)) {
return
}

navigateToTweet()
}

const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
if (event.target !== event.currentTarget) {
return
}

if (event.key !== "Enter" && event.key !== " ") {
return
}

event.preventDefault()
navigateToTweet()
}

return (
<div
className="group relative block contain-layout"
onClick={handleClick}
onKeyDown={handleKeyDown}
role="link"
tabIndex={0}
>
{children}
<div className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-lg bg-black/5 opacity-0 backdrop-blur-sm transition-opacity duration-200 ease-in-out will-change-[opacity] group-hover:pointer-events-auto group-hover:opacity-100">
<Button
asChild
variant="default"
size="default"
className="pointer-events-auto h-8 w-fit translate-y-3 px-2 transition-transform duration-200 ease-in-out will-change-transform group-hover:translate-y-0"
>
<Link href={tweetUrl}>
View Tweet
<ArrowUpRight className="h-4 w-4" />
</Link>
</Button>
</div>
</div>
)
}
22 changes: 4 additions & 18 deletions apps/www/components/sections/testimonials.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import Link from "next/link"
import { ArrowUpRight } from "lucide-react"

import { Button } from "@/components/ui/button"
import { ExpandableMasonarySection } from "@/components/sections/expandable-masonary-section"
import { TestimonialTweetLink } from "@/components/sections/testimonial-tweet-link"
import { TweetCard } from "@/registry/magicui/tweet-card"

const allTweets = [
Expand Down Expand Up @@ -64,26 +61,15 @@ export function Testimonials() {
</h2>
<ExpandableMasonarySection>
{allTweets.map((id) => (
<Link
href={`https://x.com/i/status/${id}`}
<TestimonialTweetLink
key={id}
className="group relative block contain-layout"
tweetUrl={`https://x.com/i/status/${id}`}
>
<TweetCard
id={id}
className="border-border bg-card break-inside-avoid overflow-hidden rounded-xl border transition-[border-color,background-color,box-shadow] duration-200 ease-in-out hover:shadow-md"
/>
<div className="absolute inset-0 flex items-center justify-center rounded-lg bg-black/5 opacity-0 backdrop-blur-sm transition-opacity duration-200 ease-in-out will-change-[opacity] group-hover:opacity-100">
<Button
variant="default"
size="default"
className="pointer-events-none h-8 w-fit translate-y-3 px-2 transition-transform duration-200 ease-in-out will-change-transform group-hover:translate-y-0"
>
View Tweet
<ArrowUpRight className="h-4 w-4" />
</Button>
</div>
</Link>
</TestimonialTweetLink>
))}
</ExpandableMasonarySection>
</section>
Expand Down
Loading