Calendar layout
Calendar layout idea
by leonsaysHi
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>
<div id="app">
<div class="calendar">
<div class="calendar__header">
<div></div>
<div class="days">
<div v-for="day in daysList" :class="{'-first-of-week': day.firstOfWeek, '-last-of-week': day.lastOfWeek, '-muted': day.muted}" class="day">
<span>{{ day.d.format('dd') }}</span>
<span>{{ day.d.format('D') }}</span>
<span>{{ day.d.format('MMM') }}</span>
</div>
</div>
</div>
<div class="calendar__body">
<div class="user-row" v-for="user in users">
<div>{{ user.name }}</div>
<div class="user-events">
<div class="days">
<div v-for="day in daysList" :class="{'-first-of-week': day.firstOfWeek, '-last-of-week': day.lastOfWeek, '-muted': day.muted}" class="day"></div>
</div>
<event-row v-for="event in getEventsForUser(user)" :event="event"></event-row>
</div>
</div>
</div>
</div>
</div>
SCSS
body {
font-family: arial, sans-serif;
}
.calendar {
width: 100%;
min-width: 2500px;
$deco-color: #dedede;
$user-w: 150px;
$week-w: 200px;
.days {
flex: 1 0 0;
display: flex;
> * {
flex: 1 0 0;
border-left: solid 1px $deco-color;
text-align: center;
&.-first-of-week {
border-color: darken($deco-color, 20%);
}
&.-muted {
background-color: rgba($deco-color, .25)
}
> span {
display: block;
font-size: .7rem;
}
}
}
&__header {
display: flex;
background-color: rgba($deco-color, .75);
> *:first-child {
flex: 0 0 $user-w;
}
.days {
> * {
padding: .25em 0;
text-align: center;
font-size: .9rem;
}
}
}
&__body {
.days {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
> * {
&:not(:last-child) {
border-right-width: 0;
}
}
}
.user-row {
display: grid;
grid-template-columns: $user-w 1fr;
border-bottom: solid 1px lighten($deco-color, 5%);
.user-events {
position: relative;
z-index: 2;
.event-row {
position: relative;
z-index: 2;
display: grid;
margin: 3px 0;
}
}
}
}
.event {
overflow: hidden;
font-size: 11px;
border-radius: 4px;
border-top: solid 5px black;
background-color: rgba(lighten(black, 80%), .8);
&__header {
position: relative;
padding: 2px 6px;
strong {
display: block;
font-size: 13px;
}
}
&__body {
padding: 2px 6px 4px;
border-top: solid 1px rgba(lighten(black, 70%), .8);
}
}
}
Vue
Vue.component('event-row', {
template: `
<div class="event-row" :style="{'grid-template-columns': gridStyle}">
<div></div>
<event :event="event"></event>
</div>
`,
props: ['event'],
computed: {
gridStyle() {
return `${this.event.grid.preLength}fr ${this.event.grid.eventLength}fr ${this.event.grid.postLength}fr`
}
}
})
Vue.component('event', {
template: `<div class="event" :style="{'border-top-color': event.infos.color}">
<div class="event__header" v-on:click="expanded = !expanded">
<strong>{{ event.infos.label }}</strong>
<span>{{ event.user.name }}</span>
</div>
<div class="event__body" v-if="expanded">
{{ event.infos.startAt.format('YYYY-MM-DD') }} - {{ event.infos.endAt.format('YYYY-MM-DD') }}
</div>
</div>`,
props: ['event'],
data(){
return {
expanded: false
}
}
})
new Vue({
el: "#app",
data: {
users: [
{ id: 0, name: "User 1" },
{ id: 1, name: "User 2" },
{ id: 2, name: "User 3" },
{ id: 3, name: "User 4" }
],
view: {
startAt: '2018-01',
endAt: '2018-02'
},
events: [
{ userId: 0, label: 'Event 1', startAt: '2018-01-01', endAt: '2018-01-10', color: 'red' },
{ userId: 1, label: 'Event 2', startAt: '2018-01-08', endAt: '2018-02-10', color: 'blue' },
{ userId: 1, label: 'Event 3', startAt: '2018-02-15', endAt: '2018-02-18', color: 'green' },
{ userId: 2, label: 'Event 4', startAt: '2018-01-03', endAt: '2018-01-15', color: 'rgb(190,190,0)' },
{ userId: 2, label: 'Event 5', startAt: '2018-01-03', endAt: '2018-03-15', color: 'rgb(0,190,190)' },
{ userId: 3, label: 'Event 6', startAt: '2017-10-04', endAt: '2018-01-15', color: 'rgb(190,0,190)' }
],
},
methods: {
getEventsForUser (user) {
return this.events
.filter( ev => user.id === ev.userId )
.map( ev => {
const
infos = {
startAt: moment(ev.startAt, "YYYY-MM-DD"),
...