Navigation.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <!--
  2. Menu Navigation
  3. -->
  4. <template>
  5. <!-- Navigation (écran large) -->
  6. <div v-if="mdAndUp">
  7. <LayoutNavigationTopbar />
  8. <v-row class="navigation-lg" style="margin-top: 0 !important;">
  9. <!-- Logo Opentalent -->
  10. <v-col cols="2">
  11. <nuxt-link to="/">
  12. <v-img class="logo" src="/images/logo/navigation-logo.png" />
  13. </nuxt-link>
  14. </v-col>
  15. <!-- Menu principal -->
  16. <v-col cols="10" class="pl-6">
  17. <v-menu
  18. v-for="item in menu"
  19. :key="item.label"
  20. open-on-hover
  21. >
  22. <template v-slot:activator="{ props }">
  23. <nuxt-link
  24. v-bind="props"
  25. class="menuItem first-level"
  26. :to="item.to"
  27. >
  28. {{ item.label }}
  29. </nuxt-link>
  30. </template>
  31. <v-list
  32. v-if="item.children?.length > 0"
  33. class="menu-list"
  34. >
  35. <v-list-item
  36. v-for="child in item.children"
  37. :key="child.label"
  38. :to="child.to"
  39. class="menuItem"
  40. >
  41. <v-list-item-title>{{ child.label }}</v-list-item-title>
  42. </v-list-item>
  43. </v-list>
  44. </v-menu>
  45. </v-col>
  46. </v-row>
  47. </div>
  48. <!-- Navigation (petit écran) -->
  49. <v-app
  50. v-else
  51. class="navigation-sm"
  52. >
  53. <!-- Top bar -->
  54. <v-app-bar app>
  55. <v-app-bar-nav-icon
  56. @click="toggleMenu"
  57. />
  58. <nuxt-link to="/">
  59. <v-img class="logo-md" src="/images/logo/navigation-logo.png" />
  60. </nuxt-link>
  61. <nuxt-link
  62. to="https://admin.opentalent.fr/#/login/"
  63. style="text-decoration: none"
  64. >
  65. <v-btn text>
  66. <v-icon left class="fas fa-user icon"></v-icon>
  67. </v-btn>
  68. </nuxt-link>
  69. <v-btn text>
  70. <v-icon left class="fas fa-phone icon"></v-icon>
  71. </v-btn>
  72. <AgendaLink href="/agenda-culturel" style="text-decoration: none">
  73. <v-btn text>
  74. <v-icon left class="fas fa-calendar icon"></v-icon>
  75. </v-btn>
  76. </AgendaLink>
  77. </v-app-bar>
  78. <!-- Tiroir de navigation principal -->
  79. <v-navigation-drawer
  80. v-model="isMenuOpen"
  81. app
  82. temporary
  83. >
  84. <v-list nav dense>
  85. <v-list-item
  86. v-if="isSubMenu"
  87. class="menuItem back-item"
  88. @click="onBackItemClick"
  89. >
  90. <v-list-item-title>
  91. <v-icon icon="fas fa-caret-left" class="mr-1"/> Retour
  92. </v-list-item-title>
  93. </v-list-item>
  94. <v-list-item
  95. v-for="(item, index) in activeMenu"
  96. :key="item.label"
  97. :to="item.to"
  98. class="menuItem"
  99. @click="onMenuItemClick(index, item)"
  100. >
  101. <v-list-item-title>
  102. {{ item.label }}
  103. </v-list-item-title>
  104. </v-list-item>
  105. </v-list>
  106. </v-navigation-drawer>
  107. </v-app>
  108. </template>
  109. <script setup lang="ts">
  110. import { useDisplay } from "vuetify";
  111. import type { MainMenuItem } from "~/types/interface";
  112. import AgendaLink from "~/components/Common/AgendaLink.vue";
  113. const { mdAndUp } = useDisplay();
  114. const menu: Array<MainMenuItem> = [
  115. {
  116. label: "Nos logiciels",
  117. children: [
  118. { label: "Opentalent Artist", to: "/opentalent_artist" },
  119. { label: "Opentalent School", to: "/opentalent_school" },
  120. { label: "Opentalent Manager", to: "/opentalent_manager" },
  121. ]
  122. },
  123. {
  124. label: "Nos services",
  125. children: [
  126. { label: "Formations", to: "/formations" },
  127. { label: "Webinaires", to: "/webinaires" },
  128. ]
  129. },
  130. {
  131. label: "À propos",
  132. children: [
  133. { label: "Qui sommes-nous", to: "/qui-sommes-nous" },
  134. { label: "Nous rejoindre", to: "/nous-rejoindre" },
  135. ]
  136. },
  137. { label: "Actualités", to: "/actualites" },
  138. { label: "Contact", to: "/nous-contacter" },
  139. ]
  140. // Menu dépliant (petit écran)
  141. const isMenuOpen: Ref<boolean> = ref(false);
  142. const toggleMenu = () => {
  143. isMenuOpen.value = !isMenuOpen.value;
  144. };
  145. const activeMenuIndex: Ref<number | null> = ref(null)
  146. const activeMenu = computed(() =>
  147. activeMenuIndex.value !== null ? menu[activeMenuIndex.value].children : menu
  148. )
  149. /**
  150. * Determines if the is active menu is a sub-menu .
  151. *
  152. * @function isSubMenu
  153. * @returns {boolean} - True if a sub-menu is active, otherwise false.
  154. */
  155. const isSubMenu = computed(() => activeMenuIndex.value !== null)
  156. /**
  157. * Handles the click event on a menu item.
  158. *
  159. * @param {number} index - The index of the clicked menu item.
  160. * @param {MainMenuItem} item - The clicked menu item.
  161. * @returns {void}
  162. */
  163. const onMenuItemClick = (index: number, item: MainMenuItem): void => {
  164. if (!item.children) {
  165. return
  166. }
  167. withAnimation(() => activeMenuIndex.value = index)
  168. }
  169. /**
  170. * Function to handle back button click event.
  171. */
  172. const onBackItemClick = (): void => {
  173. withAnimation(() => activeMenuIndex.value = null)
  174. }
  175. /**
  176. * Déclenche une animation de changement de menu en fermant et rouvrant le drawer
  177. * @param callback
  178. */
  179. const withAnimation = (callback: () => void) => {
  180. isMenuOpen.value = false
  181. callback()
  182. setTimeout(() => {isMenuOpen.value = true}, 85)
  183. }
  184. </script>
  185. <style scoped>
  186. .logo {
  187. height: 100px;
  188. width: 300px;
  189. }
  190. .logo-md {
  191. width: 150px;
  192. height: 300px;
  193. }
  194. .icon {
  195. color: var(--on-neutral-color);
  196. }
  197. .menuItem, .menuItem .v-list-item-title {
  198. font-weight: 500;
  199. font-size: 0.9rem;
  200. letter-spacing: 0.1em;
  201. text-transform: uppercase;
  202. color: var(--primary-color);
  203. cursor: pointer;
  204. text-decoration: none !important;
  205. }
  206. /**
  207. Navigation grands écrans
  208. */
  209. .navigation-lg {
  210. display: flex;
  211. align-items: center;
  212. background-color: var(--neutral-color);
  213. .menuItem {
  214. padding: 18px;
  215. }
  216. .menuItem.first-level {
  217. font-size: 1.3rem;
  218. margin-right: 1rem;
  219. font-weight: 700;
  220. letter-spacing: 0.05em;
  221. }
  222. }
  223. /**
  224. Navigation petits écrans
  225. */
  226. .navigation-sm {
  227. background-color: var(--neutral-color);
  228. position: fixed;
  229. top: 0;
  230. }
  231. .back-item {
  232. border-bottom: solid 1px var(--on-neutral-color-light);
  233. border-radius: 0;
  234. .v-list-item-title {
  235. display: flex;
  236. align-items: center;
  237. }
  238. }
  239. </style>