├── .gitignore ├── .jshintignore ├── Code ├── LICENSE ├── README.md ├── dist ├── ng-bootstrap-submenu.css ├── ng-bootstrap-submenu.css.map ├── ng-bootstrap-submenu.js ├── ng-bootstrap-submenu.min.css ├── ng-bootstrap-submenu.min.css.map └── ng-bootstrap-submenu.min.js ├── example.html ├── gulpfile.js ├── lib └── bootstrap-submenu-2.0.1-dist │ ├── css │ ├── bootstrap-submenu.css │ ├── bootstrap-submenu.css.map │ ├── bootstrap-submenu.min.css │ └── bootstrap-submenu.min.css.map │ └── js │ ├── bootstrap-submenu.js │ └── bootstrap-submenu.min.js ├── package.json └── src ├── bootstrapSubmenu.html ├── bootstrapSubmenu.js ├── bootstrapSubmenuController.js ├── bootstrapSubmenuDirective.js └── submenuTrigger.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | lib/ -------------------------------------------------------------------------------- /Code: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanlangton/ng-bootstrap-submenu/018446fd1d6ade5fb5eb6b2aea7043c52c4112df/Code -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryan Langton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ng-bootstrap-submenu 2 | 3 | Demo: plnkr 4 | 5 | * ng-bootstrap-submenu.min.css 6 | * ng-bootstrap-submenu.min.js 7 | 8 | Add the bootstrapSubmenu module dependency. 9 | 10 | angular.module('myApp', ['bootstrapSubmenu']); 11 | 12 | Use the directive (with ng-repeat if you have multiple dropdowns). Set the menu-item attribute to an object with the following properties: 13 | 14 | * href = the link for the item (only necessary for items with no children). 15 | * display = the text displayed for the item. 16 | * children = an array of sub-items (may be empty). 17 | 18 | Html: 19 | 20 |
21 | 40 |
41 | 42 | Javascript: 43 | 44 | angular.module('myApp', ['bootstrapSubmenu']); 45 | 46 | angular 47 | .module('myApp') 48 | .controller('menuController', menuController); 49 | 50 | function menuController() { 51 | var vm = this; 52 | vm.menuItems = [ 53 | { display: 'Dropdown Item 1', href: '#', children: [ 54 | { display: 'Child 1', href: '#', children: [ 55 | { display: 'Sub 1', href: '#sub1', children: []}, 56 | { display: 'Sub 2', href: '#sub2', children: []}]}, 57 | { display: 'Child 2', href: '#child2', children: []}]}, 58 | { display: 'Dropdown Item 2', href: '#dropdown2', children: []}, 59 | { display: 'Dropdown Item 3', href: '#', children: [ 60 | { display: 'Child 3', href: '#child3', children: []}]} 61 | ]; 62 | }); -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | .dropdown-submenu > a:after { 8 | content: ""; 9 | } 10 | @media (min-width: 768px) { 11 | .dropdown-submenu { 12 | position: relative; 13 | } 14 | .dropdown-submenu .dropdown-menu { 15 | top: 0; 16 | left: 100%; 17 | margin-top: -6px; 18 | border-top-left-radius: 0; 19 | } 20 | .dropup .dropdown-submenu .dropdown-menu, 21 | .navbar-fixed-bottom .dropdown-submenu .dropdown-menu { 22 | top: auto; 23 | bottom: 0; 24 | margin-top: 0; 25 | margin-bottom: -6px; 26 | border-top-left-radius: 4px; 27 | border-bottom-left-radius: 0; 28 | } 29 | .dropdown-menu-right .dropdown-submenu .dropdown-menu { 30 | left: auto; 31 | right: 100%; 32 | border-top-left-radius: 4px; 33 | border-top-right-radius: 0; 34 | } 35 | .dropup .dropdown-menu-right .dropdown-submenu .dropdown-menu, 36 | .navbar-fixed-bottom .dropdown-menu-right .dropdown-submenu .dropdown-menu { 37 | border-radius: 4px 4px 0; 38 | } 39 | .dropdown-submenu > a:after { 40 | float: right; 41 | margin-top: 6px; 42 | margin-right: -10px; 43 | border-left: 4px dashed; 44 | border-top: 4px solid transparent; 45 | border-bottom: 4px solid transparent; 46 | } 47 | .dropdown-menu-right .dropdown-submenu > a:after { 48 | float: left; 49 | border-left: none; 50 | margin-left: -10px; 51 | margin-right: 0; 52 | border-right: 4px dashed; 53 | border-top: 4px solid transparent; 54 | border-bottom: 4px solid transparent; 55 | } 56 | } 57 | @media (max-width: 767px) { 58 | .dropdown-submenu .dropdown-menu { 59 | position: static; 60 | margin-top: 0; 61 | border: 0; 62 | box-shadow: none; 63 | } 64 | .dropdown-submenu > a:after { 65 | margin-left: 6px; 66 | display: inline-block; 67 | vertical-align: middle; 68 | border-top: 4px dashed; 69 | border-left: 4px solid transparent; 70 | border-right: 4px solid transparent; 71 | } 72 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 73 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 74 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 75 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a, 76 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a, 77 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a { 78 | padding-left: 30px; 79 | } 80 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 81 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 82 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 83 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a, 84 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a, 85 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a { 86 | padding-left: 40px; 87 | } 88 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 89 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 90 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 91 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 92 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 93 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 94 | padding-left: 50px; 95 | } 96 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 97 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 98 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 99 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 100 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 101 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 102 | padding-left: 60px; 103 | } 104 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 105 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a { 106 | padding-left: 35px; 107 | } 108 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 109 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a { 110 | padding-left: 45px; 111 | } 112 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 113 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 114 | padding-left: 55px; 115 | } 116 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 117 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 118 | padding-left: 65px; 119 | } 120 | } 121 | /*# sourceMappingURL=bootstrap-submenu.css.map */ -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/bootstrap-submenu.less","less/mixins.less"],"names":[],"mappings":"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","sourcesContent":["// :after: friends with IE8. Use ::after in future.\n\n@import \"../node_modules/bootstrap/less/variables.less\";\n@import \"mixins.less\";\n\n// Variables\n@caret-margin: -@caret-width-base * 2 - 2;\n\n//\n// Sub-Menus\n// --------------------------------------------------\n\n.dropdown-submenu > a:after {\n content: \"\";\n}\n\n@media (min-width: @grid-float-breakpoint) {\n .dropdown-submenu {\n position: relative;\n\n .dropdown-menu {\n top: 0;\n left: 100%;\n margin-top: -6px;\n border-top-left-radius: 0;\n\n // Strictly before .dropdown-menu-right\n .dropup &,\n .navbar-fixed-bottom & {\n top: auto;\n bottom: 0;\n margin-top: 0;\n margin-bottom: -6px;\n border-top-left-radius: @border-radius-base;\n border-bottom-left-radius: 0;\n }\n\n .dropdown-menu-right & {\n left: auto;\n right: 100%;\n\n border-top-left-radius: @border-radius-base;\n border-top-right-radius: 0;\n\n .dropup &,\n .navbar-fixed-bottom & {\n border-radius: @border-radius-base @border-radius-base 0;\n }\n }\n }\n\n > a:after {\n float: right;\n margin-top: @line-height-computed / 2 - @caret-width-base;\n margin-right: @caret-margin;\n\n .make-caret(left, top, bottom);\n\n .dropdown-menu-right & {\n float: left;\n border-left: none;\n margin-left: @caret-margin;\n margin-right: 0;\n\n .make-caret(right, top, bottom);\n }\n }\n }\n}\n\n@media (max-width: @grid-float-breakpoint-max) {\n .dropdown-submenu {\n .dropdown-menu {\n position: static;\n margin-top: 0;\n border: 0;\n box-shadow: none;\n }\n\n > a:after {\n margin-left: 6px;\n display: inline-block;\n vertical-align: middle;\n\n .make-caret(top, left, right);\n }\n }\n\n .dropdown-menu > .dropdown-submenu {\n .dropdown > &,\n .dropup > &,\n .btn-group > & {\n .make-nested-list(30px, 0, 4);\n }\n\n .navbar-nav > .dropdown > & {\n .make-nested-list(35px, 0, 4);\n }\n }\n}\n",".make-caret(@base, @left, @right) {\n // dashed: fix caret size for Mozilla Firefox\n border-@{base}: @caret-width-base dashed;\n\n border-@{left}: @caret-width-base solid transparent;\n border-@{right}: @caret-width-base solid transparent;\n}\n\n.make-nested-list(@offset, @i, @n) when (@i < @n) {\n > .dropdown-menu > li {\n &.dropdown-header,\n > a {\n padding-left: @offset + (10 * @i);\n }\n\n .make-nested-list(@offset, @i + 1, @n);\n }\n}\n"]} -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | angular.module('bootstrapSubmenu', []); 3 | })(); 4 | (function(){ 5 | bootstrapSubmenuController.$inject = ["$scope", "submenuTrigger"]; 6 | angular 7 | .module('bootstrapSubmenu') 8 | .controller('bootstrapSubmenuController', bootstrapSubmenuController); 9 | 10 | function bootstrapSubmenuController($scope, submenuTrigger){ 11 | submenuTrigger.trigger(); 12 | 13 | $scope.getDropdownClass = function(){ 14 | if (!$scope.hasChildren()) return ''; 15 | return $scope.isSubMenu ? 'dropdown-submenu': 'dropdown'; 16 | }; 17 | 18 | $scope.showCaret = function(){ 19 | return (!$scope.isSubMenu && $scope.hasChildren()); 20 | }; 21 | 22 | $scope.hasChildren = function(){ 23 | return ($scope.menuItem.children !== undefined && $scope.menuItem.children.length > 0); 24 | }; 25 | } 26 | })(); 27 | (function(){ 28 | bootstrapSubmenu.$inject = ["$compile"]; 29 | angular 30 | .module('bootstrapSubmenu') 31 | .directive('bootstrapSubmenu', bootstrapSubmenu); 32 | 33 | function bootstrapSubmenu($compile) { 34 | return { 35 | restrict: 'E', 36 | scope: { 37 | menuItem: '=menuItem', 38 | isSubMenu: '@isSubMenu' 39 | }, 40 | replace: true, 41 | templateUrl: 'bootstrapSubmenu.html', 42 | controller: 'bootstrapSubmenuController', 43 | compile: function (el) { 44 | var contents = el.contents().remove(); 45 | var compiled; 46 | return function(scope,el){ 47 | if(!compiled) 48 | compiled = $compile(contents); 49 | 50 | compiled(scope,function(clone){ 51 | el.append(clone); 52 | }); 53 | }; 54 | } 55 | }; 56 | } 57 | })(); 58 | (function(){ 59 | submenuTrigger.$inject = ["$timeout"]; 60 | angular 61 | .module('bootstrapSubmenu') 62 | .factory('submenuTrigger', submenuTrigger); 63 | 64 | function submenuTrigger($timeout){ 65 | var triggered = false; 66 | 67 | return { 68 | trigger: function(){ 69 | if (triggered) return; 70 | 71 | // after angularjs digest, trigger submenupicker 72 | $timeout(function(){ 73 | $('[data-submenu]').submenupicker(); 74 | }, 100); 75 | } 76 | }; 77 | } 78 | })(); 79 | angular.module("bootstrapSubmenu").run(["$templateCache", function($templateCache) {$templateCache.put("bootstrapSubmenu.html","
  • \r\n \r\n \r\n \r\n \r\n \r\n \r\n
  • \r\n");}]); 80 | /*! 81 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 82 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 83 | * Licensed under the MIT license 84 | */ 85 | 86 | /** 87 | * $.inArray: friends with IE8. Use Array.prototype.indexOf in future. 88 | * $.proxy: friends with IE8. Use Function.prototype.bind in future. 89 | */ 90 | 91 | 'use strict'; 92 | 93 | (function(factory) { 94 | if (typeof define == 'function' && define.amd) { 95 | // AMD. Register as an anonymous module 96 | define(['jquery'], factory); 97 | } 98 | else if (typeof exports == 'object') { 99 | // Node/CommonJS 100 | module.exports = factory(require('jquery')); 101 | } 102 | else { 103 | // Browser globals 104 | factory(jQuery); 105 | } 106 | })(function($) { 107 | function Item(element) { 108 | this.$element = $(element); 109 | this.$menu = this.$element.closest('.dropdown-menu'); 110 | this.$main = this.$menu.parent(); 111 | this.$items = this.$menu.children('.dropdown-submenu'); 112 | 113 | this.init(); 114 | } 115 | 116 | Item.prototype = { 117 | init: function() { 118 | this.$element.on('keydown', $.proxy(this, 'keydown')); 119 | }, 120 | close: function() { 121 | this.$main.removeClass('open'); 122 | this.$items.trigger('hide.bs.submenu'); 123 | }, 124 | keydown: function(event) { 125 | // 27: Esc 126 | 127 | if (event.keyCode == 27) { 128 | event.stopPropagation(); 129 | 130 | this.close(); 131 | this.$main.children('a, button').trigger('focus'); 132 | } 133 | } 134 | }; 135 | 136 | function SubmenuItem(element) { 137 | this.$element = $(element); 138 | this.$main = this.$element.parent(); 139 | this.$menu = this.$main.children('.dropdown-menu'); 140 | this.$subs = this.$main.siblings('.dropdown-submenu'); 141 | this.$items = this.$menu.children('.dropdown-submenu'); 142 | 143 | this.init(); 144 | } 145 | 146 | $.extend(SubmenuItem.prototype, Item.prototype, { 147 | init: function() { 148 | this.$element.on({ 149 | click: $.proxy(this, 'click'), 150 | keydown: $.proxy(this, 'keydown') 151 | }); 152 | 153 | this.$main.on('hide.bs.submenu', $.proxy(this, 'hide')); 154 | }, 155 | click: function(event) { 156 | event.stopPropagation(); 157 | 158 | this.toggle(); 159 | }, 160 | hide: function(event) { 161 | // Stop event bubbling 162 | event.stopPropagation(); 163 | 164 | this.close(); 165 | }, 166 | open: function() { 167 | this.$main.addClass('open'); 168 | this.$subs.trigger('hide.bs.submenu'); 169 | }, 170 | toggle: function() { 171 | if (this.$main.hasClass('open')) { 172 | this.close(); 173 | } 174 | else { 175 | this.open(); 176 | } 177 | }, 178 | keydown: function(event) { 179 | // 13: Return, 32: Spacebar 180 | 181 | if (event.keyCode == 32) { 182 | // Off vertical scrolling 183 | event.preventDefault(); 184 | } 185 | 186 | if ($.inArray(event.keyCode, [13, 32]) != -1) { 187 | this.toggle(); 188 | } 189 | } 190 | }); 191 | 192 | function Submenupicker(element) { 193 | this.$element = $(element); 194 | this.$main = this.$element.parent(); 195 | this.$menu = this.$main.children('.dropdown-menu'); 196 | this.$items = this.$menu.children('.dropdown-submenu'); 197 | 198 | this.init(); 199 | } 200 | 201 | Submenupicker.prototype = { 202 | init: function() { 203 | this.$menu.off('keydown.bs.dropdown.data-api'); 204 | this.$menu.on('keydown', $.proxy(this, 'itemKeydown')); 205 | 206 | this.$menu.find('li > a').each(function() { 207 | new Item(this); 208 | }); 209 | 210 | this.$menu.find('.dropdown-submenu > a').each(function() { 211 | new SubmenuItem(this); 212 | }); 213 | 214 | this.$main.on('hidden.bs.dropdown', $.proxy(this, 'hidden')); 215 | }, 216 | hidden: function() { 217 | this.$items.trigger('hide.bs.submenu'); 218 | }, 219 | itemKeydown: function(event) { 220 | // 38: Arrow up, 40: Arrow down 221 | 222 | if ($.inArray(event.keyCode, [38, 40]) != -1) { 223 | // Off vertical scrolling 224 | event.preventDefault(); 225 | 226 | event.stopPropagation(); 227 | 228 | var $items = this.$menu.find('li:not(.disabled):visible > a'); 229 | var index = $items.index(event.target); 230 | 231 | if (event.keyCode == 38 && index !== 0) { 232 | index--; 233 | } 234 | else if (event.keyCode == 40 && index !== $items.length - 1) { 235 | index++; 236 | } 237 | else { 238 | return; 239 | } 240 | 241 | $items.eq(index).trigger('focus'); 242 | } 243 | } 244 | }; 245 | 246 | // For AMD/Node/CommonJS used elements (optional) 247 | // http://learn.jquery.com/jquery-ui/environments/amd/ 248 | return $.fn.submenupicker = function(elements) { 249 | var $elements = this instanceof $ ? this : $(elements); 250 | 251 | return $elements.each(function() { 252 | var data = $.data(this, 'bs.submenu'); 253 | 254 | if (!data) { 255 | data = new Submenupicker(this); 256 | 257 | $.data(this, 'bs.submenu', data); 258 | } 259 | }); 260 | }; 261 | }); 262 | -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | .dropdown-submenu>a:after{content:""}@media (min-width:768px){.dropdown-submenu{position:relative}.dropdown-submenu .dropdown-menu{top:0;left:100%;margin-top:-6px;border-top-left-radius:0}.dropup .dropdown-submenu .dropdown-menu,.navbar-fixed-bottom .dropdown-submenu .dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-6px;border-top-left-radius:4px;border-bottom-left-radius:0}.dropdown-menu-right .dropdown-submenu .dropdown-menu{left:auto;right:100%;border-top-left-radius:4px;border-top-right-radius:0}.dropup .dropdown-menu-right .dropdown-submenu .dropdown-menu,.navbar-fixed-bottom .dropdown-menu-right .dropdown-submenu .dropdown-menu{border-radius:4px 4px 0}.dropdown-submenu>a:after{float:right;margin-top:6px;margin-right:-10px;border-left:4px dashed;border-top:4px solid transparent;border-bottom:4px solid transparent}.dropdown-menu-right .dropdown-submenu>a:after{float:left;border-left:none;margin-left:-10px;margin-right:0;border-right:4px dashed;border-top:4px solid transparent;border-bottom:4px solid transparent}}@media (max-width:767px){.dropdown-submenu .dropdown-menu{position:static;margin-top:0;border:0;box-shadow:none}.dropdown-submenu>a:after{margin-left:6px;display:inline-block;vertical-align:middle;border-top:4px dashed;border-left:4px solid transparent;border-right:4px solid transparent}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a{padding-left:30px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:40px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:50px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:60px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a{padding-left:35px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:45px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:55px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:65px}} 8 | /*# sourceMappingURL=bootstrap-submenu.min.css.map */ -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/bootstrap-submenu.less","less/mixins.less"],"names":[],"mappings":"AAYqB,0BACnB,QAAA,GAGyC,yBACzC,kBACE,SAAA,SAEA,iCACE,IAAA,EACA,KAAA,KACA,WAAA,KACA,uBAAA,EAJF,yCAAA,sDASI,IAAA,KACA,OAAA,EACA,WAAA,EACA,cAAA,KACA,uBAAA,IACA,0BAAA,EAdJ,sDAkBI,KAAA,KACA,MAAA,KAEA,uBAAA,IACA,wBAAA,EAtBJ,8DAAA,2EA0BM,cAAA,IAAA,IAAA,EAKH,0BACD,MAAA,MACA,WAAA,IACA,aAAA,MCpDJ,YAAA,IAAA,OAEA,WAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,YD8CK,+CAQC,MAAA,KACA,YAAA,KACA,YAAA,MACA,aAAA,EC5DN,aAAA,IAAA,OAEA,WAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,aDiE6C,yBAE3C,iCACE,SAAA,OACA,WAAA,EACA,OAAA,EACA,WAAA,KAGC,0BACD,YAAA,IACA,QAAA,aACA,eAAA,OChFJ,WAAA,IAAA,OAEA,YAAA,IAAA,MAAA,YACA,aAAA,IAAA,MAAA,YAKG,8EACD,gEADC,6EACD,+DADC,2EACD,6DACE,aAAA,KAFD,gGACD,kFADC,+FACD,iFADC,6FACD,+EACE,aAAA,KAFD,kHACD,oGADC,iHACD,mGADC,+GACD,iGACE,aAAA,KAFD,oIACD,sHADC,mIACD,qHADC,iIACD,mHACE,aAAA,KAFD,yFACD,2EACE,aAAA,KAFD,2GACD,6FACE,aAAA,KAFD,6HACD,+GACE,aAAA,KAFD,+IACD,iIACE,aAAA"} -------------------------------------------------------------------------------- /dist/ng-bootstrap-submenu.min.js: -------------------------------------------------------------------------------- 1 | !function(){angular.module("bootstrapSubmenu",[])}(),function(){function n(n,e){e.trigger(),n.getDropdownClass=function(){return n.hasChildren()?n.isSubMenu?"dropdown-submenu":"dropdown":""},n.showCaret=function(){return!n.isSubMenu&&n.hasChildren()},n.hasChildren=function(){return void 0!==n.menuItem.children&&n.menuItem.children.length>0}}n.$inject=["$scope","submenuTrigger"],angular.module("bootstrapSubmenu").controller("bootstrapSubmenuController",n)}(),function(){function n(n){return{restrict:"E",scope:{menuItem:"=menuItem",isSubMenu:"@isSubMenu"},replace:!0,templateUrl:"bootstrapSubmenu.html",controller:"bootstrapSubmenuController",compile:function(e){var t,i=e.contents().remove();return function(e,o){t||(t=n(i)),t(e,function(n){o.append(n)})}}}}n.$inject=["$compile"],angular.module("bootstrapSubmenu").directive("bootstrapSubmenu",n)}(),function(){function n(n){var e=!1;return{trigger:function(){e||n(function(){$("[data-submenu]").submenupicker()},100)}}}n.$inject=["$timeout"],angular.module("bootstrapSubmenu").factory("submenuTrigger",n)}(),angular.module("bootstrapSubmenu").run(["$templateCache",function(n){n.put("bootstrapSubmenu.html",'
  • \r\n \r\n \r\n \r\n \r\n \r\n \r\n
  • \r\n')}]),function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof exports?module.exports=n(require("jquery")):n(jQuery)}(function(n){function e(e){this.$element=n(e),this.$menu=this.$element.closest(".dropdown-menu"),this.$main=this.$menu.parent(),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}function t(e){this.$element=n(e),this.$main=this.$element.parent(),this.$menu=this.$main.children(".dropdown-menu"),this.$subs=this.$main.siblings(".dropdown-submenu"),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}function i(e){this.$element=n(e),this.$main=this.$element.parent(),this.$menu=this.$main.children(".dropdown-menu"),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}return e.prototype={init:function(){this.$element.on("keydown",n.proxy(this,"keydown"))},close:function(){this.$main.removeClass("open"),this.$items.trigger("hide.bs.submenu")},keydown:function(n){27==n.keyCode&&(n.stopPropagation(),this.close(),this.$main.children("a, button").trigger("focus"))}},n.extend(t.prototype,e.prototype,{init:function(){this.$element.on({click:n.proxy(this,"click"),keydown:n.proxy(this,"keydown")}),this.$main.on("hide.bs.submenu",n.proxy(this,"hide"))},click:function(n){n.stopPropagation(),this.toggle()},hide:function(n){n.stopPropagation(),this.close()},open:function(){this.$main.addClass("open"),this.$subs.trigger("hide.bs.submenu")},toggle:function(){this.$main.hasClass("open")?this.close():this.open()},keydown:function(e){32==e.keyCode&&e.preventDefault(),n.inArray(e.keyCode,[13,32])!=-1&&this.toggle()}}),i.prototype={init:function(){this.$menu.off("keydown.bs.dropdown.data-api"),this.$menu.on("keydown",n.proxy(this,"itemKeydown")),this.$menu.find("li > a").each(function(){new e(this)}),this.$menu.find(".dropdown-submenu > a").each(function(){new t(this)}),this.$main.on("hidden.bs.dropdown",n.proxy(this,"hidden"))},hidden:function(){this.$items.trigger("hide.bs.submenu")},itemKeydown:function(e){if(n.inArray(e.keyCode,[38,40])!=-1){e.preventDefault(),e.stopPropagation();var t=this.$menu.find("li:not(.disabled):visible > a"),i=t.index(e.target);if(38==e.keyCode&&0!==i)i--;else{if(40!=e.keyCode||i===t.length-1)return;i++}t.eq(i).trigger("focus")}}},n.fn.submenupicker=function(e){var t=this instanceof n?this:n(e);return t.each(function(){var e=n.data(this,"bs.submenu");e||(e=new i(this),n.data(this,"bs.submenu",e))})}}); -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 34 |
    35 | 60 | 61 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var gulp = require('gulp'); 4 | var templateCache = require('gulp-angular-templatecache'); 5 | var concat = require('gulp-concat'); 6 | var jshint = require('gulp-jshint'); 7 | var ngAnnotate = require('gulp-ng-annotate'); 8 | var rename = require("gulp-rename"); 9 | var rimraf = require('gulp-rimraf'); 10 | var uglify = require('gulp-uglify'); 11 | var gutil = require('gulp-util'); 12 | 13 | var config = { 14 | paths: { 15 | bootstrapSubmenuCss: './lib/bootstrap-submenu-2.0.1-dist/css/*.*', 16 | bootstrapSubmenuJs: './lib/bootstrap-submenu-2.0.1-dist/js/bootstrap-submenu.js', 17 | html: './src/*.html', 18 | js: './src/*.js', 19 | dist: './dist', 20 | temp: './temp' 21 | } 22 | } 23 | 24 | gulp.task('clean', function(){ 25 | return gulp.src([config.paths.temp, config.paths.dist], {read: false}) 26 | .pipe(rimraf()); 27 | }); 28 | 29 | gulp.task('css', ['clean'], function() { 30 | return gulp.src(config.paths.bootstrapSubmenuCss) 31 | .pipe(rename({ 32 | prefix: "ng-" 33 | })) 34 | .pipe(gulp.dest(config.paths.dist)); 35 | }); 36 | 37 | gulp.task('html', ['clean'], function() { 38 | return gulp.src(config.paths.html) 39 | .pipe(templateCache({ 40 | module: 'bootstrapSubmenu' 41 | })) 42 | .pipe(gulp.dest(config.paths.temp)); 43 | }); 44 | 45 | gulp.task('minjs', ['html', 'clean'], function() { 46 | return gulp.src([config.paths.js, config.paths.temp + '/*.js', config.paths.bootstrapSubmenuJs]) 47 | .pipe(jshint()) 48 | .pipe(jshint.reporter('default')) 49 | .pipe(ngAnnotate()) 50 | .pipe(concat('ng-bootstrap-submenu.min.js')) 51 | .pipe(uglify().on('error', gutil.log)) 52 | .pipe(gulp.dest(config.paths.dist)); 53 | }); 54 | 55 | gulp.task('js', ['html', 'clean'], function() { 56 | return gulp.src([config.paths.js, config.paths.temp + '/*.js', config.paths.bootstrapSubmenuJs]) 57 | .pipe(jshint()) 58 | .pipe(jshint.reporter('default')) 59 | .pipe(ngAnnotate()) 60 | .pipe(concat('ng-bootstrap-submenu.js')) 61 | .pipe(gulp.dest(config.paths.dist)); 62 | }); 63 | 64 | gulp.task('default', ['html', 'js', 'minjs', 'css', 'clean']); -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/css/bootstrap-submenu.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | .dropdown-submenu > a:after { 8 | content: ""; 9 | } 10 | @media (min-width: 768px) { 11 | .dropdown-submenu { 12 | position: relative; 13 | } 14 | .dropdown-submenu .dropdown-menu { 15 | top: 0; 16 | left: 100%; 17 | margin-top: -6px; 18 | border-top-left-radius: 0; 19 | } 20 | .dropup .dropdown-submenu .dropdown-menu, 21 | .navbar-fixed-bottom .dropdown-submenu .dropdown-menu { 22 | top: auto; 23 | bottom: 0; 24 | margin-top: 0; 25 | margin-bottom: -6px; 26 | border-top-left-radius: 4px; 27 | border-bottom-left-radius: 0; 28 | } 29 | .dropdown-menu-right .dropdown-submenu .dropdown-menu { 30 | left: auto; 31 | right: 100%; 32 | border-top-left-radius: 4px; 33 | border-top-right-radius: 0; 34 | } 35 | .dropup .dropdown-menu-right .dropdown-submenu .dropdown-menu, 36 | .navbar-fixed-bottom .dropdown-menu-right .dropdown-submenu .dropdown-menu { 37 | border-radius: 4px 4px 0; 38 | } 39 | .dropdown-submenu > a:after { 40 | float: right; 41 | margin-top: 6px; 42 | margin-right: -10px; 43 | border-left: 4px dashed; 44 | border-top: 4px solid transparent; 45 | border-bottom: 4px solid transparent; 46 | } 47 | .dropdown-menu-right .dropdown-submenu > a:after { 48 | float: left; 49 | border-left: none; 50 | margin-left: -10px; 51 | margin-right: 0; 52 | border-right: 4px dashed; 53 | border-top: 4px solid transparent; 54 | border-bottom: 4px solid transparent; 55 | } 56 | } 57 | @media (max-width: 767px) { 58 | .dropdown-submenu .dropdown-menu { 59 | position: static; 60 | margin-top: 0; 61 | border: 0; 62 | box-shadow: none; 63 | } 64 | .dropdown-submenu > a:after { 65 | margin-left: 6px; 66 | display: inline-block; 67 | vertical-align: middle; 68 | border-top: 4px dashed; 69 | border-left: 4px solid transparent; 70 | border-right: 4px solid transparent; 71 | } 72 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 73 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 74 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 75 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a, 76 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a, 77 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a { 78 | padding-left: 30px; 79 | } 80 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 81 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 82 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 83 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a, 84 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a, 85 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a { 86 | padding-left: 40px; 87 | } 88 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 89 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 90 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 91 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 92 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 93 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 94 | padding-left: 50px; 95 | } 96 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 97 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 98 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 99 | .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 100 | .dropup > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a, 101 | .btn-group > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 102 | padding-left: 60px; 103 | } 104 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li.dropdown-header, 105 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > a { 106 | padding-left: 35px; 107 | } 108 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 109 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > a { 110 | padding-left: 45px; 111 | } 112 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 113 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 114 | padding-left: 55px; 115 | } 116 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li.dropdown-header, 117 | .navbar-nav > .dropdown > .dropdown-menu > .dropdown-submenu > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > .dropdown-menu > li > a { 118 | padding-left: 65px; 119 | } 120 | } 121 | /*# sourceMappingURL=bootstrap-submenu.css.map */ -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/css/bootstrap-submenu.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/bootstrap-submenu.less","less/mixins.less"],"names":[],"mappings":"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","sourcesContent":["// :after: friends with IE8. Use ::after in future.\n\n@import \"../node_modules/bootstrap/less/variables.less\";\n@import \"mixins.less\";\n\n// Variables\n@caret-margin: -@caret-width-base * 2 - 2;\n\n//\n// Sub-Menus\n// --------------------------------------------------\n\n.dropdown-submenu > a:after {\n content: \"\";\n}\n\n@media (min-width: @grid-float-breakpoint) {\n .dropdown-submenu {\n position: relative;\n\n .dropdown-menu {\n top: 0;\n left: 100%;\n margin-top: -6px;\n border-top-left-radius: 0;\n\n // Strictly before .dropdown-menu-right\n .dropup &,\n .navbar-fixed-bottom & {\n top: auto;\n bottom: 0;\n margin-top: 0;\n margin-bottom: -6px;\n border-top-left-radius: @border-radius-base;\n border-bottom-left-radius: 0;\n }\n\n .dropdown-menu-right & {\n left: auto;\n right: 100%;\n\n border-top-left-radius: @border-radius-base;\n border-top-right-radius: 0;\n\n .dropup &,\n .navbar-fixed-bottom & {\n border-radius: @border-radius-base @border-radius-base 0;\n }\n }\n }\n\n > a:after {\n float: right;\n margin-top: @line-height-computed / 2 - @caret-width-base;\n margin-right: @caret-margin;\n\n .make-caret(left, top, bottom);\n\n .dropdown-menu-right & {\n float: left;\n border-left: none;\n margin-left: @caret-margin;\n margin-right: 0;\n\n .make-caret(right, top, bottom);\n }\n }\n }\n}\n\n@media (max-width: @grid-float-breakpoint-max) {\n .dropdown-submenu {\n .dropdown-menu {\n position: static;\n margin-top: 0;\n border: 0;\n box-shadow: none;\n }\n\n > a:after {\n margin-left: 6px;\n display: inline-block;\n vertical-align: middle;\n\n .make-caret(top, left, right);\n }\n }\n\n .dropdown-menu > .dropdown-submenu {\n .dropdown > &,\n .dropup > &,\n .btn-group > & {\n .make-nested-list(30px, 0, 4);\n }\n\n .navbar-nav > .dropdown > & {\n .make-nested-list(35px, 0, 4);\n }\n }\n}\n",".make-caret(@base, @left, @right) {\n // dashed: fix caret size for Mozilla Firefox\n border-@{base}: @caret-width-base dashed;\n\n border-@{left}: @caret-width-base solid transparent;\n border-@{right}: @caret-width-base solid transparent;\n}\n\n.make-nested-list(@offset, @i, @n) when (@i < @n) {\n > .dropdown-menu > li {\n &.dropdown-header,\n > a {\n padding-left: @offset + (10 * @i);\n }\n\n .make-nested-list(@offset, @i + 1, @n);\n }\n}\n"]} -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/css/bootstrap-submenu.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | .dropdown-submenu>a:after{content:""}@media (min-width:768px){.dropdown-submenu{position:relative}.dropdown-submenu .dropdown-menu{top:0;left:100%;margin-top:-6px;border-top-left-radius:0}.dropup .dropdown-submenu .dropdown-menu,.navbar-fixed-bottom .dropdown-submenu .dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-6px;border-top-left-radius:4px;border-bottom-left-radius:0}.dropdown-menu-right .dropdown-submenu .dropdown-menu{left:auto;right:100%;border-top-left-radius:4px;border-top-right-radius:0}.dropup .dropdown-menu-right .dropdown-submenu .dropdown-menu,.navbar-fixed-bottom .dropdown-menu-right .dropdown-submenu .dropdown-menu{border-radius:4px 4px 0}.dropdown-submenu>a:after{float:right;margin-top:6px;margin-right:-10px;border-left:4px dashed;border-top:4px solid transparent;border-bottom:4px solid transparent}.dropdown-menu-right .dropdown-submenu>a:after{float:left;border-left:none;margin-left:-10px;margin-right:0;border-right:4px dashed;border-top:4px solid transparent;border-bottom:4px solid transparent}}@media (max-width:767px){.dropdown-submenu .dropdown-menu{position:static;margin-top:0;border:0;box-shadow:none}.dropdown-submenu>a:after{margin-left:6px;display:inline-block;vertical-align:middle;border-top:4px dashed;border-left:4px solid transparent;border-right:4px solid transparent}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a{padding-left:30px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:40px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:50px}.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.btn-group>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.dropup>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:60px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>a{padding-left:35px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:45px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:55px}.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li.dropdown-header,.navbar-nav>.dropdown>.dropdown-menu>.dropdown-submenu>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>.dropdown-menu>li>a{padding-left:65px}} 8 | /*# sourceMappingURL=bootstrap-submenu.min.css.map */ -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/css/bootstrap-submenu.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/bootstrap-submenu.less","less/mixins.less"],"names":[],"mappings":"AAYqB,0BACnB,QAAA,GAGyC,yBACzC,kBACE,SAAA,SAEA,iCACE,IAAA,EACA,KAAA,KACA,WAAA,KACA,uBAAA,EAJF,yCAAA,sDASI,IAAA,KACA,OAAA,EACA,WAAA,EACA,cAAA,KACA,uBAAA,IACA,0BAAA,EAdJ,sDAkBI,KAAA,KACA,MAAA,KAEA,uBAAA,IACA,wBAAA,EAtBJ,8DAAA,2EA0BM,cAAA,IAAA,IAAA,EAKH,0BACD,MAAA,MACA,WAAA,IACA,aAAA,MCpDJ,YAAA,IAAA,OAEA,WAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,YD8CK,+CAQC,MAAA,KACA,YAAA,KACA,YAAA,MACA,aAAA,EC5DN,aAAA,IAAA,OAEA,WAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,aDiE6C,yBAE3C,iCACE,SAAA,OACA,WAAA,EACA,OAAA,EACA,WAAA,KAGC,0BACD,YAAA,IACA,QAAA,aACA,eAAA,OChFJ,WAAA,IAAA,OAEA,YAAA,IAAA,MAAA,YACA,aAAA,IAAA,MAAA,YAKG,8EACD,gEADC,6EACD,+DADC,2EACD,6DACE,aAAA,KAFD,gGACD,kFADC,+FACD,iFADC,6FACD,+EACE,aAAA,KAFD,kHACD,oGADC,iHACD,mGADC,+GACD,iGACE,aAAA,KAFD,oIACD,sHADC,mIACD,qHADC,iIACD,mHACE,aAAA,KAFD,yFACD,2EACE,aAAA,KAFD,2GACD,6FACE,aAAA,KAFD,6HACD,+GACE,aAAA,KAFD,+IACD,iIACE,aAAA"} -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/js/bootstrap-submenu.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | /** 8 | * $.inArray: friends with IE8. Use Array.prototype.indexOf in future. 9 | * $.proxy: friends with IE8. Use Function.prototype.bind in future. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | (function(factory) { 15 | if (typeof define == 'function' && define.amd) { 16 | // AMD. Register as an anonymous module 17 | define(['jquery'], factory); 18 | } 19 | else if (typeof exports == 'object') { 20 | // Node/CommonJS 21 | module.exports = factory(require('jquery')); 22 | } 23 | else { 24 | // Browser globals 25 | factory(jQuery); 26 | } 27 | })(function($) { 28 | function Item(element) { 29 | this.$element = $(element); 30 | this.$menu = this.$element.closest('.dropdown-menu'); 31 | this.$main = this.$menu.parent(); 32 | this.$items = this.$menu.children('.dropdown-submenu'); 33 | 34 | this.init(); 35 | } 36 | 37 | Item.prototype = { 38 | init: function() { 39 | this.$element.on('keydown', $.proxy(this, 'keydown')); 40 | }, 41 | close: function() { 42 | this.$main.removeClass('open'); 43 | this.$items.trigger('hide.bs.submenu'); 44 | }, 45 | keydown: function(event) { 46 | // 27: Esc 47 | 48 | if (event.keyCode == 27) { 49 | event.stopPropagation(); 50 | 51 | this.close(); 52 | this.$main.children('a, button').trigger('focus'); 53 | } 54 | } 55 | }; 56 | 57 | function SubmenuItem(element) { 58 | this.$element = $(element); 59 | this.$main = this.$element.parent(); 60 | this.$menu = this.$main.children('.dropdown-menu'); 61 | this.$subs = this.$main.siblings('.dropdown-submenu'); 62 | this.$items = this.$menu.children('.dropdown-submenu'); 63 | 64 | this.init(); 65 | } 66 | 67 | $.extend(SubmenuItem.prototype, Item.prototype, { 68 | init: function() { 69 | this.$element.on({ 70 | click: $.proxy(this, 'click'), 71 | keydown: $.proxy(this, 'keydown') 72 | }); 73 | 74 | this.$main.on('hide.bs.submenu', $.proxy(this, 'hide')); 75 | }, 76 | click: function(event) { 77 | event.stopPropagation(); 78 | 79 | this.toggle(); 80 | }, 81 | hide: function(event) { 82 | // Stop event bubbling 83 | event.stopPropagation(); 84 | 85 | this.close(); 86 | }, 87 | open: function() { 88 | this.$main.addClass('open'); 89 | this.$subs.trigger('hide.bs.submenu'); 90 | }, 91 | toggle: function() { 92 | if (this.$main.hasClass('open')) { 93 | this.close(); 94 | } 95 | else { 96 | this.open(); 97 | } 98 | }, 99 | keydown: function(event) { 100 | // 13: Return, 32: Spacebar 101 | 102 | if (event.keyCode == 32) { 103 | // Off vertical scrolling 104 | event.preventDefault(); 105 | } 106 | 107 | if ($.inArray(event.keyCode, [13, 32]) != -1) { 108 | this.toggle(); 109 | } 110 | } 111 | }); 112 | 113 | function Submenupicker(element) { 114 | this.$element = $(element); 115 | this.$main = this.$element.parent(); 116 | this.$menu = this.$main.children('.dropdown-menu'); 117 | this.$items = this.$menu.children('.dropdown-submenu'); 118 | 119 | this.init(); 120 | } 121 | 122 | Submenupicker.prototype = { 123 | init: function() { 124 | this.$menu.off('keydown.bs.dropdown.data-api'); 125 | this.$menu.on('keydown', $.proxy(this, 'itemKeydown')); 126 | 127 | this.$menu.find('li > a').each(function() { 128 | new Item(this); 129 | }); 130 | 131 | this.$menu.find('.dropdown-submenu > a').each(function() { 132 | new SubmenuItem(this); 133 | }); 134 | 135 | this.$main.on('hidden.bs.dropdown', $.proxy(this, 'hidden')); 136 | }, 137 | hidden: function() { 138 | this.$items.trigger('hide.bs.submenu'); 139 | }, 140 | itemKeydown: function(event) { 141 | // 38: Arrow up, 40: Arrow down 142 | 143 | if ($.inArray(event.keyCode, [38, 40]) != -1) { 144 | // Off vertical scrolling 145 | event.preventDefault(); 146 | 147 | event.stopPropagation(); 148 | 149 | var $items = this.$menu.find('li:not(.disabled):visible > a'); 150 | var index = $items.index(event.target); 151 | 152 | if (event.keyCode == 38 && index !== 0) { 153 | index--; 154 | } 155 | else if (event.keyCode == 40 && index !== $items.length - 1) { 156 | index++; 157 | } 158 | else { 159 | return; 160 | } 161 | 162 | $items.eq(index).trigger('focus'); 163 | } 164 | } 165 | }; 166 | 167 | // For AMD/Node/CommonJS used elements (optional) 168 | // http://learn.jquery.com/jquery-ui/environments/amd/ 169 | return $.fn.submenupicker = function(elements) { 170 | var $elements = this instanceof $ ? this : $(elements); 171 | 172 | return $elements.each(function() { 173 | var data = $.data(this, 'bs.submenu'); 174 | 175 | if (!data) { 176 | data = new Submenupicker(this); 177 | 178 | $.data(this, 'bs.submenu', data); 179 | } 180 | }); 181 | }; 182 | }); 183 | -------------------------------------------------------------------------------- /lib/bootstrap-submenu-2.0.1-dist/js/bootstrap-submenu.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-submenu v2.0.1 (http://vsn4ik.github.io/bootstrap-submenu) 3 | * Copyright 2015 Vasily A. (https://github.com/vsn4ik) 4 | * Licensed under the MIT license 5 | */ 6 | 7 | "use strict";!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){function b(b){this.$element=a(b),this.$menu=this.$element.closest(".dropdown-menu"),this.$main=this.$menu.parent(),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}function c(b){this.$element=a(b),this.$main=this.$element.parent(),this.$menu=this.$main.children(".dropdown-menu"),this.$subs=this.$main.siblings(".dropdown-submenu"),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}function d(b){this.$element=a(b),this.$main=this.$element.parent(),this.$menu=this.$main.children(".dropdown-menu"),this.$items=this.$menu.children(".dropdown-submenu"),this.init()}return b.prototype={init:function(){this.$element.on("keydown",a.proxy(this,"keydown"))},close:function(){this.$main.removeClass("open"),this.$items.trigger("hide.bs.submenu")},keydown:function(a){27==a.keyCode&&(a.stopPropagation(),this.close(),this.$main.children("a, button").trigger("focus"))}},a.extend(c.prototype,b.prototype,{init:function(){this.$element.on({click:a.proxy(this,"click"),keydown:a.proxy(this,"keydown")}),this.$main.on("hide.bs.submenu",a.proxy(this,"hide"))},click:function(a){a.stopPropagation(),this.toggle()},hide:function(a){a.stopPropagation(),this.close()},open:function(){this.$main.addClass("open"),this.$subs.trigger("hide.bs.submenu")},toggle:function(){this.$main.hasClass("open")?this.close():this.open()},keydown:function(b){32==b.keyCode&&b.preventDefault(),-1!=a.inArray(b.keyCode,[13,32])&&this.toggle()}}),d.prototype={init:function(){this.$menu.off("keydown.bs.dropdown.data-api"),this.$menu.on("keydown",a.proxy(this,"itemKeydown")),this.$menu.find("li > a").each(function(){new b(this)}),this.$menu.find(".dropdown-submenu > a").each(function(){new c(this)}),this.$main.on("hidden.bs.dropdown",a.proxy(this,"hidden"))},hidden:function(){this.$items.trigger("hide.bs.submenu")},itemKeydown:function(b){if(-1!=a.inArray(b.keyCode,[38,40])){b.preventDefault(),b.stopPropagation();var c=this.$menu.find("li:not(.disabled):visible > a"),d=c.index(b.target);if(38==b.keyCode&&0!==d)d--;else{if(40!=b.keyCode||d===c.length-1)return;d++}c.eq(d).trigger("focus")}}},a.fn.submenupicker=function(b){var c=this instanceof a?this:a(b);return c.each(function(){var b=a.data(this,"bs.submenu");b||(b=new d(this),a.data(this,"bs.submenu",b))})}}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-bootstrap-submenu", 3 | "version": "1.0.10", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "Ryan Langton", 9 | "license": "MIT", 10 | "dependencies": { 11 | "angular": "^1.3.0", 12 | "bootstrap": "^3.3.6" 13 | }, 14 | "devDependencies": { 15 | "gulp": "^3.9.0", 16 | "gulp-angular-templatecache": "^1.8.0", 17 | "gulp-concat": "^2.6.0", 18 | "gulp-jshint": "^2.0.0", 19 | "gulp-ng-annotate": "^1.1.0", 20 | "gulp-rename": "^1.2.2", 21 | "gulp-rimraf": "^0.2.0", 22 | "gulp-uglify": "^1.5.1", 23 | "gulp-util": "^3.0.7", 24 | "jshint": "^2.9.2" 25 | }, 26 | "files": [ 27 | "dist" 28 | ], 29 | "main" :"./dist/ng-bootstrap-submenu.js" 30 | } 31 | -------------------------------------------------------------------------------- /src/bootstrapSubmenu.html: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 4 | 5 | 6 | 7 | 11 |
  • 12 | -------------------------------------------------------------------------------- /src/bootstrapSubmenu.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | angular.module('bootstrapSubmenu', []); 3 | })(); -------------------------------------------------------------------------------- /src/bootstrapSubmenuController.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | angular 3 | .module('bootstrapSubmenu') 4 | .controller('bootstrapSubmenuController', bootstrapSubmenuController); 5 | 6 | function bootstrapSubmenuController($scope, submenuTrigger){ 7 | submenuTrigger.trigger(); 8 | 9 | $scope.getDropdownClass = function(){ 10 | if (!$scope.hasChildren()) return ''; 11 | return $scope.isSubMenu ? 'dropdown-submenu': 'dropdown'; 12 | }; 13 | 14 | $scope.showCaret = function(){ 15 | return (!$scope.isSubMenu && $scope.hasChildren()); 16 | }; 17 | 18 | $scope.hasChildren = function(){ 19 | return ($scope.menuItem.children !== undefined && $scope.menuItem.children.length > 0); 20 | }; 21 | } 22 | })(); -------------------------------------------------------------------------------- /src/bootstrapSubmenuDirective.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | angular 3 | .module('bootstrapSubmenu') 4 | .directive('bootstrapSubmenu', bootstrapSubmenu); 5 | 6 | function bootstrapSubmenu($compile) { 7 | return { 8 | restrict: 'E', 9 | scope: { 10 | menuItem: '=menuItem', 11 | isSubMenu: '@isSubMenu' 12 | }, 13 | replace: true, 14 | templateUrl: 'bootstrapSubmenu.html', 15 | controller: 'bootstrapSubmenuController', 16 | compile: function (el) { 17 | var contents = el.contents().remove(); 18 | var compiled; 19 | return function(scope,el){ 20 | if(!compiled) 21 | compiled = $compile(contents); 22 | 23 | compiled(scope,function(clone){ 24 | el.append(clone); 25 | }); 26 | }; 27 | } 28 | }; 29 | } 30 | })(); -------------------------------------------------------------------------------- /src/submenuTrigger.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | angular 3 | .module('bootstrapSubmenu') 4 | .factory('submenuTrigger', submenuTrigger); 5 | 6 | function submenuTrigger($timeout){ 7 | var triggered = false; 8 | 9 | return { 10 | trigger: function(){ 11 | if (triggered) return; 12 | 13 | // after angularjs digest, trigger submenupicker 14 | $timeout(function(){ 15 | $('[data-submenu]').submenupicker(); 16 | }, 100); 17 | } 18 | }; 19 | } 20 | })(); --------------------------------------------------------------------------------