---
title: "Custom Calendar (Week)"
slug: "custom-calendar-week"
category: "Widgets"
reactComponent: "ArcWidgetCalendarWeek"
reactTier: "premium"
reactSince: "0.1.0-alpha.5"
reactExample: "calendar_week.tsx"
locale: "es"
hosts: "ninox"
---

# Custom Calendar (Week)

## AI Defaults (read first)

- **uniqueId**: Required, `"calendar-" + Nr`.
- **height**: Use `"100%"` when embedded in a layout.
- **timeZoneBalance**: Set `-1` for German timezone in `dateSettings`.
- **dateFrom/dateTo**: Must be actual date field values, not strings.
- **dragAction**: Requires `recordId: Nr` and field IDs via `fieldId(Nr, "Feldname")`. Optional `changeFieldValues` for extra field updates after a successful drag (cache/trigger fields).
- **createNew**: Needs `tableId` and field IDs via `fieldId(Nr, "Feldname")`. Optional `changeFieldValues` for extra fields on create. Optional `dataBag` after a successful create (same as Button create; `##newRecordId##`). Optional `item` for click-to-place (duration, clock, `offset: { days, time }`, `splits`, `linked`). `linked: false` + clock = satellite (does not pin the duration chain). Shift moves the whole ghost; release snaps satellites back. Create writes the same four date/time fields — overall span of the chain. Toggle with `item.active`. Empty selection: omit `item` (never `else ""`). Same-title pause: `splits`. Named leftover pieces (Teil 1 / Teil 2): three `segments`, not `splits`.
- **Persistence**: Drag/create go through the **Action Performer** (`type: "update"` / `type: "create"`). Ninox config (`dragAction` / `createNew`) is unchanged. React hosts: handle these via `onAction` — **no** global `database` stub.
- **timeSettings**: Omit for full 0–24h range. Set `from`/`to` in hours (integers) to restrict.
- **lang**: Set `lang: clientLang()` for localized weekdays/months.

# Custom Calendar (Week)

<iframe src="https://www.youtube.com/embed/ln3SRc_Cv9k?iv_load_policy=3&rel=0&modestbranding=1&playsinline=1&autoplay=1&mute=1" data-thumbnail="Medium Quality" frameborder="0" allow="presentation; fullscreen; accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"></iframe>Con el widget **Custom Calendar Week** llevas una potente vista de calendario directamente a tu base de datos Ninox. Puedes integrarlo sin problemas en cualquier tabla, diseñarlo con flexibilidad y rellenarlo dinámicamente con tus propios datos. Ya sea planificación de proyectos, gestión de recursos o vista de citas: el widget se adapta a tus necesidades.

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

- **Diseño individual:** Adapta colores, iconos y layout a tu estilo o tu marca.
- **Entradas flexibles:** Diseña la representación de tus citas, tareas o eventos exactamente como las necesites, con los campos, iconos o etiquetas que quieras.
- **Eventos de varios días y de todo el día:** Planifica con claridad y detecta de un vistazo los periodos más largos.
- **Arrastrar y soltar:** Mueve citas directamente en el calendario, de forma intuitiva y sin rodeos.
- **Filtrado dinámico:** Muestra solo las entradas relevantes, controlable mediante script de Ninox.
- **Resaltado de días especiales:** Marca festivos, vacaciones, ausencias o eventos individuales.
- **Eje de tiempo personalizable:** Muestra solo las horas importantes para tu caso de uso, p. ej. de 8 a 18 h en lugar de 0 a 24 h.
- **Integración de varias tablas:** Vincula datos de distintas fuentes, ideal para equipos o proyectos distribuidos.
- **Funciona sin conexión:** Tu calendario sigue siendo utilizable incluso sin internet.

📅 **Custom Calendar Week** hace que tu planificación de citas en Ninox sea más clara, interactiva y notablemente más eficiente, perfecto para quienes quieren sacar más partido a su base de datos.

## Código de aplicación


```javascript
let current := this;
arcCustomCalendarWeek({
        uniqueId: "cal-39",
        embedded: false,
        weekdaySettings: {
            week: 12,
            year:2024
        },
        weekStart: "",
        dateSettings: {
            startDate: date(2025, 2, 2),
            endDate: "",
            duration: 7,
            highlights: [{
                    date: date(2025, 2, 7),
                    color: "#F1B6CE"
                }, {
                    weekday: 6,
                    color: "rgba(255,0,0,0.1)"
                }, {
                    weekday: 7,
                    color: "rgba(255,0,0,0.1)"
                }]
        },
        timeZoneBalance: "",
        timeSettings: {
            timeStart: time(6, 30),
            duration: time(15, 0)
        },
        currentTime: "rgba(0,0,255,0.5)",
        lang: clientLang(),
        timeSlotWidth:  "250px",
        timeSlotHeight: "80px",
        allDayHeight: "",
        styles: {
            backgroundColor: "rgba(0,0,255,0.01)"
        },
        createNew: {
            tableId: "B",
            dateFrom: "A",
            timeFrom: "B",
            dateTo: "C",
            timeTo: "D",
            changeFieldValues: [{
                fieldId: fieldId(Nr, "CacheTrigger"),
                value: now()
            }]
        },
        timeEntries: (select 'Time Entries').[{
                title: Kalendertitel,
                subtitle: Description,
                value: "",
                customLayout: "",
                dateFrom: 'Date From',
                dateTo: 'Date To',
                timeFrom: 'Time From',
                timeTo: 'Time To',
                styles: {
                    backgroundColor: color(Person),
                    color: "#fff",
                    borderRadius: "8px",
                    padding: "5px"
                },
                insetBorder: {
                    enabled: true,
                    stroke: "dotted"
                },
                dragAction: {
                    recordId: Nr,
                    dateFrom: "A",
                    timeFrom: "B",
                    dateTo: "C",
                    timeTo: "D",
                    changeFieldValues: [{
                        fieldId: fieldId(Nr, "CacheTrigger"),
                        value: now()
                    }]
                },
                clickAction: {
                    type: "popup",
                    recordId: Nr
                },
                segments: [{
                    title: "Anfahrt",
                    timeFrom: SegVon,
                    timeTo: SegBis,
                    dateFrom: 'Seg Date From',
                    dateTo: 'Seg Date To',
                    linked: false,
                    dragAction: {
                        recordId: Nr,
                        dateFrom: "E",
                        timeFrom: "F",
                        dateTo: "G",
                        timeTo: "H"
                    }
                }]
            }]
    })
```

