Skip to content

Commit

Permalink
adding autoselect range
Browse files Browse the repository at this point in the history
  • Loading branch information
cameronpettit committed Feb 16, 2024
1 parent 575bd78 commit 2884566
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
(changeYear)="setYearToValue($event, startCalendar)" (changeHoverDate)="updateHoverDate($event)"
(changeDisplay)="changeDisplayByValue($event)" (clearDates)="clearDates.emit()"></ngds-calendar>
</div>
<div *ngIf="dateRange" class="col-auto overflow-auto">
<div *ngIf="dateRange && !hideSecondCalendar" class="col-auto overflow-auto">
<!-- Render 2 calendars for rangepicker -->
<ngds-calendar [calendarConfig]="endCalendar" [disabledDatesFn]="disabledDatesFn" [dateRange]="dateRange"
[displayDepth]="displayDepth.value" [selectedDate]="selectedDate" [hoverDate]="hoverDate"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export class NgdsCalendarManager implements OnInit {

// The precision of the datepicker. By default (0), dates can be picked.
// If minDisplayDepth is 1 or 2, only months or years will be displayed and enabled, respectively.
// When picking months or years, the datepicker will return the luxon startOf('term'), where
// 'term' is months or years, respectively.
@Input() minDisplayDepth: number = 0; // date, 1 = month, 2 = year
// When picking months or years, the datepicker will return the luxon startOf('term'), where
// 'term' is months or years, respectively.
@Input() minDisplayDepth: number = 0; // date, 1 = month, 2 = year

// The current selected date of the calendar (start date if rangepicker).
@Input() selectedDate;
Expand All @@ -50,7 +50,7 @@ export class NgdsCalendarManager implements OnInit {
// A function that returns a boolean when provided a DateTime. DateTimes that return true are disabled in the calendar.
@Input() disabledDatesFn;

// The maximum number of days that can be selected in a rangepicker. Default (0) is unlimited days.
// The maximum number of days that can be selected in a rangepicker. Default (0) is unlimited days.
@Input() maxRange: number = 0;

// Whether or not the datepicker is disabled.
Expand All @@ -59,6 +59,9 @@ export class NgdsCalendarManager implements OnInit {
// Whether or not to allow rangepickers to have disabled dates within the ranges they select.
@Input() allowDisabledInRange: boolean = false;

// Whether or not to show just one calendar in the rangepicker
@Input() hideSecondCalendar: boolean = false;

// Emits when a selected date is changed.
@Output() dateChange = new EventEmitter;

Expand All @@ -70,7 +73,7 @@ export class NgdsCalendarManager implements OnInit {

protected startCalendar = new BehaviorSubject<CalendarConfig>({ index: 1 });
protected endCalendar = new BehaviorSubject<CalendarConfig>({ index: 2 });
protected displayDepth = new BehaviorSubject<number>(0); // date, 1 = month, 2 = year
protected displayDepth = new BehaviorSubject<number>(0); // date, 1 = month, 2 = year

ngOnInit() {
// set the current display depth to the minimum allowable amount.
Expand Down Expand Up @@ -111,7 +114,7 @@ export class NgdsCalendarManager implements OnInit {
}

/**
* Changes the displayed calendar by the set number of units.
* Changes the displayed calendar by the set number of units.
* If the display depth is 0, the months are changed by the number of units.
* If the display depth is 1, the years are changed by the number of units.
* If the display depth is 2, the years are changed by 12x the number of units.
Expand Down Expand Up @@ -276,7 +279,7 @@ export class NgdsCalendarManager implements OnInit {
}

/**
* Changes the display depth of the calendar.
* Changes the display depth of the calendar.
* @param index the display depth to enable
*/
toggleDepth(index) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@
(clearDates)="handleClearDates()" (displayChange)="handleDisplayChange()" [timezone]="timezone"
[disabledDatesFn]="disabledDatesFn.bind(this)" [dateRange]="dateRange" [minDisplayDepth]="minMode"
[maxRange]="maxRange" [selectedDate]="selectedDate.value" [selectedEndDate]="selectedEndDate.value"
[allowDisabledInRange]="allowDisabledInRange">
[allowDisabledInRange]="allowDisabledInRange" [hideSecondCalendar]="hideSecondCalendar">
</ngds-calendar-manager>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { NgdsInput } from '../ngds-input.component';
import { DateTime } from 'luxon';
import { DateTime, Duration } from 'luxon';
import { BehaviorSubject } from 'rxjs';
import { ValidationErrors, ValidatorFn } from '@angular/forms';

Expand Down Expand Up @@ -42,7 +42,7 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {
// Whether or not to display the datepicker/rangepicker inline.
@Input() inline: boolean = false;

// Whether or not to allow range selections to include disabled dates.
// Whether or not to allow range selections to include disabled dates.
@Input() allowDisabledInRange: boolean = false;

// Hide the datepicker once a date is picked
Expand All @@ -53,6 +53,12 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {
// If the function returns false, the date associated with the supplied DateTime will be available to select.
@Input() customDisabledDatesCallback: (date: DateTime) => boolean;

// Whether or not to show just one calendar in the rangepicker
@Input() hideSecondCalendar: boolean = false;

// Fixed range to select when picking date. When a start date is selected, the endDate will automatically be set.
@Input() fixedRangeSize: Duration;

// Emits when the display changes.
@Output() displayChange = new EventEmitter;

Expand Down Expand Up @@ -178,19 +184,18 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {
newDate = this.selectedDate.value;
this.selectedDate.next(e);
}
if (this.dateFormat) {
this.control.setValue([this.convertDateTimeToFormat(this.selectedDate.value), this.convertDateTimeToFormat(newDate)]);
} else {
this.control.setValue([this.selectedDate.value, newDate]);
}
if (this.hideOnSelect) {
this.hideCalendar();
}
this.setEndDate(newDate);
} else {
// clear the dates and restart
this.currentDisplay = `${this.convertDateTimeToFormat(e, this.dateDisplayFormat)} ${this.rangeSeparator} ...`;
this.selectedDate.next(e);
this.selectedEndDate.next(null);
if (this.fixedRangeSize) {
// automatically set end date if fixedrangesize
let end = e.plus(this.fixedRangeSize);
this.setEndDate(end);
} else {
this.selectedEndDate.next(null);
}
}
} else {
if (this.dateFormat) {
Expand All @@ -204,6 +209,17 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {
}
}

setEndDate(dateTime) {
if (this.dateFormat) {
this.control.setValue([this.convertDateTimeToFormat(this.selectedDate.value), this.convertDateTimeToFormat(dateTime)]);
} else {
this.control.setValue([this.selectedDate.value, dateTime]);
}
if (this.hideOnSelect) {
this.hideCalendar();
}
}

/**
* Clears any currently selected dates from the calendar.
*/
Expand Down Expand Up @@ -296,7 +312,7 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {

/**
* Converts a control value into a display value if dateFormat and dateDisplayFormat are different.
* @param value string
* @param value string
* @returns string formatted for display
*/
convertValueToFormat(value) {
Expand All @@ -309,7 +325,7 @@ export class NgdsDateInput extends NgdsInput implements AfterViewInit {

/**
* When the dropdown closes, match the display to the actual control value.
* We can trigger this reset by just setting the control value to itself.
* We can trigger this reset by just setting the control value to itself.
*/
onCalendarHide() {
this.control.markAsTouched();
Expand Down
33 changes: 29 additions & 4 deletions src/app/forms/datepicker/datepickers/datepicker-snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ export const snippets = {
this.form.controls[control].enable();
}
}
disabledSwitch() {
this.isDisabled = !this.isDisabled;
}
loadingSwitch() {
this.isLoading = !this.isLoading;
}
Expand All @@ -119,7 +119,7 @@ export const snippets = {
html: `
<ngds-date-input
[control]="form?.controls?.['minDatePicker']"
[label]="'MinDate'"
[label]="'MinDate'"
[resetButton]="true"
[minDate]="getToday()">
</ngds-date-input>`,
Expand Down Expand Up @@ -149,7 +149,7 @@ export const snippets = {
html: `
<ngds-date-input
[control]="form?.controls?.['maxDatepicker']"
[label]="'MaxDate'"
[label]="'MaxDate'"
[resetButton]="true"
[maxDate]="getToday()">
</ngds-date-input>`,
Expand Down Expand Up @@ -472,6 +472,31 @@ export const snippets = {
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup} from '@angular/forms';
@Component({
selector: 'minmode-year-datepicker'
export class MinModeYearDatepicker implements OnInit {
public form;
ngOnInit(): void {
this.form = new UntypedFormGroup({
minModeYear: new UntypedFormControl(null),
})
}
}
})`
},
hideSecondCalendar: {
html: `
<ngds-date-input
[control]="form?.controls?.['minModeYear']"
[resetButton]="true"
[hideSecondCalendar]="true"
>
</ngds-date-input>`,
ts: `
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup} from '@angular/forms';
@Component({
selector: 'minmode-year-datepicker'
export class MinModeYearDatepicker implements OnInit {
Expand Down
56 changes: 53 additions & 3 deletions src/app/forms/datepicker/rangepickers/rangepicker-snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const snippets = {
disabledSwitch() {
this.isDisabled = !this.isDisabled;
}
loadingSwitch() {
this.isLoading = !this.isLoading;
}
Expand Down Expand Up @@ -170,7 +170,7 @@ export const snippets = {
}
customDisabledDatesCallback(date: DateTime): boolean {
// Disable the 15th day of each month.
// Disable the 15th day of each month.
if (date.day === 15) {
return true;
}
Expand Down Expand Up @@ -205,7 +205,7 @@ export const snippets = {
}
customDisabledDatesCallback(date: DateTime): boolean {
// Disable the 15th day of each month.
// Disable the 15th day of each month.
if (date.day === 15) {
return true;
}
Expand Down Expand Up @@ -472,4 +472,54 @@ export const snippets = {
}
})`
},
hideSecondCalendar: {
html: `
<ngds-date-input
[control]="form?.controls?.['minModeYear']"
[resetButton]="true"
[hideSecondCalendar]="true">
</ngds-date-input>`,
ts: `
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup} from '@angular/forms';
@Component({
selector: 'hide-second-calendar-rangepicker'
export class HideSecondCalendar implements OnInit {
public form;
ngOnInit(): void {
this.form = new UntypedFormGroup({
hideSecondCalendar: new UntypedFormControl(null),
})
}
}
})`
},
fixedRangeSize: {
html: `
<ngds-date-input
[control]="form?.controls?.['minModeYear']"
[resetButton]="true"
[fixedRangeSize]="duration">
</ngds-date-input>`,
ts: `
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup} from '@angular/forms';
import { Duration } from 'luxon';
@Component({
selector: 'fixed-range-rangepicker'
export class FixedRangepicker implements OnInit {
public form;
public duration = Duration.fromObject({days: 6})
ngOnInit(): void {
this.form = new UntypedFormGroup({
fixedRangeSize: new UntypedFormControl(null),
})
}
}
})`
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ <h3>Custom disabled dates</h3>
<section #section id="invalidRangepicker" class="mb-5 mt-3">
<h3>Invalid rangepicker</h3>
<p>
If you need to allow the user to select dates but reflect the validity of that date, leveraging control validation works similarly to other input types. In the example below, the selected range is invalid if it includes the 15th of any month.
If you need to allow the user to select dates but reflect the validity of that date, leveraging control validation works similarly to other input types. In the example below, the selected range is invalid if it includes the 15th of any month.
</p>
<demonstrator [htmlFile]="snippets?.invalidRangepicker?.html" [tsFile]="snippets?.invalidRangepicker?.ts"
[control]="form?.controls?.['invalidRangepicker']">
Expand Down Expand Up @@ -218,4 +218,32 @@ <h3>Minimum display mode</h3>
[dateDisplayFormat]="'yyyy'" [label]="'Year (minMode 2)'" [dateRange]="true">
</ngds-date-input>
</demonstrator>
</section>

<section #section id="hideSecondCalendar" class="mb-5 mt-3">
<h3>Show only 1 calendar</h3>
<p>
To hide the second rangepicker calendar, set <code>[hideSecondCalendar]="true"</code>.
</p>
<demonstrator [htmlFile]="snippets?.hideSecondCalendar?.html" [tsFile]="snippets?.hideSecondCalendar?.ts"
[control]="form?.controls?.['hideSecondCalendar']">
<ngds-date-input [control]="form?.controls?.['hideSecondCalendar']" [dateRange]="true" [resetButton]="true" [hideSecondCalendar]="true">
</ngds-date-input>
</demonstrator>
</section>

<section #section id="fixedRangeSize" class="mb-5 mt-3">
<h3>Autoselect range by start date</h3>
<p>
You can enforce an exact range to be selected with<code>[fixedRangeSize]="duration"</code>, where <code>duration</code> is a <code>luxon</code> Duration. When a date selection is made, the date is assumed to be the start of the range and the end date is automatically picked based on the provided Duration. Note that the Duration is added to the selected date down to the millisecond, so a Duration of 7 days will likely include 8 days (7 plus the start date) and will show as such in the calendar display.
</p>
<p>The provided example automatically selects a 7 day range starting with the date selected.</p>
<p>
See the <code>luxon</code>&nbsp;<a href="https://moment.github.io/luxon/api-docs/index.html#duration">Duration</a> documetation for more information.
</p>
<demonstrator [htmlFile]="snippets?.fixedRangeSize?.html" [tsFile]="snippets?.fixedRangeSize?.ts"
[control]="form?.controls?.['fixedRangeSize']">
<ngds-date-input [control]="form?.controls?.['fixedRangeSize']" [dateRange]="true" [resetButton]="true" [fixedRangeSize]="duration">
</ngds-date-input>
</demonstrator>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Component, TemplateRef, ViewChildren } from '@angular/core';
import { snippets } from './rangepicker-snippets';
import { SidebarService } from 'src/app/home/sidebar/sidebar.service';
import { AbstractControl, UntypedFormControl, UntypedFormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
import { DateTime } from 'luxon';
import { DateTime, Duration } from 'luxon';
import { BehaviorSubject } from 'rxjs';

@Component({
Expand All @@ -18,6 +18,7 @@ export class RangepickersComponent {
public isDisabled = false;
public isLoading = false;
public now = new BehaviorSubject(null);
public duration = Duration.fromObject({days: 6})

@ViewChildren('section') entries: TemplateRef<any>;

Expand All @@ -43,6 +44,8 @@ export class RangepickersComponent {
timezoneKiritimati: new UntypedFormControl(null),
minModeMonth: new UntypedFormControl(null),
minModeYear: new UntypedFormControl(null),
hideSecondCalendar: new UntypedFormControl(null),
fixedRangeSize: new UntypedFormControl(null),
})

setInterval(()=>{
Expand Down Expand Up @@ -76,7 +79,7 @@ export class RangepickersComponent {
}

customDisabledDatesCallback(date: DateTime): boolean {
// Disable the 15th day of each month.
// Disable the 15th day of each month.
if (date.day === 15) {
return true;
}
Expand Down

0 comments on commit 2884566

Please sign in to comment.