From 2466d81acee1667387ed1d6d6f3a3080b7e667b5 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Fri, 5 Dec 2025 16:10:08 +0100 Subject: [PATCH] [Record Page Layouts] Pin record header (#16339) https://github.com/user-attachments/assets/df988bf2-d2d6-49a4-b062-bc7ec49e1fad Closes https://github.com/twentyhq/core-team-issues/issues/1806 --- .../components/PageLayoutContent.tsx | 32 ++--- .../components/PageLayoutGridLayout.tsx | 115 +++++++++--------- .../components/PageLayoutLeftPanel.tsx | 26 +++- .../components/PageLayoutRendererContent.tsx | 84 ++++++------- .../PageLayoutVerticalListEditor.tsx | 1 + .../PageLayoutVerticalListViewer.tsx | 1 + .../PageLayoutLeftPanelContainerWidth.ts | 1 + .../components/ShowPageLeftContainer.tsx | 4 +- 8 files changed, 130 insertions(+), 134 deletions(-) create mode 100644 packages/twenty-front/src/modules/page-layout/constants/PageLayoutLeftPanelContainerWidth.ts diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx index a5c3b1154a..70eddeb81e 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx @@ -6,23 +6,10 @@ import { usePageLayoutContentContext } from '@/page-layout/contexts/PageLayoutCo import { usePageLayoutTabWithVisibleWidgetsOrThrow } from '@/page-layout/hooks/usePageLayoutTabWithVisibleWidgetsOrThrow'; import { useReorderPageLayoutWidgets } from '@/page-layout/hooks/useReorderPageLayoutWidgets'; import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState'; -import { useIsInPinnedTab } from '@/page-layout/widgets/hooks/useIsInPinnedTab'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import styled from '@emotion/styled'; import { FeatureFlagKey } from '~/generated/graphql'; -const StyledContainer = styled.div<{ isInPinnedTab: boolean }>` - background: ${({ theme }) => theme.background.primary}; - box-sizing: border-box; - flex: 1; - min-height: 100%; - position: relative; - width: 100%; - padding: ${({ theme, isInPinnedTab }) => - isInPinnedTab ? 0 : theme.spacing(2)}; -`; - export const PageLayoutContent = () => { const isRecordPageEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_ENABLED, @@ -39,7 +26,6 @@ export const PageLayoutContent = () => { const activeTab = usePageLayoutTabWithVisibleWidgetsOrThrow(tabId); const { layoutMode } = usePageLayoutContentContext(); - const { isInPinnedTab } = useIsInPinnedTab(); const isCanvasLayout = isRecordPageEnabled && layoutMode === 'canvas'; const isVerticalList = isRecordPageEnabled && layoutMode === 'vertical-list'; @@ -49,17 +35,13 @@ export const PageLayoutContent = () => { } if (isVerticalList) { - return ( - - {isPageLayoutInEditMode ? ( - - ) : ( - - )} - + return isPageLayoutInEditMode ? ( + + ) : ( + ); } diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx index aa69e31889..c7c2aea5c8 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx @@ -36,7 +36,6 @@ import { import { isDefined } from 'twenty-shared/utils'; const StyledGridContainer = styled.div` - background: ${({ theme }) => theme.background.primary}; box-sizing: border-box; flex: 1; min-height: 100%; @@ -139,65 +138,61 @@ export const PageLayoutGridLayout = ({ tabId }: PageLayoutGridLayoutProps) => { ); return ( - <> - - {isPageLayoutInEditMode && ( - <> - - - - )} + + {isPageLayoutInEditMode && ( + <> + + + + )} - : undefined - } - resizeHandles={['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw']} - onDragStart={(_layout, _oldItem, newItem) => { - setDraggingWidgetId(newItem.i); - }} - onDragStop={() => { - setDraggingWidgetId(null); - }} - onResizeStart={(_layout, _oldItem, newItem) => { - setResizingWidgetId(newItem.i); - }} - onResizeStop={() => { - setResizingWidgetId(null); - }} - onLayoutChange={handleLayoutChangeWithoutPendingPlaceholder} - onBreakpointChange={(newBreakpoint) => - setPageLayoutCurrentBreakpoint( - newBreakpoint as PageLayoutBreakpoint, - ) - } - > - {gridLayoutItems.map((item) => ( - - {item.type === 'placeholder' ? ( - - ) : ( - - )} - - ))} - - - + : undefined + } + resizeHandles={['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw']} + onDragStart={(_layout, _oldItem, newItem) => { + setDraggingWidgetId(newItem.i); + }} + onDragStop={() => { + setDraggingWidgetId(null); + }} + onResizeStart={(_layout, _oldItem, newItem) => { + setResizingWidgetId(newItem.i); + }} + onResizeStop={() => { + setResizingWidgetId(null); + }} + onLayoutChange={handleLayoutChangeWithoutPendingPlaceholder} + onBreakpointChange={(newBreakpoint) => + setPageLayoutCurrentBreakpoint(newBreakpoint as PageLayoutBreakpoint) + } + > + {gridLayoutItems.map((item) => ( + + {item.type === 'placeholder' ? ( + + ) : ( + + )} + + ))} + + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutLeftPanel.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutLeftPanel.tsx index dacc75537a..07245421c0 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutLeftPanel.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutLeftPanel.tsx @@ -6,9 +6,21 @@ import { usePageLayoutTabWithVisibleWidgetsOrThrow } from '@/page-layout/hooks/u import { getTabLayoutMode } from '@/page-layout/utils/getTabLayoutMode'; import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord'; -import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer'; +import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import styled from '@emotion/styled'; import { PageLayoutType } from '~/generated/graphql'; +const StyledContainer = styled.div` + background: ${({ theme }) => theme.background.secondary}; + border-bottom-left-radius: 8px; + border-right: ${({ theme }) => `1px solid ${theme.border.color.medium}`}; + border-top-left-radius: 8px; + box-sizing: border-box; + display: grid; + grid-template-rows: auto 1fr; + height: 100%; +`; + type PageLayoutLeftPanelProps = { pinnedLeftTabId: string; }; @@ -31,7 +43,7 @@ export const PageLayoutLeftPanel = ({ }); return ( - + - + + + - + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx index 65c26fa822..5e56df43c3 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx @@ -4,6 +4,7 @@ import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPane import { PageLayoutTabHeader } from '@/page-layout/components/PageLayoutTabHeader'; import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList'; import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect'; +import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth'; import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutTab'; import { useCurrentPageLayout } from '@/page-layout/hooks/useCurrentPageLayout'; import { useReorderPageLayoutTabs } from '@/page-layout/hooks/useReorderPageLayoutTabs'; @@ -16,7 +17,6 @@ import { getTabsByDisplayMode } from '@/page-layout/utils/getTabsByDisplayMode'; import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibleWidgets'; import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition'; import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; -import { ShowPageContainer } from '@/ui/layout/page/components/ShowPageContainer'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; @@ -25,22 +25,19 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state import styled from '@emotion/styled'; import { isDefined } from 'twenty-shared/utils'; +const StyledContainer = styled.div<{ hasPinnedTab: boolean }>` + display: grid; + grid-template-columns: ${({ hasPinnedTab }) => + hasPinnedTab ? `${PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH}px 1fr` : '1fr'}; + grid-template-rows: minmax(0, 1fr); + height: 100%; + width: 100%; +`; + const StyledTabsAndDashboardContainer = styled.div` display: flex; flex-direction: column; - height: 100%; - width: 100%; overflow: hidden; - background: ${({ theme }) => theme.background.primary}; -`; - -const StyledShowPageRightContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; - justify-content: start; - width: 100%; - overflow: auto; `; const StyledPageLayoutTabList = styled(PageLayoutTabList)` @@ -107,44 +104,43 @@ export const PageLayoutRendererContent = () => { const sortedTabs = sortTabsByPosition(tabsToRenderInTabList); return ( - + {isDefined(pinnedLeftTab) && ( )} - - - + + {(sortedTabs.length > 1 || isPageLayoutInEditMode) && ( + - {(sortedTabs.length > 1 || isPageLayoutInEditMode) && ( - - )} + )} - - - {isDefined(activeTabId) && ( - - )} - - - - + + + + {isDefined(activeTabId) && ( + + )} + + + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListEditor.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListEditor.tsx index 1309f2be56..b6f9f22997 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListEditor.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListEditor.tsx @@ -15,6 +15,7 @@ const StyledVerticalListContainer = styled.div` display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(2)}; + padding: ${({ theme }) => theme.spacing(2)}; `; const StyledDraggableWrapper = styled.div<{ isDragging: boolean }>` diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListViewer.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListViewer.tsx index b33c198696..4b6589d66f 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListViewer.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListViewer.tsx @@ -6,6 +6,7 @@ const StyledVerticalListContainer = styled.div` display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(2)}; + padding: ${({ theme }) => theme.spacing(2)}; `; type PageLayoutVerticalListViewerProps = { diff --git a/packages/twenty-front/src/modules/page-layout/constants/PageLayoutLeftPanelContainerWidth.ts b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutLeftPanelContainerWidth.ts new file mode 100644 index 0000000000..e5a0a32951 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutLeftPanelContainerWidth.ts @@ -0,0 +1 @@ +export const PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH = 348; diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx index 5261441f12..038dccacb2 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import { type ReactNode } from 'react'; +import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; @@ -20,7 +21,8 @@ const StyledOuterContainer = styled.div<{ isMobile: boolean }>` const StyledInnerContainer = styled.div<{ isMobile: boolean }>` display: flex; flex-direction: column; - width: ${({ isMobile }) => (isMobile ? `100%` : '348px')}; + width: ${({ isMobile }) => + isMobile ? `100%` : `${PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH}px`}; `; const StyledIntermediateContainer = styled.div`