485 lines
14 KiB
PHP
485 lines
14 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Public\HeroSection\Infrastructure\Ui;
|
|
|
|
use ROITheme\Shared\Domain\Entities\Component;
|
|
use ROITheme\Shared\Domain\Contracts\RendererInterface;
|
|
use ROITheme\Shared\Infrastructure\Services\PageVisibilityHelper;
|
|
|
|
/**
|
|
* HeroSectionRenderer - Renderiza la sección hero con badges y título
|
|
*
|
|
* RESPONSABILIDAD: Generar HTML de la sección hero
|
|
*
|
|
* CARACTERÍSTICAS:
|
|
* - Badges de categorías con múltiples fuentes de datos
|
|
* - Título H1 con gradiente opcional
|
|
* - Múltiples tipos de fondo (color, gradiente, imagen)
|
|
* - Lógica condicional de visibilidad por tipo de página
|
|
*
|
|
* @package ROITheme\Public\HeroSection\Presentation
|
|
*/
|
|
final class HeroSectionRenderer implements RendererInterface
|
|
{
|
|
public function render(Component $component): string
|
|
{
|
|
// Verificar visibilidad por tipo de página y exclusiones (Plan 99.10/99.11)
|
|
if (!PageVisibilityHelper::shouldShow('hero-section')) {
|
|
return '';
|
|
}
|
|
|
|
$data = $component->getData();
|
|
|
|
if (!$this->isEnabled($data)) {
|
|
return '';
|
|
}
|
|
|
|
if (!$this->shouldShowOnCurrentPage($data)) {
|
|
return '';
|
|
}
|
|
|
|
$classes = $this->buildSectionClasses($data);
|
|
$styles = $this->buildInlineStyles($data);
|
|
|
|
$html = sprintf(
|
|
'<div class="%s"%s>',
|
|
esc_attr($classes),
|
|
$styles ? ' style="' . esc_attr($styles) . '"' : ''
|
|
);
|
|
|
|
$html .= '<div class="container">';
|
|
|
|
// Categories badges
|
|
if ($this->shouldShowCategories($data)) {
|
|
$html .= $this->buildCategoriesBadges($data);
|
|
}
|
|
|
|
// Title
|
|
$html .= $this->buildTitle($data);
|
|
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
// 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('<div class="mb-3 d-flex %s">', esc_attr($alignmentClass));
|
|
$html .= '<div class="d-flex gap-2 flex-wrap justify-content-center">';
|
|
|
|
foreach ($categories as $category) {
|
|
$html .= sprintf(
|
|
'<a href="%s" class="category-badge category-badge-hero"><i class="bi %s me-1"></i>%s</a>',
|
|
esc_url($category['url']),
|
|
esc_attr($icon),
|
|
esc_html($category['name'])
|
|
);
|
|
}
|
|
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
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</%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 = <<<CSS
|
|
.hero-title::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, {$opacity});
|
|
z-index: 0;
|
|
}
|
|
.hero-title > .container {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
CSS;
|
|
}
|
|
|
|
return <<<STYLES
|
|
<style>
|
|
.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}
|
|
</style>
|
|
STYLES;
|
|
}
|
|
|
|
public function supports(string $componentType): bool
|
|
{
|
|
return $componentType === 'hero-section';
|
|
}
|
|
}
|