Mega menu en Drupal con Bootstrap y Panels

  • 18 Mayo 2015
  • Drupal 7

Hay decenas de modos de construir un mega menu en Drupal. Hay más de un módulo que se dedica pura y exclusivamente a esto. Por ejemplo, el TB Mega Menu. Este módulo y otros que se le parecen son fáciles de instalar y configurar. Pero por qué instalar un módulo si podemos hacerlo con los que ya tenemos instalados?

Para construir un mega menu no necesitamos más que crear un bloque que se abra bajo uno de los elementos del primer nivel del menú principal. No parece difícil. Este bloque debería contener por lo menos una serie de menu blocks, que podrían ser los hijos (children) de segundo nivel con sus hijos del tercer nivel. Y para poner todos estos blocks juntos, en una grid de bootstrap podemos utilizar mini-panels con un layout personalizado y un poco de jQuey.

Módulos necesarios para crear un Mega Menu

Vamos a suponer que ya se está trabajando con un subtheme de Boootstrap.

Procedimiento

En resumen, estos son los pasos a seguir para construir un mega menu:

  1. Activar menu block y Mini panels.
  2. Crear un layout de 4 columnas para Panels
  3. Crear un mini panel
  4. Crear una región en el theme donde activar el bloque creado en el punto anterior
  5. Aplicar los estilos de Yamm!3 a la barra de navegación
  6. Un poco de PHP
  7. Un poco de jQuery

Vamos a ver más en detalle algunos de estos puntos.

Crear el mini-panel

Antes de comenzar a crear el mini panel, vamos a crear un layout personalizado con 4 columnas. El manual de Panels lo explica muy bien. Vamos el directorio dentro nuestro subtheme de bootstrap y creamos layouts/fourcol. Dentro de este directorio, tendremos los siguientes archivos:

  • fourcol.css
  • fourcol.inc
  • fourcol.png
  • panels-fourcol.tpl.php

En el archivo fourcol.inc se define el layout

<?php

// Plugin definition
$plugin = array(
  'title' => t('Four column'),
  'category' => t('Columns: 4'),
  'icon' => 'fourcol.png',
  'theme' => 'panels_fourcol',
  'css' => 'fourcol.css',
  'regions' => array(
    'col1' => t('COl 1'),
    'col2' => t('COl 2'),
    'col3' => t('COl 3'),
    'col4' => t('COl 4'),
  ),
);

Y en el archivo panels-fourcol.tpl.php escribimos el código de las 4 columnas

<div class="row" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
  <div class="col-md-3"><?php print $content['col1']; ?></div>
  <div class="col-md-3"><?php print $content['col2']; ?></div>
  <div class="col-md-3"><?php print $content['col3']; ?></div>
  <div class="col-md-3"><?php print $content['col4']; ?></div>
</div>

Una vez que tenemos nuestro layout, creamos el mini-panel con el layout de 4 columnas: admin/structure/mini-panels

Drupal - Mini panels

Luego, agregamos en cada columna las partes del menu que más tarde formarán el mega menu. Para esto, seleccionamos Menus → Main menu menu tree

Drupal - Mini panels - Add content

Agregar el block del mini panel a la página principal

Una vez que tenemos el block con los contenidos de nuestro mega menu será necesario agragarlos a nuestro sitio. Para esto, podemos crear una nueva región modificando el archivo .info de nuestro theme.

regions[mega_menu]    = 'Mega menu'

Luego, agregamos la regíon a page.tpl.php

<?php if (!empty($page['mega_menu'])): ?>
  <div id="mega-menu" class="hidden">
    <?php print render($page['mega_menu']); ?>
  </div>
<?php endif; ?>

Para poder seleccionar con jQuery el punto de menu justo, agregamos una class a cada punto de menu, utilizando el hook_menu_link

function mytheme_menu_link(array $variables) {
  $element = $variables['element'];
  $sub_menu = '';

  if ($element['#below']) {
    // Prevent dropdown functions from being added to management menu so it
    // does not affect the navbar module.
    if (($element['#original_link']['menu_name'] == 'management') && (module_exists('navbar'))) {
      $sub_menu = drupal_render($element['#below']);
    }
    elseif ((!empty($element['#original_link']['depth'])) && ($element['#original_link']['depth'] == 1)) {
      // Add our own wrapper.
      unset($element['#below']['#theme_wrappers']);
      $sub_menu = '<ul class="dropdown-menu">' . drupal_render($element['#below']) . '</ul>';
      // Generate as standard dropdown.
      $element['#title'] .= ' <span class="caret"></span>';
      $element['#attributes']['class'][] = 'dropdown';
      $element['#localized_options']['html'] = TRUE;

      // Set dropdown trigger element to # to prevent inadvertant page loading
      // when a submenu link is clicked.
      $element['#localized_options']['attributes']['data-target'] = '#';
      $element['#localized_options']['attributes']['class'][] = 'dropdown-toggle';
      $element['#localized_options']['attributes']['data-toggle'] = 'dropdown';
    }
  }
  // On primary navigation menu, class 'active' is not set on active menu item.
  // @see https://drupal.org/node/1896674
  if (($element['#href'] == $_GET['q'] || ($element['#href'] == '<front>' && drupal_is_front_page())) && (empty($element['#localized_options']['language']))) {
    $element['#attributes']['class'][] = 'active';
  }

  $element['#attributes']['class'][] = "mlid-" . $element['#original_link']['mlid'];

  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

Ahora, con un poco de jQuery podemos colocar el contenido de esta región como hijo del submenu que se desee. En este ejemplo, lo agregamos debajo del punto de menu 282 (de primer nivel, claro).

jQuery(function ($) {
  
  $('document').ready(function(){    
    var megaMenuItem = $('.navbar-nav .dropdown.mlid-282');
    megaMenuItem.addClass("yamm-fw");
    var megaSubMenu = megaMenuItem.find('ul.dropdown-menu');
    megaSubMenu.replaceWith('<ul class="dropdown-menu dropdown-mega-menu">' + $("#mega-menu").html() + '</ul>');
    
  });
});

Aplicar Yamm3 al mega menu

Yamm!3 es un excelente trabajo que aplicando tan solo un par de líneas de estilo nos permite usar el código de grids como submenu. Descargamos yamm.less y lo copiamos en mytheme/less. Luego, lo agregamos a less/style.less y hacemos flush less desde admin/config/development/less.

@import 'yamm.less';