diff --git a/app/javascript/dashboard/components-next/CustomTeleport.vue b/app/javascript/dashboard/components-next/CustomTeleport.vue
deleted file mode 100644
index 3e9009f08..000000000
--- a/app/javascript/dashboard/components-next/CustomTeleport.vue
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/app/javascript/dashboard/components-next/message/Message.vue b/app/javascript/dashboard/components-next/message/Message.vue
index 4b4353cea..1393e262f 100644
--- a/app/javascript/dashboard/components-next/message/Message.vue
+++ b/app/javascript/dashboard/components-next/message/Message.vue
@@ -1,12 +1,5 @@
@@ -47,7 +84,7 @@ onMounted(() => {
class="fixed outline-none z-[9999] cursor-pointer"
:style="position"
tabindex="0"
- @blur="emit('close')"
+ @blur="handleClose"
>
diff --git a/app/javascript/dashboard/helper/position.js b/app/javascript/dashboard/helper/position.js
deleted file mode 100644
index 14e614ede..000000000
--- a/app/javascript/dashboard/helper/position.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Calculates the optimal position for a popup element to ensure it remains within viewport bounds
- *
- * @param {number} x - The initial x-coordinate (left position) for the element
- * @param {number} y - The initial y-coordinate (top position) for the element
- * @param {number} menuW - The width of the element to position
- * @param {number} menuH - The height of the element to position
- * @param {number} windowW - The width of the viewport
- * @param {number} windowH - The height of the viewport
- * @param {number} [padding=16] - Minimum padding from viewport edges (in pixels)
- * @returns {Object} An object containing the adjusted { left, top } coordinates
- */
-export const calculatePosition = (
- x,
- y,
- menuW,
- menuH,
- windowW,
- windowH,
- 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),
- };
-};
diff --git a/app/javascript/dashboard/helper/specs/position.spec.js b/app/javascript/dashboard/helper/specs/position.spec.js
deleted file mode 100644
index 15fa7a65e..000000000
--- a/app/javascript/dashboard/helper/specs/position.spec.js
+++ /dev/null
@@ -1,183 +0,0 @@
-import { calculatePosition } from '../position';
-
-describe('calculatePosition', () => {
- const WINDOW_WIDTH = 1024;
- const WINDOW_HEIGHT = 768;
- const MENU_WIDTH = 400;
- const MENU_HEIGHT = 300;
- const DEFAULT_PADDING = 16;
-
- test('returns the original position when element fits within viewport', () => {
- const x = 100;
- const y = 100;
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- expect(result).toEqual({ left: x, top: y });
- });
-
- test('adjusts position when overflowing right edge', () => {
- const x = WINDOW_WIDTH - 100; // Too far right
- const y = 100;
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- const expectedLeft = WINDOW_WIDTH - MENU_WIDTH - DEFAULT_PADDING;
- expect(result).toEqual({ left: expectedLeft, top: y });
- });
-
- test('adjusts position when overflowing bottom edge', () => {
- const x = 100;
- const y = WINDOW_HEIGHT - 100; // Too far down
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- const expectedTop = WINDOW_HEIGHT - MENU_HEIGHT - DEFAULT_PADDING;
- expect(result).toEqual({ left: x, top: expectedTop });
- });
-
- test('adjusts position when overflowing both right and bottom edges', () => {
- const x = WINDOW_WIDTH - 100; // Too far right
- const y = WINDOW_HEIGHT - 100; // Too far down
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- const expectedLeft = WINDOW_WIDTH - MENU_WIDTH - DEFAULT_PADDING;
- const expectedTop = WINDOW_HEIGHT - MENU_HEIGHT - DEFAULT_PADDING;
- expect(result).toEqual({ left: expectedLeft, top: expectedTop });
- });
-
- test('ensures minimum padding from left edge', () => {
- const x = -50; // Position would be off-screen to the left
- const y = 100;
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- expect(result).toEqual({ left: DEFAULT_PADDING, top: y });
- });
-
- test('ensures minimum padding from top edge', () => {
- const x = 100;
- const y = -50; // Position would be off-screen at the top
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- expect(result).toEqual({ left: x, top: DEFAULT_PADDING });
- });
-
- test('handles case when element is larger than viewport width', () => {
- const x = 100;
- const y = 100;
- const largeWidth = WINDOW_WIDTH + 200; // Wider than window
-
- const result = calculatePosition(
- x,
- y,
- largeWidth,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- // Should position at minimum padding
- expect(result).toEqual({ left: DEFAULT_PADDING, top: y });
- });
-
- test('handles case when element is larger than viewport height', () => {
- const x = 100;
- const y = 100;
- const largeHeight = WINDOW_HEIGHT + 200; // Taller than window
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- largeHeight,
- WINDOW_WIDTH,
- WINDOW_HEIGHT
- );
-
- // Should position at minimum padding
- expect(result).toEqual({ left: x, top: DEFAULT_PADDING });
- });
-
- test('accepts custom padding value', () => {
- const x = WINDOW_WIDTH - 100;
- const y = WINDOW_HEIGHT - 100;
- const customPadding = 32;
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT,
- customPadding
- );
-
- const expectedLeft = WINDOW_WIDTH - MENU_WIDTH - customPadding;
- const expectedTop = WINDOW_HEIGHT - MENU_HEIGHT - customPadding;
- expect(result).toEqual({ left: expectedLeft, top: expectedTop });
- });
-
- test('ensures custom minimum padding from edges', () => {
- const x = -50;
- const y = -50;
- const customPadding = 32;
-
- const result = calculatePosition(
- x,
- y,
- MENU_WIDTH,
- MENU_HEIGHT,
- WINDOW_WIDTH,
- WINDOW_HEIGHT,
- customPadding
- );
-
- expect(result).toEqual({ left: customPadding, top: customPadding });
- });
-});
diff --git a/app/javascript/dashboard/modules/conversations/components/MessageContextMenu.vue b/app/javascript/dashboard/modules/conversations/components/MessageContextMenu.vue
index 6688cd18b..6fafd15af 100644
--- a/app/javascript/dashboard/modules/conversations/components/MessageContextMenu.vue
+++ b/app/javascript/dashboard/modules/conversations/components/MessageContextMenu.vue
@@ -62,6 +62,9 @@ export default {
getAccount: 'accounts/getAccount',
currentAccountId: 'getCurrentAccountId',
}),
+ conversationPanelElement() {
+ return document.querySelector('.conversation-panel');
+ },
plainTextContent() {
return this.getPlainText(this.messageContent);
},
@@ -197,6 +200,7 @@ export default {
v-if="isOpen && !isCannedResponseModalOpen"
:x="contextMenuPosition.x"
:y="contextMenuPosition.y"
+ :lock-scroll-element="conversationPanelElement"
@close="handleClose"
>