Files
KArtSell.Aegis/frontend/src/shared/shell/layoutStore.ts
T

201 lines
4.7 KiB
TypeScript

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export interface OpenedTab {
id: string
title: string
path: string
icon: string
timestamp: number
}
export const useLayoutStore = defineStore('layout', () => {
// Sidebar state
const sidebarCollapsed = ref(
localStorage.getItem('sidebar-collapsed') === 'true'
)
const mobileMenuOpen = ref(false)
// Tabs state
const openedTabs = ref<OpenedTab[]>([])
const activeTabId = ref<string | null>(null)
const maxTabs = ref(8) // Maximum number of open tabs
// Notifications
const notifications = ref<Array<{
id: string
type: 'success' | 'error' | 'warning' | 'info'
message: string
timestamp: number
autoClose?: boolean
duration?: number
}>>([])
// Theme
const isDarkMode = ref(
localStorage.getItem('theme') === 'dark' ||
(typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(prefers-color-scheme: dark)').matches)
)
// Save sidebar state
const toggleSidebar = () => {
sidebarCollapsed.value = !sidebarCollapsed.value
localStorage.setItem('sidebar-collapsed', String(sidebarCollapsed.value))
}
// Tab management
const addTab = (tab: Omit<OpenedTab, 'timestamp'>) => {
// Standardize tab ID based on path to prevent duplicates
const tabId = tab.id || `tab-${tab.path}`
const existing = openedTabs.value.find(t => t.path === tab.path)
if (existing) {
activeTabId.value = existing.id
return
}
// Respect max tabs limit (FIFO eviction)
if (openedTabs.value.length >= maxTabs.value) {
// Find oldest tab that is NOT currently active if possible
const oldestIndex = openedTabs.value.findIndex(t => t.id !== activeTabId.value)
if (oldestIndex !== -1) {
openedTabs.value.splice(oldestIndex, 1)
} else {
openedTabs.value.shift()
}
}
const newTab: OpenedTab = {
...tab,
id: tabId,
timestamp: Date.now(),
}
openedTabs.value.push(newTab)
activeTabId.value = newTab.id
saveTabs()
}
const closeTab = (tabId: string) => {
const index = openedTabs.value.findIndex(t => t.id === tabId)
if (index === -1) return
openedTabs.value.splice(index, 1)
// Switch to next tab if closed tab was active
if (activeTabId.value === tabId) {
activeTabId.value = openedTabs.value[Math.max(0, index - 1)]?.id || null
}
saveTabs()
}
const closeAllTabs = () => {
openedTabs.value = []
activeTabId.value = null
localStorage.removeItem('opened-tabs')
}
const closeOtherTabs = (tabId: string) => {
openedTabs.value = openedTabs.value.filter(t => t.id === tabId)
activeTabId.value = tabId
saveTabs()
}
const saveTabs = () => {
localStorage.setItem('opened-tabs', JSON.stringify(openedTabs.value))
}
const loadTabs = () => {
const saved = localStorage.getItem('opened-tabs')
if (saved) {
try {
openedTabs.value = JSON.parse(saved)
if (openedTabs.value.length > 0) {
activeTabId.value = openedTabs.value[0].id
}
} catch {
openedTabs.value = []
}
}
}
// Notification management
const addNotification = (
message: string,
type: 'success' | 'error' | 'warning' | 'info' = 'info',
duration = 3000
) => {
const id = `notif-${Date.now()}-${Math.random()}`
const notif = {
id,
type,
message,
timestamp: Date.now(),
autoClose: true,
duration,
}
notifications.value.push(notif)
if (duration > 0) {
setTimeout(() => {
removeNotification(id)
}, duration)
}
}
const removeNotification = (id: string) => {
const index = notifications.value.findIndex(n => n.id === id)
if (index !== -1) {
notifications.value.splice(index, 1)
}
}
// Theme management
const setDarkMode = (dark: boolean) => {
isDarkMode.value = dark
localStorage.setItem('theme', dark ? 'dark' : 'light')
const root = document.documentElement
root.setAttribute('data-theme', dark ? 'dark' : 'light')
}
const toggleTheme = () => {
setDarkMode(!isDarkMode.value)
}
// Computed
const hasOpenedTabs = computed(() => openedTabs.value.length > 0)
// Load persisted state
loadTabs()
if (isDarkMode.value) {
const root = document.documentElement
root.setAttribute('data-theme', 'dark')
}
return {
// Sidebar
sidebarCollapsed,
mobileMenuOpen,
toggleSidebar,
// Tabs
openedTabs,
activeTabId,
hasOpenedTabs,
addTab,
closeTab,
closeAllTabs,
closeOtherTabs,
saveTabs,
// Notifications
notifications,
addNotification,
removeNotification,
// Theme
isDarkMode,
setDarkMode,
toggleTheme,
}
})