--- title: "Campos editables del builder" tags: [builder, twig, html, modules] load_priority: 80 load_when: [always] summary: "Atributos data-field-* (textfield, headfield, link, upload, list, multiv2, colors, colorpicker), agrupar campos en pestañas con data-field-group, c-if/c-for/c-class, c-form, componentes built-in del builder Acai." --- # Builder Fields — Campos editables del index-base.tpl Este documento define los campos editables que el usuario rellena desde el panel del builder de Acai. Cubre el atributo `data-field-type` con todos sus tipos (`textfield`, `headfield`, `textbox`, `wysiwyg`, `link`, `upload`, `uploadMulti`, `list`, `multiv2`, `colors`, `colorpicker`, `corners`, `ratio`), la regla `data-field-label` → nombre de variable, los atributos Acai (`c-if`, `c-else`, `c-for`, `c-class`, `c-hidden`, `c-required`), el tag ``, la inclusión de módulos, los formularios `c-form` y los componentes built-in. Léelo antes de crear o modificar cualquier `index-base.tpl`. ## Reglas de nomenclatura de variables El atributo `data-field-label` se convierte automáticamente en el nombre de variable Twig: se ponen minúsculas y se eliminan espacios y caracteres especiales. | Label | Variable resultante | |-------|---------------------| | `Categoría Noticia` | `categoranoticia` | | `Color Principal` | `colorprincipal` | | `Título Producto` | `ttuloproducto` | Reglas obligatorias: - Todo elemento con `data-field-type` DEBE incluir también `data-field-label`. - Sin `data-field-label`, el builder genera variables temporales o incorrectas y el módulo queda mal configurado. - Usa labels descriptivos y estables; no dejes labels vacíos ni genéricos como "Campo" o "Texto". - En `index-base.tpl` evita clases Tailwind con valores arbitrarios (`text-[44px]`, `font-['Cinzel']`, `leading-[1.1]`) — pueden romper el parseo. Muévelas a `style.css`. ## Tipos de campo (`data-field-type`) | Tipo | Elemento HTML | Devuelve | |------|---------------|----------| | `textfield` | `

` | String | | `headfield` | `

`–`

` | String + variable extra `_tag` con la etiqueta elegida | | `textbox` | `
` | String multilínea | | `wysiwyg` | `
` | String HTML | | `link` | `` | URL string (ya incluye barras) | | `upload` | `` | **Array** de `{urlPath, info1, info2, info3, info4}` | | `uploadMulti` | `
  • ` | Itera sobre archivos subidos | | `list` (fijo) | `
    ` | Valor seleccionado | | `list` (tabla) | `
    ` | `num` del registro | | `multiv2` | `
  • ` wrapper | Array de objetos repetibles | | `colors` | `
    ` | Objeto de N colores por nombre: `{{ colores.fondo }}` | | `colorpicker` | `
    ` | Hex color string | ### textfield ```html

    Elemento editable

    ``` ### headfield Genera 2 variables: la estándar y `_tag` con la etiqueta elegida (h1…h6). ```html

    Título de la sección

    ``` ### textbox ```html
    Texto largo editable
    ``` ### wysiwyg Editor de texto enriquecido. Acceder con `| raw` para no escapar el HTML. ```html

    Texto con estilos editables

    ``` ### link El campo `enlace` de Acai ya incluye las barras necesarias — nunca añadas barras extra. Genera 2 variables: la estándar y `_anchor` con el anchor del enlace. ```html
    Haz clic aquí ``` ### upload Devuelve un array. Acceso en Twig: `{{ imagen[0].urlPath }}`. ```html
    ``` Atributos disponibles: - `data-lazy="true"` — carga perezosa - `data-field-width="1400"` — ancho máximo sugerido - `data-field-info1` … `data-field-info5` — labels de los campos de información por imagen Los `data-field-infoN` (hasta 5) definen los labels que el builder muestra como campos editables **en cada imagen subida** (van a `infoLabels` en el `builder.json`). Sus valores se leen luego como `info1`…`info4` dentro del array del var. ```html ``` ```twig {{ imagenprincipal[0].info1 }}
    {{ imagenprincipal[0].info2 }}
    ``` Los `data-field-infoN` funcionan igual en `uploadMulti`. ### uploadMulti Itera sobre todas las imágenes subidas. Variable iteradora: `uploadMulti`. ```html
  • {{ uploadMulti.info1 }}
  • ``` ### list (opciones fijas) ```html
    ``` Formato `data-list-options`: - `opcion1,opcion2` → la opción es etiqueta y valor a la vez - `|valor3,etiqueta3` → separa valor de etiqueta con `|` ### list (tabla) Selecciona un registro de otra tabla. Devuelve el `num`. ```html
    {{ record.titulo }}
    ``` - `data-list-table` — nombre de tabla **sin prefijo `cms_`** - `data-list-value` — campo a usar como valor (normalmente `num`) - `data-list-label` — campo a mostrar como label ### multiv2 — Campos repetibles Crea grupos de campos repetibles. La variable resultante es un array de objetos. ```html
    • Nombre del producto
      Descripción del producto
    ``` Uso en Twig: ```twig {% for record in productos %}

    {{ record.nombre }}

    {{ record.descripcion }}

    {% endfor %} ``` ### colors — paleta de colores Un solo campo que agrupa VARIOS colores. Los nombres de cada color se declaran en `data-field-colors`, separados por comas: ```html
    ``` El valor guardado es un JSON con pares nombre/valor. Cada color puede ser sólido o un gradiente lineal: ```json {"fondo": "#ffffff", "titulo": "#111111", "boton": "linear-gradient(90deg, #aaa, #000)"} ``` En Twig se accede a cada color por su nombre: `{{ colores.fondo }}`. ### colorpicker — un solo color Cuando solo necesitas UN color, no una paleta. El valor es un string hexadecimal plano: ```html
    ``` Valor guardado: `#ff0000`. En Twig: `{{ colordefondo }}`. Usa `colorpicker` para un color suelto y `colors` cuando el módulo tenga varios: `colors` gasta UNA sola variable para N colores, mientras que N `colorpicker` gastan N. ### corners — radios de esquina Selector de radio de borde en escala Tailwind. Mismo patrón que `colors`. ### ratio — proporción Selector de proporción de imagen (16/9, 4/3, 1/1...). > Para un valor booleano usa `list` con dos opciones: el builder no tiene un tipo de casilla. > (No lo confundas con el tipo `checkbox` de los campos de TABLA del CMS, que sí existe y se > documenta en `05-tables-and-fields.md`. Son dos vocabularios distintos.) ## Agrupar campos en pestañas (`data-field-group`) Los campos de un módulo se reparten en pestañas dentro del panel de configuración añadiendo `data-field-group="Nombre del grupo"` al mismo elemento que ya lleva `data-field-type`. Mecanismo: cualquier atributo `data-field-*` extra —aparte de `data-field-type`, `data-field-label` y `data-field-value`— se recoge en el objeto `customDataField` del var, recortando el prefijo `data-field-`. Es decir, `data-field-group="Estilos"` acaba como `customDataField: { group: "Estilos" }` en el `builder.json`. Comportamiento del panel: - Los vars se agrupan por `customDataField.group` y se muestran en pestañas laterales. - Los vars sin `data-field-group` caen en la pestaña **Principal**. - El orden de las pestañas es el de primera aparición de cada grupo en el template. - Con un solo grupo no se muestran pestañas. - Funciona igual en vars de primer nivel y dentro de `multiv2`. ```html
    Título de la sección
    Texto de apoyo
    ``` Recomendación de uso: - Agrupa cuando el módulo pase de ~6-8 vars; por debajo, una sola lista se lee mejor. - Reparto habitual: **Contenido** (textos e imágenes), **Estilos** (colores, alineación, bordes), **Ajustes** (opciones de comportamiento, límites, enlaces). - Mantén los nombres de grupo consistentes entre módulos y en español. Son literales: `Estilos` y `estilos` generan dos pestañas distintas. ## Mostrar u ocultar campos y pestañas (`data-field-show`) Un campo o una pestaña entera pueden depender del valor de otro campo del mismo módulo. Se declara con dos atributos, que viajan al `builder.json` por el mismo mecanismo genérico que `data-field-group` (`customDataField.show` y `customDataField['group-show']`): - `data-field-show="campo=valor"` — en el elemento del campo que se quiere condicionar. - `data-field-group-show="campo=valor"` — en **una** var del grupo; condiciona la pestaña completa. Gramática: | Condición | Se muestra cuando | |-----------|-------------------| | `modo=1` | el valor es `1` | | `modo=1,2` | el valor es `1` o `2` | | `modo=` | el campo está **vacío** — es la clave de la primera opción de todo `list` | | `modo!=1` | el valor NO es `1` | | `modo=1;otro=2` | se cumplen ambas (AND) | El campo se referencia por su **nombre de variable**, no por su label: se aplican las [reglas de nomenclatura](#reglas-de-nomenclatura-de-variables), así que `Mostrar Estilos` se referencia como `mostrarestilos` y `Título` como `ttulo` (los acentos se borran, no se transliteran). Los valores no pueden contener coma ni punto y coma. ```html
    ``` Comportamiento del panel: - **Ocultar no borra.** El valor sigue guardado y sigue llegando al Twig; si el usuario vuelve a mostrar el campo, lo escrito sigue ahí. - **Por eso la plantilla debe repetir la condición con `c-if`.** Ocultar el campo en el panel NO lo quita de la web: si `Texto de Estilos` no debe pintarse cuando `mostrarestilos` está a `0`, el `index-base.tpl` necesita su propio `c-if="mostrarestilos = '1'"`. - Una pestaña se oculta sola cuando todos sus campos han quedado ocultos por su `show`; en la mayoría de casos basta con condicionar los campos y no hace falta `data-field-group-show`. - Si varias vars del mismo grupo declaran `data-field-group-show`, **gana la primera** y las demás se ignoran. - Si una condición apunta a un campo que no existe (typo, o un label renombrado que cambió el nombre de variable), el campo **se muestra igualmente** y el aviso queda en la consola del navegador. Nunca desaparece en silencio. - Dentro de `multiv2` la condición se evalúa contra los valores **de ese item**, no contra los del módulo. - En modo traducción las condiciones se evalúan contra los valores del **idioma base**. ## Atributos Acai ### `c-if` — Renderizado condicional Usa `=` (un solo igual) para comparaciones, no `==`. ```html
    {{ subtitle }}
    Grid layout
    ``` ### `c-else` Va inmediatamente después del elemento `c-if`. ```html

    No image available

    ``` ### `c-for` — Iteración sobre array ```html

    {{ item.title }}

    ``` ### `c-for` — Iteración sobre tabla de BD ```html
    • {{ producto.title }}
    ``` Parámetros opcionales: `c-where` (string SQL), `c-order` (string de orden), `c-limit` (entero). Equivalente Twig: ```twig {% for producto in 'productos' | get('visible=1','num desc',10) %}
  • {{ producto.title }}
  • {% endfor %} ``` Variables del loop: `loop.index` (1-based), `loop.index is odd`, `loop.index is even`. ### `c-class` — Clases CSS condicionales ```html
    ``` ### `c-hidden` — Variables ocultas Elemento que NO se renderiza pero SÍ declara variables builder. Patrón típico para colores y opciones de configuración. ```html
    ``` ### `c-required` — Validación condicional ```html ``` ## Tag `` — Definir variables ```html {% set gracias = 'apartados' | get('num = 20').0 %} ``` ## Incluir módulos Para incluir un módulo dentro de otro módulo o dentro de una sección general, usa el `moduleId` como etiqueta HTML: ```html ``` Ejemplos: ```html ``` El módulo hijo recibe los parámetros como variables en su contexto. ## Formularios — `c-form` Maneja automáticamente validación, almacenamiento en BD y envío de emails. ```html ``` ### Atributos `c-form` | Atributo | Descripción | |----------|-------------| | `tableName="'tabla'"` | Tabla destino (sin `cms_`) | | `mailRecord="['correos', 'ID']"` | Template de email en tabla `correos` | | `sendTo="'email@dominio.com'"` | Destinatarios (separados por coma) | | `sendToClient="'campo_email'"` | Campo del formulario con email del cliente para auto-reply | | `captcha="true"` | Activa Google reCAPTCHA | | `honeypot="true"` | Campo oculto anti-spam | | `messageOK="'texto'"` | Mensaje al enviar correctamente | | `messageKO="'texto'"` | Mensaje al fallar validación | | `redirect="'/ruta/'"` | Redirección tras envío correcto | | `attachFiles="true"` | Adjuntar archivos al email | | `showImages="true"` | Mostrar thumbnails en email | | `emailMode="'twig'"` | Email en formato Twig | | `header="'
    ...'"` | HTML cabecera del email | | `footer="'
    ...'"` | HTML footer del email | | `styles="'body { ... }'"` | CSS del email | Para formularios estándar (contacto, postulación), prefiere `c-form` antes que crear lógica custom de POST/hook. Solo crea una tabla propia si necesitas gestionar esos registros desde el admin. ## Componentes built-in ### Carousel — `c-tns-wrapper` ```html
    ``` ### Lightbox ```html ``` ### Breadcrumb ```html ``` ### Animate On Scroll (AOS) ```html
    Contenido animado
    ``` Valores comunes: `fade-up`, `fade-down`, `fade-left`, `fade-right`, `zoom-in`, `zoom-in-up`, `fade-up-right`, `fade-up-left`. Tras cambios dinámicos en JS: `AOS.refresh()`. ### Lazy loading ```html ``` ## Reglas críticas 1. Todo `data-field-type` exige `data-field-label`. 2. `data-field-label` se transforma a variable: minúsculas, sin espacios ni caracteres especiales. 3. Campos `upload` retornan **arrays** — usa `imagen[0].urlPath`, nunca `imagen`. 4. Variables dentro de `multiv2` son propiedades del objeto iterado (`record.nombre`). 5. `c-if` usa `=` (un igual). `{% if %}` usa `==` (doble igual). 6. `c-for` con tabla: nombre **sin prefijo `cms_`**. 7. `enlace` ya incluye las barras — no añadas slashes extra. 8. Checkbox guarda `1` o `0` (número), nunca `true`/`false`. 9. Evita Tailwind arbitrary-value en `index-base.tpl` — muévelos a `style.css`. 10. `script.js` y `style.css` son estáticos: NO uses sintaxis Twig dentro. Pasa valores dinámicos vía `data-*`. 11. Si el módulo lista registros de una tabla del CMS (`c-for` sobre ella, o su `hook.php` la consulta), declara esa tabla con `update_module_metadata({ cmsTables: [...] })`. No es un `data-field-*`: no se deduce del HTML, hay que declararla. Ver `03-modules-and-sections.md`.