---
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: "vue"
---

# Custom Calendar Timeline

## AI Defaults (read first)

- **uniqueId**: Required, `"timeline-" + Nr`.
- **height**: Use `"700px"` or `"100%"` when embedded; `"auto"` lets the widget grow with content (Ninox form scrolls).
- **timeZoneBalance**: Set `-1` for German timezone.
- **header**: `dateSettings.header.days` (Tages-Labels). Optional `header.months` / `header.weeks`. **`header.title`**: Titel/Layout oben links über den Spaltennamen (Text, HTML oder Widget) — nicht mehr die Tageszeile. Reiner Text ohne Extra-Typo: **16px / Schriftstärke 400**. Alte Configs mit `header.title.label` (`##day##` …) ohne `value`/`customLayout` gelten weiter als Tageszeile.
- **columns**: Label-Spalten links (Titel im Header, optional `width`); mindestens eine Spalte. Header-Farbe default = `dateSettings.header.days` (`backgroundColor` / `fontColor`); pro Spalte überschreibbar.
- **items**: Ressourcen-Zeilen; jedes Item braucht `id` (eindeutig pro Zeile) und `timeEntries`. Verschachtelung über `items` (max. eine Ebene unterhalb des Parents).
- **dateFrom/dateTo**: Actual date values in timeEntries, not strings.
- **dayWidth**: Default `"120px"`, increase for detailed timelines. Startbreite, wenn kein gespeicherter Zoom existiert; überschreibbar via `zoomSettings.defaultDayWidth`.
- **zoomSettings**: Optional. Zoom ist standardmäßig aktiv (`enabled: false` zum Abschalten). `defaultDayWidth`, `minDayWidth`, `maxDayWidth`, `step`, `persist` (LocalStorage pro `uniqueId`), `controls.enabled` für +/- unten rechts.
- **dragAction**: `recordId` + optional `resourceField` für vertikal; nur **gleiche Verschachtelungstiefe** (`_depth`, z. B. Mitarbeiter ↔ Mitarbeiter auch **über Teams hinweg**). Optional `enabled: false` schaltet Drag für diesen Balken aus. Optional `changeFieldValues` für Zusatzfelder nach erfolgreichem Drag (z. B. Cache-/Trigger-Felder).
- **Uhrzeit mode**: Activated by `timeSettings`, uses `columnWidth` instead of `dayWidth`. `timeFrom`/`timeTo` in milliseconds.
- **styles.borders**: Gleicher Vertrag wie Table (`outer` / `columns` / `rows`). Default: Rahmen an, Zeilenlinien an, **Spaltenlinien aus**. `outer: false` für bündig im Layout. Dark Mode über `backgroundColor`, `headerBackgroundColor`, `fontColor`, `borderColor`.
- **styles.dayStripes**: Default **an** — Tagesspalten abwechselnd Weiß / sehr helles Grau. `false` schaltet aus; `{ odd, even }` setzt eigene Farben. Wochenend-Highlights liegen darüber.
- **Zellen-Hintergrund**: `dateSettings.highlights[]` mit optional `applyToRows: true`; optional `items[].highlights` pro Zeile (`date`, `dateFrom`/`dateTo` bzw. `timeFrom`/`timeTo`).
- **Ghost (Neueinträge)**: Optional **pro Zeile** `items[].ghost` mit **`title`**, **`actions`**, optional **`position`** (Default **`bottomRight`**: kleiner Plus-Button unten rechts in der Spalte), optional **`styles`**. Klick führt `ghost.actions` aus. **Positionen außer `timeEntry`**: Plus erscheint erst bei **Maus über der Spalte** (Spalte wird per Zeilen-`mousemove` erkannt); **Touch**: Ghost bleibt sichtbar. **`timeEntry`**: siehe Abschnitt ghost (Idle nur in leeren Spalten). Details im Abschnitt **ghost**.
- **tooltip** *(pro Balken)*: Optional. Hover-Tooltip mit `value` (Text **oder** Widget-Deskriptor `{ widget, uid, data }`), optional `width` / `maxWidth` / `maxHeight` / `position` / `styles`. Portaliert an `document.body` (kein Clipping). Details im Abschnitt **tooltip**.

Con el widget **Custom Calendar Timeline** llevas una vista de calendario basada en recursos a tu base de datos Ninox. Los recursos (p. ej. empleados, salas, vehículos) se representan como filas, y los días u horas como columnas. Las citas aparecen como barras horizontales — ideal para planificación de intervenciones, vista de capacidades o gestión de recursos.

💡 **Lo que puedes hacer con Custom Calendar Timeline:**

- **Recursos como filas:** Cada fila representa un recurso (persona, sala, equipo, vehículo).
- **Citas como barras:** Las entradas se extienden horizontalmente sobre los días — con o sin hora.
- **Modo horario:** Muestra columnas de hora en lugar de días — ideal para diagramas de flota y planificación diaria de intervenciones.
- **Entradas jerárquicas:** `items` con sub-`items` (un nivel), filas planas con sangría — modelo de datos como Gantt.
- **Arrastrar y soltar:** Mueve citas horizontalmente (cambiar fecha/hora) o verticalmente (cambiar recurso/fila).
- **Entradas de varios días:** Muestra proyectos, vacaciones o bloques más largos a lo largo de varios días.
- **Resaltado:** Marca fines de semana, ciertas horas o días especiales con color.
- **Estilo individual:** Adapta colores, anchos y alturas a tu diseño.

📅 **Custom Calendar Timeline** es especialmente adecuado para planificación de intervenciones, planes de turnos, vistas de capacidad y **diagramas de flota** — en todos los casos donde quieras controlar recursos y citas en el tiempo.

## Código de aplicación

```javascript
let current := this;
arcCustomCalendarTimeline({
    uniqueId: "timeline-1",
    timeZoneBalance: 0,
    lang: "de",
    dateSettings: {
        startDate: today(),
        duration: 30,
        highlights: [
            { weekday: 6, color: "rgba(59,130,246,0.06)" },
            { weekday: 0, color: "rgba(59,130,246,0.06)" }
        ],
        header: {
            days: {
                label: "##weekDayShort## ##day##.",
                fontSize: "12px",
                fontColor: "#52525b",
                fontWeight: "600",
                backgroundColor: "#fafafa"
            }
        }
    },
    dayWidth: "120px",
    rowHeight: "auto",
    currentTime: "rgba(59,130,246,0.5)",
    height: "700px",
    styles: {
        backgroundColor: "#ffffff"
    },
    columns: [
        { title: "Name", width: "200px" },
        { title: "Abteilung", width: "130px" }
    ],
    items: (select Mitarbeiter).[{
        id: Nr,
        title: Name,
        subtitle: Abteilung,
        columns: [
            { title: text(Name) },
            { title: text(Abteilung) }
        ],
        timeEntries: (select Termine where Bearbeiter = Nr).[{
            title: Bezeichnung,
            subtitle: Kunde,
            dateFrom: Datum_von,
            dateTo: Datum_bis,
            timeFrom: Zeit_von,
            timeTo: Zeit_bis,
            styles: {
                backgroundColor: text(Farbe),
                color: "#fff",
                borderRadius: "6px",
                padding: "4px 8px"
            },
            dragAction: {
                recordId: Nr,
                dateFrom: fieldId(Nr, "Datum_von"),
                dateTo: fieldId(Nr, "Datum_bis"),
                resourceField: fieldId(Nr, "Bearbeiter"),
                changeFieldValues: [{
                    fieldId: fieldId(Nr, "CacheTrigger"),
                    value: now()
                }]
            },
            actions: [{
                type: "popup",
                recordId: Nr
            }]
        }]
    }]
})
```

## Ajustes generales

### uniqueId

Identificador único del widget. Importante si se usan varios widgets Timeline en una página.

```javascript
uniqueId: "timeline-1",
```

### timeZoneBalance

Corrección de zona horaria en horas. Para Alemania a menudo `-1`, para adaptar la visualización a CET/CEST.

```javascript
timeZoneBalance: 0,
timeZoneBalance: -1,
```

### lang

Idioma para el formato de fecha (p. ej. nombres de días de la semana).

```javascript
lang: "de",
lang: clientLang(),
```

### height

Altura del contenedor del widget.

- `"700px"` o `"100%"` — altura fija con scroll interno (por defecto en Ninox-Embeds).
- `"auto"` — la Timeline crece con el contenido; el formulario de Ninox hace scroll vertical (sin scroll vertical interno). Horizontalmente la Timeline sigue haciendo scroll dentro del widget.
- Con `maxHeight` se puede limitar la altura automática; entonces la Timeline hace scroll vertical internamente.

```javascript
height: "700px",
height: "100%",
height: "auto",
maxHeight: "600px",
minHeight: "200px",
```

### styles

Los bordes y las líneas de cuadrícula viven en **`styles`** — junto con fondo, radio y color de línea. Mismo contrato que Custom Table.

Por defecto: borde exterior redondeado, líneas de fila, **sin líneas de columna verticales** — como Table.

Cada campo de línea es `true` / `false` **o** `{ color, width }`.

| Prop | Default | Bedeutung |
|---|---|---|
| `styles.backgroundColor` | — | Fläche inkl. Label-Spalten links |
| `styles.headerBackgroundColor` | = `backgroundColor` | Name/Abteilung + Ecken |
| `styles.groupBackgroundColor` | = Header | Monats-/KW-Zeile |
| `styles.rowBackgroundColor` | = `backgroundColor` | Tagesraster-Zeilen |
| `styles.fontColor` | — | Text links und Fallback für Header |
| `styles.mutedColor` / `subtleColor` | = `fontColor` | Sekundärtext / Untertitel |
| `styles.borderColor` | `#e4e4e7` | Farbe für alle angeschalteten Linien |
| `styles.borderRadius` | `12px` | Eckenradius (nur wenn `outer` an) |
| `styles.borders.outer` | `true` | Außenrahmen. `false` = bündig im Layout |
| `styles.borders.columns` | `false` | Vertikale Linien im Tagesgitter und zwischen Label-Spalten. Die Kante Labels → Raster bleibt an. |
| `styles.borders.rows` | `true` | Horizontale Ressourcenlinien |
| `styles.dayStripes` | `true` | Tagesspalten abwechselnd Weiß / sehr helles Grau. `false` = aus. `{ odd, even }` = eigene Farben. |

```javascript
styles: {
  backgroundColor: "#ffffff",
  borderRadius: "12px",
  borderColor: "#e4e4e7",
  borders: {
    outer: true,
    columns: false,
    rows: true
  }
}

// In einem Layout / Card:
styles: { borders: { outer: false } }

// Tagesgitter wieder an:
styles: { borders: { columns: true } }

// Tagesspalten-Zebra aus:
styles: { dayStripes: false }

// Dunkler Hintergrund:
styles: {
  backgroundColor: "#18181b",
  borderColor: "#3f3f46",
  borders: {
    outer: true,
    rows: { color: "#27272a" }
  }
}
```

## dateSettings – periodo y representación

### startDate

Fecha de inicio de la Timeline. A partir de este día se muestran las columnas.

```javascript
startDate: today(),
startDate: date(2025, 2, 1),
```

### duration

Número de días mostrados. Se ignora si se define `endDate`.

```javascript
duration: 30,
duration: 14,
```

### endDate

*(Opcional)* Fecha de fin de la Timeline. Tiene prioridad sobre `duration`.

```javascript
dateSettings: {
    startDate: date(2025, 2, 1),
    endDate: date(2025, 2, 28)
}
```

### highlights

Resaltado de color de determinados días — p. ej. fines de semana o festivos.

```javascript
highlights: [
    { weekday: 6, color: "rgba(59,130,246,0.06)" },
    { weekday: 0, color: "rgba(59,130,246,0.06)" },
    { date: date(2025, 12, 24), color: "rgba(239,68,68,0.1)" }
],
```

- `weekday`: 0 = domingo, 6 = sábado
- `date`: Fecha concreta
- `dateFrom` / `dateTo`: Rango de fechas (opcional en lugar de `date` único)
- `color`: Color CSS (HEX o rgba)
- **`applyToRows`** *(opcional)*: `true` — el mismo resaltado se dibuja **en el área de fila** (detrás de las barras de cita) para **todas** las filas de recurso, no solo en la cabecera. Sin este flag se mantiene el comportamiento anterior (solo header).

**En modo horario** además:
- `hour`: Hora (0–23) para resaltar determinadas columnas de hora
- `timeFrom` / `timeTo`: Rango de tiempo en milisegundos (como en `timeEntries`), p. ej. pausa de mediodía

```javascript
highlights: [
    { hour: 12, color: "rgba(239,68,68,0.1)" }
]
```

```javascript
highlights: [
    { weekday: 6, color: "rgba(59,130,246,0.06)", applyToRows: true },
    { date: date(2025, 12, 24), color: "rgba(239,68,68,0.1)", applyToRows: true },
    { timeFrom: time(12, 0), timeTo: time(13, 0), color: "rgba(156,163,175,0.15)", applyToRows: true }
],
```

### highlights por fila (`items[].highlights`)

Además de los `dateSettings.highlights` globales, cada fila de recurso puede tener su propio array **`highlights`** — p. ej. vacaciones o baja por enfermedad solo para un empleado. Se aplican los mismos campos que arriba; **`applyToRows` no es necesario aquí** (siempre se aplica solo a esta fila).

```javascript
items: (select Mitarbeiter).[{
    id: Nr,
    title: Name,
    highlights: [
        { dateFrom: Urlaub_von, dateTo: Urlaub_bis, color: "rgba(34,197,94,0.15)" },
        { date: Krank_Tag, color: "rgba(239,68,68,0.12)" }
    ],
    timeEntries: [...]
}]
```

**Modo horario:** p. ej. ventanas de bloqueo o mantenimiento con `timeFrom` / `timeTo`.

### header

El bloque `header` controla todas las cabeceras de la Timeline. Consta de tres subbloques opcionales: `months`, `weeks` y `days`. Los tres tienen la misma estructura y se pueden activar de forma independiente. El orden en el widget es siempre: **meses → semanas calendario → días** (de arriba a abajo).

```javascript
header: {
    months: {
        visible: true,
        label: "##monthLong## ##year##",
        fontSize: "11px",
        fontColor: "#555",
        fontWeight: "600",
        backgroundColor: "#ececec",
        highlights: [
            { month: 5, color: "rgba(59,130,246,0.12)" }
        ]
    },
    weeks: {
        visible: true,
        label: "KW ##week##",
        fontSize: "11px",
        fontColor: "#555",
        backgroundColor: "#f2f2f2",
        highlights: [
            { week: 18, color: "rgba(239,68,68,0.1)" }
        ]
    },
    days: {
        visible: true,
        label: "##weekDayShort## ##day##.",
        fontSize: "12px",
        fontColor: "#333",
        fontWeight: "600",
        backgroundColor: "#f9f9f9"
    }
}
```

`header.title` se sitúa **arriba a la izquierda** sobre los nombres de columna (Name, Abteilung), en una superficie tan alta como la fila de mes + KW. Los nombres de columna están en **la misma fila que los días**, con línea hacia arriba. Texto simple sin `fontSize` / `fontWeight` / layout personalizado: **16px, grosor de fuente 400**.

```javascript
header: {
    title: {
        value: "Teamplanung",
        alignX: "left"
    }
}
```

| Property | Beschreibung |
|---|---|
| `value` | Text oder HTML. Alternativ ein Widget-Deskriptor `{ widget, uid, data }`. |
| `customLayout` | HTML oder Widget-Deskriptor (wie in Label-Zellen). Hat Vorrang vor `value`. |
| `alignX` | `"left"` \| `"center"` \| `"right"` |
| `fontSize` / `fontColor` / `fontWeight` / `backgroundColor` | Typo und Fläche des Slots |

Sin mes/KW no existe el slot (sin fila extra). **Legacy:** Solo `header.title` con `label` (`##day##` …) y sin `value`/`customLayout` sigue controlando la **fila de días**, cuando falta `header.days`.

Los tres bloques de fila (`months` / `weeks` / `days`) son opcionales. Si se omite un bloque, no está activo.

**Propiedades comunes (por subbloque):**

| Property | Beschreibung |
|---|---|
| `visible` | `false` blendet die Zeile aus (Standard: `true`) |
| `label` | Anzeigetext mit Platzhaltern (s. u.) |
| `alignX` | `"left"` \| `"center"` \| `"right"` – horizontale Ausrichtung in der Zelle bzw. Gruppe. Standard: **`"left"`** (Monat/KW-Gruppen und Tage). |
| `fontSize` | Schriftgröße, z. B. `"12px"` |
| `fontColor` | Schriftfarbe |
| `fontWeight` | Schriftstärke, z. B. `"600"` |
| `backgroundColor` | Hintergrund der gesamten Zeile bzw. Monats-/KW-Gruppe. **`days`**: färbt u. a. die Tages-Headerzeile; bei **`days.sticky: "left"`** hat der **Beschriftungs-`span` kein eigenes** undurchsichtiges `backgroundColor`, damit **`dateSettings.highlights`** (Farb-Overlay pro Tages-**`div`**) hinter dem Text sichtbar bleibt. |
| `highlights` | Hervorhebungen pro Monats- bzw. KW-Gruppe (s. u.) – **nur** für `months` / `weeks` |
| `sticky` | Optional für **`months`**, **`weeks`** und **`days`**: `sticky: "left"` hält die Bezeichnung bei horizontalem Scrollen sichtbar (CSS `position: sticky`); der horizontale Abstand setzt an den linken sichtbaren Rand des Zeitbereichs, berechnet als **Summe aller linken `columns`-Breiten** + `stickyPadding`. Ohne `sticky` steuert nur `alignX` die Ausrichtung. |
| `stickyPadding` | Zusätzlicher Abstand in px (Zahl) oder z. B. `"12px"`, addiert auf die Labelspalten-Breite für `sticky: "left"`. Wenn weggelassen, wird **+8** px addiert. |
| `hideBelow` | Optional. **`days`**: Spaltenbreite in px — darunter verschwindet der Tages-Text; die Zeile bleibt als Trenner. **Default:** pro Tag eine vertikale Linie (`data-arc-day-ticks`). Mit **`compactRanges: true`** stattdessen KW- bzw. Monats-Ranges (`01.09. - 07.09.`). Default-Schwelle: 36. **`weeks`/`months`**: minimale **Gruppenbreite** in px, unter der der Label-Text ausgeblendet wird — die Zeile bleibt. Defaults: `weeks` 44, `months` 72. |
| `compactRanges` | Optional, nur **`days`**. `true` = beim Rauszoomen Tage zu KW-/Monats-Ranges zusammenfassen. Default: aus (Tick-Linien). |
| `monthCompactBelow` | Optional, nur **`days`**, nur mit `compactRanges`. Spaltenbreite in px — darunter wechselt die Range von KW auf Monat. Default: 14. |
| `labelBreakpoints` | Optional, nur **`days`**: Array `{ below, label }` — bei mittlerer Spaltenbreite kürzeres Tages-Label (bevor der Text ganz verschwindet). |

**Nota:** `dateSettings.highlights` colorean por día la **celda de cabecera del día** (`div`, opcionalmente con `applyToRows` también el área de fila). En combinación con `header.days.sticky: "left"`, ten en cuenta la nota de `backgroundColor` en la tabla.

