Olivier Massot 1 год назад
Родитель
Сommit
53f9f41839
4 измененных файлов с 207 добавлено и 209 удалено
  1. 197 205
      components/Layout/Navigation.vue
  2. 1 4
      components/Layout/Navigation/Topbar.vue
  3. 3 0
      pages/index.vue
  4. 6 0
      types/interface.d.ts

+ 197 - 205
components/Layout/Navigation.vue

@@ -2,82 +2,64 @@
 Menu Navigation
 -->
 <template>
-  <LayoutNavigationTopbar />
 
-  <v-row class="menu" v-if="!mdAndDown" style="margin-top: 0 !important;">
-    <v-col cols="3">
-      <nuxt-link to="/">
-        <v-img class="logo" src="/images/logo/navigation-logo.png" />
-      </nuxt-link>
-    </v-col>
-
-    <v-col cols="9">
-      <v-menu open-on-hover>
-        <template v-slot:activator="{ props }">
-          <nuxt-link v-bind="props" class="link-style"
-            >Nos logiciels
-          </nuxt-link>
-        </template>
-
-        <v-list class="menu-list">
-          <nuxt-link
-            v-for="software in softwareLinks"
-            :key="software.name"
-            :to="software.href"
-            class="link-styles"
-          >
-            <v-list-item>
-              <v-list-item-title>{{ software.name }}</v-list-item-title>
-            </v-list-item>
-          </nuxt-link>
-        </v-list>
-      </v-menu>
-
-      <v-menu open-on-hover>
-        <template v-slot:activator="{ props }">
-          <nuxt-link v-bind="props" class="link-style">Nos services </nuxt-link>
-        </template>
-
-        <v-list>
-          <nuxt-link
-            v-for="service in serviceLinks"
-            :key="service.name"
-            :to="service.href"
-            class="link-styles"
-          >
-            <v-list-item>
-              <v-list-item-title>{{ service.name }}</v-list-item-title>
-            </v-list-item>
-          </nuxt-link>
-        </v-list>
-      </v-menu>
-
-      <v-menu open-on-hover>
-        <template v-slot:activator="{ props }">
-          <nuxt-link v-bind="props" class="link-style">à propos</nuxt-link>
-        </template>
-
-        <v-list>
-          <nuxt-link
-            v-for="info in aboutLinks"
-            :key="info.name"
-            :to="info.href"
-            class="link-styles"
+  <!-- Navigation (écran large) -->
+  <div v-if="mdAndUp">
+    <LayoutNavigationTopbar />
+
+    <v-row class="navigation-lg" style="margin-top: 0 !important;">
+      <!-- Logo Opentalent -->
+      <v-col cols="3">
+        <nuxt-link to="/">
+          <v-img class="logo" src="/images/logo/navigation-logo.png" />
+        </nuxt-link>
+      </v-col>
+
+      <!-- Menu principal -->
+      <v-col cols="9">
+        <v-menu
+          v-for="item in menu"
+          :key="item.label"
+          open-on-hover
+        >
+          <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-list-item-title>{{ info.name }}</v-list-item-title>
+            <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>
-          </nuxt-link>
-        </v-list>
-      </v-menu>
-
-      <nuxt-link class="link-style" to="/actualites">Actualités</nuxt-link>
-      <nuxt-link class="link-style" to="/nous-contacter">Contact</nuxt-link>
-    </v-col>
-  </v-row>
-  <v-app v-if="mdAndDown" class="navigation-sm">
+          </v-list>
+        </v-menu>
+      </v-col>
+    </v-row>
+  </div>
+
+  <!-- Navigation (petit écran) -->
+  <v-app
+    v-else
+    class="navigation-sm"
+  >
+    <!-- Top bar -->
     <v-app-bar app>
-      <v-app-bar-nav-icon @click="toggleDrawer"></v-app-bar-nav-icon>
+      <v-app-bar-nav-icon
+        @click="toggleMenu"
+      />
 
       <nuxt-link to="/">
         <v-img class="logo-md" src="/images/logo/navigation-logo.png" />
@@ -104,36 +86,33 @@ Menu Navigation
     </v-app-bar>
 
     <!-- Tiroir de navigation principal -->
-    <v-navigation-drawer v-model="drawer" app>
+    <v-navigation-drawer
+      v-model="isMenuOpen"
+      app
+      temporary
+    >
       <v-list nav dense>
         <v-list-item
