Improve command menu animation (#16197)

https://github.com/user-attachments/assets/c9ff288d-0e68-4877-af6b-8685a6bbeeaf


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Removes legacy layout and refactors `CommandMenuPageLayout` to use
width-based animation with shared constants and close-animation cleanup.
> 
> - **UI/Command Menu**:
>   - **Refactor `CommandMenuPageLayout`**:
> - Switch to width/margin animation on `StyledSidePanelWrapper`; remove
side panel `x` translate animation.
>     - Use shared `COMMAND_MENU_SIDE_PANEL_WIDTH` constant.
> - Add close-state handling via `isCommandMenuClosingState` and
`useCommandMenuCloseAnimationCompleteCleanup` on animation completion.
> - Simplify props: remove `isSidePanelOpen`; rely on
`isCommandMenuOpenedState` and internal `shouldRenderContent`.
>   - **Cleanup**:
>     - Remove obsolete `CommandMenuLayout.tsx`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d6cb01114c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <[email protected]>
This commit is contained in:
Abdul Rahman
2025-12-01 15:27:29 +01:00
committed by GitHub
co-authored by Félix Malfait
parent f489bbdbab
commit b5ec6df62f
2 changed files with 0 additions and 154 deletions
@@ -1,151 +0,0 @@
import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { tableWidthResizeIsActiveState } from '@/object-record/record-table/states/tableWidthResizeIsActivedState';
import { ModalContainerContext } from '@/ui/layout/modal/contexts/ModalContainerContext';
import { PageBody } from '@/ui/layout/page/components/PageBody';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { type ReactNode, useCallback, useState } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useIsMobile } from 'twenty-ui/utilities';
type CommandMenuPageLayoutProps = {
children: ReactNode;
isSidePanelOpen?: boolean;
};
const DEFAULT_SIDE_PANEL_WIDTH = 400;
const StyledLayout = styled.div`
display: flex;
flex: 1;
min-height: 0;
padding-bottom: ${({ theme }) => theme.spacing(3)};
padding-right: ${({ theme }) => theme.spacing(3)};
`;
const StyledPageBody = styled(PageBody)`
flex: 1;
min-width: 0;
padding-bottom: 0;
padding-right: 0;
`;
const StyledSidePanelWrapper = styled(motion.div)`
flex-shrink: 0;
min-width: 0;
overflow: hidden;
`;
const StyledSidePanel = styled(motion.aside)`
background: ${({ theme }) => theme.background.primary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
position: relative;
width: ${DEFAULT_SIDE_PANEL_WIDTH}px;
box-sizing: border-box;
`;
const StyledModalContainer = styled.div`
height: 100%;
left: 0;
pointer-events: none;
position: absolute;
top: 0;
width: 100%;
z-index: 1;
`;
export const CommandMenuPageLayout = ({
children,
isSidePanelOpen,
}: CommandMenuPageLayoutProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const [modalContainer, setModalContainer] = useState<HTMLDivElement | null>(
null,
);
const setTableWidthResizeIsActive = useSetRecoilState(
tableWidthResizeIsActiveState,
);
const resolvedIsSidePanelOpen =
isSidePanelOpen ?? isCommandMenuOpened ?? false;
const [shouldRenderContent, setShouldRenderContent] = useState(
resolvedIsSidePanelOpen,
);
const shouldShowContent = resolvedIsSidePanelOpen || shouldRenderContent;
const handleAnimationComplete = () => {
if (!resolvedIsSidePanelOpen) {
setShouldRenderContent(false);
}
setTableWidthResizeIsActive(true);
};
const handleAnimationStart = () => {
if (resolvedIsSidePanelOpen && !shouldRenderContent) {
setShouldRenderContent(true);
}
setTableWidthResizeIsActive(false);
};
const handleModalContainerRef = useCallback(
(element: HTMLDivElement | null) => {
setModalContainer(element);
},
[],
);
useCommandMenuHotKeys();
if (isMobile) {
return <>{children}</>;
}
return (
<StyledLayout>
<StyledPageBody>{children}</StyledPageBody>
<StyledSidePanelWrapper
initial={false}
animate={{
width: resolvedIsSidePanelOpen ? DEFAULT_SIDE_PANEL_WIDTH : 0,
marginLeft: resolvedIsSidePanelOpen ? theme.spacing(2) : 0,
}}
transition={{
duration: theme.animation.duration.normal,
}}
onAnimationStart={handleAnimationStart}
onAnimationComplete={handleAnimationComplete}
>
<StyledSidePanel
initial={false}
animate={{
x: resolvedIsSidePanelOpen ? 0 : DEFAULT_SIDE_PANEL_WIDTH,
}}
transition={{
duration: theme.animation.duration.normal,
}}
>
<StyledModalContainer ref={handleModalContainerRef} />
<ModalContainerContext.Provider value={{ container: modalContainer }}>
{shouldShowContent && <CommandMenuRouter />}
</ModalContainerContext.Provider>
</StyledSidePanel>
</StyledSidePanelWrapper>
</StyledLayout>
);
};
@@ -135,9 +135,6 @@ export const CommandMenuPageLayout = ({
>
<StyledSidePanel
initial={false}
animate={{
x: isCommandMenuOpened ? 0 : COMMAND_MENU_SIDE_PANEL_WIDTH,
}}
transition={{
duration: theme.animation.duration.normal,
}}