getData(); if (!$this->isEnabled($data)) { return ''; } if (!PageVisibilityHelper::shouldShow(self::COMPONENT_NAME)) { return ''; } $html = $this->buildHTML($data); // Si is_critical=true, CSS ya fue inyectado en por CriticalCSSService $isCritical = $data['visibility']['is_critical'] ?? false; if ($isCritical) { return $html; // Solo HTML, sin CSS inline } // CSS inline para componentes no críticos $css = $this->generateCSS($data); return sprintf("\n%s", $css, $html); } public function supports(string $componentType): bool { return $componentType === self::COMPONENT_NAME; } private function isEnabled(array $data): bool { return ($data['visibility']['is_enabled'] ?? false) === true; } /** * Generar CSS usando CSSGeneratorService * * Este método es público para que CriticalCSSService pueda * generar CSS crítico antes de wp_head sin duplicar lógica. * * @param array $data Datos del componente * @return string CSS generado */ public function generateCSS(array $data): string { $colors = $data['colors'] ?? []; $typography = $data['typography'] ?? []; $spacing = $data['spacing'] ?? []; $effects = $data['visual_effects'] ?? []; $visibility = $data['visibility'] ?? []; $gradientStart = $colors['gradient_start'] ?? '#1e3a5f'; $gradientEnd = $colors['gradient_end'] ?? '#2c5282'; $titleColor = $colors['title_color'] ?? '#FFFFFF'; $badgeBgColor = $colors['badge_bg_color'] ?? '#FFFFFF'; $badgeTextColor = $colors['badge_text_color'] ?? '#FFFFFF'; $badgeIconColor = $colors['badge_icon_color'] ?? '#FFB800'; $badgeHoverBg = $colors['badge_hover_bg'] ?? '#FF8600'; $titleFontSize = $typography['title_font_size'] ?? '2.5rem'; $titleFontSizeMobile = $typography['title_font_size_mobile'] ?? '1.75rem'; $titleFontWeight = $typography['title_font_weight'] ?? '700'; $titleLineHeight = $typography['title_line_height'] ?? '1.4'; $badgeFontSize = $typography['badge_font_size'] ?? '0.813rem'; $paddingVertical = $spacing['padding_vertical'] ?? '3rem'; $marginBottom = $spacing['margin_bottom'] ?? '1.5rem'; $badgePadding = $spacing['badge_padding'] ?? '0.375rem 0.875rem'; $badgeBorderRadius = $spacing['badge_border_radius'] ?? '20px'; $minHeight = $spacing['min_height'] ?? '120px'; $titleMinHeight = $spacing['title_min_height'] ?? '3rem'; $badgeMinHeight = $spacing['badge_min_height'] ?? '32px'; $boxShadow = $effects['box_shadow'] ?? '0 4px 16px rgba(30, 58, 95, 0.25)'; $titleTextShadow = $effects['title_text_shadow'] ?? '1px 1px 2px rgba(0, 0, 0, 0.2)'; $badgeBackdropBlur = $effects['badge_backdrop_blur'] ?? '10px'; $showOnDesktop = $visibility['show_on_desktop'] ?? true; $showOnMobile = $visibility['show_on_mobile'] ?? true; $cssRules = []; $cssRules[] = $this->cssGenerator->generate('.hero-section', [ 'background' => "linear-gradient(135deg, {$gradientStart} 0%, {$gradientEnd} 100%)", 'box-shadow' => $boxShadow, 'padding' => "{$paddingVertical} 0", 'margin-bottom' => $marginBottom, 'min-height' => $minHeight, ]); $cssRules[] = $this->cssGenerator->generate('.hero-section__title', [ 'color' => "{$titleColor} !important", 'font-weight' => $titleFontWeight, 'font-size' => $titleFontSize, 'line-height' => $titleLineHeight, 'text-shadow' => $titleTextShadow, 'margin-bottom' => '0', 'text-align' => 'center', 'min-height' => $titleMinHeight, ]); // Contenedor de badges - altura minima para prevenir CLS $cssRules[] = $this->cssGenerator->generate('.hero-section .mb-3.d-flex', [ 'min-height' => '40px', ]); $cssRules[] = $this->cssGenerator->generate('.hero-section__badge', [ 'background' => $this->hexToRgba($badgeBgColor, 0.15), 'backdrop-filter' => "blur({$badgeBackdropBlur})", '-webkit-backdrop-filter' => "blur({$badgeBackdropBlur})", 'border' => '1px solid ' . $this->hexToRgba($badgeBgColor, 0.2), 'color' => $this->hexToRgba($badgeTextColor, 0.95), 'padding' => $badgePadding, 'border-radius' => $badgeBorderRadius, 'font-size' => $badgeFontSize, 'font-weight' => '500', 'text-decoration' => 'none', 'display' => 'inline-block', 'transition' => 'all 0.3s ease', 'min-height' => $badgeMinHeight, ]); $cssRules[] = $this->cssGenerator->generate('.hero-section__badge:hover', [ 'background' => $this->hexToRgba($badgeHoverBg, 0.2), 'border-color' => $this->hexToRgba($badgeHoverBg, 0.4), 'color' => '#ffffff', ]); $cssRules[] = $this->cssGenerator->generate('.hero-section__badge i', [ 'color' => $badgeIconColor, ]); $cssRules[] = "@media (max-width: 767.98px) { .hero-section__title { font-size: {$titleFontSizeMobile}; } }"; if (!$showOnMobile) { $cssRules[] = "@media (max-width: 767.98px) { .hero-section { display: none !important; } }"; } if (!$showOnDesktop) { $cssRules[] = "@media (min-width: 768px) { .hero-section { display: none !important; } }"; } return implode("\n", $cssRules); } private function buildHTML(array $data): string { $content = $data['content'] ?? []; $showCategories = $content['show_categories'] ?? true; $showBadgeIcon = $content['show_badge_icon'] ?? true; $badgeIconClass = $content['badge_icon_class'] ?? 'bi-folder-fill'; $titleTag = $content['title_tag'] ?? 'h1'; $allowedTags = ['h1', 'h2', 'div']; if (!in_array($titleTag, $allowedTags, true)) { $titleTag = 'h1'; } $title = is_singular() ? get_the_title() : ''; if (empty($title)) { $title = wp_title('', false); } $html = '
'; $html .= '
'; if ($showCategories && is_single()) { $categories = get_the_category(); if (!empty($categories)) { $html .= '
'; $html .= '
'; foreach ($categories as $category) { $categoryLink = esc_url(get_category_link($category->term_id)); $categoryName = esc_html($category->name); $iconHtml = $showBadgeIcon ? '' : ''; $html .= sprintf( '%s%s', $categoryLink, $iconHtml, $categoryName ); } $html .= '
'; $html .= '
'; } } $html .= sprintf( '<%s class="hero-section__title">%s', $titleTag, esc_html($title), $titleTag ); $html .= '
'; $html .= '
'; return $html; } private function hexToRgba(string $hex, float $alpha): string { $hex = ltrim($hex, '#'); if (strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); return "rgba({$r}, {$g}, {$b}, {$alpha})"; } /** * Genera clases Bootstrap de visibilidad responsive * * @param bool $desktop Si debe mostrarse en desktop * @param bool $mobile Si debe mostrarse en mobile * @return string|null Clases Bootstrap o null si visible en todos */ private function getVisibilityClasses(bool $desktop, bool $mobile): ?string { if ($desktop && $mobile) { return null; } if ($desktop && !$mobile) { return 'd-none d-md-block'; } if (!$desktop && $mobile) { return 'd-block d-md-none'; } return 'd-none'; } }