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"),
        ...