Calendar.vue 13.6 KB
Newer Older
Kent Nielsen's avatar
Kent Nielsen committed
1
2
<template>
<div id="calendar">
3
    <table id="calendarTable">
4
        <thead :draggable="dragEnabled.value" @dragstart="startDrag($event, row, col)" @dragend.prevent="stopDrag()">
5
          <!-- Generating first header of schema -->
6
          <tr>
7
              <th id="calendarHead" colspan="6">
8
9
              <div id="headerDiv">
                <div>  
Kent Nielsen's avatar
Kent Nielsen committed
10
                  <button id="leftButton" @click="prevWeek()"> &larr; </button>
11
                </div>
12
                <div>
13
14
                  Uge: {{getWeek(today)}} - {{getMonthName(today.getMonth())}}
                </div>
15
                <div>
Kent Nielsen's avatar
Kent Nielsen committed
16
                  <button id="rightButton" @click="nextWeek()"> &rarr; </button>
17
18
19
                </div>
              </div>
            </th>
20
          </tr>
21
22
23
24
          <tr>
            <!-- Empty cell to make monday move 1 cell left -->
            <th></th>
            <!-- Generating weekday names and month number-->
25
26
27
28
29
            <template v-if="schedule.length == 1">
              <th v-bind:colspan="colSpanValue" v-for="(n,i) in schedule" :key="i">
                  {{weekDays[today.getDay()-1]}} - {{today.getDate()}}
              </th>
            </template>
Kent Nielsen's avatar
Kent Nielsen committed
30
            <!-- Generate a single day, if size is too small -->
31
32
33
34
35
            <template v-else>
              <th v-bind:colspan="colSpanValue" v-for="(n,i) in schedule" :key="i">
                  {{weekDays[i]}} - {{getDateOfISOWeek(getWeek(today), today.getFullYear(), i).getDate()}}
              </th>
            </template>
36
          </tr>
Kent Nielsen's avatar
Kent Nielsen committed
37
38
        </thead>
        <tbody>
39
          <!-- Generating time rows -->
40
          <tr v-for="(time,i) in time" :key="time" >
41
42
            <td>{{time}}</td>
            <!-- Generating schema based on first row-->
Kent Nielsen's avatar
Kent Nielsen committed
43
            <template v-if="firstIndex(i)">
44
              <td :colspan="colSpanValue" id="skematd"  v-for="day in schedule" :key="day">
45
                  <!-- Adding and calculatings divs for each task, offset form the first row -->
46
47
48
49
50
51
                  <a href="/about">
                    <div v-for="task in day" :key="task" 
                    :style="{height: (task.stop-0.25-task.start)*halfHourSpace + 'px', top: task.start*halfHourSpace + 'px'}">
                      {{task.name}}
                    </div>
                  </a>
Kent Nielsen's avatar
Kent Nielsen committed
52
53
              </td>
            </template>
54
            <!-- All other rows than the first are empty, but full of cells -->
Kent Nielsen's avatar
Kent Nielsen committed
55
            <template v-else>
56
              <td :colspan="colSpanValue" v-for="n in schedule" :key="n"></td>
Kent Nielsen's avatar
Kent Nielsen committed
57
58
59
60
61
62
63
64
65
            </template>
          </tr>
        </tbody>
    </table>
</div>
</template>

