import { ReactNode } from 'react'; /** Vistas disponibles del calendario. */ export type CalendarView = "month" | "week" | "day" | "agenda"; /** Familias de color semántico del DS que puede tomar un evento. */ export type CalendarEventColor = "brand" | "success" | "warning" | "error" | "pending" | "inprogress" | "neutral"; /** * Color de un evento. Además de las familias del DS acepta cualquier color CSS * como escape hatch (para datos legacy con colores hardcodeados); en ese caso el * componente lo usa tal cual y deja de ser theme-aware. * * El `& {}` mantiene el autocompletado de las familias sin cerrar el tipo. */ export type CalendarEventColorValue = CalendarEventColor | (string & {}); /** Valor de fecha aceptado en la entrada: `Date`, timestamp o string ISO. */ export type CalendarDateInput = Date | string | number; export interface CalendarEvent { readonly id: string | number; readonly title: string; /** Inicio del evento. Un string `"YYYY-MM-DD"` se interpreta en hora local. */ readonly start: CalendarDateInput; /** Fin del evento. Si falta o no es posterior al inicio, se usa `defaultEventDuration`. */ readonly end?: CalendarDateInput; /** Ocupa el día completo: va a la fila superior, sin posición horaria. */ readonly allDay?: boolean; readonly color?: CalendarEventColorValue; /** Etiqueta corta que se muestra antes del título (ej: `"OT"`, `"P"`). */ readonly tag?: string; readonly description?: string; /** No dispara `onEventClick` y se muestra atenuado. */ readonly disabled?: boolean; /** Payload del consumidor; vuelve intacto en los callbacks. */ readonly data?: TData; } /** Evento con las fechas ya resueltas a `Date`. Es lo que reciben los renderers. */ export interface ResolvedCalendarEvent { readonly event: CalendarEvent; readonly start: Date; readonly end: Date; readonly allDay: boolean; } /** Rango visible del calendario. Útil para pedir datos al backend. */ export interface CalendarRange { readonly start: Date; readonly end: Date; readonly view: CalendarView; } /** * Resultado de arrastrar o redimensionar un evento. * * El callback puede devolver `false` (o una promesa que resuelve `false` o que * se rechaza) para **rechazar** el cambio: el calendario devuelve el evento a su * posición original. Cualquier otro valor lo acepta. */ export interface CalendarEventChange { readonly event: CalendarEvent; readonly start: Date; readonly end: Date; readonly allDay: boolean; /** Posición previa, para deshacer del lado del consumidor. */ readonly previous: { readonly start: Date; readonly end: Date; readonly allDay: boolean; }; } export type CalendarChangeResult = boolean | void | Promise; /** Rango dibujado arrastrando sobre espacio vacío. */ export interface CalendarRangeSelection { readonly start: Date; readonly end: Date; readonly allDay: boolean; } /** Granularidad a la que se ajusta el arrastre, en minutos. */ export type CalendarSnapMinutes = 5 | 10 | 15 | 30 | 60; /** Contexto que reciben `renderEvent` y `renderDayCell`. */ export interface CalendarEventRenderContext { readonly view: CalendarView; /** Día de la celda donde se está pintando (un evento multi-día se repite). */ readonly day: Date; /** Forma del contenedor: chip compacto (mes), bloque horario o fila (agenda). */ readonly shape: "chip" | "block" | "row"; readonly isPast: boolean; } export interface CalendarProps { readonly events?: readonly CalendarEvent[]; /** Vista activa (modo controlado). */ readonly view?: CalendarView; readonly defaultView?: CalendarView; readonly onViewChange?: (view: CalendarView) => void; /** Vistas que ofrece la toolbar, en orden. */ readonly views?: readonly CalendarView[]; /** Fecha de referencia (modo controlado). */ readonly date?: CalendarDateInput; readonly defaultDate?: CalendarDateInput; readonly onDateChange?: (date: Date) => void; /** * Se dispara cada vez que cambia el rango visible (navegación o cambio de * vista). Es el hook para pedir los eventos al backend. */ readonly onRangeChange?: (range: CalendarRange) => void; readonly onEventClick?: (event: CalendarEvent, nativeEvent: React.MouseEvent) => void; /** Click en una celda de día (vista mes) o en el encabezado del día. */ readonly onDayClick?: (date: Date, nativeEvent: React.MouseEvent) => void; /** * Doble click en una celda de día o en el encabezado del día. * * Al pasarlo, el click simple se retrasa ~250ms para poder distinguirlos y * **no** dispara cuando el gesto termina siendo doble. Sin este handler, * `onDayClick` dispara al instante. */ readonly onDayDoubleClick?: (date: Date, nativeEvent: React.MouseEvent) => void; /** Click en un slot horario; `date` trae la hora del slot. */ readonly onSlotClick?: (date: Date, nativeEvent: React.MouseEvent) => void; /** Doble click en un slot horario. Misma desambiguación que `onDayDoubleClick`. */ readonly onSlotDoubleClick?: (date: Date, nativeEvent: React.MouseEvent) => void; /** Click en "+N más" (vista mes). Si no se pasa, cae en `onDayClick`. */ readonly onShowMore?: (date: Date, events: readonly CalendarEvent[]) => void; /** * Habilita arrastrar eventos para moverlos y redimensionarlos (esto último * solo en las vistas con horario). * * En touch el arrastre arranca con una pulsación sostenida (~350ms) para no * pisar el scroll de la grilla. */ readonly editable?: boolean; /** Habilita dibujar un rango arrastrando sobre espacio vacío. */ readonly selectable?: boolean; /** Granularidad del arrastre en minutos. Default 15. */ readonly dragSnapMinutes?: CalendarSnapMinutes; /** * Se disparó un movimiento de evento. Devolvé `false` (o una promesa que * resuelve `false` / se rechaza) para rechazarlo y que vuelva a su lugar. */ readonly onEventDrop?: (change: CalendarEventChange) => CalendarChangeResult; /** Ídem `onEventDrop`, para el cambio de duración. */ readonly onEventResize?: (change: CalendarEventChange) => CalendarChangeResult; /** Se dibujó un rango arrastrando sobre espacio vacío. */ readonly onRangeSelect?: (selection: CalendarRangeSelection) => void; /** Reemplaza el contenido del evento manteniendo posición y color. */ readonly renderEvent?: (event: CalendarEvent, context: CalendarEventRenderContext) => ReactNode; /** Acción por celda de día en la vista mes (ej: un botón "+"). */ readonly renderDayAction?: (date: Date) => ReactNode; /** Contenido extra a la derecha de la toolbar. */ readonly actionSlot?: ReactNode; /** 1 = lunes (default), 0 = domingo. */ readonly weekStartsOn?: 0 | 1; /** Locale para nombres de mes/día y formato de hora. Default `"es-AR"`. */ readonly locale?: string; /** Primera hora visible en las vistas con horario. Formato `"HH:mm"`. */ readonly minTime?: string; /** Última hora visible. `"24:00"` para el día completo. */ readonly maxTime?: string; /** Minutos por slot en las vistas con horario. */ readonly slotDuration?: 15 | 30 | 60; /** Alto en píxeles de cada slot. */ readonly slotHeight?: number; /** Hora a la que se autoscrollea al abrir. Formato `"HH:mm"`. */ readonly scrollToTime?: string; /** Línea de la hora actual en las vistas con horario. */ readonly nowIndicator?: boolean; /** Duración en minutos que se asume cuando el evento no trae `end`. */ readonly defaultEventDuration?: number; /** Eventos visibles por celda en la vista mes antes del "+N más". */ readonly maxEventsPerDay?: number; /** Días que abarca la vista agenda. Si se omite, usa la semana. */ readonly agendaDays?: number; readonly showToolbar?: boolean; /** Muestra skeletons en lugar de los eventos. */ readonly isLoading?: boolean; /** Contenido cuando no hay eventos en el rango (vista agenda). */ readonly emptyState?: ReactNode; readonly className?: string; readonly toolbarClassName?: string; readonly "aria-label"?: string; }