diff --git a/Admin/ThemeSettings/Infrastructure/FieldMapping/ThemeSettingsFieldMapper.php b/Admin/ThemeSettings/Infrastructure/FieldMapping/ThemeSettingsFieldMapper.php index 88e56c20..0d87981c 100644 --- a/Admin/ThemeSettings/Infrastructure/FieldMapping/ThemeSettingsFieldMapper.php +++ b/Admin/ThemeSettings/Infrastructure/FieldMapping/ThemeSettingsFieldMapper.php @@ -24,6 +24,10 @@ final class ThemeSettingsFieldMapper implements FieldMapperInterface public function getFieldMapping(): array { return [ + // Layout + 'themeSettingsContainerMaxWidth' => ['group' => 'layout', 'attribute' => 'container_max_width'], + 'themeSettingsContentColumnWidth' => ['group' => 'layout', 'attribute' => 'content_column_width'], + // Custom Code 'themeSettingsCustomCss' => ['group' => 'custom_code', 'attribute' => 'custom_css'], 'themeSettingsCustomJsHeader' => ['group' => 'custom_code', 'attribute' => 'custom_js_header'], diff --git a/Admin/ThemeSettings/Infrastructure/Ui/ThemeSettingsFormBuilder.php b/Admin/ThemeSettings/Infrastructure/Ui/ThemeSettingsFormBuilder.php index 77472a73..61724e03 100644 --- a/Admin/ThemeSettings/Infrastructure/Ui/ThemeSettingsFormBuilder.php +++ b/Admin/ThemeSettings/Infrastructure/Ui/ThemeSettingsFormBuilder.php @@ -27,6 +27,9 @@ final class ThemeSettingsFormBuilder $html .= $this->buildHeader($componentId); + // Layout Group (nueva seccion) + $html .= $this->buildLayoutGroup($componentId); + $html .= '
'; // Columna izquierda - CSS @@ -44,6 +47,87 @@ final class ThemeSettingsFormBuilder return $html; } + private function buildLayoutGroup(string $componentId): string + { + $html = '
'; + $html .= '
'; + $html .= '
'; + $html .= ' '; + $html .= ' Layout y Contenedor'; + $html .= '
'; + + $html .= '
'; + + // Container Max Width + $html .= '
'; + $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 .= '
'; + + // Content Column Width + $html .= '
'; + $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 .= '
'; + + $html .= '
'; + + $html .= '
'; + $html .= ' '; + $html .= ' Reduce el ancho del contenedor para dar mas espacio a los Rail Ads en pantallas grandes.'; + $html .= '
'; + + $html .= '
'; + $html .= '
'; + + return $html; + } + + private function buildSelect(string $id, string $label, string $value, array $options, string $helpText = ''): string + { + $html = '
'; + $html .= ' '; + $html .= ' '; + if (!empty($helpText)) { + $html .= '
' . esc_html($helpText) . '
'; + } + $html .= '
'; + + return $html; + } + private function buildHeader(string $componentId): string { $html = '
navbar height), rails debajo del hero - if (heroRect.bottom > navHeight) { - newTop = heroRect.bottom + gap; + if (heroRect.bottom > 0) { + lowestBottom = Math.max(lowestBottom, heroRect.bottom); } } - // Limitar el top maximo para que no quede muy abajo - var maxTop = window.innerHeight * 0.25; // Max 25% del viewport + if (featuredImage) { + var featuredRect = featuredImage.getBoundingClientRect(); + if (featuredRect.bottom > 0) { + lowestBottom = Math.max(lowestBottom, featuredRect.bottom); + } + } + + // Si algo esta visible en pantalla, posicionar debajo + if (lowestBottom > navHeight) { + newTop = lowestBottom + gap; + } + + // Limitar el top maximo al 40% del viewport (mas generoso) + var maxTop = window.innerHeight * 0.4; newTop = Math.min(newTop, maxTop); // Asegurar minimo respetando navbar @@ -400,7 +413,8 @@ final class AdsensePlacementRenderer if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', adjustRailPosition); } else { - adjustRailPosition(); + // Esperar un poco para que carguen los elementos + setTimeout(adjustRailPosition, 100); } })(); "; diff --git a/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php b/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php index 67d3783b..c9b46ef9 100644 --- a/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php +++ b/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php @@ -50,6 +50,7 @@ final class ThemeSettingsRenderer implements RendererInterface * Genera contenido para wp_head * * Incluye: + * - Layout CSS (container width) * - Custom CSS (si configurado) * - Custom JS Header (si configurado) * @@ -60,6 +61,12 @@ final class ThemeSettingsRenderer implements RendererInterface { $output = ''; + // Layout CSS (container width configurable) + $layoutOutput = $this->renderLayoutCSS($data); + if (!empty($layoutOutput)) { + $output .= $layoutOutput . "\n"; + } + // Custom CSS $cssOutput = $this->renderCustomCSS($data); if (!empty($cssOutput)) { @@ -75,6 +82,47 @@ final class ThemeSettingsRenderer implements RendererInterface return $output; } + /** + * Genera CSS para el layout configurable + * + * @param array $data Datos del componente + * @return string Bloque style o vacio si usa defaults + */ + private function renderLayoutCSS(array $data): string + { + $containerWidth = $data['layout']['container_max_width'] ?? '1320'; + + // Si es el valor default, no generar CSS extra + if ($containerWidth === '1320') { + return ''; + } + + // Validar que el valor sea seguro + $allowedWidths = ['1140', '1200', '1320', '1400', '100%']; + if (!in_array($containerWidth, $allowedWidths, true)) { + return ''; + } + + // Generar el CSS + $widthValue = ($containerWidth === '100%') ? '100%' : $containerWidth . 'px'; + + return sprintf( + ' +', + esc_attr($widthValue) + ); + } + /** * Genera contenido para wp_footer * diff --git a/Schemas/theme-settings.json b/Schemas/theme-settings.json index dbb1da50..b89daa28 100644 --- a/Schemas/theme-settings.json +++ b/Schemas/theme-settings.json @@ -1,8 +1,41 @@ { "component_name": "theme-settings", - "version": "1.3.0", - "description": "Configuraciones globales del tema: codigo personalizado", + "version": "1.4.0", + "description": "Configuraciones globales del tema: layout y codigo personalizado", "groups": { + "layout": { + "label": "Layout y Contenedor", + "priority": 5, + "fields": { + "container_max_width": { + "type": "select", + "label": "Ancho maximo del contenedor principal", + "default": "1320", + "editable": true, + "options": { + "1140": "1140px (Bootstrap md)", + "1200": "1200px (Compacto)", + "1320": "1320px (Bootstrap xxl - Default)", + "1400": "1400px (Amplio)", + "100%": "100% (Fluido)" + }, + "description": "Ancho maximo del .container principal. Valores menores dejan mas espacio para Rail Ads" + }, + "content_column_width": { + "type": "select", + "label": "Ancho columna de contenido", + "default": "col-lg-9", + "editable": true, + "options": { + "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)" + }, + "description": "Proporcion Bootstrap de la columna principal vs sidebar" + } + } + }, "custom_code": { "label": "Codigo Personalizado", "priority": 10,