322 lines
8.5 KiB
Vue
322 lines
8.5 KiB
Vue
<template>
|
|
<ModalCard
|
|
:open="open"
|
|
title="Allgemein"
|
|
:tabs="tabs"
|
|
v-model="activeTab"
|
|
@close="$emit('close')"
|
|
>
|
|
<!-- Allgemein -->
|
|
<div v-if="activeTab === 'general'">
|
|
<div class="settings-section__title">Allgemein</div>
|
|
<div class="settings-section__divider" />
|
|
|
|
<!-- Aussehen -->
|
|
<div class="settings-row">
|
|
<span class="settings-row__label">Aussehen</span>
|
|
<div class="settings-row__control">
|
|
<select
|
|
:value="settingsStore.appearance"
|
|
@change="onAppearanceChange"
|
|
class="settings-select"
|
|
:class="{ 'settings-select--dark': isDark }"
|
|
>
|
|
<option value="system">System</option>
|
|
<option value="light">Hell</option>
|
|
<option value="dark">Dunkel</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="settings-section__divider" />
|
|
|
|
<!-- Akzentfarbe -->
|
|
<div class="settings-row">
|
|
<span class="settings-row__label">Akzentfarbe</span>
|
|
<div class="settings-row__control">
|
|
<button
|
|
class="settings-accent-btn"
|
|
:class="{ 'settings-accent-btn--dark': isDark }"
|
|
@click="accentDropdownOpen = !accentDropdownOpen"
|
|
>
|
|
<span
|
|
class="settings-accent-dot"
|
|
:style="{ background: currentAccentHex }"
|
|
/>
|
|
<span>{{ currentAccentLabel }}</span>
|
|
<q-icon name="expand_more" size="16px" />
|
|
</button>
|
|
|
|
<!-- Accent dropdown -->
|
|
<Transition name="dropdown">
|
|
<div
|
|
v-if="accentDropdownOpen"
|
|
class="settings-dropdown"
|
|
:class="{ 'settings-dropdown--dark': isDark }"
|
|
>
|
|
<button
|
|
v-for="color in ACCENT_COLORS"
|
|
:key="color.value"
|
|
class="settings-dropdown__item"
|
|
@click="selectAccent(color.value)"
|
|
>
|
|
<span class="settings-accent-dot" :style="{ background: color.hex }" />
|
|
<span>{{ color.label }}</span>
|
|
<q-icon
|
|
v-if="settingsStore.accentColor === color.value"
|
|
name="check"
|
|
size="18px"
|
|
class="settings-dropdown__check"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</div>
|
|
<div class="settings-section__divider" />
|
|
|
|
<!-- Sprache -->
|
|
<div class="settings-row">
|
|
<span class="settings-row__label">Sprache</span>
|
|
<div class="settings-row__control">
|
|
<select
|
|
:value="settingsStore.language"
|
|
@change="e => { settingsStore.language = e.target.value }"
|
|
class="settings-select"
|
|
:class="{ 'settings-select--dark': isDark }"
|
|
>
|
|
<option v-for="lang in LANGUAGES" :key="lang.value" :value="lang.value">
|
|
{{ lang.label }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="settings-section__divider" />
|
|
|
|
<!-- FPS-Anzeige -->
|
|
<div class="settings-row">
|
|
<span class="settings-row__label">FPS-Anzeige</span>
|
|
<div class="settings-row__control">
|
|
<q-toggle
|
|
:model-value="settingsStore.showFps"
|
|
@update:model-value="v => { settingsStore.showFps = v }"
|
|
dense
|
|
color="green"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Benachrichtigungen (placeholder) -->
|
|
<div v-else-if="activeTab === 'notifications'">
|
|
<div class="settings-section__title">Benachrichtigungen</div>
|
|
<div class="settings-section__divider" />
|
|
<p class="settings-placeholder">Kommt bald.</p>
|
|
</div>
|
|
|
|
<!-- Personalisierung (placeholder) -->
|
|
<div v-else-if="activeTab === 'personalize'">
|
|
<div class="settings-section__title">Personalisierung</div>
|
|
<div class="settings-section__divider" />
|
|
<p class="settings-placeholder">Kommt bald.</p>
|
|
</div>
|
|
</ModalCard>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watch } from 'vue'
|
|
import { useQuasar } from 'quasar'
|
|
import ModalCard from 'components/ModalCard.vue'
|
|
import { useSettingsStore, ACCENT_COLORS, LANGUAGES } from 'stores/settings'
|
|
|
|
defineProps({ open: { type: Boolean, default: false } })
|
|
defineEmits(['close'])
|
|
|
|
const $q = useQuasar()
|
|
const settingsStore = useSettingsStore()
|
|
const isDark = computed(() => $q.dark.isActive)
|
|
|
|
const activeTab = ref('general')
|
|
const accentDropdownOpen = ref(false)
|
|
|
|
const tabs = [
|
|
{ value: 'general', label: 'Allgemein', icon: 'settings' },
|
|
{ value: 'notifications', label: 'Benachrichtigungen', icon: 'notifications' },
|
|
{ value: 'personalize', label: 'Personalisierung', icon: 'history' }
|
|
]
|
|
|
|
const currentAccentHex = computed(() => {
|
|
const found = ACCENT_COLORS.find(c => c.value === settingsStore.accentColor)
|
|
return found?.hex ?? '#9e9e9e'
|
|
})
|
|
|
|
const currentAccentLabel = computed(() => {
|
|
const found = ACCENT_COLORS.find(c => c.value === settingsStore.accentColor)
|
|
return found?.label ?? 'Standard'
|
|
})
|
|
|
|
function selectAccent(value) {
|
|
settingsStore.accentColor = value
|
|
accentDropdownOpen.value = false
|
|
}
|
|
|
|
function onAppearanceChange(e) {
|
|
const value = e.target.value
|
|
settingsStore.appearance = value
|
|
applyAppearance(value)
|
|
}
|
|
|
|
function applyAppearance(mode) {
|
|
if (mode === 'system') {
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
$q.dark.set(prefersDark)
|
|
} else {
|
|
$q.dark.set(mode === 'dark')
|
|
}
|
|
}
|
|
|
|
// Apply appearance on mount and when it changes externally
|
|
watch(() => settingsStore.appearance, applyAppearance, { immediate: true })
|
|
</script>
|
|
|
|
<style scoped>
|
|
.settings-section__title {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.settings-section__divider {
|
|
height: 1px;
|
|
background: rgba(128, 128, 128, 0.12);
|
|
margin: 16px 0;
|
|
}
|
|
|
|
.settings-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 40px;
|
|
}
|
|
|
|
.settings-row__label {
|
|
font-size: 15px;
|
|
}
|
|
|
|
.settings-row__control {
|
|
position: relative;
|
|
}
|
|
|
|
/* Native select styled */
|
|
.settings-select {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
background: rgba(128, 128, 128, 0.08);
|
|
border: 1px solid rgba(128, 128, 128, 0.15);
|
|
color: inherit;
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
padding: 6px 28px 6px 12px;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
outline: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24'%3E%3Cpath fill='%23999' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 8px center;
|
|
}
|
|
|
|
.settings-select--dark {
|
|
background-color: rgba(255, 255, 255, 0.06);
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
/* Accent button */
|
|
.settings-accent-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: rgba(128, 128, 128, 0.08);
|
|
border: 1px solid rgba(128, 128, 128, 0.15);
|
|
color: inherit;
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.settings-accent-btn--dark {
|
|
background: rgba(255, 255, 255, 0.06);
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.settings-accent-dot {
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Accent Dropdown */
|
|
.settings-dropdown {
|
|
position: absolute;
|
|
right: 0;
|
|
top: calc(100% + 6px);
|
|
min-width: 180px;
|
|
background: rgba(255, 255, 255, 0.92);
|
|
border: 1px solid rgba(128, 128, 128, 0.15);
|
|
border-radius: 12px;
|
|
padding: 6px;
|
|
z-index: 10;
|
|
backdrop-filter: blur(16px);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
.settings-dropdown--dark {
|
|
background: rgba(40, 40, 40, 0.92);
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.settings-dropdown__item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
border: none;
|
|
background: none;
|
|
color: inherit;
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
transition: background 0.12s;
|
|
}
|
|
|
|
.settings-dropdown__item:hover {
|
|
background: rgba(128, 128, 128, 0.1);
|
|
}
|
|
|
|
.settings-dropdown__check {
|
|
margin-left: auto;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
/* Placeholder text */
|
|
.settings-placeholder {
|
|
font-size: 14px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Dropdown transition */
|
|
.dropdown-enter-active,
|
|
.dropdown-leave-active {
|
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
}
|
|
|
|
.dropdown-enter-from,
|
|
.dropdown-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-4px);
|
|
}
|
|
</style>
|