Files
lbfalvy.com/src/components/BlogList.svelte

82 lines
2.9 KiB
Svelte

<script lang="ts">
import type { CollectionEntry } from "astro:content";
import { onMount } from "svelte";
import { SvelteMap } from "svelte/reactivity";
import { parseTime, printTime } from "../utils/time";
import Time from "./Time.svelte";
interface Props {
posts: CollectionEntry<"blog">[];
}
let { posts }: Props = $props();
let allTags = $derived([...new Set(posts.flatMap(item => item.data.tags))]);
let selectedTags = $state<SvelteMap<string, boolean>>();
onMount(() => {
const url = new URL(window.location.href);
const tagsQry = url.searchParams.get('tags');
const defaultTags = tagsQry ? new Set(decodeURIComponent(tagsQry).split('+')) : new Set();
selectedTags = new SvelteMap(allTags.map(tag => [tag, defaultTags.has(tag)]));
})
$effect(() => {
if (!selectedTags) return;
const newTagsQry = encodeURIComponent(
[...selectedTags.entries()].filter(([_, v]) => v).map(([k, _]) => k).join('+')
);
const url = new URL(window.location.href);
if (newTagsQry == url.searchParams.get("tags")) return;
if (newTagsQry == "") url.searchParams.delete("tags");
else url.searchParams.set("tags", newTagsQry);
window.history.pushState(null, "", url.toString());
})
let isFiltered = $derived(selectedTags && [...selectedTags.values()].includes(true));
let filteredPosts = $derived(isFiltered
? posts.filter(p => !p.data.unlisted && p.data.tags.some(t => selectedTags!.get(t)))
: posts.filter(p => !p.data.unlisted));
let shownPosts = $derived(filteredPosts.toReversed())
</script>
<div class="lg:flex flex-row-reverse justify-between items-start">
<div class="m-2 p-2 bg-side-bg lg:float-right lg:max-w-80 lg:mr-4">
<span class="inline-block m-0.5 pl-2 italic">filter:</span>
{#each allTags as tag}
<button
onclick={() => {
console.log("TRoggled tag")
selectedTags!.set(tag, !selectedTags!.get(tag))
}}
class={[
"m-0.5 rounded-4xl emph-bg px-2 cursor-pointer border-2 border-solid",
selectedTags?.get(tag) && "border-gray-400"
]}
>
{tag}
</button>
{/each}
</div>
<main class="max-w-[650px]">
{#each shownPosts as post}
<article>
<a href={`/blog/${post.id}`}>
<div class="
lg:grid grid-cols-[auto_min-content_min-content]
m-1 p-2 hover:bg-shade
">
<h2 class="font-bold">{post.data.title}</h2>
<address class="inline-block post-meta col-start-2 md:ml-3 whitespace-nowrap">
{post.data.author}
</address>
<div class="inline-block post-meta col-start-3 lg:ml-1 whitespace-nowrap">
<Time datetime={post.data.pubDate} />
</div>
<div class="col-span-3">{post.data.summary}</div>
</div>
</a>
</article>
{/each}
</main>
</div>