diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx index 1a1f1d2929..24fa33000e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx @@ -11,6 +11,17 @@ import { RecordTableRecordLimitReloadEffect } from '@/object-record/record-table import { PageFocusId } from '@/types/PageFocusId'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import { styled } from '@linaria/react'; + +const StyledRecordTablePrintBoundary = styled.div` + display: contents; + + @media print { + display: block; + max-height: 100vh; + overflow: hidden; + } +`; type RecordTableWithWrappersProps = { objectNameSingular: string; @@ -60,12 +71,14 @@ export const RecordTableWithWrappers = ({ onRecordIdentifierClick={handleRecordIdentifierClick} > - - - - + + + + + + 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 8d680f84c9..e4e967b016 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx @@ -86,6 +86,34 @@ const StyledGridContainer = styled.div` .react-grid-item:hover .widget-card-resize-handle { display: block !important; } + + @media print { + min-height: auto; + padding: 0; + user-select: auto; + + .react-grid-layout { + height: auto !important; + } + + // Flow the absolutely-positioned grid items into the page, but keep the + // pixel width and height react-grid-layout sets inline: the charts are sized + // by a resize observer (Nivo SVG and a custom canvas bar chart), so changing + // their box would re-measure mid-print and render them blank. + .react-grid-item { + break-inside: avoid; + margin-bottom: ${themeCssVariables.spacing[4]}; + page-break-inside: avoid; + position: static !important; + transform: none !important; + } + + .react-grid-placeholder, + .react-resizable-handle, + .widget-card-resize-handle { + display: none !important; + } + } `; type ExtendedResponsiveProps = ResponsiveProps & { diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabsRenderer.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabsRenderer.tsx index c6c475f1b6..3ff56e6cae 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabsRenderer.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabsRenderer.tsx @@ -36,17 +36,41 @@ const StyledContainer = styled.div<{ hasPinnedTab: boolean }>` grid-template-rows: minmax(0, 1fr); height: 100%; width: 100%; + + @media print { + display: block; + height: auto; + width: 100%; + } `; const StyledTabsAndDashboardContainer = styled.div` display: flex; flex-direction: column; overflow: hidden; + + @media print { + display: block; + overflow: visible; + + .page-layout-tab-list-print-hidden { + display: none; + } + } `; const StyledScrollWrapperContainer = styled.div` flex: 1; min-height: 0; + + @media print { + min-height: auto; + + .page-layout-scroll-wrapper { + height: auto; + overflow: visible; + } + } `; export const PageLayoutTabsRenderer = () => { @@ -184,6 +208,7 @@ export const PageLayoutTabsRenderer = () => { /> {(sortedActiveTabs.length > 1 || isPageLayoutInEditMode) && ( { { diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx index 4f23343d79..9b6f443d61 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx @@ -23,6 +23,18 @@ const StyledRow = styled.div` flex-direction: row; min-height: 0; min-width: 0; + + @media print { + display: block; + min-height: auto; + min-width: auto; + + // Only the main content (first child) is printed; the side panel and its + // resize chrome that follow it are hidden. + > *:not(:first-child) { + display: none; + } + } `; const StyledContent = styled.div` @@ -31,6 +43,13 @@ const StyledContent = styled.div` min-height: 0; min-width: 0; overflow: hidden; + + @media print { + display: block; + min-height: auto; + min-width: auto; + overflow: visible; + } `; const StyledContentTransitionContainer = styled.div` @@ -39,6 +58,13 @@ const StyledContentTransitionContainer = styled.div` min-height: 0; min-width: 0; overflow: hidden; + + @media print { + display: block; + min-height: auto; + min-width: auto; + overflow: visible; + } `; const StyledContentTransitionPage = styled(motion.div)` @@ -47,6 +73,12 @@ const StyledContentTransitionPage = styled(motion.div)` min-height: 0; min-width: 0; width: 100%; + + @media print { + display: block; + min-height: auto; + min-width: auto; + } `; const MainAppLayoutOutlet = () => { diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/PageCardLayout.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/PageCardLayout.tsx index f24eec16e9..756d625e8a 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/PageCardLayout.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/PageCardLayout.tsx @@ -16,6 +16,12 @@ const StyledRoot = styled.div` flex-direction: row; min-height: 0; min-width: 0; + + @media print { + display: block; + min-height: auto; + min-width: auto; + } `; const StyledMainCardWrapper = styled.div` @@ -26,6 +32,14 @@ const StyledMainCardWrapper = styled.div` min-width: 0; padding-left: 4px; width: 0; + + @media print { + display: block; + margin-left: 0; + min-width: auto; + padding-left: 0; + width: auto; + } `; // oxlint-disable-next-line twenty/no-hardcoded-colors @@ -48,6 +62,14 @@ const StyledCard = styled.div` -4px 0 4px 0 rgba(0, 0, 0, 0.03), 0 0 0 1px ${themeCssVariables.border.color.medium}; } + + @media print { + border-radius: 0; + box-shadow: none; + display: block; + min-height: auto; + overflow: visible; + } `; const StyledBodyContent = styled.div` @@ -56,6 +78,17 @@ const StyledBodyContent = styled.div` flex-direction: column; min-height: 0; width: 100%; + + @media print { + display: block; + min-height: auto; + } +`; + +const StyledPrintHidden = styled.div` + @media print { + display: none; + } `; export const PageCardLayout = ({ @@ -68,10 +101,14 @@ export const PageCardLayout = ({ - {header} - {secondaryBar} + {header} + {secondaryBar} - {showInformationBanner && } + {showInformationBanner && ( + + + + )} {children} diff --git a/packages/twenty-front/src/pages/page-layout/StandalonePageLayoutPage.tsx b/packages/twenty-front/src/pages/page-layout/StandalonePageLayoutPage.tsx index ec055c721f..eb16a66a72 100644 --- a/packages/twenty-front/src/pages/page-layout/StandalonePageLayoutPage.tsx +++ b/packages/twenty-front/src/pages/page-layout/StandalonePageLayoutPage.tsx @@ -17,6 +17,12 @@ const StyledPageLayoutContainer = styled.div` flex-direction: column; min-height: 0; overflow-y: auto; + + @media print { + display: block; + min-height: auto; + overflow: visible; + } `; export const StandalonePageLayoutPage = () => {