Skip to content

Commit

Permalink
feat(timepicker): add timepicker component (#2402)
Browse files Browse the repository at this point in the history
* feat(timepicker): add timepicker component

* feat(timepicker): fix styles

* feat(timepicker): fix comments

* feat(timepicker): fix comments

* feat(timepicker): fix comments

* feat(timepicker): fix comments, add docs

* feat(timepicker): fix comments

* feat(timepicker): fix bugs

* feat(timepicker): add docs

* feat(timepicker): add ControlValueAccessor

* feat(timepicker): fix format issue

* feat(timepicker): add convertToBoolProperty

* feat(timepicker): fix convertToBoolProperty

* fix(tabIndex): fix ampm issue

* fix(tabIndex): fix linter

* fix(tabIndex): fix test

* fix(tabIndex): fix comments

* fix(tabIndex): fix comments

* fix(tabIndex): fix trackBy issue

* feat(timePicker): fix issue with trackBy single column

* feat(timePicker): update timepicker icon

* style: separate big blocks with new lines

* refactor: remove redundant assignment

* refactor: use common project class setting syntax

* refactor: move internal interface to component where it used

* refactor: unify am/pm logic

* refactor(playground): remove moment adapter

* refactor(playground): remove unused modules

* revert(timepicker): remove accidentally added new line in time format

* feat(timepicker): add form control and ngModel showcases

* style: reorder imports as per repo standard

* style: reorder class members as per repo standard

* refactor: make method protected

* feat(timepicker): fix comments

* feat(timepicker): fix cell height

* feat(timepicker): fix docs

* feat(timepicker): fix date-timepicker issue

* feat(timepicker): update docs

* feat: add comment on `onStable` usage

* feat(timepicker): fix comments

* style: add semicolon

* fix: subscribe to blur events once

* fix(timepicker): don't discard value typed input

* refactor: use portal outlet to render portal

* fix(calendar with time): make cd public

As it used outside the component

* fix(timepicker): mark for check when date changes

* refactor: make date time picker default size

* fix: emit change when typing in input

* style(docs): add missing dot

* refactor: add cell work to cell related theme vars

Co-authored-by: Maksim Karatkevich <m.karatkevich@akveo.com>
Co-authored-by: Sergey Andrievskiy <yggg@users.noreply.github.com>
Co-authored-by: Sergey Andrievskiy <s.andrievskiy@akveo.com>
  • Loading branch information
4 people authored Sep 10, 2020
1 parent 4c1f35f commit 70ee391
Show file tree
Hide file tree
Showing 53 changed files with 2,851 additions and 25 deletions.
1 change: 1 addition & 0 deletions docs/assets/images/components/timepicker.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@ export const structure = [
'NbRangepickerComponent',
],
},
{
type: 'tabs',
name: 'Timepicker',
icon: 'timepicker.svg',
source: [
'NbTimePickerDirective',
'NbTimePickerComponent',
],
},
{
type: 'group',
name: 'Modals & Overlays',
Expand Down
4 changes: 3 additions & 1 deletion docs/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"@nebular/auth": ["../src/framework/auth/public_api.ts"],
"@nebular/security": ["../src/framework/security/public_api.ts"],
"@nebular/eva-icons": ["../src/framework/eva-icons/public_api.ts"],
"@nebular/firebase-auth": ["../src/framework/firebase-auth/public_api.ts"]
"@nebular/firebase-auth": ["../src/framework/firebase-auth/public_api.ts"],
"@nebular/date-fns": ["../src/framework/date-fns/public_api.ts"],
"@nebular/moment": ["../src/framework/moment/public_api.ts"]
}
},
"files": [
Expand Down
53 changes: 53 additions & 0 deletions src/app/playground-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,18 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [
component: 'DatepickerShowcaseComponent',
name: 'Datepicker Showcase',
},
{
path: 'date-timepicker-showcase.component',
link: '/datepicker/date-timepicker-showcase.component',
component: 'DateTimepickerShowcaseComponent',
name: 'Date Timepicker Showcase',
},
{
path: 'date-timepicker-single-column.component',
link: '/datepicker/date-timepicker-single-column.component',
component: 'DateTimepickerSingleColumnComponent',
name: 'Date Timepicker Single Column',
},
{
path: 'datepicker-validation.component',
link: '/datepicker/datepicker-validation.component',
Expand All @@ -463,6 +475,47 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [
},
],
},
{
path: 'timepicker',
children: [
{
path: 'timepicker-showcase.component',
link: '/timepicker/timepicker-showcase.component',
component: 'TimepickerShowcaseComponent',
name: 'Timepicker Showcase',
},
{
path: 'timepicker-twelve-hours-format.component',
link: '/timepicker/timepicker-twelve-hours-format.component',
component: 'TimepickerTwelveHoursFormatComponent',
name: 'Timepicker Twelve Hours Format',
},
{
path: 'timepicker-single-column.component',
link: '/timepicker/timepicker-single-column.component',
component: 'TimepickerSingleColumnComponent',
name: 'Timepicker Single Column',
},
{
path: 'timepicker-with-seconds.component',
link: '/timepicker/timepicker-with-seconds.component',
component: 'TimepickerWithSecondsComponent',
name: 'Timepicker With Seconds',
},
{
path: 'timepicker-form-control.component',
link: '/timepicker/timepicker-form-control.component',
component: 'TimepickerFormControlComponent',
name: 'Timepicker Form Control',
},
{
path: 'timepicker-ng-model.component',
link: '/timepicker/timepicker-ng-model.component',
component: 'TimepickerNgModelComponent',
name: 'Timepicker Ng Model',
},
],
},
{
path: 'dialog',
children: [
Expand Down
5 changes: 4 additions & 1 deletion src/framework/date-fns/services/date-fns-date.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export class NbDateFnsDateService extends NbNativeDateService {
@Optional() @Inject(NB_DATE_SERVICE_OPTIONS) options,
) {
super(locale);
this.setLocale(locale);
this.options = options || {};
}

Expand All @@ -51,4 +50,8 @@ export class NbDateFnsDateService extends NbNativeDateService {
getWeekNumber(date: Date): number {
return getWeek(date, this.options.getWeekOptions);
}

getDateFormat(): string {
return 'YYYY-MM-dd';
}
}
68 changes: 68 additions & 0 deletions src/framework/moment/services/moment-date.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ describe('moment-date-service', () => {
expect(isValid).toBeFalsy();
});

it('should validate as correct if time string is valid according to the hours format', () => {
const isValid = dateService.isValidTimeString('14:23:00', 'HH:mm:ss');
expect(isValid).toBeTruthy();
});

it('should validate as correct if time string is valid according to the twelve hours format',
() => {
const isValid = dateService.isValidTimeString('04:23:00 AM', 'hh:mm:ss A');
expect(isValid).toBeTruthy();
});

it('should validate as incorrect if time string is invalid according to the format', () => {
const isValid = dateService.isValidTimeString('24:23:00 AM', 'hh:mm:ss A');
expect(isValid).toBeFalsy();
});

it('should validate as incorrect if time string is completely incorrect', () => {
const isValid = dateService.isValidTimeString('hello, it is a time string', 'hh:mm:ss A');
expect(isValid).toBeFalsy();
});

it('should create today date', () => {
const today = dateService.today();
expect(dateService.isSameDay(today, moment())).toBeTruthy();
Expand All @@ -51,6 +72,27 @@ describe('moment-date-service', () => {
expect(dateService.getMonth(month)).toBe(5);
});

it('should get hour', () => {
const hour = moment().year(2018).month(5).date(15).hour(12);
expect(dateService.getHours(hour)).toBe(12);
});

it('should get minute', () => {
const minute = moment().year(2018).month(5).date(15).hour(12).minute(10);
expect(dateService.getMinutes(minute)).toBe(10);
});

it('should get second', () => {
const second = moment().year(2018).month(5).date(15).hour(12).minute(10).second(24);
expect(dateService.getSeconds(second)).toBe(24);
});

it('should get milliseconds', () => {
const second = moment().year(2018).month(5).date(15).hour(12)
.minute(10).second(24).milliseconds(22);
expect(dateService.getMilliseconds(second)).toBe(22);
});

it('should get year', () => {
const year = moment().year(2018).month(5).date(15);
expect(dateService.getYear(year)).toBe(2018);
Expand Down Expand Up @@ -175,6 +217,32 @@ describe('moment-date-service', () => {
expect(newDate.date()).toEqual(16);
});

it('should set hour', () => {
const newDate = dateService.setHours(moment(), 12);
expect(newDate.hour()).toEqual(12);
});

it('should set minute', () => {
const newDate = dateService.setMinutes(moment(), 30);
expect(newDate.minute()).toEqual(30);
});

it('should set seconds', () => {
const newDate = dateService.setSeconds(moment(), 30);
expect(newDate.seconds()).toEqual(30);
});

it('should add hour', () => {
const newDate = dateService.addHours(moment().hour(2), 1);
expect(newDate.hour()).toEqual(3);
});

it('should add minute', () => {
const newDate = dateService.addMinutes(moment().hour(1).minute(55), 5);
expect(newDate.hour()).toEqual(2);
expect(newDate.minute()).toEqual(0);
});

it('should create date', () => {
const date = dateService.createDate(2018, 6, 16);
expect(date.year()).toEqual(2018);
Expand Down
65 changes: 63 additions & 2 deletions src/framework/moment/services/moment-date.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { NbDateService } from '@nebular/theme';

import * as _moment from 'moment';
// @ts-ignore
import { default as _rollupMoment, Moment } from 'moment';
import { default as _rollupMoment, LongDateFormatKey, Moment } from 'moment';

const moment = _rollupMoment || _moment;

Expand All @@ -25,6 +25,7 @@ export class NbMomentDateService extends NbDateService<Moment> {
days: { [key: string]: string[] },
};

protected readonly TIME_ONLY_FORMAT_KEY: LongDateFormatKey = 'LT';
constructor(@Inject(LOCALE_ID) locale: string) {
super();
this.setLocale(locale);
Expand All @@ -35,6 +36,22 @@ export class NbMomentDateService extends NbDateService<Moment> {
this.setMomentLocaleData(locale);
}

setHours(date: Moment, hour: number): Moment {
return this.clone(date).set({ hour });
}

setMinutes(date: Moment, minute: number): Moment {
return this.clone(date).set({ minute });
}

setSeconds(date: Moment, second: number): Moment {
return this.clone(date).set({ second });
}

setMilliseconds(date: Moment, milliseconds: number): Moment {
return this.clone(date).set({ milliseconds });
}

addDay(date: Moment, days: number): Moment {
return this.clone(date).add({ days });
}
Expand All @@ -47,10 +64,22 @@ export class NbMomentDateService extends NbDateService<Moment> {
return this.clone(date).add({ years });
}

addMinutes(date: Moment, minute: number): Moment {
return this.clone(date).add( { minute });
}

addHours(date: Moment, hour: number): Moment {
return this.clone(date).add( { hour });
}

clone(date: Moment): Moment {
return date.clone().locale(this.locale);
}

valueOf(date: Moment): number {
return date.valueOf();
}

compareDates(date1: Moment, date2: Moment): number {
return this.getYear(date1) - this.getYear(date2) ||
this.getMonth(date1) - this.getMonth(date2) ||
Expand All @@ -69,6 +98,10 @@ export class NbMomentDateService extends NbDateService<Moment> {
return '';
}

getLocaleTimeFormat(): string {
return moment.localeData().longDateFormat(this.TIME_ONLY_FORMAT_KEY);
}

getDate(date: Moment): number {
return this.clone(date).date();
}
Expand All @@ -86,7 +119,23 @@ export class NbMomentDateService extends NbDateService<Moment> {
}

getMonth(date: Moment): number {
return this.clone(date).month();
return date.month();
}

getHours(date: Moment): number {
return date.hour();
}

getMinutes(date: Moment): number {
return date.minute();
}

getSeconds(date: Moment): number {
return date.second();
}

getMilliseconds(date: Moment): number {
return date.milliseconds();
}

getMonthEnd(date: Moment): Moment {
Expand Down Expand Up @@ -138,6 +187,10 @@ export class NbMomentDateService extends NbDateService<Moment> {
return moment(date, format).isValid();
}

isValidTimeString(date: string, format: string): boolean {
return moment(date, format, true).isValid();
}

parse(date: string, format: string): Moment {
return moment(date, format);
}
Expand Down Expand Up @@ -171,4 +224,12 @@ export class NbMomentDateService extends NbDateService<Moment> {
getWeekNumber(date: Moment): number {
return date.week();
}

getDateFormat(): string {
return 'YYYY-MM-DD';
}

getTwelveHoursFormat(): string {
return 'hh:mm A';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ import { NbCalendarWeekNumberComponent } from './components/calendar-week-number

import { NbNativeDateService } from './services/native-date.service';
import { NbCalendarYearModelService } from './services/calendar-year-model.service';
import { NbCalendarTimeModelService } from './services/calendar-time-model.service';
import { NbCalendarActionsComponent } from './components/calendar-actions/calendar-actions.component';


const SERVICES = [
{ provide: NbDateService, useClass: NbNativeDateService },
DatePipe,
NbCalendarMonthModelService,
NbCalendarYearModelService,
NbCalendarTimeModelService,
];

const COMPONENTS = [
Expand All @@ -48,6 +51,7 @@ const COMPONENTS = [
NbCalendarMonthPickerComponent,
NbCalendarDayPickerComponent,
NbCalendarDayCellComponent,
NbCalendarActionsComponent,
NbCalendarMonthCellComponent,
NbCalendarYearCellComponent,
NbCalendarPickerRowComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: flex;
justify-content: space-between;
}
Loading

0 comments on commit 70ee391

Please sign in to comment.