Add full project source

This commit is contained in:
Vladimír Duša
2026-03-11 09:11:08 +01:00
parent 51e3984077
commit 110e2dd471
35 changed files with 1422 additions and 19 deletions

101
src/pages/cs/index.astro Normal file
View File

@@ -0,0 +1,101 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import ArticleTile from '../../components/ArticleTile.astro';
import { getCollection } from 'astro:content';
import { useTranslations } from '../../i18n/ui';
const lang = 'cs';
const t = useTranslations(lang);
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.filter(p => p.id.startsWith('cs/'))
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
const allTags = [...new Set(posts.flatMap(p => p.data.tags))].sort();
---
<BaseLayout title="Vladimír Duša" description="Osobní web Vladimíra Duši" lang={lang} alternateUrl="/en/">
{posts.length === 0 ? (
<div class="px-8 py-16 text-gray-400 text-sm">{t('empty.articles')}</div>
) : (
<div class="px-8 pb-12">
{allTags.length > 0 && (
<div class="tag-filter" id="tag-filter">
<button class="tag-filter-btn active" data-tag="all">{t('filter.all')}</button>
{allTags.map((tag) => (
<>
<span class="tag-filter-sep">·</span>
<button class="tag-filter-btn" data-tag={tag}>{tag}</button>
</>
))}
</div>
)}
<div class="article-grid">
{posts.map((post, i) => {
const baseSlug = post.id.replace(/^cs\//, '').replace(/\.md$/, '');
return (
<ArticleTile
title={post.data.title}
href={`/cs/clanky/${baseSlug}`}
image={post.data.image}
description={post.data.description}
tags={post.data.tags}
class={i % 6 === 0 ? 'tile-featured-left' : i % 6 === 5 ? 'tile-featured-right' : 'tile-regular'}
eager={i < 3}
/>
);
})}
</div>
</div>
)}
</BaseLayout>
<script>
const buttons = document.querySelectorAll<HTMLButtonElement>('.tag-filter-btn');
const tiles = document.querySelectorAll<HTMLAnchorElement>('.article-tile');
function applyFilter(tag: string) {
buttons.forEach(btn => btn.classList.toggle('active', btn.dataset.tag === tag));
tiles.forEach(tile => {
const tileTags = (tile.dataset.tags ?? '').split(',').filter(Boolean);
const matches = tag === 'all' || tileTags.includes(tag);
tile.classList.toggle('is-filtered', !matches);
});
const url = new URL(window.location.href);
if (tag === 'all') url.searchParams.delete('tag');
else url.searchParams.set('tag', tag);
history.replaceState(null, '', url.toString());
}
const initialTag = new URLSearchParams(window.location.search).get('tag') ?? 'all';
applyFilter(initialTag);
buttons.forEach(btn => {
btn.addEventListener('click', () => applyFilter(btn.dataset.tag ?? 'all'));
});
</script>
<style>
.article-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 260px;
gap: 10px;
}
@media (max-width: 768px) {
.article-grid {
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 200px;
}
}
@media (max-width: 480px) {
.article-grid {
grid-template-columns: 1fr;
grid-auto-rows: 220px;
}
}
</style>