import React, { FC, useCallback, useEffect, useRef, useState, } from 'react'; import { scheduleToPutUserUISettings } from '~/client/services/user-ui-settings'; import { useDrawerMode, useDrawerOpened, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth, useSidebarResizeDisabled, } from '~/stores/ui'; import DrawerToggler from './Navbar/DrawerToggler'; import SidebarNav from './Sidebar/SidebarNav'; import SidebarContents from './Sidebar/SidebarContents'; import { NavigationResizeHexagon } from './Sidebar/NavigationResizeHexagon'; import StickyStretchableScroller from './StickyStretchableScroller'; const sidebarMinWidth = 240; const sidebarMinimizeWidth = 20; const GlobalNavigation = () => { const { data: currentContents } = useCurrentSidebarContents(); const { data: isCollapsed, mutate: mutateSidebarCollapsed } = useSidebarCollapsed(); const itemSelectedHandler = useCallback((selectedContents) => { let newValue = false; // already selected if (currentContents === selectedContents) { // toggle collapsed newValue = !isCollapsed; } mutateSidebarCollapsed(newValue, false); scheduleToPutUserUISettings({ isSidebarCollapsed: newValue }); }, [currentContents, isCollapsed, mutateSidebarCollapsed]); return ; }; const SidebarContentsWrapper = () => { const [resetKey, setResetKey] = useState(0); const scrollTargetSelector = '#grw-sidebar-contents-scroll-target'; const calcViewHeight = useCallback(() => { const scrollTargetElem = document.querySelector('#grw-sidebar-contents-scroll-target'); return scrollTargetElem != null ? window.innerHeight - scrollTargetElem?.getBoundingClientRect().top : window.innerHeight; }, []); return ( <>
setResetKey(Math.random())}>
); }; type Props = { } const Sidebar: FC = (props: Props) => { const { data: isDrawerMode } = useDrawerMode(); const { data: isDrawerOpened, mutate: mutateDrawerOpened } = useDrawerOpened(); const { data: currentProductNavWidth, mutate: mutateProductNavWidth } = useCurrentProductNavWidth(); const { data: isCollapsed, mutate: mutateSidebarCollapsed } = useSidebarCollapsed(); const { data: isResizeDisabled, mutate: mutateSidebarResizeDisabled } = useSidebarResizeDisabled(); const [isTransitionEnabled, setTransitionEnabled] = useState(false); const [isHover, setHover] = useState(false); const [isDragging, setDrag] = useState(false); const isResizableByDrag = !isResizeDisabled && !isDrawerMode && (!isCollapsed || isHover); const toggleDrawerMode = useCallback((bool) => { const isStateModified = isResizeDisabled !== bool; if (!isStateModified) { return; } // Drawer <-- Dock if (bool) { // disable resize mutateSidebarResizeDisabled(true, false); } // Drawer --> Dock else { // enable resize mutateSidebarResizeDisabled(false, false); } }, [isResizeDisabled, mutateSidebarResizeDisabled]); const backdropClickedHandler = useCallback(() => { mutateDrawerOpened(false, false); }, [mutateDrawerOpened]); useEffect(() => { setTimeout(() => { setTransitionEnabled(true); }, 1000); }, []); useEffect(() => { toggleDrawerMode(isDrawerMode); }, [isDrawerMode, toggleDrawerMode]); const resizableContainer = useRef(null); const setContentWidth = useCallback((newWidth) => { if (resizableContainer.current == null) { return; } resizableContainer.current.style.width = `${newWidth}px`; }, []); const hoverOnResizableContainerHandler = useCallback(() => { if (!isCollapsed || isDrawerMode || isDragging) { return; } setHover(true); setContentWidth(currentProductNavWidth); }, [isCollapsed, isDrawerMode, isDragging, setContentWidth, currentProductNavWidth]); const hoverOutHandler = useCallback(() => { if (!isCollapsed || isDrawerMode || isDragging) { return; } setHover(false); setContentWidth(sidebarMinimizeWidth); }, [isCollapsed, isDragging, isDrawerMode, setContentWidth]); const toggleNavigationBtnClickHandler = useCallback(() => { const newValue = !isCollapsed; mutateSidebarCollapsed(newValue, false); scheduleToPutUserUISettings({ isSidebarCollapsed: newValue }); }, [isCollapsed, mutateSidebarCollapsed]); useEffect(() => { if (isCollapsed) { setContentWidth(sidebarMinimizeWidth); } else { setContentWidth(currentProductNavWidth); } }, [currentProductNavWidth, isCollapsed, setContentWidth]); const draggableAreaMoveHandler = useCallback((event: MouseEvent) => { event.preventDefault(); const newWidth = event.pageX - 60; if (resizableContainer.current != null) { setContentWidth(newWidth); resizableContainer.current.classList.add('dragging'); } }, [setContentWidth]); const dragableAreaMouseUpHandler = useCallback(() => { if (resizableContainer.current == null) { return; } setDrag(false); if (resizableContainer.current.clientWidth < sidebarMinWidth) { // force collapsed mutateSidebarCollapsed(true); mutateProductNavWidth(sidebarMinWidth, false); scheduleToPutUserUISettings({ isSidebarCollapsed: true, currentProductNavWidth: sidebarMinWidth }); } else { const newWidth = resizableContainer.current.clientWidth; mutateSidebarCollapsed(false); mutateProductNavWidth(newWidth, false); scheduleToPutUserUISettings({ isSidebarCollapsed: false, currentProductNavWidth: newWidth }); } resizableContainer.current.classList.remove('dragging'); }, [mutateProductNavWidth, mutateSidebarCollapsed]); const dragableAreaMouseDownHandler = useCallback((event: React.MouseEvent) => { if (!isResizableByDrag) { return; } event.preventDefault(); setDrag(true); const removeEventListeners = () => { document.removeEventListener('mousemove', draggableAreaMoveHandler); document.removeEventListener('mouseup', dragableAreaMouseUpHandler); document.removeEventListener('mouseup', removeEventListeners); }; document.addEventListener('mousemove', draggableAreaMoveHandler); document.addEventListener('mouseup', dragableAreaMouseUpHandler); document.addEventListener('mouseup', removeEventListeners); }, [dragableAreaMouseUpHandler, draggableAreaMoveHandler, isResizableByDrag]); return ( <>
{ isResizableByDrag && (
) }
{ isDrawerOpened && (
) } ); }; export default Sidebar;