refactor: use floating ui for dropdowns
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
import type { Ref } from 'vue';
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
|
||||
export const useClickOutside = (target: Ref<HTMLElement | null>, callback: () => void) => {
|
||||
const onClick = (event: MouseEvent) => {
|
||||
if (target.value && !target.value.contains(event.target as Node)) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', onClick);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', onClick);
|
||||
});
|
||||
};
|
||||
@@ -1,77 +0,0 @@
|
||||
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<DropdownState>({
|
||||
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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user