From b798a3e1e7a9e7f36e738c139238945959aebd34 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 17:22:07 +0800 Subject: [PATCH 1/7] release: 8.0.0 - close #36, #37, #27, #16, #33, #28, #34, #32, #35, #38, #39 --- .editorconfig | 4 + .prettierrc | 13 +- .vscode/settings.json | 31 ++ README.md | 90 ++--- angular.json | 30 +- lib/index.ts | 4 - lib/karma.conf.js | 5 +- lib/spec/component.spec.ts | 273 ++++++-------- lib/src/countdown.component.ts | 293 +++++---------- lib/src/countdown.config.ts | 26 +- lib/src/countdown.module.ts | 13 +- lib/src/countdown.timer.ts | 33 +- lib/src/index.ts | 1 + lib/src/interfaces.ts | 67 ++-- lib/src/public_api.ts | 5 + lib/test.ts | 10 +- lib/tsconfig.lib.json | 2 +- lib/tsconfig.spec.json | 7 +- package.json | 70 ++-- src/app/app.component.ts | 9 +- src/app/app.module.ts | 30 +- src/app/components/alot.component.html | 419 +--------------------- src/app/components/alot.component.ts | 4 +- src/app/components/demo.component.html | 58 ++- src/app/components/demo.component.scss | 20 +- src/app/components/demo.component.ts | 81 +++-- src/app/components/layout.component.ts | 47 +-- src/app/components/nothing.component.html | 6 +- src/app/components/tpl.component.html | 14 - src/app/components/tpl.component.scss | 3 - src/app/components/tpl.component.ts | 9 - src/app/tpl/flip/flip.component.html | 17 - src/app/tpl/flip/flip.component.scss | 90 ----- src/app/tpl/flip/flip.component.ts | 38 -- src/index.html | 39 +- src/tsconfig.json | 35 +- tsconfig.json | 26 +- tslint.json | 170 +++++---- 38 files changed, 726 insertions(+), 1366 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 lib/index.ts create mode 100644 lib/src/index.ts create mode 100644 lib/src/public_api.ts delete mode 100644 src/app/components/tpl.component.html delete mode 100644 src/app/components/tpl.component.scss delete mode 100644 src/app/components/tpl.component.ts delete mode 100644 src/app/tpl/flip/flip.component.html delete mode 100644 src/app/tpl/flip/flip.component.scss delete mode 100644 src/app/tpl/flip/flip.component.ts diff --git a/.editorconfig b/.editorconfig index 9099689..f1cc3ad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,3 +9,7 @@ indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.prettierrc b/.prettierrc index 72f7c03..0690a16 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,14 @@ { - "printWidth": 80, + "printWidth": 120, "singleQuote": true, - "trailingComma": "all" + "semi": true, + "trailingComma": "all", + "overrides": [ + { + "files": ".prettierrc", + "options": { + "parser": "json" + } + } + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d7ca686 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,31 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/*/**": true, + "**/dist/*/**": true, + "**/coverage/*/**": true + }, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + }, + "[markdown]": { + "editor.formatOnSave": false + }, + "[javascript]": { + "editor.formatOnSave": false + }, + "[json]": { + "editor.formatOnSave": false + }, + "[jsonc]": { + "editor.formatOnSave": false + }, + "files.associations": { + "*.json": "jsonc", + ".prettierrc": "json", + ".stylelintrc": "json" + } +} diff --git a/README.md b/README.md index 38c4fab..f04c9c6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ Simple, easy and performance countdown for angular [![NPM version](https://img.shields.io/npm/v/ngx-countdown.svg)](https://www.npmjs.com/package/ngx-countdown) [![Build Status](https://travis-ci.org/cipchk/ngx-countdown.svg?branch=master)](https://travis-ci.org/cipchk/ngx-countdown) -[![codecov](https://codecov.io/gh/cipchk/ngx-countdown/branch/master/graph/badge.svg)](https://codecov.io/gh/cipchk/ngx-countdown) ## Demo @@ -25,9 +24,9 @@ import `CountdownModule`。 import { CountdownModule } from 'ngx-countdown'; @NgModule({ - imports: [ BrowserModule, CountdownModule ], - declarations: [AppComponent], - bootstrap: [AppComponent] + imports: [ BrowserModule, CountdownModule ], + declarations: [AppComponent], + bootstrap: [AppComponent] }) export class AppModule { } ``` @@ -35,70 +34,73 @@ export class AppModule { } ### 2、Template ```html - + ``` -| Name | Type | Default | Summary | -| ------- | ------------- | ----- | ----- | -| `config` | Config | - | see Config | -| `begin()` | - | - | Triggers when `{demand: false}` | -| `restart()` | - | - | - | -| `stop()` | - | - | - | -| `pause()` | - | - | - | -| `resume()` | - | - | - | -| `start` | `EventEmitter` | - | Triggers when start | -| `finished` | `EventEmitter` | - | Triggers when finished | -| `notify` | `EventEmitter(time: number)` | - | Triggers when notify, need setting `config.notify` values | -| `event` | `EventEmitter<{ action: string, left: number }>` | - | Catch all event | +**Method** + +| Name | Description | +|-------------|------------------------------------------------------------------------------------------------| +| `begin()` | Start countdown, you must manually call when `demand: false` | +| `restart()` | Restart countdown | +| `stop()` | Stop countdown, must call `restart` when stopped, it's different from pause, unable to recover | +| `pause()` | Pause countdown, you can use `resume` to recover again | +| `resume()` | Resume countdown | **How call component methods** -```typescript -@ViewChild(CountdownComponent) counter: CountdownComponent; -resetTimer(){ - this.counter.restart(); - this.counter.stop(); - this.counter.pause(); - this.counter.resume(); -} +```ts +@ViewChild('cd', { static: false }) private countdown: CountdownComponent; +this.countdown.begin(); ``` -## Config +## API + +### countdown + +| Name | Type | Default | Summary | +|----------|--------------------------------|---------|---------| +| `config` | `CountdownConfig` | - | Config | +| `event` | `EventEmitter` | - | Events | + +### CountdownConfig | Name | Type | Default | Summary | | ------- | ------------- | ----- | ----- | -| demand | boolean | `false` | start the counter on demand, must call `begin()` to starting | -| template | string | `$!h!时$!m!分$!s!秒` | Custom render template, if is empty use the `` content, and `$!s-ext!` it's `0.1s` accuracy | -| leftTime | number | 0 | Calculate the remaining time based on the server, e.g: `10`,`5.5`(May be dropped frames) (Unit: seconds) | -| stopTime | number | 0 | 结束时间:指的是根据本地时间至结束时间进行倒计时。(单位:UNIX时间戳 ms) | -| varRegular | RegExp | `/\$\{([\-\w]+)\}/g` | 模板解析正则表达式,有时候由于模板结构比较特殊,无法根据默认的表达式进行解析,那就需要修改它。 | -| clock | Array | | 时钟控制数组,特殊需求时可以修改,里面是三元组:指针名、进制、位数,可参考大于99小时demo | -| notify | number[] | | 第xx秒时调用 notify 函数,值必须是**正整数** | -| repaint | Function | | Custom repaintes | +| demand | `boolean` | `false` | Start the counter on demand, must call `begin()` to starting | +| leftTime | `number` | `0` | Calculate the remaining time based on the server, e.g: `10`,`5.5`, (Unit: seconds) | +| stopTime | `number` | - | Refers to counting down from local time to end time (Unit: Milliseconds second UNIX timestamp) | +| format | `string` | `HH:mm:ss` | Formats a date value, pls refer to [Accepted patterns](https://angular.io/api/common/DatePipe#usage-notes) | +| prettyText | `(text: string) => string` | - | Beautify text, generally used to convert formatted time text into HTML | +| notify | `number[] | number` | - | Should be trigger type `notify` event on the x second. When values is `0` will be trigger every time | +| formatDate | `CountdownFormatFn` | - | Default based on the implementation of `formatDate` in `@angular/common`, You can changed to other libs, e.g: [date-fns](https://date-fns.org/v2.0.1/docs/format) | +| timezone | `string` | `+0000` | A timezone offset (such as '+0430'), or a standard UTC/GMT. When not supplied, uses the end-user's local system timezone | + +### CountdownEvent + +| Name | Type | Summary | +|----------|-----------------------------------------------|----------------------------------| +| `action` | `start,stop,restart,pause,resume,notify,done` | Action of the event | +| `status` | `CountdownStatus` | Status of the countdown | +| `left` | `number` | Number of remaining milliseconds | +| `text` | `string` | Format the text | **Global Config** ```ts -function countdownConfigFactory(): Config { - return { template: `$!h!:$!m!:$!s!` }; +function countdownConfigFactory(): CountdownGlobalConfig { + return { format: `mm:ss` }; } @NgModule({ imports: [ CountdownModule ], providers: [ - { provide: CountdownConfig, useFactory: countdownConfigFactory } + { provide: CountdownGlobalConfig, useFactory: countdownConfigFactory } ], }) export class AppDemoModule {} ``` -## About repaints - -The timer will call repaint function every time, if it's `0.1s` accuracy, it will be more frequent. so you can make same special effects, like [Flip](https://cipchk.github.io/ngx-countdown/#/tpl/flip). - ## Troubleshooting Please follow this guidelines when reporting bugs and feature requests: diff --git a/angular.json b/angular.json index 8cd49b1..63b63f6 100644 --- a/angular.json +++ b/angular.json @@ -18,8 +18,7 @@ "polyfills": "src/polyfills.ts", "assets": ["src/assets"], "styles": [ - "node_modules/bootstrap/dist/css/bootstrap.css", - "node_modules/ngx-toastr/toastr.css" + "node_modules/bootstrap/dist/css/bootstrap.css" ], "scripts": [] }, @@ -54,12 +53,6 @@ } } }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "ngx-countdown:build" - } - }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { @@ -80,27 +73,6 @@ } } } - }, - "ngx-countdown-e2e": { - "root": "", - "sourceRoot": "", - "projectType": "application", - "architect": { - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "protractor.conf.js", - "devServerTarget": "ngx-countdown:serve" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [], - "exclude": ["**/node_modules/**"] - } - } - } } }, "defaultProject": "ngx-countdown", diff --git a/lib/index.ts b/lib/index.ts deleted file mode 100644 index b8d494d..0000000 --- a/lib/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './src/interfaces'; -export * from './src/countdown.component'; -export * from './src/countdown.timer'; -export * from './src/countdown.module'; diff --git a/lib/karma.conf.js b/lib/karma.conf.js index da01c56..af072fc 100644 --- a/lib/karma.conf.js +++ b/lib/karma.conf.js @@ -17,7 +17,7 @@ module.exports = function (config) { }, coverageIstanbulReporter: { dir: require('path').join(__dirname, '../coverage'), - dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly'], + reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, reporters: ['progress', 'kjhtml'], @@ -26,6 +26,7 @@ module.exports = function (config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], - singleRun: false + singleRun: false, + restartOnFileChange: true }); }; diff --git a/lib/spec/component.spec.ts b/lib/spec/component.spec.ts index 8c33d39..be6c539 100644 --- a/lib/spec/component.spec.ts +++ b/lib/spec/component.spec.ts @@ -1,21 +1,16 @@ // tslint:disable:no-use-before-declare import { Component, ViewChild, DebugElement } from '@angular/core'; -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, - discardPeriodicTasks, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { CountdownModule } from '../src/countdown.module'; -import { Config } from '../src/interfaces'; +import { CountdownConfig, CountdownEvent, CountdownStatus } from '../src/interfaces'; import { CountdownComponent } from '../src/countdown.component'; describe('Component: ngx-countdown', () => { let fixture: ComponentFixture; let context: TestNGComponent; let dl: DebugElement; + let spy: jasmine.Spy<(_e: CountdownEvent) => void>; beforeEach(() => { TestBed.configureTestingModule({ @@ -25,35 +20,25 @@ describe('Component: ngx-countdown', () => { fixture = TestBed.createComponent(TestNGComponent); context = fixture.componentInstance; dl = fixture.debugElement; - spyOn(context, 'start'); - spyOn(context, 'finished'); - spyOn(context, 'notify'); - spyOn(context, 'event'); + spy = spyOn(context, 'handleEvent'); }); - afterEach(() => context.comp.ngOnDestroy()); + afterEach(() => { + if (context.comp) context.comp.ngOnDestroy(); + }); function getSecond(): number { - const els = (dl.nativeElement as HTMLElement).querySelectorAll( - '.hand-s span', - ); - return +els[1].textContent; + return context.comp.i.value / 1000; } describe('[default]', () => { - it('fixture should not be null', () => { - fixture.detectChanges(); - expect(fixture).not.toBeNull(); - }); - it('should notify in 1s', (done: () => void) => { + it('should notify in 1s', fakeAsync(() => { context.config = { leftTime: 2, notify: [1] }; fixture.detectChanges(); - expect(context.notify).not.toHaveBeenCalled(); - setTimeout(() => { - expect(context.notify).toHaveBeenCalled(); - done(); - }, 1001); - }); + tick(1001); + expect(spy.calls.mostRecent().args[0].action).toBe('notify'); + tick(1000); + })); it('should be throw error when notify is not positive integer', () => { expect(() => { context.config = { leftTime: 2, notify: [0.1] }; @@ -63,9 +48,9 @@ describe('Component: ngx-countdown', () => { it('should be demand start', () => { context.config = { demand: true, leftTime: 1 }; fixture.detectChanges(); - expect(context.start).not.toHaveBeenCalled(); + expect(context.handleEvent).not.toHaveBeenCalled(); context.comp.begin(); - expect(context.start).toHaveBeenCalled(); + expect(spy.calls.first().args[0].status).toBe(CountdownStatus.ing); }); it('should be re-init when reassigning config value', () => { context.config = { leftTime: 2 }; @@ -75,205 +60,155 @@ describe('Component: ngx-countdown', () => { fixture.detectChanges(); expect(getSecond()).toBe(3); }); - it('should be custom render template', (done: () => void) => { - context.config = { leftTime: 2, template: '$!s-ext!s' }; + it('should be custom format', fakeAsync(() => { + context.config = { leftTime: 2, format: 'm' }; fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(1); - done(); - }, 250); - }); - it('should be custom repaint function', (done: () => void) => { - let callCount = 0; - context.config = { - leftTime: 2, - repaint: function() { - ++callCount; - }, - }; + tick(250); fixture.detectChanges(); - setTimeout(() => { - expect(callCount).toBeGreaterThan(0); - done(); - }, 250); - }); + expect(context.comp.i.text).toBe(`0`); + tick(2000); + })); }); describe('[actions]', () => { it('#begin', () => { context.config = { demand: true, leftTime: 1 }; fixture.detectChanges(); - expect(context.start).not.toHaveBeenCalled(); + expect(context.handleEvent).not.toHaveBeenCalled(); context.comp.begin(); - expect(context.start).toHaveBeenCalled(); + expect(spy.calls.first().args[0].status).toBe(CountdownStatus.ing); }); describe('#restart', () => { - it('normal', (done: () => void) => { + it('normal', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); - setTimeout(() => { - expect(getSecond()).toBe(1); - context.comp.restart(); - fixture.detectChanges(); - expect(getSecond()).toBe(2); - done(); - }, 1001); - }); - it('when stoped', (done: () => void) => { + tick(1001); + expect(getSecond()).toBe(1); + context.comp.restart(); + fixture.detectChanges(); + expect(getSecond()).toBe(2); + tick(3000); + })); + it('when stoped', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); context.comp.stop(); fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(2); - context.comp.restart(); - fixture.detectChanges(); - expect(getSecond()).toBe(2); - done(); - }, 1001); - }); + tick(1001); + expect(getSecond()).toBe(2); + context.comp.restart(); + fixture.detectChanges(); + expect(getSecond()).toBe(2); + tick(3000); + })); }); describe('#stop', () => { - it('normal', (done: () => void) => { + it('normal', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); - setTimeout(() => { - expect(getSecond()).toBe(1); - context.comp.stop(); - fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(1); - done(); - }, 1001); - }, 1001); - }); - it('when stoped', (done: () => void) => { + tick(1001); + expect(getSecond()).toBe(1); + context.comp.stop(); + fixture.detectChanges(); + tick(1001); + expect(getSecond()).toBe(1); + tick(3000); + })); + it('when stoped', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); context.comp.stop(); fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(2); - context.comp.stop(); - fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(2); - done(); - }, 1001); - }, 1001); - }); + tick(1001); + expect(getSecond()).toBe(2); + context.comp.stop(); + fixture.detectChanges(); + tick(1001); + expect(getSecond()).toBe(2); + tick(3000); + })); }); describe('#pause', () => { - it('normal', (done: () => void) => { + it('normal', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); - setTimeout(() => { - expect(getSecond()).toBe(1); - context.comp.pause(); - fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(1); - done(); - }, 1001); - }, 1001); - }); + tick(1001); + expect(getSecond()).toBe(1); + context.comp.pause(); + tick(1001); + expect(getSecond()).toBe(1); + context.comp.resume(); + tick(3000); + })); - it('when parsed', (done: () => void) => { + it('when parsed', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); context.comp.pause(); - fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(2); - context.comp.pause(); - fixture.detectChanges(); - setTimeout(() => { - expect(getSecond()).toBe(2); - done(); - }, 1001); - }, 1001); - }); + tick(1001); + expect(getSecond()).toBe(2); + context.comp.pause(); + tick(1001); + expect(getSecond()).toBe(2); + context.comp.resume(); + tick(3000); + })); }); describe('#resume', () => { - it('normal', (done: () => void) => { + it('normal', fakeAsync(() => { context.config = { leftTime: 3 }; fixture.detectChanges(); expect(getSecond()).toBe(3); - setTimeout(() => { - expect(getSecond()).toBe(2); - context.comp.pause(); - setTimeout(() => { - expect(getSecond()).toBe(2); - context.comp.resume(); - setTimeout(() => { - expect(getSecond()).toBe(1); - done(); - }, 1001); - }, 1001); - }, 1001); - }); - it('without pause', (done: () => void) => { + tick(1001); + expect(getSecond()).toBe(2); + context.comp.pause(); + tick(1001); + expect(getSecond()).toBe(2); + context.comp.resume(); + tick(1001); + expect(getSecond()).toBe(1); + context.comp.resume(); + tick(3000); + })); + it('without pause', fakeAsync(() => { context.config = { leftTime: 2 }; fixture.detectChanges(); expect(getSecond()).toBe(2); - setTimeout(() => { - expect(getSecond()).toBe(1); - context.comp.resume(); - fixture.detectChanges(); - expect(getSecond()).toBe(1); - done(); - }, 1001); - }); + tick(1001); + expect(getSecond()).toBe(1); + context.comp.resume(); + expect(getSecond()).toBe(1); + tick(3000); + })); }); }); describe('[events]', () => { - it('(start)', () => { - context.config = { demand: true, leftTime: 1 }; - fixture.detectChanges(); - expect(context.start).not.toHaveBeenCalled(); - expect(context.event).not.toHaveBeenCalled(); - context.comp.begin(); - expect(context.start).toHaveBeenCalled(); - expect(context.event).toHaveBeenCalled(); - }); - it('(finished)', (done: () => void) => { + it('(event)', () => { context.config = { demand: true, leftTime: 1 }; fixture.detectChanges(); - expect(context.finished).not.toHaveBeenCalled(); - expect(context.event).not.toHaveBeenCalled(); + expect(context.handleEvent).not.toHaveBeenCalled(); context.comp.begin(); - setTimeout(() => { - expect(context.finished).toHaveBeenCalled(); - expect(context.event).toHaveBeenCalled(); - done(); - }, 2001); + expect(context.handleEvent).toHaveBeenCalled(); }); }); }); @Component({ template: ` - - `, + + `, }) class TestNGComponent { - @ViewChild('comp') - comp: CountdownComponent; - config: Config = {}; - start() {} - finished() {} - notify() {} - event() {} + @ViewChild('comp', { static: false }) comp: CountdownComponent; + + config: CountdownConfig = {}; + + handleEvent(_e: CountdownEvent) {} } diff --git a/lib/src/countdown.component.ts b/lib/src/countdown.component.ts index 4728d1b..6eb90d4 100644 --- a/lib/src/countdown.component.ts +++ b/lib/src/countdown.component.ts @@ -1,6 +1,5 @@ import { Component, - ElementRef, Input, OnChanges, SimpleChanges, @@ -12,24 +11,23 @@ import { ChangeDetectionStrategy, ViewEncapsulation, Inject, + LOCALE_ID, + ChangeDetectorRef, + TemplateRef, } from '@angular/core'; -import { Config, Hand } from './interfaces'; -import { Timer } from './countdown.timer'; -import { CountdownConfig } from './countdown.config'; +import { CountdownConfig, CountdownStatus, CountdownEvent, CountdownEventAction, CountdownItem } from './interfaces'; +import { CountdownTimer } from './countdown.timer'; +import { CountdownGlobalConfig } from './countdown.config'; @Component({ selector: 'countdown', template: ` - + + + + `, - styles: [ - ` - countdown { - display: none; - } - `, - ], host: { '[class.count-down]': 'true' }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, @@ -37,149 +35,104 @@ import { CountdownConfig } from './countdown.config'; export class CountdownComponent implements OnInit, OnChanges, OnDestroy { private frequency = 1000; private _notify: any = {}; - private hands: Hand[] = []; private left = 0; - private paused = false; - /** 两种情况会触发:时间终止或调用 `stop()` */ - private stoped = false; + private status: CountdownStatus = CountdownStatus.ing; + private isDestroy = false; + i: CountdownItem = {}; - @Input() - config: Config; - @Output() - readonly start = new EventEmitter(); - @Output() - readonly finished = new EventEmitter(); - @Output() - readonly notify = new EventEmitter(); - @Output() - readonly event = new EventEmitter<{ action: string; left: number }>(); + @Input() config: CountdownConfig; + @Input() render: TemplateRef; + @Output() readonly event = new EventEmitter(); constructor( - private el: ElementRef, - private timer: Timer, - private cog: CountdownConfig, + @Inject(LOCALE_ID) private locale: string, + private timer: CountdownTimer, + private defCog: CountdownGlobalConfig, + private cdr: ChangeDetectorRef, ) {} - /** 开始,当 `demand: false` 时触发 */ + /** + * Start countdown, you must manually call when `demand: false` + */ begin() { - this.paused = false; - this.start.emit(); + this.status = CountdownStatus.ing; this.callEvent('start'); } - /** 重新开始 */ + /** + * Restart countdown + */ restart(): void { - if (!this.stoped) this.destroy(); + if (this.status !== CountdownStatus.stop) { + this.destroy(); + } this.init(); this.callEvent('restart'); } - /** 停止 */ + /** + * Stop countdown, must call `restart` when stopped, it's different from pause, unable to recover + */ stop() { - if (this.stoped) return; - this.stoped = true; + if (this.status === CountdownStatus.stop) { + return; + } + this.status = CountdownStatus.stop; this.destroy(); this.callEvent('stop'); } - /** 暂停(限未终止有效) */ + /** + * Pause countdown, you can use `resume` to recover again + */ pause() { - if (this.stoped || this.paused) return; - this.paused = true; + if (this.status === CountdownStatus.stop || this.status === CountdownStatus.pause) return; + this.status = CountdownStatus.pause; this.callEvent('pause'); } - /** 恢复 */ + /** + * Resume countdown + */ resume() { - if (this.stoped || !this.paused) return; - this.paused = false; + if (this.status === CountdownStatus.stop || this.status !== CountdownStatus.pause) return; + this.status = CountdownStatus.ing; this.callEvent('resume'); } - private callEvent(action: string) { - this.event.emit({ action, left: this.left }); + private callEvent(action: CountdownEventAction) { + this.event.emit({ action, left: this.left, status: this.status, text: this.i.text }); } private init() { - const me = this; - me.config = { ...new CountdownConfig(), ...me.cog, ...me.config }; - const el = me.el.nativeElement as HTMLElement; - me.paused = me.config.demand; - me.stoped = false; - - // 分析markup - const tmpl = el.innerHTML || me.config.template; - me.config.varRegular.lastIndex = 0; - el.innerHTML = tmpl.replace( - me.config.varRegular, - (str: string, type: string) => { - // 时钟频率校正. - if (type === 'u' || type === 's-ext') me.frequency = 100; - - // 生成hand的markup - let content = ''; - if (type === 's-ext') { - me.hands.push({ type: 's' }); - me.hands.push({ type: 'u' }); - content = - me.html('', 's', 'handlet') + - me.html('.', '', 'digital') + - me.html('', 'u', 'handlet'); - } else { - me.hands.push({ type: type }); - } - - return me.html(content, type, 'hand'); - }, - ); - - const clock = me.config.clock; - me.hands.forEach((hand: Hand) => { - const type = hand.type; - let base = 100, - i: number; - - hand.node = el.querySelector(`.hand-${type}`); - // radix, bits 初始化 - for (i = clock.length - 3; i > -1; i -= 3) { - if (type === clock[i]) { - break; - } - - base *= clock[i + 1]; - } - hand.base = base; - hand.radix = clock[i + 1]; - hand.bits = clock[i + 2]; + const { locale, defCog } = this; + const config = (this.config = { + ...new CountdownGlobalConfig(locale), + ...defCog, + ...this.config, }); + // tslint:disable-next-line: no-bitwise + const frq = (this.frequency = ~config.format.indexOf('S') ? 100 : 1000); + this.status = config.demand ? CountdownStatus.pause : CountdownStatus.ing; - me.getLeft(); - me.reflow(0, true); + this.getLeft(); + this.reflow(0, true); // bind reflow to me - const _reflow = me.reflow; - me.reflow = (count: number = 0) => { - return _reflow.apply(me, [count]); - }; + const _reflow = this.reflow; + this.reflow = (count: number = 0) => _reflow.apply(this, [count]); + + if (Array.isArray(config.notify)) { + config.notify.forEach((time: number) => { + if (time < 1) throw new Error(`The notify config must be a positive integer.`); - // 构建 notify - if (me.config.notify) { - me.config.notify.forEach((time: number) => { - if (time < 1) - throw new Error(`the notify config must be a positive integer.`); time = time * 1000; - time = time - (time % me.frequency); - me._notify[time] = true; + time = time - (time % frq); + this._notify[time] = true; }); } - me.timer.add(me.reflow, me.frequency); - // show - el.style.display = 'inline'; - - this.timer.start(); - - return me; + this.timer.add(this.reflow, frq).start(); } private destroy() { @@ -191,114 +144,60 @@ export class CountdownComponent implements OnInit, OnChanges, OnDestroy { * 更新时钟 */ private reflow(count: number = 0, force: boolean = false): void { - const me = this; - if (!force && (me.paused || me.stoped)) return; - me.left = me.left - me.frequency * count; + if (this.isDestroy) return; - me.hands.forEach((hand: Hand) => { - hand.lastValue = hand.value; - hand.value = Math.floor(me.left / hand.base) % hand.radix; - }); - - me.repaint(); + const { status, config, _notify } = this; + if (!force && status !== CountdownStatus.ing) return; - if (me._notify[me.left]) { - me.notify.emit(me.left); - me.callEvent('notify'); + const value = (this.left = this.left - this.frequency * count); + this.i = { + value, + text: config.formatDate({ date: value, formatStr: config.format, timezone: config.timezone }), + }; + if (typeof config.prettyText === 'function') { + this.i.text = config.prettyText(this.i.text); } + this.cdr.detectChanges(); - if (me.left < 1) { - me.finished.emit(0); - me.stoped = true; - me.callEvent('finished'); - me.destroy(); + if (config.notify === 0 || _notify[value]) { + this.callEvent('notify'); } - } - /** - * 重绘时钟 - */ - private repaint(): void { - const me = this; - if (me.config.repaint) { - me.config.repaint.apply(me); - return; + if (value < 1) { + this.status = CountdownStatus.done; + this.callEvent('done'); + this.destroy(); } - - let content: string; - - me.hands.forEach((hand: Hand) => { - if (hand.lastValue !== hand.value) { - content = ''; - - me.toDigitals(hand.value, hand.bits).forEach((digital: number) => { - content += me.html(digital.toString(), '', 'digital'); - }); - - hand.node.innerHTML = content; - } - }); } /** * 获取倒计时剩余帧数 */ private getLeft(): void { - const me = this; - let left: number = me.config.leftTime * 1000; - const end: number = me.config.stopTime; - - if (!left && end) left = end - new Date().getTime(); - - me.left = left - (left % me.frequency); - } + const { config, frequency } = this; + let left = config.leftTime * 1000; + const end = config.stopTime; - /** - * 生成需要的html代码,辅助工具 - */ - private html(con: string, className: string, type: string): string { - switch (type) { - case 'hand': - case 'handlet': - className = type + ' hand-' + className; - break; - case 'digital': - if (con === '.') { - className = type + ' ' + type + '-point ' + className; - } else { - className = type + ' ' + type + '-' + con + ' ' + className; - } - break; + if (!left && end) { + left = end - new Date().getTime(); } - return '' + con + ''; - } - /** - * 把值转换为独立的数字形式 - */ - private toDigitals(value: number, bits: number): number[] { - value = value < 0 ? 0 : value; - const digitals = []; - // 把时、分、秒等换算成数字. - while (bits--) { - digitals[bits] = value % 10; - value = Math.floor(value / 10); - } - return digitals; + this.left = left - (left % frequency); } ngOnInit() { this.init(); - if (!this.config.demand) this.begin(); + if (!this.config.demand) { + this.begin(); + } } ngOnDestroy(): void { + this.isDestroy = true; this.destroy(); } - ngOnChanges( - changes: { [P in keyof this]?: SimpleChange } & SimpleChanges, - ): void { + ngOnChanges(changes: { [P in keyof this]?: SimpleChange } & SimpleChanges): void { if (!changes.config.firstChange) { this.restart(); } diff --git a/lib/src/countdown.config.ts b/lib/src/countdown.config.ts index 6482ce7..1bb8d9c 100644 --- a/lib/src/countdown.config.ts +++ b/lib/src/countdown.config.ts @@ -1,11 +1,21 @@ -import { Injectable } from '@angular/core'; +// tslint:disable: no-inferrable-types +import { Injectable, Inject, LOCALE_ID } from '@angular/core'; +import { formatDate } from '@angular/common'; +import { CountdownFormatFn, CountdownConfig } from './interfaces'; @Injectable({ providedIn: 'root' }) -export class CountdownConfig { - demand = false; - leftTime = 0; - template = '$!h!时$!m!分$!s!秒'; - effect = 'normal'; - varRegular?: RegExp = /\$\!([\-\w]+)\!/g; - clock?: any[] = ['d', 100, 2, 'h', 24, 2, 'm', 60, 2, 's', 60, 2, 'u', 10, 1]; +export class CountdownGlobalConfig implements CountdownConfig { + constructor(@Inject(LOCALE_ID) private locale: string) {} + + demand?: boolean = false; + + leftTime?: number = 0; + + format?: string = 'HH:mm:ss'; + + timezone?: string = '+0000'; + + formatDate?: CountdownFormatFn = ({ date, formatStr, timezone }) => { + return formatDate(new Date(date), formatStr, this.locale, timezone || this.timezone || '+0000'); + }; } diff --git a/lib/src/countdown.module.ts b/lib/src/countdown.module.ts index 2ebe5e7..1554823 100644 --- a/lib/src/countdown.module.ts +++ b/lib/src/countdown.module.ts @@ -2,13 +2,12 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { CountdownComponent } from './countdown.component'; -import { Timer } from './countdown.timer'; +import { CountdownTimer } from './countdown.timer'; @NgModule({ - imports: [CommonModule], - providers: [Timer], - declarations: [CountdownComponent], - exports: [CountdownComponent] + imports: [CommonModule], + providers: [CountdownTimer], + declarations: [CountdownComponent], + exports: [CountdownComponent], }) -export class CountdownModule { -} +export class CountdownModule {} diff --git a/lib/src/countdown.timer.ts b/lib/src/countdown.timer.ts index 4e959dc..067ce3d 100644 --- a/lib/src/countdown.timer.ts +++ b/lib/src/countdown.timer.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; @Injectable() -export class Timer { - private fns: any[] = []; - private commands: Function[] = []; +export class CountdownTimer { + private fns: Array<((count: number) => number | void) | number> = []; + private commands: Array<() => void> = []; private nextTime: number; private ing = false; @@ -22,45 +22,45 @@ export class Timer { let diff = +new Date() - this.nextTime; const count = 1 + Math.floor(diff / 100); - diff = 100 - diff % 100; + diff = 100 - (diff % 100); this.nextTime += 100 * count; - let frequency: number, step: number, i: number, len: number; - for (i = 0, len = this.fns.length; i < len; i += 2) { - frequency = this.fns[i + 1]; + for (let i = 0, len = this.fns.length; i < len; i += 2) { + let frequency = this.fns[i + 1] as number; // 100/s if (0 === frequency) { - this.fns[i](count); + (this.fns[i] as (count: number) => void)(count); // 1000/s } else { // 先把末位至0,再每次加2 frequency += 2 * count - 1; - step = Math.floor(frequency / 20); + const step = Math.floor(frequency / 20); if (step > 0) { - this.fns[i](step); + (this.fns[i] as (count: number) => void)(step); } // 把末位还原成1 - this.fns[i + 1] = frequency % 20 + 1; + this.fns[i + 1] = (frequency % 20) + 1; } } - if (this.ing) { - setTimeout(() => this.process(), diff); - } + if (!this.ing) return; + + setTimeout(() => this.process(), diff); } - add(fn: Function, frequency: number) { + add(fn: () => void, frequency: number): this { this.commands.push(() => { this.fns.push(fn); this.fns.push(frequency === 1000 ? 1 : 0); this.ing = true; }); + return this; } - remove(fn: Function) { + remove(fn: () => void): this { this.commands.push(() => { const i = this.fns.indexOf(fn); if (i !== -1) { @@ -68,5 +68,6 @@ export class Timer { } this.ing = this.fns.length > 0; }); + return this; } } diff --git a/lib/src/index.ts b/lib/src/index.ts new file mode 100644 index 0000000..4aaf8f9 --- /dev/null +++ b/lib/src/index.ts @@ -0,0 +1 @@ +export * from './public_api'; diff --git a/lib/src/interfaces.ts b/lib/src/interfaces.ts index b5dfbb2..45c23a7 100644 --- a/lib/src/interfaces.ts +++ b/lib/src/interfaces.ts @@ -1,11 +1,21 @@ -export interface Config { - /** - * Custom render template, if is empty use the `` content, and `$!s-ext!` it's `0.1s` accuracy, Default: `$!h!时$!m!分$!s!秒` - */ - template?: string; +export type CountdownFormatFn = (opt: CountdownFormatFnOption) => string; + +export interface CountdownFormatFnOption { + date: number; + formatStr: string; + timezone?: string; +} + +export enum CountdownStatus { + ing, + pause, + stop, + done, +} +export interface CountdownConfig { /** - * start the counter on demand, must call `begin()` to starting, Default: `false` + * Start the counter on demand, must call `begin()` to starting, Default: `false` */ demand?: boolean; @@ -15,39 +25,48 @@ export interface Config { leftTime?: number; /** - * 结束时间,单位:UNIX时间戳 ms - * 指的是根据本地时间至结束时间进行倒计时 + * Refers to counting down from local time to end time (Unit: Milliseconds second UNIX timestamp) */ stopTime?: number; /** - * 模板解析正则表达式,默认:`/\$\{([\-\w]+)\}/g` - * 有时候由于模板结构比较特殊,无法根据默认的表达式进行解析,那就需要修改它 + * Formats a date value, pls refer to [Accepted patterns](https://angular.io/api/common/DatePipe#usage-notes), Default: `HH:mm:ss` */ - varRegular?: RegExp; + format?: string; /** - * 时钟控制数组,特殊需求时可以修改,里面是三元组:指针名、进制、位数,可参考大于99小时demo,默认:`['d', 100, 2, 'h', 24, 2, 'm', 60, 2, 's', 60, 2, 'u', 10, 1]` + * Beautify text, generally used to convert formatted time text into HTML */ - clock?: any[]; + prettyText?: (text: string) => string; /** - * 第x秒时调用 notify 函数,值必须是正整数 + * Should be trigger type `notify` event on the x second. When values is `0` will be trigger every time. */ - notify?: number[]; + notify?: number[] | number; /** - * 重绘 + * Default based on the implementation of `formatDate` in `@angular/common` + * + * You can changed to other libs, e.g: [date-fns](https://date-fns.org/v2.0.1/docs/format) */ - repaint?: Function; + formatDate?: CountdownFormatFn; + + /** + * A timezone offset (such as '+0430'), or a standard UTC/GMT. When not supplied, uses the end-user's local system timezone, Default: `+0000` + */ + timezone?: string; +} + +export type CountdownEventAction = 'start' | 'stop' | 'restart' | 'pause' | 'resume' | 'notify' | 'done'; + +export interface CountdownEvent { + action: CountdownEventAction; + status: CountdownStatus; + left: number; + text: string; } -export interface Hand { - type?: string; +export interface CountdownItem { + text?: string; value?: number; - lastValue?: number; - base?: number; - radix?: number; - bits?: number; - node?: any; } diff --git a/lib/src/public_api.ts b/lib/src/public_api.ts new file mode 100644 index 0000000..5170dfb --- /dev/null +++ b/lib/src/public_api.ts @@ -0,0 +1,5 @@ +export * from './interfaces'; +export * from './countdown.component'; +export * from './countdown.timer'; +export * from './countdown.config'; +export * from './countdown.module'; diff --git a/lib/test.ts b/lib/test.ts index b6d614d..da9049d 100644 --- a/lib/test.ts +++ b/lib/test.ts @@ -2,18 +2,12 @@ import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting, -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; declare const require: any; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), -); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. diff --git a/lib/tsconfig.lib.json b/lib/tsconfig.lib.json index dffaf79..932b4b2 100644 --- a/lib/tsconfig.lib.json +++ b/lib/tsconfig.lib.json @@ -14,7 +14,7 @@ ] }, "files": [ - "./index.ts" + "./src/public_api.ts" ], "angularCompilerOptions": { "skipTemplateCodegen": true diff --git a/lib/tsconfig.spec.json b/lib/tsconfig.spec.json index 69fcb26..6d23fc4 100644 --- a/lib/tsconfig.spec.json +++ b/lib/tsconfig.spec.json @@ -1,10 +1,7 @@ { - "extends": "./tsconfig.lib.json", + "extends": "../tsconfig.json", "compilerOptions": { - "outDir": "../out-tsc/spec", - "baseUrl": "./", - "module": "commonjs", - "target": "es5", + "outDir": "./out-tsc/spec", "types": [ "jasmine", "node" diff --git a/package.json b/package.json index 4320d91..9143a29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-countdown", - "version": "3.2.0", + "version": "8.0.0", "description": "Simple, easy and performance countdown for angular", "repository": { "type": "git", @@ -8,9 +8,9 @@ }, "keywords": [ "ngx-countdown", - "ng2-countdown", + "ng-countdown", "angular-countdown", - "angular2-countdown", + "angular countdown", "countdown", "count down" ], @@ -31,48 +31,50 @@ "release": "npm run build && cd publish && npm publish --access public" }, "devDependencies": { - "@angular/animations": "~7.2.0", - "@angular/common": "~7.2.0", - "@angular/compiler": "~7.2.0", - "@angular/core": "~7.2.0", - "@angular/forms": "~7.2.0", - "@angular/platform-browser": "~7.2.0", - "@angular/platform-browser-dynamic": "~7.2.0", - "@angular/router": "~7.2.0", - "core-js": "^2.5.4", - "rxjs": "~6.3.3", - "tslib": "^1.9.0", - "zone.js": "~0.8.26", - "@angular-devkit/build-angular": "~0.13.0", - "@angular/cli": "~7.3.0", - "@angular/compiler-cli": "~7.2.0", - "@angular/language-service": "~7.2.0", + "@angular/animations": "~8.2.4", + "@angular/common": "~8.2.4", + "@angular/compiler": "~8.2.4", + "@angular/core": "~8.2.4", + "@angular/forms": "~8.2.4", + "@angular/platform-browser": "~8.2.4", + "@angular/platform-browser-dynamic": "~8.2.4", + "@angular/router": "~8.2.4", + "rxjs": "~6.4.0", + "tslib": "^1.10.0", + "zone.js": "~0.9.1", + "@angular-devkit/build-angular": "~0.803.2", + "@angular/cli": "~8.3.2", + "@angular/compiler-cli": "~8.2.4", + "@angular/language-service": "~8.2.4", "@types/node": "~8.9.4", - "@types/jasmine": "~2.8.8", + "@types/jasmine": "~3.3.8", "@types/jasminewd2": "~2.0.3", - "codelyzer": "~4.5.0", - "jasmine-core": "~2.99.1", + "codelyzer": "^5.0.0", + "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", - "karma": "~3.1.1", + "karma": "~4.1.0", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "~2.0.1", - "karma-jasmine": "~1.1.2", - "karma-jasmine-html-reporter": "^0.2.2", + "karma-jasmine": "~2.0.1", + "karma-jasmine-html-reporter": "^1.4.0", "protractor": "~5.4.0", "ts-node": "~7.0.0", - "tslint": "~5.11.0", - "typescript": "~3.2.2", + "tslint": "~5.15.0", + "typescript": "~3.5.3", + "tslint-config-prettier": "^1.18.0", + "prettier": "^1.18.2", + "prettier-stylelint": "^0.4.2", "ngx-highlight-js": "^2.1.1", - "ngx-toastr": "^9.1.1", - "codecov": "^3.1.0", - "bootstrap": "^4.2.1", - "gh-pages": "^2.0.1", - "tsickle": "^0.34.3", - "ng-packagr": "^4.7.0" + "codecov": "^3.5.0", + "bootstrap": "^4.3.1", + "gh-pages": "^2.1.1", + "tsickle": "^0.37.0", + "ng-packagr": "^5.5.0", + "date-fns": "^2.0.1" }, "ngPackage": { "lib": { - "entryFile": "lib/index.ts" + "entryFile": "lib/src/public_api.ts" }, "dest": "./publish" } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3a40222..1b647c4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,8 +1,9 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-root', - template: `` + selector: 'app-root', + template: ` + + `, }) -export class AppComponent { -} +export class AppComponent {} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d9e188e..8e52811 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,21 +4,17 @@ import { RouterModule } from '@angular/router'; import { BrowserModule } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { HighlightJsModule } from 'ngx-highlight-js'; -import { ToastrModule } from 'ngx-toastr'; -import { CountdownModule, Config } from 'ngx-countdown'; +import { CountdownModule, CountdownGlobalConfig, CountdownConfig } from 'ngx-countdown'; import { AppComponent } from './app.component'; import { LayoutComponent } from './components/layout.component'; import { DemoComponent } from './components/demo.component'; import { ALotComponent } from './components/alot.component'; -import { TplComponent } from './components/tpl.component'; import { NothingComponent } from './components/nothing.component'; -import { TplFlipComponent } from './tpl/flip/flip.component'; -import { CountdownConfig } from 'ngx-countdown/src/countdown.config'; -export function countdownConfigFactory(): Config { - return { template: `$!h!:$!m!:$!s!` }; +export function countdownConfigFactory(): CountdownConfig { + return {}; } @NgModule({ @@ -27,7 +23,6 @@ export function countdownConfigFactory(): Config { FormsModule, CommonModule, HighlightJsModule, - ToastrModule.forRoot(), RouterModule.forRoot( [ { @@ -36,31 +31,16 @@ export function countdownConfigFactory(): Config { children: [ { path: '', component: DemoComponent }, { path: 'alot', component: ALotComponent }, - { path: 'tpl', component: TplComponent }, { path: 'nothing', component: NothingComponent }, ], }, - { - path: 'tpl', - children: [{ path: 'flip', component: TplFlipComponent }], - }, ], { useHash: true }, ), CountdownModule, ], - declarations: [ - AppComponent, - LayoutComponent, - DemoComponent, - ALotComponent, - NothingComponent, - TplComponent, - TplFlipComponent, - ], - providers: [ - { provide: CountdownConfig, useFactory: countdownConfigFactory } - ], + declarations: [AppComponent, LayoutComponent, DemoComponent, ALotComponent, NothingComponent], + providers: [{ provide: CountdownGlobalConfig, useFactory: countdownConfigFactory }], bootstrap: [AppComponent], }) export class AppDemoModule {} diff --git a/src/app/components/alot.component.html b/src/app/components/alot.component.html index 3391677..1ad6990 100644 --- a/src/app/components/alot.component.html +++ b/src/app/components/alot.component.html @@ -1,420 +1,5 @@
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- +
+
diff --git a/src/app/components/alot.component.ts b/src/app/components/alot.component.ts index 629b4a1..c0e72a7 100644 --- a/src/app/components/alot.component.ts +++ b/src/app/components/alot.component.ts @@ -4,4 +4,6 @@ import { Component } from '@angular/core'; selector: 'demo-alot', templateUrl: './alot.component.html', }) -export class ALotComponent {} +export class ALotComponent { + arr = new Array(1000).fill({}); +} diff --git a/src/app/components/demo.component.html b/src/app/components/demo.component.html index c894684..59bbe99 100644 --- a/src/app/components/demo.component.html +++ b/src/app/components/demo.component.html @@ -4,7 +4,7 @@
Basic
- +
@@ -30,10 +30,10 @@
Accuracy 0.1s
- 剩余时间:$!h!时$!m!分$!s-ext!秒 +
@@ -46,16 +46,16 @@

Notify at 2, 5 seconds

- +
- +
@@ -73,19 +73,18 @@ <countdown [config]="{ stopTime: 1493313440499 }">
- +
-
Clock config
+
Custom format date
-

时钟控制数组,特殊需求时可以修改,里面是三元组:指针名、进制、位数

- +
@@ -97,7 +96,7 @@
Demand
- +
@@ -113,7 +112,7 @@
Actions
- +
@@ -122,12 +121,35 @@
+ +
+
+
+
+
+
Using date-fns format date
+
+
+ +
+
+
+
+
+
+
+
+
Pretty text
+
+
+ +
diff --git a/src/app/components/demo.component.scss b/src/app/components/demo.component.scss index d8cca05..e884609 100644 --- a/src/app/components/demo.component.scss +++ b/src/app/components/demo.component.scss @@ -4,11 +4,27 @@ width: 6px; } &::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); border-radius: 10px; } &::-webkit-scrollbar-thumb { border-radius: 10px; - -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5); + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5); + } +} + +.custom-style { + font-size: 18px; + .item { + padding-left: 4px; + &:nth-child(1) { + color: rgb(199, 66, 0); + } + &:nth-child(2) { + color: rgb(45, 0, 170); + } + &:nth-child(3) { + color: rgb(207, 7, 147); + } } } diff --git a/src/app/components/demo.component.ts b/src/app/components/demo.component.ts index 2454888..ea9e620 100644 --- a/src/app/components/demo.component.ts +++ b/src/app/components/demo.component.ts @@ -1,13 +1,10 @@ -import { - Component, - OnInit, - ViewEncapsulation, - AfterViewInit, - ViewChild, - ElementRef, -} from '@angular/core'; -import { ToastrService } from 'ngx-toastr'; -import { CountdownComponent } from 'ngx-countdown'; +// tslint:disable: member-ordering +import { Component, ViewEncapsulation, ViewChild, Inject, LOCALE_ID } from '@angular/core'; +import { formatDate } from '@angular/common'; +import { CountdownComponent, CountdownConfig, CountdownEvent } from 'ngx-countdown'; +import { format } from 'date-fns'; + +const MINIUES = 1000 * 60; @Component({ selector: 'demo', @@ -16,31 +13,59 @@ import { CountdownComponent } from 'ngx-countdown'; encapsulation: ViewEncapsulation.None, }) export class DemoComponent { - constructor(private ts: ToastrService) {} - + @ViewChild('countdown', { static: false }) private counter: CountdownComponent; + stopConfig: CountdownConfig = { stopTime: new Date().getTime() + 1000 * 30 }; notify: string; - config: any = { leftTime: 10, notify: [2, 5] }; - onStart() { - this.notify = '开始鸟'; - } - onFinished() { - this.notify = '结束了!'; - } - onNotify(time: number) { - this.notify = `在${time}ms时通知了一下`; - } + config: CountdownConfig = { leftTime: 10, notify: [2, 5] }; + customFormat: CountdownConfig = { + leftTime: 65, + formatDate: ({ date, formatStr, timezone }) => { + let f = formatStr; + if (date > MINIUES) { + f = 'm分s秒'; + } else if (date === MINIUES) { + f = 'm分'; + } else { + f = 's秒'; + } + return formatDate(date, f, this.locale, timezone || '+0000'); + }, + }; + dateFnsConfig: CountdownConfig = { + leftTime: 60 * 60 * 24 * 365 * (2050 - 1970), + format: 'yyyy-MM-dd E HH:mm:ss a', + formatDate: ({ date, formatStr }) => format(date, formatStr), + }; + prettyConfig: CountdownConfig = { + leftTime: 60, + format: 'HH:mm:ss', + prettyText: text => { + return text + .split(':') + .map(v => `${v}`) + .join(''); + }, + }; + + constructor(@Inject(LOCALE_ID) private locale: string) {} - stopConfig: any = { stopTime: new Date().getTime() + 1000 * 30 }; resetStop() { this.stopConfig = { stopTime: new Date().getTime() + 1000 * 30 }; } - onEvent(value: any) { - console.log('event', value); - } - - @ViewChild('countdown') counter: CountdownComponent; resetTimer() { this.counter.restart(); } + + handleEvent(e: CountdownEvent) { + console.log(e); + } + + handleEvent2(e: CountdownEvent) { + this.notify = e.action.toUpperCase(); + if (e.action === 'notify') { + this.notify += ` - ${e.left} ms`; + } + console.log(e); + } } diff --git a/src/app/components/layout.component.ts b/src/app/components/layout.component.ts index 5d9e149..9cfd1f2 100644 --- a/src/app/components/layout.component.ts +++ b/src/app/components/layout.component.ts @@ -3,29 +3,34 @@ import { Component } from '@angular/core'; selector: 'demo-layout', template: `

