---
title: "Custom Drawing"
slug: "custom-drawing"
category: "Widgets"
reactComponent: "ArcWidgetDrawing"
reactTier: "premium"
reactSince: "0.1.0-alpha.5"
reactExample: "drawing.tsx"
locale: "es"
hosts: "vue"
---

# Custom Drawing

## AI Defaults (read first)

- **uniqueId**: Required, `"drawing-" + Nr`.
- **tableId**: Required – the shapes table (`tableId("Shapes")`).
- **fieldId**: The text field storing shape data (JSON), use `fieldId(Nr, "Feldname")`.
- **height**: Typically `"800px"` or `"100%"`.
- **canvas/artboard**: `canvas: { width: 4000, height: 4000 }`, `artboard: { width: 1000, height: 1000 }`.
- **shapes**: Each shape needs `shapeId: Nr` and `shapeDataValue` (from the shapes table text field).
- **embedded**: Default `false`, set `true` when nested.
- **Persistence**: Core emits `shape_create` / `shape_update` / `shape_activate` / `export`. Ninox handlers build Action Performer `create`/`update` and run shape `actions`. React: `onShapeCreate`, `onShapeUpdate`, `onShapeActivate`, `onExport` (or `onAction`).

# Custom Drawing

<iframe src="https://www.youtube.com/embed/sOn_9zeffCI?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>El **widget Custom Drawing** permite subir cualquier imagen o PDF (p. ej. planos, fotos de producto, siluetas corporales) y añadirles **pines, stickers o dibujos a mano libre** interactivos. Cada marca se puede vincular con una **nota**, **categoría** o **información adicional**. Ideal para aplicaciones en construcción, moda, medicina, logística y mucho más.

## Código de aplicación


```javascript
arcCustomDrawing({
            uniqueId: "titel " + Nr,
            embedded: false,
            height: "800px",
            tableId: tableId("Shapes"),
            fieldId: fieldId("Shapes", "helper_shapeDataValue"),
            changeFieldValues: [{
                    fieldId: fieldId("Tabelle", "Feld Name"),
                    value: "Wert"
                }],
            exportSettings: {
                allowedTypes: [{
                        format: "jpg"
                    }, {
                        format: "png"
                    }, {
                        format: "svg"
                    }, {
                        format: "pdf",
                        mergeFile: false
                    }],
                target: "file",
                recordId: Nr,
                fieldId: fieldId("Tabelle", "Feld für den Export"),
                mergeFile: "Feld mit der Originaldatei",
                apiKey: "API_KEY",
            },
            canvas: {
                width: 4000,
                height: 4000,
                offsetX: 0,
                offsetY: 0
            },
            zooming: {
                step: 0.25,
                min: 0.01,
                max: 4
            },
            drawingSettings: {
                strokeWidth: 1,
                strokeColor: "#ffffff"
            },
            artboard: {
                width: 1000,
                height: 1000,
            },
            stickerTypes:[{
                uid: uid,
                image: "stickerBase64",
                icon: base64,
                label: Name,
                width: 100,
                height: 100,
                originX: 0.5,
                originY: 0.89,
                default: false
            }],
            shapes: (Shapes).[{
                    shapeId: Nr,
                    movable: true,
                    shapeDataValue: helper_shapeDataValue,
                    stickerUID: "",
                    sidebarItem: {
                        header: "Titel des Eintrags",
                        content: "Beschreibung des Eintrags",
                    }
                }],
            rightSideContent: {
                show: true,
                header: "Header",
                footer: "Footer"
            }
        })
```

### 🔸 `embedded`

Indica si el widget se integra en una interfaz existente (p. ej. un formulario de Ninox).


```javascript
embedded: "" // default: false
embedded: true,
```

👀 **Consejo:** Con `true` no es necesaria una altura fija; el widget se adapta automáticamente al contenedor padre.

### 🔸 `height`

Con el parámetro `height` indicas la **altura total** de la herramienta de dibujo en píxeles.
Determina cuánto espacio vertical ocupa el widget en el contexto embebido (p. ej. en un formulario o en una página).

**Recomendación: **Para una mejor UI, es preferible integrar el widget en Custom Layout. Y que la altura la determine el bloque de layout.

### 🔸 `tableId` y `fieldId`

Estos dos parámetros definen **dónde se guardan o cargan los datos de forma (Shapes)**.

- `**tableId**` indica la tabla donde se gestionan las shapes.
- `**fieldId**` apunta al campo concreto dentro de esa tabla que contiene los datos de geometría (`shapeDataValue`) de cada shape. Atención: aquí debe usarse un campo de texto de Ninox.

Con esto puedes guardar dinámicamente tus propias formas, marcadores u objetos — p. ej., rectángulos, stickers, círculos, imágenes o líneas.

### 🔸 `dataBag`

Opcional. Controla cómo se leen y escriben los datos de shape en un **bote JSON** (un campo de texto con varias keys) — sin sobrescribir otras keys.

```javascript
dataBag: {
  key: "pin"              // Shape liest/schreibt nur diesen Key im Topf
  // encoding: "raw"     // Default — Plain-JSON; nur bei Legacy: "urlEncoded"
}
```

**Lectura:** El campo `shapeDataValue` de cada shape contiene todo el bote como JSON plano. El widget extrae `key` como geometría. En fórmulas: `parseJSON(helper_data)` — sin `urlDecode`.

**Escritura:** Al mover, solo se actualiza `key`; otras keys (`placedAt`, `meta`, …) se conservan.

**Ejemplo: Plan de mantenimiento con puntos de inspección** (p. ej. inspección de edificios):

```json
{
  "geo": { "type": "sticker", "stickerUID": "pin_ok", "x": 420, "y": 180, "width": 40, "height": 40 },
  "placedAt": "2026-06-13",
  "inspectionPointId": "123"
}
```

Sin `dataBag` se mantiene el comportamiento anterior: todo el campo es `shapeDataValue` (JSON raw).

> **Nota:** Esta es la **config del widget** (`key` + `encoding`). Para **acciones** (`update` con `patch` en botes de estado) rige el mismo concepto de `dataBag` en toda la plataforma — consulta [`data-bag-actions`](../patterns/data-bag-actions.md).

### 🔸 `changeFieldValues`

Permite rellenar campos o vínculos adicionales **al crear una nueva shape** — p. ej., categoría, creador, etc.

Por entrada: `{ fieldId, value }` — `fieldId` es el Field ID de Ninox (p. ej. mediante `fieldId("Tabelle", "Feldname")`).

**Específico de stickers:** Además, cada elemento en `stickerTypes` puede tener su propio array `changeFieldValues`. Al colocar un sticker, primero se aplican los `changeFieldValues` **globales** del Drawing, y después los del **tipo de sticker activo** — con el mismo `fieldId`, gana el sticker.

```javascript
changeFieldValues: [{
  fieldId: fieldId("Pruefpunkte", "Objekt zuordnen"),
  value: number(obj.Nr)
}],
stickerTypes: [{
  uid: "pin_ok",
  changeFieldValues: [{
    fieldId: fieldId("Pruefpunkte", "Kategorie"),
    value: "OK"
  }]
}]
```

**Números correlativos:** Valores como números de posición o IDs correlativos **no** deben establecerse mediante `changeFieldValues` — el valor procede del último render y es incorrecto con varios creates rápidos. En su lugar, usa un **trigger after-change** en el campo de enlace del parent; en el trigger, **`myNr := number(Nr)` antes** del filtro `number(Nr) != myNr`, de lo contrario te filtras a ti mismo.

### 🔸 `create`

Controla la **creación de nuevas shapes** (pines, líneas, rectángulos, círculos, imágenes) y agrupa opcionalmente la configuración de create. Recomendado a partir de esta versión; las props raíz `tableId`, `fieldId`, `changeFieldValues` y `setNewRecordId` **siguen siendo válidas** (legacy).

```javascript
create: {
  enabled: if lockCreate then false else true end,
  tableId: tableId("Shapes"),
  fieldId: fieldId("Shapes", "helper_shapeDataValue"),
  changeFieldValues: [{
    fieldId: fieldId("Pruefpunkte", "Objekt"),
    value: number(obj.Nr)
  }],
  setNewRecordId: [{
    recordId: Nr,
    fieldId: fieldId("Objekt", "Letzter Shape")
  }]
}
```

| Feld | Beschreibung |
|------|--------------|
| `enabled` | `false` = kein neues Shape (Toolbar ausgegraut, Pan/Klick/`actions` bleiben). Default: `true`. |
| `tableId` | Zieltabelle für neue Shapes (überschreibt Root-`tableId`, falls gesetzt). |
| `fieldId` | Textfeld für Geometrie am **neuen** Datensatz (überschreibt Root-`fieldId`). |
| `changeFieldValues` | Zusatzfelder beim Create (überschreibt Root-`changeFieldValues`). |
| `setNewRecordId` | Nach Create: neuen Datensatz in Parent verlinken (`[{ recordId, fieldId }]`). |

**Notas:**
- `dataBag` se mantiene en la raíz del widget (lectura + escritura de shapes existentes).
- `stickerTypes[].changeFieldValues` sigue siendo específico del sticker y complementa los valores globales.
- Solo `create.enabled: false` basta para bloquear — `tableId`/`fieldId` en la raíz pueden seguir configurados para las shapes existentes.

### 🔸 `setNewRecordId` (legacy en la raíz)

Opcional en la raíz del widget o dentro de `create`. Tras crear una shape, el nuevo registro se escribe en un campo de referencia — p. ej., para vincular el registro parent.

```javascript
setNewRecordId: [{
  recordId: Nr,
  fieldId: fieldId("Objekt", "Letzter Shape")
}]
```

### 🔸 `exportSettings`

Con este bloque configuras la **función de exportación del widget**. Puedes definir en qué formatos puede exportar el usuario (p. ej. PNG, PDF), dónde se guarda el archivo y si en PDF se combina con un documento existente.

💡 **Nota**: La conversión a un PDF vectorizado genera costes y requiere conexión a internet (no funciona en modo sin conexión). Consulta los costes exactos al soporte de arcRider.

<figure><table><tbody><tr><th>Feld

</th><th>Beschreibung

</th></tr><tr><td>`allowedTypes`

</td><td>Liste der erlaubten Exportformate. Du kannst `"jpg"`, `"png"`, `"svg"` und `"pdf"` angeben.
Beim Format `"pdf"` kannst du zusätzlich `mergeFile: false` oder ein Feldname setzen, um eine bestehende PDF zu ergänzen.

</td></tr><tr><td>`target`

</td><td>Legt fest, wohin die Datei exportiert werden soll.
`"file"` bedeutet, die Datei wird heruntergeladen.
`"field"` bedeutet, die Datei wird in ein Datei-Feld in Ninox hochgeladen.

</td></tr><tr><td>`recordId`

</td><td>Gibt den Datensatz an, in den exportiert werden soll (nur bei `target: "field"` erforderlich).

</td></tr><tr><td>`fieldId`

</td><td>Definiert das genaue Feld im Datensatz, in das die exportierte Datei geschrieben wird.

</td></tr><tr><td>`mergeFile`

</td><td>Nur für PDF-Exporte relevant.
Wenn angegeben, wird die exportierte Zeichnung mit der PDF aus diesem Feld zusammengeführt.

</td></tr><tr><td>`apiKey`

</td><td>Wird nur für den PDF-Export mit Zusammenführung benötigt.
Hier wird ein API-Schlüssel von Arc Rider verwendet, um das PDF-Rendering serverseitig durchzuführen.

</td></tr></tbody></table></figure>

