archy/neode-ui/src/views/dashboard/useRouteTransitions.ts

153 lines
5.9 KiB
TypeScript

import { type RouteLocationNormalizedLoaded } from 'vue-router'
/** Tab order for vertical transitions between main navigation items */
const TAB_ORDER = [
'/dashboard',
'/dashboard/apps',
'/dashboard/marketplace',
'/dashboard/cloud',
'/dashboard/mesh',
'/dashboard/server',
'/dashboard/web5',
'/dashboard/fleet',
'/dashboard/chat',
'/dashboard/settings'
]
/** Web5 group sub-tab order for mobile horizontal swipe transitions */
const WEB5_TAB_ORDER = ['/dashboard/web5', '/dashboard/cloud', '/dashboard/server', '/dashboard/mesh']
/** Route-to-background image mapping */
export const ROUTE_BACKGROUNDS: Record<string, string> = {
'/dashboard': 'bg-home.jpg',
'/dashboard/': 'bg-home.jpg',
'/dashboard/apps': 'bg-myapps.jpg',
'/dashboard/discover': 'bg-appstore.jpg',
'/dashboard/marketplace': 'bg-appstore.jpg',
'/dashboard/cloud': 'bg-cloud.jpg',
'/dashboard/mesh': 'bg-mesh.jpg',
'/dashboard/server': 'bg-network.jpg',
'/dashboard/web5': 'bg-web5.jpg',
'/dashboard/federation': 'bg-web5.jpg',
'/dashboard/settings': 'bg-settings.jpg',
'/dashboard/chat': 'bg-aiui.jpg',
}
export function isDetailRoute(path: string): boolean {
return (path.includes('/apps/') && !path.endsWith('/apps')) ||
(path.includes('/marketplace/') && !path.endsWith('/marketplace'))
}
/**
* Creates a route transition tracker that determines the appropriate
* CSS transition name based on navigation direction and route depth.
*/
export function useRouteTransitions() {
let previousPath = ''
let previousTab = ''
function getTransitionName(currentRoute: RouteLocationNormalizedLoaded): string {
const currentPath = currentRoute.path
if (!previousPath) {
previousPath = currentPath
return 'fade'
}
// Chat transitions: directional slide
const isChat = currentPath === '/dashboard/chat'
const wasChat = previousPath === '/dashboard/chat'
if (isChat) {
previousPath = currentPath
return 'chat-open'
}
if (wasChat) {
previousPath = currentPath
return 'chat-close'
}
const isAppDetails = currentPath.includes('/apps/') && !currentPath.endsWith('/apps')
const isAppsList = currentPath === '/dashboard/apps'
const wasAppDetails = previousPath.includes('/apps/') && !previousPath.endsWith('/apps')
const wasAppsList = previousPath === '/dashboard/apps'
const isMarketplaceDetails = currentPath.includes('/marketplace/') && !currentPath.endsWith('/marketplace')
const isMarketplaceList = currentPath === '/dashboard/marketplace'
const wasMarketplaceDetails = previousPath.includes('/marketplace/') && !previousPath.endsWith('/marketplace')
const wasMarketplaceList = previousPath === '/dashboard/marketplace'
const isCloudFolder = currentPath.includes('/cloud/') && !currentPath.endsWith('/cloud')
const isCloudList = currentPath === '/dashboard/cloud'
const wasCloudFolder = previousPath.includes('/cloud/') && !previousPath.endsWith('/cloud')
const wasCloudList = previousPath === '/dashboard/cloud'
let transitionName = 'fade'
// Mobile: Horizontal slide transitions between sub-tabs
if (typeof window !== 'undefined' && window.innerWidth < 768) {
const isServices = currentPath === '/dashboard/apps' && currentRoute.query.tab === 'services'
const wasServices = previousTab === 'services'
const currentAppsIdx = isServices ? 2
: currentPath === '/dashboard/marketplace' ? 1
: currentPath === '/dashboard/apps' ? 0 : -1
const prevAppsIdx = wasServices ? 2
: previousPath === '/dashboard/marketplace' ? 1
: previousPath === '/dashboard/apps' ? 0 : -1
const currentWeb5Idx = WEB5_TAB_ORDER.indexOf(currentPath)
const prevWeb5Idx = WEB5_TAB_ORDER.indexOf(previousPath)
if (currentAppsIdx !== -1 && prevAppsIdx !== -1 && currentAppsIdx !== prevAppsIdx) {
transitionName = currentAppsIdx > prevAppsIdx ? 'slide-left' : 'slide-right'
} else if (currentWeb5Idx !== -1 && prevWeb5Idx !== -1 && currentWeb5Idx !== prevWeb5Idx) {
transitionName = currentWeb5Idx > prevWeb5Idx ? 'slide-left' : 'slide-right'
} else {
const currentIndex = TAB_ORDER.indexOf(currentPath)
const previousIndex = TAB_ORDER.indexOf(previousPath)
if (currentIndex !== -1 && previousIndex !== -1 && currentIndex !== previousIndex) {
transitionName = currentIndex > previousIndex ? 'slide-down' : 'slide-up'
}
}
}
// Desktop depth transitions: list <-> detail
else if (wasAppsList && isAppDetails) {
transitionName = 'depth-forward'
} else if (wasAppDetails && isAppsList) {
transitionName = 'depth-back'
} else if (wasMarketplaceList && isMarketplaceDetails) {
transitionName = 'depth-forward'
} else if (wasMarketplaceDetails && isMarketplaceList) {
transitionName = 'depth-back'
} else if (wasCloudList && isCloudFolder) {
transitionName = 'depth-forward'
} else if (wasCloudFolder && isCloudList) {
transitionName = 'depth-back'
} else if (wasMarketplaceList && isAppDetails) {
transitionName = 'depth-forward'
} else if (wasAppDetails && isMarketplaceList) {
transitionName = 'depth-back'
}
// Desktop: no transition between Apps <-> Marketplace (same-page tab feel)
else if ((wasAppsList && isMarketplaceList) || (wasMarketplaceList && isAppsList)) {
transitionName = 'fade'
}
// Vertical transition: between main tabs (desktop)
else {
const currentIndex = TAB_ORDER.indexOf(currentPath)
const previousIndex = TAB_ORDER.indexOf(previousPath)
if (currentIndex !== -1 && previousIndex !== -1 && currentIndex !== previousIndex) {
transitionName = currentIndex > previousIndex ? 'slide-down' : 'slide-up'
}
}
previousPath = currentPath
previousTab = (currentRoute.query.tab as string) || ''
return transitionName
}
return { getTransitionName }
}