---
title: "Custom Calendar Timeline"
slug: "custom-calendar-timeline"
category: "Widgets"
reactComponent: "ArcWidgetCalendarTimeline"
reactTier: "premium"
reactSince: "0.1.0-alpha.5"
reactExample: "calendar_timeline.tsx"
locale: "es"
hosts: "react"
---

# CalendarTimeline en React

Muestra periodos de tiempo por recurso u objeto en un eje de tiempo compartido.

Referencia para `@arcrider/arcwidgets-react@0.1.0-alpha.5`. [Instalación](/docs/react/intro/setup-react) · [Todos los componentes](/docs/react/intro/react-components).

## Ejemplo completo

```tsx
import { ArcWidgetCalendarTimeline } from '@arcrider/arcwidgets-react';
import { useState } from 'react';

const lang = document.documentElement.lang.startsWith('en')
  ? 'en'
  : document.documentElement.lang.startsWith('es')
    ? 'es'
    : 'de';
const requested = new URLSearchParams(location.search).get('data');
const pack = requested === 'empty' || requested === 'busy' ? requested : 'demo';
const t = (de: string, en: string, es: string) =>
  lang === 'en' ? en : lang === 'es' ? es : de;
const day = (value: number) => new Date(2026, 8, value).getTime();

const chrome = {
  backgroundColor: '#ffffff',
  fontColor: '#18181b',
  mutedColor: '#52525b',
  subtleColor: '#71717a',
  headerBackgroundColor: '#fafafa',
  groupBackgroundColor: '#f4f4f5',
  rowBackgroundColor: '#ffffff',
  borders: { outer: true, rows: true, columns: false, color: '#e4e4e7' },
  dayStripes: { odd: '#ffffff', even: '#f4f4f5' },
};

const look = (backgroundColor: string, color: string) => ({
  backgroundColor,
  color,
  fontColor: color,
});

type Bar = {
  id: string;
  title: string;
  dateFrom: number;
  dateTo: number;
  clickable: true;
  draggable: true;
  styles: ReturnType<typeof look>;
};

type Row = {
  id: string;
  title: string;
  timeEntries: Bar[];
};

const bar = (
  id: string,
  title: string,
  from: number,
  to: number,
  backgroundColor: string,
  color: string,
): Bar => ({
  id,
  title,
  dateFrom: day(from),
  dateTo: day(to),
  clickable: true,
  draggable: true,
  styles: look(backgroundColor, color),
});

const demoRows: Row[] = [
  {
    id: 'team-a',
    title: t('Team Montage', 'Install team', 'Equipo montaje'),
    timeEntries: [
      bar(
        'job-a',
        t('Installation', 'Installation', 'Instalación'),
        16,
        18,
        '#d1fae5',
        '#065f46',
      ),
    ],
  },
  {
    id: 'team-b',
    title: t('Kundentermin', 'Site visit', 'Visita'),
    timeEntries: [
      bar(
        'job-b-out',
        t('Anfahrt', 'Travel there', 'Ida'),
        16,
        16,
        '#99f6e4',
        '#115e59',
      ),
      bar(
        'job-b',
        t('Hauptarbeit', 'On site', 'Trabajo principal'),
        17,
        17,
        '#ccfbf1',
        '#115e59',
      ),
      bar(
        'job-b-back',
        t('Abfahrt', 'Travel back', 'Vuelta'),
        18,
        18,
        '#99f6e4',
        '#115e59',
      ),
    ],
  },
];

const busyRows: Row[] = [
  ...demoRows,
  {
    id: 'team-c',
    title: t('Team Messe', 'Fair team', 'Equipo feria'),
    timeEntries: [
      bar(
        'job-c',
        t('Aufbau', 'Setup', 'Montaje'),
        15,
        17,
        '#ede9fe',
        '#5b21b6',
      ),
      bar(
        'job-d',
        t('Abbau', 'Teardown', 'Desmontaje'),
        22,
        24,
        '#ffe4e6',
        '#9f1239',
      ),
    ],
  },
];

const emptyRows: Row[] = demoRows.map((row) => ({ ...row, timeEntries: [] }));

export default function Example() {
  const [rows, setRows] = useState(
    pack === 'empty' ? emptyRows : pack === 'busy' ? busyRows : demoRows,
  );
  const [selected, setSelected] = useState(
    t(
      'Balken ziehen oder zoomen',
      'Drag bars or zoom days',
      'Arrastra barras o haz zoom',
    ),
  );
  return (
    <>
      <ArcWidgetCalendarTimeline
        id="resources"
        data={{
          timeZoneBalance: 0,
          height: '360px',
          lang,
          styles: chrome,
          dateSettings: {
            startDate: day(14),
            duration: 14,
          },
          columns: [{ title: t('Team', 'Team', 'Equipo'), width: '180px' }],
          zoomSettings: {
            enabled: true,
            defaultDayWidth: '64px',
            minDayWidth: '20px',
            maxDayWidth: '160px',
            persist: false,
          },
          items: rows.map((row) => ({
            id: row.id,
            columns: [{ title: row.title, clickable: true }],
            timeEntries: row.timeEntries,
          })),
        }}
        onEntryClick={(rowIndex, entryIndex) =>
          setSelected(
            rows[rowIndex]?.timeEntries[entryIndex]?.title ?? selected,
          )
        }
        onLabelClick={(rowIndex) =>
          setSelected(rows[rowIndex]?.title ?? selected)
        }
        onEntryDragSave={(rowIndex, entryIndex, start, rowChange) => {
          setRows((previous) => {
            const next = previous.map((row) => ({
              ...row,
              timeEntries: [...row.timeEntries],
            }));
            const [item] = next[rowIndex].timeEntries.splice(entryIndex, 1);
            next[rowChange.row_index ?? rowIndex].timeEntries.push({
              ...item,
              dateFrom: Number(start.dateFrom ?? item.dateFrom),
              dateTo: Number(start.dateTo ?? item.dateTo),
            });
            return next;
          });
          setSelected(t('Balken verschoben', 'Bar moved', 'Barra movida'));
        }}
      />
      <p aria-live="polite">{selected}</p>
    </>
  );
}
```

