Skip to content

Commit

Permalink
feat(VerticalNavigation): popup on mouse hover
Browse files Browse the repository at this point in the history
  • Loading branch information
andrefrua committed Apr 19, 2023
1 parent 5816208 commit 99ad989
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useMemo, useContext, useEffect, useState } from "react";
import clsx from "clsx";
import uniqueId from "lodash/uniqueId";
import { setId, wrapperTooltip } from "~/utils";
import { useControlled } from "~/hooks";
import {
Expand Down Expand Up @@ -57,7 +58,9 @@ export interface NavigationData {
const createListHierarchy = (
items,
id,
classes?: HvVerticalNavigationTreeClasses
classes?: HvVerticalNavigationTreeClasses,
mouseEnterHandler?: (event, item) => void,
disableTooltip = false
) =>
items.map((item) => {
const {
Expand All @@ -73,6 +76,10 @@ const createListHierarchy = (

const ItemText = wrapperTooltip(true, itemLabel, itemLabel);

const itemMouseEnterHandler = (event) => {
mouseEnterHandler?.(event, item);
};

return (
<HvVerticalNavigationTreeViewItem
id={setId(id, itemId)}
Expand All @@ -86,8 +93,18 @@ const createListHierarchy = (
payload={item}
selectable={selectable}
disabled={disabled}
onMouseEnter={itemMouseEnterHandler}
disableTooltip={disableTooltip}
>
{children ? createListHierarchy(children, id, classes) : undefined}
{children
? createListHierarchy(
children,
id,
classes,
mouseEnterHandler,
disableTooltip
)
: undefined}
</HvVerticalNavigationTreeViewItem>
);
});
Expand Down Expand Up @@ -185,7 +202,10 @@ export const HvVerticalNavigationTree = ({
} = useContext(VerticalNavigationContext);

const [navigationPopup, setNavigationPopup] = useState<{
// This value is needed to guarantee that the NavigationPopup is fully re-rendered with keeping any previous values
uniqueKey: string;
anchorEl: HTMLButtonElement | null;
fixedMode: boolean;
data: NavigationData[];
} | null>(null);

Expand All @@ -197,7 +217,12 @@ export const HvVerticalNavigationTree = ({
// We want to close the popup in case the clicked element is the same as the previous one
return prevState?.anchorEl === currentEventTarget
? null
: { anchorEl: currentEventTarget, data: selectedItem.data };
: {
uniqueKey: uniqueId(),
anchorEl: currentEventTarget,
fixedMode: true,
data: selectedItem.data,
};
});

// We need this stopPropagation or else the Popup will close due to the clickaway being triggered
Expand All @@ -213,6 +238,23 @@ export const HvVerticalNavigationTree = ({
[onChange, setSelected]
);

const treeViewItemMouseEnterHandler = (event, item) => {
const isCollapsedMode = collapsedMode === "icon" && !isOpen;

if (isCollapsedMode && item.data && !navigationPopup?.fixedMode) {
const currentEventTarget = event.currentTarget;

setNavigationPopup?.({
uniqueKey: uniqueId(),
anchorEl: currentEventTarget,
fixedMode: false,
data: item.data,
});
} else if (isCollapsedMode && !item.data && !navigationPopup?.fixedMode) {
setNavigationPopup(null);
}
};

const handleToggle = useCallback(
(event, newExpanded) => {
setExpanded(newExpanded);
Expand All @@ -225,10 +267,24 @@ export const HvVerticalNavigationTree = ({
);

const children = useMemo(
() => data && createListHierarchy(data, id, classes),
[classes, data, id]
() =>
data &&
createListHierarchy(
data,
id,
classes,
treeViewItemMouseEnterHandler,
navigationPopup?.fixedMode
),
[classes, data, id, navigationPopup, isOpen]
);

useEffect(() => {
if (!isOpen) {
setNavigationPopup?.(null);
}
}, [isOpen]);

useEffect(() => {
if (setParentSelected) setParentSelected(selected);
}, [selected, setSelected]);
Expand All @@ -246,6 +302,12 @@ export const HvVerticalNavigationTree = ({
setNavigationPopup(null);
};

const handleStyledNavMouseLeave = () => {
if (collapsedMode === "icon" && !isOpen && !navigationPopup?.fixedMode) {
setNavigationPopup(null);
}
};

return (
<StyledNav
id={id}
Expand All @@ -257,6 +319,7 @@ export const HvVerticalNavigationTree = ({
collapsedMode == "simple" &&
clsx(verticalNavigationTreeClasses.collapsed, classes?.collapsed)
)}
onMouseLeave={handleStyledNavMouseLeave}
{...others}
>
{slider ? (
Expand All @@ -281,10 +344,12 @@ export const HvVerticalNavigationTree = ({
{collapsedMode === "icon" && !isOpen && navigationPopup && (
<HvVerticalNavigationPopup
id={setId(id, "navigation-popup")}
anchorEl={navigationPopup?.anchorEl}
onClose={handleNavigationPopupClose}
key={navigationPopup.uniqueKey}
anchorEl={navigationPopup.anchorEl}
selected={selected}
data={navigationPopup?.data}
fixedMode={navigationPopup.fixedMode}
data={navigationPopup.data}
onClose={handleNavigationPopupClose}
onChange={handleChange}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@ import {
HvVerticalNavigationTree,
HvVerticalNavigation,
verticalNavigationTreeClasses,
} from "components";
import { setId } from "utils";
} from "~/components";
import { setId } from "~/utils";

import { StyledPopupContainer } from "./NavigationPopup.styles";

export type HvVerticalNavigationPopupProps = {
id?: string;
anchorEl?: HTMLElement | null;
onClose?: () => void;
fixedMode?: boolean;
data?: NavigationData[];
selected?: string;
onClose?: () => void;
onChange?: any;
};

export const HvVerticalNavigationPopup = ({
id,
anchorEl,
fixedMode,
onClose,
data,
selected,
Expand All @@ -36,6 +38,12 @@ export const HvVerticalNavigationPopup = ({
onChange(event, selectedItem.id, selectedItem);
};

const handleMouseLeave = () => {
if (!fixedMode) {
onClose?.();
}
};

return (
<Popper open anchorEl={anchorEl} placement="right-start">
<ClickAwayListener onClickAway={handleClickAway}>
Expand All @@ -49,6 +57,7 @@ export const HvVerticalNavigationPopup = ({
selected={selected}
onChange={handleChange}
data={data}
onMouseLeave={handleMouseLeave}
/>
</HvVerticalNavigation>
</StyledPopupContainer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getClasses } from "utils";
import { getClasses } from "~/utils";

export type HvVerticalNavigationPopupClasses = {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import styled from "@emotion/styled";

interface StyledIconsContainerProps {
hasExpandableItems?: boolean;
hasAnyChildWithData?: boolean;
}

export const StyledIconsContainer = styled("div")(
({ hasExpandableItems }: StyledIconsContainerProps) => ({
({ hasAnyChildWithData }: StyledIconsContainerProps) => ({
display: "flex",

[`> div:first-of-type`]: {
marginLeft: hasExpandableItems ? "auto" : "unset",
marginLeft: hasAnyChildWithData ? "auto" : "unset",
},

[`> div:nth-of-type(2)`]: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Forwards } from "@hitachivantara/uikit-react-icons";
import { useContext } from "react";

import { HvAvatar } from "components";
import { HvAvatar, VerticalNavigationContext } from "~/components";

import { TooltipWrapper } from "../TooltipWrapper";
import { SpacerDiv, StyledIconsContainer } from "./IconWrapper.styles";
Expand All @@ -9,13 +10,17 @@ export const IconWrapper = ({
icon,
label,
hasChildren,
hasExpandableItems,
showAvatar,
isOpen,
disableTooltip,
}) => {
const { hasAnyChildWithData } = useContext(VerticalNavigationContext);
return (
<TooltipWrapper showTooltip={!hasChildren && !isOpen} label={label}>
<StyledIconsContainer hasExpandableItems={hasExpandableItems}>
<TooltipWrapper
showTooltip={!hasChildren && !isOpen && !disableTooltip}
label={label}
>
<StyledIconsContainer hasAnyChildWithData={hasAnyChildWithData}>
{showAvatar ? (
<HvAvatar
variant="square"
Expand All @@ -32,7 +37,7 @@ export const IconWrapper = ({
{hasChildren && !isOpen ? (
<Forwards iconSize="XS" />
) : (
hasExpandableItems && !isOpen && <SpacerDiv />
hasAnyChildWithData && !isOpen && <SpacerDiv />
)}
</StyledIconsContainer>
</TooltipWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HvTooltip, HvTooltipPlacementType, HvTypography } from "components";
import { HvTooltip, HvTooltipPlacementType, HvTypography } from "~/components";

interface TooltipWrapperProps {
showTooltip: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,6 @@ export const HvVerticalNavigationTreeView = forwardRef(
const getFirstNode = () => getNavigableChildrenIds(null)[0];
const getParent = (id) => nodeMap.current[id].parentId;

const hasExpandableItems = () => {
return Object.keys(nodeMap.current).some((key) => {
return nodeMap.current[key].expandable === true;
});
};

/**
* This is used to determine the start and end of a selection range so
* we can get the nodes between the two border nodes.
Expand Down Expand Up @@ -984,7 +978,6 @@ export const HvVerticalNavigationTreeView = forwardRef(
mapFirstChar,
unMapFirstChar,
focus,
hasExpandableItems: hasExpandableItems(),
}),
[
registerNode,
Expand All @@ -1000,7 +993,6 @@ export const HvVerticalNavigationTreeView = forwardRef(
multiSelect,
disabledItemsFocusable,
treeId,
hasExpandableItems,
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,4 @@ interface TreeViewControlContextValue {
mapFirstChar?: (id, firstChar) => void;
unMapFirstChar?: (id, firstChar?) => void;
focus?: (id, firstChar) => void;
hasExpandableItems?: boolean;
}

0 comments on commit 99ad989

Please sign in to comment.