## Ajustes generales

### weekdaySettings – control específico de la semana calendario

Con el parámetro `**weekdaySettings**` puedes definir exactamente **qué semana calendario de qué año** se muestra en el calendario.

En este ejemplo se muestra la **semana calendario 12 del año 2025**.

En lugar de valores fijos, también puedes referenciar campos de Ninox para dinamizar los datos.

⚠️ **Importante: **Si están configurados tanto `dateSettings` como `weekdaySettings`, `dateSettings`** tiene prioridad** y sobrescribe los valores de `weekdaySettings`.

### dateSettings – inicio, duración y resaltado en la vista semanal

Con el grupo de parámetros `**dateSettings**` controlas **cuándo empieza tu calendario, cuánto tiempo se muestra y qué días deben resaltarse especialmente**.

**Parámetros individuales en dateSettings:**

- `**startDate**` *(obligatorio)*
Define la fecha de inicio del calendario. Indica aquí un campo de fecha.
→ Se puede controlar dinámicamente mediante campos de Ninox.
- `**endDate**` *(opcional)*
Indica la **fecha de fin** del calendario. Indica aquí un campo de fecha.
→ Tiene prioridad sobre `duration`.
- `**duration**` *(opcional)*
Determina el **número de días a mostrar** (por defecto: 7).
→ Solo se tiene en cuenta si `endDate` no está configurado.

**Lógica de la duración de visualización:**

- Solo `startDate` configurado → muestra **7 días** a partir de la fecha de inicio
- `startDate` + `duration` → muestra el **número de días** definido
- `startDate` + `endDate` → `**duration**`** se ignora**
- `**highlights**` *(opcional)*
Permite **resaltar con color determinados días** – ideal para vacaciones, festivos u otras ocasiones especiales.
→ Puedes marcar tanto **fechas concretas** como **días de la semana** (detalles más abajo).
- `**header**` *(opcional)*
Controla la representación de las cabeceras de día (día de la semana, fecha). Sin indicarlo se usan etiquetas estándar.

```javascript
header: {
    backgroundColor: "#F8FAFC",
    title: {
        label: "##weekDayShort##. ##day##.",
        fontSize: "15px",
        fontColor: "#000",
        alignX: "center"
    },
    subtitle: {
        label: "##weekdayName##",
        fontSize: "12px",
        fontColor: "#666"
    }
}
```

**Marcadores para label:** `##date##`, `##weekdayName##`, `##weekDayLong##`, `##weekDayShort##`, `##day##`, `##month##`, `##monthLong##`, `##monthShort##`, `##year##`

📌 **Nota:** `subtitle` es opcional. Si solo indicas `title` (sin `subtitle`), la segunda línea no se muestra – el título recibe todo el espacio.
- `**day**` *(opcional)*
Controla las acciones al hacer clic en un día (p. ej. seleccionar fecha, cambiar de vista).

```javascript
day: {
    actions: [{
        type: "update",
        recordId: Nr,
        field: fieldId(Nr, "helper_selectedDate"),
        value: "##clickedDate##"
    }]
}
```

**Marcador:** `##clickedDate##` se sustituye por la marca de tiempo en milisegundos de la fecha clicada (p. ej. `1731369600000`).


```javascript
dateSettings: {
    startDate: "",
    endDate: "",
    duration: 14,
    highlights: [{
            date: date(2024, 10, 31),
            color: 'Color Highlights'
        }, {
            weekday: 6,
            color: "rgba(255,0,0,0.1)"
        }, {
            weekday: 7,
            color: "rgba(255,0,0,0.1)"
        }]
}
```

### weekStart – definir el día de inicio de la semana calendario

Con el parámetro `**weekStart**` determinas **en qué día de la semana debe empezar tu calendario**.

📌 **Tipo:** Número (0–6)
📅 **Valores:**

- `0` = Domingo *(habitual, p. ej., en EE. UU.)*
- `1` = Lunes *(estándar en Europa Central)*
- `2` = Martes
- ...
- `6` = Sábado

💡 **Consejo: **Para la mayoría de los usuarios en el ámbito hispanohablante, `1` (lunes) es la opción adecuada.


```javascript
weekStart: 1,
```

### timeZoneBalance – corrección manual de zona horaria para una visualización precisa

**Tipo:** Número (en horas)

El parámetro `**timeZoneBalance**` te ayuda a **compensar diferencias de zona horaria** que pueden surgir por la forma en que Ninox representa los datos.

Contexto**: **Ninox usa internamente otra lógica de zona horaria, lo que puede provocar un desplazamiento de las horas en el calendario.

Recomendación para Alemania: Ajusta el valor a `-1` para adaptar la visualización a la hora centroeuropea (CET/CEST).


```javascript
timeZoneBalance: -1,

timeZoneBalance: "" // default: Zeiten werden wie in Ninox gespeichert angezeigt, die Nutzer-Zeitzone wird nicht beachtet.
```

### timeSettings – control individual del eje horario

Con el grupo de parámetros `**timeSettings**` puedes definir **qué franja horaria del día se muestra en el calendario**. Así puedes adaptar mejor la vista del calendario a tu caso de uso, p. ej., para horarios de oficina típicos o modelos de turnos.


```javascript
timeSettings: {
        timeStart: time(6, 30), // Zeitfeld oder Milisekunden
        duration: time(6, 30), // Duration in Milisekunden angeben. hier: 8 Stunden
        },
```

En este ejemplo el día del calendario empieza a las **06:00** y termina a las **22:00**.

🧠 **Importante saber:**

- Solo se admiten **horas completas**.
- Si se indica, p. ej., `6.5` (para las 06:30), **el sistema redondea a las 06:00**.
- El rango estándar sin configuración propia es de **0 a 24 h** (es decir, un día completo).

💡 **Consejo: **Menos horas visibles = representación más compacta = mejor visión de conjunto con muchas entradas.

### currentTime – marcado en color de la hora actual

El parámetro `**currentTime**` controla la representación de una barra estrecha que te muestra **la hora actual en el calendario** – de forma similar a las apps de calendario conocidas.

🎨 Función: Con este parámetro fijas el color de la barra que marca la posición actual dentro del día.


```javascript
currentTime: "rgba(0,0,255,0.5)", // RGB Codes
currentTime: "#f95757" // HEX Codes
```

### lang

Esta función se configura por defecto y no necesita ajustarse.


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

### timeSlotWidth – ancho de cada día de la semana

Con el parámetro `**timeSlotWidth**` fijas **qué ancho tienen las columnas de cada día en el calendario semanal**.

<pre data-language="JavaScript">`timeSlotWidth:  "250px" ,`</pre>→ Cada día de la semana se muestra con **250 píxeles de ancho**.

📌 **Nota:**

- El valor por defecto es de aproximadamente **100 px**, pero puede ajustarse según los requisitos del layout.
- Un número más alto ofrece **más espacio por día**, especialmente útil con muchas entradas.


### timeSlotHeight – fijar la altura de las filas horarias

Con el parámetro `**timeSlotHeight**` determinas **qué altura tiene cada hora en el calendario**.

<pre data-language="JavaScript">`timeSlotHeight: "80px",`</pre>→ Cada hora recibe una **altura de 50 píxeles** en el eje de tiempo vertical.

📌 **Nota:**

- El valor por defecto es de aproximadamente **40 px**.
- Un valor más alto ofrece **más espacio por hora.**

### allDayHeight – altura de la fila para entradas de todo el día

Con el parámetro `allDayHeight` fijas qué altura tiene la fila superior para las entradas de todo el día.

→ La fila para entradas de todo el día se muestra con una altura de 60 píxeles.

📌 Nota:

- Por defecto la altura es de aprox. 40 px.
- Un valor más alto crea más espacio para varias entradas de todo el día o representaciones de texto más grandes.

### styles – estilo individual para tu calendario

Con `**styles**` puedes **adaptar el aspecto de tu calendario** – aquí puedes personalizar actualmente el color de fondo de tu calendario.

<pre data-language="JavaScript"><code>	styles: {
            backgroundColor: "rgba(0,0,255,0.01)"
        },</code></pre>### createNew – crear nuevas citas arrastrando

Con el parámetro `**createNew**` activas la posibilidad de **crear nuevas citas directamente arrastrando un rango de tiempo en el calendario**.

🖱️ **Así funciona:**

- Haces clic en una zona libre del calendario, **arrastras con el botón del ratón pulsado** y sueltas.
- Con esto se crea automáticamente una nueva cita, incluyendo **hora de inicio y duración**.

📌 **Nota:**

- Todas las referencias de campo (`dateFrom`, `dateTo`, `timeFrom`, `timeTo`) deben coincidir con los **Field IDs** de los campos correspondientes de tu **tabla de citas del calendario (**`**tableId**`**)**.
- Puedes averiguar fácilmente los Field IDs con la función de Ninox `fieldId()` e incorporarlos directamente en la configuración.

**💡 **Consejo: Ideal para una planificación de citas rápida e intuitiva, p. ej., en turnos, reservas o asignación de salas.

Opcionalmente puedes usar **`changeFieldValues`** para establecer campos adicionales en el nuevo registro – p. ej., campos de caché o de trigger:

