emplementado entrada numero e entrada texto
This commit is contained in:
parent
fa1f93aedc
commit
de7c19be24
39 changed files with 2155 additions and 1058 deletions
2
dist/eli-vue.css
vendored
2
dist/eli-vue.css
vendored
File diff suppressed because one or more lines are too long
2404
dist/eli-vue.es.js
vendored
2404
dist/eli-vue.es.js
vendored
File diff suppressed because it is too large
Load diff
38
dist/eli-vue.umd.js
vendored
38
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
49
dist/types/componentes/EliEntrada/EliEntradaNumero.vue.d.ts
vendored
Normal file
49
dist/types/componentes/EliEntrada/EliEntradaNumero.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import type { PadroesEntradas } from "./tiposEntradas";
|
||||||
|
type EntradaNumero = PadroesEntradas["numero"];
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: PropType<EntradaNumero["value"]>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: PropType<EntradaNumero["opcoes"]>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
attrs: {
|
||||||
|
[x: string]: unknown;
|
||||||
|
};
|
||||||
|
emit: ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void) & ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void);
|
||||||
|
displayValue: import("vue").Ref<string, string>;
|
||||||
|
isInteiro: import("vue").ComputedRef<boolean>;
|
||||||
|
onUpdateModelValue: (texto: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
"update:value": (_v: EntradaNumero["value"]) => true;
|
||||||
|
/** Compat Vue2 (v-model padrão: value + input) */
|
||||||
|
input: (_v: EntradaNumero["value"]) => true;
|
||||||
|
change: (_v: EntradaNumero["value"]) => true;
|
||||||
|
focus: () => true;
|
||||||
|
blur: () => true;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: PropType<EntradaNumero["value"]>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: PropType<EntradaNumero["opcoes"]>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
onFocus?: (() => any) | undefined;
|
||||||
|
onBlur?: (() => any) | undefined;
|
||||||
|
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
}>, {
|
||||||
|
value: number | null | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
47
dist/types/componentes/EliEntrada/EliEntradaTexto.vue.d.ts
vendored
Normal file
47
dist/types/componentes/EliEntrada/EliEntradaTexto.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import type { PadroesEntradas } from "./tiposEntradas";
|
||||||
|
type EntradaTexto = PadroesEntradas["texto"];
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: PropType<EntradaTexto["value"]>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: PropType<EntradaTexto["opcoes"]>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
attrs: {
|
||||||
|
[x: string]: unknown;
|
||||||
|
};
|
||||||
|
emit: ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void) & ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void);
|
||||||
|
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
"update:value": (_v: EntradaTexto["value"]) => true;
|
||||||
|
/** Compat Vue2 (v-model padrão: value + input) */
|
||||||
|
input: (_v: EntradaTexto["value"]) => true;
|
||||||
|
change: (_v: EntradaTexto["value"]) => true;
|
||||||
|
focus: () => true;
|
||||||
|
blur: () => true;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: PropType<EntradaTexto["value"]>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: PropType<EntradaTexto["opcoes"]>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
onFocus?: (() => any) | undefined;
|
||||||
|
onBlur?: (() => any) | undefined;
|
||||||
|
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
}>, {
|
||||||
|
value: string | null | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
4
dist/types/componentes/EliEntrada/index.d.ts
vendored
Normal file
4
dist/types/componentes/EliEntrada/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import EliEntradaTexto from "./EliEntradaTexto.vue";
|
||||||
|
import EliEntradaNumero from "./EliEntradaNumero.vue";
|
||||||
|
export { EliEntradaTexto, EliEntradaNumero };
|
||||||
|
export type { PadroesEntradas, TipoEntrada } from "./tiposEntradas";
|
||||||
106
dist/types/componentes/EliEntrada/registryEliEntradas.d.ts
vendored
Normal file
106
dist/types/componentes/EliEntrada/registryEliEntradas.d.ts
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
export declare const registryTabelaCelulas: {
|
||||||
|
readonly texto: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
value: {
|
||||||
|
type: import("vue").PropType<string | null | undefined>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: import("vue").PropType<{
|
||||||
|
rotulo: string;
|
||||||
|
placeholder?: string;
|
||||||
|
} & {
|
||||||
|
limiteCaracteres?: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
attrs: {
|
||||||
|
[x: string]: unknown;
|
||||||
|
};
|
||||||
|
emit: ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void) & ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void);
|
||||||
|
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
"update:value": (_v: string | null | undefined) => true;
|
||||||
|
input: (_v: string | null | undefined) => true;
|
||||||
|
change: (_v: string | null | undefined) => true;
|
||||||
|
focus: () => true;
|
||||||
|
blur: () => true;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
value: {
|
||||||
|
type: import("vue").PropType<string | null | undefined>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: import("vue").PropType<{
|
||||||
|
rotulo: string;
|
||||||
|
placeholder?: string;
|
||||||
|
} & {
|
||||||
|
limiteCaracteres?: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
onFocus?: (() => any) | undefined;
|
||||||
|
onBlur?: (() => any) | undefined;
|
||||||
|
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||||
|
}>, {
|
||||||
|
value: string | null | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
readonly numero: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
value: {
|
||||||
|
type: import("vue").PropType<number | null | undefined>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: import("vue").PropType<{
|
||||||
|
rotulo: string;
|
||||||
|
placeholder?: string;
|
||||||
|
} & {
|
||||||
|
sufixo?: string;
|
||||||
|
prefixo?: string;
|
||||||
|
precisao?: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
attrs: {
|
||||||
|
[x: string]: unknown;
|
||||||
|
};
|
||||||
|
emit: ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void) & ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void);
|
||||||
|
displayValue: import("vue").Ref<string, string>;
|
||||||
|
isInteiro: import("vue").ComputedRef<boolean>;
|
||||||
|
onUpdateModelValue: (texto: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
"update:value": (_v: number | null | undefined) => true;
|
||||||
|
input: (_v: number | null | undefined) => true;
|
||||||
|
change: (_v: number | null | undefined) => true;
|
||||||
|
focus: () => true;
|
||||||
|
blur: () => true;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
value: {
|
||||||
|
type: import("vue").PropType<number | null | undefined>;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
opcoes: {
|
||||||
|
type: import("vue").PropType<{
|
||||||
|
rotulo: string;
|
||||||
|
placeholder?: string;
|
||||||
|
} & {
|
||||||
|
sufixo?: string;
|
||||||
|
prefixo?: string;
|
||||||
|
precisao?: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
onFocus?: (() => any) | undefined;
|
||||||
|
onBlur?: (() => any) | undefined;
|
||||||
|
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||||
|
}>, {
|
||||||
|
value: number | null | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
};
|
||||||
65
dist/types/componentes/EliEntrada/tiposEntradas.d.ts
vendored
Normal file
65
dist/types/componentes/EliEntrada/tiposEntradas.d.ts
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
* Tipos base para componentes de entrada (EliEntrada*)
|
||||||
|
*
|
||||||
|
* Objetivo:
|
||||||
|
* - Padronizar o shape de dados de todos os componentes de entrada.
|
||||||
|
* - Cada entrada possui sempre:
|
||||||
|
* 1) `value`: o valor atual (tipado)
|
||||||
|
* 2) `opcoes`: configuração do componente (rótulo, placeholder e extras por tipo)
|
||||||
|
*
|
||||||
|
* Como usar:
|
||||||
|
* - `PadroesEntradas[tipo]` retorna a tipagem completa (value + opcoes) daquele tipo.
|
||||||
|
* - `TipoEntrada` é a união com todos os tipos suportados (ex.: "texto" | "numero").
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Contrato padrão de uma entrada.
|
||||||
|
*
|
||||||
|
* @typeParam T - tipo do `value` (ex.: string | null | undefined)
|
||||||
|
* @typeParam Mais - campos adicionais dentro de `opcoes`, específicos do tipo de entrada
|
||||||
|
*/
|
||||||
|
export type tipoPadraoEntrada<T, Mais extends Record<string, unknown> = {}> = {
|
||||||
|
/** Valor atual do campo (pode aceitar null/undefined quando aplicável) */
|
||||||
|
value: T;
|
||||||
|
/** Configurações do componente (visuais + regras simples do tipo) */
|
||||||
|
opcoes: {
|
||||||
|
/** Rótulo exibido ao usuário */
|
||||||
|
rotulo: string;
|
||||||
|
/** Texto de ajuda dentro do input quando vazio */
|
||||||
|
placeholder?: string;
|
||||||
|
} & Mais;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Mapa de tipos de entrada suportados e suas configurações específicas.
|
||||||
|
*
|
||||||
|
* Observação importante:
|
||||||
|
* - As chaves deste objeto (ex.: "texto", "numero") viram o tipo `TipoEntrada`.
|
||||||
|
* - Cada item define:
|
||||||
|
* - `value`: tipo do valor
|
||||||
|
* - `opcoes`: opções comuns + extras específicas
|
||||||
|
*/
|
||||||
|
export type PadroesEntradas = {
|
||||||
|
texto: tipoPadraoEntrada<string | null | undefined, {
|
||||||
|
/** Limite máximo de caracteres permitidos (se definido) */
|
||||||
|
limiteCaracteres?: number;
|
||||||
|
}>;
|
||||||
|
numero: tipoPadraoEntrada<number | null | undefined, {
|
||||||
|
/** Unidade de medida (ex.: "kg", "m³") */
|
||||||
|
sufixo?: string;
|
||||||
|
/** Moéda (ex.: "R$") */
|
||||||
|
prefixo?: string;
|
||||||
|
/**
|
||||||
|
* Passo/precisão do valor numérico.
|
||||||
|
* - 1 => somente inteiros
|
||||||
|
* - 0.1 => 1 casa decimal
|
||||||
|
* - 0.01 => 2 casas decimais
|
||||||
|
*
|
||||||
|
* Dica: este conceito corresponde ao atributo HTML `step`.
|
||||||
|
*/
|
||||||
|
precisao?: number;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* União dos tipos de entrada suportados.
|
||||||
|
* Ex.: "texto" | "numero"
|
||||||
|
*/
|
||||||
|
export type TipoEntrada = keyof PadroesEntradas;
|
||||||
|
|
@ -13,7 +13,6 @@ export type EliColuna<T> = {
|
||||||
/** Função responsável por renderizar o conteúdo da célula. */
|
/** Função responsável por renderizar o conteúdo da célula. */
|
||||||
celula: (linha: T) => ComponenteCelula;
|
celula: (linha: T) => ComponenteCelula;
|
||||||
/** Ação opcional disparada ao clicar na célula. */
|
/** Ação opcional disparada ao clicar na célula. */
|
||||||
acao?: () => void;
|
|
||||||
/**
|
/**
|
||||||
* Campo de ordenação associado à coluna. Caso informado, a coluna passa a
|
* Campo de ordenação associado à coluna. Caso informado, a coluna passa a
|
||||||
* exibir controles de ordenação e utiliza o valor como chave para o backend.
|
* exibir controles de ordenação e utiliza o valor como chave para o backend.
|
||||||
4
dist/types/index.d.ts
vendored
4
dist/types/index.d.ts
vendored
|
|
@ -6,7 +6,8 @@ import { EliBadge } from "./componentes/indicador";
|
||||||
import { EliInput } from "./componentes/campo";
|
import { EliInput } from "./componentes/campo";
|
||||||
import { EliCartao } from "./componentes/cartao";
|
import { EliCartao } from "./componentes/cartao";
|
||||||
import { EliDataHora } from "./componentes/data_hora";
|
import { EliDataHora } from "./componentes/data_hora";
|
||||||
import { EliTabela } from "./components/eli/EliTabela";
|
import { EliTabela } from "./componentes/EliTabela";
|
||||||
|
import { EliEntradaTexto, EliEntradaNumero } from "./componentes/EliEntrada";
|
||||||
export { EliOlaMundo };
|
export { EliOlaMundo };
|
||||||
export { EliBotao };
|
export { EliBotao };
|
||||||
export { EliBadge };
|
export { EliBadge };
|
||||||
|
|
@ -14,5 +15,6 @@ export { EliInput };
|
||||||
export { EliCartao };
|
export { EliCartao };
|
||||||
export { EliDataHora };
|
export { EliDataHora };
|
||||||
export { EliTabela };
|
export { EliTabela };
|
||||||
|
export { EliEntradaTexto, EliEntradaNumero };
|
||||||
declare const EliVue: Plugin;
|
declare const EliVue: Plugin;
|
||||||
export default EliVue;
|
export default EliVue;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "eli-vue",
|
"name": "eli-vue",
|
||||||
"version": "0.1.48",
|
"version": "0.1.58",
|
||||||
"private": false,
|
"private": false,
|
||||||
"main": "./dist/eli-vue.umd.js",
|
"main": "./dist/eli-vue.umd.js",
|
||||||
"module": "./dist/eli-vue.es.js",
|
"module": "./dist/eli-vue.es.js",
|
||||||
|
|
|
||||||
236
src/componentes/EliEntrada/EliEntradaNumero.vue
Normal file
236
src/componentes/EliEntrada/EliEntradaNumero.vue
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
<template>
|
||||||
|
<v-text-field
|
||||||
|
:model-value="displayValue"
|
||||||
|
:label="opcoes?.rotulo"
|
||||||
|
:placeholder="opcoes?.placeholder"
|
||||||
|
:type="isInteiro ? 'number' : 'text'"
|
||||||
|
:inputmode="isInteiro ? 'numeric' : 'decimal'"
|
||||||
|
:pattern="isInteiro ? '[0-9]*' : '[0-9.,]*'"
|
||||||
|
v-bind="attrs"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
@focus="() => emit('focus')"
|
||||||
|
@blur="() => emit('blur')"
|
||||||
|
>
|
||||||
|
<!--
|
||||||
|
Em alguns cenários (ex.: type="number"), o prop `suffix` do Vuetify pode não aparecer.
|
||||||
|
Usamos slots para garantir exibição consistente de prefixo/sufixo.
|
||||||
|
-->
|
||||||
|
<template v-if="opcoes?.prefixo" #prepend-inner>
|
||||||
|
<span class="eli-entrada__prefixo">{{ opcoes.prefixo }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="opcoes?.sufixo" #append-inner>
|
||||||
|
<span class="eli-entrada__sufixo">{{ opcoes.sufixo }}</span>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType, ref, watch } from "vue";
|
||||||
|
import type { PadroesEntradas } from "./tiposEntradas";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatarNumero(value: number | null | undefined, casas: number | null) {
|
||||||
|
if (value === null || value === undefined) return "";
|
||||||
|
if (casas === null) return String(value);
|
||||||
|
|
||||||
|
// 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, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
function somenteNumeros(texto: string) {
|
||||||
|
return (texto ?? "").replace(/\D+/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
props: {
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: [Number, null] as unknown as PropType<EntradaNumero["value"]>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
opcoes: {
|
||||||
|
type: Object as PropType<EntradaNumero["opcoes"]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
"update:value": (_v: EntradaNumero["value"]) => true,
|
||||||
|
/** Compat Vue2 (v-model padrão: value + input) */
|
||||||
|
input: (_v: EntradaNumero["value"]) => true,
|
||||||
|
change: (_v: EntradaNumero["value"]) => true,
|
||||||
|
focus: () => true,
|
||||||
|
blur: () => true,
|
||||||
|
},
|
||||||
|
setup(props, { attrs, emit }) {
|
||||||
|
// Se `precisao` não existir => não limitamos casas (mas ainda bloqueamos caracteres inválidos).
|
||||||
|
const casasDecimais = computed<number | null>(() => {
|
||||||
|
const p = props.opcoes?.precisao;
|
||||||
|
if (p === undefined || p === null) return null;
|
||||||
|
return casasDecimaisFromPrecisao(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isInteiro = computed(() => {
|
||||||
|
// quando não existe precisão, tratamos como decimal livre
|
||||||
|
return casasDecimais.value === 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isFixedPoint = computed(() => {
|
||||||
|
// Quando precisao existe e é < 1, controlamos a vírgula automaticamente.
|
||||||
|
const casas = casasDecimais.value;
|
||||||
|
return casas !== null && casas > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Controle do texto exibido: impede o campo de ficar com caracteres inválidos.
|
||||||
|
const displayValue = ref<string>("");
|
||||||
|
const ultimoEmitido = ref<EntradaNumero["value"] | undefined>(undefined);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(v) => {
|
||||||
|
// Se foi uma atualização resultante do que acabamos de emitir, não sobrescreve.
|
||||||
|
// Isso evita o efeito de "apagar" o texto ao digitar algo parcial (ex.: "1,")
|
||||||
|
// quando o valor emitido é `1` ou `null`.
|
||||||
|
if (v === ultimoEmitido.value) return;
|
||||||
|
|
||||||
|
displayValue.value = formatarNumero(v as any, casasDecimais.value);
|
||||||
|
ultimoEmitido.value = v;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
function onUpdateModelValue(texto: string) {
|
||||||
|
// Modo fixed-point: usuário digita números continuamente e a vírgula é inserida automaticamente.
|
||||||
|
if (isFixedPoint.value) {
|
||||||
|
const casas = casasDecimais.value ?? 0;
|
||||||
|
const digitos = somenteNumeros(texto);
|
||||||
|
// ex.: casas=2, "1" => 0.01 ; "123" => 1.23
|
||||||
|
const baseInt = digitos ? Number(digitos) : 0;
|
||||||
|
const divisor = Math.pow(10, casas);
|
||||||
|
const n = digitos ? baseInt / divisor : null;
|
||||||
|
|
||||||
|
const out = (n === null ? null : n) as EntradaNumero["value"];
|
||||||
|
ultimoEmitido.value = out;
|
||||||
|
emit("update:value", out);
|
||||||
|
emit("input", out);
|
||||||
|
emit("change", out);
|
||||||
|
|
||||||
|
// display deve ser sempre o reflexo do value
|
||||||
|
displayValue.value = formatarNumero(out as any, casas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Emissão:
|
||||||
|
// - vazio => null
|
||||||
|
// - decimal parcial ("1,") => 1 (regra B)
|
||||||
|
// - caso geral => parse normal
|
||||||
|
let out: EntradaNumero["value"] = null;
|
||||||
|
if (textoFiltrado) {
|
||||||
|
const parcial = isInteiro.value ? null : valorParcialDecimal(textoFiltrado);
|
||||||
|
const n = parcial ?? parseNumero(textoFiltrado);
|
||||||
|
out = (n === null ? null : n) as EntradaNumero["value"];
|
||||||
|
}
|
||||||
|
|
||||||
|
ultimoEmitido.value = out;
|
||||||
|
|
||||||
|
emit("update:value", out);
|
||||||
|
emit("input", out);
|
||||||
|
emit("change", out);
|
||||||
|
|
||||||
|
// display deve sempre corresponder ao value final
|
||||||
|
displayValue.value = formatarNumero(out as any, casasDecimais.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { attrs, emit, displayValue, isInteiro, onUpdateModelValue };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.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>
|
||||||
57
src/componentes/EliEntrada/EliEntradaTexto.vue
Normal file
57
src/componentes/EliEntrada/EliEntradaTexto.vue
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<v-text-field
|
||||||
|
v-model="localValue"
|
||||||
|
:label="opcoes?.rotulo"
|
||||||
|
:placeholder="opcoes?.placeholder"
|
||||||
|
:counter="opcoes?.limiteCaracteres"
|
||||||
|
:maxlength="opcoes?.limiteCaracteres"
|
||||||
|
v-bind="attrs"
|
||||||
|
@focus="() => emit('focus')"
|
||||||
|
@blur="() => emit('blur')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from "vue";
|
||||||
|
import type { PadroesEntradas } from "./tiposEntradas";
|
||||||
|
|
||||||
|
type EntradaTexto = PadroesEntradas["texto"];
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliEntradaTexto",
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
/** Interface padrão (EliEntrada): value + opcoes. */
|
||||||
|
value: {
|
||||||
|
type: [String, null] as unknown as PropType<EntradaTexto["value"]>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
opcoes: {
|
||||||
|
type: Object as PropType<EntradaTexto["opcoes"]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
"update:value": (_v: EntradaTexto["value"]) => true,
|
||||||
|
/** Compat Vue2 (v-model padrão: value + input) */
|
||||||
|
input: (_v: EntradaTexto["value"]) => true,
|
||||||
|
change: (_v: EntradaTexto["value"]) => true,
|
||||||
|
focus: () => true,
|
||||||
|
blur: () => true,
|
||||||
|
},
|
||||||
|
setup(props, { attrs, emit }) {
|
||||||
|
const localValue = computed<EntradaTexto["value"]>({
|
||||||
|
get: () => props.value,
|
||||||
|
set: (v) => {
|
||||||
|
emit("update:value", v);
|
||||||
|
emit("input", v);
|
||||||
|
emit("change", v);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { attrs, emit, localValue };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
5
src/componentes/EliEntrada/index.ts
Normal file
5
src/componentes/EliEntrada/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import EliEntradaTexto from "./EliEntradaTexto.vue";
|
||||||
|
import EliEntradaNumero from "./EliEntradaNumero.vue";
|
||||||
|
|
||||||
|
export { EliEntradaTexto, EliEntradaNumero };
|
||||||
|
export type { PadroesEntradas, TipoEntrada } from "./tiposEntradas";
|
||||||
11
src/componentes/EliEntrada/registryEliEntradas.ts
Normal file
11
src/componentes/EliEntrada/registryEliEntradas.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import type { Component } from "vue";
|
||||||
|
|
||||||
|
import EliEntradaTexto from "./EliEntradaTexto.vue";
|
||||||
|
import EliEntradaNumero from "./EliEntradaNumero.vue";
|
||||||
|
|
||||||
|
import type { TipoEntrada } from "./tiposEntradas";
|
||||||
|
|
||||||
|
export const registryTabelaCelulas = {
|
||||||
|
texto: EliEntradaTexto,
|
||||||
|
numero: EliEntradaNumero,
|
||||||
|
} as const satisfies Record<TipoEntrada, Component>;
|
||||||
81
src/componentes/EliEntrada/tiposEntradas.ts
Normal file
81
src/componentes/EliEntrada/tiposEntradas.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Tipos base para componentes de entrada (EliEntrada*)
|
||||||
|
*
|
||||||
|
* Objetivo:
|
||||||
|
* - Padronizar o shape de dados de todos os componentes de entrada.
|
||||||
|
* - Cada entrada possui sempre:
|
||||||
|
* 1) `value`: o valor atual (tipado)
|
||||||
|
* 2) `opcoes`: configuração do componente (rótulo, placeholder e extras por tipo)
|
||||||
|
*
|
||||||
|
* Como usar:
|
||||||
|
* - `PadroesEntradas[tipo]` retorna a tipagem completa (value + opcoes) daquele tipo.
|
||||||
|
* - `TipoEntrada` é a união com todos os tipos suportados (ex.: "texto" | "numero").
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contrato padrão de uma entrada.
|
||||||
|
*
|
||||||
|
* @typeParam T - tipo do `value` (ex.: string | null | undefined)
|
||||||
|
* @typeParam Mais - campos adicionais dentro de `opcoes`, específicos do tipo de entrada
|
||||||
|
*/
|
||||||
|
export type tipoPadraoEntrada<T, Mais extends Record<string, unknown> = {}> = {
|
||||||
|
/** Valor atual do campo (pode aceitar null/undefined quando aplicável) */
|
||||||
|
value: T
|
||||||
|
|
||||||
|
/** Configurações do componente (visuais + regras simples do tipo) */
|
||||||
|
opcoes: {
|
||||||
|
/** Rótulo exibido ao usuário */
|
||||||
|
rotulo: string
|
||||||
|
|
||||||
|
/** Texto de ajuda dentro do input quando vazio */
|
||||||
|
placeholder?: string
|
||||||
|
} & Mais
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapa de tipos de entrada suportados e suas configurações específicas.
|
||||||
|
*
|
||||||
|
* Observação importante:
|
||||||
|
* - As chaves deste objeto (ex.: "texto", "numero") viram o tipo `TipoEntrada`.
|
||||||
|
* - Cada item define:
|
||||||
|
* - `value`: tipo do valor
|
||||||
|
* - `opcoes`: opções comuns + extras específicas
|
||||||
|
*/
|
||||||
|
export type PadroesEntradas = {
|
||||||
|
texto: tipoPadraoEntrada<
|
||||||
|
string | null | undefined,
|
||||||
|
{
|
||||||
|
/** Limite máximo de caracteres permitidos (se definido) */
|
||||||
|
limiteCaracteres?: number
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
numero: tipoPadraoEntrada<
|
||||||
|
number | null | undefined,
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/** Unidade de medida (ex.: "kg", "m³") */
|
||||||
|
sufixo?: string
|
||||||
|
|
||||||
|
/** Moéda (ex.: "R$") */
|
||||||
|
prefixo?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passo/precisão do valor numérico.
|
||||||
|
* - 1 => somente inteiros
|
||||||
|
* - 0.1 => 1 casa decimal
|
||||||
|
* - 0.01 => 2 casas decimais
|
||||||
|
*
|
||||||
|
* Dica: este conceito corresponde ao atributo HTML `step`.
|
||||||
|
*/
|
||||||
|
precisao?: number
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* União dos tipos de entrada suportados.
|
||||||
|
* Ex.: "texto" | "numero"
|
||||||
|
*/
|
||||||
|
export type TipoEntrada = keyof PadroesEntradas
|
||||||
|
|
@ -7,6 +7,7 @@ import { EliInput } from "./componentes/campo";
|
||||||
import { EliCartao } from "./componentes/cartao";
|
import { EliCartao } from "./componentes/cartao";
|
||||||
import { EliDataHora } from "./componentes/data_hora";
|
import { EliDataHora } from "./componentes/data_hora";
|
||||||
import { EliTabela } from "./componentes/EliTabela";
|
import { EliTabela } from "./componentes/EliTabela";
|
||||||
|
import { EliEntradaTexto, EliEntradaNumero } from "./componentes/EliEntrada";
|
||||||
|
|
||||||
export { EliOlaMundo };
|
export { EliOlaMundo };
|
||||||
export { EliBotao };
|
export { EliBotao };
|
||||||
|
|
@ -15,6 +16,7 @@ export { EliInput };
|
||||||
export { EliCartao };
|
export { EliCartao };
|
||||||
export { EliDataHora };
|
export { EliDataHora };
|
||||||
export { EliTabela };
|
export { EliTabela };
|
||||||
|
export { EliEntradaTexto, EliEntradaNumero };
|
||||||
|
|
||||||
const EliVue: Plugin = {
|
const EliVue: Plugin = {
|
||||||
install(app: App) {
|
install(app: App) {
|
||||||
|
|
@ -25,6 +27,8 @@ const EliVue: Plugin = {
|
||||||
app.component("EliCartao", EliCartao);
|
app.component("EliCartao", EliCartao);
|
||||||
app.component("EliDataHora", EliDataHora);
|
app.component("EliDataHora", EliDataHora);
|
||||||
app.component("EliTabela", EliTabela);
|
app.component("EliTabela", EliTabela);
|
||||||
|
app.component("EliEntradaTexto", EliEntradaTexto);
|
||||||
|
app.component("EliEntradaNumero", EliEntradaNumero);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
<v-tab value="indicador">Indicador</v-tab>
|
<v-tab value="indicador">Indicador</v-tab>
|
||||||
<v-tab value="cartao">Cartão</v-tab>
|
<v-tab value="cartao">Cartão</v-tab>
|
||||||
<v-tab value="campo">Campo</v-tab>
|
<v-tab value="campo">Campo</v-tab>
|
||||||
|
<v-tab value="entradas">Entradas</v-tab>
|
||||||
<v-tab value="data_hora">Data e hora</v-tab>
|
<v-tab value="data_hora">Data e hora</v-tab>
|
||||||
<v-tab value="tabela">Tabela</v-tab>
|
<v-tab value="tabela">Tabela</v-tab>
|
||||||
<v-tab value="ola_mundo">Demo</v-tab>
|
<v-tab value="ola_mundo">Demo</v-tab>
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
<IndicadorPlayground v-else-if="aba === 'indicador'" />
|
<IndicadorPlayground v-else-if="aba === 'indicador'" />
|
||||||
<CartaoPlayground v-else-if="aba === 'cartao'" />
|
<CartaoPlayground v-else-if="aba === 'cartao'" />
|
||||||
<CampoPlayground v-else-if="aba === 'campo'" />
|
<CampoPlayground v-else-if="aba === 'campo'" />
|
||||||
|
<EntradasPlayground v-else-if="aba === 'entradas'" />
|
||||||
<DataHoraPlayground v-else-if="aba === 'data_hora'" />
|
<DataHoraPlayground v-else-if="aba === 'data_hora'" />
|
||||||
<TabelaPlayground v-else-if="aba === 'tabela'" />
|
<TabelaPlayground v-else-if="aba === 'tabela'" />
|
||||||
<OlaMundoPlayground v-else />
|
<OlaMundoPlayground v-else />
|
||||||
|
|
@ -32,6 +34,7 @@ import BotaoPlayground from "./botao.playground.vue";
|
||||||
import IndicadorPlayground from "./indicador.playground.vue";
|
import IndicadorPlayground from "./indicador.playground.vue";
|
||||||
import CartaoPlayground from "./cartao.playground.vue";
|
import CartaoPlayground from "./cartao.playground.vue";
|
||||||
import CampoPlayground from "./campo.playground.vue";
|
import CampoPlayground from "./campo.playground.vue";
|
||||||
|
import EntradasPlayground from "./entradas.playground.vue";
|
||||||
import DataHoraPlayground from "./data_hora.playground.vue";
|
import DataHoraPlayground from "./data_hora.playground.vue";
|
||||||
import TabelaPlayground from "./tabela.playground.vue";
|
import TabelaPlayground from "./tabela.playground.vue";
|
||||||
import OlaMundoPlayground from "./ola_mundo.playground.vue";
|
import OlaMundoPlayground from "./ola_mundo.playground.vue";
|
||||||
|
|
@ -41,6 +44,7 @@ type AbaPlayground =
|
||||||
| "indicador"
|
| "indicador"
|
||||||
| "cartao"
|
| "cartao"
|
||||||
| "campo"
|
| "campo"
|
||||||
|
| "entradas"
|
||||||
| "data_hora"
|
| "data_hora"
|
||||||
| "tabela"
|
| "tabela"
|
||||||
| "ola_mundo";
|
| "ola_mundo";
|
||||||
|
|
@ -50,6 +54,7 @@ const mapaHashParaAba: Record<string, AbaPlayground> = {
|
||||||
indicador: "indicador",
|
indicador: "indicador",
|
||||||
cartao: "cartao",
|
cartao: "cartao",
|
||||||
campo: "campo",
|
campo: "campo",
|
||||||
|
entradas: "entradas",
|
||||||
"data-hora": "data_hora",
|
"data-hora": "data_hora",
|
||||||
tabela: "tabela",
|
tabela: "tabela",
|
||||||
demo: "ola_mundo",
|
demo: "ola_mundo",
|
||||||
|
|
@ -60,6 +65,7 @@ const mapaAbaParaHash: Record<AbaPlayground, string> = {
|
||||||
indicador: "indicador",
|
indicador: "indicador",
|
||||||
cartao: "cartao",
|
cartao: "cartao",
|
||||||
campo: "campo",
|
campo: "campo",
|
||||||
|
entradas: "entradas",
|
||||||
data_hora: "data-hora",
|
data_hora: "data-hora",
|
||||||
tabela: "tabela",
|
tabela: "tabela",
|
||||||
ola_mundo: "demo",
|
ola_mundo: "demo",
|
||||||
|
|
@ -72,6 +78,7 @@ export default defineComponent({
|
||||||
IndicadorPlayground,
|
IndicadorPlayground,
|
||||||
CartaoPlayground,
|
CartaoPlayground,
|
||||||
CampoPlayground,
|
CampoPlayground,
|
||||||
|
EntradasPlayground,
|
||||||
DataHoraPlayground,
|
DataHoraPlayground,
|
||||||
TabelaPlayground,
|
TabelaPlayground,
|
||||||
OlaMundoPlayground,
|
OlaMundoPlayground,
|
||||||
|
|
|
||||||
88
src/playground/entradas.playground.vue
Normal file
88
src/playground/entradas.playground.vue
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<div class="d-flex flex-column ga-6">
|
||||||
|
<section>
|
||||||
|
<h2 class="text-h6 mb-2">EliEntradas</h2>
|
||||||
|
<p class="text-body-2 mb-0">
|
||||||
|
Exemplos para <code>EliEntradaTexto</code> e <code>EliEntradaNumero</code>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<v-divider />
|
||||||
|
|
||||||
|
<section class="d-flex flex-column ga-3">
|
||||||
|
<h3 class="text-subtitle-1">Texto</h3>
|
||||||
|
|
||||||
|
<EliEntradaTexto
|
||||||
|
v-model:value="texto"
|
||||||
|
:opcoes="{
|
||||||
|
rotulo: 'Nome',
|
||||||
|
placeholder: 'Digite seu nome',
|
||||||
|
limiteCaracteres: 20,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-caption">
|
||||||
|
<strong>v-model:value</strong>: {{ texto ?? "(null)" }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-alert type="info" variant="tonal" density="comfortable">
|
||||||
|
Para EliEntradas, o padrão é <code>v-model:value</code> (prop <code>value</code> + evento
|
||||||
|
<code>update:value</code>). Mantemos também o evento <code>input</code> para compat Vue2.
|
||||||
|
</v-alert>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<v-divider />
|
||||||
|
|
||||||
|
<section class="d-flex flex-column ga-3">
|
||||||
|
<h3 class="text-subtitle-1">Número</h3>
|
||||||
|
|
||||||
|
<EliEntradaNumero
|
||||||
|
v-model:value="numero"
|
||||||
|
:opcoes="{
|
||||||
|
rotulo: 'Quantidade',
|
||||||
|
placeholder: 'Ex: 10',
|
||||||
|
precisao: 1,
|
||||||
|
sufixo: 'kg',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-caption">
|
||||||
|
<strong>v-model:value</strong>: {{ numero ?? "(null)" }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EliEntradaNumero
|
||||||
|
v-model:value="numeroDecimal"
|
||||||
|
:opcoes="{
|
||||||
|
rotulo: 'Moeda',
|
||||||
|
placeholder: 'Digite (2 casas)',
|
||||||
|
precisao: 0.01,
|
||||||
|
prefixo: 'R$',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-caption">
|
||||||
|
<strong>v-model:value</strong>: {{ numeroDecimal ?? "(null)" }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { EliEntradaNumero, EliEntradaTexto } from "../index";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EntradasPlayground",
|
||||||
|
components: {
|
||||||
|
EliEntradaTexto,
|
||||||
|
EliEntradaNumero,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
texto: null as string | null,
|
||||||
|
numero: null as number | null,
|
||||||
|
numeroDecimal: null as number | null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -26,6 +26,6 @@
|
||||||
/* Vue */
|
/* Vue */
|
||||||
"types": ["vite/client"]
|
"types": ["vite/client"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"],
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "src/componentes/EliEntrada"],
|
||||||
"exclude": ["dist", "node_modules"]
|
"exclude": ["dist", "node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue