74 lines
1.9 KiB
Plaintext
74 lines
1.9 KiB
Plaintext
|
---
|
||
|
interface Props {
|
||
|
category: string;
|
||
|
apps: Array<{
|
||
|
name: string;
|
||
|
link: string;
|
||
|
icon: string;
|
||
|
alt: string;
|
||
|
tags?: string[];
|
||
|
}>;
|
||
|
}
|
||
|
|
||
|
const { category, apps } = Astro.props;
|
||
|
import ServiceCard from "../common/ServiceCard.astro";
|
||
|
|
||
|
// Pre-compute values during server-side rendering
|
||
|
const categoryId = `category-${category.toLowerCase().replace(/\s+/g, '-')}`;
|
||
|
const categoryLower = category.toLowerCase();
|
||
|
---
|
||
|
|
||
|
<div class="mb-8 category-section" data-category={categoryLower} x-data="{ open: true }">
|
||
|
<button
|
||
|
@click="open = !open"
|
||
|
class="text-xl font-semibold mb-4 w-full text-left flex items-center justify-between"
|
||
|
aria-expanded="true"
|
||
|
:aria-expanded="open.toString()"
|
||
|
aria-controls={categoryId}
|
||
|
>
|
||
|
{category}
|
||
|
<svg
|
||
|
:class="{ 'rotate-180': open }"
|
||
|
class="w-5 h-5 transform transition-transform duration-300"
|
||
|
fill="none"
|
||
|
stroke="currentColor"
|
||
|
viewBox="0 0 24 24"
|
||
|
>
|
||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||
|
</svg>
|
||
|
</button>
|
||
|
|
||
|
<div
|
||
|
x-show="open"
|
||
|
x-transition
|
||
|
id={categoryId}
|
||
|
>
|
||
|
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
|
||
|
{apps.length > 0 ? (
|
||
|
apps.map(app => {
|
||
|
const appName = app.name.toLowerCase();
|
||
|
const appTags = app.tags ? app.tags.join(' ').toLowerCase() : '';
|
||
|
|
||
|
return (
|
||
|
<div
|
||
|
class="app-card transition-all duration-300"
|
||
|
data-app-name={appName}
|
||
|
data-app-tags={appTags}
|
||
|
data-app-category={categoryLower}
|
||
|
>
|
||
|
<ServiceCard
|
||
|
name={app.name}
|
||
|
href={app.link}
|
||
|
img={app.icon}
|
||
|
alt={app.name}
|
||
|
/>
|
||
|
</div>
|
||
|
);
|
||
|
})
|
||
|
) : (
|
||
|
<p class="text-center col-span-full">Coming soon...</p>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|