Record page layout edition frontend (#17519)

Hidden behind a new feature flag:
`IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED`


https://github.com/user-attachments/assets/112f5a8d-0dd0-4b9f-8825-9980db35fcbd
This commit is contained in:
Baptiste Devessier
2026-01-29 12:13:09 +01:00
committed by GitHub
parent 97a37604bd
commit b15d092abd
22 changed files with 369 additions and 79 deletions
@@ -3,12 +3,13 @@ import { PageLayoutGridLayout } from '@/page-layout/components/PageLayoutGridLay
import { PageLayoutVerticalListEditor } from '@/page-layout/components/PageLayoutVerticalListEditor';
import { PageLayoutVerticalListViewer } from '@/page-layout/components/PageLayoutVerticalListViewer';
import { usePageLayoutContentContext } from '@/page-layout/contexts/PageLayoutContentContext';
import { useCurrentPageLayoutOrThrow } from '@/page-layout/hooks/useCurrentPageLayoutOrThrow';
import { usePageLayoutTabWithVisibleWidgetsOrThrow } from '@/page-layout/hooks/usePageLayoutTabWithVisibleWidgetsOrThrow';
import { useReorderPageLayoutWidgets } from '@/page-layout/hooks/useReorderPageLayoutWidgets';
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated/graphql';
import { FeatureFlagKey, PageLayoutType } from '~/generated/graphql';
export const PageLayoutContent = () => {
const isRecordPageEnabled = useIsFeatureEnabled(
@@ -27,6 +28,11 @@ export const PageLayoutContent = () => {
const { layoutMode } = usePageLayoutContentContext();
const { currentPageLayout } = useCurrentPageLayoutOrThrow();
const isRecordPageLayout =
currentPageLayout.type === PageLayoutType.RECORD_PAGE;
const isCanvasLayout = isRecordPageEnabled && layoutMode === 'canvas';
const isVerticalList = isRecordPageEnabled && layoutMode === 'vertical-list';
@@ -39,6 +45,7 @@ export const PageLayoutContent = () => {
<PageLayoutVerticalListEditor
widgets={activeTab.widgets}
onReorder={reorderWidgets}
isReorderEnabled={!isRecordPageLayout}
/>
) : (
<PageLayoutVerticalListViewer widgets={activeTab.widgets} />
@@ -1,7 +1,10 @@
import { usePageLayoutShouldUseWhiteBackground } from '@/page-layout/hooks/usePageLayoutShouldUseWhiteBackground';
import { getPageLayoutVerticalListViewerVariant } from '@/page-layout/components/utils/getPageLayoutVerticalListViewerVariant';
import { pageLayoutDraggingWidgetIdComponentState } from '@/page-layout/states/pageLayoutDraggingWidgetIdComponentState';
import { type PageLayoutVerticalListViewerVariant } from '@/page-layout/types/PageLayoutVerticalListViewerVariant';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
import { useIsInPinnedTab } from '@/page-layout/widgets/hooks/useIsInPinnedTab';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import styled from '@emotion/styled';
import {
@@ -11,8 +14,10 @@ import {
type DropResult,
} from '@hello-pangea/dnd';
import { useId } from 'react';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledVerticalListContainer = styled.div<{
variant: PageLayoutVerticalListViewerVariant;
shouldUseWhiteBackground: boolean;
}>`
background: ${({ theme, shouldUseWhiteBackground }) =>
@@ -21,7 +26,9 @@ const StyledVerticalListContainer = styled.div<{
: theme.background.secondary};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${({ theme }) => theme.spacing(4)};
padding: ${({ theme, variant }) =>
variant === 'side-column' ? theme.spacing(1) : theme.spacing(2)};
`;
const StyledDraggableWrapper = styled.div<{ isDragging: boolean }>`
@@ -34,15 +41,25 @@ const StyledDraggableWrapper = styled.div<{ isDragging: boolean }>`
type PageLayoutVerticalListEditorProps = {
widgets: PageLayoutWidget[];
onReorder: (result: DropResult) => void;
isReorderEnabled?: boolean;
};
export const PageLayoutVerticalListEditor = ({
widgets,
onReorder,
isReorderEnabled = true,
}: PageLayoutVerticalListEditorProps) => {
const droppableId = `page-layout-vertical-list-${useId()}`;
const { shouldUseWhiteBackground } = usePageLayoutShouldUseWhiteBackground();
const { isInRightDrawer } = useLayoutRenderingContext();
const isMobile = useIsMobile();
const { isInPinnedTab } = useIsInPinnedTab();
const variant = getPageLayoutVerticalListViewerVariant({
isInPinnedTab,
isMobile,
isInRightDrawer,
});
const setDraggingWidgetId = useSetRecoilComponentState(
pageLayoutDraggingWidgetIdComponentState,
@@ -62,12 +79,18 @@ export const PageLayoutVerticalListEditor = ({
{(provided) => (
<StyledVerticalListContainer
ref={provided.innerRef}
shouldUseWhiteBackground={shouldUseWhiteBackground}
variant={variant}
shouldUseWhiteBackground={isMobile || isInRightDrawer}
// eslint-disable-next-line react/jsx-props-no-spreading
{...provided.droppableProps}
>
{widgets.map((widget, index) => (
<Draggable key={widget.id} draggableId={widget.id} index={index}>
<Draggable
key={widget.id}
draggableId={widget.id}
index={index}
isDragDisabled={!isReorderEnabled}
>
{(provided, snapshot) => (
<StyledDraggableWrapper
ref={provided.innerRef}
@@ -2,7 +2,6 @@ import styled from '@emotion/styled';
import { useIsMobile } from 'twenty-ui/utilities';
import { getPageLayoutVerticalListViewerVariant } from '@/page-layout/components/utils/getPageLayoutVerticalListViewerVariant';
import { usePageLayoutShouldUseWhiteBackground } from '@/page-layout/hooks/usePageLayoutShouldUseWhiteBackground';
import { type PageLayoutVerticalListViewerVariant } from '@/page-layout/types/PageLayoutVerticalListViewerVariant';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
@@ -10,8 +9,8 @@ import { useIsInPinnedTab } from '@/page-layout/widgets/hooks/useIsInPinnedTab';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
const StyledVerticalListContainer = styled.div<{
shouldUseWhiteBackground: boolean;
variant: PageLayoutVerticalListViewerVariant;
shouldUseWhiteBackground: boolean;
}>`
background: ${({ theme, shouldUseWhiteBackground }) =>
shouldUseWhiteBackground
@@ -21,6 +20,8 @@ const StyledVerticalListContainer = styled.div<{
flex-direction: column;
gap: ${({ theme, variant }) =>
variant === 'side-column' ? 0 : theme.spacing(2)};
padding: ${({ theme, variant }) =>
variant === 'side-column' ? 0 : theme.spacing(2)};
`;
type PageLayoutVerticalListViewerProps = {
@@ -30,7 +31,6 @@ type PageLayoutVerticalListViewerProps = {
export const PageLayoutVerticalListViewer = ({
widgets,
}: PageLayoutVerticalListViewerProps) => {
const { shouldUseWhiteBackground } = usePageLayoutShouldUseWhiteBackground();
const { isInRightDrawer } = useLayoutRenderingContext();
const isMobile = useIsMobile();
const { isInPinnedTab } = useIsInPinnedTab();
@@ -43,8 +43,8 @@ export const PageLayoutVerticalListViewer = ({
return (
<StyledVerticalListContainer
shouldUseWhiteBackground={shouldUseWhiteBackground}
variant={variant}
shouldUseWhiteBackground={isMobile || isInRightDrawer}
>
{widgets.map((widget) => (
<div key={widget.id}>