Skip to content

Commit 44a6bd8

Browse files
committed
Adds support for submenu classes thanks to new filters released in WordPress 6.3
1 parent d599c7a commit 44a6bd8

File tree

2 files changed

+126
-87
lines changed

2 files changed

+126
-87
lines changed

README.md

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# WordPress Menu Classes
22

3-
Allow adding custom classes to WordPress menu ul, li, a and at different depths. Perfect for TailwindCSS and AlpineJS usage.
3+
Allow adding custom classes to WordPress li, a and submenus at different depths. Perfect for TailwindCSS and AlpineJS usage.
44

55
This package adds WordPress filters to allow custom arguments to wp_nav_menu to, in turn, allow custom classes to every element of a menu. You can apply a class only to certain depth of your menu as well.
66

@@ -18,22 +18,25 @@ Install via Composer:
1818
$ composer require davidwebca/wordpress-menu-classes
1919
```
2020

21-
If your theme already uses composer, the filters will be automatically added thanks to the auto-loading and auto-instantiating class. Otherwise, if you're looking for a standalone file you want to add to your theme, either look for src/WordPressMenuClasses.php in this repository or add this [gist](https://gist.github.com/davidwebca/a7b278bbb0c0ce1d1ec5620126e863bb) in your theme's functions.php.
21+
If your theme already uses composer, the filters will be automatically added thanks to the auto-loading and auto-instantiating class. Otherwise, if you're looking for a standalone file you want to add to your theme, either look for src/WordPressMenuClasses.php in this repository.
2222

2323
## Instructions
2424

2525
The filters use the depth argument given by WordPress which is an index, thus starts with level 0 (zero).
2626

2727
Here's a list of the custom arguments you can pass to wp_nav_menu that are supported by this package :
2828

29-
- ```link_atts``` or ```link_atts_$depth``` or ```link_atts_order_$order```
30-
- Add any attribute to ```<a>``` elements
29+
- ```a_atts``` or ```a_atts_$depth``` or ```a_atts_order_$order```
3130
- ```a_class``` or ```a_class_$depth``` or ```a_class_order_$order```
32-
- Add classes to ```<a>``` elements
31+
- Add any attribute or class to ```<a>``` elements
32+
33+
- ```li_atts``` or ```li_atts_$depth``` or ```li_atts_order_$order```
3334
- ```li_class``` or ```li_class_$depth``` or ```li_class_order_$order```
34-
- Add classes to ```<li>``` elements
35+
- Add any attribute or class to ```<li>``` elements
36+
37+
- ```submenu_atts``` or ```submenu_atts_$depth```
3538
- ```submenu_class``` or ```submenu_class_$depth```
36-
- Add classes to submenu ```<ul>``` elements
39+
- Add any attribute or class to submenu ```<ul>``` elements. Note that submenus do not support order.
3740

3841
Ex.: add a "text-black" class to all links and "text-blue" class only to 3rd level links
3942

@@ -46,24 +49,39 @@ wp_nav_menu([
4649
]);
4750
```
4851

