aboutsummaryrefslogtreecommitdiff
path: root/src/pages/blog
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/blog')
-rw-r--r--src/pages/blog/[id].astro61
-rw-r--r--src/pages/blog/index.astro265
2 files changed, 312 insertions, 14 deletions
diff --git a/src/pages/blog/[id].astro b/src/pages/blog/[id].astro
index 65c8a25..935a796 100644
--- a/src/pages/blog/[id].astro
+++ b/src/pages/blog/[id].astro
@@ -61,7 +61,68 @@ const schema = {
<time datetime={entry.data.publishedAt.toISOString()}>
{formattedDate}
</time>
+ {entry.data.tags && entry.data.tags.length > 0 && (
+ <>
+ • Etiquetas:
+ <ul class="tags">
+ {entry.data.tags.map((tag: string) => (
+ <li><a href={`/blog/?tag=${encodeURIComponent(tag)}`}>{tag}</a></li>
+ ))}
+ </ul>
+ </>
+ )}
</small>
<Content />
</Layout>
+
+<style lang="scss">
+ @use "../../../styles/variables" as v;
+ @use "sass:color";
+
+ .tags {
+ display: inline-flex;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ gap: 0.75rem;
+ }
+
+ .tags li {
+ display: inline;
+ }
+
+ .tags a {
+ // Estilo de enlace normal, siguiendo los estilos predefinidos en Layout.astro
+ color: v.$accentDark;
+ font-size: 0.85rem;
+ font-family: v.$monoFontStack;
+ text-decoration: none;
+ box-shadow: 0 1px v.$accent;
+ transition: all 0.2s ease;
+
+ &:hover {
+ box-shadow: 0 2px v.$accentDark;
+ }
+
+ &:focus {
+ color: v.$accentDark;
+ outline: none;
+ background-color: v.$secondary;
+ box-shadow: 0 4px #0b0c0c;
+ }
+ }
+
+ /* Estilos para la información de la publicación */
+ small {
+ display: block;
+ margin-top: -1rem;
+ margin-bottom: 1.5rem;
+ font-size: 0.85rem;
+ color: color.adjust(v.$dark, $lightness: 30%);
+ }
+
+ time {
+ font-style: italic;
+ }
+</style>
diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro
index 68ad02e..b74781b 100644
--- a/src/pages/blog/index.astro
+++ b/src/pages/blog/index.astro
@@ -6,6 +6,7 @@ const blogCollection = (await getCollection("blog")).sort((a, b) => {
return b.data.publishedAt.getTime() - a.data.publishedAt.getTime();
});
+// Agrupar artículos por fecha
const groupedPosts = blogCollection.reduce(
(acc: Record<string, any[]>, post) => {
const year = post.data.publishedAt.getFullYear();
@@ -20,6 +21,9 @@ const groupedPosts = blogCollection.reduce(
{},
);
+// Colección de todas las etiquetas únicas
+const allTags = [...new Set(blogCollection.flatMap(post => post.data.tags || []))].sort();
+
function humaniseDate(date: Date) {
const result = date.toLocaleDateString("es-ES", {
month: "long",
@@ -64,18 +68,251 @@ const schema = {
son mías, y no representan a ninguna empresa o institución.
</p>
- {
- Object.entries(groupedPosts).map(([key, posts]) => (
- <section>
- <h2>{humaniseDate(new Date(key))}</h2>
- <ul>
- {posts.map((post) => (
- <li>
- <a href={`/blog/${post.id}`}>{post.data.title}</a>
- </li>
- ))}
- </ul>
- </section>
- ))
- }
+ {allTags.length > 0 && (
+ <div class="tags-container">
+ <h2>Etiquetas</h2>
+ <div class="tag-filter">
+ <button class="tag-button active" data-tag="all">Todas</button>
+ {allTags.map((tag) => (
+ <button class="tag-button" data-tag={tag}>{tag}</button>
+ ))}
+ </div>
+ </div>
+ )}
+
+ <div id="blog-posts">
+ {
+ Object.entries(groupedPosts).map(([key, posts]) => (
+ <section class="post-section" data-date={key}>
+ <h2>{humaniseDate(new Date(key))}</h2>
+ <ul>
+ {posts.map((post) => {
+ const postTags = post.data.tags || [];
+ const tagsAttribute = postTags.join(',');
+ return (
+ <li class="post-item" data-tags={tagsAttribute}>
+ <a href={`/blog/${post.id}`}>{post.data.title}</a>
+ {postTags.length > 0 && (
+ <ul class="post-tags">
+ {postTags.map((tag: string) => (
+ <li>
+ <button class="tag-link" data-tag={tag}>
+ {tag}
+ </button>
+ </li>
+ ))}
+ </ul>
+ )}
+ </li>
+ );
+ })}
+ </ul>
+ </section>
+ ))
+ }
+ </div>
+
+ <script>
+ // Script para el filtrado de artículos por etiqueta
+ document.addEventListener('DOMContentLoaded', () => {
+ const tagButtons = document.querySelectorAll('.tag-button');
+ const tagLinks = document.querySelectorAll('.tag-link');
+ const postItems = document.querySelectorAll('.post-item');
+ const postSections = document.querySelectorAll('.post-section');
+
+ // Función para filtrar los artículos por etiqueta
+ function filterByTag(tag: string) {
+ // Primero, restablecer la visibilidad de todos los elementos
+ postItems.forEach(item => {
+ (item as HTMLElement).style.display = '';
+ });
+ postSections.forEach(section => {
+ (section as HTMLElement).style.display = '';
+ });
+
+ // Si no es 'todas', filtrar por etiqueta
+ if (tag !== 'all') {
+ postItems.forEach(item => {
+ const itemEl = item as HTMLElement;
+ const tagsAttr = itemEl.dataset.tags || '';
+ const itemTags = tagsAttr ? tagsAttr.split(',') : [];
+ if (!itemTags.includes(tag)) {
+ itemEl.style.display = 'none';
+ }
+ });
+
+ // Ocultar secciones que no tienen artículos visibles
+ postSections.forEach(section => {
+ const items = section.querySelectorAll('.post-item');
+ let allHidden = true;
+
+ items.forEach(item => {
+ if ((item as HTMLElement).style.display !== 'none') {
+ allHidden = false;
+ }
+ });
+
+ if (allHidden) {
+ (section as HTMLElement).style.display = 'none';
+ }
+ });
+ }
+
+ // Actualizar estado activo de los botones
+ tagButtons.forEach(button => {
+ if ((button as HTMLElement).dataset.tag === tag) {
+ button.classList.add('active');
+ } else {
+ button.classList.remove('active');
+ }
+ });
+
+ // Actualizar URL con el parámetro de consulta
+ if (tag === 'all') {
+ history.replaceState(null, document.title, window.location.pathname);
+ } else {
+ history.replaceState(null, document.title, `?tag=${encodeURIComponent(tag)}`);
+ }
+
+ // Añadir un log para depuración
+ console.log(`Filtrado por etiqueta: ${tag}`);
+ console.log(`Artículos visibles: ${document.querySelectorAll('.post-item:not([style*="display: none"])')?.length || 0}`);
+ }
+
+ // Eventos de clic para los botones de etiquetas
+ tagButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ const tag = (button as HTMLElement).dataset.tag;
+ if (tag) filterByTag(tag);
+ });
+ });
+
+ // Eventos de clic para los enlaces de etiquetas dentro de los posts
+ tagLinks.forEach(link => {
+ link.addEventListener('click', (e) => {
+ e.preventDefault();
+ const tag = (link as HTMLElement).dataset.tag;
+ if (tag) filterByTag(tag);
+
+ // Desplazamiento suave hacia arriba para ver todos los resultados
+ const tagsContainer = document.querySelector('.tags-container');
+ if (tagsContainer) {
+ window.scrollTo({
+ top: (tagsContainer as HTMLElement).offsetTop - 20,
+ behavior: 'smooth'
+ });
+ }
+ });
+ });
+
+ // Verificar si hay un parámetro de consulta para filtrar
+ const urlParams = new URLSearchParams(window.location.search);
+ const tagParam = urlParams.get('tag');
+
+ if (tagParam) {
+ const tagExists = Array.from(tagButtons).some(button =>
+ (button as HTMLElement).dataset.tag === tagParam
+ );
+ if (tagExists) {
+ filterByTag(tagParam);
+ }
+ }
+ });
+ </script>
</Layout>
+
+<style lang="scss">
+ @use "../../../styles/variables" as v;
+ @use "sass:color";
+
+ .tags-container {
+ margin-bottom: 2rem;
+ }
+
+ .tag-filter {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .tag-button {
+ padding: 0.25rem 0.6rem;
+ background-color: v.$light;
+ color: v.$accent;
+ border: 1px solid v.$accent;
+ border-radius: 1.5rem;
+ font-size: 0.85rem;
+ font-family: v.$monoFontStack;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ box-shadow: v.$shadow;
+
+ &:hover {
+ background-color: color.adjust(v.$accent, $lightness: 45%);
+ color: v.$accentDark;
+ transform: translateY(-1px);
+ }
+
+ &.active {
+ background-color: v.$accent;
+ color: v.$lightAlt;
+ border-color: v.$accentDark;
+ }
+ }
+
+ .post-tags {
+ display: inline-flex;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ gap: 0.25rem;
+ margin-left: 0.5rem;
+ }
+
+ .post-tags li {
+ display: inline;
+ }
+
+ .tag-link {
+ display: inline-block;
+ padding: 0.1rem 0.4rem;
+ background-color: color.adjust(v.$background, $lightness: -3%);
+ color: v.$accentDark;
+ border: none;
+ border-radius: 1rem;
+ font-size: 0.75rem;
+ font-family: v.$monoFontStack;
+ text-decoration: none;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background-color: color.adjust(v.$accent, $lightness: 45%);
+ color: v.$accentDark;
+ transform: translateY(-1px);
+ }
+ }
+
+ /* Efecto de transición para el filtrado */
+ .post-item {
+ transition: all 0.3s ease;
+ }
+
+ .post-section {
+ transition: opacity 0.3s ease;
+ }
+
+ #blog-posts {
+ min-height: 200px;
+ }
+
+ /* Mejora el aspecto de los enlaces de posts */
+ .post-item {
+ margin-bottom: 0.5rem;
+
+ a {
+ font-weight: 500;
+ }
+ }
+</style>