4fe8e3d3b6
## 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`
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
import styled from '@emotion/styled';
|
|
|
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
|
import { Modal } from '@/ui/layout/modal/components/Modal';
|
|
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';
|
|
|
|
const StyledContainer = styled.div`
|
|
background: ${({ theme }) => theme.background.noisy};
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 12px;
|
|
height: 100dvh;
|
|
min-width: ${NAVIGATION_DRAWER_CONSTRAINTS.default}px;
|
|
width: 100%;
|
|
padding: 12px 8px 12px 8px;
|
|
overflow: hidden;
|
|
|
|
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
|
width: 100%;
|
|
}
|
|
`;
|
|
|
|
export const UserOrMetadataLoader = () => {
|
|
const showAuthModal = useShowAuthModal();
|
|
|
|
return (
|
|
<StyledContainer>
|
|
{showAuthModal && <Modal.Backdrop modalVariant="primary" />}
|
|
<LeftPanelSkeletonLoader />
|
|
<RightPanelSkeletonLoader />
|
|
</StyledContainer>
|
|
);
|
|
};
|