49-
Ex.: More complete example with some TailwindCSS classes and AlpineJS sugar
52+
Ex.: Supports classes as array (this is non-native to WordPress, but provided by this package as a convenience)
53+
54+
```php
55+
wp_nav_menu([
56+
'theme_location' => 'primary_navigation',
57+
'a_class' => ['text-white', 'bg-blue-500'],
58+
'li_atts' => [
59+
'class' => ['focus:ring-2', 'ring-orange-500']
60+
]
61+
// ...
62+
]);
63+
```
64+
65+
Ex.: More complete example with some TailwindCSS classes and AlpineJS sugar. This is a fully functional accordion navigation without additional JavaScript (requires Alpine's x-collapse plugin).
5066

5167
```php
5268
wp_nav_menu([
5369
'theme_location' => 'primary_navigation',
54-
'menu_class' => 'relative w-full z-10 pl-0 list-none flex',
55-
'link_atts_0' => [
56-
":class" => "{ 'active': tab === 'foo' }",
57-
"@click" => "tab = 'foo'"
70+
'container' => 'nav',
71+
'menu_class' => 'list-none p-0 m-0',
72+
'a_class_0' => "font-bold inline-flex items-center text-xl",
73+
'li_atts_0' => [
74+
'class' => "w-full px-6 before:mr-4 before:cursor-pointer before:shrink-0 before:grow-0 before:inline-flex before:justify-center before:items-center before:w-6 before:h-6 before:rounded before:bg-black before:text-white before:p-1 before:hover:opacity-50 before:transition",
75+
':class' => "{'before:content-[\'+\']': !opened, 'before:content-[\'-\']': opened}",
76+
'x-data' => "{opened: false}",
77+
'x-on:click' => 'opened = !opened'
5878
],
59-
'li_class' => 'w-full',
60-
'li_class_0' => 'mb-12',
61-
'a_class' => 'text-sm xl:text-xl text-white border-b hover:border-white',
62-
'a_class_0' => 'text-3xl xl:text-5xl relative after:bg-primary',
63-
'li_class_1' => 'after:bg-primary hidden lg:block',
64-
'a_class_1' => 'flex h-full items-center uppercase py-2 relative border-white border-opacity-40 hover:border-opacity-100',
65-
'submenu_class' => 'list-none pl-0 grid grid-cols-1 lg:grid-cols-2 lg:gap-x-12 xl:gap-x-24 xxl:gap-x-32',
66-
'container'=>false
79+
'submenu_class_0' => 'wowza',
80+
'submenu_atts_0' => [
81+
'x-show' => 'opened',
82+
'x-collapse' => "_",
83+
'class' => 'list-none pl-10'
84+
]
6785
]);
6886
```
6987

src/WordPressMenuClasses.php

Lines changed: 88 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class WordPressMenuClasses
1212
public function __construct()
1313
{
1414
add_filter('nav_menu_link_attributes', [$this, 'navMenuLinkAttributes'], 10, 4);
15-
add_filter('nav_menu_css_class', [$this, 'navMenuCSSClass'], 10, 4);
16-
add_filter('nav_menu_submenu_css_class', [$this, 'navMenuSubmenuCSSClass'], 10, 3);
15+
add_filter('nav_menu_item_attributes', [$this, 'navMenuItemAttributes'], 10, 4);
16+
add_filter('nav_menu_submenu_attributes', [$this, 'navSubmenuAttributes'], 10, 3);
1717
}
1818

1919
/**
@@ -30,41 +30,8 @@ public function navMenuLinkAttributes($atts, $item, $args, $depth)
3030
{
3131
$index = $item->menu_order;
3232

33-
if (property_exists($args, 'link_atts')) {
34-
$atts = array_merge($atts, $args->link_atts);
35-
}
36-
if (property_exists($args, "link_atts_$depth")) {
37-
$atts = array_merge($atts, $args->{"link_atts_$depth"});
38-
}
39-
if (property_exists($args, "link_atts_order_$index")) {
40-
$atts = array_merge($atts, $args->{"link_atts_order_$index"});
41-
}
42-
43-
if (empty($atts['class'])) {
44-
$atts['class'] = '';
45-
}
46-
47-
$classes = explode(' ', $atts['class']);
48-
49-
if (property_exists($args, 'a_class')) {
50-
$arr_classes = explode(' ', $args->a_class);
51-
$classes = array_merge($classes, $arr_classes);
52-
}
53-
if (property_exists($args, "a_class_$depth")) {
54-
$arr_classes = explode(' ', $args->{"a_class_$depth"});
55-
$classes = array_merge($classes, $arr_classes);
56-
}
57-
if (property_exists($args, "a_class_order_$index")) {
58-
$arr_classes = explode(' ', $args->{"a_class_order_$index"});
59-
$classes = array_merge($classes, $arr_classes);
60-
}
61-
62-
// Applying this here too just in case, but there's
63-
// no default user interface to add a class directly to a link in the menu
64-
// (classes are applied to li elements by default)
65-
$classes = $this->fixWordPressClasses($classes);
66-
67-
$atts['class'] = implode(' ', $classes);
33+
$atts = $this->buildAttributes('a', $atts, $args, $depth, $index);
34+
$atts = $this->buildClasses('a', $atts, $args, $depth, $index);
6835

6936
return $atts;
7037
}
@@ -79,54 +46,108 @@ public function navMenuLinkAttributes($atts, $item, $args, $depth)
7946
* This is an index, thus starts with 0 for the root level.
8047
* @return array Modified classes for the current li element
8148
*/
82-
public function navMenuCSSClass($classes, $item, $args, $depth)
49+
public function navMenuItemAttributes($atts, $item, $args, $depth)
8350
{
8451
$index = $item->menu_order;
8552

86-
if (property_exists($args, 'li_class')) {
87-
$arr_classes = explode(' ', $args->li_class);
88-
$classes = array_merge($classes, $arr_classes);
89-
}
90-
if (property_exists($args, "li_class_$depth")) {
91-
$arr_classes = explode(' ', $args->{"li_class_$depth"});
92-
$classes = array_merge($classes, $arr_classes);
93-
}
94-
if (property_exists($args, "li_class_order_$index")) {
95-
$arr_classes = explode(' ', $args->{"li_class_order_$index"});
96-
$classes = array_merge($classes, $arr_classes);
97-
}
98-
99-
100-
$classes = $this->fixWordPressClasses($classes);
53+
$atts = $this->buildAttributes('li', $atts, $args, $depth, $index);
54+
$atts = $this->buildClasses('li', $atts, $args, $depth, $index);
10155

102-
return $classes;
56+
return $atts;
10357
}
10458

10559
/**
106-
* Add custom classes to ul.sub-menu in wp_nav_menu
60+
* Add custom classes and attributes to ul.submenu in wp_nav_menu
10761
*
108-
* @param array $classes CSS classes added to all ul submenu of our menu.
109-
* @param object $args wp_nav_menu args object
110-
* @param int $depth Depth of the current menu item being parsed.
62+
* @param object $atts wp_nav_menu attributes object
63+
* @param object $args wp_nav_menu args object
64+
* @param int $depth Depth of the current submenu being parsed.
11165
* This is an index, thus starts with 0 for the root level.
11266
* @return object Modified attributes for the current ul submenu
11367
*/
114-
public function navMenuSubmenuCSSClass($classes, $args, $depth)
68+
public function navSubmenuAttributes($atts, $args, $depth)
11569
{
116-
if (property_exists($args, 'submenu_class')) {
117-
$arr_classes = explode(' ', $args->submenu_class);
118-
$classes = array_merge($classes, $arr_classes);
70+
$atts = $this->buildAttributes('submenu', $atts, $args, $depth);
71+
$atts = $this->buildClasses('submenu', $atts, $args, $depth);
72+
73+
return $atts;
74+
}
75+
76+
/**
77+
* Utility function to build the attributes
78+
*
79+
* @param String $prefix The prefix (a, li, submenu)
80+
* @param object $atts wp_nav_menu attributes object
81+
* @param object $args wp_nav_menu args object
82+
* @param int $depth Depth of the current submenu being parsed.
83+
* @param int $index The index of menu order, -1 is considered absent
84+
*
85+
* @return object Modified attributes for the current element
86+
*/
87+
public function buildAttributes($prefix, $atts, $args, $depth, $index = -1) {
88+
if (property_exists($args, "{$prefix}_atts")) {
89+
$atts = array_merge($atts, $args->{"{$prefix}_atts"});
90+
}
91+
if (property_exists($args, "{$prefix}_atts_{$depth}")) {
92+
$atts = array_merge($atts, $args->{"{$prefix}_atts_{$depth}"});
93+
}
94+
if ($index !== -1 && property_exists($args, "{$prefix}_atts_order_{$index}")) {
95+
$atts = array_merge($atts, $args->{"{$prefix}_atts_order_{$index}"});
11996
}
12097

121-
if (property_exists($args, "submenu_class_$depth")) {
122-
$arr_classes = explode(' ', $args->{"submenu_class_$depth"});
123-
$classes = array_merge($classes, $arr_classes);
98+
if (empty($atts['class'])) {
99+
$atts['class'] = '';
124100
}
101+
return $atts;
102+
}
103+
125104

126-
// Applying this here too just in case, but there's
127-
// no default user interface to add a class to a submenu
105+
106+
/**
107+
* Utility function to build the classes
108+
*
109+
* @param String $prefix The prefix (a, li, submenu)
110+
* @param object $atts wp_nav_menu attributes object
111+
* @param object $args wp_nav_menu args object
112+
* @param int $depth Depth of the current submenu being parsed.
113+
* @param int $index The index of menu order, -1 is considered absent
114+
*
115+
* @return object Modified attributes for the current element
116+
*/
117+
public function buildClasses($prefix, $atts, $args, $depth, $index = -1) {
118+
$classes = explode(' ', $atts['class']);
119+
120+
$classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class", $args));
121+
$classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class_$depth", $args));
122+
$classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class_order_$depth", $args));
123+
124+
// Applying this fix everywhere even though there's only
125+
// a user interface to add classes to links so far
128126
$classes = $this->fixWordPressClasses($classes);
129127

128+
$atts['class'] = implode(' ', $classes);
129+
130+
return $atts;
131+
}
132+
133+
/**
134+
* Utility function to accept array or string classes
135+
*
136+
* @param String $prop The property to check on our custom arguments (ex.: ul_class, li_class_order_1)
137+
* @param object $args wp_nav_menu args object
138+
*
139+
* @return object Modified attributes for the current element
140+
*/
141+
public function arrayOrStringClasses($prop, $args) {
142+
$classes = [];
143+
if (property_exists($args, $prop)) {
144+
$temp_classes = $args->{$prop};
145+
if(is_string($temp_classes)) {
146+
$temp_classes = explode(' ', $temp_classes);
147+
}
148+
$classes = array_merge($classes, $temp_classes);
149+
}
150+
130151
return $classes;
131152
}
132153

0 commit comments

Comments
 (0)