- Nuevo grupo 'Layout y Contenedor' en theme-settings - Opciones: 1140px, 1200px, 1320px (default), 1400px, 100% - CSS dinamico aplicado via ThemeSettingsRenderer - Corregir selectores de hero en Rail Ads (.hero-section, .featured-image-container) - Agregar setTimeout para esperar carga de DOM - Aumentar gap de separacion a 30px - Subir maxTop al 40% del viewport 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
253 lines
9.9 KiB
PHP
253 lines
9.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Admin\ThemeSettings\Infrastructure\Ui;
|
|
|
|
use ROITheme\Admin\Infrastructure\Ui\AdminDashboardRenderer;
|
|
|
|
/**
|
|
* FormBuilder para Theme Settings
|
|
*
|
|
* RESPONSABILIDAD: Generar formulario de configuraciones globales del tema
|
|
* (codigo personalizado - CSS y JavaScript)
|
|
*
|
|
* NOTA: Analytics y AdSense se gestionan desde el componente adsense-placement
|
|
*
|
|
* @package ROITheme\Admin\ThemeSettings\Infrastructure\Ui
|
|
*/
|
|
final class ThemeSettingsFormBuilder
|
|
{
|
|
public function __construct(
|
|
private AdminDashboardRenderer $renderer
|
|
) {}
|
|
|
|
public function buildForm(string $componentId): string
|
|
{
|
|
$html = '';
|
|
|
|
$html .= $this->buildHeader($componentId);
|
|
|
|
// Layout Group (nueva seccion)
|
|
$html .= $this->buildLayoutGroup($componentId);
|
|
|
|
$html .= '<div class="row g-3">';
|
|
|
|
// Columna izquierda - CSS
|
|
$html .= '<div class="col-lg-6">';
|
|
$html .= $this->buildCssGroup($componentId);
|
|
$html .= '</div>';
|
|
|
|
// Columna derecha - JavaScript
|
|
$html .= '<div class="col-lg-6">';
|
|
$html .= $this->buildJsGroup($componentId);
|
|
$html .= '</div>';
|
|
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildLayoutGroup(string $componentId): string
|
|
{
|
|
$html = '<div class="card shadow-sm mb-4" style="border-left: 4px solid #FF8600;">';
|
|
$html .= ' <div class="card-body">';
|
|
$html .= ' <h5 class="fw-bold mb-3" style="color: #1e3a5f;">';
|
|
$html .= ' <i class="bi bi-layout-wtf me-2" style="color: #FF8600;"></i>';
|
|
$html .= ' Layout y Contenedor';
|
|
$html .= ' </h5>';
|
|
|
|
$html .= ' <div class="row g-3">';
|
|
|
|
// Container Max Width
|
|
$html .= ' <div class="col-md-6">';
|
|
$containerWidth = $this->renderer->getFieldValue($componentId, 'layout', 'container_max_width', '1320');
|
|
$containerWidthStr = is_string($containerWidth) ? $containerWidth : '1320';
|
|
$html .= $this->buildSelect(
|
|
'themeSettingsContainerMaxWidth',
|
|
'Ancho maximo del contenedor',
|
|
$containerWidthStr,
|
|
[
|
|
'1140' => '1140px (Bootstrap md)',
|
|
'1200' => '1200px (Compacto)',
|
|
'1320' => '1320px (Bootstrap xxl - Default)',
|
|
'1400' => '1400px (Amplio)',
|
|
'100%' => '100% (Fluido)'
|
|
],
|
|
'Valores menores dejan mas espacio para Rail Ads laterales'
|
|
);
|
|
$html .= ' </div>';
|
|
|
|
// Content Column Width
|
|
$html .= ' <div class="col-md-6">';
|
|
$columnWidth = $this->renderer->getFieldValue($componentId, 'layout', 'content_column_width', 'col-lg-9');
|
|
$columnWidthStr = is_string($columnWidth) ? $columnWidth : 'col-lg-9';
|
|
$html .= $this->buildSelect(
|
|
'themeSettingsContentColumnWidth',
|
|
'Ancho columna de contenido',
|
|
$columnWidthStr,
|
|
[
|
|
'col-lg-8' => '8 columnas (66.67%)',
|
|
'col-lg-9' => '9 columnas (75% - Default)',
|
|
'col-lg-10' => '10 columnas (83.33%)',
|
|
'col-lg-12' => '12 columnas (100% sin sidebar)'
|
|
],
|
|
'Proporcion de la columna principal vs sidebar'
|
|
);
|
|
$html .= ' </div>';
|
|
|
|
$html .= ' </div>';
|
|
|
|
$html .= ' <div class="alert alert-info small mb-0 mt-3">';
|
|
$html .= ' <i class="bi bi-info-circle me-1"></i>';
|
|
$html .= ' Reduce el ancho del contenedor para dar mas espacio a los Rail Ads en pantallas grandes.';
|
|
$html .= ' </div>';
|
|
|
|
$html .= ' </div>';
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildSelect(string $id, string $label, string $value, array $options, string $helpText = ''): string
|
|
{
|
|
$html = ' <div class="mb-3">';
|
|
$html .= ' <label for="' . esc_attr($id) . '" class="form-label small mb-1 fw-semibold">';
|
|
$html .= ' ' . esc_html($label);
|
|
$html .= ' </label>';
|
|
$html .= ' <select class="form-select form-select-sm" id="' . esc_attr($id) . '">';
|
|
foreach ($options as $optionValue => $optionLabel) {
|
|
$selected = ($value === (string) $optionValue) ? ' selected' : '';
|
|
$html .= ' <option value="' . esc_attr($optionValue) . '"' . $selected . '>' . esc_html($optionLabel) . '</option>';
|
|
}
|
|
$html .= ' </select>';
|
|
if (!empty($helpText)) {
|
|
$html .= ' <div class="form-text small">' . esc_html($helpText) . '</div>';
|
|
}
|
|
$html .= ' </div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildHeader(string $componentId): string
|
|
{
|
|
$html = '<div class="rounded p-4 mb-4 shadow text-white" ';
|
|
$html .= 'style="background: linear-gradient(135deg, #0E2337 0%, #1e3a5f 100%); border-left: 4px solid #FF8600;">';
|
|
$html .= ' <div class="d-flex align-items-center justify-content-between flex-wrap gap-3">';
|
|
$html .= ' <div>';
|
|
$html .= ' <h3 class="h4 mb-1 fw-bold">';
|
|
$html .= ' <i class="bi bi-gear me-2" style="color: #FF8600;"></i>';
|
|
$html .= ' Configuraciones Globales del Tema';
|
|
$html .= ' </h3>';
|
|
$html .= ' <p class="mb-0 small" style="opacity: 0.85;">';
|
|
$html .= ' Codigo Personalizado (CSS y JavaScript)';
|
|
$html .= ' </p>';
|
|
$html .= ' </div>';
|
|
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="theme-settings">';
|
|
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
|
|
$html .= ' Restaurar valores por defecto';
|
|
$html .= ' </button>';
|
|
$html .= ' </div>';
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildCssGroup(string $componentId): string
|
|
{
|
|
$html = '<div class="card shadow-sm mb-3" style="border-left: 4px solid #1e3a5f;">';
|
|
$html .= ' <div class="card-body">';
|
|
$html .= ' <h5 class="fw-bold mb-3" style="color: #1e3a5f;">';
|
|
$html .= ' <i class="bi bi-filetype-css me-2" style="color: #FF8600;"></i>';
|
|
$html .= ' CSS Personalizado';
|
|
$html .= ' </h5>';
|
|
|
|
$customCss = $this->renderer->getFieldValue($componentId, 'custom_code', 'custom_css', '');
|
|
$html .= $this->buildTextareaCode(
|
|
'themeSettingsCustomCss',
|
|
'Estilos CSS',
|
|
$customCss,
|
|
'Se inyecta en wp_head. No incluir etiquetas <style>',
|
|
10
|
|
);
|
|
|
|
$html .= ' <div class="alert alert-info small mb-0 mt-2">';
|
|
$html .= ' <i class="bi bi-info-circle me-1"></i>';
|
|
$html .= ' El CSS se carga despues de los estilos del tema.';
|
|
$html .= ' </div>';
|
|
|
|
$html .= ' </div>';
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildJsGroup(string $componentId): string
|
|
{
|
|
$html = '<div class="card shadow-sm mb-3" style="border-left: 4px solid #1e3a5f;">';
|
|
$html .= ' <div class="card-body">';
|
|
$html .= ' <h5 class="fw-bold mb-3" style="color: #1e3a5f;">';
|
|
$html .= ' <i class="bi bi-filetype-js me-2" style="color: #FF8600;"></i>';
|
|
$html .= ' JavaScript Personalizado';
|
|
$html .= ' </h5>';
|
|
|
|
$customJsHeader = $this->renderer->getFieldValue($componentId, 'custom_code', 'custom_js_header', '');
|
|
$html .= $this->buildTextareaCode(
|
|
'themeSettingsCustomJsHeader',
|
|
'JavaScript en Header',
|
|
$customJsHeader,
|
|
'Se inyecta en wp_head. No incluir etiquetas <script>',
|
|
5
|
|
);
|
|
|
|
$customJsFooter = $this->renderer->getFieldValue($componentId, 'custom_code', 'custom_js_footer', '');
|
|
$html .= $this->buildTextareaCode(
|
|
'themeSettingsCustomJsFooter',
|
|
'JavaScript en Footer',
|
|
$customJsFooter,
|
|
'Se inyecta en wp_footer. No incluir etiquetas <script>',
|
|
5
|
|
);
|
|
|
|
$html .= ' <div class="alert alert-danger small mb-0 mt-2">';
|
|
$html .= ' <i class="bi bi-exclamation-octagon me-1"></i>';
|
|
$html .= ' <strong>Advertencia:</strong> El codigo JS puede afectar el rendimiento y seguridad del sitio.';
|
|
$html .= ' </div>';
|
|
|
|
$html .= ' </div>';
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildTextareaCode(string $id, string $label, mixed $value, string $helpText = '', int $rows = 4): string
|
|
{
|
|
$value = $this->normalizeStringValue($value);
|
|
|
|
$html = ' <div class="mb-3">';
|
|
$html .= ' <label for="' . esc_attr($id) . '" class="form-label small mb-1 fw-semibold">';
|
|
$html .= ' ' . esc_html($label);
|
|
$html .= ' </label>';
|
|
$html .= ' <textarea class="form-control form-control-sm font-monospace" id="' . esc_attr($id) . '" rows="' . $rows . '" style="font-size: 0.85em;">' . esc_textarea($value) . '</textarea>';
|
|
if (!empty($helpText)) {
|
|
$html .= ' <div class="form-text small">' . $helpText . '</div>';
|
|
}
|
|
$html .= ' </div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Normaliza un valor a string para inputs de formulario
|
|
*/
|
|
private function normalizeStringValue(mixed $value): string
|
|
{
|
|
if ($value === false) {
|
|
return '0';
|
|
}
|
|
if ($value === true) {
|
|
return '1';
|
|
}
|
|
return (string) $value;
|
|
}
|
|
}
|