#### 🔄 Auto-Export

Con `autoExport` puedes definir que se realice automáticamente una exportación tras cada cambio de shape — p. ej., para guardar una imagen de vista previa en vivo.

Las propiedades `autoExport`, `autoExportDelay`, `target`, `recordId` y `fieldId` se pueden establecer tanto globalmente como **por formato** en `allowedTypes`. Los ajustes específicos de formato sobrescriben los globales.

**Ejemplo con Auto-Export:**

```javascript
exportSettings: {
    allowedTypes: [{
        format: "jpg",
        target: "field",
        recordId: Nr,
        fieldId: fieldId("Tabelle", "Vorschaubild"),
        autoExport: true,
        autoExportDelay: 500  // Verzögerung in ms (default: 500)
    }, {
        format: "pdf",
        target: "file",       // Manueller Download
        autoExport: false
    }]
}
```

| Feld | Beschreibung |
|------|--------------|
| `autoExport` | `true` oder `false` – aktiviert automatischen Export nach jeder Shape-Änderung |
| `autoExportDelay` | Verzögerung in Millisekunden bevor der Export startet (default: 500). Dient als Debounce bei schnellen Änderungen. |

### 🔸 `canvas`

Con el bloque `canvas` defines el **área técnica de dibujo**, es decir, el área completa en la que se pueden dibujar y mover shapes.

Esta área puede ser considerablemente más grande que el recorte visible (`artboard`) y determina hasta dónde puede moverse el usuario en el área de dibujo (p. ej. al desplazar o hacer zoom).

#### 🔧 Estructura:

- `width`: El ancho total del área de dibujo en píxeles.
- `height`: La altura total del área de dibujo en píxeles.
- `offsetX`: Distancia al borde izquierdo y derecho — se puede usar para centrar contenidos.
- `offsetY`: Distancia al borde superior e inferior — ideal para espacio de trabajo adicional o márgenes de impresión.

Consejo: También puedes usar aquí valores dinámicos de campos de Ninox o cálculos. Por ejemplo, hemos desarrollado flujos de trabajo en los que hicimos que el tamaño del canvas dependiera del tamaño de la shape de fondo.

### 🔸 `zooming`

El bloque `zooming` controla **hasta dónde se puede hacer zoom dentro o fuera del área de dibujo** — p. ej., con la rueda del ratón o gestos táctiles. Con esto defines el rango de zoom y con qué finura se gradúa.

#### 🔧 Estructura:

- `step`: Indica el tamaño de un paso de zoom, p. ej. al hacer clic en los botones más/menos o al desplazarse con la tecla Ctrl pulsada. Un valor pequeño (p. ej. `0.1`) significa pasos de zoom más finos.
- `min`: El **factor de zoom mínimo permitido** (p. ej. `0.1` = 10 %). Con esto fijas hasta qué punto se puede alejar el zoom como máximo.
- `max`: El **factor de zoom máximo permitido** (p. ej. `4` = 400 %). Con esto limitas el acercamiento a un determinado nivel de detalle.

💡 **Notas:**

- El zoom inicial se ajusta automáticamente para que el artboard encaje bien en el widget.
- Las acciones de zoom afectan a la representación de las shapes, no a su tamaño real en el `canvas`.
- Si no se establece un bloque `zooming`, se aplican valores por defecto (zoom entre aprox. 10 % y 400 %).

### 🔸 `drawingSettings`

Con el bloque `drawingSettings` fijas las **propiedades por defecto para los nuevos objetos de dibujo** — p. ej., color y grosor de línea. Estos valores se aplican a todas las formas recién dibujadas, como líneas, rectángulos o círculos, mientras el usuario no cambie ajustes individuales.

#### 🔧 Estructura:

- `strokeWidth`: Determina el grosor de línea en píxeles.
Un valor de `1` da una línea fina, `2` o `3` se ven algo más gruesos.
- `strokeColor`: Define el color de las líneas en formato hexadecimal, p. ej. `#ffffff` para blanco o `#ffaa21` para naranja.

