diff --git a/src/Plugin/Block/MenuBlock.php b/src/Plugin/Block/MenuBlock.php index fab522a..5d94c95 100644 --- a/src/Plugin/Block/MenuBlock.php +++ b/src/Plugin/Block/MenuBlock.php @@ -41,17 +41,78 @@ class MenuBlock extends SystemMenuBlock { '#description' => $this->t('All menu links that have children will "Show as expanded".'), ]; + $form['advanced']['expand_only_active_trails'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Expand only active tree'), + '#default_value' => $config['expand_only_active_trails'], + '#description' => $this->t('All menu links that are in active trails will "Show as expanded".'), + '#states' => [ + 'visible' => [ + ':input[name="settings[expand]"]' => ['checked' => TRUE], + ] + ] + ]; + $menu_name = $this->getDerivativeId(); $menus = Menu::loadMultiple(array($menu_name)); $menus[$menu_name] = $menus[$menu_name]->label(); /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */ $menu_parent_selector = \Drupal::service('menu.parent_form_selector'); - $form['advanced']['parent'] = $menu_parent_selector->parentSelectElement($config['parent'], '', $menus); + if (strpos($config['parent'], 'active_trail') === FALSE) { + $form['advanced']['parent'] = $menu_parent_selector->parentSelectElement($config['parent'], '', $menus); + } + else { + $form['advanced']['parent'] = [ + '#type' => 'select', + '#options' => $menu_parent_selector->getParentSelectOptions('', $menus), + '#default_value' => $config['parent'], + ]; + } + $form['advanced']['parent']['#options'] += [ + $menu_name . ':active_trail' => $this->t('')->render(), + $menu_name . ':active_trail_parent' => $this->t('')->render(), + $menu_name . ':active_trail_custom' => $this->t('')->render(), + ]; $form['advanced']['parent'] += [ '#title' => $this->t('Fixed parent item'), '#description' => $this->t('Alter the options in “Menu levels” to be relative to the fixed parent item. The block will only contain children of the selected menu link.'), + '#attributes' => [ + 'class' => ['active-trail'], + ], + ]; + + $custom_level_options = function() { + $options = range(0,9); + return array_combine($options, $options); + }; + + $form['advanced']['custom_level'] = [ + '#type' => 'select', + '#title' => $this->t('Set custom depth, set dynamically relative to the active trail.'), + '#default_value' => $config['custom_level'], + '#options' => $custom_level_options(), + '#states' => [ + 'visible' => [ + '.active-trail' => ['value' => $menu_name . ':active_trail_custom'], + ], + ], + ]; + + $form['advanced']['hide_children'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Hide children of parent in active trail?'), + '#default_value' => $config['hide_children'], + '#states' => [ + 'visible' => [ + '.active-trail' => [ + ['value' => $menu_name . ':active_trail'], + ['value' => $menu_name . ':active_trail_parent'], + ['value' => $menu_name . ':active_trail_custom'], + ], + ], + ], ]; $form['style'] = [ @@ -99,8 +160,11 @@ class MenuBlock extends SystemMenuBlock { */ public function blockSubmit($form, FormStateInterface $form_state) { $this->configuration['level'] = $form_state->getValue('level'); + $this->configuration['custom_level'] = $form_state->getValue('custom_level'); + $this->configuration['hide_children'] = $form_state->getValue('hide_children'); $this->configuration['depth'] = $form_state->getValue('depth'); $this->configuration['expand'] = $form_state->getValue('expand'); + $this->configuration['expand_only_active_trails'] = $form_state->getValue('expand_only_active_trails'); $this->configuration['parent'] = $form_state->getValue('parent'); $this->configuration['suggestion'] = $form_state->getValue('suggestion'); } @@ -114,11 +178,21 @@ class MenuBlock extends SystemMenuBlock { // Adjust the menu tree parameters based on the block's configuration. $level = $this->configuration['level']; + $custom_level = $this->configuration['custom_level']; $depth = $this->configuration['depth']; $expand = $this->configuration['expand']; + $expand_only_active_trails = $this->configuration['expand_only_active_trails']; $parent = $this->configuration['parent']; + $hide_children = $this->configuration['hide_children']; $suggestion = $this->configuration['suggestion']; + $trail_ids = $this->menuActiveTrail->getActiveTrailIds($menu_name); + $trail_ids = array_reverse(array_filter($trail_ids)); + + if ($parent == $menu_name . ':' . 'active_trail_custom') { + $level = $this->setActiveTrailLevel($level, $custom_level, $trail_ids); + } + $parameters->setMinDepth($level); // When the depth is configured to zero, there is no depth limit. When depth // is non-zero, it indicates the number of levels that must be displayed. @@ -128,29 +202,47 @@ class MenuBlock extends SystemMenuBlock { $parameters->setMaxDepth(min($level + $depth - 1, $this->menuTree->maxDepth())); } // If expandedParents is empty, the whole menu tree is built. - if ($expand) { + if ($expand && !$expand_only_active_trails) { $parameters->expandedParents = array(); } // When a fixed parent item is set, root the menu tree at the given ID. if ($menuLinkID = str_replace($menu_name . ':', '', $parent)) { - $parameters->setRoot($menuLinkID); - - // If the starting level is 1, we always want the child links to appear, - // but the requested tree may be empty if the tree does not contain the - // active trail. - if ($level === 1 || $level === '1') { - // Check if the tree contains links. - $tree = $this->menuTree->load(NULL, $parameters); - if (empty($tree)) { - // Change the request to expand all children and limit the depth to - // the immediate children of the root. - $parameters->expandedParents = array(); - $parameters->setMinDepth(1); - $parameters->setMaxDepth(1); - // Re-load the tree. + if (strpos($menuLinkID, 'custom') == FALSE) { + // Active trail or Active trail parent option. + if (strpos($menuLinkID, 'active_trail') !== FALSE) { + // $trail_ids = $this->menuActiveTrail->getActiveTrailIds($menu_name); + // $trail_ids = array_reverse(array_filter($trail_ids)); + if ($menuLinkID == 'active_trail') { + $menuLinkID = end($trail_ids); + } + // Active trail parent. + else { + array_pop($trail_ids); + $menuLinkID = end($trail_ids); + } + } + if ($menuLinkID) { + $parameters->setRoot($menuLinkID); + } + + // If the starting level is 1, we always want the child links to appear, + // but the requested tree may be empty if the tree does not contain the + // active trail. + if ($level === 1 || $level === '1') { + // Check if the tree contains links. $tree = $this->menuTree->load(NULL, $parameters); + if (empty($tree)) { + // Change the request to expand all children and limit the depth to + // the immediate children of the root. + $parameters->expandedParents = array(); + $parameters->setMinDepth(1); + $parameters->setMaxDepth(1); + // Re-load the tree. + $tree = $this->menuTree->load(NULL, $parameters); + } } } + // end custom } // Load the tree if we haven't already. @@ -161,7 +253,13 @@ class MenuBlock extends SystemMenuBlock { array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'), ); + $tree = $this->menuTree->transform($tree, $manipulators); + // Hide parent children if it is set in config. + if ($hide_children) { + $tree = $this->hideParentChildren($tree); + } + $build = $this->menuTree->build($tree); if (!empty($build['#theme'])) { @@ -176,13 +274,54 @@ class MenuBlock extends SystemMenuBlock { } /** + * Set the menu level relative to the active trail. + */ + public function setActiveTrailLevel($level, $custom_level, $trail_ids) { + $dynamic_level = count($trail_ids) - $custom_level; + if ($dynamic_level >= 1) { + $level = $dynamic_level; + } + + return $level; + } + + /** + * Hide the children of the active parent. + */ + public function hideParentChildren($tree) { + $not_active_items = []; + $unset = FALSE; + if (count($tree)) { + foreach ($tree as $id => $branch) { + if (!$branch->inActiveTrail) { + $not_active_items[$id] = $id; + } + if ($branch->inActiveTrail && $branch->hasChildren) { + $unset = TRUE; + } + } + } + + if ($unset) { + foreach ($not_active_items as $not_active_item) { + unset($tree[$not_active_item]); + } + } + + return $tree; + } + + /** * {@inheritdoc} */ public function defaultConfiguration() { return [ 'level' => 1, + 'custom_level' => 1, + 'hide_children' => 0, 'depth' => 0, 'expand' => 0, + 'expand_only_active_trails' => 0, 'parent' => $this->getDerivativeId() . ':', 'suggestion' => strtr($this->getDerivativeId(), '-', '_'), ];