diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5c5303c30f..34e7f94377 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,7 @@ ## Next * Fixed character sorting on the Loadouts page. +* Dropdown no longer flickers on Firefox. ## 8.31.0 (2024-08-04) diff --git a/src/app/dim-ui/Dropdown.tsx b/src/app/dim-ui/Dropdown.tsx index 1eb7dde61e..bfa05e456c 100644 --- a/src/app/dim-ui/Dropdown.tsx +++ b/src/app/dim-ui/Dropdown.tsx @@ -74,13 +74,16 @@ export default function Dropdown({ const buttonRef = useRef(null); const menuRef = useRef(null); - usePopper({ - contents: menuRef, - reference: buttonRef, - placement, - offset, - fixed, - }); + usePopper( + { + contents: menuRef, + reference: buttonRef, + placement, + offset, + fixed, + }, + [isOpen, items], + ); return (
diff --git a/src/app/dim-ui/PressTip.tsx b/src/app/dim-ui/PressTip.tsx index 2bf4c71d6d..35a07706bb 100644 --- a/src/app/dim-ui/PressTip.tsx +++ b/src/app/dim-ui/PressTip.tsx @@ -93,12 +93,15 @@ function Control({ const pressTipRoot = useContext(PressTipRoot); const [customization, customizeTooltip] = useState({ className: null }); - usePopper({ - contents: tooltipContents, - reference: triggerRef, - arrowClassName: styles.arrow, - placement, - }); + usePopper( + { + contents: tooltipContents, + reference: triggerRef, + arrowClassName: styles.arrow, + placement, + }, + [open], + ); if (!tooltip) { const { style } = rest; diff --git a/src/app/dim-ui/Select.tsx b/src/app/dim-ui/Select.tsx index 542f52e761..57760ce996 100644 --- a/src/app/dim-ui/Select.tsx +++ b/src/app/dim-ui/Select.tsx @@ -79,12 +79,15 @@ export default function Select({ ); const [dropdownHeight, setDropdownHeight] = useState(); - usePopper({ - contents: menuRef, - reference: buttonRef, - placement: 'bottom-start', - offset: 2, - }); + usePopper( + { + contents: menuRef, + reference: buttonRef, + placement: 'bottom-start', + offset: 2, + }, + [isOpen, items], + ); if (!selectedItem) { throw new Error('value must correspond to one of the provided options'); diff --git a/src/app/dim-ui/usePopper.ts b/src/app/dim-ui/usePopper.ts index 22d674de9c..32944af420 100644 --- a/src/app/dim-ui/usePopper.ts +++ b/src/app/dim-ui/usePopper.ts @@ -97,35 +97,38 @@ const popperOptions = ( }; }; -export function usePopper({ - contents, - reference, - arrowClassName, - menuClassName, - boundarySelector, - placement, - offset, - fixed, - padding, -}: { - /** A ref to the rendered contents of a popper-positioned item */ - contents: React.RefObject; - /** An ref to the item that triggered the popper, which anchors it */ - reference: React.RefObject; - /** A class used to identify the arrow */ - arrowClassName?: string; - /** A class used to identify the sidecar menu */ - menuClassName?: string; - /** An optional additional selector for a "boundary area" */ - boundarySelector?: string; - /** Placement preference of the popper. Defaults to "auto" */ - placement?: Placement; - /** Offset of how far from the element to shift the popper. */ - offset?: number; - /** Is this placed on a fixed item? Workaround for https://github.com/popperjs/popper-core/issues/1156. TODO: make a "positioning context" context value for this */ - fixed?: boolean; - padding?: Padding; -}) { +export function usePopper( + { + contents, + reference, + arrowClassName, + menuClassName, + boundarySelector, + placement, + offset, + fixed, + padding, + }: { + /** A ref to the rendered contents of a popper-positioned item */ + contents: React.RefObject; + /** An ref to the item that triggered the popper, which anchors it */ + reference: React.RefObject; + /** A class used to identify the arrow */ + arrowClassName?: string; + /** A class used to identify the sidecar menu */ + menuClassName?: string; + /** An optional additional selector for a "boundary area" */ + boundarySelector?: string; + /** Placement preference of the popper. Defaults to "auto" */ + placement?: Placement; + /** Offset of how far from the element to shift the popper. */ + offset?: number; + /** Is this placed on a fixed item? Workaround for https://github.com/popperjs/popper-core/issues/1156. TODO: make a "positioning context" context value for this */ + fixed?: boolean; + padding?: Padding; + }, + deps: React.DependencyList = [], +) { const popper = useRef(); const destroy = () => { @@ -160,5 +163,24 @@ export function usePopper({ } return destroy; - }); + }, [ + contents, + reference, + arrowClassName, + menuClassName, + boundarySelector, + placement, + offset, + fixed, + padding, + + /** + * Doing ...deps allows us to pass dependencies from the components that rely on + * usePopper. Certain popovers are only shown when specific conditions are met, + * so by making those conditions dependencies we can position the popover + * correctly once the popover is actually shown. + */ + // eslint-disable-next-line react-hooks/exhaustive-deps + ...deps, + ]); }