windows/components/Taskbar.vue

308 lines
7.1 KiB
Vue

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useWindowsStore } from '../stores/windows';
import { useAppsStore } from '../stores/apps';
import { useUIStore } from '../stores/ui';
import { useI18n } from 'vue-i18n';
const { t, locale, setLocale } = useI18n();
const windowsStore = useWindowsStore();
const appsStore = useAppsStore();
const uiStore = useUIStore();
const { windows } = storeToRefs(windowsStore);
const { appInstances } = storeToRefs(appsStore);
const { focusWindow } = windowsStore;
const { focusAppInstance } = appsStore;
const { toggleStartMenu } = uiStore;
// --- Datetime Logic ---
const dateString = ref('');
const timeString = ref('');
let timer: number;
const updateTime = () => {
const now = new Date();
dateString.value = now.toLocaleDateString(locale.value, { weekday: 'short', month: 'short', day: 'numeric' });
timeString.value = now.toLocaleTimeString(locale.value, { hour: '2-digit', minute: '2-digit', hour12: false });
};
watch(locale, updateTime);
onMounted(() => {
updateTime();
timer = window.setInterval(updateTime, 1000);
});
// --- Language Switcher Logic ---
const isLanguageMenuOpen = ref(false);
const languageSwitcherWrapper = ref<HTMLElement | null>(null);
const availableLanguages = computed(() => [
{ key: 'en', label: 'English', display: 'EN' },
{ key: 'zh', label: '繁體中文', display: '注' },
]);
const currentLanguageDisplay = computed(() => {
const current = availableLanguages.value.find(lang => lang.key === locale.value);
return current?.display || '注';
});
function toggleLanguageMenu() {
isLanguageMenuOpen.value = !isLanguageMenuOpen.value;
}
function selectLanguage(lang: 'en' | 'zh') {
setLocale(lang);
isLanguageMenuOpen.value = false;
}
const handleClickOutside = (event: MouseEvent) => {
if (languageSwitcherWrapper.value && !languageSwitcherWrapper.value.contains(event.target as Node)) {
isLanguageMenuOpen.value = false;
}
};
watch(isLanguageMenuOpen, (isOpen) => {
if (isOpen) {
document.addEventListener('click', handleClickOutside);
} else {
document.removeEventListener('click', handleClickOutside);
}
});
onUnmounted(() => {
clearInterval(timer);
document.removeEventListener('click', handleClickOutside);
});
function handleTaskbarButtonClick(windowId: string) {
// Try to focus app instance first, then regular window
const appInstance = appsStore.getAppInstanceById(windowId);
if (appInstance) {
focusAppInstance(windowId);
} else {
focusWindow(windowId);
}
}
// Combined taskbar items (windows + app instances)
const taskbarItems = computed(() => {
const items = [];
// Add regular windows
windows.value.forEach(window => {
items.push({
id: window.id,
title: window.title,
isActive: window.isFocused,
type: 'window'
});
});
// Add app instances
appInstances.value.forEach(instance => {
const appInfo = appsStore.getAppById(instance.appId);
items.push({
id: instance.id,
title: instance.title,
isActive: instance.isFocused,
type: 'app',
icon: appInfo?.icon
});
});
return items;
});
</script>
<template>
<div class="taskbar">
<button @click="toggleStartMenu" class="start-button">🚀</button>
<div class="window-list">
<button
v-for="item in taskbarItems"
:key="item.id"
class="taskbar-item"
:class="{ 'is-active': item.isActive }"
@click="handleTaskbarButtonClick(item.id)"
>
<span v-if="item.icon" class="taskbar-icon">{{ item.icon }}</span>
<span class="taskbar-title">{{ item.title }}</span>
</button>
</div>
<div class="taskbar-right-controls">
<div class="language-switcher-wrapper" ref="languageSwitcherWrapper">
<button @click="toggleLanguageMenu" class="language-switcher">
{{ currentLanguageDisplay }}
</button>
<div v-if="isLanguageMenuOpen" class="language-menu">
<ul>
<li v-for="lang in availableLanguages" :key="lang.key" @click="selectLanguage(lang.key as 'en' | 'zh')">
<span class="checkmark" :style="{ visibility: locale === lang.key ? 'visible' : 'hidden' }">✓</span>
<span>{{ lang.label }}</span>
</li>
</ul>
</div>
</div>
<div class="datetime">
<span>{{ dateString }}</span>
<span>{{ timeString }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.taskbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 22px;
background-color: var(--taskbar-background);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
padding: 0 8px;
z-index: var(--z-taskbar);
color: var(--taskbar-item-text-color);
font-size: 12px;
}
.start-button {
background: none;
border: none;
font-size: 14px;
cursor: pointer;
padding: 0 8px;
color: inherit;
margin-right: 4px;
line-height: 22px;
}
.window-list {
flex: 1 1 0;
min-width: 0;
display: flex;
gap: 4px;
overflow-x: auto;
}
/* Subtle scrollbar styling */
.window-list::-webkit-scrollbar {
height: 2px;
}
.window-list::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.3);
border-radius: 1px;
}
.taskbar-item {
background-color: var(--taskbar-item-background);
color: inherit;
border: 1px solid var(--taskbar-item-border-color);
border-radius: 4px;
padding: 2px 6px;
font-size: 12px;
cursor: pointer;
transition: background-color 0.2s ease;
white-space: nowrap;
display: flex;
align-items: center;
gap: 4px;
}
.taskbar-icon {
font-size: 10px;
line-height: 1;
}
.taskbar-title {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.taskbar-item:hover {
background-color: var(--taskbar-item-background-hover);
}
.taskbar-item.is-active {
background-color: var(--taskbar-item-background-active);
font-weight: bold;
}
.taskbar-right-controls {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0; /* Prevent right controls from shrinking */
}
.language-switcher-wrapper {
position: relative;
}
.language-switcher {
background: none;
border: none;
color: inherit;
font-size: 12px;
font-weight: 500;
cursor: pointer;
padding: 0 4px;
min-width: 20px;
text-align: center;
}
.datetime {
display: flex;
align-items: center;
gap: 8px;
padding: 0 4px;
}
.language-menu {
position: absolute;
top: calc(100% + 4px);
right: 0;
width: 160px;
background-color: var(--start-menu-background);
border: 1px solid var(--start-menu-border-color);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
padding: 4px;
z-index: 10001;
}
.language-menu ul {
list-style: none;
padding: 0;
margin: 0;
}
.language-menu li {
display: flex;
align-items: center;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.language-menu li:hover {
background-color: var(--taskbar-item-background-hover);
}
.checkmark {
width: 16px;
text-align: center;
margin-right: 4px;
}
</style>