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);