Compare commits
3 commits
a9bac68e1b
...
dbf08a312d
| Author | SHA1 | Date | |
|---|---|---|---|
| dbf08a312d | |||
| 8c1d26f968 | |||
| 2763da4108 |
20 changed files with 4856 additions and 4320 deletions
8308
dist/eli-vue.es.js
vendored
8308
dist/eli-vue.es.js
vendored
File diff suppressed because one or more lines are too long
118
dist/eli-vue.umd.js
vendored
118
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -11,3 +11,14 @@ export declare function gerarCelulas(referencia: dayjs.Dayjs, diaSelecionado: da
|
||||||
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
|
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
|
||||||
*/
|
*/
|
||||||
export declare function formatarValor(valor: string | null, modo: EntradaDataHora["opcoes"]["modo"]): string;
|
export declare function formatarValor(valor: string | null, modo: EntradaDataHora["opcoes"]["modo"]): string;
|
||||||
|
/**
|
||||||
|
* Aplica máscara visual de data/hora (DD/MM/YYYY ou DD/MM/YYYY HH:mm) em texto digitado.
|
||||||
|
*/
|
||||||
|
export declare function aplicarMascaraDataHora(valor: string, modo: "data" | "dataHora"): string;
|
||||||
|
/**
|
||||||
|
* Converte um texto mascarado (ex: "15/08/2026 14:30") em string ISO 8601 com fuso local.
|
||||||
|
*/
|
||||||
|
export declare function converterTextoParaIso(texto: string, modo: "data" | "dataHora", horaMinutoPadrao?: {
|
||||||
|
hora: string;
|
||||||
|
minuto: string;
|
||||||
|
}): string | null;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import dayjs from "dayjs";
|
||||||
import "dayjs/locale/pt-br";
|
import "dayjs/locale/pt-br";
|
||||||
import type { CampoDensidade, CampoVariante } from "../../../tipos";
|
import type { CampoDensidade, CampoVariante } from "../../../tipos";
|
||||||
import type { PadroesEntradas } from "../tiposEntradas";
|
import type { PadroesEntradas } from "../tiposEntradas";
|
||||||
|
import { diasSemana, MESES } from "./useDataHoraProps";
|
||||||
|
export { diasSemana, MESES };
|
||||||
export type EntradaDataHora = PadroesEntradas["dataHora"];
|
export type EntradaDataHora = PadroesEntradas["dataHora"];
|
||||||
export interface CelulaCalendario {
|
export interface CelulaCalendario {
|
||||||
dia: number;
|
dia: number;
|
||||||
|
|
@ -11,8 +13,6 @@ export interface CelulaCalendario {
|
||||||
selecionado: boolean;
|
selecionado: boolean;
|
||||||
desabilitado: boolean;
|
desabilitado: boolean;
|
||||||
}
|
}
|
||||||
export declare const MESES: string[];
|
|
||||||
export declare const diasSemana: string[];
|
|
||||||
/** Função de emit genérica do Vue (compatível com defineComponent) */
|
/** Função de emit genérica do Vue (compatível com defineComponent) */
|
||||||
type EmitFn = (event: string, payload?: unknown) => void;
|
type EmitFn = (event: string, payload?: unknown) => void;
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -37,6 +37,8 @@ type Props = {
|
||||||
export declare function useDataHora(props: Props, emit: EmitFn): {
|
export declare function useDataHora(props: Props, emit: EmitFn): {
|
||||||
uid: number;
|
uid: number;
|
||||||
aberto: import("vue").Ref<boolean, boolean>;
|
aberto: import("vue").Ref<boolean, boolean>;
|
||||||
|
focado: import("vue").Ref<boolean, boolean>;
|
||||||
|
textoDigitado: import("vue").Ref<string, string>;
|
||||||
estiloPopover: import("vue").Ref<Record<string, string>, Record<string, string>>;
|
estiloPopover: import("vue").Ref<Record<string, string>, Record<string, string>>;
|
||||||
referencia: import("vue").Ref<{
|
referencia: import("vue").Ref<{
|
||||||
clone: () => dayjs.Dayjs;
|
clone: () => dayjs.Dayjs;
|
||||||
|
|
@ -399,10 +401,15 @@ export declare function useDataHora(props: Props, emit: EmitFn): {
|
||||||
mudarAno: (ano: number) => void;
|
mudarAno: (ano: number) => void;
|
||||||
celulas: import("vue").ComputedRef<CelulaCalendario[]>;
|
celulas: import("vue").ComputedRef<CelulaCalendario[]>;
|
||||||
abrirSeHabilitado: () => void;
|
abrirSeHabilitado: () => void;
|
||||||
|
alternarPopover: () => void;
|
||||||
fechar: () => void;
|
fechar: () => void;
|
||||||
navegarMes: (delta: number) => void;
|
navegarMes: (delta: number) => void;
|
||||||
selecionarDia: (cell: CelulaCalendario) => void;
|
selecionarDia: (cell: CelulaCalendario) => void;
|
||||||
confirmar: () => void;
|
confirmar: () => void;
|
||||||
limpar: () => void;
|
limpar: () => void;
|
||||||
|
onTextInput: (e: Event) => void;
|
||||||
|
onInputFocus: () => void;
|
||||||
|
onInputBlur: () => void;
|
||||||
|
atualizarHoraTemp: (v: string) => void;
|
||||||
|
atualizarMinutoTemp: (v: string) => void;
|
||||||
};
|
};
|
||||||
export {};
|
|
||||||
|
|
|
||||||
20
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraInput.d.ts
vendored
Normal file
20
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraInput.d.ts
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { type Ref } from "vue";
|
||||||
|
interface UseDataHoraInputOptions {
|
||||||
|
modoEfetivo: Ref<"data" | "dataHora">;
|
||||||
|
valor: Ref<string | null>;
|
||||||
|
horaTemp: Ref<string>;
|
||||||
|
minutoTemp: Ref<string>;
|
||||||
|
emitirValor: (iso: string | null) => void;
|
||||||
|
emit: (event: string, payload?: unknown) => void;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Composable responsável pelo controle do campo de texto editável com máscara e eventos de foco/blur.
|
||||||
|
*/
|
||||||
|
export declare function useDataHoraInput({ modoEfetivo, valor, horaTemp, minutoTemp, emitirValor, emit, }: UseDataHoraInputOptions): {
|
||||||
|
textoDigitado: Ref<string, string>;
|
||||||
|
focado: Ref<boolean, boolean>;
|
||||||
|
onTextInput: (e: Event) => void;
|
||||||
|
onInputFocus: () => void;
|
||||||
|
onInputBlur: () => void;
|
||||||
|
};
|
||||||
|
export {};
|
||||||
15
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraPosicao.d.ts
vendored
Normal file
15
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraPosicao.d.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { type Ref } from "vue";
|
||||||
|
interface UseDataHoraPosicaoOptions {
|
||||||
|
uid: number;
|
||||||
|
modoEfetivo: Ref<"data" | "dataHora">;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Composable responsável por calcular a posição fixed do popover do calendário.
|
||||||
|
*/
|
||||||
|
export declare function useDataHoraPosicao({ uid, modoEfetivo }: UseDataHoraPosicaoOptions): {
|
||||||
|
estiloPopover: Ref<Record<string, string>, Record<string, string>>;
|
||||||
|
calcularPosicao: () => void;
|
||||||
|
adicionarListenersPosicao: () => void;
|
||||||
|
removerListenersPosicao: () => void;
|
||||||
|
};
|
||||||
|
export {};
|
||||||
59
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraProps.d.ts
vendored
Normal file
59
dist/types/componentes/EliEntrada/EliEntradaDataHora/useDataHoraProps.d.ts
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { type Ref } from "vue";
|
||||||
|
import type { EntradaDataHora } from "./useDataHora";
|
||||||
|
export declare const MESES: string[];
|
||||||
|
export declare const diasSemana: string[];
|
||||||
|
export interface UseDataHoraPropsOptions {
|
||||||
|
props: {
|
||||||
|
value?: string | null;
|
||||||
|
opcoes?: EntradaDataHora["opcoes"];
|
||||||
|
modelValue?: string | null;
|
||||||
|
modo?: "data" | "dataHora";
|
||||||
|
rotulo?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
desabilitado?: boolean;
|
||||||
|
limpavel?: boolean;
|
||||||
|
erro?: boolean;
|
||||||
|
mensagensErro?: string | string[];
|
||||||
|
dica?: string;
|
||||||
|
dicaPersistente?: boolean;
|
||||||
|
densidade?: EntradaDataHora["opcoes"]["densidade"];
|
||||||
|
variante?: EntradaDataHora["opcoes"]["variante"];
|
||||||
|
min?: string;
|
||||||
|
max?: string;
|
||||||
|
};
|
||||||
|
referencia: Ref<dayjs.Dayjs>;
|
||||||
|
diaSelecionado: Ref<dayjs.Dayjs | null>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Composable responsável pelo cálculo de propriedades derivadas e opções do EliEntradaDataHora.
|
||||||
|
*/
|
||||||
|
export declare function useDataHoraProps({ props, referencia, diaSelecionado }: UseDataHoraPropsOptions): {
|
||||||
|
opcoesEfetivas: import("vue").ComputedRef<{
|
||||||
|
rotulo: string;
|
||||||
|
placeholder?: string;
|
||||||
|
apenasLeitura?: boolean;
|
||||||
|
} & {
|
||||||
|
modo?: "data" | "dataHora";
|
||||||
|
limpavel?: boolean;
|
||||||
|
erro?: boolean;
|
||||||
|
mensagensErro?: string | string[];
|
||||||
|
dica?: string;
|
||||||
|
dicaPersistente?: boolean;
|
||||||
|
min?: string;
|
||||||
|
max?: string;
|
||||||
|
densidade?: import("../../..").CampoDensidade;
|
||||||
|
variante?: import("../../..").CampoVariante;
|
||||||
|
}>;
|
||||||
|
modoEfetivo: import("vue").ComputedRef<"data" | "dataHora">;
|
||||||
|
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
|
||||||
|
effectiveModelValue: import("vue").ComputedRef<string | null>;
|
||||||
|
valor: import("vue").ComputedRef<string | null>;
|
||||||
|
mensagensVisiveis: import("vue").ComputedRef<string[]>;
|
||||||
|
valorFormatado: import("vue").ComputedRef<string>;
|
||||||
|
nomeMesAtual: import("vue").ComputedRef<string>;
|
||||||
|
anoAtual: import("vue").ComputedRef<number>;
|
||||||
|
mesAtual: import("vue").ComputedRef<number>;
|
||||||
|
listaAnos: import("vue").ComputedRef<number[]>;
|
||||||
|
celulas: import("vue").ComputedRef<import("./useDataHora").CelulaCalendario[]>;
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "eli-vue",
|
"name": "eli-vue",
|
||||||
"version": "0.1.227",
|
"version": "0.1.230",
|
||||||
"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",
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
"@vitejs/plugin-vue": "^5.0.0",
|
"@vitejs/plugin-vue": "^5.0.0",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"sass": "^1.94.2",
|
"sass": "^1.94.2",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.2",
|
"vite-plugin-css-injected-by-js": "^3.5.2",
|
||||||
"vite-plugin-vuetify": "^2.1.2",
|
"vite-plugin-vuetify": "^2.1.2",
|
||||||
|
|
|
||||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
|
|
@ -52,7 +52,7 @@ importers:
|
||||||
specifier: ^1.94.2
|
specifier: ^1.94.2
|
||||||
version: 1.102.0
|
version: 1.102.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^7.0.2
|
specifier: ^7
|
||||||
version: 7.0.2
|
version: 7.0.2
|
||||||
vite:
|
vite:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,8 @@
|
||||||
───────────────────────────────────────────── */
|
───────────────────────────────────────────── */
|
||||||
|
|
||||||
.eli-dh__popover {
|
.eli-dh__popover {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: calc(100% + 6px);
|
z-index: 99999;
|
||||||
left: 0;
|
|
||||||
z-index: 2000;
|
|
||||||
background: var(--cor-eli-superficie, #fff);
|
background: var(--cor-eli-superficie, #fff);
|
||||||
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
|
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@
|
||||||
:aria-selected="cell.selecionado"
|
:aria-selected="cell.selecionado"
|
||||||
:aria-label="`${cell.dia} de ${nomeMesAtual} de ${anoAtual}`"
|
:aria-label="`${cell.dia} de ${nomeMesAtual} de ${anoAtual}`"
|
||||||
@click="$emit('selecionarDia', cell)"
|
@click="$emit('selecionarDia', cell)"
|
||||||
|
@dblclick="$emit('confirmar')"
|
||||||
>
|
>
|
||||||
{{ cell.dia }}
|
{{ cell.dia }}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -91,6 +92,8 @@
|
||||||
@input="onHoraInput"
|
@input="onHoraInput"
|
||||||
@blur="onHoraBlur"
|
@blur="onHoraBlur"
|
||||||
@keydown="onHoraKeydown"
|
@keydown="onHoraKeydown"
|
||||||
|
@focus="onInputFocusSelect"
|
||||||
|
@click.stop
|
||||||
/>
|
/>
|
||||||
<span class="eli-dh__hora-sep" aria-hidden="true">:</span>
|
<span class="eli-dh__hora-sep" aria-hidden="true">:</span>
|
||||||
<input
|
<input
|
||||||
|
|
@ -105,6 +108,8 @@
|
||||||
@input="onMinutoInput"
|
@input="onMinutoInput"
|
||||||
@blur="onMinutoBlur"
|
@blur="onMinutoBlur"
|
||||||
@keydown="onMinutoKeydown"
|
@keydown="onMinutoKeydown"
|
||||||
|
@focus="onInputFocusSelect"
|
||||||
|
@click.stop
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -214,6 +219,10 @@ export default defineComponent({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onInputFocusSelect(e: Event) {
|
||||||
|
;(e.target as HTMLInputElement).select()
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
diasSemana,
|
diasSemana,
|
||||||
MESES,
|
MESES,
|
||||||
|
|
@ -225,6 +234,7 @@ export default defineComponent({
|
||||||
onMinutoInput,
|
onMinutoInput,
|
||||||
onMinutoBlur,
|
onMinutoBlur,
|
||||||
onMinutoKeydown,
|
onMinutoKeydown,
|
||||||
|
onInputFocusSelect,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -44,18 +44,29 @@
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__icone {
|
.eli-dh__icone-btn {
|
||||||
flex-shrink: 0;
|
display: flex;
|
||||||
width: 20px;
|
align-items: center;
|
||||||
height: 20px;
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
color: var(--cor-eli-neutro, #5E6553);
|
color: var(--cor-eli-neutro, #5E6553);
|
||||||
transition: color 0.18s;
|
transition: color 0.18s;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__campo--focado .eli-dh__icone {
|
.eli-dh__icone-btn:hover:not(:disabled) {
|
||||||
color: var(--cor-eli-primaria, #688C00);
|
color: var(--cor-eli-primaria, #688C00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.eli-dh__icone {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.eli-dh__conteudo {
|
.eli-dh__conteudo {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -72,23 +83,35 @@
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
transition: color 0.18s;
|
transition: color 0.18s;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__campo--focado .eli-dh__rotulo { color: var(--cor-eli-primaria, #688C00); }
|
.eli-dh__campo--focado .eli-dh__rotulo { color: var(--cor-eli-primaria, #688C00); }
|
||||||
.eli-dh__campo--erro .eli-dh__rotulo { color: var(--cor-eli-erro, #C62828); }
|
.eli-dh__campo--erro .eli-dh__rotulo { color: var(--cor-eli-erro, #C62828); }
|
||||||
|
|
||||||
.eli-dh__valor {
|
.eli-dh__input {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
outline: none;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
color: var(--cor-eli-neutro-escuro, #3E4436);
|
color: var(--cor-eli-neutro-escuro, #3E4436);
|
||||||
white-space: nowrap;
|
padding: 0;
|
||||||
overflow: hidden;
|
margin: 0;
|
||||||
text-overflow: ellipsis;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__valor--vazio {
|
.eli-dh__input::placeholder {
|
||||||
color: var(--cor-eli-borda-forte, #B5BAA8);
|
color: var(--cor-eli-borda-forte, #B5BAA8);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-dh__input:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: var(--cor-eli-neutro, #5E6553);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__limpar {
|
.eli-dh__limpar {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,71 @@ export function formatarValor(
|
||||||
modo: EntradaDataHora["opcoes"]["modo"],
|
modo: EntradaDataHora["opcoes"]["modo"],
|
||||||
): string {
|
): string {
|
||||||
if (!valor) return ""
|
if (!valor) return ""
|
||||||
return modo === "data"
|
const d = dayjs(valor)
|
||||||
? dayjs(valor).format("DD/MM/YYYY")
|
if (!d.isValid()) return ""
|
||||||
: dayjs(valor).format("DD/MM/YYYY HH:mm")
|
return modo === "data" ? d.format("DD/MM/YYYY") : d.format("DD/MM/YYYY HH:mm")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aplica máscara visual de data/hora (DD/MM/YYYY ou DD/MM/YYYY HH:mm) em texto digitado.
|
||||||
|
*/
|
||||||
|
export function aplicarMascaraDataHora(valor: string, modo: "data" | "dataHora"): string {
|
||||||
|
const digitos = valor.replace(/\D/g, "")
|
||||||
|
if (!digitos) return ""
|
||||||
|
|
||||||
|
const dia = digitos.slice(0, 2)
|
||||||
|
const mes = digitos.slice(2, 4)
|
||||||
|
const ano = digitos.slice(4, 8)
|
||||||
|
const hora = digitos.slice(8, 10)
|
||||||
|
const minuto = digitos.slice(10, 12)
|
||||||
|
|
||||||
|
let res = dia
|
||||||
|
if (mes) res += `/${mes}`
|
||||||
|
if (ano) res += `/${ano}`
|
||||||
|
|
||||||
|
if (modo === "dataHora") {
|
||||||
|
if (hora) res += ` ${hora}`
|
||||||
|
if (minuto) res += `:${minuto}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converte um texto mascarado (ex: "15/08/2026 14:30") em string ISO 8601 com fuso local.
|
||||||
|
*/
|
||||||
|
export function converterTextoParaIso(
|
||||||
|
texto: string,
|
||||||
|
modo: "data" | "dataHora",
|
||||||
|
horaMinutoPadrao?: { hora: string; minuto: string },
|
||||||
|
): string | null {
|
||||||
|
const digitos = texto.replace(/\D/g, "")
|
||||||
|
if (digitos.length < 8) return null
|
||||||
|
|
||||||
|
const d = Number.parseInt(digitos.slice(0, 2), 10)
|
||||||
|
const m = Number.parseInt(digitos.slice(2, 4), 10)
|
||||||
|
const y = Number.parseInt(digitos.slice(4, 8), 10)
|
||||||
|
|
||||||
|
if (m < 1 || m > 12 || d < 1 || d > 31 || y < 1900 || y > 2100) return null
|
||||||
|
|
||||||
|
let h = 0
|
||||||
|
let min = 0
|
||||||
|
|
||||||
|
if (modo === "dataHora") {
|
||||||
|
if (digitos.length >= 10) {
|
||||||
|
h = Math.min(23, Number.parseInt(digitos.slice(8, 10), 10) || 0)
|
||||||
|
} else if (horaMinutoPadrao) {
|
||||||
|
h = Number.parseInt(horaMinutoPadrao.hora, 10) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digitos.length >= 12) {
|
||||||
|
min = Math.min(59, Number.parseInt(digitos.slice(10, 12), 10) || 0)
|
||||||
|
} else if (horaMinutoPadrao) {
|
||||||
|
min = Number.parseInt(horaMinutoPadrao.minuto, 10) || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const strIso = `${y}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}T${String(h).padStart(2, "0")}:${String(min).padStart(2, "0")}:00`
|
||||||
|
const dt = dayjs(strIso)
|
||||||
|
return dt.isValid() ? dt.format() : null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,30 +5,49 @@
|
||||||
:id="`eli-dh-campo-${uid}`"
|
:id="`eli-dh-campo-${uid}`"
|
||||||
class="eli-dh__campo"
|
class="eli-dh__campo"
|
||||||
:class="{
|
:class="{
|
||||||
'eli-dh__campo--focado': aberto,
|
'eli-dh__campo--focado': aberto || focado,
|
||||||
'eli-dh__campo--erro': Boolean(opcoesEfetivas.erro),
|
'eli-dh__campo--erro': Boolean(opcoesEfetivas.erro),
|
||||||
'eli-dh__campo--desabilitado': desabilitadoEfetivo,
|
'eli-dh__campo--desabilitado': desabilitadoEfetivo,
|
||||||
}"
|
}"
|
||||||
role="button"
|
|
||||||
:tabindex="desabilitadoEfetivo ? -1 : 0"
|
|
||||||
:aria-haspopup="true"
|
|
||||||
:aria-expanded="aberto"
|
|
||||||
:aria-label="opcoesEfetivas.rotulo"
|
|
||||||
@click="abrirSeHabilitado"
|
@click="abrirSeHabilitado"
|
||||||
@keydown.enter.prevent="abrirSeHabilitado"
|
>
|
||||||
@keydown.space.prevent="abrirSeHabilitado"
|
<button
|
||||||
@keydown.escape="fechar"
|
class="eli-dh__icone-btn"
|
||||||
|
type="button"
|
||||||
|
tabindex="-1"
|
||||||
|
:title="`Abrir calendário (${opcoesEfetivas.rotulo})`"
|
||||||
|
:disabled="desabilitadoEfetivo"
|
||||||
|
@click.stop="alternarPopover"
|
||||||
>
|
>
|
||||||
<svg class="eli-dh__icone" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
<svg class="eli-dh__icone" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||||
<rect x="3" y="4" width="18" height="18" rx="3" ry="3" />
|
<rect x="3" y="4" width="18" height="18" rx="3" ry="3" />
|
||||||
<line x1="16" y1="2" x2="16" y2="6" /><line x1="8" y1="2" x2="8" y2="6" /><line x1="3" y1="10" x2="21" y2="10" />
|
<line x1="16" y1="2" x2="16" y2="6" /><line x1="8" y1="2" x2="8" y2="6" /><line x1="3" y1="10" x2="21" y2="10" />
|
||||||
</svg>
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="eli-dh__conteudo">
|
<div class="eli-dh__conteudo">
|
||||||
<span class="eli-dh__rotulo">{{ opcoesEfetivas.rotulo }}</span>
|
<label :for="`eli-dh-input-${uid}`" class="eli-dh__rotulo">{{ opcoesEfetivas.rotulo }}</label>
|
||||||
<span class="eli-dh__valor" :class="{ 'eli-dh__valor--vazio': !valorFormatado }">
|
<input
|
||||||
{{ valorFormatado || opcoesEfetivas.placeholder || 'Selecione...' }}
|
:id="`eli-dh-input-${uid}`"
|
||||||
</span>
|
class="eli-dh__input"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
:value="textoDigitado"
|
||||||
|
:placeholder="opcoesEfetivas.placeholder || (modoEfetivo === 'data' ? 'DD/MM/AAAA' : 'DD/MM/AAAA HH:mm')"
|
||||||
|
:disabled="desabilitadoEfetivo"
|
||||||
|
:readonly="Boolean(opcoesEfetivas.apenasLeitura)"
|
||||||
|
:aria-expanded="aberto"
|
||||||
|
:aria-haspopup="true"
|
||||||
|
:aria-label="opcoesEfetivas.rotulo"
|
||||||
|
@click="abrirSeHabilitado"
|
||||||
|
@input="onTextInput"
|
||||||
|
@blur="onInputBlur"
|
||||||
|
@focus="onInputFocus"
|
||||||
|
@keydown.down.prevent="abrirSeHabilitado"
|
||||||
|
@keydown.escape="fechar"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="Boolean(opcoesEfetivas.limpavel) && valor && !desabilitadoEfetivo"
|
v-if="Boolean(opcoesEfetivas.limpavel) && valor && !desabilitadoEfetivo"
|
||||||
class="eli-dh__limpar"
|
class="eli-dh__limpar"
|
||||||
|
|
@ -47,7 +66,7 @@
|
||||||
<div v-if="mensagensVisiveis.length" class="eli-dh__mensagem eli-dh__mensagem--erro" role="alert">
|
<div v-if="mensagensVisiveis.length" class="eli-dh__mensagem eli-dh__mensagem--erro" role="alert">
|
||||||
{{ mensagensVisiveis[0] }}
|
{{ mensagensVisiveis[0] }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="opcoesEfetivas.dica && (opcoesEfetivas.dicaPersistente || aberto)" class="eli-dh__mensagem eli-dh__mensagem--dica">
|
<div v-else-if="opcoesEfetivas.dica && (opcoesEfetivas.dicaPersistente || aberto || focado)" class="eli-dh__mensagem eli-dh__mensagem--dica">
|
||||||
{{ opcoesEfetivas.dica }}
|
{{ opcoesEfetivas.dica }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -72,8 +91,8 @@
|
||||||
@selecionarDia="selecionarDia"
|
@selecionarDia="selecionarDia"
|
||||||
@mudarMes="mudarMes"
|
@mudarMes="mudarMes"
|
||||||
@mudarAno="mudarAno"
|
@mudarAno="mudarAno"
|
||||||
@update:horaTemp="horaTemp = $event"
|
@update:horaTemp="atualizarHoraTemp"
|
||||||
@update:minutoTemp="minutoTemp = $event"
|
@update:minutoTemp="atualizarMinutoTemp"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import "dayjs/locale/pt-br"
|
import "dayjs/locale/pt-br"
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
|
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
|
||||||
import type { CampoDensidade, CampoVariante } from "../../../tipos"
|
import type { CampoDensidade, CampoVariante } from "../../../tipos"
|
||||||
import type { PadroesEntradas } from "../tiposEntradas"
|
import type { PadroesEntradas } from "../tiposEntradas"
|
||||||
import { formatarValor, gerarCelulas } from "./calendarioHelpers"
|
import { formatarValor } from "./calendarioHelpers"
|
||||||
|
import { useDataHoraInput } from "./useDataHoraInput"
|
||||||
|
import { useDataHoraPosicao } from "./useDataHoraPosicao"
|
||||||
|
import { diasSemana, MESES, useDataHoraProps } from "./useDataHoraProps"
|
||||||
|
|
||||||
|
export { diasSemana, MESES }
|
||||||
|
|
||||||
dayjs.locale("pt-br")
|
dayjs.locale("pt-br")
|
||||||
|
|
||||||
|
|
@ -18,13 +23,6 @@ export interface CelulaCalendario {
|
||||||
desabilitado: boolean
|
desabilitado: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MESES = Array.from({ length: 12 }, (_, i) => {
|
|
||||||
const s = dayjs().month(i).format("MMMM")
|
|
||||||
return s.charAt(0).toUpperCase() + s.slice(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const diasSemana = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]
|
|
||||||
|
|
||||||
/** Função de emit genérica do Vue (compatível com defineComponent) */
|
/** Função de emit genérica do Vue (compatível com defineComponent) */
|
||||||
type EmitFn = (event: string, payload?: unknown) => void
|
type EmitFn = (event: string, payload?: unknown) => void
|
||||||
|
|
||||||
|
|
@ -53,113 +51,91 @@ let contadorUid = 0
|
||||||
export function useDataHora(props: Props, emit: EmitFn) {
|
export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
const uid = ++contadorUid
|
const uid = ++contadorUid
|
||||||
const aberto = ref(false)
|
const aberto = ref(false)
|
||||||
const estiloPopover = ref<Record<string, string>>({})
|
|
||||||
const referencia = ref(dayjs())
|
const referencia = ref(dayjs())
|
||||||
const diaSelecionado = ref<dayjs.Dayjs | null>(null)
|
const diaSelecionado = ref<dayjs.Dayjs | null>(null)
|
||||||
const horaTemp = ref("00")
|
const horaTemp = ref("00")
|
||||||
const minutoTemp = ref("00")
|
const minutoTemp = ref("00")
|
||||||
|
|
||||||
const opcoesEfetivas = computed<EntradaDataHora["opcoes"]>(
|
const {
|
||||||
() =>
|
opcoesEfetivas,
|
||||||
props.opcoes ?? {
|
modoEfetivo,
|
||||||
rotulo: props.rotulo ?? "Data",
|
desabilitadoEfetivo,
|
||||||
placeholder: props.placeholder ?? "",
|
effectiveModelValue,
|
||||||
modo: props.modo ?? "dataHora",
|
valor,
|
||||||
limpavel: props.limpavel,
|
mensagensVisiveis,
|
||||||
erro: props.erro,
|
valorFormatado,
|
||||||
mensagensErro: props.mensagensErro,
|
nomeMesAtual,
|
||||||
dica: props.dica,
|
anoAtual,
|
||||||
dicaPersistente: props.dicaPersistente,
|
mesAtual,
|
||||||
densidade: props.densidade,
|
listaAnos,
|
||||||
variante: props.variante,
|
celulas,
|
||||||
min: props.min,
|
} = useDataHoraProps({ props, referencia, diaSelecionado })
|
||||||
max: props.max,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const modoEfetivo = computed<"data" | "dataHora">(() => opcoesEfetivas.value.modo ?? "dataHora")
|
const { estiloPopover, calcularPosicao, adicionarListenersPosicao, removerListenersPosicao } =
|
||||||
const desabilitadoEfetivo = computed<boolean>(() => Boolean(props.desabilitado))
|
useDataHoraPosicao({ uid, modoEfetivo })
|
||||||
|
|
||||||
const effectiveModelValue = computed<string | null>(() =>
|
function emitirValor(iso: string | null) {
|
||||||
props.value !== undefined ? (props.value ?? null) : (props.modelValue ?? null),
|
;["update:value", "input", "change", "update:modelValue", "alterar"].forEach((e) => {
|
||||||
)
|
emit(e, iso)
|
||||||
|
|
||||||
const valor = computed(() => effectiveModelValue.value)
|
|
||||||
|
|
||||||
const mensagensVisiveis = computed<string[]>(() => {
|
|
||||||
const m = opcoesEfetivas.value.erro ? opcoesEfetivas.value.mensagensErro : null
|
|
||||||
return m ? (Array.isArray(m) ? m : [m]) : []
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const valorFormatado = computed<string>(() => formatarValor(valor.value, modoEfetivo.value))
|
const { textoDigitado, focado, onTextInput, onInputFocus, onInputBlur } = useDataHoraInput({
|
||||||
|
modoEfetivo,
|
||||||
const nomeMesAtual = computed(() => MESES[referencia.value.month()])
|
valor,
|
||||||
const anoAtual = computed(() => referencia.value.year())
|
horaTemp,
|
||||||
const mesAtual = computed(() => referencia.value.month())
|
minutoTemp,
|
||||||
const listaAnos = computed(() => {
|
emitirValor,
|
||||||
const y = referencia.value.year()
|
emit,
|
||||||
const min = opcoesEfetivas.value.min
|
|
||||||
? dayjs(opcoesEfetivas.value.min).year()
|
|
||||||
: dayjs().year() - 80
|
|
||||||
const max = opcoesEfetivas.value.max
|
|
||||||
? dayjs(opcoesEfetivas.value.max).year()
|
|
||||||
: dayjs().year() + 20
|
|
||||||
const anos: number[] = []
|
|
||||||
for (let i = Math.min(min, y); i <= Math.max(max, y); i++) anos.push(i)
|
|
||||||
return anos
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const celulas = computed<CelulaCalendario[]>(() =>
|
|
||||||
gerarCelulas(
|
|
||||||
referencia.value,
|
|
||||||
diaSelecionado.value,
|
|
||||||
opcoesEfetivas.value.min,
|
|
||||||
opcoesEfetivas.value.max,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
effectiveModelValue,
|
effectiveModelValue,
|
||||||
(v) => {
|
(v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
const d = dayjs(v)
|
const d = dayjs(v)
|
||||||
|
if (d.isValid()) {
|
||||||
diaSelecionado.value = d.startOf("day")
|
diaSelecionado.value = d.startOf("day")
|
||||||
referencia.value = d.startOf("month")
|
referencia.value = d.startOf("month")
|
||||||
horaTemp.value = d.format("HH")
|
horaTemp.value = d.format("HH")
|
||||||
minutoTemp.value = d.format("mm")
|
minutoTemp.value = d.format("mm")
|
||||||
} else {
|
textoDigitado.value = formatarValor(v, modoEfetivo.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
diaSelecionado.value = null
|
diaSelecionado.value = null
|
||||||
horaTemp.value = "00"
|
horaTemp.value = "00"
|
||||||
minutoTemp.value = "00"
|
minutoTemp.value = "00"
|
||||||
}
|
textoDigitado.value = ""
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
function calcularPosicao() {
|
|
||||||
const el = document.getElementById(`eli-dh-campo-${uid}`)
|
|
||||||
if (!el) return
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const top = rect.bottom + window.scrollY + 6
|
|
||||||
let left = rect.left + window.scrollX
|
|
||||||
if (left + 316 > window.innerWidth + window.scrollX - 8)
|
|
||||||
left = rect.right + window.scrollX - 316
|
|
||||||
estiloPopover.value = { position: "absolute", top: `${top}px`, left: `${left}px` }
|
|
||||||
}
|
|
||||||
|
|
||||||
function abrirSeHabilitado() {
|
function abrirSeHabilitado() {
|
||||||
if (desabilitadoEfetivo.value) return
|
if (desabilitadoEfetivo.value) return
|
||||||
aberto.value = !aberto.value
|
if (!aberto.value) {
|
||||||
if (aberto.value) {
|
aberto.value = true
|
||||||
|
calcularPosicao()
|
||||||
emit("foco")
|
emit("foco")
|
||||||
emit("focus")
|
emit("focus")
|
||||||
nextTick(calcularPosicao)
|
nextTick(calcularPosicao)
|
||||||
|
adicionarListenersPosicao()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function alternarPopover() {
|
||||||
|
if (desabilitadoEfetivo.value) return
|
||||||
|
if (aberto.value) {
|
||||||
|
fechar()
|
||||||
|
} else {
|
||||||
|
abrirSeHabilitado()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fechar() {
|
function fechar() {
|
||||||
if (!aberto.value) return
|
if (!aberto.value) return
|
||||||
aberto.value = false
|
aberto.value = false
|
||||||
|
removerListenersPosicao()
|
||||||
emit("desfoco")
|
emit("desfoco")
|
||||||
emit("blur")
|
emit("blur")
|
||||||
}
|
}
|
||||||
|
|
@ -179,13 +155,27 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
function selecionarDia(cell: CelulaCalendario) {
|
function selecionarDia(cell: CelulaCalendario) {
|
||||||
if (cell.desabilitado) return
|
if (cell.desabilitado) return
|
||||||
diaSelecionado.value = cell.data.startOf("day")
|
diaSelecionado.value = cell.data.startOf("day")
|
||||||
if (modoEfetivo.value === "data") confirmar()
|
confirmar()
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitirValor(iso: string | null) {
|
function atualizarHoraTemp(v: string) {
|
||||||
;["update:value", "input", "change", "update:modelValue", "alterar"].forEach((e) => {
|
horaTemp.value = v
|
||||||
emit(e, iso)
|
if (diaSelecionado.value) {
|
||||||
})
|
const h = Number.parseInt(v, 10) || 0
|
||||||
|
const m = Number.parseInt(minutoTemp.value, 10) || 0
|
||||||
|
const r = diaSelecionado.value.hour(h).minute(m).second(0)
|
||||||
|
emitirValor(r.format())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function atualizarMinutoTemp(v: string) {
|
||||||
|
minutoTemp.value = v
|
||||||
|
if (diaSelecionado.value) {
|
||||||
|
const h = Number.parseInt(horaTemp.value, 10) || 0
|
||||||
|
const m = Number.parseInt(v, 10) || 0
|
||||||
|
const r = diaSelecionado.value.hour(h).minute(m).second(0)
|
||||||
|
emitirValor(r.format())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmar() {
|
function confirmar() {
|
||||||
|
|
@ -201,6 +191,7 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function limpar() {
|
function limpar() {
|
||||||
|
textoDigitado.value = ""
|
||||||
emitirValor(null)
|
emitirValor(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,11 +206,16 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => document.addEventListener("click", onDocClick, true))
|
onMounted(() => document.addEventListener("click", onDocClick, true))
|
||||||
onBeforeUnmount(() => document.removeEventListener("click", onDocClick, true))
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener("click", onDocClick, true)
|
||||||
|
removerListenersPosicao()
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid,
|
uid,
|
||||||
aberto,
|
aberto,
|
||||||
|
focado,
|
||||||
|
textoDigitado,
|
||||||
estiloPopover,
|
estiloPopover,
|
||||||
referencia,
|
referencia,
|
||||||
diaSelecionado,
|
diaSelecionado,
|
||||||
|
|
@ -239,10 +235,16 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
mudarAno,
|
mudarAno,
|
||||||
celulas,
|
celulas,
|
||||||
abrirSeHabilitado,
|
abrirSeHabilitado,
|
||||||
|
alternarPopover,
|
||||||
fechar,
|
fechar,
|
||||||
navegarMes,
|
navegarMes,
|
||||||
selecionarDia,
|
selecionarDia,
|
||||||
confirmar,
|
confirmar,
|
||||||
limpar,
|
limpar,
|
||||||
|
onTextInput,
|
||||||
|
onInputFocus,
|
||||||
|
onInputBlur,
|
||||||
|
atualizarHoraTemp,
|
||||||
|
atualizarMinutoTemp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { type Ref, ref } from "vue"
|
||||||
|
import { aplicarMascaraDataHora, converterTextoParaIso, formatarValor } from "./calendarioHelpers"
|
||||||
|
|
||||||
|
interface UseDataHoraInputOptions {
|
||||||
|
modoEfetivo: Ref<"data" | "dataHora">
|
||||||
|
valor: Ref<string | null>
|
||||||
|
horaTemp: Ref<string>
|
||||||
|
minutoTemp: Ref<string>
|
||||||
|
emitirValor: (iso: string | null) => void
|
||||||
|
emit: (event: string, payload?: unknown) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable responsável pelo controle do campo de texto editável com máscara e eventos de foco/blur.
|
||||||
|
*/
|
||||||
|
export function useDataHoraInput({
|
||||||
|
modoEfetivo,
|
||||||
|
valor,
|
||||||
|
horaTemp,
|
||||||
|
minutoTemp,
|
||||||
|
emitirValor,
|
||||||
|
emit,
|
||||||
|
}: UseDataHoraInputOptions) {
|
||||||
|
const textoDigitado = ref("")
|
||||||
|
const focado = ref(false)
|
||||||
|
|
||||||
|
function onTextInput(e: Event) {
|
||||||
|
const rawVal = (e.target as HTMLInputElement).value
|
||||||
|
const comMascara = aplicarMascaraDataHora(rawVal, modoEfetivo.value)
|
||||||
|
textoDigitado.value = comMascara
|
||||||
|
;(e.target as HTMLInputElement).value = comMascara
|
||||||
|
|
||||||
|
if (!comMascara) {
|
||||||
|
emitirValor(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const iso = converterTextoParaIso(comMascara, modoEfetivo.value, {
|
||||||
|
hora: horaTemp.value,
|
||||||
|
minuto: minutoTemp.value,
|
||||||
|
})
|
||||||
|
if (iso) {
|
||||||
|
emitirValor(iso)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInputFocus() {
|
||||||
|
focado.value = true
|
||||||
|
emit("foco")
|
||||||
|
emit("focus")
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInputBlur() {
|
||||||
|
focado.value = false
|
||||||
|
if (!valor.value) {
|
||||||
|
textoDigitado.value = ""
|
||||||
|
} else {
|
||||||
|
textoDigitado.value = formatarValor(valor.value, modoEfetivo.value)
|
||||||
|
}
|
||||||
|
emit("desfoco")
|
||||||
|
emit("blur")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
textoDigitado,
|
||||||
|
focado,
|
||||||
|
onTextInput,
|
||||||
|
onInputFocus,
|
||||||
|
onInputBlur,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { type Ref, ref } from "vue"
|
||||||
|
|
||||||
|
interface UseDataHoraPosicaoOptions {
|
||||||
|
uid: number
|
||||||
|
modoEfetivo: Ref<"data" | "dataHora">
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable responsável por calcular a posição fixed do popover do calendário.
|
||||||
|
*/
|
||||||
|
export function useDataHoraPosicao({ uid, modoEfetivo }: UseDataHoraPosicaoOptions) {
|
||||||
|
const estiloPopover = ref<Record<string, string>>({})
|
||||||
|
|
||||||
|
function calcularPosicao() {
|
||||||
|
const el = document.getElementById(`eli-dh-campo-${uid}`)
|
||||||
|
if (!el) return
|
||||||
|
const rect = el.getBoundingClientRect()
|
||||||
|
const popoverAltura = modoEfetivo.value === "dataHora" ? 380 : 320
|
||||||
|
|
||||||
|
let top = rect.bottom + 6
|
||||||
|
if (rect.bottom + popoverAltura > window.innerHeight && rect.top - popoverAltura > 0) {
|
||||||
|
top = rect.top - popoverAltura - 6
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = rect.left
|
||||||
|
if (left + 316 > window.innerWidth - 8) {
|
||||||
|
left = Math.max(8, rect.right - 316)
|
||||||
|
}
|
||||||
|
|
||||||
|
estiloPopover.value = { position: "fixed", top: `${top}px`, left: `${left}px` }
|
||||||
|
}
|
||||||
|
|
||||||
|
function adicionarListenersPosicao() {
|
||||||
|
window.addEventListener("scroll", calcularPosicao, true)
|
||||||
|
window.addEventListener("resize", calcularPosicao)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removerListenersPosicao() {
|
||||||
|
window.removeEventListener("scroll", calcularPosicao, true)
|
||||||
|
window.removeEventListener("resize", calcularPosicao)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
estiloPopover,
|
||||||
|
calcularPosicao,
|
||||||
|
adicionarListenersPosicao,
|
||||||
|
removerListenersPosicao,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
import dayjs from "dayjs"
|
||||||
|
import { computed, type Ref } from "vue"
|
||||||
|
import { formatarValor, gerarCelulas } from "./calendarioHelpers"
|
||||||
|
import type { EntradaDataHora } from "./useDataHora"
|
||||||
|
|
||||||
|
export const MESES = Array.from({ length: 12 }, (_, i) => {
|
||||||
|
const s = dayjs().month(i).format("MMMM")
|
||||||
|
return s.charAt(0).toUpperCase() + s.slice(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const diasSemana = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]
|
||||||
|
|
||||||
|
export interface UseDataHoraPropsOptions {
|
||||||
|
props: {
|
||||||
|
value?: string | null
|
||||||
|
opcoes?: EntradaDataHora["opcoes"]
|
||||||
|
modelValue?: string | null
|
||||||
|
modo?: "data" | "dataHora"
|
||||||
|
rotulo?: string
|
||||||
|
placeholder?: string
|
||||||
|
desabilitado?: boolean
|
||||||
|
limpavel?: boolean
|
||||||
|
erro?: boolean
|
||||||
|
mensagensErro?: string | string[]
|
||||||
|
dica?: string
|
||||||
|
dicaPersistente?: boolean
|
||||||
|
densidade?: EntradaDataHora["opcoes"]["densidade"]
|
||||||
|
variante?: EntradaDataHora["opcoes"]["variante"]
|
||||||
|
min?: string
|
||||||
|
max?: string
|
||||||
|
}
|
||||||
|
referencia: Ref<dayjs.Dayjs>
|
||||||
|
diaSelecionado: Ref<dayjs.Dayjs | null>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable responsável pelo cálculo de propriedades derivadas e opções do EliEntradaDataHora.
|
||||||
|
*/
|
||||||
|
export function useDataHoraProps({ props, referencia, diaSelecionado }: UseDataHoraPropsOptions) {
|
||||||
|
const opcoesEfetivas = computed<EntradaDataHora["opcoes"]>(() => {
|
||||||
|
const o = props.opcoes
|
||||||
|
return {
|
||||||
|
rotulo: o?.rotulo ?? props.rotulo ?? "Data",
|
||||||
|
placeholder: o?.placeholder ?? props.placeholder ?? "",
|
||||||
|
modo: o?.modo ?? props.modo ?? "dataHora",
|
||||||
|
limpavel: o?.limpavel ?? props.limpavel,
|
||||||
|
erro: o?.erro ?? props.erro,
|
||||||
|
mensagensErro: o?.mensagensErro ?? props.mensagensErro,
|
||||||
|
dica: o?.dica ?? props.dica,
|
||||||
|
dicaPersistente: o?.dicaPersistente ?? props.dicaPersistente,
|
||||||
|
densidade: o?.densidade ?? props.densidade,
|
||||||
|
variante: o?.variante ?? props.variante,
|
||||||
|
min: o?.min ?? props.min,
|
||||||
|
max: o?.max ?? props.max,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const modoEfetivo = computed<"data" | "dataHora">(() => opcoesEfetivas.value.modo ?? "dataHora")
|
||||||
|
const desabilitadoEfetivo = computed<boolean>(() => Boolean(props.desabilitado))
|
||||||
|
|
||||||
|
const effectiveModelValue = computed<string | null>(() =>
|
||||||
|
props.value !== undefined ? (props.value ?? null) : (props.modelValue ?? null),
|
||||||
|
)
|
||||||
|
|
||||||
|
const valor = computed(() => effectiveModelValue.value)
|
||||||
|
|
||||||
|
const mensagensVisiveis = computed<string[]>(() => {
|
||||||
|
const m = opcoesEfetivas.value.erro ? opcoesEfetivas.value.mensagensErro : null
|
||||||
|
return m ? (Array.isArray(m) ? m : [m]) : []
|
||||||
|
})
|
||||||
|
|
||||||
|
const valorFormatado = computed<string>(() => formatarValor(valor.value, modoEfetivo.value))
|
||||||
|
|
||||||
|
const nomeMesAtual = computed(() => MESES[referencia.value.month()])
|
||||||
|
const anoAtual = computed(() => referencia.value.year())
|
||||||
|
const mesAtual = computed(() => referencia.value.month())
|
||||||
|
const listaAnos = computed(() => {
|
||||||
|
const y = referencia.value.year()
|
||||||
|
const min = opcoesEfetivas.value.min
|
||||||
|
? dayjs(opcoesEfetivas.value.min).year()
|
||||||
|
: dayjs().year() - 80
|
||||||
|
const max = opcoesEfetivas.value.max
|
||||||
|
? dayjs(opcoesEfetivas.value.max).year()
|
||||||
|
: dayjs().year() + 20
|
||||||
|
const anos: number[] = []
|
||||||
|
for (let i = Math.min(min, y); i <= Math.max(max, y); i++) anos.push(i)
|
||||||
|
return anos
|
||||||
|
})
|
||||||
|
|
||||||
|
const celulas = computed(() =>
|
||||||
|
gerarCelulas(
|
||||||
|
referencia.value,
|
||||||
|
diaSelecionado.value,
|
||||||
|
opcoesEfetivas.value.min,
|
||||||
|
opcoesEfetivas.value.max,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
opcoesEfetivas,
|
||||||
|
modoEfetivo,
|
||||||
|
desabilitadoEfetivo,
|
||||||
|
effectiveModelValue,
|
||||||
|
valor,
|
||||||
|
mensagensVisiveis,
|
||||||
|
valorFormatado,
|
||||||
|
nomeMesAtual,
|
||||||
|
anoAtual,
|
||||||
|
mesAtual,
|
||||||
|
listaAnos,
|
||||||
|
celulas,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="eli-tabela">
|
<div class="eli-tabela">
|
||||||
<EliTabelaDebug :isDev="isDev" :menuAberto="menuAberto" :menuPopupPos="menuPopupPos">
|
|
||||||
<div>paginaAtual: {{ paginaAtual }}</div>
|
|
||||||
<div>limit: {{ registrosPorConsulta }}</div>
|
|
||||||
<div>texto_busca: {{ (valorBusca || '').trim() }}</div>
|
|
||||||
<div>filtrosAvancadosAtivos: {{ JSON.stringify(filtrosAvancadosAtivos) }}</div>
|
|
||||||
<div>quantidadeTotal: {{ quantidade }}</div>
|
|
||||||
</EliTabelaDebug>
|
|
||||||
|
|
||||||
<EliTabelaCabecalho
|
<EliTabelaCabecalho
|
||||||
v-if="exibirBusca || temAcoesSuperiores"
|
v-if="exibirBusca || temAcoesSuperiores"
|
||||||
|
|
@ -123,7 +116,6 @@ import { defineComponent, type PropType } from "vue"
|
||||||
import "./EliTabela.css"
|
import "./EliTabela.css"
|
||||||
import EliTabelaBody from "./EliTabelaBody.vue"
|
import EliTabelaBody from "./EliTabelaBody.vue"
|
||||||
import EliTabelaCabecalho from "./EliTabelaCabecalho.vue"
|
import EliTabelaCabecalho from "./EliTabelaCabecalho.vue"
|
||||||
import EliTabelaDebug from "./EliTabelaDebug.vue"
|
|
||||||
import EliTabelaEstados from "./EliTabelaEstados.vue"
|
import EliTabelaEstados from "./EliTabelaEstados.vue"
|
||||||
import EliTabelaHead from "./EliTabelaHead.vue"
|
import EliTabelaHead from "./EliTabelaHead.vue"
|
||||||
import EliTabelaMenuAcoes from "./EliTabelaMenuAcoes.vue"
|
import EliTabelaMenuAcoes from "./EliTabelaMenuAcoes.vue"
|
||||||
|
|
@ -143,7 +135,6 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
EliTabelaCabecalho,
|
EliTabelaCabecalho,
|
||||||
EliTabelaEstados,
|
EliTabelaEstados,
|
||||||
EliTabelaDebug,
|
|
||||||
EliTabelaHead,
|
EliTabelaHead,
|
||||||
EliTabelaBody,
|
EliTabelaBody,
|
||||||
EliTabelaMenuAcoes,
|
EliTabelaMenuAcoes,
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@
|
||||||
.playground-console-scroll::-webkit-scrollbar-thumb:hover {
|
.playground-console-scroll::-webkit-scrollbar-thumb:hover {
|
||||||
background: #52525b;
|
background: #52525b;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, provide, ref } from "vue"
|
import { defineComponent, provide, ref } from "vue"
|
||||||
import { EliFeedbackWidget } from "../componentes/FeedbackWidget"
|
import { EliFeedbackWidget } from "../componentes/FeedbackWidget"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue