Browse Source

complete responsiveness for navigation bar

Olivier Massot 1 năm trước cách đây
mục cha
commit
2ef8049116

+ 12 - 228
components/Layout/Navigation.vue

@@ -2,128 +2,25 @@
 Menu Navigation
 -->
 <template>
-  <!-- Navigation (écran large) -->
-  <div v-if="mdAndUp" v-intersect="onIntersect">
-    <LayoutNavigationTopbar />
-
-    <v-row class="navigation-lg" style="margin-top: 0 !important;">
-      <!-- Logo Opentalent -->
-      <v-col cols="2">
-        <nuxt-link to="/">
-          <v-img class="logo" src="/images/logo/navigation-logo.png" />
-        </nuxt-link>
-      </v-col>
-
-      <!-- Menu principal -->
-      <v-col cols="10" class="pl-6">
-        <v-menu
-          v-for="item in menu"
-          :key="item.label"
-          :open-on-hover="true"
-        >
-          <template v-slot:activator="{ props }">
-            <nuxt-link
-              v-bind="props"
-              class="menuItem first-level"
-              :to="item.to"
-            >
-              {{ item.label }}
-            </nuxt-link>
-          </template>
-
-          <v-list
-            v-if="item.children?.length > 0"
-            class="menu-list"
-          >
-            <v-list-item
-              v-for="child in item.children"
-              :key="child.label"
-              :to="child.to"
-              class="menuItem"
-            >
-              <v-list-item-title>{{ child.label }}</v-list-item-title>
-            </v-list-item>
-          </v-list>
-        </v-menu>
-      </v-col>
-    </v-row>
+  <div v-intersect="onIntersect">
+    <!-- Navigation (écran large) -->
+    <div v-if="lgAndUp">
+      <LayoutNavigationLg :menu="menu" />
+    </div>
+
+    <!-- Navigation (petit écran) -->
+    <div v-else>
+      <LayoutNavigationMd :menu="menu" />
+    </div>
   </div>
-
-  <!-- Navigation (petit écran) -->
-  <v-app
-    v-else
-    class="navigation-sm"
-  >
-    <!-- Top bar -->
-    <v-app-bar app>
-      <v-app-bar-nav-icon
-        @click="toggleMenu"
-      />
-
-      <nuxt-link to="/">
-        <v-img class="logo-md" src="/images/logo/navigation-logo.png" />
-      </nuxt-link>
-
-      <nuxt-link
-        to="https://admin.opentalent.fr/#/login/"
-        style="text-decoration: none"
-      >
-        <v-btn text>
-          <v-icon left class="fas fa-user icon"></v-icon>
-        </v-btn>
-      </nuxt-link>
-
-      <v-btn text>
-        <v-icon left class="fas fa-phone icon"></v-icon>
-      </v-btn>
-
-      <AgendaLink href="/agenda-culturel" style="text-decoration: none">
-        <v-btn text>
-          <v-icon left class="fas fa-calendar icon"></v-icon>
-        </v-btn>
-      </AgendaLink>
-    </v-app-bar>
-
-    <!-- Tiroir de navigation principal -->
-    <v-navigation-drawer
-      v-model="isMenuOpen"
-      app
-      temporary
-    >
-      <v-list nav dense>
-        <v-list-item
-          v-if="isSubMenu"
-          class="menuItem back-item"
-          @click="onBackItemClick"
-        >
-          <v-list-item-title>
-            <v-icon icon="fas fa-caret-left" class="mr-1"/> Retour
-          </v-list-item-title>
-        </v-list-item>
-
-        <v-list-item
-          v-for="(item, index) in activeMenu"
-          :key="item.label"
-          :to="item.to"
-          class="menuItem"
-          @click="onMenuItemClick(index, item)"
-        >
-          <v-list-item-title>
-            {{ item.label }}
-          </v-list-item-title>
-        </v-list-item>
-      </v-list>
-    </v-navigation-drawer>
-  </v-app>
 </template>
 
 <script setup lang="ts">
 import { useDisplay } from "vuetify";
 import type { MainMenuItem } from "~/types/interface";
-import AgendaLink from "~/components/Common/AgendaLink.vue";
-import Footer from "~/components/Layout/Footer/Footer.vue";
 import { useLayoutStore } from "~/stores/layoutStore";
