kenhyuwa / litepie-datepicker

Litepie Datepicker is a date range picker component for Vue.js and Tailwind CSS, dependent to day.js.
https://litepie.com
MIT License
373 stars 75 forks source link

Start date is not reactive #31

Open MariusSpring opened 3 years ago

MariusSpring commented 3 years ago

Hello!

I have a computed property for "startDate". I expect when this computed propety changes, then the datepicker will react to that and change the start position also. At the moment it only takes into account the first computed value, and not updated ones.

kenhyuwa commented 3 years ago

hey, computed by default is getters only, you can use setter or using watcher

MariusSpring commented 3 years ago

Hello, thanks for reply, could you maybe provide an example? @kenhyuwa

When my calendar first loads, it's start date is today (correct), however, after some async operation, I want to change the start date to some other date.

Code so far:

computed: {
  /**
   * The available dates for the product.
   */
  dates() {
    return this.calendar === null ? [] : Object.keys(this.calendar);
  },
  /**
   * The start date.
   */
  startDate() {
    console.log(this.dates[0] || new Date().toString());
    return this.dates[0] || new Date().toString();
  },
},
kenhyuwa commented 3 years ago
methods: {
 someOperation(){
  this.calendar = [] // asigne with new date
 }
}
MariusSpring commented 3 years ago

Hello, yes, I use this (see code below). However, when the calendar updates, and the computed property "startDate" updates, it is not visually shown in the LitepieDatepicker component. It still shows today as startDate despite it now being equal to the first date in the calendar array.

/**
 * Called when the component mounts.
 */
async mounted() {
  this.loading = true;
  this.calendar = await fetchCalendar(this.product.id);
  this.loading = false;

  console.log('loaded calendar for product ' + `${this.product.id} (${this.product.type})`, this.calendar);
},

console:

old startDate: Tue Jul 06 2021 11:23:25 GMT+0200 (sentraleuropeisk sommertid)
loaded calendar for product 12 (Do) 
new startDate: 2021-12-07
kenhyuwa commented 3 years ago

are you value of calendar state match with your formatter?

MariusSpring commented 3 years ago

Maybe not? I had to do a workaround for it to display the format as I want. Not sure if I bugged it? It does however should the date as I want.

<template>
  <LitepieDatepicker
    v-bind="pickerAttrs"
    :overlay="true"
    :shortcuts="false"
    :placeholder="placeholder"
    :formatter="formatter"
    :separator="separator"
    :start-from="startFrom"
    :disable-date="disableDate"
    :model-value="modelValue"
    @update:model-value="(value) => $emit('update:modelValue', value)"
  >
    <div class="flex items-center justify-between px-3 py-3 rounded cursor-pointer gap-x-4" :class="{ 'opacity-75 pointer-events-none': loading }" v-bind="containerAttrs">

      <!-- Label -->
      <div class="flex items-center gap-x-3">
        <font-awesome-icon
          class="text-lg text-current"
          :icon="[ 'far', 'calendar-alt' ]"
        />
        <span>
          {{ displayValue || placeholder }}
        </span>
      </div>

      <!-- Loading -->
      <spinner class="w-5 h-5" v-if="loading" />

    </div>
  </LitepieDatepicker>
</template>

Computed:

/**
 * The display value.
 */
displayValue() {
  // Get date format and seperator.
  const format    = 'll';
  const separator = this.separator;

  // Format value.
  if (Array.isArray(this.modelValue)) {
    return this.modelValue.map((d) => this.$moment(d).format(format)).join(separator);
  } else {
    return this.$moment(this.modelValue).format(format);
  }
},
MariusSpring commented 3 years ago

By the way, @kenhyuwa , I am talking about changing the month outlined in red in the picture below to the same month as the first date in my calendar array. For example, if the first date is in december, when I open the date picker, it should show the december month without me having to scroll there to see the calendar dates. bilde

MariusSpring commented 3 years ago

Is there no way we could bind to these values so we can update them programatically?

    const panel = reactive({
      previous: {
        calendar: true,
        month: false,
        year: false
      },
      next: {
        calendar: true,
        month: false,
        year: false
      }
    });
kenhyuwa commented 3 years ago

By the way, @kenhyuwa , I am talking about changing the month outlined in red in the picture below to the same month as the first date in my calendar array. For example, if the first date is in december, when I open the date picker, it should show the december month without me having to scroll there to see the calendar dates. bilde

the panel is binding from dateValue with formatter state. please make sure the state of value is match with formater

MariusSpring commented 3 years ago

The issue is that I want the value to be in ISO format (to match server API), but the value actually displayed to be "ll" (another format). This is pretty critical. @kenhyuwa

Relevant?: https://github.com/kenhyuwa/litepie-datepicker/issues/11

This is why I am computing the displayValue to handle this myself. This way, the formatter should match the modelValue.

kenhyuwa commented 3 years ago

yeah i know, for now you can format the response API with formatter matching before initialize the date. I think, I resolve for next release. thank you

MariusSpring commented 3 years ago

So there is no way I can change the startFrom after it has already been set, and have it visually shown in the date picker? @kenhyuwa

afunnydev commented 3 years ago

@MariusTechweb If I may help, it looks like your initial startFrom and the async startFrom are maybe not in the same format. From your example, old = "Tue Jul 06 2021 11:23:25 GMT+0200" and new = "2021-12-07".

From the source code, looks like startFrom doesn't use any formatter and should then be a Date object or ISO 8601 string (as per day.js parse recommandations without formatter). You should format this.dates[0] appropriately for it to get picked up, as 2021-12-07 is not ISO 8601. Hope this helps!

MariusSpring commented 3 years ago

@afunnydev Hello and thank you, however, it seems like your provided solution does not work.

/**
 * The start date (computed property).
 */
startDate() {
  console.log(this.$moment(this.dates[0] || new Date()).toDate().toString());

  return this.$moment(this.dates[0] || new Date()).toDate();
},

The start date (startFrom in the date picker) is still always today, regardless if this.dates[0] changes and becomes present as 10th of December.

Console:

10:05:12.055 Tue Aug 10 2021 10:05:12 GMT+0200 (sentraleuropeisk sommertid) 
// calendar loaded here, startDate changes to below value.
10:05:13.158 Fri Dec 10 2021 00:00:00 GMT+0100 (sentraleuropeisk normaltid)

Update: Keying (:key) the date picker with startDate seemed to work and force the calendar to update, but should that be neccecary? Shouldn't the DatePicker notice that startDate has changed, and re-render itself.?

santiagopoveda commented 2 years ago

I can also reproduce this issue, and you can even see it live on the main page: https://litepie.com/#as-single-use-range.

Type a valid date, and the date picker will close. Even though the date is actually set, the UI doesn't update.

Using @afunnydev's suggestion works, and while the UI does update when re-opened, an error is thrown when you set the date (at least when using a single date):

TypeError: Cannot read properties of null (reading 'getBoundingClientRect') at useVisibleViewport