## Props del componente

### id

ID opcional y estable de la instancia del widget. Sin especificarlo, React genera un ID.

### data

Configuración de tipo `ArcWidgetCalendarTimelineData`. Pasa los cambios como objetos nuevos; gestiona los datos de negocio y la selección en el estado de React.

### onAction

Fallback opcional `(type: string, ...args: unknown[]) => unknown`. El callback concreto tiene prioridad. Detalles: [Estado y eventos](/docs/react/patterns/react-state-events).

### onEntryClick

Índice de la cita, y en Timeline también la fila del recurso.

```typescript
onEntryClick?: (row_index: number, entry_index: number) => void;
```

### onEntryDragSave

Fin del arrastre con los datos originales. No hay horas de destino completas nuevas; consulta las notas sobre persistencia.

```typescript
onEntryDragSave?: (
        row_index: number,
        entry_index: number,
        start_data: ArcWidgetCalendarTimelineDragStart,
        row_change: ArcWidgetCalendarTimelineRowChange
    ) => void;
```

### onLabelClick

Índices de la fila de recurso y la columna de etiqueta clicadas.

```typescript
onLabelClick?: (row_index: number, column_index: number) => void;
```

### onGhostClick

Posición del área libre clicada.

```typescript
onGhostClick?: (row_index: number, column_value: number) => void;
```

## Configuración

Los siguientes campos pertenecen a `data`. Los tamaños se indican como strings, los números y valores de tiempo según su tipo respectivo.

### height

Altura del widget, p. ej. `"320px"`. Los valores porcentuales necesitan un contenedor padre con altura definida.

Tipo: `string`.

### timeZoneBalance

Corrección en horas; los ejemplos usan `0` y valores de día generados localmente.

Tipo: `number | string`.

### dateSettings

Rango de fechas, cabeceras y marcados.

El tipo y los campos anidados están en `ArcWidgetCalendarTimelineData`, bajo tipos de datos.

### columns

Columnas de etiqueta izquierdas de los recursos.

Tipo: `ArcWidgetCalendarTimelineColumn[]`.

### items

Entradas de la selección o de la representación; consulta el tipo de datos correspondiente.

Tipo: `ArcWidgetCalendarTimelineItem[]`.

### dayWidth

Ancho de un día en el eje de tiempo.

Tipo: `string`.

### zoomSettings

Límites de zoom y controles.

