diff --git a/Public/HeroSection/Infrastructure/Ui/HeroSectionRenderer.php b/Public/HeroSection/Infrastructure/Ui/HeroSectionRenderer.php new file mode 100644 index 00000000..680da217 --- /dev/null +++ b/Public/HeroSection/Infrastructure/Ui/HeroSectionRenderer.php @@ -0,0 +1,484 @@ +getData(); + + if (!$this->isEnabled($data)) { + return ''; + } + + if (!$this->shouldShowOnCurrentPage($data)) { + return ''; + } + + $classes = $this->buildSectionClasses($data); + $styles = $this->buildInlineStyles($data); + + $html = sprintf( + '
', + esc_attr($classes), + $styles ? ' style="' . esc_attr($styles) . '"' : '' + ); + + $html .= '
'; + + // Categories badges + if ($this->shouldShowCategories($data)) { + $html .= $this->buildCategoriesBadges($data); + } + + // Title + $html .= $this->buildTitle($data); + + $html .= '
'; + $html .= '
'; + + // Custom styles + $html .= $this->buildCustomStyles($data); + + return $html; + } + + private function isEnabled(array $data): bool + { + return isset($data['visibility']['is_enabled']) && + $data['visibility']['is_enabled'] === true; + } + + private function shouldShowOnCurrentPage(array $data): bool + { + $showOn = $data['visibility']['show_on_pages'] ?? 'posts'; + + switch ($showOn) { + case 'all': + return true; + + case 'home': + return is_front_page(); + + case 'posts': + return is_single() && get_post_type() === 'post'; + + case 'pages': + return is_page(); + + case 'custom': + $postTypes = $data['visibility']['custom_post_types'] ?? ''; + $allowedTypes = array_map('trim', explode(',', $postTypes)); + return in_array(get_post_type(), $allowedTypes, true); + + default: + return true; + } + } + + private function shouldShowCategories(array $data): bool + { + return isset($data['categories']['show_categories']) && + $data['categories']['show_categories'] === true; + } + + private function buildSectionClasses(array $data): string + { + $classes = ['container-fluid', 'hero-title']; + + $paddingClass = $this->getPaddingClass($data['styles']['padding_vertical'] ?? 'normal'); + $classes[] = $paddingClass; + + $marginClass = $this->getMarginClass($data['styles']['margin_bottom'] ?? 'normal'); + if ($marginClass) { + $classes[] = $marginClass; + } + + return implode(' ', $classes); + } + + private function getPaddingClass(string $padding): string + { + $paddings = [ + 'compact' => 'py-3', + 'normal' => 'py-5', + 'spacious' => 'py-6', + 'extra-spacious' => 'py-7' + ]; + + return $paddings[$padding] ?? 'py-5'; + } + + private function getMarginClass(string $margin): string + { + $margins = [ + 'none' => '', + 'small' => 'mb-2', + 'normal' => 'mb-4', + 'large' => 'mb-5' + ]; + + return $margins[$margin] ?? 'mb-4'; + } + + private function buildInlineStyles(array $data): string + { + $styles = []; + $backgroundType = $data['styles']['background_type'] ?? 'gradient'; + + switch ($backgroundType) { + case 'color': + $bgColor = $data['styles']['background_color'] ?? '#1e3a5f'; + $styles[] = "background-color: {$bgColor}"; + break; + + case 'gradient': + $startColor = $data['styles']['gradient_start_color'] ?? '#1e3a5f'; + $endColor = $data['styles']['gradient_end_color'] ?? '#2c5282'; + $angle = $data['styles']['gradient_angle'] ?? 135; + $styles[] = "background: linear-gradient({$angle}deg, {$startColor}, {$endColor})"; + break; + + case 'image': + $imageUrl = $data['styles']['background_image_url'] ?? ''; + if (!empty($imageUrl)) { + $styles[] = "background-image: url('" . esc_url($imageUrl) . "')"; + $styles[] = "background-size: cover"; + $styles[] = "background-position: center"; + $styles[] = "background-repeat: no-repeat"; + + if (isset($data['styles']['background_overlay']) && $data['styles']['background_overlay']) { + $opacity = ($data['styles']['overlay_opacity'] ?? 60) / 100; + $styles[] = "position: relative"; + } + } + break; + } + + // Text color + if (!empty($data['styles']['text_color'])) { + $styles[] = 'color: ' . $data['styles']['text_color']; + } + + return implode('; ', $styles); + } + + private function buildCategoriesBadges(array $data): string + { + $categories = $this->getCategories($data); + + if (empty($categories)) { + return ''; + } + + $maxCategories = $data['categories']['max_categories'] ?? 5; + $categories = array_slice($categories, 0, $maxCategories); + + $alignment = $data['categories']['categories_alignment'] ?? 'center'; + $alignmentClasses = [ + 'left' => 'justify-content-start', + 'center' => 'justify-content-center', + 'right' => 'justify-content-end' + ]; + $alignmentClass = $alignmentClasses[$alignment] ?? 'justify-content-center'; + + $icon = $data['categories']['category_icon'] ?? 'bi-folder-fill'; + if (strpos($icon, 'bi-') !== 0) { + $icon = 'bi-' . $icon; + } + + $html = sprintf('
', esc_attr($alignmentClass)); + $html .= '
'; + + foreach ($categories as $category) { + $html .= sprintf( + '%s', + esc_url($category['url']), + esc_attr($icon), + esc_html($category['name']) + ); + } + + $html .= '
'; + $html .= '
'; + + return $html; + } + + private function getCategories(array $data): array + { + $source = $data['categories']['categories_source'] ?? 'post_categories'; + + switch ($source) { + case 'post_categories': + return $this->getPostCategories(); + + case 'post_tags': + return $this->getPostTags(); + + case 'custom_taxonomy': + $taxonomy = $data['categories']['custom_taxonomy_name'] ?? ''; + return $this->getCustomTaxonomyTerms($taxonomy); + + case 'custom_list': + $list = $data['categories']['custom_categories_list'] ?? ''; + return $this->parseCustomCategoriesList($list); + + default: + return []; + } + } + + private function getPostCategories(): array + { + $categories = get_the_category(); + if (empty($categories)) { + return []; + } + + $result = []; + foreach ($categories as $category) { + $result[] = [ + 'name' => $category->name, + 'url' => get_category_link($category->term_id) + ]; + } + + return $result; + } + + private function getPostTags(): array + { + $tags = get_the_tags(); + if (empty($tags)) { + return []; + } + + $result = []; + foreach ($tags as $tag) { + $result[] = [ + 'name' => $tag->name, + 'url' => get_tag_link($tag->term_id) + ]; + } + + return $result; + } + + private function getCustomTaxonomyTerms(string $taxonomy): array + { + if (empty($taxonomy)) { + return []; + } + + $terms = get_the_terms(get_the_ID(), $taxonomy); + if (empty($terms) || is_wp_error($terms)) { + return []; + } + + $result = []; + foreach ($terms as $term) { + $result[] = [ + 'name' => $term->name, + 'url' => get_term_link($term) + ]; + } + + return $result; + } + + private function parseCustomCategoriesList(string $list): array + { + if (empty($list)) { + return []; + } + + $lines = explode("\n", $list); + $result = []; + + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) { + continue; + } + + $parts = explode('|', $line); + if (count($parts) >= 2) { + $result[] = [ + 'name' => trim($parts[0]), + 'url' => trim($parts[1]) + ]; + } + } + + return $result; + } + + private function buildTitle(array $data): string + { + $titleText = $this->getTitleText($data); + + if (empty($titleText)) { + return ''; + } + + $titleTag = $data['title']['title_tag'] ?? 'h1'; + $titleClasses = $data['title']['title_classes'] ?? 'display-5 fw-bold'; + $alignment = $data['title']['title_alignment'] ?? 'center'; + + $alignmentClasses = [ + 'left' => 'text-start', + 'center' => 'text-center', + 'right' => 'text-end' + ]; + $alignmentClass = $alignmentClasses[$alignment] ?? 'text-center'; + + $classes = trim($titleClasses . ' ' . $alignmentClass); + + $titleStyle = ''; + if (isset($data['title']['enable_gradient']) && $data['title']['enable_gradient']) { + $titleStyle = $this->buildGradientStyle($data); + $classes .= ' roi-gradient-text'; + } + + return sprintf( + '<%s class="%s"%s>%s', + esc_attr($titleTag), + esc_attr($classes), + $titleStyle ? ' style="' . esc_attr($titleStyle) . '"' : '', + esc_html($titleText), + esc_attr($titleTag) + ); + } + + private function getTitleText(array $data): string + { + $source = $data['title']['title_source'] ?? 'post_title'; + + switch ($source) { + case 'post_title': + return get_the_title(); + + case 'custom_field': + $fieldName = $data['title']['custom_field_name'] ?? ''; + if (!empty($fieldName)) { + $value = get_post_meta(get_the_ID(), $fieldName, true); + return is_string($value) ? $value : ''; + } + return ''; + + case 'custom_text': + return $data['title']['custom_text'] ?? ''; + + default: + return get_the_title(); + } + } + + private function buildGradientStyle(array $data): string + { + $startColor = $data['title']['gradient_color_start'] ?? '#1e3a5f'; + $endColor = $data['title']['gradient_color_end'] ?? '#FF8600'; + $direction = $data['title']['gradient_direction'] ?? 'to-right'; + + $directions = [ + 'to-right' => 'to right', + 'to-left' => 'to left', + 'to-bottom' => 'to bottom', + 'to-top' => 'to top', + 'diagonal' => '135deg' + ]; + + $gradientDirection = $directions[$direction] ?? 'to right'; + + return "background: linear-gradient({$gradientDirection}, {$startColor}, {$endColor}); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;"; + } + + private function buildCustomStyles(array $data): string + { + $badgeBg = $data['styles']['category_badge_background'] ?? 'rgba(255, 255, 255, 0.2)'; + $badgeTextColor = $data['styles']['category_badge_text_color'] ?? '#FFFFFF'; + $badgeBlur = isset($data['styles']['category_badge_blur']) && $data['styles']['category_badge_blur']; + + $blurStyle = $badgeBlur ? 'backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);' : ''; + + $overlayStyle = ''; + if (($data['styles']['background_type'] ?? '') === 'image' && + isset($data['styles']['background_overlay']) && + $data['styles']['background_overlay']) { + $opacity = ($data['styles']['overlay_opacity'] ?? 60) / 100; + $overlayStyle = << .container { + position: relative; + z-index: 1; +} +CSS; + } + + return << +.category-badge-hero { + background-color: {$badgeBg}; + color: {$badgeTextColor}; + padding: 0.5rem 1rem; + border-radius: 2rem; + text-decoration: none; + font-size: 0.875rem; + font-weight: 500; + display: inline-flex; + align-items: center; + transition: all 0.3s ease; + {$blurStyle} +} +.category-badge-hero:hover { + background-color: rgba(255, 134, 0, 0.3); + color: {$badgeTextColor}; + transform: translateY(-2px); +} +.roi-gradient-text { + display: inline-block; +} +{$overlayStyle} + +STYLES; + } + + public function supports(string $componentType): bool + { + return $componentType === 'hero-section'; + } +} diff --git a/TemplateParts/content-hero.php b/TemplateParts/content-hero.php new file mode 100644 index 00000000..d1cbd8f9 --- /dev/null +++ b/TemplateParts/content-hero.php @@ -0,0 +1,61 @@ + + +
+
+
+ + + +
+ + name !== 'Uncategorized' && $category->name !== 'Sin categoría') : ?> + name); ?> + + +
+ + + +

+ + +
+ + + + + | + + + + + + | + + + + + +
+ +
+
+
diff --git a/functions-addon.php b/functions-addon.php index 28090ed9..94b053e8 100644 --- a/functions-addon.php +++ b/functions-addon.php @@ -297,6 +297,9 @@ function roi_render_component(string $componentName): string { case 'cta-lets-talk': $renderer = new \ROITheme\Public\CtaLetsTalk\Infrastructure\Ui\CtaLetsTalkRenderer($cssGenerator); break; + case 'hero-section': + $renderer = new \ROITheme\Public\HeroSection\Infrastructure\Ui\HeroSectionRenderer(); + break; case 'featured-image': $renderer = new \ROITheme\Public\FeaturedImage\Infrastructure\Ui\FeaturedImageRenderer($cssGenerator); break;