From a85961cc20e0568f5bef7ed13d676d45abe1f7d9 Mon Sep 17 00:00:00 2001 From: MaTeMaTuK Date: Tue, 3 Aug 2021 21:27:44 +0300 Subject: [PATCH] RTL + NEW ROWS --- example/src/App.tsx | 25 ++-- src/components/calendar/calendar.tsx | 27 +++- src/components/gantt/gantt.tsx | 21 ++- src/components/gantt/task-gantt-content.tsx | 2 + src/components/gantt/task-gantt.tsx | 1 + src/components/grid/grid-body.tsx | 19 +++ src/components/other/arrow.tsx | 92 ++++++++++-- src/components/other/horizontal-scroll.tsx | 1 + src/components/other/tooltip.tsx | 54 ++++--- .../other/vertical-scroll.module.css | 1 - src/components/other/vertical-scroll.tsx | 16 ++- src/components/task-item/bar/bar.tsx | 3 +- src/components/task-item/task-item.tsx | 18 ++- src/components/task-list/task-list-table.tsx | 2 +- src/helpers/bar-helper.ts | 136 +++++++++++++----- src/helpers/date-helper.ts | 14 +- 16 files changed, 331 insertions(+), 101 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index a15fd19..e2717d6 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -64,20 +64,17 @@ const App = () => { isChecked={isChecked} />

Gantt With Unlimited Height

-
- -
+

Gantt With Limited Height

= ({ dateSetup, locale, viewMode, + rtl, headerHeight, columnWidth, fontFamily, @@ -30,7 +33,6 @@ export const Calendar: React.FC = ({ const getCalendarValuesForMonth = () => { const topValues: ReactChild[] = []; const bottomValues: ReactChild[] = []; - const topDefaultWidth = columnWidth * 6; const topDefaultHeight = headerHeight * 0.5; for (let i = 0; i < dateSetup.dates.length; i++) { const date = dateSetup.dates[i]; @@ -50,6 +52,12 @@ export const Calendar: React.FC = ({ date.getFullYear() !== dateSetup.dates[i - 1].getFullYear() ) { const topValue = date.getFullYear().toString(); + let xText: number; + if (rtl) { + xText = (6 + i + date.getMonth() + 1) * columnWidth; + } else { + xText = (6 + i - date.getMonth()) * columnWidth; + } topValues.push( = ({ x1Line={columnWidth * i} y1Line={0} y2Line={topDefaultHeight} - xText={ - topDefaultWidth + columnWidth * i - date.getMonth() * columnWidth - } + xText={xText} yText={topDefaultHeight * 0.9} /> ); @@ -88,7 +94,7 @@ export const Calendar: React.FC = ({ {bottomValue} @@ -149,7 +155,12 @@ export const Calendar: React.FC = ({ x1Line={columnWidth * (i + 1)} y1Line={0} y2Line={topDefaultHeight} - xText={columnWidth * (i + 1) - date.getDate() * columnWidth * 0.5} + xText={ + columnWidth * (i + 1) - + getDaysInMonth(date.getMonth(), date.getFullYear()) * + columnWidth * + 0.5 + } yText={topDefaultHeight * 0.9} /> ); @@ -174,7 +185,7 @@ export const Calendar: React.FC = ({ @@ -196,8 +207,10 @@ export const Calendar: React.FC = ({ ); } } + return [topValues, bottomValues]; }; + let topValues: ReactChild[] = []; let bottomValues: ReactChild[] = []; switch (dateSetup.viewMode) { diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index 1236bab..e2eaf06 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -74,19 +74,22 @@ export const Gantt: React.FunctionComponent = ({ const [selectedTask, setSelectedTask] = useState(); const [failedTask, setFailedTask] = useState(null); - const [scrollY, setScrollY] = useState(0); - const [scrollX, setScrollX] = useState(0); - const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false); - const svgWidth = dateSetup.dates.length * columnWidth; const ganttFullHeight = barTasks.length * rowHeight; + const [scrollY, setScrollY] = useState(0); + const [scrollX, setScrollX] = useState(-1); + const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false); + // task change events useEffect(() => { const [startDate, endDate] = ganttDateRange(tasks, viewMode); let newDates = seedDates(startDate, endDate, viewMode); if (rtl) { newDates = newDates.reverse(); + if (scrollX === -1) { + setScrollX(newDates.length * columnWidth); + } } setDateSetup({ dates: newDates, viewMode }); setBarTasks( @@ -130,6 +133,7 @@ export const Gantt: React.FunctionComponent = ({ milestoneBackgroundColor, milestoneBackgroundSelectedColor, rtl, + scrollX, ]); useEffect(() => { @@ -211,7 +215,7 @@ export const Gantt: React.FunctionComponent = ({ } setScrollX(newScrollX); event.preventDefault(); - } else { + } else if (ganttHeight) { let newScrollY = scrollY + event.deltaY; if (newScrollY < 0) { newScrollY = 0; @@ -238,7 +242,7 @@ export const Gantt: React.FunctionComponent = ({ wrapperRef.current.removeEventListener("wheel", handleWheel); } }; - }, [wrapperRef.current, scrollY, scrollX, ganttHeight, svgWidth]); + }, [wrapperRef.current, scrollY, scrollX, ganttHeight, svgWidth, rtl]); const handleScrollY = (event: SyntheticEvent) => { if (scrollY !== event.currentTarget.scrollTop && !ignoreScrollEvent) { @@ -326,6 +330,7 @@ export const Gantt: React.FunctionComponent = ({ rowHeight, dates: dateSetup.dates, todayColor, + rtl, }; const calendarProps: CalendarProps = { dateSetup, @@ -335,6 +340,7 @@ export const Gantt: React.FunctionComponent = ({ columnWidth, fontFamily, fontSize, + rtl, }; const barProps: TaskGanttContentProps = { tasks: barTasks, @@ -408,6 +414,8 @@ export const Gantt: React.FunctionComponent = ({ headerHeight={headerHeight} taskListWidth={taskListWidth} TooltipContent={TooltipContent} + rtl={rtl} + svgWidth={svgWidth} /> )} = ({ headerHeight={headerHeight} scroll={scrollY} onScroll={handleScrollY} + rtl={rtl} /> = ({ rowHeight={rowHeight} taskHeight={taskHeight} arrowIndent={arrowIndent} + rtl={rtl} /> ); }); @@ -278,6 +279,7 @@ export const TaskGanttContent: React.FC = ({ onEventStart={handleBarEventStart} key={task.id} isSelected={!!selectedTask && task.id === selectedTask.id} + rtl={rtl} /> ); })} diff --git a/src/components/gantt/task-gantt.tsx b/src/components/gantt/task-gantt.tsx index 7f9bc57..73a7668 100644 --- a/src/components/gantt/task-gantt.tsx +++ b/src/components/gantt/task-gantt.tsx @@ -41,6 +41,7 @@ export const TaskGantt: React.FC = ({
= ({ tasks, @@ -18,6 +19,7 @@ export const GridBody: React.FC = ({ svgWidth, columnWidth, todayColor, + rtl, }) => { let y = 0; const gridRows: ReactChild[] = []; @@ -95,6 +97,23 @@ export const GridBody: React.FC = ({ /> ); } + // rtl for today + if ( + rtl && + i + 1 !== dates.length && + date.getTime() >= now.getTime() && + dates[i + 1].getTime() < now.getTime() + ) { + today = ( + + ); + } tickX += columnWidth; } return ( diff --git a/src/components/other/arrow.tsx b/src/components/other/arrow.tsx index 5ba3547..52e8f28 100644 --- a/src/components/other/arrow.tsx +++ b/src/components/other/arrow.tsx @@ -7,6 +7,7 @@ type ArrowProps = { rowHeight: number; taskHeight: number; arrowIndent: number; + rtl: boolean; }; export const Arrow: React.FC = ({ taskFrom, @@ -14,19 +15,28 @@ export const Arrow: React.FC = ({ rowHeight, taskHeight, arrowIndent, + rtl, }) => { - const indexCompare = taskFrom.index > taskTo.index ? -1 : 1; - const taskToEndPosition = taskTo.y + taskHeight / 2; + let path: string; + let trianglePoints: string; + if (rtl) { + [path, trianglePoints] = drownPathAndTriangleRTL( + taskFrom, + taskTo, + rowHeight, + taskHeight, + arrowIndent + ); + } else { + [path, trianglePoints] = drownPathAndTriangle( + taskFrom, + taskTo, + rowHeight, + taskHeight, + arrowIndent + ); + } - const path = `M ${taskFrom.x2} ${taskFrom.y + taskHeight / 2} - h ${arrowIndent} - v ${(indexCompare * rowHeight) / 2} - H ${taskTo.x1 - arrowIndent} - V ${taskToEndPosition} - h ${arrowIndent}`; - const trianglePoints = `${taskTo.x1},${taskToEndPosition} - ${taskTo.x1 - 5},${taskToEndPosition - 5} - ${taskTo.x1 - 5},${taskToEndPosition + 5}`; return ( @@ -34,3 +44,63 @@ export const Arrow: React.FC = ({ ); }; + +const drownPathAndTriangle = ( + taskFrom: BarTask, + taskTo: BarTask, + rowHeight: number, + taskHeight: number, + arrowIndent: number +) => { + const indexCompare = taskFrom.index > taskTo.index ? -1 : 1; + const taskToEndPosition = taskTo.y + taskHeight / 2; + const taskFromEndPosition = taskFrom.x2 + arrowIndent * 2; + const taskFromHorizontalOffsetValue = + taskFromEndPosition < taskTo.x1 ? "" : `H ${taskTo.x1 - arrowIndent}`; + const taskToHorizontalOffsetValue = + taskFromEndPosition > taskTo.x1 + ? arrowIndent + : taskTo.x1 - taskFrom.x2 - arrowIndent; + + const path = `M ${taskFrom.x2} ${taskFrom.y + taskHeight / 2} + h ${arrowIndent} + v ${(indexCompare * rowHeight) / 2} + ${taskFromHorizontalOffsetValue} + V ${taskToEndPosition} + h ${taskToHorizontalOffsetValue}`; + + const trianglePoints = `${taskTo.x1},${taskToEndPosition} + ${taskTo.x1 - 5},${taskToEndPosition - 5} + ${taskTo.x1 - 5},${taskToEndPosition + 5}`; + return [path, trianglePoints]; +}; + +const drownPathAndTriangleRTL = ( + taskFrom: BarTask, + taskTo: BarTask, + rowHeight: number, + taskHeight: number, + arrowIndent: number +) => { + const indexCompare = taskFrom.index > taskTo.index ? -1 : 1; + const taskToEndPosition = taskTo.y + taskHeight / 2; + const taskFromEndPosition = taskFrom.x1 - arrowIndent * 2; + const taskFromHorizontalOffsetValue = + taskFromEndPosition > taskTo.x2 ? "" : `H ${taskTo.x2 + arrowIndent}`; + const taskToHorizontalOffsetValue = + taskFromEndPosition < taskTo.x2 + ? -arrowIndent + : taskTo.x2 - taskFrom.x1 + arrowIndent; + + const path = `M ${taskFrom.x1} ${taskFrom.y + taskHeight / 2} + h ${-arrowIndent} + v ${(indexCompare * rowHeight) / 2} + ${taskFromHorizontalOffsetValue} + V ${taskToEndPosition} + h ${taskToHorizontalOffsetValue}`; + + const trianglePoints = `${taskTo.x2},${taskToEndPosition} + ${taskTo.x2 + 5},${taskToEndPosition + 5} + ${taskTo.x2 + 5},${taskToEndPosition - 5}`; + return [path, trianglePoints]; +}; diff --git a/src/components/other/horizontal-scroll.tsx b/src/components/other/horizontal-scroll.tsx index 3a179dd..250d6a0 100644 --- a/src/components/other/horizontal-scroll.tsx +++ b/src/components/other/horizontal-scroll.tsx @@ -18,6 +18,7 @@ export const HorizontalScroll: React.FC<{ return (
= ({ task, rowHeight, + rtl, svgContainerHeight, svgContainerWidth, scrollX, @@ -40,30 +43,41 @@ export const Tooltip: React.FC = ({ const [relatedX, setRelatedX] = useState(0); useEffect(() => { if (tooltipRef.current) { - let newRelatedX = - task.x2 + arrowIndent + arrowIndent * 0.5 + taskListWidth - scrollX; - let newRelatedY = task.index * rowHeight - scrollY + headerHeight; - const tooltipHeight = tooltipRef.current.offsetHeight * 1.1; const tooltipWidth = tooltipRef.current.offsetWidth * 1.1; - const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY; - const tooltipLeftmostPoint = tooltipWidth + newRelatedX; - const fullChartWidth = taskListWidth + svgContainerWidth; - - if (tooltipLeftmostPoint > fullChartWidth) { - newRelatedX = - task.x1 + - taskListWidth - - arrowIndent - - arrowIndent * 0.5 - - scrollX - - tooltipWidth; + let newRelatedY = task.index * rowHeight - scrollY + headerHeight; + let newRelatedX: number; + if (rtl) { + newRelatedX = task.x1 - arrowIndent * 1.5 - tooltipWidth - scrollX; + if (newRelatedX < 0) { + newRelatedX = task.x2 + arrowIndent * 1.5 - scrollX; + } + const tooltipLeftmostPoint = tooltipWidth + newRelatedX; + if (tooltipLeftmostPoint > svgContainerWidth) { + newRelatedX = svgContainerWidth - tooltipWidth; + newRelatedY += rowHeight; + } + } else { + newRelatedX = task.x2 + arrowIndent * 1.5 + taskListWidth - scrollX; + const tooltipLeftmostPoint = tooltipWidth + newRelatedX; + const fullChartWidth = taskListWidth + svgContainerWidth; + if (tooltipLeftmostPoint > fullChartWidth) { + newRelatedX = + task.x1 + + taskListWidth - + arrowIndent * 1.5 - + scrollX - + tooltipWidth; + } + if (newRelatedX < taskListWidth) { + newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth; + newRelatedY += rowHeight; + } } - if (newRelatedX < taskListWidth) { - newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth; - newRelatedY += rowHeight; - } else if (tooltipLowerPoint > svgContainerHeight - scrollY) { + + const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY; + if (tooltipLowerPoint > svgContainerHeight - scrollY) { newRelatedY = svgContainerHeight - tooltipHeight; } setRelatedY(newRelatedY); diff --git a/src/components/other/vertical-scroll.module.css b/src/components/other/vertical-scroll.module.css index c2dcbc5..dddf3c5 100644 --- a/src/components/other/vertical-scroll.module.css +++ b/src/components/other/vertical-scroll.module.css @@ -1,6 +1,5 @@ .scroll { overflow: hidden auto; - margin-left: -17px; width: 17px; flex-shrink: 0; } diff --git a/src/components/other/vertical-scroll.tsx b/src/components/other/vertical-scroll.tsx index a3bd157..87b3f21 100644 --- a/src/components/other/vertical-scroll.tsx +++ b/src/components/other/vertical-scroll.tsx @@ -6,8 +6,16 @@ export const VerticalScroll: React.FC<{ ganttHeight: number; ganttFullHeight: number; headerHeight: number; + rtl: boolean; onScroll: (event: SyntheticEvent) => void; -}> = ({ scroll, ganttHeight, ganttFullHeight, headerHeight, onScroll }) => { +}> = ({ + scroll, + ganttHeight, + ganttFullHeight, + headerHeight, + rtl, + onScroll, +}) => { const scrollRef = useRef(null); useEffect(() => { @@ -18,7 +26,11 @@ export const VerticalScroll: React.FC<{ return (
= ({ task, isProgressChangeable, isDateChangeable, + rtl, onEventStart, isSelected, }) => { const progressPoint = getProgressPoint( - task.progressWidth + task.x1, + +!rtl * task.progressWidth + task.progressX, task.y, task.height ); diff --git a/src/components/task-item/task-item.tsx b/src/components/task-item/task-item.tsx index 3314bbb..fc20311 100644 --- a/src/components/task-item/task-item.tsx +++ b/src/components/task-item/task-item.tsx @@ -15,6 +15,7 @@ export type TaskItemProps = { isDateChangeable: boolean; isDelete: boolean; isSelected: boolean; + rtl: boolean; onEventStart: ( action: GanttContentMoveAction, selectedTask: BarTask, @@ -29,6 +30,7 @@ export const TaskItem: React.FC = props => { isDelete, taskHeight, isSelected, + rtl, onEventStart, } = { ...props, @@ -63,9 +65,19 @@ export const TaskItem: React.FC = props => { const getX = () => { const width = task.x2 - task.x1; const hasChild = task.barChildren.length > 0; - return isTextInside - ? task.x1 + width * 0.5 - : task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2; + if (isTextInside) { + return task.x1 + width * 0.5; + } + if (rtl && textRef.current) { + return ( + task.x1 - + textRef.current.getBBox().width - + arrowIndent * +hasChild - + arrowIndent * 0.2 + ); + } else { + return task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2; + } }; return ( diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index 1a276e4..e2a7229 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -12,7 +12,7 @@ export const TaskListTableDefault: React.FC<{ selectedTaskId: string; setSelectedTask: (taskId: string) => void; }> = ({ rowHeight, rowWidth, tasks, fontFamily, fontSize, locale }) => { - const dateTimeOptions = { + const dateTimeOptions: Intl.DateTimeFormatOptions = { weekday: "short", year: "numeric", month: "long", diff --git a/src/helpers/bar-helper.ts b/src/helpers/bar-helper.ts index 69ee677..9531c0c 100644 --- a/src/helpers/bar-helper.ts +++ b/src/helpers/bar-helper.ts @@ -165,8 +165,8 @@ const convertToBar = ( let x1: number; let x2: number; if (rtl) { - x2 = taskXCoordinate(task.start, dates, dateDelta, columnWidth); - x1 = taskXCoordinate(task.end, dates, dateDelta, columnWidth); + x2 = taskXCoordinateRTL(task.start, dates, dateDelta, columnWidth); + x1 = taskXCoordinateRTL(task.end, dates, dateDelta, columnWidth); } else { x1 = taskXCoordinate(task.start, dates, dateDelta, columnWidth); x2 = taskXCoordinate(task.end, dates, dateDelta, columnWidth); @@ -177,14 +177,12 @@ const convertToBar = ( x2 = x1 + handleWidth * 2; } - const progressWidth = progressWithByParams(x1, x2, task.progress); - let progressX: number; - if (rtl) { - progressX = x2 - progressWidth; - } else { - progressX = x1; - } - + const [progressWidth, progressX] = progressWithByParams( + x1, + x2, + task.progress, + rtl + ); const y = taskYCoordinate(index, rowHeight, taskHeight); const styles = { @@ -281,7 +279,16 @@ const taskXCoordinate = ( ); return x; }; - +const taskXCoordinateRTL = ( + xDate: Date, + dates: Date[], + dateDelta: number, + columnWidth: number +) => { + let x = taskXCoordinate(xDate, dates, dateDelta, columnWidth); + x += columnWidth; + return x; +}; const taskYCoordinate = ( index: number, rowHeight: number, @@ -294,9 +301,17 @@ const taskYCoordinate = ( export const progressWithByParams = ( taskX1: number, taskX2: number, - progress: number + progress: number, + rtl: boolean ) => { - return (taskX2 - taskX1) * progress * 0.01; + const progressWidth = (taskX2 - taskX1) * progress * 0.01; + let progressX: number; + if (rtl) { + progressX = taskX2 - progressWidth; + } else { + progressX = taskX1; + } + return [progressWidth, progressX]; }; export const progressByProgressWidth = ( @@ -319,6 +334,15 @@ const progressByX = (x: number, task: BarTask) => { return progressPercent; } }; +const progressByXRTL = (x: number, task: BarTask) => { + if (x >= task.x2) return 0; + else if (x <= task.x1) return 100; + else { + const barWidth = task.x2 - task.x1; + const progressPercent = Math.round(((task.x2 - x) * 100) / barWidth); + return progressPercent; + } +}; export const getProgressPoint = ( progressX: number, @@ -431,19 +455,21 @@ const handleTaskBySVGMouseEventForBar = ( let isChanged = false; switch (action) { case "progress": - changedTask.progress = progressByX(svgX, selectedTask); + if (rtl) { + changedTask.progress = progressByXRTL(svgX, selectedTask); + } else { + changedTask.progress = progressByX(svgX, selectedTask); + } isChanged = changedTask.progress !== selectedTask.progress; if (isChanged) { - changedTask.progressWidth = progressWithByParams( + const [progressWidth, progressX] = progressWithByParams( changedTask.x1, changedTask.x2, - changedTask.progress + changedTask.progress, + rtl ); - if (rtl) { - changedTask.progressX = changedTask.x2 - changedTask.progressWidth; - } else { - changedTask.progressX = changedTask.x1; - } + changedTask.progressWidth = progressWidth; + changedTask.progressX = progressX; } break; case "start": { @@ -451,13 +477,31 @@ const handleTaskBySVGMouseEventForBar = ( changedTask.x1 = newX1; isChanged = changedTask.x1 !== selectedTask.x1; if (isChanged) { - changedTask.start = dateByX( - newX1, - selectedTask.x1, - selectedTask.start, - xStep, - timeStep + if (rtl) { + changedTask.end = dateByX( + newX1, + selectedTask.x1, + selectedTask.end, + xStep, + timeStep + ); + } else { + changedTask.start = dateByX( + newX1, + selectedTask.x1, + selectedTask.start, + xStep, + timeStep + ); + } + const [progressWidth, progressX] = progressWithByParams( + changedTask.x1, + changedTask.x2, + changedTask.progress, + rtl ); + changedTask.progressWidth = progressWidth; + changedTask.progressX = progressX; } break; } @@ -466,13 +510,31 @@ const handleTaskBySVGMouseEventForBar = ( changedTask.x2 = newX2; isChanged = changedTask.x2 !== selectedTask.x2; if (isChanged) { - changedTask.end = dateByX( - newX2, - selectedTask.x2, - selectedTask.end, - xStep, - timeStep + if (rtl) { + changedTask.start = dateByX( + newX2, + selectedTask.x2, + selectedTask.start, + xStep, + timeStep + ); + } else { + changedTask.end = dateByX( + newX2, + selectedTask.x2, + selectedTask.end, + xStep, + timeStep + ); + } + const [progressWidth, progressX] = progressWithByParams( + changedTask.x1, + changedTask.x2, + changedTask.progress, + rtl ); + changedTask.progressWidth = progressWidth; + changedTask.progressX = progressX; } break; } @@ -500,6 +562,14 @@ const handleTaskBySVGMouseEventForBar = ( ); changedTask.x1 = newMoveX1; changedTask.x2 = newMoveX2; + const [progressWidth, progressX] = progressWithByParams( + changedTask.x1, + changedTask.x2, + changedTask.progress, + rtl + ); + changedTask.progressWidth = progressWidth; + changedTask.progressX = progressX; } break; } diff --git a/src/helpers/date-helper.ts b/src/helpers/date-helper.ts index 795fd65..400b47d 100644 --- a/src/helpers/date-helper.ts +++ b/src/helpers/date-helper.ts @@ -83,11 +83,17 @@ export const ganttDateRange = (tasks: Task[], viewMode: ViewMode) => { newStartDate = addToDate(newStartDate, -1, "day"); newEndDate = addToDate(newEndDate, 19, "day"); break; - default: + case ViewMode.QuarterDay: newStartDate = startOfDate(newStartDate, "day"); newEndDate = startOfDate(newEndDate, "day"); newStartDate = addToDate(newStartDate, -1, "day"); - newEndDate = addToDate(newEndDate, 5, "day"); + newEndDate = addToDate(newEndDate, 66, "hour"); // 24(1 day)*3 - 6 + break; + case ViewMode.HalfDay: + newStartDate = startOfDate(newStartDate, "day"); + newEndDate = startOfDate(newEndDate, "day"); + newStartDate = addToDate(newStartDate, -1, "day"); + newEndDate = addToDate(newEndDate, 108, "hour"); // 24(1 day)*5 - 12 break; } return [newStartDate, newEndDate]; @@ -163,3 +169,7 @@ export const getWeekNumberISO8601 = (date: Date) => { return weekNumber; } }; + +export const getDaysInMonth = (month: number, year: number) => { + return new Date(year, month + 1, 0).getDate(); +};