**Marcadores para `days.label`:**
- `##weekDayShort##` – Día de la semana corto (Lu, Ma, …)
- `##weekDayLong##` – Día de la semana largo (Lunes, Martes, …)
- `##day##` – Día (1–31)
- `##monthShort##` – Mes corto (Ene, Feb, …)
- `##monthLong##` – Mes largo (Enero, Febrero, …)
- `##year##` – Año

**Marcadores para `weeks.label`:**
- `##week##` – Número de semana calendario (01–53, dos cifras con cero inicial)
- `##year##` – Año

**Marcadores para `months.label`:**
- `##monthLong##` – Nombre de mes largo (Enero, Febrero, …)
- `##monthShort##` – Nombre de mes corto (Ene, Feb, …)
- `##year##` – Año

**`highlights` por subbloque:**

```javascript
months: {
    highlights: [
        { month: 5, color: "rgba(59,130,246,0.12)" }   // Mai hervorheben (1 = Januar)
    ]
},
weeks: {
    highlights: [
        { week: 18, color: "rgba(239,68,68,0.1)" }     // KW 18 hervorheben
    ]
}
```

- `months.highlights`: `{ month: N }` – N = 1 (enero) a 12 (diciembre)
- `weeks.highlights`: `{ week: N }` – N = número de semana calendario ISO

## Parámetros de layout

### dayWidth

Ancho de una columna de día (valor inicial). Si el zoom está activo y el usuario aún no tiene un nivel de zoom guardado, se aplica `zoomSettings.defaultDayWidth` — si no, este valor.

```javascript
dayWidth: "120px",
dayWidth: "90px",
```

### zoomSettings

Controla el zoom interactivo y el ancho de columna inicial. El nivel de zoom elegido se guarda por `uniqueId` en el LocalStorage (junto con la posición de scroll), salvo que `persist` sea `false`.

```javascript
zoomSettings: {
    enabled: true,
    defaultDayWidth: 48,
    minDayWidth: 16,
    maxDayWidth: 200,
    step: 8,
    persist: true,
    controls: {
        enabled: true,
        position: "bottomRight"
    }
}
```

- **enabled**: `false` desactiva el zoom por completo — solo `dayWidth` estático, sin controles.
- **defaultDayWidth**: Ancho inicial en px, si no existe un valor guardado.
- **minDayWidth** / **maxDayWidth**: Límite inferior y superior del ancho de columna.
- **step**: Paso en px por clic en +/−.
- **persist**: Guardar el nivel de zoom en LocalStorage (por defecto: `true`).
- **controls**: Botones flotantes abajo a la derecha en el widget.

El zoom con rueda de ratón/pinch no está incluido en v1 (previsto).

### rowHeight

Altura de una fila de recurso. `"auto"` se adapta al contenido.

```javascript
rowHeight: "auto",
rowHeight: "36px",
```

### labelWidth

Ancho estándar para **una** columna de etiqueta, si falta `columns` o si en `columns[i]` no se ha definido `width`. Por defecto: `"120px"`.

```javascript
labelWidth: "150px",
```

### currentTime

Color de la barra que marca la hora actual. Vacío o `false` la oculta.

```javascript
currentTime: "rgba(59,130,246,0.5)",
currentTime: "",
```

## items – recursos y citas

`items` es un array de filas de recurso. Cada fila tiene `timeEntries` (barras). Opcional: `items` (hijos, un nivel) y `columns` (textos de celda por columna de etiqueta).

### Estructura de una fila

```javascript
{
    id: Nr,
    title: Name,
    subtitle: Abteilung,
    fontSize: "13px",
    fontWeight: "normal",
    columns: [
        { title: text(Name) },
        { title: text(Abteilung) }
    ],
    styles: {},
    highlights: [],
    timeEntries: [...],
    items: [...]
}
```

- **id** *(obligatorio)*: ID único (p. ej. Nr del registro). Se necesita para arrastrar y soltar (`resourceField`) y **collapsible**.
- **title** / **subtitle**: Fallback para la **primera** columna de etiqueta, si falta `columns` o la primera celda está vacía.
- **fontSize** / **fontWeight** *(opcional, nivel item)*: Tipografía estándar para todas las celdas de etiqueta de esta fila, siempre que ni la columna de nivel superior ni `columns[i]` definan nada. Orden de prioridad (mayor primero): **`columns[i]`** → **Item** `fontSize`/`fontWeight` → **`styles.fontSize`/`styles.fontWeight`**.
- **columns** *(por fila)*: Un objeto por columna de etiqueta (mismo número que `columns` de nivel superior): `title`, `subtitle`, opcional **`customLayout`** (HTML para esta celda), `actions`, opcional `alignX`, `alignY`, `fontSize`, `fontWeight`.
- **styles**: Opcional (p. ej. fondo de fila; `fontSize`/`fontWeight` aquí actúan como último fallback de tipografía).
- **highlights** *(opcional)*: Fondo de celda para esta fila (ausencias, horarios bloqueados); consulta la sección **highlights por fila**.
- **timeEntries**: Citas/barras en esta fila. **Espacio horizontal:** fijo **4px** hasta el borde de la columna (como el padding de fila arriba/abajo); en barras **de varias columnas** solo en el inicio y fin **exteriores**, no entre los días (no modificable mediante `timeEntries[].styles`).
- **items**: Opcional. Subfilas (un nivel).

### Estructura de un timeEntry (cita/barra)

```javascript
{
    title: Bezeichnung,
    subtitle: Kunde,
    dateFrom: Datum_von,
    dateTo: Datum_bis,
    timeFrom: Zeit_von,
    timeTo: Zeit_bis,
    styles: {},
    dragAction: {},
    actions: [],
    tooltip: {},
    customLayout: "",
    value: ""
}
```

- **title**: Texto en la barra, u objeto `{ value: …, width: … }` (ver abajo). En indicaciones de hora se puede usar `##entrytime##`.
- **subtitle**: Opcional. Segundo bloque de texto en la barra (string u objeto como en **title**).
- **direction** *(opcional)*: `"vertical"` u `"horizontal"` – título y subtitle **uno bajo el otro** o **uno junto al otro**.
  - **Default en strings simples** (sin forma de objeto): `"vertical"` — el subtitle permanece visible aunque el título sea largo.
  - **Default en forma de objeto** (`{ value, width }`): `"horizontal"` — útil para el emparejamiento de ancho (p. ej. título + hora uno junto al otro).
  - Configurarlo explícitamente siempre sobrescribe el default.
