Skip to content

Instantly share code, notes, and snippets.

@nickfmc
Last active February 13, 2026 04:26
Show Gist options
  • Select an option

  • Save nickfmc/e319e455da9e7895483d7c3a80e5b82c to your computer and use it in GitHub Desktop.

Select an option

Save nickfmc/e319e455da9e7895483d7c3a80e5b82c to your computer and use it in GitHub Desktop.
<?php
/**
* GenerateBlocks Pro - Theme Integration
*
* Custom Walker for WordPress Navigation with Mega Menu Support
*
* This snippet allows you to use GenerateBlocks Pro overlay panels as mega menus
* with standard WordPress navigation (wp_nav_menu) without requiring the
* GenerateBlocks Navigation block.
*
* INSTRUCTIONS:
* 1. Copy this file to your theme directory (e.g., /wp-content/themes/your-theme/)
* 2. Include it in your theme's functions.php:
* require_once get_template_directory() . '/theme_integration.php';
* 3. Use the walker in your wp_nav_menu() calls (see example at bottom)
*
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Custom Walker for GenerateBlocks Pro Mega Menus
*
* Extends WordPress's default menu walker to add support for overlay panels
* configured as mega menus in the WordPress menu editor.
*/
class GB_Pro_Mega_Menu_Walker extends Walker_Nav_Menu {
/**
* Mega menu trigger type (click or hover)
*
* @var string
*/
private $trigger_type = 'click';
/**
* Cache for mega menu visibility checks
*
* @var array
*/
private static $mega_menu_cache = [];
/**
* Constructor
*
* @param string $trigger_type The trigger type for mega menus ('click' or 'hover').
*/
public function __construct( $trigger_type = 'click' ) {
$this->trigger_type = in_array( $trigger_type, [ 'click', 'hover' ], true ) ? $trigger_type : 'click';
}
/**
* Check if a mega menu should be displayed
*
* Checks if the overlay exists, is published, and passes any display conditions.
*
* @param int $overlay_id The overlay post ID.
* @return bool Whether the mega menu should be displayed.
*/
private function should_display_mega_menu( $overlay_id ) {
// Check cache first.
if ( isset( self::$mega_menu_cache[ $overlay_id ] ) ) {
return self::$mega_menu_cache[ $overlay_id ];
}
// Check if overlay exists and is published.
$overlay_post = get_post( $overlay_id );
if ( ! $overlay_post || 'gblocks_overlay' !== $overlay_post->post_type || 'publish' !== $overlay_post->post_status ) {
self::$mega_menu_cache[ $overlay_id ] = false;
return false;
}
// Check display conditions if GenerateBlocks Pro is active.
if ( class_exists( 'GenerateBlocks_Pro_Overlays' ) && class_exists( 'GenerateBlocks_Pro_Conditions' ) ) {
$display_condition = GenerateBlocks_Pro_Overlays::get_overlay_meta( $overlay_id, '_gb_overlay_display_condition' );
$display_conditions = [];
if ( $display_condition ) {
$condition_post = get_post( $display_condition );
if ( $condition_post && 'publish' === $condition_post->post_status ) {
$display_conditions = get_post_meta( $display_condition, '_gb_conditions', true ) ?? [];
}
}
$show = true;
if ( ! empty( $display_conditions ) ) {
$show = GenerateBlocks_Pro_Conditions::show( $display_conditions );
$invert_condition = GenerateBlocks_Pro_Overlays::get_overlay_meta( $overlay_id, '_gb_overlay_display_condition_invert' );
// If invert is enabled, flip the result.
if ( $invert_condition ) {
$show = ! $show;
}
}
self::$mega_menu_cache[ $overlay_id ] = $show;
return $show;
}
// Default to showing if no conditions system is available.
self::$mega_menu_cache[ $overlay_id ] = true;
return true;
}
/**
* Starts the element output.
*
* Adds mega menu support to menu items by:
* - Adding data attributes for overlay triggers
* - Adding ARIA attributes for accessibility
* - Outputting the mega menu overlay content
*
* @param string $output Used to append additional content (passed by reference).
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $id Current item ID.
*/
public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
// Get mega menu ID from post meta.
$mega_menu_id = get_post_meta( $item->ID, '_gb_mega_menu', true );
$has_mega_menu = $mega_menu_id && $this->should_display_mega_menu( $mega_menu_id );
// Build the standard menu item.
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classes = empty( $item->classes ) ? [] : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
// Add mega menu class if applicable.
if ( $has_mega_menu ) {
$classes[] = 'menu-item-has-mega-menu';
}
/**
* Filters the arguments for a single nav menu item.
*
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
*/
$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
/**
* Filters the CSS classes applied to a menu item's list item element.
*
* @param string[] $classes Array of the CSS classes that are applied to the menu item's `<li>` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
/**
* Filters the ID applied to a menu item's list item element.
*
* @param string $menu_id The ID that is applied to the menu item's `<li>` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $class_names . '>';
$atts = [];
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
// Add mega menu attributes.
if ( $has_mega_menu ) {
$overlay_id = 'gb-overlay-' . $mega_menu_id;
// Create a unique ID for this menu link that can be referenced for positioning.
$anchor_id = 'gb-menu-anchor-' . $item->ID;
$atts['id'] = $anchor_id;
// Required attributes for mega menu functionality.
$atts['data-gb-overlay'] = $overlay_id;
$atts['data-gb-overlay-trigger-type'] = $this->trigger_type;
$atts['aria-controls'] = $overlay_id;
$atts['aria-haspopup'] = 'menu';
// Add role and aria-expanded for click-based menus.
if ( 'click' === $this->trigger_type ) {
$atts['role'] = 'button';
$atts['aria-expanded'] = 'false';
}
}
/**
* Filters the HTML attributes applied to a menu item's anchor element.
*
* @param array $atts {
* The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
*
* @type string $title Title attribute.
* @type string $target Target attribute.
* @type string $rel The rel attribute.
* @type string $href The href attribute.
* @type string $aria_current The aria-current attribute.
* }
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
/** This filter is documented in wp-includes/post-template.php */
$title = apply_filters( 'the_title', $item->title, $item->ID );
/**
* Filters a menu item's title.
*
* @param string $title The menu item's title.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
$item_output = $args->before ?? '';
$item_output .= '<a' . $attributes . '>';
$item_output .= ( $args->link_before ?? '' ) . $title . ( $args->link_after ?? '' );
$item_output .= '</a>';
$item_output .= $args->after ?? '';
// Output the mega menu overlay content.
if ( $has_mega_menu ) {
$action_id = 'gb-mega-menu-' . $mega_menu_id;
ob_start();
/**
* Fires when outputting a mega menu overlay.
*
* GenerateBlocks Pro hooks into this action to output the overlay content.
*
* @param WP_Post $item The menu item object.
*/
do_action( $action_id, $item );
$mega_menu_content = ob_get_clean();
$item_output .= $mega_menu_content;
}
/**
* Filters a menu item's starting output.
*
* The menu item's starting output only includes `$args->before`, the opening `<a>`,
* the menu item's title, the closing `</a>`, and `$args->after`. Currently, there is
* no filter for modifying the opening and closing `<li>` for a menu item.
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/**
* USAGE EXAMPLE
*
* Copy the code below to your theme template file (e.g., header.php) where you
* want to display the navigation menu with mega menu support.
*
* ----------------------------------------------------------------------------
*
* Example 1: Click-based mega menus (default)
*
* wp_nav_menu( array(
* 'theme_location' => 'primary',
* 'walker' => new GB_Pro_Mega_Menu_Walker( 'click' ),
* 'container' => 'nav',
* 'menu_class' => 'main-navigation',
* ) );
*
* ----------------------------------------------------------------------------
*
* Example 2: Hover-based mega menus
*
* wp_nav_menu( array(
* 'theme_location' => 'primary',
* 'walker' => new GB_Pro_Mega_Menu_Walker( 'hover' ),
* 'container' => 'nav',
* 'menu_class' => 'main-navigation',
* ) );
*
* ----------------------------------------------------------------------------
*
* CONFIGURING MEGA MENUS:
*
* 1. Go to Appearance > Menus in your WordPress admin
* 2. Edit a menu item and expand it
* 3. You'll see a "Mega Menu" dropdown field (added by GenerateBlocks Pro)
* 4. Select the overlay panel you want to use as a mega menu
* 5. Save the menu
*
* The mega menu will now appear when users interact with that menu item
* (either click or hover, depending on your walker configuration).
*
* ----------------------------------------------------------------------------
*
* POSITIONING:
*
* Mega menu positioning is controlled in the overlay panel editor, not in the menu settings.
*
* To configure positioning:
* 1. Edit your overlay panel (the one set as type "Mega Menu")
* 2. In the panel settings, configure:
* - PLACEMENT: Choose where the overlay appears (Top Right, Top Left, Bottom Right, etc.)
* - POSITION TO PARENT: Enter a CSS selector to position relative to a specific element
*
* Example positioning configurations:
* - Position relative to the menu link: Use "#gb-menu-anchor-{MENU_ITEM_ID}"
* (each menu link automatically gets an ID like gb-menu-anchor-123)
* - Position relative to the menu item's <li>: Use ".menu-item-{MENU_ITEM_ID}"
* - Position relative to the entire nav: Use your nav's ID or class (e.g., "#main-nav")
* - Position relative to site header: Use your header selector (e.g., ".site-header")
*
* The walker automatically adds an ID to each menu link (gb-menu-anchor-{item-id}) which
* you can reference in your overlay positioning settings if needed.
*
* ----------------------------------------------------------------------------
*
* REQUIREMENTS:
* - GenerateBlocks Pro plugin must be active
* - You must have created overlay panels with the type set to "Mega Menu"
* - The overlays must be assigned to menu items in the WordPress menu editor
*
* ----------------------------------------------------------------------------
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment