Coder Social home page Coder Social logo

navi's Introduction

Navi

Latest Stable Version Total Downloads Build Status

Hate the WordPress NavWalker? Me too.

Navi is a developer-friendly alternative to the NavWalker. Easily build your WordPress menus using an iterable object inside of a template/view.

Requirements

Installation

Bedrock (or Sage)

Install via Composer:

$ composer require log1x/navi

Manual

Download the latest release .zip and install into wp-content/plugins.

Usage

Check out the examples folder to see how to use Navi in your project.

Basic Usage

<?php

use Log1x\Navi\Navi;

$navigation = Navi::make()->build('primary_navigation');

if ($navigation->isEmpty()) {
  return;
}

return $navigation->toArray();

When building the navigation menu, Navi retains the menu object and makes it available using the get() method.

By default, get() returns the rawwp_get_nav_menu_object() allowing you to access it directly.

Optionally, you may pass a key and default to call a specific object key with a fallback have it be null, empty, or not set.

$navigation->get()->name;
$navigation->get('name', 'My menu title');

Acorn Usage

If you are using Navi alongside Acorn (e.g. Sage), you may generate a usable view component using Acorn's CLI:

$ acorn make:navi

Once generated, you may use the view component in an existing view like so:

<x-navigation />

Accessing Page Objects

If your menu item is linked to a page object (e.g. not a custom link) – you can retrieve the ID of the page using the objectId attribute.

# Blade
{{ get_post_type($item->objectId) }}

# PHP
<?php echo get_post_type($item->objectId); ?>

Accessing Custom Fields

In a scenario where you need to access a custom field attached directly to your menu item – you can retrieve the ID of the menu item using the id attribute.

Below we'll get a label override field attached to our menu using ACF – falling back to the default menu label if the field is empty.

# Blade
{{ get_field('custom_nav_label', $item->id) ?: $item->label }}

# PHP
<?php echo get_field('custom_nav_label', $item->id) ?: $item->label; ?>

Example Output

When calling build(), Navi will parse the passed navigation menu and return a fluent container containing your menu items. To return an array of objects, simply call ->toArray().

By default, build() calls primary_navigation which is the default menu theme location on Sage.

array [
  5 => {
    +"active": true
    +"activeAncestor": false
    +"activeParent": false
    +"classes": "example"
    +"dbId": 5
    +"description": false
    +"id": 5
    +"label": "Home"
    +"object": "page"
    +"objectId": "99"
    +"parent": false
    +"slug": "home"
    +"target": "_blank"
    +"title": false
    +"type": "post_type"
    +"url": "https://sage.test/"
    +"xfn": false
    +"order": 1
    +"parentObjectId": false
    +"children": false
  }
  6 => {
    +"active": false
    +"activeAncestor": false
    +"activeParent": false
    +"classes": false
    +"dbId": 6
    +"description": false
    +"id": 6
    +"label": "Sample Page"
    +"object": "page"
    +"objectId": "100"
    +"parent": false
    +"slug": "sample-page"
    +"target": false
    +"title": false
    +"type": "post_type"
    +"url": "https://sage.test/sample-page/"
    +"xfn": false
    +"order": 2
    +"parentObjectId": false
    +"children": array [
      7 => {
        +"active": false
        +"activeAncestor": false
        +"activeParent": false
        +"classes": false
        +"dbId": 7
        +"description": false
        +"id": 7
        +"label": "Example"
        +"object": "custom"
        +"objectId": "101"
        +"parent": 6
        +"slug": "example"
        +"target": false
        +"title": false
        +"type": "custom"
        +"url": "#"
        +"xfn": false
        +"order": 3
        +"parentObjectId": 100
        +"children": array [
          ...
        ]
      }
    ]
  }
]

That being said, depending on how deep your menu is– you can ultimately just keep looping over ->children indefinitely.

Bug Reports

If you discover a bug in Navi, please open an issue.

Contributing

Contributing whether it be through PRs, reporting an issue, or suggesting an idea is encouraged and appreciated.

License

Navi is provided under the MIT License.

navi's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

navi's Issues

An example of multiple menus?

I was wondering how we might implement a menu that has two or more menus? For example, a primary menu then also a menu for mobile only and perhaps another type of menu in the footer whereas the html output for each is different.

Undefined variable

Hi Brandon,

I've just installed latest Sage 10 release, and attempting to add back in various navs.

Unfortunately, I'm getting a weird 'Undefined variable' error:

Heres what I have stripped back to just one nav:

Composers > Navigation.php:

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use Log1x\Navi\Facades\Navi;

class Navigation extends Composer
{
    /**
     * @var array
     */
    protected static $views = [
        'partials.header.nav.upper',
    ];

    /**
     * @return array
     */
    public function with()
    {
        return [
            'upper' => $this->upper(),
        ];
    }

    /**
     * @return array
     */
    public function upper()
    {
        if (Navi::build()->isEmpty()) {
            return;
        }

        return Navi::build()->toArray();
    }
}

then in header.blade.php:

        @if ($upper)
          SHOW ME THIS TEXT IF WORKING CORRECTLY
        @endif

I've tested the same setup on an older version of Sage 10 and it works as expected.

Any idea why this may be happening on newest release (but I could be doing something stupid 🙈)?

Here is error screen attached.

Thanks

Screenshot 2021-04-02 at 01 20 00

Navi Sage 9 Examples?

Hi, I'd love to learn more about how to use this with Sage 9.
Does anyone have an examples or further documentation on how this would be setup with Sage 9?

Add type and object properties to menu item stdClass

Hey. Great library.

Any chance of adding type and object properties to the list of attributes so they get passed down from the original menu WP_Post object?

I'm working on a project where I need to change the the URL and appearance of links based on their target. I can do most of it by checking the classes property but some of the classes are being removed here so it doesn't cover all of the scenarios. For example if the target is a page the classes property is empty since they get removed since those classes are in the $disallowedClasses property.

I can do a PR but I just wanted to check if this request makes sense to you.

Thnx.

Navi for taxonomy terms

Would it be outside the scope for Navi to build hierarchical WP term objects as well?

We run into a similar pain point using get_terms or get_categories and it would be awesome to have an object structured the same way for taxonomies.

No way to get object ID's

There is currently no way to get the Post ID for the related nav item.
This prevents getting a field from a page in a menu.

Updating the parse method allows for this.

      protected function parse($items)
      {
          if (! is_array($items)) {
              return;
          }

          _wp_menu_item_classes_by_context($items);

          return $this->tree(
              collect($items)
                  ->map(function ($item) {
                      return (object) [
                          'parent' => $this->hasParent($item),
                          'id' => $item->ID,
+                         'db_id' => $item->db_id,
+                         'object_id' => $item->object_id,
                          'label' => $item->title,
                          'slug' => $item->post_name,
                          'url' => $item->url,
                          'active' => $item->current,
                          'activeAncestor' => $item->current_item_ancestor,
                          'activeParent' => $item->current_item_parent,
                          'classes' => $this->filterClasses($item->classes),
                          'title' => $item->attr_title,
                          'description' => $item->description,
                          'target' => $item->target,
                          'xfn' => $item->xfn,
                      ];
                  })
          );
      }

Nav setup

If setting up multiple navs can this be adapted to return also a secondary nav eg. footer nav? So essentially, without the need to create separate php files for each nav?

    /**
     * Returns the primary navigation.
     *
     * @return array
     */
    public function navigation()
    {
        if (Navi::build('primary')->isEmpty()) {
            return;
        }
        
        return Navi::build('primary')->toArray();
    }
}

array keys and item id values no longer match in generated array

Hi,

In the readme's example output, the array's keys and "id" values match, and correspond to the ID of the menu item created in WP. In the example, it starts at 5, 6, etc.

Since (I think) 2.0.4, the keys and id values don't match. For example, the generated array now seems to increments from 1, 2, etc.. which doesn't correspond to the IDs of the menu items created in WP.

This broke a few implementations for us, since we'd set array items based on the menu item ID (e.g. if a particular menu item should be active in certain contexts).

A quick workaround restores the previous behavior:

// generate the array like usual:
$nav = (new Navi())->build('primary')->toArray();

// workaround: set array keys to match menu item ids:
foreach ($nav as $id => $item) {
    $nav[$item->id] = $item;
    unset($nav[$id]);
}

Thanks!

WPML language switcher not showing up in menu

Hi,

I'm using WPML to make my website multilingual, but the language switcher is not showing with Navi. The switcher is present when using wp_get_nav_menu_items().

[...]
35:
ID: "wpml-ls-2-en"
attr_title: ""
classes: (8) ["menu-item", "wpml-ls-slot-2", "wpml-ls-item", "wpml-ls-item-en", "wpml-ls-current-language", "wpml-ls-menu-item", "wpml-ls-first-item", "wpml-ls-last-item"]
db_id: "wpml-ls-2-en"
description: null
menu_item_parent: 0
menu_order: 36
object: "wpml_ls_menu_item"
object_id: "wpml-ls-2-en"
post_parent: null
post_title: "<img class="wpml-ls-flag" src="http://localhost:3002/app/plugins/sitepress-multilingual-cms/res/flags/en.png" alt="English">"
post_type: "nav_menu_item"
target: null
title: "<img class="wpml-ls-flag" src="http://localhost:3002/app/plugins/sitepress-multilingual-cms/res/flags/en.png" alt="English">"
type: "wpml_ls_menu_item"
type_label: null
url: "http://localhost:3002"
xfn: null
_invalid: false
[...]

When changing:

navi/src/Builder.php

Lines 81 to 83 in 5e1e3dd

$menu = collect($menu)->filter(function ($item) {
return $item instanceof \WP_Post;
})->all();

To:

$menu = collect($menu)->filter(function ($item) {
    return $item;
})->all();

Are there possible side effects of removing: instanceof \WP_Post ?

Nav items with Icons

Brandon, is there a way to add different icons beside nav items using Navi (ps. currently using Tailwind so trying to avoid using css pseudo classes). Thanks

Parent slug isn't correct

Evening 🤗

**What I am trying to do: **
To get the parent of a child in the menus post ID.

I am creating a top level parent of the menu that only has custom links (with anchor tags) as child(s) like below.
Menu with child item

The about us page has the post ID 214 (seen in the URL):
url-slug

However when I do a <?php dump( $child); ?> from the code do I get a completely different value (215):
wrong-value

{#2770 ▼ +"active": false +"activeAncestor": false +"activeParent": false +"classes": false +"dbId": 291 +"description": false +"id": 291 +"label": "Custom link" +"objectId": "291" +"parent": "215" +"slug": "{hidden by me}" +"target": false +"title": false +"url": "#test" +"xfn": false +"children": [] }

Shouldn't not the parent: equal 214? Because I would need to get out this value from the child itself to be able to get it's parent permalink 😊 Is it something I am doing wrong or have misunderstood with the logic? I have used the objectId since its correct with the post ID for other stuff but when I am on a child and need to pick out the value from there the only reference is the parent but that value isn't correct.

PS: This is the dump for the parent itself (About us) before it continues down to the child.
dump-of-parent
{#2771 ▼ +"active": false +"activeAncestor": false +"activeParent": false +"classes": false +"dbId": 215 +"description": false +"id": 215 +"label": "About us" +"objectId": "214" +"parent": false +"slug": "215" +"target": false +"title": false +"url": "http://localhost:8181/about-us/" +"xfn": false +"children": array:1 [▼ 291 => {#2770 ▶} ] }

I also don't know if it is a similar error to what is happening for LucasDemea #58

Target class [navi] does not exist. Sage 10 release (March 1st, 2022)

With the latest release of sage 10 (yesterday), I am attempting to load this and am getting this same error.
I copied the example for sage 10 exactly and then found this thread and attempted wp acorn optimize:clear on my local and receive the following error via terminal:

/current$ wp acorn optimize:clear

Error: Your PHP installation appears to be missing the MySQL extension which is required by WordPress.

Not sure what is happening here?

WooCommerce Shop page is never active

Hello !

It's weird, when adding the WooCommerce's shop page in my menu, the item returned by navi has 'active' set to false when I am on the shop page.

Works fine for any other page.

Some kind of message when no menu assigned to location?

Hey and thanks for the work.

Would it make sense to have some kind of alert message when the user hasn't yet created a menu?

One inelegant approach would be to add code to Navi->build() something like:

 if ($menu === 0 || $menu === '' || $menu === null){
    print_r("Log1x\Navi reports: No menu assigned to location. Please assign a menu to location in the WordPress admin panel.");
    die();
 }

Nav not showing

I've registered a nav called 'Primary', added a few items, and assigned as 'Primary' nav.

Screenshot 2020-09-09 at 18 07 13

then in setup:

    register_nav_menus([
        'primary' => __('Primary', 'sage'),
    ]);

and in Composers > Navigation:

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use Log1x\Navi\Facades\Navi;

class Navigation extends Composer
{
    /**
     * List of views served by this composer.
     *
     * @var array
     */
    protected static $views = [
        'partials.header.nav.primary',
    ];

    /**
     * Data to be passed to view before rendering.
     *
     * @return array
     */
    public function with()
    {
        return [
            'primary' => $this->navigation(),
        ];
    }

    /**
     * Returns the primary navigation.
     *
     * @return array
     */
    public function navigation()
    {
        if (Navi::build()->isEmpty()) {
            return;
        }
        
        return Navi::build()->toArray();
    }
}

then in my views:

@if ($primary)
<ul 
  class="inline-block relative"
  x-data="{ open: false }"
  @click.away="open = false"
>
  @foreach ($primary as $item)
    <li class="inline-block {{ $item->classes ?? '' }} {{ $item->active ? '' : '' }}">
      <a href="{{ $item->url }}" {!! $item->children ? '@click="open = !open"' : '' !!} class="focus:outline-none cursor-pointer inline-block hover:text-g-dg flex px-3 py-3 text-pcol">
        {{ $item->label }}
      </a>
    </li>
  @endforeach
</ul>
@endif

But menu not displaying.

And even if I just have the following, nothing displays:

@if ($primary)
<h1>test</h1>
@endif

I'm using Sage 10.

Any idea what may be wrong?

Thanks.

Not compatible with Acorn ^3.0

In Providers/NaviServiceProvider.php the use should be changed from:

use Roots\Acorn\ServiceProvider;

To:

use Roots\Acorn\Sage\SageServiceProvider;

Class Navi not found

When pushing changes to my dev server I get the fatal error 'Class Navi not found', when using: @php($menu = Navi::build($name)) it works on my local just fine. I double checked composer but versions are the same, files also all seem to be there. Any ideas?

Class 'Log1x\Navi\Facades\Navi' not found

Hi, I am just getting started with Sage 10 after being a Sage 10 scaredy cat and I'm trying to use Navi.

I've registered a menu called navigation.
I've looked through the issues and I'm following this ( #23 ) and the Sage 10 example but I'm getting the error below.

Class 'Log1x\Navi\Facades\Navi' not found 
(View: /srv/www/project.com/current/web/app/themes/cck/resources/views/partials/header.blade.php) 
(View: /srv/www/project.com/current/web/app/themes/cck/resources/views/partials/header.blade.php) 
(View: /srv/www/project.com/current/web/app/themes/cck/resources/views/partials/header.blade.php)

setup.php

    register_nav_menus([
        'navigation' => __('Navigation', 'sage')
    ]);

composers > navigation.php

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use Log1x\Navi\Facades\Navi;

class Navigation extends Composer
{
    /**
     * List of views served by this composer.
     *
     * @var array
     */
    protected static $views = [
        'partials.navigation',
    ];

    /**
     * Data to be passed to view before rendering.
     *
     * @return array
     */
    public function with()
    {
        return [
            'navigation' => $this->navigation(),
        ];
    }

    /**
     * Returns the primary navigation.
     *
     * @return array
     */
    public function navigation()
    {
        if (Navi::build()->isEmpty()) {
            return;
        }

        return Navi::build()->toArray();
    }
}

navigation.blade.php

@if ($navigation)
  <ul class="my-menu">
    @foreach ($navigation as $item)
      <li class="my-menu-item {{ $item->classes ?? '' }} {{ $item->active ? 'active' : '' }}">
        <a href="{{ $item->url }}">
          {{ $item->label }}
        </a>

        @if ($item->children)
          <ul class="my-child-menu">
            @foreach ($item->children as $child)
              <li class="my-child-item {{ $child->classes ?? '' }} {{ $child->active ? 'active' : '' }}">
                <a href="{{ $child->url }}">
                  {{ $child->label }}
                </a>
              </li>
            @endforeach
          </ul>
        @endif
      </li>
    @endforeach
  </ul>
@endif

header.blade.php

<header class="banner">
  <a class="brand" href="{{ home_url('/') }}">
    {{ $siteName }}
  </a>

  @include('partials/navigation')

ACF Menu Item on Navi?

Hi Brandon,

This probably isn't an issue but I have been trying to get ACF Fields displaying on the menu items on Navi - I have tried a few different methods but struggling to get the data appearing.

What is the best way to approach this on Navi?

https://www.advancedcustomfields.com/resources/adding-fields-menu-items/

I am tying to build up a megamenu and have some fields on the menu items to determine dropdown styles.

I have tried your method from docs but this is related the standard ACF page fields rather than the menu item fields I think.

Also have Sage directives and tried with @field but no joy.

Thanks.

Navigations placed in widgets

Hi!

This is more of a feedback/idea than an issue. I wanted to share how I managed to have navi working with widgets, so that maybe you could include it somewhere in your examples.

First, to separate the logic as much as possible within my view composer, I'm including my views with a "menu" property. It's using Navi's standard which is to either receive a menu location or a menu object (WP_term).

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use Log1x\Navi\Facades\Navi;

class Navigation extends Composer
{
    /**
     * List of views served by this composer.
     *
     * @var array
     */
    protected static $views = [
        'partials.navigation',
    ];

    /**
     * Data to be passed to view before rendering.
     *
     * @return array
     */
    public function with()
    {
        $data = $this->view->getData();

        return [
            'navigation' => $this->navigation($data['menu']),
        ];
    }

    /**
     * Returns the primary navigation.
     * 
     * @param  int|string|WP_Term $menu
     * 
     * @return array
     */
    public function navigation($menu_or_location = 'primary_navigation')
    {
        if (Navi::build($menu_or_location)->isEmpty()) {
            return;
        }

        return Navi::build($menu_or_location)->toArray();
    }
}

Of course, when I'm including my navigation, I now need to pass in the location, for example, the primary nav.

@include('partials.navigation', ['menu' => 'primary_navigation'])

But after this step, it's easy to add some simple filters to functions.php or filters.php if using sage.

To make sure I don't conflict with weird nav_menus being used by some plugins and page builders, I pass a 'navi' attribute to 'widget_nav_menu_args' to make sure I really only ever target menus in widgets. I'm also forcing "echo" to true, even though it should already be true, because the 'pre_wp_nav_menu' filter needs echo to be true to process the output of our shortcircuit.

The beauty of 'pre_wp_nav_menu', if you look at the code, is really that nothing gets further processed by WordPress and that the execution of wp_nav_menu is stopped right there.

use function Roots\view;

/**
 * Use Navi for widget menus
 */
add_filter('widget_nav_menu_args', function($nav_menu_args, $nav_menu, $args, $instance) {
    $nav_menu_args['navi'] = true;
    $nav_menu_args['echo'] = true;
    return $nav_menu_args;
}, 10, 4);

add_filter('pre_wp_nav_menu', function($shortcircuit, $args) {

    if(isset($args->navi) && $args->navi) {
        $shortcircuit = view('partials.navigation', ['menu' => $args->menu])->render();
    }

    return $shortcircuit;
}, 10, 2);

Widget away! 🥳

Add 2nd level dropdown

Hi Brandon,

I'm trying to add a second level dropdown using

@if ($primary)
<div x-data="{ open: false }">
  <nav :class="{'flex': open, 'hidden': !open}" class="hidden pb-4 md:pb-0 lg:flex lg:justify-end lg:flex-row">
  <ul class="flex text-white text-sm">
    @foreach ($primary as $item)
      @if ($item->children)

        <li @click.away="open = false"
        class="relative ml-4 mr-1 hover:text-tcol"
        x-data="{ open: true, timeout: null }"
        {{-- x-data="{ open: false, timeout: null }" --}}
        x-on:mouseenter="open = true; clearTimeout(timeout)"
        {{-- x-on:mouseleave="timeout = setTimeout(() => { open = false }, 100)" --}}
        >
          <button @click="open = !open" class="flex flex-row">
          <a class="tracking-widest focus:outline-none uppercase {{ $item->classes ?? '' }} {{ $item->active ? 'text-tcol border-b-2 border-tcol pb-2' : '' }}" href="{{ $item->url }}">
            {{ $item->label }}
          </a>
          <svg
            viewBox="0 0 24 12"
            :class="{'rotate-180': open, 'rotate-0': !open}"
            class="inline w-4 h-4 transition-transform duration-200 transform text-white"
            x-cloak
            >
            <path d="M12 9.67a.68.68 0 01-.48-.19l-6-6a.68.68 0 011-1L12 8l5.52-5.52a.68.68 0 011 1l-6 6a.68.68 0 01-.52.19z" fill="currentColor"/>
          </svg>
          </button>

          <div
            x-show="open"
            class="absolute left-0 w-full mt-2 origin-top-right rounded-md shadow-lg md:w-48 z-30"
            style="display: none;"
          >
            <ul class="px-2 py-2 bg-white rounded-md shadow-2xl">
              @foreach ($item->children as $child)
              <li>
                <a class="block focus:outline-none text-pcol hover:text-scol mx-4 my-4 relative {{ $item->classes ?? '' }} {{ $child->active ? 'text-scol' : '' }}" href="{{ $child->url }}">
                  {{ $child->label }}
                </a>

                  <ul class="absolute bg-scol shadow-lg rounded w-48 p-4" style="left: 180px; top: 0;">
                  @foreach ($item->children as $child)
                    <li>
                      <a class="text-white">
                        {{ $child->label }}
                      </a>
                    </li>
                  @endforeach
                  </ul>

              </li>
              @endforeach
            </ul>
          </div>

        </li>

      @else
      <li class="mx-4 hover:text-tcol">
        <a class="tracking-widest focus:outline-none uppercase {{ $item->classes ?? '' }} {{ $item->active ? 'text-tcol border-b-2 border-tcol pb-2' : '' }}" href="{{ $item->url }}">
          {{ $item->label }}
        </a>
      </li>
      @endif
    @endforeach
    </ul>
  </nav>
</div>

@endif

but its just repeating label from first level dropdown:

<ul class="absolute bg-scol shadow-lg rounded w-48 p-4" style="left: 180px; top: 0;">
@foreach ($item->children as $child)
  <li>
    <a class="text-white">
      {{ $child->label }}
    </a>
  </li>
@endforeach
</ul>

Any idea what I'm doing wrong here?

Thanks.

Screenshot 2021-02-06 at 00 09 29

Sage 9 Integration

Probably dumb question, I have this working with Sage 10, just wondered what I need to do to get it working with Sage 9. On the docs it shows:

`use Log1x\Navi\Navi;

$navigation = (new Navi())->build('primary_navigation');`

Just wondering where this goes?

Thanks

Conflict with Polylang menu language switcher

Hi. very helpful extension.

  1. when Polylang is activated as the main localization plugin which I preferred.
  2. after added polylang Language switcher to the primary navigation rendered by Navi -
  3. and checked the option Displays as a dropdown
  4. as a result the site crashed and get into an infinite loop and acquired a fatal error
    Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 1421312 bytes) in /var/www/html/wp-content/themes/prfwp/vendor/log1x/navi/src/Navi.php on line 97

Screenshot from 2019-12-10 18-15-03

by the way, in the documentation for Sage10 the line 102 on README.md
{{ get_field('custom_nav_item_label', $child->object_id) ?: $child->label }}
are contain get_field function that should not work without ACF...

Big Thx!

Adding a menu item via a filter/dynamically

I've created a menu item using a filter that injects a link after a certain link in my menu if I use a standard wp walker I can inject my button and it displays fine.

However I think the process maybe a little different for navi is there anyway to dynamically add an item in at X location in my menu array as I don't think standard wp filters work?

PHP 8.1 deprecation notices

FYI after upgrading to PHP 8.1. I started seeing these deprecation notices:

Return type of Log1x\Navi\Navi::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice

Here’s a screenshot from Query Monitor:

Screen Shot 2022-07-09 at 2 35 55 PM

More info here https://stackoverflow.com/questions/71133749/reference-return-type-of-should-either-be-compatible-with-or-the-re

Not urgent, still works fine, just wanted to let you know. Thanks for all your work on Navi and all your other packages!

Class navi does not exist

There is an error when using page navi with Root Sage v10.

The error is located in NaviFacade.php :
protected static function getFacadeAccessor() { return 'navi'; }

The main menu is set and the doc example as been used to test it.

Plugins deactivated.

Menu depth

How would I go about displaying a menu that is 3 levels deep? I am using the sage 9 example and I can get the first two levels but not the third.

Here is my code:

    @if ($navigation->isNotEmpty())
      <ul class="my-menu">
        @foreach ($navigation->toArray() as $item)
          <li class="my-menu-item {{ $item->classes ?? '' }} {{ $item->active ? 'active' : '' }}">
            <a href="{{ $item->url }}">
              {{ $item->label }}
            </a>
            @if ($item->children)
              <ul class="my-child-menu">
                @foreach ($item->children as $child)
                  <li class="my-child-item {{ $child->classes ?? '' }} {{ $child->active ? 'active' : '' }}">
                    <a href="{{ $child->url }}">
                      {{ $child->label }}
                    </a>
                     @if ($item->children->children)
                        <ul class="my-child-menu">
                          @foreach ($item->children->children as $child)
                            <li class="my-child-item {{ $child->classes ?? '' }} {{ $child->active ? 'active' : '' }}">
                              <a href="{{ $child->url }}">
                                {{ $child->label }}
                              </a>
                            </li>
                          @endforeach
                        </ul>
                      @endif
                  </li>
                @endforeach
              </ul>
            @endif
          </li>
        @endforeach
      </ul>
    @endif

HTML encoding for navi items

Having an issue with using an ampersand within the navi items - on Navi its displays as &

Screenshot 2021-07-20 at 13 49 05

Page titles display fine though.

Screenshot 2021-07-20 at 13 50 45

Have you come across this issue before?

Thanks

No way to get the 'name' of a menu?

Great extension.

On rare occasions I need to pull the Name of the menu into the frontend, but I don't think this information gets pulled through?

Active Parent if Child isActive

Hi Buddy,

Not really an issue but can see in docs for this either - I am trying to get the Parent Item to apply th eactive style if one of the child items is active.

I have partially working with code below but must be doing something wrong.

The last item on the sub nav if active makes the parent active but all other sub nav items dont apply the active to the Parent if that makes sense.

Here is my current code (Tailwind Classes):

`@if ($primary)

    @foreach ($primary as $pitem)
  •     @if ($pitem->children)
          <ul class="z-110 shadow-2xl rounded-lg absolute w-48 bg-white hidden overflow-hidden" style="top: calc(100% + 10px)">
            @foreach ($pitem->children as $child)
              <li class="block bg-none {{ $child->active ? 'active' : '' }}">
                <a href="{{ $child->url }}" class="block py-2 px-3 text-pcol normal-case text-base hover:bg-g-lg hover:text-scol {{ $child->active ? 'text-scol' : '' }}">
                  {{ $child->label }}
                </a>
              </li>
            @endforeach
          </ul>
          @if ($child->active)              
          <a href="{{ $pitem->url }}" class="text-scol hover:text-scol block text-lg p-1 tracking-wide">
            {{ $pitem->label }}
          </a>
          @else
          <a href="{{ $pitem->url }}" class="text-pcol hover:text-scol block text-lg p-1 tracking-wide">
            {{ $pitem->label }}
          </a>
          @endif
    
        @else
          <a href="{{ $pitem->url }}" class="text-pcol hover:text-scol block text-lg p-1 tracking-wide {{ $pitem->classes ?? '' }} {{ $pitem->active ? 'text-scol border-b-2 border-scol' : '' }}">
            {{ $pitem->label }}
          </a>
        @endif
      </li>
    @endforeach
    
@endif`

Register a second navigation with Sage 10 using Navi

I'm looking to create a second nav called 'offcanvas' in Sage 10 and in my 'Navigation.php' I have the following:

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use Log1x\Navi\Facades\Navi;

class Navigation extends Composer
{
    protected static $views = [
        'partials.navigation',
        'partials.offcanvas',
    ];

    public function with()
    {
        return [
            'navigation' => $this->navigation(),
            'offcanvas' => $this->navigation(),
        ];
    }

    public function navigation()
    {
        if (Navi::build()->isEmpty()) {
            return;
        }
        
        return Navi::build()->toArray();
    }
}

and then, on my view offcanvas partial I have:

@if ($offcanvas)
  <ul 
  x-show="isOpen"
  class="offcanvas absolute top-0 left-0 w-screen p-10 bg-gray-900 text-white mt-16"
  @click.away="isOpen = false"
  >
    @foreach ($offcanvas as $item)
      <li {{ $item->classes ?? '' }} {{ $item->active ? '' : '' }}">
        <a href="{{ $item->url }}" {!! $item->children ? '@click="open = !open"' : '' !!} class="focus:outline-none cursor-pointer inline-block text-white hover:text-gray-500 flex px-3 py-3">
          {{ $item->label }}
        </a>
      </li>
    @endforeach
  </ul>
@endif

Unfortunately, it's still showing the nav items from the first nav I registered. Is there something I need to alter on Navigation.php to make this work correctly?

Thanks

Navi with Tailwind and Alpine

Sorry, I know this is not a Navi specific issue but I'm just wondering if you could advise on this?

I'm trying out Navi with Tailwind and Alpine. I have a basic version sorta working but unfortunately I'm struggling with a few issues:

  1. When I click parent nav items that have no child items it still triggers the dropdown 😔
@if ($navigation)
  <ul class="inline-block relative" x-data="{open: false}">
    @foreach ($navigation as $item)
      <li class="inline-block {{ $item->classes ?? '' }} {{ $item->active ? '' : '' }}">
        <a href="{{ $item->url }}" @click="open = !open" class="focus:outline-none cursor-pointer inline-block text-white hover:text-indigo-600 flex px-4 py-3">
          {{ $item->label }}
        </a>

        @if ($item->children)
        <ul x-show="open" class="bg-white absolute mt-2 shadow rounded w-60 py-1 text-indigo-600"
          x-transition:enter="transition ease-out duration-300"
          x-transition:enter-start="opacity-0 transform -translate-y-2"
          x-transition:enter-end="opacity-100 transform translate-y-0"
          x-transition:leave="transition ease-in duration-300"
          x-transition:leave-end="opacity-0 transform -translate-y-3"
            >
            @foreach ($item->children as $child)
              <li class="my-child-item px-5 py-3 {{ $item->classes ?? '' }} {{ $child->active ? '' : '' }}">
                <a href="{{ $child->url }}">
                  {{ $child->label }}
                </a>
              </li>
            @endforeach
          </ul>
        @endif

      </li>
    @endforeach
  </ul>
@endif
  1. I'm also looking to add an SVG dropdown arrow on the parent item which has child nav items, but just don't know where it would go on Navi eg:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" :class="{'rotate-180': open}" class="ml-1 transform inline-block fill-current text-gray-500 w-6 h-6"><path fill-rule="evenodd" d="M15.3 10.3a1 1 0 011.4 1.4l-4 4a1 1 0 01-1.4 0l-4-4a1 1 0 011.4-1.4l3.3 3.29 3.3-3.3z"/></svg>

Any help would be much appreciated as I've gone around in circles on this for the past few days.

Thanks

current_page_parent css class on menus with CPT

I'm using Navi to build my main menu. In this menu there is the regular Blog menu and also a menu corresponding to a CPT.
The problem is when I'm on CPT single page, the Blog menu gets the current_page_parent class.

So I googled and find that I can use this nav_menu_css_class filter to remove the class current_page_parent from the blog menu when I'm in a CPT.

This works fine when I render the menu with wp_nav_menu, but if I use Navi, the blog menu keeps getting the current_page_parent class.

Is there a way to handle this in Navi? Is Navi ignoring the nav_menu_css_class filter or I need to do it some other way?
Maybe I'm missing something...

Thanks!

Add more values from WP_Post?

Hi there - I really appreciate the repo!

Wondering if you would consider adding current_item_ancestor and current_item_parent coming from the WP_Post object as values to the object you construct in the parse() method. Currently there is only an active value set from WP_Post current.

This would enable more flexibility when building out hierarchical "active" states in navigation menus. I often leverage some of the other items stored in WP_Post too.

Thanks for your consideration

Add target attribute

Is there a way for Navi to include 'target' attribute to menu links? I need just one item to go to target _BLANK.

Active items are shown as "false"

It is happening to me that no item is shown active. It didn't happen before, maybe with previous versions of WP.

The screenshot shows how being on the "Noticias" page, the "Noticias" item has active: false

WP 6.3
Navi 2.0.4

Captura de pantalla 2023-08-20 a las 17 40 26

Error with polylang

Hello,

As soon as you activate "polylang" plugin, you systematically get the following error:

Log1x\Navi\MenuBuilder::handle(): Argument #2 ($parent) must be of type int, string given, called in...

Would be great if Navi v3 continues to work with polylang as before.

(I am using latest sage, acorn and navi versions as of today.)

Thank you !

Docblocks indicate `Navi->items` should be array but can be null

Hi, thanks for the package!

Noticed a very minor issue. The docblocks for Log1x\Navi\Navi->items indicate that the property should be an array, and instantiates it as an array.

When you build a menu (new Navi())->build('menu_name'), if that menu doesn't have items, the builder function returns void instead and items is set to null. See line 66 below:

navi/src/MenuBuilder.php

Lines 56 to 73 in 555653b

/**
* Build a filtered array of objects containing the navigation menu items.
*
* @param array $menu
* @return array
*/
public function build($menu)
{
$this->menu = $this->filter((array) $menu);
if (empty($this->menu)) {
return;
}
return $this->tree(
$this->map($this->menu)
);
}

I could see two approaches:

  • Updating the docblocks for Navi->items (and related functions like Navi->toArray() and MenuBuilder->build()) to indicate that the value can be null, or:
  • Updating MenuBuilder->build() line 66 to return [] instead of return

The latter option seems to make more sense to me but is potentially a breaking change if anyone is using strict comparison for null checking, e.g. Navi->toArray() === null or Navi->getItems() === null

Happy to submit a PR if there's a preferred approach.

Tailwind Active Class

Using Tailwind, I'm trying to add an 'active' background class to a parent item (so it shows different class based on the page your currently on), heres what I've tried but not showing a change:

<li class="inline my-menu-item px-8 active:bg-indigo-400 {{ $item->active ? 'active' : '' }}">

and also this

<li class="inline my-menu-item px-8 {{ $item->active ? 'active:bg-indigo-400' : '' }}">

Any ideas why this may not work?

Thanks.

Target class [navi] does not exist.

Hello there. I followed along with your sample code for sage10 and I'm getting this error:

Target class [navi] does not exist.

I cut and pasted in the sample code exactly and it's being called with the following code in the header.blade.php file:

@include('partials.navigation')

Any idea what could be causing it? I'm suing the dev-main version of Sage 10 just downloaded today.

Thanks so much for your help.

Post IDs as keys re-sort in Javascript

At first I thought I could solve this with a simple array_values()call, and I did for the top level menu items. But when I finally needed to put my children menu items into a vue loop, the key it came away with was the WP_Post ID for the nav item and NOT the order that the menu item is set to.

Order in a PHP Array:
image

Order in a Javascript object:
image

So my first thought was to iterate over the entire menu recursively and do array_values() on each children array...but then I was like...thats gross...can't I just set it to an "in order" list in the first place? So I dug around and changed the tree function to this:

protected function tree($items, $parent = 0, $branch = [])
    {
        foreach ($items as $key => $item) {
            if ($item->parent == $parent) {
                $children = $this->tree($items, $item->id);
                $item->children = ! empty($children) ? $children : [];

                $branch[$key] = $item;
                unset($item);
            }
        }

        return $branch;
    }

I wanted to be able to make this an option but I didn't see any existing "options" like things, and unless the cleanest thing to do is to pass a boolean all the way from the Navi instance like (new Navi())->build('primary_navigation', true) and have it end up in the tree function, then I don't have a clean way to implement.

I would almost prefer at this point a better way of doing an array_values() on each children item, and maybe just articulating it here will help.

The closest I got was this recursive array function, but it just array_values's everything indiscriminately, so theres no useful data afterwards...I tried to get it to only do "children" elements but...no such luck...

// I found this online, I didn't make this up
function array_values_recursive( $array ) {
    $array = array_values( $array );
    for ( $i = 0, $n = count( $array ); $i < $n; $i++ ) {
        $element = $array[$i];
        if ( is_array( $element ) ) {
            $array[$i] = array_values_recursive( $element );
        }
    }
    return $array;
}

image

Wrong term slug

Hi, thank you for this nice package !
When a menu contains taxonomy terms, the returned slug is not correct :
image

Custom classes excluded

Hey @Log1x! 👋🏻

It seems custom classes added to nav items aren't being added to $item->classes.

Screenshot 2021-04-21 at 17 08 00


Had a quick scan through the code and noticed this is excluding everything except what's in the exclusion list? Unless I'm misreading it.

https://github.com/Log1x/navi/blob/master/src/MenuBuilder.php#L90-L95

$classes = array_filter($item->classes, function ($class) {
    return array_key_exists(
        $class,
        array_flip($this->classes)
    );
});

Excluded classes (however they're being allowed). Not sure if this is intentional and simply a typo in the comment:
https://github.com/Log1x/navi/blob/master/src/MenuBuilder.php#L37-L50

/**
  * Blacklisted Classes
  *
  * @var array
  */
protected $classes = [
    'current-menu',
    'current_page',
    'sub-menu',
    'menu-item',
    'menu_item',
    'page-item',
    'page_item',
];

I'll try debugging a bit later on and pop a PR over if I can fix, just thought I'd submit this issue in case you know of a solution off the top of your head.

Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.