mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	 9bd658137a
			
		
	
	9bd658137a
	
	
	
		
			
			This PR uses `useScrollLock` from `VueUse` to lock scrolling on the conversation panel when the message context menu is open.
		
			
				
	
	
		
			98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <script setup>
 | |
| import {
 | |
|   computed,
 | |
|   onMounted,
 | |
|   nextTick,
 | |
|   onUnmounted,
 | |
|   useTemplateRef,
 | |
|   inject,
 | |
| } from 'vue';
 | |
| import { useWindowSize, useElementBounding, useScrollLock } from '@vueuse/core';
 | |
| 
 | |
| import TeleportWithDirection from 'dashboard/components-next/TeleportWithDirection.vue';
 | |
| 
 | |
| const props = defineProps({
 | |
|   x: { type: Number, default: 0 },
 | |
|   y: { type: Number, default: 0 },
 | |
| });
 | |
| 
 | |
| const emit = defineEmits(['close']);
 | |
| 
 | |
| const elementToLock = inject('contextMenuElementTarget', null);
 | |
| 
 | |
| const menuRef = useTemplateRef('menuRef');
 | |
| 
 | |
| const scrollLockElement = computed(() => {
 | |
|   if (!elementToLock?.value) return null;
 | |
|   return elementToLock.value?.$el;
 | |
| });
 | |
| 
 | |
| const isLocked = useScrollLock(scrollLockElement);
 | |
| 
 | |
| const { width: windowWidth, height: windowHeight } = useWindowSize();
 | |
| const { width: menuWidth, height: menuHeight } = useElementBounding(menuRef);
 | |
| 
 | |
| const calculatePosition = (x, y, menuW, menuH, windowW, windowH) => {
 | |
|   const PADDING = 16;
 | |
|   // Initial position
 | |
|   let left = x;
 | |
|   let top = y;
 | |
|   // Boundary checks
 | |
|   const isOverflowingRight = left + menuW > windowW - PADDING;
 | |
|   const isOverflowingBottom = top + menuH > windowH - PADDING;
 | |
|   // Adjust position if overflowing
 | |
|   if (isOverflowingRight) left = windowW - menuW - PADDING;
 | |
|   if (isOverflowingBottom) top = windowH - menuH - PADDING;
 | |
|   return {
 | |
|     left: Math.max(PADDING, left),
 | |
|     top: Math.max(PADDING, top),
 | |
|   };
 | |
| };
 | |
| 
 | |
| const position = computed(() => {
 | |
|   if (!menuRef.value) return { top: `${props.y}px`, left: `${props.x}px` };
 | |
| 
 | |
|   const { left, top } = calculatePosition(
 | |
|     props.x,
 | |
|     props.y,
 | |
|     menuWidth.value,
 | |
|     menuHeight.value,
 | |
|     windowWidth.value,
 | |
|     windowHeight.value
 | |
|   );
 | |
| 
 | |
|   return {
 | |
|     top: `${top}px`,
 | |
|     left: `${left}px`,
 | |
|   };
 | |
| });
 | |
| 
 | |
| onMounted(() => {
 | |
|   isLocked.value = true;
 | |
|   nextTick(() => menuRef.value?.focus());
 | |
| });
 | |
| 
 | |
| const handleClose = () => {
 | |
|   isLocked.value = false;
 | |
|   emit('close');
 | |
| };
 | |
| 
 | |
| onUnmounted(() => {
 | |
|   isLocked.value = false;
 | |
| });
 | |
| </script>
 | |
| 
 | |
| <template>
 | |
|   <TeleportWithDirection to="body">
 | |
|     <div
 | |
|       ref="menuRef"
 | |
|       class="fixed outline-none z-[9999] cursor-pointer"
 | |
|       :style="position"
 | |
|       tabindex="0"
 | |
|       @blur="handleClose"
 | |
|     >
 | |
|       <slot />
 | |
|     </div>
 | |
|   </TeleportWithDirection>
 | |
| </template>
 |