final
This commit is contained in:
@@ -1,31 +1,46 @@
|
||||
<template>
|
||||
<header class="masthead">
|
||||
<div class="masthead-main">
|
||||
<div>
|
||||
<h1 class="masthead-title">{{ competitionTitle }}</h1>
|
||||
<div class="masthead-brand">
|
||||
<picture>
|
||||
<source srcset="/logo.svg" type="image/svg+xml" />
|
||||
<img class="masthead-logo" src="/logo.png" :alt="competitionTitle" />
|
||||
</picture>
|
||||
<p class="masthead-subtitle">{{ t('subtitle') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="masthead-controls">
|
||||
<div class="controls-grid">
|
||||
<div class="control-box">
|
||||
<p class="control-label">{{ t('labels.mode') }}</p>
|
||||
<div class="language-switcher mode-switcher">
|
||||
<button class="lang-btn" :class="{ active: mode === 'view' }" @click="$emit('change-mode', 'view')">{{ t('viewMode') }}</button>
|
||||
<button class="lang-btn" :class="{ active: mode === 'admin' }" @click="$emit('change-mode', 'admin')">{{ t('adminMode') }}</button>
|
||||
</div>
|
||||
<div ref="controlsRoot" class="controls-popup-anchor">
|
||||
<div class="control-toggle-row">
|
||||
<button class="control-toggle-btn" type="button" :aria-expanded="controlsOpen ? 'true' : 'false'" @click="toggleControls">
|
||||
<span>{{ optionsLabel }}</span>
|
||||
<span class="control-toggle-meta">{{ modeShort }} · {{ languageShort }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="control-box">
|
||||
<p class="control-label">{{ t('labels.language') }}</p>
|
||||
<div class="language-switcher">
|
||||
<button class="lang-btn" :class="{ active: language === 'ar' }" @click="$emit('change-language', 'ar')">العربية</button>
|
||||
<button class="lang-btn" :class="{ active: language === 'en' }" @click="$emit('change-language', 'en')">English</button>
|
||||
<Transition name="controls-reveal">
|
||||
<div v-if="controlsOpen" class="controls-grid controls-popover">
|
||||
<div class="control-box">
|
||||
<p class="control-label">{{ t('labels.mode') }}</p>
|
||||
<div class="language-switcher mode-switcher">
|
||||
<button class="lang-btn" :class="{ active: mode === 'view' }" @click="changeMode('view')">{{ t('viewMode') }}</button>
|
||||
<button class="lang-btn" :class="{ active: mode === 'live' }" @click="changeMode('live')">{{ t('liveMode') }}</button>
|
||||
<button class="lang-btn" :class="{ active: mode === 'admin' }" @click="changeMode('admin')">{{ t('adminMode') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-box">
|
||||
<p class="control-label">{{ t('labels.language') }}</p>
|
||||
<div class="language-switcher">
|
||||
<button class="lang-btn" :class="{ active: language === 'ar' }" @click="changeLanguage('ar')">العربية</button>
|
||||
<button class="lang-btn" :class="{ active: language === 'en' }" @click="changeLanguage('en')">English</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div class="status-row">
|
||||
<div class="live-badge">{{ mode === 'view' ? '● ' + t('labels.liveTracker') : t('adminPanel') }}</div>
|
||||
<div class="live-badge">{{ modeStatusLabel }}</div>
|
||||
<div class="server-time" v-if="serverTime">{{ t('labels.lastSync') }}: {{ serverTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -34,7 +49,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
t: { type: Function, required: true },
|
||||
competitionTitle: { type: String, required: true },
|
||||
mode: { type: String, required: true },
|
||||
@@ -42,6 +59,55 @@ defineProps({
|
||||
serverTime: { type: String, default: '' },
|
||||
})
|
||||
|
||||
defineEmits(['change-mode', 'change-language'])
|
||||
</script>
|
||||
const emit = defineEmits(['change-mode', 'change-language'])
|
||||
|
||||
const controlsOpen = ref(false)
|
||||
const controlsRoot = ref(null)
|
||||
|
||||
const optionsLabel = computed(() => (props.language === 'ar' ? 'خيارات' : 'Options'))
|
||||
const modeShort = computed(() => {
|
||||
if (props.mode === 'view') return props.language === 'ar' ? 'عرض' : 'View'
|
||||
if (props.mode === 'live') return props.language === 'ar' ? 'حي' : 'Live'
|
||||
return props.language === 'ar' ? 'إدارة' : 'Admin'
|
||||
})
|
||||
const languageShort = computed(() => (props.language === 'ar' ? 'AR' : 'EN'))
|
||||
const modeStatusLabel = computed(() => {
|
||||
if (props.mode === 'view') return '● ' + props.t('labels.liveTracker')
|
||||
if (props.mode === 'live') return '● ' + props.t('liveMode')
|
||||
return props.t('adminPanel')
|
||||
})
|
||||
|
||||
function toggleControls() {
|
||||
controlsOpen.value = !controlsOpen.value
|
||||
}
|
||||
|
||||
function changeMode(nextMode) {
|
||||
emit('change-mode', nextMode)
|
||||
controlsOpen.value = false
|
||||
}
|
||||
|
||||
function changeLanguage(nextLanguage) {
|
||||
emit('change-language', nextLanguage)
|
||||
controlsOpen.value = false
|
||||
}
|
||||
|
||||
function onGlobalPointerDown(event) {
|
||||
if (!controlsOpen.value) return
|
||||
const root = controlsRoot.value
|
||||
if (root && !root.contains(event.target)) controlsOpen.value = false
|
||||
}
|
||||
|
||||
function onGlobalKeyDown(event) {
|
||||
if (event.key === 'Escape') controlsOpen.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('pointerdown', onGlobalPointerDown)
|
||||
window.addEventListener('keydown', onGlobalKeyDown)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('pointerdown', onGlobalPointerDown)
|
||||
window.removeEventListener('keydown', onGlobalKeyDown)
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user