My experience with PrimeVue led me to discover an issue where clicking a menu on one card allowed you to open the same menu on every card, despite its click-outside-to-close functionality. Usually, clicking outside an open menu should close it. I found a helpful Vue3 directive resource here: Migrating "detect click outside" custom directive from Vue 2 to Vue 3.
The problem in my case using PrimeVue seemed to be related to the logic they internally use, particularly the consistency of el.target
when clicking the same button in different card components. To address this, I implemented a composable that tracks the unique id of the current open menu and added a watcher in each menu-rendering component.
./src/composables/useSingleMenu.js
import { ref } from 'vue';
const lastOpenedId = ref(null);
export const useSingleMenu = () => {
const open = (id) => {
lastOpenedId.value = id;
};
return {
lastOpenedId,
open,
close,
};
};
./src/components/SandwichCard.vue
<script setup>
import { ref, computed, watch } from 'vue';
import { useSingleMenu } from '@/composables/useSingleMenu';
const singleMenu = useSingleMenu();
const menu = ref();
const menuId = computed(() => `sandwich-options-${props.sandwich.id}`);
const toggleMenu = (event) => {
singleMenu.open(menuId.value);
menu.value.toggle(event);
};
watch(() => singleMenu.lastOpenedId.value, () => {
if (singleMenu.lastOpenedId.value !== menuId.value) menu.value.hide();
});
</script>
To implement this solution, simply integrate the provided composable and perform two actions:
- Call
singleMenu.open('some-unique-id-1337');
when opening a menu
- Add a watcher that closes the menu whenever
singleMenu.lastOpenedId.value
changes, if it doesn't match the menu's id
This approach establishes a global state for tracking the currently-opened menu id. Each menu then monitors its unique id against this state, automatically closing if it doesn't match. Consequently, only one menu can be open at any given time, maintaining sync with the active menu id.