fooder-app/src/lib/components/diary/CalendarPicker.svelte
2026-04-01 23:51:16 +02:00

91 lines
2.7 KiB
Svelte

<script lang="ts">
import { today } from '$lib/utils/date';
interface Props {
selected: string;
onSelect: (date: string) => void;
}
let { selected, onSelect }: Props = $props();
const todayStr = today();
let viewYear = $state(parseInt(selected.slice(0, 4)));
let viewMonth = $state(parseInt(selected.slice(5, 7)) - 1); // 0-based
function prevMonth() {
if (viewMonth === 0) { viewYear--; viewMonth = 11; }
else viewMonth--;
}
function nextMonth() {
if (viewMonth === 11) { viewYear++; viewMonth = 0; }
else viewMonth++;
}
const monthLabel = $derived(
new Date(viewYear, viewMonth, 1).toLocaleDateString(undefined, { month: 'long', year: 'numeric' })
);
function iso(year: number, month: number, day: number): string {
return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}
const cells = $derived.by(() => {
const firstDay = new Date(viewYear, viewMonth, 1);
const startDow = (firstDay.getDay() + 6) % 7; // Mon = 0
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
const result: (string | null)[] = [];
for (let i = 0; i < startDow; i++) result.push(null);
for (let d = 1; d <= daysInMonth; d++) result.push(iso(viewYear, viewMonth, d));
return result;
});
</script>
<div>
<div class="flex items-center justify-between mb-4">
<button
onclick={prevMonth}
class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-zinc-800 transition-colors"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
<span class="font-medium text-sm">{monthLabel}</span>
<button
onclick={nextMonth}
class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-zinc-800 transition-colors"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
<div class="grid grid-cols-7 mb-1">
{#each ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] as h}
<div class="text-center text-xs text-zinc-600 py-1">{h}</div>
{/each}
</div>
<div class="grid grid-cols-7 gap-y-1">
{#each cells as cell}
{#if cell === null}
<div></div>
{:else}
<button
onclick={() => onSelect(cell)}
class="aspect-square w-full rounded-full text-sm flex items-center justify-center transition-colors
{cell === selected
? 'bg-green-600 text-white font-semibold'
: cell === todayStr
? 'ring-1 ring-green-500 text-green-400'
: 'hover:bg-zinc-800 text-zinc-300'}"
>
{parseInt(cell.slice(8))}
</button>
{/if}
{/each}
</div>
</div>