windows/components/Taskbar.vue

247 lines
5.5 KiB
Vue
Raw Normal View History

2025-09-23 16:43:57 +00:00
<script setup lang="ts">
2025-09-25 02:10:57 +00:00
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
2025-09-23 16:43:57 +00:00
import { storeToRefs } from 'pinia';
import { useWindowsStore } from '../stores/windows';
import { useUIStore } from '../stores/ui';
2025-09-25 02:10:57 +00:00
import { useI18n } from 'vue-i18n';
2025-09-23 16:43:57 +00:00
2025-09-25 02:10:57 +00:00
const { t, locale } = useI18n();
2025-09-23 16:43:57 +00:00
const windowsStore = useWindowsStore();
const uiStore = useUIStore();
const { windows } = storeToRefs(windowsStore);
const { focusWindow } = windowsStore;
const { toggleStartMenu } = uiStore;
2025-09-24 09:45:36 +00:00
// --- Datetime Logic ---
const dateString = ref('');
const timeString = ref('');
let timer: number;
const updateTime = () => {
const now = new Date();
2025-09-25 02:10:57 +00:00
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 });
2025-09-24 09:45:36 +00:00
};
2025-09-25 02:10:57 +00:00
watch(locale, updateTime);
2025-09-24 09:45:36 +00:00
onMounted(() => {
updateTime();
timer = window.setInterval(updateTime, 1000);
});
// --- Input Mode Logic ---
const inputMode = ref('A');
const isInputMenuOpen = ref(false);
const inputSwitcherWrapper = ref<HTMLElement | null>(null);
2025-09-25 02:10:57 +00:00
const availableInputModes = computed(() => [
{ key: '注', label: t('taskbar.zhuyin') },
{ key: 'A', label: t('taskbar.english_us') },
]);
2025-09-24 09:45:36 +00:00
function toggleInputMenu() {
isInputMenuOpen.value = !isInputMenuOpen.value;
}
function selectInputMode(mode: 'A' | '注') {
inputMode.value = mode;
isInputMenuOpen.value = false;
}
const handleClickOutside = (event: MouseEvent) => {
if (inputSwitcherWrapper.value && !inputSwitcherWrapper.value.contains(event.target as Node)) {
isInputMenuOpen.value = false;
}
};
watch(isInputMenuOpen, (isOpen) => {
if (isOpen) {
document.addEventListener('click', handleClickOutside);
} else {
document.removeEventListener('click', handleClickOutside);
}
});
onUnmounted(() => {
clearInterval(timer);
document.removeEventListener('click', handleClickOutside);
});
2025-09-23 16:43:57 +00:00
function handleTaskbarButtonClick(windowId: string) {
focusWindow(windowId);
}
</script>
<template>
<div class="taskbar">
<button @click="toggleStartMenu" class="start-button">🚀</button>
<div class="window-list">
<button
v-for="window in windows"
:key="window.id"
class="taskbar-item"
:class="{ 'is-active': window.isFocused }"
@click="handleTaskbarButtonClick(window.id)"
>
{{ window.title }}
</button>
</div>
2025-09-24 09:45:36 +00:00
<div class="taskbar-right-controls">
<div class="input-switcher-wrapper" ref="inputSwitcherWrapper">
<button @click="toggleInputMenu" class="input-switcher">
{{ inputMode }}
</button>
<div v-if="isInputMenuOpen" class="input-menu">
<ul>
<li v-for="mode in availableInputModes" :key="mode.key" @click="selectInputMode(mode.key as 'A' | '注')">
<span class="checkmark" :style="{ visibility: inputMode === mode.key ? 'visible' : 'hidden' }"></span>
<span>{{ mode.label }}</span>
</li>
</ul>
</div>
</div>
<div class="datetime">
<span>{{ dateString }}</span>
<span>{{ timeString }}</span>
</div>
</div>
2025-09-23 16:43:57 +00:00
</div>
</template>
<style scoped>
.taskbar {
position: fixed;
2025-09-24 09:45:36 +00:00
top: 0;
2025-09-23 16:43:57 +00:00
left: 0;
right: 0;
2025-09-24 09:45:36 +00:00
height: 22px;
2025-09-23 16:43:57 +00:00
background-color: var(--taskbar-background);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
2025-09-24 09:45:36 +00:00
padding: 0 8px;
2025-09-23 16:43:57 +00:00
z-index: var(--z-taskbar);
2025-09-24 09:45:36 +00:00
color: var(--taskbar-item-text-color);
font-size: 12px;
2025-09-23 16:43:57 +00:00
}
.start-button {
background: none;
border: none;
2025-09-24 09:45:36 +00:00
font-size: 14px;
2025-09-23 16:43:57 +00:00
cursor: pointer;
2025-09-24 09:45:36 +00:00
padding: 0 8px;
color: inherit;
margin-right: 4px;
line-height: 22px;
2025-09-23 16:43:57 +00:00
}
.window-list {
2025-09-24 09:45:36 +00:00
flex: 1 1 0;
min-width: 0;
2025-09-23 16:43:57 +00:00
display: flex;
2025-09-24 09:45:36 +00:00
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;
2025-09-23 16:43:57 +00:00
}
.taskbar-item {
background-color: var(--taskbar-item-background);
2025-09-24 09:45:36 +00:00
color: inherit;
2025-09-23 16:43:57 +00:00
border: 1px solid var(--taskbar-item-border-color);
2025-09-24 09:45:36 +00:00
border-radius: 4px;
padding: 2px 6px;
font-size: 12px;
2025-09-23 16:43:57 +00:00
cursor: pointer;
transition: background-color 0.2s ease;
2025-09-24 09:45:36 +00:00
white-space: nowrap;
2025-09-23 16:43:57 +00:00
}
.taskbar-item:hover {
background-color: var(--taskbar-item-background-hover);
}
.taskbar-item.is-active {
background-color: var(--taskbar-item-background-active);
font-weight: bold;
}
2025-09-24 09:45:36 +00:00
.taskbar-right-controls {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0; /* Prevent right controls from shrinking */
}
.input-switcher-wrapper {
position: relative;
}
.input-switcher {
background: none;
border: none;
color: inherit;
font-size: 12px;
font-weight: 500;
cursor: pointer;
padding: 0 4px;
}
.datetime {
display: flex;
align-items: center;
gap: 8px;
padding: 0 4px;
}
.input-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;
}
.input-menu ul {
list-style: none;
padding: 0;
margin: 0;
}
.input-menu li {
display: flex;
align-items: center;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.input-menu li:hover {
background-color: var(--taskbar-item-background-hover);
}
.checkmark {
width: 16px;
text-align: center;
margin-right: 4px;
}
2025-09-23 16:43:57 +00:00
</style>