mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 03:41:10 +02:00
refactor(calendar): extract shared constants and event filtering utilities
Phase 1 of calendar refactoring: - Create calendarConstants.ts with HOUR_HEIGHT_PX, SNAP_INTERVAL_MINUTES, etc. - Create eventFiltering.ts with reusable filter functions: - getVisibleTimedEvents, getVisibleAllDayEvents, getVisibleOverflowEvents - filterByVisibleCalendars, filterByHourRange, getEventMinutes - Update WeekView, DayView, MultiDayView, MonthView to use new utilities - Remove ~200 lines of duplicated filtering logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cdc3cd3ec8
commit
c4fe9ea192
6 changed files with 335 additions and 205 deletions
|
|
@ -13,6 +13,13 @@
|
|||
useCurrentTimeIndicator,
|
||||
} from '$lib/composables/useVisibleHours.svelte';
|
||||
import { toDate } from '$lib/utils/eventDateHelpers';
|
||||
import { HOUR_HEIGHT_PX, SNAP_INTERVAL_MINUTES } from '$lib/utils/calendarConstants';
|
||||
import {
|
||||
getVisibleTimedEvents,
|
||||
getVisibleAllDayEvents,
|
||||
getVisibleOverflowEvents,
|
||||
type OverflowEvents,
|
||||
} from '$lib/utils/eventFiltering';
|
||||
import TaskBlock from './TaskBlock.svelte';
|
||||
import EventContextMenu from '$lib/components/event/EventContextMenu.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -29,9 +36,9 @@
|
|||
|
||||
let { onQuickCreate, onEventClick, onTaskClick }: Props = $props();
|
||||
|
||||
// Constants
|
||||
const HOUR_HEIGHT = 60; // pixels per hour
|
||||
const SNAP_MINUTES = 15; // snap to 15-minute intervals
|
||||
// Use shared constants
|
||||
const HOUR_HEIGHT = HOUR_HEIGHT_PX;
|
||||
const SNAP_MINUTES = SNAP_INTERVAL_MINUTES;
|
||||
|
||||
// Use composables for hour filtering and time indicator
|
||||
const visibleHours = useVisibleHours();
|
||||
|
|
@ -48,75 +55,37 @@
|
|||
let currentTimePosition = $derived(minutesToPercent(timeIndicator.currentMinutes));
|
||||
|
||||
// Get timed events, filtering out those outside visible range when hour filter is enabled
|
||||
let timedEvents = $derived.by(() => {
|
||||
// Filter by visible calendars first
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(viewStore.currentDate)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
|
||||
if (settingsStore.filterHoursEnabled) {
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
return allEvents.filter((event) => {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event overlaps with visible range
|
||||
return eventStartMinutes < visibleEndMinutes && eventEndMinutes > visibleStartMinutes;
|
||||
});
|
||||
}
|
||||
|
||||
return allEvents;
|
||||
});
|
||||
let timedEvents = $derived(
|
||||
getVisibleTimedEvents(
|
||||
eventsStore.getEventsForDay(viewStore.currentDate),
|
||||
calendarsStore.visibleCalendars,
|
||||
{
|
||||
filterHoursEnabled: settingsStore.filterHoursEnabled,
|
||||
dayStartHour: settingsStore.dayStartHour,
|
||||
dayEndHour: settingsStore.dayEndHour,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Get events that are completely outside the visible time range
|
||||
let overflowEvents = $derived.by(() => {
|
||||
let overflowEvents = $derived.by((): OverflowEvents => {
|
||||
if (!settingsStore.filterHoursEnabled) {
|
||||
return { before: [] as CalendarEvent[], after: [] as CalendarEvent[] };
|
||||
return { before: [], after: [] };
|
||||
}
|
||||
|
||||
// Filter by visible calendars
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(viewStore.currentDate)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
const before: CalendarEvent[] = [];
|
||||
const after: CalendarEvent[] = [];
|
||||
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
for (const event of allEvents) {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event ends before visible range starts
|
||||
if (eventEndMinutes <= visibleStartMinutes) {
|
||||
before.push(event);
|
||||
}
|
||||
// Event starts after visible range ends
|
||||
else if (eventStartMinutes >= visibleEndMinutes) {
|
||||
after.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
return { before, after };
|
||||
return getVisibleOverflowEvents(
|
||||
eventsStore.getEventsForDay(viewStore.currentDate),
|
||||
calendarsStore.visibleCalendars,
|
||||
settingsStore.dayStartHour,
|
||||
settingsStore.dayEndHour
|
||||
);
|
||||
});
|
||||
|
||||
let allDayEvents = $derived.by(() => {
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
return eventsStore
|
||||
.getEventsForDay(viewStore.currentDate)
|
||||
.filter((e) => e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
});
|
||||
let allDayEvents = $derived(
|
||||
getVisibleAllDayEvents(
|
||||
eventsStore.getEventsForDay(viewStore.currentDate),
|
||||
calendarsStore.visibleCalendars
|
||||
)
|
||||
);
|
||||
|
||||
// Get display mode for an event (per-event override takes precedence over global setting)
|
||||
function getEventDisplayMode(event: CalendarEvent): 'header' | 'block' {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { filterByVisibleCalendars } from '$lib/utils/eventFiltering';
|
||||
|
||||
import type { CalendarEvent } from '@calendar/shared';
|
||||
|
||||
|
|
@ -194,17 +195,18 @@
|
|||
// ============================================================================
|
||||
// Event Handlers
|
||||
// ============================================================================
|
||||
function getEventsForDay(day: Date) {
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
return eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => visibleCalendarIds.has(e.calendarId))
|
||||
.slice(0, 3); // Max 3 events shown
|
||||
function getEventsForDay(day: Date): CalendarEvent[] {
|
||||
return filterByVisibleCalendars(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars
|
||||
).slice(0, 3); // Max 3 events shown
|
||||
}
|
||||
|
||||
function getAllEventsForDay(day: Date) {
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
return eventsStore.getEventsForDay(day).filter((e) => visibleCalendarIds.has(e.calendarId));
|
||||
function getAllEventsForDay(day: Date): CalendarEvent[] {
|
||||
return filterByVisibleCalendars(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars
|
||||
);
|
||||
}
|
||||
|
||||
function handleDayClick(day: Date, e: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@
|
|||
useCurrentTimeIndicator,
|
||||
} from '$lib/composables/useVisibleHours.svelte';
|
||||
import { toDate } from '$lib/utils/eventDateHelpers';
|
||||
import { HOUR_HEIGHT_PX, SNAP_INTERVAL_MINUTES } from '$lib/utils/calendarConstants';
|
||||
import {
|
||||
getVisibleTimedEvents,
|
||||
getVisibleAllDayEvents,
|
||||
getVisibleOverflowEvents,
|
||||
type OverflowEvents,
|
||||
} from '$lib/utils/eventFiltering';
|
||||
import TaskBlock from './TaskBlock.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import {
|
||||
|
|
@ -27,9 +34,9 @@
|
|||
import { de, enUS, fr, es, it } from 'date-fns/locale';
|
||||
import { locale } from 'svelte-i18n';
|
||||
|
||||
// Constants
|
||||
const HOUR_HEIGHT = 60; // px - should match CSS --hour-height
|
||||
const MINUTES_PER_SLOT = 15; // Snap to 15-minute intervals
|
||||
// Use shared constants
|
||||
const HOUR_HEIGHT = HOUR_HEIGHT_PX;
|
||||
const MINUTES_PER_SLOT = SNAP_INTERVAL_MINUTES;
|
||||
|
||||
import type { CalendarEvent } from '@calendar/shared';
|
||||
|
||||
|
|
@ -119,75 +126,35 @@
|
|||
// Reference to the days container for position calculations
|
||||
let daysContainerEl: HTMLDivElement;
|
||||
|
||||
function getEventsForDay(day: Date) {
|
||||
// Filter by visible calendars first
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
|
||||
// If hour filtering is enabled, only show events that overlap with visible range
|
||||
if (settingsStore.filterHoursEnabled) {
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
return allEvents.filter((event) => {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event overlaps with visible range
|
||||
return eventStartMinutes < visibleEndMinutes && eventEndMinutes > visibleStartMinutes;
|
||||
});
|
||||
}
|
||||
|
||||
return allEvents;
|
||||
function getEventsForDay(day: Date): CalendarEvent[] {
|
||||
return getVisibleTimedEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars,
|
||||
{
|
||||
filterHoursEnabled: settingsStore.filterHoursEnabled,
|
||||
dayStartHour: settingsStore.dayStartHour,
|
||||
dayEndHour: settingsStore.dayEndHour,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Get events that are completely outside the visible time range
|
||||
function getOverflowEventsForDay(day: Date): { before: CalendarEvent[]; after: CalendarEvent[] } {
|
||||
function getOverflowEventsForDay(day: Date): OverflowEvents {
|
||||
if (!settingsStore.filterHoursEnabled) {
|
||||
return { before: [], after: [] };
|
||||
}
|
||||
|
||||
// Filter by visible calendars
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
const before: CalendarEvent[] = [];
|
||||
const after: CalendarEvent[] = [];
|
||||
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
for (const event of allEvents) {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event ends before visible range starts
|
||||
if (eventEndMinutes <= visibleStartMinutes) {
|
||||
before.push(event);
|
||||
}
|
||||
// Event starts after visible range ends
|
||||
else if (eventStartMinutes >= visibleEndMinutes) {
|
||||
after.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
return { before, after };
|
||||
return getVisibleOverflowEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars,
|
||||
settingsStore.dayStartHour,
|
||||
settingsStore.dayEndHour
|
||||
);
|
||||
}
|
||||
|
||||
function getAllDayEventsForDay(day: Date) {
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
return eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
function getAllDayEventsForDay(day: Date): CalendarEvent[] {
|
||||
return getVisibleAllDayEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars
|
||||
);
|
||||
}
|
||||
|
||||
// Get display mode for an event (per-event override takes precedence over global setting)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
useCurrentTimeIndicator,
|
||||
} from '$lib/composables/useVisibleHours.svelte';
|
||||
import { toDate } from '$lib/utils/eventDateHelpers';
|
||||
import { HOUR_HEIGHT_PX, SNAP_INTERVAL_MINUTES } from '$lib/utils/calendarConstants';
|
||||
import {
|
||||
getVisibleTimedEvents,
|
||||
getVisibleAllDayEvents,
|
||||
getVisibleOverflowEvents,
|
||||
type OverflowEvents,
|
||||
} from '$lib/utils/eventFiltering';
|
||||
import TaskBlock from './TaskBlock.svelte';
|
||||
import EventContextMenu from '$lib/components/event/EventContextMenu.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -41,9 +48,9 @@
|
|||
|
||||
let { onQuickCreate, onEventClick, onTaskClick }: Props = $props();
|
||||
|
||||
// Constants
|
||||
const HOUR_HEIGHT = 60; // px - should match CSS --hour-height
|
||||
const MINUTES_PER_SLOT = 15; // Snap to 15-minute intervals
|
||||
// Use shared constants
|
||||
const HOUR_HEIGHT = HOUR_HEIGHT_PX;
|
||||
const MINUTES_PER_SLOT = SNAP_INTERVAL_MINUTES;
|
||||
|
||||
// Get date-fns locale based on current app locale
|
||||
const dateLocales = { de, en: enUS, fr, es, it };
|
||||
|
|
@ -147,75 +154,35 @@
|
|||
selectedBirthday = null;
|
||||
}
|
||||
|
||||
function getEventsForDay(day: Date) {
|
||||
// Filter by visible calendars first
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
|
||||
// If hour filtering is enabled, only show events that overlap with visible range
|
||||
if (settingsStore.filterHoursEnabled) {
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
return allEvents.filter((event) => {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event overlaps with visible range
|
||||
return eventStartMinutes < visibleEndMinutes && eventEndMinutes > visibleStartMinutes;
|
||||
});
|
||||
}
|
||||
|
||||
return allEvents;
|
||||
function getEventsForDay(day: Date): CalendarEvent[] {
|
||||
return getVisibleTimedEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars,
|
||||
{
|
||||
filterHoursEnabled: settingsStore.filterHoursEnabled,
|
||||
dayStartHour: settingsStore.dayStartHour,
|
||||
dayEndHour: settingsStore.dayEndHour,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Get events that are completely outside the visible time range
|
||||
function getOverflowEventsForDay(day: Date): { before: CalendarEvent[]; after: CalendarEvent[] } {
|
||||
function getOverflowEventsForDay(day: Date): OverflowEvents {
|
||||
if (!settingsStore.filterHoursEnabled) {
|
||||
return { before: [], after: [] };
|
||||
}
|
||||
|
||||
// Filter by visible calendars
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
const allEvents = eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => !e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
const before: CalendarEvent[] = [];
|
||||
const after: CalendarEvent[] = [];
|
||||
|
||||
const visibleStartMinutes = settingsStore.dayStartHour * 60;
|
||||
const visibleEndMinutes = settingsStore.dayEndHour * 60;
|
||||
|
||||
for (const event of allEvents) {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
|
||||
const eventStartMinutes = start.getHours() * 60 + start.getMinutes();
|
||||
const eventEndMinutes = end.getHours() * 60 + end.getMinutes();
|
||||
|
||||
// Event ends before visible range starts
|
||||
if (eventEndMinutes <= visibleStartMinutes) {
|
||||
before.push(event);
|
||||
}
|
||||
// Event starts after visible range ends
|
||||
else if (eventStartMinutes >= visibleEndMinutes) {
|
||||
after.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
return { before, after };
|
||||
return getVisibleOverflowEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars,
|
||||
settingsStore.dayStartHour,
|
||||
settingsStore.dayEndHour
|
||||
);
|
||||
}
|
||||
|
||||
function getAllDayEventsForDay(day: Date) {
|
||||
const visibleCalendarIds = new Set(calendarsStore.visibleCalendars.map((c) => c.id));
|
||||
return eventsStore
|
||||
.getEventsForDay(day)
|
||||
.filter((e) => e.isAllDay && visibleCalendarIds.has(e.calendarId));
|
||||
function getAllDayEventsForDay(day: Date): CalendarEvent[] {
|
||||
return getVisibleAllDayEvents(
|
||||
eventsStore.getEventsForDay(day),
|
||||
calendarsStore.visibleCalendars
|
||||
);
|
||||
}
|
||||
|
||||
// Get display mode for an event (per-event override takes precedence over global setting)
|
||||
|
|
|
|||
60
apps/calendar/apps/web/src/lib/utils/calendarConstants.ts
Normal file
60
apps/calendar/apps/web/src/lib/utils/calendarConstants.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Shared calendar constants
|
||||
* Single source of truth for magic numbers used across calendar views
|
||||
*/
|
||||
|
||||
/**
|
||||
* Height of one hour in pixels (should match CSS --hour-height variable)
|
||||
*/
|
||||
export const HOUR_HEIGHT_PX = 60;
|
||||
|
||||
/**
|
||||
* Snap interval for drag/drop and resize operations in minutes
|
||||
*/
|
||||
export const SNAP_INTERVAL_MINUTES = 15;
|
||||
|
||||
/**
|
||||
* Default event duration in minutes when creating quick events
|
||||
*/
|
||||
export const DEFAULT_EVENT_DURATION_MINUTES = 60;
|
||||
|
||||
/**
|
||||
* Minimum event height as percentage of visible hours
|
||||
*/
|
||||
export const MIN_EVENT_HEIGHT_PERCENT = 1.5;
|
||||
|
||||
/**
|
||||
* Maximum number of event dots to show in month view cells
|
||||
*/
|
||||
export const MAX_EVENT_DOTS = 5;
|
||||
|
||||
/**
|
||||
* Days buffer for infinite scroll in date strip
|
||||
*/
|
||||
export const DATE_STRIP_BUFFER_DAYS = 60;
|
||||
|
||||
/**
|
||||
* Default visible hours range
|
||||
*/
|
||||
export const DEFAULT_DAY_START_HOUR = 0;
|
||||
export const DEFAULT_DAY_END_HOUR = 24;
|
||||
|
||||
/**
|
||||
* Week starts on (0 = Sunday, 1 = Monday)
|
||||
*/
|
||||
export const DEFAULT_WEEK_STARTS_ON = 1;
|
||||
|
||||
/**
|
||||
* All constants as a single object for convenient destructuring
|
||||
*/
|
||||
export const CALENDAR_CONSTANTS = {
|
||||
HOUR_HEIGHT_PX,
|
||||
SNAP_INTERVAL_MINUTES,
|
||||
DEFAULT_EVENT_DURATION_MINUTES,
|
||||
MIN_EVENT_HEIGHT_PERCENT,
|
||||
MAX_EVENT_DOTS,
|
||||
DATE_STRIP_BUFFER_DAYS,
|
||||
DEFAULT_DAY_START_HOUR,
|
||||
DEFAULT_DAY_END_HOUR,
|
||||
DEFAULT_WEEK_STARTS_ON,
|
||||
} as const;
|
||||
165
apps/calendar/apps/web/src/lib/utils/eventFiltering.ts
Normal file
165
apps/calendar/apps/web/src/lib/utils/eventFiltering.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Event Filtering Utilities
|
||||
* Reusable functions for filtering calendar events by visibility, time range, etc.
|
||||
*/
|
||||
|
||||
import type { CalendarEvent } from '@calendar/shared';
|
||||
import type { Calendar } from '@calendar/shared';
|
||||
import { toDate } from './eventDateHelpers';
|
||||
|
||||
/**
|
||||
* Create a Set of visible calendar IDs for efficient lookup
|
||||
*/
|
||||
export function getVisibleCalendarIds(visibleCalendars: Calendar[]): Set<string> {
|
||||
return new Set(visibleCalendars.map((c) => c.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter events to only include those from visible calendars
|
||||
*/
|
||||
export function filterByVisibleCalendars(
|
||||
events: CalendarEvent[],
|
||||
visibleCalendars: Calendar[]
|
||||
): CalendarEvent[] {
|
||||
const visibleIds = getVisibleCalendarIds(visibleCalendars);
|
||||
return events.filter((e) => visibleIds.has(e.calendarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter events to only include timed (non-all-day) events
|
||||
*/
|
||||
export function filterTimedEvents(events: CalendarEvent[]): CalendarEvent[] {
|
||||
return events.filter((e) => !e.isAllDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter events to only include all-day events
|
||||
*/
|
||||
export function filterAllDayEvents(events: CalendarEvent[]): CalendarEvent[] {
|
||||
return events.filter((e) => e.isAllDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event time in minutes from midnight
|
||||
*/
|
||||
export function getEventMinutes(event: CalendarEvent): { start: number; end: number } {
|
||||
const start = toDate(event.startTime);
|
||||
const end = toDate(event.endTime);
|
||||
return {
|
||||
start: start.getHours() * 60 + start.getMinutes(),
|
||||
end: end.getHours() * 60 + end.getMinutes(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an event overlaps with a given time range (in minutes from midnight)
|
||||
*/
|
||||
export function eventOverlapsTimeRange(
|
||||
event: CalendarEvent,
|
||||
startMinutes: number,
|
||||
endMinutes: number
|
||||
): boolean {
|
||||
const { start: eventStart, end: eventEnd } = getEventMinutes(event);
|
||||
return eventStart < endMinutes && eventEnd > startMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter timed events that overlap with a visible hour range
|
||||
*/
|
||||
export function filterByHourRange(
|
||||
events: CalendarEvent[],
|
||||
dayStartHour: number,
|
||||
dayEndHour: number
|
||||
): CalendarEvent[] {
|
||||
const startMinutes = dayStartHour * 60;
|
||||
const endMinutes = dayEndHour * 60;
|
||||
return events.filter((event) => eventOverlapsTimeRange(event, startMinutes, endMinutes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type for overflow events
|
||||
*/
|
||||
export interface OverflowEvents {
|
||||
before: CalendarEvent[];
|
||||
after: CalendarEvent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events that are outside the visible hour range
|
||||
* Returns events that end before the visible range starts (before)
|
||||
* and events that start after the visible range ends (after)
|
||||
*/
|
||||
export function getOverflowEvents(
|
||||
events: CalendarEvent[],
|
||||
dayStartHour: number,
|
||||
dayEndHour: number
|
||||
): OverflowEvents {
|
||||
const startMinutes = dayStartHour * 60;
|
||||
const endMinutes = dayEndHour * 60;
|
||||
|
||||
const before: CalendarEvent[] = [];
|
||||
const after: CalendarEvent[] = [];
|
||||
|
||||
for (const event of events) {
|
||||
const { start: eventStart, end: eventEnd } = getEventMinutes(event);
|
||||
|
||||
if (eventEnd <= startMinutes) {
|
||||
before.push(event);
|
||||
} else if (eventStart >= endMinutes) {
|
||||
after.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
return { before, after };
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined filter: Get visible timed events for a day with optional hour filtering
|
||||
*/
|
||||
export function getVisibleTimedEvents(
|
||||
events: CalendarEvent[],
|
||||
visibleCalendars: Calendar[],
|
||||
options?: {
|
||||
filterHoursEnabled?: boolean;
|
||||
dayStartHour?: number;
|
||||
dayEndHour?: number;
|
||||
}
|
||||
): CalendarEvent[] {
|
||||
let filtered = filterByVisibleCalendars(events, visibleCalendars);
|
||||
filtered = filterTimedEvents(filtered);
|
||||
|
||||
if (
|
||||
options?.filterHoursEnabled &&
|
||||
options.dayStartHour !== undefined &&
|
||||
options.dayEndHour !== undefined
|
||||
) {
|
||||
filtered = filterByHourRange(filtered, options.dayStartHour, options.dayEndHour);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined filter: Get visible all-day events for a day
|
||||
*/
|
||||
export function getVisibleAllDayEvents(
|
||||
events: CalendarEvent[],
|
||||
visibleCalendars: Calendar[]
|
||||
): CalendarEvent[] {
|
||||
let filtered = filterByVisibleCalendars(events, visibleCalendars);
|
||||
return filterAllDayEvents(filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined filter: Get overflow events for visible calendars
|
||||
*/
|
||||
export function getVisibleOverflowEvents(
|
||||
events: CalendarEvent[],
|
||||
visibleCalendars: Calendar[],
|
||||
dayStartHour: number,
|
||||
dayEndHour: number
|
||||
): OverflowEvents {
|
||||
let filtered = filterByVisibleCalendars(events, visibleCalendars);
|
||||
filtered = filterTimedEvents(filtered);
|
||||
return getOverflowEvents(filtered, dayStartHour, dayEndHour);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue