From ce7f0c03f75f0426eae38159a2fea5dce98bd147 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Thu, 18 Dec 2025 01:48:31 -1000 Subject: [PATCH] Fixed command menu and main container layout (#16665) This PR fixes https://github.com/twentyhq/twenty/issues/16645 It solves two problems : - Command menu for mobile was outside of its proper place, it should have been portaled instead of lifted that high in the hierarchy, which is done here, thus avoiding context issues. - CSS was odd due to a code path removing main container styling for mobile display, everything has been cleaned with explicit and durable naming. I used "main container layout" instead of "page layout" to disambiguate from page layout feature. ## Before image ## After image --- .../components/CommandMenuForMobile.tsx | 34 ++++++++++ ...tsx => CommandMenuSidePanelForDesktop.tsx} | 2 +- .../components/CommandMenuPageLayout.tsx | 45 ------------- .../MainContainerLayoutWithCommandMenu.tsx | 65 +++++++++++++++++++ .../components/RecordIndexContainerGater.tsx | 6 +- .../layout/page/components/DefaultLayout.tsx | 14 ---- .../ui/layout/page/components/PageBody.tsx | 6 -- .../ui/layout/page/components/PageHeader.tsx | 1 - .../pages/object-record/RecordShowPage.tsx | 6 +- 9 files changed, 106 insertions(+), 73 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuForMobile.tsx rename packages/twenty-front/src/modules/command-menu/components/{CommandMenuSidePanel.tsx => CommandMenuSidePanelForDesktop.tsx} (98%) delete mode 100644 packages/twenty-front/src/modules/object-record/components/CommandMenuPageLayout.tsx create mode 100644 packages/twenty-front/src/modules/object-record/components/MainContainerLayoutWithCommandMenu.tsx diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuForMobile.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuForMobile.tsx new file mode 100644 index 0000000000..e5abf6cddf --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuForMobile.tsx @@ -0,0 +1,34 @@ +import { CommandMenuOpenContainer } from '@/command-menu/components/CommandMenuOpenContainer'; +import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter'; +import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; +import styled from '@emotion/styled'; + +import { AnimatePresence } from 'framer-motion'; +import { createPortal } from 'react-dom'; +import { useRecoilValue } from 'recoil'; + +const StyledCommandMenuMobileFullScreenContainer = styled.div` + height: 100%; + width: 100%; +`; + +export const CommandMenuForMobile = () => { + const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); + + return ( + + {isCommandMenuOpened && ( + <> + {createPortal( + + + + + , + document.body, + )} + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanel.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanelForDesktop.tsx similarity index 98% rename from packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanel.tsx rename to packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanelForDesktop.tsx index 5beb3815e1..ceee4944c3 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanel.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuSidePanelForDesktop.tsx @@ -53,7 +53,7 @@ const StyledModalContainer = styled.div` const GAP_WIDTH = 8; -export const CommandMenuSidePanel = () => { +export const CommandMenuSidePanelForDesktop = () => { const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); const isCommandMenuClosing = useRecoilValue(isCommandMenuClosingState); const [commandMenuWidth, setCommandMenuWidth] = useRecoilState( diff --git a/packages/twenty-front/src/modules/object-record/components/CommandMenuPageLayout.tsx b/packages/twenty-front/src/modules/object-record/components/CommandMenuPageLayout.tsx deleted file mode 100644 index 924f6e7bbf..0000000000 --- a/packages/twenty-front/src/modules/object-record/components/CommandMenuPageLayout.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { CommandMenuSidePanel } from '@/command-menu/components/CommandMenuSidePanel'; -import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys'; -import { PageBody } from '@/ui/layout/page/components/PageBody'; -import styled from '@emotion/styled'; -import { type ReactNode } from 'react'; -import { useIsMobile } from 'twenty-ui/utilities'; - -type CommandMenuPageLayoutProps = { - children: ReactNode; -}; - -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 1 0; - min-width: 0; - width: 0; - padding-bottom: 0; - padding-right: 0; -`; - -export const CommandMenuPageLayout = ({ - children, -}: CommandMenuPageLayoutProps) => { - const isMobile = useIsMobile(); - - useCommandMenuHotKeys(); - - if (isMobile) { - return <>{children}; - } - - return ( - - {children} - - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/components/MainContainerLayoutWithCommandMenu.tsx b/packages/twenty-front/src/modules/object-record/components/MainContainerLayoutWithCommandMenu.tsx new file mode 100644 index 0000000000..f1bb8ce242 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/components/MainContainerLayoutWithCommandMenu.tsx @@ -0,0 +1,65 @@ +import { CommandMenuForMobile } from '@/command-menu/components/CommandMenuForMobile'; +import { CommandMenuSidePanelForDesktop } from '@/command-menu/components/CommandMenuSidePanelForDesktop'; +import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys'; +import { PageBody } from '@/ui/layout/page/components/PageBody'; +import styled from '@emotion/styled'; +import { type ReactNode } from 'react'; +import { useIsMobile } from 'twenty-ui/utilities'; + +type MainContainerLayoutWithCommandMenuProps = { + children: ReactNode; +}; + +const StyledMainContainerLayoutForDesktop = styled.div` + display: flex; + flex: 1; + min-height: 0; + padding-bottom: ${({ theme }) => theme.spacing(3)}; + padding-right: ${({ theme }) => theme.spacing(3)}; +`; + +const StyledPageBodyForDesktop = styled(PageBody)` + flex: 1 1 0; + min-width: 0; + width: 0; + padding-bottom: 0; + padding-right: 0; +`; + +const StyledMainContainerLayoutForMobile = styled.div` + display: flex; + flex: 1; + min-height: 0; + padding: 0; +`; + +const StyledPageBodyForMobile = styled(PageBody)` + padding-bottom: 0; + padding-left: ${({ theme }) => theme.spacing(1)}; + + padding-right: ${({ theme }) => theme.spacing(1.5)}; +`; + +export const MainContainerLayoutWithCommandMenu = ({ + children, +}: MainContainerLayoutWithCommandMenuProps) => { + const isMobile = useIsMobile(); + + useCommandMenuHotKeys(); + + if (isMobile) { + return ( + + {children} + + + ); + } + + return ( + + {children} + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx index 90f34b26b2..0cd969aac4 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx @@ -3,7 +3,7 @@ import { RecordIndexContextProvider } from '@/object-record/record-index/context import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; -import { CommandMenuPageLayout } from '@/object-record/components/CommandMenuPageLayout'; +import { MainContainerLayoutWithCommandMenu } from '@/object-record/components/MainContainerLayoutWithCommandMenu'; import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { lastShowPageRecordIdState } from '@/object-record/record-field/ui/states/lastShowPageRecordId'; @@ -98,14 +98,14 @@ export const RecordIndexContainerGater = () => { > - + - + diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx index 0e4c98aa7d..2568084ba9 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx @@ -1,7 +1,4 @@ import { AuthModal } from '@/auth/components/AuthModal'; -import { CommandMenuOpenContainer } from '@/command-menu/components/CommandMenuOpenContainer'; -import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter'; -import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary'; import { AppFullScreenErrorFallback } from '@/error-handler/components/AppFullScreenErrorFallback'; import { AppPageErrorFallback } from '@/error-handler/components/AppPageErrorFallback'; @@ -21,7 +18,6 @@ import { Global, css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { AnimatePresence, LayoutGroup, motion } from 'framer-motion'; import { Outlet } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; import { useScreenSize } from 'twenty-ui/utilities'; const StyledLayout = styled.div` @@ -67,7 +63,6 @@ export const DefaultLayout = () => { const windowsWidth = useScreenSize().width; const showAuthModal = useShowAuthModal(); const useShowFullScreen = useShowFullscreen(); - const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); return ( <> @@ -124,15 +119,6 @@ export const DefaultLayout = () => { )} {isMobile && !showAuthModal && } - {isMobile && ( - - {isCommandMenuOpened && ( - - - - )} - - )} diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/PageBody.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/PageBody.tsx index 103d9bc1d2..eaee8fcaab 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/PageBody.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/PageBody.tsx @@ -1,7 +1,6 @@ import styled from '@emotion/styled'; import { type ReactNode } from 'react'; -import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; import { PagePanel } from './PagePanel'; type PageBodyProps = { @@ -21,11 +20,6 @@ const StyledMainContainer = styled.div` padding-right: ${({ theme }) => theme.spacing(3)}; padding-left: 0; width: 100%; - - @media (max-width: ${MOBILE_VIEWPORT}px) { - padding-left: ${({ theme }) => theme.spacing(3)}; - padding-bottom: 0; - } `; type LeftContainerProps = { diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx index ceba37fa92..0b95df4336 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx @@ -133,7 +133,6 @@ export const PageHeader = ({ )} - diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx index 6855251739..ed4fd05d9b 100644 --- a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx @@ -5,7 +5,7 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; -import { CommandMenuPageLayout } from '@/object-record/components/CommandMenuPageLayout'; +import { MainContainerLayoutWithCommandMenu } from '@/object-record/components/MainContainerLayoutWithCommandMenu'; import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper'; import { PageLayoutDispatcher } from '@/object-record/record-show/components/PageLayoutDispatcher'; import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; @@ -51,7 +51,7 @@ export const RecordShowPage = () => { - + { }} /> - +