💡 **Nota:**

También puedes pasar `strokeColor` y `strokeWidth` **de forma dinámica desde campos de Ninox**, para p. ej. hacer que las opciones de dibujo sean **seleccionables por el usuario**. Así puedes permitir estilos de dibujo individuales por registro.

### 🔸 `toolbarSettings`

Opcional. Controla **orden, visibilidad y agrupación** de los botones de la toolbar. Sin `toolbarSettings` se mantiene el layout estándar (Move, herramientas de dibujo, submenú de stickers, exportación).

#### Estructura

- `top`: Botones en la barra superior de la toolbar (desde arriba en desktop / a la izquierda en móvil).
- `bottom`: Botones en la barra inferior (exportación etc.).

#### Entradas en `top` / `bottom`

| Form | Bedeutung |
|------|-----------|
| `"move"` | Tool mit Standard-Icon (Kurzform) |
| `{ tool: "draw", icon: "..." }` | Tool mit eigenem Icon (URL, Base64 oder SVG) |
| `"stickers"` | Alle `stickerTypes` **direkt** in der Toolbar (Ebene 1) |
| `{ sticker: "pin_rot" }` | Nur dieser Sticker direkt in der Toolbar |
| `{ group: "zeichnen", icon: "...", items: ["draw", "rect", "circle"] }` | Gruppe mit Unterpanel (wie bisher bei Stickers/Export) |

**Omitirlo = no mostrarlo.** No hay un flag `enabled` separado — lo que no está en el layout no aparece.

#### Ejemplo: pines de punto de inspección, solo Move + stickers seleccionados

```javascript
toolbarSettings: {
  top: [
    "move",
    { sticker: "pin_rot" },
    { sticker: "pin_gruen" }
  ],
  bottom: ["export"]
}
```

#### Ejemplo: agrupar herramientas de dibujo

```javascript
toolbarSettings: {
  top: [
    "move",
    {
      group: "zeichnen",
      label: "Zeichnen",
      icon: "data:image/svg+xml,...",
      items: ["draw", "rect", "circle"]
    },
    "image",
    "stickers"
  ],
  bottom: ["export"]
}
```

### 🔸 `artboard`

El bloque `artboard` define el **recorte visible de tu área de dibujo** — es decir, lo que el usuario ve primero al abrir el widget. Es, por así decirlo, el «papel» en el que se dibuja — mientras que el área `canvas` representa el «taller completo».

#### 🔧 Estructura:

- `width`: Ancho del área de dibujo visible en píxeles.
- `height`: Altura del área de dibujo visible en píxeles.

Esta área se centra y escala automáticamente para que sea completamente visible en el widget (dependiendo de los parámetros de zoom y visualización).

💡 **Notas:**

- El `artboard` es puramente visual — todas las shapes pueden estar también **fuera de esta área**, siempre que estén dentro del `canvas`.
- Si quieres una determinada proporción (p. ej. A4 o cuadrado), puedes ajustar `width` y `height` en consecuencia.
- En combinación con `zooming` y `canvas` puedes controlar de forma específica cuánto espacio tienen los usuarios para su dibujo — y qué se ve de inmediato al iniciar.

Consejo: También puedes usar aquí valores dinámicos de campos de Ninox o cálculos. Por ejemplo, hemos desarrollado flujos de trabajo en los que hicimos que el tamaño del artboard dependiera del tamaño de la shape de fondo. Así el usuario no tiene que desplazarse tanto de un lado a otro.

### 🔸 `stickerTypes`

El bloque `stickerTypes` te permite ofrecer **stickers individuales** que los usuarios pueden colocar en el área de dibujo — p. ej., iconos, símbolos, marcadores o pictogramas.

Cada sticker es un objeto con propiedades como fuente de imagen, tamaño y comportamiento de posicionamiento.
Ideal para casos de uso como:

