UNPKG

@coreui/vue-pro

Version:

UI Components Library for Vue.js

131 lines (108 loc) 3.1 kB
import { ref, computed, watch, onUnmounted } from 'vue' import type { Options, Placement } from '@popperjs/core' import { usePopper } from './usePopper' import { isRTL } from '../utils' export const useDropdownWithPopper = <T extends HTMLElement = never>( popperConfig?: Partial<Options>, ) => { // References to the dropdown elements const dropdownRefElement = ref<T | null>(null) const dropdownMenuElement = ref<HTMLDivElement | null>(null) // Reactive state to control dropdown visibility const isOpen = ref<boolean>(false) // Popper.js integration const { popper, initPopper, destroyPopper } = usePopper() // Computed Popper configuration based on RTL and user-provided settings const _popperConfig = computed(() => ({ placement: (isRTL(dropdownRefElement.value) ? 'bottom-end' : 'bottom-start') as Placement, modifiers: [ { name: 'preventOverflow', options: { boundary: 'clippingParents', }, }, { name: 'offset', options: { offset: [0, 2], }, }, ], ...popperConfig, })) // Methods to control dropdown state const closeDropdown = () => { isOpen.value = false } const openDropdown = () => { isOpen.value = true } const toggleDropdown = () => { isOpen.value = !isOpen.value } const updatePopper = () => { if (popper) { popper.update() } } // Event handlers const handleKeyUp = (event: KeyboardEvent) => { if (event.key === 'Escape') { isOpen.value = false return } if (event.key === 'Tab') { const activeElement = document.activeElement as HTMLElement if ( dropdownRefElement.value?.contains(activeElement) || dropdownMenuElement.value?.contains(activeElement) ) { return } isOpen.value = false } } const handleMouseUp = (event: Event) => { const target = event.target as HTMLElement if (dropdownMenuElement.value?.contains(target) || dropdownRefElement.value?.contains(target)) { return } isOpen.value = false } // Utility functions to manage event listeners const addEventListeners = () => { window.addEventListener('mouseup', handleMouseUp) window.addEventListener('keyup', handleKeyUp) } const removeEventListeners = () => { window.removeEventListener('mouseup', handleMouseUp) window.removeEventListener('keyup', handleKeyUp) } // Watcher to handle side effects when `isOpen` changes watch(isOpen, (open) => { if (open) { addEventListeners() if (dropdownRefElement.value && dropdownMenuElement.value) { initPopper(dropdownRefElement.value, dropdownMenuElement.value, _popperConfig.value) } } else { removeEventListeners() destroyPopper() } }) // Cleanup on component unmount onUnmounted(() => { removeEventListeners() destroyPopper() }) return { dropdownRefElement, dropdownMenuElement, isOpen, closeDropdown, openDropdown, toggleDropdown, updatePopper, } }