-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for menu items as an array and template strings as callables
- Loading branch information
1 parent
e6451e3
commit 4ee01a8
Showing
4 changed files
with
203 additions
and
47 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
* MarkupMenu is a module for generating menu markup. See README.md for more details. | ||
* Some ideas and code in this module are based on the Markup Simple Navigation module. | ||
* | ||
* @version 0.10.0 | ||
* @version 0.11.0 | ||
* @author Teppo Koivula <[email protected]> | ||
* @license Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/ | ||
*/ | ||
|
@@ -54,6 +54,12 @@ class MarkupMenu extends WireData implements Module { | |
'parent' => '&--parent', | ||
'has_children' => '&--has-children', | ||
], | ||
'array_item_keys' => [ | ||
'id' => 'id', | ||
'is_current' => 'is_current', | ||
'is_parent' => 'is_parent', | ||
'children' => 'children', | ||
], | ||
]; | ||
|
||
/** | ||
|
@@ -83,12 +89,53 @@ public function render(array $options = []): string { | |
// generate menu markup | ||
$menu = ''; | ||
if (!empty($options['root_page'] || !empty($options['menu_items']))) { | ||
$menu = $this->renderTree($options, $options['root_page'], $options['menu_items']); | ||
if (is_array($options['menu_items'])) { | ||
$menu = $this->renderArray($options, $options['menu_items']); | ||
} else { | ||
$menu = $this->renderTree($options, $options['root_page'], $options['menu_items']); | ||
} | ||
} | ||
|
||
return $menu; | ||
} | ||
|
||
/** | ||
* Render menu from fixed array of items | ||
* | ||
* @param array $options Options for rendering | ||
* @param array $items Menu items | ||
* @param int $level Current tree level (depth) | ||
* @return string Rendered menu markup | ||
*/ | ||
protected function renderArray(array $options = [], array $items, int $level = 1): string { | ||
|
||
$out = ''; | ||
|
||
// iterate items and render markup for each separately | ||
foreach ($items as $item) { | ||
$out .= $this->renderArrayItem($options, $item, $level); | ||
} | ||
|
||
if (!empty($out) && (!empty($options['templates']['list']) || !empty($options['templates']['nav']))) { | ||
|
||
// set up a placeholders | ||
$placeholders = [ | ||
'level' => $level, | ||
'root_page' => $options['root_page'], | ||
]; | ||
|
||
// generate list markup | ||
$out = $this->applyTemplate('list', $placeholders, $options, null, $out); | ||
|
||
// generate nav markup | ||
if ($level === 1) { | ||
$out = $this->applyTemplate('nav', $placeholders, $options, null, $out); | ||
} | ||
} | ||
|
||
return $out; | ||
} | ||
|
||
/** | ||
* Render tree of items using recursion | ||
* | ||
|
@@ -128,7 +175,6 @@ protected function renderTree(array $options = [], Page $root = null, PageArray | |
if ($level === 1) { | ||
$out = $this->applyTemplate('nav', $placeholders, $options, null, $out); | ||
} | ||
|
||
} | ||
|
||
return $out; | ||
|
@@ -164,6 +210,78 @@ protected function ___getItems(array $options, Page $root = null, int $level): P | |
return $items; | ||
} | ||
|
||
/** | ||
* Render markup for a single array item | ||
* | ||
* @param array $options Options for rendering | ||
* @param array $item Menu item being rendered | ||
* @param int $level Current tree level (depth) | ||
* @return string Rendered menu item markup | ||
*/ | ||
protected function ___renderArrayItem(array $options = [], array $item = null, int $level = 1): string { | ||
|
||
$out = ''; | ||
|
||
// bail out early if item is empty | ||
if (empty($item)) { | ||
return $out; | ||
} | ||
|
||
// instantiate new MarkupMenuData object and populate with item properties | ||
$data_item = new MarkupMenuData($item); | ||
|
||
// array item keys | ||
$keys = $options['array_item_keys']; | ||
|
||
// default classes | ||
$classes = []; | ||
if (!empty($options['classes']['page_id']) && !empty($item[$keys['id']])) { | ||
$classes['page_id'] = $options['classes']['page_id'] . $item[$keys['id']]; | ||
} | ||
|
||
// is this current page? | ||
$item_is_current = $item[$keys['is_current']] ?? (!empty($item[$keys['id']]) && $options['current_page'] && $options['current_page']->id === $item[$keys['id']]); | ||
if ($item_is_current) $classes['current'] = $options['classes']['current']; | ||
|
||
// is this a parent page? | ||
$item_is_parent = $item[$keys['is_parent']] ?? (!$item_is_current && !empty($item[$keys['id']]) && (!empty($root) && $item->id !== $root->id || !$options['flat_root']) && $options['current_page'] && $options['current_page']->parents->has("id=" . $item[$keys['id']])); | ||
if ($item_is_parent) $classes['parent'] = $options['classes']['parent']; | ||
|
||
// have we reached the level limit? | ||
$level_limit_reached = $options['exclude']['level_greater_than'] && $level >= $options['exclude']['level_greater_than']; | ||
|
||
// does this page have children? | ||
$has_children = (!empty($root) && !empty($item[$keys['id']]) && $item[$keys['id']] !== $root->id || !$options['flat_root']) && !$level_limit_reached && !empty($item[$keys['children']]); | ||
if ($has_children) $classes['has_children'] = $options['classes']['has_children']; | ||
|
||
// should we render the children for this item? | ||
$with_children = $has_children && (!$options['collapsed'] || $item_is_current || $item_is_parent); | ||
|
||
// placeholders for string replacements | ||
$placeholders = array_merge( | ||
[ | ||
'level' => $level, | ||
'item' => $data_item, | ||
'classes' => $classes, | ||
], | ||
$options['placeholders'] | ||
); | ||
|
||
// generate markup for menu item | ||
$item_template_name = 'item' . ($item_is_current ? '_current' : ''); | ||
$item_markup = $this->applyTemplate($item_template_name, $placeholders, $options, $data_item); | ||
|
||
// generate markup for menu item children | ||
if ($with_children) { | ||
$item_markup .= $this->renderArray($options, $item[$keys['children']], $level + 1); | ||
} | ||
|
||
// generate markup for current list item | ||
$out .= $this->applyTemplate('list_item', $placeholders, $options, $data_item, $item_markup); | ||
|
||
return $out; | ||
} | ||
|
||
/** | ||
* Render markup for a single menu item | ||
* | ||
|
@@ -272,12 +390,13 @@ protected function getPage($source = null, $default = null): ?Page { | |
* Get template for rendering an element | ||
* | ||
* @param string $template_name Template name | ||
* @param Page|null $item Item being rendered | ||
* @param Page|MarkupMenuData|null $item Item being rendered | ||
* @param array $options An array of options | ||
* @return string Template | ||
*/ | ||
protected function ___getTemplate($template_name, ?Page $item = null, array $options = []): string { | ||
return $options['templates'][$template_name]; | ||
protected function ___getTemplate($template_name, ?WireData $item = null, array $options = []): string { | ||
$template = $options['templates'][$template_name]; | ||
return is_callable($template) ? $template($item, $options) : $template; | ||
} | ||
|
||
/** | ||
|
@@ -286,11 +405,11 @@ protected function ___getTemplate($template_name, ?Page $item = null, array $opt | |
* @param string $template_name Name of the template | ||
* @param array $placeholders Array of placeholders for string replacements | ||
* @param array $options An array of options | ||
* @param Page|null $item Item being rendered | ||
* @param Page|MarkupMenuData|null $item Item being rendered | ||
* @param string|null $content Content to be wrapped in template | ||
* @return string Content either wrapped in template, or as-is if no template was defined | ||
*/ | ||
protected function applyTemplate(string $template_name, array $placeholders, array $options, ?Page $item = null, ?string $content = null): string { | ||
protected function applyTemplate(string $template_name, array $placeholders, array $options, ?WireData $item = null, ?string $content = null): string { | ||
|
||
$out = ''; | ||
|
||
|
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