Skip to content

Instantly share code, notes, and snippets.

@thatcatcancode
Last active August 15, 2022 16:53
Show Gist options
  • Save thatcatcancode/0c1222d01159033c15a550997dbc97cb to your computer and use it in GitHub Desktop.
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"
//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