mirror of
https://github.com/KevinMidboe/zoff.git
synced 2025-12-29 05:21:01 +00:00
Playlist-element
This commit is contained in:
24
frontend/components/Styleguide.vue
Normal file
24
frontend/components/Styleguide.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Styleguide</h1>
|
||||||
|
<div class="row">
|
||||||
|
<h2>Playlist-element</h2>
|
||||||
|
<Playlist />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Playlist from "@/components/playlist/Playlist";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Playlist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
95
frontend/components/playlist/ContextMenu.vue
Normal file
95
frontend/components/playlist/ContextMenu.vue
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<div class="context-menu-container" :style="'top:' + mouseY + 'px;left:' + mouseX + 'px;'">
|
||||||
|
<ul>
|
||||||
|
<li>Copy link</li>
|
||||||
|
<li>Find similar</li>
|
||||||
|
<li>Added by {{ addedBy }}</li>
|
||||||
|
<hr />
|
||||||
|
<li>Delete</li>
|
||||||
|
</ul>
|
||||||
|
<div class="context-menu-background" @mouseout="closeSelf" @click="closeSelf"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
addedBy: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
mouseX: {
|
||||||
|
required: true,
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
mouseY: {
|
||||||
|
required: true,
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeSelf: function(e) {
|
||||||
|
if (e.toElement == null || e.toElement.nodeName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.toElement.nodeName == "UL" || e.toElement.nodeName == "LI") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit("closeContextMenu");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.context-menu-background {
|
||||||
|
background: #000000a0;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
position: absolute;
|
||||||
|
left: -85%;
|
||||||
|
top: -100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.context-menu-container {
|
||||||
|
font-family: sans-serif;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
z-index: 2;
|
||||||
|
list-style: none;
|
||||||
|
padding: 10px 0 10px 0;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
& li {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 15px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& hr {
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
144
frontend/components/playlist/Playlist.vue
Normal file
144
frontend/components/playlist/Playlist.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div class="playlist-conatiner">
|
||||||
|
<div class="song-container" v-for="(song, i) in paginatedList" :key="i">
|
||||||
|
<Song
|
||||||
|
class="song"
|
||||||
|
:title="song.title"
|
||||||
|
:thumbnail="song.thumbnail"
|
||||||
|
:votes="song.votes"
|
||||||
|
:addedBy="song.added_by"
|
||||||
|
:id="song.id"
|
||||||
|
:type="song.type"
|
||||||
|
@contextmenu="moreInfo"
|
||||||
|
/>
|
||||||
|
<div class="song-context-button" @click="moreInfo($event, song.id)">more</div>
|
||||||
|
</div>
|
||||||
|
<div class="pagination-buttons">
|
||||||
|
<button @click="firstPage" :disabled="disabledPrev" class="first"><</button>
|
||||||
|
<button @click="prevPage" :disabled="disabledPrev">previous</button>
|
||||||
|
<span>{{ page + 1 }} / {{ pages }}</span>
|
||||||
|
<button @click="nextPage" :disabled="disabledNext">next</button>
|
||||||
|
<button @click="lastPage" :disabled="disabledNext" class="last">></button>
|
||||||
|
</div>
|
||||||
|
<ContextMenu
|
||||||
|
v-if="contextMenuOpen"
|
||||||
|
:addedBy="contextOnElement.added_by"
|
||||||
|
:id="contextOnElement.id"
|
||||||
|
:type="contextOnElement.type"
|
||||||
|
:mouseX="mouseX"
|
||||||
|
:mouseY="mouseY"
|
||||||
|
@closeContextMenu="closeContextMenu"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Song from "@/components/playlist/Song";
|
||||||
|
import ContextMenu from "@/components/playlist/ContextMenu";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Song,
|
||||||
|
ContextMenu
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
paginatedList: function() {
|
||||||
|
return this.playlist.slice(
|
||||||
|
this.page * this.perPage,
|
||||||
|
(1 + this.page) * this.perPage
|
||||||
|
);
|
||||||
|
},
|
||||||
|
disabledPrev: function() {
|
||||||
|
return this.page == 0;
|
||||||
|
},
|
||||||
|
disabledNext: function() {
|
||||||
|
return this.playlist.length < (this.page + 1) * this.perPage;
|
||||||
|
},
|
||||||
|
pages: function() {
|
||||||
|
return Math.ceil(this.playlist.length / this.perPage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async beforeMount() {
|
||||||
|
const request = await fetch("https://zoff.me/api/list/summér", {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
const playlist = await request.json();
|
||||||
|
if (this.playlist.error == true) {
|
||||||
|
console.error(this.playlist.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.playlist = playlist.results;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
moreInfo: function(e, id) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.contextOnElement = this.playlist.find(song => song.id == id);
|
||||||
|
this.mouseX = e.pageX;
|
||||||
|
this.mouseY = e.pageY;
|
||||||
|
this.contextMenuOpen = true;
|
||||||
|
},
|
||||||
|
closeContextMenu: function() {
|
||||||
|
this.contextMenuOpen = false;
|
||||||
|
this.contextOnElement = null;
|
||||||
|
},
|
||||||
|
firstPage: function() {
|
||||||
|
this.page = 0;
|
||||||
|
},
|
||||||
|
lastPage: function() {
|
||||||
|
this.page = Math.floor(this.playlist.length / this.perPage);
|
||||||
|
},
|
||||||
|
prevPage: function() {
|
||||||
|
if (this.page == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.page -= 1;
|
||||||
|
},
|
||||||
|
nextPage: function() {
|
||||||
|
if (this.playlist.length < (this.page + 1) * this.perPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.page += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contextMenuOpen: false,
|
||||||
|
contextOnElement: {},
|
||||||
|
page: 0,
|
||||||
|
perPage: 10,
|
||||||
|
playlist: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.song-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
& .song {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
& .song-context-button {
|
||||||
|
width: 25%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& button {
|
||||||
|
width: 35%;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
&.first,
|
||||||
|
&.last {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
99
frontend/components/playlist/Song.vue
Normal file
99
frontend/components/playlist/Song.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div class="song-container">
|
||||||
|
<div class="song-voteable-container" @click="clickedSong" @contextmenu="$emit('contextmenu', $event, id)">
|
||||||
|
<div class="song-thumbnail">
|
||||||
|
<img :src="thumbnail" :alt="title" />
|
||||||
|
</div>
|
||||||
|
<div class="song-info">
|
||||||
|
<div class="song-title">{{ title }}</div>
|
||||||
|
<div class="song-votes">{{ votes }} vote{{votes > 1 ? "s" : null }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ContextMenu from "@/components/playlist/ContextMenu";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ContextMenu
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
thumbnail: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
required: true,
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
addedBy: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
required: true,
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contextMenuOpen: false,
|
||||||
|
mouseX: 0,
|
||||||
|
mouseY: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickedSong: function() {
|
||||||
|
console.log("Clicked on song with info", this.title, this.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.song-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.song-voteable-container {
|
||||||
|
width: 75%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& .song-thumbnail {
|
||||||
|
width: 25%;
|
||||||
|
|
||||||
|
& img {
|
||||||
|
width: 100%;
|
||||||
|
border-top-left-radius: 7.5px;
|
||||||
|
border-bottom-left-radius: 7.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .song-info {
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .song-mutation {
|
||||||
|
width: 25%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -9,6 +9,11 @@ let routes = [
|
|||||||
path: '/player',
|
path: '/player',
|
||||||
component: (resolve) => require(['./components/Player.vue'], resolve)
|
component: (resolve) => require(['./components/Player.vue'], resolve)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'styleguide',
|
||||||
|
path: '/styleguide',
|
||||||
|
component: (resolve) => require(['./components/Styleguide.vue'], resolve)
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// name: '404',
|
// name: '404',
|
||||||
// path: '/404',
|
// path: '/404',
|
||||||
|
|||||||
7766
package-lock.json
generated
7766
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,12 +21,11 @@
|
|||||||
"url": "https://github.com/zoff-music/zoff/issues"
|
"url": "https://github.com/zoff-music/zoff/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^6.0.3",
|
"cross-env": "~5.2",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-uglify": "^3.0.2",
|
"gulp-uglify": "^3.0.2",
|
||||||
|
"@babel/core": "~7.2",
|
||||||
"@babel/core": "~7.2",
|
|
||||||
"@babel/plugin-proposal-class-properties": "~7.3",
|
"@babel/plugin-proposal-class-properties": "~7.3",
|
||||||
"@babel/plugin-proposal-decorators": "~7.3",
|
"@babel/plugin-proposal-decorators": "~7.3",
|
||||||
"@babel/plugin-proposal-json-strings": "~7.2",
|
"@babel/plugin-proposal-json-strings": "~7.2",
|
||||||
@@ -35,7 +34,6 @@
|
|||||||
"@babel/preset-env": "~7.3",
|
"@babel/preset-env": "~7.3",
|
||||||
"babel-loader": "~8.0",
|
"babel-loader": "~8.0",
|
||||||
"compression-webpack-plugin": "~2.0",
|
"compression-webpack-plugin": "~2.0",
|
||||||
"cross-env": "~5.2",
|
|
||||||
"css-loader": "~0.28",
|
"css-loader": "~0.28",
|
||||||
"friendly-errors-webpack-plugin": "~1.7",
|
"friendly-errors-webpack-plugin": "~1.7",
|
||||||
"html-webpack-plugin": "~3.2",
|
"html-webpack-plugin": "~3.2",
|
||||||
@@ -90,9 +88,6 @@
|
|||||||
"socket.io-redis": "^5.2.0",
|
"socket.io-redis": "^5.2.0",
|
||||||
"sticky-session": "^1.1.2",
|
"sticky-session": "^1.1.2",
|
||||||
"uniqid": "5.0.3",
|
"uniqid": "5.0.3",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"@babel/polyfill": "~7.2",
|
"@babel/polyfill": "~7.2",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"vue": "~2.6",
|
"vue": "~2.6",
|
||||||
|
|||||||
Reference in New Issue
Block a user