Vuetify is a popular UI framework for Vue apps.
In this article, we’ll look at how to work with the Vuetify framework.
Calendar Categories
We can categorize calendar events with various categories.
For example, we can write:
<template>
<v-container>
<v-row class="text-center">
<v-col col="12">
<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 v-if="$refs.calendar">{{ $refs.calendar.title }}</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
</v-sheet>
<v-sheet height="600">
<v-calendar
ref="calendar"
v-model="focus"
color="primary"
type="category"
category-show-all
:categories="categories"
:events="events"
:event-color="getEventColor"
[@change](http://twitter.com/change "Twitter profile for @change")="fetchEvents"
></v-calendar>
</v-sheet>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: "HelloWorld",
data: () => ({
focus: "",
events: [],
colors: [
"blue",
"indigo",
"deep-purple"
],
names: [
"Meeting",
"Holiday",
"Leave"
],
categories: ["John Smith", "Mary Walker"],
}),
mounted() {
this.$refs.calendar.checkChange();
},
methods: {
getEventColor(event) {
return event.color;
},
setToday() {
this.focus = "";
},
prev() {
this.$refs.calendar.prev();
},
next() {
this.$refs.calendar.next();
},
fetchEvents({ start, end }) {
const events = [];
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: first,
end: second,
color: this.colors[this.rnd(0, this.colors.length - 1)],
timed: !allDay,
category: this.categories[this.rnd(0, this.categories.length - 1)],
});
}
this.events = events;
},
rnd(a, b) {
return Math.floor((b - a + 1) * Math.random()) + a;
},
},
};
</script>
We set the categories
to the categories we want to display.
category-show-all
makes all category show.
Also, we set the type
prop to category
so that we display event categories.
Drag and Drop
We can drag and drop events in the calendar.
For example, we can write:
<template>
<v-container>
<v-row class="text-center">
<v-col col="12">
<v-sheet height="600">
<v-calendar
ref="calendar"
v-model="value"
color="primary"
type="4day"
:events="events"
:event-color="getEventColor"
:event-ripple="false"
@change="getEvents"
@mousedown:event="startDrag"
@mousedown:time="startTime"
@mousemove:time="mouseMove"
@mouseup:time="endDrag"
@mouseleave.native="cancelDrag"
>
<template #event="{ event, timed, eventSummary }">
<div class="v-event-draggable" v-html="eventSummary()"></div>
<div v-if="timed" class="v-event-drag-bottom" @mousedown.stop="extendBottom(event)"></div>
</template>
</v-calendar>
</v-sheet>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: "HelloWorld",
data: () => ({
value: "",
events: [],
colors: [
"red",
"green",
"blue",
],
names: [
"Birthday",
"Conference",
"Party",
],
dragEvent: null,
dragStart: null,
createEvent: null,
createStart: null,
extendOriginal: null,
}),
methods: {
startDrag({ event, timed }) {
if (event && timed) {
this.dragEvent = event;
this.dragTime = null;
this.extendOriginal = null;
}
},
startTime(tms) {
const mouse = this.toTime(tms);
if (this.dragEvent && this.dragTime === null) {
const start = this.dragEvent.start;
this.dragTime = mouse - start;
} else {
this.createStart = this.roundTime(mouse);
this.createEvent = {
name: `Event #${this.events.length}`,
color: this.rndElement(this.colors),
start: this.createStart,
end: this.createStart,
timed: true,
};
this.events.push(this.createEvent);
}
},
extendBottom(event) {
this.createEvent = event;
this.createStart = event.start;
this.extendOriginal = event.end;
},
mouseMove(tms) {
const mouse = this.toTime(tms);
if (this.dragEvent && this.dragTime !== null) {
const start = this.dragEvent.start;
const end = this.dragEvent.end;
const duration = end - start;
const newStartTime = mouse - this.dragTime;
const newStart = this.roundTime(newStartTime);
const newEnd = newStart + duration;
this.dragEvent.start = newStart;
this.dragEvent.end = newEnd;
} else if (this.createEvent && this.createStart !== null) {
const mouseRounded = this.roundTime(mouse, false);
const min = Math.min(mouseRounded, this.createStart);
const max = Math.max(mouseRounded, this.createStart);
this.createEvent.start = min;
this.createEvent.end = max;
}
},
endDrag() {
this.dragTime = null;
this.dragEvent = null;
this.createEvent = null;
this.createStart = null;
this.extendOriginal = null;
},
cancelDrag() {
if (this.createEvent) {
if (this.extendOriginal) {
this.createEvent.end = this.extendOriginal;
} else {
const i = this.events.indexOf(this.createEvent);
if (i !== -1) {
this.events.splice(i, 1);
}
}
}
this.createEvent = null;
this.createStart = null;
this.dragTime = null;
this.dragEvent = null;
},
roundTime(time, down = true) {
const roundTo = 15;
const roundDownTime = roundTo * 60 * 1000;
return down
? time - (time % roundDownTime)
: time + (roundDownTime - (time % roundDownTime));
},
toTime(tms) {
return new Date(
tms.year,
tms.month - 1,
tms.day,
tms.hour,
tms.minute
).getTime();
},
getEventColor(event) {
const rgb = parseInt(event.color.substring(1), 16);
const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff;
return event === this.dragEvent
? `rgba(${r}, ${g}, ${b}, 0.7)`
: event === this.createEvent
? `rgba(${r}, ${g}, ${b}, 0.7)`
: event.color;
},
getEvents({ start, end }) {
const events = [];
const min = new Date(`${start.date}T00:00:00`).getTime();
const max = new Date(`${end.date}T23:59:59`).getTime();
const days = (max - min) / 86400000;
const eventCount = this.rnd(days, days + 20);
for (let i = 0; i < eventCount; i++) {
const timed = this.rnd(0, 3) !== 0;
const firstTimestamp = this.rnd(min, max);
const secondTimestamp = this.rnd(2, timed ? 8 : 288) * 900000;
const start = firstTimestamp - (firstTimestamp % 900000);
const end = start + secondTimestamp;
events.push({
name: this.rndElement(this.names),
color: this.rndElement(this.colors),
start,
end,
timed,
});
}
this.events = events;
},
rnd(a, b) {
return Math.floor((b - a + 1) * Math.random()) + a;
},
rndElement(arr) {
return arr[this.rnd(0, arr.length - 1)];
},
},
};
</script>
We have the mousedown
and mouseup
events to let us handle the dragging and dropping of the events.
The event start and end time of the event as we move the event.
The startTime
event sets the this.createEvent
when we move the calendar item.
Then in the mouseMove
method, we set the start and end date.
Now we’ll see the event update when we drag and drop it.
Conclusion
We can create calendars that move events and set categories.