<script>
export default {
66
  name: 'Cal',
67
68
69
70
71

  props: ['row', 'col'],

  inject: [ 'dragEnabled', 'startDrag', 'stopDrag' ],

72
73
  data() {
    return {
74
      // Currently showing schema data
75
76
77
78
79
80
81
      schedule: [
        {a: {name: "Exsys", start: 2*6, stop: 2*8}},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b: {name: "Numla", start: 2*4, stop: 2*6}},
        {a: {name: "Numla TØ", start: 2*3, stop: 2*5}, b:{name: "Exsys", start: 2*6, stop: 2*7}},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b:{name: "Numla", start: 2*4, stop: 2*6}, c:{name: "Exsys TØ", start: 2*6, stop: 2*9}},
        {a: {name: "CompArk TØ", start: 0, stop: 2*3}}
      ],
82
83
84
      scheduleDay:[
        {a: {name: "Exsys", start: 2*6, stop: 2*8}}
      ],
85
86
      
      // Dummy data for week 12 
87
88
89
90
91
92
93
      scheduleWeek12: [
        {a: {name: "Exsys", start: 2*6, stop: 2*8}},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b: {name: "Numla", start: 2*4, stop: 2*6}},
        {a: {name: "Numla TØ", start: 2*3, stop: 2*5}, b:{name: "Exsys", start: 2*6, stop: 2*7}},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b:{name: "Numla", start: 2*4, stop: 2*6}, c:{name: "Exsys TØ", start: 2*6, stop: 2*9}},
        {a: {name: "CompArk TØ", start: 0, stop: 2*3}}
      ],
94
95

      // Dummy data for week 14 (Monday is a day off in this week)
96
97
98
99
100
101
102
      scheduleWeek14: [
        {},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b: {name: "Numla", start: 2*4, stop: 2*6}},
        {a: {name: "Numla TØ", start: 2*3, stop: 2*5}, b:{name: "Exsys", start: 2*6, stop: 2*7}},
        {a: {name: "CompArk", start: 2*2, stop: 2*4}, b:{name: "Numla", start: 2*4, stop: 2*6}, c:{name: "Exsys TØ", start: 2*6, stop: 2*9}},
        {a: {name: "CompArk TØ", start: 0, stop: 2*3}}
      ],
103
104
      
      // Dummy data for an empty week
105
106
107
      scheduleEmpty: [
        {},{},{},{},{}
      ],
108
109

      // Time intervals, each time segment is contained in a single tr in the table
110
      time: ["8:00", "9:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00"],
111
112
      
      // the size of an half hour of time in the schema
113
      halfHourSpace: [19],
114
      colSpanValue: [1],
Kent Nielsen's avatar
Kent Nielsen committed
115
      calendarDayWidth: [400],
116
117

      // Names of all month, used in the schema header
118
119
120
      monthName: ["January", "Februar", "Marts", "April", "Maj", "Juni",
                    "Juli", "August", "September", "Oktober", "November", "December"
                    ],
121
122

      // Shorthands of names of weekday, used in the schema header
Kent Nielsen's avatar
Kent Nielsen committed
123
      weekDays: ["Man", "Tirs", "Ons", "Tors", "Fre"],
124
125

      // The current date the schema is showing. This is changed on each movement of schema
