getData();
// Validar visibilidad general
if (!$this->isEnabled($data)) {
return '';
}
// Validar visibilidad por página
if (!$this->shouldShowOnCurrentPage($data)) {
return '';
}
// Generar CSS usando CSSGeneratorService
$css = $this->generateCSS($data);
// Generar HTML
$html = $this->buildHTML($data);
// Siempre incluir CSS inline con el componente
// Nota: is_critical se reserva para futura implementación con output buffering
return sprintf(
"\n%s",
$css,
$html
);
}
/**
* {@inheritDoc}
*/
public function supports(string $componentType): bool
{
return $componentType === 'top-notification-bar';
}
/**
* Verificar si el componente está habilitado
*
* @param array $data Datos del componente
* @return bool
*/
private function isEnabled(array $data): bool
{
return ($data['visibility']['is_enabled'] ?? false) === true;
}
/**
* Verificar si debe mostrarse en la página actual
*
* @param array $data Datos del componente
* @return bool
*/
private function shouldShowOnCurrentPage(array $data): bool
{
$showOn = $data['visibility']['show_on_pages'] ?? 'all';
return match ($showOn) {
'all' => true,
'home' => is_front_page(),
'posts' => is_single(),
'pages' => is_page(),
'custom' => $this->isInCustomPages($data),
default => true,
};
}
/**
* Verificar si está en páginas personalizadas
*
* @param array $data Datos del componente
* @return bool
*/
private function isInCustomPages(array $data): bool
{
$pageIds = $data['visibility']['custom_page_ids'] ?? '';
if (empty($pageIds)) {
return false;
}
$allowedIds = array_map('trim', explode(',', $pageIds));
$currentId = (string) get_the_ID();
return in_array($currentId, $allowedIds, true);
}
/**
* Verificar si el componente fue dismissed por el usuario
*
* @param array $data Datos del componente
* @return bool
*/
private function isDismissed(array $data): bool
{
if (!$this->isDismissible($data)) {
return false;
}
$cookieName = 'roi_notification_bar_dismissed';
return isset($_COOKIE[$cookieName]) && $_COOKIE[$cookieName] === '1';
}
/**
* Verificar si el componente es dismissible
*
* @param array $data Datos del componente
* @return bool
*/
private function isDismissible(array $data): bool
{
return ($data['behavior']['is_dismissible'] ?? false) === true;
}
/**
* Generar CSS usando CSSGeneratorService
*
* @param array $data Datos del componente
* @return string CSS generado
*/
private function generateCSS(array $data): string
{
$css = '';
// Estilos base de la barra
$baseStyles = [
'background_color' => $data['styles']['background_color'] ?? '#0E2337',
'color' => $data['styles']['text_color'] ?? '#FFFFFF',
'font_size' => $data['styles']['font_size'] ?? '0.9rem',
'padding' => $data['styles']['padding'] ?? '0.5rem 0',
'width' => '100%',
'z_index' => '1050',
];
$css .= $this->cssGenerator->generate('.top-notification-bar', $baseStyles);
// Estilos del ícono
$iconStyles = [
'color' => $data['styles']['icon_color'] ?? '#FF8600',
];
$css .= "\n" . $this->cssGenerator->generate('.top-notification-bar .notification-icon', $iconStyles);
// Estilos de la etiqueta (label)
$labelStyles = [
'color' => $data['styles']['label_color'] ?? '#FF8600',
];
$css .= "\n" . $this->cssGenerator->generate('.top-notification-bar .notification-label', $labelStyles);
// Estilos del enlace
$linkStyles = [
'color' => $data['styles']['link_color'] ?? '#FFFFFF',
];
$css .= "\n" . $this->cssGenerator->generate('.top-notification-bar .notification-link', $linkStyles);
// Estilos del enlace hover
$linkHoverStyles = [
'color' => $data['styles']['link_hover_color'] ?? '#FF8600',
];
$css .= "\n" . $this->cssGenerator->generate('.top-notification-bar .notification-link:hover', $linkHoverStyles);
// Estilos del ícono personalizado
$customIconStyles = [
'width' => '24px',
'height' => '24px',
];
$css .= "\n" . $this->cssGenerator->generate('.top-notification-bar .custom-icon', $customIconStyles);
return $css;
}
/**
* Generar HTML del componente
*
* @param array $data Datos del componente
* @return string HTML generado
*/
private function buildHTML(array $data): string
{
$classes = $this->buildClasses($data);
$content = $this->buildContent($data);
return sprintf(
'
%s
',
esc_attr($classes),
$content
);
}
/**
* Construir clases CSS del componente
*
* @param array $data Datos del componente
* @return string Clases CSS
*/
private function buildClasses(array $data): string
{
return 'top-notification-bar';
}
/**
* Construir atributos data para dismissible
*
* @param array $data Datos del componente
* @return string Atributos HTML
*/
private function buildDismissAttributes(array $data): string
{
if (!$this->isDismissible($data)) {
return '';
}
$days = (int) ($data['behavior']['dismissible_cookie_days'] ?? 7);
return sprintf(' data-dismissible-days="%d"', $days);
}
/**
* Construir contenido del componente
*
* @param array $data Datos del componente
* @return string HTML del contenido
*/
private function buildContent(array $data): string
{
$html = '';
$html .= '
';
// Ícono
$html .= $this->buildIcon($data);
// Texto del anuncio
$html .= $this->buildAnnouncementText($data);
// Enlace
$html .= $this->buildLink($data);
$html .= '
';
$html .= '
';
return $html;
}
/**
* Construir ícono del componente
*
* @param array $data Datos del componente
* @return string HTML del ícono
*/
private function buildIcon(array $data): string
{
// Siempre usar Bootstrap icon desde content.icon_class
$iconClass = $data['content']['icon_class'] ?? 'bi-megaphone-fill';
// Asegurar prefijo 'bi-'
if (strpos($iconClass, 'bi-') !== 0) {
$iconClass = 'bi-' . $iconClass;
}
return sprintf(
'',
esc_attr($iconClass)
);
}
/**
* Construir texto del anuncio
*
* @param array $data Datos del componente
* @return string HTML del texto
*/
private function buildAnnouncementText(array $data): string
{
$label = $data['content']['label_text'] ?? '';
$text = $data['content']['message_text'] ?? '';
if (empty($text)) {
return '';
}
$html = '';
if (!empty($label)) {
$html .= sprintf('%s ', esc_html($label));
}
$html .= esc_html($text);
$html .= '';
return $html;
}
/**
* Construir enlace de acción
*
* @param array $data Datos del componente
* @return string HTML del enlace
*/
private function buildLink(array $data): string
{
$linkText = $data['content']['link_text'] ?? '';
$linkUrl = $data['content']['link_url'] ?? '#';
if (empty($linkText)) {
return '';
}
return sprintf(
'%s',
esc_url($linkUrl),
esc_html($linkText)
);
}
/**
* Construir botón de cerrar
*
* @return string HTML del botón
*/
private function buildDismissButton(): string
{
return '';
}
/**
* Construir estilos CSS de animaciones
*
* @param array $data Datos del componente
* @return string CSS de animaciones
*/
private function buildAnimationStyles(array $data): string
{
$animationType = $data['visual_effects']['animation_type'] ?? 'slide-down';
$animations = [
'slide-down' => [
'keyframes' => '@keyframes roiSlideDown { from { transform: translateY(-100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } }',
'animation' => 'roiSlideDown 0.5s ease-out',
],
'fade-in' => [
'keyframes' => '@keyframes roiFadeIn { from { opacity: 0; } to { opacity: 1; } }',
'animation' => 'roiFadeIn 0.5s ease-out',
],
];
$anim = $animations[$animationType] ?? $animations['slide-down'];
return sprintf(
"%s\n.top-notification-bar.roi-animated.roi-%s { animation: %s; }",
$anim['keyframes'],
$animationType,
$anim['animation']
);
}
/**
* Construir script para funcionalidad dismissible
*
* @param array $data Datos del componente
* @return string JavaScript
*/
private function buildDismissScript(array $data): string
{
$days = (int) ($data['behavior']['dismissible_cookie_days'] ?? 7);
return sprintf(
'',
$days
);
}
/**
* Obtiene las clases CSS de Bootstrap para visibilidad responsive
*
* Implementa tabla de decisión según especificación (10.03):
* - Desktop Y Mobile = null (visible en ambos)
* - Solo Desktop = 'd-none d-lg-block'
* - Solo Mobile = 'd-lg-none'
* - Ninguno = 'd-none' (oculto)
*
* @param bool $desktop Mostrar en desktop
* @param bool $mobile Mostrar en mobile
* @return string|null Clases CSS o null si visible en ambos
*/
private function getVisibilityClasses(bool $desktop, bool $mobile): ?string
{
// Desktop Y Mobile = visible en ambos dispositivos
if ($desktop && $mobile) {
return null; // Sin clases = visible siempre
}
// Solo Desktop
if ($desktop && !$mobile) {
return 'd-none d-lg-block';
}
// Solo Mobile
if (!$desktop && $mobile) {
return 'd-lg-none';
}
// Ninguno = oculto completamente
return 'd-none';
}
}