├── demo ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── app.component.css │ │ ├── app.module.ts │ │ ├── app.component.ts │ │ └── app.component.html │ ├── favicon.ico │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── styles.css │ ├── index.html │ ├── main.ts │ ├── tsconfig.json │ ├── test.ts │ └── polyfills.ts ├── e2e │ ├── app.po.ts │ ├── app.e2e-spec.ts │ └── tsconfig.json ├── .editorconfig ├── .gitignore ├── protractor.conf.js ├── README.md ├── .angular-cli.json ├── karma.conf.js ├── package.json └── tslint.json ├── index.ts ├── src ├── odometer │ ├── odometer.d.ts │ ├── index.ts │ ├── themes │ │ ├── index.ts │ │ ├── minimal.theme.ts │ │ ├── default.theme.ts │ │ ├── plaza.theme.ts │ │ ├── digital.theme.ts │ │ ├── train-station.theme.ts │ │ ├── car.theme.ts │ │ └── slot-machine.theme.ts │ ├── odometer.config.ts │ ├── odometer.model.ts │ └── odometer.component.ts └── index.ts ├── travis.bak ├── .travis.yml.bak ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── CODE_OF_CONDUCT.md ├── .editorconfig ├── .gitignore ├── .npmignore ├── LICENSE ├── tsconfig.json ├── tslint.json ├── package.json └── README.md /demo/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | ng2-odometer { 2 | font-size: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmandreslopez/ng2-odometer/HEAD/demo/src/favicon.ico -------------------------------------------------------------------------------- /demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.19.17 3 | */ 4 | 5 | export * from './dist/odometer'; 6 | -------------------------------------------------------------------------------- /src/odometer/odometer.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | declare module 'odometer'; 6 | -------------------------------------------------------------------------------- /src/odometer/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export * from './odometer.component'; 6 | -------------------------------------------------------------------------------- /demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | body { 4 | padding: 20px; 5 | margin: 0; 6 | } 7 | 8 | .hide { 9 | display: block; 10 | } 11 | -------------------------------------------------------------------------------- /demo/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class DemoPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /travis.bak: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '7' 9 | before_script: 10 | - npm prune 11 | after_success: 12 | - npm run semantic-release 13 | branches: 14 | except: 15 | - /^v\d+\.\d+\.\d+$/ 16 | -------------------------------------------------------------------------------- /.travis.yml.bak: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '7' 9 | before_script: 10 | - npm prune 11 | after_success: 12 | - npm run semantic-release 13 | branches: 14 | except: 15 | - /^v\d+\.\d+\.\d+$/ 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 2 | 3 | 4 | 5 | * **What is the current behavior?** (You can also link to an open issue here) 6 | 7 | 8 | 9 | * **What is the new behavior (if this is a feature change)?** 10 | 11 | 12 | 13 | * **Other information**: 14 | -------------------------------------------------------------------------------- /src/odometer/themes/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export * from './car.theme'; 6 | export * from './default.theme'; 7 | export * from './digital.theme'; 8 | export * from './minimal.theme'; 9 | export * from './plaza.theme'; 10 | export * from './slot-machine.theme'; 11 | export * from './train-station.theme'; 12 | -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { DemoPage } from './app.po'; 2 | 3 | describe('demo App', () => { 4 | let page: DemoPage; 5 | 6 | beforeEach(() => { 7 | page = new DemoPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /demo/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [**.ts] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [*.md] 17 | max_line_length = off 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [**.ts] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [*.md] 18 | max_line_length = false 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | /bower_components 4 | 5 | # IDEs and editors 6 | /.idea 7 | /.vscode 8 | .project 9 | .classpath 10 | *.launch 11 | .settings/ 12 | 13 | # Misc 14 | npm-debug.log 15 | npm-debug.log.* 16 | 17 | # Build and dist for now 18 | /dist 19 | /temp 20 | /logs 21 | 22 | # System Files 23 | .DS_Store 24 | Thumbs.db 25 | 26 | # App 27 | src/**/*.js 28 | src/**/*.map 29 | -------------------------------------------------------------------------------- /demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Misc 3 | .DS_Store 4 | .github 5 | **/.DS_Store 6 | nbproject 7 | manifest.mf 8 | build.xml 9 | node_modules/* 10 | npm-debug.log 11 | npm-debug.log.* 12 | Thumbs.db 13 | coverage 14 | *.ts 15 | !*.d.ts 16 | tests 17 | *.spec.* 18 | *.test.* 19 | 20 | # IDE 21 | .idea 22 | .project 23 | .settings 24 | .vscode 25 | .idea/* 26 | *.iml 27 | 28 | # Misc Project 29 | *.yml 30 | demo 31 | karma* 32 | typings 33 | tsconfig.json 34 | tslint.json 35 | typings.json 36 | -------------------------------------------------------------------------------- /demo/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": [ 8 | "es2016" 9 | ], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "outDir": "../dist/out-tsc-e2e", 13 | "sourceMap": true, 14 | "target": "es6", 15 | "typeRoots": [ 16 | "../node_modules/@types" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { Ng2OdometerModule } from 'ng2-odometer'; 4 | import { AppComponent } from './app.component'; 5 | 6 | @NgModule({ 7 | imports: [ 8 | BrowserModule, 9 | Ng2OdometerModule.forRoot() 10 | ], 11 | declarations: [AppComponent], 12 | bootstrap: [AppComponent] 13 | }) 14 | export class AppModule { 15 | // 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "", 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": [ 8 | "es2016", 9 | "dom" 10 | ], 11 | "mapRoot": "./", 12 | "module": "es2015", 13 | "moduleResolution": "node", 14 | "outDir": "../dist/out-tsc", 15 | "sourceMap": true, 16 | "target": "es5", 17 | "typeRoots": [ 18 | "../node_modules/@types" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | import { NgModule, ModuleWithProviders } from '@angular/core'; 6 | import { CommonModule } from '@angular/common'; 7 | import { Ng2OdometerComponent } from './odometer'; 8 | 9 | @NgModule({ 10 | imports: [CommonModule], 11 | declarations: [Ng2OdometerComponent], 12 | exports: [Ng2OdometerComponent] 13 | }) 14 | export class Ng2OdometerModule { 15 | static forRoot(): ModuleWithProviders { 16 | return { 17 | ngModule: Ng2OdometerModule 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/odometer/odometer.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 6.15.17 3 | */ 4 | 5 | import { Component } from '@angular/core'; 6 | import { Observable } from 'rxjs'; 7 | 8 | export interface Ng2OdometerConfigModel { 9 | animation?: string; 10 | format?: string; 11 | theme?: string; 12 | value?: number; 13 | duration?: number; 14 | auto?: boolean; 15 | } 16 | 17 | export class Ng2OdometerConfig implements Ng2OdometerConfigModel { 18 | animation?: string = 'slide'; 19 | format: string = '(,ddd)'; 20 | theme?: string = 'default'; 21 | value?: number = 0; 22 | duration?: number = 2000; 23 | auto?: boolean = true; 24 | } 25 | -------------------------------------------------------------------------------- /src/odometer/odometer.model.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export interface OdometerModel { 6 | MAX_VALUES: number; 7 | digits: Array; 8 | el: HTMLElement; 9 | format: { 10 | precision: number; 11 | radix: any; 12 | repeating: string; 13 | }; 14 | inside: HTMLElement; 15 | options: { 16 | el: HTMLElement; 17 | duration: number; 18 | theme: string 19 | format: string; 20 | animation: string; 21 | }; 22 | ribbons: any; 23 | transitionEndBound: boolean; 24 | value: 0; 25 | 26 | // Methods 27 | update(number: number): void; 28 | } 29 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage/* 30 | /libpeerconnection.log 31 | npm-debug.log 32 | testem.log 33 | /typings 34 | 35 | # e2e 36 | /e2e/*.js 37 | /e2e/*.map 38 | 39 | #System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Observable, Observer } from 'rxjs'; 3 | 4 | @Component({ 5 | selector: 'app', 6 | styleUrls: ['app.component.css'], 7 | templateUrl: 'app.component.html' 8 | }) 9 | export class AppComponent { 10 | public number: number = 3000; 11 | public observable: Observable; 12 | private observer: Observer; 13 | 14 | constructor() { 15 | this.observable = new Observable((observer: any) => this.observer = observer).share(); 16 | 17 | // For auto mode 18 | setTimeout(() => this.number += this.number, 5000); // Update on 5 seconds 19 | } 20 | 21 | public trigger() { 22 | this.observer.next(true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/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 | /*global jasmine */ 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | './e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | beforeLaunch: function() { 24 | require('ts-node').register({ 25 | project: 'e2e' 26 | }); 27 | }, 28 | onPrepare() { 29 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Submitting Pull Requests 2 | 3 | If you're changing the structure of the repository please create an issue first. 4 | 5 | The release process is automated using [semantic-release](https://github.com/semantic-release/semantic-release), for best release result follow these simple steps: 6 | 7 | ``` 8 | git add --all 9 | npm run commit 10 | git push origin master 11 | ``` 12 | 13 | * Do not add/change the package.json version field. 14 | * Create unit test for any new functionality you add in your commits. 15 | 16 | ## Submitting bug reports 17 | 18 | Make sure you are on latest changes and that you ran this command `npm run clean:install` after updating your local repository. If you can, please provide more information about your environment such as browser, operating system, node version, and npm version. 19 | 20 | ## Project Structure 21 | 22 | The configuration files are devided in config/, config/advance/ and config/custom 23 | - src/core 24 | - src/providers/ This folder contains one folder per provider 25 | -------------------------------------------------------------------------------- /demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

Odometer for Angular2

2 |

3 |

4 |

5 |

6 |

7 |

8 |

9 |

10 |

11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jose Andres 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 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-beta.32.3. 4 | 5 | ## Development server 6 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 7 | 8 | ## Code scaffolding 9 | 10 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 11 | 12 | ## Build 13 | 14 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 15 | 16 | ## Running unit tests 17 | 18 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 19 | 20 | ## Running end-to-end tests 21 | 22 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 23 | Before running the tests make sure you are serving the app via `ng serve`. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "noEmitHelpers": false, 11 | "noImplicitAny": true, 12 | "declaration": true, 13 | "skipLibCheck": true, 14 | "stripInternal": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "removeComments": true, 18 | "noLib": false, 19 | "pretty": true, 20 | "allowUnreachableCode": false, 21 | "allowUnusedLabels": false, 22 | "noImplicitReturns": false, 23 | "noImplicitUseStrict": false, 24 | "noFallthroughCasesInSwitch": true, 25 | "outDir": "dist", 26 | "lib": [ 27 | "dom", 28 | "es6" 29 | ], 30 | "types": [ 31 | "node", 32 | "karma", 33 | "jasmine", 34 | "lodash" 35 | ] 36 | }, 37 | "files": [ 38 | "src/index.ts" 39 | ], 40 | "exclude": [ 41 | "node_modules" 42 | ], 43 | "compileOnSave": false, 44 | "angularCompilerOptions": { 45 | "skipTemplateCodegen": true 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/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/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Note: for support questions, please use one of these channels:** 2 | 3 | * **I'm submitting a ...** 4 | [ ] bug report 5 | [ ] feature request 6 | [ ] question about the decisions made in the repository 7 | 8 | * **Do you want to request a *feature* or report a *bug*?** 9 | 10 | 11 | 12 | * **What is the current behavior?** 13 | 14 | 15 | 16 | * **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via 17 | https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). 18 | 19 | 20 | 21 | * **What is the expected behavior?** 22 | 23 | 24 | 25 | * **What is the motivation / use case for changing the behavior?** 26 | 27 | 28 | 29 | * **Please tell us about your environment:** 30 | 31 | - Angular version: 2.1.2 32 | - Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] 33 | 34 | 35 | 36 | * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) 37 | -------------------------------------------------------------------------------- /demo/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "version": "1.0.0-beta.32.3", 5 | "name": "demo" 6 | }, 7 | "apps": [ 8 | { 9 | "root": "src", 10 | "outDir": "dist", 11 | "assets": [ 12 | "assets", 13 | "favicon.ico" 14 | ], 15 | "index": "index.html", 16 | "main": "main.ts", 17 | "polyfills": "polyfills.ts", 18 | "test": "test.ts", 19 | "tsconfig": "tsconfig.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [], 25 | "environmentSource": "environments/environment.ts", 26 | "environments": { 27 | "dev": "environments/environment.ts", 28 | "prod": "environments/environment.prod.ts" 29 | } 30 | } 31 | ], 32 | "e2e": { 33 | "protractor": { 34 | "config": "./protractor.conf.js" 35 | } 36 | }, 37 | "lint": [ 38 | { 39 | "files": "src/**/*.ts", 40 | "project": "src/tsconfig.json" 41 | }, 42 | { 43 | "files": "e2e/**/*.ts", 44 | "project": "e2e/tsconfig.json" 45 | } 46 | ], 47 | "test": { 48 | "karma": { 49 | "config": "./karma.conf.js" 50 | } 51 | }, 52 | "defaults": { 53 | "styleExt": "css", 54 | "component": {} 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /demo/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 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/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | files: [ 19 | { pattern: './src/test.ts', watched: false } 20 | ], 21 | preprocessors: { 22 | './src/test.ts': ['@angular/cli'] 23 | }, 24 | mime: { 25 | 'text/x-typescript': ['ts','tsx'] 26 | }, 27 | coverageIstanbulReporter: { 28 | reports: [ 'html', 'lcovonly' ], 29 | fixWebpackSourcePaths: true 30 | }, 31 | angularCli: { 32 | config: './.angular-cli.json', 33 | environment: 'dev' 34 | }, 35 | reporters: config.angularCli && config.angularCli.codeCoverage 36 | ? ['progress', 'coverage-istanbul'] 37 | : ['progress', 'kjhtml'], 38 | port: 9876, 39 | colors: true, 40 | logLevel: config.LOG_INFO, 41 | autoWatch: true, 42 | browsers: ['Chrome'], 43 | singleRun: false 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-odometer-demo", 3 | "version": "1.0.0", 4 | "description": "Odometer for Angular2", 5 | "author": "Jose Andres ", 6 | "license": "MIT", 7 | "angular-cli": {}, 8 | "scripts": { 9 | "ng": "ng", 10 | "start": "ng serve", 11 | "test": "ng test", 12 | "lint": "ng lint", 13 | "e2e": "ng e2e" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "@angular/common": "^2.4.0", 18 | "@angular/compiler": "^2.4.0", 19 | "@angular/core": "^2.4.0", 20 | "@angular/forms": "^2.4.0", 21 | "@angular/http": "^2.4.0", 22 | "@angular/platform-browser": "^2.4.0", 23 | "@angular/platform-browser-dynamic": "^2.4.0", 24 | "@angular/router": "^3.4.0", 25 | "core-js": "^2.4.1", 26 | "ng2-odometer": "^1.1.1", 27 | "odometer": "^0.4.8", 28 | "rxjs": "^5.1.0", 29 | "zone.js": "^0.7.6" 30 | }, 31 | "devDependencies": { 32 | "@angular/cli": "1.0.0-beta.32.3", 33 | "@angular/compiler-cli": "^2.4.0", 34 | "@types/jasmine": "2.5.38", 35 | "@types/node": "~6.0.60", 36 | "codelyzer": "~2.0.0-beta.4", 37 | "jasmine-core": "~2.5.2", 38 | "jasmine-spec-reporter": "~3.2.0", 39 | "karma": "~1.4.1", 40 | "karma-chrome-launcher": "~2.0.0", 41 | "karma-cli": "~1.0.1", 42 | "karma-jasmine": "~1.1.0", 43 | "karma-jasmine-html-reporter": "^0.2.2", 44 | "karma-coverage-istanbul-reporter": "^0.2.0", 45 | "protractor": "~5.1.0", 46 | "ts-node": "~2.0.0", 47 | "tslint": "~4.4.2", 48 | "typescript": "~2.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/set'; 35 | 36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 37 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 38 | 39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 41 | 42 | 43 | /** Evergreen browsers require these. **/ 44 | import 'core-js/es6/reflect'; 45 | import 'core-js/es7/reflect'; 46 | 47 | 48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "class-name": true, 7 | "comment-format": [ 8 | true, 9 | "check-space" 10 | ], 11 | "component-class-suffix": true, 12 | "curly": true, 13 | "directive-class-suffix": false, 14 | "directive-selector": [ 15 | true, 16 | "attribute", 17 | "", 18 | "camelCase" 19 | ], 20 | "component-selector": [ 21 | true, 22 | "element", 23 | "", 24 | "kebab-case" 25 | ], 26 | "eofline": true, 27 | "forin": true, 28 | "indent": [ 29 | true, 30 | "spaces" 31 | ], 32 | "label-position": true, 33 | "max-line-length": [ 34 | true, 35 | 140 36 | ], 37 | "member-access": false, 38 | "member-ordering": [ 39 | true, 40 | "static-before-instance", 41 | "variables-before-functions" 42 | ], 43 | "no-arg": true, 44 | "no-attribute-parameter-decorator": true, 45 | "no-bitwise": true, 46 | "no-console": [ 47 | true, 48 | "debug", 49 | "info", 50 | "time", 51 | "timeEnd", 52 | "trace" 53 | ], 54 | "no-construct": true, 55 | "no-debugger": true, 56 | "no-duplicate-variable": true, 57 | "no-empty": false, 58 | "no-eval": true, 59 | "no-forward-ref": true, 60 | "no-inferrable-types": true, 61 | "no-input-rename": true, 62 | "no-output-rename": true, 63 | "no-shadowed-variable": true, 64 | "no-string-literal": false, 65 | "no-switch-case-fall-through": true, 66 | "no-trailing-whitespace": true, 67 | "no-unused-expression": true, 68 | "no-use-before-declare": true, 69 | "no-var-keyword": true, 70 | "object-literal-sort-keys": false, 71 | "one-line": [false], 72 | "pipe-naming": [ 73 | true, 74 | "camelCase" 75 | ], 76 | "quotemark": [ 77 | true, 78 | "single" 79 | ], 80 | "radix": true, 81 | "semicolon": [ 82 | true, 83 | "always" 84 | ], 85 | "triple-equals": [ 86 | true, 87 | "allow-null-check" 88 | ], 89 | "typedef-whitespace": [ 90 | true, 91 | { 92 | "call-signature": "nospace", 93 | "index-signature": "nospace", 94 | "parameter": "nospace", 95 | "property-declaration": "nospace", 96 | "variable-declaration": "nospace" 97 | } 98 | ], 99 | "use-host-property-decorator": true, 100 | "use-input-property-decorator": true, 101 | "use-life-cycle-interface": true, 102 | "use-output-property-decorator": true, 103 | "use-pipe-transform-interface": true, 104 | "variable-name": false, 105 | "whitespace": [ 106 | true, 107 | "check-branch", 108 | "check-decl", 109 | "check-operator", 110 | "check-separator", 111 | "check-type" 112 | ] 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /demo/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": [true, "ignore-params"], 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jonnybgod@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-odometer", 3 | "description": "Odometer for Angular2", 4 | "author": "Jose Andres ", 5 | "license": "MIT", 6 | "version": "1.1.3", 7 | "keywords": [ 8 | "hubspot", 9 | "odometer", 10 | "hubspot", 11 | "angular", 12 | "angular2", 13 | "ng2-odometer" 14 | ], 15 | "main": "./dist/index", 16 | "typings": "./dist/index.d.ts", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/jmandreslopez/ng2-odometer.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/jmandreslopez/ng2-odometer/issues" 23 | }, 24 | "homepage": "https://github.com/jmandreslopez/ng2-odometer#readme", 25 | "scripts": { 26 | "rimraf": "rimraf", 27 | "lint": "tslint ./*.ts ./src/{,*/}*.ts", 28 | "clean": "npm cache clean && npm run rimraf -- node_modules dist", 29 | "preclean:install": "npm run clean", 30 | "clean:install": "npm set progress=false && npm install", 31 | "commit": "git-cz", 32 | "build": "tsc -d && ngc", 33 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 34 | }, 35 | "dependencies": { 36 | "odometer": "^0.4.8" 37 | }, 38 | "devDependencies": { 39 | "@angular/cli": "^1.1.2", 40 | "@angular/common": "^2.4.7", 41 | "@angular/compiler": "^2.4.7", 42 | "@angular/compiler-cli": "^2.4.7", 43 | "@angular/core": "^2.4.7", 44 | "@angular/forms": "^2.4.7", 45 | "@angular/platform-browser": "^2.4.7", 46 | "@angular/platform-browser-dynamic": "^2.4.7", 47 | "@angular/platform-server": "^2.4.7", 48 | "@angular/router": "3.4.7", 49 | "@angular/tsc-wrapped": "0.5.1", 50 | "@ngtools/webpack": "1.1.4", 51 | "@types/core-js": "0.9.35", 52 | "@types/es6-shim": "^0.31.32", 53 | "@types/jasmine": "^2.5.35", 54 | "@types/karma": "0.13.33", 55 | "@types/lodash": "^4.14.52", 56 | "@types/node": "^7.0.0", 57 | "angular2-template-loader": "0.6.0", 58 | "bulma": "0.2.3", 59 | "classlist-polyfill": "1.0.3", 60 | "codecov": "1.0.1", 61 | "codelyzer": "~2.0.0-beta.4", 62 | "commitizen": "^2.8.6", 63 | "concurrently": "^3.1.0", 64 | "conventional-changelog-cli": "1.2.0", 65 | "conventional-github-releaser": "1.1.3", 66 | "core-js": "^2.4.0", 67 | "cpy": "5.0.0", 68 | "cpy-cli": "1.0.1", 69 | "css-loader": "^0.23.1", 70 | "cz-conventional-changelog": "^1.2.0", 71 | "del-cli": "0.2.1", 72 | "es6-promise": "^3.0.2", 73 | "es6-shim": "^0.35.0", 74 | "gitignore-to-glob": "0.3.0", 75 | "google-code-prettify": "1.0.5", 76 | "highlight.js": "^9.1.0", 77 | "html-loader": "^0.4.3", 78 | "jasmine": "2.5.3", 79 | "jasmine-core": "^2.4.1", 80 | "json-loader": "^0.5.4", 81 | "karma": "^1.4.0", 82 | "karma-chrome-launcher": "^2.0.0", 83 | "karma-cli": "^1.0.1", 84 | "karma-jasmine": "^1.0.2", 85 | "karma-mocha-reporter": "^2.2.0", 86 | "karma-phantomjs-launcher": "^1.0.2", 87 | "karma-webpack": "^1.8.0", 88 | "lodash": "^4.17.4", 89 | "marked": "^0.3.6", 90 | "ngm-cli": "0.4.4", 91 | "node-sass": "^3.6.0", 92 | "npm-run-all": "^4.0.1", 93 | "phantomjs-prebuilt": "^2.1.7", 94 | "pre-commit": "1.2.2", 95 | "raw-loader": "^0.5.1", 96 | "reflect-metadata": "^0.1.3", 97 | "require-dir": "0.3.1", 98 | "rimraf": "~2.5.4", 99 | "rxjs": "^5.0.3", 100 | "sass-loader": "^5.0.0", 101 | "semantic-release": "^6.3.2", 102 | "style-loader": "^0.13.1", 103 | "systemjs-builder": "0.15.34", 104 | "ts-helpers": "^1.1.1", 105 | "ts-loader": "^1.0.0", 106 | "ts-node": "2.0.0", 107 | "tslint": "4.3.1", 108 | "typescript": "^2.1.6", 109 | "webpack": "^2.2.1", 110 | "zone.js": "^0.7.6" 111 | }, 112 | "engines": { 113 | "node": ">=6.2.0" 114 | }, 115 | "config": { 116 | "commitizen": { 117 | "path": "node_modules/cz-conventional-changelog" 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/odometer/themes/minimal.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const MINIMAL_THEME = ` 6 | .odometer.odometer-auto-theme, 7 | .odometer.odometer-theme-minimal { 8 | display: inline-block; 9 | vertical-align: middle; 10 | *vertical-align: auto; 11 | *zoom: 1; 12 | *display: inline; 13 | position: relative; 14 | } 15 | .odometer.odometer-auto-theme .odometer-digit, 16 | .odometer.odometer-theme-minimal .odometer-digit { 17 | display: inline-block; 18 | vertical-align: middle; 19 | *vertical-align: auto; 20 | *zoom: 1; 21 | *display: inline; 22 | position: relative; 23 | } 24 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 25 | .odometer.odometer-theme-minimal .odometer-digit .odometer-digit-spacer { 26 | display: inline-block; 27 | vertical-align: middle; 28 | *vertical-align: auto; 29 | *zoom: 1; 30 | *display: inline; 31 | visibility: hidden; 32 | } 33 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 34 | .odometer.odometer-theme-minimal .odometer-digit .odometer-digit-inner { 35 | text-align: left; 36 | display: block; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | right: 0; 41 | bottom: 0; 42 | overflow: hidden; 43 | } 44 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 45 | .odometer.odometer-theme-minimal .odometer-digit .odometer-ribbon { 46 | display: block; 47 | } 48 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 49 | .odometer.odometer-theme-minimal .odometer-digit .odometer-ribbon-inner { 50 | display: block; 51 | -webkit-backface-visibility: hidden; 52 | } 53 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 54 | .odometer.odometer-theme-minimal .odometer-digit .odometer-value { 55 | display: block; 56 | -webkit-transform: translateZ(0); 57 | } 58 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 59 | .odometer.odometer-theme-minimal .odometer-digit .odometer-value.odometer-last-value { 60 | position: absolute; 61 | } 62 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 63 | .odometer.odometer-theme-minimal.odometer-animating-up .odometer-ribbon-inner { 64 | -webkit-transition: -webkit-transform 2s; 65 | -moz-transition: -moz-transform 2s; 66 | -ms-transition: -ms-transform 2s; 67 | -o-transition: -o-transform 2s; 68 | transition: transform 2s; 69 | } 70 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 71 | .odometer.odometer-theme-minimal.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 72 | -webkit-transform: translateY(-100%); 73 | -moz-transform: translateY(-100%); 74 | -ms-transform: translateY(-100%); 75 | -o-transform: translateY(-100%); 76 | transform: translateY(-100%); 77 | } 78 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 79 | .odometer.odometer-theme-minimal.odometer-animating-down .odometer-ribbon-inner { 80 | -webkit-transform: translateY(-100%); 81 | -moz-transform: translateY(-100%); 82 | -ms-transform: translateY(-100%); 83 | -o-transform: translateY(-100%); 84 | transform: translateY(-100%); 85 | } 86 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 87 | .odometer.odometer-theme-minimal.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 88 | -webkit-transition: -webkit-transform 2s; 89 | -moz-transition: -moz-transform 2s; 90 | -ms-transition: -ms-transform 2s; 91 | -o-transition: -o-transform 2s; 92 | transition: transform 2s; 93 | -webkit-transform: translateY(0); 94 | -moz-transform: translateY(0); 95 | -ms-transform: translateY(0); 96 | -o-transform: translateY(0); 97 | transform: translateY(0); 98 | } 99 | `; 100 | -------------------------------------------------------------------------------- /src/odometer/themes/default.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const DEFAULT_THEME = ` 6 | .odometer.odometer-auto-theme, 7 | .odometer.odometer-theme-default { 8 | display: inline-block; 9 | vertical-align: middle; 10 | *vertical-align: auto; 11 | *zoom: 1; 12 | *display: inline; 13 | position: relative; 14 | } 15 | .odometer.odometer-auto-theme .odometer-digit, 16 | .odometer.odometer-theme-default .odometer-digit { 17 | display: inline-block; 18 | vertical-align: middle; 19 | *vertical-align: auto; 20 | *zoom: 1; 21 | *display: inline; 22 | position: relative; 23 | } 24 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 25 | .odometer.odometer-theme-default .odometer-digit .odometer-digit-spacer { 26 | display: inline-block; 27 | vertical-align: middle; 28 | *vertical-align: auto; 29 | *zoom: 1; 30 | *display: inline; 31 | visibility: hidden; 32 | } 33 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 34 | .odometer.odometer-theme-default .odometer-digit .odometer-digit-inner { 35 | text-align: left; 36 | display: block; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | right: 0; 41 | bottom: 0; 42 | overflow: hidden; 43 | } 44 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 45 | .odometer.odometer-theme-default .odometer-digit .odometer-ribbon { 46 | display: block; 47 | } 48 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 49 | .odometer.odometer-theme-default .odometer-digit .odometer-ribbon-inner { 50 | display: block; 51 | -webkit-backface-visibility: hidden; 52 | } 53 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 54 | .odometer.odometer-theme-default .odometer-digit .odometer-value { 55 | display: block; 56 | -webkit-transform: translateZ(0); 57 | } 58 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 59 | .odometer.odometer-theme-default .odometer-digit .odometer-value.odometer-last-value { 60 | position: absolute; 61 | } 62 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 63 | .odometer.odometer-theme-default.odometer-animating-up .odometer-ribbon-inner { 64 | -webkit-transition: -webkit-transform 2s !important; 65 | -moz-transition: -moz-transform 2s; 66 | -ms-transition: -ms-transform 2s; 67 | -o-transition: -o-transform 2s; 68 | transition: transform 2s; 69 | } 70 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 71 | .odometer.odometer-theme-default.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 72 | -webkit-transform: translateY(-100%); 73 | -moz-transform: translateY(-100%); 74 | -ms-transform: translateY(-100%); 75 | -o-transform: translateY(-100%); 76 | transform: translateY(-100%); 77 | } 78 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 79 | .odometer.odometer-theme-default.odometer-animating-down .odometer-ribbon-inner { 80 | -webkit-transform: translateY(-100%); 81 | -moz-transform: translateY(-100%); 82 | -ms-transform: translateY(-100%); 83 | -o-transform: translateY(-100%); 84 | transform: translateY(-100%); 85 | } 86 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 87 | .odometer.odometer-theme-default.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 88 | -webkit-transition: -webkit-transform 2s; 89 | -moz-transition: -moz-transform 2s; 90 | -ms-transition: -ms-transform 2s; 91 | -o-transition: -o-transform 2s; 92 | transition: transform 2s; 93 | -webkit-transform: translateY(0); 94 | -moz-transform: translateY(0); 95 | -ms-transform: translateY(0); 96 | -o-transform: translateY(0); 97 | transform: translateY(0); 98 | } 99 | .odometer.odometer-auto-theme, 100 | .odometer.odometer-theme-default { 101 | font-family: "Helvetica Neue", sans-serif; 102 | line-height: 1.1em; 103 | } 104 | .odometer.odometer-auto-theme .odometer-value, 105 | .odometer.odometer-theme-default .odometer-value { 106 | text-align: center; 107 | } 108 | `; 109 | -------------------------------------------------------------------------------- /src/odometer/themes/plaza.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const PLAZA_THEME = ` 6 | .odometer.odometer-auto-theme, 7 | .odometer.odometer-theme-plaza { 8 | display: inline-block; 9 | vertical-align: middle; 10 | *vertical-align: auto; 11 | *zoom: 1; 12 | *display: inline; 13 | position: relative; 14 | } 15 | .odometer.odometer-auto-theme .odometer-digit, 16 | .odometer.odometer-theme-plaza .odometer-digit { 17 | display: inline-block; 18 | vertical-align: middle; 19 | *vertical-align: auto; 20 | *zoom: 1; 21 | *display: inline; 22 | position: relative; 23 | } 24 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 25 | .odometer.odometer-theme-plaza .odometer-digit .odometer-digit-spacer { 26 | display: inline-block; 27 | vertical-align: middle; 28 | *vertical-align: auto; 29 | *zoom: 1; 30 | *display: inline; 31 | visibility: hidden; 32 | } 33 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 34 | .odometer.odometer-theme-plaza .odometer-digit .odometer-digit-inner { 35 | text-align: left; 36 | display: block; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | right: 0; 41 | bottom: 0; 42 | overflow: hidden; 43 | } 44 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 45 | .odometer.odometer-theme-plaza .odometer-digit .odometer-ribbon { 46 | display: block; 47 | } 48 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 49 | .odometer.odometer-theme-plaza .odometer-digit .odometer-ribbon-inner { 50 | display: block; 51 | -webkit-backface-visibility: hidden; 52 | } 53 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 54 | .odometer.odometer-theme-plaza .odometer-digit .odometer-value { 55 | display: block; 56 | -webkit-transform: translateZ(0); 57 | } 58 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 59 | .odometer.odometer-theme-plaza .odometer-digit .odometer-value.odometer-last-value { 60 | position: absolute; 61 | } 62 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 63 | .odometer.odometer-theme-plaza.odometer-animating-up .odometer-ribbon-inner { 64 | -webkit-transition: -webkit-transform 2s; 65 | -moz-transition: -moz-transform 2s; 66 | -ms-transition: -ms-transform 2s; 67 | -o-transition: -o-transform 2s; 68 | transition: transform 2s; 69 | } 70 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 71 | .odometer.odometer-theme-plaza.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 72 | -webkit-transform: translateY(-100%); 73 | -moz-transform: translateY(-100%); 74 | -ms-transform: translateY(-100%); 75 | -o-transform: translateY(-100%); 76 | transform: translateY(-100%); 77 | } 78 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 79 | .odometer.odometer-theme-plaza.odometer-animating-down .odometer-ribbon-inner { 80 | -webkit-transform: translateY(-100%); 81 | -moz-transform: translateY(-100%); 82 | -ms-transform: translateY(-100%); 83 | -o-transform: translateY(-100%); 84 | transform: translateY(-100%); 85 | } 86 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 87 | .odometer.odometer-theme-plaza.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 88 | -webkit-transition: -webkit-transform 2s; 89 | -moz-transition: -moz-transform 2s; 90 | -ms-transition: -ms-transform 2s; 91 | -o-transition: -o-transform 2s; 92 | transition: transform 2s; 93 | -webkit-transform: translateY(0); 94 | -moz-transform: translateY(0); 95 | -ms-transform: translateY(0); 96 | -o-transform: translateY(0); 97 | transform: translateY(0); 98 | } 99 | .odometer.odometer-auto-theme, 100 | .odometer.odometer-theme-plaza { 101 | -moz-border-radius: 0.15em; 102 | -webkit-border-radius: 0.15em; 103 | border-radius: 0.15em; 104 | background-color: #f0f8ff; 105 | font-family: "Helvetica Neue", sans-serif; 106 | font-weight: 100; 107 | padding: 0 0.12em; 108 | line-height: 1.2em; 109 | font-size: 1.2em; 110 | background-size: 16px 16px; 111 | } 112 | .odometer.odometer-auto-theme .odometer-digit, 113 | .odometer.odometer-theme-plaza .odometer-digit { 114 | -moz-border-radius: 0.1em; 115 | -webkit-border-radius: 0.1em; 116 | border-radius: 0.1em; 117 | padding: 0 0.03em; 118 | color: #648baf; 119 | } 120 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 121 | .odometer.odometer-theme-plaza .odometer-digit .odometer-digit-inner { 122 | left: 0.03em; 123 | } 124 | `; 125 | -------------------------------------------------------------------------------- /src/odometer/themes/digital.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const DIGITAL_THEME = ` 6 | @import url("//fonts.googleapis.com/css?family=Wallpoet"); 7 | .odometer.odometer-auto-theme, 8 | .odometer.odometer-theme-digital { 9 | display: inline-block; 10 | vertical-align: middle; 11 | *vertical-align: auto; 12 | *zoom: 1; 13 | *display: inline; 14 | position: relative; 15 | } 16 | .odometer.odometer-auto-theme .odometer-digit, 17 | .odometer.odometer-theme-digital .odometer-digit { 18 | display: inline-block; 19 | vertical-align: middle; 20 | *vertical-align: auto; 21 | *zoom: 1; 22 | *display: inline; 23 | position: relative; 24 | } 25 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 26 | .odometer.odometer-theme-digital .odometer-digit .odometer-digit-spacer { 27 | display: inline-block; 28 | vertical-align: middle; 29 | *vertical-align: auto; 30 | *zoom: 1; 31 | *display: inline; 32 | visibility: hidden; 33 | } 34 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 35 | .odometer.odometer-theme-digital .odometer-digit .odometer-digit-inner { 36 | text-align: left; 37 | display: block; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | overflow: hidden; 44 | } 45 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 46 | .odometer.odometer-theme-digital .odometer-digit .odometer-ribbon { 47 | display: block; 48 | } 49 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 50 | .odometer.odometer-theme-digital .odometer-digit .odometer-ribbon-inner { 51 | display: block; 52 | -webkit-backface-visibility: hidden; 53 | } 54 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 55 | .odometer.odometer-theme-digital .odometer-digit .odometer-value { 56 | display: block; 57 | -webkit-transform: translateZ(0); 58 | } 59 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 60 | .odometer.odometer-theme-digital .odometer-digit .odometer-value.odometer-last-value { 61 | position: absolute; 62 | } 63 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 64 | .odometer.odometer-theme-digital.odometer-animating-up .odometer-ribbon-inner { 65 | -webkit-transition: -webkit-transform 2s; 66 | -moz-transition: -moz-transform 2s; 67 | -ms-transition: -ms-transform 2s; 68 | -o-transition: -o-transform 2s; 69 | transition: transform 2s; 70 | } 71 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 72 | .odometer.odometer-theme-digital.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 73 | -webkit-transform: translateY(-100%); 74 | -moz-transform: translateY(-100%); 75 | -ms-transform: translateY(-100%); 76 | -o-transform: translateY(-100%); 77 | transform: translateY(-100%); 78 | } 79 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 80 | .odometer.odometer-theme-digital.odometer-animating-down .odometer-ribbon-inner { 81 | -webkit-transform: translateY(-100%); 82 | -moz-transform: translateY(-100%); 83 | -ms-transform: translateY(-100%); 84 | -o-transform: translateY(-100%); 85 | transform: translateY(-100%); 86 | } 87 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 88 | .odometer.odometer-theme-digital.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 89 | -webkit-transition: -webkit-transform 2s; 90 | -moz-transition: -moz-transform 2s; 91 | -ms-transition: -ms-transform 2s; 92 | -o-transition: -o-transform 2s; 93 | transition: transform 2s; 94 | -webkit-transform: translateY(0); 95 | -moz-transform: translateY(0); 96 | -ms-transform: translateY(0); 97 | -o-transform: translateY(0); 98 | transform: translateY(0); 99 | } 100 | .odometer.odometer-auto-theme, 101 | .odometer.odometer-theme-digital { 102 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjUwJSIgY3k9IjUwJSIgcj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzhiZjVhNSIgc3RvcC1vcGFjaXR5PSIwLjQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiLz48L3JhZGlhbEdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); 103 | background-size: 100%; 104 | background-image: -moz-radial-gradient(rgba(139, 245, 165, 0.4), #000000); 105 | background-image: -webkit-radial-gradient(rgba(139, 245, 165, 0.4), #000000); 106 | background-image: radial-gradient(rgba(139, 245, 165, 0.4), #000000); 107 | background-color: #000; 108 | font-family: "Wallpoet", monospace; 109 | padding: 0 0.2em; 110 | line-height: 1.1em; 111 | color: #8bf5a5; 112 | } 113 | .odometer.odometer-auto-theme .odometer-digit + .odometer-digit, 114 | .odometer.odometer-theme-digital .odometer-digit + .odometer-digit { 115 | margin-left: 0.1em; 116 | } 117 | `; 118 | -------------------------------------------------------------------------------- /src/odometer/odometer.component.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /** 4 | * Created by Jose Andres on 6.15.17 5 | */ 6 | 7 | import * as _ from 'lodash'; 8 | import { Component, ViewEncapsulation, Input, OnInit, OnDestroy, OnChanges, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; 9 | import { Observable } from 'rxjs/Observable'; 10 | import { Subscription } from 'rxjs/Subscription'; 11 | import { OdometerModel } from './odometer.model'; 12 | import { Ng2OdometerConfig, Ng2OdometerConfigModel } from './odometer.config'; 13 | import { 14 | CAR_THEME, 15 | DEFAULT_THEME, 16 | DIGITAL_THEME, 17 | MINIMAL_THEME, 18 | PLAZA_THEME, 19 | SLOT_MACHINE_THEME, 20 | TRAIN_STATION_THEME, 21 | } from './themes'; 22 | 23 | // HubSpot's Oodometer 24 | // https://github.com/HubSpot/odometer 25 | const Odometer = require('odometer'); 26 | 27 | @Component({ 28 | selector: 'ng2-odometer', 29 | encapsulation: ViewEncapsulation.None, 30 | styles: [ 31 | CAR_THEME, 32 | DEFAULT_THEME, 33 | DIGITAL_THEME, 34 | MINIMAL_THEME, 35 | PLAZA_THEME, 36 | SLOT_MACHINE_THEME, 37 | TRAIN_STATION_THEME, 38 | ` 39 | .odometer, 40 | .odometer-inside, 41 | .odometer-digit, 42 | .odometer-digit-spacer, 43 | .odometer-digit-inner, 44 | .odometer-ribbon, 45 | .odometer-ribbon-inner, 46 | .odometer-value, 47 | .odometer-formatting-mark { 48 | color: inherit; 49 | font-size: inherit; 50 | font-family: inherit; 51 | } 52 | `, 53 | ], 54 | template: `
` 55 | }) 56 | export class Ng2OdometerComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { 57 | private subscription: Subscription; 58 | private odometer: OdometerModel; 59 | @ViewChild('container', { read: ElementRef }) container: ElementRef; 60 | @Input() number: number; // Required 61 | @Input() config: Ng2OdometerConfigModel = {}; 62 | @Input() observable: Observable = undefined; 63 | 64 | // Individual configuration attributes 65 | @Input() animation: string = undefined; 66 | @Input() format: string = undefined; 67 | @Input() theme: string = undefined; 68 | @Input() value: number = undefined; 69 | @Input() duration: number = undefined; 70 | @Input() auto: boolean = undefined; 71 | 72 | // Available themes 73 | private themes: Array = [ 74 | 'car', 75 | 'default', 76 | 'digital', 77 | 'minimal', 78 | 'plaza', 79 | 'slot-machine', 80 | 'train-station' 81 | ]; 82 | 83 | // Start Odometer 84 | private initOdometer() { 85 | if (!_.isUndefined(this.container) 86 | && typeof Odometer !== 'undefined') { 87 | 88 | this.odometer = new Odometer({ 89 | el: this.container.nativeElement, 90 | animation: this.config.animation, 91 | value: this.config.value, 92 | duration: this.config.duration, 93 | format: this.config.format, 94 | theme: this.config.theme, 95 | }); 96 | 97 | if (!_.isUndefined(this.number) && this.config.auto) { 98 | this.odometer.update(this.number); 99 | } 100 | } 101 | } 102 | 103 | private initConfig() { 104 | this.config = _.defaults(this.config, new Ng2OdometerConfig()); 105 | 106 | // Animation 107 | if (!_.isUndefined(this.animation)) { 108 | this.config.animation = this.animation; 109 | } 110 | 111 | // Format 112 | if (!_.isUndefined(this.format)) { 113 | this.config.format = this.format; 114 | } 115 | 116 | // Theme 117 | if (!_.isUndefined(this.theme)) { 118 | this.config.theme = !_.includes(this.themes, this.theme) ? 'default' : this.theme; 119 | } 120 | 121 | // Value 122 | if (!_.isUndefined(this.value)) { 123 | this.config.value = this.value; 124 | } 125 | 126 | // Duration 127 | if (!_.isUndefined(this.duration)) { 128 | this.config.duration = this.duration; 129 | } 130 | 131 | // Auto 132 | if (!_.isUndefined(this.auto)) { 133 | this.config.auto = this.auto; 134 | } 135 | 136 | // Validate theme. If not part of the 137 | // available themes array, use the default 138 | if (!_.includes(this.themes, this.config.theme)) { 139 | this.config.theme = 'default'; 140 | } 141 | } 142 | 143 | // *************************************** 144 | // LIFECYCLES 145 | // *************************************** 146 | 147 | public ngOnInit() { 148 | 149 | // Bind Observable 150 | if (!_.isUndefined(this.observable) && !this.config.auto) { 151 | this.subscription = this.observable.subscribe((trigger: boolean) => { 152 | if (!_.isUndefined(trigger) && trigger) { 153 | this.odometer.update(this.number); 154 | } 155 | }); 156 | } 157 | 158 | // Apply defaults and 159 | // individual configurations 160 | this.initConfig(); 161 | } 162 | 163 | public ngOnDestroy() { 164 | if (!_.isUndefined(this.subscription)) { 165 | this.subscription.unsubscribe(); 166 | } 167 | } 168 | 169 | public ngOnChanges() { 170 | if (!_.isUndefined(this.number) && !_.isUndefined(this.odometer) && this.config.auto) { 171 | this.odometer.update(this.number); 172 | } 173 | } 174 | 175 | public ngAfterViewInit() { 176 | this.initOdometer(); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ng2-odometer [![npm version](https://img.shields.io/npm/v/ng2-odometer.svg?style=flat)](https://www.npmjs.com/package/ng2-odometer) [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) 2 | 3 | Odometer for Angular2 that wraps HubSpot's Odometer [http://github.hubspot.com/odometer/docs/welcome/](http://github.hubspot.com/odometer/docs/welcome/) 4 | 5 | ## Quick Start 6 | 7 | ``` 8 | npm install ng2-odometer --save 9 | ``` 10 | 11 | ## Table of contents 12 | 13 | - [Setup](#setup) 14 | - [Usage](#usage) 15 | - [Configuration](#configuration) 16 | - [Demo](#demo) 17 | 18 | ## Setup 19 | 20 | First you need to install the npm module: 21 | ```sh 22 | npm install ng2-odometer --save 23 | ``` 24 | 25 | Then add the `Ng2OdometerModule` to the imports array of your application module. 26 | 27 | ```typescript 28 | import { NgModule } from '@angular/core'; 29 | import { BrowserModule } from '@angular/platform-browser'; 30 | import { Ng2OdometerModule } from 'ng2-odometer'; // <-- import the module 31 | import { AppComponent} from './app.component'; 32 | 33 | @NgModule({ 34 | imports: [ 35 | BrowserModule, 36 | Ng2OdometerModule.forRoot() // <-- include it in your app module 37 | ], 38 | declarations: [AppComponent], 39 | bootstrap: [AppComponent] 40 | }) 41 | export class AppModule { 42 | // 43 | } 44 | ``` 45 | 46 | ## Usage 47 | 48 | Use the `` component to create an odometer. The `number` is required attribute. 49 | The `number` represents the limit at which the odometer will travel. The `config` an object with the configuration properties, this is NOT required. 50 | 51 | ```js 52 | @Component({ 53 | selector: 'main-element', 54 | template: ` 55 | ... 56 | 57 | 58 | ... 59 | ` 60 | }) 61 | export class MainElementComponent { 62 | public number: number = 1000; 63 | } 64 | ``` 65 | 66 | When on manual mode (`[config]="{ auto: false }"`), you can update the `number` attribute and that will trigger an odometer update right away. The `observable` is an Observable which will be used as a trigger for the odometer when on manual mode. 67 | 68 | ```js 69 | @Component({ 70 | selector: 'main-element', 71 | template: ` 72 | ... 73 | 74 | 75 | ... 76 | ` 77 | }) 78 | export class MainElementComponent { 79 | public number: number = 1000; 80 | public observable: Observable; 81 | private observer: Observer; 82 | 83 | constructor() { 84 | this.observable = new Observable((observer: any) => this.observer = observer).share(); 85 | 86 | // Trigger odometer after 2s 87 | setTimeout(() => this.observer.next(true), 2000); 88 | } 89 | } 90 | ``` 91 | 92 | ## Configuration 93 | 94 | The component accepts either a `[config]="{ ... }"` attribute with an object with all the configurable attribues or independent attributes for each configuration. 95 | 96 | | Option | Type | Default | Description | 97 | | --------------| --------- | ----------- |-------------- | 98 | | `animation` | string | 'slide' | Animation effect type.
Options: 'slide', 'count' 99 | | `format` | string | '(,ddd)' | Format to apply on the numbers.
Format - Example:
(,ddd) - 12,345,678
(,ddd).dd - 12,345,678.09
(.ddd),dd - 12.345.678,09
( ddd),dd - 12 345 678,09
d - 12345678 100 | | `theme` | string | 'default' | The desired theme.
Options: 'default', 'minima', 'digital', 'car', 'plaza', 'slot-machine', 'train-station' 101 | | `value` | number | 0 | Initial value of the odometer 102 | | `auto` | boolean | true | Setup auto or manual mode for the odometer 103 | 104 | ```js 105 | @Component({ 106 | selector: 'main-element', 107 | template: ` 108 | ... 109 | 113 | 114 | 115 | 119 | 120 | 121 | 122 | 130 | 131 | 132 | ... 133 | ` 134 | }) 135 | export class MainElementComponent { 136 | public config = { 137 | animation: 'count', 138 | format: 'd', 139 | theme: 'car', 140 | value: 50, 141 | auto: true, 142 | } 143 | 144 | ... 145 | } 146 | ``` 147 | 148 | If you add both, the `[config]` and any independent configuration, the independent config will overwrite the `[config]` object. 149 | 150 | ## Demo 151 | 152 | The [demo](demo) subfolder contains a project created with angular-cli that has been adapted to showcase the functionality of ng2-odometer. 153 | To execute the code follow this steps: 154 | 155 | ``` 156 | // Go the the demo folder 157 | cd demo 158 | 159 | // Install dependencies 160 | npm install 161 | 162 | // Run the server 163 | ng serve 164 | ``` 165 | 166 | Then go to [http://localhost:4200](http://localhost:4200/) to check the demo running. 167 | 168 | ## TODO: 169 | 170 | * Update to Angular4 171 | * Add tests to the library and demo 172 | * Add new themes 173 | * Create a Directive also 174 | 175 | ## License 176 | 177 | [MIT](LICENSE) 178 | -------------------------------------------------------------------------------- /src/odometer/themes/train-station.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const TRAIN_STATION_THEME = ` 6 | @import url("//fonts.googleapis.com/css?family=Economica"); 7 | .odometer.odometer-auto-theme, 8 | .odometer.odometer-theme-train-station { 9 | display: inline-block; 10 | vertical-align: middle; 11 | *vertical-align: auto; 12 | *zoom: 1; 13 | *display: inline; 14 | position: relative; 15 | } 16 | .odometer.odometer-auto-theme .odometer-digit, 17 | .odometer.odometer-theme-train-station .odometer-digit { 18 | display: inline-block; 19 | vertical-align: middle; 20 | *vertical-align: auto; 21 | *zoom: 1; 22 | *display: inline; 23 | position: relative; 24 | } 25 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 26 | .odometer.odometer-theme-train-station .odometer-digit .odometer-digit-spacer { 27 | display: inline-block; 28 | vertical-align: middle; 29 | *vertical-align: auto; 30 | *zoom: 1; 31 | *display: inline; 32 | visibility: hidden; 33 | } 34 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 35 | .odometer.odometer-theme-train-station .odometer-digit .odometer-digit-inner { 36 | text-align: left; 37 | display: block; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | overflow: hidden; 44 | } 45 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 46 | .odometer.odometer-theme-train-station .odometer-digit .odometer-ribbon { 47 | display: block; 48 | } 49 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 50 | .odometer.odometer-theme-train-station .odometer-digit .odometer-ribbon-inner { 51 | display: block; 52 | -webkit-backface-visibility: hidden; 53 | } 54 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 55 | .odometer.odometer-theme-train-station .odometer-digit .odometer-value { 56 | display: block; 57 | -webkit-transform: translateZ(0); 58 | } 59 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 60 | .odometer.odometer-theme-train-station .odometer-digit .odometer-value.odometer-last-value { 61 | position: absolute; 62 | } 63 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 64 | .odometer.odometer-theme-train-station.odometer-animating-up .odometer-ribbon-inner { 65 | -webkit-transition: -webkit-transform 2s; 66 | -moz-transition: -moz-transform 2s; 67 | -ms-transition: -ms-transform 2s; 68 | -o-transition: -o-transform 2s; 69 | transition: transform 2s; 70 | } 71 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 72 | .odometer.odometer-theme-train-station.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 73 | -webkit-transform: translateY(-100%); 74 | -moz-transform: translateY(-100%); 75 | -ms-transform: translateY(-100%); 76 | -o-transform: translateY(-100%); 77 | transform: translateY(-100%); 78 | } 79 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 80 | .odometer.odometer-theme-train-station.odometer-animating-down .odometer-ribbon-inner { 81 | -webkit-transform: translateY(-100%); 82 | -moz-transform: translateY(-100%); 83 | -ms-transform: translateY(-100%); 84 | -o-transform: translateY(-100%); 85 | transform: translateY(-100%); 86 | } 87 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 88 | .odometer.odometer-theme-train-station.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 89 | -webkit-transition: -webkit-transform 2s; 90 | -moz-transition: -moz-transform 2s; 91 | -ms-transition: -ms-transform 2s; 92 | -o-transition: -o-transform 2s; 93 | transition: transform 2s; 94 | -webkit-transform: translateY(0); 95 | -moz-transform: translateY(0); 96 | -ms-transform: translateY(0); 97 | -o-transform: translateY(0); 98 | transform: translateY(0); 99 | } 100 | .odometer.odometer-auto-theme, 101 | .odometer.odometer-theme-train-station { 102 | font-family: "Economica", sans-serif; 103 | } 104 | .odometer.odometer-auto-theme .odometer-digit, 105 | .odometer.odometer-theme-train-station .odometer-digit { 106 | display: inline-block; 107 | vertical-align: middle; 108 | *vertical-align: auto; 109 | *zoom: 1; 110 | *display: inline; 111 | -moz-border-radius: 0.1em; 112 | -webkit-border-radius: 0.1em; 113 | border-radius: 0.1em; 114 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzExMTExMSIvPjxzdG9wIG9mZnNldD0iMzUlIiBzdG9wLWNvbG9yPSIjMTExMTExIi8+PHN0b3Agb2Zmc2V0PSI1NSUiIHN0b3AtY29sb3I9IiMzMzMzMzMiLz48c3RvcCBvZmZzZXQ9IjU1JSIgc3RvcC1jb2xvcj0iIzExMTExMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzExMTExMSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); 115 | background-size: 100%; 116 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #111111), color-stop(35%, #111111), color-stop(55%, #333333), color-stop(55%, #111111), color-stop(100%, #111111)); 117 | background-image: -moz-linear-gradient(top, #111111 0%, #111111 35%, #333333 55%, #111111 55%, #111111 100%); 118 | background-image: -webkit-linear-gradient(top, #111111 0%, #111111 35%, #333333 55%, #111111 55%, #111111 100%); 119 | background-image: linear-gradient(to bottom, #111111 0%, #111111 35%, #333333 55%, #111111 55%, #111111 100%); 120 | background-color: #222; 121 | padding: 0 0.15em; 122 | color: #fff; 123 | } 124 | .odometer.odometer-auto-theme .odometer-digit + .odometer-digit, 125 | .odometer.odometer-theme-train-station .odometer-digit + .odometer-digit { 126 | margin-left: 0.1em; 127 | } 128 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 129 | .odometer.odometer-theme-train-station .odometer-digit .odometer-digit-inner { 130 | left: 0.15em; 131 | } 132 | `; 133 | -------------------------------------------------------------------------------- /src/odometer/themes/car.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const CAR_THEME = ` 6 | @import url("//fonts.googleapis.com/css?family=Arimo"); 7 | .odometer.odometer-auto-theme, 8 | .odometer.odometer-theme-car { 9 | display: inline-block; 10 | vertical-align: middle; 11 | *vertical-align: auto; 12 | *zoom: 1; 13 | *display: inline; 14 | position: relative; 15 | } 16 | .odometer.odometer-auto-theme .odometer-digit, 17 | .odometer.odometer-theme-car .odometer-digit { 18 | display: inline-block; 19 | vertical-align: middle; 20 | *vertical-align: auto; 21 | *zoom: 1; 22 | *display: inline; 23 | position: relative; 24 | } 25 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 26 | .odometer.odometer-theme-car .odometer-digit .odometer-digit-spacer { 27 | display: inline-block; 28 | vertical-align: middle; 29 | *vertical-align: auto; 30 | *zoom: 1; 31 | *display: inline; 32 | visibility: hidden; 33 | } 34 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 35 | .odometer.odometer-theme-car .odometer-digit .odometer-digit-inner { 36 | text-align: left; 37 | display: block; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | overflow: hidden; 44 | } 45 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 46 | .odometer.odometer-theme-car .odometer-digit .odometer-ribbon { 47 | display: block; 48 | } 49 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 50 | .odometer.odometer-theme-car .odometer-digit .odometer-ribbon-inner { 51 | display: block; 52 | -webkit-backface-visibility: hidden; 53 | } 54 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 55 | .odometer.odometer-theme-car .odometer-digit .odometer-value { 56 | display: block; 57 | -webkit-transform: translateZ(0); 58 | } 59 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 60 | .odometer.odometer-theme-car .odometer-digit .odometer-value.odometer-last-value { 61 | position: absolute; 62 | } 63 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 64 | .odometer.odometer-theme-car.odometer-animating-up .odometer-ribbon-inner { 65 | -webkit-transition: -webkit-transform 2s; 66 | -moz-transition: -moz-transform 2s; 67 | -ms-transition: -ms-transform 2s; 68 | -o-transition: -o-transform 2s; 69 | transition: transform 2s; 70 | } 71 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 72 | .odometer.odometer-theme-car.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 73 | -webkit-transform: translateY(-100%); 74 | -moz-transform: translateY(-100%); 75 | -ms-transform: translateY(-100%); 76 | -o-transform: translateY(-100%); 77 | transform: translateY(-100%); 78 | } 79 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 80 | .odometer.odometer-theme-car.odometer-animating-down .odometer-ribbon-inner { 81 | -webkit-transform: translateY(-100%); 82 | -moz-transform: translateY(-100%); 83 | -ms-transform: translateY(-100%); 84 | -o-transform: translateY(-100%); 85 | transform: translateY(-100%); 86 | } 87 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 88 | .odometer.odometer-theme-car.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 89 | -webkit-transition: -webkit-transform 2s; 90 | -moz-transition: -moz-transform 2s; 91 | -ms-transition: -ms-transform 2s; 92 | -o-transition: -o-transform 2s; 93 | transition: transform 2s; 94 | -webkit-transform: translateY(0); 95 | -moz-transform: translateY(0); 96 | -ms-transform: translateY(0); 97 | -o-transform: translateY(0); 98 | transform: translateY(0); 99 | } 100 | .odometer.odometer-auto-theme, 101 | .odometer.odometer-theme-car { 102 | -moz-border-radius: 0.34em; 103 | -webkit-border-radius: 0.34em; 104 | border-radius: 0.34em; 105 | font-family: "Arimo", monospace; 106 | padding: 0.15em; 107 | background: #000; 108 | color: #eee0d3; 109 | } 110 | .odometer.odometer-auto-theme .odometer-digit, 111 | .odometer.odometer-theme-car .odometer-digit { 112 | -moz-box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); 113 | -webkit-box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); 114 | box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); 115 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjMzMzMzMzIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiMxMDEwMTAiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); 116 | background-size: 100%; 117 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #333333), color-stop(40%, #333333), color-stop(60%, #101010), color-stop(80%, #333333), color-stop(100%, #333333)); 118 | background-image: -moz-linear-gradient(top, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); 119 | background-image: -webkit-linear-gradient(top, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); 120 | background-image: linear-gradient(to bottom, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); 121 | padding: 0 0.15em; 122 | } 123 | .odometer.odometer-auto-theme .odometer-digit:first-child, 124 | .odometer.odometer-theme-car .odometer-digit:first-child { 125 | -moz-border-radius: 0.2em 0 0 0.2em; 126 | -webkit-border-radius: 0.2em; 127 | border-radius: 0.2em 0 0 0.2em; 128 | } 129 | .odometer.odometer-auto-theme .odometer-digit:last-child, 130 | .odometer.odometer-theme-car .odometer-digit:last-child { 131 | -moz-border-radius: 0 0.2em 0.2em 0; 132 | -webkit-border-radius: 0; 133 | border-radius: 0 0.2em 0.2em 0; 134 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjZWVlMGQzIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiNiYmFhOWEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); 135 | background-size: 100%; 136 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #eee0d3), color-stop(40%, #eee0d3), color-stop(60%, #bbaa9a), color-stop(80%, #eee0d3), color-stop(100%, #eee0d3)); 137 | background-image: -moz-linear-gradient(top, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); 138 | background-image: -webkit-linear-gradient(top, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); 139 | background-image: linear-gradient(to bottom, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); 140 | background-color: #eee0d3; 141 | color: #000; 142 | } 143 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 144 | .odometer.odometer-theme-car .odometer-digit .odometer-digit-inner { 145 | left: 0.15em; 146 | } 147 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 148 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 149 | .odometer.odometer-theme-car.odometer-animating-up .odometer-ribbon-inner, 150 | .odometer.odometer-theme-car.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 151 | -webkit-transition-timing-function: linear; 152 | -moz-transition-timing-function: linear; 153 | -ms-transition-timing-function: linear; 154 | -o-transition-timing-function: linear; 155 | transition-timing-function: linear; 156 | } 157 | `; 158 | -------------------------------------------------------------------------------- /src/odometer/themes/slot-machine.theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jose Andres on 02.23.17 3 | */ 4 | 5 | export const SLOT_MACHINE_THEME = ` 6 | @import url("//fonts.googleapis.com/css?family=Rye"); 7 | .odometer.odometer-auto-theme, 8 | .odometer.odometer-theme-slot-machine { 9 | display: inline-block; 10 | vertical-align: middle; 11 | *vertical-align: auto; 12 | *zoom: 1; 13 | *display: inline; 14 | position: relative; 15 | } 16 | .odometer.odometer-auto-theme .odometer-digit, 17 | .odometer.odometer-theme-slot-machine .odometer-digit { 18 | display: inline-block; 19 | vertical-align: middle; 20 | *vertical-align: auto; 21 | *zoom: 1; 22 | *display: inline; 23 | position: relative; 24 | } 25 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, 26 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-digit-spacer { 27 | display: inline-block; 28 | vertical-align: middle; 29 | *vertical-align: auto; 30 | *zoom: 1; 31 | *display: inline; 32 | visibility: hidden; 33 | } 34 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 35 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-digit-inner { 36 | text-align: left; 37 | display: block; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | overflow: hidden; 44 | } 45 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, 46 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-ribbon { 47 | display: block; 48 | } 49 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, 50 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-ribbon-inner { 51 | display: block; 52 | -webkit-backface-visibility: hidden; 53 | } 54 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, 55 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-value { 56 | display: block; 57 | -webkit-transform: translateZ(0); 58 | } 59 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 60 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-value.odometer-last-value { 61 | position: absolute; 62 | } 63 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, 64 | .odometer.odometer-theme-slot-machine.odometer-animating-up .odometer-ribbon-inner { 65 | -webkit-transition: -webkit-transform 2s; 66 | -moz-transition: -moz-transform 2s; 67 | -ms-transition: -ms-transform 2s; 68 | -o-transition: -o-transform 2s; 69 | transition: transform 2s; 70 | } 71 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, 72 | .odometer.odometer-theme-slot-machine.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 73 | -webkit-transform: translateY(-100%); 74 | -moz-transform: translateY(-100%); 75 | -ms-transform: translateY(-100%); 76 | -o-transform: translateY(-100%); 77 | transform: translateY(-100%); 78 | } 79 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, 80 | .odometer.odometer-theme-slot-machine.odometer-animating-down .odometer-ribbon-inner { 81 | -webkit-transform: translateY(-100%); 82 | -moz-transform: translateY(-100%); 83 | -ms-transform: translateY(-100%); 84 | -o-transform: translateY(-100%); 85 | transform: translateY(-100%); 86 | } 87 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, 88 | .odometer.odometer-theme-slot-machine.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 89 | -webkit-transition: -webkit-transform 2s; 90 | -moz-transition: -moz-transform 2s; 91 | -ms-transition: -ms-transform 2s; 92 | -o-transition: -o-transform 2s; 93 | transition: transform 2s; 94 | -webkit-transform: translateY(0); 95 | -moz-transform: translateY(0); 96 | -ms-transform: translateY(0); 97 | -o-transform: translateY(0); 98 | transform: translateY(0); 99 | } 100 | .odometer.odometer-auto-theme, 101 | .odometer.odometer-theme-slot-machine { 102 | -moz-border-radius: 0.34em; 103 | -webkit-border-radius: 0.34em; 104 | border-radius: 0.34em; 105 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmYwMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmYTUwMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); 106 | background-size: 100%; 107 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffff00), color-stop(100%, #ffa500)); 108 | background-image: -moz-linear-gradient(#ffff00, #ffa500); 109 | background-image: -webkit-linear-gradient(#ffff00, #ffa500); 110 | background-image: linear-gradient(#ffff00, #ffa500); 111 | background-color: #fc0; 112 | font-family: "Rye", monospace; 113 | padding: 0.15em; 114 | color: #f80000; 115 | line-height: 1.35em; 116 | border: 0.03em solid #000; 117 | -webkit-text-stroke: 0.05em #000; 118 | } 119 | .odometer.odometer-auto-theme .odometer-digit, 120 | .odometer.odometer-theme-slot-machine .odometer-digit { 121 | -moz-box-shadow: inset 0 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 122 | -webkit-box-shadow: inset 0 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 123 | box-shadow: inset 0 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 124 | -moz-border-radius: 0.2em; 125 | -webkit-border-radius: 0.2em; 126 | border-radius: 0.2em; 127 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2NjY2NjYyIvPjxzdG9wIG9mZnNldD0iMjAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNjY2NjY2MiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); 128 | background-size: 100%; 129 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cccccc), color-stop(20%, #ffffff), color-stop(80%, #ffffff), color-stop(100%, #cccccc)); 130 | background-image: -moz-linear-gradient(top, #cccccc 0%, #ffffff 20%, #ffffff 80%, #cccccc 100%); 131 | background-image: -webkit-linear-gradient(top, #cccccc 0%, #ffffff 20%, #ffffff 80%, #cccccc 100%); 132 | background-image: linear-gradient(to bottom, #cccccc 0%, #ffffff 20%, #ffffff 80%, #cccccc 100%); 133 | border: 0.03em solid #444; 134 | padding: 0.1em 0.15em 0; 135 | } 136 | .odometer.odometer-auto-theme .odometer-digit:first-child, 137 | .odometer.odometer-theme-slot-machine .odometer-digit:first-child { 138 | -moz-box-shadow: inset 0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 139 | -webkit-box-shadow: inset 0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 140 | box-shadow: inset 0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 141 | } 142 | .odometer.odometer-auto-theme .odometer-digit:last-child, 143 | .odometer.odometer-theme-slot-machine .odometer-digit:last-child { 144 | -moz-box-shadow: inset -0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 145 | -webkit-box-shadow: inset -0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 146 | box-shadow: inset -0.05em 0 0.1em rgba(0, 0, 0, 0.5), 0 0 0 0.03em #fff, 0 0 0 0.05em rgba(0, 0, 0, 0.2); 147 | } 148 | .odometer.odometer-auto-theme .odometer-digit + .odometer-digit, 149 | .odometer.odometer-theme-slot-machine .odometer-digit + .odometer-digit { 150 | margin-left: 0.15em; 151 | } 152 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 153 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-digit-inner { 154 | padding-top: 0.08em; 155 | } 156 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, 157 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, 158 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-digit-inner, 159 | .odometer.odometer-theme-slot-machine .odometer-digit .odometer-value.odometer-last-value { 160 | left: 0; 161 | right: 0; 162 | text-align: center; 163 | } 164 | `; 165 | --------------------------------------------------------------------------------