React Datepicker Year Dropdown

React Date picker Year Dropdown
Project: React Date picker
Author: Sathish kumar
Edit Online: View on CodePen
License: MIT

This React component helps you to create a datepicker with year dropdown. It renders a calendar UI with an input element based on the current month and year, with the ability to navigate to the previous or next month using arrow buttons. The component contains functions for determining the start date of each week. Users can easily select dates by clicking on a day in the calendar.

You can integrate this date picker to allow users to select a date for the date of birth or schedule an event. Moreover, the calendar UI can be customized using additional CSS according to your needs.

How to Create React Datepicker Year Dropdown

First of all, load the Reset CSS by adding the following CDN link into the head tag of your HTML document.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">

After that, create a container in HTML in which the datepicker component will be rendered:

<div id="container">
  <!-- This element's contents will be replaced with your component. -->
</div>

Now, add the following CSS styles to your project for datepicker UI. You can change the CSS rules in order to customize the date picker.

* {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

body {
	background: #fff;
	font: normal 13px/18px "Helvetica Neue", "Segoe UI", Helvetica, Arial,
		"Lucida Grande", sans-serif;
}

#container {
	width: 250px;
	margin: 50px auto;
}

.hidden-input {
	height: 0px;
	width: 0px;
	opacity: 0;
}

.datepicker-wrapper {
	position: relative;
	width: 250px;
}

.datepicker-input {
	border-bottom: 1px solid #e4e0e9;
	background: #fff;
	font-size: 13px;
	outline: none;
}

.is-focused {
	border-bottom: 1px solid #188fff;
}

.is-invalid {
    border-bottom: 1px solid red;
}

.datepicker-label {
	color: #979ba7;
	width: 64px;
	line-height: 28px;
	display: inline-block;
}

.datepicker-value {
	padding: 0 8px;
	margin-left: 8px;
	line-height: 28px;
	text-align: center;
	border: none;
	outline: none;
	color: #26282a;
	width: 100px;
    font-size: 12px;
	display: inline-block;
}

.datepicker-value.is-focused {
	background-color: rgba(24, 143, 255, 0.16);
	border-radius: 2px;
	height: 28px;
}

.datepicker-value.is-invalid {
	background-color: rgba(241, 88, 88, 0.16);
	border-radius: 2px;
	height: 28px;
}

.is-focused .arrow-down {
	border-color: #188fff transparent;
}

.arrow-down {
	border-color: #e0e4e9 transparent;
	border-style: solid;
	border-width: 5px 5px 0 5px;
	float: right;
	margin-top: 12px;
	margin-right: 8px;
	cursor: pointer;
}

.datepicker-calendar:after {
	content: "";
	position: absolute;
	transform: rotate(45deg);
	width: 12px;
	height: 12px;
	top: -7px;
	left: 102px;
	background: #fff;
}

.datepicker-calendar:before {
	content: "";
	position: absolute;
	transform: rotate(45deg);
	width: 12px;
	height: 12px;
	top: -7px;
	left: 102px;
	z-index: -1;
	box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
}

.datepicker-calendar {
	position: absolute;
	background: #fff;
	border-radius: 3px;
	padding: 12px 16px 16px;
	box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
	top: 38px;
	width: 230px;
	left: 50%;
	margin-left: -114px;
}

.datepicker-calendar.is-active {
    border: 1px solid #188fff;
}

.datepicker-calendar.is-active:after {
   border-top: 1px solid #188fff;
   border-left: 1px solid #188fff;
}

.day-header-wrapper {
	margin-top: 8px;
	margin-bottom: 2px;
}

.month-header-wrapper {
	font-size: 13px;
	height: 20px;
	border-bottom: 1px solid #e4e0e9;
}

.title-wrapper {
	display: inline-block;
	width: calc(100% - 40px);
	color: #26282a;
	font-weight: bold;
}

.year-title {
	margin-left: 4px;
}

.days-wrapper {
	margin-bottom: 2px;
}

.arrow {
	color: #188fff;
	width: 20px;
	height: 20px;
	text-align: center;
	cursor: pointer;
	font-weight: bold;
	-webkit-user-select: none;
	   -moz-user-select: none;
	    -ms-user-select: none;
	        user-select: none;
}

.right {
	float: right;
}

.day-header,
.day {
	display: inline-block;
	line-height: 20px;
	width: 28px;
	height: 20px;
	font-size: 11px;
	text-align: center;
}

.day-header {
	color: #b9bdc5;
}

.day {
	cursor: pointer;
}

.day.disabled {
	color: #979ba7;
	font-size: regular;
}

.day.selected {
	background: #188fff;
	border-radius: 3px;
	color: white;
	font-weight: bold;
}

.day:not(.selected):hover {
	background: #e4e0e9;
	border-radius: 3px;
}

.date-part {
	margin: 0 4px;
	text-align: center;
}

Load the React JS by adding the following CDN links before closing the body tag. Skip this step if you already loaded React framework into your project.

<script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js'></script>

Finally, add the following JavaScript function to activate the datepicker component.

const daysToAdjust = {
  "37": -1,
  "38": -7,
  "39": 1,
  "40": 7 };


function cloneDate(date) {
  return new Date(date.valueOf());
}

function isValidDate(value, format = "mm/dd/yyyy") {
  let month, day, year;
  const delimiter = /[^mdy]/.exec(format)[0];
  const formatParts = format.split(delimiter);
  const dateParts = value.split(delimiter);

  for (let i = 0; i < dateParts.length; i++) {
    if (/m/.test(formatParts[i])) month = dateParts[i];
    if (/d/.test(formatParts[i])) day = dateParts[i];
    if (/y/.test(formatParts[i])) year = dateParts[i];
  }

  return (
    month > 0 &&
    month < 13 &&
    year &&
    year.length === 4 &&
    day > 0 &&
    day <= new Date(year, month, 0).getDate());

}

function getFirstDayofWeek(date) {
  const temp = cloneDate(date);

  temp.setDate(temp.getDate() - temp.getDay());

  return temp;
}

function getAllDaysInAWeek(date) {
  const weekStartDate = getFirstDayofWeek(date);
  const days = [weekStartDate];

  for (let i = 1; i <= 6; i++) {
    const next = cloneDate(weekStartDate);

    next.setDate(next.getDate() + i);
    days.push(next);
  }

  return days;
}

function WeekHeader() {
  var header = ["S", "M", "T", "W", "T", "F", "S"].map(function (day, index) {
    return /*#__PURE__*/(
      React.createElement("span", { key: day + index, className: "day-header" },
      day));


  });

  return header;
}

function buildWeeks(date) {
  const tempDate = cloneDate(date);

  tempDate.setDate(1);

  const month = tempDate.getMonth();
  const weekStartDate = getFirstDayofWeek(tempDate);
  const currentDate = cloneDate(weekStartDate);
  const weeks = [cloneDate(currentDate)];

  currentDate.setDate(currentDate.getDate() + 7);

  while (currentDate.getMonth() === month) {
    weeks.push(cloneDate(currentDate));
    currentDate.setDate(currentDate.getDate() + 7);
  }

  return weeks;
}

function isWeekend(date) {
  const weekday = date.getDay();

  return weekday === 0 || weekday === 6;
}

function Day({ day, month, selected, onSelect }) {
  let className =
  day.getMonth() !== month || isWeekend(day) ? "disabled" : "";

  if (day.getTime() === selected.getTime()) {
    className = "selected";
  }

  return /*#__PURE__*/(
    React.createElement("span", {
      className: "day " + className,
      onMouseUp: onSelect.bind(null, day) },

    day.getDate()));


}

function Week({ weekStart, selected, month, onSelect }) {
  return /*#__PURE__*/(
    React.createElement("div", { className: "days-wrapper" },
    getAllDaysInAWeek(weekStart).map(function (day, i) {
      return /*#__PURE__*/(
        React.createElement(Day, {
          key: i,
          month: month,
          day: new Date(day),
          selected: selected,
          onSelect: onSelect }));


    })));


}

function Weeks({ date, month, selected, onSelect }) {
  return /*#__PURE__*/(
    React.createElement("div", null,
    buildWeeks(date).map(
    function (day, i) {
      return /*#__PURE__*/(
        React.createElement(Week, {
          key: i,
          month: month,
          weekStart: day,
          selected: selected,
          onSelect: onSelect }));


    }.bind(this))));



}

const Calendar = React.createClass({ displayName: "Calendar",
  getInitialState: function () {
    return {
      selected: this.props.selectedDate };

  },

  componentWillReceiveProps({ selectedDate }) {
    this.setState({
      selected: new Date(selectedDate) });

  },

  setDate: function (selected) {
    this.setState({ selected }, this.props.onMonthChange);
  },

  nextMonth: function () {
    const clone = new Date(this.state.selected);

    clone.setMonth(this.state.selected.getMonth() + 1);
    this.setDate(clone);
  },

  previousMonth: function () {
    const clone = new Date(this.state.selected);

    clone.setMonth(this.state.selected.getMonth() - 1);
    this.setDate(clone);
  },

  render: function () {
    const { selected } = this.state;
    const { onSelect, selectedDate } = this.props;
    const year = selected.getFullYear();
    const month = selected.toLocaleString("en-us", { month: "long" });

    return /*#__PURE__*/(
      React.createElement("div", null, /*#__PURE__*/
      React.createElement("div", { className: "month-header-wrapper" }, /*#__PURE__*/
      React.createElement("span", { className: "title-wrapper" }, /*#__PURE__*/
      React.createElement("span", { className: "month-title" }, month), /*#__PURE__*/
      React.createElement("span", { className: "year-title " }, year)), /*#__PURE__*/

      React.createElement("span", { className: "arrow right", onClick: this.nextMonth }, ">"), /*#__PURE__*/


      React.createElement("span", { className: "arrow right", onClick: this.previousMonth }, "<")), /*#__PURE__*/



      React.createElement("div", { className: "day-header-wrapper" }, WeekHeader()), /*#__PURE__*/
      React.createElement(Weeks, {
        date: selected,
        onSelect: onSelect,
        selected: selectedDate,
        month: selected.getMonth() })));



  } });


const DatePicker = React.createClass({ displayName: "DatePicker",
  getInitialState: function () {
    return {
      isOpen: false,
      isFocused: false,
      currentSelected: new Date(),
      // change to desired format
      tempDate: new Date().toLocaleDateString() };

  },

  componentDidMount: function () {
    document.addEventListener("click", this.handleWindowMouseDown);
  },

  handleWindowMouseDown(event) {
    const datepicker = ReactDOM.findDOMNode(this.datePicker);
    const calendar = ReactDOM.findDOMNode(this.calendar);

    if (!(calendar && datepicker)) {
      return;
    }

    const eventIsOutside =
    !calendar.contains(event.target) && calendar !== event.target;
    const eventIsOnPopoverAnchor =
    datepicker.contains(event.target) || datepicker === event.target;

    if (eventIsOutside && !eventIsOnPopoverAnchor) {
      this.setState({ isOpen: false });
    }
  },

  close: function () {
    this.setState({ isOpen: false, isFocused: false });
  },

  handleKeyDown: function (event) {
    switch (event.keyCode) {
      case 37:
      case 38:
      case 39:
      case 40:
        if (!event.shiftKey) {
          return;
        }

        event.preventDefault();

        const newDate = new Date(this.state.currentSelected);

        newDate.setDate(
        newDate.getDate() + daysToAdjust[event.keyCode]);


        this.setState({ currentSelected: newDate });

        return;
      case 13:
        event.preventDefault();

        const { isOpen, currentSelected } = this.state;

        if (!isOpen) {
          this.setState({ isOpen: true });
        } else {
          this.setState({
            isOpen: false,
            currentSelected,
            tempDate: currentSelected.toLocaleDateString() });

        }
        return;
      case 9:
        if (event.shiftKey || !this.state.isOpen) {
          return;
        }

        this.close();

        return;
      case 27:
        this.close();

        return;}

  },

  handleFocus: function () {
    this.setState({ isFocused: true });
  },

  handleBlur: function () {
    this.setState({ isFocused: false });
  },

  open: function () {
    if (this.state.isOpen) {
      return;
    }

    this.setState({ isOpen: true });

    this.input.focus();
  },

  select: function (currentSelected) {
    this.setState({
      isOpen: false,
      currentSelected,
      invalidDate: false,
      tempDate: currentSelected.toLocaleDateString() });


    this.input.focus();
  },

  updateDate({ target: { value } }) {
    const newState = { tempDate: value, invalidDate: true };

    if (isValidDate(value)) {
      newState.invalidDate = false;
      newState.currentSelected = new Date(value);
    }

    this.setState(newState);
  },

  render: function () {
    const {
      isOpen,
      tempDate,
      isFocused,
      currentSelected,
      invalidDate = false } =
    this.state;
    const isInvalid = invalidDate ? " is-invalid" : "";
    const focusClass =
    !invalidDate && (isFocused || isOpen) ? " is-focused" : "";

    return /*#__PURE__*/(
      React.createElement("div", {
        className: "datepicker-wrapper",
        ref: ref => this.datePicker = ref }, /*#__PURE__*/

      React.createElement("div", {
        onClick: this.open,
        className: "datepicker-input" + focusClass + isInvalid }, /*#__PURE__*/

      React.createElement("span", { className: "arrow-down" }), /*#__PURE__*/
      React.createElement("label", { className: "datepicker-label" }, "From"), /*#__PURE__*/
      React.createElement("input", {
        type: "text",
        value: tempDate,
        placeholder: "mm/dd/yyyy",
        onChange: this.updateDate,
        onKeyDown: this.handleKeyDown,
        ref: input => this.input = input,
        className: "datepicker-value" + focusClass + isInvalid })),


      isOpen && /*#__PURE__*/
      React.createElement("div", {
        className: "datepicker-calendar",
        ref: ref => this.calendar = ref }, /*#__PURE__*/

      React.createElement(Calendar, {
        onSelect: this.select,
        selectedDate: currentSelected,
        onMonthChange: () => this.input.focus() }))));





  } });


ReactDOM.render( /*#__PURE__*/React.createElement(DatePicker, null), document.getElementById("container"));

That’s all! hopefully, you have successfully integrated this datepicker with year dropdown into your React project. If you have any questions or suggestions, feel free to comment below.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply