getData(); if (!$this->isEnabled($data)) { return ''; } $css = $this->generateCSS($data); $html = $this->buildMenu($data); return sprintf( "\n%s", $css, $html ); } private function isEnabled(array $data): bool { return isset($data['visibility']['is_enabled']) && $data['visibility']['is_enabled'] === true; } private function shouldShowOnMobile(array $data): bool { return isset($data['visibility']['show_on_mobile']) && $data['visibility']['show_on_mobile'] === true; } /** * Generar CSS usando CSSGeneratorService * * @param array $data Datos del componente * @return string CSS generado */ private function generateCSS(array $data): string { $css = ''; // Obtener valores de configuración $stickyEnabled = $data['visibility']['sticky_enabled'] ?? true; $paddingVertical = $data['layout']['padding_vertical'] ?? '0.75rem 0'; $zIndex = $data['layout']['z_index'] ?? '1030'; $bgColor = $data['colors']['background_color'] ?? '#1e3a5f'; $boxShadow = $data['colors']['box_shadow'] ?? '0 4px 12px rgba(30, 58, 95, 0.15)'; $linkTextColor = $data['links']['text_color'] ?? '#FFFFFF'; $linkHoverColor = $data['links']['hover_color'] ?? '#FF8600'; $linkActiveColor = $data['links']['active_color'] ?? '#FF8600'; $linkFontSize = $data['links']['font_size'] ?? '0.9rem'; $linkFontWeight = $data['links']['font_weight'] ?? '500'; $linkPadding = $data['links']['padding'] ?? '0.5rem 0.65rem'; $linkBorderRadius = $data['links']['border_radius'] ?? '4px'; $showUnderlineEffect = $data['links']['show_underline_effect'] ?? true; $underlineColor = $data['links']['underline_color'] ?? '#FF8600'; // Estilos del navbar container $navbarStyles = [ 'background-color' => $bgColor . ' !important', 'box-shadow' => $boxShadow, 'padding' => $paddingVertical, 'transition' => 'all 0.3s ease', ]; if ($stickyEnabled) { $navbarStyles['position'] = 'sticky'; $navbarStyles['top'] = '0'; $navbarStyles['z-index'] = $zIndex; } $css .= $this->cssGenerator->generate('.navbar', $navbarStyles); // Efecto scrolled del navbar $css .= "\n" . $this->cssGenerator->generate('.navbar.scrolled', [ 'box-shadow' => '0 6px 20px rgba(30, 58, 95, 0.25)', ]); // Estilos de los enlaces del navbar $navLinkStyles = [ 'color' => 'rgba(255, 255, 255, 0.9) !important', 'font-weight' => $linkFontWeight, 'position' => 'relative', 'padding' => $linkPadding . ' !important', 'transition' => 'all 0.3s ease', 'font-size' => $linkFontSize, 'white-space' => 'nowrap', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .nav-link', $navLinkStyles); // Efecto de subrayado (::after pseudo-element) if ($showUnderlineEffect) { $css .= "\n.navbar .nav-link::after {"; $css .= "\n content: '';"; $css .= "\n position: absolute;"; $css .= "\n bottom: 0;"; $css .= "\n left: 50%;"; $css .= "\n transform: translateX(-50%) scaleX(0);"; $css .= "\n width: 80%;"; $css .= "\n height: 2px;"; $css .= "\n background: {$underlineColor};"; $css .= "\n transition: transform 0.3s ease;"; $css .= "\n}"; $css .= "\n.navbar .nav-link:hover::after {"; $css .= "\n transform: translateX(-50%) scaleX(1);"; $css .= "\n}"; } // Estilos hover y focus de los enlaces $navLinkHoverStyles = [ 'color' => $linkHoverColor . ' !important', 'background-color' => 'rgba(255, 133, 0, 0.1)', 'border-radius' => $linkBorderRadius, ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .nav-link:hover, .navbar .nav-link:focus', $navLinkHoverStyles); // Estilos de enlaces activos $navLinkActiveStyles = [ 'color' => $linkActiveColor . ' !important', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .nav-link.active, .navbar .nav-item.current-menu-item > .nav-link', $navLinkActiveStyles); // Estilos del dropdown menu $dropdownMaxHeight = $data['visual_effects']['dropdown_max_height'] ?? '300px'; $dropdownStyles = [ 'background' => $data['visual_effects']['background_color'] ?? '#ffffff', 'border' => 'none', 'box-shadow' => $data['visual_effects']['shadow'] ?? '0 8px 24px rgba(0, 0, 0, 0.12)', 'border-radius' => $data['visual_effects']['border_radius'] ?? '8px', 'padding' => '0.5rem 0', 'max-height' => $dropdownMaxHeight, 'overflow-y' => 'auto', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .dropdown-menu', $dropdownStyles); // Hover en desktop para mostrar dropdown (sin necesidad de clic) $css .= "\n@media (min-width: 992px) {"; $css .= "\n .navbar .dropdown:hover > .dropdown-menu {"; $css .= "\n display: block;"; $css .= "\n margin-top: 0;"; $css .= "\n }"; $css .= "\n .navbar .dropdown > .dropdown-toggle:active {"; $css .= "\n pointer-events: none;"; $css .= "\n }"; $css .= "\n}"; // Estilos de items del dropdown $dropdownItemStyles = [ 'color' => $data['visual_effects']['item_color'] ?? '#495057', 'padding' => $data['visual_effects']['item_padding'] ?? '0.625rem 1.25rem', 'transition' => 'all 0.3s ease', 'font-weight' => '500', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .dropdown-item', $dropdownItemStyles); // Estilos hover de items del dropdown $dropdownItemHoverStyles = [ 'background-color' => $data['visual_effects']['item_hover_background'] ?? 'rgba(255, 133, 0, 0.1)', 'color' => $linkHoverColor, ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .dropdown-item:hover, .navbar .dropdown-item:focus', $dropdownItemHoverStyles); // Estilos del brand (texto) $brandStyles = [ 'color' => ($data['media']['brand_color'] ?? '#FFFFFF') . ' !important', 'font-weight' => '700', 'font-size' => $data['media']['brand_font_size'] ?? '1.5rem', 'transition' => 'color 0.3s ease', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .navbar-brand, .navbar .roi-navbar-brand', $brandStyles); // Estilos hover del brand $brandHoverStyles = [ 'color' => ($data['media']['brand_hover_color'] ?? '#FF8600') . ' !important', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .navbar-brand:hover, .navbar .roi-navbar-brand:hover', $brandHoverStyles); // Estilos del logo (imagen) $logoStyles = [ 'height' => $data['media']['logo_height'] ?? '40px', 'width' => 'auto', ]; $css .= "\n" . $this->cssGenerator->generate('.navbar .roi-navbar-logo', $logoStyles); return $css; } private function buildMenu(array $data): string { $menuLocation = $data['behavior']['menu_location'] ?? 'primary'; $enableDropdowns = $data['behavior']['enable_dropdowns'] ?? true; $mobileBreakpoint = $data['behavior']['mobile_breakpoint'] ?? 'lg'; $ulClass = 'navbar-nav mb-2 mb-lg-0'; $args = [ 'theme_location' => $menuLocation === 'custom' ? '' : $menuLocation, 'menu' => $menuLocation === 'custom' ? ($data['behavior']['custom_menu_id'] ?? 0) : '', 'container' => false, 'menu_class' => $ulClass, 'fallback_cb' => '__return_false', 'items_wrap' => '', 'depth' => $enableDropdowns ? 2 : 1, 'walker' => new ROI_Bootstrap_Nav_Walker() ]; ob_start(); wp_nav_menu($args); return ob_get_clean(); } /** * Obtiene las clases CSS de Bootstrap para visibilidad responsive * * Implementa tabla de decisión según especificación: * - 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 { if ($desktop && $mobile) { return null; // Sin clases = visible siempre } if ($desktop && !$mobile) { return 'd-none d-lg-block'; } if (!$desktop && $mobile) { return 'd-lg-none'; } return 'd-none'; } public function supports(string $componentType): bool { return $componentType === 'navbar'; } } /** * Custom Walker for Bootstrap 5 Navigation * * RESPONSABILIDAD: Adaptar wp_nav_menu() a Bootstrap 5 * * CARACTERÍSTICAS: * - Clases Bootstrap 5 (.nav-item, .nav-link, .dropdown) * - Atributos data-bs-toggle para dropdowns * - Soporte para current-menu-item */ class ROI_Bootstrap_Nav_Walker extends Walker_Nav_Menu { public function start_lvl(&$output, $depth = 0, $args = null) { $indent = str_repeat("\t", $depth); $output .= "\n$indent