feat: melhoria componentes de data

This commit is contained in:
MarcioJRGodoi 2026-06-24 13:51:05 -03:00
parent 84383c3bbf
commit 642ccf4c1a
21 changed files with 6888 additions and 4607 deletions

8640
dist/eli-vue.es.js vendored

File diff suppressed because one or more lines are too long

76
dist/eli-vue.umd.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,231 +0,0 @@
import { type PropType } from "vue";
import type { CampoDensidade, CampoVariante } from "../../tipos";
import type { PadroesEntradas } from "./tiposEntradas";
type EntradaDataHora = PadroesEntradas["dataHora"];
type PropsAntigas = {
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;
};
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
value: {
type: PropType<EntradaDataHora["value"]>;
default: undefined;
};
opcoes: {
type: PropType<EntradaDataHora["opcoes"]>;
required: false;
default: undefined;
};
modelValue: {
type: PropType<string | null>;
default: null;
};
modo: {
type: PropType<PropsAntigas["modo"]>;
default: undefined;
};
rotulo: {
type: StringConstructor;
default: undefined;
};
placeholder: {
type: StringConstructor;
default: undefined;
};
desabilitado: {
type: BooleanConstructor;
default: undefined;
};
limpavel: {
type: BooleanConstructor;
default: undefined;
};
erro: {
type: BooleanConstructor;
default: undefined;
};
mensagensErro: {
type: PropType<string | string[]>;
default: undefined;
};
dica: {
type: StringConstructor;
default: undefined;
};
dicaPersistente: {
type: BooleanConstructor;
default: undefined;
};
densidade: {
type: PropType<CampoDensidade>;
default: undefined;
};
variante: {
type: PropType<CampoVariante>;
default: undefined;
};
min: {
type: PropType<string | undefined>;
default: undefined;
};
max: {
type: PropType<string | undefined>;
default: undefined;
};
}>, {
attrs: import("vue").Attrs;
valor: import("vue").WritableComputedRef<string, string>;
tipoInput: import("vue").ComputedRef<"date" | "datetime-local">;
minLocal: import("vue").ComputedRef<string | undefined>;
maxLocal: import("vue").ComputedRef<string | undefined>;
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("../../tipos").CampoDensidade;
variante?: import("../../tipos").CampoVariante;
}>;
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
emitCompatFocus: () => void;
emitCompatBlur: () => void;
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
"update:value": (_v: string | null) => true;
input: (_v: string | null) => true;
change: (_v: string | null) => true;
"update:modelValue": (_v: string | null) => true;
alterar: (_v: string | null) => true;
foco: () => true;
desfoco: () => true;
focus: () => true;
blur: () => true;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
value: {
type: PropType<EntradaDataHora["value"]>;
default: undefined;
};
opcoes: {
type: PropType<EntradaDataHora["opcoes"]>;
required: false;
default: undefined;
};
modelValue: {
type: PropType<string | null>;
default: null;
};
modo: {
type: PropType<PropsAntigas["modo"]>;
default: undefined;
};
rotulo: {
type: StringConstructor;
default: undefined;
};
placeholder: {
type: StringConstructor;
default: undefined;
};
desabilitado: {
type: BooleanConstructor;
default: undefined;
};
limpavel: {
type: BooleanConstructor;
default: undefined;
};
erro: {
type: BooleanConstructor;
default: undefined;
};
mensagensErro: {
type: PropType<string | string[]>;
default: undefined;
};
dica: {
type: StringConstructor;
default: undefined;
};
dicaPersistente: {
type: BooleanConstructor;
default: undefined;
};
densidade: {
type: PropType<CampoDensidade>;
default: undefined;
};
variante: {
type: PropType<CampoVariante>;
default: undefined;
};
min: {
type: PropType<string | undefined>;
default: undefined;
};
max: {
type: PropType<string | undefined>;
default: undefined;
};
}>> & Readonly<{
onInput?: ((_v: string | null) => any) | undefined;
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
onBlur?: (() => any) | undefined;
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
onChange?: ((_v: string | null) => any) | undefined;
onFocus?: (() => any) | undefined;
onAlterar?: ((_v: string | null) => any) | undefined;
onFoco?: (() => any) | undefined;
onDesfoco?: (() => any) | undefined;
}>, {
modo: "data" | "dataHora" | undefined;
limpavel: boolean;
erro: boolean;
mensagensErro: string | string[];
dica: string;
dicaPersistente: boolean;
min: string | undefined;
max: string | undefined;
densidade: CampoDensidade;
variante: CampoVariante;
desabilitado: boolean;
opcoes: {
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("../../tipos").CampoDensidade;
variante?: import("../../tipos").CampoVariante;
};
value: string | null | undefined;
modelValue: string | null;
rotulo: string;
placeholder: string;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,144 @@
import type dayjs from "dayjs";
import { type PropType } from "vue";
import type { CelulaCalendario } from "./useDataHora";
import "./Calendario.css";
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
uid: {
type: NumberConstructor;
required: true;
};
aberto: {
type: BooleanConstructor;
required: true;
};
estiloPopover: {
type: PropType<Record<string, string>>;
required: true;
};
rotulo: {
type: StringConstructor;
required: true;
};
modo: {
type: PropType<"data" | "dataHora">;
required: true;
};
nomeMesAtual: {
type: StringConstructor;
required: true;
};
anoAtual: {
type: NumberConstructor;
required: true;
};
mesAtual: {
type: NumberConstructor;
required: true;
};
listaAnos: {
type: PropType<number[]>;
required: true;
};
celulas: {
type: PropType<CelulaCalendario[]>;
required: true;
};
diaSelecionado: {
type: PropType<dayjs.Dayjs | null>;
default: null;
};
horaTemp: {
type: StringConstructor;
required: true;
};
minutoTemp: {
type: StringConstructor;
required: true;
};
}>, {
diasSemana: string[];
MESES: string[];
onMesSelectChange: (e: Event) => void;
onAnoSelectChange: (e: Event) => void;
onHoraInput: (e: Event) => void;
onHoraBlur: () => void;
onHoraKeydown: (e: KeyboardEvent) => void;
onMinutoInput: (e: Event) => void;
onMinutoBlur: () => void;
onMinutoKeydown: (e: KeyboardEvent) => void;
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
fechar: () => true;
confirmar: () => true;
navegarMes: (_delta: number) => true;
selecionarDia: (_cell: CelulaCalendario) => true;
mudarMes: (_mes: number) => true;
mudarAno: (_ano: number) => true;
"update:horaTemp": (_v: string) => true;
"update:minutoTemp": (_v: string) => true;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
uid: {
type: NumberConstructor;
required: true;
};
aberto: {
type: BooleanConstructor;
required: true;
};
estiloPopover: {
type: PropType<Record<string, string>>;
required: true;
};
rotulo: {
type: StringConstructor;
required: true;
};
modo: {
type: PropType<"data" | "dataHora">;
required: true;
};
nomeMesAtual: {
type: StringConstructor;
required: true;
};
anoAtual: {
type: NumberConstructor;
required: true;
};
mesAtual: {
type: NumberConstructor;
required: true;
};
listaAnos: {
type: PropType<number[]>;
required: true;
};
celulas: {
type: PropType<CelulaCalendario[]>;
required: true;
};
diaSelecionado: {
type: PropType<dayjs.Dayjs | null>;
default: null;
};
horaTemp: {
type: StringConstructor;
required: true;
};
minutoTemp: {
type: StringConstructor;
required: true;
};
}>> & Readonly<{
onFechar?: (() => any) | undefined;
onConfirmar?: (() => any) | undefined;
onNavegarMes?: ((_delta: number) => any) | undefined;
onSelecionarDia?: ((_cell: CelulaCalendario) => any) | undefined;
onMudarMes?: ((_mes: number) => any) | undefined;
onMudarAno?: ((_ano: number) => any) | undefined;
"onUpdate:horaTemp"?: ((_v: string) => any) | undefined;
"onUpdate:minutoTemp"?: ((_v: string) => any) | undefined;
}>, {
diaSelecionado: dayjs.Dayjs | null;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,13 @@
import dayjs from "dayjs";
import type { CelulaCalendario, EntradaDataHora } from "./useDataHora";
/**
* 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.
*/
export declare function gerarCelulas(referencia: dayjs.Dayjs, diaSelecionado: dayjs.Dayjs | null, min?: string, max?: string): CelulaCalendario[];
/**
* Formata um valor ISO para exibição no campo.
* @param valor - string ISO 8601 ou null
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
*/
export declare function formatarValor(valor: string | null, modo: EntradaDataHora["opcoes"]["modo"]): string;

View file

@ -0,0 +1,700 @@
import { type PropType } from "vue";
import type { CampoDensidade, CampoVariante } from "../../../tipos";
import "./EliEntradaDataHora.css";
import type { EntradaDataHora } from "./useDataHora";
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
value: {
type: PropType<EntradaDataHora["value"]>;
default: undefined;
};
opcoes: {
type: PropType<EntradaDataHora["opcoes"]>;
required: false;
default: undefined;
};
modelValue: {
type: PropType<string | null>;
default: null;
};
modo: {
type: PropType<"data" | "dataHora">;
default: undefined;
};
rotulo: {
type: StringConstructor;
default: undefined;
};
placeholder: {
type: StringConstructor;
default: undefined;
};
desabilitado: {
type: BooleanConstructor;
default: undefined;
};
limpavel: {
type: BooleanConstructor;
default: undefined;
};
erro: {
type: BooleanConstructor;
default: undefined;
};
mensagensErro: {
type: PropType<string | string[]>;
default: undefined;
};
dica: {
type: StringConstructor;
default: undefined;
};
dicaPersistente: {
type: BooleanConstructor;
default: undefined;
};
densidade: {
type: PropType<CampoDensidade>;
default: undefined;
};
variante: {
type: PropType<CampoVariante>;
default: undefined;
};
min: {
type: PropType<string | undefined>;
default: undefined;
};
max: {
type: PropType<string | undefined>;
default: undefined;
};
}>, {
uid: number;
aberto: import("vue").Ref<boolean, boolean>;
estiloPopover: import("vue").Ref<Record<string, string>, Record<string, string>>;
referencia: import("vue").Ref<{
clone: () => import("dayjs").Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): import("dayjs").Dayjs;
};
month: {
(): number;
(value: number): import("dayjs").Dayjs;
};
date: {
(): number;
(value: number): import("dayjs").Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): import("dayjs").Dayjs;
};
hour: {
(): number;
(value: number): import("dayjs").Dayjs;
};
minute: {
(): number;
(value: number): import("dayjs").Dayjs;
};
second: {
(): number;
(value: number): import("dayjs").Dayjs;
};
millisecond: {
(): number;
(value: number): import("dayjs").Dayjs;
};
set: (unit: import("dayjs").UnitType, value: number) => import("dayjs").Dayjs;
get: (unit: import("dayjs").UnitType) => number;
add: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
subtract: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
startOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
endOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
format: (template?: string) => string;
diff: (date?: import("dayjs").ConfigType, unit?: import("dayjs").QUnitType | import("dayjs").OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): import("dayjs").Dayjs;
};
isBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSame: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): import("dayjs").Dayjs;
};
isSameOrAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSameOrBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => import("dayjs").Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => import("dayjs").Dayjs;
local: () => import("dayjs").Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): import("dayjs").Dayjs;
};
}, import("dayjs").Dayjs | {
clone: () => import("dayjs").Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): import("dayjs").Dayjs;
};
month: {
(): number;
(value: number): import("dayjs").Dayjs;
};
date: {
(): number;
(value: number): import("dayjs").Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): import("dayjs").Dayjs;
};
hour: {
(): number;
(value: number): import("dayjs").Dayjs;
};
minute: {
(): number;
(value: number): import("dayjs").Dayjs;
};
second: {
(): number;
(value: number): import("dayjs").Dayjs;
};
millisecond: {
(): number;
(value: number): import("dayjs").Dayjs;
};
set: (unit: import("dayjs").UnitType, value: number) => import("dayjs").Dayjs;
get: (unit: import("dayjs").UnitType) => number;
add: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
subtract: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
startOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
endOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
format: (template?: string) => string;
diff: (date?: import("dayjs").ConfigType, unit?: import("dayjs").QUnitType | import("dayjs").OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): import("dayjs").Dayjs;
};
isBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSame: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): import("dayjs").Dayjs;
};
isSameOrAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSameOrBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => import("dayjs").Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => import("dayjs").Dayjs;
local: () => import("dayjs").Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): import("dayjs").Dayjs;
};
}>;
diaSelecionado: import("vue").Ref<{
clone: () => import("dayjs").Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): import("dayjs").Dayjs;
};
month: {
(): number;
(value: number): import("dayjs").Dayjs;
};
date: {
(): number;
(value: number): import("dayjs").Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): import("dayjs").Dayjs;
};
hour: {
(): number;
(value: number): import("dayjs").Dayjs;
};
minute: {
(): number;
(value: number): import("dayjs").Dayjs;
};
second: {
(): number;
(value: number): import("dayjs").Dayjs;
};
millisecond: {
(): number;
(value: number): import("dayjs").Dayjs;
};
set: (unit: import("dayjs").UnitType, value: number) => import("dayjs").Dayjs;
get: (unit: import("dayjs").UnitType) => number;
add: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
subtract: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
startOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
endOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
format: (template?: string) => string;
diff: (date?: import("dayjs").ConfigType, unit?: import("dayjs").QUnitType | import("dayjs").OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): import("dayjs").Dayjs;
};
isBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSame: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): import("dayjs").Dayjs;
};
isSameOrAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSameOrBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => import("dayjs").Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => import("dayjs").Dayjs;
local: () => import("dayjs").Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): import("dayjs").Dayjs;
};
} | null, import("dayjs").Dayjs | {
clone: () => import("dayjs").Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): import("dayjs").Dayjs;
};
month: {
(): number;
(value: number): import("dayjs").Dayjs;
};
date: {
(): number;
(value: number): import("dayjs").Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): import("dayjs").Dayjs;
};
hour: {
(): number;
(value: number): import("dayjs").Dayjs;
};
minute: {
(): number;
(value: number): import("dayjs").Dayjs;
};
second: {
(): number;
(value: number): import("dayjs").Dayjs;
};
millisecond: {
(): number;
(value: number): import("dayjs").Dayjs;
};
set: (unit: import("dayjs").UnitType, value: number) => import("dayjs").Dayjs;
get: (unit: import("dayjs").UnitType) => number;
add: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
subtract: {
(value: number, unit?: import("dayjs").ManipulateType): import("dayjs").Dayjs;
(duration: plugin.Duration): import("dayjs").Dayjs;
};
startOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
endOf: (unit: import("dayjs").OpUnitType) => import("dayjs").Dayjs;
format: (template?: string) => string;
diff: (date?: import("dayjs").ConfigType, unit?: import("dayjs").QUnitType | import("dayjs").OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): import("dayjs").Dayjs;
};
isBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSame: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): import("dayjs").Dayjs;
};
isSameOrAfter: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
isSameOrBefore: (date?: import("dayjs").ConfigType, unit?: import("dayjs").OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: import("dayjs").ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => import("dayjs").Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => import("dayjs").Dayjs;
local: () => import("dayjs").Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): import("dayjs").Dayjs;
};
} | null>;
horaTemp: import("vue").Ref<string, string>;
minutoTemp: import("vue").Ref<string, string>;
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("../../../tipos").CampoDensidade;
variante?: import("../../../tipos").CampoVariante;
}>;
modoEfetivo: import("vue").ComputedRef<"data" | "dataHora">;
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
valor: import("vue").ComputedRef<string | null>;
valorFormatado: import("vue").ComputedRef<string>;
mensagensVisiveis: import("vue").ComputedRef<string[]>;
nomeMesAtual: import("vue").ComputedRef<string>;
anoAtual: import("vue").ComputedRef<number>;
mesAtual: import("vue").ComputedRef<number>;
listaAnos: import("vue").ComputedRef<number[]>;
mudarMes: (mes: number) => void;
mudarAno: (ano: number) => void;
celulas: import("vue").ComputedRef<import("./useDataHora").CelulaCalendario[]>;
abrirSeHabilitado: () => void;
fechar: () => void;
navegarMes: (delta: number) => void;
selecionarDia: (cell: import("./useDataHora").CelulaCalendario) => void;
confirmar: () => void;
limpar: () => void;
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
"update:value": (_v: string | null) => true;
input: (_v: string | null) => true;
change: (_v: string | null) => true;
"update:modelValue": (_v: string | null) => true;
alterar: (_v: string | null) => true;
foco: () => true;
desfoco: () => true;
focus: () => true;
blur: () => true;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
value: {
type: PropType<EntradaDataHora["value"]>;
default: undefined;
};
opcoes: {
type: PropType<EntradaDataHora["opcoes"]>;
required: false;
default: undefined;
};
modelValue: {
type: PropType<string | null>;
default: null;
};
modo: {
type: PropType<"data" | "dataHora">;
default: undefined;
};
rotulo: {
type: StringConstructor;
default: undefined;
};
placeholder: {
type: StringConstructor;
default: undefined;
};
desabilitado: {
type: BooleanConstructor;
default: undefined;
};
limpavel: {
type: BooleanConstructor;
default: undefined;
};
erro: {
type: BooleanConstructor;
default: undefined;
};
mensagensErro: {
type: PropType<string | string[]>;
default: undefined;
};
dica: {
type: StringConstructor;
default: undefined;
};
dicaPersistente: {
type: BooleanConstructor;
default: undefined;
};
densidade: {
type: PropType<CampoDensidade>;
default: undefined;
};
variante: {
type: PropType<CampoVariante>;
default: undefined;
};
min: {
type: PropType<string | undefined>;
default: undefined;
};
max: {
type: PropType<string | undefined>;
default: undefined;
};
}>> & Readonly<{
onInput?: ((_v: string | null) => any) | undefined;
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
onBlur?: (() => any) | undefined;
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
onChange?: ((_v: string | null) => any) | undefined;
onFocus?: (() => any) | undefined;
onFoco?: (() => any) | undefined;
onDesfoco?: (() => any) | undefined;
onAlterar?: ((_v: string | null) => any) | undefined;
}>, {
modo: "data" | "dataHora";
limpavel: boolean;
erro: boolean;
mensagensErro: string | string[];
dica: string;
dicaPersistente: boolean;
min: string | undefined;
max: string | undefined;
densidade: CampoDensidade;
variante: CampoVariante;
desabilitado: boolean;
opcoes: {
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("../../../tipos").CampoDensidade;
variante?: import("../../../tipos").CampoVariante;
};
value: string | null | undefined;
modelValue: string | null;
rotulo: string;
placeholder: string;
}, {}, {
Calendario: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
uid: {
type: NumberConstructor;
required: true;
};
aberto: {
type: BooleanConstructor;
required: true;
};
estiloPopover: {
type: PropType<Record<string, string>>;
required: true;
};
rotulo: {
type: StringConstructor;
required: true;
};
modo: {
type: PropType<"data" | "dataHora">;
required: true;
};
nomeMesAtual: {
type: StringConstructor;
required: true;
};
anoAtual: {
type: NumberConstructor;
required: true;
};
mesAtual: {
type: NumberConstructor;
required: true;
};
listaAnos: {
type: PropType<number[]>;
required: true;
};
celulas: {
type: PropType<import("./useDataHora").CelulaCalendario[]>;
required: true;
};
diaSelecionado: {
type: PropType<import("dayjs").Dayjs | null>;
default: null;
};
horaTemp: {
type: StringConstructor;
required: true;
};
minutoTemp: {
type: StringConstructor;
required: true;
};
}>, {
diasSemana: string[];
MESES: string[];
onMesSelectChange: (e: Event) => void;
onAnoSelectChange: (e: Event) => void;
onHoraInput: (e: Event) => void;
onHoraBlur: () => void;
onHoraKeydown: (e: KeyboardEvent) => void;
onMinutoInput: (e: Event) => void;
onMinutoBlur: () => void;
onMinutoKeydown: (e: KeyboardEvent) => void;
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
fechar: () => true;
confirmar: () => true;
navegarMes: (_delta: number) => true;
selecionarDia: (_cell: import("./useDataHora").CelulaCalendario) => true;
mudarMes: (_mes: number) => true;
mudarAno: (_ano: number) => true;
"update:horaTemp": (_v: string) => true;
"update:minutoTemp": (_v: string) => true;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
uid: {
type: NumberConstructor;
required: true;
};
aberto: {
type: BooleanConstructor;
required: true;
};
estiloPopover: {
type: PropType<Record<string, string>>;
required: true;
};
rotulo: {
type: StringConstructor;
required: true;
};
modo: {
type: PropType<"data" | "dataHora">;
required: true;
};
nomeMesAtual: {
type: StringConstructor;
required: true;
};
anoAtual: {
type: NumberConstructor;
required: true;
};
mesAtual: {
type: NumberConstructor;
required: true;
};
listaAnos: {
type: PropType<number[]>;
required: true;
};
celulas: {
type: PropType<import("./useDataHora").CelulaCalendario[]>;
required: true;
};
diaSelecionado: {
type: PropType<import("dayjs").Dayjs | null>;
default: null;
};
horaTemp: {
type: StringConstructor;
required: true;
};
minutoTemp: {
type: StringConstructor;
required: true;
};
}>> & Readonly<{
onFechar?: (() => any) | undefined;
onConfirmar?: (() => any) | undefined;
onNavegarMes?: ((_delta: number) => any) | undefined;
onSelecionarDia?: ((_cell: import("./useDataHora").CelulaCalendario) => any) | undefined;
onMudarMes?: ((_mes: number) => any) | undefined;
onMudarAno?: ((_ano: number) => any) | undefined;
"onUpdate:horaTemp"?: ((_v: string) => any) | undefined;
"onUpdate:minutoTemp"?: ((_v: string) => any) | undefined;
}>, {
diaSelecionado: import("dayjs").Dayjs | null;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,408 @@
import dayjs from "dayjs";
import "dayjs/locale/pt-br";
import type { CampoDensidade, CampoVariante } from "../../../tipos";
import type { PadroesEntradas } from "../tiposEntradas";
export type EntradaDataHora = PadroesEntradas["dataHora"];
export interface CelulaCalendario {
dia: number;
data: dayjs.Dayjs;
atual: boolean;
hoje: boolean;
selecionado: 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) */
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;
};
/** Composable principal do EliEntradaDataHora. */
export declare function useDataHora(props: Props, emit: EmitFn): {
uid: number;
aberto: import("vue").Ref<boolean, boolean>;
estiloPopover: import("vue").Ref<Record<string, string>, Record<string, string>>;
referencia: import("vue").Ref<{
clone: () => dayjs.Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): dayjs.Dayjs;
};
month: {
(): number;
(value: number): dayjs.Dayjs;
};
date: {
(): number;
(value: number): dayjs.Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): dayjs.Dayjs;
};
hour: {
(): number;
(value: number): dayjs.Dayjs;
};
minute: {
(): number;
(value: number): dayjs.Dayjs;
};
second: {
(): number;
(value: number): dayjs.Dayjs;
};
millisecond: {
(): number;
(value: number): dayjs.Dayjs;
};
set: (unit: dayjs.UnitType, value: number) => dayjs.Dayjs;
get: (unit: dayjs.UnitType) => number;
add: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
subtract: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
startOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
endOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
format: (template?: string) => string;
diff: (date?: dayjs.ConfigType, unit?: dayjs.QUnitType | dayjs.OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): dayjs.Dayjs;
};
isBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSame: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): dayjs.Dayjs;
};
isSameOrAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSameOrBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => dayjs.Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => dayjs.Dayjs;
local: () => dayjs.Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): dayjs.Dayjs;
};
}, dayjs.Dayjs | {
clone: () => dayjs.Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): dayjs.Dayjs;
};
month: {
(): number;
(value: number): dayjs.Dayjs;
};
date: {
(): number;
(value: number): dayjs.Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): dayjs.Dayjs;
};
hour: {
(): number;
(value: number): dayjs.Dayjs;
};
minute: {
(): number;
(value: number): dayjs.Dayjs;
};
second: {
(): number;
(value: number): dayjs.Dayjs;
};
millisecond: {
(): number;
(value: number): dayjs.Dayjs;
};
set: (unit: dayjs.UnitType, value: number) => dayjs.Dayjs;
get: (unit: dayjs.UnitType) => number;
add: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
subtract: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
startOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
endOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
format: (template?: string) => string;
diff: (date?: dayjs.ConfigType, unit?: dayjs.QUnitType | dayjs.OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): dayjs.Dayjs;
};
isBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSame: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): dayjs.Dayjs;
};
isSameOrAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSameOrBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => dayjs.Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => dayjs.Dayjs;
local: () => dayjs.Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): dayjs.Dayjs;
};
}>;
diaSelecionado: import("vue").Ref<{
clone: () => dayjs.Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): dayjs.Dayjs;
};
month: {
(): number;
(value: number): dayjs.Dayjs;
};
date: {
(): number;
(value: number): dayjs.Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): dayjs.Dayjs;
};
hour: {
(): number;
(value: number): dayjs.Dayjs;
};
minute: {
(): number;
(value: number): dayjs.Dayjs;
};
second: {
(): number;
(value: number): dayjs.Dayjs;
};
millisecond: {
(): number;
(value: number): dayjs.Dayjs;
};
set: (unit: dayjs.UnitType, value: number) => dayjs.Dayjs;
get: (unit: dayjs.UnitType) => number;
add: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
subtract: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
startOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
endOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
format: (template?: string) => string;
diff: (date?: dayjs.ConfigType, unit?: dayjs.QUnitType | dayjs.OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): dayjs.Dayjs;
};
isBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSame: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): dayjs.Dayjs;
};
isSameOrAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSameOrBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => dayjs.Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => dayjs.Dayjs;
local: () => dayjs.Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): dayjs.Dayjs;
};
} | null, dayjs.Dayjs | {
clone: () => dayjs.Dayjs;
isValid: () => boolean;
year: {
(): number;
(value: number): dayjs.Dayjs;
};
month: {
(): number;
(value: number): dayjs.Dayjs;
};
date: {
(): number;
(value: number): dayjs.Dayjs;
};
day: {
(): 0 | 1 | 2 | 3 | 4 | 5 | 6;
(value: number): dayjs.Dayjs;
};
hour: {
(): number;
(value: number): dayjs.Dayjs;
};
minute: {
(): number;
(value: number): dayjs.Dayjs;
};
second: {
(): number;
(value: number): dayjs.Dayjs;
};
millisecond: {
(): number;
(value: number): dayjs.Dayjs;
};
set: (unit: dayjs.UnitType, value: number) => dayjs.Dayjs;
get: (unit: dayjs.UnitType) => number;
add: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
subtract: {
(value: number, unit?: dayjs.ManipulateType): dayjs.Dayjs;
(duration: plugin.Duration): dayjs.Dayjs;
};
startOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
endOf: (unit: dayjs.OpUnitType) => dayjs.Dayjs;
format: (template?: string) => string;
diff: (date?: dayjs.ConfigType, unit?: dayjs.QUnitType | dayjs.OpUnitType, float?: boolean) => number;
valueOf: () => number;
unix: () => number;
daysInMonth: () => number;
toDate: () => Date;
toJSON: () => string;
toISOString: () => string;
toString: () => string;
utcOffset: {
(): number;
(offset: number | string, keepLocalTime?: boolean): dayjs.Dayjs;
};
isBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSame: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
locale: {
(): string;
(preset: string | ILocale, object?: Partial<ILocale>): dayjs.Dayjs;
};
isSameOrAfter: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
isSameOrBefore: (date?: dayjs.ConfigType, unit?: dayjs.OpUnitType) => boolean;
fromNow: (withoutSuffix?: boolean) => string;
from: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
toNow: (withoutSuffix?: boolean) => string;
to: (compared: dayjs.ConfigType, withoutSuffix?: boolean) => string;
tz: (timezone?: string, keepLocalTime?: boolean) => dayjs.Dayjs;
offsetName: (type?: "short" | "long") => string | undefined;
utc: (keepLocalTime?: boolean) => dayjs.Dayjs;
local: () => dayjs.Dayjs;
isUTC: () => boolean;
week: {
(): number;
(value: number): dayjs.Dayjs;
};
} | null>;
horaTemp: import("vue").Ref<string, string>;
minutoTemp: import("vue").Ref<string, string>;
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("../../../tipos").CampoDensidade;
variante?: import("../../../tipos").CampoVariante;
}>;
modoEfetivo: import("vue").ComputedRef<"data" | "dataHora">;
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
valor: import("vue").ComputedRef<string | null>;
valorFormatado: import("vue").ComputedRef<string>;
mensagensVisiveis: import("vue").ComputedRef<string[]>;
nomeMesAtual: import("vue").ComputedRef<string>;
anoAtual: import("vue").ComputedRef<number>;
mesAtual: import("vue").ComputedRef<number>;
listaAnos: import("vue").ComputedRef<number[]>;
mudarMes: (mes: number) => void;
mudarAno: (ano: number) => void;
celulas: import("vue").ComputedRef<CelulaCalendario[]>;
abrirSeHabilitado: () => void;
fechar: () => void;
navegarMes: (delta: number) => void;
selecionarDia: (cell: CelulaCalendario) => void;
confirmar: () => void;
limpar: () => void;
};
export {};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue";
import EliEntradaCoordenadas from "./EliEntradaCoordenadas.vue";
import EliEntradaDataHora from "./EliEntradaDataHora.vue";
import EliEntradaDataHora from "./EliEntradaDataHora/index.vue";
import EliEntradaNumero from "./EliEntradaNumero.vue";
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue";
import EliEntradaSelecao from "./EliEntradaSelecao.vue";