El tipo y los campos anidados están en `ArcWidgetCalendarTimelineData`, bajo tipos de datos.

### timeSettings

Rango horario y cuadrícula; las unidades son milisegundos.

Tipo: `Record<string, unknown>`.

### columnWidth

Ancho del área de columna izquierda.

Tipo: `string`.

### lang

Idioma de las etiquetas del calendario.

Tipo: `string`.

### styles

Colores, espaciados y bordes comunes; los campos ofrecidos son específicos del widget.

Tipo: `ArcWidgetCalendarTimelineStyles | string`.

### borders

Configuración histórica de bordes. Prefiere `styles.borders`, si el widget lo ofrece.

Tipo: `ArcWidgetCalendarTimelineBorders`.

### borderRadius

Redondeo de esquinas.

Tipo: `string`.

## Comportamiento y límites

- Para JSX en citas usa `customLayout`. Los campos estándar `title`, `subtitle` y `value` se procesan como texto o HTML.

- Los valores de fecha son marcas de tiempo en milisegundos; las horas son milisegundos desde el inicio del día. Consulta [Fecha y hora](/docs/react/patterns/react-dates).

- `clickable: true` activa la selección. Un índice de callback no es un ID de registro; resuélvelo mediante tu lista actual de entradas.

- Los callbacks de arrastre actuales contienen el estado original (`start_data`), pero no horas de destino completas nuevas. Por eso el ejemplo establece `draggable: false`. Edita las citas mediante el estado de React hasta que exista un contrato de persistencia completo para arrastrar y soltar.

- `items` contiene filas de recurso, dentro de las cuales están los `timeEntries`. `columns` define las columnas de etiqueta izquierdas.

- `onEntryClick(rowIndex, entryIndex)` usa dos índices. `dayWidth` y `zoomSettings` controlan la escala.

Los campos `ArcWidgetSlot` admitidos permiten elementos JSX individuales, texto o descriptores de widget. Pasa texto externo como texto JSX; los strings HTML se interpretan como HTML. [Combinar contenidos](/docs/react/patterns/nested-widget-descriptors).

## Tipos de datos

Estos tipos relacionados con el widget se exportan desde el paquete. También están disponibles tipos comunes como `ArcWidgetSlot`, `ArcWidgetStyles` y `ArcReactWidgetProps`. Las firmas de índice permiten extensiones; no garantizan que el widget evalúe campos arbitrarios.

