Browse Source

input fix

Vincent GUFFON 3 years ago
parent
commit
18a9c791f0

+ 75 - 23
components/Ui/Input/Autocomplete.vue

@@ -10,7 +10,7 @@ Liste déroulante avec autocompletion
       autocomplete="search"
       :value="data"
       :items="itemsToDisplayed"
-      :label="$t(label)"
+      :label="$t(label_field)"
       item-text="itemTextDisplay"
       :item-value="itemValue"
       :no-data-text="$t('autocomplete_research')"
@@ -22,6 +22,7 @@ Liste déroulante avec autocompletion
       :search-input.sync="search"
       :prepend-icon="prependIcon"
       :error="error"
+      :rules="rules"
       @input="onChange($event)"
     >
       <template v-if="slotText" v-slot:item="data">
@@ -32,7 +33,7 @@ Liste déroulante avec autocompletion
 </template>
 
 <script lang="ts">
-import { computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted } from '@nuxtjs/composition-api'
+import {computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted, useContext} from '@nuxtjs/composition-api'
 import { AnyJson } from '~/types/interfaces'
 import * as _ from 'lodash'
 import {$objectProperties} from "~/services/utils/objectProperties";
@@ -51,7 +52,7 @@ export default defineComponent({
       default: null
     },
     data: {
-      type: [String, Number, Object],
+      type: [String, Number, Object, Array],
       required: false,
       default: null
     },
@@ -72,6 +73,11 @@ export default defineComponent({
       type: Array,
       required: true
     },
+    group:{
+      type: String,
+      required: false,
+      default: null
+    },
     slotText: {
       type: Array,
       required: false,
@@ -96,45 +102,91 @@ export default defineComponent({
     prependIcon: {
       type: String
     },
+    translate: {
+      type: Boolean,
+      default: false
+    },
+    rules: {
+      type: Array,
+      required: false,
+      default: () => []
+    },
   },
   setup (props, { emit }) {
+    const {app:{i18n}} = useContext()
     const search:Ref<string|null> = ref(null)
     const {error, onChange} = $useError(props.field, emit)
 
-    // On reconstruit les items à afficher car le text de l'Item doit être construit par rapport au itemText passé en props
+    // On reconstruit les items à afficher...
     const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {
-      return props.items.map((item: any) => {
-        const slotTextDisplay: Array<string> = []
-        const itemTextDisplay: Array<string> = []
+      const itemsByGroup:Array<Array<string>> = classItemsByGroup(props.items)
+      return prepareItemsToDisplayed(itemsByGroup)
+    })
+
+    const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
+      if(newResearch !== oldResearch && oldResearch !== null)
+        emit('research', newResearch)
+    }, 500))
+
+    onUnmounted(() => {
+      unwatch()
+    })
 
+
+    /**
+     * On construit l'Array à double entrée contenant les groups (headers) et les propositions
+     * @param items
+     */
+    const classItemsByGroup = (items:Array<any>): Array<Array<string>> => {
+      const itemsByGroup:Array<Array<string>> = []
+      for (const item of items){
         if(item){
+          if(!itemsByGroup[item[props.group as string]])
+            itemsByGroup[item[props.group as string]] = []
+
+          itemsByGroup[item[props.group as string]].push(item)
+        }
+      }
+      return itemsByGroup
+    }
+
+    /**
+     * Construction de l'Array JSON contenant toutes les propositions à afficher dans le select
+     * @param itemsByGroup
+     */
+    const prepareItemsToDisplayed = (itemsByGroup:Array<Array<string>>):Array<AnyJson> => {
+      let finalItems:Array<AnyJson> = []
+      for(const group in itemsByGroup){
+
+        //Si un group est présent, alors on créer le group options header
+        if(group !== 'undefined'){
+          finalItems.push({header: i18n.t(group as string)})
+        }
+
+        //On parcours les items pour préparer les texts/slotTexts à afficher
+        finalItems = finalItems.concat(itemsByGroup[group].map((item: any) => {
+          const slotTextDisplay: Array<string> = []
+          const itemTextDisplay: Array<string> = []
+
           item = $objectProperties.cloneAndFlatten(item)
 
           //Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
           if(props.slotText){
             for (const text of props.slotText) {
-              slotTextDisplay.push(item[text as string])
+              slotTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
             }
           }
 
           for (const text of props.itemText) {
-            itemTextDisplay.push(item[text as string])
+            itemTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
           }
-        }
 
-        //On reconstruit l'objet
-        return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
-      })
-    })
-
-    const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
-      if(newResearch !== oldResearch && oldResearch !== null)
-        emit('research', newResearch)
-    }, 500))
-
-    onUnmounted(() => {
-      unwatch()
-    })
+          //On reconstruit l'objet
+          return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
+        }))
+      }
+      return finalItems
+    }
 
     return {
       label_field: props.label ?? props.field,

+ 3 - 3
components/Ui/Input/AutocompleteWithAPI.vue

@@ -44,7 +44,7 @@ export default defineComponent({
       required: true
     },
     data: {
-      type: [String, Number, Object],
+      type: [String, Number, Object, Array],
       required: false,
       default: null
     },
@@ -87,8 +87,8 @@ export default defineComponent({
       isLoading.value = false
     }
 
-    const unwatch = watch(data,(data) => {
-      items.value = [data]
+    const unwatch = watch(data,(d) => {
+      items.value = [d]
     })
 
     onUnmounted(() => {

+ 1 - 0
components/Ui/Input/Checkbox.vue

@@ -10,6 +10,7 @@ Case à cocher
     fluid
   >
     <v-checkbox
+      v-model="data"
       :value="data"
       :label="$t(label_field)"
       :disabled="readonly"

+ 1 - 1
components/Ui/Input/Phone.vue

@@ -87,7 +87,7 @@ export default defineComponent({
       onInput,
       onChangeValue,
       rules: [
-        () => isValid.value || i18n.t('phone_error')
+        (phone: string) => (!phone || isValid.value) || i18n.t('phone_error')
       ]
     }
   }

+ 24 - 11
components/Ui/Input/Text.vue

@@ -5,22 +5,24 @@ Champs de saisie de texte
 -->
 
 <template>
-  <v-text-field
-    autocomplete="off"
-    :value="data"
-    :label="$t(label_field)"
-    :rules="rules"
-    :disabled="readonly"
-    :type="type"
-    :error="error || violations"
-    :error-messages="errorMessage"
-    @change="onChange($event)"
-  />
+    <v-text-field
+      autocomplete="off"
+      :value="data"
+      :label="$t(label_field)"
+      :rules="rules"
+      :disabled="readonly"
+      :type="type"
+      :error="error || violations"
+      :error-messages="errorMessage"
+      @change="onChange($event)"
+      v-mask="mask"
+    />
 </template>
 
 <script lang="ts">
 import { defineComponent } from '@nuxtjs/composition-api'
 import {$useError} from "~/use/form/useError";
+import {mask} from 'vue-the-mask';
 
 export default defineComponent({
   props: {
@@ -61,6 +63,11 @@ export default defineComponent({
       type: String,
       required: false,
       default: null
+    },
+    mask: {
+      type: [Array, Boolean],
+      required: false,
+      default: false
     }
   },
   setup (props, {emit}) {
@@ -71,6 +78,12 @@ export default defineComponent({
       violations,
       onChange
     }
+  },
+  directives: {
+    mask: (el, binding, vnode, oldVnode) => {
+      if (!binding.value) return;
+      mask(el, binding, vnode, oldVnode);
+    }
   }
 })
 </script>