UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

286 lines (285 loc) 8.86 kB
import { jsx, jsxs } from "react/jsx-runtime"; import { useContext, useState, useCallback, useMemo, useEffect } from "react"; import { useDefaultProps } from "@hitachivantara/uikit-react-utils"; import { useControlled } from "../../hooks/useControlled.js"; import { uniqueId } from "../../utils/helpers.js"; import { setId } from "../../utils/setId.js"; import { NavigationPopupContainer } from "../NavigationPopup/NavigationPopupContainer.js"; import { getParentItemById } from "../NavigationSlider/utils.js"; import { VerticalNavigationContext } from "../VerticalNavigationContext.js"; import { useClasses } from "./Navigation.styles.js"; import { staticClasses } from "./Navigation.styles.js"; import { HvVerticalNavigationSlider } from "../NavigationSlider/NavigationSlider.js"; import { HvVerticalNavigationTreeView } from "../TreeView/TreeView.js"; import { HvVerticalNavigationTreeViewItem } from "../TreeView/TreeViewItem.js"; const createListHierarchy = (items, id, classes, mouseEnterHandler, disableTooltip = false) => items.map((item) => { const { id: itemId, label: itemLabel, icon, data: children, selectable, disabled, href, target } = item; const itemMouseEnterHandler = (event) => { mouseEnterHandler?.(event, item); }; return /* @__PURE__ */ jsx( HvVerticalNavigationTreeViewItem, { id: setId(id, itemId), className: classes?.listItem, href, target, nodeId: itemId, label: itemLabel, icon, payload: item, selectable, disabled, onMouseEnter: itemMouseEnterHandler, disableTooltip, children: children ? createListHierarchy( children, id, classes, mouseEnterHandler, disableTooltip ) : void 0 }, itemId ); }); const getAllParents = (items) => { const parents = items.filter( (item) => item.data != null && item.data.length > 0 ); const childParents = parents.flatMap((item) => getAllParents(item.data)); return [...parents, ...childParents]; }; function pathToElement(data, targetId) { const path = []; if (data != null && data.length > 0) { for (let i = 0; i !== data.length; ++i) { const item = data[i]; if (item.id === targetId) { path.push(item.id); break; } const subPaths = pathToElement(item.data, targetId); if (subPaths.length > 0) { path.push(item.id); path.push(...subPaths); break; } } } return path; } const HvVerticalNavigationTree = (props) => { const { id, className, classes: classesProp, data, mode = "navigation", collapsible = false, expanded: expandedProp, defaultExpanded, onToggle, selected: selectedProp, defaultSelected, onChange, sliderForwardButtonAriaLabel = "Navigate to submenu", ...others } = useDefaultProps("HvVerticalNavigationTree", props); const { classes, cx } = useClasses(classesProp); const [selected, setSelected] = useControlled(selectedProp, defaultSelected); const [expanded, setExpanded] = useControlled(expandedProp, () => { if (defaultExpanded === true) { return getAllParents(data).map((item) => item.id); } if (defaultExpanded === false) { return []; } if (defaultExpanded == null) { if (selected != null) { const path = pathToElement(data, selected); return path.slice(0, -1); } return []; } return defaultExpanded; }); const { isOpen, useIcons, slider, parentItem, setParentItem, withParentData, navigateToChildHandler, setParentData, setParentSelected } = useContext(VerticalNavigationContext); const [navigationPopup, setNavigationPopup] = useState(null); const handleChange = useCallback( (event, selectedId, selectedItem) => { if (useIcons && !isOpen && selectedItem.data) { const currentEventTarget = event.currentTarget; setNavigationPopup((prevState) => { return prevState?.anchorEl === currentEventTarget ? null : { uniqueKey: uniqueId(), anchorEl: currentEventTarget, fixedMode: true, data: selectedItem.data }; }); event.stopPropagation(); } else { setSelected(selectedId); setExpanded((prevState) => { if (!isOpen) { return [...prevState, ...pathToElement(data, selectedId)]; } return [...prevState]; }); setNavigationPopup(null); onChange?.(event, selectedItem); } }, [onChange, setSelected, setExpanded, isOpen, useIcons, data] ); const treeViewItemMouseEnterHandler = useCallback( (event, item) => { const isCollapsed = useIcons && !isOpen; if (isCollapsed && item.data && !navigationPopup?.fixedMode) { const currentEventTarget = event.currentTarget; setNavigationPopup?.({ uniqueKey: uniqueId(), anchorEl: currentEventTarget, fixedMode: false, data: item.data }); } else if (isCollapsed && !item.data && !navigationPopup?.fixedMode) { setNavigationPopup(null); } }, [isOpen, useIcons, navigationPopup] ); const handleToggle = useCallback( (event, newExpanded) => { setExpanded(newExpanded); onToggle?.(event, newExpanded); }, [onToggle, setExpanded] ); const children = useMemo( () => data && createListHierarchy( data, id, classes, treeViewItemMouseEnterHandler, navigationPopup?.fixedMode ), [classes, data, id, navigationPopup, treeViewItemMouseEnterHandler] ); useEffect(() => { if (!isOpen) { setNavigationPopup?.(null); } }, [isOpen]); useEffect(() => { if (setParentSelected) setParentSelected(selected); }, [selected, setSelected, setParentSelected]); useEffect(() => { if (setParentData) setParentData(data); }, [data, setParentData]); useEffect(() => { if (withParentData && selected && setParentItem && getParentItemById(withParentData, selected)) { setParentItem(getParentItemById(withParentData, selected)); } }, [withParentData, selected, setParentItem]); const navigateToTargetHandler = (event, selectedItem) => handleChange(event, selectedItem.id, selectedItem); const handleNavigationPopupClose = () => setNavigationPopup(null); const handleStyledNavMouseLeave = () => { if (useIcons && !isOpen && !navigationPopup?.fixedMode) { setNavigationPopup(null); } }; const handleNavigationPopupMouseLeave = () => { if (!navigationPopup?.fixedMode) { handleNavigationPopupClose(); } }; const handleNavigationPopupChange = (event, selectedItem) => { handleChange(event, selectedItem.id, selectedItem); }; return /* @__PURE__ */ jsx( "nav", { id, className: cx( classes.root, { [classes.collapsed]: !isOpen && !useIcons }, className ), onMouseLeave: handleStyledNavMouseLeave, ...others, children: slider ? /* @__PURE__ */ jsx( HvVerticalNavigationSlider, { data: parentItem.data || withParentData, selected, onNavigateToTarget: navigateToTargetHandler, onNavigateToChild: navigateToChildHandler, forwardButtonAriaLabel: sliderForwardButtonAriaLabel } ) : /* @__PURE__ */ jsxs( HvVerticalNavigationTreeView, { id: setId(id, "tree"), className: classes.list, selectable: true, mode, collapsible, selected, onChange: handleChange, expanded, onToggle: handleToggle, children: [ useIcons && !isOpen && navigationPopup && /* @__PURE__ */ jsx( NavigationPopupContainer, { anchorEl: navigationPopup.anchorEl, onClose: handleNavigationPopupClose, className: classes.navigationPopup, children: /* @__PURE__ */ jsx( HvVerticalNavigationTree, { className: classes.popup, id: setId(id, "navigation-popup-tree"), collapsible: true, defaultExpanded: true, selected, data: navigationPopup.data, onChange: handleNavigationPopupChange, onMouseLeave: handleNavigationPopupMouseLeave } ) }, navigationPopup.uniqueKey ), children ] } ) } ); }; export { HvVerticalNavigationTree, staticClasses as verticalNavigationTreeClasses };