-          v-for="(item, index) in menuItems"
-          :key="item.id"
-          @click="selectMenu(item.id)"
+          v-if="isSubMenu"
+          class="menuItem back-item"
+          @click="onBackItemClick"
         >
           <v-list-item-title>
-            {{ item.label }}
+            <v-icon icon="fas fa-caret-left" class="mr-1"/> Retour
           </v-list-item-title>
         </v-list-item>
-      </v-list>
-    </v-navigation-drawer>
 
-    <!-- Tiroir de sous-menu -->
-    <v-navigation-drawer v-model="subMenuDrawer" app right temporary>
-      <v-list nav dense>
-        <v-list-item>
-          <v-list-item-title @click="closeSubMenu">Retour</v-list-item-title>
-        </v-list-item>
-        <nuxt-link
-          v-for="(subItem, subIndex) in subMenus[currentMenu]"
-          :key="subIndex"
-          :to="subItem.href"
-          style="text-decoration: none !important; color: black"
+        <v-list-item
+          v-for="(item, index) in activeMenu"
+          :key="item.label"
+          :to="item.to"
+          class="menuItem"
+          @click="onMenuItemClick(index, item)"
         >
-          <v-list-item>
-            <v-list-item-title>{{ subItem.name }}</v-list-item-title>
-          </v-list-item>
-        </nuxt-link>
+          <v-list-item-title>
+            {{ item.label }}
+          </v-list-item-title>
+        </v-list-item>
       </v-list>
     </v-navigation-drawer>
   </v-app>
@@ -141,139 +120,152 @@ Menu Navigation
 
 <script setup lang="ts">
 import { useDisplay } from "vuetify";
