You are here

tb_megamenu.module in The Better Mega Menu 8

Same filename and directory in other branches
  1. 7 tb_megamenu.module
  2. 2.x tb_megamenu.module

TB Mega Menu.

File

tb_megamenu.module
View source
<?php

/**
 * @file
 * TB Mega Menu.
 */
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Serialization\Json;
use Drupal\tb_megamenu\Entity\MegaMenuConfig;
use Drupal\Core\Url;

/**
 * Implements hook_help().
 */
function tb_megamenu_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.tb_megamenu':
      $output = '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('TB Mega Menu allows you to create a mega menu with an innovative back-end user interface and synchronized with Drupal core menu.') . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<p>' . t('TB Mega Menu is developed following Drupal standards and best practices. Along with those must-have functions of a mega menu: display not just menu items but image, slideshow, video, article and customize any of them upon your favour...; below are the most noticeable features that TB Mega Menu has:') . '</p>';
      $output .= '<ul>';
      $output .= '<li>' . t('Synchronized with Drupal core menu, auto updated if there is any change with Drupal core menu') . '</li>';
      $output .= '<li>' . t('Bootstrap framework') . '</li>';
      $output .= '<li>' . t('Responsive ready') . '</li>';
      $output .= '<li>' . t('Multiple built-in styles designed with years of theming experience') . '</li>';
      $output .= '<li>' . t('Visual and friendly user interface - what you see is what you get') . '</li>';
      $output .= '<li>' . t('Custom style for each element of your mega menu, just input extra css class name') . '</li>';
      $output .= '<li>' . t('Multiple built-in CSS3 animated effects') . '</li>';
      $output .= '<li>' . t('Work independently with themes') . '</li>';
      $output .= '</ul>';
      return $output;
  }
}

/**
 * Implements hook_theme().
 */
