import React, { memo, useEffect, useRef, useCallback, useState, useMemo, } from "react"; import classNames from "classnames"; import * as Sentry from "@sentry/react"; import { useDispatch, useSelector } from "react-redux"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { getFirstTimeUserOnboardingComplete, getIsFirstTimeUserOnboardingEnabled, } from "selectors/onboardingSelectors"; import Explorer from "pages/Editor/Explorer"; import { setExplorerActiveAction } from "actions/explorerActions"; import { getExplorerActive, getExplorerPinned, } from "selectors/explorerSelector"; import { tailwindLayers } from "constants/Layers"; import { TooltipComponent } from "design-system"; import { previewModeSelector } from "selectors/editorSelectors"; import useHorizontalResize from "utils/hooks/useHorizontalResize"; import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar"; import Pages from "pages/Editor/Explorer/Pages"; import { EntityProperties } from "pages/Editor/Explorer/Entity/EntityProperties"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { SIDEBAR_ID } from "constants/Explorer"; import { isMultiPaneActive } from "selectors/multiPaneSelectors"; type Props = { width: number; onWidthChange?: (width: number) => void; onDragEnd?: () => void; }; export const EntityExplorerSidebar = memo((props: Props) => { let tooltipTimeout: ReturnType; const dispatch = useDispatch(); const active = useSelector(getExplorerActive); const sidebarRef = useRef(null); let pinned = useSelector(getExplorerPinned); const isMultiPane = useSelector(isMultiPaneActive); if (isMultiPane) pinned = false; const isPreviewMode = useSelector(previewModeSelector); const enableFirstTimeUserOnboarding = useSelector( getIsFirstTimeUserOnboardingEnabled, ); const resizer = useHorizontalResize( sidebarRef, props.onWidthChange, props.onDragEnd, ); const [tooltipIsOpen, setTooltipIsOpen] = useState(false); const isFirstTimeUserOnboardingComplete = useSelector( getFirstTimeUserOnboardingComplete, ); PerformanceTracker.startTracking(PerformanceTransactionName.SIDE_BAR_MOUNT); useEffect(() => { PerformanceTracker.stopTracking(); }); // registering event listeners useEffect(() => { document.addEventListener("mousemove", onMouseMove); return () => { document.removeEventListener("mousemove", onMouseMove); }; }, [active, pinned, resizer.resizing]); /** * passing the event to touch move on mouse move * * @param event */ const onMouseMove = (event: MouseEvent) => { const eventWithTouches = Object.assign({}, event, { touches: [{ clientX: event.clientX, clientY: event.clientY }], }); onTouchMove(eventWithTouches); }; /** * calculate the new width based on the pixel moved * * @param event */ const onTouchMove = ( event: | TouchEvent | (MouseEvent & { touches: { clientX: number; clientY: number }[] }), ) => { const currentX = event.touches[0].clientX; // only calculate the following in unpin mode if (!pinned) { if (active) { // if user cursor is out of the entity explorer width ( with some extra window = 20px ), make the // entity explorer inactive. Also, 20px here is to increase the window in which a user can drag the resizer if (currentX >= props.width + 20 && !resizer.resizing) { dispatch(setExplorerActiveAction(false)); } } else { // check if user cursor is at extreme left when the explorer is inactive, if yes, make the explorer active if (currentX <= 20) { dispatch(setExplorerActiveAction(true)); } } } }; /** * on hover of resizer, show tooltip */ const onHoverResizer = useCallback(() => { tooltipTimeout = setTimeout(() => { setTooltipIsOpen(true); }, 250); }, [setTooltipIsOpen]); /** * on hover end of resizer, hide tooltip */ const onHoverEndResizer = useCallback(() => { clearTimeout(tooltipTimeout); setTooltipIsOpen(false); }, [setTooltipIsOpen]); /** * resizer left position */ const resizerLeft = useMemo(() => { return !pinned && !active ? 0 : props.width; }, [pinned, active, props.width]); useEffect(() => { dispatch({ type: ReduxActionTypes.SET_ENTITY_INFO, payload: { show: false }, }); }, [resizerLeft, pinned, isPreviewMode]); return (
{/* SIDEBAR */}
{(enableFirstTimeUserOnboarding || isFirstTimeUserOnboardingComplete) && } {/* PagesContainer */} {/* Popover that contains the bindings info */} {/* Contains entity explorer & widgets library along with a switcher*/}
{/* RESIZER */}
Drag to resize
} hoverOpenDelay={200} isOpen={tooltipIsOpen && !resizer.resizing} position="right" >
); }); EntityExplorerSidebar.displayName = "EntityExplorerSidebar"; export default Sentry.withProfiler(EntityExplorerSidebar);