Last active
August 15, 2022 16:53
-
-
Save thatcatcancode/0c1222d01159033c15a550997dbc97cb to your computer and use it in GitHub Desktop.
Code for customizing ngb bootstrap date picker to indicate certain days are "special"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//HTML | |
<!-- (navigate)="handleNavigate($event)" --> | |
<!-- <span class="position-absolute" [class.dot]="specialDates && markSpecial(date)"></span> --> | |
/** | |
* This is less performant, but actually responds to special dates updating. | |
* A pipe did not do the trick, used for formatting data not setting classes | |
* https://github.com/ng-bootstrap/ng-bootstrap/issues/2391 | |
*/ | |
public markSpecial(date: NgbDateStruct): boolean { | |
// We should either require input of NgbDateStruct[] or do this type conversion | |
// in the setter to avoid running that logic all the time here. | |
const formatSpecialDates = this.specialDates?.map((d: Date) => { | |
// I think we can use the NgbDateNativeAdapter for this conversion | |
// see @link https://ng-bootstrap.github.io/#/components/datepicker/api#NgbDateNativeAdapter | |
return convertDateToDateStruct(d) | |
}) | |
return checkDatesHaveEvents(formatSpecialDates, date) | |
} | |
/** | |
* Does not fire with change detection! | |
* More performant way of setting the special dot class | |
* Used ngb-bootstrap's Day Template Context | |
* see @link https://ng-bootstrap.github.io/#/components/datepicker/api#DayTemplateContext | |
* Example of the difference in performance (day template data vs template function) | |
* see @link https://stackblitz.com/edit/angular-apujun?file=src%2Fapp%2Fdatepicker-customday.html | |
* @param date Each visible date on the calendar. | |
* @returns The css class to be set on the data variable. | |
*/ | |
public data = (date: NgbDate) => { | |
// TODO Should move this formatting to the setter | |
const formatSpecialDates = this.specialDates?.map((d: Date) => { | |
// I think we can use the NgbDateNativeAdapter for this conversion | |
// see @link https://ng-bootstrap.github.io/#/components/datepicker/api#NgbDateNativeAdapter | |
return convertDateToDateStruct(d) | |
}) | |
return checkDatesHaveEvents(formatSpecialDates, date) ? 'dot' : null | |
} | |
/** | |
* Needed for date picker possibly to emit up when month has changed, | |
* to make a new API call to mark special dates (dots) | |
*/ | |
@Output() | |
monthChanged = new EventEmitter<{ month: number; year: number }>() | |
/** | |
* Dates that will be indicated with a dot | |
* One use case of a "special day" is a date that has flosports events. | |
* There could be other reasons to mark a date as "special". | |
* We leave it up to the parent to determine which dates are special | |
* and keep this component dumb as to why to indicate speciality. | |
*/ | |
private _specialDates: ReadonlyArray<Date> | |
@Input() | |
get specialDates(): ReadonlyArray<Date> { | |
return this._specialDates | |
} | |
set specialDates(val: ReadonlyArray<Date>) { | |
// Convert to NgbDateStruct here? | |
this._specialDates = val | |
} | |
/** | |
* An event emitted right before the navigation happens and the month displayed by the datepicker changes. | |
* This allows for the opportunity of an API call to update special and disabled date inputs | |
* Whenever months/years are paged | |
*/ | |
public handleNavigate(event: NgbDatepickerNavigateEvent) { | |
this.monthChanged.emit({ year: event.next.year, month: event.next.month }) | |
// The calendar does not update with the below code. | |
// We want to page to a new month and make an API call to more special dates | |
// Then update the calendar view to indicate special days. | |
// setTimeout(() => { | |
// //For example, if you page from April to May, you should see a few days in May with dots | |
// if (event.next.year === 2022 && event.next.month === 5) { | |
// this.specialDates = [new Date('May 2, 2022'), new Date('May 15, 2022')] | |
// } | |
// console.log('here - timeout over') | |
// }, 1) | |
} | |
// TODO rename this to be more generic | |
export function checkDatesHaveEvents( | |
hasEvents: ReadonlyArray<NgbDateStruct> | undefined, | |
calendarDate: NgbDateStruct | |
): boolean { | |
const index = hasEvents?.findIndex(obj => { | |
return obj.day === calendarDate.day && obj.month === calendarDate.month && obj.year === calendarDate.year | |
}) | |
return index !== -1 | |
} | |
// Story | |
export const DaysWithEvents = (args: DatePickerArgs) => ({ | |
component: DatePickerComponent, | |
moduleMetadata, | |
props: args | |
}) | |
const specialDates = [new Date('April 1, 2022'), new Date('April 17, 2022')] | |
DaysWithEvents.args = { | |
minDate: { year: 1901, month: 1, day: 1 }, | |
maxDate: { year: 2025, month: 12, day: 31 }, | |
startDate: today, | |
specialDates, | |
showCalendarToggle: false | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment