diff --git a/README.md b/README.md
index 5e6494d..b615ba8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# WordPress Menu Classes
-Allow adding custom classes to WordPress menu ul, li, a and at different depths. Perfect for TailwindCSS and AlpineJS usage.
+Allow adding custom classes to WordPress li, a and submenus at different depths. Perfect for TailwindCSS and AlpineJS usage.
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.
@@ -18,7 +18,7 @@ Install via Composer:
$ composer require davidwebca/wordpress-menu-classes
```
-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.
+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.
## Instructions
@@ -26,14 +26,17 @@ The filters use the depth argument given by WordPress which is an index, thus st
Here's a list of the custom arguments you can pass to wp_nav_menu that are supported by this package :
-- ```link_atts``` or ```link_atts_$depth``` or ```link_atts_order_$order```
- - Add any attribute to `````` elements
+- ```a_atts``` or ```a_atts_$depth``` or ```a_atts_order_$order```
- ```a_class``` or ```a_class_$depth``` or ```a_class_order_$order```
- - Add classes to `````` elements
+ - Add any attribute or class to `````` elements
+
+- ```li_atts``` or ```li_atts_$depth``` or ```li_atts_order_$order```
- ```li_class``` or ```li_class_$depth``` or ```li_class_order_$order```
- - Add classes to `````` elements
+ - Add any attribute or class to `````` elements
+
+- ```submenu_atts``` or ```submenu_atts_$depth```
- ```submenu_class``` or ```submenu_class_$depth```
- - Add classes to submenu `````` elements
+ - Add any attribute or class to submenu `````` elements. Note that submenus do not support order.
Ex.: add a "text-black" class to all links and "text-blue" class only to 3rd level links
@@ -46,24 +49,39 @@ wp_nav_menu([
]);
```
-Ex.: More complete example with some TailwindCSS classes and AlpineJS sugar
+Ex.: Supports classes as array (this is non-native to WordPress, but provided by this package as a convenience)
+
+```php
+wp_nav_menu([
+ 'theme_location' => 'primary_navigation',
+ 'a_class' => ['text-white', 'bg-blue-500'],
+ 'li_atts' => [
+ 'class' => ['focus:ring-2', 'ring-orange-500']
+ ]
+ // ...
+]);
+```
+
+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).
```php
wp_nav_menu([
'theme_location' => 'primary_navigation',
- 'menu_class' => 'relative w-full z-10 pl-0 list-none flex',
- 'link_atts_0' => [
- ":class" => "{ 'active': tab === 'foo' }",
- "@click" => "tab = 'foo'"
+ 'container' => 'nav',
+ 'menu_class' => 'list-none p-0 m-0',
+ 'a_class_0' => "font-bold inline-flex items-center text-xl",
+ 'li_atts_0' => [
+ '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",
+ ':class' => "{'before:content-[\'+\']': !opened, 'before:content-[\'-\']': opened}",
+ 'x-data' => "{opened: false}",
+ 'x-on:click' => 'opened = !opened'
],
- 'li_class' => 'w-full',
- 'li_class_0' => 'mb-12',
- 'a_class' => 'text-sm xl:text-xl text-white border-b hover:border-white',
- 'a_class_0' => 'text-3xl xl:text-5xl relative after:bg-primary',
- 'li_class_1' => 'after:bg-primary hidden lg:block',
- 'a_class_1' => 'flex h-full items-center uppercase py-2 relative border-white border-opacity-40 hover:border-opacity-100',
- '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',
- 'container'=>false
+ 'submenu_class_0' => 'wowza',
+ 'submenu_atts_0' => [
+ 'x-show' => 'opened',
+ 'x-collapse' => "_",
+ 'class' => 'list-none pl-10'
+ ]
]);
```
diff --git a/src/WordPressMenuClasses.php b/src/WordPressMenuClasses.php
index 0dad2f3..7a893d8 100644
--- a/src/WordPressMenuClasses.php
+++ b/src/WordPressMenuClasses.php
@@ -12,8 +12,8 @@ class WordPressMenuClasses
public function __construct()
{
add_filter('nav_menu_link_attributes', [$this, 'navMenuLinkAttributes'], 10, 4);
- add_filter('nav_menu_css_class', [$this, 'navMenuCSSClass'], 10, 4);
- add_filter('nav_menu_submenu_css_class', [$this, 'navMenuSubmenuCSSClass'], 10, 3);
+ add_filter('nav_menu_item_attributes', [$this, 'navMenuItemAttributes'], 10, 4);
+ add_filter('nav_menu_submenu_attributes', [$this, 'navSubmenuAttributes'], 10, 3);
}
/**
@@ -30,41 +30,8 @@ public function navMenuLinkAttributes($atts, $item, $args, $depth)
{
$index = $item->menu_order;
- if (property_exists($args, 'link_atts')) {
- $atts = array_merge($atts, $args->link_atts);
- }
- if (property_exists($args, "link_atts_$depth")) {
- $atts = array_merge($atts, $args->{"link_atts_$depth"});
- }
- if (property_exists($args, "link_atts_order_$index")) {
- $atts = array_merge($atts, $args->{"link_atts_order_$index"});
- }
-
- if (empty($atts['class'])) {
- $atts['class'] = '';
- }
-
- $classes = explode(' ', $atts['class']);
-
- if (property_exists($args, 'a_class')) {
- $arr_classes = explode(' ', $args->a_class);
- $classes = array_merge($classes, $arr_classes);
- }
- if (property_exists($args, "a_class_$depth")) {
- $arr_classes = explode(' ', $args->{"a_class_$depth"});
- $classes = array_merge($classes, $arr_classes);
- }
- if (property_exists($args, "a_class_order_$index")) {
- $arr_classes = explode(' ', $args->{"a_class_order_$index"});
- $classes = array_merge($classes, $arr_classes);
- }
-
- // Applying this here too just in case, but there's
- // no default user interface to add a class directly to a link in the menu
- // (classes are applied to li elements by default)
- $classes = $this->fixWordPressClasses($classes);
-
- $atts['class'] = implode(' ', $classes);
+ $atts = $this->buildAttributes('a', $atts, $args, $depth, $index);
+ $atts = $this->buildClasses('a', $atts, $args, $depth, $index);
return $atts;
}
@@ -79,54 +46,108 @@ public function navMenuLinkAttributes($atts, $item, $args, $depth)
* This is an index, thus starts with 0 for the root level.
* @return array Modified classes for the current li element
*/
- public function navMenuCSSClass($classes, $item, $args, $depth)
+ public function navMenuItemAttributes($atts, $item, $args, $depth)
{
$index = $item->menu_order;
- if (property_exists($args, 'li_class')) {
- $arr_classes = explode(' ', $args->li_class);
- $classes = array_merge($classes, $arr_classes);
- }
- if (property_exists($args, "li_class_$depth")) {
- $arr_classes = explode(' ', $args->{"li_class_$depth"});
- $classes = array_merge($classes, $arr_classes);
- }
- if (property_exists($args, "li_class_order_$index")) {
- $arr_classes = explode(' ', $args->{"li_class_order_$index"});
- $classes = array_merge($classes, $arr_classes);
- }
-
-
- $classes = $this->fixWordPressClasses($classes);
+ $atts = $this->buildAttributes('li', $atts, $args, $depth, $index);
+ $atts = $this->buildClasses('li', $atts, $args, $depth, $index);
- return $classes;
+ return $atts;
}
/**
- * Add custom classes to ul.sub-menu in wp_nav_menu
+ * Add custom classes and attributes to ul.submenu in wp_nav_menu
*
- * @param array $classes CSS classes added to all ul submenu of our menu.
- * @param object $args wp_nav_menu args object
- * @param int $depth Depth of the current menu item being parsed.
+ * @param object $atts wp_nav_menu attributes object
+ * @param object $args wp_nav_menu args object
+ * @param int $depth Depth of the current submenu being parsed.
* This is an index, thus starts with 0 for the root level.
* @return object Modified attributes for the current ul submenu
*/
- public function navMenuSubmenuCSSClass($classes, $args, $depth)
+ public function navSubmenuAttributes($atts, $args, $depth)
{
- if (property_exists($args, 'submenu_class')) {
- $arr_classes = explode(' ', $args->submenu_class);
- $classes = array_merge($classes, $arr_classes);
+ $atts = $this->buildAttributes('submenu', $atts, $args, $depth);
+ $atts = $this->buildClasses('submenu', $atts, $args, $depth);
+
+ return $atts;
+ }
+
+ /**
+ * Utility function to build the attributes
+ *
+ * @param String $prefix The prefix (a, li, submenu)
+ * @param object $atts wp_nav_menu attributes object
+ * @param object $args wp_nav_menu args object
+ * @param int $depth Depth of the current submenu being parsed.
+ * @param int $index The index of menu order, -1 is considered absent
+ *
+ * @return object Modified attributes for the current element
+ */
+ public function buildAttributes($prefix, $atts, $args, $depth, $index = -1) {
+ if (property_exists($args, "{$prefix}_atts")) {
+ $atts = array_merge($atts, $args->{"{$prefix}_atts"});
+ }
+ if (property_exists($args, "{$prefix}_atts_{$depth}")) {
+ $atts = array_merge($atts, $args->{"{$prefix}_atts_{$depth}"});
+ }
+ if ($index !== -1 && property_exists($args, "{$prefix}_atts_order_{$index}")) {
+ $atts = array_merge($atts, $args->{"{$prefix}_atts_order_{$index}"});
}
- if (property_exists($args, "submenu_class_$depth")) {
- $arr_classes = explode(' ', $args->{"submenu_class_$depth"});
- $classes = array_merge($classes, $arr_classes);
+ if (empty($atts['class'])) {
+ $atts['class'] = '';
}
+ return $atts;
+ }
+
- // Applying this here too just in case, but there's
- // no default user interface to add a class to a submenu
+
+ /**
+ * Utility function to build the classes
+ *
+ * @param String $prefix The prefix (a, li, submenu)
+ * @param object $atts wp_nav_menu attributes object
+ * @param object $args wp_nav_menu args object
+ * @param int $depth Depth of the current submenu being parsed.
+ * @param int $index The index of menu order, -1 is considered absent
+ *
+ * @return object Modified attributes for the current element
+ */
+ public function buildClasses($prefix, $atts, $args, $depth, $index = -1) {
+ $classes = explode(' ', $atts['class']);
+
+ $classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class", $args));
+ $classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class_$depth", $args));
+ $classes = array_merge($classes, $this->arrayOrStringClasses("{$prefix}_class_order_$depth", $args));
+
+ // Applying this fix everywhere even though there's only
+ // a user interface to add classes to links so far
$classes = $this->fixWordPressClasses($classes);
+ $atts['class'] = implode(' ', $classes);
+
+ return $atts;
+ }
+
+ /**
+ * Utility function to accept array or string classes
+ *
+ * @param String $prop The property to check on our custom arguments (ex.: ul_class, li_class_order_1)
+ * @param object $args wp_nav_menu args object
+ *
+ * @return object Modified attributes for the current element
+ */
+ public function arrayOrStringClasses($prop, $args) {
+ $classes = [];
+ if (property_exists($args, $prop)) {
+ $temp_classes = $args->{$prop};
+ if(is_string($temp_classes)) {
+ $temp_classes = explode(' ', $temp_classes);
+ }
+ $classes = array_merge($classes, $temp_classes);
+ }
+
return $classes;
}