nexxtway/react-rainbow

View on GitHub
src/components/Calendar/index.js

Summary

Maintainability
A
3 hrs
Test Coverage
/* eslint-disable react/jsx-props-no-spreading */
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useLocale } from '../../libs/hooks';
import SingleCalendar from './singleCalendar';
import DoubleCalendar from './doubleCalendar';
import { buildNewRangeFromValue } from './helpers';
import { useCurrentDateFromValue, useRangeFromValue } from './hooks';

/**
 * Calendar provide a simple way to select a single date.
 */
export default function Calendar(props) {
    const { locale, selectionType, variant, value, onChange, ...rest } = props;
    const currentLocale = useLocale(locale);
    const currentValue = useCurrentDateFromValue(value);
    const range = useRangeFromValue(value, selectionType);

    const handleChange = useCallback(
        newValue => {
            if (selectionType === 'single') return onChange(newValue);
            const result = buildNewRangeFromValue(newValue, range);
            return onChange(result.range);
        },
        [selectionType, onChange, range],
    );

    if (variant === 'double')
        return (
            <DoubleCalendar
                locale={currentLocale}
                value={currentValue}
                selectedRange={range}
                selectionType={selectionType}
                onChange={handleChange}
                {...rest}
            />
        );

    return (
        <SingleCalendar
            locale={currentLocale}
            value={currentValue}
            selectedRange={range}
            selectionType={selectionType}
            onChange={handleChange}
            {...rest}
        />
    );
}

export { SingleCalendar as Component };

Calendar.propTypes = {
    /** Sets the date for the Calendar programmatically. */
    value: PropTypes.oneOfType([
        PropTypes.instanceOf(Date),
        PropTypes.string,
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string])),
    ]),
    /** The ending of a range of valid dates. The range includes the endDate.
     * The default value is current date + 100 years. */
    maxDate: PropTypes.instanceOf(Date),
    /** The beginning of a range of valid dates. The range includes the startDate.
     * The default value is current date - 100 years. */
    minDate: PropTypes.instanceOf(Date),
    /** The action triggered when a value attribute changes. */
    onChange: PropTypes.func,
    /** A CSS class for the outer element, in addition to the component's base classes. */
    className: PropTypes.string,
    /** An object with custom style applied to the outer element. */
    style: PropTypes.object,
    /** The id of the outer element. */
    id: PropTypes.string,
    /** The component locale. If the locale is not passed, it defaults to the context language, and if the context language is not passed, it will default to the browser's language. */
    locale: PropTypes.string,
    /** The type of the selection. It can be a single date or a range. The default value is 'single'. */
    selectionType: PropTypes.oneOf(['single', 'range']),
    /** The component variant. Defaults to 'single' */
    variant: PropTypes.oneOf(['single', 'double']),
    /** An array containing the days that should be disabled */
    disabledDays: PropTypes.arrayOf(
        PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
    ),
    /** An array containing the days that should be highlighted with a mark. Used for example to show an indicator that there are notifications in these days. */
    highlightedDays: PropTypes.arrayOf(
        PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
    ),
};

Calendar.defaultProps = {
    value: undefined,
    minDate: undefined,
    maxDate: undefined,
    onChange: () => {},
    className: undefined,
    style: undefined,
    id: undefined,
    locale: undefined,
    selectionType: 'single',
    variant: 'single',
    disabledDays: [],
    highlightedDays: [],
};