diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer.tsx index 7586427243..e511cd331a 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer.tsx @@ -1,13 +1,47 @@ +import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; +import isPropValid from '@emotion/is-prop-valid'; import styled from '@emotion/styled'; +import { PageLayoutType } from '~/generated/graphql'; -const StyledListItem = styled.div` +const StyledListItem = styled('div', { + shouldForwardProp: (prop) => + isPropValid(prop) && prop !== 'noHorizontalPadding', +})<{ noHorizontalPadding?: boolean }>` align-items: center; justify-content: space-between; gap: ${({ theme }) => theme.spacing(1)}; display: flex; height: ${({ theme }) => theme.spacing(10)}; - padding-left: ${({ theme }) => theme.spacing(3)}; - padding-right: ${({ theme }) => theme.spacing(2)}; + padding-left: ${({ theme, noHorizontalPadding }) => + noHorizontalPadding ? 0 : theme.spacing(3)}; + padding-right: ${({ theme, noHorizontalPadding }) => + noHorizontalPadding ? 0 : theme.spacing(2)}; `; -export { StyledListItem as RecordDetailRecordsListItemContainer }; +type RecordDetailRecordsListItemContainerProps = { + children: React.ReactNode; + className?: string; +}; + +/** + * TODO: Remove noHorizontalPadding logic once the traditional record show page is removed. + */ +export const RecordDetailRecordsListItemContainer = ({ + children, + className, +}: RecordDetailRecordsListItemContainerProps) => { + const layoutRenderingContext = useLayoutRenderingContext(); + + const isInRecordPageLayout = + layoutRenderingContext?.layoutType === PageLayoutType.RECORD_PAGE && + !layoutRenderingContext?.isLegacyRecordShowPage; + + return ( + + {children} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx index b9d2d44964..f4ddd0f7b0 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx @@ -12,7 +12,9 @@ import { getCompaniesMock } from '~/testing/mock-data/companies'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordDetailRelationSection } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSection'; +import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext'; import { ComponentDecorator } from 'twenty-ui/testing'; +import { PageLayoutType } from '~/generated/graphql'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { RightDrawerDecorator } from '~/testing/decorators/RightDrawerDecorator'; import { allMockPersonRecords } from '~/testing/mock-data/people'; @@ -34,29 +36,42 @@ const meta: Meta = { component: RecordDetailRelationSection, decorators: [ (Story) => ( - - name === 'people', - )!, - objectMetadataItem: mockedCompanyObjectMetadataItem, - }), - isRecordFieldReadOnly: false, - }} + - name === 'people', + )!, + objectMetadataItem: mockedCompanyObjectMetadataItem, + }), + isRecordFieldReadOnly: false, + }} > - - - - + + + + + + ), RightDrawerDecorator, ComponentDecorator, diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/property-box/components/PropertyBox.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/property-box/components/PropertyBox.tsx index 10e25de32b..5e728f8581 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/property-box/components/PropertyBox.tsx +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/property-box/components/PropertyBox.tsx @@ -1,5 +1,7 @@ +import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; import isPropValid from '@emotion/is-prop-valid'; import styled from '@emotion/styled'; +import { PageLayoutType } from '~/generated/graphql'; interface PropertyBoxProps { children: React.ReactNode; @@ -8,8 +10,9 @@ interface PropertyBoxProps { } const StyledPropertyBoxContainer = styled('div', { - shouldForwardProp: isPropValid, -})` + shouldForwardProp: (prop) => + isPropValid(prop) && prop !== 'noHorizontalPadding', +})<{ noHorizontalPadding?: boolean }>` align-self: stretch; border-radius: ${({ theme }) => theme.border.radius.sm}; display: flex; @@ -17,16 +20,33 @@ const StyledPropertyBoxContainer = styled('div', { gap: ${({ theme }) => theme.spacing(2)}; padding-top: ${({ theme }) => theme.spacing(3)}; padding-bottom: ${({ theme }) => theme.spacing(3)}; - padding-left: ${({ theme }) => theme.spacing(3)}; - padding-right: ${({ theme }) => theme.spacing(2)}; + padding-left: ${({ theme, noHorizontalPadding }) => + noHorizontalPadding ? 0 : theme.spacing(3)}; + padding-right: ${({ theme, noHorizontalPadding }) => + noHorizontalPadding ? 0 : theme.spacing(2)}; `; +/** + * TODO: Remove noHorizontalPadding logic once the traditional record show page is removed. + */ export const PropertyBox = ({ children, className, dataTestId, -}: PropertyBoxProps) => ( - - {children} - -); +}: PropertyBoxProps) => { + const layoutRenderingContext = useLayoutRenderingContext(); + + const isInRecordPageLayout = + layoutRenderingContext?.layoutType === PageLayoutType.RECORD_PAGE && + !layoutRenderingContext?.isLegacyRecordShowPage; + + return ( + + {children} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/PageLayoutDispatcher.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/PageLayoutDispatcher.tsx index bc477a1397..464bc82e98 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/PageLayoutDispatcher.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/PageLayoutDispatcher.tsx @@ -47,6 +47,8 @@ export const PageLayoutDispatcher = ({ }, layoutType: PageLayoutType.RECORD_PAGE, isInRightDrawer, + // TODO: Remove once the traditional record show page is removed. + isLegacyRecordShowPage: true, }} > ` background: ${({ theme, shouldUseWhiteBackground }) => shouldUseWhiteBackground @@ -12,7 +19,8 @@ const StyledVerticalListContainer = styled.div<{ : theme.background.secondary}; display: flex; flex-direction: column; - gap: ${({ theme }) => theme.spacing(2)}; + gap: ${({ theme, variant }) => + variant === 'side-column' ? 0 : theme.spacing(2)}; `; type PageLayoutVerticalListViewerProps = { @@ -23,10 +31,20 @@ export const PageLayoutVerticalListViewer = ({ widgets, }: PageLayoutVerticalListViewerProps) => { const { shouldUseWhiteBackground } = usePageLayoutShouldUseWhiteBackground(); + const { isInRightDrawer } = useLayoutRenderingContext(); + const isMobile = useIsMobile(); + const { isInPinnedTab } = useIsInPinnedTab(); + + const variant = getPageLayoutVerticalListViewerVariant({ + isInPinnedTab, + isMobile, + isInRightDrawer, + }); return ( {widgets.map((widget) => (
diff --git a/packages/twenty-front/src/modules/page-layout/components/utils/getPageLayoutVerticalListViewerVariant.ts b/packages/twenty-front/src/modules/page-layout/components/utils/getPageLayoutVerticalListViewerVariant.ts new file mode 100644 index 0000000000..8b06737691 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/components/utils/getPageLayoutVerticalListViewerVariant.ts @@ -0,0 +1,19 @@ +import { type PageLayoutVerticalListViewerVariant } from '@/page-layout/types/PageLayoutVerticalListViewerVariant'; + +type GetPageLayoutVerticalListViewerVariantParams = { + isInPinnedTab: boolean; + isMobile: boolean; + isInRightDrawer: boolean; +}; + +export const getPageLayoutVerticalListViewerVariant = ({ + isInPinnedTab, + isMobile, + isInRightDrawer, +}: GetPageLayoutVerticalListViewerVariantParams): PageLayoutVerticalListViewerVariant => { + if (isInPinnedTab || isMobile || isInRightDrawer) { + return 'side-column'; + } + + return 'default'; +}; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts b/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts index 01a3a488c1..3dd9f2b870 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts @@ -38,7 +38,7 @@ export const useTemporaryFieldsConfiguration = ( let otherPosition = 0; fieldsToDisplay.forEach((field) => { - if (field.type === FieldMetadataType.LINKS) { + if (field.isCustom === true) { otherFields.push({ fieldMetadataId: field.id, position: otherPosition++, diff --git a/packages/twenty-front/src/modules/page-layout/types/PageLayoutVerticalListViewerVariant.ts b/packages/twenty-front/src/modules/page-layout/types/PageLayoutVerticalListViewerVariant.ts new file mode 100644 index 0000000000..7f8081a8dc --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/types/PageLayoutVerticalListViewerVariant.ts @@ -0,0 +1 @@ +export type PageLayoutVerticalListViewerVariant = 'default' | 'side-column'; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx index a0c9c8c217..d7f49c461b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx @@ -53,6 +53,7 @@ export const DashboardWidgetPlaceholder = () => { className="widget" > { const isRichTextWidget = widget.type === WidgetType.STANDALONE_RICH_TEXT; const hideRichTextHeader = isRichTextWidget && !isPageLayoutInEditMode; - const showHeader = layoutMode !== 'canvas' && !hideRichTextHeader; + const showHeader = + layoutMode !== 'canvas' && + !hideRichTextHeader && + // TODO: use a more generic approach after record page layout v1 release + widget.type !== WidgetType.FIELDS; const handleClick = () => { handleEditWidget({ @@ -140,6 +144,7 @@ export const WidgetRenderer = ({ widget }: WidgetRendererProps) => { {showHeader && ( theme.border.radius.sm}; + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(3)}; + padding-bottom: ${({ theme }) => theme.spacing(3)}; +`; + type FieldsWidgetProps = { widget: PageLayoutWidget; }; @@ -117,7 +126,7 @@ export const FieldsWidget = ({ widget }: FieldsWidgetProps) => { key={section.id} title={section.title} > - + {isPrefetchLoading ? ( ) : ( @@ -182,7 +191,7 @@ export const FieldsWidget = ({ widget }: FieldsWidgetProps) => { )} )} - + ))} diff --git a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidgetSectionContainer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidgetSectionContainer.tsx index eed1cf46a8..902329210c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidgetSectionContainer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidgetSectionContainer.tsx @@ -4,24 +4,12 @@ import { useState } from 'react'; import { IconChevronDown } from 'twenty-ui/display'; import { AnimatedExpandableContainer, Section } from 'twenty-ui/layout'; -const StyledFieldsWidgetSectionContainer = styled(Section)` - padding-top: ${({ theme }) => theme.spacing(3)}; - padding-bottom: 0; - width: auto; - - &:not(:first-of-type) { - padding-top: 0; - } -`; - const StyledHeader = styled.header` align-items: center; cursor: pointer; display: flex; height: 24px; justify-content: space-between; - padding-left: ${({ theme }) => theme.spacing(3)}; - padding-right: ${({ theme }) => theme.spacing(2)}; `; const StyledTitleLabel = styled.div` @@ -53,7 +41,7 @@ export const FieldsWidgetSectionContainer = ({ setIsExpanded((previousIsExpanded) => !previousIsExpanded); return ( - +
{title} {children} - +
); }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCard.tsx b/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCard.tsx index 9fa919fa61..6ddf53aba2 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCard.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCard.tsx @@ -80,7 +80,9 @@ const StyledWidgetCard = styled.div<{ if (variant === 'side-column' && !isEditable) { return css` - padding: ${theme.spacing(2)}; + gap: ${theme.spacing(2)}; + padding: ${theme.spacing(3)}; + ${isLastWidget !== true && css` border-bottom: 1px solid ${theme.border.color.light}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardHeader.tsx b/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardHeader.tsx index aaf72f8820..19f8059ab7 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardHeader.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardHeader.tsx @@ -2,18 +2,20 @@ import { WidgetActionRenderer } from '@/page-layout/widgets/components/WidgetAct import { widgetCardHoveredComponentFamilyState } from '@/page-layout/widgets/states/widgetCardHoveredComponentFamilyState'; import { type WidgetAction } from '@/page-layout/widgets/types/WidgetAction'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; -import { useTheme } from '@emotion/react'; +import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { type ReactNode } from 'react'; import { IconTrash, OverflowingTextWithTooltip } from 'twenty-ui/display'; import { IconButton } from 'twenty-ui/input'; +import { type WidgetCardVariant } from '@/page-layout/widgets/types/WidgetCardVariant'; import { WidgetGrip } from '@/page-layout/widgets/widget-card/components/WidgetGrip'; import { AnimatePresence, motion } from 'framer-motion'; import { isDefined, isNonEmptyArray } from 'twenty-shared/utils'; export type WidgetCardHeaderProps = { + variant: WidgetCardVariant; widgetId: string; isInEditMode: boolean; isEmpty?: boolean; @@ -32,14 +34,24 @@ const StyledWidgetCardHeader = styled.div` flex-shrink: 0; `; -const StyledTitleContainer = styled.div` +const StyledTitleContainer = styled.div<{ variant: WidgetCardVariant }>` color: ${({ theme }) => theme.font.color.primary}; flex: 1; font-size: ${({ theme }) => theme.font.size.md}; - padding-inline: ${({ theme }) => theme.spacing(1)}; font-weight: ${({ theme }) => theme.font.weight.medium}; user-select: none; overflow: hidden; + + ${({ theme, variant }) => { + switch (variant) { + case 'side-column': + return undefined; + default: + return css` + padding-inline: ${theme.spacing(1)}; + `; + } + }} `; const StyledRightContainer = styled.div` @@ -62,6 +74,7 @@ const StyledIconButtonContainer = styled(motion.div)` export const WidgetCardHeader = ({ widgetId, + variant, isEmpty = false, isInEditMode = false, isResizing = false, @@ -88,7 +101,7 @@ export const WidgetCardHeader = ({ /> )} - + diff --git a/packages/twenty-front/src/modules/ui/layout/contexts/LayoutRenderingContext.tsx b/packages/twenty-front/src/modules/ui/layout/contexts/LayoutRenderingContext.tsx index 28bd4965a1..fd9d159dd5 100644 --- a/packages/twenty-front/src/modules/ui/layout/contexts/LayoutRenderingContext.tsx +++ b/packages/twenty-front/src/modules/ui/layout/contexts/LayoutRenderingContext.tsx @@ -11,6 +11,9 @@ export type LayoutRenderingContextType = { layoutType: PageLayoutType; isInRightDrawer: boolean; + + /** TODO: Remove once the traditional record show page is removed. */ + isLegacyRecordShowPage?: boolean; }; export const [LayoutRenderingProvider, useLayoutRenderingContext] =