# 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 |
| El valor |
| value | Date | Valor del campo. Con formato JSON Date, Ejemplo: | |
| required | Boolean |
| En valor |
| 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: En la propiedad En la propiedad En la propiedad En la propiedad 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>