function tb_megamenu_theme($existing, $type, $theme, $path) {
  $items['tb_megamenu'] = [
    'variables' => [
      'menu_name' => NULL,
      'content' => NULL,
      'section' => 'frontend',
      'block_theme' => \Drupal::config('system.theme')
        ->get('default'),
    ],
  ];
  $items['tb_megamenu_nav'] = [
    'variables' => [
      'menu_name' => NULL,
      'level' => NULL,
      'items' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_item'] = [
    'variables' => [
      'menu_name' => NULL,
      'item' => NULL,
      'level' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'submenu' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_submenu'] = [
    'variables' => [
      'menu_name' => NULL,
      'parent' => NULL,
      'level' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_row'] = [
    'variables' => [
      'menu_name' => NULL,
      'row' => NULL,
      'parent' => NULL,
      'level' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_column'] = [
    'variables' => [
      'menu_name' => NULL,
      'col' => NULL,
      'parent' => NULL,
      'level' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_block'] = [
    'variables' => [
      'block_id' => NULL,
      'section' => 'frontend',
      'showblocktitle' => 1,
    ],
  ];
  $items['tb_megamenu_subnav'] = [
    'variables' => [
      'menu_name' => NULL,
      'col' => NULL,
      'level' => NULL,
      'items' => NULL,
      'menu_config' => NULL,
      'block_config' => NULL,
      'trail' => NULL,
      'section' => 'frontend',
    ],
  ];
  $items['tb_megamenu_admin_settings'] = [
    'render element' => 'form',
  ];
  $items['tb_megamenu_backend'] = [
    'variables' => [
      'blocks' => NULL,
      'menu_name' => NULL,
      'block_theme' => \Drupal::config('system.theme')
        ->get('default'),
      'menu_content' => NULL,
    ],
    'path' => $path . '/templates/backend',
  ];
  $items['tb_megamenu_item_toolbox'] = [
    'variables' => [],
    'path' => $path . '/templates/backend',
  ];
  $items['tb_megamenu_submenu_toolbox'] = [
    'variables' => [],
    'path' => $path . '/templates/backend',
  ];
  $items['tb_megamenu_column_toolbox'] = [
    'variables' => [
      'blocks_options' => [],
    ],
    'path' => $path . '/templates/backend',
  ];
  return $items;
}

/**
 * Prepare variables for tb_megamenu_row.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_row(array &$vars) {
  $row = $vars['row'];
  $vars['columns'] = [];
  foreach ($row as $col) {
    $vars['columns'][] = [
      '#theme' => 'tb_megamenu_column',
      '#menu_name' => $vars['menu_name'],
      '#col' => $col,
      '#parent' => $vars['parent'],
      '#level' => $vars['level'],
      '#menu_config' => $vars['menu_config'],
      '#block_config' => $vars['block_config'],
      '#trail' => $vars['trail'],
      '#section' => $vars['section'],
    ];
  }
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = 'row-fluid';
}

/**
 * Prepare variables for tb_megamenu_column.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_column(array &$vars) {
  $col = $vars['col']['col_content'];
  $col_config = isset($vars['col']['col_config']) ? $vars['col']['col_config'] : [];
  \Drupal::service('tb_megamenu.menu_builder')
    ->editColumnConfig($col_config);
  $vars['col_config'] = $col_config;
  $vars['close_button'] = FALSE;
  $vars['tb_items'] = [];
  $vars['attributes'] = new Attribute();
  foreach ($col as $tb_item) {
    if ($tb_item['type'] == 'menu_item') {
      $vars['tb_items'][] = [
        '#theme' => 'tb_megamenu_subnav',
        '#menu_name' => $vars['menu_name'],
        '#col' => $col,
        '#items' => $vars['parent']->subtree,
        '#level' => $vars['level'],
        '#menu_config' => $vars['menu_config'],
        '#block_config' => $vars['block_config'],
        '#trail' => $vars['trail'],
        '#section' => $vars['section'],
      ];
      break;
    }
    else {
      $vars['tb_items'][] = [
        '#theme' => 'tb_megamenu_block',
        '#block_id' => $tb_item['block_id'],
        '#section' => $vars['section'],
        '#showblocktitle' => $col_config['showblocktitle'],
      ];
      $vars['attributes']['data-showblocktitle'] = $col_config['showblocktitle'];
      if ($vars['section'] !== 'frontend') {
        $msg = t("Remove this block");
        $vars['close_button'] = [
          '#markup' => '<span class="close fa fa-times-circle" title="' . $msg . '">&nbsp;</span>',
        ];
      }
    }
  }
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = 'span' . $col_config['width'];
  $vars['attributes']['class'][] = 'mega-col-nav';
  if (!empty($col_config['class'])) {
    $vars['attributes']['class'][] = $col_config['class'];
  }
  if (!empty($col_config['hidewcol'])) {
    $vars['attributes']['class'][] = $col_config['class'];
    $vars['attributes']['class'][] = 'hidden-collapse';
  }
  $vars['attributes']['data-class'] = $col_config['class'];
  $vars['attributes']['data-width'] = $col_config['width'];
  $vars['attributes']['data-hidewcol'] = $col_config['hidewcol'];
  $num_cols = isset($vars['block_config']['number-columns']) ? $vars['block_config']['number-columns'] : 1;
  $vars['attributes']['id'] = \Drupal::service('tb_megamenu.menu_builder')
    ->getIdColumn($num_cols);
}

/**
 * Prepare variables for tb_megamenu_subnav.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_subnav(array &$vars) {
  $col = $vars['col'];
  $level = $vars['level'];
  $vars['lis'] = [];
  foreach ($col as $tb_item) {
    $plugin_id = !empty($tb_item['plugin_id']) ? $tb_item['plugin_id'] : '';
    $item = \Drupal::service('tb_megamenu.menu_builder')
      ->getMenuItem($vars['menu_name'], $plugin_id);
    if (empty($tb_item)) {

      // @todo figure out how to handle NULLs in columns.
      continue;
    }
    if ($item->link == NULL) {
      continue;
    }
    $weight = $item->link
      ->getWeight();
    $title = $item->link
      ->getTitle();
    $vars['lis'][$weight][$title] = [
      '#theme' => 'tb_megamenu_item',
      '#menu_name' => $vars['menu_name'],
      '#level' => $level + 1,
      '#item' => $item,
      '#menu_config' => $vars['menu_config'],
      '#block_config' => $vars['block_config'],
      '#trail' => $vars['trail'],
      '#section' => $vars['section'],
    ];
  }

  // Sort items by link weights as array keys.
  ksort($vars['lis']);

  // Restructure to account for duplicate weights.
  $new_list = [];
  foreach ($vars['lis'] as $weight_group) {
    ksort($weight_group);
    foreach ($weight_group as $item) {
      $new_list[] = $item;
    }
  }
  $vars['lis'] = $new_list;
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = "mega-nav";
  $vars['attributes']['class'][] = "level-" . $level;
  $vars['attributes']['class'][] = "items-" . count($col);

  // Add a11y attributes.
  $vars['attributes']['role'] = 'list';
}

/**
 * Prepare variables for tb_megamenu_submenu.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_submenu(array &$vars) {
  $parent = $vars['parent'];
  $plugin_id = $parent->link
    ->getPluginId();
  $menu_config = $vars['menu_config'];
  $item_config = isset($menu_config[$plugin_id]) ? $menu_config[$plugin_id] : [];
  $submenu_config = isset($item_config['submenu_config']) ? $item_config['submenu_config'] : [];
  $vars['submenu_config'] = $submenu_config;
  \Drupal::service('tb_megamenu.menu_builder')
    ->editSubMenuConfig($submenu_config);
  $vars['rows'] = [];
  foreach ($item_config['rows_content'] as $row) {
    $vars['rows'][] = [
      '#theme' => 'tb_megamenu_row',
      '#menu_name' => $vars['menu_name'],
      '#row' => $row,
      '#parent' => $vars['parent'],
      '#level' => $vars['level'],
      '#menu_config' => $vars['menu_config'],
      '#block_config' => $vars['block_config'],
      '#trail' => $vars['trail'],
      '#section' => $vars['section'],
    ];
  }
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  if ($submenu_config['class'] != "") {
    $vars['attributes']['class'][] = $submenu_config['class'];
  }
  if ($submenu_config['group']) {
    $vars['attributes']['class'][] = 'mega-group-ct';
  }
  else {
    $vars['attributes']['class'][] = 'dropdown-menu';
    $vars['attributes']['class'][] = 'mega-dropdown-menu';
  }
  $vars['attributes']['class'][] = 'nav-child';
  $vars['attributes']['data-class'] = $submenu_config['class'];
  $vars['attributes']['data-width'] = $submenu_config['width'];
  if (!empty($submenu_config['width'])) {
    $vars['attributes']['style'] = "width: " . $submenu_config['width'] . "px;";
  }

  // Add a11y attributes.
  $vars['attributes']['role'] = 'list';
}

/**
 * Prepare variables for tb_megamenu.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu(array &$vars) {
  $menu_name = $vars['menu_name'];
  $theme = $vars['block_theme'];
  $vars['#attached']['drupalSettings']['TBMegaMenu']['theme'] = $theme;
  $menu_tree = \Drupal::menuTree();
  $menu_active_trail = \Drupal::service('menu.active_trail')
    ->getActiveTrailIds($menu_name);
  $parameters = (new MenuTreeParameters())
    ->setActiveTrail($menu_active_trail)
    ->onlyEnabledLinks();
  $menu_items = $menu_tree
    ->load($menu_name, $parameters);
  $manipulators = [
    [
      'callable' => 'menu.default_tree_manipulators:checkNodeAccess',
    ],
    [
      'callable' => 'menu.default_tree_manipulators:checkAccess',
    ],
  ];
  $menu_items = $menu_tree
    ->transform($menu_items, $manipulators);
  tb_megamenu_check_access($menu_items);

  /*
   * This function is old, we need to solve this later.
   * if (function_exists('i18n_menu_localize_tree')) {
   *  $items = i18n_menu_localize_tree($items);
   * }
   */

  // @todo what does this function to do ?
  // $items_page = menu_tree_page_data($menu_name);
  // $trail = tb_megamenu_build_page_trail($items_page);
  $menu_builder = \Drupal::service('tb_megamenu.menu_builder');
  $trail = $menu_builder
    ->buildPageTrail($menu_items);
  $menu_config = $menu_builder
    ->getMenuConfig($menu_name, $theme);
  $block_config = $menu_builder
    ->getBlockConfig($menu_name, $theme);
  $menu_builder
    ->editBlockConfig($block_config);
  $menu_builder
    ->syncConfigAll($menu_items, $menu_config, $vars['section']);
  $vars['block_config'] = $block_config;
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = $theme_name . '-' . str_replace('_', '-', $menu_name);
  if ($vars['section'] == 'frontend' && $block_config['style'] != '') {
    $vars['#attached']['library'][] = 'tb_megamenu/block_style.' . $block_config['style'];
    $vars['attributes']['class'][] = "style-" . $block_config['style'];
  }
  if ($block_config['animation'] != 'none') {
    $vars['attributes']['class'][] = "animate";
    $vars['attributes']['class'][] = $block_config['animation'];
    $vars['attributes']['data-duration'] = $block_config['duration'];
    $vars['attributes']['data-delay'] = $block_config['delay'];
    $vars['attributes']['id'] = "tb-megamenu-" . str_replace('_', '-', $menu_name);
  }
  $vars['content'] = [
    '#theme' => 'tb_megamenu_nav',
    '#menu_name' => $vars['menu_name'],
    '#level' => 0,
    '#menu_config' => $menu_config,
    '#block_config' => $block_config,
    '#items' => $menu_items,
    '#trail' => $trail,
    '#section' => $vars['section'],
  ];
  if ($vars['section'] == 'frontend' && $block_config['animation'] != 'none') {
    $css = '';
    $css .= '  transition-delay: ' . $block_config['delay'] . 'ms;';
    $css .= '  -webkit-transition-delay: ' . $block_config['delay'] . 'ms;';
    $css .= '  -ms-transition-delay: ' . $block_config['delay'] . 'ms;';
    $css .= '  -o-transition-delay: ' . $block_config['delay'] . 'ms;';
    $css .= '  transition-duration: ' . $block_config['duration'] . 'ms;';
    $css .= '  -webkit-transition-duration: ' . $block_config['duration'] . 'ms;';
    $css .= '  -ms-transition-duration: ' . $block_config['duration'] . 'ms;';
    $css .= '  -o-transition-duration: ' . $block_config['duration'] . 'ms;';
    $vars['css_style'] = $css;
  }

  // Add a11y attributes.
  $vars['attributes']['role'] = 'navigation';
  $vars['attributes']['aria-label'] = t('Main navigation');
}

/**
 * Prepare variables for tb_megamenu_backend.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_backend(array &$vars) {
  $menu_name = $vars['menu_name'];
  $theme = $vars['block_theme'];
  $menu_builder = \Drupal::service('tb_megamenu.menu_builder');
  $block_config = $menu_builder
    ->getBlockConfig($menu_name, $theme);
  $menu_builder
    ->editBlockConfig($block_config);
  $vars['menu_content'] = $menu_builder
    ->renderBlock($menu_name, $theme);
  $vars['item_toolbox'] = [
    '#theme' => 'tb_megamenu_item_toolbox',
  ];
  $vars['submenu_toolbox'] = [
    '#theme' => 'tb_megamenu_submenu_toolbox',
  ];
  $vars['column_toolbox'] = [
    '#theme' => 'tb_megamenu_column_toolbox',
    '#blocks_options' => $menu_builder
      ->getAllBlocks($theme),
  ];
  $vars['block_config'] = $block_config;
  $vars['animation_options'] = $menu_builder
    ->createAnimationOptions($block_config);
  $vars['style_options'] = $menu_builder
    ->createStyleOptions($block_config);
  $vars['edit_links'] = Url::fromRoute('entity.menu.edit_form', [
    'menu' => $menu_name,
  ])
    ->toString();

  // Attach this to init TB Mega Menu (Importance).
  $vars['#attached']['drupalSettings']['TBMegaMenu']['menu_name'] = $menu_name;
}

/**
 * Prepare variables for tb_megamenu_block.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_block(array &$vars) {
  $entity_block = \Drupal::service('tb_megamenu.menu_builder')
    ->loadEntityBlock($vars['block_id']);
  if ($entity_block) {
    $plugin = $entity_block
      ->getPlugin();
    $plugin_configuration = $plugin
      ->getConfiguration();
    $content = $plugin
      ->build();
    if ($content) {
      if (empty($vars['showblocktitle'])) {
        $plugin_configuration['label_display'] = '';
      }
      $vars['content'] = [
        '#theme' => 'block',
        '#configuration' => $plugin_configuration,
        '#plugin_id' => $plugin
          ->getPluginId(),
        '#base_plugin_id' => $plugin
          ->getBaseId(),
        '#derivative_plugin_id' => $plugin
          ->getDerivativeId(),
        'content' => $content,
        '#id' => $plugin_configuration['id'],
      ];
    }
    elseif ($vars['section'] == 'backend') {
      $vars['content'] = $plugin_configuration['label'];
    }
    $vars['attributes'] = new Attribute();
    $vars['attributes']['class'] = [];
    $vars['attributes']['class'][] = "tb-block";
    $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
    $vars['attributes']['class'][] = $theme_name;
    $vars['attributes']['data-type'] = "block";
    $vars['attributes']['data-block'] = $vars['block_id'];
  }
  else {
    $vars['content'] = NULL;
  }
}

/**
 * Prepare variables for tb_megamenu_nav.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_nav(array &$vars) {
  $items = $vars['items'];
  $level = $vars['level'];
  $vars['lis'] = [];
  foreach ($items as $item) {
    if ($item->link
      ->isEnabled()) {
      $weight = $item->link
        ->getWeight();
      $title = $item->link
        ->getTitle();
      $vars['lis'][$weight][$title] = [
        '#theme' => 'tb_megamenu_item',
        '#menu_name' => $vars['menu_name'],
        '#level' => $level + 1,
        '#item' => $item,
        '#menu_config' => $vars['menu_config'],
        '#block_config' => $vars['block_config'],
        '#trail' => $vars['trail'],
        '#section' => $vars['section'],
      ];
    }
  }

  // Sort items by link weights as array keys.
  ksort($vars['lis']);

  // Restructure to account for duplicate weights.
  $new_list = [];
  foreach ($vars['lis'] as $weight_group) {
    ksort($weight_group);
    foreach ($weight_group as $item) {
      $new_list[] = $item;
    }
  }
  $vars['lis'] = $new_list;
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = "nav";
  $vars['attributes']['class'][] = "level-" . $level;
  $vars['attributes']['class'][] = "items-" . count($items);

  // Add a11y attributes to unordered list tag.
  $vars['attributes']['role'] = 'list';
}

/**
 * Prepare variables for tb_megamenu_item.
 *
 * @param array $vars
 *   Variables are declared in theme.
 */
function template_preprocess_tb_megamenu_item(array &$vars) {
  $level = $vars['level'];
  $item = $vars['item'];
  $vars['link'] = $item->link
    ->getPluginDefinition();
  $vars['link']['url'] = $item->link
    ->getUrlObject()
    ->toString();
  $vars['link']['title_translate'] = $item->link
    ->getTitle();
  $plugin_id = $vars['link']['id'];
  $trail = $vars['trail'];
  $menu_config = $vars['menu_config'];
  $item_config = isset($menu_config[$plugin_id]['item_config']) ? $menu_config[$plugin_id]['item_config'] : [];
  $menu_builder = \Drupal::service('tb_megamenu.menu_builder');
  $menu_builder
    ->editItemConfig($item_config);
  $vars['item_config'] = $item_config;
  if (!empty($menu_config[$plugin_id]['rows_content']) && ($vars['section'] == 'backend' || !$item_config['hidesub'])) {
    $vars['submenu'] = [
      '#theme' => 'tb_megamenu_submenu',
      '#menu_name' => $vars['menu_name'],
      '#parent' => $item,
      '#level' => $level,
      '#menu_config' => $vars['menu_config'],
      '#block_config' => $vars['block_config'],
      '#trail' => $vars['trail'],
      '#section' => $vars['section'],
    ];
  }
  else {
    $vars['submenu'] = NULL;
  }
  $vars['link']['attributes'] = new Attribute();
  $vars['attributes'] = new Attribute();
  $vars['attributes']['class'] = [];
  $vars['link']['attributes']['class'] = [];
  $theme_name = str_replace('_', '-', $vars['theme_hook_original']);
  $vars['attributes']['class'][] = $theme_name;
  $vars['attributes']['class'][] = 'level-' . $level;
  $vars['attributes']['class'][] = 'mega';
  if (!empty($item_config['class'])) {
    $vars['attributes']['class'][] = $item_config['class'];
  }
  if (!empty($item_config['alignsub'])) {
    $vars['attributes']['class'][] = "mega-align-" . $item_config['alignsub'];
  }
  if ($item_config['hidewcol']) {
    $vars['attributes']['class'][] = "sub-hidden-collapse";
  }
  if ($item_config['group'] && $vars['submenu']) {
    $vars['attributes']['class'][] = "mega-group";
    $vars['link']['attributes']['class'][] = 'mega-group-title';
  }
  elseif ($vars['submenu'] && !$item_config['hidesub']) {
    $vars['link']['attributes']['class'][] = 'dropdown-toggle';
    if ($level == 1) {
      $vars['attributes']['class'][] = 'dropdown';
    }
    else {
      $vars['attributes']['class'][] = 'dropdown-submenu';
    }
  }
  if (isset($trail[$plugin_id])) {
    $vars['attributes']['class'][] = 'active';
    $vars['attributes']['class'][] = 'active-trail';
    $vars['link']['attributes']['class'][] = 'active-trail';
  }
  $vars['attributes']['data-id'] = $plugin_id;
  $vars['attributes']['data-level'] = $level;
  $vars['attributes']['data-type'] = 'menu_item';
  $vars['attributes']['data-class'] = $item_config['class'];
  $vars['attributes']['data-xicon'] = $item_config['xicon'];
  $vars['attributes']['data-caption'] = $item_config['caption'];
  $vars['attributes']['data-alignsub'] = $item_config['alignsub'];
  $vars['attributes']['data-group'] = $item_config['group'];
  $vars['attributes']['data-hidewcol'] = $item_config['hidewcol'];
  $vars['attributes']['data-hidesub'] = $item_config['hidesub'];
  $vars['attributes']['data-label'] = $item->link
    ->getTitle();
  if ($vars['section'] == 'backend') {
    $vars['attributes']['title'] = t("Menu Item");
  }

  // Add a11y attributes to list item tag.
  $vars['attributes']['aria-level'] = $level;

  // Add a11y attributes to anchor tag.
  if (!empty($vars['link']['description'])) {
    $vars['link']['attributes']['title'] = $vars['link']['description'];
  }
  if ($vars['submenu'] != NULL) {
    $vars['link']['attributes']['aria-expanded'] = t('false');
  }

  // Make other menu attributes available to TB Mega Menu Anchor Links.
  if (!empty($vars['link']['options']['attributes']['target'])) {
    $vars['link']['attributes']['target'] = $vars['link']['options']['attributes']['target'];
  }
  if (!empty($vars['link']['options']['attributes']['rel'])) {
    $vars['link']['attributes']['rel'] = $vars['link']['options']['attributes']['rel'];
  }
  if (!empty($vars['link']['options']['attributes']['class'])) {
    $vars['link']['attributes']
      ->addClass($vars['link']['options']['attributes']['class']);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function tb_megamenu_form_menu_edit_form_alter(&$form, &$form_state, $form_id) {
  $form['actions']['submit']['#submit'][] = 'tb_megamenu_update_megamenus';
}

/**
 * Sync configuration with tb_megamenu when item in menu is updated.
 */
function tb_megamenu_update_megamenus(&$form, FormStateInterface $form_state) {
  $menu_name = $form_state
    ->getValue('id');
  if ($menu_name) {
    $theme = \Drupal::config('system.theme')
      ->get('default');
    $menu_builder = \Drupal::service('tb_megamenu.menu_builder');
    if ($menu_config = $menu_builder
      ->getMenuConfig($menu_name, $theme)) {
      $menu_tree_parameters = (new MenuTreeParameters())
        ->onlyEnabledLinks();
      $menu_items = \Drupal::menuTree()
        ->load($menu_name, $menu_tree_parameters);
      $block_config = $menu_builder
        ->getBlockConfig($menu_name, $theme);
      $menu_builder
        ->syncConfigAll($menu_items, $menu_config, 'backend');
      $menu_builder
        ->syncOrderMenus($menu_config);
      $config = MegaMenuConfig::loadMenu($menu_name, $theme);
      if ($config === NULL) {
        \Drupal::messenger()
          ->addStatus(t("Cannot create a new config object in save!"));
        return;
      }
      $config->block_config = Json::encode($block_config);
      $config->menu_config = Json::encode($menu_config);
      $config
        ->save();
    }
  }
}

/**
 * Implements hook_block_view_BASE_BLOCK_ID_alter().
 */
function tb_megamenu_block_view_tb_megamenu_menu_block_alter(array &$build, BlockPluginInterface $block) {
  $build['#contextual_links']['tb_megamenu_block']['route_parameters'] = [
    'tb_megamenu' => "{$build['#derivative_plugin_id']}__{$block->getThemeName()}",
  ];
  $build['#contextual_links']['menu']['route_parameters'] = [
    'menu' => $build['#derivative_plugin_id'],
  ];
}

/**
 * Recursively remove menu items without access rights.
 *
 * @param array $menu_items
 *   Menu section to check.
 */
function tb_megamenu_check_access(array &$menu_items) {
  foreach ($menu_items as $route => &$item) {
    if ($item->access instanceof AccessResultInterface && !$item->access
      ->isAllowed()) {
      unset($menu_items[$route]);
      continue;
    }
    if ($item->hasChildren) {
      tb_megamenu_check_access($item->subtree);
    }
  }
}

Functions

Namesort descending Description
tb_megamenu_block_view_tb_megamenu_menu_block_alter Implements hook_block_view_BASE_BLOCK_ID_alter().
tb_megamenu_check_access Recursively remove menu items without access rights.
tb_megamenu_form_menu_edit_form_alter Implements hook_form_FORM_ID_alter().
tb_megamenu_help Implements hook_help().
tb_megamenu_theme Implements hook_theme().
tb_megamenu_update_megamenus Sync configuration with tb_megamenu when item in menu is updated.
template_preprocess_tb_megamenu Prepare variables for tb_megamenu.
template_preprocess_tb_megamenu_backend Prepare variables for tb_megamenu_backend.
template_preprocess_tb_megamenu_block Prepare variables for tb_megamenu_block.
template_preprocess_tb_megamenu_column Prepare variables for tb_megamenu_column.
template_preprocess_tb_megamenu_item Prepare variables for tb_megamenu_item.
template_preprocess_tb_megamenu_nav Prepare variables for tb_megamenu_nav.
template_preprocess_tb_megamenu_row Prepare variables for tb_megamenu_row.
template_preprocess_tb_megamenu_submenu Prepare variables for tb_megamenu_submenu.
template_preprocess_tb_megamenu_subnav Prepare variables for tb_megamenu_subnav.