Last active
March 7, 2022 17:14
-
-
Save GLWalker/eb025c726cbac3631510928539ddc172 to your computer and use it in GitHub Desktop.
Bootstrap 5 WP Page Walker Widget Classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Post API: Walker_Page class | |
* | |
* @package ClassicPress | |
* @subpackage Template | |
* @since WP-4.4.0 | |
* | |
* Modified for Bootstrap 5 List Group with Accordian | |
*/ | |
/** | |
* Core walker class used to create an HTML list of pages. | |
* | |
* @since WP-2.1.0 | |
* | |
* @see Walker | |
*/ | |
class WT_Walker_Page extends Walker_Page | |
{ | |
/** | |
* What the class handles. | |
* | |
* @since WP-2.1.0 | |
* @var string | |
* | |
* @see Walker::$tree_type | |
*/ | |
public $tree_type = 'page'; | |
/** | |
* Database fields to use. | |
* | |
* @since WP-2.1.0 | |
* @var array | |
* | |
* @see Walker::$db_fields | |
* @todo Decouple this. | |
*/ | |
public $db_fields = array('parent' => 'post_parent', 'id' => 'ID'); | |
/** | |
* Set the current in items inside start_el() | |
* to use on start_lvl() | |
* | |
* @var String | |
*/ | |
private $id_submenu; | |
private $toggle_submenu; | |
/** | |
* Outputs the beginning of the current level in the tree before elements are output. | |
* | |
* @since WP-2.1.0 | |
* | |
* @see Walker::start_lvl() | |
* | |
* @param string $output Used to append additional content (passed by reference). | |
* @param int $depth Optional. Depth of page. Used for padding. Default 0. | |
* @param array $args Optional. Arguments for outputting the next level. | |
* Default empty array. | |
*/ | |
public function start_lvl(&$output, $depth = 0, $args = array()) | |
{ | |
if (isset($args['item_spacing']) && 'preserve' === $args['item_spacing']) { | |
$t = "\t"; | |
$n = "\n"; | |
} else { | |
$t = ''; | |
$n = ''; | |
} | |
$indent = str_repeat($t, $depth); | |
$class_names = 'class="children submenu list-group list-group-flush ' . $this->toggle_submenu . '"'; | |
$aria_labelledby = 'aria-labelledby="' . $this->id_submenu . '"'; | |
$id = 'id="' . $this->id_submenu . '"'; | |
$data_parent = 'data-bs-parent="#page_list_accordion"'; | |
$output .= "{$n}{$indent}<ul $class_names $aria_labelledby $id $data_parent >{$n}"; | |
} | |
/** | |
* Outputs the end of the current level in the tree after elements are output. | |
* | |
* @since WP-2.1.0 | |
* | |
* @see Walker::end_lvl() | |
* | |
* @param string $output Used to append additional content (passed by reference). | |
* @param int $depth Optional. Depth of page. Used for padding. Default 0. | |
* @param array $args Optional. Arguments for outputting the end of the current level. | |
* Default empty array. | |
*/ | |
public function end_lvl(&$output, $depth = 0, $args = array()) | |
{ | |
if (isset($args['item_spacing']) && 'preserve' === $args['item_spacing']) { | |
$t = "\t"; | |
$n = "\n"; | |
} else { | |
$t = ''; | |
$n = ''; | |
} | |
$indent = str_repeat($t, $depth); | |
$output .= "{$indent}</ul>{$n}"; | |
} | |
/** | |
* Outputs the beginning of the current element in the tree. | |
* | |
* @see Walker::start_el() | |
* @since WP-2.1.0 | |
* | |
* @param string $output Used to append additional content. Passed by reference. | |
* @param WP_Post $page Page data object. | |
* @param int $depth Optional. Depth of page. Used for padding. Default 0. | |
* @param array $args Optional. Array of arguments. Default empty array. | |
* @param int $current_page Optional. Page ID. Default 0. | |
*/ | |
public function start_el(&$output, $page, $depth = 0, $args = array(), $current_page = 0) | |
{ | |
if (isset($args['item_spacing']) && 'preserve' === $args['item_spacing']) { | |
$t = "\t"; | |
$n = "\n"; | |
} else { | |
$t = ''; | |
$n = ''; | |
} | |
if ($depth) { | |
$indent = str_repeat($t, $depth); | |
} else { | |
$indent = ''; | |
} | |
$css_class = array('page_item', 'page-item-' . $page->ID . ' list-group-item list-group-item-action'); | |
//https://getbootstrap.com/docs/5.1/components/list-group/#contextual-classes | |
$css_class[] = 'list-group-item-primary'; | |
$toggle = ''; | |
$toggle = ' collapse'; | |
$this->toggle_switch($toggle); | |
if (isset($args['pages_with_children'][$page->ID])) { | |
$css_class[] = 'page_item_has_children'; | |
} | |
if (!empty($current_page)) { | |
$_current_page = get_post($current_page); | |
if ($_current_page && in_array($page->ID, $_current_page->ancestors)) { | |
$css_class[] = 'current_page_ancestor'; | |
// $css_class[] = 'active'; | |
$toggle = 'show '; | |
$this->toggle_switch($toggle); | |
} | |
if ($page->ID == $current_page) { | |
$css_class[] = 'current_page_item'; | |
$css_class[] = 'active'; | |
} elseif ($_current_page && $page->ID == $_current_page->post_parent) { | |
$css_class[] = 'current_page_parent'; | |
$toggle = 'show '; | |
$this->toggle_switch($toggle); | |
} | |
} elseif ($page->ID == get_option('page_for_posts')) { | |
$css_class[] = 'current_page_parent'; | |
} | |
/** | |
* Filters the list of CSS classes to include with each page item in the list. | |
* | |
* @since WP-2.8.0 | |
* | |
* @see wp_list_pages() | |
* | |
* @param array $css_class An array of CSS classes to be applied | |
* to each list item. | |
* @param WP_Post $page Page data object. | |
* @param int $depth Depth of page, used for padding. | |
* @param array $args An array of arguments. | |
* @param int $current_page ID of the current page. | |
*/ | |
$css_classes = implode(' ', apply_filters('page_css_class', $css_class, $page, $depth, $args, $current_page)); | |
if ('' === $page->post_title) { | |
/* translators: %d: ID of a post */ | |
$page->post_title = sprintf(__('#%d (no title)'), $page->ID); | |
} | |
# access the right property later in start_lvl (->title is made up) | |
$submenu_id = $this->create_sub_menu_id($page); | |
$args['link_before'] = empty($args['link_before']) ? '' : $args['link_before']; | |
$args['link_after'] = empty($args['link_after']) ? '' : $args['link_after']; | |
$atts = array(); | |
$atts['href'] = get_permalink($page->ID); | |
$atts['aria-current'] = ($page->ID == $current_page) ? 'page' : ''; | |
$atts['id'] = $submenu_id; | |
if (isset($args['pages_with_children'][$page->ID])) { | |
$atts['class'] = 'accordion-button ' . $this->toggle_submenu . 'd'; | |
$atts['data-bs-toggle'] = 'collapse'; | |
$atts['data-bs-target'] = '#' . $this->id_submenu; | |
} | |
/** | |
* Filters the HTML attributes applied to a page menu item's anchor element. | |
* | |
* @since WP-4.8.0 | |
* | |
* @param array $atts { | |
* The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored. | |
* | |
* @type string $href The href attribute. | |
* @type string $aria_current The aria-current attribute. | |
* } | |
* @param WP_Post $page Page data object. | |
* @param int $depth Depth of page, used for padding. | |
* @param array $args An array of arguments. | |
* @param int $current_page ID of the current page. | |
*/ | |
$atts = apply_filters('page_menu_link_attributes', $atts, $page, $depth, $args, $current_page); | |
$attributes = ''; | |
foreach ($atts as $attr => $value) { | |
if (!empty($value)) { | |
$value = esc_attr($value); | |
$attributes .= ' ' . $attr . '="' . $value . '"'; | |
} | |
} | |
$output .= $indent . sprintf( | |
'<li class="%s"><a%s>%s%s%s</a>', | |
$css_classes, | |
$attributes, | |
$args['link_before'], | |
/** This filter is documented in wp-includes/post-template.php */ | |
apply_filters('the_title', $page->post_title, $page->ID), | |
$args['link_after'] | |
); | |
if (!empty($args['show_date'])) { | |
if ('modified' == $args['show_date']) { | |
$time = $page->post_modified; | |
} else { | |
$time = $page->post_date; | |
} | |
$date_format = empty($args['date_format']) ? '' : $args['date_format']; | |
$output .= " " . mysql2date($date_format, $time); | |
} | |
} | |
/** | |
* Outputs the end of the current element in the tree. | |
* | |
* @since WP-2.1.0 | |
* | |
* @see Walker::end_el() | |
* | |
* @param string $output Used to append additional content. Passed by reference. | |
* @param WP_Post $page Page data object. Not used. | |
* @param int $depth Optional. Depth of page. Default 0 (unused). | |
* @param array $args Optional. Array of arguments. Default empty array. | |
*/ | |
public function end_el(&$output, $page, $depth = 0, $args = array()) | |
{ | |
if (isset($args['item_spacing']) && 'preserve' === $args['item_spacing']) { | |
$t = "\t"; | |
$n = "\n"; | |
} else { | |
$t = ''; | |
$n = ''; | |
} | |
$output .= "</li>{$n}"; | |
} | |
/** | |
* Create a submenu ID based on | |
* menu post type | |
* | |
* @param WP_Post $item Menu item data object. | |
*/ | |
private function create_sub_menu_id($item) | |
{ | |
return $this->id_submenu = "submenu-" . $item->post_name; | |
} | |
/** | |
* Create a switch to toggle accordian open | |
* if menu link is active | |
* | |
* @param WP_Post $item Menu item data object. | |
*/ | |
private function toggle_switch($val) | |
{ | |
return $this->toggle_submenu = $val; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Widget API: WP_Widget_Pages class | |
* | |
* @package WordPress | |
* @subpackage Widgets | |
* @since 4.4.0 | |
* | |
* Modified for Bootstrap 5 List Group with Accordian | |
*/ | |
/** | |
* Core class used to implement a Pages widget. | |
* | |
* @since 2.8.0 | |
* | |
* @see WP_Widget | |
*/ | |
//class WT_Widget_Pages extends WP_Widget_Pages | |
// Need to only extend WP_Widget in this case | |
class WT_Widget_Pages extends WP_Widget | |
{ | |
/** | |
* Sets up a new Pages widget instance. | |
* | |
* @since 2.8.0 | |
*/ | |
public function __construct() | |
{ | |
$widget_ops = array( | |
'classname' => 'widget_pages', | |
'description' => __('A list of your site’s Pages.'), | |
'customize_selective_refresh' => true, | |
'show_instance_in_rest' => true, | |
); | |
parent::__construct('pages', __('Pages'), $widget_ops); | |
} | |
/** | |
* Outputs the content for the current Pages widget instance. | |
* | |
* @since 2.8.0 | |
* | |
* @param array $args Display arguments including 'before_title', 'after_title', | |
* 'before_widget', and 'after_widget'. | |
* @param array $instance Settings for the current Pages widget instance. | |
*/ | |
public function widget($args, $instance) | |
{ | |
$default_title = __('Pages'); | |
$title = !empty($instance['title']) ? $instance['title'] : $default_title; | |
/** | |
* Filters the widget title. | |
* | |
* @since 2.6.0 | |
* | |
* @param string $title The widget title. Default 'Pages'. | |
* @param array $instance Array of settings for the current widget. | |
* @param mixed $id_base The widget ID. | |
*/ | |
$title = apply_filters('widget_title', $title, $instance, $this->id_base); | |
$sortby = empty($instance['sortby']) ? 'menu_order' : $instance['sortby']; | |
$exclude = empty($instance['exclude']) ? '' : $instance['exclude']; | |
if ('menu_order' === $sortby) { | |
$sortby = 'menu_order, post_title'; | |
} | |
$output = wp_list_pages( | |
/** | |
* Filters the arguments for the Pages widget. | |
* | |
* @since 2.8.0 | |
* @since 4.9.0 Added the `$instance` parameter. | |
* | |
* @see wp_list_pages() | |
* | |
* @param array $args An array of arguments to retrieve the pages list. | |
* @param array $instance Array of settings for the current widget. | |
*/ | |
apply_filters( | |
'widget_pages_args', | |
array( | |
'title_li' => '', | |
'echo' => 0, | |
'sort_column' => $sortby, | |
'exclude' => $exclude, | |
'item_spacing' => '', | |
), | |
$instance | |
) | |
); | |
if (!empty($output)) { | |
echo $args['before_widget']; | |
if ($title) { | |
echo $args['before_title'] . $title . $args['after_title']; | |
} | |
// The title may be filtered: Strip out HTML and make sure the aria-label is never empty. | |
$title = trim(strip_tags($title)); | |
$aria_label = $title ? $title : $default_title; | |
echo '<nav role="navigation" aria-label="' . esc_attr($aria_label) . '">'; | |
?> | |
<ul id="page_list_accordion" class="list-group"> | |
<?php echo $output; ?> | |
</ul> | |
<?php | |
echo '</nav>'; | |
echo $args['after_widget']; | |
} | |
} | |
/** | |
* Handles updating settings for the current Pages widget instance. | |
* | |
* @since 2.8.0 | |
* | |
* @param array $new_instance New settings for this instance as input by the user via | |
* WP_Widget::form(). | |
* @param array $old_instance Old settings for this instance. | |
* @return array Updated settings to save. | |
*/ | |
public function update($new_instance, $old_instance) | |
{ | |
$instance = $old_instance; | |
$instance['title'] = sanitize_text_field($new_instance['title']); | |
if (in_array($new_instance['sortby'], array('post_title', 'menu_order', 'ID'), true)) { | |
$instance['sortby'] = $new_instance['sortby']; | |
} else { | |
$instance['sortby'] = 'menu_order'; | |
} | |
$instance['exclude'] = sanitize_text_field($new_instance['exclude']); | |
return $instance; | |
} | |
/** | |
* Outputs the settings form for the Pages widget. | |
* | |
* @since 2.8.0 | |
* | |
* @param array $instance Current settings. | |
*/ | |
public function form($instance) | |
{ | |
// Defaults. | |
$instance = wp_parse_args( | |
(array) $instance, | |
array( | |
'sortby' => 'post_title', | |
'title' => '', | |
'exclude' => '', | |
) | |
); | |
?> | |
<p> | |
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php _e('Title:'); ?></label> | |
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($instance['title']); ?>" /> | |
</p> | |
<p> | |
<label for="<?php echo esc_attr($this->get_field_id('sortby')); ?>"><?php _e('Sort by:'); ?></label> | |
<select name="<?php echo esc_attr($this->get_field_name('sortby')); ?>" id="<?php echo esc_attr($this->get_field_id('sortby')); ?>" class="widefat"> | |
<option value="post_title" <?php selected($instance['sortby'], 'post_title'); ?>><?php _e('Page title'); ?></option> | |
<option value="menu_order" <?php selected($instance['sortby'], 'menu_order'); ?>><?php _e('Page order'); ?></option> | |
<option value="ID" <?php selected($instance['sortby'], 'ID'); ?>><?php _e('Page ID'); ?></option> | |
</select> | |
</p> | |
<p> | |
<label for="<?php echo esc_attr($this->get_field_id('exclude')); ?>"><?php _e('Exclude:'); ?></label> | |
<input type="text" value="<?php echo esc_attr($instance['exclude']); ?>" name="<?php echo esc_attr($this->get_field_name('exclude')); ?>" id="<?php echo esc_attr($this->get_field_id('exclude')); ?>" class="widefat" /> | |
<br /> | |
<small><?php _e('Page IDs, separated by commas.'); ?></small> | |
</p> | |
<?php | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Your theme should have BootStrap5 CSS and the javascript enqued. | |
* | |
* This creates a WordPress pages widget with BootStrap 5 list group menu with accordian dropdown for submenus | |
* Accordian works for all levels, but is quirky after the 2cnd level becuase the top level accordian button likes to close | |
* onClick of the second level - once reopend, the deeper levels do show, | |
* can probably be modified with some JS if it's really that important. | |
* | |
* Add the following two class files to your theme and call theme using whatever your prefered method is: | |
* | |
* /class-wt-walker-page.php | |
* /class-wt-widget-pages.php | |
* | |
* Add the following code to your functions: | |
*/ | |
/** | |
* Pages Widget | |
* | |
*/ | |
function wt_widget_pages_args($args) | |
{ | |
$args = array_merge($args, array('walker' => new WT_Walker_Page())); | |
return $args; | |
} | |
add_filter('widget_pages_args', 'wt_widget_pages_args'); | |
/** | |
* Add inline styles widget | |
* change wp_add_inline_style('bootstrap', $css_output); | |
* to match whatever your bootsrap stylesheet handle is | |
* | |
* @return css | |
*/ | |
function wt_pla_inline_style() | |
{ | |
$css_input = ' | |
/* Pages List Accordian Widget */ | |
#page_list_accordion li a{display:block;color:inherit!important}#page_list_accordion a:hover{color:inherit!important}#page_list_accordion li ul{padding-top:1rem;margin-bottom:-.5rem}#page_list_accordion li ul li{width:calc(100% + 2rem)!important;padding:.5rem 1rem;margin-left:-1rem;margin-right:-1rem;overflow:hidden!important}#page_list_accordion .accordion-button{display:flex!important;padding:.25rem 0 .33rem!important;font-size:inherit!important;color:inherit!important;background-color:inherit!important;box-shadow:none}#page_list_accordion .accordion-button:not(.collapsed){color:inherit!important;background-color:inherit!important;box-shadow:inherit!important}#page_list_accordion .accordion-button:focus{border-color:inherit!important;box-shadow:none}#page_list_accordion li a{display:block;color:inherit!important}#page_list_accordion a:hover{color:inherit!important}#page_list_accordion li ul{padding-top:1rem;margin-bottom:-.5rem}#page_list_accordion li ul li{width:calc(100% + 2rem)!important;padding:.5rem 1rem;margin-left:-1rem;margin-right:-1rem;overflow:hidden!important}#page_list_accordion .accordion-button{display:flex!important;padding:.25rem 0 .33rem!important;font-size:inherit!important;color:inherit!important;background-color:inherit!important;box-shadow:none}#page_list_accordion .accordion-button:not(.collapsed){color:inherit!important;background-color:inherit!important;box-shadow:inherit!important}#page_list_accordion .accordion-button:focus{border-color:inherit!important;box-shadow:none} | |
'; | |
$css_output = esc_html($css_input); | |
wp_add_inline_style('bootstrap', $css_output); | |
wp_enqueue_style('bootstrap-inline-styles', get_stylesheet_uri()); | |
} | |
add_action('wp_enqueue_scripts', 'wt_pla_inline_style'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment