Created
May 26, 2020 18:54
-
-
Save Nicklas2751/da1fbe81878df7bb531fa4978c0ce7a8 to your computer and use it in GitHub Desktop.
The vuetify event calendar example with typescript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<v-row class="fill-height"> | |
<v-col> | |
<v-sheet height="64"> | |
<v-toolbar flat color="white"> | |
<v-btn outlined class="mr-4" color="grey darken-2" @click="setToday">Today</v-btn> | |
<v-btn fab text small color="grey darken-2" @click="prev"> | |
<v-icon small>mdi-chevron-left</v-icon> | |
</v-btn> | |
<v-btn fab text small color="grey darken-2" @click="next"> | |
<v-icon small>mdi-chevron-right</v-icon> | |
</v-btn> | |
<v-toolbar-title>{{ title }}</v-toolbar-title> | |
<v-spacer></v-spacer> | |
<v-menu bottom right> | |
<template v-slot:activator="{ on }"> | |
<v-btn outlined color="grey darken-2" v-on="on"> | |
<span>{{ typeToLabel[type] }}</span> | |
<v-icon right>mdi-menu-down</v-icon> | |
</v-btn> | |
</template> | |
<v-list> | |
<v-list-item @click="type = 'day'"> | |
<v-list-item-title>Day</v-list-item-title> | |
</v-list-item> | |
<v-list-item @click="type = 'week'"> | |
<v-list-item-title>Week</v-list-item-title> | |
</v-list-item> | |
<v-list-item @click="type = 'month'"> | |
<v-list-item-title>Month</v-list-item-title> | |
</v-list-item> | |
<v-list-item @click="type = '4day'"> | |
<v-list-item-title>4 days</v-list-item-title> | |
</v-list-item> | |
</v-list> | |
</v-menu> | |
</v-toolbar> | |
</v-sheet> | |
<v-sheet height="600"> | |
<v-calendar | |
ref="calendar" | |
v-model="focus" | |
color="primary" | |
:events="events" | |
:event-color="getEventColor" | |
:now="today" | |
:type="type" | |
@click:event="showEvent" | |
@click:more="viewDay" | |
@click:date="viewDay" | |
@change="updateRange" | |
></v-calendar> | |
<v-menu | |
v-model="selectedOpen" | |
:close-on-content-click="false" | |
:activator="selectedElement" | |
offset-x | |
> | |
<v-card color="grey lighten-4" min-width="350px" flat> | |
<v-toolbar :color="selectedEvent.color" dark> | |
<v-btn icon> | |
<v-icon>mdi-pencil</v-icon> | |
</v-btn> | |
<v-toolbar-title v-html="selectedEvent.name"></v-toolbar-title> | |
<v-spacer></v-spacer> | |
<v-btn icon> | |
<v-icon>mdi-heart</v-icon> | |
</v-btn> | |
<v-btn icon> | |
<v-icon>mdi-dots-vertical</v-icon> | |
</v-btn> | |
</v-toolbar> | |
<v-card-text> | |
<span v-html="selectedEvent.details"></span> | |
</v-card-text> | |
<v-card-actions> | |
<v-btn text color="secondary" @click="selectedOpen = false">Cancel</v-btn> | |
</v-card-actions> | |
</v-card> | |
</v-menu> | |
</v-sheet> | |
</v-col> | |
</v-row> | |
</template> | |
<script lang="ts"> | |
import { Component, Vue } from 'vue-property-decorator' | |
@Component | |
export default class Calendar extends Vue { | |
focus = '' | |
type = 'month' | |
today = '' | |
typeToLabel = { | |
month: 'Month', | |
week: 'Week', | |
day: 'Day', | |
'4day': '4 Days' | |
} | |
start: Start | null = null | |
end: End | null = null | |
selectedEvent = {} | |
selectedElement = null | |
selectedOpen = false | |
events: Event[] = [] | |
colors = ['blue', 'indigo', 'deep-purple', 'cyan', 'green', 'orange', 'grey darken-1'] | |
names = ['Meeting', 'Holiday', 'PTO', 'Travel', 'Event', 'Birthday', 'Conference', 'Party'] | |
get title () { | |
const { start, end } = this | |
if (!start || !end) { | |
return '' | |
} | |
const startMonth = this.monthFormatter(start) | |
const endMonth = this.monthFormatter(end) | |
const suffixMonth = startMonth === endMonth ? '' : endMonth | |
const startYear = start.year | |
const endYear = end.year | |
const suffixYear = startYear === endYear ? '' : endYear | |
const startDay = start.day + this.nth(start.day) | |
const endDay = end.day + this.nth(end.day) | |
switch (this.type) { | |
case 'month': | |
return `${startMonth} ${startYear}` | |
case 'week': | |
case '4day': | |
return `${startMonth} ${startDay} ${startYear} - ${suffixMonth} ${endDay} ${suffixYear}` | |
case 'day': | |
return `${startMonth} ${startDay} ${startYear}` | |
} | |
return '' | |
} | |
get monthFormatter () { | |
return this.calendarInstance.getFormatter({ | |
timeZone: 'UTC', month: 'long' | |
}) | |
} | |
mounted () { | |
this.calendarInstance.checkChange() | |
} | |
viewDay ({ date }: { date: string}) { | |
this.focus = date | |
this.type = 'day' | |
} | |
getEventColor (event: Event) { | |
return event.color | |
} | |
setToday () { | |
this.focus = this.today | |
} | |
prev () { | |
this.calendarInstance.prev() | |
} | |
next () { | |
this.calendarInstance.next() | |
} | |
showEvent ({ nativeEvent, event }: { nativeEvent: any; event: Event}) { | |
const open = () => { | |
this.selectedEvent = event | |
this.selectedElement = nativeEvent.target | |
setTimeout(() => (this.selectedOpen = true), 10) | |
} | |
if (this.selectedOpen) { | |
this.selectedOpen = false | |
setTimeout(open, 10) | |
} else { | |
open() | |
} | |
nativeEvent.stopPropagation() | |
} | |
updateRange ({ start, end }: {start: Start; end: End}) { | |
const events: Event[] = [] | |
const min = new Date(`${start.date}T00:00:00`) | |
const max = new Date(`${end.date}T23:59:59`) | |
const days = (max.getTime() - min.getTime()) / 86400000 | |
const eventCount = this.rnd(days, days + 20) | |
for (let i = 0; i < eventCount; i++) { | |
const allDay = this.rnd(0, 3) === 0 | |
const firstTimestamp = this.rnd(min.getTime(), max.getTime()) | |
const first = new Date(firstTimestamp - (firstTimestamp % 900000)) | |
const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000 | |
const second = new Date(first.getTime() + secondTimestamp) | |
events.push({ | |
name: this.names[this.rnd(0, this.names.length - 1)], | |
start: this.formatDate(first, !allDay), | |
end: this.formatDate(second, !allDay), | |
color: this.colors[this.rnd(0, this.colors.length - 1)] | |
}) | |
} | |
this.start = start | |
this.end = end | |
this.events = events | |
} | |
nth (d: number) { | |
return d > 3 && d < 21 | |
? 'th' | |
: ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'][d % 10] | |
} | |
rnd (a: number, b: number) { | |
return Math.floor((b - a + 1) * Math.random()) + a | |
} | |
formatDate (a: Date, withTime: boolean) { | |
return withTime | |
? `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()} ${a.getHours()}:${a.getMinutes()}` | |
: `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()}` | |
} | |
private get calendarInstance (): Vue & { prev: () => void; next: () => void; checkChange: () => void; getFormatter: (format: any) => any } { | |
return this.$refs.calendar as Vue & { prev: () => void; next: () => void; checkChange: () => void; getFormatter: (format: any) => any } | |
} | |
} | |
export type Start = { | |
date: string; | |
time: string; | |
year: number; | |
month: number; | |
day: number; | |
hour: number; | |
minute: number; | |
weekday: number; | |
hasDay: boolean; | |
hasTime: boolean; | |
past: boolean; | |
present: boolean; | |
future: boolean; | |
} | |
export type End = { | |
date: string; | |
time: string; | |
year: number; | |
month: number; | |
day: number; | |
hour: number; | |
minute: number; | |
weekday: number; | |
hasDay: boolean; | |
hasTime: boolean; | |
past: boolean; | |
present: boolean; | |
future: boolean; | |
} | |
export type Event = { | |
name: string; | |
start: string; | |
end: string; | |
color: string; | |
} | |
</script> |
Can you help me?
@EmanuelAngelo What help do you need?
@Nicklas2751
how can I do to put the schedules and they will not collide?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you buddy!