Files
chatwoot/app/javascript/dashboard/composables/useDropdownPosition.js
T

64 lines
2.0 KiB
JavaScript

import { computed } from 'vue';
import { useElementBounding, useWindowSize } from '@vueuse/core';
/**
* Auto-position dropdown based on available space
* @param {Ref} triggerRef - Trigger element ref
* @param {Ref} dropdownRef - Dropdown element ref
* @param {Ref} enabled - Whether to calculate position
* @returns {Object} { positionClasses, updatePosition }
*/
export function useDropdownPosition(triggerRef, dropdownRef, enabled) {
const SAFE_MARGIN = 16;
const RTL = 'rtl';
const triggerBounds = useElementBounding(triggerRef);
const dropdownBounds = useElementBounding(dropdownRef);
const { height: windowHeight, width: windowWidth } = useWindowSize();
const positionClasses = computed(() => {
// Don't calculate if not enabled
if (!enabled?.value) {
return 'top-full mt-2 ltr:left-0 rtl:right-0';
}
if (!triggerRef.value || !dropdownRef.value) {
return 'top-full mt-2 ltr:left-0 rtl:right-0';
}
const spaceBelow = windowHeight.value - triggerBounds.bottom.value;
const dropdownHeight = dropdownBounds.height.value || 200;
const dropdownWidth = dropdownBounds.width.value || 200;
// Check if RTL by checking app div with dir attribute
const appDiv = document.querySelector('#app[dir]');
const isRTL = appDiv?.getAttribute('dir') === RTL;
// Use appropriate edge based on text direction
const triggerEdge = isRTL
? triggerBounds.right.value
: triggerBounds.left.value;
const wouldOverflow = isRTL
? triggerEdge - dropdownWidth - SAFE_MARGIN < 0
: triggerEdge + dropdownWidth + SAFE_MARGIN > windowWidth.value;
const vertical =
spaceBelow >= dropdownHeight + SAFE_MARGIN
? 'top-full mt-2'
: 'bottom-full mb-2';
const horizontal = wouldOverflow
? 'ltr:right-0 rtl:left-0'
: 'ltr:left-0 rtl:right-0';
return `${vertical} ${horizontal}`;
});
const updatePosition = () => {
triggerBounds.update();
dropdownBounds.update();
};
return { positionClasses, updatePosition };
}