```javascript
createNew: {
    tableId: "B",
    dateFrom: fieldId(Nr, "Datum von"),
    timeFrom: fieldId(Nr, "Von"),
    dateTo: fieldId(Nr, "Datum bis"),
    timeTo: fieldId(Nr, "Bis"),
    changeFieldValues: [{
        fieldId: fieldId(Nr, "CacheTrigger"),
        value: now()
    }]
}
```

Opcional **`dataBag`**: tras crear correctamente, se actualiza el mismo bote de estado JSON que en Button-Create. Solo se ejecuta si el registro se creó realmente. `##newRecordId##` se sustituye por el ID de la nueva cita. Típico: vaciar la selección para que desaparezca el ghost.

```javascript
createNew: {
    tableId: "B",
    dateFrom: fieldId(Nr, "Datum von"),
    timeFrom: fieldId(Nr, "Von"),
    dateTo: fieldId(Nr, "Datum bis"),
    timeTo: fieldId(Nr, "Bis"),
    item: if Auswahl then {
        /* Ghost */
    },
    dataBag: {
        recordId: Nr,
        fieldId: fieldId(Nr, "helper_uiState"),
        base: helper_uiState,
        patch: { auswahlId: null }
    }
}
```

`null` en el patch elimina la key. Guardar también el nuevo ID de cita: `patch: { auswahlId: null, lastTerminId: "##newRecordId##" }`. Sintaxis: [`data-bag-actions`](../patterns/data-bag-actions.md).

📌 **Persistencia / React:** Create se ejecuta mediante `{ type: "create", tableId, changeFieldValues, setNewRecordId?, dataBag?, popup?, fullscreen? }`. React: procesar la misma acción mediante `onAction`.

### createNew.item – colocar una cita preconfigurada con un clic

Con **`createNew.item`** colocas una **cita ya preparada** en el calendario, en lugar de arrastrar un intervalo de tiempo. Caso típico: una selección en una barra lateral (tipo, duración, fases) escribe en el campo de fórmula — el calendario muestra un **ghost** que sigue al ratón. Un clic lo coloca.

Sin `item` se mantiene el arrastre habitual. Con `item` (y con `duration` o duraciones de segmento definidas) el arrastre queda desactivado — excepto si **`active`** es `false`.

El modo de colocación se activa **fuera** del calendario: un campo sí/no, una casilla en la barra lateral o la propia selección.

```javascript
createNew: {
    tableId: "B",
    dateFrom: fieldId(first(Termine), "Datum von"),
    timeFrom: fieldId(first(Termine), "Von"),
    dateTo: fieldId(first(Termine), "Datum bis"),
    timeTo: fieldId(first(Termine), "Bis"),
    changeFieldValues: if Auswahl then [{
        fieldId: fieldId(first(Termine), "Typ"),
        value: number(Auswahl)
    }] else [{
        fieldId: fieldId(first(Termine), "CacheTrigger"),
        value: now()
    }],
    item: if Auswahl then {
        active: Platzieren,
        title: Auswahl.Name,
        styles: {
            backgroundColor: color(Auswahl),
            color: "#fff",
            borderRadius: "8px"
        },
        segments: [{
            title: "Anfahrt",
            linked: false,
            offset: { days: -1 },
            timeFrom: time(16, 0),
            timeTo: time(17, 0)
        }, {
            title: "Kernarbeit",
            timeFrom: time(11, 0),
            timeTo: time(14, 0),
            splits: [{
                title: "Pause",
                timeFrom: time(12, 30),
                timeTo: time(13, 0)
            }]
        }, {
            title: "Abfahrt",
            linked: false,
            offset: { days: 1 },
            timeFrom: time(8, 0),
            timeTo: time(9, 0)
        }]
    }
}
```

**Campos en `item`:**

- **`active`** — opcional. `true` u omitido: ghost activado, el clic coloca. `false` / vacío: ghost desactivado, el arrastre vuelve a estar activo. Se vincula a un campo fuera del widget (p. ej. `Platzieren`).
- **`duration`** — longitud relativa. Las fases de solo duración se enganchan a la fase de seguimiento anterior (la primera: al ratón).
- **`timeFrom` / `timeTo`** — hora fija en el día de anclaje (día bajo el ratón). Fija **esta** fase. Con `linked: false` permanece como satélite — la cadena de seguimiento sigue siguiendo al ratón. Sin `linked: false` la cadena queda enganchada a esta hora.
- **`linked`** — igual que en los segmentos guardados. `false` = satélite (ida/vuelta). Por defecto `true`.
- **`offset`** — `{ days, time }`, ambos opcionales y con signo. `days: -1` día anterior, `days: 1` día siguiente. `time: -time(1, 0)` una hora antes (ratón / fase anterior). Combinable: `{ days: -1, time: -time(1, 0) }`. Con hora fija, `days` elige el día del calendario; `time` desplaza la hora junto con él. `days: 0` mantiene la fase en el día de anclaje si la fase anterior estaba en otro día. No confundir con el `offset: time(…)` plano en **`splits`**.
- **`dateFrom` / `dateTo`** — valores de fecha reales (como en `timeEntries`), cuando la fase está fija en un día concreto del calendario.
- **`splits`** — un nivel de interrupciones en esta fase (típicamente una pausa). Ambos trozos principales conservan el título de la fase. Ver más abajo.
- **`title` / `subtitle` / `value` / `customLayout` / `styles` / `insetBorder`** — como un `timeEntry`.

Resolución por fase, primera regla que aplica: fecha fija + hora → hora (`timeFrom`/`timeTo`, opcional `offset.days` / `offset.time`) → `offset` + `duration` → solo `duration`. `linked: false` + hora no actualiza el cursor de seguimiento.

**Tecla Mayús** en el ghost: mover toda la cadena, incluidos los satélites. Al soltar: los satélites vuelven a su hora fija. El clic guarda lo que se ve.

Núcleo bajo el ratón, satélite con hora fija:

```javascript
segments: [{
    title: "Anfahrt",
    linked: false,
    offset: { days: -1 },
    timeFrom: time(16, 0),
    timeTo: time(17, 0)
}, {
    title: "Vorarbeit",
    offset: { days: 0 },
    duration: time(0, 30)
}, {
    title: "Kern",
    duration: time(3, 0)
}, {
    title: "Abfahrt",
    offset: { days: 1 },
    duration: time(1, 0)
}]
```

Sin horas fijas (sigue al ratón):

```javascript
segments: [{
    title: "Anfahrt",
    offset: { days: -1, time: -time(1, 0) },
    duration: time(1, 0)
}, {
    title: "Kernarbeit",
    offset: { days: 0 },
    duration: time(3, 0),
    splits: [{
        title: "Pause",
        offset: time(1, 30),
        duration: time(0, 30)
    }]
}, {
    title: "Abfahrt",
    offset: { days: 1 },
    duration: time(1, 0)
}]
```

Si las partes deben tener **nombres distintos** (Parte 1 / Pausa / Parte 2): tres `segments` con `duration`, sin `splits`.

El ghost puede abarcar varios días. Por fase la hora avanza en tiempo real (`##entrytime##`).

El ghost ocupa un **carril propio** — las citas existentes en el mismo horario se desplazan hacia un lado, igual que con dos entradas reales. Al retirar el ratón, las demás vuelven a su ancho completo.

Ocultar: **ojo** en la toolbar o **Esc**. Volver a mostrar solo mediante el ojo. La selección de la barra lateral se mantiene.

Se guarda **un** nuevo registro en los mismos cuatro campos de `createNew`: desde la primera fase hasta el final de la última. `changeFieldValues` y `dataBag` igual que al arrastrar.

Selección vacía: omitir `item` (nunca `else ""`). Entonces se aplica de nuevo el arrastre.

## timeEntries – tus entradas de calendario en detalle

El bloque `timeEntries` define qué entradas de tiempo concretas se muestran en el calendario.

Aquí determinas cómo se representa cada entrada individual: desde el título hasta el color, e incluso layouts propios. Así puedes diseñar tus tarjetas de calendario de forma totalmente individual y adaptarlas a tu caso de uso. Con esto se crea automáticamente una nueva cita, incluyendo hora de inicio y duración.

📌 **Nota:**

Para mostrar el **cambio en tiempo real** de los datos de la cita (al crear o también al mover una cita), el marcador **"##entrytime##"** se puede usar en cualquier parte de `timeEntries` donde se deba mostrar la **hora de la cita**, es decir, en title, subtitle, value, pero también en todos los widgets que se usen aquí. Por ejemplo, cuando se usa un `customLayout` para el diseño del timeEntry o en botones, inputs, etc.

Por ejemplo dentro de un bloque de layout en el widget Text:


```javascript
...
value: arcCustomText({
                    alignY: "top",
                    fontSize: typo.paragraph3.fontSize,
                    fontWeight: "400",
                    fontColor: appointmentType.colorShade,
                    value: "##entrytime##"
                    }),
      ...
```


➡️ Todos los parámetros para las timeEntries los encuentras en la **siguiente sección**.

### title – título de la entrada del calendario

Con el parámetro `title` fijas qué se muestra en la primera línea de la entrada del calendario.

🔧 Dos opciones:

- Título propio:
Indica un campo o un texto que debe mostrarse como título en la entrada.
- Sin indicación (`"`):
Si dejas el campo vacío (`"`), se muestra en su lugar la hora de la cita.

<pre data-language="JavaScript"><code>title: Kalendertitel, // Ninox Feld
title: "", // Default: Gibt die Uhrzeit des Kalendereintrages an</code></pre>📌 Nota:

- Si no asignas un título y en su lugar se muestra la hora, esta se actualiza en vivo con los cambios, p. ej., al arrastrar o alargar una entrada.
- **Valor vacío:** Si `title` está vacío o solo contiene espacios, la línea del título se oculta – el espacio pasa a `subtitle` o `##entrytime##`.

### subtitle – descripción bajo el título

Con el parámetro `subtitle` puedes mostrar una línea adicional bajo el título en la entrada del calendario.

Normalmente aquí se coloca una breve descripción o una indicación complementaria, como el motivo de la cita o un estado.

<pre data-language="JavaScript"><code>subtitle: Description, // Ninox Feld
subtitle: "" // Textfeld</code></pre>→ Muestra, p. ej., el contenido del campo *„Description“* bajo el título en la entrada del calendario.

📌 **Nota:**

- El `subtitle` es opcional y **solo se muestra si existe un valor de campo correspondiente**.
- **Valor vacío:** Si `subtitle` está vacío o solo contiene espacios, la línea de subtitle se oculta – el espacio pasa al título. Título y subtitle se tratan de forma simétrica: si ambos están vacíos, solo se muestra `##entrytime##`.

