salix-front/src/components/common/VnInput.vue

158 lines
4.1 KiB
Vue

<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useValidator } from 'src/composables/useValidator';
import { useAttrs } from 'vue';
const $attrs = useAttrs();
const emit = defineEmits([
'update:modelValue',
'update:options',
'keyup.enter',
'remove',
]);
const $props = defineProps({
modelValue: {
type: [String, Number],
default: null,
},
isOutlined: {
type: Boolean,
default: false,
},
info: {
type: String,
default: '',
},
clearable: {
type: Boolean,
default: true,
},
});
const { validations } = useValidator();
const { t } = useI18n();
const requiredFieldRule = (val) => validations().required($attrs.required, val);
const vnInputRef = ref(null);
const value = computed({
get() {
return $props.modelValue;
},
set(value) {
emit('update:modelValue', value);
},
});
const hover = ref(false);
const styleAttrs = computed(() => {
return $props.isOutlined
? {
dense: true,
outlined: true,
rounded: true,
}
: {};
});
const focus = () => {
vnInputRef.value.focus();
};
const mixinRules = [
requiredFieldRule,
...($attrs.rules ?? []),
(val) => {
const { min, max } = vnInputRef.value.$attrs;
if (!min) return null;
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
if (!max) return null;
if (max > 0) {
if (Math.floor(val) > max) return t('inputMax', { value: max });
}
},
];
defineExpose({
focus,
});
const isInsertMode = ref(false);
const handleKeydown = (event) => {
const qInputEl = vnInputRef.value.$el.querySelector('input');
const cursorPos = qInputEl.selectionStart;
if (event.key === 'Insert') {
isInsertMode.value = !isInsertMode.value;
} else if (isInsertMode.value && !isNaN(event.key) && event.key.length === 1) {
event.preventDefault();
const currentValue = value.value;
if (cursorPos !== null) {
const newValue =
currentValue.slice(0, cursorPos) +
event.key +
currentValue.slice(cursorPos + 1);
value.value = newValue;
setTimeout(() => {
qInputEl.setSelectionRange(cursorPos + 1, cursorPos + 1);
}, 0);
}
}
};
</script>
<template>
<div @mouseover="hover = true" @mouseleave="hover = false">
<QInput
@keydown="handleKeydown"
ref="vnInputRef"
v-model="value"
v-bind="{ ...$attrs, ...styleAttrs }"
:type="$attrs.type"
:class="{ required: $attrs.required }"
@keyup.enter="emit('keyup.enter')"
:clearable="false"
:rules="mixinRules"
:lazy-rules="true"
hide-bottom-space
>
<template v-if="$slots.prepend" #prepend>
<slot name="prepend" />
</template>
<template #append>
<QIcon
name="close"
size="xs"
v-if="hover && value && !$attrs.disabled && $props.clearable"
@click="
() => {
value = null;
vnInputRef.focus();
emit('remove');
}
"
></QIcon>
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
<QIcon v-if="info" name="info">
<QTooltip max-width="350px">
{{ info }}
</QTooltip>
</QIcon>
</template>
</QInput>
</div>
</template>
<i18n lang="yml">
en:
inputMin: Must be more than {value}
inputMax: Must be less than {value}
es:
inputMin: Debe ser mayor a {value}
inputMax: Debe ser menor a {value}
</i18n>
<style lang="scss">
.q-field__append {
padding-inline: 0;
}
</style>