From 10443b3c02b30d2b3c49daf8e0a107e66f89a816 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Fri, 22 Mar 2024 10:43:10 -0700 Subject: [PATCH] Separate floating sidebar hidden state --- src-web/components/Sidebar.tsx | 7 ++--- src-web/components/SidebarActions.tsx | 25 +++++++++++----- src-web/components/Workspace.tsx | 36 ++++++++++------------- src-web/hooks/useFloatingSidebarHidden.ts | 13 ++++++++ src-web/hooks/useShouldFloatSidebar.ts | 8 +++++ src-web/hooks/useSidebarHidden.ts | 10 +------ 6 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 src-web/hooks/useFloatingSidebarHidden.ts create mode 100644 src-web/hooks/useShouldFloatSidebar.ts diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index c180586c..c8065391 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -56,7 +56,7 @@ interface TreeNode { } export function Sidebar({ className }: Props) { - const { hidden, show, hide } = useSidebarHidden(); + const [hidden, setHidden] = useSidebarHidden(); const sidebarRef = useRef(null); const activeRequest = useActiveRequest(); const activeEnvironmentId = useActiveEnvironmentId(); @@ -241,16 +241,15 @@ export function Sidebar({ className }: Props) { useKeyPressEvent('Delete', handleDeleteKey); useHotKey('sidebar.focus', async () => { - console.log('sidebar.focus', { hidden, hasFocus }); // Hide the sidebar if it's already focused if (!hidden && hasFocus) { - await hide(); + await setHidden(true); return; } // Show the sidebar if it's hidden if (hidden) { - await show(); + await setHidden(false); } // Select 0 index on focus if none selected diff --git a/src-web/components/SidebarActions.tsx b/src-web/components/SidebarActions.tsx index 8ff42c08..33499817 100644 --- a/src-web/components/SidebarActions.tsx +++ b/src-web/components/SidebarActions.tsx @@ -1,12 +1,22 @@ -import { memo } from 'react'; +import { useMemo } from 'react'; +import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden'; +import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { trackEvent } from '../lib/analytics'; import { IconButton } from './core/IconButton'; import { HStack } from './core/Stacks'; import { CreateDropdown } from './CreateDropdown'; -export const SidebarActions = memo(function SidebarActions() { - const { hidden, show, hide } = useSidebarHidden(); +export function SidebarActions() { + const floating = useShouldFloatSidebar(); + const [normalHidden, setNormalHidden] = useSidebarHidden(); + const [floatingHidden, setFloatingHidden] = useFloatingSidebarHidden(); + + const hidden = floating ? floatingHidden : normalHidden; + const setHidden = useMemo( + () => (floating ? setFloatingHidden : setNormalHidden), + [floating, setFloatingHidden, setNormalHidden], + ); return ( @@ -14,10 +24,9 @@ export const SidebarActions = memo(function SidebarActions() { onClick={async () => { trackEvent('sidebar', 'toggle'); - // NOTE: We're not using `toggle` because it may be out of sync - // from changes in other windows - if (hidden) await show(); - else await hide(); + // NOTE: We're not using the (h) => !h pattern here because the data + // might be different if another window changed it (out of sync) + await setHidden(!hidden); }} className="pointer-events-auto" size="sm" @@ -35,4 +44,4 @@ export const SidebarActions = memo(function SidebarActions() { ); -}); +} diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 35c5dc0a..844e434c 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -6,14 +6,16 @@ import type { MouseEvent as ReactMouseEvent, ReactNode, } from 'react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId'; +import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden'; import { useImportData } from '../hooks/useImportData'; import { useIsFullscreen } from '../hooks/useIsFullscreen'; import { useOsInfo } from '../hooks/useOsInfo'; +import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarWidth } from '../hooks/useSidebarWidth'; import { useWorkspaces } from '../hooks/useWorkspaces'; @@ -37,34 +39,22 @@ const head = { gridArea: 'head' }; const body = { gridArea: 'body' }; const drag = { gridArea: 'drag' }; -const WINDOW_FLOATING_SIDEBAR_WIDTH = 600; - export default function Workspace() { const workspaces = useWorkspaces(); const activeWorkspace = useActiveWorkspace(); const activeWorkspaceId = useActiveWorkspaceId(); const { setWidth, width, resetWidth } = useSidebarWidth(); - const { hide, show, hidden } = useSidebarHidden(); + const [sidebarHidden, setSidebarHidden] = useSidebarHidden(); + const [floatingSidebarHidden, setFloatingSidebarHidden] = useFloatingSidebarHidden(); const activeRequest = useActiveRequest(); const windowSize = useWindowSize(); const importData = useImportData(); - const [floating, setFloating] = useState(false); + const floating = useShouldFloatSidebar(); const [isResizing, setIsResizing] = useState(false); const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>( null, ); - // float/un-float sidebar on window resize - useEffect(() => { - const shouldHide = windowSize.width <= WINDOW_FLOATING_SIDEBAR_WIDTH; - if (shouldHide && !floating) { - setFloating(true); - hide().catch(console.error); - } else if (!shouldHide && floating) { - setFloating(false); - } - }, [floating, hide, windowSize.width]); - const unsub = () => { if (moveState.current !== null) { document.documentElement.removeEventListener('mousemove', moveState.current.move); @@ -84,10 +74,10 @@ export default function Workspace() { e.preventDefault(); // Prevent text selection and things const newWidth = startWidth + (e.clientX - mouseStartX); if (newWidth < 50) { - await hide(); + await setSidebarHidden(true); resetWidth(); } else { - await show(); + await setSidebarHidden(false); setWidth(newWidth); } }, @@ -101,10 +91,10 @@ export default function Workspace() { document.documentElement.addEventListener('mouseup', moveState.current.up); setIsResizing(true); }, - [setWidth, resetWidth, width, hide, show], + [width, setSidebarHidden, resetWidth, setWidth], ); - const sideWidth = hidden ? 0 : width; + const sideWidth = sidebarHidden ? 0 : width; const styles = useMemo( () => ({ gridTemplate: floating @@ -144,7 +134,11 @@ export default function Workspace() { )} > {floating ? ( - + setFloatingSidebarHidden(true)} + > ({ + namespace: 'no_sync', + key: ['floating_sidebar_hidden', activeWorkspaceId ?? 'n/a'], + fallback: false, + }); + + return [value, set] as const; +} diff --git a/src-web/hooks/useShouldFloatSidebar.ts b/src-web/hooks/useShouldFloatSidebar.ts new file mode 100644 index 00000000..8e8159d6 --- /dev/null +++ b/src-web/hooks/useShouldFloatSidebar.ts @@ -0,0 +1,8 @@ +import { useWindowSize } from 'react-use'; + +const WINDOW_FLOATING_SIDEBAR_WIDTH = 600; + +export function useShouldFloatSidebar() { + const windowSize = useWindowSize(); + return windowSize.width <= WINDOW_FLOATING_SIDEBAR_WIDTH; +} diff --git a/src-web/hooks/useSidebarHidden.ts b/src-web/hooks/useSidebarHidden.ts index e2d06536..5a06ad55 100644 --- a/src-web/hooks/useSidebarHidden.ts +++ b/src-web/hooks/useSidebarHidden.ts @@ -1,4 +1,3 @@ -import { useMemo } from 'react'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useKeyValue } from './useKeyValue'; @@ -10,12 +9,5 @@ export function useSidebarHidden() { fallback: false, }); - return useMemo(() => { - return { - show: () => set(false), - hide: () => set(true), - toggle: () => set((h) => !h), - hidden: value, - }; - }, [set, value]); + return [value, set] as const; }