import type { DropdownItem } from "~/types/dropdown" interface DropdownOptions { placement?: 'right' | 'left' | 'center'; verticality?: 'ascending' | 'descending'; width?: string | number; minWidth?: string; maxWidth?: string; } interface DropdownState { open: boolean; x: number; y: number; itemsFactory: (() => DropdownItem[]) | null; options: DropdownOptions | null; } const dropdownState = reactive({ open: false, x: 0, y: 0, itemsFactory: null, options: null, }) export const useDropdown = () => { const openDropdown = (e: MouseEvent, itemsFactory: () => DropdownItem[], options?: DropdownOptions) => { // TODO: take in placement logic and prefered verticality, and measure the space // available in the direction of the prefered verticality, and if it doesnt fit // in the direction of the placement, flip the placement const rect = (e.target as HTMLElement).getBoundingClientRect() dropdownState.x = rect.left dropdownState.y = rect.bottom if (options) { switch (options.placement) { case 'right': dropdownState.x = rect.right dropdownState.y = rect.bottom break; case 'left': dropdownState.x = rect.left dropdownState.y = rect.bottom break; case 'center': dropdownState.x = rect.left + rect.width / 2 dropdownState.y = rect.bottom break; } switch (options.verticality) { case 'ascending': dropdownState.y = rect.top; break; case 'descending': dropdownState.y = rect.bottom; break; } } dropdownState.itemsFactory = itemsFactory dropdownState.options = options ?? null dropdownState.open = true } const closeDropdown = () => { dropdownState.open = false dropdownState.itemsFactory = null dropdownState.options = null } return { dropdownState, openDropdown, closeDropdown, } }