Skip to content

Commit

Permalink
v3.1.0 (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
Log1x authored Oct 1, 2024
2 parents d84b4ce + 25d8931 commit ed28a3a
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 68 deletions.
90 changes: 56 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,59 +28,67 @@ Download the [latest release](https://github.com/Log1x/navi/releases/latest) `.z

## Usage

Check out the [**examples**](examples) folder to see how to use Navi in your project.

### Basic Usage
Building your menu can be done by passing your menu location to `Navi::make()->build()`:

```php
<?php

use Log1x\Navi\Navi;

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

if ($navigation->isEmpty()) {
return;
}
By default, `build()` uses `primary_navigation` if no menu location is specified.

Retrieving an array of menu items can be done using `all()`:

return $navigation->toArray();
```php
if ($menu->isNotEmpty()) {
return $menu->all();
}
```

When building the navigation menu, Navi retains the menu object and makes it available using the `get()` method.
> [!NOTE]
> Check out the [**examples**](examples) folder to see how to use Navi in your project.
By default, `get()` returns the raw[`wp_get_nav_menu_object()`](https://codex.wordpress.org/Function_Reference/wp_get_nav_menu_object) allowing you to access it directly.
### Menu Item Classes

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.
By default, Navi removes the default WordPress classes from menu items such as `menu-item` and `current-menu-item` giving you full control over your menu markup while still passing through custom classes.

If you would like these classes to be included on your menu items, you may call `withDefaultClasses()` before building your menu:

```php
$navigation->get()->name;
$navigation->get('name', 'My menu title');
$menu = Navi::make()->withDefaultClasses()->build();
```

### Acorn Usage
In some situations, plugins may add their own classes to menu items. If you would like to prevent these classes from being added, you may pass an array of partial strings to `withoutClasses()` match against when building.

If you are using Navi alongside [Acorn](https://roots.io/acorn/) (e.g. Sage), you may generate a usable view component using Acorn's CLI:
```php
$menu = Navi::make()->withoutClasses(['shop-'])->build();
```

```sh
$ acorn make:navi
### Accessing Menu Object

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

By default, `get()` returns the raw [`wp_get_nav_menu_object()`](https://codex.wordpress.org/Function_Reference/wp_get_nav_menu_object) allowing you to access it directly.

```php
$menu->get()->name;
```

Once generated, you may use the [view component](https://laravel.com/docs/11.x/blade#components) in an existing view like so:
Optionally, you may pass a `key` and `default` to call a specific object key with a fallback when the value is blank:

```php
<x-navigation />
$menu->get('name', 'My menu title');
```

### 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.

```php
# Blade
{{ get_post_type($item->objectId) }}
Below is an example of getting the post type of the current menu item:

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

### Accessing Custom Fields
Expand All @@ -90,18 +98,34 @@ In a scenario where you need to access a custom field attached directly to your
Below we'll get a label override field attached to our menu [using ACF](https://www.advancedcustomfields.com/resources/adding-fields-menus/) – falling back to the default menu label if the field is empty.

```php
# Blade
{{ get_field('custom_nav_label', $item->id) ?: $item->label }}
$label = get_field('custom_menu_label', $item->id) ?: $item->label;
```

### Acorn Usage

If you are using Navi alongside [Acorn](https://roots.io/acorn/) (e.g. Sage), you may generate a usable view component using Acorn's CLI:

# PHP
<?php echo get_field('custom_nav_label', $item->id) ?: $item->label; ?>
```sh
$ wp acorn navi:make Menu
```

Once generated, you may use the [view component](https://laravel.com/docs/11.x/blade#components) in an existing view like so:

```php
<x-menu name="footer_navigation" />
```

To list all registered locations and their assigned menus, you can use the list command:

```sh
$ wp acorn navi:list
```

## 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()`.
When calling `build()`, Navi will retrieve the WordPress navigation menu assigned to the passed location and build out an array containing the menu items.

By default, `build()` calls `primary_navigation` which is the default menu theme location on Sage.
An example of the menu output can be seen below:

```php
array [
Expand Down Expand Up @@ -177,8 +201,6 @@ 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](https://github.com/Log1x/navi/issues).
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"php": "^8.0"
},
"require-dev": {
"laravel/pint": "^1.14"
"laravel/pint": "^1.14",
"roots/acorn": "^4.3"
},
"extra": {
"acorn": {
Expand Down
2 changes: 1 addition & 1 deletion examples/vanilla/template-parts/site-nav.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<?php if ( $navigation->isNotEmpty() ) : ?>
<nav id="site-navigation" class="main-navigation">
<ul id="primary-menu">
<?php foreach ( $navigation->toArray() as $item ) : ?>
<?php foreach ( $navigation->all() as $item ) : ?>
<li class="<?php echo $item->classes; ?> <?php echo $item->active ? 'current-item' : ''; ?>">
<a href="<?php echo $item->url; ?>">
<?php echo $item->label; ?>
Expand Down
2 changes: 1 addition & 1 deletion plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Plugin Name: Navi
* Plugin URI: https://github.com/log1x/navi
* Description: A developer-friendly alternative to the WordPress NavWalker.
* Version: 3.0.3
* Version: 3.1.0
* Author: Brandon Nifong
* Author URI: https://github.com/log1x
*/
Expand Down
48 changes: 48 additions & 0 deletions src/Console/NaviListCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Log1x\Navi\Console;

use Illuminate\Console\Command;

class NaviListCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'navi:list';

/**
* The console command description.
*
* @var string
*/
protected $description = 'List registered navigation menus';

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$registered = collect(get_registered_nav_menus());
$locations = collect(get_nav_menu_locations());
$menus = collect(wp_get_nav_menus());

$rows = $registered
->map(fn ($label, $value) => $menus->firstWhere('term_id', $locations->get($value)))
->map(fn ($menu, $location) => collect([
$location,
$menu?->name ?? 'Unassigned',
$menu?->count ?? 0,
])->map(fn ($value) => $menu?->name ? $value : "<fg=red>{$value}</>"));

$this->table([
'<fg=blue>Location</>',
'<fg=blue>Assigned Menu</>',
'<fg=blue>Menu Items</>',
], $rows, tableStyle: 'box');
}
}
23 changes: 21 additions & 2 deletions src/Console/NaviMakeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,28 @@ public function handle()
return false;
}

$name = strtolower(trim($this->argument('name')));
$component = Str::of($this->argument('name'))
->lower()
->trim();

$this->components->info("Navi component <fg=blue><x-{$name} /></> is ready for use.");
$default = $this->option('default') ?? 'primary_navigation';

$locations = collect(get_registered_nav_menus())
->take(5)
->map(fn ($name, $slug) => $slug === $default
? "{$name}: <fg=blue><x-{$component} /></>"
: "{$name}: <fg=blue><x-{$component} name=\"{$slug}\" /></>"
);

$this->components->info("Navi component <fg=blue><x-{$component} /></> is ready for use.");

if ($locations->isEmpty()) {
$this->components->warn('Your theme does not appear to have any registered navigation menu locations.');

return;
}

$this->components->bulletList($locations->all());
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/Exceptions/MenuLifecycleException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Log1x\Navi\Exceptions;

use Exception;

class MenuLifecycleException extends Exception
{
//
}
8 changes: 7 additions & 1 deletion src/Facades/Navi.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
use Illuminate\Support\Facades\Facade;

/**
* @method static \Log1x\Navi\Navi build(string $menu = 'primary_navigation')
* @method static \Log1x\Navi\Navi build(mixed $menu = null)
* @method static \Log1x\Navi\Navi withClasses(string|array $classes)
* @method static \Log1x\Navi\Navi withoutClasses(string|array $classes)
* @method static \Log1x\Navi\Navi withDefaultClasses()
* @method static mixed get(string $key = null, mixed $default = null)
* @method static bool isEmpty()
* @method static bool isNotEmpty()
* @method static array all()
* @method static array toArray()
* @method static string toJson(int $options = 0)
*
* @see \Log1x\Navi\Navi
*/
Expand Down
40 changes: 22 additions & 18 deletions src/MenuBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,20 @@ class MenuBuilder
'label' => 'title',
'object' => 'object',
'objectId' => 'object_id',
'order' => 'menu_order',
'parent' => 'menu_item_parent',
'slug' => 'post_name',
'target' => 'target',
'title' => 'attr_title',
'type' => 'type',
'url' => 'url',
'xfn' => 'xfn',
'order' => 'menu_order',
];

/**
* The disallowed menu classes.
* The classes to remove from menu items.
*/
protected array $disallowedClasses = [
'current-menu',
'current_page',
'sub-menu',
'menu-item',
'menu-item-type-post_type',
'menu-item-object-page',
'menu-item-type-custom',
'menu-item-object-custom',
'menu_item',
'page-item',
'page_item',
];
protected array $withoutClasses = [];

/**
* Make a new Menu Builder instance.
Expand Down Expand Up @@ -93,7 +81,15 @@ protected function filter(array $menu = []): array
_wp_menu_item_classes_by_context($menu);

return array_map(function ($item) {
$classes = array_filter($item->classes, fn ($class) => ! in_array($class, $this->disallowedClasses));
$classes = array_filter($item->classes, function ($class) {
foreach ($this->withoutClasses as $value) {
if (str_starts_with($class, $value)) {
return false;
}
}

return true;
});

$item->classes = is_array($classes) ? implode(' ', $classes) : $classes;

Expand Down Expand Up @@ -142,10 +138,18 @@ protected function handle(array $items, string|int $parent = 0): array
$item->children = $this->handle($items, $item->id);

$menu[$item->id] = $item;

unset($item);
}

return $menu;
}

/**
* Remove classes from menu items.
*/
public function withoutClasses(array $classes = []): self
{
$this->withoutClasses = $classes;

return $this;
}
}
Loading

0 comments on commit ed28a3a

Please sign in to comment.