diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c05c84a..4ac25562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Change Log All notable changes to this project will be documented in this file +## 2.9.0 (2018-11-24) + +### Features + +* feat(timepicker dial): add accessibility from keyboard and input restrictions for a dial, closes [(#56)](https://github.com/Agranom/ngx-material-timepicker/issues/56) + +## 2.8.4 (2018-11-20) + +### Fixes + +* fix(ExpressionChangedAfterItHasBeenCheckedError): closes [(#56)](https://github.com/Agranom/ngx-material-timepicker/issues/56) ## 2.8.4 (2018-11-20) diff --git a/package-lock.json b/package-lock.json index af7adf50..30f573d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ngx-material-timepicker", - "version": "2.8.3", + "version": "2.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -19234,6 +19234,31 @@ } } }, + "tsickle": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.34.0.tgz", + "integrity": "sha512-O3wCPRtL18Hc/ZBnaiKwmmjVzeCWTOTpsi0btfC7FWL3RnXpxLPxD6hoJ0QEXuSfG/0QJk+MWNjqT9N6fOyyIg==", + "dev": true, + "requires": { + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map": "0.7.3" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, "tslib": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", diff --git a/package.json b/package.json index d671c7de..6a8f7e3f 100644 --- a/package.json +++ b/package.json @@ -1,84 +1,85 @@ { - "name": "ngx-material-timepicker", - "description": "Handy material design timepicker for angular", - "version": "2.8.4", - "license": "MIT", - "author": "Vitalii Boiko ", - "keywords": [ - "angular", - "angular 2", - "angular 4", - "angular 5", - "angular 6", - "angular 7", - "material design", - "timepicker", - "material timepicker", - "ngx timepicker", - "angular timepicker", - "ng timepicker" - ], - "repository": { - "type": "git", - "url": "https://github.com/Agranom/ngx-material-timepicker.git" - }, - "bugs": { - "url": "https://github.com/Agranom/ngx-material-timepicker/issues" - }, - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build --prod", - "lint": "ng lint", - "test": "ng test --watch=false --code-coverage --browsers ChromeHeadlessNoSandbox", - "test:watch": "ng test --code-coverage", - "codecov": "codecov", - "packagr": "ng-packagr -p ng-package.json", - "publish": "run-s packagr && npm publish dist", - "gh-publish": "ng build --prod --base-href ngx-material-timepicker && gh-pages -d dist" - }, - "types": ".src/app/material-timepicker/index.d.ts", - "private": false, - "peerDependencies": {}, - "dependencies": { - "moment": "^2.21.0" - }, - "devDependencies": { - "@angular-devkit/build-angular": "~0.6.5", - "@angular/animations": "^7.0.3", - "@angular/cli": "^7.0.5", - "@angular/common": "^7.0.3", - "@angular/compiler": "^7.0.3", - "@angular/compiler-cli": "^7.0.3", - "@angular/core": "^7.0.3", - "@angular/forms": "^7.0.3", - "@angular/http": "^7.0.3", - "@angular/language-service": "^7.0.3", - "@angular/platform-browser": "^7.0.3", - "@angular/platform-browser-dynamic": "^7.0.3", - "@angular/router": "^7.0.3", - "@types/jasmine": "~2.8.3", - "@types/jasminewd2": "~2.0.2", - "@types/node": "~6.0.60", - "codecov": "^3.1.0", - "codelyzer": "^4.0.1", - "core-js": "^2.4.1", - "jasmine-core": "~2.8.0", - "jasmine-spec-reporter": "~4.2.1", - "karma": "~2.0.0", - "karma-chrome-launcher": "~2.2.0", - "karma-coverage-istanbul-reporter": "^1.2.1", - "karma-jasmine": "~1.1.0", - "karma-jasmine-html-reporter": "^0.2.2", - "ng-packagr": "^4.4.0", - "ngx-material-timepicker": "^2.8.3", - "npm-run-all": "^4.1.3", - "protractor": "~5.1.2", - "rxjs": "^6.3.3", - "travis-deploy-once": "^5.0.0", - "ts-node": "~4.1.0", - "tslint": "~5.9.1", - "typescript": "3.1.6", - "zone.js": "^0.8.26" - } + "name": "ngx-material-timepicker", + "description": "Handy material design timepicker for angular", + "version": "2.9.0", + "license": "MIT", + "author": "Vitalii Boiko ", + "keywords": [ + "angular", + "angular 2", + "angular 4", + "angular 5", + "angular 6", + "angular 7", + "material design", + "timepicker", + "material timepicker", + "ngx timepicker", + "angular timepicker", + "ng timepicker" + ], + "repository": { + "type": "git", + "url": "https://github.com/Agranom/ngx-material-timepicker.git" + }, + "bugs": { + "url": "https://github.com/Agranom/ngx-material-timepicker/issues" + }, + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build --prod", + "lint": "ng lint", + "test": "ng test --watch=false --code-coverage --browsers ChromeHeadlessNoSandbox", + "test:watch": "ng test --code-coverage", + "codecov": "codecov", + "packagr": "ng-packagr -p ng-package.json", + "publish": "run-s packagr && npm publish dist", + "gh-publish": "ng build --prod --base-href ngx-material-timepicker && gh-pages -d dist" + }, + "types": ".src/app/material-timepicker/index.d.ts", + "private": false, + "peerDependencies": { + "moment": "^2.21.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.6.5", + "@angular/animations": "^7.0.3", + "@angular/cli": "^7.0.5", + "@angular/common": "^7.0.3", + "@angular/compiler": "^7.0.3", + "@angular/compiler-cli": "^7.0.3", + "@angular/core": "^7.0.3", + "@angular/forms": "^7.0.3", + "@angular/http": "^7.0.3", + "@angular/language-service": "^7.0.3", + "@angular/platform-browser": "^7.0.3", + "@angular/platform-browser-dynamic": "^7.0.3", + "@angular/router": "^7.0.3", + "@types/jasmine": "~2.8.3", + "@types/jasminewd2": "~2.0.2", + "@types/node": "~6.0.60", + "codecov": "^3.1.0", + "codelyzer": "^4.0.1", + "core-js": "^2.4.1", + "jasmine-core": "~2.8.0", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~2.0.0", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "^1.2.1", + "karma-jasmine": "~1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", + "ng-packagr": "^4.4.0", + "ngx-material-timepicker": "^2.8.3", + "npm-run-all": "^4.1.3", + "protractor": "~5.1.2", + "rxjs": "^6.3.3", + "travis-deploy-once": "^5.0.0", + "ts-node": "~4.1.0", + "tsickle": "^0.34.0", + "tslint": "~5.9.1", + "typescript": "3.1.6", + "zone.js": "^0.8.26", + "moment": "^2.21.0" + } } diff --git a/src/app/app.component.html b/src/app/app.component.html index 7d0795af..e78d55b1 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -175,7 +175,7 @@

