[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
This commit is contained in:
committed by
GitHub
parent
58b8b49d1d
commit
2466d81ace
@@ -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 (
|
||||
<StyledContainer isInPinnedTab={isInPinnedTab}>
|
||||
{isPageLayoutInEditMode ? (
|
||||
<PageLayoutVerticalListEditor
|
||||
widgets={activeTab.widgets}
|
||||
onReorder={reorderWidgets}
|
||||
/>
|
||||
) : (
|
||||
<PageLayoutVerticalListViewer widgets={activeTab.widgets} />
|
||||
)}
|
||||
</StyledContainer>
|
||||
return isPageLayoutInEditMode ? (
|
||||
<PageLayoutVerticalListEditor
|
||||
widgets={activeTab.widgets}
|
||||
onReorder={reorderWidgets}
|
||||
/>
|
||||
) : (
|
||||
<PageLayoutVerticalListViewer widgets={activeTab.widgets} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<StyledGridContainer ref={gridContainerRef}>
|
||||
{isPageLayoutInEditMode && (
|
||||
<>
|
||||
<PageLayoutGridOverlay />
|
||||
<PageLayoutGridLayoutDragSelector
|
||||
gridContainerRef={gridContainerRef}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<StyledGridContainer ref={gridContainerRef}>
|
||||
{isPageLayoutInEditMode && (
|
||||
<>
|
||||
<PageLayoutGridOverlay />
|
||||
<PageLayoutGridLayoutDragSelector
|
||||
gridContainerRef={gridContainerRef}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ResponsiveGridLayout
|
||||
className="layout"
|
||||
layouts={layouts}
|
||||
breakpoints={PAGE_LAYOUT_CONFIG.breakpoints}
|
||||
cols={PAGE_LAYOUT_CONFIG.columns}
|
||||
rowHeight={PAGE_LAYOUT_GRID_ROW_HEIGHT}
|
||||
maxCols={12}
|
||||
containerPadding={[0, 0]}
|
||||
margin={[PAGE_LAYOUT_GRID_MARGIN, PAGE_LAYOUT_GRID_MARGIN]}
|
||||
isDraggable={isPageLayoutInEditMode}
|
||||
isResizable={isPageLayoutInEditMode}
|
||||
draggableHandle=".drag-handle"
|
||||
compactType="vertical"
|
||||
preventCollision={false}
|
||||
resizeHandle={
|
||||
isPageLayoutInEditMode ? <PageLayoutGridResizeHandle /> : 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) => (
|
||||
<ReactGridLayoutCardWrapper key={item.id}>
|
||||
{item.type === 'placeholder' ? (
|
||||
<WidgetPlaceholder />
|
||||
) : (
|
||||
<WidgetRenderer widget={item.widget} />
|
||||
)}
|
||||
</ReactGridLayoutCardWrapper>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
</StyledGridContainer>
|
||||
</>
|
||||
<ResponsiveGridLayout
|
||||
className="layout"
|
||||
layouts={layouts}
|
||||
breakpoints={PAGE_LAYOUT_CONFIG.breakpoints}
|
||||
cols={PAGE_LAYOUT_CONFIG.columns}
|
||||
rowHeight={PAGE_LAYOUT_GRID_ROW_HEIGHT}
|
||||
maxCols={12}
|
||||
containerPadding={[0, 0]}
|
||||
margin={[PAGE_LAYOUT_GRID_MARGIN, PAGE_LAYOUT_GRID_MARGIN]}
|
||||
isDraggable={isPageLayoutInEditMode}
|
||||
isResizable={isPageLayoutInEditMode}
|
||||
draggableHandle=".drag-handle"
|
||||
compactType="vertical"
|
||||
preventCollision={false}
|
||||
resizeHandle={
|
||||
isPageLayoutInEditMode ? <PageLayoutGridResizeHandle /> : 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) => (
|
||||
<ReactGridLayoutCardWrapper key={item.id}>
|
||||
{item.type === 'placeholder' ? (
|
||||
<WidgetPlaceholder />
|
||||
) : (
|
||||
<WidgetRenderer widget={item.widget} />
|
||||
)}
|
||||
</ReactGridLayoutCardWrapper>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
</StyledGridContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<ShowPageLeftContainer>
|
||||
<StyledContainer>
|
||||
<SummaryCard
|
||||
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
|
||||
objectRecordId={targetRecordIdentifier.id}
|
||||
@@ -44,8 +56,14 @@ export const PageLayoutLeftPanel = ({
|
||||
layoutMode,
|
||||
}}
|
||||
>
|
||||
<PageLayoutContent />
|
||||
<ScrollWrapper
|
||||
componentInstanceId={`page-layout-left-panel-${pinnedLeftTabId}`}
|
||||
defaultEnableXScroll={false}
|
||||
defaultEnableYScroll={true}
|
||||
>
|
||||
<PageLayoutContent />
|
||||
</ScrollWrapper>
|
||||
</PageLayoutContentProvider>
|
||||
</ShowPageLeftContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+40
-44
@@ -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 (
|
||||
<ShowPageContainer>
|
||||
<StyledContainer hasPinnedTab={isDefined(pinnedLeftTab)}>
|
||||
{isDefined(pinnedLeftTab) && (
|
||||
<PageLayoutLeftPanel pinnedLeftTabId={pinnedLeftTab.id} />
|
||||
)}
|
||||
|
||||
<StyledShowPageRightContainer>
|
||||
<StyledTabsAndDashboardContainer>
|
||||
<PageLayoutTabListEffect
|
||||
<StyledTabsAndDashboardContainer>
|
||||
<PageLayoutTabListEffect
|
||||
tabs={sortedTabs}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
defaultTabIdToFocusOnMobileAndSidePanel={
|
||||
currentPageLayout.defaultTabIdToFocusOnMobileAndSidePanel
|
||||
}
|
||||
/>
|
||||
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
|
||||
<StyledPageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
defaultTabIdToFocusOnMobileAndSidePanel={
|
||||
currentPageLayout.defaultTabIdToFocusOnMobileAndSidePanel
|
||||
}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={isPageLayoutInEditMode}
|
||||
onReorder={isPageLayoutInEditMode ? reorderTabs : undefined}
|
||||
/>
|
||||
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
|
||||
<StyledPageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={isPageLayoutInEditMode}
|
||||
onReorder={isPageLayoutInEditMode ? reorderTabs : undefined}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
|
||||
<PageLayoutTabHeader />
|
||||
<StyledScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</StyledScrollWrapper>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
</StyledShowPageRightContainer>
|
||||
</ShowPageContainer>
|
||||
<PageLayoutTabHeader />
|
||||
|
||||
<StyledScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</StyledScrollWrapper>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+1
@@ -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 }>`
|
||||
|
||||
+1
@@ -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 = {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH = 348;
|
||||
+3
-1
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user