MainMenu.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <!--
  2. Menu principal de l'application
  3. Prend en paramètre une liste de ItemMenu et les met en forme
  4. -->
  5. <template>
  6. <v-navigation-drawer
  7. :mini-variant.sync="miniVariant"
  8. v-model="open"
  9. class="bg-ot-dark-grey text-ot-menu-color main-menu"
  10. >
  11. <template #prepend>
  12. <slot name="title"></slot>
  13. </template>
  14. <v-list
  15. open-strategy="single"
  16. active-class="active"
  17. class="left-menu"
  18. >
  19. <div v-for="(item, i) in menu.children" :key="i">
  20. <!-- Cas 1 : l'item n'a pas d'enfants, c'est un lien -->
  21. <v-list-item
  22. v-if="!item.children"
  23. :title="$t(item.label)"
  24. :prepend-icon="item.icon.name"
  25. :href="!isInternalLink(item) ? item.to : undefined"
  26. :to="isInternalLink(item) ? item.to : undefined"
  27. exact
  28. class="text-ot-menu-color"
  29. height="48px"
  30. ></v-list-item>
  31. <!-- Cas 2 : l'item a des enfants, c'est un groupe -->
  32. <v-list-group
  33. v-else
  34. expand-icon="fas fa-angle-right"
  35. collapse-icon="fas fa-angle-down"
  36. class="text-ot-menu-color"
  37. v-model="item.expanded"
  38. >
  39. <template #activator="{ props }">
  40. <v-list-item
  41. v-bind="props"
  42. :prepend-icon="item.icon.name"
  43. :title="$t(item.label)"
  44. class="text-ot-menu-color"
  45. height="48px"
  46. ></v-list-item>
  47. </template>
  48. <v-list-item
  49. v-for="child in item.children"
  50. :key="$t(child.label)"
  51. :title="$t(child.label)"
  52. :prepend-icon="child.icon.name"
  53. :href="!isInternalLink(child) ? child.to : undefined"
  54. :to="isInternalLink(child) ? child.to : undefined"
  55. exact
  56. height="48px"
  57. class="text-ot-white"
  58. ></v-list-item>
  59. </v-list-group>
  60. </div>
  61. </v-list>
  62. <template #append>
  63. <slot name="foot"></slot>
  64. </template>
  65. </v-navigation-drawer>
  66. </template>
  67. <script setup lang="ts">
  68. import {onUnmounted, watch, WatchStopHandle} from "@vue/runtime-core";
  69. import {ref, toRefs} from "@vue/reactivity";
  70. import {MenuGroup, MenuItem} from "~/types/layout";
  71. import {MENU_LINK_TYPE} from "~/types/enum/layout";
  72. const props = defineProps({
  73. menu: {
  74. type: Object as () => MenuGroup,
  75. required: true
  76. },
  77. miniVariant: {
  78. type: Boolean,
  79. required: true
  80. },
  81. openMenu: {
  82. type: Boolean,
  83. required: true
  84. }
  85. })
  86. const { openMenu } = toRefs(props)
  87. const open = ref(true)
  88. // Par défaut si l'écran est trop petit au chargement de la page, le menu doit être fermé.
  89. if(process.client)
  90. open.value = window.innerWidth >= 1264
  91. // TODO : ajouter une petite explication de ce qu'on fait dans ce watch
  92. const unwatch: WatchStopHandle = watch(openMenu, (newValue, oldValue) => {
  93. if (newValue !== oldValue) {
  94. open.value = true
  95. }
  96. })
  97. const isInternalLink = (menuItem: MenuItem | MenuGroup) => {
  98. return 'type' in menuItem && menuItem.type === MENU_LINK_TYPE.INTERNAL
  99. }
  100. onUnmounted(() => {
  101. unwatch()
  102. })
  103. </script>
  104. <style scoped lang="scss">
  105. .v-list-item {
  106. min-height: 10px !important;
  107. }
  108. :deep(.v-list-item-title),
  109. :deep(.v-icon),
  110. {
  111. font-size: 14px;
  112. color: rgb(var(--v-theme-ot-menu-color));
  113. }
  114. .v-list-item__prepend {
  115. margin: 10px 0;
  116. margin-right: 10px !important;
  117. }
  118. .v-application--is-ltr .v-list-group--no-action > .v-list-group__header {
  119. margin-left: 0;
  120. padding-left: 0;
  121. }
  122. .v-application--is-ltr .v-list-group--no-action > .v-list-group__items > .v-list-item {
  123. padding-left: 30px;
  124. }
  125. .v-list-item__content {
  126. padding: 8px 0;
  127. }
  128. .v-list-group__items .v-list-item {
  129. padding-inline-start: 30px !important;
  130. }
  131. .v-list-group--no-action > .v-list-group__header,
  132. .v-list-item
  133. {
  134. border-left:3px solid rgb(var(--v-theme-ot-dark-grey));
  135. height: 48px;
  136. }
  137. .v-list-item:hover,
  138. .v-list-item.active,
  139. :deep(.v-list-group__items .v-list-item)
  140. {
  141. border-left: 3px solid rgb(var(--v-theme-ot-green));
  142. background: rgb(var(--v-theme-ot-dark-grey-hover));
  143. }
  144. :deep(.v-list-group__items .v-list-item-title) {
  145. color: rgb(var(--v-theme-ot-white));
  146. }
  147. :deep(.v-list-item .v-icon) {
  148. margin-right: 10px;
  149. }
  150. </style>