- **dateFrom**, **dateTo**: Fecha de inicio y fin (obligatorio).
- **timeFrom**, **timeTo**: Opcional. Hora en milisegundos (0 = medianoche). Sin indicación, la entrada se muestra como de todo el día.
- **styles**: Propiedades similares a CSS (backgroundColor, color, borderRadius, padding).
- **dragAction**: Configuración para arrastrar y soltar.
- **actions**: Array de acciones al hacer clic en la barra (como en otros widgets). El anterior campo único **`clickAction`** sigue siendo compatible y se ejecuta como una entrada de `actions`.
- **tooltip**: Opcional. Overlay al pasar el ratón con información adicional (ver abajo).
- **customLayout**: Opcional. Layout propio en lugar de title/subtitle/value.
- **value**: Opcional. Contenido HTML adicional en la barra (después del bloque título/subtitle).

**Forma de objeto para `title` / `subtitle`:** `{ value: TextOderHtml, width: "fraction" | "auto" | CSS-Länge }`

- **`value`**: Texto o HTML a mostrar (como antes); `##entrytime##` posible.
- **`width`** *(opcional, default `"fraction"`)**:
  - **`"fraction"`** – usa el espacio **restante** en la fila; el texto largo se recorta con puntos suspensivos.
  - **`"auto"`** – **ancho de contenido**; no se reduce a favor del compañero (p. ej. para horas que deben permanecer totalmente visibles). Un texto muy largo puede terminar con puntos suspensivos en el borde de la barra igualmente.
  - **Longitud CSS** (p. ej. `"72px"`, `"4.5rem"`) – ancho base fijo (`flex-basis`) para este bloque.

Si **title** y **subtitle** son solo **strings** (sin objeto) y **direction** no está configurado, se colocan **uno bajo el otro**. Si al menos un campo usa la forma de objeto (sin `direction`), se colocan **uno junto al otro** (Flex + `width`). Con **`direction: "horizontal"`** o **`"vertical"`** fuerzas la alineación.

```javascript
title: { value: text(Bezeichnung), width: "fraction" },
subtitle: { value: "##entrytime##", width: "auto" },
```

### tooltip – información al pasar el ratón sobre la barra

Tooltip opcional al pasar el ratón por `timeEntry`. El overlay se cuelga de `document.body` (flecha + sombra), no se recorta con la barra y permanece abierto de forma estable al pasar el ratón — incluso si Ninox vuelve a renderizar en segundo plano. Es posible mover el ratón hacia el tooltip (widgets/botones interactivos).

```javascript
tooltip: {
    value: "Kunde: Meier GmbH" + newline() + "Status: geplant",
    width: "280px",
    maxWidth: "360px",
    maxHeight: "300px",
    position: "auto",
    styles: {
        backgroundColor: "#1f2937",
        color: "#fff"
    },
    trigger: "hover"
}
```

- **value** *(obligatorio)*: Contenido — **string de texto/HTML** **o** descriptor de nueva generación `{ widget: "arc-widget-layout", uid: "tt-" + Nr, data: { … } }`. ID completo del registro (`arc-widget-…`), config bajo **`data`**, `uid` propio. Widgets anidados con acciones funcionan.
- **width** / **maxWidth** / **maxHeight**: Opcional. Longitudes CSS; con `maxHeight` el contenido hace scroll internamente.
- **position**: `"top"` | `"bottom"` | `"left"` | `"right"` | `"auto"` (default `"auto"` — prefiere arriba, cambia si falta espacio).
- **styles**: Opcional. entre otros `backgroundColor`, `color`, `borderRadius`, `padding`, `boxShadow`. Para un contorno continuo alrededor de la caja **y** la flecha: `borderWidth` + `borderColor` (SVG `stroke` en la forma de la burbuja).
- **trigger**: v1 solo `"hover"` (reservado para un futuro `"click"`).

Técnicamente: el tooltip es una **burbuja SVG** (un path para rectángulo redondeado + flecha integrada), portada a `document.body`. Fill y stroke se aplican a toda la forma — sin triángulo CSS separado.

Un clic en la barra sigue ejecutando `actions`. Sin `tooltip` no cambia nada.

#### Ejemplos

**1) Oscuro + borde (texto)**

```javascript
tooltip: {
    value: "Kunde: Meier GmbH" + newline() +
        "Status: geplant" + newline() +
        "Notiz: Parkplatz hinter dem Gebäude",
    width: "280px",
    position: "auto",
    styles: {
        backgroundColor: "#1f2937",
        color: "#fff",
        borderWidth: "1px",
        borderColor: "rgba(255,255,255,0.28)"
    }
}
```

**2) Claro suave (claro + borde)**

```javascript
tooltip: {
    value: html("<strong>Wartung Anlage Nord</strong><br>Dauer: 3 Tage<br>Ersatzteil: Filterkit #442"),
    maxWidth: "300px",
    position: "bottom",
    styles: {
        backgroundColor: "#ffffff",
        color: "#1f2937",
        borderWidth: "1px",
        borderColor: "rgba(15,23,42,0.14)",
        borderRadius: "10px",
        boxShadow: "0 10px 28px rgba(15, 23, 42, 0.12)"
    }
}
```

**3) Claro + widget (vertical, botón a todo lo ancho)**

```javascript
tooltip: {
    value: {
        widget: "arc-widget-layout",
        uid: "tt-" + Nr,
        data: {
            direction: "vertical",
            gap: "8px",
            width: "100%",
            blocks: [
                {
                    width: "100%",
                    value: html("<div style='font-weight:600;color:#0f172a'>Termin-Details</div>")
                },
                {
                    width: "100%",
                    value: html("<div style='color:#334155;font-size:12px'>Kunde: Schmidt AG<br>Thema: Angebot Q3</div>")
                },
                {
                    width: "100%",
                    value: {
                        widget: "arc-widget-button",
                        uid: "tt-btn-" + Nr,
                        data: {
                            title: "Öffnen",
                            width: "100%",
                            height: "30px",
                            fontSize: "12px",
                            backgroundColor: "#3b82f6",
                            fontColor: "#fff",
                            borderRadius: "6px",
                            actions: [{ type: "popup", recordId: Nr }]
                        }
                    }
                }
            ]
        }
    },
    width: "240px",
    position: "top",
    styles: {
        backgroundColor: "#ffffff",
        color: "#0f172a",
        borderWidth: "1px",
        borderColor: "rgba(15,23,42,0.16)",
        borderRadius: "12px",
        padding: "12px"
    }
}
```

