Move SubNavigation to its own component

This commit is contained in:
Ramon Wenger 2024-10-27 20:24:35 +01:00
parent 72c9fe8c9e
commit 44e618bf9d
4 changed files with 108 additions and 79 deletions

View File

@ -28,7 +28,14 @@ const isExternalLink = computed(() => {
<slot />
<it-icon-external-link />
</a>
<router-link v-else v-slot="{ isActive, href, navigate }" v-bind="$props" custom>
<!-- make `:to` explicit -->
<router-link
v-else
v-slot="{ isActive, href, navigate }"
v-bind="$props"
:to="$props.to"
custom
>
<a
v-bind="$attrs"
:class="isActive ? activeClass : ''"

View File

@ -0,0 +1,95 @@
<script setup lang="ts">
import SubNavItem from "@/components/header/SubNavItem.vue";
import { Listbox, ListboxOption, ListboxOptions } from "@headlessui/vue";
import { computed, ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
export interface EntryRoute {
name: string;
}
export type EntryOrExternalRoute = EntryRoute | string;
export interface SubNavEntry {
id: number;
name: string;
route: EntryOrExternalRoute;
}
export interface Props {
items: SubNavEntry[];
}
const props = defineProps<Props>();
const isCurrentRoute = (route: { name: string } | string) => {
return typeof route !== "string" && route?.name === router.currentRoute.value.name;
};
const currentRouteName = computed(() => {
return props.items.find((item) => isCurrentRoute(item.route))?.name || "";
});
const open = ref<boolean>(false);
const currentRoute = ref(props.items.find((item) => isCurrentRoute(item.route)));
const selectRoute = (current: SubNavEntry) => {
// we use this to mimic VueRouter's active flag
open.value = false;
currentRoute.value = current;
};
</script>
<template>
<nav class="border-b bg-white px-4 lg:px-8">
<Listbox as="div" :model-value="currentRoute" by="id">
<div class="relative w-full py-2 lg:hidden">
<button
class="relative flex w-full cursor-default flex-row items-center border bg-white py-3 pl-5 pr-10 text-left"
@click="open = !open"
>
{{ currentRouteName }}
<span
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
>
<it-icon-arrow-down class="h-5 w-5" aria-hidden="true" />
</span>
</button>
<ListboxOptions
v-if="open"
class="absolute top-14 z-50 flex w-full cursor-default flex-col rounded-xl border-0 bg-white text-left shadow-lg"
static
>
<ListboxOption
v-for="item in items"
:key="item.id"
v-slot="{ selected }"
:value="item"
class="relative w-full border-b py-3 pl-10 pr-10 last:border-b-0"
>
<SubNavItem
:to="item.route"
class="flex items-center gap-2"
@click="selectRoute(item)"
>
<it-icon-check
v-if="selected"
class="absolute left-2 top-1/2 w-8 -translate-y-1/2"
/>
{{ item.name }}
</SubNavItem>
</ListboxOption>
</ListboxOptions>
</div>
</Listbox>
<ul class="hidden flex-col gap-12 lg:flex lg:flex-row">
<li
v-for="item in items"
:key="item.id"
class="border-t-2 border-t-transparent"
:class="{ 'border-b-2 border-b-blue-900': isCurrentRoute(item.route) }"
>
<!-- todo: split by external / internal and align external links to the right -->
<SubNavItem :to="item.route" class="block py-3">
{{ item.name }}
</SubNavItem>
</li>
</ul>
</nav>
</template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import SubNavItem from "@/components/header/SubNavItem.vue";
import SubNavigation, { type SubNavEntry } from "@/components/header/SubNavigation.vue";
import { useCurrentCourseSession, useEvaluationWithFeedback } from "@/composables";
import {
CERTIFICATES_ROUTE,
@ -7,16 +7,12 @@ import {
COMPETENCES_ROUTE,
SELF_EVALUATION_ROUTE,
} from "@/router/names";
import { Listbox, ListboxOption, ListboxOptions } from "@headlessui/vue";
import { useTranslation } from "i18next-vue";
import * as log from "loglevel";
import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { onMounted } from "vue";
log.debug("CompetenceParentPage created");
const router = useRouter();
const { t } = useTranslation();
const currentCourseSession = useCurrentCourseSession();
@ -39,11 +35,7 @@ const competencesRoute = {
name: COMPETENCES_ROUTE,
};
const isCurrentRoute = (route: { name: string } | string) => {
return typeof route !== "string" && route?.name === router.currentRoute.value.name;
};
const items = [
const items: SubNavEntry[] = [
{ id: 0, name: t("a.Übersicht"), route: competenceRoute },
...(currentCourseSession.value.course.configuration.enable_competence_certificates
? [
@ -66,84 +58,19 @@ const items = [
{
id: 4,
name: "MS Teams",
iconName: "it-icon-external-link",
route: "https://iterativ.ch",
},
{
id: 5,
name: "Vorschau Teilnehmer",
iconName: "it-icon-external-link",
route: "https://iterativ.ch",
},
];
const currentRouteName = computed(() => {
return items.find((item) => isCurrentRoute(item.route))?.name || "";
});
const open = ref<boolean>(false);
const currentRoute = ref(items.find((item) => isCurrentRoute(item.route)));
const selectRoute = (current) => {
// we use this to mimic VueRouter's active flag
open.value = false;
currentRoute.value = current;
};
</script>
<template>
<div class="bg-gray-200">
<nav class="border-b bg-white px-4 lg:px-8">
<Listbox as="div" :model-value="currentRoute" by="id">
<div class="relative w-full py-2 lg:hidden">
<button
class="relative flex w-full cursor-default flex-row items-center border bg-white py-3 pl-5 pr-10 text-left"
@click="open = !open"
>
{{ currentRouteName }}
<span
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
>
<it-icon-arrow-down class="h-5 w-5" aria-hidden="true" />
</span>
</button>
<ListboxOptions
v-if="open"
class="absolute top-14 z-50 flex w-full cursor-default flex-col rounded-xl border-0 bg-white text-left shadow-lg"
static
>
<ListboxOption
v-for="item in items"
:key="item.id"
v-slot="{ selected }"
:value="item"
class="relative w-full border-b py-3 pl-10 pr-10 last:border-b-0"
>
<SubNavItem
:to="item.route"
class="flex items-center gap-2"
@click="selectRoute(item)"
>
<it-icon-check
v-if="selected"
class="absolute left-2 top-1/2 w-8 -translate-y-1/2"
/>
{{ item.name }}
</SubNavItem>
</ListboxOption>
</ListboxOptions>
</div>
</Listbox>
<ul class="hidden flex-col gap-12 lg:flex lg:flex-row">
<li
v-for="item in items"
:key="item.id"
class="border-t-2 border-t-transparent"
:class="{ 'border-b-2 border-b-blue-900': isCurrentRoute(item.route) }"
>
<SubNavItem :to="item.route" class="block py-3">
{{ item.name }}
</SubNavItem>
</li>
</ul>
</nav>
<SubNavigation :items="items" />
<main>
<router-view></router-view>
</main>

View File

@ -1,4 +1,4 @@
import { computed, ref } from "vue";
import { computed } from "vue";
import { useRoute } from "vue-router";
export function useRouteLookups() {