Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

min and max dates #59

Open
ggrillone opened this issue Apr 28, 2017 · 3 comments
Open

min and max dates #59

ggrillone opened this issue Apr 28, 2017 · 3 comments

Comments

@ggrillone
Copy link

ggrillone commented Apr 28, 2017

it would be nice to have the ability to set a min/max date on the component. leveraging the onChange callback that's already there with moment.js wouldn't be too difficult to implement that, but it's difficult to give a visual indicator (applying styles or class names) of what dates are "disabled" from outside the component

@ggrillone
Copy link
Author

for anyone else wanting to add a min/max date check, moment.js makes it super easy: http://momentjs.com/docs/#/query/

@ggrillone
Copy link
Author

ggrillone commented Apr 28, 2017

I ended up coming with a solution, it's a bit hacky as it uses js to change the class names of the table cells in the calendar. but it works for now, hopefully helpful to others:

import React from 'react'
import moment from 'moment'
import InputMoment from 'input-moment'

function _validateDate(minDate, maxDate, dateVal) {
  if (!minDate && !maxDate) {
    return true
  } else if (minDate && maxDate) {
    return _validateMinDate(minDate, dateVal) && _validateMaxDate(maxDate, dateVal)
  } else if (minDate) {
    return _validateMinDate(minDate, dateVal)
  } else if (maxDate) {
    return _validateMaxDate(maxDate, dateVal)
  }
}

function _validateMinDate(minDate, dateVal) {
  return !moment(dateVal).isBefore(minDate)
}

function _validateMaxDate(maxDate, dateVal) {
  return !moment(dateVal).isAfter(maxDate)
}


class DateTimePicker extends React.Component {
  constructor(props) {
    super(props)

    this.state = { dateTime: null }
  }

  componentDidMount() {
    this.disableOutOfRangeDates()
  }

  componentDidUpdate() {
    this.disableOutOfRangeDates()
  }

  disableOutOfRangeDates() {
    if (this.props.minDate || this.props.maxDate) {
      const inputMomentElem = this.refs['date-time-picker'].getElementsByClassName('m-input-moment')[0]

      if (inputMomentElem) {
        const calendarElem = inputMomentElem.getElementsByClassName('m-calendar tab is-active')[0]

        // only do if date selector is active
        if (calendarElem) {
          const calendarRows = calendarElem.getElementsByTagName('table')[0].getElementsByTagName('tbody')[0].getElementsByTagName('tr')

          this.disableCalendarRows(calendarRows)
        }
      }
    }
  }

  disableCalendarRows(calendarRows) {
    for (let i = 0;i < calendarRows.length;i++) {
      const rowCells = calendarRows[i].getElementsByTagName('td')

      for (let j = 0;j < rowCells.length;j++) {
        if (rowCells[j].className.match(/prev\-month/gi) && this.props.minDate) {
          this.setDisabledForPrevMonthCells(rowCells[j])
        } else if (rowCells[j].className.match(/next\-month/gi) && this.props.maxDate) {
          this.setDisabledForNextMonthCells(rowCells[j])
        } else if (!rowCells[j].className.match(/prev\-month/gi) && !rowCells[j].className.match(/next\-month/gi)) {
          this.setDisabledForCurrentMonthCells(rowCells[j])
        }
      }
    }
  }

  setDisabledForPrevMonthCells(row) {
    let
      dateTime = moment(this.state.dateTime),
      currentYear = parseInt(dateTime.format('YYYY'), 10),
      currentMonth = parseInt(dateTime.format('MM'), 10),
      day = row.innerHTML.toString(),
      prevMonth = (currentMonth - 1).toString()

    // if currently in january, go back to december of previous year
    if (currentMonth === 1) {
      prevMonth = 12
      currentYear = currentYear - 1
    }

    // for proper ISO format
    if (prevMonth.length === 1) {
      prevMonth = `0${prevMonth}`
    }

    // for proper ISO format
    if (day.length === 1) {
      day = `0${day}`
    }

    if (!_validateMinDate(this.props.minDate, `${currentYear}-${prevMonth}-${day}`)) {
      row.className += ' disabled'
    } else {
      row.className = row.className.replace(/disabled/gi, '')
    }
  }

  setDisabledForNextMonthCells(row) {
    let
      dateTime = moment(this.state.dateTime),
      currentYear = parseInt(dateTime.format('YYYY'), 10),
      currentMonth = parseInt(dateTime.format('MM'), 10),
      day = row.innerHTML.toString(),
      nextMonth = (currentMonth + 1).toString()

    // if currently in december, go to january of next year
    if (currentMonth === 12) {
      nextMonth = '01'
      currentYear = currentYear + 1
    }

    // for proper ISO format
    if (nextMonth.length === 1) {
      nextMonth = `0${nextMonth}`
    }

    // for proper ISO format
    if (day.length === 1) {
      day = `0${day}`
    }

    if (!_validateMaxDate(this.props.maxDate, `${currentYear}-${nextMonth}-${day}`)) {
      row.className += ' disabled'
    } else {
      row.className = row.className.replace(/disabled/gi, '')
    }
  }

  setDisabledForCurrentMonthCells(row) {
    let
      dateTime = moment(this.state.dateTime),
      currentYear = dateTime.format('YYYY'),
      currentMonth = dateTime.format('MM'),
      day = row.innerHTML.toString(),
      disabledForMinDate = false

    // for proper ISO format
    if (day.length === 1) {
      day = `0${day}`
    }

    if (this.props.minDate && !_validateMinDate(this.props.minDate, `${currentYear}-${currentMonth}-${day}`)) {
      disabledForMinDate = true
      row.className += ' disabled'
    } else {
      row.className = row.className.replace(/disabled/gi, '')
    }

    if (this.props.maxDate && !_validateMaxDate(this.props.maxDate, `${currentYear}-${currentMonth}-${day}`)) {
      row.className += ' disabled'
    } else {
      if (!disabledForMinDate) {
        row.className = row.className.replace(/disabled/gi, '')
      }
    }
  }

  onChangeCallback(newVal) {
    const isValidDate = _validateDate(this.props.minDate, this.props.maxDate, newVal)

    if (isValidDate) {
      this.setState({ dateTime: newVal })

      this.props.onChange(newVal)
    }
  }

  render() {
    ...
  }
}

DateTimePicker.propTypes = {
  defaultDateTime: oneOfType([string, object]).isRequired,
  onSave: func,
  onChange: func,
  minDate: oneOfType([string, object]),
  maxDate: oneOfType([string, object])
}

@anderconal
Copy link

@ggrillone I've just opened a new pull request trying to solve this issue:

#85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants