Skip to content

Instantly share code, notes, and snippets.

@brandy
Forked from smutek/Bootstrap4Nav-Sage9.md
Created June 29, 2018 00:21
Show Gist options
  • Save brandy/55da5b29a9b1c6ecd4a80066ff33ff71 to your computer and use it in GitHub Desktop.
Save brandy/55da5b29a9b1c6ecd4a80066ff33ff71 to your computer and use it in GitHub Desktop.
Bootstrap 4 Walker for Sage 9

Credit

This is a frankensteind version of the current Soil nav walker, by the Roots team, and Michael Remoero's Sagextras walker. All credit goes to those good folks. :)

Use

  • Replace the contents of header.blade.php with the attached header.
  • Copy the walker.php file to the /app directory.
  • Add walker.php to the Sage required files array in resources/functions.php - eg. on a stock Sage install the entry would look like:
/**
 * Sage required files
 *
 * The mapped array determines the code library included in your theme.
 * Add or remove files to the array as needed. Supports child theme overrides.
 */
array_map(function ($file) use ($sage_error) {
    $file = "../app/{$file}.php";
    if (!locate_template($file, true, true)) {
        $sage_error(sprintf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file), 'File not found');
    }
}, ['helpers', 'setup', 'filters', 'admin', 'walker']);

Basic output looks like:

- ul id="menu-main" class="navbar-nav mr-auto"
  -  li class="nav-item menu-item menu-page-1"
    - a class="nav-link" 

Also supports dropdowns.

