Events & Data

Calendars & Filtering

Calendars group events and give them a colour. Three filters decide which of them are shown: hidden calendars, a search string, and a predicate of your own.

Calendars

calendars is a list of named groups: Work, Team, Personal. An event joins one through calendarId and takes its colour unless it sets a color of its own. The details of an event name its calendar with a swatch.

<script lang="ts">
  import { Scheduler, type EventInput, type SchedulerCalendar } from '@sv5ui/scheduler';

  const calendars: SchedulerCalendar[] = [
    { id: 'work', title: 'Work', color: 'primary' },
    { id: 'team', title: 'Team', color: 'info' },
    { id: 'personal', title: 'Personal', color: 'success' }
  ];

  // Events join a calendar through calendarId and take its colour
  let events = $state<EventInput[]>([
    { id: 'planning', title: 'Sprint planning', calendarId: 'team', start: '...', end: '...' },
    { id: 'gym', title: 'Gym', calendarId: 'personal', start: '...', end: '...' }
  ]);
</script>

<Scheduler bind:events {calendars} />

SchedulerCalendar

FieldDescription
idWhat events name in calendarId
titleShown in the checklist and in event details
colorColour of its events, unless an event sets its own. primary when unset

Hiding Calendars

hiddenCalendars lists the ids whose events are not shown. It is bindable, so the checklist in the sidebar and buttons of your own move the same array. An event with no calendarId belongs to no calendar and is never hidden this way; the Monday afternoon event below stays whatever you untick.

hiddenCalendars: personal

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
<script lang="ts">
  let hiddenCalendars = $state<string[]>(['personal']);
</script>

<!-- Bindable: the sidebar checklist and your own controls change the same array.
     An event with no calendarId is never hidden this way. -->
<Scheduler bind:events {calendars} bind:hiddenCalendars sidebar />

<Button label="Work only" onclick={() => (hiddenCalendars = ['team', 'personal'])} />
<Button label="Show all" onclick={() => (hiddenCalendars = [])} />

Search

search keeps the events whose title contains the text. Case and accents are ignored under the scheduler's locale, and the Vietnamese đ matches d, so hop doi finds Họp đội sản phẩm and zoe finds Zoë. Bindable, so the sidebar's field and an input of your own stay in step.

search: ""

October 2026

  • Monday, October 5

    1 event · 15m
  • Tuesday, October 6

    1 event · 15m
  • Wednesday, October 7

    1 event · 15m
  • Thursday, October 8

    1 event · 15m
  • Friday, October 9

    1 event · 15m
  • Monday, October 12

    1 event · 15m
  • Tuesday, October 13

    1 event · 15m
  • Wednesday, October 14

    1 event · 15m
  • Thursday, October 15

    1 event · 15m
  • Friday, October 16

    2 events · 1h 45m
  • Saturday, October 17

    1 event · 45m
  • Monday, October 19

    2 events · 1h 45m
  • Tuesday, October 20

    Today 3 events · 3h 15m
  • Wednesday, October 21

    5 events · 4h 15m
  • Thursday, October 22

    4 events · 2h 15m
  • Friday, October 23

    4 events · 4h 45m
  • Saturday, October 24

    1 event · 1h
  • Monday, October 26

    2 events · 1h 15m
  • Tuesday, October 27

    1 event · 15m
  • Wednesday, October 28

    2 events · 15m
  • Thursday, October 29

    1 event · 15m
  • Friday, October 30

    1 event · 15m
<script lang="ts">
  let search = $state('');
</script>

<!-- Matches the title, ignoring case and accents: "hop doi" finds "Họp đội".
     Bind it to the sidebar's field, or to an input of your own. -->
<Scheduler bind:events bind:search sidebar />
<Input bind:value={search} placeholder="Search events" />

Your Own Filter

filter is a predicate over your own data, applied after the two built-in filters. Here the status and owner live in event.data. All three run together, so the sidebar checklist still applies while a status is picked; the standalone CalendarList beside the controls is bound to the same array the scheduler reads.

My calendars

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<script lang="ts">
  interface Meeting { status: 'confirmed' | 'tentative'; mine: boolean }

  let events = $state<EventInput<Meeting>[]>([/* ... */]);
  let status = $state('all');
  let onlyMine = $state(false);
</script>

<!-- Runs after hiddenCalendars and search, on every event in the range.
     Return false to hide one. Occurrences carry the data of their series. -->
<Scheduler
  bind:events
  {calendars}
  sidebar
  filter={(event) =>
    (status === 'all' || event.data?.status === status) &&
    (!onlyMine || event.data?.mine === true)}
/>

What Filters Affect

Filtering happens after the events for the visible range are gathered and the series are expanded, so it is cheap to keep the predicate simple and pointless to make it clever about dates. Filters that the server should apply belong in the source function.

// Three filters, applied in this order to every event in the visible range:
//   1. hiddenCalendars   drops events whose calendarId is listed
//   2. search            keeps titles containing the text, case and accents ignored
//   3. filter            your predicate
// They reach every view, the DateNavigator's dots, and the events a sidebar
// snippet receives. They do not reach source: the server still gets the range.

Where the three filters reach

WhereFiltered
Every viewYes
DateNavigator dotsYes: a day whose only events are hidden loses its dot
events given to a sidebar snippetYes
Occurrences of a seriesYes: the series is tested, occurrences follow
sourceNo: the server is asked for the range, then the result is filtered
bind:eventsNo: the array keeps every event

Props

The three filters and the list they read. hiddenCalendars and search are bindable.

PropTypeDefault
calendarsSchedulerCalendar[][]
hiddenCalendarsstring[][]
searchstring''
filter(event: SchedulerEvent<T>) => boolean-