Calendar
Grade de mês com navegação e eventos por dia.
import { Calendar } from "@vincisys/eyeson";
export function CalendarBasicDemo() {
return (
<div className="h-[640px] w-full">
<Calendar defaultMonth={today} events={events} />
</div>
);
} Instalação
import { Calendar } from '@vincisys/eyeson'; Uso
import { Calendar, type CalendarEvent } from '@vincisys/eyeson';
const events: CalendarEvent[] = [
{
id: '1',
date: new Date(2026, 6, 10),
label: 'SDU → GRU | 6:30',
color: 'blue-main',
},
];
export function Example() {
return <Calendar defaultMonth={new Date(2026, 6, 1)} events={events} />;
} Exemplos
Básico
import { Calendar } from "@vincisys/eyeson";
export function CalendarBasicDemo() {
return (
<div className="h-[640px] w-full">
<Calendar defaultMonth={today} events={events} />
</div>
);
} Controlado
import { Calendar } from "@vincisys/eyeson";
import { useState } from "react";
export function CalendarControlledDemo() {
const [month, setMonth] = useState(today);
return (
<div className="h-[640px] w-full">
<Calendar month={month} onMonthChange={setMonth} events={events} />
</div>
);
} Cores dos eventos
A bolinha de cada evento aceita qualquer cor semântica do design system
(EdsSemanticColor), permitindo usar a paleta para indicar status
diferentes.
import { Calendar } from "@vincisys/eyeson";
export function CalendarColorsDemo() {
return (
<div className="h-[640px] w-full">
<Calendar
defaultMonth={today}
events={statusColors}
maxVisibleEvents={statusColors.length}
/>
</div>
);
} Customizando o conteúdo do evento
Use renderEvent para substituir a pill padrão por qualquer conteúdo —
inclusive controlando você mesmo o clique, em vez de usar onEventClick.
import { Calendar, CalendarEvent } from "@vincisys/eyeson";
import { Plane } from "lucide-react";
import { useState, CSSProperties } from "react";
export function CalendarCustomEventDemo() {
const [selected, setSelected] = useState<CalendarEvent | null>(null);
return (
<div className="h-[640px] w-full">
<Calendar
defaultMonth={today}
events={events}
renderEvent={(event) => (
<button
type="button"
onClick={() => setSelected(event)}
className="flex h-8 w-full shrink-0 items-center gap-1.5 rounded-eds-md border border-eds-neutral-disabled bg-white px-2 text-left text-xs font-medium text-eds-neutral-main"
>
<Plane
className="shrink-0 text-[var(--eds-flight-color)]"
style={
{
'--eds-flight-color': `var(--color-eds-${event.color})`,
} as CSSProperties
}
size={12}
/>
<span className="truncate">{event.label}</span>
</button>
)}
/>
{selected && (
<p className="mt-2 text-sm text-eds-neutral-elements">
Selecionado: {selected.label}
</p>
)}
</div>
);
} Abrindo um modal ao clicar no dia
onDayClick dispara ao clicar em qualquer lugar do cartão do dia (não só
no número) e recebe a data clicada — basta guardar esse valor no estado do
seu app para abrir um modal (aqui usando @base-ui/react/dialog) com um
formulário para adicionar um novo evento naquele dia.
import { Dialog } from "@base-ui/react/dialog";
import { Button, Calendar, CalendarEvent, Input, Select, SelectOption } from "@vincisys/eyeson";
import { format, isSameDay } from "date-fns";
import { ptBR } from "date-fns/locale";
import { useState } from "react";
export function CalendarDayModalDemo() {
const [dayEvents, setDayEvents] = useState<CalendarEvent[]>(events);
const [openDay, setOpenDay] = useState<Date | null>(null);
const [label, setLabel] = useState('');
const [color, setColor] = useState<SelectOption | null>(
EVENT_COLOR_OPTIONS[0],
);
const eventsForOpenDay = openDay
? dayEvents.filter((event) => isSameDay(event.date, openDay))
: [];
function handleAddEvent() {
if (!openDay || !label.trim() || !color) return;
setDayEvents((prev) => [
...prev,
{
id: crypto.randomUUID(),
date: openDay,
label: label.trim(),
color: color.value as CalendarEvent['color'],
},
]);
setLabel('');
}
return (
<div className="h-[640px] w-full">
<Calendar
defaultMonth={today}
events={dayEvents}
onDayClick={setOpenDay}
/>
<Dialog.Root
open={openDay !== null}
onOpenChange={(open) => !open && setOpenDay(null)}
>
<Dialog.Portal>
<Dialog.Backdrop className="fixed inset-0 z-40 bg-black/40" />
<Dialog.Popup className="fixed top-1/2 left-1/2 z-50 flex max-h-[85vh] w-80 -translate-x-1/2 -translate-y-1/2 flex-col rounded-eds-md bg-white p-4 shadow-lg">
<Dialog.Title className="shrink-0 text-base font-medium text-eds-neutral-main capitalize">
{openDay
? format(openDay, "d 'de' MMMM", {
locale: ptBR,
})
: ''}
</Dialog.Title>
<div className="mt-3 flex flex-col gap-2 overflow-y-auto">
{eventsForOpenDay.length === 0 && (
<p className="text-sm text-eds-neutral-elements">
Nenhum evento neste dia.
</p>
)}
{eventsForOpenDay.map((event) => (
<div
key={event.id}
className="text-sm text-eds-neutral-main"
>
{event.label}
</div>
))}
</div>
<div className="mt-4 flex shrink-0 flex-col gap-3 border-t border-eds-hairline pt-4">
<Input
label="Novo evento"
placeholder="Ex: Reunião com cliente"
value={label}
onChange={setLabel}
/>
<Select
label="Cor"
items={EVENT_COLOR_OPTIONS}
value={color}
onValueChange={setColor}
/>
<Button
text="Adicionar evento"
size="sm"
onClick={handleAddEvent}
disabled={!label.trim()}
/>
</div>
<Dialog.Close className="mt-4 shrink-0 cursor-pointer text-sm text-eds-blue-main">
Fechar
</Dialog.Close>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
</div>
);
} Página completa
O Calendar ocupa 100% da altura do seu contêiner. Veja um exemplo de
página real, com um cabeçalho simples e o calendário preenchendo o
restante da tela: /examples/voos.