Skip to content

Commit

Permalink
feat(stark-ui): add stark-date-time-picker component
Browse files Browse the repository at this point in the history
  - add component
  - add demo

CLOSES ISSUE: NationalBankBelgium#587
  • Loading branch information
carlo-nomes committed Apr 29, 2019
1 parent 686c0f7 commit a67cd67
Show file tree
Hide file tree
Showing 16 changed files with 378 additions and 27 deletions.
51 changes: 26 additions & 25 deletions packages/stark-ui/assets/stark-ui-bundle.scss
Original file line number Diff line number Diff line change
@@ -1,58 +1,59 @@
/* Stark styles */
@import "../assets/styles/base";
@import "../assets/theming/base-theme";
@import "../assets/styles/base";
@import "../assets/styles/components/button";
@import "../assets/styles/components/button-theme";
@import "../assets/styles/components/icon";
@import "../assets/styles/components/card";
@import "../assets/styles/components/card-theme";
@import "../assets/styles/components/header";
@import "../assets/styles/components/header-theme";
@import "../assets/styles/components/icon";
/* Stark components */
@import "../src/modules/app-logo/components/app-logo.component";
@import "../src/modules/app-logo/components/app-logo-theme";
@import "../src/modules/app-data/components/app-data.component";
@import "../src/modules/action-bar/components/action-bar-theme";
@import "../src/modules/action-bar/components/action-bar.component";
@import "../src/modules/app-data/components/app-data-theme";
@import "../src/modules/app-footer/components/app-footer.component";
@import "../src/modules/app-data/components/app-data.component";
@import "../src/modules/app-footer/components/app-footer-theme";
@import "../src/modules/app-menu/components/app-menu.component";
@import "../src/modules/app-footer/components/app-footer.component";
@import "../src/modules/app-logo/components/app-logo-theme";
@import "../src/modules/app-logo/components/app-logo.component";
@import "../src/modules/app-menu/components/app-menu-theme";
@import "../src/modules/action-bar/components/action-bar.component";
@import "../src/modules/action-bar/components/action-bar-theme";
@import "../src/modules/app-sidebar/components/app-sidebar.component";
@import "../src/modules/app-menu/components/app-menu.component";
@import "../src/modules/app-sidebar/components/app-sidebar-theme";
@import "../src/modules/app-sidebar/components/app-sidebar.component";
@import "../src/modules/breadcrumb/components/breadcrumb.component";
@import "../src/modules/collapsible/components/collapsible.component";
@import "../src/modules/collapsible/components/collapsible-theme";
@import "../src/modules/collapsible/components/collapsible.component";
@import "../src/modules/date-range-picker/components/date-range-picker.component";
@import "../src/modules/date-time-picker/components/date-time-picker.component";
@import "../src/modules/dialogs/components/alert-dialog-theme";
@import "../src/modules/dialogs/components/alert-dialog.component";
@import "../src/modules/dialogs/components/confirm-dialog-theme";
@import "../src/modules/dialogs/components/prompt-dialog.component";
@import "../src/modules/dialogs/components/prompt-dialog-theme";
@import "../src/modules/dialogs/components/prompt-dialog.component";
@import "../src/modules/dropdown/components/dropdown-theme";
@import "../src/modules/dropdown/components/dropdown.component";
@import "../src/modules/generic-search/components/generic-search/generic-search.component";
@import "../src/modules/language-selector/components/language-selector.component";
@import "../src/modules/message-pane/components/message-pane.component";
@import "../src/modules/message-pane/components/message-pane-theme";
@import "../src/modules/minimap/components/minimap.component";
@import "../src/modules/message-pane/components/message-pane.component";
@import "../src/modules/minimap/components/minimap-theme";
@import "../src/modules/slider/components/slider-theme";
@import "../src/modules/pagination/components/pagination.component";
@import "../src/modules/minimap/components/minimap.component";
@import "../src/modules/pagination/components/pagination-theme";
@import "../src/modules/pagination/components/pagination.component";
@import "../src/modules/pretty-print/components/pretty-print.component";
@import "../src/modules/table/components/table.component";
@import "../src/modules/table/components/table-theme";
@import "../src/modules/table/components/dialogs/multisort.component";
@import "../src/modules/dropdown/components/dropdown-theme";
@import "../src/modules/route-search/components/route-search.component";
@import "../src/modules/route-search/components/route-search-theme";
@import "../src/modules/toast-notification/components/toast-notification.component";
@import "../src/modules/toast-notification/components/toast-notification-theme";
@import "../src/modules/dropdown/components/dropdown.component";
@import "../src/modules/route-search/components/route-search.component";
@import "../src/modules/session-ui/components/session-card/session-card.component";
@import "../src/modules/slider/components/slider-theme";
@import "../src/modules/table/components/dialogs/multisort.component";
@import "../src/modules/table/components/table-theme";
@import "../src/modules/table/components/table.component";
@import "../src/modules/toast-notification/components/toast-notification-theme";
@import "../src/modules/toast-notification/components/toast-notification.component";
/* Stark session-ui pages */
@import "../src/modules/session-ui/pages/session-ui-pages";
@import "../src/modules/session-ui/pages/login/login-page.component";
@import "../src/modules/session-ui/pages/preloading/preloading-page.component";
@import "../src/modules/session-ui/pages/session-expired/session-expired-page.component";
@import "../src/modules/session-ui/pages/session-logout/session-logout-page.component";
@import "../src/modules/session-ui/pages/session-ui-pages";
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from "./modules/breadcrumb";
export * from "./modules/collapsible";
export * from "./modules/date-picker";
export * from "./modules/date-range-picker";
export * from "./modules/date-time-picker";
export * from "./modules/dialogs";
export * from "./modules/dropdown";
export * from "./modules/generic-search";
Expand Down
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules/date-time-picker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./date-time-picker/date-time-picker.module";
export * from "./date-time-picker/components";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./components/date-time-picker.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="date-time-wrapper" [formGroup]="dateTimeFormGroup">
<stark-date-picker formControlName="date" (dateChange)="handleChange()"></stark-date-picker>

