diff --git a/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.css b/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.css index 3d91a5e..09e1985 100644 --- a/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.css +++ b/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.css @@ -3,10 +3,8 @@ ───────────────────────────────────────────── */ .eli-dh__popover { - position: absolute; - top: calc(100% + 6px); - left: 0; - z-index: 2000; + position: fixed; + z-index: 99999; background: var(--cor-eli-superficie, #fff); border: 1.5px solid var(--cor-eli-borda, #D5D8CC); border-radius: 14px; diff --git a/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.vue b/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.vue index 6e275b3..6044ac8 100644 --- a/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.vue +++ b/src/componentes/EliEntrada/EliEntradaDataHora/Calendario.vue @@ -67,6 +67,7 @@ :aria-selected="cell.selecionado" :aria-label="`${cell.dia} de ${nomeMesAtual} de ${anoAtual}`" @click="$emit('selecionarDia', cell)" + @dblclick="$emit('confirmar')" > {{ cell.dia }} @@ -91,6 +92,8 @@ @input="onHoraInput" @blur="onHoraBlur" @keydown="onHoraKeydown" + @focus="onInputFocusSelect" + @click.stop /> @@ -214,6 +219,10 @@ export default defineComponent({ ) } } + function onInputFocusSelect(e: Event) { + ;(e.target as HTMLInputElement).select() + } + return { diasSemana, MESES, @@ -225,6 +234,7 @@ export default defineComponent({ onMinutoInput, onMinutoBlur, onMinutoKeydown, + onInputFocusSelect, } }, }) diff --git a/src/componentes/EliEntrada/EliEntradaDataHora/EliEntradaDataHora.css b/src/componentes/EliEntrada/EliEntradaDataHora/EliEntradaDataHora.css index 02efb49..1f048a0 100644 --- a/src/componentes/EliEntrada/EliEntradaDataHora/EliEntradaDataHora.css +++ b/src/componentes/EliEntrada/EliEntradaDataHora/EliEntradaDataHora.css @@ -44,18 +44,29 @@ opacity: 0.6; } -.eli-dh__icone { - flex-shrink: 0; - width: 20px; - height: 20px; +.eli-dh__icone-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 0; + border: none; + background: none; + cursor: pointer; color: var(--cor-eli-neutro, #5E6553); 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); } +.eli-dh__icone { + width: 20px; + height: 20px; +} + .eli-dh__conteudo { flex: 1; display: flex; @@ -72,23 +83,35 @@ letter-spacing: 0.02em; text-transform: uppercase; transition: color 0.18s; + cursor: pointer; } .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__valor { +.eli-dh__input { + width: 100%; + border: none; + background: transparent; + outline: none; font-size: 15px; font-weight: 500; + font-family: inherit; color: var(--cor-eli-neutro-escuro, #3E4436); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + padding: 0; + margin: 0; + line-height: 1.2; } -.eli-dh__valor--vazio { +.eli-dh__input::placeholder { color: var(--cor-eli-borda-forte, #B5BAA8); font-weight: 400; + opacity: 1; +} + +.eli-dh__input:disabled { + cursor: not-allowed; + color: var(--cor-eli-neutro, #5E6553); } .eli-dh__limpar { diff --git a/src/componentes/EliEntrada/EliEntradaDataHora/calendarioHelpers.ts b/src/componentes/EliEntrada/EliEntradaDataHora/calendarioHelpers.ts index a8240a9..1965b06 100644 --- a/src/componentes/EliEntrada/EliEntradaDataHora/calendarioHelpers.ts +++ b/src/componentes/EliEntrada/EliEntradaDataHora/calendarioHelpers.ts @@ -67,7 +67,71 @@ export function formatarValor( modo: EntradaDataHora["opcoes"]["modo"], ): string { if (!valor) return "" - return modo === "data" - ? dayjs(valor).format("DD/MM/YYYY") - : dayjs(valor).format("DD/MM/YYYY HH:mm") + const d = dayjs(valor) + if (!d.isValid()) return "" + 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 } diff --git a/src/componentes/EliEntrada/EliEntradaDataHora/index.vue b/src/componentes/EliEntrada/EliEntradaDataHora/index.vue index 3d811bb..63bf663 100644 --- a/src/componentes/EliEntrada/EliEntradaDataHora/index.vue +++ b/src/componentes/EliEntrada/EliEntradaDataHora/index.vue @@ -5,30 +5,49 @@ :id="`eli-dh-campo-${uid}`" class="eli-dh__campo" :class="{ - 'eli-dh__campo--focado': aberto, + 'eli-dh__campo--focado': aberto || focado, 'eli-dh__campo--erro': Boolean(opcoesEfetivas.erro), 'eli-dh__campo--desabilitado': desabilitadoEfetivo, }" - role="button" - :tabindex="desabilitadoEfetivo ? -1 : 0" - :aria-haspopup="true" - :aria-expanded="aberto" - :aria-label="opcoesEfetivas.rotulo" @click="abrirSeHabilitado" - @keydown.enter.prevent="abrirSeHabilitado" - @keydown.space.prevent="abrirSeHabilitado" - @keydown.escape="fechar" > - + +
- {{ opcoesEfetivas.rotulo }} - - {{ valorFormatado || opcoesEfetivas.placeholder || 'Selecione...' }} - + +
+