An agenda to read and rearrange: day, week, month and year views, with events that can be dragged and stretched. Opening one for editing stays with you.
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
/*
* This page is prerendered, so the clock cannot be read while the markup is being built:
* the build date would be written into the HTML and the browser would disagree with it.
* The schedule below therefore starts on a fixed week and moves onto the current one once
* the page is in the browser, which is what the theme does here for the same reason.
*/
const REFERENCE_MONDAY = '2026-01-05'
const anchor = ref(REFERENCE_MONDAY)
const events = ref<CalendarEvent[]>(scheduleFor(REFERENCE_MONDAY))
onMounted(() => {
anchor.value = mondayOf(new Date())
events.value = scheduleFor(anchor.value)
})
/** A date as the local ISO day the calendar reads, never through UTC. */
function isoOf(date: Date): string {
const two = (part: number) => String(part).padStart(2, '0')
return `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}`
}
/** The Monday of the week a date falls in. */
function mondayOf(date: Date): string {
const monday = new Date(date)
monday.setDate(date.getDate() - ((date.getDay() + 6) % 7))
return isoOf(monday)
}
/** The same three appointments, on whichever week starts on the Monday given. */
function scheduleFor(monday: string): CalendarEvent[] {
const day = (offset: number) => {
// The time keeps the parse local: a bare `YYYY-MM-DD` is read as UTC.
const date = new Date(`${monday}T00:00:00`)
date.setDate(date.getDate() + offset)
return isoOf(date)
}
return [
{
id: 'standup',
title: 'Standup',
start: day(0),
end: day(0),
startTime: '09:00',
endTime: '09:15',
},
{
id: 'review',
title: 'Design review',
start: day(1),
end: day(1),
startTime: '14:00',
endTime: '15:30',
description: 'With Anna and Ravi',
},
{
id: 'lunch',
title: 'Team lunch',
start: day(3),
end: day(3),
startTime: '12:30',
endTime: '13:30',
},
]
}
</script>
<template>
<VCalendar v-model:date="anchor" v-model:events="events" label="Team schedule" class="calendar" />
</template>
<style scoped>
/* A calendar has no height of its own, and collapses without one. */
.calendar {
block-size: 34rem;
}
</style>
Examples
Month
The month view draws each day as a square holding its events as chips. monthEventLimit caps how many are shown before the rest are counted.
vue
June 2026
MonTueWedThuFriSatSun
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
/* A year is read for its shape rather than its detail, so the events below are spread over
it: a few single days, and the two stretches that make a run of days busy at once. */
const events = ref<CalendarEvent[]>([
{
id: 'kickoff',
title: 'Kickoff',
start: '2026-01-13',
end: '2026-01-13',
startTime: '10:00',
endTime: '11:00',
},
{
id: 'audit',
title: 'Accessibility audit',
start: '2026-03-02',
end: '2026-03-06',
startTime: '00:00',
endTime: '23:59',
},
{
id: 'review',
title: 'Design review',
start: '2026-04-21',
end: '2026-04-21',
startTime: '14:00',
endTime: '15:30',
},
{
id: 'quarterly',
title: 'Quarterly plan',
start: '2026-06-10',
end: '2026-06-10',
startTime: '08:00',
endTime: '09:00',
},
{
id: 'summer',
title: 'Summer break',
start: '2026-08-03',
end: '2026-08-14',
startTime: '00:00',
endTime: '23:59',
},
{
id: 'conference',
title: 'Conference',
start: '2026-09-22',
end: '2026-09-24',
startTime: '00:00',
endTime: '23:59',
},
{
id: 'retro',
title: 'Retrospective',
start: '2026-11-05',
end: '2026-11-05',
startTime: '16:00',
endTime: '17:00',
},
])
</script>
<template>
<VCalendar
v-model:events="events"
view="year"
:views="['year', 'month', 'week']"
date="2026-06-10"
label="Year"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 38rem;
}
</style>
Custom view
The custom view leaves the length to you: customDays says how many days it shows, and how far Previous and Next step.
vue
10 – 14 June 2026
Wed10
Thu11
Fri12
Sat13
Sun14
8:009:0010:0011:0012:0013:0014:0015:0016:0017:00
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
/* The span below runs from Wednesday to Sunday, which is what shows what the custom view
is for: a week would have stopped at the Sunday boundary and started again on Monday. */
const events = ref<CalendarEvent[]>([
{
id: 'standup',
title: 'Standup',
start: '2026-06-10',
end: '2026-06-10',
startTime: '09:00',
endTime: '09:15',
},
{
id: 'review',
title: 'Design review',
start: '2026-06-10',
end: '2026-06-10',
startTime: '14:00',
endTime: '15:30',
},
{
id: 'workshop',
title: 'Workshop',
start: '2026-06-11',
end: '2026-06-11',
startTime: '09:30',
endTime: '12:00',
},
{
id: 'retro',
title: 'Retrospective',
start: '2026-06-12',
end: '2026-06-12',
startTime: '16:00',
endTime: '17:00',
},
{
id: 'conference',
title: 'Conference',
start: '2026-06-13',
end: '2026-06-13',
startTime: '10:00',
endTime: '16:00',
},
{
id: 'handover',
title: 'Handover',
start: '2026-06-14',
end: '2026-06-14',
startTime: '11:00',
endTime: '12:00',
},
])
</script>
<template>
<VCalendar
v-model:events="events"
view="custom"
:custom-days="5"
:views="['day', 'custom', 'week']"
:day-start="8"
:day-end="18"
date="2026-06-10"
label="Custom span"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
</style>
Which days are shown
weekdays decides which days appear at all, as numbers from 0 for Sunday, its first entry being the day a week starts on. Without it, firstDayOfWeek sets that first day over the locale. dayStart and dayEnd crop the hours shown in the time grids.
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
const events = ref<CalendarEvent[]>([
{
id: 'standup',
title: 'Standup',
start: '2026-06-08',
end: '2026-06-08',
startTime: '09:00',
endTime: '09:15',
},
{
id: 'review',
title: 'Design review',
start: '2026-06-09',
end: '2026-06-09',
startTime: '10:00',
endTime: '11:30',
},
{
id: 'lunch',
title: 'Team lunch',
start: '2026-06-10',
end: '2026-06-10',
startTime: '12:30',
endTime: '13:30',
},
{
id: 'workshop',
title: 'Workshop',
start: '2026-06-11',
end: '2026-06-11',
startTime: '09:30',
endTime: '12:00',
},
{
id: 'retro',
title: 'Retrospective',
start: '2026-06-12',
end: '2026-06-12',
startTime: '16:00',
endTime: '17:00',
},
])
</script>
<template>
<!-- Monday to Friday, eight in the morning to seven in the evening. Every view is on the
menu, since the weekend is missing from all five and not only from the columns. -->
<VCalendar
v-model:events="events"
:weekdays="[1, 2, 3, 4, 5]"
:day-start="8"
:day-end="19"
:custom-days="3"
:views="['day', 'custom', 'week', 'month', 'year']"
date="2026-06-10"
label="Working week"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
</style>
All-day events
allDay puts an event in the band above the grid, where an event lasting 24 hours or more already goes. One running past midnight for less than that stays in the grid, as a card in each of its two days.
vue
8 – 14 June 2026
Mon8
Tue9
Wed10
Thu11
Fri12
Sat13
Sun14
All day
8:009:0010:0011:0012:0013:0014:0015:0016:0017:00
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
const events = ref<CalendarEvent[]>([
// Marked `allDay`, so it goes to the band above the grid although it sits on one day.
{
id: 'holiday',
title: 'Bank holiday',
start: '2026-06-08',
end: '2026-06-08',
startTime: '00:00',
endTime: '23:59',
allDay: true,
},
// These two run from one day to the next, which puts them in the band on their own.
{
id: 'workshop',
title: 'Workshop',
start: '2026-06-09',
end: '2026-06-11',
startTime: '00:00',
endTime: '23:59',
},
{
id: 'release',
title: 'Release window',
start: '2026-06-10',
end: '2026-06-12',
startTime: '00:00',
endTime: '23:59',
},
{
id: 'review',
title: 'Design review',
start: '2026-06-10',
end: '2026-06-10',
startTime: '10:00',
endTime: '11:30',
},
{
id: 'retro',
title: 'Retrospective',
start: '2026-06-11',
end: '2026-06-11',
startTime: '16:00',
endTime: '17:00',
},
])
</script>
<template>
<VCalendar
v-model:events="events"
:day-start="8"
:day-end="18"
date="2026-06-10"
label="All-day events"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
</style>
Overlapping events
Events happening at once share the width of their day, grouped into clusters so a crowded morning does not narrow a lone afternoon meeting.
vue
Wednesday, 10 June 2026
Wed10
8:009:0010:0011:0012:0013:0014:0015:0016:0017:00
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
const DAY = '2026-06-10'
/* Four events crowd the morning and one stands alone after it, which is what shows the
clustering: the lunch keeps the full width the morning has to share. */
const events = ref<CalendarEvent[]>([
{
id: 'standup',
title: 'Standup',
start: DAY,
end: DAY,
startTime: '09:00',
endTime: '10:00',
},
{
id: 'review',
title: 'Design review',
start: DAY,
end: DAY,
startTime: '09:30',
endTime: '11:00',
},
{
id: 'interview',
title: 'Interview',
start: DAY,
end: DAY,
startTime: '10:00',
endTime: '10:30',
},
{
id: 'workshop',
title: 'Workshop',
start: DAY,
end: DAY,
startTime: '09:00',
endTime: '09:30',
},
{
id: 'lunch',
title: 'Team lunch',
start: DAY,
end: DAY,
startTime: '12:30',
endTime: '13:30',
},
])
</script>
<template>
<VCalendar
v-model:events="events"
view="day"
:views="['day', 'week']"
:day-start="8"
:day-end="18"
:date="DAY"
label="Overlapping events"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
</style>
Colours
An event with no color takes a hue derived from its id. One naming a colour of its own uses it for its leading edge and a wash of its face.
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, type CalendarEvent } from 'vectis-ui'
const DAY = '2026-06-10'
/* The first six name no colour, so each takes the hue its id derives. The last two name
one of their own, in two of the notations any CSS colour may be written in. */
const events = ref<CalendarEvent[]>([
{ id: 'hue-1', title: 'Standup', start: DAY, end: DAY, startTime: '08:00', endTime: '09:00' },
{
id: 'hue-2',
title: 'Design review',
start: DAY,
end: DAY,
startTime: '09:00',
endTime: '10:00',
},
{ id: 'hue-3', title: 'Workshop', start: DAY, end: DAY, startTime: '10:00', endTime: '11:00' },
{
id: 'hue-4',
title: 'Retrospective',
start: DAY,
end: DAY,
startTime: '11:00',
endTime: '12:00',
},
{ id: 'hue-5', title: 'Team lunch', start: DAY, end: DAY, startTime: '12:00', endTime: '13:00' },
{ id: 'hue-6', title: 'Interview', start: DAY, end: DAY, startTime: '13:00', endTime: '14:00' },
{
id: 'own-1',
title: 'One to one',
start: DAY,
end: DAY,
startTime: '14:00',
endTime: '15:00',
color: '#7c3aed',
},
{
id: 'own-2',
title: 'Release window',
start: DAY,
end: DAY,
startTime: '16:00',
endTime: '18:00',
color: 'oklch(0.65 0.15 200)',
},
])
</script>
<template>
<VCalendar
v-model:events="events"
view="day"
:views="['day', 'week']"
:day-start="8"
:day-end="20"
:date="DAY"
label="Event colours"
class="calendar"
/>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
</style>
Custom event content
The #event slot replaces what a card shows and receives the event, its formatted timeText, the layout it is drawn in, whether it continues before or after the day, and whether it is being dragged or held by the keyboard.
vue
Wednesday, 10 June 2026
Wed10
8:009:0010:0011:0012:0013:0014:0015:0016:0017:00
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { ref } from 'vue'
import { VCalendar, VIcon, type CalendarEvent } from 'vectis-ui'
import { schedule as scheduleIcon } from 'vectis-ui/icons'
/* An interface of your own extending the contract: the extra fields reach the slot typed,
rather than as something to cast there. */
interface Booking extends CalendarEvent {
room: string
attendees: number
}
const DAY = '2026-06-10'
const events = ref<Booking[]>([
{
id: 'review',
title: 'Design review',
start: DAY,
end: DAY,
startTime: '10:00',
endTime: '11:30',
room: 'B2',
attendees: 6,
},
{
id: 'workshop',
title: 'Workshop',
start: DAY,
end: DAY,
startTime: '14:00',
endTime: '16:00',
room: 'A1',
attendees: 12,
},
])
</script>
<template>
<VCalendar
v-model:events="events"
view="day"
:views="['day', 'week']"
:day-start="8"
:day-end="18"
:date="DAY"
label="Custom event content"
class="calendar"
>
<template #event="{ event, timeText }">
<span class="title">{{ event.title }}</span>
<span class="line">
<VIcon :name="scheduleIcon" :size="14" aria-hidden="true" />
{{ timeText }}
</span>
<span class="line">Room {{ event.room }}, {{ event.attendees }} people</span>
</template>
</VCalendar>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
.title {
font-weight: var(--vectis-font-weight-semibold);
}
.line {
display: flex;
align-items: center;
gap: var(--vectis-space-1);
font-size: var(--vectis-text-caption-size);
}
</style>
Creating and editing events
The calendar reports what the reader did and leaves the list to you, so creating and editing come down to three events and one dialog. Drawing out a slot with creatable fires event-create with both ends, clicking an empty cell fires cell-activate with a start only, and a button in the #actions slot opens a blank form. Clicking a card fires event-activate, which opens the same form filled in. Saving writes a new array to v-model:events, the same model dragging and resizing already write to.
Press Enter to open this event. Press Space to take hold of it, then the arrow keys to move it and Shift with the arrow keys to change when it ends.
<script setup lang="ts">
import { computed, reactive, ref, useId } from 'vue'
import {
VButton,
VCalendar,
VDateInput,
VDialog,
VInput,
VSwitch,
VTextarea,
VTimeInput,
type CalendarCell,
type CalendarEvent,
type CalendarEventId,
type CalendarEventTimes,
type CalendarView,
} from 'vectis-ui'
const view = ref<CalendarView>('week')
const anchor = ref('2026-06-10')
const events = ref<CalendarEvent[]>([
{
id: 'standup',
title: 'Standup',
start: '2026-06-08',
end: '2026-06-08',
startTime: '09:00',
endTime: '09:15',
},
{
id: 'review',
title: 'Design review',
start: '2026-06-10',
end: '2026-06-10',
startTime: '14:00',
endTime: '15:30',
description: 'With Anna and Ravi',
},
])
let nextId = 1
/** What the form edits: a copy, so Cancel leaves the calendar as it was. */
interface Draft {
title: string
allDay: boolean
start: string | null
startTime: string | null
end: string | null
endTime: string | null
description: string
}
const open = ref(false)
const formId = useId()
/** The event being edited, or null while a new one is being written. */
const editingId = ref<CalendarEventId | null>(null)
const draft = reactive<Draft>(emptyDraft())
function emptyDraft(): Draft {
return {
title: '',
allDay: false,
start: null,
startTime: null,
end: null,
endTime: null,
description: '',
}
}
/** A local date and time moved by some minutes, crossing midnight when it has to. */
function addMinutes(date: string, time: string, minutes: number): { date: string; time: string } {
const moved = new Date(`${date}T${time}:00`)
moved.setMinutes(moved.getMinutes() + minutes)
const two = (part: number) => String(part).padStart(2, '0')
return {
date: `${moved.getFullYear()}-${two(moved.getMonth() + 1)}-${two(moved.getDate())}`,
time: `${two(moved.getHours())}:${two(moved.getMinutes())}`,
}
}
function openNew(times: CalendarEventTimes) {
Object.assign(draft, emptyDraft(), times)
editingId.value = null
open.value = true
}
/* A drawn-out slot already has both ends. */
function onEventCreate(times: CalendarEventTimes) {
openNew(times)
}
/* A clicked cell only has a start, so the event is given an hour. A day of the month view
reports the hour the grid starts at, midnight here, which is a poor default for a meeting. */
function onCellActivate(cell: CalendarCell) {
const startTime = view.value === 'month' ? '09:00' : cell.time
const end = addMinutes(cell.date, startTime, 60)
openNew({ start: cell.date, startTime, end: end.date, endTime: end.time })
}
/* The toolbar button has neither, and starts on the day the calendar is showing. */
function onNewClick() {
openNew({ start: anchor.value, startTime: '09:00', end: anchor.value, endTime: '10:00' })
}
function onEventActivate(event: CalendarEvent) {
Object.assign(draft, {
title: event.title,
allDay: event.allDay ?? false,
start: event.start,
startTime: event.startTime,
end: event.end,
endTime: event.endTime,
description: event.description ?? '',
})
editingId.value = event.id
open.value = true
}
/* ISO dates and 24-hour times compare correctly as strings, so no Date is needed here. */
const endsBeforeStart = computed(() => {
if (!draft.start || !draft.end) return false
if (draft.allDay) return draft.end < draft.start
if (!draft.startTime || !draft.endTime) return false
return `${draft.end} ${draft.endTime}` <= `${draft.start} ${draft.startTime}`
})
function save() {
// `required` has the browser refuse empty fields before this runs. What it cannot know is
// whether the end comes after the start, or that a title made of spaces is no title.
const title = draft.title.trim()
// The contract always wants times: an all-day event spans the whole of its days.
const startTime = draft.allDay ? '00:00' : draft.startTime
const endTime = draft.allDay ? '23:59' : draft.endTime
if (!title || !draft.start || !draft.end || !startTime || !endTime) return
if (endsBeforeStart.value) return
const saved: CalendarEvent = {
id: editingId.value ?? `event-${nextId++}`,
title,
start: draft.start,
end: draft.end,
startTime,
endTime,
allDay: draft.allDay || undefined,
description: draft.description.trim() || undefined,
}
// A new array rather than a push: the calendar is handed a list and never mutates it.
events.value =
editingId.value === null
? [...events.value, saved]
: events.value.map((event) => (event.id === saved.id ? saved : event))
open.value = false
}
function remove() {
events.value = events.value.filter((event) => event.id !== editingId.value)
open.value = false
}
</script>
<template>
<VCalendar
v-model:view="view"
v-model:date="anchor"
v-model:events="events"
creatable
:views="['day', 'week', 'month']"
:day-start="7"
:day-end="20"
label="Editable schedule"
class="calendar"
@event-create="onEventCreate"
@cell-activate="onCellActivate"
@event-activate="onEventActivate"
>
<template #actions>
<VButton size="sm" @click="onNewClick">New event</VButton>
</template>
</VCalendar>
<!-- Closing the dialog hands the focus back to whatever opened it: the card, the cell or
the button. The form lives in the body and its buttons in the footer, so the submit
button reaches it through the `form` attribute. -->
<VDialog
v-model:open="open"
:title="editingId === null ? 'New event' : 'Edit event'"
width="32rem"
>
<form :id="formId" class="form" @submit.prevent="save">
<VInput v-model="draft.title" label="Title" required />
<VSwitch v-model="draft.allDay" label="All day" class="all-day" />
<div class="row">
<VDateInput v-model="draft.start" label="Start date" show-picker required />
<VTimeInput
v-if="!draft.allDay"
v-model="draft.startTime"
label="Start time"
:minute-step="15"
required
/>
</div>
<div class="row">
<VDateInput
v-model="draft.end"
label="End date"
show-picker
required
:invalid="endsBeforeStart"
:hint="endsBeforeStart ? 'The event has to end after it starts.' : undefined"
/>
<VTimeInput
v-if="!draft.allDay"
v-model="draft.endTime"
label="End time"
:minute-step="15"
required
:invalid="endsBeforeStart"
/>
</div>
<VTextarea v-model="draft.description" label="Description" :rows="3" />
</form>
<template #footer>
<VButton
v-if="editingId !== null"
class="delete"
variant="ghost"
tone="danger"
@click="remove"
>
Delete
</VButton>
<VButton variant="ghost" tone="neutral" @click="open = false">Cancel</VButton>
<VButton type="submit" :form="formId">Save</VButton>
</template>
</VDialog>
</template>
<style scoped>
.calendar {
block-size: 34rem;
}
.form {
display: grid;
gap: var(--vectis-space-4);
}
/* A grid item stretches by default, which would push the label away from its switch. */
.all-day {
justify-self: start;
}
.row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
gap: var(--vectis-space-3);
align-items: start;
}
/* Pushed to the other end of the footer, away from the button that saves. */
.delete {
margin-inline-end: auto;
}
</style>
Which views the menu offers, in the order it lists them. Narrowing it is how a calendar that only ever shows weeks stops offering anything else.
customDays
number
4
How many days the custom view shows, and how far Previous and Next step in it.
weekdays
number[]
none
Which weekdays are on show, as numbers from 0 for Sunday. The order matters as well: the first entry is the day a week starts on, and it wins over firstDayOfWeek. Left out, the seven days starting on firstDayOfWeek.
firstDayOfWeek
number
none
The day a week starts on, from 0 for Sunday, when weekdays is not given. Left out, the locale decides.
locale
string
none
The language the days, months and times are written in. It falls back to the global one.
format
CalendarFormat'12h' | '24h'
none
Whether times are shown on a 12- or a 24-hour clock. It follows the locale.
dayStart
number
0
The hour the grid starts at, from 0.
dayEnd
number
24
The hour it ends at, up to 24.
slotDuration
number
15
The step everything snaps to, in minutes: how far a nudge moves an event, and the unit a slot is drawn out in.
scrollTime
string
'08:00'
Where the grid is scrolled to when it first appears, so the working day is in view.
hideCurrentTime
boolean
false
Leaves out the line drawn across today's column at the time it is now, and the dot on its leading edge. Left in, it ticks once a minute while the calendar is on screen.
monthEventLimit
number
3
How many events a day of the month view shows before it starts counting the rest.
readonly
boolean
false
Stops events being moved and stretched, by dragging them and with the keyboard. They stay readable and clickable, and nothing else.
disabled
boolean
false
Freezes the whole calendar: nothing can be moved, created or opened, and no other period can be reached. The cards leave the tab order while the grid keeps its own, so the agenda can still be read. That is what separates it from readonly, which stops the editing alone.
creatable
boolean
false
Lets an empty stretch of a time grid be drawn out with the pointer, up or down from the slot pressed. On release its times are reported through event-create and nothing is added to events: putting the event on the calendar is yours to do. A click, or Enter on a focused cell, reports cell-activate with or without this.
edgeStepDelay
number
800
How long a dragged event has to rest against the side of the calendar before the view turns to the previous or next period, in milliseconds. Zero turns that off. The wait is the point of it: paging the instant the pointer touched the edge would make the last day of a week impossible to aim at.
noEdgeScroll
boolean
false
Stops dragging near the top or bottom of a time grid from scrolling it.
label
string
none
What the calendar is called, for anyone who cannot see it.
Which span the calendar is showing. It opens on the week.
v-model:date
string
today
The day the view is anchored on, as an ISO string. It opens on today.
v-model:events
E[]
[]
What is on the calendar. It is a model rather than a plain prop because dragging and resizing write back to it: the calendar rearranges what it is given and hands the new list back, never mutating the one it received.
Events
Event
Type
event-activate
[event: E]
A card was clicked or activated, the cue to open an editor of your own.
An empty part of the grid was activated, at this day and this time. A day of the month view has no hour of its own, and reports the one the time grids start at.
An empty stretch of a day was drawn out, and these are its times. Nothing has been added to the list: this is the cue to make the event, in your own model or through your own form.
Slots
Slot
Type
actions
{}
Extra controls in the toolbar, between the range and the view menu.
The head of one day column, replacing the weekday and the number.
all-day-label
{}
The label beside the band of all-day events.
Types
The types the tables above name, written as the library declares them. The ones carrying export can be imported from vectis-ui to type your own code; the others are the shape of what a slot hands out.