salcode / bootstrap-genesis

WordPress Genesis Child Theme setup to use Bootstrap, Sass, and Grunt
MIT License
185 stars 63 forks source link

Cleanup Nav Filters #136

Open bryanwillis opened 8 years ago

bryanwillis commented 8 years ago

This gives us a lot more freedom to edit/remove the menus individually.

bryanwillis commented 8 years ago

Any thought on this?

Here's an even more advanced example... I can honestly say that this has made things so much easier when editing the menu as each menu is completely customizable individually?

/**
 * Bootstrap Nav Markup
 */
add_filter('genesis_do_nav', 'genesis_child_nav', 10, 3);
add_filter('genesis_do_subnav', 'genesis_child_nav', 10, 3);
function genesis_child_nav($nav_output, $nav, $args)
{
    $args['depth'] = 2;
    $args['menu_class'] = 'nav navbar-nav';
    $args['fallback_cb'] = 'wp_bootstrap_navwalker::fallback';
    $args['walker'] = new wp_bootstrap_navwalker();

    $nav = wp_nav_menu($args);
    $sanitized_location = sanitize_key($args['theme_location']);
    $data_target = 'nav-collapse-' . $sanitized_location;

    $nav_markup = <<<EOT
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#{$data_target}">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
EOT;
    $nav_markup.= apply_filters("bsg_navbar_brand_{$sanitized_location}", $navbar_brand);
    $nav_markup.= '</div>'; // .navbar-header

    $nav_markup.= '<div class="collapse navbar-collapse" id="' . $data_target . '">';

    ob_start();
    do_action('before_nav_' . $sanitized_location);
    $before_nav = ob_get_clean();

    ob_start();
    do_action('after_nav_' . $sanitized_location);
    $after_nav = ob_get_clean();

    $nav_markup.= $before_nav . $nav . $after_nav;

    $nav_markup.= '</div>'; // .collapse .navbar-collapse

    $nav_markup_open = sprintf('<nav %s>', genesis_attr('nav-' . $sanitized_location));
    $nav_markup_open.= genesis_structural_wrap('menu-' . $sanitized_location, 'open', 0);
    $nav_markup_close = genesis_structural_wrap('menu-' . $sanitized_location, 'close', 0) . '</nav>';

    $nav_output = $nav_markup_open . $nav_markup . $nav_markup_close;
    return $nav_output;
}

Then I also have these helper functions:

/**
 * Filter genesis_attr_structural-wrap to use BS .container-fluid classes
 */
add_filter( 'genesis_attr_structural-wrap', 'bsg_attributes_structural_wrap' );
function bsg_attributes_structural_wrap( $attributes ) {
    $attributes['class'] = 'container';
    return $attributes;
}
/**
 * Add structural-wrap replacement utility function to use 
 * Bootstrap responsive .container class
 *
 * Useage: add_filter( 'genesis_structural_wrap-{$context}', 'bsg_wrap_container_fluid');
 */
function bsg_wrap_container_fluid( $output, $original_output ) {
    if ( $original_output == 'open' ) {
        $output = sprintf( '<div %s>', genesis_attr( 'container-fluid' ) );
    }
    return $output;
}
/**
 * Helper function to easily change the nav menu location 
 */
function child_bsg_navbar_right( $nav_output, $nav ) {
    $nav_output = str_replace( 'nav navbar-nav', 'nav navbar-nav navbar-right', $nav_output );
    return $nav_output;
}
bryanwillis commented 8 years ago

Here's some examples possible with all this:

Change primary menu container to container-fluid:

add_filter( 'genesis_structural_wrap-menu-primary', 'tbg3_wrap_container_fluid', 99, 2);

Move submenu to the right side:

add_filter( 'genesis_do_subnav', 'child_bsg_navbar_right', 10, 2 );

Add the search form after the primary menu:

add_action('after_nav_menu-primary', function() { get_search_form(); });

Add to secondary brand area

add_filter('bsg_navbar_brand_menu-secondary', function() {

}
salcode commented 8 years ago

I'm still wrapping my head around this code but a couple of quick notes so I don't need to remember them for later.

PHP Warning

I'm getting the warning

call_user_func_array() expects parameter 1 to be a valid callback, function 'bsg_nav_menu' not found or invalid function name

Menus are Disappearing

When I switch to this branch, my menus disappear.

salcode commented 8 years ago

Looking at the Genesis code in lib/functions/menu.php, it looks like the filters genesis_do_nav and genesis_do_subnav are still in place for backwards compatibility. So instead it looks like we should be using the filter hooks that come from 'genesis_' . $sanitized_location . '_nav'.

salcode commented 8 years ago

I believe the Bootstrap nav only supports a depth of 2.

So, I think we should change this value from 3 to 2.

$args['depth'] = 3;
bryanwillis commented 8 years ago

Sorry about that Sal. I added a third level to my menus a while back, but didn't include that code since it wasn't part of bootstrap core and I forgot to change it back when I proposed that change.

Anyway, for reference and for anyone who would like to use three level menus with this all you need to do is change the 2 to 3 and then drop this in your functions.php (technically these should be included using wp_enqueue_scripts so compiling within theme scripts/styles is preferred since the code is dependent on bs nav javascript and jquery).

add_action('wp_head', 'bsg_multilevel_dropdown_menu_css', 999);
function bsg_multilevel_dropdown_menu_css() {
    echo '<style>ul.dropdown-menu .caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-left:4px solid;border-right:4px solid transparent;border-top:4px solid transparent;border-bottom:4px solid transparent}@media (max-width:767px){ul.dropdown-menu .caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}}ul.dropdown-menu ul.dropdown-menu{top:0;left:100%}</style>';
}

add_action('wp_footer', 'bsg_multilevel_dropdown_menu_js', 999);
function bsg_multilevel_dropdown_menu(){
    echo '<script>!function(n){"use strict";n(function(){n("ul.dropdown-menu .dropdown>a").on("click",function(o){o.preventDefault(),o.stopPropagation();var t=n(this).parent();t.parent().find("li.dropdown").not(n(this).parent()).removeClass("open"),t.toggleClass("open")}),n("ul.navbar-nav a.dropdown-toggle").on("click",function(o){var t=n(this).parent();t.parent().find("li.dropdown").not(n(this).parent()).removeClass("open")})})}(jQuery);</script>'; 
}

And here's link to uncompressed js/css for those who want to include it other ways. https://gist.github.com/bryanwillis/f4b5c25c4be8bd78d742

salcode commented 8 years ago

@bryanwillis

Cool, thanks for sharing this.

salcode commented 8 years ago

With this code change I am seeing some differences in the output nav code.

For Example

Old Output

<ul id="menu-testing-menu" class="nav navbar-nav"><li id="menu-item-1355" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1355"><a title="About" href="http://genesis.dev/about/" itemprop="url"><span itemprop="name">About</span></a></li>

New Output

<ul id="menu-testing-menu-1" class="nav navbar-nav"><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1355"><a title="About" href="http://genesis.dev/about/" itemprop="url"><span itemprop="name">About</span></a></li>

Is this a problem?

I don't think these changes are a problem but I wanted to make note and look at them more deeply before merging. You can see the work I'm doing with this PR on the branch https://github.com/salcode/bootstrap-genesis/tree/nav-filters-to-genesis-hooks-137