Цветовой эффект React Date Picker при наведении и заданном диапазоне дат - PullRequest
0 голосов
/ 19 апреля 2020

В настоящее время я использую React для создания компонента выбора даты, где вы можете установить диапазон дат. В качестве образца и модели я использовал пакет даты реакции . Теперь мне удалось построить функциональность средства выбора даты, но я не знаю, как добиться изменения цвета всех дат между начальной и конечной датой. Потому что в версии airbnb, как только вы установите дату начала, она автоматически устанавливает все дни между датой начала и курсором мыши в другом цвете. Этот эффект также присутствует, когда установлены как начальная, так и конечная дата (см. Приложение для справки).

Date Picker Hover effect

Вот мой код, Я также рад за каждое предложение по улучшению:)

import React, { useState, useEffect } from 'react';

//component
function Test() {
  const currentDate = new Date();

  //state for the datepicker month and year heading
  let [datePicker, setDatePicker] = useState({
    currentMonth: currentDate.getMonth(),
    currentYear: currentDate.getFullYear(),
  });

  //state that stores start and end date and keeps track which date is currently selected to be overridden
  let [dateRange, setDateRange] = useState({
    selectStartDate: true,
    selectEndDate: false,
    startDate: null,
    endDate: null,
  });

  //useEffect that colors the current start and end date in a different color
  useEffect(() => {
    let startClass = document.querySelector('.start-date');
    let endClass = document.querySelector('.end-date');

    if (startClass) startClass.classList.remove('start-date');
    if (endClass) endClass.classList.remove('end-date');

    if (dateRange.startDate) {
      let startDateAttribute = getDateFormatted(dateRange.startDate);
      let start = document.querySelectorAll(`[day="${startDateAttribute}"]`)[0];
      if (start) start.classList.add('start-date');
    }
    if (dateRange.endDate) {
      let endDateAttribute = getDateFormatted(dateRange.endDate);
      let end = document.querySelectorAll(`[day="${endDateAttribute}"]`)[0];
      if (end) end.classList.add('end-date');
    }
  }, [dateRange.startDate, dateRange.endDate, datePicker.currentMonth, datePicker.currentYear]);

  //initializes the grid and gets the days of the current month and year
  const monthGrid = [];
  getMonthGrid(datePicker.currentYear, datePicker.currentMonth, monthGrid);

  //takes the monthgrid and maps it to day elements that are then displayed on the page
  const days = monthGrid.map((element, id) => {
    if (element === null) {
      return <div className='day empty-day' key={id}></div>;
    }
    let date = new Date(datePicker.currentYear, datePicker.currentMonth, element);
    date = getDateFormatted(date);
    return (
      <div className='day' key={date} day={date} onClick={(e) => onClickDateRange(e)}>
        {element}
      </div>
    );
  });

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
        height: '100vh',
      }}
    >
      <div className='date-picker'>
        <div className='input-container'>
          <input
            type='text'
            name='start-date'
            className='date-picker__input'
            placeholder='Start Date'
            value={dateRange.startDate ? getDateFormatted(dateRange.startDate) : ''}
            onClick={onInputStartClick}
          />
          <input
            type='text'
            name='end-date'
            className='date-picker__input'
            placeholder='End Date'
            value={dateRange.endDate ? getDateFormatted(dateRange.endDate) : ''}
            onClick={onInputEndClick}
          />
        </div>

        <div className='year-container'>
          <button className='prev-month' onClick={prevMonth}>
            Prev
          </button>
          <h2>{`${getMonthTitle(datePicker.currentMonth)} ${datePicker.currentYear}`}</h2>
          <button className='next-month' onClick={nextMonth}>
            Next
          </button>
        </div>
        <div className='week-container'>
          <div>Mo</div>
          <div>Tu</div>
          <div>We</div>
          <div>Th</div>
          <div>Fr</div>
          <div>Sa</div>
          <div>So</div>
        </div>
        <div className='days'>{days}</div>
        <button onClick={getNumberOfDays}>Submit</button>
        <button onClick={clearDateRange}>Clear</button>
      </div>
    </div>
  );

  //MAIN FUNCTIONS
  //gets the days of the current month and year
  function getMonthGrid(year, month, monthGrid) {
    const firstDayType = new Date(year, month, 1).getDay();
    const lastDay = new Date(year, month + 1, 0);
    let lastDayType = lastDay.getDay();
    const numberOfDays = lastDay.getDate();

    if (firstDayType === 0) {
      for (let i = 1; i < 7; i++) {
        monthGrid.push(null);
      }
    } else {
      for (let i = 1; i < firstDayType; i++) {
        monthGrid.push(null);
      }
    }

    for (let i = 0; i < numberOfDays; i++) {
      monthGrid.push(i + 1);
    }

    if (lastDayType !== 0) {
      for (lastDayType; lastDayType < 7; lastDayType++) {
        monthGrid.push(null);
      }
    }
  }

  //on click function that is called whenever a day element is clicked
  function onClickDateRange(e) {
    //gets the date of the element that was clicked
    let date = new Date(datePicker.currentYear, datePicker.currentMonth, e.target.innerText);

    //end date is selected to be overridden, but date is out of bounds (smaller than start date)
    if (dateRange.selectEndDate && dateRange.startDate && date < dateRange.startDate) {
      setDateRange({
        selectStartDate: false,
        selectEndDate: true,
        startDate: date,
        endDate: null,
      });
      return;
    }

    //start date is selected to be overridden, but date is out of bounds (bigger than end date)
    if (dateRange.selectStartDate && dateRange.endDate && date > dateRange.endDate) {
      setDateRange({
        selectStartDate: false,
        selectEndDate: true,
        startDate: date,
        endDate: null,
      });
      return;
    }

    //there is no start date but an end date and date is out of bounds (bigger than end date)
    if (!dateRange.startDate && dateRange.endDate && date > dateRange.endDate) {
      setDateRange({
        selectStartDate: false,
        selectEndDate: true,
        startDate: date,
        endDate: null,
      });
      return;
    }

    //there is no start date but an end date
    if (!dateRange.startDate && dateRange.endDate) {
      setDateRange({
        ...dateRange,
        selectStartDate: false,
        selectEndDate: true,
        startDate: date,
      });
      return;
    }

    //base case if end date is selected
    if (dateRange.selectEndDate) {
      setDateRange({
        ...dateRange,
        endDate: date,
      });
      return;
    }

    //base case if start date is selected
    if (dateRange.selectStartDate) {
      setDateRange({
        ...dateRange,
        selectStartDate: false,
        selectEndDate: true,
        startDate: date,
      });
      return;
    }
  }

  //Handles clicks on the Inputs => sets which dates have to be overwritten
  function onInputStartClick() {
    setDateRange({ ...dateRange, selectStartDate: true, selectEndDate: false });
  }

  function onInputEndClick() {
    setDateRange({ ...dateRange, selectStartDate: false, selectEndDate: true });
  }

  //HELPER FUNCTIONS
  //gets the name of the current month
  function getMonthTitle(month) {
    const months = [
      'January',
      'February',
      'March',
      'April',
      'May',
      'June',
      'July',
      'August',
      'September',
      'October',
      'November',
      'December',
    ];
    return months[month];
  }

  //onclick function that sets the state of the datepicker to preview or next month
  function prevMonth() {
    if (datePicker.currentMonth - 1 < 0) {
      setDatePicker({ currentMonth: 11, currentYear: datePicker.currentYear - 1 });
      return;
    }
    setDatePicker({ ...datePicker, currentMonth: datePicker.currentMonth - 1 });
  }

  function nextMonth() {
    if (datePicker.currentMonth + 1 > 11) {
      setDatePicker({ currentMonth: 0, currentYear: datePicker.currentYear + 1 });
      return;
    }
    setDatePicker({ ...datePicker, currentMonth: datePicker.currentMonth + 1 });
  }

  //onclick function of submmit button that calculates the number of days set in the date range
  function getNumberOfDays() {
    const difference = (dateRange.endDate - dateRange.startDate) / (1000 * 60 * 60 * 24);
    console.log(difference);
  }

  //onclick function that clears the date range
  function clearDateRange() {
    setDateRange({
      selectStartDate: true,
      selectEndDate: false,
      startDate: null,
      endDate: null,
    });
  }

  //function that takes a date object as imput and formats it as dd/mm/yyyy
  function getDateFormatted(date) {
    const day = String(date.getDate()).padStart(2, '0');
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const year = date.getFullYear();
    return `${day}/${month}/${year}`;
  }
}

export default Test;

Я благодарен за любую помощь, которую я могу получить с этим, поскольку я не знаю, как достичь этого эффекта. Спасибо:)

1 Ответ

0 голосов
/ 19 апреля 2020

добавьте атрибут data-* к вашему элементу даты следующим образом:

<div 
  className='day' 
  key={date} 
  day={date} 
  data-date={UTCdateString} 
  onClick={(e) => onClickDateRange(e)}>
  {element}
</div>

присвойте ему строку даты ut c даты. создайте класс в вашем css для стилей в диапазоне, который вы хотите. внутри вашего useEffect обратного вызова добавьте условие, чтобы проверить, когда выбраны даты начала и окончания. внутри условия, используйте querySelectorAll для выбора всех элементов даты, go через эти элементы в for-l oop или что вы хотите получить значение даты-даты от каждого элемента (element.dataset.date - - MDN ), создать функцию решение с переполнением стека , чтобы проверить, попадает ли дата между датами начала / окончания, если это так, добавить класс (.in-range или как бы вы ни назвали его) этому элементу.

если вы чувствуете себя смелым, я бы создал компонент дат для всех бизнес-логи c, а затем возвратил бы сопоставленный массив компонентов дат, которые принимают реквизиты для визуализации стилей , затем избавьтесь от использования useEffect querySelector.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...