chore: Replace message menu with context menu

This commit is contained in:
iamsivin
2025-05-08 16:52:18 +05:30
parent 2856818931
commit cffd74fda3
11 changed files with 343 additions and 152 deletions
@@ -0,0 +1,38 @@
/**
* 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),
};
};
@@ -0,0 +1,183 @@
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 });
});
});