View file

@ -1,6 +1,6 @@
{
"name": "eli-vue",
"version": "0.1.217",
"version": "0.1.218",
"private": false,
"main": "./dist/eli-vue.umd.js",
"module": "./dist/eli-vue.es.js",

8
pnpm-lock.yaml generated
View file

@ -20,9 +20,6 @@ importers:
dayjs:
specifier: ^1.11.19
version: 1.11.21
leaflet:
specifier: ^1.9.4
version: 1.9.4
p-comuns:
specifier: git+https://git2.idz.one/publico/_comuns.git
version: git+https://git2.idz.one/publico/_comuns.git#1e219ad6570b314d7abd26bb725a03f8c8337be1(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3)
@ -48,11 +45,14 @@ importers:
'@vitejs/plugin-vue':
specifier: ^5.0.0
version: 5.2.4(vite@6.4.3(@types/node@25.9.2)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))
leaflet:
specifier: ^1.9.4
version: 1.9.4
sass:
specifier: ^1.94.2
version: 1.100.0
typescript:
specifier: ^6.0.3
specifier: ^6
version: 6.0.3
vite:
specifier: ^6.0.0

View file

@ -1,223 +0,0 @@
<template>
<div class="eli-data-hora">
<v-text-field
v-model="valor"
lang="pt-BR"
:type="tipoInput"
:label="opcoesEfetivas.rotulo"
:placeholder="opcoesEfetivas.placeholder"
:disabled="desabilitadoEfetivo"
:clearable="Boolean(opcoesEfetivas.limpavel)"
:error="Boolean(opcoesEfetivas.erro)"
:error-messages="opcoesEfetivas.mensagensErro"
:hint="opcoesEfetivas.dica"
:persistent-hint="Boolean(opcoesEfetivas.dicaPersistente)"
:density="opcoesEfetivas.densidade ?? 'comfortable'"
:variant="opcoesEfetivas.variante ?? 'outlined'"
:min="minLocal"
:max="maxLocal"
:readonly="Boolean(opcoesEfetivas.apenasLeitura)"
v-bind="attrs"
@focus="emitCompatFocus"
@blur="emitCompatBlur"
/>
</div>
</template>
<script lang="ts">
import dayjs from "dayjs"
import { computed, defineComponent, type PropType } from "vue"
import type { CampoDensidade, CampoVariante } from "../../tipos"
import type { PadroesEntradas } from "./tiposEntradas"
type EntradaDataHora = PadroesEntradas["dataHora"]
type PropsAntigas = {
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
}
export default defineComponent({
name: "EliEntradaDataHora",
inheritAttrs: false,
props: {
// --- Novo padrão EliEntrada ---
value: {
type: String as PropType<EntradaDataHora["value"]>,
default: undefined,
},
opcoes: {
type: Object as PropType<EntradaDataHora["opcoes"]>,
required: false,
default: undefined,
},
// --- Compatibilidade com componente antigo EliDataHora ---
modelValue: {
type: String as PropType<string | null>,
default: null,
},
modo: {
type: String as PropType<PropsAntigas["modo"]>,
default: undefined,
},
rotulo: { type: String, default: undefined },
placeholder: { type: String, default: undefined },
desabilitado: { type: Boolean, default: undefined },
limpavel: { type: Boolean, default: undefined },
erro: { type: Boolean, default: undefined },
mensagensErro: {
type: [String, Array] as PropType<string | string[]>,
default: undefined,
},
dica: { type: String, default: undefined },
dicaPersistente: { type: Boolean, default: undefined },
densidade: { type: String as PropType<CampoDensidade>, default: undefined },
variante: { type: String as PropType<CampoVariante>, default: undefined },
min: { type: String as PropType<string | undefined>, default: undefined },
max: { type: String as PropType<string | undefined>, default: undefined },
},
emits: {
// Novo padrão
"update:value": (_v: string | null) => true,
input: (_v: string | null) => true, // compat Vue2
change: (_v: string | null) => true,
// Compat antigo
"update:modelValue": (_v: string | null) => true,
alterar: (_v: string | null) => true,
foco: () => true,
desfoco: () => true,
focus: () => true,
blur: () => true,
},
setup(props, { emit, attrs }) {
const opcoesEfetivas = computed<EntradaDataHora["opcoes"]>(() => {
// 1) se veio `opcoes` (novo), usa
if (props.opcoes) return props.opcoes
// 2) fallback: constrói a partir das props antigas
return {
rotulo: props.rotulo ?? "Data e hora",
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 tipoInput = computed<"date" | "datetime-local">(() =>
modoEfetivo.value === "data" ? "date" : "datetime-local",
)
function isoParaInputDatetime(valorIso: string): string {
if (modoEfetivo.value === "data") {
return dayjs(valorIso).format("YYYY-MM-DD")
}
return dayjs(valorIso).format("YYYY-MM-DDTHH:mm")
}
function inputDatetimeParaIsoLocal(valorInput: string): string {
if (modoEfetivo.value === "data") {
return dayjs(`${valorInput}T00:00`).format()
}
return dayjs(valorInput).format()
}
const effectiveModelValue = computed<string | null>(() => {
// Prioridade: value (novo) se vier definido; senão usa modelValue (antigo)
return props.value !== undefined ? (props.value ?? null) : props.modelValue
})
const valor = computed<string>({
get: () => {
if (!effectiveModelValue.value) return ""
return isoParaInputDatetime(effectiveModelValue.value)
},
set: (v) => {
const normalizado = v && v.length > 0 ? v : null
if (!normalizado) {
emit("update:value", null)
emit("input", null)
emit("change", null)
emit("update:modelValue", null)
emit("alterar", null)
return
}
const valorEmitido = inputDatetimeParaIsoLocal(normalizado)
emit("update:value", valorEmitido)
emit("input", valorEmitido)
emit("change", valorEmitido)
emit("update:modelValue", valorEmitido)
emit("alterar", valorEmitido)
},
})
const minLocal = computed<string | undefined>(() => {
const min = opcoesEfetivas.value.min
if (!min) return undefined
return isoParaInputDatetime(min)
})
const maxLocal = computed<string | undefined>(() => {
const max = opcoesEfetivas.value.max
if (!max) return undefined
return isoParaInputDatetime(max)
})
function emitCompatFocus() {
emit("foco")
emit("focus")
}
function emitCompatBlur() {
emit("desfoco")
emit("blur")
}
return {
attrs,
valor,
tipoInput,
minLocal,
maxLocal,
opcoesEfetivas,
desabilitadoEfetivo,
emitCompatFocus,
emitCompatBlur,
}
},
})
</script>
<style>
.eli-data-hora {
width: 100%;
}
</style>

View file

@ -0,0 +1,228 @@
/* ─────────────────────────────────────────────
EliEntradaDataHora — Popover do calendário
───────────────────────────────────────────── */
.eli-dh__popover {
position: absolute;
top: calc(100% + 6px);
left: 0;
z-index: 2000;
background: var(--cor-eli-superficie, #fff);
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
border-radius: 14px;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.06),
0 10px 30px -4px rgba(0, 0, 0, 0.12);
overflow: hidden;
min-width: 300px;
transform-origin: top left;
}
.eli-dh-pop-enter-active,
.eli-dh-pop-leave-active { transition: opacity 0.16s ease, transform 0.16s ease; }
.eli-dh-pop-enter-from,
.eli-dh-pop-leave-to { opacity: 0; transform: translateY(-6px) scale(0.97); }
.eli-dh__cal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px 10px;
background: linear-gradient(135deg, var(--cor-eli-primaria, #688C00) 0%, var(--cor-eli-primaria-acao, #8AAF00) 100%);
}
.eli-dh__cal-nav {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: none;
background: rgba(255, 255, 255, 0.18);
border-radius: 8px;
cursor: pointer;
color: #fff;
transition: background 0.15s;
padding: 0;
}
.eli-dh__cal-nav:hover { background: rgba(255, 255, 255, 0.30); }
.eli-dh__cal-nav svg { width: 18px; height: 18px; }
.eli-dh__cal-titulo {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.eli-dh__seletores {
display: flex;
align-items: center;
gap: 6px;
}
.eli-dh__select {
border: none;
background: rgba(255, 255, 255, 0.14);
color: #fff;
font-family: inherit;
font-size: 14px;
font-weight: 700;
padding: 4px 8px;
border-radius: 6px;
cursor: pointer;
outline: none;
transition: background 0.15s;
appearance: none;
-webkit-appearance: none;
}
.eli-dh__select:hover {
background: rgba(255, 255, 255, 0.26);
}
.eli-dh__select option {
background: var(--cor-eli-superficie, #fff);
color: var(--cor-eli-neutro-escuro, #3E4436);
font-weight: 600;
}
.eli-dh__cal-body { padding: 12px 14px 14px; }
.eli-dh__cal-semana {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
margin-bottom: 6px;
}
.eli-dh__cal-dia-sem {
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
color: var(--cor-eli-neutro, #5E6553);
text-transform: uppercase;
letter-spacing: 0.04em;
height: 28px;
}
.eli-dh__cal-grade {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.eli-dh__cal-dia {
display: flex;
align-items: center;
justify-content: center;
height: 36px;
border: none;
border-radius: 8px;
background: none;
font-size: 13px;
font-weight: 500;
color: var(--cor-eli-neutro-escuro, #3E4436);
cursor: pointer;
transition: background 0.12s, color 0.12s;
padding: 0;
font-family: inherit;
}
.eli-dh__cal-dia:hover:not(:disabled):not(.eli-dh__cal-dia--fora):not(.eli-dh__cal-dia--selecionado) {
background: var(--cor-eli-primaria-bg, #EEF3D0);
color: var(--cor-eli-primaria, #688C00);
}
.eli-dh__cal-dia--fora { color: var(--cor-eli-borda-forte, #B5BAA8); cursor: default; }
.eli-dh__cal-dia--hoje {
color: var(--cor-eli-primaria, #688C00);
font-weight: 700;
position: relative;
}
.eli-dh__cal-dia--hoje::after {
content: "";
position: absolute;
bottom: 4px;
left: 50%;
transform: translateX(-50%);
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--cor-eli-primaria, #688C00);
}
.eli-dh__cal-dia--selecionado { background: var(--cor-eli-primaria, #688C00); color: #fff; font-weight: 700; }
.eli-dh__cal-dia--selecionado.eli-dh__cal-dia--hoje::after { background: #fff; }
.eli-dh__cal-dia--desabilitado { opacity: 0.32; cursor: not-allowed; }
.eli-dh__hora-secao {
border-top: 1px solid var(--cor-eli-borda, #D5D8CC);
padding: 12px 16px 14px;
display: flex;
align-items: center;
gap: 10px;
}
.eli-dh__hora-icone { width: 16px; height: 16px; color: var(--cor-eli-neutro, #5E6553); flex-shrink: 0; }
.eli-dh__hora-campos { display: flex; align-items: center; gap: 6px; flex: 1; }
.eli-dh__hora-input {
width: 52px;
padding: 6px 8px;
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
border-radius: 6px;
background: var(--cor-eli-superficie, #fff);
text-align: center;
font-size: 16px;
font-weight: 600;
font-family: inherit;
color: var(--cor-eli-neutro-escuro, #3E4436);
transition: border-color 0.15s;
outline: none;
-moz-appearance: textfield;
}
.eli-dh__hora-input::-webkit-inner-spin-button,
.eli-dh__hora-input::-webkit-outer-spin-button { -webkit-appearance: none; }
.eli-dh__hora-input:focus {
border-color: var(--cor-eli-primaria, #688C00);
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__rodape {
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;
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:hover { background: var(--cor-eli-borda, #D5D8CC); }
.eli-dh__btn--confirmar { background: var(--cor-eli-primaria, #688C00); color: #fff; }
.eli-dh__btn--confirmar:hover { background: var(--cor-eli-primaria-escura, #4A6500); }

View file

@ -0,0 +1,231 @@
<template>
<Teleport to="body">
<Transition name="eli-dh-pop">
<div
v-if="aberto"
:id="`eli-dh-popover-${uid}`"
class="eli-dh__popover"
:style="estiloPopover"
role="dialog"
:aria-label="`Selecionar ${rotulo}`"
@keydown.escape="$emit('fechar')"
@mousedown.stop
>
<!-- Cabeçalho -->
<div class="eli-dh__cal-header">
<button class="eli-dh__cal-nav" type="button" title="Mês anterior" aria-label="Mês anterior" @click="$emit('navegarMes', -1)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6" /></svg>
</button>
<div class="eli-dh__cal-titulo">
<div class="eli-dh__seletores">
<select
:value="mesAtual"
class="eli-dh__select"
aria-label="Selecionar mês"
@change="onMesSelectChange"
>
<option v-for="(m, idx) in MESES" :key="idx" :value="idx">
{{ m }}
</option>
</select>
<select
:value="anoAtual"
class="eli-dh__select"
aria-label="Selecionar ano"
@change="onAnoSelectChange"
>
<option v-for="ano in listaAnos" :key="ano" :value="ano">
{{ ano }}
</option>
</select>
</div>
</div>
<button class="eli-dh__cal-nav" type="button" title="Próximo mês" aria-label="Próximo mês" @click="$emit('navegarMes', 1)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6" /></svg>
</button>
</div>
<!-- Grade de dias -->
<div class="eli-dh__cal-body">
<div class="eli-dh__cal-semana" role="row">
<div v-for="ds in diasSemana" :key="ds" class="eli-dh__cal-dia-sem" role="columnheader">{{ ds }}</div>
</div>
<div class="eli-dh__cal-grade" role="grid">
<button
v-for="(cell, i) in celulas"
:key="i"
class="eli-dh__cal-dia"
type="button"
:class="{
'eli-dh__cal-dia--fora': !cell.atual,
'eli-dh__cal-dia--hoje': cell.hoje,
'eli-dh__cal-dia--selecionado': cell.selecionado,
'eli-dh__cal-dia--desabilitado': cell.desabilitado,
}"
:disabled="cell.desabilitado"
:tabindex="cell.selecionado ? 0 : -1"
:aria-selected="cell.selecionado"
:aria-label="`${cell.dia} de ${nomeMesAtual} de ${anoAtual}`"
@click="$emit('selecionarDia', cell)"
>
{{ cell.dia }}
</button>
</div>
</div>
<!-- Seção de hora -->
<div v-if="modo === 'dataHora'" class="eli-dh__hora-secao">
<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" />
</svg>
<div class="eli-dh__hora-campos">
<input
:id="`eli-dh-hora-${uid}`"
:value="horaTemp"
class="eli-dh__hora-input"
type="text"
inputmode="numeric"
maxlength="2"
placeholder="HH"
:aria-label="`Hora de ${rotulo}`"
@input="onHoraInput"
@blur="onHoraBlur"
@keydown="onHoraKeydown"
/>
<span class="eli-dh__hora-sep" aria-hidden="true">:</span>
<input
:id="`eli-dh-min-${uid}`"
:value="minutoTemp"
class="eli-dh__hora-input"
type="text"
inputmode="numeric"
maxlength="2"
placeholder="MM"
:aria-label="`Minuto de ${rotulo}`"
@input="onMinutoInput"
@blur="onMinutoBlur"
@keydown="onMinutoKeydown"
/>
</div>
</div>
<!-- Rodapé -->
<div class="eli-dh__rodape">
<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>
</div>
</div>
</Transition>
</Teleport>
</template>
<script lang="ts">
import type dayjs from "dayjs"
import { defineComponent, type PropType } from "vue"
import type { CelulaCalendario } from "./useDataHora"
import { diasSemana, MESES } from "./useDataHora"
import "./Calendario.css"
export default defineComponent({
name: "EliDhCalendario",
props: {
uid: { type: Number, required: true },
aberto: { type: Boolean, required: true },
estiloPopover: { type: Object as PropType<Record<string, string>>, required: true },
rotulo: { type: String, required: true },
modo: { type: String as PropType<"data" | "dataHora">, required: true },
nomeMesAtual: { type: String, required: true },
anoAtual: { type: Number, required: true },
mesAtual: { type: Number, required: true },
listaAnos: { type: Array as PropType<number[]>, required: true },
celulas: { type: Array as PropType<CelulaCalendario[]>, required: true },
diaSelecionado: { type: Object as PropType<dayjs.Dayjs | null>, default: null },
horaTemp: { type: String, required: true },
minutoTemp: { type: String, required: true },
},
emits: {
fechar: () => true,
confirmar: () => true,
navegarMes: (_delta: number) => true,
selecionarDia: (_cell: CelulaCalendario) => true,
mudarMes: (_mes: number) => true,
mudarAno: (_ano: number) => true,
"update:horaTemp": (_v: string) => true,
"update:minutoTemp": (_v: string) => true,
},
setup(props, { emit }) {
function onMesSelectChange(e: Event) {
const v = Number.parseInt((e.target as HTMLSelectElement).value, 10)
emit("mudarMes", v)
}
function onAnoSelectChange(e: Event) {
const v = Number.parseInt((e.target as HTMLSelectElement).value, 10)
emit("mudarAno", v)
}
function onHoraInput(e: Event) {
const raw = (e.target as HTMLInputElement).value.replace(/\D/g, "").slice(0, 2)
emit("update:horaTemp", raw)
}
function onHoraBlur() {
const v = Number.parseInt(props.horaTemp, 10)
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"),
)
}
}
return {
diasSemana,
MESES,
onMesSelectChange,
onAnoSelectChange,
onHoraInput,
onHoraBlur,
onHoraKeydown,
onMinutoInput,
onMinutoBlur,
onMinutoKeydown,
}
},
})
</script>

View file

@ -0,0 +1,123 @@
/* ─────────────────────────────────────────────
EliEntradaDataHora — Campo de acionamento
───────────────────────────────────────────── */
.eli-dh {
position: relative;
width: 100%;
font-family: var(--eli-font-family, "Google Sans", system-ui, sans-serif);
}
.eli-dh__campo {
display: flex;
align-items: center;
gap: 8px;
padding: 0 14px;
height: 56px;
border: 1.5px solid var(--cor-eli-borda, #D5D8CC);
border-radius: 8px;
background: var(--cor-eli-superficie, #fff);
cursor: pointer;
transition: border-color 0.18s, box-shadow 0.18s;
user-select: none;
position: relative;
}
.eli-dh__campo:hover:not(.eli-dh__campo--desabilitado) {
border-color: var(--cor-eli-primaria-media, #ADCC44);
}
.eli-dh__campo--focado {
border-color: var(--cor-eli-primaria, #688C00);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--cor-eli-primaria, #688C00) 14%, transparent);
outline: none;
}
.eli-dh__campo--erro {
border-color: var(--cor-eli-erro, #C62828);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--cor-eli-erro, #C62828) 12%, transparent);
}
.eli-dh__campo--desabilitado {
background: var(--cor-eli-fundo, #F2F3EE);
cursor: not-allowed;
opacity: 0.6;
}
.eli-dh__icone {
flex-shrink: 0;
width: 20px;
height: 20px;
color: var(--cor-eli-neutro, #5E6553);
transition: color 0.18s;
}
.eli-dh__campo--focado .eli-dh__icone {
color: var(--cor-eli-primaria, #688C00);
}
.eli-dh__conteudo {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
gap: 1px;
}
.eli-dh__rotulo {
font-size: 11px;
font-weight: 600;
color: var(--cor-eli-neutro, #5E6553);
line-height: 1.2;
letter-spacing: 0.02em;
text-transform: uppercase;
transition: color 0.18s;
}
.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 {
font-size: 15px;
font-weight: 500;
color: var(--cor-eli-neutro-escuro, #3E4436);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.eli-dh__valor--vazio {
color: var(--cor-eli-borda-forte, #B5BAA8);
font-weight: 400;
}
.eli-dh__limpar {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border: none;
background: none;
border-radius: 50%;
cursor: pointer;
color: var(--cor-eli-borda-forte, #B5BAA8);
transition: color 0.15s, background 0.15s;
padding: 0;
}
.eli-dh__limpar:hover {
color: var(--cor-eli-erro, #C62828);
background: var(--cor-eli-erro-bg, #FFEBEE);
}
.eli-dh__mensagem {
margin-top: 4px;
padding: 0 14px;
font-size: 12px;
line-height: 1.4;
}
.eli-dh__mensagem--erro { color: var(--cor-eli-erro, #C62828); }
.eli-dh__mensagem--dica { color: var(--cor-eli-neutro, #5E6553); }

View file

@ -0,0 +1,73 @@
import dayjs from "dayjs"
import type { CelulaCalendario, EntradaDataHora } from "./useDataHora"
/**
* 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.
*/
export function gerarCelulas(
referencia: dayjs.Dayjs,
diaSelecionado: dayjs.Dayjs | null,
min?: string,
max?: string,
): CelulaCalendario[] {
const inicio = referencia.startOf("month")
const fim = referencia.endOf("month")
const hoje = dayjs()
const minDt = min ? dayjs(min).startOf("day") : null
const maxDt = max ? dayjs(max).startOf("day") : null
const lista: CelulaCalendario[] = []
for (let i = inicio.day() - 1; i >= 0; i--) {
const d = inicio.subtract(i + 1, "day")
lista.push({
dia: d.date(),
data: d,
atual: false,
hoje: false,
selecionado: false,
desabilitado: true,
})
}
for (let d = inicio; d.isBefore(fim) || d.isSame(fim, "day"); d = d.add(1, "day")) {
lista.push({
dia: d.date(),
data: d,
atual: true,
hoje: d.isSame(hoje, "day"),
selecionado: diaSelecionado !== null && d.isSame(diaSelecionado, "day"),
desabilitado: (minDt !== null && d.isBefore(minDt)) || (maxDt !== null && d.isAfter(maxDt)),
})
}
let extra = 1
while (lista.length < 42) {
const d = fim.add(extra++, "day")
lista.push({
dia: d.date(),
data: d,
atual: false,
hoje: false,
selecionado: false,
desabilitado: true,
})
}
return lista
}
/**
* Formata um valor ISO para exibição no campo.
* @param valor - string ISO 8601 ou null
* @param modo - "data" exibe apenas a data, "dataHora" inclui hora
*/
export function formatarValor(
valor: string | null,
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")
}

View file

@ -0,0 +1,133 @@
<template>
<div class="eli-dh">
<!-- Campo de acionamento -->
<div
:id="`eli-dh-campo-${uid}`"
class="eli-dh__campo"
:class="{
'eli-dh__campo--focado': aberto,
'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"
>
<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" />
<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>
<div class="eli-dh__conteudo">
<span class="eli-dh__rotulo">{{ opcoesEfetivas.rotulo }}</span>
<span class="eli-dh__valor" :class="{ 'eli-dh__valor--vazio': !valorFormatado }">
{{ valorFormatado || opcoesEfetivas.placeholder || 'Selecione...' }}
</span>
</div>
<button
v-if="Boolean(opcoesEfetivas.limpavel) && valor && !desabilitadoEfetivo"
class="eli-dh__limpar"
type="button"
title="Limpar"
:aria-label="`Limpar ${opcoesEfetivas.rotulo}`"
@click.stop="limpar"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" width="14" height="14" aria-hidden="true">
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<!-- Mensagens -->
<div v-if="mensagensVisiveis.length" class="eli-dh__mensagem eli-dh__mensagem--erro" role="alert">
{{ mensagensVisiveis[0] }}
</div>
<div v-else-if="opcoesEfetivas.dica && (opcoesEfetivas.dicaPersistente || aberto)" class="eli-dh__mensagem eli-dh__mensagem--dica">
{{ opcoesEfetivas.dica }}
</div>
<!-- Popover calendário -->
<Calendario
:uid="uid"
:aberto="aberto"
:estilo-popover="estiloPopover"
:rotulo="opcoesEfetivas.rotulo"
:modo="modoEfetivo"
:nome-mes-atual="nomeMesAtual"
:ano-atual="anoAtual"
:mes-atual="mesAtual"
:lista-anos="listaAnos"
:celulas="celulas"
:dia-selecionado="diaSelecionado"
:hora-temp="horaTemp"
:minuto-temp="minutoTemp"
@fechar="fechar"
@confirmar="confirmar"
@navegarMes="navegarMes"
@selecionarDia="selecionarDia"
@mudarMes="mudarMes"
@mudarAno="mudarAno"
@update:horaTemp="horaTemp = $event"
@update:minutoTemp="minutoTemp = $event"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, type PropType } from "vue"
import type { CampoDensidade, CampoVariante } from "../../../tipos"
import "./EliEntradaDataHora.css"
import Calendario from "./Calendario.vue"
import type { EntradaDataHora } from "./useDataHora"
import { useDataHora } from "./useDataHora"
export default defineComponent({
name: "EliEntradaDataHora",
components: { Calendario },
inheritAttrs: false,
props: {
value: { type: String as PropType<EntradaDataHora["value"]>, default: undefined },
opcoes: {
type: Object as PropType<EntradaDataHora["opcoes"]>,
required: false,
default: undefined,
},
modelValue: { type: String as PropType<string | null>, default: null },
modo: { type: String as PropType<"data" | "dataHora">, default: undefined },
rotulo: { type: String, default: undefined },
placeholder: { type: String, default: undefined },
desabilitado: { type: Boolean, default: undefined },
limpavel: { type: Boolean, default: undefined },
erro: { type: Boolean, default: undefined },
mensagensErro: { type: [String, Array] as PropType<string | string[]>, default: undefined },
dica: { type: String, default: undefined },
dicaPersistente: { type: Boolean, default: undefined },
densidade: { type: String as PropType<CampoDensidade>, default: undefined },
variante: { type: String as PropType<CampoVariante>, default: undefined },
min: { type: String as PropType<string | undefined>, default: undefined },
max: { type: String as PropType<string | undefined>, default: undefined },
},
emits: {
"update:value": (_v: string | null) => true,
input: (_v: string | null) => true,
change: (_v: string | null) => true,
"update:modelValue": (_v: string | null) => true,
alterar: (_v: string | null) => true,
foco: () => true,
desfoco: () => true,
focus: () => true,
blur: () => true,
},
setup(props, { emit }) {
type EmitFn = (event: string, payload?: unknown) => void
const emitFn: EmitFn = (event: string, payload?: unknown) =>
(emit as unknown as EmitFn)(event, payload)
return useDataHora(props, emitFn)
},
})
</script>

View 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,
}
}

View file

@ -1,6 +1,6 @@
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue"
import EliEntradaCoordenadas from "./EliEntradaCoordenadas.vue"
import EliEntradaDataHora from "./EliEntradaDataHora.vue"
import EliEntradaDataHora from "./EliEntradaDataHora/index.vue"
import EliEntradaNumero from "./EliEntradaNumero.vue"
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue"
import EliEntradaSelecao from "./EliEntradaSelecao.vue"

View file

@ -1,7 +1,7 @@
import type { Component } from "vue"
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue"
import EliEntradaCoordenadas from "./EliEntradaCoordenadas.vue"
import EliEntradaDataHora from "./EliEntradaDataHora.vue"
import EliEntradaDataHora from "./EliEntradaDataHora/index.vue"
import EliEntradaNumero from "./EliEntradaNumero.vue"
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue"
import EliEntradaSelecao from "./EliEntradaSelecao.vue"