# Componente

# Introducción

Este componente renderizará un input de tipo date picker. El mismo se compone de dos partes, una de renderizado y otra de lógica (IrDatePicker Bone), esta ultima será la encargada de el manejo de los datos.

# Uso

Es importante recalcar el nombre del componente IrDatePicker a nivel de código, ya que este deberá usarse para llamar al componente para renderizarlo, ya sea individualmente o en un IrForm.

# Ejemplo

# Propiedades

Propiedad Tipo Default Descripción
label String

Define el label del campo.

readonly Boolean

false

El valor readonly = true, establecerá al campo como solo lectura.

value Date

Valor del campo. Con formato JSON Date, Ejemplo: 2022-12-20T00:00:00.000Z

required Boolean

false

En valor true, define que el componente debe ser valorizado para ser válido en un formulario.

clearable String

String que define el tooltip que se mostrará en el botón de limpiar el valor del campo. Solo se mostrará el botón si esta propiedad está valorizada.

hint Object | String

''

Genera un texto de ayuda para el componente, que podrá ser o no, clickeable para navegar a un link determinado

Propiedad con estructura de objeto con los siguientes keys:

{
  link: String,
  text: String
  openNewTab:Boolean
  classes:String
}

En la propiedad text del objeto se determinará el texto que mostrará el hint, esta propiedad debe estar valorizada para que se muestre el hint.

En la propiedad link del objeto se determinará el link al cual navegará al clickear en el hint, si no se encuentra valorizada el hint no será clickeable, solo será texto.

En la propiedad openNewTab del objeto se determinará si al clickearse el hint con link valorizado, se navegará en una nueva pestaña. Solo es útil paracuando la propedad link del objeto está definida. Si no se encuentra definido, se navegará en la misma pestaña.

En la propiedad classes del objeto, se determinarán las clases que se aplicarán al hint, las clases serán separadas por un espacio, sirven para definir estilos. Se utilizarán principalmente clases de vuetify.

Las clases aplicables para las filas de la tabla podrán encontrarse en Vuetify en los siguientes enlaces:

# Código fuente

<template>
  <ir-date-picker-bone
    v-bind="$attrs"
    ref="datePickerBone"
    :key="componentKey"
    v-model="localValue"
    @input="emitInput"
  >
    <div
      slot-scope="{
        inputEvents,
        placeholder,
        vallocal,
        readonly,
        rules,
        hideDetails,
        label,
        comportamientos,
        clearable,
        comportamientosPositions,
        id_resolver,
        comportamientoEvents
      }"
      class="input-group js-parent-focus d-flex align-center ir-date-picker"
      style="width: 100%"
    >
      <!-- eslint-disable-next-line vue/no-template-shadow -->
      <v-menu
        ref="menu"
        v-model="menu"
        :disabled="readonly"
        :close-on-content-click="false"
        :return-value.sync="vallocal"
        transition="scale-transition"
        offset-y
        min-width="auto"
      >
        <template #activator="{ on: onMenu }">
          <div style="width: 100%">
            <ir-label
              v-if="isOutlined"
              :label="label"
              :error="hasError"
              :disabled="handledAttrs.disabled"
              :readonly="readonly"
            />
            <v-text-field
              v-if="!handledAttrs.multiple"
              :ref="irDatePickerRef"
              type="date"
              :rules="rules"
              :value="formatDate"
              :label="isOutlined ? undefined : label"
              v-bind="getFilteredProps()"
              style="width: 100%"
              :disabled="readonly || handledAttrs.disabled"
              :class="readonly ? 'not-readonly-tf' : 'is-readonly-tf'"
              data-cy="dateTextField"
              @update:error="setError"
              @blur="(e) => updateValue(e, inputEvents)"
              @keydown.enter="(e) => updateValue(e, inputEvents)"
              @click.prevent=""
              @touchstart="openCalendarMenu"
              @touchend.prevent=""
            >
              <template #append>
                <v-icon
                  class="mr-2"
                  v-on="onMenu"
                >
                  mdi-calendar-month-outline
                </v-icon>
                <v-tooltip
                  v-if="clearable && clearable != ''"
                  bottom
                >
                  <!-- eslint-disable-next-line vue/no-template-shadow -->
                  <template #activator="{ on: onClose }">
                    <v-icon
                      class="mr-2"
                      :disabled="readonly || handledAttrs.disabled"
                      v-on="onClose"
                      @click="clearValue(inputEvents)"
                    >
                      mdi-close
                    </v-icon>
                  </template>
                  <span>{{ clearable }}</span>
                </v-tooltip>
                <template v-for="(comportamiento, i) in comportamientos">
                  <ir-comportamiento
                    v-if="isCompValid(comportamiento)"
                    :key="i"
                    :value="localValue"
                    class="mt-1 mb-1"
                    v-bind="comportamiento"
                    :current_id_resolver="id_resolver"
                    :father-component-name="datePickerComponentName"
                    v-on="comportamientoEvents"
                  />
                </template>
              </template>
              <template #message="{ message }">
                <!-- eslint-disable-next-line vue/no-template-shadow -->
                <div
                  class="d-block mb-1"
                  v-html="message"
                />
              </template>
              <template
                v-for="position in comportamientosPositions"
                #[position]
              >
                <div
                  :key="'comportamientos-slot-' + position"
                  class="d-flex"
                  style="gap: 10px"
                >
                  <template v-for="(comportamiento, i) in comportamientos">
                    <ir-comportamiento
                      v-if="isCompValid(comportamiento, position)"
                      :key="'comportamiento-' + position + '-' + i"
                      :value="localValue"
                      v-bind="comportamiento"
                      :current_id_resolver="id_resolver"
                      :father-component-name="datePickerComponentName"
                      v-on="comportamientoEvents"
                    />
                  </template>
                </div>
              </template>
              <template
                v-if="readonly && !localValue"
                #prepend-inner
              >
                <div class="pr-2">
                  <v-icon color="warning"> mdi-alert-circle </v-icon>
                </div>
              </template>
            </v-text-field>
            <v-combobox
              v-else
              :ref="irDatePickerRef"
              readonly
              multiple
              chips
              small-chips
              :rules="rules"
              :value="formatDate"
              :label="isOutlined ? undefined : label"
              v-bind="getFilteredProps()"
              style="width: 100%"
              :disabled="readonly || handledAttrs.disabled"
              :class="readonly ? 'not-readonly-tf' : 'is-readonly-tf'"
              data-cy="dateTextField"
              @update:error="setError"
              @blur="(e) => updateValue(e, inputEvents)"
              @keydown.enter="(e) => updateValue(e, inputEvents)"
              @click.prevent=""
              @touchstart="openCalendarMenu"
              @touchend.prevent=""
              @click="menu = true"
            >
              <template #append>
                <v-icon
                  class="mr-2"
                  v-on="onMenu"
                >
                  mdi-calendar-month-outline
                </v-icon>
                <v-tooltip
                  v-if="clearable && clearable != ''"
                  bottom
                >
                  <!-- eslint-disable-next-line vue/no-template-shadow -->
                  <template #activator="{ on: onClose }">
                    <v-icon
                      class="mr-2"
                      :disabled="readonly || handledAttrs.disabled"
                      v-on="onClose"
                      @click="clearValue(inputEvents)"
                    >
                      mdi-close
                    </v-icon>
                  </template>
                  <span>{{ clearable }}</span>
                </v-tooltip>
                <template v-for="(comportamiento, i) in comportamientos">
                  <ir-comportamiento
                    v-if="isCompValid(comportamiento)"
                    :key="i"
                    :value="localValue"
                    class="mt-1 mb-1"
                    v-bind="comportamiento"
                    :current_id_resolver="id_resolver"
                    :father-component-name="datePickerComponentName"
                    v-on="comportamientoEvents"
                  />
                </template>
              </template>
              <template #message="{ message }">
                <!-- eslint-disable-next-line vue/no-template-shadow -->
                <div
                  class="d-block mb-1"
                  v-html="message"
                />
              </template>
              <template
                v-for="position in comportamientosPositions"
                #[position]
              >
                <div
                  :key="'comportamientos-slot-' + position"
                  class="d-flex"
                  style="gap: 10px"
                >
                  <template v-for="(comportamiento, i) in comportamientos">
                    <ir-comportamiento
                      v-if="isCompValid(comportamiento, position)"
                      :key="'comportamiento-' + position + '-' + i"
                      :value="localValue"
                      v-bind="comportamiento"
                      :current_id_resolver="id_resolver"
                      :father-component-name="datePickerComponentName"
                      v-on="comportamientoEvents"
                    />
                  </template>
                </div>
              </template>
              <template
                v-if="readonly && !localValue"
                #prepend-inner
              >
                <div class="pr-2">
                  <!-- Deshabilita regla de indentación que pisa a prettier -->
                  <!-- eslint-disable-next-line vue/no-template-shadow vue/singleline-html-element-content-newline -->
                  <v-icon color="warning"> mdi-alert-circle </v-icon>
                </div>
              </template>
            </v-combobox>
          </div>
        </template>
        <v-date-picker
          v-bind="handledAttrs"
          :placeholder="placeholder"
          :value="vallocal"
          :hide-details="hideDetails"
          :rules="rules"
          scrollable
          :disabled="readonly || handledAttrs.disabled"
          :label="label"
          v-on="inputEvents"
          @input="vallocal = $event.target"
        >
          <div
            class="d-flex py-2"
            style="gap: 10px; justify-content: end; width: 100%"
          >
            <v-btn
              text
              color="primary"
              @click="menu = false"
            >
              Cancel
            </v-btn>
            <v-btn
              text
              color="primary"
              @click="$refs.menu.save(vallocal)"
            >
              OK
            </v-btn>
          </div>
        </v-date-picker>
      </v-menu>
    </div>
  </ir-date-picker-bone>
</template>

<script>
import IrDatePickerBone from './IrDatePickerBone.vue'
import { datePickerComponentName } from '../../../constants'
import { setHintTag } from '../../../helpers/hint'
import { isOutlined } from '../../../helpers/utils.js'
import focusAndScrollMixin from '../../../mixins/focusAndScroll'
import { filterProps } from '../../../helpers/filterProps'
export default {
  name: datePickerComponentName,
  components: {
    IrDatePickerBone
  },
  mixins: [focusAndScrollMixin],
  inheritAttrs: false,
  props: {
    value: {
      type: undefined,
      default: 0,
      required: true
    },
    isFocused: {
      type: Boolean,
      default: false
    },
    hint: {
      type: [String, Object],
      default: ''
    }
  },
  data() {
    return {
      irDatePickerRef: datePickerComponentName,
      localValue: this.value,
      otherProps: { ...this.$attrs },
      hasError: false,
      menu: false,
      datePickerComponentName: datePickerComponentName,
      componentKey: 0
    }
  },
  computed: {
    formatDate() {
      if(Array.isArray(this.localValue)){
        return this.localValue.map((date) => {
          console.log(this.applyDateFormat(date))
          return this.applyDateFormat(date)
        })
      }
      if (this.localValue && this.localValue.toString() !== 'Invalid Date') {
        return this.applyDateFormat(this.localValue)
      } else {
        return undefined
      }
    },
    isOutlined() {
      return isOutlined(this.handledAttrs.outlined)
    },
    handledAttrs() {
      const hint = setHintTag(this.otherProps, this.hint)
      let aux = { ...this.otherProps, ...this.$attrs }
      delete aux.hint
      if (hint) {
        aux = { ...aux, hint }
      }
      delete aux.readonly
      delete aux.rules
      delete aux.pattern
      delete aux.required
      delete aux.label
      delete aux.value
      delete aux.width
      delete aux.clearable
      return aux
    }
  },
  watch: {
    value(isValue) {
      this.localValue = isValue
    }
  },
  mounted() {
    if (this.isFocused) {
      const ref = this.$refs[this.irDatePickerRef]
      let element =
        ref && ref.$el && ref.$el.querySelector('input')
          ? ref.$el.querySelector('input')
          : undefined
      this.focusAndScroll(element)
    }
  },
  created() {
    this.otherProps = { ...this.$attrs }
  },
  methods: {
    applyDateFormat(date) {
      let fecha = new Date(date)
      let day = String(fecha.getUTCDate()).padStart(2, '0')
      let month = String(fecha.getUTCMonth() + 1).padStart(2, '0')
      let year = fecha.getUTCFullYear()
      let dateStr = `${year}-${month}-${day}`
      return dateStr
    },
    emitInput() {
      // Emite evento a input con valor  tratado para formato de fecha de DB y '' para fecha vacía
      if (typeof this.localValue === 'undefined' || this.localValue === null)
        this.$emit('input', '')
      else if (!Array.isArray(this.localValue)) {
        let localValueAux
        try {
          localValueAux = this.localValue.toJSON()
        } catch (error) {
          localValueAux = new Date(this.localValue)
        }
        this.$emit('input', localValueAux)
      } else this.$emit('input', this.localValue)
    },
    isCompValid(comp, pos) {
      if (pos)
        return (
          (typeof comp.visible === 'undefined' || comp.visible) &&
          comp.position === pos
        )
      return (
        (typeof comp.visible === 'undefined' || comp.visible) &&
        comp.position === 'append'
      )
    },
    openCalendarMenu() {
      this.$refs.menu.isActive = true
    },
    updateValue(e) {
      if (e.target.value) {
        this.localValue = e.target.value
        this.hasError = false
        this.emitInput()
      } else if (!this.$refs.menu.isActive) {
        this.hasError = true
        this.resetInput()
      }
    },
    getFilteredProps() {
      return filterProps(this.$refs[this.irDatePickerRef], this.handledAttrs, [
        'data-cy'
      ])
    },
    clearValue(inputEvents) {
      // limpia valor de fecha, setea undefined
      this.localValue = undefined
      inputEvents.input(this.localValue)
      this.emitInput()
    },
    setError(eventValue) {
      if (eventValue) {
        this.hasError = eventValue
        this.resetInput()
      }
    },
    resetInput() {
      this.componentKey++
      this.localValue = this.value
      this.emitInput()
    }
  }
}
</script>

<style scoped lang="scss">
.v-text-field ::-webkit-calendar-picker-indicator {
  // Oculta el ícono de calendario por defecto del input
  display: none !important;
}
</style>
Last Updated: 4/5/2024, 4:52:19 PM