-const { mdAndDown } = useDisplay();
-
-const softwareLinks = ref([
-  { name: "Opentalent Artist", href: "/opentalent_artist" },
-  { name: "Opentalent School", href: "/opentalent_school" },
-  { name: "Opentalent Manager", href: "/opentalent_manager" },
-]);
-
-const serviceLinks = ref([
-  { name: "Formations", href: "/formations" },
-  { name: "Webinaires", href: "/webinaires" },
-]);
-
-const aboutLinks = ref([
-  { name: "Qui sommes-nous", href: "/qui-sommes-nous" },
-  { name: "Nous rejoindre", href: "/nous-rejoindre" },
-]);
-
-const drawer = ref(false);
-const subMenuDrawer = ref(false);
-const currentMenu = ref("");
-const menuItems = ref([
-  { id: "logiciels", label: "Nos logiciels" },
-  { id: "services", label: "Nos services" },
-  { id: "about", label: "À propos" },
-  { id: "actualites", label: "Actualités" },
-  { id: "contact", label: "Contact" },
-]);
-
-const subMenus = ref({
-  logiciels: [
-    { name: "Opentalent Artist", href: "/opentalent_artist" },
-    { name: "Opentalent School", href: "/opentalent_school" },
-    { name: "Opentalent Manager", href: "/opentalent_manager" },
-  ],
-  services: [
-    { name: "Formations", href: "/formations" },
-    { name: "Webinaires", href: "/webinaires" },
-  ],
-  propos: [
-    { name: "Qui sommes-nous", href: "/qui-sommes-nous" },
-    { name: "Nous rejoindre", href: "/nous-rejoindre" },
-  ],
-});
-
-const toggleDrawer = () => {
-  drawer.value = !drawer.value;
+import { MainMenuItem } from "~/types/interface";
+const { mdAndUp } = useDisplay();
+
+const menu: Array<MainMenuItem> = [
+  {
+    label: "Nos logiciels",
+    children: [
+      { label: "Opentalent Artist", to: "/opentalent_artist" },
+      { label: "Opentalent School", to: "/opentalent_school" },
+      { label: "Opentalent Manager", to: "/opentalent_manager" },
+    ]
+  },
+  {
+    label: "Nos services",
+    children: [
+      { label: "Formations", to: "/formations" },
+      { label: "Webinaires", to: "/webinaires" },
+    ]
+  },
+  {
+    label: "À propos",
+    children: [
+      { label: "Qui sommes-nous", to: "/qui-sommes-nous" },
+      { label: "Nous rejoindre", to: "/nous-rejoindre" },
+    ]
+  },
+  { label: "Actualités", to: "/actualites" },
+  { label: "Contact", to: "/nous-contacter" },
+]
+
+// Menu dépliant (petit écran)
+const isMenuOpen: Ref<boolean> = ref(false);
+const toggleMenu = () => {
+  isMenuOpen.value = !isMenuOpen.value;
 };
 
-const selectMenu = (id) => {
-  if (subMenus.value[id]) {
-    currentMenu.value = id;
-    subMenuDrawer.value = true;
-    drawer.value = false;
-  } else {
-    switch (id) {
-      case "actualites":
-        window.location.href = "/actualites";
-        break;
-      case "contact":
-        window.location.href = "/nous-contacter";
-        break;
-    }
+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 closeSubMenu = () => {
-  subMenuDrawer.value = false;
-  drawer.value = true;
-};
 </script>
 
 <style scoped>
-.navigation-sm {
-  background-color: #ffffff;
-  position: fixed;
-  top: 0;
+
+.logo {
+  height: 8rem;
 }
-/* =============== menu Styles =============== */
 
-.menu {
-  display: flex;
-  align-items: center;
-  background-color: #ffffff;
+.logo-md {
+  width: 150px;
+  height: 300px;
 }
-.v-list-item-title {
-  font-family: "Barlow";
+
+.icon {
+  color: #000000;
+}
+
+.menuItem, .menuItem .v-list-item-title {
+  font-family: "Barlow", serif;
   font-style: normal;
   font-weight: 500;
   font-size: 0.9rem;
   letter-spacing: 0.1em;
   text-transform: uppercase;
   color: #0e2d32;
-}
-.common-styles {
-  font-family: "Barlow";
-  font-style: normal;
-  font-size: 1.1rem;
-  padding: 0.1rem;
-  font-weight: 700;
-  letter-spacing: 0.1em;
-  text-transform: uppercase;
-  color: #0e2d32;
-  text-decoration: none !important;
-}
-.link-style {
-  font-family: "Barlow";
-  font-style: normal;
-  font-size: 1rem;
-  margin-right: 3rem;
-  font-weight: 700;
-  letter-spacing: 0.05em;
-  text-transform: uppercase;
-  color: #0e2d32;
-  text-decoration: none !important;
   cursor: pointer;
-}
-.link-styles {
-  font-family: "Barlow";
-  font-style: normal;
   text-decoration: none !important;
-  color: #0e2d32;
 }
-.logo {
-  height: 8rem;
+
+/**
+  Navigation grands écrans
+ */
+.navigation-lg {
+  display: flex;
+  align-items: center;
+  background-color: #ffffff;
+
+  .menuItem  {
+    padding: 18px;
+  }
+
+  .menuItem.first-level {
+    font-size: 1rem;
+    margin-right: 1rem;
+    font-weight: 700;
+    letter-spacing: 0.05em;
+  }
 }
 
-.logo-md {
-  width: 150px;
-  height: 300px;
+/**
+  Navigation petits écrans
+ */
+.navigation-sm {
+  background-color: #ffffff;
+  position: fixed;
+  top: 0;
 }
 
-.icon {
-  color: #000000;
+.back-item {
+  border-bottom: solid 1px darkgray;
+  border-radius: 0;
+
+  .v-list-item-title {
+    display: flex;
+    align-items: center;
+  }
 }
 </style>

+ 1 - 4
components/Layout/Navigation/Topbar.vue

@@ -1,8 +1,5 @@
 <template>
-  <div
-    class="top-bar"
-    v-if="!mdAndDown"
-  >
+  <div class="top-bar" >
       <v-btn
         href="https://admin.opentalent.fr/#/login/"
         prepend-icon="fas fa-user"

+ 3 - 0
pages/index.vue

@@ -25,6 +25,9 @@
 </template>
 
 <script setup>
+import { useDisplay } from "vuetify";
+
+const { mdAndDown } = useDisplay();
 </script>
 
 <style scoped>

+ 6 - 0
types/interface.d.ts

@@ -11,3 +11,9 @@ interface StickyMenuAction {
   text: string,
   url?: string,
 }
+
+interface MainMenuItem {
+  label: string,
+  to?: string,
+  children?: Array<MainMenuItem>
+}