mirror of
https://github.com/romancm/gamebrary
synced 2025-02-25 11:17:10 +00:00
Added loading placeholders
This commit is contained in:
parent
1bcd18715a
commit
dd0936c272
8 changed files with 1072 additions and 1063 deletions
88
src/components/GameBoard/GameBoardPlaceholder.vue
Normal file
88
src/components/GameBoard/GameBoardPlaceholder.vue
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="gameboard-placeholder">
|
||||||
|
<div class="list" v-for="list in lists" :key="list.name">
|
||||||
|
<div class="list-header" />
|
||||||
|
|
||||||
|
<div class="games">
|
||||||
|
<div class="game">
|
||||||
|
<placeholder
|
||||||
|
image
|
||||||
|
v-for="n in list.games.length"
|
||||||
|
:lines="2"
|
||||||
|
:key="n"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import Placeholder from '@/components/Placeholder/Placeholder';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Placeholder,
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState(['gameLists', 'platform']),
|
||||||
|
|
||||||
|
lists() {
|
||||||
|
return this.gameLists && this.gameLists[this.platform.code]
|
||||||
|
? this.gameLists[this.platform.code]
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
randomColumn() {
|
||||||
|
return Math.floor(Math.random() * 4) + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||||
|
@import "~styles/styles.scss";
|
||||||
|
|
||||||
|
.gameboard-placeholder {
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: default;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
background: $color-white;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: $list-width;
|
||||||
|
margin-right: $gp;
|
||||||
|
max-height: calc(100vh - 81px);
|
||||||
|
|
||||||
|
.list-header {
|
||||||
|
background: $color-dark-gray;
|
||||||
|
height: $list-header-height;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.games {
|
||||||
|
margin-top: $list-header-height;
|
||||||
|
width: 100%;
|
||||||
|
padding: $gp / 2;
|
||||||
|
|
||||||
|
.game {
|
||||||
|
.placeholder {
|
||||||
|
padding-right: $gp;
|
||||||
|
background: $color-white;
|
||||||
|
margin-bottom: $gp / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
110
src/components/GameDetail/GameDetailPlaceholder.vue
Normal file
110
src/components/GameDetail/GameDetailPlaceholder.vue
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div>
|
||||||
|
<div class="game-detail">
|
||||||
|
<div class="game-hero" />
|
||||||
|
|
||||||
|
<div class="game-detail-container">
|
||||||
|
<div class="game-info">
|
||||||
|
<placeholder image large />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<placeholder :lines="1" large class="title" />
|
||||||
|
<placeholder :lines="5" class="description" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Placeholder from '@/components/Placeholder/Placeholder';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Placeholder,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||||
|
@import "~styles/styles.scss";
|
||||||
|
|
||||||
|
.game-detail {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background: $color-light-gray;
|
||||||
|
min-height: calc(100vh - #{$navHeight});
|
||||||
|
|
||||||
|
@media($small) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
.game-detail-container {
|
||||||
|
background: #333;
|
||||||
|
color: $color-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-hero {
|
||||||
|
background-color: $color-dark-gray;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
height: 400px;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@media($small) {
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-info {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 180px auto;
|
||||||
|
grid-gap: $gp * 2;
|
||||||
|
margin: 0 $gp;
|
||||||
|
|
||||||
|
@media($small) {
|
||||||
|
grid-gap: 0;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.game-description {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-description {
|
||||||
|
line-height: 1.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-detail-container {
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
-webkit-box-shadow: 0 0 2px 0 $color-gray;
|
||||||
|
box-shadow: 0 0 2px 0 $color-gray;
|
||||||
|
width: $container-width;
|
||||||
|
max-width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
margin: 100px;
|
||||||
|
padding: $gp 0;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media($small) {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
86
src/components/Placeholder/Placeholder.vue
Normal file
86
src/components/Placeholder/Placeholder.vue
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="placeholder" :class="{ 'has-image': image, large }" v-if="image || lines">
|
||||||
|
<div class="image" v-if="image" />
|
||||||
|
|
||||||
|
<div class="text" v-if="lines">
|
||||||
|
<div
|
||||||
|
class="text-line"
|
||||||
|
v-for="n in lines"
|
||||||
|
:key="n"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
image: Boolean,
|
||||||
|
large: Boolean,
|
||||||
|
lines: Number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||||
|
@import "~styles/styles.scss";
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&.has-image {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 80px auto;
|
||||||
|
grid-gap: $gp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
@keyframes placeholder{
|
||||||
|
0%{
|
||||||
|
background-position: -468px 0
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
background-position: 468px 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animated-background {
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-name: placeholder;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
background: $color-light-gray;
|
||||||
|
background: linear-gradient(to right,
|
||||||
|
$color-light-gray 8%,
|
||||||
|
#F0F0F0 18%, $color-light-gray 33%);
|
||||||
|
background-size: 800px 104px;
|
||||||
|
height: 96px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
height: 120px;
|
||||||
|
width: 80px;
|
||||||
|
@extend .animated-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-line {
|
||||||
|
height: 12px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: $gp / 2;
|
||||||
|
@extend .animated-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
.text-line {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
height: 220px;
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -7,14 +7,7 @@
|
||||||
:class="{ dark: darkModeEnabled, 'drag-scroll-active': dragScrollActive }"
|
:class="{ dark: darkModeEnabled, 'drag-scroll-active': dragScrollActive }"
|
||||||
v-dragscroll:nochilddrag
|
v-dragscroll:nochilddrag
|
||||||
>
|
>
|
||||||
<template v-if="loading">
|
<game-board-placeholder v-if="loading" />
|
||||||
Loading...
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- <div v-else-if="!gameLists[platform.code]">
|
|
||||||
// TODO: SHOW FTU
|
|
||||||
Boom
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<list
|
<list
|
||||||
|
@ -22,7 +15,7 @@
|
||||||
:games="list.games"
|
:games="list.games"
|
||||||
:listIndex="listIndex"
|
:listIndex="listIndex"
|
||||||
:key="`${list.name}-${listIndex}`"
|
:key="`${list.name}-${listIndex}`"
|
||||||
v-if="list"
|
v-if="list && !loading"
|
||||||
v-for="(list, listIndex) in gameLists[platform.code]"
|
v-for="(list, listIndex) in gameLists[platform.code]"
|
||||||
@end="dragEnd"
|
@end="dragEnd"
|
||||||
@remove="tryDelete(listIndex)"
|
@remove="tryDelete(listIndex)"
|
||||||
|
@ -39,6 +32,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { dragscroll } from 'vue-dragscroll';
|
import { dragscroll } from 'vue-dragscroll';
|
||||||
import AddList from '@/components/Lists/AddList';
|
import AddList from '@/components/Lists/AddList';
|
||||||
|
import GameBoardPlaceholder from '@/components/GameBoard/GameBoardPlaceholder';
|
||||||
import Panel from '@/components/Panel/Panel';
|
import Panel from '@/components/Panel/Panel';
|
||||||
import { $success, $error, swal } from '@/shared/modals';
|
import { $success, $error, swal } from '@/shared/modals';
|
||||||
import List from '@/components/GameBoard/List';
|
import List from '@/components/GameBoard/List';
|
||||||
|
@ -58,6 +52,7 @@ export default {
|
||||||
draggable,
|
draggable,
|
||||||
List,
|
List,
|
||||||
AddList,
|
AddList,
|
||||||
|
GameBoardPlaceholder,
|
||||||
Panel,
|
Panel,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,7 +205,7 @@ export default {
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
margin-right: $gp;
|
margin-right: $gp;
|
||||||
width: 300px;
|
width: $list-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lists {
|
.lists {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template lang="html">
|
<template lang="html">
|
||||||
<!-- eslint-disable -->
|
<!-- eslint-disable -->
|
||||||
<div class="game-detail" v-if="game" :class="{ dark: darkModeEnabled }">
|
<div>
|
||||||
|
<game-detail-placeholder v-if="!game" />
|
||||||
|
|
||||||
|
<div class="game-detail" v-else :class="{ dark: darkModeEnabled }">
|
||||||
<div class="game-hero" :style="style" />
|
<div class="game-hero" :style="style" />
|
||||||
|
|
||||||
<div class="game-detail-container">
|
<div class="game-detail-container">
|
||||||
|
@ -45,6 +48,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -55,6 +59,7 @@ import GameScreenshots from '@/components/GameDetail/GameScreenshots';
|
||||||
import GameVideos from '@/components/GameDetail/GameVideos';
|
import GameVideos from '@/components/GameDetail/GameVideos';
|
||||||
import GameReviewBox from '@/components/GameDetail/GameReviewBox';
|
import GameReviewBox from '@/components/GameDetail/GameReviewBox';
|
||||||
import AffiliateLink from '@/components/GameDetail/AffiliateLink';
|
import AffiliateLink from '@/components/GameDetail/AffiliateLink';
|
||||||
|
import GameDetailPlaceholder from '@/components/GameDetail/GameDetailPlaceholder';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -63,6 +68,7 @@ export default {
|
||||||
GameVideos,
|
GameVideos,
|
||||||
GameReviewBox,
|
GameReviewBox,
|
||||||
AffiliateLink,
|
AffiliateLink,
|
||||||
|
GameDetailPlaceholder,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template lang="html">
|
<template lang="html">
|
||||||
<main v-dragscroll:nochilddrag>
|
<main v-dragscroll:nochilddrag>
|
||||||
<span v-if="loading">Loading...</span>
|
<game-board-placeholder v-if="loading" />
|
||||||
|
|
||||||
<section
|
<section
|
||||||
v-if="!loading && listData && publicGameData"
|
v-if="!loading && listData && publicGameData"
|
||||||
|
@ -9,11 +9,9 @@
|
||||||
>
|
>
|
||||||
<header>{{ list.name }} ({{ list.games.length }})</header>
|
<header>{{ list.name }} ({{ list.games.length }})</header>
|
||||||
|
|
||||||
<div class="empty-list" v-if="!list.games.length">
|
<div class="games">
|
||||||
<h3>This collection is empty</h3>
|
<p v-if="!list.games.length">This list is empty</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="games" v-else>
|
|
||||||
<div
|
<div
|
||||||
v-if="publicGameData[game]"
|
v-if="publicGameData[game]"
|
||||||
class="game-card"
|
class="game-card"
|
||||||
|
@ -35,6 +33,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import firebase from 'firebase/app';
|
import firebase from 'firebase/app';
|
||||||
|
import GameBoardPlaceholder from '@/components/GameBoard/GameBoardPlaceholder';
|
||||||
import { swal } from '@/shared/modals';
|
import { swal } from '@/shared/modals';
|
||||||
import { dragscroll } from 'vue-dragscroll';
|
import { dragscroll } from 'vue-dragscroll';
|
||||||
import { mapState } from 'vuex';
|
import { mapState } from 'vuex';
|
||||||
|
@ -48,6 +47,10 @@ db.settings({
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
GameBoardPlaceholder,
|
||||||
|
},
|
||||||
|
|
||||||
directives: {
|
directives: {
|
||||||
dragscroll,
|
dragscroll,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@ $gp: 16px;
|
||||||
$navHeight: 48px;
|
$navHeight: 48px;
|
||||||
$container-width: 900px;
|
$container-width: 900px;
|
||||||
$list-header-height: 30px;
|
$list-header-height: 30px;
|
||||||
|
$list-width: 300px;
|
||||||
|
|
||||||
// Media queries
|
// Media queries
|
||||||
$small: "max-width: 780px";
|
$small: "max-width: 780px";
|
||||||
|
|
Loading…
Add table
Reference in a new issue