219 lines
6.0 KiB
Vue
219 lines
6.0 KiB
Vue
<script setup>
|
|
import { nextTick, watch, computed, ref, useAttrs } from 'vue';
|
|
import { date, getCssVar } from 'quasar';
|
|
import VnDate from './VnDate.vue';
|
|
import { useRequired } from 'src/composables/useRequired';
|
|
|
|
const $attrs = useAttrs();
|
|
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
const model = defineModel({ type: [String, Date] });
|
|
|
|
const $props = defineProps({
|
|
isOutlined: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
showEvent: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
});
|
|
|
|
const vnInputDateRef = ref(null);
|
|
const errColor = getCssVar('negative');
|
|
const textColor = ref('');
|
|
|
|
const dateFormat = 'DD/MM/YYYY';
|
|
const isPopupOpen = ref();
|
|
const hover = ref();
|
|
|
|
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
|
|
|
const popupDate = computed(() =>
|
|
model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value,
|
|
);
|
|
|
|
const styleAttrs = computed(() => {
|
|
return $props.isOutlined
|
|
? {
|
|
dense: true,
|
|
outlined: true,
|
|
rounded: true,
|
|
}
|
|
: {};
|
|
});
|
|
|
|
const inputValue = ref('');
|
|
|
|
const validateAndCleanInput = (value) => {
|
|
inputValue.value = value.replace(/[^0-9./-]/g, '');
|
|
};
|
|
|
|
const manageDate = (date) => {
|
|
inputValue.value = date.split('/').reverse().join('/');
|
|
isPopupOpen.value = false;
|
|
};
|
|
|
|
watch(
|
|
() => model.value,
|
|
(nVal) => {
|
|
if (nVal) inputValue.value = date.formatDate(new Date(model.value), dateFormat);
|
|
else inputValue.value = '';
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
const formatDate = () => {
|
|
let value = inputValue.value;
|
|
if (!value || value === model.value) {
|
|
textColor.value = '';
|
|
return;
|
|
}
|
|
const regex =
|
|
/^([0]?[1-9]|[12][0-9]|3[01])([./-])([0]?[1-9]|1[0-2])([./-](\d{1,4}))?$/;
|
|
if (!regex.test(value)) {
|
|
textColor.value = errColor;
|
|
return;
|
|
}
|
|
|
|
value = value.replace(/[.-]/g, '/');
|
|
const parts = value.split('/');
|
|
if (parts.length < 2) {
|
|
textColor.value = errColor;
|
|
return;
|
|
}
|
|
|
|
let [day, month, year] = parts;
|
|
if (day.length === 1) day = '0' + day;
|
|
if (month.length === 1) month = '0' + month;
|
|
|
|
const currentYear = Date.vnNew().getFullYear();
|
|
if (!year) year = currentYear;
|
|
const millennium = currentYear.toString().slice(0, 1);
|
|
|
|
switch (year.length) {
|
|
case 1:
|
|
year = `${millennium}00${year}`;
|
|
break;
|
|
case 2:
|
|
year = `${millennium}0${year}`;
|
|
break;
|
|
case 3:
|
|
year = `${millennium}${year}`;
|
|
break;
|
|
case 4:
|
|
break;
|
|
}
|
|
|
|
let isoCandidate = `${year}/${month}/${day}`;
|
|
isoCandidate = date.formatDate(
|
|
new Date(isoCandidate).toISOString(),
|
|
'YYYY-MM-DDTHH:mm:ss.SSSZ',
|
|
);
|
|
const [isoYear, isoMonth, isoDay] = isoCandidate.split('-').map((e) => parseInt(e));
|
|
const parsedDate = new Date(isoYear, isoMonth - 1, isoDay);
|
|
|
|
const isValidDate =
|
|
parsedDate instanceof Date &&
|
|
!isNaN(parsedDate) &&
|
|
parsedDate.getFullYear() === parseInt(year) &&
|
|
parsedDate.getMonth() === parseInt(month) - 1 &&
|
|
parsedDate.getDate() === parseInt(day);
|
|
|
|
if (!isValidDate) {
|
|
textColor.value = errColor;
|
|
return;
|
|
}
|
|
|
|
if (model.value) {
|
|
const original =
|
|
model.value instanceof Date ? model.value : new Date(model.value);
|
|
parsedDate.setHours(
|
|
original.getHours(),
|
|
original.getMinutes(),
|
|
original.getSeconds(),
|
|
original.getMilliseconds(),
|
|
);
|
|
}
|
|
|
|
model.value = parsedDate.toISOString();
|
|
|
|
textColor.value = '';
|
|
};
|
|
|
|
const handleEnter = (event) => {
|
|
formatDate();
|
|
|
|
nextTick(() => {
|
|
const newEvent = new KeyboardEvent('keydown', {
|
|
key: 'Enter',
|
|
code: 'Enter',
|
|
bubbles: true,
|
|
cancelable: true,
|
|
});
|
|
vnInputDateRef.value?.$el?.dispatchEvent(newEvent);
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
|
<QInput
|
|
ref="vnInputDateRef"
|
|
v-model="inputValue"
|
|
class="vn-input-date"
|
|
placeholder="dd/mm/aaaa"
|
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
|
:class="{ required: isRequired }"
|
|
:rules="mixinRules"
|
|
:clearable="false"
|
|
:input-style="{ color: textColor }"
|
|
@click="isPopupOpen = !isPopupOpen"
|
|
@keydown="isPopupOpen = false"
|
|
@blur="formatDate"
|
|
@keydown.enter.prevent="handleEnter"
|
|
hide-bottom-space
|
|
:data-cy="($attrs['data-cy'] ?? $attrs.label) + '_inputDate'"
|
|
@update:model-value="validateAndCleanInput"
|
|
>
|
|
<template #append>
|
|
<QIcon
|
|
name="close"
|
|
size="xs"
|
|
v-if="
|
|
($attrs.clearable == undefined || $attrs.clearable) &&
|
|
hover &&
|
|
inputValue &&
|
|
!$attrs.disable
|
|
"
|
|
@click="
|
|
vnInputDateRef.focus();
|
|
inputValue = null;
|
|
model = null;
|
|
isPopupOpen = false;
|
|
"
|
|
/>
|
|
</template>
|
|
<QMenu
|
|
v-if="$q.screen.gt.xs"
|
|
transition-show="scale"
|
|
transition-hide="scale"
|
|
v-model="isPopupOpen"
|
|
anchor="bottom left"
|
|
self="top start"
|
|
:no-focus="true"
|
|
:no-parent-event="true"
|
|
>
|
|
<VnDate v-model="popupDate" @update:model-value="manageDate" />
|
|
</QMenu>
|
|
<QDialog v-else v-model="isPopupOpen">
|
|
<VnDate v-model="popupDate" @update:model-value="manageDate" />
|
|
</QDialog>
|
|
</QInput>
|
|
</div>
|
|
</template>
|
|
<i18n>
|
|
es:
|
|
Open date: Abrir fecha
|
|
</i18n>
|