**4) Posición izquierda / derecha**

```javascript
tooltip: {
    value: "Zusatzinfo neben dem Balken",
    width: "240px",
    position: "right", // oder "left"
    styles: {
        backgroundColor: "#ffffff",
        color: "#0f172a",
        borderWidth: "1px",
        borderColor: "rgba(15,23,42,0.16)"
    }
}
```

Consejo: En la barra a menudo conviene establecer `direction: "vertical"`, para que título y subtitle queden uno bajo el otro y el subtitle permanezca visible.

### styles – estilo de las barras de cita

```javascript
styles: {
    backgroundColor: "#3b82f6",
    color: "#fff",
    borderRadius: "6px",
    padding: "4px 8px"
},
```

### dragAction – mover citas

Define qué campos se actualizan al arrastrar.

```javascript
dragAction: {
    recordId: Nr,
    dateFrom: fieldId(Nr, "Datum_von"),
    dateTo: fieldId(Nr, "Datum_bis"),
    resourceField: fieldId(Nr, "Bearbeiter"),
    enabled: true,
    changeFieldValues: [{
        fieldId: fieldId(Nr, "CacheTrigger"),
        value: now()
    }]
},
```

- **recordId**: ID del registro de la cita (normalmente `Nr`).
- **dateFrom**, **dateTo**: Field IDs para fecha de inicio y fin. Se actualizan al mover horizontalmente.
- **resourceField**: Opcional. Field ID para el recurso (p. ej. responsable). Se actualiza al mover verticalmente (cambio de fila) con el `id` de la fila de destino — **solo si** la fila de origen y la de destino tienen la misma **profundidad** en la jerarquía de `items` (`_depth` igual). Necesario, por ejemplo, para que ninguna cita se arrastre de una fila de equipo a una fila de empleado (o al revés); **empleado bajo equipo A → empleado bajo equipo B** está permitido.
- **enabled**: Opcional. `false`: sin arrastre para esta barra (p. ej. sumas de parent). Si se omite, el arrastre está activo en cuanto se define `dragAction`.
- **changeFieldValues**: Opcional. Array de `{ fieldId, value }` – se escribe **después** de un arrastre exitoso (la fecha/hora y/o el recurso han cambiado). Uso típico: actualizar campos de caché o de trigger. Estructura igual que en las acciones de Create (`ghost.actions`).

⚠️ **Nota:** Sin `dragAction`, arrastrar y soltar está desactivado. El arrastre vertical entre **diferentes profundidades** (p. ej. fila de equipo ↔ fila de empleado) no es posible; dentro de la misma profundidad (p. ej. solo filas de empleado) sí es posible **incluso entre diferentes parents** (equipos).

### actions – clic en cita

```javascript
actions: [{
    type: "popup",
    recordId: Nr
}],
```

- **type**: `"popup"` abre el registro en una ventana popup.
- **recordId**: ID del registro de la cita clicada.

Varias entradas en el array se ejecutan sucesivamente. **Legacy:** Un único objeto **`clickAction`** (sin array) sigue funcionando.

### actions – clic en celda de etiqueta

En **`items[].columns[i]`** se puede definir opcionalmente **`actions`**. Si el array no está vacío, un clic en la celda ejecuta las mismas acciones de Ninox. **Nota:** En la **primera** columna de etiqueta, en **grupos contraídos** (`collapsible`) el clic tiene prioridad para expandir/contraer; ahí no se ejecutan `actions` adicionales en la celda.

```javascript
items: (select Fahrzeuge).[{
    id: Nr,
    columns: [
        { title: text(Bezeichnung), subtitle: "Fahrzeug" },
        {
            title: "⌂",
            actions: [{
                type: "popup",
                recordId: Nr
            }]
        }
    ],
    timeEntries: [...]
}]
```

`columns` pertenece a la fila correspondiente bajo **`items`**. Un clic en la segunda columna (aquí **⌂**) abre el registro de la fila (`Nr`). Se ejecutan sucesivamente tantas entradas como se quiera en el array `actions`.

### ghost – barra de fila para nuevas entradas (wrapper)

A **nivel de cada entrada en `items[]`** (no en `columns[]`) se puede definir opcionalmente un objeto **`ghost`** — **independiente por fila** (recurso). Una fila sin `ghost` no tiene franja de creación.

**Campos:**

- **`title`** – opcional, se usa como **tooltip del navegador** (atributo `title`) en el botón de más (p. ej. «Crear cita»).
- **`position`** – opcional, controla la visualización y el layout del **botón** ghost por columna:
  - **`bottomRight`** (default, puede omitirse) – botón cuadrado pequeño (**28×28 px**), **absoluto** en la columna correspondiente abajo a la derecha; no ocupa **toda** la anchura de columna. El botón está **fuera del grid CSS** de la fila (absoluto respecto a la fila), para que los **timeEntries** permanezcan en la misma fila de grid que sin ghost. **Stacking:** el botón queda justo **por encima** de las barras normales (`z-index` **11**), **por debajo** del hover de barra (**10**) y de la barra **activa/en arrastre** (**100**).
  - **`bottomLeft`**, **`topRight`**, **`topLeft`** – mismo botón, otra esquina de la columna.
  - **`timeEntry`** – **comportamiento clásico**: todo el ancho de columna en la **primera fila de grid** (con **4px** de margen horizontal como las barras de cita), desplaza las barras de cita visibles a la fila siguiente (`align-content: start` en la fila).
- **`actions`** – obligatorio, al menos una acción de Ninox; normalmente **`type: "create"`** con campos precargados. Un clic en el **botón de más** ejecuta estas acciones con el **valor de esta columna** (ver marcadores).
- **`styles`** – opcional, objeto con **propiedades CSS en camelCase** (como en **`timeEntries[].styles`**), p. ej. `backgroundColor`, **`fontColor`** (color de texto incl. icono de más mediante `currentColor`), `fontSize`, `fontWeight`, `borderRadius`, **`paddingX`** / **`paddingY`** (se combinan en `padding`) o **`padding`** (shorthand), `border`, `boxShadow`. El legacy **`color`** se mapea como **`fontColor`** al color de fuente; si **`fontColor`** está definido, tiene prioridad. Se aplican al botón; **`gridColumn` / `gridRow`** los define el widget para la colocación en la cuadrícula (sobrescribe posibles valores propios en `styles`).

