Skip to content

Commit

Permalink
Calendar Date Range Selection (#1054)
Browse files Browse the repository at this point in the history
* fix: date range selection should be accurate when selecting am/pm

* feat: validation for time in date range selection

* fix: possible bug when selecting am / pm could produce incorrect time

* fix: return undefined startDate and endDate to prior state

* fix: tests should reflect internal changes
  • Loading branch information
steveblue authored Sep 27, 2024
1 parent a924fac commit fc407c6
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 97 deletions.
5 changes: 5 additions & 0 deletions projects/swimlane/ngx-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## HEAD (unreleased)

- Enhancement (`ngx-calendar`): Should initialize with Date when `range` Input is used
- Enhancement (`ngx-calendar`): Validation for time in date range selection
- Fix (`ngx-calendar`): Possible bug when emitting date range selection and selecting AM/PM
- Fix (`ngx-calendar`): Possible bug where time could appear `0` when selecting a date range

## 48.0.4 (2024-09-18)

- Enhancement (`ngx-toggle`): Added `timeStamp` when emiting `change`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,23 +163,25 @@
<div class="time-row" *ngIf="range.startDate">
<div>
<ngx-input
type="number"
type="text"
hint="Hour"
label="{{ formatDate(range.startDate) }}"
[ngModel]="startHour"
min="1"
max="12"
[minlength]="1"
[maxlength]="2"
[pattern]="'^([0-9]|0[0-9]|1[0-2])$'"
(change)="hourChanged($event, 'start')"
>
</ngx-input>
</div>
<div>
<ngx-input
type="number"
type="text"
hint="Minute"
[ngModel]="startMinute"
min="0"
max="59"
[minlength]="2"
[maxlength]="2"
[pattern]="'^[0-5]?[0-9]$'"
(change)="minuteChanged($event, 'start')"
>
</ngx-input>
Expand Down Expand Up @@ -208,23 +210,25 @@
<div class="time-row" *ngIf="range.endDate">
<div>
<ngx-input
type="number"
type="text"
hint="Hour"
label="{{ formatDate(range.endDate) }}"
[ngModel]="endHour"
min="1"
max="12"
[minlength]="1"
[maxlength]="2"
[pattern]="'^([0-9]|0[0-9]|1[0-2])$'"
(change)="hourChanged($event, 'end')"
>
</ngx-input>
</div>
<div>
<ngx-input
type="number"
type="text"
hint="Minute"
[ngModel]="endMinute"
min="0"
max="59"
[minlength]="2"
[maxlength]="2"
[pattern]="'^[0-5]?[0-9]$'"
(change)="minuteChanged($event, 'end')"
>
</ngx-input>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface CalendarDateRange {
endDate: Date | undefined;
}

interface CalendarDateRangeSelection {
startDateSelection: boolean;
endDateSelection: boolean;
}

@Component({
selector: 'ngx-calendar',
exportAs: 'ngxCalendar',
Expand Down Expand Up @@ -61,7 +66,10 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
@Input() inputFormats: Array<string | MomentBuiltinFormat> = ['L', 'LT', 'L LT', moment.ISO_8601];
@Input() selectType: string = CalendarSelect.Single;
@Input() dateLabelFormat: string = 'MMM D YYYY';
@Input() range: CalendarDateRange = { startDate: undefined, endDate: undefined };
@Input() range: CalendarDateRange = {
startDate: undefined,
endDate: undefined
};

@Input('minView')
get minView() {
Expand Down Expand Up @@ -114,12 +122,16 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
monthsList = moment.monthsShort();
startYear: number;

startHour: number;
endHour: number;
startMinute: number;
endMinute: number;
startAmPmVal: string;
endAmPmVal: string;
startHour: string;
endHour: string;
startMinute: string;
endMinute: string;
startAmPmVal = 'AM';
endAmPmVal = 'AM';
dateRangeSelection: CalendarDateRangeSelection = {
startDateSelection: false,
endDateSelection: false
};

readonly CalendarView = CalendarView;
readonly CalendarSelect = CalendarSelect;
Expand Down Expand Up @@ -153,7 +165,6 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
} else {
this.currentView = this.minView;
}

this.weeks = getMonth(this.focusDate);
}

Expand All @@ -177,27 +188,23 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
* Initializes the values for initial time
*/
initializeTime(): void {
this.startHour = this.range.startDate
? this.range.startDate.getHours() % 12
: +moment('2001-01-01T00:00:00').format('hh') % 12;
this.endHour = this.range.endDate
? this.range.endDate.getHours() % 12
: +moment('2001-01-01T00:00:00').format('hh') % 12;

this.startMinute = this.range.startDate
? this.range.startDate.getMinutes()
: +moment('2001-01-01T00:00:00').format('mm');
this.endMinute = this.range.endDate ? this.range.endDate.getMinutes() : +moment('2001-01-01T00:00:00').format('mm');
this.startAmPmVal = this.range.startDate
? this.range.startDate.getHours() >= 12
? 'PM'
: 'AM'
: moment('2001-01-01T00:00:00').format('A');
this.endAmPmVal = this.range.endDate
? this.range.endDate.getHours() >= 12
? 'PM'
: 'AM'
: moment('2001-01-01T00:00:00').format('A');
let startDate: Date | undefined, endDate: Date | undefined;
if (!this.range.startDate) {
startDate = new Date(new Date().setHours(0, 0, 0, 0));
} else {
startDate = this.range.startDate;
}
if (!this.range.endDate) {
endDate = new Date(new Date().setHours(0, 0, 0, 0));
} else {
endDate = this.range.endDate;
}
this.startHour = moment(startDate).format('hh');
this.startMinute = moment(startDate).format('mm');
this.startAmPmVal = moment(startDate).format('a').toUpperCase();
this.endHour = moment(endDate).format('hh');
this.endMinute = moment(endDate).format('mm');
this.endAmPmVal = moment(endDate).format('a').toUpperCase();
}

/**
Expand Down Expand Up @@ -324,24 +331,35 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc

if (this.range.startDate === undefined && this.range.endDate === undefined) {
this.range.startDate = this.focusDate.toDate();
this.range.startDate.setHours(this.startHour);
this.range.startDate.setMinutes(+this.startMinute);
this.range.startDate.setHours(Number(this.startHour));
this.range.startDate.setMinutes(Number(this.startMinute));
this.dateRangeSelection.startDateSelection = true;
} else if (this.range.endDate === undefined) {
if (this.focusDate.toDate() > this.range.startDate) {
this.range.endDate = this.focusDate.toDate();
this.range.endDate.setHours(this.endHour);
this.range.endDate.setMinutes(+this.endMinute);
this.range.endDate.setHours(Number(this.endHour));
this.range.endDate.setMinutes(Number(this.endMinute));
} else {
this.range.startDate = this.focusDate.toDate();
this.range.startDate.setHours(this.startHour);
this.range.startDate.setMinutes(+this.startMinute);
this.range.startDate.setHours(Number(this.startHour));
this.range.startDate.setMinutes(Number(this.startMinute));
this.dateRangeSelection.startDateSelection = true;
}
} else {
this.range.startDate = this.focusDate.toDate();
this.range.startDate.setHours(this.startHour);
this.range.startDate.setMinutes(+this.startMinute);
this.range.startDate.setHours(Number(this.startHour));
this.range.startDate.setMinutes(Number(this.startMinute));
this.dateRangeSelection.endDateSelection = false;
this.range.endDate = undefined;
}

if (this.dateRangeSelection.startDateSelection && this.dateRangeSelection.endDateSelection) {
this.dateRangeSelection.startDateSelection = false;
this.dateRangeSelection.endDateSelection = false;
this.range.startDate = undefined;
this.range.endDate = undefined;
}

this.onRangeSelect.emit({ startDate: this.range.startDate, endDate: this.range.endDate });

if (day.prevMonth || day.nextMonth) {
Expand Down Expand Up @@ -375,52 +393,59 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
}
}

hourChanged(newVal: number, type: string) {
newVal = +newVal % 12;
hourChanged(newVal: string, type: string) {
if (type === 'start') {
if (this.range.startDate) {
if (this.startAmPmVal === 'PM') newVal = 12 + newVal;
this.range.startDate.setHours(newVal);
if (this.endAmPmVal === 'PM') {
this.range.startDate.setHours(12 + Number(newVal));
} else {
this.range.startDate.setHours(Number(newVal));
}
}
this.startHour = newVal % 12;
this.startHour = newVal;
} else {
if (this.range.endDate) {
if (this.endAmPmVal === 'PM') newVal = 12 + newVal;
this.range.endDate.setHours(newVal);
if (this.endAmPmVal === 'PM') {
this.range.endDate.setHours(12 + Number(newVal));
} else {
this.range.endDate.setHours(Number(newVal));
}
}
this.endHour = newVal % 12;
this.endHour = newVal;
}
this.onRangeSelect.emit({ startDate: this.range.startDate, endDate: this.range.endDate });
}
minuteChanged(newVal: number, type: string) {
minuteChanged(newVal: string, type: string) {
if (type === 'start') {
if (this.range.startDate) this.range.startDate.setMinutes(newVal);
if (this.range.startDate) {
this.range.startDate.setMinutes(Number(newVal));
}
this.startMinute = newVal;
} else {
if (this.range.endDate) this.range.endDate.setMinutes(newVal);
if (this.range.endDate) {
this.range.endDate.setMinutes(Number(newVal));
}
this.endMinute = newVal;
}
this.onRangeSelect.emit({ startDate: this.range.startDate, endDate: this.range.endDate });
}
onAmPmChange(newVal, type) {
if (type === 'start') {
if (this.range.startDate) {
const hourClone = this.range.startDate.getHours();
if (newVal === 'AM' && this.startAmPmVal === 'PM') {
this.range.startDate.setHours(hourClone - 12);
} else if (newVal === 'PM' && this.startAmPmVal === 'AM') {
this.range.startDate.setHours(hourClone + 12);
}
const hourClone = this.range.startDate.getHours();
if (hourClone >= 12 && newVal === 'AM') {
this.range.startDate.setHours(hourClone - 12);
}
if (hourClone >= 0 && hourClone < 12 && newVal === 'PM') {
this.range.startDate.setHours(hourClone + 12);
}
this.startAmPmVal = newVal;
} else {
if (this.range.endDate) {
const hourClone = this.range.endDate.getHours();
if (newVal === 'AM' && this.endAmPmVal === 'PM') {
this.range.endDate.setHours(hourClone - 12);
} else if (newVal === 'PM' && this.endAmPmVal === 'AM') {
this.range.endDate.setHours(hourClone + 12);
}
const hourClone = this.range.endDate.getHours();
if (hourClone >= 12 && newVal === 'AM') {
this.range.endDate.setHours(hourClone - 12);
}
if (hourClone >= 0 && hourClone < 12 && newVal === 'PM') {
this.range.endDate.setHours(hourClone + 12);
}
this.endAmPmVal = newVal;
}
Expand Down Expand Up @@ -508,8 +533,6 @@ export class CalendarComponent implements OnInit, AfterViewInit, ControlValueAcc
}

onDayDown(event: KeyboardEvent) {
// console.log(event.code);

let stop = false;

if (this.currentView === CalendarView.Date) {
Expand Down
Loading

0 comments on commit fc407c6

Please sign in to comment.