Files
twenty/packages/twenty-front/src/loading/components/UserOrMetadataLoader.tsx
T
Félix Malfait 4fe8e3d3b6 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`
2025-12-18 09:09:21 +01:00

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>
);
};