Kaynağa Gözat

add tooltips

Olivier Massot 3 yıl önce
ebeveyn
işleme
5fbc00d447

+ 107 - 0
components/Ui/Help.vue

@@ -0,0 +1,107 @@
+<template>
+  <v-tooltip
+    :value="show"
+    :top="top"
+    :bottom="bottom"
+    :right="right"
+    :left="leftOrDefault"
+    :open-on-hover="false"
+    :open-on-focus="false"
+    :open-on-click="false"
+    max-width="360px"
+  >
+    <template #activator="{}">
+      <v-icon
+        @click="onIconClicked"
+        icon
+        class="ml-3"
+        size="18px"
+        ref="iconRef"
+      >
+        {{ icon }}
+      </v-icon>
+    </template>
+
+    <div ref="slotDiv" class="tooltip" v-click-outside="onClickOutside">
+      <slot></slot>
+    </div>
+  </v-tooltip>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, Ref } from '@nuxtjs/composition-api'
+
+
+export default defineComponent({
+  props: {
+    left: {
+      type: Boolean,
+      required: false,
+      default: false
+    },
+    right: {
+      type: Boolean,
+      required: false,
+      default: false
+    },
+    top: {
+      type: Boolean,
+      required: false,
+      default: false
+    },
+    bottom: {
+      type: Boolean,
+      required: false,
+      default: false
+    },
+    icon: {
+      type: String,
+      required: false,
+      default: 'mdi-help-circle'
+    }
+  },
+  setup(props, $refs) {
+    const show: Ref<Boolean> = ref(false)
+
+    // template reference to the icon object
+    const iconRef = ref(null)
+
+    // Because left is the default direction, this property will return true if
+    // left has been defined in the props, or if no other direction has been given
+    const leftOrDefault: Ref<Boolean> = ref(props.left || (!props.right && !props.bottom && !props.top))
+
+    const onIconClicked = (e: any) => {
+      show.value = !show.value
+      e.stopPropagation()
+    }
+
+    const onClickOutside = (e: any) => {
+      if (show.value) {
+        if (e.target === (iconRef.value as any).$el)     {
+          return
+        }
+        show.value = false
+      }
+    }
+
+    return {
+      iconRef,
+      leftOrDefault,
+      show,
+      onIconClicked,
+      onClickOutside
+    }
+  }
+})
+</script>
+
+<style>
+.v-icon {
+  cursor: pointer;
+  height: 18px;
+  width: 18px;
+}
+.v-tooltip__content {
+  pointer-events: all !important;
+}
+</style>

+ 2 - 1
config/nuxtConfig/plugins.js

@@ -10,6 +10,7 @@ export default {
     '~/plugins/Data/dataProvider',
     '~/plugins/Data/dataDeleter',
     '~/plugins/vuexOrm.js',
-    '~/plugins/phone-input'
+    '~/plugins/phone-input',
+    '~/plugins/directives.js'
   ]
 }

+ 2 - 1
lang/field/fr-FR.js

@@ -81,6 +81,7 @@ export default (context, locale) => {
     country: 'Pays',
     addresstype: 'Nature',
     contactpoint_type: 'Type de contact',
-    phoneNumberInvalid: 'Numéro de téléphone invalide'
+    phoneNumberInvalid: 'Numéro de téléphone invalide',
+    logo: 'Logo',
   })
 }

+ 2 - 0
lang/fr-FR.js

@@ -5,6 +5,7 @@ import rulesAndErrors from '@/lang/rulesAndErrors/fr-FR'
 import form from '@/lang/form/fr-FR'
 import breadcrumbs from '@/lang/breadcrumbs/fr-FR'
 import menuKey from '@/lang/menuKey/fr-FR'
+import help from '@/lang/help/fr-FR'
 import contentSubscription from '@/lang/content/subscription/fr-FR'
 
 export default (context, locale) => {
@@ -16,6 +17,7 @@ export default (context, locale) => {
     ...form(context, locale),
     ...breadcrumbs(context, locale),
     ...menuKey(context, locale),
+    ...help(context, locale),
     ...contentSubscription(context, locale)
   }
 }

+ 24 - 0
lang/help/fr-FR.js

@@ -0,0 +1,24 @@
+/**
+ * Translations for the help tooltips
+ *
+ * @param context
+ * @param locale
+ * @returns {{get_more_functionalities_with_version: string, only_for_cmf_members: string, contact_us_at: string, contact_us_for_show_and_demo: string, starting_from_x_eur_ttc_per_month: string, download_order_form: string, for_x_sms: string, for_only_x_eur_ttc_by_month: string, example: string, domain_name: string, and_benefit: string, public_price_x_ttc_a_year: string, product_sheet: string, get_your_own_domain_and_up_to_five_emails_for_only_x_eur_ttc_per_month: string, dummy_domain_name: string, website: string, version_x_up_to_x_students: string, download_cmf_order_form: string, send_sms_from_app_to_your_members: string, freely_try_our_software: string, starting_from_x_eur_ttc_per_ssm: string, yearly_paid_giving_x_eur_ttc_per_year: string, excluding_license_and_training_fees: string, a_suitable_solution_for_your_artistic_school: string, dummy_email_address: string, associated_mail_address: string, switch_to_version: string, or_by_mail_at: string, of_accounts_for_teachers_and_students: string, of_a_complete_website: string}}
+ */
+export default (context, locale) => {
+  return ({
+    type_of_practices_autocomplete: 'Sélectionnez parmi la liste des types de pratiques, une ou plusieurs pratiques correspondant aux activités de votre structure',
+    logo_upload: '<div>Le logo est utilisée: </div>' +
+      `\n<ul>` +
+      '    <li>dans l\'entête des documents que vos exportez</li>' +
+      '    <li>sur le site internet</li>' +
+      '    <li>dans la recherche d\'une structure sur le site Opentalent ou sur le site d\'une fédération si vous êtes membre de la CMF ' +
+      '        (voir <a target="_blank" href="https://fmfaucigny.opentalent.fr/presentation/societes-adherentes">exemple ici</a>)</li>' +
+      '</ul>',
+    communication_image_upload: 'L\'image est utilisée\n' +
+      'dans la description détaillée d\'une structure sur le site Opentalent ou ' +
+      'sur le site d\'une fédération si vous êtes membre de la CMF ' +
+      '(voir <a target="_blank" href="https://fmfaucigny.opentalent.fr/presentation/societes-adherentes">exemple ici</a>)'
+
+  })
+}

+ 26 - 8
pages/organization/index.vue

@@ -32,6 +32,12 @@ Contient toutes les informations sur l'organization courante
                 </v-col>
 
                 <v-col cols="12" sm="6">
+                  <div>
+                    <span>{{ $t('logo') }}</span>
+                    <UiHelp right>
+                      <p v-html="$t('logo_upload')" />
+                    </UiHelp>
+                  </div>
                   <UiImage
                     :id="getIdFromUri(entry['logo'])"
                     :upload="true"
@@ -59,6 +65,7 @@ Contient toutes les informations sur l'organization courante
                 </v-col>
 
                 <v-col cols="12" sm="6" v-if="organizationProfile.isCmf()">
+                  <div class="d-flex flex-row">
                     <UiInputAutocomplete
                       field="typeOfPractices"
                       :items="typeOfPractices"
@@ -70,7 +77,12 @@ Contient toutes les informations sur l'organization courante
                       group="category"
                       :rules="rules().typeOfPractice"
                       @update="updateRepository($event.map((id) => `/api/type_of_practices/${id}`), 'typeOfPractices')"
+                      class="flex"
                     />
+                    <UiHelp>
+                      {{ $t('type_of_practices_autocomplete') }}
+                    </UiHelp>
+                  </div>
                 </v-col>
                 <v-col cols="12" sm="6" v-if="getIdsFromUris(entry['typeOfPractices']).indexOf(37) >= 0">
                   <UiInputTextArea field="otherPractice" :data="entry['otherPractice']" @update="updateRepository" />
@@ -338,14 +350,20 @@ Contient toutes les informations sur l'organization courante
                 </v-col>
 
                 <v-col cols="12" sm="6">
-                  <UiImage
-                    :id="getIdFromUri(entry['image'])"
-                    :upload="true"
-                    :width="200"
-                    field="image"
-                    :ownerId="id"
-                    @update="updateRepository"
-                  ></UiImage>
+                  <div class="d-flex flex-column">
+                    <UiHelp class="d-flex flex-row">
+                      <span>{{ $t('image') }}</span>
+                      <p v-html="$t('communication_image_upload')" />
+                    </UiHelp>
+                    <UiImage
+                      :id="getIdFromUri(entry['image'])"
+                      :upload="true"
+                      :width="200"
+                      field="image"
+                      :ownerId="id"
+                      @update="updateRepository"
+                    ></UiImage>v
+                  </div>
                 </v-col>
 
                 <v-col cols="12" sm="12">

+ 33 - 0
plugins/directives.js

@@ -0,0 +1,33 @@
+import Vue from "vue";
+
+/**
+ * Register a specific event: v-click-outside
+ *
+ * Example for some modale div:
+ *
+ *   <template>
+ *     <div v-click-outside="onClickOutside">
+  *      <slot></slot>
+  *    </div>
+ *   </template>
+ *
+ *   <script>
+ *     setup() {
+ *       const show: Ref<Boolean> = ref(true)
+ *       const onClickOutside = (e: any) => { show.value = false }
+ *     }
+ *   </script>
+ */
+Vue.directive("click-outside", {
+  bind: function (el, binding, vnode) {
+    el.clickOutsideEvent = (event) => {
+      if (!(el === event.target || el.contains(event.target))) {
+        vnode.context[binding.expression](event)
+      }
+    };
+    document.body.addEventListener("click", el.clickOutsideEvent);
+  },
+  unbind(el) {
+    document.body.removeEventListener("click", el.clickOutsideEvent);
+  },
+});