ActionMenu.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. <template>
  6. <!-- Écrans larges : menu lateral, accroché au bord droit de l'écran -->
  7. <div class="sticky-menu lateral" v-if="lgAndUp">
  8. <v-row
  9. v-for="(action, index) in actionsOrDefault"
  10. :key="index"
  11. :class="['square', action.color]"
  12. @click="() => onActionClick(action)"
  13. >
  14. <NuxtLink :to="action.url" class="link">
  15. <div>
  16. <v-icon
  17. :class="action.icon"
  18. />
  19. <p class="text-square mt-2">
  20. {{ action.text }}
  21. </p>
  22. </div>
  23. </NuxtLink>
  24. </v-row>
  25. </div>
  26. <!-- Petits écrans : menu sous forme de bandeau en pied de page (sauf si le footer du site est visible) -->
  27. <div class="sticky-menu band" v-else-if="!layoutStore.isFooterVisible">
  28. <v-btn
  29. v-for="(action, index) in actionsOrDefault"
  30. :key="index"
  31. :class="[action.color]"
  32. @click="() => onActionClick(action)"
  33. >
  34. {{ action.text }}
  35. </v-btn>
  36. </div>
  37. </template>
  38. <script setup lang="ts">
  39. import { useRouter } from "vue-router";
  40. import { useDisplay } from "vuetify";
  41. import { useLayoutStore } from "~/stores/layoutStore";
  42. import { ActionMenuItemType } from "~/types/enum/layout";
  43. import { ActionMenuItem } from "~/types/interface";
  44. const { mdAndDown, lgAndUp } = useDisplay();
  45. const router = useRouter();
  46. const layoutStore = useLayoutStore()
  47. const { isMobileDevice } = useClientDevice()
  48. const telephoneNumber = "09 72 12 60 17";
  49. // Actions par défaut du menu, peut-être surchargé via la propriété `actions`
  50. const defaultActions: Array<ActionMenuItem> = [
  51. {
  52. type: ActionMenuItemType.FOLLOW_LINK,
  53. color: "secondary",
  54. icon: "far fa-comments",
  55. text: "Nous contacter",
  56. url: "/nous-contacter",
  57. },
  58. {
  59. type: ActionMenuItemType.CALL_US,
  60. color: "primary",
  61. icon: "fas fa-phone",
  62. text: "Nous Appeler",
  63. },
  64. ];
  65. const props = defineProps({
  66. /**
  67. * Actions accessibles via le menu (par défaut : "Nous contacter", "Nous appeler")
  68. */
  69. actions: {
  70. type: Array<ActionMenuItem>,
  71. required: false,
  72. default: []
  73. }
  74. })
  75. const actionsOrDefault: ComputedRef<Array<ActionMenuItem>> = computed(() => {
  76. return props.actions.length > 0 ? props.actions : defaultActions
  77. })
  78. const callUs = () => {
  79. if (isMobileDevice()) {
  80. window.location.href = `tel:${telephoneNumber}`;
  81. } else {
  82. alert(`Notre numéro de téléphone : ${telephoneNumber}`);
  83. }
  84. }
  85. /**
  86. * On a cliqué sur une des actions du menu
  87. * @param action
  88. */
  89. const onActionClick = (action: ActionMenuItem) => {
  90. switch (action.type) {
  91. case ActionMenuItemType.ASK_FOR_A_DEMO:
  92. router.push({ path: action.url, query: { request: "demo" } });
  93. break;
  94. case ActionMenuItemType.CALL_US:
  95. callUs()
  96. break;
  97. case ActionMenuItemType.FOLLOW_LINK:
  98. if (!action.url) {
  99. throw Error('Missing prop : url')
  100. }
  101. router.push({ path: action.url });
  102. break
  103. default:
  104. throw Error('Unrecognized action')
  105. }
  106. };
  107. </script>
  108. <style scoped lang="scss">
  109. .sticky-menu {
  110. z-index: 100;
  111. }
  112. // Menu format lateral (pour affichage écrans larges)
  113. .sticky-menu.lateral {
  114. position: sticky;
  115. right: 0;
  116. top: 60%;
  117. transform: translateY(-50%);
  118. float: right;
  119. display: flex;
  120. flex-direction: column;
  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. .v-btn {
  137. margin: 4px 6px;
  138. }
  139. }
  140. .square {
  141. position: relative;
  142. width: 7rem;
  143. padding: 1rem;
  144. cursor: pointer;
  145. transition: transform 0.3s ease-in-out;
  146. }
  147. .square:hover {
  148. transform: translateX(-10px);
  149. }
  150. .link {
  151. text-decoration: none;
  152. }
  153. .primary {
  154. background: var(--action-menu-primary-color);
  155. color: var(--action-menu-on-primary-color);
  156. a {
  157. color: var(--action-menu-on-primary-color);
  158. }
  159. }
  160. .secondary {
  161. background: var(--action-menu-secondary-color);
  162. color: var(--action-menu-on-secondary-color);
  163. a {
  164. color: var(--action-menu-on-secondary-color);
  165. }
  166. }
  167. </style>