<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="{{ home_url('/') }}">{{ get_bloginfo('name', 'display') }}</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
@if (has_nav_menu('primary_navigation'))
{!! wp_nav_menu(['theme_location' => 'primary_navigation', 'menu_class' => 'navbar-nav mr-auto']) !!}
@endif
</div>
</nav>
<?php
namespace App;
/**
* Class NavWalker
*
* Bootstrap 4 walker with cleaner markup for wp_nav_menu()
* For use with Sage >= 8.5
*
* Based on Soil NavWalker
* @url https://github.com/roots/soil
*
*
* Walker_Nav_Menu (WordPress default) example output:
* <li id="menu-item-8" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-8"><a href="/">Home</a></li>
*
* NavWalker example output:
* <li class="nav-item menu-item menu-home"><a class="nav-link" href="/">Home</a></li>
*
* @package Roots\Sage\Nav
*/
class NavWalker extends \Walker_Nav_Menu {
/**
* @var bool
*/
private $cpt; // Boolean, is current post a custom post type
/**
* @var false|string
*/
private $archive; // Stores the archive page for current URL
/**
* NavWalker constructor.
*/
public function __construct() {
add_filter( 'nav_menu_css_class', array( $this, 'cssClasses' ), 10, 2 );
add_filter( 'nav_menu_item_id', '__return_null' );
$cpt = get_post_type();
$this->cpt = in_array( $cpt, get_post_types( array( '_builtin' => false ) ) );
$this->archive = get_post_type_archive_link( $cpt );
}
/**
* Check item classes for current or active
*
* @param $classes
*
* @return int
*/
public function checkCurrent( $classes ) {
return preg_match( '/(current[-_])|active/', $classes );
}
// @codingStandardsIgnoreStart
/**
* Add dropdown menu class to dropdown UL
*
* @param string $output
* @param int $depth
* @param array $args
*/
function start_lvl( &$output, $depth = 0, $args = [] ) {
$output .= "\n<ul class=\"dropdown-menu\" aria-labelledby=\"navbarDropdownMenuLink\">\n";
}
/**
* Add required Bootstrap 4 classes to anchor links.
*
* @param string $output
* @param \WP_Post $item
* @param int $depth
* @param array $args
* @param int $id
*/
function start_el( &$output, $item, $depth = 0, $args = [], $id = 0 ) {
$item_html = '';
parent::start_el( $item_html, $item, $depth, $args );
if ( $item->is_subitem ) {
$item_html = str_replace( '<a', '<a class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"', $item_html );
$item_html = str_replace( '</a>', ' <b class="caret"></b></a>', $item_html );
} else {
$item_html = str_replace( '<a', '<a class="nav-link"', $item_html );
}
$item_html = apply_filters( 'wp_nav_menu_item', $item_html );
$output .= $item_html;
}
/**
* Add active classes to active items & sub items
*
* @param object $element
* @param array $children_elements
* @param int $max_depth
* @param int $depth
* @param array $args
* @param string $output
*/
public function display_element( $element, &$children_elements, $max_depth, $depth = 0, $args, &$output ) {
$element->is_subitem = ( ( ! empty( $children_elements[ $element->ID ] ) && ( ( $depth + 1 ) < $max_depth || ( $max_depth === 0 ) ) ) );
if ( $element->is_subitem ) {
foreach ( $children_elements[ $element->ID ] as $child ) {
if ( $child->current_item_parent || $this->url_compare( $this->archive, $child->url ) ) {
$element->classes[] = 'active';
}
}
}
$element->is_active = ( ! empty( $element->url ) && strpos( $this->archive, $element->url ) );
if ( $element->is_active ) {
$element->classes[] = 'active';
}
parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
// @codingStandardsIgnoreEnd
/**
* Clean up css classes
*
* @param $classes
* @param $item
*
* @return array
*/
public function cssClasses( $classes, $item ) {
$slug = sanitize_title( $item->title );
// Fix core `active` behavior for custom post types
if ( $this->cpt ) {
$classes = str_replace( 'current_page_parent', '', $classes );
if ( $this->url_compare( $this->archive, $item->url ) ) {
$classes[] = 'active';
}
}
// Remove most core classes
$classes = preg_replace( '/(current(-menu-|[-_]page[-_])(item|parent|ancestor))/', 'active', $classes );
$classes = preg_replace( '/^((menu|page)[-_\w+]+)+/', '', $classes );
// Add `menu-item` class & re-add core `menu-item` class
$classes[] = 'nav-item menu-item';
// Add `dropdown` class & re-add core `menu-item-has-children` class on parent elements
if ( $item->is_subitem ) {
$classes[] = 'dropdown menu-item-has-children';
}
// Add `menu-<slug>` class
$classes[] = 'menu-' . $slug;
$classes = array_unique( $classes );
$classes = array_map( 'trim', $classes );
return array_filter( $classes );
}
/**
* Make a URL relative
*
* Utility function, from soil
* @url https://github.com/roots/soil
*
* @param $input
*
* @return string
*/
public function root_relative_url( $input ) {
if ( is_feed() ) {
return $input;
}
$url = parse_url( $input );
if ( ! isset( $url['host'] ) || ! isset( $url['path'] ) ) {
return $input;
}
$site_url = parse_url( network_home_url() ); // falls back to home_url
if ( ! isset( $url['scheme'] ) ) {
$url['scheme'] = $site_url['scheme'];
}
$hosts_match = $site_url['host'] === $url['host'];
$schemes_match = $site_url['scheme'] === $url['scheme'];
$ports_exist = isset( $site_url['port'] ) && isset( $url['port'] );
$ports_match = ( $ports_exist ) ? $site_url['port'] === $url['port'] : true;
if ( $hosts_match && $schemes_match && $ports_match ) {
return wp_make_link_relative( $input );
}
return $input;
}
/**
* Compare URL against relative URL
*
* Utility function, from Soil
* @url https://github.com/roots/soil
*
* @param $url
* @param $rel
*
* @return bool
*/
public function url_compare( $url, $rel ) {
$url = trailingslashit( $url );
$rel = trailingslashit( $rel );
return ( ( strcasecmp( $url, $rel ) === 0 ) || $this->root_relative_url( $url ) == $rel );
}
}
/**
* Clean up wp_nav_menu_args
*
* Remove the container
* Remove the id="" on nav menu items
*
* @param string $args
*
* @return array
*/
function nav_menu_args( $args = '' ) {
$nav_menu_args = [];
$nav_menu_args['container'] = false;
if ( is_array($args) && !$args['items_wrap'] ) {
$nav_menu_args['items_wrap'] = '<ul class="%2$s">%3$s</ul>';
}
if ( ! $args['walker'] ) {
$nav_menu_args['walker'] = new NavWalker();
}
return array_merge( $args, $nav_menu_args );
}
add_filter( 'wp_nav_menu_args', __NAMESPACE__ . '\\nav_menu_args' );
add_filter( 'nav_menu_item_id', '__return_null' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment