My fiddles

Features: API fetching, infinite scroll, lazy loading.

by Simon Herteby

HTML

<script src="https://unpkg.com/[email protected]/dist/vue-mugen-scroll.min.js"></script>
<div id="vue">
  <header>
    <span class="user">{{user}} -</span> My fiddles
  </header>
  <div v-for="fiddle in fiddles" :class="{fiddle:true,fullscreen:fullscreen == fiddle}" ref="fiddle">
    <h2>
      <a :href="fiddle.url">{{fiddle.title}}</a>
      <button @click="fullscreen ? fullscreen = false : fullscreen = fiddle">
        {{fullscreen ? 'Disable Fullscreen' : 'Fullscreen'}}
      </button>
    </h2>
    <p v-if="fiddle.description">{{fiddle.description}}</p>
    <iframe v-if="fiddle.show" :src="fiddle.url + 'embedded/result,js,html,css'" frameborder="0"></iframe>
  </div>
  <mugen-scroll v-if="loadMore" :handler="load" :should-handle="loadMore && !loading">Loading...</mugen-scroll>
  <div v-else class="mugen-scroll">You have reached the end.</div>
</div>

SCSS

body{
  font-family:sans-serif;
  margin:0;
  background:#c8d1d5;
}
a{
  text-decoration:inherit;
  color:inherit;
  &:hover{
    color:#0af !important;
  }
}
.mugen-scroll{
  font-size:20px;
  text-align:center;
  margin:20px;
}
header{
  background:#333;
  font-size:30px;
  padding:20px;
  color:white;
  .user{
    color:lightgrey;
  }
}
.fiddle{
  background:white;
  margin:20px;
  box-shadow:0px 1px 4px rgba(0,0,0,0.4);
  transition:0.2s;
  display:flex;
  flex-direction:column;
  min-height:400px;
  animation:appear 0.5s;
  &.fullscreen{
    position:fixed;
    top:0;
    bottom:0;
    left:0;
    right:0;
    z-index:100;
    margin:0;
  }
  h2{
    text-decoration:none;
    padding:10px;
    margin:0px;
    display:flex;
    button{
      margin-left:10px;
    }
  }
  p{
    margin:0 10px;
  }
  iframe{
    flex-grow:1;
  }
}
button{
  background:linear-gradient(#0af,#08f);
  color:white;
  border:none;
  border-radius:99px;
  padding:5px 15px;
  cursor:pointer;
  box-shadow:0px 1px 1px rgba(0,0,0,0.4);
  outline:none;
  &:hover{
    background:linear-gradient(#0bf,#09f);
  }
  &.big{
    display:block;
    margin:20px auto;
    font-size:16px;
    padding:10px 20px;
  }
  &[disabled]{
    background:grey;
    cursor:default;
  }
}
@keyframes appear{
  from{
    opacity:0;
  }
}

JavaScript

new Vue({
  el: '#vue',
  data: {
  	user:'Herteby',
    fiddles: [],
    fullscreen: false,
    start:0,
    loading:false,
    loadMore:true,
  },
  methods:{
  	load(){
    	this.loading = true
    	fetch(location.origin + '/api/user/' + this.user + '/demo/list.json?start=' + this.start, {
      	mode: 'same-origin'
    	})
    	.then(res => res.json())
    	.then(res => {
      	this.loading = false
      	if(!res.length){
        	this.loadMore = false
        } else {
        	res.forEach(fiddle => {
          	if(fiddle.title !== 'My fiddles'){ //Avoid infinite recursion!
            	this.fiddles.push(fiddle)
            }
          })
          this.$nextTick(this.scroll)
        }
      })
      this.start += 10
    },
    scroll(){
    	this.fiddles.forEach((fiddle, i) => {
      	if(!fiddle.show && this.$refs.fiddle[i].offsetTop < window.scrollY + window.innerHeight){
        	this.$set(fiddle, 'show', true)
        }
      })
    }
  },
  mounted(){
    window.addEventListener('scroll', this.scroll)
    window.addEventListener('resize', this.scroll)
  }
})