Examples

- +
diff --git a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.html b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.html index 966cc93b..137dc727 100644 --- a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.html +++ b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.html @@ -2,4 +2,4 @@ + (blur)="formatTime()" [readonly]="!isEditable" [timepickerAutofocus]="isActive" (keydown)="onKeyDown($event)"> diff --git a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.scss b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.scss index 3413d7b4..7c9b6a65 100644 --- a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.scss +++ b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.scss @@ -9,7 +9,7 @@ padding: 0; border-radius: 3px; &_editable { - &:focus, &::selection { + &:focus { color: $primary-color; background-color: #fff; outline: $primary-color; diff --git a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.spec.ts b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.spec.ts index 0b22a228..5ec1ac48 100644 --- a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.spec.ts +++ b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.spec.ts @@ -1,7 +1,8 @@ import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {NgxMaterialTimepickerDialControlComponent} from './ngx-material-timepicker-dial-control.component'; -import {NO_ERRORS_SCHEMA} from '@angular/core'; +import {NO_ERRORS_SCHEMA, SimpleChanges} from '@angular/core'; import {TimeUnit} from '../../models/time-unit.enum'; +import {TimepickerTime} from '../../timepicker-time.namespace'; describe('NgxMaterialTimepickerDialControlComponent', () => { let fixture: ComponentFixture; @@ -20,12 +21,12 @@ describe('NgxMaterialTimepickerDialControlComponent', () => { let timeUnit = null; component.timeUnitChanged.subscribe(unit => timeUnit = unit); - component.time = 10; + component.time = '10'; expect(component.previousTime).toBeUndefined(); component.saveTimeAndChangeTimeUnit({preventDefault: () => null} as FocusEvent, TimeUnit.MINUTE); - expect(component.previousTime).toBe(10); + expect(component.previousTime).toBe('10'); expect(timeUnit).toBe(TimeUnit.MINUTE); })); @@ -34,7 +35,7 @@ describe('NgxMaterialTimepickerDialControlComponent', () => { let time = null; component.timeList = [timeMock]; component.timeChanged.subscribe(t => time = t); - component.time = 1; + component.time = '1'; component.updateTime(); tick(); @@ -42,12 +43,12 @@ describe('NgxMaterialTimepickerDialControlComponent', () => { expect(component.previousTime).toBe(1); })); - it('should not emit changed time if it exists and disabled', fakeAsync(() => { - const timeMock = {time: 1, angle: 30, disabled: true}; + it('should not emit changed time if it does not exists', fakeAsync(() => { + const timeMock = {time: 1, angle: 30}; let time = null; component.timeList = [timeMock]; component.timeChanged.subscribe(t => time = t); - component.time = 1; + component.time = ''; component.updateTime(); tick(); @@ -55,35 +56,168 @@ describe('NgxMaterialTimepickerDialControlComponent', () => { expect(component.previousTime).toBeUndefined(); })); - it('should revert previous time if no time exists', () => { - const timeMock = {time: 1, angle: 30, disabled: false}; - component.timeList = [timeMock]; - component.time = 2; - component.previousTime = 1; + it('should format time once it changes', () => { + const changes: SimpleChanges = { + time: { + previousValue: undefined, + currentValue: 1, + firstChange: true, + isFirstChange: () => null + } + }; component.timeUnit = TimeUnit.HOUR; - component.revertTimeAndFormat(); + component.ngOnChanges(changes); - expect(component.time.toString()).toBe('01'); + expect(component.time).toBe('01'); }); - it('should revert previous time if time is disabled', () => { - const timeMock = {time: 1, angle: 30, disabled: true}; - component.timeList = [timeMock]; - component.time = 1; - component.previousTime = 2; + it('should not format time if editable and the second change', () => { + const changes: SimpleChanges = { + time: { + previousValue: undefined, + currentValue: '1', + firstChange: false, + isFirstChange: () => null + } + }; + component.time = '4'; component.timeUnit = TimeUnit.HOUR; - component.revertTimeAndFormat(); + component.isEditable = true; + component.ngOnChanges(changes); - expect(component.time.toString()).toBe('02'); + expect(component.time).toBe('4'); }); - it(`should format time from '1' to '01'`, () => { - const timeMock = {time: 1, angle: 30, disabled: false}; - component.timeList = [timeMock]; - component.time = 1; - component.timeUnit = TimeUnit.HOUR; - component.revertTimeAndFormat(); + it('should do nothing', () => { + const changes: SimpleChanges = { + timeUnit: { + previousValue: undefined, + currentValue: null, + firstChange: false, + isFirstChange: () => null + } + }; + component.time = '4'; + component.ngOnChanges(changes); + expect(component.time).toBe('4'); + }); + + it('should format time if editable', () => { + component.isEditable = true; + component.time = '2'; + component.previousTime = 4; + component.timeUnit = TimeUnit.MINUTE; + + component.formatTime(); + expect(component.time).toBe('02'); + + component.time = ''; + component.formatTime(); + expect(component.time).toBe('04'); + + component.time = '5'; + component.isEditable = false; + component.formatTime(); + expect(component.time).toBe('5'); + }); - expect(component.time.toString()).toBe('01'); + describe('onKeyDown', () => { + let counter = 0; + const event = { + keyCode: 0, preventDefault: () => { + counter++ + } + } as KeyboardEvent; + const numbers = Array(10).fill(48).map((v, i) => v + i); + const numpadNumbers = Array(10).fill(96).map((v, i) => v + i); + const arrows = Array(6).fill(35).map((v, i) => v + i); // home, end, left, right, up, down + const specialKeys = [46, 8, 9, 27, 13]; // backspace, delete, tab, escape, enter + + beforeEach(() => { + counter = 0; + component.timeList = TimepickerTime.getHours(24); + }); + + + it('should allow numbers', () => { + + const keyCodes = numbers.concat(numpadNumbers); + component.time = ''; + + + keyCodes.forEach(code => { + component.onKeyDown({...event, keyCode: code}); + expect(counter).toBe(0); + }); + }); + + it('should allow backspace, delete, tab, escape, enter', () => { + specialKeys.forEach(code => { + component.onKeyDown({...event, keyCode: code}); + expect(counter).toBe(0); + }); + }); + + it('should allow home, end, left, right, up, down', () => { + arrows.forEach(code => { + component.onKeyDown({...event, keyCode: code}); + expect(counter).toBe(0); + }); + }); + + it('should allow ctrl/cmd+a, ctrl/cmd+c, ctrl/cmd+x', () => { + const chars = [65, 67, 88]; + + chars.forEach(code => { + component.onKeyDown({...event, keyCode: code, ctrlKey: true}); + expect(counter).toBe(0); + }); + + chars.forEach(code => { + component.onKeyDown({...event, keyCode: code, metaKey: true}); + expect(counter).toBe(0); + }); + }); + + it('should not allow chars but numbers, backspace, delete, tab, escape, enter, home, end, left, right, up, down', () => { + const allKeyCodes = Array(114).fill(8).map((v, i) => v + i); + const allowedCodes = [...numbers, ...numpadNumbers, ...specialKeys, ...arrows]; + const restrictedCodes = allKeyCodes.filter(code => !allowedCodes.includes(code)); + + restrictedCodes.forEach((code, index) => { + component.onKeyDown({...event, keyCode: code}); + expect(counter).toBe(index + 1); + }) + }); + + it('should call preventDefault if no time exist or time disabled', () => { + const NUM_1 = 49; // 1 + component.timeList = [{time: 1, angle: 30, disabled: true}]; + component.time = '1'; + + + component.onKeyDown({...event, keyCode: NUM_1}); + expect(counter).toBe(1); + + component.time = ''; + component.onKeyDown({...event, keyCode: NUM_1}); + expect(counter).toBe(2) + }); + + it('should up time by 1', () => { + const ARROW_UP = 38; + component.time = '11'; + + component.onKeyDown({...event, keyCode: ARROW_UP}); + expect(component.time).toBe('12'); + }); + + it('should down time by 1', () => { + const ARROW_DOWN = 40; + component.time = '11'; + + component.onKeyDown({...event, keyCode: ARROW_DOWN}); + expect(component.time).toBe('10'); + }); }); }); diff --git a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.ts b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.ts index bdd25811..15989e83 100644 --- a/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.ts +++ b/src/app/material-timepicker/components/timepicker-dial-control/ngx-material-timepicker-dial-control.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnChanges, Output, SimpleChanges} from '@angular/core'; import {ClockFaceTime} from '../../models/clock-face-time.interface'; import {TimeUnit} from '../../models/time-unit.enum'; import {TimeFormatterPipe} from '../../pipes/time-formatter.pipe'; @@ -8,13 +8,13 @@ import {TimeFormatterPipe} from '../../pipes/time-formatter.pipe'; templateUrl: 'ngx-material-timepicker-dial-control.component.html', styleUrls: ['ngx-material-timepicker-dial-control.component.scss'] }) -export class NgxMaterialTimepickerDialControlComponent { +export class NgxMaterialTimepickerDialControlComponent implements OnChanges { previousTime: number | string; @Input() timeList: ClockFaceTime[]; - @Input() time: number | string; @Input() timeUnit: TimeUnit; + @Input() time: string; @Input() isActive: boolean; @Input() isEditable: boolean; @@ -22,7 +22,18 @@ export class NgxMaterialTimepickerDialControlComponent { @Output() timeChanged = new EventEmitter(); private get selectedTime(): ClockFaceTime { - return this.timeList.find(t => +t.time === +this.time); + if (!!this.time) { + return this.timeList.find(t => t.time === +this.time); + } + } + + ngOnChanges(changes: SimpleChanges) { + if (changes['time'] && (changes['time'].currentValue !== undefined)) { + if (this.isEditable && !changes['time'].firstChange) { + return; + } + this.time = new TimeFormatterPipe().transform(+changes['time'].currentValue, this.timeUnit) + } } saveTimeAndChangeTimeUnit(event: FocusEvent, unit: TimeUnit): void { @@ -33,17 +44,78 @@ export class NgxMaterialTimepickerDialControlComponent { updateTime(): void { const time = this.selectedTime; - if (time && !time.disabled) { + if (time) { this.timeChanged.next(time); this.previousTime = time.time; } } - revertTimeAndFormat(): void { - const time = this.selectedTime; - if (!time || time.disabled) { - this.time = this.previousTime; + formatTime(): void { + if (this.isEditable) { + const time = this.time || this.previousTime; + this.time = new TimeFormatterPipe().transform(+time, this.timeUnit); } - this.time = new TimeFormatterPipe().transform(+this.time, this.timeUnit); } + + onKeyDown(e: KeyboardEvent): void { + const char = String.fromCharCode(e.keyCode); + + + if ((!isInputAllowed(e)) || isTimeDisabledToChange(this.time, char, this.timeList)) { + e.preventDefault(); + } + + if (isInputAllowed(e)) { + this.changeTimeByArrow(e.keyCode); + } + } + + private changeTimeByArrow(keyCode: number): void { + const ARROW_UP = 38; + const ARROW_DOWN = 40; + let time: string; + + if (keyCode === ARROW_UP) { + time = String(+this.time + 1); + } else if (keyCode === ARROW_DOWN) { + time = String(+this.time - 1); + } + + if (!isTimeUnavailable(time, this.timeList)) { + this.time = time; + this.updateTime(); + } + } + +} + +function isInputAllowed(e: KeyboardEvent): boolean { + // Allow: backspace, delete, tab, escape, enter + if ([46, 8, 9, 27, 13].some(n => n === e.keyCode) || + // Allow: Ctrl/cmd+A + (e.keyCode == 65 && (e.ctrlKey === true || e.metaKey === true)) || + // Allow: Ctrl/cmd+C + (e.keyCode == 67 && (e.ctrlKey === true || e.metaKey === true)) || + // Allow: Ctrl/cmd+X + (e.keyCode == 88 && (e.ctrlKey === true || e.metaKey === true)) || + // Allow: home, end, left, right, up, down + (e.keyCode >= 35 && e.keyCode <= 40)) { + + return true; + } + return !((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105)) +} + +function isTimeDisabledToChange(currentTime: string, nextTime: string, timeList: ClockFaceTime[]): boolean { + const isNumber = /\d/.test(nextTime); + + if (isNumber) { + const time = currentTime + nextTime; + return isTimeUnavailable(time, timeList); + } +} + +function isTimeUnavailable(time: string, timeList: ClockFaceTime[]): boolean { + const selectedTime = timeList.find(value => value.time === +time); + return !selectedTime || (selectedTime && selectedTime.disabled); } diff --git a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.html b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.html index d8bdd19c..15703998 100644 --- a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.html +++ b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.html @@ -14,7 +14,7 @@ *ngFor="let time of faceTime.slice(12, 24); trackBy: trackByTime"> - {{time.time}} + {{time.time === 0 ? '00' : time.time}} @@ -29,7 +29,7 @@ *ngFor="let time of faceTime; trackBy: trackByTime"> - {{time.time | minutesFormatter: minutesGap}} + {{time.time === 0 ? '00' : time.time | minutesFormatter: minutesGap}} diff --git a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.spec.ts b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.spec.ts index bf49bc83..2cda5f32 100644 --- a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.spec.ts +++ b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.spec.ts @@ -47,7 +47,7 @@ describe('NgxMaterialTimepickerFaceComponent', () => { }); it('should decrease clock hand with format 24 and time is \'00\' ', () => { - component.selectedTime = {time: '00', angle: 30}; + component.selectedTime = {time: 0, angle: 30}; component.format = 24; component.ngAfterViewInit(); diff --git a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.ts b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.ts index f642f83c..f2f859bf 100644 --- a/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.ts +++ b/src/app/material-timepicker/components/timepicker-face/ngx-material-timepicker-face.component.ts @@ -159,7 +159,7 @@ export class NgxMaterialTimepickerFaceComponent implements AfterViewInit, OnChan private setClockHandPosition(): void { if (this.format === 24) { - if (this.selectedTime.time > 12 || this.selectedTime.time === '00') { + if (this.selectedTime.time > 12 || this.selectedTime.time === 0) { this.decreaseClockHand(); } else { this.increaseClockHand(); diff --git a/src/app/material-timepicker/components/timepicker-period/ngx-material-timepicker-period.component.scss b/src/app/material-timepicker/components/timepicker-period/ngx-material-timepicker-period.component.scss index d2add470..a8712916 100644 --- a/src/app/material-timepicker/components/timepicker-period/ngx-material-timepicker-period.component.scss +++ b/src/app/material-timepicker/components/timepicker-period/ngx-material-timepicker-period.component.scss @@ -6,7 +6,7 @@ flex-direction: column; position: relative; &__btn { - padding: 0; + padding: 1px 3px; border: 0; background-color: transparent; font-size: 18px; @@ -14,6 +14,11 @@ font-family: $primary-font-family; user-select: none; outline: none; + border-radius: 3px; + transition: background-color .5s; + &:focus { + background-color: rgba(0, 0, 0, .07); + } } &__warning { padding: 5px 10px; diff --git a/src/app/material-timepicker/models/clock-face-time.interface.ts b/src/app/material-timepicker/models/clock-face-time.interface.ts index 3fc3309e..e5a9ec41 100644 --- a/src/app/material-timepicker/models/clock-face-time.interface.ts +++ b/src/app/material-timepicker/models/clock-face-time.interface.ts @@ -1,5 +1,5 @@ export interface ClockFaceTime { - time: number | string; - angle: number | string; + time: number; + angle: number; disabled?: boolean; } diff --git a/src/app/material-timepicker/ngx-material-timepicker.component.html b/src/app/material-timepicker/ngx-material-timepicker.component.html index 30813c47..510d4367 100644 --- a/src/app/material-timepicker/ngx-material-timepicker.component.html +++ b/src/app/material-timepicker/ngx-material-timepicker.component.html @@ -2,8 +2,8 @@
- { angle: 360 }; const DEFAULT_MINUTE: ClockFaceTime = { - time: '00', + time: 0, angle: 360 }; @@ -68,8 +68,8 @@ describe('NgxMaterialTimepickerService', () => { timepickerService.defaultTime = '00:00'; - expect(selectedHour).toEqual({...DEFAULT_HOUR, time: '00'}); - expect(selectedMinute).toEqual({...DEFAULT_MINUTE, time: '00'}); + expect(selectedHour).toEqual({...DEFAULT_HOUR, time: 0}); + expect(selectedMinute).toEqual({...DEFAULT_MINUTE, time: 0}); }); it('should reset time if default time is invalid', () => { diff --git a/src/app/material-timepicker/services/ngx-material-timepicker.service.ts b/src/app/material-timepicker/services/ngx-material-timepicker.service.ts index 3663fc7d..d5e5acf7 100644 --- a/src/app/material-timepicker/services/ngx-material-timepicker.service.ts +++ b/src/app/material-timepicker/services/ngx-material-timepicker.service.ts @@ -13,7 +13,7 @@ const DEFAULT_HOUR: ClockFaceTime = { angle: 360 }; const DEFAULT_MINUTE: ClockFaceTime = { - time: '00', + time: 0, angle: 360 }; @@ -52,8 +52,8 @@ export class NgxMaterialTimepickerService { const defaultTime = moment(time, TimeFormat.TWENTY_FOUR).toDate(); if (moment(defaultTime).isValid()) { - this.hour = {...DEFAULT_HOUR, time: defaultTime.getHours() === 0 ? '00' : defaultTime.getHours()}; - this.minute = {...DEFAULT_MINUTE, time: defaultTime.getMinutes() === 0 ? '00' : defaultTime.getMinutes()}; + this.hour = {...DEFAULT_HOUR, time: defaultTime.getHours()}; + this.minute = {...DEFAULT_MINUTE, time: defaultTime.getMinutes()}; this.period = time.substr(time.length - 2).toUpperCase(); } else { this.resetTime(); diff --git a/src/app/material-timepicker/timepicker-time.namespace.spec.ts b/src/app/material-timepicker/timepicker-time.namespace.spec.ts index 3eb3c25b..e9fed8fc 100644 --- a/src/app/material-timepicker/timepicker-time.namespace.spec.ts +++ b/src/app/material-timepicker/timepicker-time.namespace.spec.ts @@ -21,7 +21,7 @@ describe('TimepickerTime', () => { const angleStep = 30; const time = i + 1; - expect(hours[i]).toEqual({time: time === 24 ? '00' : time, angle: time * angleStep}); + expect(hours[i]).toEqual({time: time === 24 ? 0 : time, angle: time * angleStep}); } }); @@ -111,7 +111,7 @@ describe('TimepickerTime', () => { for (let i = 0; i < minutes.length; i++) { const angle = i * angleStep; - expect(minutes[i]).toEqual({time: i === 0 ? '00' : i, angle: angle !== 0 ? angle : 360}); + expect(minutes[i]).toEqual({time: i, angle: angle !== 0 ? angle : 360}); } }); @@ -125,7 +125,7 @@ describe('TimepickerTime', () => { for (let i = 0; i < minutesWithGap.length; i++) { const angle = i * angleStep * gap; - expect(minutesWithGap[i]).toEqual({time: i === 0 ? '00' : i * gap, angle: angle !== 0 ? angle : 360}); + expect(minutesWithGap[i]).toEqual({time: i * gap, angle: angle !== 0 ? angle : 360}); } }); @@ -139,7 +139,7 @@ describe('TimepickerTime', () => { expect(disabledMinutes.length).toBe(10); for (let i = 0; i < disabledMinutes.length; i++) { - const time = disabledMinutes[i].time === '00' ? 0 : disabledMinutes[i].time; + const time = disabledMinutes[i].time; expect(time).toBe(i); } diff --git a/src/app/material-timepicker/timepicker-time.namespace.ts b/src/app/material-timepicker/timepicker-time.namespace.ts index 7f7393cf..3994f20c 100644 --- a/src/app/material-timepicker/timepicker-time.namespace.ts +++ b/src/app/material-timepicker/timepicker-time.namespace.ts @@ -13,7 +13,7 @@ export namespace TimepickerTime { const angleStep = 30; const time = v + i; const angle = angleStep * time; - return {time: time === 24 ? '00' : time, angle}; + return {time: time === 24 ? 0 : time, angle}; }); } @@ -41,7 +41,7 @@ export namespace TimepickerTime { for (let i = 0; i < minutesCount; i++) { const angle = angleStep * i; if (i % gap === 0) { - minutes.push({time: i === 0 ? '00' : i, angle: angle !== 0 ? angle : 360}); + minutes.push({time: i, angle: angle !== 0 ? angle : 360}); } } return minutes;