<input #timeInput [starkTimestampMask]="timeMaskConfig" matInput formControlName="time" (blur)="handleChange()" />
<div class="mat-form-field-suffix mat-datepicker-toggle">
<button mat-icon-button (click)="focusTime($event)">
<mat-icon svgIcon="clock"></mat-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
stark-date-time-picker {
.date-time-wrapper {
width: 100%;
display: flex;

input {
text-align: right;
}

// Time
> input.mat-input-element {
flex: 1;
}

// Date
> stark-date-picker {
flex: 1;
}
}

.mat-datepicker-toggle {
transition: color 500ms;
}

&:not(.floating) .mat-datepicker-toggle {
color: rgba(0, 0, 0, 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* tslint:disable:no-null-keyword */
import { Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, Optional, Output, Self, ViewChild } from "@angular/core";
import { ControlValueAccessor, FormBuilder, FormGroup, NgControl } from "@angular/forms";
import { FocusMonitor, FocusOrigin } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { MatFormFieldControl } from "@angular/material/form-field";
import moment from "moment";
import { Subject } from "rxjs";
import noop from "lodash-es/noop";
import { StarkTimestampMaskConfig } from "../../input-mask-directives/directives/timestamp-mask-config.intf";

@Component({
selector: "stark-date-time-picker",
templateUrl: "./date-time-picker.component.html",
providers: [{ provide: MatFormFieldControl, useExisting: StarkDateTimePickerComponent }]
})
export class StarkDateTimePickerComponent implements MatFormFieldControl<Date>, ControlValueAccessor, OnDestroy {
public stateChanges = new Subject<void>();

private static nextId = 0;
@HostBinding() public id = `stark-date-time-picker${StarkDateTimePickerComponent.nextId++}`;

@HostBinding("attr.aria-describedby") public describedBy = "";

@HostBinding("class.floating")
public get shouldLabelFloat(): boolean {
return this.focused || !this.empty;
}

@ViewChild("timeInput")
public timeInput!: ElementRef<HTMLInputElement>;

/**
* @ignore
*/
public setDescribedByIds(ids: string[]): void {
this.describedBy = ids.join(" ");
}

@Input()
public get value(): Date | null {
return this._value;
}

public set value(value: Date | null) {
this._value = value;
this.stateChanges.next();
}

private _value: Date | null = null;

@Input()
public get placeholder(): string {
return this._placeholder;
}

public set placeholder(plh: string) {
this._placeholder = plh;
this.stateChanges.next();
}

private _placeholder = "";

@Input()
public get required(): boolean {
return this._required;
}

public set required(req: boolean) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}

private _required = false;

@Input()
public get disabled(): boolean {
return this._disabled;
}

public set disabled(dis: boolean) {
this._disabled = coerceBooleanProperty(dis);
}

private _disabled = false;

@Input()
public timeMaskConfig: StarkTimestampMaskConfig = { format: "HH:mm:ss" };

@Output()
public dateTimeChange = new EventEmitter<Date | null>();

/**
* @ignore
*/
private _onChange: (val: Date | null) => void = noop;

/**
* @ignore
*/
private _onTouched: () => void = noop;

public focused = false;

public get empty(): boolean {
return !this.dateTimeFormGroup.value.date && !this.dateTimeFormGroup.value.time;
}

public get errorState(): boolean {
if (!this.ngControl) {
return false;
}

return !!this.ngControl.invalid;
}

public controlType = "stark-date-time-picker";

public dateTimeFormGroup: FormGroup;

public constructor(
private _fb: FormBuilder,
@Optional()
@Self()
public ngControl: NgControl,
private _fm: FocusMonitor,
private _elRef: ElementRef
) {
if (this.ngControl) {
this.ngControl.valueAccessor = this;
}

this.dateTimeFormGroup = this._fb.group({
date: null,
time: null
});

this._fm.monitor(this._elRef.nativeElement, true).subscribe((origin: FocusOrigin) => {
this.focused = !!origin;
this.stateChanges.next();
});
}

/**
* Angular lifecycle method
*/
public ngOnDestroy(): void {
this.stateChanges.complete();
}

/**
* @ignore
*/
public onContainerClick(event: MouseEvent): void {
if ((event.target as Element).tagName.toLowerCase() !== "input") {
this._elRef.nativeElement.querySelector("input").focus();
}
}

/**
* @ignore
*/
public handleChange(): void {
const time = this._parseTime(this.dateTimeFormGroup.value.time);
const date = this.dateTimeFormGroup.value.date || new Date(0);

const timeValues = !time ? [] : [time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds()];
const dateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), ...timeValues);

this._onTouched();
this._onChange(dateTime);
this.dateTimeChange.emit(dateTime);
}

/**
* @ignore
*/
private _parseTime(timeValue: string = ""): Date | null {
const time = moment(timeValue, this.timeMaskConfig.format);
return time.isValid() ? time.toDate() : null;
}

/**
* Focus the input for time
*/
public focusTime(event: Event): void {
event.preventDefault();
event.stopPropagation();

this.timeInput.nativeElement.focus();
}

/*Control Value Accessor methods*/

/**
* @ignore
*/
public writeValue(value: Date): void {
this.value = value;
}

/**
* @ignore
*/
public registerOnChange(fn: (val: Date | null) => void): void {
this._onChange = fn;
}

/**
* @ignore
*/
public registerOnTouched(fn: () => void): void {
this._onTouched = fn;
}

/**
* @ignore
*/
public setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { MatIconModule } from "@angular/material/icon";
import { StarkDatePickerModule } from "../date-picker";
import { StarkInputMaskDirectivesModule } from "../input-mask-directives";
import { StarkDateTimePickerComponent } from "./components/date-time-picker.component";

@NgModule({
imports: [
CommonModule,
MatFormFieldModule,
MatInputModule,
FormsModule,
ReactiveFormsModule,
StarkDatePickerModule,
StarkInputMaskDirectivesModule,
MatButtonModule,
MatIconModule
],
declarations: [StarkDateTimePickerComponent],
exports: [StarkDateTimePickerComponent]
})
export class StarkDateTimePickerModule {}
Loading

0 comments on commit a67cd67

Please sign in to comment.