Skip to content

Commit a7e38af

Browse files
feat(date-range): prefix/suffix directives; streamline display formatting #5732
1 parent 53cc019 commit a7e38af

File tree

7 files changed

+979
-1
lines changed

7 files changed

+979
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { Component, ContentChild, Pipe, PipeTransform, OnInit, Input, Directive, TemplateRef } from '@angular/core';
2+
import { IgxInputGroupComponent } from '../input-group/input-group.component';
3+
import { IgxInputGroupBase } from '../input-group/input-group.common';
4+
import { NgControl } from '@angular/forms';
5+
import { IgxDateTimeEditorDirective } from '../directives/date-time-editor';
6+
import { formatDate } from '@angular/common';
7+
8+
export interface DateRange {
9+
start: Date;
10+
end: Date;
11+
}
12+
13+
/** @hidden @internal */
14+
@Pipe({ name: 'dateRange' })
15+
export class DateRangeFormatPipe implements PipeTransform {
16+
transform(values: DateRange, inputFormat?: string, locale?: string): string {
17+
if (!values) {
18+
return '';
19+
}
20+
const { start, end } = values;
21+
// TODO: move default locale from IgxDateTimeEditorDirective to its commons file/use displayFormat
22+
const startDate = inputFormat ? formatDate(start, inputFormat, locale || 'en') : start?.toLocaleDateString();
23+
const endDate = inputFormat ? formatDate(end, inputFormat, locale || 'en') : end?.toLocaleDateString();
24+
let formatted;
25+
if (start) {
26+
formatted = `${startDate} - `;
27+
if (end) {
28+
formatted += endDate;
29+
}
30+
} else if (end) {
31+
formatted = `${endDate} - `;
32+
if (start) {
33+
formatted += startDate;
34+
}
35+
}
36+
37+
// TODO: no need to set format twice
38+
return formatted ? formatted : '';
39+
}
40+
41+
parse(value: string): DateRange {
42+
const values = value.trim().split(/ - /g);
43+
return {
44+
start: values[0] && new Date(Date.parse(values[0])),
45+
end: values[1] && new Date(Date.parse(values[1]))
46+
};
47+
}
48+
}
49+
50+
/** @hidden @internal */
51+
@Component({
52+
template: ``,
53+
selector: `igx-date-range-base`,
54+
providers: [{ provide: IgxInputGroupBase, useExisting: IgxDateRangeBaseComponent }]
55+
})
56+
class IgxDateRangeBaseComponent extends IgxInputGroupComponent implements OnInit {
57+
@ContentChild(NgControl)
58+
protected ngControl: NgControl;
59+
60+
@ContentChild(IgxDateTimeEditorDirective)
61+
public dateTimeEditor: IgxDateTimeEditorDirective;
62+
63+
/** @hidden @internal */
64+
public ngOnInit(): void {
65+
// this.input.nativeElement.readOnly = true;
66+
}
67+
68+
/** @hidden @internal */
69+
public get nativeElement() {
70+
return this.element.nativeElement;
71+
}
72+
73+
/** @hidden @internal */
74+
public setFocus(): void {
75+
this.input.focus();
76+
}
77+
78+
/** @hidden @internal */
79+
public updateInput(value: Date) {
80+
if (this.ngControl) {
81+
this.ngControl.control.setValue(value);
82+
}
83+
84+
this.dateTimeEditor.value = value;
85+
}
86+
}
87+
88+
@Component({
89+
selector: 'igx-date-start',
90+
templateUrl: '../input-group/input-group.component.html',
91+
providers: [{ provide: IgxInputGroupBase, useExisting: IgxDateStartComponent }]
92+
})
93+
export class IgxDateStartComponent extends IgxDateRangeBaseComponent { }
94+
95+
@Component({
96+
selector: 'igx-date-end',
97+
templateUrl: '../input-group/input-group.component.html',
98+
providers: [{ provide: IgxInputGroupBase, useExisting: IgxDateEndComponent }]
99+
})
100+
export class IgxDateEndComponent extends IgxDateRangeBaseComponent { }
101+
102+
@Component({
103+
selector: 'igx-date-single',
104+
templateUrl: 'igx-date-range-inputs.common.html',
105+
providers: [{ provide: IgxInputGroupBase, useExisting: IgxDateSingleComponent }]
106+
})
107+
export class IgxDateSingleComponent extends IgxDateRangeBaseComponent {
108+
public updateInput(value: any, inputFormat?: string, locale?: string) {
109+
value = new DateRangeFormatPipe().transform(value, inputFormat, locale);
110+
if (this.ngControl) {
111+
this.ngControl.control.setValue(value);
112+
} else {
113+
this.input.value = value;
114+
}
115+
}
116+
}
117+
118+
@Directive({
119+
selector: '[igxDateRangePrefix]',
120+
exportAs: 'igxDateRangePrefix',
121+
})
122+
export class IgxDateRangePrefixDirective {
123+
constructor(public templateRef: TemplateRef<any>) { }
124+
}
125+
126+
@Directive({
127+
selector: '[igxDateRangeSuffix]',
128+
exportAs: 'igxDateRangeSuffix',
129+
})
130+
export class IgxDateRangeSuffixDirective {
131+
constructor(public templateRef: TemplateRef<any>) { }
132+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<div #toggle igxToggle [style.width]="'600px'" (onOpened)="onCalendarOpened()" (onClosing)="onClosing()">
2+
<!-- TODO: create dynamic with overlay service api, move to separate component -->
3+
<igx-calendar #calendar (keydown)="onKeyDown($event)" selection="range" [weekStart]="weekStart"
4+
[hideOutsideDays]="hideOutsideDays" [monthsViewNumber]="monthsViewNumber"
5+
(onSelection)="handleSelection($event)"></igx-calendar>
6+
<div class="buttonsWrapper">
7+
<div *ngIf="mode === 'dialog'">
8+
<div class="doneButton">
9+
<button igxButton type="button" (click)="close()">{{ doneButtonText }}</button>
10+
</div>
11+
</div>
12+
</div>
13+
</div>
14+
15+
<ng-container *ngTemplateOutlet="this.hasProjectedInputs ? startEndTemplate : defTemplate"></ng-container>
16+
17+
<ng-template #singleTemplate>
18+
<div (click)="open()" class="content-wrap">
19+
<ng-content select="igx-date-single"></ng-content>
20+
</div>
21+
</ng-template>
22+
23+
<ng-template #suffixTemplate>
24+
<ng-content select="igx-suffix,[igxSuffix]"></ng-content>
25+
</ng-template>
26+
27+
<ng-template #prefixTemplate>
28+
<ng-content select="igx-prefix,[igxPrefix]"></ng-content>
29+
</ng-template>
30+
31+
<ng-template #hintTemplate>
32+
<ng-content select="igx-hint,[igxHint]"></ng-content>
33+
</ng-template>
34+
35+
<ng-template #labelTemplate>
36+
<ng-content select="[igxLabel]"></ng-content>
37+
</ng-template>
38+
39+
<ng-template #defPrefix>
40+
<ng-container ngProjectAs="igx-prefix">
41+
<igx-prefix (click)="open()">
42+
<igx-icon>
43+
calendar_today
44+
</igx-icon>
45+
</igx-prefix>
46+
</ng-container>
47+
</ng-template>
48+
49+
<ng-template #defLabel>
50+
<label for="singleInput" igxLabel>Range</label>
51+
</ng-template>
52+
53+
<ng-template #startEndTemplate>
54+
<div style="display: flex; flex-flow: row; flex-direction: row;">
55+
<div (click)="open()" style="cursor: pointer;">
56+
<igx-icon>calendar_today</igx-icon>
57+
</div>
58+
<ng-content select="igx-date-start"></ng-content>
59+
<div>to</div> <!-- TODO: make templatable -->
60+
<ng-content select="igx-date-end"></ng-content>
61+
</div>
62+
</ng-template>
63+
64+
<ng-template #defIcon>
65+
<igx-icon>
66+
calendar_today
67+
</igx-icon>
68+
</ng-template>
69+
70+
<ng-template #defTemplate>
71+
<igx-date-single>
72+
<!-- display format instead? what about shortDate, etc? -->
73+
<input #singleInput igxInput [placeholder]="appliedFormat" type="text" readonly>
74+
75+
<igx-prefix *ngIf="!this.prefix && !this.suffix" (click)="open()">
76+
<ng-container *ngTemplateOutlet="defIcon"></ng-container>
77+
</igx-prefix>
78+
79+
<igx-prefix *ngIf="this.prefix" (click)="open()">
80+
<ng-container *ngTemplateOutlet="this.prefix.templateRef"></ng-container>
81+
</igx-prefix>
82+
83+
<ng-container ngProjectAs="igxLabel">
84+
<ng-container *ngTemplateOutlet="this.label ? labelTemplate : defLabel"></ng-container>
85+
</ng-container>
86+
<ng-container ngProjectAs="igx-prefix">
87+
<ng-container *ngTemplateOutlet="prefixTemplate"></ng-container>
88+
</ng-container>
89+
<ng-container ngProjectAs="igx-suffix">
90+
<ng-container *ngTemplateOutlet="suffixTemplate"></ng-container>
91+
</ng-container>
92+
<ng-container ngProjectAs="igx-hint">
93+
<ng-container *ngTemplateOutlet="hintTemplate"></ng-container>
94+
</ng-container>
95+
96+
<igx-suffix *ngIf="this.suffix" (click)="open()">
97+
<ng-container *ngTemplateOutlet="this.suffix.templateRef"></ng-container>
98+
</igx-suffix>
99+
</igx-date-single>
100+
</ng-template>

0 commit comments

Comments
 (0)