mirror of
https://github.com/romancm/gamebrary
synced 2024-11-23 19:53:14 +00:00
Progress styling (#164)
* style progress bar/range * add progress percentage next to bar * Linting * Separate GameProgress and GameProgressModal markup * Display process percentage within/on top of range * refactor progress element * add missing gameprogress it got lost in the rebase * add margin and center progress on mobile
This commit is contained in:
parent
01be6fc7e5
commit
52af31e488
9 changed files with 339 additions and 105 deletions
|
@ -22,11 +22,11 @@
|
|||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<progress
|
||||
<game-progress
|
||||
v-if="gameProgress"
|
||||
max="100"
|
||||
:value="gameProgress"
|
||||
@click="openDetails"
|
||||
small
|
||||
:progress="gameProgress"
|
||||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<i
|
||||
|
@ -59,12 +59,14 @@
|
|||
|
||||
<script>
|
||||
import GameRating from '@/components/GameDetail/GameRating';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameCardUtils from '@/components/GameCards/GameCard';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameRating,
|
||||
GameProgress,
|
||||
Tag,
|
||||
},
|
||||
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
<i class="fas fa-grip-vertical game-drag-handle" />
|
||||
|
||||
<progress
|
||||
<game-progress
|
||||
v-if="gameProgress"
|
||||
max="100"
|
||||
:value="gameProgress"
|
||||
@click="openDetails"
|
||||
small
|
||||
:progress="gameProgress"
|
||||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<game-rating
|
||||
|
@ -60,12 +60,14 @@
|
|||
|
||||
<script>
|
||||
import GameRating from '@/components/GameDetail/GameRating';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameCardUtils from '@/components/GameCards/GameCard';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameRating,
|
||||
GameProgress,
|
||||
Tag,
|
||||
},
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<progress
|
||||
<game-progress
|
||||
v-if="gameProgress"
|
||||
max="100"
|
||||
:value="gameProgress"
|
||||
@click="openDetails"
|
||||
small
|
||||
:progress="gameProgress"
|
||||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<i
|
||||
|
@ -53,12 +53,14 @@
|
|||
|
||||
<script>
|
||||
import GameRating from '@/components/GameDetail/GameRating';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameCardUtils from '@/components/GameCards/GameCard';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameRating,
|
||||
GameProgress,
|
||||
Tag,
|
||||
},
|
||||
|
||||
|
@ -159,7 +161,7 @@ export default {
|
|||
grid-row: 2;
|
||||
}
|
||||
|
||||
.game-progresses {
|
||||
.game-progress {
|
||||
justify-self: end;
|
||||
grid-column: 2;
|
||||
grid-row: span 2;
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<progress
|
||||
<game-progress
|
||||
v-if="gameProgress"
|
||||
max="100"
|
||||
:value="gameProgress"
|
||||
@click="openDetails"
|
||||
small
|
||||
:progress="gameProgress"
|
||||
@click.native="openDetails"
|
||||
/>
|
||||
|
||||
<i
|
||||
|
@ -50,12 +50,14 @@
|
|||
|
||||
<script>
|
||||
import GameRating from '@/components/GameDetail/GameRating';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameCardUtils from '@/components/GameCards/GameCard';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameRating,
|
||||
GameProgress,
|
||||
Tag,
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template lang="html">
|
||||
<div class="game-actions">
|
||||
<game-progress />
|
||||
<game-progress-modal />
|
||||
<game-notes />
|
||||
|
||||
<div v-if="hasTags" class="tags">
|
||||
|
@ -36,12 +36,12 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import GameNotes from '@/components/GameDetail/GameNotes';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameProgressModal from '@/components/GameDetail/GameProgressModal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameNotes,
|
||||
GameProgress,
|
||||
GameProgressModal,
|
||||
},
|
||||
|
||||
props: {
|
||||
|
|
|
@ -1,103 +1,122 @@
|
|||
<template lang="html">
|
||||
<modal :title="$t('progresses.modalTitle')" ref="progressModal">
|
||||
<button class="primary" :title="buttonLabel">
|
||||
<i class="fas fa-clock" />
|
||||
</button>
|
||||
|
||||
<div slot="content">
|
||||
<h2>{{ localProgress }}%</h2>
|
||||
|
||||
<input
|
||||
v-model="localProgress"
|
||||
type="range"
|
||||
max="100"
|
||||
step="5"
|
||||
@change="saveProgress"
|
||||
>
|
||||
|
||||
<button class="danger" @click="deleteProgress">
|
||||
{{ $t('progresses.deleteProgress') }}
|
||||
</button>
|
||||
<div :class="['game-progress', { small }]">
|
||||
<div
|
||||
class="progress"
|
||||
ref="progress"
|
||||
:style="`--progress: ${progress}%; --progress-width: ${width}px`"
|
||||
>
|
||||
<div
|
||||
class="progress-bar-label not-progressed"
|
||||
:data-progress="progress"
|
||||
></div>
|
||||
<div
|
||||
class="progress-bar-label progressed"
|
||||
:data-progress="progress"
|
||||
></div>
|
||||
</div>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '@/components/Modal';
|
||||
import { debounce } from 'lodash';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
props: {
|
||||
progress: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
small: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
localProgress: {},
|
||||
width: 0,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['gameProgress']),
|
||||
|
||||
defaultValue() {
|
||||
return {
|
||||
value: 0,
|
||||
};
|
||||
},
|
||||
|
||||
buttonLabel() {
|
||||
return this.gameProgress
|
||||
? this.$t('progresses.updateProgress')
|
||||
: this.$t('progresses.addProgress');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
gameProgress() {
|
||||
this.reset();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.reset();
|
||||
this.getProgressBarWidth();
|
||||
},
|
||||
|
||||
methods: {
|
||||
reset() {
|
||||
this.localProgress = this.gameProgress
|
||||
? JSON.parse(JSON.stringify(this.gameProgress))
|
||||
: 0;
|
||||
getProgressBarWidth() {
|
||||
this.width = this.$refs.progress.clientWidth;
|
||||
},
|
||||
|
||||
async deleteProgress() {
|
||||
this.$store.commit('REMOVE_GAME_PROGRESS');
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES_NO_MERGE')
|
||||
.catch(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'There was an error deleting your progress', type: 'error' });
|
||||
this.$router.push({ name: 'sessionExpired' });
|
||||
});
|
||||
|
||||
this.$bus.$emit('TOAST', { message: 'Progress deleted' });
|
||||
this.$refs.progressModal.close();
|
||||
},
|
||||
|
||||
saveProgress: debounce(
|
||||
// eslint-disable-next-line
|
||||
function () {
|
||||
this.$store.commit('SET_GAME_PROGRESS', this.localProgress);
|
||||
|
||||
this.$store.dispatch('SAVE_PROGRESSES')
|
||||
.then(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'Progress updated' });
|
||||
})
|
||||
.catch(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'There was an error saving your progress', type: 'error' });
|
||||
this.$router.push({ name: 'sessionExpired' });
|
||||
});
|
||||
}, 300),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
@import "~styles/styles";
|
||||
|
||||
.game-progress {
|
||||
display: grid;
|
||||
width: 140px;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
margin: $gp / 2 0;
|
||||
|
||||
@media($small) {
|
||||
margin: $gp auto;
|
||||
}
|
||||
|
||||
.progress {
|
||||
display: grid;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
margin: $gp / 4 0;
|
||||
overflow: hidden;
|
||||
border-radius: $border-radius / 2;
|
||||
}
|
||||
|
||||
.progress-bar-label {
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: attr(data-progress) "%";
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
width: 140px;
|
||||
text-align: center;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
&.not-progressed {
|
||||
background: var(--list-background);
|
||||
}
|
||||
|
||||
&.progressed {
|
||||
z-index: 1;
|
||||
width: var(--progress);
|
||||
background: var(--accent-color);
|
||||
|
||||
&::after {
|
||||
color: var(--game-card-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.small {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
|
||||
.progress {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.progress-bar-label {
|
||||
font-size: $font-size-xsmall;
|
||||
|
||||
&::after {
|
||||
width: var(--progress-width);
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
103
src/components/GameDetail/GameProgressModal.vue
Normal file
103
src/components/GameDetail/GameProgressModal.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<template lang="html">
|
||||
<modal :title="$t('progresses.modalTitle')" ref="progressModal">
|
||||
<button class="primary" :title="buttonLabel">
|
||||
<i class="fas fa-clock" />
|
||||
</button>
|
||||
|
||||
<div slot="content">
|
||||
<h2>{{ localProgress }}%</h2>
|
||||
|
||||
<input
|
||||
v-model="localProgress"
|
||||
type="range"
|
||||
max="100"
|
||||
step="5"
|
||||
@change="saveProgress"
|
||||
>
|
||||
|
||||
<button class="danger" @click="deleteProgress">
|
||||
{{ $t('progresses.deleteProgress') }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '@/components/Modal';
|
||||
import { debounce } from 'lodash';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
localProgress: {},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['gameProgress']),
|
||||
|
||||
defaultValue() {
|
||||
return {
|
||||
value: 0,
|
||||
};
|
||||
},
|
||||
|
||||
buttonLabel() {
|
||||
return this.gameProgress
|
||||
? this.$t('progresses.updateProgress')
|
||||
: this.$t('progresses.addProgress');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
gameProgress() {
|
||||
this.reset();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.reset();
|
||||
},
|
||||
|
||||
methods: {
|
||||
reset() {
|
||||
this.localProgress = this.gameProgress
|
||||
? JSON.parse(JSON.stringify(this.gameProgress))
|
||||
: 0;
|
||||
},
|
||||
|
||||
async deleteProgress() {
|
||||
this.$store.commit('REMOVE_GAME_PROGRESS');
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES_NO_MERGE')
|
||||
.catch(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'There was an error deleting your progress', type: 'error' });
|
||||
this.$router.push({ name: 'sessionExpired' });
|
||||
});
|
||||
|
||||
this.$bus.$emit('TOAST', { message: 'Progress deleted' });
|
||||
this.$refs.progressModal.close();
|
||||
},
|
||||
|
||||
saveProgress: debounce(
|
||||
// eslint-disable-next-line
|
||||
function () {
|
||||
this.$store.commit('SET_GAME_PROGRESS', this.localProgress);
|
||||
|
||||
this.$store.dispatch('SAVE_PROGRESSES')
|
||||
.then(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'Progress updated' });
|
||||
})
|
||||
.catch(() => {
|
||||
this.$bus.$emit('TOAST', { message: 'There was an error saving your progress', type: 'error' });
|
||||
this.$router.push({ name: 'sessionExpired' });
|
||||
});
|
||||
}, 300),
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -22,14 +22,14 @@
|
|||
<h2>{{ games[id].name }}</h2>
|
||||
<h4>{{ platform.name }}</h4>
|
||||
|
||||
<progress
|
||||
<game-progress
|
||||
v-if="gameProgress"
|
||||
max="100"
|
||||
:value="gameProgress"
|
||||
:progress="gameProgress"
|
||||
/>
|
||||
|
||||
<game-rating v-if="games[id].rating" :rating="games[id].rating" />
|
||||
<game-tags />
|
||||
|
||||
<!-- TODO: set list id to store instead of passing it around -->
|
||||
<game-actions :list-id="listId" />
|
||||
</div>
|
||||
|
@ -68,6 +68,7 @@
|
|||
import { mapState, mapGetters } from 'vuex';
|
||||
import GameScreenshots from '@/components/GameDetail/GameScreenshots';
|
||||
import VueMarkdown from 'vue-markdown';
|
||||
import GameProgress from '@/components/GameDetail/GameProgress';
|
||||
import GameActions from '@/components/GameDetail/GameActions';
|
||||
import GameTags from '@/components/GameDetail/GameTags';
|
||||
import GameRating from '@/components/GameDetail/GameRating';
|
||||
|
@ -89,6 +90,7 @@ export default {
|
|||
GameScreenshots,
|
||||
GameActions,
|
||||
VueMarkdown,
|
||||
GameProgress,
|
||||
GameTags,
|
||||
GameVideos,
|
||||
GameDetails,
|
||||
|
|
|
@ -76,3 +76,105 @@ textarea {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
border: 0;
|
||||
border-radius: $border-radius;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
order: 1;
|
||||
margin: $gp 0;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
&::-webkit-slider-runnable-track,
|
||||
&::-ms-fill-lower,
|
||||
&::-ms-fill-upper {
|
||||
background: var(--accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
background: var(--accent-color);
|
||||
border-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
background: var(--primary-background);
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
box-shadow: 500px 0 0 500px var(--list-background);
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
overflow: hidden;
|
||||
background: var(--list-background);
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: var(--primary-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-moz-range-progress {
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: var(--accent-color);
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
border-width: 16px 0;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background: var(--accent-color);
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
&::-ms-fill-upper {
|
||||
background: var(--accent-color);
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border-radius: 0;
|
||||
background: var(--primary-background);
|
||||
box-shadow: 500px 0 0 500px var(--list-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue