Client - add dropdown to select language

This commit is contained in:
Sam 2021-08-04 13:59:51 +02:00
parent 1e77fa68b6
commit b01f867d80
8 changed files with 186 additions and 22 deletions

View File

@ -1,2 +0,0 @@
VUE_APP_I18N_LOCALE=en
VUE_APP_I18N_FALLBACK_LOCALE=en

View File

@ -0,0 +1,97 @@
<template>
<div class="dropdown-wrapper">
<div class="dropdown-selected" @click="openDropdown">
<slot></slot>
</div>
<ul class="dropdown-list" v-if="isOpen">
<li
class="dropdown-item"
:class="{ selected: option.value === selected }"
v-for="(option, index) in dropdownOptions"
:key="index"
@click="updateSelected(option)"
>
{{ option.label }}
</li>
</ul>
</div>
</template>
<script lang="ts">
import { PropType, defineComponent, ref } from 'vue'
import { IDropdownOption, TDropdownOptions } from '@/types'
export default defineComponent({
name: 'Dropdown',
props: {
options: {
type: Object as PropType<TDropdownOptions>,
required: true,
},
selected: {
type: String,
required: true,
},
},
emits: {
selected: (option: IDropdownOption) => option,
},
setup(props, { emit }) {
let isOpen = ref(false)
let dropdownOptions = props.options.map((option) => option)
function openDropdown() {
isOpen.value = true
}
function updateSelected(option: IDropdownOption) {
emit('selected', option)
isOpen.value = false
}
function getSelectedLabel(selectedValue: string) {
return props.options.filter(
(option: IDropdownOption) => option.value === selectedValue
)[0].label
}
return {
dropdownOptions,
updateSelected,
getSelectedLabel,
isOpen,
openDropdown,
}
},
})
</script>
<style scoped lang="scss">
.dropdown-list {
list-style-type: none;
background-color: #ffffff;
padding: 0;
margin: 5px 0;
position: absolute;
text-align: left;
border: solid 1px lightgrey;
box-shadow: 2px 2px 5px lightgrey;
li {
padding-top: 5px;
}
li:last-child {
padding-bottom: 5px;
}
}
.dropdown-item {
cursor: default;
&.selected {
font-weight: bold;
}
&.selected::after {
content: ' ✔';
}
}
</style>

View File

