Atualização floresta

This commit is contained in:
Luiz Silva 2026-05-31 11:27:51 -03:00
parent 0886fd5c3e
commit 2d4d8f8929
103 changed files with 19266 additions and 14160 deletions

View file

@ -27,126 +27,19 @@
<script lang="ts">
import { computed, defineComponent, type PropType, ref, watch } from "vue"
import type { PadroesEntradas } from "./tiposEntradas"
import "./EliEntradaNumero.css"
import {
casasDecimaisFromPrecisao,
formatarNumero,
limitarCasasDecimais,
parseNumero,
somenteNumeros,
somenteNumerosESeparadorDecimal,
valorParcialDecimal,
} from "./EliEntradaNumero.helpers"
type EntradaNumero = PadroesEntradas["numero"]
function casasDecimaisFromPrecisao(precisao: number): number {
if (!Number.isFinite(precisao) || precisao <= 0) return 0
if (precisao >= 1) return 0
// Preferência por contar casas decimais na representação (ex.: 0.01 -> 2)
const texto = precisao.toString()
if (texto.includes("e-")) {
const [, exp] = texto.split("e-")
const n = Number(exp)
return Number.isFinite(n) ? n : 0
}
const idx = texto.indexOf(".")
if (idx === -1) return 0
const dec = texto.slice(idx + 1).replace(/0+$/, "")
return dec.length
}
/**
* Analisa a entrada de texto e converte em um valor numérico válido (float).
* Troca a vírgula brasileira por ponto antes de passar para o parser nativo.
*
* @param texto - O texto bruto digitado ou vindo do estado
* @returns O número processado ou `null` caso inválido ou vazio
*/
function parseNumero(texto: string): number | null {
const normalizado = (texto ?? "").trim().replace(/,/g, ".")
if (!normalizado) return null
const n = Number(normalizado)
if (Number.isNaN(n)) return null
return n
}
/**
* Formata um valor numérico para a exibição na interface (usando vírgula como separador).
* Em caso de precisão nula (modo livre), preserva a semântica de exibição direta sem forçar o preenchimento de zeros (toFixed).
*
* @param value - O valor numérico de fato emitido
* @param casas - Opcionalmente, o número exato de casas decimais para fixar
* @returns String formatada amigável para exibição em pt-BR
*/
function formatarNumero(
value: number | null | undefined,
casas: number | null,
) {
if (value === null || value === undefined) return ""
if (casas === null) return String(value).replace(/\./g, ",")
// Garantimos que o texto visual corresponda ao value (fixed).
const fixed = Number(value).toFixed(Math.max(0, casas))
// Exibe com vírgula (pt-BR)
return fixed.replace(/\./g, ",")
}
/**
* Retira todo tipo de sujeira de uma string, devolvendo restritamente números (para inteiros).
*
* @param texto - String de entrada
* @returns Apenas dígitos de 0 a 9
*/
function somenteNumeros(texto: string) {
return (texto ?? "").replace(/\D+/g, "")
}
/**
* Higieniza o texto preservando apenas os dígitos e exatamente UM separador decimal.
* Colapsa múltiplos separadores e normaliza se o separador vier no início (",4" => "0,4").
*
* @param texto - Texto misto de entrada
* @returns String contendo número e (opcionalmente) uma vírgula ou ponto residual
*/
function somenteNumerosESeparadorDecimal(texto: string) {
// Mantém apenas números e UM separador decimal ("," ou "."), sem sinais.
// Implementação baseada em regex + colapso de separadores.
const limpo = (texto ?? "").replace(/[^0-9.,]+/g, "")
const matchSep = limpo.match(/[.,]/)
if (!matchSep) return limpo
const sep = matchSep[0]
const idx = limpo.indexOf(sep)
const antesRaw = limpo.slice(0, idx).replace(/[.,]/g, "")
const depois = limpo.slice(idx + 1).replace(/[.,]/g, "")
// Se o usuário começa pelo separador (",4"), normalizamos para "0,4".
const antes = antesRaw.length ? antesRaw : "0"
return `${antes}${sep}${depois}`
}
function limitarCasasDecimais(texto: string, casas: number | null) {
if (casas === null) return texto
if (casas <= 0) return texto.replace(/[.,]/g, "")
const matchSep = texto.match(/[.,]/)
if (!matchSep) return texto
const sep = matchSep[0]
const idx = texto.indexOf(sep)
const inteiro = texto.slice(0, idx)
const frac = texto.slice(idx + 1)
return `${inteiro}${sep}${frac.slice(0, casas)}`
}
/**
* Resolve o edge-case de digitação transitória parcial terminada em separador (ex: "1,").
* Identifica se a string tem este formato pendente e retorna temporariamente sua base inteira
* sem permitir que o valor se torne nulo.
*
* @param texto - O texto já higienizado, tipicamente terminando em vírgula ou ponto
* @returns A conversão parcial estrita ou `null` caso falhe
*/
function valorParcialDecimal(texto: string): number | null {
// Regra (B): se o usuário digitou "1," ou "1." emite 1.
const m = texto.match(/^(\d+)[.,]$/)
if (!m) return null
const n = Number(m[1])
return Number.isNaN(n) ? null : n
}
export default defineComponent({
name: "EliEntradaNumero",
inheritAttrs: false,
@ -229,12 +122,8 @@ export default defineComponent({
}
// Modo livre (sem precisao) ou inteiro: aceita números e 1 separador (no caso livre)
const base = isInteiro.value
? somenteNumeros(texto)
: somenteNumerosESeparadorDecimal(texto)
const textoFiltrado = isInteiro.value
? base
: limitarCasasDecimais(base, casasDecimais.value)
const base = isInteiro.value ? somenteNumeros(texto) : somenteNumerosESeparadorDecimal(texto)
const textoFiltrado = isInteiro.value ? base : limitarCasasDecimais(base, casasDecimais.value)
// Emissão:
// - vazio => null
@ -242,9 +131,7 @@ export default defineComponent({
// - caso geral => parse normal
let out: EntradaNumero["value"] = null
if (textoFiltrado) {
const parcial = isInteiro.value
? null
: valorParcialDecimal(textoFiltrado)
const parcial = isInteiro.value ? null : valorParcialDecimal(textoFiltrado)
const n = parcial ?? parseNumero(textoFiltrado)
out = (n === null ? null : n) as EntradaNumero["value"]
}
@ -269,19 +156,3 @@ export default defineComponent({
})
</script>
<style>
.eli-entrada__prefixo,
.eli-entrada__sufixo {
opacity: 0.75;
font-size: 0.9em;
white-space: nowrap;
}
.eli-entrada__prefixo {
margin-right: 6px;
}
.eli-entrada__sufixo {
margin-left: 6px;
}
</style>