222 lines
5.7 KiB
Vue
222 lines
5.7 KiB
Vue
<script setup>
|
|
import { computed, ref, useAttrs, nextTick } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useRequired } from 'src/composables/useRequired';
|
|
|
|
const $attrs = useAttrs();
|
|
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
const { t } = useI18n();
|
|
const emit = defineEmits([
|
|
'update:modelValue',
|
|
'update:options',
|
|
'keyup.enter',
|
|
'remove',
|
|
'blur',
|
|
]);
|
|
|
|
const $props = defineProps({
|
|
modelValue: {
|
|
type: [String, Number],
|
|
default: null,
|
|
},
|
|
isOutlined: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
info: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
clearable: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
emptyToNull: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
insertable: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
maxlength: {
|
|
type: Number,
|
|
default: null,
|
|
},
|
|
uppercase: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
const vnInputRef = ref(null);
|
|
const value = computed({
|
|
get() {
|
|
return $props.modelValue;
|
|
},
|
|
set(value) {
|
|
if ($props.emptyToNull && value === '') value = null;
|
|
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();
|
|
};
|
|
|
|
defineExpose({
|
|
focus,
|
|
vnInputRef,
|
|
});
|
|
|
|
const mixinRules = [
|
|
requiredFieldRule,
|
|
...($attrs.rules ?? []),
|
|
(val) => {
|
|
const { maxlength } = vnInputRef.value;
|
|
if (maxlength && +val.length > maxlength)
|
|
return t(`maxLength`, { value: maxlength });
|
|
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 });
|
|
}
|
|
},
|
|
];
|
|
|
|
const handleKeydown = (e) => {
|
|
if (e.key === 'Backspace') return;
|
|
|
|
if ($props.insertable && e.key.match(/[0-9]/)) {
|
|
handleInsertMode(e);
|
|
}
|
|
};
|
|
|
|
const handleInsertMode = (e) => {
|
|
e.preventDefault();
|
|
const input = e.target;
|
|
const cursorPos = input.selectionStart;
|
|
const { maxlength } = vnInputRef.value;
|
|
let currentValue = value.value;
|
|
if (!currentValue) currentValue = e.key;
|
|
const newValue = e.key;
|
|
if (newValue && !isNaN(newValue) && cursorPos < maxlength) {
|
|
value.value =
|
|
currentValue.substring(0, cursorPos) +
|
|
newValue +
|
|
currentValue.substring(cursorPos + 1);
|
|
}
|
|
nextTick(() => {
|
|
input.setSelectionRange(cursorPos + 1, cursorPos + 1);
|
|
});
|
|
};
|
|
|
|
const handleUppercase = () => {
|
|
value.value = value.value?.toUpperCase() || '';
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
|
<QInput
|
|
ref="vnInputRef"
|
|
v-model="value"
|
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
|
:type="$attrs.type"
|
|
:class="{ required: isRequired }"
|
|
@keyup.enter="emit('keyup.enter')"
|
|
@blur="emit('blur')"
|
|
@keydown="handleKeydown"
|
|
:clearable="false"
|
|
:rules="mixinRules"
|
|
:lazy-rules="true"
|
|
hide-bottom-space
|
|
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'"
|
|
>
|
|
<template #prepend v-if="$slots.prepend">
|
|
<slot name="prepend" />
|
|
</template>
|
|
<template #append>
|
|
<QIcon
|
|
name="close"
|
|
size="xs"
|
|
:style="{
|
|
visibility:
|
|
hover &&
|
|
value &&
|
|
!$attrs.disabled &&
|
|
!$attrs.readonly &&
|
|
$props.clearable
|
|
? 'visible'
|
|
: 'hidden',
|
|
}"
|
|
@click="
|
|
() => {
|
|
value = null;
|
|
vnInputRef.focus();
|
|
emit('remove');
|
|
}
|
|
"
|
|
></QIcon>
|
|
|
|
<QIcon
|
|
name="match_case"
|
|
size="xs"
|
|
v-if="!$attrs.disabled && !$attrs.readonly && $props.uppercase"
|
|
@click="handleUppercase"
|
|
class="uppercase-icon"
|
|
>
|
|
<QTooltip>
|
|
{{ t('Convert to uppercase') }}
|
|
</QTooltip>
|
|
</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>
|
|
|
|
<style>
|
|
.uppercase-icon {
|
|
transition:
|
|
color 0.3s,
|
|
transform 0.2s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.uppercase-icon:hover {
|
|
color: #ed9937;
|
|
transform: scale(1.2);
|
|
}
|
|
</style>
|
|
<i18n>
|
|
en:
|
|
inputMin: Must be more than {value}
|
|
maxLength: The value exceeds {value} characters
|
|
inputMax: Must be less than {value}
|
|
|
|
es:
|
|
inputMin: Debe ser mayor a {value}
|
|
maxLength: El valor excede los {value} carácteres
|
|
inputMax: Debe ser menor a {value}
|
|
Convert to uppercase: Convertir a mayúsculas
|
|
</i18n>
|