最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make prev and next date buttons only move one date - Stack Overflow

programmeradmin2浏览0评论

I built a date picker that shows a list of dates, with three previous dates, the current date in the middle, and three next dates. On either side, there are buttons:

The Previous button moves the list backward.
The Next button moves the list forward.

The idea is that this should work in a circular way so when I reach the last date in the list and click Next, it should loop back to the first few dates, and the same goes in reverse when clicking Previous.

For example, if my current date list looks like this:

< 01/22 02/22 03/22 04/22 (Current) 05/22 06/22 07/22 >

Clicking Next should ideally wrap around and continue like this:

< 02/22 03/22 04/22 05/22 (Current) 06/22 07/22 08/22 >

But instead, my implementation just goes to the next available dates after 07/22 and the list is 08/22 and onwards.

Here is my code:

import { DatePicker, Button } from 'antd';
import './ButtonedAntDDatePicker.css';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import { useState, useEffect } from 'react';

dayjs.extend(customParseFormat);

function ButtonedAntDDatePicker({
    selectedDate,
    disabledDate,
    isDatePickerDisabled,
    enabledDates,
    setSelectedDate
}) {
    const [isAnimating, setIsAnimating] = useState(false);
    const [prevButtonsStyle, setPrevButtonsStyle] = useState({});
    const [nextButtonsStyle, setNextButtonsStyle] = useState({});
    const [datePickerInput, setDatePickerInput] = useState(null);

    // handle date picker input field
    useEffect(() => {
        const input = document.querySelector('.date-picker-container .ant-picker-input input');
        if (input) {
            setDatePickerInput(input);

            // If selectedDate is already set (from URL), update the input field
            if (selectedDate) {
                input.value = selectedDate.format('DD/MM/YYYY');
            }

            const handleInputEvent = (e) => {
                const inputValue = e.target.value;
                const formats = ['DD/MM/YYYY', 'D/M/YYYY', 'DD/M/YYYY', 'D/MM/YYYY'];

                for (const format of formats) {
                    const parsedDate = dayjs(inputValue, format, true);

                    if (parsedDate.isValid()) {
                        setSelectedDate(parsedDate);
                        e.target.value = parsedDate.format('DD/MM/YYYY');
                        break;
                    }
                }
            };

            input.addEventListener('blur', handleInputEvent);
            input.addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    handleInputEvent(e);
                }
            });

            return () => {
                input.removeEventListener('blur', handleInputEvent);
                input.removeEventListener('keypress', handleInputEvent);
            };
        }
    }, [setSelectedDate, selectedDate]);

    const findSurroundingDates = (currentDate, count) => {
        if (enabledDates.length === 0 || !currentDate) return { prevDates: [], nextDates: [] };

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const currentIndex = sortedDates.findIndex(date =>
            dayjs(date).isSame(currentDate, 'day')
        );

        const effectiveIndex = currentIndex === -1
            ? sortedDates.findIndex(date => dayjs(date).isAfter(currentDate))
            : currentIndex;

        const prevDates = sortedDates
            .slice(Math.max(0, effectiveIndex - count), effectiveIndex)
            .map(date => dayjs(date));

        const nextDates = sortedDates
            .slice(effectiveIndex + 1, effectiveIndex + count + 1)
            .map(date => dayjs(date));

        let hasFirstDate = false;
        let firstDateIndex = -1;

        const isNearEnd = effectiveIndex >= sortedDates.length - count;

        if (nextDates.length < count) {
            const remainingDates = count - nextDates.length;
            const firstDates = sortedDates.slice(0, remainingDates).map(date => dayjs(date));
            if (firstDates.length > 0) {
                hasFirstDate = true;
                firstDateIndex = nextDates.length;
            }
            nextDates.push(...firstDates);
        }

        //if it is first date in the list prev dates should be last dates in the list 
        if (prevDates.length < count) {
            const remainingDates = count - prevDates.length;
            const lastDates = sortedDates.slice(-remainingDates).map(date => dayjs(date));
            prevDates.unshift(...lastDates);
        }

        return { prevDates, nextDates, hasFirstDate, firstDateIndex, isNearEnd };
    };

    const { prevDates, nextDates, hasFirstDate, firstDateIndex, isNearEnd } = findSurroundingDates(selectedDate, 4);

    const handleDatePickerChange = (date) => {
        if (!date) return;
        setSelectedDate(date);
    };

    const handleDateButtonClick = (date) => {
        if (isAnimating || !date) return;
        setIsAnimating(true);

        const isGoingLeft = date.isAfter(selectedDate);

        if (isGoingLeft) {
            setPrevButtonsStyle({ animation: 'moveLeftOut 0.15s cubic-bezier(0.33, 1.0, 0.68, 1.0) forwards' });
            setNextButtonsStyle({ animation: 'moveLeftIn 0.15s cubic-bezier(0.22, 1.0, 0.36, 1.0) forwards' });
        } else {
            setPrevButtonsStyle({ animation: 'moveRightIn 0.15s cubic-bezier(0.22, 1.0, 0.36, 1.0) forwards' });
            setNextButtonsStyle({ animation: 'moveRightOut 0.15s cubic-bezier(0.33, 1.0, 0.68, 1.0) forwards' });
        }

        setTimeout(() => {
            setSelectedDate(date);
            setIsAnimating(false);

            setPrevButtonsStyle({});
            setNextButtonsStyle({});
        }, 200);
    };

    const formatButtonDate = (date) => {
        return date.format('DD/MM/YYYY');
    };

    // checks if it is first date in the sorted dates list
    const isFirstDate = (date) => {
        if (!date || enabledDates.length === 0) return false;

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const firstDate = dayjs(sortedDates[0]);
        return date.isSame(firstDate, 'day');
    };

    // checks if it is the last date in the sorted dates list
    const isLastDate = (date) => {
        if (!date || enabledDates.length === 0) return false;

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const lastDate = dayjs(sortedDates[sortedDates.length - 1]);
        return date.isSame(lastDate, 'day');
    };

    const renderPrevButtons = () => {
        if (!prevDates.length) return null;

        return prevDates.map((date, index) => {
            const isLastDateIndicator = isLastDate(date);
            return (
                <div
                    key={`prev-${index}`}
                    style={{ position: 'relative' }}
                >
                    <Button
                        onClick={() => handleDateButtonClick(date)}
                        className={`date-button prev-date ${index === 0 ? 'first-button' : ''}`}
                    >
                        {index === 0 ? <i className="fa-solid fa-caret-left"></i> : formatButtonDate(date)}
                    </Button>
                </div>
            );
        });
    };

    const renderNextButtons = () => {
        if (!nextDates.length) return null;

        return nextDates.map((date, index) => {
            return (
                <div
                    key={`next-${index}`}
                    style={{ position: 'relative' }}
                >
                    <Button
                        onClick={() => handleDateButtonClick(date)}
                        className={`date-button next-date ${index === nextDates.length - 1 ? 'last-button' : ''}`}
                    >
                        {index === nextDates.length - 1 ? <i className="fa-solid fa-caret-right"></i> : formatButtonDate(date)}
                    </Button>
                </div>
            );
        });
    };

    if (isDatePickerDisabled) {
        return null;
    }

    return (
        <div className="date-picker-container">
            <div
                className="date-buttons-container prev-dates"
                style={prevButtonsStyle}
            >
                {renderPrevButtons()}
            </div>

            <DatePicker
                value={selectedDate}
                style={{ zIndex: 9999, margin: '0px' }}
                format={'DD/MM/YYYY'}
                onChange={handleDatePickerChange}
                disabledDate={disabledDate}
                allowClear={true}
                disabled={isDatePickerDisabled}
                inputReadOnly={false}
            />

            <div
                className="date-buttons-container next-dates"
                style={nextButtonsStyle}
            >
                {renderNextButtons()}
            </div>
        </div>
    );
}

export default ButtonedAntDDatePicker;
<div className='datepickers'>
     <ButtonedAntDDatePicker
       selectedDate={startDate}
       disabledDate={disabledDate}
       isDatePickerDisabled={isDatePickerDisabled}
       enabledDates={enabledDates}
       setSelectedDate={setStartDate}
     />
</div>

I expect that when I click on next/prev button it will only move to the prev date in the list not move whole thing. I've tried shifting the dates but it didn't quite work. Anyone has any idea on how to approach this issue?

发布评论

评论列表(0)

  1. 暂无评论