ActionMenu.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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 && isVisible">
  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="isVisible">
  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 type { ActionMenuItem } from "~/types/interface";
  44. const { lgAndUp } = useDisplay();
  45. const router = useRouter();
  46. const layoutStore = useLayoutStore()
  47. const { isMobileDevice } = useClientDevice()
  48. const telephoneNumber = "09 72 12 60 17";
  49. const isVisible: ComputedRef<boolean> = computed(() =>
  50. !layoutStore.isHeaderVisible && !layoutStore.isFooterVisible
  51. )
  52. // Actions par défaut du menu, peut-être surchargé via la propriété `actions`
  53. const defaultActions: Array<ActionMenuItem> = [
  54. {
  55. type: ActionMenuItemType.FOLLOW_LINK,
  56. color: "secondary",
  57. icon: "far fa-comments",
  58. text: "Nous contacter",
  59. url: "/nous-contacter",
  60. },
  61. {
  62. type: ActionMenuItemType.CALL_US,
  63. color: "secondary",
  64. icon: "fas fa-phone",
  65. text: "Nous Appeler",
  66. },
  67. ];
  68. const props = defineProps({
  69. /**
  70. * Actions accessibles via le menu (par défaut : "Nous contacter", "Nous appeler")
  71. */
  72. actions: {
  73. type: Array<ActionMenuItem>,
  74. required: false,
  75. default: []
  76. }
  77. })
  78. const actionsOrDefault: ComputedRef<Array<ActionMenuItem>> = computed(() => {
  79. return props.actions.length > 0 ? props.actions : defaultActions
  80. })
  81. const callUs = () => {
  82. if (isMobileDevice()) {
  83. window.location.href = `tel:${telephoneNumber}`;
  84. } else {
  85. alert(`Notre numéro de téléphone : ${telephoneNumber}`);
  86. }
  87. }
  88. /**
  89. * On a cliqué sur une des actions du menu
  90. * @param action
  91. */
  92. const onActionClick = (action: ActionMenuItem) => {
  93. switch (action.type) {
  94. case ActionMenuItemType.ASK_FOR_A_DEMO:
  95. router.push({ path: action.url, query: { request: "demo" } });
  96. break;
  97. case ActionMenuItemType.CALL_US:
  98. callUs()
  99. break;
  100. case ActionMenuItemType.FOLLOW_LINK:
  101. if (!action.url) {
  102. throw Error('Missing prop : url')
  103. }
  104. router.push({ path: action.url });
  105. break
  106. default:
  107. throw Error('Unrecognized action')
  108. }
  109. };
  110. </script>
  111. <style scoped lang="scss">
  112. .sticky-menu {
  113. z-index: 100;
  114. }
  115. // Menu format lateral (pour affichage écrans larges)
  116. .sticky-menu.lateral {
  117. position: sticky;
  118. right: 0;
  119. top: 60%;
  120. transform: translateY(-50%);
  121. float: right;
  122. display: flex;
  123. flex-direction: column;
  124. color: var(--on-primary-color);
  125. font-weight: 500;
  126. font-size: 0.7rem;
  127. line-height: 15px;
  128. text-align: center;
  129. letter-spacing: 0.2em;
  130. text-transform: uppercase;
  131. }
  132. // Menu format ruban (pour affichage petits écrans)
  133. .sticky-menu.band {
  134. position: fixed;
  135. height: 46px;
  136. bottom: 0;
  137. width: 100%;
  138. display: flex;
  139. justify-content: center;
  140. background-color: var(--neutral-color);
  141. max-width: 100vw;
  142. .v-btn {
  143. margin: 6px 2%;
  144. width: 46%;
  145. }
  146. }
  147. .square {
  148. position: relative;
  149. width: 7rem;
  150. padding: 1rem;
  151. cursor: pointer;
  152. transition: transform 0.3s ease-in-out;
  153. box-shadow: -1px 2px 4px 2px var(--on-neutral-color-light);
  154. }
  155. .square:last-child {
  156. }
  157. .square:hover {
  158. transform: translateX(-10px);
  159. }
  160. .link {
  161. text-decoration: none;
  162. color: var(--on-primary-color);
  163. }
  164. .primary {
  165. background: var(--action-menu-primary-color);
  166. color: var(--action-menu-on-primary-color);
  167. a {
  168. color: var(--action-menu-on-primary-color);
  169. }
  170. }
  171. .secondary {
  172. background: var(--action-menu-secondary-color);
  173. color: var(--action-menu-on-secondary-color);
  174. a {
  175. color: var(--action-menu-on-secondary-color);
  176. }
  177. }
  178. </style>