frontend/app/components/Taskbar.vue

235 lines
5.3 KiB
Vue
Raw Normal View History

2025-09-10 23:43:41 +00:00
<template>
<div class="taskbar">
<div class="start-section">
<button class="start-button" @click="toggleStartMenu">
<span class="start-icon">🪟</span>
<span>{{ t('start') }}</span>
</button>
<div v-if="showStartMenu" class="start-menu">
2025-09-11 14:22:41 +00:00
<div class="menu-item" @click="openTerminal">
{{ t('terminal') }}
</div>
2025-09-10 23:43:41 +00:00
<div class="menu-item" @click="openAboutMe">
{{ t('about_me') }}
</div>
<!-- More menu items can go here -->
<div class="menu-item" @click="closeAllWindows">
{{ t('close_all_windows') }}
</div>
</div>
</div>
<div class="task-buttons">
<button
v-for="window in minimizedWindows"
:key="window.id"
class="task-button"
@click="restoreMinimizedWindow(window.id)"
>
{{ t(window.title) }}
</button>
</div>
<div class="right-section">
<div class="language-toggle" @click="toggleLanguage">
2025-09-11 11:35:49 +00:00
<span>{{ locale === 'zh-tw' ? '注音' : 'ABC' }}</span>
2025-09-10 23:43:41 +00:00
</div>
<div class="clock">
2025-09-11 11:35:49 +00:00
<span v-if="isMounted">{{ currentTime }}</span>
2025-09-10 23:43:41 +00:00
</div>
</div>
</div>
</template>
<script setup>
2025-09-11 14:22:41 +00:00
import { ref, onMounted, onUnmounted, watch } from 'vue';
2025-09-10 23:43:41 +00:00
import { useI18n } from 'vue-i18n';
2025-09-11 11:35:49 +00:00
const { t, locale, setLocale } = useI18n();
defineProps({
2025-09-10 23:43:41 +00:00
minimizedWindows: {
type: Array,
default: () => [],
},
});
2025-09-11 11:45:25 +00:00
const currentTime = ref('');
2025-09-11 11:35:49 +00:00
const isMounted = ref(false);
2025-09-10 23:43:41 +00:00
let timerId = null;
const showStartMenu = ref(false);
2025-09-11 14:22:41 +00:00
const emit = defineEmits(['open-about-me', 'restore-window', 'close-all-windows', 'open-terminal']);
2025-09-10 23:43:41 +00:00
const toggleStartMenu = () => {
showStartMenu.value = !showStartMenu.value;
};
2025-09-11 14:22:41 +00:00
const openTerminal = () => {
emit('open-terminal');
showStartMenu.value = false;
};
2025-09-10 23:43:41 +00:00
const openAboutMe = () => {
emit('open-about-me');
showStartMenu.value = false;
};
const closeAllWindows = () => {
emit('close-all-windows');
showStartMenu.value = false;
};
const toggleLanguage = () => {
2025-09-11 11:35:49 +00:00
const newLocale = locale.value === 'zh-tw' ? 'en' : 'zh-tw';
locale.value = newLocale;
setLocale(newLocale);
2025-09-10 23:43:41 +00:00
};
const restoreMinimizedWindow = (id) => {
emit('restore-window', id);
};
2025-09-11 11:45:25 +00:00
const updateCurrentTime = () => {
// Map i18n locale to a specific locale for time formatting
const timeLocale = locale.value === 'zh-tw' ? 'zh-TW' : 'en-US';
currentTime.value = new Date().toLocaleTimeString(timeLocale);
};
// Watch for locale changes to update the time format immediately
watch(locale, updateCurrentTime);
2025-09-10 23:43:41 +00:00
onMounted(() => {
2025-09-11 11:35:49 +00:00
isMounted.value = true;
2025-09-11 11:45:25 +00:00
updateCurrentTime(); // Set initial time based on current locale
timerId = setInterval(updateCurrentTime, 1000);
2025-09-10 23:43:41 +00:00
});
onUnmounted(() => {
if (timerId) {
clearInterval(timerId);
}
});
</script>
<style scoped>
.taskbar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #c0c0c0; /* Classic Windows gray */
padding: 4px 6px;
2025-09-11 14:22:41 +00:00
border-top: 2px solid #ffffff;
border-bottom: 2px solid #808080;
2025-09-10 23:43:41 +00:00
width: 100%;
box-sizing: border-box;
height: 40px;
position: relative; /* For positioning the start menu */
}
.start-section {
position: relative;
display: flex;
align-items: center;
flex-grow: 0; /* Don't let start section grow */
}
.right-section {
display: flex;
align-items: center;
gap: 6px;
}
.task-buttons {
display: flex;
flex-grow: 1; /* Allow task buttons to take available space */
margin-left: 10px; /* Space between start button and task buttons */
gap: 6px;
overflow-x: auto; /* Allow horizontal scrolling if many windows */
padding-bottom: 2px; /* Prevent scrollbar from overlapping border */
}
.task-button {
padding: 4px 8px;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
background-color: #c0c0c0;
font-weight: bold;
font-size: 0.9rem;
cursor: pointer;
color: black;
white-space: nowrap; /* Prevent text wrapping */
flex-shrink: 0; /* Prevent buttons from shrinking */
}
.task-button:active {
border-color: #ffffff #808080 #808080 #ffffff;
}
.start-button {
display: flex;
align-items: center;
padding: 4px 8px;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
background-color: #c0c0c0;
font-weight: bold;
font-size: 1rem;
cursor: pointer;
color: black;
}
.start-button:active {
border-color: #808080 #ffffff #ffffff #808080;
}
.start-icon {
margin-right: 8px;
}
.start-menu {
position: absolute;
2025-09-11 14:22:41 +00:00
top: 100%; /* Opens downwards from the taskbar */
2025-09-10 23:43:41 +00:00
left: 0;
background-color: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 4px;
min-width: 160px;
z-index: 1001;
}
.menu-item {
padding: 4px 8px;
cursor: pointer;
white-space: nowrap;
border: 1px solid transparent;
color: black;
}
.menu-item:hover {
background-color: #000080;
color: white;
border-color: #000080;
}
.language-toggle {
padding: 4px 8px;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
font-size: 0.9rem;
color: black;
cursor: pointer;
background-color: #c0c0c0;
}
.language-toggle:active {
border-color: #ffffff #808080 #808080 #ffffff;
}
.clock {
padding: 4px 8px;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
font-size: 0.9rem;
color: black;
background-color: #c0c0c0;
}
</style>