### value – información adicional o campos interactivos

El parámetro `value` representa la tercera línea de tu entrada de calendario. Aquí puedes mostrar más información o incluso integrar elementos interactivos como campos de entrada o listas de selección.

🔧 Posibilidades de uso:

- Información adicional como, p. ej., lugar, estado, responsable
- Widgets interactivos directamente en la entrada del calendario – p. ej.:</p><ul><li data-preset-tag="p"><p>Un campo de selección para elegir al empleado responsable
- Un campo de entrada para notas breves o respuestas

</li></ul><pre data-language="JavaScript"><code>value: "",
value: Kommentar, </code></pre>→ Muestra el contenido del campo *„Kommentar“* en la tercera línea.

📌 **Nota:**

- `value` es **opcional**, pero amplía la representación de tus entradas con un nivel adicional.
- En combinación con widgets integrados surge una **experiencia de calendario interactiva directamente en la entrada**.

💡 **Consejo: **Usa `value` para mostrar información adicional importante de inmediato – o para que tus usuarios trabajen directamente en el calendario.

### customLayout – layout individual completo para entradas de calendario

Con el parámetro `**customLayout**` puedes definir un **layout completamente propio** para tus entradas de calendario.

Si usas este parámetro, los campos estándar `**title**`, `**subtitle**` y `**value**` **ya no se muestran**. En su lugar puedes diseñar libremente el aspecto de tu entrada de calendario, con los widgets que quieras.

<pre data-language="JavaScript">`customLayout: "",`</pre>💡 Consejo: Usa `customLayout` cuando necesites una representación completamente individual, p. ej., con colores propios, iconos, estructura o condiciones lógicas.

### dateFrom, dateTo, timeFrom, timeTo – hora de inicio y fin por entrada

Con estos cuatro parámetros indicas **qué campos de tu registro de entrada de tiempo** deben usarse para la **fecha y la hora**.


```javascript
dateFrom: 'Date From',
dateTo: 'Date To',
timeFrom: 'Time From',
timeTo: 'Time To',
```

