|
@@ -2,7 +2,7 @@ import { h, Fragment } from 'preact';
|
|
import { createPortal } from 'preact/compat';
|
|
import { createPortal } from 'preact/compat';
|
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
|
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
|
|
|
|
|
|
-const WINDOW_PADDING = 10;
|
|
|
|
|
|
+const WINDOW_PADDING = 20;
|
|
|
|
|
|
export default function RelativeModal({
|
|
export default function RelativeModal({
|
|
className,
|
|
className,
|
|
@@ -13,7 +13,7 @@ export default function RelativeModal({
|
|
relativeTo,
|
|
relativeTo,
|
|
widthRelative = false,
|
|
widthRelative = false,
|
|
}) {
|
|
}) {
|
|
- const [position, setPosition] = useState({ top: -999, left: -999 });
|
|
|
|
|
|
+ const [position, setPosition] = useState({ top: -9999, left: -9999 });
|
|
const [show, setShow] = useState(false);
|
|
const [show, setShow] = useState(false);
|
|
const portalRoot = portalRootID && document.getElementById(portalRootID);
|
|
const portalRoot = portalRootID && document.getElementById(portalRootID);
|
|
const ref = useRef(null);
|
|
const ref = useRef(null);
|
|
@@ -53,33 +53,43 @@ export default function RelativeModal({
|
|
const windowWidth = window.innerWidth;
|
|
const windowWidth = window.innerWidth;
|
|
const windowHeight = window.innerHeight;
|
|
const windowHeight = window.innerHeight;
|
|
const { width: menuWidth, height: menuHeight } = ref.current.getBoundingClientRect();
|
|
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
|
|
// 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
|
|
// too far left
|
|
else if (left < WINDOW_PADDING) {
|
|
else if (left < WINDOW_PADDING) {
|
|
- left = WINDOW_PADDING;
|
|
|
|
|
|
+ newLeft = WINDOW_PADDING;
|
|
}
|
|
}
|
|
// too close to bottom
|
|
// 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 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) {
|
|
if (widthRelative) {
|
|
- newPosition.width = relativeWidth;
|
|
|
|
|
|
+ newPosition.width = relativeToWidth;
|
|
}
|
|
}
|
|
setPosition(newPosition);
|
|
setPosition(newPosition);
|
|
const focusable = ref.current.querySelector('[tabindex]');
|
|
const focusable = ref.current.querySelector('[tabindex]');
|
|
@@ -89,7 +99,9 @@ export default function RelativeModal({
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
if (position.top >= 0) {
|
|
if (position.top >= 0) {
|
|
- setShow(true);
|
|
|
|
|
|
+ window.requestAnimationFrame(() => {
|
|
|
|
+ setShow(true);
|
|
|
|
+ });
|
|
} else {
|
|
} else {
|
|
setShow(false);
|
|
setShow(false);
|
|
}
|
|
}
|
|
@@ -100,13 +112,13 @@ export default function RelativeModal({
|
|
<div data-testid="scrim" key="scrim" className="absolute inset-0 z-10" onClick={handleDismiss} />
|
|
<div data-testid="scrim" key="scrim" className="absolute inset-0 z-10" onClick={handleDismiss} />
|
|
<div
|
|
<div
|
|
key="menu"
|
|
key="menu"
|
|
- className={`z-10 bg-white dark:bg-gray-700 dark:text-white absolute shadow-lg rounded w-auto h-auto transition-all duration-75 transform scale-90 opacity-0 overflow-scroll ${
|
|
|
|
|
|
+ className={`z-10 bg-white dark:bg-gray-700 dark:text-white absolute shadow-lg rounded w-auto h-auto transition-transform transition-opacity duration-75 transform scale-90 opacity-0 overflow-x-hidden overflow-y-auto ${
|
|
show ? 'scale-100 opacity-100' : ''
|
|
show ? 'scale-100 opacity-100' : ''
|
|
} ${className}`}
|
|
} ${className}`}
|
|
onKeyDown={handleKeydown}
|
|
onKeyDown={handleKeydown}
|
|
role={role}
|
|
role={role}
|
|
ref={ref}
|
|
ref={ref}
|
|
- style={position.top >= 0 ? position : null}
|
|
|
|
|
|
+ style={position}
|
|
>
|
|
>
|
|
{children}
|
|
{children}
|
|
</div>
|
|
</div>
|