Vuepic / vue3-date-time-picker

Datepicker component for Vue 3
https://vue3datepicker.com
MIT License
158 stars 13 forks source link

What are the situations "setMenuPosition" runs or reposition the menu #81

Closed Codefa closed 2 years ago

Codefa commented 2 years ago

First of all thanks for this awesome date time picker in vuejs composition API.

Let's come to my issue I have a carousel with multiple cards each cards have time label at top right, when I click there the date picker opens i can update the date or time that works well, Because of the carousel the last card date picker menu is little bit overflow to the right not visible the select button in viewport i fixed that by calculating the free space to the right side of the browser window and move a bit to the left to adjust the position that too works well, but when I click on any day, click on next,prev Arrow ( month's ) select a tim, then the menu recalculate the position and come back to the real one, how can I fix that ? is your version 2.5 comes as responsive one then that fixes this issue by default ?

Jasenkoo commented 2 years ago

Can you provide some kind of a demo code so I could check the issue?

Codefa commented 2 years ago

Can you provide some kind of a demo code so I could check the issue?

sorry can't share the exact code because it's company project, anyway i will try to make a codesandbox demo based on the issue ( it will take some time sorry for that )

Codefa commented 2 years ago

@Jasenkoo here is the demo Demo please click on the last card ended label ( top right of the card ) ( if something not works open in incognito ) ignore the scrolling below ( not responsive now ) in my case it's responsive can't scroll again, thank you

Jasenkoo commented 2 years ago

Tnx, will be fixed in v2.5

Jasenkoo commented 2 years ago

Investigated your reported issue. So far this is an edge case and I can't provide some uni solution, since it may cause unwanted positioning behavior. In the upcoming version, there will be an option to stack multiple calendars one below another and not in a single row. Or I can also provide an event on position recalculation and you can handle it that way. Would that solve your issue?

I can also make a prop that will auto position based on on-screen position, but it may move depending on the window.

Codefa commented 2 years ago

I understood I have to use the multiple calenders as now I fixed the positioning by this way please check the codes at first I call agendaDpOpen when opening the picker menu

agendaDpOpen() {
      setTimeout(() => {
        this.convertHtmlCollectionsToArray()
      })
    }

from there I have to get the dp__menu element

convertHtmlCollectionsToArray() {
      const shapesHC = document.getElementsByClassName('dp__menu')
      const shapesArrHCSpread = [...shapesHC]
      if (shapesArrHCSpread) {
        this.setDpMenuPosition(shapesArrHCSpread)
      }
    }

if I have the dp__menu element then call setDpMenuPosition to reposition the picker menu (I really don't know how to access the dp__menu by ref )

then on setDpMenuPosition i do this I hope you can understand this block of codes.

setDpMenuPosition(shapesArrHCSpread) {
      let dpMenu = shapesArrHCSpread[0]
      const el = this.$refs && this.$refs.tcstBuilderAgendaDp

      if (el) {
        const { right, width } = el.getBoundingClientRect()
        const fullWidth = window.innerWidth

        const freeSpaceRight = fullWidth - right - width

        if (freeSpaceRight < 0) {
          const dpMenuStyle = dpMenu.style
          dpMenu.children[0].style.left = '90%'
          dpMenuStyle.animation = 'slide-out'
          dpMenuStyle.animationDuration = '0.5s'
          dpMenuStyle.animationDirection = 'forwards'
          dpMenuStyle.left = `${right - 239.4}px`
        }
      }
    }

this method works well on opening the picker menu, but when i click on next & prev month icon, select a time in a time-picker the position comes to old one, if you can alternate this code a little bit then I'm happy i can manage this issue like this for now. if i can prevent reposition menu on other clicks or i can reposition again using this methods that will be helpfull.

Jasenkoo commented 2 years ago

Ok, then I will emit an event when the calendar is repositioning, so you can handle the event @recalculatePosition="convertHtmlCollectionsToArray"

Codefa commented 2 years ago

Ok, then I will emit an event when the calendar is repositioning, so you can handle the event @recalculatePosition="convertHtmlCollectionsToArray"

Yep I hope that will work well, thank you, is there any timeline for the 2.5.0 release ? also i tried to use state driven css ( v-bind css varibles ) unfortunately that won't work inside a teleport ( known issue )

Jasenkoo commented 2 years ago

At the end of the month, next weekend probably

Codefa commented 2 years ago

@Jasenkoo I tired another trick to fix, it works well

Dynamic CSS varible method ( in vue 3 there is a state driven css( v-bind) that won't work here with this picker beacause of the teleport feature )

add a :root varible in the css section (initial left is 0px)

:root {
  --leftPos: 0;
}

inside setDpMenuPosition method add

const r = document.querySelector(':root')

then when there is no free space on the right assign

r.style.setProperty('--leftPos', `${right - 239.4}px`)

also we need a Boolean to know there is free space on right or not ( this is for adding conditional menuClassName this.hasFreeSpaceRight = false hasFreeSpaceRight deafault is true

then we need to add css to do the job

.dp-custom-menu-agenda-time {
  left: var(--leftPos) !important;
  animation: 0.5s forwards slide-out;
}
@keyframes slide-out {
  0% {
    transform: translateX(0%);
  }
  100% {
    transform: translateX(-50%);
  }
}

we can add this css class to date-picker component menuClassName

:menuClassName="!hasFreeSpaceRight ? 'dp-custom-menu-agenda-time' : ''"

Here is the full code of setDpMenuPosition

setDpMenuPosition(shapesArrHCSpread) {
      let dpMenu = shapesArrHCSpread[0]
      const el = this.$refs && this.$refs.tcstBuilderAgendaDp
      const r = document.querySelector(':root')

      if (el) {
        const { right, width } = el.getBoundingClientRect()
        const fullWidth = window.innerWidth
        const freeSpaceRight = fullWidth - right - width

        if (freeSpaceRight < 0) {
          this.hasFreeSpaceRight = false
          dpMenu.children[0].style.left = '90%'
          r.style.setProperty('--leftPos', `${right - 239.4}px`)
        }
      }
    },

this will do the fixes for now maybe usefull for someone who read this later

and I'm also waiting for your recalculatePosition event but maybe if recalculate the Position everytime there will be a little shaking for the menu i think, hope we can fix that with css ! ( please check this too )

Jasenkoo commented 2 years ago

Will do, thanks 😄

Jasenkoo commented 2 years ago

Included in the v2.5.0

Codefa commented 2 years ago

@Jasenkoo I tried the new version sadly the issue is not resolved the recalculation event is not emiting on

Jasenkoo commented 2 years ago

@Codefa Ok, then the problem is not in the recalculatePosition, this might be happening because the CSS alters the position.

I've added an option on altPosition prop to pass a custom positioning function. You can use that instead of the setDpMenuPosition that you tried, make sure to return { top: string; left: string; } from that function and you can position the menu whenver. Also, if the recalculation method is called, it will call that function again, but since you don't get anything on the event, that is not a problem.