StickyMenu.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <!--
  2. Menu d'actions rapides (appel, contact, ...), qui reste accroché au bord droit
  3. de l'écran (ou au bas de l'écran sur les petits écrans)
  4. -->
  5. <!-- TODO: implémenter le theming ici -->
  6. <template>
  7. <!-- Écrans larges : menu lateral -->
  8. <div class="sticky-menu lateral" v-if="lgAndUp">
  9. <v-row
  10. v-for="(action, index) in actionsOrDefault"
  11. :key="index"
  12. :class="['square', action.bgColor]"
  13. @click="() => onActionClick(action)"
  14. >
  15. <NuxtLink :to="action.url" class="link">
  16. <div>
  17. <v-icon :class="action.iconClass" />
  18. <p class="text-square mt-2">{{ action.text }}</p>
  19. </div>
  20. </NuxtLink>
  21. </v-row>
  22. </div>
  23. <!-- Petits : menu sous forme de bandeau en pied de page (sauf si le footer du site est visible) -->
  24. <div class="sticky-menu band" v-else-if="!layoutStore.isFooterVisible">
  25. <v-btn
  26. v-for="(action, index) in actionsOrDefault"
  27. :key="index"
  28. :class="[action.bgColor]"
  29. @click="() => onActionClick(action)"
  30. >
  31. {{ action.text }}
  32. </v-btn>
  33. </div>
  34. </template>
  35. <script setup lang="ts">
  36. import { defineProps, ComputedRef } from "vue";
  37. import { useRouter } from "vue-router";
  38. import { useDisplay } from "vuetify";
  39. import { useLayoutStore } from "~/stores/layoutStore";
  40. import { StickyMenuActionType } from "~/types/enum/layout";
  41. import { StickyMenuAction } from "~/types/interface";
  42. const { mdAndDown, lgAndUp } = useDisplay();
  43. const router = useRouter();
  44. const layoutStore = useLayoutStore()
  45. const { isMobileDevice } = useClientDevice()
  46. // TODO: passer en conf?
  47. const telephoneNumber = "09 72 12 60 17";
  48. // Actions par défaut du menu, peut-être surchargé via la propriété `actions`
  49. const defaultActions: Array<StickyMenuAction> = [
  50. {
  51. type: StickyMenuActionType.FOLLOW_LINK,
  52. bgColor: "green-square",
  53. iconClass: "fa-regular fa-comments icon",
  54. text: "Nous contacter",
  55. url: "/nous-contacter",
  56. },
  57. {
  58. type: StickyMenuActionType.CALL_US,
  59. bgColor: "darkblue-square",
  60. iconClass: "fa-solid fa-phone icon",
  61. text: "Nous Appeler",
  62. },
  63. ];
  64. const props = defineProps({
  65. /**
  66. * Actions accessibles via le menu (par défaut: "Nous contacter", "Nous appeller")
  67. */
  68. actions: {
  69. type: Array<StickyMenuAction>,
  70. required: false,
  71. default: []
  72. }
  73. })
  74. const actionsOrDefault: ComputedRef<Array<StickyMenuAction>> = computed(() => {
  75. return props.actions.length > 0 ? props.actions : defaultActions
  76. })
  77. const callUs = () => {
  78. if (isMobileDevice()) {
  79. window.location.href = `tel:${telephoneNumber}`;
  80. } else {
  81. alert(`Notre numéro de téléphone : ${telephoneNumber}`);
  82. }
  83. }
  84. /**
  85. * On a cliqué sur une des actions du menu
  86. * @param action
  87. */
  88. const onActionClick = (action: StickyMenuAction) => {
  89. switch (action.type) {
  90. case StickyMenuActionType.ASK_FOR_A_DEMO:
  91. router.push({ path: action.url, query: { request: "demo" } });
  92. break;
  93. case StickyMenuActionType.CALL_US:
  94. callUs()
  95. break;
  96. case StickyMenuActionType.FOLLOW_LINK:
  97. if (!action.url) {
  98. throw Error('Missing prop : url')
  99. }
  100. router.push({ path: action.url });
  101. break
  102. default:
  103. throw Error('Unrecognized action')
  104. }
  105. };
  106. </script>
  107. <style scoped lang="scss">
  108. .sticky-menu {
  109. z-index: 100;
  110. }
  111. // Menu format lateral (pour affichage écrans larges)
  112. .sticky-menu.lateral {
  113. position: sticky;
  114. right: 0;
  115. top: 60%;
  116. transform: translateY(-50%);
  117. float: right;
  118. display: flex;
  119. flex-direction: column;
  120. color: var(--on-primary-color);
  121. font-weight: 500;
  122. font-size: 0.7rem;
  123. line-height: 15px;
  124. text-align: center;
  125. letter-spacing: 0.2em;
  126. text-transform: uppercase;
  127. }
  128. // Menu format ruban (pour affichage petits écrans)
  129. .sticky-menu.band {
  130. position: fixed;
  131. height: 46px;
  132. bottom: 0;
  133. width: 100%;
  134. display: flex;
  135. justify-content: center;
  136. background-color: var(--neutral-color);
  137. .v-btn {
  138. margin: 4px 6px;
  139. }
  140. }
  141. .square {
  142. position: relative;
  143. font-family: "Barlow", serif;
  144. width: 7rem;
  145. padding: 1rem;
  146. cursor: pointer;
  147. transition: transform 0.3s ease-in-out;
  148. }
  149. .square:hover {
  150. transform: translateX(-10px);
  151. }
  152. .link {
  153. text-decoration: none;
  154. color: var(--on-primary-color);
  155. }
  156. .yellow-square {
  157. background: rgb(250, 194, 10);
  158. color: #0e2d32;
  159. }
  160. .green-square {
  161. background: #9edbdd;
  162. }
  163. .red-square {
  164. background: #d8050b;
  165. }
  166. .blue-square {
  167. background: #2093be;
  168. }
  169. .logo-square {
  170. background: var(--Bleu-School-60, rgba(32, 147, 190));
  171. }
  172. .darkblue-square {
  173. background: #0e2d32;
  174. }
  175. </style>