├── .bowerrc ├── liteServerConfig.js ├── docs └── img │ └── demo.png ├── .gitignore ├── demo ├── demo.css ├── demo.js └── index.html ├── bower.json ├── webpack.config.dev.js ├── webpack.config.prod.js ├── LICENSE ├── package.json ├── dist ├── angular-number-picker.min.js └── angular-number-picker.js ├── README.md ├── src └── angular-number-picker.js └── .eslintrc /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "demo/bower" 3 | } -------------------------------------------------------------------------------- /liteServerConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = {port: 8000, server: {baseDir: './demo'}}; 2 | -------------------------------------------------------------------------------- /docs/img/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/angular-number-picker/HEAD/docs/img/demo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #npm 2 | node_modules/ 3 | 4 | 5 | #demo 6 | demo/angular-number-picker.js 7 | demo/angular-number-picker.js.map 8 | demo/bower/ 9 | 10 | #osx 11 | .DS_Store -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | .input-group{ 2 | width: 210px; 3 | margin-left: auto; 4 | margin-right: auto; 5 | } 6 | 7 | span[disabled] 8 | { 9 | pointer-events: none; 10 | cursor: default; 11 | background-color: #eee; 12 | opacity: 1; 13 | } 14 | 15 | .input-group-addon.active{ 16 | color: #fff; 17 | background-color: #007aff; 18 | } 19 | 20 | .picker-unit-left{ 21 | margin-right: 10px; 22 | } 23 | 24 | .picker-unit-right{ 25 | margin-left: 10px; 26 | } 27 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | 2 | var demo = angular.module('demo', [window.ngNumberPicker]); 3 | 4 | demo.controller('DemoController', [ 5 | '$scope', 6 | function($scope) { 7 | 8 | $scope.input = {num: 2}; 9 | 10 | $scope.getNumber = function() { 11 | alert('The number is: [' + $scope.input.num + ']'); 12 | }; 13 | 14 | $scope.onChange = function() { 15 | console.log('number changed', $scope.input.num); 16 | }; 17 | 18 | } 19 | ]); 20 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-number-picker", 3 | "version": "2.2.0", 4 | "homepage": "https://github.com/leftstick/angular-number-picker", 5 | "description": "A directive used for picking number by using up/down button, instead of typing", 6 | "keywords": [ 7 | "angular", 8 | "number", 9 | "picker" 10 | ], 11 | "files": [ 12 | "dist/angular-number-picker.js", 13 | "dist/angular-number-picker.min.js", 14 | "src/angular-number-picker.js" 15 | ], 16 | "authors": [ 17 | "Howard.Zuo" 18 | ], 19 | "license": "MIT", 20 | "devDependencies": { 21 | "angular": "~1.5.5", 22 | "bootstrap": "~3.3.6" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | entry: { 5 | index: './src/angular-number-picker.js' 6 | }, 7 | output: { 8 | path: path.resolve(__dirname, 'demo'), 9 | filename: 'angular-number-picker.js', 10 | libraryTarget: 'umd' 11 | }, 12 | debug: true, 13 | devtool: 'source-map', 14 | module: { 15 | loaders: [ 16 | { 17 | test: /\.js$/, 18 | loader: 'babel?{"presets":["es2015"]}', 19 | exclude: /(node_modules)/ 20 | } 21 | ] 22 | }, 23 | externals: { 24 | angular: 'angular' 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var UnminifiedWebpackPlugin = require('unminified-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: { 7 | index: './src/angular-number-picker.js' 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'angular-number-picker.min.js', 12 | libraryTarget: 'umd' 13 | }, 14 | module: { 15 | loaders: [ 16 | { 17 | test: /\.js$/, 18 | loader: 'babel?{"presets":["es2015"]}', 19 | exclude: /(node_modules)/ 20 | } 21 | ] 22 | }, 23 | externals: { 24 | angular: 'angular' 25 | }, 26 | plugins: [ 27 | new webpack.optimize.UglifyJsPlugin({ 28 | compress: { 29 | warnings: false 30 | } 31 | }), 32 | new UnminifiedWebpackPlugin() 33 | ] 34 | }; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Howard.Zuo 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-number-picker", 3 | "version": "2.2.0", 4 | "main": "dist/angular-number-picker.js", 5 | "files": [ 6 | "dist/angular-number-picker.js", 7 | "dist/angular-number-picker.min.js", 8 | "src/angular-number-picker.js" 9 | ], 10 | "scripts": { 11 | "watch": "webpack -w --config webpack.config.dev.js", 12 | "start": "concurrently \"npm run watch\" \"lite-server -c liteServerConfig.js\"", 13 | "dist": "rm -rf dist; webpack --config webpack.config.prod.js" 14 | }, 15 | "description": "A directive used for picking number by using up/down button, instead of typing", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/leftstick/angular-number-picker.git" 19 | }, 20 | "keywords": [ 21 | "angular", 22 | "number", 23 | "picker" 24 | ], 25 | "author": "Howard.Zuo", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/leftstick/angular-number-picker/issues" 29 | }, 30 | "homepage": "https://github.com/leftstick/angular-number-picker", 31 | "devDependencies": { 32 | "babel-core": "^6.8.0", 33 | "babel-loader": "^6.2.4", 34 | "babel-preset-es2015": "^6.6.0", 35 | "concurrently": "^2.0.0", 36 | "lite-server": "^2.2.0", 37 | "unminified-webpack-plugin": "^1.0.0", 38 | "webpack": "^1.13.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEMO 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
Demo
16 |
17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /dist/angular-number-picker.min.js: -------------------------------------------------------------------------------- 1 | !function(n,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("angular"));else if("function"==typeof define&&define.amd)define(["angular"],e);else{var t=e("object"==typeof exports?require("angular"):n.angular);for(var r in t)("object"==typeof exports?exports:n)[r]=t[r]}}(this,function(n){return function(n){function e(r){if(t[r])return t[r].exports;var i=t[r]={exports:{},id:r,loaded:!1};return n[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var t={};return e.m=n,e.c=t,e.p="",e(0)}([function(n,e,t){"use strict";function r(n){if(n&&n.__esModule)return n;var e={};if(null!=n)for(var t in n)Object.prototype.hasOwnProperty.call(n,t)&&(e[t]=n[t]);return e.default=n,e}Object.defineProperty(e,"__esModule",{value:!0}),e.ngNumberPicker=void 0;var i=t(1),o=r(i),a=function(n){return null===n||void 0===n},u=function(){"function"!=typeof Object.assign&&(Object.assign=function(n){if(a(n))throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(n),t=1;t0?o.element(n.touches[0].target):o.element(n.target)},a=function(n){return i(n).attr("type")},c=function(n){for(var e in n){var r=n[e];n[e]=t(r)}},l=function(n,t){return{restrict:"E",transclude:!0,scope:{value:"=",singular:"@",plural:"@",unitPosition:"@",min:"@",max:"@",step:"@",change:"&"},link:function(o,u){var l=Object.assign({},e,{min:o.min,max:o.max,step:o.step});c(l),r(l.min),r(l.max),r(l.step),l.min>o.value&&(o.value=l.min),o.$watch("value",function(n,e){o.canDown=n>l.min,o.canUp=n=l.max)return;o.value+=l.step,o.value>l.max&&(o.value=l.max)}else if("down"===e){if(o.value<=l.min)return;o.value-=l.step,o.value-+'}};return o.module(n,[]).directive("hNumber",["$timeout","$interval",l]),n}()},function(e,t){e.exports=n}])}); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-number-picker # 2 | ===================== 3 | 4 | ![][bower-url] 5 | [![NPM version][npm-image]][npm-url] 6 | ![][david-url] 7 | ![][dt-url] 8 | ![][license-url] 9 | 10 | 11 | A directive used for picking number by using -/+ button, instead of typing the number directly. 12 | 13 | This is an `angular` directive designed in `mobile-first` concept. Which means you would have better user experience while in mobile development. 14 | 15 | > While running on mobile device, you would increase/decrease the number continuously by long tap the -/+ button. 16 | 17 | ![](https://raw.githubusercontent.com/leftstick/angular-number-picker/master/docs/img/demo.png) 18 | 19 | Try it: [plunker](http://plnkr.co/edit/b2p7Vb?p=preview) 20 | 21 | 22 | ## Requirement ## 23 | 24 | - [angularjs](http://angularjs.org/) (1.4.3+) 25 | 26 | ## UI dependency(optional) ## 27 | 28 | - [bootstrap](http://getbootstrap.com) (3.3.5+) 29 | 30 | ## Installation ## 31 | 32 | ### via bower ### 33 | 34 | ```JavaScript 35 | bower install angular-number-picker --save 36 | ``` 37 | 38 | ### via npm ### 39 | 40 | ```JavaScript 41 | npm install angular-number-picker --save 42 | ``` 43 | 44 | ### via script ### 45 | 46 | ```html 47 | 48 | 49 | ``` 50 | 51 | ## Import ## 52 | 53 | ### via ES2015 ### 54 | 55 | ```javascript 56 | import {ngNumberPicker} from 'angular-number-picker'; 57 | ``` 58 | 59 | ### via CommonJS ### 60 | 61 | ```javascript 62 | var ngNumberPicker = require('angular-number-picker').ngNumberPicker; 63 | ``` 64 | 65 | ### via script ### 66 | 67 | ```html 68 | 71 | ``` 72 | 73 | ## Basic Usage ## 74 | 75 | **Add `ngNumberPicker` module as your angular app's dependency** 76 | 77 | ```javascript 78 | var demo = angular.module('demo', [ngNumberPicker]); 79 | ``` 80 | 81 | >`ngNumberPicker` is the variable you get from above "Import" stage 82 | 83 | **Use `h-number` tag in your html** 84 | 85 | ```HTML 86 |
87 | 88 |
89 | ``` 90 | 91 | **You can use transclusion, too** 92 | 93 | ```HTML 94 |
95 | 96 | 97 | 98 |
99 | ``` 100 | 101 | **Writing `DemoController`** 102 | 103 | ```javascript 104 | demo.controller('DemoController', ['$scope', function($scope) { 105 | $scope.input = { 106 | num: 0 107 | }; 108 | 109 | $scope.getNumber = function() { 110 | alert('The number is: [' + $scope.input.num + ']'); 111 | }; 112 | 113 | $scope.onChange = function(){ 114 | console.log('The number is Changed ', $scope.input.num); 115 | }; 116 | }]); 117 | ``` 118 | 119 | ## `h-number` configuration ## 120 | 121 | | Attribute | Type | Required | Description | 122 | | :------------- |:-------------| :-----:| :-----| 123 | | value | [expression] | Yes | Expression to evaluate as the input number | 124 | | min | Number | No | The minimal point to limit the operation. 0 by default | 125 | | max | Number | No | The maximum point to limit the operation. 100 by default | 126 | | step | Number | No | The step value for the operation. 1 by default| 127 | | singular | String | No | Label to be included after value when value is 1| 128 | | plural | String | No | Label to be included after value when value is not 1| 129 | | unit-position | String | No | where to place the singular/plural. Available options: `left`, `right`. `right` by default. | 130 | | change | Function | No | Function to be called while number is changed| 131 | 132 | ## build-in class ## 133 | 134 | ### `input-group` ### 135 | 136 | The wrapper class for the `h-number` element 137 | 138 | ### `input-group-addon` ### 139 | 140 | The wrapper class for `+-` operator 141 | 142 | ### `form-control` ### 143 | 144 | The wrapper class for the number area at the center 145 | 146 | ### `active` ### 147 | 148 | The `active` class will be added to the `input-group-addon` button, while touching them. So it's possible to implement your own behavior. 149 | 150 | ### `picker-unit-left` ### 151 | 152 | The `picker-unit-left` class will be added to the left `unit` span 153 | 154 | ### `picker-unit-right` ### 155 | 156 | The `picker-unit-right` class will be added to the right `unit` span 157 | 158 | 159 | > It's easy to implement those classes to achieve your own UI 160 | 161 | 162 | ## run demo locally ## 163 | 164 | ### Install npm dependency ### 165 | 166 | ```bash 167 | npm install 168 | ``` 169 | 170 | ### Install bower dependency ### 171 | 172 | ```bash 173 | bower install 174 | ``` 175 | 176 | ### run demo ### 177 | ```Shell 178 | npm start 179 | ``` 180 | 181 | >I will launch a debug server at [http://localhost:8000/](http://localhost:8000/) 182 | 183 | 184 | ## LICENSE ## 185 | 186 | [MIT License](https://raw.githubusercontent.com/leftstick/angular-number-picker/master/LICENSE) 187 | 188 | 189 | [expression]: https://docs.angularjs.org/guide/expression 190 | [bower-url]: https://img.shields.io/bower/v/angular-number-picker.svg 191 | [npm-url]: https://npmjs.org/package/angular-number-picker 192 | [npm-image]: https://badge.fury.io/js/angular-number-picker.png 193 | [david-url]: https://david-dm.org/leftstick/angular-number-picker.png 194 | [dt-url]:https://img.shields.io/npm/dt/angular-number-picker.svg 195 | [license-url]:https://img.shields.io/npm/l/angular-number-picker.svg 196 | -------------------------------------------------------------------------------- /src/angular-number-picker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines `hNumberPicker` directive which can only be used as element. 4 | * 5 | * It allows end-user to choose number, instead of typing 6 | * 7 | * usage: 8 | * 9 | * 10 | * 11 | * @author Howard.Zuo 12 | * @date May 16th, 2016 13 | * 14 | */ 15 | 16 | import * as ng from 'angular'; 17 | 18 | const isNull = obj => obj === null || obj === undefined; 19 | 20 | const polyfill = function() { 21 | if (typeof Object.assign !== 'function') { 22 | Object.assign = function(target) { 23 | 24 | // We must check against these specific cases. 25 | if (isNull(target)) { 26 | throw new TypeError('Cannot convert undefined or null to object'); 27 | } 28 | 29 | var output = Object(target); 30 | for (var index = 1; index < arguments.length; index++) { 31 | var source = arguments[index]; 32 | if (!isNull(source)) { 33 | for (var nextKey in source) { 34 | if (source.hasOwnProperty(nextKey)) { 35 | output[nextKey] = source[nextKey]; 36 | } 37 | } 38 | } 39 | } 40 | return output; 41 | }; 42 | } 43 | }; 44 | 45 | export const ngNumberPicker = (function() { 46 | 47 | polyfill(); 48 | 49 | let name = 'ngNumberPicker'; 50 | 51 | var defaults = { 52 | min: 0, 53 | max: 100, 54 | step: 1, 55 | timeout: 600 56 | }; 57 | 58 | var toNumber = function(value) { 59 | return Number(value); 60 | }; 61 | 62 | var checkNumber = function(value) { 63 | if (!ng.isNumber(value)) { 64 | throw new Error('value [' + value + '] is not a valid number'); 65 | } 66 | }; 67 | 68 | var getTarget = function(e) { 69 | if (e.touches && e.touches.length > 0) { 70 | return ng.element(e.touches[0].target); 71 | } 72 | return ng.element(e.target); 73 | }; 74 | 75 | var getType = function(e) { 76 | return getTarget(e).attr('type'); 77 | }; 78 | 79 | var transform = function(opts) { 80 | for (var key in opts) { 81 | var value = opts[key]; 82 | opts[key] = toNumber(value); 83 | } 84 | }; 85 | 86 | var directive = function($timeout, $interval) { 87 | 88 | return { 89 | restrict: 'E', 90 | transclude: true, 91 | scope: { 92 | value: '=', 93 | singular: '@', 94 | plural: '@', 95 | unitPosition: '@', 96 | min: '@', 97 | max: '@', 98 | step: '@', 99 | change: '&' 100 | }, 101 | link: function($scope, element) { 102 | 103 | var opts = Object.assign({}, defaults, { 104 | min: $scope.min, 105 | max: $scope.max, 106 | step: $scope.step 107 | }); 108 | 109 | transform(opts); 110 | 111 | checkNumber(opts.min); 112 | checkNumber(opts.max); 113 | checkNumber(opts.step); 114 | 115 | 116 | if (opts.min > $scope.value) { 117 | $scope.value = opts.min; 118 | } 119 | 120 | $scope.$watch('value', function(newValue, oldValue) { 121 | $scope.canDown = newValue > opts.min; 122 | $scope.canUp = newValue < opts.max; 123 | $scope.unit = newValue === 1 ? $scope.singular : $scope.plural; 124 | 125 | if (newValue !== oldValue) { 126 | $scope.change(); 127 | } 128 | }); 129 | 130 | var changeNumber = function($event) { 131 | var type = getType($event); 132 | 133 | //cast existing value to number, so += will really increment decimal number 134 | $scope.value = Number($scope.value); 135 | 136 | if (type === 'up') { 137 | if ($scope.value >= opts.max) { 138 | return; 139 | } 140 | $scope.value += opts.step; 141 | if ($scope.value > opts.max) { 142 | $scope.value = opts.max; 143 | } 144 | } else if (type === 'down') { 145 | if ($scope.value <= opts.min) { 146 | return; 147 | } 148 | $scope.value -= opts.step; 149 | if ($scope.value < opts.min) { 150 | $scope.value = opts.min; 151 | } 152 | } 153 | }; 154 | 155 | var isPressing; 156 | var timeoutPro; 157 | var intervalPro; 158 | var start; 159 | var end; 160 | var addon = element.find('span'); 161 | 162 | addon.on('click', function(e) { 163 | changeNumber(e); 164 | $scope.$apply(); 165 | e.stopPropagation(); 166 | }); 167 | 168 | addon.on('touchstart', function(e) { 169 | if (isPressing) { 170 | return; 171 | } 172 | isPressing = true; 173 | getTarget(e).addClass('active'); 174 | start = new Date().getTime(); 175 | timeoutPro = $timeout(function() { 176 | intervalPro = $interval(function() { 177 | changeNumber(e); 178 | }, 200); 179 | }, opts.timeout); 180 | e.preventDefault(); 181 | }); 182 | 183 | addon.on('touchend', function(e) { 184 | end = new Date().getTime(); 185 | if (intervalPro) { 186 | $interval.cancel(intervalPro); 187 | intervalPro = undefined; 188 | } 189 | if (timeoutPro) { 190 | $timeout.cancel(timeoutPro); 191 | timeoutPro = undefined; 192 | } 193 | if ((end - start) < opts.timeout) { 194 | changeNumber(e); 195 | $scope.$apply(); 196 | } 197 | getTarget(e).removeClass('active'); 198 | e.stopPropagation(); 199 | isPressing = false; 200 | }); 201 | 202 | $scope.$on('$destroy', function() { 203 | addon.off('touchstart touchend click'); 204 | }); 205 | 206 | }, 207 | template: '
-+
' 208 | }; 209 | }; 210 | 211 | ng.module(name, []) 212 | .directive('hNumber', ['$timeout', '$interval', directive]); 213 | 214 | return name; 215 | }()); 216 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions":{ 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | "globalReturn": true, 7 | "impliedStrict": false, 8 | "jsx": false, 9 | "experimentalObjectRestSpread": true 10 | } 11 | }, 12 | "env": { 13 | "shared-node-browser": true, 14 | "browser": true, 15 | "commonjs": true, 16 | "node": true, 17 | "es6": true, 18 | "mocha": true, 19 | "jquery": true 20 | }, 21 | "rules": { 22 | "comma-dangle": [2, "never"], 23 | "no-cond-assign": [2, "except-parens"], 24 | "no-console": 0, 25 | "no-constant-condition": 1, 26 | "no-control-regex": 2, 27 | "no-debugger": 2, 28 | "no-dupe-args": 2, 29 | "no-dupe-keys": 2, 30 | "no-duplicate-case": 2, 31 | "no-empty-character-class": 1, 32 | "no-empty": 1, 33 | "no-ex-assign": 2, 34 | "no-extra-boolean-cast": 1, 35 | "no-extra-parens": [1, "functions"], 36 | "no-extra-semi": 2, 37 | "no-func-assign": 2, 38 | "no-inner-declarations": [1, "functions"], 39 | "no-invalid-regexp": 2, 40 | "no-irregular-whitespace": 1, 41 | "no-negated-in-lhs": 2, 42 | "no-obj-calls": 2, 43 | "no-regex-spaces": 1, 44 | "no-sparse-arrays": 2, 45 | "no-unexpected-multiline": 2, 46 | "no-unreachable": 2, 47 | "use-isnan": 2, 48 | "valid-jsdoc": 0, 49 | "valid-typeof": 2, 50 | "accessor-pairs": [1, { "getWithoutSet": true, "setWithoutGet": true }], 51 | "array-callback-return": 1, 52 | "block-scoped-var": 0, 53 | "complexity": [1, 7], 54 | "consistent-return": 0, 55 | "curly": 1, 56 | "default-case": 2, 57 | "dot-location": [1, "property"], 58 | "dot-notation": [1, { "allowKeywords": true}], 59 | "eqeqeq": 2, 60 | "guard-for-in": 0, 61 | "no-alert": 2, 62 | "no-caller": 2, 63 | "no-case-declarations": 2, 64 | "no-div-regex": 1, 65 | "no-else-return": 1, 66 | "no-empty-function": 0, 67 | "no-empty-pattern": 2, 68 | "no-eq-null": 2, 69 | "no-eval": 2, 70 | "no-extend-native": 1, 71 | "no-extra-bind": 1, 72 | "no-extra-label": 1, 73 | "no-fallthrough": 2, 74 | "no-floating-decimal": 2, 75 | "no-implicit-coercion": 0, 76 | "no-implicit-globals": 0, 77 | "no-implied-eval": 2, 78 | "no-invalid-this": 0, 79 | "no-iterator": 1, 80 | "no-labels": [2, {"allowLoop": false, "allowSwitch": false}], 81 | "no-lone-blocks": 2, 82 | "no-loop-func": 2, 83 | "no-magic-numbers": 0, 84 | "no-multi-spaces": 1, 85 | "no-multi-str": 2, 86 | "no-native-reassign": 2, 87 | "no-new-func": 2, 88 | "no-new-wrappers": 2, 89 | "no-new": 1, 90 | "no-octal-escape": 1, 91 | "no-octal": 1, 92 | "no-param-reassign": [2, {"props": false}], 93 | "no-process-env": 0, 94 | "no-proto": 2, 95 | "no-redeclare": [2, { "builtinGlobals": true }], 96 | "no-return-assign": [2, "except-parens"], 97 | "no-script-url": 2, 98 | "no-self-assign": 2, 99 | "no-self-compare": 2, 100 | "no-sequences": 2, 101 | "no-throw-literal": 2, 102 | "no-unmodified-loop-condition": 1, 103 | "no-unused-expressions": [2, { "allowShortCircuit": true, "allowTernary": false }], 104 | "no-unused-labels": 1, 105 | "no-useless-call": 1, 106 | "no-useless-concat": 1, 107 | "no-void": 2, 108 | "no-warning-comments": [1, { "terms": ["todo", "fix"], "location": "anywhere" }], 109 | "no-with": 2, 110 | "radix": [2, "as-needed"], 111 | "vars-on-top": 0, 112 | "wrap-iife": [2, "outside"], 113 | "yoda": [1, "never", { "onlyEquality": true }], 114 | "strict": [1, "safe"], 115 | "init-declarations": 0, 116 | "no-catch-shadow": 2, 117 | "no-delete-var": 2, 118 | "no-label-var": 2, 119 | "no-shadow-restricted-names": 2, 120 | "no-shadow": [2, {"builtinGlobals": false, "hoist": "functions"}], 121 | "no-undef-init": 1, 122 | "no-undef": [2, { "typeof": false }], 123 | "no-undefined": 1, 124 | "no-unused-vars": [1, { "vars": "all", "args": "none" }], 125 | "no-use-before-define": [1, {"functions": true, "classes": true}], 126 | "array-bracket-spacing": [1, "never"], 127 | "block-spacing": [1, "always"], 128 | "brace-style": [1, "1tbs", { "allowSingleLine": true }], 129 | "camelcase": [1, {"properties": "never"}], 130 | "comma-spacing": [2, {"before": false, "after": true}], 131 | "comma-style": [1, "last"], 132 | "computed-property-spacing": [1, "never"], 133 | "consistent-this": [1, "_this", "self", "ctx"], 134 | "eol-last": [1, "unix"], 135 | "func-names": 0, 136 | "func-style": [1, "expression", { "allowArrowFunctions": true }], 137 | "id-length": [2, {"min": 1, "max": 35, "properties": "never"}], 138 | "id-match": 0, 139 | "id-blacklist": 0, 140 | "indent": [1, 4, {"VariableDeclarator": 1, "SwitchCase": 1}], 141 | "jsx-quotes": 0, 142 | "key-spacing": [1, {"beforeColon": false, "afterColon": true}], 143 | "keyword-spacing": [1, {"before": true, "after": true}], 144 | "linebreak-style": [1, "unix"], 145 | "lines-around-comment": [1, { "beforeBlockComment": true, "afterBlockComment": false, "beforeLineComment": true, "afterLineComment": false, "allowBlockStart": true, "allowBlockEnd": true, "allowObjectStart": true, "allowArrayStart": false, "allowArrayEnd": false }], 146 | "max-depth": [1, 5], 147 | "max-len": [1, {"code": 120, "comments": 120, "tabWidth": 4, "ignoreUrls": true}], 148 | "max-nested-callbacks": [1, 5], 149 | "max-params": [1, 10], 150 | "max-statements": [1, 12, {"ignoreTopLevelFunctions": true}], 151 | "new-cap": [1, {"newIsCap": true, "capIsNew": true, "properties": false}], 152 | "new-parens": 1, 153 | "newline-after-var": 0, 154 | "newline-per-chained-call": 0, 155 | "no-array-constructor": 1, 156 | "no-bitwise": 0, 157 | "no-continue": 0, 158 | "no-inline-comments": 0, 159 | "no-lonely-if": 1, 160 | "no-mixed-spaces-and-tabs": 1, 161 | "no-multiple-empty-lines": [1, {"max": 2}], 162 | "no-negated-condition": 0, 163 | "no-nested-ternary": 1, 164 | "no-new-object": 1, 165 | "no-plusplus": 0, 166 | "no-restricted-syntax": [1, "WithStatement"], 167 | "no-whitespace-before-property": 1, 168 | "no-spaced-func": 1, 169 | "no-ternary": 0, 170 | "no-trailing-spaces": [1, { "skipBlankLines": true }], 171 | "no-underscore-dangle": 0, 172 | "no-unneeded-ternary": 1, 173 | "object-curly-spacing": [1, "never"], 174 | "one-var": 0, 175 | "one-var-declaration-per-line": 0, 176 | "operator-assignment": 0, 177 | "operator-linebreak": 0, 178 | "padded-blocks": 0, 179 | "quote-props": [1, "as-needed"], 180 | "quotes": [1, "single"], 181 | "require-jsdoc": 0, 182 | "semi-spacing": [2, { "before": false, "after": true }], 183 | "semi": [1, "always"], 184 | "sort-vars": 0, 185 | "sort-imports": 0, 186 | "space-before-blocks": [1, { "functions": "always", "keywords": "always", "classes": "always" }], 187 | "space-before-function-paren": [1, {"anonymous": "never", "named": "never"}], 188 | "space-in-parens": [1, "never"], 189 | "space-infix-ops": 1, 190 | "space-unary-ops": [1, { "words": true, "nonwords": false }], 191 | "spaced-comment": 0, 192 | "wrap-regex": 0 193 | }, 194 | "globals": { 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /dist/angular-number-picker.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("angular")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["angular"], factory); 6 | else { 7 | var a = typeof exports === 'object' ? factory(require("angular")) : factory(root["angular"]); 8 | for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; 9 | } 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | /******/ 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 'use strict'; 58 | 59 | Object.defineProperty(exports, "__esModule", { 60 | value: true 61 | }); 62 | exports.ngNumberPicker = undefined; 63 | 64 | var _angular = __webpack_require__(1); 65 | 66 | var ng = _interopRequireWildcard(_angular); 67 | 68 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 69 | 70 | var isNull = function isNull(obj) { 71 | return obj === null || obj === undefined; 72 | }; /** 73 | * 74 | * Defines `hNumberPicker` directive which can only be used as element. 75 | * 76 | * It allows end-user to choose number, instead of typing 77 | * 78 | * usage: 79 | * 80 | * 81 | * 82 | * @author Howard.Zuo 83 | * @date May 16th, 2016 84 | * 85 | */ 86 | 87 | var polyfill = function polyfill() { 88 | if (typeof Object.assign !== 'function') { 89 | Object.assign = function (target) { 90 | 91 | // We must check against these specific cases. 92 | if (isNull(target)) { 93 | throw new TypeError('Cannot convert undefined or null to object'); 94 | } 95 | 96 | var output = Object(target); 97 | for (var index = 1; index < arguments.length; index++) { 98 | var source = arguments[index]; 99 | if (!isNull(source)) { 100 | for (var nextKey in source) { 101 | if (source.hasOwnProperty(nextKey)) { 102 | output[nextKey] = source[nextKey]; 103 | } 104 | } 105 | } 106 | } 107 | return output; 108 | }; 109 | } 110 | }; 111 | 112 | var ngNumberPicker = exports.ngNumberPicker = function () { 113 | 114 | polyfill(); 115 | 116 | var name = 'ngNumberPicker'; 117 | 118 | var defaults = { 119 | min: 0, 120 | max: 100, 121 | step: 1, 122 | timeout: 600 123 | }; 124 | 125 | var toNumber = function toNumber(value) { 126 | return Number(value); 127 | }; 128 | 129 | var checkNumber = function checkNumber(value) { 130 | if (!ng.isNumber(value)) { 131 | throw new Error('value [' + value + '] is not a valid number'); 132 | } 133 | }; 134 | 135 | var getTarget = function getTarget(e) { 136 | if (e.touches && e.touches.length > 0) { 137 | return ng.element(e.touches[0].target); 138 | } 139 | return ng.element(e.target); 140 | }; 141 | 142 | var getType = function getType(e) { 143 | return getTarget(e).attr('type'); 144 | }; 145 | 146 | var transform = function transform(opts) { 147 | for (var key in opts) { 148 | var value = opts[key]; 149 | opts[key] = toNumber(value); 150 | } 151 | }; 152 | 153 | var directive = function directive($timeout, $interval) { 154 | 155 | return { 156 | restrict: 'E', 157 | transclude: true, 158 | scope: { 159 | value: '=', 160 | singular: '@', 161 | plural: '@', 162 | unitPosition: '@', 163 | min: '@', 164 | max: '@', 165 | step: '@', 166 | change: '&' 167 | }, 168 | link: function link($scope, element) { 169 | 170 | var opts = Object.assign({}, defaults, { 171 | min: $scope.min, 172 | max: $scope.max, 173 | step: $scope.step 174 | }); 175 | 176 | transform(opts); 177 | 178 | checkNumber(opts.min); 179 | checkNumber(opts.max); 180 | checkNumber(opts.step); 181 | 182 | if (opts.min > $scope.value) { 183 | $scope.value = opts.min; 184 | } 185 | 186 | $scope.$watch('value', function (newValue, oldValue) { 187 | $scope.canDown = newValue > opts.min; 188 | $scope.canUp = newValue < opts.max; 189 | $scope.unit = newValue === 1 ? $scope.singular : $scope.plural; 190 | 191 | if (newValue !== oldValue) { 192 | $scope.change(); 193 | } 194 | }); 195 | 196 | var changeNumber = function changeNumber($event) { 197 | var type = getType($event); 198 | 199 | //cast existing value to number, so += will really increment decimal number 200 | $scope.value = Number($scope.value); 201 | 202 | if (type === 'up') { 203 | if ($scope.value >= opts.max) { 204 | return; 205 | } 206 | $scope.value += opts.step; 207 | if ($scope.value > opts.max) { 208 | $scope.value = opts.max; 209 | } 210 | } else if (type === 'down') { 211 | if ($scope.value <= opts.min) { 212 | return; 213 | } 214 | $scope.value -= opts.step; 215 | if ($scope.value < opts.min) { 216 | $scope.value = opts.min; 217 | } 218 | } 219 | }; 220 | 221 | var isPressing; 222 | var timeoutPro; 223 | var intervalPro; 224 | var start; 225 | var end; 226 | var addon = element.find('span'); 227 | 228 | addon.on('click', function (e) { 229 | changeNumber(e); 230 | $scope.$apply(); 231 | e.stopPropagation(); 232 | }); 233 | 234 | addon.on('touchstart', function (e) { 235 | if (isPressing) { 236 | return; 237 | } 238 | isPressing = true; 239 | getTarget(e).addClass('active'); 240 | start = new Date().getTime(); 241 | timeoutPro = $timeout(function () { 242 | intervalPro = $interval(function () { 243 | changeNumber(e); 244 | }, 200); 245 | }, opts.timeout); 246 | e.preventDefault(); 247 | }); 248 | 249 | addon.on('touchend', function (e) { 250 | end = new Date().getTime(); 251 | if (intervalPro) { 252 | $interval.cancel(intervalPro); 253 | intervalPro = undefined; 254 | } 255 | if (timeoutPro) { 256 | $timeout.cancel(timeoutPro); 257 | timeoutPro = undefined; 258 | } 259 | if (end - start < opts.timeout) { 260 | changeNumber(e); 261 | $scope.$apply(); 262 | } 263 | getTarget(e).removeClass('active'); 264 | e.stopPropagation(); 265 | isPressing = false; 266 | }); 267 | 268 | $scope.$on('$destroy', function () { 269 | addon.off('touchstart touchend click'); 270 | }); 271 | }, 272 | template: '
-+
' 273 | }; 274 | }; 275 | 276 | ng.module(name, []).directive('hNumber', ['$timeout', '$interval', directive]); 277 | 278 | return name; 279 | }(); 280 | 281 | /***/ }, 282 | /* 1 */ 283 | /***/ function(module, exports) { 284 | 285 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 286 | 287 | /***/ } 288 | /******/ ]) 289 | }); 290 | ; --------------------------------------------------------------------------------