feat: add resizable navigation drawer and command menu panels (#16612)
## Summary Adds Notion-style resizable panels for the navigation drawer (left sidebar) and command menu (right panel). ## Behavior - **Hover** at panel edge → resize cursor appears - **Click** → collapse/close the panel - **Drag** → resize the panel (5px movement threshold to distinguish from click) ## Constraints | Panel | Min | Max | Default | Collapse Threshold | |-------|-----|-----|---------|-------------------| | Navigation Drawer | 180px | 350px | 220px | 150px | | Command Menu | 320px | 600px | 400px | 250px | ## Performance Optimizations - **CSS variables** for smooth 60fps resize (no React re-renders during drag) - **Table resize observer disabled** during panel resize to prevent expensive recalculations - **React.memo wrapper** on page body to prevent unnecessary re-renders ## Architecture - `useResizablePanel` hook following the same pattern as `useResizeTableHeader` - `ResizablePanelEdge` - resize handle positioned at panel edge - `ResizablePanelGap` - resize handle in the gap between panels - `cssVariableEffect` - Recoil effect to sync CSS variables with state ## Refactoring - Split `recoil-effects.ts` into separate files in `utils/recoil/` (one export per file) - Persist panel widths to localStorage via existing `localStorageEffect`
This commit is contained in:
@@ -3,7 +3,7 @@ import { motion } from 'framer-motion';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
||||
import { NAVIGATION_DRAWER_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/NavigationDrawerConstraints';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { ANIMATION } from 'twenty-ui/theme';
|
||||
@@ -54,9 +54,7 @@ export const LeftPanelSkeletonLoader = () => {
|
||||
<StyledAnimatedContainer
|
||||
initial={false}
|
||||
animate={{
|
||||
width: isMobile
|
||||
? NAV_DRAWER_WIDTHS.menu.mobile.collapsed
|
||||
: NAV_DRAWER_WIDTHS.menu.desktop.expanded,
|
||||
width: isMobile ? 0 : NAVIGATION_DRAWER_CONSTRAINTS.default,
|
||||
opacity: isMobile ? 0 : 1,
|
||||
}}
|
||||
transition={{ duration: ANIMATION.duration.fast }}
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
||||
import { NAVIGATION_DRAWER_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/NavigationDrawerConstraints';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonLoader';
|
||||
import { RightPanelSkeletonLoader } from '~/loading/components/RightPanelSkeletonLoader';
|
||||
@@ -14,7 +14,7 @@ const StyledContainer = styled.div`
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
height: 100dvh;
|
||||
min-width: ${NAV_DRAWER_WIDTHS.menu.desktop.expanded}px;
|
||||
min-width: ${NAVIGATION_DRAWER_CONSTRAINTS.default}px;
|
||||
width: 100%;
|
||||
padding: 12px 8px 12px 8px;
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user