[ 'field' => 'show_facebook', 'url_field' => 'facebook_url', 'icon' => 'bi-facebook', 'label' => 'Facebook', 'share_pattern' => 'https://www.facebook.com/sharer/sharer.php?u=%s', ], 'instagram' => [ 'field' => 'show_instagram', 'url_field' => 'instagram_url', 'icon' => 'bi-instagram', 'label' => 'Instagram', 'share_pattern' => '', // Instagram no tiene share directo - requiere URL configurada ], 'linkedin' => [ 'field' => 'show_linkedin', 'url_field' => 'linkedin_url', 'icon' => 'bi-linkedin', 'label' => 'LinkedIn', 'share_pattern' => 'https://www.linkedin.com/sharing/share-offsite/?url=%s', ], 'whatsapp' => [ 'field' => 'show_whatsapp', 'url_field' => 'whatsapp_number', 'icon' => 'bi-whatsapp', 'label' => 'WhatsApp', 'share_pattern' => 'https://wa.me/?text=%s', // Compartir via WhatsApp ], 'twitter' => [ 'field' => 'show_twitter', 'url_field' => 'twitter_url', 'icon' => 'bi-twitter-x', 'label' => 'X', 'share_pattern' => 'https://twitter.com/intent/tweet?url=%s&text=%s', ], 'email' => [ 'field' => 'show_email', 'url_field' => 'email_address', 'icon' => 'bi-envelope', 'label' => 'Email', 'share_pattern' => 'mailto:?subject=%s&body=%s', // Compartir via Email ], ]; public function __construct( private CSSGeneratorInterface $cssGenerator ) {} public function render(Component $component): string { $data = $component->getData(); if (!$this->isEnabled($data)) { return ''; } if (!PageVisibilityHelper::shouldShow(self::COMPONENT_NAME)) { return ''; } $css = $this->generateCSS($data); $html = $this->buildHTML($data); return sprintf("\n%s", $css, $html); } public function supports(string $componentType): bool { return $componentType === self::COMPONENT_NAME; } private function isEnabled(array $data): bool { $value = $data['visibility']['is_enabled'] ?? false; return $value === true || $value === '1' || $value === 1; } private function generateCSS(array $data): string { $colors = $data['colors'] ?? []; $spacing = $data['spacing'] ?? []; $typography = $data['typography'] ?? []; $effects = $data['visual_effects'] ?? []; $visibility = $data['visibility'] ?? []; $cssRules = []; $transitionDuration = $effects['transition_duration'] ?? '0.3s'; $buttonBorderWidth = $effects['button_border_width'] ?? '2px'; // Container styles $cssRules[] = $this->cssGenerator->generate('.social-share-container', [ 'margin-top' => $spacing['container_margin_top'] ?? '3rem', 'margin-bottom' => $spacing['container_margin_bottom'] ?? '3rem', 'padding-top' => $spacing['container_padding_top'] ?? '1.5rem', 'padding-bottom' => $spacing['container_padding_bottom'] ?? '1.5rem', 'border-top' => sprintf('%s solid %s', $effects['border_top_width'] ?? '1px', $colors['border_top_color'] ?? '#dee2e6' ), ]); // Label styles $cssRules[] = $this->cssGenerator->generate('.social-share-container .share-label', [ 'font-size' => $typography['label_font_size'] ?? '1rem', 'color' => $colors['label_color'] ?? '#6c757d', 'margin-bottom' => $spacing['label_margin_bottom'] ?? '1rem', ]); // Buttons wrapper $cssRules[] = $this->cssGenerator->generate('.social-share-container .share-buttons', [ 'display' => 'flex', 'flex-wrap' => 'wrap', 'gap' => $spacing['buttons_gap'] ?? '0.5rem', ]); // Base button styles $cssRules[] = $this->cssGenerator->generate('.social-share-container .share-buttons .btn', [ 'padding' => $spacing['button_padding'] ?? '0.25rem 0.5rem', 'font-size' => $typography['icon_font_size'] ?? '1rem', 'border-width' => $buttonBorderWidth, 'border-radius' => $effects['button_border_radius'] ?? '0.375rem', 'transition' => "all {$transitionDuration} ease", 'background-color' => $colors['button_background'] ?? '#ffffff', ]); // Hover effect $cssRules[] = $this->cssGenerator->generate('.social-share-container .share-buttons .btn:hover', [ 'box-shadow' => $effects['hover_box_shadow'] ?? '0 4px 12px rgba(0, 0, 0, 0.15)', ]); // Network-specific colors $networkColors = [ 'facebook' => $colors['facebook_color'] ?? '#0d6efd', 'instagram' => $colors['instagram_color'] ?? '#dc3545', 'linkedin' => $colors['linkedin_color'] ?? '#0dcaf0', 'whatsapp' => $colors['whatsapp_color'] ?? '#198754', 'twitter' => $colors['twitter_color'] ?? '#212529', 'email' => $colors['email_color'] ?? '#6c757d', ]; foreach ($networkColors as $network => $color) { // Outline style $cssRules[] = $this->cssGenerator->generate(".social-share-container .btn-share-{$network}", [ 'color' => $color, 'border-color' => $color, ]); // Hover fills the button $cssRules[] = $this->cssGenerator->generate(".social-share-container .btn-share-{$network}:hover", [ 'background-color' => $color, 'color' => '#ffffff', ]); } // Responsive visibility (normalizar booleanos desde BD) $showOnDesktop = $visibility['show_on_desktop'] ?? true; $showOnDesktop = $showOnDesktop === true || $showOnDesktop === '1' || $showOnDesktop === 1; $showOnMobile = $visibility['show_on_mobile'] ?? true; $showOnMobile = $showOnMobile === true || $showOnMobile === '1' || $showOnMobile === 1; if (!$showOnMobile) { $cssRules[] = "@media (max-width: 991.98px) { .social-share-container { display: none !important; } }"; } if (!$showOnDesktop) { $cssRules[] = "@media (min-width: 992px) { .social-share-container { display: none !important; } }"; } return implode("\n", $cssRules); } private function buildHTML(array $data): string { $content = $data['content'] ?? []; $networks = $data['networks'] ?? []; $labelText = $content['label_text'] ?? 'Compartir:'; $showLabel = $content['show_label'] ?? true; $showLabel = $showLabel === true || $showLabel === '1' || $showLabel === 1; $html = '
'; // Label if ($showLabel && !empty($labelText)) { $html .= sprintf( '

%s

', esc_html($labelText) ); } // Buttons wrapper $html .= '
'; // Get current post data for share URLs $shareUrl = $this->getCurrentUrl(); $shareTitle = $this->getCurrentTitle(); foreach (self::NETWORKS as $networkKey => $networkData) { $fieldKey = $networkData['field']; $isEnabled = $networks[$fieldKey] ?? true; $isEnabled = $isEnabled === true || $isEnabled === '1' || $isEnabled === 1; if (!$isEnabled) { continue; } // Obtener URL configurada para esta red $urlFieldKey = $networkData['url_field']; $configuredUrl = $networks[$urlFieldKey] ?? ''; $shareHref = $this->buildNetworkUrl( $networkKey, $configuredUrl, $networkData['share_pattern'], $shareUrl, $shareTitle ); // Si no hay URL válida usar "#" como fallback (para mantener el icono visible) if (empty($shareHref)) { $shareHref = '#'; } $ariaLabel = sprintf('Compartir en %s', $networkData['label']); $html .= sprintf( ' ', esc_url($shareHref), esc_attr($networkKey), esc_attr($ariaLabel), esc_attr($networkData['icon']) ); } $html .= '
'; // .share-buttons $html .= '
'; // .social-share-container return $html; } private function getCurrentUrl(): string { if (is_singular()) { return get_permalink() ?: ''; } return home_url(add_query_arg([], $GLOBALS['wp']->request ?? '')); } private function getCurrentTitle(): string { if (is_singular()) { return get_the_title() ?: ''; } return wp_title('', false) ?: get_bloginfo('name'); } /** * Construye la URL para un botón de red social * * Prioridad: * 1. URL configurada por el usuario → enlace directo al perfil * 2. Sin URL configurada → usar patrón de compartir (si existe) */ private function buildNetworkUrl( string $network, string $configuredUrl, string $sharePattern, string $pageUrl, string $pageTitle ): string { // Si hay URL configurada, usarla directamente if (!empty($configuredUrl)) { return $this->formatConfiguredUrl($network, $configuredUrl); } // Si no hay URL configurada pero existe patrón de compartir if (!empty($sharePattern)) { return $this->formatShareUrl($network, $sharePattern, $pageUrl, $pageTitle); } return '#'; } /** * Formatea URL configurada según el tipo de red */ private function formatConfiguredUrl(string $network, string $url): string { switch ($network) { case 'whatsapp': // Para WhatsApp, el número debe ir sin el + $number = preg_replace('/[^0-9]/', '', $url); return "https://wa.me/{$number}"; case 'email': // Para email, agregar mailto: si no lo tiene if (!str_starts_with($url, 'mailto:')) { return "mailto:{$url}"; } return $url; default: return $url; } } /** * Formatea URL de compartir usando el patrón */ private function formatShareUrl(string $network, string $pattern, string $url, string $title): string { $encodedUrl = rawurlencode($url); $encodedTitle = rawurlencode($title); switch ($network) { case 'twitter': return sprintf($pattern, $encodedUrl, $encodedTitle); case 'whatsapp': $text = $title . ' - ' . $url; return sprintf($pattern, rawurlencode($text)); case 'email': return sprintf($pattern, $encodedTitle, $encodedUrl); default: return sprintf($pattern, $encodedUrl); } } private function getVisibilityClasses(bool $desktop, bool $mobile): ?string { if (!$desktop && !$mobile) { return null; } if (!$desktop && $mobile) { return 'd-lg-none'; } if ($desktop && !$mobile) { return 'd-none d-lg-block'; } return ''; } }