diff --git a/src/components/input/CronInput.vue b/src/components/input/CronInput.vue index 77e11cb4..461f0559 100644 --- a/src/components/input/CronInput.vue +++ b/src/components/input/CronInput.vue @@ -8,7 +8,44 @@ const props = defineProps({ const emit = defineEmits(['update:modelValue']) +const menu = ref(false) const currentCron = ref(props.modelValue) +const menuRoot = ref() +const instance = getCurrentInstance() +const menuContentClass = `cron-input-menu-${instance?.uid ?? 'default'}` +const menuContentSelector = `.${menuContentClass}` + +function isCronMenuTarget(target: EventTarget | null) { + if (!(target instanceof Element)) return false + + if (menuRoot.value?.contains(target)) return true + + const menuContent = document.querySelector(menuContentSelector) + + if (menuContent?.contains(target)) return true + + const overlayId = target.closest('.v-overlay')?.getAttribute('id') + + if (!overlayId || !menuContent) return false + + return Array.from(menuContent.querySelectorAll('[aria-owns]')).some( + activator => activator.getAttribute('aria-owns') === overlayId, + ) +} + +function closeOnOutsidePointerDown(event: PointerEvent) { + if (!menu.value || isCronMenuTarget(event.target)) return + + menu.value = false +} + +onMounted(() => { + document.addEventListener('pointerdown', closeOnOutsidePointerDown, true) +}) + +onBeforeUnmount(() => { + document.removeEventListener('pointerdown', closeOnOutsidePointerDown, true) +}) watch(currentCron, newVal => { emit('update:modelValue', newVal) @@ -23,8 +60,13 @@ watch(