Project:Better Exposed Filters
Version:7.x-3.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:Unassigned
Status:active

Issue Summary

The title might be a little confusing but couldn't think of anything else atm.

I've got a use case that needs views exposed filters (hierarchical taxonomy terms) structured like the "Nested Checkboxes/Radio Buttons" but with a twist:
Instead of checkboxes or radio buttons I need links.
Sort of like a "Nested Links" display. (hence the title of this issue)

We've got "Nested Checkboxes/Radio Buttons", we've got "Links", why not combine them into "Nested Links" ? From what I understand it wouldn't be too hard to implement and theming-wise it would make some things a lot easier!

over-simplified, having an output like

<ul>
  <li><a href=#>term 1</a></li>
  <li><a href=#>term 2</a>
    <ul>
      <li><a href=#>sub-term</a></li>
    </li>
  </li>
  <li><a href=#>term 3</a></li>
</ul>

would mean easier to theme hierarchical exposed filters, plus the ability to use a few js tricks to make this prettier and more functional.

Comments

#1

I was in the same situation. To save a bit of time, I copied the theme function code in better_exposed_filters.theme function theme_select_as_tree for counting depth

<?php
preg_match
('/^(-*).*$/', $option_label, $matches);
$depth = strlen($matches[1]);
?>

And then wrapped a div tag around the link with a depth class

<?php
$link
= '<div class="depth-'.$depth.'">'.l($value, bef_replace_query_string_arg($name, $key, $multiple));.'</div>';
?>

and then I can style the different depth levels that I need. Not a great solution, but it works.

Here's the whole thing to go into your template.php file:

<?php
function YOURTHEME_select_as_links($vars) {
 
$element = $vars['element'];

 
$output = '';
 
$name = $element['#name'];

 
// Collect selected values so we can properly style the links later
 
$selected_options = array();
  if (empty(
$element['#value'])) {
    if (!empty(
$element['#default_values'])) {
     
$selected_options[] = $element['#default_values'];
    }
  }
  else {
   
$selected_options[] = $element['#value'];
  }

 
// Add to the selected options specified by Views whatever options are in the
  // URL query string, but only for this filter
 
$urllist = parse_url(request_uri());
  if (isset(
$urllist['query'])) {
   
$query = array();
   
parse_str(urldecode($urllist['query']), $query);
    foreach (
$query as $key => $value) {
      if (
$key != $name) {
        continue;
      }
      if (
is_array($value)) {
       
// This filter allows multiple selections, so put each one on the selected_options array
       
foreach ($value as $option) {
         
$selected_options[] = $option;
        }
      }
      else {
       
$selected_options[] = $value;
      }
    }
  }

 
$curr_depth = 0;
 
$element['#options']['All'] = t('View All');
 
// Go through each filter option and build the appropriate link or plain text
 
foreach ($element['#options'] as $option => $elem) {
   
   
// Check for Taxonomy-based filters
   
if (is_object($elem)) {
      list(
$option, $elem) = each(array_slice($elem->option, 0, 1, TRUE));
    }

   
/*
     * Check for optgroups.  Put subelements in the $element_set array and add a group heading.
     * Otherwise, just add the element to the set
     */
   
$element_set = array();
    if (
is_array($elem)) {
     
$element_set = $elem;
    }
    else {
     
$element_set[$option] = $elem;
    }

   
$links = array();
   
$multiple = !empty($element['#multiple']);
   
            
   
$html = '';
    foreach (
$element_set as $key => $value) {
     
//dpm($value);
     
preg_match('/^(-*).*$/', $value, $matches);
     
$depth = strlen($matches[1]);
     
//dpm($depth);

     
      // Custom ID for each link based on the <select>'s original ID
     
$id = drupal_html_id($element['#id'] . '-' . $key);
      if (
array_search($key, $selected_options) === FALSE) {
       
$link = l(ltrim($value, '-'), bef_replace_query_string_arg($name, $key, $multiple));
       
$html = '<div class="depth-'.$depth.'">'.$link.'</div>';
       
$output .= theme('form_element', array('element' => array( '#id' => $id, '#children' => $html, '#markup' => '',
         
'#name' => 'depth-'.$depth,)));
      } else {
       
// Selected value is output without a link
        // TODO: add link to remove this option from the filter?
       
$elem = array(
         
'#id' => $id,
         
'#children' => $value,
         
'#markup' => '',
         
'#name' => 'depth-'.$depth,
          );
       
_form_set_class($elem, array('bef-select-as-links-selected'));
       
$output .= theme('form_element', array('element' => $elem));
      }
    }
// end foreach
   

 
} // end foreach



 
$properties = array(
   
'#description' => isset($element['#description']) ? $element['#description'] : '',
   
'#children' => $output,
   
'#name' => 'test-list',
  );

  return
'<div class="bef-select-as-links">'
   
. theme('form_element', array('element' => $properties))
    .
'</div>';
}
?>

Daniel

#2

Wow, thank a lot! that actually works!!

nobody click here