StockholmSyndrom
StockholmSyndrom

Reputation: 435

How to implements range in DatePicker component?

I have DatePicker component, and I want implements choice multiple dates on one Calendar component(DatePicker). Any ideas?

My idea:

Create an array with 2 values: startDate and endDate and int property "step" with an initial value are 0.

If I click on first cell date, step equal to 1, initial startDate and when step equals 1 I can click on second cell and initial endDate

And how store intermediate values around startDate and endDate?

Upvotes: 1

Views: 385

Answers (1)

Niklesh Raut
Niklesh Raut

Reputation: 34914

Going forward with your example and idea to make

setDateRange(dt) {
  let dateFor = this.step ? 'endDate' : 'startDate';
  this.rangeData[dateFor] = dt;
},
selectStartEnd(){
  this.step = !this.step;
},

so here is complete demo

var V = new Vue({
    el: "#app",
    filters: {
      monthName(month) {
        if(month === 0) {
          return 'Jan'
        }
        else if(month === 1) {
          return 'Feb'
        }
        else if(month === 2) {
          return 'Маr'
        }
        else if(month === 3) {
          return 'Apr'
        }
        else if(month === 4) {
          return 'May'
        }
        else if(month === 5) {
          return 'Jun'
        }
        else if(month === 6) {
          return 'Jul'
        }
        else if(month === 7) {
          return 'Aug'
        }
        else if(month === 8) {
          return 'Sep'
        }
        else if(month === 9) {
          return 'Oct'
        }
        else if(month === 10) {
          return 'Nov'
        }
        else if(month === 11) {
          return 'Dec'
        }
      }

    },
    data() {
      const startDate = new Date()
      return {
        isShow: false,
        selectedDate: null,
        pageTimestamp: startDate.setDate(1),
        step: 0,
        message: '',
        rangeData: {
          startDate: null,
          endDate: null
        },
        months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
          'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
        daysOfWeek: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
    }
  },
  computed: {
    days() {
      const d = this.pageDate
      let days = []
      let dObj = new Date(d.getFullYear(), d.getMonth(), 1)
      let daysInMonth = this.isDaysInMonth(dObj.getFullYear(), dObj.getMonth())
      for(let i = 0; i < daysInMonth; i++) {
        days.push({
          date: dObj.getDate(),
          fullDate: dObj.toLocaleDateString(),
          // Неделя начинается с 0. 0 - Воскресенье, 1 - понедельник,... 6 - суббота
          isWeekend: dObj.getDay() === 0 || dObj.getDay() === 6,
          isSaturday: dObj.getDay() === 6,
          isSunday: dObj.getDay() === 0
        })
        dObj.setDate(dObj.getDate() + 1)
      }
      return days
    },
    getMonth() {
      return this.pageDate.getMonth()
    },
    pageDate() {
      return new Date(this.pageTimestamp)
    },
    formattedValue() {
      let str = [];
      if(this.rangeData.startDate !== null){
        str.push(this.rangeData.startDate.fullDate);
      }
      if(this.rangeData.endDate !== null){
        str.push(this.rangeData.endDate.fullDate);
      }
      return str.join(' - ');
    },
  },
  methods: {
    setDateRange(dt) {
      let dateFor = this.step ? 'endDate' : 'startDate';
      this.rangeData[dateFor] = dt;
    },
    selectStartEnd(){
      this.step = !this.step;
    },
    validateDates(){
      let startMilliSeconds = Date.parse(this.rangeData.startDate.fullDate);
      let endMilliSeconds = Date.parse(this.rangeData.endDate.fullDate);
      if(endMilliSeconds < startMilliSeconds){
        this.message = "Start must be less than End date";
      }else{
        this.message = "Dates are valid";
        this.showCalendar();
      }
    },
    updateRangeDate(rangeDate) {
      this.rangeDate = rangeDate
    },
    showCalendar () {
      this.isShow = !this.isShow;
    },
    
    startDate(index) {
      this.$emit('start-date', this.days[index].fullDate)
    },
    endDate(index) {
      this.$emit('end-date', this.days[index].fullDate)
    },
    
    formatDate (date, format) {
      let year = date.getFullYear()
      let month = date.getMonth() + 1
      let day = date.getDate()
      let str = format.replace(/yyyy/, year).replace(/d/, day)
      return str
    },
    isDaysInMonth (year, month) {
      // В 8, 3, 5, 10 месяце - 30 дней
      return /8|3|5|10/.test(month) ? 30 : month === 1 ? (!(year % 4) && year % 100) || !(year % 400) ? 29 : 28 : 31
    },
    changeMonth (incrementBy) {
      let date = this.pageDate;
      date.setMonth(date.getMonth() + incrementBy)
      this.setPageDate(date)
    },
    previousMonth() {
      this.changeMonth(-1)
    },
    nextMonth () {
      this.changeMonth(+1)
    },
    setPageDate (date) {
      this.pageTimestamp = (new Date(date)).setDate(1)
    },
  }
  });
.calendar {
  color: #fff;
}
.calendar__month {
  color: #fff;
}
.calendar__prev,
.calendar__next {
  background-color: #bd3ba6;
  border-radius: 11px;
  width: 20px;
  height: 20px;
}
.calendar__input {
  display: none;
}
.calendar__list {
  position: relative;
  border-bottom: 1px solid #6d7ab9;
  min-width: 200px;
}
.calendar__list p {
  color: #5a64aa;
}
.calendar__list:after {
  content: '';
  position: absolute;
  right: 0;
  top: 2px;
  border: 6px solid transparent;
  border-top: 8px solid #6d7ab9;
}
.calendar__arrow-left {
  padding: 5px;
  border: #fff;
  border-width: 0 10px 10px 0;
}
.calendar__box {
  margin-top: 10px;
  padding: 25px 30px 15px 30px;
  box-shadow: 0 0 4px rgba(0,0,0,0.5);
  background-color: #1c2247;
}
.calendar__day p {
  color: rgba(255,255,255,0.46);
}
.calendar__days {
  margin-top: 35px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
.calendar__days-box {
  display: flex;
  flex-wrap: wrap;
  margin-top: 20px;
}
.calendar__cell {
  padding: 5px;
}
.calendar__header {
  display: flex;
  justify-content: space-between;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>


<div id="app">
<div class="calendar">
  <div
    class="calendar__wrapper">
    <input
      :value="formattedValue"
      placeholder="The calendar"
      readonly
      type="text"
      class="calendar__input">
    <div
      class="calendar__list"
      @click="showCalendar">
      <p>The calendar</p>
    </div>
    <div v-show="formattedValue" class="calendar__box">
      {{ formattedValue }}
    </div>
    <div v-show="message" class="calendar__box">{{message}}</div>
    <div v-show="isShow">
      <div class="calendar__box">
        <header class="calendar__header">
          <span
            class="calendar__prev"
            @click="previousMonth">
            <span class="calendar__arrow-left"/>
          </span>
          <h3 class='calendar__month'>{{ getMonth | monthName(getMonth) + ' ' + pageDate.getFullYear() }}</h3>
          <span
            class="calendar__next"
            @click="nextMonth">
          </span>
        </header>
        <div class="calendar__days">
          <div
            class="calendar__day"
            v-for="(weekDay, index) in daysOfWeek">
            <p>{{ weekDay }}</p>
          </div>
          <div class="calendar__days-box">
          <!-- add some method for ranges when click on cell -->
            <p 
              v-for="(day, index) in days"
              :key="index"
              @click="setDateRange(day)"
              class="calendar__cell calendar__cell_day">{{ day.date }}</p>
          </div>
        </div>
        <button @click="selectStartEnd">{{step ? 'Select End Date':'Select Start Date'}}</button>
        <button @click="validateDates">Ok</button>
      </div>
    </div>
  </div>
  </div>
</div>

Live demo on codesandbox

Now you can handle this.rangeData or display anywhere.

Upvotes: 1

Related Questions