try
This commit is contained in:
parent
7108096ad7
commit
c9e4a0b004
1 changed files with 53 additions and 89 deletions
|
|
@ -23,7 +23,48 @@ type ShowcaseBlockProps = {
|
||||||
items?: ShowcaseItem[]
|
items?: ShowcaseItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMING_SOON_BG = 'bg-muted/50'
|
function ShowcaseImage({ item }: { item: ShowcaseItem }) {
|
||||||
|
const imageUrl = item.image && typeof item.image === 'object' ? item.image.url : null
|
||||||
|
const imageAlt = item.image && typeof item.image === 'object' ? item.image.alt : item.title
|
||||||
|
|
||||||
|
const inner = imageUrl ? (
|
||||||
|
<>
|
||||||
|
<Image src={imageUrl} alt={imageAlt ?? item.title} fill className="object-cover" />
|
||||||
|
{item.imageUrl && (
|
||||||
|
<div className="absolute inset-0 bg-background/0 group-hover:bg-background/20 transition-colors duration-200" />
|
||||||
|
)}
|
||||||
|
{item.imageUrl && (
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||||
|
<i className="ti ti-external-link text-foreground/80" style={{ fontSize: 20 }} aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="absolute inset-0 flex flex-col items-center justify-center gap-2 bg-muted/50">
|
||||||
|
<i className="ti ti-photo-off text-foreground/15" style={{ fontSize: 32 }} aria-hidden="true" />
|
||||||
|
<span className="text-xs text-foreground/20">Coming soon</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (item.imageUrl) {
|
||||||
|
return (
|
||||||
|
|
||||||
|
href={item.imageUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block relative w-full aspect-video overflow-hidden rounded-t-xl group"
|
||||||
|
>
|
||||||
|
{inner}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-full aspect-video overflow-hidden rounded-t-xl">
|
||||||
|
{inner}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function ShowcaseBlock({ heading, subheading, items }: ShowcaseBlockProps) {
|
export function ShowcaseBlock({ heading, subheading, items }: ShowcaseBlockProps) {
|
||||||
if (!Array.isArray(items) || items.length === 0) return null
|
if (!Array.isArray(items) || items.length === 0) return null
|
||||||
|
|
@ -44,93 +85,16 @@ export function ShowcaseBlock({ heading, subheading, items }: ShowcaseBlockProps
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{items.map((item, i) => {
|
{items.map((item, i) => (
|
||||||
const imageUrl =
|
<div
|
||||||
item.image && typeof item.image === 'object' ? item.image.url : null
|
key={i}
|
||||||
const imageAlt =
|
className="bg-muted/50 border-0 rounded-xl overflow-hidden flex flex-col"
|
||||||
item.image && typeof item.image === 'object' ? item.image.alt : item.title
|
>
|
||||||
|
<ShowcaseImage item={item} />
|
||||||
|
|
||||||
const imageContent = imageUrl ? (
|
<div className="p-4 flex flex-col gap-3 flex-1">
|
||||||
<Image
|
<h3 className="text-sm font-medium text-foreground/85">{item.title}</h3>
|
||||||
src={imageUrl}
|
|
||||||
alt={imageAlt ?? item.title}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center gap-2">
|
|
||||||
<i className="ti ti-photo-off text-foreground/15" style={{ fontSize: 32 }} aria-hidden="true" />
|
|
||||||
<span className="text-xs text-foreground/20">Coming soon</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
const imageWrapper = item.imageUrl ? (
|
{item.description && (
|
||||||
|
<p className="text-xs text-foreground/50 leading-relaxed">{item.description}</p>
|
||||||
href={item.imageUrl}
|
)}
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="block relative w-full aspect-video overflow-hidden rounded-t-xl group"
|
|
||||||
>
|
|
||||||
{imageContent}
|
|
||||||
<div className="absolute inset-0 bg-background/0 group-hover:bg-background/20 transition-colors duration-200" />
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
|
||||||
<i className="ti ti-external-link text-foreground/80" style={{ fontSize: 20 }} aria-hidden="true" />
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<div className={`relative w-full aspect-video overflow-hidden rounded-t-xl ${!imageUrl ? COMING_SOON_BG : ''}`}>
|
|
||||||
{imageContent}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="bg-muted/50 border-0 rounded-xl overflow-hidden flex flex-col"
|
|
||||||
>
|
|
||||||
{imageWrapper}
|
|
||||||
|
|
||||||
<div className="p-4 flex flex-col gap-3 flex-1">
|
|
||||||
<h3 className="text-sm font-medium text-foreground/85">{item.title}</h3>
|
|
||||||
|
|
||||||
{item.description && (
|
|
||||||
<p className="text-xs text-foreground/50 leading-relaxed">{item.description}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{Array.isArray(item.tags) && item.tags.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-1.5">
|
|
||||||
{item.tags.map(({ tag }, j) => (
|
|
||||||
<span
|
|
||||||
key={j}
|
|
||||||
className="text-xs text-foreground/50 bg-background/50 border border-foreground/10 rounded-full px-2.5 py-0.5"
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{Array.isArray(item.links) && item.links.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-3 mt-auto pt-2 border-t border-foreground/8">
|
|
||||||
{item.links.map((link, k) => (
|
|
||||||
|
|
||||||
key={k}
|
|
||||||
href={link.url}
|
|
||||||
target={link.newTab ? '_blank' : '_self'}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="flex items-center gap-1.5 text-xs text-foreground/50 hover:text-foreground/80 transition-colors"
|
|
||||||
>
|
|
||||||
<i className="ti ti-external-link" style={{ fontSize: 13 }} aria-hidden="true" />
|
|
||||||
{link.label}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue