Skip to content

Commit

Permalink
fix submenu offscreen #203
Browse files Browse the repository at this point in the history
  • Loading branch information
fkhadra committed Nov 5, 2022
1 parent 49e9a13 commit d3afad0
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 36 deletions.
47 changes: 16 additions & 31 deletions src/components/Submenu.tsx
@@ -1,4 +1,4 @@
import React, { ReactNode, useEffect, useRef, useState } from 'react';
import React, { ReactNode, useRef } from 'react';
import cx from 'clsx';

import { InternalProps, BooleanPredicate, HandlerParamsEvent } from '../types';
Expand Down Expand Up @@ -37,13 +37,6 @@ export interface SubMenuProps
hidden?: BooleanPredicate;
}

interface SubMenuState {
left?: string | number;
right?: string | number;
top?: string | number;
bottom?: string | number;
}

export const Submenu: React.FC<SubMenuProps> = ({
arrow,
children,
Expand All @@ -59,42 +52,36 @@ export const Submenu: React.FC<SubMenuProps> = ({
const menuRefTracker = useRefTrackerContext();
const refTracker = useRefTracker();
const nodeRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<SubMenuState>({
left: '100%',
top: 0,
bottom: 'initial',
});
const handlerParams = {
triggerEvent: triggerEvent as HandlerParamsEvent,
props: propsFromTrigger,
};
const isDisabled = getPredicateValue(disabled, handlerParams);
const isHidden = getPredicateValue(hidden, handlerParams);

useEffect(() => {
function setPosition() {
if (nodeRef.current) {
const { innerWidth, innerHeight } = window;
nodeRef.current.style.left = '100%';
nodeRef.current.style.top = '0';
const rect = nodeRef.current.getBoundingClientRect();
const style: SubMenuState = {};

if (rect.right < innerWidth) {
style.left = '100%';
style.right = undefined;
nodeRef.current.style.left = '100%';
nodeRef.current.style.right = '';
} else {
style.right = '100%';
style.left = undefined;
nodeRef.current.style.right = '100%';
nodeRef.current.style.left = '';
}

if (rect.bottom > innerHeight) {
style.bottom = 0;
style.top = 'initial';
nodeRef.current.style.bottom = '0';
nodeRef.current.style.top = 'initial';
} else {
style.bottom = 'initial';
nodeRef.current.style.bottom = 'initial';
}

setPosition(style);
}
}, []);
}

function handleClick(e: React.SyntheticEvent) {
e.stopPropagation();
Expand All @@ -106,6 +93,7 @@ export const Submenu: React.FC<SubMenuProps> = ({
node,
isSubmenu: true,
submenuRefTracker: refTracker,
setSubmenuPosition: setPosition,
});
}

Expand All @@ -115,11 +103,6 @@ export const Submenu: React.FC<SubMenuProps> = ({
[`${STYLE.itemDisabled}`]: isDisabled,
});

const submenuStyle = {
...style,
...position,
};

return (
<RefTrackerProvider refTracker={refTracker}>
<div
Expand All @@ -130,12 +113,14 @@ export const Submenu: React.FC<SubMenuProps> = ({
role="menuitem"
aria-haspopup
aria-disabled={isDisabled}
onMouseEnter={setPosition}
onTouchStart={setPosition}
>
<div className={STYLE.itemContent} onClick={handleClick}>
{label}
<span className={STYLE.submenuArrow}>{arrow || <Arrow />}</span>
</div>
<div className={STYLE.submenu} ref={nodeRef} style={submenuStyle}>
<div className={STYLE.submenu} ref={nodeRef} style={style}>
{cloneItems(children, {
propsFromTrigger,
// injected by the parent
Expand Down
11 changes: 6 additions & 5 deletions src/components/menuController.ts
Expand Up @@ -74,17 +74,18 @@ export function createMenuController() {
function openSubmenu() {
if (isFocused() && isSubmenuFocused()) {
const submenuItems = getSubmenuItems();
const currentNode = currentItems[focusedIndex].node;
const { node, setSubmenuPosition } = currentItems[focusedIndex];

menuList.set(currentNode, {
menuList.set(node, {
isRoot: isAtRoot,
focusedIndex,
parentNode: parentNode || currentNode,
parentNode: parentNode || node,
items: currentItems,
});

currentNode.classList.add(STYLE.submenuOpen);
parentNode = currentNode;
setSubmenuPosition!();
node.classList.add(STYLE.submenuOpen);
parentNode = node;

if (submenuItems.length > 0) {
focusedIndex = 0;
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useRefTracker.ts
Expand Up @@ -4,6 +4,7 @@ export interface RefTrackerValue {
node: HTMLElement;
isSubmenu: boolean;
submenuRefTracker?: RefTracker;
setSubmenuPosition?: () => void;
}

export type RefTracker = ReturnType<typeof useRefTracker>;
Expand Down

0 comments on commit d3afad0

Please sign in to comment.