JSFiddle - React, Tailwind, and code Playground
by ajai jothi
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div ng-app="menu" ng-controller="MenuCtrl">
<div dropdown-menu="menuConfig"></div>
<script type="text/ng-template" id="menu.html">
<div class="m-dropdown-menu" ng-init="menu=config.menu" ng-click="$event.stopPropagation();config.active=!config.active">
<a>{{config.displayText || 'Dropdown'}}
<b ng-if="filter.length">({{filter.length}})</b>
<i class="filter {{config.iconClass}}" ng-if="config.iconClass"></i>
</a>
<div class="m-dropdown" ng-class="{active: config.active}" ng-include="'dropdown.html'"></div>
</div>
</script>
<script type="text/ng-template" id="dropdown.html">
<ul>
<li ng-repeat="m in menu" ng-click="toggleMenu(m, $event)">
<a ng-class="{hasDropdown:m.menu.length, active:m.active}"><i class="fa fa-check" ng-if="m.active"></i> {{m.text}}</a>
<div class="m-dropdown" ng-class="{active:m.active}" ng-if="m.menu.length" ng-include="'dropdown.html'" ng-init="menu = m.menu"></div>
</li>
</ul>
</script>
</div>
SCSS
.m-dropdown-menu {
box-shadow: #999 0 0 5px;
border-radius:2px;
position: relative;
font-family: arial, sans-serif;
font-size: 12px;
display: inline-block;
border:1px solid #222;
background: #333;
padding: 5px 10px;
color: white;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
* {
box-sizing: border- box;
}
.filter {
color: #999;
margin-left: 10px;
}
&>.m-dropdown {
left: 0 !important;
top: 100% !important;
}
ul {
margin: 0;
padding: 0;
list-style: none;
background: white;
}
li {
position: relative;
min-width: 180px;
border-bottom: 1px solid #eee;
&:last-child{
border-bottom:0px;
}
a {
display: block;
padding: 5px;
color: #333;
&:hover {
background: #ddd;
}
&.active{
background: #ddd;
}
&.hasDropdown {
&:after {
content: '';
position: absolute;
right: 0px;
top: 30%;
width: 0px;
height: 0px;
border: 5px solid transparent;
border-left-color: #eee;
}
}
}
}
.m-dropdown {
box-shadow: #ccc 0 3px 5px;
position: absolute;
left: 70%;
top: -1px;
display: none;
border: 1px solid #ddd;
&.active {
display: block;
}
}
}
JavaScript
function DropdownMenu() {
return {
restrict: 'A',
templateUrl: 'menu.html',
scope: {
config: '=dropdownMenu'
},
link: function(scope, element) {
scope.filter = [];
angular.element(document).on('click', function(e) {
scope.config.active = false;
scope.$apply();
});
scope.toggleMenu = function(menu, evt, forceState) {
evt.stopPropagation();
var index = scope.filter.indexOf(menu);
if (index >= 0) {
scope.filter.splice(index, 1);
} else {
scope.filter.push(menu);
}
menu.active = angular.isDefined(forceState) ? forceState : !menu.active;
(function iterate(menu) {
if (!menu.active && menu.menu && menu.menu.length) {
menu.menu.forEach(function(m) {
m.active = false;
index = scope.filter.indexOf(m);
if (index >= 0) {
scope.filter.splice(index, 1);
}
iterate(m);
});
}
})(menu);
scope.config.onSelect(scope.filter, menu);
};
}
}
}
function MenuCtrl($scope) {
$scope.menu = [{
text: 'abcd'
}, {
text: 'abcd',
menu: [{
text: 'abcd'
}, {
text: 's-abcd',
menu: [{
text: 'abcd'
}, {
text: 'abcd'
}, {
text: 'abcd',
menu: [{
text: 's-abcd'
}]
}]
}]
}];
$scope.menuConfig = {
displayText: 'Category Filter',
iconClass: 'fa fa-filter',
menu: $scope.menu,
onSelect: function(filter, menu) {
console.log(filter, menu);
}
}
}
angular.module('menu', [])
.directive('dropdownMenu', DropdownMenu)
.controller('MenuCtrl', MenuCtrl);