Skip to content

Commit 348e110

Browse files
Add ability to include badges on nav items in pfVerticalNavigation
This also fixes an issue where the body content might not have been setup at startup. Rather than getting it once and holding a reference, get the element each time
1 parent 4900570 commit 348e110

File tree

4 files changed

+354
-76
lines changed

4 files changed

+354
-76
lines changed

misc/examples.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,10 @@ hr {
123123
.example-info-text:first-of-type {
124124
margin-top: 10px;
125125
}
126+
127+
.example-error-background {
128+
background-color: #cc0000 !important;
129+
}
130+
.example-warning-background {
131+
background-color: #ec7a08 !important;
132+
}

src/navigation/vertical-navigation-directive.js

Lines changed: 159 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*
1919
* @param {string} brandSrc src for brand image
2020
* @param {string} brandAlt Text for product name when brand image is not available
21-
* @param {boolean} hasSubMenus Flag if there are secondary and/or tertiary navigation items, default: true
21+
* @param {boolean} showBadges Flag if badges are used on navigation items, default: false
2222
* @param {boolean} persistentSecondary Flag to use persistent secondary menus, default: false
2323
* @param {boolean} hiddenIcons Flag to not show icons on the primary menu, default: false
2424
* @param {array} items List of navigation items
@@ -27,6 +27,13 @@
2727
* <li>.iconClass - (string) Classes for icon to be shown on the menu (ex. "fa fa-dashboard")
2828
* <li>.href - (string) href link to navigate to on click
2929
* <li>.children - (array) Submenu items (same structure as top level items)
30+
* <li>.badges - (array) Badges to display for the item, badges with a zero count are not displayed.
31+
* <ul style='list-style-type: none'>
32+
* <li>.count - (number) Count to display in the badge
33+
* <li>.iconClass - (string) Class to use for showing an icon before the count
34+
* <li>.tooltip - (string) Tooltip to display for the badge
35+
* <li>.badgeClass: - (string) Additional class(es) to add to the badge container
36+
* </ul>
3037
* </ul>
3138
* @param {function} navigateCallback function(item) Callback method invoked on a navigation item click (one with no submenus)
3239
* @param {function} itemClickCallback function(item) Callback method invoked on an item click
@@ -43,7 +50,7 @@
4350
</div>
4451
<div id="verticalNavLayout" class="layout-pf layout-pf-fixed faux-layout hidden" ng-controller="vertNavController">
4552
<div pf-vertical-navigation items="navigationItems" brand-alt="ANGULAR PATTERNFLY"
46-
has-sub-menus="true" pinnable-menus="true" update-active-items-on-click="true"
53+
show-badges="true" pinnable-menus="true" update-active-items-on-click="true"
4754
navigate-callback="handleNavigateClick">
4855
<div>
4956
<ul class="nav navbar-nav">
@@ -75,7 +82,7 @@
7582
</ul>
7683
</div>
7784
</div>
78-
<div id="contentContainer" class="container-fluid container-cards-pf container-pf-nav-pf-vertical container-pf-nav-pf-vertical-with-sub-menus example-page-container">
85+
<div id="contentContainer" class="container-fluid container-cards-pf container-pf-nav-pf-vertical example-page-container">
7986
<div id="includedContent"></div>
8087
</div>
8188
</div>
@@ -92,7 +99,13 @@
9299
{
93100
title: "Dolor",
94101
iconClass : "fa fa-shield",
95-
href: "#/dolor"
102+
href: "#/dolor",
103+
badges: [
104+
{
105+
count: 1283,
106+
tooltip: "Total number of items"
107+
}
108+
]
96109
},
97110
{
98111
title: "Ipsum",
@@ -103,15 +116,35 @@
103116
children: [
104117
{
105118
title: "Recteque",
106-
href: "#/ipsum/intellegam/recteque"
119+
href: "#/ipsum/intellegam/recteque",
120+
badges: [
121+
{
122+
count: 6,
123+
tooltip: "Total number of error items",
124+
badgeClass: 'example-error-background'
125+
}
126+
]
107127
},
108128
{
109129
title: "Suavitate",
110-
href: "#/ipsum/intellegam/suavitate"
130+
href: "#/ipsum/intellegam/suavitate",
131+
badges: [
132+
{
133+
count: 2,
134+
tooltip: "Total number of items"
135+
}
136+
]
111137
},
112138
{
113139
title: "Vituperatoribus",
114-
href: "#/ipsum/intellegam/vituperatoribus"
140+
href: "#/ipsum/intellegam/vituperatoribus",
141+
badges: [
142+
{
143+
count: 18,
144+
tooltip: "Total number of warning items",
145+
badgeClass: 'example-warning-background'
146+
}
147+
]
115148
}
116149
]
117150
},
@@ -120,15 +153,51 @@
120153
children: [
121154
{
122155
title: "Exerci",
123-
href: "#/ipsum/copiosae/exerci"
156+
href: "#/ipsum/copiosae/exerci",
157+
badges: [
158+
{
159+
count: 2,
160+
tooltip: "Total number of error items",
161+
iconClass: 'pficon pficon-error-circle-o'
162+
},
163+
{
164+
count: 6,
165+
tooltip: "Total number warning error items",
166+
iconClass: 'pficon pficon-warning-triangle-o'
167+
}
168+
]
124169
},
125170
{
126171
title: "Quaeque",
127-
href: "#/ipsum/copiosae/quaeque"
172+
href: "#/ipsum/copiosae/quaeque",
173+
badges: [
174+
{
175+
count: 0,
176+
tooltip: "Total number of error items",
177+
iconClass: 'pficon pficon-error-circle-o'
178+
},
179+
{
180+
count: 4,
181+
tooltip: "Total number warning error items",
182+
iconClass: 'pficon pficon-warning-triangle-o'
183+
}
184+
]
128185
},
129186
{
130187
title: "Utroque",
131-
href: "#/ipsum/copiosae/utroque"
188+
href: "#/ipsum/copiosae/utroque",
189+
badges: [
190+
{
191+
count: 1,
192+
tooltip: "Total number of error items",
193+
iconClass: 'pficon pficon-error-circle-o'
194+
},
195+
{
196+
count: 2,
197+
tooltip: "Total number warning error items",
198+
iconClass: 'pficon pficon-warning-triangle-o'
199+
}
200+
]
132201
}
133202
]
134203
},
@@ -151,7 +220,19 @@
151220
},
152221
{
153222
title: "Accumsan",
154-
href: "#/ipsum/Accumsan"
223+
href: "#/ipsum/Accumsan",
224+
badges: [
225+
{
226+
count: 2,
227+
tooltip: "Total number of error items",
228+
iconClass: 'pficon pficon-error-circle-o'
229+
},
230+
{
231+
count: 6,
232+
tooltip: "Total number warning error items",
233+
iconClass: 'pficon pficon-warning-triangle-o'
234+
}
235+
]
155236
}
156237
]
157238
},
@@ -492,7 +573,7 @@
492573
scope: {
493574
brandSrc: '@',
494575
brandAlt: '@',
495-
hasSubMenus: '@',
576+
showBadges: '@',
496577
persistentSecondary: '@',
497578
pinnableMenus: '@',
498579
hiddenIcons: '@',
@@ -508,12 +589,13 @@
508589
controller: function ($scope) {
509590
var routeChangeListener;
510591

511-
$scope.hasSubMenus = $scope.hasSubMenus !== 'false';
592+
$scope.showBadges = $scope.showBadges === 'true';
512593
$scope.persistentSecondary = $scope.persistentSecondary === 'true';
513594
$scope.pinnableMenus = $scope.pinnableMenus === 'true';
514595
$scope.hiddenIcons = $scope.hiddenIcons === 'true';
515596
$scope.updateActiveItemsOnClick = $scope.updateActiveItemsOnClick === 'true';
516597
$scope.ignoreMobile = $scope.ignoreMobile === 'true';
598+
$scope.activeSecondary = false;
517599

518600
$scope.clearActiveItems = function () {
519601
$scope.items.forEach(function (item) {
@@ -573,11 +655,27 @@
573655
'desktop': 1200
574656
};
575657

576-
var bodyContentElement = angular.element(document.querySelector('.container-pf-nav-pf-vertical'));
658+
var getBodyContentElement = function () {
659+
return angular.element(document.querySelector('.container-pf-nav-pf-vertical'));
660+
};
661+
577662
var explicitCollapse = false;
578663
var hoverDelay = 500;
579664
var hideDelay = hoverDelay + 200;
580665

666+
var initBodyElement = function () {
667+
var bodyContentElement = getBodyContentElement();
668+
if ($scope.showBadges) {
669+
bodyContentElement.addClass('nav-pf-vertical-with-badges');
670+
}
671+
if ($scope.persistentSecondary) {
672+
bodyContentElement.addClass('nav-pf-persistent-secondary');
673+
}
674+
if ($scope.hiddenIcons) {
675+
bodyContentElement.addClass('hidden-icons-pf');
676+
}
677+
};
678+
581679
var updateMobileMenu = function (selected, secondaryItem) {
582680
$scope.items.forEach(function (item) {
583681
item.isMobileItem = false;
@@ -606,6 +704,7 @@
606704

607705
var checkNavState = function () {
608706
var width = $window.innerWidth;
707+
var bodyContentElement = getBodyContentElement();
609708

610709
// Check to see if we need to enter/exit the mobile state
611710
if (!$scope.ignoreMobile && width < breakpoints.tablet) {
@@ -640,6 +739,7 @@
640739
};
641740

642741
var collapseMenu = function () {
742+
var bodyContentElement = getBodyContentElement();
643743
$scope.navCollapsed = true;
644744

645745
//Set the body class to the correct state
@@ -649,6 +749,7 @@
649749
};
650750

651751
var expandMenu = function () {
752+
var bodyContentElement = getBodyContentElement();
652753
$scope.navCollapsed = false;
653754

654755
//Set the body class to the correct state
@@ -668,17 +769,6 @@
668769
}, 500);
669770
};
670771

671-
var setFirstChildActive = function (item) {
672-
if (item && item.children && item.children.length > 0) {
673-
if ($scope.updateActiveItemsOnClick ) {
674-
item.children[0].isActive = true;
675-
}
676-
setFirstChildActive(item.children[0]);
677-
} else if (item && $scope.navigateCallback) {
678-
$scope.navigateCallback(item);
679-
}
680-
};
681-
682772
var setParentActive = function (item) {
683773
$scope.items.forEach(function (topLevel) {
684774
if (topLevel.children) {
@@ -699,18 +789,52 @@
699789
});
700790
};
701791

702-
var navigateToItem = function (item) {
703-
var navTo = item.href;
792+
var getFirstNavigateChild = function (item) {
793+
var firstChild;
704794
if (!item.children || item.children.length < 1) {
795+
firstChild = item;
796+
} else {
797+
firstChild = getFirstNavigateChild(item.children[0]);
798+
}
799+
return firstChild;
800+
};
801+
802+
var setSecondaryItemVisible = function () {
803+
var bodyContentElement = getBodyContentElement();
804+
$scope.activeSecondary = false;
805+
806+
if ($scope.persistentSecondary && !$scope.inMobileState) {
807+
$scope.items.forEach(function (topLevel) {
808+
if (topLevel.children) {
809+
topLevel.children.forEach(function (secondLevel) {
810+
if (secondLevel.isActive) {
811+
$scope.activeSecondary = true;
812+
}
813+
});
814+
}
815+
});
816+
if ($scope.activeSecondary) {
817+
bodyContentElement.addClass('secondary-visible-pf');
818+
} else {
819+
bodyContentElement.removeClass('secondary-visible-pf');
820+
}
821+
}
822+
};
823+
824+
var navigateToItem = function (item) {
825+
var navItem = getFirstNavigateChild(item);
826+
var navTo;
827+
if (navItem) {
705828
$scope.showMobileNav = false;
829+
navTo = navItem.href;
706830
if (navTo) {
707831
if (navTo.startsWith('#/')) {
708832
navTo = navTo.substring(2);
709833
}
710834
location.path(navTo);
711835
}
712836
if ($scope.navigateCallback) {
713-
$scope.navigateCallback(item);
837+
$scope.navigateCallback(navItem);
714838
}
715839
}
716840

@@ -720,11 +844,11 @@
720844

721845
if ($scope.updateActiveItemsOnClick ) {
722846
$scope.clearActiveItems();
723-
item.isActive = true;
724-
setParentActive(item);
847+
navItem.isActive = true;
848+
setParentActive(navItem);
849+
setSecondaryItemVisible();
725850
}
726-
727-
setFirstChildActive(item);
851+
setSecondaryItemVisible();
728852
};
729853

730854
var primaryHover = function () {
@@ -752,6 +876,7 @@
752876
};
753877

754878
var updateSecondaryCollapsedState = function (setCollapsed, collapsedItem) {
879+
var bodyContentElement = getBodyContentElement();
755880
if (collapsedItem) {
756881
collapsedItem.secondaryCollapsed = setCollapsed;
757882
}
@@ -773,6 +898,7 @@
773898
};
774899

775900
var updateTertiaryCollapsedState = function (setCollapsed, collapsedItem) {
901+
var bodyContentElement = getBodyContentElement();
776902
if (collapsedItem) {
777903
collapsedItem.tertiaryCollapsed = setCollapsed;
778904
}
@@ -808,16 +934,6 @@
808934
$scope.navCollapsed = false;
809935
$scope.forceHidden = false;
810936

811-
if ($scope.hasSubMenus) {
812-
bodyContentElement.addClass('container-pf-nav-pf-vertical-with-sub-menus');
813-
}
814-
if ($scope.persistentSecondary) {
815-
bodyContentElement.addClass('nav-pf-persistent-secondary');
816-
}
817-
if ($scope.hiddenIcons) {
818-
bodyContentElement.addClass('hidden-icons-pf');
819-
}
820-
821937
$scope.handleNavBarToggleClick = function () {
822938

823939
if ($scope.inMobileState) {
@@ -981,6 +1097,7 @@
9811097
event.stopImmediatePropagation();
9821098
};
9831099

1100+
initBodyElement();
9841101
checkNavState();
9851102

9861103
angular.element($window).bind('resize', function () {

0 commit comments

Comments
 (0)