From 57d4d9d8fffeac7feb5ae19e55f5c684178648b5 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 8 Jan 2018 09:17:01 +0100 Subject: [PATCH] MOBILE-2312 core: Implement CoreChrono component --- src/components/chrono/chrono.ts | 116 ++++++++++++++++++++++++++++ src/components/components.module.ts | 11 ++- src/pipes/seconds-to-hms.ts | 3 + 3 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/components/chrono/chrono.ts diff --git a/src/components/chrono/chrono.ts b/src/components/chrono/chrono.ts new file mode 100644 index 000000000..ee0d421bf --- /dev/null +++ b/src/components/chrono/chrono.ts @@ -0,0 +1,116 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, Input, OnChanges, OnDestroy, Output, EventEmitter, SimpleChange, ChangeDetectorRef } from '@angular/core'; + +/** + * This component shows a chronometer in format HH:MM:SS. + * + * If no startTime is provided, it will start at 00:00:00. + * If an endTime is provided, the chrono will stop and emit an event in the onEnd output when that number of milliseconds is + * reached. E.g. if startTime=60000 and endTime=120000, the chrono will start at 00:01:00 and end when it reaches 00:02:00. + * + * This component has 2 boolean inputs to control the timer: running (to start and stop it) and reset. + * + * Example usage: + * + */ +@Component({ + selector: 'core-chrono', + template: '{{ time / 1000 | coreSecondsToHMS }}' +}) +export class CoreChronoComponent implements OnChanges, OnDestroy { + @Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it. + @Input() startTime?: number = 0; // Number of milliseconds to put in the chrono before starting. + @Input() endTime?: number; // Number of milliseconds to stop the chrono. + @Input() reset?: boolean; // Set it to true to reset the chrono. + @Output() onEnd?: EventEmitter; // Will emit an event when the endTime is reached. + + time: number = 0; + protected interval; + + constructor(private cdr: ChangeDetectorRef) { + this.onEnd = new EventEmitter(); + } + + /** + * Component being initialized. + */ + ngOnInit() { + this.time = this.startTime || 0; + } + + /** + * Component being initialized. + */ + ngOnChanges(changes: {[name: string]: SimpleChange}) { + if (changes && changes.running) { + if (changes.running.currentValue) { + this.start(); + } else { + this.stop(); + } + } + if (changes && changes.reset && changes.reset.currentValue) { + this.resetChrono(); + } + } + + /** + * Reset the chrono, stopping it and setting it to startTime. + */ + protected resetChrono() : void { + this.stop(); + this.time = this.startTime || 0; + } + + /** + * Start the chrono if it isn't running. + */ + protected start() : void { + if (this.interval) { + // Already setup. + return; + } + + let lastExecTime = Date.now(); + + this.interval = setInterval(() => { + // Increase the chrono. + this.time += Date.now() - lastExecTime; + lastExecTime = Date.now(); + + if (typeof this.endTime != 'undefined' && this.time > this.endTime) { + // End time reached, stop the timer and call the end function. + this.stop(); + this.onEnd.emit(); + } + + // Force change detection. Angular doesn't detect these async operations. + this.cdr.detectChanges(); + }, 200); + } + + /** + * Stop the chrono, leaving the same time it has. + */ + protected stop() : void { + clearInterval(this.interval); + delete this.interval; + } + + ngOnDestroy() { + this.stop(); + } +} diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 8be44288c..c8c96e009 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreDirectivesModule } from '../directives/directives.module'; +import { CorePipesModule } from '../pipes/pipes.module'; import { CoreLoadingComponent } from './loading/loading'; import { CoreMarkRequiredComponent } from './mark-required/mark-required'; import { CoreInputErrorsComponent } from './input-errors/input-errors'; @@ -28,6 +29,7 @@ import { CoreFileComponent } from './file/file'; import { CoreContextMenuComponent } from './context-menu/context-menu'; import { CoreContextMenuItemComponent } from './context-menu/context-menu-item'; import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-popover'; +import { CoreChronoComponent } from './chrono/chrono'; @NgModule({ declarations: [ @@ -42,7 +44,8 @@ import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-pop CoreFileComponent, CoreContextMenuComponent, CoreContextMenuItemComponent, - CoreContextMenuPopoverComponent + CoreContextMenuPopoverComponent, + CoreChronoComponent ], entryComponents: [ CoreContextMenuPopoverComponent @@ -50,7 +53,8 @@ import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-pop imports: [ IonicModule, TranslateModule.forChild(), - CoreDirectivesModule + CoreDirectivesModule, + CorePipesModule ], exports: [ CoreLoadingComponent, @@ -63,7 +67,8 @@ import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-pop CoreSearchBoxComponent, CoreFileComponent, CoreContextMenuComponent, - CoreContextMenuItemComponent + CoreContextMenuItemComponent, + CoreChronoComponent ] }) export class CoreComponentsModule {} diff --git a/src/pipes/seconds-to-hms.ts b/src/pipes/seconds-to-hms.ts index c88164cbc..8160522a2 100644 --- a/src/pipes/seconds-to-hms.ts +++ b/src/pipes/seconds-to-hms.ts @@ -54,6 +54,9 @@ export class CoreSecondsToHMSPipe implements PipeTransform { seconds = numberSeconds; } + // Don't allow decimals. + seconds = Math.floor(seconds); + hours = Math.floor(seconds / CoreConstants.secondsHour); seconds -= hours * CoreConstants.secondsHour; minutes = Math.floor(seconds / CoreConstants.secondsMinute);