Simple, easy and performance countdown for angular

- `, + `, }) export class LayoutComponent {} diff --git a/src/app/components/nothing.component.html b/src/app/components/nothing.component.html index b4bc41d..cab1630 100644 --- a/src/app/components/nothing.component.html +++ b/src/app/components/nothing.component.html @@ -1,2 +1,4 @@ -

本页由于无任何组件使用到Countdown,因此,Timer将会自动停止运行。

-

确认这一点,可以在Chrome-Profiles中观察到。

+

+ Since there are no any countdown component on this page, Timer will automatically stop running. +

+

Confirm this and you can observe it in Chrome-Profiles.

diff --git a/src/app/components/tpl.component.html b/src/app/components/tpl.component.html deleted file mode 100644 index bcf342a..0000000 --- a/src/app/components/tpl.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
-
-
Flip
-
- -
- Get Code - DEMO -
-
-
-
-
diff --git a/src/app/components/tpl.component.scss b/src/app/components/tpl.component.scss deleted file mode 100644 index e2718ba..0000000 --- a/src/app/components/tpl.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -img { - max-width: 100%; -} diff --git a/src/app/components/tpl.component.ts b/src/app/components/tpl.component.ts deleted file mode 100644 index 6150474..0000000 --- a/src/app/components/tpl.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core'; - -@Component({ - selector: 'demo-tpl', - templateUrl: './tpl.component.html', - styleUrls: ['./tpl.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class TplComponent {} diff --git a/src/app/tpl/flip/flip.component.html b/src/app/tpl/flip/flip.component.html deleted file mode 100644 index 6ba09ad..0000000 --- a/src/app/tpl/flip/flip.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
- -
$!d! -
-
-
$!h! -
-
-
$!m! -
-
-
$!s! -
-
-
-
-code diff --git a/src/app/tpl/flip/flip.component.scss b/src/app/tpl/flip/flip.component.scss deleted file mode 100644 index c067ead..0000000 --- a/src/app/tpl/flip/flip.component.scss +++ /dev/null @@ -1,90 +0,0 @@ -.flip-cd { - .time { - border-radius: 5px; - box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.5); - display: inline-block; - text-align: center; - position: relative; - height: 95px; - width: 65px; - perspective: 479px; - backface-visibility: hidden; - transform: translateZ(0); - transform: translate3d(0, 0, 0); - } - .count { - background: #202020; - color: #f8f8f8; - display: block; - font-family: 'Oswald', sans-serif; - font-size: 3.3em; - line-height: 1.8em; - overflow: hidden; - position: absolute; - text-align: center; - text-shadow: 0 0 10px rgba(0, 0, 0, 0.8); - top: 0; - width: 100%; - transform: translateZ(0); - transform-style: flat; - &.top { - border-top: 1px solid rgba(255, 255, 255, 0.2); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 5px 5px 0 0; - height: 50%; - transform-origin: 50% 100%; - } - &.bottom { - background-image: linear-gradient(rgba(255, 255, 255, 0.1), transparent); - border-top: 1px solid #000; - border-bottom: 1px solid #000; - border-radius: 0 0 5px 5px; - line-height: 0; - height: 50%; - top: 50%; - transform-origin: 50% 0; - } - &.curr { - &.top { - transform: rotateX(0deg); - z-index: 3; - } - } - &.next { - &.bottom { - transform: rotateX(90deg); - z-index: 2; - } - } - } - .label { - font-size: normal; - margin-top: 5px; - display: block; - position: absolute; - top: 95px; - width: 100%; - } - .hand { - display: block; - height: 100%; - width: 100%; - margin: 0 !important; - } - .flip { - .count { - &.curr { - &.top { - transition: all 250ms ease-in-out; - transform: rotateX(-90deg); - } - } - &.next { - &.bottom { - transition: all 250ms ease-in-out 250ms; - transform: rotateX(0deg); - } - } - } - } -} diff --git a/src/app/tpl/flip/flip.component.ts b/src/app/tpl/flip/flip.component.ts deleted file mode 100644 index 2f59a8b..0000000 --- a/src/app/tpl/flip/flip.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core'; -import { Config } from 'ngx-countdown'; - -@Component({ - selector: 'demo-tpl-flip', - templateUrl: './flip.component.html', - styleUrls: ['./flip.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class TplFlipComponent { - config: Config = { - leftTime: 60 * 60 * 24 * 7, - repaint: function() { - // 这里不可以使用箭头函数,因为对于箭头函数this是强制性的,为了让重绘有更大的权限,必须是function - const me: any = this; - let content: string; - - me.hands.forEach((hand: any) => { - if (hand.lastValue !== hand.value) { - content = ''; - const cur = me.toDigitals(hand.value + 1, hand.bits).join(''), - next = me.toDigitals(hand.value, hand.bits).join(''); - - hand.node.innerHTML = ` - ${cur} - ${next} - ${next} - ${cur} - `; - hand.node.parentElement.className = 'time'; - setTimeout(() => { - hand.node.parentElement.className = 'time flip'; - }); - } - }); - }, - }; -} diff --git a/src/index.html b/src/index.html index 210edc3..5a12dc6 100644 --- a/src/index.html +++ b/src/index.html @@ -1,24 +1,21 @@ - + + + + ngx-countdown | Simple, easy and performance countdown for angular + + + + + + - - - ngx-countdown | Simple, easy and performance countdown for angular - - - - - - - - -
- Loading... -
- - - Fork me on GitHub - - - + +
+ Loading... +
+ + Fork me on GitHub + + diff --git a/src/tsconfig.json b/src/tsconfig.json index b38e846..4f690e6 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,22 +1,17 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "sourceMap": false, - "outDir": "../out-tsc/app", - "baseUrl": "./", - "module": "es2015", - "types": [], - "paths": { - "ngx-countdown": [ - "../lib/index" - ], - "ngx-countdown/*": [ - "../lib/*" - ] - } - }, - "exclude": [ - "test.ts", - "**/*.spec.ts" - ] + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "./main.ts", + "./polyfills.ts" + ], + "include": [ + "**/*.ts" + ], + "exclude": [ + "**/*.spec.ts" + ] } diff --git a/tsconfig.json b/tsconfig.json index f9211db..7a7e941 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,29 @@ { "compileOnSave": false, "compilerOptions": { + "baseUrl": "./", "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, + "downlevelIteration": true, "experimentalDecorators": true, - "target": "es5", - "typeRoots": ["node_modules/@types"], - "lib": ["es2017", "dom"], - "baseUrl": "./", + "module": "esnext", + "moduleResolution": "node", + "importHelpers": true, + "target": "es2015", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2018", + "dom" + ], "paths": { - "ngx-countdown": ["./lib/src/countdown.module"], - "ngx-countdown/*": ["./lib/*"] + "ngx-countdown": ["./lib/src/index"] } + }, + "angularCompilerOptions": { + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true } } diff --git a/tslint.json b/tslint.json index 0255a3d..afb19dd 100644 --- a/tslint.json +++ b/tslint.json @@ -1,91 +1,105 @@ { - "rulesDirectory": ["node_modules/codelyzer"], + "extends": [ + "tslint:latest", + "tslint-config-prettier" + ], + "rulesDirectory": [ + "codelyzer" + ], "rules": { - "callable-types": true, - "class-name": true, - "comment-format": [true, "check-space"], - "curly": false, - "eofline": true, - "forin": true, - "import-blacklist": [true], - "import-spacing": true, - "indent": [true, "spaces"], - "interface-over-type-literal": true, - "label-position": true, - "max-line-length": [false, 140], - "member-access": false, - "member-ordering": [false], - "no-arg": true, - "no-bitwise": true, - "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-construct": true, - "no-debugger": true, - "no-duplicate-variable": true, - "no-empty": false, - "no-empty-interface": true, - "no-eval": true, - "no-inferrable-types": [true, "ignore-params"], - "no-shadowed-variable": true, - "no-string-literal": false, - "no-string-throw": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ + // Angular parts + "array-type": false, + "arrow-parens": false, + "deprecation": { + "severity": "warn" + }, + "component-class-suffix": [true, "Component", "Widget"], + "contextual-lifecycle": true, + "directive-class-suffix": true, + "directive-selector": [ false, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" + "attribute", + "app", + "camelCase" ], - "prefer-const": true, - "quotemark": [true, "single"], - "radix": true, - "semicolon": [true, "always"], - "triple-equals": [true, "allow-null-check"], - "typedef-whitespace": [ + "component-selector": [ + false, + "element", + "app", + "kebab-case" + ], + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "interface-name": [true, "never-prefix"], + "max-classes-per-file": false, + "max-line-length": [ + false, + 140 + ], + "member-access": false, + "member-ordering": [ true, { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] } ], - "typeof-compare": true, - "unified-signatures": true, - "variable-name": false, - "whitespace": [ + "no-consecutive-blank-lines": false, + "no-console": [ true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-empty": false, + "no-inferrable-types": [ + false, + "ignore-params" + ], + "no-non-null-assertion": false, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-use-before-declare": false, + "no-var-requires": false, + "object-literal-key-quotes": [ + false, + "as-needed" ], - - "directive-selector": [false, "attribute", "app", "camelCase"], - "component-selector": [false, "element", "app", "kebab-case"], - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": false, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, + "object-literal-sort-keys": false, + "ordered-imports": false, + "quotemark": [ + false, + "single" + ], + "trailing-comma": false, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": false, + "no-input-rename": false, + "no-inputs-metadata-property": true, + "no-output-native": false, + "no-output-on-prefix": true, + "no-output-rename": false, + "no-outputs-metadata-property": true, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true, - "no-access-missing-member": true, - "templates-use-public": true, - "invoke-injectable": true - }, - "linterOptions": { - "exclude": [ - "lib/**/*.spec.ts" - ] + // Custom parts + "curly": false, + "variable-name": false, + "no-submodule-imports": false, + "no-implicit-dependencies": false, + "no-object-literal-type-assertion": false, + "no-bitwise": false, + "no-this-assignment": false, + "prefer-conditional-expression": false } } From 54dfcf7a8b0ce14bd8bdaed9a8dbe7b94094a734 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 17:28:14 +0800 Subject: [PATCH 2/7] chore: fix travis --- .nvmrc | 1 + .travis.yml | 15 --------------- 2 files changed, 1 insertion(+), 15 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..e3cbcda --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +10.9.0 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index b06a7c2..9280267 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,5 @@ sudo: required language: node_js -node_js: - - '8.11' - -addons: - apt: - sources: - - google-chrome - packages: - - google-chrome-stable - - google-chrome-beta git: depth: 1 @@ -19,11 +9,6 @@ cache: directories: - ./node_modules -before_install: - - export CHROME_BIN=chromium-browser - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start - env: - TASK=build - TASK=test From b4148455445d9a3464132f59f810a19e276d8ffd Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 17:34:39 +0800 Subject: [PATCH 3/7] chore: fix polyfills --- src/polyfills.ts | 74 ++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/polyfills.ts b/src/polyfills.ts index 20d4075..2f258e5 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -11,66 +11,52 @@ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. * - * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + * Learn more in https://angular.io/guide/browser-support */ /*************************************************************************************************** * BROWSER POLYFILLS */ -/** IE9, IE10 and IE11 requires all of the following polyfills. **/ -// import 'core-js/es6/symbol'; -// import 'core-js/es6/object'; -// import 'core-js/es6/function'; -// import 'core-js/es6/parse-int'; -// import 'core-js/es6/parse-float'; -// import 'core-js/es6/number'; -// import 'core-js/es6/math'; -// import 'core-js/es6/string'; -// import 'core-js/es6/date'; -// import 'core-js/es6/array'; -// import 'core-js/es6/regexp'; -// import 'core-js/es6/map'; -// import 'core-js/es6/weak-map'; -// import 'core-js/es6/set'; - /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. -/** IE10 and IE11 requires the following for the Reflect API. */ -// import 'core-js/es6/reflect'; - - -/** Evergreen browsers require these. **/ -// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; - - /** - * Required to support Web Animations `@angular/platform-browser/animations`. - * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation - **/ + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ // import 'web-animations-js'; // Run `npm install --save web-animations-js`. - +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ /*************************************************************************************************** - * Zone JS is required by Angular itself. + * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ - -/** - * Date, currency, decimal and percent pipes. - * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 - */ -// import 'intl'; // Run `npm install --save intl`. -/** - * Need to import at least one locale-data with intl. - */ -// import 'intl/locale-data/jsonp/en'; From fa56287d0b5cb19f329814a0a1736f6472678878 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 17:39:56 +0800 Subject: [PATCH 4/7] chore: fix --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9280267..ce33561 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,13 @@ cache: directories: - ./node_modules +addons: + apt: + sources: + - google-chrome + packages: + - google-chrome-stable + env: - TASK=build - TASK=test From ce4d8ee7849c6ffd74cc64b92b06a651ea00ebb5 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 17:51:36 +0800 Subject: [PATCH 5/7] chore: fix travis --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index ce33561..d51d9ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,11 @@ cache: directories: - ./node_modules +before_install: + - export NG_CLI_ANALYTICS=ci + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + addons: apt: sources: From 9a161de1bdcc58f572c1d67f1c154eb32aeca55f Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 18:00:07 +0800 Subject: [PATCH 6/7] chore: fix travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d51d9ae..500596a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ before_install: - export NG_CLI_ANALYTICS=ci - export CHROME_BIN=chromium-browser - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start addons: apt: From 8845a99125cd9a65e17b91e00bc2a99413c7d5d1 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sat, 31 Aug 2019 18:04:37 +0800 Subject: [PATCH 7/7] chore: try fix travis --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 500596a..f6cc84c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ sudo: required +dist: trusty language: node_js git: @@ -10,10 +11,11 @@ cache: - ./node_modules before_install: - - export NG_CLI_ANALYTICS=ci - - export CHROME_BIN=chromium-browser + - set -e - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start + - sleep 3 + - export NG_CLI_ANALYTICS=ci addons: apt: