Files
roi-theme/_planificacion/01-design-system/08-PATRONES-FORMULARIOS.md
FrankZamora 0f6387ab46 refactor: reorganizar openspec y planificacion con spec recaptcha
- renombrar openspec/ a _openspec/ (carpeta auxiliar)
- mover specs de features a changes/
- crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura
- migrar _planificacion/ con design-system y roi-theme-template
- agregar especificacion recaptcha anti-spam (proposal, tasks, spec)
- corregir rutas y referencias en todas las specs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:30:45 -06:00

444 lines
12 KiB
Markdown

# 📝 PATRONES DE FORMULARIOS
## 1. Form Switch (Checkbox Toggle)
```html
<div class="mb-2">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="enabled" checked>
<label class="form-check-label small" for="enabled" style="color: #495057;">
<i class="bi bi-toggle-on me-1" style="color: #FF8600;"></i>
<strong>Activar Top Bar</strong>
<span class="text-muted">(Mostrar en el sitio)</span>
</label>
</div>
</div>
```
**Uso:** Activación/desactivación de features
**CSS necesario (Fix WordPress):**
```css
/* Eliminar pseudo-elementos de WordPress */
body .form-switch .form-check-input[type="checkbox"]::before,
body .form-switch .form-check-input[type="checkbox"]::after {
content: none !important;
display: none !important;
}
body .form-switch .form-check-input[type="checkbox"] {
background-size: contain !important;
background-repeat: no-repeat !important;
background-position: left center !important;
}
body .form-switch .form-check-input[type="checkbox"]:checked {
background-position: right center !important;
}
/* Fix alineación vertical */
.form-check.form-switch {
display: flex !important;
align-items: center !important;
}
.form-switch .form-check-input {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.form-switch .form-check-label {
line-height: 16px !important;
padding-top: 0 !important;
margin-top: 0 !important;
}
```
---
## 2. Color Picker
```html
<div class="col-6">
<label for="bgColor" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-paint-bucket me-1" style="color: #FF8600;"></i>
Color de Fondo
</label>
<input type="color" id="bgColor"
class="form-control form-control-color w-100"
value="#0E2337"
title="Seleccionar color de fondo">
<small class="text-muted d-block mt-1" id="bgColorValue">#0E2337</small>
</div>
```
**Características:**
- Grid de 2 columnas (`col-6`)
- Display del valor hex debajo
- Width 100% (`.w-100`)
**JavaScript:**
```javascript
const colorInput = document.getElementById('bgColor');
const colorValue = document.getElementById('bgColorValue');
colorInput.addEventListener('input', function() {
colorValue.textContent = this.value.toUpperCase();
updatePreview();
});
```
---
## 3. Text Input con Icono
```html
<div class="mb-2">
<label for="highlightText" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-chat-text me-1" style="color: #FF8600;"></i>
Texto Destacado
<span class="text-danger">*</span> <!-- Si es requerido -->
</label>
<input type="text" id="highlightText"
class="form-control form-control-sm"
placeholder="Ej: Nuevo:"
value="Nuevo:"
maxlength="50">
</div>
```
**Uso:** Campos de texto cortos (nombres, títulos, etc.)
---
## 4. Textarea con Contador y Progress Bar
```html
<div class="mb-2">
<label for="messageText" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-chat-left-text me-1" style="color: #FF8600;"></i>
Mensaje Principal <span class="text-danger">*</span>
<span class="float-end text-muted">
<span id="messageTextCount" class="fw-bold">0</span>/250
</span>
</label>
<textarea id="messageText"
class="form-control form-control-sm"
rows="2"
maxlength="250"
placeholder="Ej: Accede a más de 200,000 APUs actualizados"
required></textarea>
<div class="progress mt-1" style="height: 3px;">
<div id="messageTextProgress"
class="progress-bar"
role="progressbar"
style="width: 0%; background-color: #FF8600;"
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="250"></div>
</div>
</div>
```
**JavaScript:**
```javascript
const textarea = document.getElementById('messageText');
const counter = document.getElementById('messageTextCount');
const progress = document.getElementById('messageTextProgress');
textarea.addEventListener('input', function() {
const length = this.value.length;
const maxLength = 250;
const percentage = (length / maxLength) * 100;
counter.textContent = length;
progress.style.width = percentage + '%';
progress.setAttribute('aria-valuenow', length);
// Cambiar color según el uso
if (percentage > 90) {
progress.style.backgroundColor = '#dc3545'; // Rojo
} else if (percentage > 75) {
progress.style.backgroundColor = '#ffc107'; // Amarillo
} else {
progress.style.backgroundColor = '#FF8600'; // Orange
}
updatePreview();
});
```
---
## 5. Select Dropdown
```html
<div class="mb-2">
<label for="fontSize" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-fonts me-1" style="color: #FF8600;"></i>
Tamaño de Fuente
</label>
<select id="fontSize" class="form-select form-select-sm">
<option value="small">Pequeño (0.875rem)</option>
<option value="normal" selected>Normal (1rem)</option>
<option value="large">Grande (1.125rem)</option>
</select>
</div>
```
**Uso:** Opciones predefinidas (tamaños, estilos, etc.)
---
## 6. Badges Informativos
### Badge en Label
```html
<label class="form-label small mb-1 fw-semibold">
<i class="bi bi-code me-1" style="color: #FF8600;"></i>
Clase del Icono
<span class="badge bg-secondary" style="font-size: 0.65rem;">Bootstrap Icons</span>
</label>
```
### Badge Standalone
```html
<!-- Opcional -->
<span class="badge text-dark" style="background-color: #FFB800; font-size: 0.65rem;">
Opcional
</span>
<!-- Info -->
<span class="badge bg-secondary" style="font-size: 0.65rem;">
Info
</span>
<!-- Requerido -->
<span class="badge bg-danger" style="font-size: 0.65rem;">
Requerido
</span>
```
---
## 7. Links de Ayuda
```html
<small class="text-muted d-block mt-1">
<i class="bi bi-info-circle me-1"></i>
Ver: <a href="https://icons.getbootstrap.com/" target="_blank"
class="text-decoration-none" style="color: #FF8600;">
Bootstrap Icons <i class="bi bi-box-arrow-up-right"></i>
</a>
</small>
```
**Características:**
- Icono de info
- Link orange con hover
- Icono de "abrir en nueva ventana"
- `target="_blank"` para abrir en pestaña nueva
---
## 8. Grid de Inputs Compactos
### 2 Columnas Iguales
```html
<div class="row g-2 mb-2">
<div class="col-6">
<label for="bgColor" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-paint-bucket me-1" style="color: #FF8600;"></i>
Color Fondo
</label>
<input type="color" id="bgColor"
class="form-control form-control-color w-100"
value="#0E2337">
<small class="text-muted d-block mt-1" id="bgColorValue">#0E2337</small>
</div>
<div class="col-6">
<label for="textColor" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-fonts me-1" style="color: #FF8600;"></i>
Color Texto
</label>
<input type="color" id="textColor"
class="form-control form-control-color w-100"
value="#ffffff">
<small class="text-muted d-block mt-1" id="textColorValue">#FFFFFF</small>
</div>
</div>
```
### 3 Columnas Desiguales
```html
<div class="row g-2 mb-2">
<div class="col-5">
<label class="form-label small mb-1 fw-semibold">Campo 1</label>
<input type="text" class="form-control form-control-sm">
</div>
<div class="col-5">
<label class="form-label small mb-1 fw-semibold">Campo 2</label>
<input type="text" class="form-control form-control-sm">
</div>
<div class="col-2">
<label class="form-label small mb-1 fw-semibold">Icono</label>
<input type="text" class="form-control form-control-sm">
</div>
</div>
```
---
## 9. Campo de URL
```html
<div class="mb-2">
<label for="linkUrl" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-link-45deg me-1" style="color: #FF8600;"></i>
URL del Enlace
</label>
<input type="url" id="linkUrl"
class="form-control form-control-sm"
placeholder="https://ejemplo.com"
value="/catalogo">
<small class="text-muted d-block mt-1">
<i class="bi bi-info-circle me-1"></i>
Usa rutas relativas (/) o absolutas (https://)
</small>
</div>
```
---
## 10. Campos Relacionados (Link)
```html
<!-- Switch para mostrar/ocultar link -->
<div class="mb-2">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="showLink" checked>
<label class="form-check-label small" for="showLink" style="color: #495057;">
<i class="bi bi-link me-1" style="color: #FF8600;"></i>
<strong>Mostrar Enlace</strong>
<span class="text-muted">(Call-to-action)</span>
</label>
</div>
</div>
<!-- Campos del link (se muestran/ocultan según el switch) -->
<div id="linkFields">
<!-- Texto del link -->
<div class="mb-2">
<label for="linkText" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-chat-text me-1" style="color: #FF8600;"></i>
Texto del Enlace
</label>
<input type="text" id="linkText"
class="form-control form-control-sm"
placeholder="Ej: Ver Catálogo →"
value="Ver Catálogo →"
maxlength="50">
</div>
<!-- URL del link -->
<div class="mb-2">
<label for="linkUrl" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-link-45deg me-1" style="color: #FF8600;"></i>
URL del Enlace
</label>
<input type="url" id="linkUrl"
class="form-control form-control-sm"
placeholder="https://ejemplo.com"
value="/catalogo">
</div>
<!-- Target del link -->
<div class="mb-2">
<label for="linkTarget" class="form-label small mb-1 fw-semibold" style="color: #495057;">
<i class="bi bi-box-arrow-up-right me-1" style="color: #FF8600;"></i>
Abrir En
</label>
<select id="linkTarget" class="form-select form-select-sm">
<option value="_self" selected>Misma ventana (_self)</option>
<option value="_blank">Nueva pestaña (_blank)</option>
</select>
</div>
</div>
```
**JavaScript para mostrar/ocultar:**
```javascript
const showLink = document.getElementById('showLink');
const linkFields = document.getElementById('linkFields');
showLink.addEventListener('change', function() {
linkFields.style.display = this.checked ? 'block' : 'none';
updatePreview();
});
```
---
## 11. Validación de Campos
```javascript
/**
* Valida los campos del formulario
*/
function validateForm() {
let isValid = true;
const errors = [];
// Validar campo requerido
const messageText = document.getElementById('messageText').value.trim();
if (!messageText) {
errors.push('El mensaje principal es requerido');
isValid = false;
}
// Validar longitud
if (messageText.length > 250) {
errors.push('El mensaje no puede exceder 250 caracteres');
isValid = false;
}
// Validar URL
const linkUrl = document.getElementById('linkUrl').value.trim();
if (linkUrl && !isValidUrl(linkUrl)) {
errors.push('La URL no es válida');
isValid = false;
}
if (!isValid) {
alert('⚠️ Errores de validación:\n\n' + errors.join('\n'));
}
return isValid;
}
function isValidUrl(string) {
// Permitir rutas relativas
if (string.startsWith('/')) {
return true;
}
// Validar URLs absolutas
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}
```
---
## Volver al Índice
[← Volver al README](README.md)