Home.vue 8.66 KB
Newer Older
1
<template>
2
  <div class="home">
3
    <div id="grid">
Kent Nielsen's avatar
Kent Nielsen committed
4
5
      <div @mousedown="resizeVertical($event, rowIndex)" class="row" v-for="(rowBlock, rowIndex) in layout" :key="rowIndex" :style="{ height: rowBlock.height + 'px' }">
        <div @mousedown="resizeHorizontal($event, rowIndex, colIndex)" class="col" v-for="(colBlock, colIndex) in rowBlock.blocks" :key="colIndex" :style="{ 'flex-grow': colBlock.width }">
Johan Degn's avatar
Johan Degn committed
6
          <component :is="colBlock.type"></component>
7
8
        </div>
      </div>
Johan Degn's avatar
Johan Degn committed
9
10
11
      <button class="add-block" @click="addBlock()">
        <img v-svg-inline class="icon" src="@/assets/collapse-plus.svg"/>
      </button>
12
    </div>
13
14
15
16
  </div>
</template>

<script>
17
18
19
20
import ActiveCourses from "@/components/blocks/ActiveCourses";
import Deadlines from "@/components/blocks/Deadlines";
import Feed from "@/components/blocks/Feed";
import Mail from "@/components/blocks/Mail";
21
import Calendar from "@/components/blocks/Calendar";
22

Anders B. Clausen's avatar
Anders B. Clausen committed
23
const sendServerRequest = (type, payload) => {
24
25
26
27
28
29
30
31
32
  const xhr = new XMLHttpRequest();
  const url = "/persistence";

  xhr.open("GET", url + "?type=" + type + "&" + payload);
  xhr.send();

  return xhr;
}