126
127
128
129
      today: new Date()
    }
  },
  methods: {
Kent Nielsen's avatar
Kent Nielsen committed
130
131
132
    // Calculate how many days a movement in the calendar should be
    // forward: boolean - whether the move is forward or not, changes
    //                    the calculate on friday and monday
133
    daysToAdd (forward){
Kent Nielsen's avatar
Kent Nielsen committed
134
135
136
137
138
139
140
141
142
      // If in week view, move 7 days
      if(this.schedule.length != 1){
        return 7;
      }

      // If in day view and at friday or monday
      // The calendar should move 3 days
      if((this.today.getDay() == 5 && forward) || (this.today.getDay() == 1 && !forward)){
        return 3;
143
      }
Kent Nielsen's avatar
Kent Nielsen committed
144
145
146

      // Neither week view nor friday or monday
      return 1;
147
148
    },

Kent Nielsen's avatar
Kent Nielsen committed
149
150
151
152
153
154
    // Move the date back based on calendar view
    prevWeek(){
      const currentYear = this.today.getFullYear();
      const currentMonth = this.today.getMonth();
      const dayToMoveCalendarBackTo = this.today.getDate() - this.daysToAdd(false);
      this.today = new Date(currentYear, currentMonth, dayToMoveCalendarBackTo);
155
      this.changeSchedule();
Kent Nielsen's avatar
Kent Nielsen committed
156
      this.updateDaysInDisplay();
157
    },
158

Kent Nielsen's avatar
Kent Nielsen committed
159
    // Move the date forward based on calendar view
160
    nextWeek(){
Kent Nielsen's avatar
Kent Nielsen committed
161
162
163
164
      const currentYear = this.today.getFullYear();
      const currentMonth = this.today.getMonth();
      const dayToMoveCalendarForwardTo = this.today.getDate() + this.daysToAdd(true);
      this.today = new Date(currentYear, currentMonth, dayToMoveCalendarForwardTo);
165
      this.changeSchedule();
Kent Nielsen's avatar
Kent Nielsen committed
166
      this.updateDaysInDisplay();
167
    },
168
169

    // Changes the schedule to the current week, is invoked each time the date is changed
170
171
172
173
174
    changeSchedule(){
      const week = this.getWeek(this.today);
      if(week == "12"){this.schedule=this.scheduleWeek12}
      else if(week == "13"){
        this.schedule = this.scheduleEmpty;
Kent Nielsen's avatar
Kent Nielsen committed
175
      }
176
177
178
179
180
181
182
      else if(week == "14"){
        this.schedule=this.scheduleWeek14;
      }
      else if(week < 6 || week > 20){
        this.schedule=this.scheduleEmpty;
      }
      else{this.schedule = this.scheduleWeek12}
Kent Nielsen's avatar
Kent Nielsen committed
183
    },
184
185

    // Check for first index in a loop
186
    firstIndex(index){
Kent Nielsen's avatar
Kent Nielsen committed
187
      return index === 0;
188
    },
189

190
    // Source: https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
191
    // Calculate the ISO week, of the current date. Used to show week number
192
193
194
195
196
197
198
199
    getWeek(day) {
      var date = new Date(day.getTime());
      date.setHours(0, 0, 0, 0);
      date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
      var week1 = new Date(date.getFullYear(), 0, 4);
      return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
                      - 3 + (week1.getDay() + 6) % 7) / 7);
    },
200
201
202
203

    // Source: Stackexchange - lost link in battle
    // Calculate the first day of a iso week in a given year
    // Used to calculate the datenumber for the schema header
204
    getDateOfISOWeek(w, y, offset) {
205
206
207
208
209
210
211
212
213
      var simple = new Date(y, 0, 1 + (w - 1) * 7);
      var dow = simple.getDay();
      var ISOweekStart = simple;
      if (dow <= 4) {
        ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
      }
      else {
        ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
      }
Kent Nielsen's avatar
Kent Nielsen committed
214
      ISOweekStart.setDate(ISOweekStart.getDate() + offset);
215
216
      return ISOweekStart;
    },
217
218

    // Returns the month name of a given month number
219
220
    getMonthName(month){
      return this.monthName[month];
221
222
    },

Kent Nielsen's avatar
Kent Nielsen committed
223
    // Update the vertical size of events within the calendar
224
    updateHalfHourSize(){
Kent Nielsen's avatar
Kent Nielsen committed
225
      const tbody = document.getElementById("calendar").getElementsByTagName("tbody")[0];
226
      this.halfHourSpace = (parseInt(window.getComputedStyle(tbody).height)/(2*9));
227
228
    },

Kent Nielsen's avatar
Kent Nielsen committed
229
230
    // Update the amount of days in the display
    // Therefor changes form week to day view
231
    updateDaysInDisplay(){
Kent Nielsen's avatar
Kent Nielsen committed
232
      if(parseInt(document.getElementById("calendar").clientWidth) < this.calendarDayWidth){
233
234
        if(this.schedule.length == 5){
          this.colSpanValue = 5;
Kent Nielsen's avatar
Kent Nielsen committed
235
          const day = this.schedule[this.today.getDay() - 1];
236
237
          this.scheduleDay[0] = day;
          this.schedule = this.scheduleDay;
238
239
240
          const buttons = document.getElementsByTagName("button");
          for (let i = 0; i < buttons.length; i++) {
            const element = buttons[i];
Kent Nielsen's avatar
Kent Nielsen committed
241
            element.style.width = 40 + "px";
242
          }
243
        }
244
245
      }else{
        this.colSpanValue = 1;
Kent Nielsen's avatar
Kent Nielsen committed
246
        this.schedule = this.scheduleWeek12;
247
        this.changeSchedule();
248
249
        const buttons = document.getElementsByTagName("button");
        for (let i = 0; i < buttons.length; i++) {
250
          const element = buttons[i];
251
252
            element.style.width = 60 + "px";
          }
253
      }
254
      this.updateHalfHourSize();
255
256
    },

