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 { useDisplay } from "vuetify";
  40. import { useLayoutStore } from "~/stores/layoutStore";
  41. import { ActionMenuItemType } from "~/types/enum/layout";
  42. import { ActionMenuItem } from "~/types/interface";
  43. const { mdAndDown, lgAndUp } = useDisplay();
  44. const router = useRouter();
  45. const layoutStore = useLayoutStore()
  46. const { isMobileDevice } = useClientDevice()
  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<ActionMenuItem> = [
  50. {
  51. type: ActionMenuItemType.FOLLOW_LINK,
  52. color: "secondary",
  53. icon: "far fa-comments",
  54. text: "Nous contacter",
  55. url: "/nous-contacter",
  56. },
  57. {
  58. type: ActionMenuItemType.CALL_US,
  59. color: "primary",
  60. icon: "fas fa-phone",
  61. text: "Nous Appeler",
  62. },
  63. ];
  64. const props = defineProps({
  65. /**
  66. * Actions accessibles via le menu (par défaut : "Nous contacter", "Nous appeler")
  67. */
  68. actions: {
  69. type: Array<ActionMenuItem>,
  70. required: false,
  71. default: []
  72. }
  73. })
  74. const actionsOrDefault: ComputedRef<Array<ActionMenuItem>> = 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: ActionMenuItem) => {
  89. switch (action.type) {
  90. case ActionMenuItemType.ASK_FOR_A_DEMO:
  91. router.push({ path: action.url, query: { request: "demo" } });
  92. break;
  93. case ActionMenuItemType.CALL_US:
  94. callUs()
  95. break;
  96. case ActionMenuItemType.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. font-weight: 500;
  121. font-size: 0.7rem;
  122. line-height: 15px;
  123. text-align: center;
  124. letter-spacing: 0.2em;
  125. text-transform: uppercase;
  126. }
  127. // Menu format ruban (pour affichage petits écrans)
  128. .sticky-menu.band {
  129. position: fixed;
  130. height: 46px;
  131. bottom: 0;
  132. width: 100%;
  133. display: flex;
  134. justify-content: center;
  135. .v-btn {
  136. margin: 4px 6px;
  137. }
  138. }
  139. .square {
  140. position: relative;
  141. font-family: "Barlow", serif;
  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>