![](https://framerusercontent.com/images/zRF7pUd7VZBvsfI8yuyCarMGYs.png)

📌 Nota:

- Deben indicarse campos de fecha y hora reales de Ninox.
- La combinación de fecha + hora determina la posición y la duración de la entrada del calendario.

⚠️ Importante saber:

- Si falta `dateTo`, se usa automáticamente el día de `dateFrom` (y viceversa).
- Si faltan ambos campos de fecha, la entrada no se muestra.
- Si falta `timeTo`, se usa automáticamente `timeFrom + 5 minutos`.
- Si falta `timeFrom`, se asume `timeTo - 5 minutos`.
→ Así es posible una visualización mínima de todos modos.

💡 Consejo: Si solo quieres mostrar entradas de día sin horas exactas, también puedes trabajar solo con campos de fecha – el calendario mostrará entonces la entrada como cita de todo el día.

### styles – estilo individual para entradas de calendario

Con el parámetro `**styles**` puedes adaptar el **aspecto de entradas de calendario individuales** – p. ej., colores, espaciado interno (padding) o esquinas redondeadas (border-radius).

<pre data-language="JavaScript"><code>styles: {
                    backgroundColor: color(Person),
                    color: "#fff",
                    borderRadius: "8px",
                    padding: ""
                },</code></pre>📌 **Propiedades admitidas:**

- `backgroundColor`: Color de fondo de la tarjeta
- `color`: Color de fuente
- `padding`: Espaciado interno (p. ej. `"6px"`)
- `borderRadius`: Redondeo de esquinas (p. ej. `"8px"`)

💡 Consejo: Con `styles` puedes **diferenciar visualmente** tus entradas entre sí, p. ej., por estado, tipo o prioridad – también de forma dinámica, si usas campos de tu fuente de datos como valor.

### dragAction – definir campos para mover entradas

Con el parámetro `**dragAction**` defines **qué campos de fecha y hora deben actualizarse al arrastrar o mover una entrada**.

Así se garantiza que la nueva hora quede **guardada en el registro** cuando se mueve una entrada en el calendario.

```javascript
dragAction: {
    recordId: Nr,
    dateFrom: fieldId(Nr, "Datum von"),
    timeFrom: fieldId(Nr, "Von"),
    dateTo: fieldId(Nr, "Datum bis"),
    timeTo: fieldId(Nr, "Bis"),
    changeFieldValues: [{
        fieldId: fieldId(Nr, "CacheTrigger"),
        value: now()
    }]
}
```

📌 Nota:

- Indica aquí los Field IDs correctos de cada campo en la tabla de entradas de tiempo.
- Aquí puedes usar la función de Ninox `fieldId()`.
- **changeFieldValues**: Opcional. Array de `{ fieldId, value }` – se escribe **después** de un arrastre exitoso (la fecha/hora ha cambiado). Uso típico: actualizar campos de caché o de trigger. Estructura igual que en `createNew.changeFieldValues` y como en la Timeline.

⚠️ Importante: `recordId` y los campos de fecha/hora son obligatorios si quieres que las entradas se puedan mover mediante arrastrar y soltar o cambiar de duración.

📌 **Persistencia / React:** Al guardar tras el arrastre, el widget envía acciones del Action Performer `{ type: "update", recordId, field, value }` (una acción por campo modificado). En Ninox, el adaptador lo mapea a `database.update`. En el adaptador React esto llega a `onAction` — sin `database` global.

### clickAction – abrir entradas con un clic en Ninox

Con el parámetro `**clickAction**` permites que el usuario, al hacer clic en una entrada de calendario, pueda abrir directamente el **registro correspondiente en Ninox**.

<pre data-language="JavaScript"><code>clickAction: {
                    type: "popup",
                    recordId: Nr
                }</code></pre>📌 Significado de los parámetros:

- `type: "popup"` – Abre el registro en una ventana popup (podrían seguir otros tipos).
- `recordId`: El campo en la entrada del calendario que contiene la clave primaria (Record ID) – normalmente `"Nr"`.

⚠️ Importante:

- El campo indicado en `recordId` debe contener el ID real del registro de Ninox.
- Si no se encuentra un valor válido, no ocurre nada al hacer clic.

### insetBorder – línea inset vertical en la entrada del calendario

Con `**insetBorder**` puedes mostrar una **línea vertical discreta** en el borde izquierdo (o derecho) de cada entrada del calendario – similar al estilo del calendario de Apple. La función está **activa por defecto** y solo hay que desactivarla si no la quieres.

```javascript
insetBorder: {
    enabled: true,       // Standard: true – false blendet die Linie aus
    position: "left",    // "left" (Standard) oder "right"
    width: "2px",        // Breite der Linie
    stroke: "dotted",    // "solid", "dashed" oder "dotted"
    color: "#ffffff"     // Farbe (Standard: Schriftfarbe des Eintrags)
}
```

📌 **Nota:**

- La línea reacciona automáticamente al `borderRadius` de la entrada y se acorta a medida que aumenta el radio.
- En segmentos conectados (cadena) se continúa sin cortes en las transiciones.
- Puedes definir `insetBorder` tanto a nivel del `timeEntry` como por segmento – el nivel de segmento sobrescribe el nivel de entrada.

### segments – dividir citas en fases

Con el parámetro `**segments**` puedes dividir una cita en varias **fases de tiempo relacionadas entre sí** – p. ej., desplazamiento de ida, trabajo previo, trabajo principal, pausa, trabajo posterior y desplazamiento de vuelta.

Cada segmento es una entrada independiente con su propio horario y diseño, pero puede **vincularse** con otros segmentos para que se muevan juntos al desplazarlos.

```javascript
timeEntries: (select Termine).[{
    title: Bezeichnung,
    dateFrom: 'Datum von',
    dateTo: 'Datum bis',
    timeFrom: Von,
    timeTo: Bis,
    styles: {
        backgroundColor: "#5b6af0",
        color: "#fff",
        borderRadius: "8px"
    },
    dragAction: {
        recordId: Nr,
        dateFrom: fieldId(Nr, "Datum von"),
        timeFrom: fieldId(Nr, "Von"),
        dateTo: fieldId(Nr, "Datum bis"),
        timeTo: fieldId(Nr, "Bis")
    },
    segmentSettings: {
        dayJump: {
            threshold: time(22, 0),
            startTime: time(7, 0)
        }
    },
    segments: [{
            title: "Anfahrt",
            dateFrom: 'Seg Datum von',
            dateTo: 'Seg Datum bis',
            timeFrom: SegVon,
            timeTo: SegBis,
            styles: {
                backgroundColor: "#3b4fd0",
                color: "#fff"
            },
            linked: false,
            segmentType: "travel",
            dragAction: {
                recordId: Nr,
                dateFrom: fieldId(Nr, "Seg Datum von"),
                timeFrom: fieldId(Nr, "SegVon"),
                dateTo: fieldId(Nr, "Seg Datum bis"),
                timeTo: fieldId(Nr, "SegBis")
            }
        }, {
            title: "Kernarbeit",
            dateFrom: 'Kern Datum von',
            dateTo: 'Kern Datum bis',
            timeFrom: KernVon,
            timeTo: KernBis,
            styles: {
                backgroundColor: "#5b6af0",
                color: "#fff"
            },
            linked: true
        }]
}]
```

#### Campos por segmento

| Parameter | Typ | Beschreibung |
|---|---|---|
| `title` | Text | Titel des Segments |
| `subtitle` | Text | Untertitel (optional) |
| `value` | Text/Widget | Zusatzzeile oder eingebettetes Widget |
| `dateFrom`, `dateTo` | Datum | Start- und Enddatum |
| `timeFrom`, `timeTo` | Zeit | Start- und Endzeit |
| `styles` | Objekt | Wie beim `timeEntry` – überschreibt Parent-Styles |
| `linked` | Boolean | `true` = Segment wird mit anderen `linked`-Segmenten gemeinsam verschoben (Standard: `true`) |
| `segmentType` | Text | Frei wählbarer Typ-Bezeichner (z. B. `"travel"`, `"core"`) |
| `insetBorder` | Objekt | Inset-Linie für dieses Segment (überschreibt Entry-Level) |
| `dragAction` | Objekt | Felder für Drag & Drop – Aufbau identisch mit `timeEntry.dragAction` |
| `segmentSettings` | Objekt | Segment-Einstellungen für dieses einzelne Segment (z. B. `dayJump`) |
| `splits` | Array | Eine Ebene Unterbrechungen in dieser Phase (z. B. Pause). Das Widget macht daraus: Teil 1 → Pause → Teil 2. |

💡 **Consejo:** Los campos que no se definan en el segmento heredan el valor del `timeEntry` superior. Así basta con definir solo las diferencias por segmento.

#### splits – pausa dentro de una fase

`splits` divide **una** fase (normalmente el trabajo principal), sin que tengas que crear en Ninox „Kern Teil 1 / Kern Teil 2“. Sin más anidación — solo este único nivel.

```javascript
{
    title: "Kernarbeit",
    dateFrom: 'Datum von',
    dateTo: 'Datum bis',
    timeFrom: time(11, 0),
    timeTo: time(14, 0),
    splits: [{
        title: "Pause",
        timeFrom: time(12, 30),
        timeTo: time(13, 0),
        styles: {
            backgroundColor: "#94a3b8"
        }
    }]
}
```

O relativo al inicio de la fase: `offset: time(1, 30)`, `duration: time(0, 30)`.

Se permiten varios splits; se ordenan por hora de inicio y se limitan al rango de tiempo de la fase. Los trozos principales conservan el look y el título de la fase, la pausa el suyo propio.

Títulos distintos para los trozos principales (Parte 1 / Parte 2): **tres `segments`**, sin `splits` — p. ej., `duration: time(1, 30)`, pausa, `duration: time(1, 30)`.

#### linked – vincular o desacoplar segmentos

Con `linked: true` (por defecto) un segmento pasa a formar parte de la **cadena conectada**. Al mover en el calendario, todos los segmentos vinculados se mueven juntos.

Con `linked: false` un segmento queda **desacoplado** – p. ej., ida y vuelta, que pueden estar en otro día y deben moverse de forma independiente. En el **placement-ghost** (`createNew.item`) rige lo mismo: una hora fija en un satélite `linked: false` no fija el núcleo.

#### Toolbar

Abajo a la derecha hay una **pequeña toolbar**. Es **siempre visible**, salvo que `segmentSettings.toolbar` sea `false`.

**Candado** — mover segmentos en conjunto o por separado:

- **Bloqueado** (candado cerrado): Todos los segmentos del `timeEntry` se mueven juntos
- **Individual** (candado abierto): Los segmentos `linked` forman un grupo, los segmentos `linked: false` se pueden mover de forma independiente

Sin segmentos, el candado está inactivo. Al pasar el ratón por encima aparece una breve indicación.

**Ojo** — solo con `createNew.item`: mostrar y ocultar el ghost. **Esc** lo oculta. Volver a mostrarlo solo mediante el ojo. Al ocultarlo, se aplica de nuevo el arrastre, sin cambiar la selección en la barra lateral.

💡 **Tecla Mayús:** Durante un arrastre o en el **placement-ghost** mantén pulsada **Mayús** para invertir el lock: los satélites guardados o los satélites del ghost se mueven junto con el conjunto. Al soltar, los satélites del ghost vuelven a su hora fija. El icono del candado muestra el estado en tiempo real.

💡 **Mayús + redimensionar:** Si mantienes pulsada la tecla Mayús y arrastras desde el borde de un segmento (arriba/abajo), todos los segmentos adyacentes se desplazan junto con él – el **bloque completo se hace más grande**, en lugar de quitar tiempo al segmento siguiente.

## segmentSettings – ajustes globales para segmentos

El parámetro opcional `**segmentSettings**` a nivel de todo el widget controla el **comportamiento por defecto de todos los segmentos** – p. ej., si los grupos están bloqueados por defecto o si se muestra la toolbar.

```javascript
arcCustomCalendarWeek({
    // ...
    segmentSettings: {
        lockGroups: true,
        edgeMagnetism: true,
        toolbar: true,
        dayJump: {
            threshold: time(22, 0),
            startTime: time(7, 0)
        }
    },
    timeEntries: [...]
})
```

📌 **Vista general de los parámetros:**

| Parameter | Typ | Beschreibung |
|---|---|---|
| `lockGroups` | Boolean | `true` = Segmente werden standardmäßig gemeinsam verschoben (Standard: `true`) |
| `edgeMagnetism` | Boolean | `true` = Beim Resize bleibt die gemeinsame Grenze angrenzender Segmente synchron (Standard: `true`) |
| `toolbar` | Boolean | `false` = Toolbar ausblenden. Sonst immer sichtbar (Schloss + Auge bei `createNew.item`) |
| `dayJump.threshold` | Zeit | Ab dieser Uhrzeit springt ein Segment beim Drag auf den nächsten Tag |
| `dayJump.startTime` | Zeit | Startzeit am neuen Tag nach dem Sprung |

⚠️ **Prioridad:** Los ajustes a nivel de `timeEntry` (`segmentSettings` dentro de una entrada) sobrescriben los `segmentSettings` globales. Los ajustes a nivel de segmento sobrescriben a su vez el nivel de entrada.

---

Con Custom Calendar Week llevas planificación estructurada y total flexibilidad a tu base de datos Ninox – adaptable de forma individual, intuitivo de usar y listo para tu día a día.

Happy Widgeting 🥳
