├── .gitignore ├── LICENSE ├── README.md ├── demo ├── controllers │ └── demo.js ├── index.html └── resources │ └── styles │ └── demo.css ├── package.json └── source ├── Makefile ├── angular-material-badge.css └── angular-material-badge.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Juan Manuel Mouriz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-material-badge 2 | 3 | Material Badge for AngularJS 4 | 5 | ## Demo 6 | 7 | View [online demo](https://jmouriz.github.io/angular-material-badge/demo/demo.html) 8 | 9 | ## Install 10 | 11 | Download the package: 12 | 13 | ``` 14 | yarn add angular-material-badge 15 | ``` 16 | 17 | ## Usage 18 | 19 | In your document head, include: 20 | 21 | ```html 22 | 23 | ``` 24 | 25 | Use the md-badge where you wish: 26 | 27 | ```html 28 | 12 29 | 30 | 31 | ``` 32 | 33 | Then, just before close body tag, include: 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | Include the module in your application: 40 | 41 | ```javascript 42 | var application = angular.module('Application', ['ngMdBadge']); 43 | ``` 44 | -------------------------------------------------------------------------------- /demo/controllers/demo.js: -------------------------------------------------------------------------------- 1 | var demo = angular.module('Demo', ['ngMaterial', 'ngMdIcons', 'ngMdBadge', 'hc.marked']); 2 | 3 | demo.config(['markedProvider', function(markedProvider) { 4 | markedProvider.setOptions({ 5 | gfm: true, 6 | tables: true, 7 | highlight: function(code, language) { 8 | if (!language) { 9 | language = 'bash'; 10 | } else if (language == 'html') { 11 | language = 'markup'; 12 | } 13 | return Prism.highlight(code, Prism.languages[language]); 14 | } 15 | }); 16 | }]); 17 | 18 | var themes = [{ 19 | name: 'red', 20 | description: 'Red' 21 | }, { 22 | name: 'green', 23 | description: 'Green' 24 | }, { 25 | name: 'blue', 26 | description: 'Blue' 27 | }, { 28 | name: 'orange', 29 | description: 'Orange' 30 | }, { 31 | name: 'indigo', 32 | description: 'Indigo' 33 | }, { 34 | name: 'pink', 35 | description: 'Pink' 36 | }, { 37 | name: 'teal', 38 | description: 'Teal' 39 | }, { 40 | name: 'purple', 41 | description: 'Purple' 42 | }, { 43 | name: 'yellow', 44 | description: 'Yellow' 45 | }, { 46 | name: 'brown', 47 | description: 'Brown' 48 | }, { 49 | name: 'grey', 50 | description: 'Grey' 51 | }, { 52 | name: 'cyan', 53 | description: 'Cyan' 54 | }, { 55 | name: 'lime', 56 | description: 'Lime' 57 | }, { 58 | name: 'amber', 59 | description: 'Amber' 60 | }]; 61 | 62 | demo.config(['$mdThemingProvider', function($mdThemingProvider) { 63 | $mdThemingProvider.theme('default').primaryPalette('purple').accentPalette('blue-grey').warnPalette('red'); 64 | $mdThemingProvider.theme('blue').primaryPalette('blue').accentPalette('blue-grey').warnPalette('red'); 65 | $mdThemingProvider.theme('red').primaryPalette('red').accentPalette('blue-grey').warnPalette('red'); 66 | $mdThemingProvider.theme('green').primaryPalette('green').accentPalette('blue-grey').warnPalette('red'); 67 | $mdThemingProvider.theme('pink').primaryPalette('pink').accentPalette('blue-grey').warnPalette('red'); 68 | $mdThemingProvider.theme('orange').primaryPalette('orange').accentPalette('blue-grey').warnPalette('red'); 69 | $mdThemingProvider.theme('indigo').primaryPalette('indigo').accentPalette('blue-grey').warnPalette('red'); 70 | $mdThemingProvider.theme('teal').primaryPalette('teal').accentPalette('blue-grey').warnPalette('red'); 71 | $mdThemingProvider.theme('purple').primaryPalette('purple').accentPalette('blue-grey').warnPalette('red'); 72 | $mdThemingProvider.theme('yellow').primaryPalette('yellow').accentPalette('blue-grey').warnPalette('red'); 73 | $mdThemingProvider.theme('grey').primaryPalette('grey').accentPalette('blue-grey').warnPalette('red'); 74 | $mdThemingProvider.theme('cyan').primaryPalette('cyan').accentPalette('blue-grey').warnPalette('red'); 75 | $mdThemingProvider.theme('brown').primaryPalette('brown').accentPalette('blue-grey').warnPalette('red'); 76 | $mdThemingProvider.theme('lime').primaryPalette('lime').accentPalette('blue-grey').warnPalette('red'); 77 | $mdThemingProvider.theme('amber').primaryPalette('amber').accentPalette('blue-grey').warnPalette('red'); 78 | $mdThemingProvider.alwaysWatchTheme(true); 79 | }]); 80 | 81 | demo.controller('Demo', ['$scope', function($scope) { 82 | $scope.theme = 'red'; 83 | $scope.themes = themes; 84 | $scope.show = true; 85 | }]); 86 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

