# Componente

# Introducción

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

# Uso

Es importante recalcar el nombre del componente IrTimePicker 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 Time ''

Valor del campo. Con el siguiente formato: 0800 para 08:00

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-time-picker-bone
    v-bind="$attrs"
    ref="timePickerBone"
    :key="componentKey"
    v-model="value"
    @input="(e) => updateValue(e)"
  >
    <div
      slot-scope="{
        placeholder,
        readonly,
        rules,
        hideDetails,
        label,
        comportamientos,
        clearable,
        comportamientosPositions,
        id_resolver,
        comportamientoEvents
      }"
      class="d-flex align-center"
      style="width: 100%"
    >
      <v-menu
        ref="menu"
        v-model="menu"
        :disabled="readonly"
        :close-on-content-click="false"
        transition="scale-transition"
        offset-y
        min-width="auto"
      >
        <template #activator="{ on: onMenu }">
          <div style="width: 100%">
            <ir-label
              v-if="isOutlinedd"
              :label="label"
              :error="hasError"
              :disabled="handledAttrs.disabled"
              :readonly="readonly"
            />
            <v-text-field
              ref="irTimePickerRef"
              type="time"
              :rules="rules"
              :hide-details="hideDetails"
              :placeholder="placeholder"
              :value="hourInFormat"
              :label="isOutlinedd ? undefined : label"
              v-bind="getFilteredProps()"
              style="width: 100%"
              :disabled="readonly || handledAttrs.disabled"
              :class="readonly ? 'not-readonly-tf' : 'is-readonly-tf'"
              data-cy="timeTextField"
              @update:error="setError"
              @blur="(e) => updateValue(e)"
              @keydown.enter="(e) => updateValue(e)"
            >
              <template #append>
                <v-tooltip
                  v-if="clearable && clearable != ''"
                  bottom
                >
                  <template #activator="{ on: onClose }">
                    <v-icon
                      class="mr-2"
                      :disabled="readonly || handledAttrs.disabled"
                      v-on="onClose"
                      @click="clearValue"
                    >
                      mdi-close
                    </v-icon>
                  </template>
                  <span>{{ clearable }}</span>
                </v-tooltip>
              </template>
              <template #message="{ message }">
                <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="value"
                      v-bind="comportamiento"
                      :current_id_resolver="id_resolver"
                      :father-component-name="timePickerComponentName"
                      v-on="comportamientoEvents"
                    />
                  </template>
                </div>
              </template>
              <template
                v-if="readonly && !value"
                #prepend-inner
              >
                <div class="pr-2">
                  <v-icon color="warning"> mdi-alert-circle </v-icon>
                </div>
              </template>
            </v-text-field>
          </div>
        </template>
      </v-menu>
    </div>
  </ir-time-picker-bone>
</template>

<script>
import IrTimePickerBone from './IrTimePickerBone.vue'
import { setHintTag } from '../../../helpers/hint'
import { isOutlined } from '../../../helpers/utils.js'
import focusAndScrollMixin from '../../../mixins/focusAndScroll'
import { filterProps } from '../../../helpers/filterProps'
import { ref, computed, onMounted, useAttrs } from 'vue'
import { timePickerComponentName } from '../../../constants'
import { normalizeProps } from '../../../helpers/propsGenerator'

const localProps = normalizeProps({
  value: {
    type: String,
    default: '',
    required: true
  },
  isFocused: {
    type: Boolean,
    default: false
  },
  hint: {
    type: [String, Object],
    default: ''
  }
})

export default {
  name: timePickerComponentName,
  components: { IrTimePickerBone },
  props: localProps,
  setup(props, { emit, attrs: $attrs }) {
    const irTimePickerRef = ref(null)
    const otherProps = ref({ ...$attrs })
    const hasError = ref(false)
    const menu = ref(false)
    const componentKey = ref(0)

    const isOutlinedd = computed(() => {
      return isOutlined(handledAttrs.value.outlined)
    })

    const handledAttrs = computed(() => {
      const hint = setHintTag(otherProps.value, props.hint)
      let aux = { ...otherProps.value, ...$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
    })

    onMounted(() => {
      if (props.isFocused) {
        const ref = irTimePickerRef.value.$el.querySelector('input')
        focusAndScrollMixin.methods.focusAndScroll(ref)
      }
    })

    const updateValue = (e) => {
      const regex = /^([01]\d|2[0-3]):([0-5]\d)$/ // Se valida que cumpla con el formato hh:mm
      if (!regex.test(e.target.value)) {
        resetInput()
      } else {
        emitInput(e.target.value)
      }
    }

    const hourInFormat = computed(() => {
      if (props.value && props.value.length === 4) {
        return `${props.value.slice(0, 2)}:${props.value.slice(2)}`
      }
      return props.value
    })

    const emitInput = (value) => {
      if (value && value.includes(':')) {
        const [hours, minutes] = value.split(':')
        const formattedValue = hours.padStart(2, '0') + minutes.padStart(2, '0')
        emit('input', formattedValue) // Emitir en formato HHmm
      } else {
        emit('input', value)
      }
    }

    const resetInput = () => {
      componentKey.value++
    }

    const 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'
      )
    }

    const getFilteredProps = () => {
      let props = filterProps(irTimePickerRef.value, otherProps.value, ['data-cy'])
      props = { ...props, clearable: typeof props.clearable === 'undefined' }
      return props
    }

    const clearValue = () => {
      emit('input', '')
    }

    const setError = (eventValue) => {
      if (eventValue) {
        hasError.value = eventValue
      }
    }

    return {
      irTimePickerRef,
      otherProps,
      hasError,
      menu,
      componentKey,
      isOutlinedd,
      handledAttrs,
      updateValue,
      hourInFormat,
      resetInput,
      emitInput,
      isCompValid,
      getFilteredProps,
      clearValue,
      setError
    }
  }
}
</script>

<style scoped lang="scss"></style>
Last Updated: 9/17/2024, 4:36:15 PM