/ Basic nuxt setup (some areas still broken)

This commit is contained in:
Alexis
2021-03-26 12:23:54 +01:00
parent 19ee29b936
commit 5ca4f4eceb
56 changed files with 19612 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
<template>
<div class="navbar">
<ul class="navbar-menu no-style">
<li v-for="item in items" :key="item.url" class="navbar-item">
<router-link :to="item.link" class="navbar-link no-style">
{{ item.text }}
</router-link>
</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: 'Navbar',
data () {
return {
items: [
{
text: 'Home',
link: '/'
},
{
text: 'Astres',
link: '/astres'
}
]
}
}
})
</script>
<style lang="scss" scoped>
.navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 70px;
padding: 0 20px;
background: $fs-black;
box-shadow: 0 5px 5px rgba($black, 0.3);
z-index: 9999;
&:after {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 50%;
display: block;
content: "";
background-image: url("/nav-bg.png");
background-size: cover;
background-position: center right;
opacity: 0.75;
}
.navbar-menu {
display: flex;
align-items: center;
height: 70px;
line-height: 70px;
.navbar-item {
&:not(:first-child) {
padding-left: 20px;
}
.navbar-link {
color: $white;
position: relative;
padding: 0 8px;
&:after {
display: block;
content: "";
position: absolute;
background: $white;
width: 0;
height: 1px;
bottom: -8px;
transition: width 0.25s ease-in-out;
}
&.router-link-active {
&:after {
width: 100%;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<div class="fs-loader-wrapper">
<div class="fs-loader" />
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: 'NestLoader'
})
</script>
<style lang="scss">
.fs-loader-wrapper {
padding: 100px 0;
.fs-loader {
display: block;
position: relative;
height: 100px;
width: 100px;
margin: -25px auto 0 auto;
border: 2px solid transparent;
border-top-color: $white;
border-radius: 50%;
-webkit-animation: spin7 1.5s ease infinite;
animation: spin7 1.5s ease infinite;
&:before {
content: "";
position: absolute;
top: 14px;
right: 14px;
bottom: 14px;
left: 14px;
border: 2px solid transparent;
border-radius: 50%;
border-top-color: $white;
-webkit-animation: spin7 3s linear infinite;
animation: spin7 3s linear infinite;
}
&:after {
content: "";
position: absolute;
top: 30px;
right: 30px;
bottom: 30px;
left: 30px;
border: 2px solid transparent;
border-radius: 50%;
border-top-color: $white;
-webkit-animation: spin7 1.5s ease infinite;
animation: spin7 1.5s ease infinite;
}
}
}
@-webkit-keyframes spin7 {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin7 {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
</style>

View File

@@ -0,0 +1,222 @@
<template>
<div class="celestial-card">
<div class="card-wrapper">
<div class="card-icon">
<img
v-if="celestial.type === 'planète à lunes'"
src="/icons/saturn-and-other-planets.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="celestial.type === 'planète'"
src="/icons/saturn-planet-shape.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="celestial.type === 'lune'"
src="icons/moon-and-stars-in-a-cloud.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else-if="celestial.type === 'étoile'"
src="icons/sun-shape.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else
src="icons/stars-group.svg"
alt="moon icon"
class="fs-icon"
>
</div>
<div class="card-header">
<h3 class="heading-3">
{{ celestial.name }}
</h3>
<p>
Type :
<span class="txt-capitalize">{{ celestial.type }}</span>
</p>
</div>
<div class="card-info">
<router-link
:to="{ name: 'CelesteSingle', params: { slug: celestial.id } }"
class="no-style"
>
<span class="material-icons-round">info</span>
</router-link>
</div>
<div class="card-content">
<div v-if="celestial.aroundPlanet">
<p>
Orbite autour de
<router-link
:to="{
name: 'CelesteSingle',
params: { slug: celestial.aroundPlanet.planet }
}"
class="txt-capitalize"
>
{{ celestial.aroundPlanet.planet }}
</router-link>
</p>
</div>
<div v-if="celestial.gravity">
<p>Facteur Gravité : {{ celestial.gravity }}</p>
</div>
<div v-if="celestial.density">
<p>Facteur Densité : {{ celestial.density }}</p>
</div>
</div>
<div v-if="celestial.discoveredBy" class="card-footer">
<p>
<i>
Ce corps céleste a été découvert par
{{ celestial.discoveredBy }} le {{ celestial.discoveryDate }}
</i>
</p>
</div>
<div class="card-actions">
<button
class="favourite no-style"
:class="isFav ? 'active' : ''"
@click="toggleFav(celestial)"
>
<span class="icon material-icons-round">favorite</span>
</button>
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
import { v4 as uuidv4 } from 'uuid'
import store from '@/store'
export default defineComponent({
name: 'CelestialCard',
props: {
celestial: {
type: Object,
default: () => { return {} }
}
},
data () {
return {
type: 'unknown'
}
},
computed: {
isFav () {
if (store.getters.isFav(this.celestial?.id)) {
return true
}
return false
}
},
methods: {
toggleFav: (celestial) => {
store.dispatch('toggleFav', celestial.id)
if (store.getters.isFav(celestial.id)) {
store.dispatch('toasts/pushToast', {
id: uuidv4(),
title: 'Favori ajouté',
message: `${celestial.name} a été ajouté à la liste des favoris.`,
category: 'valid',
timer: 3
})
} else {
store.dispatch('toasts/pushToast', {
id: uuidv4(),
title: 'Favori retiré',
message: `${celestial.name} a été retiré de la liste des favoris.`,
category: 'valid',
timer: 3
})
}
}
}
})
</script>
<style lang="scss" scoped>
.celestial-card {
height: 100%;
min-height: 100%;
padding: 20px;
color: $fs-black;
background: rgba($white, 85%);
border-radius: 5px;
.card-wrapper {
position: relative;
height: 100%;
display: flex;
flex-flow: column;
align-items: stretch;
.card-icon {
display: block;
position: absolute;
top: 20px;
bottom: 20px;
left: 0;
right: 0;
user-select: none;
pointer-events: none;
opacity: 4%;
img {
height: 100%;
width: 100%;
z-index: -1;
}
}
> * {
flex: 0 1 auto;
}
.card-header {
padding-right: 60px;
}
.card-content {
flex: 1 1 auto;
min-height: 30px;
}
.card-info {
position: absolute;
top: 0;
right: 0;
}
.card-actions {
color: #afafaf;
.favourite {
.icon {
color: #afafaf;
}
&.active {
.icon {
position: relative;
display: inline-block;
will-change: font-size;
animation: toggleFavHeart 0.6s cubic-bezier(0.17, 0.89, 0.32, 1.49);
animation-fill-mode: forwards;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,120 @@
<template>
<div class="celestial-filters">
<ul class="no-style">
<li>
<button
class="btn btn-primary"
:class="[filters.all ? 'active' : '']"
@click="emitFilter('all')"
>
Tous
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.planets ? 'active' : '']"
@click="emitFilter('planets')"
>
Planètes
<img
src="icons/saturn-planet-shape-white.svg"
alt=""
class="fs-icon icon-sm"
>
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.moons ? 'active' : '']"
@click="emitFilter('moons')"
>
Lunes
<img
src="icons/moon-and-stars-in-a-cloud-white.svg"
alt=""
class="fs-icon icon-sm"
>
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.stars ? 'active' : '']"
@click="emitFilter('stars')"
>
Etoiles
<img src="icons/sun-shape-white.svg" alt="" class="fs-icon icon-sm">
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.others ? 'active' : '']"
@click="emitFilter('others')"
>
Autres
<img
src="icons/stars-group-white.svg"
alt=""
class="fs-icon icon-sm"
>
</button>
</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: 'CelestialFilters',
props: {
celestials: {
type: Array,
default: () => { return [] }
}
},
data () {
return {
filters: {
all: true,
planets: false,
moons: false,
stars: false,
others: false
}
}
},
methods: {
/**
* Emits the type of celestial body to filter for to parent
*/
emitFilter (type) {
// Reset filter appearance
this.filters = {
all: false,
planets: false,
moons: false,
stars: false,
others: false
}
// Triggers the correct filter
if (type === 'all') {
this.filters.all = true
} else if (type === 'planets') {
this.filters.planets = true
} else if (type === 'moons') {
this.filters.moons = true
} else if (type === 'stars') {
this.filters.stars = true
} else if (type === 'others') {
this.filters.others = true
}
this.$emit('filter:celestials', type)
}
}
})
</script>
<style lang="scss" scoped>
.celestial-filters {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<div class="celestial-list-wrapper">
<celestial-filters @filter:celestials="filterCelestials" />
<ul v-if="celestials" class="celestial-list grid no-style">
<li
v-for="(celestial, index) in sortedCelestials"
:key="index"
class="celestial-item"
>
<celestial-card :celestial="celestial" />
</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
import CelestialCard from '@/components/celestials/CelestialCard.vue'
import CelestialFilters from './CelestialFilters.vue'
export default defineComponent({
name: 'CelestialList',
components: {
CelestialCard,
CelestialFilters
},
props: {
celestials: {
type: Array,
required: true
}
},
// Initial state
data () {
return {
activeFilter: 'all'
}
},
computed: {
sortedCelestials () {
if (this.activeFilter === 'moons') {
return this.celestials.filter(e => e.type === 'lune')
} else if (this.activeFilter === 'planets') {
return this.celestials.filter(
e => e.type === 'planète' || e.type === 'planète à lunes'
)
} else if (this.activeFilter === 'stars') {
return this.celestials.filter(e => e.type === 'étoile')
} else if (this.activeFilter === 'others') {
return this.celestials.filter(e => e.type === 'autre')
} else {
return this.celestials
}
}
},
methods: {
/**
* Applies the filter type to the data
*/
filterCelestials (type) {
this.activeFilter = type
}
}
})
</script>
<style lang="scss" scoped>
.celestial-list {
display: flex;
justify-content: space-between;
align-items: stretch;
flex-flow: row wrap;
.celestial-item {
width: calc(33% - 15px);
margin-bottom: 20px;
}
}
</style>

View File

@@ -0,0 +1,100 @@
<template>
<div class="toast-card" :class="[toast.category, closing ? 'closing' : '']">
<div class="toast-title">
<slot name="title" />
</div>
<div class="toast-message">
<slot name="message" />
</div>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
import store from '@/store'
export default defineComponent({
name: 'ToastCard',
props: {
toast: {
type: Object,
default: () => { return {} }
}
},
data () {
return {
closing: false
}
},
mounted () {
// Starts countdown to initial timer value
this.countdown(this.toast.timer)
},
methods: {
// Counts down until 0 is reached
countdown (timeToLive) {
if (timeToLive > 0) {
setTimeout(() => {
timeToLive = timeToLive - 1
this.countdown(timeToLive)
}, 900)
} else {
// When timer ends
this.closing = true
setTimeout(() => {
this.close()
}, 1200)
}
},
close () {
store.dispatch('toasts/purgeToast', this.toast.id)
}
}
})
</script>
<style lang="scss" scoped>
.toast-card {
position: relative;
width: 300px;
padding: 20px 30px 26px;
border-radius: 5px;
animation: fadeIn 1s cubic-bezier(0.175, 1, 0.32, 1),
slideFromRight 1s cubic-bezier(0.175, 1, 0.32, 1);
overflow: hidden;
.toast-title {
margin-bottom: 5px;
font-size: 16px;
font-weight: $fw-bold;
}
&:after {
position: absolute;
display: block;
content: "";
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 6px;
background: rgba($fs-black, 33%);
}
&.info {
background-color: $info;
}
&.valid {
background-color: $valid;
}
&.warning {
color: $fs-black;
background-color: $warning;
}
&.danger {
background-color: $danger;
}
&.closing {
animation: fadeOut 1.2s cubic-bezier(0.175, 1, 0.32, 1),
slideFromLeft 1.2s cubic-bezier(0.175, 1, 0.32, 1);
}
}
</style>

View File

@@ -0,0 +1,47 @@
<template>
<div class="toast-wrapper">
<div v-for="toast in queue" :key="toast.id" class="toast-item">
<toast-card :variant="toast.category" :toast="toast">
<template #title>
{{ toast.title }}
</template>
<template #message>
{{ toast.message }}
</template>
</toast-card>
</div>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
import ToastCard from './ToastCard.vue'
export default defineComponent({
name: 'ToastsList',
components: {
ToastCard
},
computed: {
queue () {
return this.$store.state.toasts.queue
}
}
})
</script>
<style lang="scss" scoped>
.toast-wrapper {
position: fixed;
top: 90px;
height: 100%;
right: 0;
pointer-events: none;
.toast-item {
pointer-events: all;
margin-right: 20px;
margin-bottom: 20px;
}
}
</style>