| 1 |
<?php |
| 2 |
|
| 3 |
namespace MasterAddons\Modules\Display\MegaMenu; |
| 4 |
|
| 5 |
use MasterAddons\Modules\Display\MegaMenu\JLTMA_Megamenu_Options; |
| 6 |
use MasterAddons\Inc\Classes\Helper; |
| 7 |
// use \Walker_Nav_Menu; |
| 8 |
|
| 9 |
class Megamenu_Nav_Walker extends \Walker_Nav_Menu |
| 10 |
{ |
| 11 |
|
| 12 |
private $currentItem; |
| 13 |
private static $_instance = null; |
| 14 |
public $menu_Settings; |
| 15 |
|
| 16 |
// Get Item Custom Method |
| 17 |
public function get_item_meta($menu_item_id) |
| 18 |
{ |
| 19 |
$meta_key = Megamenu_Options::$jltma_menuitem_settings_key; |
| 20 |
$data = get_post_meta($menu_item_id, $meta_key, true); |
| 21 |
$data = (array) json_decode($data); |
| 22 |
|
| 23 |
$format = [ |
| 24 |
"menu_id" => null, |
| 25 |
"menu_has_child" => '', |
| 26 |
"menu_enable" => 0, |
| 27 |
"menu_trigger_effect" => 'hover', |
| 28 |
"menu_icon" => '', |
| 29 |
"menu_disable_description" => 0, |
| 30 |
"menu_label_enable" => 0, |
| 31 |
"menu_icon_color" => '', |
| 32 |
"menu_width_type" => 'default', |
| 33 |
"menu_width_size" => '1000px', |
| 34 |
"menu_transition" => '', |
| 35 |
"menu_badge_text" => '', |
| 36 |
"menu_badge_color" => '', |
| 37 |
"menu_badge_background" => '', |
| 38 |
"menu_mobile_submenu_content_type" => 'builder_content' |
| 39 |
]; |
| 40 |
|
| 41 |
return array_merge($format, $data); |
| 42 |
} |
| 43 |
|
| 44 |
public function is_megamenu($menu_slug) |
| 45 |
{ |
| 46 |
$menu_slug = (((gettype($menu_slug) == 'object') && isset($menu_slug->slug)) ? $menu_slug->slug : $menu_slug); |
| 47 |
|
| 48 |
$cache_key = 'master_megamenu_data_' . $menu_slug; |
| 49 |
$cached = wp_cache_get($cache_key); |
| 50 |
if (false !== $cached) { |
| 51 |
return $cached; |
| 52 |
} |
| 53 |
|
| 54 |
$return = 0; |
| 55 |
$settings = Megamenu_Options::get_instance()->get_option(Megamenu_Options::$jltma_menu_settings_key, []); |
| 56 |
$term = get_term_by('slug', $menu_slug, 'nav_menu'); |
| 57 |
|
| 58 |
if ( |
| 59 |
isset($term->term_id) |
| 60 |
&& isset($settings['menu_location_' . $term->term_id]) |
| 61 |
&& $settings['menu_location_' . $term->term_id]['is_enabled'] == '1' |
| 62 |
) { |
| 63 |
|
| 64 |
$return = 1; |
| 65 |
} |
| 66 |
|
| 67 |
wp_cache_set($cache_key, $return); |
| 68 |
return $return; |
| 69 |
} |
| 70 |
|
| 71 |
public function is_megamenu_item($item_meta, $menu) |
| 72 |
{ |
| 73 |
if ($this->is_megamenu($menu) == 1 && $item_meta['menu_enable'] == 1 && class_exists('Elementor\Plugin')) { |
| 74 |
return true; |
| 75 |
} |
| 76 |
return false; |
| 77 |
} |
| 78 |
|
| 79 |
/** |
| 80 |
* Starts the list before the elements are added. |
| 81 |
*/ |
| 82 |
public function start_lvl(&$output, $depth = 0, $args = array()) |
| 83 |
{ |
| 84 |
$indent = str_repeat("\t", $depth); |
| 85 |
|
| 86 |
$output .= "\n$indent<ul class=\"jltma-dropdown jltma-sub-menu dropdown-menu\">\n"; |
| 87 |
} |
| 88 |
/** |
| 89 |
* Ends the list of after the elements are added. |
| 90 |
* |
| 91 |
* @see Walker::end_lvl() |
| 92 |
* @since 3.0.0 |
| 93 |
* @param string $output Passed by reference. Used to append additional content. |
| 94 |
* @param int $depth Depth of menu item. Used for padding. |
| 95 |
* @param array $args An array of arguments. @see wp_nav_menu() |
| 96 |
*/ |
| 97 |
public function end_lvl(&$output, $depth = 0, $args = array()) |
| 98 |
{ |
| 99 |
$indent = str_repeat("\t", $depth); |
| 100 |
$output .= "$indent</ul>\n"; |
| 101 |
} |
| 102 |
/** |
| 103 |
* Start the element output. |
| 104 |
* @see Walker::start_el() |
| 105 |
* @param int $id Current item ID. |
| 106 |
*/ |
| 107 |
public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) |
| 108 |
{ |
| 109 |
$this->currentItem = $item; |
| 110 |
|
| 111 |
$indent = ($depth) ? str_repeat("\t", $depth) : ''; |
| 112 |
$classes = empty($item->classes) ? array() : (array) $item->classes; |
| 113 |
$classes[] = 'menu-item-' . $item->ID; |
| 114 |
|
| 115 |
|
| 116 |
/** |
| 117 |
* Filter the CSS class(es) applied to a menu item's list item element. |
| 118 |
*/ |
| 119 |
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args, $depth)); |
| 120 |
|
| 121 |
$class_names .= ' nav-item'; |
| 122 |
$item_meta = $this->get_item_meta($item->ID); |
| 123 |
|
| 124 |
$menu_obj = wp_get_nav_menu_object($args->menu); |
| 125 |
$is_megamenu_item = $this->is_megamenu_item($item_meta, $menu_obj); |
| 126 |
$data_menu_width_type = ''; |
| 127 |
if (in_array('menu-item-has-children', $classes) || $is_megamenu_item == true) { |
| 128 |
$class_names .= ' jltma-menu-has-children'; |
| 129 |
$data_menu_width_type = ' data-menu-type="'.$item_meta['menu_width_type'].'"'; |
| 130 |
if ($is_megamenu_item == true) { |
| 131 |
if ($item_meta['menu_mobile_submenu_content_type'] == 'submenu_list') { |
| 132 |
$class_names .= ' jltma-default-submenu'; |
| 133 |
} |
| 134 |
$class_names .= ' jltma-has-megamenu '; |
| 135 |
}else{ |
| 136 |
$class_names .= ' jltma-default-submenu'; |
| 137 |
} |
| 138 |
} |
| 139 |
|
| 140 |
// if ($depth === 0 && in_array('menu-item-has-children', $classes)) { |
| 141 |
// $class_names .= ' dropdown'; |
| 142 |
// } |
| 143 |
|
| 144 |
if (in_array('menu-item-has-children', $classes)) { |
| 145 |
$class_names .= ' dropdown'; |
| 146 |
} |
| 147 |
|
| 148 |
if ($item_meta['menu_trigger_effect'] != "") { |
| 149 |
$class_names .= ' jltma-megamenu-' . $item_meta['menu_trigger_effect']; |
| 150 |
} |
| 151 |
|
| 152 |
if ($item_meta['menu_transition'] != "") { |
| 153 |
$class_names .= ' ' . $item_meta['menu_transition']; |
| 154 |
} |
| 155 |
|
| 156 |
|
| 157 |
if ($item_meta['menu_mobile_submenu_content_type'] == 'builder_content') { |
| 158 |
$class_names .= ' jltma-mobile-builder-content'; |
| 159 |
} |
| 160 |
|
| 161 |
if (in_array('current-menu-item', $classes)) { |
| 162 |
$class_names .= ' active'; |
| 163 |
} |
| 164 |
|
| 165 |
$class_names = $class_names ? ' class="' . esc_attr($class_names) . '"' : ''; |
| 166 |
|
| 167 |
/** |
| 168 |
* Filter the ID applied to a menu item's list item element. |
| 169 |
*/ |
| 170 |
$id = apply_filters('nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth); |
| 171 |
$id = $id ? ' id="' . esc_attr($id) . '"' : ''; |
| 172 |
|
| 173 |
$output .= $indent . '<li' . $id . $class_names . $data_menu_width_type . '>'; |
| 174 |
$atts = array(); |
| 175 |
$atts['title'] = !empty($item->attr_title) ? $item->attr_title : ''; |
| 176 |
|
| 177 |
$atts['target'] = !empty($item->target) ? $item->target : ''; |
| 178 |
$atts['rel'] = !empty($item->xfn) ? $item->xfn : ''; |
| 179 |
$atts['href'] = !empty($item->url) ? $item->url : ''; |
| 180 |
|
| 181 |
$submenu_indicator = ''; |
| 182 |
|
| 183 |
// New |
| 184 |
if ($depth === 0) { |
| 185 |
$atts['class'] = 'jltma-menu-nav-link nav-link'; |
| 186 |
} |
| 187 |
|
| 188 |
if (in_array('menu-item-has-children', $classes) || $is_megamenu_item == true) { |
| 189 |
$atts['data-toggle'] = 'dropdown'; |
| 190 |
$atts['class'] .= ' jltma-menu-dropdown-toggle'; |
| 191 |
} |
| 192 |
|
| 193 |
// Build submenu indicator with mega menu icon if set |
| 194 |
if (in_array('menu-item-has-children', $classes) || $is_megamenu_item == true) { |
| 195 |
$indicator_icon = ''; |
| 196 |
// Check if mega menu has icon set |
| 197 |
if ($this->is_megamenu($menu_obj) == 1 && !empty($item_meta['menu_icon'])) { |
| 198 |
// Only apply color style if explicitly set (not default brand color) |
| 199 |
$icon_style = ''; |
| 200 |
if (!empty($item_meta['menu_icon_color']) && $item_meta['menu_icon_color'] !== '#6814cd') { |
| 201 |
$icon_style = 'color:' . $item_meta['menu_icon_color'] . ';'; |
| 202 |
} |
| 203 |
$v = explode('|', $item_meta['menu_icon']); |
| 204 |
$menu_icon_class = isset($v[1]) ? $v[0] . ' ' . $v[1] : $item_meta['menu_icon']; |
| 205 |
if (!empty($icon_style)) { |
| 206 |
$indicator_icon = '<i class="' . esc_attr($menu_icon_class) . '" style="' . esc_attr($icon_style) . '"></i>'; |
| 207 |
} else { |
| 208 |
$indicator_icon = '<i class="' . esc_attr($menu_icon_class) . '"></i>'; |
| 209 |
} |
| 210 |
} |
| 211 |
$submenu_indicator = '<span class="jltma-submenu-indicator">' . $indicator_icon . '</span>'; |
| 212 |
} |
| 213 |
if ($depth > 0) { |
| 214 |
$manual_class = array_values($classes)[0] . ' ' . 'dropdown-item'; |
| 215 |
$atts['class'] = $manual_class; |
| 216 |
} |
| 217 |
if (in_array('current-menu-item', $item->classes)) { |
| 218 |
$atts['class'] .= ' active'; |
| 219 |
} |
| 220 |
|
| 221 |
/** |
| 222 |
* Filter the HTML attributes applied to a menu item's anchor element. |
| 223 |
*/ |
| 224 |
$atts = apply_filters('nav_menu_link_attributes', $atts, $item, $args, $depth); |
| 225 |
$attributes = ''; |
| 226 |
foreach ($atts as $attr => $value) { |
| 227 |
if (!empty($value)) { |
| 228 |
$value = ('href' === $attr) ? esc_url($value) : esc_attr($value); |
| 229 |
$attributes .= ' ' . $attr . '="' . $value . '"'; |
| 230 |
} |
| 231 |
} |
| 232 |
$item_output = $args->before; |
| 233 |
|
| 234 |
$item_output .= '<a' . $attributes . '>'; |
| 235 |
$menu_obj = wp_get_nav_menu_object($args->menu); |
| 236 |
|
| 237 |
if ($this->is_megamenu($menu_obj) == 1) { |
| 238 |
|
| 239 |
// add badge text |
| 240 |
if ($item_meta['menu_badge_text'] != '') { |
| 241 |
$badge_style = 'background:' . $item_meta['menu_badge_background'] . '; color:' . $item_meta['menu_badge_color']; |
| 242 |
$badge_carret_style = 'border-top-color:' . $item_meta['menu_badge_background']; |
| 243 |
$item_output .= '<span style="' . $badge_style . '; padding: 0px 7px;font-size: 10px;border-radius: 10px;position: absolute;right: 3px;top: -7px;" class="jltma-menu-badge">' . $item_meta['menu_badge_text'] . '<i style="' . $badge_carret_style . '" class="jltma-menu-badge-arrow"></i></span>'; |
| 244 |
} |
| 245 |
|
| 246 |
if ($item_meta['menu_disable_description'] == "1") { |
| 247 |
$item_output .= (!empty($item->description) and 0 == $depth) |
| 248 |
? '<small class="jltma-menu-description">' . esc_attr($item->description) . '</small>' : ''; |
| 249 |
} |
| 250 |
} |
| 251 |
|
| 252 |
|
| 253 |
/** This filter is documented in wp-includes/post-template.php */ |
| 254 |
$item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after; |
| 255 |
|
| 256 |
if ($item_meta['menu_label_enable'] == "1") { |
| 257 |
$item_output .= '<span class="jltma-menu-label">' . $item->attr_title . '</span>'; |
| 258 |
} |
| 259 |
|
| 260 |
$item_output .= $submenu_indicator . '</a>'; |
| 261 |
$item_output .= $args->after; |
| 262 |
/** |
| 263 |
* Filter a menu item's starting output. |
| 264 |
* |
| 265 |
*/ |
| 266 |
$output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args); |
| 267 |
} |
| 268 |
/** |
| 269 |
* Ends the element output |
| 270 |
*/ |
| 271 |
public function end_el(&$output, $item, $depth = 0, $args = array()) |
| 272 |
{ |
| 273 |
if ($depth === 0) { |
| 274 |
$menu_obj = wp_get_nav_menu_object($args->menu); |
| 275 |
if ($this->is_megamenu($menu_obj) == 1) { |
| 276 |
|
| 277 |
$item_meta = $this->get_item_meta($item->ID); |
| 278 |
|
| 279 |
$menu_transition = 'fade-up'; |
| 280 |
if ($item_meta['menu_transition'] != "") { |
| 281 |
$menu_transition = $item_meta['menu_transition']; |
| 282 |
} |
| 283 |
|
| 284 |
if ($item_meta['menu_enable'] == 1 && class_exists('Elementor\Plugin')) { |
| 285 |
$width = 'max-content'; |
| 286 |
if( $item_meta['menu_width_type'] == 'custom_width' ){ |
| 287 |
if( $item_meta['menu_width_size'] != '' ){ |
| 288 |
$width = $item_meta['menu_width_size']; |
| 289 |
} |
| 290 |
} |
| 291 |
if( $item_meta['menu_width_type'] == 'full_width' ){ |
| 292 |
$width = '100%'; |
| 293 |
} |
| 294 |
|
| 295 |
$builder_post_title = 'mastermega-content-megamenu-menuitem' . $item->ID; |
| 296 |
$builder_post = Helper::get_page_by_title( $builder_post_title, 'mastermega_content' ); |
| 297 |
|
| 298 |
// Get Elementor page settings for background |
| 299 |
$megamenu_style = 'width:'.$width.';'; |
| 300 |
if ($builder_post != null) { |
| 301 |
$page_settings = get_post_meta($builder_post->ID, '_elementor_page_settings', true); |
| 302 |
if (!empty($page_settings) && is_array($page_settings)) { |
| 303 |
// Handle background color |
| 304 |
if (!empty($page_settings['background_background'])) { |
| 305 |
if ($page_settings['background_background'] === 'classic' && !empty($page_settings['background_color'])) { |
| 306 |
$megamenu_style .= 'background-color:' . esc_attr($page_settings['background_color']) . ';'; |
| 307 |
} |
| 308 |
// Handle gradient background |
| 309 |
if ($page_settings['background_background'] === 'gradient') { |
| 310 |
$gradient_color = !empty($page_settings['background_color']) ? $page_settings['background_color'] : '#fff'; |
| 311 |
$gradient_color_b = !empty($page_settings['background_color_b']) ? $page_settings['background_color_b'] : '#000'; |
| 312 |
$gradient_type = !empty($page_settings['background_gradient_type']) ? $page_settings['background_gradient_type'] : 'linear'; |
| 313 |
$gradient_angle = !empty($page_settings['background_gradient_angle']['size']) ? $page_settings['background_gradient_angle']['size'] : 180; |
| 314 |
$gradient_position = !empty($page_settings['background_gradient_position']) ? $page_settings['background_gradient_position'] : 'center center'; |
| 315 |
|
| 316 |
if ($gradient_type === 'linear') { |
| 317 |
$megamenu_style .= 'background:linear-gradient(' . esc_attr($gradient_angle) . 'deg, ' . esc_attr($gradient_color) . ', ' . esc_attr($gradient_color_b) . ');'; |
| 318 |
} else { |
| 319 |
$megamenu_style .= 'background:radial-gradient(at ' . esc_attr($gradient_position) . ', ' . esc_attr($gradient_color) . ', ' . esc_attr($gradient_color_b) . ');'; |
| 320 |
} |
| 321 |
} |
| 322 |
} |
| 323 |
// Handle background image |
| 324 |
if (!empty($page_settings['background_image']['url'])) { |
| 325 |
$megamenu_style .= 'background-image:url(' . esc_url($page_settings['background_image']['url']) . ');'; |
| 326 |
if (!empty($page_settings['background_position'])) { |
| 327 |
$megamenu_style .= 'background-position:' . esc_attr($page_settings['background_position']) . ';'; |
| 328 |
} |
| 329 |
if (!empty($page_settings['background_size'])) { |
| 330 |
$megamenu_style .= 'background-size:' . esc_attr($page_settings['background_size']) . ';'; |
| 331 |
} |
| 332 |
if (!empty($page_settings['background_repeat'])) { |
| 333 |
$megamenu_style .= 'background-repeat:' . esc_attr($page_settings['background_repeat']) . ';'; |
| 334 |
} |
| 335 |
} |
| 336 |
// Handle padding |
| 337 |
if (!empty($page_settings['padding'])) { |
| 338 |
$padding = $page_settings['padding']; |
| 339 |
if (!empty($padding['top']) || !empty($padding['right']) || !empty($padding['bottom']) || !empty($padding['left'])) { |
| 340 |
$unit = !empty($padding['unit']) ? $padding['unit'] : 'px'; |
| 341 |
$megamenu_style .= 'padding:' . |
| 342 |
(isset($padding['top']) ? $padding['top'] : '0') . $unit . ' ' . |
| 343 |
(isset($padding['right']) ? $padding['right'] : '0') . $unit . ' ' . |
| 344 |
(isset($padding['bottom']) ? $padding['bottom'] : '0') . $unit . ' ' . |
| 345 |
(isset($padding['left']) ? $padding['left'] : '0') . $unit . ';'; |
| 346 |
} |
| 347 |
} |
| 348 |
} |
| 349 |
} |
| 350 |
|
| 351 |
$output .= '<ul class="dropdown-menu jltma-megamenu ' . esc_attr($menu_transition) . '" style="' . esc_attr($megamenu_style) . '">'; |
| 352 |
if ($builder_post != null) { |
| 353 |
$elementor = \Elementor\Plugin::instance(); |
| 354 |
// Pass $with_css = true so the mega menu content's Elementor CSS is |
| 355 |
// printed inline with the markup. The walker runs in <body> after |
| 356 |
// wp_head has fired, so the external post-{id}.css enqueue is too late; |
| 357 |
// inline CSS keeps styling correct on every render regardless of cache state. |
| 358 |
$output .= $elementor->frontend->get_builder_content_for_display($builder_post->ID, true); |
| 359 |
|
| 360 |
// Register this mega menu content post with Elementor's Atomic Widgets |
| 361 |
// styles manager. Atomic Elements render class-based CSS as external |
| 362 |
// file handles enqueued on `elementor/frontend/after_enqueue_post_styles`, |
| 363 |
// collecting post IDs from `elementor/post/render`. Elementor fires both |
| 364 |
// only for the main queried post -- never for mega menu CPT posts. We are |
| 365 |
// already in the exact scope of an active, rendered mega menu item, so we |
| 366 |
// register it here (no global scan) and trigger the styles pass once in |
| 367 |
// the footer (see queue_megamenu_styles()). |
| 368 |
do_action('elementor/post/render', $builder_post->ID); |
| 369 |
self::queue_megamenu_styles(); |
| 370 |
} else { |
| 371 |
$output .= esc_html__('Menu content not found', 'master-addons' ); |
| 372 |
} |
| 373 |
|
| 374 |
$output .= '</ul>'; |
| 375 |
} |
| 376 |
} |
| 377 |
$output .= "</li>\n"; |
| 378 |
} |
| 379 |
} |
| 380 |
|
| 381 |
|
| 382 |
/** |
| 383 |
* Trigger Elementor's Atomic Widgets styles pass for the mega menu content |
| 384 |
* posts collected via `elementor/post/render` during the walk. |
| 385 |
* |
| 386 |
* The walker runs in <body>, after Elementor's head-time styles pass |
| 387 |
* (wp_enqueue_scripts priority 20) has already fired -- too late to enqueue |
| 388 |
* into <head>. So we re-fire `after_enqueue_post_styles` once on wp_footer |
| 389 |
* at priority 5, before WordPress prints late styles (wp_print_footer_scripts |
| 390 |
* at priority 20). The atomic styles manager renders the collected posts' |
| 391 |
* class-based CSS and enqueues it as late styles. No FOUC: the mega menu |
| 392 |
* dropdown is display:none until hover, so the CSS lands before any reveal. |
| 393 |
* |
| 394 |
* Hooked once per request, and only when an active mega menu actually |
| 395 |
* rendered -- pages without a mega menu do zero extra work. |
| 396 |
*/ |
| 397 |
private static $styles_queued = false; |
| 398 |
|
| 399 |
private static function queue_megamenu_styles() |
| 400 |
{ |
| 401 |
if (self::$styles_queued) { |
| 402 |
return; |
| 403 |
} |
| 404 |
self::$styles_queued = true; |
| 405 |
|
| 406 |
add_action('wp_footer', function () { |
| 407 |
do_action('elementor/frontend/after_enqueue_post_styles'); |
| 408 |
}, 5); |
| 409 |
} |
| 410 |
|
| 411 |
public static function get_instance() |
| 412 |
{ |
| 413 |
if (is_null(self::$_instance)) { |
| 414 |
self::$_instance = new self(); |
| 415 |
} |
| 416 |
return self::$_instance; |
| 417 |
} |
| 418 |
} |
| 419 |
|