feat: melhoria componentes de data
This commit is contained in:
parent
84383c3bbf
commit
642ccf4c1a
21 changed files with 6888 additions and 4607 deletions
248
src/componentes/EliEntrada/EliEntradaDataHora/useDataHora.ts
Normal file
248
src/componentes/EliEntrada/EliEntradaDataHora/useDataHora.ts
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
import dayjs from "dayjs"
|
||||
import "dayjs/locale/pt-br"
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
|
||||
import type { CampoDensidade, CampoVariante } from "../../../tipos"
|
||||
import type { PadroesEntradas } from "../tiposEntradas"
|
||||
import { formatarValor, gerarCelulas } from "./calendarioHelpers"
|
||||
|
||||
dayjs.locale("pt-br")
|
||||
|
||||
export type EntradaDataHora = PadroesEntradas["dataHora"]
|
||||
|
||||
export interface CelulaCalendario {
|
||||
dia: number
|
||||
data: dayjs.Dayjs
|
||||
atual: boolean
|
||||
hoje: boolean
|
||||
selecionado: 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) */
|
||||
type EmitFn = (event: string, payload?: unknown) => void
|
||||
|
||||
type 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?: CampoDensidade
|
||||
variante?: CampoVariante
|
||||
min?: string
|
||||
max?: string
|
||||
}
|
||||
|
||||
let contadorUid = 0
|
||||
|
||||
/** Composable principal do EliEntradaDataHora. */
|
||||
export function useDataHora(props: Props, emit: EmitFn) {
|
||||
const uid = ++contadorUid
|
||||
const aberto = ref(false)
|
||||
const estiloPopover = ref<Record<string, string>>({})
|
||||
const referencia = ref(dayjs())
|
||||
const diaSelecionado = ref<dayjs.Dayjs | null>(null)
|
||||
const horaTemp = ref("00")
|
||||
const minutoTemp = ref("00")
|
||||
|
||||
const opcoesEfetivas = computed<EntradaDataHora["opcoes"]>(
|
||||
() =>
|
||||
props.opcoes ?? {
|
||||
rotulo: props.rotulo ?? "Data",
|
||||
placeholder: props.placeholder ?? "",
|
||||
modo: props.modo ?? "dataHora",
|
||||
limpavel: props.limpavel,
|
||||
erro: props.erro,
|
||||
mensagensErro: props.mensagensErro,
|
||||
dica: props.dica,
|
||||
dicaPersistente: props.dicaPersistente,
|
||||
densidade: props.densidade,
|
||||
variante: props.variante,
|
||||
min: props.min,
|
||||
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<CelulaCalendario[]>(() =>
|
||||
gerarCelulas(
|
||||
referencia.value,
|
||||
diaSelecionado.value,
|
||||
opcoesEfetivas.value.min,
|
||||
opcoesEfetivas.value.max,
|
||||
),
|
||||
)
|
||||
|
||||
watch(
|
||||
effectiveModelValue,
|
||||
(v) => {
|
||||
if (v) {
|
||||
const d = dayjs(v)
|
||||
diaSelecionado.value = d.startOf("day")
|
||||
referencia.value = d.startOf("month")
|
||||
horaTemp.value = d.format("HH")
|
||||
minutoTemp.value = d.format("mm")
|
||||
} else {
|
||||
diaSelecionado.value = null
|
||||
horaTemp.value = "00"
|
||||
minutoTemp.value = "00"
|
||||
}
|
||||
},
|
||||
{ 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() {
|
||||
if (desabilitadoEfetivo.value) return
|
||||
aberto.value = !aberto.value
|
||||
if (aberto.value) {
|
||||
emit("foco")
|
||||
emit("focus")
|
||||
nextTick(calcularPosicao)
|
||||
}
|
||||
}
|
||||
|
||||
function fechar() {
|
||||
if (!aberto.value) return
|
||||
aberto.value = false
|
||||
emit("desfoco")
|
||||
emit("blur")
|
||||
}
|
||||
|
||||
function navegarMes(delta: number) {
|
||||
referencia.value = referencia.value.add(delta, "month")
|
||||
}
|
||||
|
||||
function mudarMes(mes: number) {
|
||||
referencia.value = referencia.value.month(mes)
|
||||
}
|
||||
|
||||
function mudarAno(ano: number) {
|
||||
referencia.value = referencia.value.year(ano)
|
||||
}
|
||||
|
||||
function selecionarDia(cell: CelulaCalendario) {
|
||||
if (cell.desabilitado) return
|
||||
diaSelecionado.value = cell.data.startOf("day")
|
||||
if (modoEfetivo.value === "data") confirmar()
|
||||
}
|
||||
|
||||
function emitirValor(iso: string | null) {
|
||||
;["update:value", "input", "change", "update:modelValue", "alterar"].forEach((e) => {
|
||||
emit(e, iso)
|
||||
})
|
||||
}
|
||||
|
||||
function confirmar() {
|
||||
if (!diaSelecionado.value) return
|
||||
const h = Number.parseInt(horaTemp.value, 10) || 0
|
||||
const m = Number.parseInt(minutoTemp.value, 10) || 0
|
||||
const r =
|
||||
modoEfetivo.value === "data"
|
||||
? diaSelecionado.value.startOf("day")
|
||||
: diaSelecionado.value.hour(h).minute(m).second(0)
|
||||
emitirValor(r.format())
|
||||
fechar()
|
||||
}
|
||||
|
||||
function limpar() {
|
||||
emitirValor(null)
|
||||
}
|
||||
|
||||
function onDocClick(e: MouseEvent) {
|
||||
if (!aberto.value) return
|
||||
const alvo = e.target as Node
|
||||
if (
|
||||
!document.getElementById(`eli-dh-campo-${uid}`)?.contains(alvo) &&
|
||||
!document.getElementById(`eli-dh-popover-${uid}`)?.contains(alvo)
|
||||
)
|
||||
fechar()
|
||||
}
|
||||
|
||||
onMounted(() => document.addEventListener("click", onDocClick, true))
|
||||
onBeforeUnmount(() => document.removeEventListener("click", onDocClick, true))
|
||||
|
||||
return {
|
||||
uid,
|
||||
aberto,
|
||||
estiloPopover,
|
||||
referencia,
|
||||
diaSelecionado,
|
||||
horaTemp,
|
||||
minutoTemp,
|
||||
opcoesEfetivas,
|
||||
modoEfetivo,
|
||||
desabilitadoEfetivo,
|
||||
valor,
|
||||
valorFormatado,
|
||||
mensagensVisiveis,
|
||||
nomeMesAtual,
|
||||
anoAtual,
|
||||
mesAtual,
|
||||
listaAnos,
|
||||
mudarMes,
|
||||
mudarAno,
|
||||
celulas,
|
||||
abrirSeHabilitado,
|
||||
fechar,
|
||||
navegarMes,
|
||||
selecionarDia,
|
||||
confirmar,
|
||||
limpar,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue