- 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>
444 lines
12 KiB
Markdown
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)
|