From 5e63d16b0935c615cba5fd3d6a6bae11cfefb40a Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Tue, 30 Jul 2024 12:54:10 +0300 Subject: [PATCH] CW-long-breadcrumbs Added tooltip logic Added tooltip logic Updated tooltip component Optimized Breadcrumbs --- .../BreadcrumbsItem.module.scss | 13 +++ .../BreadcrumbsItem/BreadcrumbsItem.tsx | 48 ++++++++--- .../FeedItemBreadcrumbs.tsx | 41 ++++++---- .../FeedBreadcrumbsItem.tsx | 66 +++++++-------- .../TooltipContent/TooltipContent.module.scss | 5 ++ .../TooltipContent/TooltipContent.tsx | 82 +++++++++++++------ 6 files changed, 174 insertions(+), 81 deletions(-) diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.module.scss b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.module.scss index 76206a70e3..428a4bf37f 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.module.scss +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.module.scss @@ -29,6 +29,19 @@ } } +.tooltipContent { + font-family: PoppinsSans, sans-serif; + font-size: $moderate-xsmall; + font-weight: normal; + color: var(--breadcrumbs-color); +} + +.li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + @include tablet { .li { &:first-child { diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.tsx index 2839262254..7b84a29081 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/BreadcrumbsItem/BreadcrumbsItem.tsx @@ -1,7 +1,13 @@ -import React, { FC, useRef } from "react"; +import React, { FC, useRef, useMemo } from "react"; import { useHistory } from "react-router"; +import { useMeasure } from "react-use"; import { useRoutesContext } from "@/shared/contexts"; -import { ContextMenuRef } from "@/shared/ui-kit"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, + ContextMenuRef, +} from "@/shared/ui-kit"; import { ProjectsStateItem } from "@/store/states"; import { truncateBreadcrumbName } from "../../utils"; import { BreadcrumbsMenu } from "../BreadcrumbsMenu"; @@ -18,6 +24,9 @@ export interface BreadcrumbsItemProps { onClick?: () => void; } +const PADDING = 24; +const OVERLAY_THRESHOLD = 5; + const BreadcrumbsItem: FC = (props) => { const { activeItem, @@ -30,8 +39,18 @@ const BreadcrumbsItem: FC = (props) => { onClick, } = props; const history = useHistory(); + const [containerRef, { width: containerWidth }] = useMeasure(); + const [buttonRef, { width: buttonWidth }] = useMeasure(); + + const hasOverlay = useMemo(() => { + return ( + Math.abs( + Math.floor(containerWidth) - Math.floor(buttonWidth + PADDING), + ) >= OVERLAY_THRESHOLD + ); + }, [containerWidth, buttonWidth]); + const { getCommonPagePath } = useRoutesContext(); - const containerRef = useRef(null); const contextMenuRef = useRef(null); const handleButtonClick = () => { @@ -40,17 +59,26 @@ const BreadcrumbsItem: FC = (props) => { onClick?.(); return; } - if (containerRef.current) { - const { x, y, height } = containerRef.current.getBoundingClientRect(); - contextMenuRef.current?.open(x, y + height); - } }; return (
  • - + + + + + + {activeItem.name} + + {withMenu && ( = (props) => { - const { breadcrumbs, itemsWithMenus, truncate } = props; +const FeedItemBreadcrumbs: FC = ({ + breadcrumbs, + itemsWithMenus, + truncate, +}) => { const dispatch = useDispatch(); const goToCreateCommon = useGoToCreateCommon(); const isMobileView = useIsTabletView(); - const breadcrumbsItems = truncate - ? [breadcrumbs.items[0], breadcrumbs.items[breadcrumbs.items.length - 1]] - : breadcrumbs.items; - const handleItemClick = (item: ProjectsStateItem) => { - if (item.rootCommonId) { - dispatch( - commonLayoutActions.resetCurrentCommonIdAndProjects(item.rootCommonId), - ); - } - }; + const breadcrumbsItems = useMemo(() => { + return truncate + ? [breadcrumbs.items[0], breadcrumbs.items[breadcrumbs.items.length - 1]] + : breadcrumbs.items; + }, [breadcrumbs.items, truncate]); + + const handleItemClick = useCallback( + (item: ProjectsStateItem) => { + if (item.rootCommonId) { + dispatch( + commonLayoutActions.resetCurrentCommonIdAndProjects( + item.rootCommonId, + ), + ); + } + }, + [dispatch], + ); useEffect(() => { const commonIds = breadcrumbs.items.map((item) => item.commonId); @@ -55,7 +66,9 @@ const FeedItemBreadcrumbs: FC = (props) => { }); }); - return unsubscribe; + return () => { + unsubscribe(); + }; }, [breadcrumbs.activeItem?.id]); return ( diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx index 6daa302210..95b0055525 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx @@ -1,4 +1,4 @@ -import React, { FC, useMemo } from "react"; +import React, { FC, useMemo, useCallback } from "react"; import { useSelector } from "react-redux"; import { ProjectsStateItem, @@ -21,49 +21,49 @@ const getItemsByParentId = ( ): ProjectsStateItem[] => data.filter((item) => item.directParent?.commonId === parentId); -const FeedBreadcrumbsItem: FC = (props) => { - const { activeItem, ...restProps } = props; +const FeedBreadcrumbsItem: FC = ({ + activeItem, + ...restProps +}) => { const { commons, areCommonsFetched } = useSelector( selectCommonLayoutCommonsState, ); const { projects, areProjectsFetched } = useSelector( selectCommonLayoutProjectsState, ); + const parentCommonId = activeItem.directParent?.commonId; - const baseItems = useMemo( - () => - parentCommonId ? getItemsByParentId(parentCommonId, projects) : commons, - [parentCommonId, projects, commons], - ); + + const baseItems = useMemo(() => { + return parentCommonId + ? getItemsByParentId(parentCommonId, projects) + : commons; + }, [parentCommonId, projects, commons]); + const areItemsLoading = parentCommonId ? !areProjectsFetched : !areCommonsFetched; - const hasParentPermissionToAddProject = useMemo( - () => - (parentCommonId && - ( - commons.find((item) => item.commonId === parentCommonId) || - projects.find((item) => item.commonId === parentCommonId) - )?.hasPermissionToAddProject) ?? - false, - [commons, projects, parentCommonId], - ); - const items = useMemo( - () => - baseItems.length === 0 - ? [activeItem] - : [...baseItems].sort((prevItem, nextItem) => { - if (prevItem.commonId === activeItem.commonId) { - return -1; - } - if (nextItem.commonId === activeItem.commonId) { - return 1; - } - return 0; - }), - [baseItems, activeItem], - ); + const hasParentPermissionToAddProject = useMemo(() => { + if (!parentCommonId) return false; + + const parentItem = + commons.find((item) => item.commonId === parentCommonId) || + projects.find((item) => item.commonId === parentCommonId); + + return parentItem?.hasPermissionToAddProject ?? false; + }, [commons, projects, parentCommonId]); + + const items = useMemo(() => { + if (baseItems.length === 0) { + return [activeItem]; + } + return [...baseItems].sort((prevItem, nextItem) => { + if (prevItem.commonId === activeItem.commonId) return -1; + if (nextItem.commonId === activeItem.commonId) return 1; + return 0; + }); + }, [baseItems, activeItem]); return ( { + withOverlay?: boolean; +} + const TooltipContent: ForwardRefRenderFunction< HTMLDivElement, - HTMLProps + TooltipContentProps > = (props, propRef) => { + const { withOverlay } = props; const state = useTooltipContext(); const { children, ...containerProps } = state.getFloatingProps(props); const [side] = state.placement.split("-"); @@ -44,35 +49,64 @@ const TooltipContent: ForwardRefRenderFunction< [state.floating, propRef], ); + const divProps = useMemo( + () => ({ + ref, + style: { + position: state.strategy, + top: state.y ?? 0, + left: state.x ?? 0, + visibility: state.x === null ? "hidden" : "visible", + ...props.style, + } as CSSProperties, + ...containerProps, + className: containerClassName, + }), + [ + state.strategy, + state.x, + state.y, + props.style, + containerProps, + containerClassName, + ], + ); + return ( {state.open && ( -
    - {children} -
    - -
    -
    + <> + {withOverlay ? ( + +
    + {children} +
    + +
    +
    +
    + ) : ( +
    + {children} +
    + +
    +
    + )} + )}
    ); }; -export default forwardRef>( +export default forwardRef( TooltipContent, );