El doc cubria data-field-group (repartir vars en pestanas) pero no habia forma documentada de condicionar que se ve: los modulos con modos excluyentes ensenaban al usuario todos los campos de todos los modos a la vez. Se documentan los dos atributos nuevos (data-field-show en el campo, data-field-group-show en una var del grupo), que llegan al builder.json por el mismo mecanismo generico que data-field-group, con la tabla de la gramatica y un ejemplo de las dos formas de uso. Dos avisos que ahorran el error tipico: el campo se referencia por su NOMBRE DE VARIABLE y no por su label (los acentos se borran, no se transliteran: "Titulo" es `ttulo`), y `campo=` significa vacio, que es la clave de la primera opcion de todo `list`.
593 lines
21 KiB
Markdown
593 lines
21 KiB
Markdown
---
|
||
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 `<set>`, 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` | `<p>` | String |
|
||
| `headfield` | `<h1>`–`<h6>` | String + variable extra `_tag` con la etiqueta elegida |
|
||
| `textbox` | `<div>` | String multilínea |
|
||
| `wysiwyg` | `<div class="wysiwyg">` | String HTML |
|
||
| `link` | `<a>` | URL string (ya incluye barras) |
|
||
| `upload` | `<img>` | **Array** de `{urlPath, info1, info2, info3, info4}` |
|
||
| `uploadMulti` | `<li>` | Itera sobre archivos subidos |
|
||
| `list` (fijo) | `<div data-list-options="...">` | Valor seleccionado |
|
||
| `list` (tabla) | `<div data-list-table="...">` | `num` del registro |
|
||
| `multiv2` | `<li>` wrapper | Array de objetos repetibles |
|
||
| `colors` | `<div data-field-colors="fondo,titulo">` | Objeto de N colores por nombre: `{{ colores.fondo }}` |
|
||
| `colorpicker` | `<div>` | Hex color string |
|
||
|
||
### textfield
|
||
|
||
```html
|
||
<p data-field-type="textfield" data-field-label="Título">
|
||
Elemento editable
|
||
</p>
|
||
```
|
||
|
||
### headfield
|
||
|
||
Genera 2 variables: la estándar y `_tag` con la etiqueta elegida (h1…h6).
|
||
|
||
```html
|
||
<p data-field-type="headfield" data-field-label="Titulo" >
|
||
Título de la sección
|
||
</p>
|
||
```
|
||
|
||
### textbox
|
||
|
||
```html
|
||
<div data-field-type="textbox" data-field-label="Descripción">
|
||
Texto largo editable
|
||
</div>
|
||
```
|
||
|
||
### wysiwyg
|
||
|
||
Editor de texto enriquecido. Acceder con `| raw` para no escapar el HTML.
|
||
|
||
```html
|
||
<div class="wysiwyg" data-field-type="wysiwyg" data-field-label="Contenido Enriquecido">
|
||
<p>Texto con <strong>estilos</strong> editables</p>
|
||
</div>
|
||
```
|
||
|
||
### 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
|
||
<a data-field-type="link" data-field-label="Enlace">
|
||
Haz clic aquí
|
||
</a>
|
||
```
|
||
|
||
### upload
|
||
|
||
Devuelve un array. Acceso en Twig: `{{ imagen[0].urlPath }}`.
|
||
|
||
```html
|
||
<div class="p-1/6 relative">
|
||
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
|
||
data-field-type="upload"
|
||
data-field-label="Imagen Principal"
|
||
data-lazy="true"
|
||
data-field-info1="titulo"
|
||
data-field-width="1400"
|
||
alt="">
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<img data-field-type="upload"
|
||
data-field-label="Imagen Principal"
|
||
data-field-info1="Texto alternativo"
|
||
data-field-info2="Pie de foto"
|
||
data-field-width="1400"
|
||
alt="">
|
||
```
|
||
|
||
```twig
|
||
<img src="{{ imagenprincipal[0].urlPath }}" alt="{{ imagenprincipal[0].info1 }}">
|
||
<figcaption>{{ imagenprincipal[0].info2 }}</figcaption>
|
||
```
|
||
|
||
Los `data-field-infoN` funcionan igual en `uploadMulti`.
|
||
|
||
### uploadMulti
|
||
|
||
Itera sobre todas las imágenes subidas. Variable iteradora: `uploadMulti`.
|
||
|
||
```html
|
||
<li data-field-type="uploadMulti" data-field-label="Galería" data-field-info1="titulo">
|
||
<div class="relative min-h-screen">
|
||
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
|
||
data-src="{{ uploadMulti.urlPath | imagec(2100) }}"
|
||
alt="{{ uploadMulti.info1 }}">
|
||
</div>
|
||
</li>
|
||
```
|
||
|
||
### list (opciones fijas)
|
||
|
||
```html
|
||
<div data-field-type="list"
|
||
data-field-label="Color Producto"
|
||
data-list-options="Rojo,Azul,|Verde,3|Amarillo">
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div data-field-type="list"
|
||
data-field-label="Noticia Destacada"
|
||
data-list-table="noticias"
|
||
data-list-value="num"
|
||
data-list-label="titulo">
|
||
{{ record.titulo }}
|
||
</div>
|
||
```
|
||
|
||
- `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
|
||
<ul>
|
||
<li data-field-type="multiv2" data-field-label="Productos">
|
||
<div data-field-type="textfield" data-field-label="Nombre">
|
||
Nombre del producto
|
||
</div>
|
||
<div data-field-type="textbox" data-field-label="Descripción">
|
||
Descripción del producto
|
||
</div>
|
||
<div class="p-1/6 relative">
|
||
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
|
||
data-field-type="upload"
|
||
data-field-label="Imagen"
|
||
data-lazy="true"
|
||
data-field-width="800"
|
||
alt="">
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
```
|
||
|
||
Uso en Twig:
|
||
|
||
```twig
|
||
{% for record in productos %}
|
||
<div class="producto">
|
||
<h3>{{ record.nombre }}</h3>
|
||
<p>{{ record.descripcion }}</p>
|
||
<img src="{{ record.imagen[0].urlPath }}" alt="">
|
||
</div>
|
||
{% 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
|
||
<div c-hidden="true">
|
||
<div data-field-type="colors"
|
||
data-field-label="Colores"
|
||
data-field-colors="fondo,titulo,boton"></div>
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div c-hidden="true">
|
||
<div data-field-type="colorpicker" data-field-label="Color de fondo"></div>
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div data-field-type="textfield" data-field-label="Título" data-field-group="Contenido">
|
||
Título de la sección
|
||
</div>
|
||
<div data-field-type="textbox" data-field-label="Descripción" data-field-group="Contenido">
|
||
Texto de apoyo
|
||
</div>
|
||
|
||
<div c-hidden="true">
|
||
<div data-field-type="colorpicker" data-field-label="Color de fondo" data-field-group="Estilos"></div>
|
||
<div data-field-type="list"
|
||
data-field-label="Alineación"
|
||
data-list-options="|Izquierda,1|Centro,2|Derecha"
|
||
data-field-group="Estilos"></div>
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div c-hidden="true">
|
||
<!-- Pestaña completa: "Estilos" solo aparece si el usuario activa el switch -->
|
||
<div data-field-type="list" data-field-label="Mostrar Estilos" data-list-options="|No,1|Si"></div>
|
||
<div data-field-type="textfield"
|
||
data-field-label="Texto de Estilos"
|
||
data-field-group="Estilos"
|
||
data-field-group-show="mostrarestilos=1"></div>
|
||
|
||
<!-- Campo a campo: cada opción del list muestra su propio campo -->
|
||
<div data-field-type="list"
|
||
data-field-label="Modo Modulo"
|
||
data-list-options="|Opcion 1,1|Opcion 2,2|Opcion 3"
|
||
data-field-group="Contenido"></div>
|
||
<div data-field-type="textfield" data-field-label="Opcion 1" data-field-group="Contenido" data-field-show="modomodulo="></div>
|
||
<div data-field-type="textfield" data-field-label="Opcion 2" data-field-group="Contenido" data-field-show="modomodulo=1"></div>
|
||
<div data-field-type="textfield" data-field-label="Opcion 3" data-field-group="Contenido" data-field-show="modomodulo=2"></div>
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div c-if="subtitle">{{ subtitle }}</div>
|
||
<div c-if="layout = 'grid'">Grid layout</div>
|
||
```
|
||
|
||
### `c-else`
|
||
|
||
Va inmediatamente después del elemento `c-if`.
|
||
|
||
```html
|
||
<div c-if="image">
|
||
<img src="{{ image[0].urlPath }}">
|
||
</div>
|
||
<div c-else>
|
||
<p>No image available</p>
|
||
</div>
|
||
```
|
||
|
||
### `c-for` — Iteración sobre array
|
||
|
||
```html
|
||
<div c-for="item in record.features">
|
||
<h3>{{ item.title }}</h3>
|
||
</div>
|
||
```
|
||
|
||
### `c-for` — Iteración sobre tabla de BD
|
||
|
||
```html
|
||
<ul>
|
||
<li c-for="producto in productos"
|
||
c-where="'visible=1'"
|
||
c-order="'num desc'"
|
||
c-limit="10">
|
||
{{ producto.title }}
|
||
</li>
|
||
</ul>
|
||
```
|
||
|
||
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) %}
|
||
<li>{{ producto.title }}</li>
|
||
{% endfor %}
|
||
```
|
||
|
||
Variables del loop: `loop.index` (1-based), `loop.index is odd`, `loop.index is even`.
|
||
|
||
### `c-class` — Clases CSS condicionales
|
||
|
||
```html
|
||
<!-- Simple -->
|
||
<div c-class="{ 'text-center': alineacion == '1', 'text-right': alineacion == '2' }">
|
||
|
||
<!-- Múltiples condiciones -->
|
||
<div c-class="{
|
||
'flex-row-reverse': orden == '1',
|
||
'cursor-pointer click-a-child': record.enlace_anchor,
|
||
'rounded-xl': radioborde == '4'
|
||
}">
|
||
|
||
<!-- Con loop -->
|
||
<div c-class="{
|
||
'md:order-1': loop.index is odd,
|
||
'md:pl-6': loop.index is even
|
||
}">
|
||
|
||
<!-- Combinado con clases estáticas -->
|
||
<div class="flex items-center" c-class="{ 'justify-center': centrado }">
|
||
```
|
||
|
||
### `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
|
||
<div c-hidden="true">
|
||
<input data-field-type="textfield" data-field-label="Color de fondo" value="">
|
||
<div data-field-type="list"
|
||
data-field-label="Color titulo resaltado"
|
||
data-list-options="|Main color,1|Main color light,2|Main color dark"></div>
|
||
</div>
|
||
```
|
||
|
||
### `c-required` — Validación condicional
|
||
|
||
```html
|
||
<input type="text" name="telefono"
|
||
c-required="'2' not in camposquitar"
|
||
placeholder="Teléfono">
|
||
```
|
||
|
||
## Tag `<set>` — Definir variables
|
||
|
||
```html
|
||
<!-- Obtener configuración de la BD -->
|
||
<set :tienda="'configuracion_tienda' | get('num != 0')[0]"></set>
|
||
|
||
<!-- Construir URLs dinámicas -->
|
||
<set :logo="tienda.logo.0.urlPath
|
||
? 'https://' ~ server.HTTP_HOST ~ tienda.logo.0.urlPath
|
||
: 'https://' ~ server.HTTP_HOST ~ '/template/estandar/images/logo.png'">
|
||
</set>
|
||
|
||
<!-- Twig set para expresiones complejas -->
|
||
{% 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
|
||
<module_id :param1="value1" :param2="'string value'"></module_id>
|
||
```
|
||
|
||
Ejemplos:
|
||
```html
|
||
<header_menu :showLogo="true" :menuItems="items"></header_menu>
|
||
<product_card :product="selectedProduct" :showPrice="true"></product_card>
|
||
```
|
||
|
||
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
|
||
<c-form
|
||
class="max-w-2xl mx-auto p-6 bg-white rounded-lg shadow"
|
||
tableName="'solicitudes'"
|
||
mailRecord="['correos', 'CONTACTO']"
|
||
sendTo="'contacto@empresa.com'"
|
||
sendToClient="'email'"
|
||
captcha="true"
|
||
honeypot="true"
|
||
messageOK="'¡Gracias! Te contactaremos pronto'"
|
||
messageKO="'Por favor, completa todos los campos'"
|
||
redirect="'/gracias'"
|
||
attachFiles="true">
|
||
|
||
<input name="nombre" type="text" required class="w-full p-2 border rounded">
|
||
<input name="email" type="email" required class="w-full p-2 border rounded">
|
||
<textarea name="mensaje" required class="w-full p-2 border rounded" rows="5"></textarea>
|
||
|
||
<label class="flex items-center">
|
||
<input name="acepto_politica" type="checkbox" class="mr-2" required>
|
||
<span>Acepto la política de privacidad</span>
|
||
</label>
|
||
|
||
<button type="submit" class="bg-teal-500 text-white px-6 py-2 rounded">Enviar</button>
|
||
<captcha/>
|
||
</c-form>
|
||
```
|
||
|
||
### 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="'<div>...'"` | HTML cabecera del email |
|
||
| `footer="'<div>...'"` | 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
|
||
<div class="c-tns-wrapper"
|
||
data-responsive='{"0":1,"768":2,"1024":3}'
|
||
data-speed="400"
|
||
data-nav="true"
|
||
data-autoplay-timeout="3000">
|
||
<div c-for="slide in record.slides">
|
||
<img src="{{ slide.image[0].urlPath }}">
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### Lightbox
|
||
|
||
```html
|
||
<a href="{{ image[0].urlPath }}" class="glightbox" data-gallery="gallery1">
|
||
<img src="{{ image[0].urlPath | imagec(400) }}">
|
||
</a>
|
||
```
|
||
|
||
### Breadcrumb
|
||
|
||
```html
|
||
<breadCrumb/>
|
||
<breadCrumb class="bg-gray-200 p-3 rounded" c-prevlinks="null"></breadCrumb>
|
||
```
|
||
|
||
### Animate On Scroll (AOS)
|
||
|
||
```html
|
||
<div data-aos="fade-up" data-aos-delay="200" data-aos-duration="800">
|
||
Contenido animado
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<img class="lazyload" data-src="{{ image[0].urlPath }}">
|
||
<!-- O en builder field: -->
|
||
<img data-field-type="upload" data-field-label="Imagen" data-lazy="true">
|
||
```
|
||
|
||
## 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`.
|