Skip to content

Commit

Permalink
Improve active menu item detection
Browse files Browse the repository at this point in the history
  • Loading branch information
jeroennoten committed Aug 20, 2016
1 parent 306bf94 commit 8f76b06
Show file tree
Hide file tree
Showing 12 changed files with 516 additions and 22 deletions.
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"JeroenNoten\\LaravelAdminLte\\": "src/"
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
]
},
"require": {
"laravel/framework": "5.1.*|5.2.*|5.3.*",
"php": ">=5.5.9"
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
<phpunit colors="true">
<testsuites>
<testsuite>
<directory suffix="Test.php">./tests</directory>
Expand Down
12 changes: 4 additions & 8 deletions resources/views/partials/menu-item-top-nav.blade.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
@if (is_array($item))
<li @if (isset($item['submenu'])) class="dropdown" @endif
@if (isset($item['url']))
{!! Request::is($item['url']) ? 'class="active"' : null !!}
@endif
>
<a href="{{ isset($item['url']) ? url($item['url']) : '#' }}"
<li class="{{ $item['top_nav_class'] }}">
<a href="{{ $item['href'] }}"
@if (isset($item['submenu'])) class="dropdown-toggle" data-toggle="dropdown" @endif
>
<i class="fa fa-fw fa-{{ $item['icon'] or 'circle-o' }} {{ isset($item['icon_color']) ? 'text-' . $item['icon_color'] : '' }}"></i>
Expand All @@ -25,8 +21,8 @@
<li class="dropdown-header">{{ $subitem }}</li>
@endif
@else
<li>
<a href="{{ isset($subitem['url']) ? url($subitem['url']) : '#' }}">
<li class="{{ $subitem['top_nav_class'] }}">
<a href="{{ $subitem['href'] }}">
<i class="fa fa-{{ $subitem['icon'] or 'circle-o' }} {{ isset($subitem['icon_color']) ? 'text-' . $subitem['icon_color'] : '' }}"></i>
{{ $subitem['text'] }}
@if (isset($subitem['label']))
Expand Down
10 changes: 3 additions & 7 deletions resources/views/partials/menu-item.blade.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
@if (is_string($item))
<li class="header">{{ $item }}</li>
@else
<li {!! isset($item['submenu']) ? 'class="treeview"' : '' !!}
@if (isset($item['url']))
{!! Request::is($item['url']) ? 'class="active"' : null !!}
@endif
>
<a href="{{ isset($item['url']) ? url($item['url']) : '#' }}">
<li class="{{ $item['class'] }}">
<a href="{{ $item['href'] }}">
<i class="fa fa-fw fa-{{ $item['icon'] or 'circle-o' }} {{ isset($item['icon_color']) ? 'text-' . $item['icon_color'] : '' }}"></i>
<span>{{ $item['text'] }}</span>
@if (isset($item['label']))
Expand All @@ -18,7 +14,7 @@
@endif
</a>
@if (isset($item['submenu']))
<ul class="treeview-menu">
<ul class="{{ $item['submenu_class'] }}">
@each('adminlte::partials.menu-item', $item['submenu'], 'item')
</ul>
@endif
Expand Down
12 changes: 10 additions & 2 deletions src/AdminLte.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@


use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Routing\UrlGenerator;
use JeroenNoten\LaravelAdminLte\Events\BuildingMenu;
use JeroenNoten\LaravelAdminLte\Menu\ActiveChecker;
use JeroenNoten\LaravelAdminLte\Menu\Builder;

class AdminLte
Expand All @@ -14,9 +16,15 @@ class AdminLte

private $events;

public function __construct(Dispatcher $events)
private $urlGenerator;

private $activeChecker;

public function __construct(Dispatcher $events, UrlGenerator $urlGenerator, ActiveChecker $activeChecker)
{
$this->events = $events;
$this->urlGenerator = $urlGenerator;
$this->activeChecker = $activeChecker;
}

public function menu()
Expand All @@ -30,7 +38,7 @@ public function menu()

protected function buildMenu()
{
$builder = new Builder;
$builder = new Builder($this->urlGenerator, $this->activeChecker);

$this->events->fire(new BuildingMenu($builder));

Expand Down
71 changes: 71 additions & 0 deletions src/Menu/ActiveChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php


namespace JeroenNoten\LaravelAdminLte\Menu;


use Illuminate\Http\Request;

class ActiveChecker
{
private $request;

public function __construct(Request $request)
{
$this->request = $request;
}

public function isActive($item)
{
if (isset($item['active'])) {
return $this->isExplicitActive($item['active']);
}

if (isset($item['submenu'])) {
return $this->containsActive($item['submenu']);
}

if (isset($item['url'])) {
return $this->isActiveUrl($item['url']);
}

return false;
}

protected function isActiveUrl($url)
{
return $this->checkExact($url) || $this->checkSub($url);
}

protected function checkExact($url)
{
return $this->request->is($url);
}

protected function checkSub($url)
{
return $this->request->is($url . '/*');
}

protected function containsActive($items)
{
foreach ($items as $item) {
if ($this->isActive($item)) {
return true;
}
}

return false;
}

private function isExplicitActive($active)
{
foreach ($active as $url) {
if ($this->isActiveUrl($url)) {
return true;
}
}

return false;
}
}
81 changes: 79 additions & 2 deletions src/Menu/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,93 @@
namespace JeroenNoten\LaravelAdminLte\Menu;


use Illuminate\Contracts\Routing\UrlGenerator;

class Builder
{
public $menu = [];

private $urlGenerator;

private $activeChecker;

public function __construct(UrlGenerator $urlGenerator, ActiveChecker $activeChecker)
{
$this->urlGenerator = $urlGenerator;
$this->activeChecker = $activeChecker;
}

public function add()
{
$items = func_get_args();
$items = $this->transformItems(func_get_args());

foreach($items as $item) {
foreach ($items as $item) {
array_push($this->menu, $item);
}
}

protected function transformItems($items)
{
return array_map([$this, 'transformItem'], $items);
}

protected function transformItem($item)
{
if (is_string($item)) {
return $item;
}

$item['href'] = $this->makeHref($item);
$item['active'] = $this->isActive($item);

if (isset($item['submenu'])) {
$item['submenu'] = $this->transformItems($item['submenu']);
$item['submenu_open'] = $item['active'];
$item['submenu_classes'] = $this->makeSubmenuClasses($item);
$item['submenu_class'] = implode(' ', $item['submenu_classes']);
}

$item['classes'] = $this->makeClasses($item);
$item['class'] = implode(' ', $item['classes']);
$item['top_nav_classes'] = $this->makeClasses($item, true);
$item['top_nav_class'] = implode(' ', $item['top_nav_classes']);

return $item;
}

protected function makeHref($item)
{
if (!isset($item['url'])) {
return '#';
}

return $this->urlGenerator->to($item['url']);
}

protected function makeClasses($item, $topNav = false)
{
$classes = [];

if ($item['active']) {
$classes[] = 'active';
}

if (isset($item['submenu'])) {
$classes[] = $topNav ? 'dropdown': 'treeview';
}

return $classes;
}

protected function isActive($item)
{
return $this->activeChecker->isActive($item);
}

protected function makeSubmenuClasses($item)
{
$classes = ['treeview-menu'];

return $classes;
}
}
19 changes: 19 additions & 0 deletions tests/AdminLteTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

use JeroenNoten\LaravelAdminLte\Events\BuildingMenu;

class AdminLteTest extends TestCase
{
public function testMenu()
{
$adminLte = $this->makeAdminLte();

$this->getDispatcher()->listen(BuildingMenu::class, function (BuildingMenu $event) {
$event->menu->add(['text' => 'Home']);
});

$menu = $adminLte->menu();

$this->assertEquals('Home', $menu[0]['text']);
}
}
95 changes: 95 additions & 0 deletions tests/Menu/ActiveCheckerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php


class ActiveCheckerTest extends TestCase
{
public function testExact()
{
$checker = $this->makeActiveChecker('http://example.com/about');

$this->assertTrue($checker->isActive(['url' => 'about']));
}

public function testRoot()
{
$checker = $this->makeActiveChecker('http://example.com');

$this->assertTrue($checker->isActive(['url' => '/']));
}

public function testNotActive()
{
$checker = $this->makeActiveChecker('http://example.com/about');

$this->assertFalse($checker->isActive(['url' => 'home']));
}

public function testStringNotActive()
{
$checker = $this->makeActiveChecker();

$this->assertFalse($checker->isActive('HEADER'));
}

public function testSub()
{
$checker = $this->makeActiveChecker('http://example.com/about/sub');

$this->assertTrue($checker->isActive(['url' => 'about']));
}

public function testSubmenu()
{
$checker = $this->makeActiveChecker('http://example.com/home');

$isActive = $checker->isActive([
'submenu' => [
['url' => 'home']
]
]);

$this->assertTrue($isActive);
}

public function testMultiLevelSubmenu()
{
$checker = $this->makeActiveChecker('http://example.com/home');

$isActive = $checker->isActive([
'text' => 'Level 0',
'submenu' => [
[
'text' => 'Level 1',
'submenu' => [
['url' => 'home']
]
]
]
]);

$this->assertTrue($isActive);
}

public function testExplicitActive()
{
$checker = $this->makeActiveChecker('http://example.com/home');

$isActive = $checker->isActive([
'active' => ['home']
]);

$this->assertTrue($isActive);
}

public function testExplicitActiveRegex()
{
$checker = $this->makeActiveChecker('http://example.com/home/sub');

$isActive = $checker->isActive([
'active' => ['home/*']
]);

$this->assertTrue($isActive);
}

}
Loading

1 comment on commit 8f76b06

@Lucky-Loek
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing, thanks for this!

Please sign in to comment.