valor-software/ng2-bootstrap

View on GitHub
src/datepicker/engine/flag-days-calendar.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import {
  DaysCalendarViewModel,
  DayViewModel,
  WeekViewModel
} from '../models/index';
import { isSameDay, isSameMonth } from '../../chronos/utils/date-getters';
import { isAfter, isBefore } from '../../chronos/utils/date-compare';
import { isMonthDisabled } from '../utils/bs-calendar-utils';
import { shiftDate } from '../../chronos/utils/date-setters';
import { endOf, startOf } from '../../chronos/utils/start-end-of';

export interface FlagDaysCalendarOptions {
  isDisabled: boolean;
  minDate: Date;
  maxDate: Date;
  hoveredDate: Date;
  selectedDate: Date;
  selectedRange: Date[];
  displayMonths: number;
  monthIndex: number;
}

export function flagDaysCalendar(
  formattedMonth: DaysCalendarViewModel,
  options: FlagDaysCalendarOptions
): DaysCalendarViewModel {
  formattedMonth.weeks.forEach((week: WeekViewModel, weekIndex: number) => {
    week.days.forEach((day: DayViewModel, dayIndex: number) => {
      // datepicker
      const isOtherMonth = !isSameMonth(day.date, formattedMonth.month);

      const isHovered =
        !isOtherMonth && isSameDay(day.date, options.hoveredDate);
      // date range picker
      const isSelectionStart =
        !isOtherMonth &&
        options.selectedRange &&
        isSameDay(day.date, options.selectedRange[0]);
      const isSelectionEnd =
        !isOtherMonth &&
        options.selectedRange &&
        isSameDay(day.date, options.selectedRange[1]);

      const isSelected =
        (!isOtherMonth && isSameDay(day.date, options.selectedDate)) ||
        isSelectionStart ||
        isSelectionEnd;

      const isInRange =
        !isOtherMonth &&
        options.selectedRange &&
        isDateInRange(day.date, options.selectedRange, options.hoveredDate);

      const isDisabled =
        options.isDisabled ||
        isBefore(day.date, options.minDate, 'day') ||
        isAfter(day.date, options.maxDate, 'day');

      // decide update or not
      const newDay = Object.assign({}, day, {
        isOtherMonth,
        isHovered,
        isSelected,
        isSelectionStart,
        isSelectionEnd,
        isInRange,
        isDisabled
      });

      if (
        day.isOtherMonth !== newDay.isOtherMonth ||
        day.isHovered !== newDay.isHovered ||
        day.isSelected !== newDay.isSelected ||
        day.isSelectionStart !== newDay.isSelectionStart ||
        day.isSelectionEnd !== newDay.isSelectionEnd ||
        day.isDisabled !== newDay.isDisabled ||
        day.isInRange !== newDay.isInRange
      ) {
        week.days[dayIndex] = newDay;
      }
    });
  });

  // todo: add check for linked calendars
  formattedMonth.hideLeftArrow =
    options.isDisabled ||
    (options.monthIndex > 0 && options.monthIndex !== options.displayMonths);
  formattedMonth.hideRightArrow =
    options.isDisabled ||
    (options.monthIndex < options.displayMonths &&
      options.monthIndex + 1 !== options.displayMonths);

  formattedMonth.disableLeftArrow = isMonthDisabled(
    shiftDate(formattedMonth.month, { month: -1 }),
    options.minDate,
    options.maxDate
  );
  formattedMonth.disableRightArrow = isMonthDisabled(
    shiftDate(formattedMonth.month, { month: 1 }),
    options.minDate,
    options.maxDate
  );

  return formattedMonth;
}

function isDateInRange(
  date: Date,
  selectedRange: Date[],
  hoveredDate: Date
): boolean {
  if (!date || !selectedRange[0]) {
    return false;
  }

  if (selectedRange[1]) {
    return date > selectedRange[0] && date <= selectedRange[1];
  }

  if (hoveredDate) {
    return date > selectedRange[0] && date <= hoveredDate;
  }

  return false;
}