Files
agenticSystem/docs/vue-builder-rules.md
2026-04-01 23:16:45 +01:00

28 KiB

Reglas CMS-VUE

Aplica estas reglas ÚNICAMENTE cuando el usuario incluya "cms-vue" o "CMS-VUE" (en cualquier combinación de mayúsculas/minúsculas) en su mensaje. Ejemplos válidos: "dame el cms-vue", "cms-vue personalizado", "crea el CMS-VUE", "necesito el cms-vue de este módulo". Si el mensaje NO contiene "cms-vue", ignora completamente estas instrucciones.


1. Estructura general: Tabs (acai-vue-tabs)

  • Analiza el HTML proporcionado para determinar cuántos tabs son necesarios y cómo nombrarlos.
  • Tabs base comunes: Configuración, Imágenes, Textos, Bloques (records), Enlaces, Colores.
  • Añade tabs adicionales si el módulo lo requiere (ej: "Formulario", "Video", "Overlay", "Slider", etc.).
  • Si un tab solo tendría 1 campo, evalúa fusionarlo con otro tab relacionado.
  • Cada tab tiene su propio id, label, color e icon (SVG inline).
  • SVG dentro del template usan :style="{ color: color }" para heredar el color del tab.
  • Textos descriptivos claros y orientados al usuario final del CMS.
  • Usa storage-key único: 'nombre-modulo-tabs-' + (section_id || 'default').
  • Siempre añade :apply-theme-styles="true".
  • IMPORTANTE: La prop para pasar los tabs es :tabs (NO :tabs-config).
  • IMPORTANTE: Siempre añadir v-if="data" en el <acai-vue-tabs> para evitar renderizar antes de que los datos estén listos.

Template de cada tab:

<template #idtab="{ color }">
    <div class="w-full mb-6">
        <p class="text-xl font-semibold text-gray-800">Título descriptivo del tab</p>
    </div>
    <!-- campos -->
</template>

2. Colorpicker según contexto

2.1 En tab "Colores" (campos generales de color de texto/fondo)

Siempre con SVG + título + descripción + colorpicker + textfield oculto:

<div class="flex w-full items-center mt-6">
    <div class="w-full">
        <div class="flex items-center">
            <svg class="w-10 h-10 flex-shrink-0 mr-4 stroke-current" :style="{ color: color }" ...>...</svg>
            <p class="leading-snug text-gray-600"><b class="text-black">Nombre :</b> Descripción.</p>
        </div>
        <p class="text-xs leading-snug text-gray-500 mt-2 ml-14"><b class="text-gray-700">Nota :</b> valor por defecto.</p>
        <div class="relative mt-2 ml-14">
            <acai-vue-colorpicker :builder="builder" :data="data" :field="'campo'" :label="'Etiqueta'" :color="'#hex'" @save-data="saveData"></acai-vue-colorpicker>
        </div>
        <div style="display: none">
            <acai-vue-textfield :builder="builder" :data="data" :field="'campo'" @save-data="saveData"></acai-vue-textfield>
        </div>
    </div>
</div>

2.2 Colorpicker en otros tabs (ej: color del overlay en tab Imágenes)

Misma estructura con SVG + título + descripción + colorpicker + textfield oculto, pero usando el color del tab donde se encuentre. Se coloca junto a los campos relacionados (ver regla 10.2).

2.3 Dentro de <acai-vue-records> (sin icono ni descripción)

Se coloca debajo del campo al que corresponde:

  • Nota con mt-4 si campo anterior es textfield o title.
  • Nota con mt-3 si campo anterior es textbox o wysiwyg.
  • Colorpicker siempre con mt-1.
<p class="text-xs leading-snug text-gray-500 mt-4 ml-14"><b class="text-gray-700">Nota :</b> color por defecto (#hex).</p>
<div class="relative mt-1 ml-14">
    <acai-vue-colorpicker :builder="builder.vars.records" :data="record" :field="'campo'" :label="'Etiqueta'" :color="'#hex'" @save-data="saveData"></acai-vue-colorpicker>
</div>
<div style="display: none">
    <acai-vue-textfield :builder="builder.vars.records" :data="record" :field="'campo'" @save-data="saveData"></acai-vue-textfield>
</div>

2.4 Campos tipo list para colores

Se usan como <acai-vue-selectv2> con icono y nota. El componente detecta automáticamente si las opciones son colores y muestra el modo color selector con swatches. No llevan colorpicker ni textfield oculto.

2.5 Extraer color por defecto

Del HTML: style="color: {{ campo ? campo : '#HEX' }}" → usar #HEX. Si no hay color, usar #111827 (textos) o transparent (fondos).


3. Campos: estructura en tabs

Fuera de records:

<div class="flex w-full items-center mt-6">
    <div class="w-full">
        <div class="flex items-center">
            <svg class="w-10 h-10 flex-shrink-0 mr-4 stroke-current" :style="{ color: color }" ...>...</svg>
            <p class="leading-snug text-gray-600"><b class="text-black">Nombre :</b> Descripción.</p>
        </div>
        <p class="text-xs leading-snug text-gray-500 mt-2 ml-14"><b class="text-gray-700">Nota :</b> info adicional.</p>
        <div class="relative mt-2 ml-14">
            <!-- componente -->
        </div>
    </div>
</div>

Dentro de records:

<div class="w-full mt-6">
    <div class="flex items-center">
        <svg class="w-10 h-10 flex-shrink-0 mr-4 stroke-current" :style="{ color: color }" ...>...</svg>
        <p class="leading-snug text-gray-600"><b class="text-black">Nombre :</b> Descripción.</p>
    </div>
    <div class="relative mt-2 ml-14">
        <!-- componente con builder.vars.records y record -->
    </div>
</div>

4. Nombres de campos (:field)

  • Construir uniendo palabras del data-field-label en minúsculas sin espacios.
  • Eliminar acentos: á→a, é→e, í→i, ó→o, ú→u, ñ→(eliminar).
  • Ejemplos: Color del títulocolordeltitulo, Valoraciónvaloracion, Tamañotamao.

5. Upload de imágenes

General:

<acai-vue-upload ref="upload_campo" :reference="'upload_campo'" :tablename="'builder_custom'" :fieldname="builder.vars.campo.relations.builder_custom" :recordnum="data.campo.recordNum" :field="data.campo" :builder_field="builder.vars.campo" :presavetempid="data.campo.preSaveTempId" :add_button="true" @add_button_click="$parent.openCute('campo',data,false,'upload_campo')" class="border-2 px-3 py-2 border-gray-600 rounded-lg shadow bg-gray-200"></acai-vue-upload>

En records:

<acai-vue-upload :ref="'upload_campo_' + builder.vars.records.vars.campo.relations.builder_custom + '_' + record.campo.recordNum" :reference="'upload_campo_' + builder.vars.records.vars.campo.relations.builder_custom + '_' + record.campo.recordNum" :tablename="'builder_custom'" :fieldname="builder.vars.records.vars.campo.relations.builder_custom" :recordnum="record.campo.recordNum" :field="record.campo" :builder_field="builder.vars.records.vars.campo" :presavetempid="record.campo.preSaveTempId" :add_button="true" @add_button_click="$parent.openCute('campo',record,true,'upload_campo_' + builder.vars.records.vars.campo.relations.builder_custom + '_' + record.campo.recordNum)" class="border-2 px-3 py-2 border-gray-600 rounded-lg shadow bg-gray-200"></acai-vue-upload>

6. Componentes y URLs

Solo incluir los que se usen. Los componentes personalizados (tabs, selectv2) se cargan desde impulse; los estándar desde cocosolution:

// ── Componentes personalizados (impulse) ──
'acai-vue-tabs': httpVueLoader('https://impulse.webserver2.plandeweb.com/template/estandar/css/builder-acaivuetabsv2.vue?timestamp=' + new Date().getTime()),
'acai-vue-selectv2': httpVueLoader('https://impulse.webserver2.plandeweb.com/template/estandar/css/builder-acaivueselect-v2.vue?timestamp=' + new Date().getTime()),

// ── Componentes estándar (cocosolution) ──
'acai-vue-colorpicker': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuecolorpicker.vue?timestamp=' + new Date().getTime()),
'acai-vue-upload': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivueupload.vue?timestamp=' + new Date().getTime()),
'acai-vue-records': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuerecords.vue?timestamp=' + new Date().getTime()),
'acai-vue-title': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetitle.vue?timestamp=' + new Date().getTime()),
'acai-vue-wysiwyg': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuewysiwyg.vue?timestamp=' + new Date().getTime()),
'acai-vue-linkv2': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuelinkv2.vue?timestamp=' + new Date().getTime()),
'acai-vue-textbox': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetextbox.vue?timestamp=' + new Date().getTime()),
'acai-vue-textfield': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetextfield.vue?timestamp=' + new Date().getTime()),
'acai-vue-datepicker': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuedatepicker.vue?timestamp=' + new Date().getTime()),

IMPORTANTE: acai-vue-list ha sido reemplazado por acai-vue-selectv2 en todos los VUEs. NO usar acai-vue-list en nuevos VUEs.


7. Mapeo HTML → Vue

data-field-type Componente
textfield acai-vue-textfield
headfield acai-vue-title
wysiwyg acai-vue-wysiwyg
textbox acai-vue-textbox
list acai-vue-selectv2
upload / uploadMulti acai-vue-upload
linkv2 acai-vue-linkv2 (siempre con :show_text="true")
multiv2 acai-vue-records
textfield (usado como fecha) acai-vue-datepicker + acai-vue-textfield oculto

8. Script base

module.exports = {
    props: ["active", "section_id"],
    data() {
        return {
            data: null,
            builder: null,
            idiomas: IDIOMAS,
            tabsConfig: [ /* tabs */ ],
            // iconos para toggles (solo si hay campos de 2 opciones con iconos)
            // iconosNombreCampo: { '': '<svg>...</svg>', '1': '<svg>...</svg>' }
        };
    },
    components: { /* solo los usados */ },
    mounted() { this.$emit("child-mounted"); },
    methods: { saveData() { this.$emit("save-data"); } },
};

9. Decisión de tabs según contenido HTML y contexto semántico

9.1 Organización contextual (PRIORITARIA)

IMPORTANTE: Primero analizar el nombre del campo para determinar su contexto semántico, independientemente del tipo. Un campo list llamado "tipo de imagen" debe ir en el tab Imágenes, no en Configuración.

Keywords para tab Imágenes:

Campos que contengan: imagen, photo, video, fondo, background, logo, icono, icon

Ejemplos:

  • "tipo de imagen" (list) → Imágenes
  • "video de fondo" (list) → Imágenes
  • "logo principal" (upload) → Imágenes

Keywords para tab Enlaces:

Campos que contengan: enlace, link, boton, button, url, href

Ejemplos:

  • "texto del botón" (textfield) → Enlaces
  • "url externa" (textfield) → Enlaces
  • "estilo del enlace" (list) → Enlaces

Keywords para tab Textos:

Campos que contengan: titulo, title, texto, text, descripcion, description, contenido, content, label, etiqueta

Ejemplos:

  • "título principal" (headfield) → Textos
  • "descripción corta" (textfield) → Textos

9.2 Organización por tipo (fallback)

Si el nombre del campo no coincide con ninguna keyword, usar el tipo:

Tipo Tab
headfield, textfield, textbox, wysiwyg Textos
upload, image Imágenes
linkv2 Enlaces
list, select (sin contexto) Configuración
multiv2 (records) Bloques
Otros campos de configuración Configuración

10. Reglas especiales

10.1 Selector imagen/video con v-show

Cuando el HTML tenga un campo list con opciones tipo "|Imagen,1|Video":

  • El selector "Tipo de fondo" va en el tab Imágenes como primer campo (encima de los uploads).
  • Se renderiza como toggle con iconos (foto/vídeo) usando acai-vue-selectv2 con :toggle-icons.
  • Usa el icono icon-tabler-photo-video:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" :style="{ color: color }" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-10 h-10 flex-shrink-0 mr-4 stroke-current icon icon-tabler icons-tabler-outline icon-tabler-photo-video"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 15h-3a3 3 0 0 1 -3 -3v-6a3 3 0 0 1 3 -3h6a3 3 0 0 1 3 3v3" /><path d="M9 12a3 3 0 0 1 3 -3h6a3 3 0 0 1 3 3v6a3 3 0 0 1 -3 3h-6a3 3 0 0 1 -3 -3l0 -6" /><path d="M3 12l2.296 -2.296a2.41 2.41 0 0 1 3.408 0l.296 .296" /><path d="M14 13.5v3l2.5 -1.5l-2.5 -1.5" /><path d="M7 6v.01" /></svg>
  • El upload de imágenes lleva: v-show="data.tipodeimagen && data.tipodeimagen.newValues.builder_custom.value == ''" (visible cuando es imagen o vacío).
  • El upload de video lleva: v-show="data.tipodeimagen && data.tipodeimagen.newValues.builder_custom.value == '1'" (visible cuando es video).
  • NUNCA quitar estos v-show, son esenciales para mostrar uno u otro según la selección.
  • Iconos del toggle:
iconosTipoImagen: {
    '': '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-photo"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" /><path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3" /></svg>',
    '1': '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-video"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 10l4.553 -2.276a1 1 0 0 1 1.447 .894v6.764a1 1 0 0 1 -1.447 .894l-4.553 -2.276v-4z" /><path d="M3 6m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z" /></svg>'
}

10.2 Grupo overlay (tipo + color + opacidad)

Cuando el HTML contenga campos de overlay (tipo de overlay, color del overlay, opacidad del overlay):

  • Los tres campos van juntos en el tab Imágenes, debajo de la imagen/video sobre la que se aplica el overlay.
  • El orden es: tipo de overlay → color del overlay (colorpicker) → opacidad del overlay.
  • El color del overlay NO va en el tab Colores, va en Imágenes junto al resto del grupo overlay.
  • El tipo de overlay (2 opciones: Sin degradado / Con degradado) se renderiza como toggle con iconos:
iconosOverlay: {
    '': '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-square"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 3m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /></svg>',
    '1': '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-gradient"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 3m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /><path d="M7 3v18" /><path d="M3 14h4" /><path d="M3 10h4" /><path d="M3 6h4" /><path d="M3 18h4" /></svg>'
}

10.3 Campos que afectan al enlace

Los campos list que modifican propiedades del botón de enlace (radio borde, estilo, etc.) van en el tab Enlaces, debajo del campo linkv2 al que afectan. NO van en Configuración ni en Imágenes.

10.4 Tabs base: definición fija de id, label, color e icono

Los tabs base siempre usan la siguiente definición fija. Este es el orden por defecto; solo se incluyen los tabs que el módulo necesite. Tabs adicionales (ej: "Formulario", "Video") se crean con id, label, color e icono nuevos.

{ id: "configuracion", label: "Configuración", color: "#f59e0b", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"/><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/></svg>' },

{ id: "imagenes", label: "Imágenes", color: "#10b981", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M11 20h-4a3 3 0 0 1 -3 -3v-10a3 3 0 0 1 3 -3h10a3 3 0 0 1 3 3v4" /><path d="M4 15l4 -4c.928 -.893 2.072 -.893 3 0l3 3" /><path d="M14 14l1 -1c.31 -.298 .644 -.497 .987 -.596" /><path d="M18.42 15.61a2.1 2.1 0 0 1 2.97 2.97l-3.39 3.42h-3v-3l3.42 -3.39" /></svg>' },

{ id: "textos", label: "Textos", color: "#3b82f6", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 10h-14" /><path d="M5 6h14" /><path d="M14 14h-9" /><path d="M5 18h6" /><path d="M18 15v6" /><path d="M15 18h6" /></svg>' },

{ id: "bloques", label: "Bloques", color: "#ec4899", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 4l-8 4l8 4l8 -4l-8 -4" /><path d="M4 12l8 4l8 -4" /><path d="M4 16l8 4l8 -4" /></svg>' },

{ id: "enlaces", label: "Enlaces", color: "#ef4444", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 15l6 -6"/><path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464"/><path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463"/></svg>' },

{ id: "colores", label: "Colores", color: "#8b5cf6", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 3h-4a2 2 0 0 0 -2 2v12a4 4 0 0 0 8 0v-12a2 2 0 0 0 -2 -2" /><path d="M13 7.35l-2 -2a2 2 0 0 0 -2.828 0l-2.828 2.828a2 2 0 0 0 0 2.828l9 9" /><path d="M7.3 13h-2.3a2 2 0 0 0 -2 2v4a2 2 0 0 0 2 2h12" /><path d="M17 17l0 .01" /></svg>' },

10.5 Campos globales que afectan al multi van DENTRO del tab Bloques

Los campos list o textfield generales (no de records) que afectan visualmente a los elementos del multi (ej: radio de borde de los bloques, alineación del texto de los bloques, diseño del enlace de los bloques) deben colocarse dentro del tab Bloques, en la zona superior, ANTES del bloque descriptivo "Bloques del multi" y del <acai-vue-records>. Estos campos NO van en Configuración ni en otros tabs, ya que pertenecen conceptualmente a los bloques.

10.6 Bloque descriptivo "Bloques del multi" antes de acai-vue-records

Siempre añadir un bloque descriptivo con el icono icon-tabler-stack-2 y el texto "Bloques del multi : Personaliza los bloques del multi." justo antes de <acai-vue-records>:

<!-- Multi -->
<div class="flex w-full items-center mt-6">
    <div class="w-full">
        <div class="flex items-center">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" :style="{ color: color }" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-10 h-10 flex-shrink-0 mr-4 icon icon-tabler icons-tabler-outline icon-tabler-stack-2"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 4l-8 4l8 4l8 -4l-8 -4" /><path d="M4 12l8 4l8 -4" /><path d="M4 16l8 4l8 -4" /></svg>
            <p class="leading-snug text-gray-600"><b class="text-black">Bloques del multi :</b> Personaliza los bloques del multi.</p>
        </div>
    </div>
</div>

10.7 Slot de acai-vue-records: NO desestructurar color

El slot de <acai-vue-records> NUNCA debe desestructurar color. Siempre usar:

<template v-slot="{ record, index }">

NUNCA usar:

<template v-slot="{ record, color, index }">

De esta forma, color dentro del multi resuelve al color del tab padre (<template #bloques="{ color }">), y los iconos SVG con :style="{ color: color }" siempre mostrarán el color correcto del tab.

10.8 Estructura del componente acai-vue-records

El componente <acai-vue-records> siempre debe incluir todas estas props y atributos:

<acai-vue-records :data="data" :builder="builder" :active="active" :section_id="section_id" :root_builder_vue="$parent" ref="recordsNode">
    <template v-slot="{ record, index }">
        <!-- campos del record -->
    </template>
</acai-vue-records>

NUNCA usar una versión simplificada sin :active, :section_id, :root_builder_vue o ref.

10.9 Orden del campo "Color título resaltado" en tab Colores

Cuando el módulo tenga un campo de Color título resaltado (tipo list con opciones Main color / Main color light / Main color dark), este campo debe colocarse inmediatamente debajo del campo Color del título en el tab Colores. Nunca en el tab Textos ni en otra posición del tab Colores.


11. Consistencia de iconos y textos descriptivos entre VUEs

11.1 Iconos

Los campos que ya tienen un icono SVG asignado en VUEs anteriores deben usar SIEMPRE ese mismo icono en todos los VUEs futuros. Solo se crean o personalizan iconos nuevos para campos que no se hayan visto antes en ningún VUE previo.

11.2 Textos descriptivos

Los textos descriptivos (título en negrita + descripción + nota) de campos recurrentes (pretítulo, título, subtítulo, texto largo, enlace, color de fondo, color del texto, etc.) deben ser idénticos en todos los VUEs. Solo se modifican si el HTML del módulo revela un comportamiento diferente para ese campo concreto.

11.3 Registro de referencia

Usar como referencia los iconos y textos del primer VUE en que apareció cada tipo de campo. Ante cualquier duda, mantener consistencia con lo ya establecido.


12. Componente acai-vue-selectv2 (reemplazo de acai-vue-list)

12.1 Descripción general

acai-vue-selectv2 reemplaza completamente a acai-vue-list. Es un componente inteligente que detecta automáticamente cómo renderizar según el número y tipo de opciones:

  • 2 opciones → modo toggle (pill deslizante con animación)
  • 2+ opciones con nombres de color → modo color selector (dropdown con swatches)
  • 3+ opciones normales → modo select (dropdown estándar con vue-select)

12.2 Props

<acai-vue-selectv2
    :builder="builder"
    :data="data"
    :field="'nombrecampo'"
    :toggle-icons="iconosObjeto"  <!-- opcional, solo para toggles con iconos -->
    @save-data="saveData">
</acai-vue-selectv2>

12.3 Toggle con iconos (:toggle-icons)

Para campos de 2 opciones donde se quieran iconos visuales en el toggle, se pasa un objeto con las claves correspondientes a los valores de las opciones:

iconosNombreCampo: {
    '': '<svg>...</svg>',   // icono para la primera opción (valor vacío)
    '1': '<svg>...</svg>'   // icono para la segunda opción
}

Iconos de toggle establecidos:

  • Lado texto (2 opciones: Izquierda/Derecha): icon-tabler-align-box-left-middle / icon-tabler-align-box-right-middle
  • Ver sombra (No/Si): icon-tabler-x / icon-tabler-check
  • Tipo imagen (Imagen/Video): icon-tabler-photo / icon-tabler-video
  • Tipo overlay (Sin degradado/Con degradado): icon-tabler-square / icon-tabler-gradient

12.4 Modo color automático

El componente detecta automáticamente si las opciones son colores cuando al menos la mitad de las labels coinciden con:

  • Nombres del mapa interno: main color, blanco, negro, gris, gris claro, gris oscuro, gris calido, rojo, azul, verde, etc. (español e inglés)
  • Códigos hex (#fff, #ff0000)
  • Valores rgb/rgba
  • Valores hsl/hsla

Los colores main color, main color light y main color dark se resuelven consultando la configuración del CMS en tiempo real.

12.5 Campos de 3+ opciones sin iconos

No necesitan :toggle-icons. Se renderizan como dropdown estándar:

<acai-vue-selectv2 :builder="builder" :data="data" :field="'container'" @save-data="saveData"></acai-vue-selectv2>

13. Componente acai-vue-datepicker (campos de fecha)

13.1 Uso

Cuando un campo textfield en el HTML se usa para fechas (se identifica por el label "Fecha" o similar), se usa acai-vue-datepicker junto con un acai-vue-textfield oculto:

<div class="relative mt-2">
    <acai-vue-datepicker :builder="builder" :data="data" :field="'fecha'" :label="'Fecha'" @save-data="saveData"></acai-vue-datepicker>
</div>
<div style="display: none">
    <acai-vue-textfield :builder="builder" :data="data" :field="'fecha'" @save-data="saveData"></acai-vue-textfield>
</div>

13.2 Notas estándar para datepicker

<p class="text-xs leading-snug text-gray-500 mt-2 ml-14"><b class="text-gray-700">Nota :</b> puedes elegir el formato de la fecha en el selector.</p>
<p class="text-xs leading-snug text-gray-500 mt-1 ml-14"><b class="text-gray-700">Recuerda :</b> también puedes mostrar la hora activando el botón del reloj.</p>

13.3 Icono estándar para fecha

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" :style="{ color: color }" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-10 h-10 flex-shrink-0 mr-4 icon icon-tabler icons-tabler-outline icon-tabler-calendar-week"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12" /><path d="M16 3v4" /><path d="M8 3v4" /><path d="M4 11h16" /><path d="M7 14h.013" /><path d="M10.01 14h.005" /><path d="M13.01 14h.005" /><path d="M16.015 14h.005" /><path d="M13.015 17h.005" /><path d="M7.01 17h.005" /><path d="M10.01 17h.005" /></svg>

14. Reglas de spacing (separación entre elementos)

14.1 Separación entre nota/recuerda y componente

  • El componente siempre lleva mt-2 respecto a la nota o recuerda que lo precede.
  • Nunca mt-1 entre nota/recuerda y componente.

14.2 Nota y Recuerda juntos

Cuando un campo tiene Nota y Recuerda:

  • Nota siempre lleva mt-2 respecto al bloque de icono+texto anterior.
  • Recuerda lleva mt-1 respecto a la Nota (va justo debajo).
  • El componente lleva mt-2 respecto al Recuerda.

Ejemplo:

<p class="text-xs leading-snug text-gray-500 mt-2 ml-14"><b class="text-gray-700">Nota :</b> texto de la nota.</p>
<p class="text-xs leading-snug text-gray-500 mt-1 ml-14"><b class="text-gray-700">Recuerda :</b> texto del recuerda.</p>
<div class="relative mt-2 ml-14">
    <!-- componente -->
</div>

14.3 Solo Nota (sin Recuerda)

<p class="text-xs leading-snug text-gray-500 mt-2 ml-14"><b class="text-gray-700">Nota :</b> texto.</p>
<div class="relative mt-2 ml-14">
    <!-- componente -->
</div>

14.4 Solo Recuerda (sin Nota)

El Recuerda usa el estilo especial (sin ml-14, con font-light):

<p class="text-xs leading-snug text-gray-600 font-light mt-2"><b class="text-black">Recuerda :</b> texto del recuerda.</p>
<div class="relative mt-2 ml-14">
    <!-- componente -->
</div>

14.5 Sin Nota ni Recuerda

El componente lleva mt-2 directamente:

<div class="relative mt-2 ml-14">
    <!-- componente -->
</div>

14.6 Separación entre campos

Siempre mt-6 entre bloques de campo:

<div class="flex w-full items-center mt-6">

El primer campo de cada tab NO lleva mt-6 (no hay campo previo).