Skip to content

Commit

Permalink
feat: added dateScheduleRangeField() (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
dereekb authored Dec 7, 2022
1 parent 994c6b1 commit 1979f3b
Show file tree
Hide file tree
Showing 60 changed files with 1,955 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import { DbxCalendarScheduleSelectionStore } from '@dereekb/dbx-form/calendar';
selector: 'doc-extension-calendar-schedule-example',
template: `
<dbx-schedule-selection-calendar></dbx-schedule-selection-calendar>
<dbx-content-border>
<p>Selection: {{ calendarSelectionValue$ | async | json }}</p>
</dbx-content-border>
<dbx-subsection header="Selector Components">
<dbx-subsection header="dbx-schedule-selection-calendar-date-range" hint="Component used to control and set the date range.">
<dbx-schedule-selection-calendar-date-range></dbx-schedule-selection-calendar-date-range>
<dbx-schedule-selection-calendar-date-range [showCustomize]="true">
<dbx-schedule-selection-calendar-date-dialog-button customizeButton></dbx-schedule-selection-calendar-date-dialog-button>
</dbx-schedule-selection-calendar-date-range>
</dbx-subsection>
<dbx-subsection header="dbx-schedule-selection-calendar-date-days" hint="Component used to pick days in the selection.">
<dbx-schedule-selection-calendar-date-days></dbx-schedule-selection-calendar-date-days>
Expand All @@ -16,4 +21,8 @@ import { DbxCalendarScheduleSelectionStore } from '@dereekb/dbx-form/calendar';
`,
providers: [DbxCalendarScheduleSelectionStore]
})
export class DocExtensionCalendarScheduleSelectionComponent {}
export class DocExtensionCalendarScheduleSelectionComponent {
readonly calendarSelectionValue$ = this.dbxCalendarScheduleSelectionStore.currentSelectionValue$;

constructor(readonly dbxCalendarScheduleSelectionStore: DbxCalendarScheduleSelectionStore) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { addDays, startOfDay } from 'date-fns';
import { Component } from '@angular/core';
import { DbxCalendarScheduleSelectionStore } from '@dereekb/dbx-form/calendar';
import { DateScheduleDateFilterConfig } from '@dereekb/date';

export const DOC_EXTENSION_CALENDAR_SCHEDULE_TEST_FILTER: DateScheduleDateFilterConfig = {
start: startOfDay(new Date()),
end: addDays(new Date(), 14), // two weeks
w: '345', // Tues/Weds/Thurs
ex: []
};

@Component({
selector: 'doc-extension-calendar-schedule-with-filter-example',
template: `
<dbx-schedule-selection-calendar></dbx-schedule-selection-calendar>
<dbx-content-border>
<p>Selection: {{ calendarSelectionValue$ | async | json }}</p>
</dbx-content-border>
`,
providers: [DbxCalendarScheduleSelectionStore]
})
export class DocExtensionCalendarScheduleSelectionWithFilterComponent {
readonly calendarSelectionValue$ = this.dbxCalendarScheduleSelectionStore.currentSelectionValue$;

constructor(readonly dbxCalendarScheduleSelectionStore: DbxCalendarScheduleSelectionStore) {
dbxCalendarScheduleSelectionStore.setFilter(DOC_EXTENSION_CALENDAR_SCHEDULE_TEST_FILTER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
<doc-feature-layout header="Calendar Extension" hint="">
<doc-feature-derived type="uses" from="mattlewis92/angular-calendar" url="https://github.com/mattlewis92/angular-calendar">
<p>Styles are not required, as the dbx-web library already builds custom/modified styling for use.</p>
<p>All style colors are used as css variables, allowing for easy overwriting of values.</p>
</doc-feature-derived>
<!-- Examples -->
<doc-feature-example header="dbx-schedule-selection-calendar" hint="Used select dates to form a DateSchedule or to select days of a DateSchedule. Uses a DbxCalendarScheduleSelectionStore.">
<doc-extension-calendar-schedule-example></doc-extension-calendar-schedule-example>
</doc-feature-example>
<doc-feature-example header="dbx-calendar" hint="Used to render a calendar. Uses a DbxCalendarStore.">
<dbx-calendar (clickEvent)="event = $event"></dbx-calendar>
<p>Picked Date: {{ date$ | async | date }}</p>
<p>Clicked Event: {{ event | json }}</p>
<doc-feature-example dbxMapboxStoreParentBlocker header="dateScheduleRangeField()" hint="A field used for building a schedule.">
<doc-form-example-form [dbxFormlyFields]="dateScheduleRangeFields" [dbxFormSource]="defaultDateScheduleRangeFieldValue$"></doc-form-example-form>
</doc-feature-example>
<doc-feature-example header="dbx-calendar in restricted two-column" hint="Example showing a calendar in a two-column view with a set height.">
<button (click)="showRight = !showRight" mat-raised-button color="accent">Toggle Right</button>
Expand All @@ -25,5 +21,16 @@
</dbx-two-column-right>
</dbx-two-column>
</doc-feature-example>
<doc-feature-example header="dbx-schedule-selection-calendar" hint="Used select dates to form a DateSchedule or to select days of a DateSchedule. Uses a DbxCalendarScheduleSelectionStore.">
<dbx-subsection header="With DateSchedule Filter" hint="A dbx-schedule-selection-calendar can be limited to a specific filter range.">
<doc-extension-calendar-schedule-with-filter-example></doc-extension-calendar-schedule-with-filter-example>
</dbx-subsection>
<doc-extension-calendar-schedule-example></doc-extension-calendar-schedule-example>
</doc-feature-example>
<doc-feature-example header="dbx-calendar" hint="Used to render a calendar. Uses a DbxCalendarStore.">
<dbx-calendar (clickEvent)="event = $event"></dbx-calendar>
<p>Picked Date: {{ date$ | async | date }}</p>
<p>Clicked Event: {{ event | json }}</p>
</doc-feature-example>
</doc-feature-layout>
</dbx-content-container>
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { OnInit, Component } from '@angular/core';
import { DbxCalendarEvent, DbxCalendarStore } from '@dereekb/dbx-web/calendar';
import { DateBlock, DateBlockCollection, dateBlockTiming, durationSpanToDateRange, expandDateBlockCollection } from '@dereekb/date';
import { DateBlock, DateBlockCollection, dateBlockTiming, DateScheduleRange, durationSpanToDateRange, expandDateBlockCollection } from '@dereekb/date';
import { addMonths, setHours } from 'date-fns/esm';
import { Maybe, range } from '@dereekb/util';
import { CalendarEvent } from 'angular-calendar';
import { dateScheduleRangeField } from '@dereekb/dbx-form/calendar';
import { startOfDay, addDays } from 'date-fns';
import { Observable, of } from 'rxjs';
import { DOC_EXTENSION_CALENDAR_SCHEDULE_TEST_FILTER } from '../component/selection.filter.calendar.component';

export interface TestCalendarEventData extends DateBlock {
value: string;
Expand All @@ -18,6 +22,30 @@ export class DocExtensionCalendarComponent implements OnInit {

event: Maybe<DbxCalendarEvent<TestCalendarEventData>>;

readonly defaultDateScheduleRangeFieldValue$ = of({
dateSchedule: {
start: startOfDay(new Date()),
end: addDays(startOfDay(new Date()), 14),
w: '8',
ex: [0, 3, 4, 5]
}
});

readonly dateScheduleRangeFields = [
dateScheduleRangeField({
key: 'dateSchedule',
required: true,
label: 'Custom Label',
description: 'Input field used for picking a DateScheduleRange value.'
}),
dateScheduleRangeField({
key: 'dateScheduleWithFilter',
required: true,
description: 'Date schedule with a filter applied to it',
filter: DOC_EXTENSION_CALENDAR_SCHEDULE_TEST_FILTER
})
];

readonly date$ = this.calendarStore.date$;

constructor(readonly calendarStore: DbxCalendarStore<TestCalendarEventData>) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import { DocExtensionMapboxContentExampleComponent } from './component/mapbox.co
import { DocExtensionMapboxMarkersExampleComponent } from './component/mapbox.markers.example.component';
import { DbxCalendarRootModule } from '@dereekb/dbx-web/calendar';
import { DocExtensionCalendarScheduleSelectionComponent } from './component/selection.calendar.component';
import { DbxFormCalendarModule } from '@dereekb/dbx-form/calendar';
import { DbxFormCalendarModule, DbxFormDateScheduleRangeFieldModule } from '@dereekb/dbx-form/calendar';
import { DocExtensionCalendarScheduleSelectionWithFilterComponent } from './component/selection.filter.calendar.component';

@NgModule({
imports: [
DocSharedModule,
DbxCalendarRootModule,
DbxFormCalendarModule,
DocFormComponentsModule,
DbxFormDateScheduleRangeFieldModule,
DbxWidgetModule,
DbxMapboxModule,
NgxMapboxGLModule,
Expand All @@ -41,6 +43,7 @@ import { DbxFormCalendarModule } from '@dereekb/dbx-form/calendar';
DocExtensionMapboxContentExampleComponent,
DocExtensionMapboxMarkersExampleComponent,
DocExtensionCalendarScheduleSelectionComponent,
DocExtensionCalendarScheduleSelectionWithFilterComponent,
// container
DocExtensionLayoutComponent,
DocExtensionHomeComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DateScheduleDayCode } from './../../../../../../../../../packages/date/src/lib/date/date.schedule';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { Component } from '@angular/core';
import { addressField, addressListField, cityField, countryField, emailField, phoneField, nameField, phoneAndLabelSectionField, wrappedPhoneAndLabelField, repeatArrayField, stateField, textAreaField, textField, zipCodeField, phoneListField, dateTimeField, DbxDateTimeFieldTimeMode, toggleField, checkboxField, numberField, latLngTextField, DbxDateTimeValueMode, dateRangeField, dollarAmountField, DateTimePickerConfiguration } from '@dereekb/dbx-form';
import { addDays, startOfDay } from 'date-fns';
import { addSuffixFunction, randomBoolean } from '@dereekb/util';
import { of } from 'rxjs';
import { DateScheduleDayCode } from '@dereekb/date';

@Component({
templateUrl: './value.component.html'
Expand Down
17 changes: 17 additions & 0 deletions packages/date/src/lib/date/date.block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
dateBlocksInDateBlockRange,
dateBlockTiming,
DateBlockTiming,
dateTimingRelativeIndexFactory,
expandDateBlockRange,
expandUniqueDateBlocksFunction,
getCurrentDateBlockTimingOffset,
Expand Down Expand Up @@ -109,6 +110,22 @@ describe('getCurrentDateBlockTimingStartDate()', () => {
});
});

describe('dateTimingRelativeIndexFactory()', () => {
describe('scenarios', () => {
describe('timezone change', () => {
const start = new Date('2023-03-12T06:00:00.000Z');
const dstDay = addDays(start, 1);

it('should handle daylight savings time changes.', () => {
const factory = dateTimingRelativeIndexFactory({ start });
const result = factory(dstDay);

expect(result).toBe(1);
});
});
});
});

describe('getRelativeIndexForDateTiming()', () => {
const start = startOfDay(new Date());
const startsAt = addHours(start, 12); // Noon on the day
Expand Down
51 changes: 47 additions & 4 deletions packages/date/src/lib/date/date.block.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DayOfWeek, RequiredOnKeys, IndexNumber, IndexRange, indexRangeCheckFunction, IndexRef, MINUTES_IN_DAY, MS_IN_DAY, UniqueModel, lastValue, FactoryWithRequiredInput, FilterFunction, mergeFilterFunctions, range, Milliseconds, Hours, MapFunction, getNextDay, SortCompareFunction, sortAscendingIndexNumberRefFunction, mergeArrayIntoArray, Configurable, ArrayOrValue, asArray, sumOfIntegersBetween, filterMaybeValues, Maybe } from '@dereekb/util';
import { dateRange, DateRange, DateRangeDayDistanceInput, DateRangeStart, DateRangeType, isDateRange, isDateRangeStart } from './date.range';
import { DateDurationSpan } from './date.duration';
import { differenceInDays, differenceInMilliseconds, isBefore, addDays, addMinutes, getSeconds, getMilliseconds, getMinutes, addMilliseconds, hoursToMilliseconds, addHours, differenceInHours, isAfter } from 'date-fns';
import { differenceInDays, differenceInMilliseconds, isBefore, addDays, addMinutes, getSeconds, getMilliseconds, getMinutes, addMilliseconds, hoursToMilliseconds, addHours, differenceInHours, isAfter, millisecondsToHours, minutesToHours } from 'date-fns';
import { isDate, copyHoursAndMinutesFromDate, roundDownToMinute } from './date';
import { Expose, Type } from 'class-transformer';
import { getCurrentSystemOffsetInHours } from './date.timezone';
Expand Down Expand Up @@ -142,12 +142,18 @@ export type DateTimingRelativeIndexFactory<T extends DateBlockTimingStart = Date
*/
export function dateTimingRelativeIndexFactory<T extends DateBlockTimingStart = DateBlockTimingStart>(timing: T): DateTimingRelativeIndexFactory<T> {
const startDate = getCurrentDateBlockTimingStartDate(timing);
const baseOffset = startDate.getTimezoneOffset();

const factory = ((input: DateOrDateBlockIndex) => {
if (typeof input === 'number') {
return input;
} else {
const diff = differenceInHours(input, startDate);
const inputOffset = input.getTimezoneOffset();
const offsetDifferenceHours = minutesToHours(baseOffset - inputOffset); // handle timezone offset changes

const diff = differenceInHours(input, startDate) + offsetDifferenceHours;
const daysOffset = Math.floor(diff / 24);

return daysOffset;
}
}) as Configurable<Partial<DateTimingRelativeIndexFactory>>;
Expand All @@ -161,10 +167,46 @@ export function dateTimingRelativeIndexFactory<T extends DateBlockTimingStart =
* @param timing
* @param date
*/
export function getRelativeIndexForDateTiming(timing: DateBlockTiming, date: DateOrDateBlockIndex = new Date()): DateBlockIndex {
export function getRelativeIndexForDateTiming(timing: DateBlockTimingStart, date: DateOrDateBlockIndex = new Date()): DateBlockIndex {
return dateTimingRelativeIndexFactory(timing)(date);
}

/**
* Returns the Date of the input DateBlockIndex relative to the configured Date. If a date is entered, the date is returned.
*/
export type DateBlockTimingDateFactory<T extends DateBlockTimingStart = DateBlockTimingStart> = ((input: DateOrDateBlockIndex) => Date) & {
readonly _timing: T;
};

/**
* Creates a DateBlockTimingDateFactory.
*
* @param timing
* @returns
*/
export function dateBlockTimingDateFactory<T extends DateBlockTimingStart = DateBlockTimingStart>(timing: T): DateBlockTimingDateFactory<T> {
const startDate = getCurrentDateBlockTimingStartDate(timing);
const factory = ((input: DateOrDateBlockIndex) => {
if (isDate(input)) {
return input;
} else {
return addDays(startDate, input); // TODO: Is this right to use days, or should it use hours to avoid daylight savings?
}
}) as Configurable<Partial<DateBlockTimingDateFactory>>;
factory._timing = timing;
return factory as DateBlockTimingDateFactory<T>;
}

/**
* Returns the date of the input index.
*
* @param timing
* @param date
*/
export function getRelativeDateForDateBlockTiming(timing: DateBlockTimingStart, input: DateOrDateBlockIndex): Date {
return dateBlockTimingDateFactory(timing)(input);
}

/**
* The DateRange input for dateBlockTiming()
*/
Expand Down Expand Up @@ -286,7 +328,8 @@ export type DateBlockDayOfWeekFactory = MapFunction<DateBlockIndex, DayOfWeek>;
* @param dayForIndexZero
* @returns
*/
export function dateBlockDayOfWeekFactory(dayForIndexZero: DayOfWeek): DateBlockDayOfWeekFactory {
export function dateBlockDayOfWeekFactory(inputDayForIndexZero: DayOfWeek | Date): DateBlockDayOfWeekFactory {
const dayForIndexZero = typeof inputDayForIndexZero === 'number' ? inputDayForIndexZero : (inputDayForIndexZero.getUTCDay() as DayOfWeek);
return (index: DateBlockIndex) => getNextDay(dayForIndexZero, index);
}

Expand Down
8 changes: 8 additions & 0 deletions packages/date/src/lib/date/date.format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export function formatToISO8601DayString(date: Date = new Date()): ISO8601DayStr
return format(date, 'yyyy-MM-dd');
}

export function formatToShortDateString(date: Date = new Date()): ISO8601DayString {
return format(date, 'MM/dd/yyyy');
}

export function formatToMonthDayString(date: Date = new Date()): ISO8601DayString {
return format(date, 'MM/dd');
}

export function formatToDateString(date: Date): string {
return format(date, 'EEE, MMM do');
}
Expand Down
Loading

0 comments on commit 1979f3b

Please sign in to comment.