feat: extracted theme change logic
This commit is contained in:
parent
351cd2d626
commit
48503e0abf
File diff suppressed because one or more lines are too long
@ -1,18 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<aside
|
<aside
|
||||||
id="sidebar"
|
id="sidebar"
|
||||||
class="top-0 left-0 flex flex-col w-64 h-screen bg-panels-bg text-foreground"
|
class="top-0 left-0 flex flex-col h-screen bg-panels-bg text-foreground"
|
||||||
:class="{ slim: isSlim }"
|
:class="{ 'w-12': isSlim, 'w-64': !isSlim }"
|
||||||
role="navigation"
|
role="navigation"
|
||||||
aria-label="sidebar"
|
aria-label="sidebar"
|
||||||
ref="sidebar"
|
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
tag="a"
|
tag="a"
|
||||||
v-for="link in links"
|
v-for="link in links"
|
||||||
:key="link.id"
|
:key="link.name"
|
||||||
class="relative flex items-center h-16 no-underline group main_tablinks hover:bg-background-main text-foreground"
|
class="relative flex items-center h-16 no-underline group main_tablinks hover:bg-background-main text-foreground"
|
||||||
:id="link.id"
|
|
||||||
:class="{ 'bg-background-main': activeTablink === link.name }"
|
:class="{ 'bg-background-main': activeTablink === link.name }"
|
||||||
:aria-label="link.ariaLabel"
|
:aria-label="link.ariaLabel"
|
||||||
:to="{ name: link.routerName }"
|
:to="{ name: link.routerName }"
|
||||||
@ -24,50 +22,49 @@
|
|||||||
>
|
>
|
||||||
{{ link.icon }}
|
{{ link.icon }}
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-5 overflow-hidden capitalize whitespace-no-wrap main_tablinks_text" style="letter-spacing: 1.3px">
|
<span
|
||||||
|
class="ml-5 overflow-hidden capitalize whitespace-no-wrap main-tablinks-text"
|
||||||
|
:class="{ hidden: isSlim }"
|
||||||
|
style="letter-spacing: 1.3px"
|
||||||
|
>
|
||||||
{{ $t(link.label) }}
|
{{ $t(link.label) }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="link.name === 'about' && updateAvailable"
|
v-if="link.name === 'about' && updateAvailable"
|
||||||
id="update-notification"
|
id="update-notification"
|
||||||
class="w-3 h-3 bg-red-600 rounded-full"
|
class="absolute w-3 h-3 bg-red-600 rounded-full"
|
||||||
></span>
|
></span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<span id="theme_selector" class="flex h-12 mt-5" role="link" aria-label="theme selector">
|
<span
|
||||||
|
id="theme_selector"
|
||||||
|
class="flex h-12 mt-5"
|
||||||
|
role="link"
|
||||||
|
aria-label="theme selector"
|
||||||
|
:class="{ 'inline-grid gap-2': isSlim }"
|
||||||
|
>
|
||||||
<i class="p-2 text-3xl transition-all duration-500 cursor-default material-icons side_icon side_icon--theme">
|
<i class="p-2 text-3xl transition-all duration-500 cursor-default material-icons side_icon side_icon--theme">
|
||||||
brush
|
brush
|
||||||
</i>
|
</i>
|
||||||
<div id="theme_togglers" class="relative flex items-center w-full justify-evenly">
|
|
||||||
<div
|
<div
|
||||||
v-for="theme of themes"
|
id="theme_togglers"
|
||||||
|
class="relative flex items-center w-full justify-evenly"
|
||||||
|
:class="{ 'inline-grid gap-2': isSlim }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="theme of THEMES"
|
||||||
:key="theme"
|
:key="theme"
|
||||||
class="w-6 h-6 border rounded-full cursor-pointer theme_toggler border-grayscale-500"
|
class="w-6 h-6 border rounded-full cursor-pointer theme_toggler border-grayscale-500 gap"
|
||||||
:class="[{ 'theme_toggler--active': activeTheme === theme }, `theme_toggler--${theme}`]"
|
:class="[{ 'theme_toggler--active': currentTheme === theme }, `theme_toggler--${theme}`]"
|
||||||
@click="changeTheme(theme)"
|
@click="currentTheme = theme"
|
||||||
></div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#sidebar.slim {
|
|
||||||
width: 46px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar.slim .main_tablinks_text {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar.slim #theme_selector,
|
|
||||||
#sidebar.slim #theme_togglers {
|
|
||||||
display: inline-grid;
|
|
||||||
grid-gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#update-notification {
|
#update-notification {
|
||||||
position: absolute;
|
|
||||||
left: 30px;
|
left: 30px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
}
|
}
|
||||||
@ -94,121 +91,40 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/utils/socket'
|
import { computed, defineComponent, reactive, toRefs } from '@vue/composition-api'
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
|
|
||||||
export default {
|
import { links } from '@/data/sidebar'
|
||||||
data() {
|
import { socket } from '@/utils/socket'
|
||||||
return {
|
import { useTheme } from '@/use/theme'
|
||||||
activeTheme: 'light',
|
|
||||||
themes: ['purple', 'dark', 'light'],
|
export default defineComponent({
|
||||||
|
setup(props, ctx) {
|
||||||
|
const state = reactive({
|
||||||
activeTablink: 'home',
|
activeTablink: 'home',
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
links: [
|
links
|
||||||
{
|
|
||||||
id: 'main_home_tablink',
|
|
||||||
name: 'home',
|
|
||||||
ariaLabel: 'home',
|
|
||||||
routerName: 'Home',
|
|
||||||
icon: 'home',
|
|
||||||
label: 'sidebar.home'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_search_tablink',
|
|
||||||
name: 'search',
|
|
||||||
ariaLabel: 'search',
|
|
||||||
routerName: 'Search',
|
|
||||||
icon: 'search',
|
|
||||||
label: 'sidebar.search'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_charts_tablink',
|
|
||||||
name: 'charts',
|
|
||||||
ariaLabel: 'charts',
|
|
||||||
routerName: 'Charts',
|
|
||||||
icon: 'show_chart',
|
|
||||||
label: 'sidebar.charts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_favorites_tablink',
|
|
||||||
name: 'favorites',
|
|
||||||
ariaLabel: 'favorites',
|
|
||||||
routerName: 'Favorites',
|
|
||||||
icon: 'star',
|
|
||||||
label: 'sidebar.favorites'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_analyzer_tablink',
|
|
||||||
name: 'analyzer',
|
|
||||||
ariaLabel: 'link analyzer',
|
|
||||||
routerName: 'Link Analyzer',
|
|
||||||
icon: 'link',
|
|
||||||
label: 'sidebar.linkAnalyzer'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_settings_tablink',
|
|
||||||
name: 'settings',
|
|
||||||
ariaLabel: 'settings',
|
|
||||||
routerName: 'Settings',
|
|
||||||
icon: 'settings',
|
|
||||||
label: 'sidebar.settings'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'main_about_tablink',
|
|
||||||
name: 'about',
|
|
||||||
ariaLabel: 'info',
|
|
||||||
routerName: 'About',
|
|
||||||
icon: 'info',
|
|
||||||
label: 'sidebar.about'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
isSlim: 'getSlimSidebar'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
/* === Current theme handling === */
|
|
||||||
this.activeTheme = localStorage.getItem('selectedTheme') || 'dark'
|
|
||||||
|
|
||||||
this.$router.afterEach((to, from) => {
|
|
||||||
const linkInSidebar = this.links.find(link => link.routerName === to.name)
|
|
||||||
|
|
||||||
if (!linkInSidebar) return
|
|
||||||
|
|
||||||
this.activeTablink = linkInSidebar.name
|
|
||||||
})
|
})
|
||||||
|
const { THEMES, currentTheme } = useTheme()
|
||||||
|
|
||||||
/* === Add update notification near info === */
|
/* === Add update notification near info === */
|
||||||
socket.on('updateAvailable', () => {
|
socket.on('updateAvailable', () => {
|
||||||
this.updateAvailable = true
|
state.updateAvailable = true
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
changeTheme(newTheme) {
|
|
||||||
if (newTheme === this.activeTheme) return
|
|
||||||
|
|
||||||
this.activeTheme = newTheme
|
|
||||||
document.documentElement.setAttribute('data-theme', newTheme)
|
|
||||||
localStorage.setItem('selectedTheme', newTheme)
|
|
||||||
|
|
||||||
// Animating everything to have a smoother theme switch
|
|
||||||
const allElements = document.querySelectorAll('*')
|
|
||||||
|
|
||||||
allElements.forEach(el => {
|
|
||||||
el.classList.add('changing-theme')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
document.documentElement.addEventListener('transitionend', function transitionHandler() {
|
ctx.root.$router.afterEach((to, from) => {
|
||||||
allElements.forEach(el => {
|
const linkInSidebar = state.links.find(link => link.routerName === to.name)
|
||||||
el.classList.remove('changing-theme')
|
|
||||||
|
if (!linkInSidebar) return
|
||||||
|
|
||||||
|
state.activeTablink = linkInSidebar.name
|
||||||
})
|
})
|
||||||
|
|
||||||
document.documentElement.removeEventListener('transitionend', transitionHandler)
|
return {
|
||||||
})
|
...toRefs(state),
|
||||||
|
THEMES,
|
||||||
|
currentTheme,
|
||||||
|
isSlim: computed(() => ctx.root.$store.getters.getSlimSidebar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
51
src/data/sidebar.js
Normal file
51
src/data/sidebar.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
export const links = [
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
ariaLabel: 'home',
|
||||||
|
routerName: 'Home',
|
||||||
|
icon: 'home',
|
||||||
|
label: 'sidebar.home'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'search',
|
||||||
|
ariaLabel: 'search',
|
||||||
|
routerName: 'Search',
|
||||||
|
icon: 'search',
|
||||||
|
label: 'sidebar.search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'charts',
|
||||||
|
ariaLabel: 'charts',
|
||||||
|
routerName: 'Charts',
|
||||||
|
icon: 'show_chart',
|
||||||
|
label: 'sidebar.charts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'favorites',
|
||||||
|
ariaLabel: 'favorites',
|
||||||
|
routerName: 'Favorites',
|
||||||
|
icon: 'star',
|
||||||
|
label: 'sidebar.favorites'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'analyzer',
|
||||||
|
ariaLabel: 'link analyzer',
|
||||||
|
routerName: 'Link Analyzer',
|
||||||
|
icon: 'link',
|
||||||
|
label: 'sidebar.linkAnalyzer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'settings',
|
||||||
|
ariaLabel: 'settings',
|
||||||
|
routerName: 'Settings',
|
||||||
|
icon: 'settings',
|
||||||
|
label: 'sidebar.settings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'about',
|
||||||
|
ariaLabel: 'info',
|
||||||
|
routerName: 'About',
|
||||||
|
icon: 'info',
|
||||||
|
label: 'sidebar.about'
|
||||||
|
}
|
||||||
|
]
|
46
src/use/theme.js
Normal file
46
src/use/theme.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { ref, watch } from '@vue/composition-api'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {string} Theme
|
||||||
|
*/
|
||||||
|
|
||||||
|
const THEMES = {
|
||||||
|
dark: 'dark',
|
||||||
|
light: 'light',
|
||||||
|
purple: 'purple'
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialTheme = localStorage.getItem('selectedTheme') || document.documentElement.dataset.theme || THEMES.dark
|
||||||
|
const currentTheme = ref(initialTheme)
|
||||||
|
|
||||||
|
watch(currentTheme, (newTheme, oldTheme) => {
|
||||||
|
// No operation needed
|
||||||
|
if (oldTheme === newTheme) return
|
||||||
|
|
||||||
|
localStorage.setItem('selectedTheme', newTheme)
|
||||||
|
document.documentElement.dataset.theme = newTheme
|
||||||
|
|
||||||
|
animateAllElements()
|
||||||
|
})
|
||||||
|
|
||||||
|
function animateAllElements() {
|
||||||
|
// Animating everything to have a smoother theme switch
|
||||||
|
const allElements = document.querySelectorAll('*')
|
||||||
|
|
||||||
|
allElements.forEach(el => {
|
||||||
|
el.classList.add('changing-theme')
|
||||||
|
})
|
||||||
|
|
||||||
|
document.documentElement.addEventListener('transitionend', function transitionHandler() {
|
||||||
|
allElements.forEach(el => {
|
||||||
|
el.classList.remove('changing-theme')
|
||||||
|
})
|
||||||
|
|
||||||
|
document.documentElement.removeEventListener('transitionend', transitionHandler)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTheme = () => ({
|
||||||
|
THEMES,
|
||||||
|
currentTheme
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user