EN · DE · RU · FR · ES

#2654: DateInput.jsx

projectforge-webapp/src/components/design/input/calendar/DateInput.jsx Type: JavaScript/React · Role: Calendar · Source: projectforge-webapp/src/components/design/input/calendar/DateInput.jsx 184 lines · 159 code · 1 comments · 24 blank
React date/time input component providing calendar picker, date range, and formatted display for the ProjectForge web app.

Code Structure

Hooks used: Selector, Selector, Selector, State, State, State, Ref, Effect

Imports from: ../../../../utilities/propTypes, ../../popper/AdvancedPopper, ../AdditionalLabel, ../InputContainer, ./CalendarInput.module.scss, moment, prop-types, react, react-day-picker, react-day-picker/locale, react-redux

Has PropTypes for: DateInput

Uses CSS Modules for styling.

Source Code (abridged)

import moment from 'moment';
import 'moment/min/locales';
import PropTypes from 'prop-types';
import React from 'react';
import { DayPicker } from 'react-day-picker';
import { de } from 'react-day-picker/locale';
import { useSelector } from 'react-redux';
import { colorPropType } from '../../../../utilities/propTypes';
import AdvancedPopper from '../../popper/AdvancedPopper';
import AdditionalLabel from '../AdditionalLabel';
import InputContainer from '../InputContainer';
import styles from './CalendarInput.module.scss';

function DateInput(
    {
        additionalLabel,
        color,
        hideDayPicker = false,
        label,
        noInputContainer = false,
        setDate,
        todayButton,
        value,
    },
) {
    const jsDateFormat = useSelector((state) => state.authentication.user.jsDateFormat);
    const locale = useSelector((state) => state.authentication.user.locale) || 'en';
    const weekStartsOn = useSelector((state) => state.authentication.user.firstDayOfWeekSunday0);
    const [inputValue, setInputValue] = React.useState('');
    const [isActive, setIsActive] = React.useState(false);
    const [isOpen, setIsOpen] = React.useState(false);
    const inputRef = React.useRef(null);
    const Tag = noInputContainer ? React.Fragment : InputContainer;

    React.useEffect(() => {
        if (value) {
            setInputValue(moment(value)
                .format(jsDateFormat));
        } else {
            setInputValue('');
        }
    }, [value]);

    const handleBlur = () => {
        setIsActive(false);

        if (inputValue.trim() === '') {
            setDate(undefined);
            return;
        }

        const momentDate = moment(inputValue, jsDateFormat);

        if (momentDate.isValid()) {
            setDate(momentDate.toDate());
        } else {
            setInputValue(moment(value)
                .format(jsDateFormat));
        }
    };

    const handleChange = ({ target }) => {
        setInputValue(target.value);

        // Has to be strict, so moment doesnt correct your input every time you time
        const momentDate = moment(target.value, jsDateFormat, true);

        if (momentDate.isValid()) {
            setDate(momentDate.toDate());
        }
    };

    const handleFocus = () => setIsActive(true);

    const handleKeyDown = (event) => {
        const momentDate = moment(inputValue, jsDateFormat, true);

        if (momentDate.isValid()) {
            let newDate;
            if (event.key === 'ArrowUp') {
                newDate = momentDate.add(1, 'd');
            } else if (event.key === 'ArrowDown') {
                newDate = momentDate.subtract(1, 'd');
            }

            if (newDate) {
                event.preventDefault();
                setDate(newDate.toDate());
            }
        }
    };

    const handleTagClick = () => {
        if (inputRef.current) {
            inputRef.current.focus();
        }
    };

    const tagProps = {};

    if (Tag !== React.Fragment) {
        tagProps.color = color;
        tagProps.isActive = isActive || inputValue !== '';
        tagProps.label = label;
        tagProps.onClick = handleTagClick;
        tagProps.withMargin = true;
    }

    const placeholder = jsDateFormat
        .split('')
        .filter((char, index) => index >= inputValue.length)
        .join('');

    const input = (
        <>
            <Tag {...tagProps}>
                <div className={styles.dateInput}>
                    {isActive && (
                        <span
                            className={styles.placeholder}
// ... (truncated, total 184 lines)

Git History

bf988bc6d Eliminate 43 npm vulnerabilities: react-scripts→Vite, ESLint 9, dependency cleanup, bugfixes
ae4653957 fix date input default month
95b5d879a DayPicker also in German.
f586d54a5 fix daypicker first day of week
284e5d90f fix day picker month navigation