Ir al contenido

Slots API

La Slots API te permite reemplazar cualquier sub-componente con el tuyo propio. Los componentes slot reciben props tipados — construye UIs completamente personalizadas mientras mantienes la gestion de estado, algoritmos de layout y navegacion del calendario.

Pasa tus componentes personalizados via la prop slots:

<Calendar
events={events}
slots={{
toolbar: MiToolbarPersonalizado,
dayCell: MiCeldaDeDia,
timeEvent: MiEventoTemporal,
allDayEvent: MiEventoDeDiaCompleto,
popover: MiPopover,
agendaEvent: MiEventoAgenda,
}}
/>
SlotPropsUsado en
toolbarToolbarSlotPropsTodas las vistas
dayCellDayCellSlotPropsVista de mes
timeEventTimeEventSlotPropsVista de semana/dia
allDayEventAllDayEventSlotPropsVista de semana/dia
popoverPopoverSlotPropsTodas las vistas
agendaEventAgendaEventSlotPropsVista de agenda
resourceHeaderResourceHeaderSlotPropsVistas de recursos (dia/semana con recursos)
interface ToolbarSlotProps {
currentDate: DateString;
view: CalendarView;
onPrev: () => void;
onNext: () => void;
onToday: () => void;
onViewChange: (view: CalendarView) => void;
formattedDate: string; // Titulo con formato de locale, ej: "marzo 2026"
customButtons: CustomButton[]; // Botones del prop customButtons
canGoPrev: boolean; // false cuando se alcanza el inicio de validRange
canGoNext: boolean; // false cuando se alcanza el fin de validRange
}
interface DayCellSlotProps {
date: DateString;
isToday: boolean;
isCurrentMonth: boolean;
events: CalendarEvent[];
}
interface TimeEventSlotProps {
event: CalendarEvent;
positioned: PositionedEvent;
// positioned.column: indice de columna (base 0)
// positioned.totalColumns: total de columnas en grupo de solapamiento
// positioned.top: posicion % desde arriba
// positioned.height: altura en %
}
interface AllDayEventSlotProps {
event: CalendarEvent;
segment: EventSegment;
// segment.date: la fecha de este segmento
// segment.isStart: primer dia del evento
// segment.isEnd: ultimo dia del evento
}
interface PopoverSlotProps {
event: CalendarEvent;
onClose: () => void;
}
interface AgendaEventSlotProps {
event: CalendarEvent;
}
interface ResourceHeaderSlotProps {
resource: Resource;
// resource.id: identificador unico
// resource.title: nombre para mostrar
// resource.color: color opcional
}
import type { ToolbarSlotProps, CalendarView } from "trud-calendar";
function MiToolbar({
formattedDate,
view,
onPrev,
onNext,
onToday,
onViewChange,
customButtons,
canGoPrev,
canGoNext,
}: ToolbarSlotProps) {
return (
<div className="flex items-center justify-between p-4 border-b">
<div className="flex gap-2">
<button onClick={onPrev} disabled={!canGoPrev}>Anterior</button>
<button onClick={onToday}>Hoy</button>
<button onClick={onNext} disabled={!canGoNext}>Siguiente</button>
</div>
<h2 className="text-lg font-bold">{formattedDate}</h2>
<div className="flex gap-2">
<select
value={view}
onChange={(e) => onViewChange(e.target.value as CalendarView)}
>
<option value="month">Mes</option>
<option value="week">Semana</option>
<option value="day">Dia</option>
<option value="agenda">Agenda</option>
<option value="year">Ano</option>
</select>
{customButtons.map((btn) => (
<button key={btn.key} onClick={btn.onClick}>
{btn.text}
</button>
))}
</div>
</div>
);
}
<Calendar events={events} slots={{ toolbar: MiToolbar }} />
import type { DayCellSlotProps } from "trud-calendar";
function MiCeldaDeDia({ date, isToday, isCurrentMonth, events }: DayCellSlotProps) {
return (
<div
className={`p-2 min-h-[80px] ${
isToday ? "bg-blue-50 border-blue-300" : ""
} ${!isCurrentMonth ? "opacity-30" : ""}`}
>
<div className="font-bold">{new Date(date).getDate()}</div>
{events.map((e) => (
<div key={e.id} className="text-xs truncate" style={{ color: e.color }}>
{e.title}
</div>
))}
</div>
);
}
import type { AgendaEventSlotProps } from "trud-calendar";
function MiEventoAgenda({ event }: AgendaEventSlotProps) {
return (
<div className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: event.color }}
/>
<div>
<div className="font-medium">{event.title}</div>
<div className="text-sm text-gray-500">
{event.description || "Sin descripcion"}
</div>
</div>
</div>
);
}