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

Close nested VDropdown without closing parent programmatically #874

Open
kduvignau opened this issue Jul 1, 2022 · 10 comments
Open

Close nested VDropdown without closing parent programmatically #874

kduvignau opened this issue Jul 1, 2022 · 10 comments

Comments

@kduvignau
Copy link

kduvignau commented Jul 1, 2022

Hello! I would like to close a nested VDropdown inside another VDropdown.

I tried with the v-close-popper directive on the div node and it works. However, I would like to do some processing (async calls for example) before closing the popper so I can't use the directive as-is.

I also tried to use the hide method exposed by the #popper slot, I got the same result.

I also don't want to use :auto-hide="false" on the parent VDropdown because I want to autoclose when the click is outside

Here is a minimal example, when I click on Hello, the nested AND the parent VDropdown close.

<template>
  <div class="example flex justify-center items-center gap-6">
    <VDropdown placement="bottom-start">
      <button class="border border-gray-300 rounded px-4 py-2">Click me</button>

      <template #popper>
        <VDropdown v-model:shown="open" placement="right-start">
          <button class="rounded hover:bg-green-100 px-4 py-2">Option ></button>

          <template #popper>
            <div class="px-6 py-2" @click="onClick">Hello</div>
          </template>
        </VDropdown>
      </template>
    </VDropdown>
  </div>
</template>

<script setup>
import { ref } from "vue";
const open = ref(false);

function onClick() {
  // Some processing ...
  open.value = false;
}
</script>

@or2e
Copy link

or2e commented Jun 2, 2023

bump

Reproduction: https://stackblitz.com/edit/vitejs-vite-t58z89?file=src%2FApp.vue

Expected behavior: only the closest dropdown is closed when calling the hide method
Current behavior: the all dropdown hierarchy closes

@zhaivoronok
Copy link

Same here, really need to fix it!

@or2e
Copy link

or2e commented Sep 16, 2023

zhaivoronok, kduvignau
Very dirty hack, nothing seems to be broken (but it's not for sure)

https://github.com/Akryum/floating-vue/blob/main/packages/floating-vue/src/components/Popper.ts#L454

if (this.shownChildren.size > 0) {
-   this.$_pendingHide = true
    return
}

https://github.com/Akryum/floating-vue/blob/main/packages/floating-vue/src/components/Popper.ts#L477

- this.$_scheduleHide(event, skipDelay)
+ setTimeout(() => {
+  this.$_scheduleHide(event, skipDelay)
+ }, 0);

@kduvignau
Copy link
Author

Thanks for providing your solution @or2e !

FYI, there is an official package for floating ui for Vue, you can find out here :
https://www.npmjs.com/package/@floating-ui/vue

@ishaiavrahami
Copy link

ishaiavrahami commented Dec 6, 2023

Hi was this fixed? @zhaivoronok @kduvignau @or2e

@RAIbrahim360
Copy link

Solution:

<template>
  <Dropdown :shown="open" @show="onShow" @hide="onHide">
    <slot></slot>
    <div slot="popper"></div>
  </Dropdown>
</template>

<script>
import { Dropdown } from 'v-tooltip'

export default {
  name: 'Popover',
  components: {
    Dropdown,
  },
  data() {
    return {
      autoHide_: this.autoHide,
      isOpened: false,
    }
  },
  watch: {
    autoHide(newValue) {
      this.autoHide_ = newValue
    },
  },
  methods: {
    setParentAutoHideToFalse() {
      let parent = this.$parent
      while (parent) {
        if (parent.$el.classList.contains('v-popper') && parent.isOpened) {
          parent.autoHide_ = false
          if (parent.$el.parentNode === document.body) {
            break
          }
        }
        parent = parent.$parent
      }
    },
    setParentAutoHideToTrue() {
      let parent = this.$parent
      while (parent) {
        if (parent.$el.classList.contains('v-popper') && parent.isOpened) {
          parent.autoHide_ = true
          break
        }
        parent = parent.$parent
      }
    },
    onShow() {
      this.isOpened = true
      setTimeout(() => this.setParentAutoHideToFalse(), 200)
    },
    onHide() {
      this.isOpened = false
      setTimeout(() => this.setParentAutoHideToTrue(), 100)
    },
  },
}
</script>

@or2e
Copy link

or2e commented Jan 30, 2024

@vkolova
Copy link

vkolova commented Feb 16, 2024

This is still not resolved after two years?

@bendemartin97
Copy link

Any updates here?

@RAIbrahim360
Copy link

RAIbrahim360 commented Oct 18, 2024

vue 3 composition api:

const currentInstance = getCurrentInstance()

const $autoHide = ref()
const setParentAutoHide = (bool) => {
  let parent = currentInstance.parent
  while (parent) {
    const el = parent.proxy?.$el
    if (el?.classList?.contains('v-popper') && parent.exposed?.isOpen) {
      parent.exposed.$autoHide.value = bool
      if (el.parentNode === document.body) {
        break
      }
    }
    parent = parent.parent
  }
}

const onShow = () => {
  setTimeout(() => setParentAutoHide(false), 200)
}
const onHide = () => {
  setTimeout(() => setParentAutoHide(true), 100)
}

defineExpose({
  $autoHide,
})

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

Successfully merging a pull request may close this issue.

7 participants