diff --git a/src/components/Body.tsx b/src/components/Body.tsx index 2506cdd..3c61d26 100644 --- a/src/components/Body.tsx +++ b/src/components/Body.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { memo, useMemo } from 'react'; import { SlotComponentProps, VisibilityMatrix } from '../types'; import classnames from '../utils/classnames'; import styles from './styles.less'; @@ -31,49 +31,51 @@ export type BodyPrivateProps = { }; }; -const Body = ({ - numberOfWeeks, - numberOfTodayWeek, - startRenderOnCurrentWeek, - locale, - numberOfRowsPreRender, - startDate, - updateVisibilityMatrix, - visibilityMatrix, - slots, - slotProps, -}: BodyPrivateProps): JSX.Element => { - const RootSlot = slots?.root || 'div'; - const rootProps = { ...(slotProps?.root || {}), className: classnames(styles.body, slotProps?.root?.className) }; +const Body = memo( + ({ + numberOfWeeks, + numberOfTodayWeek, + startRenderOnCurrentWeek, + locale, + numberOfRowsPreRender, + startDate, + updateVisibilityMatrix, + visibilityMatrix, + slots, + slotProps, + }: BodyPrivateProps): JSX.Element => { + const RootSlot = useMemo(() => slots?.root || 'div', [slots]); + const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body, slotProps?.root?.className) }), [slotProps]); - return ( - - {!!startDate && - Array.from(Array(numberOfWeeks + 1).keys()).map(numberOfWeek => ( - - ))} - - ); -}; + return ( + + {!!startDate && + Array.from(Array(numberOfWeeks + 1).keys()).map(numberOfWeek => ( + + ))} + + ); + }, +); export default Body; diff --git a/src/components/BodyCell.tsx b/src/components/BodyCell.tsx index 5410a4f..96ebf0f 100644 --- a/src/components/BodyCell.tsx +++ b/src/components/BodyCell.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, memo } from 'react'; import { BodyCellData } from '../types'; import classnames from '../utils/classnames'; import styles from './styles.less'; @@ -21,24 +21,28 @@ export type BodyCellPrivateProps = { }; }; -const BodyCell = ({ data, locale, slots, slotProps }: BodyCellPrivateProps): JSX.Element => { - const [RootSlot, ContentSlot] = useMemo(() => [slots?.root || 'li', slots?.content || BodyCellContent], [slots]); - const { onClick, className, ...restRootProps } = slotProps?.root || {}; - const rootProps = useMemo(() => ({ ...restRootProps, className: classnames(styles.body__cell, className) }), [restRootProps, className]); - const contentProps = useMemo(() => ({ ...(slotProps?.content || {}), className: classnames(styles.body__cell__content, slotProps?.content?.className) }), [slotProps]); +const BodyCell = memo( + ({ data, locale, slots, slotProps }: BodyCellPrivateProps): JSX.Element => { + const [RootSlot, ContentSlot] = useMemo(() => [slots?.root || 'li', slots?.content || BodyCellContent], [slots]); + const { onClick, className, ...restRootProps } = slotProps?.root || {}; + const rootProps = useMemo(() => ({ ...restRootProps, className: classnames(styles.body__cell, className) }), [restRootProps, className]); + const contentProps = useMemo(() => ({ ...(slotProps?.content || {}), className: classnames(styles.body__cell__content, slotProps?.content?.className) }), [ + slotProps, + ]); - const handleCellClick = useCallback( - (event: React.MouseEventHandler) => { - if (onClick) onClick(event, data); - }, - [onClick, data], - ); + const handleCellClick = useCallback( + (event: React.MouseEventHandler) => { + if (onClick) onClick(event, data); + }, + [onClick, data], + ); - return ( - - - - ); -}; + return ( + + + + ); + }, +); export default BodyCell; diff --git a/src/components/BodyCellContent.tsx b/src/components/BodyCellContent.tsx index 2e8c574..239da4a 100644 --- a/src/components/BodyCellContent.tsx +++ b/src/components/BodyCellContent.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { memo } from 'react'; import { BodyCellData, SlotComponentProps } from '../types'; import { getBodyCellContent } from '../helpers'; export type BodyCellContentProps = SlotComponentProps<'div', { data: BodyCellData; locale?: string }>; -const BodyCellContent = ({ data, locale, ...rest }: BodyCellContentProps): JSX.Element =>
{getBodyCellContent(data, locale)}
; +const BodyCellContent = memo(({ data, locale, ...rest }: BodyCellContentProps): JSX.Element =>
{getBodyCellContent(data, locale)}
); export default BodyCellContent; diff --git a/src/components/BodyRow.tsx b/src/components/BodyRow.tsx index 2dfbbf2..4246a62 100644 --- a/src/components/BodyRow.tsx +++ b/src/components/BodyRow.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useEffect } from 'react'; +import React, { useMemo, useRef, useEffect, memo } from 'react'; import useOnScreen from '../hooks/useOnScreen'; import { getCellAttributes } from '../helpers'; import { VisibilityMatrix, SlotRefComponentProps } from '../types'; @@ -30,58 +30,60 @@ export type BodyRowPrivateProps = { }; }; -const BodyRow = ({ - numberOfWeek, - numberOfTodayWeek, - startRenderOnCurrentWeek, - startDate, - locale, - visibilityMatrix, - updateVisibilityMatrix, - numberOfRowsPreRender, - slots, - slotProps, -}: BodyRowPrivateProps): JSX.Element => { - const ref = useRef(null); - const shouldScroll = useRef(startRenderOnCurrentWeek && numberOfWeek === numberOfTodayWeek); - const isVisible = useOnScreen(ref); - const shouldRender = useMemo( - () => - visibilityMatrix[numberOfWeek] || - Array(numberOfRowsPreRender) - .fill(null) - .some((_, idx) => visibilityMatrix[numberOfWeek - idx]), - [visibilityMatrix, numberOfWeek, numberOfRowsPreRender], - ); - const data = useMemo(() => { - if (!startDate || !shouldRender) return []; - const dates = Array.from(Array(7).keys()).map(day => getCellAttributes(startDate, numberOfWeek, day, locale)); - return dates; - }, [startDate, numberOfWeek, locale, shouldRender]); - const RootSlot = useMemo(() => slots?.root || 'ul', [slots]); - const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body__row, slotProps?.root?.className) }), [slotProps]); +const BodyRow = memo( + ({ + numberOfWeek, + numberOfTodayWeek, + startRenderOnCurrentWeek, + startDate, + locale, + visibilityMatrix, + updateVisibilityMatrix, + numberOfRowsPreRender, + slots, + slotProps, + }: BodyRowPrivateProps): JSX.Element => { + const ref = useRef(null); + const shouldScroll = useRef(startRenderOnCurrentWeek && numberOfWeek === numberOfTodayWeek); + const isVisible = useOnScreen(ref); + const shouldRender = useMemo( + () => + visibilityMatrix[numberOfWeek] || + Array(numberOfRowsPreRender) + .fill(null) + .some((_, idx) => visibilityMatrix[numberOfWeek - idx]), + [visibilityMatrix, numberOfWeek, numberOfRowsPreRender], + ); + const data = useMemo(() => { + if (!startDate || !shouldRender) return []; + const dates = Array.from(Array(7).keys()).map(day => getCellAttributes(startDate, numberOfWeek, day, locale)); + return dates; + }, [startDate, numberOfWeek, locale, shouldRender]); + const RootSlot = useMemo(() => slots?.root || 'ul', [slots]); + const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body__row, slotProps?.root?.className) }), [slotProps]); - useEffect(() => { - if (isVisible && !shouldRender && updateVisibilityMatrix) updateVisibilityMatrix(numberOfWeek); - }, [isVisible, shouldRender, updateVisibilityMatrix, numberOfWeek]); + useEffect(() => { + if (isVisible && !shouldRender && updateVisibilityMatrix) updateVisibilityMatrix(numberOfWeek); + }, [isVisible, shouldRender, updateVisibilityMatrix, numberOfWeek]); - useEffect(() => { - if (ref.current && shouldScroll.current) ref.current.scrollIntoView(); - }, []); + useEffect(() => { + if (ref.current && shouldScroll.current) ref.current.scrollIntoView(); + }, []); - return ( - - {data.map(cell => ( - - ))} - - ); -}; + return ( + + {data.map(cell => ( + + ))} + + ); + }, +); export default BodyRow; diff --git a/src/components/Container.tsx b/src/components/Container.tsx index a458a3b..5307e29 100644 --- a/src/components/Container.tsx +++ b/src/components/Container.tsx @@ -17,8 +17,7 @@ export type ContainerPrivateProps = { const Container = memo( ({ children, slots, slotProps }: ContainerPrivateProps): JSX.Element => { - const containerClassName = classnames(styles.calendar, slotProps?.root?.className); - const containerProps = { ...(slotProps?.root || {}), className: containerClassName }; + const containerProps = { ...(slotProps?.root || {}), className: classnames(styles.calendar, slotProps?.root?.className) }; const ContainerSlot = slots?.root || 'div'; return {children}; diff --git a/src/components/Empty.tsx b/src/components/Empty.tsx index 3e57f09..ff9dee8 100644 --- a/src/components/Empty.tsx +++ b/src/components/Empty.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { memo } from 'react'; import classnames from '../utils/classnames'; import styles from './styles.less'; import { SlotComponentProps } from '../types'; @@ -14,17 +14,19 @@ export type EmptyPrivateProps = { }; }; -const Empty = ({ slots, slotProps }: EmptyPrivateProps): JSX.Element => { - const rootClassName = classnames(styles.empty, slotProps?.root?.className); - const { label = 'There is no date range to display', ...restProps } = slotProps?.root || {}; - const rootProps = { ...restProps, ...(slots?.root ? { label } : {}), className: rootClassName }; - const RootSlot = slots?.root || 'div'; +const Empty = memo( + ({ slots, slotProps }: EmptyPrivateProps): JSX.Element => { + const rootClassName = classnames(styles.empty, slotProps?.root?.className); + const { label = 'There is no date range to display', ...restProps } = slotProps?.root || {}; + const rootProps = { ...restProps, ...(slots?.root ? { label } : {}), className: rootClassName }; + const RootSlot = slots?.root || 'div'; - return ( - -

{label}

-
- ); -}; + return ( + +

{label}

+
+ ); + }, +); export default Empty; diff --git a/src/index.tsx b/src/index.tsx index 994a259..d8d4063 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,6 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState, memo } from 'react'; import { getCalendarBaseAttributes } from './helpers'; -import { CalendarTuple, VisibilityMatrix, WeekdayIndex } from './types'; +import { CalendarTuple, VisibilityMatrix, WeekdayIndex, HeaderCellData, BodyCellData } from './types'; import Container, { ContainerProps } from './components/Container'; import Header, { HeaderProps, HeaderCellContentProps, HeaderCellProps } from './components/Header'; import Body, { BodyProps } from './components/Body'; @@ -9,7 +9,18 @@ import { BodyCellProps } from './components/BodyCell'; import { BodyCellContentProps } from './components/BodyCellContent'; import Empty, { EmptyProps } from './components/Empty'; -export type { ContainerProps, HeaderProps, HeaderCellProps, HeaderCellContentProps, BodyProps, BodyRowProps, BodyCellProps, BodyCellContentProps }; +export type { + ContainerProps, + HeaderProps, + HeaderCellProps, + HeaderCellContentProps, + BodyProps, + BodyRowProps, + BodyCellProps, + BodyCellContentProps, + HeaderCellData, + BodyCellData, +}; type Slots = { root?: React.ElementType; @@ -47,61 +58,63 @@ export type IntervalCalendarProps = { slotProps?: SlotProps; }; -const IntervalCalendar = ({ - start = undefined, - end = undefined, - locale = 'default', - numberOfRowsFirstRender = 8, - numberOfRowsPreRender = 4, - startRenderOnCurrentWeek = false, - weekStartsOn = 0, - slots, - slotProps, -}: IntervalCalendarProps): JSX.Element => { - const [startDate, , numberOfWeeks, numberOfTodayWeek] = useMemo(() => getCalendarBaseAttributes(start, end, weekStartsOn), [start, end, weekStartsOn]); +const IntervalCalendar = memo( + ({ + start = undefined, + end = undefined, + locale = 'default', + numberOfRowsFirstRender = 8, + numberOfRowsPreRender = 4, + startRenderOnCurrentWeek = false, + weekStartsOn = 0, + slots, + slotProps, + }: IntervalCalendarProps): JSX.Element => { + const [startDate, , numberOfWeeks, numberOfTodayWeek] = useMemo(() => getCalendarBaseAttributes(start, end, weekStartsOn), [start, end, weekStartsOn]); - const [visibilityMatrix, setVisibilityMatrix] = useState( - Array(startRenderOnCurrentWeek ? numberOfTodayWeek + numberOfRowsFirstRender : numberOfRowsFirstRender) - .fill(null) - .reduce((acc: VisibilityMatrix, _, week) => ({ ...acc, [week]: startRenderOnCurrentWeek ? !(week < numberOfTodayWeek) : true }), {}), - ); + const [visibilityMatrix, setVisibilityMatrix] = useState( + Array(startRenderOnCurrentWeek ? numberOfTodayWeek + numberOfRowsFirstRender : numberOfRowsFirstRender) + .fill(null) + .reduce((acc: VisibilityMatrix, _, week) => ({ ...acc, [week]: startRenderOnCurrentWeek ? !(week < numberOfTodayWeek) : true }), {}), + ); - const handleVisibilityMatrixChange = useCallback( - (week: number) => { - setVisibilityMatrix(prevState => ({ - ...prevState, - [week]: true, - })); - }, - [setVisibilityMatrix], - ); + const handleVisibilityMatrixChange = useCallback( + (week: number) => { + setVisibilityMatrix(prevState => ({ + ...prevState, + [week]: true, + })); + }, + [setVisibilityMatrix], + ); - return ( - -
- {!!numberOfWeeks && !!startDate ? ( - +
- ) : ( - - )} - - ); -}; + {!!numberOfWeeks && !!startDate ? ( + + ) : ( + + )} + + ); + }, +); export default IntervalCalendar;