33
34
export default {
  name: 'Home',
35
36
37

  data() {
    return {
Johan Degn's avatar
WIP    
Johan Degn committed
38
      layout: [
39
        {
Johan Degn's avatar
Johan Degn committed
40
41
          height: 400,
          blocks: [{type: "Calendar", width: 75}, {type: "ActiveCourses", width: 25}]
42
43
        },
        {
Johan Degn's avatar
Johan Degn committed
44
45
          height: 250,
          blocks: [{type: "Deadlines", width: 100}]
46
47
        },
        {
Johan Degn's avatar
Johan Degn committed
48
49
          height: 300,
          blocks: [{type: "Feed", width: 50}, {type: "Mail", width: 25}, {type: "Mail", width: 25}]
Johan Degn's avatar
WIP    
Johan Degn committed
50
        }
51
      ],
52
      mouseDragPos: {x: 0, y: 0},
Kent Nielsen's avatar
Kent Nielsen committed
53
      verticalResizeObject: {index: 0},
Kent Nielsen's avatar
Kent Nielsen committed
54
      horizontalResizeObject: {row: 0, col: 0},
55
      prevWinWidth: 1000
56
57
58
59
    }
  },

  methods: {
Johan Degn's avatar
Johan Degn committed
60
61
62
63
    addBlock() {
      this.layout.push({
        height: 300,
        blocks: [{ type: "ActiveCourses", width: 30 },{ type: "Feed", width: 70 }]
64
      });
65
66
    },

67
    resizeHorizontalMouseMove(event) {
68
69
      const deltaX = this.mouseDragPos.x - event.x;
      this.mouseDragPos.x = event.x;
Kent Nielsen's avatar
Kent Nielsen committed
70
71
72
      const row = this.horizontalResizeObject.row;
      const col = this.horizontalResizeObject.col;

Kent Nielsen's avatar
Kent Nielsen committed
73
      // Find index of select col in html document
Kent Nielsen's avatar
Kent Nielsen committed
74
75
76
77
78
79
80
      let colIndex = col;
      for (let i = 0; i < row; i++) {
        const element = this.layout[i];
        colIndex += element.blocks.length;
      }
      const leftCol = document.getElementsByClassName("col")[colIndex - 1];
      const rigthCol = document.getElementsByClassName("col")[colIndex];
Kent Nielsen's avatar
Kent Nielsen committed
81
82
83
      
      // Total flex allowed from layout
      const totalFlex = this.layout[row].blocks[col -1].width + this.layout[row].blocks[col].width;
Kent Nielsen's avatar
Kent Nielsen committed
84
85
      const contentWidth = leftCol.clientWidth + rigthCol.clientWidth + 40;
      
Kent Nielsen's avatar
Kent Nielsen committed
86
87
88
89
90
91
92
93
94
95
96
97
98
      // Resized size
      const newLeftWidth = leftCol.clientWidth - deltaX;
      const newRightWidth = rigthCol.clientWidth + deltaX;

      // Calculating new flexGrow value
      // This is not normalized to 100"%"
      let newFlexRight = contentWidth/newRightWidth;
      let newFlexLeft = (newLeftWidth * parseFloat(newFlexRight))/newRightWidth;

      // Normalizing flewGrow to 100"%"
      const preAdjustedTotalFlex = newFlexRight+newFlexLeft;
      newFlexRight = (newFlexRight/preAdjustedTotalFlex)*totalFlex;
      newFlexLeft = (newFlexLeft/preAdjustedTotalFlex)*totalFlex;
Kent Nielsen's avatar
Kent Nielsen committed
99
      
Kent Nielsen's avatar
Kent Nielsen committed
100
101
102
      // Storing new style.
      this.layout[row].blocks[col -1].width = newFlexLeft;
      this.layout[row].blocks[col].width = newFlexRight;
103
104
    },

105
106
107
    resizeVerticalMouseMove(event){
      const deltaY = this.mouseDragPos.y - event.y;
      this.mouseDragPos.y = event.y;
Kent Nielsen's avatar
Kent Nielsen committed
108
      this.layout[this.verticalResizeObject.index].height -= deltaY
109
110
    },

Kent Nielsen's avatar
Kent Nielsen committed
111
    resizeHorizontal(event, row, col){
112
      if(event.offsetX < 0){
113
        this.mouseDragPos.x = event.x;
Kent Nielsen's avatar
Kent Nielsen committed
114
115
        this.horizontalResizeObject.row = row;
        this.horizontalResizeObject.col = col;
116
        document.addEventListener("mousemove", this.resizeHorizontalMouseMove, false);
Kent Nielsen's avatar
Kent Nielsen committed
117
        document.addEventListener("mouseup", this.eventRemover, false);
118
        document.getElementsByTagName("html")[0].classList.add("no-select");
119
      }
120
121
    },

Kent Nielsen's avatar
Kent Nielsen committed
122
123
    resizeVertical(event, index){
      if(event.offsetY > this.layout[index].height){
Kent Nielsen's avatar
Kent Nielsen committed
124
        this.mouseDragPos.y = event.y;
125
        this.verticalResizeObject.index = index; 
Kent Nielsen's avatar
Kent Nielsen committed
126
127
        document.addEventListener("mousemove", this.resizeVerticalMouseMove, false);
        document.addEventListener("mouseup", this.eventRemover, false);
128
        document.getElementsByTagName("html")[0].classList.add("no-select");
129
      }
Kent Nielsen's avatar
Kent Nielsen committed
130
131
132
133
    },

    eventRemover(){
      document.removeEventListener("mousemove", this.resizeHorizontalMouseMove, false);
Kent Nielsen's avatar
Kent Nielsen committed
134
135
      document.removeEventListener("mousemove", this.resizeVerticalMouseMove, false);
      document.removeEventListener("mouseup", this.eventRemover, false);
136
      document.getElementsByTagName("html")[0].classList.remove("no-select");
Kent Nielsen's avatar
Kent Nielsen committed
137
138
139
140
    },

    mobileLayoutResize(){
      const winWidth = window.innerWidth;
Kent Nielsen's avatar
Kent Nielsen committed
141
142
143
144
145
146
      if((this.prevWinWidth - 992) > 0 && (winWidth - 992) > 0){
        return;
      }
      if((this.prevWinWidth - 992) < 0 && (winWidth - 992) < 0){
        return;
      }
Kent Nielsen's avatar
Kent Nielsen committed
147
148
149
150
151
152
153
154

      const rows = document.getElementsByClassName("row");
      for (let i = 0; i < rows.length; i++) {
        const row = rows[i];
        const cols = row.getElementsByClassName("col");
        for (let j = 0; j < cols.length; j++) {
          const col = cols[j];
          if(winWidth < 992){
155
            col.style.flexGrow = 0;
Kent Nielsen's avatar
Kent Nielsen committed
156
157
158
159
160
          }else{
            col.style.flexGrow = this.layout[i].blocks[j].width;
          }
        }
        if(winWidth < 992){
161
          row.style.height = 'auto';
Kent Nielsen's avatar
Kent Nielsen committed
162
163
164
        }else{
          row.style.height = this.layout[i].height + 'px';
        }
Kent Nielsen's avatar
Kent Nielsen committed
165
166
      }
      this.prevWinWidth = winWidth;      
167
    }
168

169
170
171
172
    removeBlock(side, row, col) {
      this[side][row].blocks.splice(col, 1);
      if (this[side][row].blocks.length === 0)
        this[side].splice(row, 1);
173
174
    },

175
    saveLayout(id) {
176
      const type = "save"
Anders B. Clausen's avatar
Anders B. Clausen committed
177
      const payload = "id=" + id + "&" + "data=" + JSON.stringify(this.$data);
178

179
      const request = sendServerRequest(type, payload);
180
181
182
183
      request.onload = () => {
        const response = request.response;
        console.log(response);
      }
184
185
186
    },

    loadLayout(id) {
187
188
      const type = "load"
      const payload = "id=" + id;
189

190
      const request = sendServerRequest(type, payload);
Anders B. Clausen's avatar
Anders B. Clausen committed
191
192
      request.onload = () => {
        const response = request.response;
193
        Object.assign(this.$data, JSON.parse(response));
194
      }
195
    },
196
197
  },

Kent Nielsen's avatar
Kent Nielsen committed
198
199
  mounted(){
    this.mobileLayoutResize();
200
    this.prevWinWidth = window.innerWidth;
Kent Nielsen's avatar
Kent Nielsen committed
201
202
203
    window.addEventListener('resize', this.mobileLayoutResize);
  },

204
205
206
207
  updated(){
    this.mobileLayoutResize();
  },

208
209
  components: {
    ActiveCourses,
210
211
    Deadlines,
    Feed,
212
213
    Mail,
    Calendar
214
  }
215
216
}
</script>
217
218

<style scoped lang="scss">
219
  $gap: $default-margin;
220
221
222

  #grid {
    width: 100%;
Kent Nielsen's avatar
Kent Nielsen committed
223
    padding: $gap/2;
224
225
226
227
  }

  .row {
    display: flex;
Kent Nielsen's avatar
Kent Nielsen committed
228
229
    margin-bottom: $gap/2;
    flex-direction: column;
Kent Nielsen's avatar
Kent Nielsen committed
230
    position: relative;
Kent Nielsen's avatar
Kent Nielsen committed
231
    min-height: 150px
Kent Nielsen's avatar
Kent Nielsen committed
232
233
  }

Kent Nielsen's avatar
Kent Nielsen committed
234
  // Must be outside ".row" or else after affect children
Kent Nielsen's avatar
Kent Nielsen committed
235
  .row::after{
236
237
238
239
240
241
242
    content: " ";
    background-color: rgba(0, 0, 0, 0);
    position: absolute;
    bottom: - $gap;
    width: 100%;
    height: $gap;
    cursor: n-resize;
243
  }
244

Johan Degn's avatar
Johan Degn committed
245
246
  .col {
    flex-basis: 0; // a basis of 0px. very important!
247
    background-color: $light-green-theme-color;
Johan Degn's avatar
Johan Degn committed
248
    border-radius: 4px;
249
    border: 2px solid $theme-color;
Kent Nielsen's avatar
Kent Nielsen committed
250
    position: relative;
251

Johan Degn's avatar
Johan Degn committed
252
    &:not(:last-child) {
Kent Nielsen's avatar
Kent Nielsen committed
253
      margin-bottom: $gap / 2;
254
255
    }
  }
Johan Degn's avatar
Johan Degn committed
256
  
257
258
259
260
261
262
  .add-block {
    width: 50px;
    height: 50px;
    border: none;
    background: none;
    padding: 0;
Kent Nielsen's avatar
Kent Nielsen committed
263
    display: none;
264
265

    svg {
266
      fill: #777f80;
267
268
269
270
271
272
    }

    &:hover, &:focus {
      cursor: pointer;

      svg {
273
        fill: $darker-theme-color;
274
275
276
      }
    }
  }
Kent Nielsen's avatar
Kent Nielsen committed
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

  @media only screen and (min-width: $mobile-width-cutoff) {
    #grid{
      padding: $gap;
    }

    .row{
      flex-direction: row;
      margin-bottom: $gap;
    }

    .col{
      flex-grow: 0;

       &:not(:first-child) {
        margin-left: $gap / 2;
        
        &::before{
          content: " ";
          background-color: rgba(0, 0, 0, 0);
          position: absolute;
          left: - $gap - 2px;
          width: $gap;
          height: 100%;
          cursor: w-resize;
        }
      }
      &:not(:last-child) {
        margin-right: $gap / 2;
        margin-bottom: 0;
      }
    }

    .add-block{
      display: initial;
    }

    // Must be outside ".row" or else after affect children
    .row::after{
          content: " ";
          background-color: rgba(0, 0, 0, 0);
          position: absolute;
          bottom: - $gap;
          width: 100%;
          height: $gap;
          cursor: n-resize;
    }
  }
325
</style>