123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- import {
- format,
- isSameMonth,
- isWeekend,
- differenceInCalendarDays,
- isBefore,
- addDays,
- startOfWeek,
- endOfWeek,
- getSeconds,
- differenceInHours,
- getMinutes,
- getHours,
- addHours,
- isSameDay,
- endOfDay,
- startOfDay,
- toDate,
- Locale,
- } from 'date-fns';
- import { EventObject, ParsedRangeEvent } from './foundation';
- const copyEvent = (event: EventObject, date: Date, start?: Date, end?: Date, allDay = false) => {
- const copied = { ...event };
- copied.date = date;
- start ? copied.start = start : null;
- end ? copied.end = end : null;
- copied.allDay = allDay;
- return copied;
- };
- const isDateInRange = (dirtyDate: Date, dirtyStart: Date, dirtyEnd: Date) => {
- const date = toDate(dirtyDate);
- const start = toDate(dirtyStart);
- const end = toDate(dirtyEnd);
- return date.getTime() < end.getTime() && date.getTime() >= start.getTime();
- };
- export const sortDate = (a: Date | string, b: Date | string) => {
- const res = isBefore(new Date(a), new Date(b)) ? -1 : 1;
- return res;
- };
- export const checkWeekend = (val: Date) => isWeekend(val);
- export const getCurrDate = () => new Date();
- export const round = (value: number) => Math.round(value * 1000) / 1000;
- export const getPos = (value: Date | number) => {
- const currSec = (getHours(value) * 60 + getMinutes(value)) * 60 + getSeconds(value);
- const totalSec = 24 * 60 * 60;
- return currSec / totalSec;
- };
- export const isAllDayEvent = (event: EventObject) => 'allDay' in event && event.allDay;
- /**
- *
- * @param {object} event
- * normalize event object:
- * if event object does not have start time, add start time = end time - 1h; if not same day, then startday of the end
- * if event object does not have end time, add end time = start time + 1h; if not same day, then endday of the start
- */
- export const amendEvent = (event: EventObject) => {
- const { start, end } = event;
- if (!start && !end) {
- return undefined;
- } else if (!start) {
- event.start = isSameDay(end, addHours(end, -1)) ? addHours(end, -1) : startOfDay(end);
- } else {
- event.end = isSameDay(start, addHours(start, 1)) ? addHours(start, 1) : endOfDay(start);
- }
- return event;
- };
- /**
- *
- * @param {arr} events
- * find the max topInd and used as row height
- */
- export const calcRowHeight = (events: ParsedRangeEvent[]) => {
- const topIndArr = events.map(item => item.topInd);
- return topIndArr.length ? Math.max(...topIndArr) + 1 : 1;
- };
- export interface DateObj {
- ind: number;
- date: Date;
- dayString: string;
- weekday: string;
- isToday: boolean;
- isWeekend: boolean;
- isSameMonth: boolean;
- month: string;
- }
- export type weeekStartsOnEnum = 0 | 1 | 2 | 3 | 4 | 5 | 6;
- export const calcRangeData = (value: Date, start: Date, rangeLen: number, mode: string, locale: Locale) => {
- const today = getCurrDate();
- const arr: Array<DateObj> = [];
- [...Array(rangeLen).keys()].map(ind => {
- const dateObj = {} as DateObj;
- const date = addDays(start, ind);
- dateObj.ind = ind;
- dateObj.date = date;
- dateObj.dayString = format(date, 'd', { locale });
- dateObj.weekday = format(date, 'EEE', { locale });
- dateObj.isToday = isSameDay(date, today);
- dateObj.isWeekend = checkWeekend(date);
- if (mode === 'month') {
- dateObj.isSameMonth = isSameMonth(value, date);
- dateObj.month = format(date, 'LLL', { locale });
- }
- arr.push(dateObj);
- });
- return arr;
- };
- /**
- *
- * @param {value} date
- * @param {string} mode
- * @param {string} locale
- * @returns {object[]} { date: Date, dayString: string, ind: number, isToday: boolean, isWeekend: boolean, weekday: string }
- * create weekly object array
- */
- export const calcWeekData = (value: Date, mode = 'week', locale: Locale, weekStartsOn: weeekStartsOnEnum) => {
- const start = startOfWeek(value, { weekStartsOn });
- return calcRangeData(value, start, 7, mode, locale);
- };
- /**
- *
- * @param {object} event
- * @param {boolean} allDay
- * @returns {object[]} { allDay: boolean, data: Date, start: Date, end: Date, children: ReactNode }
- * parsed a spanned all-day event into multiple dates
- */
- export const parseAllDayEvent = (event: EventObject, allDay = true, currDate: Date = undefined) => {
- const res = [];
- const { start, end } = event;
- if (start && end) {
- const diff = differenceInCalendarDays(end, start);
- [...Array(diff + 1).keys()].map(day => {
- res.push(copyEvent(event, addDays(start, day), null, null, allDay));
- });
- } else {
- const date = start || end || currDate;
- res.push(copyEvent(event, startOfDay(date), null, null, allDay));
- }
- return res;
- };
- /**
- *
- * @param {object} event
- * @returns {object[]} { allDay: boolean, data: Date, start: Date, end: Date, children: ReactNode }
- * parsed events
- */
- export const parseEvent = (event: EventObject) => {
- const { start, end } = event;
- let res: EventObject[] = [];
- if (isAllDayEvent(event)) {
- return parseAllDayEvent(event);
- }
- if (start && end) {
- if (!isBefore(start, end)) {
- [event.start, event.end] = [event.end, event.start];
- }
- if (isSameDay(start, end)) {
- res.push(copyEvent(event, startOfDay(start)));
- } else if (Math.abs(differenceInHours(start, end)) < 24) {
- res.push(copyEvent(event, startOfDay(start), null, endOfDay(start)));
- res.push(copyEvent(event, startOfDay(end), startOfDay(end)));
- } else {
- res = res.concat(parseAllDayEvent(event));
- }
- } else {
- const amend = amendEvent(event);
- res.push(copyEvent(amend, startOfDay(amend.start)));
- }
- return res;
- };
- /**
- *
- * @param {arr} arr
- * @param {key}
- * @param {function} func callback function
- * @returns {map}
- * convert events array to may, use datestring as key
- */
- export const convertEventsArrToMap = (
- arr: EventObject[],
- key: 'start' | 'date',
- func: (val: Date) => Date,
- displayValue?: Date
- ) => {
- const res = new Map();
- arr.forEach(item => {
- let val;
- if (key in item) {
- val = item[key];
- } else {
- val = startOfDay(displayValue);
- }
- const k = func ? func(val).toString() : val.toString();
- if (res.has(k)) {
- res.get(k).push(item);
- } else {
- res.set(k, [item]);
- }
- });
- return res;
- };
- /**
- * @returns {arr}
- * filter out event that is not in the date range
- */
- export const filterEvents = (events: Map<string, EventObject[]>, start: Date, end: Date) => {
- const res = new Map<string, EventObject[]>();
- [...events.keys()].map(day => {
- const item = events.get(day);
- const date = new Date(day);
- if (isDateInRange(date, start, end)) {
- res.set(day, item);
- } else if (isBefore(end, date)) {
- // do nothing
- } else {
- const filtered = item.filter(i => !i.end || !isBefore(i.end, start));
- const key = start.toString();
- if (res.has(key)) {
- res.set(key, [...res.get(key), ...filtered]);
- } else {
- res.set(key, item);
- }
- }
- });
- return res;
- };
- /**
- * @returns {arr}
- * filter out event that is not in the week range
- */
- // eslint-disable-next-line max-len
- export const filterWeeklyEvents = (events: Map<string, EventObject[]>, weekStart: Date) => filterEvents(events, weekStart, addDays(endOfWeek(weekStart), 1));
- /**
- * @returns {arr}
- * arrange and sort all day event for a range
- */
- export const parseRangeAllDayEvent = (
- event: EventObject[],
- startDate: Date,
- rangeStart: Date,
- rangeEnd: Date,
- parsed: Array<Array<ParsedRangeEvent>>
- ) => {
- const dateRangeLen = differenceInCalendarDays(rangeEnd, rangeStart);
- event.sort((a, b) => sortDate(a.start, b.start)).forEach(item => {
- const itemInfo = { ...item };
- const { end } = item;
- let dateLength;
- const j = differenceInCalendarDays(startDate, rangeStart);
- let i = 0;
- while (Boolean(parsed[i]) && Boolean(parsed[i][j])) {
- i++;
- }
- if (!end) {
- dateLength = 0;
- } else {
- dateLength = isDateInRange(end, rangeStart, rangeEnd) ?
- differenceInCalendarDays(end, startDate) :
- differenceInCalendarDays(rangeEnd, startDate);
- }
- itemInfo.leftPos = round(Number(j) / dateRangeLen);
- itemInfo.width = Math.min(1 - round(Number(j) / dateRangeLen), round((dateLength + 1) * 1 / dateRangeLen));
- itemInfo.topInd = i;
- [...Array(dateLength + 1).keys()].forEach(dist => {
- if (!parsed[i]) {
- parsed[i] = [];
- }
- if (dist > 0) {
- parsed[i][j + dist] = item;
- } else {
- parsed[i][j + dist] = itemInfo;
- }
- });
- });
- return parsed;
- };
- /**
- * @returns {arr}
- * arrange and sort weekly all day event
- */
- export const parseWeeklyAllDayEvent = (
- event: EventObject[],
- startDate: Date,
- weekStart: Date,
- parsed: Array<Array<ParsedRangeEvent>>
- ) => parseRangeAllDayEvent(event, startDate, weekStart, addDays(endOfWeek(startDate), 1), parsed);
- export const collectDailyEvents = (events: ParsedRangeEvent[][]) => {
- const collections = {} as ParsedRangeEvent[][];
- events.forEach((row, rowInd) => {
- row.forEach((event, ind) => {
- if (collections[ind]) {
- collections[ind][rowInd] = event;
- } else {
- collections[ind] = [];
- collections[ind][rowInd] = event;
- }
- });
- });
- return collections;
- };
- export const renderDailyEvent = (event: EventObject) => {
- // eslint-disable-next-line prefer-const
- let { start, end, allDay, children } = event;
- let startPos,
- endPos;
- if (isAllDayEvent(event)) {
- startPos = 0;
- endPos = 0;
- } else if (!start || !end) {
- const amend = amendEvent(event);
- endPos = getPos(amend.end);
- startPos = getPos(amend.start);
- } else {
- if (!isBefore(start, end)) {
- [start, end] = [end, start];
- }
- startPos = getPos(start);
- endPos = getPos(end);
- }
- const parsed = {
- startPos: round(startPos),
- endPos: round(endPos),
- children,
- allDay: Boolean(allDay),
- };
- return parsed;
- };
|