Implement basic edition for record page layouts (#15237)
1. Add actions for record page layouts to side panel 2. Support drag-and-drop of widgets https://github.com/user-attachments/assets/c4cef7c5-08a8-4999-b8a5-e1831cc66709
This commit is contained in:
committed by
GitHub
parent
c2a477a9d6
commit
c6addc2bb4
@@ -0,0 +1,64 @@
|
||||
import { PageLayoutGridLayout } from '@/page-layout/components/PageLayoutGridLayout';
|
||||
import { PageLayoutVerticalListEditor } from '@/page-layout/components/PageLayoutVerticalListEditor';
|
||||
import { PageLayoutVerticalListViewer } from '@/page-layout/components/PageLayoutVerticalListViewer';
|
||||
import { useCurrentPageLayout } from '@/page-layout/hooks/useCurrentPageLayout';
|
||||
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 styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.primary};
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type PageLayoutContentProps = {
|
||||
tabId: string;
|
||||
};
|
||||
|
||||
export const PageLayoutContent = ({ tabId }: PageLayoutContentProps) => {
|
||||
const isRecordPageEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_ENABLED,
|
||||
);
|
||||
|
||||
const isPageLayoutInEditMode = useRecoilComponentValue(
|
||||
isPageLayoutInEditModeComponentState,
|
||||
);
|
||||
|
||||
const { currentPageLayout } = useCurrentPageLayout();
|
||||
const { reorderWidgets } = useReorderPageLayoutWidgets(tabId);
|
||||
|
||||
const activeTab = currentPageLayout?.tabs.find((tab) => tab.id === tabId);
|
||||
|
||||
if (!isDefined(currentPageLayout) || !isDefined(activeTab)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isVerticalList =
|
||||
isRecordPageEnabled && activeTab.layoutMode === 'vertical-list';
|
||||
|
||||
if (isVerticalList) {
|
||||
return (
|
||||
<StyledContainer>
|
||||
{isPageLayoutInEditMode ? (
|
||||
<PageLayoutVerticalListEditor
|
||||
widgets={activeTab.widgets}
|
||||
onReorder={reorderWidgets}
|
||||
/>
|
||||
) : (
|
||||
<PageLayoutVerticalListViewer widgets={activeTab.widgets} />
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return <PageLayoutGridLayout tabId={tabId} />;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SummaryCard } from '@/object-record/record-show/components/SummaryCard';
|
||||
import { PageLayoutGridLayout } from '@/page-layout/components/PageLayoutGridLayout';
|
||||
import { PageLayoutContent } from '@/page-layout/components/PageLayoutContent';
|
||||
import { useCurrentPageLayout } from '@/page-layout/hooks/useCurrentPageLayout';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
|
||||
@@ -29,7 +29,7 @@ export const PageLayoutLeftPanel = ({
|
||||
isInRightDrawer={isInRightDrawer}
|
||||
/>
|
||||
|
||||
<PageLayoutGridLayout tabId={pinnedLeftTabId} />
|
||||
<PageLayoutContent tabId={pinnedLeftTabId} />
|
||||
</ShowPageLeftContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { PageLayoutGridLayout } from '@/page-layout/components/PageLayoutGridLayout';
|
||||
import { PageLayoutContent } from '@/page-layout/components/PageLayoutContent';
|
||||
import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel';
|
||||
import { PageLayoutTabHeader } from '@/page-layout/components/PageLayoutTabHeader';
|
||||
import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutTab';
|
||||
@@ -93,7 +93,7 @@ export const PageLayoutRendererContent = () => {
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && (
|
||||
<PageLayoutGridLayout tabId={activeTabId} />
|
||||
<PageLayoutContent tabId={activeTabId} />
|
||||
)}
|
||||
</StyledScrollWrapper>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
DragDropContext,
|
||||
Draggable,
|
||||
Droppable,
|
||||
type DropResult,
|
||||
} from '@hello-pangea/dnd';
|
||||
import { useId } from 'react';
|
||||
import { type PageLayoutWidget } from '~/generated/graphql';
|
||||
|
||||
const StyledVerticalListContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledDraggableWrapper = styled.div<{ isDragging: boolean }>`
|
||||
background: ${({ theme, isDragging }) =>
|
||||
isDragging ? theme.background.transparent.light : 'transparent'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
transition: background 0.1s ease;
|
||||
`;
|
||||
|
||||
type PageLayoutVerticalListEditorProps = {
|
||||
widgets: PageLayoutWidget[];
|
||||
onReorder: (result: DropResult) => void;
|
||||
};
|
||||
|
||||
export const PageLayoutVerticalListEditor = ({
|
||||
widgets,
|
||||
onReorder,
|
||||
}: PageLayoutVerticalListEditorProps) => {
|
||||
const droppableId = `page-layout-vertical-list-${useId()}`;
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={onReorder}>
|
||||
<Droppable droppableId={droppableId}>
|
||||
{(provided) => (
|
||||
<StyledVerticalListContainer
|
||||
ref={provided.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{widgets.map((widget, index) => (
|
||||
<Draggable key={widget.id} draggableId={widget.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<StyledDraggableWrapper
|
||||
ref={provided.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.draggableProps}
|
||||
isDragging={snapshot.isDragging}
|
||||
>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div {...provided.dragHandleProps}>
|
||||
<WidgetRenderer widget={widget} />
|
||||
</div>
|
||||
</StyledDraggableWrapper>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</StyledVerticalListContainer>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
|
||||
import styled from '@emotion/styled';
|
||||
import { type PageLayoutWidget } from '~/generated/graphql';
|
||||
|
||||
const StyledVerticalListContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type PageLayoutVerticalListViewerProps = {
|
||||
widgets: PageLayoutWidget[];
|
||||
};
|
||||
|
||||
export const PageLayoutVerticalListViewer = ({
|
||||
widgets,
|
||||
}: PageLayoutVerticalListViewerProps) => {
|
||||
return (
|
||||
<StyledVerticalListContainer>
|
||||
{widgets.map((widget) => (
|
||||
<div key={widget.id}>
|
||||
<WidgetRenderer widget={widget} />
|
||||
</div>
|
||||
))}
|
||||
</StyledVerticalListContainer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user