UNPKG

react-calendar

Version:

Ultimate calendar for your React app.

155 lines (116 loc) 3.77 kB
import { getRange } from './dates.js'; import type { Range, RangeType, Value } from './types.js'; /** * Returns a value no smaller than min and no larger than max. * * @param {Date} value Value to return. * @param {Date} min Minimum return value. * @param {Date} max Maximum return value. * @returns {Date} Value between min and max. */ export function between<T extends Date>(value: T, min?: T | null, max?: T | null): T { if (min && min > value) { return min; } if (max && max < value) { return max; } return value; } export function isValueWithinRange(value: Date, range: Range<Date>): boolean { return range[0] <= value && range[1] >= value; } export function isRangeWithinRange(greaterRange: Range<Date>, smallerRange: Range<Date>): boolean { return greaterRange[0] <= smallerRange[0] && greaterRange[1] >= smallerRange[1]; } export function doRangesOverlap(range1: Range<Date>, range2: Range<Date>): boolean { return isValueWithinRange(range1[0], range2) || isValueWithinRange(range1[1], range2); } function getRangeClassNames( valueRange: Range<Date>, dateRange: Range<Date>, baseClassName: string, ): string[] { const isRange = doRangesOverlap(dateRange, valueRange); const classes = []; if (isRange) { classes.push(baseClassName); const isRangeStart = isValueWithinRange(valueRange[0], dateRange); const isRangeEnd = isValueWithinRange(valueRange[1], dateRange); if (isRangeStart) { classes.push(`${baseClassName}Start`); } if (isRangeEnd) { classes.push(`${baseClassName}End`); } if (isRangeStart && isRangeEnd) { classes.push(`${baseClassName}BothEnds`); } } return classes; } function isCompleteValue<T>(value: T | null | Range<T | null>): value is T | Range<T> { if (Array.isArray(value)) { return value[0] !== null && value[1] !== null; } return value !== null; } export function getTileClasses(args: { date?: Date | Range<Date>; dateType?: RangeType; hover?: Date | null; value?: Value; valueType?: RangeType; }): string[] { if (!args) { throw new Error('args is required'); } const { value, date, hover } = args; const className = 'react-calendar__tile'; const classes = [className]; if (!date) { return classes; } const now = new Date(); const dateRange = (() => { if (Array.isArray(date)) { return date; } const { dateType } = args; if (!dateType) { throw new Error('dateType is required when date is not an array of two dates'); } return getRange(dateType, date); })(); if (isValueWithinRange(now, dateRange)) { classes.push(`${className}--now`); } if (!value || !isCompleteValue(value)) { return classes; } const valueRange = (() => { if (Array.isArray(value)) { return value; } const { valueType } = args; if (!valueType) { throw new Error('valueType is required when value is not an array of two dates'); } return getRange(valueType, value); })(); if (isRangeWithinRange(valueRange, dateRange)) { classes.push(`${className}--active`); } else if (doRangesOverlap(valueRange, dateRange)) { classes.push(`${className}--hasActive`); } const valueRangeClassNames = getRangeClassNames(valueRange, dateRange, `${className}--range`); classes.push(...valueRangeClassNames); const valueArray = Array.isArray(value) ? value : [value]; if (hover && valueArray.length === 1) { const hoverRange: Range<Date> = hover > valueRange[0] ? [valueRange[0], hover] : [hover, valueRange[0]]; const hoverRangeClassNames = getRangeClassNames(hoverRange, dateRange, `${className}--hover`); classes.push(...hoverRangeClassNames); } return classes; }