diff --git a/web/src/components/RelativeModal.jsx b/web/src/components/RelativeModal.jsx
index 644a0b875..2398f643f 100644
--- a/web/src/components/RelativeModal.jsx
+++ b/web/src/components/RelativeModal.jsx
@@ -2,7 +2,7 @@ import { h, Fragment } from 'preact';
import { createPortal } from 'preact/compat';
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
-const WINDOW_PADDING = 10;
+const WINDOW_PADDING = 20;
export default function RelativeModal({
className,
@@ -13,7 +13,7 @@ export default function RelativeModal({
relativeTo,
widthRelative = false,
}) {
- const [position, setPosition] = useState({ top: -999, left: -999 });
+ const [position, setPosition] = useState({ top: -9999, left: -9999 });
const [show, setShow] = useState(false);
const portalRoot = portalRootID && document.getElementById(portalRootID);
const ref = useRef(null);
@@ -53,33 +53,43 @@ export default function RelativeModal({
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const { width: menuWidth, height: menuHeight } = ref.current.getBoundingClientRect();
- const { x, y, width: relativeWidth, height } = relativeTo.current.getBoundingClientRect();
+ const {
+ x: relativeToX,
+ y: relativeToY,
+ width: relativeToWidth,
+ // height: relativeToHeight,
+ } = relativeTo.current.getBoundingClientRect();
- const width = widthRelative ? relativeWidth : menuWidth;
+ const _width = widthRelative ? relativeToWidth : menuWidth;
+ const width = _width * 1.1;
+
+ const left = relativeToX + window.scrollX;
+ const top = relativeToY + window.scrollY;
+
+ let newTop = top;
+ let newLeft = left;
- let top = y + height;
- let left = x;
// too far right
- if (left + width >= windowWidth - WINDOW_PADDING) {
- left = windowWidth - width - WINDOW_PADDING;
+ if (newLeft + width + WINDOW_PADDING >= windowWidth - WINDOW_PADDING) {
+ newLeft = windowWidth - width - WINDOW_PADDING;
}
// too far left
else if (left < WINDOW_PADDING) {
- left = WINDOW_PADDING;
+ newLeft = WINDOW_PADDING;
}
// too close to bottom
- if (top + menuHeight > windowHeight - WINDOW_PADDING) {
- top = y - menuHeight;
+ if (top + menuHeight > windowHeight - WINDOW_PADDING + window.scrollY) {
+ newTop = relativeToY - menuHeight;
}
- if (top <= WINDOW_PADDING) {
- top = WINDOW_PADDING;
+ if (top <= WINDOW_PADDING + window.scrollY) {
+ newTop = WINDOW_PADDING;
}
const maxHeight = windowHeight - WINDOW_PADDING * 2 > menuHeight ? null : windowHeight - WINDOW_PADDING * 2;
- const newPosition = { left: left + window.scrollX, top: top + window.scrollY, maxHeight };
+ const newPosition = { left: newLeft, top: newTop, maxHeight };
if (widthRelative) {
- newPosition.width = relativeWidth;
+ newPosition.width = relativeToWidth;
}
setPosition(newPosition);
const focusable = ref.current.querySelector('[tabindex]');
@@ -89,7 +99,9 @@ export default function RelativeModal({
useEffect(() => {
if (position.top >= 0) {
- setShow(true);
+ window.requestAnimationFrame(() => {
+ setShow(true);
+ });
} else {
setShow(false);
}
@@ -100,13 +112,13 @@ export default function RelativeModal({
= 0 ? position : null}
+ style={position}
>
{children}
diff --git a/web/src/components/Tooltip.jsx b/web/src/components/Tooltip.jsx
index b0faadaef..034623af2 100644
--- a/web/src/components/Tooltip.jsx
+++ b/web/src/components/Tooltip.jsx
@@ -1,15 +1,15 @@
import { h } from 'preact';
import { createPortal } from 'preact/compat';
-import { useEffect, useRef, useState } from 'preact/hooks';
+import { useLayoutEffect, useRef, useState } from 'preact/hooks';
const TIP_SPACE = 20;
export default function Tooltip({ relativeTo, text }) {
- const [position, setPosition] = useState({ top: -Infinity, left: -Infinity });
+ const [position, setPosition] = useState({ top: -9999, left: -9999 });
const portalRoot = document.getElementById('tooltips');
const ref = useRef();
- useEffect(() => {
+ useLayoutEffect(() => {
if (ref && ref.current && relativeTo && relativeTo.current) {
const windowWidth = window.innerWidth;
const {
@@ -18,7 +18,9 @@ export default function Tooltip({ relativeTo, text }) {
width: relativeToWidth,
height: relativeToHeight,
} = relativeTo.current.getBoundingClientRect();
- const { width: tipWidth, height: tipHeight } = ref.current.getBoundingClientRect();
+ const { width: _tipWidth, height: _tipHeight } = ref.current.getBoundingClientRect();
+ const tipWidth = _tipWidth * 1.1;
+ const tipHeight = _tipHeight * 1.1;
const left = relativeToX + Math.round(relativeToWidth / 2) + window.scrollX;
const top = relativeToY + Math.round(relativeToHeight / 2) + window.scrollY;
@@ -47,11 +49,11 @@ export default function Tooltip({ relativeTo, text }) {
const tooltip = (
= 0 ? 'opacity-100' : ''
+ className={`shadow max-w-lg absolute pointer-events-none bg-gray-900 dark:bg-gray-200 bg-opacity-80 rounded px-2 py-1 transition-transform transition-opacity duration-75 transform scale-90 opacity-0 text-gray-100 dark:text-gray-900 text-sm ${
+ position.top >= 0 ? 'opacity-100 scale-100' : ''
}`}
ref={ref}
- style={position.top >= 0 ? position : null}
+ style={position}
>
{text}
diff --git a/web/src/components/__tests__/Toolltip.test.jsx b/web/src/components/__tests__/Toolltip.test.jsx
index d2d8e0c63..d538f6865 100644
--- a/web/src/components/__tests__/Toolltip.test.jsx
+++ b/web/src/components/__tests__/Toolltip.test.jsx
@@ -26,8 +26,8 @@ describe('Tooltip', () => {
const tooltip = await screen.findByRole('tooltip');
const style = window.getComputedStyle(tooltip);
- expect(style.left).toEqual('105px');
- expect(style.top).toEqual('70px');
+ expect(style.left).toEqual('103px');
+ expect(style.top).toEqual('68.5px');
});
test('if too far right, renders to the left', async () => {
@@ -54,7 +54,7 @@ describe('Tooltip', () => {
const tooltip = await screen.findByRole('tooltip');
const style = window.getComputedStyle(tooltip);
- expect(style.left).toEqual('942px');
+ expect(style.left).toEqual('937px');
expect(style.top).toEqual('97px');
});
@@ -109,7 +109,7 @@ describe('Tooltip', () => {
const tooltip = await screen.findByRole('tooltip');
const style = window.getComputedStyle(tooltip);
- expect(style.left).toEqual('87px');
- expect(style.top).toEqual('160px');
+ expect(style.left).toEqual('84px');
+ expect(style.top).toEqual('158.5px');
});
});