68 lines
2.2 KiB
Svelte
68 lines
2.2 KiB
Svelte
<script lang="ts">
|
|
import Calendar from "$lib/components/ui/calendar/calendar.svelte";
|
|
import CalendarDay from "$lib/components/ui/calendar/calendar-day.svelte";
|
|
import { CalendarDate, type DateValue } from "@internationalized/date";
|
|
|
|
let {
|
|
value = $bindable<CalendarDate | undefined>(
|
|
new CalendarDate(2026, 2, 1),
|
|
),
|
|
} = $props();
|
|
|
|
let placeholder = $state<CalendarDate | undefined>(undefined);
|
|
let imageMap = $state<Map<string, string>>(new Map());
|
|
|
|
let displayedMonth = $derived(placeholder ?? value);
|
|
|
|
$effect(() => {
|
|
if (displayedMonth) {
|
|
const month = `${displayedMonth.year}-${String(displayedMonth.month).padStart(2, "0")}`;
|
|
fetchMonthImages(month);
|
|
}
|
|
});
|
|
|
|
async function fetchMonthImages(month: string) {
|
|
try {
|
|
const res = await fetch(`/api/entry?month=${month}`);
|
|
const entries = await res.json();
|
|
const newMap = new Map<string, string>();
|
|
for (const entry of entries) {
|
|
if (entry.image) {
|
|
const d = new Date(entry.date);
|
|
const key = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
|
|
newMap.set(key, entry.image);
|
|
}
|
|
}
|
|
imageMap = newMap;
|
|
} catch (err) {
|
|
console.error("Failed to fetch month images:", err);
|
|
}
|
|
}
|
|
|
|
function getImageForDate(day: DateValue): string | null {
|
|
const key = `${day.year}-${day.month}-${day.day}`;
|
|
return imageMap.get(key) ?? null;
|
|
}
|
|
</script>
|
|
|
|
<Calendar
|
|
type="single"
|
|
bind:value
|
|
bind:placeholder
|
|
class="rounded-lg border shadow-sm md:[--cell-size:--spacing(14)]"
|
|
>
|
|
{#snippet day({ day })}
|
|
<CalendarDay>
|
|
{@const img = getImageForDate(day)}
|
|
{#if img}
|
|
<img
|
|
src={img}
|
|
alt=""
|
|
class="absolute inset-0 h-full w-full rounded-sm object-cover hover:opacity-20"
|
|
/>
|
|
{/if}
|
|
<span class="relative z-10">{day.day}</span>
|
|
</CalendarDay>
|
|
{/snippet}
|
|
</Calendar>
|