- 🔧 Marcas técnicas en fotos o planos
- 📍 Pines de ubicación en mapas o planos
- 👕 Controles de calidad en el sector de la moda
- 🏥 Anotaciones médicas en siluetas corporales

#### 🔧 Estructura por sticker:

- `uid`: ID único del sticker (p. ej. `"pin_rot"`).
- `image`: Data URL en base64 o URL a la imagen — se usa al colocar en el canvas.
- `icon` (opcional): Imagen de vista previa **solo** para el botón de la toolbar (si difiere de la imagen del sticker). Se permite:
  - Imagen (markup SVG, PNG/JPG/data URL, URL HTTP) — como `image`
  - Objeto widget `arcCustomIcon`: `{ widget: "arcCustomIcon", data: { iconSet: "phosphor", name: "map-pin", color: "#1565c0" } }`
- `label`: Denominación del sticker. Dos formas posibles:
  - **String** (legacy): solo tooltip al pasar el ratón sobre el botón de la toolbar, sin texto visible.
  - **Objeto** (nuevo): texto visible bajo el icono en el botón de la toolbar — `{ value: "K", visible: true, fontColor: "#1565c0", fontSize: 10, maxWidth: 50 }`. `visible: false` se comporta como el caso string (solo tooltip). El texto demasiado largo se recorta con `…` en `maxWidth`.
- `width`: Ancho del sticker en píxeles.
- `height`: Altura del sticker en píxeles.
- `originX`: Punto de anclaje horizontal (0 = izquierda, 0.5 = centro, 1 = derecha).
- `originY`: Punto de anclaje vertical (0 = arriba, 0.5 = centro, 1 = abajo).
- `default` (opcional): Si es `true`, este sticker queda preseleccionado.
- `labelPlaceholder` (opcional): String marcador en el SVG (`image`) que se sustituye por `label` en la shape — convención: `##LABEL##` (como en otros arcWidgets: `##key##`).
- `defaultLabel` (opcional): Fallback para `label` justo al colocar, hasta que Ninox entregue la fórmula de nuevo.
- `defaultPlaceholders` (opcional): Objeto fallback para `shape.placeholders` al colocar (mismas keys que en el SVG).
- `changeFieldValues` (opcional): Campos adicionales al **crear** un registro para este tipo de sticker — se combinan con los `changeFieldValues` globales (el sticker sobrescribe con el mismo `fieldId`).

#### Marcadores SVG dinámicos (sticker + shape)

Para marcadores con texto, punto de estado e iconos, la **plantilla SVG** puede contener marcadores. El widget los sustituye al renderizar y en la exportación:

| Quelle | Ersetzung |
|--------|-----------|
| `stickerType.labelPlaceholder` + `shape.label` | z. B. `##LABEL##` → `"K15"` |
| `shape.placeholders` (Objekt) | Jeder Key `foo` ersetzt `##foo##` im SVG |
| `stickerType.defaultLabel` / `defaultPlaceholders` | Sofort beim Platzieren, bis Ninox reagiert |

**Nota de data URL:** Si el SVG está incrustado mediante `encodeURIComponent` en `data:image/svg+xml,…`, el widget también sustituye los marcadores en forma codificada por URL (p. ej. `%23%23LABEL%23%23` → `K15`, `%23ff0000` para `#ff0000`).

**¿Por qué `##key##`?** Igual que en arcCustomInput (`##inputValue##`) y arcCustomCalendarWeek (`##clickedDate##`). A diferencia de `{{key}}`, `##key##` no colisiona con la sintaxis de Ninox `{variable}` en bloques de texto.

Ejemplo de `shape.placeholders` para iconos de estado mediante `display` (todas las variantes en el SVG, Ninox controla la visibilidad):

```javascript
placeholders: {
  statusColor: "#4caf50",
  showCheck: "block",
  showWarn: "none",
  showError: "none"
}
```

Ejemplo de `defaultPlaceholders` en el `stickerType` (fallback hasta que Ninox reaccione):

```javascript
defaultLabel: "",
defaultPlaceholders: {
  statusColor: "#9e9e9e",
  showCheck: "none",
  showWarn: "none",
  showError: "none"
}
```

Los nuevos marcadores **no requieren cambios en el widget** — solo `##key##` en el SVG y la key correspondiente en `shape.placeholders`.

💡 **Notas:**

- Los stickers aparecen en el menú izquierdo del widget bajo «Stickers».
- Al colocar un sticker, su posición se calcula automáticamente según `originX` / `originY`. Así puedes, p. ej., alinear marcadores exactamente con su punta en el punto de clic.
- Puedes definir tantos stickers como quieras — p. ej., pines de colores, iconos para defectos, emojis, símbolos de estado, etc.
- Los gráficos de sticker deberían prepararse idealmente como **SVG** o como **PNG con fondo transparente**.

**Consejo**: Crea una tabla de Ninox con stickers. Carga tu archivo SVG o PNG en un campo de imagen. Usa la función de Ninox `loadFileAsBase64URL(Image)` en un campo de fórmula para obtener el base64 para tu lista de stickers en el Drawing. Por supuesto, en tu lista de stickers también puedes definir directamente ancho, alto, nombre, etc.

### 🔸 `shapes`

El bloque `shapes` define la lista de **objetos de dibujo** que registras en el widget. Cada objeto del array corresponde a una entrada guardada en tu `tableId` y contiene la información necesaria para la representación, posición e interacción.

Importante: para poder usar Drawing correctamente, es esencial configurar una tabla Shapes y crear en ella al menos un campo de texto `helper_shapeDataValue`.

#### 🔧 Estructura por shape:

- `shapeId`: El ID único de la shape (normalmente el `Nr` del registro).
- `movable`: Indica si el objeto se puede mover en el área de dibujo (`true` o `false`). `false` solo bloquea el movimiento — clic, hover y `actions` siguen funcionando. En **modo sticker**: las shapes con `actions` capturan los clics (p. ej. detalle en la sidebar); las shapes de fondo puras sin `actions` permiten que pase la colocación de pines.
- `shapeDataValue`: Los datos de forma guardados (p. ej. coordenadas, tamaño, color, tipo, etc.). Con `dataBag.key` aquí puede estar todo el bote JSON; el widget usa entonces solo el valor de la key como geometría.
- `label` (opcional): Texto dinámico para stickers con `labelPlaceholder` en el `stickerType`.
- `placeholders` (opcional): Objeto con sustituciones SVG adicionales — cada key sustituye `##key##` en el SVG del sticker (p. ej. `statusColor`, `showCheck`).
- `scalable` (opcional): En stickers, por defecto `false` — configúralo para marcadores estables a nivel de píxel en planos.
- `actions`: Opcional – lista de acciones que se ejecutan al **hacer clic** (no al arrastrar). Mismo formato que en otros arcWidgets (`update`, `popup`, `openRecord`, …). Admite `value` plano o `dataBag`/`patch` para actualizaciones parciales en un bote de estado:

```javascript
actions: [{
  type: "update",
  recordId: stateRecordId,
  fieldId: stateField,
  dataBag: {
    base: helper_mainContentState,
    patch: { selectedShape: Nr }
  }
}]
```

Caso de uso típico: escribir el ID de la shape en un estado de UI compartido → la sidebar (`rightSideContent`) muestra el detalle. En detalle: [`data-bag-actions`](../patterns/data-bag-actions.md).
- `stickerUID`: Si quieres mostrar un sticker distinto del creado inicialmente (p. ej. porque ha cambiado un estado), aquí debes indicar el UID del sticker correspondiente.
- `sidebarItem`: Opcional – muestra la shape además en la barra lateral derecha, con:</p><ul><li data-preset-tag="p"><p>`header`: Título (p. ej. «Defecto #3»)
- `content`: Descripción o más información
- `hide`: `true` o `false` – oculta el elemento en la sidebar sin eliminarlo (p. ej. para vistas filtradas)

</li></ul>

### Receta: plan de mantenimiento con puntos de inspección

Configuración típica para inspección de edificios, mantenimiento de instalaciones o marcadores similares basados en plano — un **fondo de plano no movible** más **pines de punto de inspección movibles** con persistencia en una tabla hija.

#### Componentes

1. **Fondo de plano** — una shape con `type: "image"`, tamaño completo del artboard, `movable: false`, `hidden: true` (solo visual, no en la sidebar). Imagen desde upload mediante `loadFileAsBase64URL(PlanBild)`.
2. **Shapes de punto de inspección** — de `(select Pruefpunkte where Objekt = obj.Nr)` con `movable`, `placeholders`, `actions` (escribir selección en el estado).
3. **`dataBag: { key: "geo" }`** en el Drawing — geometría en un bote JSON, los metadatos (`placedAt`, `inspectionPointId`, …) se conservan.
4. **`canvas` / `artboard`** — a partir de los metadatos del upload (ancho/alto del plano).
5. **`toolbarSettings`** — solo Move + los stickers necesarios en línea (ver ejemplo arriba).
6. **Bloquear movimiento** — booleano en el estado (`lockShapeMove`), por shape `movable: if lockShapeMove then false else true end`. Toggle mediante botón con patch de `dataBag`. Consulta la regla de gestión de estado (`07-state-management.mdc`).

### 🔸 `rightSideContent`

El bloque `rightSideContent` controla si **se muestra una barra lateral derecha en el widget** y qué se representa allí. Esta sidebar es especialmente útil para mostrar una lista de los elementos dibujados o para ofrecer información adicional.

#### 🔧 Estructura:

- `show`: `true` o `false` – activa o desactiva la sidebar derecha.
- `header`: (opcional) Widgets o contenido de texto para la parte superior de la sidebar.
- `footer`: (opcional) Widgets o contenido de texto para la parte inferior de la sidebar.
- `content`: (opcional) Configuración individual del contenido central.

Si `show: true` está definido y las shapes contienen entradas `sidebarItem` (ver `shapes`), se muestra automáticamente una **lista interactiva de todas las shapes** — con título, descripción, efecto hover y comportamiento de selección.

<h5>🔍 Bloque `content` en detalle:</h5>- `showShapeLayers`:
Si es `true`, se muestra una lista automática de todas las shapes existentes (con `sidebarItem`) — incluyendo título, descripción, comportamiento de hover y clic.
Ideal, p. ej., para listas de defectos, puntos de inspección o anotaciones.
- `customLayout`:
Contenido personalizado opcional que se puede mostrar en el área central.
Aquí puedes integrar, p. ej., HTML, resultados de fórmulas de Ninox o componentes de layout preparados.

Si `showShapeLayers` está activo, se muestra la lista de shapes. Si en su lugar quieres mostrar tu propio layout, puedes usar `customLayout`.

💡 **Notas:**

- La sidebar funciona especialmente bien cuando dotas a las shapes de información `sidebarItem`.
- El área se puede adaptar según el caso de uso de forma puramente informativa, interactiva o visual.
- También puedes tomar `customLayout` de forma dinámica desde un campo de Ninox — p. ej. mediante una fórmula de texto o un componente HTML.
- Si quieres combinar ambos (lista de capas + contenido propio), puedes insertar `customLayout` sobre el footer y dejar `showShapeLayers` en `true`.

### 🔧 ¿No eres desarrollador? No hay problema.

¿No tienes tiempo para ocuparte de código y configuración? El **equipo de arcRider** te ofrece **soporte de instalación y configuración** para el widget `arcCustomDrawing` — a medida de tu caso de uso.

➡️ **Escríbenos a **[**office@arc-rider.com**](mailto:office@arc-rider.com).

Te ayudamos a empezar en pocas horas, sin quebraderos de cabeza técnicos.
