Add Vuex and create Notifications Component

This commit is contained in:
Valentin Kaelin 2019-08-23 00:50:00 +02:00
parent 4b98a83b78
commit 84eae5380f
10 changed files with 184 additions and 32 deletions

View file

@ -4522,10 +4522,13 @@
} }
}, },
"eslint-utils": { "eslint-utils": {
"version": "1.3.1", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
"dev": true "dev": true,
"requires": {
"eslint-visitor-keys": "^1.0.0"
}
}, },
"eslint-visitor-keys": { "eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
@ -11232,6 +11235,11 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true "dev": true
}, },
"vuex": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz",
"integrity": "sha512-ER5moSbLZuNSMBFnEBVGhQ1uCBNJslH9W/Dw2W7GZN23UQA69uapP5GTT9Vm8Trc0PzBSVt6LzF3hGjmv41xcg=="
},
"watchpack": { "watchpack": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",

View file

@ -11,7 +11,8 @@
"axios": "^0.18.1", "axios": "^0.18.1",
"vue": "^2.6.6", "vue": "^2.6.6",
"vue-axios": "^2.1.4", "vue-axios": "^2.1.4",
"vue-router": "^3.0.6" "vue-router": "^3.0.6",
"vuex": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"@fullhuman/postcss-purgecss": "^1.2.0", "@fullhuman/postcss-purgecss": "^1.2.0",

View file

@ -1,36 +1,18 @@
<template> <template>
<div id="app" class="font-sans bg-gray-200 antialiased"> <div id="app" class="font-sans bg-gray-200 antialiased min-h-screen">
<!-- <div class="nav">
<router-link to="/">Accueil</router-link> |
<router-link :to="`/summoner/euw/${summoner}`">
{{ linkText }}
</router-link>
</div> -->
<NotificationsContainer/>
<router-view/> <router-view/>
</div> </div>
</template> </template>
<style>
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
}
</style>
<script> <script>
import NotificationsContainer from '@/components/NotificationsContainer.vue'
export default { export default {
data() { components: {
return { NotificationsContainer
linkText: 'Test link',
summoner: 'Kalane'
}
}, },
} }
</script> </script>

View file

@ -43,3 +43,14 @@
.fade-enter, .fade-leave-to { .fade-enter, .fade-leave-to {
opacity: 0; opacity: 0;
} }
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.4s;
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(400px);
opacity: 0;
}

View file

@ -0,0 +1,24 @@
<template>
<div class="pr-2 pb-2 fixed bottom-0 right-0 z-50">
<PopupNotification
v-for="notification in notifications"
:key="notification.id"
:notification="notification"
/>
</div>
</template>
<script>
import PopupNotification from '@/components/PopupNotification.vue';
import { mapState } from 'vuex'
export default {
components: {
PopupNotification
},
computed: {
...mapState('notification', ['notifications'])
}
}
</script>

View file

@ -0,0 +1,69 @@
<template>
<transition name="slide-fade">
<div
:class="{
'bg-red-500': notification.type === 'error',
'bg-green-500': notification.type === 'success'
}"
class="mt-2 relative rounded-lg shadow-md p-6 pr-10 text-white"
style="min-width: 240px"
>
<button
@click="deleteNotification"
class="cursor-pointer block absolute top-0 right-0 border border-transparent rounded-full py-1 px-1 my-1 mx-1 focus:outline-none hover:border-white"
>
<svg class="fill-current w-3 h-3" viewBox="0 0 20 20">
<path
d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"
/>
</svg>
</button>
<div class="flex items-center text-white">
<svg v-if="notification.type === 'success'" class="w-6 fill-current" viewBox="0 0 20 20">
<path
d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"
/>
</svg>
<svg v-if="notification.type === 'error'" class="w-6 fill-current" viewBox="0 0 20 20">
<path
d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"
/>
</svg>
<span class="ml-3">{{ notification.message }}</span>
</div>
</div>
</transition>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: {
notification: {
type: Object,
required: true
}
},
data() {
return {
timeout: null
};
},
methods: {
deleteNotification() {
this.remove(this.notification)
},
...mapActions('notification', ['remove'])
},
beforeDestroy() {
clearTimeout(this.timeout)
},
mounted() {
this.timeout = setTimeout(() => this.deleteNotification(), 3000)
}
};
</script>

View file

@ -85,8 +85,16 @@ export default {
}; };
}, },
formSubmit() { formSubmit() {
console.log('form submit child'); const regexNames = new RegExp('^[0-9\\p{L} _\\.]+$', 'u');
if(regexNames.exec(this.summoner)) {
this.$emit('formSubmit', this.summoner.split(' ').join(''), this.selectedRegion.toLowerCase()); this.$emit('formSubmit', this.summoner.split(' ').join(''), this.selectedRegion.toLowerCase());
} else {
this.$store.dispatch('notification/add', {
type: 'error',
message: 'Summoner Name entered is incorrect.'
})
}
} }
} }
}; };

View file

@ -5,19 +5,24 @@ import DotLoader from 'vue-spinner/src/DotLoader.vue'
import '@/assets/css/main.css' import '@/assets/css/main.css'
import 'vue-awesome/icons' import 'vue-awesome/icons'
import Icon from 'vue-awesome/components/Icon'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import Icon from 'vue-awesome/components/Icon' import store from './store'
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(VueAxios, axios) Vue.use(VueAxios, axios)
Vue.component('v-icon', Icon) Vue.component('v-icon', Icon)
Vue.component('dot-loader', DotLoader) Vue.component('dot-loader', DotLoader)
Vue.prototype.$patch = '9.16.1' Vue.prototype.$patch = '9.16.1'
new Vue({ new Vue({
router, router,
store,
render: h => h(App), render: h => h(App),
}).$mount('#app') }).$mount('#app')

14
client/src/store/index.js Normal file
View file

@ -0,0 +1,14 @@
import Vue from 'vue'
import Vuex from 'vuex'
import * as notification from '@/store/modules/notification'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
notification,
},
strict: debug
})

View file

@ -0,0 +1,30 @@
export const namespaced = true
export const state = {
notifications: []
}
let nextId = 1
export const mutations = {
PUSH(state, notification) {
state.notifications.push({
...notification,
id: nextId++
})
},
DELETE(state, notificationToRemove) {
state.notifications = state.notifications.filter(
notification => notification.id !== notificationToRemove.id
)
}
}
export const actions = {
add({ commit }, notification) {
commit('PUSH', notification)
},
remove({ commit }, notificationToRemove) {
commit('DELETE', notificationToRemove)
}
}