rafatoração de componentes de entrada

This commit is contained in:
Luiz Silva 2026-01-29 11:27:08 -03:00
parent 6aedf2469f
commit 27c9e4d5e2
45 changed files with 1295 additions and 2605 deletions

View file

@ -1,6 +1,8 @@
<template>
<v-text-field
v-model="localValue"
:type="inputHtmlType"
:inputmode="inputMode"
:label="opcoes?.rotulo"
:placeholder="opcoes?.placeholder"
:counter="opcoes?.limiteCaracteres"
@ -8,12 +10,16 @@
v-bind="attrs"
@focus="() => emit('focus')"
@blur="() => emit('blur')"
@input="onInput"
/>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from "vue";
import type { PadroesEntradas } from "./tiposEntradas";
import { formatarCpfCnpj } from "./utils/cpfCnpj";
import { formatTelefone } from "./utils/telefone";
import { formatarCep } from "./utils/cep";
type EntradaTexto = PadroesEntradas["texto"];
@ -40,6 +46,8 @@ export default defineComponent({
blur: () => true,
},
setup(props, { attrs, emit }) {
const formato = computed(() => props.opcoes?.formato ?? "texto");
const localValue = computed<EntradaTexto["value"]>({
get: () => props.value,
set: (v) => {
@ -49,7 +57,43 @@ export default defineComponent({
},
});
return { attrs, emit, localValue };
const inputHtmlType = computed(() => {
if (formato.value === "email") return "email";
if (formato.value === "url") return "url";
return "text";
});
const inputMode = computed<string | undefined>(() => {
if (formato.value === "telefone") return "tel";
if (formato.value === "cpfCnpj" || formato.value === "cep") return "numeric";
return undefined;
});
function aplicarFormato(valor: string) {
switch (formato.value) {
case "telefone":
return formatTelefone(valor);
case "cpfCnpj":
return formatarCpfCnpj(valor);
case "cep":
return formatarCep(valor);
default:
return valor;
}
}
function onInput(e: Event) {
const target = e.target as HTMLInputElement;
const resultado = aplicarFormato(target.value);
// garante que o input mostre o valor formatado
target.value = resultado;
// regra do projeto: value sempre igual ao que aparece
localValue.value = resultado;
}
return { attrs, emit, localValue, inputHtmlType, inputMode, onInput };
},
});
</script>

View file

@ -47,18 +47,24 @@ export type PadroesEntradas = {
{
/** Limite máximo de caracteres permitidos (se definido) */
limiteCaracteres?: number
/**
* Formato/máscara aplicada ao texto.
* Obs: o `value` SEMPRE será o texto formatado (o que aparece no input).
*/
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep"
}
>
numero: tipoPadraoEntrada<
number | null | undefined,
{
/** Unidade de medida (ex.: "kg", "m³") */
/** Unidade de medida (ex.: "kg", "m³") */
sufixo?: string
/** Moéda (ex.: "R$") */
/** Moéda (ex.: "R$") */
prefixo?: string
/**

View file

@ -0,0 +1,10 @@
function somenteNumeros(valor: string) {
return valor.replace(/\D+/g, "");
}
/** Formata CEP no padrão 00000-000 */
export function formatarCep(valor: string) {
const digitos = somenteNumeros(valor);
if (!digitos) return "";
return digitos.replace(/^(\d{5})(\d)/, "$1-$2").slice(0, 9);
}

View file

@ -0,0 +1,24 @@
function somenteNumeros(v: string): string {
return v.replace(/\D+/g, "");
}
export function formatarCpfCnpj(v: string): string {
const d = somenteNumeros(v);
// CPF
if (d.length <= 11) {
return d
.replace(/(\d{3})(\d)/, "$1.$2")
.replace(/(\d{3})(\d)/, "$1.$2")
.replace(/(\d{3})(\d{1,2})$/, "$1-$2")
.slice(0, 14);
}
// CNPJ
return d
.replace(/^(\d{2})(\d)/, "$1.$2")
.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3")
.replace(/\.(\d{3})(\d)/, ".$1/$2")
.replace(/(\d{4})(\d)/, "$1-$2")
.slice(0, 18);
}

View file

@ -0,0 +1,29 @@
/**
* Remove tudo que não é número
*/
export function sanitizeTelefone(value: string): string {
return value.replace(/\D+/g, "");
}
/**
* Aplica máscara dinâmica de telefone BR
*/
export function formatTelefone(value: string): string {
const digits = sanitizeTelefone(value);
if (!digits) return "";
// (99) 9999-9999
if (digits.length <= 10) {
return digits
.replace(/^(\d{2})(\d)/, "($1) $2")
.replace(/(\d{4})(\d)/, "$1-$2")
.slice(0, 14);
}
// (99) 99999-9999
return digits
.replace(/^(\d{2})(\d)/, "($1) $2")
.replace(/(\d{5})(\d)/, "$1-$2")
.slice(0, 15);
}