Make active first-level nav items better distinguishable

This commit is contained in:
Jennifer Mourek 2019-03-19 13:45:31 +01:00
parent 1e79ba5c1c
commit b3b80fdc31
7 changed files with 114 additions and 34 deletions

View File

@ -37,6 +37,13 @@ class NavigationItem implements IteratorAggregate
*/ */
protected $active; protected $active;
/**
* Whether this item is selected
*
* @var bool
*/
protected $selected;
/** /**
* The CSS class used for the outer li element * The CSS class used for the outer li element
* *
@ -213,6 +220,39 @@ class NavigationItem implements IteratorAggregate
return $this; return $this;
} }
/**
* Return whether this item is selected
*
* @return bool
*/
public function getSelected()
{
if ($this->selected === null) {
$this->active = false;
if ($this->getUrl() !== null && Icinga::app()->getRequest()->getUrl()->matches($this->getUrl())) {
$this->setSelected();
}
}
return $this->selected;
}
/**
* Set whether this item is active
*
* If it's active and has a parent, the parent gets activated as well.
*
* @param bool $selected
*
* @return $this
*/
public function setSelected($selected = true)
{
$this->selected = (bool) $selected;
return $this;
}
/** /**
* Get the CSS class used for the outer li element * Get the CSS class used for the outer li element
* *

View File

@ -322,6 +322,10 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
$cssClasses[] = static::CSS_CLASS_ACTIVE; $cssClasses[] = static::CSS_CLASS_ACTIVE;
} }
if ($item->getSelected()) {
$cssClasses[] = static::CSS_CLASS_SELECTED;
}
if ($cssClass = $item->getCssClass()) { if ($cssClass = $item->getCssClass()) {
$cssClasses[] = $cssClass; $cssClasses[] = $cssClass;
} }

View File

@ -22,6 +22,13 @@ interface NavigationRendererInterface
*/ */
const CSS_CLASS_ACTIVE = 'active'; const CSS_CLASS_ACTIVE = 'active';
/**
* CSS class for selected items
*
* @var string
*/
const CSS_CLASS_SELECTED = 'selected';
/** /**
* CSS class for dropdown items * CSS class for dropdown items
* *

View File

@ -79,10 +79,16 @@
> a { > a {
padding: 0.5em 0.5em 0.5em .75em; padding: 0.5em 0.5em 0.5em .75em;
}
&:focus, &:hover { &.active:not(.selected) > a:focus,
color: @menu-highlight-color; &.active:not(.selected) > a:hover {
} background-color: mix(#000, @menu-active-bg-color, 20);
}
&:not(.selected) > a:hover,
&:not(.selected) > a:focus {
background-color: mix(#000, @menu-bg-color, 20);
} }
// Balance icon weight for non active menu items // Balance icon weight for non active menu items
@ -102,9 +108,6 @@ ul:not(.nav-level-2) > .selected > a {
background-color: @menu-highlight-color; background-color: @menu-highlight-color;
color: @text-color-inverted; color: @text-color-inverted;
&:focus {
background-color: mix(#000, @menu-highlight-color, 20);
}
&:hover { &:hover {
color: @text-color-inverted; color: @text-color-inverted;
@ -134,26 +137,19 @@ ul:not(.nav-level-2) > .selected > a {
> a { > a {
color: @menu-2ndlvl-color; color: @menu-2ndlvl-color;
font-size: @font-size-small; font-size: @font-size-small;
padding: 0.364em 0.545em 0.364em .8em; padding: 0.364em 0.545em 0.364em (@icon-width + .75em);
}
&:hover, &:focus { &:not(.selected):not(.active) > a:hover,
color: @menu-2ndlvl-highlight-color; &:not(.selected):not(.active) > a:focus {
} background-color: mix(#000, @menu-active-bg-color, 20);
color: @menu-2ndlvl-highlight-color;
} }
&.active { &.active {
background-color: @menu-highlight-color; background-color: @menu-highlight-color;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
&.active {
background-color: @menu-highlight-color;
overflow: hidden;
position: relative;
}
&.active > a:focus {
background-color: mix(#000, @menu-highlight-color, 20);
} }
// Little caret on active level-2 item // Little caret on active level-2 item

View File

@ -52,11 +52,11 @@
var $active = _this.$menu.find('li.active'); var $active = _this.$menu.find('li.active');
if ($active.length) { if ($active.length) {
$active.each(function() { $active.each(function() {
_this.setActive($(this)); _this.setActiveAndSelected($(this));
}); });
} else { } else {
// if no item is marked as active, try to select the menu from the current URL // if no item is marked as active, try to select the menu from the current URL
_this.setActiveByUrl($('#col1').data('icingaUrl')); _this.setActiveAndSelectedByUrl($('#col1').data('icingaUrl'));
} }
} }
@ -70,7 +70,7 @@
// restore selection to current active element // restore selection to current active element
if (this.active) { if (this.active) {
var $el = $(this.icinga.utils.getElementByDomPath(this.active)); var $el = $(this.icinga.utils.getElementByDomPath(this.active));
this.setActive($el); this.setActiveAndSelected($el);
/* /*
* Recreate the html content of the menu item to force the browser to update the layout, or else * Recreate the html content of the menu item to force the browser to update the layout, or else
@ -98,9 +98,9 @@
if (href.match(/#/)) { if (href.match(/#/)) {
// ...it may be a menu section without a dedicated link. // ...it may be a menu section without a dedicated link.
// Switch the active menu item: // Switch the active menu item:
_this.setActive($a); _this.setActiveAndSelected($a);
} else { } else {
_this.setActive($(event.target)); _this.setActiveAndSelected($(event.target));
} }
// update target url of the menu container to the clicked link // update target url of the menu container to the clicked link
var $menu = $('#menu'); var $menu = $('#menu');
@ -116,7 +116,7 @@
* *
* @param url {String} The url to match * @param url {String} The url to match
*/ */
Navigation.prototype.setActiveByUrl = function(url) { Navigation.prototype.setActiveAndSelectedByUrl = function(url) {
var $menu = $('#menu'); var $menu = $('#menu');
if (! $menu.length) { if (! $menu.length) {
@ -124,17 +124,17 @@
} }
// try to active the first item that has an exact URL match // try to active the first item that has an exact URL match
this.setActive($menu.find('[href="' + url + '"]')); this.setActiveAndSelected($menu.find('[href="' + url + '"]'));
// the url may point to the search field, which must be activated too // the url may point to the search field, which must be activated too
if (! this.active) { if (! this.active) {
this.setActive($menu.find('form[action="' + this.icinga.utils.parseUrl(url).path + '"]')); this.setActiveAndSelected($menu.find('form[action="' + this.icinga.utils.parseUrl(url).path + '"]'));
} }
// some urls may have custom filters which won't match any menu item, in that case search // some urls may have custom filters which won't match any menu item, in that case search
// for a menu item that points to the base action without any filters // for a menu item that points to the base action without any filters
if (! this.active) { if (! this.active) {
this.setActive($menu.find('[href="' + this.icinga.utils.parseUrl(url).path + '"]').first()); this.setActiveAndSelected($menu.find('[href="' + this.icinga.utils.parseUrl(url).path + '"]').first());
} }
}; };
@ -143,11 +143,12 @@
* *
* @param url * @param url
*/ */
Navigation.prototype.trySetActiveByUrl = function(url) { Navigation.prototype.trySetActiveAndSelectedByUrl = function(url) {
var active = this.active; var active = this.active;
this.setActiveByUrl(url); this.setActiveAndSelectedByUrl(url);
if (! this.active && active) { if (! this.active && active) {
this.setActive($(this.icinga.utils.getElementByDomPath(active))); this.setActiveAndSelected($(this.icinga.utils.getElementByDomPath(active)));
} }
}; };
@ -160,6 +161,15 @@
} }
}; };
/**
* Remove all selected elements
*/
Navigation.prototype.clearSelected = function() {
if (this.$menu) {
this.$menu.find('.selected').removeClass('selected');
}
};
/** /**
* Select all menu items in the selector as active and unfold surrounding menus when necessary * Select all menu items in the selector as active and unfold surrounding menus when necessary
* *
@ -184,6 +194,11 @@
} }
}; };
Navigation.prototype.setActiveAndSelected = function ($el) {
this.setActive($el);
this.setSelected($el);
};
/** /**
* Change the active menu element * Change the active menu element
* *
@ -202,6 +217,15 @@
// TODO: push to history // TODO: push to history
}; };
Navigation.prototype.setSelected = function($el) {
this.clearSelected();
$el = $el.closest('li');
if ($el.length) {
$el.addClass('selected');
}
};
/** /**
* Reset the active element to nothing * Reset the active element to nothing
*/ */
@ -210,6 +234,14 @@
this.active = null; this.active = null;
}; };
/**
* Reset the selected element to nothing
*/
Navigation.prototype.resetSelected = function() {
this.clearSelected();
this.selected = null;
};
/** /**
* Show the fly-out menu * Show the fly-out menu
* *
@ -318,9 +350,10 @@
); );
return; return;
} }
this.setActive($(active)); this.setActiveAndSelected($(active))
} else { } else {
this.resetActive(); this.resetActive();
this.resetSelected();
} }
}; };

View File

@ -604,7 +604,7 @@
var url = req.url; var url = req.url;
if (req.$target[0].id === 'col1') { if (req.$target[0].id === 'col1') {
this.icinga.behaviors.navigation.trySetActiveByUrl(url); this.icinga.behaviors.navigation.trySetActiveAndSelectedByUrl(url);
} }
var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]');

View File

@ -186,7 +186,7 @@
var kill = this.cutContainer($('#col1')); var kill = this.cutContainer($('#col1'));
this.pasteContainer($('#col1'), col2); this.pasteContainer($('#col1'), col2);
this.fixControls(); this.fixControls();
this.icinga.behaviors.navigation.trySetActiveByUrl($('#col1').data('icingaUrl')); this.icinga.behaviors.navigation.trySetActiveAndSelectedByUrl($('#col1').data('icingaUrl'));
}, },
cutContainer: function ($col) { cutContainer: function ($col) {