Kent Nielsen's avatar
Kent Nielsen committed
257
    // Initilized the current day on mount of calendar
258
259
260
    initToday(){
      const firstDayOfWeek = this.getDateOfISOWeek(this.getWeek(this.today), this.today.getFullYear(),0).getDate();
      const lastDayOfWeek = this.getDateOfISOWeek(this.getWeek(this.today), this.today.getFullYear(),4).getDate();
Kent Nielsen's avatar
Kent Nielsen committed
261
262
263
      const isCurrentDayAfterFriday = this.today.getDate() > lastDayOfWeek;
      const isCurrentDayBeforeMonday = this.today.getDate() < firstDayOfWeek;
      if(isCurrentDayAfterFriday || isCurrentDayBeforeMonday){
264
        while(this.today.getDate() < lastDayOfWeek){
Kent Nielsen's avatar
Kent Nielsen committed
265
266
267
268
          const currentYear = this.today.getFullYear();
          const currentMonth = this.today.getMonth();
          const dayToInitCalendarTo = this.today.getDate() - 1;
          this.today = new Date(currentYear, currentMonth, dayToInitCalendarTo);
269
270
        }
      }
Kent Nielsen's avatar
Kent Nielsen committed
271
    }
272
  },
Kent Nielsen's avatar
Kent Nielsen committed
273

274
  mounted(){
275
    this.initToday();
276
    this.updateHalfHourSize();
277
    this.updateDaysInDisplay();
278
    new ResizeObserver(() => this.updateDaysInDisplay()).observe(document.getElementById("calendar"));
Kent Nielsen's avatar
Kent Nielsen committed
279
  }
280
}
Kent Nielsen's avatar
Kent Nielsen committed
281
282
</script>

Kent Nielsen's avatar
Kent Nielsen committed
283
<style lang="scss" scoped>
Kent Nielsen's avatar
Kent Nielsen committed
284

Kent Nielsen's avatar
Kent Nielsen committed
285
286
  table{
    width: 100%;
287
    height: 100%;
Kent Nielsen's avatar
Kent Nielsen committed
288
289
    border-collapse: collapse;

290
291
292
293
    thead{
      border: 0;
      padding: 0;
      height: 100%;
294
      cursor: move;
295
296

      tr{
297
298
299
        &:first-of-type{
          height: 37px;
        }
300

301
        th{
302
303
          height: 28px;
          font-size: 106%;
304
          box-sizing: border-box;
305
306
307
308
          vertical-align: middel;
        }

        div{
Lasse Overgaard Møldrup's avatar
Lasse Overgaard Møldrup committed
309
          height: 28px;
310
          vertical-align: middle;
Lasse Overgaard Møldrup's avatar
Lasse Overgaard Møldrup committed
311
          margin-bottom: 4px;
312
313
314
315
        }
      }

      th{
316
317
        background-color: $theme-color;
        color: $lightest-theme-color;
318
        padding: 1px;        
319
320
      }

Kent Nielsen's avatar
Kent Nielsen committed
321
322
    }
    
Kent Nielsen's avatar
Kent Nielsen committed
323
    tbody{    
324
325
326
327
      tr{
        margin: 0px;
        padding: 0px;
      }
Kent Nielsen's avatar
Kent Nielsen committed
328
    }
329

Kent Nielsen's avatar
Kent Nielsen committed
330
331
332
333
334
    td{
      vertical-align: top;
      border:1px solid rgb(185, 185, 185);
      padding: 0;
      align-items: center;
335
      vertical-align: middle;
336

337
338
339
340
      &:first-of-type{
        width: 10%;
      }

Kent Nielsen's avatar
Kent Nielsen committed
341
342
343
344
      &:first-child{
        background-color: $theme-color;
        color: $lightest-theme-color;
      }
345
346
347
348
      a:hover{
        color: $text-color;
      }

Kent Nielsen's avatar
Kent Nielsen committed
349
      div{
350
        background-color: $light-theme-color+af;
Kent Nielsen's avatar
Kent Nielsen committed
351
352
353
354
355
356
        position: absolute;
        offset-position: 0 0;
        width: 90%;
        margin-left: 5%;
        margin-right: 5%;
        box-sizing: border-box;
357
        box-shadow: 1px 1px 2px;
358
        padding-top: 5px;
Kent Nielsen's avatar
Kent Nielsen committed
359

360
361
362
        &:hover{
          box-shadow: 1px 1px 6px;
        }
Kent Nielsen's avatar
Kent Nielsen committed
363
364
365
366
      }
    }
    
    tr{
367
368
369
370
371
372
373
      &:first-child td{
        border-top: none;
      }

      &:last-child td{
        border-bottom: none;
      }
374

Kent Nielsen's avatar
Kent Nielsen committed
375
376
377
      td:last-child {
        border-right: 0;
      }
Kent Nielsen's avatar
Kent Nielsen committed
378

Kent Nielsen's avatar
Kent Nielsen committed
379
380
      td:first-child {
        border-left: 0;
381
        padding: 0 3px 0 3px;
Kent Nielsen's avatar
Kent Nielsen committed
382
383
      }
    }
Kent Nielsen's avatar
Kent Nielsen committed
384

Kent Nielsen's avatar
Kent Nielsen committed
385
386
387
    button{
      cursor: pointer;
      border: 0;
388
389
390
391
392
      border-radius: 3px;
      box-sizing: border-box;
      width: 50%;
      height: 100%;
      font-size: 125%;
393
394
      background-color: $dark-theme-color;
      color: $lightest-theme-color;
395
      vertical-align: bottom;
396
      display: inline-block;
397
      padding-bottom: 11px;
398
399
      padding-top: 0px;
      box-sizing: border-box;
Kent Nielsen's avatar
Kent Nielsen committed
400
      box-shadow: 1px 1px 3px black;
Kent Nielsen's avatar
Kent Nielsen committed
401
    }
Kent Nielsen's avatar
Kent Nielsen committed
402
403
  }

Kent Nielsen's avatar
Kent Nielsen committed
404
405
406
407
408
409
410
411
412
413
  #rightButton{
    float: right;
    margin-right: $default-margin;
  }

  #leftButton{
    float: left;
    margin-left: $default-margin;
  }

414
415
416
417
418
419
420
421
422
423
424
425
426
427
  #headerDiv{
    display: flex;

    div{
      flex-grow: 20;
    }

    div+div{
      &:not(:last-of-type){
        flex-grow: 60;
      }
    }
  }

Kent Nielsen's avatar
Kent Nielsen committed
428
429
  #skematd{
    position: relative;
Kent Nielsen's avatar
Kent Nielsen committed
430
    vertical-align: top;
Kent Nielsen's avatar
Kent Nielsen committed
431
432
433
434
    
    div{
      overflow: hidden;
    }
Kent Nielsen's avatar
Kent Nielsen committed
435
436
437
  }
  
  #calendar{
438
    min-width: 250px;
439
    width: 100%;
440
    height: 100%;
441
    position: relative;
442
    box-sizing: border-box;
Kent Nielsen's avatar
Kent Nielsen committed
443
    overflow: auto;
Kent Nielsen's avatar
Kent Nielsen committed
444
  }
445
446
447
448
449
450
451
452

  #calendarHead{
    width: 100%;
    padding: 0;

    div{
      margin: 0;
      line-height: 28px;
453
454
455
456

      button{
        width: 60px;
      }
457
458
    }
  }
459
</style>