This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const dropdownVisible = ref(false);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
dropdownVisible.value = !dropdownVisible.value;
|
||||
}
|
||||
|
||||
const menuContainer = ref(null);
|
||||
|
||||
const handleClickOutside = (e) => {
|
||||
if (menuContainer.value && !menuContainer.value.contains(e.target)) {
|
||||
dropdownVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
|
||||
// i18n
|
||||
const { locale } = useI18n()
|
||||
|
||||
const setLocale = (lang) => {
|
||||
locale.value = lang
|
||||
dropdownVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-container" ref="menuContainer">
|
||||
<button class="menu-button" @click="toggleDropdown">Language </button>
|
||||
|
||||
<div class="dropdown" v-show="dropdownVisible">
|
||||
<div class="section">
|
||||
<div class="menu-item" @click="setLocale('en')">
|
||||
🇬🇧 English
|
||||
</div>
|
||||
<div class="menu-item" @click="setLocale('es')">
|
||||
🇪🇸 Spanish
|
||||
</div>
|
||||
<div class="menu-item" @click="setLocale('ca')">
|
||||
🇦🇩 Catalan
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-button {
|
||||
background: #161b22;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
border: 1px solid #30363d;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.dropdown {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 0;
|
||||
margin-right: 10px;
|
||||
background: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 8px;
|
||||
padding: 8px 0;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
/* Items */
|
||||
.menu-item {
|
||||
padding: 8px 16px;
|
||||
color: #c9d1d9;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
&.active {
|
||||
background: #30363d;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
17
frontend/app/components/parts/site_options/SiteOptions.vue
Normal file
17
frontend/app/components/parts/site_options/SiteOptions.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import LanguageSelector from './LanguageSelector.vue';
|
||||
import ThemeSelector from './ThemeSelector.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="site-options">
|
||||
<ThemeSelector></ThemeSelector>
|
||||
<LanguageSelector></LanguageSelector>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.site-options {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
182
frontend/app/components/parts/site_options/ThemeSelector.vue
Normal file
182
frontend/app/components/parts/site_options/ThemeSelector.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { theme, accent, setTheme, setAccent } from '~/composables/theme'
|
||||
|
||||
// ref for dropdown visibility
|
||||
const dropdownVisible = ref(false)
|
||||
|
||||
// toggle function
|
||||
const toggleDropdown = () => {
|
||||
dropdownVisible.value = !dropdownVisible.value
|
||||
}
|
||||
|
||||
// close dropdown if click outside
|
||||
const menuContainer = ref(null)
|
||||
|
||||
const handleClickOutside = (e) => {
|
||||
if (menuContainer.value && !menuContainer.value.contains(e.target)) {
|
||||
dropdownVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-container" ref="menuContainer">
|
||||
<button class="menu-button" @click="toggleDropdown">Theme </button>
|
||||
|
||||
<div class="dropdown" v-show="dropdownVisible">
|
||||
<div class="two-columns">
|
||||
<div class="section">
|
||||
<div class="menu-section">
|
||||
<div class="menu-header">
|
||||
Theme
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="menu-section">
|
||||
<div :class="{'menu-item': true, 'active': theme === 'dark' }" @click="setTheme('dark')">
|
||||
<div class="circle dark"></div>Dark
|
||||
</div>
|
||||
<div :class="{'menu-item': true, 'active': theme === 'light' }" @click="setTheme('light')">
|
||||
<div class="circle light"></div>Light
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="menu-section">
|
||||
<div class="menu-header">
|
||||
Accent
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="menu-section">
|
||||
<div :class="{'menu-item': true, 'active': accent === 'alfdir' }" @click="setAccent('alfdir')">
|
||||
<div class="circle alfdir"></div>Alfdir
|
||||
</div>
|
||||
<div :class="{'menu-item': true, 'active': accent === 'solus' }" @click="setAccent('solus')">
|
||||
<div class="circle solus"></div>Solus
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.circle {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-top: -3px;
|
||||
border-radius: 50%; /* makes it a circle */
|
||||
z-index: 200;
|
||||
justify-content: center;
|
||||
border: 2px var(--color-border) solid;
|
||||
|
||||
&.dark {
|
||||
background-color: rgb(28, 28, 28);
|
||||
}
|
||||
&.light {
|
||||
background-color: rgb(232, 232, 232);
|
||||
}
|
||||
&.alfdir {
|
||||
background-color: #4f8fba;
|
||||
}
|
||||
&.solus {
|
||||
background-color: #e9a02b;
|
||||
}
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.menu-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.two-columns {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.section {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.menu-button {
|
||||
background: #161b22;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
border: 1px solid #30363d;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
/* Dropdown */
|
||||
.dropdown {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 0;
|
||||
margin-right: 10px;
|
||||
width: 320px;
|
||||
background: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 8px;
|
||||
padding: 8px 0;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
.menu-section {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* Items */
|
||||
.menu-item {
|
||||
padding: 8px 16px;
|
||||
color: #c9d1d9;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
border-radius: 5px;
|
||||
gap: 10px;
|
||||
margin: 8px;
|
||||
align-items: center;
|
||||
|
||||
&.active {
|
||||
background: #30363d;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
padding: 8px 16px;
|
||||
color: #c9d1d9;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #21262d;
|
||||
}
|
||||
|
||||
/* Divider */
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #30363d;
|
||||
margin: 6px 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user