melhoria no componente de data
This commit is contained in:
parent
dbf08a312d
commit
85658f80d0
12 changed files with 2542 additions and 2569 deletions
4662
dist/eli-vue.es.js
vendored
4662
dist/eli-vue.es.js
vendored
File diff suppressed because one or more lines are too long
60
dist/eli-vue.umd.js
vendored
60
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,14 @@
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { CelulaCalendario, EntradaDataHora } from "./useDataHora";
|
export interface CelulaCalendario {
|
||||||
|
dia: number;
|
||||||
|
data: dayjs.Dayjs;
|
||||||
|
atual: boolean;
|
||||||
|
hoje: boolean;
|
||||||
|
selecionado: boolean;
|
||||||
|
desabilitado: boolean;
|
||||||
|
}
|
||||||
|
export declare const OPCOES_HORAS: string[];
|
||||||
|
export declare const OPCOES_MINUTOS: string[];
|
||||||
/**
|
/**
|
||||||
* Gera as 42 células (6 semanas) do calendário para o mês de referência.
|
* Gera as 42 células (6 semanas) do calendário para o mês de referência.
|
||||||
* Preenche dias do mês anterior, atual e posterior para alinhar a grade.
|
* Preenche dias do mês anterior, atual e posterior para alinhar a grade.
|
||||||
|
|
@ -10,7 +19,7 @@ export declare function gerarCelulas(referencia: dayjs.Dayjs, diaSelecionado: da
|
||||||
* @param valor - string ISO 8601 ou null
|
* @param valor - string ISO 8601 ou null
|
||||||
* @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: "data" | "dataHora"): string;
|
||||||
/**
|
/**
|
||||||
* Aplica máscara visual de data/hora (DD/MM/YYYY ou DD/MM/YYYY HH:mm) em texto digitado.
|
* Aplica máscara visual de data/hora (DD/MM/YYYY ou DD/MM/YYYY HH:mm) em texto digitado.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,10 @@ 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 { type CelulaCalendario } from "./calendarioHelpers";
|
||||||
import { diasSemana, MESES } from "./useDataHoraProps";
|
import { diasSemana, MESES } from "./useDataHoraProps";
|
||||||
export { diasSemana, MESES };
|
export { type CelulaCalendario, diasSemana, MESES };
|
||||||
export type EntradaDataHora = PadroesEntradas["dataHora"];
|
export type EntradaDataHora = PadroesEntradas["dataHora"];
|
||||||
export interface CelulaCalendario {
|
|
||||||
dia: number;
|
|
||||||
data: dayjs.Dayjs;
|
|
||||||
atual: boolean;
|
|
||||||
hoje: boolean;
|
|
||||||
selecionado: boolean;
|
|
||||||
desabilitado: boolean;
|
|
||||||
}
|
|
||||||
/** 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 = {
|
||||||
value?: string | null;
|
value?: string | null;
|
||||||
|
|
@ -405,6 +397,7 @@ export declare function useDataHora(props: Props, emit: EmitFn): {
|
||||||
fechar: () => void;
|
fechar: () => void;
|
||||||
navegarMes: (delta: number) => void;
|
navegarMes: (delta: number) => void;
|
||||||
selecionarDia: (cell: CelulaCalendario) => void;
|
selecionarDia: (cell: CelulaCalendario) => void;
|
||||||
|
definirAgora: () => void;
|
||||||
confirmar: () => void;
|
confirmar: () => void;
|
||||||
limpar: () => void;
|
limpar: () => void;
|
||||||
onTextInput: (e: Event) => void;
|
onTextInput: (e: Event) => void;
|
||||||
|
|
|
||||||
|
|
@ -55,5 +55,5 @@ export declare function useDataHoraProps({ props, referencia, diaSelecionado }:
|
||||||
anoAtual: import("vue").ComputedRef<number>;
|
anoAtual: import("vue").ComputedRef<number>;
|
||||||
mesAtual: import("vue").ComputedRef<number>;
|
mesAtual: import("vue").ComputedRef<number>;
|
||||||
listaAnos: import("vue").ComputedRef<number[]>;
|
listaAnos: import("vue").ComputedRef<number[]>;
|
||||||
celulas: import("vue").ComputedRef<import("./useDataHora").CelulaCalendario[]>;
|
celulas: import("vue").ComputedRef<import("./calendarioHelpers").CelulaCalendario[]>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "eli-vue",
|
"name": "eli-vue",
|
||||||
"version": "0.1.230",
|
"version": "0.1.237",
|
||||||
"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",
|
||||||
|
|
|
||||||
|
|
@ -162,64 +162,64 @@
|
||||||
|
|
||||||
.eli-dh__hora-secao {
|
.eli-dh__hora-secao {
|
||||||
border-top: 1px solid var(--cor-eli-borda, #D5D8CC);
|
border-top: 1px solid var(--cor-eli-borda, #D5D8CC);
|
||||||
padding: 12px 16px 14px;
|
padding: 10px 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--cor-eli-fundo-suave, #F8F9F5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__hora-icone { width: 16px; height: 16px; color: var(--cor-eli-neutro, #5E6553); flex-shrink: 0; }
|
.eli-dh__hora-rotulo { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--cor-eli-neutro-escuro, #3E4436); }
|
||||||
|
.eli-dh__hora-icone { width: 16px; height: 16px; color: var(--cor-eli-primaria, #688C00); flex-shrink: 0; }
|
||||||
|
.eli-dh__hora-campos { display: flex; align-items: center; gap: 6px; }
|
||||||
|
|
||||||
.eli-dh__hora-campos { display: flex; align-items: center; gap: 6px; flex: 1; }
|
.eli-dh__hora-select {
|
||||||
|
height: 32px;
|
||||||
.eli-dh__hora-input {
|
padding: 2px 6px;
|
||||||
width: 52px;
|
|
||||||
padding: 6px 8px;
|
|
||||||
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
|
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: var(--cor-eli-superficie, #fff);
|
background: var(--cor-eli-superficie, #fff);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 16px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--cor-eli-neutro-escuro, #3E4436);
|
color: var(--cor-eli-neutro-escuro, #3E4436);
|
||||||
transition: border-color 0.15s;
|
cursor: pointer;
|
||||||
outline: none;
|
outline: none;
|
||||||
-moz-appearance: textfield;
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__hora-input::-webkit-inner-spin-button,
|
.eli-dh__hora-select:focus {
|
||||||
.eli-dh__hora-input::-webkit-outer-spin-button { -webkit-appearance: none; }
|
|
||||||
|
|
||||||
.eli-dh__hora-input:focus {
|
|
||||||
border-color: var(--cor-eli-primaria, #688C00);
|
border-color: var(--cor-eli-primaria, #688C00);
|
||||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--cor-eli-primaria, #688C00) 14%, transparent);
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--cor-eli-primaria, #688C00) 14%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eli-dh__hora-sep { font-size: 20px; font-weight: 700; color: var(--cor-eli-neutro, #5E6553); line-height: 1; margin-bottom: 2px; }
|
.eli-dh__hora-select option { background: var(--cor-eli-superficie, #fff); color: var(--cor-eli-neutro-escuro, #3E4436); font-weight: 600; }
|
||||||
|
.eli-dh__hora-sep { font-size: 16px; font-weight: 700; color: var(--cor-eli-neutro, #5E6553); line-height: 1; }
|
||||||
|
|
||||||
.eli-dh__rodape {
|
.eli-dh__btn-agora {
|
||||||
padding: 10px 14px 12px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 8px;
|
|
||||||
border-top: 1px solid var(--cor-eli-borda, #D5D8CC);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-dh__btn {
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
justify-content: center;
|
||||||
padding: 7px 16px;
|
height: 32px;
|
||||||
border: none;
|
padding: 0 10px;
|
||||||
border-radius: 8px;
|
border: 1px solid var(--cor-eli-primaria, #688C00);
|
||||||
font-size: 13px;
|
border-radius: 6px;
|
||||||
font-weight: 600;
|
background: color-mix(in srgb, var(--cor-eli-primaria, #688C00) 10%, transparent);
|
||||||
|
color: var(--cor-eli-primaria, #688C00);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.14s, opacity 0.14s;
|
transition: background 0.15s, color 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.eli-dh__btn-agora:hover { background: var(--cor-eli-primaria, #688C00); color: #fff; }
|
||||||
|
.eli-dh__rodape { padding: 10px 14px 12px; display: flex; align-items: center; justify-content: space-between; gap: 8px; border-top: 1px solid var(--cor-eli-borda, #D5D8CC); }
|
||||||
|
.eli-dh__rodape-acoes { display: flex; align-items: center; gap: 8px; margin-left: auto; }
|
||||||
|
.eli-dh__btn { display: inline-flex; align-items: center; gap: 5px; padding: 7px 16px; border: none; border-radius: 8px; font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; transition: background 0.14s, opacity 0.14s; }
|
||||||
.eli-dh__btn--cancelar { background: var(--cor-eli-fundo, #F2F3EE); color: var(--cor-eli-neutro, #5E6553); }
|
.eli-dh__btn--cancelar { background: var(--cor-eli-fundo, #F2F3EE); color: var(--cor-eli-neutro, #5E6553); }
|
||||||
.eli-dh__btn--cancelar:hover { background: var(--cor-eli-borda, #D5D8CC); }
|
.eli-dh__btn--cancelar:hover { background: var(--cor-eli-borda, #D5D8CC); }
|
||||||
.eli-dh__btn--confirmar { background: var(--cor-eli-primaria, #688C00); color: #fff; }
|
.eli-dh__btn--confirmar { background: var(--cor-eli-primaria, #688C00); color: #fff; }
|
||||||
|
|
|
||||||
|
|
@ -76,50 +76,60 @@
|
||||||
|
|
||||||
<!-- Seção de hora -->
|
<!-- Seção de hora -->
|
||||||
<div v-if="modo === 'dataHora'" class="eli-dh__hora-secao">
|
<div v-if="modo === 'dataHora'" class="eli-dh__hora-secao">
|
||||||
|
<div class="eli-dh__hora-rotulo">
|
||||||
<svg class="eli-dh__hora-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__hora-icone" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||||
<circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
|
<circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
|
||||||
</svg>
|
</svg>
|
||||||
|
<span>Horário:</span>
|
||||||
|
</div>
|
||||||
<div class="eli-dh__hora-campos">
|
<div class="eli-dh__hora-campos">
|
||||||
<input
|
<select
|
||||||
:id="`eli-dh-hora-${uid}`"
|
|
||||||
:value="horaTemp"
|
:value="horaTemp"
|
||||||
class="eli-dh__hora-input"
|
class="eli-dh__hora-select"
|
||||||
type="text"
|
:aria-label="`Selecionar hora de ${rotulo}`"
|
||||||
inputmode="numeric"
|
@change="onHoraSelectChange"
|
||||||
maxlength="2"
|
|
||||||
placeholder="HH"
|
|
||||||
:aria-label="`Hora de ${rotulo}`"
|
|
||||||
@input="onHoraInput"
|
|
||||||
@blur="onHoraBlur"
|
|
||||||
@keydown="onHoraKeydown"
|
|
||||||
@focus="onInputFocusSelect"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
>
|
||||||
|
<option v-for="h in OPCOES_HORAS" :key="h" :value="h">{{ h }}h</option>
|
||||||
|
</select>
|
||||||
<span class="eli-dh__hora-sep" aria-hidden="true">:</span>
|
<span class="eli-dh__hora-sep" aria-hidden="true">:</span>
|
||||||
<input
|
<select
|
||||||
:id="`eli-dh-min-${uid}`"
|
|
||||||
:value="minutoTemp"
|
:value="minutoTemp"
|
||||||
class="eli-dh__hora-input"
|
class="eli-dh__hora-select"
|
||||||
type="text"
|
:aria-label="`Selecionar minuto de ${rotulo}`"
|
||||||
inputmode="numeric"
|
@change="onMinutoSelectChange"
|
||||||
maxlength="2"
|
|
||||||
placeholder="MM"
|
|
||||||
:aria-label="`Minuto de ${rotulo}`"
|
|
||||||
@input="onMinutoInput"
|
|
||||||
@blur="onMinutoBlur"
|
|
||||||
@keydown="onMinutoKeydown"
|
|
||||||
@focus="onInputFocusSelect"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
>
|
||||||
|
<option v-for="m in OPCOES_MINUTOS" :key="m" :value="m">{{ m }}m</option>
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
class="eli-dh__btn-agora"
|
||||||
|
type="button"
|
||||||
|
title="Definir horário atual"
|
||||||
|
@click="$emit('definirAgora')"
|
||||||
|
>
|
||||||
|
Agora
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Rodapé -->
|
<!-- Rodapé -->
|
||||||
<div class="eli-dh__rodape">
|
<div class="eli-dh__rodape">
|
||||||
|
<button
|
||||||
|
v-if="modo === 'data'"
|
||||||
|
class="eli-dh__btn-agora"
|
||||||
|
type="button"
|
||||||
|
title="Definir para a data de hoje"
|
||||||
|
@click="$emit('definirAgora')"
|
||||||
|
>
|
||||||
|
Hoje
|
||||||
|
</button>
|
||||||
|
<div class="eli-dh__rodape-acoes">
|
||||||
<button class="eli-dh__btn eli-dh__btn--cancelar" type="button" @click="$emit('fechar')">Cancelar</button>
|
<button class="eli-dh__btn eli-dh__btn--cancelar" type="button" @click="$emit('fechar')">Cancelar</button>
|
||||||
<button class="eli-dh__btn eli-dh__btn--confirmar" type="button" :disabled="!diaSelecionado" @click="$emit('confirmar')">✓ Confirmar</button>
|
<button class="eli-dh__btn eli-dh__btn--confirmar" type="button" :disabled="!diaSelecionado" @click="$emit('confirmar')">✓ Confirmar</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -127,6 +137,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type dayjs from "dayjs"
|
import type dayjs from "dayjs"
|
||||||
import { defineComponent, type PropType } from "vue"
|
import { defineComponent, type PropType } from "vue"
|
||||||
|
import { OPCOES_HORAS, OPCOES_MINUTOS } from "./calendarioHelpers"
|
||||||
import type { CelulaCalendario } from "./useDataHora"
|
import type { CelulaCalendario } from "./useDataHora"
|
||||||
import { diasSemana, MESES } from "./useDataHora"
|
import { diasSemana, MESES } from "./useDataHora"
|
||||||
import "./Calendario.css"
|
import "./Calendario.css"
|
||||||
|
|
@ -151,6 +162,7 @@ export default defineComponent({
|
||||||
emits: {
|
emits: {
|
||||||
fechar: () => true,
|
fechar: () => true,
|
||||||
confirmar: () => true,
|
confirmar: () => true,
|
||||||
|
definirAgora: () => true,
|
||||||
navegarMes: (_delta: number) => true,
|
navegarMes: (_delta: number) => true,
|
||||||
selecionarDia: (_cell: CelulaCalendario) => true,
|
selecionarDia: (_cell: CelulaCalendario) => true,
|
||||||
mudarMes: (_mes: number) => true,
|
mudarMes: (_mes: number) => true,
|
||||||
|
|
@ -158,83 +170,29 @@ export default defineComponent({
|
||||||
"update:horaTemp": (_v: string) => true,
|
"update:horaTemp": (_v: string) => true,
|
||||||
"update:minutoTemp": (_v: string) => true,
|
"update:minutoTemp": (_v: string) => true,
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(_props, { emit }) {
|
||||||
function onMesSelectChange(e: Event) {
|
function onMesSelectChange(e: Event) {
|
||||||
const v = Number.parseInt((e.target as HTMLSelectElement).value, 10)
|
emit("mudarMes", Number.parseInt((e.target as HTMLSelectElement).value, 10))
|
||||||
emit("mudarMes", v)
|
|
||||||
}
|
}
|
||||||
function onAnoSelectChange(e: Event) {
|
function onAnoSelectChange(e: Event) {
|
||||||
const v = Number.parseInt((e.target as HTMLSelectElement).value, 10)
|
emit("mudarAno", Number.parseInt((e.target as HTMLSelectElement).value, 10))
|
||||||
emit("mudarAno", v)
|
|
||||||
}
|
}
|
||||||
function onHoraInput(e: Event) {
|
function onHoraSelectChange(e: Event) {
|
||||||
const raw = (e.target as HTMLInputElement).value.replace(/\D/g, "").slice(0, 2)
|
emit("update:horaTemp", (e.target as HTMLSelectElement).value)
|
||||||
emit("update:horaTemp", raw)
|
|
||||||
}
|
}
|
||||||
function onHoraBlur() {
|
function onMinutoSelectChange(e: Event) {
|
||||||
const v = Number.parseInt(props.horaTemp, 10)
|
emit("update:minutoTemp", (e.target as HTMLSelectElement).value)
|
||||||
emit(
|
|
||||||
"update:horaTemp",
|
|
||||||
Number.isNaN(v) ? "00" : String(Math.min(23, Math.max(0, v))).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
function onHoraKeydown(e: KeyboardEvent) {
|
|
||||||
if (e.key === "ArrowUp") {
|
|
||||||
e.preventDefault()
|
|
||||||
emit(
|
|
||||||
"update:horaTemp",
|
|
||||||
String(((Number.parseInt(props.horaTemp, 10) || 0) + 1) % 24).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
} else if (e.key === "ArrowDown") {
|
|
||||||
e.preventDefault()
|
|
||||||
emit(
|
|
||||||
"update:horaTemp",
|
|
||||||
String(((Number.parseInt(props.horaTemp, 10) || 0) - 1 + 24) % 24).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onMinutoInput(e: Event) {
|
|
||||||
const raw = (e.target as HTMLInputElement).value.replace(/\D/g, "").slice(0, 2)
|
|
||||||
emit("update:minutoTemp", raw)
|
|
||||||
}
|
|
||||||
function onMinutoBlur() {
|
|
||||||
const v = Number.parseInt(props.minutoTemp, 10)
|
|
||||||
emit(
|
|
||||||
"update:minutoTemp",
|
|
||||||
Number.isNaN(v) ? "00" : String(Math.min(59, Math.max(0, v))).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
function onMinutoKeydown(e: KeyboardEvent) {
|
|
||||||
if (e.key === "ArrowUp") {
|
|
||||||
e.preventDefault()
|
|
||||||
emit(
|
|
||||||
"update:minutoTemp",
|
|
||||||
String(((Number.parseInt(props.minutoTemp, 10) || 0) + 1) % 60).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
} else if (e.key === "ArrowDown") {
|
|
||||||
e.preventDefault()
|
|
||||||
emit(
|
|
||||||
"update:minutoTemp",
|
|
||||||
String(((Number.parseInt(props.minutoTemp, 10) || 0) - 1 + 60) % 60).padStart(2, "0"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onInputFocusSelect(e: Event) {
|
|
||||||
;(e.target as HTMLInputElement).select()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
diasSemana,
|
diasSemana,
|
||||||
MESES,
|
MESES,
|
||||||
|
OPCOES_HORAS,
|
||||||
|
OPCOES_MINUTOS,
|
||||||
onMesSelectChange,
|
onMesSelectChange,
|
||||||
onAnoSelectChange,
|
onAnoSelectChange,
|
||||||
onHoraInput,
|
onHoraSelectChange,
|
||||||
onHoraBlur,
|
onMinutoSelectChange,
|
||||||
onHoraKeydown,
|
|
||||||
onMinutoInput,
|
|
||||||
onMinutoBlur,
|
|
||||||
onMinutoKeydown,
|
|
||||||
onInputFocusSelect,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import type { CelulaCalendario, EntradaDataHora } from "./useDataHora"
|
|
||||||
|
export interface CelulaCalendario {
|
||||||
|
dia: number
|
||||||
|
data: dayjs.Dayjs
|
||||||
|
atual: boolean
|
||||||
|
hoje: boolean
|
||||||
|
selecionado: boolean
|
||||||
|
desabilitado: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OPCOES_HORAS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"))
|
||||||
|
export const OPCOES_MINUTOS = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, "0"))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gera as 42 células (6 semanas) do calendário para o mês de referência.
|
* Gera as 42 células (6 semanas) do calendário para o mês de referência.
|
||||||
|
|
@ -62,10 +73,7 @@ export function gerarCelulas(
|
||||||
* @param valor - string ISO 8601 ou null
|
* @param valor - string ISO 8601 ou null
|
||||||
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
|
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
|
||||||
*/
|
*/
|
||||||
export function formatarValor(
|
export function formatarValor(valor: string | null, modo: "data" | "dataHora"): string {
|
||||||
valor: string | null,
|
|
||||||
modo: EntradaDataHora["opcoes"]["modo"],
|
|
||||||
): string {
|
|
||||||
if (!valor) return ""
|
if (!valor) return ""
|
||||||
const d = dayjs(valor)
|
const d = dayjs(valor)
|
||||||
if (!d.isValid()) return ""
|
if (!d.isValid()) return ""
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
@mudarAno="mudarAno"
|
@mudarAno="mudarAno"
|
||||||
@update:horaTemp="atualizarHoraTemp"
|
@update:horaTemp="atualizarHoraTemp"
|
||||||
@update:minutoTemp="atualizarMinutoTemp"
|
@update:minutoTemp="atualizarMinutoTemp"
|
||||||
|
@definirAgora="definirAgora"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,16 @@ import "dayjs/locale/pt-br"
|
||||||
import { 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 } from "./calendarioHelpers"
|
import { type CelulaCalendario, formatarValor } from "./calendarioHelpers"
|
||||||
import { useDataHoraInput } from "./useDataHoraInput"
|
import { useDataHoraInput } from "./useDataHoraInput"
|
||||||
import { useDataHoraPosicao } from "./useDataHoraPosicao"
|
import { useDataHoraPosicao } from "./useDataHoraPosicao"
|
||||||
import { diasSemana, MESES, useDataHoraProps } from "./useDataHoraProps"
|
import { diasSemana, MESES, useDataHoraProps } from "./useDataHoraProps"
|
||||||
|
|
||||||
export { diasSemana, MESES }
|
export { type CelulaCalendario, diasSemana, MESES }
|
||||||
|
|
||||||
dayjs.locale("pt-br")
|
dayjs.locale("pt-br")
|
||||||
|
|
||||||
export type EntradaDataHora = PadroesEntradas["dataHora"]
|
export type EntradaDataHora = PadroesEntradas["dataHora"]
|
||||||
|
|
||||||
export interface CelulaCalendario {
|
|
||||||
dia: number
|
|
||||||
data: dayjs.Dayjs
|
|
||||||
atual: boolean
|
|
||||||
hoje: boolean
|
|
||||||
selecionado: boolean
|
|
||||||
desabilitado: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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 = {
|
||||||
|
|
@ -99,21 +88,20 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
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")
|
||||||
textoDigitado.value = formatarValor(v, modoEfetivo.value)
|
if (!focado.value) textoDigitado.value = formatarValor(v, modoEfetivo.value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diaSelecionado.value = null
|
diaSelecionado.value = null
|
||||||
horaTemp.value = "00"
|
horaTemp.value = "00"
|
||||||
minutoTemp.value = "00"
|
minutoTemp.value = "00"
|
||||||
textoDigitado.value = ""
|
if (!focado.value) textoDigitado.value = ""
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
function abrirSeHabilitado() {
|
function abrirSeHabilitado() {
|
||||||
if (desabilitadoEfetivo.value) return
|
if (desabilitadoEfetivo.value || aberto.value) return
|
||||||
if (!aberto.value) {
|
|
||||||
aberto.value = true
|
aberto.value = true
|
||||||
calcularPosicao()
|
calcularPosicao()
|
||||||
emit("foco")
|
emit("foco")
|
||||||
|
|
@ -121,15 +109,10 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
nextTick(calcularPosicao)
|
nextTick(calcularPosicao)
|
||||||
adicionarListenersPosicao()
|
adicionarListenersPosicao()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function alternarPopover() {
|
function alternarPopover() {
|
||||||
if (desabilitadoEfetivo.value) return
|
if (desabilitadoEfetivo.value) return
|
||||||
if (aberto.value) {
|
aberto.value ? fechar() : abrirSeHabilitado()
|
||||||
fechar()
|
|
||||||
} else {
|
|
||||||
abrirSeHabilitado()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fechar() {
|
function fechar() {
|
||||||
|
|
@ -155,7 +138,24 @@ 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()
|
||||||
|
} else {
|
||||||
|
const h = Number.parseInt(horaTemp.value, 10) || 0
|
||||||
|
const m = Number.parseInt(minutoTemp.value, 10) || 0
|
||||||
|
emitirValor(diaSelecionado.value.hour(h).minute(m).second(0).format())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function definirAgora() {
|
||||||
|
const agora = dayjs()
|
||||||
|
diaSelecionado.value = agora.startOf("day")
|
||||||
|
referencia.value = agora.startOf("month")
|
||||||
|
horaTemp.value = agora.format("HH")
|
||||||
|
minutoTemp.value = agora.format("mm")
|
||||||
|
const r = modoEfetivo.value === "data" ? agora.startOf("day") : agora.second(0)
|
||||||
|
emitirValor(r.format())
|
||||||
|
if (modoEfetivo.value === "data") fechar()
|
||||||
}
|
}
|
||||||
|
|
||||||
function atualizarHoraTemp(v: string) {
|
function atualizarHoraTemp(v: string) {
|
||||||
|
|
@ -163,8 +163,7 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
if (diaSelecionado.value) {
|
if (diaSelecionado.value) {
|
||||||
const h = Number.parseInt(v, 10) || 0
|
const h = Number.parseInt(v, 10) || 0
|
||||||
const m = Number.parseInt(minutoTemp.value, 10) || 0
|
const m = Number.parseInt(minutoTemp.value, 10) || 0
|
||||||
const r = diaSelecionado.value.hour(h).minute(m).second(0)
|
emitirValor(diaSelecionado.value.hour(h).minute(m).second(0).format())
|
||||||
emitirValor(r.format())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,8 +172,7 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
if (diaSelecionado.value) {
|
if (diaSelecionado.value) {
|
||||||
const h = Number.parseInt(horaTemp.value, 10) || 0
|
const h = Number.parseInt(horaTemp.value, 10) || 0
|
||||||
const m = Number.parseInt(v, 10) || 0
|
const m = Number.parseInt(v, 10) || 0
|
||||||
const r = diaSelecionado.value.hour(h).minute(m).second(0)
|
emitirValor(diaSelecionado.value.hour(h).minute(m).second(0).format())
|
||||||
emitirValor(r.format())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,6 +237,7 @@ export function useDataHora(props: Props, emit: EmitFn) {
|
||||||
fechar,
|
fechar,
|
||||||
navegarMes,
|
navegarMes,
|
||||||
selecionarDia,
|
selecionarDia,
|
||||||
|
definirAgora,
|
||||||
confirmar,
|
confirmar,
|
||||||
limpar,
|
limpar,
|
||||||
onTextInput,
|
onTextInput,
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,19 @@ export function useDataHoraInput({
|
||||||
minuto: minutoTemp.value,
|
minuto: minutoTemp.value,
|
||||||
})
|
})
|
||||||
if (iso) {
|
if (iso) {
|
||||||
|
const digitos = comMascara.replace(/\D/g, "")
|
||||||
|
if (modoEfetivo.value === "dataHora") {
|
||||||
|
if (digitos.length >= 10) {
|
||||||
|
horaTemp.value = String(
|
||||||
|
Math.min(23, Number.parseInt(digitos.slice(8, 10), 10) || 0),
|
||||||
|
).padStart(2, "0")
|
||||||
|
}
|
||||||
|
if (digitos.length >= 12) {
|
||||||
|
minutoTemp.value = String(
|
||||||
|
Math.min(59, Number.parseInt(digitos.slice(10, 12), 10) || 0),
|
||||||
|
).padStart(2, "0")
|
||||||
|
}
|
||||||
|
}
|
||||||
emitirValor(iso)
|
emitirValor(iso)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue