From 29051acc74d3c3bf0b2680398f3fd72ef3ea4d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6ckelmann?= <gp5761@partner.kit.edu> Date: Mon, 3 Feb 2025 16:13:47 +0100 Subject: [PATCH] WIP: Breadcrumbs --- src/App.vue | 82 +++++++++++++++++++++++++++++++++++++++++++++ src/router/index.ts | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/src/App.vue b/src/App.vue index 68e90636a..b37d838ab 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,8 +1,90 @@ +<script setup lang="ts"> +import type { RouteLocationMatched, RouteLocationNormalizedLoaded } from 'vue-router' + +interface BreadCrumbItemData { + name: string + path: any + active: boolean +} +const currentRoute: RouteLocationNormalizedLoaded<string | symbol> = useRoute() +const router = useRouter() +const breadcrumbs = ref<BreadCrumbItemData[]>(calcBreadCrumbs()) + +watch(currentRoute, () => { + breadcrumbs.value = calcBreadCrumbs() +}) + +function calcBreadCrumbContent(matchedRoutes: RouteLocationMatched, calculatedBreadcrumbs: BreadCrumbItemData[], params: object): void { + console.log(params) + if (matchedRoutes && matchedRoutes.meta && typeof matchedRoutes.meta.resolveName === 'function') { + calculatedBreadcrumbs.push({ + path: matchedRoutes.path, + active: false, + name: matchedRoutes.meta.resolveName(), + }) + } + + if (matchedRoutes && matchedRoutes.meta && typeof matchedRoutes.meta.resolveObjectHierarchy === 'function') { + const hierarchy: string[] = matchedRoutes.meta.resolveObjectHierarchy(currentRoute.params) + for (const element of hierarchy) { + calculatedBreadcrumbs.push({ + path: matchedRoutes.path, + active: false, + name: element, + }) + } + } +} + +function calcBreadCrumbs(): BreadCrumbItemData[] { + // Start if route is Home with empty breadcrumbs else with Home to have the Home Breadcrumb in each route as first entry + const calculatedBreadcrumbs: BreadCrumbItemData[] = [] + // Matched Routes are sorted by their definition in the Route Tree. + const matchedRoutes = currentRoute.matched + while (matchedRoutes.length > 0) { + const currentMatchedRoute = matchedRoutes.shift() + if (currentMatchedRoute !== undefined) { + if (currentMatchedRoute && currentMatchedRoute.meta && typeof currentMatchedRoute.meta.resolveParents === 'function') { + const parents = currentMatchedRoute.meta.resolveParents() + for (const parent of parents) { + const resolvedParents = router.resolve(parent).matched + resolvedParents.forEach((resolvedParent) => { + calcBreadCrumbContent(resolvedParent, calculatedBreadcrumbs, currentRoute.params) + }) + } + } + calcBreadCrumbContent(currentMatchedRoute, calculatedBreadcrumbs, currentRoute.params) + } + } + + return calculatedBreadcrumbs +} +</script>iii + <template> <TopBar /> <NavSidebar> <TransactionSidebar> <main min-h-0 overflow-scroll> + <!-- Todo: Fix styling of card so it aligns with dark/white theme --> + <card style="border: none; background-color: #eaeaea; border-radius: 1px"> + <card-content> + <breadcrumb> + <breadcrumb-list> + <!-- Todo: Maybe adjust coloring of active item to be blue like in old NETVS --> + <breadcrumb-item v-for="(breadcrumb, index) of breadcrumbs" :key="index"> + <breadcrumb-link v-if="breadcrumb.active || index + 1 < breadcrumbs.length" :href="breadcrumb.path"> + {{ breadcrumb.name }} + </breadcrumb-link> + <breadcrumb-page v-else> + {{ breadcrumb.name }} + </breadcrumb-page> + <breadcrumb-separator /> + </breadcrumb-item> + </breadcrumb-list> + </breadcrumb> + </card-content> + </card> <RouterView /> </main> </TransactionSidebar> diff --git a/src/router/index.ts b/src/router/index.ts index bdf2e1c73..13c8ed064 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,8 +7,67 @@ const router = createRouter({ { path: '/', name: 'home', + meta: { + resolveName: () => 'Home', + }, component: HomeView, }, + // Das sind nur Beispiel-Routen um die Breadcrumbs zu testen! + { + path: '/dashboard', + name: 'Dashboard', + meta: { + resolveName: () => 'Dashboard', + resolveParents: () => ['/'], + }, + component: HomeView, + children: [ + { + path: '/dashboard/details', + name: 'Dashboard Details', + meta: { + resolveName: () => 'Children', + resolveParents: () => ['/'], + }, + component: HomeView, + }, + ], + }, + { + path: '/org/ou', + name: 'ous', + component: () => HomeView, + meta: { + resolveName: () => 'Organizational Units', + resolveParents: () => ['/'], + }, + children: [ + { + path: '/org/ou/:ou', + name: 'ou', + component: () => HomeView, + meta: { + resolveObjectHierarchy: (query: any) => { + // Todo: Sobald Login implementiert ist, muss die ObjektHierarchy durch einen API Call aufgelöst werden + return ['STUWE', query.ou.toUpperCase()] + }, + }, + children: [ + { + path: '/org/ou/:ou/something/:test', + name: 'test', + component: () => HomeView, + meta: { + resolveName: () => 'Something Unit', + resolveObjectHierarchy: (_query: any) => { + return ['Test'] + }, + }, + }, + ], + }, + ], + }, // { // path: '/about', // name: 'about', -- GitLab