```typescript
export type ArcWidgetCalendarTimelineBorderLine = boolean | {
	color?: string;
	width?: string;
	borderColor?: string;
	borderWidth?: string;
	borderSize?: string;
	enabled?: boolean;
};

export interface ArcWidgetCalendarTimelineBorders {
	color?: string;
	width?: string;
	outer?: ArcWidgetCalendarTimelineBorderLine;
	columns?: ArcWidgetCalendarTimelineBorderLine;
	rows?: ArcWidgetCalendarTimelineBorderLine;
}

export interface ArcWidgetCalendarTimelineStyles extends ArcWidgetStyles {
	borders?: ArcWidgetCalendarTimelineBorders;
	headerBackgroundColor?: string;
	groupBackgroundColor?: string;
	rowBackgroundColor?: string;
	mutedColor?: string;
	subtleColor?: string;
	dayStripes?: boolean | {
		enabled?: boolean;
		odd?: string;
		even?: string;
		oddColor?: string;
		evenColor?: string;
	};
}

export interface ArcWidgetCalendarTimelineColumn {
	title?: ArcWidgetSlot;
	width?: string;
	backgroundColor?: string;
	fontColor?: string;
	fontSize?: string;
	fontWeight?: string | number;
	[key: string]: unknown;
}

export interface ArcWidgetCalendarTimelineItemColumn {
	title?: ArcWidgetSlot;
	subtitle?: ArcWidgetSlot;
	clickable?: boolean;
	alignX?: string;
	alignY?: string;
	fontSize?: string;
	fontWeight?: string | number;
	[key: string]: unknown;
}

export interface ArcWidgetCalendarTimelineGhost {
	title?: ArcWidgetSlot;
	clickable?: boolean;
	position?: 'bottomRight' | 'timeEntry' | string;
	styles?: ArcWidgetStyles | string;
	[key: string]: unknown;
}

export interface ArcWidgetCalendarTimelineEntryLabel {
	value?: string | number;
	width?: string | number;
}

export interface ArcWidgetCalendarTimelineTimeEntry {
	id?: string | number;
	title?: string | ArcWidgetCalendarTimelineEntryLabel;
	subtitle?: string | ArcWidgetCalendarTimelineEntryLabel;
	value?: string;
	customLayout?: ArcWidgetSlot;
	dateFrom?: unknown;
	dateTo?: unknown;
	timeFrom?: unknown;
	timeTo?: unknown;
	styles?: ArcWidgetStyles | string;
	clickable?: boolean;
	draggable?: boolean;
	tooltip?: {
		value?: ArcWidgetSlot;
		width?: string;
		maxWidth?: string;
		maxHeight?: string;
		position?: string;
		styles?: ArcWidgetStyles | string;
		[key: string]: unknown;
	};
	[key: string]: unknown;
}

export interface ArcWidgetCalendarTimelineItem {
	id: string | number;
	title?: ArcWidgetSlot;
	subtitle?: ArcWidgetSlot;
	columns?: ArcWidgetCalendarTimelineItemColumn[];
	timeEntries?: ArcWidgetCalendarTimelineTimeEntry[];
	items?: ArcWidgetCalendarTimelineItem[];
	highlights?: Array<{
		date?: unknown;
		dateFrom?: unknown;
		dateTo?: unknown;
		timeFrom?: unknown;
		timeTo?: unknown;
		color?: string;
		[key: string]: unknown;
	}>;
	ghost?: ArcWidgetCalendarTimelineGhost;
	[key: string]: unknown;
}

export interface ArcWidgetCalendarTimelineDragStart {
	dateFrom?: unknown;
	dateTo?: unknown;
	timeFrom?: unknown;
	timeTo?: unknown;
}

export interface ArcWidgetCalendarTimelineRowChange {
	start_row_index: number;
	row_index: number;
}

export interface ArcWidgetCalendarTimelineData extends Omit<ArcWidgetBaseData, 'actions'> {
	height?: string;
	timeZoneBalance?: number | string;
	dateSettings?: {
		header?: {
			title?: ArcWidgetSlot | {
				value?: ArcWidgetSlot;
				customLayout?: unknown;
				alignX?: string;
				fontSize?: string;
				fontColor?: string;
				fontWeight?: string | number;
				backgroundColor?: string;
				[key: string]: unknown;
			};
			days?: {
				compactRanges?: boolean;
				monthCompactBelow?: number;
				[key: string]: unknown;
			};
			months?: unknown;
			weeks?: unknown;
			alignX?: string;
			sticky?: string;
			stickyPadding?: string;
			[key: string]: unknown;
		};
		highlights?: Array<{
			date?: unknown;
			weekday?: number;
			color?: string;
			applyToRows?: boolean;
			styles?: ArcWidgetCalendarTimelineStyles | string;
			[key: string]: unknown;
		}>;
		[key: string]: unknown;
	};
	columns?: ArcWidgetCalendarTimelineColumn[];
	items?: ArcWidgetCalendarTimelineItem[];
	dayWidth?: string;
	zoomSettings?: {
		enabled?: boolean;
		defaultDayWidth?: string;
		minDayWidth?: string;
		maxDayWidth?: string;
		step?: number | string;
		persist?: boolean;
		controls?: { enabled?: boolean; [key: string]: unknown };
		[key: string]: unknown;
	};
	timeSettings?: Record<string, unknown>;
	columnWidth?: string;
	lang?: string;
	styles?: ArcWidgetCalendarTimelineStyles | string;
	borders?: ArcWidgetCalendarTimelineBorders;
	borderRadius?: string;
}

export interface ArcWidgetCalendarTimelineProps extends ArcReactWidgetProps<ArcWidgetCalendarTimelineData> {
	onEntryClick?: (row_index: number, entry_index: number) => void;
	onEntryDragSave?: (
		row_index: number,
		entry_index: number,
		start_data: ArcWidgetCalendarTimelineDragStart,
		row_change: ArcWidgetCalendarTimelineRowChange
	) => void;
	onLabelClick?: (row_index: number, column_index: number) => void;
	onGhostClick?: (row_index: number, column_value: number) => void;
}
```