@ -2,14 +2,14 @@
<div id="nav"> <div id="nav">
<div class="container"> <div class="container">
<div class="nav-app-name"> <div class="nav-app-name">
<span class="nav-item app-name">FitTrackee</span> <div class="nav-item app-name">FitTrackee</div>
</div> </div>
<div class="nav-icon-open" :class="{ 'menu-open': isMenuOpen }"> <div class="nav-icon-open" :class="{ 'menu-open': isMenuOpen }">
<i class="fa fa-bars hamburger-icon" @click="openMenu()"></i> <i class="fa fa-bars hamburger-icon" @click="openMenu()"></i>
</div> </div>
<div class="nav-items" :class="{ 'menu-open': isMenuOpen }"> <div class="nav-items" :class="{ 'menu-open': isMenuOpen }">
<div class="nav-items-close"> <div class="nav-items-close">
<span class="app-name">FitTrackee</span> <div class="app-name">FitTrackee</div>
<i <i
class="fa fa-close close-icon nav-item" class="fa fa-close close-icon nav-item"
:class="{ 'menu-closed': !isMenuOpen }" :class="{ 'menu-closed': !isMenuOpen }"
@ -20,16 +20,25 @@
<router-link class="nav-item" to="/">{{ <router-link class="nav-item" to="/">{{
t('dashboard.DASHBOARD') t('dashboard.DASHBOARD')
}}</router-link> }}</router-link>
<span class="nav-item">{{ t('workouts.WORKOUTS') }}</span> <div class="nav-item">{{ t('workouts.WORKOUTS') }}</div>
<span class="nav-item">{{ t('statistics.STATISTICS') }}</span> <div class="nav-item">{{ t('statistics.STATISTICS') }}</div>
<span class="nav-item">{{ t('administration.ADMIN') }}</span> <div class="nav-item">{{ t('administration.ADMIN') }}</div>
<span class="nav-item">{{ t('workouts.ADD_WORKOUT') }}</span> <div class="nav-item">{{ t('workouts.ADD_WORKOUT') }}</div>
</div> </div>
<div class="nav-items-user-menu"> <div class="nav-items-user-menu">
<span class="nav-item">User</span> <div class="nav-item">User</div>
<span class="nav-item">{{ t('user.LOGOUT') }}</span> <div class="nav-item">{{ t('user.LOGOUT') }}</div>
<span class="nav-item">{{ t('user.REGISTER') }}</span> <!-- <span class="nav-item">{{ t('user.REGISTER') }}</span>-->
<span class="nav-item">{{ t('user.LOGIN') }}</span> <!-- <span class="nav-item">{{ t('user.LOGIN') }}</span>-->
<Dropdown
v-if="availableLanguages && language"
class="nav-item"
:options="availableLanguages"
:selected="language"
@selected="updateLanguage"
>
<i class="fa fa-language"></i>
</Dropdown>
</div> </div>
</div> </div>
</div> </div>
@ -37,21 +46,45 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from 'vue' import { computed, defineComponent, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import { IDropdownOption } from '@/types'
import Dropdown from '@/components/Common/Dropdown.vue'
export default defineComponent({ export default defineComponent({
name: 'NavBar', name: 'NavBar',
components: {
Dropdown,
},
setup() { setup() {
const { t, locale, availableLocales } = useI18n()
const store = useStore()
const availableLanguages = availableLocales.map((l) => {
return { label: l.toUpperCase(), value: l }
})
let isMenuOpen = ref(false) let isMenuOpen = ref(false)
const { t } = useI18n()
function openMenu() { function openMenu() {
isMenuOpen.value = true isMenuOpen.value = true
} }
function closeMenu() { function closeMenu() {
isMenuOpen.value = false isMenuOpen.value = false
} }
return { isMenuOpen, openMenu, closeMenu, t } function updateLanguage(option: IDropdownOption) {
locale.value = option.value.toString()
store.commit('setLanguage', option.value)
}
return {
availableLanguages,
isMenuOpen,
language: computed(() => store.state.language),
t,
openMenu,
closeMenu,
updateLanguage,
}
}, },
}) })
</script> </script>
@ -74,7 +107,6 @@
font-size: 1.2em; font-size: 1.2em;
font-weight: bold; font-weight: bold;
margin-right: 10px; margin-right: 10px;
line-height: 12px;
} }
.fa { .fa {
@ -84,6 +116,7 @@
.nav-icon-open { .nav-icon-open {
display: none; display: none;
} }
.hamburger-icon, .hamburger-icon,
.close-icon { .close-icon {
display: none; display: none;
@ -99,8 +132,25 @@
display: none; display: none;
} }
.nav-items-app-menu,
.nav-items-user-menu {
display: flex;
margin: 0;
padding: 0;
}
.nav-item { .nav-item {
padding: 10px 10px; padding: 0 10px;
&.dropdown-wrapper {
width: 60px;
}
::v-deep(.dropdown-list) {
margin-left: -10px;
padding-left: 10px;
width: 75px;
}
} }
} }
@ -155,9 +205,13 @@
justify-content: space-between; justify-content: space-between;
.app-name { .app-name {
padding: 19px 25px; padding: 15px 25px;
} }
} }
.nav-item {
padding: 7px 25px;
}
} }
} }
} }

View File

@ -21,7 +21,8 @@ function loadLocaleMessages(): LocaleMessages<VueMessageType> {
export default createI18n({ export default createI18n({
legacy: false, legacy: false,
locale: process.env.VUE_APP_I18N_LOCALE || 'en', locale: 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', fallbackLocale: 'en',
globalInjection: true,
messages: loadLocaleMessages(), messages: loadLocaleMessages(),
}) })

View File

@ -1,4 +1,5 @@
{ {
"LANGUAGE": "Language",
"LOGIN": "Login", "LOGIN": "Login",
"LOGOUT": "Logout", "LOGOUT": "Logout",
"REGISTER": "Register" "REGISTER": "Register"

View File

@ -1,4 +1,5 @@
{ {
"LANGUAGE": "Langue",
"LOGIN": "Se connecter", "LOGIN": "Se connecter",
"LOGOUT": "Se déconnecter", "LOGOUT": "Se déconnecter",
"REGISTER": "S'inscrire" "REGISTER": "S'inscrire"

View File

@ -1,8 +1,14 @@
import { createStore } from 'vuex' import { createStore } from 'vuex'
export default createStore({ export default createStore({
state: {}, state: {
mutations: {}, language: 'en',
},
mutations: {
setLanguage(state, language: string) {
state.language = language
},
},
actions: {}, actions: {},
modules: {}, modules: {},
}) })

View File

@ -0,0 +1,6 @@
export interface IDropdownOption {
value: string | number
label: string
}
export type TDropdownOptions = IDropdownOption[]