🚸 feat(calendar): auto-scroll to current hour and hide tasks by default

- Hide tasks in calendar by default on app load (not persisted from sessions)
- Auto-scroll to current hour when loading DayView, WeekView, MultiDayView
- Center current hour in viewport for immediate visibility
- Exclude task/sidebar settings from cloud sync to ensure clean initial state
This commit is contained in:
Till-JS 2026-02-13 22:05:35 +01:00
parent bba696e241
commit 2200ed64e8
4 changed files with 94 additions and 3 deletions

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { viewStore } from '$lib/stores/view.svelte';
import { eventsStore } from '$lib/stores/events.svelte';
import { calendarsStore } from '$lib/stores/calendars.svelte';
@ -124,6 +125,30 @@
let dragPreviewHeight = $state(0);
let dayColumnRef = $state<HTMLElement | null>(null);
// Scroll to current hour on mount
onMount(() => {
const scrollContainer = dayColumnRef?.parentElement;
if (!scrollContainer) return;
// Use CSS variable for hour height (default 48px)
const hourHeight =
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--hour-height')) ||
48;
const currentHour = new Date().getHours();
const viewportHeight = scrollContainer.clientHeight;
// Calculate scroll position to center current hour in viewport
// Account for firstVisibleHour (if hour filtering is enabled)
const effectiveHour = Math.max(0, currentHour - firstVisibleHour);
const scrollPosition = effectiveHour * hourHeight - viewportHeight / 2 + hourHeight / 2;
// Scroll to position (instant, not smooth, for initial load)
scrollContainer.scrollTo({
top: Math.max(0, scrollPosition),
behavior: 'instant',
});
});
// ============================================================================
// Resize State
// ============================================================================

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { viewStore } from '$lib/stores/view.svelte';
import { eventsStore } from '$lib/stores/events.svelte';
import { calendarsStore } from '$lib/stores/calendars.svelte';
@ -147,6 +148,30 @@
// Reference to the days container for position calculations
let daysContainerEl: HTMLDivElement;
// Scroll to current hour on mount
onMount(() => {
const scrollContainer = daysContainerEl?.parentElement;
if (!scrollContainer) return;
// Use CSS variable for hour height (default 48px)
const hourHeight =
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--hour-height')) ||
48;
const currentHour = new Date().getHours();
const viewportHeight = scrollContainer.clientHeight;
// Calculate scroll position to center current hour in viewport
// Account for firstVisibleHour (if hour filtering is enabled)
const effectiveHour = Math.max(0, currentHour - firstVisibleHour);
const scrollPosition = effectiveHour * hourHeight - viewportHeight / 2 + hourHeight / 2;
// Scroll to position (instant, not smooth, for initial load)
scrollContainer.scrollTo({
top: Math.max(0, scrollPosition),
behavior: 'instant',
});
});
function getEventsForDay(day: Date): CalendarEvent[] {
return getVisibleTimedEvents(
eventsStore.getEventsForDay(day),

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { viewStore } from '$lib/stores/view.svelte';
import { eventsStore } from '$lib/stores/events.svelte';
import { calendarsStore } from '$lib/stores/calendars.svelte';
@ -147,6 +148,32 @@
// Reference to the days container for position calculations
let daysContainerEl: HTMLDivElement;
// Reference to the time grid (scroll container)
let timeGridEl: HTMLDivElement;
// Scroll to current hour on mount
onMount(() => {
if (!timeGridEl) return;
// Use CSS variable for hour height (default 48px)
const hourHeight =
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--hour-height')) ||
48;
const currentHour = new Date().getHours();
const viewportHeight = timeGridEl.clientHeight;
// Calculate scroll position to center current hour in viewport
// Account for firstVisibleHour (if hour filtering is enabled)
const effectiveHour = Math.max(0, currentHour - firstVisibleHour);
const scrollPosition = effectiveHour * hourHeight - viewportHeight / 2 + hourHeight / 2;
// Scroll to position (instant, not smooth, for initial load)
timeGridEl.scrollTo({
top: Math.max(0, scrollPosition),
behavior: 'instant',
});
});
// Birthday Popover (using composable)
const birthdayPopover = useBirthdayPopover();
@ -933,7 +960,7 @@
</div>
<!-- Time grid -->
<div class="time-grid scrollbar-thin">
<div class="time-grid scrollbar-thin" bind:this={timeGridEl}>
<!-- Time column -->
<div class="time-column">
{#each hours as hour}

View file

@ -100,7 +100,7 @@ const DEFAULT_SETTINGS: CalendarAppSettings = {
showBirthdays: true,
showBirthdayAge: true,
showTasksInCalendar: false,
sidebarCollapsed: false,
sidebarCollapsed: true,
quickViewPillViews: ['week', 'month', 'agenda'],
customDayCount: 30,
defaultEventDuration: 60,
@ -131,6 +131,13 @@ const baseStore = createAppSettingsStore<CalendarAppSettings>(
}
);
// Always start with tasks/sidebar hidden when the app loads
// (don't persist this from previous sessions)
if (browser) {
baseStore.set('showTasksInCalendar', false);
baseStore.set('sidebarCollapsed', true);
}
// Load settings from cloud
function loadFromCloud(): Partial<CalendarAppSettings> | null {
if (!userSettings.loaded) return null;
@ -266,7 +273,14 @@ export const settingsStore = {
if (!initialSyncDone) {
const cloudSettings = loadFromCloud();
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
baseStore.update(cloudSettings);
// Exclude showTasksInCalendar and sidebarCollapsed from cloud sync
// - always start with tasks hidden and sidebar collapsed
const {
showTasksInCalendar: _,
sidebarCollapsed: __,
...settingsWithoutTasks
} = cloudSettings;
baseStore.update(settingsWithoutTasks);
} else {
syncToCloud(baseStore.settings);
}