**Representación (default `bottomRight` entre otros):** Por columna, un **área absoluta con clic transparente** sobre la fila (sin elemento de grid — evita el desplazamiento de las barras). El **botón de más** es **blanco**, con **sombra ligera**, esquina según `position`. **Hover de columna (ratón en la columna):** El botón se hace visible con **fondo blanco semitransparente** y desenfoque **`backdrop-filter`** (frosted glass). **Directamente sobre el botón:** blanco **opaco**, sin blur — legibilidad total. Mediante **`styles`** puedes definir, por ejemplo, `backgroundColor` (sobrescribe los colores estándar).

**Representación (`timeEntry`):** Como una **barra** estrecha sobre el ancho de columna en la fila 1 (con **4px** de margen izquierda/derecha como las barras de cita); los **timeEntries** de la fila empiezan debajo.

**Visibilidad / hover:**

- **Todas las posiciones excepto `timeEntry`:** El botón de más es **invisible por defecto** y aparece solo cuando el **ratón está en algún lugar de esta columna de día/hora** (incluso sobre una barra); la columna se determina según la posición del ratón sobre la **fila**. En **dispositivos táctiles** (sin hover preciso), los botones ghost permanecen **visibles**.

- **`timeEntry`:** Para cada columna se comprueba por separado si hay una barra de cita **visible** que llegue a la columna. **Sin** barra: con **`@media (hover: hover)`** la barra ghost está oculta inicialmente por opacidad, hasta que el ratón pasa sobre la **celda ghost**. **Con** barra en la columna: la barra ghost permanece visible. En **táctil** los elementos ghost permanecen visibles.

**Marcadores** (en cualquier valor string dentro de la acción, de forma recursiva):

- **`##columnValue##`** – valor en milisegundos de la **columna bajo el clic**: en **modo día** inicio del día (`startDate` + índice de columna), en **modo hora** inicio del intervalo de tiempo (`startTime` + índice × `amount`).
- **`##clickedDate##`** – alias con la misma semántica (compatibilidad con Calendar Grid / Week).

```javascript
items: (select Mitarbeiter).[{
    id: Nr,
    ghost: {
        position: "bottomRight",
        title: "Termin anlegen",
        styles: {
            backgroundColor: "rgba(0, 0, 0, 0.08)",
            fontColor: "#333333",
            borderRadius: "6px",
            paddingY: "4px",
            paddingX: "8px"
        },
        actions: [{
            type: "create",
            tableId: "B",
            popup: true,
            changeFieldValues: [
                { fieldId: fieldId(Nr, "Datum"), value: "##columnValue##" },
                { fieldId: fieldId(Nr, "Bearbeiter"), value: number(Nr) }
            ]
        }]
    },
    timeEntries: [...]
}]
```

Los clics en las **barras de cita** (timeEntries) siguen disparando solo las **`actions`** de cada barra, no `ghost.actions`.

**Ejemplo – modo horario:** `##columnValue##` es el **inicio del intervalo de tiempo clicado** en milisegundos (como `timeFrom` en las barras).

```javascript
arcCustomCalendarTimeline({
    uniqueId: "timeline-fleet-neu",
    mode: "time",
    timeSettings: {
        startTime: 6,
        duration: 12,
        amount: 0.25
    },
    columnWidth: "60px",
    rowHeight: "36px",
    height: "700px",
    dateSettings: {
        header: {
            title: {
                label: "##hour##:##minute##",
                fontSize: "11px",
                fontColor: "#333"
            }
        }
    },
    columns: [
        { title: "Fahrzeug", width: "160px" },
        { title: "Info", width: "80px" }
    ],
    items: (select Fahrzeuge).[{
        id: Nr,
        ghost: {
            title: "Neue Fahrt",
            actions: [{
                type: "create",
                tableId: "B",
                popup: true,
                changeFieldValues: [
                    { fieldId: fieldId(Nr, "Startzeit"), value: "##columnValue##" },
                    { fieldId: fieldId(Nr, "Fahrzeug"), value: number(Nr) }
                ]
            }]
        },
        timeEntries: [...]
    }]
})
```

Alternativamente puedes usar **`##clickedDate##`** en lugar de `##columnValue##` — ambos se resuelven igual.

## weekStart

Día de inicio de la semana (0 = domingo, 1 = lunes, …). Afecta al orden de los días.

```javascript
weekStart: 1,
```

## timeSettings – modo horario (Time Mode)

Si en lugar de días quieres mostrar **horas** como columnas, puedes activar el modo horario. Es ideal para **diagramas de flota**, **planes de turnos** o **planificación diaria de intervenciones**, donde el eje X representa un solo día.

El modo horario se activa automáticamente en cuanto se define `timeSettings` — o explícitamente con `mode: "time"`.

```javascript
arcCustomCalendarTimeline({
    uniqueId: "fleet-timeline",
    mode: "time",
    timeSettings: {
        startTime: 6,
        duration: 12,
        amount: 0.25
    },
    columnWidth: "60px",
    rowHeight: "36px",
    height: "700px",
    dateSettings: {
        header: {
            title: {
                label: "##hour##:##minute##",
                fontSize: "11px",
                fontColor: "#333"
            }
        }
    },
    columns: [
        { title: "Fahrzeug / Tag", width: "160px" },
        { title: "Info", width: "80px" }
    ],
    items: (select Fahrzeuge).[{
        id: Nr,
        title: Bezeichnung,
        fontSize: "12px",
        fontWeight: "600",
        columns: [
            { title: text(Bezeichnung), subtitle: "Fahrzeug" },
            { title: "⌂" }
        ],
        items: (select Einsatztage where Fahrzeug = Nr).[{
            id: Nr,
            title: Wochentag,
            fontSize: "11px",
            columns: [
                { title: text(Wochentag) },
                { title: "" }
            ],
            timeEntries: (select Fahrten where Einsatztag = Nr).[{
                title: Route,
                timeFrom: Startzeit,
                timeTo: Endzeit,
                styles: {
                    backgroundColor: text(Farbe),
                    color: "#fff"
                }
            }]
        }]
    }]
})
```

### startTime

A partir de qué hora empieza la línea de tiempo (p. ej. `6` = 06:00 h).

```javascript
startTime: 6,
startTime: 8,
```

### duration

Cuántas horas se muestran (p. ej. `12` = 12 horas desde `startTime`).

```javascript
duration: 12,
duration: 8,
```

### amount

Intervalo de columna en horas. Determina la granularidad de la división de tiempo.

```javascript
amount: 1,
amount: 0.5,
amount: 0.25,
```

- `1` = columnas por hora (08:00, 09:00, …)
- `0.5` = columnas de 30 minutos (08:00, 08:30, 09:00, …)
- `0.25` = columnas de 15 minutos (08:00, 08:15, 08:30, …)

