Navigation.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <!--
  2. Menu Navigation
  3. -->
  4. <template>
  5. <!-- Navigation (écran large) -->
  6. <div v-if="mdAndUp" v-intersect="onIntersect">
  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. import Footer from "~/components/Layout/Footer/Footer.vue";
  114. import { useLayoutStore } from "~/stores/layoutStore";
  115. const { mdAndUp } = useDisplay();
  116. const menu: Array<MainMenuItem> = [
  117. {
  118. label: "Nos logiciels",
  119. children: [
  120. { label: "Opentalent Artist", to: "/opentalent_artist" },
  121. { label: "Opentalent School", to: "/opentalent_school" },
  122. { label: "Opentalent Manager", to: "/opentalent_manager" },
  123. ]
  124. },
  125. {
  126. label: "Nos services",
  127. children: [
  128. { label: "Formations", to: "/formations" },
  129. { label: "Webinaires", to: "/webinaires" },
  130. ]
  131. },
  132. {
  133. label: "À propos",
  134. children: [
  135. { label: "Qui sommes-nous", to: "/qui-sommes-nous" },
  136. { label: "Nous rejoindre", to: "/nous-rejoindre" },
  137. ]
  138. },
  139. { label: "Actualités", to: "/actualites" },
  140. { label: "Contact", to: "/nous-contacter" },
  141. ]
  142. // Menu dépliant (petit écran)
  143. const isMenuOpen: Ref<boolean> = ref(false);
  144. const toggleMenu = () => {
  145. isMenuOpen.value = !isMenuOpen.value;
  146. };
  147. const activeMenuIndex: Ref<number | null> = ref(null)
  148. const activeMenu = computed(() =>
  149. activeMenuIndex.value !== null ? menu[activeMenuIndex.value].children : menu
  150. )
  151. /**
  152. * Determines if the is active menu is a sub-menu .
  153. *
  154. * @function isSubMenu
  155. * @returns {boolean} - True if a sub-menu is active, otherwise false.
  156. */
  157. const isSubMenu = computed(() => activeMenuIndex.value !== null)
  158. /**
  159. * Handles the click event on a menu item.
  160. *
  161. * @param {number} index - The index of the clicked menu item.
  162. * @param {MainMenuItem} item - The clicked menu item.
  163. * @returns {void}
  164. */
  165. const onMenuItemClick = (index: number, item: MainMenuItem): void => {
  166. if (!item.children) {
  167. return
  168. }
  169. withAnimation(() => activeMenuIndex.value = index)
  170. }
  171. /**
  172. * Function to handle back button click event.
  173. */
  174. const onBackItemClick = (): void => {
  175. withAnimation(() => activeMenuIndex.value = null)
  176. }
  177. /**
  178. * Déclenche une animation de changement de menu en fermant et rouvrant le drawer
  179. * @param callback
  180. */
  181. const withAnimation = (callback: () => void) => {
  182. isMenuOpen.value = false
  183. callback()
  184. setTimeout(() => {isMenuOpen.value = true}, 85)
  185. }
  186. const layoutStore = useLayoutStore()
  187. const onIntersect = (isIntersecting: boolean) => {
  188. layoutStore.setIsHeaderVisible(isIntersecting)
  189. }
  190. </script>
  191. <style scoped>
  192. .logo {
  193. height: 100px;
  194. width: 300px;
  195. }
  196. .logo-md {
  197. width: 150px;
  198. height: 300px;
  199. }
  200. .icon {
  201. color: var(--on-neutral-color);
  202. }
  203. .menuItem, .menuItem .v-list-item-title {
  204. font-weight: 500;
  205. font-size: 0.9rem;
  206. letter-spacing: 0.1em;
  207. text-transform: uppercase;
  208. color: var(--primary-color);
  209. cursor: pointer;
  210. text-decoration: none !important;
  211. }
  212. /**
  213. Navigation grands écrans
  214. */
  215. .navigation-lg {
  216. display: flex;
  217. align-items: center;
  218. background-color: var(--neutral-color);
  219. .menuItem {
  220. padding: 18px;
  221. }
  222. .menuItem.first-level {
  223. font-size: 1.3rem;
  224. margin-right: 1rem;
  225. font-weight: 700;
  226. letter-spacing: 0.05em;
  227. }
  228. }
  229. /**
  230. Navigation petits écrans
  231. */
  232. .navigation-sm {
  233. background-color: var(--neutral-color);
  234. position: fixed;
  235. top: 0;
  236. }
  237. .back-item {
  238. border-bottom: solid 1px var(--on-neutral-color-light);
  239. border-radius: 0;
  240. .v-list-item-title {
  241. display: flex;
  242. align-items: center;
  243. }
  244. }
  245. </style>