From f00628f4e5d676a2fefbbbd4216e38d1e37f620c Mon Sep 17 00:00:00 2001 From: Paul Armstrong Date: Sat, 6 Feb 2021 20:59:11 -0800 Subject: [PATCH] refactor(web): menu positioning --- web/src/components/AppBar.jsx | 2 +- web/src/components/Menu.jsx | 7 +++-- web/src/components/RelativeModal.jsx | 44 ++++++++++++++++------------ web/src/components/Select.jsx | 2 +- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/web/src/components/AppBar.jsx b/web/src/components/AppBar.jsx index 698635d88..c5b11956c 100644 --- a/web/src/components/AppBar.jsx +++ b/web/src/components/AppBar.jsx @@ -82,7 +82,7 @@ export default function AppBar({ title }) {
-
+
diff --git a/web/src/components/Menu.jsx b/web/src/components/Menu.jsx index dc521a44e..d1bab4016 100644 --- a/web/src/components/Menu.jsx +++ b/web/src/components/Menu.jsx @@ -2,7 +2,7 @@ import { h } from 'preact'; import RelativeModal from './RelativeModal'; import { useCallback, useEffect } from 'preact/hooks'; -export default function Menu({ className, children, onDismiss, relativeTo }) { +export default function Menu({ className, children, onDismiss, relativeTo, widthRelative }) { return relativeTo ? ( ) : null; } @@ -38,11 +39,11 @@ export function MenuItem({ focus, icon: Icon, label, onSelect, value }) { role="option" > {Icon ? ( -
+
) : null} - {label} +
{label}
); } diff --git a/web/src/components/RelativeModal.jsx b/web/src/components/RelativeModal.jsx index 4f7e5242e..46d2ce1c4 100644 --- a/web/src/components/RelativeModal.jsx +++ b/web/src/components/RelativeModal.jsx @@ -2,10 +2,18 @@ import { h, Fragment } from 'preact'; import { createPortal } from 'preact/compat'; import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; -const WINDOW_PADDING = 20; +const WINDOW_PADDING = 10; -export default function RelativeModal({ className, role = 'dialog', children, onDismiss, portalRootID, relativeTo }) { - const [position, setPosition] = useState({ top: -999, left: 0, width: 0 }); +export default function RelativeModal({ + className, + role = 'dialog', + children, + onDismiss, + portalRootID, + relativeTo, + widthRelative = false, +}) { + const [position, setPosition] = useState({ top: -999, left: -999 }); const [show, setShow] = useState(false); const portalRoot = portalRootID && document.getElementById(portalRootID); const ref = useRef(null); @@ -44,12 +52,15 @@ export default function RelativeModal({ className, role = 'dialog', children, on const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const { width: menuWidth, height: menuHeight } = ref.current.getBoundingClientRect(); - const { x, y, width, height } = relativeTo.current.getBoundingClientRect(); + const { x, y, width: relativeWidth, height } = relativeTo.current.getBoundingClientRect(); + + const width = widthRelative ? relativeWidth : menuWidth; + let top = y + height; let left = x; // too far right - if (left + menuWidth >= windowWidth - WINDOW_PADDING) { - left = windowWidth - menuWidth - WINDOW_PADDING; + if (left + width >= windowWidth - WINDOW_PADDING) { + left = windowWidth - width - WINDOW_PADDING; } // too far left else if (left < WINDOW_PADDING) { @@ -65,20 +76,23 @@ export default function RelativeModal({ className, role = 'dialog', children, on } const maxHeight = windowHeight - WINDOW_PADDING * 2 > menuHeight ? null : windowHeight - WINDOW_PADDING * 2; - setPosition({ left, top: top + window.scrollY, width, height: maxHeight }); + const newPosition = { left: left + window.scrollX, top: top + window.scrollY, maxHeight }; + if (widthRelative) { + newPosition.width = relativeWidth; + } + setPosition(newPosition); const focusable = ref.current.querySelector('[tabindex]'); - focusable && console.log('focusing'); focusable && focusable.focus(); } - }, [relativeTo && relativeTo.current, ref && ref.current]); + }, [relativeTo && relativeTo.current, ref && ref.current, widthRelative]); useEffect(() => { - if (position.width) { + if (position.top >= 0) { setShow(true); } else { setShow(false); } - }, [show, position.width, ref.current]); + }, [show, position.top, ref.current]); const menu = ( @@ -91,13 +105,7 @@ export default function RelativeModal({ className, role = 'dialog', children, on onkeydown={handleKeydown} role={role} ref={ref} - style={ - position.width > 0 - ? `min-width: ${position.width}px; ${position.height ? `max-height: ${position.height}px;` : ''} top: ${ - position.top - }px; left: ${position.left}px` - : '' - } + style={position.top >= 0 ? position : null} > {children}
diff --git a/web/src/components/Select.jsx b/web/src/components/Select.jsx index ae5e1fe95..bf2e47690 100644 --- a/web/src/components/Select.jsx +++ b/web/src/components/Select.jsx @@ -95,7 +95,7 @@ export default function Select({ label, onChange, options: inputOptions = [], se value={options[selected]?.label} /> {showMenu ? ( - + {options.map(({ value, label }, i) => ( ))}