AngularJS Material Badge online Demo

17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{theme.description}} 26 | 27 | 28 | 29 | Show 30 | 31 |
32 |
33 |
34 | 35 | Badge demo 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |   45 | 46 | {{value}} 47 |
48 |
49 | 50 | 51 | Badge Demo 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /demo/resources/styles/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 24px; 3 | } 4 | 5 | .test { 6 | align-self: center; 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-material-badge", 3 | "version": "1.2.91", 4 | "description": "Material Badge for AngularJS", 5 | "main": "angular-material-badge.js", 6 | "repository": "https://github.com/jmouriz/angular-material-badge", 7 | "author": "Juan Manuel ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "angular": "^1.6.5", 11 | "angular-animate": "^1.6.5", 12 | "angular-aria": "^1.6.5", 13 | "angular-material": "^1.1.4" 14 | }, 15 | "devDependencies": { 16 | "angular-marked": "^1.2.2", 17 | "angular-material-icons": "^0.7.1", 18 | "minify": "^3.0.1", 19 | "prismjs": "^1.6.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE := $(shell grep name ../package.json | head -1 | cut -d: -f2 | tr -d '" ,') 2 | SOURCE := $(PACKAGE) 3 | TARGET := ../build/$(PACKAGE).min 4 | 5 | all: $(TARGET).js $(TARGET).css 6 | 7 | $(TARGET).css: $(SOURCE).css 8 | @../node_modules/.bin/minify $< > $@ 9 | 10 | $(TARGET).js: $(SOURCE).js 11 | @../node_modules/.bin/minify $< > $@ 12 | -------------------------------------------------------------------------------- /source/angular-material-badge.css: -------------------------------------------------------------------------------- 1 | .md-badge { 2 | background-color: rgb(0, 130, 130); 3 | border-image-outset: 0px; 4 | border-image-repeat: stretch; 5 | border-image-slice: 100%; 6 | border-image-source: none; 7 | color: rgb(255, 255, 255); 8 | border-radius: 10px; 9 | box-sizing: border-box; 10 | cursor: pointer; 11 | display: inline-block; 12 | font-family: Roboto, "Helvetica Neue", sans-serif; 13 | font-size: 11px; 14 | font-stretch: normal; 15 | font-style: normal; 16 | font-variant-caps: normal; 17 | font-variant-ligatures: normal; 18 | font-variant-numeric: normal; 19 | font-weight: normal; 20 | height: 20px; 21 | letter-spacing: 0.4px; 22 | line-height: 18px; 23 | min-width: 20px; 24 | padding-bottom: 0px; 25 | padding-left: 2px; 26 | padding-right: 2px; 27 | padding-top: 1px; 28 | text-align: center; 29 | text-indent: 0px; 30 | text-rendering: auto; 31 | text-shadow: none; 32 | text-size-adjust: 100%; 33 | text-transform: none; 34 | white-space: normal; 35 | word-spacing: 0px; 36 | z-index: 9999; 37 | writing-mode: horizontal-tb; 38 | -webkit-font-smoothing: antialiased; 39 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 40 | } 41 | -------------------------------------------------------------------------------- /source/angular-material-badge.js: -------------------------------------------------------------------------------- 1 | /* onDOMChange: https://stackoverflow.com/a/3219767/1804902 */ 2 | (function (window) { 3 | 'use strict'; 4 | 5 | var last = +new Date(); 6 | var delay = 100; 7 | var stack = []; 8 | 9 | function callback() { 10 | var now = +new Date(); 11 | if (now - last > delay) { 12 | for (var i = 0; i < stack.length; i++) { 13 | stack[i](); 14 | } 15 | last = now; 16 | } 17 | } 18 | 19 | var onDOMChange = function(handler, _delay) { 20 | if (_delay) { 21 | delay = _delay; 22 | } 23 | stack.push(handler); 24 | }; 25 | 26 | function native() { 27 | var last = document.getElementsByTagName('*'); 28 | var _length = last.length; 29 | var timer = setTimeout(function check() { 30 | var current = document.getElementsByTagName('*'); 31 | var length = current.length; 32 | 33 | if (length != _length) { 34 | last = []; 35 | } 36 | 37 | for (var i = 0; i < length; i++) { 38 | if (current[i] !== last[i]) { 39 | callback(); 40 | last = current; 41 | _length = length; 42 | break; 43 | } 44 | } 45 | 46 | setTimeout(check, delay); 47 | }, delay); 48 | } 49 | 50 | var support = {}; 51 | var element = document.documentElement; 52 | var remain = 3; 53 | 54 | function decide() { 55 | if (support.DOMNodeInserted) { 56 | window.addEventListener('DOMContentLoaded', function() { 57 | if (support.DOMSubtreeModified) { 58 | element.addEventListener('DOMSubtreeModified', callback, false); 59 | } else { 60 | element.addEventListener('DOMNodeInserted', callback, false); 61 | element.addEventListener('DOMNodeRemoved', callback, false); 62 | } 63 | }, false); 64 | } else if (document.onpropertychange) { 65 | document.onpropertychange = callback; 66 | } else { 67 | native(); 68 | } 69 | } 70 | 71 | function test(event) { 72 | element.addEventListener(event, function handler() { 73 | support[event] = true; 74 | element.removeEventListener(event, handler, false); 75 | if (--remain === 0) { 76 | decide(); 77 | } 78 | }, false); 79 | } 80 | 81 | if (window.addEventListener) { 82 | test('DOMSubtreeModified'); 83 | test('DOMNodeInserted'); 84 | test('DOMNodeRemoved'); 85 | } else { 86 | decide(); 87 | } 88 | 89 | /* 90 | var dummy = document.createElement('div'); 91 | element.appendChild(dummy); 92 | element.removeChild(dummy); 93 | */ 94 | 95 | window.onDOMChange = onDOMChange; 96 | })(window); 97 | 98 | (function(window, angular, document) { 99 | 'use strict'; 100 | 101 | var module = angular.module('ngMdBadge', ['ngMaterial']); 102 | 103 | module.directive('mdBadge', ['$mdTheming', function($mdTheming) { 104 | return { 105 | restrict: 'E', 106 | replace: true, 107 | transclude: true, 108 | link: function(scope, element, attributes) { 109 | $mdTheming(element); 110 | }, 111 | template: function(element, attributes) { 112 | return '
'; 113 | } 114 | }; 115 | }]); 116 | 117 | module.directive('mdBadge', ['$mdTheming', '$mdColors', '$timeout', '$window', function($mdTheming, $mdColors, $timeout, $window) { 118 | return { 119 | restrict: 'A', 120 | link: function(scope, element, attributes) { 121 | $mdTheming(element); 122 | // 123 | var parent = element.parent(); 124 | var badge = document.createElement('div'); 125 | var offset = parseInt(attributes.mdBadgeOffset); 126 | if (isNaN(offset)) { 127 | offset = 10; 128 | } 129 | function style(where, color) { 130 | if (color) { 131 | if (color.startsWith(':')) { 132 | color = $mdColors.getThemeColor(color.substr(1)); 133 | } 134 | badge.style[where] = color; 135 | } 136 | } 137 | badge.classList.add('md-badge'); 138 | badge.style.position = 'absolute'; 139 | parent.append(badge); 140 | scope.$watch(function() { 141 | return attributes.mdBadgeColor; 142 | }, function(value){ 143 | style('color', value); 144 | }); 145 | scope.$watch(function() { 146 | return attributes.mdBadgeFill; 147 | }, function(value){ 148 | style('background-color', value); 149 | }); 150 | scope.$watch(function() { 151 | return attributes.mdBadge; 152 | }, function(value){ 153 | badge.textContent = value; 154 | badge.style.display = value ? 'initial' : 'none'; 155 | }); 156 | var position = function(value) { 157 | var top = element.prop('offsetTop'); 158 | badge.style.display = attributes.mdBadge && top ? 'initial' : 'none'; 159 | badge.style.left = value.left + value.width - 20 + offset + 'px'; 160 | badge.style.top = value.top + value.height - 20 + offset + 'px'; 161 | }; 162 | scope.$watch(function() { 163 | return { 164 | top: element.prop('offsetTop'), 165 | left: element.prop('offsetLeft'), 166 | width: element.prop('offsetWidth'), 167 | height: element.prop('offsetHeight') 168 | }; 169 | }, function(value) { 170 | position(value); 171 | }, true); 172 | $timeout(function() { 173 | scope.$digest(); 174 | }); 175 | var update = function() { 176 | position({ 177 | top: element.prop('offsetTop'), 178 | left: element.prop('offsetLeft'), 179 | width: element.prop('offsetWidth'), 180 | height: element.prop('offsetHeight') 181 | }); 182 | }; 183 | angular.element($window).bind('resize', function(){ 184 | update(); 185 | }); 186 | onDOMChange(function() { 187 | update(); 188 | }); 189 | }, 190 | }; 191 | }]); 192 | })(window, window.angular, document); 193 | --------------------------------------------------------------------------------