-const { mdAndUp } = useDisplay();
+
+const { lgAndUp } = useDisplay();
 
 const menu: Array<MainMenuItem> = [
   {
@@ -152,57 +49,6 @@ const menu: Array<MainMenuItem> = [
   { label: "Contact", to: "/nous-contacter" },
 ]
 
-// Menu dépliant (petit écran)
-const isMenuOpen: Ref<boolean> = ref(false);
-const toggleMenu = () => {
-  isMenuOpen.value = !isMenuOpen.value;
-};
-
-const activeMenuIndex: Ref<number | null> = ref(null)
-
-const activeMenu = computed(() =>
-  activeMenuIndex.value !== null ? menu[activeMenuIndex.value].children : menu
-)
-
-/**
- * Determines if the is active menu is a sub-menu .
- *
- * @function isSubMenu
- * @returns {boolean} - True if a sub-menu is active, otherwise false.
- */
-const isSubMenu = computed(() => activeMenuIndex.value !== null)
-
-/**
- * Handles the click event on a menu item.
- *
- * @param {number} index - The index of the clicked menu item.
- * @param {MainMenuItem} item - The clicked menu item.
- * @returns {void}
- */
-const onMenuItemClick = (index: number, item: MainMenuItem): void => {
-  if (!item.children) {
-    return
-  }
-  withAnimation(() => activeMenuIndex.value = index)
-}
-
-/**
- * Function to handle back button click event.
- */
-const onBackItemClick = (): void => {
-  withAnimation(() => activeMenuIndex.value = null)
-}
-
-/**
- * Déclenche une animation de changement de menu en fermant et rouvrant le drawer
- * @param callback
- */
-const withAnimation = (callback: () => void) => {
-  isMenuOpen.value = false
-  callback()
-  setTimeout(() => {isMenuOpen.value = true}, 85)
-}
-
 const layoutStore = useLayoutStore()
 const onIntersect = (isIntersecting: boolean) => {
   layoutStore.setIsHeaderVisible(isIntersecting)
@@ -212,66 +58,4 @@ const onIntersect = (isIntersecting: boolean) => {
 
 <style scoped>
 
-.logo {
-  height: 100px;
-  width: 300px;
-}
-
-.logo-md {
-  width: 150px;
-  height: 300px;
-}
-
-.icon {
-  color: var(--on-neutral-color);
-}
-
-.menuItem, .menuItem .v-list-item-title {
-  font-weight: 500;
-  font-size: 0.9rem;
-  letter-spacing: 0.1em;
-  text-transform: uppercase;
-  color: var(--primary-color);
-  cursor: pointer;
-  text-decoration: none !important;
-}
-
-/**
-  Navigation grands écrans
- */
-.navigation-lg {
-  display: flex;
-  align-items: center;
-  background-color: var(--neutral-color);
-
-  .menuItem  {
-    padding: 18px;
-  }
-
-  .menuItem.first-level {
-    font-size: 1.3rem;
-    margin-right: 1rem;
-    font-weight: 700;
-    letter-spacing: 0.05em;
-  }
-}
-
-/**
-  Navigation petits écrans
- */
-.navigation-sm {
-  background-color: var(--neutral-color);
-  position: fixed;
-  top: 0;
-}
-
-.back-item {
-  border-bottom: solid 1px var(--on-neutral-color-light);
-  border-radius: 0;
-
-  .v-list-item-title {
-    display: flex;
-    align-items: center;
-  }
-}
 </style>

+ 97 - 0
components/Layout/Navigation/Lg.vue

@@ -0,0 +1,97 @@
+<!-- Barre de navigation (écran large) -->
+<template>
+  <div>
+    <LayoutNavigationTopbar />
+
+    <v-row class="navigation-lg" style="margin-top: 0 !important;">
+      <!-- Logo Opentalent -->
+      <v-col cols="2">
+        <nuxt-link to="/">
+          <v-img class="logo" src="/images/logo/navigation-logo.png" />
+        </nuxt-link>
+      </v-col>
+
+      <!-- Menu principal -->
+      <v-col cols="10" class="pl-6">
+        <v-menu
+          v-for="item in menu"
+          :key="item.label"
+          :open-on-hover="true"
+        >
+          <template v-slot:activator="{ props }">
+            <nuxt-link
+              v-bind="props"
+              class="menuItem first-level"
+              :to="item.to"
+            >
+              {{ item.label }}
+            </nuxt-link>
+          </template>
+
+          <v-list
+            v-if="item.children?.length! > 0"
+            class="menu-list"
+          >
+            <v-list-item
+              v-for="child in item.children"
+              :key="child.label"
+              :to="child.to"
+              class="menuItem"
+            >
+              <v-list-item-title>{{ child.label }}</v-list-item-title>
+            </v-list-item>
+          </v-list>
+        </v-menu>
+      </v-col>
+    </v-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from "@vue/runtime-core";
+import type { MainMenuItem } from "~/types/interface";
+
+const props = defineProps({
+  menu: {
+    type: Array as PropType<Array<MainMenuItem>>
+  }
+})
+</script>
+
+<style scoped lang="scss">
+.logo {
+  height: 100px;
+  width: 300px;
+}
+
+.icon {
+  color: var(--on-neutral-color);
+}
+
+.menuItem, .menuItem .v-list-item-title {
+  font-weight: 500;
+  font-size: 0.9rem;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+  color: var(--primary-color);
+  cursor: pointer;
+  text-decoration: none !important;
+}
+
+.navigation-lg {
+  display: flex;
+  align-items: center;
+  background-color: var(--neutral-color);
+
+  .menuItem  {
+    padding: 18px;
+  }
+
+  .menuItem.first-level {
+    font-size: 1.3rem;
+    margin-right: 1rem;
+    font-weight: 700;
+    letter-spacing: 0.05em;
+  }
+}
+</style>

+ 163 - 0
components/Layout/Navigation/Md.vue

@@ -0,0 +1,163 @@
+<template>
+  <v-app class="navigation-sm">
+    <!-- Top bar -->
+    <v-app-bar app>
+      <v-app-bar-nav-icon
+        @click="toggleMenu"
+      />
+
+      <nuxt-link to="/">
+        <v-img class="logo-md" src="/images/logo/navigation-logo.png" />
+      </nuxt-link>
+
+      <v-spacer />
+
+      <div class="mx-4">
+        <v-btn
+          href="https://admin.opentalent.fr/#/login/"
+          icon="fas fa-user"
+          class="icon"
+        />
+
+        <v-btn
+          to="/nous-contacter"
+          icon="fas fa-phone"
+          class="icon"
+        />
+
+        <AgendaLink href="/agenda-culturel">
+          <v-btn
+            icon="fas fa-calendar"
+            class="icon"
+          />
+        </AgendaLink>
+      </div>
+    </v-app-bar>
+
+    <!-- Tiroir de navigation principal -->
+    <v-navigation-drawer
+      v-model="isMenuOpen"
+      app
+      temporary
+    >
+      <v-list nav dense>
+        <v-list-item
+          v-if="isSubMenu"
+          class="menuItem back-item"
+          @click="onBackItemClick"
+        >
+          <v-list-item-title>
+            <v-icon icon="fas fa-caret-left" class="mr-1"/> Retour
+          </v-list-item-title>
+        </v-list-item>
+
+        <v-list-item
+          v-for="(item, index) in activeMenu"
+          :key="item.label"
+          :to="item.to"
+          class="menuItem"
+          @click="onMenuItemClick(index, item)"
+        >
+          <v-list-item-title class="d-flex flex-row w-100">
+            <span class="flex-grow-1">
+              {{ item.label }}
+            </span>
+            <span v-if="item.children?.length! > 0">
+              <v-icon icon="fa fa-angle-right" />
+            </span>
+          </v-list-item-title>
+        </v-list-item>
+      </v-list>
+    </v-navigation-drawer>
+  </v-app>
+</template>
+
+<script setup lang="ts">
+import AgendaLink from "~/components/Common/AgendaLink.vue";
+import type { PropType } from "@vue/runtime-core";
+import type { MainMenuItem } from "~/types/interface";
+
+const props = defineProps({
+  menu: {
+    type: Array as PropType<Array<MainMenuItem>>
+  }
+})
+
+const isMenuOpen: Ref<boolean> = ref(false);
+const toggleMenu = () => {
+  isMenuOpen.value = !isMenuOpen.value;
+};
+
+const activeMenuIndex: Ref<number | null> = ref(null)
+
+const activeMenu = computed(() =>
+  activeMenuIndex.value !== null ? props.menu![activeMenuIndex.value].children : props.menu
+)
+
+/**
+ * Determines if the is active menu is a sub-menu .
+ *
+ * @function isSubMenu
+ * @returns {boolean} - True if a sub-menu is active, otherwise false.
+ */
+const isSubMenu = computed(() => activeMenuIndex.value !== null)
+
+/**
+ * Handles the click event on a menu item.
+ *
+ * @param {number} index - The index of the clicked menu item.
+ * @param {MainMenuItem} item - The clicked menu item.
+ * @returns {void}
+ */
+const onMenuItemClick = (index: number, item: MainMenuItem): void => {
+  if (!item.children) {
+    return
+  }
+  withAnimation(() => activeMenuIndex.value = index)
+}
+
+/**
+ * Function to handle back button click event.
+ */
+const onBackItemClick = (): void => {
+  withAnimation(() => activeMenuIndex.value = null)
+}
+
+/**
+ * Déclenche une animation de changement de menu en fermant et rouvrant le drawer
+ * @param callback
+ */
+const withAnimation = (callback: () => void) => {
+  isMenuOpen.value = false
+  callback()
+  setTimeout(() => {isMenuOpen.value = true}, 85)
+}
+</script>
+
+<style scoped lang="scss">
+.navigation-sm {
+  background-color: var(--neutral-color);
+  position: fixed;
+  top: 0;
+  z-index: 1000;
+}
+
+.logo-md {
+  width: 150px;
+  height: 300px;
+}
+
+.back-item {
+  border-bottom: solid 1px var(--on-neutral-color-light);
+  border-radius: 0;
+
+  .v-list-item-title {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.icon {
+  color: var(--on-neutral-color);
+}
+</style>