I am using Vue3, Pinia and the floating-vue library.
I am trying to dynamically create a popup at a position which depends on other elements that may move in the viewport. For this I place a dummy div popup-anchor
to which my Dropdown Popup is attached.
When I click a specific item, to which I want to attach the popup to, the item is saved in the pinia store.
Whenever the item in the store changes I call setPosition()
and show()
for this Vue Component.
selectedItemCoords (newValue, oldValue) {
if (this.$refs.infoPopup) {
if (newValue) {
const pixelCoords = this.getPixelPositionOfItem()
this.$refs.infoPopup.setPosition(pixelCoords)
this.$refs.infoPopup.show();
} else {
this.$refs.infoPopup.close();
}
} else {
console.log("Info Popupvalue not found")
}
}
This works well, if there is a delay between updates of the item in the store (i.e. the item is null in between) and I click somewhere else on the screen. However, if I click on one item after another, the popup closes and does not reopen, unless I click something which is not an item and the store value is set to null.
However, I want to popup to show up whenever showPopup=true
, i.e. if the selected items changes, I just want to move the popup and update its contents.
Below is the Vue Component, which holds the dummy anchor and the floating-vue dropdown.
<template>
<div
id="popup-anchor"
ref="anchor"
class="w-1 h-1 "
:style="{ position: 'absolute'}"
>
<Dropdown
:shown="showPopup"
:distance="1"
:triggers="[]"
:popper-triggers="[]"
>
<template #popper>
<div
v-if="item"
:item="item"
@share="share"
>
item.text
</div>
<div
v-else
class="loading-text"
>
Loading...
</div>
</template>
</Dropdown>
</div>
</template>
<script>
import { ref, computed } from 'vue';
import {Dropdown, hideAllPoppers, recomputeAllPoppers} from 'floating-vue';
import { useStore} from "@/store/store";
export default {
components: {
Dropdown,
},
props: {
position: {
type: Object,
required: true,
default: () => ({ top: '0px', left: '0px' }),
},
},
setup(props, { expose }) {
const anchor = ref(null);
const showPopup = ref(false);
const storage = useStore();
const item = computed(() => storage.item);
// Method to toggle the visibility of the InfoPopup
const show = () => {
showPopup.value = true
console.log('show', showPopup.value)
};
const close = () => {
showPopup.value = false
hideAllPoppers();
console.log('close', showPopup.value)
};
const setPosition = (pixelCoords) => {
anchor.value.style.top = `${pixelCoords[1]}px`;
anchor.value.style.left =`${pixelCoords[0]}px`;
recomputeAllPoppers();
};
const share = () => {
console.log('share')
};
expose({ anchor, show, close , setPosition});
return {
anchor,
show,
close,
share,
showPopup,
item,
};
},
};
</script>
After extensive debugging I found that the problem was not the position of the anchoring div, but that the popup needs to have time to close and reopen. So setting showPopup = false
and then to showPopup = true
in the same tick, does in fact hide the popup, but not reopen it. The solution is to introduce a delay between both actions.
const show = () => {
if (showPopup.value = true) {
// if the popup is already showing we have to close it
// and reopen it in the next tick so it can update the position
close();
nextTick(() => {
showPopup.value = true
});
} else {
showPopup.value = true
}
};