├── .editorconfig ├── .gitignore ├── README.md ├── angular-autonumeric.iml ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── src ├── README.md ├── browserslist ├── index.ts ├── karma.conf.js ├── lib │ ├── autonumeric-defaults.service.ts │ ├── autonumeric.directive.ts │ ├── autonumeric.model.ts │ ├── autonumeric.module.ts │ └── index.ts ├── ng-package.json ├── ng-package.prod.json ├── package-lock.json ├── package.json ├── public_api.ts ├── test.ts ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | src/node_modules 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## @angularfy/autonumeric 2 | 3 | An Angular library that wraps the awesome [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) library 4 | 5 | Get in touch : abdelghani@ainouss.fr 6 | 7 | --- 8 | 9 | @angularfy/autonumeric supports most of [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) options. 10 | 11 | **Checkout the [demo](https://angularfy-autonumeric.stackblitz.io)** 12 | 13 | *Note: In order to minimize the size of the @angularfy/autonumeric, the AutoNumeric library dependency **is not** bundled with it.* 14 | 15 | This means you **need** to include the [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) library. 16 | 17 | ## Dependencies 18 | 19 | The dependency is [autoNumeric 4](https://github.com/autoNumeric/autoNumeric/). 20 | Here is the list of minimal required versions: 21 | 22 | | @angularfy/autonumeric | angular | autoNumeric | 23 | | ---------------------- | ------- | ------------- | 24 | | 1.x.x | ^4.0.0 | ^4.0.0 | 25 | | 2.x.x | ^4.0.0 | ^4.0.0 | 26 | | 3.x.x | ^4.0.0 | ^4.0.0 | 27 | 28 | --- 29 | ## Installation 30 | 31 | After installing the above dependencies, install `@angularfy/autonumeric` via npm: 32 | 33 | ```shell 34 | npm install --save @angularfy/autonumeric 35 | ``` 36 | or yarn : 37 | 38 | ```shell 39 | yarn add @angularfy/autonumeric 40 | ``` 41 | 42 | Once installed you need to import our main module: 43 | ```js 44 | import { AutonumericModule } from '@angularfy/autonumeric'; 45 | 46 | @NgModule({ 47 | //... 48 | imports: [ 49 | AutonumericModule.forRoot(), // ... 50 | ], 51 | //... 52 | }) 53 | export class YourAppModule { 54 | } 55 | ``` 56 | --- 57 | ### How to use the @angularfy/autonumeric ? 58 | 59 | The AutoNumeric component can be instantiated the same way `AutoNumeric` can. 60 | 61 | After importing the AutonumericModule 62 | in your component, you can define your options as follow : 63 | ```ts 64 | this.myOptions = { 65 | digitGroupSeparator: '.', 66 | decimalCharacter: ',', 67 | decimalCharacterAlternative: '.', 68 | currencySymbol: '\u00a0€', 69 | currencySymbolPlacement: 's', 70 | roundingMethod: 'U', 71 | minimumValue: '0' 72 | } 73 | 74 | ``` 75 | in your HTML : 76 | ```html 77 | 78 | ``` 79 | 80 | or simply with a predefined option name: 81 | ```html 82 | 83 | ``` 84 | you can also use object literal as options directly in HTML 85 | 86 | ```html 87 | 96 | ``` 97 | The library supports also reactive forms. 98 | 99 | --- 100 | #### Customize autonumeric defaults 101 | 102 | You can override autonumeric default by providing default configuration: 103 | 104 | ```js 105 | import { AutonumericModule } from '@angularfy/autonumeric'; 106 | 107 | @NgModule({ 108 | //... 109 | imports: [ 110 | AutonumericModule.forRoot({ 111 | // user defaults here 112 | }), // ... 113 | ], 114 | //... 115 | }) 116 | export class YourAppModule { 117 | } 118 | ``` 119 | You can also use providers to achieve this : 120 | 121 | ```js 122 | const userDefaults :AutonumericOptions= { 123 | // default options 124 | } 125 | export function defaultsFactory(userDefaults: AutonumericOptions): AutonumericDefaults { 126 | const defaults: AutonumericDefaults = new AutonumericDefaults(); 127 | Object.assign(defaults, userDefaults); 128 | return defaults; 129 | } 130 | @NgModule({ 131 | imports: [AutonumericModule], 132 | providers: [{ 133 | provide: USER_DEFAULTS, 134 | useValue: userDefaults 135 | }, 136 | { 137 | provide: AutonumericDefaults, 138 | useFactory: defaultsFactory, 139 | deps: [USER_DEFAULTS] 140 | }] 141 | }) 142 | export class YourAppModule { 143 | } 144 | ``` 145 | 146 | #### Supported events 147 | 148 | Alongside with native events, autonumeric emits two events : formatted, rawValueModified, those events are bubbled from the 149 | native library. Please refer to official docs for more details 150 | 151 | ``` HTML 152 | 163 | 164 | ``` 165 | you can also use an input with {..., readOnly : true } in options. 166 | 167 | #### Styling 168 | we are agnostic about how the input should be styled. you can define your own style rules 169 | ```css 170 | input[autonumeric],span[autonumeric] { 171 | text-align:right; 172 | } 173 | 174 | ``` 175 | #### Integration with other scripts & events support 176 | 177 | If some reason you need to access the native autonumeric instance, you can reference your element using 178 | @ViewChild. 179 | ``` HTML 180 | 181 | ``` 182 | in your component : 183 | ``` ts 184 | @ViewChild('myNumericField', { static: true }) 185 | myNumericField:AutonumericDirective; 186 | 187 | ... 188 | reset(){ 189 | this.myNumericField.instance.reset(); 190 | } 191 | set(val:any){ 192 | this.myNumericField.instance.set(val); 193 | } 194 | 195 | ... 196 | ``` 197 | 198 | ### Demo 199 | 200 | The official AutoNumeric [documentation](http://autonumeric.org/#/guide) 201 | 202 | 203 | ### Requirements 204 | 205 | - [AutoNumeric](https://github.com/autoNumeric/autoNumeric) `^v4` 206 | - [Angular](https://angular.io/) `^v4` 207 | 208 | ### Browser support 209 | 210 | This supports the same browsers than AutoNumeric supports: 211 | - Firefox and 212 | - Chrome 213 | 214 | *(latest 2 versions)* 215 | If you use IE/Edge/Safari/Opera, this *might* work ;) 216 | 217 | ### What's next ? 218 | 219 | I will be working on supporting more AutoNumeric options. If you have any suggestions please feel free to reach by email bellow. 220 | 221 | ### Greetings 222 | 223 | This project is hugely inspired from [vue-js implementtation of AutoNumeric](https://github.com/autoNumeric/vue-autoNumeric) by [Alexandre Bonneau](https://github.com/AlexandreBonneau) 224 | 225 | 226 | ### Support 227 | 228 | As always, if you find this useful, please consider [supporting its development](https://www.patreon.com/ainouss)! 229 | Huge Thanks :) 230 | 231 | ### License 232 | 233 | `@angularfy/autonumeric` is open-source and released under the [MIT License] 234 | 235 | > PS: 236 | I would love to know how you're using @angularfy/autonumeric. 237 | Contact and tell me!, abdelghani@ainouss.fr :) 238 | 239 | -------------------------------------------------------------------------------- /angular-autonumeric.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json", 3 | "version": 1, 4 | "newProjectRoot": "", 5 | "projects": { 6 | "autonumeric": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "library", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-ng-packagr:build", 13 | "options": { 14 | "project": "src/ng-package.json" 15 | }, 16 | "configurations": { 17 | "production": { 18 | "tsConfig": "src/tsconfig.json", 19 | "project": "src/ng-package.prod.json" 20 | } 21 | } 22 | }, 23 | "test": { 24 | "builder": "@angular-devkit/build-angular:karma", 25 | "options": { 26 | "main": "src/test.ts", 27 | "tsConfig": "src/tsconfig.spec.json", 28 | "codeCoverageExclude": [ 29 | "src/test.ts", 30 | "src/test/**" 31 | ], 32 | "karmaConfig": "src/karma.conf.js" 33 | } 34 | }, 35 | "lint": { 36 | "builder": "@angular-devkit/build-angular:tslint", 37 | "options": { 38 | "tsConfig": [ 39 | "src/tsconfig.json" 40 | ], 41 | "exclude": [ 42 | "**/node_modules/**" 43 | ] 44 | } 45 | } 46 | } 47 | } 48 | }, 49 | "cli": { 50 | "packageManager": "yarn" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to autonumeric!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | })); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angularfy/autonumeric", 3 | "version": "3.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng-packagr -p package.json", 8 | "pack": "cd dist/autonumeric && npm pack", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "repository": { 13 | "url": "git://github.com/angularfy/autonumeric.git" 14 | }, 15 | "dependencies": {}, 16 | "devDependencies": { 17 | "@angular-devkit/build-angular": "~0.13.0", 18 | "@angular-devkit/build-ng-packagr": "~0.10.0", 19 | "@angular/cli": "~7.3.0", 20 | "@angular/compiler-cli": "~7.2.0", 21 | "@angular/language-service": "~7.2.0", 22 | "@angular/compiler": "7.2.15", 23 | "@types/jasmine": "~2.8.8", 24 | "@types/jasminewd2": "~2.0.3", 25 | "@types/node": "~8.9.4", 26 | "autonumeric": "^4.0.0", 27 | "codelyzer": "~4.5.0", 28 | "jasmine-core": "~2.99.1", 29 | "jasmine-spec-reporter": "~4.2.1", 30 | "karma": "~3.1.1", 31 | "karma-chrome-launcher": "~2.2.0", 32 | "karma-coverage-istanbul-reporter": "~2.0.1", 33 | "karma-jasmine": "~1.1.2", 34 | "karma-jasmine-html-reporter": "^0.2.2", 35 | "ng-packagr": "^4.2.0", 36 | "protractor": "~5.4.0", 37 | "ts-node": "~7.0.0", 38 | "tsickle": "^0.34.2", 39 | "tslint": "~5.11.0", 40 | "typescript": "~3.2.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ## @angularfy/autonumeric 2 | 3 | An Angular library that wraps the awesome [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) library 4 | 5 | Get in touch on autonumeric@angularfy.com 6 | 7 | --- 8 | 9 | @angularfy/autonumeric supports most of [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) options. 10 | 11 | **Checkout the [demo](https://angularfy-autonumeric.stackblitz.io)** 12 | 13 | _Note: In order to minimize the size of the @angularfy/autonumeric, the AutoNumeric library dependency **is not** bundled with it._ 14 | 15 | This means you **need** to include the [AutoNumeric](https://github.com/autoNumeric/autoNumeric/) library. 16 | 17 | ## Dependencies 18 | 19 | The dependency is [autoNumeric 4](https://github.com/autoNumeric/autoNumeric/). 20 | Here is the list of minimal required versions: 21 | 22 | | @angularfy/autonumeric | angular | autoNumeric | 23 | | ---------------------- | ------- | ----------- | 24 | | 1.x.x | ^4.0.0 | ^4.0.0 | 25 | | 2.x.x | ^4.0.0 | ^4.0.0 | 26 | | 3.x.x | ^4.0.0 | ^4.0.0 | 27 | 28 | --- 29 | 30 | ## Installation 31 | 32 | After installing the above dependencies, install `@angularfy/autonumeric` via npm: 33 | 34 | ```shell 35 | npm install --save @angularfy/autonumeric 36 | ``` 37 | 38 | or yarn : 39 | 40 | ```shell 41 | yarn add @angularfy/autonumeric 42 | ``` 43 | 44 | Once installed you need to import our main module: 45 | 46 | ```js 47 | import { AutonumericModule } from "@angularfy/autonumeric"; 48 | 49 | @NgModule({ 50 | //... 51 | imports: [ 52 | AutonumericModule.forRoot(), // ... 53 | ], 54 | //... 55 | }) 56 | export class YourAppModule {} 57 | ``` 58 | 59 | --- 60 | 61 | ### How to use the @angularfy/autonumeric ? 62 | 63 | The AutoNumeric component can be instantiated the same way `AutoNumeric` can. 64 | 65 | After importing the AutonumericModule 66 | in your component, you can define your options as follow : 67 | 68 | ```ts 69 | this.myOptions = { 70 | digitGroupSeparator: ".", 71 | decimalCharacter: ",", 72 | decimalCharacterAlternative: ".", 73 | currencySymbol: "\u00a0€", 74 | currencySymbolPlacement: "s", 75 | roundingMethod: "U", 76 | minimumValue: "0", 77 | }; 78 | ``` 79 | 80 | in your HTML : 81 | 82 | ```html 83 | 84 | ``` 85 | 86 | or simply with a predefined option name: 87 | 88 | ```html 89 | 90 | ``` 91 | 92 | you can also use object literal as options directly in HTML 93 | 94 | ```html 95 | 108 | ``` 109 | 110 | The library supports also reactive forms. 111 | 112 | --- 113 | 114 | #### Customize autonumeric defaults 115 | 116 | You can override autonumeric default by providing default configuration: 117 | 118 | ```js 119 | import { AutonumericModule } from "@angularfy/autonumeric"; 120 | 121 | @NgModule({ 122 | //... 123 | imports: [ 124 | AutonumericModule.forRoot({ 125 | // user defaults here 126 | }), // ... 127 | ], 128 | //... 129 | }) 130 | export class YourAppModule {} 131 | ``` 132 | 133 | You can also use providers to achieve this : 134 | 135 | ```js 136 | const userDefaults: AutonumericOptions = { 137 | // default options 138 | }; 139 | export function defaultsFactory( 140 | userDefaults: AutonumericOptions 141 | ): AutonumericDefaults { 142 | const defaults: AutonumericDefaults = new AutonumericDefaults(); 143 | Object.assign(defaults, userDefaults); 144 | return defaults; 145 | } 146 | @NgModule({ 147 | imports: [AutonumericModule], 148 | providers: [ 149 | { 150 | provide: USER_DEFAULTS, 151 | useValue: userDefaults, 152 | }, 153 | { 154 | provide: AutonumericDefaults, 155 | useFactory: defaultsFactory, 156 | deps: [USER_DEFAULTS], 157 | }, 158 | ], 159 | }) 160 | export class YourAppModule {} 161 | ``` 162 | 163 | #### Supported events 164 | 165 | Alongside with native events, autonumeric emits two events : formatted, rawValueModified, those events are bubbled from the 166 | native library. Please refer to official docs for more details 167 | 168 | ```HTML 169 | 182 | 183 | ``` 184 | 185 | you can also use an input with {..., readOnly : true } in options. 186 | 187 | #### Styling 188 | 189 | we are agnostic about how the input should be styled. you can define your own style rules 190 | 191 | ```css 192 | input[autonumeric], 193 | span[autonumeric] { 194 | text-align: right; 195 | } 196 | ``` 197 | 198 | #### Integration with other scripts & events support 199 | 200 | If some reason you need to access the native autonumeric instance, you can reference your element using 201 | @ViewChild. 202 | 203 | ```HTML 204 | 205 | ``` 206 | 207 | in your component : 208 | 209 | ```ts 210 | @ViewChild('myNumericField', { static: true }) 211 | myNumericField:AutonumericDirective; 212 | 213 | ... 214 | reset(){ 215 | this.myNumericField.instance.reset(); 216 | } 217 | set(val:any){ 218 | this.myNumericField.instance.set(val); 219 | } 220 | 221 | ... 222 | ``` 223 | 224 | ### Demo 225 | 226 | The official AutoNumeric [documentation](http://autonumeric.org/#/guide) 227 | 228 | ### Requirements 229 | 230 | - [AutoNumeric](https://github.com/autoNumeric/autoNumeric) `^v4` 231 | - [Angular](https://angular.io/) `^v4` 232 | 233 | ### Browser support 234 | 235 | This supports the same browsers than AutoNumeric supports: 236 | 237 | - Firefox and 238 | - Chrome 239 | 240 | _(latest 2 versions)_ 241 | If you use IE/Edge/Safari/Opera, this _might_ work ;) 242 | 243 | ### What's next ? 244 | 245 | I will be working on supporting more AutoNumeric options. If you have any suggestions please feel free to reach by email bellow. 246 | 247 | ### Greetings 248 | 249 | This project is hugely inspired from [vue-js implementtation of AutoNumeric](https://github.com/autoNumeric/vue-autoNumeric) by [Alexandre Bonneau](https://github.com/AlexandreBonneau) 250 | 251 | ### Support 252 | 253 | As always, if you find this useful, please consider [supporting its development](https://www.patreon.com/ainouss)! 254 | Huge Thanks :) 255 | 256 | ### License 257 | 258 | `@angularfy/autonumeric` is open-source and released under the [MIT License] 259 | 260 | > PS: 261 | > I would love to know how you're using @angularfy/autonumeric. 262 | > Contact and tell me!, abdelghani@ainouss.fr :) 263 | -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/index'; 2 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage/autonumeric'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /src/lib/autonumeric-defaults.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from "@angular/core"; 2 | import {AutonumericOptions} from "./autonumeric.model"; 3 | import AutoNumeric from 'autonumeric'; 4 | @Injectable() 5 | export class AutonumericDefaults implements AutonumericOptions { 6 | allowDecimalPadding = AutoNumeric.options.allowDecimalPadding.always; 7 | alwaysAllowDecimalCharacter = AutoNumeric.options.alwaysAllowDecimalCharacter.doNotAllow; 8 | caretPositionOnFocus = AutoNumeric.options.caretPositionOnFocus.doNoForceCaretPosition; 9 | createLocalList = AutoNumeric.options.createLocalList.createList; 10 | currencySymbol = AutoNumeric.options.currencySymbol.none; 11 | currencySymbolPlacement = AutoNumeric.options.currencySymbolPlacement.prefix; 12 | decimalCharacter = AutoNumeric.options.decimalCharacter.dot; 13 | decimalCharacterAlternative = AutoNumeric.options.decimalCharacterAlternative.none; 14 | decimalPlaces = AutoNumeric.options.decimalPlaces.two; 15 | decimalPlacesRawValue = AutoNumeric.options.decimalPlacesRawValue.useDefault; 16 | decimalPlacesShownOnBlur = AutoNumeric.options.decimalPlacesShownOnBlur.useDefault; 17 | decimalPlacesShownOnFocus = AutoNumeric.options.decimalPlacesShownOnFocus.useDefault; 18 | defaultValueOverride = AutoNumeric.options.defaultValueOverride.doNotOverride; 19 | digitalGroupSpacing = AutoNumeric.options.digitalGroupSpacing.three; 20 | digitGroupSeparator = AutoNumeric.options.digitGroupSeparator.comma; 21 | divisorWhenUnfocused = AutoNumeric.options.divisorWhenUnfocused.none; 22 | emptyInputBehavior = AutoNumeric.options.emptyInputBehavior.focus; 23 | eventBubbles = AutoNumeric.options.eventBubbles.bubbles; 24 | eventIsCancelable = AutoNumeric.options.eventIsCancelable.isCancelable; 25 | failOnUnknownOption = AutoNumeric.options.failOnUnknownOption.ignore; 26 | formatOnPageLoad = AutoNumeric.options.formatOnPageLoad.format; 27 | formulaMode = AutoNumeric.options.formulaMode.disabled; 28 | historySize = AutoNumeric.options.historySize.medium; 29 | isCancellable = AutoNumeric.options.isCancellable.cancellable; 30 | leadingZero = AutoNumeric.options.leadingZero.deny; 31 | maximumValue = AutoNumeric.options.maximumValue.tenTrillions; 32 | minimumValue = AutoNumeric.options.minimumValue.tenTrillions; 33 | modifyValueOnWheel = AutoNumeric.options.modifyValueOnWheel.modifyValue; 34 | negativeBracketsTypeOnBlur = AutoNumeric.options.negativeBracketsTypeOnBlur.none; 35 | negativePositiveSignPlacement = AutoNumeric.options.negativePositiveSignPlacement.none; 36 | negativeSignCharacter = AutoNumeric.options.negativeSignCharacter.hyphen; 37 | noEventListeners = AutoNumeric.options.noEventListeners.addEvents; 38 | onInvalidPaste = AutoNumeric.options.onInvalidPaste.error; 39 | outputFormat = AutoNumeric.options.outputFormat.none; 40 | overrideMinMaxLimits = AutoNumeric.options.overrideMinMaxLimits.doNotOverride; 41 | positiveSignCharacter = AutoNumeric.options.positiveSignCharacter.plus; 42 | rawValueDivisor = AutoNumeric.options.rawValueDivisor.none; 43 | readOnly = AutoNumeric.options.readOnly.readWrite; 44 | roundingMethod = AutoNumeric.options.roundingMethod.halfUpSymmetric; 45 | saveValueToSessionStorage = AutoNumeric.options.saveValueToSessionStorage.doNotSave; 46 | selectNumberOnly = AutoNumeric.options.selectNumberOnly.selectNumbersOnly; 47 | selectOnFocus = AutoNumeric.options.selectOnFocus.select; 48 | serializeSpaces = AutoNumeric.options.serializeSpaces.plus; 49 | showOnlyNumbersOnFocus = AutoNumeric.options.showOnlyNumbersOnFocus.showAll; 50 | showPositiveSign = AutoNumeric.options.showPositiveSign.hide; 51 | showWarnings = AutoNumeric.options.showWarnings.show; 52 | styleRules = AutoNumeric.options.styleRules.none; 53 | suffixText = AutoNumeric.options.suffixText.none; 54 | symbolWhenUnfocused = AutoNumeric.options.symbolWhenUnfocused.none; 55 | unformatOnHover = AutoNumeric.options.unformatOnHover.unformat; 56 | unformatOnSubmit = AutoNumeric.options.unformatOnSubmit.keepCurrentValue; 57 | valuesToStrings = AutoNumeric.options.valuesToStrings.none; 58 | watchExternalChanges = AutoNumeric.options.watchExternalChanges.doNotWatch; 59 | wheelOn = AutoNumeric.options.wheelOn.focus; 60 | wheelStep = AutoNumeric.options.wheelStep.progressive; 61 | } 62 | -------------------------------------------------------------------------------- /src/lib/autonumeric.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Directive, 4 | ElementRef, 5 | EventEmitter, 6 | forwardRef, 7 | Input, 8 | OnChanges, 9 | OnDestroy, 10 | OnInit, 11 | Output, 12 | Renderer2, 13 | SimpleChanges, 14 | } from '@angular/core'; 15 | import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; 16 | import AutoNumeric from 'autonumeric'; 17 | import {AutonumericDefaults} from './autonumeric-defaults.service'; 18 | import {AutonumericOptions} from './autonumeric.model'; 19 | 20 | export const AUTONUMERIC_CONTROL_VALUE_ACCESSOR: any = { 21 | provide: NG_VALUE_ACCESSOR, 22 | useExisting: forwardRef(() => AutonumericDirective), 23 | multi: true 24 | }; 25 | 26 | @Directive({ 27 | selector: '[autonumeric]', 28 | providers: [AUTONUMERIC_CONTROL_VALUE_ACCESSOR], 29 | // tslint:disable-next-line:use-host-property-decorator 30 | host: { 31 | '(blur)': 'onTouchedFn()' 32 | }, 33 | }) 34 | export class AutonumericDirective implements OnInit, AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor { 35 | 36 | /* Defines the strategy to apply when options change. 37 | * reset will drop any previous options 38 | * update will change old values one by one but keep those that are not mentioned in the new options 39 | */ 40 | @Input() 41 | strategy: 'reset' | 'update' = 'reset'; 42 | 43 | @Input() 44 | options: AutonumericOptions; 45 | @Input() 46 | predefined: string; 47 | instance: any; 48 | private isDisabled = false; 49 | private initialValue: any; 50 | unsubscribeFormat: () => void; 51 | unsubscribeRawValueModified: () => void; 52 | @Output() 53 | formatted = new EventEmitter(); 54 | @Output() 55 | rawValueModified = new EventEmitter(); 56 | onChangeFn: (value: any) => void = () => { 57 | }; 58 | 59 | onTouchedFn = () => { 60 | }; 61 | 62 | constructor( 63 | private elm: ElementRef, 64 | private defaults: AutonumericDefaults, 65 | private renderer: Renderer2, 66 | ) { 67 | } 68 | 69 | ngOnInit(): void { 70 | } 71 | 72 | private normalize(options: any) { 73 | const normalized = {}; 74 | Object.keys(AutoNumeric.options).forEach(key => { 75 | if (typeof options[key] === 'undefined') { 76 | normalized[key] = (this.defaults as any)[key]; 77 | } else { 78 | normalized[key] = options[key]; 79 | } 80 | }); 81 | return normalized; 82 | } 83 | 84 | ngAfterViewInit(): void { 85 | this.instance = new AutoNumeric( 86 | this.elm.nativeElement, 87 | this.getOptions() 88 | ); 89 | this.setDisabledState(this.isDisabled); 90 | this.unsubscribeFormat = this.renderer.listen(this.elm.nativeElement, 'autoNumeric:formatted', ($event) => { 91 | this.formatted.emit($event); 92 | }); 93 | this.unsubscribeRawValueModified = this.renderer.listen(this.elm.nativeElement, 'autoNumeric:rawValueModified', ($event) => { 94 | this.onChangeFn($event.detail.newRawValue); 95 | this.rawValueModified.emit($event); 96 | }); 97 | } 98 | 99 | private getOptions() { 100 | if (this.options === undefined && this.predefined === undefined) { 101 | return this.defaults; 102 | } 103 | if (this.options !== undefined && this.predefined !== undefined) { 104 | throw new Error('predefined attribute could not be combined with options. Please use either predefined or options'); 105 | } 106 | if (this.options !== undefined) { 107 | return this.normalize(this.options); 108 | } 109 | const predefined = AutoNumeric.getPredefinedOptions()[this.predefined]; 110 | return this.normalize(predefined); 111 | } 112 | 113 | ngOnChanges(changes: SimpleChanges): void { 114 | if (!this.instance) { 115 | return; 116 | } 117 | if (changes.options || changes.predefined) { 118 | if (this.strategy === 'reset') { 119 | this.instance.options.reset(); 120 | } 121 | this.instance.update(this.getOptions()); 122 | } 123 | } 124 | 125 | ngOnDestroy(): void { 126 | this.unsubscribeFormat(); 127 | this.unsubscribeRawValueModified(); 128 | try { 129 | this.instance.remove(); // remove listeners 130 | } catch (e) { 131 | } 132 | } 133 | 134 | writeValue(value: any): void { 135 | if (this.instance) { 136 | this.instance.set(value); 137 | } else { 138 | // autonumeric hasn't been initialised yet, store the value for later use 139 | this.initialValue = value; 140 | } 141 | } 142 | 143 | registerOnChange(fn: any): void { 144 | this.onChangeFn = fn; 145 | } 146 | 147 | registerOnTouched(fn: () => void): void { 148 | this.onTouchedFn = fn; 149 | } 150 | 151 | setDisabledState(isDisabled: boolean): void { 152 | this.isDisabled = isDisabled; 153 | if (this.instance) { 154 | if (this.isDisabled) { 155 | this.renderer.setProperty(this.instance.domElement, 'disabled', 'disabled'); 156 | } else { 157 | this.renderer.removeAttribute(this.instance.domElement, 'disabled'); 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/lib/autonumeric.model.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:max-line-length 2 | export interface AutonumericOptions { 3 | /* Defines if the decimal places should be padded with zeroes 4 | * `true` : always pad decimals with zeros (ie. '12.3400') 5 | * `false` : never pad with zeros (ie. '12.34') 6 | * `'floats'` : pad with zeroes only when there are decimals (ie. '12' and '12.3400') 7 | * Note: setting allowDecimalPadding to 'false' will override the 'decimalPlaces' setting. 8 | */ 9 | allowDecimalPadding?: boolean | string; 10 | 11 | /* Defines if the decimal character or decimal character alternative should be accepted when there is already 12 | * a decimal character shown in the element. 13 | * If set to `true`| any decimal character input will be accepted and will subsequently modify the decimal character position| 14 | * as well as the `rawValue`. 15 | * If set to `false`| the decimal character and its alternative key will be dropped as before. This is the default setting. 16 | */ 17 | alwaysAllowDecimalCharacter?: boolean; 18 | 19 | /* Defines where should be positioned the caret on focus 20 | * null : Do not enforce any caret positioning on focus (this is needed when using `selectOnFocus`) 21 | * `'start'` : put the caret of the far left side of the value (excluding the positive/negative sign and currency symbol| if any) 22 | * `'end'` : put the caret of the far right side of the value (excluding the positive/negative sign and currency symbol| if any) 23 | * `'decimalLeft'` : put the caret of the left of the decimal character if any 24 | * `'decimalRight'` : put the caret of the right of the decimal character if any 25 | */ 26 | caretPositionOnFocus?: string | null; 27 | 28 | /* Defines if a local list of AutoNumeric objects should be kept when initializing this object. 29 | * This list is used by the `global.*` functions. 30 | */ 31 | createLocalList?: boolean; 32 | 33 | /* Defines the currency symbol string. 34 | * It can be a string of more than one character (allowing for instance to use a space on either side of it| example: '$ ' or ' $') 35 | * cf. https://en.wikipedia.org/wiki/Currency_symbol 36 | */ 37 | currencySymbol?: string; 38 | 39 | 40 | /* Defines where the currency symbol should be placed (before of after the numbers) 41 | * for prefix currencySymbolPlacement: "p" (default) 42 | * for suffix currencySymbolPlacement: "s" 43 | */ 44 | currencySymbolPlacement?: string; 45 | 46 | /* Defines what decimal separator character is used 47 | */ 48 | decimalCharacter?: string; 49 | 50 | /* Allow to declare an alternative decimal separator which is automatically replaced by `decimalCharacter` when typed. 51 | * This is used by countries that use a comma '|' as the decimal character and have keyboards with a numeric pads that have 52 | * a period 'full stop' as the decimal character (France or Spain for instance). 53 | */ 54 | decimalCharacterAlternative?: null | string; 55 | 56 | /* Defines the default number of decimal places to show on the formatted value| and keep for the precision. 57 | * Incidentally| since we need to be able to show that many decimal places| this also defines the raw value precision by default. 58 | */ 59 | decimalPlaces?: number; 60 | /* Defines how many decimal places should be kept for the raw value (ie. This is the precision for float values). 61 | * 62 | * If this option is set to `null` (which is the default)| then the value of `decimalPlaces` is used for `decimalPlacesRawValue` as well. 63 | * Note: Setting this to a lower number of decimal places than the one to be shown will lead to confusion for the users. 64 | */ 65 | decimalPlacesRawValue?: null | number; 66 | 67 | /* Defines how many decimal places should be visible when the element is unfocused. 68 | * If this is set to `null`| then this option is ignored| and the `decimalPlaces` option value will be used instead. 69 | * This means this is optional ; if omitted the decimal places will be the same when the input has the focus. 70 | * 71 | * This option can be used in conjonction with the two other `scale*` options| which allows to display 72 | a different formatted value when the element is unfocused| while another formatted value is shown when focused. 73 | */ 74 | decimalPlacesShownOnBlur?: null | number; 75 | 76 | /* Defines how many decimal places should be visible when the element has the focus. 77 | * If this is set to `null`| then this option is ignored| and the `decimalPlaces` option value will be used instead. 78 | * 79 | * Example: 80 | * For instance if `decimalPlacesShownOnFocus` is set to `5` and the default number of decimal places is `2`| then on focus `1|000.12345` 81 | * will be shown| while without focus `1|000.12` will be set back. 82 | * Note 1: the results depends on the rounding method used. 83 | * Note 2: the `getNumericString()` method returns the extended decimal places 84 | */ 85 | decimalPlacesShownOnFocus?: null | number; 86 | 87 | /* Helper option for ASP.NET postback 88 | * This should be set as the value of the unformatted default value 89 | * examples: 90 | * no default value="" {defaultValueOverride: ""} 91 | * value=1234.56 {defaultValueOverride: '1234.56'} 92 | */ 93 | defaultValueOverride?: null; 94 | 95 | /* Defines how many numbers should be grouped together (usually for the thousand separator) 96 | * - "2"| results in 99|99|99|999 India's lakhs 97 | * - "2s"| results in 99|999|99|99|999 India's lakhs scaled 98 | * - "3"| results in 999|999|999 (default) 99 | * - "4"| results in 9999|9999|9999 used in some Asian countries 100 | * Note: This option does not accept other grouping choice. 101 | */ 102 | digitalGroupSpacing?: string | number; 103 | 104 | /* Defines the thousand grouping separator character 105 | * Example : If `'.'` is set| then you'll get `'1.234.567'` 106 | */ 107 | digitGroupSeparator?: string; 108 | 109 | /* The `divisorWhenUnfocused` divide the element value on focus. 110 | * On blur| the element value is multiplied back. 111 | * Example : Display percentages using { divisorWhenUnfocused: 100 } (or directly in the Html with 112 | *``) 113 | * The divisor value does not need to be an integer| but please understand that Javascript has limited accuracy in math; 114 | use with caution. 115 | * Note: The `getNumericString` method returns the full value| including the 'hidden' decimals. 116 | */ 117 | divisorWhenUnfocused?: null | number; 118 | 119 | /* Defines what should be displayed in the element if the raw value is an empty string (''). 120 | * - 'focus' : The currency sign is displayed when the input receives focus (default) 121 | * - 'press' : The currency sign is displayed whenever a key is being pressed 122 | * - 'always' : The currency sign is always displayed 123 | * - 'zero' : A zero is displayed ('rounded' with or without a currency sign) if the input has no value on focus out 124 | * - 'min' : The minimum value is displayed if the input has no value on focus out 125 | * - 'max' : The maximum value is displayed if the input has no value on focus out 126 | * - 'null' : When the element is empty| the `rawValue` and the element value/text is set to `null`. This also allows to set 127 | * the value to `null` using `anElement.set(null)`. 128 | */ 129 | emptyInputBehavior?: string; 130 | 131 | /* Defines if the custom and native events triggered by AutoNumeric should bubble up or not. 132 | */ 133 | eventBubbles?: boolean; 134 | 135 | /* Defines if the custom and native events triggered by AutoNumeric should be cancelable. 136 | */ 137 | eventIsCancelable?: boolean; 138 | 139 | /* This option is the 'strict mode' (aka 'debug' mode)| which allows autoNumeric to strictly analyse the options passed| 140 | * and fails if an unknown options is used in the settings object. 141 | * You should set that to `true` if you want to make sure you are only using 'pure' autoNumeric settings objects in your code. 142 | * If you see uncaught errors in the console and your code starts to fail| this means somehow those options gets polluted by 143 | * another program (which usually happens when using frameworks). 144 | */ 145 | failOnUnknownOption?: boolean; 146 | 147 | /* Determine if the default value will be formatted on initialization. 148 | */ 149 | formatOnPageLoad?: boolean; 150 | 151 | /* Defines if the 'formula mode' can be activated by the user. 152 | * If set to `true`| then the user can enter the formula mode by entering the '=' character. 153 | * He will then be allowed to enter any simple math formula using numeric characters as well as 154 | * the following operators +| -| *| /| ( and ). 155 | * The formula mode is closed when the user either validate their math expression using the `Enter` key| or when the element is blurred. 156 | * If the formula is invalid| the previous valid `rawValue` is set back| and the `autoNumeric:invalidFormula` event is sent. 157 | * When a valid formula is accepted| then its result is `set()`| and the `autoNumeric:validFormula` event is sent. 158 | * 159 | * By default| this mode is disabled. 160 | */ 161 | formulaMode?: boolean; 162 | 163 | /* Set the undo/redo history table size. 164 | * Each record keeps the raw value as well and the last known caret/selection positions. 165 | */ 166 | historySize?: number; 167 | /* Allow the user to 'cancel' and undo the changes he made to the given autonumeric-managed element| by pressing the 'Escape' key. 168 | * Whenever the user 'validate' the input (either by hitting 'Enter'| or blurring the element)| the new value is saved for subsequent 169 | * 'cancellation'. 170 | * The process : 171 | * - save the input value on focus 172 | * - if the user change the input value| and hit `Escape`| then the initial value saved on focus is set back 173 | * - on the other hand if the user either have used `Enter` to validate (`Enter` throws a change event) his entries| 174 | * or if the input value has been changed by another script in the mean time| then we save the new input value 175 | * - on a successful 'cancel'| select the whole value (while respecting the `selectNumberOnly` option) 176 | * - bonus; if the value has not changed| hitting 'Esc' just select all the input value (while respecting the `selectNumberOnly` option) 177 | */ 178 | isCancellable?: boolean; 179 | 180 | /* Controls the leading zero behavior 181 | * - 'allow' : allows leading zeros to be entered. Zeros will be truncated when entering additional digits. 182 | * On focusout zeros will be deleted 183 | * - 'deny' : allows only one leading zero on values that are between 1 and -1 184 | * - 'keep' : allows leading zeros to be entered. on focusout zeros will be retained 185 | */ 186 | leadingZero?: string; 187 | 188 | /* Defines the maximum possible value a user can enter. 189 | * Notes: 190 | * - this value must be a string and use the period for the decimal point 191 | * - this value needs to be larger than `minimumValue` 192 | */ 193 | maximumValue?: string | number; 194 | 195 | /* Defines the minimum possible value a user can enter. 196 | * Notes: 197 | * - this value must be a string and use the period for the decimal point 198 | * - this value needs to be smaller than `maximumValue` 199 | * - if this is superior to 0| then you'll effectively prevent your user to entirely delete the content of your element 200 | */ 201 | minimumValue?: string | number; 202 | 203 | /* Allows the user to increment or decrement the element value with the mouse wheel. 204 | * The wheel behavior can be modified by the `wheelStep` option. 205 | * This `wheelStep` option can be used in two ways| either by setting: 206 | * - a 'fixed' step value (`wheelStep : 1000`)| or 207 | * - the 'progressive' string (`wheelStep : 'progressive'`)| which will then activate a special mode where the step is automatically 208 | * calculated based on the element value size. 209 | * 210 | * Note : 211 | * You can activate/deactivate the wheel event for each `wheelOn` option value by using the 'Shift' modifier key while using 212 | * the mouse wheel. 213 | */ 214 | modifyValueOnWheel?: boolean; 215 | 216 | /* Adds brackets on negative values (ie. transforms '-$ 999.99' to '($999.99)') 217 | * Those brackets are visible only when the field does NOT have the focus. 218 | * The left and right symbols should be enclosed in quotes and separated by a comma. 219 | */ 220 | negativeBracketsTypeOnBlur?: string | null; 221 | 222 | /* Placement of the negative/positive sign relative to the `currencySymbol` option. 223 | * 224 | * Example: 225 | * // Default values 226 | * -1|234.56 => default no options required 227 | * $-1|234.56 => {currencySymbol: "$"| negativePositiveSignPlacement: "r"} // Default if negativePositiveSignPlacement is 228 | * 'null' and currencySymbol is not empty 229 | * 230 | * // Sign on the left hand side of the whole number 231 | * -$1|234.56 => {currencySymbol: "$"} or {currencySymbol: "$"| negativePositiveSignPlacement: "l"} 232 | * -1|234.56$ => {currencySymbol: "$"| currencySymbolPlacement: "s"| negativePositiveSignPlacement: "p"} 233 | * // Default if negativePositiveSignPlacement is 'null' and currencySymbol is not empty 234 | * 235 | * // Sign on the right hand side of the whole number 236 | * 1|234.56- => {negativePositiveSignPlacement: "s"} 237 | * $1|234.56- => {currencySymbol: "$"| negativePositiveSignPlacement: "s"} 238 | * 1|234.56-$ => {currencySymbol: "$"| currencySymbolPlacement: "s"} 239 | * 1|234.56$- => {currencySymbol: "$"| currencySymbolPlacement: "s"| negativePositiveSignPlacement: "r"} 240 | */ 241 | negativePositiveSignPlacement?: string | null; 242 | 243 | /* Defines the negative sign symbol. 244 | * It can be a string of only one character. 245 | */ 246 | negativeSignCharacter?: string; 247 | 248 | /* Defines if the element should have event listeners activated on it. 249 | * By default| those event listeners are only added to elements and html element with the `contenteditable` 250 | * attribute set to `true`| but not on the other html tags. 251 | * This allows to initialize elements without any event listeners. 252 | * Warning: Since AutoNumeric will not check the input content after its initialization| using some autoNumeric 253 | * methods afterwards *will* probably leads to formatting problems. 254 | */ 255 | noEventListeners?: boolean; 256 | 257 | /* Manage how autoNumeric react when the user tries to paste an invalid number. 258 | * - 'error' : (This is the default behavior) The input value is not changed and an error is output in the console. 259 | * - 'ignore' : idem than 'error'| but fail silently without outputting any error/warning in the console. 260 | * - 'clamp' : if the pasted value is either too small or too big regarding the minimumValue and maximumValue range| 261 | * then the result is clamped to those limits. 262 | * - 'truncate' : autoNumeric will insert as many pasted numbers it can at the initial caret/selection| until everything is pasted| 263 | * or the range limit is hit. 264 | * The non-pasted numbers are dropped and therefore not used at all. 265 | * - 'replace' : autoNumeric will first insert as many pasted numbers it can at the initial caret/selection| then if the range limit 266 | * is hit| it will try 267 | * to replace one by one the remaining initial numbers (on the right side of the caret) with the rest of the pasted 268 | * numbers. 269 | * 270 | * Note 1 : A paste content starting with a negative sign '-' will be accepted anywhere in the input| and will set the resulting value 271 | * as a negative number 272 | * Note 2 : A paste content starting with a number will be accepted| even if the rest is gibberish (ie. '123foobar456'). 273 | * Only the first number will be used (here '123'). 274 | * Note 3 : The paste event works with the `decimalPlacesShownOnFocus` option too. 275 | */ 276 | onInvalidPaste?: string; 277 | 278 | /* Defines how the value should be formatted when wanting a 'localized' version of it. 279 | * - null or 'string' => 'nnnn.nn' or '-nnnn.nn' as text type. This is the default behavior. 280 | * - 'number' => nnnn.nn or -nnnn.nn as a Number (Warning: this works only for integers inferior to Number.MAX_SAFE_INTEGER) 281 | * - '|' or '-|' => 'nnnn|nn' or '-nnnn|nn' 282 | * - '.-' => 'nnnn.nn' or 'nnnn.nn-' 283 | * - '|-' => 'nnnn|nn' or 'nnnn|nn-' 284 | * 285 | * Note: The hyphen '-' is translated to the custom negative sign defined in `negativeSignCharacter` 286 | */ 287 | outputFormat?: string | null; 288 | /* Override the minimum and maximum limits 289 | * overrideMinMaxLimits: "ceiling" adheres to maximumValue and ignores minimumValue settings 290 | * overrideMinMaxLimits: "floor" adheres to minimumValue and ignores maximumValue settings 291 | * overrideMinMaxLimits: "ignore" ignores both minimumValue & maximumValue 292 | */ 293 | overrideMinMaxLimits?: string | null; 294 | /* Defines the positive sign symbol. 295 | * It can be a string of only one character. 296 | * This is shown only if `showPositiveSign` is set to `true`. 297 | */ 298 | positiveSignCharacter?: string | null; 299 | 300 | /* The `rawValueDivisor` divides the formatted value shown in the AutoNumeric element and store the result in `rawValue`. 301 | * @example { rawValueDivisor: '100' } or 302 | * Given the `0.01234` raw value| the formatted value will be displayed as `'1.234'`. 303 | * This is useful when displaying percentage for instance| and avoid the need to divide/multiply by 100 304 | * between the number shown and the raw value. 305 | */ 306 | rawValueDivisor?: null | number; 307 | 308 | /* Defines if the element (`` or another allowed html tag) should be set as read-only on initialization. 309 | * When set to `true`| then: 310 | * - the `readonly` html property is added to the element on initialization| or 311 | * - the `contenteditable` attribute is set to `false` on non-input elements. 312 | */ 313 | readOnly?: boolean; 314 | 315 | /* Defines the rounding method to use. 316 | * roundingMethod: "S"| Round-Half-Up Symmetric (default) 317 | * roundingMethod: "A"| Round-Half-Up Asymmetric 318 | * roundingMethod: "s"| Round-Half-Down Symmetric (lower case s) 319 | * roundingMethod: "a"| Round-Half-Down Asymmetric (lower case a) 320 | * roundingMethod: "B"| Round-Half-Even "Bankers Rounding" 321 | * roundingMethod: "U"| Round Up "Round-Away-From-Zero" 322 | * roundingMethod: "D"| Round Down "Round-Toward-Zero" - same as truncate 323 | * roundingMethod: "C"| Round to Ceiling "Toward Positive Infinity" 324 | * roundingMethod: "F"| Round to Floor "Toward Negative Infinity" 325 | * roundingMethod: "N05" Rounds to the nearest .05 => same as "CHF" used in 1.9X and still valid 326 | * roundingMethod: "U05" Rounds up to next .05 327 | * roundingMethod: "D05" Rounds down to next .05 328 | */ 329 | roundingMethod?: string; 330 | /* Set to `true` to allow the `decimalPlacesShownOnFocus` value to be saved with sessionStorage 331 | * If IE 6 or 7 is detected| the value will be saved as a session cookie. 332 | */ 333 | saveValueToSessionStorage?: boolean; 334 | 335 | /* Determine if the select all keyboard command will select the complete input text| or only the input numeric value 336 | * Note : If the currency symbol is between the numeric value and the negative sign| only the numeric value will be selected 337 | */ 338 | selectNumberOnly?: boolean; 339 | 340 | /* Defines if the element value should be selected on focus. 341 | * Note: The selection is done using the `selectNumberOnly` option. 342 | */ 343 | selectOnFocus?: boolean; 344 | 345 | /* Defines how the serialize functions should treat the spaces. 346 | * Those spaces ' ' can either be converted to the plus sign '+'| which is the default| or to '%20'. 347 | * Both values being valid per the spec (http://www.w3.org/Addressing/URL/uri-spec.html). 348 | * Also see the summed up answer on http://stackoverflow.com/a/33939287. 349 | * 350 | * tl;dr : Spaces should be converted to '%20' before the '?' sign| then converted to '+' after. 351 | * In our case since we serialize the query| we use '+' as the default (but allow the user to get back the old *wrong* behavior). 352 | */ 353 | serializeSpaces?: string; 354 | /* Defines if the element value should be converted to the raw value on focus (and back to the formatted on blur). 355 | * If set to `true`| then autoNumeric remove the thousand separator| currency symbol and suffix on focus. 356 | * Example: 357 | * If the input value is '$ 1|999.88 suffix'| on focus it becomes '1999.88' and back to '$ 1|999.88 suffix' on blur. 358 | */ 359 | showOnlyNumbersOnFocus?: boolean; 360 | 361 | /* Allow the positive sign symbol `+` to be displayed for positive numbers. 362 | * By default| this positive sign is not shown. 363 | * The sign placement is controlled by the 'negativePositiveSignPlacement' option| mimicking the negative sign placement rules. 364 | */ 365 | showPositiveSign?: boolean; 366 | 367 | /* Defines if warnings should be shown in the console. 368 | * Those warnings can be ignored| but are usually printed when something could be improved by the user (ie. option conflicts). 369 | */ 370 | showWarnings?: boolean; 371 | 372 | /* Defines the rules that calculate the CSS class(es) to apply on the element| based on the raw unformatted value. 373 | * This can also be used to call callbacks whenever the `rawValue` is updated. 374 | * Important: all callbacks must return `null` if no ranges/userDefined classes are selected 375 | * @example 376 | * { 377 | * positive : 'autoNumeric-positive'| // Or `null` to not use it 378 | * negative : 'autoNumeric-negative'| 379 | * ranges : [ 380 | * { min: 0| max: 25| class: 'autoNumeric-red' }| 381 | * { min: 25| max: 50| class: 'autoNumeric-orange' }| 382 | * { min: 50| max: 75| class: 'autoNumeric-yellow' }| 383 | * { min: 75| max: Number.MAX_SAFE_INTEGER| class: 'autoNumeric-green' }| 384 | * ]| 385 | * userDefined: [ 386 | * // If 'classes' is a string| set it if `true`| remove it if `false` 387 | * { callback: rawValue => { return true; }| classes: 'thisIsTrue' }| 388 | * // If 'classes' is an array with only 2 elements| set the first class if `true`| the second if `false` 389 | * { callback: rawValue => rawValue % 2 === 0| classes: ['autoNumeric-even'| 'autoNumeric-odd'] }| 390 | * // Return only one index to use on the `classes` array (here| 'class3') 391 | * { callback: rawValue => { return 2; }| classes: ['class1'| 'class2'| 'class3'] }| 392 | * // Return an array of indexes to use on the `classes` array (here| 'class1' and 'class3') 393 | * { callback: rawValue => { return [0| 2]; }| classes: ['class1'| 'class2'| 'class3'] }| 394 | * // If 'classes' is `undefined` or `null`| then the callback is called with the AutoNumeric object passed as a parameter 395 | * { callback: anElement => { return anElement.getFormatted(); } }| 396 | * ]| 397 | * } 398 | */ 399 | styleRules?: any; 400 | /* Add a text on the right hand side of the element value. 401 | * This suffix text can have any characters in its string| except numeric characters and the negative/positive sign. 402 | * Example: ' dollars' 403 | */ 404 | suffixText?: string; 405 | 406 | /* The three options (divisorWhenUnfocused| decimalPlacesShownOnBlur & symbolWhenUnfocused) handle scaling 407 | * of the input when the input does not have focus 408 | * Please note that the non-scaled value is held in data and it is advised that you use the `saveValueToSessionStorage` 409 | * option to ensure retaining the value 410 | * ["divisor"| "decimal places"| "symbol"] 411 | * Example: with the following options set {divisorWhenUnfocused: '1000'| decimalPlacesShownOnBlur: '1'| symbolWhenUnfocused: ' K'} 412 | * Example: focusin value "1|111.11" focusout value "1.1 K" 413 | */ 414 | 415 | /* The `symbolWhenUnfocused` option is a symbol placed as a suffix when not in focus. 416 | * This is optional too. 417 | */ 418 | symbolWhenUnfocused?: null | string; 419 | 420 | /* Defines if the element value should be unformatted when the user hover his mouse over it while holding the `Alt` key. 421 | * Unformatting there means that this removes any non-number characters and displays the *raw* value| as understood 422 | * by Javascript (ie. `12.34` is a valid number| while `12|34` is not). 423 | * We reformat back before anything else if : 424 | * - the user focus on the element by tabbing or clicking into it| 425 | * - the user releases the `Alt` key| and 426 | * - if we detect a mouseleave event. 427 | * 428 | * We unformat again if : 429 | * - while the mouse is over the element| the user hit `Alt` again 430 | */ 431 | unformatOnHover?: boolean; 432 | 433 | /* Removes the formatting and use the raw value in each autoNumeric elements of the parent form element| on the form `submit` event. 434 | * The output format is a numeric string (nnnn.nn or -nnnn.nn). 435 | */ 436 | unformatOnSubmit?: boolean; 437 | 438 | /* Provides a way for automatically replacing the formatted value with a pre-defined string| when the raw value is equal to a 439 | * specific value 440 | * Here you can specify as many 'conversion' as needed. 441 | */ 442 | valuesToStrings?: null | { 0: '-', } | { '-1': 'Min', 1: 'Max', }; 443 | /* Defines if the AutoNumeric element should watch external changes made without using `.set()`| but by using the basic 444 | * `aNElement.node().value = 42` notation. 445 | * If set to `watch`| then AutoNumeric will format the new value using `.set()` internally. 446 | * Otherwise it will neither format it| nor save it in the history. 447 | */ 448 | watchExternalChanges?: boolean; 449 | 450 | /* Defines when the wheel event will increment or decrement the element value. 451 | * When set to `'focus'`| the AutoNumeric-managed element needs to be focused for the wheel event to change the value. 452 | * When set to `'hover'`| using the wheel event while the mouse is hovering the element is sufficient (no focus needed). 453 | * 454 | * Note : 455 | * When `wheelOn` is set to `'focus'`| you can use the 'Shift' modifier key while using the mouse wheel in order 456 | * to temporarily activate the increment/decrement feature even if the element is not focused. 457 | * When `wheelOn` is set to `'hover'`| you can use the 'Shift' modifier key while using the mouse wheel in order 458 | * to temporarily disable the increment/decrement feature even if the element is not hovered. 459 | */ 460 | wheelOn?: string; 461 | 462 | /* That option is linked to the `modifyValueOnWheel` one and will only be used if the latter is set to `true`. 463 | * This option will modify the wheel behavior and can be used in two ways| either by setting : 464 | * - a 'fixed' step value (a positive float or integer number `1000`)| or 465 | * - the `'progressive'` string. 466 | * 467 | * The 'fixed' mode always increment/decrement the element value by that amount| while respecting 468 | * the `minimumValue` and `maximumValue` settings. 469 | * The 'progressive' mode will increment/decrement the element value based on its current value. The bigger the 470 | * number| the bigger the step| and vice versa. 471 | */ 472 | wheelStep?: string; 473 | 474 | /* Options Change strategy, 475 | * 476 | */ 477 | } 478 | -------------------------------------------------------------------------------- /src/lib/autonumeric.module.ts: -------------------------------------------------------------------------------- 1 | import {InjectionToken, ModuleWithProviders, NgModule} from '@angular/core'; 2 | import {AutonumericOptions} from "./autonumeric.model"; 3 | import {AutonumericDefaults} from "./autonumeric-defaults.service"; 4 | import {AutonumericDirective} from "./autonumeric.directive"; 5 | import {CommonModule} from '@angular/common'; 6 | 7 | export const USER_DEFAULTS = new InjectionToken('autonumeric defaults'); 8 | 9 | export function defaultsFactory(userDefaults: AutonumericOptions): AutonumericDefaults { 10 | const defaults: AutonumericDefaults = new AutonumericDefaults(); 11 | Object.assign(defaults, userDefaults); 12 | return defaults; 13 | } 14 | 15 | @NgModule({ 16 | imports: [CommonModule], 17 | declarations: [AutonumericDirective], 18 | exports: [AutonumericDirective] 19 | }) 20 | export class AutonumericModule { 21 | static forRoot(userDefaults: AutonumericOptions = {}): ModuleWithProviders { 22 | return { 23 | ngModule: AutonumericModule, 24 | providers: [ 25 | { 26 | provide: USER_DEFAULTS, 27 | useValue: userDefaults 28 | }, 29 | { 30 | provide: AutonumericDefaults, 31 | useFactory: defaultsFactory, 32 | deps: [USER_DEFAULTS] 33 | } 34 | ] 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './autonumeric.module'; 2 | export {AutonumericDefaults} from './autonumeric-defaults.service'; 3 | -------------------------------------------------------------------------------- /src/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../dist/autonumeric", 4 | "deleteDestPath": false, 5 | "lib": { 6 | "entryFile": "./index.ts", 7 | "umdId": "nga", 8 | "amdId": "nga" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ng-package.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../dist/autonumeric", 4 | "lib": { 5 | "flatModuleFile": "autonumeric", 6 | "entryFile": "./index.ts", 7 | "umdId": "nga", 8 | "amdId": "nga" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angularfy/autonumeric", 3 | "version": "3.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@angular/common": { 8 | "version": "4.4.7", 9 | "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.4.7.tgz", 10 | "integrity": "sha512-5R0POjbT4CR+8vXS7P33SiozJpTEKDsHq07EMm90OCwoofU5DIKDLNyEqr362zsbpzGUTmhGbSiLZib5Qt4djA==", 11 | "dev": true, 12 | "requires": { 13 | "tslib": "^1.7.1" 14 | } 15 | }, 16 | "@angular/core": { 17 | "version": "4.4.7", 18 | "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.4.7.tgz", 19 | "integrity": "sha512-Jxs6gNTl5KjXflg5vi5rlnokq1johFccN94qSOgDv+Mg1iuGF2i9p7EHkw3Y8jBCVaSLw1qgHE+wMb6KTlJDLA==", 20 | "dev": true, 21 | "requires": { 22 | "tslib": "^1.7.1" 23 | } 24 | }, 25 | "@angular/forms": { 26 | "version": "4.4.7", 27 | "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.4.7.tgz", 28 | "integrity": "sha512-EXGutI4GNBptpwkCQdCTxWAlJll8aCV7m3cA1FHZgFP7VNSgYF0pD+PscM5jSeajG30cRjaKxgL4cqj6yMMtww==", 29 | "dev": true, 30 | "requires": { 31 | "tslib": "^1.7.1" 32 | } 33 | }, 34 | "autonumeric": { 35 | "version": "4.5.10", 36 | "resolved": "https://registry.npmjs.org/autonumeric/-/autonumeric-4.5.10.tgz", 37 | "integrity": "sha512-/PAltg7FZe6YvXLI1b1ndB8JAHWDp8mAq344Nr23SP43Kvdrsk+pM62C1ca4wf+5CUZAri6AQwKQj7gU2MT7Zw==", 38 | "dev": true 39 | }, 40 | "tslib": { 41 | "version": "1.10.0", 42 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 43 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", 44 | "dev": true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angularfy/autonumeric", 3 | "version": "3.0.3", 4 | "description": "Angular implementation of autoNumeric", 5 | "keywords": [ 6 | "angular", 7 | "numeric", 8 | "components", 9 | "autonumeric", 10 | "autonumeric", 11 | "auto", 12 | "numeric", 13 | "number", 14 | "mask", 15 | "decimal", 16 | "regex", 17 | "mask" 18 | ], 19 | "author": "Abdelghani AINOUSS", 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/angularfy/autonumeric.git" 23 | }, 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/angularfy/autonumeric/issues" 27 | }, 28 | "homepage": "http://www.angularfy.com/projects/autonumeric", 29 | "peerDependencies": { 30 | "@angular/common": "^4.0.1", 31 | "@angular/core": "^4.0.1", 32 | "@angular/forms": "^4.0.1", 33 | "autonumeric": "^4.0.1" 34 | }, 35 | "devDependencies": { 36 | "@angular/common": "^4.0.1", 37 | "@angular/core": "^4.0.1", 38 | "@angular/forms": "^4.0.1", 39 | "autonumeric": "^4.0.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "inlineSources": true, 5 | "importHelpers": true 6 | }, 7 | "angularCompilerOptions": { 8 | "annotateForClosureCompiler": true, 9 | "skipTemplateCodegen": true, 10 | "strictMetadataEmit": true, 11 | "fullTemplateTypeCheck": true, 12 | "strictInjectionParameters": true 13 | }, 14 | "include": [ 15 | "./**/*.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "./test.ts" 12 | ], 13 | "include": [ 14 | "./**/*.spec.ts", 15 | "./**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "angularCompilerOptions": { 4 | "annotationsAs": "decorators" 5 | }, 6 | "compilerOptions": { 7 | "baseUrl": "./", 8 | "outDir": "./dist/out-tsc", 9 | "sourceMap": true, 10 | "declaration": false, 11 | "module": "es2015", 12 | "moduleResolution": "node", 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "importHelpers": true, 16 | "target": "es5", 17 | "typeRoots": [ 18 | "node_modules/@types" 19 | ], 20 | "lib": [ 21 | "es2018", 22 | "dom" 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "no-output-on-prefix": true, 65 | "use-input-property-decorator": true, 66 | "use-output-property-decorator": true, 67 | "use-host-property-decorator": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-life-cycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "component-class-suffix": true, 73 | "directive-class-suffix": true 74 | } 75 | } 76 | --------------------------------------------------------------------------------