### Marcadores de cabecera en modo horario

En modo horario hay marcadores adicionales disponibles para `dateSettings.header.days.label`:

- `##hour##` – Hora con cero inicial (06, 07, …)
- `##minute##` – Minuto con cero inicial (00, 15, 30, …)
- `##time##` – Hora completa (06:00, 08:15, …)

### columnWidth

Ancho de una columna de tiempo (alias de `dayWidth` en modo horario).

```javascript
columnWidth: "60px",
columnWidth: "80px",
```

### timeEntries en modo horario

En modo horario, `timeEntries` usa los campos `timeFrom`/`timeTo` en lugar de `dateFrom`/`dateTo` (en milisegundos desde medianoche):

```javascript
timeEntries: [{
    title: "RVS GZ-NU-KE",
    timeFrom: 27000000,
    timeTo: 41400000,
    styles: {
        backgroundColor: "#0066cc"
    }
}]
```

💡 Conversión: `Horas × 3600000` (p. ej. 07:30 = 7,5 × 3600000 = 27000000)

### Arrastrar y soltar en modo horario

En modo horario, al arrastrar y soltar se actualizan `timeFrom`/`timeTo` en lugar de `dateFrom`/`dateTo`:

```javascript
dragAction: {
    recordId: Nr,
    timeFrom: fieldId(Nr, "Startzeit"),
    timeTo: fieldId(Nr, "Endzeit"),
    resourceField: fieldId(Nr, "Einsatztag")
}
```

## columns (nivel superior)

Define las **columnas de etiqueta izquierdas** (junto al área de tiempo). Por entrada en el array: `title` (aparece en la fila con las etiquetas de día), opcional `subtitle`, opcional `width` (ancho CSS, p. ej. `"200px"`). Si falta `width`, se aplica `labelWidth` (por defecto `120px`).

Opcional: **`alignX`**: `"left"` \| `"center"` \| `"right"` (por defecto `left`), **`alignY`**: `"top"` \| `"center"` \| `"bottom"` (por defecto `center`) – alineación de la cabecera y el contenido de celda en esta columna. **`fontSize`** y **`fontWeight`** (valores CSS, p. ej. `"13px"`, `"600"` o `"bold"`) se aplican a cabecera y celdas de esta columna, siempre que ni **`items[].columns[i]`** ni el **item**-`fontSize`/`fontWeight` estén definidos. Por celda se puede sobrescribir en `items[].columns[i]` `alignX`, `alignY`, `fontSize`, `fontWeight`.

**Cabecera de las columnas de etiqueta** (Name, Abteilung): El color estándar procede de **`dateSettings.header.days`** (`backgroundColor`, `fontColor`). Se puede sobrescribir por columna con `columns[n].backgroundColor` y `columns[n].fontColor`.

Sin `columns` o array vacío: **una** columna con `labelWidth`.

## items – filas de datos y anidación

Todos los recursos se llaman uniformemente **`items`**. Lista plana: `items: [{ id, title, subtitle, timeEntries, … }]`.

**Anidación (un nivel):** Un item puede contener `items: [...]`. El parent y los hijos se renderizan como **filas propias** una bajo la otra; la primera columna de etiqueta en los hijos aparece **con sangría**. Más de un nivel no está previsto actualmente en la Timeline (la estructura de datos se puede ampliar más adelante).

**`items[].columns`** (por fila): Array con **una entrada por columna de etiqueta** (misma longitud que `columns` de nivel superior). Campos por celda: `title`, `subtitle`, opcional `customLayout`, opcional `actions`, opcional `alignX` / `alignY`, opcional `fontSize` / `fontWeight` (máxima prioridad para tipografía). Después actúan **item**-`fontSize`/`fontWeight`, luego la columna de nivel superior, luego **`styles`**. Entradas faltantes → celda vacía. **Fallback:** Si falta `columns` o la primera celda no tiene contenido, se usan `title`/`subtitle` del item **solo para la primera columna**.

**Parent con subfilas:** La fila parent tiene `timeEntries` como cualquier otra fila (p. ej. barras de suma). Los hijos están en `items` y tienen sus propios valores de `columns`.

```javascript
columns: [
    { title: "Fahrzeug / Tag", width: "160px" },
    { title: "Info", width: "80px" }
],
items: [{
    id: "v1",
    title: "A-MS 419",
    subtitle: "GPS",
    columns: [
        { title: "A-MS 419", subtitle: "GPS" },
        { title: "⌂", subtitle: "Fzg." }
    ],
    timeEntries: [{ title: "Tagesblock", timeFrom: ... }],
    items: [{
        id: "v1-mo",
        title: "Mo",
        columns: [{ title: "Mo" }, { title: "" }],
        timeEntries: [...]
    }]
}]
```

## collapsible – expandir y contraer grupos

Si usas entradas jerárquicas (`items` con sub-`items`), puedes **contraer y expandir** la **fila parent** (p. ej. nombre de equipo o vehículo) mediante una prop del widget. El estado se persiste **por `uniqueId` en el navegador** (`localStorage`).

### Configuración

```javascript
arcCustomCalendarTimeline({
    uniqueId: "timeline-teams-" + Nr,
    collapsible: {
        enabled: true,
        iconPosition: "left",
        iconCollapse: "",
        iconExpand: ""
    },
    columns: [ { title: "Team / Person", width: "200px" }, { title: "Termine", width: "120px" } ],
    items: [ /* Parent mit items */ ]
})
```

| Property | Bedeutung |
|----------|-----------|
| `enabled` | `true`: Toggle-Icon in der **ersten Label-Spalte** der Parent-Zeile; Klick klappt alle Unterzeilen ein/aus. |
| `iconPosition` | *(Optional)* `"left"` oder `"right"` – Icon vor/neben dem Text (Standard: `left`). |
| `iconCollapse` | *(Optional)* HTML/SVG für **aufgeklappt** (Standard: Chevron unten). |
| `iconExpand` | *(Optional)* HTML/SVG für **eingeklappt** (Standard: Chevron rechts). |

- Se aplica a filas con `items` (hijos). **`id` del item parent** debe ser único (estado de plegado).
- `styles` en el parent/hijo actúan sobre toda la fila de etiqueta (fondo de las celdas).

---

Con Custom Calendar Timeline llevas planificación basada en recursos y total flexibilidad a tu base de datos Ninox — ideal para planificación de intervenciones, vista de capacidades, gestión de recursos y diagramas de flota.

Happy Widgeting 🥳
